Production MFS Payment
Integration Reference.
Integrate SwapnoPay directly using standard HTTPS REST requests, HMAC-SHA256 webhooks, and the hosted Checkout Widget (`widget.html`). No third-party SDK packages or CMS plugins required.
Platform Mechanism & Architecture
SwapnoPay unifies carrier MFS SMS verification on Android, merchant databases, and web checkouts into a high-speed settlement pipeline.
Merchant Android workers intercept raw carrier MFS SMS (bKash, Nagad, Rocket, Upay) and perform atomic regex extraction of Transaction ID, amount, and sender phone.
Platform Owner Admin Supabase database manages API key peppered HMAC digests, platform fee accounting, idempotency guards, and centralized payment telemetry.
Express.js + Socket.io server emits instant payment_status events (PAID, FAILED, CANCELLED) to listening web checkout widgets.
System Sequence & Flowchart
End-to-end payment lifecycle from checkout initialization to carrier match and webhook dispatch.
┌─────────────────┐ ┌────────────────────┐ ┌────────────────────────┐
│ Customer Web │ │ SwapnoPay Backend │ │ Merchant Android Phone │
│ Checkout Widget │ │ (Node + Socket.io) │ │ (Carrier SMS Receiver) │
└────────┬────────┘ └─────────┬──────────┘ └───────────┬────────────┘
│ │ │
1. Join Order Room │ │
├─────────────────────────>│ │
│ │ │
2. Customer Sends Money (bKash) │ │
│ │ │
3. Android Worker Intercepts SMS │ │
│ │ 4. Regex Pattern Match │
│ │ TxID, Amount, Phone │
│ │<─────────────────────────────┤
│ │ 5. POST /v1/payment/verify │
│ │ │
6. Socket.io 'payment_status' │ │
(Status = PAID, Redirect URL) │ │
│<─────────────────────────┤ │
│ │ │
7. Instant Redirect to Success Page│ │
│ │ │
Authentication & Headers
Authenticate API calls using your Secret Key and protect against duplicate transactions with idempotency.
| Header Name | Type | Description |
|---|---|---|
| X-Admin-Secret | string | Your merchant API Secret Key starting with sk_live_... |
| Idempotency-Key | string (UUID) | Unique client request token to guarantee each charge is executed exactly once. |
| Content-Type | string | Must be set to application/json. |
Core API Endpoints Reference
Production HTTP endpoints for payment creation, instant verification, status queries, and dynamic hosted forms.
/v1/payment/create
Creates a new payment checkout session and generates the hosted payment link.
{
"order_id": "ORD-2026-9812",
"amount": 1250.00,
"currency": "BDT",
"customer_name": "Tanvir Hasan",
"customer_phone": "01712963652",
"customer_email": "tanvir@example.com",
"payment_method": "bKash",
"redirect_url": "https://merchant.example.com/checkout/success",
"cancel_url": "https://merchant.example.com/checkout/cancel"
}
{
"status": "SUCCESS",
"code": 200,
"data": {
"order_id": "ORD-2026-9812",
"payment_url": "https://pay.swapnopay.top/pay/ORD-2026-9812",
"assigned_gateway_number": "01784992118",
"payable_amount": 1250.00,
"expires_at": "2026-09-07T21:15:00Z"
}
}
/v1/payment/verify
Invoked by the carrier SMS receiver to verify transaction parameters and settle order.
{
"order_id": "ORD-2026-9812",
"tran_id": "9H8B7G6F5E",
"sender_phone": "01712963652",
"amount": 1250.00,
"payment_method": "bKash"
}
/v1/payment/status/{orderId}
Inspect live settlement status (PAID, PENDING, EXPIRED).
/v1/hosted-form/submit
Dynamic hosted form submission supporting custom date pickers, dropdowns, and file upload attachments.
| Error Code | HTTP | Description & Resolution |
|---|---|---|
| ERR_INVALID_HMAC | 401 | HMAC-SHA256 signature verification failed. Check your webhook secret. |
| ERR_ORDER_EXPIRED | 400 | Order payment lifetime (default 10 min) elapsed before payment completion. |
| ERR_DUPLICATE_IDEMPOTENCY | 409 | Request with this Idempotency-Key was already processed. Read cached order. |
| ERR_INSUFFICIENT_AMOUNT | 422 | Customer paid less than order invoice. Transaction flagged for manual appeal. |
| ERR_GATEWAY_OFFLINE | 503 | No Android receiver phone currently online for requested payment gateway. |
Webhooks & HMAC-SHA256 Signature Security
SwapnoPay signs all outbound webhook notifications with HMAC-SHA256 in the X-SwapnoPay-Signature header (t=<timestamp>,v1=<hmac>).
Node.js Signature Verification (Zero SDK — Built-in node:crypto):
const crypto = require('node:crypto');
function verifySwapnoPayWebhook(rawBodyString, signatureHeader, webhookSecret) {
const parts = Object.fromEntries(
String(signatureHeader || '').split(',').map(p => p.trim().split('='))
);
if (!parts.t || !parts.v1) return false;
// Replay protection: reject timestamps older than 5 minutes (300s)
if (Math.abs(Math.floor(Date.now() / 1000) - Number(parts.t)) > 300) return false;
const expected = crypto
.createHmac('sha256', webhookSecret)
.update(`${parts.t}.${rawBodyString}`, 'utf8')
.digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(parts.v1, 'hex'));
}
Python Signature Verification (Built-in hmac & hashlib):
import hmac
import hashlib
import time
def verify_swapnopay_webhook(raw_body: str, signature_header: str, secret: str) -> bool:
parts = dict(item.strip().split("=", 1) for item in signature_header.split(",") if "=" in item)
ts = int(parts.get("t", "0"))
sig = parts.get("v1", "")
if not ts or not sig or abs(int(time.time()) - ts) > 300:
return False
signed_payload = f"{ts}.{raw_body}".encode("utf-8")
expected = hmac.new(secret.encode("utf-8"), signed_payload, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, sig)
Enterprise Production Deployment & Go-Live Guideline
Mandatory production architecture, security controls, Android SIM Worker configuration, and reconciliation checklist.
architecture 1. Direct HTTP REST + Hosted Widget
SwapnoPay requires zero third-party SDKs or CMS plugins. Your backend creates orders via standard HTTPS POST /v1/payment/create (or /v1/developer/sandbox/order) and directs customers to the hosted checkout URL or embeds widget.html inside an iframe.
key 2. Credential Isolation & Secret Storage
Store sk_live_... and whsec_... strictly in backend environment variables. Never bundle secret keys inside frontend SPAs, Android/iOS client apps, or public repositories.
smartphone 3. Dedicated Android SIM Bridge Setup
Run the SwapnoPay Merchant Android App on a dedicated device holding your active bKash (16247), Nagad (16167), Rocket (16216), or Upay (16268) SIMs. Grant RECEIVE_SMS permission and disable Android Battery Optimization so the foreground socket stays connected 24/7.
verified_user 4. Idempotent Order Fulfillment
Enforce a UNIQUE database constraint on both order_id and carrier trx_id in your database. Always verify the X-SwapnoPay-Signature header and confirm the paid amount matches your invoice total before fulfilling an order.
Direct HTTP REST & Checkout Widget Examples (No SDK Required)
Copy-paste production integrations using standard built-in HTTP clients for your stack.
const crypto = require('node:crypto');
async function createSwapnoPayOrder() {
const response = await fetch('https://pay.swapnopay.top/v1/payment/create', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer sk_live_your_api_secret',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({
order_id: 'ORD-2026-9812',
amount: 1250.00,
customer_phone: '01712963652',
payment_method: 'bKash',
redirect_url: 'https://mysite.com/success',
webhook_url: 'https://mysite.com/api/webhooks/swapnopay'
})
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
const result = await response.json();
console.log('Redirect checkout URL:', result.data.payment_url);
}
import requests
import uuid
response = requests.post(
"https://pay.swapnopay.top/v1/payment/create",
headers={
"Content-Type": "application/json",
"Authorization": "Bearer sk_live_your_api_secret",
"Idempotency-Key": str(uuid.uuid4())
},
json={
"order_id": "ORD-2026-9812",
"amount": 1250.00,
"customer_phone": "01712963652",
"payment_method": "bKash",
"redirect_url": "https://mysite.com/success",
"webhook_url": "https://mysite.com/api/webhooks/swapnopay"
},
timeout=15
)
response.raise_for_status()
print(response.json())
<?php
$ch = curl_init('https://pay.swapnopay.top/v1/payment/create');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_TIMEOUT => 15,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer sk_live_your_api_secret',
'Idempotency-Key: ' . bin2hex(random_bytes(16))
],
CURLOPT_POSTFIELDS => json_encode([
'order_id' => 'ORD-2026-9812',
'amount' => 1250.00,
'customer_phone' => '01712963652',
'payment_method' => 'bKash',
'redirect_url' => 'https://mysite.com/success',
'webhook_url' => 'https://mysite.com/api/webhooks/swapnopay'
])
]);
$response = curl_exec($ch);
curl_close($ch);
print_r(json_decode($response, true));
?>
val client = OkHttpClient()
val json = JSONObject().apply {
put("order_id", "ORD-2026-9812")
put("amount", 1250.00)
put("customer_phone", "01712963652")
put("payment_method", "bKash")
put("redirect_url", "https://mysite.com/success")
}
val request = Request.Builder()
.url("https://pay.swapnopay.top/v1/payment/create")
.addHeader("Authorization", "Bearer sk_live_your_api_secret")
.addHeader("Idempotency-Key", java.util.UUID.randomUUID().toString())
.post(json.toString().toRequestBody("application/json".toMediaType()))
.build()
client.newCall(request).execute().use { response ->
println(response.body?.string())
}
<iframe src="https://pay.swapnopay.top/widget.html?merchant_id=YOUR_MERCHANT_UUID&amount=1250.00&order_id=ORD-2026-9812" width="100%" height="620" style="border:none; border-radius:16px; max-width:480px;" allow="clipboard-write"> </iframe>