diff --git a/README.md b/README.md index fcd077d..ca1f9af 100644 --- a/README.md +++ b/README.md @@ -166,6 +166,8 @@ Code examples and usage can be found in [Usage.md](USAGE.md) If you prefer a step by step tutorial, you can read the article I wrote on [How to integrate Mpesa into your Laravel Application](https://www.iankumu.com/blog/laravel-mpesa). +You can also find a [demo application](https://github.com/Iankumu/Payments) I created that uses the package. It provides a starting point on how to integrate Mpesa into a Laravel Application. + ### Testing ```bash diff --git a/src/Exceptions/CallbackException.php b/src/Exceptions/CallbackException.php new file mode 100644 index 0000000..24cd8a5 --- /dev/null +++ b/src/Exceptions/CallbackException.php @@ -0,0 +1,17 @@ + $callbackurl ]; } else { - return response()->json([ - 'error' => 'Callback URL cannot be null' - ], Response::HTTP_NOT_ACCEPTABLE); + throw CallbackException::make( + 'callback_url', + 'Ensure you have set a Callback URL in the mpesa config file' + ); } - $response = $this->MpesaRequest($url, $data); - return $response; + return $this->MpesaRequest($url, $data); } /** @@ -149,7 +150,7 @@ public function stkpush($phonenumber, $amount, $account_number, $callbackurl = n * This method is used to check the status of a Lipa Na M-Pesa Online Payment. * * @param string $checkoutRequestId This is a global unique identifier of the processed checkout transaction request. - * @return object Curl Response from Mpesa + * @return \Illuminate\Http\Client\Response */ public function stkquery($checkoutRequestId) @@ -163,8 +164,7 @@ public function stkquery($checkoutRequestId) $url = $this->url . "/mpesa/stkpushquery/v1/query"; - $response = $this->MpesaRequest($url, $post_data); - return $response; + return $this->MpesaRequest($url, $post_data); } @@ -177,7 +177,7 @@ public function stkquery($checkoutRequestId) * @param int $phonenumber The phone number of the recipient in the format 254xxxxxxxxx * @param string $command_id The type of transaction being made. Can be SalaryPayment,BusinessPayment or PromotionPayment * @param string $remarks Any additional information. Must be present. - * @return object Curl Response from Mpesa + * @return \Illuminate\Http\Client\Response */ public function b2c($phonenumber, $command_id, $amount, $remarks) { @@ -196,8 +196,21 @@ public function b2c($phonenumber, $command_id, $amount, $remarks) "Occassion" => '', //can be null ]; - $response = $this->MpesaRequest($url, $body); - return $response; + if (is_null($this->b2cresult)) { + throw CallbackException::make( + 'b2c_result_url', + 'Ensure you have set the B2C Result URL in the mpesa config file' + ); + } + + if (is_null($this->b2ctimeout)) { + throw CallbackException::make( + 'b2c_timeout_url', + 'Ensure you have set the B2C Timeout URL in the mpesa config file' + ); + } + + return $this->MpesaRequest($url, $body); } /** * Business to Client With Validation @@ -220,7 +233,7 @@ public function b2c($phonenumber, $command_id, $amount, $remarks) * @param string $command_id The type of transaction being made. Can be SalaryPayment,BusinessPayment or PromotionPayment * @param string $remarks Any additional information. Must be present. * @param string $id_number The id number of the recipient - * @return object Curl Response from Mpesa + * @return \Illuminate\Http\Client\Response */ public function validated_b2c($phonenumber, $command_id, $amount, $remarks, $id_number) { @@ -240,6 +253,21 @@ public function validated_b2c($phonenumber, $command_id, $amount, $remarks, $id_ "IDType" => "01", //01 for national id "IDNumber" => $id_number, ]; + + if (is_null($this->b2cresult)) { + throw CallbackException::make( + 'b2c_result_url', + 'Ensure you have set the B2C Result URL in the mpesa config file' + ); + } + + if (is_null($this->b2ctimeout)) { + throw CallbackException::make( + 'b2c_timeout_url', + 'Ensure you have set the B2C Timeout URL in the mpesa config file' + ); + } + return $this->MpesaRequest($url, $body); } @@ -249,7 +277,7 @@ public function validated_b2c($phonenumber, $command_id, $amount, $remarks, $id_ * This method is used to register URLs for callbacks when money is sent from the MPesa toolkit menu * * @param string $shortcode The till number or paybill number the urls will be associated with - * @return object Curl Response from Mpesa + * @return \Illuminate\Http\Client\Response */ public function c2bregisterURLS($shortcode) { @@ -263,8 +291,21 @@ public function c2bregisterURLS($shortcode) "ValidationURL" => $this->c2bvalidate, //url should be https and should not contain keywords such as mpesa,safaricom etc ]; - $response = $this->MpesaRequest($url, $body); - return $response; + if (is_null($this->c2bconfirm)) { + throw CallbackException::make( + 'c2b_confirmation_url', + 'Ensure you have set the C2B Confirmation URL in the mpesa config file' + ); + } + + if (is_null($this->c2bvalidate)) { + throw CallbackException::make( + 'c2b_validation_url', + 'Ensure you have set the C2B Validate URL in the mpesa config file' + ); + } + + return $this->MpesaRequest($url, $body); } /** @@ -277,7 +318,7 @@ public function c2bregisterURLS($shortcode) * @param string $shortcode The Paybill/Till number receiving the funds * @param string $command_id The Type of transaction. Whether it is a paybill transaction(CustomerPayBillOnline) or a Till number transaction(CustomerBuyGoodsOnline) * @param string $account_number The account number for a paybill. The default is null - * @return object Curl Response from Safaricom + * @return \Illuminate\Http\Client\Response */ public function c2bsimulate($phonenumber, $amount, $shortcode, $command_id, $account_number = null) { @@ -303,9 +344,7 @@ public function c2bsimulate($phonenumber, $amount, $shortcode, $command_id, $acc $url = $this->url . '/mpesa/c2b/v2/simulate'; - - $response = $this->MpesaRequest($url, $data); - return $response; + return $this->MpesaRequest($url, $data); } @@ -318,7 +357,7 @@ public function c2bsimulate($phonenumber, $amount, $shortcode, $command_id, $acc * @param string $transactionid Unique identifier to identify a transaction on M-Pesa * @param int $identiertype identifier to identify the orginization * @param string $remarks Any additional information. Must be present. - * @return object Curl Response from Safaricom + * @return \Illuminate\Http\Client\Response */ public function transactionStatus($shortcode, $transactionid, $identiertype, $remarks) { @@ -338,8 +377,21 @@ public function transactionStatus($shortcode, $transactionid, $identiertype, $re "Occassion" => "", ]; - $response = $this->MpesaRequest($url, $body); - return $response; + if (is_null($this->statusresult)) { + throw CallbackException::make( + 'status_result_url', + 'Ensure you have set the Transaction Status Result URL in the mpesa config file' + ); + } + + if (is_null($this->statustimeout)) { + throw CallbackException::make( + 'status_timeout_url', + 'Ensure you have set the Transaction Status Timeout URL in the mpesa config file' + ); + } + + return $this->MpesaRequest($url, $body); } /** @@ -350,7 +402,7 @@ public function transactionStatus($shortcode, $transactionid, $identiertype, $re * @param int $shortcode Organization/MSISDN receiving the transaction * @param int $identiertype identifier to identify the orginization * @param string $remarks Any additional information. Must be present. - * @return object Curl Response from Safaricom + * @return \Illuminate\Http\Client\Response */ public function accountBalance($shortcode, $identiertype, $remarks) { @@ -367,8 +419,21 @@ public function accountBalance($shortcode, $identiertype, $remarks) "QueueTimeOutURL" => $this->baltimeout, ]; - $response = $this->MpesaRequest($url, $body); - return $response; + if (is_null($this->statusresult)) { + throw CallbackException::make( + 'balance_result_url', + 'Ensure you have set the Account Balance Result URL in the mpesa config file' + ); + } + + if (is_null($this->statustimeout)) { + throw CallbackException::make( + 'balance_timeout_url', + 'Ensure you have set the Account Balance Timeout URL in the mpesa config file' + ); + } + + return $this->MpesaRequest($url, $body); } /** @@ -380,7 +445,7 @@ public function accountBalance($shortcode, $identiertype, $remarks) * @param int $shortcode Your Org's shortcode. * @param string $transactionid This is the M-Pesa Transaction ID of the transaction which you wish to reverse. * @param string $remarks Any additional information. Must be present. - * @return object Curl Response from Safaricom + * @return \Illuminate\Http\Client\Response */ public function reversal($shortcode, $transactionid, $amount, $remarks) { @@ -401,7 +466,20 @@ public function reversal($shortcode, $transactionid, $amount, $remarks) "Occasion" => "" ]; - $response = $this->MpesaRequest($url, $body); - return $response; + if (is_null($this->reverseresult)) { + throw CallbackException::make( + 'reversal_result_url', + 'Ensure you have set the Reversal Result URL in the mpesa config file' + ); + } + + if (is_null($this->reversetimeout)) { + throw CallbackException::make( + 'reversal_timeout_url', + 'Ensure you have set the Reversal Timeout URL in the mpesa config file' + ); + } + + return $this->MpesaRequest($url, $body); } } diff --git a/tests/TestCase.php b/tests/TestCase.php index 50a40cd..1af48b4 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -2,7 +2,7 @@ namespace Iankumu\Mpesa\Tests; -use Iankumu\Mpesa\Mpesa; +use Iankumu\Mpesa\MpesaServiceProvider; use Orchestra\Testbench\TestCase as Orchestra; class TestCase extends Orchestra @@ -13,7 +13,6 @@ class TestCase extends Orchestra protected function setUp(): void { parent::setUp(); - config(['app.url' => 'https://49cb48b01f608f.lhr.life']); } /* @@ -27,6 +26,9 @@ protected function getEnvironmentSetUp($app) { // Alter the testing mpesa environment $app['config']->set('mpesa.environment', 'sandbox'); + $app['config']->set('mpesa.mpesa_consumer_key', '12345'); + $app['config']->set('mpesa.mpesa_consumer_secret', '12345'); + $app['config']->set('mpesa.callback_url', null); } /** @@ -35,7 +37,9 @@ protected function getEnvironmentSetUp($app) */ protected function getPackageProviders($app) { - return ['Iankumu\Mpesa\MpesaServiceProvider']; + return [ + MpesaServiceProvider::class + ]; } /** @test */ diff --git a/tests/Unit/B2CTest.php b/tests/Unit/B2CTest.php index a7abc49..b8a3437 100644 --- a/tests/Unit/B2CTest.php +++ b/tests/Unit/B2CTest.php @@ -4,6 +4,7 @@ use Iankumu\Mpesa\Mpesa; use Iankumu\Mpesa\Tests\TestCase; +use Iankumu\Mpesa\Exceptions\CallbackException; class B2CTest extends TestCase { @@ -19,4 +20,13 @@ public function can_initiate_b2c() $result = $mpesa->b2c('0707070707', 'SalaryPayment', 100, 'Salary Payment'); $this->assertSame(true, $result); } + + /** @test */ + public function b2c_will_throw_an_exception_when_the_callbacks_are_null() + { + $this->expectException(CallbackException::class); + + //Should Throw an Exception as the callback is null + (new Mpesa())->b2c('0707070707', 'SalaryPayment', 100, 'Salary Payment'); + } } diff --git a/tests/Unit/C2BRegisterURLSTest.php b/tests/Unit/C2BRegisterURLSTest.php index b3c656d..0192629 100644 --- a/tests/Unit/C2BRegisterURLSTest.php +++ b/tests/Unit/C2BRegisterURLSTest.php @@ -2,6 +2,7 @@ namespace Iankumu\Mpesa\Tests\Unit; +use Iankumu\Mpesa\Exceptions\CallbackException; use Iankumu\Mpesa\Mpesa; use Iankumu\Mpesa\Tests\TestCase; @@ -18,4 +19,13 @@ public function can_register_c2b_urls() $this->assertSame(true, $mpesa->c2bregisterURLS(12345)); } + + /** @test */ + public function c2b_will_throw_an_exception_when_the_callbacks_are_null() + { + $this->expectException(CallbackException::class); + + //Should Throw an Exception as the callback is null + (new Mpesa())->c2bregisterURLS(12345); + } } diff --git a/tests/Unit/GenerateSecurityCredentialTest.php b/tests/Unit/GenerateSecurityCredentialTest.php index 33a2e56..835bb39 100644 --- a/tests/Unit/GenerateSecurityCredentialTest.php +++ b/tests/Unit/GenerateSecurityCredentialTest.php @@ -4,7 +4,6 @@ use Iankumu\Mpesa\Mpesa; use Iankumu\Mpesa\Tests\TestCase; -use Iankumu\Mpesa\Utils\MpesaHelper; class GenerateSecurityCredentialTest extends TestCase { diff --git a/tests/Unit/MpesaSTKTest.php b/tests/Unit/MpesaSTKTest.php index d26abae..179fad5 100644 --- a/tests/Unit/MpesaSTKTest.php +++ b/tests/Unit/MpesaSTKTest.php @@ -2,6 +2,7 @@ namespace Iankumu\Mpesa\Tests\Unit; +use Iankumu\Mpesa\Exceptions\CallbackException; use Iankumu\Mpesa\Mpesa; use Iankumu\Mpesa\Tests\TestCase; @@ -18,4 +19,13 @@ public function can_initiate_stkpush() $this->assertSame(true, $result); } + + /** @test */ + public function stkpush_will_throw_an_exception_when_the_callbacks_are_null() + { + $this->expectException(CallbackException::class); + + //Should Throw an Exception as the callback is null + (new Mpesa())->stkpush('0707070707', 100, 12345); + } } diff --git a/tests/Unit/ValidatedB2CTest.php b/tests/Unit/ValidatedB2CTest.php index cb298e0..bd6a465 100644 --- a/tests/Unit/ValidatedB2CTest.php +++ b/tests/Unit/ValidatedB2CTest.php @@ -2,6 +2,7 @@ namespace Iankumu\Mpesa\Tests\Unit; +use Iankumu\Mpesa\Exceptions\CallbackException; use Iankumu\Mpesa\Mpesa; use Iankumu\Mpesa\Tests\TestCase; use Illuminate\Support\Facades\Log; @@ -15,10 +16,10 @@ public function can_validate_b2c() $mpesa = $this->createStub(Mpesa::class); $mpesa->method('validated_b2c') - ->with('0707070707', 'SalaryPayment', 100, 'Salary Payment','120912992')//Will take a phone number and ID Number of the person to be paid + ->with('0707070707', 'SalaryPayment', 100, 'Salary Payment', '120912992') //Will take a phone number and ID Number of the person to be paid ->willReturn(true); - $result = $mpesa->validated_b2c('0707070707', 'SalaryPayment', 100, 'Salary Payment','120912992'); + $result = $mpesa->validated_b2c('0707070707', 'SalaryPayment', 100, 'Salary Payment', '120912992'); $result->assertJsonStructure([ 'ConversationID', 'OriginatorConversationID', @@ -26,4 +27,13 @@ public function can_validate_b2c() 'ResponseDescription', ]); } + + /** @test */ + public function validated_b2c_will_throw_an_exception_when_the_callbacks_are_null() + { + $this->expectException(CallbackException::class); + + //Should Throw an Exception as the callback is null + (new Mpesa())->validated_b2c('0707070707', 'SalaryPayment', 100, 'Salary Payment', '120912992');; + } }