PHP支付网关
这是一个用于支付网关集成的PHP包。该包支持PHP 7.2+
。
如果您喜欢这个包,可以捐赠给我 :sunglasses: :bowtie:
对于Laravel集成,您可以使用shetabit/payment包。
此包可以使用多个驱动程序,如果在当前可用驱动程序列表中找不到所需的驱动程序,您可以创建自定义驱动程序。
目录
可用驱动程序列表
- aqayepardakht :heavy_check_mark:
- asanpardakht :heavy_check_mark:
- atipay :heavy_check_mark:
- azkiVam (分期付款) :heavy_check_mark:
- behpardakht (mellat) :heavy_check_mark:
- bitpay :heavy_check_mark:
- digipay :heavy_check_mark:
- etebarino (分期付款) :heavy_check_mark:
- fanavacard :heavy_check_mark:
- gooyapay :heavy_check_mark:
- idpay :heavy_check_mark:
- irankish :heavy_check_mark:
- jibit :heavy_check_mark:
- local :heavy_check_mark:
- minipay :heavy_check_mark:
- nextpay :heavy_check_mark:
- omidpay :heavy_check_mark:
- parsian :heavy_check_mark:
- pasargad :heavy_check_mark:
- payfa :heavy_check_mark:
- payir :heavy_check_mark:
- paypal (将在下一版本中添加)
- payping :heavy_check_mark:
- paystar :heavy_check_mark:
- poolam :heavy_check_mark:
- rayanpay :heavy_check_mark:
- sadad (melli) :heavy_check_mark:
- saman :heavy_check_mark:
- sep (saman electronic payment) Keshavarzi & Saderat :heavy_check_mark:
- sepehr (saderat) :heavy_check_mark:
- sepordeh :heavy_check_mark:
- sizpay :heavy_check_mark:
- snapppay :heavy_check_mark:
- toman :heavy_check_mark:
- vandar :heavy_check_mark:
- walleta (分期付款) :heavy_check_mark:
- yekpay :heavy_check_mark:
- zarinpal :heavy_check_mark:
- zibal :heavy_check_mark:
- 其他正在开发中。
通过创建pull requests
来帮助我添加以下网关
- stripe
- authorize
- 2checkout
- braintree
- skrill
- payU
- amazon payments
- wepay
- payoneer
- paysimple
如果列表中不存在,您可以创建自己的自定义驱动程序,请阅读
创建自定义驱动程序
部分。
安装
通过Composer安装
$ composer require shetabit/multipay
配置
a. 将config/payment.php
复制到项目中的某个位置。(您也可以在vendor/shetabit/multipay/config/payment.php
路径中找到它)。
b. 在配置文件中,您可以设置用于所有支付的默认驱动程序
,也可以在运行时更改驱动程序。
选择您想在应用程序中使用的网关。然后将其设为默认驱动程序,这样您就不必在每个地方都指定它。但是,您也可以在一个项目中使用多个网关。
// 例如,如果您想使用zarinpal。
'default' => 'zarinpal',
然后在驱动程序数组中填写该网关的凭据。
'drivers' => [
'zarinpal' => [
// 在此处填写凭据。
'apiPurchaseUrl' => 'https://www.zarinpal.com/pg/rest/WebGate/PaymentRequest.json',
'apiPaymentUrl' => 'https://www.zarinpal.com/pg/StartPay/',
'apiVerificationUrl' => 'https://www.zarinpal.com/pg/rest/WebGate/PaymentVerification.json',
'merchantId' => '',
'callbackUrl' => 'http://yoursite.com/path/to',
'description' => 'payment in '.config('app.name'),
],
...
]
c. 实例化Payment
类并将配置传递给它,如下所示:
use Shetabit\Multipay\Payment;
// 从项目中加载配置文件
$paymentConfig = require('path/to/payment.php');
$payment = new Payment($paymentConfig);
如何使用
您的Invoice
保存了支付详情,所以我们首先讨论Invoice
类。
处理发票
在做任何事情之前,您需要使用Invoice
类来创建发票。
在代码中,像这样使用它:
// 在文件顶部。
use Shetabit\Multipay\Invoice;
...
// 创建新发票。
$invoice = new Invoice;
// 设置发票金额。
$invoice->amount(1000);
// 添加发票详情:有4种可用语法。
// 1
$invoice->detail(['detailName' => 'your detail goes here']);
// 2
$invoice->detail('detailName','your detail goes here');
// 3
$invoice->detail(['name1' => 'detail1','name2' => 'detail2']);
// 4
$invoice->detail('detailName1','your detail1 goes here')
->detail('detailName2','your detail2 goes here');
可用方法:
uuid
: 设置发票的唯一IDgetUuid
: 获取发票当前的唯一IDdetail
: 在发票中附加一些自定义详情getDetails
: 获取所有自定义详情amount
: 设置发票金额getAmount
: 获取发票金额transactionId
: 设置发票支付交易IDgetTransactionId
: 获取支付交易IDvia
: 设置我们用来支付发票的驱动程序getDriver
: 获取驱动程序
购买发票
为了支付发票,我们需要支付交易ID。 我们购买发票以获取交易ID:
// 在文件顶部。
use Shetabit\Multipay\Invoice;
use Shetabit\Multipay\Payment;
...
// 从项目中加载配置文件
$paymentConfig = require('path/to/payment.php');
$payment = new Payment($paymentConfig);
// 创建新发票。
$invoice = (new Invoice)->amount(1000);
// 购买给定的发票。
$payment->purchase($invoice,function($driver, $transactionId) {
// 我们可以将$transactionId存储在数据库中。
});
// purchase方法接受一个回调函数。
$payment->purchase($invoice, function($driver, $transactionId) {
// 我们可以将$transactionId存储在数据库中。
});
// 您可以指定callbackUrl
$payment->callbackUrl('http://yoursite.com/verify')->purchase(
$invoice,
function($driver, $transactionId) {
// 我们可以将$transactionId存储在数据库中。
}
);
支付发票
购买发票后,我们可以将用户重定向到银行支付页面:
// 在文件顶部。
use Shetabit\Multipay\Invoice;
use Shetabit\Multipay\Payment;
...
// 从项目中加载配置文件
$paymentConfig = require('path/to/payment.php');
$payment = new Payment($paymentConfig);
// 创建新发票。
$invoice = (new Invoice)->amount(1000);
// 购买并支付给定发票。
// 你应该使用return语句将用户重定向到银行页面。
return $payment->purchase($invoice, function($driver, $transactionId) {
// 将transactionId存储在数据库中,因为我们将来需要它来验证支付。
})->pay()->render();
// 在一行中完成所有操作。
return $payment->purchase(
(new Invoice)->amount(1000),
function($driver, $transactionId) {
// 将transactionId存储在数据库中。
// 我们需要transactionId来验证未来的支付。
}
)->pay()->render();
// 获取重定向的JSON格式(在这种情况下你可以处理重定向到银行网关)
return $payment->purchase(
(new Invoice)->amount(1000),
function($driver, $transactionId) {
// 将transactionId存储在数据库中。
// 我们需要transactionId来验证未来的支付。
}
)->pay()->toJson();
验证支付
当用户完成支付后,银行会将他们重定向到你的网站,然后你需要验证你的支付以确保invoice
已经支付。
// 在文件顶部。
use Shetabit\Multipay\Payment;
use Shetabit\Multipay\Exceptions\InvalidPaymentException;
...
// 从你的项目加载配置文件
$paymentConfig = require('path/to/payment.php');
$payment = new Payment($paymentConfig);
// 你需要验证支付以确保发票已成功支付。
// 我们使用交易ID来验证支付
// 同时添加发票金额是个好做法。
try {
$receipt = $payment->amount(1000)->transactionId($transaction_id)->verify();
// 你可以向用户显示支付参考ID。
echo $receipt->getReferenceId();
...
} catch (InvalidPaymentException $exception) {
/**
当支付未验证时,它会抛出一个异常。
我们可以捕获异常来处理无效的支付。
getMessage方法返回一个适合在用户界面使用的消息。
**/
echo $exception->getMessage();
}
有用的方法
-
callbackUrl
: 可以用于在运行时更改回调URL。// 在文件顶部。 use Shetabit\Multipay\Invoice; use Shetabit\Multipay\Payment; ... // 从你的项目加载配置文件 $paymentConfig = require('path/to/payment.php'); $payment = new Payment($paymentConfig); // 创建新发票。 $invoice = (new Invoice)->amount(1000); // 购买给定发票。 $payment->callbackUrl($url)->purchase( $invoice, function($driver, $transactionId) { // 我们可以将$transactionId存储在数据库中。 } );
-
amount
: 你可以直接设置发票金额// 在文件顶部。 use Shetabit\Multipay\Invoice; use Shetabit\Multipay\Payment; ... // 从你的项目加载配置文件 $paymentConfig = require('path/to/payment.php'); $payment = new Payment($paymentConfig); // 购买(我们将发票设置为null)。 $payment->callbackUrl($url)->amount(1000)->purchase( null, function($driver, $transactionId) { // 我们可以将$transactionId存储在数据库中。 } );
-
via
: 动态更改驱动// 在文件顶部。 use Shetabit\Multipay\Invoice; use Shetabit\Multipay\Payment; ... // 从你的项目加载配置文件 $paymentConfig = require('path/to/payment.php'); $payment = new Payment($paymentConfig); // 创建新发票。 $invoice = (new Invoice)->amount(1000); // 购买给定发票。 $payment->via('driverName')->purchase( $invoice, function($driver, $transactionId) { // 我们可以将$transactionId存储在数据库中。 } );
-
config
: 动态设置驱动配置// 在文件顶部。 use Shetabit\Multipay\Invoice; use Shetabit\Multipay\Payment; ... // 从你的项目加载配置文件 $paymentConfig = require('path/to/payment.php'); $payment = new Payment($paymentConfig); // 创建新发票。 $invoice = (new Invoice)->amount(1000); // 使用自定义驱动配置购买给定发票。 $payment->config('mechandId', 'your mechand id')->purchase( $invoice, function($driver, $transactionId) { // 我们可以将$transactionId存储在数据库中。 } ); // 我们也可以同时更改多个配置。 $payment->config(['key1' => 'value1', 'key2' => 'value2'])->purchase( $invoice, function($driver, $transactionId) { // 我们可以将$transactionId存储在数据库中。 } );
-
自定义字段
: 使用网关的自定义字段(并非所有网关都支持此功能) SEP网关最多支持4个自定义字段,你可以将值设置为最多50个字符的字符串。 这些自定义字段只在用户面板查看报告时显示。// 在文件顶部。 use Shetabit\Multipay\Invoice; ... // 创建新发票。 $invoice = (new Invoice)->amount(1000); // 使用发票包来存储自定义字段值。 $invoice->detail([ 'ResNum1' => $order->orderId, 'ResNum2' => $customer->verifiedCode, 'ResNum3' => $someValue, 'ResNum4' => $someOtherValue, ]);
创建自定义驱动:
首先,你必须在drivers数组中添加你的驱动名称,同时也可以指定任何你想要的配置参数。
'drivers' => [
'zarinpal' => [...],
'my_driver' => [
... // 你的配置参数在这里。
]
]
现在你必须创建一个Driver Map Class,用于支付发票。
在你的驱动中,你只需要继承Shetabit\Multipay\Abstracts\Driver
。
例如,你创建了一个类:App\Packages\Multipay\Driver\MyDriver
。
namespace App\Packages\Multipay\Driver;
use Shetabit\Multipay\Abstracts\Driver;
use Shetabit\Multipay\Exceptions\InvalidPaymentException;
use Shetabit\Multipay\{Contracts\ReceiptInterface, Invoice, RedirectionForm, Receipt};
class MyDriver extends Driver
{
protected $invoice; // 发票。
protected $settings; // 驱动设置。
public function __construct(Invoice $invoice, $settings)
{
$this->invoice($invoice); // 设置发票。
$this->settings = (object) $settings; // 设置设置。
}
// 购买发票,保存其交易ID并最终返回。
public function purchase() {
// 请求支付交易ID。
...
$this->invoice->transactionId($transId);
return $transId;
}
// 使用交易ID重定向到银行,完成支付。
public function pay() : RedirectionForm {
// 最好在config/payment.php中设置bankApiUrl并在此处检索:
$bankUrl = $this->settings->bankApiUrl; // bankApiUrl是配置名称。
// 准备支付URL。
$payUrl = $bankUrl.$this->invoice->getTransactionId();
// 重定向到银行。
$url = $payUrl;
$inputs = [];
$method = 'GET';
return $this->redirectWithForm($url, $inputs, $method);
}
// 验证支付(我们必须验证以确保用户已支付发票)。
public function verify(): ReceiptInterface {
$verifyPayment = $this->settings->verifyApiUrl;
$verifyUrl = $verifyPayment.$this->invoice->getTransactionId();
...
/**
然后我们向$verifyUrl发送请求,如果支付无效,我们会抛出一个带有适当消息的InvalidPaymentException。
**/
throw new InvalidPaymentException('一个适当的消息');
/**
如果一切正常,我们为这次支付创建一个收据。
**/
return new Receipt('driverName', 'payment_receipt_number');
}
}
一旦你创建了这个类,你必须在payment.php
配置文件的map
部分指定它。
'map' => [
...
'my_driver' => App\Packages\Multipay\Driver\MyDriver::class,
]
注意: 你必须确保map
数组的键与drivers
数组的键相同。
事件:
注意1: 事件监听器将全局注册所有支付。
注意2: 如果你希望你的监听器正常工作,你必须在目标事件分发之前订阅它们。
最好在你的应用程序的入口点或主服务提供者中订阅事件,这样事件就会在任何事件分发之前被订阅。
你可以监听3个事件:
- purchase
- pay
- verify
- purchase: 发生在发票被购买时(成功购买发票后)。
// 添加购买事件监听器
Payment::addPurchaseListener(function($driver, $invoice) {
echo $driver;
echo $invoice;
});
- pay: 发生在准备支付发票时。
// 添加支付事件监听器
Payment::addPayListener(function($driver, $invoice) {
echo '第一个监听器';
});
// 我们可以添加多个监听器
Payment::addPayListener(function($driver, $invoice) {
echo '第二个监听器';
});
- verify: 发生在发票成功验证时。
// 我们可以添加多个监听器,也可以删除它们!!!
$firstListener = function($driver, $invoice) {
echo '第一个监听器';
};
$secondListener = function($driver, $invoice) {
echo '第二个监听器';
};
Payment::addVerifyListener($firstListener);
Payment::addVerifyListener($secondListener);
// 删除第一个监听器
Payment::removeVerifyListener($firstListener);
// 如果我们调用removeListener时不带任何参数,它将删除所有监听器
Payment::removeVerifyListener(); // 删除所有验证监听器 :D
本地驱动
Local
驱动可以模拟真实网关的支付流程,用于开发目的。
可以像任何其他驱动一样发起支付
$invoice = (new Invoice)->amount(10000);
$payment->via('local')->purchase($invoice, function($driver, $transactionId) {
// 生成并返回一个虚假的交易ID。
})->pay()->render();
调用render()
方法将渲染一个带有接受和取消按钮的HTML
表单,模拟真实支付网关的相应操作,并重定向到指定的回调URL。
transactionId
参数将始终在返回的查询URL中可用。
收到回调请求后可以验证支付。
$receipt = $payment->via('local')->verify();
如果支付成功,$receipt
将包含以下参数:
[
'orderId' => // 虚假订单号
'traceNo' => // 虚假跟踪号(应存储在数据库中)
'referenceNo' => // 在`purchase`方法回调中生成的交易ID
'cardNo' => // 虚假的卡号后四位
]
如果支付被取消,将抛出PurchaseFailedException
异常以模拟网关验证失败。
可以通过Invoice
详情包配置驱动功能。
-
可用参数
$invoice->detail([
// 设置此值将导致`purchase`方法抛出`PurchaseFailedException`异常
// 以模拟网关无法初始化支付的情况。
'failedPurchase' => '描述错误的自定义消息',
// 设置此参数将在支付表单中显示。
'orderId' => 4444,
]);
-
外观
支付表单的外观可以通过payment.php
文件中local
驱动的配置参数进行自定义。
'local' => [
// 驱动的默认回调URL
'callbackUrl' => '/callback',
// 表单的主标题
'title' => '测试网关',
// 在标题下显示的更多说明
'description' => '此网关仅用于开发环境。',
// 显示为订单号的自定义标签
'orderLabel' => '订单号',
// 显示为应付金额的自定义标签
'amountLabel' => '应付金额',
// 成功支付按钮的自定义标签
'payButton' => '成功支付',
// 取消支付按钮的自定义标签
'cancelButton' => '取消支付',
],
更新日志
请查看CHANGELOG了解最近的更改。
贡献
请查看CONTRIBUTING和CONDUCT了解详情。
安全
如果您发现任何与安全相关的问题,请发送邮件至khanzadimahdi@gmail.com,而不是使用问题跟踪器。
致谢
许可证
MIT许可证(MIT)。请查看许可证文件了解更多信息。