diff --git a/src/server/backend/plugins/payments/stripe/stripeRoutes.js b/src/server/backend/plugins/payments/stripe/stripeRoutes.js index 4d441a1..dcb9645 100644 --- a/src/server/backend/plugins/payments/stripe/stripeRoutes.js +++ b/src/server/backend/plugins/payments/stripe/stripeRoutes.js @@ -20,6 +20,7 @@ const endpointSecret = stripConfig[ 'endpointSecret' ]; let sessionReference = {}; let waitingClients = {}; +let pendingPayments = {}; let paymentOk = {}; // TODO: Remove all selected tickets if timestamp more than user defined amount ago @@ -40,7 +41,6 @@ module.exports = ( app, settings ) => { if ( dat[ 0 ] ) { db.getJSONData( 'events' ).then( events => { let data = JSON.parse( dat[ 0 ].data ); - console.log( data ); ( async () => { for ( let event in data ) { for ( let item in data[ event ] ) { @@ -58,6 +58,7 @@ module.exports = ( app, settings ) => { } 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 ); } )(); } ); @@ -84,19 +85,21 @@ module.exports = ( app, settings ) => { response.write( 'data: connected\n\n' ); waitingClients[ request.session.id ] = response; const ping = setInterval( () => { - const stat = TicketGenerator.getGenerationStatus( request.session.id ); - if ( stat === 'done' ) { - clearInterval( ping ); - setTimeout( () => { - response.write( 'data: ready\n\n' ); + if ( !pendingPayments[ request.session.id ] ) { + const stat = TicketGenerator.getGenerationStatus( request.session.id ); + if ( stat === 'done' ) { + clearInterval( ping ); + setTimeout( () => { + response.write( 'data: ready\n\n' ); + response.end(); + delete waitingClients[ request.session.id ]; + }, 2000 ); + } else if ( stat === 'noTicket' ) { + clearInterval( ping ); + response.write( 'data: noData\n\n' ); response.end(); delete waitingClients[ request.session.id ]; - }, 2000 ); - } else if ( stat === 'noTicket' ) { - clearInterval( ping ); - response.write( 'data: noData\n\n' ); - response.end(); - delete waitingClients[ request.session.id ]; + } } }, 2000 ); } ); @@ -139,26 +142,26 @@ module.exports = ( app, settings ) => { db.getDataSimple( 'temp', 'user_id', sessionReference[ event.data.object.id ][ 'tok' ] ).then( dat => { db.getDataSimple( 'users', 'email', sessionReference[ event.data.object.id ][ 'email' ] ).then( user => { if ( user[ 0 ] ) { - console.log( sessionReference[ event.data.object.id ][ 'tok' ] ); const tickets = JSON.parse( dat[ 0 ].data ); db.writeDataSimple( 'orders', 'account_id', user[ 0 ].account_id, { 'account_id': user[ 0 ].account_id, 'tickets': dat[ 0 ].data, 'order_name': sessionReference[ event.data.object.id ][ 'tok' ] } ).then( () => { + console.log( sessionReference[ event.data.object.id ][ 'tok' ] ); + delete pendingPayments[ sessionReference[ event.data.object.id ][ 'tok' ] ]; TicketGenerator.generateTickets( sessionReference[ event.data.object.id ] ); - } ); - db.getJSONData( 'booked' ).then( ret => { - let booked = ret ?? {}; - for ( let event in tickets ) { - if ( !booked[ String( event ) ] ) { - booked[ String( event ) ] = {}; + db.getJSONData( 'booked' ).then( ret => { + let booked = ret ?? {}; + for ( let event in tickets ) { + if ( !booked[ String( event ) ] ) { + booked[ String( event ) ] = {}; + } + for ( let tik in tickets[ event ] ) { + booked[ event ][ tik ] = tickets[ event ][ tik ]; + } } - for ( let tik in tickets[ event ] ) { - booked[ event ][ tik ] = tickets[ event ][ tik ]; - } - } - db.writeJSONData( 'booked', booked ); - } ); - - db.deleteDataSimple( 'temp', 'user_id', sessionReference[ event.data.object.id ][ 'tok' ] ).catch( error => { - console.error( '[ STRIPE ] ERROR whilst deleting data from DB: ' + error ); + db.writeJSONData( 'booked', booked ); + db.deleteDataSimple( 'temp', 'user_id', sessionReference[ event.data.object.id ][ 'tok' ] ).catch( error => { + console.error( '[ STRIPE ] ERROR whilst deleting data from DB: ' + error ); + } ); + } ); } ); } else { console.log( sessionReference[ event.data.object.id ][ 'email' ] ); diff --git a/src/server/backend/tickets/ticketGenerator.js b/src/server/backend/tickets/ticketGenerator.js index 1f578a1..a71a706 100644 --- a/src/server/backend/tickets/ticketGenerator.js +++ b/src/server/backend/tickets/ticketGenerator.js @@ -35,11 +35,10 @@ class TicketGenerator { this.runningTickets = {}; } - // TODO: Save to disk / DB in case of crash of server / reboot / whatever - // and continue processing once back online + // TODO: continue processing once back online generateTickets ( order ) { this.ticketQueue[ this.jobId ] = { 'order': order }; - this.runningTickets[ order ] = 'processing'; + this.runningTickets[ order.tok ] = 'processing'; this.jobId += 1; this.queueHandler(); } diff --git a/src/server/ui/en/payments/ticketMail.html b/src/server/ui/en/payments/ticketMail.html index a43e17f..e8ff201 100644 --- a/src/server/ui/en/payments/ticketMail.html +++ b/src/server/ui/en/payments/ticketMail.html @@ -8,7 +8,6 @@ body { font-family: sans-serif; width: 100%; - height: 800px; margin: 0; display: flex; justify-content: center; @@ -18,7 +17,7 @@ .content { width: 80%; - height: 90%; + height: 800px; display: flex; justify-content: center; align-items: center; diff --git a/src/webapp/main/src/components/noseatplan.vue b/src/webapp/main/src/components/noseatplan.vue index 92788c6..48a92fb 100644 --- a/src/webapp/main/src/components/noseatplan.vue +++ b/src/webapp/main/src/components/noseatplan.vue @@ -16,7 +16,7 @@ add - {{ cart[ event.name ] ? ( cart[ event.name ][ 'tickets' ][ ticket.id + '_' + ticketOption.id ] ? cart[ event.name ][ 'tickets' ][ ticket.id + '_' + ticketOption.id ][ 'count' ] : 0 ) : 0 }} + {{ cart[ event.eventID ] ? ( cart[ event.eventID ][ 'tickets' ][ ticket.id + '_' + ticketOption.id ] ? cart[ event.eventID ][ 'tickets' ][ ticket.id + '_' + ticketOption.id ][ 'count' ] : 0 ) : 0 }} remove @@ -41,7 +41,7 @@ export default { data () { return { tickets: { 'ticket1': { 'name': 'Ticket 1', 'id': 'ticket1', 'category': 1, 'free': 20 }, 'ticket2': { 'name': 'Ticket 2', 'id': 'ticket2', 'category': 2, 'free': 20 } }, - event: { 'name': 'TestEvent', 'location': 'TestLocation', 'date': 'TestDate', 'RoomName': 'TestRoom', 'currency': 'CHF', 'categories': { '1': { 'price': { '1':25, '2':35 }, 'bg': 'black', 'fg': 'white', 'name': 'Category 1' }, '2': { 'price': { '1':15, '2':20 }, 'bg': 'green', 'fg': 'white', 'name': 'Category 2' } }, 'ageGroups': { '1':{ 'id': 1, 'name':'Child', 'age':'0 - 15.99 years' }, '2':{ 'id': 2, 'name': 'Adult', 'age': null } }, 'stage': true }, + event: { 'name': 'TestEvent2', 'location': 'TestLocation2', 'eventID': 'test2', 'date': '2023-07-15', 'currency': 'CHF', 'categories': { '1': { 'price': { '1':25, '2':35 }, 'bg': 'black', 'fg': 'white', 'name': 'Category 1' }, '2': { 'price': { '1':15, '2':20 }, 'bg': 'green', 'fg': 'white', 'name': 'Category 2' } }, 'ageGroups': { '1':{ 'id': 1, 'name':'Child', 'age':'0 - 15.99' }, '2':{ 'id': 2, 'name': 'Adult' } }, 'maxTickets': 2 }, cart: {}, } }, @@ -65,20 +65,41 @@ export default { } else { this.cartHandling( 'select', this.tickets[ id ], option ); } - // TODO: make call to server to reserve ticket }, cartHandling ( operation, data, option ) { if ( operation === 'select' ) { - if ( this.cart[ this.event.eventID ] ) { - if ( this.cart[ this.event.eventID ][ 'tickets' ][ data.id + '_' + option ] ) { - this.cart[ this.event.eventID ][ 'tickets' ][ data.id + '_' + option ][ 'count' ] += 1; - } else { - this.cart[ this.event.eventID ][ 'tickets' ][ data.id + '_' + option ] = { 'displayName': data.name + ' (' + this.event.ageGroups[ option ].name + ')', 'price': this.event.categories[ data.category ].price[ option ], 'id': data.id + '_' + option, 'count': 1 }; + const options = { + method: 'post', + body: JSON.stringify( { 'id': data.id + '_' + option, 'component': data.category, 'ticketOption': option, 'eventID': this.event.eventID, 'count': ( this.cart[ this.event.eventID ] ? ( this.cart[ this.event.eventID ][ 'tickets' ][ data.id + '_' + option ] ? this.cart[ this.event.eventID ][ 'tickets' ][ data.id + '_' + option ][ 'count' ] : 0 ) : 0 ) + 1, 'category': data.category, 'name': 'Ticket ' + data.category + ' (' + this.event.ageGroups[ option ].name + ')' } ), + headers: { + 'Content-Type': 'application/json', + 'charset': 'utf-8' } - } else { - this.cart[ this.event.eventID ] = { 'displayName': this.event.name, 'tickets': {} }; - this.cart[ this.event.eventID ][ 'tickets' ][ data.id + '_' + option ] = { 'displayName': data.name + ' (' + this.event.ageGroups[ option ].name + ')', 'price': this.event.categories[ data.category ].price[ option ], 'id': data.id + '_' + option, 'count': 1 }; - } + }; + fetch( localStorage.getItem( 'url' ) + '/API/reserveTicket', options ).then( res => { + if ( res.status === 200 ) { + if ( this.cart[ this.event.eventID ] ) { + if ( this.cart[ this.event.eventID ][ 'tickets' ][ data.id + '_' + option ] ) { + this.cart[ this.event.eventID ][ 'tickets' ][ data.id + '_' + option ][ 'count' ] += 1; + } else { + this.cart[ this.event.eventID ][ 'tickets' ][ data.id + '_' + option ] = { 'displayName': data.name + ' (' + this.event.ageGroups[ option ].name + ')', 'price': this.event.categories[ data.category ].price[ option ], 'id': data.id + '_' + option, 'count': 1 }; + } + } else { + this.cart[ this.event.eventID ] = { 'displayName': this.event.name, 'tickets': {} }; + this.cart[ this.event.eventID ][ 'tickets' ][ data.id + '_' + option ] = { 'displayName': data.name + ' (' + this.event.ageGroups[ option ].name + ')', 'price': this.event.categories[ data.category ].price[ option ], 'id': data.id + '_' + option, 'count': 1 }; + } + } else if ( res.status === 409 ) { + setTimeout( () => { + this.$refs.popups.openPopup( 'Unfortunately, the seat you just tried to select was reserved by somebody else since the last time the seat plan was refreshed. Please select another one. We are sorry for the inconvenience.', {}, 'string' ); + }, 300 ); + } + if ( Object.keys( this.cart[ this.event.eventID ][ 'tickets' ] ).length < 1 ) { + delete this.cart[ this.event.eventID ]; + } + + this.$refs.cart.calculateTotal(); + localStorage.setItem( 'cart', JSON.stringify( this.cart ) ); + } ); } else if ( operation === 'deselect' ) { if ( this.cart[ this.event.eventID ][ 'tickets' ][ data + '_' + option ][ 'count' ] === 1 ) { delete this.cart[ this.event.eventID ][ 'tickets' ][ data + '_' + option ]; diff --git a/src/webapp/main/src/views/purchasing/TicketsOrderingView.vue b/src/webapp/main/src/views/purchasing/TicketsOrderingView.vue index 3fd788d..7bd7eba 100644 --- a/src/webapp/main/src/views/purchasing/TicketsOrderingView.vue +++ b/src/webapp/main/src/views/purchasing/TicketsOrderingView.vue @@ -50,7 +50,7 @@ }, data() { return { - hasSeatplan: true, + hasSeatplan: false, eventID: '', } },