Paid, but the order never finished
The money went through. The order stayed a draft, because finishing it depended on the customer coming back to our page.
A customer adds a few extras to their trip, pays for them, and closes the tab. The payment went through. On our side the items are still drafts, so nobody is sure what they bought.
Where the mistake was
The confirmation ran on our payment status page. The flow was:
- Customer pays at the provider
- Provider sends them back to our site
- Our page loads and calls the endpoint that publishes the items
Step 3 only happens if step 2 happens. And step 2 is the customer, on their phone, deciding whether to come back. They might close the tab. Their connection might drop on the way back. They might just be done, in their mind they already paid.
The money is real either way. Only our record of it was optional.
What it should have been
The payment provider will tell our backend directly, if we ask it to. So now it does. When a payment succeeds, the provider posts to our backend, and the backend does the work:
- find the payment record, get the invoice
- take the draft items on that invoice and publish them
- add up the published items again so the total is right
The customer coming back is now just a nice screen for them. It is not what makes the order real.
What I keep from it
Anything that must happen should not be sitting behind a step the user can skip. If it matters, it belongs server to server, where no one can close it.
I still kept the page doing its call. Two paths to the same result is fine when the result is the same either way. What I did not want was one path, running in the place I control least.