Implement the ux-first-run change: a state-derived setup checklist on /operator/setup with a landing region that recedes once required steps are done, and empty states that distinguish blocked from empty across operator and member surfaces. Also add production deployment and environment reference docs, plus a config-key completeness test.
Stripe Integration (internal)
This package implements the Stripe provider integration. For self-hosting setup instructions, see docs/stripe.md.
Data flow
Operator creates product/price in MC
→ Row inserted into billing schema
→ Outbox entry enqueued (integration.outbox)
→ Temporal outbox poller picks up entry
→ Stripe API call (creates Stripe object)
→ Mapping row written (stripe.*_mappings)
Stripe sends webhook
→ POST /webhooks/stripe
→ Inserted into integration.webhook_events
→ Temporal webhook processor picks up event
→ Mapping row created/updated/confirmed
→ Core billing tables updated (subscriptions, invoices, payments)
Mapping tables
Each billing entity has a corresponding mapping table in the stripe schema:
| MC table | Mapping table | Stripe ID column |
|---|---|---|
billing.products |
stripe.product_mappings |
stripe_product_id |
billing.prices |
stripe.price_mappings |
stripe_price_id |
billing.accounts |
stripe.customer_mappings |
stripe_customer_id |
billing.subscriptions |
stripe.subscription_mappings |
stripe_subscription_id |
billing.invoices |
stripe.invoice_mappings |
stripe_invoice_id |
billing.payments |
stripe.payment_mappings |
stripe_payment_intent_id |
Each mapping row has a sync_status: pending, synced, or deleted.
No Stripe IDs appear on core billing tables (Decision 113). The mapping tables are the only join point.
Provider config
Stripe declares its configuration via ConfigSpec() (stripe-api-key, stripe-webhook-secret, stripe-mode); credentials live in environment variables, never in the database. Non-secret keys are operator-overridable app-wide via core.integration_config_overrides (see docs/building-an-integration.md §6). The former stripe.provider_configs singleton was dropped in migration 00002 — it was never read or written by any code.
Temporal workflows
Two long-running workflows in internal/integrations/stripe/workflows/:
stripe-outbox-poller(outbox.go) — Pollsintegration.outboxfor pending entries, executes them (Stripe API calls), writes mapping rows. UsesContinueAsNew.stripe-webhook-processor(webhook.go) — Pollsintegration.webhook_events, dispatches to per-event-type handlers.
Both start automatically on member-console start.
Outbox actions
| Action type | Trigger | Executor |
|---|---|---|
create_stripe_customer |
Billing account creation | executeCreateStripeCustomer |
create_stripe_product |
Migration seed / operator UI | executeCreateStripeProduct |
create_stripe_price |
Migration seed / operator UI | executeCreateStripePrice |
Key files
migrations/— Schema and mapping table DDL, seed outbox entriesqueries/— sqlc queries for mapping table CRUDensure_customer.go— Stripe Customer creation payloadinternal/integrations/stripe/workflows/outbox.go— Outbox poller and executorsinternal/integrations/stripe/workflows/webhook.go— Webhook event dispatcher and handlersinternal/server/stripe_webhook.go— HTTP handler forPOST /webhooks/stripe