> ## Documentation Index
> Fetch the complete documentation index at: https://docs.onepay.la/llms.txt
> Use this file to discover all available pages before exploring further.

# Casos de uso de débitos automáticos

> Debita tarjetas, cuentas bancarias o billeteras digitales. Cada método tiene su propio flujo de captura y tiempos de procesamiento.

## Un endpoint, múltiples métodos de pago

Todos los débitos automáticos se ejecutan con `POST /charges`. Lo que cambia es **cómo capturas y autorizas** el método de pago antes de cobrar.

| Método                        | Captura                                  | Procesamiento       | Interacción del cliente                   |
| ----------------------------- | ---------------------------------------- | ------------------- | ----------------------------------------- |
| **Tarjeta de crédito/débito** | SDK Elements (recomendado) o API directa | Inmediato           | Solo al registrar la tarjeta              |
| **Cuenta bancaria (ACH)**     | API con número de cuenta                 | Siguiente día hábil | Solo al autorizar la cuenta               |
| **Nequi**                     | API + autorización en app                | Inmediato           | Autoriza en la app Nequi cada vinculación |
| **Daviplata**                 | API + código OTP por SMS                 | Inmediato           | Ingresa OTP recibido por SMS              |

<Note>
  Todos los métodos requieren `authorization: true` al momento de registrar el medio de pago. Sin esto, no podrás crear cargos.
</Note>

***

## Débito con tarjeta de crédito/débito

Hay dos formas de capturar los datos de la tarjeta:

### Opción A: SDK Elements (recomendado)

El [SDK Elements](/docs/elements/introduction) captura los datos sensibles en un iframe seguro y te devuelve un `card_token`. Tu servidor nunca toca los datos de la tarjeta, lo que simplifica el cumplimiento PCI DSS.

```mermaid theme={null}
sequenceDiagram
    participant Cliente
    participant Tu Frontend
    participant SDK Elements
    participant Tu Backend
    participant OnePay API

    Cliente->>Tu Frontend: Ingresa datos de tarjeta
    Tu Frontend->>SDK Elements: Captura en iframe seguro
    SDK Elements-->>Tu Frontend: card_token
    Tu Frontend->>Tu Backend: Envía card_token
    Tu Backend->>OnePay API: POST /cards/tokenized (card_token)
    OnePay API-->>Tu Backend: card_id
    Tu Backend->>OnePay API: POST /charges (card_id)
    OnePay API-->>Tu Backend: charge (succeeded/declined)
```

**Paso 1: Tokenizar en el frontend**

Integra el SDK Elements en tu página de captura. Consulta la [guía de integración](/docs/elements/introduction) para el setup completo.

**Paso 2: Registrar la tarjeta tokenizada**

```bash theme={null}
curl https://api.onepay.la/v1/cards/tokenized \
  -X POST \
  -H "Authorization: Bearer sk_test_xxx" \
  -H "Content-Type: application/json" \
  -H "x-idempotency: card-token-001" \
  -d '{
    "card_token": "TOKEN_DEL_SDK",
    "customer_id": "CUSTOMER_ID",
    "authorization": true
  }'
```

**Paso 3: Crear el cargo**

```bash theme={null}
curl https://api.onepay.la/v1/charges \
  -X POST \
  -H "Authorization: Bearer sk_test_xxx" \
  -H "Content-Type: application/json" \
  -H "x-idempotency: cargo-tarjeta-001" \
  -d '{
    "title": "Mensualidad Enero",
    "customer_id": "CUSTOMER_ID",
    "amount": 85000,
    "card_id": "CARD_ID"
  }'
```

### Opción B: API directa (requiere PCI DSS)

Si tu empresa cumple con PCI DSS, puedes enviar los datos de tarjeta directamente a la API.

<Warning>
  Este endpoint usa un dominio diferente: `https://cards.onepay.la/v1/cards`. Esto se implementa mediante un proxy para garantizar el cumplimiento PCI DSS.
</Warning>

```bash theme={null}
curl https://cards.onepay.la/v1/cards \
  -X POST \
  -H "Authorization: Bearer pk_test_xxx" \
  -H "Content-Type: application/json" \
  -H "x-idempotency: card-directa-001" \
  -d '{
    "card_number": "4111111111111111",
    "expiration_year": "29",
    "expiration_month": "12",
    "ccv": "123",
    "holder_name": "MARIA LOPEZ",
    "customer_id": "CUSTOMER_ID",
    "authorization": true
  }'
```

