1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Shetabit\Multipay\Drivers\Sadad; |
4
|
|
|
|
5
|
|
|
use GuzzleHttp\Client; |
6
|
|
|
use Shetabit\Multipay\Abstracts\Driver; |
7
|
|
|
use Shetabit\Multipay\Exceptions\InvalidPaymentException; |
8
|
|
|
use Shetabit\Multipay\Exceptions\PurchaseFailedException; |
9
|
|
|
use Shetabit\Multipay\Contracts\ReceiptInterface; |
10
|
|
|
use Shetabit\Multipay\Invoice; |
11
|
|
|
use Shetabit\Multipay\Receipt; |
12
|
|
|
use Shetabit\Multipay\RedirectionForm; |
13
|
|
|
use Shetabit\Multipay\Request; |
14
|
|
|
use DateTimeZone; |
15
|
|
|
use DateTime; |
16
|
|
|
|
17
|
|
|
class Sadad extends Driver |
18
|
|
|
{ |
19
|
|
|
/** |
20
|
|
|
* Sadad 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
|
|
|
* Sadad constructor. |
42
|
|
|
* Construct the class with the relevant settings. |
43
|
|
|
* |
44
|
|
|
* @param Invoice $invoice |
45
|
|
|
* @param $settings |
46
|
|
|
*/ |
47
|
|
|
public function __construct(Invoice $invoice, $settings) |
48
|
|
|
{ |
49
|
|
|
$this->invoice($invoice); |
50
|
|
|
$this->settings = (object) $settings; |
51
|
|
|
$this->client = new Client(); |
52
|
|
|
} |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* Purchase Invoice. |
56
|
|
|
* |
57
|
|
|
* @return string |
58
|
|
|
* |
59
|
|
|
* @throws PurchaseFailedException |
60
|
|
|
* @throws \GuzzleHttp\Exception\GuzzleException |
61
|
|
|
*/ |
62
|
|
|
public function purchase() |
63
|
|
|
{ |
64
|
|
|
$terminalId = $this->settings->terminalId; |
65
|
|
|
$orderId = crc32($this->invoice->getUuid()); |
66
|
|
|
$amount = $this->invoice->getAmount() * ($this->settings->currency == 'T' ? 10 : 1); // convert to rial |
67
|
|
|
$key = $this->settings->key; |
68
|
|
|
|
69
|
|
|
$signData = $this->encrypt_pkcs7("$terminalId;$orderId;$amount", $key); |
70
|
|
|
$iranTime = new DateTime('now', new DateTimeZone('Asia/Tehran')); |
71
|
|
|
|
72
|
|
|
//set Description for payment |
73
|
|
|
if (!empty($this->invoice->getDetails()['description'])) { |
74
|
|
|
$description = $this->invoice->getDetails()['description']; |
75
|
|
|
} else { |
76
|
|
|
$description = $this->settings->description; |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
//set MobileNo for get user cards |
80
|
|
|
if (!empty($this->invoice->getDetails()['mobile'])) { |
81
|
|
|
$mobile = $this->invoice->getDetails()['mobile']; |
82
|
|
|
} else { |
83
|
|
|
$mobile = ""; |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
$data = [ |
87
|
|
|
'MerchantId' => $this->settings->merchantId, |
88
|
|
|
'ReturnUrl' => $this->settings->callbackUrl, |
89
|
|
|
'LocalDateTime' => $iranTime->format("m/d/Y g:i:s a"), |
90
|
|
|
'SignData' => $signData, |
91
|
|
|
'TerminalId' => $terminalId, |
92
|
|
|
'Amount' => $amount, |
93
|
|
|
'OrderId' => $orderId, |
94
|
|
|
'additionalData' => $description, |
95
|
|
|
'UserId' => $mobile, |
96
|
|
|
]; |
97
|
|
|
|
98
|
|
|
$mode = $this->getMode(); |
99
|
|
|
|
100
|
|
|
if ($mode == 'paymentbyidentity') { |
101
|
|
|
//set PaymentIdentity for payment |
102
|
|
|
if (!empty($this->invoice->getDetails()['payment_identity'])) { |
103
|
|
|
$data['PaymentIdentity'] = $this->invoice->getDetails()['payment_identity']; |
104
|
|
|
} else { |
105
|
|
|
$data['PaymentIdentity'] = $this->settings->PaymentIdentity; |
106
|
|
|
} |
107
|
|
|
} elseif ($mode == 'paymentbymultiidentity') { |
108
|
|
|
//set MultiIdentityData for payment |
109
|
|
|
if (!empty($this->invoice->getDetails()['multi_identity_rows'])) { |
110
|
|
|
$multiIdentityRows = $this->invoice->getDetails()['multi_identity_rows']; |
111
|
|
|
} else { |
112
|
|
|
$multiIdentityRows = $this->settings->MultiIdentityRows; |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
// convert to rial |
116
|
|
|
if ($this->settings->currency == 'T') { |
117
|
|
|
$multiIdentityRows = array_map(function ($item) { |
118
|
|
|
$item['Amount'] = $item['Amount'] * 10; |
119
|
|
|
return $item; |
120
|
|
|
}, $multiIdentityRows); |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
$data['MultiIdentityData'] = ['MultiIdentityRows' => $multiIdentityRows]; |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
$response = $this |
127
|
|
|
->client |
128
|
|
|
->request( |
129
|
|
|
'POST', |
130
|
|
|
$this->getPaymentUrl(), |
131
|
|
|
[ |
132
|
|
|
"json" => $data, |
133
|
|
|
"headers" => [ |
134
|
|
|
'Content-Type' => 'application/json', |
135
|
|
|
'User-Agent' => '', |
136
|
|
|
], |
137
|
|
|
"http_errors" => false, |
138
|
|
|
] |
139
|
|
|
); |
140
|
|
|
|
141
|
|
|
$body = @json_decode($response->getBody()->getContents()); |
142
|
|
|
|
143
|
|
|
if (empty($body)) { |
144
|
|
|
throw new PurchaseFailedException('دسترسی به صفحه مورد نظر امکان پذیر نمی باشد.'); |
145
|
|
|
} elseif ($body->ResCode != 0) { |
146
|
|
|
throw new PurchaseFailedException($body->Description); |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
$this->invoice->transactionId($body->Token); |
150
|
|
|
|
151
|
|
|
// return the transaction's id |
152
|
|
|
return $this->invoice->getTransactionId(); |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
/** |
156
|
|
|
* Pay the Invoice |
157
|
|
|
* |
158
|
|
|
* @return RedirectionForm |
159
|
|
|
*/ |
160
|
|
|
public function pay() : RedirectionForm |
161
|
|
|
{ |
162
|
|
|
$token = $this->invoice->getTransactionId(); |
163
|
|
|
$payUrl = $this->getPurchaseUrl(); |
164
|
|
|
|
165
|
|
|
return $this->redirectWithForm($payUrl, ['Token' => $token], 'GET'); |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
/** |
169
|
|
|
* Verify payment |
170
|
|
|
* |
171
|
|
|
* @return ReceiptInterface |
172
|
|
|
* |
173
|
|
|
* @throws InvalidPaymentException |
174
|
|
|
* @throws \GuzzleHttp\Exception\GuzzleException |
175
|
|
|
*/ |
176
|
|
|
public function verify() : ReceiptInterface |
177
|
|
|
{ |
178
|
|
|
$key = $this->settings->key; |
179
|
|
|
$token = $this->invoice->getTransactionId() ?? Request::input('token'); |
180
|
|
|
$resCode = Request::input('ResCode'); |
181
|
|
|
|
182
|
|
|
if ($resCode != 0) { |
|
|
|
|
183
|
|
|
throw new InvalidPaymentException($this->translateStatus($resCode), $resCode); |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
$data = array( |
187
|
|
|
'Token' => $token, |
188
|
|
|
'SignData' => $this->encrypt_pkcs7($token, $key) |
189
|
|
|
); |
190
|
|
|
|
191
|
|
|
$response = $this |
192
|
|
|
->client |
193
|
|
|
->request( |
194
|
|
|
'POST', |
195
|
|
|
$this->settings->apiVerificationUrl, |
196
|
|
|
[ |
197
|
|
|
"json" => $data, |
198
|
|
|
"headers" => [ |
199
|
|
|
'Content-Type' => 'application/json', |
200
|
|
|
'User-Agent' => '', |
201
|
|
|
], |
202
|
|
|
"http_errors" => false, |
203
|
|
|
] |
204
|
|
|
); |
205
|
|
|
|
206
|
|
|
$body = json_decode($response->getBody()->getContents()); |
207
|
|
|
|
208
|
|
|
$bodyResponse = $body->ResCode; |
209
|
|
|
if ($bodyResponse != 0) { |
210
|
|
|
throw new InvalidPaymentException($this->translateStatus($bodyResponse), $bodyResponse); |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
/** |
214
|
|
|
* شماره سفارش : $orderId = Request::input('OrderId') |
215
|
|
|
* شماره پیگیری : $body->SystemTraceNo |
216
|
|
|
* شماره مرجع : $body->RetrievalRefNo |
217
|
|
|
*/ |
218
|
|
|
|
219
|
|
|
$receipt = $this->createReceipt($body->SystemTraceNo); |
220
|
|
|
$receipt->detail([ |
221
|
|
|
'orderId' => $body->OrderId, |
222
|
|
|
'traceNo' => $body->SystemTraceNo, |
223
|
|
|
'referenceNo' => $body->RetrivalRefNo, |
224
|
|
|
'description' => $body->Description, |
225
|
|
|
]); |
226
|
|
|
|
227
|
|
|
return $receipt; |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
/** |
231
|
|
|
* Generate the payment's receipt |
232
|
|
|
* |
233
|
|
|
* @param $referenceId |
234
|
|
|
* |
235
|
|
|
* @return Receipt |
236
|
|
|
*/ |
237
|
|
|
protected function createReceipt($referenceId) |
238
|
|
|
{ |
239
|
|
|
$receipt = new Receipt('sadad', $referenceId); |
240
|
|
|
|
241
|
|
|
return $receipt; |
242
|
|
|
} |
243
|
|
|
|
244
|
|
|
/** |
245
|
|
|
* Create sign data(Tripledes(ECB,PKCS7)) |
246
|
|
|
* |
247
|
|
|
* @param $str |
248
|
|
|
* @param $key |
249
|
|
|
* |
250
|
|
|
* @return string |
251
|
|
|
*/ |
252
|
|
|
protected function encrypt_pkcs7($str, $key) |
253
|
|
|
{ |
254
|
|
|
$key = base64_decode($key); |
255
|
|
|
$ciphertext = OpenSSL_encrypt($str, "DES-EDE3", $key, OPENSSL_RAW_DATA); |
256
|
|
|
|
257
|
|
|
return base64_encode($ciphertext); |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
/** |
261
|
|
|
* Retrieve payment mode. |
262
|
|
|
* |
263
|
|
|
* @return string |
264
|
|
|
*/ |
265
|
|
|
protected function getMode() : string |
266
|
|
|
{ |
267
|
|
|
return strtolower($this->settings->mode); |
268
|
|
|
} |
269
|
|
|
|
270
|
|
|
|
271
|
|
|
/** |
272
|
|
|
* Retrieve purchase url |
273
|
|
|
* |
274
|
|
|
* @return string |
275
|
|
|
*/ |
276
|
|
|
protected function getPurchaseUrl() : string |
277
|
|
|
{ |
278
|
|
|
return $this->settings->apiPurchaseUrl; |
279
|
|
|
} |
280
|
|
|
|
281
|
|
|
/** |
282
|
|
|
* Retrieve Payment url |
283
|
|
|
* |
284
|
|
|
* @return string |
285
|
|
|
*/ |
286
|
|
|
protected function getPaymentUrl() : string |
287
|
|
|
{ |
288
|
|
|
$mode = $this->getMode(); |
289
|
|
|
|
290
|
|
|
switch ($mode) { |
291
|
|
|
case 'paymentbyidentity': |
292
|
|
|
$url = $this->settings->apiPaymentByIdentityUrl; |
293
|
|
|
break; |
294
|
|
|
case 'paymentbymultiidentity': |
295
|
|
|
$url = $this->settings->apiPaymentByMultiIdentityUrl; |
296
|
|
|
break; |
297
|
|
|
default: // default: normal |
298
|
|
|
$url = $this->settings->apiPaymentUrl; |
299
|
|
|
break; |
300
|
|
|
} |
301
|
|
|
|
302
|
|
|
return $url; |
303
|
|
|
} |
304
|
|
|
|
305
|
|
|
/** |
306
|
|
|
* Convert status to a readable message. |
307
|
|
|
* |
308
|
|
|
* @param $status |
309
|
|
|
* |
310
|
|
|
* @return mixed|string |
311
|
|
|
*/ |
312
|
|
|
private function translateStatus($status) |
313
|
|
|
{ |
314
|
|
|
$translations = [ |
315
|
|
|
'0' => 'تراکنش با موفقیت انجام شد', |
316
|
|
|
'3' => 'پذيرنده کارت فعال نیست لطفا با بخش امور پذيرندگان، تماس حاصل فرمائید', |
317
|
|
|
'23' => 'پذيرنده کارت نا معتبر لطفا با بخش امور پذيرندگان، تماس حاصل فرمائید', |
318
|
|
|
'58' => 'انجام تراکنش مربوطه توسط پايانه ی انجام دهنده مجاز نمی باشد', |
319
|
|
|
'61' => 'مبلغ تراکنش از حد مجاز بالاتر است', |
320
|
|
|
'101' => 'مهلت ارسال تراکنش به پايان رسیده است', |
321
|
|
|
'1000' => 'ترتیب پارامترهای ارسالی اشتباه می باشد', |
322
|
|
|
'1001' => 'پارامترهای پرداخت اشتباه می باشد', |
323
|
|
|
'1002' => 'خطا در سیستم- تراکنش ناموفق', |
324
|
|
|
'1003' => 'IP پذيرنده اشتباه است', |
325
|
|
|
'1004' => 'شماره پذيرنده اشتباه است', |
326
|
|
|
'1005' => 'خطای دسترسی:لطفا بعدا تلاش فرمايید', |
327
|
|
|
'1006' => 'خطا در سیستم', |
328
|
|
|
'1011' => 'درخواست تکراری- شماره سفارش تکراری می باشد', |
329
|
|
|
'1012' => 'اطلاعات پذيرنده صحیح نیست، يکی از موارد تاريخ،زمان يا کلید تراکنش اشتباه است', |
330
|
|
|
'1015' => 'پاسخ خطای نامشخص از سمت مرکز', |
331
|
|
|
'1017' => 'مبلغ درخواستی شما جهت پرداخت از حد مجاز تعريف شده برای اين پذيرنده بیشتر است', |
332
|
|
|
'1018' => 'اشکال در تاريخ و زمان سیستم. لطفا تاريخ و زمان سرور خود را با بانک هماهنگ نمايید', |
333
|
|
|
'1019' => 'امکان پرداخت از طريق سیستم شتاب برای اين پذيرنده امکان پذير نیست', |
334
|
|
|
'1020' => 'پذيرنده غیرفعال شده است', |
335
|
|
|
'1023' => 'آدرس بازگشت پذيرنده نامعتبر است', |
336
|
|
|
'1024' => 'مهر زمانی پذيرنده نامعتبر است', |
337
|
|
|
'1025' => 'امضا تراکنش نامعتبر است', |
338
|
|
|
'1026' => 'شماره سفارش تراکنش نامعتبر است', |
339
|
|
|
'1027' => 'شماره پذيرنده نامعتبر است', |
340
|
|
|
'1028' => 'شماره ترمینال پذيرنده نامعتبر است', |
341
|
|
|
'1029' => 'آدرس IP پرداخت در محدوده آدرس های معتبر اعلام شده توسط پذيرنده نیست', |
342
|
|
|
'1030' => 'آدرس Domain پرداخت در محدوده آدرس های معتبر اعلام شده توسط پذیرنده نیست', |
343
|
|
|
'1031' => 'مهلت زمانی شما جهت پرداخت به پايان رسیده است.لطفا مجددا سعی بفرمایید', |
344
|
|
|
'1032' => 'پرداخت با اين کارت , برای پذيرنده مورد نظر شما امکان پذير نیست', |
345
|
|
|
'1033' => 'به علت مشکل در سايت پذيرنده, پرداخت برای اين پذيرنده غیرفعال شده است', |
346
|
|
|
'1036' => 'اطلاعات اضافی ارسال نشده يا دارای اشکال است', |
347
|
|
|
'1037' => 'شماره پذيرنده يا شماره ترمینال پذيرنده صحیح نمیباشد', |
348
|
|
|
'1053' => 'خطا: درخواست معتبر، از سمت پذيرنده صورت نگرفته است لطفا اطلاعات پذيرنده خود را چک کنید', |
349
|
|
|
'1055' => 'مقدار غیرمجاز در ورود اطلاعات', |
350
|
|
|
'1056' => 'سیستم موقتا قطع میباشد.لطفا بعدا تلاش فرمايید', |
351
|
|
|
'1058' => 'سرويس پرداخت اينترنتی خارج از سرويس می باشد.لطفا بعدا سعی بفرمایید', |
352
|
|
|
'1061' => 'اشکال در تولید کد يکتا. لطفا مرورگر خود را بسته و با اجرای مجدد عملیات پرداخت را انجام دهید', |
353
|
|
|
'1064' => 'لطفا مجددا سعی بفرمايید', |
354
|
|
|
'1065' => 'ارتباط ناموفق .لطفا چند لحظه ديگر مجددا سعی کنید', |
355
|
|
|
'1066' => 'سیستم سرويس دهی پرداخت موقتا غیر فعال شده است', |
356
|
|
|
'1068' => 'با عرض پوزش به علت بروزرسانی , سیستم موقتا قطع میباشد', |
357
|
|
|
'1072' => 'خطا در پردازش پارامترهای اختیاری پذيرنده', |
358
|
|
|
'1101' => 'مبلغ تراکنش نامعتبر است', |
359
|
|
|
'1103' => 'توکن ارسالی نامعتبر است', |
360
|
|
|
'1104' => 'اطلاعات تسهیم صحیح نیست', |
361
|
|
|
'1105' => 'تراکنش بازگشت داده شده است(مهلت زمانی به پايان رسیده است)' |
362
|
|
|
]; |
363
|
|
|
|
364
|
|
|
$unknownError = 'خطای ناشناخته رخ داده است. در صورت کسر مبلغ از حساب حداکثر پس از 72 ساعت به حسابتان برمیگردد'; |
365
|
|
|
|
366
|
|
|
return array_key_exists($status, $translations) ? $translations[$status] : $unknownError; |
367
|
|
|
} |
368
|
|
|
} |
369
|
|
|
|