feat(kilo-pass): disallow duplicate card fingerprints across Kilo Pass subscriptions#3309
Conversation
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Executive SummaryThe incremental commit adds only migration Resolved Issues
Files Reviewed (14 files)
Reviewed by claude-sonnet-4.6 · 607,768 tokens Review guidance: REVIEW.md from base branch |
5de2b4e to
1321cfb
Compare
c239e62 to
a9590f5
Compare
|
Alright new requirement - we're also going to block the user that tried to buy a kilo pass with a duplicate credit card |
|
Also, now that #3526 is merged, we need to make sure this PR properly supersedes that work |
24d6ed3 to
3360519
Compare
|
Implemented in commit 3360519. When the card fingerprint gate detects a duplicate card, the offending user's account is now blocked ( |
|
Rebased onto
When this PR's gate triggers (duplicate card detected), the subscription is canceled before the issuance/promo code runs, so #3526's welcome-promo logic is never reached for that invoice. When the gate does not trigger, #3526's code correctly applies the welcome-promo guard as before. No additional integration work was needed. |
6d7d4a5 to
76a3274
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.
…te blocks subscription
…erge conflict markers The 0141_snapshot.json had unresolved merge conflict markers from a previous rebase. Deleted branch-local migration files and regenerated a clean migration (0142) using pnpm drizzle generate.
Rename branch migration from 0142 to 0143 since main added 0142_dashing_blue_marvel. Restore main's 0142 snapshot and journal, regenerate clean migration.
When user.google_user_email is falsy in maybeSendDuplicateCardCanceledEmail, the transactional_email_log marker was left in the DB permanently. This suppressed future email sends even if the user later acquired an email. Now the marker is deleted before returning, consistent with the error-path cleanup logic.
…gers When the card fingerprint gate detects a duplicate card across users, the offending user's account is now blocked (blocked_reason set to 'kilo_pass_duplicate_card') in addition to the existing cancel + refund. Does not overwrite an existing blocked_reason. Follows the same pattern as cancel-and-refund.ts.
76a3274 to
b861118
Compare
… to fix FK violation The checkDuplicateCardFingerprintGate function was passing dbOrTx through a parameter to appendKiloPassAuditLog, but the FK constraint on kilo_pass_audit_log.kilo_pass_subscription_id was being violated inside the transaction. Move the audit log insert and user-blocking update into the handler where tx is used directly. Also fix the catch block to omit kiloPassSubscriptionId when the transaction may have rolled back, and wrap the failure audit log in a try-catch so it doesn't mask the original error.
…d check constraint
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
Manually ran through the flow and made sure that duplicate card users got their sub canceled and account blocked:
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.