Skip to content
Techimpace
Web & E-commerce

Payment webhooks, failed transactions and reconciliation: what a reliable business system must handle

Why a payment shows paid at the gateway and pending in your system, and how signatures, idempotency, retries, refunds and daily reconciliation fix it.

Paritosh BagFounder & CEO, Techimpace17 min read
Person holding a payment card while entering the details on a laptop
In this article

A customer pays. The money leaves their account. Your system still says the order is pending, so they pay again, or they call. Somebody opens the gateway dashboard, finds the payment, and marks the order paid by hand. If that happens every week in your business, the gateway is not broken. The integration is treating payment as a single yes-or-no answer, and it is not one.

This guide explains why the gateway and your database disagree, and the eight things a business system has to handle so that they stop disagreeing: signatures, duplicates, ordering, pending states, refunds, reconciliation, audit trails and alerts. Where behaviour differs by gateway, it says so and names the provider's own documentation.

Why the gateway and your database disagree

A card or UPI payment is a conversation between the customer's bank, a payment network, the gateway and your server. Your system learns the outcome in two ways. The first is the redirect: the customer's browser comes back to your site with a result. The second is the webhook: the gateway's server calls yours directly.

Each can fail on its own. The customer closes the tab before the redirect, or their phone loses signal after approving the payment in a banking app. The webhook arrives while your server is restarting. The bank answers late, after the gateway has already told you the payment failed. None of this is unusual. Gateways document all of it, which is why their guidance is consistent on one point: build for events that are late, repeated and out of order.

A payment is a set of states, not a yes or no

Start by writing down the states a payment can be in for your business, and which ones allow you to hand over goods, activate a subscription or issue a receipt. The diagram below is a generic version. Every gateway has its own names for these moments.

Collection
  1. CreatedAn order exists. Nothing is paid.
  2. Customer actionOTP, 3-D Secure or a UPI approval.
  3. ProcessingThe bank has not answered yet.
  4. SucceededSafe to fulfil.
  5. SettledThe money reaches your bank.
Did not complete
  1. FailedDeclined, or timed out.
  2. AbandonedThe customer left the page.
  3. Authorised, not capturedFunds held, then released.
Refund
  1. Refund requestedBy staff or by rule.
  2. Refund pendingThe gateway is attempting it.
  3. RefundedConfirmed by the gateway.
Exceptions after payment
  1. Refund failedThe money comes back to you.
  2. DisputedThe customer's bank has reversed it.
  3. Late successPaid after you marked it failed.
A generic payment lifecycle. The dashed boxes are the states that cause support calls when a system does not model them.
How two gateways name the same moments, from their documentation on 10 October 2026. The states do not map one to one.
MomentStripe PaymentIntentRazorpay payment
Waiting for the customerrequires_payment_method, requires_confirmation, requires_actioncreated
Bank has not confirmedprocessingcreated, until it resolves
Funds held, not yet takenrequires_captureauthorized
Paidsucceededcaptured
Did not completecanceled, or back to requires_payment_method after a failed attemptfailed
RefundedA separate Refund object with its own statusrefunded, with a separate refund entity
How two gateways name the same moments, from their documentation on 10 October 2026. The states do not map one to one.

Keep your own state names and map each gateway's states to them in one place in the code. If you add a second gateway later, or the first one renames a state, the change stays in that mapping.

The browser redirect is not proof of payment

The redirect is for the customer's screen. It can be missing, and a URL can be edited. Decide that an order is paid only from something your server has verified: a signed webhook, or a status your server fetched from the gateway's API with your secret key. Stripe's own guidance is to fulfil on server-side events such as payment_intent.succeeded, and for Checkout on checkout.session.completed together with checkout.session.async_payment_succeeded for payment methods that confirm later.

A good pattern is to do both. When the customer returns, show a page that asks your server for the current status. If the webhook has already arrived, the page shows success. If not, it shows that the payment is being confirmed and checks again. The customer sees progress, and the decision still rests on verified data.

Verify every webhook before you trust it

A webhook endpoint is a public URL. Anyone who finds it can post a message saying an order was paid. Gateways solve this by signing each message with a secret that only you and they hold. Your server recomputes the signature and rejects anything that does not match.

Signature verification as each provider documents it
GatewayHeaderHow it is verified
StripeStripe-SignatureHMAC-SHA256 over the timestamp and the raw body, keyed with the endpoint secret. Official libraries reject events older than five minutes by default.
RazorpayX-Razorpay-SignatureHMAC-SHA256 over the raw body, keyed with the webhook secret.
PayPalTransmission headersEither a call to PayPal's verify-webhook-signature API, or local verification against PayPal's certificate.
Signature verification as each provider documents it
  • Verify against the raw request body. Most frameworks parse JSON before your code runs, and a re-serialised body no longer matches the signature. This is the most common reason verification fails on a correct secret.
  • Keep the timestamp check where the gateway provides one. It stops an old, genuine message from being replayed.
  • Store the webhook secret like any other credential: outside the code, different for test and live, and rotated when staff with access leave.
  • Reject and log a failed verification. Do not process the event with a warning.

