One API and one checkout for every way people pay in Bangladesh

Paynex is a self-hosted payment gateway. Create a payment with one signed API call, send the customer to the hosted checkout, and get a webhook when the money is confirmed — bank transfer, Bangla QR, crypto and more.

HMAC-signed REST APIIdempotent payment creationSigned webhooks with retriesMobile-ready checkout & deep links
# 1. create a payment (from your server)
POST https://paynex.xyz/v1/payments
{ "amount": "1500.00", "merchant_ref": "INV-1042",
  "return_url": "https://shop.example/thanks",
  "cancel_url": "https://shop.example/cart" }

# 2. send the customer to
"checkout_url": "https://paynex.xyz/c/FLeipf0FGgJ2agDku55a7Jh…"

# 3. your webhook receives
X-PG-Event: payment.paid
{ "event": "payment.paid", "data": { "id": "92189b87-…", "status": "paid" } }

How it works

Three steps. Your server holds the API key; the customer only ever sees the hosted checkout.

Create a payment

Call POST /v1/payments with the amount, your invoice number and return URLs. The response carries a checkout_url. Same Idempotency-Key = same payment, so retries are safe.

Customer pays on the hosted checkout

A fast, mobile-friendly page with your logo where the customer picks a method and pays. Bank transfer and Bangla QR take a receipt upload; crypto shows a unique amount and address and confirms itself on-chain.

Get the webhook, ship the order

Paynex posts a signed payment.paid event to your URL and retries for 24 hours. Verify the signature, fetch GET /v1/payments/{id}, deliver. Refunds are one call away.

Payment methods

Enable methods per store from the admin. Customers see only what you turn on.

Bank transferaccount details + receipt upload, staff approvelive
Bangla QRyour static QR made dynamic per payment — bKash, Nagad, Rocket, any bank applive
USDTTRC20 · BEP20 · ERC20, unique amount per payment, auto-confirmedlive
Bitcoin & Etheron-chain confirmation, treasury sweeps, store withdrawalslive
Binance directUSDT straight into the store's own Binance account, verified by read-only APIlive
bKashmerchant API adapterroadmap
Nagadmerchant API adapterroadmap
CardsSSLCommerz · Striperoadmap

Also built in: Sandbox Pay simulator for test keys, payment links from the admin (QR, WhatsApp/email share), refunds, per-store accounts statement, review queue, audit log and a "Risk & device" panel (IP, device, language, referer) on every payment.

Quick start

Every request is signed with your key (pk_…) and secret (sk_…) — see Authentication. Or skip the recipe and use an SDK.

KEY_ID=pk_sandbox_…; SECRET=sk_sandbox_…; BASE=https://paynex.xyz
BODY='{"amount":"1500.00","merchant_ref":"INV-1042","return_url":"https://shop.example/thanks","cancel_url":"https://shop.example/cart"}'
TS=$(date +%s); NONCE=$(openssl rand -hex 16)
SIG=$(printf '%s\n%s\nPOST\n/v1/payments\n%s' "$TS" "$NONCE" "$(printf '%s' "$BODY" | sha256sum | cut -d' ' -f1)" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $NF}')
curl -s $BASE/v1/payments -H "Authorization: Paynex-HMAC key_id=$KEY_ID" \
  -H "X-PG-Timestamp: $TS" -H "X-PG-Nonce: $NONCE" -H "X-PG-Signature: $SIG" \
  -H "Idempotency-Key: INV-1042-1500.00" -H "Content-Type: application/json" -d "$BODY"
const { Paynex } = require('./paynex');            // /docs/sdk/paynex-node.zip
const px = new Paynex({ baseUrl: 'https://paynex.xyz', keyId: process.env.PAYNEX_KEY_ID, secret: process.env.PAYNEX_SECRET });

const p = await px.createPayment({ amount: '1500.00', merchant_ref: 'INV-1042',
  return_url: 'https://shop.example/thanks', cancel_url: 'https://shop.example/cart' }, 'INV-1042-1500.00');
res.redirect(p.checkout_url);

// webhook (Express): raw body is required for the signature
app.post('/paynex/webhook', express.raw({ type: () => true }), Paynex.webhookHandler(WEBHOOK_SECRET, async (ev) => {
  if (ev.event !== 'payment.paid') return;
  const full = await px.getPayment(ev.data.id);
  if (full.status === 'paid') await markOrderPaid(full.merchant_ref, full.id);
}));
require 'Paynex.php';                                  // /docs/sdk/paynex-php.zip
$px = new Paynex('https://paynex.xyz', getenv('PAYNEX_KEY_ID'), getenv('PAYNEX_SECRET'));

