Failed payments, retries and refunds
Hold it while you take payment
Section titled “Hold it while you take payment”Optional. Worth it when payment can take a while — a reservation stops the holder spending the same voucher somewhere else mid-checkout.
const { token } = await bm.vouchers.reserve({ code });try { await charge(customer, voucher.priceUsd); await bm.vouchers.redeem({ ticket: token, externalRef: order.id });} finally { await bm.vouchers.release({ ticket: token, reason: 'payment_failed' });}Two things about that finally:
releaseafter a successfulredeemis not an error. There is nothing left to release, and it answers{ released: false }. That is exactly why it is safe there.- A reservation you forget about is a voucher your customer cannot use. It lapses on its own after fifteen minutes, but fifteen minutes is a long time at a till.
Retries
Section titled “Retries”Send the same externalRef every time. See
the five-minute guide — it is the
single most common way to get this wrong, and the symptom is a customer losing a second
voucher to a network blip.
Refunds, after the voucher has burned
Section titled “Refunds, after the voucher has burned”The customer refunds the order. What happens to the voucher?
It is not un-burned. The chain says this voucher was redeemed against your order on that date, and that remains true — a refund is a later fact, not a correction of an earlier one. Rewriting a sealed ledger because the story continued is exactly the thing our customers pay us not to do.
What you can do instead: ask us to issue the customer a fresh voucher. A reissue is a new row, linked to the old one, and both are visible. It is honest about what happened, where an erased redemption would not be.
Voucher states you can meet
Section titled “Voucher states you can meet”| status | what it means for you |
|---|---|
Active | usable |
Reserved | held behind a live ticket — yours, if you reserved it |
Redeemed | already used. inspect still answers; redeem replays the receipt |
Expired | past validUntil |
Void | cancelled by the issuing brand |
Distributed | this row was handed on; the holder’s voucher is a different row |