Make every handler safe to run twice

Gateways deliver webhooks at least once, which means sometimes twice. Stripe and Razorpay both say so and both tell you to deduplicate. Stripe recommends recording the event IDs you have processed. Razorpay sends an x-razorpay-event-id header for the same purpose.

Idempotency means that running the same operation twice has the same result as running it once. In practice it takes two layers:

  • Record each event ID in a table with a unique constraint, in the same database transaction as the work it triggers. A second delivery fails the constraint and is acknowledged without doing anything.
  • Make the business step itself conditional. An order moves from pending to paid only if it is currently pending. A receipt number is issued only when that transition happens. Stock is reduced once.

The same idea applies in the other direction. When your server asks the gateway to create a charge or a refund and the network drops, you do not know whether it happened. Stripe accepts an Idempotency-Key header on such requests and returns the original result if the same key is sent again, with keys kept for at least 24 hours. An Idempotency-Key header has been proposed as an internet standard, but the draft expired in 2026 without becoming one, so each gateway defines its own behaviour. Read the one you use.

Expect late, repeated and out-of-order events

If your endpoint does not answer with a success code, the gateway tries again. How long it keeps trying depends on the provider.

Webhook retry behaviour as documented on 10 October 2026
GatewayRetriesWorth knowing
StripeUp to three days in live mode, with exponential backoffEvent order is not guaranteed.
RazorpayFor 24 hours, with exponential backoffYour endpoint must respond within five seconds. A webhook that fails continuously for 24 hours is disabled and has to be re-enabled by hand.
PayPalUp to 25 times over three daysAfter that the event is marked failed and can be resent manually.
Webhook retry behaviour as documented on 10 October 2026

Three design rules follow from that table.

  • Acknowledge first, work second. Verify the signature, store the event, return success, and do the real work from a queue. A handler that sends email and generates a PDF before answering will time out on a busy day, and the gateway will send the event again.
  • Never let an older event move an order backwards. If a payment-failed event arrives after the payment-captured event for the same payment, the order stays paid. Compare against the current state, not against the order of arrival.
  • When in doubt, ask the gateway. If an event refers to a payment you cannot place, fetch its current status from the API and act on that. Stripe's newer thin events are built around this: the event carries only an ID, and you fetch the latest object.

Pending is not failed

The most expensive mistake in a payment integration is treating no answer as a no. Banks sometimes answer late. Razorpay documents this as late authorisation: when the bank does not respond, the payment stays in the created state, is marked failed after ten minutes, and can still move to authorised if the bank confirms within the following three days. Stripe's processing status covers payment methods such as bank debits, where confirmation can take several days and the payment is not guaranteed in the meantime.

So a customer can be debited for an order your system has already cancelled. Decide in advance what happens then:

  • If the order can still be fulfilled, honour the late payment and tell the customer.
  • If it cannot, refund it automatically and tell the customer. Do not leave it for someone to notice.
  • If the customer already paid again, keep one payment and refund the other. Link every payment attempt to its order so duplicates are visible.

Capture settings matter here too. With Razorpay, an authorised payment that is not captured within the configured window is refunded to the customer automatically. An integration that authorises and then forgets to capture produces orders that looked paid and were not.

Failed transactions in India

For Indian payment rails, the Reserve Bank of India's September 2019 circular on turnaround time sets deadlines for reversing a failed transaction and compensation of ₹100 a day when a deadline is missed. For a card payment at an online merchant, or a UPI payment to a merchant, where the customer is debited and the merchant never receives confirmation, the reversal is due within five days of the transaction. For a UPI or IMPS fund transfer where the beneficiary is not credited, it is one day. Those deadlines bind banks and payment operators. They also tell you how long a customer may reasonably wait, and what your support team should say.

Refunds have their own lifecycle

A refund is a second transaction with its own states, not a switch on the original payment. Stripe refunds can be pending, requires_action, succeeded, failed or canceled. Razorpay refunds are pending, processed or failed, and Razorpay puts a normal refund at five to seven working days to reach the customer.

  • Record a refund as its own row, linked to the payment, with the amount, the reason, who requested it and the gateway's refund ID.
  • Show staff and customers "refund initiated" until the gateway confirms. Mark it refunded on the gateway's event, not on the button click.
  • Handle the failure event. A refund can fail after it was accepted, for example when the customer's account has closed, and the money returns to your balance. Somebody has to send it another way.
  • Support partial refunds from the start. The sum of refunds can never exceed the amount captured.

Reconcile every day, from the settlement backwards

