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 19, 2025

Summary by CodeRabbit

  • Refactor
    • Reorganized checkout and promo-code flows so lead tracking and webhooks are handled in a single, dedicated side-effects step for more consistent behavior.
    • Lead counts and "last lead" timestamps are now updated uniformly; lead-related webhooks are dispatched from the dedicated side-effects block rather than the main sale path.

@vercel
Copy link
Contributor

vercel bot commented Nov 19, 2025

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

Project Deployment Preview Updated (UTC)
dub Ready Ready Preview Nov 19, 2025 6:09am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 19, 2025

Walkthrough

Moved lead-related webhook dispatch and lead state updates out of the main checkout sale path into a dedicated lead side-effects block invoked during promo-code attribution; standardized workspace usage increment to 1 and added an internal helper to increment link leads.

Changes

Cohort / File(s) Change Summary
Checkout Session Webhook & Promo Attribution
apps/web/app/(ee)/api/stripe/integration/webhook/checkout-session-completed.ts
Removed shouldSendLeadWebhook and conditional lead.created dispatch from main sale flow; standardized workspace usage increment to 1; added webhookEnabled to workspace query; added incrementLinkLeads(linkId) helper; introduced lead side-effects block in promo-code attribution that updates link leads/lastLeadAt, triggers partner commissions and leadRecorded workflow, syncs partner link stats, and sends lead.created webhook after side-effects.

Sequence Diagram

sequenceDiagram
    participant Stripe
    participant Webhook as Checkout Handler
    participant PromoAttr as Promo Attribution
    participant DB as Database
    participant Webhooks as Webhook Dispatch

    Stripe->>Webhook: Checkout Session Completed
    Webhook->>DB: Record sale
    Webhook->>DB: Increment workspace usage (1)

    alt Promo code present
        Webhook->>PromoAttr: attributeViaPromoCode()
        PromoAttr->>DB: Create/update lead record

        Note right of PromoAttr: Lead side-effects block
        PromoAttr->>DB: incrementLinkLeads(linkId) // increments leads, updates lastLeadAt
        PromoAttr->>DB: If partner -> trigger commissions & leadRecorded workflow
        PromoAttr->>DB: Sync partner link stats
        PromoAttr->>Webhooks: Send transformed lead.created webhook
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Inspect partner commission and leadRecorded workflow calls in the new side-effects block.
  • Verify correctness of incrementLinkLeads (atomicity, timestamps).
  • Confirm webhook payload transformation and webhookEnabled handling.

Possibly related PRs

Suggested reviewers

  • TWilson023
  • devkiran

Poem

🐇 I nudged the lead hook from checkout's race,
I hop to promo's tidy, calmer place.
I tally links and whisper to partners near,
Then send the webhook once the side-effects clear —
A cheerful hop, a cleaner trace.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% 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 title directly describes the main change: recording lead commissions and workflows in the attributeViaPromoCode path, which matches the primary refactoring in the changeset.
✨ 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 stripe-promocode-lead

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d0e2da6 and c67c4e1.

📒 Files selected for processing (1)
  • apps/web/app/(ee)/api/stripe/integration/webhook/checkout-session-completed.ts (4 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: steven-tey
Repo: dubinc/dub PR: 2924
File: apps/web/lib/api/conversions/track-lead.ts:7-7
Timestamp: 2025-10-02T22:46:22.739Z
Learning: In apps/web/lib/api/conversions/track-lead.ts, lead events are cached in Redis for 5 minutes (keys: `leadCache:${customer.id}` and `leadCache:${customer.id}:${stringifiedEventName}`) to provide immediate data availability while Tinybird ingestion happens asynchronously. This caching pattern allows for async-only recording without breaking "wait" mode semantics.
📚 Learning: 2025-10-02T22:46:22.739Z
Learnt from: steven-tey
Repo: dubinc/dub PR: 2924
File: apps/web/lib/api/conversions/track-lead.ts:7-7
Timestamp: 2025-10-02T22:46:22.739Z
Learning: In apps/web/lib/api/conversions/track-lead.ts, lead events are cached in Redis for 5 minutes (keys: `leadCache:${customer.id}` and `leadCache:${customer.id}:${stringifiedEventName}`) to provide immediate data availability while Tinybird ingestion happens asynchronously. This caching pattern allows for async-only recording without breaking "wait" mode semantics.

Applied to files:

  • apps/web/app/(ee)/api/stripe/integration/webhook/checkout-session-completed.ts
📚 Learning: 2025-08-25T17:33:45.072Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2736
File: apps/web/app/(ee)/api/stripe/integration/webhook/invoice-paid.ts:12-12
Timestamp: 2025-08-25T17:33:45.072Z
Learning: The WorkflowTrigger enum in packages/prisma/schema/workflow.prisma contains three values: leadRecorded, saleRecorded, and commissionEarned. All three are properly used throughout the codebase.

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 (6)
packages/prisma/index.ts (1)
  • prisma (3-9)
apps/web/lib/types.ts (1)
  • WebhookPartner (410-410)
apps/web/lib/partners/create-partner-commission.ts (1)
  • createPartnerCommission (59-379)
apps/web/lib/api/workflows/execute-workflows.ts (1)
  • executeWorkflows (17-99)
apps/web/lib/webhook/publish.ts (1)
  • sendWorkspaceWebhook (8-45)
apps/web/lib/webhook/transform.ts (1)
  • transformLeadEventData (60-79)
⏰ 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)

