First version with credit card integration and UI
This commit is contained in:
132
foftickets.js
132
foftickets.js
@@ -216,7 +216,7 @@ app.get('/emaillink',(req,res) => {
|
||||
app.get('/camps',requireSuperUser, (req,res) => {
|
||||
const camplist={};
|
||||
for (const c in camps) {
|
||||
camplist[c]={ leader:camps[c].leader, issued:0, claimed:0, used:0 };
|
||||
camplist[c]={ leader:camps[c].leader, issued:0, claimed:0, used:0, npaid:0, paid:0 };
|
||||
}
|
||||
for (const t in tickets) {
|
||||
const parts=t.split("-");
|
||||
@@ -226,6 +226,8 @@ app.get('/camps',requireSuperUser, (req,res) => {
|
||||
camplist[campname].issued+=1;
|
||||
if (tickets[t].owner!="") camplist[campname].claimed+=1;
|
||||
if (tickets[t].status=="u") camplist[campname].used+=1;
|
||||
if (tickets[t].paid>0) camplist[campname].npaid+=1;
|
||||
camplist[campname].paid+=tickets[t].paid;
|
||||
}
|
||||
}
|
||||
return res.render("camps",{ username:req.session.username, superuser:req.session.superuser, camps:camplist });
|
||||
@@ -238,7 +240,7 @@ app.post("/camps",requireSuperUser,(req,res) => {
|
||||
camps[campname]??={ leader:leader, lastid:0 };
|
||||
for (let i=0; i<qty; i++) {
|
||||
camps[campname].lastid+=1; // LOG
|
||||
tickets[campname+'-'+camps[campname].lastid]={ owner: "", offered: leader, status:"i" }; // LOG
|
||||
tickets[campname+'-'+camps[campname].lastid]={ owner: "", offered: leader, status:"i", paid:0 }; // LOG
|
||||
}
|
||||
EmailTickets(leader);
|
||||
return res.redirect("/camps");
|
||||
@@ -258,6 +260,7 @@ app.get('/editcamp', requireSuperUser, (req,res) => {
|
||||
edit.tickets[t].owner=tickets[t].owner;
|
||||
edit.tickets[t].offered=tickets[t].offered;
|
||||
edit.tickets[t].status=tickets[t].status;
|
||||
edit.tickets[t].paid=tickets[t].paid;
|
||||
}
|
||||
}
|
||||
return res.render("editcamp",edit);
|
||||
@@ -282,6 +285,7 @@ app.get('/manytickets', requireLogin, (req,res) => {
|
||||
for (const t in tickets) if (tickets[t].owner==username && tickets[t].status=="i") {
|
||||
edit.tickets[t]={};
|
||||
edit.tickets[t].offered=tickets[t].offered;
|
||||
edit.tickets[t].paid=tickets[t].paid;
|
||||
}
|
||||
return res.render("manytickets",edit);
|
||||
})
|
||||
@@ -301,6 +305,7 @@ app.get('/mytickets',requireLogin, async (req,res)=> {
|
||||
edit.tickets[t]={};
|
||||
edit.tickets[t].owner=tickets[t].owner;
|
||||
edit.tickets[t].offered=tickets[t].offered;
|
||||
edit.tickets[t].paid=tickets[t].paid;
|
||||
}
|
||||
}
|
||||
let message="";
|
||||
@@ -313,25 +318,47 @@ app.get('/mytickets',requireLogin, async (req,res)=> {
|
||||
const hash=hash1.digest("base64").slice(0,6);
|
||||
const useurl=MainURL+'/useticket?t='+theticket+'&h='+hashQR(theticket,username);
|
||||
const dataURL=await QRCode.toDataURL(useurl);
|
||||
return res.render("oneticket",{ username:username, superuser:req.session.superuser, message:message, ticket:theticket, offered:tickets[theticket].offered, qrcode:dataURL, useurl:useurl });
|
||||
return res.render("oneticket",{ username:username, superuser:req.session.superuser, message:message, magiclink:GetMagicLink(username),
|
||||
ticket:theticket, offered:tickets[theticket].offered, paid:tickets[theticket].paid, qrcode:dataURL, useurl:useurl });
|
||||
}
|
||||
else return res.render("manytickets",edit);
|
||||
});
|
||||
|
||||
app.post('/oneticket',requireLogin, async (req,res)=> {
|
||||
app.get("/oneticket",requireLogin, async (req,res) => {
|
||||
let username=req.session.username;
|
||||
let theticket=req.body.ticket;
|
||||
let ticket=req.query.t;
|
||||
console.log("Ticket ",tickets);
|
||||
if (tickets[ticket]) console.log("Onwer ",tickets[ticket].owner);
|
||||
if (!tickets[ticket] || tickets[ticket].owner!=username) return res.render("error", { username:username, superuser:req.session.superuser, message: "You are not the owner of ticket "+ticket });
|
||||
let offered=tickets[ticket].offered;
|
||||
let message="";
|
||||
const hash0=crypto.createHash('sha256');
|
||||
const hash1=hash0.update(ticket+QRSalt);
|
||||
const hash=hash1.digest("base64").slice(0,6);
|
||||
const useurl=MainURL+'/useticket?t='+ticket+'&h='+hashQR(ticket,username);
|
||||
const dataURL=await QRCode.toDataURL(MainURL+'/useticket?t='+ticket+'&h='+hashQR(ticket,username));
|
||||
return res.render("oneticket", { username:username, superuser:req.session.superuser, message:message, magiclink:GetMagicLink(username),
|
||||
ticket:ticket, offered:tickets[ticket].offered, paid:tickets[ticket].paid, qrcode:dataURL, useurl:useurl });
|
||||
});
|
||||
|
||||
|
||||
app.post('/oneticket',requireLogin, async (req,res) => { // Make sure the ticket is owned by trhe logged in user!
|
||||
let username=req.session.username;
|
||||
let ticket=req.body.ticket;
|
||||
if (!tickets[ticket] || tickets[ticket].owner!=username) return res.render("error", { username:username, superuser:req.session.superuser, message: "You are not the owner of ticket "+ticket });
|
||||
let offered=req.body.offered;
|
||||
let message="";
|
||||
if (tickets[theticket].owner==username && tickets[theticket].status=="i") {
|
||||
tickets[theticket].offered=offered; // LOG
|
||||
if (tickets[ticket].owner==username && tickets[ticket].status=="i") {
|
||||
tickets[ticket].offered=offered; // LOG
|
||||
EmailTickets(offered);
|
||||
}
|
||||
const hash0=crypto.createHash('sha256');
|
||||
const hash1=hash0.update(theticket+QRSalt);
|
||||
const hash1=hash0.update(ticket+QRSalt);
|
||||
const hash=hash1.digest("base64").slice(0,6);
|
||||
const dataURL=await QRCode.toDataURL(MainURL+'/useticket?t='+theticket+'&h='+hashQR(theticket,username));
|
||||
return res.render("oneticket", { username:username, superuser:req.session.superuser, message:message, magiclink:GetMagicLink(username), ticket:theticket, offered:tickets[theticket].offered, qrcode:dataURL });
|
||||
const dataURL=await QRCode.toDataURL(MainURL+'/useticket?t='+ticket+'&h='+hashQR(ticket,username));
|
||||
const useurl=MainURL+'/useticket?t='+ticket+'&h='+hashQR(ticket,username);
|
||||
return res.render("oneticket", { username:username, superuser:req.session.superuser, message:message, magiclink:GetMagicLink(username),
|
||||
ticket:ticket, offered:tickets[ticket].offered, paid:tickets[ticket].paid, qrcode:dataURL, useurl:useurl });
|
||||
});
|
||||
|
||||
|
||||
@@ -407,10 +434,12 @@ app.get("/useticket",(req,res) => {
|
||||
if (!tickets[ticket]) return res.status(500).send("Ticket "+ticket+" not found.");
|
||||
if (hashQR(ticket,tickets[ticket].owner)!=hash) return res.status(500).send("Ticket "+ticket+" was transferred to "+tickets[ticket].owner);
|
||||
if (tickets[ticket].status!="i") return res.status(500).send("Ticket "+ticket+" has already been used.");
|
||||
if (settings['enable-email']) {
|
||||
let paid_message=tickets[ticket].paid;
|
||||
if (tickets[ticket].paid==0) paid_message="This ticket has not been paid for. Encourage a donation at the gate.";
|
||||
if (settings['enable-ticket-use']) {
|
||||
tickets[ticket].status="u"; // LOG
|
||||
return res.send("<h1>Welcome, "+tickets[ticket].owner+" to Falls on Fire! Ticket "+ticket+" has now been used.</h1>");
|
||||
} else return res.send("<h1>Your ticket is good, "+tickets[ticket].owner+", but the server is not in Event Mode, so Ticket "+ticket+" is still valid.");
|
||||
return res.send("<h1>Welcome, "+tickets[ticket].owner+" to Falls on Fire!<br>Ticket "+ticket+" has now been used.</h1><br>"+paid_message);
|
||||
} else return res.send("<h1>Your ticket is good, "+tickets[ticket].owner+"<br>The server is not in Event Mode, so Ticket "+ticket+" is still valid.<br>"+paid_message);
|
||||
})
|
||||
|
||||
|
||||
@@ -592,46 +621,51 @@ app.post('/update-setting', requireSuperUser, (req, res) => {
|
||||
res.json({ success: true, message: 'Checkbox state updated successfully' });
|
||||
});
|
||||
|
||||
|
||||
app.get('/checkout', (req, res) => {
|
||||
// We’ll render a payment form here
|
||||
res.render('checkout2');
|
||||
app.post('/pay0',requireLogin,(req,res) => {
|
||||
return res.render("pay",{ username:req.session.username, superuser:req.session.superuser, ticket:req.body.ticket, amount:req.body.amount });
|
||||
});
|
||||
|
||||
app.post('/charge', async (req, res) => {
|
||||
try {
|
||||
// Token or Payment Method ID from the client
|
||||
const paymentMethodId = req.body.paymentMethodId;
|
||||
|
||||
// Create a PaymentIntent on the server
|
||||
const return_url=base_url+'/mytickets';
|
||||
const paymentIntent = await stripe.paymentIntents.create({
|
||||
amount: 1999, // Amount in cents (e.g., $19.99)
|
||||
currency: 'usd',
|
||||
payment_method: paymentMethodId,
|
||||
confirmation_method: 'automatic',
|
||||
confirm: true, // Attempt to confirm the payment immediately
|
||||
return_url: return_url,
|
||||
});
|
||||
app.post('/charge', requireLogin, async (req, res) => {
|
||||
const ticket=req.body.ticket;
|
||||
if (tickets[ticket].status=='r' || tickets[ticket].owner!=req.session.username) {
|
||||
res.json({ error: "Sanity check in /charge" });
|
||||
} else try {
|
||||
// Token or Payment Method ID from the client
|
||||
const paymentMethodId = req.body.paymentMethodId;
|
||||
|
||||
// Check status of payment intent
|
||||
if (paymentIntent.status === 'requires_action') {
|
||||
// Additional action is required (e.g. 3D Secure)
|
||||
res.json({
|
||||
requiresAction: true,
|
||||
paymentIntentClientSecret: paymentIntent.client_secret,
|
||||
});
|
||||
} else if (paymentIntent.status === 'succeeded') {
|
||||
// Payment is complete
|
||||
console.log("Returning json success: true");
|
||||
res.json({ success: true, redirect_url: return_url });
|
||||
} else {
|
||||
res.json({ error: 'Invalid PaymentIntent status' });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Payment error:', error);
|
||||
res.json({ error: error.message });
|
||||
}
|
||||
// Create a PaymentIntent on the server
|
||||
const pennies=Math.round(parseFloat(req.body.amount) * 100);
|
||||
console.log("Pennies=",pennies);
|
||||
const return_url=base_url+'/mytickets';
|
||||
const paymentIntent = await stripe.paymentIntents.create({
|
||||
amount: pennies, // Amount in cents
|
||||
currency: 'usd',
|
||||
payment_method: paymentMethodId,
|
||||
confirmation_method: 'automatic',
|
||||
confirm: true, // Attempt to confirm the payment immediately
|
||||
return_url: return_url,
|
||||
});
|
||||
|
||||
// Check status of payment intent
|
||||
if (paymentIntent.status === 'requires_action') {
|
||||
// Additional action is required (e.g. 3D Secure)
|
||||
res.json({
|
||||
requiresAction: true,
|
||||
paymentIntentClientSecret: paymentIntent.client_secret,
|
||||
});
|
||||
} else if (paymentIntent.status === 'succeeded') {
|
||||
// Payment is complete
|
||||
tickets[ticket].paid=pennies;
|
||||
console.log("Paid ",pennies," for ticket ",ticket);
|
||||
res.json({ success: true, redirect_url: return_url });
|
||||
} else {
|
||||
res.json({ error: 'Invalid PaymentIntent status' });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Payment error:', error);
|
||||
res.json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user