adelowo / gbowo
hippy尼日利亚支付网关的统一API。包含适配器,适用于Paystack和Amplifypay。
Requires
- php: ~7.0
- guzzlehttp/guzzle: ^6.2
Requires (Dev)
- mockery/mockery: ^0.9.5
- phpunit/phpunit: ^6.0
- scrutinizer/ocular: ~1.1
- squizlabs/php_codesniffer: ~2.3
Suggests
- adelowo/gbowo-paystack: Extra plugins for the Paystack adapter
- vlucas/phpdotenv: Keep private tokens (secret keys) out of your code by loading them into `$_ENV` automagically from a .env file (~2.2).
README
安装
以下方法之一安装 Gbowo
- Composer (推荐)
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 以完成交易。
其他适配器实现可能有所不同,例如内部重定向(通过适配器),但这并非出于良好原因。这是因为不同的系统可能以不同的方式通过 Request 或 Response 对象等方式执行重定向。所以只要您能获取返回的 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_code、transaction_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 适配器。
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
仍然使用 AdapterA
、AdapterQ
等的功能(和因此接口)。现在想象一下,这种场景在 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
。