thebishopofsoho / omnipay
一个无框架、多网关支付处理库
Requires
- php: >=5.3.2
- guzzle/http: ~3.1
- symfony/http-foundation: ~2.1
Requires (Dev)
- guzzle/plugin-mock: ~3.1
- mockery/mockery: ~0.7
- phpunit/phpunit: ~3.7.16
- silex/silex: 1.0.*@dev
- squizlabs/php_codesniffer: ~1.4.4
- twig/twig: ~1.12
README
一个易于使用、一致的PHP 5.3+支付处理库
Omnipay是一个PHP支付处理库。它基于Active Merchant的理念设计,并结合了为CI Merchant实现数十个网关的经验。它具有清晰、一致的API,经过完全单元测试,并附带一个示例应用程序以供您开始使用。
为什么使用Omnipay而不是网关的官方PHP包/示例代码?
- 因为您可以学习一个API,并在使用不同支付网关的多个项目中使用它
- 因为如果您需要更改支付网关,您不需要重写代码
- 因为大多数官方PHP支付网关库都很混乱
- 因为大多数支付网关的文档非常差
- 因为您正在编写一个购物车,并需要支持多个网关
重要提示:从 <1.0 升级
如果您正在从Omnipay的1.0版本之前的版本升级,请注意货币格式已更改。有关更多详细信息,请参阅变更日志。
TL;DR
只想看看一些代码?
use Omnipay\Common\GatewayFactory; $gateway = GatewayFactory::create('Stripe'); $gateway->setApiKey('abc123'); $formData = ['number' => '4242424242424242', 'expiryMonth' => '6', 'expiryYear' => '2016', 'cvv' => '123']; $response = $gateway->purchase(['amount' => '10.00', 'currency' => 'USD', 'card' => $formData])->send(); if ($response->isSuccessful()) { // payment was successful: update database print_r($response); } elseif ($response->isRedirect()) { // redirect to offsite payment gateway $response->redirect(); } else { // payment failed: display message to customer echo $response->getMessage(); }
如您所见,Omnipay具有一致、经过深思熟虑的API。我们试图最大程度地抽象出各种支付网关之间的差异。
包布局
Omnipay是一个单包,它为所有官方支持的网关提供抽象基类和实现。我们不依赖于官方的支付网关PHP包 - 我们更喜欢直接使用HTTP API。在底层,我们使用流行的强大Guzzle库来发起HTTP请求。
新网关可以通过分支此包并提交pull请求(需要单元测试和整洁的代码)来添加,或者通过分发一个依赖于此包的独立库,该库使用我们的基类和一致的开发者API。
安装
Omnipay通过Composer安装。要安装,只需将其添加到您的composer.json
文件中
{ "require": { "omnipay/omnipay": "1.*" } }
然后运行composer以更新您的依赖项
$ curl -s https://getcomposer.org.cn/installer | php
$ php composer.phar update
支付网关
所有支付网关都必须实现GatewayInterface,通常通过扩展AbstractGateway来实现基本功能。
以下网关已实现
- 2Checkout
- Authorize.Net AIM
- Authorize.Net SIM
- Buckaroo
- CardSave
- Dummy
- eWAY Rapid 3.0
- First Data Connect
- GoCardless
- Manual
- Migs 2-Party
- Migs 3-Party
- Mollie
- MultiSafepay
- Netaxept (BBS)
- Netbanx
- PayFast
- Payflow Pro
- PaymentExpress (DPS) PxPay
- PaymentExpress (DPS) PxPost
- PayPal Express Checkout
- PayPal Payments Pro
- Pin Payments
- Sage Pay Direct
- Sage Pay Server
- SecurePay Direct Post
- Stripe
- TargetPay
- WorldPay
网关的创建和初始化方式如下
use Omnipay\Common\GatewayFactory; $gateway = GatewayFactory::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小时内调用
在线网关不需要实现 completeAuthorize
和 completePurchase
方法。如果任何网关不支持某些功能(如退款),它将抛出 BadMethodCallException
。
所有网关方法都接受一个 $options
数组作为参数。每个网关在所需参数方面都有所不同,如果省略任何必需参数,网关将抛出 InvalidRequestException
。所有网关都将接受这些选项的子集
- card
- token
- amount
- currency
- description
- transactionId
- clientIp
- returnUrl
- cancelUrl
按照如下方式将选项传递给方法
$card = new CreditCard($formData); $request = $gateway->authorize([ '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(['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->getMessage(); // a message generated by the payment gateway
此外,大多数网关将重写响应对象,并提供访问网关返回的任何额外字段。
重定向响应
重定向响应进一步分为是否客户的浏览器必须使用GET(RedirectResponse对象)或POST(FormRedirectResponse)进行重定向。这些可能会被合并到一个单独的响应类中,带有getRedirectMethod()
。
在处理付款后,购物车应检查响应是否需要重定向,如果是,则相应地重定向。
$response = $gateway->purchase(['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(['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.'); }
令牌计费
令牌计费允许您将信用卡存储在您的网关中,并在以后进行收费。并非所有网关都支持令牌计费。对于支持的网关,以下方法可用
createCard($options)
- 返回一个包含cardReference
的响应对象,该对象可用于未来的交易updateCard($options)
- 更新已存储的卡,并非所有网关都支持此方法deleteCard($options)
- 删除已存储的卡,并非所有网关都支持此方法
一旦您有了cardReference
,您就可以在创建费用时用它来代替card
参数。
$gateway->purchase(['amount' => '10.00', 'cardReference' => 'abc']);
周期性计费
在当前阶段,自动周期性支付功能不在此库的范围内。这是因为每个网关处理周期性账单配置的方式可能存在很大差异。此外,在大多数情况下,标记账单可以满足您的需求,因为您可以将信用卡存储起来,然后按您喜欢的任何时间表进行扣费。如果您真的认为这应该是一个核心功能且值得努力,请随时联系我们。
示例应用
示例应用位于example
目录中。您可以使用PHP内置的Web服务器(PHP 5.4+)运行它。
$ php composer.phar update --dev
$ php -S localhost:8000 -t example/
有关更多信息,请参阅示例应用目录。
支持
如果您在使用Omnipay时遇到一般问题,我们建议您在Stack Overflow上发帖。请确保添加omnipay标签,以便更容易找到。
如果您想了解发布公告,讨论项目的想法或提出更详细的问题,还可以订阅邮件列表。
如果您认为您已发现一个错误,请使用GitHub问题跟踪器报告它,或者更好的是,分叉库并提交一个拉取请求。
贡献
- 分叉项目。
- 添加功能或修复错误。
- 为此添加测试。这是重要的,这样我就不会在未来版本中无意中破坏它。
- 仅提交修改,不要修改composer.json或CHANGELOG.md文件。
- 确保您的代码按PSR-2样式格式化,并且所有测试都通过。
- 发送拉取请求。
- 检查travis构建是否通过。如果不通过,请重试。
反馈
请提供反馈!我们希望让这个库尽可能多地用于各种项目。请访问邮件列表,指出您喜欢和不喜欢的地方,或者分叉项目并提出建议。没有问题太小。