UnoPayv1.0.0
  1. خانه
  2. اعتبارسنجی امضا

راهنماها

اعتبارسنجی امضا

InvalidSignatureError صادر شده، مستند شده و کد پایدار دارد — و هیچ‌چیز در SDK آن را پرتاب نمی‌کند. این نقص مستندات نیست؛ وضعیت فعلی کد است.

زرین‌پال امضا ندارد

کال‌بک یک Authority خام حمل می‌کند. احراز هویت به این دلیل معتبر است که verify.json از درگاه می‌پرسد آیا آن آتوریتی واقعاً متعلق به پذیرنده شماست و واقعاً پرداخت شده — نه به این دلیل که چیزی امضا شده بود. آن استعلام تمام مدل امنیتی این آداپتور است.

UnoPay واقعاً چه چیزی را بررسی می‌کند

ZarinpalAdapter.verifyCallback سه بررسی انجام می‌دهد، به این ترتیب، و دو مورد اول پیش از هر فراخوانی شبکه اجرا می‌شود.

  1. Authority در query یا body موجود باشد. نبودنش PaymentValidationError است.
  2. amount موجود و عددی باشد. غایب یا NaN بودنش PaymentValidationError است.
  3. از درگاه پرسیده می‌شود که آتوریتی را در برابر شناسه پذیرنده شما و مبلغ تبدیل‌شده تایید کند. کدی غیر از 100 یا 101 به‌صورت isSuccessful: false برمی‌گردد.
بررسی مبلغ روی کال‌بک شماست، نه روی سفارشتان

زرین‌پال Authority و Status می‌فرستد — نه مبلغ. اگر هندلر شما مبلغ را از پایگاه داده خودتان در CallbackRequest.body تزریق کند، فراخوانی وریفای با عددی موفق می‌شود که پرداخت‌کننده هرگز مجازش نکرده. مبلغ ذخیره‌شده کنار آتوریتی را ترجیح دهید.

چه وقت InvalidSignatureError پرتاب درستی است

برای آداپتورهای درگاه‌هایی رزرو شده که کال‌بک را امضا می‌کنند — دسته‌ای که چند PSP بانکی ماتریس به آن تعلق دارند. فقط برای یک چیز از آن استفاده کنید: کال‌بکی که رسید ولی احراز هویت نشد.

signed-adapter.ts ts
import {
  InvalidSignatureError, PaymentValidationError,
  type CallbackRequest, type GatewayAdapter, type VerificationResult,
} from 'unopay';

class SignedAdapter implements GatewayAdapter {
  constructor(private secret: string) {}

  async createPayment(): Promise<never> {
    throw new Error('not implemented');
  }

  async verifyCallback(req: CallbackRequest): Promise<VerificationResult> {
    const signature = req.query.SIGNATURE ?? String(req.body.SIGNATURE ?? '');
    const refId = req.query.RefId ?? String(req.body.RefId ?? '');

    if (!signature || !refId) {
      throw new PaymentValidationError('signature and RefId are both required');
    }

    // Compare in constant time, not with === , on a signature.
    const expected = hmac(this.secret, refId);
    if (!timingSafeEqual(signature, expected)) {
      throw new InvalidSignatureError('callback signature mismatch', { signature, refId });
    }

    return { isSuccessful: true, transactionId: refId, settledAmount: 0 };
  }
}

از InvalidSignatureError برای معنی «مبلغ مطابقت ندارد» یا «این آتوریتی مال من نیست» استفاده نکنید. آن‌ها به‌ترتیب مسئله اعتبارسنجی و پرداخت ردشده هستند. این کلاس برای یک کار وجود دارد و بارگذاری بیش از آن باعث می‌شود بلوک‌های catch شما دروغ بگویند.

محافظت در برابر بازپخش بر عهده شماست

نه SDK و نه درگاه جلوی دو بار صدا زدن verifyCallback برای یک آتوریتی را نمی‌گیرد، و فراخوانی دوم به‌درستی 101 با isSuccessful: true برمی‌گرداند. این رفتار درست درگاه است و هیچ محافظتی برای منطق تحویل شما نیست. به‌روزرسانی سفارش را شرطی کنید.

وب‌هوک و idempotency