feat(kilo-pass): disallow duplicate card fingerprints across Kilo Pass subscriptions#3309
Open
jrf0110 wants to merge 11 commits into
Open
feat(kilo-pass): disallow duplicate card fingerprints across Kilo Pass subscriptions#3309jrf0110 wants to merge 11 commits into
jrf0110 wants to merge 11 commits into
Conversation
Contributor
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Executive SummaryIncremental review of the single commit pushed since Resolved Issues (all closed)
Files Reviewed (9 files)
Reviewed by claude-4.6-sonnet-20260217 · 812,537 tokens Review guidance: REVIEW.md from base branch |
dd45044 to
cd3f998
Compare
…erprint-gate test
…sByCardFingerprint to card-fingerprint-gate
…transaction-safe audit logs
…mail_log markers persist
…for blockedGateResult
…otInArray - Return blockedEmailParams from db.transaction instead of mutating outer variable, fixing tsgo narrowing the type to 'never' because it doesn't track mutations across async closures. - Replace raw sql`NOT IN` with Drizzle's notInArray for type safety.
cd3f998 to
6954391
Compare
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.
Summary
Enforce that a single credit card fingerprint can be attached to at most one active Kilo Pass subscription across all Kilo users at any time. This blocks a fraud vector where multiple accounts purchase Kilo Pass using the same physical card.
What's new
findActiveKiloPassByCardFingerprinthelper inapps/web/src/lib/stripe.ts— queries payment methods for a matching fingerprint on a different user, then checks whether that user has an active Kilo Pass subscription.checkDuplicateCardFingerprintGateinapps/web/src/lib/kilo-pass/card-fingerprint-gate.ts— runs in theinvoice.paidwebhook handler after the Stripe charge succeeds. If a duplicate is found: cancels the subscription on Stripe, refunds the invoice, writes an audit log, and sends a customer-facing email notification.DuplicateCardSubscriptionCanceledaudit log action — added to theKiloPassAuditLogActionenum for tracking duplicate-card cancellations.kiloPassDuplicateCardCanceledtemplate andsendKiloPassDuplicateCardCanceledEmailsender function, usingtransactional_email_logfor idempotent dedup.Edge cases handled
excludingUserId).transactional_email_logunique index; audit logs are idempotent via the existing Stripe event ID tracking.Verification
Manual verification only. Automated tests exist but require a running Postgres instance.
Visual Changes
N/A
Reviewer Notes
stripe-handlers-invoice-paid.tsafter the subscription upsert but before credit issuance — if blocked, the subscription row is markedcanceledwithended_atset, and no credits are issued.findActiveKiloPassByCardFingerprintquery joins payment_methods to kilo_pass_subscriptions, filtering for active subscriptions (not in canceled/unpaid/incomplete_expired, and ended_at IS NULL).0135_duplicate_card_gate.sqladds theDuplicateCardSubscriptionCanceledenum value to the check constraint.