Antes de ir a producción
Todo lo de esta lista rompe integraciones reales.
Datos y validación
Sección titulada «Datos y validación»- El
phonedel beneficiario se carga siempre, también cuando pagas portransferencia. El alta no lo exige; el riel sí. → - El
bankCodese valida contra el catálogo del instrumento que vas a usar (GET /payouts/banks?method=…). Las dos listas no coinciden: 31 vs. 24 bancos. - El
accountNumberson 20 dígitos y empieza por elbankCode. - El
fullNameva tal como lo tiene el banco destino, no como lo tienes tú: el rechazo bancario más frecuente es de titularidad. - Mandas el bloque
sender, o tienes configurado el remitente por defecto de tu programa. Sin ninguno, la dispersión falla antes de salir. →
- Todos los importes viajan como enteros en unidades menores, y los
parseas de vuelta con enteros de precisión arbitraria, no con
Number. → - Ningún importe queda al borde del mínimo del riel (~925 Bs hoy, y se mueve con la tasa). →
- Sabes tu tope por operación y has pedido subirlo si tu caso de uso lo necesita (pago móvil: 100.000,00 Bs por defecto).
- Cotizas justo antes de dispersar: el TTL es de ~60 segundos y la cotización es de un solo uso. →
Idempotencia y reintentos
Sección titulada «Idempotencia y reintentos»- Toda llamada a
POST /payoutsllevaIdempotency-Key, con una clave única por operación. → - Un timeout o una conexión cortada se reintenta con la misma clave.
- Un
status: "unknown"no se reintenta nunca. - Un
status: "failed"se reintenta con una cotización nueva, después de corregir la causa dereason. - Tu cliente HTTP respeta
Retry-Afteren un429.
El desenlace
Sección titulada «El desenlace»- Tu sistema marca el pago como pagado con el webhook
payment.settled, no con la respuesta dePOST /payouts. → - Tu receptor verifica la firma HMAC sobre los bytes crudos y rechaza timestamps de más de 5 minutos. →
- Tu handler deduplica por
id: la entrega es at-least-once. - Tu receptor responde
2xxen menos de 5 segundos y procesa en segundo plano. - Tienes una alerta sobre
GET /v1/events?status=dead: si tu receptor estuvo caído, ahí está lo que no llegó. - Tratas
payment.reversed: el banco puede devolver un pago días después.
Errores
Sección titulada «Errores»- Tu código programa contra
error.code, nunca contramessage, y tiene undefaultpara códigos desconocidos. → - Distingues
error.code(error HTTP) dereason(rechazo del riel dentro de una respuesta201). Son dos taxonomías distintas. - Registras el
request_idde cada error: es lo que necesita soporte.
Operación
Sección titulada «Operación»- Repartes las claves: una con
payments:writepara el backend que dispersa, otra sólo de lectura para el dashboard. → - Sabes si operas prefondeado o con cierre diario, y qué cifra vigilar
(
prefundingBalanceocreditDrawn). → - Tu proceso de cuadre usa
from/tosobresettled_at, no la fecha de creación. → - Guardas
bank_referencede cada pago: es el comprobante del beneficiario.
La prueba que hay que hacer sí o sí
Sección titulada «La prueba que hay que hacer sí o sí»Al pasar a live
Sección titulada «Al pasar a live»- Cambias
sk_test_…porsk_live_…y confirmas conGET /v1/tenant/mequemodeeslive. - Tu
webhookUrlde producción está registrada y su secreto guardado aparte de la API key. - Has hecho una dispersión real, pequeña pero por encima del mínimo, y la
has seguido hasta
settledcon subank_reference.