shetabit/multipay

PHP支付网关集成包


README

PHP支付网关

Software License Latest Version on Packagist Total Downloads on Packagist StyleCI Maintainability Quality Score

这是一个用于支付网关集成的PHP包。本包支持PHP 7.2+

捐赠我,如果你喜欢这个包 😎 :bowtie:

对于Laravel集成,你可以使用shetabit/payment包。

此包支持多个驱动程序,如果你在当前驱动程序列表(以下列表)中找不到它们,你可以创建自定义驱动程序。

目录

可用驱动程序列表

通过创建 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. 在配置文件中,您可以设置用于所有付款的 默认驱动程序,也可以在运行时更改驱动程序。

选择您在应用程序中希望使用的网关。然后将它设置为默认驱动程序,这样您就无需在每个地方指定它。但是,您也可以在项目中使用多个网关。

// Eg. if you want to use zarinpal.
'default' => 'zarinpal',

然后在驱动程序数组中填写该网关的凭据。

'drivers' => [
    'zarinpal' => [
        // Fill in the credentials here.
        '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;

    // load the config file from your project
    $paymentConfig = require('path/to/payment.php');

    $payment = new Payment($paymentConfig);

如何使用

您的 Invoice 包含付款详情,所以我们将首先介绍 Invoice 类。

与发票一起工作

在进行任何操作之前,您需要使用 Invoice 类创建发票。

在您的代码中,使用如下所示的方式

// At the top of the file.
use Shetabit\Multipay\Invoice;
...

// Create new invoice.
$invoice = new Invoice;

// Set invoice amount.
$invoice->amount(1000);

// Add invoice details: There are 4 syntax available for this.
// 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: 设置发票的唯一 ID
  • getUuid: 获取发票当前的唯一 ID
  • detail: 将一些自定义详情附加到发票
  • getDetails: 获取所有自定义详情
  • amount: 设置发票金额
  • getAmount: 获取发票金额
  • transactionId: 设置发票付款交易 ID
  • getTransactionId: 获取付款交易 ID
  • via: 设置支付发票时使用的驱动程序
  • getDriver: 获取驱动程序

采购发票

为了支付发票,我们需要付款交易 ID。我们购买发票以检索交易 ID

// At the top of the file.
use Shetabit\Multipay\Invoice;
use Shetabit\Multipay\Payment;
...

// load the config file from your project
$paymentConfig = require('path/to/payment.php');

$payment = new Payment($paymentConfig);


// Create new invoice.
$invoice = (new Invoice)->amount(1000);

// Purchase the given invoice.
$payment->purchase($invoice,function($driver, $transactionId) {
	// We can store $transactionId in database.
});

// Purchase method accepts a callback function.
$payment->purchase($invoice, function($driver, $transactionId) {
    // We can store $transactionId in database.
});

// You can specify callbackUrl
$payment->callbackUrl('http://yoursite.com/verify')->purchase(
    $invoice,
    function($driver, $transactionId) {
    	// We can store $transactionId in database.
	}
);

支付发票

购买发票后,我们可以将用户重定向到银行付款页面

// At the top of the file.
use Shetabit\Multipay\Invoice;
use Shetabit\Multipay\Payment;
...

// load the config file from your project
$paymentConfig = require('path/to/payment.php');

$payment = new Payment($paymentConfig);


// Create new invoice.
$invoice = (new Invoice)->amount(1000);

// Purchase and pay the given invoice.
// You should use return statement to redirect user to the bank page.
return $payment->purchase($invoice, function($driver, $transactionId) {
    // Store transactionId in database as we need it to verify payment in the future.
})->pay()->render();

// Do all things together in a single line.
return $payment->purchase(
    (new Invoice)->amount(1000), 
    function($driver, $transactionId) {
    	// Store transactionId in database.
        // We need the transactionId to verify payment in the future.
	}
)->pay()->render();

// Retrieve json format of Redirection (in this case you can handle redirection to bank gateway)
return $payment->purchase(
    (new Invoice)->amount(1000), 
    function($driver, $transactionId) {
    	// Store transactionId in database.
        // We need the transactionId to verify payment in the future.
	}
)->pay()->toJson();

验证支付

当用户完成付款时,银行将他们重定向到您的网站,然后您需要 验证您的付款 以确保 发票支付

// At the top of the file.
use Shetabit\Multipay\Payment;
use Shetabit\Multipay\Exceptions\InvalidPaymentException;
...

// load the config file from your project
$paymentConfig = require('path/to/payment.php');

$payment = new Payment($paymentConfig);


// You need to verify the payment to ensure the invoice has been paid successfully.
// We use transaction id to verify payments
// It is a good practice to add invoice amount as well.
try {
	$receipt = $payment->amount(1000)->transactionId($transaction_id)->verify();

    // You can show payment referenceId to the user.
    echo $receipt->getReferenceId();

    ...
} catch (InvalidPaymentException $exception) {
    /**
    	when payment is not verified, it will throw an exception.
    	We can catch the exception to handle invalid payments.
    	getMessage method, returns a suitable message that can be used in user interface.
    **/
    echo $exception->getMessage();
}

有用方法

  • callbackUrl: 可以用于在运行时更改回调 URL。
    // At the top of the file.
    use Shetabit\Multipay\Invoice;
    use Shetabit\Multipay\Payment;
    ...
    
    // load the config file from your project
    $paymentConfig = require('path/to/payment.php');
    
    $payment = new Payment($paymentConfig);
    
    
    // Create new invoice.
    $invoice = (new Invoice)->amount(1000);
    
    // Purchase the given invoice.
    $payment->callbackUrl($url)->purchase(
        $invoice, 
        function($driver, $transactionId) {
        // We can store $transactionId in database.
    	}
    );
  • amount: 您可以直接设置发票金额
    // At the top of the file.
    use Shetabit\Multipay\Invoice;
    use Shetabit\Multipay\Payment;
    ...
    
    // load the config file from your project
    $paymentConfig = require('path/to/payment.php');
    
    $payment = new Payment($paymentConfig);
    
    
    // Purchase (we set invoice to null).
    $payment->callbackUrl($url)->amount(1000)->purchase(
        null,
        function($driver, $transactionId) {
        // We can store $transactionId in database.
    	}
    );
  • via: 动态更改驱动程序
    // At the top of the file.
    use Shetabit\Multipay\Invoice;
    use Shetabit\Multipay\Payment;
    ...
    
    // load the config file from your project
    $paymentConfig = require('path/to/payment.php');
    
    $payment = new Payment($paymentConfig);
    
    
    // Create new invoice.
    $invoice = (new Invoice)->amount(1000);
    
    // Purchase the given invoice.
    $payment->via('driverName')->purchase(
        $invoice, 
        function($driver, $transactionId) {
        // We can store $transactionId in database.
    	}
    );
  • config: 动态设置驱动程序配置
    // At the top of the file.
    use Shetabit\Multipay\Invoice;
    use Shetabit\Multipay\Payment;
    ...
    
    // load the config file from your project
    $paymentConfig = require('path/to/payment.php');
    
    $payment = new Payment($paymentConfig);
    
    
    // Create new invoice.
    $invoice = (new Invoice)->amount(1000);
    
    // Purchase the given invoice with custom driver configs.
    $payment->config('mechandId', 'your mechand id')->purchase(
        $invoice,
        function($driver, $transactionId) {
        // We can store $transactionId in database.
    	}
    );
    
    // Also we can change multiple configs at the same time.
    $payment->config(['key1' => 'value1', 'key2' => 'value2'])->purchase(
        $invoice,
        function($driver, $transactionId) {
        // We can store $transactionId in database.
    	}
    );
  • custom fields: 使用网关的自定义字段(并非所有网关都支持此功能) SEP 网关支持最多 4 个自定义字段,并且可以将值设置为最多 50 个字符的字符串。这些自定义字段仅在用户面板中查看报告时显示。

    // At the top of the file.
    use Shetabit\Multipay\Invoice;
    ...
    
    
    // Create new invoice.
    $invoice = (new Invoice)->amount(1000);
    
    // Use invoice bag to store custom field values.
    $invoice->detail([
                'ResNum1' => $order->orderId,
                'ResNum2' => $customer->verifiedCode,
                'ResNum3' => $someValue,
                'ResNum4' => $someOtherValue,
                ]);

创建自定义驱动程序

首先,您需要在驱动程序数组中添加您驱动程序的名称,并且您还可以指定任何您想要配置的参数。

'drivers' => [
    'zarinpal' => [...],
    'my_driver' => [
        ... // Your Config Params here.
    ]
]

现在,您需要创建一个用于支付发票的驱动程序映射类。在您的驱动程序中,您只需扩展 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; // Invoice.

    protected $settings; // Driver settings.

    public function __construct(Invoice $invoice, $settings)
    {
        $this->invoice($invoice); // Set the invoice.
        $this->settings = (object) $settings; // Set settings.
    }

    // Purchase the invoice, save its transactionId and finaly return it.
    public function purchase() {
        // Request for a payment transaction id.
        ...

        $this->invoice->transactionId($transId);

        return $transId;
    }

    // Redirect into bank using transactionId, to complete the payment.
    public function pay() : RedirectionForm {
        // It is better to set bankApiUrl in config/payment.php and retrieve it here:
        $bankUrl = $this->settings->bankApiUrl; // bankApiUrl is the config name.

        // Prepare payment url.
        $payUrl = $bankUrl.$this->invoice->getTransactionId();

        // Redirect to the bank.
        $url = $payUrl;
        $inputs = [];
        $method = 'GET';

        return $this->redirectWithForm($url, $inputs, $method);
    }
  
    // Verify the payment (we must verify to ensure that user has paid the invoice).
    public function verify(): ReceiptInterface {
        $verifyPayment = $this->settings->verifyApiUrl;
  
        $verifyUrl = $verifyPayment.$this->invoice->getTransactionId();
  
        ...
  
        /**
			Then we send a request to $verifyUrl and if payment is not valid we throw an InvalidPaymentException with a suitable message.
        **/
        throw new InvalidPaymentException('a suitable message');
  
        /**
        	We create a receipt for this payment if everything goes normally.
        **/
        return new Receipt('driverName', 'payment_receipt_number');
    }
}

创建该类后,您必须在 payment.php 配置文件的 map 部分中指定它。

'map' => [
    ...
    'my_driver' => App\Packages\Multipay\Driver\MyDriver::class,
]

注意:您必须确保 map 数组的键与 drivers 数组的键相同。

事件

注意1:事件监听器将全局注册到所有支付中。

注意2:如果您想让您的监听器正常工作,您必须在目标事件分发之前订阅它们。

最好在您的应用程序的入口点或主要服务提供者中订阅事件,以便在任何事件分发之前进行订阅。

您可以监听3个事件

  1. 购买
  2. 支付
  3. 验证.
  • 购买:在发票购买成功后发生(购买发票完成后)。
// add purchase event listener
Payment::addPurchaseListener(function($driver, $invoice) {
    echo $driver;
    echo $invoice;
});
  • 支付:在准备支付发票时发生。
// add pay event listener
Payment::addPayListener(function($driver, $invoice) {
    echo 'first listener';
});

// we can add multiple listeners
Payment::addPayListener(function($driver, $invoice) {
    echo 'second listener';
});
  • 验证:在发票成功验证时发生。
// we can add multiple listeners and also remove them!!!

$firstListener = function($driver, $invoice) {
    echo 'first listener';
};

$secondListener = function($driver, $invoice) {
    echo 'second listener';
};

Payment::addVerifyListener($firstListener);
Payment::addVerifyListener($secondListener);

// remove first listener
Payment::removeVerifyListener($firstListener);

// if we call remove listener without any arguments, it will remove all listeners
Payment::removeVerifyListener(); // remove all verify listeners :D

本地驱动程序

Local 驱动程序可以模拟真实网关的支付流程,用于开发目的。

支付可以像任何其他驱动程序一样启动

$invoice = (new Invoice)->amount(10000);
$payment->via('local')->purchase($invoice, function($driver, $transactionId) {
    // a fake transaction ID is generated and returned.
})->pay()->render();

调用 render() 方法将渲染一个带有 接受取消 按钮的 HTML 表单,这些按钮模拟了真实支付网关相应的操作,并将重定向到指定的回调URL。返回的查询URL中始终会提供 transactionId 参数。

在收到回调请求后可以验证支付。

$receipt = $payment->via('local')->verify();

在支付成功的情况下,$receipt 将包含以下参数

[
'orderId' => // fake order number 
'traceNo' => // fake trace number (this should be stored in databse)
'referenceNo' => // generated transaction ID in `purchase` method callback
'cardNo' => // fake last four digits of card 
]

在支付取消的情况下,将抛出 PurchaseFailedException 来模拟网关验证失败。

可以通过 Invoice 详细信息包配置驱动程序功能。

  • 可用参数
$invoice->detail([
    // setting this value will cause `purchase` method to throw an `PurchaseFailedException` 
    // to simulate when a gateway can not initialize the payment.
        'failedPurchase' => 'custom message to decribe the error',

    // Setting this parameter will be shown in payment form.
        'orderId' => 4444,
]);
  • 外观

支付表单的外观可以通过 payment.php 文件中 local 驱动程序的配置参数进行自定义。

'local' => [
    // default callback url of the driver
    'callbackUrl' => '/callback',

    // main title of the form
    'title' => 'Test gateway',
  
    // a description to show under the title for more clarification
    'description' => 'This gateway is for using in development environments only.',
  
    // custom label to show as order No.
    'orderLabel' => 'Order No.',
  
    // custom label to show as payable amount
    'amountLabel' => 'Payable amount',
  
    // custom label of successful payment button
    'payButton' => 'Successful Payment',
  
    // custom label of cancel payment button
    'cancelButton' => 'Cancel Payment',
],

变更日志

有关最近更改的更多信息,请参阅 变更日志

贡献

有关详细信息,请参阅 贡献指南行为准则

安全性

如果您发现任何安全相关的问题,请通过电子邮件 khanzadimahdi@gmail.com 而不是使用问题跟踪器。

致谢

许可证

MIT许可(MIT)。有关更多信息,请参阅 许可文件