فهرست مستندات پروژه
سوالات متداول
چرا بدون وابستگی؟
کتابخانههای پرداخت زیرساخت حیاتی هستند. هر وابستگی یک ریسک زنجیره تامین و سطح تعارض نسخهها است. پرداخت فقط به اکستنشنهای PHP متکی است (ext-curl، ext-json، ext-openssl، ext-soap) که در نصبهای استاندارد PHP موجود هستند. بدون Guzzle، بدون کامپوننتهای Symfony، بدون وابستگی به فریمورک.
اگر Guzzle یا کلاینت HTTP دیگری را ترجیح میدهید، اینترفیس HttpClient را پیادهسازی و تزریق کنید — به کتاب آشپزی مراجعه کنید.
چرا مبالغ داخلی به ریال ذخیره میشوند؟
APIهای درگاههای پرداخت ایرانی ناهماهنگ هستند: برخی ریال و برخی تومان میخواهند. تبدیل ۱۰ برابری رایجترین منبع باگهای پرداخت در تجارت الکترونیک ایران است. با ذخیره همه مبالغ به ریال (کوچکترین واحد) در داخل، هر درگاه بهطور خودکار به واحد مورد نیاز API خود تبدیل میکند. شما با هر واحدی که ترجیح میدهید کار کنید:
Amount::fromToman(50_000)->inRials(); // 500,000
Amount::fromRials(500_000)->inToman(); // 50,000
تفاوت درگاههای SOAP و REST چیست؟
درگاههای سنتی بانکی ایران (ملت، سامان، پارسیان) از وبسرویسهای SOAP استفاده میکنند. درگاههای پرداخت مدرن (زرینپال، آیدیپی، زیبال) از APIهای REST استفاده میکنند. از نظر کد شما، هر دو GatewayInterface را یکسان پیادهسازی میکنند. تنها تفاوت قابل مشاهده در ریدایرکت است — درگاههای SOAP نیاز به فرم POST دارند، در حالی که درگاههای REST از ریدایرکت ساده URL استفاده میکنند.
تسویه چیست و چرا برخی درگاهها به آن نیاز دارند؟
ملت و پارسیان از پروتکل سهمرحلهای استفاده میکنند: خرید ← تایید ← تسویه. پس از تایید، پرداخت در وضعیت «در انتظار تسویه» است. اگر در مهلت درگاه (معمولاً ۱۵ تا ۳۰ دقیقه) settle() را فراخوانی نکنید، پرداخت بهطور خودکار برگشت میخورد و پول به حساب خریدار باز میگردد.
این مکانیزم وجود دارد چون بانک «تایید انجام پرداخت» (verify) را از «تایید تمایل پذیرنده به دریافت پول» (settle) جدا میکند. از instanceof SupportsSettlement برای مدیریت عمومی این موضوع استفاده کنید.
چگونه بدون درگاه واقعی تست کنم؟
چندین گزینه دارید:
- حالت سندباکس — زرینپال و آیدیپی محیط سندباکس دارند:
new ZarinpalConfig(merchantId: 'test', sandbox: true);
new IDPayConfig(apiKey: 'test', sandbox: true);
-
ماک کردن HttpClient — اینترفیس
HttpClientرا پیادهسازی کنید تا در تستها پاسخهای ثابت برگرداند. -
ماک کردن درگاه — چون درگاهها
GatewayInterfaceرا پیادهسازی میکنند، میتوانید کل درگاه را در تستهای اپلیکیشن ماک کنید.
چرا پکیج یکپارچهسازی لاراول/سیمفونی وجود ندارد؟
پرداخت طوری طراحی شده که با هر اپلیکیشن PHP کار کند. یکپارچهسازی با فریمورک معمولاً فقط یک سرویس پروایدر است که کانفیگ را میخواند و نمونه Pardakht را در کانتینر ثبت میکند — تقریباً ۲۰ خط کد. ما معتقدیم این به اندازه کافی ساده است و یک پکیج اختصاصی بار نگهداری بیشتری نسبت به ارزشش اضافه میکند.
مثال برای لاراول:
</div>php
// AppServiceProvider
$this->app->singleton(Pardakht::class, fn () => new Pardakht(
logger: new LaravelLogger(),
));
<div dir="ltr">
آیا میتوان از چند درگاه همزمان استفاده کرد؟
بله. یک نمونه Pardakht میتواند هر تعداد درگاه بسازد:
</div>php
$pardakht = new Pardakht();
$zarinpal = $pardakht->create('zarinpal', new ZarinpalConfig('merchant-1'));
$mellat = $pardakht->create('mellat', new MellatConfig(123, 'user', 'pass'));
<div dir="ltr">
آنها کلاینت HTTP و لاگر مشترک دارند اما در غیر این صورت مستقل هستند.
چگونه کالبک درگاههای مختلف را مدیریت کنم؟
هر درگاه پارامترهای متفاوتی در کالبک ارسال میکند. متد verify() این موضوع را انتزاع میکند — بهطور خودکار دادههای $_POST یا $_GET را تشخیص داده و آنچه نیاز دارد استخراج میکند. همچنین میتوانید دادههای کالبک را صریحاً ارسال کنید:
</div>php
// تشخیص خودکار (از $_POST یا $_GET میخواند)
$transaction = $gateway->verify();
// داده صریح (در فریمورکها مفید است)
$transaction = $gateway->verify($request->all());
<div dir="ltr">
اگر تایید پرداخت ناموفق باشد چه اتفاقی میافتد؟
یک VerificationException پرتاب میشود که از GatewayException ارثبری دارد. نام درگاه و کد خطا را حمل میکند:
</div>php
try {
$transaction = $gateway->verify();
} catch (VerificationException $e) {
$e->getGatewayName(); // "zarinpal"
$e->getErrorCode(); // -51
$e->getMessage(); // پیام خطای قابل خواندن
}
کدام نسخههای PHP پشتیبانی میشوند؟
PHP نسخه ۸.۱ و بالاتر. کتابخانه از enum، readonly properties، named arguments و سایر ویژگیهای PHP 8.1+ استفاده میکند.