Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

eSewa Integration Guide for PHP Apps

By Kokil Thapa | Last reviewed: August 2026

Implementing a reliable payment gateway is often the most critical bottleneck for Nepali eCommerce projects. This eSewa Integration Guide for PHP Apps provides the exact technical steps to integrate Nepal’s largest digital wallet into Laravel or custom PHP applications without relying on outdated documentation. Whether you are building a legal-tech portal or an online store, getting the handshake, signature generation, and verification loop correct is non-negotiable for securing real-money transactions.

Many developers I work with in Kathmandu still rely on deprecated MD5 implementations or test-mode snippets found in old forums. In 2026, eSewa mandates HMAC-SHA256 for all production integrations, and failing to implement proper server-side verification exposes your business to chargebacks and fraud. For those building broader payment ecosystems, this guide complements general Laravel payment integration strategies by focusing specifically on eSewa's unique cryptographic requirements and callback behaviors.

How does the eSewa payment flow work in PHP?

Understanding the sequence of redirects and API calls prevents the most common integration failures. The eSewa protocol is not a simple REST API where you send money and get a JSON response immediately. It is a redirect-based flow with an asynchronous verification step that many developers skip or implement incorrectly.

User BrowserPHP BackendeSewa GatewayDatabase1. Click Pay2. Create Pending Order3. Redirect + Signature4. User Pays on eSewa5. Return to Success URL6. Server Verify (API)7. Status Response8. Update Order Status
Correct eSewa payment flow showing mandatory server-to-server verification step

The critical takeaway from this flow is step 6. When eSewa redirects the user back to your success_url, that GET request contains transaction parameters but no proof of payment. A malicious user can manually visit your success URL with forged parameters. Your PHP backend must ignore these parameters for order confirmation and instead use them only as lookup keys to query eSewa’s verification endpoint directly from your server.

How do you generate HMAC-SHA256 signatures for eSewa?

Signature generation is where most integrations fail silently. eSewa rejects requests with invalid signatures without detailed error messages, leading to hours of debugging. The signature must be generated using HMAC-SHA256 with your merchant secret key, and the signed string must follow an exact format.

Preparing the signature string

The data to sign is a single concatenated string containing specific transaction fields separated by commas. The order matters absolutely. For the current ePay v2 API, the typical format is:

<?php
// Total amount, transaction UUID, product code, secret key
// NOTE: Verify exact field order in latest eSewa docs - this is illustrative
$totalAmount = '1000.00';
$transactionUuid = 'TXN-' . time() . '-' . random_int(1000, 9999);
$productCode = 'EPAYTEST'; // Use your assigned production code
$secretKey = config('services.esewa.secret_key');

// Build the message string exactly as specified
$message = "total_amount={$totalAmount},transaction_uuid={$transactionUuid},product_code={$productCode}";

// Generate HMAC-SHA256 signature
$signature = base64_encode(hash_hmac('sha256', $message, $secretKey, true));

A common mistake is including spaces after commas or using different decimal formatting than what you send in the form. If your form sends 1000 but your signature uses 1000.00, verification fails. Always normalize amounts to two decimal places consistently across both the signature generation and the HTML form fields.

Laravel implementation pattern

In Laravel applications, encapsulate this logic in a dedicated service class rather than scattering it across controllers. This makes testing easier and ensures consistent formatting. Store credentials in .env and access via config:

// app/Services/EsewaService.php
class EsewaService
{
    public function generateSignature(float $amount, string $uuid): string
    {
        $formattedAmount = number_format($amount, 2, '.', '');
        
        $message = sprintf(
            'total_amount=%s,transaction_uuid=%s,product_code=%s',
            $formattedAmount,
            $uuid,
            config('services.esewa.product_code')
        );
        
        return base64_encode(
            hash_hmac('sha256', $message, config('services.esewa.secret_key'), true)
        );
    }
}

For developers working on eCommerce platforms in Nepal, wrapping this in a reusable package saves significant time across multiple client projects. Remember that the secret key differs between test and production environments—never hardcode it.

What is the correct server-side verification process?

