Do not let abandoned payments hold your stock hostage
Card payments get abandoned constantly: people close the tab, lose signal, or walk away. If a checkout reserves stock, you need a plan for the ones that never finish, or your inventory slowly locks up.
The abandoned-checkout problem
When a customer pays by card with 3D Secure, they leave your site for the bank’s page. Many never come back. If you reserved stock when they started, that stock is now held by a payment that will never complete. Do this enough times and your catalog looks sold out when it is not.
Reserve behind a payment intent
I model each checkout as a payment intent: a short-lived record that captures the cart, reserves the stock, locks the exchange rate, and holds for thirty minutes. The customer goes to the bank, pays, and comes back, and only a verified callback from the bank turns the intent into a real, confirmed order.
Sweep what never finished
A background sweeper runs every few minutes and finds intents that started but never finished. It expires them with a compare-and-set, so it only releases an intent that is still pending. A late callback that already finalized the same order is never disturbed. The reserved stock goes back, and the action is written to an audit log.
The bank quirks the docs leave out
Bank integrations are full of quirks the documentation does not mention: the exact text encoding the signatures are built over, and responses that are signed in more than one format depending on the path the payment took. Handling both, instead of trusting the happy path, is the difference between a demo and something that survives real traffic.
Where I used it
This is how the dealer ordering portal takes card payments through the bank, so inventory stays honest even when most checkouts are abandoned.
Questions
Why reserve stock before the payment finishes?
So two people cannot buy the last item at once while one of them is still on the bank's page. The reservation holds it for a short window, then is released if the payment never completes.
What stops the sweeper from clashing with a late callback?
A compare-and-set update. The sweeper only expires an intent if it is still pending, so if the bank's callback finalizes it first, the sweep does nothing.