Quickstart
Take your first payment end to end in the sandbox in a few minutes.
This walkthrough takes a payment from creation to a verified webhook — entirely in the sandbox, so no real money moves.
Before you start
You'll need, from the dashboard:
- A business with a test API key (
sk_test_…) - Your
Client-IdandBusiness-Id - A webhook URL pointed at your endpoint (a tunnel like ngrok works for local dev) and its signing secret
See Businesses & providers to set these up.
Create a payment
Call the sandbox with the test phone number that forces a successful payment
(01770618575 — see Testing):
curl -X POST https://sandbox.shadhinpay.pay/api/v1/payments \
-H "Client-Id: HM_4f8c2b1a" \
-H "Business-Id: HB_71c4a09e" \
-H "X-Api-Key: sk_test_aB3kC9...9xQ2" \
-H "X-Idempotency-Key: quickstart-order-1" \
-H "Content-Type: application/json" \
-d '{
"amount": "500.00",
"currency": "BDT",
"merchantTxnId": "quickstart-order-1",
"callbackUrl": "http://localhost:3000/return",
"customerPhone": "01770618575"
}'You get back a paymentId and a paymentUrl, with status: "PENDING".
Send the customer to checkout
Redirect the customer's browser to the paymentUrl from the response. In the
sandbox the outcome is decided by the phone number, so the payment will complete.
After paying, the customer returns to your callbackUrl.
Receive and verify the webhook
Your endpoint receives a payment.completed event. Verify its signature before
trusting it — see the Webhooks
guide for the full Node.js example:
if (!verifyWebhook(rawBody, req.headers['x-shadhinpay-signature'], secret)) {
return res.status(400).end() // reject forgeries
}
const event = JSON.parse(rawBody)
if (event.eventType === 'payment.completed') {
fulfilOrder(event.payload.merchantTxnId) // 'quickstart-order-1'
}
res.status(200).json({ status: 'received' }) // acknowledgeConfirm the status
As a belt-and-braces check (and in case you miss a webhook), read the payment:
curl https://sandbox.shadhinpay.pay/api/v1/payments/PAY_20260517_K8X3M2 \
-H "Client-Id: HM_4f8c2b1a" \
-H "Business-Id: HB_71c4a09e" \
-H "X-Api-Key: sk_test_aB3kC9...9xQ2"status is now COMPLETED. That's a full payment, end to end.
Don't fulfil on the redirect alone
The browser returning to your callbackUrl is just a UX signal. Treat the
verified webhook (or a status read) as the source of truth before releasing
goods.
Where to go next
- Authentication — keys, headers, live vs test
- Idempotency — retry safely
- Payments — the full payment reference
- Webhooks — events, payloads, verification
- Errors — every error code