PayBangla

Developer portal · sandbox

PayBangla API

These examples call the routes this server implements. Test keys use the pb_test_ prefix. livePayment is always false. A customer transaction ID does not approve a payment.

API overview

Create a payment, send the customer to the hosted checkout, then read the status. The status becomes PENDING_VERIFICATION after submission and APPROVED only after an operator records an independent check.

Authentication

Send Authorization: Bearer pb_test_…. Create calls also require Idempotency-Key of 8 to 80 characters. The same key and body replay the first response. A different body returns 409. Revoked keys return 401. Live keys are not issued.

Create payment

POST /api/v1/payments
POST /api/v1/payment-links
POST /api/v1/payment-requests

Body fields: orderId, amount, optional currency (only BDT), description, customerName, customerPhone, customerReference, merchantReference, returnUrl, and expiresInHours from 1 to 168. The response includes paymentId, checkoutUrl, status, and expiresAt.

Node.js

const response = await fetch(process.env.PAYBANGLA_URL + "/api/v1/payments", {
  method: "POST",
  headers: {
    Authorization: "Bearer " + process.env.PAYBANGLA_TEST_KEY,
    "Content-Type": "application/json",
    "Idempotency-Key": "order-1001-attempt-1",
  },
  body: JSON.stringify({
    orderId: "ORDER-1001",
    amount: "1250.00",
    currency: "BDT",
    description: "Sandbox order",
  }),
});
const payment = await response.json();
// payment.status is AWAITING_PAYMENT
// payment.checkoutUrl is the hosted checkout
// payment.livePayment is false

PHP

$ch = curl_init($base . "/api/v1/payments");
curl_setopt_array($ch, [
  CURLOPT_POST => true,
  CURLOPT_HTTPHEADER => [
    "Authorization: Bearer " . getenv("PAYBANGLA_TEST_KEY"),
    "Content-Type: application/json",
    "Idempotency-Key: order-1001-attempt-1",
  ],
  CURLOPT_POSTFIELDS => json_encode([
    "orderId" => "ORDER-1001",
    "amount" => "1250.00",
    "currency" => "BDT",
  ]),
  CURLOPT_RETURNTRANSFER => true,
]);
$payment = json_decode(curl_exec($ch), true);

Python

import os, requests
payment = requests.post(
  os.environ["PAYBANGLA_URL"] + "/api/v1/payments",
  headers={
    "Authorization": "Bearer " + os.environ["PAYBANGLA_TEST_KEY"],
    "Idempotency-Key": "order-1001-attempt-1",
  },
  json={"orderId": "ORDER-1001", "amount": "1250.00", "currency": "BDT"},
  timeout=20,
).json()

Hosted checkout

Open checkoutUrl. It accepts the payment id or the original checkout token. Only enabled methods with an active receiving account are shown. Submission stores a tracking reference and sets PENDING_VERIFICATION.

Payment status

GET /api/v1/payments/:id
GET /api/v1/payments
GET /api/v1/payment-links/:id

Disable an unused link with POST /api/v1/payment-links/:id. After a customer submits, the amount cannot be changed and the link cannot be disabled.

Webhooks

The stored event type and the merchant-facing event name are both real. PAYMENT_SUCCESS is the same delivery as payment.approved. It is queued only after manual approval. The receiver must return a 2xx response. A failed response is retried with exponential backoff. Signatures use PayBangla-Signature: t=<unix>,v1=<hmac sha256 of timestamp.body> and expire after five minutes.

payment.created        PAYMENT_CREATED
payment.pending        PAYMENT_PENDING
payment.under_review   PAYMENT_UNDER_REVIEW
payment.approved       PAYMENT_SUCCESS
payment.rejected       PAYMENT_REJECTED
payment.expired        PAYMENT_EXPIRED
webhook.test           connectivity only

Events are written in the same database transaction as the payment change, then delivered by the webhook job. A delivery is marked delivered only after a 2xx response. Rotate the webhook secret from the merchant desk. The secret is not included in API responses after that response. Endpoint checks reject private addresses.

Error codes

401 missing or revoked key. 403 merchant not approved or live collection disabled. 409 duplicate order, idempotency conflict, or a second decision. 400 validation, including a non-BDT currency. 429 rate limit.

Sandbox testing

Register a merchant, open the verification link from signup, and wait for an operator to approve sandbox access. Generate a key from the merchant desk. It is shown once. The seeded bKash receiving number is a labeled sandbox fixture, not a live wallet.

Compliance disclosures