My first attempt at a payment processor was synchronous and dangerous. If anything timed out or failed mid-way, I ended up with inconsistent data or double charges.
To fix it, I had to stop trying to do everything inside the HTTP request. I moved to a model where the API just records the intent to pay, and a separate worker handles the actual execution.
Step 1: Idempotency (The "Don't Double Charge" Fix)
The first thing I needed was an Idempotency-Key . It’s just a unique string from the client. If I see the same key twice, I return the existing result instead of starting a new process.
I added a unique index to my payments table and updated the handler to check for it first:
idemKey := r . Header . Get ( "Idempotency-Key" ) existing , err := s . store . GetPaymentByIdempotency ( ctx , idemKey ) if err == nil { // We've seen this before, just return the result writeJSON ( w , http .…