diff --git a/.travis.yml b/.travis.yml index 2185801..c9be028 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,7 @@ language: php php: - - 5.5 - - 5.6 - - 7.0 + - 7.1 - hhvm matrix: diff --git a/README.md b/README.md index cbc84f1..ae59ebe 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ ## Installation -[PHP](https://php.net) 5.4+ or [HHVM](http://hhvm.com) 3.3+, and [Composer](https://getcomposer.org) are required. +[PHP](https://php.net) 7.1+ or [HHVM](http://hhvm.com) 3.3+, and [Composer](https://getcomposer.org) are required. To get the latest version of Laravel Paystack, simply require it @@ -57,32 +57,61 @@ A configuration-file named `paystack.php` with some sensible defaults will be pl ```php + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +return [ /** * Public Key From Paystack Dashboard * */ - 'publicKey' => getenv('PAYSTACK_PUBLIC_KEY'), + 'publicKey' => $publicKey = env('PAYSTACK_PUBLIC_KEY', 'publicKey'), /** * Secret Key From Paystack Dashboard * */ - 'secretKey' => getenv('PAYSTACK_SECRET_KEY'), + 'secretKey' => $secretKey = env('PAYSTACK_SECRET_KEY', 'secretKey'), /** * Paystack Payment URL * */ - 'paymentUrl' => getenv('PAYSTACK_PAYMENT_URL'), + 'paymentUrl' => $paymentUrl = env('PAYSTACK_PAYMENT_URL'), /** * Optional email address of the merchant * */ - 'merchantEmail' => getenv('MERCHANT_EMAIL'), + 'merchantEmail' => $merchantEmail = env('MERCHANT_EMAIL'), + + 'default' => 'test', + /** + * Here you can specify different Paystack connection. + */ + 'connections' => [ + 'test' => [ + 'publicKey' => $publicKey, + 'secretKey' => $secretKey, + 'paymentUrl' => $paymentUrl, + 'cache' => false, + ], + 'live' => [ + 'publicKey' => $publicKey, + 'secretKey' => $secretKey, + 'paymentUrl' => $paymentUrl, + 'cache' => false, + ], + ], ]; ``` @@ -193,6 +222,12 @@ class PaymentController extends Controller Let me explain the fluent methods this package provides a bit here. ```php +/** + * To use the Multi connection Feature you need to prefix your call like this otherwise + * the default connection will be used as specified in the paystack.php config file. + */ +Paystack::connection('live')->getAuthorizationUrl()->redirectNow(); + /** * This fluent method does all the dirty work of sending a POST request with the form data * to Paystack Api, then it gets the authorization Url and redirects the user to Paystack diff --git a/composer.json b/composer.json index 9b4eb51..3e18663 100644 --- a/composer.json +++ b/composer.json @@ -13,18 +13,23 @@ "email": "info@devfunsho.com" } ], - "minimum-stability": "stable", + "minimum-stability": "dev", + "prefer-stable": true, "require": { - "php": "^5.4.0|^7.0", - "illuminate/support": "5.*", - "guzzlehttp/guzzle": "5.*|6.*" + "php": "^7.1.3", + "illuminate/support": "^5.7", + "xeviant/paystack": "dev-master", + "graham-campbell/cache-plugin": "^1.1", + "graham-campbell/manager": "^4.2" }, "require-dev": { - "phpunit/phpunit" : "4.*", - "scrutinizer/ocular": "~1.1", - "satooshi/php-coveralls": "^0.7.0", - "mockery/mockery": ">=0.7.2" - }, + "mockery/mockery": "^1.2", + "phpunit/phpunit": "^7.0", + "orchestra/testbench": "3.7.*", + "graham-campbell/testbench": "^5.2", + "php-http/guzzle6-adapter": "^2.0", + "madewithlove/illuminate-psr-cache-bridge": "^1.0" + }, "autoload": { "psr-4": { "Unicodeveloper\\Paystack\\": "src/" diff --git a/phpunit.xml.dist b/phpunit.xml.dist index e4d77b6..9866227 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -22,7 +22,7 @@ - + diff --git a/resources/config/paystack.php b/resources/config/paystack.php index e6d0d29..c094f30 100644 --- a/resources/config/paystack.php +++ b/resources/config/paystack.php @@ -1,5 +1,7 @@ getenv('PAYSTACK_PUBLIC_KEY'), + 'publicKey' => $publicKey = env('PAYSTACK_PUBLIC_KEY', 'publicKey'), /** * Secret Key From Paystack Dashboard * */ - 'secretKey' => getenv('PAYSTACK_SECRET_KEY'), + 'secretKey' => $secretKey = env('PAYSTACK_SECRET_KEY', 'secretKey'), /** * Paystack Payment URL * */ - 'paymentUrl' => getenv('PAYSTACK_PAYMENT_URL'), + 'paymentUrl' => $paymentUrl = env('PAYSTACK_PAYMENT_URL'), /** * Optional email address of the merchant * */ - 'merchantEmail' => getenv('MERCHANT_EMAIL'), + 'merchantEmail' => $merchantEmail = env('MERCHANT_EMAIL'), + + 'default' => 'test', + /** + * Here you can specify different Paystack connection. + */ + 'connections' => [ + 'test' => [ + 'publicKey' => $publicKey, + 'secretKey' => $secretKey, + 'paymentUrl' => $paymentUrl, + 'cache' => false, + ], + 'live' => [ + 'publicKey' => $publicKey, + 'secretKey' => $secretKey, + 'paymentUrl' => $paymentUrl, + 'cache' => false, + ], + ], ]; diff --git a/src/Endpoint.php b/src/Endpoint.php new file mode 100644 index 0000000..6222a95 --- /dev/null +++ b/src/Endpoint.php @@ -0,0 +1,35 @@ +dispatch($event->getName(), $payload); + } +} diff --git a/src/Facades/Paystack.php b/src/Facades/Paystack.php index 8805f92..a3b4acf 100644 --- a/src/Facades/Paystack.php +++ b/src/Facades/Paystack.php @@ -1,5 +1,7 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Unicodeveloper\Paystack\Facades; + +use Illuminate\Support\Facades\Facade; + +class PaystackV1 extends Facade +{ + /** + * Get the registered name of the component + * @return string + */ + protected static function getFacadeAccessor() + { + return 'laravel-paystack'; + } +} diff --git a/src/Helpers/HelpsRequest.php b/src/Helpers/HelpsRequest.php new file mode 100644 index 0000000..8e7d8d2 --- /dev/null +++ b/src/Helpers/HelpsRequest.php @@ -0,0 +1,12 @@ + $data) { + request()->request->add([$key => $data]); + } + } +} diff --git a/src/Http/ClientBuilder.php b/src/Http/ClientBuilder.php new file mode 100644 index 0000000..9539df2 --- /dev/null +++ b/src/Http/ClientBuilder.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Unicodeveloper\Paystack\Http; + + +use GrahamCampbell\CachePlugin\CachePlugin; +use Http\Client\Common\Plugin\Cache\Generator\CacheKeyGenerator; +use Psr\Cache\CacheItemPoolInterface; +use Xeviant\Paystack\HttpClient\Builder; + +class ClientBuilder extends Builder +{ + /** + * Adds Cache Plugin to builder + * + * @param CacheItemPoolInterface $cacheItemPool + * @param array $config + * @throws \ReflectionException + */ + public function addCache(CacheItemPoolInterface $cacheItemPool, array $config = []) + { + $this->setCachePlugin($cacheItemPool, $config['generator'] ?? null, $config['lifetime'] ?? null); + + $this->setPropertyValue('httpClientModified', true); + } + + /** + * Add a cache plugin to cache responses locally. + * + * @param CacheItemPoolInterface $cacheItemPool + * @param CacheKeyGenerator|null $generator + * @param int|null $lifetime + * @throws \ReflectionException + */ + protected function setCachePlugin(CacheItemPoolInterface $cacheItemPool, CacheKeyGenerator $generator = null, int $lifetime = null): void + { + $stream = $this->getPropertyValue('streamFactory'); + + $this->setPropertyValue('cachePlugin', new CachePlugin($cacheItemPool, $stream, $generator, $lifetime)); + } + + /** + * Retrieves the value of a builder property + * + * @param string $name + * @return mixed + * @throws \ReflectionException + */ + protected function getPropertyValue(string $name) + { + return static::getProperty($name)->getValue($this); + } + + /** + * Sets the value of a builder property + * + * @param string $name + * @param $value + * @throws \ReflectionException + */ + protected function setPropertyValue(string $name, $value) + { + return static::getProperty($name)->setValue($this, $value); + } + + /** + * Gets the builder reflection property for the given name + * + * @param string $name + * @return \ReflectionProperty + * @throws \ReflectionException + */ + protected static function getProperty(string $name) + { + $prop = (new \ReflectionClass(Builder::class))->getProperty($name); + + $prop->setAccessible(true); + + return $prop; + } +} \ No newline at end of file diff --git a/src/Paystack.php b/src/Paystack.php index d3b4a7c..4aeafc8 100644 --- a/src/Paystack.php +++ b/src/Paystack.php @@ -1,5 +1,7 @@ setKey(); - $this->setBaseUrl(); - $this->setRequestOptions(); - } - /** - * Get Base Url from Paystack config file + * @var \Xeviant\Paystack\Client */ - public function setBaseUrl() - { - $this->baseUrl = Config::get('paystack.paymentUrl'); - } + private $paystack; /** - * Get secret key from Paystack config file + * Authorization URL + * + * @var string */ - public function setKey() - { - $this->secretKey = Config::get('paystack.secretKey'); - } + private $url; /** - * Set options for making the Client request + * Paystack constructor. */ - private function setRequestOptions() + public function __construct() { - $authBearer = 'Bearer '. $this->secretKey; - - $this->client = new Client( - [ - 'base_uri' => $this->baseUrl, - 'headers' => [ - 'Authorization' => $authBearer, - 'Content-Type' => 'application/json', - 'Accept' => 'application/json' - ] - ] - ); + $this->paystack = app()->make('paystack.connection'); } - - /** - + + /** * Initiate a payment request to Paystack - * Included the option to pass the payload to this method for situations + * Included the option to pass the payload to this method for situations * when the payload is built on the fly (not passed to the controller from a view) + * @param null $data * @return Paystack */ - - public function makePaymentRequest( $data = null) + public function makePaymentRequest($data = null) { if ( $data == null ) { $data = [ @@ -130,7 +88,7 @@ public function makePaymentRequest( $data = null) * . * . * ] - * + * * ] */ 'metadata' => request()->metadata @@ -140,29 +98,7 @@ public function makePaymentRequest( $data = null) array_filter($data); } - $this->setHttpResponse('/transaction/initialize', 'POST', $data); - - return $this; - } - - - /** - * @param string $relativeUrl - * @param string $method - * @param array $body - * @return Paystack - * @throws IsNullException - */ - private function setHttpResponse($relativeUrl, $method, $body = []) - { - if (is_null($method)) { - throw new IsNullException("Empty method not allowed"); - } - - $this->response = $this->client->{strtolower($method)}( - $this->baseUrl . $relativeUrl, - ["body" => json_encode($body)] - ); + $this->response = $this->paystack->transactions()->initialize($data); return $this; } @@ -179,11 +115,12 @@ public function getAuthorizationUrl() return $this; } - - /** + + /** * Get the authorization callback response - * In situations where Laravel serves as an backend for a detached UI, the api cannot redirect + * In situations where Laravel serves as an backend for a detached UI, the api cannot redirect * and might need to take different actions based on the success or not of the transaction + * @param $data * @return array */ public function getAuthorizationResponse($data) @@ -202,9 +139,7 @@ private function verifyTransactionAtGateway() { $transactionRef = request()->query('trxref'); - $relativeUrl = "/transaction/verify/{$transactionRef}"; - - $this->response = $this->client->get($this->baseUrl . $relativeUrl, []); + $this->response = $this->paystack->transactions()->verify($transactionRef); } /** @@ -218,10 +153,10 @@ public function isTransactionVerificationValid() $result = $this->getResponse()['message']; switch ($result) { - case self::VS: + case self::VERIFICATION_SUCCESSFUL: $validate = true; break; - case self::ITF: + case self::INVALID_TRANSACTION_REFERENCE: $validate = false; break; default: @@ -234,7 +169,7 @@ public function isTransactionVerificationValid() /** * Get Payment details if the transaction was verified successfully - * @return json + * @return array * @throws PaymentVerificationFailedException */ public function getPaymentData() @@ -255,7 +190,7 @@ public function redirectNow() } /** - * Get Access code from transaction callback respose + * Get Access code from transaction callback response * @return string */ public function getAccessCode() @@ -278,9 +213,7 @@ public function genTranxRef() */ public function getAllCustomers() { - $this->setRequestOptions(); - - return $this->setHttpResponse("/customer", 'GET', [])->getData(); + return $this->paystack->cutsomers()->list(); } /** @@ -289,9 +222,7 @@ public function getAllCustomers() */ public function getAllPlans() { - $this->setRequestOptions(); - - return $this->setHttpResponse("/plan", 'GET', [])->getData(); + return $this->paystack->plans()->list(); } /** @@ -300,9 +231,7 @@ public function getAllPlans() */ public function getAllTransactions() { - $this->setRequestOptions(); - - return $this->setHttpResponse("/transaction", 'GET', [])->getData(); + return $this->paystack->transactions()->list(); } /** @@ -311,16 +240,7 @@ public function getAllTransactions() */ private function getResponse() { - return json_decode($this->response->getBody(), true); - } - - /** - * Get the data response from a get operation - * @return array - */ - private function getData() - { - return $this->getResponse()['data']; + return $this->response; } /** @@ -338,29 +258,25 @@ public function createPlan() "currency" => request()->currency, ]; - $this->setRequestOptions(); - - $this->setHttpResponse("/plan", 'POST', $data); - + return $this->paystack->plans()->create($data); } /** * Fetch any plan based on its plan id or code - * @param $plan_code + * @param $planCode * @return array */ - public function fetchPlan($plan_code) + public function fetchPlan($planCode) { - $this->setRequestOptions(); - return $this->setHttpResponse('/plan/' . $plan_code, 'GET', [])->getResponse(); + return $this->paystack->plans()->fetch($planCode); } /** * Update any plan's details based on its id or code - * @param $plan_code + * @param $planCode * @return array */ - public function updatePlan($plan_code) + public function updatePlan($planCode) { $data = [ "name" => request()->name, @@ -372,8 +288,7 @@ public function updatePlan($plan_code) "currency" => request()->currency, ]; - $this->setRequestOptions(); - return $this->setHttpResponse('/plan/' . $plan_code, 'PUT', $data)->getResponse(); + return $this->paystack->plans()->update($planCode, $data); } /** @@ -390,27 +305,25 @@ public function createCustomer() ]; - $this->setRequestOptions(); - return $this->setHttpResponse('/customer', 'POST', $data)->getResponse(); + return $this->paystack->customers()->create($data); } /** * Fetch a customer based on id or code - * @param $customer_id + * @param $customerId * @return array */ - public function fetchCustomer($customer_id) + public function fetchCustomer($customerId) { - $this->setRequestOptions(); - return $this->setHttpResponse('/customer/'. $customer_id, 'GET', [])->getResponse(); + return $this->paystack->customers()->fetch($customerId); } /** * Update a customer's details based on their id or code - * @param $customer_id + * @param $customerId * @return array */ - public function updateCustomer($customer_id) + public function updateCustomer($customerId) { $data = [ "email" => request()->email, @@ -421,8 +334,7 @@ public function updateCustomer($customer_id) ]; - $this->setRequestOptions(); - return $this->setHttpResponse('/customer/'. $customer_id, 'PUT', $data)->getResponse(); + return $this->paystack->customers()->update($customerId, $data); } /** @@ -437,8 +349,7 @@ public function exportTransactions() 'settled' => request()->settled ]; - $this->setRequestOptions(); - return $this->setHttpResponse('/transaction/export', 'GET', $data)->getResponse(); + return $this->paystack->transactions()->export($data); } /** @@ -452,8 +363,7 @@ public function createSubscription() "authorization" => request()->authorization_code ]; - $this->setRequestOptions(); - $this->setHttpResponse('/subscription', 'POST', $data); + return $this->paystack->subscriptions()->create($data); } /** @@ -463,35 +373,29 @@ public function createSubscription() */ public function getAllSubscriptions() { - $this->setRequestOptions(); - - return $this->setHttpResponse("/subscription", 'GET', [])->getData(); + return $this->paystack->subscriptions()->list(); } /** * Get customer subscriptions * - * @param integer $customer_id + * @param integer $customerId * @return array */ - public function getCustomerSubscriptions($customer_id) + public function getCustomerSubscriptions($customerId) { - $this->setRequestOptions(); - - return $this->setHttpResponse('/subscription?customer=' . $customer_id, 'GET', [])->getData(); + return $this->paystack->subscriptions()->list(['customer' => $customerId]); } /** * Get plan subscriptions * - * @param integer $plan_id + * @param integer $planId * @return array */ - public function getPlanSubscriptions($plan_id) + public function getPlanSubscriptions($planId) { - $this->setRequestOptions(); - - return $this->setHttpResponse('/subscription?plan=' . $plan_id, 'GET', [])->getData(); + return $this->paystack->subscriptions()->list(['plan' => $planId]); } /** @@ -505,8 +409,7 @@ public function enableSubscription() "token" => request()->token, ]; - $this->setRequestOptions(); - return $this->setHttpResponse('/subscription/enable', 'POST', $data)->getResponse(); + return $this->paystack->subscrptions()->enable($data); } /** @@ -520,19 +423,17 @@ public function disableSubscription() "token" => request()->token, ]; - $this->setRequestOptions(); - return $this->setHttpResponse('/subscription/disable', 'POST', $data)->getResponse(); + return $this->paystack->subscriptions()->disabled($data); } /** * Fetch details about a certain subscription - * @param mixed $subscription_id + * @param mixed $subscriptionId * @return array */ - public function fetchSubscription($subscription_id) + public function fetchSubscription($subscriptionId) { - $this->setRequestOptions(); - return $this->setHttpResponse('/subscription/'.$subscription_id, 'GET', [])->getResponse(); + return $this->paystack->subscriptions()->fetch($subscriptionId); } /** @@ -546,8 +447,7 @@ public function createPage() "amount" => request()->amount ]; - $this->setRequestOptions(); - $this->setHttpResponse('/page', 'POST', $data); + return $this->paystack->pages()->create($data); } /** @@ -556,27 +456,25 @@ public function createPage() */ public function getAllPages() { - $this->setRequestOptions(); - return $this->setHttpResponse('/page', 'GET', [])->getResponse(); + return $this->paystack->pages()->list(); } /** * Fetch details about a certain page using its id or slug - * @param mixed $page_id + * @param mixed $pageId * @return array */ - public function fetchPage($page_id) + public function fetchPage($pageId) { - $this->setRequestOptions(); - return $this->setHttpResponse('/page/'.$page_id, 'GET', [])->getResponse(); + return $this->paystack->pages()->fetch($pageId); } /** * Update the details about a particular page - * @param $page_id + * @param $pageId * @return array */ - public function updatePage($page_id) + public function updatePage($pageId) { $data = [ "name" => request()->name, @@ -584,19 +482,19 @@ public function updatePage($page_id) "amount" => request()->amount ]; - $this->setRequestOptions(); - return $this->setHttpResponse('/page/'.$page_id, 'PUT', $data)->getResponse(); + return $this->paystack->pages()->update($pageId, $data); } - /** + /** * Creates a subaccount to be used for split payments . Required params are business_name , settlement_bank , account_number , percentage_charge - * + * * @return array */ - - public function createSubAccount(){ + + public function createSubAccount() + { $data = [ - "business_name" => request()->business_name, + "business_name" => request()->business_name, "settlement_bank" => request()->settlement_bank, "account_number" => request()->account_number, "percentage_charge" => request()->percentage_charge, @@ -607,45 +505,41 @@ public function createSubAccount(){ 'settlement_schedule' => request()->settlement_schedule ]; - $this->setRequestOptions(); - return $this->setHttpResponse('/subaccount', 'POST', array_filter($data))->getResponse(); - + return $this->paystack->subAccount()->create($data); } - /** + /** * Fetches details of a subaccount * @param subaccount code * @return array */ - public function fetchSubAccount($subaccount_code){ - - $this->setRequestOptions(); - return $this->setHttpResponse("/subaccount/{$subaccount_code}","GET",[])->getResponse(); - + public function fetchSubAccount($subAccountCode) + { + return $this->paystack->subAccount()->fetch($subAccountCode); } - /** + /** * Lists all the subaccounts associated with the account - * @param $per_page - Specifies how many records to retrieve per page , $page - SPecifies exactly what page to retrieve + * @param $perPage - Specifies how many records to retrieve per page , $page - SPecifies exactly what page to retrieve + * @param $page * @return array */ - public function listSubAccounts($per_page,$page){ - - $this->setRequestOptions(); - return $this->setHttpResponse("/subaccount/?perPage=".(int) $per_page."&page=".(int) $page,"GET")->getResponse(); - + public function listSubAccounts($perPage = null, $page = null) + { + return $this->paystack->subAccount()->list(['perPage' => $perPage, 'page' => $page]); } /** - * Updates a subaccount to be used for split payments . Required params are business_name , settlement_bank , account_number , percentage_charge - * @param subaccount code + * Updates a sub-account to be used for split payments . Required params are business_name , settlement_bank , account_number , percentage_charge + * @param sub-account code * @return array */ - - public function updateSubAccount($subaccount_code){ + + public function updateSubAccount($subAccountCode) + { $data = [ - "business_name" => request()->business_name, + "business_name" => request()->business_name, "settlement_bank" => request()->settlement_bank, "account_number" => request()->account_number, "percentage_charge" => request()->percentage_charge, @@ -657,8 +551,6 @@ public function updateSubAccount($subaccount_code){ 'settlement_schedule' => request()->settlement_schedule ]; - $this->setRequestOptions(); - return $this->setHttpResponse("/subaccount/{$subaccount_code}", "PUT", array_filter($data))->getResponse(); - + return $this->paystack->subAccount()->update($subAccountCode, $data); } -} +} \ No newline at end of file diff --git a/src/PaystackFactory.php b/src/PaystackFactory.php new file mode 100644 index 0000000..79c4eb1 --- /dev/null +++ b/src/PaystackFactory.php @@ -0,0 +1,116 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Unicodeveloper\Paystack; + + +use Closure; +use Illuminate\Contracts\Cache\Factory; +use Madewithlove\IlluminatePsrCacheBridge\Laravel\CacheItemPool; +use Unicodeveloper\Paystack\Event\EventHandler; +use Unicodeveloper\Paystack\Http\ClientBuilder; +use Xeviant\Paystack\App\PaystackApplication; +use Xeviant\Paystack\Client; +use Xeviant\Paystack\Config; +use Xeviant\Paystack\Contract\Config as PaystackConfigContract; +use Xeviant\Paystack\Exception\InvalidArgumentException; +use Xeviant\Paystack\HttpClient\Builder; + +class PaystackFactory +{ + /** + * Laravel Cache Instance + * + * @var Factory + */ + private $cache; + + /** + * PaystackFactory constructor. + * + * @param Factory|null $cache + */ + public function __construct(Factory $cache = null) + { + $this->cache = $cache; + } + + /** + * Creates A Paystack Client Object + * + * @param array $config + * @return Client + * @throws \ReflectionException + * @throws \Illuminate\Contracts\Container\BindingResolutionException + */ + public function make(array $config) + { + if ($this->secretKeyDoesNotExist($config)) { + throw new InvalidArgumentException('You cannot use the Paystack Factory without a SECRET key, go into "config/paystack.php" to set one.'); + } + + $compatibleConfig = $this->createCompatibleConfiguration($config); + + $app = new PaystackApplication; + + $app->instance(Builder::class, $this->getBuilder($config)); + $app->instance(PaystackConfigContract::class, $compatibleConfig); + + $client = new Client($app); + + // We register a Global Event listener + $client->getEvent()->listen('*', Closure::fromCallable([new EventHandler, 'handle'])); + + return $client; + } + + /** + * Check to see if Secret key doesn't exists + * + * @param array $config + * @return bool + */ + protected function secretKeyDoesNotExist(array $config) + { + return !array_key_exists('secretKey', $config) || (isset($config['secretKey']) && empty($config['secretKey'])); + } + + /** + * Creates a Compatible Paystack Client Configuration from a configuration array + * + * @param array $config + * @return Config + */ + public function createCompatibleConfiguration(array $config) + { + return new Config(null, $config['publicKey'] ?: null, $config['secretKey'] ?: null, 'v1'); + } + + /** + * Prepares and retrieves the Paystack client builder + * + * @param $config + * @return ClientBuilder + * @throws \ReflectionException + */ + protected function getBuilder($config) + { + $builder = new ClientBuilder(); + + if ($this->cache && class_exists(CacheItemPool::class) && $cache = array_get($config, 'cache')) { + $builder->addCache(new CacheItemPool($this->cache->store( $cache === true ? null : $cache))); + } + + return $builder; + } +} diff --git a/src/PaystackManager.php b/src/PaystackManager.php new file mode 100644 index 0000000..35aea53 --- /dev/null +++ b/src/PaystackManager.php @@ -0,0 +1,114 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Unicodeveloper\Paystack; + +/** + * @method \Xeviant\Paystack\Api\Customers customers() + * @method \Xeviant\Paystack\Api\Balance balance() + * @method \Xeviant\Paystack\Api\Bank bank() + * @method \Xeviant\Paystack\Api\BulkCharges bulkCharges() + * @method \Xeviant\Paystack\Api\Bvn bvn() + * @method \Xeviant\Paystack\Api\Charge charge() + * @method \Xeviant\Paystack\Api\Integration integration() + * @method \Xeviant\Paystack\Api\Invoices invoices() + * @method \Xeviant\Paystack\Api\Pages pages() + * @method \Xeviant\Paystack\Api\Plans plans() + * @method \Xeviant\Paystack\Api\Refund refund() + * @method \Xeviant\Paystack\Api\Settlements settlements() + * @method \Xeviant\Paystack\Api\SubAccount subAccount() + * @method \Xeviant\Paystack\Api\Subscriptions subscriptions() + * @method \Xeviant\Paystack\Api\Transactions transactions() + * @method \Xeviant\Paystack\Api\TransferRecipients transferRecipients() + * @method \Xeviant\Paystack\Api\Transfers transfers() + */ + + +use GrahamCampbell\Manager\AbstractManager; +use Illuminate\Config\Repository; + +class PaystackManager extends AbstractManager +{ + /** + * @var PaystackFactory + */ + private $factory; + + /** + * PaystackManager constructor. + * + * @param Repository $repository + * @param PaystackFactory $factory + */ + public function __construct(Repository $repository, PaystackFactory $factory) + { + parent::__construct($repository); + $this->factory = $factory; + } + + /** + * Create the connection instance. + * + * @param array $config + * + * @return \Xeviant\Paystack\Client + */ + protected function createConnection(array $config) + { + return $this->factory->make($config); + } + + /** + * Get the configuration name. + * + * @return string + */ + protected function getConfigName() + { + return 'paystack'; + } + + /** + * Gets the instance of the Paystack Factory + * + * @return PaystackFactory + */ + public function getFactory() + { + return $this->factory; + } + + /** + * {@inheritdoc} + */ + public function __call(string $method, array $parameters) + { + $legacyObject = $this->getLegacyObject(); + + if (method_exists($legacyObject, $method)) { + return $legacyObject->{$method}(...$parameters); + } + + return parent::__call($method, $parameters); + } + + /** + * Gets the Legacy Paystack Object from v1 of this package + * + * @return Paystack + */ + protected function getLegacyObject() + { + return new Paystack; + } +} diff --git a/src/PaystackServiceProvider.php b/src/PaystackServiceProvider.php index ec2cc54..4d5b910 100644 --- a/src/PaystackServiceProvider.php +++ b/src/PaystackServiceProvider.php @@ -1,5 +1,7 @@ setupConfig(); + } + + /** + * Sets up Paystack configuration file + */ + protected function setupConfig() + { + $config = realpath($raw = __DIR__.'/../resources/config/paystack.php') ?: $raw; + + if ($this->app instanceof LaravelApp && $this->app->runningInConsole()) { + $this->publishes([ + $config => config_path('paystack.php') + ]); + } elseif ($this->app instanceof LumenApp) { + $this->app->configure('paystack'); + } - $this->publishes([ - $config => config_path('paystack.php') - ]); + $this->mergeConfigFrom($config, 'paystack'); } /** @@ -41,10 +61,67 @@ public function boot() public function register() { $this->app->bind('laravel-paystack', function () { - return new Paystack; + }); + $this->registerPaystackFactory() + ->registerPaystackManager() + ->registerCoreBindings(); + } + + /** + * Registers the Paystack factory + * + * @return $this + */ + protected function registerPaystackFactory() + { + $this->app->singleton('paystack.factory', function (Container $container) { + $cache = $container['cache']; + + return new PaystackFactory($cache); }); + + $this->app->alias('paystack.factory', PaystackFactory::class); + + return $this; + } + + /** + * Registers Paystack manager + * + * @return $this + */ + protected function registerPaystackManager() + { + $this->app->singleton('paystack', function (Container $container) { + $config = $container['config']; + $factory = $container['paystack.factory']; + + return new PaystackManager($config, $factory); + }); + + $this->app->alias('paystack', PaystackManager::class); + + return $this; + } + + /** + * Registers the Core Paystack Binding + * + * @return $this + */ + protected function registerCoreBindings() + { + $this->app->bind('paystack.connection', function (Container $container) { + $manager = $container['paystack']; + + return $manager->connection(); + }); + + $this->app->alias('paystack.connection', Client::class); + + return $this; } /** @@ -53,6 +130,11 @@ public function register() */ public function provides() { - return ['laravel-paystack']; + return [ + 'paystack', + 'paystack.factory', + 'laravel-paystack', + 'paystack.connection', + ]; } } diff --git a/tests/AbstractTestCase.php b/tests/AbstractTestCase.php new file mode 100644 index 0000000..200a505 --- /dev/null +++ b/tests/AbstractTestCase.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + + +namespace Unicodeveloper\Paystack\Test\Facades; + +use GrahamCampbell\TestBenchCore\FacadeTrait; +use Unicodeveloper\Paystack\Facades\Paystack; +use Unicodeveloper\Paystack\PaystackManager; +use Unicodeveloper\Paystack\Test\AbstractTestCase; + +/** + * This is the github facade test class. + */ +class PaystackTest extends AbstractTestCase +{ + use FacadeTrait; + + /** + * Get the facade accessor. + * + * @return string + */ + protected function getFacadeAccessor() + { + return 'paystack'; + } + + /** + * Get the facade class. + * + * @return string + */ + protected function getFacadeClass() + { + return Paystack::class; + } + + /** + * Get the facade root. + * + * @return string + */ + protected function getFacadeRoot() + { + return PaystackManager::class; + } +} diff --git a/tests/PaystackFactoryTest.php b/tests/PaystackFactoryTest.php new file mode 100644 index 0000000..f858edb --- /dev/null +++ b/tests/PaystackFactoryTest.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Unicodeveloper\Paystack\Test; + + +use GrahamCampbell\TestBench\AbstractTestCase as AbstractTestBenchTestCase; +use Illuminate\Cache\Repository; +use Illuminate\Contracts\Cache\Factory; +use Mockery; +use Unicodeveloper\Paystack\PaystackFactory; +use Xeviant\Paystack\Client; +use Xeviant\Paystack\Exception\InvalidArgumentException; + +final class PaystackFactoryTest extends AbstractTestBenchTestCase +{ + + public function testIfFactoryCanBeCreatedWithMake() + { + $factory = $this->getFactory(); + + $client = $factory[0]->make(['secretKey' => 'sk_123', 'publicKey' => 'pk_123']); + + self::assertInstanceOf(Client::class, $client); + } + + public function testMakeWithCache() + { + $factory = $this->getFactory(); + + $factory[1]->shouldReceive('store')->once()->with(null)->andReturn(Mockery::mock(Repository::class)); + + $client = $factory[0]->make(['secretKey' => 'sk_123', 'publicKey' => 'pk_123', 'cache' => true]); + + $this->assertInstanceOf(Client::class, $client); + } + + public function testMakeWithApiUrl() + { + $factory = $this->getFactory(); + + $client = $factory[0]->make(['secretKey' => 'sk_123', 'publicKey' => 'pk_123', 'paymentUrl' => 'https://api.example.co']); + + $this->assertInstanceOf(Client::class, $client); + } + + public function testMakeWithApiVersion() + { + $factory = $this->getFactory(); + + $client = $factory[0]->make(['secretKey' => 'sk_123', 'publicKey' => 'pk_123', 'apiVersion' => 'v2']); + + $this->assertInstanceOf(Client::class, $client); + } + + /** + * + */ + public function testMakeShouldFailIfKeysAreNotSet() + { + $this->expectException(InvalidArgumentException::class); + $factory = $this->getFactory(); + + $factory[0]->make([]); + } + + + protected function getFactory() + { + $cache = Mockery::mock(Factory::class); + return [new PaystackFactory($cache), $cache]; + } +} \ No newline at end of file diff --git a/tests/PaystackManagerTest.php b/tests/PaystackManagerTest.php new file mode 100644 index 0000000..98377bb --- /dev/null +++ b/tests/PaystackManagerTest.php @@ -0,0 +1,52 @@ + 'sk_123abc', 'publicKey' => 'pk_123abc']; + + $manager = $this->getManager($config); + + $manager->getConfig()->shouldReceive('get')->once() + ->with('paystack.default')->andReturn('main'); + + $this->assertSame([], $manager->getConnections()); + + $return = $manager->connection(); + + $this->assertInstanceOf(Client::class, $return); + + $this->assertArrayHasKey('main', $manager->getConnections()); + } + + protected function getManager(array $config) + { + $repo = Mockery::mock(Repository::class); + $factory = Mockery::mock(PaystackFactory::class); + + + $manager = new PaystackManager($repo, $factory); + + $manager->getConfig()->shouldReceive('get')->once() + ->with('paystack.connections')->andReturn(['main' => $config]); + + $config['name'] = 'main'; + + $manager->getFactory()->shouldReceive('make')->once() + ->with($config)->andReturn(Mockery::mock(Client::class)); + + return $manager; + } +} \ No newline at end of file diff --git a/tests/PaystackServiceProviderTest.php b/tests/PaystackServiceProviderTest.php new file mode 100644 index 0000000..b8289f0 --- /dev/null +++ b/tests/PaystackServiceProviderTest.php @@ -0,0 +1,34 @@ +assertIsInjectable(PaystackFactory::class); + } + + public function testIfPaystackManagerIsInjectable() + { + $this->assertIsInjectable(PaystackManager::class); + } + + public function testBindings() + { + $this->assertIsInjectable(Client::class); + + $original = $this->app['paystack.connection']; + $this->app['paystack']->reconnect(); + $new = $this->app['paystack.connection']; + + $this->assertNotSame($original, $new); + $this->assertEquals($original, $new); + } +} \ No newline at end of file diff --git a/tests/PaystackTest.php b/tests/PaystackTest.php index 04f7f5e..a8a08b1 100644 --- a/tests/PaystackTest.php +++ b/tests/PaystackTest.php @@ -1,5 +1,7 @@ paystack->shouldReceive('getAllPlans')->andReturn(['intermediate-plan']); + $array = $this->paystack->shouldReceive('getAllPlans')->andReturn([]); $this->assertEquals('array', gettype(array($array))); }