mirror of
https://github.com/wasp-lang/open-saas.git
synced 2025-03-29 11:12:19 +01:00
update webhook and doc links
This commit is contained in:
parent
90566cf5d3
commit
aade090e33
@ -1,6 +1,7 @@
|
||||
# NOTE: if you setup your DB using `wasp start db` then you DO NOT need to add a DATABASE_URL env.
|
||||
# DATABASE_URL=
|
||||
|
||||
# for testing, go to https://dashboard.stripe.com/test/apikeys and get a test stripe key that starts with "sk_test_..."
|
||||
STRIPE_KEY=
|
||||
SUBSCRIPTION_PRICE_ID=
|
||||
|
||||
|
19
main.wasp
19
main.wasp
@ -1,6 +1,6 @@
|
||||
app SaaSTemplate {
|
||||
wasp: {
|
||||
version: "^0.11.1"
|
||||
version: "^0.11.6"
|
||||
},
|
||||
title: "My SaaS App",
|
||||
head: [
|
||||
@ -10,7 +10,7 @@ app SaaSTemplate {
|
||||
"<meta property='og:image' content='src/client/static/image.png' />",
|
||||
// you can put your google analytics script here, too!
|
||||
],
|
||||
// 🔐 Auth out of the box! https://wasp-lang.dev/docs/language/features#authentication--authorization
|
||||
// 🔐 Auth out of the box! https://wasp-lang.dev/docs/auth/overview
|
||||
auth: {
|
||||
userEntity: User,
|
||||
externalAuthEntity: SocialLogin,
|
||||
@ -30,7 +30,7 @@ app SaaSTemplate {
|
||||
getEmailContentFn: import { getPasswordResetEmailContent } from "@server/auth/email.js",
|
||||
},
|
||||
},
|
||||
google: { // Guide for setting up Auth via Google https://wasp-lang.dev/docs/integrations/google
|
||||
google: { // Guide for setting up Auth via Google https://wasp-lang.dev/docs/auth/social-auth/overview
|
||||
getUserFieldsFn: import { getUserFields } from "@server/auth/google.js",
|
||||
configFn: import { config } from "@server/auth/google.js",
|
||||
},
|
||||
@ -60,13 +60,13 @@ app SaaSTemplate {
|
||||
("request-ip", "3.3.0"),
|
||||
("@types/request-ip", "0.0.37"),
|
||||
("node-fetch", "3.3.0"),
|
||||
("react-hook-form", "7.43.1"),
|
||||
("react-hook-form", "^7.45.4"),
|
||||
("stripe", "11.15.0"),
|
||||
],
|
||||
}
|
||||
|
||||
/* 💽 Wasp defines DB entities via Prisma Database Models:
|
||||
* https://wasp-lang.dev/docs/language/features#entity
|
||||
* https://wasp-lang.dev/docs/data-model/entities
|
||||
*/
|
||||
|
||||
entity User {=psl
|
||||
@ -80,6 +80,7 @@ entity User {=psl
|
||||
checkoutSessionId String?
|
||||
hasPaid Boolean @default(false)
|
||||
sendEmail Boolean @default(false)
|
||||
subscriptionStatus String?
|
||||
datePaid DateTime?
|
||||
credits Int @default(3)
|
||||
relatedObject RelatedObject[]
|
||||
@ -108,7 +109,7 @@ psl=}
|
||||
|
||||
|
||||
/* 📡 These are the Wasp Routes (You can protect them easily w/ 'authRequired: true');
|
||||
* https://wasp-lang.dev/docs/language/features#route
|
||||
* https://wasp-lang.dev/docs/tutorial/pages
|
||||
*/
|
||||
|
||||
route RootRoute { path: "/", to: MainPage }
|
||||
@ -164,7 +165,7 @@ page CheckoutPage {
|
||||
}
|
||||
|
||||
/* ⛑ These are the Wasp Operations, which allow the client and server to interact:
|
||||
* https://wasp-lang.dev/docs/language/features#queries-and-actions-aka-operations
|
||||
* https://wasp-lang.dev/docs/data-model/operations/overview
|
||||
*/
|
||||
|
||||
// 📝 Actions aka Mutations
|
||||
@ -198,7 +199,7 @@ query getRelatedObjects {
|
||||
|
||||
/*
|
||||
* 📡 These are custom Wasp API Endpoints. Use them for callbacks, webhooks, etc.
|
||||
* https://wasp-lang.dev/docs/language/features#apis
|
||||
* https://wasp-lang.dev/docs/advanced/apis
|
||||
*/
|
||||
|
||||
api stripeWebhook {
|
||||
@ -208,7 +209,7 @@ api stripeWebhook {
|
||||
}
|
||||
|
||||
/* 🕵️♂️ These are the Wasp Cron Jobs. Use them to set up recurring tasks and/or queues:
|
||||
* https://wasp-lang.dev/docs/language/features#jobs
|
||||
* https://wasp-lang.dev/docs/advanced/jobs
|
||||
*/
|
||||
|
||||
job emailChecker {
|
||||
|
@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "User" ADD COLUMN "subscriptionStatus" TEXT;
|
@ -70,12 +70,18 @@ export default function Example({ user }: { user: User }) {
|
||||
|
||||
function BuyMoreButton({ isLoading, setIsLoading }: { isLoading: boolean, setIsLoading: Dispatch<SetStateAction<boolean>> }) {
|
||||
const handleClick = async () => {
|
||||
setIsLoading(true);
|
||||
const stripeResults = await stripePayment();
|
||||
if (stripeResults?.sessionUrl) {
|
||||
window.open(stripeResults.sessionUrl, '_self');
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const stripeResults = await stripePayment();
|
||||
if (stripeResults?.sessionUrl) {
|
||||
window.open(stripeResults.sessionUrl, '_self');
|
||||
}
|
||||
|
||||
} catch (error: any) {
|
||||
alert(error?.message ?? 'Something went wrong.')
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -16,6 +16,7 @@ export const stripePayment: StripePayment<void, StripePaymentResult> = async (_a
|
||||
if (!context.user) {
|
||||
throw new HttpError(401);
|
||||
}
|
||||
|
||||
let customer: Stripe.Customer;
|
||||
const stripeCustomers = await stripe.customers.list({
|
||||
email: context.user.email!,
|
||||
|
@ -24,7 +24,6 @@ const stripe = new Stripe(process.env.STRIPE_KEY!, {
|
||||
});
|
||||
|
||||
export const stripeWebhook: StripeWebhook = async (request, response, context) => {
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
const detectedIp = requestIp.getClientIp(request) as string;
|
||||
const isStripeIP = STRIPE_WEBHOOK_IPS.includes(detectedIp);
|
||||
@ -42,6 +41,7 @@ export const stripeWebhook: StripeWebhook = async (request, response, context) =
|
||||
event = request.body;
|
||||
|
||||
if (event.type === 'checkout.session.completed') {
|
||||
console.log('Checkout session completed');
|
||||
const session = event.data.object as Stripe.Checkout.Session;
|
||||
userStripeId = session.customer as string;
|
||||
|
||||
@ -59,13 +59,14 @@ export const stripeWebhook: StripeWebhook = async (request, response, context) =
|
||||
},
|
||||
data: {
|
||||
hasPaid: true,
|
||||
datePaid: new Date(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* and here is an example of handling a different type of product
|
||||
* make sure to configure it in the Stripe dashboard first!
|
||||
* make sure to configure it in the Stripe dashboard first!
|
||||
*/
|
||||
|
||||
// if (line_items?.data[0]?.price?.id === process.env.CREDITS_PRICE_ID) {
|
||||
@ -81,10 +82,47 @@ export const stripeWebhook: StripeWebhook = async (request, response, context) =
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
} else if (event.type === 'invoice.paid') {
|
||||
console.log('>>>> invoice.paid: ', userStripeId);
|
||||
const invoice = event.data.object as Stripe.Invoice;
|
||||
const periodStart = new Date(invoice.period_start * 1000);
|
||||
await context.entities.User.updateMany({
|
||||
where: {
|
||||
stripeId: userStripeId,
|
||||
},
|
||||
data: {
|
||||
hasPaid: true,
|
||||
datePaid: periodStart,
|
||||
},
|
||||
});
|
||||
} else if (event.type === 'customer.subscription.updated') {
|
||||
const subscription = event.data.object as Stripe.Subscription;
|
||||
userStripeId = subscription.customer as string;
|
||||
|
||||
if (subscription.status === 'active') {
|
||||
console.log('Subscription active ', userStripeId);
|
||||
await context.entities.User.updateMany({
|
||||
where: {
|
||||
stripeId: userStripeId,
|
||||
},
|
||||
data: {
|
||||
subscriptionStatus: 'active',
|
||||
},
|
||||
});
|
||||
}
|
||||
// you'll want to make a check on the front end to see if the subscription is past due
|
||||
// and then prompt the user to update their payment method
|
||||
// this is useful if the user's card expires or is canceled and automatic subscription renewal fails
|
||||
if (subscription.status === 'past_due') {
|
||||
console.log('Subscription past due: ', userStripeId);
|
||||
await context.entities.User.updateMany({
|
||||
where: {
|
||||
stripeId: userStripeId,
|
||||
},
|
||||
data: {
|
||||
subscriptionStatus: 'past_due',
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Stripe will send a subscription.updated event when a subscription is canceled
|
||||
* but the subscription is still active until the end of the period.
|
||||
|
Loading…
x
Reference in New Issue
Block a user