پرش به محتوا
راهنمای پذیرش

درگاه پرداخت لاراول: پکیج آماده، وبهوک امن و تست بدون پول

تیم تحریریه آبان گیت وی۱۴ دقیقه خواندن

درگاه پرداخت لاراول: خط نصب پکیج با composer کنار مسیر پرداخت کارت به کارت و رویداد InvoicePaid

سایت را با لاراول ساخته اید و حالا نوبت پول است. اولین جستجو برای درگاه پرداخت لاراول معمولاً به آموزشی میرسد که با «کد پذیرنده را در فایل تنظیمات بگذارید» شروع میشود. یعنی فرض کرده قرارداد درگاه شاپرکی را بسته اید، اینماد دارید و دامنه سایت در پنل درگاه تأیید شده. اگر هنوز به آنجا نرسیده اید، یا کارفرمایی که برایش سایت میسازید نرسیده، آن آموزش از خط اول گیر میکند.

ما مهر ۱۴۰۵ بسته رسمی PHP و لاراول آبان گیت وی را منتشر کردیم. موقع ساختن و آزمودنش به چند تله خوردیم که در آموزش های درگاه شاپرکی نیامده، چون آنجا پرداخت همان لحظه برگشت خریدار تمام میشود و در کارت به کارت نه. این مقاله هم راه نصب را میگوید، هم آن تله ها را.

خلاصه ۳۰ ثانیه ای برای درگاه پرداخت لاراول دو راه هست: درگاه شاپرکی که کد پذیرنده و اینماد میخواهد، و کارت به کارت با تأیید خودکار که فقط کلید API میخواهد. بسته ما با یک خط composer نصب میشود، برای هر سفارش فاکتور میسازد و وقتی پول رسید، رویداد InvoicePaid را میفرستد. سفارش را در همین رویداد تحویل بدهید، نه وقتی خریدار به سایت برگشت. پیش از فروش واقعی هم با کلید آزمایشی کل مسیر را بدون پول امتحان کنید.

درگاه پرداخت برای لاراول بدون کد پذیرنده شاپرک، شدنی است؟

بله، ولی نه از راه شاپرک. هر پکیج درگاه پرداخت لاراول که برای یک درگاه شاپرکی نوشته شده، هر قدر هم تمیز باشد، آخرش یک کد پذیرنده یا شماره ترمینال میخواهد. آن کد را شرکت درگاه بعد از قرارداد میدهد، و قرارداد معمولاً اینماد روی همان دامنه و کد رهگیری مالیاتی میخواهد. چرا این یکی را نمیشود خودتان ساخت، در ساخت درگاه پرداخت آمده؛ اگر مدارکش را ندارید، درگاه بدون اینماد راه ها را کنار هم گذاشته.

راه دوم کارت به کارت است، با این فرق که تأییدش دستی نیست. خریدار در صفحه پرداخت شماره کارت شما و مبلغ دقیق را میبیند و از اپ بانک خودش انتقال میدهد. پیامک واریز روی گوشی شما میرسد و سرور ما آن را با فاکتور تطبیق میدهد؛ جزئیات تطبیق را در تأیید خودکار کارت به کارت نوشته ایم. به همین خاطر به آن درگاه کارت به کارت خودکار هم میگویند. پول مستقیم روی کارت خودتان مینشیند و از شاپرک رد نمیشود، پس کد پذیرنده ای هم در کار نیست.

درگاه شاپرکی کارت به کارت با تأیید خودکار
چه چیزی در .env میرود کد پذیرنده یا ترمینال کلید API و کلید امضای وبهوک
پیش نیاز قرارداد، اینماد، تأیید دامنه حساب در پنل، با کارت و گوشی خودتان
پرداخت کجا انجام میشود صفحه بانک اپ بانک خود خریدار
تأیید کی میرسد همان لحظه برگشت خریدار وقتی پیامک واریز رسید، با وبهوک
پول کجا مینشیند حساب شما، با تسویه شاپرکی همان لحظه روی کارت شما
حالت آزمایشی بسته به شرکت دارد، با کلید ag_test_

