Coupons.
A coupon discounts a single one-time checkout before the customer pays. It is resolved server-side — at create, or when the buyer applies a code on the hosted checkout page: the amount the buyer is actually charged on-chain is the discounted amount, and the original list price is preserved on the checkout so you can reconcile the difference. One coupon per checkout.
One-time payments only
Coupons apply to mode payment checkouts backed by an inline amount or a one-time priceId. Passing couponCode on a subscription checkout, or on any invoiceId-backed checkout (invoices are fixed-amount), is rejected. Recurring discounts are not modeled as coupons; price the plan accordingly.
Apply at create
Pass couponCode on CheckoutCreate. The code is validated and the discount is computed against the pre-discount amount before the session is returned. An unknown, expired, or inapplicable code fails the create call rather than silently charging full price.
const checkout = await os.checkouts.create({
mode: "payment",
amount: 4999, // $49.99 list price, minor units / cents
currency: "USD",
chain: "base",
token: "USDC",
couponCode: "LAUNCH20", // resolved server-side at create
});
checkout.originalAmountMinor; // 4999 — list price
checkout.couponCode; // "LAUNCH20"
checkout.couponDiscountMinor; // 1000 — amount taken off
checkout.amountMinor; // 3999 — what the buyer is actually chargedApply on the hosted page
If you create the checkout without a code, the hosted checkout page shows an apply-coupon field. The customer enters a code, it is validated server-side, and the charged amount updates in place. The end state is identical to passing couponCode at create: amountMinor becomes the discounted amount and the coupon fields are populated on the checkout.
The $0.50 charge floor
A coupon can never drive the charged amount below 50 minor units ($0.50). A discount large enough to undercut that floor — including any 100%-off coupon — is rejected: the create (or hosted apply-coupon) call returns a 4xx rather than charging an unsettleable amount, and the buyer is never silently charged full price either. Gas and on-chain dust make a sub-$0.50 settlement impractical. To give something away for free, do it outside the payment rail rather than with a coupon.
Reconciling the discount
This is the part finance teams care about. When a coupon applies, Checkout.amountMinor is the discounted, actually-charged amount — never the list price. To book the discount, read all three fields:
originalAmountMinor— the list price before the coupon (amountMinor + couponDiscountMinor).couponCode— the applied code, ornullif no coupon was used.couponDiscountMinor— the amount taken off, ornullwith no coupon.
Reconciling on-chain settlement against originalAmountMinor will read as a permanent underpayment on every discounted order. Gate amount checks on amountMinor (the charged amount), and post couponDiscountMinor as a discount line against originalAmountMinor in your ledger.