Status
Meta Museum uses Stripe-hosted Checkout Sessions for the initial one-time supporter flow. The connected sandbox belongs to Sun & Rain Works, the sole-proprietorship operator of the Meta Museum project. Production checkout remains disabled until live-account activation, offer/refund approval, webhook evidence retention, and production secrets are completed.
Architecture
- `POST /api/stripe/checkout` accepts a bounded whole-dollar amount ($5–$500) and creates a one-time hosted Checkout Session.
- Only `sk_test_` keys are accepted by the application route. This prevents accidental live charges during sandbox development.
- Stripe receives all payment details; Meta Museum never receives card numbers.
- Offer and revision metadata are attached to both the Session and PaymentIntent.
- `POST /api/stripe/webhook` verifies Stripe's signature over the unchanged raw request body with a five-minute tolerance, then writes to Postgres transactionally.
- `stripe_webhook_events` enforces unique provider event IDs; a reused ID with different evidence fails as a collision.
- A transaction-scoped advisory lock serializes concurrent events for the same PaymentIntent before the receipt projection is rebuilt.
- `stripe_payment_receipts` retains the deterministic lifecycle projection and its reproducible SHA-256 receipt.
- Checkout, PaymentIntent, charge, asynchronous failure/success, refund, and dispute events reduce in provider-created order, so delivery order does not control the result.
- Amount, currency, offer/revision, payment status, refunds, disputes, settlement, and observed Stripe fees remain explicit. An unobserved fee is `null`, never silently zero.
- Every `livemode: false` receipt is permanently `excluded-test-mode` with `verifiedRevenueMinor: 0`.
- The revenue adapter emits no event for sandbox, unsettled, refunded, disputed, non-USD, unlinked, or incomplete evidence. It also requires the independently retained checkout-start event ID.
Local verification
Add sandbox credentials and the Stripe CLI-provided signing secret to `.env`. Never commit either value. The repeatable proof flow is:
pnpm revenue:stripe:sandbox-delivery-proof
pnpm revenue:stripe:sandbox-receipt-verify
The first command opens a bounded five-minute listener window and creates a real hosted sandbox Checkout Session. Complete that Session with a Stripe test card. It retains a non-secret delivery proof only after HTTP 200 delivery, Postgres persistence, receipt hashing, and test-revenue exclusion succeed. The second command runs in a fresh process and reproduces the stored receipt hash, checks event-ID uniqueness, and retains restart evidence.
Recovery: redeliver the same Stripe event. The event primary key makes it a no-op when its payload digest matches; a digest mismatch raises a collision error. If projection fails, Stripe receives HTTP 500 and retries. Never delete an event row to force reprocessing; repair the reducer and deterministically rebuild the receipt from retained normalized events.
Production requires a separate reviewed change: live keys in encrypted hosting configuration, a production webhook endpoint registration, durable idempotent event receipts, refunds/disputes, approved public terms, and a live-mode smoke test that does not count self-funding or test transactions as revenue.