Refunds
Refunds pull por meio de um link de resgate privado — por que não são automáticos, como o link chega ao comprador, a taxa de rede e os transfers com token errado.
Última atualização 27 de out. de 2026
Qualquer dinheiro que chega a um endereço de depósito e não é devido ao vendedor fica retido para o comprador em LIABILITY:REFUND_ESCROW:<invoice> e se torna um refund:
reason | Quando |
|---|---|
OVERPAYMENT | A invoice é paga com mais do que o seu total: o excedente (invoice OVERPAID). |
EXPIRED_UNDERPAID | Um pagamento parcial quando a invoice expira (invoice EXPIRED_UNDERPAID, depois REFUNDED quando é devolvido). |
LATE_PAYMENT | Um transfer confirmado depois que a invoice expirou, foi liquidada ou anulada. |
WRONG_TOKEN | Outro token canônico da mesma rede enviado ao endereço (USDC para uma invoice em USDT). |
Pull, não push
Os refunds nunca são devolvidos automaticamente ao endereço que pagou. Esse endereço costuma ser a hot wallet de uma exchange ou um contrato que não consegue receber (nem creditar) o dinheiro. Em vez disso, o comprador resgata o refund: ele abre um link privado, cola uma wallet que controla na rede do pagamento, e o worker envia o valor para lá.
O link de resgate
<CRYPTO_CHECKOUT_BASE_URL>/pay/<invoice id>/refund/<claim token>. O token (256 bits) é a única credencial do comprador, por isso é mantido fora de todo lugar por onde o id da invoice circula:
- O checkout público (
GET /api/v1/public/pay/:id, a página/pay/:id) mostra que há um refund retido, de quanto, o seu status e de onde vem o link —refund.deliveryéemailouseller— mas nunca o link. - Com
buyer_emailna invoice, o comprador recebe o link por email quando o refund se torna resgatável (delivery: "email"). - Caso contrário, o vendedor o encaminha (
delivery: "seller"). O merchant o lê emGET /api/v1/crypto/invoices/:id(refunds[].claim_url),GET /api/v1/crypto/refunds, no webhookrefund.claimable(que também trazbuyer_email) ou no botão Copy claim link do dashboard. A visualização da resposta bruta no dashboard o oculta.
Os endpoints públicos de refund são identificados apenas pelo claim token: GET /api/v1/public/refunds/:claimToken e POST /api/v1/public/refunds/:claimToken/claim.
Resgatando
POST …/claim { "destination_address": "0x…" } verifica o endereço (válido, checksum EIP-55 quando há maiúsculas e minúsculas, não é um dos endereços do próprio gateway, não é um contrato — um contrato responde 422), fixa a taxa de rede e o valor líquido, e move o refund CLAIMABLE → PROCESSING (202). Resgatar de novo para o mesmo endereço é idempotente.
A taxa de rede é o custo de enviar o refund, cobrado do próprio refund: gas estimado × max fee per gas × CRYPTO_NATIVE_PRICE_USD_<FAMILY> (Polygon 0.50, BSC 600, Base 3000 por padrão), no mínimo 0.01, lançada como REVENUE:REFUND_NETWORK_FEE. O portal mostra o valor retido, a taxa e o "você recebe" antes de o comprador confirmar. Quando a taxa consumiria todo o refund (poeira), o resgate é recusado com 422.
O worker só envia um refund depois que todos os transfers daquela invoice e daquele token passaram pelo sweep e o sweep está confirmado, e então o marca como COMPLETED ao atingir o limiar de confirmações da rede (webhook refund.completed). Uma invoice EXPIRED_UNDERPAID se torna REFUNDED.
Token errado
Um transfer de outro token canônico nunca detecta, paga nem liquida a invoice. Uma vez confirmado, ele fica em escrow no seu próprio token (REFUND_ESCROW:<invoice>:<network>:<token>) com um refund WRONG_TOKEN pago de volta nesse token, qualquer que seja o status da invoice. Nada é creditado a nenhum vendedor nesse token. Os sweeps rodam por invoice × token, então o refund espera apenas o sweep do seu próprio token. Tokens que não estão no registry não são monitorados.
[!NOTE] Um refund que falha on chain se torna
FAILEDe o seu escrow continua devido. Ainda não há botão de retry; é preciso um operador.