developer
Paytm for Business Webhook Reconciliation Guide for 2026
Implement Paytm payment callbacks and webhooks safely with checksum validation, status reconciliation, idempotent order updates, retries, and monitoring.
Fact-checked and updated
Paytm for Business supports callbacks, webhooks, and status APIs across several payment products. These surfaces overlap but are not interchangeable. A customer redirect helps the browser continue; a server notification helps your backend react; a status enquiry lets your backend reconcile.
Paytm’s official callback and webhook guide says a callback response may be intermediate or final, while webhooks notify configured endpoints about payment, refund, subscription, dispute, and other events. Your implementation must follow the documentation for the exact Paytm product and API version you enabled.
Paytm callback and webhook flow
For a one-time online payment:
Merchant backend ──► Paytm initiate-transaction API
│ │
│ └── transaction token
▼
Customer opens Paytm checkout and authorises payment
│
├── callback POST ─────► merchant callback endpoint
│
└── webhook POST ──────► merchant webhook endpoint
│
▼
status reconciliation
│
▼
idempotent order update
The browser should never send a trusted paid=true signal to your order service. A callback handled on your backend can redirect the customer after validation, but fulfilment should be based on the verified transaction state.
Configure distinct URLs
Use clear routes:
https://payments.example.com/paytm/callback
https://payments.example.com/paytm/webhook/payment
https://payments.example.com/paytm/webhook/refund
Paytm’s public guide states that webhook endpoints currently use port 443. The endpoint should:
- accept Paytm’s documented HTTP method and content type;
- capture the received fields without changing them before validation;
- respond quickly after a durable write;
- avoid redirects, HTML challenges, and login pages;
- run behind HTTPS with a valid certificate;
- be excluded from CSRF checks intended for browser sessions;
- retain request size and timeout limits.
Do not log the merchant key, checksum source material containing sensitive fields, full card data, or customer identifiers that your support workflow does not need.
Verify checksum signatures
Paytm’s checksum documentation says its utility uses SHA-256 and AES-128 as part of the checksum mechanism. This is not a generic HMAC-SHA256(rawBody, secret) protocol. Use Paytm’s current official utility for your language.
For a Node/Express callback that receives form data:
import express from "express";
import PaytmChecksum from "paytmchecksum";
const app = express();
app.use("/paytm/callback", express.urlencoded({ extended: false }));
app.post("/paytm/callback", async (req, res) => {
const params = { ...req.body };
const checksum = String(params.CHECKSUMHASH ?? "");
delete params.CHECKSUMHASH;
const valid = await PaytmChecksum.verifySignature(
params,
process.env.PAYTM_MERCHANT_KEY!,
checksum,
);
if (!valid) {
return res.status(401).send("Invalid checksum");
}
await enqueuePaytmReconciliation({
orderId: String(params.ORDERID ?? ""),
callback: params,
});
return res.redirect(
303,
`/orders/${encodeURIComponent(String(params.ORDERID ?? ""))}`,
);
});
Names and content types differ among Paytm products. Copy the current field names from the relevant API reference, not from a blog post. If the documented signature covers a JSON body rather than name-value pairs, pass the exact required representation to the utility.
Keep checksum code server-side
The merchant key must never appear in:
- React/Vue/Angular bundles;
- Android or iOS code that can be extracted;
- Shopify themes;
- WordPress page markup;
- analytics events;
- client-visible environment variables.
Your frontend receives a transaction token or other public checkout material generated by your backend. It does not receive the merchant key.
Separate environment credentials
Paytm staging and live systems use different hosts and credentials. Model them as one environment object:
const paytm = {
mid: mustGetEnv("PAYTM_MID"),
merchantKey: mustGetEnv("PAYTM_MERCHANT_KEY"),
host: mustGetEnv("PAYTM_HOST"),
environment: mustGetEnv("PAYTM_ENV"),
};
if (
paytm.environment === "production" &&
paytm.host.includes("stage")
) {
throw new Error("Production Paytm config points at staging");
}
This prevents the common half-switched deployment where a live MID calls a staging host.
Reconcile transaction status
Paytm’s JS Checkout integration overview explicitly calls for callback validation and server-side Transaction Status API integration. Treat the status response specified for your Paytm product as the authoritative reconciliation result.
The request generally includes:
- merchant ID (
MID); - merchant-generated order ID;
- a signed request head or checksum;
- product-specific API version and host.
The response can contain:
- result status and code;
- Paytm transaction ID;
- bank transaction ID;
- order ID;
- transaction amount;
- payment mode;
- transaction time;
- refund amount or related metadata.
Your order update must compare more than TXN_SUCCESS:
function assertExpectedPayment(
status: PaytmStatus,
order: LocalOrder,
) {
if (status.body.resultInfo.resultStatus !== "TXN_SUCCESS") {
throw new Error("Payment is not successful");
}
if (status.body.mid !== order.paytmMid) {
throw new Error("MID mismatch");
}
if (status.body.orderId !== order.providerOrderId) {
throw new Error("Order ID mismatch");
}
if (toPaise(status.body.txnAmount) !== order.amountPaise) {
throw new Error("Amount mismatch");
}
}
function toPaise(value: string): bigint {
const [rupees, paise = ""] = value.split(".");
return BigInt(rupees) * 100n + BigInt((paise + "00").slice(0, 2));
}
Never use floating-point arithmetic for ledger comparisons.
Callback, webhook, and status can race
Any of these orders is normal:
A. callback → status query → webhook
B. webhook → callback → status query
C. status poll → webhook → callback
D. callback → temporary status pending → later webhook success
Design one transaction reducer that consumes verified evidence from any source rather than writing three independent pieces of fulfilment code.
Build an idempotent handler
Use a database table with unique constraints:
CREATE TABLE payment_events (
id BIGSERIAL PRIMARY KEY,
provider TEXT NOT NULL,
dedupe_key TEXT NOT NULL,
order_id TEXT NOT NULL,
transaction_id TEXT,
status TEXT NOT NULL,
payload_hash TEXT NOT NULL,
received_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE (provider, dedupe_key)
);
Choose a documented stable event identifier when Paytm provides one. Otherwise derive a conservative key from immutable fields such as merchant, order, transaction, event type, and final status:
import { createHash } from "node:crypto";
function paytmDedupeKey(event: PaytmEvent) {
return createHash("sha256")
.update([
event.mid,
event.orderId,
event.txnId,
event.eventType,
event.status,
].join("|"))
.digest("hex");
}
Within one database transaction:
- insert the event under the unique key;
- lock the merchant order;
- verify permitted state transition;
- store Paytm transaction identifiers;
- mark the order paid if appropriate;
- add an outbox row for fulfilment;
- commit;
- return HTTP success.
If the unique insert conflicts, return success. A retry is not an application error.
State transitions
Use monotonic transitions:
CREATED ─► PENDING ─► PAID
│ └────► FAILED
└────────────────► CANCELLED
PAID ─► REFUND_PENDING ─► PARTIALLY_REFUNDED
└──► REFUNDED
Do not let a late PENDING callback overwrite PAID. Do not let a duplicated refund event increment the refunded amount twice.
Operate and monitor webhooks
Respond quickly
The HTTP handler should validate enough to authenticate and persist the event, then enqueue slow work:
app.post("/paytm/webhook/payment", async (req, res) => {
const verified = await verifyPaytmPayload(req.body);
if (!verified) return res.status(401).send();
const inserted = await persistEventOnce(req.body);
res.status(200).send("OK");
if (inserted) {
void jobs.enqueue("reconcile-paytm-order", {
orderId: req.body.ORDERID,
});
}
});
In serverless environments, do not rely on unawaited work after sending the response. Persist to a durable queue before returning.
Reconciliation schedule
A robust schedule might be:
| Age of pending order | Action |
|---|---|
| 0–2 minutes | Customer page may poll your backend with backoff |
| 2–15 minutes | Worker checks status every few minutes |
| 15 minutes–24 hours | Lower-frequency reconciliation |
| After policy threshold | Flag for operations review; do not silently discard |
Use Paytm’s documented rate limits and retry guidance. Add jitter so every pending order does not query at the same second.
Alerts
Alert on:
- checksum failure rate above a small threshold;
- unknown MID or order IDs;
- amount mismatch;
- callback success followed by status failure;
- webhook queue lag;
- long-running pending orders;
- repeated status API errors;
- events received for a deactivated merchant environment.
Dashboards should distinguish customer failure, bank/provider pending, your application error, and reconciliation backlog.
Test matrix
Before live traffic, use Paytm’s staging credentials and test:
- success, failure, and pending results;
- tampered checksum;
- duplicate webhook;
- callback before webhook and webhook before callback;
- temporary status API timeout;
- incorrect amount;
- unknown order;
- refund initiated and refund completed;
- endpoint unavailable followed by retry;
- staging credentials accidentally sent to the live host.
Paytm webhooks become reliable when they are one input to a state machine—not a one-off controller that sends an email. Validate the Paytm checksum with the official utility, confirm status server-side, preserve idempotency, and reconcile every ambiguous order.
Direct answers
Frequently asked questions
- What is the difference between a Paytm callback URL and webhook URL?
- The callback participates in the customer payment flow and can carry an intermediate or final response. A webhook is a server-to-server notification configured for final payment or related events. Your backend should validate both and reconcile with the applicable status API.
- Must a merchant verify the Paytm checksum?
- Yes. Paytm documents its checksum signature as the integrity and authenticity control for requests and responses. Use Paytm's maintained checksum utility and the merchant key on the server; never generate or verify it in browser code.
- Can the callback response alone trigger fulfilment?
- A robust integration validates the checksum and then confirms the transaction using Paytm's server-side status API where the product documentation requires it. Compare MID, order ID, amount, and success status before fulfilment.
- Why can a Paytm webhook arrive more than once?
- At-least-once notification and retry behavior can produce duplicate deliveries. Store a unique event or transaction key and make every order-state transition idempotent.
- Which port can a Paytm webhook endpoint use?
- Paytm's public callback and webhook documentation states that webhook URLs currently allow port 443. Use a publicly reachable HTTPS endpoint and avoid a non-standard exposed port.
Build your payment flow
Explore the API and browser-only merchant tools.
Create UPI checkout orders, verify signed events, or test the free calculators and generators without exposing credentials.