feat(billing): consume Stripe reconciliation triggers with leases - #574
feat(billing): consume Stripe reconciliation triggers with leases#574seonghobae wants to merge 18 commits into
Conversation
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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 |
|
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 |
Buyer/reliability objective
Refs #488. This stacked Draft consumes the durable verified-event reconciliation triggers produced by #570/#573 without treating webhook arrival order as lifecycle or entitlement authority. It adds finite worker leases, bounded retry/backoff, durable attempt evidence, explicit dead-letter state, and a production bootstrap that invokes the existing authoritative Subscription/Invoice reconciliation service only after resolving tenant authority from server-owned normalized Stripe identity.
Exact current stack
develop:2c328875e00e86537df3e965170be80532571cad;feat/stripe-checkout-identity-bootstrap-488@7e480f0c10ac5598e641fff03d6f7d7a8afca8f2;3e6b0eb17a063bdd2f21f856f362c5ac63a9b24f;Any parent, contributor-head, protected-base, review, or check movement invalidates ancestry- and head-sensitive evidence until freshly revalidated.
Worker and data-integrity contract
server/stripe_reconciliation_worker.mjsadds two normalized relations while preserving the immutable trigger table:billing_stripe_reconciliation_jobskeeps one current scheduling head per verified Event trigger; andbilling_stripe_reconciliation_attemptskeeps append-only attempt/lease outcome evidence.The worker lazily seeds jobs from already-durable triggers, claims one ready trigger under an opaque finite lease, stores only the SHA-256 lease-token digest, prevents concurrent double processing, reclaims expired leases, rejects stale completion, retries with capped exponential backoff, and moves permanently failing work to explicit
dead_letterafter a finite attempt budget. Arbitrary provider exception text is never persisted; only bounded machine-readablestripe_*codes are retained, otherwise failure collapses tostripe_reconciliation_failed.Tenant authority is not caller-selectable. The repository resolves organization identity through the existing normalized
billing_stripe_subscriptions→billing_stripe_customers.organization_idchain. Missing identity remains explicit retryable work and does not call the provider reconciliation port.runNextStripeReconciliationJobthen invokes the existing authoritative reconciliation service with exact organization, Subscription, and verified Event provenance. Its receipt must match that tenant and Subscription before the lease can complete. This slice never writesorgs.plan, membership/RBAC, browser sessions, or capabilities.Bootstrap and transaction boundary
server/db.mjsinstalls worker state after the existing trigger/evidence/claim prerequisites and exportsreconcileNextStripeBillingTrigger(), configured with the production Subscription observation, Invoice observation, and entitlement-claim repositories.Worker state changes use named SQLite savepoints. Success releases only after the complete mutation; failure uses
ROLLBACK TO SAVEPOINTand releases only after rollback is confirmed, preserving the causal error. Final-attempt lease expiry is terminal dead-letter evidence rather than an unclaimable pending row.TDD and causal repair chronology
5e8e673c5f103868e9edfaacc0853a802562bb71introduced a realistic worker lifecycle contract while the production module was absent, establishing a deterministic module-resolution RED;org_idalias instead of the realbilling_stripe_customers.organization_idproduction schema;b6fa011ae3c0575056c471cd07e16d8928af52c5replaced the custom identity tables with the actual production Subscription observation schema, making schema drift executable evidence;c022d632949d14a1664e00780bff7792ec4d1962binds tenant lookup toorganization_idand also prevents final-attempt lease expiry from leaving permanently pending but unclaimable work.The focused behavior covers successful worker completion, lease exclusion/reclaim and stale-worker rejection, bounded retry/dead-letter, secret-text non-persistence, and missing-identity retry without provider invocation.
package.jsonandtests/unit/coverage-script-contract.test.mjslock the worker into normal unit CI and canonical c8 instrumentation/cases.Standards and traceability
docs/doctoring/stripe-reconciliation-worker.mdrecords active-PR versus protected-shipped truth, normalized state ownership, lease secrecy, retry/dead-letter policy, savepoint semantics, rollback, privacy boundaries, and APA 7 references to current Stripe webhook delivery guidance plus SQLite transaction/savepoint documentation.Stripe documents automatic retries and explicitly does not guarantee event delivery ordering. This implementation therefore treats events only as durable reconciliation triggers and re-fetches current provider state through the pre-existing #569 service before claim evaluation.
Current evidence boundary
Opening this PR creates fresh repository/organization checks for this contributor head. Until every applicable exact-head deterministic/browser/statement/branch/function/line coverage/docstring/CodeQL/SAST/security/dependency/supply-chain/package/provenance gate is terminal and substantive, valid findings are zero, and qualifying independent current-head approval exists under live governance, pending/queued/absent/stale/predecessor/status-only/model-only evidence is non-passing.
Remain Draft and do not integrate independently of #573/#570/#569/#568 and the preceding #488 billing stack. This slice does not add a perpetual scheduler or operator dead-letter recovery API/UI; those and final out-of-order end-to-end convergence/release acceptance remain subsequent bounded work.