Skip to content

Only confirm Stripe intents minted for this user's membership - #424

Merged
aamoghS merged 1 commit into
devfrom
fix/confirm-payment-gate
Sep 25, 2026
Merged

aamoghS merged 1 commit into
devfrom
fix/confirm-payment-gate

Conversation

@github-actions

@github-actions github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Automated PR tracking changes from fix/confirm-payment-gate into dev.


Note

High Risk
Changes payment confirmation authorization and membership grant idempotency—security-sensitive paths where bugs could grant free or duplicate memberships.

Overview
Tightens confirmMembershipAfterPayment so only portal-minted membership or bootcamp add-on intents can grant access, aligning confirm with webhook/reconcile rules.

Live confirmation now requires matching metadata.userId, type of membership or the bootcamp add-on type, and an amount at or below MAX_MEMBERSHIP_CHARGE_CENTS. Intents without userId (Checkout links, Dashboard charges, etc.) or wrong type/amount return FORBIDDEN instead of silently granting—especially when plan was missing and defaulted to a full year. Mock confirm paths always tag non–add-on intents with type: "membership".

Race with the webhook: payment rows insert with onConflictDoNothing(); if the webhook already recorded the intent, confirm returns success without a second membership grant or a unique-constraint 500. membershipGrants increments only when this path actually grants.

Tests add configurable Stripe retrieve/conflict mocks and a “confirming a live payment” suite for allow/deny cases and webhook-first idempotency.

Reviewed by Cursor Bugbot for commit e2ccdc2. Bugbot is set up for automated code reviews on this repo. Configure here.

confirmMembershipAfterPayment refused an intent only when metadata.userId
was present and named someone else. A succeeded intent with no userId —
hosted Checkout, a payment link, a Dashboard charge — passed with no type
or amount check, and readPlan(undefined) granted a full year. Anyone
holding such a charge id could claim it, including a $1 link or a $15
semester checkout confirmed before the webhook arrived.

It now applies the same gate as the webhook and reconcileMyPayments:
metadata.userId must equal the caller, metadata.type must be a
membership or the bootcamp add-on, and the amount must be within
MAX_MEMBERSHIP_CHARGE_CENTS. Every intent createPaymentIntent mints has
carried type since June; mock intents now carry it too.

The insert also uses onConflictDoNothing: when the webhook records the
same intent first, confirm skips the grant instead of surfacing the
unique violation as a 500 on a successful payment.
@github-actions
github-actions Bot requested a review from aamoghS as a code owner September 25, 2026 02:50
@aamoghS aamoghS changed the title Feature: fix/confirm-payment-gate to dev Only confirm Stripe intents minted for this user's membership Sep 25, 2026
@aamoghS
aamoghS merged commit 401ed50 into dev Sep 25, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant