SukhNexus API

Base URL: https://pay.sukhnexus.com/api/v1

Overview

The API is organised around REST. Requests and responses are JSON; amounts are always integers in the smallest currency unit (paise) — ₹499.00 is 49900. Objects follow the familiar Razorpay-style shape, so most existing integrations port with minimal changes.

UPI only: customers pay by scanning a UPI QR (or tapping "Open UPI app" on a phone). The money goes straight from the customer's bank to the merchant's verified UPI ID — SukhNexus never holds funds, charges no transaction fee, and has no settlements.

Authentication

Use HTTP Basic auth with your Key ID as username and Key Secret as password. Generate keys in Dashboard → API Keys. Test keys (snx_test_…) only see test data; live keys (snx_live_…) work after KYC approval.

curl -u snx_test_AbCd1234:YOUR_SECRET https://pay.sukhnexus.com/api/v1/orders

Keep your secret on the server. Rate limit: 300 requests/minute per key.

Integration flow

  1. Create an order on your server with the amount (POST /orders).
  2. Open checkout in the browser with checkout.js and the order id — or redirect the customer to the order's checkout_url.
  3. On success you receive sukhnexus_payment_id, sukhnexus_order_id and sukhnexus_signature.
  4. Verify the signature on your server, then fulfil the order.
  5. Optionally listen to webhooks (payment.captured) as the source of truth.

Checkout.js

<button id="pay">Pay ₹499</button>
<script src="https://pay.sukhnexus.com/checkout.js"></script>
<script>
document.getElementById('pay').onclick = function () {
  var checkout = new SukhNexus({
    key: 'snx_test_AbCd1234',          // key_id only
    order_id: 'order_Lx8Qe2Rk91AbCd',  // from your server
    prefill: { name: 'Asha', email: 'asha@example.com', contact: '9876543210' },
    handler: function (res) {
      // POST res.sukhnexus_payment_id, res.sukhnexus_order_id,
      // res.sukhnexus_signature to your server and verify
    },
    modal: { ondismiss: function () { console.log('closed'); } }
  });
  checkout.on('payment.failed', function (res) { console.log(res.error); });
  checkout.open();
};
</script>

Redirect flow: pass callback_url when creating the order and send the customer to checkout_url. After payment they are redirected to callback_url?sukhnexus_payment_id=…&sukhnexus_order_id=…&sukhnexus_signature=… (or error[code], error[description] on failure).

Verify the payment signature

signature = HMAC_SHA256(order_id + "|" + payment_id, key_secret) (hex). Always verify on your server before marking an order as paid.

PHP

$expected = hash_hmac('sha256', $_POST['sukhnexus_order_id'].'|'.$_POST['sukhnexus_payment_id'], $keySecret);
if (hash_equals($expected, $_POST['sukhnexus_signature'])) {
    // payment is authentic — mark order paid
}

Node.js

const crypto = require('crypto');
const expected = crypto.createHmac('sha256', keySecret)
  .update(orderId + '|' + paymentId).digest('hex');
const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));

Python

import hmac, hashlib
expected = hmac.new(key_secret.encode(), f"{order_id}|{payment_id}".encode(), hashlib.sha256).hexdigest()
ok = hmac.compare_digest(expected, signature)

Orders

Create an order — POST /orders

ParameterTypeDescription
amount *integerIn paise, minimum 100 (₹1)
currencystringINR (default)
receiptstringYour reference, max 64 chars
notesobjectUp to 15 key-value pairs, returned in payments & webhooks
customerobjectname, email, contact — prefilled on checkout
callback_urlurlRedirect target after payment (redirect flow)
expire_in_minutesintegerDefault 1440 (24 h)
curl -u KEY_ID:KEY_SECRET -X POST https://pay.sukhnexus.com/api/v1/orders \
  -H "Content-Type: application/json" \
  -d '{"amount":49900,"currency":"INR","receipt":"rcpt_11","notes":{"plan":"pro"}}'
{
  "id": "order_Lx8Qe2Rk91AbCd",
  "entity": "order",
  "amount": 49900,
  "amount_paid": 0,
  "amount_due": 49900,
  "currency": "INR",
  "receipt": "rcpt_11",
  "status": "created",
  "attempts": 0,
  "notes": { "plan": "pro" },
  "checkout_url": "https://pay.sukhnexus.com/checkout/order_Lx8Qe2Rk91AbCd",
  "expires_at": 1767312000,
  "created_at": 1767225600
}

Order status: created → attempted → paid (or expired).

GET /ordersList orders. Query: count (≤100), skip, from, to (unix), receipt, status
GET /orders/{id}Fetch an order
GET /orders/{id}/paymentsAll payment attempts for an order

Payments

