Mpesa::submit_request()   A
last analyzed

Complexity

Conditions 4
Paths 4

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 25
rs 9.52
c 0
b 0
f 0
cc 4
nc 4
nop 2
1
<?php
2
3
namespace Konectdigital\Mpesa;
4
5
use Exception;
6
use Illuminate\Support\Facades\File;
7
8
9
class Mpesa
10
{
11
    /**
12
     * The common part of the MPesa API endpoints
13
     * @var string $base_url
14
     */
15
    private $base_url;
16
    /**
17
     * The consumer key
18
     * @var string $consumer_key
19
     */
20
    public $consumer_key;
21
    /**
22
     * The consumer key secret
23
     * @var string $consumer_secret
24
     */
25
    public $consumer_secret;
26
    /**
27
     * The MPesa Paybill number
28
     * @var int $paybill
29
     */
30
    public $paybill;
31
    /**
32
     * The Lipa Na MPesa paybill number
33
     * @var int $lipa_na_mpesa
34
     */
35
    public $lipa_na_mpesa;
36
    /**
37
     * The Lipa Na MPesa paybill number SAG Key
38
     * @var string $lipa_na_mpesa_key
39
     */
40
    public $lipa_na_mpesa_key;
41
    /**
42
     * The Mpesa portal Username
43
     * @var string $initiator_username
44
     */
45
    public $initiator_username;
46
    /**
47
     * The Mpesa portal Password
48
     * @var string $initiator_password
49
     */
50
    public $initiator_password;
51
    /**
52
     * The Callback common part of the URL eg "https://domain.com/callbacks/"
53
     * @var string $initiator_password
54
     */
55
    private $callback_baseurl;
56
    /**
57
     * The test phone number provided by safaricom. For developers
58
     * @var string $test_msisdn
59
     */
60
    private $test_msisdn;
61
    /**
62
     * The signed API credentials
63
     * @var string $cred
64
     */
65
    private $cred;
66
    private $access_token;
67
68
    /*Callbacks*/
69
    public $bctimeout;
70
    public $bcresult;
71
    public $bbtimeout;
72
    public $bbresult;
73
    public $baltimeout;
74
    public $balresult;
75
    public $statustimeout;
76
    public $statusresult;
77
    public $reversetimeout;
78
    public $reverseresult;
79
    public $cbvalidate;
80
    public $cbconfirm;
81
    public $lnmocallback;
82
    /**
83
     * Construct method
84
     *
85
     * Initializes the class with an array of API values.
86
     *
87
     * @param array $config
88
     * @return void
89
     * @throws exception if the values array is not valid
90
     */
0 ignored issues
show
Bug introduced by
There is no parameter named $config. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
91
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
92
93
94
    public function __construct()
95
    {
96
        if (config('mpesa.mpesa_env') == 'sandbox') {
97
            $this->base_url = 'https://sandbox.safaricom.co.ke/mpesa/';
98
        } else {
99
            $this->base_url = 'https://api.safaricom.co.ke/mpesa/';
100
        }
101
        //Base URL for the API endpoints. This is basically the 'common' part of the API endpoints
102
        $this->consumer_key = config('mpesa.consumer_key');
103
        $this->consumer_secret = config('mpesa.consumer_secret');
104
        $this->paybill = config('mpesa.paybill');
105
        $this->lipa_na_mpesa = config('mpesa.lipa_na_mpesa');
106
        $this->lipa_na_mpesa_key = config('mpesa.lipa_na_mpesa_passkey');
107
        $this->initiator_username = config('mpesa.initiator_username');
108
        $this->initiator_password = config('mpesa.initiator_password');
109
110
        $this->callback_baseurl = config('mpesa.lnmocallback');
111
        $this->lnmocallback = config('mpesa.lnmocallback');
112
        $this->test_msisdn = config('mpesa.test_msisdn');
113
114
        // c2b the urls
115
        $this->cbvalidate = config('mpesa.c2b_validate_callback');
116
        $this->cbconfirm = config('mpesa.c2b_confirm_callback');
117
118
        // b2c URLs
119
        $this->bctimeout = config('mpesa.b2c_timeout');
120
        $this->bcresult = config('mpesa.b2c_result');
121
122
        //Set up access token
123
        $this->access_token = $this->getAccessToken();
124
    }
125
126
    /**
127
     * Submit Request
128
     *
129
     * Handles submission of all API endpoints queries
130
     *
131
     * @param string $url The API endpoint URL
132
     * @param json $data The data to POST to the endpoint $url
0 ignored issues
show
Bug introduced by
There is no parameter named $url. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
133
     * @return object|boolean Curl response or FALSE on failure
0 ignored issues
show
Bug introduced by
There is no parameter named $data. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
134
     * @throws exception if the Access Token is not valid
135
     */
136
137
    public function setCred()
138
    {
139
        if (config('mpesa.mpesa_env') == 'sandbox') {
140
141
            $pubkey = File::get(__DIR__ . '/cert/sandbox.cer');
142
        } else {
143
            $pubkey = File::get(__DIR__ . '/cert/production.cer');
144
        }
145
        openssl_public_encrypt($this->initiator_password, $output, $pubkey, OPENSSL_PKCS1_PADDING);
146
        $this->cred = base64_encode($output);
147
    }
148
149
150
    public function getAccessToken()
151
    {
152
        $credentials = base64_encode($this->consumer_key . ':' . $this->consumer_secret);
153
        $ch = curl_init();
154
        curl_setopt($ch, CURLOPT_URL, 'https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials');
155
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
156
        curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: Basic ' . $credentials, 'Content-Type: application/json'));
157
        $response = curl_exec($ch);
158
        $info = curl_getinfo($ch);
159
        curl_close($ch);
160
        $response = json_decode($response);
161
162
        if ($info["http_code"] == 200) {
163
            $access_token = $response->access_token;
164
            $this->access_token = $access_token;
165
            return $access_token;
166
        } else {
167
            //throw new Exception("Invalid Consumer key or secret");
168
            return false;
169
        }
170
    }
171
172
    private function submit_request($url, $data)
173
    { // Returns cURL response
174
175
        if (isset($this->access_token)) {
176
            $access_token = $this->access_token;
177
        } else {
178
            $access_token = $this->getAccessToken();
179
        }
180
181
        if ($access_token != '' || $access_token !== FALSE) {
182
            $curl = curl_init();
183
            curl_setopt($curl, CURLOPT_URL, $url);
184
            curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: application/json', 'Authorization: Bearer ' . $access_token));
185
186
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
187
            curl_setopt($curl, CURLOPT_POST, TRUE);
188
            curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
189
190
            $response = curl_exec($curl);
191
            curl_close($curl);
192
            return $response;
193
        } else {
194
            return FALSE;
195
        }
196
    }
197
198
    /**
199
     * Business to Client
200
     *
201
     * This method is used to send money to the clients Mpesa account.
202
     *
203
     * @param int $amount The amount to send to the client
204
     * @param int $phone The phone number of the client in the format 2547xxxxxxxx
205
     * @return object Curl Response from submit_request, FALSE on failure
206
     */
207
208
    public function b2c($amount, $phone, $command_id, $remarks)
209
    {
210
        //this function will set b2c credentials
211
        $this->setCred();
212
        $request_data = array(
213
            'InitiatorName' => $this->initiator_username,
214
            'SecurityCredential' => $this->cred,
215
            'CommandID' => $command_id,
216
            'Amount' => $amount,
217
            'PartyA' => $this->paybill,
218
            'PartyB' => $phone,
219
            'Remarks' => $remarks,
220
            'QueueTimeOutURL' => $this->bctimeout,
221
            'ResultURL' => $this->bcresult,
222
            'Occasion' => '' //Optional
223
        );
224
        $data = json_encode($request_data);
225
        $url = $this->base_url . 'b2c/v1/paymentrequest';
226
        $response = $this->submit_request($url, $data);
227
        return $response;
228
    }
229
230
    /**
231
     * Business to Business
232
     *
233
     * This method is used to send money to other business Mpesa paybills.
234
     *
235
     * @param int $amount The amount to send to the business
236
     * @param int $shortcode The shortcode of the business to send to
237
     * @return object Curl Response from submit_request, FALSE on failure
238
     */
239
240
    public function b2b($amount, $shortcode)
0 ignored issues
show
Unused Code introduced by
The parameter $amount is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $shortcode is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
241
    {
242
        $request_data = array(
243
            'Initiator' => $this->initiator_username,
244
            'SecurityCredential' => $this->cred,
245
            'CommandID' => 'BusinessToBusinessTransfer',
246
            'SenderIdentifierType' => 'Shortcode',
247
            'RecieverIdentifierType' => 'Shortcode',
248
            'Amount' => 100,
249
            'PartyA' => $this->paybill,
250
            'PartyB' => 600000,
251
            'AccountReference' => 'Bennito',
252
            'Remarks' => 'This is a test comment or remark',
253
            'QueueTimeOutURL' => $this->bbtimeout,
254
            'ResultURL' => $this->bbresult,
255
        );
256
        $data = json_encode($request_data);
257
        $url = $this->base_url . 'b2b/v1/paymentrequest';
258
        $response = $this->submit_request($url, $data);
259
        return $response;
260
    }
261
262
    /**
263
     * Client to Business
264
     *
265
     * This method is used to register URLs for callbacks when money is sent from the MPesa toolkit menu
266
     *
267
     * @param string $confirmURL The local URL that MPesa calls to confirm a payment
268
     * @param string $ValidationURL The local URL that MPesa calls to validate a payment
0 ignored issues
show
Bug introduced by
There is no parameter named $confirmURL. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
269
     * @return object Curl Response from submit_request, FALSE on failure
0 ignored issues
show
Bug introduced by
There is no parameter named $ValidationURL. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
270
     */
271
272
    public function c2bRegisterUrls()
273
    {
274
        $request_data = array(
275
            'ShortCode' => $this->paybill,
276
            'ResponseType' => 'Completed',
277
            'ConfirmationURL' => $this->cbconfirm,
278
            'ValidationURL' => $this->cbvalidate
279
        );
280
        $data = json_encode($request_data);
281
        //header('Content-Type: application/json');
282
283
        $url = $this->base_url . 'c2b/v1/registerurl';
284
        $response = $this->submit_request($url, $data);
285
        //\Log::info($response);
286
        return $response;
287
    }
288
289
    /**
290
     * C2B Simulation
291
     *
292
     * This method is used to simulate a C2B Transaction to test your ConfirmURL and ValidationURL in the Client to Business method
293
     *
294
     * @param int $amount The amount to send to Paybill number
295
     * @param int $msisdn A dummy Safaricom phone number to simulate transaction in the format 2547xxxxxxxx
296
     * @param string $ref A reference name for the transaction
297
     * @return object Curl Response from submit_request, FALSE on failure
298
     */
299
300
    public function simulateC2B($amount, $msisdn, $ref)
301
    {
302
        $data = array(
303
            'ShortCode' => $this->paybill,
304
            'CommandID' => 'CustomerPayBillOnline',
305
            'Amount' => $amount,
306
            'Msisdn' => $msisdn,
307
            'BillRefNumber' => $ref
308
        );
309
        $data = json_encode($data);
310
        $url = $this->base_url . 'c2b/v1/simulate';
311
        $response = $this->submit_request($url, $data);
312
        return $response;
313
    }
314
315
    /**
316
     * Check Balance
317
     *
318
     * Check Paybill balance
319
     *
320
     * @return object Curl Response from submit_request, FALSE on failure
321
     */
322
    public function check_balance()
323
    {
324
        $data = array(
325
            'CommandID' => 'AccountBalance',
326
            'PartyA' => $this->paybill,
327
            'IdentifierType' => '4',
328
            'Remarks' => 'Remarks or short description',
329
            'Initiator' => $this->initiator_username,
330
            'SecurityCredential' => $this->cred,
331
            'QueueTimeOutURL' => $this->baltimeout,
332
            'ResultURL' => $this->balresult
333
        );
334
        $data = json_encode($data);
335
        $url = $this->base_url . 'accountbalance/v1/query';
336
        $response = $this->submit_request($url, $data);
337
        return $response;
338
    }
339
340
    /**
341
     * Transaction status request
342
     *
343
     * This method is used to check a transaction status
344
     *
345
     * @param string $transaction ID eg LH7819VXPE
346
     * @return object Curl Response from submit_request, FALSE on failure
347
     */
348
349 View Code Duplication
    public function status_request($transaction = 'LH7819VXPE')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
350
    {
351
        $data = array(
352
            'CommandID' => 'TransactionStatusQuery',
353
            'PartyA' => $this->paybill,
354
            'IdentifierType' => 4,
355
            'Remarks' => 'Testing API',
356
            'Initiator' => $this->initiator_username,
357
            'SecurityCredential' => $this->cred,
358
            'QueueTimeOutURL' => $this->statustimeout,
359
            'ResultURL' => $this->statusresult,
360
            'TransactionID' => $transaction,
361
            'Occassion' => 'Test'
362
        );
363
        $data = json_encode($data);
364
        $url = $this->base_url . 'transactionstatus/v1/query';
365
        $response = $this->submit_request($url, $data);
366
        return $response;
367
    }
368
369
    /**
370
     * Transaction Reversal
371
     *
372
     * This method is used to reverse a transaction
373
     *
374
     * @param int $receiver Phone number in the format 2547xxxxxxxx
375
     * @param string $trx_id Transaction ID of the Transaction you want to reverse eg LH7819VXPE
376
     * @param int $amount The amount from the transaction to reverse
377
     * @return object Curl Response from submit_request, FALSE on failure
378
     */
379
380 View Code Duplication
    public function reverse_transaction($receiver, $trx_id, $amount)
0 ignored issues
show
Unused Code introduced by
The parameter $receiver is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $trx_id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
381
    {
382
        $data = array(
383
            'CommandID' => 'TransactionReversal',
384
            'ReceiverParty' => $this->test_msisdn,
385
            'RecieverIdentifierType' => 1, //1=MSISDN, 2=Till_Number, 4=Shortcode
386
            'Remarks' => 'Testing',
387
            'Amount' => $amount,
388
            'Initiator' => $this->initiator_username,
389
            'SecurityCredential' => $this->cred,
390
            'QueueTimeOutURL' => $this->reversetimeout,
391
            'ResultURL' => $this->reverseresult,
392
            'TransactionID' => 'LIE81C8EFI'
393
        );
394
        $data = json_encode($data);
395
        $url = $this->base_url . 'reversal/v1/request';
396
        $response = $this->submit_request($url, $data);
397
        return $response;
398
    }
399
400
    /*********************************************************************
401
     *
402
     * 	LNMO APIs
403
     *
404
     * *******************************************************************/
405
406
    public function express($amount, $phone, $ref = "Payment", $desc = "Payment")
407
    {
408
        if (!is_numeric($amount) || $amount < 1 || !is_numeric($phone)) {
409
            throw new Exception("Invalid amount and/or phone number. Amount should be 10 or more, phone number should be 07xxxxxxxxx");
410
            return FALSE;
0 ignored issues
show
Unused Code introduced by
return FALSE; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
411
        }
412
413
        $phone     = (substr($phone, 0, 1) == "+") ? str_replace("+", "", $phone) : $phone;
414
        $phone     = (substr($phone, 0, 1) == "0") ? preg_replace("/^0/", "254", $phone) : $phone;
415
        $phone     = (substr($phone, 0, 1) == "7") ? "254{$phone}" : $phone;
416
417
        $timestamp = date('YmdHis');
418
        $passwd = base64_encode($this->lipa_na_mpesa . $this->lipa_na_mpesa_key . $timestamp);
419
        $data = array(
420
            'BusinessShortCode' => $this->lipa_na_mpesa,
421
            'Password' => $passwd,
422
            'Timestamp' => $timestamp,
423
            'TransactionType' => 'CustomerPayBillOnline',
424
            'Amount' => $amount,
425
            'PartyA' => $phone,
426
            'PartyB' => $this->lipa_na_mpesa,
427
            'PhoneNumber' => $phone,
428
            'CallBackURL' => $this->lnmocallback,
429
            'AccountReference' => $ref,
430
            'TransactionDesc' => $desc,
431
        );
432
        $data = json_encode($data);
433
        $url = $this->base_url . 'stkpush/v1/processrequest';
434
        $response = $this->submit_request($url, $data);
435
436
        if (isset($response)) {
437
            return $response;
438
        } else {
439
            return false;
440
        }
441
    }
442
443
    private function lnmoQuery($checkoutRequestID = null)
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
444
    {
445
        $timestamp = date('YmdHis');
446
        $passwd = base64_encode($this->lipa_na_mpesa . $this->lipa_na_mpesa_key . $timestamp);
447
448
        if ($checkoutRequestID == null || $checkoutRequestID == '') {
449
            //throw new Exception("Checkout Request ID cannot be null");
450
            return FALSE;
451
        }
452
453
        $data = array(
454
            'BusinessShortCode' => $this->lipa_na_mpesa,
455
            'Password' => $passwd,
456
            'Timestamp' => $timestamp,
457
            'CheckoutRequestID' => $checkoutRequestID
458
        );
459
        $data = json_encode($data);
460
        $url = $this->base_url . 'stkpushquery/v1/query';
461
        $response = $this->submit_request($url, $data);
462
        return $response;
463
    }
464
}
465