Luego crea el cargo con `POST /charges` igual que en la Opción A.

### Respuesta de la tarjeta

```json theme={null}
{
  "id": "9e01eeae-2868-4564-9d04-84d1d1d027d2",
  "brand": "Mastercard",
  "last_four": "8099",
  "card_name": "MARIA LOPEZ",
  "type": "credit_card",
  "cvc_check": true,
  "verified": true,
  "action": {
    "action": "NULL",
    "next": null
  }
}
```

<Note>
  Si `action.action` no es `"NULL"`, la tarjeta requiere autenticación 3D Secure. Redirige al cliente a la URL en `action.next` para completar la verificación.
</Note>

***

## Débito con cuenta bancaria (ACH)

Los débitos a cuentas bancarias se procesan mediante ACH (Cámara de Compensación Automatizada). El procesamiento **no es inmediato**: los fondos se acreditan el **siguiente día hábil**.

### Tiempos de procesamiento

| Tipo de cuenta            | Procesamiento       | Horario de corte     |
| ------------------------- | ------------------- | -------------------- |
| Ahorros / Corriente (ACH) | Siguiente día hábil | Antes de las 4:00 PM |
| Nequi                     | Inmediato           | 24/7                 |
| Daviplata                 | Inmediato           | 24/7                 |

<Warning>
  Los cargos ACH enviados después del horario de corte se procesan al **segundo día hábil siguiente**. Los fines de semana y festivos no cuentan como días hábiles.
</Warning>

### Flujo completo

```mermaid theme={null}
sequenceDiagram
    participant Tu App
    participant OnePay API
    participant ACH
    participant Banco

    Tu App->>OnePay API: POST /accounts (authorization: true)
    OnePay API-->>Tu App: account_id (status: PENDING)
    Note over OnePay API,Banco: El banco valida la cuenta
    OnePay API-->>Tu App: Webhook account.active
    Tu App->>OnePay API: POST /charges (account_id)
    OnePay API->>ACH: Envía instrucción de débito
    Note over ACH,Banco: Procesamiento siguiente día hábil
    ACH->>Banco: Débito
    Banco-->>OnePay API: Confirmación
    OnePay API->>Tu App: Webhook charge.succeeded
```

**Paso 1: Consultar bancos disponibles**

```bash theme={null}
curl https://api.onepay.la/v1/accounts/banks \
  -H "Authorization: Bearer sk_test_xxx"
```

**Paso 2: Registrar la cuenta bancaria**

```bash theme={null}
curl https://api.onepay.la/v1/accounts \
  -X POST \
  -H "Authorization: Bearer sk_test_xxx" \
  -H "Content-Type: application/json" \
  -H "x-idempotency: cuenta-ach-001" \
  -d '{
    "customer_id": "CUSTOMER_ID",
    "bank_id": "BANK_ID",
    "subtype": "SAVINGS",
    "account_number": "123456789",
    "authorization": true
  }'
```

Tipos de cuenta (`subtype`):

| Valor                | Descripción                             |
| -------------------- | --------------------------------------- |
| `SAVINGS`            | Cuenta de ahorros                       |
| `CHECKING`           | Cuenta corriente                        |
| `ELECTRONIC_DEPOSIT` | Depósito electrónico (Nequi, Daviplata) |

**Paso 3: Crear el cargo**

```bash theme={null}
curl https://api.onepay.la/v1/charges \
  -X POST \
  -H "Authorization: Bearer sk_test_xxx" \
  -H "Content-Type: application/json" \
  -H "x-idempotency: cargo-ach-001" \
  -d '{
    "title": "Cuota mensual",
    "customer_id": "CUSTOMER_ID",
    "amount": 200000,
    "account_id": "ACCOUNT_ID"
  }'
```

**Paso 4: Esperar confirmación**

El cargo queda en estado `pending` hasta que ACH procese el débito. Recibirás un [webhook](/client/webhooks/charges) con el resultado:

```json theme={null}
{
  "charge": {
    "id": "9e02966f-2ddf-4ee7-a391-5b5b7653e232",
    "status": "succeeded",
    "amount": 200000
  },
  "event": {
    "type": "charge.succeeded",
    "timestamp": 1689262934,
    "environment": "production"
  }
}
```

***

