Contenido
  1. 1Introducción
  2. 2Inicio rápido
  3. 3Autenticación
  4. 4Crear un pago
  5. 5Mostrar QR / checkout
  6. 6Idempotencia
  7. 7Consultar un pago
  8. 8Listar pagos
  9. 9Estados de pago
  10. 10Webhooks
  11. 11Verificación HMAC
  12. 12TEST y LIVE
  13. 13Errores HTTP
  14. 14Checklist de integración

Guía de integración

Integra cobros con QR en tu sistema

Tu sistema (ERP, tienda, membresías...) crea cobros con una llamada a nuestra API, muestra a su cliente el QR o el enlace de pago y recibe la confirmación cuando se paga.

Solo hablas con nuestra API. Los pagos se procesan mediante TAYPI a través de nuestra integración: tu sistema nunca se conecta con TAYPI ni recibe sus credenciales.

Lo que te entregamos

DatoEjemploPara qué
URL basehttps://pagos.sentra.com.peTodas las peticiones
API Keysk_live_TU_API_KEYAutenticar cada petición (secreta)
Secreto del webhookwhsec_TU_SECRETOVerificar los avisos que te enviamos (secreto)
Ambientetest o liveDónde cobra tu cuenta (ver TEST y LIVE)

La API Key y el secreto se muestran una sola vez al generarlos. Guárdalos en la configuración segura de tu servidor, nunca en código de navegador ni de apps móviles. Si se filtran, pídenos unos nuevos.

Inicio rápido

Dos recorridos, y en ambos tu sistema solo conversa con nuestra API:

1 · Crear el cobro

2 · Confirmar el pago

  1. Crea el pago con POST /api/v1/payments, tu X-API-Key y una Idempotency-Key.
  2. Muestra el cobro a tu cliente: el checkout_url o el QR.
  3. Recibe el webhook payment.completed, verifica su firma y marca tu orden como pagada.
  4. Si dudas, consulta el pago con GET /api/v1/payments/{payment_id}.

Autenticación

Envía tu API Key en la cabecera X-API-Key de cada petición, siempre por HTTPS y siempre desde tu servidor.

Cabecera
X-API-Key: sk_live_TU_API_KEY

Todas las respuestas tienen la forma {"success": bool, "message": str, "data": ...} e incluyen la cabecera X-Request-ID: indícala si nos reportas un problema.

Las claves empiezan siempre por sk_live_, también en pruebas: el prefijo no indica el ambiente.

Crear un pago

POST/api/v1/payments

POST Crear un pago
curl -X POST "https://pagos.sentra.com.pe/api/v1/payments" \
  -H "X-API-Key: sk_live_TU_API_KEY" \
  -H "Idempotency-Key: ORD-12345" \
  -H "Content-Type: application/json" \
  -d '{"amount": "25.00", "reference": "ORD-12345", "description": "Pedido #12345"}'
CampoObligatorioDescripción
amountSíTexto con hasta 2 decimales: "25.00". Mínimo 1.00.
referenceSíTu identificador del cobro (orden, factura...). Hasta 100 caracteres.
currencyNoSolo PEN (por defecto).
descriptionNoHasta 255 caracteres.
metadataNoHasta 20 pares clave-valor de texto, para tu uso.

Cualquier otro campo se rechaza. Respuesta 201:

201 Respuesta 201
{
  "success": true,
  "message": "Pago creado correctamente",
  "data": {
    "id": "pay_xxxxx",
    "status": "pending",
    "amount": "25.00",
    "currency": "PEN",
    "reference": "ORD-12345",
    "description": "Pedido #12345",
    "metadata": {},
    "qr_code": "000201...",
    "qr_image": "data:image/svg+xml;base64,...",
    "checkout_url": "https://...",
    "expires_at": "2026-10-18T15:15:00-05:00",
    "paid_at": null,
    "payer": null,
    "failure_code": null,
    "environment": "live",
    "created_at": "2026-10-18T15:00:00-05:00",
    "updated_at": "2026-10-18T15:00:00-05:00"
  }
}

Guarda data.id: es el payment_id del pago en esta API.

Mostrar QR / checkout

El pago nace en pending. Estos campos de la respuesta te sirven para cobrar y conciliar:

