Refund the local amount, the dollar amount, or the current rate? Who absorbs the FX difference, how stablecoin payout refunds work, and a policy to copy.
Refund the amount whose currency your terms fixed. If the customer paid R$ 5,400, most businesses send back R$ 5,400 and absorb the dollar difference. If the contract was in USD, you can refund USD and let the local amount float. Decide which side is fixed before the first refund, then price the refund with a fresh quote.
Refunds are where exchange rate risk shows up late. The sale happened weeks ago at one rate, the refund happens today at another, and someone has to eat the gap. The good news: it's a policy decision, not a math problem. The math is two divisions.
Key takeaways
Because the refund converts at today's rate, not the sale's rate. Exchange rates move every day; Brazil's central bank publishes a new PTAX dollar rate each business day in its open data portal. A refund three weeks later crosses a different rate, so either the dollar cost or the local amount changes.
Nothing is wrong when that happens. The original payment was priced correctly, and so is the refund. They just ran at two moments. The FX gap between a sale and a payout is the same effect in the other direction.
The one your customer agreed to pay in. If the price was shown in reais, refund reais. If the invoice was in dollars, refund dollars. Everything else follows from that choice.
Three policies cover almost every business:
| Policy | Fixed amount | Who absorbs the FX move | Fits |
|---|---|---|---|
| Refund the original local amount | What the customer paid, in their currency | You | Consumer checkout, marketplaces, anything priced in local currency |
| Refund the original USD amount | What you received, in dollars | The customer | B2B invoices issued in USD, contracts that name USD |
| Refund at the original rate, split the difference | Neither, by agreement | Shared | Negotiated enterprise contracts |
The first policy is the default for a reason. A customer who paid R$ 5,400 and gets back R$ 5,180 calls support, no matter how clearly the rate moved. That ticket costs more than the FX difference on most refunds. Passing through or absorbing FX costs covers the same trade-off for the original sale.
They fix the customer's side. Stripe's localized pricing docs say that if a currency-converted payment is disputed or refunded, the amount you received is converted back to the presentment currency at the current exchange rate, so the amount deducted from your balance "might be more or less than the original payment." Stripe also says it always refunds the customer "the exact amount they paid and in the currency they paid in."
Stripe's own example: a 60 USD payment converted at 0.88 EUR per USD settled as 52.80 EUR. At 0.86 on the refund date, 51.60 EUR came out of the merchant's balance. The merchant came out ahead that time. Next time it could go the other way.
That's the norm customers already know from cards. A stablecoin payout flow that refunds the dollar amount instead will feel worse to them, even if it's fair.
Illustrative example. A marketplace sells an item for R$ 5,400 and converts it at 5.40 BRL per USDC, so the sale is worth 1,000 USDC. Three weeks later the buyer asks for a refund. Two scenarios for the refund-day rate:
| Refund-day rate | Refund R$ 5,400 (local fixed) | Refund 1,000 USDC worth (USD fixed) |
|---|---|---|
| 5.70 BRL per USDC (real weakened) | 5,400 / 5.70 = 947.37 USDC. You keep 52.63 USDC | 1,000 × 5.70 = R$ 5,700. Customer gets R$ 300 more |
| 5.10 BRL per USDC (real strengthened) | 5,400 / 5.10 = 1,058.82 USDC. You pay 58.82 USDC extra | 1,000 × 5.10 = R$ 5,100. Customer gets R$ 300 less |
Fees are left out on purpose. On BlindPay, per-transaction fees vary by rail and network and are disclosed in the live quote before execute, so the real refund cost is the quote's sender_amount, not this table.
Look at the second row under "USD fixed." That's the refund that produces the angry ticket. Under "local fixed," the business pays 58.82 USDC to avoid it. Over a year of refunds, the gains and losses in the first column tend to offset each other, since nobody can reliably call the direction of the next move.
The stablecoins come back unconverted, so there's no FX difference to settle. BlindPay's payouts docs define refunded as: the stablecoins were returned to the funding source instead of being converted to fiat.
BlindPay operates as a non-custodial payment processor for payout flows from customer-controlled wallets: you authorize the exact amount and BlindPay executes that single transaction. If a payout cannot be delivered after the stablecoins are collected, BlindPay refunds them to the quote's refund wallet, or to the sending wallet when none is set; on Pix and other non-USD local rails the refund is automatic, while USD payouts and payouts whose review times out are refunded after an operator review.
Two cases to keep separate in code:
refunded. Stablecoins are back. Re-quote and resend, or return them to whoever funded the payout.failed. Not an automatic refund. The docs tell you to contact support rather than assume it resolves on its own.Payout statuses explained lists every state and what to do in each.
As a brand-new payout. A refund to a customer's bank account is a payout like any other: bank account, quote, execute. The quote locks the rate and fees for 5 minutes, and the window can be shorter on SEPA and Faster Payments (cut-off times), so read expires_at from the response.
To hit an exact local amount, set currency_type: receiver on the payout quote. request_amount is then in the currency the bank account receives, in minor units. R$ 5,400.00 is 540000. The response tells you how much stablecoin that costs at the current rate. To refund a fixed dollar value instead, use currency_type: sender and pass the stablecoin amount.
Set cover_fees deliberately too. With cover_fees: true the sender pays the fees on top, so the customer receives the full R$ 5,400. With false, fees come out of the amount received, and the customer sees less than they paid. For refunds, that's usually the wrong default.
Four lines, written before you need them:
Then give finance a line in the ledger for refund FX gains and losses, separate from the original sale. Recording stablecoin payouts in the ledger covers that entry, and the support playbook for payout amount questions has the reply to send when a customer asks why the dollar value changed. For how providers earn on the conversion itself, see how on/off-ramp providers make money.
On a development instance, create a payout quote with currency_type: receiver and cover_fees: true for your typical refund amount, and compare its sender_amount with the original sale. The payout quotes docs list every field.
Seven places blockchain payments beat bank rails: contractor payroll, remittances, B2B suppliers, marketplaces, 24/7 treasury, bill pay, and PSP payouts.
Push means the payer sends an ACH transfer. Pull means you debit a connected account. How each funds a USD to USDC on-ramp, the fees, and the return risk.
AP2, ACP, and x402 each verify that an AI agent had permission to spend. Here is what every protocol covers, who backs it, and the reconciliation gap none of them close.