A Payment Is Not “Successful” Just Because the Button Worked

A payment button is easy to see. The work behind it is what I need to understand properly. Somebody can finish checkout, close the page and still be waiting for the application to confirm what happened.

A connection can fail. A provider can send confirmation later. The same notification can arrive again. I need the product to handle those situations before telling the person everything is complete.

I need clear payment states

  • Pending: the payment has started, but the final result is not confirmed yet.
  • Successful: the server has verified the payment. Track fulfilment separately until the order or service is delivered.
  • Failed: the provider has confirmed that this attempt failed.

If verification itself times out, I shouldn't call the payment failed. The result is still unknown and needs checking. Depending on the provider, I may also need states for cancellation, refunds or reversals.

The database and the message on the screen need to agree. A loading animation only shows that something is waiting; it doesn't prove the payment status.

A success page is not enough proof

I should not deliver a paid service just because the browser reached a success URL. The server needs to verify the transaction through the provider's documented API or authenticated webhook flow.

Before marking an order paid, check the reference, amount, currency, provider status and the order it belongs to. Make sure the same transaction hasn't already been processed.

For a webhook, verify its authenticity exactly as the provider documents. A request claiming to be from the provider is still something to verify.

Give each order a reference

I want the order recorded before checkout starts, with its expected amount, purpose, owner and a unique reference. Then the confirmation can be matched to the correct order.

That reference helps with support too. “I paid yesterday” is hard to trace. A reference, date and amount give the person and support a clearer starting point without exposing card details.

Repeated confirmation must not deliver twice

A webhook may be retried, and a request may time out after the server has already finished. The system needs to expect that.

This is where idempotency matters: processing the same confirmed payment again should not activate the service or deliver the order again.

Use database protections and safe processing logic, including handling concurrent requests. Disabling a button alone won't stop a repeated server request.

Pending needs an explanation

If confirmation is still happening, tell the person what is known and give a safe way to check the status. For example:

We are still confirming this payment attempt. Please don't pay again yet. Keep your reference if you need to contact support.

The message must match what the system knows. I shouldn't say money was received when only an attempt was recorded, or tell someone to pay again because verification is temporarily unavailable.

I want useful logs, with private details kept out

Order IDs, transaction references, response statuses and event times can help trace a problem. Secret keys, full card details and unnecessary private data don't belong in the logs.

What I need to test

Before calling the flow ready, I want to check interrupted redirects, slow responses, repeated notifications and refreshes. I also need to check the wrong amount, currency or order reference and make sure none of those grants the service.

Then I need to check the person's experience. Can they understand the status, find their reference and reach support? When money is involved, I don't want someone left guessing.

For more context, see Building Digital Products for Nigerian Users. For a provider example, Stripe documents webhook verification and delivery behaviour and checking payment status. Follow the documentation for the provider your product actually uses.

Post a Comment

Previous Post Next Post

Contact Form