Skip to content

fix: replace demo billing paths with verified Stripe lifecycle - #456

Closed
seonghobae wants to merge 18 commits into
developfrom
fix/production-billing-fail-closed-20260809
Closed

fix: replace demo billing paths with verified Stripe lifecycle#456
seonghobae wants to merge 18 commits into
developfrom
fix/production-billing-fail-closed-20260809

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Summary

  • fail closed when production Checkout credentials are absent or incomplete
  • create Stripe Checkout Sessions through the server trust boundary with an idempotency key, fixed API version, organization metadata on both session and subscription, and trusted checkout-host validation
  • add raw-body Stripe signature verification, timestamp tolerance, constant-time HMAC comparison, event-ID deduplication, and transactional entitlement updates
  • add unit and API regressions for unsigned events, tampering, retries, subscription statuses, provider failures, and development-only mock isolation
  • include billing modules in production coverage

Current implementation state

The production route integration is now present directly in server/app.mjs. The completed one-shot writer workflow was removed from the branch so no self-modifying CI remains. Exact-head CI/security/review evidence must run on the current branch head before Ready/merge.

Security boundary

  • mock Checkout is available only under SCOPEWEAVE_DEV=1
  • provider credentials and raw provider payloads are never returned
  • unsigned, stale, malformed, oversized, or wrong-secret events fail closed
  • entitlement changes are idempotent across Stripe retries
  • an unknown organization cannot be upgraded

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fed11941-9398-4f9a-92f5-3b306e9398db

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copy link
Copy Markdown
Contributor Author

Closing this draft because it is not yet a production-safe subscription lifecycle despite substantial useful groundwork.

Current merge blockers are architectural, not cosmetic:

  • the route reads the entire raw webhook body before applying the advertised 1 MiB limit;
  • Checkout success/cancel URLs are derived from the request origin rather than an operator-configured public origin;
  • the provider call has no connection/body timeout or response-size bound;
  • the generated idempotency key is random per request and is not tied to a durable checkout attempt;
  • event-ID deduplication does not solve Stripe's documented out-of-order delivery, yet the implementation immediately overwrites entitlements without an event/object freshness cursor;
  • the runtime creates billing_event_records inside webhook processing rather than through the database schema/migration contract;
  • no durable Stripe customer, subscription, checkout-session, or price mapping exists, so metadata alone is the long-term organization identity;
  • the documentation claims production startup validation, but the code validates only when Checkout is requested;
  • renewal provisioning is not modeled through invoice.paid/payment-failure state, while Stripe's subscription guidance treats those asynchronous events as part of the lifecycle.

The fixed 2026-02-25.clover API version is still an official Clover GA version, and raw-body HMAC verification plus constant-time comparison are worth retaining. The replacement must preserve those pieces while adding a normalized billing schema, bounded transport/body handling, trusted public-origin configuration, durable checkout idempotency, customer/subscription linkage, and monotonic event reconciliation. No entitlement-changing billing PR should merge until those invariants have true database and API tests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant