NAGAD API v3.3

Developer Portal & Reference

Production Ready SDKs

Complete Full-Flow SDK Implementations

নিচে প্রতিটি জনপ্রিয় প্রোগ্রামিং ল্যাঙ্গুয়েজ ও ফ্রেমওয়ার্কের (PHP, Laravel, Node.js, Python, Java) জন্য সরাসরি কপি করে ব্যবহার করার মতো সম্পূর্ণ ফাংশনাল ও প্রোডাকশন-রেডি SDK ক্লাস দেওয়া হলো।

1. Pure PHP Nagad SDK Class (No framework required)

PHP 7.4+ & 8.x
NagadPaymentGateway.php
<?php

class NagadPaymentGateway {
    private string $merchantId;
    private string $merchantPrivateKey;
    private string $nagadPublicKey;
    private string $baseUrl;
    private string $clientIp;

    public function __construct(string $merchantId, string $merchantPrivateKey, string $nagadPublicKey, bool $isSandbox = true, string $clientIp = '127.0.0.1') {
        $this->merchantId = $merchantId;
        $this->merchantPrivateKey = $merchantPrivateKey;
        $this->nagadPublicKey = $nagadPublicKey;
        $this->baseUrl = $isSandbox ? 'http://sandbox.mynagad.com:10080' : 'https://api.mynagad.com';
        $this->clientIp = $clientIp;
    }

    /**
     * Create Checkout URL in one single call (Handles Initialize + Place Order)
     */
    public function createPayment(string $orderId, float $amount, string $callbackUrl, array $additionalInfo = []): string {
        // Step 1: Initialize
        $initData = $this->initializeOrder($orderId);
        
        // Step 2: Place Order and get redirect URL
        return $this->placeOrder($initData['paymentRefId'], $initData['challenge'], $orderId, $amount, $callbackUrl, $additionalInfo);
    }

    private function initializeOrder(string $orderId): array {
        $dateTime = date('YmdHis');
        $challenge = bin2hex(random_bytes(20)); // 40 chars

        $sensitiveJson = json_encode([
            'merchantId' => $this->merchantId,
            'datetime' => $dateTime,
            'orderId' => $orderId,
            'challenge' => $challenge
        ]);

        $sensitiveData = $this->encrypt($sensitiveJson);
        $signature = $this->sign($sensitiveJson);

        $url = "{$this->baseUrl}/remote-payment-gateway-1.0/api/dfs/check-out/initialize/{$this->merchantId}/{$orderId}?locale=BN";

        $response = $this->httpRequest('POST', $url, [
            'dateTime' => $dateTime,
            'sensitiveData' => $sensitiveData,
            'signature' => $signature
        ]);

        $resDecrypted = $this->decrypt($response['sensitiveData']);
        $resJson = json_decode($resDecrypted, true);

        return [
            'paymentRefId' => $resJson['paymentReferenceId'],
            'challenge' => $resJson['challenge'] ?? $resJson['random']
        ];
    }

    private function placeOrder(string $paymentRefId, string $challenge, string $orderId, float $amount, string $callbackUrl, array $additionalInfo): string {
        $sensitiveJson = json_encode([
            'merchantId' => $this->merchantId,
            'orderId' => $orderId,
            'currencyCode' => '050',
            'amount' => number_format($amount, 2, '.', ''),
            'challenge' => $challenge
        ]);

        $sensitiveData = $this->encrypt($sensitiveJson);
        $signature = $this->sign($sensitiveJson);

        $url = "{$this->baseUrl}/remote-payment-gateway-1.0/api/dfs/check-out/complete/{$paymentRefId}";

        $payload = [
            'sensitiveData' => $sensitiveData,
            'signature' => $signature,
            'merchantCallbackURL' => $callbackUrl,
            'additionalMerchantInfo' => !empty($additionalInfo) ? $additionalInfo : ['serviceName' => 'Web Checkout']
        ];

        $response = $this->httpRequest('POST', $url, $payload);
        return $response['callBackUrl'];
    }

