Do this once per environment (test vs live). Customers click Subscribe in the dashboard; Stripe calls our HTTPS webhook to attach subscription state to the tenant ID.
Widget embed (no Stripe): customers complete Website setup at the top of /app so their live https:// origin is allowlisted for sign-in — they do not configure Google or Turnstile themselves.
Stripe Connect (same Worker): sign in, then open Connect setup for the V2 Connect flow (onboarding, products, storefront, thin + subscription webhooks). The operator dashboard SPA at /app does not embed this flow yet — it is a separate page served by the Worker.
One Price ID per tier. Checkout sends your selected tier to the Worker, which loads a different Stripe price_… for Starter, Pro, and Business. If every Worker secret points at the same Price (for example only the $99 Business price), Stripe will always show $99/mo no matter which radio the customer picks. Use three distinct recurring prices from Stripe Products.
| Public plan | Checkout tier / dashboard radio | Worker secret (paste your Stripe Price ID) |
|---|---|---|
| Starter — $9/mo | starter | STRIPE_PRICE_STARTER_ID → price_1TW1Or7W05iPLYSvorue2Fdj |
| Pro — $29/mo | pro | STRIPE_PRICE_PRO_ID (or legacy STRIPE_PRICE_ID) → price_1TW1Or7W05iPLYSv5Vg4ErKE |
| Business — $99/mo | business | STRIPE_PRICE_BUSINESS_ID → price_1TW1Os7W05iPLYSvG4YEmF4L |
If you fork CapchaCloud or use a different Stripe account, replace the price_… values with your own from Stripe → Products → Price. The Worker still reads them from secrets (npx wrangler secret put …); nothing in git pushes them to Cloudflare automatically.
trust-engine:
npx wrangler d1 migrations apply consent-metadata --remoteUse your database name from
wrangler.jsonc if different.
price_…) for the tiers you sell publicly — e.g. Starter ($9), Pro ($29), Business ($99) — and map them to STRIPE_PRICE_STARTER_ID, STRIPE_PRICE_PRO_ID (or legacy STRIPE_PRICE_ID), and STRIPE_PRICE_BUSINESS_ID.
trust-engine:
npx wrangler secret put STRIPE_SECRET_KEY npx wrangler secret put STRIPE_WEBHOOK_SECRET npx wrangler secret put STRIPE_PRICE_STARTER_ID npx wrangler secret put STRIPE_PRICE_PRO_ID npx wrangler secret put STRIPE_PRICE_BUSINESS_ID npx wrangler secret put STRIPE_PRICE_ID
The last line is optional: STRIPE_PRICE_ID is a Pro-only legacy default when STRIPE_PRICE_PRO_ID is unset. Starter and Business never read STRIPE_PRICE_ID.
scripts\set-stripe-secrets.ps1 (interactive). Non-interactive: set $env:STRIPE_* then scripts\push-stripe-secrets-from-env.ps1. Test keys sk_test_; live sk_live_.
STRIPE_PRICE_ID is stored as a secret so it isn't exposed to the browser. It is the Pro default when STRIPE_PRICE_PRO_ID is unset. For Starter and Business checkout, set STRIPE_PRICE_STARTER_ID and STRIPE_PRICE_BUSINESS_ID separately — they do not fall back to STRIPE_PRICE_ID (that mismatch used to charge the wrong tier). Redeploy after changing secrets.
https://YOUR_DOMAIN/webhooks/stripe (e.g. https://capchacloud.com/webhooks/stripe).checkout.session.completed,
customer.subscription.updated,
customer.subscription.deleted.whsec_…) and set it as STRIPE_WEBHOOK_SECRET.
npx wrangler deploy --env "" --keep-vars
stripe login stripe listen --forward-to http://127.0.0.1:8787/webhooks/stripeCopy the CLI webhook signing secret into
.dev.vars as STRIPE_WEBHOOK_SECRET while developing.
Verify: sign in → Trust & billing → Subscribe → complete Checkout → billing status should become active after webhook delivery (refresh).