add stripe / payment events
This commit is contained in:
parent
d778f61f33
commit
899a604d6d
3 changed files with 123 additions and 55 deletions
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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 };
|
||||||
}),
|
}
|
||||||
|
),
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue