CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/hub/servers/app/webhooks/stripe.ts
Views: 687
1
/*
2
Stripe Webhook to handle some events:
3
4
- invoice.paid --
5
- customer.subscription.created -- used for the webhook side of @cocalc/server/purchases/stripe-usage-based-subscription.ts
6
7
We *do* check the stripe signature to only handle requests that actually come from stripe.
8
See https://stripe.com/docs/webhooks/signatures for where the code comes from.
9
10
To test this in dev you need to use the stripe cli, which is a big GO program.
11
For some reason the binary just hangs when trying to run it on cocalc.com (maybe
12
due to how locked down our Docker containers are?), so it also seems only
13
possible to test/debug this in cocalc-docker or somewhere else.
14
*/
15
16
import { Router } from "express";
17
import { getLogger } from "@cocalc/hub/logger";
18
import { getServerSettings } from "@cocalc/database/settings/server-settings";
19
import getConn from "@cocalc/server/stripe/connection";
20
import {
21
createCreditFromPaidStripeInvoice,
22
createCreditFromPaidStripePaymentIntent,
23
} from "@cocalc/server/purchases/create-invoice";
24
import * as express from "express";
25
import { isValidUUID } from "@cocalc/util/misc";
26
import { setUsageSubscription } from "@cocalc/server/purchases/stripe-usage-based-subscription";
27
28
const logger = getLogger("hub:stripe-webhook");
29
30
export default function init(router: Router) {
31
router.post(
32
"/webhooks/stripe",
33
express.raw({ type: "application/json" }),
34
async (req, res) => {
35
logger.debug("POST");
36
37
try {
38
await handleRequest(req);
39
} catch (err) {
40
const error = `Webhook Error: ${err.message}`;
41
logger.error(error);
42
console.error(error);
43
res.status(400).send(error);
44
return;
45
}
46
47
// Return a 200 response to acknowledge receipt of the event
48
res.send();
49
}
50
);
51
}
52
53
async function handleRequest(req) {
54
const { stripe_webhook_secret } = await getServerSettings();
55
const stripe = await getConn();
56
const sig = req.headers["stripe-signature"];
57
const event = stripe.webhooks.constructEvent(
58
req.body,
59
sig,
60
stripe_webhook_secret
61
);
62
logger.debug("event.type = ", event.type);
63
64
// Handle the event
65
switch (event.type) {
66
case "invoice.paid":
67
const invoice = event.data.object;
68
// Then define and call a function to handle the event invoice.paid
69
logger.debug("invoice = ", invoice);
70
await createCreditFromPaidStripeInvoice(invoice);
71
break;
72
73
case "payment_intent.succeeded":
74
// This is I think ONLY used for deprecated credit cards.
75
const intent = event.data.object;
76
logger.debug("intent = ", intent);
77
await createCreditFromPaidStripePaymentIntent(intent);
78
break;
79
80
case "customer.subscription.created":
81
logger.debug("event = ", event);
82
const { id, object } = (event.data?.object ?? {}) as any;
83
if (object == "subscription") {
84
const { account_id, service } =
85
(event.data?.object as any)?.metadata ?? {};
86
if (isValidUUID(account_id) && service == "credit") {
87
await setUsageSubscription({ account_id, subscription_id: id });
88
}
89
}
90
break;
91
92
default:
93
// we don't handle any other event types yet.
94
logger.debug(`Unhandled event type ${event.type}`);
95
// logger.debug(event);
96
}
97
}
98
99