## Débito con billeteras digitales (Nequi / Daviplata)

Las billeteras digitales requieren que el **cliente autorice la vinculación** desde su app bancaria antes de poder debitar. A diferencia de ACH, el procesamiento es **inmediato**.

<Warning>
  El cliente debe tener la app de Nequi o Daviplata instalada y activa. Si no completa la autorización, la cuenta quedará en estado `PENDING` y no podrás crear cargos.
</Warning>

### Nequi: Autorización en la app

El cliente debe aprobar la vinculación directamente en la app de Nequi. No se requiere OTP.

```mermaid theme={null}
sequenceDiagram
    participant Tu App
    participant OnePay API
    participant Nequi App
    participant Cliente

    Tu App->>OnePay API: POST /accounts (bank_id Nequi)
    OnePay API-->>Tu App: account_id (PENDING)
    OnePay API->>Nequi App: Solicitud de vinculación
    Nequi App->>Cliente: Notificación push
    Cliente->>Nequi App: Autoriza débito automático
    Nequi App-->>OnePay API: Vinculación aprobada
    Tu App->>OnePay API: POST /accounts/{id}/validate
    OnePay API-->>Tu App: status: ACTIVE
    Tu App->>OnePay API: POST /charges (account_id)
    OnePay API-->>Tu App: charge (succeeded)
```

<Steps>
  <Step title="Crear la cuenta Nequi">
    ```bash theme={null}
    curl https://api.onepay.la/v1/accounts \
      -X POST \
      -H "Authorization: Bearer sk_test_xxx" \
      -H "Content-Type: application/json" \
      -H "x-idempotency: nequi-001" \
      -d '{
        "customer_id": "CUSTOMER_ID",
        "bank_id": "9957817d-239b-4032-8dca-bc508a76579c",
        "subtype": "ELECTRONIC_DEPOSIT",
        "account_number": "3201112233",
        "authorization": true
      }'
    ```

    <Note>
      El `account_number` es el número de celular registrado en Nequi (sin prefijo +57).
    </Note>
  </Step>

  <Step title="El cliente autoriza en Nequi">
    Muestra instrucciones al cliente:

    1. Abre la app de **Nequi**
    2. Busca la notificación de autorización de débito automático
    3. Aprueba la vinculación

    El cliente recibirá una notificación push en la app de Nequi.
  </Step>

  <Step title="Validar la cuenta">
    Una vez que el cliente autoriza en Nequi, valida la cuenta. Para Nequi **no se requiere OTP**.

    ```bash theme={null}
    curl https://api.onepay.la/v1/accounts/ACCOUNT_ID/validate \
      -X POST \
      -H "Authorization: Bearer sk_test_xxx" \
      -H "Content-Type: application/json" \
      -H "x-idempotency: validar-nequi-001"
    ```
  </Step>

  <Step title="Crear el cargo">
    Con la cuenta en estado `ACTIVE`, puedes debitar:

    ```bash theme={null}
    curl https://api.onepay.la/v1/charges \
      -X POST \
      -H "Authorization: Bearer sk_test_xxx" \
      -H "Content-Type: application/json" \
      -H "x-idempotency: cargo-nequi-001" \
      -d '{
        "title": "Suscripción mensual",
        "customer_id": "CUSTOMER_ID",
        "amount": 45000,
        "account_id": "ACCOUNT_ID"
      }'
    ```
  </Step>
</Steps>

### Daviplata: Validación con OTP

Daviplata requiere que el cliente ingrese un código OTP recibido por SMS para completar la vinculación.

```mermaid theme={null}
sequenceDiagram
    participant Tu App
    participant OnePay API
    participant Daviplata
    participant Cliente

    Tu App->>OnePay API: POST /accounts (bank_id Daviplata)
    OnePay API-->>Tu App: account_id (PENDING)
    OnePay API->>Daviplata: Solicitud de vinculación
    Daviplata->>Cliente: SMS con código OTP
    Cliente->>Tu App: Ingresa código OTP
    Tu App->>OnePay API: POST /accounts/{id}/validate (otp)
    OnePay API-->>Tu App: status: ACTIVE
    Tu App->>OnePay API: POST /charges (account_id)
    OnePay API-->>Tu App: charge (succeeded)
```