پکیج درگاه پرداخت لاراول: نصب تا اولین فاکتور

اتصال درگاه پرداخت در لاراول با بسته آبان گیت وی چهار قدم دارد: بسته را با composer نصب کنید، کلید API و کلید امضا را در .env بگذارید، برای هر سفارش با invoiceForOrder فاکتور بسازید و خریدار را به paymentUrl بفرستید، و در شنونده رویداد InvoicePaid سفارش را تحویل بدهید. مسیر دریافت وبهوک را خود بسته ثبت میکند.

نصب و دو کلید در .env

composer require abangateway/abangateway-php-package

لاراول بسته را خودش پیدا میکند و لازم نیست service provider یا facade را دستی ثبت کنید. بعد این دو خط را در .env بگذارید:

ABANGATEWAY_KEY=ag_live_xxxx…xxxx
ABANGATEWAY_WEBHOOK_SECRET=...

کلید API را در پنل، صفحه تنظیمات، بخش «کلیدهای API» بسازید. کلید کامل فقط همان لحظه ساخت نشان داده میشود؛ همان جا کپی اش کنید. کلید امضا در همان صفحه، بخش وبهوک، با دکمه «ساخت کلید امضا» ساخته میشود. کلیدی که با ag_test_ شروع شود فاکتور آزمایشی میسازد و پولی جابه جا نمیکند.

ساختن فاکتور برای سفارش

use AbanGateway\Laravel\Facades\AbanGateway;

$invoice = AbanGateway::invoiceForOrder((string) $order->id, [
    'amount_toman' => $order->total,
    'callback_url' => route('abangateway.webhook'),
    'return_url'   => route('orders.show', $order),
]);

if ($invoice->isPaid()) {
    return redirect()->route('orders.show', $order);
}

return redirect()->away($invoice->paymentUrl);

callback_url همان مسیری است که بسته ثبت کرده و خبر پرداخت به آن میرسد. return_url جایی است که خریدار بعد از پرداخت به آن برمیگردد؛ بهتر است صفحه سفارش باشد، نه صفحه «پرداخت موفق»، و چرایش را پایین تر میگوییم. شرط isPaid هم برای خریداری است که سفارشش را قبلاً پرداخت کرده و دوباره دکمه پرداخت را زده.

یه نکته ریز درباره همین callback_url: API ما آن را فقط با https میپذیرد. اگر سایت پشت CDN یا پروکسی است و لاراول درخواست ها را http میبیند، route() نشانی http میسازد و ساختن فاکتور با خطای unsafe_callback_url رد میشود. پروکسی را در تنظیم trustProxies معرفی کنید، یا در AppServiceProvider با URL::forceScheme('https') نشانی ها را https کنید.

چرا سفارش را با وبهوک تحویل میدهیم، نه با برگشت خریدار؟

بیشتر آموزش های درگاه پرداخت لاراول پرداخت را در مسیر برگشت تأیید میکنند: خریدار از صفحه بانک برمیگردد، کنترلر verify را صدا میزند و سفارش تحویل میشود. در درگاه شاپرکی این کار درست است، چون پرداخت همان جا تمام شده. در کارت به کارت ترتیب فرق دارد. خریدار پول را از اپ بانک خودش میزند و پیامک واریز کمی بعد میرسد؛ در آمار ۲۰ مهر ۱۴۰۵ ما، میانه فاصله ساخته شدن فاکتور تا پرداخت ۹۵ ثانیه بود. ممکن است خریدار اصلاً به سایت برنگردد: تب را بسته، اینترنتش قطع شده یا از گوشی دیگری پرداخت کرده.

