Saltar al contenido principal

Pagos cruzados

Tu cliente transfiere pesos por SPEI. Tu cuenta se abona en dólares digitales. Ninguno de los dos toca un exchange.

Eso es un pago cruzado, y es lo que separa un enlace de pago de Binkio de un botón de cobro corriente: el riel por el que entra el dinero y la moneda en la que quedas abonado son decisiones independientes.

No hay que activarlo. Sale solo de cómo creas el enlace: currency es lo que recibes; accepted_methods es lo que puede mandar quien paga. Si no coinciden, hay conversión.

{
"amount": "100.00",
"currency": "USDC",
"accepted_methods": ["SPEI", "MXNB", "USDC"]
}

Ese enlace cobra 100 dólares digitales, y tu cliente puede pagarlos con una transferencia en pesos, con MXNB en Arbitrum o con USDC en Ethereum. Los tres caminos te dejan los mismos 100 USDC.


Lo que ve el pagador​

En el detalle del enlace, cada método trae su propio importe:

{
"method": "SPEI",
"asset": "MXN",
"requires_conversion": true,
"amount_due": "1738.42",
"exchange_rate": "17.0234",
"quote_status": "locked",
"amount_due_expires_at": "2026-09-07T10:29:22.481Z",
"instructions": { "clabe": "000000000000000000" }
}
  • amount_due — lo que tiene que mandar, en la moneda de ese método.
  • exchange_rate — el tipo de cambio con el que se calculó.
  • amount_due_expires_at — hasta cuándo vale ese importe.
  • requires_conversion — true te dice que este método cruza monedas.

El bloqueo de cotización​

Aquí está lo que hay que entender para integrar bien.

Cuando un método requiere conversión, el importe se fija durante una ventana de tiempo y se registra con el banco. En SPEI eso significa algo muy concreto: generamos una CLABE de un solo uso, para exactamente ese importe.

:::danger Una transferencia por otro importe no se acredita La CLABE está registrada para amount_due exacto. Si tu cliente manda 1.738,00 en lugar de 1.738,42, ese dinero no cuadra con ningún cobro. Enseña el importe con todos sus decimales y no dejes que se edite. :::

Por qué el importe caduca​

El tipo de cambio se mueve. Si fijáramos el importe para siempre, un enlace creado hoy y pagado en tres semanas te dejaría abonado bastante menos de lo que pediste — y esa diferencia la comería tu negocio.

Así que la cotización vive una ventana. Cuando vence pasan dos cosas, en este orden:

  1. Se intenta extender. Si el importe que ya está bloqueado sigue cubriendo tu amount al tipo de cambio nuevo, la ventana se corre hacia adelante: misma CLABE, mismo importe. Es el caso normal.
  2. Si ya no cubre, se genera una cotización nueva: importe nuevo y CLABE nueva.

:::warning La CLABE puede cambiar mientras el pagador la mira Es la consecuencia práctica del punto 2, y hay que preverla en la interfaz.

Si construyes tu propia pantalla de cobro, escucha payment_link:quotes_updated por el canal en vivo que te da el checkout, y vuelve a pintar importe y CLABE cuando llegue. Si usas la página alojada (checkout_url), ya está resuelto. :::


Qué ingresa exactamente tu cuenta​

Esta es la parte que hay que tener clara para conciliar.

Con fee_bearer: "payer" —el valor por defecto— tu cuenta ingresa exactamente el amount publicado. Ni más ni menos, pase lo que pase con el tipo de cambio.

El pagador manda algo más que la conversión estricta: un margen que cubre el movimiento del tipo de cambio dentro de la ventana. Si la tasa no se mueve, ese margen sobra.

Ese sobrante no es tuyo. Cubre el riesgo de la conversión y ahí se queda. Lo que varía con el mercado es nuestro ingreso, nunca el tuyo — que es justo lo que significa «con la comisión a cargo del pagador, recibes el importe íntegro».

La consecuencia práctica al conciliar: compara contra amount, no contra lo que mandó el pagador. Son cifras distintas y sólo la primera es predecible.


Cuándo un método no está disponible​

No todas las rutas admiten todos los importes. Un método fuera de rango se publica como no disponible con el motivo, antes de que nadie intente pagar:

MotivoQué pasó
BELOW_MIN_THRESHOLDEl importe es menor que el mínimo de esa ruta
ABOVE_MAX_THRESHOLDSupera el máximo

Es información, no un error: el enlace se crea igual con los métodos que sí caben. Si ninguno cabe, la creación falla y te lo dice.

Consecuencia de diseño para tu tienda: si vendes tanto artículos de 50 pesos como de 500.000, no ofrezcas la misma lista de métodos en los dos casos. Pide el detalle del enlace y enseña sólo lo que está disponible.


El camino completo, paso a paso​

Para un enlace en USDC pagado por SPEI:

Entre la transferencia y el abono el enlace queda en processing. No es un fallo: es la conversión ocurriendo. Enseña «estamos confirmando tu pago».


Y si algo se queda a medias​

Un pago cruzado tiene más piezas que uno directo, así que conviene saber qué esperar:

  • Llegó menos de lo pedido → recibes payment_link.partial_deposit con lo requerido y lo recibido. El enlace sigue active.
  • Llegó de más → se acredita lo publicado y el exceso se devuelve.
  • El enlace se queda en processing más de lo razonable → no lo canceles ni crees otro. Escríbenos con el code y el request_id: el dinero está localizado, y crear un segundo cobro sólo duplica el problema.

:::tip Concilia siempre contra la API El webhook es la vía rápida, no la fuente de verdad. Para cuadrar contabilidad, GET /v1/transactions y GET /v1/payment_links son el estado real de tu cuenta. Tu servidor puede estar caído justo cuando pasa algo, y ninguna escalera de reintentos dura para siempre. :::