add stripe / payment events

This commit is contained in:
MarconLP 2023-04-23 19:52:14 +02:00
parent d778f61f33
commit 899a604d6d
No known key found for this signature in database
GPG key ID: A08A9C8B623F5EA5
3 changed files with 123 additions and 55 deletions

View file

@ -9,6 +9,7 @@ import {
handleSubscriptionCreatedOrUpdated, handleSubscriptionCreatedOrUpdated,
} from "~/server/stripe-webhook-handlers"; } from "~/server/stripe-webhook-handlers";
import { stripe } from "~/server/stripe"; import { stripe } from "~/server/stripe";
import { posthog } from "~/server/posthog";
// Stripe requires the raw body to construct the event. // Stripe requires the raw body to construct the event.
export const config = { export const config = {
@ -41,6 +42,7 @@ export default async function handler(
event, event,
stripe, stripe,
prisma, prisma,
posthog,
}); });
break; break;
case "customer.subscription.created": case "customer.subscription.created":
@ -48,6 +50,7 @@ export default async function handler(
await handleSubscriptionCreatedOrUpdated({ await handleSubscriptionCreatedOrUpdated({
event, event,
prisma, prisma,
posthog,
}); });
break; break;
case "customer.subscription.updated": case "customer.subscription.updated":
@ -55,6 +58,7 @@ export default async function handler(
await handleSubscriptionCreatedOrUpdated({ await handleSubscriptionCreatedOrUpdated({
event, event,
prisma, prisma,
posthog,
}); });
break; break;
case "invoice.payment_failed": case "invoice.payment_failed":
@ -63,6 +67,17 @@ export default async function handler(
// Use this webhook to notify your user that their payment has // Use this webhook to notify your user that their payment has
// failed and to retrieve new card details. // failed and to retrieve new card details.
// Can also have Stripe send an email to the customer notifying them of the failure. See settings: https://dashboard.stripe.com/settings/billing/automatic // Can also have Stripe send an email to the customer notifying them of the failure. See settings: https://dashboard.stripe.com/settings/billing/automatic
const subscription = event.data.object as Stripe.Subscription;
const userId = subscription.metadata.userId;
if (userId) {
posthog.capture({
distinctId: userId,
event: "stripe invoice.payment_failed",
});
void posthog.shutdownAsync();
}
break; break;
case "customer.subscription.deleted": case "customer.subscription.deleted":
// handle subscription cancelled automatically based // handle subscription cancelled automatically based
@ -70,6 +85,7 @@ export default async function handler(
await handleSubscriptionCanceled({ await handleSubscriptionCanceled({
event, event,
prisma, prisma,
posthog,
}); });
break; break;
default: default:

View file

@ -6,9 +6,8 @@ import { z } from "zod";
export const stripeRouter = createTRPCRouter({ export const stripeRouter = createTRPCRouter({
createCheckoutSession: protectedProcedure createCheckoutSession: protectedProcedure
.input(z.object({ billedAnnually: z.boolean() })) .input(z.object({ billedAnnually: z.boolean() }))
.mutation(async ({ ctx, input }) => { .mutation(
const { stripe, session, prisma, req } = ctx; async ({ ctx: { prisma, stripe, session, req, posthog }, input }) => {
const customerId = await getOrCreateStripeCustomerIdForUser({ const customerId = await getOrCreateStripeCustomerIdForUser({
prisma, prisma,
stripe, stripe,
@ -51,11 +50,20 @@ export const stripeRouter = createTRPCRouter({
throw new Error("Could not create checkout session"); throw new Error("Could not create checkout session");
} }
return { checkoutUrl: checkoutSession.url }; posthog.capture({
}), distinctId: session.user.id,
createBillingPortalSession: protectedProcedure.mutation(async ({ ctx }) => { event: "visiting checkout page",
const { stripe, session, prisma, req } = ctx; properties: {
billingCycle: input.billedAnnually ? "annual" : "monthly",
},
});
void posthog.shutdownAsync();
return { checkoutUrl: checkoutSession.url };
}
),
createBillingPortalSession: protectedProcedure.mutation(
async ({ ctx: { stripe, session, prisma, req, posthog } }) => {
const customerId = await getOrCreateStripeCustomerIdForUser({ const customerId = await getOrCreateStripeCustomerIdForUser({
prisma, prisma,
stripe, stripe,
@ -81,6 +89,16 @@ export const stripeRouter = createTRPCRouter({
throw new Error("Could not create billing portal session"); throw new Error("Could not create billing portal session");
} }
posthog.capture({
distinctId: session.user.id,
event: "visiting billing portal page",
properties: {
stripeSubscriptionStatus: session.user.stripeSubscriptionStatus,
},
});
void posthog.shutdownAsync();
return { billingPortalUrl: stripeBillingPortalSession.url }; return { billingPortalUrl: stripeBillingPortalSession.url };
}), }
),
}); });

View file

@ -1,5 +1,6 @@
import type { PrismaClient } from "@prisma/client"; import type { PrismaClient } from "@prisma/client";
import type Stripe from "stripe"; import type Stripe from "stripe";
import { type PostHog } from "posthog-node";
// retrieves a Stripe customer id for a given user if it exists or creates a new one // retrieves a Stripe customer id for a given user if it exists or creates a new one
export const getOrCreateStripeCustomerIdForUser = async ({ export const getOrCreateStripeCustomerIdForUser = async ({
@ -54,10 +55,12 @@ export const handleInvoicePaid = async ({
event, event,
stripe, stripe,
prisma, prisma,
posthog,
}: { }: {
event: Stripe.Event; event: Stripe.Event;
stripe: Stripe; stripe: Stripe;
prisma: PrismaClient; prisma: PrismaClient;
posthog: PostHog;
}) => { }) => {
const invoice = event.data.object as Stripe.Invoice; const invoice = event.data.object as Stripe.Invoice;
const subscriptionId = invoice.subscription; const subscriptionId = invoice.subscription;
@ -76,14 +79,27 @@ export const handleInvoicePaid = async ({
stripeSubscriptionStatus: subscription.status, stripeSubscriptionStatus: subscription.status,
}, },
}); });
if (userId && subscription.status) {
posthog.capture({
distinctId: userId,
event: "stripe invoice.paid",
properties: {
stripeSubscriptionStatus: subscription.status,
},
});
void posthog.shutdownAsync();
}
}; };
export const handleSubscriptionCreatedOrUpdated = async ({ export const handleSubscriptionCreatedOrUpdated = async ({
event, event,
prisma, prisma,
posthog,
}: { }: {
event: Stripe.Event; event: Stripe.Event;
prisma: PrismaClient; prisma: PrismaClient;
posthog: PostHog;
}) => { }) => {
const subscription = event.data.object as Stripe.Subscription; const subscription = event.data.object as Stripe.Subscription;
const userId = subscription.metadata.userId; const userId = subscription.metadata.userId;
@ -98,14 +114,24 @@ export const handleSubscriptionCreatedOrUpdated = async ({
stripeSubscriptionStatus: subscription.status, stripeSubscriptionStatus: subscription.status,
}, },
}); });
if (userId && subscription.status) {
posthog.capture({
distinctId: userId,
event: "stripe subscription created or updated",
});
void posthog.shutdownAsync();
}
}; };
export const handleSubscriptionCanceled = async ({ export const handleSubscriptionCanceled = async ({
event, event,
prisma, prisma,
posthog,
}: { }: {
event: Stripe.Event; event: Stripe.Event;
prisma: PrismaClient; prisma: PrismaClient;
posthog: PostHog;
}) => { }) => {
const subscription = event.data.object as Stripe.Subscription; const subscription = event.data.object as Stripe.Subscription;
const userId = subscription.metadata.userId; const userId = subscription.metadata.userId;
@ -120,4 +146,12 @@ export const handleSubscriptionCanceled = async ({
stripeSubscriptionStatus: null, stripeSubscriptionStatus: null,
}, },
}); });
if (userId && subscription.status) {
posthog.capture({
distinctId: userId,
event: "stripe subscription cancelled",
});
void posthog.shutdownAsync();
}
}; };