@
All checks were successful
Deploy to Production / deploy (push) Successful in 1m22s

feat(billing): in-app embedded Stripe checkout + webhook hardening

Checkout previously used hosted ui_mode → window.location to checkout.stripe.com,
which pops out of the installed PWA into the system browser. Switch to embedded:

- API: ui_mode embedded_page (stripe-node v22 / API 2025-10 renamed the enum),
  return_url instead of success/cancel_url, returns client_secret.
- web: @stripe/react-stripe-js EmbeddedCheckout mounted in an in-app modal;
  NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY baked at build (Dockerfile arg + compose arg).
- .env.production.example: full Stripe section (was missing) + admin-email
  placeholder (INF-001).

Also bundled (same files): BILL-002 invoice.paid resets quota only on
subscription_cycle; BILL-003 webhook dedup rolled back on handler failure;
BILL-001 change-plan writes plan locally; BILL-004 webhook cross-checks
sub.customer before trusting metadata.orgId; INF-003 API routed off the raw
docker.sock through a locked-down tecnativa/docker-socket-proxy (CONTAINERS+POST).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@
This commit is contained in:
Marco Sadjadi
2026-05-29 20:56:40 +02:00
parent 9d5386ccba
commit cf423de3d5
8 changed files with 256 additions and 20 deletions

View File

@@ -46,9 +46,9 @@ OAUTH_ISSUER=https://api.buildmymcpserver.com
SECRETS_ENCRYPTION_KEY=CHANGE-ME-run-openssl-rand-hex-32
# ---- Admin bootstrap (upserted idempotently on API boot) ----
ADMIN_EMAIL=marco.frangiskatos@gmail.com
ADMIN_EMAIL=CHANGE-ME-admin@example.com
ADMIN_PASSWORD=CHANGE-ME-strong-admin-password
ADMIN_NAME=Marco Frangiskatos
ADMIN_NAME=CHANGE-ME-Admin
# ---- Anthropic (empty = mock generation; set for real Claude generation) ----
ANTHROPIC_API_KEY=
@@ -75,6 +75,23 @@ RUNNER_HOST=buildmymcpserver.com
RUNNER_PORT_RANGE_START=4400
RUNNER_PORT_RANGE_END=4900
# ---- Stripe (billing) ----
# Secret key (server-side only — NEVER expose). From Stripe Dashboard → Developers → API keys.
STRIPE_SECRET_KEY=CHANGE-ME-sk_live_...
# Publishable key (safe to expose). Used by the embedded in-app checkout.
STRIPE_PUBLISHABLE_KEY=CHANGE-ME-pk_live_...
# Same publishable key, exposed to the web client bundle at BUILD time (the web
# image is rebuilt by the deploy, so this must be set before deploying or the
# in-app checkout shows "not configured"). Keep it identical to STRIPE_PUBLISHABLE_KEY.
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=CHANGE-ME-pk_live_...
# Webhook signing secret — from the endpoint you create at /v1/billing/webhook.
STRIPE_WEBHOOK_SECRET=CHANGE-ME-whsec_...
# Price IDs (price_… not prod_…) from each product's pricing in the Dashboard.
STRIPE_PRICE_PRO_MONTHLY=CHANGE-ME-price_...
STRIPE_PRICE_PRO_YEARLY=CHANGE-ME-price_...
STRIPE_PRICE_TEAM_MONTHLY=CHANGE-ME-price_...
STRIPE_PRICE_TEAM_YEARLY=CHANGE-ME-price_...
# ---- Observability (optional) ----
SENTRY_DSN=
OTEL_EXPORTER_OTLP_ENDPOINT=