Passed
Pull Request — master (#246)
by
unknown
07:27 queued 04:39
created

SnappPay::status()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 1
eloc 0
c 1
b 0
f 1
nc 1
nop 0
dl 0
loc 2
rs 10
1
<?php
2
3
4
namespace Shetabit\Multipay\Drivers\SnappPay;
5
6
use GuzzleHttp\Client;
7
use GuzzleHttp\RequestOptions;
8
use Shetabit\Multipay\Abstracts\Driver;
9
use Shetabit\Multipay\Contracts\ReceiptInterface;
10
use Shetabit\Multipay\Exceptions\InvalidPaymentException;
11
use Shetabit\Multipay\Exceptions\PurchaseFailedException;
12
use Shetabit\Multipay\Invoice;
13
use Shetabit\Multipay\RedirectionForm;
14
15
class SnappPay extends Driver
16
{
17
    const VERSION = '1.8';
18
    const RELEASE_DATE = '2023-01-08';
19
20
    const OAUTH_URL = '/api/online/v1/oauth/token';
21
    const ELIGIBLE_URL = '/api/online/offer/v1/eligible';
22
    const TOKEN_URL = '/api/online/payment/v1/token';
23
    const VERIFY_URL = '/api/online/payment/v1/verify';
24
    const SETTLE_URL = '/api/online/payment/v1/settle';
25
    const REVERT_URL = '/api/online/payment/v1/revert';
26
    const STATUS_URL = '/api/online/payment/v1/status';
27
    const CANCEL_URL = '/api/online/payment/v1/cancel';
28
    const UPDATE_URL = '/api/online/payment/v1/update';
29
30
    /**
31
     * SnappPay Client.
32
     *
33
     * @var Client
34
     */
35
    protected $client;
36
37
    /**
38
     * Invoice
39
     *
40
     * @var Invoice
41
     */
42
    protected $invoice;
43
44
    /**
45
     * Driver settings
46
     *
47
     * @var object
48
     */
49
    protected $settings;
50
    /**
51
     * SnappPay Oauth Data
52
     *
53
     * @var string
54
     */
55
    protected $oauthToken;
56
57
    /**
58
     * SnappPay payment url
59
     *
60
     * @var string
61
     */
62
    protected $paymentUrl;
63
64
    /**
65
     * SnappPay constructor.
66
     * Construct the class with the relevant settings.
67
     */
68
    public function __construct(Invoice $invoice, $settings)
69
    {
70
        $this->invoice($invoice);
71
        $this->settings = (object) $settings;
72
        $this->client = new Client();
73
        $this->oauthToken = $this->oauth();
74
    }
75
76
    /**
77
     * @throws PurchaseFailedException
78
     */
79
    public function purchase(): string
80
    {
81
        $phone = $this->invoice->getDetail('phone')
82
            ?? $this->invoice->getDetail('cellphone')
83
            ?? $this->invoice->getDetail('mobile');
84
85
        $data = [
86
            'amount' => $this->invoice->getAmount() * ($this->settings->currency == 'T' ? 10 : 1),
87
            'mobile' => $phone,
88
            'paymentMethodTypeDto' => 'INSTALLMENT',
89
            'transactionId' => $this->invoice->getUuid(),
90
            'returnURL' => $this->settings->callbackUrl,
91
        ];
92
93
        if (!is_null($discountAmount = $this->invoice->getDetail('discountAmount'))) {
94
            $data['discountAmount'] = $discountAmount;
95
        }
96
97
        if (!is_null($externalSourceAmount = $this->invoice->getDetail('externalSourceAmount'))) {
98
            $data['externalSourceAmount'] = $externalSourceAmount;
99
        }
100
101
        if (!is_null($cartList = $this->invoice->getDetail('cartList'))) {
102
            $data['cartList'] = $cartList;
103
        }
104
105
        $response = $this
106
            ->client
107
            ->post(
108
                $this->settings->apiPaymentUrl.self::TOKEN_URL,
109
                [
110
                    RequestOptions::FORM_PARAMS => $data,
111
                    RequestOptions::HEADERS => [
112
                        'Content-Type' => 'application/json',
113
                        'Authorization' => 'Bearer '.$this->oauthToken,
114
                    ],
115
                    RequestOptions::HTTP_ERRORS => false,
116
                ]
117
            );
118
119
        $body = json_decode($response->getBody()->getContents(), true);
120
121
        if ($response->getStatusCode() != 200 || $body['successful'] == false) {
122
            // error has happened
123
            $message = 'خطا در هنگام درخواست برای پرداخت رخ داده است.';
124
            throw new PurchaseFailedException($message);
125
        }
126
127
        $this->invoice->transactionId($body['response']['paymentToken']);
128
        $this->setPaymentUrl($body['response']['paymentPageUrl']);
129
130
        // return the transaction's id
131
        return $this->invoice->getTransactionId();
132
    }
133
134
    public function pay(): RedirectionForm
135
    {
136
        return $this->redirectWithForm($this->getPaymentUrl(), [], 'GET');
137
    }
138
139
    /**
140
     * @throws InvalidPaymentException
141
     */
142
    public function verify(): ReceiptInterface
143
    {
144
        $paymentToken = $this->invoice->getTransactionId();
145
146
        $response = $this
0 ignored issues
show
Unused Code introduced by
The assignment to $response is dead and can be removed.
Loading history...
147
            ->client
148
            ->post(
149
                $this->settings->apiPaymentUrl.self::TOKEN_URL,
150
                [
151
                    RequestOptions::BODY => [
152
                        'paymentToken' => $paymentToken,
153
                    ],
154
                    RequestOptions::HEADERS => [
155
                        'Authorization' => 'Bearer '.$this->oauthToken,
156
                    ],
157
                    RequestOptions::HTTP_ERRORS => false,
158
                ]
159
            );
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return Shetabit\Multipay\Contracts\ReceiptInterface. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
160
    }
161
162
    /**
163
     * @throws PurchaseFailedException
164
     */
165
    protected function oauth()
166
    {
167
        $response = $this
168
            ->client
169
            ->post(
170
                $this->settings->apiPaymentUrl.self::OAUTH_URL,
171
                [
172
                    RequestOptions::HEADERS => [
173
                        'Authorization' => 'Basic '.base64_encode("{$this->settings->client_id}:{$this->settings->client_secret}"),
174
                    ],
175
                    RequestOptions::FORM_PARAMS => [
176
                        'grant_type' => 'password',
177
                        'scope' => 'online-merchant',
178
                        'username' => $this->settings->username,
179
                        'password' => $this->settings->password,
180
                    ],
181
                    RequestOptions::HTTP_ERRORS => false,
182
                ]
183
            );
184
185
        if ($response->getStatusCode() != 200) {
186
            throw new PurchaseFailedException('خطا در هنگام احراز هویت.');
187
        }
188
189
        $body = json_decode($response->getBody()->getContents(), true);
190
191
        return $body['access_token'];
192
    }
193
194
    /**
195
     * @throws PurchaseFailedException
196
     */
197
    public function eligible()
198
    {
199
        if (is_null($amount = $this->invoice->getDetail('amount'))) {
200
            throw new PurchaseFailedException('"amount" is required for this method.');
201
        }
202
203
        $response = $this->client->get(self::ELIGIBLE_URL, [
204
            RequestOptions::HEADERS => [
205
                'Authorization' => 'Bearer '.$this->oauthToken,
206
            ],
207
            RequestOptions::QUERY => [
208
                'amount' => $amount,
209
            ],
210
        ]);
211
212
        $body = json_decode($response->getBody()->getContents(), true);
213
214
        if ($response->getStatusCode() != 200) {
215
            throw new InvalidPaymentException('', (int) $response->getStatusCode());
216
        }
217
218
        return $body;
219
    }
220
221
    public function settle()
222
    {
223
    }
224
225
    public function revert()
226
    {
227
    }
228
229
    public function status()
230
    {
231
    }
232
233
    public function cancel()
234
    {
235
    }
236
237
    public function update()
238
    {
239
    }
240
241
    public function getPaymentUrl(): string
242
    {
243
        return $this->paymentUrl;
244
    }
245
246
    public function setPaymentUrl(string $paymentUrl): void
247
    {
248
        $this->paymentUrl = $paymentUrl;
249
    }
250
}
251