Zibal   A
last analyzed

Complexity

Total Complexity 22

Size/Duplication

Total Lines 270
Duplicated Lines 0 %

Importance

Changes 10
Bugs 1 Features 0
Metric Value
eloc 110
c 10
b 1
f 0
dl 0
loc 270
rs 10
wmc 22

9 Methods

Rating   Name   Duplication   Size   Complexity  
A pay() 0 9 2
A purchase() 0 49 5
A __construct() 0 5 1
A notVerified() 0 6 2
A checkOptionalDetails() 0 21 3
A verify() 0 50 3
A extractDetails() 0 10 3
A translateStatus() 0 21 2
A createReceipt() 0 5 1
1
<?php
2
3
namespace Shetabit\Multipay\Drivers\Zibal;
4
5
use GuzzleHttp\Client;
6
use Shetabit\Multipay\Abstracts\Driver;
7
use Shetabit\Multipay\Exceptions\InvalidPaymentException;
8
use Shetabit\Multipay\Exceptions\PurchaseFailedException;
9
use Shetabit\Multipay\Contracts\ReceiptInterface;
10
use Shetabit\Multipay\Invoice;
11
use Shetabit\Multipay\Receipt;
12
use Shetabit\Multipay\RedirectionForm;
13
use Shetabit\Multipay\Request;
14
15
class Zibal extends Driver
16
{
17
    /**
18
     * Invoice
19
     *
20
     * @var Invoice
21
     */
22
    protected $invoice;
23
24
    /**
25
     * Driver settings
26
     *
27
     * @var object
28
     */
29
    protected $settings;
30
31
    /**
32
     * Zibal constructor.
33
     * Construct the class with the relevant settings.
34
     *
35
     * @param Invoice $invoice
36
     * @param $settings
37
     */
38
    public function __construct(Invoice $invoice, $settings)
39
    {
40
        $this->invoice($invoice);
41
        $this->settings = (object) $settings;
42
        $this->client = new Client();
0 ignored issues
show
Bug Best Practice introduced by
The property client does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
43
    }
44
45
    /**
46
     * Purchase Invoice.
47
     *
48
     * @return string
49
     *
50
     * @throws \GuzzleHttp\Exception\GuzzleException
51
     */
52
    public function purchase()
53
    {
54
        $details = $this->invoice->getDetails();
55
56
        $amount = $this->invoice->getAmount() * ($this->settings->currency == 'T' ? 10 : 1); // convert to rial
57
58
        $orderId = crc32($this->invoice->getUuid()).time();
59
        if (!empty($details['orderId'])) {
60
            $orderId = $details['orderId'];
61
        } elseif (!empty($details['order_id'])) {
62
            $orderId = $details['order_id'];
63
        }
64
65
        $curl = curl_init();
66
67
        curl_setopt_array($curl, array(
68
            CURLOPT_URL => $this->settings->apiPurchaseUrl,
69
            CURLOPT_RETURNTRANSFER => true,
70
            CURLOPT_ENCODING => '',
71
            CURLOPT_MAXREDIRS => 10,
72
            CURLOPT_TIMEOUT => 0,
73
            CURLOPT_FOLLOWLOCATION => true,
74
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
75
            CURLOPT_CUSTOMREQUEST => 'POST',
76
            CURLOPT_POSTFIELDS =>'{
77
			"merchant": "'.$this->settings->merchantId.'",
78
			"amount": "'.$amount.'",
79
			"callbackUrl": "'.$this->settings->callbackUrl.'",
80
			"orderId": "'.$orderId.'"
81
		}',
82
            CURLOPT_HTTPHEADER => array(
83
                'Content-Type: application/json'
84
            ),
85
        ));
86
87
        $response = curl_exec($curl);
88
89
        curl_close($curl);
90
        $body = json_decode($response, false);
0 ignored issues
show
Bug introduced by
It seems like $response 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

90
        $body = json_decode(/** @scrutinizer ignore-type */ $response, false);
Loading history...
91
92
        if ($body->result != 100) {
93
            // some error has happened
94
            throw new PurchaseFailedException($body->message);
95
        }
96
97
        $this->invoice->transactionId($body->trackId);
98
99
        // return the transaction's id
100
        return $this->invoice->getTransactionId();
101
    }
102
103
    /**
104
     * Pay the Invoice
105
     *
106
     * @return RedirectionForm
107
     */
108
    public function pay() : RedirectionForm
109
    {
110
        $payUrl = $this->settings->apiPaymentUrl.$this->invoice->getTransactionId();
111
112
        if (strtolower($this->settings->mode) == 'direct') {
113
            $payUrl .= '/direct';
114
        }
115
116
        return $this->redirectWithForm($payUrl);
117
    }
118
119
    /**
120
     * Verify payment
121
     *
122
     * @return mixed|void
123
     *
124
     * @throws InvalidPaymentException
125
     * @throws \GuzzleHttp\Exception\GuzzleException
126
     */
127
    public function verify() : ReceiptInterface
