adelowo/gbowo

hippy尼日利亚支付网关的统一API。包含适配器,适用于Paystack和Amplifypay。

1.7.1 2019-09-01 22:43 UTC

This package is auto-updated.

Last update: 2024-08-29 03:58:10 UTC


README

Latest Version on Packagist Software License Build Status Scrutinizer Coverage Quality Score Total Downloads

安装

以下方法之一安装 Gbowo

    composer require "adelowo/gbowo" : "~1.0"

用法

require_once 'vendor/autoload.php';

$adapter = new \Gbowo\Adapter\Paystack\PaystackAdapter();

$response = $adapter->charge();

对于 paystack 和 amplifypay 适配器,接收到的响应将是一个字符串,表示一个 authorization__url。您需要将浏览器重定向到该 URL 以完成交易。

其他适配器实现可能有所不同,例如内部重定向(通过适配器),但这并非出于良好原因。这是因为不同的系统可能以不同的方式通过 RequestResponse 对象等方式执行重定向。所以只要您能获取返回的 URL,就可以在您的框架或其他任何地方使用 Adapter

一个基本示例可能如下所示

header("Location : {$response}");
exit();

适配器

快速使用

$adapter = (new \Gbowo\GbowoFactory())->createAdapter("paystack"); //or "amplifypay"

return $adapter->charge();

深入使用

Gbowo 包含两个适配器:一个用于 paystack,另一个用于 Amplifypay

虽然这两个支付网关提供类似的功能,但还有一些细微的差异(显然)。

Gbowo 需要在环境中设置一些值,例如 $_ENV。对于 paystack,这是 $_ENV['PAYSTACK_SECRET_KEY'],而 amplifypay 适配器需要两个值:您的商户 ID 和 API 密钥。应以下格式提供:$_ENV['AMPLIFYPAY_MERCHANT_ID']$_ENV['AMPLIFYPAY_API_KEY']

强烈建议您将密钥和/或令牌保存在代码之外,并使用其他方式将它们加载到 $_ENV 中。我们并不强制执行此操作,但这是一种最佳实践,请参阅 12 Factor App Guideline。一个有助于此的库是 vlucas/phpdotenv。只需一个 .env 文件即可。请记住不要提交 .env 文件,否则它仍然没有离开您的“代码”。在 resources 目录中提供了一个示例 .env.example 文件。您可以将该文件复制并重命名为 .env 并放在您的根目录下。

$paystack = new \Gbowo\Adapter\Paystack\PaystackAdapter();

$amplifyPay = new \Gbowo\Adapter\Amplifypay\AmplifypayAdapter()

将自动创建一个 GuzzleHttp Client 实例,并使用从 $_ENV 获取的值来设置适当的授权头。

您可以通过在构造函数中提供一个 Guzzlehttp Client 实例来防止此“自动连接”。

$client = new \GuzzleHttp\Client(['key' => "value"]);

$amplifyPay = new \Gbowo\Adapter\Amplifypay\AmplifypayAdapter($client)

对于两个适配器,支付流程基本相同。用户首先进行一次交易,然后被重定向到输入付款详情的安全页面。在此之后(成功的支付请求),网关将重定向到您提供的回调 URL。在此 URL 中,您应该获取有关用户(现在是客户)的详细信息,例如 authorization_codetransaction_reference 等。这是用于循环交易,并应持久化到存储机制中。

应通过在适配器上调用 charge 方法来启动交易。根据前面所述,将重定向到响应。

要从您选择的网关注册的 URL 回调中获取数据,您必须在适配器上调用 getPaymentData 方法。它的响应将包含有关客户的一些数据。

//paystack adapter
var_dump($adapter->getPaymentData($_GET['trxref'])) ; //you should clean this up.

//amplifypay adapter
var_dump($adapter->getPaymentData($_GET['tran_response'])); // clean up

您还可以在数据库或其它存储机制中存储的一些交易参考上调用 getPaymentData 方法。

当您在 paystack 适配器上调用 getPaymentData 时,在发送响应之前,由 paystack 验证参考。

适配器方法。

Paystack :

  • charge(array $data = [])
  • getCustomer(int $id)
  • getAllCustomers()
  • chargeWithToken(array $userToken) // 一个token加上电子邮件地址(或自定义内容)
  • getPaymentData(string $transRef)
  • fetchPlan($planIdentifier)
  • fetchAllPlans()

查看 gbowo-paystack。它包含了一组额外的 插件 用于 paystack 适配器。

Amplifypay :

  • charge(array $data = [])
  • unsubcribeCustomerFromPlan(array $data)
  • chargeWithToken(array $userToken) // Amplifypay 中的 token 是一对值。
  • getPaymentData(string $transRef)
  • fetchPlan($planIdentifier)
  • fetchAllPlans()

charge 方法参数 (array $data = []) 中应该包含诸如金额、电子邮件、x、y、z 等信息。这些将被传递给支付网关。

自定义适配器

使用 laravel,请查看如何 添加您的自定义适配器

//let's assume it is an enterprisey app

$interswitch = new class implements \Gbowo\Contract\Adapter\AdapterInterface
{
    protected $interswitch;

    public function __construct()
    {
        $this->interswitch = new \stdClass(new \ArrayObject(new \stdClass())); // It wasn't me
    }

    public function charge(array $data = [])
    {
        return "charged by interswitch";
    }
};

