86 lines
5.1 KiB
Markdown
86 lines
5.1 KiB
Markdown
---
|
|
title: "Stripe Integration"
|
|
audience: [admin, developer]
|
|
summary: "Configure Stripe: flags and file-based secrets, the webhook endpoint and events, catalog-to-Stripe sync, and making a product purchasable."
|
|
---
|
|
|
|
# Stripe
|
|
|
|
This guide covers connecting the member console to Stripe for payment processing. The member console is the source of truth for the product catalog — Stripe handles payment execution.
|
|
|
|
## Prerequisites
|
|
|
|
- A Stripe account (test mode is fine for initial setup)
|
|
- The Stripe secret key (`sk_test_...` or `sk_live_...`)
|
|
- A webhook endpoint signing secret (`whsec_...`)
|
|
|
|
## Configuration
|
|
|
|
| Flag | Description |
|
|
|------|-------------|
|
|
| `--stripe-api-key` | Stripe secret key |
|
|
| `--stripe-api-key-file` | Path to file containing the API key |
|
|
| `--stripe-webhook-secret` | Webhook endpoint signing secret |
|
|
| `--stripe-webhook-secret-file` | Path to file containing the webhook secret |
|
|
| `--stripe-mode` | `test` or `live` (controls dashboard link URLs) |
|
|
|
|
All flags support `_FILE` variants for secret injection from mounted files.
|
|
|
|
## Webhook endpoint
|
|
|
|
Register a webhook in the Stripe Dashboard pointing to:
|
|
|
|
```
|
|
https://<your-domain>/webhooks/stripe
|
|
```
|
|
|
|
Subscribe to these event types:
|
|
|
|
- `customer.created`, `customer.updated`, `customer.deleted`
|
|
- `product.created`, `product.updated`, `product.deleted`
|
|
- `price.created`, `price.updated`
|
|
- `checkout.session.completed`, `checkout.session.expired`
|
|
- `customer.subscription.created`, `customer.subscription.updated`, `customer.subscription.deleted`
|
|
- `invoice.created`, `invoice.finalized`, `invoice.paid`, `invoice.payment_failed`, `invoice.voided`
|
|
- `payment_method.attached`, `payment_method.detached`
|
|
|
|
## How it works
|
|
|
|
The member console is the catalog source of truth; Stripe holds a mirror used for payment execution. Catalog objects do **not** sync to Stripe automatically — syncing a product to Stripe is an **explicit operator action** (see "Making a product purchasable" below). Once synced, members subscribe via Stripe Checkout, Stripe runs the billing cycle, and webhooks project Stripe state (subscriptions, invoices, payments) back into the member console's core tables.
|
|
|
|
Sync runs through a transactional outbox: the operator action enqueues `create_stripe_product` and `create_stripe_price` entries, and a worker drains them against the Stripe API. The resulting `stripe.product_mappings` / `stripe.price_mappings` rows record the mapping and its `sync_status`, which moves from `pending` to `synced` (or to `deleted`). `dead_letter` is a status on the outbox entry itself, not on the mapping: when an entry fails repeatedly and the outbox marks it `dead_letter`, the mapping it was trying to create stays at `pending` until an operator retries the entry and the retry succeeds.
|
|
|
|
## Making a product purchasable
|
|
|
|
A member can only buy a product once **every** purchasability precondition is met. The operator product edit page (`/operator/products/{id}`) shows a **Purchasability** panel that evaluates each one and renders a single verdict — **Purchasable** or **Incomplete** — naming exactly what is missing. The preconditions:
|
|
|
|
1. **Published** — set the product's lifecycle to `published`.
|
|
2. **Public & active** — mark it Active and Public so it appears in the member catalog.
|
|
3. **Structural kind** — put it on a plan ladder to sell it as a plan (a single-tier ladder is the idiom for a standalone plan), **or** give it a product type (`addon` / `usage` / `one_time`). A published, untyped product on no ladder is invisible "limbo", which the panel flags.
|
|
4. **Active price** — add a price on the product's Prices view.
|
|
5. **Stripe-mapped price** — the active price is mapped to a live Stripe price.
|
|
|
|
The last step is the one to do **explicitly**: on the Purchasability panel, click **Sync to Stripe**. That enqueues the product and price sync; the precondition shows **Sync pending** until the outbox worker drains, then flips to **Met** and the verdict becomes **Purchasable**. Adding a price does **not** auto-sync — click Sync to Stripe whenever a product or its active price is not yet mapped.
|
|
|
|
If Stripe is **not configured** for the deployment (no API key), the panel says so on the Stripe-mapped precondition instead of a dead "missing" marker — pricing simply cannot be Stripe-mapped, though grant-based access still works without Stripe.
|
|
|
|
## Operator guidelines
|
|
|
|
### Do in the member console
|
|
|
|
- Create and manage products, prices, and entitlement sets
|
|
- Monitor sync status in the operator panel
|
|
- Create grants for admin-granted access (bypasses billing)
|
|
|
|
### Do not do in Stripe
|
|
|
|
- **Do not create products or prices in the Stripe Dashboard.** They will not be recognized by the member console.
|
|
- **Do not delete products or prices in Stripe.** Deactivate them in the member console instead.
|
|
- **Do not modify subscription quantities or prices in Stripe.** Changes should flow through the member console.
|
|
|
|
### Acceptable in Stripe
|
|
|
|
- View payment details, invoices, and customer records
|
|
- Handle disputes and issue refunds (until member console tooling exists)
|
|
- Configure tax settings, payment method types, and Stripe-level features the member console does not manage
|