changes
This commit is contained in:
135
foftickets.js
135
foftickets.js
@@ -59,9 +59,9 @@ const QRSalt ="!SaltyMagic5392370662";
|
||||
// + Create Account (User)
|
||||
// + Change Password (User)
|
||||
// + 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)
|
||||
// Purchase individual open camping tickets
|
||||
// + See how much each camp/ticket has paid (Admin)
|
||||
// + Purchase individual open camping tickets
|
||||
// + Deactivate individual magic links (Admin)
|
||||
// Maybe:
|
||||
// Deactivate individual magic links (User)
|
||||
// Option to "Email me my QR Code"
|
||||
@@ -69,9 +69,9 @@ const QRSalt ="!SaltyMagic5392370662";
|
||||
// ToDo, Later
|
||||
// + Use a templating engine
|
||||
// + Store password hashed and salted
|
||||
// + Stripe Integration
|
||||
// Make all HTML look nice
|
||||
// Logging and Replay system(?)
|
||||
// + Stripe Integration
|
||||
// More efficent data structure: TicketsByCamp, TicketsByOffered, TicketsByOwner
|
||||
//
|
||||
|
||||
@@ -183,6 +183,7 @@ function InitDatabase() {
|
||||
for (const key in camps ) delete camps[key];
|
||||
users["teppy@egenesis.com" ]= { password: hashPW("x6321872X.1"), superuser:true, linksalt:"" };
|
||||
users["ginachen94@gmail.com" ]= { password: hashPW("indiantreeonfire"),superuser:true, linksalt:"" };
|
||||
camps["open"]={ leader:"", lastid:0 };
|
||||
}
|
||||
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 });
|
||||
})
|
||||
|
||||
|
||||
|
||||
app.post("/camps",requireSuperUser,(req,res) => {
|
||||
const campname=req.body.campname;
|
||||
const leader=req.body.leader;
|
||||
@@ -333,7 +336,6 @@ app.get('/mytickets',requireLogin, async (req,res)=> {
|
||||
let owned=0;
|
||||
let theticket="";
|
||||
const edit={ username:req.session.username, superuser:req.session.superuser, tickets: {}, settings:settings };
|
||||
console.log(edit);
|
||||
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].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),
|
||||
ticket:theticket, offered:tickets[theticket].offered, paid:tickets[theticket].paid, qrcode:dataURL, useurl:useurl,
|
||||
settings: settings }
|
||||
console.log(data);
|
||||
return res.render("oneticket",data);
|
||||
}
|
||||
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;
|
||||
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;
|
||||
console.log("Trying to transfer ",ticket);
|
||||
let message="";
|
||||
if (req.session.cantransfer && tickets[ticket].owner==username && tickets[ticket].status=="i") {
|
||||
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) });
|
||||
})
|
||||
|
||||
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) => {
|
||||
res.render('settings',{ username:req.session.username, superuser:req.session.superuser, settings:settings, message: "" })
|
||||
});
|
||||
@@ -683,58 +677,107 @@ app.post('/purge',requireSuperUser, (req,res) => {
|
||||
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) => {
|
||||
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' });
|
||||
});
|
||||
|
||||
|
||||
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) => {
|
||||
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;
|
||||
console.log("paymentMethodId: ",paymentMethodId);
|
||||
const payforwhat=req.body.payforwhat;
|
||||
if (!check_payforwhat(payforwhat,req)) return res.json({ error: 'Invalid PayForWhat' });
|
||||
try {
|
||||
// Token or Payment Method ID from the client
|
||||
const paymentMethodId = req.body.paymentMethodId;
|
||||
|
||||
// Create a PaymentIntent on the server
|
||||
const pennies=Math.round(parseFloat(req.body.amount) * 100);
|
||||
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,
|
||||
// Create a PaymentIntent on the server
|
||||
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 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') {
|
||||
if (paymentIntent.status === 'requires_action') {
|
||||
// Additional action is required (e.g. 3D Secure)
|
||||
res.json({
|
||||
requiresAction: true,
|
||||
paymentIntentClientSecret: paymentIntent.client_secret,
|
||||
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' });
|
||||
} else if (paymentIntent.status === 'succeeded') {
|
||||
// Payment is complete
|
||||
do_payforwhat(payforwhat);
|
||||
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 });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
Number of Tickets:<br>
|
||||
<input type="number" min="1" max="4" step="1" placeholder="1" name="qty"><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>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -8,8 +8,9 @@
|
||||
<%- include('partials/nav') %>
|
||||
<div class="content">
|
||||
<form id="payment-form">
|
||||
Payment Here!!!<%=amount%>
|
||||
|
||||
<!-- 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>
|
||||
<div id="card-number-element"></div>
|
||||
@@ -23,7 +24,7 @@ Payment Here!!!<%=amount%>
|
||||
<label for="card-postal-element">ZIP Code</label>
|
||||
<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>
|
||||
</div>
|
||||
<!-- Stripe.js -->
|
||||
@@ -46,54 +47,54 @@ Payment Here!!!<%=amount%>
|
||||
|
||||
const paymentForm = document.getElementById("payment-form");
|
||||
paymentForm.addEventListener("submit", async (e) => {
|
||||
e.preventDefault();
|
||||
e.preventDefault();
|
||||
|
||||
// 1. Create a PaymentMethod
|
||||
const { paymentMethod, error } = await stripe.createPaymentMethod({
|
||||
// 1. Create a PaymentMethod
|
||||
const { paymentMethod, error } = await stripe.createPaymentMethod({
|
||||
type: "card",
|
||||
card: cardNumberElement,
|
||||
});
|
||||
});
|
||||
|
||||
if (error) {
|
||||
if (error) {
|
||||
console.error(error);
|
||||
alert(error.message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Send PaymentMethod ID to server
|
||||
const response = await fetch("/charge", {
|
||||
// 2. Send PaymentMethod ID to server
|
||||
console.log("Payforwhat: ",<%-JSON.stringify(payforwhat)%>);
|
||||
const pfw = <%-JSON.stringify(payforwhat)%>;
|
||||
const response = await fetch("/charge", {
|
||||
method: "POST",
|
||||
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();
|
||||
|
||||
if (responseData.error) {
|
||||
if (responseData.error) {
|
||||
console.error(responseData.error);
|
||||
alert(responseData.error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (responseData.requiresAction) {
|
||||
// 3. Handle Additional Action if required (e.g., 3DS)
|
||||
const { error: confirmError } = await stripe.confirmCardPayment(
|
||||
responseData.paymentIntentClientSecret
|
||||
);
|
||||
if (responseData.requiresAction) {
|
||||
// 3. Handle Additional Action if required (e.g., 3DS)
|
||||
const { error: confirmError } = await stripe.confirmCardPayment(responseData.paymentIntentClientSecret);
|
||||
|
||||
if (confirmError) {
|
||||
if (confirmError) {
|
||||
console.error(confirmError);
|
||||
alert(confirmError.message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
alert("Payment succeeded");
|
||||
// redirect or show success message
|
||||
window.location.href = responseData.redirect_url;
|
||||
} else if (responseData.success) {
|
||||
// Payment succeeded without additional action
|
||||
alert("Payment succeeded.");
|
||||
window.location.href = responseData.redirect_url;
|
||||
alert("Payment succeeded");
|
||||
// redirect or show success message
|
||||
window.location.href = responseData.redirect_url;
|
||||
} else if (responseData.success) {
|
||||
// Payment succeeded without additional action
|
||||
alert("Payment succeeded.");
|
||||
window.location.href = responseData.redirect_url;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -23,7 +23,13 @@
|
||||
<form action='/deserialize' method='post'>
|
||||
<button type="submit" >Deserialize</button>
|
||||
</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">
|
||||
Import Tickets (Frostburn Format):
|
||||
<input type="file" name="file">
|
||||
<input type="submit" value="Upload">
|
||||
</form>
|
||||
|
||||
Reference in New Issue
Block a user