GET /paymentsList payments (count, skip, from, to, status)
GET /payments/{id}Fetch a payment
{
  "id": "pay_Ab12Cd34Ef56Gh",
  "entity": "payment",
  "amount": 49900,
  "currency": "INR",
  "status": "captured",
  "order_id": "order_Lx8Qe2Rk91AbCd",
  "method": "upi",
  "amount_refunded": 0,
  "refund_status": null,
  "captured": true,
  "email": "asha@example.com",
  "contact": "9876543210",
  "vpa": "asha@okaxis",
  "acquirer_data": { "utr": "412345678901", "rrn": "412345678901" },
  "error_code": null,
  "error_description": null,
  "notes": { "plan": "pro" },
  "created_at": 1767225650,
  "captured_at": 1767225660
}

Payment status: created · pending (direct UPI awaiting merchant confirmation) · captured · failed · refunded.

Refunds

Create a refund — POST /payments/{id}/refund

amountintegerIn paise. Omit for a full refund.
reasonstringOptional
notesobjectOptional
curl -u KEY_ID:KEY_SECRET -X POST https://pay.sukhnexus.com/api/v1/payments/pay_Ab12Cd34Ef56Gh/refund \
  -H "Content-Type: application/json" -d '{"amount":10000,"reason":"Partial return"}'
GET /payments/{id}/refundsRefunds of a payment
GET /refundsList refunds
GET /refunds/{id}Fetch a refund — status pending · processed · failed

Create — POST /payment_links

amountintegerIn paise. Omit to let the customer enter any amount.
min_amountintegerMinimum when the customer chooses the amount
title, descriptionstringShown on the payment page
reusablebooleanAccept many payments (like a payment page)
reference_idstringYour invoice / reference number
customerobjectname, email, contact
expire_byintegerUnix timestamp
callback_urlurlRedirect after payment
curl -u KEY_ID:KEY_SECRET -X POST https://pay.sukhnexus.com/api/v1/payment_links \
  -H "Content-Type: application/json" \
  -d '{"amount":150000,"title":"Invoice #102","customer":{"name":"Ravi","contact":"9876543210"}}'
# → { "id": "plink_…", "short_url": "https://pay.sukhnexus.com/l/ab12cd34", "status": "active", ... }
GET /payment_linksList links
GET /payment_links/{id}Fetch a link (status active · paid · expired · cancelled)
POST /payment_links/{id}/cancelCancel an active link

How UPI payments work

  1. The customer opens checkout and taps UPI. A QR with the exact amount (and an "Open UPI app" button on phones) is shown, along with the merchant's verified payee name.
  2. The customer pays from any UPI app; the money reaches the merchant's bank instantly.
  3. The customer enters the 12-digit UTR / UPI Ref. No. — the payment becomes pending and you receive a payment.pending webhook and an email.
  4. The merchant checks the credit in their bank and clicks I received it → the payment is captured, the order paid, and payment.captured + order.paid webhooks are sent. Not received → failed.

Fulfil orders on order.paid (or after verifying the checkout signature on a captured payment), never on pending.

Refunds are recorded through the API or dashboard (refund.processed webhook); the merchant sends the money back to the customer from their own UPI app.

Webhooks

Configure an HTTPS endpoint in Dashboard → Webhooks. We POST JSON like:

{
  "entity": "event",
  "event": "payment.captured",
  "account_id": "mid_Q1w2E3r4T5y6U7",
  "mode": "live",
  "contains": ["payment"],
  "payload": { "payment": { "entity": { "id": "pay_…", "amount": 49900, "status": "captured", ... } } },
  "created_at": 1767225660
}

Headers: X-SukhNexus-Event, X-SukhNexus-Event-Id (use for idempotency), X-SukhNexus-Signature = HMAC_SHA256(raw_body, webhook_secret).

EventWhen
payment.capturedMoney received
payment.failedAttempt failed or cancelled
payment.pendingDirect-UPI payment submitted with a UTR, awaiting your confirmation
order.paidOrder fully paid
refund.created / refund.processedRefund lifecycle
payment_link.paidA payment link received a payment

Return any 2xx within 8 seconds. Failed deliveries retry after 1 min, 5 min, 30 min, 2 h, 6 h and 24 h. You can resend any delivery from the dashboard.

Errors

HTTP 400
{ "error": { "code": "BAD_REQUEST_ERROR", "description": "The amount must be at least 100.", "field": "amount" } }
400Validation error — see field
401Invalid key id / secret
403Live mode not activated, or account suspended
404Object not found (or belongs to the other mode)
429Rate limit exceeded

Test mode

With test keys, checkout shows a UPI simulator where you choose Success or Failure — no QR, no money. Test payments fire real webhooks (with "mode": "test") so you can test your whole flow end-to-end.