Need a hand?

Setting up the panel or the Android app? The merchant guide walks through it.

Merchant Guide

Direct Payment API No payment page

আপনার app বা bot থেকে payment request তৈরি করুন, customer-কে payment number ও exact amount দিন, তারপর TrxID submit করে payment verify করুন। Customer-এর জন্য কোনো payment page খুলবে না।

Base URL https://api.nagorikpay.com
  • 01Request তৈরি
  • 02Number-এ payment
  • 03TrxID submit
  • 04Paid বা pending response

1. Create payment request

POST
https://api.nagorikpay.com/api/payment/create-request

Compatibility alias: /api/payment/create-reqeust. Send the brand API key in the API-KEY header. The API never redirects the customer to a payment page.

Access: এই endpoint admin-এর অনুমোদন সাপেক্ষে। আপনার account-এ create-request access চালু না থাকলে valid API key দিয়েও payment request তৈরি হবে না — সাথে সাথে 403 এবং create-request is not enabled for this account. Please contact support. ফেরত আসবে। চালু করতে support-এ যোগাযোগ করুন।
Sandbox: use https://sandbox-api.nagorikpay.com/ with sandbox API keys and the sandbox database. Sandbox-ও admin-এর অনুমোদন সাপেক্ষে — অনুমোদন ছাড়া brand edit page থেকে sandbox key তৈরি করা যায় না, আর আগে তৈরি করা key-ও সাথে সাথে অচল হয়ে যায়। Never send production API keys or live customer payments to the sandbox domain.
Request example
curl -X POST 'https://api.nagorikpay.com/api/payment/create-request' \
  -H 'API-KEY: YOUR_BRAND_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "amount": "500",
    "payment_method": "bkash",
    "account_type": "personal",
    "cus_name": "Customer Name",
    "cus_phone": "01700000000",
    "webhook_url": "https://merchant.example.com/nagorikpay-webhook",
    "metadata": {"order_id": "ORDER-10025"}
    }'
Success response
{
  "status": "success",
  "payment_id": "NP202609130001",
  "payment_method": "bkash",
  "account_type": "personal",
  "payment_number": "017XXXXXXXX",
  "amount": "500.00",
  "charge": "10.00",
  "total_amount": "510.00",
  "pay_amount": "510.00",
  "breakdown": {
    "amount": "500.00",
    "charge": "10.00",
    "total_payable": "510.00"
  },
  "currency": "BDT",
  "expires_at": "2026-09-13 13:00:00"
}
Customer-কে payment_number, pay_amount (= total_amount, অর্থাৎ amount + charge) এবং payment_id দেখাবেন। শুধু amount দেখালে customer কম টাকা পাঠাবে এবং payment কখনো verify হবে না — যে টাকা gateway-র নিজের checkout page-এ "Total Payable" লেখা থাকে, customer-কে ঠিক সেই টাকাই পাঠাতে হবে। Request ৩০ মিনিট পর expire হবে।

2. Submit transaction ID

POST
https://api.nagorikpay.com/api/payment-submit-transaction
Request example
curl -X POST 'https://api.nagorikpay.com/api/payment-submit-transaction' \
  -H 'API-KEY: YOUR_BRAND_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "payment_id": "NP202609130001",
    "transaction_id": "ABC123XYZ"
    }'
Server stored SMS/provider data-এর সঙ্গে TrxID মিলিয়ে exact amount check করবে। Duplicate TrxID reject হবে এবং client পাঠানো amount কখনো trust করা হবে না।

Statuses and responses

StatusMeaning
paidTransaction matched and payment completed.
pendingAccepted for review or provider verification is not complete yet.
failedTransaction ID, amount or request could not be verified.
expiredThe 30-minute payment request expired.

Response fields can include payment_id, transaction_id, amount, pay_amount, charge, status, message and the original verification details.

Every submit response — paid, pending, failed or expired — carries amount, charge, total_amount and pay_amount together, so the create response and the submit response never disagree about the money.

amount is always the merchant-requested amount, never the payable one — show pay_amount to the customer. When an amount mismatch is detected, expected_amount (what the request needs) and received_amount (what the provider SMS says arrived) are returned as well, so the difference can be shown without any guessing.

Signed webhook

If webhook_url was supplied, the same payment status notifications are delivered to it. Existing form fields remain unchanged for compatibility. New headers are added:

Webhook headers
Content-Type: application/x-www-form-urlencoded
X-NagorikPay-Timestamp: 1789290000
X-NagorikPay-Event: auto-completed
X-NagorikPay-Signature: sha256=HEX_SIGNATURE

The signing key is your brand's Webhook Secret if you have generated one (merchant panel → Brands → edit brand → Webhook Secret), and your brand API key if you have not. The Webhook Secret is the recommended setup: it is a separate value, so a leaked API key can no longer be used to forge webhooks.

Signing formula
timestamp + "." + raw_form_body
HMAC-SHA256(message, webhook_secret_or_api_key)

Webhook delivery attempts, response code, response body and payload are recorded in the merchant Webhook Logs page.

Backward compatibility: every webhook already carries this signature and the form fields are unchanged, so existing integrations keep working untouched — X-NagorikPay-Signature is an extra header, not a replacement. If you verify with the brand API key today, that continues to work; generating a Webhook Secret is an upgrade you can make whenever you are ready, and you update your verifier in the same step.

Verify the webhook

Read the raw request body — not a re-encoded array, and never a parsed-then-rebuilt string, since the signature covers the exact bytes that were sent. Reject requests with a timestamp older than five minutes, then compare the signature with a constant-time comparison.

PHP verification example
$timestamp = $_SERVER['HTTP_X_NAGORIKPAY_TIMESTAMP'] ?? '';
$given = $_SERVER['HTTP_X_NAGORIKPAY_SIGNATURE'] ?? '';
$rawBody = file_get_contents('php://input');
$expected = 'sha256=' . hash_hmac(
    'sha256',
    $timestamp . '.' . $rawBody,
    'YOUR_WEBHOOK_SECRET_OR_API_KEY'
);

// A signature is always sent — a missing one means the request is not ours.
if ($given === '' || abs(time() - (int) $timestamp) > 300 || !hash_equals($expected, $given)) {
    http_response_code(401);
    exit('Invalid signature');
}

parse_str($rawBody, $payload);
// Process payment only after the signature check.
Replay protection: store the payment ID/event combination after processing and ignore the same completed event twice.