Aliquot

Refunds

Refunds pull mediante un link de reclamo privado — por qué no son automáticos, cómo le llega el link al comprador, el fee de red y las transferencias con token equivocado.

Última actualización 27 oct 2026

Todo dinero que llega a una dirección de depósito y no se le debe al seller queda retenido para el comprador en LIABILITY:REFUND_ESCROW:<invoice> y se convierte en un refund:

reasonCuándo
OVERPAYMENTLa invoice se paga con más que su total: el excedente (invoice OVERPAID).
EXPIRED_UNDERPAIDUn pago parcial cuando la invoice vence (invoice EXPIRED_UNDERPAID, luego REFUNDED una vez devuelto).
LATE_PAYMENTUna transferencia confirmada después de que la invoice venció, se liquidó o se anuló.
WRONG_TOKENOtro token canónico de la misma red enviado a la dirección (USDC a una invoice en USDT).

Pull, no push

Los refunds nunca se devuelven automáticamente a la dirección que pagó. Esa dirección suele ser la hot wallet de un exchange o un contrato que no puede recibir (ni acreditar) el dinero. En cambio, el comprador reclama el refund: abre un link privado, pega una wallet que controla en la red del pago y el worker lo envía ahí.

<CRYPTO_CHECKOUT_BASE_URL>/pay/<invoice id>/refund/<claim token>. El token (256 bits) es la única credencial del comprador, así que se mantiene fuera de todos los lugares por donde viaja el id de la invoice:

  • El checkout público (GET /api/v1/public/pay/:id, la página /pay/:id) muestra que hay un refund retenido, cuánto, su estado y de dónde viene el link — refund.delivery es email o seller — pero nunca el link.
  • Con buyer_email en la invoice, el comprador recibe el link por email cuando el refund pasa a ser reclamable (delivery: "email").
  • Si no, el seller se lo reenvía (delivery: "seller"). El merchant lo lee de GET /api/v1/crypto/invoices/:id (refunds[].claim_url), GET /api/v1/crypto/refunds, el webhook refund.claimable (que también lleva buyer_email) o el botón Copy claim link del dashboard. La vista de respuesta cruda del dashboard lo oculta.

Los endpoints públicos de refund se identifican solo por el claim token: GET /api/v1/public/refunds/:claimToken y POST /api/v1/public/refunds/:claimToken/claim.

Reclamar

POST …/claim { "destination_address": "0x…" } valida la dirección (válida, checksum EIP-55 cuando mezcla mayúsculas y minúsculas, que no sea una de las direcciones propias del gateway, que no sea un contrato — un contrato responde 422), fija el fee de red y el monto neto, y mueve el refund CLAIMABLE → PROCESSING (202). Reclamar de nuevo a la misma dirección es idempotente.

El fee de red es el costo de enviar el refund, y se le cobra al refund: gas estimado × max fee per gas × CRYPTO_NATIVE_PRICE_USD_<FAMILY> (Polygon 0.50, BSC 600, Base 3000 por defecto), como mínimo 0.01, registrado como REVENUE:REFUND_NETWORK_FEE. El portal muestra lo retenido, el fee y "you receive" antes de que el comprador confirme. Cuando el fee se comería todo el refund (dust), el reclamo se rechaza con 422.

El worker envía un refund solo después de que cada transferencia de esa invoice y ese token fue barrida y el sweep está confirmado, y después lo marca COMPLETED al alcanzar el umbral de confirmaciones de la red (webhook refund.completed). Una invoice EXPIRED_UNDERPAID pasa a REFUNDED.

Token equivocado

Una transferencia de otro token canónico nunca detecta, paga ni liquida la invoice. Una vez confirmada, queda en escrow en su propio token (REFUND_ESCROW:<invoice>:<network>:<token>) con un refund WRONG_TOKEN que se devuelve en ese token, cualquiera sea el estado de la invoice. No se le acredita nada a ningún seller en ese token. Los sweeps corren por invoice × token, así que el refund espera solo el sweep de su propio token. Los tokens que no están en el registry no se vigilan.

[!NOTE] Un refund que falla on chain pasa a FAILED y su escrow se sigue debiendo. Todavía no hay botón de reintento; necesita un operador.

En esta página