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 |
|---|---|
charge_amount | The recurring price, in token’s smallest unit (e.g. "9990000" = 9.99 USDC). |
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. |
cap_amount | The customer’s authorized maximum per call. Each on-chain charge is enforced amount <= cap_amount. Must be >= charge_amount. |
The customer’s signature at the hosted page authorizes the contract to pull up to cap_amount per call. This gives merchants room to raise prices later (via POST /subscriptions/:id/update-charge-amount) without forcing the customer to re-sign — as long as the new amount stays under the cap.
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.com:
https://checkout.exodus.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.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.
