CubaPayDocs
Volver

API de CubaPay

Cobra por Transfermóvil y recibe confirmación automática cuando el pago llega. Sin botones de “ya pagué”: la confirmación se detecta por SMS bancario.

1. Cómo funciona

  1. Tu servidor crea una orden con el monto en CUP.
  2. CubaPay devuelve un importe exacto (con centavos únicos) y la cuenta receptora del comercio.
  3. El cliente transfiere ese importe exacto por Transfermóvil.
  4. Un terminal recibe el SMS del banco, lo valida y lo reporta firmado.
  5. CubaPay empareja el pago por cuenta + importe y marca la orden como paid.
  6. Recibes un webhook firmado y/o consultas el estado de la orden.

2. Autenticación

Todas las llamadas usan tu clave API en el encabezado Authorization. Las claves cbp_live_ son de producción y cbp_test_ de prueba. Nunca las publiques en código del navegador.

Authorization: Bearer cbp_live_tu_clave_secreta

3. Integra CubaPay en tu web (paso a paso)

La integración tiene solo tres piezas, igual que servicios como Supabase o Stripe: tu clave secreta vive en tu servidor, tu página muestra el pago, y CubaPay confirma automáticamente. Nunca pongas la clave cbp_live_ en el código del navegador.

Paso 1 — Guarda tu clave en el servidor

Pide tu clave en el panel (sección Claves API) y guárdala como variable de entorno. Así el navegador nunca la ve.

# .env de tu proyecto
CUBAPAY_API_KEY=cbp_live_tu_clave_secreta
CUBAPAY_BASE_URL=https://cubapay-api.com

Paso 2 — Crea la orden desde TU backend

Tu página web le pide a tu propio servidor que cree el cobro; tu servidor llama a CubaPay con la clave secreta y te devuelve el importe exacto y la cuenta. Ejemplo con Node/Next.js:

// POST /api/checkout  (en TU servidor)
export async function POST(req) {
  const { amount, reference } = await req.json()

  const r = await fetch(process.env.CUBAPAY_BASE_URL + '/api/v1/orders', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer ' + process.env.CUBAPAY_API_KEY,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ amount, reference }),
  })

  const order = await r.json()
  // Devuelve al navegador SOLO lo necesario (nunca la clave)
  return Response.json(order)
}

Con PHP o cualquier otro lenguaje es lo mismo: un POST con el encabezado Authorization: Bearer ....

<?php
// checkout.php (en TU servidor)
$body = json_encode(['amount' => 100, 'reference' => 'pedido-123']);
$ch = curl_init(getenv('CUBAPAY_BASE_URL') . '/api/v1/orders');
curl_setopt_array($ch, [
  CURLOPT_POST => true,
  CURLOPT_POSTFIELDS => $body,
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => [
    'Authorization: Bearer ' . getenv('CUBAPAY_API_KEY'),
    'Content-Type: application/json',
  ],
]);
$order = curl_exec($ch);
header('Content-Type: application/json');
echo $order;

Paso 3 — Muestra el pago y espera la confirmación

En el navegador llamas a TU endpoint, muestras el importe exacto y la cuenta, y consultas el estado cada 2–3 segundos hasta que sea paid. No hace falta botón de “ya pagué”: se confirma solo.

// En el navegador (tu página de checkout)
async function iniciarPago() {
  // 1. Crear la orden a través de TU backend
  const res = await fetch('/api/checkout', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ amount: 100, reference: 'pedido-123' }),
  })
  const order = await res.json()

  // 2. Mostrar al cliente cuánto pagar y a qué cuenta
  mostrarEnPantalla(
    order.payable_amount_cup + ' CUP',   // importe EXACTO
    order.destination_account            // cuenta receptora
  )

  // 3. Consultar el estado hasta que se pague
  const intervalo = setInterval(async () => {
    const r = await fetch('/api/checkout/' + order.id) // proxy a GET /api/v1/orders/:id
    const estado = await r.json()
    if (estado.status === 'paid') {
      clearInterval(intervalo)
      alert('¡Pago confirmado! ✅')
    } else if (estado.status === 'expired') {
      clearInterval(intervalo)
      alert('La orden expiró, inténtalo de nuevo.')
    }
  }, 2500)
}

El endpoint /api/checkout/:id de tu servidor solo reenvía la consulta a GET /api/v1/orders/:id con tu clave, igual que en el Paso 2.

💡 ¿Prefieres no hacer polling? Configura un webhook (ver más abajo) y CubaPay le avisa a tu servidor en el instante en que llega el pago.

4. Crear una orden

POST /api/v1/orders

curl -X POST https://cubapay-api.com/api/v1/orders \
  -H "Authorization: Bearer cbp_live_tu_clave" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 100, "reference": "pedido-123" }'

Respuesta:

{
  "id": "b1d2...-uuid",
  "reference": "pedido-123",
  "status": "pending",
  "mode": "live",
  "amount_cup": 100,
  "payable_amount_cup": 100.37,
  "payable_amount_cents": 10037,
  "destination_account": "9224069998586123",
  "expires_at": "2026-09-16T15:00:00Z"
}

Muestra payable_amount_cup (importe exacto a transferir) y destination_account a tu cliente. El importe lleva centavos únicos para poder emparejar el pago sin ambigüedad.

5. Consultar el estado

GET /api/v1/orders/:id

curl https://cubapay-api.com/api/v1/orders/b1d2...-uuid \
  -H "Authorization: Bearer cbp_live_tu_clave"

Respuesta cuando el pago llega:

{ "id": "b1d2...-uuid", "status": "paid", "paid_at": "2026-09-16T14:32:10Z", ... }

Estados posibles: pending, paid, expired. Puedes hacer polling cada 2–3 segundos mientras esté pending.

6. Listar pagos

GET /api/v1/payments?status=paid&limit=50

curl "https://cubapay-api.com/api/v1/payments?status=paid" \
  -H "Authorization: Bearer cbp_live_tu_clave"

7. Webhooks

Cuando una orden pasa a paid, enviamos un POST a tu URL configurada con este cuerpo:

{
  "event": "payment.confirmed",
  "data": {
    "id": "b1d2...-uuid",
    "reference": "pedido-123",
    "amountCents": 10037,
    "status": "paid",
    "operationId": "...",
    "paidAt": "2026-09-16T14:32:10Z"
  }
}

Encabezados: X-CubaPay-Signature, X-CubaPay-Timestamp, X-CubaPay-Event. Verifica la firma así:

import crypto from 'crypto'

function verify(rawBody, signature, timestamp, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(timestamp + '.' + rawBody)
    .digest('hex')
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))
}

8. Códigos de error

  • 401 unauthorized — clave ausente, inválida o revocada.
  • 403 merchant_inactive — el comercio está desactivado.
  • 400 invalid_amount — monto fuera de rango (1 a 1,000,000 CUP).
  • 429 rate_limited — límite de 120 peticiones/minuto por clave.

¿Listo para producción? Solicita una clave cbp_live_ al administrador de la plataforma.