changes
This commit is contained in:
@@ -59,9 +59,9 @@ const QRSalt ="!SaltyMagic5392370662";
|
|||||||
// + Create Account (User)
|
// + Create Account (User)
|
||||||
// + Change Password (User)
|
// + Change Password (User)
|
||||||
// + Change most POST routes to end in redirect instead of render.
|
// + Change most POST routes to end in redirect instead of render.
|
||||||
// Deactivate individual magic links (Admin)
|
// + See how much each camp/ticket has paid (Admin)
|
||||||
// See how much each camp/ticket has paid (Admin)
|
// + Purchase individual open camping tickets
|
||||||
// Purchase individual open camping tickets
|
// + Deactivate individual magic links (Admin)
|
||||||
// Maybe:
|
// Maybe:
|
||||||
// Deactivate individual magic links (User)
|
// Deactivate individual magic links (User)
|
||||||
// Option to "Email me my QR Code"
|
// Option to "Email me my QR Code"
|
||||||
@@ -69,9 +69,9 @@ const QRSalt ="!SaltyMagic5392370662";
|
|||||||
// ToDo, Later
|
// ToDo, Later
|
||||||
// + Use a templating engine
|
// + Use a templating engine
|
||||||
// + Store password hashed and salted
|
// + Store password hashed and salted
|
||||||
|
// + Stripe Integration
|
||||||
// Make all HTML look nice
|
// Make all HTML look nice
|
||||||
// Logging and Replay system(?)
|
// Logging and Replay system(?)
|
||||||
// + Stripe Integration
|
|
||||||
// More efficent data structure: TicketsByCamp, TicketsByOffered, TicketsByOwner
|
// More efficent data structure: TicketsByCamp, TicketsByOffered, TicketsByOwner
|
||||||
//
|
//
|
||||||
|
|
||||||
@@ -183,6 +183,7 @@ function InitDatabase() {
|
|||||||
for (const key in camps ) delete camps[key];
|
for (const key in camps ) delete camps[key];
|
||||||
users["teppy@egenesis.com" ]= { password: hashPW("x6321872X.1"), superuser:true, linksalt:"" };
|
users["teppy@egenesis.com" ]= { password: hashPW("x6321872X.1"), superuser:true, linksalt:"" };
|
||||||
users["ginachen94@gmail.com" ]= { password: hashPW("indiantreeonfire"),superuser:true, linksalt:"" };
|
users["ginachen94@gmail.com" ]= { password: hashPW("indiantreeonfire"),superuser:true, linksalt:"" };
|
||||||
|
camps["open"]={ leader:"", lastid:0 };
|
||||||
}
|
}
|
||||||
InitDatabase();
|
InitDatabase();
|
||||||
|
|
||||||
@@ -269,6 +270,8 @@ app.get('/camps',requireSuperUser, (req,res) => {
|
|||||||
return res.render("camps",{ username:req.session.username, superuser:req.session.superuser, camps:camplist });
|
return res.render("camps",{ username:req.session.username, superuser:req.session.superuser, camps:camplist });
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
app.post("/camps",requireSuperUser,(req,res) => {
|
app.post("/camps",requireSuperUser,(req,res) => {
|
||||||
const campname=req.body.campname;
|
const campname=req.body.campname;
|
||||||
const leader=req.body.leader;
|
const leader=req.body.leader;
|
||||||
@@ -333,7 +336,6 @@ app.get('/mytickets',requireLogin, async (req,res)=> {
|
|||||||
let owned=0;
|
let owned=0;
|
||||||
let theticket="";
|
let theticket="";
|
||||||
const edit={ username:req.session.username, superuser:req.session.superuser, tickets: {}, settings:settings };
|
const edit={ username:req.session.username, superuser:req.session.superuser, tickets: {}, settings:settings };
|
||||||
console.log(edit);
|
|
||||||
for (const t in tickets) {
|
for (const t in tickets) {
|
||||||
if (tickets[t].status=="i" && tickets[t].offered==username) { claimed++; tickets[t].owner=username; tickets[t].offered=""; } // LOG
|
if (tickets[t].status=="i" && tickets[t].offered==username) { claimed++; tickets[t].owner=username; tickets[t].offered=""; } // LOG
|
||||||
if (tickets[t].status=="i" && tickets[t].owner==username) {
|
if (tickets[t].status=="i" && tickets[t].owner==username) {
|
||||||
@@ -358,7 +360,6 @@ app.get('/mytickets',requireLogin, async (req,res)=> {
|
|||||||
const data={ username:username, superuser:req.session.superuser, message:message, magiclink:GetMagicLink(username),
|
const data={ 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,
|
ticket:theticket, offered:tickets[theticket].offered, paid:tickets[theticket].paid, qrcode:dataURL, useurl:useurl,
|
||||||
settings: settings }
|
settings: settings }
|
||||||
console.log(data);
|
|
||||||
return res.render("oneticket",data);
|
return res.render("oneticket",data);
|
||||||
}
|
}
|
||||||
else return res.render("manytickets",edit);
|
else return res.render("manytickets",edit);
|
||||||
@@ -388,6 +389,7 @@ app.post('/oneticket',requireLogin, async (req,res) => { // Make sure the ticket
|
|||||||
let ticket=req.body.ticket;
|
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 });
|
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 offered=req.body.offered;
|
||||||
|
console.log("Trying to transfer ",ticket);
|
||||||
let message="";
|
let message="";
|
||||||
if (req.session.cantransfer && tickets[ticket].owner==username && tickets[ticket].status=="i") {
|
if (req.session.cantransfer && tickets[ticket].owner==username && tickets[ticket].status=="i") {
|
||||||
tickets[ticket].offered=offered; // LOG
|
tickets[ticket].offered=offered; // LOG
|
||||||
@@ -626,14 +628,6 @@ app.post('/qrcodesu',requireSuperUser,async (req,res) => {
|
|||||||
return res.send({ owner:username, qrcode: URL, magiclink:GetMagicLink(username) });
|
return res.send({ owner:username, qrcode: URL, magiclink:GetMagicLink(username) });
|
||||||
})
|
})
|
||||||
|
|
||||||
app.get("/buy",(req,res) => {
|
|
||||||
return res.render("buy",{ username:req.session.username, superuser:req.session.superuser, settings:settings, message: "" });
|
|
||||||
});
|
|
||||||
|
|
||||||
app.post("/buy",(req,res) => {
|
|
||||||
// Stopped here
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get('/settings',requireSuperUser, (req,res) => {
|
app.get('/settings',requireSuperUser, (req,res) => {
|
||||||
res.render('settings',{ username:req.session.username, superuser:req.session.superuser, settings:settings, message: "" })
|
res.render('settings',{ username:req.session.username, superuser:req.session.superuser, settings:settings, message: "" })
|
||||||
});
|
});
|
||||||
@@ -683,29 +677,79 @@ app.post('/purge',requireSuperUser, (req,res) => {
|
|||||||
return res.redirect("/settings");
|
return res.redirect("/settings");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.post("/killmagiclink",requireSuperUser,(req,res) => {
|
||||||
|
if (!users[req.body.email]) users[req.body.email]={ };
|
||||||
|
users[req.body.email].linksalt=generateSecureToken();
|
||||||
|
req.session.message="Deactivated any previous Magic Links for "+req.body.email;
|
||||||
|
return res.redirect("/settings");
|
||||||
|
});
|
||||||
|
|
||||||
app.post('/update-setting', requireSuperUser, (req, res) => {
|
app.post('/update-setting', requireSuperUser, (req, res) => {
|
||||||
settings[req.body.name]=req.body.checked;
|
settings[req.body.name]=req.body.checked;
|
||||||
console.log("setting got updated to ",settings[req.body.name]);
|
|
||||||
res.json({ success: true, message: 'Checkbox state updated successfully' });
|
res.json({ success: true, message: 'Checkbox state updated successfully' });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
app.post('/pay0',requireLogin,(req,res) => {
|
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 });
|
return res.render("pay",{ username:req.session.username,
|
||||||
});
|
superuser:req.session.superuser,
|
||||||
|
payforwhat: { kind:"existing", ticket:req.body.ticket, amount:+req.body.amount, total:+req.body.amount, qty:1 },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function check_payforwhat(payforwhat,req) {
|
||||||
|
if (payforwhat.kind=="existing") {
|
||||||
|
let ticket=tickets[payforwhat.ticket];
|
||||||
|
if (!ticket || ticket.status=="r" || ticket.owner!=req.session.username) return false;
|
||||||
|
}
|
||||||
|
else if (payforwhat.kind=="new") {
|
||||||
|
if (payforwhat.qty<=0 || payforwhat.qty>6 || payforwhat.amounteach<=0 || payforwhat.amounteach>100000) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function assure_camp_exists(camp) {
|
||||||
|
if (!camps[camp]) camps[camp]={ leader:"", lastid:0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
function do_payforwhat(payforwhat) {
|
||||||
|
if (payforwhat.kind=="existing") {
|
||||||
|
tickets[payforwhat.ticket].paid=100.0*payforwhat.amount;
|
||||||
|
}
|
||||||
|
else if (payforwhat.kind=="new") {
|
||||||
|
assure_camp_exists(payforwhat.camp);
|
||||||
|
for (let i=0; i<payforwhat.qty; i++) {
|
||||||
|
camps[payforwhat.camp].lastid++;
|
||||||
|
tickets[payforwhat.camp+"-"+camps[payforwhat.camp].lastid]={ owner:payforwhat.email, offered:"",paid:100.0*payforwhat.amounteach,status:"i" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
app.get("/buy",(req,res) => {
|
||||||
|
return res.render("buy",{ username:req.session.username, superuser:req.session.superuser, settings:settings, message: "" });
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post('/buy',requireLogin,(req,res) => {
|
||||||
|
return res.render("pay",{ username:req.session.username,
|
||||||
|
superuser:req.session.superuser,
|
||||||
|
payforwhat: { kind:"new", camp:"open", email:req.body.email, qty:req.body.qty, amounteach:req.body.amounteach, total:req.body.qty*req.body.amounteach }});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
app.post('/charge', requireLogin, async (req, res) => {
|
app.post('/charge', requireLogin, async (req, res) => {
|
||||||
const ticket=req.body.ticket;
|
const payforwhat=req.body.payforwhat;
|
||||||
if (tickets[ticket].status=='r' || tickets[ticket].owner!=req.session.username) {
|
if (!check_payforwhat(payforwhat,req)) return res.json({ error: 'Invalid PayForWhat' });
|
||||||
res.json({ error: "Sanity check in /charge" });
|
try {
|
||||||
} else try {
|
|
||||||
// Token or Payment Method ID from the client
|
// Token or Payment Method ID from the client
|
||||||
const paymentMethodId = req.body.paymentMethodId;
|
const paymentMethodId = req.body.paymentMethodId;
|
||||||
console.log("paymentMethodId: ",paymentMethodId);
|
|
||||||
|
|
||||||
// Create a PaymentIntent on the server
|
// Create a PaymentIntent on the server
|
||||||
const pennies=Math.round(parseFloat(req.body.amount) * 100);
|
let pennies=0;
|
||||||
|
if (payforwhat.kind=="new") pennies=Math.round(parseFloat(payforwhat.amounteach))*parseInt(payforwhat.qty) * 100;
|
||||||
|
else if (payforwhat.kind=="existing") pennies=Math.round(parseFloat(payforwhat.amount))*100;
|
||||||
const return_url=base_url+'/mytickets';
|
const return_url=base_url+'/mytickets';
|
||||||
const paymentIntent = await stripe.paymentIntents.create({
|
const paymentIntent = await stripe.paymentIntents.create({
|
||||||
amount: pennies, // Amount in cents
|
amount: pennies, // Amount in cents
|
||||||
@@ -725,8 +769,7 @@ app.post('/charge', requireLogin, async (req, res) => {
|
|||||||
});
|
});
|
||||||
} else if (paymentIntent.status === 'succeeded') {
|
} else if (paymentIntent.status === 'succeeded') {
|
||||||
// Payment is complete
|
// Payment is complete
|
||||||
tickets[ticket].paid=pennies;
|
do_payforwhat(payforwhat);
|
||||||
console.log("Paid ",pennies," for ticket ",ticket);
|
|
||||||
res.json({ success: true, redirect_url: return_url });
|
res.json({ success: true, redirect_url: return_url });
|
||||||
} else {
|
} else {
|
||||||
res.json({ error: 'Invalid PaymentIntent status' });
|
res.json({ error: 'Invalid PaymentIntent status' });
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
Number of Tickets:<br>
|
Number of Tickets:<br>
|
||||||
<input type="number" min="1" max="4" step="1" placeholder="1" name="qty"><br>
|
<input type="number" min="1" max="4" step="1" placeholder="1" name="qty"><br>
|
||||||
Pay how much for each ticket:<br>
|
Pay how much for each ticket:<br>
|
||||||
<input type="number" min="1.00" max="1000.00" step="0.01" placeholder="50.00" name="amount"><br>
|
<input type="number" min="1.00" max="1000.00" step="0.01" placeholder="50.00" name="amounteach"><br>
|
||||||
<button id="Submit" type="submit">Pay</button>
|
<button id="Submit" type="submit">Pay</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,8 +8,9 @@
|
|||||||
<%- include('partials/nav') %>
|
<%- include('partials/nav') %>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<form id="payment-form">
|
<form id="payment-form">
|
||||||
Payment Here!!!<%=amount%>
|
|
||||||
<!-- Stripe Elements will inject the Card Element here -->
|
<!-- Stripe Elements will inject the Card Element here -->
|
||||||
|
You are about to charge $<%=payforwhat.total.toFixed(2)%> for <%=payforwhat.qty%> tickets.<br>
|
||||||
|
|
||||||
<label for="card-number-element">Card Number</label>
|
<label for="card-number-element">Card Number</label>
|
||||||
<div id="card-number-element"></div>
|
<div id="card-number-element"></div>
|
||||||
@@ -23,7 +24,7 @@ Payment Here!!!<%=amount%>
|
|||||||
<label for="card-postal-element">ZIP Code</label>
|
<label for="card-postal-element">ZIP Code</label>
|
||||||
<div id="card-postal-element"></div>
|
<div id="card-postal-element"></div>
|
||||||
|
|
||||||
<button id="submit-button" type="submit">Pay <%=amount%> Now</button>
|
<button id="submit-button" type="submit">Pay Now</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<!-- Stripe.js -->
|
<!-- Stripe.js -->
|
||||||
@@ -61,10 +62,12 @@ Payment Here!!!<%=amount%>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2. Send PaymentMethod ID to server
|
// 2. Send PaymentMethod ID to server
|
||||||
|
console.log("Payforwhat: ",<%-JSON.stringify(payforwhat)%>);
|
||||||
|
const pfw = <%-JSON.stringify(payforwhat)%>;
|
||||||
const response = await fetch("/charge", {
|
const response = await fetch("/charge", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({ paymentMethodId: paymentMethod.id, ticket:"<%=ticket%>", amount:"<%=amount%>" }),
|
body: JSON.stringify({ paymentMethodId: paymentMethod.id, payforwhat: pfw }),
|
||||||
});
|
});
|
||||||
|
|
||||||
const responseData = await response.json();
|
const responseData = await response.json();
|
||||||
@@ -77,9 +80,7 @@ Payment Here!!!<%=amount%>
|
|||||||
|
|
||||||
if (responseData.requiresAction) {
|
if (responseData.requiresAction) {
|
||||||
// 3. Handle Additional Action if required (e.g., 3DS)
|
// 3. Handle Additional Action if required (e.g., 3DS)
|
||||||
const { error: confirmError } = await stripe.confirmCardPayment(
|
const { error: confirmError } = await stripe.confirmCardPayment(responseData.paymentIntentClientSecret);
|
||||||
responseData.paymentIntentClientSecret
|
|
||||||
);
|
|
||||||
|
|
||||||
if (confirmError) {
|
if (confirmError) {
|
||||||
console.error(confirmError);
|
console.error(confirmError);
|
||||||
|
|||||||
@@ -23,7 +23,13 @@
|
|||||||
<form action='/deserialize' method='post'>
|
<form action='/deserialize' method='post'>
|
||||||
<button type="submit" >Deserialize</button>
|
<button type="submit" >Deserialize</button>
|
||||||
</form>
|
</form>
|
||||||
|
<form action='/killmagiclink' method='post'>
|
||||||
|
Deactivate Magic Links for:
|
||||||
|
<input type="email" name="email">
|
||||||
|
<button type="submit">Deactivate</button>
|
||||||
|
</form>
|
||||||
<form action="/importfb" method="post" enctype="multipart/form-data">
|
<form action="/importfb" method="post" enctype="multipart/form-data">
|
||||||
|
Import Tickets (Frostburn Format):
|
||||||
<input type="file" name="file">
|
<input type="file" name="file">
|
||||||
<input type="submit" value="Upload">
|
<input type="submit" value="Upload">
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
Reference in New Issue
Block a user