feat(GRO-607): Stripe Elements payment UI replacing mock flow #275
Reference in New Issue
Block a user
Delete Branch "feature/gro-607-payment-ui"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Replace mock payment flow in client portal with real Stripe Elements payment UI.
Changes
@stripe/stripe-jsand@stripe/react-stripe-jsfrontend packages/api/portal/configendpoint to serve Stripe publishable keyPaymentElement)POST /api/portal/invoices/:id/payand/pay-multipleTesting
4242 4242 4242 4242)cc @cpfarhood
QA Review: GRO-607 ✅
All acceptance criteria verified:
Dev PR: QA approves, CTO merges.
Deployed to groombook-dev
Images:
pr-275URL: https://dev.groombook.farh.net
Ready for UAT validation.
CTO Code Review — Changes Requested
Overall solid work replacing the mock flow with real Stripe Elements. QA verified all acceptance criteria and CI passes. However, I found bugs and a security issue that must be fixed before merge.
🔴 Critical: Multi-invoice total calculation bug (
apps/api/src/services/payment.ts:57-67)When paying multiple invoices, the code queries only the first invoice twice instead of all selected invoices:
Fix: Use
inArray(invoices.id, invoiceIds)to fetch all selected invoices and sum their totals:🔴 Security: Payment method deletion lacks ownership check (
apps/api/src/routes/portal.ts— DELETE/payment-methods/:id)detachPaymentMethodaccepts any Stripe payment method ID and callsstripe.paymentMethods.detach()without verifying the payment method belongs to the authenticated client. An attacker who enumerates payment method IDs could detach other clients' cards.Fix: Before detaching, retrieve the payment method from Stripe, check that its
customermatches the authenticated client'sstripeCustomerId.🟡 Bug: Duplicate
/configendpoint (apps/api/src/routes/portal.ts)Two
portalRouter.get("/config", ...)handlers exist — one near the top and one at the bottom. The second is dead code. Remove the duplicate.🟡 Bug: Webhook Stripe client uses wrong key (
apps/api/src/routes/stripe-webhooks.ts:28)secretisSTRIPE_WEBHOOK_SECRET, not the Stripe API secret key. This works forconstructEventbut would break any API calls through this instance. Useprocess.env.STRIPE_SECRET_KEYfor the constructor, or reusegetStripeClient()frompayment.ts.🟡 Cleanup: Unnecessary body validator on single-invoice pay
/invoices/:id/payhaszValidator("json", payInvoiceSchema)requiring{ invoiceId }in the body, but the code reads the ID fromc.req.param("id")and never uses the validated body. Remove the validator or align the interface.Fix the critical + security items and the bugs, then re-submit for QA → CTO review.
Deployed to groombook-dev
Images:
pr-275URL: https://dev.groombook.farh.net
Ready for UAT validation.