# API de pagos

> Crea y gestiona sesiones de pago en criptomonedas con la API de pagos de 2328.io.

La API de pagos te permite crear sesiones de pago, redirigir a los clientes a un checkout alojado y hacer seguimiento del estado del pago.

## Crear pago

Crea una sesión de pago y devuelve una URL para que el cliente realice el pago.

### Parámetros de la solicitud

| Campo | Tipo | Requerido | Descripción | Valores |
|-------|------|-----------|-------------|---------|
| `amount` | decimal | sí | Monto del pago en la divisa indicada, p. ej. `100.00` |  |
| `currency` | string | sí | Divisa fiat (USD, EUR, RUB, …) o criptomoneda (USDT, TRX, BTC, …) | `USD`, `EUR`, `RUB`, `KZT`, `UAH`, `UZS`, `USDT`, `USDC`, `BTC`, `ETH`, `GRAM`, `SOL`, `TRX`, `BNB`, `POL`, `LTC`, `DASH`, `DOGE`, `ZEC`, `XRP`, `XMR` |
| `order_id` | string | sí | Tu ID de pedido, p. ej. `ORDER-12345` (hasta 128 caracteres) |  |
| `to_currency` | string | no | Criptomoneda preseleccionada | `USDT`, `USDC`, `BTC`, `ETH`, `GRAM`, `SOL`, `TRX`, `BNB`, `POL`, `LTC`, `DASH`, `DOGE`, `ZEC`, `XRP`, `XMR` |
| `network` | string | no\* | Código de red (obligatorio si `to_currency` está definido o si `currency` es una criptomoneda) | `TRX-TRC20`, `ETH-ERC20`, `BASE`, `BSC-BEP20`, `AVAX-C`, `POL-MATIC`, `TON`, `SOL`, `BTC`, `LTC`, `DASH`, `DOGE`, `ZEC`, `XRP`, `XMR` |
| `url_return` | string | no | URL de redirección tras el pago, p. ej. `https://your-site.com/return` |  |
| `url_success` | string | no | Alternativa a `url_return` |  |
| `url_callback` | string | sí | URL para las notificaciones de webhook, p. ej. `https://your-site.com/webhook` |  |
| `invite_code` | string | no | Código de referido |  |
| `fee_split` | decimal | no | Porcentaje de la comisión del comerciante que asume el pagador, 0–100 (%). 0 = el comerciante paga todo, 100 = el pagador paga todo. Sobrescribe el ajuste a nivel de proyecto. **Ejemplo: `30`** (el pagador cubre el 30 % de la comisión). |  |
| `price_markup` | decimal | no | Recargo o descuento sobre el monto facturado, de −99 a 100 (%). Sobrescribe el ajuste a nivel de proyecto. **Ejemplo: `5`** (+5 %) o `-10` (10 % de descuento). |  |
| `description` | string | no | Descripción opcional de la factura (máx. 200 caracteres). Se muestra al pagador en la página de pago. **Ejemplo: `Premium plan — Order #12345`**. |  |
| `ttl_seconds` | int | no | Tiempo de vida de la factura en segundos, de `300` (5 minutos) a `86400` (24 horas). Pasado ese tiempo la factura caduca y ya no puede pagarse. Valor por defecto: `3600` (1 hora). **Ejemplo: `3600`**. |  |

### Respuesta

```json
{
  "state": 0,
  "result": {
    "uuid": "abc123-def456-...",
    "order_id": "ORDER-12345",
    "amount": "100.00",
    "currency": "USD",
    "amount_usd": "100.00",
    "exchange_rate": null,
    "url": "https://2328.io/pay/abc123-def456-...",
    "tg_deeplink": "https://t.me/my2328bot?start=pay_abc123-def456-...",
    "expires_at": "2026-01-11T21:00:00Z",
    "created_at": "2026-01-11T20:00:00Z",
    "payer_currency": "USDT",
    "payer_amount": "100.50",
    "network": "TRX-TRC20",
    "address": "TXYZabc123...",
    "payment_status": "check",
    "txid": null,
    "payment_amount": null,
    "qr": "data:image/png;base64,iVBORw0..."
  }
}
```

