Skip to main content
Esta guía es para desarrolladores que implementan la integración del plugin Bank Transfer. Cubre los patrones y decisiones que van más allá de las llamadas individuales a endpoints: idempotencia, estrategia de reintentos, manejo de estados y validación de webhooks. Para los parámetros de los endpoints y los esquemas de respuesta, consulta la Referencia de API.

Idempotencia


Cada solicitud mutante (initiate, process, cancel) requiere un header X-Idempotency. Si envías la misma clave dos veces, el plugin devuelve la respuesta original sin crear una operación duplicada. Reglas:
  • Usa un UUID v4 o un identificador de negocio único (por ejemplo, tu ID de orden interno)
  • Longitud máxima: 255 caracteres
  • El plugin asigna cada clave a la organización efectiva. La misma clave de dos organizaciones cuenta como dos solicitudes distintas.
  • El plugin devuelve una respuesta en caché durante la ventana de idempotencia configurada (IDEMPOTENCY_RETRY_WINDOW_SEC, por defecto 300 segundos)
  • Una respuesta reemitida es byte-idéntica a la original: mismo código de estado, mismo body. La respuesta no tiene un header para marcar un replay, así que diseña tu cliente para que sea seguro en cualquier caso.
No reutilices claves de idempotencia en diferentes operaciones. No reutilices una clave de initiate para procesar o cancelar la misma transferencia.

Detección de duplicados

Más allá de las claves de idempotencia, el plugin detecta duplicados basados en el contenido. Genera una huella (fingerprint) a partir de:
  • senderAccountId
  • los datos del destinatario (ISPB, agencia, cuenta, documento del titular)
  • el monto
  • el propósito
El plugin almacena la huella en Redis durante 5 minutos. El valor por defecto es 300 segundos. Los operadores lo ajustan por tenant a través del ajuste de systemplane idempotency.duplicate_guard_ttl_seconds. La organización no forma parte de la huella. El aislamiento de tenant proviene del prefijo de 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 captura los casos en que el cliente envía la misma transferencia con una clave de idempotencia diferente. 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 errores transitorios. No reintentes todos los errores. Programación de backoff recomendada para 5xx/503: 0s, 5s, 25s, 60s, 120s (5 intentos en total).
Cuando JD SPB no está disponible, la respuesta es HTTP 503. El campo error.code lleva entonces el código del proveedor JD sin transformar — por ejemplo, TRANSPORT para fallas de transporte o ACE95 para timeouts. El plugin no envuelve las fallas de la cadena JD en un código BTF-. Marca la transferencia para reconciliación manual después de que se agoten los reintentos. No reintentes sin límite. La red JD SPB tiene horarios operativos definidos.

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 de CREATED o PENDING.
Máquina de estados TED OUT
Qué hacer en cada estado:

Máquina de estados de iniciación

El endpoint initiate crea una entidad PaymentInitiation. Esta entidad tiene su propio ciclo de vida antes de que el plugin cree un Transfer.
Máquina de estados de iniciación

Máquina de estados de TED IN

Máquina de estados TED IN

Máquina de estados de P2P

P2P no tiene estado PENDING. La liquidación es atómica e instantánea.
Máquina de estados P2P

Polling vs. webhooks

Prefiere los webhooks para el estado en tiempo real. Si aún no configuraste los webhooks, haz polling en GET /v1/transfers/{transferId}. Usa un máximo de 10 intentos con la misma programación 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 Obtener Transferencia y Webhooks.

Integración de webhooks


Para los esquemas de payload de eventos y la lista completa de eventos, consulta Webhooks.

Validación de firma

Cada solicitud de webhook incluye headers que tu endpoint usa para verificar la autenticidad:
  • X-Webhook-Signature — firma HMAC-SHA256 versionada en el formato v1,sha256=<hex>
  • X-Webhook-Timestamp — timestamp Unix en segundos (UTC) de cuando el plugin construyó la solicitud
  • X-Webhook-Event — el tipo de evento (por ejemplo, transfer.completed). Este header no forma parte de la firma.
El plugin calcula la firma así:
El payload firmado tiene cuatro partes en orden: el prefijo v1:, el valor del timestamp de X-Webhook-Timestamp, un único punto ASCII (.) y luego los bytes crudos del cuerpo de la solicitud. Usa los bytes del cuerpo exactamente como llegan por la red. No los parsees ni los recodifiques primero. Para validar:
  1. Lee X-Webhook-Signature y X-Webhook-Timestamp de los headers de la solicitud.
  2. Construye el payload firmado: "v1:" + timestamp + "." + rawBody.
  3. Calcula HMAC-SHA256 sobre el payload firmado con tu WEBHOOK_SIGNING_SECRET, luego codifica el resultado en hex.
  4. Antepón v1,sha256=, luego compara contra X-Webhook-Signature con una función de igualdad de tiempo constante.
  5. Rechaza la solicitud si el timestamp está fuera de una ventana de frescura aceptable (una tolerancia de 5 minutos es típica) para prevenir replay.
Aparte de X-Webhook-Signature y X-Webhook-Timestamp, el plugin establece únicamente X-Webhook-Event (el tipo de evento). No envía X-Webhook-Event-Type, X-Webhook-Routing-Key ni X-Webhook-Delivery-Attempt.

Procesamiento idempotente de webhooks

Tu endpoint puede recibir el mismo evento más de una vez (entrega al menos una vez). Usa transferId + event como clave compuesta para deduplicar.

Patrones de manejo de errores


Mapea los códigos de error de la API a acciones orientadas al usuario. Consulta la lista completa de errores para todos los códigos. Las respuestas de error siguen esta estructura:

Lista de verificación para salida en producción


Antes de habilitar la integración en producción:
  • Envía X-Idempotency en cada solicitud de initiate, process y cancel
  • Lógica de reintentos implementada con backoff exponencial para errores 5xx/503
  • Endpoint de webhook desplegado y devolviendo 200 en menos de 5 segundos
  • Validación de firma activa en el endpoint de webhook
  • Deduplicación de eventos de webhook implementada usando transferId + event
  • Horario de funcionamiento validado en el lado del cliente antes de llamar a initiate (reduce los 422 innecesarios)
  • Tanto transferId como confirmationNumber almacenados para reconciliación
  • Estados terminales (COMPLETED, REJECTED, FAILED, CANCELLED) gestionados en la interfaz
  • Expiración de iniciación (24h) manejada — solicita al usuario que reinicie cuando la ventana expira
  • Readiness del servicio monitoreado en tu sistema de alertas para despliegues BYOC
  • Redis accesible y monitoreado — el servicio rechaza solicitudes cuando Redis está caído
  • PLUGIN_AUTH_ENABLED=true configurado en producción, con un PLUGIN_AUTH_ADDRESS válido (HTTPS)