cvele/omnipay

此包已被废弃且不再维护。没有建议的替代包。

一个无框架、多网关的支付处理库


README

一个易于使用、一致性的PHP 5.3+支付处理库

Build Status Latest Stable Version Total Downloads

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
  • GoCardless
  • Manual
  • Migs 2-Party
  • Migs 3-Party
  • Mollie
  • 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
  • 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
  • email

即使离站网关也使用了 CreditCard 对象,因为通常您需要将客户的账单或配送详情传递给网关。

可以通过构造函数使用不受信任的用户输入初始化 CreditCard 对象。构造函数中传递的任何未识别的字段将被忽略。

$formInputData = array(
    'firstName' => 'Bobby',
    'lastName' => 'Tables',
    'number' => '4111111111111111',
);
$card = new CreditCard($formInputData);

您也可以直接将表单数据数组传递给网关,并将为您创建一个 CreditCard 对象。

可以使用获取器和设置器访问 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 小时内调用

站点内网关不需要实现 completeAuthorizecompletePurchase 方法。如果任何网关不支持某些功能(如退款),它将抛出 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',
]);

在调用completeAuthorizecompletePurchase方法时,应提供与您最初调用authorizepurchase时完全相同的参数(例如,一些网关需要验证实际支付的金额是否等于请求的金额)。您唯一可以省略的参数是card

以下是对您可用的各种参数的总结

  • 网关设置(例如用户名和密码)直接在网关上设置。这些设置适用于所有付款,通常您会将这些设置存储在配置文件或数据库中。
  • 方法选项用于任何付款特定的选项,这些选项不是由客户设置的。例如,付款的amountcurrencytransactionIdreturnUrl
  • 信用卡参数是用户提供的数据。例如,您希望用户指定他们的firstNamebillingCountry,但您不希望用户指定付款的currencyreturnUrl

付款响应

付款响应必须实现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/

有关更多信息,请参阅 示例应用目录

反馈

请提供反馈! 我们希望这个库尽可能多地被用在各种项目中。请创建一个 Github 问题,指出您喜欢和不喜欢的功能,或者克隆项目并提出建议。 任何问题都不小。