<Steps>
  <Step title="Crear la cuenta Daviplata">
    ```bash theme={null}
    curl https://api.onepay.la/v1/accounts \
      -X POST \
      -H "Authorization: Bearer sk_test_xxx" \
      -H "Content-Type: application/json" \
      -H "x-idempotency: daviplata-001" \
      -d '{
        "customer_id": "CUSTOMER_ID",
        "bank_id": "9957817d-26b5-456d-bc49-4a8ba1bc021b",
        "subtype": "ELECTRONIC_DEPOSIT",
        "account_number": "3109998877",
        "authorization": true
      }'
    ```

    <Note>
      El `account_number` es el número de celular registrado en Daviplata (sin prefijo +57).
    </Note>
  </Step>

  <Step title="El cliente recibe el OTP">
    Daviplata envía un SMS con un código de 6 dígitos al número del cliente. Muestra un campo en tu interfaz para que el cliente ingrese el código.
  </Step>

  <Step title="Validar la cuenta con OTP">
    ```bash theme={null}
    curl https://api.onepay.la/v1/accounts/ACCOUNT_ID/validate \
      -X POST \
      -H "Authorization: Bearer sk_test_xxx" \
      -H "Content-Type: application/json" \
      -H "x-idempotency: validar-daviplata-001" \
      -d '{
        "otp": "123456"
      }'
    ```
  </Step>

  <Step title="Crear el cargo">
    Con la cuenta validada, puedes debitar:

    ```bash theme={null}
    curl https://api.onepay.la/v1/charges \
      -X POST \
      -H "Authorization: Bearer sk_test_xxx" \
      -H "Content-Type: application/json" \
      -H "x-idempotency: cargo-daviplata-001" \
      -d '{
        "title": "Pago de servicio",
        "customer_id": "CUSTOMER_ID",
        "amount": 30000,
        "account_id": "ACCOUNT_ID"
      }'
    ```
  </Step>
</Steps>

***

## Estados de la cuenta

Después de crear una cuenta, pasa por estos estados antes de poder debitar:

| Estado       | Descripción                                       |
| ------------ | ------------------------------------------------- |
| `PENDING`    | Cuenta creada, esperando autorización del cliente |
| `VALIDATING` | Validación en progreso (Daviplata con OTP)        |
| `ACTIVE`     | Lista para débitos                                |
| `REJECTED`   | Autorización rechazada por el cliente o el banco  |
| `INACTIVE`   | Cuenta desactivada                                |

## Comparación rápida

|                       | Tarjeta                    | Cuenta ACH               | Nequi                          | Daviplata                              |
| --------------------- | -------------------------- | ------------------------ | ------------------------------ | -------------------------------------- |
| **Captura**           | SDK Elements o API directa | Número de cuenta + banco | Celular                        | Celular                                |
| **Autorización**      | Al registrar la tarjeta    | Al crear la cuenta       | App Nequi (push)               | OTP por SMS                            |
| **Validación extra**  | 3DS (si aplica)            | No                       | `POST /accounts/{id}/validate` | `POST /accounts/{id}/validate` con OTP |
| **Procesamiento**     | Inmediato                  | Siguiente día hábil      | Inmediato                      | Inmediato                              |
| **Webhook resultado** | `charge.succeeded`         | `charge.succeeded`       | `charge.succeeded`             | `charge.succeeded`                     |

## Errores comunes

| Error                       | Causa                                    | Solución                                     |
| --------------------------- | ---------------------------------------- | -------------------------------------------- |
| `ACCOUNT_IS_NOT_AUTHORIZED` | La cuenta no tiene `authorization: true` | Crea la cuenta con `authorization: true`     |
| `INSUFFICIENT_FUNDS`        | Fondos insuficientes                     | Notifica al cliente y reintenta              |
| `CARD_EXPIRED`              | Tarjeta expirada                         | Solicita al cliente que actualice la tarjeta |
| `invalid_otp`               | OTP incorrecto o expirado (Daviplata)    | Solicita un nuevo OTP al cliente             |
| `validation_error`          | Falta `customer_id` o método de pago     | Verifica que envías `card_id` o `account_id` |

## Webhooks relevantes

* [Webhook de cuentas](/client/webhooks/accounts): Cambios de estado de cuentas (vinculación aprobada/rechazada)
* [Webhook de cargos](/client/webhooks/charges): Resultado de los débitos (aprobado/rechazado)
* [Webhook de tarjetas](/client/webhooks/cards): Cambios de estado de tarjetas