This is the most security-critical section of this eSewa Integration Guide for PHP Apps. After the customer completes payment, eSewa redirects them to your success URL with query parameters like oid, amt, refId, and txnId. These parameters are not trustworthy. You must verify them server-to-server.

Receive Success CallbackExtract refId & txnId from GET paramsCall eSewa Verify API (Server-to-Server)Status == COMPLETE?NOYESLog Fraud / Show ErrorVerify Amount Matches DBMark Order Paid & Fulfill
Decision tree for secure eSewa transaction verification preventing callback forgery

Making the verification request

Use Laravel’s HTTP client or Guzzle to POST to eSewa’s verification endpoint. Include the transaction reference ID and your merchant credentials. The verification endpoint returns JSON indicating whether the transaction completed successfully and for what amount.

// In your PaymentController@success method
$response = Http::asForm()->post(config('services.esewa.verify_url'), [
    'amt' => $request->input('amt'),
    'rid' => $request->input('refId'),
    'pid' => config('services.esewa.product_code'),
    'scd' => config('services.esewa.merchant_code'),
]);

$data = $response->json();

if (($data['status'] ?? '') !== 'COMPLETE') {
    Log::warning('eSewa verification failed', ['refId' => $request->input('refId'), 'response' => $data]);
    return redirect()->route('checkout.failed')->with('error', 'Payment could not be verified.');
}

// CRITICAL: Verify amount matches YOUR stored order amount
$order = Order::where('esewa_txn_id', $request->input('txnId'))->firstOrFail();
if (abs((float)$data['amt'] - (float)$order->total_amount) > 0.01) {
    Log::error('eSewa amount mismatch', ['expected' => $order->total_amount, 'received' => $data['amt']]);
    abort(403, 'Transaction amount mismatch');
}

// Only NOW mark as paid
$order->update(['status' => 'paid', 'paid_at' => now()]);

The amount check is not optional. Attackers have exploited gateways by paying Re 1 for orders worth Rs 10,000. Without verifying the returned amount against your database record, you will ship goods for fractions of their price. This pattern applies equally whether you’re running a multi-gateway Laravel setup or a standalone integration.

How do you handle eSewa test vs production environments safely?

Mixing test and production credentials causes lost payments and debugging nightmares. eSewa maintains separate endpoints, merchant codes, and secret keys for each environment. Your application must switch between them based on configuration, never runtime detection.

Configuration AspectTest EnvironmentProduction Environment
Gateway URLhttps://uat.esewa.com.np/epay/mainhttps://epay.esewa.com.np/epay/main
Verification URLhttps://uat.esewa.com.np/epay/transrechttps://epay.esewa.com.np/epay/transrec
Product CodeEPAYTESTYour assigned merchant code
Secret KeyPublic test key from docsPrivate key from merchant dashboard
SSL RequirementOptional (but recommended)Mandatory HTTPS everywhere

In Laravel, use environment-specific config files or conditional logic in config/services.php:

// config/services.php
'esewa' => [
    'gateway_url' => env('ESEWA_GATEWAY_URL', 'https://uat.esewa.com.np/epay/main'),
    'verify_url' => env('ESEWA_VERIFY_URL', 'https://uat.esewa.com.np/epay/transrec'),
    'product_code' => env('ESEWA_PRODUCT_CODE', 'EPAYTEST'),
    'merchant_code' => env('ESEWA_MERCHANT_CODE'),
    'secret_key' => env('ESEWA_SECRET_KEY'),
],

Never commit production secrets to version control. On shared hosting or VPS deployments common in Nepal, ensure your .env file permissions are restricted to 600 and owned by the web server user. I’ve seen too many sites where .env was world-readable, exposing payment credentials.

What are common eSewa integration pitfalls and how do you avoid them?

