GatoPago Flow · API

Cobra en USDC desde tu servidor

Crea un cobro, comparte su link o QR y recibe un webhook firmado cuando se paga. Tu cliente paga desde GatoPago o desde cualquier wallet, y el dinero llega directo a tu cuenta: GatoPago nunca lo custodia.

Beta pública en redes de prueba: usa claves de prueba (sk_test_…) y USDC de prueba.

Primeros pasos

  1. Abre la consola con tu cuenta GatoPago y crea una clave API.
  2. Crea un cobro desde tu servidor y envía a tu cliente su checkout_url.
  3. Registra una URL de webhook y entrega el pedido cuando llegue payment_intent.succeeded.

Autenticación

Cada solicitud lleva tu clave: Authorization: Bearer sk_test_…. Con ella se crean cobros a tu nombre: guárdala en tu servidor, nunca en una app ni en una página web. Si se filtra, revócala en la consola y crea otra.

Crear un cobro

Solo amount es obligatorio: un decimal en USDC con hasta 6 decimales. description (hasta 200 caracteres) la ve quien paga; metadata (hasta 20 valores de texto) es para ti; expires_in va en segundos, de 5 minutos a 7 días (24 horas por defecto).

Solicitud
curl -X POST https://api.gatopago.com/v1/payment_intents \
  -H "Authorization: Bearer sk_test_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1042" \
  -d '{
    "amount": "18.00",
    "description": "Pedido 1042",
    "metadata": { "order_id": "1042" },
    "expires_in": 3600
  }'
Respuesta · 201
{
  "id": "pi_3f9c…",
  "object": "payment_intent",
  "amount": "18.00",
  "currency": "USDC",
  "status": "requires_payment",
  "description": "Pedido 1042",
  "metadata": { "order_id": "1042" },
  "checkout_url": "https://gatopago.com/pay/pi_3f9c…",
  "expires_at": 1767229200,
  "created_at": 1767225600,
  "livemode": false,
  "payment": null
}

Reintenta sin miedo: con la misma Idempotency-Key y los mismos datos recibes el mismo cobro, nunca uno segundo. Si la respuesta se perdió, repite la solicitud antes de crear otro.

Estados

requires_payment
Esperando el pago.
processing
Pagado desde otra red; Circle está entregando los USDC en tu red.
succeeded
Pagado: el dinero ya está en tu cuenta.
canceled
Lo cancelaste antes de que se pagara.
expired
Venció sin pagarse (por defecto, a las 24 horas).

En redes de prueba, POST /v1/payment_intents/:id/simulate marca un cobro como pagado, así pruebas tu webhook sin pagar.

Webhooks

Registra tu URL en la consola. GatoPago envía cada evento con un POST y reintenta las entregas fallidas hasta 10 veces; responde con un 2xx apenas lo recibas.

Evento
{
  "id": "evt_…",
  "object": "event",
  "type": "payment_intent.succeeded",
  "created_at": 1767226000,
  "data": { "id": "pi_3f9c…", "status": "succeeded", "amount": "18.00", "…": "…" }
}
payment_intent.created
Se creó un cobro.
payment_intent.processing
Se pagó desde otra red y está en camino.
payment_intent.succeeded
Se pagó: entrega el pedido.
payment_intent.duplicate_payment
Llegó un segundo pago para un cobro ya pagado.
payment_intent.canceled
Se canceló.
payment_intent.expired
Venció sin pagarse.

Verifica cada evento. El encabezado GatoPago-Signature es t=<unix>,v1=<hex>: un HMAC-SHA256 de <t>.<body> con el secreto de tu endpoint (whsec_…), que la consola muestra una sola vez.

Node.js
import { createHmac, timingSafeEqual } from 'node:crypto';

// Usa el cuerpo crudo, tal como llegó.
export function verifyGatoPago(rawBody, header, secret) {
  const { t, v1 } = Object.fromEntries(header.split(',').map((part) => part.split('=')));
  // Rechaza entregas viejas (5 minutos).
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
  const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
  return v1.length === expected.length && timingSafeEqual(Buffer.from(v1), Buffer.from(expected));
}

Referencia

URL base: https://api.gatopago.com

POST /v1/payment_intentsCrea un cobro.
GET /v1/payment_intentsLista los últimos 100.
GET /v1/payment_intents/:idLee uno.
POST /v1/payment_intents/:id/cancelCancela uno sin pagar.
POST /v1/payment_intents/:id/simulateLo marca como pagado (solo en redes de prueba).
GET /v1/eventsEventos y cómo fue cada entrega.
POST /v1/events/:id/resendReenvía un evento.
GET · POST /v1/webhook_endpointsLista o registra URLs de webhooks.
DELETE /v1/webhook_endpoints/:idQuita una URL.
GET · POST /v1/api_keysClaves API: solo desde la consola.

Los errores responden con un estado HTTP y un código estable en error_code:

UNAUTHENTICATED
Falta la clave o no es válida (401).
INVALID_AMOUNT
El monto no es un decimal válido mayor a cero (400).
IDEMPOTENCY_KEY_REUSED
Usaste la misma Idempotency-Key con otros datos (409).
INTENT_NOT_CANCELABLE
El cobro ya se pagó, venció o se canceló (409).
RATE_LIMITED
Demasiadas solicitudes; reintenta luego (429).