Skip to content

Fix checkout.session.completed webhook#3885

Merged
steven-tey merged 1 commit into
mainfrom
fix-checkout-completed
May 12, 2026
Merged

Fix checkout.session.completed webhook#3885
steven-tey merged 1 commit into
mainfrom
fix-checkout-completed

Conversation

@steven-tey
Copy link
Copy Markdown
Collaborator

@steven-tey steven-tey commented May 11, 2026

Summary by CodeRabbit

  • Bug Fixes
    • Ensures the 14-day trial welcome email is consistently sent during post-checkout onboarding.
    • Domain claiming now verifies an active subscription before finalizing the claim.

Review Change Stack

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented May 11, 2026

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

Project Deployment Actions Updated (UTC)
dub Ready Ready Preview May 11, 2026 11:59pm

Request Review

@steven-tey
Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 11, 2026

✅ Actions performed

Full review triggered.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 11, 2026

📝 Walkthrough

Walkthrough

Webhook onboarding and email flow moved to use Stripe subscription.status; program creation and saved-domain claiming logic updated accordingly. Domain-claim utility parameter types narrowed to Pick<Project,...>.

Changes

Subscription-Status Domain Claiming

Layer / File(s) Summary
Domain Claim Type Signatures
apps/web/lib/api/domains/claim-dot-link-domain.ts
claimDotLinkDomain and sendDomainClaimedEmails now accept Pick<Project, ...> types; imports updated to use Prisma Project.
Webhook Dependencies
apps/web/app/(ee)/api/stripe/webhook/checkout-session-completed.ts
Removed billing/trial and upgrade-email helpers; added Redis, Prisma User type, and trial email template import.
Webhook Onboarding and Email Flow
apps/web/app/(ee)/api/stripe/webhook/checkout-session-completed.ts
Promise.allSettled now calls completeOnboarding with subscription; notification logic simplified to send trial-started email when programOnboarding is absent.
Onboarding Function Contract
apps/web/app/(ee)/api/stripe/webhook/checkout-session-completed.ts
completeOnboarding signature accepts subscription (status); workspace query no longer includes users and programs.
Program Creation Gating
apps/web/app/(ee)/api/stripe/webhook/checkout-session-completed.ts
Program creation gated only by presence of workspace.store?.["programOnboarding"] (removed workspace.programs.length === 0 check).
Subscription-Status Domain Claiming
apps/web/app/(ee)/api/stripe/webhook/checkout-session-completed.ts
Saved-domain claiming now checks subscription.status === "active" instead of workspace.trialEndsAt/billing-trial logic.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped through webhooks, types, and mail,
Claimed dot-links when subscriptions sail,
Project fields now snug and tight,
Onboarding sings when status is right,
A small rabbit cheers this tidy trail.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Fix checkout.session.completed webhook' directly describes the main changes in the PR—re-wiring the Stripe webhook's onboarding and email behavior, and updating domain-claiming logic based on subscription status.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-checkout-completed

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
Copy Markdown
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/webhook/checkout-session-completed.ts (1)

219-241: ⚠️ Potential issue | 🟠 Major

Confirm: saved domains remain unclaimed when trial subscriptions complete checkout.

The concern is valid. When a trial subscription completes checkout, subscription.status is "trialing" (not "active"), so the saved domain claiming at line 220 won't execute. The customer-subscription-updated webhook fires later when the trial converts to active status, but it doesn't claim saved domains. Add domain claiming logic to customer-subscription-updated when subscription.status transitions to "active", or remove the subscription.status === "active" gate to allow trials.

🤖 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/web/app/`(ee)/api/stripe/webhook/checkout-session-completed.ts around
lines 219 - 241, The saved-domain claim is gated by subscription.status ===
"active" in the checkout-session-completed flow so trialing subscriptions never
trigger claimDotLinkDomain; update the customer-subscription-updated webhook
handler to perform the same domain-claiming logic when a subscription
transitions to "active" (i.e., detect previous status vs current status or check
current === "active" if previous was "trialing"), reusing the same redis key
`onboarding-domain:${workspaceId}` and calling claimDotLinkDomain({ domain,
userId, workspace }) with identical error handling; alternatively, if you prefer
claiming at checkout, remove the strict subscription.status === "active" guard
(or allow "trialing") in the checkout-session-completed block so trials also
trigger the claim.
🤖 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/web/app/`(ee)/api/stripe/webhook/checkout-session-completed.ts:
- Around line 148-167: The code sends the 14-day trial email whenever
programOnboarding is missing, which incorrectly mails paying customers; update
the guard around sendBatchEmail so it only fires when the Stripe
checkout/subscription actually includes an active trial. Concretely, before
calling sendBatchEmail/TrialStartedEmail, check the Stripe session/subscription
trial fields (e.g., subscription.trial_end exists and is a future timestamp or
the checkout session indicates a trial) and only send when that condition is
true; keep the existing programOnboarding check
(updatedWorkspace.store?.["programOnboarding"]) and use symbols sendBatchEmail,
TrialStartedEmail, updatedWorkspace, and plan.name to locate and modify the
logic.