After integrating eSewa across multiple production systems—from legal service portals to florist eCommerce stores—I’ve catalogued recurring failure modes. Avoiding these saves days of troubleshooting.

  • Decimal formatting inconsistencies: eSewa expects amounts formatted as 1000.00, not 1,000.00 or 1000. Use number_format($amount, 2, '.', '') consistently everywhere.
  • Missing timeout handling: eSewa’s verification API can occasionally take 5-10 seconds under load. Set explicit timeouts (15s) and implement retry logic with exponential backoff. Don’t let users hang indefinitely.
  • Ignoring failure URLs: Users cancel payments or encounter errors. Your failure_url handler should gracefully restore the cart/session state so customers can retry without re-entering everything.
  • Stale session data: If your success handler relies on session-stored order IDs, sessions may expire during the payment redirect. Always pass the transaction UUID in the eSewa form and look up orders by that UUID, not session.
  • Timezone mismatches: eSewa timestamps are in NPT (UTC+5:45). If your server runs UTC, convert explicitly when comparing transaction times or generating daily reconciliation reports.
❌ Wrong Signature FormatSpaces in message stringInconsistent decimalsWrong field order✅ Correct ImplementationExact comma separationnumber_format(x, 2, '.', '')Documented field sequence❌ Trusting Client ParamsUpdating order from GET dataSkipping amount verificationNo server-to-server call✅ Secure VerificationAlways verify via APICompare amounts exactlyLog mismatches as fraud⚠️ Environment Mix-upsTest keys in productionHardcoded URLsSolution: Config-driven switching
Visual comparison of insecure vs secure eSewa integration patterns

One subtle issue specific to Nepal: some corporate firewalls and ISP proxies intermittently block outbound HTTPS to eSewa’s verification endpoint. If your verification calls fail sporadically despite correct credentials, test connectivity from your production server using curl -v https://epay.esewa.com.np/epay/transrec. Consider implementing a queue-based verification fallback that retries failed verifications every 30 seconds for up to 10 minutes before alerting support.

Secure eSewa Integration Checklist for Production

Before going live with any eSewa Integration Guide for PHP Apps implementation, run through this verification checklist. Each item addresses a real vulnerability or operational failure observed in production systems:

  1. HMAC-SHA256 only: Confirm no MD5 or SHA1 signature code remains anywhere in your codebase.
  2. Server-side verification mandatory: Audit every success route to ensure it calls eSewa’s API before updating any order status.
  3. Amount validation enforced: Verify your comparison logic handles floating-point precision correctly (use integer paisa or epsilon comparison).
  4. Credentials externalized: Check that no secret keys appear in git history or deployed artifacts.
  5. Timeout and retry configured: Ensure HTTP clients have explicit timeouts and verification failures trigger background retries.
  6. Logging comprehensive: Log every verification attempt with refId, expected amount, received amount, and API response (redact sensitive fields).
  7. Failure path tested: Manually test cancelled payments, expired sessions, and network timeouts to confirm graceful degradation.
  8. SSL enforced end-to-end: Verify your site serves only over HTTPS and eSewa callbacks arrive via HTTPS.

Integrating eSewa correctly requires discipline more than complexity. The cryptographic primitives are standard; the challenge is maintaining rigor around verification and environment separation when deadlines pressure teams to cut corners. For teams building financial or legal platforms where transaction integrity is non-negotiable, investing in proper admin panel tooling to monitor and reconcile eSewa transactions pays dividends immediately.

If you’re implementing eSewa for a production system and need architecture review or troubleshooting, reach out directly. I’ve debugged enough eSewa integrations to spot signature issues and verification gaps quickly, whether you’re running Laravel, Symfony, or vanilla PHP.

Frequently Asked Questions

You need a Merchant ID, Secret Key, and Product Code from the eSewa merchant dashboard. Production credentials differ from UAT; never use test keys in live environments or expose secrets in client-side code.

Standard merchant fees are typically 1.5% per transaction plus NPR 10 settlement fee, but rates vary by business category and volume. Confirm exact pricing directly with your eSewa account manager before estimating project costs.

No official SDK exists. Most Nepali developers build custom wrappers using GuzzleHTTP. I maintain a private package handling signature generation and callback verification based on patterns refined across multiple production integrations since 2018.

Never trust callback data blindly. Always validate the HMAC-SHA256 signature using your secret key against the raw POST payload. Additionally, perform a server-to-server status check via the transaction verification endpoint to confirm the payment actually succeeded on eSewa's side before updating order status. This prevents replay attacks and spoofed success responses that bypass signature validation.

