এই API আলাদা হোস্টে। Withdraw endpoint-গুলো https://cashier.nagorikpay.com-এ চলে, এই ডকুমেন্টেশনের সাইটে নয়।
ভুল হোস্টে POST করলে 404 আসবে।
প্রতিটি request-এ চারটা header লাগবে। Merchant panel → Withdraw → Withdraw API পেজে আপনার key ও secret আছে।
| Header | Value |
X-API-Key | আপনার API key। Authorization: Bearer YOUR_KEY-ও চলবে। |
X-Timestamp | Unix timestamp (seconds)। আমাদের ঘড়ির সঙ্গে ৩০০ সেকেন্ডের বেশি ফারাক চলবে না। |
X-Nonce | প্রতি request-এ নতুন random string, ৮–৬৪ অক্ষর। একবার ব্যবহৃত nonce আর চলবে না। |
X-Signature | নিচের নিয়মে বানানো HMAC-SHA256, lowercase hex। |
Signing formula
HMAC-SHA256(
key = API_SECRET,
message = X-Timestamp + "." + X-Nonce + "." + RAW_REQUEST_BODY
)
Body অবশ্যই হুবহু। Signature পুরো raw body-র উপর হয় — JSON আবার encode করলে বা field-এর ক্রম বদলালে signature মিলবে না।
যে string পাঠাচ্ছেন, সেই string দিয়েই sign করুন।
PHP signer
$key = 'YOUR_API_KEY';
$secret = 'YOUR_API_SECRET';
$url = 'https://cashier.nagorikpay.com/api/v1/request';
$body = json_encode([
'operator' => 'bkash',
'amount' => 1000,
'recipient' => '01700000000',
'reference' => 'INV-1001',
], JSON_UNESCAPED_SLASHES);
$timestamp = (string) time();
$nonce = bin2hex(random_bytes(8));
$signature = hash_hmac('sha256', $timestamp . '.' . $nonce . '.' . $body, $secret);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $body,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-API-Key: ' . $key,
'X-Timestamp: ' . $timestamp,
'X-Nonce: ' . $nonce,
'X-Signature: ' . $signature,
],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
IP allow-list সেট করা থাকলে শুধু সেই IP/CIDR থেকেই call হবে। খালি রাখলে সব IP খোলা — কোনটা চালু আছে সেটা আপনার Withdraw API পেজে লেখা থাকে।
POST
https://cashier.nagorikpay.com/api/v1/request
| Field | Required | Note |
operator | হ্যাঁ | bkash, nagad, rocket, upay, tap |
amount | হ্যাঁ | ০-এর বেশি। ৩ দশমিক স্থান পর্যন্ত রাখা হয়। |
recipient | হ্যাঁ | যে নম্বরে টাকা যাবে। ৩০ অক্ষরে কাটা হয়। |
reference | না | আপনার নিজের reference — webhook-এ ফেরত আসে। |
idempotency_key | না | একই key আবার পাঠালে নতুন transaction হবে না, আগেরটাই ফেরত আসবে। সবসময় পাঠানোই ভালো। |
callback_url | না | ফলাফল জানানোর জন্য। অবশ্যই https:// হতে হবে। |
success_keyword | না | SMS-এ এই শব্দ থাকলে success ধরা হবে। না দিলে operator-এর ডিফল্ট শব্দ। |
brand_id | না | কোন brand-এর টাকা — হিসাবের জন্য। |
নম্বর কখনো পাঠাবেন না। কে call করছে সেটা API key থেকেই বোঝা যায়, তাই request-এ কোনো uid নেই — জাল করার মতো কিছু নেই।
Response — accepted
{
"status": "success",
"message": "Request accepted.",
"data": {
"transaction_id": "WD202609220001",
"reference": "INV-1001",
"status": "pending",
"operator": "bkash",
"amount": 1000,
"recipient": "01700000000",
"smstranx": "",
"error_message": "",
"created_at": "2026-09-22 14:05:11",
"completed_at": ""
}
}
status প্রথমে pending থাকে — হ্যান্ডসেট কাজ শেষ করলে বদলাবে। ফলাফল পেতে webhook ব্যবহার করুন, অথবা transaction_id দিয়ে poll করুন।
GET
https://cashier.nagorikpay.com/api/v1/transactions/{transaction_id}
Authentication header আগের মতোই — তবে GET-এ body নেই, তাই signature হয় timestamp + "." + nonce + "." + "" (খালি string)।
Request example
curl 'https://cashier.nagorikpay.com/api/v1/transactions/WD202609220001' \
-H 'X-API-Key: YOUR_API_KEY' \
-H 'X-Timestamp: 1789290000' \
-H 'X-Nonce: 9f2c1ab34de5' \
-H 'X-Signature: SIGNATURE_HEX'
Response ওই একই data shape-এ আসে। অন্য merchant-এর id আর না-থাকা id — দুটোতেই একই 404 No such transaction. আসে।
| Status | Meaning |
pending | গ্রহণ করা হয়েছে, হ্যান্ডসেট এখনো শুরু করেনি। |
processing | কোনো হ্যান্ডসেট নিয়ে USSD চালাচ্ছে। |
success | SMS মিলেছে, টাকা গেছে। |
failed | USSD বা SMS বলছে হয়নি। error_message দেখুন। |
cancelled | বাতিল করা হয়েছে। |
timeout | নির্ধারিত সময়ে SMS আসেনি। |
HTTP errors
| Code | When |
401 | Key নেই / ভুল key, timestamp ৩০০ সেকেন্ডের বাইরে, nonce আগে ব্যবহৃত, signature মেলেনি। |
403 | Key বন্ধ, account নিষ্ক্রিয়, withdraw access বন্ধ, অথবা IP allow-list-এ নেই। |
422 | operator অজানা, amount শূন্য বা কম, recipient খালি। |
429 | নির্ধারিত limit ছাড়িয়ে গেছে — কারণ message-এ লেখা থাকে। |
503 | Withdraw system এখনো ইনস্টল হয়নি। |
সবসময় {status, message, data?} shape-এ উত্তর আসে, পাশে আসল HTTP code-ও থাকে — যেকোনো একটা ধরে branch করতে পারেন।
Admin আপনার account-এ যত ceilings দিয়েছে, সেগুলো প্রতি request-এ যাচাই হয়। ছাড়িয়ে গেলে 429 আসে আর message-এ কারণ লেখা থাকে।
| Limit | What it caps |
| Per transaction | এক request-এ সর্বোচ্চ কত টাকা। |
| Per day | একদিনে মোট কত টাকা। |
| Transaction count | daily / monthly / total — কতগুলো transaction। |
| Devices | কতগুলো হ্যান্ডসেট approve করা যাবে। |
Approve করা হ্যান্ডসেট না থাকলে request pending-এ পড়ে থাকবে — কেউ নিয়ে কাজ করবে না। আগে Withdraw → Devices থেকে হ্যান্ডসেট approve করে নিন।
callback_url দিলে status বদলালেই সেখানে POST যাবে — JSON body, সঙ্গে signature header।
Headers
Content-Type: application/json
X-Timestamp: 1789290000
X-Nonce: 3f9c1ab34de5
X-Signature: SIGNATURE_HEX
X-Event: withdraw.success
Payload
{
"event": "withdraw.success",
"transaction_id": "WD202609220001",
"reference": "INV-1001",
"status": "success",
"operator": "bkash",
"amount": 1000,
"recipient": "01700000000",
"smstranx": "ABC123XYZ",
"completed_at": "2026-09-22 14:06:02"
}
Signing key আপনার API secret। নিয়ম একই — timestamp + "." + nonce + "." + raw body, তবে এখানে nonce আমরাই বানাই এবং X-Nonce header-এ পাঠাই।
PHP verification
$timestamp = $_SERVER['HTTP_X_TIMESTAMP'] ?? '';
$nonce = $_SERVER['HTTP_X_NONCE'] ?? '';
$given = $_SERVER['HTTP_X_SIGNATURE'] ?? '';
$rawBody = file_get_contents('php://input');
$expected = hash_hmac('sha256', $timestamp . '.' . $nonce . '.' . $rawBody, 'YOUR_API_SECRET');
// Signature সবসময় আসে — না এলে request আমাদের নয়।
if ($given === '' || abs(time() - (int) $timestamp) > 300 || !hash_equals($expected, $given)) {
http_response_code(401);
exit('Invalid signature');
}
$payload = json_decode($rawBody, true);
// Signature check-এর পরেই payment process করুন।
HTTPS বাধ্যতামূলক। http:// callback URL নেওয়া হয় না।
আর একই transaction_id একবারই process করুন — আবার এলেই ignore।
| Step | Where |
| Withdraw access চালু করান | Admin → Withdraw Access |
| API key ও secret নিন | Withdraw → Withdraw API |
| Callback URL ও IP allow-list বসান | একই পেজ |
| হ্যান্ডসেট approve করুন | Withdraw → Devices |
| App ইনস্টল করে login করান | ফোনে, Devices পেজের username ও password দিয়ে |
Withdraw access বন্ধ করলেই সাথে সাথে বন্ধ হয়ে যাবে — হ্যান্ডসেটও, API key-ও। কারণ access প্রতি call-এ আবার যাচাই হয়।