Referencia API de Pagos
Payments API Reference
Complete reference for the Payment Gateway REST API.
Base URL
https://api.sanpay.io/api/v1 Authentication
All API requests require authentication via Bearer token:
Authorization: Bearer YOUR_API_KEY Payments
Create Payment
POST /payments
Create a new payment request.
Request Body| Field | Type | Required | Description |
|---|---|---|---|
fiatAmount | number | ✅ | Importe en moneda fiat (ej. 99.99) |
fiatCurrency | string | ✅ | Código de moneda fiat: USD, EUR, etc. |
coinId | string (UUID) | ❌ | ID específico de la moneda. Si se omite con allowUserSelection: true, el cliente elige. |
chainId | string | ❌ | Identificador de blockchain (ej. BTC, ETH) |
metadata | object | ❌ | Pares clave-valor personalizados (retornados en webhooks) |
externalId | string | ❌ | Tu ID de pedido/referencia interna |
callbackUrl | string | ❌ | URL para webhooks y redirección después del pago |
ttlSeconds | integer | ❌ | Tiempo de vida en segundos (por defecto: 1800) |
allowUserSelection | boolean | ❌ | Permitir al cliente elegir la criptomoneda (por defecto: false) |
confirmations | integer | ❌ | Confirmaciones blockchain requeridas |
isTestnet | boolean | ❌ | Usar modo testnet (por defecto: false) |
idempotencyKey | string | ❌ | Clave única para prevenir pagos duplicados |
curl -X POST https://api.sanpay.io/api/v1/payments \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"fiatAmount": 99.99,
"fiatCurrency": "USD",
"chainId": "BTC",
"externalId": "order_12345",
"callbackUrl": "https://example.com/webhook"
}' 201 Created {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "PENDING_ASSIGNMENT",
"requestedFiatAmount": 99.99,
"fiatCurrency": "USD",
"requestedCryptoAmount": "0.00250000",
"coinSymbol": "BTC",
"address": null,
"qrData": null,
"paymentUrl": "https://pay.sanpay.io/p/a1b2c3d4",
"fxRateUsed": "39996.00",
"fxLockedAt": "2024-01-15T11:00:00Z",
"fxLockedUntil": "2024-01-15T11:30:00Z",
"expiresAt": "2024-01-15T11:30:00Z",
"createdAt": "2024-01-15T11:00:00Z"
} Get Payment
GET /payments/{paymentId}
Retrieve a payment by ID.
Response200 OK {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "CONFIRMED",
"requestedFiatAmount": 99.99,
"fiatCurrency": "USD",
"requestedCryptoAmount": "0.00250000",
"coinSymbol": "BTC",
"address": "bc1qxy2kgdygjrsqvzq3rj...",
"txHash": "abc123def456...",
"confirmations": 6,
"confirmedAt": "2024-01-15T11:15:00Z",
"createdAt": "2024-01-15T11:00:00Z"
} Payment Statuses
| Status | Description |
|---|---|
PENDING_ASSIGNMENT | Pago creado, esperando asignación de dirección |
PENDING_SELECTION | Esperando que el cliente seleccione la criptomoneda (selección diferida) |
AWAITING_PAYMENT | Dirección asignada, esperando que el cliente envíe los fondos |
DETECTED | Transacción vista en la mempool (0 confirmaciones) |
PARTIALLY_PAID | Pago parcial recibido, esperando el restante |
CONFIRMING | Transacción recibida, esperando las confirmaciones requeridas |
CONFIRMED | Pago confirmado con las confirmaciones blockchain requeridas |
CONFIRMED_PARTIAL | Importe parcial confirmado |
SETTLED | Pago completamente procesado — estado final de éxito |
PARTIALLY_SETTLED | Importe parcial liquidado |
EXPIRED | Ventana de pago expirada sin pago |
EXPIRED_PARTIAL | Ventana de pago expirada con pago parcial recibido |
CANCELLED | Pago cancelado por el comerciante |
REFUND_PENDING_ADDRESS | Reembolso iniciado, esperando dirección de reembolso del cliente |
REFUND_PROCESSING | Transacción de reembolso en proceso |
REFUNDED | Reembolso completado exitosamente |
REFUND_FAILED | Transacción de reembolso fallida |
REFUND_EXPIRED | Recolección de dirección de reembolso expirada |
REORG_DETECTED | Reorganización blockchain detectada — pago en revisión |
List Payments
GET /payments
List all payments for your account.
Query Parameters| Field | Type | Description |
|---|---|---|
status | string | Filter by status |
externalId | string | Filter by order ID |
since | string | ISO 8601 timestamp |
until | string | ISO 8601 timestamp |
limit | integer | Max results (default: 20, max: 100) |
offset | integer | Pagination offset |
200 OK {
"data": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "SETTLED",
"requestedFiatAmount": 99.99,
"fiatCurrency": "USD",
"requestedCryptoAmount": "0.00250000",
"coinSymbol": "BTC",
"createdAt": "2024-01-15T11:00:00Z"
}
],
"pagination": {
"total": 156,
"limit": 20,
"offset": 0
}
} Checkout Sessions
Create Checkout Session
POST /checkout/sessions
Create a hosted checkout session.
Request Body| Field | Type | Required | Description |
|---|---|---|---|
fiatAmount | number | ✅ | Importe en moneda fiat |
fiatCurrency | string | ✅ | Código de moneda fiat: USD, EUR, etc. |
description | string | ❌ | Descripción del pedido |
externalId | string | ❌ | Tu ID de pedido interno para correlación |
successUrl | string | ✅ | URL de redirección en caso de éxito |
cancelUrl | string | ✅ | URL de redirección en caso de cancelación |
customerEmail | string | ❌ | Pre-rellenar email del cliente |
ttlSeconds | integer | ❌ | Tiempo de vida en segundos (por defecto: 1800) |
201 Created {
"id": "cs_xyz789",
"url": "https://checkout.sanpay.io/cs_xyz789",
"expiresAt": "2024-01-15T12:00:00Z"
} Refunds
Create Refund
POST /payments/{paymentId}/refunds
Record a refund for a payment.
Request Body| Field | Type | Required | Description |
|---|---|---|---|
amount | string | ✅ | Refund amount in crypto |
txHash | string | ❌ | Refund transaction hash |
reason | string | ❌ | Reason for refund |
201 Created {
"id": "ref_abc123",
"paymentId": "pay_abc123",
"amount": "0.00250000",
"currency": "BTC",
"txHash": "abc123...",
"status": "completed",
"createdAt": "2024-01-16T10:00:00Z"
} Webhooks
Payment events are delivered to your webhook URL:
| Event | Description |
|---|---|
PAYMENT_CREATED | Solicitud de pago creada |
PAYMENT_ADDRESS_ASSIGNED | Dirección crypto asignada al pago |
PAYMENT_DETECTED | Transacción vista en la mempool |
PAYMENT_CONFIRMATION_UPDATE | Conteo de confirmaciones actualizado |
PAYMENT_CONFIRMED | Pago alcanzó las confirmaciones requeridas |
PAYMENT_SETTLED | Pago completamente liquidado — evento final de éxito |
PAYMENT_EXPIRED | Ventana de pago expirada |
PAYMENT_EXPIRED_PARTIAL | Pago expirado con importe parcial recibido |
PAYMENT_CANCELLED | Pago cancelado por el comerciante |
PAYMENT_REFUND_INITIATED | Proceso de reembolso iniciado |
PAYMENT_REFUND_COMPLETED | Reembolso completado exitosamente |
PAYMENT_REFUND_FAILED | Transacción de reembolso fallida |
PAYMENT_TX_UNMATCHED | Transacción recibida pero no coincidente |
PAYMENT_REORG | Reorganización blockchain detectada |
PAYMENT_LATE_DETECTED | Pago detectado después de la expiración |
Webhook Payload
{
"eventId": "evt-a1b2c3d4-e5f6",
"eventType": "PAYMENT_CONFIRMED",
"timestamp": "2024-01-15T11:15:00Z",
"tenantId": "your-tenant-id",
"paymentRequestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"address": "bc1qxy2kgdygjrsqvzq3rj...",
"coinSymbol": "BTC",
"chainId": "BTC",
"status": "CONFIRMED",
"confirmations": 6,
"requiredConfirmations": 3,
"requestedFiatAmount": 99.99,
"fiatCurrency": "USD",
"requestedCryptoAmount": "0.00250000"
} Error Responses
{
"error": {
"code": "PAYMENT_EXPIRED",
"message": "The payment request has expired",
"details": {
"paymentId": "pay_abc123",
"expiredAt": "2024-01-15T11:30:00Z"
}
}
} Payment Error Codes
| Code | Status | Description |
|---|---|---|
PAYMENT_NOT_FOUND | 404 | Payment does not exist |
PAYMENT_EXPIRED | 400 | Payment has expired |
INVALID_AMOUNT | 400 | Invalid amount format |
UNSUPPORTED_CURRENCY | 400 | Currency not supported |
RATE_LIMITED | 429 | Too many requests |
Cancelar Pago
POST /payments/{paymentId}/cancel
Cancela una solicitud de pago. Solo se pueden cancelar pagos con estado `AWAITING_PAYMENT` o `PENDING_SELECTION`. Los pagos con transacciones detectadas no pueden cancelarse.
Response200 OK {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "CANCELLED",
"cancelledAt": "2026-01-15T11:20:00Z",
"reason": "Solicitud de cancelación del cliente"
} Obtener Sesión de Checkout
GET /checkout/sessions/{sessionId}
Recupera una sesión de checkout por ID.
Response200 OK {
"id": "cs_xyz789",
"status": "completed",
"paymentId": "pay_abc123",
"amount": "99.99",
"currency": "USD",
"successUrl": "https://example.com/success",
"cancelUrl": "https://example.com/cancel",
"expiresAt": "2024-01-15T12:00:00Z",
"createdAt": "2024-01-15T11:00:00Z"
} Expirar Sesión de Checkout
DELETE /checkout/sessions/{sessionId}
Expira una sesión de checkout inmediatamente. La sesión ya no podrá utilizarse para el pago.
Response200 OK {
"id": "cs_xyz789",
"status": "expired",
"expiredAt": "2024-01-15T11:30:00Z"
} Listar Reembolsos
GET /payments/{paymentId}/refunds
Lista todos los reembolsos de un pago específico.
Response200 OK {
"data": [
{
"id": "ref_abc123",
"paymentId": "pay_abc123",
"amount": "0.00125000",
"currency": "BTC",
"status": "completed",
"reason": "Solicitud del cliente",
"createdAt": "2024-01-16T10:00:00Z"
}
],
"pagination": {
"total": 1,
"limit": 20,
"offset": 0
}
} Obtener Reembolso
GET /refunds/{refundId}
Recupera un reembolso por ID.
Response200 OK {
"id": "ref_abc123",
"paymentId": "pay_abc123",
"amount": "0.00125000",
"currency": "BTC",
"txHash": "abc123def456...",
"status": "completed",
"reason": "Solicitud del cliente",
"createdAt": "2024-01-16T10:00:00Z"
} Enlaces de Pago
Los enlaces de pago son URLs reutilizables que permiten a los clientes pagar cualquier monto. Ideales para donaciones, propinas o facturas.
Crear Enlace de Pago
POST /payment-links
Crea un enlace de pago reutilizable.
Request Body| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
name | string | ✅ | Nombre mostrado para el enlace de pago |
amount | string | ❌ | Monto fijo (si se omite, el cliente ingresa el monto) |
currency | string | ✅ | Código de moneda fiat: USD, EUR, etc. |
description | string | ❌ | Descripción mostrada al cliente |
redirectUrl | string | ❌ | URL de redirección después del pago |
metadata | object | ❌ | Pares clave-valor personalizados |
201 Created {
"id": "plink_abc123",
"name": "Suscripción Premium",
"url": "https://pay.sanpay.io/l/plink_abc123",
"amount": "99.99",
"currency": "USD",
"active": true,
"createdAt": "2024-01-15T11:00:00Z"
} Listar Enlaces de Pago
GET /payment-links
Lista todos los enlaces de pago de tu cuenta.
Query Parameters| Campo | Tipo | Descripción |
|---|---|---|
active | boolean | Filtrar por estado activo |
limit | integer | Máx resultados (default: 20, max: 100) |
offset | integer | Offset de paginación |
200 OK {
"data": [
{
"id": "plink_abc123",
"name": "Suscripción Premium",
"url": "https://pay.sanpay.io/l/plink_abc123",
"amount": "99.99",
"currency": "USD",
"active": true,
"paymentsCount": 42,
"totalCollected": "4199.58",
"createdAt": "2024-01-15T11:00:00Z"
}
],
"pagination": {
"total": 5,
"limit": 20,
"offset": 0
}
} Obtener Enlace de Pago
GET /payment-links/{linkId}
Recupera un enlace de pago por ID.
Desactivar Enlace de Pago
DELETE /payment-links/{linkId}
Desactiva un enlace de pago. La URL del enlace ya no aceptará pagos.
Response200 OK {
"id": "plink_abc123",
"active": false,
"deactivatedAt": "2024-01-20T15:00:00Z"
} Tipos de Cambio
Obtén tipos de cambio de criptomonedas en tiempo real.
Obtener Tipos de Cambio
GET /exchange-rates
Recupera los tipos de cambio actuales para criptomonedas soportadas.
Query Parameters| Campo | Tipo | Descripción |
|---|---|---|
base | string | Moneda fiat base (default: USD) |
symbols | string | Símbolos crypto separados por coma (ej. BTC,ETH) |
200 OK {
"base": "USD",
"timestamp": "2024-01-15T11:00:00Z",
"rates": {
"BTC": "0.000025",
"ETH": "0.00045",
"USDT": "1.0001",
"USDC": "0.9999"
}
} Cuenta
Obtener Saldo de Cuenta
GET /account/balance
Recupera los saldos actuales de tu cuenta para todas las criptomonedas.
Response200 OK {
"balances": [
{
"currency": "BTC",
"available": "1.25000000",
"pending": "0.05000000",
"total": "1.30000000"
},
{
"currency": "ETH",
"available": "15.50000000",
"pending": "0.00000000",
"total": "15.50000000"
}
],
"updatedAt": "2024-01-15T11:00:00Z"
} Límites de Tasa
Las solicitudes API tienen límites de tasa para garantizar un uso justo. La información de límites se incluye en los headers de respuesta.
Headers de Límite de Tasa
| Header | Descripción |
|---|---|
X-RateLimit-Limit | Solicitudes máximas permitidas por ventana |
X-RateLimit-Remaining | Solicitudes restantes en la ventana actual |
X-RateLimit-Reset | Timestamp Unix cuando se reinicia la ventana |
Límites de Tasa Predeterminados
| Endpoint | Límite | Ventana |
|---|---|---|
| POST /payments | 100 | 1 minuto |
| GET /payments | 300 | 1 minuto |
| POST /checkout/sessions | 100 | 1 minuto |
| GET /exchange-rates | 60 | 1 minuto |
Idempotencia
Usa claves de idempotencia para repetir solicitudes de forma segura sin riesgo de operaciones duplicadas.
Uso de Claves de Idempotencia
Incluye un campo `idempotencyKey` en el cuerpo de la solicitud con un valor único (ej. UUID) para solicitudes POST:
curl -X POST https://api.sanpay.io/api/v1/payments \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"fiatAmount": 99.99, "fiatCurrency": "USD", "idempotencyKey": "550e8400-e29b-41d4-a716-446655440000"}' Comportamiento de Idempotencia
- Las claves son válidas por 24 horas desde la primera solicitud
- Solicitudes subsiguientes con la misma clave retornan la respuesta original
- Las claves están asociadas a tu API key
- Recomendamos usar UUID v4 para claves de idempotencia
Ejemplos de SDK
Aquí hay ejemplos de creación de pago en lenguajes de programación populares:
Node.js
const response = await fetch('https://api.sanpay.io/api/v1/payments', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.SANPAY_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
fiatAmount: 99.99,
fiatCurrency: 'USD',
externalId: 'ORDER-12345',
description: 'Plan Premium'
})
});
const payment = await response.json(); Python
import requests
response = requests.post(
'https://api.sanpay.io/api/v1/payments',
headers={
'Authorization': f'Bearer {os.environ["SANPAY_API_KEY"]}',
'Content-Type': 'application/json'
},
json={
'fiatAmount': 99.99,
'fiatCurrency': 'USD',
'externalId': 'ORDER-12345',
'description': 'Plan Premium'
}
)
payment = response.json() PHP
$ch = curl_init('https://api.sanpay.io/api/v1/payments');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('SANPAY_API_KEY'),
'Content-Type: application/json'
],
CURLOPT_POSTFIELDS => json_encode([
'fiatAmount' => 99.99,
'fiatCurrency' => 'USD',
'externalId' => 'ORDER-12345',
'description' => 'Plan Premium'
])
]);
$payment = json_decode(curl_exec($ch), true);