Payments · ships as a module
Take card payments on your mesh
Hosted Stripe Checkout for one-off purchases and monthly plans, with signature-verified webhooks. Three configuration keys and one endpoint — no SDK, no redeploy, no card data on your servers.
What ships
- Hosted Checkout — one-off (
mode=payment) and recurring (mode=subscription) sessions with inline prices, so re-pricing a plan needs no Stripe Product administration. - Verified webhooks — HMAC-SHA256 over
{timestamp}.{body}, constant-time compare, five-minute replay window. No delivery is ever acted on unsigned. - Renewals and endings —
invoice.paidandcustomer.subscription.deletedare read in every metadata shape Stripe has shipped, so an API-version move cannot quietly stop renewing anyone. - Cancel at period end — a subscriber who cancels keeps the month they paid for. Stripe reports the actual ending; the portal never guesses.
What it deliberately does not do
- No card data touches this portal. The buyer pays on Stripe's own page; this module holds only a session id and a URL.
- No SDK. The three REST calls it makes are asserted field-by-field by its own suite. An SDK would pin an API version — exactly the thing the webhook reader has to stay tolerant of.
- No routing decisions. Where a buyer returns to is passed in by the caller, so moving a checkout surface can never leave the payment provider pointing at a dead URL.
Setting it up
| Key | What it is |
|---|---|
Commerce:Stripe:SecretKey |
the server-side API secret (sk_test_… / sk_live_…) |
Commerce:Stripe:WebhookSecret |
the endpoint signing secret (whsec_…) |
Commerce:BaseUrl |
this portal's public base URL |
Add a Stripe endpoint at {Commerce:BaseUrl}/api/hooks/Store/Payments, subscribe it to
checkout.session.completed, checkout.session.expired, invoice.paid and
customer.subscription.deleted, and allowlist the target with WebhookInbox:Targets:0 = Store/Payments.
A portal with no secret key simply does not sell — it says so, rather than failing at the till.