MCPcopy Index your code
hub / github.com/simstudioai/sim / handleAbandonedCheckout

Function handleAbandonedCheckout

apps/sim/lib/billing/webhooks/checkout.ts:19–58  ·  view source on GitHub ↗
(event: Stripe.Event)

Source from the content-addressed store, hash-verified

17 * Skips users who have already completed a subscription (session may expire after a successful upgrade).
18 */
19export async function handleAbandonedCheckout(event: Stripe.Event): Promise<void> {
20 const session = event.data.object as Stripe.Checkout.Session
21
22 if (session.mode !== 'subscription') return
23
24 const customerId = typeof session.customer === 'string' ? session.customer : session.customer?.id
25 if (!customerId) {
26 logger.warn('No customer ID on expired session', { sessionId: session.id })
27 return
28 }
29
30 const [userData] = await db
31 .select({ id: user.id, email: user.email, name: user.name })
32 .from(user)
33 .where(eq(user.stripeCustomerId, customerId))
34 .limit(1)
35
36 if (!userData?.email) {
37 logger.warn('No user found for Stripe customer', { customerId, sessionId: session.id })
38 return
39 }
40
41 // Skip if the user already has a paid plan (direct or via org) — covers session expiring after a successful upgrade
42 const alreadySubscribed = await isProPlan(userData.id)
43 if (alreadySubscribed) return
44
45 const { from, replyTo } = getPersonalEmailFrom()
46 const html = await renderAbandonedCheckoutEmail(userData.name || undefined)
47
48 await sendEmail({
49 to: userData.email,
50 subject: getEmailSubject('abandoned-checkout'),
51 html,
52 from,
53 replyTo,
54 emailType: 'notifications',
55 })
56
57 logger.info('Sent abandoned checkout email', { userId: userData.id, sessionId: session.id })
58}

Callers 1

auth.tsFile · 0.90

Calls 8

isProPlanFunction · 0.90
getPersonalEmailFromFunction · 0.90
sendEmailFunction · 0.90
getEmailSubjectFunction · 0.90
infoMethod · 0.80
warnMethod · 0.65
eqFunction · 0.50

Tested by

no test coverage detected