---

Outside diff comments:
In `@apps/web/app/`(ee)/api/stripe/webhook/checkout-session-completed.ts:
- Around line 219-241: The saved-domain claim is gated by subscription.status
=== "active" in the checkout-session-completed flow so trialing subscriptions
never trigger claimDotLinkDomain; update the customer-subscription-updated
webhook handler to perform the same domain-claiming logic when a subscription
transitions to "active" (i.e., detect previous status vs current status or check
current === "active" if previous was "trialing"), reusing the same redis key
`onboarding-domain:${workspaceId}` and calling claimDotLinkDomain({ domain,
userId, workspace }) with identical error handling; alternatively, if you prefer
claiming at checkout, remove the strict subscription.status === "active" guard
(or allow "trialing") in the checkout-session-completed block so trials also
trigger the claim.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: e74e05de-7c97-4f5b-b463-4905ec0e0f0c

📥 Commits

Reviewing files that changed from the base of the PR and between 94627f0 and 63d1a31.

📒 Files selected for processing (2)
  • apps/web/app/(ee)/api/stripe/webhook/checkout-session-completed.ts
  • apps/web/lib/api/domains/claim-dot-link-domain.ts

Comment thread apps/web/app/(ee)/api/stripe/webhook/checkout-session-completed.ts
Copy link
Copy Markdown
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.

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/webhook/checkout-session-completed.ts (1)

219-241: ⚠️ Potential issue | 🔴 Critical

Saved domains for trial signups will never be claimed.

The subscription.status === "active" guard at line 220 causes checkout.session.completed to skip the domain claim for trial subscriptions. When the trial later converts to active, the customer.subscription.updated webhook does not read the onboarding-domain:${workspaceId} cache key or call claimDotLinkDomain. As a result, saved domains from trial signups are permanently abandoned and never registered.

Add domain claiming logic to customer.subscription.updated to handle trial-to-active conversions, or remove the subscription.status === "active" guard so all successful checkouts attempt to claim the domain regardless of subscription status.

🤖 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/web/app/`(ee)/api/stripe/webhook/checkout-session-completed.ts around
lines 219 - 241, The current checkout.session.completed handler only calls
claimDotLinkDomain when subscription.status === "active", so trial checkouts
never register saved domains; either remove that status guard in the
checkout.session.completed flow (so the async block that reads
redis.get(`onboarding-domain:${workspaceId}`) and calls claimDotLinkDomain({
domain, userId, workspace }) runs for all successful checkouts) or also add
equivalent logic to the customer.subscription.updated webhook to detect
transitions from trial (or non-active) to active and perform the same redis
lookup and claimDotLinkDomain call (use the same
onboarding-domain:${workspaceId} key and error handling). Ensure you reference
and reuse the existing claimDotLinkDomain invocation/params and workspaceId
lookup so saved domains are claimed when the subscription becomes active.
🤖 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.

Outside diff comments:
In `@apps/web/app/`(ee)/api/stripe/webhook/checkout-session-completed.ts:
- Around line 219-241: The current checkout.session.completed handler only calls
claimDotLinkDomain when subscription.status === "active", so trial checkouts
never register saved domains; either remove that status guard in the
checkout.session.completed flow (so the async block that reads
redis.get(`onboarding-domain:${workspaceId}`) and calls claimDotLinkDomain({
domain, userId, workspace }) runs for all successful checkouts) or also add
equivalent logic to the customer.subscription.updated webhook to detect
transitions from trial (or non-active) to active and perform the same redis
lookup and claimDotLinkDomain call (use the same
onboarding-domain:${workspaceId} key and error handling). Ensure you reference
and reuse the existing claimDotLinkDomain invocation/params and workspaceId
lookup so saved domains are claimed when the subscription becomes active.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 09601f5e-6832-41d7-b274-389a200c2001

📥 Commits

Reviewing files that changed from the base of the PR and between 94627f0 and 63d1a31.

📒 Files selected for processing (2)
  • apps/web/app/(ee)/api/stripe/webhook/checkout-session-completed.ts
  • apps/web/lib/api/domains/claim-dot-link-domain.ts

@steven-tey steven-tey merged commit 92db46e into main May 12, 2026
12 checks passed
@steven-tey steven-tey deleted the fix-checkout-completed branch May 12, 2026 01:53
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.

1 participant