پرش به مطلب اصلی

وب‌هوک و امضا

در پروژه، تب وب‌هوک، آدرس https سرور خود را ثبت کنید. هر آدرس یک secret (whsec_…) دارد.

رویدادها

رویدادکی
payment.confirmedپرداخت تأیید شد (خودکار یا دستی)
payment.expiredمهلت تمام شد
payment.rejectedفروشنده رد کرد (reason دارد)
payment.canceledمشتری انصراف داد

بدنه (JSON):

{
"event": "payment.confirmed",
"paymentId": "01a0acf1-e835-7ed7-91e8-f25a128276f3",
"status": "confirmed",
"orderId": "ORD-1024",
"amountRequested": "1000000",
"amountFinal": "1014320",
"confirmedAt": "2026-09-17T10:03:12.000Z",
"confirmationSource": "auto_sms",
"sandbox": false,
"metadata": { "userId": 42 },
"deliveryId": "01a0acf2-...",
"sentAt": "2026-09-17T10:03:13.000Z"
}

هدرها: X-Varizyar-Event، X-Varizyar-Delivery، X-Varizyar-Signature.

بررسی امضا

X-Varizyar-Signature: t=1758103393,v1=5f3c...e2
v1 = HMAC-SHA256(secret, t + "." + rawBody) // hex
  • rawBody را دقیقاً همان‌طور که رسیده (قبل از parse) امضا کنید.
  • t را با زمان فعلی مقایسه کنید؛ بیشتر از ۵ دقیقه اختلاف را رد کنید (جلوگیری از replay).
  • مقایسه‌ی امن (constant-time) انجام دهید.

Node.js

import { createHmac, timingSafeEqual } from 'node:crypto';

export function verifyVarizyarSignature(secret, header, rawBody, toleranceSeconds = 300) {
const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
const t = Number(parts.t);
if (!Number.isFinite(t) || Math.abs(Date.now() / 1000 - t) > toleranceSeconds) return false;
const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
const a = Buffer.from(expected);
const b = Buffer.from(parts.v1 ?? '');
return a.length === b.length && timingSafeEqual(a, b);
}

PHP

function verifyVarizyarSignature(string $secret, string $header, string $rawBody, int $tolerance = 300): bool {
$parts = [];
foreach (explode(',', $header) as $p) { [$k, $v] = explode('=', $p, 2) + [null, null]; $parts[$k] = $v; }
$t = (int)($parts['t'] ?? 0);
if ($t === 0 || abs(time() - $t) > $tolerance) return false;
$expected = hash_hmac('sha256', $t . '.' . $rawBody, $secret);
return hash_equals($expected, $parts['v1'] ?? '');
}

پاسخ و تکرار

با کد 2xx پاسخ دهید (بدنه مهم نیست). در غیر این صورت تا ۱۰ بار با فاصله‌ی افزایشی (۱ دقیقه تا ۲۴ ساعت) تکرار می‌شود و بعد متوقف و به فروشنده اعلان داده می‌شود. پردازش را idempotent کنید: deliveryId یا paymentId را ذخیره و تکراری را نادیده بگیرید.

آدرس‌های داخلی (localhost، IPهای خصوصی) پذیرفته نمی‌شوند.