    /**
     * Server-to-server status verification
     */
    public function verifyPayment(string $paymentRefId): array {
        $url = "{$this->baseUrl}/remote-payment-gateway-1.0/api/dfs/verify/payment/{$paymentRefId}";
        return $this->httpRequest('GET', $url);
    }

    // --- Crypto Helpers ---
    private function encrypt(string $data): string {
        $pubKey = openssl_pkey_get_public($this->nagadPublicKey);
        openssl_public_encrypt($data, $encrypted, $pubKey, OPENSSL_PKCS1_PADDING);
        return base64_encode($encrypted);
    }

    private function sign(string $data): string {
        $privKey = openssl_pkey_get_private($this->merchantPrivateKey);
        openssl_sign($data, $signature, $privKey, OPENSSL_ALGO_SHA1);
        return base64_encode($signature);
    }

    private function decrypt(string $base64Cipher): string {
        $privKey = openssl_pkey_get_private($this->merchantPrivateKey);
        openssl_private_decrypt(base64_decode($base64Cipher), $decrypted, $privKey, OPENSSL_PKCS1_PADDING);
        return $decrypted;
    }

    private function httpRequest(string $method, string $url, array $body = []): array {
        $ch = curl_init($url);
        $headers = [
            'Content-Type: application/json',
            'X-KM-IP-V4: ' . $this->clientIp,
            'X-KM-Client-Type: PC_WEB',
            'X-KM-Api-Version: v-0.2.0'
        ];

        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

        if ($method === 'POST') {
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
        }

        $res = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        return json_decode($res, true) ?? [];
    }
}

2. Laravel Integration (Config, Service & Controller)

Laravel 9, 10, 11+
config/nagad.php
<?php

return [
    'sandbox' => env('NAGAD_SANDBOX', true),
    'merchant_id' => env('NAGAD_MERCHANT_ID', ''),
    'merchant_private_key' => env('NAGAD_MERCHANT_PRIVATE_KEY', ''),
    'nagad_public_key' => env('NAGAD_PUBLIC_KEY', ''),
    'base_url' => env('NAGAD_SANDBOX', true) 
        ? 'http://sandbox.mynagad.com:10080' 
        : 'https://api.mynagad.com',
];
app/Http/Controllers/NagadPaymentController.php
<?php

namespace App\Http\Controllers;

use App\Services\NagadPaymentService;
use App\Models\Order;
use Illuminate\Http\Request;

class NagadPaymentController extends Controller {

    protected NagadPaymentService $nagad;

    public function __construct(NagadPaymentService $nagad) {
        $this->nagad = $nagad;
    }

    public function pay(Request $request, $orderId) {
        $order = Order::findOrFail($orderId);

        $redirectUrl = $this->nagad->createPayment([
            'orderId' => 'ORD_' . $order->id,
            'amount' => $order->total_amount,
            'callbackUrl' => route('nagad.callback'),
            'clientIp' => $request->ip()
        ]);

        return redirect()->away($redirectUrl);
    }

    public function callback(Request $request) {
        $paymentRefId = $request->query('payment_ref_id');
        $status = $request->query('status');

        if (!$paymentRefId || strtolower($status) !== 'success') {
            return redirect()->route('checkout.failed')->with('error', 'Payment was not completed.');
        }

        // Server-to-Server Verification
        $verification = $this->nagad->verifyPayment($paymentRefId, $request->ip());

        if ($verification['status'] === 'Success' && $verification['statusCode'] === '00_000_00') {
            $orderId = str_replace('ORD_', '', $verification['orderId']);
            $order = Order::findOrFail($orderId);

            if ((float)$order->total_amount === (float)$verification['amount']) {
                $order->update([
                    'status' => 'paid',
                    'transaction_id' => $verification['issuerPaymentRefNo'],
                    'payment_method' => 'nagad'
                ]);

                return redirect()->route('checkout.success', ['order' => $order->id]);
            }
        }

        return redirect()->route('checkout.failed')->with('error', 'Transaction verification failed.');
    }
}