پس منبع حقیقت، خبری است که سرور ما به callback_url میفرستد، و برگشت خریدار فقط یک بازدید است. نشانی برگشت را هر کسی میتواند باز کند. این همان منطقی است که فیش واریز جعلی را خطرناک میکند: چیزی که خریدار نشان میدهد، مدرک پولی که رسیده نیست.

درگاه پرداخت با لاراول از سفارش تا رویداد: فاکتور، پرداخت کارت به کارت، پیامک بانک، وبهوک و InvoicePaid خریدار شاید برنگردد؛ خبر پرداخت از سرور به سرور میرسد.

مسیر آماده و CSRF

بسته مسیر POST /abangateway/webhook را با اسم abangateway.webhook ثبت میکند و آن را بیرون از گروه web میگذارد. این جزئیات کوچک نیست. درخواستی که از سرور ما میآید توکن CSRF ندارد، و اگر مسیر وبهوک را خودتان در routes/web.php بسازید، لاراول آن را با خطای ۴۱۹ رد میکند. اگر به هر دلیلی مسیر خودتان را میخواهید، در لاراول ۱۱ به بعد آن را در bootstrap/app.php از بررسی CSRF مستثنا کنید.

مسیر آماده سه کار پشت سر هم انجام میدهد. امضای هدر X-Signature را روی بایت های خام بدنه چک میکند. بعد وضعیت فاکتور را با کلید خودتان از API میپرسد. آخر سر بر اساس جواب API، نه ادعای خود وبهوک، یکی از چهار رویداد را میفرستد: InvoicePaid، InvoicePartiallyPaid، InvoiceExpired یا InvoiceCancelled. با php artisan vendor:publish --tag=abangateway-config فایل تنظیمات بسته منتشر میشود؛ نشانی و middleware این مسیر را آنجا میشود عوض کرد، و اگر وبهوک را خودتان مدیریت میکنید، خاموشش کرد.

خبری که دو بار میرسد

اگر جواب سرور شما گم شود یا دیر برسد، ما خبر را دوباره میفرستیم. شنونده ای که سفارش را دو بار تحویل بدهد، یعنی دو اکانت یا دو ارسال برای یک پول. خواندن paid_at و بعد نوشتنش بیشتر وقت ها کافی است، ولی اگر دو خبر دقیقاً همزمان برسند، هر دو آن را خالی میبینند. امن ترش یک update شرطی است:

use AbanGateway\Laravel\Events\InvoicePaid;
use Illuminate\Support\Facades\Event;

Event::listen(function (InvoicePaid $event) {
    $marked = Order::whereKey($event->orderId())
        ->whereNull('paid_at')
        ->update(['paid_at' => now()]);

    if ($marked === 1) {
        DeliverOrder::dispatch($event->orderId());
    }
});

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

ده ثانیه برای جواب

سرور ما برای هر تحویل ده ثانیه منتظر جواب میماند. اگر شنونده شما ایمیل میفرستد، روی سرور دیگری اکانت میسازد یا فایل آماده میکند، این ده ثانیه زود تمام میشود؛ ما آن را شکست حساب میکنیم و دوباره میفرستیم، در حالی که سفارش شاید تحویل هم شده باشد. برای همین در نمونه بالا تحویل واقعی به یک job در صف سپرده شده و جواب وبهوک همان لحظه برمیگردد. برای job مقدار $tries بگذارید تا اگر سرویس بیرونی خطا داد، صف خودش دوباره امتحانش کند.

خریدار فردا برگشت؛ با فاکتور منقضی چه کنیم؟

فاکتور کارت به کارت مهلت دارد، چون مبلغ یکتایش فقط تا مدتی برای همان خریدار نگه داشته میشود. سفارش لاراولی شما ولی مهلت ندارد. خریدار امروز سبد را میبندد و فردا از ایمیل یادآوری برمیگردد.

