The interface showed that a payment request had been accepted. A later process still had to validate the account, record the transaction and update the loan balance. The successful response described the request, not the final result.
When the later work failed, the page kept showing a completed action because it only knew about the first response. Retrying from the interface could then submit the same request again.
We changed the response to return an operation state and gave the interface a route
for reading that state. The form now distinguishes submitted, processing,
completed and failed.
What could break was no longer hidden behind a loading indicator. The request flow now includes the work that continues after the HTTP response and the errors that can still change its outcome.