fix(stripe): avoid duplicate customer creation on lookup errors#29528
fix(stripe): avoid duplicate customer creation on lookup errors#29528Shreyas2004wagh wants to merge 3 commits into
Conversation
|
Welcome to Cal.diy, @Shreyas2004wagh! Thanks for opening this pull request. A few things to keep in mind:
A maintainer will review your PR soon. Thanks for contributing! |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughThis PR refactors the createStripeCustomerId method to replace its try/catch fallback pattern with a direct list-based lookup. Previously, if stripe.customers.list threw an error, the method would create a new customer. The updated implementation calls list directly, reuses the first customer from the response if present, and creates a new customer only when the list response is empty. Any list error now propagates instead of triggering creation. The PR adds tests verifying customer reuse, customer creation, and error propagation. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
apps/api/v2/src/modules/stripe/stripe.service.spec.ts (1)
64-101: ⚡ Quick winAssert
customers.listcall args to lock in the bugfix contract.Add expectations that lookup is called with
{ email: user.email, limit: 1 }in all scenarios, so the regression boundary is explicit in tests.Suggested diff
it("reuses an existing Stripe customer when lookup finds one", async () => { mockCustomersList.mockResolvedValue(createCustomerSearchResult([createCustomer("cus_existing")])); await expect(service.createStripeCustomerId(user)).resolves.toBe("cus_existing"); + expect(mockCustomersList).toHaveBeenCalledWith({ email: user.email, limit: 1 }); expect(mockCustomersCreate).not.toHaveBeenCalled(); expect(mockUsersRepository.updateByEmail).toHaveBeenCalledWith(user.email, { metadata: { existing: "value", @@ it("creates a Stripe customer when lookup returns no customers", async () => { mockCustomersList.mockResolvedValue(createCustomerSearchResult([])); mockCustomersCreate.mockResolvedValue(createCustomer("cus_new")); await expect(service.createStripeCustomerId(user)).resolves.toBe("cus_new"); + expect(mockCustomersList).toHaveBeenCalledWith({ email: user.email, limit: 1 }); expect(mockCustomersCreate).toHaveBeenCalledWith({ email: user.email }); expect(mockUsersRepository.updateByEmail).toHaveBeenCalledWith(user.email, { metadata: { existing: "value", @@ it("propagates Stripe lookup errors without creating duplicate customers", async () => { const stripeError = new Error("Stripe lookup failed"); mockCustomersList.mockRejectedValue(stripeError); await expect(service.createStripeCustomerId(user)).rejects.toThrow(stripeError); + expect(mockCustomersList).toHaveBeenCalledWith({ email: user.email, limit: 1 }); expect(mockCustomersCreate).not.toHaveBeenCalled(); expect(mockUsersRepository.updateByEmail).not.toHaveBeenCalled(); });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/api/v2/src/modules/stripe/stripe.service.spec.ts` around lines 64 - 101, Add an assertion in each test that the Stripe customers.list mock (mockCustomersList) is invoked with the exact lookup contract { email: user.email, limit: 1 }; update the three tests ("reuses an existing Stripe customer...", "creates a Stripe customer...", "propagates Stripe lookup errors...") to include expect(mockCustomersList).toHaveBeenCalledWith({ email: user.email, limit: 1 }) so the lookup call signature is locked in for createStripeCustomerId and prevents regressions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/api/v2/src/modules/stripe/stripe.service.ts`:
- Around line 14-20: The file imports type-only symbols AppConfig and StripeData
as regular imports; update their import statements to use TypeScript's type-only
form (import type) for AppConfig and StripeData in
apps/api/v2/src/modules/stripe/stripe.service.ts so those identifiers are
treated as types only and do not produce runtime imports—locate the existing
imports of AppConfig and StripeData and change them to use import type
accordingly.
---
Nitpick comments:
In `@apps/api/v2/src/modules/stripe/stripe.service.spec.ts`:
- Around line 64-101: Add an assertion in each test that the Stripe
customers.list mock (mockCustomersList) is invoked with the exact lookup
contract { email: user.email, limit: 1 }; update the three tests ("reuses an
existing Stripe customer...", "creates a Stripe customer...", "propagates Stripe
lookup errors...") to include expect(mockCustomersList).toHaveBeenCalledWith({
email: user.email, limit: 1 }) so the lookup call signature is locked in for
createStripeCustomerId and prevents regressions.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 9b35df4e-d941-40ed-a449-61198c088925
📒 Files selected for processing (2)
apps/api/v2/src/modules/stripe/stripe.service.spec.tsapps/api/v2/src/modules/stripe/stripe.service.ts
What does this PR do?
Fixes #29455
createStripeCustomerIdpreviously handled Stripe customer lookup with a broadtry/catch. That meant real lookup failures, such as auth errors, network failures, or rate limits, were treated the same as "no customer exists" and could silently create a duplicate Stripe customer.This PR changes the flow so:
customers.list()returns oneHow should this be tested?
Local result:
I also ran: