Passed
Pull Request — master (#231)
by
unknown
03:31 queued 56s
created

Digipay::reverse()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 39
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 5
eloc 23
c 1
b 0
f 1
nc 4
nop 0
dl 0
loc 39
rs 9.2408
1
<?php
2
3
4
namespace Shetabit\Multipay\Drivers\Digipay;
5
6
use DateTime;
7
use GuzzleHttp\Client;
8
use GuzzleHttp\Exception\GuzzleException;
9
use GuzzleHttp\RequestOptions;
10
use Shetabit\Multipay\Abstracts\Driver;
11
use Shetabit\Multipay\Contracts\ReceiptInterface;
12
use Shetabit\Multipay\Exceptions\InvalidPaymentException;
13
use Shetabit\Multipay\Exceptions\PurchaseFailedException;
14
use Shetabit\Multipay\Invoice;
15
use Shetabit\Multipay\Receipt;
16
use Shetabit\Multipay\RedirectionForm;
17
use Shetabit\Multipay\Request;
18
19
class Digipay extends Driver
20
{
21
    const VERSION      = '2022-02-02';
22
    const OAUTH_URL    = '/digipay/api/oauth/token';
23
    const PURCHASE_URL = '/digipay/api/tickets/business';
24
    const VERIFY_URL   = '/digipay/api/purchases/verify/';
25
    const REVERSE_URL  = '/digipay/api/reverse';
26
    const DELIVER_URL  = '/digipay/api/purchases/deliver';
27
28
    /**
29
     * Digipay Client.
30
     *
31
     * @var Client
32
     */
33
    protected $client;
34
35
    /**
36
     * Invoice
37
     *
38
     * @var Invoice
39
     */
40
    protected $invoice;
41
42
    /**
43
     * Driver settings
44
     *
45
     * @var object
46
     */
47
    protected $settings;
48
    /**
49
     * Digipay Oauth Token
50
     *
51
     * @var string
52
     */
53
    protected $oauthToken;
54
55
    /**
56
     * Digipay payment url
57
     *
58
     * @var string
59
     */
60
    protected $paymentUrl;
61
62
    /**
63
     * Digipay constructor.
64
     * Construct the class with the relevant settings.
65
     *
66
     * @param Invoice $invoice
67
     * @param $settings
68
     */
69
    public function __construct(Invoice $invoice, $settings)
70
    {
71
        $this->invoice($invoice);
72
        $this->settings   = (object) $settings;
73
        $this->client     = new Client();
74
        $this->oauthToken = $this->oauth();
75
    }
76
77
    /**
78
     * @throws PurchaseFailedException
79
     */
80
    public function purchase(): string
81
    {
82
        $phone = $this->invoice->getDetail('phone')
83
            ?? $this->invoice->getDetail('cellphone')
84
            ?? $this->invoice->getDetail('mobile');
85
86
        /**
87
         * @see https://docs.mydigipay.com/upg.html#_request_fields_2
88
         */
89
        $data = [
90
            'amount'      => $this->invoice->getAmount() * ($this->settings->currency == 'T' ? 10 : 1),
91
            'cellNumber'  => $phone,
92
            'providerId'  => $this->invoice->getUuid(),
93
            'callbackUrl' => $this->settings->callbackUrl,
94
        ];
95
96
        if (!is_null($basketDetailsDto = $this->invoice->getDetail('basketDetailsDto'))) {
97
            $data['basketDetailsDto'] = $basketDetailsDto;
98
        }
99
100
        if (!is_null($preferredGateway = $this->invoice->getDetail('preferredGateway'))) {
101
            $data['preferredGateway'] = $preferredGateway;
102
        }
103
104
        if (!is_null($splitDetailsList = $this->invoice->getDetail('splitDetailsList'))) {
105
            $data['splitDetailsList'] = $splitDetailsList;
106
        }
107
108
        /**
109
         * @see https://docs.mydigipay.com/upg.html#_query_parameters_2
110
         */
111
        $digipayType = $this->invoice->getDetail('digipayType') ?? 11;
112
113
        $response = $this
114
            ->client
115
            ->request(
116
                'POST',
117
                $this->settings->apiPaymentUrl . self::PURCHASE_URL,
118
                [
119
                    RequestOptions::BODY  => json_encode($data),
120
                    RequestOptions::QUERY  => ['type' => $digipayType],
121
                    RequestOptions::HEADERS   => [
122
                        'Agent' => $this->invoice->getDetail('agent') ?? 'WEB',
123
                        'Content-Type'  => 'application/json',
124
                        'Authorization' => 'Bearer ' . $this->oauthToken,
125
                        'Digipay-Version' => self::VERSION,
126
                    ],
127
                    RequestOptions::HTTP_ERRORS => false,
128
                ]
129
            );
130
131
        $body = json_decode($response->getBody()->getContents(), true);
132
133
        if ($response->getStatusCode() != 200) {
134
            // error has happened
135
            $message = $body['result']['message'] ?? 'خطا در هنگام درخواست برای پرداخت رخ داده است.';
136
            throw new PurchaseFailedException($message);
137
        }
138
139
        $this->invoice->transactionId($body['ticket']);
140
        $this->setPaymentUrl($body['redirectUrl']);
141
142
        // return the transaction's id
143
        return $this->invoice->getTransactionId();
144
    }
145
146
    public function pay(): RedirectionForm
147
    {
148
        return $this->redirectWithForm($this->getPaymentUrl(), [], 'GET');
149
    }
150
151
    /**
152
     * @throws InvalidPaymentException
153
     */
154
    public function verify(): ReceiptInterface
155
    {
156
        /**
157
         * @see https://docs.mydigipay.com/upg.html#ticket_type
158
         */
159
        $digipayTicketType = Request::input('type');
160
        $tracingId = Request::input('trackingCode');
161
162
        $response = $this->client->request(
163
            'POST',
164
            $this->settings->apiPaymentUrl . self::VERIFY_URL . $tracingId,
165
            [
166
                RequestOptions::QUERY      => ['type' => $digipayTicketType],
167
                RequestOptions::HEADERS    => [
168
                    "Accept"        => "application/json",
169
                    "Authorization" => "Bearer " . $this->oauthToken,
170
                ],
171
                RequestOptions::HTTP_ERRORS => false,
172
            ]
173
        );
174
175
        $body = json_decode($response->getBody()->getContents(), true);
176
177
        if ($response->getStatusCode() != 200) {
178
            $message = $body['result']['message'] ?? 'تراکنش تایید نشد';
179
            throw new InvalidPaymentException($message, (int) $response->getStatusCode());
180
        }
181
182
        return (new Receipt('digipay', $body["trackingCode"]))->detail($body);
183
    }
184
185
    /**
186
     * @throws PurchaseFailedException
187
     */
188
    protected function oauth()
189
    {
190
        $response = $this
191
            ->client
192
            ->request(
193
                'POST',
194
                $this->settings->apiPaymentUrl . self::OAUTH_URL,
195
                [
196
                    RequestOptions::HEADERS   => [
197
                        'Authorization' => 'Basic ' . base64_encode("{$this->settings->client_id}:{$this->settings->client_secret}"),
198
                    ],
199
                    RequestOptions::MULTIPART   => [
200
                        [
201
                            "name"     => "username",
202
                            "contents" => $this->settings->username,
203
                        ],
204
                        [
205
                            "name"     => "password",
206
                            "contents" => $this->settings->password,
207
                        ],
208
                        [
209
                            "name"     => "grant_type",
210
                            "contents" => 'password',
211
                        ],
212
                    ],
213
                    RequestOptions::HTTP_ERRORS => false,
214
                ]
215
            );
216
217
        if ($response->getStatusCode() != 200) {
218
            if ($response->getStatusCode() == 401) {
219
                throw new PurchaseFailedException("خطا نام کاربری یا رمز عبور شما اشتباه می باشد.");
220
            } else {
221
                throw new PurchaseFailedException("خطا در هنگام احراز هویت.");
222
            }
223
        }
224
225
        $body = json_decode($response->getBody()->getContents(), true);
226
227
        $this->oauthToken = $body['access_token'];
228
        return $body['access_token'];
229
    }
230
231
    /**
232
     * @see https://docs.mydigipay.com/upg.html#_purchase_reverse
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 || $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
     * @throws PurchaseFailedException
279
     */
280
    public function deliver()
281
    {
282
        $details = ['type', 'invoiceNumber', 'deliveryDate', 'trackingCode', 'products'];
283
        foreach ($details as $detail) {
284
            if (empty($value = $this->invoice->getDetail($detail))) {
285
                throw new PurchaseFailedException("\"$detail\" is required for this method.");
286
            }
287
            // define variable with the same name as the value of $detail
288
            $$detail = $value;
289
290
            if ($detail != 'type') {
291
                $data[$detail] = $value;
292
            }
293
        }
294
295
        /**
296
         * This api call only need when has a type [5,13] in pay response
297
         */
298
        if (!in_array($type, [5,13])) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $type seems to be never defined.
Loading history...
299
            throw new PurchaseFailedException('This method is not supported for this type.');
300
        }
301
302
        if (! is_array($products)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $products seems to be never defined.
Loading history...
303
            throw new PurchaseFailedException('products must be an array.');
304
        }
305
306
        if (! DateTime::createFromFormat('Y-m-d', $deliveryDate)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $deliveryDate seems to be never defined.
Loading history...
307
            throw new PurchaseFailedException('deliveryDate must be a valid date with format Y-m-d.');
308
        }
309
310
        $response = $this
311
            ->client
312
            ->request(
313
                'POST',
314
                $this->settings->apiPaymentUrl . self::DELIVER_URL,
315
                [
316
                    RequestOptions::BODY        => json_encode($data),
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $data does not seem to be defined for all execution paths leading up to this point.
Loading history...
317
                    RequestOptions::QUERY       => ['type' => $type],
318
                    RequestOptions::HEADERS     => [
319
                        'Content-Type'    => 'application/json;charset=UTF-8',
320
                        'Authorization'   => 'Bearer ' . $this->oauthToken,
321
                    ],
322
                    RequestOptions::HTTP_ERRORS => false,
323
                ]
324
            );
325
326
        $body = json_decode($response->getBody()->getContents(), true);
327
328
        if ($response->getStatusCode() != 200 || $body['result']['code'] != 0) {
329
            $message = $body['result']['message'] ?? 'خطا در هنگام درخواست برای تحویل کالا رخ داده است.';
330
            throw new InvalidPaymentException($message, (int) $response->getStatusCode());
331
        }
332
333
        return $body;
334
    }
335
336
    /**
337
     * @return string
338
     */
339
    public function getPaymentUrl(): string
340
    {
341
        return $this->paymentUrl;
342
    }
343
344
    /**
345
     * @param string $paymentUrl
346
     */
347
    public function setPaymentUrl(string $paymentUrl): void
348
    {
349
        $this->paymentUrl = $paymentUrl;
350
    }
351
}
352