Payments
Create a payment, redirect the customer to checkout, track its status, and list payments.
A payment represents one attempt to collect money from a customer. You create it, send the customer to the hosted checkout, and ShadhinPay tells you the outcome via status and webhooks.
The flow at a glance
POST /payments) and get a paymentUrl.paymentUrl to pay with bKash or Nagad.callbackUrl, and you receive a payment.completed webhook.Create a payment
POST /api/v1/paymentsRequires the auth headers and an
X-Idempotency-Key.
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
amount | string | yes | Decimal string, "10"–"200000" BDT |
currency | string | yes | "BDT" |
merchantTxnId | string | yes | Your order reference; unique per business |
callbackUrl | string | yes | Where the customer returns after paying (HTTPS in live) |
preferredVendor | string | no | BKASH or NAGAD — skips the picker |
customerPhone | string | no | Helps pre-fill checkout |
customerEmail | string | no | |
description | string | no | Shown on checkout |
valueA–valueD | string | no | Your own metadata, echoed back on reads/webhooks |
expiresInMinutes | integer | no | Checkout window; default 15 |
isSandbox | boolean | no | Only valid with a test key |
Example
curl -X POST https://api.shadhinpay.pay/api/v1/payments \
-H "Client-Id: HM_4f8c2b1a" \
-H "Business-Id: HB_71c4a09e" \
-H "X-Api-Key: sk_live_aB3kC9...9xQ2" \
-H "X-Idempotency-Key: order-7831-attempt-1" \
-H "Content-Type: application/json" \
-d '{
"amount": "1500.50",
"currency": "BDT",
"merchantTxnId": "order-7831",
"callbackUrl": "https://shop.example.com/checkout/return",
"preferredVendor": "BKASH",
"customerPhone": "8801712345678",
"description": "Order #7831",
"valueA": "campaign=summer",
"expiresInMinutes": 15
}'Response — 201 Created
{
"status": "success",
"message": "Payment initiated successfully",
"data": {
"paymentId": "PAY_20260517_K8X3M2",
"merchantTxnId": "order-7831",
"amount": "1500.50",
"currency": "BDT",
"status": "PENDING",
"vendor": "BKASH",
"paymentUrl": "https://com.shadhinpay.pay/pay/PAY_20260517_K8X3M2",
"description": "Order #7831",
"refundTotal": "0.00",
"initiatedAt": "2026-05-17T10:00:00Z",
"completedAt": null,
"expiresAt": "2026-05-17T10:15:00Z",
"valueA": "campaign=summer"
}
}Your next step: redirect the customer's browser to paymentUrl. That hosted
page either jumps straight to the chosen provider (if you set preferredVendor) or
shows a picker. After paying, the customer is sent back to your callbackUrl.
Don't fulfil on the redirect alone
The return to your callbackUrl is a UX signal, not proof of payment. Always
confirm the outcome from the webhook or by reading the
payment status before releasing goods.
Payment statuses
| Status | Meaning |
|---|---|
PENDING | Created; waiting for the customer to approve at the provider |
COMPLETED | Paid successfully |
FAILED | The provider rejected the payment |
CANCELLED | The customer cancelled |
EXPIRED | The checkout window passed without payment |
REFUNDED | Fully refunded (partial refunds keep status COMPLETED) |
You may also briefly see INITIATED — an internal pre-PENDING state — in reads
immediately after creation.
Get a payment
GET /api/v1/payments/{paymentId}Returns the full payment object, including current status and refundTotal.
Use this to confirm an outcome or reconcile against your records.
curl https://api.shadhinpay.pay/api/v1/payments/PAY_20260517_K8X3M2 \
-H "Client-Id: HM_4f8c2b1a" \
-H "Business-Id: HB_71c4a09e" \
-H "X-Api-Key: sk_live_aB3kC9...9xQ2"List payments
GET /api/v1/paymentsSupports pagination plus filters:
| Query param | Notes |
|---|---|
status | Filter by payment status |
vendor | BKASH or NAGAD |
from / to | ISO-8601 date-time range |
amountMin / amountMax | Numeric bounds |
merchantTxnId | Find by your order reference |
page / size | Page index (from 0) and page size (max 100) |
The list response may include a summary block with successCount,
failedCount, and totalAmount for the filtered set.