checkout_url
Página de pago lista para abrir o compartir. Muestra el QR, el monto y la referencia.
qr_code
Contenido del QR, para dibujarlo con tu propia librería.
qr_image
El QR ya dibujado como imagen data:, para un <img src="...">.
expires_at
Vencimiento del QR. Usa siempre el valor que devuelve la API: no asumas una duración fija. Si vence sin pagarse, el pago pasa a expired y creas uno nuevo.
id (payment_id)
Identificador del pago en esta API (pay_xxxxx). Es el que usas para consultarlo y el que llega en los webhooks como payment_id.
reference
Tu propio identificador, tal como lo enviaste. Te sirve para encontrar tu orden.
environment
test o live. Ver TEST y LIVE.

Es un QR interoperable procesado mediante TAYPI: se paga con Yape, Plin o la app de cualquier banco conectado a la CCE. Solo soles.

Idempotencia

La cabecera Idempotency-Key es obligatoria al crear un pago (1 a 255 caracteres visibles, sin espacios; por ejemplo el id de tu orden). Si se corta la conexión, hay un timeout o recibes 503, repite la petición con la misma clave y los mismos datos: obtienes el mismo pago, nunca uno nuevo.

SituaciónRespuesta
Primera vez201 con el pago nuevo
Misma clave y mismos datos200, el mismo pago y la cabecera Idempotent-Replayed: true
Misma clave con otros datos422: usa otra clave para otro cobro
Misma clave con la primera aún en curso409: reintenta en unos segundos

Consultar un pago

GET/api/v1/payments/{payment_id}

GET Consultar un pago
curl "https://pagos.sentra.com.pe/api/v1/payments/pay_xxxxx" \
  -H "X-API-Key: sk_live_TU_API_KEY"

Devuelve el pago con la misma forma que al crearlo. Solo ves tus propios pagos: un id ajeno responde 404.

Listar pagos

GET/api/v1/payments

GET Listar pagos
curl "https://pagos.sentra.com.pe/api/v1/payments?status=completed&reference=ORD-12345&limit=50&offset=0" \
  -H "X-API-Key: sk_live_TU_API_KEY"

Filtros opcionales: status y reference. Paginación con limit y offset.

Estados de pago

EstadoSignificado
pendingQR emitido, esperando el pago
completedPagado y confirmado; trae paid_at y payer
expiredEl QR venció sin pagarse
failedRechazado; ver failure_code (GATEWAY_UNCONFIRMED: la pasarela nunca confirmó el cobro; crea otro pago)
cancelledCancelado antes de pagarse
createdRegistrado pero sin confirmar con la pasarela: repite el POST con la misma Idempotency-Key

completed es definitivo. Un pago expired o failed solo puede pasar después a completed si la pasarela confirma el cobro.

Webhooks

Si activamos el webhook de tu cuenta (nos das una URL https pública de tu sistema), te enviamos un POST cuando uno de tus pagos llega a payment.completed, payment.failed, payment.expired o payment.cancelled. Así no necesitas consultar el pago en bucle.

POST Petición que recibes
POST https://tu-sistema.com/webhooks/pagos
Content-Type: application/json; charset=utf-8
X-Pagos-Event-Id:   evt_xxxxx
X-Pagos-Event-Type: payment.completed
X-Pagos-Timestamp:  1760886245
X-Pagos-Signature:  v1=5f2b...
X-Pagos-Attempt:    1
CabeceraContenido
X-Pagos-Event-IdId único del evento (evt_...), igual a id del cuerpo
X-Pagos-Event-TypeTipo de evento, igual a type del cuerpo
X-Pagos-TimestampSegundos Unix del envío
X-Pagos-Signaturev1= + firma HMAC-SHA256 (ver Verificación HMAC)
X-Pagos-AttemptNúmero de intento: 1, 2, ...

Cuerpo (se envía compacto, en una línea; aquí formateado):

Cuerpo del evento
{
  "created_at": "2026-10-19T15:04:05.123456+00:00",
  "data": {
    "amount": "25.00",
    "currency": "PEN",
    "environment": "live",
    "expires_at": "2026-10-19T15:18:00+00:00",
    "failure_code": null,
    "metadata": {},
    "paid_at": "2026-10-19T15:04:01+00:00",
    "payment_id": "pay_xxxxx",
    "reference": "ORD-12345",
    "status": "completed"
  },
  "environment": "live",
  "id": "evt_xxxxx",
  "type": "payment.completed"
}

