Passed
Push — main ( 6b17f2...1e3b41 )
by Miaad
11:18
created

zarinpal::processCallback()   A

Complexity

Conditions 6
Paths 4

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 6
eloc 8
c 1
b 0
f 1
nc 4
nop 1
dl 0
loc 16
rs 9.2222
1
<?php
2
3
namespace BPT\pay;
4
5
use BPT\pay\zarinpal\refundInterface;
6
use BPT\pay\zarinpal\requestInterface;
7
use BPT\pay\zarinpal\unverifiedInterface;
8
use BPT\pay\zarinpal\verifyInterface;
9
use BPT\settings;
10
use CurlHandle;
11
use JetBrains\PhpStorm\NoReturn;
12
13
class zarinpal {
14
    public static string $merchant_id = '';
15
    public static bool $sandbox = false;
16
    public static bool $zarin_gate = false;
17
18
    const API_BASE = 'https://api.zarinpal.com/pg/v4/payment/';
19
20
    const SANDBOX_API_BASE = 'https://sandbox.zarinpal.com/pg/v4/payment/';
21
22
    const PAY_BASE = 'https://www.zarinpal.com/pg/StartPay/';
23
24
    const SANDBOX_PAY_BASE = 'https://sandbox.zarinpal.com/pg/StartPay/';
25
26
    private static CurlHandle $session;
27
28
    public static function init (string $merchant_id = '', bool $sandbox = false, bool $zarin_gate = false): void {
29
        self::$sandbox = settings::$pay['zarinpal']['sandbox'] ?? $sandbox;
30
        self::$zarin_gate = settings::$pay['zarinpal']['zarin_gate'] ?? settings::$pay['zarinpal']['zaringate'] ?? $zarin_gate;
31
        self::$merchant_id = settings::$pay['zarinpal']['merchant_id'] ?? $merchant_id;
32
        self::$session = curl_init();
33
        curl_setopt(self::$session, CURLOPT_RETURNTRANSFER, true);
34
        curl_setopt(self::$session, CURLOPT_SSL_VERIFYPEER, 1);
35
        curl_setopt(self::$session, CURLOPT_SSL_VERIFYHOST, 2);
36
        curl_setopt(self::$session, CURLOPT_HTTPHEADER, [
37
            'Content-Type: application/json',
38
            'Accept: application/json'
39
        ]);
40
        curl_setopt(self::$session, CURLOPT_POST, true);
41
    }
42
43
    private static function getUrl (string $endpoint, bool $pay = false): string {
44
        if ($pay) {
45
            $url = self::$sandbox ? self::SANDBOX_PAY_BASE : self::PAY_BASE;
46
        }
47
        else {
48
            $url = self::$sandbox ? self::SANDBOX_API_BASE : self::API_BASE;
49
        }
50
        $url .= $endpoint;
51
        if (self::$zarin_gate) {
52
            $url .= '/ZarinGate';
53
        }
54
        return $url;
55
    }
56
57
    private static function execute (string $endpoint, array $params = []): object {
58
        foreach ($params as $key => $value) {
59
            if (empty($value)) {
60
                unset($params[$key]);
61
            }
62
        }
63
64
        $session = self::$session;
65
66
        $params['merchant_id'] = self::$merchant_id;
67
68
        if (isset($params['authorization'])) {
69
            curl_setopt(self::$session, CURLOPT_HTTPHEADER, [
70
                'Content-Type: application/json',
71
                'Accept: application/json',
72
                'authorization: Bearer '.$params['authorization']
73
            ]);
74
            unset($params['authorization']);
75
        }
76
77
        curl_setopt($session, CURLOPT_POSTFIELDS, json_encode($params));
78
        curl_setopt($session, CURLOPT_URL, self::getUrl($endpoint));
79
80
        $result = json_decode(curl_exec($session));
0 ignored issues
show
Bug introduced by
It seems like curl_exec($session) can also be of type true; however, parameter $json of json_decode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

80
        $result = json_decode(/** @scrutinizer ignore-type */ curl_exec($session));
Loading history...
81
        if (isset($result->data)) {
82
            return $result->data;
83
        }
84
85
        return $result;
86
    }
87
88
    /**
89
     * @return object|requestInterface
90
     */
91
    public static function request (int $amount, string $description, string $callback_url, array $metadata = [], string $mobile = '', string $email = '', array $wages = [], int $card_pan = null, string $currency = ''): object {
92
        return self::execute('/request.json', [
93
            'amount'       => $amount,
94
            'description'  => $description,
95
            'callback_url' => $callback_url,
96
            'metadata'     => $metadata,
97
            'mobile'       => $mobile,
98
            'email'        => $email,
99
            'wages'        => $wages,
100
            'card_pan'     => $card_pan,
101
            'currency'     => $currency,
102
        ]);
103
    }
104
105
    public static function payURL (string|array $authority): bool|string {
106
        if (is_array($authority)) {
0 ignored issues
show
introduced by
The condition is_array($authority) is always true.
Loading history...
107
            if (!isset($authority->authority)) {
108
                return false;
109
            }
110
            $authority = $authority->authority;
111
        }
112
        return self::getUrl("/$authority", true);
113
    }
114
115
    /**
116
     * @return object|verifyInterface
117
     */
118
    public static function verify (int $amount, string $authority): object {
119
        return self::execute('/verify.json', [
120
            'amount'    => $amount,
121
            'authority' => $authority
122
        ]);
123
    }
124
125
    /**
126
     * @return object|unverifiedInterface
127
     */
128
    public static function unVerified (): object {
129
        return self::execute('/unVerified.json');
130
    }
131
132
    /**
133
     * @return object|refundInterface
134
     */
135
    public static function refund (string $authorization, string $authority): object {
136
        return self::execute('/refund.json', [
137
            'authorization' => $authorization,
138
            'authority'     => $authority
139
        ]);
140
    }
141
142
    #[NoReturn]
143
    public static function redirect (string $url): void {
144
        @header('Location: ' . $url);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for header(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

144
        /** @scrutinizer ignore-unhandled */ @header('Location: ' . $url);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
Bug introduced by
Are you sure the usage of header('Location: ' . $url) is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
145
        die("<meta http-equiv='refresh' content='0; url=$url' /><script>window.location.href = '$url';</script>");
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
146
    }
147
148
    public static function processCallback (int $amount): object|bool|int {
149
        if (!isset($_GET['Authority']) || !isset($_GET['Status'])) {
150
            return false;
151
        }
152
153
        if ($_GET['status'] != 'OK') {
154
            return false;
155
        }
156
157
        $detail = self::verify($amount, $_GET['Authority']);
158
159
        if (isset($detail->code) && $detail->code != 100) {
160
            return $detail->code;
161
        }
162
163
        return $detail;
164
    }
165
}
166