API Documentation

Base URL: https://jatrapay.agi.bd

Go to Dashboard → Brands → open your Brand and copy the Brand API Key (bk_…). Send it in the header of every request:

x-brand-key: bk_xxxxxxxxxxxxxxxx

⚠️ Keep this key on your server only — never in browser code or a mobile app.

POST/api/v1/payments

Creates a new payment link. Send your customer to pay_url.

curl -X POST https://jatrapay.agi.bd/api/v1/payments \
  -H "x-brand-key: bk_xxx" -H "Content-Type: application/json" \
  -d '{
    "amount": 500,
    "order_id": "ORD-1001",
    "invoice_name": "Premium Plan",
    "gateway": "bkash",
    "callback_url": "https://yoursite.com/webhook/jatrapay"
  }'

Response:

{ "success": true,
  "token": "9f3c…",
  "pay_url": "https://jatrapay.agi.bd/pay/9f3c…",
  "expires_at": 1767225600 }

amount is required (1–1,000,000). If callback_url is omitted, the Brand's default callback is used. Links are valid for 24 hours by default.

GET/api/v1/payments/:token

Returns the payment status (pending, paid, expired).

{ "success": true, "token": "9f3c…", "status": "paid", "amount": 500,
  "order_id": "ORD-1001", "gateway": "bkash", "trx_id": "9K3X1ABCDE", "paid_at": 1767139200 }

GET/api/v1/payments?status=paid&page=1&limit=20 — lists all of your payments.

Webhook (callback)

When a payment is verified, a POST is sent to your callback_url (up to 3 attempts on failure). You must verify the signature:

POST https://yoursite.com/webhook/jatrapay
x-jatrapay-signature: <HMAC-SHA256 hex of raw body, key = Brand API Key>

{ "event": "payment.paid", "status": "paid", "token": "9f3c…",
  "order_id": "ORD-1001", "amount": 500, "trx_id": "9K3X1ABCDE",
  "gateway": "bkash", "paid_at": 1767139200 }

Node.js:

const crypto = require('crypto');
app.post('/webhook/jatrapay', express.raw({ type: '*/*' }), (req, res) => {
  const sig = crypto.createHmac('sha256', process.env.JATRAPAY_KEY).update(req.body).digest('hex');
  if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(req.headers['x-jatrapay-signature'] || '')))
    return res.sendStatus(401);
  const data = JSON.parse(req.body);   // mark order data.order_id as paid
  res.sendStatus(200);
});

PHP:

<?php
$raw = file_get_contents('php://input');
$sig = hash_hmac('sha256', $raw, getenv('JATRAPAY_KEY'));
if (!hash_equals($sig, $_SERVER['HTTP_X_JATRAPAY_SIGNATURE'] ?? '')) { http_response_code(401); exit; }
$data = json_decode($raw, true);   // mark $data['order_id'] as paid

For safety, callbacks are only sent to public (https) hosts. For final confirmation you can re-check the status with GET /api/v1/payments/:token.

POST/api/v1/verify

Use this on your own checkout: when a customer submits a TRX ID and amount, it is matched against the received SMS and the amount is credited to the Brand wallet. Enable Verify API on the Brand page to get a bapi_… key.

curl -X POST https://jatrapay.agi.bd/api/v1/verify \
  -H "x-verify-key: bapi_xxx" -H "Content-Type: application/json" \
  -d '{ "trx_id": "9K3X1ABCDE", "amount": 500, "gateway": "bkash" }'
{ "success": true, "trx_id": "9K3X1ABCDE", "amount": 500, "gateway": "bkash", "balance": 1500 }

The legacy POST /api/brand/verify (header x-brand-api-key) still works.

Android App API

Header: x-jatrapay-key: mp_… (from Dashboard → Settings)

GET  /api/ping                 → { "status": "ok" }          (heartbeat, ?device_name=…)
POST /api/verify               { gmail, api_key }           (app login check)
POST /api/sms/receive          { transaction_id, amount, gateway, sender, raw_sms, timestamp, device_name }
POST /api/log                  { log, device_name }

Errors

Every error has this shape:

{ "success": false, "error": "message", "code": "used" }

code: not_found SMS not received yet · used TRX already used · amount_mismatch · expired · invalid_trx. HTTP 401 = invalid key, 429 = too many requests.