How to make Stripe Checkout auto-renewal compliant
What Stripe Checkout shows by default for a subscription, what ROSCA and state automatic-renewal laws additionally require, and the exact Checkout Session parameters, page changes, emails and portal settings that close each gap.
Stripe Checkout is the fastest way to sell a subscription, but its default subscription page was not designed around automatic-renewal law. This guide maps each legal requirement to what Stripe shows and to the change that closes the gap. Every requirement is from Checkbox's legal catalog, with its citation.
What Stripe shows by default
For a mode: "subscription" session, Stripe's hosted page shows the product, the recurring price and interval in the order summary, a trial line when there is a trial, and a short authorization statement near the button. It does not, by default:
- state that the subscription continues until cancelled and how to cancel;
- ask for consent to the renewal terms with a checkbox the customer must tick;
- make the terms conspicuous — Stripe's custom text is small and grey and you cannot restyle it.
The gaps and the fixes
| Requirement | Citation (examples) | Stripe default | Fix |
|---|---|---|---|
| Disclose that it renews until cancelled, the price, frequency, term, trial terms and how to cancel, before billing information | 15 U.S.C. § 8403(1); Cal. Bus. & Prof. Code § 17602(a)(1); N.Y. Gen. Bus. Law § 527-a(1)(a) | Price and interval only | custom_text.submit.message with the full disclosure |
| Affirmative (express) consent to the renewal terms before charging | 15 U.S.C. § 8403(2); Cal. Bus. & Prof. Code § 17602(a)(2), (a)(4); N.Y. Gen. Bus. Law § 527-a(1)(b) | None | consent_collection.terms_of_service: "required" and custom_text.terms_of_service_acceptance.message stating the terms |
| Clear and conspicuous, in visual proximity to the consent | Cal. Bus. & Prof. Code § 17601(c), § 17602(a)(1) | Small grey text | Also show the set-off disclosure on your own page before the redirect |
| Acknowledgment the consumer can keep, with terms and how to cancel | Cal. Bus. & Prof. Code § 17602(a)(3); N.Y. Gen. Bus. Law § 527-a(1)(c) | Stripe's receipt (no renewal terms or cancellation instructions) | Send an acknowledgment from your checkout.session.completed handler |
| Trial-ending and renewal notices inside each state's window | Cal. Bus. & Prof. Code § 17602(b); N.Y. Gen. Bus. Law § 527-a(1)(f), (h) | Optional trial reminder 7 days before | Send them on the dates from checkNoticeSchedule |
| Simple online cancellation | 15 U.S.C. § 8403(3); Cal. Bus. & Prof. Code § 17602(d); N.Y. Gen. Bus. Law § 527-a(1)(d) | Customer portal, if enabled | Enable cancellation in the portal and link it from the account page |
The Checkout Session params
Checkbox generates the wording from your real terms and verifies it with the rules engine. Ask for it with your own params (here a 14-day trial at $19.99 a month):
cat > session.json <<'EOF'
{
"mode": "subscription",
"line_items": [{ "price_data": { "currency": "usd", "unit_amount": 1999, "recurring": { "interval": "month" }, "product_data": { "name": "Joybox Pro" } }, "quantity": 1 }],
"subscription_data": { "trial_period_days": 14 },
"success_url": "https://joybox.example/welcome"
}
EOF
checkbox check stripe-session --file session.json --merchant-name Joybox \
--cancel-url https://joybox.example/account/cancel --offlineThe correction adds:
[
{ "op": "add", "path": "/consent_collection", "value": { "terms_of_service": "required" } },
{
"op": "add",
"path": "/custom_text",
"value": {
"submit": { "message": "Your free trial lasts 14 days, then $19.99 every month. Cancel before the trial ends and you won't be charged. Your Joybox subscription automatically renews every month at $19.99 (plus applicable taxes) until you cancel. You can cancel anytime online at https://joybox.example/account/cancel; …" },
"terms_of_service_acceptance": { "message": "I agree that after my 14-day free trial, my Joybox subscription renews automatically every month at $19.99 (plus applicable taxes) until I cancel, and I understand I can cancel anytime online at https://joybox.example/account/cancel." }
}
}
]consent_collection.terms_of_service requires a Terms of Service URL in your Stripe settings (Dashboard → Settings → Public details).
Why one check stays "needs review"
Even with the params above, Checkbox reports signup.disclosure.conspicuous as needs_review on any Stripe-hosted page: whether text is clear and conspicuous depends on its size, contrast and setting-off, and Stripe renders custom text in its own small grey style. California defines "clear and conspicuous" as larger type, contrasting type, font or color, or text set off by symbols — legible is not enough (Cal. Bus. & Prof. Code § 17601(c)). The robust fix is to show the same disclosure on your own page, next to the button that starts checkout, set off in a box at body size or larger. Checkbox Elements renders exactly that.
After checkout
- Acknowledgment: Stripe's receipt does not restate the renewal terms or how to cancel. Send your own from the
checkout.session.completedwebhook; check the template withcheckEmail(kind: "acknowledgment"). - Reminders: get the dates for your plan from
checkNoticeSchedule. - Cancellation: enable cancellation in the customer portal or use the hosted cancellation page.
- Prove it end to end: a test subscription runs the whole lifecycle in Stripe test mode with a test clock.
Do it with your coding agent
Make our Stripe Checkout subscription flow compliant with automatic-renewal laws using Checkbox
(https://checkbox.site/docs/learn/stripe-checkout-auto-renewal-compliance.md). Preflight the exact params we pass to
stripe.checkout.sessions.create with `checkbox check stripe-session --json`, apply correction.patch, add the
disclosure on our pricing page with <checkbox-disclosure>, and send an acknowledgment email from our
checkout.session.completed handler. Re-check until everything passes or only needs_review items remain.Checkbox is compliance software, not a law firm; nothing in these docs is legal advice (details). For AI agents: every page is available as Markdown at the same URL plus .md; the index is /llms.txt.