Skip to content
RugWard
☰ मेनू

RugWard Pay

वेबहुक और सिग्नेचर सत्यापन

हर इनवॉइस स्थिति बदलने पर साइन की हुई HTTPS सूचना पाएँ, उसे curl, Node.js, Python या PHP में सत्यापित करें, और दोबारा प्रयासों व डुप्लिकेट को सुरक्षित रूप से सँभालें।

इस पेज पर

वेबहुक कैसे काम करते हैं#

जब किसी इनवॉइस की स्थिति बदलती है, RugWard आपके एंडपॉइंट पर JSON बॉडी के साथ POST भेजता है। हर अनुरोध आपके साइनिंग सीक्रेट (signing secret) से साइन होता है, ताकि आप साबित कर सकें कि वह RugWard से आया है और बदला नहीं गया।

वेबहुक सेटिंग
Webhook बॉक्स: एंडपॉइंट URL, New signing secret, Send test ping और डिलीवरी लॉग

सेटअप#

  1. डैशबोर्ड में Webhook में अपना एंडपॉइंट डालें और Save पर क्लिक करें।
  2. साइनिंग सीक्रेट (whsec_ और उसके बाद 40 अक्षर) केवल एक बार दिखाया जाता है। इसे अपने सर्वर पर API कुंजी के साथ रखें।
  3. Send test ping पर क्लिक करें। डैशबोर्ड वह HTTP स्थिति दिखाता है जो आपके एंडपॉइंट ने लौटाई।
  4. बॉक्स के नीचे डिलीवरी लॉग हर इवेंट, उसका परिणाम और प्रयासों की संख्या दिखाता है।

एंडपॉइंट के नियम:

  • केवल https://, मानक पोर्ट पर (:8443 नहीं), अधिकतम 500 अक्षर;
  • सार्वजनिक होस्ट नाम: IP पते, localhost और निजी नेटवर्क अस्वीकार होते हैं;
  • 10 सेकंड के भीतर किसी भी 2xx स्थिति से जवाब दें। रीडायरेक्ट फ़ॉलो नहीं होते: 3xx जवाब विफलता माना जाता है।

New signing secret सीक्रेट को तुरंत बदल देता है; नया तुरंत लागू करें (पुराने सीक्रेट से बने सिग्नेचर तुरंत बंद हो जाते हैं)। खाली URL सहेजने से वेबहुक बंद हो जाते हैं।

इवेंट#

इवेंटकब भेजा जाता है
invoice.pendingभुगतान दिखा, पुष्टियों या बाकी राशि की प्रतीक्षा
invoice.paidपूरा भुगतान और पुष्टि: सामान भेजें
invoice.overpaid1% से अधिक ज़्यादा भुगतान: सामान भेजें
invoice.underpaidदर लॉक बहुत कम पुष्ट राशि के साथ खत्म हुआ
invoice.lateसमाप्ति के बाद भुगतान आया
invoice.expiredबिना भुगतान के समाप्त
invoice.canceledआपने रद्द किया
pingडैशबोर्ड का Send test ping बटन

हर इवेंट प्रति इनवॉइस अधिकतम एक बार भेजा जाता है। टेस्ट इनवॉइस कभी वेबहुक नहीं भेजते।

अनुरोध#

