Passed
Push — master ( 3847e9...5c849b )
by Iman
01:30
created

JeniusHttp::paymentRefund()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 39
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 24
c 0
b 0
f 0
dl 0
loc 39
rs 9.536
cc 1
nc 1
nop 5
1
<?php
2
3
namespace Jenius;
4
5
use Carbon\Carbon;
6
use Unirest\Request;
7
use Unirest\Request\Body;
8
use Unirest\Response;
9
10
class JeniusHttp
11
{
12
    /**
13
     * Default Timezone.
14
     *
15
     * @var string
16
     */
17
    private static $timezone = 'Asia/Jakarta';
18
19
    /**
20
     * Default Jenius Port.
21
     *
22
     * @var int
23
     */
24
    private static $port = 443;
25
26
    /**
27
     * Default Jenius Host.
28
     *
29
     * @var string
30
     */
31
    private static $hostName = 'apidev.btpn.com';
32
33
    /**
34
     * Default Jenius Scheme.
35
     *
36
     * @var string
37
     */
38
    private static $scheme = 'https';
39
40
    /**
41
     * Default Jenius Segment.
42
     *
43
     * @var string
44
     */
45
    private static $segment = 'pay';
46
47
    /**
48
     * Timeout curl.
49
     *
50
     * @var int
51
     */
52
    private static $timeOut = 60;
53
54
    /**
55
     * Default Curl Options.
56
     *
57
     * @var int
58
     */
59
    private static $curlOptions = array(
60
        CURLOPT_SSL_VERIFYHOST => 0,
61
        CURLOPT_SSLVERSION => 6,
62
        CURLOPT_SSL_VERIFYPEER => false,
63
        CURLOPT_TIMEOUT => 60
64
    );
65
66
    /**
67
     * Default Jenius Settings.
68
     *
69
     * @var array
70
     */
71
    protected $settings = array(
72
        'x_channel_id' => '',
73
        'client_id' => '',
74
        'client_secret' => '',
75
        'api_key' => '',
76
        'secret_key' => '',
77
        'curl_options' => array(),
78
        // Backward compatible
79
        'host' => 'apidev.btpn.com',
80
        'scheme' => 'https',
81
        'segment' => 'pay',
82
        'timeout' => 60,
83
        'port' => 443,
84
        // New Options
85
        'options' => array(
86
            'host' => 'apidev.btpn.com',
87
            'scheme' => 'https',
88
            'segment' => 'pay',
89
            'timeout' => 60,
90
            'port' => 443
91
        )
92
    );
93
94
    /**
95
     * Default Constructor.
96
     *
97
     * @param string $x_channel_id
98
     * @param string $client_id
99
     * @param string $client_secret
100
     * @param string $api_key
101
     * @param string $secret_key
102
     * @param array $options
103
     */
104
    public function __construct($x_channel_id, $client_id, $client_secret, $api_key, $secret_key, array $options = [])
105
    {
106
        // Required parameters.
107
        $this->settings['x-channel-id'] = $x_channel_id;
108
        $this->settings['client_id'] = $client_id;
109
        $this->settings['client_secret'] = $client_secret;
110
        $this->settings['api_key'] = $api_key;
111
        $this->settings['secret_key'] = $secret_key;
112
        $this->settings['host'] =
113
            preg_replace('/http[s]?\:\/\//', '', $this->settings['host'], 1);
114
115
        foreach ($options as $key => $value) {
116
            if (isset($this->settings[$key])) {
117
                $this->settings[$key] = $value;
118
            }
119
        }
120
121
        // Setup optional scheme, if scheme is empty
122
        if (isset($options['scheme'])) {
123
            $this->settings['scheme'] = $options['scheme'];
124
            $this->settings['options']['scheme'] = $options['scheme'];
125
        } else {
126
            $this->settings['scheme'] = self::getScheme();
127
            $this->settings['options']['scheme'] = self::getScheme();
128
        }
129
130
        // Setup optional host, if host is empty
131
        if (isset($options['host'])) {
132
            $this->settings['host'] = $options['host'];
133
            $this->settings['options']['host'] = $options['host'];
134
        } else {
135
            $this->settings['host'] = self::getHostName();
136
            $this->settings['options']['host'] = self::getHostName();
137
        }
138
139
        // Setup optional segment, if segment is empty
140
        if (isset($options['segment'])) {
141
            $this->settings['segment'] = $options['segment'];
142
            $this->settings['options']['segment'] = $options['segment'];
143
        } else {
144
            $this->settings['segment'] = self::getSegment();
145
            $this->settings['options']['segment'] = self::getSegment();
146
        }
147
148
        // Setup optional port, if port is empty
149
        if (isset($options['port'])) {
150
            $this->settings['port'] = $options['port'];
151
            $this->settings['options']['port'] = $options['port'];
152
        } else {
153
            $this->settings['port'] = self::getPort();
154
            $this->settings['options']['port'] = self::getPort();
155
        }
156
157
        // Setup optional timeout, if timeout is empty
158
        if (isset($options['timeout'])) {
159
            $this->settings['timeout'] = $options['timeout'];
160
            $this->settings['options']['timeout'] = $options['timeout'];
161
        } else {
162
            $this->settings['timeout'] = self::getTimeOut();
163
            $this->settings['options']['timeout'] = self::getTimeOut();
164
        }
165
166
        // Set Default Curl Options.
167
        Request::curlOpts(self::$curlOptions);
0 ignored issues
show
Bug introduced by
self::curlOptions of type integer is incompatible with the type array expected by parameter $options of Unirest\Request::curlOpts(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

167
        Request::curlOpts(/** @scrutinizer ignore-type */ self::$curlOptions);
Loading history...
168
169
        // Set custom curl options
170
        if (!empty($this->settings['curl_options'])) {
171
            $data = self::mergeCurlOptions(self::$curlOptions, $this->settings['curl_options']);
0 ignored issues
show
Bug introduced by
self::curlOptions of type integer is incompatible with the type array expected by parameter $existing_options of Jenius\JeniusHttp::mergeCurlOptions(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

171
            $data = self::mergeCurlOptions(/** @scrutinizer ignore-type */ self::$curlOptions, $this->settings['curl_options']);
Loading history...
172
            Request::curlOpts($data);
173
        }
174
    }
175
176
    /**
177
     * Get Settings
178
     *
179
     * @return array
180
     */
181
    public function getSettings()
182
    {
183
        return $this->settings;
184
    }
185
186
    /**
187
     * Build the ddn domain.
188
     * output = 'https://apidev.btpn.com:443'
189
     * scheme = http(s)
190
     * host = apidev.btpn.com
191
     * port = 80 ? 443
192
     *
193
     * @return string
194
     */
195
    private function ddnDomain()
196
    {
197
        return $this->settings['scheme'] . '://' . $this->settings['host'] . ':' . $this->settings['port'] . '/';
198
    }
199
200
    /**
201
     * Get Token
202
     *
203
     * @return Response
204
     */
205
    public function httpAuth()
206
    {
207
        $client_id = $this->settings['client_id'];
208
        $client_secret = $this->settings['client_secret'];
209
210
        $headerToken = base64_encode("$client_id:$client_secret");
211
212
        $headers = array(
213
            'Content-Type' => 'application/x-www-form-urlencoded',
214
            'Accept' => 'application/json',
215
            'Authorization' => "Basic $headerToken"
216
        );
217
218
        $request_path = "oauth/token";
219
        $domain = $this->ddnDomain();
220
        $full_url = $domain . $request_path;
221
222
        $data = array('grant_type' => 'client_credentials');
223
        $body = Body::form($data);
224
        $response = Request::post($full_url, $headers, $body);
225
226
        return $response;
227
    }
228
229
    /**
230
     * Create payment request
231
     *
232
     * @param string $oauth_token
233
     * @param int $amount
234
     * @param $cashtag
235
     * @param $promoCode
236
     * @param $urlCallback
237
     * @param $purchageDesc
238
     * @param $createdAt
239
     * @param $referenceNo
240
     * @return Response
241
     */
242
    public function paymentRequest(
243
        $oauth_token,
244
        $amount,
245
        $cashtag,
246
        $promoCode,
247
        $urlCallback,
248
        $purchageDesc,
249
        $createdAt,
250
        $referenceNo
251
    ) {
252
        $uriSign = "POST:/" . $this->settings['segment'] . "/payrequest";
253
        $apiKey = $this->settings['api_key'];
254
        $apiSecret = $this->settings['secret_key'];
255
256
        $btpnTimestamp = self::generateBtpnTimestamp();
257
        $btpnOriginalTimestamp = self::generateOriginalTimestamp($createdAt);
258
259
        $headers = array();
260
        $headers['Accept'] = 'application/json';
261
        $headers['Content-Type'] = 'application/json';
262
        $headers['Authorization'] = "Bearer $oauth_token";
263
        $headers['BTPN-ApiKey'] = $apiKey;
264
        $headers['BTPN-Timestamp'] = $btpnTimestamp;
265
        $headers['X-Channel-Id'] = $this->settings['x-channel-id'];
266
        $headers['X-Node'] = 'Jenius Pay';
267
        $headers['X-Transmission-Date-Time'] = $btpnTimestamp;
268
        $headers['X-Original-Transmission-Date-Time'] = $btpnOriginalTimestamp;
269
        $headers['X-Reference-No'] = $referenceNo;
270
271
        $request_path = $this->settings['segment'] . "/payrequest";
272
        $domain = $this->ddnDomain();
273
        $full_url = $domain . $request_path;
274
275
        $bodyData = array();
276
        $bodyData['txn_amount'] = $amount;
277
        $bodyData['cashtag'] = $cashtag;
278
        $bodyData['promo_code'] = $promoCode;
279
        $bodyData['url_callback'] = $urlCallback;
280
        $bodyData['purchase_desc'] = $purchageDesc;
281
282
        $authSignature = self::generateSign($uriSign, $apiKey, $apiSecret, $btpnTimestamp, $bodyData);
283
284
        $headers['BTPN-Signature'] = $authSignature;
285
286
        $encoderData = json_encode($bodyData, JSON_UNESCAPED_SLASHES);
287
288
        $body = Body::form($encoderData);
289
        $response = Request::post($full_url, $headers, $body);
290
291
        return $response;
292
    }
293
294
    /**
295
     * Get payment status
296
     *
297
     * @param string $oauth_token
298
     * @param $referenceNo
299
     * @param $createdAt
300
     * @return Response
301
     */
302
    public function paymentStatus(
303
        $oauth_token,
304
        $referenceNo,
305
        $createdAt
306
    ) {
307
        $uriSign = "GET:/" . $this->settings['segment'] . "/paystatus";
308
        $apiKey = $this->settings['api_key'];
309
        $apiSecret = $this->settings['secret_key'];
310
311
        $btpnTimestamp = self::generateBtpnTimestamp();
312
        $btpnOriginalTimestamp = self::generateOriginalTimestamp($createdAt);
313
314
        $headers = array();
315
        $headers['Accept'] = 'application/json';
316
        $headers['Content-Type'] = 'application/json';
317
        $headers['Authorization'] = "Bearer $oauth_token";
318
        $headers['BTPN-ApiKey'] = $apiKey;
319
        $headers['BTPN-Timestamp'] = $btpnTimestamp;
320
        $headers['X-Channel-Id'] = $this->settings['x-channel-id'];
321
        $headers['X-Node'] = 'Jenius Pay';
322
        $headers['X-Transmission-Date-Time'] = $btpnTimestamp;
323
        $headers['X-Original-Transmission-Date-Time'] = $btpnOriginalTimestamp;
324
        $headers['X-Reference-No'] = $referenceNo;
325
326
        $request_path = $this->settings['segment'] . "/paystatus";
327
        $domain = $this->ddnDomain();
328
        $full_url = $domain . $request_path;
329
330
        $authSignature = self::generateSign($uriSign, $apiKey, $apiSecret, $btpnTimestamp, []);
331
332
        $headers['BTPN-Signature'] = $authSignature;
333
334
        $response = Request::get($full_url, $headers, null);
335
336
        return $response;
337
    }
338
339
    /**
340
     * Create payment refund
341
     *
342
     * @param string $oauth_token
343
     * @param $approvalCode
344
     * @param $referenceNo
345
     * @param $amount
346
     * @param $createdAt
347
     * @return Response
348
     */
349
    public function paymentRefund(
350
        $oauth_token,
351
        $approvalCode,
352
        $referenceNo,
353
        $amount,
354
        $createdAt
355
    ) {
356
        $uriSign = "DELETE:/" . $this->settings['segment'] . "/payrefund?approval=" . $approvalCode;
357
        $apiKey = $this->settings['api_key'];
358
        $apiSecret = $this->settings['secret_key'];
359
360
        $btpnTimestamp = self::generateBtpnTimestamp();
361
        $btpnOriginalTimestamp = self::generateOriginalTimestamp($createdAt);
362
363
        $headers = array();
364
        $headers['Accept'] = 'application/json';
365
        $headers['Content-Type'] = 'application/json';
366
        $headers['Authorization'] = "Bearer $oauth_token";
367
        $headers['BTPN-ApiKey'] = $apiKey;
368
        $headers['BTPN-Timestamp'] = $btpnTimestamp;
369
        $headers['X-Channel-Id'] = $this->settings['x-channel-id'];
370
        $headers['X-Node'] = 'Jenius Pay';
371
        $headers['X-Transmission-Date-Time'] = $btpnTimestamp;
372
        $headers['X-Original-Transmission-Date-Time'] = $btpnOriginalTimestamp;
373
        $headers['X-Reference-No'] = $referenceNo;
374
        $headers['X-Amount'] = $amount;
375
376
377
        $request_path = $this->settings['segment'] . "/payrefund?approval=" . $approvalCode;
378
        $domain = $this->ddnDomain();
379
        $full_url = $domain . $request_path;
380
381
        $authSignature = self::generateSign($uriSign, $apiKey, $apiSecret, $btpnTimestamp, []);
382
383
        $headers['BTPN-Signature'] = $authSignature;
384
385
        $response = Request::delete($full_url, $headers, null);
386
387
        return $response;
388
    }
389
390
    /**
391
     * Generate Signature.
392
     *
393
     * @param string $url Url yang akan disign.
394
     * @param $apiKey
395
     * @param $apiSecret
396
     * @param $btpnTimestamp
397
     * @param array|mixed $bodyToHash array Body yang akan dikirimkan ke Server Jenius.
398
     *
399
     * @return string
400
     */
401
    public static function generateSign($url, $apiKey, $apiSecret, $btpnTimestamp, $bodyToHash = [])
402
    {
403
        if (!empty($bodyToHash)) {
404
            $encoderData = json_encode($bodyToHash, JSON_UNESCAPED_SLASHES);
405
            $stringToSign = $url . ":" . $apiKey . ":" . $btpnTimestamp . ":" . $encoderData;
406
        } else {
407
            $stringToSign = $url . ":" . $apiKey . ":" . $btpnTimestamp;
408
        }
409
410
        $auth_signature = hash_hmac('sha256', $stringToSign, $apiSecret, true);
411
        return base64_encode($auth_signature);
412
    }
413
414
    /**
415
     * Set TimeZone.
416
     *
417
     * @param string $timeZone
418
     *
419
     * @return string
420
     */
421
    public static function setTimeZone($timeZone)
422
    {
423
        self::$timezone = $timeZone;
424
    }
425
426
    /**
427
     * Get TimeZone.
428
     *
429
     * @return string
430
     */
431
    public static function getTimeZone()
432
    {
433
        return self::$timezone;
434
    }
435
436
    /**
437
     * Set Jenius Hostname
438
     *
439
     * @param string $hostName
440
     *
441
     * @return string
442
     */
443
    public static function setHostName($hostName)
444
    {
445
        self::$hostName = $hostName;
446
    }
447
448
    /**
449
     * Get Jenius Hostname
450
     *
451
     * @return string
452
     */
453
    public static function getHostName()
454
    {
455
        return self::$hostName;
456
    }
457
458
    /**
459
     * Set Jenius Segment
460
     *
461
     * @param string $segment
462
     *
463
     * @return string
464
     */
465
    public static function setSegment($segment)
466
    {
467
        self::$segment = $segment;
468
    }
469
470
    /**
471
     * Get Jenius Segment
472
     *
473
     * @return string
474
     */
475
    public static function getSegment()
476
    {
477
        return self::$segment;
478
    }
479
480
    /**
481
     * Get Max Execution Time.
482
     *
483
     * @return string
484
     */
485
    public static function getTimeOut()
486
    {
487
        return self::$timeOut;
488
    }
489
490
    /**
491
     * Get Curl Options
492
     *
493
     * @return string
494
     */
495
    public static function getCurlOptions()
496
    {
497
        return self::$curlOptions;
498
    }
499
500
    /**
501
     * Setup Curl Options.
502
     *
503
     * @param array $curlOpts
504
     * @return void
505
     */
506
    public static function setCurlOptions(array $curlOpts = [])
507
    {
508
        $data = self::mergeCurlOptions(self::$curlOptions, $curlOpts);
0 ignored issues
show
Bug introduced by
self::curlOptions of type integer is incompatible with the type array expected by parameter $existing_options of Jenius\JeniusHttp::mergeCurlOptions(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

508
        $data = self::mergeCurlOptions(/** @scrutinizer ignore-type */ self::$curlOptions, $curlOpts);
Loading history...
509
        self::$curlOptions = $data;
0 ignored issues
show
Documentation Bug introduced by
It seems like $data of type array is incompatible with the declared type integer of property $curlOptions.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
510
    }
511
512
    /**
513
     * Set Max Execution Time
514
     *
515
     * @param int $timeOut
516
     *
517
     * @return string
518
     */
519
    public static function setTimeOut($timeOut)
520
    {
521
        self::$timeOut = $timeOut;
522
        return self::$timeOut;
523
    }
524
525
    /**
526
     * Set Jenius Port
527
     *
528
     * @param int $port
529
     *
530
     * @return void
531
     */
532
    public static function setPort($port)
533
    {
534
        self::$port = $port;
535
    }
536
537
    /**
538
     * Get Jenius Port
539
     *
540
     * @return int
541
     */
542
    public static function getPort()
543
    {
544
        return self::$port;
545
    }
546
547
    /**
548
     * Set Jenius Scheme
549
     *
550
     * @param int $scheme
551
     *
552
     * @return string
553
     */
554
    public static function setScheme($scheme)
555
    {
556
        self::$scheme = $scheme;
557
    }
558
559
    /**
560
     * Get Jenius Scheme
561
     *
562
     * @return string
563
     */
564
    public static function getScheme()
565
    {
566
        return self::$scheme;
567
    }
568
569
    /**
570
     * Generate ISO8601 Time.
571
     *
572
     * @return string
573
     */
574
    public static function generateBtpnTimestamp()
575
    {
576
        $date = Carbon::now(self::getTimeZone());
577
        date_default_timezone_set(self::getTimeZone());
578
        $fmt = $date->format('Y-m-d\TH:i:s');
579
        $ISO8601 = sprintf("$fmt.%s%s", substr(microtime(), 2, 3), date('P'));
580
581
        return $ISO8601;
582
    }
583
584
    // 2020-09-26T21:14:07
585
    public static function generateOriginalTimestamp($date)
586
    {
587
        $ISO8601 = sprintf("$date.%s%s", substr(microtime(), 2, 3), date('P'));
588
        return $ISO8601;
589
    }
590
591
    /**
592
     * Merge from existing array.
593
     *
594
     * @param array $existing_options
595
     * @param array $new_options
596
     * @return array
597
     */
598
    private static function mergeCurlOptions(&$existing_options, $new_options)
599
    {
600
        $existing_options = $new_options + $existing_options;
601
        return $existing_options;
602
    }
603
604
    /**
605
     * Implode an array with the key and value pair giving
606
     * a glue, a separator between pairs and the array
607
     * to implode.
608
     *
609
     * @param string $glue The glue between key and value
610
     * @param string $separator Separator between pairs
611
     * @param array $array The array to implode
612
     *
613
     * @return string The imploded array
614
     * @throws JeniusHttpException error
615
     */
616
    public static function arrayImplode($glue, $separator, $array = [])
617
    {
618
        if (!is_array($array)) {
0 ignored issues
show
introduced by
The condition is_array($array) is always true.
Loading history...
619
            throw new JeniusHttpException('Data should array.');
620
        }
621
        if (empty($array)) {
622
            throw new JeniusHttpException('Parameter can`t be empty.');
623
        }
624
        foreach ($array as $key => $val) {
625
            if (is_array($val)) {
626
                $val = implode(',', $val);
627
            }
628
            $string[] = "{$key}{$glue}{$val}";
629
        }
630
631
        return implode($separator, $string);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $string seems to be defined by a foreach iteration on line 624. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
632
    }
633
}
634