Skip to content

Commit 97c8a7c

Browse files
committed
fix(payments): add organization membership check to top-up checkout
Verify the authenticated user belongs to the organization before creating or reusing its Stripe customer on the top-up path.
1 parent e715789 commit 97c8a7c

1 file changed

Lines changed: 11 additions & 4 deletions

File tree

src/app/payments/topup/route.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { MAXIMUM_TOP_UP_AMOUNT, MINIMUM_TOP_UP_AMOUNT } from '@/lib/constants';
66
import { isValidReturnUrl } from '@/lib/payment-return-url';
77
import { captureException } from '@sentry/nextjs';
88
import { getOrCreateStripeCustomerIdForOrganization } from '@/lib/organizations/organization-billing';
9+
import { getAuthorizedOrgContext } from '@/lib/organizations/organization-auth';
910

1011
/**
1112
* NOTE: Crypto payment support (Coinbase Commerce) was removed in January 2026.
@@ -63,10 +64,16 @@ export async function POST(request: NextRequest): Promise<NextResponse<unknown>>
6364
return NextResponse.json({ error: 'Invalid org id' }, { status: 400 });
6465
}
6566

66-
const stripeCustomerId = organizationId
67-
? // TODO(bmc): should we check user permission to organization here?
68-
await getOrCreateStripeCustomerIdForOrganization(organizationId)
69-
: currentUser.stripe_customer_id;
67+
let stripeCustomerId: string | null | undefined;
68+
if (organizationId) {
69+
const orgContext = await getAuthorizedOrgContext(organizationId);
70+
if (!orgContext.success) {
71+
return orgContext.nextResponse;
72+
}
73+
stripeCustomerId = await getOrCreateStripeCustomerIdForOrganization(organizationId);
74+
} else {
75+
stripeCustomerId = currentUser.stripe_customer_id;
76+
}
7077

7178
const cancelPathRaw = searchParams.get('cancel-path');
7279
const cancelPath = cancelPathRaw && isValidReturnUrl(cancelPathRaw) ? cancelPathRaw : null;

0 commit comments

Comments
 (0)