Idempotencia
Cada solicitud que modifica datos (initiate, process, cancel, return) requiere un encabezado
X-Idempotency. Una solicitud sin él se rechaza con 400 BTF-0001. Si envías la misma solicitud dos veces con una misma clave, el plugin devuelve la respuesta original sin crear una operación duplicada.
Reglas:
- Usa un UUID v4 o un identificador de negocio único (p. ej. el ID de pedido de tu sistema)
- Longitud máxima: 255 caracteres, formados por letras, dígitos,
.,_y- - El plugin limita cada clave a la organización efectiva. La misma clave desde dos organizaciones cuenta como dos solicitudes distintas.
- El plugin repite una respuesta exitosa durante 7 días. Una repetición lleva el mismo código de estado y el mismo cuerpo, más el encabezado
X-Idempotency-Replayed: true. - Una respuesta
4xxno consume la clave: corrige la solicitud y envíala de nuevo con la misma clave. - Después de una respuesta
5xx, el resultado es desconocido: lee primero el recurso y envía la solicitud de nuevo solo con una clave nueva. Una clave que la respuesta bloqueó (X-Idempotency-Fenced: true) se rechaza con422 BTF-0014. Una respuesta503 BTF-9000o503 BTF-9017no ejecutó nada: envía la solicitud de nuevo con la misma clave. - La misma clave con un cuerpo distinto se rechaza con
422 BTF-0017. Un reenvío mientras la primera solicitud sigue en ejecución recibe409 BTF-0016conRetry-After. - Cada rechazo de idempotencia indica su solución en
detail. Léela antes de reenviar.
Detección de duplicados
Más allá de las claves de idempotencia, el plugin detecta duplicados basados en el contenido. Construye una huella a partir de:senderAccountId- los datos del receptor (ISPB, sucursal, cuenta, documento del titular)
- el monto
- el propósito
idempotency.duplicate_guard_ttl_seconds. La organización no forma parte de la huella. El aislamiento entre tenants viene del prefijo de la clave de Redis. El plugin rechaza la solicitud con 409 BTF-0012 si el cliente ya envió una transferencia coincidente dentro de la ventana.
Esto detecta los casos en que el cliente envía la misma transferencia con una clave de idempotencia distinta. Un ejemplo es un reintento después de un timeout, cuando el cliente no recibió la respuesta original.
Estrategia de reintentos
Usa backoff exponencial para los errores transitorios. No reintentes todos los errores.
Programa de backoff recomendado para 5xx: 0s, 5s, 25s, 60s, 120s (5 intentos en total).
Durante
POST /v1/transfers/process, cuando JD SPB no está disponible, la respuesta es HTTP 503 con code BTF-1000, o BTF-1003 en un timeout de JD. upstream.code lleva el código propio de JD cuando JD devolvió uno. transferId y transferStatus identifican la transferencia. Una transferencia en PROCESSING mantiene su retención mientras la conciliación le pide el resultado a JD: no la proceses de nuevo.Manejo de estados
Máquina de estados de TED OUT
Las transferencias siguen una progresión estricta. No puedes cancelar una transferencia después de que sale deCREATED o PENDING.
Máquina de estados del inicio
El endpoint initiate crea una entidadPaymentInitiation. Esta entidad tiene su propio ciclo de vida antes de que el plugin cree una Transfer.
Máquina de estados de TED IN
Máquina de estados de P2P
P2P no tiene un estadoPENDING. La liquidación es atómica e instantánea.
Polling frente a webhooks
Prefiere los webhooks para el estado en tiempo real. Si todavía no configuraste webhooks, consultaGET /v1/transfers/{transferId}. Usa como máximo 10 intentos con el mismo programa de backoff que los reintentos. Marca la transferencia para revisión manual después de 10 minutos sin un estado terminal (COMPLETED, REJECTED, FAILED, CANCELLED).
Consulta Get Transfer y Webhooks.
Integración de webhooks
Para los esquemas de payload de los eventos y la lista completa de eventos, consulta Webhooks.
Validación de la firma
Cada solicitud de webhook incluye encabezados que tu endpoint usa para verificar la autenticidad:X-Webhook-Signature: firma HMAC-SHA256 versionada con la formav1,sha256=<hex>X-Webhook-Timestamp: marca de tiempo Unix en segundos (UTC) del momento en que el plugin construyó la solicitudX-Webhook-Event: el tipo de evento (por ejemplo,transfer.completed). Este encabezado no forma parte de la firma.
v1:, el valor de la marca de tiempo de X-Webhook-Timestamp, un punto ASCII (.), y luego los bytes del cuerpo de la solicitud sin procesar. Usa los bytes del cuerpo exactamente como llegan por la red. No los analices ni los vuelvas a codificar antes.
Para validar:
- Lee
X-Webhook-SignatureyX-Webhook-Timestampde los encabezados de la solicitud. - Construye la cadena firmada:
"v1:" + timestamp + "." + rawBody. - Calcula
HMAC-SHA256sobre la cadena firmada con elsigningSecretdel registro del webhook, y luego codifica el resultado en hexadecimal. - Antepón
v1,sha256=, y luego compáralo conX-Webhook-Signaturecon una función de igualdad de tiempo constante. - Rechaza la solicitud si la marca de tiempo queda fuera de una ventana de frescura aceptable (una tolerancia de 5 minutos es lo típico) para evitar repeticiones.
JavaScript
JavaScript
Python
Python
Go
Go
Procesamiento idempotente de webhooks
Tu endpoint puede recibir el mismo evento más de una vez (entrega al menos una vez). UsatransferId + el encabezado X-Webhook-Event como clave compuesta para deduplicar. Para los eventos de devolución de una TED IN, agrega status y reconciliationReasonCode del cuerpo a la clave.
Patrones de manejo de errores
Asigna los códigos de error de la API a acciones de cara al usuario. Consulta la lista completa de errores para todos los códigos.
Las respuestas de error son documentos de problema RFC 9457 (
application/problem+json). Los campos específicos de cada ruta van en el nivel superior:
Lista de verificación para salir a producción
Antes de habilitar la integración en producción:
- Envía
X-Idempotencyen cada solicitud de initiate, process y cancel - Lógica de reintentos implementada con backoff exponencial para los errores 5xx/503
- Endpoint de webhook desplegado y que devuelve
200en menos de 5 segundos - Validación de la firma activa en el endpoint de webhook
- Deduplicación de eventos de webhook implementada con
transferId + event - Horario de operación validado del lado del cliente antes de llamar a initiate (reduce los 422 innecesarios)
-
transferIdyconfirmationNumberalmacenados para la conciliación - Estados terminales (
COMPLETED,REJECTED,FAILED,CANCELLED) manejados en la interfaz - Expiración del inicio manejada (
expiresAt): pide al usuario que reinicie cuando se cumpla - Preparación del servicio monitoreada en tu sistema de alertas para los despliegues BYOC
- Redis alcanzable y monitoreado: el servicio rechaza solicitudes cuando Redis está caído
-
PLUGIN_AUTH_ENABLED=trueconfigurado en producción, con unPLUGIN_AUTH_ADDRESSválido (HTTPS)

