PluginProbe
HyperPay Payments / 2.3.3
HyperPay Payments v2.3.3
6.6.0 trunk 1.7 1.8 1.8.1 1.8.2 1.8.3 1.8.4 1.8.5 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.1.0 2.2.0 2.3.0 2.3.1 2.3.2 2.3.3 3.0.0 3.0.5 4.0.0 4.0.2 All 34 releases
hyperpay-gateways / includes / Hyperpay_main_class.php

Hyperpay_main_class.php in HyperPay Payments 2.3.3, at includes/Hyperpay_main_class.php

984 lines 33.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * HayperPay main class created to extends from it
5 * when create a new paymentsgateways
6 *
7 */
8 class Hyperpay_main_class extends WC_Payment_Gateway
9 {
10
11 /**
12 * if payments have direct fields on ckeckout page
13 *
14 * @var boolean
15 */
16 public $has_fields = false;
17 protected $loader;
18 public $invoice_id;
19
20 /**
21 * check if user sigined in or not
22 *
23 * @var boolean
24 */
25 protected $is_registered_user = false;
26
27 /**
28 * Mada BlackBins
29 *
30 * @var array
31 */
32 protected $blackBins = [];
33
34 /**
35 * supported brands thats will showing on settings and checkout page
36 *
37 * @var array
38 */
39 protected $supported_brands = [];
40
41 /**
42 * displayed error msg
43 */
44 protected $failed_msg = '';
45
46 /**
47 * regular expressions
48 */
49
50 public $successCodePattern = '/^(000\.000\.|000\.100\.1|000\.[36])/';
51 public $successManualReviewCodePattern = '/^(000\.400\.0|000\.400\.100)/';
52 public $pendingCodePattern = '/^(800\.400\.5|100\.400\.500)/';
53
54 /**
55 * CopyAndPay script URL
56 *
57 * @var string
58 */
59 protected $script_url = "https://eu-prod.oppwa.com/v1/paymentWidgets.js?checkoutId=";
60
61 /**
62 * CopyAndPay prepare checkout link
63 *
64 * @method POST
65 * @var string
66 */
67 protected $token_url = "https://eu-prod.oppwa.com/v1/checkouts";
68
69 /**
70 * get transaction status
71 * @method GET
72 * @var string
73 *
74 * ##TOKEN## will replace with transaction id when fire the request
75 */
76 protected $transaction_status_url = "https://eu-prod.oppwa.com/v1/checkouts/##TOKEN##/payment";
77
78 /**
79 * get transaction status
80 * @method GET
81 * @var string
82 *
83 * ##TOKEN## will replace with transaction id when fire the request
84 */
85 protected $capture = "https://eu-test.oppwa.com/v1/payments/";
86
87 /**
88 * Query transaction report
89 *
90 * @method GET
91 * @var string
92 */
93 protected $query_url = "https://eu-prod.oppwa.com/v1/query";
94
95
96 /**
97 * payment styles that will show in settings
98 *
99 * @var array
100 *
101 */
102 protected $hyperpay_payment_style = [
103 'card' => 'Card',
104 'plain' => 'Plain'
105 ];
106
107 protected $dataTosend = [];
108
109
110
111 function __construct()
112 {
113
114 $this->init_settings(); // <== to get saved settings from database
115 $this->init_form_fields(); // <== render form inside admin panel
116 $this->is_arabic = substr(get_locale(), 0, 2) == 'ar'; // <== to get current locale
117
118 $this->testmode = $this->get_option('testmode'); // <== check if payments on test mode
119 $this->title = $this->get_option('title'); // <== get title from setting
120 $this->trans_type = $this->get_option('trans_type'); // <== get transaction type [DB / Pre-Auth] from setting
121 $this->trans_mode = $this->get_option('trans_mode'); // <== get transaction mode [INTERNAL / EXTERNAL / LIVE] from setting
122 $this->accesstoken = $this->get_option('accesstoken'); // <== get accesstoke from setting
123 $this->entityid = $this->get_option('entityId'); // <== get entityId from setting
124 $this->brands = is_array($this->get_option('hyper_pay_brands')) ? $this->get_option('hyper_pay_brands') : [$this->get_option('hyper_pay_brands')]; // <== get brands from setting
125
126 $this->payment_style = $this->get_option('payment_style'); // <== get style from setting
127 $this->mailerrors = $this->get_option('mailerrors'); // <== get if mail error check or not from setting
128 $this->order_status = $this->get_option('order_status'); // <== get order status after success from setting
129 $this->redirect_page_id = $this->get_option('redirect_page_id'); // <== after order complete redirect to selected page
130 $this->custom_style = $this->get_option('custom_style'); // <== get custom style from setting
131 $this->latin_validation = $this->get_option('latin_validation'); // <== get custom style from setting
132 $this->currency = get_woocommerce_currency();
133
134
135 /**
136 * if test mode is one
137 * overwrite currents URLs ti test URLs
138 */
139 if ($this->testmode) {
140 $this->query_url = "https://test.oppwa.com/v1/query";
141 $this->token_url = "https://test.oppwa.com/v1/checkouts";
142 $this->script_url = "https://test.oppwa.com/v1/paymentWidgets.js?checkoutId=";
143 $this->transaction_status_url = "https://test.oppwa.com/v1/checkouts/##TOKEN##/payment";
144 }
145
146 $this->query_url .= "?entityId=" . $this->entityid;
147 $this->transaction_status_url .= "?entityId=" . $this->entityid;
148
149 /**
150 * default failed message
151 * @var string
152 */
153 $this->failed_message = __('Your transaction not completed .', 'hyperpay-payments');
154 $this->success_message = __('Your payment has been processed successfully.', 'hyperpay-payments');
155
156 /**
157 * overwrite default update function
158 *
159 * @param woocommerce_update_options_payment_gateways_<payment_id>
160 * @param array[class,function_name]
161 */
162
163 add_action('woocommerce_update_options_payment_gateways_' . $this->id, array($this, 'process_admin_options'));
164
165 /**
166 * prepare checkout form
167 *
168 * @param string woocommerce_receipt_<payments_id>
169 * @param array[class,function_name]
170 */
171
172 add_action("woocommerce_receipt_{$this->id}", [$this, 'receipt_page']);
173
174 /**
175 * set payments icon from assets/images/BRAND-log.png
176 *
177 * make sure when add new image to rename image according this format BRAND_NAME-logo.svg
178 *
179 * @param string woocommerce_gateway_icon
180 * @param array[class,function_name]
181 *
182 */
183 add_filter('woocommerce_gateway_icon', [$this, 'set_icons'], 10, 2);
184
185 /**
186 * to include assets/js/admin.js <JavaScript>
187 *
188 * @param string admin_enqueue_scripts
189 * @param array[class,function_name]
190 *
191 */
192 add_action('admin_enqueue_scripts', [$this, 'admin_script']);
193 add_action("woocommerce_thankyou_order_received_text", [$this, "order_received_text"], 10, 2);
194 add_action('before_woocommerce_pay', [$this, 'action_before_woocommerce_pay'], 10, 0);
195
196 add_action('woocommerce_order_action_capture_payment', [$this, 'capture_payment']);
197 }
198
199 public function capture_payment($order)
200 {
201
202 $uniqueId = $order->get_meta('transaction_id');
203 $url = $this->capture . $uniqueId;
204
205 $orderAmount = number_format($order->get_total(), 2, '.', '');
206 $amount = number_format(round($orderAmount, 2), 2, '.', '');
207
208 $gateway_name = 'WC_' . ucfirst($order->get_payment_method()) . "_Gateway";
209 $gateway = new $gateway_name();
210
211 $data = [
212 'headers' => [
213 "Authorization" => "Bearer {$gateway->accesstoken}"
214 ],
215 'body' => [
216 "entityId" => $gateway->entityid,
217 "amount" => $amount,
218 "currency" => $gateway->currency,
219 "paymentType" => 'CP',
220 ]
221 ];
222
223 $response = wp_remote_post($url, $data);
224 $resultJson = wp_remote_retrieve_body($response);
225 $resultJson = json_decode($resultJson, true);
226 $resultCode = $resultJson['result']['code'] ?? '';
227
228 if (preg_match($this->successCodePattern, $resultCode) || preg_match($this->successManualReviewCodePattern, $resultCode)) {
229 $order->add_order_note("Captured Successfully");
230 $order->update_status($this->order_status);
231 } else {
232 $order->add_order_note("Captured Faild" . $resultCod['result']['description'] ?? 'Unknown reason');
233 }
234
235 $location = $_SERVER['HTTP_REFERER'];
236 wp_safe_redirect($location);
237 die;
238 }
239
240
241
242 public function action_before_woocommerce_pay()
243 {
244 global $wp;
245
246 $order_id = absint($wp->query_vars['order-pay']); // The order ID
247
248 $order = wc_get_order($order_id);
249
250 if ($order->has_status('on-hold')) {
251 $order->update_status('pending');
252 } elseif ($order->has_status($this->order_status)) {
253 wp_redirect($this->get_return_url($order));
254 }
255 }
256
257 public function order_received_text($thanks_text, $order)
258 {
259
260 $msg = $order->get_meta('gateway_note');
261 if ($order->get_payment_method() == $this->id && $order->get_status() == 'on-hold' && !empty($msg)) {
262 wc_add_notice($msg, "notice");
263 wc_print_notices();
264 } else {
265 return $thanks_text;
266 }
267 }
268
269 /**
270 * for validate settings form
271 * @return void
272 */
273
274 public function admin_script(): void
275 {
276 global $current_tab, $current_section;
277
278 /**
279 * to make sure load admin.js just when currents pyments opened
280 *
281 */
282 if ($current_tab == 'checkout' && $current_section == $this->id) {
283
284 $data = [
285 'id' => $this->id,
286 'url' => $this->token_url,
287 'code_setting' => wp_enqueue_code_editor(['type' => 'text/css'])
288 ];
289
290 wp_enqueue_script('hyperpay_admin', HYPERPAY_PLUGIN_DIR . '/assets/js/admin.js', ['jquery'], false, true);
291 wp_localize_script('hyperpay_admin', 'data', $data);
292 }
293 }
294
295
296 /**
297 * to set payment icon based on supported brands
298 *
299 * @param string $icon
300 * @param string $id currnet payment id
301 *
302 * @return string $icon new icon
303 *
304 */
305
306 public function set_icons($icon, $id): string
307 {
308
309 if ($id == $this->id) {
310 $icons = "";
311 foreach ($this->brands as $brand) {
312 $img = HYPERPAY_PLUGIN_DIR . '/assets/images/default.png';
313
314 if (file_exists(HYPERPAY_ABSPATH . '/assets/images/' . esc_attr($brand) . "-logo.svg"))
315 $img = HYPERPAY_PLUGIN_DIR . '/assets/images/' . esc_attr($brand) . "-logo.svg";
316
317 $icons .= "<img style='padding:2px ; ' src='$img' >";
318 }
319 return $icons;
320 }
321 return $icon;
322 }
323
324 /**
325 * Here you can define all fiels thats will showning in setting page
326 * @return void
327 */
328 public function init_form_fields(): void
329 {
330
331 $this->form_fields = [
332 'enabled' => [
333 'title' => __('Enable/Disable', 'hyperpay-payments'),
334 'type' => 'checkbox',
335 'label' => __('Enable Payment Module.', 'hyperpay-payments'),
336 'default' => 'no'
337 ],
338 'testmode' => [
339 'title' => __('Test mode', 'hyperpay-payments'),
340 'type' => 'select',
341 'options' => ['0' => __('Off', 'hyperpay-payments'), '1' => __('On', 'hyperpay-payments')]
342 ],
343 'title' => [
344 'title' => __('Title:', 'hyperpay-payments'),
345 'type' => 'text',
346 'description' => ' ' . __('This controls the title which the user sees during checkout.', 'hyperpay-payments'),
347 'default' => $this->method_title ?? __('Credit Card', 'hyperpay-payments')
348 ],
349 'trans_type' => [
350 'title' => __('Transaction type', 'hyperpay-payments'),
351 'type' => 'select',
352 'options' => $this->get_hyperpay_trans_type(),
353 ],
354 'trans_mode' => array(
355 'title' => __('Transaction mode', 'hyperpay-payments'),
356 'type' => 'select',
357 'options' => $this->get_hyperpay_trans_mode(),
358 'description' => ''
359 ),
360 'accesstoken' => [
361 'title' => __('Access Token', 'hyperpay-payments'),
362 'type' => 'text',
363 ],
364 'entityId' => [
365 'title' => __('Entity ID', 'hyperpay-payments'),
366 'type' => 'text',
367 ],
368 'secret' => [
369 'title' => __('Webhook Key', 'hyperpay-payments'),
370 'type' => 'text',
371 ],
372 'webhock' => [
373 'title' => __('Webhook URL', 'hyperpay-payments'),
374 'type' => 'text',
375 'class' => 'disabled',
376 'default' => get_site_url() . "/?rest_route=/hyperpay/v1/" . get_class($this)
377 ],
378 'hyper_pay_brands' => [
379 'title' => __('Brands', 'hyperpay-payments'),
380 'class' => count($this->supported_brands) !== 1 ?: 'disabled',
381 'type' => count($this->supported_brands) > 1 ? 'multiselect' : 'select',
382 'options' => $this->supported_brands,
383 ],
384 'payment_style' => [
385 'title' => __('Payment Style', 'hyperpay-payments'),
386 'type' => 'select',
387 'class' => count($this->hyperpay_payment_style) !== 1 ?: 'disabled',
388 'options' => $this->hyperpay_payment_style,
389 'default' => 'plain'
390 ],
391 'custom_style' => [
392 'title' => __('Custom Style', 'hyperpay-payments'),
393 'type' => 'textarea',
394 'description' => 'Input custom css for payment (Optional)',
395 'class' => 'hyperpay_custom_style'
396 ],
397 'mailerrors' => [
398 'title' => __('Enable error logging by email?', 'hyperpay-payments'),
399 'type' => 'checkbox',
400 'label' => __('Yes'),
401 'default' => 'no',
402 'description' => __('If checked, an email will be sent to ' . get_bloginfo('admin_email') . ' whenever a callback fails.'),
403 ],
404 'latin_validation' => [
405 'title' => __('Enable Input validation (Accept English Characters only)', 'hyperpay-payments'),
406 'type' => 'checkbox',
407 'label' => __('Yes'),
408 'default' => 'yes',
409 'description' => __('Disable this option may cause transaction declined by bank due to 3DSecure', 'hyperpay-payments'),
410 ],
411 'redirect_page_id' => [
412 'title' => __('Return Page', 'hyperpay-payments'),
413 'type' => 'select',
414 'options' => $this->get_pages('Select Page'),
415 'description' => __("success page", 'hyperpay-payments')
416 ],
417 'order_status' => [
418 'title' => __('Status Of Order', 'hyperpay-payments'),
419 'type' => 'select',
420 'options' => $this->get_order_status(),
421 'description' => __("select order status after success transaction.", 'hyperpay-payments')
422 ]
423 ];
424 }
425
426
427 /**
428 * to fill order_status select fiels
429 *
430 * @return array
431 */
432 function get_order_status(): array
433 {
434 $order_status = [
435
436 'processing' => __('Processing', 'hyperpay-payments'),
437 'completed' => __('Completed', 'hyperpay-payments')
438 ];
439
440 return $order_status;
441 }
442
443 /**
444 * to fill trans_type select fiels
445 *
446 * @return array
447 */
448 function get_hyperpay_trans_type(): array
449 {
450 $hyperpay_trans_type = [
451 'DB' => 'Debit',
452 'PA' => 'Pre-Authorization'
453 ];
454
455 return $hyperpay_trans_type;
456 }
457
458 /**
459 * to fill trans_mode select fiels
460 *
461 * @return array
462 */
463 function get_hyperpay_trans_mode(): array
464 {
465 $hyperpay_trans_type = [
466 'INTERNAL' => 'Internal',
467 'EXTERNAL' => 'External',
468 'LIVE' => 'Live'
469 ];
470
471
472 return $hyperpay_trans_type;
473 }
474
475 /**
476 * This function fire when click on Place order at checkout page
477 * @param int $order_id
478 *
479 * @return void
480 */
481 function receipt_page($order_id)
482 {
483 $error = false;
484 $order = new WC_Order($order_id);
485
486 // if we have id param that mean the page result ACI redirection
487 if (isset($_GET['id'])) {
488 $token = sanitize_text_field($_GET['id']);
489 $url = str_replace('##TOKEN##', $token, $this->transaction_status_url);
490 // set header request to contain access token
491 $auth = [
492 'headers' => ['Authorization' => 'Bearer ' . $this->accesstoken]
493 ];
494 $response = wp_remote_get($url, $auth);
495 $resultJson = wp_remote_retrieve_body($response);
496 $resultJson = json_decode($resultJson, true);
497
498 if (isset($resultJson['result']['code'])) {
499 $status = $this->check_status($resultJson);
500 if ($order_id) {
501 // dynamic fire proper function
502 $order = new WC_Order($order_id);
503 return $this->$status($order, $resultJson);
504 } else {
505 $error = true;
506 }
507 } else {
508 $error = true;
509 }
510
511
512 if ($error)
513 $this->failed($order, $resultJson);
514 } else { // process a new transaction
515 $checkout = $this->prepareCheckout($order_id);
516 $token = $checkout['token'];
517 $transactionKey = $checkout['transactionKey'];
518 $this->renderPaymentForm($order, $token, $transactionKey);
519 }
520 }
521
522 private function isJson($string)
523 {
524 json_decode($string);
525 return json_last_error() === JSON_ERROR_NONE;
526 }
527
528 /**
529 *
530 * render CopyAndPay form
531 * @param WC_Order $order
532 * @param string $token
533 * @return void
534 */
535 private function renderPaymentForm(WC_Order $order, string $token, int $transactionKey): void
536 {
537
538 $scriptURL = $this->script_url;
539 $scriptURL .= $token;
540
541 $payment_brands = $this->brands;
542 if (is_array($this->brands))
543 $payment_brands = implode(' ', $this->brands);
544
545 $postbackURL = $order->get_checkout_payment_url(true);
546
547 if (parse_url($postbackURL, PHP_URL_QUERY)) {
548 $postbackURL .= '&';
549 } else {
550 $postbackURL .= '?';
551 }
552 $postbackURL .= 'callback=true';
553 $postbackURL .= "&transaction-key=$transactionKey";
554
555
556 $dataObj = [
557 'is_arabic' => esc_js($this->is_arabic),
558 'style' => esc_js($this->payment_style),
559 'postbackURL' => esc_url($postbackURL),
560 'payment_brands' => esc_js($payment_brands)
561 ];
562
563 // this key used to query the transaction status on ACI
564
565 // include CopyAndPay script to show the form
566 wp_enqueue_script('wpwl_hyperpay_script', $scriptURL, null, null);
567
568 // include assests\js\script.js to set wpwlOptions
569 wp_enqueue_script('hyperpay_script', HYPERPAY_PLUGIN_DIR . '/assets/js/script.js', ['jquery'], '4', true);
570
571 // pass data to assests\js\script.js
572 wp_localize_script('hyperpay_script', 'dataObj', $dataObj);
573
574 // apply custom style that's entered on setting page <custom_style>
575
576
577 wp_register_style('hyperpay-inline', false); // phpcs:ignore
578 wp_enqueue_style('hyperpay-inline');
579 wp_add_inline_style('hyperpay-inline', $this->custom_style);
580
581 if ($this->id == 'hyperpay_mada') {
582 wp_enqueue_style('hyperpay_mada_style', HYPERPAY_PLUGIN_DIR . '/assets/css/mada.css');
583 }
584 }
585
586
587 /**
588 * Process the payment and return the result
589 * @param int $order_id
590 * @return array[redirect,token,result]
591 *
592 */
593 public function process_payment($order_id): array
594 {
595 $order = new WC_Order($order_id);
596 /**
597 *
598 * validate data to prevent arabic character
599 */
600
601 if ($this->latin_validation == 'yes') {
602 $firstName = $order->get_billing_first_name();
603 $family = $order->get_billing_last_name();
604 $street = $order->get_billing_address_1();
605 $city = $order->get_billing_city();
606 $email = $order->get_billing_email();
607
608 $data_to_validate = [
609 'first name' => $firstName,
610 'last name' => $family,
611 'street' => $street,
612 'city' => $city,
613 'email' => $email,
614 ];
615
616 $this->validate_form($data_to_validate);
617 }
618
619
620 // add g2p_token to url query
621 return [
622 'result' => 'success',
623 'redirect' => $order->get_checkout_payment_url(true)
624 ];
625 }
626
627 public function prepareCheckout($order_id)
628 {
629
630 global $woocommerce;
631 $order = new WC_Order($order_id);
632
633
634 $shipping_cost = number_format($order->get_shipping_total(), 2, '.', '');
635
636 $orderAmount = number_format($order->get_total(), 2, '.', '');
637 $amount = number_format(round($orderAmount, 2), 2, '.', '');
638
639 $firstName = $order->get_billing_first_name();
640 $family = $order->get_billing_last_name();
641 $street = $order->get_billing_address_1();
642 $city = $order->get_billing_city();
643 $state = $order->get_billing_state() ?? $city;
644 $email = $order->get_billing_email();
645 $zip = $order->get_billing_postcode();
646 $country = $order->get_billing_country();
647
648
649 $firstName = preg_replace('/\s/', '', str_replace("&", "", $firstName));
650 $family = preg_replace('/\s/', '', str_replace("&", "", $family));
651 $street = preg_replace('/\s/', '', str_replace("&", "", $street));
652 $city = preg_replace('/\s/', '', str_replace("&", "", $city));
653 $state = preg_replace('/\s/', '', str_replace("&", "", $state));
654 $country = preg_replace('/\s/', '', str_replace("&", "", $country));
655 $transactionKey = rand(11111111, 99999999);
656
657
658 // set data to post
659 $url = $this->token_url;
660 $data = [
661 'headers' => [
662 "Authorization" => "Bearer {$this->accesstoken}"
663 ],
664 'body' => [
665 "entityId" => $this->entityid,
666 "amount" => $amount,
667 "currency" => $this->currency,
668 "paymentType" => $this->trans_type,
669 "merchantTransactionId" => $order_id . "I" . $transactionKey,
670 "customer.email" => $email,
671 "notificationUrl" => $order->get_checkout_payment_url(true),
672 "customParameters[bill_number]" => $order_id . "I" . $transactionKey,
673 "customer.givenName" => $firstName,
674 "customer.surname" => $family,
675 "billing.street1" => $street,
676 "billing.city" => $city,
677 "billing.state" => $state,
678 "billing.country" => $country,
679 "billing.postcode" => $zip,
680 "shipping.postcode" => $zip,
681 "shipping.street1" => $street,
682 "shipping.city" => $city,
683 "shipping.state" => $state,
684 "shipping.country" => $country,
685 "shipping.cost" => $shipping_cost,
686 "customParameters[branch_id]" => '1',
687 "customParameters[teller_id]" => '1',
688 "customParameters[device_id]" => '1',
689 "customParameters[plugin]" => 'wordpress',
690
691 ]
692 ];
693
694
695 if ($this->testmode) {
696 $data['body']["testMode"] = $this->trans_mode;
697 }
698
699
700
701 // add extra parameters if exists
702 $data = array_merge_recursive($data, $this->setExtraData($order));
703
704 // HTTP Request to oppwa to get checkout id
705 $response = wp_remote_post($url, $data);
706
707 if (is_wp_error($response) || wp_remote_retrieve_response_code($response) != 200) {
708 $description = json_decode(wp_remote_retrieve_body($response), true)['result']['description'];
709 wc_add_notice(__("Problem with payments :$description", 'hyperpay-payments'), 'error');
710 throw new \Exception();
711 }
712
713 $response = wp_remote_retrieve_body($response);
714
715 $result = json_decode($response, true);
716
717
718
719 if (array_key_exists('id', $result)) {
720 $token = $result['id'];
721 }
722
723 return [
724 'token' => $token,
725 'transactionKey' => $transactionKey
726 ];
727 }
728
729
730 /**
731 * to get all pages of website to fill <redirect to> option in admin setting
732 *
733 * @param bool
734 * @param bool
735 * @return array
736 *
737 */
738 function get_pages(bool $title = false, bool $indent = true): array
739 {
740 $wp_pages = get_pages('sort_column=menu_order');
741 $page_list = [];
742
743 if ($title)
744 $page_list[] = $title;
745
746 foreach ($wp_pages as $page) {
747 $prefix = '';
748 // show indented child pages?
749 if ($indent) {
750 $has_parent = $page->post_parent;
751 while ($has_parent) {
752 $prefix .= ' - ';
753 $next_page = get_page($has_parent);
754 $has_parent = $next_page->post_parent;
755 }
756 }
757 // add to page list array array
758 $page_list[$page->ID] = $prefix . $page->post_title;
759 }
760
761 return $page_list;
762 }
763
764 /**
765 * check if all data valid to post {English Charachter}
766 * @param array
767 * @return void
768 */
769 function validate_form(array $data): void
770 {
771 $errors = [];
772
773
774 foreach ($data as $key => $field) {
775 if (!preg_match("/^[a-zA-Z0-9-._!`'#%&,:;<>=@{}~\$\(\)\*\+\/\\\?\[\]\^\| +]+$/", $field) || strlen($field) < 3)
776 $errors[$key] = __($key, 'hyperpay-payments') . ' ' . __('format error', 'hyperpay-payments');
777 }
778
779 if (!preg_match('/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,})$/i', $data['email'])) {
780 $errors['email'] = __('Email format not valid', 'hyperpay-payments');
781 }
782
783
784 if ($errors) {
785 foreach ($errors as $msg) {
786 wc_add_notice('<strong>*' . $msg . '</strong>', 'error');
787 }
788 throw new Exception('Validation Error', 400);
789 }
790 }
791
792 /**
793 *
794 * GET request to transaction report to check if transaction exists or not
795 * @param int
796 * @return array $response
797 *
798 */
799 public function queryTransactionReport(string $merchantTrxId): array
800 {
801
802 $url = $this->query_url . "&merchantTransactionId=$merchantTrxId";
803 $response = wp_remote_get($url, ["headers" => ["Authorization" => "Bearer {$this->accesstoken}"]]);
804
805 $response = wp_remote_retrieve_body($response);
806 $response = json_decode($response, true);
807
808 return $response;
809 }
810
811
812
813 /**
814 *
815 * check the status
816 *
817 * @param array $resultJson
818 * @return string
819 */
820 public function check_status(array $resultJson): string
821 {
822
823 $status = 'failed';
824 $resultCode = $resultJson['result']['code'];
825
826 if (preg_match($this->successCodePattern, $resultCode) || preg_match($this->successManualReviewCodePattern, $resultCode)) {
827 $status = 'success';
828 } elseif (preg_match($this->pendingCodePattern, $resultCode)) {
829 $status = "pending";
830 } elseif (isset($resultJson['card']['bin']) && $resultJson['result']['code'] == '800.300.401' && in_array($resultJson['card']['bin'], $this->blackBins)) {
831 $this->failed_message = __('Sorry! Please select "mada" payment option in order to be able to complete your purchase successfully.', 'hyperpay-payments');
832 }
833
834 return $status;
835 }
836
837
838 /**
839 * handel failed pyments
840 * @param WC_Order $order
841 * @param string $messege
842 * @return void
843 */
844 public function failed(WC_Order $order, $resultJson)
845 {
846
847 if (isset($_GET["callback"]) && isset($_GET['transaction-key'])) {
848
849 $hpOrderId = $order->get_id();
850 $transactionKey = sanitize_text_field($_GET['transaction-key']);
851 $merchantTrxId = $hpOrderId . "I" . $transactionKey;
852 $queryResponse = $this->queryTransactionReport($merchantTrxId);
853
854 if (array_key_exists("payments", $queryResponse)) {
855 $this->processQueryResult($queryResponse, $order);
856 }
857 }
858
859
860
861 $error_code = $resultJson["result"]["code"];
862 $error_description = $resultJson["result"]["description"];
863 $aci_msg = $error_code == "600.200.500" ? "configration error" : $error_description;
864
865 $order->add_order_note("{$this->failed_message} $error_code : $error_description");
866 wc_add_notice($this->failed_message, "error");
867 wc_add_notice($aci_msg, "error");
868
869 /**
870 * get extended description
871 */
872 if (isset($resultJson["resultDetails"]["ExtendedDescription"])) {
873 $resultDetails = $resultJson["resultDetails"]["ExtendedDescription"];
874 if ($this->isJson($resultDetails)) {
875 $resultDetails = json_decode($resultDetails, true);
876 if (array_key_exists("details", $resultDetails)) {
877 $error_list = $resultDetails["details"];
878 } elseif (array_key_exists("message", $resultDetails)) {
879 $order->add_order_note("extended description2 : " . $resultDetails['message']);
880 wc_add_notice($resultDetails['message'], "error");
881 }
882 }
883 }
884
885 foreach ($error_list ?? [] as $error) {
886 $order->add_order_note("extended description : " . $error["error"]);
887 wc_add_notice($error["error"], "error");
888 }
889
890
891 $order->update_status("cancelled");
892 wc_print_notices();
893 }
894
895 /**
896 * check the result of transaction if success of failed
897 *
898 * @param array $resultJson
899 * @param WC_Order $order
900 * @return void
901 */
902 public function processQueryResult(array $resultJson, WC_Order $order)
903 {
904 unset($_GET["callback"]);
905
906 $payment = end($resultJson["payments"]); // get the last transaction
907
908 if (isset($payment["result"]["code"])) {
909 $status = $this->check_status($payment);
910 $this->$status($order, $payment);
911 die;
912 }
913 }
914
915 /**
916 * set customParameters of requested data
917 * @param WC_Order $order
918 * @return array
919 */
920 public function setExtraData(WC_Order $order): array
921 {
922 return [];
923 }
924
925 /**
926 * update success order
927 * @param WC_Order $order
928 * @param array $resultJson
929 * @return void
930 */
931 public function success(WC_Order $order, $resultJson)
932 {
933 global $woocommerce;
934
935
936 $woocommerce->cart->empty_cart();
937 $uniqueId = $resultJson["id"];
938
939 //to add action in order details to capture the pre authorization payments
940 if (array_key_exists('paymentType', $resultJson) && $resultJson['paymentType'] == "PA") {
941 $order->add_meta_data("is_pre_authorization", true);
942 $order->add_meta_data("transaction_id", $uniqueId);
943 $order->add_order_note("pre authorization transaction, need to capture");
944 $this->order_status = "on-hold";
945 }
946
947 if (array_key_exists("invoice_id", $resultJson["resultDetails"])) {
948 $this->invoice_id = $resultJson["resultDetails"]["invoice_id"];
949 $order->add_meta_data("invoice_id", $this->invoice_id);
950 $order->add_order_note("invoice id : " . $this->invoice_id);
951 }
952
953 $order->add_order_note($this->success_message . __("Transaction ID: ", "hyperpay-payments") . esc_html($uniqueId));
954 $order->update_status($this->order_status);
955 $order->save();
956
957
958
959 wp_redirect($this->get_return_url($order));
960 }
961
962 /**
963 * update pending order
964 * @param WC_Order $order
965 * @param array $resultJson
966 * @return void
967 */
968 public function pending(WC_Order $order, $resultJson)
969 {
970 global $woocommerce;
971
972
973 $order->update_status("on-hold");
974 $order->add_meta_data("gateway_note", __("Transaction is pending confirmation from ", "hyperpay-payments") . str_replace("hyperpay_", "", $order->get_payment_method()));
975 $order->save();
976
977 $woocommerce->cart->empty_cart();
978 $uniqueId = $resultJson["id"];
979
980 $order->add_order_note("the order waiting gateway confirmation" . __("Transaction ID: ", "hyperpay-payments") . esc_html($uniqueId));
981 wp_redirect($this->get_return_url($order));
982 }
983 }
984