Webhooks keep your system up to date. Reconciliation proves it is right. It is a daily three-way comparison between what your system believes, what the gateway recorded, and what reached the bank.

Work backwards from the money. A settlement or payout is one bank credit that covers many payments, minus fees, taxes, refunds and adjustments. Both major gateways expose that breakdown: Stripe through balance transactions and a payout reconciliation report, Razorpay through a settlement reconciliation API that lists the payments, refunds and adjustments in each settlement with the fee, the tax and the bank reference. Match each line to an order, and list what does not match.

The reconciliation exceptions worth a named owner
What you findWhat it usually meansWhat to do
Paid at the gateway, pending in your systemA missed or rejected webhookFetch the status, mark it paid, and find out why the event was lost
Paid in your system, absent at the gatewayA manual edit, or a trusted redirectTreat as unpaid until proven, and review who changed it
Two payments for one orderThe customer retried, or a late successRefund one and tell the customer
Amount differsA partial payment, a currency conversion or a feeCorrect the ledger entry; do not adjust the order to fit
Refunded at the gateway, order still fulfilledA refund made in the dashboard, outside your systemRecord the refund and reverse what was delivered if you can
Settled amount differs from the bank creditFees, taxes or an adjustmentPost each component to its own account
The reconciliation exceptions worth a named owner

A healthy system produces a short exceptions list every morning and an empty one most days. If the list is long, the cause is upstream, in one of the earlier sections.

Audit trails, alerts and a manual queue

Every payment problem ends with the same question: what did the gateway tell us, and when? Keep enough to answer it.

  • Store every webhook as received, with its headers, the time, the verification result and what your system did with it.
  • Log every status change on an order or payment with its cause: which event, which job, or which member of staff.
  • Allow manual corrections, but require a reason and keep the previous value. Reconciliation will need them.
  • Alert on what matters: no webhooks received for an unusual length of time, a rising verification failure rate, payments pending beyond your threshold, and unmatched lines after the daily reconciliation.
  • Give the exceptions a queue with an owner. An automated system still needs a person for the small number of cases it cannot settle.
The test of a payment integration is not the successful payment. It is what happens to the one that is neither clearly paid nor clearly failed.

A payment reliability checklist

Twelve checks for a payment integration

Where to start with an existing system

If your team already corrects payments by hand, start with the evidence. Take one month of settlements and reconcile them against your orders. The exceptions will show which of the sections above is causing the work, and that is usually one or two things. Fix those before rebuilding anything.

Techimpace builds and maintains payment integrations inside business software, including online fee collection in Academica ERP and GYMBIM. A payment integration review reads the webhook handlers, the state model, the refund path and the reconciliation process, and ends with a short list of fixes in priority order. It does not start with a rewrite.

Frequently asked questions

Why does the payment gateway show a payment as successful while my system shows it as pending?

Usually because the webhook that reports the success was missed, rejected or processed out of order, and the system relied on the browser redirect. Verify and store every webhook, check the payment status from your server when the customer returns, and reconcile daily to catch anything that slips through.

Is the redirect back to my website enough to confirm a payment?

No. The redirect can fail to arrive and can be tampered with. Confirm payment from a signed webhook or from a status your server fetches from the gateway's API.

What is idempotency in payment processing?

It means an operation can run more than once and still have the effect of running once. Gateways deliver webhooks at least once, so a handler has to recognise an event it has already processed and skip it, and a retried API request must not create a second charge or refund.

How long do payment gateways retry a failed webhook?

It depends on the gateway. At the time of writing, Stripe documents retries for up to three days in live mode, Razorpay for 24 hours, and PayPal up to 25 attempts over three days. Check the current documentation for the gateway you use.

What should happen when a customer is debited but the order fails?

Your system should detect it from a late success event or from reconciliation, then either honour the order or refund the payment automatically, and tell the customer. In India, the RBI's 2019 circular also sets deadlines for banks and operators to reverse failed transactions.

How often should a business reconcile online payments?

Daily. Match orders to gateway payments and to the settlement that reached the bank, and give unmatched items to a named person. Problems are much easier to resolve within a day than at month end.

Can Techimpace review an existing payment integration?

Yes. Techimpace builds and maintains payment integrations inside business software. A review covers the webhook handlers, the payment state model, refunds and reconciliation, and ends with a prioritised list of fixes.

Written by
Paritosh Bag
Founder & CEO, Techimpace

Paritosh Bag is a software engineer and the Founder & CEO of Techimpace Innovations Pvt Ltd. He has been building business software since 2010 and has led Techimpace since founding it in 2013, working across PHP and Laravel, JavaScript, cloud infrastructure, payment systems and AI automation.

Payment integration review

Still fixing payments by hand?

Tell us which gateway you use and how often orders and payments disagree. We review the webhook handling, the state model and the reconciliation process, and start with the fixes that remove the most manual work.