Skip to content

feat(billing): persist verified Stripe webhook events - #521

Draft
seonghobae wants to merge 35 commits into
feat/stripe-webhook-trust-boundary-488from
feat/stripe-webhook-event-ledger-488
Draft

feat(billing): persist verified Stripe webhook events#521
seonghobae wants to merge 35 commits into
feat/stripe-webhook-trust-boundary-488from
feat/stripe-webhook-event-ledger-488

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

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

  • protected develop: df0fa17bd5035af6455c889022c540b4f439e3d6;
  • exact parent/base: fix(billing): verify Stripe webhook raw-body signatures #520 feat/stripe-webhook-trust-boundary-488@9c72a3adfdfaeaf826f694e5a25699341c3358fe;
  • exact contributor head: 3f63e97cc730cd3874f3d78baf6ecadf50c38623;
  • state: Draft and mechanically mergeable;
  • fresh exact parent→child comparison: ahead 30, behind 0, exact parent as merge base, 10 changed paths, +735/-16.

The child is a non-destructive reconciliation of predecessor child dde711f12988c816e5714e33eb9ed038da695191 with exact current parent 9c72a3adfdfaeaf826f694e5a25699341c3358fe, 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.mjs route graph, protected /toast-state.css delivery, 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.mjs owns a bootstrap-installed SQLite boundary with two normalized relations:

  • billing_stripe_webhook_events stores one immutable bounded event fact, provider/object metadata, SHA-256 of the exact authenticated bytes, and first receipt time; and
  • billing_stripe_webhook_deliveries appends 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:

  • release follows the operation only on success;
  • after an operation failure, release occurs only when ROLLBACK TO SAVEPOINT is confirmed;
  • cleanup-release failure is suppressed only after confirmed rollback so it cannot replace the causal operation error; and
  • an unconfirmed failed savepoint remains open rather than risking a partial commit.

This prevents the outermost-savepoint hazard where unconditional RELEASE after a failed rollback could commit partial writes.

TDD and causal repairs

The ledger and route contracts were hardened through observed RED→GREEN cycles:

  • malformed non-null provider request envelopes failed before the narrow request validator was added;
  • a schema contract exposed redundant processing_result state, and hosted tests stayed RED until the production column/insert and then the stale API query were removed;
  • savepoint-cleanup regressions preserve the original operation error and fail closed when rollback cannot be confirmed; and
  • the stack repair initially exposed an unintended parent changelog regression. Current head restores the exact parent changelog and adds only the five intended event-ledger lines, proven by the exact parent→child comparison.

tests/unit/coverage-script-contract.test.mjs locks 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 3f63e97cc730cd3874f3d78baf6ecadf50c38623 are queued and therefore non-passing:

  • Server Tests 32272733355;
  • Dependency Review 32272733489; and
  • OSV Scanner 32272734205.

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.

@coderabbitai

coderabbitai Bot commented Aug 16, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

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.

@opencode-agent opencode-agent Bot added area: api API, protocol, event, or external contract priority: medium Normal-priority or P2 work status: draft Draft pull request type: feature New or expanded product capability labels Aug 22, 2026
@coderabbitai

coderabbitai Bot commented Aug 22, 2026

Copy link
Copy Markdown

Caution

Review failed

An 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.

❤️ Share

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

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

Labels

area: api API, protocol, event, or external contract priority: medium Normal-priority or P2 work status: draft Draft pull request type: feature New or expanded product capability

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant