diff --git a/.docker/.gitpod.Dockerfile b/.docker/.gitpod.Dockerfile new file mode 100644 index 0000000..1c3bd34 --- /dev/null +++ b/.docker/.gitpod.Dockerfile @@ -0,0 +1,3 @@ +FROM gitpod/workspace-full + +RUN sudo install-packages php-xdebug \ No newline at end of file diff --git a/.github/workflows/change-review.yml b/.github/workflows/change-review.yml index 2dc7be7..8d3895c 100644 --- a/.github/workflows/change-review.yml +++ b/.github/workflows/change-review.yml @@ -14,7 +14,7 @@ jobs: strategy: fail-fast: true matrix: - php: [7.4, 8.1, 8.2] + php: [7.4, 8.1, 8.2, 8.3] env: XDEBUG_MODE: coverage @@ -48,17 +48,13 @@ jobs: - name: Install dependencies run: composer install --prefer-dist --no-progress - - name: 'Create env file' - run: | - touch .env - echo PUBLIC_KEY=${PUBLIC_KEY} >> .env - echo SECRET_KEY=${SECRET_KEY} >> .env - echo ENCRYPTION_KEY=${ENCRYPTION_KEY} >> .env - echo ENV=${ENV} >> .env - ls -a ${{ github.workspace }} - - name: run unit tests and coverage scan run: ./vendor/bin/pest --coverage --min=20 --coverage-clover ./coverage.xml + env: + PUBLIC_KEY: ${{ secrets.PUBLIC_KEY }} + SECRET_KEY: ${{ secrets.SECRET_KEY }} + ENCRYPTION_KEY: ${{ secrets.ENCRYPTION_KEY }} + ENV: ${{ secrets.ENV }} - name: Upload to Codecov uses: codecov/codecov-action@v2 diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 0000000..485aafb --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,15 @@ +# This configuration file was automatically generated by Gitpod. +# Please adjust to your needs (see https://www.gitpod.io/docs/introduction/learn-gitpod/gitpod-yaml) +# and commit this file to your remote git repository to share the goodness with others. + +# Learn more from ready-to-use templates: https://www.gitpod.io/docs/introduction/getting-started/quickstart + +image: + file: .docker/.gitpod.Dockerfile + +tasks: + - init: make + +vscode: + extensions: + - felixfbecker.php-debug diff --git a/Makefile b/Makefile index 0fa8af9..4db333a 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,15 @@ .PHONY: init -test: +check: @echo "Installing dependencies..." @composer install @echo "Installing dependencies... Done" @./vendor/bin/pest --coverage --min=0 --coverage-clover ./coverage.xml +test: + @./vendor/bin/pest --coverage --min=0 --coverage-clover ./coverage.xml + +debug: + XDEBUG_MODE=coverage ./vendor/bin/pest --coverage --coverage-html .log + + diff --git a/examples/card.php b/examples/card.php index 3141857..0f7ade0 100644 --- a/examples/card.php +++ b/examples/card.php @@ -4,7 +4,26 @@ use Flutterwave\Util\AuthMode; use Flutterwave\Util\Currency; -\Flutterwave\Flutterwave::bootstrap(); +use Flutterwave\Config\ForkConfig; +use Dotenv\Dotenv; +// custom config. + +if(!file_exists( '.env' )) { + $dotenv = Dotenv::createImmutable(__DIR__."/../"); +} else { + $dotenv = Dotenv::createImmutable(__DIR__."/"); +} + +$dotenv->safeLoad(); + +$config = ForkConfig::setUp( + $_ENV['SECRET_KEY'], + $_ENV['PUBLIC_KEY'], + $_ENV['ENV'], + $_ENV['ENCRYPTION_KEY'] +); + +\Flutterwave\Flutterwave::bootstrap($config); try { @@ -48,6 +67,8 @@ $data['customer'] = $customerObj; $payload = $cardpayment->payload->create($data); + + if(!empty($_REQUEST)) { $request = $_REQUEST; @@ -59,7 +80,7 @@ if(isset($request['make'])){ $result = $cardpayment->initiate($payload); - + dd($cardpayment); if($result['mode'] === AuthMode::PIN){ $instruction = $result['instruction']; require __DIR__."/view/form/pin.php"; diff --git a/phpunit.xml.dist b/phpunit.xml.dist index fc42dbe..86240f3 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -19,6 +19,12 @@ ./src + + ./vendor + ./vendor + ./vendor + tests/bootstrap.php + diff --git a/processPayment.php b/processPayment.php index 622ad36..0946145 100644 --- a/processPayment.php +++ b/processPayment.php @@ -9,15 +9,33 @@ use Flutterwave\EventHandlers\ModalEventHandler as PaymentHandler; use Flutterwave\Flutterwave; use Flutterwave\Library\Modal; +use \Flutterwave\Config\ForkConfig; -# start a session. +// start a session. session_start(); +// Define custom config. +// $myConfig = ForkConfig::setUp( +// 'FLWSECK_TEST-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-X', //Secret key +// 'FLWPUBK_TEST-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-X', // Public key +// 'FLWSECK_TESTXXXXXXXXXXX', //Encryption key +// 'staging' //Environment Variable +// ); + +// uncomment the block if you just want to pass the keys with a specific configuration. +// $_ENV['SECRET_KEY'] = "FLWSECK_TEST-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-X"; +// $_ENV['PUBLIC_KEY'] = "FLWPUBK_TEST-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-X"; +// $_ENV['ENCRYPTION_KEY'] = "FLWSECK_TESTXXXXXXXXXXXX"; +// $_ENV['ENV'] = "staging"; + +// controller default +$controller = null; + try { - Flutterwave::bootstrap(); + Flutterwave::bootstrap(); // create a .env or Flutterwave::bootstrap($myConfig) $customHandler = new PaymentHandler(); $client = new Flutterwave(); - $modalType = Modal::POPUP; // Modal::POPUP or Modal::STANDARD + $modalType = Modal::STANDARD; // Modal::POPUP or Modal::STANDARD $controller = new PaymentController( $client, $customHandler, $modalType ); } catch(\Exception $e ) { echo $e->getMessage(); diff --git a/setup.php b/setup.php index 1c5b813..88934ec 100644 --- a/setup.php +++ b/setup.php @@ -5,7 +5,7 @@ $flutterwave_installation = 'composer'; -if( !file_exists( '.env' )) { +if( !file_exists( '.env' ) && !is_dir('vendor')) { $dotenv = Dotenv::createImmutable(__DIR__."/../../../"); # on the event that the package is install via composer. } else { $flutterwave_installation = "manual"; @@ -17,7 +17,12 @@ //check if the current version of php is compatible if(!Helper\CheckCompatibility::isCompatible()) { - echo "Flutterwave: This SDK only support php version ". Helper\CheckCompatibility::MINIMUM_COMPATIBILITY. " or greater."; + if (PHP_SAPI === 'cli') { + echo "❌ Flutterwave: This SDK only support php version ". Helper\CheckCompatibility::MINIMUM_COMPATIBILITY. " or greater."; + } else { + echo "Flutterwave: This SDK only support php version ". Helper\CheckCompatibility::MINIMUM_COMPATIBILITY. " or greater."; + } + exit; } @@ -28,22 +33,27 @@ try{ foreach($flutterwaveKeys as $key) { - if( empty( $_ENV[ $key ] ) ) + if(empty($_ENV[ $key ]) && empty(\getenv($key))) { throw new InvalidArgumentException("$key environment variable missing."); } } }catch(\Exception $e) { - echo "Flutterwave sdk: " .$e->getMessage().""; + if (PHP_SAPI === 'cli') { + echo "❌❌Flutterwave sdk: " .$e->getMessage(); + echo "Kindly create a .env in the project root and add the required environment variables. ❌". PHP_EOL; + } else { + echo "Flutterwave sdk: " .$e->getMessage().""; + echo "
Kindly create a .env in the project root and add the required environment variables."; + } - echo "
Kindly create a .env in the project root and add the required environment variables."; exit; } $keys = [ - 'SECRET_KEY' => $_ENV['SECRET_KEY'], - 'PUBLIC_KEY' => $_ENV['PUBLIC_KEY'], - 'ENV' => $_ENV['ENV'], - 'ENCRYPTION_KEY' => $_ENV['ENCRYPTION_KEY'] + 'SECRET_KEY' => $_ENV['SECRET_KEY'] ?? \getenv('SECRET_KEY'), + 'PUBLIC_KEY' => $_ENV['PUBLIC_KEY'] ?? \getenv('PUBLIC_KEY'), + 'ENV' => $_ENV['ENV'] ?? \getenv('ENV'), + 'ENCRYPTION_KEY' => $_ENV['ENCRYPTION_KEY'] ?? \getenv('ENCRYPTION_KEY') ]; \ No newline at end of file diff --git a/src/Config/AbstractConfig.php b/src/Config/AbstractConfig.php index 6a34aad..63a9cc7 100644 --- a/src/Config/AbstractConfig.php +++ b/src/Config/AbstractConfig.php @@ -43,7 +43,11 @@ protected function __construct(string $secret_key, string $public_key, string $e [ 'base_uri' => EnvVariables::BASE_URL, 'timeout' => 60, - RequestOptions::VERIFY => \Composer\CaBundle\CaBundle::getSystemCaRootBundlePath() + 'headers' => ['User-Agent' => sprintf( + 'FlutterwavePHP/%d', EnvVariables::SDK_VERSION + )], + RequestOptions::VERIFY => + \Composer\CaBundle\CaBundle::getSystemCaRootBundlePath() ] ); diff --git a/src/Contract/ControllerInterface.php b/src/Contract/ControllerInterface.php new file mode 100644 index 0000000..6cb9c2e --- /dev/null +++ b/src/Contract/ControllerInterface.php @@ -0,0 +1,18 @@ +meta->authorization->mode; + + $mode = $resource['mode'] ?? $response->meta->authorization->mode; if (property_exists($response, 'data')) { $transactionId = $response->data->id; @@ -124,6 +125,12 @@ public function onAuthorization(\stdClass $response, ?array $resource = null): a $data['instruction'] = $response->data->processor_response; $data['validate'] = true; break; + default: + $data['data_to_save']['status'] = $response->data->status; + $data['data_to_save']['amount'] = $response->data->amount; + $data['data_to_save']['currency'] = $response->data->currency; + $data['data_to_save']['customer_name'] = $response->data->customer->name; + $data['data_to_save']['customer_email'] = $response->data->customer->email; } $data['mode'] = $mode; diff --git a/src/Helper/EnvVariables.php b/src/Helper/EnvVariables.php index 1806271..f70babe 100644 --- a/src/Helper/EnvVariables.php +++ b/src/Helper/EnvVariables.php @@ -7,6 +7,7 @@ class EnvVariables { public const VERSION = 'v3'; + public const SDK_VERSION = '1.0.7'; public const BASE_URL = 'https://api.flutterwave.com/' . self::VERSION; public const TIME_OUT = 30; } diff --git a/src/HttpAdapter/CurlClient.php b/src/HttpAdapter/CurlClient.php index 8fd34a5..bcd9ce6 100644 --- a/src/HttpAdapter/CurlClient.php +++ b/src/HttpAdapter/CurlClient.php @@ -1,6 +1,6 @@ logger->notice('Bill Payment Service::Retrieving Top Categories.'); + self::startRecording(); + $response = $this->request(null, 'GET', "top-".$this->name); + self::setResponseTime(); + return $response; + } + + /** + * Retrieve items under a specific biller code. + */ + public function getBillerItems(string $biller_code = null): \stdClass + { + if(is_null($biller_code)) { + $msg = "The required parameter" . $biller_code . " is not present in payload"; + $this->logger->error("Bill Payment Service::$msg"); + throw new \InvalidArgumentException("Bill Payment Service:$msg"); + } + + $this->logger->notice('Bill Payment Service::Retrieving items under biller '. $biller_code); + self::startRecording(); + $response = $this->request(null, 'GET', sprintf('billers/%s/items', $biller_code)); + self::setResponseTime(); + return $response; + } + /** * @throws ClientExceptionInterface + * @deprecated Use `validateCustomerInfo()` instead. */ public function validateService(string $item_code): \stdClass { @@ -47,6 +85,30 @@ public function validateService(string $item_code): \stdClass return $response; } + public function validateCustomerInfo(\Flutterwave\Payload $payload): \stdClass + { + $payload = $payload->toArray(); + + foreach (['biller_code', 'customer', 'item_code'] as $param) { + if (! array_key_exists($param, $payload)) { + $msg = "The required parameter ". $param. " is not present in payload"; + $this->logger->error("Bill Payment Service::$msg"); + throw new \InvalidArgumentException("Bill Payment Service:$msg"); + } + } + + $code = $payload['biller_code']; + $customer = $payload['customer']; + $customer = $customer[0] == '+' ? substr($customer, 1) : $customer; + $item_code = $payload['item_code']; + + $this->logger->notice('Bill Payment Service::Retrieving all Plans.'); + self::startRecording(); + $response = $this->request(null, 'GET', sprintf("bill-items/{$item_code}/validate?code=%s&customer=%s", $code, $customer)); + self::setResponseTime(); + return $response; + } + /** * @throws ClientExceptionInterface */ @@ -55,7 +117,7 @@ public function createPayment(\Flutterwave\Payload $payload): \stdClass $payload = $payload->toArray(); foreach ($this->requiredParams as $param) { if (! array_key_exists($param, $payload)) { - $msg = 'The required parameter {$param} is not present in payload'; + $msg = "The required parameter ". $param. " is not present in payload"; $this->logger->error("Bill Payment Service::$msg"); throw new \InvalidArgumentException("Bill Payment Service:$msg"); } @@ -63,9 +125,12 @@ public function createPayment(\Flutterwave\Payload $payload): \stdClass $body = $payload; + $biller_code = $payload['biller_code']; + $item_code = $payload['item_code']; + $this->logger->notice('Bill Payment Service::Creating a Bill Payment.'); self::startRecording(); - $response = $this->request($body, 'POST', 'bills'); + $response = $this->request($body, 'POST', sprintf('billers/%s/items/%s/payment', $biller_code, $item_code)); $this->logger->notice('Bill Payment Service::Created a Bill Payment Successfully.'); self::setResponseTime(); return $response; diff --git a/src/Service/CardPayment.php b/src/Service/CardPayment.php index 0bbf487..faadfb4 100644 --- a/src/Service/CardPayment.php +++ b/src/Service/CardPayment.php @@ -131,6 +131,20 @@ public function encryption(string $params): string */ public function handleAuthState(\stdClass $response, $payload): array { + // dd($response); + if(property_exists( $response, 'data' ) && property_exists( $response->data, 'status' ) && $response->data->status === 'successful') { + return [ + 'card_info' => $response->data->card, + 'transaction_id' => $response->data->id, + 'reference' => $response->data->tx_ref, + 'amount' => $response->data->amount, + 'mode' => $response->data->auth_model, + 'currency' => $response->data->currency, + 'customer' => $response->data->customer, + 'fraud_status' => $response->data->fraud_status + ]; + } + $mode = $response->meta->authorization->mode; if ($mode === 'pin') { $data = $this->eventHandler->onAuthorization($response, ['logger' => $this->logger]); diff --git a/src/Service/PayoutSubaccount.php b/src/Service/PayoutSubaccount.php index e63dcaf..493019d 100644 --- a/src/Service/PayoutSubaccount.php +++ b/src/Service/PayoutSubaccount.php @@ -8,20 +8,20 @@ use Flutterwave\EventHandlers\PayoutSubaccoutEventHandler; use Flutterwave\Payload; use Psr\Http\Client\ClientExceptionInterface; +use Flutterwave\EventHandlers\EventTracker; use stdClass; class PayoutSubaccount extends Service { + use EventTracker; private string $name = 'payout-subaccounts'; private array $requiredParams = [ 'email', 'mobilenumber','country' ]; - private PayoutSubaccoutEventHandler $eventHandler; public function __construct(?ConfigInterface $config = null) { parent::__construct($config); $endpoint = $this->name; $this->url = $this->baseUrl . '/' . $endpoint; - $this->eventHandler = new PayoutSubaccoutEventHandler(); } public function confirmPayload(Payload $payload): array @@ -51,9 +51,9 @@ public function create(Payload $payload): stdClass $this->logger->notice('PSA Service::Creating new Payout Subaccount.'); $body = $this->confirmPayload($payload); $this->logger->notice('PSA Service::Payload Confirmed.'); - $this->eventHandler::startRecording(); + self::startRecording(); $response = $this->request($body, 'POST'); - $this->eventHandler::setResponseTime(); + self::setResponseTime(); return $response; } @@ -62,9 +62,9 @@ public function create(Payload $payload): stdClass */ public function list(): stdClass { - $this->eventHandler::startRecording(); + self::startRecording(); $response = $this->request(null, 'GET'); - $this->eventHandler::setResponseTime(); + self::setResponseTime(); return $response; } @@ -73,9 +73,9 @@ public function list(): stdClass */ public function get(string $account_reference): \stdClass { - $this->eventHandler::startRecording(); + self::startRecording(); $response = $this->request(null, 'GET', "/$account_reference"); - $this->eventHandler::setResponseTime(); + self::setResponseTime(); return $response; } @@ -90,9 +90,9 @@ public function update(string $account_reference, Payload $payload): \stdClass throw new \InvalidArgumentException($msg); } - $this->eventHandler::startRecording(); + self::startRecording(); $response = $this->request($payload->toArray(), 'PUT', "/{$account_reference}"); - $this->eventHandler::setResponseTime(); + self::setResponseTime(); return $response; } @@ -101,9 +101,9 @@ public function update(string $account_reference, Payload $payload): \stdClass */ public function fetchTransactions(string $account_reference): \stdClass { - $this->eventHandler::startRecording(); + self::startRecording(); $response = $this->request(null, 'GET', "/{$account_reference}/transactions"); - $this->eventHandler::setResponseTime(); + self::setResponseTime(); return $response; } @@ -112,9 +112,9 @@ public function fetchTransactions(string $account_reference): \stdClass */ public function fetchAvailableBalance(string $account_reference, string $currency = 'NGN'): \stdClass { - $this->eventHandler::startRecording(); + self::startRecording(); $response = $this->request(null, 'GET', "/{$account_reference}/balances?currency={$currency}"); - $this->eventHandler::setResponseTime(); + self::setResponseTime(); return $response; } @@ -123,9 +123,9 @@ public function fetchAvailableBalance(string $account_reference, string $currenc */ public function fetchStaticVirtualAccounts(string $account_reference, string $currency = 'NGN'): stdClass { - $this->eventHandler::startRecording(); + self::startRecording(); $response = $this->request(null, 'GET', "/{$account_reference}/static-account?currency={$currency}"); - $this->eventHandler::setResponseTime(); + self::setResponseTime(); return $response; } } diff --git a/src/Service/Transactions.php b/src/Service/Transactions.php index ea40d0e..155b20d 100644 --- a/src/Service/Transactions.php +++ b/src/Service/Transactions.php @@ -5,12 +5,13 @@ namespace Flutterwave\Service; use Flutterwave\Contract\ConfigInterface; -use Flutterwave\EventHandlers\TransactionVerificationEventHandler; +use Flutterwave\EventHandlers\EventTracker; use Flutterwave\Traits\ApiOperations\Post; use Psr\Http\Client\ClientExceptionInterface; class Transactions extends Service { + use EventTracker; use Post; public const ENDPOINT = 'transactions'; @@ -26,14 +27,12 @@ class Transactions extends Service private array $payment_type = [ 'card','debit_ng_account','mobilemoney','bank_transfer', 'ach_payment', ]; - private TransactionVerificationEventHandler $eventHandler; public function __construct(?ConfigInterface $config = null) { parent::__construct($config); - $this->baseUrl = $this->config::BASE_URL; $this->end_point = Transactions::ENDPOINT; - $this->eventHandler = new TransactionVerificationEventHandler(); + } /** @@ -43,13 +42,13 @@ public function verify(string $transactionId): \stdClass { $this->checkTransactionId($transactionId); $this->logger->notice('Transaction Service::Verifying Transaction...' . $transactionId); - TransactionVerificationEventHandler::startRecording(); + self::startRecording(); $response = $this->request( null, 'GET', self::ENDPOINT . "/{$transactionId}/verify", ); - TransactionVerificationEventHandler::setResponseTime(); + self::setResponseTime(); return $response; } @@ -60,13 +59,13 @@ public function verify(string $transactionId): \stdClass public function verifyWithTxref(string $tx_ref): \stdClass { $this->logger->notice('Transaction Service::Verifying Transaction...' . $tx_ref); - TransactionVerificationEventHandler::startRecording(); + self::startRecording(); $response = $this->request( null, 'GET', self::ENDPOINT . '/verify_by_reference?tx_ref=' . $tx_ref, ); - TransactionVerificationEventHandler::setResponseTime(); + self::setResponseTime(); return $response; } @@ -77,13 +76,13 @@ public function refund(string $trasanctionId): \stdClass { $this->checkTransactionId($trasanctionId); $this->logger->notice("Transaction Service::Refunding Transaction...{$trasanctionId}"); - TransactionVerificationEventHandler::startRecording(); + self::startRecording(); $response = $this->request( null, 'GET', self::ENDPOINT . "/{$trasanctionId}/refund", ); - TransactionVerificationEventHandler::setResponseTime(); + self::setResponseTime(); return $response; } @@ -93,13 +92,13 @@ public function refund(string $trasanctionId): \stdClass public function getAllTransactions(): \stdClass { $this->logger->notice('Transaction Service::Retrieving all Transaction for Merchant'); - TransactionVerificationEventHandler::startRecording(); + self::startRecording(); $response = $this->request( null, 'GET', self::ENDPOINT, ); - TransactionVerificationEventHandler::setResponseTime(); + self::setResponseTime(); return $response; } @@ -110,13 +109,13 @@ public function getRefundInfo(string $trasanctionId): \stdClass { $this->checkTransactionId($trasanctionId); $this->logger->notice("Transaction Service::Retrieving refund:Transactionid => {$trasanctionId}"); - TransactionVerificationEventHandler::startRecording(); + self::startRecording(); $response = $this->request( null, 'GET', "refunds/{$trasanctionId}", ); - TransactionVerificationEventHandler::setResponseTime(); + self::setResponseTime(); return $response; } @@ -151,13 +150,13 @@ public function getTransactionFee( $logData = json_encode($data); $this->logger->notice("Transaction Service::Retrieving Transaction Fee: Util => {$logData}"); - TransactionVerificationEventHandler::startRecording(); + self::startRecording(); $response = $this->request( null, 'GET', self::ENDPOINT . "/fee?{$query}", ); - TransactionVerificationEventHandler::setResponseTime(); + self::setResponseTime(); return $response; } @@ -168,13 +167,13 @@ public function resendFailedHooks(string $transactionId): \stdClass { $this->checkTransactionId($transactionId); $this->logger->notice("Transaction Service::Resending Transaction Webhook: TransactionId => {$transactionId}"); - TransactionVerificationEventHandler::startRecording(); + self::startRecording(); $response = $this->request( null, - 'GET', + 'POST', self::ENDPOINT . "/{$transactionId}/resend-hook", ); - TransactionVerificationEventHandler::setResponseTime(); + self::setResponseTime(); return $response; } @@ -187,13 +186,13 @@ public function retrieveTimeline(string $transactionId): \stdClass $this->logger->notice( "Transaction Service::Retrieving Transaction Timeline: TransactionId => {$transactionId}" ); - TransactionVerificationEventHandler::startRecording(); + self::startRecording(); $response = $this->request( null, 'GET', - self::ENDPOINT . "/{$transactionId}/timeline", + self::ENDPOINT . "/{$transactionId}/events", ); - TransactionVerificationEventHandler::setResponseTime(); + self::setResponseTime(); return $response; } diff --git a/src/Traits/Setup/Configure.php b/src/Traits/Setup/Configure.php index 981fea9..dcc7cf1 100644 --- a/src/Traits/Setup/Configure.php +++ b/src/Traits/Setup/Configure.php @@ -6,21 +6,22 @@ use Flutterwave\Contract\ConfigInterface; use Flutterwave\Helper\Config; +use Flutterwave\Config\PackageConfig; use Flutterwave\Config\ForkConfig; trait Configure { public static function bootstrap(?ConfigInterface $config = null): void { - if (\is_null($config)) { + if (\is_null($config) && \is_null(self::$config)) { include __DIR__ . '/../../../setup.php'; if ('composer' === $flutterwave_installation) { - $config = Config::setUp( - $keys[Config::SECRET_KEY], - $keys[Config::PUBLIC_KEY], - $keys[Config::ENCRYPTION_KEY], - $keys[Config::ENV] + $config = PackageConfig::setUp( + $keys[PackageConfig::SECRET_KEY], + $keys[PackageConfig::PUBLIC_KEY], + $keys[PackageConfig::ENCRYPTION_KEY], + $keys[PackageConfig::ENV] ); } @@ -34,7 +35,7 @@ public static function bootstrap(?ConfigInterface $config = null): void } } - if (\is_null(self::$config)) { + if (\is_null(self::$config) && !\is_null($config)) { self::$config = $config; } diff --git a/tests/Resources/Card/test_cards.php b/tests/Resources/Card/test_cards.php new file mode 100644 index 0000000..72f8e8e --- /dev/null +++ b/tests/Resources/Card/test_cards.php @@ -0,0 +1,77 @@ + "5531886652142950", + "cvv" => "564", + "expiry_month" => "09", + "expiry_year" => "32" + ]; + + const MSTR_CARD_PIN_TWO = [ + "card_number" => "5399838383838381", + "cvv" => "470", + "expiry_month" => "10", + "expiry_year" => "31" + ]; + + const MSTR_3DS = [ + "card_number" => "5438898014560229", + "cvv" => "564", + "expiry_month" => "10", + "expiry_year" => "31" + ]; + + const VISA_3DS = [ + "card_number" => "4187427415564246", + "cvv" => "828", + "expiry_month" => "09", + "expiry_year" => "32" + ]; + + const VISA_3DS_TWO = [ + "card_number" => "4242424242424242", + "cvv" => "812", + "expiry_month" => "01", + "expiry_year" => "31" + ]; + + const VISA_3DS_THREE = [ + "card_number" => "4751763236699647", + "cvv" => "812", + "expiry_month" => "09", + "expiry_year" => "35" + ]; + + const VERVE_NOAUTH = [ + "card_number" => "5061460410120223210", + "cvv" => "780", + "expiry_month" => "12", + "expiry_year" => "31" + ]; + + const VERVE_PIN = [ + "card_number" => "5061460166976054667", + "cvv" => "780", + "expiry_month" => "10", + "expiry_year" => "22" + ]; + + const AVS = [ + "card_number" => "4556052704172643", + "cvv" => "899", + "expiry_month" => "09", + "expiry_year" => "32" + ]; + + const PREATH = [ + "card_number" => "5377283645077450", + "cvv" => "789", + "expiry_month" => "09", + "expiry_year" => "31" + ]; +} \ No newline at end of file diff --git a/tests/Unit/Service/CheckoutTest.php b/tests/Unit/Checkout/CheckoutTest.php similarity index 77% rename from tests/Unit/Service/CheckoutTest.php rename to tests/Unit/Checkout/CheckoutTest.php index f8680ac..1f2aac7 100644 --- a/tests/Unit/Service/CheckoutTest.php +++ b/tests/Unit/Checkout/CheckoutTest.php @@ -1,6 +1,6 @@ 'FLW_TEST|' . random_int( 10, 2000) . '|' . uniqid('aMx') ], + [ + "tx_ref" => 'FLW_TEST|' . random_int(10, 2000) . '|' . uniqid('aMx') + ], new ModalEventHandler(), ForkConfig::setUp( - $_ENV['SECRET_KEY'], - $_ENV['PUBLIC_KEY'], - $_ENV['ENCRYPTION_KEY'], - $_ENV['ENV'] + $_ENV['SECRET_KEY'] ?? \getenv('SECRET_KEY'), + $_ENV['PUBLIC_KEY'] ?? \getenv('PUBLIC_KEY'), + $_ENV['ENCRYPTION_KEY'] ?? \getenv('ENCRYPTION_KEY'), + $_ENV['ENV'] ?? \getenv('ENV') ), [ 'amount' => 3000, @@ -100,10 +116,10 @@ public function checkoutProvider() { [ "tx_ref" => 'FLW_TEST|' . random_int( 10, 2000) . '|' . uniqid('mAx') ], new ModalEventHandler(), ForkConfig::setUp( - $_ENV['SECRET_KEY'], - $_ENV['PUBLIC_KEY'], - $_ENV['ENCRYPTION_KEY'], - $_ENV['ENV'] + $_ENV['SECRET_KEY'] ?? \getenv('SECRET_KEY'), + $_ENV['PUBLIC_KEY'] ?? \getenv('PUBLIC_KEY'), + $_ENV['ENCRYPTION_KEY'] ?? \getenv('ENCRYPTION_KEY'), + $_ENV['ENV'] ?? \getenv('ENV') ), [ 'amount' => 1500, diff --git a/tests/Unit/Service/CardTest.php b/tests/Unit/Service/CardTest.php index 93c9b5a..ed77e5b 100644 --- a/tests/Unit/Service/CardTest.php +++ b/tests/Unit/Service/CardTest.php @@ -8,6 +8,7 @@ use PHPUnit\Framework\TestCase; use Flutterwave\Util\Currency; use Flutterwave\Test\Resources\Setup\Config; +use Test_Cards; class CardTest extends TestCase { @@ -32,12 +33,7 @@ public function testAuthModeReturnPin() ], "preauthorize" => false, "payment_plan" => null, - "card_details" => [ - "card_number" => "5531886652142950", - "cvv" => "564", - "expiry_month" => "09", - "expiry_year" => "32" - ] + "card_details" => Test_Cards::MSTR_CARD_PIN_TWO ], ]; @@ -47,6 +43,7 @@ public function testAuthModeReturnPin() "email" => "ol868gjdfjua@gmail.com", "phone" => "+2349067985861" ]); + $data['customer'] = $customerObj; $payload = $cardpayment->payload->create($data); $result = $cardpayment->initiate($payload); @@ -93,12 +90,7 @@ public function testAuthModeReturnRedirect() ], "preauthorize" => false, "payment_plan" => null, - "card_details" => [ - "card_number" => "5531886652142950", - "cvv" => "564", - "expiry_month" => "09", - "expiry_year" => "32" - ] + "card_details" => Test_Cards::MSTR_CARD_PIN_ONE ], ]; @@ -155,8 +147,42 @@ public function testAuthModeReturnRedirect() // $this->assertSame(AuthMode::AVS, $result['mode']); // } + public function testPreuthCard() + { + $data = [ + "amount" => 2000, + "currency" => Currency::NGN, + "tx_ref" => "TEST-".uniqid().time(), + "redirectUrl" => "https://www.example.com", + "additionalData" => [ + "subaccounts" => [ + ["id" => "RSA_345983858845935893"] + ], + "meta" => [ + "unique_id" => uniqid().uniqid() + ], + "preauthorize" => false, + "payment_plan" => null, + "card_details" => Test_Cards::PREATH + ], + ]; + + $cardpayment = Flutterwave::create("card"); + $customerObj = $cardpayment->customer->create([ + "full_name" => "Olaobaju Abraham", + "email" => "ol868gjdfjua@gmail.com", + "phone" => "+2349062985861" + ]); + $data['customer'] = $customerObj; + $payload = $cardpayment->payload->create($data); + $result = $cardpayment->initiate($payload); + + $this->assertTrue(!empty($result['url'])); + } + public function testAuthModelReturnNoauth() { $this->assertTrue(true); } + } \ No newline at end of file diff --git a/tests/Unit/Service/MomoTest.php b/tests/Unit/Service/MomoTest.php index 0afd9ab..d57137b 100644 --- a/tests/Unit/Service/MomoTest.php +++ b/tests/Unit/Service/MomoTest.php @@ -66,6 +66,8 @@ public function testInitiateTanzaniaRedirect(){ $payload = $momopayment->payload->create($data); $result = $momopayment->initiate($payload); $this->assertSame('pending',$result['data_to_save']['status']); + + return $data['tx_ref']; } public function testAuthModeGhanaRedirect(){ diff --git a/tests/Unit/Service/TransactionTest.php b/tests/Unit/Service/TransactionTest.php index f8e8ebe..f6ba683 100644 --- a/tests/Unit/Service/TransactionTest.php +++ b/tests/Unit/Service/TransactionTest.php @@ -2,9 +2,65 @@ namespace Unit\Service; +use Flutterwave\Service\Transactions; use PHPUnit\Framework\TestCase; class TransactionTest extends TestCase { + public Transactions $service; + protected function setUp(): void + { + $this->service = new Transactions(); + } + + /** + * @depends Unit\Service\MomoTest::testInitiateTanzaniaRedirect + */ + public function testVerifyingTransaction(string $tx_ref) + { + $result = $this->service->verifyWithTxref($tx_ref); + $data = $result->data; + $this->assertSame($data->customer->email, "developers@flutterwavego.com"); + return [ "id" => $data->id, "amount" => $data->amount, "currency" => $data->currency ]; + } + + /** + * @depends testVerifyingTransaction + */ + public function testVerifyingTransactionWithId(array $data) + { + $tx_id = $data['id']; + + $result = $this->service->verify($tx_id); + $data = $result->data; + $this->assertSame($data->customer->email, "developers@flutterwavego.com"); + } + + /** + * @depends testVerifyingTransaction + */ + public function testResendingFailedHooks( array $data ) + { + sleep(6); + $tx_id = $data['id']; + $result = $this->service->resendFailedHooks($tx_id); + $this->assertTrue( $result->status === "success" && $result->data === "hook sent"); + } + + /** + * @depends testVerifyingTransaction + */ + public function testRetrievingTimeline( array $data ) + { + $tx_id = $data['id']; + $result = $this->service->retrieveTimeline($tx_id); + $this->assertTrue( $result->status === "success" && $result->message === "Transaction events fetched"); + } + + // public function testValidateCharge( string $flw_ref ) + // { + // $result = $this->service->validate("3310", $flw_ref); + // dd($result); + // } } \ No newline at end of file diff --git a/tests/bootstrap.php b/tests/bootstrap.php index a0eed40..de73edc 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,4 +1,5 @@