Skip to main content
BillingCoupons

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.

checkout.ts
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 charged

Apply 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:

  • originalAmountMinorthe list price before the coupon (amountMinor + couponDiscountMinor).
  • couponCodethe applied code, or null if no coupon was used.
  • couponDiscountMinorthe amount taken off, or null with 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.