├── .gitignore ├── src ├── Support │ └── helpers.php ├── Exceptions │ ├── IsNullException.php │ └── PaymentVerificationFailedException.php ├── Facades │ └── Paystack.php ├── PaystackServiceProvider.php ├── TransRef.php └── Paystack.php ├── .phpunit.result.cache ├── CHANGELOG.md ├── .travis.yml ├── tests ├── HelpersTest.php └── PaystackTest.php ├── resources └── config │ └── paystack.php ├── phpunit.xml.dist ├── LICENSE.md ├── CONTRIBUTING.md ├── composer.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | vendor 3 | .DS_Store 4 | composer.lock 5 | .idea -------------------------------------------------------------------------------- /src/Support/helpers.php: -------------------------------------------------------------------------------- 1 | make('laravel-paystack'); 8 | } 9 | } -------------------------------------------------------------------------------- /.phpunit.result.cache: -------------------------------------------------------------------------------- 1 | {"version":1,"defects":[],"times":{"Unicodeveloper\\Paystack\\Test\\HelpersTest::it_returns_instance_of_paystack":0.213,"Unicodeveloper\\Paystack\\Test\\PaystackTest::testAllCustomersAreReturned":0.089,"Unicodeveloper\\Paystack\\Test\\PaystackTest::testAllTransactionsAreReturned":0.001,"Unicodeveloper\\Paystack\\Test\\PaystackTest::testAllPlansAreReturned":0.001}} -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All Notable changes to `laravel-paystack` will be documented in this file 4 | 5 | ## 2015-11-04 6 | - Initial release 7 | 8 | ## 2020-05-23 9 | - Support for Laravel 7 10 | - Support for splitting payments into subaccounts 11 | - Support for more than one currency. Now you can use USD! 12 | - Support for multiple quantities 13 | - Support for helpers -------------------------------------------------------------------------------- /src/Exceptions/IsNullException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Unicodeveloper\Paystack\Exceptions; 13 | 14 | use Exception; 15 | 16 | class IsNullException extends Exception 17 | { 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/Exceptions/PaymentVerificationFailedException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Unicodeveloper\Paystack\Exceptions; 13 | 14 | use Exception; 15 | 16 | class PaymentVerificationFailedException extends Exception 17 | { 18 | 19 | } 20 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 7.3 5 | 6 | install: travis_retry composer install --no-interaction --prefer-source 7 | 8 | script: 9 | - mkdir -p build/logs 10 | - vendor/bin/phpunit -c phpunit.xml.dist 11 | - vendor/bin/phpunit --coverage-text --coverage-clover=coverage.clover 12 | - vendor/bin/phpunit --coverage-clover build/logs/clover.xml 13 | 14 | after_script: 15 | - wget https://scrutinizer-ci.com/ocular.phar 16 | - php ocular.phar code-coverage:upload --format=php-clover coverage.clover 17 | - travis_retry php vendor/bin/coveralls -v 18 | 19 | notifications: 20 | slack: red-creek:5lI8ybvl6YTcCNPosh4TE13h -------------------------------------------------------------------------------- /src/Facades/Paystack.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Unicodeveloper\Paystack\Facades; 13 | 14 | use Illuminate\Support\Facades\Facade; 15 | 16 | class Paystack extends Facade 17 | { 18 | /** 19 | * Get the registered name of the component 20 | * @return string 21 | */ 22 | protected static function getFacadeAccessor() 23 | { 24 | return 'laravel-paystack'; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/HelpersTest.php: -------------------------------------------------------------------------------- 1 | paystack = m::mock('Unicodeveloper\Paystack\Paystack'); 15 | $this->mock = m::mock('GuzzleHttp\Client'); 16 | } 17 | 18 | public function tearDown(): void 19 | { 20 | m::close(); 21 | } 22 | 23 | /** 24 | * Tests that helper returns 25 | * 26 | * @test 27 | * @return void 28 | */ 29 | function it_returns_instance_of_paystack () { 30 | 31 | $this->assertInstanceOf("Unicodeveloper\Paystack\Paystack", $this->paystack); 32 | } 33 | } -------------------------------------------------------------------------------- /resources/config/paystack.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | return [ 13 | 14 | /** 15 | * Public Key From Paystack Dashboard 16 | * 17 | */ 18 | 'publicKey' => getenv('PAYSTACK_PUBLIC_KEY'), 19 | 20 | /** 21 | * Secret Key From Paystack Dashboard 22 | * 23 | */ 24 | 'secretKey' => getenv('PAYSTACK_SECRET_KEY'), 25 | 26 | /** 27 | * Paystack Payment URL 28 | * 29 | */ 30 | 'paymentUrl' => getenv('PAYSTACK_PAYMENT_URL'), 31 | 32 | /** 33 | * Optional email address of the merchant 34 | * 35 | */ 36 | 'merchantEmail' => getenv('MERCHANT_EMAIL'), 37 | 38 | ]; 39 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | tests 15 | 16 | 17 | 18 | 19 | src/ 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Prosper Otemuyiwa 4 | 5 | > Permission is hereby granted, free of charge, to any person obtaining a copy 6 | > of this software and associated documentation files (the "Software"), to deal 7 | > in the Software without restriction, including without limitation the rights 8 | > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | > copies of the Software, and to permit persons to whom the Software is 10 | > furnished to do so, subject to the following conditions: 11 | > 12 | > The above copyright notice and this permission notice shall be included in 13 | > all copies or substantial portions of the Software. 14 | > 15 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | > THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/PaystackServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Unicodeveloper\Paystack; 13 | 14 | use Illuminate\Support\ServiceProvider; 15 | 16 | class PaystackServiceProvider extends ServiceProvider 17 | { 18 | 19 | /* 20 | * Indicates if loading of the provider is deferred. 21 | * 22 | * @var bool 23 | */ 24 | protected $defer = false; 25 | 26 | /** 27 | * Publishes all the config file this package needs to function 28 | */ 29 | public function boot() 30 | { 31 | $config = realpath(__DIR__.'/../resources/config/paystack.php'); 32 | 33 | $this->publishes([ 34 | $config => config_path('paystack.php') 35 | ]); 36 | } 37 | 38 | /** 39 | * Register the application services. 40 | */ 41 | public function register() 42 | { 43 | $this->app->bind('laravel-paystack', function () { 44 | 45 | return new Paystack; 46 | 47 | }); 48 | } 49 | 50 | /** 51 | * Get the services provided by the provider 52 | * @return array 53 | */ 54 | public function provides() 55 | { 56 | return ['laravel-paystack']; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are **welcome** and will be fully **credited**. 4 | 5 | We accept contributions via Pull Requests on [Github](https://github.com/unicodeveloper/laravel-paystack). 6 | 7 | 8 | ## Pull Requests 9 | 10 | - **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](http://pear.php.net/package/PHP_CodeSniffer). 11 | 12 | - **Add tests!** - Your patch won't be accepted if it doesn't have tests. 13 | 14 | - **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. 15 | 16 | - **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option. 17 | 18 | - **Create feature branches** - Don't ask us to pull from your master branch. 19 | 20 | - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. 21 | 22 | - **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](http://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. 23 | 24 | 25 | ## Running Tests 26 | 27 | ``` bash 28 | $ composer test 29 | ``` 30 | 31 | 32 | **Happy coding**! 33 | -------------------------------------------------------------------------------- /tests/PaystackTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Unicodeveloper\Paystack\Test; 13 | 14 | use Mockery as m; 15 | use GuzzleHttp\Client; 16 | use PHPUnit\Framework\TestCase; 17 | use Unicodeveloper\Paystack\Paystack; 18 | use Illuminate\Support\Facades\Config; 19 | use Illuminate\Support\Facades\Facade as Facade; 20 | 21 | class PaystackTest extends TestCase 22 | { 23 | protected $paystack; 24 | 25 | public function setUp(): void 26 | { 27 | $this->paystack = m::mock('Unicodeveloper\Paystack\Paystack'); 28 | $this->mock = m::mock('GuzzleHttp\Client'); 29 | } 30 | 31 | public function tearDown(): void 32 | { 33 | m::close(); 34 | } 35 | 36 | public function testAllCustomersAreReturned() 37 | { 38 | $array = $this->paystack->shouldReceive('getAllCustomers')->andReturn(['prosper']); 39 | 40 | $this->assertEquals('array', gettype(array($array))); 41 | } 42 | 43 | public function testAllTransactionsAreReturned() 44 | { 45 | $array = $this->paystack->shouldReceive('getAllTransactions')->andReturn(['transactions']); 46 | 47 | $this->assertEquals('array', gettype(array($array))); 48 | } 49 | 50 | public function testAllPlansAreReturned() 51 | { 52 | $array = $this->paystack->shouldReceive('getAllPlans')->andReturn(['intermediate-plan']); 53 | 54 | $this->assertEquals('array', gettype(array($array))); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "unicodeveloper/laravel-paystack", 3 | "description": "A Laravel Package for Paystack", 4 | "keywords": [ 5 | "php", 6 | "github", 7 | "laravel", 8 | "Open Source", 9 | "payments", 10 | "subscription", 11 | "paystack", 12 | "paystack.co", 13 | "laravel 6", 14 | "laravel 7", 15 | "laravel 8" 16 | ], 17 | "license": "MIT", 18 | "authors": [ 19 | { 20 | "name": "unicodeveloper", 21 | "email": "prosperotemuyiwa@gmail.com" 22 | }, 23 | { 24 | "name": "iamfunsho", 25 | "email": "info@devfunsho.com" 26 | } 27 | ], 28 | "minimum-stability": "stable", 29 | "require": { 30 | "php": "^7.2|^8.0|^8.1", 31 | "illuminate/support": "~6|~7|~8|~9|^10.0|^11.0", 32 | "guzzlehttp/guzzle": "~6|~7|~8|~9" 33 | }, 34 | "require-dev": { 35 | "phpunit/phpunit": "^8.4|^9.0|^10.5", 36 | "scrutinizer/ocular": "~1.1", 37 | "php-coveralls/php-coveralls": "^2.0", 38 | "mockery/mockery": "^1.3" 39 | }, 40 | "autoload": { 41 | "files": [ 42 | "src/Support/helpers.php" 43 | ], 44 | "psr-4": { 45 | "Unicodeveloper\\Paystack\\": "src/" 46 | } 47 | }, 48 | "autoload-dev": { 49 | "psr-4": { 50 | "Unicodeveloper\\Paystack\\Test\\": "tests" 51 | } 52 | }, 53 | "scripts": { 54 | "test": "vendor/bin/phpunit" 55 | }, 56 | "extra": { 57 | "laravel": { 58 | "providers": [ 59 | "Unicodeveloper\\Paystack\\PaystackServiceProvider" 60 | ], 61 | "aliases": { 62 | "Paystack": "Unicodeveloper\\Paystack\\Facades\\Paystack" 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/TransRef.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * Source http://stackoverflow.com/a/13733588/179104 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Unicodeveloper\Paystack; 15 | 16 | class TransRef 17 | { 18 | /** 19 | * Get the pool to use based on the type of prefix hash 20 | * @param string $type 21 | * @return string 22 | */ 23 | private static function getPool($type = 'alnum') 24 | { 25 | switch ($type) { 26 | case 'alnum': 27 | $pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; 28 | break; 29 | case 'alpha': 30 | $pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; 31 | break; 32 | case 'hexdec': 33 | $pool = '0123456789abcdef'; 34 | break; 35 | case 'numeric': 36 | $pool = '0123456789'; 37 | break; 38 | case 'nozero': 39 | $pool = '123456789'; 40 | break; 41 | case 'distinct': 42 | $pool = '2345679ACDEFHJKLMNPRSTUVWXYZ'; 43 | break; 44 | default: 45 | $pool = (string) $type; 46 | break; 47 | } 48 | 49 | return $pool; 50 | } 51 | 52 | /** 53 | * Generate a random secure crypt figure 54 | * @param integer $min 55 | * @param integer $max 56 | * @return integer 57 | */ 58 | private static function secureCrypt($min, $max) 59 | { 60 | $range = $max - $min; 61 | 62 | if ($range < 0) { 63 | return $min; // not so random... 64 | } 65 | 66 | $log = log($range, 2); 67 | $bytes = (int) ($log / 8) + 1; // length in bytes 68 | $bits = (int) $log + 1; // length in bits 69 | $filter = (int) (1 << $bits) - 1; // set all lower bits to 1 70 | do { 71 | $rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes))); 72 | $rnd = $rnd & $filter; // discard irrelevant bits 73 | } while ($rnd >= $range); 74 | 75 | return $min + $rnd; 76 | } 77 | 78 | /** 79 | * Finally, generate a hashed token 80 | * @param integer $length 81 | * @return string 82 | */ 83 | public static function getHashedToken($length = 25) 84 | { 85 | $token = ""; 86 | $max = strlen(static::getPool()); 87 | for ($i = 0; $i < $length; $i++) { 88 | $token .= static::getPool()[static::secureCrypt(0, $max)]; 89 | } 90 | 91 | return $token; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # laravel-paystack 2 | 3 | [![Latest Stable Version](https://poser.pugx.org/unicodeveloper/laravel-paystack/v/stable.svg)](https://packagist.org/packages/unicodeveloper/laravel-paystack) 4 | [![License](https://poser.pugx.org/unicodeveloper/laravel-paystack/license.svg)](LICENSE.md) 5 | [![Build Status](https://img.shields.io/travis/unicodeveloper/laravel-paystack.svg)](https://travis-ci.org/unicodeveloper/laravel-paystack) 6 | [![Quality Score](https://img.shields.io/scrutinizer/g/unicodeveloper/laravel-paystack.svg?style=flat-square)](https://scrutinizer-ci.com/g/unicodeveloper/laravel-paystack) 7 | [![Total Downloads](https://img.shields.io/packagist/dt/unicodeveloper/laravel-paystack.svg?style=flat-square)](https://packagist.org/packages/unicodeveloper/laravel-paystack) 8 | 9 | > A Laravel Package for working with Paystack seamlessly 10 | 11 | ## Installation 12 | 13 | [PHP](https://php.net) 5.4+ or [HHVM](http://hhvm.com) 3.3+, and [Composer](https://getcomposer.org) are required. 14 | 15 | To get the latest version of Laravel Paystack, simply require it 16 | 17 | ```bash 18 | composer require unicodeveloper/laravel-paystack 19 | ``` 20 | 21 | Or add the following line to the require block of your `composer.json` file. 22 | 23 | ``` 24 | "unicodeveloper/laravel-paystack": "1.0.*" 25 | ``` 26 | 27 | You'll then need to run `composer install` or `composer update` to download it and have the autoloader updated. 28 | 29 | 30 | 31 | Once Laravel Paystack is installed, you need to register the service provider. Open up `config/app.php` and add the following to the `providers` key. 32 | 33 | ```php 34 | 'providers' => [ 35 | ... 36 | Unicodeveloper\Paystack\PaystackServiceProvider::class, 37 | ... 38 | ] 39 | ``` 40 | 41 | > If you use **Laravel >= 5.5** you can skip this step and go to [**`configuration`**](https://github.com/unicodeveloper/laravel-paystack#configuration) 42 | 43 | * `Unicodeveloper\Paystack\PaystackServiceProvider::class` 44 | 45 | Also, register the Facade like so: 46 | 47 | ```php 48 | 'aliases' => [ 49 | ... 50 | 'Paystack' => Unicodeveloper\Paystack\Facades\Paystack::class, 51 | ... 52 | ] 53 | ``` 54 | 55 | ## Configuration 56 | 57 | You can publish the configuration file using this command: 58 | 59 | ```bash 60 | php artisan vendor:publish --provider="Unicodeveloper\Paystack\PaystackServiceProvider" 61 | ``` 62 | 63 | A configuration-file named `paystack.php` with some sensible defaults will be placed in your `config` directory: 64 | 65 | ```php 66 | getenv('PAYSTACK_PUBLIC_KEY'), 75 | 76 | /** 77 | * Secret Key From Paystack Dashboard 78 | * 79 | */ 80 | 'secretKey' => getenv('PAYSTACK_SECRET_KEY'), 81 | 82 | /** 83 | * Paystack Payment URL 84 | * 85 | */ 86 | 'paymentUrl' => getenv('PAYSTACK_PAYMENT_URL'), 87 | 88 | /** 89 | * Optional email address of the merchant 90 | * 91 | */ 92 | 'merchantEmail' => getenv('MERCHANT_EMAIL'), 93 | 94 | ]; 95 | ``` 96 | 97 | 98 | ## General payment flow 99 | 100 | Though there are multiple ways to pay an order, most payment gateways expect you to follow the following flow in your checkout process: 101 | 102 | ### 1. The customer is redirected to the payment provider 103 | After the customer has gone through the checkout process and is ready to pay, the customer must be redirected to the site of the payment provider. 104 | 105 | The redirection is accomplished by submitting a form with some hidden fields. The form must send a POST request to the site of the payment provider. The hidden fields minimally specify the amount that must be paid, the order id and a hash. 106 | 107 | The hash is calculated using the hidden form fields and a non-public secret. The hash used by the payment provider to verify if the request is valid. 108 | 109 | 110 | ### 2. The customer pays on the site of the payment provider 111 | The customer arrives on the site of the payment provider and gets to choose a payment method. All steps necessary to pay the order are taken care of by the payment provider. 112 | 113 | ### 3. The customer gets redirected back to your site 114 | After having paid the order the customer is redirected back. In the redirection request to the shop-site some values are returned. The values are usually the order id, a payment result and a hash. 115 | 116 | The hash is calculated out of some of the fields returned and a secret non-public value. This hash is used to verify if the request is valid and comes from the payment provider. It is paramount that this hash is thoroughly checked. 117 | 118 | 119 | ## Usage 120 | 121 | Open your .env file and add your public key, secret key, merchant email and payment url like so: 122 | 123 | ```php 124 | PAYSTACK_PUBLIC_KEY=xxxxxxxxxxxxx 125 | PAYSTACK_SECRET_KEY=xxxxxxxxxxxxx 126 | PAYSTACK_PAYMENT_URL=https://api.paystack.co 127 | MERCHANT_EMAIL=unicodeveloper@gmail.com 128 | ``` 129 | *If you are using a hosting service like heroku, ensure to add the above details to your configuration variables.* 130 | 131 | Set up routes and controller methods like so: 132 | 133 | Note: Make sure you have `/payment/callback` registered in Paystack Dashboard [https://dashboard.paystack.co/#/settings/developer](https://dashboard.paystack.co/#/settings/developer) like so: 134 | 135 | ![payment-callback](https://cloud.githubusercontent.com/assets/2946769/12746754/9bd383fc-c9a0-11e5-94f1-64433fc6a965.png) 136 | 137 | ```php 138 | // Laravel 5.1.17 and above 139 | Route::post('/pay', 'PaymentController@redirectToGateway')->name('pay'); 140 | ``` 141 | 142 | OR 143 | 144 | ```php 145 | Route::post('/pay', [ 146 | 'uses' => 'PaymentController@redirectToGateway', 147 | 'as' => 'pay' 148 | ]); 149 | ``` 150 | OR 151 | 152 | ```php 153 | // Laravel 8 & 9 154 | Route::post('/pay', [App\Http\Controllers\PaymentController::class, 'redirectToGateway'])->name('pay'); 155 | ``` 156 | 157 | 158 | ```php 159 | Route::get('/payment/callback', 'PaymentController@handleGatewayCallback'); 160 | ``` 161 | 162 | OR 163 | 164 | ```php 165 | // Laravel 5.0 166 | Route::get('payment/callback', [ 167 | 'uses' => 'PaymentController@handleGatewayCallback' 168 | ]); 169 | ``` 170 | 171 | OR 172 | 173 | ```php 174 | // Laravel 8 & 9 175 | Route::get('/payment/callback', [App\Http\Controllers\PaymentController::class, 'handleGatewayCallback']); 176 | ``` 177 | 178 | ```php 179 | redirectNow(); 201 | }catch(\Exception $e) { 202 | return Redirect::back()->withMessage(['msg'=>'The paystack token has expired. Please refresh the page and try again.', 'type'=>'error']); 203 | } 204 | } 205 | 206 | /** 207 | * Obtain Paystack payment information 208 | * @return void 209 | */ 210 | public function handleGatewayCallback() 211 | { 212 | $paymentDetails = Paystack::getPaymentData(); 213 | 214 | dd($paymentDetails); 215 | // Now you have the payment details, 216 | // you can store the authorization_code in your db to allow for recurrent subscriptions 217 | // you can then redirect or do whatever you want 218 | } 219 | } 220 | ``` 221 | 222 | ```php 223 | /** 224 | * In the case where you need to pass the data from your 225 | * controller instead of a form 226 | * Make sure to send: 227 | * required: email, amount, reference, orderID(probably) 228 | * optionally: currency, description, metadata 229 | * e.g: 230 | * 231 | */ 232 | $data = array( 233 | "amount" => 700 * 100, 234 | "reference" => '4g4g5485g8545jg8gj', 235 | "email" => 'user@mail.com', 236 | "currency" => "NGN", 237 | "orderID" => 23456, 238 | ); 239 | 240 | return Paystack::getAuthorizationUrl($data)->redirectNow(); 241 | 242 | ``` 243 | 244 | Let me explain the fluent methods this package provides a bit here. 245 | ```php 246 | /** 247 | * This fluent method does all the dirty work of sending a POST request with the form data 248 | * to Paystack Api, then it gets the authorization Url and redirects the user to Paystack 249 | * Payment Page. We've abstracted all of it, so you don't have to worry about that. 250 | * Just eat your cookies while coding! 251 | */ 252 | Paystack::getAuthorizationUrl()->redirectNow(); 253 | 254 | /** 255 | * Alternatively, use the helper. 256 | */ 257 | paystack()->getAuthorizationUrl()->redirectNow(); 258 | 259 | /** 260 | * This fluent method does all the dirty work of verifying that the just concluded transaction was actually valid, 261 | * It verifies the transaction reference with Paystack Api and then grabs the data returned from Paystack. 262 | * In that data, we have a lot of good stuff, especially the `authorization_code` that you can save in your db 263 | * to allow for easy recurrent subscription. 264 | */ 265 | Paystack::getPaymentData(); 266 | 267 | /** 268 | * Alternatively, use the helper. 269 | */ 270 | paystack()->getPaymentData(); 271 | 272 | /** 273 | * This method gets all the customers that have performed transactions on your platform with Paystack 274 | * @returns array 275 | */ 276 | Paystack::getAllCustomers(); 277 | 278 | /** 279 | * Alternatively, use the helper. 280 | */ 281 | paystack()->getAllCustomers(); 282 | 283 | 284 | /** 285 | * This method gets all the plans that you have registered on Paystack 286 | * @returns array 287 | */ 288 | Paystack::getAllPlans(); 289 | 290 | /** 291 | * Alternatively, use the helper. 292 | */ 293 | paystack()->getAllPlans(); 294 | 295 | 296 | /** 297 | * This method gets all the transactions that have occurred 298 | * @returns array 299 | */ 300 | Paystack::getAllTransactions(); 301 | 302 | /** 303 | * Alternatively, use the helper. 304 | */ 305 | paystack()->getAllTransactions(); 306 | 307 | /** 308 | * This method generates a unique super secure cryptographic hash token to use as transaction reference 309 | * @returns string 310 | */ 311 | Paystack::genTranxRef(); 312 | 313 | /** 314 | * Alternatively, use the helper. 315 | */ 316 | paystack()->genTranxRef(); 317 | 318 | 319 | /** 320 | * This method creates a subaccount to be used for split payments 321 | * @return array 322 | */ 323 | Paystack::createSubAccount(); 324 | 325 | /** 326 | * Alternatively, use the helper. 327 | */ 328 | paystack()->createSubAccount(); 329 | 330 | 331 | /** 332 | * This method fetches the details of a subaccount 333 | * @return array 334 | */ 335 | Paystack::fetchSubAccount(); 336 | 337 | /** 338 | * Alternatively, use the helper. 339 | */ 340 | paystack()->fetchSubAccount(); 341 | 342 | 343 | /** 344 | * This method lists the subaccounts associated with your paystack account 345 | * @return array 346 | */ 347 | Paystack::listSubAccounts(); 348 | 349 | /** 350 | * Alternatively, use the helper. 351 | */ 352 | paystack()->listSubAccounts(); 353 | 354 | 355 | /** 356 | * This method Updates a subaccount to be used for split payments 357 | * @return array 358 | */ 359 | Paystack::updateSubAccount(); 360 | 361 | /** 362 | * Alternatively, use the helper. 363 | */ 364 | paystack()->updateSubAccount(); 365 | ``` 366 | 367 | A sample form will look like so: 368 | 369 | ```php 370 | "percentage", 375 | "currency" => "KES", 376 | "subaccounts" => [ 377 | [ "subaccount" => "ACCT_li4p6kte2dolodo", "share" => 10 ], 378 | [ "subaccount" => "ACCT_li4p6kte2dolodo", "share" => 30 ], 379 | ], 380 | "bearer_type" => "all", 381 | "main_account_share" => 70 382 | ]; 383 | ?> 384 | ``` 385 | 386 | ```html 387 |
388 |
389 |
390 |

