Subscription Checkouts
A subscription checkout is a single-use intent that lets a customer authorize a recurring stablecoin subscription. It mirrors the one-shot Checkout flow: the merchant creates an intent on the server, redirects the customer to a hosted subscribe page, and receives webhooks when the customer signs and the first charge confirms on-chain.
Why intents
The subscription plan (price, feature gating, billing day) lives on the merchant side. Exodus only records the per-subscriber intent and captures the resulting on-chain state. One intent maps to one subscriber and one resulting subscription — same lifecycle as one-shot Checkout.
This means there are no reusable /subscribe/:planId URLs in v1: every subscribe flow starts with a fresh merchant-server call to POST /subscription-checkouts.
Atomic first charge
When the customer signs at the hosted page, the on-chain subscribeAndCharge transaction registers the subscription and performs the first charge in a single transaction. The intent never transitions to completed without a confirmed first charge — there is no “subscribed but not charged” state.
Lifecycle
| Status | Meaning |
|---|---|
pending | Intent created; no on-chain subscribe yet. |
completed | Customer signed subscribeAndCharge; first charge confirmed; Subscription materialized. |
cancelled | Merchant called PATCH /subscription-checkouts/:id/cancel, or customer dismissed the hosted page. |
expired | expires_at elapsed without a successful subscribe. Default expiry is 5 minutes; maximum is 24 hours. |
Race resolution — on-chain truth wins
If a subscribeAndCharge transaction is already in the mempool when the merchant cancels (or when the cron sweeper marks the intent expired), and that transaction later confirms on-chain, the indexer flips the intent back to completed and dispatches a subscription_checkout.completed webhook.
Merchants must handle late subscription_checkout.completed webhooks that arrive after a
.cancelled or .expired event. The latest webhook is authoritative.
Chain identifiers (CAIP-2)
The subscription surface uses CAIP-2 chain identifier strings throughout — eip155:1 for Ethereum mainnet, eip155:137 for Polygon, etc. The merchant publishes supported_chains: string[] at intent creation; the customer picks one at sign time based on their wallet balances.
V1 supports the eip155: namespace only (EVM chains).
| CAIP-2 | Network |
|---|---|
eip155:1 | Ethereum mainnet |
eip155:137 | Polygon |
eip155:42161 | Arbitrum One |
eip155:8453 | Base |
eip155:10 | Optimism |
Amounts and cap
| Field | Meaning |
|---|---|
price | The recurring price. Token-denominated plans (no price_currency): settlement-token smallest unit (e.g. "9990000" = 9.99 USDC). FIAT plans: minor units of price_currency. |
price_currency | Optional pricing denomination. Omitted = token-denominated; an ISO 4217 code (e.g. ARS) = FIAT, re-rated to the settlement token each cycle. |
period_duration | Seconds between scheduled charges. Minimum 1 hour. Day-of-month and other calendar billing is the merchant’s responsibility — schedule the API call when desired. |
recommended_cap | Optional. The merchant’s suggested cap, in the settlement token’s smallest unit, shown to the customer at sign time. If provided, must be >= the settlement-token value of price. The customer sets the actual on-chain cap_amount when subscribing — the cap is subscriber-sovereign. |
The customer chooses the on-chain cap_amount when they sign at the hosted page — optionally seeded by the merchant’s recommended_cap — authorizing the contract to pull up to that cap per call. FIAT plans re-rate each cycle, so a generous cap absorbs FX swings and price growth; a cycle that would exceed the cap reverts until the customer raises it, never a silent over-pull.
The customer’s wallet approves the on-chain contract for uint256.max allowance at subscribe time
(industry standard pattern matching Uniswap, Aave, etc.). Cap enforcement happens in the contract,
not the ERC-20 allowance.
Hosted pages
Two hosted pages live on checkout.exodus-int.com:
https://checkout.exodus-int.com/subscribe/:subscription_checkout_id— the subscribe page returned incheckout_url. The customer connects a wallet, picks a chain, optionally signs an EIP-2612 permit, and signssubscribeAndCharge.https://checkout.exodus-int.com/cancel/:subscription_id— a customer-facing cancel page keyed on the on-chainsubscription_id. The customer connects the subscribing wallet and signscancel(subscriptionId)directly. Surface this URL in your customer account UI for self-service cancellation.
Available Endpoints
- Create Subscription Checkout — Create a new intent and redirect URL
- Get Subscription Checkout — Retrieve intent details
- List Subscription Checkouts — List intents with filters
- Cancel Subscription Checkout — Cancel a pending intent
After the customer signs, the resulting on-chain subscription is managed via the Subscriptions endpoints.
