Ir al contenido

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
fiatAmountnumberImporte en moneda fiat (ej. 99.99)
fiatCurrencystringCódigo de moneda fiat: USD, EUR, etc.
coinIdstring (UUID)ID específico de la moneda. Si se omite con allowUserSelection: true, el cliente elige.
chainIdstringIdentificador de blockchain (ej. BTC, ETH)
metadataobjectPares clave-valor personalizados (retornados en webhooks)
externalIdstringTu ID de pedido/referencia interna
callbackUrlstringURL para webhooks y redirección después del pago
ttlSecondsintegerTiempo de vida en segundos (por defecto: 1800)
allowUserSelectionbooleanPermitir al cliente elegir la criptomoneda (por defecto: false)
confirmationsintegerConfirmaciones blockchain requeridas
isTestnetbooleanUsar modo testnet (por defecto: false)
idempotencyKeystringClave única para prevenir pagos duplicados
Example
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"
  }'
Response 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.

Response 200 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_ASSIGNMENTPago creado, esperando asignación de dirección
PENDING_SELECTIONEsperando que el cliente seleccione la criptomoneda (selección diferida)
AWAITING_PAYMENTDirección asignada, esperando que el cliente envíe los fondos
DETECTEDTransacción vista en la mempool (0 confirmaciones)
PARTIALLY_PAIDPago parcial recibido, esperando el restante
CONFIRMINGTransacción recibida, esperando las confirmaciones requeridas
CONFIRMEDPago confirmado con las confirmaciones blockchain requeridas
CONFIRMED_PARTIALImporte parcial confirmado
SETTLEDPago completamente procesado — estado final de éxito
PARTIALLY_SETTLEDImporte parcial liquidado
EXPIREDVentana de pago expirada sin pago
EXPIRED_PARTIALVentana de pago expirada con pago parcial recibido
CANCELLEDPago cancelado por el comerciante
REFUND_PENDING_ADDRESSReembolso iniciado, esperando dirección de reembolso del cliente
REFUND_PROCESSINGTransacción de reembolso en proceso
REFUNDEDReembolso completado exitosamente
REFUND_FAILEDTransacción de reembolso fallida
REFUND_EXPIREDRecolección de dirección de reembolso expirada
REORG_DETECTEDReorganización blockchain detectada — pago en revisión

List Payments

GET /payments

List all payments for your account.

Query Parameters
Field Type Description
statusstringFilter by status
externalIdstringFilter by order ID
sincestringISO 8601 timestamp
untilstringISO 8601 timestamp
limitintegerMax results (default: 20, max: 100)
offsetintegerPagination offset
Response 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
fiatAmountnumberImporte en moneda fiat
fiatCurrencystringCódigo de moneda fiat: USD, EUR, etc.
descriptionstringDescripción del pedido
externalIdstringTu ID de pedido interno para correlación
successUrlstringURL de redirección en caso de éxito
cancelUrlstringURL de redirección en caso de cancelación
customerEmailstringPre-rellenar email del cliente
ttlSecondsintegerTiempo de vida en segundos (por defecto: 1800)
Response 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
amountstringRefund amount in crypto
txHashstringRefund transaction hash
reasonstringReason for refund
Response 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_CREATEDSolicitud de pago creada
PAYMENT_ADDRESS_ASSIGNEDDirección crypto asignada al pago
PAYMENT_DETECTEDTransacción vista en la mempool
PAYMENT_CONFIRMATION_UPDATEConteo de confirmaciones actualizado
PAYMENT_CONFIRMEDPago alcanzó las confirmaciones requeridas
PAYMENT_SETTLEDPago completamente liquidado — evento final de éxito
PAYMENT_EXPIREDVentana de pago expirada
PAYMENT_EXPIRED_PARTIALPago expirado con importe parcial recibido
PAYMENT_CANCELLEDPago cancelado por el comerciante
PAYMENT_REFUND_INITIATEDProceso de reembolso iniciado
PAYMENT_REFUND_COMPLETEDReembolso completado exitosamente
PAYMENT_REFUND_FAILEDTransacción de reembolso fallida
PAYMENT_TX_UNMATCHEDTransacción recibida pero no coincidente
PAYMENT_REORGReorganización blockchain detectada
PAYMENT_LATE_DETECTEDPago 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_FOUND404Payment does not exist
PAYMENT_EXPIRED400Payment has expired
INVALID_AMOUNT400Invalid amount format
UNSUPPORTED_CURRENCY400Currency not supported
RATE_LIMITED429Too 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.

Response 200 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.

Response 200 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.

Response 200 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.

Response 200 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.

Response 200 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"
}

Los enlaces de pago son URLs reutilizables que permiten a los clientes pagar cualquier monto. Ideales para donaciones, propinas o facturas.

POST /payment-links

Crea un enlace de pago reutilizable.

Request Body
Campo Tipo Requerido Descripción
namestringNombre mostrado para el enlace de pago
amountstringMonto fijo (si se omite, el cliente ingresa el monto)
currencystringCódigo de moneda fiat: USD, EUR, etc.
descriptionstringDescripción mostrada al cliente
redirectUrlstringURL de redirección después del pago
metadataobjectPares clave-valor personalizados
Response 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"
}

GET /payment-links

Lista todos los enlaces de pago de tu cuenta.

Query Parameters
Campo Tipo Descripción
activebooleanFiltrar por estado activo
limitintegerMáx resultados (default: 20, max: 100)
offsetintegerOffset de paginación
Response 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
  }
}

GET /payment-links/{linkId}

Recupera un enlace de pago por ID.


DELETE /payment-links/{linkId}

Desactiva un enlace de pago. La URL del enlace ya no aceptará pagos.

Response 200 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
basestringMoneda fiat base (default: USD)
symbolsstringSímbolos crypto separados por coma (ej. BTC,ETH)
Response 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.

Response 200 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-LimitSolicitudes máximas permitidas por ventana
X-RateLimit-RemainingSolicitudes restantes en la ventana actual
X-RateLimit-ResetTimestamp Unix cuando se reinicia la ventana

Límites de Tasa Predeterminados

Endpoint Límite Ventana
POST /payments1001 minuto
GET /payments3001 minuto
POST /checkout/sessions1001 minuto
GET /exchange-rates601 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);