Responder y reintentos

  • Responde 2xx en menos de 10 segundos. Si tienes trabajo pesado, encólalo y responde ya.
  • Cualquier otra respuesta, un timeout o un error de red se reintenta tras 1 min, 5 min, 15 min, 30 min, 1 h, 2 h, 4 h, 8 h y 12 h: 10 intentos en total. Las redirecciones no se siguen.
  • Por los reintentos, un evento puede llegar más de una vez: deduplica por id.
  • Si dudas de un evento, confírmalo con GET /api/v1/payments/{payment_id}.
  • El cuerpo nunca incluye credenciales, API Keys ni secretos.

Verificación HMAC

Firma
X-Pagos-Signature: v1=HMAC-SHA256(webhook_secret, "<timestamp>.<raw_body>")
  1. Lee el cuerpo crudo (los bytes exactos), antes de parsear el JSON.
  2. Rechaza si X-Pagos-Timestamp difiere más de 5 minutos de tu reloj.
  3. Calcula el HMAC con tu secreto y compáralo en tiempo constante con cada valor v1=.
  4. Si no coincide, responde 400 o 401 y no proceses nada.
  5. Usa id para no procesar dos veces el mismo evento.
Python
import hashlib, hmac, time

WEBHOOK_SECRET = "whsec_TU_SECRETO"  # desde tu configuracion segura

def verificar(cuerpo: bytes, firma: str, timestamp: str, secreto: str) -> bool:
    if abs(time.time() - int(timestamp)) > 300:
        return False
    esperada = hmac.new(secreto.encode(), timestamp.encode() + b"." + cuerpo, hashlib.sha256).hexdigest()
    return any(hmac.compare_digest(esperada, p[3:]) for p in firma.split(",") if p.startswith("v1="))
Node.js
// Express: express.raw({ type: "application/json" }) en esta ruta
const crypto = require("crypto");
const WEBHOOK_SECRET = "whsec_TU_SECRETO"; // desde tu configuracion segura

function verificar(cuerpo, firma, timestamp, secreto) {
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
  const esperada = crypto.createHmac("sha256", secreto).update(`${timestamp}.`).update(cuerpo).digest("hex");
  return firma.split(",").some(p => p.startsWith("v1=") && p.length === 67 &&
    crypto.timingSafeEqual(Buffer.from(p.slice(3)), Buffer.from(esperada)));
}
PHP
<?php
// $cuerpo = file_get_contents('php://input');
const WEBHOOK_SECRET = 'whsec_TU_SECRETO'; // desde tu configuracion segura

function verificar(string $cuerpo, string $firma, string $timestamp, string $secreto): bool {
    if (abs(time() - (int) $timestamp) > 300) return false;
    $esperada = hash_hmac('sha256', $timestamp . '.' . $cuerpo, $secreto);
    foreach (explode(',', $firma) as $parte) {
        if (str_starts_with($parte, 'v1=') && hash_equals($esperada, substr($parte, 3))) return true;
    }
    return false;
}

TEST y LIVE

La URL y la API Key son las mismas en ambos ambientes. El ambiente lo fija la configuración de tu cuenta, que administramos nosotros: las cuentas nuevas empiezan en test y pasan a live cuando tu integración está lista.

test

Cobros de prueba. No mueven dinero real.

live

Cobros reales.

Cada pago y cada webhook traen environment: compruébalo antes de dar una orden por pagada (por ejemplo, ignora en producción un evento test).

Errores HTTP

HTTPSignificadoQué hacer
400Datos inválidos o falta la Idempotency-KeyRevisa cuerpo y cabeceras
401API Key inválida o revocadaRevisa la cabecera o pide una clave nueva
403Cuenta o servicio no habilitadoContáctanos
404El pago no existe o no es tuyoRevisa el id
409Otra petición con la misma Idempotency-Key en cursoReintenta en unos segundos
422Clave reutilizada con otros datos, o cobro rechazadoUsa otra clave o corrige el monto
429Demasiadas peticionesEspera lo que indique Retry-After
503Pasarela o cuenta de cobro no disponibleReintenta con la misma Idempotency-Key

Checklist de integración

  • La API Key vive solo en el servidor y viaja en X-API-Key.
  • Cada cobro usa una Idempotency-Key estable (el id de tu orden) y los reintentos la repiten.
  • Muestras checkout_url o el QR, y usas el expires_at que devuelve la API.
  • El webhook verifica la firma sobre el cuerpo crudo y el timestamp, y deduplica por id.
  • Respondes 2xx rápido y procesas en segundo plano.
  • Compruebas environment antes de dar una orden por pagada.
  • Probaste en test antes de pedir el paso a live.