اینجا تله ای هست که موقع ساختن ماژول درگاه پرداخت WHMCS خودمان به آن خوردیم. API ما برای شماره سفارش تکراری همان فاکتور قبلی را برمیگرداند، حتی اگر منقضی شده باشد. این رفتار عمدی است و جلوی دو بار پول گرفتن را میگیرد، ولی اگر createInvoice را با همان شماره سفارش صدا بزنید، خریدار به صفحه ای میرسد که دیگر زنده نیست.

invoiceForOrder برای همین ساخته شد. فاکتور باز با همان مبلغ را دوباره میدهد. اگر قبلی منقضی یا لغو شده باشد، تلاش تازه ای مثل 1043#2 میسازد. اگر مبلغ سفارش عوض شده باشد، مثلاً کالایی به سبد اضافه شده، فاکتور قبلی را لغو میکند و تازه میسازد. سفارشی هم که در یکی از تلاش ها پرداخت شده، همان را برمیگرداند تا دوباره پول نگیرید.

پکیج درگاه پرداخت لاراول و فاکتور منقضی: تلاش اول سفارش ۱۰۴۳ منقضی شد و تلاش دوم پرداخت شد شماره سفارش ثابت میماند؛ فقط شماره تلاش جلو میرود.

سه اشتباه که پیش از اولین فروش باید بگیرید

تومان یا ریال

مبلغ را یا با amount_toman بدهید یا با amount_rial، نه هر دو. ولی هر مبلغی که API برمیگرداند ریالی است. اشتباهی که راحت پیش میآید: مبلغ برگشتی را با قیمت تومانی سفارش مقایسه کنید و پرداخت درست را «مبلغ نادرست» ببینید، یا قیمت تومانی را در amount_rial بگذارید و خریدار یک دهم قیمت را بپردازد. پیش از اولین فروش، یک فاکتور کوچک آزمایشی بسازید و مبلغ صفحه پرداخت را با سبد خرید بسنجید.

کلیدی که ۴۰۱ میگیرد

کلید API با ag_live_ یا ag_test_ شروع میشود و بعدش دقیقاً ۴۸ نویسه دارد. مهر امسال یکی از برنامه نویس هایی که سایتش را به API ما وصل میکرد، روی هر درخواست ۴۰۱ میگرفت. کلیدی که میفرستاد با live_ شروع میشد، و وقتی دنبال ریشه اش گشتیم، دیدیم مستندات سایت خودمان هم پیشوند را همین طور نوشته بود. مستندات را درست کردیم. بسته هم شکل کلید را پیش از هر درخواست چک میکند و اگر جور نباشد، همان جا ConfigurationException میدهد، نه یک ۴۰۱ مبهم از سرور.

کیف پول کارمزد خالی

کارمزد آبان گیت وی از کیف پول کارمزدی کم میشود که خودتان شارژ میکنید، نه از پول فروش. اگر کیف پول خالی باشد، ساختن فاکتور با کد خطای insufficient_fee_wallet و وضعیت ۴۰۲ رد میشود. این خطا را بگیرید و به خریدار پیام کوتاه و قابل فهم نشان بدهید؛ صفحه خطای ۵۰۰ خریداری را که آماده پرداخت بوده فراری میدهد.

use AbanGateway\Exception\ApiException;

try {
    $invoice = AbanGateway::invoiceForOrder((string) $order->id, $params);
} catch (ApiException $e) {
    report($e); // مثلاً insufficient_fee_wallet
    return back()->with('error', 'پرداخت موقتاً در دسترس نیست. چند دقیقه دیگر دوباره امتحان کنید.');
}

شرط را روی getErrorCode بگذارید، نه روی متن خطا. متن ها ممکن است عوض شوند، کدها نه.

تست درگاه پرداخت در لاراول بدون جابه جا شدن پول

با کلید ag_test_ همه چیز مثل حالت واقعی است جز پول. فاکتور ساخته میشود، صفحه پرداخت باز میشود، و این یک خط پرداخت را شبیه سازی میکند:

AbanGateway::simulatePayment($invoice->id);

