Processing Stripe Dance Payments

This commit is contained in:
metacryst
2025-12-07 22:01:37 -06:00
parent 9a1aa55297
commit 279be987a4
11 changed files with 152 additions and 420 deletions

10
.gitignore vendored
View File

@@ -1,9 +1,7 @@
.DS_Store
package-lock.json
node_modules
_/build
content
server/.env
abetaccredited.html
qrCodes/qr_codes
.env
.env
db/db.json
qrCodes/qr_codes

View File

@@ -1,389 +0,0 @@
{
"nodes": {
"HY-1": {
"fullName": "Founder"
},
"MEMBER-1": {
"id": 1,
"email": "samrussell99@pm.me",
"firstName": "Sam",
"lastName": "Russell",
"password": "$argon2id$v=19$m=65536,t=3,p=4$PDu5qx47zzY0dX0/4iQuqw$JxePLouwOEi2Q0wA4JHQQ4GwvqAvxqWhj7n9aC8HZIk",
"address": {
"address1": "2014 E 9th St",
"address2": "Unit A",
"zip": "78702",
"state": "Texas",
"city": "Austin"
},
"tokenUsed": "810387b6-851e-4883-b9a3-c59703dc0fc9",
"joined": "11.24.2025-12:54:360784am"
},
"MEMBER-2": {
"id": 2,
"email": "nate@sun.museum",
"firstName": "Nate",
"lastName": "De Luna",
"password": "$argon2id$v=19$m=65536,t=3,p=4$laoMb54lVHlXD+VsWe2lNg$YSNqIk/MC32jUc+Hj48LBTSul3+WXQAyfYasfH6L0k4",
"address": {
"address1": "2014 E 9th St",
"address2": "Unit A",
"zip": "78702",
"state": "Texas",
"city": "Austin"
},
"tokenUsed": "1d57b6e0-e6c3-4ab6-808f-72b64b8303ee",
"joined": "11.25.2025-6:34:040998pm"
},
"TOKEN-810387b6-851e-4883-b9a3-c59703dc0fc9": {
"index": 1,
"url": "https://hyperia.so/signup?token=",
"uuid": "810387b6-851e-4883-b9a3-c59703dc0fc9",
"used": true
},
"TOKEN-d0b417c4-d69a-4958-9e1d-0658176d2710": {
"index": 2,
"url": "https://hyperia.so/signup?token=",
"uuid": "d0b417c4-d69a-4958-9e1d-0658176d2710",
"used": false
},
"TOKEN-1719b6d8-9b13-4720-ab94-9af5cbe42137": {
"index": 3,
"url": "https://hyperia.so/signup?token=",
"uuid": "1719b6d8-9b13-4720-ab94-9af5cbe42137",
"used": false
},
"TOKEN-16659e04-afbb-4192-9195-90f2f49277b2": {
"index": 4,
"url": "https://hyperia.so/signup?token=",
"uuid": "16659e04-afbb-4192-9195-90f2f49277b2",
"used": false
},
"TOKEN-88abc4b3-8416-4499-9fa7-6778841e4b23": {
"index": 5,
"url": "https://hyperia.so/signup?token=",
"uuid": "88abc4b3-8416-4499-9fa7-6778841e4b23",
"used": false
},
"TOKEN-e3342dd5-a214-4280-9488-f48f91e728ce": {
"index": 6,
"url": "https://hyperia.so/signup?token=",
"uuid": "e3342dd5-a214-4280-9488-f48f91e728ce",
"used": false
},
"TOKEN-a8176432-4e72-42b7-ac9a-2d799039f729": {
"index": 7,
"url": "https://hyperia.so/signup?token=",
"uuid": "a8176432-4e72-42b7-ac9a-2d799039f729",
"used": false
},
"TOKEN-1d57b6e0-e6c3-4ab6-808f-72b64b8303ee": {
"index": 8,
"url": "https://hyperia.so/signup?token=",
"uuid": "1d57b6e0-e6c3-4ab6-808f-72b64b8303ee",
"used": true
},
"TOKEN-67264f06-c4b1-4334-8ba8-0e5e045021df": {
"index": 9,
"url": "https://hyperia.so/signup?token=",
"uuid": "67264f06-c4b1-4334-8ba8-0e5e045021df",
"used": false
},
"TOKEN-e9fa5701-d90f-45d0-8e2b-7c3ef9022362": {
"index": 10,
"url": "https://hyperia.so/signup?token=",
"uuid": "e9fa5701-d90f-45d0-8e2b-7c3ef9022362",
"used": false
},
"TOKEN-c082880a-f12c-4eb0-b136-e11df9e6bde8": {
"index": 11,
"url": "https://hyperia.so/signup?token=",
"uuid": "c082880a-f12c-4eb0-b136-e11df9e6bde8",
"used": false
},
"TOKEN-083a18f5-6491-4dbf-9a28-2b22b6b4a5bd": {
"index": 12,
"url": "https://hyperia.so/signup?token=",
"uuid": "083a18f5-6491-4dbf-9a28-2b22b6b4a5bd",
"used": false
},
"TOKEN-af94443c-a0c5-4c58-a2e5-16a8c6d573c8": {
"index": 13,
"url": "https://hyperia.so/signup?token=",
"uuid": "af94443c-a0c5-4c58-a2e5-16a8c6d573c8",
"used": false
},
"TOKEN-5673dd07-25b1-441f-abbc-89b5e5a2b3e9": {
"index": 14,
"url": "https://hyperia.so/signup?token=",
"uuid": "5673dd07-25b1-441f-abbc-89b5e5a2b3e9",
"used": false
},
"TOKEN-f2b30979-1ece-41e1-87ec-6a102a21e809": {
"index": 15,
"url": "https://hyperia.so/signup?token=",
"uuid": "f2b30979-1ece-41e1-87ec-6a102a21e809",
"used": false
},
"TOKEN-d52c1bcc-8b97-4535-8395-dfdab4ee543a": {
"index": 16,
"url": "https://hyperia.so/signup?token=",
"uuid": "d52c1bcc-8b97-4535-8395-dfdab4ee543a",
"used": false
},
"TOKEN-7fed8604-1801-4500-8615-2ac8eb50c896": {
"index": 17,
"url": "https://hyperia.so/signup?token=",
"uuid": "7fed8604-1801-4500-8615-2ac8eb50c896",
"used": false
},
"TOKEN-e312c854-8df7-4c68-9f3f-1822b718b092": {
"index": 18,
"url": "https://hyperia.so/signup?token=",
"uuid": "e312c854-8df7-4c68-9f3f-1822b718b092",
"used": false
},
"TOKEN-5979853c-1755-4403-bea5-3b111384ffb3": {
"index": 19,
"url": "https://hyperia.so/signup?token=",
"uuid": "5979853c-1755-4403-bea5-3b111384ffb3",
"used": false
},
"TOKEN-d93307e4-a0a0-4b4a-86d0-897ca5382cdd": {
"index": 20,
"url": "https://hyperia.so/signup?token=",
"uuid": "d93307e4-a0a0-4b4a-86d0-897ca5382cdd",
"used": false
},
"TOKEN-b73b803a-c49d-4ea9-b8ed-f04be0373157": {
"index": 21,
"url": "https://hyperia.so/signup?token=",
"uuid": "b73b803a-c49d-4ea9-b8ed-f04be0373157",
"used": false
},
"TOKEN-5a0a7a8c-f080-4708-a2e1-fcd2d613adb1": {
"index": 22,
"url": "https://hyperia.so/signup?token=",
"uuid": "5a0a7a8c-f080-4708-a2e1-fcd2d613adb1",
"used": false
},
"TOKEN-99c547fe-1ec1-4ddc-bfcb-b95d73fdd907": {
"index": 23,
"url": "https://hyperia.so/signup?token=",
"uuid": "99c547fe-1ec1-4ddc-bfcb-b95d73fdd907",
"used": false
},
"TOKEN-6da35b58-81ea-478f-9ef5-8a70b407bfcf": {
"index": 24,
"url": "https://hyperia.so/signup?token=",
"uuid": "6da35b58-81ea-478f-9ef5-8a70b407bfcf",
"used": false
},
"TOKEN-402f4f83-165b-4b5a-b1f2-3dccc7ec5ef6": {
"index": 25,
"url": "https://hyperia.so/signup?token=",
"uuid": "402f4f83-165b-4b5a-b1f2-3dccc7ec5ef6",
"used": false
},
"TOKEN-04afd891-1250-4a6d-b239-3bdd06082c71": {
"index": 26,
"url": "https://hyperia.so/signup?token=",
"uuid": "04afd891-1250-4a6d-b239-3bdd06082c71",
"used": false
},
"TOKEN-c807c67f-2d8d-4f54-b657-8cb3aaf19b28": {
"index": 27,
"url": "https://hyperia.so/signup?token=",
"uuid": "c807c67f-2d8d-4f54-b657-8cb3aaf19b28",
"used": false
},
"TOKEN-5cf7639e-22b3-4469-8155-d490cc164367": {
"index": 28,
"url": "https://hyperia.so/signup?token=",
"uuid": "5cf7639e-22b3-4469-8155-d490cc164367",
"used": false
},
"TOKEN-90e82173-c7f8-47d9-9619-b333e032d1af": {
"index": 29,
"url": "https://hyperia.so/signup?token=",
"uuid": "90e82173-c7f8-47d9-9619-b333e032d1af",
"used": false
},
"TOKEN-5eed9d33-81ea-420f-834b-a71b01eb6f54": {
"index": 30,
"url": "https://hyperia.so/signup?token=",
"uuid": "5eed9d33-81ea-420f-834b-a71b01eb6f54",
"used": false
},
"TOKEN-2e96ecaa-bf8e-403c-89f3-8b2924788df3": {
"index": 31,
"url": "https://hyperia.so/signup?token=",
"uuid": "2e96ecaa-bf8e-403c-89f3-8b2924788df3",
"used": false
},
"TOKEN-7edb52b1-35ce-42c6-8cd0-341f3ee38f4c": {
"index": 32,
"url": "https://hyperia.so/signup?token=",
"uuid": "7edb52b1-35ce-42c6-8cd0-341f3ee38f4c",
"used": false
},
"TOKEN-ad5f483d-338f-4efc-936d-8253854b38a5": {
"index": 33,
"url": "https://hyperia.so/signup?token=",
"uuid": "ad5f483d-338f-4efc-936d-8253854b38a5",
"used": false
},
"TOKEN-d49a3c80-d0b8-4796-b1e2-f42d5ae0cc69": {
"index": 34,
"url": "https://hyperia.so/signup?token=",
"uuid": "d49a3c80-d0b8-4796-b1e2-f42d5ae0cc69",
"used": false
},
"TOKEN-382a7ffd-9227-4edc-8bc6-8c3ae82ccf14": {
"index": 35,
"url": "https://hyperia.so/signup?token=",
"uuid": "382a7ffd-9227-4edc-8bc6-8c3ae82ccf14",
"used": false
},
"TOKEN-27762835-f211-4c72-8187-b3f87ccb846c": {
"index": 36,
"url": "https://hyperia.so/signup?token=",
"uuid": "27762835-f211-4c72-8187-b3f87ccb846c",
"used": false
},
"TOKEN-7344f7ce-2609-46f4-b2cb-28dafc547270": {
"index": 37,
"url": "https://hyperia.so/signup?token=",
"uuid": "7344f7ce-2609-46f4-b2cb-28dafc547270",
"used": false
},
"TOKEN-fd23ce26-d78a-49fc-b198-1a73071a2db4": {
"index": 38,
"url": "https://hyperia.so/signup?token=",
"uuid": "fd23ce26-d78a-49fc-b198-1a73071a2db4",
"used": false
},
"TOKEN-869227be-1c80-421f-a17e-e052929c43e3": {
"index": 39,
"url": "https://hyperia.so/signup?token=",
"uuid": "869227be-1c80-421f-a17e-e052929c43e3",
"used": false
},
"TOKEN-3ac9b048-a132-4fad-bb2d-9bb1820592c8": {
"index": 40,
"url": "https://hyperia.so/signup?token=",
"uuid": "3ac9b048-a132-4fad-bb2d-9bb1820592c8",
"used": false
},
"TOKEN-7d9c71ac-646b-4a6b-b758-f55f632cec6f": {
"index": 41,
"url": "https://hyperia.so/signup?token=",
"uuid": "7d9c71ac-646b-4a6b-b758-f55f632cec6f",
"used": false
},
"TOKEN-6ad3a682-14fa-48d7-96bd-fd61b2b314dd": {
"index": 42,
"url": "https://hyperia.so/signup?token=",
"uuid": "6ad3a682-14fa-48d7-96bd-fd61b2b314dd",
"used": false
},
"TOKEN-c02ae012-089a-4c3b-bd28-8d7cedc2df0d": {
"index": 43,
"url": "https://hyperia.so/signup?token=",
"uuid": "c02ae012-089a-4c3b-bd28-8d7cedc2df0d",
"used": false
},
"TOKEN-df82c2b6-bd8c-4b69-bc8c-a1d7df53727c": {
"index": 44,
"url": "https://hyperia.so/signup?token=",
"uuid": "df82c2b6-bd8c-4b69-bc8c-a1d7df53727c",
"used": false
},
"TOKEN-1444c46c-4985-4e52-8cbf-f6d77795deae": {
"index": 45,
"url": "https://hyperia.so/signup?token=",
"uuid": "1444c46c-4985-4e52-8cbf-f6d77795deae",
"used": false
},
"TOKEN-38954160-0865-4f55-b190-e2a5a2268df1": {
"index": 46,
"url": "https://hyperia.so/signup?token=",
"uuid": "38954160-0865-4f55-b190-e2a5a2268df1",
"used": false
},
"TOKEN-0e88166f-936a-4156-a89d-e71e825db36e": {
"index": 47,
"url": "https://hyperia.so/signup?token=",
"uuid": "0e88166f-936a-4156-a89d-e71e825db36e",
"used": false
},
"TOKEN-a94aee8f-c47d-4606-a734-69355dfc642e": {
"index": 48,
"url": "https://hyperia.so/signup?token=",
"uuid": "a94aee8f-c47d-4606-a734-69355dfc642e",
"used": false
},
"TOKEN-de407269-9e31-485b-a857-5ba5d1a5ca5b": {
"index": 49,
"url": "https://hyperia.so/signup?token=",
"uuid": "de407269-9e31-485b-a857-5ba5d1a5ca5b",
"used": false
},
"TOKEN-f2016b40-a179-47ec-a415-84caa7da8521": {
"index": 50,
"url": "https://hyperia.so/signup?token=",
"uuid": "f2016b40-a179-47ec-a415-84caa7da8521",
"used": false
},
"POST-HY-1": {
"text": "This is the first message ever.",
"time": "11.24.2025-12:54:360784am",
"sentBy": "MEMBER-1"
},
"POST-HY-2": {
"text": "asdf",
"sentBy": "MEMBER-1",
"time": "11.25.2025-5:36:510165pm"
},
"POST-HY-3": {
"text": "Hey guys!",
"sentBy": "MEMBER-2",
"time": "11.25.2025-6:35:420148pm"
},
"POST-HY-4": {
"text": "(I am not real)",
"sentBy": "MEMBER-2",
"time": "11.25.2025-6:43:280160pm"
},
"CONVERSATION-1": {
"id": 1,
"between": [
"MEMBER-1",
"MEMBER-2"
],
"lastUpdated": "11.25.2025-6:43:280160pm"
},
"DM-1": {
"id": 1,
"conversation": "CONVERSATION-1",
"from": "MEMBER-1",
"text": "What's up?",
"time": "11.25.2025-6:43:280160pm"
},
"DM-2": {
"time": "11.26.2025-12:12:590659am",
"from": "MEMBER-2",
"conversation": "CONVERSATION-1",
"text": "Nothin much, you?",
"id": 2
},
"DM-3": {
"time": "11.26.2025-12:13:180437am",
"from": "MEMBER-1",
"conversation": "CONVERSATION-1",
"text": "Just grindin it out",
"id": 3
}
},
"edges": {}
}

