Setting Stripe up
This package ships the payment provider. Nothing here needs a Stripe SDK, a build step or a redeploy — a portal that holds the keys sells, and a portal that does not says so.
1. The three keys
| Key | What it is | Where it comes from |
|---|---|---|
Commerce:Stripe:SecretKey |
the server-side API secret (sk_test_… / sk_live_…) |
Stripe dashboard → Developers → API keys |
Commerce:Stripe:WebhookSecret |
the endpoint signing secret (whsec_…) |
Stripe dashboard → the endpoint you add in step 2 |
Commerce:BaseUrl |
this portal's public base URL | the deployment |
As environment variables they are Commerce__Stripe__SecretKey, Commerce__Stripe__WebhookSecret
and Commerce__BaseUrl.
🚨 The key names are contract. They are read out of environment variables, Key Vault secret names and helm values on every portal that sells, so renaming one is a silent deletion: a renamed key reads as absent, and absent means "this portal does not sell".
🚨 A secret's VALUE never leaves configuration. Nothing logs it, renders it or stores it on a node. The only thing derived from the secret key is its prefix, which names test-vs-live mode.
2. The webhook endpoint
Add an endpoint in the Stripe dashboard pointing at:
{Commerce:BaseUrl}/api/hooks/Store/Payments
Subscribe it to exactly these four events:
checkout.session.completed— the purchase that payscheckout.session.expired— the buyer who walked awayinvoice.paid— the monthly RENEWAL of a subscriptioncustomer.subscription.deleted— the END of a subscription
🚨 The last two are what keeps a subscription alive. An endpoint not subscribed to
invoice.paid grants each plan exactly one month while Stripe keeps charging the card;
without customer.subscription.deleted a cancelled subscriber keeps access forever.
Then allowlist the target on the portal: WebhookInbox:Targets:0 = Store/Payments. The inbox is
fail-closed — without the allowlist the endpoint answers 404 and stores nothing, however correct
the Stripe side is.
3. Test and live are separate worlds
Test-mode and live-mode endpoints are configured separately at Stripe, so a live-mode endpoint
never fires for an sk_test_… checkout and vice versa. A key lists only the endpoints of its own
mode — which is what lets Store/Maintenance → PaymentPathAudit tell "no endpoint registered"
apart from "registered in the other mode".
What the module does, and deliberately does not
- Does: create hosted Checkout sessions (one-off and recurring, with inline prices), verify
every delivery's
Stripe-SignatureHMAC inside a five-minute replay window, parse the events, and stop a subscription at the END of the period already paid for. - Does not: decide who is entitled to what, price anything, write an order, or send anyone anywhere. Where a buyer returns to is passed in by the caller, so moving a checkout surface can never leave the payment provider redirecting at a dead URL.
Reading further
- Payments — what the Store does with a verified delivery, and how to read a
non-empty
_Rejected. - Subscriptions — the recurring lane end to end.