مهم ترین ویژگی اش این است که یک وبهوک واقعی و امضاشده به callback_url شما میفرستد. یعنی مسیر وبهوک، چک امضا، پرسیدن دوباره از API و شنونده InvoicePaid همه واقعاً اجرا میشوند. با کلید زنده این متد خطای sandbox_only میدهد، پس اشتباهی روی فروش واقعی اجرا نمیشود.

راستش جایی که احتمالاً اول گیر میکنید، لپ تاپ خودتان است. روی سیستم خودتان route('abangateway.webhook') نشانی localhost میسازد، و API ما نشانی محلی را نمیپذیرد، چون از اینترنت به آن نمیرسد. یا پروژه را روی یک سرور آزمایشی با دامنه و https بالا بیاورید، یا یک تونل https به سیستم خودتان باز کنید و همان را callback_url بدهید.

برای تست خودکار هم یک تجربه از تست های خود بسته: Event::fake() بدون ورودی، رویدادهای خود لاراول را هم نگه میدارد و اجرا نمیکند. فقط رویدادهای بسته را fake کنید، مثلاً Event::fake([InvoicePaid::class])، تا بقیه برنامه مثل همیشه کار کند.

با کدام نسخه لاراول و PHP کار میکند؟

این پکیج درگاه پرداخت لاراول روی نسخه های ۱۰ تا ۱۳ آزموده شده است؛ لاراول ۱۳ از نسخه 1.0.1 بسته، که ۲۳ مهر ۱۴۰۵ منتشر شد. هسته بسته فقط PHP 7.4 و افزونه های cURL و JSON میخواهد، ولی برای بخش لاراول، شرط PHP همان شرط نسخه لاراول شماست: لاراول ۱۳ دست کم PHP 8.3 میخواهد.

طبق جدول Support Policy در مستندات خود لاراول، لاراول ۱۳ از ۲۶ اسفند ۱۴۰۴ منتشر شده و پشتیبانی امنیتی لاراول ۱۱ از ۲۱ اسفند ۱۴۰۴ و لاراول ۱۰ از ۱۶ بهمن ۱۴۰۳ تمام شده است. بسته روی ۱۰ و ۱۱ هم کار میکند، ولی سایتی که پول میگیرد بهتر است روی نسخه ای باشد که هنوز وصله امنیتی میگیرد.

یک تجربه از آزمودن بسته روی لاراول ۱۰: Composer همه نسخه های پایدار ۱۰ را کنار گذاشت، چون برای همه شان هشدار امنیتی منتشر شده بود. اگر پروژه شما هنوز روی ۱۰ است و composer update ناگهان نسخه ای را که انتظار دارید نصب نمیکند، دلیلش احتمالاً همین است، و راه درستش ارتقاست، نه خاموش کردن هشدارها.

کجا این راه به کارتان نمیآید؟

درگاه پرداخت لاراول با کارت به کارت برای همه سایت ها نیست. خریدار باید خودش از اپ بانک انتقال بدهد، و سایتی که مشتری اش فقط صفحه بانک را میشناسد، چند خریدار را سر همین قدم از دست میدهد. شارژ خودکار اشتراک هم ممکن نیست، چون هر پرداخت را خود خریدار انجام میدهد. مبلغ های بزرگ هم به سقف کارت به کارت میخورند، که از خرداد ۱۴۰۵ روزانه ۱۵ میلیون تومان برای هر کارت است؛ آن وقت صفحه پرداخت مبلغ را چند تکه میکند و تا آخرین تکه نرسیده، فقط InvoicePartiallyPaid میگیرید.

ولی خب اگر اینماد و درگاه شاپرکی دارید، لازم نیست یکی را انتخاب کنید. هر دو را در صفحه پرداخت سایت بگذارید و بگذارید خریدار انتخاب کند. اگر هم سایت را بدون فریم ورک نوشته اید، همین بسته در PHP ساده کار میکند و قدم هایش در اتصال درگاه پرداخت به سایت آمده.