$adapter = new \Gbowo\GbowoFactory(["interswitch" => $interswitch]); //add the interswith adapter as a custom one.

$interswitchAdapter = $adapter->createAdapter("interswitch");

$interswitchAdapter->charge(['a' => 'b', 'c' => 'd']);

通过插件扩展适配器

不同的网关实现了各种功能,我们无法在不失去理智的情况下支持所有这些功能。

支持所有功能会导致膨胀(接口、类膨胀)。例如,为支持 AdapterE 的功能 X 创建 InterfaceX,而 AdapterE 仍然使用 AdapterAAdapterQ 等的功能(和因此接口)。现在想象一下,这种场景在 4-5 个适配器中都会发生。

除了膨胀之外,我们无法创建一个(视觉)图来表示哪些接口正在使用,这会导致我们无法删除某个特定的类或接口,因为我们不知道(适配器)依赖它们。

为了防止这种情况,Gbowo 实现了一个插件架构,它简化了扩展或“向现有适配器添加新方法/行为”的过程,而无需继承或触摸核心代码。为了实现这一点,有一个包含逻辑的 Pluggable 特性,并且必须由适配器实现导入。

查看 paystack 适配器amplifypay,会发现它们在其公共 API 中没有上述描述的方法。事实上,它们仅公开了 3 个方法

  • __construct(Client $client = null) // 如果它算作一个
  • getHttpClient()
  • charge(array $data = []) // 这是从 AdapterInterface 实现中获得的。

但是查看它们的 registerPlugins 方法——这是从 Pluggable 特性中获得的——就能了解上述 Adapters method 部分中描述的方法是如何产生的。

插件是一个简单的 PHP 类,必须实现 PluginInterface。此接口公开了两个方法

  • getPluginAccessor() : string
  • setAdapter(Adapter $adapter)
  • handle(string $reference) // 尽可能多地使用类型提示。2、3 个参数?由您决定。

我在这里写了一篇关于这个的 详细文章

namespace Vendor\AdapterName\Plugin;

use Gbowo/Contract/Plugin/PluginInterface;
use Gbowo\Exception\TransactionVerficationFailedException;

class ApiPinger implements PluginInterface
{

    public function getPluginAccessor():string
    {
        return "pingApi"; //Oops.. Let's confirm if the api isn't dead before making any request. And it must be a string without parenthesis
    }

    /**
     * You can also leave this method out but you must extend the `AbstractPlugin` class. Doing so, means you'd have to get rid of the plugin interface here as the abstract plugin already does that.
     */
    public function setAdapter(AdapterInterface $adapter)
    {
        //useful for helpers like getting stuffs from "accessors" on the adapter instance like the already configured HttpClient object
        $this->adapter = $adapter ;
        return $this;
    }

    /**
     * Ping the gateway Api
     * @param  bool $shouldThrow. Should an exception be thrown if the api is down ?.
     * @return bool true - if the api is up and running.
                   false - if the api is down and $throw is set to false.
     * @throws \Exception if the api is down and $throw is set to false.
     */
    public function handle(bool $shouldThrow = false)
    {
        $response = $this->adapter->getHttpClient()->get("https://api.homepage.com");

        if ($response->getStatusCode() === 200 ) {
            return true;
        }

        if ($shouldThrow) {
            throw TransactionVerficationFailedException::createFromResponse($response);
        }

        return false;
    }
}

createFromResponse 是一个包装器,允许客户端代码检查响应以了解为什么失败(例如,无效的 HTTP 状态代码)。您应该在异常上调用 getResponse 以检查它。这同样适用于 Gbowo 提供的官方插件。

在这里,getPluginAccessor 非常引人关注,因为它决定了方法调用应该延迟到哪个插件。这是通过在 Pluggable 特性中的魔法方法 __call 来实现的。

$adapter->addPlugin(new Vendor\AdapterName\Plugin\ApiPinger(PaystackAdapter::API_LINK));
//Usage like this

$adapter->pingApi(true);
$adapter->pingApi();

并非所有插件最终都会进入核心,甚至核心中的所有插件在实例化时也不会全部“添加”。例如,GetAllCustomers 插件并没有内部添加到 PaystackAdapter 中。要使用该插件,您必须自行添加。

$adapter->addPlugin(new GetAllCustomers(PaystackAdapter::API_LINK))

框架集成

Laravel

v1.5.0 之前,此包提供 alongside 一个 Laravel 桥接器,但考虑到最佳利益,它 已移至其自己的仓库

如果您从之前的版本升级到 v1.5.0,请注意您需要运行 composer require adelowo/laravel-gbowo

请注意,给 Laravel 桥接器一个自己的生命并不意味着 BC break。命名空间已被保留,将继续按原样工作。

示例应用

Gbowo-app - 使用 SlimPHP 构建。

贡献

太棒了,我非常愿意这样做。Fork,发送 PR。但嘿,单元测试是一个绝妙的主意。让我们有更多这样的。

错误报告、问题跟踪和安全漏洞

请使用 问题跟踪器 进行错误报告、功能请求等,但除安全问题外。如果您确实发现了一个漏洞,请发送邮件至 me@lanreadelowo.com

许可证

MIT