
وبهوک راهی است که فروشگاه، پنل بیلینگ یا ربات شما از پرداخت مشتری باخبر میشود. SnapEX Pay برای هر رویداد فاکتور، پرداخت و برداشت یک درخواست HTTP POST به URL شما میفرستد. اگر درست پیاده شود، سریعترین و مطمئنترین سیگنالی است که دارید. اگر بیدقت پیاده شود، راهی است برای پرداختشده علامتزدن سفارشهایی که هرگز پرداخت نشدهاند. این راهنما بخشهای مهم را پوشش میدهد.
تنظیم
در پنل فروشنده به Invoices → کارت Webhook بروید، URL خود را وارد کنید و Save را بزنید. یک URL برای هر حساب همه انواع رویداد را دریافت میکند. در اولین ذخیره، پنل رمز امضای whsec_… را فقط یک بار نشان میدهد: آن را فقط روی سرور خود نگه دارید.
URL باید یک آدرس مطلق https:// با میزبان عمومی باشد: بدون login:password، بدون localhost و میزبان نباید به IP خصوصی یا رزروشده اشاره کند. ریدایرکتها دنبال نمیشوند، پس URL نهایی را بدهید.
چه چیزی میرسد
هر درخواست چهار هدر دارد:
| هدر | معنی |
|---|---|
Webhook-Id |
شناسه یکتای رویداد. در تکرارها یا ارسال دستی دوباره تغییر نمیکند. |
Webhook-Timestamp |
زمان یونیکس (ثانیه) ارسال این تلاش. هر تکرار مقدار تازهای دارد. |
Webhook-Signature |
v1= و HMAC-SHA256 هگز با حروف کوچک از {Webhook-Timestamp}.{raw body}. |
Content-Type |
همیشه application/json. |
بدنه شامل id (همان Webhook-Id)، type، created_at و data است. برای رویدادهای invoice.*، data همان فاکتور با ساختار GET /invoices/{id} است، از جمله environment، amount_usd، status و external_id شما.
رویدادهای فاکتور:
| رویداد | چه زمانی |
|---|---|
invoice.created |
فاکتور ساخته شد، هنوز روش پرداختی انتخاب نشده. |
invoice.awaiting |
روش پرداخت انتخاب و آدرس صادر شد. |
invoice.underpaid |
بخشی از مبلغ رسید؛ همان آدرس منتظر باقیمانده است. |
invoice.paid |
کل مبلغ پرداخت و واریز شد. تنها سیگنال برای ارسال سفارش. |
invoice.partially_paid |
فقط بخشی پرداخت شد، مهلت تکمیل تمام شد و بخش دریافتشده به شما واریز شد. |
invoice.expired |
مهلت فاکتور بدون پرداخت تمام شد. |
invoice.extra_payment |
انتقال دیگری پس از پرداخت فاکتور رسید؛ جداگانه واریز میشود. |
بررسی امضا
- بدنه خام درخواست را بهصورت بایت، پیش از هر پردازش JSON، بخوانید. JSONی که دوباره سریالسازی شده مطابقت نخواهد داشت.
- HMAC-SHA256 را روی
{Webhook-Timestamp}.{raw body}با کل رمز بهعنوان کلید (همراه با پیشوندwhsec_) محاسبه و به هگز تبدیل کنید. v1=+ هگز را با هدرWebhook-Signatureبا یک تابع زمانثابت مقایسه کنید.Webhook-Timestampی را که بیش از ۵ دقیقه با ساعت شما اختلاف دارد رد کنید. تکرارها زمان تازه دارند، پس این کار آنها را خراب نمیکند.
PHP:
$secret = getenv('SNAPEX_WEBHOOK_SECRET'); // whsec_… from the cabinet
$body = file_get_contents('php://input'); // raw body, before json_decode
$ts = $_SERVER['HTTP_WEBHOOK_TIMESTAMP'] ?? '';
$sig = $_SERVER['HTTP_WEBHOOK_SIGNATURE'] ?? '';
$expected = 'v1=' . hash_hmac('sha256', $ts . '.' . $body, $secret);
if (!hash_equals($expected, $sig) || abs(time() - (int) $ts) > 300) {
http_response_code(400);
exit;
}
$event = json_decode($body, true);
// skip if $event['id'] was already processed; do the heavy work in a queue
http_response_code(200);
Node.js (Express):
const crypto = require('crypto');
app.post('/hooks/snapex', express.raw({ type: 'application/json' }), (req, res) => {
const ts = req.get('Webhook-Timestamp') || '';
const sig = Buffer.from(req.get('Webhook-Signature') || '');
const expected = Buffer.from('v1=' + crypto
.createHmac('sha256', process.env.SNAPEX_WEBHOOK_SECRET)
.update(ts + '.' + req.body)
.digest('hex'));
const fresh = Math.abs(Date.now() / 1000 - Number(ts)) <= 300;
if (!fresh || sig.length !== expected.length || !crypto.timingSafeEqual(sig, expected)) {
return res.sendStatus(400);
}
const event = JSON.parse(req.body);
// skip if event.id was already processed; queue the heavy work
res.sendStatus(200);
});
تکرارها، تحویلهای تکراری و ترتیب
- موفقیت یعنی هر پاسخ 2xx. هر کد دیگر، تایماوت یا خطای شبکه شکست است. تایماوتها ۵ ثانیه برای اتصال و ۱۵ ثانیه برای کل درخواست هستند، پس سریع پاسخ دهید و کار سنگین را ناهمگام انجام دهید.
- تکرارها. تا ۵ تلاش برای هر رویداد: تلاش اول، سپس حدود ۱۰ ثانیه، ۱ دقیقه، ۵ دقیقه و ۱۵ دقیقه پس از هر شکست. پس از پنجمین شکست، ارسال خودکار متوقف میشود؛ پنل تحویلهای اخیر را فهرست میکند و دکمه Retry دارد که رویداد را با همان
Webhook-Idدوباره میفرستد. - تحویلهای تکراری. تحویل at-least-once است: یک رویداد ممکن است بیش از یک بار برسد. مقادیر
Webhook-Idپردازششده را ذخیره کنید و تکرارها را نادیده بگیرید. - ترتیب. رویدادها ممکن است نامرتب برسند: یک رویداد قدیمی تکرارشده میتواند پس از رویداد جدیدتر برسد. به ترتیب تکیه نکنید؛ وضعیت داخل رویداد را با آنچه از قبل دارید مقایسه کنید.
- در صورت تمایل دوباره بررسی کنید. پس از وبهوک میتوانید شیء را دوباره از API بخوانید:
GET /invoices/{id}.
تست بدون پول واقعی
یک فاکتور تست ("environment": "test") بسازید، آن را در صفحه پرداخت بپردازید و ببینید وبهوکها با data.environment = "test" میرسند. جزئیات: چگونه پرداخت کریپتو را بدون پول واقعی تست کنیم.
ماژولهای آماده برای WHMCS، BILLmanager، WooCommerce و پلتفرمهای دیگر از قبل امضا را بررسی میکنند و هرگز یک پرداخت را دو بار واریز نمیکنند؛ pay.snapex.pro را ببینید. مرجع کامل: مستندات API SnapEX Pay.