Files
cgalo5758 dd3962990b Adopt entity keys and add invoice numbers
Replace the entity slugs on organizations, workspaces, resource pools,
and
plan ladders with nullable `key` columns and add keys to products,
prices,
and entitlement sets. Rename `providers.slug` to `provider` and add
partial
unique indexes for system and org role names.

Assign invoice numbers per billing account from a gapless transactional
counter; Stripe's number moves to the invoice mapping as an external
reference.

Seeds, fixtures, and the operator lookup address rows by key, and the
returning-login resync no longer blanks a display name when the IdP
sends
no `name` claim.
2026-08-29 20:12:04 -05:00
..

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) — Polls integration.outbox for pending entries, executes them (Stripe API calls), writes mapping rows. Uses ContinueAsNew.
  • stripe-webhook-processor (webhook.go) — Polls integration.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 entries
  • queries/ — sqlc queries for mapping table CRUD
  • ensure_customer.go — Stripe Customer creation payload
  • internal/integrations/stripe/workflows/outbox.go — Outbox poller and executors
  • internal/integrations/stripe/workflows/webhook.go — Webhook event dispatcher and handlers
  • internal/server/stripe_webhook.go — HTTP handler for POST /webhooks/stripe