diff --git a/.gitignore b/.gitignore
index 3372c3b8..a80850a3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
*.swp
_site
.DS_Store
+.env
+node_modules
diff --git a/_pages/pay.md b/_pages/pay.md
index 027c1d2a..4bc674b9 100644
--- a/_pages/pay.md
+++ b/_pages/pay.md
@@ -3,14 +3,9 @@
title: Pay MITOC
---
-
-
@@ -67,37 +47,21 @@ See the signup page [here](/join).
### Trip and Event Fees
@@ -136,68 +88,42 @@ Be sure to fill out the visitor log ([Camelot](https://docs.google.com/spreadshe
Days spent on-site but not in the cabin (e.g. Yurt / Lean-to / camping) cost 0.5 person-days (i.e. half price of using the cabin.) Enter the total number of fractional days spent outside and full days spent inside in the form below.
diff --git a/_pages/pay/success.md b/_pages/pay/success.md
new file mode 100644
index 00000000..99a44a26
--- /dev/null
+++ b/_pages/pay/success.md
@@ -0,0 +1,10 @@
+---
+permalink: /pay/success
+title: Payment Received
+---
+
+Your payment was successful. A receipt has been sent to your email.
+
+Questions? Contact [mitoc-bursar@mit.edu](mailto:mitoc-bursar@mit.edu).
+
+
Back to payments
diff --git a/docker-compose.yml b/docker-compose.yml
index 971c7a10..aeec6360 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -7,3 +7,22 @@ services:
- .:/site
ports:
- "4000:4000"
+
+ functions:
+ image: node:18-alpine
+ working_dir: /app
+ volumes:
+ - .:/app
+ command: node docker/functions-server.js
+ env_file:
+ - .env
+
+ proxy:
+ image: nginx:alpine
+ volumes:
+ - ./docker/nginx.conf:/etc/nginx/nginx.conf:ro
+ ports:
+ - "8888:8888"
+ depends_on:
+ - jekyll
+ - functions
diff --git a/docker/functions-server.js b/docker/functions-server.js
new file mode 100644
index 00000000..14badd43
--- /dev/null
+++ b/docker/functions-server.js
@@ -0,0 +1,26 @@
+const http = require('http');
+const handler = require('../netlify/functions/create-checkout-session');
+
+const server = http.createServer(async (req, res) => {
+ if (req.method !== 'POST') {
+ res.writeHead(405);
+ res.end('Method Not Allowed');
+ return;
+ }
+
+ let body = '';
+ req.on('data', chunk => body += chunk);
+ req.on('end', async () => {
+ const event = {
+ httpMethod: 'POST',
+ body,
+ headers: { origin: req.headers.origin || 'http://localhost:8888' },
+ };
+
+ const result = await handler.handler(event);
+ res.writeHead(result.statusCode, result.headers || {});
+ res.end(result.body);
+ });
+});
+
+server.listen(9999, () => console.log('Functions server on port 9999'));
diff --git a/docker/nginx.conf b/docker/nginx.conf
new file mode 100644
index 00000000..5c10568e
--- /dev/null
+++ b/docker/nginx.conf
@@ -0,0 +1,16 @@
+events {}
+
+http {
+ server {
+ listen 8888;
+
+ location /.netlify/functions/ {
+ proxy_pass http://functions:9999/;
+ proxy_set_header Origin http://localhost:8888;
+ }
+
+ location / {
+ proxy_pass http://jekyll:4000;
+ }
+ }
+}
diff --git a/js/mitoc.js b/js/mitoc.js
index 21c9712b..765b4254 100644
--- a/js/mitoc.js
+++ b/js/mitoc.js
@@ -1,30 +1,86 @@
----
----
-
-//Load trip fee schedule from Google Sheets on the payment page
-function load_trip_fees() {
- //Populate dropdown menu and list of prices
- var form = document.forms['trip_form'];
- var options = form.merchantDefinedData2.options;
- var prices = {
- {% for fee in site.data.trip_fees %}
- "{{fee.name}}": { "price": "{{ fee.price }}", "category": "{{ fee.category }}" },
- {% endfor %}
- };
- //Add handler to calculate price when quantity changes
- var calculate_amount = function() {
- if(form.merchantDefinedData2.value) {
- form.amount.value = form.merchantDefinedData4.value * prices[form.merchantDefinedData2.value].price;
- }
- };
- $('#merchantDefinedData4trip').keyup(calculate_amount);
- $('#merchantDefinedData4trip').change(calculate_amount);
-
- //Add handler to calculate price and populate category when trip selection changes
- $('#merchantDefinedData2trip').change(function() {
- if(form.merchantDefinedData2.value) {
- calculate_amount();
- form.merchantDefinedData1.value = prices[form.merchantDefinedData2.value].category;
- }
- });
-}
+---
+---
+
+function submit_payment(amount_dollars, description, metadata) {
+ var amount_cents = Math.round(parseFloat(amount_dollars) * 100);
+ if (!amount_cents || amount_cents < 50) {
+ alert('Please enter a valid amount (minimum $0.50).');
+ return;
+ }
+
+ fetch('/.netlify/functions/create-checkout-session', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ amount_cents: amount_cents, description: description, metadata: metadata }),
+ })
+ .then(function(res) { return res.json(); })
+ .then(function(data) {
+ if (data.url) {
+ window.location = data.url;
+ } else {
+ alert('Payment error. Please try again or contact mitoc-bursar@mit.edu.');
+ }
+ })
+ .catch(function() {
+ alert('Payment error. Please try again or contact mitoc-bursar@mit.edu.');
+ });
+}
+
+function init_payment_forms() {
+ var gear_form = document.getElementById('gear_form');
+ if (gear_form) {
+ gear_form.addEventListener('submit', function(e) {
+ e.preventDefault();
+ var type = gear_form.querySelector('[name="payment_type"]').value;
+ var renter = gear_form.querySelector('[name="renter_name"]').value;
+ var amount = gear_form.querySelector('[name="amount"]').value;
+ submit_payment(amount, 'Gear: ' + type, { payment_type: type, renter_name: renter });
+ });
+ }
+
+ var trip_form = document.getElementById('trip_form');
+ if (trip_form) {
+ var prices = {
+ {% for fee in site.data.trip_fees %}
+ "{{ fee.name }}": {{ fee.price }},
+ {% endfor %}
+ };
+
+ var calc_trip_amount = function() {
+ var trip = trip_form.querySelector('[name="trip_name"]').value;
+ var qty = parseInt(trip_form.querySelector('[name="quantity"]').value, 10) || 1;
+ trip_form.querySelector('[name="amount"]').value =
+ trip && prices[trip] ? (prices[trip] * qty).toFixed(2) : '0.00';
+ };
+
+ trip_form.querySelector('[name="trip_name"]').addEventListener('change', calc_trip_amount);
+ trip_form.querySelector('[name="quantity"]').addEventListener('change', calc_trip_amount);
+ trip_form.querySelector('[name="quantity"]').addEventListener('keyup', calc_trip_amount);
+
+ trip_form.addEventListener('submit', function(e) {
+ e.preventDefault();
+ var trip = trip_form.querySelector('[name="trip_name"]').value;
+ var qty = trip_form.querySelector('[name="quantity"]').value;
+ var comments = trip_form.querySelector('[name="comments"]').value;
+ var amount = trip_form.querySelector('[name="amount"]').value;
+ if (!trip) { alert('Please select a trip.'); return; }
+ submit_payment(amount, trip, { trip: trip, quantity: qty, comments: comments });
+ });
+ }
+
+ var cabin_form = document.getElementById('cabin_form');
+ if (cabin_form) {
+ cabin_form.addEventListener('submit', function(e) {
+ e.preventDefault();
+ var cabin = cabin_form.querySelector('[name="cabin"]').value;
+ var nights = cabin_form.querySelector('[name="person_nights"]').value;
+ var keyholder = cabin_form.querySelector('[name="keyholder_name"]').value;
+ var amount = cabin_form.querySelector('[name="amount"]').value;
+ submit_payment(
+ amount,
+ 'Cabin: ' + cabin + ' x ' + nights + ' person-nights',
+ { cabin: cabin, person_nights: nights, keyholder_name: keyholder }
+ );
+ });
+ }
+}
diff --git a/netlify.toml b/netlify.toml
index 6621d15f..5388575e 100644
--- a/netlify.toml
+++ b/netlify.toml
@@ -1,3 +1,6 @@
[build]
publish = "_site"
command = "jekyll build"
+
+[functions]
+ directory = "netlify/functions"
diff --git a/netlify/functions/create-checkout-session.js b/netlify/functions/create-checkout-session.js
new file mode 100644
index 00000000..764eb735
--- /dev/null
+++ b/netlify/functions/create-checkout-session.js
@@ -0,0 +1,50 @@
+const Stripe = require('stripe');
+
+exports.handler = async function (event) {
+ if (event.httpMethod !== 'POST') {
+ return { statusCode: 405, body: 'Method Not Allowed' };
+ }
+
+ let body;
+ try {
+ body = JSON.parse(event.body);
+ } catch {
+ return { statusCode: 400, body: 'Invalid request body' };
+ }
+
+ const { amount_cents, description, metadata } = body;
+
+ if (!Number.isInteger(amount_cents) || amount_cents < 50) {
+ return { statusCode: 400, body: 'amount_cents must be an integer >= 50' };
+ }
+
+ const stripe = Stripe(process.env.STRIPE_SECRET_KEY);
+ const origin = event.headers.origin || 'https://mitoc.mit.edu';
+
+ try {
+ const session = await stripe.checkout.sessions.create({
+ mode: 'payment',
+ line_items: [
+ {
+ price_data: {
+ currency: 'usd',
+ unit_amount: amount_cents,
+ product_data: { name: description },
+ },
+ quantity: 1,
+ },
+ ],
+ metadata: metadata || {},
+ success_url: `${origin}/pay/success`,
+ cancel_url: `${origin}/pay`,
+ });
+
+ return {
+ statusCode: 200,
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ url: session.url }),
+ };
+ } catch (err) {
+ return { statusCode: 500, body: err.message };
+ }
+};
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 00000000..ca5b3552
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,304 @@
+{
+ "name": "mitoc-website",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "mitoc-website",
+ "dependencies": {
+ "stripe": "^16.0.0"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "25.6.2",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.2.tgz",
+ "integrity": "sha512-sokuT28dxf9JT5Kady1fsXOvI4HVpjZa95NKT5y9PNTIrs2AsobR4GFAA90ZG8M+nxVRLysCXsVj6eGC7Vbrlw==",
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~7.19.0"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz",
+ "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==",
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/qs": {
+ "version": "6.15.1",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz",
+ "integrity": "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+ "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3",
+ "side-channel-list": "^1.0.0",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz",
+ "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/stripe": {
+ "version": "16.12.0",
+ "resolved": "https://registry.npmjs.org/stripe/-/stripe-16.12.0.tgz",
+ "integrity": "sha512-H7eFVLDxeTNNSn4JTRfL2//LzCbDrMSZ+2q1c7CanVWgK2qIW5TwS+0V7N9KcKZZNpYh/uCqK0PyZh/2UsaAtQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": ">=8.1.0",
+ "qs": "^6.11.0"
+ },
+ "engines": {
+ "node": ">=12.*"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "7.19.2",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz",
+ "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==",
+ "license": "MIT"
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..4247bbc6
--- /dev/null
+++ b/package.json
@@ -0,0 +1,7 @@
+{
+ "name": "mitoc-website",
+ "private": true,
+ "dependencies": {
+ "stripe": "^16.0.0"
+ }
+}