feat(billing): persist verified Stripe webhook events - #521
Draft
seonghobae wants to merge 35 commits into
Draft
Conversation
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: trueThanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Caution Review failedAn error occurred during the review process. Please try again later. 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. Comment |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Buyer and evidence impact
Refs #488. This bounded stacked slice turns already-authenticated Stripe webhook deliveries into normalized durable evidence without granting entitlement authority or retaining the signed raw body. It separates immutable provider-event facts from individual delivery/replay observations and fails closed when one event identity is presented with different authenticated bytes.
Exact current stack and scope
develop:df0fa17bd5035af6455c889022c540b4f439e3d6;feat/stripe-webhook-trust-boundary-488@9c72a3adfdfaeaf826f694e5a25699341c3358fe;3f63e97cc730cd3874f3d78baf6ecadf50c38623;The child is a non-destructive reconciliation of predecessor child
dde711f12988c816e5714e33eb9ed038da695191with exact current parent9c72a3adfdfaeaf826f694e5a25699341c3358fe, followed by a narrow changelog repair. The effective semantic delta is limited to:CHANGELOG.md— exactly five event-ledger lines added, with zero parent deletions;docs/doctoring/stripe-webhook-event-ledger.md;package.json;server/db.mjs;server/stripe_webhook.mjs;server/stripe_webhook_event_ledger.mjs;tests/api/stripe-webhook.test.mjs;tests/unit/coverage-script-contract.test.mjs;tests/unit/stripe-webhook-event-ledger.test.mjs;tests/unit/stripe-webhook-recorder-integration.test.mjs.The semantic package and coverage-contract merge preserves the parent’s composed
server/app.mjs/server/application_routes.mjsroute graph, protected/toast-state.cssdelivery, OpenCode and contextual-orchestrator attribution/routing regressions, Checkout attempt/reconciliation/provider tests, webhook trust tests, accessibility tests, and current owned-production coverage registrations. No Clearfolio, attachment, general auth/session, dependency lockfile, workflow, scanner suppression, or branch-protection artifact is changed.Any parent, contributor-head, or protected-base movement invalidates ancestry- and head-specific evidence until freshly reconciled.
Normalized durable evidence contract
server/stripe_webhook_event_ledger.mjsowns a bootstrap-installed SQLite boundary with two normalized relations:billing_stripe_webhook_eventsstores one immutable bounded event fact, provider/object metadata, SHA-256 of the exact authenticated bytes, and first receipt time; andbilling_stripe_webhook_deliveriesappends each accepted delivery/replay observation with one non-redundant replay classification.The ledger does not retain signed raw bodies, Stripe secrets, session tokens, arbitrary provider text, or entitlement decisions. Exact event-ID/hash duplicates append explicit replay evidence; the same event ID with different authenticated bytes fails closed without creating false duplicate evidence. Non-null Stripe request metadata must be a bounded non-array object with a valid request ID. The production route proves that a signed durable event cannot upgrade an organization, and concurrent exact duplicates converge to one immutable event fact plus separate delivery evidence.
Transaction and cleanup safety
Event-fact and delivery mutations share one SQLite savepoint:
ROLLBACK TO SAVEPOINTis confirmed;This prevents the outermost-savepoint hazard where unconditional
RELEASEafter a failed rollback could commit partial writes.TDD and causal repairs
The ledger and route contracts were hardened through observed RED→GREEN cycles:
processing_resultstate, and hosted tests stayed RED until the production column/insert and then the stale API query were removed;tests/unit/coverage-script-contract.test.mjslocks the mounted parent route graph, orchestrator attribution, webhook verifier, durable event ledger, and both focused event suites into the canonical owned-production coverage path.Current exact-head evidence
Fresh repository-native workflows for exact contributor head
3f63e97cc730cd3874f3d78baf6ecadf50c38623are queued and therefore non-passing:32272733355;32272733489; and32272734205.The current review inventory contains no submitted review and no review thread. There is no qualifying independent current-head approval. Queued, pending, skipped-required, cancelled, absent, neutral, failed, stale, predecessor, synthetic, status-only, author-only, or model-only evidence is non-passing.
Scope boundary
This PR remains Draft and must not integrate independently of #520, #516, #511, #507, and #505. It does not infer monotonic subscription state, process out-of-order lifecycle changes, retrieve current provider state, grant or revoke entitlement, or define long-term evidence retention/export policy. #525 and later stacked slices own subsequent provider-read and observation boundaries.
Merge gate
After the prerequisite stack reaches protected
develop, reconcile this exact bounded semantic diff onto the resulting live head and rerun every then-applicable exact-head CI, browser E2E, statement/branch/function/line coverage, docstring, SAST, security, dependency, supply-chain, package/provenance, migration/recovery, resolved-thread, and qualifying independent-approval gate required by live policy. No predecessor-head, author-only, skipped, neutral, status-only, synthetic, or model-only evidence transfers.