Webhook request (what your endpoint receives)
POST /rugward-webhook HTTP/1.1
Host: shop.example
Content-Type: application/json
User-Agent: RugWard-Pay-Webhook/1.0
RugWard-Event-Id: Zx3kQ8pL2mN7vR1tY5wB9c:paid
RugWard-Event: invoice.paid
RugWard-Signature: t=1793324105,v1=5f0c3d0e8b1a…(64 hex characters)
Body: invoice.paid
{
  "id": "Zx3kQ8pL2mN7vR1tY5wB9c:paid",
  "type": "invoice.paid",
  "created": 1793324100,
  "data": {
    "invoice": {
      "id": "Zx3kQ8pL2mN7vR1tY5wB9c",
      "status": "paid",
      "amountUsd": "25.00",
      "orderId": "1234",
      "description": "1 x T-shirt, size M",
      "redirectUrl": "https://shop.example/thanks?order=1234",
      "createdAt": 1793323282069,
      "expiresAt": 1793326882069,
      "paidAt": 1793324100512,
      "assets": ["BTC", "ETH-8453", "USDC-8453"],
      "test": false,
      "quotes": [{ "id": 4711, "asset": "USDC-8453", "amount": "25.000629", "rateUsd": "1", "address": "0x…", "expiresAt": 1793325082069 }],
      "payments": [{ "txid": "0x…", "asset": "USDC-8453", "amount": "24.875626", "confirmations": 30, "required": 30, "confirmed": true, "seenAt": 1793324012000, "explorer": "https://basescan.org/tx/0x…" }]
    }
  }
}
Body: test ping (dashboard button)
{ "id": "ping:1793324000000", "type": "ping", "created": 1793324000, "data": {} }
  • id का रूप <invoice id>:<status> है और यह RugWard-Event-Id हेडर में भी है: डुप्लिकेट अनदेखा करने के लिए इसका इस्तेमाल करें।
  • created Unix सेकंड में है; data.invoice वही ऑब्जेक्ट है जो GET /api/pay/merchant/invoices/:id लौटाता है।

सिग्नेचर सत्यापित करें#

हेडर ऐसा दिखता है: RugWard-Signature: t=1793324100,v1=5f2c… (64 हेक्स अक्षर)।

  1. अनुरोध की कच्ची (raw) बॉडी ठीक वैसे पढ़ें जैसी मिली (किसी भी JSON पार्सिंग से पहले)।
  2. HMAC-SHA256(secret, t + "." + raw_body) निकालें और हेक्स में एन्कोड करें।
  3. इसे v1 से स्थिर समय (constant time) में मिलाएँ।
  4. अगर t आपकी घड़ी से 5 मिनट से अधिक अलग है तो अनुरोध अस्वीकार करें (रीप्ले रोकता है)।
  5. उसके बाद ही JSON पार्स करें।

पहले JSON पार्स करके फिर से सीरियलाइज़ करने से बाइट बदल जाते हैं और सिग्नेचर टूट जाता है। सिग्नेचर हमेशा कच्ची बॉडी पर जाँचें।

Node.js (Express): verify, then process
import crypto from "node:crypto";
import express from "express";

const SECRET = process.env.RUGWARD_WEBHOOK_SECRET; // whsec_…
const app = express();

// Use the RAW body: the signature covers the exact bytes RugWard sent.
app.post("/rugward-webhook", express.raw({ type: "application/json" }), (req, res) => {
  const raw = req.body.toString("utf8");
  const m = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(req.get("RugWard-Signature") || "");
  if (!m || Math.abs(Date.now() / 1000 - Number(m[1])) > 300) return res.sendStatus(400);
  const want = crypto.createHmac("sha256", SECRET).update(`${m[1]}.${raw}`).digest("hex");
  if (!crypto.timingSafeEqual(Buffer.from(want), Buffer.from(m[2]))) return res.sendStatus(400);

  const event = JSON.parse(raw);
  // De-duplicate: the same event id can arrive more than once.
  // if (alreadyProcessed(event.id)) return res.sendStatus(200);
  if (event.type === "invoice.paid" || event.type === "invoice.overpaid") {
    const inv = event.data.invoice;
    // markOrderPaid(inv.orderId, inv.id);  // ship only now
  }
  res.sendStatus(200); // any 2xx = delivered
});
app.listen(3000);
Python (Flask)
import hashlib, hmac, json, os, re, time
from flask import Flask, abort, request

SECRET = os.environ["RUGWARD_WEBHOOK_SECRET"].encode()  # whsec_…
app = Flask(__name__)