128
    {
129
        $successFlag = Request::input('success');
130
        $status = Request::input('status');
131
        $transactionId = $this->invoice->getTransactionId() ?? Request::input('trackId');
132
133
        if ($successFlag != 1) {
134
            $this->notVerified($this->translateStatus($status), $status);
135
        }
136
137
        //start verfication
138
139
        $curl = curl_init();
140
141
        $postData = json_encode([
142
            "merchant" => $this->settings->merchantId,
143
            "trackId" => $transactionId,
144
        ]);
145
146
        curl_setopt_array($curl, array(
147
            CURLOPT_URL => $this->settings->apiVerificationUrl,
148
            CURLOPT_RETURNTRANSFER => true,
149
            CURLOPT_ENCODING => '',
150
            CURLOPT_MAXREDIRS => 10,
151
            CURLOPT_TIMEOUT => 30,
152
            CURLOPT_FOLLOWLOCATION => true,
153
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
154
            CURLOPT_CUSTOMREQUEST => 'POST',
155
            CURLOPT_POSTFIELDS => $postData,
156
            CURLOPT_HTTPHEADER => [
157
                'Content-Type: application/json',
158
                'Content-Length: ' . strlen($postData),
159
            ],
160
        ));
161
162
        $response = curl_exec($curl);
163
164
        curl_close($curl);
165
        $body = json_decode($response, false);
0 ignored issues
show
Bug introduced by
It seems like $response 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

165
        $body = json_decode(/** @scrutinizer ignore-type */ $response, false);
Loading history...
166
167
        if ($body->result != 100) {
168
            $this->notVerified($body->message, $body->result);
169
        }
170
171
        /*
172
            for more info:
173
            var_dump($body);
174
        */
175
176
        return $this->createReceipt($body->refNumber);
177
    }
178
179
    /**
180
     * Generate the payment's receipt
181
     *
182
     * @param $referenceId
183
     *
184
     * @return Receipt
185
     */
186
    protected function createReceipt($referenceId)
187
    {
188
        $receipt = new Receipt('Zibal', $referenceId);
189
190
        return $receipt;
191
    }
192
193
    private function translateStatus($status)
194
    {
195
        $translations = [
196
            -2 => 'خطای داخلی',
197
            -1 => 'در انتظار پردخت',
198
            2 => 'پرداخت شده - تاییدنشده',
199
            3 => 'تراکنش توسط کاربر لغو شد.',
200
            4 => 'شماره کارت نامعتبر می‌باشد.',
201
            5 => 'موجودی حساب کافی نمی‌باشد.',
202
            6 => 'رمز واردشده اشتباه می‌باشد.',
203
            7 => 'تعداد درخواست‌ها بیش از حد مجاز می‌باشد.',
204
            8 => 'تعداد پرداخت اینترنتی روزانه بیش از حد مجاز می‌باشد.',
205
            9 => 'مبلغ پرداخت اینترنتی روزانه بیش از حد مجاز می‌باشد.',
206
            10 => 'صادرکننده‌ی کارت نامعتبر می‌باشد.',
207
            11 => '‌خطای سوییچ',
208
            12 => 'کارت قابل دسترسی نمی‌باشد.'
209
        ];
210
211
        $unknownError = 'خطای ناشناخته ای رخ داده است.';
212
213
        return array_key_exists($status, $translations) ? $translations[$status] : $unknownError;
214
    }
215
216
    /**
217
     * Trigger an exception
218
     *
219
     * @param $message
220
     * @throws InvalidPaymentException
221
     */
222
    private function notVerified($message, $code = 0)
223
    {
224
        if (empty($message)) {
225
            throw new InvalidPaymentException('خطای ناشناخته ای رخ داده است.', $code);
226
        } else {
227
            throw new InvalidPaymentException($message, $code);
228
        }
229
    }
230
231
    /**
232
     * Retrieve data from details using its name.
233
     *
234
     * @return string
235
     */
236
    private function extractDetails($name)
237
    {
238
        $detail = null;
239
        if (!empty($this->invoice->getDetails()[$name])) {
240
            $detail = $this->invoice->getDetails()[$name];
241
        } elseif (!empty($this->settings->$name)) {
242
            $detail = $this->settings->$name;
243
        }
244
245
        return $detail;
246
    }
247
248
    /**
249
     * Checks optional parameters existence (except orderId) and
250
     * adds them to the given $data array and returns new array
251
     * with optional parameters for api call.
252
     *
253
     * To avoid errors and have a cleaner api call log, `null`
254
     * parameters are not sent.
255
     *
256
     * To add new parameter support in the future, all that
257
     * is needed is to add parameter name to $optionalParameters
258
     * array.
259
     *
260
     * @param $data
261
     *
262
     * @return array
263
     */
264
    private function checkOptionalDetails($data)
265
    {
266
        $optionalParameters = [
267
            'mobile',
268
            'description',
269
            'allowedCards',
270
            'feeMode',
271
            'percentMode',
272
            'multiplexingInfos'
273
        ];
274
275
        foreach ($optionalParameters as $parameter) {
276
            if (!is_null($this->extractDetails($parameter))) {
277
                $parameterArray = array(
278
                    $parameter => $this->extractDetails($parameter)
279
                );
280
                $data = array_merge($data, $parameterArray);
281
            }
282
        }
283
284
        return $data;
285
    }
286
}
287