Skip to content

fix: prevent duplicate Stripe customers from broad catch in createStripeCustomerId#29764

Open
prathamesh04 wants to merge 1 commit into
calcom:mainfrom
prathamesh04:fix/stripe-duplicate-customers
Open

fix: prevent duplicate Stripe customers from broad catch in createStripeCustomerId#29764
prathamesh04 wants to merge 1 commit into
calcom:mainfrom
prathamesh04:fix/stripe-duplicate-customers

Conversation

@prathamesh04

Copy link
Copy Markdown

Overview

The createStripeCustomerId function used a broad try/catch that treated all errors the same as 'no customer found'. When Stripe API errors occurred (auth failures, network issues, rate limits), the catch block silently created a new customer instead of propagating the error — resulting in duplicate Stripe customers for the same email.

Fixes #29455

Changes

  • Replaced the broad try/catch with an explicit check on customersResponse.data.length
  • Only creates a new customer when the list is genuinely empty (no existing customer found)
  • Real Stripe API errors (auth, network, rate limit) now propagate naturally instead of being swallowed

Technical Details

Before:

try {
  const customersResponse = await stripe.customers.list({ email, limit: 1 });
  customerId = customersResponse.data[0].id;  // throws if empty list
} catch (error) {
  // catches BOTH 'empty list' AND real Stripe errors
  const customer = await stripe.customers.create({ email });
  customerId = customer.id;
}

After:

const customersResponse = await stripe.customers.list({ email, limit: 1 });
if (customersResponse.data.length > 0) {
  customerId = customersResponse.data[0].id;
} else {
  const customer = await stripe.customers.create({ email });
  customerId = customer.id;
}

The root cause was that data[0] on an empty array returns undefined, and accessing .id on undefined throws a TypeError. The catch block then treated this the same as any real Stripe error and created a new customer — causing duplicates.

Test plan

  • Verified that existing customer is returned when found in Stripe
  • Verified that new customer is created only when list is empty
  • Verified that Stripe API errors now propagate instead of creating duplicates
  • Recommended: add unit test for createStripeCustomerId covering empty list, existing customer, and Stripe error cases

…ipeCustomerId

The createStripeCustomerId function used a try/catch that treated all
errors the same as 'no customer found'. When Stripe API errors occurred
(auth failures, network issues, rate limits), the catch block silently
created a new customer instead of propagating the error, resulting in
duplicate Stripe customers.

Replace the broad catch with an explicit check on the response data
length — only create a new customer when the list is genuinely empty.

Fixes calcom#29455

Signed-off-by: Prathamesh Hukkeri <prathamesh04@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor

Welcome to Cal.diy, @prathamesh04! Thanks for opening this pull request.

A few things to keep in mind:

  • This is Cal.diy, not Cal.com. Cal.diy is a community-driven, fully open-source fork of Cal.com licensed under MIT. Your changes here will be part of Cal.diy — they will not be deployed to the Cal.com production app.
  • Please review our Contributing Guidelines if you haven't already.
  • Make sure your PR title follows the Conventional Commits format.

A maintainer will review your PR soon. Thanks for contributing!

@github-actions github-actions Bot added the 🐛 bug Something isn't working label Jul 13, 2026
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Updated createStripeCustomerId to list Stripe customers by email, reuse the first match, or create a new customer when none exists. The method continues to store the resulting ID in user metadata and return it. The previous catch-based fallback for list errors was removed.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the Stripe customer de-duplication fix and the catch-to-branch logic change.
Description check ✅ Passed The description matches the code change and explains the duplicate-customer bug and its fix.
Linked Issues check ✅ Passed The change satisfies [#29455] by only creating customers when the list is empty and letting real Stripe errors propagate.
Out of Scope Changes check ✅ Passed No unrelated changes are evident; the edit is confined to the Stripe customer lookup flow.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
apps/api/v2/src/modules/stripe/stripe.service.ts (1)

235-259: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Potential TOCTOU race when creating Stripe customers.

Two concurrent requests for the same email could both observe an empty customer list and proceed to create duplicate customers. While this PR correctly fixes the broad-catch path to duplicates, the race window remains. Consider using a database-level unique constraint on stripeCustomerId metadata or a distributed lock if duplicate prevention is a strict requirement.

This is a pre-existing pattern shared with the downstream consumer in packages/app-store/stripepayment/lib/customer.ts:33-67, so it may be out of scope here.

🤖 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.ts` around lines 235 - 259, The
createStripeCustomerId flow still has a check-then-create race that can produce
duplicate Stripe customers for the same email. If duplicate prevention is in
scope, add synchronization around this flow using a database-enforced uniqueness
mechanism for stripeCustomerId or an appropriate distributed lock, while
preserving the existing lookup, creation, metadata update, and return behavior;
otherwise leave this pre-existing pattern unchanged.
🤖 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.

Nitpick comments:
In `@apps/api/v2/src/modules/stripe/stripe.service.ts`:
- Around line 235-259: The createStripeCustomerId flow still has a
check-then-create race that can produce duplicate Stripe customers for the same
email. If duplicate prevention is in scope, add synchronization around this flow
using a database-enforced uniqueness mechanism for stripeCustomerId or an
appropriate distributed lock, while preserving the existing lookup, creation,
metadata update, and return behavior; otherwise leave this pre-existing pattern
unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f06c3c72-5b0d-4c59-8a8a-2133e0ab3ba1

📥 Commits

Reviewing files that changed from the base of the PR and between f004349 and 18ff5c7.

📒 Files selected for processing (1)
  • apps/api/v2/src/modules/stripe/stripe.service.ts

@prathamesh04

Copy link
Copy Markdown
Author

Hey @bandhan-majumder — this is a small fix for #29455 where the broad catch in createStripeCustomerId silently creates duplicate Stripe customers on real API errors (auth failures, rate limits, etc). The fix just replaces the try/catch with an explicit empty-list check. Would love a review when you get a chance!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🐛 bug Something isn't working size/S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MEDIUM: createStripeCustomerId catches too broadly, silently creating duplicate customers on real Stripe errors

1 participant