Skip to content

Commit

Permalink
Use stripe integration
Browse files Browse the repository at this point in the history
  • Loading branch information
lukevella committed Feb 9, 2025
1 parent ddc898d commit 822ece7
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 8 deletions.
1 change: 1 addition & 0 deletions apps/web/src/app/api/stripe/checkout/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ export async function POST(request: NextRequest) {
automatic_tax: {
enabled: true,
},
expires_at: Math.floor(Date.now() / 1000) + 30 * 60, // 30 minutes
after_expiration: {
recovery: {
enabled: true,
Expand Down
27 changes: 20 additions & 7 deletions apps/web/src/app/api/stripe/webhook/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,18 +210,21 @@ export async function POST(request: NextRequest) {
break;
}
case "checkout.session.expired": {
console.info("Checkout session expired");
const session = event.data.object as Stripe.Checkout.Session;
// When a Checkout Session expires, the customer's email isn't returned in
// the webhook payload unless they give consent for promotional content
const email = session.customer_details?.email;
const recoveryUrl = session.after_expiration?.recovery?.url;
const userId = session.metadata?.userId;
if (!userId) {
console.info("No user ID found in Checkout Session metadata");
Sentry.captureMessage("No user ID found in Checkout Session metadata");
break;
}
// Do nothing if the Checkout Session has no email or recovery URL
if (!email || !recoveryUrl) {
console.info("No email or recovery URL found in Checkout Session");
Sentry.captureMessage(
"No email or recovery URL found in Checkout Session",
);
Expand All @@ -230,12 +233,14 @@ export async function POST(request: NextRequest) {
const promoEmailKey = `promo_email_sent:${email}`;
// Track that a promotional email opportunity has been shown to this user
const hasReceivedPromo = await kv.get(promoEmailKey);
console.info("Has received promo", hasReceivedPromo);

const user = await prisma.user.findUnique({
where: {
id: userId,
},
select: {
locale: true,
subscription: {
select: {
active: true,
Expand All @@ -247,16 +252,24 @@ export async function POST(request: NextRequest) {
const isPro = !!user?.subscription?.active;

// Avoid spamming people who abandon Checkout multiple times
if (!hasReceivedPromo && !isPro) {
if (user && !hasReceivedPromo && !isPro) {
console.info("Sending abandoned checkout email");
// Set the flag with a 30-day expiration (in seconds)
await kv.set(promoEmailKey, 1, { ex: 30 * 24 * 60 * 60, nx: true });
getEmailClient().sendTemplate("AbandonedCheckoutEmail", {
to: email,
props: {
name: session.customer_details?.name ?? undefined,
recoveryUrl,
getEmailClient(user.locale ?? undefined).sendTemplate(
"AbandonedCheckoutEmail",
{
to: email,
from: {
name: "Luke from Rallly",
address: "[email protected]",
},
props: {
name: session.customer_details?.name ?? undefined,
recoveryUrl,
},
},
});
);
}

break;
Expand Down
6 changes: 5 additions & 1 deletion packages/emails/src/send-email.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import type { TemplateComponent, TemplateName, TemplateProps } from "./types";

type SendEmailOptions<T extends TemplateName> = {
to: string;
from?: {
name: string;
address: string;
};
props: TemplateProps<T>;
attachments?: Mail.Options["attachments"];
};
Expand Down Expand Up @@ -106,7 +110,7 @@ export class EmailClient {

try {
await this.sendEmail({
from: this.config.mail.from,
from: options.from || this.config.mail.from,
to: options.to,
subject,
html,
Expand Down

0 comments on commit 822ece7

Please sign in to comment.