Signature mismatches usually stem from parameter ordering differences between request and verification, incorrect encoding of special characters, or using UAT secrets against production endpoints. Ensure you sort parameters alphabetically before hashing, use raw URL encoding without double-encoding, and confirm environment-specific credentials. In my experience debugging client projects, whitespace trimming on secret keys copied from dashboards causes more failures than actual logic errors.

Technically possible but strongly discouraged and likely violating merchant terms. eSewa redirects users back to your site with sensitive tokens; transmitting these over HTTP exposes session hijacking risks. All payment callbacks and verification requests should occur over HTTPS. Let's Encrypt provides free certificates via Certbot on Ubuntu servers, removing cost as a barrier for Nepal-based deployments.

eSewa has broader bank coverage and higher user adoption among older demographics, while Khalti offers cleaner documentation and faster sandbox approval. For legal-tech portals serving diverse age groups, I often integrate both. Development effort is similar since neither provides official PHP SDKs. Choose based on your customer base rather than technical superiority; many Nepal-focused shops support both gateways simultaneously.

Create a dedicated payment_transactions table storing eSewa's transaction UUID, merchant reference, amount, status, callback payload hash, and timestamps. Never overwrite existing order records directly. Link payments to orders via foreign key with nullable constraints to handle pending states. Store raw callback JSON separately for audit trails. This separation allows reconciliation when eSewa reports differ from local state during dispute resolution.

Implement idempotent callback processing using transaction UUID deduplication. Set reasonable HTTP timeouts (30 seconds) for verification calls with exponential backoff retry logic. Queue verification jobs asynchronously so user-facing pages don't block on gateway latency. Display intermediate "processing" states rather than immediate failure messages. On several client projects, I've seen 2-5% of callbacks arrive delayed; synchronous processing loses legitimate payments during peak hours.

Not natively through standard merchant APIs. You must implement tokenized mandate flows requiring separate approval and additional compliance documentation. Most Nepal businesses handle subscriptions via manual invoice generation with saved customer references rather than automated recurring charges. Evaluate whether true automation justifies the regulatory overhead; many service businesses find scheduled email reminders with fresh payment links sufficient for retention.

Use eSewa's UAT environment with provided test cards covering success, failure, and timeout scenarios. Mock verification endpoints in unit tests to avoid external dependencies during CI runs. Test callback handlers with fixture payloads matching production signature formats. Verify edge cases like partial amounts, currency mismatches, and duplicate transaction IDs. Automated tests catch regression bugs faster than manual UAT cycles, especially after framework upgrades.

Download daily settlement CSVs from the merchant portal and import via artisan commands matching transaction UUIDs. Flag discrepancies where local status shows success but settlement excludes the transaction. Build admin interfaces showing unmatched records for manual review. Settlement delays of T+1 to T+3 are normal; design reconciliation to handle lagging data. On accounting platforms I've built, automated mismatch alerts reduce month-end closing time significantly compared to spreadsheet-based processes.

Since eSewa handles card data on their hosted pages, full PCI-DSS scope doesn't apply to your PHP app. However, you must still secure stored transaction references, encrypt secret keys at rest, restrict dashboard access, and maintain TLS everywhere. Avoid logging sensitive callback fields. Compliance burden is lower than direct card processing but not zero; document your security controls for future audits or enterprise client due diligence questionnaires.

Hardcoding currency as USD instead of NPR, ignoring timezone differences between UTC callbacks and BKT business hours, missing trailing slashes in configured return URLs, and deploying without opcache invalidation causing stale credential caches. Also verify that your server's system clock syncs via NTP; signature validation rejects requests with timestamp drift exceeding five minutes. These operational oversights cause more production incidents than flawed integration logic itself.

Core payment flow with callback verification takes 3-5 days including UAT testing. Add 2-3 days for reconciliation tooling, error handling, and edge case coverage. Budget extra time for merchant account approval which can stretch weeks depending on business documentation completeness. First-time integrators should expect double these estimates. Factor in ongoing maintenance for API version changes; eSewa updates signatures periodically requiring proactive monitoring rather than reactive fixes after checkout breaks.

Share this article

Quick Contact Options
Choose how you want to connect me: