MCPcopy
hub / github.com/simstudioai/sim / sendPaymentFailureEmails

Function sendPaymentFailureEmails

apps/sim/lib/billing/webhooks/invoices.ts:281–364  ·  view source on GitHub ↗

* Send payment failure notification emails to affected users * Note: This is only called when billing is enabled (Stripe plugin loaded)

(
  sub: { plan: string | null; referenceId: string },
  invoice: Stripe.Invoice,
  stripeCustomerId: string
)

Source from the content-addressed store, hash-verified

279 * Note: This is only called when billing is enabled (Stripe plugin loaded)
280 */
281async function sendPaymentFailureEmails(
282 sub: { plan: string | null; referenceId: string },
283 invoice: Stripe.Invoice,
284 stripeCustomerId: string
285): Promise<void> {
286 try {
287 const billingPortalUrl = await createBillingPortalUrl(stripeCustomerId)
288 const amountDue = invoice.amount_due / 100 // Convert cents to dollars
289 const { lastFourDigits, failureReason } = await getPaymentMethodDetails(invoice)
290
291 // Notify based on subscription scope — org-scoped subs alert owners/admins.
292 let usersToNotify: Array<{ email: string; name: string | null }> = []
293 const orgScoped = await isSubscriptionOrgScoped(sub)
294
295 if (orgScoped) {
296 const members = await db
297 .select({
298 userId: member.userId,
299 role: member.role,
300 })
301 .from(member)
302 .where(eq(member.organizationId, sub.referenceId))
303
304 const ownerAdminIds = members.filter((m) => isOrgAdminRole(m.role)).map((m) => m.userId)
305
306 if (ownerAdminIds.length > 0) {
307 const users = await db
308 .select({ email: user.email, name: user.name })
309 .from(user)
310 .where(inArray(user.id, ownerAdminIds))
311
312 usersToNotify = users.filter((u) => u.email && quickValidateEmail(u.email).isValid)
313 }
314 } else {
315 const users = await db
316 .select({ email: user.email, name: user.name })
317 .from(user)
318 .where(eq(user.id, sub.referenceId))
319 .limit(1)
320
321 if (users.length > 0) {
322 usersToNotify = users.filter((u) => u.email && quickValidateEmail(u.email).isValid)
323 }
324 }
325
326 // Send emails to all affected users
327 for (const userToNotify of usersToNotify) {
328 try {
329 const emailHtml = await render(
330 PaymentFailedEmail({
331 userName: userToNotify.name || undefined,
332 amountDue,
333 lastFourDigits,
334 billingPortalUrl,
335 failureReason,
336 sentDate: new Date(),
337 })
338 )

Callers 1

Calls 12

isSubscriptionOrgScopedFunction · 0.90
quickValidateEmailFunction · 0.90
PaymentFailedEmailFunction · 0.90
getPersonalEmailFromFunction · 0.90
sendEmailFunction · 0.90
createBillingPortalUrlFunction · 0.85
getPaymentMethodDetailsFunction · 0.85
isOrgAdminRoleFunction · 0.85
infoMethod · 0.80
errorMethod · 0.80
eqFunction · 0.50
renderFunction · 0.50

Tested by

no test coverage detected