- Redirige al cliente a `result.url` para completar el pago.
- `tg_deeplink` — deeplink del bot de Telegram para pagar a través de Telegram MiniApp.
- `qr` — código QR codificado en Base64 (data URI) de la dirección de depósito. Está presente cuando ya se ha asignado una dirección (cuando `network` se define junto con `to_currency`, o cuando `currency` es una criptomoneda); en caso contrario, es `null`.
- `txid`, `payment_amount` — son `null` hasta que el cliente paga. Se completan una vez que la transacción se detecta en la blockchain. Escucha el webhook con `payment_status: paid` para enterarte.
- `exchange_rate` — es `null` cuando la conversión aún no aplica (p. ej., aún no se ha fijado el tipo fiat → cripto). Se completa una vez que se elige una divisa de pago.

> Use your project UUID and the endpoint-appropriate API key from the merchant dashboard.

#### Interactive request: `POST /v1/payment`
  - `amount` (decimal, required)
  - `currency` (enum, required): USD,EUR,RUB,KZT,UAH,UZS,USDT,USDC,BTC,ETH,GRAM,SOL,TRX,BNB,POL,LTC,DASH,DOGE,ZEC,XRP,XMR
  - `order_id` (string, required)
  - `to_currency` (enum): USDT,USDC,BTC,ETH,GRAM,SOL,TRX,BNB,POL,LTC,DASH,DOGE,ZEC,XRP,XMR
  - `network` (enum): TRX-TRC20,ETH-ERC20,BASE,BSC-BEP20,AVAX-C,POL-MATIC,TON,SOL,BTC,LTC,DASH,DOGE,ZEC,XRP,XMR
  - `url_return` (string)
  - `url_success` (string)
  - `url_callback` (string, required)
  - `invite_code` (string)
  - `fee_split` (decimal)
  - `price_markup` (decimal)
  - `description` (string)
  - `ttl_seconds` (integer)

## Hosted checkout, H2H y cantidades exactas de criptomonedas

El mismo extremo soporta tres formas de invoice distintas. Elige uno deliberadamente; No mezcles la semántica de la cantidad.

### Hosted checkout con la elección del pagador

Envía `amount`, `currency`, `order_id` y `url_callback`, pero omite `to_currency` y `network`. La respuesta contiene `result.url`; Los campos `address`, `qr` y a veces pagador permanecen `null` hasta que el pagador selecciona una dirección en la página alojada.

```json
{
  "amount": "125.00",
  "currency": "EUR",
  "order_id": "ORDER-2026-1042",
  "url_callback": "https://merchant.example/webhooks/2328",
  "url_return": "https://merchant.example/orders/ORDER-2026-1042"
}
```

### Dirección directa H2H invoice

Envía tanto `to_currency` como `network`. 2328.io crea la invoice blockchain durante el API call, por lo que una respuesta exitosa puede darse dentro de tu caja sin redirigir al cliente.

```json
{
  "amount": "100.00",
  "currency": "USD",
  "to_currency": "USDT",
  "network": "TRX-TRC20",
  "order_id": "ORDER-2026-1043",
  "url_callback": "https://merchant.example/webhooks/2328"
}
```

Renderiza estos valores exactamente como se devolvió:

- `payer_amount` y `payer_currency` — la instrucción de pago;
- `network` y `address` — el único destino para este invoice;
- `qr` — un URI de datos para la misma dirección;
- `expires_at` — la fecha límite invoice;
- `url` — un fallback alojado útil cuando no se puede completar el pago personalizado.

> **DANGER:** Nunca generes ni sustituyas una dirección, reutilices una dirección de otro invoice ni calcules `payer_amount` a partir de un precio de spot público. La respuesta de la API es autoritativa.

### Invoice para una cantidad exacta de cripto

Mete la criptomoneda en `currency` cuando el invoice en sí esté denominado en cripto:

```json
{
  "amount": "25.000000",
  "currency": "USDT",
  "network": "TRX-TRC20",
  "order_id": "ORDER-2026-1044",
  "url_callback": "https://merchant.example/webhooks/2328"
}
```

El valor de criptomoneda solicitado se conserva en `payer_currency` / `payer_amount`. El servicio también puede mantener una valoración en USD internamente para los campos de contabilidad y tarifas; no reemplaces la instrucción exacta de criptomoneda con esa valoración. Conserva las cadenas decimales devueltas, incluida la precisión final.

Para una criptomoneda con solo una red compatible, la red puede seleccionarse automáticamente. Todavía se recomienda proporcionar `network` explícitamente para una integración determinista. Para activos de múltiples redes, como las stablecoins, siempre envíalo.

