{"id":15627115,"url":"https://github.com/adelowo/gbowo","last_synced_at":"2025-04-28T18:34:13.538Z","repository":{"id":54894091,"uuid":"66685598","full_name":"adelowo/gbowo","owner":"adelowo","description":"A unified API for hippy nigerian payment processors (Currently supports Paystack and Amplifypay)","archived":false,"fork":false,"pushed_at":"2022-05-05T12:36:29.000Z","size":165,"stargazers_count":22,"open_issues_count":1,"forks_count":5,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-20T11:16:17.659Z","etag":null,"topics":["amplifypay","paystack","php7"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/adelowo.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.MD","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-08-26T23:39:50.000Z","updated_at":"2022-12-04T07:03:12.000Z","dependencies_parsed_at":"2022-08-14T06:00:40.978Z","dependency_job_id":null,"html_url":"https://github.com/adelowo/gbowo","commit_stats":null,"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adelowo%2Fgbowo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adelowo%2Fgbowo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adelowo%2Fgbowo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adelowo%2Fgbowo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/adelowo","download_url":"https://codeload.github.com/adelowo/gbowo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251366508,"owners_count":21578131,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["amplifypay","paystack","php7"],"created_at":"2024-10-03T10:15:22.410Z","updated_at":"2025-04-28T18:34:13.522Z","avatar_url":"https://github.com/adelowo.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Gbowo - Unified API for Hippy Nigerian Payment Gateways\n\n[![Latest Version on Packagist](https://img.shields.io/packagist/v/adelowo/gbowo.svg?style=flat-square)](https://packagist.org/packages/adelowo/gbowo)\n[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md)\n[![Build Status](https://img.shields.io/travis/adelowo/gbowo/master.svg?style=flat-square)](https://travis-ci.org/adelowo/gbowo)\n[![Scrutinizer Coverage](https://img.shields.io/scrutinizer/coverage/g/adelowo/gbowo.svg?style=flat-square)](https://scrutinizer-ci.com/g/adelowo/gbowo/?branch=master)\n[![Quality Score](https://img.shields.io/scrutinizer/g/adelowo/gbowo.svg?style=flat-square)](https://scrutinizer-ci.com/g/adelowo/gbowo)\n[![Total Downloads](https://img.shields.io/packagist/dt/adelowo/gbowo.svg?style=flat-square)](https://packagist.org/packages/adelowo/gbowo)\n\n- [Installation](#installation)\n- [Usage](#usage)\n    - [Adapters](#adapters)\n        - [Custom Adapters](#extend)\n- [Plugins](#plugins)\n- [Framework Integration/Bridges](#frameworks)\n    - [Laravel](#laravel)\n- [Example Application](#example)\n\n\u003ch2 id=\"installation\"\u003eInstallation\n\u003c/h2\u003e\n\nInstall Gbowo via one of the following methods :\n\n- [Composer](https://getcomposer.org) (Recommended) :\n\n```bash\n    composer require \"adelowo/gbowo\" : \"~1.0\"\n```\n\n\u003ch2 id=\"usage\"\u003e Usage \u003c/h2\u003e\n\n```php\n\nrequire_once 'vendor/autoload.php';\n\n$adapter = new \\Gbowo\\Adapter\\Paystack\\PaystackAdapter();\n\n$response = $adapter-\u003echarge();\n\n```\n\nFor the paystack and amplifypay adapters, the response received would be a string, which denotes an _authorization__url_. You are to make a redirect to the url to complete the transaction.\n\n\u003e Other adapters implementation may do something much different like redirect internally ( from the adapter) but this isn't done for good reasons. This is because different systems may have different ways of performing redirects via _Request_ or _Response_ objects, or whatever have they. So as long as you can get the returned url, you can use the _Adapter_ in your framework or whatever have you.\n\nA basic example would be something like\n\n```php\n\nheader(\"Location : {$response}\");\nexit();\n\n```\n\n\u003ch2 id=\"adapters\"\u003eAdapters\u003c/h2\u003e\n\n#### Quick usage\n```php\n$adapter = (new \\Gbowo\\GbowoFactory())-\u003ecreateAdapter(\"paystack\"); //or \"amplifypay\"\n\nreturn $adapter-\u003echarge();\n```\n\n#### Usage in depth\n\n_Gbowo_ ships with two adapters : one for [paystack](https://paystack.co) and the other for [Amplifypay](https://amplifypay.com).\n\nWhile both payment gateway offer similar features, there are a few subtle differences (obviously).\n\n_Gbowo_ requires some value to be present in the environment i.e `$_ENV`. For paystack, this is `$_ENV['PAYSTACK_SECRET_KEY']` while the amplifypay adapter requires two values : your merchant id and an api key. This should be present in the following format : `$_ENV['AMPLIFYPAY_MERCHANT_ID']` and `$_ENV['AMPLIFYPAY_API_KEY']`.\n\n\u003e You are strongly advised to keep your keys and/or tokens out of your code and instead load them into `$_ENV` by some other means. We don't enforce this but it is a best practice, [12 Factor App Guideline](https://12factor.net/config).\nA library that would help with this is `vlucas/phpdotenv`. All it needs is a `.env` file and you are golden. Remember not to commit the `.env` else it still isn't out of your \"code\".\nA sample `.env.example` has been provided in the `resources` directory. You can copy and rename that to `.env` in your root dir.\n\n```php\n\n$paystack = new \\Gbowo\\Adapter\\Paystack\\PaystackAdapter();\n\n$amplifyPay = new \\Gbowo\\Adapter\\Amplifypay\\AmplifypayAdapter()\n\n```\n\nA GuzzleHttp `Client` instance would be created automatically and values gotten from the `$_ENV` would be used to set the appropriate authorization headers where applicable.\n\nYou can prevent this \"auto-wiring\" by providing an instance of Guzzlehttp Client in the constructor.\n\n```php\n\n$client = new \\GuzzleHttp\\Client(['key' =\u003e \"value\"]);\n\n$amplifyPay = new \\Gbowo\\Adapter\\Amplifypay\\AmplifypayAdapter($client)\n\n```\n\nThe payment flow for both adapters is pretty much the same. User initiates first time / one time transaction and is redirected to a secure page where payment details are to be inputted. After this (a successful payment request), the gateway would issue a redirect to a url you have supplied them as a callback. In this url, you should fetch the details of a user (who is now a customer) such as an *authorization_code* , *transaction_reference* among others. This is for recurrent transactions and should be persisted to a storage mechanism.\n\nInitiating the transaction should be performed by calling the `charge` method on the adapter. And making a redirect where applicable to the response as described earlier.\n\nTo fetch the data from the url callback you have registered on your chosen gateway, you have to call the `getPaymentData` method on the adapter. It's response would contain some data about the customer.\n\n```php\n\n//paystack adapter\nvar_dump($adapter-\u003egetPaymentData($_GET['trxref'])) ; //you should clean this up.\n\n//amplifypay adapter\nvar_dump($adapter-\u003egetPaymentData($_GET['tran_response'])); // clean up\n\n```\n\n\u003e The `getPaymentData` method can also be called on some transaction reference you have stored in your db or some other form of storage mechanism.\n\n\u003e When you call the `getPaymentData` on the paystack adapter. The reference is verified by paystack before a response is sent\n\n### Adapters Methods.\n\n[Paystack](https://paystack.co) :\n\n* `charge(array $data = [])`\n* `getCustomer(int $id)`\n* `getAllCustomers()`\n* `chargeWithToken(array $userToken)` // a token plus email address (or custom stuff)\n* `getPaymentData(string $transRef)`\n* `fetchPlan($planIdentifier)`\n* `fetchAllPlans()`\n\n\u003e Checkout [gbowo-paystack](https://github.com/adelowo/gbowo-paystack) . It contains an additional set of [plugins](#plugins) for the paystack adapter.\n\n[Amplifypay](https://amplifypay.com) :\n\n* `charge(array $data = [])`\n* `unsubcribeCustomerFromPlan(array $data)`\n* `chargeWithToken(array $userToken)` //a token in amplifypay is a key pair of values.\n* `getPaymentData(string $transRef)`\n* `fetchPlan($planIdentifier)`\n* `fetchAllPlans()`\n\n\u003e The `charge` method parameter (`array $data = []`) should contain stuffs like amount, email, x, y, z). Those would be handed over to the payment gateway\n\n\u003ch2 id=\"extend\"\u003eCustom Adapters\u003c/h2\u003e\n\n\u003e Using laravel, please check out how to [add your custom adapter](#laravel)\n\n```php\n\n//let's assume it is an enterprisey app\n\n$interswitch = new class implements \\Gbowo\\Contract\\Adapter\\AdapterInterface\n{\n    protected $interswitch;\n\n    public function __construct()\n    {\n        $this-\u003einterswitch = new \\stdClass(new \\ArrayObject(new \\stdClass())); // It wasn't me\n    }\n\n    public function charge(array $data = [])\n    {\n        return \"charged by interswitch\";\n    }\n};\n\n$adapter = new \\Gbowo\\GbowoFactory([\"interswitch\" =\u003e $interswitch]); //add the interswith adapter as a custom one.\n\n$interswitchAdapter = $adapter-\u003ecreateAdapter(\"interswitch\");\n\n$interswitchAdapter-\u003echarge(['a' =\u003e 'b', 'c' =\u003e 'd']);\n```\n\n\u003ch2 id=\"plugins\"\u003eExtending Adapters via Plugins\u003c/h2\u003e\n\nDifferent gateways implement various features and there's no way we can support all of them without losing our sanity.\n\nSupporting all features would lead to a bloat (an interface, class bloat). Take for instance : create `InterfaceX` to support feature X for `AdapterE` while `AdapterE` still makes use of features (and therefore interfaces) for `AdapterA`,`AdapterQ` and so on. Now imagine this scenario plays out for 4 -5 adapters.\n\n Apart from the bloat, we cannot create a (visual) diagram of which interfaces are being used and it'd lead to a situation where we cannot remove a certain class or interface because we do not know who (what adapter) depends on them.\n\nTo prevent this, _Gbowo_ implements a plugin architecture that eases extension or \"adding new methods/behaviours\" to an existing adapter without inheritance or touching core code. To achieve this, there is a `Pluggable` trait that contains the logic and **MUST** be imported by an adapter implementation.\n\nA look at the [paystack adapter](src/Gbowo/Adapter/Paystack/PaystackAdapter.php) and [amplifypay](src/Gbowo/Adapter/Amplifypay/AmplifypayAdapter.php) would reveal that they do not have the methods described above in their public api. In fact they expose only 3 methods :\n* `__construct(Client $client = null)` // if it counts as one\n* `getHttpClient()`\n* `charge(array $data = [])` //This is gotten from the `AdapterInterface` implemented.\n\nBut a look at their `registerPlugins` method  - which is gotten from the __Pluggable__ trait - tells how the methods described in the `Adapters method` section above come about.\n\nA plugin is a plain PHP class that **MUST** implement the `PluginInterface`. This interface exposes two methods :\n\n* `getPluginAccessor() : string`\n* `setAdapter(Adapter $adapter)`\n* `handle(string $reference)` //Typehint as much as you want. 2,3 args ? Your call.\n\n\u003e I have written a [detailed post about this here](https://lanreadelowo.com/blog/2017/03/08/extending-gbowo-via-plugins/)\n\n```php\n\nnamespace Vendor\\AdapterName\\Plugin;\n\nuse Gbowo/Contract/Plugin/PluginInterface;\nuse Gbowo\\Exception\\TransactionVerficationFailedException;\n\nclass ApiPinger implements PluginInterface\n{\n\n    public function getPluginAccessor():string\n    {\n        return \"pingApi\"; //Oops.. Let's confirm if the api isn't dead before making any request. And it must be a string without parenthesis\n    }\n\n    /**\n     * 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.\n     */\n    public function setAdapter(AdapterInterface $adapter)\n    {\n        //useful for helpers like getting stuffs from \"accessors\" on the adapter instance like the already configured HttpClient object\n        $this-\u003eadapter = $adapter ;\n        return $this;\n    }\n\n    /**\n     * Ping the gateway Api\n     * @param  bool $shouldThrow. Should an exception be thrown if the api is down ?.\n     * @return bool true - if the api is up and running.\n                   false - if the api is down and $throw is set to false.\n     * @throws \\Exception if the api is down and $throw is set to false.\n     */\n    public function handle(bool $shouldThrow = false)\n    {\n        $response = $this-\u003eadapter-\u003egetHttpClient()-\u003eget(\"https://api.homepage.com\");\n\n        if ($response-\u003egetStatusCode() === 200 ) {\n            return true;\n        }\n\n        if ($shouldThrow) {\n            throw TransactionVerficationFailedException::createFromResponse($response);\n        }\n\n        return false;\n    }\n}\n\n```\n\n\u003e `createFromResponse` is a wrapper that allows client code inspect the response for why there was a failure (say an invalid HTTP status code).. You should call `getResponse` on the exception in other to inspect it. This is also true for official plugins provided by Gbowo.\n\nThe `getPluginAccessor` is of tremendous interest here since it determines what plugin the method call should be deferred to. This is done by the magic method `__call` in the [`Pluggable`](src/Gbowo/Traits/Pluggable.php) trait.\n\n\n```php\n$adapter-\u003eaddPlugin(new Vendor\\AdapterName\\Plugin\\ApiPinger(PaystackAdapter::API_LINK));\n//Usage like this\n\n$adapter-\u003epingApi(true);\n$adapter-\u003epingApi();\n\n```\nNot all plugins would make it to the core eventually and not even all plugins in the core would be \"added\" on instantiation. For instance, the `GetAllCustomers` plugin isn't added to the `PaystackAdapter` internally. To use the plugin, you'd have to add it yourself.\n\n```php\n\n$adapter-\u003eaddPlugin(new GetAllCustomers(PaystackAdapter::API_LINK))\n\n```\n\n\u003ch2 id=\"frameworks\"\u003e Framework Integration\u003c/h2\u003e\n\n\u003ch2 id=\"laravel\"\u003eLaravel \u003c/h2\u003e\n\nPrior to `v1.5.0`, a Laravel bridge was provided alongside this package but in light of best  interests, it [has been moved to it's own repository](https://github.com/adelowo/laravel-gbowo).\n\nIf upgrading from a previous version to `v1.5.0`, please note that you would have to run `composer require adelowo/laravel-gbowo`\n\n\u003e Please note that the decision to give the Laravel bridge a life of it's own doesn't mean BC break. The namespaces were preserved and would continue to work as is.\n\n\u003ch2 id=\"example\"\u003e \u003c/h2\u003e\n\n##### Sample App\n\n[Gbowo-app](https://github.com/adelowo/gbowo-app) - Built with SlimPHP.\n\n\n### Contributing\n\nAwesome, I'd love that. Fork, send PR. But hey, unit testing is one honking great idea. Let's have more of that.\n\n### Bug Reports, Issue tracking and Security Vulnerabilities\n\nPlease make use of the [issue tracker](https://github.com/adelowo/gbowo/issues) for bug reports, feature request and others except Security issues. If you do discover a vulnerability, please send a mail to `me@lanreadelowo.com`.\n\n### License\n[MIT](http://opensource.org/licenses/MIT)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadelowo%2Fgbowo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fadelowo%2Fgbowo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadelowo%2Fgbowo/lists"}