Behpardakht   A
last analyzed

Complexity

Total Complexity 24

Size/Duplication

Total Lines 277
Duplicated Lines 0 %

Importance

Changes 12
Bugs 4 Features 1
Metric Value
eloc 127
c 12
b 4
f 1
dl 0
loc 277
rs 10
wmc 24

9 Methods

Rating   Name   Duplication   Size   Complexity  
A translateStatus() 0 54 2
A __construct() 0 4 1
A pay() 0 13 2
B verify() 0 46 7
A client() 0 9 3
A createReceipt() 0 3 1
A prepareVerificationData() 0 13 1
A purchase() 0 27 4
A preparePurchaseData() 0 21 3
1
<?php
2
3
namespace Shetabit\Multipay\Drivers\Behpardakht;
4
5
use Shetabit\Multipay\Abstracts\Driver;
6
use Shetabit\Multipay\Exceptions\InvalidPaymentException;
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\Request;
12
use Carbon\Carbon;
13
use Shetabit\Multipay\RedirectionForm;
14
use SoapClient;
15
16
class Behpardakht extends Driver
17
{
18
    /**
19
     * Invoice
20
     *
21
     * @var Invoice
22
     */
23
    protected $invoice;
24
25
    /**
26
     * Driver settings
27
     *
28
     * @var object
29
     */
30
    protected $settings;
31
32
    /**
33
     * Behpardakht constructor.
34
     * Construct the class with the relevant settings.
35
     *
36
     * @param Invoice $invoice
37
     * @param $settings
38
     */
39
    public function __construct(Invoice $invoice, $settings)
40
    {
41
        $this->invoice($invoice);
42
        $this->settings = (object)$settings;
43
    }
44
45
    /**
46
     * Purchase Invoice.
47
     *
48
     * @return string
49
     *
50
     * @throws PurchaseFailedException
51
     * @throws \SoapFault
52
     */
53
54
    public function purchase()
55
    {
56
        $soap = $this->client($this->settings->apiPurchaseUrl);
57
58
        $purchaseData = $this->preparePurchaseData();
59
60
        if ($this->settings->cumulativeDynamicPayStatus) {
61
            $response = $soap->bpCumulativeDynamicPayRequest($purchaseData);
62
        } else {
63
            $response = $soap->bpPayRequest($purchaseData);
64
        }
65
66
        // fault has happened in bank gateway
67
        if ($response->return == 21) {
68
            throw new PurchaseFailedException('پذیرنده معتبر نیست.', 21);
69
        }
70
71
        $data = explode(',', $response->return);
72
73
        // purchase was not successful
74
        if ($data[0] != "0") {
75
            throw new PurchaseFailedException($this->translateStatus($data[0]), (int)$data[0]);
76
        }
77
78
        $this->invoice->transactionId($data[1]);
79
80
        return $this->invoice->getTransactionId();
81
    }
82
83
    /**
84
     * Pay the Invoice
85
     *
86
     * @return RedirectionForm
87
     */
88
    public function pay(): RedirectionForm
89
    {
90
        $payUrl = $this->settings->apiPaymentUrl;
91
92
        $data = [
93
            'RefId' => $this->invoice->getTransactionId()
94
        ];
95
96
        if (!empty($this->invoice->getDetails()['mobile'])) {
97
            $data['MobileNo'] = $this->invoice->getDetails()['mobile'];
98
        }
99
100
        return $this->redirectWithForm($payUrl, $data, 'POST');
101
    }
102
103
    /**
104
     * Verify payment
105
     *
106
     * @return mixed|Receipt
107
     *
108
     * @throws InvalidPaymentException
109
     * @throws \SoapFault
110
     */
111
    public function verify(): ReceiptInterface
112
    {
113
        $resCode = Request::input('ResCode');
114
        if ($resCode != '0') {
115
            throw new InvalidPaymentException($this->translateStatus($resCode), $resCode);
116
        }
117
118
        $data = $this->prepareVerificationData();
119
120
        $soap = $this->client($this->settings->apiVerificationUrl);
121
122
        // step1: verify request
123
        $verifyResponse = (int)$soap->bpVerifyRequest($data)->return;
124
        if ($verifyResponse != 0) {
125
            // rollback money and throw exception
126
            // avoid rollback if request was already verified
127
            if ($verifyResponse != 43) {
128
                $soap->bpReversalRequest($data);
129
            }
130
131
            throw new InvalidPaymentException($this->translateStatus($verifyResponse), $verifyResponse);
132
        }
133
134
        // step2: settle request
135
        $settleResponse = $soap->bpSettleRequest($data)->return;
136
        if ($settleResponse != 0) {
137
            // rollback money and throw exception
138
            // avoid rollback if request was already settled/reversed
139
            if ($settleResponse != 45 && $settleResponse != 48) {
140
                $soap->bpReversalRequest($data);
141
            }
142
143
            throw new InvalidPaymentException($this->translateStatus($settleResponse), $settleResponse);
144
        }
145
146
        $receipt = $this->createReceipt($data['saleReferenceId']);
147
148
        $receipt->detail([
149
            "RefId" => Request::input('RefId'),
150
            "SaleOrderId" => Request::input('SaleOrderId'),
151
            "CardHolderPan" => Request::input('CardHolderPan'),
152
            "CardHolderInfo" => Request::input('CardHolderInfo'),
153
            "SaleReferenceId" => Request::input('SaleReferenceId'),
154
        ]);
155
156
        return $receipt;
157
    }
158
159
    /**
160
     * Generate the payment's receipt
161
     *
162
     * @param $referenceId
163
     *
164
     * @return Receipt
165
     */
166
    protected function createReceipt($referenceId)
167
    {
168
        return new Receipt('behpardakht', $referenceId);
169
    }
170
171
    /**
172
     * Prepare data for payment verification
173
     *
174
     * @return array
175
     */
176
    protected function prepareVerificationData()
177
    {
178
        $orderId = Request::input('SaleOrderId');
179
        $verifySaleOrderId = Request::input('SaleOrderId');
180
        $verifySaleReferenceId = Request::input('SaleReferenceId');
181
182
        return array(
183
            'terminalId' => $this->settings->terminalId,
184
            'userName' => $this->settings->username,
185
            'userPassword' => $this->settings->password,
186
            'orderId' => $orderId,
187
            'saleOrderId' => $verifySaleOrderId,
188
            'saleReferenceId' => $verifySaleReferenceId
189
        );
190
    }
191
192
    /**
193
     * Prepare data for purchasing invoice
194
     *
195
     * @return array
196
     */
197
    protected function preparePurchaseData()
198
    {
199
        if (!empty($this->invoice->getDetails()['description'])) {
200
            $description = $this->invoice->getDetails()['description'];
201
        } else {
202
            $description = $this->settings->description;
203
        }
204
205
        $payerId = $this->invoice->getDetails()['payerId'] ?? 0;
206
207
        return array(
208
            'terminalId' => $this->settings->terminalId,
209
            'userName' => $this->settings->username,
210
            'userPassword' => $this->settings->password,
211
            'callBackUrl' => $this->settings->callbackUrl,
212
            'amount' => $this->invoice->getAmount() * ($this->settings->currency == 'T' ? 10 : 1), // convert to rial
213
            'localDate' => Carbon::now()->format('Ymd'),
214
            'localTime' => Carbon::now()->format('Gis'),
215
            'orderId' => crc32($this->invoice->getUuid()),
216
            'additionalData' => $description,
217
            'payerId' => $payerId
218
        );
219
    }
220
221
    private function client(string $url): SoapClient
222
    {
223
        if (isset($_SERVER['SERVER_PROTOCOL']) && $_SERVER['SERVER_PROTOCOL'] == "HTTP/2.0") {
224
            $context = stream_context_create(['ssl' => ['verify_peer' => false, 'verify_peer_name' => false]]);
225
226
            return new SoapClient($url, ['stream_context' => $context]);
227
        }
228
229
        return new SoapClient($url);
230
    }
231
232
    /**
233
     * Convert status to a readable message.
234
     *
235
     * @param $status
236
     *
237
     * @return mixed|string
238
     */
239
    private function translateStatus($status)
240
    {
241
        $translations = [
242
            '0' => 'تراکنش با موفقیت انجام شد',
243
            '11' => 'شماره کارت نامعتبر است',
244
            '12' => 'موجودی کافی نیست',
245
            '13' => 'رمز نادرست است',
246
            '14' => 'تعداد دفعات وارد کردن رمز بیش از حد مجاز است',
247
            '15' => 'کارت نامعتبر است',
248
            '16' => 'دفعات برداشت وجه بیش از حد مجاز است',
249
            '17' => 'کاربر از انجام تراکنش منصرف شده است',
250
            '18' => 'تاریخ انقضای کارت گذشته است',
251
            '19' => 'مبلغ برداشت وجه بیش از حد مجاز است',
252
            '111' => 'صادر کننده کارت نامعتبر است',
253
            '112' => 'خطای سوییچ صادر کننده کارت',
254
            '113' => 'پاسخی از صادر کننده کارت دریافت نشد',
255
            '114' => 'دارنده کارت مجاز به انجام این تراکنش نیست',
256
            '21' => 'پذیرنده نامعتبر است',
257
            '23' => 'خطای امنیتی رخ داده است',
258
            '24' => 'اطلاعات کاربری پذیرنده نامعتبر است',
259
            '25' => 'مبلغ نامعتبر است',
260
            '31' => 'پاسخ نامعتبر است',
261
            '32' => 'فرمت اطلاعات وارد شده صحیح نمی‌باشد',
262
            '33' => 'حساب نامعتبر است',
263
            '34' => 'خطای سیستمی',
264
            '35' => 'تاریخ نامعتبر است',
265
            '41' => 'شماره درخواست تکراری است',
266
            '42' => 'تراکنش Sale یافت نشد',
267
            '43' => 'قبلا درخواست Verify داده شده است',
268
            '44' => 'درخواست Verify یافت نشد',
269
            '45' => 'تراکنش Settle شده است',
270
            '46' => 'تراکنش Settle نشده است',
271
            '47' => 'تراکنش Settle یافت نشد',
272
            '48' => 'تراکنش Reverse شده است',
273
            '412' => 'شناسه قبض نادرست است',
274
            '413' => 'شناسه پرداخت نادرست است',
275
            '414' => 'سازمان صادر کننده قبض نامعتبر است',
276
            '415' => 'زمان جلسه کاری به پایان رسیده است',
277
            '416' => 'خطا در ثبت اطلاعات',
278
            '417' => 'شناسه پرداخت کننده نامعتبر است',
279
            '418' => 'اشکال در تعریف اطلاعات مشتری',
280
            '419' => 'تعداد دفعات ورود اطلاعات از حد مجاز گذشته است',
281
            '421' => 'IP نامعتبر است',
282
            '51' => 'تراکنش تکراری است',
283
            '54' => 'تراکنش مرجع موجود نیست',
284
            '55' => 'تراکنش نامعتبر است',
285
            '61' => 'خطا در واریز',
286
            '62' => 'مسیر بازگشت به سایت در دامنه ثبت شده برای پذیرنده قرار ندارد',
287
            '98' => 'سقف استفاده از رمز ایستا به پایان رسیده است'
288
        ];
289
290
        $unknownError = 'خطای ناشناخته رخ داده است.';
291
292
        return array_key_exists($status, $translations) ? $translations[$status] : $unknownError;
293
    }
294
}
295