---
updatedAt: 2026-08-31T13:40:35.000Z
agentTools:
  projectIndex: https://docs.dlocal.com/llms.txt
---

# Wallet payments

Learn how to start receiving wallet payments.

Wallet payments (`WALLET`) let users pay by authenticating with their wallet provider (the wallet app or service that confirms the payment).

<Callout icon="⚠️" theme="warn">
  ### Do not start new integrations on the legacy wallet token model

  If you are integrating for the first time, use the [Enrollment API](#tokenized-wallets-enrollment-api) for all tokenized and recurring wallet flows. The legacy `wallet.token` model is supported for existing merchants only. It does not receive new feature development. Starting on the legacy model might require a migration later.
</Callout>

For tokenized/recurring flows, dLocal supports two integration models:

* **Enrollment API (use this for all new integrations):** The user completes a one-time redirect to authorize future payments. dLocal returns an enrollment ID used for all subsequent direct charges. No redirect or re-authentication required.
* **Legacy wallet token (existing merchants only):** The user completes a redirect and the webhook returns a `wallet.token`. No new integrations should use this model. See [Tokenized wallets: Legacy](#tokenized-wallets-legacy).

## Prerequisites

Complete these steps before you create a wallet payment:

1. Use the [Create payment](https://docs.dlocal.com/reference/create-payment) endpoint for one-time wallet payments and legacy token enrollment. Use the [Create enrollment](https://docs.dlocal.com/reference/create-enrollment) endpoint for new Enrollment API integrations.
2. Identify the wallet `payment_method_id` for the user's country in the [Payment method section](https://docs.dlocal.com/docs/payment-method).
3. Set `payment_method_flow` based on the payment flow:
   * Use `REDIRECT` for one-time payments and enrollment.
   * Use `DIRECT` only after you receive and store a valid `wallet.token` (legacy) or an `ACTIVE` enrollment ID (Enrollment API).
4. Include `notification_url` to receive the final asynchronous payment or enrollment status.
5. Include `callback_url` for `REDIRECT` flows so the wallet provider can return the user to your site.
6. Include `device.type` for all `REDIRECT` requests.

## Related references

Use these pages when you build and validate your integration:

* [Create payment](https://docs.dlocal.com/reference/create-payment): create one-time wallet payments, legacy token enrollments, and recurring `DIRECT` charges.
* [Create enrollment](https://docs.dlocal.com/reference/create-enrollment): create enrollments using the Enrollment API (recommended for new integrations).
* [Get enrollment](https://docs.dlocal.com/reference/get-enrollment): retrieve enrollment status. Use this to check the current state if you miss an enrollment webhook.
* [Cancel an enrollment](https://docs.dlocal.com/reference/cancel-an-enrollment): cancel an active enrollment when a user unsubscribes or changes their payment method.
* [The wallet object](https://docs.dlocal.com/reference/the-wallet-object): review the supported `wallet` fields for the legacy flow, including `save`, `verify`, `capture`, and `token`.
* [Payment method section](https://docs.dlocal.com/docs/payment-method): confirm wallet availability, country support, and payment-method-specific requirements.
* [Receive notifications](https://docs.dlocal.com/docs/receive-notifications): implement webhook handling for asynchronous `PENDING`, `PAID`, `REJECTED`, `VERIFIED`, and `ACTIVE` updates.
* [Get wallet token status](https://docs.dlocal.com/docs/wallet-payments#check-a-wallettoken-status): check whether a `wallet.token` is active, pending, cancelled, or rejected.
* [Cancel a `wallet.token`](https://docs.dlocal.com/reference/cancel-wallet-token): cancel a stored legacy token when a user cancels or changes their payment method.

## Available wallets

To integrate wallet payments, identify each wallet you want to support and set its `payment_method_id` in the payment request.

<Callout icon="📘" theme="info">
  ### Payment method list

  Get the payment method list or view all available wallets by country in the [Payment method section](https://docs.dlocal.com/docs/payment-method).
</Callout>

# Payment flow

Choose one of these flows based on whether you need a one-time payment or future `DIRECT` charges.

| Flow                                                                              | Description                                                                                                                                                                                                                                                                                                                                   | Best for                                                                |
| :-------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------- |
| **One-time payments** (`REDIRECT`)                                                | Redirect the user to the wallet's website to authenticate and complete the payment. After authentication, the wallet provider redirects the user back to your site. dLocal does not issue a token.                                                                                                                                            | One-time purchases                                                      |
| **Tokenized (recurring) payments: Enrollment API** (`REDIRECT`, then `DIRECT`) 🆕 | Redirect the user once to authorize future payments via the [Create enrollment](https://docs.dlocal.com/reference/create-enrollment) endpoint. dLocal returns an enrollment ID. Use it for subsequent `DIRECT` payments without redirecting the user again.                                                                                   | Subscriptions, recurring billing. **Recommended for new integrations.** |
| **Tokenized (recurring) payments: Legacy** (`REDIRECT`, then `DIRECT`) ❗          | Redirect the user to the wallet's website to authenticate and authorize future payments. Send `wallet.save = true` in the request. The asynchronous notification includes a `wallet.token`. Use that token for subsequent `DIRECT` payments. **Do not use for new integrations.** See [Tokenized wallets: Legacy](#tokenized-wallets-legacy). | **Existing merchants only. No new integrations.**                       |

# One-time payments

In this flow, redirect the user to the wallet's app/website to authenticate and complete the payment.

## Sale

```json Example request
{
    "amount": 26,
    "currency": "BRL",
    "country": "BR",
    "payment_method_id": "PZ",
    "payment_method_flow": "REDIRECT",
    "payer": {
      "name": "Example Payer",
      "email": "payer@example.com",
      "document": "<DOCUMENT_NUMBER>"
    },
    "device": {
      "type": "WEB"
    },
    "order_id": "5346523564",
    "notification_url": "http://merchantsite.com/notifications",
    "callback_url": "http://merchantsite.com/callback"
  }
```
```json Example response
{
  "id": "R-4-41f8628f-b6ec-4c02-96d5-c5b03cac7cb0",
  "amount": 26,
  "currency": "BRL",
  "payment_method_id": "PZ",
  "payment_method_type": "WALLET",
  "payment_method_flow": "REDIRECT",
  "country": "BR",
  "created_date": "2018-12-26T20:37:20.000+0000",
  "status": "PENDING",
  "status_detail": "The payment is pending.",
  "status_code": "100",
  "order_id": "5346523564",
  "notification_url": "http://merchantsite.com/notifications",
  "redirect_url": "https://pay.dlocal.com/gmf-apm/payments-redirect/M-0aa0cc00-..."
}
```
```json Asynchronous response
{
  "id": "R-4-41f8628f-b6ec-4c02-96d5-c5b03cac7cb0",
  "amount": 26,
  "currency": "BRL",
  "payment_method_id": "PZ",
  "payment_method_type": "WALLET",
  "payment_method_flow": "REDIRECT",
  "country": "BR",
  "created_date": "2018-12-26T20:37:20.000+0000",
  "status": "PAID",
  "status_detail": "The payment was paid.",
  "status_code": "200",
  "order_id": "5346523564",
  "notification_url": "http://merchantsite.com/notifications"
}
```

After you receive the synchronous response, redirect the user to `redirect_url`. dLocal sends the final `PAID` or `REJECTED` status by webhook to your `notification_url`.

# Tokenized wallets: Enrollment API

<Callout icon="👍" theme="okay">
  ### Recommended for new integrations

  Use the Enrollment API to establish a reusable authorization. All subsequent charges go directly, with no redirect or re-authentication required. For method-specific parameters, see [Yape Recurring](https://docs.dlocal.com/docs/peru#yape-recurring) and [Pix with Biometrics](https://docs.dlocal.com/docs/brazil-pix-biometrics).
</Callout>

The Enrollment API supports two integration patterns:

| Pattern                                                                                  | When to use                                                                                                                            |
| :--------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------- |
| **Enrollment only** (`POST /enrollments`)                                                | Establish the user authorization without an initial charge. Use when enrollment and first payment happen at different steps.           |
| **Payment + Enrollment in the same request** (`POST /payments` with `enrollment` object) | Create a payment and establish the authorization in a single API call. Use when the first charge and enrollment happen simultaneously. |

## Enrollment only

Use `POST /enrollments` when you want to create the enrollment without an initial payment. See the full [Create enrollment](https://docs.dlocal.com/reference/create-enrollment) API reference for all available fields.

**Enrollment types:**

The valid `type` values depend on the payment method. Not all payment methods support all types.

* `ON_DEMAND`: Merchant initiates recurring payments at any time, with no frequency or amount restrictions. Valid for all enrollment-capable payment methods.
* `MERCHANT_SUBSCRIPTION`: Merchant initiates recurring payments based on defined subscription settings (frequency, amount, dates). Only valid for payment methods that expose a structured subscription protocol (for example, Pix Automático). See the individual payment method pages to confirm which type applies.

<Callout icon="ℹ️" theme="info">
  ### external_id

  `external_id` is your internal identifier for the enrollment. It appears in all enrollment webhooks, letting you correlate dLocal enrollment events back to your own records.
</Callout>

```json Enrollment request
{
  "currency": "BRL",
  "country": "BR",
  "type": "ON_DEMAND",
  "payment_method_id": "<PM_CODE>",
  "payment_method_flow": "REDIRECT",
  "payer": {
    "name": "Example Payer",
    "document": "<DOCUMENT_NUMBER>",
    "email": "payer@example.com"
  },
  "device": {
    "type": "WEB"
  },
  "external_id": "<YOUR_EXTERNAL_ID>",
  "description": "Subscription enrollment",
  "notification_url": "http://merchantsite.com/notifications",
  "callback_url": "http://merchantsite.com/callback"
}
```
```json Synchronous response
{
  "id": "E-4-32e1218f-b6ec-3f21-13d5-50v12ere2ca4",
  "external_id": "<YOUR_EXTERNAL_ID>",
  "currency": "BRL",
  "country": "BR",
  "type": "ON_DEMAND",
  "payment_method_id": "<PM_CODE>",
  "payment_method_flow": "REDIRECT",
  "created_date": "2025-12-26T20:37:20.000+0000",
  "status": "PENDING",
  "status_detail": "The enrollment is pending.",
  "status_code": "100",
  "redirect_url": "https://pay.dlocal.com/gmf-apm/payments/E-4-32e1218f-b6ec-3f21-13d5-50v12ere2ca4",
  "notification_url": "http://merchantsite.com/notifications"
}
```
```json Asynchronous notification
{
  "id": "E-4-32e1218f-b6ec-3f21-13d5-50v12ere2ca4",
  "external_id": "<YOUR_EXTERNAL_ID>",
  "currency": "BRL",
  "country": "BR",
  "type": "ON_DEMAND",
  "payment_method_id": "<PM_CODE>",
  "payment_method_flow": "REDIRECT",
  "created_date": "2025-12-26T20:37:20.000+0000",
  "status": "ACTIVE",
  "status_detail": "The enrollment is active.",
  "status_code": "200",
  "notification_url": "http://merchantsite.com/notifications"
}
```

After receiving the synchronous response, redirect the user to `redirect_url`. The user completes authorization with their wallet provider. dLocal then sends the enrollment webhook to your `notification_url` with the final status.

<Callout icon="ℹ️" theme="info">
  ### callback_url behavior

  When and how the user lands on `callback_url` after completing the enrollment redirect depends on the payment method. See the individual payment method pages for specifics.
</Callout>

<Callout icon="📘" theme="info">
  ### Enrollment ID

  Once the enrollment status is `ACTIVE`, the `id` field (for example, `E-4-32e1218f-...`) is your enrollment ID. Persist it: it must be included in all subsequent recurring payment requests for this user.

  To check the current enrollment status at any time (for example, if your server was temporarily unavailable), use the [Get enrollment](https://docs.dlocal.com/reference/get-enrollment) endpoint.

  To cancel an enrollment, use the [Cancel an enrollment](https://docs.dlocal.com/reference/cancel-an-enrollment) endpoint.
</Callout>

**Enrollment status codes:**

For the full list of enrollment `status_code` values and their meanings, see the [Get enrollment](https://docs.dlocal.com/reference/get-enrollment) and [Create enrollment](https://docs.dlocal.com/reference/create-enrollment) API references.

| `status`    | `status_code` | Description                                                                                |
| :---------- | :------------ | :----------------------------------------------------------------------------------------- |
| `PENDING`   | `100`         | The enrollment has been created and is awaiting user authorization.                        |
| `ACTIVE`    | `200`         | The user completed authorization. The enrollment ID is ready to use for recurring charges. |
| `CANCELLED` | `400`         | The enrollment was cancelled.                                                              |
| `REJECTED`  | `300`         | The user did not complete or declined the authorization.                                   |

<Callout icon="ℹ️" theme="info">
  ### Examples contain placeholders

  Replace `<PM_CODE>` with the `payment_method_id` of the wallet you are integrating (for example, `YE` for Yape Recurring). See the [Payment method section](https://docs.dlocal.com/docs/payment-method) for all available IDs.
</Callout>

## Payment + Enrollment in the same request

Include an `enrollment` object in a `POST /payments` call to create a payment and an enrollment in one request. Use this pattern when the first charge and enrollment happen simultaneously. See the [Create payment](https://docs.dlocal.com/reference/create-payment) and [Create enrollment](https://docs.dlocal.com/reference/create-enrollment) API references for all available fields.

<Callout icon="ℹ️" theme="info">
  ### Payment method-specific requirements

  The example below uses `payment_method_flow: "DIRECT"` as a generic skeleton. **For Yape (**`YE`**), the first enrollment+payment request must use&#x20;**`payment_method_flow: "REDIRECT"`.

  Yape requires a user redirect for first-time authorization. See the [Yape Recurring](https://docs.dlocal.com/docs/peru#yape-recurring) section for the correct Yape-specific example.

  Yape also requires `device.type` in all enrollment requests, and `payer.phone` is mandatory when `device.type` is `WEB`.
</Callout>

```json Example request
{
  "amount": 285,
  "currency": "BRL",
  "country": "BR",
  "payment_method_id": "<PM_CODE>",
  "payment_method_flow": "DIRECT",
  "payer": {
    "name": "Example Payer",
    "email": "payer@example.com",
    "document": "<DOCUMENT_NUMBER>"
  },
  "device": {
    "type": "WEB"
  },
  "enrollment": {
    "external_id": "<YOUR_EXTERNAL_ID>",
    "type": "ON_DEMAND",
    "description": "Monthly subscription authorization",
    "notification_url": "https://merchantsite.com/enrollment_notifications",
    "callback_url": "https://merchantsite.com/enrollment_callback"
  },
  "order_id": "ord-221124442ab",
  "notification_url": "https://merchantsite.com/payments",
  "callback_url": "https://merchantsite.com/payment_callback"
}
```
```json Synchronous response
{
  "id": "R-4-22112444-2ab0-4f5g-6h7i-8j9k0l1m2n3o",
  "amount": 285,
  "currency": "BRL",
  "country": "BR",
  "payment_method_id": "<PM_CODE>",
  "payment_method_type": "WALLET",
  "payment_method_flow": "REDIRECT",
  "created_date": "2024-07-26T20:37:20.000+0000",
  "status": "PENDING",
  "status_detail": "The payment is pending.",
  "status_code": "100",
  "order_id": "ord-221124442ab",
  "notification_url": "https://merchantsite.com/payments",
  "redirect_url": "https://pay.dlocal.com/gmf-apm/payments-redirect/M-0aa0cc00-...",
  "enrollment": {
    "id": "E-4-32e1218f-b6ec-3f21-13d5-50v12ere2ca4",
    "external_id": "<YOUR_EXTERNAL_ID>",
    "created_date": "2024-07-26T20:37:20.000+0000",
    "status": "PENDING",
    "status_detail": "The enrollment is pending.",
    "status_code": "100"
  }
}
```
```json Asynchronous notification (enrollment)
{
  "id": "E-4-32e1218f-b6ec-3f21-13d5-50v12ere2ca4",
  "external_id": "<YOUR_EXTERNAL_ID>",
  "currency": "BRL",
  "country": "BR",
  "type": "ON_DEMAND",
  "payment_method_id": "<PM_CODE>",
  "payment_method_flow": "REDIRECT",
  "created_date": "2024-07-26T20:37:20.000+0000",
  "status": "ACTIVE",
  "status_detail": "The enrollment is active.",
  "status_code": "200",
  "notification_url": "https://merchantsite.com/enrollment_notifications"
}
```
```json Asynchronous notification (payment)
{
  "id": "R-4-22112444-2ab0-4f5g-6h7i-8j9k0l1m2n3o",
  "amount": 285,
  "status": "PAID",
  "status_detail": "The payment was paid.",
  "status_code": "200",
  "currency": "BRL",
  "country": "BR",
  "payment_method_id": "<PM_CODE>",
  "payment_method_type": "WALLET",
  "payment_method_flow": "REDIRECT",
  "payer": {
    "name": "Example Payer",
    "email": "payer@example.com",
    "document": "<DOCUMENT_NUMBER>"
  },
  "enrollment": {
    "id": "E-4-32e1218f-b6ec-3f21-13d5-50v12ere2ca4",
    "external_id": "<YOUR_EXTERNAL_ID>"
  },
  "order_id": "ord-221124442ab",
  "notification_url": "https://merchantsite.com/payments",
  "created_date": "2024-07-26T20:37:20.000+0000"
}
```

<Callout icon="📘" theme="info">
  ### Single request, dual outcome

  This request produces two independent asynchronous notifications: one for the enrollment (sent to `enrollment.notification_url`) and one for the payment (sent to `notification_url`). They may arrive in any order. Persist the `enrollment.id` only after the enrollment notification reaches `ACTIVE` status. Grant entitlement only after the payment notification reaches `PAID` status.
</Callout>

## Recurring charges with an enrollment ID

Once the enrollment is `ACTIVE`, charge the user directly. Add `enrollment.id` inside an `enrollment` object in subsequent `POST /payments` calls. No redirect or re-authentication required. See the [Create payment](https://docs.dlocal.com/reference/create-payment) API reference for the full list of fields.

```json Example request
{
  "amount": 26,
  "currency": "BRL",
  "country": "BR",
  "payment_method_id": "<PM_CODE>",
  "payment_method_flow": "DIRECT",
  "payer": {
    "name": "Example Payer",
    "email": "payer@example.com",
    "document": "<DOCUMENT_NUMBER>"
  },
  "enrollment": {
    "id": "E-4-32e1218f-b6ec-3f21-13d5-50v12ere2ca4"
  },
  "order_id": "ord-221124442cd",
  "notification_url": "https://merchantsite.com/payments"
}
```
```json Example response
{
  "id": "R-4-55ab7891-cd12-4ef3-ab56-789012cdef34",
  "amount": 26,
  "currency": "BRL",
  "country": "BR",
  "payment_method_id": "<PM_CODE>",
  "payment_method_type": "WALLET",
  "payment_method_flow": "DIRECT",
  "created_date": "2024-07-27T10:15:00.000+0000",
  "status": "PAID",
  "status_detail": "The payment was paid.",
  "status_code": "200",
  "order_id": "ord-221124442cd",
  "notification_url": "https://merchantsite.com/payments"
}
```

<Callout icon="⚠️" theme="warn">
  ### Final status is payment-method-specific

  For most wallets (Mercado Pago, Nequi, and NuPay, for example), the synchronous response to a `DIRECT` charge is the final status (`PAID` or `REJECTED`), as shown above. **Yape (**`YE`**) is the exception:** it always returns `PENDING` synchronously, and dLocal delivers the final status by webhook within seconds. Always confirm per-payment-method behavior in the relevant country section before implementing.
</Callout>

# Tokenized wallets: Legacy

<Callout icon="❗" theme="error">
  ### Legacy integration

  The wallet object tokenization flow below is supported for **existing merchants only** and does not receive new feature development. New integrations must use the [Enrollment API](#tokenized-wallets-enrollment-api) described above.
</Callout>

The enrollment and recurring charge are two separate API calls.

## Step 1: Enrollment (`REDIRECT`)

Redirect the user to the wallet's website to authenticate and authorize future payments. Send `wallet.save = true` in the request. The asynchronous notification includes a `wallet.token`. Use that `wallet.token` for future payments without authenticating the user again. See [The wallet object](https://docs.dlocal.com/reference/the-wallet-object) for the supported wallet fields.

### Option A: Sale + verify flow

Enroll and charge simultaneously (`wallet.verify`: `false`, `amount` > `0`). Use this option to charge the user and issue a `wallet.token` in the same flow.

```json Example request
{
  "amount": 26,
  "currency": "BRL",
  "country": "BR",
  "payment_method_id": "PZ",
  "payment_method_flow": "REDIRECT",
  "payer": {
    "name": "Example Payer",
    "email": "payer@example.com",
    "document": "<DOCUMENT_NUMBER>"
  },
  "wallet": {
    "save": true,
    "verify": false,
    "capture": true
  },
  "device": {
    "type": "WEB"
  },
  "order_id": "5346523564",
  "notification_url": "http://merchantsite.com/notifications",
  "callback_url": "http://merchantsite.com/callback"
}
```
```json Example response
{
  "id": "R-4-41f8628f-b6ec-4c02-96d5-c5b03cac7cb0",
  "amount": 26,
  "currency": "BRL",
  "payment_method_id": "PZ",
  "payment_method_type": "WALLET",
  "payment_method_flow": "REDIRECT",
  "country": "BR",
  "created_date": "2018-12-26T20:37:20.000+0000",
  "status": "PENDING",
  "status_detail": "The payment is pending.",
  "status_code": "100",
  "order_id": "5346523564",
  "notification_url": "http://merchantsite.com/notifications",
  "redirect_url": "https://pay.dlocal.com/gmf-apm/payments-redirect/M-0aa0cc00-..."
}
```
```json Asynchronous response
{
  "id": "R-4-41f8628f-b6ec-4c02-96d5-c5b03cac7cb0",
  "amount": 26,
  "currency": "BRL",
  "payment_method_id": "PZ",
  "payment_method_type": "WALLET",
  "payment_method_flow": "REDIRECT",
  "country": "BR",
  "created_date": "2018-12-26T20:37:20.000+0000",
  "status": "PAID",
  "status_detail": "The payment was paid.",
  "status_code": "200",
  "order_id": "5346523564",
  "wallet": {
    "token": "<WALLET_TOKEN>"
  },
  "notification_url": "http://merchantsite.com/notifications"
}
```

<Callout icon="⚠️" theme="warn">
  ### Payment failed, enrollment succeeded

  The notification includes a `wallet.token` as long as the user authenticated successfully, regardless of whether dLocal approved the payment.
</Callout>

### Option B: Verify flow

Enroll without charging (`wallet.verify`: `true`, `amount`: `0`). dLocal verifies the wallet and issues a `wallet.token` without making a charge. Use this for free trials or deferred billing.

```json Example request
{
  "amount": 0,
  "currency": "BRL",
  "country": "BR",
  "payment_method_id": "PZ",
  "payment_method_flow": "REDIRECT",
  "payer": {
    "name": "Example Payer",
    "email": "payer@example.com",
    "document": "<DOCUMENT_NUMBER>"
  },
  "wallet": {
    "save": true,
    "verify": true,
    "capture": false
  },
  "device": {
    "type": "WEB"
  },
  "order_id": "5346523564",
  "notification_url": "http://merchantsite.com/notifications",
  "callback_url": "http://merchantsite.com/callback"
}
```
```json Example response
{
  "id": "D-4-75c7473a-ab86-4e43-bd39-c840268747d3",
  "amount": 0,
  "currency": "BRL",
  "payment_method_id": "PZ",
  "payment_method_type": "WALLET",
  "payment_method_flow": "REDIRECT",
  "country": "BR",
  "created_date": "2018-12-26T20:37:20.000+0000",
  "status": "PENDING",
  "status_detail": "The payment is pending",
  "status_code": "100",
  "order_id": "5346523564",
  "notification_url": "http://merchantsite.com/notifications",
  "redirect_url": "https://pay.dlocal.com/gmf-apm/payments-redirect/M-0aa0cc00-..."
}
```
```json Asynchronous response
{
  "id": "D-4-75c7473a-ab86-4e43-bd39-c840268747d3",
  "amount": 0,
  "status": "VERIFIED",
  "status_detail": "The wallet was verified.",
  "status_code": "700",
  "currency": "BRL",
  "country": "BR",
  "payment_method_id": "PZ",
  "payment_method_type": "WALLET",
  "payment_method_flow": "REDIRECT",
  "payer": {
    "name": "Example Payer",
    "email": "payer@example.com",
    "document": "<DOCUMENT_NUMBER>"
  },
  "wallet": {
    "token": "<WALLET_TOKEN>"
  },
  "order_id": "5346523564",
  "notification_url": "http://merchantsite.com/notifications",
  "created_date": "2018-12-26T20:37:20.000+0000"
}
```

Expect the asynchronous response at `notification_url` after the user approves the enrollment in the wallet provider.

Persist `wallet.token` for all future charges. Only store tokens from **VERIFIED (700)** or **PAID (200)** status. Tokens from rejected flows are invalid.

## Check a `wallet.token` status

If you miss the asynchronous notification, or need to confirm a token is still usable before charging it, retrieve its current status directly.

```bash Example request
curl --request GET \
  --url "https://api.dlocal.com/payments/wallet/<WALLET_TOKEN>"
```
```json Example response
{
  "wallet": {
    "token": "<WALLET_TOKEN>",
    "status": "ACTIVE",
    "status_detail": "The wallet token is active."
  }
}
```

| `status`    | Meaning                                                      |
| :---------- | :----------------------------------------------------------- |
| `ACTIVE`    | The token is approved and ready to use in a `DIRECT` charge. |
| `PENDING`   | The user hasn't finished authorizing the token yet.          |
| `CANCELLED` | The token was cancelled and can no longer be used.           |
| `REJECTED`  | The token was rejected and can no longer be used.            |

If the token doesn't exist, the endpoint returns a 404:

```json Example response
{
  "code": "resource not found",
  "detail": ""
}
```

## Step 2: Recurring charge (`DIRECT`)

Pay with a `wallet.token`. The request does not redirect the user.

<Callout icon="⚠️" theme="warn">
  ### Recurring wallet payments

  For recurring wallet payments in the Philippines and Indonesia, include the user's mobile number in the `payer.phone` field of the payment request.
</Callout>

```json Example request
{
    "amount": 10,
    "currency": "BRL",
    "country": "BR",
    "payment_method_id": "PZ",
    "payment_method_flow": "DIRECT",
    "payer": {
        "name": "Example Payer",
        "email": "payer@example.com",
        "phone": "<PHONE_NUMBER>",
        "document": "<DOCUMENT_NUMBER>",
        "address": {
            "country": "BR",
            "state": "Example State",
            "city": "Example City",
            "zip_code": "<ZIP_CODE>",
            "street": "Example Street",
            "number": "123"
        }
    },
    "wallet": {
        "token": "<WALLET_TOKEN>"
    },
    "order_id": "fe32a6ef-3954-4b5f-8712-d1c1079beba3",
    "description": "test-wallet",
    "notification_url": "https://notifications.merchant.com"
}
```
```json Example response
{
  "id": "F-4-b302affe-e590-4b29-98cc-57ec2dd10592",
  "amount": 10,
  "currency": "BRL",
  "payment_method_id": "PZ",
  "payment_method_type": "WALLET",
  "payment_method_flow": "DIRECT",
  "country": "BR",
  "created_date": "2022-09-29T06:52:48.000+0000",
  "expiration_date": "2022-10-04T06:52:48.000+0000",
  "status": "PAID",
  "status_detail": "The payment was paid.",
  "status_code": "200",
  "order_id": "fe32a6ef-3954-4b5f-8712-d1c1079beba3",
  "description": "test-wallet",
  "notification_url": "https://notifications.merchant.com"
}
```

<Callout icon="⚠️" theme="warn">
  ### Synchronous response

  For most wallets, the synchronous response to a `DIRECT` charge is the final status (`PAID` or `REJECTED`). No need to wait for a notification. **Yape (YE)** is the exception: it returns `PENDING` synchronously, and dLocal delivers the final status by webhook within seconds.

  **Always confirm per-payment-method behavior in the relevant section.**
</Callout>

<Callout icon="📘" theme="info">
  ### Device ID for Mercado Pago

  Add the Device ID by following [The DeviceId Object (for Mercado Pago only)](https://docs.dlocal.com/reference/the-deviceid-object-for-mercado-pago-only).

  Include it in the [wallet object](https://docs.dlocal.com/reference/the-wallet-object). It improves fraud detection on Mercado Pago's side and can improve authorization rates.
</Callout>

## Cancel a `wallet.token`

### Cancel with the API

Call the cancel endpoint when a user unsubscribes, changes their payment method, or you need to cancel access programmatically.

See [Cancel a `wallet.token`](https://docs.dlocal.com/reference/cancel-wallet-token) in the API reference section.

```bash Example request
curl --request POST \
  --url "https://api.dlocal.com/payments/wallet/<WALLET_TOKEN>/cancel"
```
```json Example response
{
  "wallet": {
    "token": "<WALLET_TOKEN>"
  },
  "status": "CANCELLED",
  "status_detail": "The wallet token was cancelled."
}
```

### Customer-initiated cancellation

Users can cancel a subscription or recurring authorization directly from their wallet app (Nequi, GCash, Mercado Pago, and Yape, for example). The behavior when this happens depends on which integration model you are using:

**Enrollment API:** The next `DIRECT` charge attempt returns `status_code` `328` when the enrollment has been cancelled. Handle `328` by stopping all future charge attempts against that enrollment ID and triggering a new enrollment flow for the user. You can also use the [Cancel an enrollment](https://docs.dlocal.com/reference/cancel-an-enrollment) endpoint proactively when a user unsubscribes on your side, so the enrollment is invalidated at dLocal before the next scheduled charge runs.

**Legacy wallet token:** The next `DIRECT` charge attempt using the cancelled `wallet.token` returns `status_code` `328` (invalid token). Handle `328` by stopping all future charge attempts and triggering a new enrollment flow. Always call the [cancel token](https://docs.dlocal.com/reference/cancel-wallet-token) endpoint on your side when a user unsubscribes so your records stay in sync with the wallet provider.

Cancellation, whether merchant-initiated or customer-initiated, permanently invalidates the `wallet.token` or enrollment reference. Never retry a `328`.

**How wallet providers notify dLocal of user-initiated cancellations:**

Not all wallet providers behave the same way when a user cancels access from within their app:

**Proactive notification:** The wallet provider notifies dLocal in real time. dLocal sends a webhook to your Payins Notification URL configured in dLocal's merchant dashboard to confirm that the authorization has been canceled. Listen for this webhook and update your records before the next scheduled charge.

**Silent cancellation:** The wallet provider does not notify dLocal. Your integration discovers the cancellation when the next `DIRECT` charge attempt returns `status_code` `328`. Stop retrying and start a new enrollment flow.

# Device type

Include `device.type` in all `REDIRECT` flows where the user is present, including both `POST /payments` and `POST /enrollments`. Wallet providers can use different experiences, such as deep links, app-to-app redirects, or push notifications. dLocal uses this field to route the best experience for each context without requiring integration changes.

Omit `device.type` from `DIRECT` charges that use a `wallet.token` or an `enrollment.id` reference. Those are customer-not-present requests.

| Value        | Description                                        |
| :----------- | :------------------------------------------------- |
| `WEB`        | The user is on a desktop or mobile browser.        |
| `MOBILE_APP` | The user is inside your native iOS or Android app. |

```json Example request
{
  "amount": 26,
  "currency": "BRL",
  "country": "BR",
  "payment_method_id": "PZ",
  "payment_method_flow": "REDIRECT",
  "payer": {
    "name": "Example Payer",
    "email": "payer@example.com",
    "document": "<DOCUMENT_NUMBER>"
  },
  "device": {
    "type": "MOBILE_APP"
  },
  "order_id": "5346523564",
  "notification_url": "http://merchantsite.com/notifications",
  "callback_url": "http://merchantsite.com/callback"
}
```

For payment methods where `device.type` is already mandatory, such as **Yape**, see the payment-method-specific sections for full behavior details.

# Sandbox testing

Use the `description` field in your request body to control payment behavior in the sandbox environment.

## Enrollment flows (`REDIRECT`)

| `description` value | Behavior                                                                                                                              |
| :------------------ | :------------------------------------------------------------------------------------------------------------------------------------ |
| Omitted             | Returns `PENDING`. Use the mock playground UI to manually approve or reject. Recommended for UX testing.                              |
| `"100:approved"`    | Returns `PENDING`, then auto-sends a `PAID` (or `VERIFIED` / `ACTIVE`) webhook after \~60 seconds. Recommended for automated testing. |
| `"100:rejected"`    | Returns `PENDING`, then auto-sends a `REJECTED` webhook after \~60 seconds.                                                           |

## Recurring charges (`DIRECT`)

| `description` value | Behavior                        |
| :------------------ | :------------------------------ |
| `"200"`             | Returns `PAID` immediately.     |
| `"300"`             | Returns `REJECTED` immediately. |

<Callout icon="⚠️" theme="warn">
  ### Mock server vs. real provider environment

  By default, all sandbox payments are routed through dLocal's mock server, which provides stable, predictable behavior across all wallet payment methods. If you need to test against the actual sandbox environment of a specific wallet provider to validate provider-side behavior or UX, check with your assigned dLocal TAM. Some providers require additional configuration or have limited sandbox availability.
</Callout>