View File

@@ -15,6 +15,7 @@
"express-useragent": "^2.0.2",
"jsonwebtoken": "^9.0.2",
"moment": "^2.30.1",
"stripe": "^20.0.0",
"ws": "^8.18.3",
"zod": "^4.1.12"
},

View File

@@ -5,6 +5,7 @@ import QuillDB from "../_/quilldb.js"
import Titles from "./model/Titles.js"
import Members from './model/Members.js'
import Tokens from './model/Tokens.js'
import Payments from "./model/Payments.js"
import Posts from "./model/Forum/Posts.js"
import Conversations from "./model/Messages/Conversations.js"
import Messages from "./model/Messages/Messages.js"
@@ -13,6 +14,7 @@ export default class Database {
titles = new Titles()
members = new Members()
tokens = new Tokens()
payments = new Payments()
posts = new Posts()
conversations = new Conversations()
messages = new Messages()
@@ -21,6 +23,7 @@ export default class Database {
"HY": this.titles,
"MEMBER": this.members,
"TOKEN": this.tokens,
"PAYMENT": this.payments,
"POST": this.posts,
"CONVERSATION": this.conversations,
"DM": this.messages
@@ -79,8 +82,8 @@ export default class Database {
this.tokens.entries,
this.posts.entries,
this.conversations.entries,
this.messages.entries
this.messages.entries,
this.payments.entries,
]
let ids = [
Object.entries(this.titles.ids),
@@ -89,7 +92,7 @@ export default class Database {
Object.entries(this.posts.ids),
Object.entries(this.conversations.ids),
Object.entries(this.messages.ids),
Object.entries(this.payments.ids),
]
for(let i=0; i<arrs.length; i++) {
let arr = arrs[i]

View File

@@ -0,0 +1,42 @@
import OrderedObject from "./OrderedObject.js"
const { z } = require("zod")
export default class Payments extends OrderedObject {
schema = z.object({
id: z.number(),
name: z.string(),
email: z.string(),
time: z.string(),
amount: z.number(),
product: z.string(),
})
save(payment) {
let id = `PAYMENT-${payment.id}`
let result = this.schema.safeParse(payment)
if(result.success) {
try {
super.add(id, payment)
} catch(e) {
console.error(e)
throw e
}
} else {
console.error(result.error)
throw new global.ServerError(400, "Invalid Member Data!");
}
}
add(paymentObj) {
let toSave = {
id: this.entries.length+1,
...paymentObj
}
this.save(toSave)
}
get(id) {
return this.entries[this.ids[`PAYMENT-${id}`]]
}
}

View File

@@ -12,6 +12,7 @@ import "./util.js"
import Socket from './ws/ws.js'
import Database from "./db/db.js"
import AuthHandler from './auth.js';
import PaymentsHandler from "./payments.js"
class Server {
db;
@@ -20,10 +21,16 @@ class Server {
DBPath = path.join(__dirname, './db')
registerRoutes(router) {
// router.post('/api/location', handlers.updateLocation)
/* Stripe */
router.post("/create-checkout-session", PaymentsHandler.danceTicket)
router.post("/webhook", express.raw({ type: "application/json" }), PaymentsHandler.webhook)
/* Auth */
router.post('/login', this.auth.login)
router.get('/profile', this.auth.getProfile)
router.get('/signout', this.auth.logout)
/* Site */
router.get('/signup', this.verifyToken, this.get)
router.post('/signup', this.verifyToken, this.newUserSubmission)
router.get('/db/images/*', this.getUserImage)
@@ -162,6 +169,7 @@ class Server {
global.db = this.db
this.auth = new AuthHandler()
const app = express();
app.post("/webhook", express.raw({ type: "application/json" }), PaymentsHandler.webhook)
app.use(cors({ origin: '*' }));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

68
server/payments.js Normal file
View File

@@ -0,0 +1,68 @@
const Stripe = require("stripe")
const dotenv = require("dotenv")
dotenv.config();
const stripe = new Stripe(process.env.STRIPE_SECRET);
export default class PaymentsHandler {
static async danceTicket(req, res) {
try {
const session = await stripe.checkout.sessions.create({
mode: "payment",
payment_method_types: ["card"],
metadata: {
productId: "austin_winter_ball_2025_ticket"
},
line_items: [
{
price_data: {
currency: "usd",
product_data: {
name: "Hyperia Winter Ball"
},
unit_amount: 3500
},
quantity: 1
}
],
success_url: `${process.env.PROTOCOL + process.env.BASE_URL}/success`,
cancel_url: `${process.env.PROTOCOL + process.env.BASE_URL}/events`
});
res.json({ url: session.url });
} catch (err) {
console.error(err);
res.status(500).json({ error: "Something went wrong." });
}
}
static webhook = (req, res) => {
const sig = req.headers["stripe-signature"];
try {
const event = stripe.webhooks.constructEvent(
req.body,
sig,
process.env.WEBHOOK_SECRET
);
if (event.type === "checkout.session.completed") {
const session = event.data.object;
let toStore = {
"product": session.metadata.productId,
"email": session.customer_details.email,
"name": session.customer_details.name,
"time": global.currentTime(),
"amount": session.amount_total,
}
global.db.payments.add(toStore)
}
res.sendStatus(200);
} catch (err) {
console.error(err);
res.sendStatus(400);
}
}
}

View File

@@ -51,8 +51,11 @@ class Events extends Shadow {
.border("1px solid #ab2f007d")
.background('var(--green)')
.marginLeft("auto")
.onClick(() => {
window.open("https://app.fluidpay.com/spp/hyperia-winter-ball?add_products=d4o523v0i47c3987j320:1:35.00", "_blank");
.onClick(async function() {
this.innerText = "Loading..."
const res = await fetch("/create-checkout-session", { method: "POST" });
const data = await res.json();
window.location = data.url;
})
})
.gap(3, em)

View File

@@ -4,6 +4,7 @@ import "./Why.js"
import "./Events.js"
import "./Join.js"
import "./SignIn.js"
import "./Success.js"
class Home extends Shadow {
render() {
@@ -38,7 +39,7 @@ class Home extends Shadow {
.maxWidth(isMobile() ? 0.8 : 100, em)
if(!isMobile()) {
let text = "A Classical Christian Association"
let text = "A Classical Christian Network"
p(isMobile() ? text : text.toUpperCase())
.x(50, vw).yBottom(isMobile() ? 1 : 3, vh)
.center()
@@ -57,6 +58,9 @@ class Home extends Shadow {
case "/join":
Join()
break;
case "/success":
Success()
break;
default:
if(window.location.pathname.startsWith("/signup")) {

View File

@@ -0,0 +1,9 @@
class Success extends Shadow {
render() {
p("Thanks for your purchase! You will receive a confirmation email shortly. <br><br> <b>Keep that email; it will be checked at the door.</b>")
.x(50, vw).y(50, vh)
.center()
}
}
register(Success)

View File

@@ -1,20 +1,5 @@
See https://github.com/return-to-the-land/go-backend for instructions.
# Installs
# Style Guidelines
- Font size is defined in the index.html. Only use rem as a unit.
Stripe CLI
# Documentation
```go install golang.org/x/tools/cmd/godoc@latest```
```$HOME/go/bin/godoc -http=:6060``` (to run on Mac)
Fix Redirect Issues:
Have the go server determine redirect, generate a random URL from it
When random url is accessed by frontend -> redirect to home
# Resources
Convert image to webp (so it's smaller)
https://www.freeconvert.com/jpg-to-webp/download
stripe listen --forward-to localhost:3003/webhook