Vouchers
The customer gets a cash reference at checkout — a barcode, a slip, or a number pair — and pays it in physical cash at a store, bank, or ATM. The payment succeeds when the scheme registers the cash, hours or days later. Essential in markets where a large share of customers have no card at all.
The voucher methods
| Method | payment_method | Region | Currency | The reference |
|---|---|---|---|---|
| OXXO | oxxo | Mexico | MXN | Barcode, paid at any OXXO convenience store |
| Boleto | boleto | Brazil | BRL | Numbered slip (linha digitável), paid at banks, ATMs, lottery agents, or bank apps |
| Konbini | konbini | Japan | JPY | Payment code / barcode, paid at convenience-store kiosks |
| Multibanco | multibanco | Portugal | EUR | Entity + reference pair, paid at ATMs or via home banking |
The cash flow
The flow is maximally asynchronous: after checkout, nothing happens until a human walks to a counter. The payment sits in requires_action the whole time — that's normal and can last days. Two consequences:
- Never fulfill from the return redirect. The customer coming back to your
return_urlmeans they saw the voucher, not that they paid it. Fulfillment gates onpayment.succeeded, full stop. - Hold inventory deliberately. Decide how long you'll reserve stock for an unpaid voucher — the voucher's expiry (shown on the hosted page and printed on the reference) is the natural bound.
Creating a voucher payment
curl https://api.finscale.dev/v1/payments \
-H "Authorization: Bearer sk_test_51FinscaleDemo…" \
-H "Idempotency-Key: idem_ord_9f21_0716" \
-H "Content-Type: application/json" \
-d '{
"amount": 89900,
"currency": "MXN",
"payment_method": "oxxo",
"reference": "ord_9f21_0716",
"customer": { "email": "anna@example.com" },
"return_url": "https://shop.example.com/checkout/return"
}'
{
"id": "pay_8Q2mX4nT1cVb",
"object": "payment",
"amount": 89900,
"currency": "MXN",
"status": "requires_action",
"payment_method": "oxxo",
"reference": "ord_9f21_0716",
"next_action": {
"type": "redirect",
"url": "https://pay.finscale.dev/r/8Q2mX4nT"
},
"created_at": "2026-07-16T09:24:31Z",
"livemode": false
}
The hosted page behind next_action.url renders the reference itself — barcode for OXXO and Konbini, the slip for Boleto, the entity + reference pair for Multibanco — with the amount, your reference, and the expiry deadline. Customers can save or print it; the page keeps working after checkout ends.
Living with the delay
- Conversion tails are long. Most vouchers that get paid are paid within a day; the rest trickle in until expiry. Don't alarm on
requires_actionages that would be pathological for any other family. - Exact amounts only. The reference is bound to the amount — cashiers and ATMs collect exactly it. There are no partial or over-payments to handle.
- Idempotent order state. A customer who lost the voucher will often re-checkout, producing a second payment for the same order. Cancel the abandoned one with
POST /v1/payments/{id}/cancelwhen the replacement succeeds, and key fulfillment on yourreference.
Refunds — no chargebacks
Cash rails have no reverse gear, so refunds travel as bank credit transfers instead: after POST /v1/refunds, Finscale collects the customer's account details through a secure hosted claim page (linked from the refund notification) and pushes the transfer once they're in. The refund stays pending until then — expect the extra step to add a day or two versus card refunds. Details in Refunds.
Settlement timing
The scheme registers the cash and confirms to Finscale — that's your succeeded. Funds then join the unified payout on the standard T+2 schedule in the voucher's currency. The settlement report line carries the payment id as usual, so reconciliation doesn't care that the customer paid in physical cash.
Error scenarios
| Scenario | What the API does |
|---|---|
| Voucher expires unpaid | The payment moves to failed and payment.failed fires. Expired references are rejected at the counter — a customer who still wants to pay needs a fresh checkout. |
| Customer pays after expiry anyway | The scheme refuses the cash at registration; nothing reaches the API. (Store systems reject expired barcodes at scan time.) |
| Amount above the scheme's cash ceiling | Cash schemes cap single transactions: creation fails with a 400 invalid_request_error rather than issuing an unpayable voucher. |
| Currency mismatch | 400 invalid_request_error with currency_not_supported — each voucher scheme is single-currency (see the table above). |
| Duplicate checkout for the same order | Two live payments with the same reference — cancel the stale one; see Living with the delay. |
Related
- Payment methods overview — families, payload shapes, full catalog
- Webhooks — reliable delivery for days-later confirmations
- Refunds — the credit-transfer refund path
- Settlement & payouts — how cash volume lands in your payout
- Method catalog on finscale.dev — regions and currencies