start working on integrating stripe
This commit is contained in:
parent
c9fac35ee3
commit
9fc109449d
|
@ -29,6 +29,7 @@
|
||||||
"pug": "^3.0.2",
|
"pug": "^3.0.2",
|
||||||
"redis": "^3.1.2",
|
"redis": "^3.1.2",
|
||||||
"session-file-store": "^1.5.0",
|
"session-file-store": "^1.5.0",
|
||||||
|
"stripe": "^10.13.0",
|
||||||
"uuid": "^8.3.2"
|
"uuid": "^8.3.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import authRouter from "./routers/auth-router";
|
||||||
import apiRouter from "./routers/api-router";
|
import apiRouter from "./routers/api-router";
|
||||||
import inProd from "./lib/inProd";
|
import inProd from "./lib/inProd";
|
||||||
import feedbackRouter from "./routers/feedback-router";
|
import feedbackRouter from "./routers/feedback-router";
|
||||||
|
import paymentRouter from "./routers/payment-router";
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
|
@ -29,7 +30,8 @@ setupPassport(passport);
|
||||||
app.use("/", authRouter(passport));
|
app.use("/", authRouter(passport));
|
||||||
// REST API - returning json
|
// REST API - returning json
|
||||||
app.use("/api", apiRouter);
|
app.use("/api", apiRouter);
|
||||||
app.use("/feedback", feedbackRouter)
|
app.use("/feedback", feedbackRouter);
|
||||||
|
app.use("/payment", paymentRouter);
|
||||||
|
|
||||||
// START 💨 //
|
// START 💨 //
|
||||||
app.listen(4000, () => console.log("Server Has Started on 4000"));
|
app.listen(4000, () => console.log("Server Has Started on 4000"));
|
||||||
|
|
|
@ -9,6 +9,7 @@ const names = [
|
||||||
"LINGDOCS_ACCOUNT_GITHUB_CLIENT_SECRET",
|
"LINGDOCS_ACCOUNT_GITHUB_CLIENT_SECRET",
|
||||||
"LINGDOCS_ACCOUNT_RECAPTCHA_SECRET",
|
"LINGDOCS_ACCOUNT_RECAPTCHA_SECRET",
|
||||||
"LINGDOCS_ACCOUNT_UPGRADE_PASSWORD",
|
"LINGDOCS_ACCOUNT_UPGRADE_PASSWORD",
|
||||||
|
"STRIPE_SECRET_KEY",
|
||||||
];
|
];
|
||||||
|
|
||||||
const values = names.map((name) => ({
|
const values = names.map((name) => ({
|
||||||
|
@ -33,4 +34,5 @@ export default {
|
||||||
githubClientSecret: values[7].value,
|
githubClientSecret: values[7].value,
|
||||||
recaptchaSecret: values[8].value,
|
recaptchaSecret: values[8].value,
|
||||||
upgradePassword: values[9].value,
|
upgradePassword: values[9].value,
|
||||||
|
stripeSecretKey: values[10].value,
|
||||||
};
|
};
|
||||||
|
|
|
@ -300,7 +300,10 @@ const authRouter = (passport: PassportStatic) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post("/sign-out", (req, res) => {
|
router.post("/sign-out", (req, res) => {
|
||||||
req.logOut();
|
req.logOut((err) => {
|
||||||
|
console.error("error logging out");
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
res.redirect("/");
|
res.redirect("/");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,125 @@
|
||||||
|
import express from "express";
|
||||||
|
import * as T from "../../../website/src/types/account-types";
|
||||||
|
import env from "../lib/env-vars";
|
||||||
|
import Stripe from "stripe";
|
||||||
|
|
||||||
|
const stripe = new Stripe(env.stripeSecretKey, {
|
||||||
|
apiVersion: "2022-08-01",
|
||||||
|
});
|
||||||
|
|
||||||
|
const paymentRouter = express.Router();
|
||||||
|
|
||||||
|
// Guard all api with authentication
|
||||||
|
paymentRouter.use((req, res, next) => {
|
||||||
|
if (req.isAuthenticated()) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
const r: T.APIResponse = { ok: false, error: "401 Unauthorized" };
|
||||||
|
return res.status(401).send(r);
|
||||||
|
});
|
||||||
|
|
||||||
|
paymentRouter.post("/create-checkout-session", async (req, res, next) => {
|
||||||
|
const prices = await stripe.prices.list({
|
||||||
|
lookup_keys: [req.body.lookup_key],
|
||||||
|
expand: ['data.product'],
|
||||||
|
});
|
||||||
|
const session = await stripe.checkout.sessions.create({
|
||||||
|
billing_address_collection: 'auto',
|
||||||
|
line_items: [
|
||||||
|
{
|
||||||
|
price: prices.data[0].id,
|
||||||
|
// For metered billing, do not pass quantity
|
||||||
|
quantity: 1,
|
||||||
|
|
||||||
|
},
|
||||||
|
],
|
||||||
|
mode: 'subscription',
|
||||||
|
success_url: `/success`,
|
||||||
|
cancel_url: `/cancel`,
|
||||||
|
});
|
||||||
|
if (!session.url) {
|
||||||
|
return next("error creating session url");
|
||||||
|
}
|
||||||
|
res.redirect(303, session.url);
|
||||||
|
});
|
||||||
|
|
||||||
|
paymentRouter.post('/create-portal-session', async (req, res, next) => {
|
||||||
|
if (!req.user) {
|
||||||
|
return next("error finding user");
|
||||||
|
}
|
||||||
|
const portalSession = await stripe.billingPortal.sessions.create({
|
||||||
|
customer: req.user.userId,
|
||||||
|
return_url: "/",
|
||||||
|
});
|
||||||
|
|
||||||
|
res.redirect(303, portalSession.url);
|
||||||
|
});
|
||||||
|
|
||||||
|
paymentRouter.post(
|
||||||
|
'/webhook',
|
||||||
|
express.raw({ type: 'application/json' }),
|
||||||
|
(request, response) => {
|
||||||
|
let event = request.body;
|
||||||
|
// Replace this endpoint secret with your endpoint's unique secret
|
||||||
|
// If you are testing with the CLI, find the secret by running 'stripe listen'
|
||||||
|
// If you are using an endpoint defined with the API or dashboard, look in your webhook settings
|
||||||
|
// at https://dashboard.stripe.com/webhooks
|
||||||
|
const endpointSecret = 'whsec_12345';
|
||||||
|
// Only verify the event if you have an endpoint secret defined.
|
||||||
|
// Otherwise use the basic event deserialized with JSON.parse
|
||||||
|
if (endpointSecret) {
|
||||||
|
// Get the signature sent by Stripe
|
||||||
|
const signature = request.headers['stripe-signature'] || "";
|
||||||
|
try {
|
||||||
|
event = stripe.webhooks.constructEvent(
|
||||||
|
request.body,
|
||||||
|
signature,
|
||||||
|
endpointSecret
|
||||||
|
);
|
||||||
|
} catch (err: any) {
|
||||||
|
console.log(`⚠️ Webhook signature verification failed.`, err.message);
|
||||||
|
return response.sendStatus(400);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let subscription;
|
||||||
|
let status;
|
||||||
|
// Handle the event
|
||||||
|
switch (event.type) {
|
||||||
|
case 'customer.subscription.trial_will_end':
|
||||||
|
subscription = event.data.object;
|
||||||
|
status = subscription.status;
|
||||||
|
console.log(`Subscription status is ${status}.`);
|
||||||
|
// Then define and call a method to handle the subscription trial ending.
|
||||||
|
// handleSubscriptionTrialEnding(subscription);
|
||||||
|
break;
|
||||||
|
case 'customer.subscription.deleted':
|
||||||
|
subscription = event.data.object;
|
||||||
|
status = subscription.status;
|
||||||
|
console.log(`Subscription status is ${status}.`);
|
||||||
|
// Then define and call a method to handle the subscription deleted.
|
||||||
|
// handleSubscriptionDeleted(subscriptionDeleted);
|
||||||
|
break;
|
||||||
|
case 'customer.subscription.created':
|
||||||
|
subscription = event.data.object;
|
||||||
|
status = subscription.status;
|
||||||
|
console.log(`Subscription status is ${status}.`);
|
||||||
|
// Then define and call a method to handle the subscription created.
|
||||||
|
// handleSubscriptionCreated(subscription);
|
||||||
|
break;
|
||||||
|
case 'customer.subscription.updated':
|
||||||
|
subscription = event.data.object;
|
||||||
|
status = subscription.status;
|
||||||
|
console.log(`Subscription status is ${status}.`);
|
||||||
|
// Then define and call a method to handle the subscription update.
|
||||||
|
// handleSubscriptionUpdated(subscription);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Unexpected event type
|
||||||
|
console.log(`Unhandled event type ${event.type}.`);
|
||||||
|
}
|
||||||
|
// Return a 200 response to acknowledge receipt of the event
|
||||||
|
response.send();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export default paymentRouter;
|
|
@ -26,6 +26,11 @@
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1 class="my-4">LingDocs Auth Admin</h1>
|
<h1 class="my-4">LingDocs Auth Admin</h1>
|
||||||
|
<form action="/payment/create-checkout-session" method="POST">
|
||||||
|
<!-- Add a hidden field with the lookup_key of your Price -->
|
||||||
|
<input type="hidden" name="lookup_key" value="student-yearly" />
|
||||||
|
<button id="checkout-and-portal-button" type="submit">Checkout</button>
|
||||||
|
</form>
|
||||||
<p><%= users.length %> Users</p>
|
<p><%= users.length %> Users</p>
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue