anandsiddharth/laravel-paytm-wallet
A Laravel package for integrating Paytm Wallet payments into your application. It simplifies payment initiation, checksum generation, callback handling, and transaction verification, giving you a clean, configurable API for accepting Paytm wallet payments without writing low-level integration code.
composer require anandsiddharth/laravel-paytm-wallet. v2.0.0 is a major release, so expect breaking changes if you're coming from 1.x — read the upgrade diff (issue #58 / PR #61) before bumping an existing app.php artisan vendor:publish --tag=paytm-config or the provider's publish group). This is where MID, merchant key, industry type, channel, website, and the callback URL live. Everything else in the package reads from here..env and reference them from config/paytm.php — never commit the merchant key. Add PAYTM_MID, PAYTM_MERCHANT_KEY, PAYTM_ENV (staging/production), PAYTM_CALLBACK_URL.Order-then-pay (recommended)
Create your own orders/transactions row before calling Paytm, store the order_id, and pass that same ID through the payment request. Paytm's callback is the only reliable source of truth — never trust the browser redirect alone.
$order = Order::create(['user_id' => auth()->id(), 'amount' => $amount, 'status' => 'pending']);
$payment = Paytm::initiate([
'order_id' => (string) $order->id,
'amount' => $order->amount,
'cust_id' => (string) auth()->id(),
'email' => auth()->user()->email,
'mobile' => auth()->user()->phone,
'callback_url' => route('paytm.callback'),
]);
return redirect($payment->getRedirectUrl());
Callback handling as a single entry point
One route handles both success and failure; branch on STATUS/RESPCODE after checksum verification. Wrap the verification in a transaction and make the handler idempotent — Paytm retries callbacks.
public function callback(Request $request)
{
abort_unless($this->paytm->verifyChecksum($request->all()), 403);
$order = Order::whereKey($request->input('ORDERID'))->lockForUpdate()->firstOrFail();
if ($order->status !== 'pending') {
return response('OK'); // already processed, ack the retry
}
$order->update([
'status' => $request->input('STATUS') === 'TXN_SUCCESS' ? 'paid' : 'failed',
'txn_id' => $request->input('TXNID'),
'payment_meta' => $request->all(),
]);
return response('OK');
}
Reconciliation as a safety net
Don't rely solely on callbacks. Schedule a job that polls transaction status for orders stuck in pending for more than N minutes, using the order ID against the package's status/transaction lookup. This is the pattern that saves you when callbacks get dropped.
Refunds
Keep refunds as their own model/audit trail (refund_id, order_id, amount, status, raw_response). Refunds are asynchronous on Paytm's side and can partially succeed — treat the initial response as an acknowledgement, not a settlement.
Environment switching Drive staging vs production from config only, not from code branches. Many bugs come from a staging MID accidentally shipped to production (or vice versa) — assert the environment in a boot-time check in non-production.
Testing integration
Swap the package's client for a fake in tests (bind a stub in a service provider under APP_ENV=testing), or at minimum fake the callback route with a pre-computed checksum payload. Never hit the real Paytm sandbox from CI.
^1.0 in apps you can't migrate yet.99.50) must not be cast to 99.5 by your model. Use number_format($amount, 2, '.', '') at the boundary.POST and excluded from CSRF (VerifyCsrfToken::$except) — otherwise you'll see 419s in production logs and orders that silently never settle.order_id uniqueness. If your order IDs aren't globally unique across retries/environments, Paytm rejects duplicates. Prefix with an environment or timestamp if you reuse sequences.payment_logs table. Debugging checksum mismatches without the original payload is guesswork. Strip nothing on write; redact on display.config/paytm.php or .env, run php artisan config:clear. Stale cached config is the most common "it works locally but not on the server" cause.If you can share the PR #61 diff or issue #58 details, the migration notes above can be tightened into exact symbol-level changes.
How can I help you explore Laravel packages today?