Public: anyone can read this; the crew can change it.

Stripe is connected by pasting one key: checkout and the customer portal run over fetch, with nothing to set up in Stripe's dashboard #11163

opened this in answer to

Check · Review · Land · Live — Its check is running. Next: reviewer, no review yet.

Ready to land

The admin can now connect Stripe by pasting one key, and the Stripe client for checkout, portal and reconcile is written and tested against a fake `fetch`. Nothing calls checkout, portal or reconcile yet, and nothing receives `/hooks/stripe`.

12 files · +582 −10

working checking now deploy 5dc2d00

Check running · 2 min · 422 of 2713 files · 2595 passed, 0 failedCheck of 5dc2d00checked against main at 8869849fast tier: running · full gate runs on land

Website spec: 19 findings (report only)
opened this

The admin can now connect Stripe by pasting one key, and the Stripe client for checkout, portal and reconcile is written and tested against a fake fetch. Nothing calls checkout, portal or reconcile yet, and nothing receives /hooks/stripe.

tsc, scripts/gate.ts, the design lint and the billing, Stripe and cloud-gate tests all pass. I also ran the manifest and cloud test files. One of them, test/cloud-accounts-etag.test.ts, printed a route-timings Invalid Date warning. I did not rerun it after the fix below, so I can't say it passes. I did not run the full suite. The keep_plan tool wasn't available to me, so no plan is kept on the issue.

  • Stripe client: src/billing/stripe.ts is fetch and Web Crypto only. It has checkout, portal, reconcile, addWebhook and removeWebhook.
  • Wire format: every call pins Stripe-Version (the STRIPE_VERSION constant), sends Authorization: Bearer, and uses the bracketed form encoding. Each POST carries an Idempotency-Key: checkout-<order> and portal-<order>. The webhook registration key is derived from the endpoint URL, since there is no order id at that point.
  • Errors: they carry Stripe's status and error code only, never the key or the request body.
  • Admin Billing page: a Provider section takes the key. The word sandbox or live appears beside stripe, and the key is never shown. The key is sealed as STRIPE_KEY_TEST or STRIPE_KEY_LIVE, so the mode comes from the secret's name and viewing the page opens no secret.
  • Connect: the forge registers its webhook at <origin>/hooks/stripe with the five events and seals the signing secret.
  • Key problems: a key Stripe rejects is not stored. A key without webhook permission is stored, told so in plain words, and the page offers a whsec_ field. Disconnect removes the endpoint and the sealed values.
  • Vendor declaration: I added vendor stripe and use payments to the cloud.json sets. The forge declares them for itself in FORGE_STRIPE, and every call checks mayUse first.
  • Docs: docs/PROFIT.md has the five-line "Connect Stripe" steps, with docs/features/billing.md and docs/CLOUD.md updated.
  • Tests: test/stripe.test.ts asserts the exact checkout form body and headers, the status mapping, error redaction and the same-origin refusal. It also checks that the key never reaches the page, the secrets table or the events.
  • Changed test: I changed one assertion in test/billing.test.ts. It said the Billing page must not contain "Stripe", which was only true while no processor existed.

Left out:

  • The /hooks/stripe receiver and signature verification.
  • The buttons or routes that call checkout, portal and reconcile.
  • A terminal or MCP door for connecting.

The signing secret is sealed but nothing reads it yet.

Try: open /settings/billing as an admin, paste a sk_test_ key under Provider and press Connect.

🤖 Generated with Claude Code

Closes #11136

commented

The admin can now connect Stripe by pasting one key, and the Stripe client for checkout, portal and reconcile is written and tested against a fake fetch. Nothing calls checkout, portal or reconcile yet, and nothing receives /hooks/stripe.

tsc, scripts/gate.ts, the design lint and the billing, Stripe and cloud-gate tests all pass. I also ran the manifest and cloud test files. One of them, test/cloud-accounts-etag.test.ts, printed a route-timings Invalid Date warning. I did not rerun it after the fix below, so I can't say it passes. I did not run the full suite. The keep_plan tool wasn't available to me, so no plan is kept on the issue.

  • Stripe client: src/billing/stripe.ts is fetch and Web Crypto only. It has checkout, portal, reconcile, addWebhook and removeWebhook.
  • Wire format: every call pins Stripe-Version (the STRIPE_VERSION constant), sends Authorization: Bearer, and uses the bracketed form encoding. Each POST carries an Idempotency-Key: checkout-<order> and portal-<order>. The webhook registration key is derived from the endpoint URL, since there is no order id at that point.
  • Errors: they carry Stripe's status and error code only, never the key or the request body.
  • Admin Billing page: a Provider section takes the key. The word sandbox or live appears beside stripe, and the key is never shown. The key is sealed as STRIPE_KEY_TEST or STRIPE_KEY_LIVE, so the mode comes from the secret's name and viewing the page opens no secret.
  • Connect: the forge registers its webhook at <origin>/hooks/stripe with the five events and seals the signing secret.
  • Key problems: a key Stripe rejects is not stored. A key without webhook permission is stored, told so in plain words, and the page offers a whsec_ field. Disconnect removes the endpoint and the sealed values.
  • Vendor declaration: I added vendor stripe and use payments to the cloud.json sets. The forge declares them for itself in FORGE_STRIPE, and every call checks mayUse first.
  • Docs: docs/PROFIT.md has the five-line "Connect Stripe" steps, with docs/features/billing.md and docs/CLOUD.md updated.
  • Tests: test/stripe.test.ts asserts the exact checkout form body and headers, the status mapping, error redaction and the same-origin refusal. It also checks that the key never reaches the page, the secrets table or the events.
  • Changed test: I changed one assertion in test/billing.test.ts. It said the Billing page must not contain "Stripe", which was only true while no processor existed.

Left out:

  • The /hooks/stripe receiver and signature verification.
  • The buttons or routes that call checkout, portal and reconcile.
  • A terminal or MCP door for connecting.

The signing secret is sealed but nothing reads it yet.

Try: open /settings/billing as an admin, paste a sk_test_ key under Provider and press Connect.

🤖 Generated with Claude Code

commented

Stripe can now be connected by pasting one key. This push fixes the failing check: src/apps/cloud.ts is imported now, so it comes off the unreached allow list in scripts/unreached-allow.json. The pull imports it through the new src/billing/stripe.ts and src/billing/connection.ts.

Of the three failures in the check log, only that one needed a fix:

  • Unreached list: test/unreached.test.ts now passes locally.
  • test/traffic-history.test.ts: passes locally; I changed nothing there.
  • test/quotas-running.test.ts: passes locally in about 19 s. The check log shows the preview-limit test taking 420 s, so I read that as the shard timing out under load, not as a code fault. I did not find a cause for it, so it may fail again.

bun x tsc --noEmit is clean. I did not re-run scripts/gate.ts or scripts/design-lint.ts for this one-line change.

To try it: open /settings/billing as an admin, paste an sk_test_ key under Provider and press Connect. The word "sandbox" then shows beside stripe.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs