UnoPayv1.0.0
  1. خانه
  2. سندباکس و تست

راهنماها

راهبرد سندباکس و تست

سه درگاه از یازده درگاه ماتریس سندباکس تولید واقعی دارند. هشت درگاه دیگر به استاب محلی نیاز دارند، و دانستن اینکه کدام‌ها کدام‌اند پیش از برنامه‌ریزی یک فصل، زمان زیادی نجات می‌دهد.

کدام درگاه‌ها سندباکس واقعی عملیاتی دارند
درگاهپروتکلسندباکسیادداشت‌هاوضعیت در SDK
زرین‌پال · ZarinPalREST (JSON)بلهسندباکس تولید در sandbox.zarinpal.comپشتیبانی‌شده
آیدی‌پی · IDPayREST (JSON)بلهسندباکس تولید از طریق هدر درخواست X-SANDBOXبرنامه‌ریزی‌شده
به‌پرداخت ملت · Behpardakht MellatSOAP 1.1 (WSDL)خیرندارد، نیاز به سندباکس محلیبرنامه‌ریزی‌شده
سامان کیش · Saman SEPREST / SOAP، مبتنی بر توکنخیرندارد، نیاز به سندباکس محلیبرنامه‌ریزی‌شده
سداد بانک ملی · Sadad MelliREST / SOAPخیرنداردفقط سند
تجارت الکترونیک پارسیان · Parsian PECSOAP / توکن RESTخیرنداردفقط سند
پرداخت الکترونیک پاسارگاد · Pasargad PEPREST / ریدایرکتخیرنداردفقط سند
آسان پرداخت · Asan PardakhtREST / SOAPخیرنداردفقط سند
پرداخت نوین آرین · Pardakht NovinREST / SOAPخیرنداردفقط سند
ایران‌کیش · IranKishREST / SOAPخیرنداردفقط سند
سیزپی · SizPayREST (JSON)بلهسندباکس موجود استفقط سند

زرین‌پال: همان که امروز می‌توانید استفاده کنید

sandbox: true نشانی پایه آداپتور را به https://sandbox.zarinpal.com/pg تغییر می‌دهد و هیچ چیز دیگری. شکل درخواست، کدهای پاسخ و جریان وریفای درست مثل تولید است. آتوریتی سندباکس با S و آتوریتی پروداکشن با A شروع می‌شود — یک assertion ارزان برای نگه داشتن در fixtureهای تست.

vitest.config.ts ts
import { describe, it, expect } from 'vitest';
import { UnoPay, ZarinpalAdapter, GatewayProviderError,
  PaymentValidationError, toRials } from 'unopay';

describe('sandbox integration', () => {
  const unopay = new UnoPay({
    zarinpal: new ZarinpalAdapter({ merchantId: 'sandbox-merchant', sandbox: true }),
  });

  it('normalises Toman to Rials', () => {
    expect(toRials(50_000, 'IRT')).toBe(500_000);
    expect(toRials(500_000, 'IRR')).toBe(500_000);
    // a typo is silently IRR — assert the behaviour you actually get
    expect(toRials(50_000, 'TOMN')).toBe(50_000);
  });

  it('creates a payment against the sandbox', async () => {
    const payment = await unopay.createPayment('zarinpal', {
      amount: 1000,
      currency: 'IRT',
      callbackUrl: 'https://example.com/callback',
    });

    expect(payment.redirectUrl).toContain('sandbox.zarinpal.com');
    expect(payment.transactionId.startsWith('S')).toBe(true);
    expect(payment.providerToken).toBe(payment.transactionId);
  });

  it('rejects a callback with no Authority before any network call', async () => {
    await expect(
      unopay.verifyCallback('zarinpal', { method: 'GET', query: {}, body: {} }),
    ).rejects.toBeInstanceOf(PaymentValidationError);
  });

  it('surfaces a gateway rejection as GatewayProviderError', async () => {
    await expect(
      unopay.createPayment('zarinpal', {
        amount: 1000,
        currency: 'IRT',
        callbackUrl: 'https://example.com/callback',
        metadata: { description: 'x'.repeat(300) }, // over the gateway limit
      }),
    ).rejects.toBeInstanceOf(GatewayProviderError);
  });
});

برای هشت درگاه بدون سندباکس: آداپتور را استاب کنید

برای تست یکپارچه‌سازی خود به درگاه نیاز ندارید. به چیزی نیاز دارید که GatewayAdapter را برآورده کند. چون اینترفیس دو متدی است، یک استاب بیست خط است و در میکروثانیه اجرا می‌شود.

stub.ts ts
import {
  UnoPay, PaymentError, GatewayNetworkError,
  type GatewayAdapter, type PaymentRequest, type PaymentResult,
  type CallbackRequest, type VerificationResult,
} from 'unopay';

// Scripted outcomes so you can exercise every branch without a gateway.
export function makeStub(outcome: 'paid' | 'declined' | 'fault'): GatewayAdapter {
  return {
    async createPayment(req: PaymentRequest): Promise<PaymentResult> {
      if (outcome === 'fault') throw new GatewayNetworkError('stub network failure');
      return {
        redirectUrl: 'https://stub.test/start/STUB-1',
        transactionId: 'STUB-1',
        providerToken: 'STUB-1',
      };
    },
    async verifyCallback(req: CallbackRequest): Promise<VerificationResult> {
      if (outcome === 'fault') throw new GatewayNetworkError('stub network failure');
      const ok = outcome === 'paid';
      return {
        isSuccessful: ok,
        transactionId: ok ? 'REF-1' : req.query.Authority ?? 'unknown',
        settledAmount: Number(req.query.amount ?? 0),
        errorReason: ok ? undefined : 'stubbed decline',
      };
    },
  };
}

// Your handler code is unchanged — only the adapter differs.
export const stubbed = new UnoPay({ gateway: makeStub('paid') });
// stubbed.createPayment('gateway', …)   // 'gateway' is the only legal key
سه لایه را جدا تست کنید

استاب برای منطق هندلر خودتان، سندباکس برای قالب سیمی آداپتور، و یک کال‌بک واقعی ضبط‌شده به‌عنوان fixture برای رگرسیون. لایه سوم همان چیزی است که تیم‌ها رد می‌کنند و باگ‌های «فیلد عوض نام داده» را می‌گیرد.