From 5505313e3e92148c95d52ef1bc0beaad712de814 Mon Sep 17 00:00:00 2001 From: Janis Hutz Date: Sat, 19 Aug 2023 14:10:40 +0200 Subject: [PATCH] app endpoints, enforce email verification --- src/server/admin/appApiRoutes.js | 63 ++++++++++++++ .../plugins/payments/payrexx/payrexxRoutes.js | 82 ++++++++++-------- .../plugins/payments/stripe/stripeRoutes.js | 83 +++++++++++-------- src/server/backend/tickets/ticketGenerator.js | 2 +- src/server/backend/userAPIRoutes.js | 1 - .../src/views/purchasing/PurchaseView.vue | 15 +++- 6 files changed, 169 insertions(+), 77 deletions(-) create mode 100644 src/server/admin/appApiRoutes.js diff --git a/src/server/admin/appApiRoutes.js b/src/server/admin/appApiRoutes.js new file mode 100644 index 0000000..7132a6f --- /dev/null +++ b/src/server/admin/appApiRoutes.js @@ -0,0 +1,63 @@ +/* +* libreevent - appApiRoutes.js +* +* Created by Janis Hutz 08/19/2023, Licensed under the GPL V3 License +* https://janishutz.com, development@janishutz.com +* +* +*/ + +const bodyParser = require( 'body-parser' ); +const db = require( '../backend/db/db.js' ); +const pwHandler = require( './pwdmanager.js' ); + +module.exports = ( app ) => { + app.post( '/app/authenticate', bodyParser.json(), ( req, res ) => { + pwHandler.checkpassword( req.body.email, req.body.password ).then( status => { + if ( status ) { + if ( status.status ) { + res.send( 'authOk' ); + } else { + res.send( 'wrong' ); + } + } else { + res.send( 'wrong' ); + } + } ); + } ); + + app.post( '/app/ticketLookup', bodyParser.json(), ( req, res ) => { + pwHandler.checkpassword( req.body.email, req.body.password ).then( status => { + if ( status ) { + if ( status.status ) { + db.getDataSimple( 'orders', 'order_name', req.body.ticketID.slice( 0, req.body.ticketID.indexOf( '_' ) ) ).then( dat => { + if ( dat[ 0 ] ) { + const tickets = JSON.parse( dat[ 0 ][ 'tickets' ] ); + const event = req.body.ticketID.slice( req.body.ticketID.indexOf( '_' ) + 1, req.body.ticketID.indexOf( '-' ) ); + const ticket = req.body.ticketID.slice( req.body.ticketID.indexOf( '-' ) + 1, req.body.ticketID.length ); + if ( tickets[ event ] ) { + if ( tickets[ event ][ ticket ] ) { + if ( !tickets[ event ][ ticket ][ 'invalidated' ] ) { + res.send( 'ticketValid' ); + } else { + res.send( 'ticketInvalid' ); + } + } else { + res.send( 'ticketInvalid' ); + } + } else { + res.send( 'ticketInvalid' ); + } + } else { + res.send( 'ticketInvalid' ); + } + } ); + } else { + res.send( 'wrong' ); + } + } else { + res.send( 'wrong' ); + } + } ); + } ); +}; \ No newline at end of file diff --git a/src/server/backend/plugins/payments/payrexx/payrexxRoutes.js b/src/server/backend/plugins/payments/payrexx/payrexxRoutes.js index a2eb63c..6e88338 100644 --- a/src/server/backend/plugins/payments/payrexx/payrexxRoutes.js +++ b/src/server/backend/plugins/payments/payrexx/payrexxRoutes.js @@ -27,47 +27,57 @@ let paymentOk = {}; module.exports = ( app, settings ) => { app.post( '/payments/prepare', bodyParser.json(), ( req, res ) => { if ( req.session.loggedInUser ) { - let purchase = { - 'successRedirectUrl': settings.yourDomain + '/payments/success', - 'cancelRedirectUrl': settings.yourDomain + '/payments/canceled', - 'failedRedirectUrl': settings.yourDomain + '/payments/failed', - 'currency': settings.currency, - 'basket': [], - 'amount': 0, - }; + db.getDataSimple( 'users', 'email', req.session.username ).then( user => { + if ( user[ 0 ] ) { + if ( user[ 0 ][ 'mail_confirmed' ] ) { + let purchase = { + 'successRedirectUrl': settings.yourDomain + '/payments/success', + 'cancelRedirectUrl': settings.yourDomain + '/payments/canceled', + 'failedRedirectUrl': settings.yourDomain + '/payments/failed', + 'currency': settings.currency, + 'basket': [], + 'amount': 0, + }; - db.getDataSimple( 'temp', 'user_id', req.session.id ).then( dat => { - if ( dat[ 0 ] ) { - db.getJSONData( 'events' ).then( events => { - let data = JSON.parse( dat[ 0 ].data ); - ( async () => { - for ( let event in data ) { - for ( let item in data[ event ] ) { - purchase[ 'basket' ].push( { - 'name': data[ event ][ item ].name, - 'quantity': data[ event ][ item ].count ?? 1, - 'amount': Math.round( parseFloat( events[ event ][ 'categories' ][ data[ event ][ item ].category ].price[ data[ event ][ item ][ 'ticketOption' ] ] ) * 100 ), - } ); - purchase[ 'amount' ] += Math.round( parseFloat( events[ event ][ 'categories' ][ data[ event ][ item ].category ].price[ data[ event ][ item ][ 'ticketOption' ] ] ) * 100 ) * ( data[ event ][ item ].count ?? 1 ); - } - } - const response = await payrexx.createGateway( purchase ); - if ( response.status === 200 ) { - const session = response.data.data[ 0 ]; - sessionReference[ session.id ] = { 'tok': req.session.id, 'email': req.session.username }; - pendingPayments[ req.session.id ] = true; - res.send( session.link ); + db.getDataSimple( 'temp', 'user_id', req.session.id ).then( dat => { + if ( dat[ 0 ] ) { + db.getJSONData( 'events' ).then( events => { + let data = JSON.parse( dat[ 0 ].data ); + ( async () => { + for ( let event in data ) { + for ( let item in data[ event ] ) { + purchase[ 'basket' ].push( { + 'name': data[ event ][ item ].name, + 'quantity': data[ event ][ item ].count ?? 1, + 'amount': Math.round( parseFloat( events[ event ][ 'categories' ][ data[ event ][ item ].category ].price[ data[ event ][ item ][ 'ticketOption' ] ] ) * 100 ), + } ); + purchase[ 'amount' ] += Math.round( parseFloat( events[ event ][ 'categories' ][ data[ event ][ item ].category ].price[ data[ event ][ item ][ 'ticketOption' ] ] ) * 100 ) * ( data[ event ][ item ].count ?? 1 ); + } + } + const response = await payrexx.createGateway( purchase ); + if ( response.status === 200 ) { + const session = response.data.data[ 0 ]; + sessionReference[ session.id ] = { 'tok': req.session.id, 'email': req.session.username }; + pendingPayments[ req.session.id ] = true; + res.send( session.link ); + } else { + res.status( 500 ).send( 'ERR_PAYMENT' ); + } + } )(); + } ); } else { - res.status( 500 ).send( 'ERR_PAYMENT' ); + res.status( 400 ).send( 'ERR_UID_NOT_FOUND' ); } - } )(); - } ); + } ).catch( error => { + console.error( '[ STRIPE ] DB ERROR: ' + error ); + res.status( 500 ).send( 'ERR_DB' ); + } ); + } else { + res.status( 428 ).send( 'ERR_MAIL_UNCONFIRMED' ); + } } else { - res.status( 400 ).send( 'ERR_UID_NOT_FOUND' ); + res.status( 428 ).send( 'ERR_MAIL_UNCONFIRMED' ); } - } ).catch( error => { - console.error( '[ STRIPE ] DB ERROR: ' + error ); - res.status( 500 ).send( 'ERR_DB' ); } ); } else { res.status( 403 ).send( 'ERR_UNAUTHORIZED' ); diff --git a/src/server/backend/plugins/payments/stripe/stripeRoutes.js b/src/server/backend/plugins/payments/stripe/stripeRoutes.js index b25e0e5..63051b0 100644 --- a/src/server/backend/plugins/payments/stripe/stripeRoutes.js +++ b/src/server/backend/plugins/payments/stripe/stripeRoutes.js @@ -27,46 +27,57 @@ let paymentOk = {}; module.exports = ( app, settings ) => { app.post( '/payments/prepare', bodyParser.json(), ( req, res ) => { if ( req.session.loggedInUser ) { - let purchase = { - 'line_items': [], - 'mode': 'payment', - 'success_url': settings.yourDomain + '/payments/success', - 'cancel_url': settings.yourDomain + '/payments/canceled', - 'submit_type': 'book', - 'customer_email': req.session.username - }; + db.getDataSimple( 'users', 'email', req.session.username ).then( user => { + if ( user[ 0 ] ) { + if ( user[ 0 ][ 'mail_confirmed' ] ) { + let purchase = { + 'line_items': [], + 'mode': 'payment', + 'success_url': settings.yourDomain + '/payments/success', + 'cancel_url': settings.yourDomain + '/payments/canceled', + 'submit_type': 'book', + 'customer_email': req.session.username + }; - db.getDataSimple( 'temp', 'user_id', req.session.id ).then( dat => { - if ( dat[ 0 ] ) { - db.getJSONData( 'events' ).then( events => { - let data = JSON.parse( dat[ 0 ].data ); - ( async () => { - for ( let event in data ) { - for ( let item in data[ event ] ) { - purchase[ 'line_items' ].push( { - 'price_data': { - 'product_data': { - 'name': data[ event ][ item ].name, - }, - 'currency': settings.currency, - 'unit_amount': Math.round( parseFloat( events[ event ][ 'categories' ][ data[ event ][ item ].category ].price[ data[ event ][ item ][ 'ticketOption' ] ] ) * 100 ), - }, - 'quantity': data[ event ][ item ].count ?? 1, - } ); - } + // Get cart and prepare order + db.getDataSimple( 'temp', 'user_id', req.session.id ).then( dat => { + if ( dat[ 0 ] ) { + db.getJSONData( 'events' ).then( events => { + let data = JSON.parse( dat[ 0 ].data ); + ( async () => { + for ( let event in data ) { + for ( let item in data[ event ] ) { + purchase[ 'line_items' ].push( { + 'price_data': { + 'product_data': { + 'name': data[ event ][ item ].name, + }, + 'currency': settings.currency, + 'unit_amount': Math.round( parseFloat( events[ event ][ 'categories' ][ data[ event ][ item ].category ].price[ data[ event ][ item ][ 'ticketOption' ] ] ) * 100 ), + }, + 'quantity': data[ event ][ item ].count ?? 1, + } ); + } + } + const session = await stripe.checkout.sessions.create( purchase ); + sessionReference[ session.id ] = { 'tok': req.session.id, 'email': req.session.username }; + pendingPayments[ req.session.id ] = true; + res.send( session.url ); + } )(); + } ); + } else { + res.status( 400 ).send( 'ERR_UID_NOT_FOUND' ); } - const session = await stripe.checkout.sessions.create( purchase ); - sessionReference[ session.id ] = { 'tok': req.session.id, 'email': req.session.username }; - pendingPayments[ req.session.id ] = true; - res.send( session.url ); - } )(); - } ); + } ).catch( error => { + console.error( '[ STRIPE ] DB ERROR: ' + error ); + res.status( 500 ).send( 'ERR_DB' ); + } ); + } else { + res.status( 428 ).send( 'ERR_MAIL_UNCONFIRMED' ); + } } else { - res.status( 400 ).send( 'ERR_UID_NOT_FOUND' ); + res.status( 428 ).send( 'ERR_MAIL_UNCONFIRMED' ); } - } ).catch( error => { - console.error( '[ STRIPE ] DB ERROR: ' + error ); - res.status( 500 ).send( 'ERR_DB' ); } ); } else { res.status( 403 ).send( 'ERR_UNAUTHORIZED' ); diff --git a/src/server/backend/tickets/ticketGenerator.js b/src/server/backend/tickets/ticketGenerator.js index 27efa2a..06bdfae 100644 --- a/src/server/backend/tickets/ticketGenerator.js +++ b/src/server/backend/tickets/ticketGenerator.js @@ -176,7 +176,7 @@ class TicketGenerator { 'eventName': this.events[ event ][ 'name' ], 'locationAndTime': new Date( this.events[ event ][ 'date' ] ).toLocaleString(), 'ticketName': order[ event ][ ticket ][ 'name' ], - 'ticketQRCode': ord[ 0 ].order_name + '_' + order[ event ][ ticket ][ 'id' ], + 'ticketQRCode': ord[ 0 ].order_name + '_' + event + '-' + order[ event ][ ticket ][ 'id' ], } ]; const page = await pdfLib.PDFDocument.load( await pdfme.generate( { 'template': template, 'inputs': data } ) ); const p = await doc.copyPages( page, page.getPageIndices() ); diff --git a/src/server/backend/userAPIRoutes.js b/src/server/backend/userAPIRoutes.js index a100410..adbbae5 100644 --- a/src/server/backend/userAPIRoutes.js +++ b/src/server/backend/userAPIRoutes.js @@ -14,7 +14,6 @@ const getHandler = new geth(); const path = require( 'path' ); const bodyParser = require( 'body-parser' ); -// settings is missing in arguments which shouldn't pose any problem module.exports = ( app, settings ) => { // Add specific routes here to have them be checked first to not get general handling diff --git a/src/webapp/main/src/views/purchasing/PurchaseView.vue b/src/webapp/main/src/views/purchasing/PurchaseView.vue index 78e66a6..0005507 100644 --- a/src/webapp/main/src/views/purchasing/PurchaseView.vue +++ b/src/webapp/main/src/views/purchasing/PurchaseView.vue @@ -18,7 +18,9 @@
-

Billing

+

Purchase

+

Ready to buy? Please once again check that all the right items are in your cart.

+ -
+ TODO: FUTURE: Implement +
-->
@@ -293,6 +295,13 @@ export default { window.location.href = text; }, 300 ); } ); + } else if ( res.status === 428 ) { + res.text().then( text => { + if ( text === 'ERR_MAIL_UNCONFIRMED' ) { + this.$refs.notification.cancelNotification( prep ); + this.$refs.notification.createNotification( 'Please confirm your email address to proceed', 10, 'error', 'high' ); + } + } ); } } ).catch( err => { console.error( err );