krsman / omnipay
Omnipay支付处理库
Requires
- php: ^5.6|^7
- omnipay/common: ^3
- php-http/guzzle6-adapter: ^1.1|^2
Requires (Dev)
- omnipay/tests: ^3
- dev-master / 3.0.x-dev
- v3.0.2
- v3.0.1
- v3.0.0
- v3.0-alpha.1
- 2.3.x-dev
- 2.3.2
- v2.3.1
- v2.3.0
- v2.1.0
- v2.0.0
- 1.1.x-dev
- v1.1.0
- 1.0.x-dev
- v1.0.4
- v1.0.3
- v1.0.2
- v1.0.1
- v1.0.0
- v0.9.3
- v0.9.2
- v0.9.1
- v0.9.0
- v0.8.4
- v0.8.3
- v0.8.2
- v0.8.1
- v0.8.0
- v0.7.1
- v0.7.0
- v0.6.1
- v0.6.0
- dev-barryvdh-travis-73_74
- dev-guzzle6-adapter-v2
- dev-judgej-patch-1
- dev-delatbabel-patch-1
This package is auto-updated.
Last update: 2024-09-18 06:42:02 UTC
README
一个易于使用、统一的PHP支付处理库
Omnipay是一个PHP支付处理库。它基于Active Merchant的理念设计,并结合了为[CI Merchant]实现数十个网关的经验。它具有清晰、一致的API,经过全面单元测试,并提供了一个示例应用程序以帮助您入门。
为什么使用Omnipay而不是网关的官方PHP包/示例代码?
- 因为你可以学习一个API,并将其用于使用不同支付网关的多个项目中
- 因为如果你需要更换支付网关,你不需要重写代码
- 因为大多数官方PHP支付网关库都是一团糟
- 因为大多数支付网关的文档非常糟糕
- 因为你在编写购物车并且需要支持多个网关
TL;DR
只想看看一些代码?
use Omnipay\Omnipay; $gateway = Omnipay::create('Stripe'); $gateway->setApiKey('abc123'); $formData = array('number' => '4242424242424242', 'expiryMonth' => '6', 'expiryYear' => '2030', 'cvv' => '123'); $response = $gateway->purchase(array('amount' => '10.00', 'currency' => 'USD', 'card' => $formData))->send(); if ($response->isRedirect()) { // redirect to offsite payment gateway $response->redirect(); } elseif ($response->isSuccessful()) { // payment was successful: update database print_r($response); } else { // payment failed: display message to customer echo $response->getMessage(); }
如你所见,Omnipay具有一致、经过深思熟虑的API。我们试图最大程度地抽象各种支付网关之间的差异。
包布局
Omnipay是一组包,它们都依赖于omnipay/common包,以提供一致的接口。不依赖于官方的支付网关PHP包 - 我们更喜欢直接使用HTTP API。在底层,我们使用流行的强大库PHP-HTTP来发送HTTP请求。在使用league/omnipay
时,默认需要Guzzle适配器。
可以通过克隆现有包的布局来创建新的网关。在选择包名称时,请不要使用omnipay
供应商前缀,因为这表示它是官方支持的。你应该使用自己的用户名作为供应商前缀,并在包名称前添加omnipay-
以使其清晰,表明您的包与Omnipay兼容。例如,如果你的GitHub用户名是santa
,并且你正在实现giftpay
支付库,一个不错的composer包名称将是santa/omnipay-giftpay
。
安装
Omnipay通过Composer安装。对于大多数使用场景,你需要要求league/omnipay
和个别网关
composer require league/omnipay:^3 omnipay/paypal
如果你想要使用自己的HTTP客户端而不是Guzzle(这是league/omnipay
的默认值),你可以要求league/common
和任何php-http/client-implementation
(请参阅PHP Http)
composer require league/common:^3 omnipay/paypal php-http/buzz-adapter
从v2升级到v3
如果你的网关支持v3,你可以要求该版本。请确保你要求league/omnipay
或单独的Http适配器。
如果还没有v3的版本,请提出一个issue或自己升级网关并创建一个PR。请参阅omnipay/common的升级指南
注意:包名称已从
omnipay/omnipay
更改为league/omnipay
以适应v3
支付网关
所有支付网关都必须实现GatewayInterface接口,并且通常会扩展AbstractGateway以实现基本功能。
以下是一些可用的网关:
网关的创建和初始化方式如下:
use Omnipay\Omnipay; $gateway = Omnipay::create('PayPal_Express'); $gateway->setUsername('adrian'); $gateway->setPassword('12345');
大多数设置都是针对特定网关的。如果您需要查询网关以获取可用设置的列表,可以调用getDefaultParameters()
。
$settings = $gateway->getDefaultParameters(); // default settings array format: array( 'username' => '', // string variable 'testMode' => false, // boolean variable 'landingPage' => array('billing', 'login'), // enum variable, first item should be treated as default );
通常,大多数支付网关可以分为以下两种类型:
- 离线网关,如PayPal Express,其中客户将被重定向到第三方网站以输入付款详情
- 在线(商家托管)网关,如PayPal Pro,其中客户在您的网站上输入其信用卡详情
然而,也有一些网关,如Sage Pay Direct,您可以在网站上获取信用卡详情,然后根据客户的卡是否支持3D Secure身份验证可选性地进行重定向。因此,没有必要在两种类型的网关之间进行区分(除了支持的方法不同之外)。
信用卡/付款表单输入
用户表单输入被定向到一个CreditCard对象。这提供了一个安全的方式来接受用户输入。
CreditCard
对象具有以下字段:
- firstName
- lastName
- number
- expiryMonth
- expiryYear
- startMonth
- startYear
- cvv
- issueNumber
- type
- billingAddress1
- billingAddress2
- billingCity
- billingPostcode
- billingState
- billingCountry
- billingPhone
- shippingAddress1
- shippingAddress2
- shippingCity
- shippingPostcode
- shippingState
- shippingCountry
- shippingPhone
- company
即使离线网关也会使用CreditCard
对象,因为通常您需要通过网关传递客户的账单或送货详情。
CreditCard
对象可以通过构造函数使用不受信任的用户输入进行初始化。传递给构造函数的任何未识别字段将被忽略。
$formInputData = array( 'firstName' => 'Bobby', 'lastName' => 'Tables', 'number' => '4111111111111111', ); $card = new CreditCard($formInputData);
您还可以直接将表单数据数组传递给网关,网关将为您创建一个CreditCard
对象。
您可以使用getter和setter访问CreditCard字段
$number = $card->getNumber(); $card->setFirstName('Adrian');
如果您提交明显无效的信用卡详情(缺少必填字段或未通过Luhn检查的数字),将抛出InvalidCreditCardException。在将详情提交给网关之前,您应使用框架的验证库验证卡片详情,以避免不必要的API调用。
对于在线支付网关,以下卡片字段通常是必需的:
- firstName
- lastName
- number
- expiryMonth
- expiryYear
- cvv
您还可以通过调用Helper::validateLuhn($number)
使用Luhn算法验证卡片号码。
网关方法
网关实现的主要方法包括:
authorize($options)
- 在客户的卡上授权一定金额completeAuthorize($options)
- 处理授权后的离线网关返回capture($options)
- 捕获先前已授权的金额purchase($options)
- 授权并立即在客户的卡上捕获一定金额completePurchase($options)
- 处理购买后的离线网关返回refund($options)
- 退还已处理的交易void($options)
- 通常只能在提交交易后24小时内调用acceptNotification()
- 将来自离线网关的传入请求转换为用于进一步处理的通用通知对象
现场网关无需实现completeAuthorize
和completePurchase
方法。未收到支付通知的网关无需实现acceptNotification
方法。如果任何网关不支持某些功能(例如退款),则将抛出BadMethodCallException
。
除了acceptNotification
方法之外的所有网关方法都将$options
数组作为参数。acceptNotification
方法不接收任何参数,将隐式访问HTTP URL变量或POST数据。每个网关所需的参数各不相同,如果您省略了任何必需的参数,网关将抛出InvalidRequestException
。所有网关都接受这些选项的子集
- 卡片
- 令牌
- 金额
- 货币
- 描述
- 交易ID
- 客户端IP
- 返回URL
- 取消URL
像这样将选项传递给方法
$card = new CreditCard($formData); $request = $gateway->authorize(array( 'amount' => '10.00', // this represents $10.00 'card' => $card, 'returnUrl' => 'https://www.example.com/return', ));
在调用completeAuthorize
或completePurchase
方法时,应提供与您最初进行authorize
或purchase
调用时完全相同的参数(某些网关可能需要验证例如实际支付金额是否等于请求的金额)。您可以省略的唯一参数是card
。
总结您可用的各种参数
- 网关设置(例如用户名和密码)直接在网关上设置。这些设置适用于所有支付,通常您将它们存储在配置文件或数据库中。
- 方法选项用于任何特定于支付的选项,这些选项不是由客户设置的。例如,支付
amount
、currency
、transactionId
和returnUrl
。 - 信用卡参数是用户提供的数据。例如,您希望用户指定他们的
firstName
和billingCountry
,但您不希望用户指定支付currency
或returnUrl
。
支付响应
支付响应必须实现ResponseInterface。主要有两种类型的响应
- 支付成功(标准响应)
- 网站需要重定向到外部支付表单(重定向响应)
成功响应
对于成功响应,通常会生成一个参考,可以在以后的时间捕获或退款交易。以下方法总是可用的
$response = $gateway->purchase(array('amount' => '10.00', 'card' => $card))->send(); $response->isSuccessful(); // is the response successful? $response->isRedirect(); // is the response a redirect? $response->getTransactionReference(); // a reference generated by the payment gateway $response->getTransactionId(); // the reference set by the originating website if available. $response->getMessage(); // a message generated by the payment gateway
此外,大多数网关将重写响应对象,并提供访问网关返回的任何额外字段。
重定向响应
重定向响应进一步细分为客户浏览器是否必须使用GET(RedirectResponse对象)或POST(FormRedirectResponse)重定向。这些可以合并为单个响应类,具有getRedirectMethod()
。
处理支付后,购物车应检查响应是否需要重定向,如果是,则相应地重定向
$response = $gateway->purchase(array('amount' => '10.00', 'card' => $card))->send(); if ($response->isSuccessful()) { // payment is complete } elseif ($response->isRedirect()) { $response->redirect(); // this will automatically forward the customer } else { // not successful }
客户不会被自动转发,因为通常购物车或开发者会想自定义重定向方法(如果支付处理发生在AJAX调用中,他们可能想将JS返回给浏览器)。
要显示您自己的重定向页面,只需在响应上调用getRedirectUrl()
,然后相应地显示它
$url = $response->getRedirectUrl(); // for a form redirect, you can also call the following method: $data = $response->getRedirectData(); // associative array of fields which must be posted to the redirectUrl
错误处理
您可以通过在响应对象上调用isSuccessful()
来测试是否为成功响应。如果与网关通信出现错误,或您的请求明显无效,则会抛出异常。通常,如果网关不抛出异常,但返回不成功的响应,则是您应向客户显示的消息。如果抛出异常,则可能是您的代码中的错误(缺少必需的字段)或与网关的通信错误。
您可以通过将整个请求包装在try-catch块中来处理这两种情况
try { $response = $gateway->purchase(array('amount' => '10.00', 'card' => $card))->send(); if ($response->isSuccessful()) { // mark order as complete } elseif ($response->isRedirect()) { $response->redirect(); } else { // display error to customer exit($response->getMessage()); } } catch (\Exception $e) { // internal error, log exception and display a generic message to the customer exit('Sorry, there was an error processing your payment. Please try again later.'); }
测试模式和开发者模式
大多数网关允许您设置一个沙盒或开发者账户,使用不同的URL和凭证。一些网关还允许您对实时网站进行测试交易,但这不会导致实时交易。
仅实现开发者账户的网关(大多数网关)将其称为testMode。然而,Authorize.net实现了这两种模式,并将其称为developerMode。
当使用多个网关实现时,您应使用以下类似的构造
if ($is_developer_mode) { if (method_exists($gateway, 'setDeveloperMode')) { $gateway->setDeveloperMode(TRUE); } else { $gateway->setTestMode(TRUE); } }
令牌计费
令牌计费允许您将信用卡存储在您的网关中,并在以后进行计费。并非所有网关都支持令牌计费。对于支持的网关,以下方法可用
createCard($options)
- 返回一个包含cardReference
的响应对象,该对象可用于未来的交易updateCard($options)
- 更新存储的信用卡,并非所有网关都支持此方法deleteCard($options)
- 删除存储的信用卡,并非所有网关都支持此方法
一旦您有了cardReference
,您可以在创建费用时使用它而不是使用card
参数
$gateway->purchase(array('amount' => '10.00', 'cardReference' => 'abc'));
周期性计费
目前,此库不包含自动周期性支付功能。这是因为每个网关处理周期性计费配置文件的方式可能存在很大差异。此外,在大多数情况下,令牌计费可以满足您的需求,因为您可以将信用卡存储起来,然后按您喜欢的任何时间表进行计费。如果您真的认为这应该是一个核心功能并且值得努力,请随时联系我们。
入站通知
一些网关(例如Cybersource、GoPay)提供HTTP通知,以通知商家支付完成(或通常,状态)。为了帮助处理此类通知,acceptNotification()
方法将提取交易引用和支付状态从HTTP请求中,并返回一个通用的NotificationInterface
。
$notification = $gateway->acceptNotification(); $notification->getTransactionReference(); // A reference provided by the gateway to represent this transaction $notification->getTransactionStatus(); // Current status of the transaction, one of NotificationInterface::STATUS_* $notification->getMessage(); // Additional message, if any, provided by the gateway // update the status of the corresponding transaction in your database
注意:一些早期网关使用completeAuthorize
和completePurchase
消息来处理入站通知。这些消息正在转换为acceptNotification
消息,并被弃用。在OmniPay 2.x中,它们不会被删除,但建议在方便时切换到acceptNotification
消息。例如,Sage Pay Server的completeAuthorize
现在由acceptNotification
处理。
示例应用
在omnipay/example仓库中提供了一个示例应用。您可以使用PHP内置的web服务器(PHP 5.4+)运行它。
$ php composer.phar update --dev
$ php -S localhost:8000
有关更多信息,请参阅Omnipay示例应用。
支持
如果您在使用Omnipay时遇到一般问题,我们建议在Stack Overflow上发布。确保添加omnipay标签,以便容易找到。
如果您想了解发布公告、讨论项目想法或提出更详细的问题,还可以订阅邮件列表。
如果您认为您已经发现了错误,请使用相应包的GitHub问题跟踪器报告它,或者更好的是,fork库并提交pull请求。
安全性
如果您发现任何与安全相关的问题,请通过电子邮件barryvdh@gmail.com联系,而不是使用问题跟踪器。
反馈
请提供反馈! 我们希望这个库在尽可能多的项目中变得有用。请访问邮件列表,指出你喜欢和不喜欢的地方,或者分叉项目提出建议。没有问题太小。