## Idempotency y retries

`order_id` está limitado al proyecto autenticado merchant y actúa como la clave de creación idempotency. Si ya existe un pago, la API devuelve esa sesión con `state: 0`.

> **WARNING:** Un retry con el mismo `order_id` no** significa “actualizar este invoice”. Los campos de cantidad, moneda, callback, markup, TTL o dirección modificados pueden ser ignorados porque se devuelve la sesión existente. Persiste la primera solicitud y rechaza retries conflictivos en tu propia aplicación.

Algoritmo de creación recomendado:

1. Inserta tu intento de pago local y `order_id` único en una transacción de base de datos.
2. Envía la solicitud API firmada.
3. Persiste el `uuid` devuelto y la respuesta completa.
4. Si se pierde el resultado HTTP, retry la solicitud idéntica o consulta `/v1/payment/info` mediante `order_id`.
5. Nunca crees un segundo pedido local solo porque la solicitud ascendente haya caducado.

## Casos límite de pago

| Situación | Manejo correcto |
|-----------|------------------|
| `address` / `qr` es `null` | La dirección del pagador no ha sido inicializada. Redirige a `url`, o crea un nuevo H2H invoice correctamente especificado con un nuevo `order_id`. |
| Error de validación HTTP `400` | Lea el nivel de campo `errors`; no retry la entrada sin cambios. |
| HTTP `429` | Retry con retroceso exponencial con jitter y mantenga el mismo `order_id`. |
| HTTP `503` / `direction_disabled` | Actualizar `/v1/directions`; oculte temporalmente la dirección o retry más tarde. |
| Solicitud del cliente timeout | Trate el resultado como desconocido. Consulte mediante `order_id` antes de crear cualquier otra cosa. |
| `underpaid_check` | Almacene el evento parcial y espere un complemento o estado posterior. No acredite dos veces cuando lleguen más txids. |
| `underpaid` | Estado final de subpago. Aplique su política configurada de cumplimiento/revisión manual al monto realmente acreditado. |
| `overpaid` | Pago exitoso con fondos excedentes. Cumpla de manera idempotente y conserve los montos reales para la política reconciliation/reembolso. |
| `aml_lock` | No cumpla ni libere fondos automáticamente; enrútelo al flujo de trabajo de cumplimiento/soporte. |
| `cancel` | Invoice expiró o fue cancelado. No infiera que una transferencia en cadena tardía sea imposible; reconcilie cualquier evento posterior con soporte. |

La URL de retorno del navegador es solo de navegación. Un cliente puede abrirla sin pagar, cerrarla después de pagar o reproducirla más tarde. Solo un estado verificado de API/webhook puede liquidar la orden merchant.

## Información del pago

Obtén el estado actual del pago mediante `uuid` u `order_id`.

### Parámetros de la solicitud

| Campo | Tipo | Requerido | Descripción | Valores |
|-------|------|-----------|-------------|---------|
| `uuid` | string | sí\* | UUID del pago (de `result.uuid` al crearlo) |  |
| `order_id` | string | sí\* | Tu ID de pedido |  |

> **INFO:** Se requiere al menos uno de `uuid` u `order_id`.

#### Interactive request: `POST /v1/payment/info`
  - `uuid` (string)
  - `order_id` (string)

## Lista de pagos

Obtén una lista de todos los pagos con filtros y paginación.

### Parámetros de la solicitud

| Campo | Tipo | Requerido | Descripción | Valores |
|-------|------|-----------|-------------|---------|
| `status` | string | no | Filtrar por estado del pago (consulta [References](/docs/references)) | `pending`, `check`, `paid`, `underpaid_check`, `underpaid`, `overpaid`, `cancel` |
| `date_from` | date | no | Fecha de inicio (YYYY-MM-DD), p. ej. `2026-01-01` |  |
| `date_to` | date | no | Fecha de fin (YYYY-MM-DD), p. ej. `2026-01-31` |  |
| `page` | int | no | Número de página, por defecto `1` |  |
| `per_page` | int | no | Elementos por página, por defecto `15`, máximo `5000` |  |

#### Interactive request: `POST /v1/payment/list`
  - `status` (enum): pending,check,paid,underpaid_check,underpaid,overpaid,cancel
  - `date_from` (string)
  - `date_to` (string)
  - `page` (integer)
  - `per_page` (integer)