From 4c26f3ccf3cd5fc083d05bbc1455c96b7321ffb9 Mon Sep 17 00:00:00 2001 From: James Perkins Date: Sun, 30 Mar 2025 16:15:51 -0400 Subject: [PATCH 1/4] Stripe alerts for trials --- .../app/api/webhooks/stripe/route.ts | 53 ++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/apps/dashboard/app/api/webhooks/stripe/route.ts b/apps/dashboard/app/api/webhooks/stripe/route.ts index 8c552dde71..776b639ecf 100644 --- a/apps/dashboard/app/api/webhooks/stripe/route.ts +++ b/apps/dashboard/app/api/webhooks/stripe/route.ts @@ -73,10 +73,61 @@ export const POST = async (req: Request): Promise => { }); break; } + case "customer.subscription.created": { + const sub = event.data.object as Stripe.Subscription; + + // Only handle trial subscriptions + if (!sub.trial_end) { + return new Response("OK"); + } + + // Get product and price information + const price = await stripe.prices.retrieve(sub.items.data[0].price.id); + const product = await stripe.products.retrieve(price.product as string); + const customer = await stripe.customers.retrieve(sub.customer as string) as Stripe.Customer; + await alertSlack(product.name, price.unit_amount!.toString(), customer.email!, customer.name!); + break; + } default: - console.error("Incoming stripe event, that should not be received", event.type); + console.warn("Incoming stripe event, that should not be received", event.type); break; } return new Response("OK"); }; + + +async function alertSlack(product: string, price: string, email: string, name?: string): Promise { + const url = process.env.SLACK_WEBHOOK_CUSTOMERS; + if (!url) { + return; + } + + + await fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + "blocks": [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": `:bugeyes: New customer ${name} signed up for a two week trial` + } + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": `A new trial for ${product} with ${price} price has been signed up by ${email} :moneybag: ` + } + }, + ] + }), + }).catch((err: Error) => { + console.error(err); + }); +} \ No newline at end of file From 4f33c3be3f9c4e34bac350374308ac54374a5cea Mon Sep 17 00:00:00 2001 From: James Perkins Date: Sun, 30 Mar 2025 16:30:44 -0400 Subject: [PATCH 2/4] fmt price --- apps/dashboard/app/api/webhooks/stripe/route.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/dashboard/app/api/webhooks/stripe/route.ts b/apps/dashboard/app/api/webhooks/stripe/route.ts index 776b639ecf..c20ec5e4a6 100644 --- a/apps/dashboard/app/api/webhooks/stripe/route.ts +++ b/apps/dashboard/app/api/webhooks/stripe/route.ts @@ -85,7 +85,14 @@ export const POST = async (req: Request): Promise => { const price = await stripe.prices.retrieve(sub.items.data[0].price.id); const product = await stripe.products.retrieve(price.product as string); const customer = await stripe.customers.retrieve(sub.customer as string) as Stripe.Customer; - await alertSlack(product.name, price.unit_amount!.toString(), customer.email!, customer.name!); + + const formattedPrice = new Intl.NumberFormat("en-US", { + style: "currency", + currency: "USD", + }).format(price.unit_amount! / 100); + + + await alertSlack(product.name, formattedPrice, customer.email!, customer.name!); break; } From 53a7be539dca859d4db0b981208fcbdecff7e95f Mon Sep 17 00:00:00 2001 From: James Perkins Date: Sun, 30 Mar 2025 16:34:12 -0400 Subject: [PATCH 3/4] update wording --- apps/dashboard/app/api/webhooks/stripe/route.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dashboard/app/api/webhooks/stripe/route.ts b/apps/dashboard/app/api/webhooks/stripe/route.ts index c20ec5e4a6..d2d671d950 100644 --- a/apps/dashboard/app/api/webhooks/stripe/route.ts +++ b/apps/dashboard/app/api/webhooks/stripe/route.ts @@ -129,7 +129,7 @@ async function alertSlack(product: string, price: string, email: string, name?: "type": "section", "text": { "type": "mrkdwn", - "text": `A new trial for ${product} with ${price} price has been signed up by ${email} :moneybag: ` + "text": `A new trial for the ${product} tier has started at a price of ${price} by ${email} :moneybag: ` } }, ] From 674079a2fe5ac3f6cae6e0e76ed3ea61b800d8d5 Mon Sep 17 00:00:00 2001 From: James Perkins Date: Sun, 30 Mar 2025 16:40:00 -0400 Subject: [PATCH 4/4] fmt --- .../app/api/webhooks/stripe/route.ts | 46 ++++++++++--------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/apps/dashboard/app/api/webhooks/stripe/route.ts b/apps/dashboard/app/api/webhooks/stripe/route.ts index d2d671d950..7a78e94f06 100644 --- a/apps/dashboard/app/api/webhooks/stripe/route.ts +++ b/apps/dashboard/app/api/webhooks/stripe/route.ts @@ -75,23 +75,22 @@ export const POST = async (req: Request): Promise => { } case "customer.subscription.created": { const sub = event.data.object as Stripe.Subscription; - + // Only handle trial subscriptions if (!sub.trial_end) { - return new Response("OK"); + return new Response("OK"); } // Get product and price information const price = await stripe.prices.retrieve(sub.items.data[0].price.id); const product = await stripe.products.retrieve(price.product as string); - const customer = await stripe.customers.retrieve(sub.customer as string) as Stripe.Customer; + const customer = (await stripe.customers.retrieve(sub.customer as string)) as Stripe.Customer; const formattedPrice = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", }).format(price.unit_amount! / 100); - await alertSlack(product.name, formattedPrice, customer.email!, customer.name!); break; } @@ -103,13 +102,16 @@ export const POST = async (req: Request): Promise => { return new Response("OK"); }; - -async function alertSlack(product: string, price: string, email: string, name?: string): Promise { +async function alertSlack( + product: string, + price: string, + email: string, + name?: string, +): Promise { const url = process.env.SLACK_WEBHOOK_CUSTOMERS; if (!url) { return; } - await fetch(url, { method: "POST", @@ -117,24 +119,24 @@ async function alertSlack(product: string, price: string, email: string, name?: "Content-Type": "application/json", }, body: JSON.stringify({ - "blocks": [ + blocks: [ + { + type: "section", + text: { + type: "mrkdwn", + text: `:bugeyes: New customer ${name} signed up for a two week trial`, + }, + }, { - "type": "section", - "text": { - "type": "mrkdwn", - "text": `:bugeyes: New customer ${name} signed up for a two week trial` - } + type: "section", + text: { + type: "mrkdwn", + text: `A new trial for the ${product} tier has started at a price of ${price} by ${email} :moneybag: `, + }, }, - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": `A new trial for the ${product} tier has started at a price of ${price} by ${email} :moneybag: ` - } - }, - ] + ], }), }).catch((err: Error) => { console.error(err); }); -} \ No newline at end of file +}