3. Node.js & Express Implementation

ESM / CommonJS
nagadService.js
const crypto = require('crypto');
const axios = require('axios');

class NagadService {
  constructor({ merchantId, merchantPrivateKey, nagadPublicKey, isSandbox = true }) {
    this.merchantId = merchantId;
    this.merchantPrivateKey = merchantPrivateKey;
    this.nagadPublicKey = nagadPublicKey;
    this.baseUrl = isSandbox ? 'http://sandbox.mynagad.com:10080' : 'https://api.mynagad.com';
  }

  async createPayment({ orderId, amount, callbackUrl, clientIp }) {
    // 1. Initialize
    const challenge = crypto.randomBytes(20).toString('hex');
    const now = new Date();
    const pad = (n) => String(n).padStart(2, '0');
    const dateTime = `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;

    const initSensitive = JSON.stringify({
      merchantId: this.merchantId,
      datetime: dateTime,
      orderId,
      challenge,
    });

    const initEncrypted = this.encrypt(initSensitive);
    const initSigned = this.sign(initSensitive);

    const initRes = await axios.post(
      `${this.baseUrl}/remote-payment-gateway-1.0/api/dfs/check-out/initialize/${this.merchantId}/${orderId}?locale=BN`,
      { dateTime, sensitiveData: initEncrypted, signature: initSigned },
      {
        headers: {
          'Content-Type': 'application/json',
          'X-KM-IP-V4': clientIp,
          'X-KM-Client-Type': 'PC_WEB',
          'X-KM-Api-Version': 'v-0.2.0',
        },
      }
    );

    const decryptedInit = JSON.parse(this.decrypt(initRes.data.sensitiveData));
    const paymentRefId = decryptedInit.paymentReferenceId;
    const returnedChallenge = decryptedInit.challenge || decryptedInit.random;

    // 2. Place Order
    const placeSensitive = JSON.stringify({
      merchantId: this.merchantId,
      orderId,
      currencyCode: '050',
      amount: Number(amount).toFixed(2),
      challenge: returnedChallenge,
    });

    const placeEncrypted = this.encrypt(placeSensitive);
    const placeSigned = this.sign(placeSensitive);

    const placeRes = await axios.post(
      `${this.baseUrl}/remote-payment-gateway-1.0/api/dfs/check-out/complete/${paymentRefId}`,
      {
        sensitiveData: placeEncrypted,
        signature: placeSigned,
        merchantCallbackURL: callbackUrl,
        additionalMerchantInfo: { serviceName: 'Online Store' },
      },
      {
        headers: {
          'Content-Type': 'application/json',
          'X-KM-IP-V4': clientIp,
          'X-KM-Client-Type': 'PC_WEB',
          'X-KM-Api-Version': 'v-0.2.0',
        },
      }
    );

    return placeRes.data.callBackUrl;
  }

  async verifyPayment(paymentRefId, clientIp) {
    const res = await axios.get(
      `${this.baseUrl}/remote-payment-gateway-1.0/api/dfs/verify/payment/${paymentRefId}`,
      {
        headers: {
          'X-KM-IP-V4': clientIp,
          'X-KM-Client-Type': 'PC_WEB',
          'X-KM-Api-Version': 'v-0.2.0',
        },
      }
    );
    return res.data;
  }

  encrypt(plainText) {
    return crypto.publicEncrypt({ key: this.nagadPublicKey, padding: crypto.constants.RSA_PKCS1_PADDING }, Buffer.from(plainText)).toString('base64');
  }

  sign(plainText) {
    const signer = crypto.createSign('RSA-SHA1');
    signer.update(plainText);
    signer.end();
    return signer.sign(this.merchantPrivateKey, 'base64');
  }

  decrypt(base64Cipher) {
    return crypto.privateDecrypt({ key: this.merchantPrivateKey, padding: crypto.constants.RSA_PKCS1_PADDING }, Buffer.from(base64Cipher, 'base64')).toString('utf8');
  }
}

module.exports = NagadService;

4. Python (pycryptodome & requests)

Python 3.8+
nagad_gateway.py
import datetime
import secrets
import json
import base64
import requests
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5 as Cipher_PKCS1_v1_5
from Crypto.Signature import PKCS1_v1_5 as Signature_PKCS1_v1_5
from Crypto.Hash import SHA1

class NagadGateway:
    def __init__(self, merchant_id: str, merchant_private_key: str, nagad_public_key: str, is_sandbox: bool = True):
        self.merchant_id = merchant_id
        self.merchant_priv_key = RSA.import_key(merchant_private_key)
        self.nagad_pub_key = RSA.import_key(nagad_public_key)
        self.base_url = "http://sandbox.mynagad.com:10080" if is_sandbox else "https://api.mynagad.com"

    def create_payment(self, order_id: str, amount: float, callback_url: str, client_ip: str = "127.0.0.1") -> str:
        # Step 1: Initialize
        date_time = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
        challenge = secrets.token_hex(20)

        init_payload = json.dumps({
            "merchantId": self.merchant_id,
            "datetime": date_time,
            "orderId": order_id,
            "challenge": challenge
        })

        init_headers = {
            "Content-Type": "application/json",
            "X-KM-IP-V4": client_ip,
            "X-KM-Client-Type": "PC_WEB",
            "X-KM-Api-Version": "v-0.2.0"
        }

        init_res = requests.post(
            f"{self.base_url}/remote-payment-gateway-1.0/api/dfs/check-out/initialize/{self.merchant_id}/{order_id}?locale=BN",
            headers=init_headers,
            json={
                "dateTime": date_time,
                "sensitiveData": self._encrypt(init_payload),
                "signature": self._sign(init_payload)
            }
        ).json()

        decrypted_init = json.loads(self._decrypt(init_res["sensitiveData"]))
        payment_ref_id = decrypted_init["paymentReferenceId"]
        returned_challenge = decrypted_init.get("challenge") or decrypted_init.get("random")

        # Step 2: Complete / Place Order
        place_payload = json.dumps({
            "merchantId": self.merchant_id,
            "orderId": order_id,
            "currencyCode": "050",
            "amount": f"{amount:.2f}",
            "challenge": returned_challenge
        })

        place_res = requests.post(
            f"{self.base_url}/remote-payment-gateway-1.0/api/dfs/check-out/complete/{payment_ref_id}",
            headers=init_headers,
            json={
                "sensitiveData": self._encrypt(place_payload),
                "signature": self._sign(place_payload),
                "merchantCallbackURL": callback_url,
                "additionalMerchantInfo": {"serviceName": "Python Checkout"}
            }
        ).json()

        return place_res["callBackUrl"]

    def verify_payment(self, payment_ref_id: str, client_ip: str = "127.0.0.1") -> dict:
        headers = {
            "X-KM-IP-V4": client_ip,
            "X-KM-Client-Type": "PC_WEB",
            "X-KM-Api-Version": "v-0.2.0"
        }
        res = requests.get(
            f"{self.base_url}/remote-payment-gateway-1.0/api/dfs/verify/payment/{payment_ref_id}",
            headers=headers
        )
        return res.json()

    def _encrypt(self, text: str) -> str:
        cipher = Cipher_PKCS1_v1_5.new(self.nagad_pub_key)
        return base64.b64encode(cipher.encrypt(text.encode('utf-8'))).decode('utf-8')

    def _sign(self, text: str) -> str:
        signer = Signature_PKCS1_v1_5.new(self.merchant_priv_key)
        h = SHA1.new(text.encode('utf-8'))
        return base64.b64encode(signer.sign(h)).decode('utf-8')

    def _decrypt(self, cipher_b64: str) -> str:
        cipher = Cipher_PKCS1_v1_5.new(self.merchant_priv_key)
        return cipher.decrypt(base64.b64decode(cipher_b64), None).decode('utf-8')