Stripe
Payments run through the tenant’s own Stripe account — Orchard orchestrates checkout and records orders, but funds settle to the tenant. Orchard never custodies money or touches card data (PCI SAQ A — see Security & compliance).
What the store owner configures
A store owner sets this up from Integrations → Stripe in the admin
(/admin/settings/integrations/stripe), not you. It’s covered here so you know
what state the integration can be in before you build against it.
- Integration mode:
directorconnect(see the callout below). directmode fields: the tenant’s own Stripe secret key (sk_...) and publishable key (pk_...).connectmode fields: a connected account ID (acct_...) under an Orchard platform Stripe account.- Webhook signing secret (
whsec_...) — required in both modes, generated by pointing a Stripe webhook endpoint at the URL below. - Test / live — each of the above is captured twice, once per Stripe environment, matching the test/live split of your own API keys.
connectmode is not a working checkout path today. The admin exposes it as a setup option, but the Stripe client Orchard uses for checkout and webhooks only reads a per-tenant secret key — it never applies the connected account’sStripe-Accountheader. A tenant configured forconnectmode has no secret key on file, so checkout will fail. Build againstdirectmode. If you’re evaluating Orchard for a multi-account/platform setup, confirm connect-mode support with your Orchard operator before committing to it.
Creating a checkout session
Your integration code doesn’t pick a mode or pass Stripe credentials — you call the same endpoint regardless of how the tenant is configured:
curl -X POST https://api-test.theorchard.dev/api/v1/checkout/sessions \
-H "Authorization: Bearer sk_test_your_key" \
-H "Content-Type: application/json" \
-d '{
"items": [{ "product_slug": "coldbrew-concentrate", "quantity": 2 }],
"return_url": "https://your-store.com/checkout/success",
"cancel_url": "https://your-store.com/cart",
"customer_email": "shopper@example.com"
}'Response:
{
"checkout_url": "https://checkout.stripe.com/c/pay/cs_test_...",
"order_id": "0191...o9",
"order_number": "ORD-0001"
}Redirect the shopper to checkout_url. See
Cart to checkout to order for the full flow, and
Subscriptions for recurring carts.
Inbound webhooks — Stripe to Orchard
Stripe delivers events to POST /api/webhooks/stripe/{tenant_slug}, verified
with the tenant’s Stripe signing secret. This is internal to Orchard — it’s
how Orchard learns a payment succeeded, a subscription renewed, or a dispute was
opened, and it is a completely different thing from the
outbound webhooks Orchard sends to your integration
(order.paid, subscription.created, etc.). You never configure or consume the
Stripe-inbound endpoint directly; it’s set up once by the store owner from the
Stripe settings page, which shows the exact URL to paste into a Stripe webhook
endpoint.
The signing secret can be rotated from the same page (TOTP-gated). Rotation creates a new Stripe webhook endpoint at the same URL, disables the prior one, and reveals the new secret once, in-page — it’s never placed in a URL or log.
What this means for you as an integrator
You build against one surface — /api/v1 and webhooks — and Stripe
is resolved behind it based on your API key’s tenant. You never pass Stripe
credentials or select a mode; that’s entirely the store owner’s configuration
(with the connect-mode caveat above).