Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@steven-tey
Copy link
Collaborator

@steven-tey steven-tey commented Nov 27, 2025

Summary by CodeRabbit

  • Refactor

    • Streamlined Stripe webhook processing: workspace is discovered early and passed through attribution flows.
    • Removed redundant database fetches and simplified sale attribution logic for reliability and efficiency.
    • Promotion-code attribution now requires and uses workspace context.
  • Bug Fixes

    • Broadened customer lookup to match additional identifier types.
    • Added early returns/logging when workspace is missing to avoid incorrect processing.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link
Contributor

vercel bot commented Nov 27, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Updated (UTC)
dub Ready Ready Preview Nov 27, 2025 10:43pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 27, 2025

Walkthrough

Moved workspace lookup to the webhook entry, broadened customer lookup criteria, threaded the discovered workspace through promotion-code attribution and sale attribution, removed redundant workspace fetches, and exposed Project type in function signatures.

Changes

Cohort / File(s) Summary
Webhook handler & workspace propagation
apps/web/app/(ee)/api/stripe/integration/webhook/checkout-session-completed.ts
Add top-level workspace lookup by stripeConnectId (selecting id/defaultProgramId/stripeConnectId/webhookEnabled); early-return if missing; remove inner workspace fetches; broaden customer search to consider externalId, email, and stripeCustomerId; thread workspace into attribution calls; import Project type.
Promotion-code attribution signature
apps/web/app/(ee)/api/stripe/integration/webhook/checkout-session-completed.ts (same file, attributeViaPromoCode)
Change attributeViaPromoCode signature to accept `workspace: Pick<Project, "id"

Sequence Diagram

sequenceDiagram
    participant Webhook as Stripe Checkout Webhook
    participant DB as Database (Prisma)
    participant Promo as attributeViaPromoCode
    participant Sale as Sale Attribution

    Webhook->>DB: find workspace by stripeConnectId (select id, defaultProgramId, stripeConnectId, webhookEnabled)
    alt workspace found
        DB-->>Webhook: workspace
        Webhook->>DB: find customer by clientReferenceId / externalId / email / stripeCustomerId
        DB-->>Webhook: customer?
        Webhook->>Promo: attributeViaPromoCode(workspace, promotionCodeId, stripeAccountId, mode, charge)
        Promo-->>Webhook: promo attribution result
        Webhook->>Sale: attribute sale/lead using workspace + attribution result
        Sale-->>Webhook: attribution completed
    else workspace missing
        DB-->>Webhook: no workspace -> early return / log
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20–30 minutes

  • Verify every attributeViaPromoCode call site updated to pass the new workspace param.
  • Check type correctness for the Pick<Project,...> usage and any exposed public signatures.
  • Validate broadened customer lookup logic for unintended matches or edge cases.
  • Inspect Promise.all result handling to ensure no lost side effects.

Possibly related PRs

Suggested reviewers

  • devkiran

Poem

🐇 I threaded a workspace, neat and spry,

no double fetch, just one clear sky.
Promo hops to the passed-in den,
customers found, then sales begin.
A tiny rabbit clap—code runs sly.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title states the change improves the webhook to match customer by email, which aligns with the broadened customer lookup logic now considering externalId via stripeCustomerEmail.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch checkout-webhook-customer

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 and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/app/(ee)/api/stripe/integration/webhook/checkout-session-completed.ts (1)

94-107: Comment-code mismatch: Missing email and stripeCustomerId field checks.

The comment states it should check "or email or stripeCustomerId", but the OR conditions only check the externalId field. Compare with lines 208-221 which properly search by stripeCustomerId and email fields.

Should this be:

 existingCustomer = await prisma.customer.findFirst({
   where: {
     projectId: workspace.id,
-    // check for existing customer with the same externalId (via clickId or email) or email or stripeCustomerId
+    // check for existing customer with the same externalId, email, or stripeCustomerId
     OR: [
       {
         externalId: clickEvent.click_id,
       },
       {
         externalId: stripeCustomerEmail,
       },
+      ...(stripeCustomerEmail ? [{ email: stripeCustomerEmail }] : []),
+      ...(stripeCustomerId ? [{ stripeCustomerId }] : []),
     ],
   },
 });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between beac3b7 and 245393e.

📒 Files selected for processing (1)
  • apps/web/app/(ee)/api/stripe/integration/webhook/checkout-session-completed.ts (8 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-10-28T19:17:28.085Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2985
File: apps/web/app/(ee)/api/network/programs/[programSlug]/route.ts:20-27
Timestamp: 2025-10-28T19:17:28.085Z
Learning: In this codebase, Prisma's extendedWhereUnique feature is enabled (or Prisma 5.x is used), allowing findUniqueOrThrow to accept additional non-unique filters alongside unique fields like slug. This pattern is valid and acceptable.

Applied to files:

  • apps/web/app/(ee)/api/stripe/integration/webhook/checkout-session-completed.ts
📚 Learning: 2025-08-25T17:42:13.600Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2736
File: apps/web/lib/api/get-workspace-users.ts:76-83
Timestamp: 2025-08-25T17:42:13.600Z
Learning: Business rule confirmed: Each workspace has exactly one program. The code should always return workspace.programs[0] since there's only one program per workspace.

Applied to files:

  • apps/web/app/(ee)/api/stripe/integration/webhook/checkout-session-completed.ts
📚 Learning: 2025-06-18T20:26:25.177Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2538
File: apps/web/ui/partners/overview/blocks/commissions-block.tsx:16-27
Timestamp: 2025-06-18T20:26:25.177Z
Learning: In the Dub codebase, components that use workspace data (workspaceId, defaultProgramId) are wrapped in `WorkspaceAuth` which ensures these values are always available, making non-null assertions safe. This is acknowledged as a common pattern in their codebase, though not ideal.

Applied to files:

  • apps/web/app/(ee)/api/stripe/integration/webhook/checkout-session-completed.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (4)
apps/web/app/(ee)/api/stripe/integration/webhook/checkout-session-completed.ts (4)

30-30: LGTM!

The Project type import is needed for the updated attributeViaPromoCode function signature.


63-77: Good refactor: Workspace lookup at entry point.

Moving the workspace fetch to the function entry enables early validation and eliminates redundant database queries downstream.


366-366: LGTM!

The _sale underscore prefix correctly indicates the unused return value, and workspace no longer needs to be extracted here since it's fetched at the entry point.


508-523: Good refactor: Workspace threading eliminates redundant DB fetch.

Passing the workspace as a parameter with a properly narrowed type (Pick<Project, ...>) is cleaner than fetching it again internally.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/web/app/(ee)/api/stripe/integration/webhook/checkout-session-completed.ts (1)

212-229: Consider adding projectId to the stripeCustomerId branch for explicit data isolation.

The conditional spread for stripeCustomerEmail addresses the undefined-value issue flagged previously. However, the stripeCustomerId branch still lacks a projectId constraint. While Stripe customer IDs are unique per connected account (and the webhook context ensures the correct workspace), scoping by projectId provides an explicit safeguard against accidental cross-workspace matching.

       existingCustomer = await prisma.customer.findFirst({
         where: {
           OR: [
             {
+              projectId: workspace.id,
               stripeCustomerId,
             },
             ...(stripeCustomerEmail
               ? [
                   {
                     projectId: workspace.id,
                     email: stripeCustomerEmail,
                   },
                 ]
               : []),
           ],
         },
       });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 245393e and 2d0ead0.

📒 Files selected for processing (1)
  • apps/web/app/(ee)/api/stripe/integration/webhook/checkout-session-completed.ts (8 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-10-28T19:17:28.085Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2985
File: apps/web/app/(ee)/api/network/programs/[programSlug]/route.ts:20-27
Timestamp: 2025-10-28T19:17:28.085Z
Learning: In this codebase, Prisma's extendedWhereUnique feature is enabled (or Prisma 5.x is used), allowing findUniqueOrThrow to accept additional non-unique filters alongside unique fields like slug. This pattern is valid and acceptable.

Applied to files:

  • apps/web/app/(ee)/api/stripe/integration/webhook/checkout-session-completed.ts
📚 Learning: 2025-07-30T15:29:54.131Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2673
File: apps/web/ui/partners/rewards/rewards-logic.tsx:268-275
Timestamp: 2025-07-30T15:29:54.131Z
Learning: In apps/web/ui/partners/rewards/rewards-logic.tsx, when setting the entity field in a reward condition, dependent fields (attribute, operator, value) should be reset rather than preserved because different entities (customer vs sale) have different available attributes. Maintaining existing fields when the entity changes would create invalid state combinations and confusing UX.

Applied to files:

  • apps/web/app/(ee)/api/stripe/integration/webhook/checkout-session-completed.ts
📚 Learning: 2025-08-25T17:42:13.600Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2736
File: apps/web/lib/api/get-workspace-users.ts:76-83
Timestamp: 2025-08-25T17:42:13.600Z
Learning: Business rule confirmed: Each workspace has exactly one program. The code should always return workspace.programs[0] since there's only one program per workspace.

Applied to files:

  • apps/web/app/(ee)/api/stripe/integration/webhook/checkout-session-completed.ts
📚 Learning: 2025-06-18T20:26:25.177Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2538
File: apps/web/ui/partners/overview/blocks/commissions-block.tsx:16-27
Timestamp: 2025-06-18T20:26:25.177Z
Learning: In the Dub codebase, components that use workspace data (workspaceId, defaultProgramId) are wrapped in `WorkspaceAuth` which ensures these values are always available, making non-null assertions safe. This is acknowledged as a common pattern in their codebase, though not ideal.

Applied to files:

  • apps/web/app/(ee)/api/stripe/integration/webhook/checkout-session-completed.ts
🧬 Code graph analysis (1)
apps/web/app/(ee)/api/stripe/integration/webhook/checkout-session-completed.ts (1)
packages/prisma/index.ts (1)
  • prisma (3-9)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (3)
apps/web/app/(ee)/api/stripe/integration/webhook/checkout-session-completed.ts (3)

63-77: Good refactor: centralized workspace lookup.

Moving the workspace discovery to the webhook entry point with a minimal field selection is clean. This eliminates redundant fetches and provides clear early-exit behavior.


94-111: LGTM!

The customer lookup is correctly scoped with projectId at the top level, and the conditional spread for stripeCustomerEmail properly avoids the undefined-value pitfall in Prisma's OR clauses.


516-528: LGTM!

Using Pick<Project, ...> for the workspace parameter is a clean approach—it documents exactly which fields the function depends on while eliminating the redundant database lookup that was previously inside this function.

@steven-tey steven-tey merged commit 38aca29 into main Nov 27, 2025
7 of 8 checks passed
@steven-tey steven-tey deleted the checkout-webhook-customer branch November 27, 2025 23:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants