Asanpardakht::pay()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 2
eloc 5
nc 2
nop 0
dl 0
loc 12
rs 10
c 2
b 0
f 0
1
<?php
2
3
namespace Shetabit\Multipay\Drivers\Asanpardakht;
4
5
use GuzzleHttp\Client;
6
use Shetabit\Multipay\Abstracts\Driver;
7
use Shetabit\Multipay\Exceptions\PurchaseFailedException;
8
use Shetabit\Multipay\Contracts\ReceiptInterface;
9
use Shetabit\Multipay\Invoice;
10
use Shetabit\Multipay\Receipt;
11
use Shetabit\Multipay\RedirectionForm;
12
13
class Asanpardakht extends Driver
14
{
15
    const TokenURL = 'Token';
16
    const TimeURL = 'Time';
17
    const TranResultURL = 'TranResult';
18
    const CardHashURL = 'CardHash';
19
    const SettlementURL = 'Settlement';
20
    const VerifyURL = 'Verify';
21
    const CancelURL = 'Cancel';
22
    const ReverseURL = 'Reverse';
23
24
    /**
25
     * Invoice
26
     *
27
     * @var Invoice
28
     */
29
    protected $invoice;
30
31
    /**
32
     * Response
33
     *
34
     * @var object
35
     */
36
    protected $response;
37
38
    /**
39
     * PayGateTransactionId
40
     *
41
     */
42
    protected $payGateTransactionId;
43
44
    /**
45
     * Driver settings
46
     *
47
     * @var object
48
     */
49
    protected $settings;
50
51
    /**
52
     * Asanpardakht constructor.
53
     * Construct the class with the relevant settings.
54
     *
55
     * @param Invoice $invoice
56
     * @param $settings
57
     */
58
    public function __construct(Invoice $invoice, $settings)
59
    {
60
        $this->invoice($invoice);
61
        $this->settings = (object)$settings;
62
    }
63
64
    /**
65
     * Purchase Invoice.
66
     *
67
     * @return string
68
     *
69
     * @throws PurchaseFailedException
70
     */
71
    public function purchase()
72
    {
73
        $this->invoice->uuid(crc32($this->invoice->getUuid()));
74
75
        $result = $this->token();
76
77
        if (!isset($result['status_code']) or $result['status_code'] != 200) {
78
            $this->purchaseFailed($result['status_code']);
79
        }
80
81
        $this->invoice->transactionId($result['content']);
82
83
        // return the transaction's id
84
        return $this->invoice->getTransactionId();
85
    }
86
87
    /**
88
     * Pay the Invoice
89
     *
90
     * @return RedirectionForm
91
     */
92
    public function pay(): RedirectionForm
93
    {
94
        $data = [
95
            'RefID' => $this->invoice->getTransactionId()
96
        ];
97
98
        //set mobileap for get user cards
99
        if (!empty($this->invoice->getDetails()['mobile'])) {
100
            $data['mobileap'] = $this->invoice->getDetails()['mobile'];
101
        }
102
103
        return $this->redirectWithForm($this->settings->apiPaymentUrl, $data, 'POST');
104
    }
105
106
    /**
107
     * Verify payment
108
     *
109
     * @return mixed|Receipt
110
     *
111
     * @throws PurchaseFailedException
112
     */
113
    public function verify(): ReceiptInterface
114
    {
115
        $result = $this->transactionResult();
116
117
        if (!isset($result['status_code']) or $result['status_code'] != 200) {
118
            $this->purchaseFailed($result['status_code']);
119
        }
120
121
        $this->payGateTransactionId = $result['content']['payGateTranID'];
122
123
        //step1: verify
124
        $verify_result = $this->verifyTransaction();
125
126
        if (!isset($verify_result['status_code']) or $verify_result['status_code'] != 200) {
127
            $this->purchaseFailed($verify_result['status_code']);
128
        }
129
130
        //step2: settlement
131
        $this->settlement();
132
133
        $receipt = $this->createReceipt($this->payGateTransactionId);
134
        $receipt->detail([
135
            'traceNo' => $this->payGateTransactionId,
136
            'referenceNo' => $result['content']['rrn'],
137
            'transactionId' => $result['content']['refID'],
138
            'cardNo' => $result['content']['cardNumber'],
139
        ]);
140
141
        return $receipt;
142
    }
143
144
    /**
145
     * send request to Asanpardakht
146
     *
147
     * @param $method
148
     * @param $url
149
     * @param array $data
150
     * @return array
151
     */
152
    protected function callApi($method, $url, $data = []): array
153
    {
154
        $client = new Client(['base_uri' => $this->settings->apiRestPaymentUrl]);
155
156
        $response = $client->request($method, $url, [
157
            "json" => $data,
158
            "headers" => [
159
                'Content-Type' => 'application/json',
160
                'usr' => $this->settings->username,
161
                'pwd' => $this->settings->password
162
            ],
163
            "http_errors" => false,
164
        ]);
165
166
        return [
167
            'status_code' => $response->getStatusCode(),
168
            'content' => json_decode($response->getBody()->getContents(), true)
169
        ];
170
    }
171
172
    /**
173
     * Generate the payment's receipt
174
     *
175
     * @param $referenceId
176
     *
177
     * @return Receipt
178
     */
179
    protected function createReceipt($referenceId): Receipt
180
    {
181
        $receipt = new Receipt('asanpardakht', $referenceId);
182
183
        return $receipt;
184
    }
185
186
    /**
187
     * call create token request
188
     *
189
     * @return array
190
     */
191
    public function token(): array
192
    {
193
        if (strpos($this->settings->callbackUrl, '?') !== false) {
194
            $query = '&' . http_build_query(['invoice' => $this->invoice->getUuid()]);
195
        } else {
196
            $query = '?' . http_build_query(['invoice' => $this->invoice->getUuid()]);
197
        }
198
199
        return $this->callApi('POST', self::TokenURL, [
200
            'serviceTypeId' => 1,
201
            'merchantConfigurationId' => $this->settings->merchantConfigID,
202
            'localInvoiceId' => $this->invoice->getUuid(),
203
            'amountInRials' => $this->invoice->getAmount() * ($this->settings->currency == 'T' ? 10 : 1), // convert to rial
204
            'localDate' => $this->getTime()['content'],
205
            'callbackURL' => $this->settings->callbackUrl . $query,
206
            'paymentId' => "0",
207
            'additionalData' => '',
208
        ]);
209
    }
210
211
    /**
212
     * call reserve request
213
     *
214
     * @return array
215
     */
216
    public function reverse(): array
217
    {
218
        return $this->callApi('POST', self::ReverseURL, [
219
            'merchantConfigurationId' => (int)$this->settings->merchantConfigID,
220
            'payGateTranId' => (int)$this->invoice->getUuid()
221
        ]);
222
    }
223
224
    /**
225
     * send cancel request
226
     *
227
     * @return array
228
     */
229
    public function cancel(): array
230
    {
231
        return $this->callApi('POST', self::CancelURL, [
232
            'merchantConfigurationId' => (int)$this->settings->merchantConfigID,
233
            'payGateTranId' => (int)$this->payGateTransactionId
234
        ]);
235
    }
236
237
    /**
238
     * send verify request
239
     *
240
     * @return array
241
     */
242
    public function verifyTransaction(): array
243
    {
244
        return $this->callApi('POST', self::VerifyURL, [
245
            'merchantConfigurationId' => (int)$this->settings->merchantConfigID,
246
            'payGateTranId' => (int)$this->payGateTransactionId
247
        ]);
248
    }
249
250
    /**
251
     * send settlement request
252
     *
253
     * @return array
254
     */
255
    public function settlement(): array
256
    {
257
        return $this->callApi('POST', self::SettlementURL, [
258
            'merchantConfigurationId' => (int)$this->settings->merchantConfigID,
259
            'payGateTranId' => (int)$this->payGateTransactionId
260
        ]);
261
    }
262
263
    /**
264
     * get card hash request
265
     *
266
     * @return array
267
     */
268
    public function cardHash(): array
269
    {
270
        return $this->callApi('GET', self::CardHashURL . '?merchantConfigurationId=' . $this->settings->merchantConfigID . '&localInvoiceId=' . $this->invoice->getTransactionId(), []);
271
    }
272
273
    /**
274
     * get transaction result
275
     *
276
     * @return array
277
     */
278
    public function transactionResult(): array
279
    {
280
        return $this->callApi('GET', self::TranResultURL . '?merchantConfigurationId=' . $this->settings->merchantConfigID . '&localInvoiceId=' . $this->invoice->getTransactionId(), []);
281
    }
282
283
    /**
284
     * get Asanpardakht server time
285
     *
286
     * @return array
287
     */
288
    public function getTime(): array
289
    {
290
        return $this->callApi('GET', self::TimeURL);
291
    }
292
293
    /**
294
     * Trigger an exception
295
     *
296
     * @param $status
297
     *
298
     * @throws PurchaseFailedException
299
     */
300
    protected function purchaseFailed($status)
301
    {
302
        $translations = [
303
            400 => "bad request",
304
            401 => "unauthorized. probably wrong or unsent header(s)",
305
            471 => "identity not trusted to proceed",
306
            472 => "no records found",
307
            473 => "invalid merchant username or password",
308
            474 => "invalid incoming request machine ip. check response body to see your actual public IP address",
309
            475 => "invoice identifier is not a number",
310
            476 => "request amount is not a number",
311
            477 => "request local date length is invalid",
312
            478 => "request local date is not in valid format",
313
            479 => "invalid service type id",
314
            480 => "invalid payer id",
315
            481 => "incorrect settlement description format",
316
            482 => "settlement slices does not match total amount",
317
            483 => "unregistered iban",
318
            484 => "internal error for other reasons",
319
            485 => "invalid local date",
320
            486 => "amount not in range",
321
            487 => "service not found or not available for merchant",
322
            488 => "invalid default callback",
323
            489 => "duplicate local invoice id",
324
            490 => "merchant disabled or misconfigured",
325
            491 => "too many settlement destinations",
326
            492 => "unprocessable request",
327
            493 => "error processing special request for other reasons like business restrictions",
328
            494 => "invalid payment_id for governmental payment",
329
            495 => "invalid referenceId in additionalData",
330
            496 => "invalid json in additionalData",
331
            497 => "invalid payment_id location",
332
            571 => "misconfiguration OR not yet processed",
333
            572 => "misconfiguration OR transaction status undetermined",
334
            573 => "misconfiguraed valid ips for configuration OR unable to request for verification due to an internal error",
335
            574 => "internal error in uthorization",
336
            575 => "no valid ibans found for merchant",
337
            576 => "internal error",
338
            577 => "internal error",
339
            578 => "no default sharing is defined for merchant",
340
            579 => "cant submit ibans with default sharing endpoint",
341
            580 => "error processing special request"
342
        ];
343
344
        if (array_key_exists($status, $translations)) {
345
            throw new PurchaseFailedException($translations[$status]);
346
        } else {
347
            throw new PurchaseFailedException('خطای ناشناخته ای رخ داده است.');
348
        }
349
    }
350
}
351