Saltearse al contenido

Convenciones

Base URL

https://api.pay.ingalca.com

Todos los endpoints viven bajo /v1/. La URL es la misma para test y live; el entorno lo define la API key (ver entornos).

Estructura de respuesta

Éxito:

{
"data": { ... }
}

Listado:

{
"data": [...],
"meta": {
"next_cursor": "eyJpZCI6MTIzfQ",
"prev_cursor": null,
"per_page": 25
}
}

Error:

{
"error": {
"code": "VALIDATION_FAILED",
"message": "...",
"details": { /* opcional */ }
}
}

IDs públicos

Cada recurso tiene un ID público con prefijo:

RecursoPrefijoEjemplo
Paymentpay_pay_abc123def456
Cardcard_card_xyz789abc012
Customercust_cust_qrs456tuv789
Webhookevt_evt_a1b2c3d4e5f6

Los IDs no son enumerables (random base32-like). Asumí que pueden cambiar de longitud en el futuro — no los parsees.

Paginación

Todos los listados usan cursor pagination. NO hay ?page=N.

GET /v1/payments?cursor=eyJpZCI6MTIzfQ&per_page=50

La respuesta trae meta.next_cursor (más resultados) y meta.prev_cursor (página anterior). Si next_cursor es null, llegaste al final.

Filtros

Los listados aceptan filtros como query params. Filtros vacíos se ignoran.

GET /v1/payments?status=confirmed&provider=dinelco&from=2026-05-01&to=2026-05-31

Cada endpoint documenta sus filtros disponibles.

Fechas

Todas las fechas en respuestas son ISO 8601 con timezone:

2026-05-02T14:32:11+00:00

Para filtros, aceptamos YYYY-MM-DD (interpretado como Paraguay) o ISO 8601 completo.

Montos

Los montos van en enteros representando la unidad mínima de la moneda. Para PYG (sin decimales), el monto es directamente la cantidad de guaraníes:

{
"amount": 150000,
"currency": "PYG"
}

Esto son ₲ 150.000.

Hoy soportamos solo PYG. Si en el futuro agregamos USD, los amount van a ser en centavos (amount: 1000 = $10.00).

Idempotencia

Los POST que crean un cobro son idempotentes por reference. La reference que enviás es la clave de idempotencia: identifica de forma única a la operación dentro de tu cuenta y entorno (test/live).

Aplica a:

  • POST /v1/cards/{card_id}/charge — cobro tokenizado.
  • POST /v1/payments — creación de pago.

Cómo funciona

  • Reenvío del mismo request (misma reference, mismo body) → devolvemos el pago original (mismo id), sin ejecutar un segundo cobro. La respuesta es 200 (en vez de 201) para que distingas un replay de un cobro nuevo.
  • Misma reference con un body distinto (otro amount, card_id, currency o provider) → 409 REFERENCE_ALREADY_USED. No cobramos: es la señal de que reusaste una reference para otra operación.
  • Es seguro ante concurrencia: dos requests simultáneos con la misma reference resuelven a un solo cobro.

El replay devuelve el resultado ORIGINAL — incluso si falló

Si el cobro original terminó failed o rejected, un reenvío con la misma reference te devuelve ese resultado fallido — no reintenta el cobro. Es la semántica estándar de idempotencia.

Para reintentar de verdad una operación fallida, usá una reference nueva. Una reference = una operación, para siempre.

Por qué reintentar es seguro

Ante un timeout de red podés reenviar el mismo request sin miedo a un doble cobro: si el primero llegó y cobró, el reenvío te devuelve ese pago; si no llegó, el reenvío lo procesa por primera vez.

Ojo con el replay: los campos del pago son los del original

En una respuesta 200 (replay), todos los campos del pago son los del cobro original — no de esta llamada. Concretamente:

  • id, status, amount, metadata, customer → los del pago que ya existía.
  • created_at, confirmed_at, failed_at → timestamps de cuando pasó de verdad, hace horas o días.
  • provider_data → los datos del provider en el cobro original (mismos operation_number, authorization_number, etc.).

Impacto práctico para tu UI: si tu app pinta “pago confirmado el X” y estás en modo replay, X no es ahora — es cuando el pago se cobró originalmente. Distinguí el 200 del 201 para no mostrarle al operador “pago recién confirmado” cuando en realidad ya fue confirmado hace rato. Y para no marcarle al cliente que le acabaste de cobrar cuando el cobro ya sucedió y notificaste hace tiempo.

Headers comunes

HeaderPara qué
AuthorizationBearer token (tu API key). Obligatorio en /v1/*.
Content-Typeapplication/json para POST/PUT con body JSON.
Idempotency-KeyNo se usa. La idempotencia es por reference (ver Idempotencia).

HTTP status codes

CodeSignificado
200OK
201Recurso creado
204OK sin body
400Request mal formado
401API key inválida o ausente
403API key válida pero sin permisos para esta acción
404Recurso no existe (o pertenece a otro tenant)
409Conflicto (ej. reference ya usada para otra operación)
422Validación falló (mirar error.details.fields)
429Rate limit superado
500Error interno (deberíamos verlo nosotros antes que vos)
502Error del provider externo (Dinelco / Bancard)
503Servicio temporalmente no disponible