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')