391 |

392 | Lagos Eyo Print Tee Shirt 393 | ₦ 2,950 394 |
395 |

396 | {{-- required --}} 397 | 398 | {{-- required in kobo --}} 399 | 400 | 401 | {{-- For other necessary things you want to add to your payload. it is optional though --}} 402 | {{-- required --}} 403 | 404 | {{-- to support transaction split. more details https://paystack.com/docs/payments/multi-split-payments/#using-transaction-splits-with-payments --}} 405 | {{-- to support dynamic transaction split. More details https://paystack.com/docs/payments/multi-split-payments/#dynamic-splits --}} 406 | {{ csrf_field() }} {{-- works only when using laravel 5.1, 5.2 --}} 407 | 408 | {{-- employ this in place of csrf_field only in laravel 5.0 --}} 409 | 410 |

411 | 414 |

415 |
416 |
417 |
418 | ``` 419 | 420 | When clicking the submit button the customer gets redirected to the Paystack site. 421 | 422 | So now we've redirected the customer to Paystack. The customer did some actions there (hopefully he or she paid the order) and now gets redirected back to our shop site. 423 | 424 | Paystack will redirect the customer to the url of the route that is specified in the Callback URL of the Web Hooks section on Paystack dashboard. 425 | 426 | We must validate if the redirect to our site is a valid request (we don't want imposters to wrongfully place non-paid order). 427 | 428 | In the controller that handles the request coming from the payment provider, we have 429 | 430 | `Paystack::getPaymentData()` - This function calls the verification methods and ensure it is a valid transaction else it throws an exception. 431 | 432 | You can test with these details 433 | 434 | ```bash 435 | Card Number: 4123450131001381 436 | Expiry Date: any date in the future 437 | CVV: 883 438 | ``` 439 | 440 | ## Todo 441 | 442 | * Charge Returning Customers 443 | * Add Comprehensive Tests 444 | * Implement Transaction Dashboard to see all of the transactions in your laravel app 445 | 446 | ## Contributing 447 | 448 | Please feel free to fork this package and contribute by submitting a pull request to enhance the functionalities. 449 | 450 | ## How can I thank you? 451 | 452 | Why not star the github repo? I'd love the attention! Why not share the link for this repository on Twitter or HackerNews? Spread the word! 453 | 454 | Don't forget to [follow me on twitter](https://twitter.com/unicodeveloper)! 455 | 456 | Thanks! 457 | Prosper Otemuyiwa. 458 | 459 | ## License 460 | 461 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 462 | -------------------------------------------------------------------------------- /src/Paystack.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Unicodeveloper\Paystack; 13 | 14 | use GuzzleHttp\Client; 15 | use Illuminate\Support\Facades\Config; 16 | use Unicodeveloper\Paystack\Exceptions\IsNullException; 17 | use Unicodeveloper\Paystack\Exceptions\PaymentVerificationFailedException; 18 | 19 | class Paystack 20 | { 21 | /** 22 | * Transaction Verification Successful 23 | */ 24 | const VS = 'Verification successful'; 25 | 26 | /** 27 | * Invalid Transaction reference 28 | */ 29 | const ITF = "Invalid transaction reference"; 30 | 31 | /** 32 | * Issue Secret Key from your Paystack Dashboard 33 | * @var string 34 | */ 35 | protected $secretKey; 36 | 37 | /** 38 | * Instance of Client 39 | * @var Client 40 | */ 41 | protected $client; 42 | 43 | /** 44 | * Response from requests made to Paystack 45 | * @var mixed 46 | */ 47 | protected $response; 48 | 49 | /** 50 | * Paystack API base Url 51 | * @var string 52 | */ 53 | protected $baseUrl; 54 | 55 | /** 56 | * Authorization Url - Paystack payment page 57 | * @var string 58 | */ 59 | protected $authorizationUrl; 60 | 61 | public function __construct() 62 | { 63 | $this->setKey(); 64 | $this->setBaseUrl(); 65 | $this->setRequestOptions(); 66 | } 67 | 68 | /** 69 | * Get Base Url from Paystack config file 70 | */ 71 | public function setBaseUrl() 72 | { 73 | $this->baseUrl = Config::get('paystack.paymentUrl'); 74 | } 75 | 76 | /** 77 | * Get secret key from Paystack config file 78 | */ 79 | public function setKey() 80 | { 81 | $this->secretKey = Config::get('paystack.secretKey'); 82 | } 83 | 84 | /** 85 | * Set options for making the Client request 86 | */ 87 | private function setRequestOptions() 88 | { 89 | $authBearer = 'Bearer ' . $this->secretKey; 90 | 91 | $this->client = new Client( 92 | [ 93 | 'base_uri' => $this->baseUrl, 94 | 'headers' => [ 95 | 'Authorization' => $authBearer, 96 | 'Content-Type' => 'application/json', 97 | 'Accept' => 'application/json' 98 | ] 99 | ] 100 | ); 101 | } 102 | 103 | 104 | /** 105 | 106 | * Initiate a payment request to Paystack 107 | * Included the option to pass the payload to this method for situations 108 | * when the payload is built on the fly (not passed to the controller from a view) 109 | * @return Paystack 110 | */ 111 | 112 | public function makePaymentRequest($data = null) 113 | { 114 | if ($data == null) { 115 | 116 | $quantity = intval(request()->quantity ?? 1); 117 | 118 | $data = array_filter([ 119 | "amount" => intval(request()->amount) * $quantity, 120 | "reference" => request()->reference, 121 | "email" => request()->email, 122 | "channels" => request()->channels, 123 | "plan" => request()->plan, 124 | "first_name" => request()->first_name, 125 | "last_name" => request()->last_name, 126 | "callback_url" => request()->callback_url, 127 | "currency" => (request()->currency != "" ? request()->currency : "NGN"), 128 | 129 | /* 130 | Paystack allows for transactions to be split into a subaccount - 131 | The following lines trap the subaccount ID - as well as the ammount to charge the subaccount (if overriden in the form) 132 | both values need to be entered within hidden input fields 133 | */ 134 | "subaccount" => request()->subaccount, 135 | "transaction_charge" => request()->transaction_charge, 136 | 137 | /** 138 | * Paystack allows for transaction to be split into multi accounts(subaccounts) 139 | * The following lines trap the split ID handling the split 140 | * More details here: https://paystack.com/docs/payments/multi-split-payments/#using-transaction-splits-with-payments 141 | */ 142 | "split_code" => request()->split_code, 143 | 144 | /** 145 | * Paystack allows transaction to be split into multi account(subaccounts) on the fly without predefined split 146 | * form need an input field: 147 | * array must be set up as: 148 | * $split = [ 149 | * "type" => "percentage", 150 | * "currency" => "KES", 151 | * "subaccounts" => [ 152 | * { "subaccount" => "ACCT_li4p6kte2dolodo", "share" => 10 }, 153 | * { "subaccount" => "ACCT_li4p6kte2dolodo", "share" => 30 }, 154 | * ], 155 | * "bearer_type" => "all", 156 | * "main_account_share" => 70, 157 | * ] 158 | * More details here: https://paystack.com/docs/payments/multi-split-payments/#dynamic-splits 159 | */ 160 | "split" => request()->split, 161 | /* 162 | * to allow use of metadata on Paystack dashboard and a means to return additional data back to redirect url 163 | * form need an input field: 164 | * array must be set up as: 165 | * $array = [ 'custom_fields' => [ 166 | * ['display_name' => "Cart Id", "variable_name" => "cart_id", "value" => "2"], 167 | * ['display_name' => "Sex", "variable_name" => "sex", "value" => "female"], 168 | * . 169 | * . 170 | * . 171 | * ] 172 | * ] 173 | */ 174 | 'metadata' => request()->metadata 175 | ]); 176 | } 177 | 178 | $this->setHttpResponse('/transaction/initialize', 'POST', $data); 179 | 180 | return $this; 181 | } 182 | 183 | 184 | /** 185 | * @param string $relativeUrl 186 | * @param string $method 187 | * @param array $body 188 | * @return Paystack 189 | * @throws IsNullException 190 | */ 191 | private function setHttpResponse($relativeUrl, $method, $body = []) 192 | { 193 | if (is_null($method)) { 194 | throw new IsNullException("Empty method not allowed"); 195 | } 196 | 197 | $this->response = $this->client->{strtolower($method)}( 198 | $this->baseUrl . $relativeUrl, 199 | ["body" => json_encode($body)] 200 | ); 201 | 202 | return $this; 203 | } 204 | 205 | /** 206 | * Get the authorization url from the callback response 207 | * @return Paystack 208 | */ 209 | public function getAuthorizationUrl($data = null) 210 | { 211 | $this->makePaymentRequest($data); 212 | 213 | $this->url = $this->getResponse()['data']['authorization_url']; 214 | 215 | return $this; 216 | } 217 | 218 | /** 219 | * Get the authorization callback response 220 | * In situations where Laravel serves as an backend for a detached UI, the api cannot redirect 221 | * and might need to take different actions based on the success or not of the transaction 222 | * @return array 223 | */ 224 | public function getAuthorizationResponse($data) 225 | { 226 | $this->makePaymentRequest($data); 227 | 228 | $this->url = $this->getResponse()['data']['authorization_url']; 229 | 230 | return $this->getResponse(); 231 | } 232 | 233 | /** 234 | * Hit Paystack Gateway to Verify that the transaction is valid 235 | */ 236 | private function verifyTransactionAtGateway($transaction_id = null) 237 | { 238 | $transactionRef = $transaction_id ?? request()->query('trxref'); 239 | 240 | $relativeUrl = "/transaction/verify/{$transactionRef}"; 241 | 242 | $this->response = $this->client->get($this->baseUrl . $relativeUrl, []); 243 | } 244 | 245 | /** 246 | * True or false condition whether the transaction is verified 247 | * @return boolean 248 | */ 249 | public function isTransactionVerificationValid($transaction_id = null) 250 | { 251 | $this->verifyTransactionAtGateway($transaction_id); 252 | 253 | $result = $this->getResponse()['message']; 254 | 255 | switch ($result) { 256 | case self::VS: 257 | $validate = true; 258 | break; 259 | case self::ITF: 260 | $validate = false; 261 | break; 262 | default: 263 | $validate = false; 264 | break; 265 | } 266 | 267 | return $validate; 268 | } 269 | 270 | /** 271 | * Get Payment details if the transaction was verified successfully 272 | * @return json 273 | * @throws PaymentVerificationFailedException 274 | */ 275 | public function getPaymentData() 276 | { 277 | if ($this->isTransactionVerificationValid()) { 278 | return $this->getResponse(); 279 | } else { 280 | throw new PaymentVerificationFailedException("Invalid Transaction Reference"); 281 | } 282 | } 283 | 284 | /** 285 | * Fluent method to redirect to Paystack Payment Page 286 | */ 287 | public function redirectNow() 288 | { 289 | return redirect($this->url); 290 | } 291 | 292 | /** 293 | * Get Access code from transaction callback respose 294 | * @return string 295 | */ 296 | public function getAccessCode() 297 | { 298 | return $this->getResponse()['data']['access_code']; 299 | } 300 | 301 | /** 302 | * Generate a Unique Transaction Reference 303 | * @return string 304 | */ 305 | public function genTranxRef() 306 | { 307 | return TransRef::getHashedToken(); 308 | } 309 | 310 | /** 311 | * Get all the customers that have made transactions on your platform 312 | * @return array 313 | */ 314 | public function getAllCustomers() 315 | { 316 | $this->setRequestOptions(); 317 | 318 | return $this->setHttpResponse("/customer", 'GET', [])->getData(); 319 | } 320 | 321 | /** 322 | * Get all the plans that you have on Paystack 323 | * @return array 324 | */ 325 | public function getAllPlans() 326 | { 327 | $this->setRequestOptions(); 328 | 329 | return $this->setHttpResponse("/plan", 'GET', [])->getData(); 330 | } 331 | 332 | /** 333 | * Get all the transactions that have happened overtime 334 | * @return array 335 | */ 336 | public function getAllTransactions() 337 | { 338 | $this->setRequestOptions(); 339 | 340 | return $this->setHttpResponse("/transaction", 'GET', [])->getData(); 341 | } 342 | 343 | /** 344 | * Get the whole response from a get operation 345 | * @return array 346 | */ 347 | private function getResponse() 348 | { 349 | return json_decode($this->response->getBody(), true); 350 | } 351 | 352 | /** 353 | * Get the data response from a get operation 354 | * @return array 355 | */ 356 | private function getData() 357 | { 358 | return $this->getResponse()['data']; 359 | } 360 | 361 | /** 362 | * Create a plan 363 | */ 364 | public function createPlan() 365 | { 366 | $data = [ 367 | "name" => request()->name, 368 | "description" => request()->desc, 369 | "amount" => intval(request()->amount), 370 | "interval" => request()->interval, 371 | "send_invoices" => request()->send_invoices, 372 | "send_sms" => request()->send_sms, 373 | "currency" => request()->currency, 374 | ]; 375 | 376 | $this->setRequestOptions(); 377 | 378 | return $this->setHttpResponse("/plan", 'POST', $data)->getResponse(); 379 | } 380 | 381 | /** 382 | * Fetch any plan based on its plan id or code 383 | * @param $plan_code 384 | * @return array 385 | */ 386 | public function fetchPlan($plan_code) 387 | { 388 | $this->setRequestOptions(); 389 | return $this->setHttpResponse('/plan/' . $plan_code, 'GET', [])->getResponse(); 390 | } 391 | 392 | /** 393 | * Update any plan's details based on its id or code 394 | * @param $plan_code 395 | * @return array 396 | */ 397 | public function updatePlan($plan_code) 398 | { 399 | $data = [ 400 | "name" => request()->name, 401 | "description" => request()->desc, 402 | "amount" => intval(request()->amount), 403 | "interval" => request()->interval, 404 | "send_invoices" => request()->send_invoices, 405 | "send_sms" => request()->send_sms, 406 | "currency" => request()->currency, 407 | ]; 408 | 409 | $this->setRequestOptions(); 410 | return $this->setHttpResponse('/plan/' . $plan_code, 'PUT', $data)->getResponse(); 411 | } 412 | 413 | /** 414 | * Create a customer 415 | */ 416 | public function createCustomer($data = null) 417 | { 418 | if ($data == null) { 419 | 420 | $data = [ 421 | "email" => request()->email, 422 | "first_name" => request()->fname, 423 | "last_name" => request()->lname, 424 | "phone" => request()->phone, 425 | "metadata" => request()->additional_info /* key => value pairs array */ 426 | 427 | ]; 428 | } 429 | 430 | $this->setRequestOptions(); 431 | return $this->setHttpResponse('/customer', 'POST', $data)->getResponse(); 432 | } 433 | 434 | /** 435 | * Fetch a customer based on id or code 436 | * @param $customer_id 437 | * @return array 438 | */ 439 | public function fetchCustomer($customer_id) 440 | { 441 | $this->setRequestOptions(); 442 | return $this->setHttpResponse('/customer/' . $customer_id, 'GET', [])->getResponse(); 443 | } 444 | 445 | /** 446 | * Update a customer's details based on their id or code 447 | * @param $customer_id 448 | * @return array 449 | */ 450 | public function updateCustomer($customer_id) 451 | { 452 | $data = [ 453 | "email" => request()->email, 454 | "first_name" => request()->fname, 455 | "last_name" => request()->lname, 456 | "phone" => request()->phone, 457 | "metadata" => request()->additional_info /* key => value pairs array */ 458 | 459 | ]; 460 | 461 | $this->setRequestOptions(); 462 | return $this->setHttpResponse('/customer/' . $customer_id, 'PUT', $data)->getResponse(); 463 | } 464 | 465 | /** 466 | * Export transactions in .CSV 467 | * @return array 468 | */ 469 | public function exportTransactions() 470 | { 471 | $data = [ 472 | "from" => request()->from, 473 | "to" => request()->to, 474 | 'settled' => request()->settled 475 | ]; 476 | 477 | $this->setRequestOptions(); 478 | return $this->setHttpResponse('/transaction/export', 'GET', $data)->getResponse(); 479 | } 480 | 481 | /** 482 | * Create a subscription to a plan from a customer. 483 | */ 484 | public function createSubscription() 485 | { 486 | $data = [ 487 | "customer" => request()->customer, //Customer email or code 488 | "plan" => request()->plan, 489 | "authorization" => request()->authorization_code 490 | ]; 491 | 492 | $this->setRequestOptions(); 493 | return $this->setHttpResponse('/subscription', 'POST', $data)->getResponse(); 494 | } 495 | 496 | /** 497 | * Get all the subscriptions made on Paystack. 498 | * 499 | * @return array 500 | */ 501 | public function getAllSubscriptions() 502 | { 503 | $this->setRequestOptions(); 504 | 505 | return $this->setHttpResponse("/subscription", 'GET', [])->getData(); 506 | } 507 | 508 | /** 509 | * Get customer subscriptions 510 | * 511 | * @param integer $customer_id 512 | * @return array 513 | */ 514 | public function getCustomerSubscriptions($customer_id) 515 | { 516 | $this->setRequestOptions(); 517 | 518 | return $this->setHttpResponse('/subscription?customer=' . $customer_id, 'GET', [])->getData(); 519 | } 520 | 521 | /** 522 | * Get plan subscriptions 523 | * 524 | * @param integer $plan_id 525 | * @return array 526 | */ 527 | public function getPlanSubscriptions($plan_id) 528 | { 529 | $this->setRequestOptions(); 530 | 531 | return $this->setHttpResponse('/subscription?plan=' . $plan_id, 'GET', [])->getData(); 532 | } 533 | 534 | /** 535 | * Enable a subscription using the subscription code and token 536 | * @return array 537 | */ 538 | public function enableSubscription() 539 | { 540 | $data = [ 541 | "code" => request()->code, 542 | "token" => request()->token, 543 | ]; 544 | 545 | $this->setRequestOptions(); 546 | return $this->setHttpResponse('/subscription/enable', 'POST', $data)->getResponse(); 547 | } 548 | 549 | /** 550 | * Disable a subscription using the subscription code and token 551 | * @return array 552 | */ 553 | public function disableSubscription() 554 | { 555 | $data = [ 556 | "code" => request()->code, 557 | "token" => request()->token, 558 | ]; 559 | 560 | $this->setRequestOptions(); 561 | return $this->setHttpResponse('/subscription/disable', 'POST', $data)->getResponse(); 562 | } 563 | 564 | /** 565 | * Fetch details about a certain subscription 566 | * @param mixed $subscription_id 567 | * @return array 568 | */ 569 | public function fetchSubscription($subscription_id) 570 | { 571 | $this->setRequestOptions(); 572 | return $this->setHttpResponse('/subscription/' . $subscription_id, 'GET', [])->getResponse(); 573 | } 574 | 575 | /** 576 | * Create pages you can share with users using the returned slug 577 | */ 578 | public function createPage() 579 | { 580 | $data = [ 581 | "name" => request()->name, 582 | "description" => request()->description, 583 | "amount" => request()->amount 584 | ]; 585 | 586 | $this->setRequestOptions(); 587 | return $this->setHttpResponse('/page', 'POST', $data)->getResponse(); 588 | } 589 | 590 | /** 591 | * Fetches all the pages the merchant has 592 | * @return array 593 | */ 594 | public function getAllPages() 595 | { 596 | $this->setRequestOptions(); 597 | return $this->setHttpResponse('/page', 'GET', [])->getResponse(); 598 | } 599 | 600 | /** 601 | * Fetch details about a certain page using its id or slug 602 | * @param mixed $page_id 603 | * @return array 604 | */ 605 | public function fetchPage($page_id) 606 | { 607 | $this->setRequestOptions(); 608 | return $this->setHttpResponse('/page/' . $page_id, 'GET', [])->getResponse(); 609 | } 610 | 611 | /** 612 | * Update the details about a particular page 613 | * @param $page_id 614 | * @return array 615 | */ 616 | public function updatePage($page_id) 617 | { 618 | $data = [ 619 | "name" => request()->name, 620 | "description" => request()->description, 621 | "amount" => request()->amount 622 | ]; 623 | 624 | $this->setRequestOptions(); 625 | return $this->setHttpResponse('/page/' . $page_id, 'PUT', $data)->getResponse(); 626 | } 627 | 628 | /** 629 | * Creates a subaccount to be used for split payments . Required params are business_name , settlement_bank , account_number , percentage_charge 630 | * 631 | * @return array 632 | */ 633 | 634 | public function createSubAccount() 635 | { 636 | $data = [ 637 | "business_name" => request()->business_name, 638 | "settlement_bank" => request()->settlement_bank, 639 | "account_number" => request()->account_number, 640 | "percentage_charge" => request()->percentage_charge, 641 | "primary_contact_email" => request()->primary_contact_email, 642 | "primary_contact_name" => request()->primary_contact_name, 643 | "primary_contact_phone" => request()->primary_contact_phone, 644 | "metadata" => request()->metadata, 645 | 'settlement_schedule' => request()->settlement_schedule 646 | ]; 647 | 648 | $this->setRequestOptions(); 649 | return $this->setHttpResponse('/subaccount', 'POST', array_filter($data))->getResponse(); 650 | } 651 | 652 | /** 653 | * Fetches details of a subaccount 654 | * @param subaccount code 655 | * @return array 656 | */ 657 | public function fetchSubAccount($subaccount_code) 658 | { 659 | 660 | $this->setRequestOptions(); 661 | return $this->setHttpResponse("/subaccount/{$subaccount_code}", "GET", [])->getResponse(); 662 | } 663 | 664 | /** 665 | * Lists all the subaccounts associated with the account 666 | * @param $per_page - Specifies how many records to retrieve per page , $page - SPecifies exactly what page to retrieve 667 | * @return array 668 | */ 669 | public function listSubAccounts($per_page, $page) 670 | { 671 | 672 | $this->setRequestOptions(); 673 | return $this->setHttpResponse("/subaccount/?perPage=" . (int) $per_page . "&page=" . (int) $page, "GET")->getResponse(); 674 | } 675 | 676 | 677 | /** 678 | * Updates a subaccount to be used for split payments . Required params are business_name , settlement_bank , account_number , percentage_charge 679 | * @param subaccount code 680 | * @return array 681 | */ 682 | 683 | public function updateSubAccount($subaccount_code) 684 | { 685 | $data = [ 686 | "business_name" => request()->business_name, 687 | "settlement_bank" => request()->settlement_bank, 688 | "account_number" => request()->account_number, 689 | "percentage_charge" => request()->percentage_charge, 690 | "description" => request()->description, 691 | "primary_contact_email" => request()->primary_contact_email, 692 | "primary_contact_name" => request()->primary_contact_name, 693 | "primary_contact_phone" => request()->primary_contact_phone, 694 | "metadata" => request()->metadata, 695 | 'settlement_schedule' => request()->settlement_schedule 696 | ]; 697 | 698 | $this->setRequestOptions(); 699 | return $this->setHttpResponse("/subaccount/{$subaccount_code}", "PUT", array_filter($data))->getResponse(); 700 | } 701 | 702 | 703 | /** 704 | * Get a list of all supported banks and their properties 705 | * @param $country - The country from which to obtain the list of supported banks, $per_page - Specifies how many records to retrieve per page , 706 | * $use_cursor - Flag to enable cursor pagination on the endpoint 707 | * @return array 708 | */ 709 | public function getBanks(?string $country, int $per_page = 50, bool $use_cursor = false) 710 | { 711 | if (!$country) 712 | $country = request()->country ?? 'nigeria'; 713 | 714 | $this->setRequestOptions(); 715 | return $this->setHttpResponse("/bank/?country=" . $country . "&use_cursor=" . $use_cursor . "&perPage=" . (int) $per_page, "GET")->getResponse(); 716 | } 717 | 718 | /** 719 | * Confirm an account belongs to the right customer 720 | * @param $account_number - Account Number, $bank_code - You can get the list of bank codes by calling the List Banks endpoint 721 | * @return array 722 | */ 723 | public function confirmAccount(string $account_number, string $bank_code) 724 | { 725 | 726 | $this->setRequestOptions(); 727 | return $this->setHttpResponse("/bank/resolve/?account_number=" . $account_number . "&bank_code=" . $bank_code, "GET")->getResponse(); 728 | } 729 | } 730 | --------------------------------------------------------------------------------