$p = $px->createPayment([
    'amount' => '1500.00', 'merchant_ref' => 'INV-1042',
    'return_url' => 'https://shop.example/invoice/1042', 'cancel_url' => 'https://shop.example/invoice/1042',
], 'INV-1042-1500.00');
header('Location: ' . $p['checkout_url']);

// webhook.php
$ev = Paynex::verifyWebhook(getenv('PAYNEX_WEBHOOK_SECRET'), $_SERVER['HTTP_X_PG_SIGNATURE'] ?? null, file_get_contents('php://input'));
if ($ev['event'] === 'payment.paid' && $px->getPayment($ev['data']['id'])['status'] === 'paid') markInvoicePaid($ev['data']['id']);
c := paynex.New("https://paynex.xyz", os.Getenv("PAYNEX_KEY_ID"), os.Getenv("PAYNEX_SECRET")) // /docs/sdk/paynex-go.zip

p, err := c.CreatePayment(ctx, paynex.CreatePaymentParams{
    Amount: "1500.00", MerchantRef: "INV-1042",
    ReturnURL: "https://shop.example/thanks", CancelURL: "https://shop.example/cart",
}, "INV-1042-1500.00")
http.Redirect(w, r, p.CheckoutURL, http.StatusSeeOther)

http.Handle("/paynex/webhook", paynex.WebhookHandler(webhookSecret, func(ctx context.Context, ev *paynex.Event) error {
    if ev.Event != "payment.paid" { return nil }
    d, _ := ev.Payment()
    full, err := c.GetPayment(ctx, d.ID)
    if err == nil && full.Paid() { markOrderPaid(full.MerchantRef, full.ID) }
    return err
}))
final px = Paynex(baseUrl: 'https://paynex.xyz', keyId: env['PAYNEX_KEY_ID']!, secret: env['PAYNEX_SECRET']!); // /docs/sdk/paynex-dart.zip

final p = await px.createPayment(
  amount: '1500.00', merchantRef: 'INV-1042',
  returnUrl: 'myapp://paynex/return', cancelUrl: 'myapp://paynex/cancel',   // Flutter deep links
  idempotencyKey: 'INV-1042-1500.00');
// hand p.checkoutUrl to the app → launchUrl(..., mode: LaunchMode.inAppBrowserView)

final ev = Paynex.verifyWebhook(webhookSecret, headers['x-pg-signature'], rawBody);
if (ev.event == 'payment.paid' && (await px.getPayment(ev.data['id'])).paid) await markOrderPaid(ev.data['id']);

Built for merchants and developers

🔐

Signed API, no shared sessions

Every call carries an HMAC-SHA256 signature over method, path, body and a nonce. Keys have scopes; secrets are encrypted at rest.

🔁

Webhooks you can trust

Signed deliveries, unique delivery ids, retries at 1m · 5m · 30m · 2h · 12h · 24h, manual redelivery from the admin.

📱

Mobile apps

Open the checkout in an in-app browser, get the customer back via myapp:// deep links or App Links, poll a public status JSON. SDKs for the backend.

🧾

Payment links

No code needed: generate a link from the admin, share by QR, WhatsApp or email, watch it get paid.

🏪

Multi-store

Stores with their own domains, logos, methods, webhook secrets, API keys, account statements and users (owner, store admin, ops, viewer).

🪙

Crypto done properly

Unique amounts on static addresses, treasury sweeps to save gas, withdrawals with owner approval — or Binance direct deposit with zero custody.

🛡️

Risk & device data

IP, device, language and referer for every checkout step, with heuristic flags on the payment page — ready for rules.

🧪

Sandbox mode

Test keys and a payment simulator run the full flow — webhooks, verify, refunds — without moving money.

SDKs & downloads

Dependency-free, single-file clients for your backend. Same surface everywhere: create / get / list / cancel / verify payments, refunds, store info, webhook and return-redirect verification.

LanguageDownloadNotes
Gopaynex-go.zip · READMEpaynex.New(base, keyID, secret), WebhookHandler for net/http
Node.js 18+paynex-node.zip · READMECommonJS + TypeScript types, Express webhookHandler
Dart 3 / Flutterpaynex-dart.zip · README + Flutter guidebackend client; the app side needs no key (url_launcher + app_links)
PHP 7.4+paynex-php.zip · READMEsingle Paynex.php, cURL — WHMCS, Laravel, WordPress