378-388: LGTM: Workspace usage increment simplified.

The fixed increment of 1 per sale is a clear improvement over potentially conditional logic, ensuring consistent usage tracking across all checkout flows.


515-515: LGTM: Required field for workspace webhook.

The webhookEnabled field is correctly added to support the sendWorkspaceWebhook call at line 643.


601-656: LGTM: Well-structured lead side-effects block.

The implementation correctly:

  • Increments link leads before side-effects (needed for webhook payload)
  • Uses Promise.allSettled to ensure all partner operations execute independently
  • Wraps everything in waitUntil to avoid blocking the main response
  • Conditionally includes partner data in the webhook payload when available
  • Sends workspace webhook regardless of partner link status

The error handling is sound: if createPartnerCommission fails, webhookPartner remains undefined and the webhook is sent without partner data.

Based on learnings


666-679: LGTM: Clean helper abstraction.

The incrementLinkLeads helper provides a consistent way to update lead stats across both attribution flows. The inclusion of includeTags ensures complete link data for webhook payloads (addressing the previous review comment).

Note: Error handling relies on the caller's context (waitUntil), which is acceptable for non-blocking side-effects.


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: 2

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

600-665: Consider error handling: Link update failure blocks downstream operations.

The lead side-effects block uses a single async IIFE within waitUntil. If the link update (lines 603-613) fails, the partner commissions, workflows, and webhooks won't be triggered. Since recordLead already succeeded (line 598), this could lead to inconsistent state.

Consider wrapping the link update in a try-catch or using Promise.allSettled for all operations to ensure partner-related side-effects run even if link updates fail.

Example refactor:

waitUntil(
  (async () => {
    const [linkUpdateResult, ...partnerResults] = await Promise.allSettled([
      prisma.link.update({
        where: { id: link.id },
        data: {
          leads: { increment: 1 },
          lastLeadAt: new Date(),
        },
        include: includeTags,
      }),
      // Partner operations
      link.programId && link.partnerId
        ? Promise.allSettled([
            createPartnerCommission({ /* ... */ }),
            executeWorkflows({ /* ... */ }),
            syncPartnerLinksStats({ /* ... */ }),
          ])
        : Promise.resolve([]),
    ]);

    const linkUpdated = linkUpdateResult.status === 'fulfilled' 
      ? linkUpdateResult.value 
      : link; // fallback to original link if update failed

    // Send webhook with best available data
    await sendWorkspaceWebhook({ /* ... */ });
  })(),
);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5dedf6d and d0e2da6.

📒 Files selected for processing (1)
  • apps/web/app/(ee)/api/stripe/integration/webhook/checkout-session-completed.ts (3 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-10-02T22:46:22.739Z
Learnt from: steven-tey
Repo: dubinc/dub PR: 2924
File: apps/web/lib/api/conversions/track-lead.ts:7-7
Timestamp: 2025-10-02T22:46:22.739Z
Learning: In apps/web/lib/api/conversions/track-lead.ts, lead events are cached in Redis for 5 minutes (keys: `leadCache:${customer.id}` and `leadCache:${customer.id}:${stringifiedEventName}`) to provide immediate data availability while Tinybird ingestion happens asynchronously. This caching pattern allows for async-only recording without breaking "wait" mode semantics.

Applied to files:

  • apps/web/app/(ee)/api/stripe/integration/webhook/checkout-session-completed.ts
📚 Learning: 2025-08-25T17:33:45.072Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2736
File: apps/web/app/(ee)/api/stripe/integration/webhook/invoice-paid.ts:12-12
Timestamp: 2025-08-25T17:33:45.072Z
Learning: The WorkflowTrigger enum in packages/prisma/schema/workflow.prisma contains three values: leadRecorded, saleRecorded, and commissionEarned. All three are properly used throughout the codebase.

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 (5)
apps/web/lib/types.ts (1)
  • WebhookPartner (410-410)
apps/web/lib/partners/create-partner-commission.ts (1)
  • createPartnerCommission (59-379)
apps/web/lib/api/workflows/execute-workflows.ts (1)
  • executeWorkflows (17-99)
apps/web/lib/webhook/publish.ts (1)
  • sendWorkspaceWebhook (8-45)
apps/web/lib/webhook/transform.ts (1)
  • transformLeadEventData (60-79)
⏰ 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)

377-387: LGTM: Workspace usage increment standardized.

The fixed increment of 1 per sale is clearer than the previous conditional logic. Lead-related usage tracking is now handled separately in the attribution flow.


389-402: LGTM: Customer stats update simplified.

The customer stats update now focuses solely on sale metrics, which is consistent with the refactoring to move lead-related side-effects to the dedicated attribution block.


514-514: LGTM: Required field for workspace webhook.

The webhookEnabled field is needed for the sendWorkspaceWebhook call in the lead side-effects block (line 652).

@steven-tey steven-tey merged commit c1fdc13 into main Nov 19, 2025
7 of 8 checks passed
@steven-tey steven-tey deleted the stripe-promocode-lead branch November 19, 2025 06:23
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