mirror of
https://github.com/wasp-lang/open-saas.git
synced 2025-11-19 05:26:57 +01:00
stripe
This commit is contained in:
@@ -10,16 +10,16 @@ import { stripeClient } from "./stripeClient";
|
|||||||
export async function ensureStripeCustomer(
|
export async function ensureStripeCustomer(
|
||||||
userEmail: NonNullable<User["email"]>,
|
userEmail: NonNullable<User["email"]>,
|
||||||
): Promise<Stripe.Customer> {
|
): Promise<Stripe.Customer> {
|
||||||
const stripeCustomers = await stripeClient.customers.list({
|
const customers = await stripeClient.customers.list({
|
||||||
email: userEmail,
|
email: userEmail,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (stripeCustomers.data.length === 0) {
|
if (customers.data.length === 0) {
|
||||||
return stripeClient.customers.create({
|
return stripeClient.customers.create({
|
||||||
email: userEmail,
|
email: userEmail,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return stripeCustomers.data[0];
|
return customers.data[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,13 +36,13 @@ export const stripePaymentProcessor: PaymentProcessor = {
|
|||||||
prismaUserDelegate,
|
prismaUserDelegate,
|
||||||
);
|
);
|
||||||
|
|
||||||
const stripeSession = await createStripeCheckoutSession({
|
const checkoutSession = await createStripeCheckoutSession({
|
||||||
customerId: customer.id,
|
customerId: customer.id,
|
||||||
priceId: paymentPlan.getPaymentProcessorPlanId(),
|
priceId: paymentPlan.getPaymentProcessorPlanId(),
|
||||||
mode: paymentPlanEffectToStripeCheckoutSessionMode(paymentPlan.effect),
|
mode: paymentPlanEffectToStripeCheckoutSessionMode(paymentPlan.effect),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!stripeSession.url) {
|
if (!checkoutSession.url) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"Stripe checkout session URL is missing. Checkout session might not be active.",
|
"Stripe checkout session URL is missing. Checkout session might not be active.",
|
||||||
);
|
);
|
||||||
@@ -50,8 +50,8 @@ export const stripePaymentProcessor: PaymentProcessor = {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
session: {
|
session: {
|
||||||
url: stripeSession.url,
|
url: checkoutSession.url,
|
||||||
id: stripeSession.id,
|
id: checkoutSession.id,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -35,38 +35,30 @@ export const stripeWebhook: PaymentsWebhook = async (
|
|||||||
) => {
|
) => {
|
||||||
const prismaUserDelegate = context.entities.User;
|
const prismaUserDelegate = context.entities.User;
|
||||||
try {
|
try {
|
||||||
const stripeEvent = constructStripeEvent(request);
|
const event = constructStripeEvent(request);
|
||||||
|
|
||||||
// If you'd like to handle more events, you can add more cases below.
|
// If you'd like to handle more events, you can add more cases below.
|
||||||
// When deploying your app, you configure your webhook in the Stripe dashboard
|
// When deploying your app, you configure your webhook in the Stripe dashboard
|
||||||
// to only send the events that you're handling above.
|
// to only send the events that you're handling above.
|
||||||
// See: https://docs.opensaas.sh/guides/deploying/#setting-up-your-stripe-webhook
|
// See: https://docs.opensaas.sh/guides/deploying/#setting-up-your-stripe-webhook
|
||||||
switch (stripeEvent.type) {
|
switch (event.type) {
|
||||||
case "invoice.paid":
|
case "invoice.paid":
|
||||||
await handleInvoicePaid(stripeEvent, prismaUserDelegate);
|
await handleInvoicePaid(event, prismaUserDelegate);
|
||||||
break;
|
break;
|
||||||
case "customer.subscription.updated":
|
case "customer.subscription.updated":
|
||||||
await handleCustomerSubscriptionUpdated(
|
await handleCustomerSubscriptionUpdated(event, prismaUserDelegate);
|
||||||
stripeEvent,
|
|
||||||
prismaUserDelegate,
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
case "customer.subscription.deleted":
|
case "customer.subscription.deleted":
|
||||||
await handleCustomerSubscriptionDeleted(
|
await handleCustomerSubscriptionDeleted(event, prismaUserDelegate);
|
||||||
stripeEvent,
|
|
||||||
prismaUserDelegate,
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new UnhandledWebhookEventError(stripeEvent.type);
|
throw new UnhandledWebhookEventError(event.type);
|
||||||
}
|
}
|
||||||
return response.status(204).send();
|
return response.status(204).send();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof UnhandledWebhookEventError) {
|
if (error instanceof UnhandledWebhookEventError) {
|
||||||
// In development, it is likely that we will receive events that we are not handling.
|
// In development, it is likely that we will receive events that we are not handling.
|
||||||
// E.g. via the `stripe trigger` command.
|
// E.g. via the `stripe trigger` command.
|
||||||
// While these can be ignored safely in development, it's good to be aware of them.
|
|
||||||
// For production we shouldn't have any extra webhook events.
|
|
||||||
if (process.env.NODE_ENV === "development") {
|
if (process.env.NODE_ENV === "development") {
|
||||||
console.info("Unhandled Stripe webhook event in development: ", error);
|
console.info("Unhandled Stripe webhook event in development: ", error);
|
||||||
} else if (process.env.NODE_ENV === "production") {
|
} else if (process.env.NODE_ENV === "production") {
|
||||||
@@ -82,7 +74,7 @@ export const stripeWebhook: PaymentsWebhook = async (
|
|||||||
return response.status(400).json({ error: error.message });
|
return response.status(400).json({ error: error.message });
|
||||||
} else {
|
} else {
|
||||||
return response
|
return response
|
||||||
.status(400)
|
.status(500)
|
||||||
.json({ error: "Error processing Stripe webhook event" });
|
.json({ error: "Error processing Stripe webhook event" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -190,14 +182,31 @@ async function handleCustomerSubscriptionUpdated(
|
|||||||
function getOpenSaasSubscriptionStatus(
|
function getOpenSaasSubscriptionStatus(
|
||||||
subscription: Stripe.Subscription,
|
subscription: Stripe.Subscription,
|
||||||
): SubscriptionStatus | undefined {
|
): SubscriptionStatus | undefined {
|
||||||
if (subscription.status === SubscriptionStatus.Active) {
|
const stripeToOpenSaasSubscriptionStatusMap: Record<
|
||||||
if (subscription.cancel_at_period_end) {
|
Stripe.Subscription.Status,
|
||||||
return SubscriptionStatus.CancelAtPeriodEnd;
|
SubscriptionStatus | undefined
|
||||||
}
|
> = {
|
||||||
return SubscriptionStatus.Active;
|
trialing: SubscriptionStatus.Active,
|
||||||
} else if (subscription.status === SubscriptionStatus.PastDue) {
|
active: SubscriptionStatus.Active,
|
||||||
return SubscriptionStatus.PastDue;
|
past_due: SubscriptionStatus.PastDue,
|
||||||
|
canceled: SubscriptionStatus.Deleted,
|
||||||
|
unpaid: SubscriptionStatus.Deleted,
|
||||||
|
incomplete_expired: SubscriptionStatus.Deleted,
|
||||||
|
paused: undefined,
|
||||||
|
incomplete: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
const subscriptionStauts =
|
||||||
|
stripeToOpenSaasSubscriptionStatusMap[subscription.status];
|
||||||
|
|
||||||
|
if (
|
||||||
|
subscriptionStauts === SubscriptionStatus.Active &&
|
||||||
|
subscription.cancel_at_period_end
|
||||||
|
) {
|
||||||
|
return SubscriptionStatus.CancelAtPeriodEnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return subscriptionStauts;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSubscriptionPriceId(
|
function getSubscriptionPriceId(
|
||||||
|
|||||||
Reference in New Issue
Block a user