-
Notifications
You must be signed in to change notification settings - Fork 0
Ticket tiers can be purchased, even when sold out leading to crash #21
Description
Description
The available ticket inventory is only checked during the initial rendering of /cure/registration/form. The checkout page will assume the ticket is in-stock and attempt to decrement the number of available tickets. Upon doing so an uncaught ApiException is raised. The uncaught exception causes the application to crash and not accept further requests. The oversold ticket is billed and marked as paid in the database and the Square inventory is not updated.
The tickets can be oversold by creating a single registration with multiple entries for the low-stock ticket tier or by multiple visitors starting their registration in parallel (visitor A starts the registration process, visitor B starts the registration process, visitor B checks out, visitor A checks out; leading to a crash).
Steps to reproduce
- Identify the inventory count in Square of the ticket tier (i.e. 1)
- Create a registration containing 2 attendees at that ticket tier
- Attempt to check-out. Purchaser is redirect to the "successful checkout" page.
- Observe uncaught exception in console. Additionally, observe that server is no longer accepting requests.
Remediation
Add error handling to ensure calls to external services are successful.
Exception details
kicweb-1 | Unhandled exception. Square.Exceptions.ApiException: HTTP Response Not OK
kicweb-1 | at APIMatic.Core.Utilities.CoreHelper.RunVoidTask(Task t)
kicweb-1 | at APIMatic.Core.Utilities.CoreHelper.RunTask[T](Task`1 t)
kicweb-1 | at Square.Apis.InventoryApi.BatchChangeInventory(BatchChangeInventoryRequest body)
kicweb-1 | at KiCData.Services.PaymentService.<>c__DisplayClass13_0.<ReduceTicketInventory>b__0() in /src/kicdata/Services/PaymentService.cs:line 300
kicweb-1 | at System.Threading.Tasks.Task`1.InnerInvoke()
kicweb-1 | at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
kicweb-1 | --- End of stack trace from previous location ---
kicweb-1 | at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
kicweb-1 | at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)
kicweb-1 | --- End of stack trace from previous location ---
kicweb-1 | at KiCData.Services.PaymentService.ReduceTicketInventory(List`1 registrationViewModels) in /src/kicdata/Services/PaymentService.cs:line 300
kicweb-1 | at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__128_1(Object state)
kicweb-1 | at System.Threading.QueueUserWorkItemCallback.Execute()
kicweb-1 | at System.Threading.ThreadPoolWorkQueue.Dispatch()
kicweb-1 | at System.Threading.PortableThreadPool.WorkerThread.WorkerThreadStart()