@app.post("/rugward-webhook")
def rugward_webhook():
    raw = request.get_data()  # raw bytes, exactly as sent
    m = re.fullmatch(r"t=(\d+),v1=([0-9a-f]{64})", request.headers.get("RugWard-Signature", ""))
    if not m or abs(time.time() - int(m.group(1))) > 300:
        abort(400)
    want = hmac.new(SECRET, m.group(1).encode() + b"." + raw, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(want, m.group(2)):
        abort(400)
    event = json.loads(raw)
    if event["type"] in ("invoice.paid", "invoice.overpaid"):
        inv = event["data"]["invoice"]
        # mark_order_paid(inv["orderId"], inv["id"])  # de-duplicate on event["id"]
    return "", 200
PHP 8
<?php
$secret = getenv("RUGWARD_WEBHOOK_SECRET"); // whsec_…
$raw = file_get_contents("php://input");
$sig = $_SERVER["HTTP_RUGWARD_SIGNATURE"] ?? "";
if (!preg_match('/^t=(\d+),v1=([0-9a-f]{64})$/', $sig, $m) || abs(time() - (int)$m[1]) > 300) {
    http_response_code(400); exit;
}
$want = hash_hmac("sha256", $m[1] . "." . $raw, $secret);
if (!hash_equals($want, $m[2])) { http_response_code(400); exit; }

$event = json_decode($raw, true);
if (in_array($event["type"], ["invoice.paid", "invoice.overpaid"], true)) {
    $inv = $event["data"]["invoice"];
    // mark_order_paid($inv["orderId"], $inv["id"]); // de-duplicate on $event["id"]
}
http_response_code(200);

अपने एंडपॉइंट को हाथ से टेस्ट करने के लिए, बॉडी खुद साइन करें:

Shell (openssl): compute a signature to test your endpoint
SECRET='whsec_…'            # your signing secret
BODY='{"id":"ping:1","type":"ping","created":1793324000,"data":{}}'
T=$(date +%s)
SIG=$(printf '%s.%s' "$T" "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -hex | sed 's/^.* //')

curl -X POST https://shop.example/rugward-webhook \
  -H "Content-Type: application/json" \
  -H "RugWard-Event: ping" \
  -H "RugWard-Signature: t=$T,v1=$SIG" \
  --data "$BODY"

दोबारा प्रयास और डुप्लिकेट#

अगर आपका एंडपॉइंट टाइम आउट हो जाए या 2xx न लौटाए, RugWard दोबारा कोशिश करता है: कुल अधिकतम 4 प्रयास, लगभग 30 सेकंड, 2 मिनट और 8 मिनट बाद। उसके बाद इवेंट डिलीवरी लॉग में विफल चिह्नित होता है। इसलिए आपके हैंडलर को:

  • जल्दी 2xx लौटाना चाहिए और धीमा काम बाद में करना चाहिए;
  • idempotent होना चाहिए: RugWard-Event-Id सहेजें और पहले से संसाधित इवेंट छोड़ दें;
  • क्रम पर निर्भर नहीं होना चाहिए: संदेह हो तो API से इनवॉइस दोबारा पढ़ें।

वेबहुक तेज़ संकेत हैं, अकेले नहीं। अगर आपका सर्वर कुछ समय बंद रहा, तो API से हाल के इनवॉइस सूचीबद्ध करें और स्थितियाँ मिलाएँ।

समस्या निवारण#

लक्षणसंभावित कारण
"Webhook URL must be https:// with a public host name"IP पता, कस्टम पोर्ट, http:// या आंतरिक होस्ट नाम
पिंग HTTP 301/302/308 दिखाता हैआपका सर्वर रीडायरेक्ट करता है (जैसे http→https, या अंत का स्लैश गायब); अंतिम URL इस्तेमाल करें
पिंग 405 दिखाता हैरूट POST स्वीकार नहीं करता
सिग्नेचर कभी मेल नहीं खाताहैश से पहले बॉडी पार्स या दोबारा एन्कोड हुई, गलत सीक्रेट इस्तेमाल हो रहा है (New signing secret के बाद), या सर्वर की घड़ी गलत है
टेस्ट इनवॉइस के लिए कोई इवेंट नहींअपेक्षित: टेस्ट इनवॉइस कभी वेबहुक नहीं भेजते

अंतिम अपडेट: 30 अक्टूबर 2026

वेबहुक और सिग्नेचर सत्यापन · RugWard दस्तावेज़ | RugWard