سؤالات متداول

بدون اینماد هم میشود در لاراول پرداخت آنلاین گرفت؟

با درگاه شاپرکی معمولاً نه، چون قرارداد درگاه اینترنتی برای دامنه اینماد میخواهد. با کارت به کارت بله: پول مستقیم به کارت خودتان میرود و برای ساختن کلید API فقط حساب در پنل آبان گیت وی لازم است.

پکیج درگاه پرداخت لاراول شما رایگان است؟

خود بسته رایگان و متن باز است، با پروانه MIT، و از Packagist نصب میشود. حق اشتراک ماهانه هم ندارد. کارمزد آبان گیت وی پلکانی است و فقط روی پرداخت موفق، از کیف پول کارمزد کم میشود، نه از پول فروش.

بدون لاراول، با PHP ساده هم کار میکند؟

بله. هسته بسته به فریم ورک وابسته نیست و از PHP 7.4 کار میکند: همان invoiceForOrder، چک امضای وبهوک با Webhook::parse و پرسیدن دوباره با Webhook::confirm. نمونه درخواست ها و خطاها در مستندات API آمده.

اگر سایت موقع پرداخت از دسترس خارج باشد، پرداخت گم میشود؟

نه. پول همان لحظه روی کارت شما نشسته و فاکتور در پنل پرداخت شده است؛ فقط خبرش به سایت نرسیده. هر خبر تا پنج بار فرستاده میشود و فاصله تلاش ها از ده ثانیه تا ده دقیقه بیشتر میشود. اگر سایت بیشتر از آن خاموش بود، خبر در پنل، صفحه گزارش ها، بخش «تحویل های وب هوک» با وضعیت «نرسید» میماند. وقتی سایت برگشت، دکمه «دوباره بفرست» را بزنید تا همان خبر با امضای تازه برسد. قاعده های تحویل در وبهوک و بررسی امضا آمده.

درگاه پرداخت بانک ملت یا سامان را هم میشود کنار این بسته داشت؟

بله، این دو به هم کاری ندارند. درگاه شاپرکی با پکیج و قرارداد خودش کار میکند و بسته ما با کلید API. در صفحه پرداخت دو گزینه بگذارید و برای هر سفارش همان راهی را صدا بزنید که خریدار انتخاب کرده. فقط یادتان باشد رویداد InvoicePaid فقط برای پرداخت های کارت به کارت میآید؛ پرداخت شاپرکی را همان مسیر برگشت درگاه خودش تأیید میکند.

درگاه پرداخت لاراول وقتی کارش را درست انجام داده که پول رسیده باشد، سفارش یک بار و فقط یک بار تحویل شده باشد، و خریداری که فردا برمیگردد به صفحه زنده برسد. اگر کد پذیرنده ندارید یا نمیخواهید منتظرش بمانید، با درگاه پرداخت کارت به کارت شروع کنید: حساب بسازید، کلید آزمایشی بگیرید و کل مسیر را پیش از اولین فروش امتحان کنید.

نظرها

هنوز نظری ثبت نشده. اولین نفر باشید.

نظر خود را بنویسید

امتیازتان به مقاله
نظرها پس از بررسی نمایش داده می‌شوند.

خواندن بعدی

درگاه پرداخت WHMCS: فاکتور هاست که با کارت به کارت پرداخت میشود و سرویس که خودکار فعال میشود
راهنمای پذیرش

درگاه پرداخت WHMCS؛ شاپرکی یا کارت به کارت، و فعال شدن خودکار سرویس

درگاه پرداخت WHMCS خودش پول نمیگیرد؛ ماژول درگاه پول را میگیرد و بعد WHMCS سرویس را فعال میکند. سه راه فروشنده ایرانی، از انتقال بانکی دستی تا کارت به کارت خودکار، و تله هایی که موقع ساختن ماژول دیدیم.

۱۹ مهر ۱۴۰۵ · ۱۰ دقیقه