Issues (52)

src/Drivers/Digipay/Digipay.php (1 issue)

Severity
1
<?php
2
3
4
namespace Shetabit\Multipay\Drivers\Digipay;
5
6
use DateTime;
7
use GuzzleHttp\Client;
8
use GuzzleHttp\RequestOptions;
9
use Shetabit\Multipay\Abstracts\Driver;
10
use Shetabit\Multipay\Contracts\ReceiptInterface;
11
use Shetabit\Multipay\Exceptions\InvalidPaymentException;
12
use Shetabit\Multipay\Exceptions\PurchaseFailedException;
13
use Shetabit\Multipay\Invoice;
14
use Shetabit\Multipay\Receipt;
15
use Shetabit\Multipay\RedirectionForm;
16
use Shetabit\Multipay\Request;
17
18
class Digipay extends Driver
19
{
20
    const VERSION = '2022-02-02';
21
    const OAUTH_URL = '/digipay/api/oauth/token';
22
    const PURCHASE_URL = '/digipay/api/tickets/business';
23
    const VERIFY_URL = '/digipay/api/purchases/verify/';
24
    const REVERSE_URL = '/digipay/api/reverse';
25
    const DELIVER_URL = '/digipay/api/purchases/deliver';
26
    const REFUNDS_CONFIG = '/digipay/api/refunds/config';
27
    const REFUNDS_REQUEST = '/digipay/api/refunds';
28
29
    /**
30
     * Digipay Client.
31
     *
32
     * @var Client
33
     */
34
    protected $client;
35
36
    /**
37
     * Invoice
38
     *
39
     * @var Invoice
40
     */
41
    protected $invoice;
42
43
    /**
44
     * Driver settings
45
     *
46
     * @var object
47
     */
48
    protected $settings;
49
    /**
50
     * Digipay Oauth Token
51
     *
52
     * @var string
53
     */
54
    protected $oauthToken;
55
56
    /**
57
     * Digipay payment url
58
     *
59
     * @var string
60
     */
61
    protected $paymentUrl;
62
63
    /**
64
     * Digipay constructor.
65
     * Construct the class with the relevant settings.
66
     */
67
    public function __construct(Invoice $invoice, $settings)
68
    {
69
        $this->invoice($invoice);
70
        $this->settings = (object) $settings;
71
        $this->client = new Client();
72
        $this->oauthToken = $this->oauth();
73
    }
74
75
    /**
76
     * @throws PurchaseFailedException
77
     */
78
    public function purchase(): string
79
    {
80
        $phone = $this->invoice->getDetail('phone')
81
            ?? $this->invoice->getDetail('cellphone')
82
            ?? $this->invoice->getDetail('mobile');
83
84
        /**
85
         * @see https://docs.mydigipay.com/upg.html#_request_fields_2
86
         */
87
        $data = [
88
            'amount' => $this->invoice->getAmount() * ($this->settings->currency == 'T' ? 10 : 1),
89
            'cellNumber' => $phone,
90
            'providerId' => $this->invoice->getUuid(),
91
            'callbackUrl' => $this->settings->callbackUrl,
92
        ];
93
94
        if (!is_null($basketDetailsDto = $this->invoice->getDetail('basketDetailsDto'))) {
95
            $data['basketDetailsDto'] = $basketDetailsDto;
96
        }
97
98
        if (!is_null($preferredGateway = $this->invoice->getDetail('preferredGateway'))) {
99
            $data['preferredGateway'] = $preferredGateway;
100
        }
101
102
        if (!is_null($splitDetailsList = $this->invoice->getDetail('splitDetailsList'))) {
103
            $data['splitDetailsList'] = $splitDetailsList;
104
        }
105
106
        /**
107
         * @see https://docs.mydigipay.com/upg.html#_query_parameters_2
108
         */
109
        $digipayType = $this->invoice->getDetail('digipayType') ?? 11;
110
111
        $response = $this
112
            ->client
113
            ->request(
114
                'POST',
115
                $this->settings->apiPaymentUrl.self::PURCHASE_URL,
116
                [
117
                    RequestOptions::BODY => json_encode($data),
118
                    RequestOptions::QUERY => ['type' => $digipayType],
119
                    RequestOptions::HEADERS => [
120
                        'Agent' => $this->invoice->getDetail('agent') ?? 'WEB',
121
                        'Content-Type' => 'application/json',
122
                        'Authorization' => 'Bearer '.$this->oauthToken,
123
                        'Digipay-Version' => self::VERSION,
124
                    ],
125
                    RequestOptions::HTTP_ERRORS => false,
126
                ]
127
            );
128
129
        $body = json_decode($response->getBody()->getContents(), true);
130
131
        if ($response->getStatusCode() != 200) {
132
            // error has happened
133
            $message = $body['result']['message'] ?? 'خطا در هنگام درخواست برای پرداخت رخ داده است.';
134
            throw new PurchaseFailedException($message);
135
        }
136
137
        $this->invoice->transactionId($body['ticket']);
138
        $this->setPaymentUrl($body['redirectUrl']);
139
140
        // return the transaction's id
141
        return $this->invoice->getTransactionId();
142
    }
143
144
    public function pay(): RedirectionForm
145
    {
146
        return $this->redirectWithForm($this->getPaymentUrl(), [], 'GET');
147
    }
148
149
    /**
150
     * @throws InvalidPaymentException
151
     */
152
    public function verify(): ReceiptInterface
153
    {
154
        /**
155
         * @see https://docs.mydigipay.com/upg.html#ticket_type
156
         */
157
        $digipayTicketType = Request::input('type');
158
        $tracingId = Request::input('trackingCode');
159
160
        $response = $this->client->request(
161
            'POST',
162
            $this->settings->apiPaymentUrl.self::VERIFY_URL.$tracingId,
163
            [
164
                RequestOptions::QUERY => ['type' => $digipayTicketType],
165
                RequestOptions::HEADERS => [
166
                    'Accept' => 'application/json',
167
                    'Authorization' => 'Bearer '.$this->oauthToken,
168
                ],
169
                RequestOptions::HTTP_ERRORS => false,
170
            ]
171
        );
172
173
        $body = json_decode($response->getBody()->getContents(), true);
174
175
        if ($response->getStatusCode() != 200) {
176
            $message = $body['result']['message'] ?? 'تراکنش تایید نشد';
177
            throw new InvalidPaymentException($message, (int) $response->getStatusCode());
178
        }
179
180
        return (new Receipt('digipay', $body["trackingCode"]))->detail($body);
181
    }
182
183
    /**
184
     * @throws PurchaseFailedException
185
     */
186
    protected function oauth()
187
    {
188
        $response = $this
189
            ->client
190
            ->request(
191
                'POST',
192
                $this->settings->apiPaymentUrl.self::OAUTH_URL,
193
                [
194
                    RequestOptions::HEADERS => [
195
                        'Authorization' => 'Basic '.base64_encode("{$this->settings->client_id}:{$this->settings->client_secret}"),
196
                    ],
197
                    RequestOptions::MULTIPART => [
198
                        [
199
                            'name' => 'username',
200
                            'contents' => $this->settings->username,
201
                        ],
202
                        [
203
                            'name' => 'password',
204
                            'contents' => $this->settings->password,
205
                        ],
206
                        [
207
                            'name' => 'grant_type',
208
                            'contents' => 'password',
209
                        ],
210
                    ],
211
                    RequestOptions::HTTP_ERRORS => false,
212
                ]
213
            );
214
215
        if ($response->getStatusCode() != 200) {
216
            if ($response->getStatusCode() == 401) {
217
                throw new PurchaseFailedException('خطا نام کاربری یا رمز عبور شما اشتباه می‌باشد.');
218
            } else {
219
                throw new PurchaseFailedException('خطا در هنگام احراز هویت.');
220
            }
221
        }
222
223
        $body = json_decode($response->getBody()->getContents(), true);
224
225
        $this->oauthToken = $body['access_token'];
226
227
        return $body['access_token'];
228
    }
229
230
    /**
231
     * @see https://docs.mydigipay.com/upg.html#_purchase_reverse
232
     *
233
     * @throws PurchaseFailedException
234
     */
235
    public function reverse()
236
    {
237
        if (is_null($digipayTicketType = $this->invoice->getDetail('type'))) {
238
            throw new PurchaseFailedException('"type" is required for this method.');
239
        }
240
241
        if (is_null($trackingCode = $this->invoice->getDetail('trackingCode'))) {
242
            throw new PurchaseFailedException('"trackingCode" is required for this method.');
243
        }
244
245
        $data = [
246
            'trackingCode' => $trackingCode,
247
            'providerId' => $this->invoice->getTransactionId() ?? $this->invoice->getDetail('providerId') ?? $this->invoice->getUuid(),
248
        ];
249
250
        $response = $this
251
            ->client
252
            ->request(
253
                'POST',
254
                $this->settings->apiPaymentUrl.self::REVERSE_URL,
255
                [
256
                    RequestOptions::BODY => json_encode($data),
257
                    RequestOptions::QUERY => ['type' => $digipayTicketType],
258
                    RequestOptions::HEADERS => [
259
                        'Content-Type' => 'application/json;charset=UTF-8',
260
                        'Authorization' => 'Bearer '.$this->oauthToken,
261
                    ],
262
                    RequestOptions::HTTP_ERRORS => false,
263
                ]
264
            );
265
266
        $body = json_decode($response->getBody()->getContents(), true);
267
268
        if ($response->getStatusCode() != 200 || (isset($body['result']['code']) && $body['result']['code'] != 0)) {
269
            $message = $body['result']['message'] ?? 'خطا در هنگام درخواست برای برگشت وجه رخ داده است.';
270
            throw new InvalidPaymentException($message, (int) $response->getStatusCode());
271
        }
272
273
        return $body;
274
    }
275
276
    /**
277
     * @see https://docs.mydigipay.com/upg.html#_purchase_delivery
278
     *
279
     * @throws PurchaseFailedException
280
     */
281
    public function deliver()
282
    {
283
        if (empty($type = $this->invoice->getDetail('type'))) {
284
            throw new PurchaseFailedException('"type" is required for this method.');
285
        }
286
287
        if (!in_array($type, [5, 13])) {
288
            throw new PurchaseFailedException('This method is not supported for this type.');
289
        }
290
291
        if (empty($invoiceNumber = $this->invoice->getDetail('invoiceNumber'))) {
292
            throw new PurchaseFailedException('"invoiceNumber" is required for this method.');
293
        }
294
295
        if (empty($deliveryDate = $this->invoice->getDetail('deliveryDate'))) {
296
            throw new PurchaseFailedException('"deliveryDate" is required for this method.');
297
        }
298
299
        if (!DateTime::createFromFormat('Y-m-d', $deliveryDate)) {
300
            throw new PurchaseFailedException('"deliveryDate" must be a valid date with format Y-m-d.');
301
        }
302
303
        if (empty($trackingCode = $this->invoice->getDetail('trackingCode'))) {
304
            throw new PurchaseFailedException('"trackingCode" is required for this method.');
305
        }
306
307
        if (empty($products = $this->invoice->getDetail('products'))) {
308
            throw new PurchaseFailedException('"products" is required for this method.');
309
        }
310
311
        if (!is_array($products)) {
0 ignored issues
show
The condition is_array($products) is always false.
Loading history...
312
            throw new PurchaseFailedException('"products" must be an array.');
313
        }
314
315
        $data = [
316
            'invoiceNumber' => $invoiceNumber,
317
            'deliveryDate' => $deliveryDate,
318
            'trackingCode' => $trackingCode,
319
            'products' => $products,
320
        ];
321
322
        $response = $this
323
            ->client
324
            ->request(
325
                'POST',
326
                $this->settings->apiPaymentUrl.self::DELIVER_URL,
327
                [
328
                    RequestOptions::BODY => json_encode($data),
329
                    RequestOptions::QUERY => ['type' => $type],
330
                    RequestOptions::HEADERS => [
331
                        'Content-Type' => 'application/json;charset=UTF-8',
332
                        'Authorization' => 'Bearer '.$this->oauthToken,
333
                    ],
334
                    RequestOptions::HTTP_ERRORS => false,
335
                ]
336
            );
337
338
        $body = json_decode($response->getBody()->getContents(), true);
339
340
        if ($response->getStatusCode() != 200 || (isset($body['result']['code']) && $body['result']['code'] != 0)) {
341
            $message = $body['result']['message'] ?? 'خطا در هنگام درخواست برای تحویل کالا رخ داده است.';
342
            throw new InvalidPaymentException($message, (int) $response->getStatusCode());
343
        }
344
345
        return $body;
346
    }
347
348
    public function getRefundConfig()
349
    {
350
        $response = $this->client->request(
351
            'POST',
352
            $this->settings->apiPaymentUrl.self::REFUNDS_CONFIG,
353
            [
354
                RequestOptions::HEADERS => [
355
                    'Accept' => 'application/json',
356
                    'Authorization' => 'Bearer '.$this->oauthToken,
357
                ],
358
                RequestOptions::HTTP_ERRORS => false,
359
            ]
360
        );
361
362
        $body = json_decode($response->getBody()->getContents(), true);
363
364
        if ($response->getStatusCode() != 200 || (isset($body['result']['code']) && $body['result']['code'] != 0)) {
365
            $message = $body['result']['message'] ?? 'خطا در هنگام درخواست برای دریافت تنظیمات مرجوعی رخ داده است.';
366
            throw new InvalidPaymentException($message, (int) $response->getStatusCode());
367
        }
368
369
        $certFile = $response['certFile'];
370
371
        return $certFile;
372
    }
373
374
    public function refundTransaction()
375
    {
376
        if (empty($type = $this->invoice->getDetail('type'))) {
377
            throw new PurchaseFailedException('"type" is required for this method.');
378
        }
379
380
        if (empty($providerId = $this->invoice->getDetail('providerId'))) {
381
            throw new PurchaseFailedException('"providerId" is required for this method.');
382
        }
383
384
        if (empty($amount = $this->invoice->getDetail('amount'))) {
385
            throw new PurchaseFailedException('"amount" is required for this method.');
386
        }
387
388
        if (empty($saleTrackingCode = $this->invoice->getDetail('saleTrackingCode'))) {
389
            throw new PurchaseFailedException('"saleTrackingCode" is required for this method.');
390
        }
391
392
        $data = [
393
            'providerId' => $providerId,
394
            'amount' => $amount,
395
            'saleTrackingCode' => $saleTrackingCode,
396
        ];
397
398
        $response = $this
399
            ->client
400
            ->request(
401
                'POST',
402
                $this->settings->apiPaymentUrl.self::REFUNDS_REQUEST,
403
                [
404
                    RequestOptions::BODY => json_encode($data),
405
                    RequestOptions::QUERY => ['type' => $type],
406
                    RequestOptions::HEADERS => [
407
                        'Content-Type' => 'application/json;charset=UTF-8',
408
                        'Authorization' => 'Bearer '.$this->oauthToken,
409
                    ],
410
                    RequestOptions::HTTP_ERRORS => false,
411
                ]
412
            );
413
414
        $body = json_decode($response->getBody()->getContents(), true);
415
416
        if ($response->getStatusCode() != 200 || (isset($body['result']['code']) && $body['result']['code'] != 0)) {
417
            $message = $body['result']['message'] ?? 'خطا در هنگام درخواست مرجوعی تراکنش رخ داده است.';
418
            throw new InvalidPaymentException($message, (int) $response->getStatusCode());
419
        }
420
421
        return $body;
422
    }
423
424
    private function getPaymentUrl(): string
425
    {
426
        return $this->paymentUrl;
427
    }
428
429
    private function setPaymentUrl(string $paymentUrl): void
430
    {
431
        $this->paymentUrl = $paymentUrl;
432
    }
433
}
434