Pasargad   A
last analyzed

Complexity

Total Complexity 17

Size/Duplication

Total Lines 251
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 83
c 2
b 0
f 0
dl 0
loc 251
rs 10
wmc 17

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A pay() 0 8 1
A purchase() 0 8 1
A request() 0 25 2
A prepareSignature() 0 3 1
A getDefaultExceptionMessage() 0 3 1
A getPreparedInvoiceData() 0 7 2
A verify() 0 25 3
A prepareInvoiceData() 0 26 3
A createReceipt() 0 14 1
A sign() 0 8 1
1
<?php
2
3
namespace Shetabit\Multipay\Drivers\Pasargad;
4
5
use GuzzleHttp\Client;
6
use Shetabit\Multipay\Invoice;
7
use Shetabit\Multipay\Receipt;
8
use Shetabit\Multipay\Abstracts\Driver;
9
use Shetabit\Multipay\Contracts\ReceiptInterface;
10
use Shetabit\Multipay\Exceptions\InvalidPaymentException;
11
use Shetabit\Multipay\Drivers\Pasargad\Utils\RSAProcessor;
12
use Shetabit\Multipay\RedirectionForm;
13
use Shetabit\Multipay\Request;
14
use DateTimeZone;
15
use DateTime;
16
17
class Pasargad extends Driver
18
{
19
    /**
20
     * Guzzle client
21
     *
22
     * @var object
23
     */
24
    protected $client;
25
26
    /**
27
     * Invoice
28
     *
29
     * @var Invoice
30
     */
31
    protected $invoice;
32
33
    /**
34
     * Driver settings
35
     *
36
     * @var object
37
     */
38
    protected $settings;
39
40
    /**
41
     * Prepared invoice's data
42
     *
43
     * @var array
44
     */
45
    protected $preparedData = array();
46
47
    /**
48
     * Pasargad(PEP) constructor.
49
     * Construct the class with the relevant settings.
50
     *
51
     * @param Invoice $invoice
52
     * @param $settings
53
     */
54
    public function __construct(Invoice $invoice, $settings)
55
    {
56
        $this->invoice($invoice);
57
        $this->settings = (object) $settings;
58
        $this->client = new Client();
59
    }
60
61
    /**
62
     * Purchase Invoice.
63
     *
64
     * @return string
65
     */
66
    public function purchase()
67
    {
68
        $invoiceData = $this->getPreparedInvoiceData();
69
70
        $this->invoice->transactionId($invoiceData['InvoiceNumber']);
71
72
        // return the transaction's id
73
        return $this->invoice->getTransactionId();
74
    }
75
76
    /**
77
     * Pay the Invoice
78
     *
79
     * @return RedirectionForm
80
     */
81
    public function pay() : RedirectionForm
82
    {
83
        $paymentUrl = $this->settings->apiPaymentUrl;
84
        $getTokenUrl = $this->settings->apiGetToken;
85
        $tokenData = $this->request($getTokenUrl, $this->getPreparedInvoiceData());
86
87
        // redirect using HTML form
88
        return $this->redirectWithForm($paymentUrl, $tokenData, 'POST');
89
    }
90
91
    /**
92
     * Verify payment
93
     *
94
     * @return ReceiptInterface
95
     *
96
     * @throws InvalidPaymentException
97
     * @throws \GuzzleHttp\Exception\GuzzleException
98
     */
99
    public function verify() : ReceiptInterface
100
    {
101
        $invoiceDetails = $this->request(
102
            $this->settings->apiCheckTransactionUrl,
103
            [
104
                'TransactionReferenceID' => Request::input('tref')
105
            ]
106
        );
107
        $amount = $this->invoice->getAmount() * ($this->settings->currency == 'T' ? 10 : 1); // convert to rial
108
        if ($amount != $invoiceDetails['Amount']) {
109
            throw new InvalidPaymentException('Invalid amount');
110
        }
111
        $iranTime = new DateTime('now', new DateTimeZone('Asia/Tehran'));
112
        $fields = [
113
            'MerchantCode' => $invoiceDetails['MerchantCode'],
114
            'TerminalCode' => $invoiceDetails['TerminalCode'],
115
            'InvoiceNumber' => $invoiceDetails['InvoiceNumber'],
116
            'InvoiceDate' => $invoiceDetails['InvoiceDate'],
117
            'Amount' => $invoiceDetails['Amount'],
118
            'Timestamp' => $iranTime->format("Y/m/d H:i:s"),
119
        ];
120
121
        $verifyResult = $this->request($this->settings->apiVerificationUrl, $fields);
122
123
        return $this->createReceipt($verifyResult, $invoiceDetails);
124
    }
125
126
    /**
127
     * Generate the payment's receipt
128
     *
129
     * @param $referenceId
130
     *
131
     * @return Receipt
132
     */
133
    protected function createReceipt($verifyResult, $invoiceDetails)
134
    {
135
        $referenceId = $invoiceDetails['TransactionReferenceID'];
136
        $traceNumber = $invoiceDetails['TraceNumber'];
137
        $referenceNumber = $invoiceDetails['ReferenceNumber'];
138
139
        $reciept = new Receipt('Pasargad', $referenceId);
140
141
        $reciept->detail('TraceNumber', $traceNumber);
142
        $reciept->detail('ReferenceNumber', $referenceNumber);
143
        $reciept->detail('MaskedCardNumber', $verifyResult['MaskedCardNumber']);
144
        $reciept->detail('ShaparakRefNumber', $verifyResult['ShaparakRefNumber']);
145
146
        return $reciept;
147
    }
148
149
    /**
150
     * A default message for exceptions
151
     *
152
     * @return string
153
     */
154
    protected function getDefaultExceptionMessage()
155
    {
156
        return 'مشکلی در دریافت اطلاعات از بانک به وجود آمده است';
157
    }
158
159
    /**
160
     * Sign given data.
161
     *
162
     * @param string $data
163
     *
164
     * @return string
165
     */
166
    public function sign($data)
167
    {
168
        $certificate = $this->settings->certificate;
169
        $certificateType = $this->settings->certificateType;
170
171
        $processor = new RSAProcessor($certificate, $certificateType);
172
173
        return $processor->sign($data);
174
    }
175
176
    /**
177
     * Retrieve prepared invoice's data
178
     *
179
     * @return array
180
     */
181
    protected function getPreparedInvoiceData()
182
    {
183
        if (empty($this->preparedData)) {
184
            $this->preparedData = $this->prepareInvoiceData();
185
        }
186
187
        return $this->preparedData;
188
    }
189
190
    /**
191
     * Prepare invoice data
192
     *
193
     * @return array
194
     */
195
    protected function prepareInvoiceData(): array
196
    {
197
        $action = 1003; // 1003 : for buy request (bank standard)
198
        $merchantCode = $this->settings->merchantId;
199
        $terminalCode = $this->settings->terminalCode;
200
        $amount = $this->invoice->getAmount() * ($this->settings->currency == 'T' ? 10 : 1); // convert to rial
201
        $redirectAddress = $this->settings->callbackUrl;
202
        $invoiceNumber = crc32($this->invoice->getUuid()) . rand(0, time());
203
204
        $iranTime = new DateTime('now', new DateTimeZone('Asia/Tehran'));
205
        $timeStamp = $iranTime->format("Y/m/d H:i:s");
206
        $invoiceDate = $iranTime->format("Y/m/d H:i:s");
207
208
        if (!empty($this->invoice->getDetails()['date'])) {
209
            $invoiceDate = $this->invoice->getDetails()['date'];
210
        }
211
212
        return [
213
            'InvoiceNumber' => $invoiceNumber,
214
            'InvoiceDate' => $invoiceDate,
215
            'Amount' => $amount,
216
            'TerminalCode' => $terminalCode,
217
            'MerchantCode' => $merchantCode,
218
            'RedirectAddress' => $redirectAddress,
219
            'Timestamp' => $timeStamp,
220
            'Action' => $action,
221
        ];
222
    }
223
224
    /**
225
     * Prepare signature based on Pasargad document
226
     *
227
     * @param string $data
228
     * @return string
229
     */
230
    public function prepareSignature(string $data): string
231
    {
232
        return base64_encode($this->sign(sha1($data, true)));
233
    }
234
235
    /**
236
     * Make request to pasargad's Api
237
     *
238
     * @param string $url
239
     * @param array $body
240
     * @param string $method
241
     * @return array
242
     */
243
    protected function request(string $url, array $body, $method = 'POST'): array
0 ignored issues
show
Unused Code introduced by
The parameter $method is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

243
    protected function request(string $url, array $body, /** @scrutinizer ignore-unused */ $method = 'POST'): array

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
244
    {
245
        $body = json_encode($body);
246
        $sign = $this->prepareSignature($body);
247
248
        $response = $this->client->request(
249
            'POST',
250
            $url,
251
            [
252
                'body' => $body,
253
                'headers' => [
254
                    'content-type' => 'application/json',
255
                    'Sign' => $sign
256
                ],
257
                "http_errors" => false,
258
            ]
259
        );
260
261
        $result = json_decode($response->getBody(), true);
262
263
        if ($result['IsSuccess'] === false) {
264
            throw new InvalidPaymentException($result['Message']);
265
        }
266
267
        return $result;
268
    }
269
}
270