Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 1 addition & 15 deletions functions/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions functions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@
"main": "lib/index.js",
"dependencies": {
"firebase-admin": "^9.11.1",
"firebase-functions": "^3.19.0",
"stripe": "^8.209.0"
"firebase-functions": "^3.19.0"
},
"devDependencies": {
"firebase-functions-test": "^0.3.3",
Expand Down
101 changes: 0 additions & 101 deletions functions/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as admin from "firebase-admin";
import * as functions from "firebase-functions";
import Stripe from "stripe";

import { GameMode, findSet, generateSeed, modes, replayEvents } from "./game";

Expand All @@ -17,12 +16,6 @@ if (process.env.FUNCTIONS_EMULATOR) {
admin.initializeApp();
}

const stripe = !functions.config().stripe
? null
: new Stripe(functions.config().stripe.secret, {
apiVersion: "2020-08-27",
});

const MAX_GAME_ID_LENGTH = 64;
const MAX_UNFINISHED_GAMES_PER_HOUR = 4;

Expand Down Expand Up @@ -347,45 +340,6 @@ export const createGame = functions.https.onCall(async (data, context) => {
return snapshot.val();
});

/** Generate a link to the customer portal. */
export const customerPortal = functions.https.onCall(async (data, context) => {
if (!stripe) {
throw new functions.https.HttpsError(
"failed-precondition",
"Stripe is not supported."
);
}

if (!context.auth) {
throw new functions.https.HttpsError(
"failed-precondition",
"This function must be called while authenticated."
);
}

const user = await admin.auth().getUser(context.auth.uid);
if (!user.email) {
throw new functions.https.HttpsError(
"failed-precondition",
"This function must be called by an authenticated user with email."
);
}

const customerResponse = await stripe.customers.list({ email: user.email });
if (!customerResponse.data.length) {
throw new functions.https.HttpsError(
"not-found",
`A subscription with email ${user.email} was not found.`
);
}

const portalResponse = await stripe.billingPortal.sessions.create({
customer: customerResponse.data[0].id,
return_url: data.returnUrl,
});
return portalResponse.url;
});

/** Periodically remove stale user connections */
export const clearConnections = functions.pubsub
.schedule("every 1 minutes")
Expand All @@ -408,58 +362,3 @@ export const clearConnections = functions.pubsub
actions.push(admin.database().ref("stats/onlineUsers").set(numUsers));
await Promise.all(actions);
});

/** Webhook that handles Stripe customer events. */
export const handleStripe = functions.https.onRequest(async (req, res) => {
if (!stripe) {
res.status(400).send("Stripe is not supported");
return;
}

const payload = req.rawBody;
const sig = req.get("stripe-signature");

if (!sig) {
res.status(400).send("Webhook Error: Missing stripe-signature");
return;
}

const { endpoint_secret } = functions.config().stripe;
let event;
try {
event = stripe.webhooks.constructEvent(payload, sig, endpoint_secret);
} catch (error: any) {
res.status(400).send(`Webhook Error: ${error.message}`);
return;
}

console.log(`Received ${event.type}: ${JSON.stringify(event.data)}`);
if (
event.type === "customer.subscription.created" ||
event.type === "customer.subscription.deleted"
) {
const subscription = event.data.object as Stripe.Subscription;
const { email } = (await stripe.customers.retrieve(
subscription.customer as string
)) as Stripe.Response<Stripe.Customer>;

if (email) {
const user = await admin
.auth()
.getUserByEmail(email)
.catch(() => null);

if (user) {
const newState = event.type === "customer.subscription.created";
await admin.database().ref(`users/${user.uid}/patron`).set(newState);
console.log(`Processed ${email} (${user.uid}): newState = ${newState}`);
} else {
console.log(`Failed to find user: ${email}`);
}
} else {
console.log("Subscription event received with no email, ignoring");
}
}

res.status(200).end();
});
7 changes: 0 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
"@material-ui/core": "^4.12.3",
"@material-ui/icons": "^4.11.2",
"@react-spring/web": "^9.7.5",
"@stripe/stripe-js": "^1.25.0",
"chart.js": "^3.7.1",
"clsx": "^1.1.1",
"date-fns": "^4.1.0",
Expand Down
7 changes: 0 additions & 7 deletions src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ const config = {
projectId: "setwithforks-dev",
appId: "1:488130851214:web:e656dc30f31e99e160b4df",
},
stripe: null, // Stripe not supported in development
},
staging: {
firebase: {
Expand All @@ -22,7 +21,6 @@ const config = {
appId: "1:488130851214:web:e656dc30f31e99e160b4df",
measurementId: "G-LQR3228JDG",
},
stripe: null, // No Stripe yet
},
production: {
firebase: {
Expand All @@ -35,11 +33,6 @@ const config = {
appId: "1:970544876139:web:06295fe4079007f76abf2e",
measurementId: "G-QDX193SN7R",
},
stripe: {
publishableKey:
"pk_live_51I0VxyCWK9K42cLJX34X6lIqsuZSWQX6I8WuOgmvEGANYlNyCsZDl2MmWGXQhuM5QnVciouCiYZ9lWq5Ope68aSj00bllKdnRr",
priceId: "price_1I2QWcCWK9K42cLJFp2sUkSh",
},
},
};

Expand Down
25 changes: 0 additions & 25 deletions src/stripe.js

This file was deleted.