PluginProbe
Tamara Checkout / trunk
Tamara Checkout vtrunk
1.9.9.23 1.9.9.22 1.9.9.20 trunk 1.0.13 1.7.4 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 1.9.9.1 1.9.9.11 1.9.9.12 1.9.9.13 1.9.9.14 1.9.9.15 1.9.9.16 1.9.9.17 1.9.9.2 1.9.9.3 1.9.9.4 1.9.9.5 All 29 releases
tamara-checkout / src / TamaraCheckout.php

TamaraCheckout.php in Tamara Checkout trunk, at src/TamaraCheckout.php

3,147 lines 108.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3
4 namespace Tamara\Wp\Plugin;
5
6 use Exception;
7 use Tamara\Wp\Plugin\Dependencies\Illuminate\Container\Container;
8 use Tamara\Wp\Plugin\Dependencies\Tamara\Model\Money;
9 use Tamara\Wp\Plugin\Dependencies\Tamara\Model\Payment\Refund;
10 use Tamara\Wp\Plugin\Dependencies\Tamara\Request\Order\GetOrderByReferenceIdRequest;
11 use Tamara\Wp\Plugin\Dependencies\Tamara\Request\Order\GetOrderRequest;
12 use Tamara\Wp\Plugin\Dependencies\Tamara\Request\Payment\RefundRequest;
13 use Tamara\Wp\Plugin\Helpers\MoneyHelper;
14 use Tamara\Wp\Plugin\Interfaces\WPPluginInterface;
15 use Tamara\Wp\Plugin\Services\TamaraNotificationService;
16 use Tamara\Wp\Plugin\Services\ViewService;
17 use Tamara\Wp\Plugin\Services\WCTamaraGateway;
18 use Tamara\Wp\Plugin\Services\WCTamaraGatewayPayNextMonth;
19 use Tamara\Wp\Plugin\Services\WCTamaraGatewayPayNow;
20 use Tamara\Wp\Plugin\Services\WCTamaraGatewayCheckout;
21 use Tamara\Wp\Plugin\Services\WCTamaraGatewayPayByInstalments;
22 use Tamara\Wp\Plugin\Traits\ConfigTrait;
23 use Tamara\Wp\Plugin\Traits\ServiceTrait;
24 use Tamara\Wp\Plugin\Traits\TamaraPaymentTypesTrait;
25 use Tamara\Wp\Plugin\Traits\WPAttributeTrait;
26 use WC_Order;
27 use WP;
28
29 class TamaraCheckout extends Container implements WPPluginInterface
30 {
31 use ConfigTrait;
32 use WPAttributeTrait;
33 use TamaraPaymentTypesTrait;
34
35 /**
36 * @var string Tamara CheckoutFrame JS Url
37 */
38 public const
39 TAMARA_SUMMARY_WIDGET_URL = 'https://cdn.tamara.co/widget-v2/tamara-widget.js',
40 TAMARA_SUMMARY_WIDGET_SANDBOX_URL = 'https://cdn-sandbox.tamara.co/widget-v2/tamara-widget.js',
41
42 TAMARA_LOGO_BADGE_EN_URL = 'https://cdn.tamara.co/assets/png/tamara-logo-badge-en.png',
43 TAMARA_LOGO_BADGE_AR_URL = 'https://cdn.tamara.co/assets/png/tamara-logo-badge-ar.png',
44 MESSAGE_LOG_FILE_NAME = 'tamara-custom.log',
45 TAMARA_GATEWAY_ID = 'tamara-gateway',
46 TAMARA_GATEWAY_PAY_NOW = 'tamara-gateway-pay-now',
47 TAMARA_GATEWAY_PAY_NEXT_MONTH = 'tamara-gateway-pay-next-month',
48 TAMARA_GATEWAY_PAY_BY_INSTALMENTS_ID = 'tamara-gateway-pay-by-instalments',
49 TAMARA_GATEWAY_PAY_IN_X = 'tamara-gateway-pay-in-',
50 TAMARA_GATEWAY_PAY_IN_2 = 'tamara-gateway-pay-in-2',
51 TAMARA_GATEWAY_PAY_IN_3 = 'tamara-gateway-pay-in-3',
52 TAMARA_GATEWAY_PAY_IN_4 = 'tamara-gateway-pay-in-4',
53 TAMARA_GATEWAY_PAY_IN_5 = 'tamara-gateway-pay-in-5',
54 TAMARA_GATEWAY_PAY_IN_6 = 'tamara-gateway-pay-in-6',
55 TAMARA_GATEWAY_PAY_IN_7 = 'tamara-gateway-pay-in-7',
56 TAMARA_GATEWAY_PAY_IN_8 = 'tamara-gateway-pay-in-8',
57 TAMARA_GATEWAY_PAY_IN_9 = 'tamara-gateway-pay-in-9',
58 TAMARA_GATEWAY_PAY_IN_10 = 'tamara-gateway-pay-in-10',
59 TAMARA_GATEWAY_PAY_IN_11 = 'tamara-gateway-pay-in-11',
60 TAMARA_GATEWAY_PAY_IN_12 = 'tamara-gateway-pay-in-12',
61 TAMARA_GATEWAY_CHECKOUT_ID = 'tamara-gateway-checkout',
62 TAMARA_AUTHORISED_STATUS = 'authorised',
63 TAMARA_AUTHORIZED_STATUS = 'authorized',
64 TAMARA_CANCELED_STATUS = 'canceled',
65 TAMARA_EXPIRED_STATUS = 'expired',
66 TAMARA_DECLINED_STATUS = 'declined',
67 TAMARA_REFUNDED_STATUS = 'refunded',
68 TAMARA_CAPTURED_STATUS = 'captured',
69 TAMARA_PARTIALLY_CAPTURED_STATUS = 'partially_captured',
70 TAMARA_FULLY_CAPTURED_STATUS = 'fully_captured',
71 TAMARA_PARTIALLY_REFUNDED_STATUS = 'partially_refunded',
72 TAMARA_FULLY_REFUNDED_STATUS = 'fully_refunded',
73 TAMARA_INLINE_TYPE_KNOWMORE_WIDGET_INT = 1,
74 TAMARA_INLINE_TYPE_PRODUCT_WIDGET_INT = 2,
75 TAMARA_INLINE_TYPE_CART_WIDGET_INT = 3,
76 TAMARA_INLINE_TYPE_SINGLE_CHECKOUT_WIDGET = 6,
77 DOWN_PAYMENT = 'down_payment',
78 INSTALMENT = 'instalment',
79 PAY_LATER_PDP_MAX_AMOUNT = 200;
80
81 /**
82 * @var string Version of this plugin
83 */
84 public $version;
85
86 /** @noinspection PhpUnusedElementInspection */
87 /**
88 * @var string Base path to this plugin
89 */
90 public $basePath;
91
92 /** @noinspection PhpUnusedElementInspection */
93 /**
94 * @var string Base url of the folder of this plugin
95 */
96 public $baseUrl;
97
98 /**
99 * @var string The filename of the plugin (it should have full path + file name)
100 */
101 public $pluginFilename;
102
103 /**
104 * @var \WP_REST_Request $restApiRequest
105 */
106 protected $restApiRequest;
107
108 /**
109 * @var string The customer phone number on checkout
110 */
111 protected $customerPhoneNumber;
112
113 /**
114 * @var string The customer billing country on checkout (ISO 3166-1 alpha-2)
115 */
116 protected $customerBillingCountry;
117
118 /**
119 * @var array<int, bool>
120 */
121 protected $orderReceivedAuthoriseResults = [];
122
123 /**
124 * Tamara_Checkout constructor.
125 *
126 * @param $config
127 */
128 public function __construct($config)
129 {
130 $this->bindConfig($config);
131
132 // phpcs:ignore PSR2.ControlStructures.ControlStructureSpacing.SpacingAfterOpenBrace
133 if (!empty($services = $config['services'] ?? null)) {
134 $this->registerServices($services);
135 }
136 }
137
138 /**
139 * @param float $totalAmount
140 * @param int $numberOfInstalments
141 *
142 * @return array
143 */
144 public static function calculateInstalmentPlan(float $totalAmount, int $numberOfInstalments = 3): array
145 {
146 $totalAmount = $totalAmount * 100;
147 $modAmount = $totalAmount % $numberOfInstalments;
148 $downPayment = round(floatval((($totalAmount - $modAmount) / $numberOfInstalments / 100) + ($modAmount / 100)), 2);
149 $instalment = ($totalAmount - $modAmount) / $numberOfInstalments / 100;
150
151 return [
152 static::DOWN_PAYMENT => $downPayment,
153 static::INSTALMENT => $instalment,
154 ];
155 }
156
157 /**
158 * Register service providers set in config
159 *
160 * @param $services
161 */
162 protected function registerServices($services)
163 {
164 foreach ($services as $serviceClassname => $serviceConfig) {
165 $this->singleton(
166 $serviceClassname,
167 function ($container) use ($serviceClassname, $serviceConfig) {
168 $serviceInstance = new $serviceClassname();
169 if (method_exists($serviceInstance, 'bindConfig')) {
170 $serviceInstance->bindConfig($serviceConfig);
171 }
172
173 if (in_array(ServiceTrait::class, class_uses($serviceInstance))) {
174 $serviceInstance->setContainer($container);
175 $serviceInstance->init();
176 }
177
178 return $serviceInstance;
179 }
180 );
181 }
182 }
183
184 /** @noinspection PhpFullyQualifiedNameUsageInspection */
185 /**
186 * @param string $alias
187 *
188 * @return mixed
189 *
190 * @throws \Illuminate\Contracts\Container\BindingResolutionException
191 */
192 public function getService($alias)
193 {
194 return $this->make($alias);
195 }
196
197 /** @noinspection PhpFullyQualifiedNameUsageInspection */
198 /**
199 * Get the `view` service
200 *
201 * @return ViewService
202 * @throws \Illuminate\Contracts\Container\BindingResolutionException
203 */
204 public static function getServiceView()
205 {
206 return static::getInstance()->getService(ViewService::class);
207 }
208
209 /**
210 * @param $config
211 *
212 * @throws Exception
213 */
214 public static function initInstanceWithConfig($config)
215 {
216 if (is_null(static::$instance)) {
217 static::setInstance(new static($config));
218 }
219
220 // phpcs:ignore PSR2.ControlStructures.ControlStructureSpacing.SpacingAfterOpenBrace
221 if (!static::getInstance() instanceof static) {
222 throw new Exception('No plugin initialized.');
223 }
224 }
225
226 /**
227 * Initialize all needed things for this plugin: hooks, assignments...
228 */
229 public function initPlugin(): void
230 {
231 add_action('init', [$this, 'checkWooCommerceExistence']);
232 if (!class_exists('WooCommerce')) {
233 return;
234 }
235
236 // Load text domain
237 add_action('init', [$this, 'tamaraLoadTextDomain']);
238
239 // Register new Tamara custom statuses
240 add_action('init', [$this, 'registerTamaraCustomOrderStatuses']);
241
242 // For Admin
243 add_action('admin_enqueue_scripts', [$this, 'enqueueAdminSettingScripts']);
244
245 // Handle refund when a refund is created
246 add_action('woocommerce_create_refund', [$this, 'tamaraRefundPayment'], 10, 2);
247
248 // Add Tamara custom statuses to wc order status list
249 add_filter('wc_order_statuses', [$this, 'addTamaraCustomOrderStatuses']);
250
251 // Add note on Refund
252 add_action('woocommerce_order_item_add_action_buttons', [$this, 'addRefundNote']);
253
254 add_filter('woocommerce_rest_prepare_shop_order_object', [$this, 'updateTamaraCheckoutDataToOrder'], 10, 3);
255
256 add_action('init', [$this, 'addCustomRewriteRules']);
257 add_action('init', [$this, 'addTamaraAuthoriseFailedMessage'], 1000);
258 add_action('parse_request', [$this, 'handleTamaraApi'], 1000);
259 add_action('wp_enqueue_scripts', [$this, 'enqueueScripts']);
260 // add_filter('woocommerce_checkout_fields', [$this, 'adjustBillingPhoneDescription']);
261 add_filter('woocommerce_payment_gateways', [$this, 'registerTamaraPaymentGateway']);
262 add_filter('woocommerce_available_payment_gateways', [$this, 'adjustTamaraPaymentTypesOnCheckout'], 9998, 1);
263 add_action('woocommerce_update_options_checkout_'.static::TAMARA_GATEWAY_ID, [$this, 'onSaveSettings'], 10, 1);
264 add_action($this->getTamaraPopupWidgetPosition(), [$this, 'showTamaraProductPopupWidget']);
265 add_action($this->getTamaraCartPopupWidgetPosition(), [$this, 'showTamaraCartProductPopupWidget']);
266 add_action('wp_ajax_tamara_perform_cron', [$this, 'performCron']);
267 add_action('wp_ajax_tamara-authorise', [$this, 'tamaraAuthoriseHandler']);
268 add_action('wp_ajax_nopriv_tamara-authorise', [$this, 'tamaraAuthoriseHandler']);
269 add_action('wp_head', [$this, 'addTamaraGeneratorMeta']);
270 add_filter('script_loader_tag', [$this, 'addDeferToScriptTags'], 10, 3);
271 add_action('woocommerce_checkout_update_order_review', [$this, 'getUpdatedPhoneNumberOnCheckout']);
272
273 add_action('admin_footer', [$this, 'addCronJobTriggerScript']);
274
275 add_shortcode('tamara_show_popup', [$this, 'tamaraProductPopupWidget']);
276 add_shortcode('tamara_show_cart_popup', [$this, 'tamaraCartPopupWidget']);
277 add_shortcode('tamara_authorise_order', [$this, 'doAuthoriseOrderAction']);
278
279 // For Rest Api
280 add_filter('rest_pre_dispatch', [$this, 'populateRestApiRequest'], 1, 3);
281
282 // Update Settings Url in admin for Pay By Instalments
283 add_action('admin_head', [$this, 'updatePayByInstalmentSettingUrl']);
284 add_filter('woocommerce_billing_fields', [$this, 'forceRequireBillingPhone'], 1001, 2);
285
286 add_action('wp_ajax_tamara-get-instalment-plan', [$this, 'getInstalmentPlanAccordingToProductVariation']);
287 add_action('wp_ajax_nopriv_tamara-get-instalment-plan', [$this, 'getInstalmentPlanAccordingToProductVariation']);
288
289 add_action('wp_ajax_update-tamara-checkout-params', [$this, 'updateTamaraCheckoutParams']);
290 add_action('wp_ajax_nopriv_update-tamara-checkout-params', [$this, 'updateTamaraCheckoutParams']);
291
292 add_action('wp_loaded', [$this, 'overrideWcClearCart'], 0);
293 add_action('wp_loaded', [$this, 'cancelOrder'], 21);
294 add_action('template_redirect', [$this, 'maybeAuthoriseTamaraOrderOnOrderReceivedPage'], 5);
295
296 // Add Tamara Note on Order Received page
297 add_filter('woocommerce_thankyou_order_received_text', [$this, 'tamaraOrderReceivedText'], 10, 2);
298 add_filter('woocommerce_my_account_my_orders_actions', [$this, 'removeTamaraPayOrderActionOnOrderReceived'], 10, 2);
299
300 }
301
302 /**
303 * Populate Rest Api Request
304 *
305 * @param mixed $result
306 * @param \WP_REST_Server $restApiServer
307 * @param \WP_REST_Request $restApiRequest
308 *
309 * @return mixed
310 */
311 public function populateRestApiRequest($result, $restApiServer, $restApiRequest)
312 {
313 $this->setRestApiRequest($restApiRequest);
314
315 return $result;
316 }
317
318 /** @noinspection PhpFullyQualifiedNameUsageInspection */
319 /**
320 * Add Tamara Note after successful payment
321 *
322 * @param string $str
323 * @param \Automattic\WooCommerce\Admin\Overrides\Order $order
324 *
325 * @return string
326 *
327 * @throws \Illuminate\Contracts\Container\BindingResolutionException
328 */
329 public function tamaraOrderReceivedText($str, $order)
330 {
331 if (empty($order)) {
332 return $str;
333 }
334
335 if ($this->isTamaraOrder($order)) {
336 $order = wc_get_order($order->get_id());
337 if (!$order instanceof \WC_Order) {
338 return $str;
339 }
340
341 $showPayButton = $order->has_status('pending') && !$this->isOrderAuthorised($order->get_id());
342 $tamaraOrderReceivedHtml = $this->getServiceView()->render('views/woocommerce/checkout/tamara-order-received-button',
343 [
344 'textDomain' => 'tamara-checkout',
345 'showPayButton' => $showPayButton,
346 ]);
347
348 return $str.$tamaraOrderReceivedHtml;
349 }
350
351 return $str;
352 }
353
354 /**
355 * Remove the WooCommerce "Pay" action from order details on the thank-you page
356 * once the Tamara order no longer needs payment.
357 *
358 * @param array $actions
359 * @param WC_Order $order
360 *
361 * @return array
362 */
363 public function removeTamaraPayOrderActionOnOrderReceived($actions, $order)
364 {
365 if (!$order instanceof \WC_Order || !is_order_received_page() || !$this->isTamaraOrder($order)) {
366 return $actions;
367 }
368
369 $order = wc_get_order($order->get_id());
370 if (!$order instanceof \WC_Order) {
371 return $actions;
372 }
373
374 if ($this->isOrderAuthorised($order->get_id()) || !$order->has_status('pending')) {
375 unset($actions['pay']);
376 }
377
378 return $actions;
379 }
380
381 /**
382 * Handle Tamara log message
383 *
384 * @param string $message
385 *
386 */
387 public function logMessage($message)
388 {
389 if ($this->isCustomLogMessageEnabled()) {
390 if (is_array($message)) {
391 $message = json_encode($message);
392 }
393 $fileHandle = fopen($this->logMessageFilePath(), "a");
394 fwrite($fileHandle, "[".gmdate('Y-m-d h:i:s')."] ".$message."\n");
395 fclose($fileHandle);
396 }
397 }
398
399 /**
400 * Update order status and add order note wrapper
401 *
402 * @param WC_Order $wcOrder
403 * @param string $orderNote
404 * @param string $newOrderStatus
405 * @param string $updateOrderStatusNote
406 *
407 */
408 public function updateOrderStatusAndAddOrderNote($wcOrder, $orderNote, $newOrderStatus, $updateOrderStatusNote)
409 {
410 if ($wcOrder) {
411 $this->logMessage(sprintf("Tamara - Prepare to Update Order Status - Order ID: %s, Order Note: %s, new order status: %s, order status note: %s", $wcOrder->get_id(), $orderNote, $newOrderStatus, $updateOrderStatusNote));
412 try {
413 $wcOrder->add_order_note($orderNote);
414 $wcOrder->update_status($newOrderStatus, $updateOrderStatusNote, true);
415 } catch (Exception $exception) {
416 $this->logMessage(sprintf("Tamara - Failed to Update Order Status - Order ID: %s, Order Note: %s, new order status: %s, order status note: %s. Error Message: %s", $wcOrder->get_id(), $orderNote, $newOrderStatus, $updateOrderStatusNote, $exception->getMessage()));
417 }
418 }
419 }
420
421 /** @noinspection PhpFullyQualifiedNameUsageInspection */
422 /**
423 * Get WC Tamara Gateway Pay By Later class
424 *
425 * @return WCTamaraGateway
426 *
427 * @throws \Illuminate\Contracts\Container\BindingResolutionException
428 */
429 public function getWCTamaraGatewayService()
430 {
431 return $this->getService(WCTamaraGateway::class);
432 }
433
434 /** @noinspection PhpFullyQualifiedNameUsageInspection */
435 /**
436 * Get WC Tamara Gateway Pay Now class
437 *
438 * @return WCTamaraGatewayPayNow
439 *
440 * @throws \Illuminate\Contracts\Container\BindingResolutionException
441 */
442 public function getWCTamaraGatewayPayNowService()
443 {
444 return $this->getService(WCTamaraGatewayPayNow::class);
445 }
446
447 /** @noinspection PhpFullyQualifiedNameUsageInspection */
448 /**
449 * Get WC Tamara Gateway Pay By Instalments class
450 *
451 * @return WCTamaraGateway
452 *
453 * @throws \Illuminate\Contracts\Container\BindingResolutionException
454 */
455 public function getWCTamaraGatewayPayByInstalmentsService()
456 {
457 return $this->getService(WCTamaraGatewayPayByInstalments::class);
458 }
459
460 /** @noinspection PhpFullyQualifiedNameUsageInspection */
461 /**
462 * Get WC Tamara Gateway Pay In X class
463 *
464 * @param $instalment
465 *
466 * @return WCTamaraGateway
467 *
468 * @throws \Illuminate\Contracts\Container\BindingResolutionException
469 */
470 public function getWCTamaraGatewayPayInXService($instalment)
471 {
472 $instalmentService = 'Tamara\Wp\Plugin\Services\WCTamaraGatewayPayIn'.$instalment;
473
474 return $this->getService($instalmentService);
475 }
476
477 /** @noinspection PhpFullyQualifiedNameUsageInspection */
478 /**
479 * Get WC Tamara Gateway Single Checkout class
480 *
481 * @return WCTamaraGateway
482 *
483 * @throws \Illuminate\Contracts\Container\BindingResolutionException
484 */
485 public function getWCTamaraGatewayCheckoutService()
486 {
487 return $this->getService(WCTamaraGatewayCheckout::class);
488 }
489
490 /** @noinspection PhpFullyQualifiedNameUsageInspection */
491 /**
492 * Get WC Tamara Gateway Pay Next Month class
493 *
494 * @return WCTamaraGateway
495 *
496 * @throws \Illuminate\Contracts\Container\BindingResolutionException
497 */
498 public function getWCTamaraGatewayPayNextMonthService()
499 {
500 return $this->getService(WCTamaraGatewayPayNextMonth::class);
501 }
502
503 /**
504 * Get Tamara Popup Widget postion
505 */
506 public function getTamaraPopupWidgetPosition()
507 {
508 return $this->getWCTamaraGatewayOptions()['popup_widget_position'] ?? 'woocommerce_before_add_to_cart_form';
509 }
510
511 /**
512 * Get Tamara Cart Popup Widget postion
513 */
514 public function getTamaraCartPopupWidgetPosition()
515 {
516 return $this->getWCTamaraGatewayOptions()['cart_popup_widget_position'] ?? 'woocommerce_proceed_to_checkout';
517 }
518
519 /**
520 * Check if Payment type Pay By Later is enabled in admin settings
521 */
522 public function isPayByLaterEnabled()
523 {
524 return 'yes' === ($this->getWCTamaraGatewayOptions()['pay_by_later_enabled'] ?? 'no');
525 }
526
527 /**
528 * Check if Payment type Pay Now is enabled in admin settings
529 */
530 public function isPayNowEnabled()
531 {
532 return 'yes' === ($this->getWCTamaraGatewayOptions()['pay_now_enabled'] ?? 'no');
533 }
534
535 /**
536 * Check if Payment type Pay By Instalments is enabled in admin settings
537 */
538 public function isPayByInstalmentsEnabled()
539 {
540 return 'yes' === ($this->getWCTamaraGatewayOptions()['pay_by_instalments_enabled'] ?? 'no');
541 }
542
543 /**
544 * Check if a specific Pay In X payment type is enabled in admin settings
545 *
546 * @param $instalment
547 * @param $countryCode
548 *
549 * @return bool
550 */
551 public function isPayInXEnabled($instalment, $countryCode)
552 {
553 return 'yes' === ($this->getWCTamaraGatewayOptions()['pay_in_'.$instalment.'_'.$countryCode] ?? 'no');
554 }
555
556 /**
557 * Check if Tamara Gateway is enabled in admin settings
558 */
559 public function isTamaraGatewayEnabled()
560 {
561 return 'yes' === ($this->getWCTamaraGatewayOptions()['enabled'] ?? 'no');
562 }
563
564 /**
565 * Check if Tamara custom log message is enabled in admin settings
566 */
567 public function isCustomLogMessageEnabled()
568 {
569 return 'yes' === ($this->getWCTamaraGatewayOptions()['custom_log_message_enabled'] ?? 'no');
570 }
571
572 /**
573 * Check if Tamara force billing phone option is enabled in admin settings
574 */
575 public function isForceBillingPhoneEnabled()
576 {
577 return 'yes' === ($this->getWCTamaraGatewayOptions()['force_billing_phone'] ?? 'no');
578 }
579
580 /**
581 * Check if Cronjob is enabled in admin settings
582 */
583 public function isCronjobEnabled()
584 {
585 return 'yes' === ($this->getWCTamaraGatewayOptions()['crobjob_enabled'] ?? 'no');
586 }
587
588 /**
589 * Check if Tamara Pay Later popup widget is enabled in admin settings
590 */
591 public function isPayLaterPDPEnabled()
592 {
593 return 'yes' === ($this->getWCTamaraGatewayOptions()['pay_later_popup_widget_enabled'] ?? 'no');
594 }
595
596 /**
597 * Check if Always Show Popup Widget is enabled in admin settings
598 */
599 public function isAlwaysShowWidgetPopupEnabled()
600 {
601 return 'yes' === ($this->getWCTamaraGatewayOptions()['always_show_popup_widget_enabled'] ?? 'no');
602 }
603
604 /**
605 * Check if Showing Popup Widget is disabled in admin settings
606 */
607 public function isWidgetPopupDisabled()
608 {
609 return 'yes' === ($this->getWCTamaraGatewayOptions()['popup_widget_disabled'] ?? 'no');
610 }
611
612 /**
613 * Check if Showing Popup Widget in Cart page is disabled in admin settings
614 */
615 public function isCartWidgetPopupDisabled()
616 {
617 return 'yes' === ($this->getWCTamaraGatewayOptions()['cart_popup_widget_disabled'] ?? 'no');
618 }
619
620 /**
621 * Check if Credit Precheck is enabled in admin settings
622 */
623 public function isCreditPrecheckEnabled()
624 {
625 return 'yes' === ($this->getWCTamaraGatewayOptions()['credit_precheck_enabled'] ?? 'no');
626 }
627
628 /**
629 * Get WC Tamara Gateway options
630 */
631 public function getWCTamaraGatewayOptions()
632 {
633 return get_option($this->getWCTamaraGatewayOptionKey(), null);
634 }
635
636 /**
637 * Get WC Tamara Gateway options
638 */
639 public function getWCTamaraGatewayOptionKey()
640 {
641 return 'woocommerce_'.static::TAMARA_GATEWAY_ID.'_settings';
642 }
643
644 /** @noinspection PhpFullyQualifiedNameUsageInspection */
645 /**
646 * Get on save settings method from WC Tamara Gateway
647 *
648 * @param $settings
649 *
650 * @return void
651 *
652 * @throws \Illuminate\Contracts\Container\BindingResolutionException
653 */
654 public function onSaveSettings($settings)
655 {
656 return $this->getWCTamaraGatewayService()->onSaveSettings($settings);
657 }
658
659 /**
660 * Tamara Log File Path
661 */
662 public function logMessageFilePath()
663 {
664 $upload_dir = wp_upload_dir();
665 $plugin_dir = $upload_dir['basedir'] . '/tamara-checkout';
666 if ( ! file_exists( $plugin_dir ) ) {
667 wp_mkdir_p( $plugin_dir );
668 }
669 return $plugin_dir . '/' . static::MESSAGE_LOG_FILE_NAME;
670 }
671
672 /**
673 * Tamara Log File Url
674 */
675 public function logMessageFileUrl()
676 {
677 return wp_upload_dir()['baseurl'].'/'.static::MESSAGE_LOG_FILE_NAME;
678 }
679
680 /** @noinspection PhpFullyQualifiedNameUsageInspection */
681 /**
682 * Force pending capture payments within 180 days to be captured
683 *
684 * @throws \Illuminate\Contracts\Container\BindingResolutionException
685 */
686 public function forceCaptureTamaraOrder()
687 {
688 $tamaraCapturePaymentStatus = $this->getWCTamaraGatewayService()->tamaraStatus['payment_capture'] ?? 'wc-completed';
689 $customerOrders = [
690 'fields' => 'ids',
691 'post_type' => 'shop_order',
692 'post_status' => $tamaraCapturePaymentStatus,
693 'date_query' => [
694 'before' => date('Y-m-d', strtotime('-14 days')),
695 'after' => date('Y-m-d', strtotime('-180 days')),
696 'inclusive' => true,
697 ],
698 'meta_query' => [
699 'relation' => 'AND',
700 [
701 'key' => '_tamara_order_id',
702 'compare' => 'EXISTS',
703 ],
704 [
705 'key' => '_tamara_capture_id',
706 'compare' => 'NOT EXISTS',
707 ],
708 [
709 'key' => '_tamara_force_capture_checked',
710 'compare' => 'NOT EXISTS',
711 ],
712 ],
713 ];
714
715 $customerOrdersQuery = new \WP_Query($customerOrders);
716
717 $wcOrderIds = $customerOrdersQuery->posts;
718
719 foreach ($wcOrderIds as $wcOrderId) {
720 update_post_meta($wcOrderId, '_tamara_force_capture_checked', 1);
721
722 if (static::TAMARA_FULLY_CAPTURED_STATUS === TamaraCheckout::getInstance()->getTamaraOrderStatus($wcOrderId)) {
723 $tamaraCaptureId = $this->getWCTamaraGatewayService()->getTamaraCaptureId($wcOrderId);
724 update_post_meta($wcOrderId, '_tamara_capture_id', $tamaraCaptureId);
725
726 return true;
727 } else {
728 $this->getWCTamaraGatewayService()->captureWcOrder($wcOrderId);
729 }
730 }
731 }
732
733 /**
734 * Force pending authorise payments within 180 days to be synced with Tamara
735 *
736 */
737 public function forceAuthoriseTamaraOrder()
738 {
739 $toAuthoriseStatus = 'wc-pending';
740 $customerOrders = [
741 'fields' => 'ids',
742 'post_type' => 'shop_order',
743 'post_status' => $toAuthoriseStatus,
744 'date_query' => [
745 'before' => date('Y-m-d', strtotime('-1 second')),
746 'after' => date('Y-m-d', strtotime('-180 days')),
747 'inclusive' => true,
748 ],
749 'meta_query' => [
750 'relation' => 'AND',
751 [
752 'key' => '_tamara_checkout_session_id',
753 'compare' => 'EXISTS',
754 ],
755 [
756 'key' => '_tamara_order_id',
757 'compare' => 'NOT EXISTS',
758 ],
759 [
760 'key' => '_tamara_force_authorise_checked',
761 'compare' => 'NOT EXISTS',
762 ],
763 ],
764 ];
765
766 $customerOrdersQuery = new \WP_Query($customerOrders);
767
768 $wcOrderIds = $customerOrdersQuery->posts;
769
770 foreach ($wcOrderIds as $wcOrderId) {
771 update_post_meta($wcOrderId, '_tamara_force_authorise_checked', 1);
772 if (!$this->isOrderAuthorised($wcOrderId)) {
773 $this->authoriseOrder($wcOrderId);
774 }
775 }
776 }
777
778 /**
779 * Add Tamara Refund Note
780 *
781 * @param WC_Order $order
782 */
783 public function addRefundNote($order)
784 {
785 if ($this->isTamaraGateway($order->get_payment_method())) {
786 echo '<br>' . esc_html(__('This order is paid via Tamara Pay Later.', 'tamara-checkout'));
787 echo '<br>' . '<strong>' . esc_html(__('You need to refund the full shipping amount.',
788 'tamara-checkout')) . '</strong>';
789 }
790 }
791
792 /**
793 * Register Tamara new statuses
794 */
795 public function registerTamaraCustomOrderStatuses()
796 {
797 register_post_status('wc-tamara-p-canceled', [
798 'label' => _x('Tamara Payment Cancelled', 'Order status', 'tamara-checkout'),
799 'public' => true,
800 'exclude_from_search' => false,
801 'show_in_admin_all_list' => true,
802 'show_in_admin_status_list' => true,
803 // translators: %s: Number of orders
804 'label_count' => _n_noop('Tamara Payment Cancelled <span class="count">(%s)</span>',
805 'Tamara Payment Cancelled <span class="count">(%s)</span>', 'tamara-checkout'),
806 ]);
807
808 register_post_status('wc-tamara-p-failed', [
809 'label' => _x('Tamara Payment Failed', 'Order status', 'tamara-checkout'),
810 'public' => true,
811 'exclude_from_search' => false,
812 'show_in_admin_all_list' => true,
813 'show_in_admin_status_list' => true,
814 // translators: %s: Number of orders
815 'label_count' => _n_noop('Tamara Payment Failed <span class="count">(%s)</span>',
816 'Tamara Payment Failed <span class="count">(%s)</span>', 'tamara-checkout'),
817 ]);
818
819 register_post_status('wc-tamara-c-failed', [
820 'label' => _x('Tamara Capture Failed', 'Order status', 'tamara-checkout'),
821 'public' => true,
822 'exclude_from_search' => false,
823 'show_in_admin_all_list' => true,
824 'show_in_admin_status_list' => true,
825 // translators: %s: Number of orders
826 'label_count' => _n_noop('Tamara Capture Failed <span class="count">(%s)</span>',
827 'Tamara Capture Failed <span class="count">(%s)</span>', 'tamara-checkout'),
828 ]);
829
830 register_post_status('wc-tamara-a-done', [
831 'label' => _x('Tamara Authorise Success', 'Order status', 'tamara-checkout'),
832 'public' => true,
833 'exclude_from_search' => false,
834 'show_in_admin_all_list' => true,
835 'show_in_admin_status_list' => true,
836 // translators: %s: Number of orders
837 'label_count' => _n_noop('Tamara Authorise Success <span class="count">(%s)</span>',
838 'Tamara Authorise Success <span class="count">(%s)</span>', 'tamara-checkout'),
839 ]);
840
841 register_post_status('wc-tamara-a-failed', [
842 'label' => _x('Tamara Authorise Failed', 'Order status', 'tamara-checkout'),
843 'public' => true,
844 'exclude_from_search' => false,
845 'show_in_admin_all_list' => true,
846 'show_in_admin_status_list' => true,
847 // translators: %s: Number of orders
848 'label_count' => _n_noop('Tamara Authorise Failed <span class="count">(%s)</span>',
849 'Tamara Authorise Failed <span class="count">(%s)</span>', 'tamara-checkout'),
850 ]);
851
852 register_post_status('wc-tamara-o-canceled', [
853 'label' => _x('Tamara Order Cancelled', 'Order status', 'tamara-checkout'),
854 'public' => true,
855 'exclude_from_search' => false,
856 'show_in_admin_all_list' => true,
857 'show_in_admin_status_list' => true,
858 // translators: %s: Number of orders
859 'label_count' => _n_noop('Tamara Order Cancelled <span class="count">(%s)</span>',
860 'Tamara Order Cancelled <span class="count">(%s)</span>', 'tamara-checkout'),
861 ]);
862
863 register_post_status('wc-tamara-p-capture', [
864 'label' => _x('Tamara Payment Capture', 'Order status', 'tamara-checkout'),
865 'public' => true,
866 'exclude_from_search' => false,
867 'show_in_admin_all_list' => true,
868 'show_in_admin_status_list' => true,
869 // translators: %s: Number of orders
870 'label_count' => _n_noop('Tamara Payment Capture <span class="count">(%s)</span>',
871 'Tamara Payment Capture <span class="count">(%s)</span>', 'tamara-checkout'),
872 ]);
873 }
874
875 /**
876 * Add Tamara Statuses to the list of WC Order statuses
877 *
878 * @param array $order_statuses
879 *
880 * @return array $order_statuses
881 */
882 public function addTamaraCustomOrderStatuses($order_statuses)
883 {
884 $order_statuses['wc-tamara-p-canceled'] = _x('Tamara Payment Cancelled', 'Order status',
885 'tamara-checkout');
886 $order_statuses['wc-tamara-p-failed'] = _x('Tamara Payment Failed', 'Order status',
887 'tamara-checkout');
888 $order_statuses['wc-tamara-c-failed'] = _x('Tamara Capture Failed', 'Order status',
889 'tamara-checkout');
890 $order_statuses['wc-tamara-a-done'] = _x('Tamara Authorise Done', 'Order status',
891 'tamara-checkout');
892 $order_statuses['wc-tamara-a-failed'] = _x('Tamara Authorise Failed', 'Order status',
893 'tamara-checkout');
894 $order_statuses['wc-tamara-o-canceled'] = _x('Tamara Order Cancelled', 'Order status',
895 'tamara-checkout');
896 $order_statuses['wc-tamara-p-capture'] = _x('Tamara Payment Capture', 'Order status',
897 'tamara-checkout');
898
899 return $order_statuses;
900 }
901
902 /**
903 * Localize the plugin
904 */
905 public function tamaraLoadTextDomain()
906 {
907 load_plugin_textdomain(
908 'tamara-checkout',
909 false,
910 dirname(plugin_basename($this->pluginFilename)).'/languages'
911 );
912 }
913
914 /** @noinspection PhpFullyQualifiedNameUsageInspection */
915 /**
916 * Handle process for Tamara endpoint slug returned
917 *
918 * @param WP $wp
919 *
920 * @throws \Illuminate\Contracts\Container\BindingResolutionException
921 */
922 public function handleTamaraApi($wp)
923 {
924 $pagename = $wp->query_vars['pagename'] ?? null;
925 $tamaraPageSlugs = [
926 WCTamaraGateway::IPN_SLUG,
927 WCTamaraGateway::WEBHOOK_SLUG,
928 WCTamaraGateway::PAYMENT_SUCCESS_SLUG,
929 WCTamaraGateway::PAYMENT_CANCEL_SLUG,
930 WCTamaraGateway::PAYMENT_FAIL_SLUG,
931 ];
932 if (in_array($pagename, $tamaraPageSlugs)) {
933 $this->logMessage(sprintf('Pagename: %s', $pagename));
934 }
935
936 if (WCTamaraGateway::IPN_SLUG === $pagename) {
937 /** @var TamaraNotificationService $tamara_notification_service */
938 $tamara_notification_service = $this->getService(TamaraNotificationService::class);
939 $tamara_notification_service->handleIpnRequest();
940 exit;
941 } // Handle webhook
942 elseif (WCTamaraGateway::WEBHOOK_SLUG === $pagename) {
943 /** @var TamaraNotificationService $tamara_notification_service */
944 $tamara_notification_service = $this->getService(TamaraNotificationService::class);
945 $tamara_notification_service->handleWebhook();
946 exit;
947 } elseif (WCTamaraGateway::PAYMENT_CANCEL_SLUG === $pagename) {
948 $this->handleTamaraCancelUrl();
949 do_action('after_tamara_cancel');
950 exit;
951 } elseif (WCTamaraGateway::PAYMENT_FAIL_SLUG === $pagename) {
952 $this->handleTamaraFailureUrl();
953 do_action('after_tamara_failure');
954 exit;
955 }
956 }
957
958 /**
959 * Detect if an order is authorised or not
960 *
961 * @param $wcOrderId
962 *
963 * @return bool
964 */
965 public function isOrderAuthorised($wcOrderId)
966 {
967 return !!get_post_meta($wcOrderId, 'tamara_authorized', true);
968 }
969
970 /**
971 * Prevent an order is cancelled from FE if its payment has been authorised from Tamara
972 *
973 * @param WC_Order $wcOrder
974 * @param int $wcOrderId
975 *
976 */
977 protected function preventOrderCancelAction($wcOrder, $wcOrderId)
978 {
979 $orderNote = 'This order can not be cancelled because the payment was authorised from Tamara. Order ID: '.$wcOrderId;
980 if ($wcOrder instanceof \WC_Order) {
981 $wcOrder->add_order_note($orderNote);
982 }
983 $this->logMessage($orderNote);
984 wp_safe_redirect(wc_get_cart_url());
985 exit;
986 }
987
988 /**
989 * Output Tamara Checkout generator meta tag in document head.
990 */
991 public function addTamaraGeneratorMeta()
992 {
993 echo '<meta name="generator" content="TamaraCheckout '.esc_attr($this->version).'" />'."\n";
994 }
995
996 /**
997 * Add defer attribute to Tamara summary widget script tag.
998 *
999 * @param string $tag
1000 * @param string $handle
1001 * @param string $src
1002 *
1003 * @return string
1004 */
1005 public function addDeferToScriptTags($tag, $handle, $src)
1006 {
1007 if ('tamara-summary-widget' === $handle) {
1008 return str_replace('<script ', '<script defer ', $tag);
1009 }
1010
1011 return $tag;
1012 }
1013
1014 /** @noinspection PhpFullyQualifiedNameUsageInspection */
1015 /**
1016 * Detect if payment for WC order has been approved from Tamara
1017 *
1018 * @param int $wcOrderId
1019 *
1020 * @return bool
1021 *
1022 * @throws \Illuminate\Contracts\Container\BindingResolutionException
1023 */
1024 protected function isOrderTamaraApproved($wcOrderId)
1025 {
1026 $tamaraOrder = $this->getTamaraOrderByWcOrderId($wcOrderId);
1027 if ($tamaraOrder && ('approved' === $tamaraOrder->getStatus() || 'authorized' === $tamaraOrder->getStatus())) {
1028 return true;
1029 }
1030
1031 return false;
1032 }
1033
1034 /** @noinspection PhpFullyQualifiedNameUsageInspection */
1035 /**
1036 * Get Tamara order id by WC order Id
1037 *
1038 * @param int $wcOrderId
1039 *
1040 * @return string
1041 *
1042 * @throws \Illuminate\Contracts\Container\BindingResolutionException
1043 */
1044 protected function getTamaraOrderId($wcOrderId)
1045 {
1046 $tamaraOrder = $this->getTamaraOrderByWcOrderId($wcOrderId);
1047 if ($tamaraOrder) {
1048 return $tamaraOrder->getOrderId();
1049 }
1050
1051 return null;
1052 }
1053
1054 /** @noinspection PhpFullyQualifiedNameUsageInspection */
1055 /**
1056 * Get Tamara order by WC order Id
1057 *
1058 * @param int $wcOrderId
1059 *
1060 * @return null|Dependencies\Tamara\Response\Order\GetOrderByReferenceIdResponse
1061 *
1062 * @throws \Illuminate\Contracts\Container\BindingResolutionException
1063 */
1064 public function getTamaraOrderByWcOrderId($wcOrderId)
1065 {
1066 $tamaraClient = $this->getWCTamaraGatewayService()->tamaraClient;
1067 $tamaraOrderId = $this->getStoredTamaraOrderId($wcOrderId);
1068
1069 if ($tamaraOrderId !== '') {
1070 try {
1071 $tamaraOrderResponse = $tamaraClient->getOrder(new GetOrderRequest($tamaraOrderId));
1072 $this->logMessage(sprintf("Tamara Get Order by ID Response: %s", print_r($tamaraOrderResponse, true)));
1073 if ($tamaraOrderResponse->isSuccess()) {
1074 $this->getWCTamaraGatewayService()->syncTamaraOrderMetaFromRemoteOrder($wcOrderId, $tamaraOrderResponse);
1075
1076 return $tamaraOrderResponse;
1077 }
1078 } catch (Exception $tamaraOrderByIdException) {
1079 $this->logMessage(
1080 sprintf(
1081 "Tamara Get Order by ID Failed Response.\nError message: '%s'.\nTrace: %s",
1082 $tamaraOrderByIdException->getMessage(),
1083 $tamaraOrderByIdException->getTraceAsString()
1084 )
1085 );
1086 }
1087 }
1088
1089 try {
1090 $tamaraOrderResponse = $tamaraClient->getOrderByReferenceId(new GetOrderByReferenceIdRequest($wcOrderId));
1091 $this->logMessage(sprintf("Tamara Get Order by Reference ID Response: %s", print_r($tamaraOrderResponse, true)));
1092 if ($tamaraOrderResponse->isSuccess()) {
1093 $this->getWCTamaraGatewayService()->syncTamaraOrderMetaFromRemoteOrder($wcOrderId, $tamaraOrderResponse);
1094
1095 return $tamaraOrderResponse;
1096 }
1097 } catch (Exception $tamaraOrderResponseException) {
1098 $this->logMessage(
1099 sprintf(
1100 "Tamara Get Order by Reference ID Failed Response.\nError message: '%s'.\nTrace: %s",
1101 $tamaraOrderResponseException->getMessage(),
1102 $tamaraOrderResponseException->getTraceAsString()
1103 )
1104 );
1105 }
1106
1107 return null;
1108 }
1109
1110 /**
1111 * Read a saved Tamara order id from WooCommerce order meta.
1112 *
1113 * @param int $wcOrderId
1114 *
1115 * @return string
1116 */
1117 protected function getStoredTamaraOrderId($wcOrderId)
1118 {
1119 $tamaraOrderId = get_post_meta($wcOrderId, '_tamara_order_id', true);
1120 if (empty($tamaraOrderId)) {
1121 $tamaraOrderId = get_post_meta($wcOrderId, 'tamara_order_id', true);
1122 }
1123
1124 return !empty($tamaraOrderId) ? (string) $tamaraOrderId : '';
1125 }
1126
1127 /**
1128 * Authorise/sync the Tamara order during the order-received page request, before templates render.
1129 */
1130 public function maybeAuthoriseTamaraOrderOnOrderReceivedPage()
1131 {
1132 if (!$this->getWCTamaraGatewayService()->isTamaraCheckoutOrderReceivedPage()) {
1133 return;
1134 }
1135
1136 $wcOrderId = $this->resolveOrderReceivedWcOrderId();
1137 if (!$wcOrderId) {
1138 return;
1139 }
1140
1141 $wcOrder = wc_get_order($wcOrderId);
1142 if (!$wcOrder instanceof \WC_Order || !$this->isTamaraGateway($wcOrder->get_payment_method())) {
1143 return;
1144 }
1145
1146 $orderKey = $this->resolveOrderReceivedOrderKey();
1147
1148 if (!$this->verifyOrderOwnership($wcOrder, $orderKey) || !$this->isTamaraOrder($wcOrder)) {
1149 return;
1150 }
1151
1152 if ($this->isOrderAuthorised($wcOrderId) || !$wcOrder->has_status('pending')) {
1153 $this->orderReceivedAuthoriseResults[$wcOrderId] = true;
1154 $this->refreshWcOrderCache($wcOrderId);
1155
1156 return;
1157 }
1158
1159 $this->orderReceivedAuthoriseResults[$wcOrderId] = $this->authoriseOrder($wcOrderId);
1160 $this->refreshWcOrderCache($wcOrderId);
1161 }
1162
1163 /**
1164 * @param int $wcOrderId
1165 *
1166 * @return bool
1167 */
1168 public function wasOrderReceivedAuthoriseSuccessful($wcOrderId)
1169 {
1170 return !empty($this->orderReceivedAuthoriseResults[$wcOrderId]);
1171 }
1172
1173 /**
1174 * @return int
1175 */
1176 public function resolveOrderReceivedWcOrderId()
1177 {
1178 global $wp;
1179
1180 $wcOrderId = absint(wp_unslash($_GET['wcOrderId'] ?? 0));
1181 if (!$wcOrderId && !empty($wp->query_vars['order-received'])) {
1182 $wcOrderId = absint($wp->query_vars['order-received']);
1183 }
1184
1185 return $wcOrderId;
1186 }
1187
1188 /**
1189 * @return string
1190 */
1191 public function resolveOrderReceivedOrderKey()
1192 {
1193 if (isset($_GET['key'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1194 return wc_clean(wp_unslash($_GET['key']));
1195 }
1196
1197 if (isset($_GET['order'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1198 return wc_clean(wp_unslash($_GET['order']));
1199 }
1200
1201 return '';
1202 }
1203
1204 /**
1205 * @param int $wcOrderId
1206 */
1207 protected function refreshWcOrderCache($wcOrderId)
1208 {
1209 if (function_exists('wc_delete_shop_order_transients')) {
1210 wc_delete_shop_order_transients($wcOrderId);
1211 }
1212
1213 clean_post_cache($wcOrderId);
1214
1215 $wcOrder = wc_get_order($wcOrderId);
1216 if ($wcOrder instanceof \WC_Order) {
1217 $wcOrder->read_meta_data(true);
1218 }
1219 }
1220
1221 /**
1222 * If the order is pending payment, sync it with the remote Tamara order status.
1223 */
1224 public function tamaraAuthoriseHandler()
1225 {
1226 $wcOrderId = absint(wp_unslash($_POST['wcOrderId'] ?? 0));
1227 $orderKey = isset($_POST['order']) ? wc_clean(wp_unslash($_POST['order'])) : '';
1228 $wcOrder = $wcOrderId ? wc_get_order($wcOrderId) : false;
1229
1230 if (!$wcOrder instanceof \WC_Order || !$this->verifyOrderOwnership($wcOrder, $orderKey) || !$this->isTamaraOrder($wcOrder)) {
1231 wp_send_json(
1232 [
1233 'message' => 'authorise_failed',
1234 ],
1235 400
1236 );
1237 }
1238
1239 $authoriseSuccessResponse = [
1240 'message' => 'authorise_success',
1241 ];
1242
1243 if ($this->isOrderAuthorised($wcOrderId) || $this->authoriseOrder($wcOrderId)) {
1244 wp_send_json($authoriseSuccessResponse);
1245 }
1246
1247 wp_send_json(
1248 [
1249 'message' => 'authorise_failed',
1250 ],
1251 202
1252 );
1253 }
1254
1255 /**
1256 * Do authorise order with order id from payload returning from Tamara
1257 */
1258 public function doAuthoriseOrderAction()
1259 {
1260 $wcOrderId = absint(filter_input(INPUT_GET, 'wcOrderId', FILTER_SANITIZE_NUMBER_INT));
1261 $wcOrderId || $wcOrderId = absint(filter_input(INPUT_POST, 'wcOrderId', FILTER_SANITIZE_NUMBER_INT));
1262 $orderKey = isset($_REQUEST['order']) ? wc_clean(wp_unslash($_REQUEST['order'])) : '';
1263 $wcOrder = $wcOrderId ? wc_get_order($wcOrderId) : false;
1264
1265 if (!$this->verifyOrderOwnership($wcOrder, $orderKey) || !$this->isTamaraGateway($wcOrder->get_payment_method())) {
1266 return;
1267 }
1268
1269 $this->authoriseOrder($wcOrderId);
1270 }
1271
1272 /**
1273 * Build auth query args for public Tamara payment return URLs.
1274 *
1275 * Uses WooCommerce order key plus a long-lived HMAC (and WC cancel nonce) so return
1276 * requests can be proven to belong to the checkout that created the merchant URL.
1277 *
1278 * @param WC_Order $wcOrder
1279 * @param string $action cancel|fail|authorise
1280 *
1281 * @return array
1282 */
1283 public function getPaymentReturnAuthParams($wcOrder, $action)
1284 {
1285 $orderId = $wcOrder->get_id();
1286 $orderKey = $wcOrder->get_order_key();
1287
1288 return [
1289 'wcOrderId' => $orderId,
1290 'order' => $orderKey,
1291 'tamara_sig' => $this->createPaymentReturnSignature($orderId, $orderKey, $action),
1292 '_wpnonce' => wp_create_nonce('woocommerce-cancel_order'),
1293 ];
1294 }
1295
1296 /**
1297 * Create HMAC signature for a payment return URL.
1298 *
1299 * @param int $orderId
1300 * @param string $orderKey
1301 * @param string $action
1302 *
1303 * @return string
1304 */
1305 public function createPaymentReturnSignature($orderId, $orderKey, $action)
1306 {
1307 return hash_hmac(
1308 'sha256',
1309 absint($orderId).'|'.(string) $orderKey.'|'.(string) $action,
1310 wp_salt('nonce')
1311 );
1312 }
1313
1314 /**
1315 * Verify order key matches the WooCommerce order.
1316 *
1317 * @param WC_Order|false|null $wcOrder
1318 * @param string $orderKey
1319 *
1320 * @return bool
1321 */
1322 public function verifyOrderOwnership($wcOrder, $orderKey)
1323 {
1324 if (!$wcOrder instanceof \WC_Order) {
1325 return false;
1326 }
1327
1328 if ($orderKey !== '' && hash_equals($wcOrder->get_order_key(), (string) $orderKey)) {
1329 return true;
1330 }
1331
1332 $customerId = (int) $wcOrder->get_customer_id();
1333 if ($customerId > 0 && is_user_logged_in() && (int) get_current_user_id() === $customerId) {
1334 return true;
1335 }
1336
1337 return false;
1338 }
1339
1340 /**
1341 * Verify a public cancel/fail return request belongs to the order.
1342 *
1343 * Requires a matching order key and either a valid HMAC signature or WC cancel nonce.
1344 *
1345 * @param WC_Order $wcOrder
1346 * @param string $action
1347 *
1348 * @return bool
1349 */
1350 public function verifyPaymentReturnRequest($wcOrder, $action)
1351 {
1352 $orderKey = isset($_GET['order']) ? wc_clean(wp_unslash($_GET['order'])) : '';
1353 if (!$this->verifyOrderOwnership($wcOrder, $orderKey)) {
1354 return false;
1355 }
1356
1357 $sig = isset($_GET['tamara_sig']) ? wc_clean(wp_unslash($_GET['tamara_sig'])) : '';
1358 if ($sig !== '') {
1359 $expected = $this->createPaymentReturnSignature($wcOrder->get_id(), $wcOrder->get_order_key(), $action);
1360 if (hash_equals($expected, $sig)) {
1361 return true;
1362 }
1363 }
1364
1365 if (
1366 isset($_GET['_wpnonce']) &&
1367 wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'])), 'woocommerce-cancel_order')
1368 ) {
1369 return true;
1370 }
1371
1372 return false;
1373 }
1374
1375 /**
1376 * Whether an order may be moved to Tamara cancelled/failed from a public return URL.
1377 *
1378 * @param WC_Order $wcOrder
1379 *
1380 * @return bool
1381 */
1382 protected function canPubliclyCancelOrFailOrder($wcOrder)
1383 {
1384 if (!$this->isTamaraGateway($wcOrder->get_payment_method())) {
1385 return false;
1386 }
1387
1388 $validStatuses = apply_filters(
1389 'woocommerce_valid_order_statuses_for_cancel',
1390 ['pending', 'failed'],
1391 $wcOrder
1392 );
1393
1394 return $wcOrder->has_status($validStatuses);
1395 }
1396
1397 /**
1398 * Sync a pending WooCommerce order with the remote Tamara order status.
1399 *
1400 * @param int $wcOrderId
1401 *
1402 * @return bool true when the order was synced or already finalised, false when still pending
1403 */
1404 public function authoriseOrder($wcOrderId)
1405 {
1406 /** @var WC_Order $wcOrder */
1407 $wcOrder = wc_get_order($wcOrderId);
1408
1409 if (!$wcOrder) {
1410 return false;
1411 }
1412
1413 if ($this->isOrderAuthorised($wcOrderId) || $wcOrder->get_status() !== 'pending') {
1414 return true;
1415 }
1416
1417 try {
1418 $tamaraOrder = $this->getTamaraOrderByWcOrderId($wcOrderId);
1419 if (!$tamaraOrder) {
1420 return false;
1421 }
1422
1423 $tamaraOrderId = (string) $tamaraOrder->getOrderId();
1424 if ($tamaraOrderId === '') {
1425 return false;
1426 }
1427
1428 $this->getWCTamaraGatewayService()->updateTamaraOrderId($wcOrderId, $tamaraOrderId);
1429
1430 return $this->processTamaraOrderByRemoteStatus($wcOrder, $wcOrderId, $tamaraOrder, $tamaraOrderId);
1431 } catch (Exception $exception) {
1432 $this->logMessage(
1433 sprintf(
1434 "Tamara - Failed to sync pending order.\nError message: '%s'.\nTrace: %s",
1435 $exception->getMessage(),
1436 $exception->getTraceAsString()
1437 )
1438 );
1439 }
1440
1441 return false;
1442 }
1443
1444 /**
1445 * Apply WooCommerce order updates based on the remote Tamara order status.
1446 *
1447 * @param WC_Order $wcOrder
1448 * @param int $wcOrderId
1449 * @param mixed $tamaraOrder
1450 * @param string $tamaraOrderId
1451 *
1452 * @return bool
1453 *
1454 * @throws \Illuminate\Contracts\Container\BindingResolutionException
1455 */
1456 protected function processTamaraOrderByRemoteStatus($wcOrder, $wcOrderId, $tamaraOrder, $tamaraOrderId)
1457 {
1458 $tamaraOrderStatus = strtolower((string) $tamaraOrder->getStatus());
1459
1460 if ($this->isTamaraRemoteAuthorisedStatus($tamaraOrderStatus)) {
1461 return $this->handleTamaraRemoteAuthorisedOrder($wcOrder, $wcOrderId, $tamaraOrderId);
1462 }
1463
1464 if ($this->isTamaraRemoteCapturedStatus($tamaraOrderStatus)) {
1465 return $this->handleTamaraRemoteCapturedOrder($wcOrder, $wcOrderId, $tamaraOrder, $tamaraOrderStatus);
1466 }
1467
1468 if ($this->isTamaraRemoteCancelledStatus($tamaraOrderStatus)) {
1469 return $this->handleTamaraRemoteCancelledOrder($wcOrder, $tamaraOrderStatus);
1470 }
1471
1472 if ('approved' === $tamaraOrderStatus) {
1473 /** @var TamaraNotificationService $tamaraNotificationService */
1474 $tamaraNotificationService = $this->getService(TamaraNotificationService::class);
1475 $tamaraNotificationService->authoriseOrder($wcOrderId, $tamaraOrderId);
1476
1477 return $this->isOrderAuthorised($wcOrderId);
1478 }
1479
1480 return false;
1481 }
1482
1483 /**
1484 * @param string $tamaraOrderStatus
1485 *
1486 * @return bool
1487 */
1488 protected function isTamaraRemoteAuthorisedStatus($tamaraOrderStatus)
1489 {
1490 return in_array($tamaraOrderStatus, [static::TAMARA_AUTHORISED_STATUS, static::TAMARA_AUTHORIZED_STATUS], true);
1491 }
1492
1493 /**
1494 * @param string $tamaraOrderStatus
1495 *
1496 * @return bool
1497 */
1498 protected function isTamaraRemoteCapturedStatus($tamaraOrderStatus)
1499 {
1500 return in_array(
1501 $tamaraOrderStatus,
1502 [static::TAMARA_FULLY_CAPTURED_STATUS, static::TAMARA_PARTIALLY_CAPTURED_STATUS, static::TAMARA_CAPTURED_STATUS],
1503 true
1504 );
1505 }
1506
1507 /**
1508 * @param string $tamaraOrderStatus
1509 *
1510 * @return bool
1511 */
1512 protected function isTamaraRemoteCancelledStatus($tamaraOrderStatus)
1513 {
1514 return in_array(
1515 $tamaraOrderStatus,
1516 [
1517 static::TAMARA_EXPIRED_STATUS,
1518 static::TAMARA_DECLINED_STATUS,
1519 static::TAMARA_CANCELED_STATUS,
1520 static::TAMARA_REFUNDED_STATUS,
1521 static::TAMARA_PARTIALLY_REFUNDED_STATUS,
1522 static::TAMARA_FULLY_REFUNDED_STATUS,
1523 ],
1524 true
1525 );
1526 }
1527
1528 /**
1529 * @param mixed $tamaraOrder
1530 *
1531 * @return array
1532 */
1533 protected function getTamaraCaptureIdsFromOrder($tamaraOrder)
1534 {
1535 $captureIds = [];
1536 $transactions = $tamaraOrder->getTransactions();
1537
1538 if (!$transactions || !$transactions->getCaptures()) {
1539 return $captureIds;
1540 }
1541
1542 foreach ($transactions->getCaptures()->toArray() as $capture) {
1543 if (!empty($capture['capture_id'])) {
1544 $captureIds[] = $capture['capture_id'];
1545 }
1546 }
1547
1548 return $captureIds;
1549 }
1550
1551 /**
1552 * @param WC_Order $wcOrder
1553 * @param int $wcOrderId
1554 * @param string $tamaraOrderId
1555 *
1556 * @return bool
1557 *
1558 * @throws \Illuminate\Contracts\Container\BindingResolutionException
1559 */
1560 protected function handleTamaraRemoteAuthorisedOrder($wcOrder, $wcOrderId, $tamaraOrderId)
1561 {
1562 $tamaraStatus = $this->getWCTamaraGatewayService()->tamaraStatus;
1563 $orderNote = 'Tamara - Order is already authorised on Tamara.';
1564 $newOrderStatus = $tamaraStatus['authorise_done'];
1565 $updateOrderStatusNote = 'Payment received. ';
1566
1567 $this->updateOrderStatusAndAddOrderNote($wcOrder, $orderNote, $newOrderStatus, $updateOrderStatusNote);
1568 $this->finalizeTamaraAuthorisedOrder($wcOrder, $wcOrderId, $tamaraOrderId);
1569
1570 return true;
1571 }
1572
1573 /**
1574 * @param WC_Order $wcOrder
1575 * @param int $wcOrderId
1576 * @param mixed $tamaraOrder
1577 * @param string $tamaraOrderStatus
1578 *
1579 * @return bool
1580 *
1581 * @throws \Illuminate\Contracts\Container\BindingResolutionException
1582 */
1583 protected function handleTamaraRemoteCapturedOrder($wcOrder, $wcOrderId, $tamaraOrder, $tamaraOrderStatus)
1584 {
1585 $captureIds = $this->getTamaraCaptureIdsFromOrder($tamaraOrder);
1586 $captureIdsString = !empty($captureIds) ? implode(', ', $captureIds) : 'N/A';
1587 $captureType = static::TAMARA_PARTIALLY_CAPTURED_STATUS === $tamaraOrderStatus ? 'partially' : 'fully';
1588 $orderNote = sprintf(
1589 'Order Payment is %s captured on Tamara, Tamara Capture IDs: %s',
1590 $captureType,
1591 $captureIdsString
1592 );
1593
1594 $this->updateOrderStatusAndAddOrderNote($wcOrder, $orderNote, 'wc-processing', 'Payment received. ');
1595
1596 if (!empty($captureIds[0])) {
1597 $this->getWCTamaraGatewayService()->updateTamaraCaptureId($wcOrderId, $captureIds[0]);
1598 }
1599
1600 $this->finalizeTamaraAuthorisedOrder($wcOrder, $wcOrderId, (string) $tamaraOrder->getOrderId());
1601
1602 return true;
1603 }
1604
1605 /**
1606 * @param WC_Order $wcOrder
1607 * @param string $tamaraOrderStatus
1608 *
1609 * @return bool
1610 */
1611 protected function handleTamaraRemoteCancelledOrder($wcOrder, $tamaraOrderStatus)
1612 {
1613 $tamaraStatus = $this->getWCTamaraGatewayService()->tamaraStatus;
1614 $orderNote = sprintf('Tamara order status: %s', $tamaraOrderStatus);
1615 $newOrderStatus = $tamaraStatus['payment_cancelled'];
1616
1617 $this->updateOrderStatusAndAddOrderNote($wcOrder, $orderNote, $newOrderStatus, '');
1618
1619 return true;
1620 }
1621
1622 /**
1623 * @param WC_Order $wcOrder
1624 * @param int $wcOrderId
1625 * @param string $tamaraOrderId
1626 *
1627 * @throws \Illuminate\Contracts\Container\BindingResolutionException
1628 */
1629 protected function finalizeTamaraAuthorisedOrder($wcOrder, $wcOrderId, $tamaraOrderId)
1630 {
1631 if (function_exists('WC') && WC()->cart) {
1632 WC()->cart->empty_cart();
1633 }
1634
1635 update_post_meta($wcOrderId, 'tamara_authorized', true);
1636 update_post_meta($wcOrderId, 'payment_method', $wcOrder->get_payment_method());
1637 $this->getWCTamaraGatewayService()->updateTamaraOrderId($wcOrderId, $tamaraOrderId);
1638
1639 if (static::TAMARA_GATEWAY_CHECKOUT_ID === $wcOrder->get_payment_method()) {
1640 $this->updateWcOrderPaymentMethodAccordingToTamaraOrder($wcOrderId, $wcOrder);
1641 }
1642 }
1643
1644 /**
1645 * Add Tamara Authorise Failed Message on cart page
1646 */
1647 public function addTamaraAuthoriseFailedMessage()
1648 {
1649 $tamaraAuthoriseParam = isset($_GET['tamara_authorise']) ? sanitize_text_field(wp_unslash($_GET['tamara_authorise'])) : '';
1650 if ('failed' === $tamaraAuthoriseParam && !static::isRestRequest()) {
1651 if (function_exists('wc_add_notice')) {
1652 wc_add_notice(__('We are unable to authorise your payment from Tamara. Please contact us if you need assistance.', 'tamara-checkout'), 'error');
1653 }
1654 }
1655 }
1656
1657 /** @noinspection PhpFullyQualifiedNameUsageInspection */
1658 /**
1659 * Do needed things on Tamara Cancel Url returned
1660 *
1661 * @throws \Illuminate\Contracts\Container\BindingResolutionException
1662 */
1663 public function handleTamaraCancelUrl()
1664 {
1665 $orderId = absint(filter_input(INPUT_GET, 'wcOrderId', FILTER_SANITIZE_NUMBER_INT));
1666 $wcOrder = $orderId ? wc_get_order($orderId) : false;
1667
1668 if (!$wcOrder || !$this->verifyPaymentReturnRequest($wcOrder, 'cancel')) {
1669 wp_safe_redirect(wc_get_cart_url());
1670 exit;
1671 }
1672
1673 if ($this->isOrderAuthorised($orderId)) {
1674 $this->preventOrderCancelAction($wcOrder, $orderId);
1675 }
1676
1677 if (!$this->canPubliclyCancelOrFailOrder($wcOrder)) {
1678 wp_safe_redirect(wc_get_cart_url());
1679 exit;
1680 }
1681
1682 $newOrderStatus = $this->getWCTamaraGatewayService()->tamaraStatus['payment_cancelled'];
1683 $orderNote = 'The payment for this order has been cancelled from Tamara.';
1684 $this->updateOrderStatusAndAddOrderNote($wcOrder, $orderNote, $newOrderStatus, '');
1685 $cancelUrlFromTamara = add_query_arg(
1686 [
1687 'tamara_custom_status' => 'tamara-p-canceled',
1688 'redirect_from' => 'tamara',
1689 'cancel_order' => 'true',
1690 'order' => $wcOrder->get_order_key(),
1691 'order_id' => $orderId,
1692 '_wpnonce' => wp_create_nonce('woocommerce-cancel_order'),
1693 ],
1694 $wcOrder->get_cancel_order_url_raw()
1695 );
1696 wp_safe_redirect($cancelUrlFromTamara);
1697 exit;
1698 }
1699
1700 /** @noinspection PhpFullyQualifiedNameUsageInspection */
1701 /**
1702 * Do needed things on Tamara Failure Url returned
1703 *
1704 * @throws \Illuminate\Contracts\Container\BindingResolutionException
1705 */
1706 public function handleTamaraFailureUrl()
1707 {
1708 $orderId = absint(filter_input(INPUT_GET, 'wcOrderId', FILTER_SANITIZE_NUMBER_INT));
1709 $wcOrder = $orderId ? wc_get_order($orderId) : false;
1710
1711 if (!$wcOrder || !$this->verifyPaymentReturnRequest($wcOrder, 'fail')) {
1712 wp_safe_redirect(wc_get_cart_url());
1713 exit;
1714 }
1715
1716 if ($this->isOrderAuthorised($orderId)) {
1717 $this->preventOrderCancelAction($wcOrder, $orderId);
1718 }
1719
1720 if (!$this->canPubliclyCancelOrFailOrder($wcOrder)) {
1721 wp_safe_redirect(wc_get_cart_url());
1722 exit;
1723 }
1724
1725 $newOrderStatus = $this->getWCTamaraGatewayService()->tamaraStatus['payment_failed'];
1726 $orderNote = 'The payment for this order has been declined from Tamara.';
1727 $this->updateOrderStatusAndAddOrderNote($wcOrder, $orderNote, $newOrderStatus, '');
1728 $failureUrlFromTamara = add_query_arg(
1729 [
1730 'tamara_custom_status' => 'tamara-p-failed',
1731 'redirect_from' => 'tamara',
1732 'cancel_order' => 'true',
1733 'order' => $wcOrder->get_order_key(),
1734 'order_id' => $orderId,
1735 '_wpnonce' => wp_create_nonce('woocommerce-cancel_order'),
1736 ],
1737 $wcOrder->get_cancel_order_url_raw()
1738 );
1739 wp_safe_redirect($failureUrlFromTamara);
1740 exit;
1741 }
1742
1743 /**
1744 * Do some needed things when activate plugin
1745 */
1746 public function activatePlugin()
1747 {
1748 if (!class_exists('WooCommerce')) {
1749 die(sprintf(__('Plugin `%s` needs Woocommerce to be activated', 'tamara-checkout'),
1750 'Tamara Checkout'));
1751 }
1752 }
1753
1754 /**
1755 * @noinspection PhpUnusedDeclarationInspection
1756 */
1757 public function deactivatePlugin()
1758 {
1759 // The problem with calling flush_rewrite_rules() is that the rules instantly get regenerated, while your plugin's hooks are still active.
1760 delete_option('rewrite_rules');
1761 }
1762
1763 /**
1764 * Add rewrite rule for Tamara IPN and Webhook response page
1765 */
1766 public function addCustomRewriteRules()
1767 {
1768 add_rewrite_rule(WCTamaraGateway::IPN_SLUG.'/?$', 'index.php?pagename='.WCTamaraGateway::IPN_SLUG, 'top');
1769 add_rewrite_rule(WCTamaraGateway::WEBHOOK_SLUG.'/?$', 'index.php?pagename='.WCTamaraGateway::WEBHOOK_SLUG, 'top');
1770 add_rewrite_rule(WCTamaraGateway::PAYMENT_SUCCESS_SLUG.'/?$', 'index.php?pagename='.WCTamaraGateway::PAYMENT_SUCCESS_SLUG, 'top');
1771 add_rewrite_rule(WCTamaraGateway::PAYMENT_CANCEL_SLUG.'/?$', 'index.php?pagename='.WCTamaraGateway::PAYMENT_CANCEL_SLUG, 'top');
1772 add_rewrite_rule(WCTamaraGateway::PAYMENT_FAIL_SLUG.'/?$', 'index.php?pagename='.WCTamaraGateway::PAYMENT_FAIL_SLUG, 'top');
1773 }
1774
1775 /**
1776 * Run this method under the "init" action
1777 */
1778 public function checkWooCommerceExistence()
1779 {
1780 if (class_exists('WooCommerce')) {
1781 // Add "Settings" link when the plugin is active
1782 add_filter('plugin_action_links_tamara-checkout/tamara-checkout.php', [$this, 'addSettingsLinks']);
1783 } else {
1784 require_once(ABSPATH.'wp-admin/includes/plugin.php');
1785 // Throw a notice if WooCommerce is NOT active
1786 deactivate_plugins(plugin_basename($this->pluginFilename));
1787 add_action('admin_notices', [$this, 'noticeNonWooCommerce']);
1788 }
1789 }
1790
1791 /** @noinspection PhpFullyQualifiedNameUsageInspection */
1792 /**
1793 * Add more links to plugin settings
1794 *
1795 * @param $pluginLinks
1796 *
1797 * @return array
1798 *
1799 * @throws \Illuminate\Contracts\Container\BindingResolutionException
1800 */
1801 public function addSettingsLinks($pluginLinks)
1802 {
1803 $pluginLinks[] = '<a href="'.$this->getAdminSettingLink().'">'.esc_html__('Settings',
1804 'tamara-checkout').'</a>';
1805
1806 return $pluginLinks;
1807 }
1808
1809 /**
1810 * Throw a notice if WooCommerce is NOT active
1811 */
1812 public function noticeNonWooCommerce()
1813 {
1814 $class = 'notice notice-warning';
1815
1816 // translators: %s: Plugin name
1817 $message = sprintf(esc_html__('Plugin `%s` deactivated because WooCommerce is not active. Please activate WooCommerce first.',
1818 'tamara-checkout'), esc_html('Tamara Checkout'));
1819
1820 printf('<div class="%1$s"><p><strong>%2$s</strong></p></div>', $class, $message);
1821 }
1822
1823 /** @noinspection PhpFullyQualifiedNameUsageInspection */
1824 /**
1825 * Add Tamara Payment Gateway
1826 *
1827 * @param $gateways
1828 *
1829 * @return array
1830 *
1831 * @throws \Illuminate\Contracts\Container\BindingResolutionException
1832 */
1833 public function registerTamaraPaymentGateway($gateways)
1834 {
1835 $gateways[] = $this->getWCTamaraGatewayService();
1836
1837 return $gateways;
1838 }
1839
1840 /** @noinspection PhpFullyQualifiedNameUsageInspection */
1841 /**
1842 * Adjust Tamara payment types on checkout page based on Tamara settings
1843 *
1844 * @param $availableGateways
1845 *
1846 * @return array
1847 *
1848 * @throws \Illuminate\Contracts\Container\BindingResolutionException
1849 */
1850 public function adjustTamaraPaymentTypesOnCheckout($availableGateways)
1851 {
1852 if (!$this->isTamaraGatewayEnabled() || !is_checkout()) {
1853 return $availableGateways;
1854 }
1855
1856 return array_filter($availableGateways, function ($gateway, $gatewayId) {
1857 if ('tamara-gateway' === $gatewayId) {
1858 return true;
1859 }
1860
1861 if (is_string($gatewayId) && 0 === strpos($gatewayId, 'tamara-gateway')) {
1862 return false;
1863 }
1864
1865 if (!empty($gateway->id) && 0 === strpos($gateway->id, 'tamara-gateway') && TamaraCheckout::TAMARA_GATEWAY_ID !== $gateway->id) {
1866 return false;
1867 }
1868
1869 return true;
1870 }, ARRAY_FILTER_USE_BOTH);
1871 }
1872
1873 /**
1874 * Enqueue admin scripts for settings
1875 */
1876 public function enqueueAdminSettingScripts()
1877 {
1878 // Only enqueue the setting scripts on the Tamara Checkout settings screen.
1879 if ($this->isTamaraAdminSettingsScreen()) {
1880 wp_enqueue_script('tamara-checkout-settings-js', $this->baseUrl.'/assets/dist/js/admin.js', ['jquery'],
1881 $this->version, true);
1882 wp_enqueue_style('tamara-admin-css', $this->baseUrl.'/assets/dist/css/admin.css', [],
1883 $this->version);
1884 } // Load the admin stylesheet on shop order screen
1885 elseif (isset($_GET['post_type']) && ('shop_order' === $_GET['post_type'])) {
1886 wp_enqueue_style('tamara-admin-css', $this->baseUrl.'/assets/dist/css/admin.css', [],
1887 $this->version);
1888 }
1889 }
1890
1891 /**
1892 * Add some help text for billing phone when using Tamara payment
1893 *
1894 * @param $checkoutFields
1895 *
1896 * @return mixed
1897 */
1898 public function adjustBillingPhoneDescription($checkoutFields)
1899 {
1900 if (isset($checkoutFields['billing'], $checkoutFields['billing']['billing_phone'])) {
1901 $checkoutFields['billing']['billing_phone']['description'] = __('If you use Tamara Payment, this should be your full Tamara registered phone number (e.g. +966504449999 for KSA, +97150888444 for UAE)',
1902 'tamara-checkout');
1903 }
1904
1905 return $checkoutFields;
1906 }
1907
1908 /**
1909 * Enqueue FE stylesheet and scripts
1910 */
1911 public function enqueueScripts()
1912 {
1913 $storeCurrency = esc_js(get_woocommerce_currency());
1914 $publicKey = esc_js($this->getWCTamaraGatewayService()->getPublicKey() ?? '');
1915 $siteLocale = esc_js(substr(get_locale(), 0, 2) ?: 'en');
1916 $countryCode = esc_js($this->getWCTamaraGatewayService()->getCurrentCountryCode());
1917 $ajaxUrl = esc_js(admin_url('admin-ajax.php'));
1918
1919 $summaryWidgetUrl = $this->getWCTamaraGatewayService()->isLiveMode()
1920 ? static::TAMARA_SUMMARY_WIDGET_URL
1921 : static::TAMARA_SUMMARY_WIDGET_SANDBOX_URL;
1922
1923 wp_enqueue_script('tamara-summary-widget', $summaryWidgetUrl, [], $this->version, false);
1924
1925 $inlineScript = sprintf(
1926 'let tamaraCheckoutParams = {"ajaxUrl":"%s","publicKey":"%s","currency":"%s","country":"%s"}; window.tamaraWidgetConfig = {"lang":"%s","country":"%s","publicKey":"%s"};',
1927 $ajaxUrl,
1928 $publicKey,
1929 $storeCurrency,
1930 $countryCode,
1931 $siteLocale,
1932 $countryCode,
1933 $publicKey
1934 );
1935 wp_add_inline_script('tamara-summary-widget', $inlineScript, 'before');
1936
1937 wp_enqueue_style('tamara-checkout', $this->baseUrl.'/assets/dist/css/main.css', [], $this->version . '&' . time());
1938 wp_enqueue_script('tamara-checkout', $this->baseUrl.'/assets/dist/js/main.js', ['jquery'], $this->version . '&' . time(), true);
1939 }
1940
1941 /** @noinspection PhpFullyQualifiedNameUsageInspection */
1942 /**
1943 * Add relevant links to plugins page
1944 *
1945 * @throws \Illuminate\Contracts\Container\BindingResolutionException
1946 */
1947 public function getAdminSettingLink()
1948 {
1949 if (version_compare(WC()->version, '2.6', '>=')) {
1950 $sectionSlug = $this->getWCTamaraGatewayService()->id;
1951 } else {
1952 $sectionSlug = strtolower(WCTamaraGateway::class);
1953 }
1954
1955 return admin_url('admin.php?page=wc-settings&tab=checkout&section='.$sectionSlug);
1956 }
1957
1958 /** @noinspection PhpFullyQualifiedNameUsageInspection */
1959 /**
1960 * @param \WC_Order_Refund $wcOrderRefund
1961 * @param $args
1962 *
1963 * @throws \Illuminate\Contracts\Container\BindingResolutionException
1964 * @throws Exception
1965 */
1966 public function tamaraRefundPayment($wcOrderRefund, $args)
1967 {
1968 $wcOrder = wc_get_order($args['order_id']);
1969 $wcOrderId = $args['order_id'];
1970 $payment_method = $wcOrder->get_payment_method();
1971
1972 if ($this->isTamaraGateway($payment_method)) {
1973 $tamaraOrderId = $this->getWCTamaraGatewayService()->getTamaraOrderId($wcOrderId);
1974 $captureId = $this->getWCTamaraGatewayService()->getTamaraCaptureId($wcOrderId);
1975 $refundCollection = [];
1976 $wcOrderTotal = new Money(MoneyHelper::formatNumber(abs($wcOrderRefund->get_amount())),
1977 $wcOrder->get_currency());
1978 $wcShippingTotal = new Money(MoneyHelper::formatNumber(abs($wcOrderRefund->get_shipping_total())),
1979 $wcOrder->get_currency());
1980 $wcTaxTotal = new Money(MoneyHelper::formatNumber(abs($wcOrderRefund->get_total_tax())),
1981 $wcOrder->get_currency());
1982 $wcDiscountTotal = new Money(MoneyHelper::formatNumber($wcOrderRefund->get_discount_total()),
1983 $wcOrder->get_currency());
1984 $wcOrderItemsRefund = $this->getWCTamaraGatewayService()->populateTamaraRefundOrderItems($wcOrderRefund);
1985
1986 try {
1987 $refundItem = new Refund($captureId, $wcOrderTotal, $wcShippingTotal, $wcTaxTotal,
1988 $wcDiscountTotal,
1989 $wcOrderItemsRefund);
1990 array_push($refundCollection, $refundItem);
1991 $refundResponse = $this->getWCTamaraGatewayService()->tamaraClient->refund(new RefundRequest($tamaraOrderId,
1992 $refundCollection));
1993 $this->logMessage(sprintf("Tamara Refund Response Data: %s", print_r($refundResponse, true)));
1994 } catch (Exception $tamaraRefundException) {
1995 $this->logMessage(sprintf("Tamara Service timeout or disconnected.\nError message: '%s'.\nTrace: %s",
1996 $tamaraRefundException->getMessage(), $tamaraRefundException->getTraceAsString()));
1997 }
1998
1999 if (isset($refundResponse) && $refundResponse->isSuccess()) {
2000 $wcOrder->add_order_note(
2001 /* translators: Refund ID */
2002 sprintf(__('Order has been refunded successfully - Refund ID: #%1$s', 'tamara-checkout'),
2003 $wcOrderRefund->get_id()));
2004
2005 } else {
2006 $errorMessage = null;
2007
2008 if (isset($tamaraRefundException) && $tamaraRefundException instanceof Exception) {
2009
2010 $errorMessage = $tamaraRefundException->getMessage();
2011 $this->logMessage($errorMessage);
2012
2013 } elseif (isset($refundResponse)) {
2014 if ('refund.shipping_amount_invalid' === $refundResponse->getMessage()) {
2015 throw new Exception(__('You need to enter the full shipping amount to refund.',
2016 'tamara-checkout'));
2017 } elseif ('items_is_empty' === $refundResponse->getErrors()[0]['error_code']) {
2018 throw new Exception(__('Refund item is empty. Please choose your item to refund.',
2019 'tamara-checkout'));
2020 } elseif ('refund.capture_not_found' === $refundResponse->getMessage()) {
2021 $captureNotFoundMessage = __('Tamara Capture ID not found. Please capture the payment before making a refund.',
2022 'tamara-checkout');
2023 $wcOrder->add_order_note($captureNotFoundMessage);
2024 throw new Exception($captureNotFoundMessage);
2025 }
2026 }
2027 throw new Exception(__('Error! Tamara is having a problem. Please contact Tamara and try again later',
2028 'tamara-checkout'));
2029 }
2030 }
2031 }
2032
2033 /** @noinspection PhpFullyQualifiedNameUsageInspection */
2034 /**
2035 * Tamara show widget popup shortcode callback method
2036 *
2037 * @param $attributes
2038 *
2039 * @return bool|string|void|null
2040 * @throws \Illuminate\Contracts\Container\BindingResolutionException
2041 */
2042 public function tamaraProductPopupWidget($attributes)
2043 {
2044 extract(shortcode_atts(array(
2045 'price' => '',
2046 'currency' => '',
2047 'language' => '',
2048 ), $attributes));
2049 $dataPrice = !empty($price) ? $price : $this->getDisplayedProductPrice();
2050 $dataCurrency = !empty($currency) ? $currency : get_woocommerce_currency();
2051 $dataLanguage = !empty($language) ? $language : substr(get_locale(), 0, 2);
2052 if ($this->isWidgetPopupDisabled() ||
2053 (!empty($this->getDisplayedProductId()) && $this->isExcludedProduct($this->getDisplayedProductId())) ||
2054 (!empty($this->getDisplayedProductCategoryIds())
2055 && $this->isExcludedProductCategory($this->getDisplayedProductCategoryIds()))) {
2056 return false;
2057 } else {
2058 $itemPrice = is_array($dataPrice) ? $this->getAppropriateVariationProductPrice($dataPrice) : $dataPrice;
2059 return $this->getServiceView()->render('views/woocommerce/checkout/tamara-popup-widget',
2060 [
2061 'dataPrice' => $itemPrice ?? 0,
2062 'dataCurrency' => $dataCurrency,
2063 'dataLanguage' => $dataLanguage ?? 'en',
2064 'inlineType' => static::TAMARA_INLINE_TYPE_PRODUCT_WIDGET_INT,
2065 ]);
2066 }
2067 }
2068
2069 /** @noinspection PhpFullyQualifiedNameUsageInspection */
2070 /**
2071 * Tamara show cart widget popup shortcode callback method
2072 *
2073 * @param $attributes
2074 *
2075 * @return bool|string|void|null
2076 * @throws \Illuminate\Contracts\Container\BindingResolutionException
2077 */
2078 public function tamaraCartPopupWidget($attributes)
2079 {
2080 extract(shortcode_atts(array(
2081 'price' => '',
2082 'currency' => '',
2083 'language' => '',
2084 ), $attributes));
2085 $getPrice = is_cart() ? WC()->cart->get_total( null ) : $this->getDisplayedProductPrice();
2086 $dataPrice = !empty($price) ? $price : $getPrice;
2087 $dataCurrency = !empty($currency) ? $currency : get_woocommerce_currency();
2088 $dataLanguage = !empty($language) ? $language : substr(get_locale(), 0, 2);
2089
2090
2091 $tamaraExcludedProductItems = TamaraCheckout::getInstance()->getExcludedProductIds() ?? null;
2092 $tamaraExcludedProductCategories = TamaraCheckout::getInstance()->getExcludedProductCategoryIds() ?? null;
2093 $cartItemIds = TamaraCheckout::getInstance()->getAllProductIdsInCart();
2094 $cartItemCategoryIds = TamaraCheckout::getInstance()->getAllProductCategoryIdsInCart();
2095 $tamaraExcludedProductItemsInCart = (count(array_intersect(
2096 $cartItemIds, $tamaraExcludedProductItems))) ? true : false;
2097 $tamaraExcludedProductCategoriesInCart = (count(array_intersect(
2098 $cartItemCategoryIds, $tamaraExcludedProductCategories))) ? true : false;
2099
2100 if ($this->isCartWidgetPopupDisabled() ||
2101 ($tamaraExcludedProductItemsInCart) ||
2102 ($tamaraExcludedProductCategoriesInCart)
2103 ) {
2104 return false;
2105 } else {
2106 $itemPrice = is_array($dataPrice) ? $this->getAppropriateVariationProductPrice($dataPrice) : $dataPrice;
2107 return $this->getServiceView()->render('views/woocommerce/checkout/tamara-popup-widget',
2108 [
2109 'dataPrice' => $itemPrice ?? 0,
2110 'dataCurrency' => $dataCurrency,
2111 'dataLanguage' => $dataLanguage ?? 'en',
2112 'inlineType' => static::TAMARA_INLINE_TYPE_PRODUCT_WIDGET_INT,
2113 ]);
2114 }
2115 }
2116
2117 /**
2118 * Show Tamara popup widget
2119 */
2120 public function showTamaraProductPopupWidget()
2121 {
2122 if ($this->isTamaraGatewayEnabled()) {
2123 echo do_shortcode('[tamara_show_popup]');
2124 }
2125 }
2126
2127 /**
2128 * Show Tamara popup widget on Cart page
2129 */
2130 public function showTamaraCartProductPopupWidget()
2131 {
2132 if ($this->isTamaraGatewayEnabled() && !$this->isCartWidgetPopupDisabled()) {
2133 echo do_shortcode('[tamara_show_cart_popup]');
2134 }
2135 }
2136
2137 /**
2138 * Get displayed product price on FE
2139 */
2140 public function getDisplayedProductPrice()
2141 {
2142 global $product;
2143 if ($product) {
2144 if ($product instanceof \WC_Product) {
2145 if ($product instanceof \WC_Product_Variable) {
2146 return $product->get_variation_prices(true)['price'];
2147 } else {
2148 return wc_get_price_to_display($product);
2149 }
2150 }
2151 }
2152
2153 return null;
2154 }
2155
2156 /**
2157 * Get displayed product id on FE
2158 */
2159 public function getDisplayedProductId()
2160 {
2161 global $product;
2162 if ($product) {
2163 if ($product instanceof \WC_Product) {
2164 return $product->get_id();
2165 }
2166 }
2167
2168 return null;
2169 }
2170
2171 /**
2172 * Get all category ids of displayed product on FE
2173 */
2174 public function getDisplayedProductCategoryIds()
2175 {
2176 global $product;
2177 if ($product) {
2178 if ($product instanceof \WC_Product) {
2179 $productId = $product->get_id();
2180
2181 return wc_get_product_cat_ids($productId);
2182 }
2183 }
2184
2185 return null;
2186 }
2187
2188 /** @noinspection PhpFullyQualifiedNameUsageInspection */
2189 /**
2190 * @param $productPrice
2191 *
2192 * @return bool
2193 *
2194 * @throws \Illuminate\Contracts\Container\BindingResolutionException
2195 */
2196 public function isProductPriceValid($productPrice)
2197 {
2198 // Force pull country payment types from remote api
2199 $countryPaymentTypes = $this->getWCTamaraGatewayService()->getCountryPaymentTypes();
2200 $paymentTypes = $this->getWCTamaraGatewayService()->getPaymentTypes();
2201
2202 if (!$this->isAlwaysShowWidgetPopupEnabled() && !$this->getWCTamaraGatewayService()->isSingleCheckoutEnabled()) {
2203 if (is_array($productPrice)) {
2204 foreach ($productPrice as $item => $variationPrice) {
2205 if (($this->getWCTamaraGatewayService()->populateMinLimit() <= $variationPrice
2206 && $this->getWCTamaraGatewayService()->populateMaxLimit() >= $variationPrice)
2207 || ($this->populatePayInXsLimitAmountBasedOnProductPrice($variationPrice)['instalmentMinAmount'] <= $variationPrice
2208 && $this->populatePayInXsLimitAmountBasedOnProductPrice($variationPrice)['instalmentMaxAmount'] >= $variationPrice)
2209 || ($this->populatePayNextMonthMinLimit() <= $variationPrice
2210 && $this->populatePayNextMonthMaxLimit() >= $variationPrice)
2211 ) {
2212 return true;
2213 break;
2214 }
2215 }
2216 } else {
2217 return ($this->getWCTamaraGatewayService()->populateMinLimit() <= $productPrice
2218 && $this->getWCTamaraGatewayService()->populateMaxLimit() >= $productPrice)
2219 || ($this->populatePayInXsLimitAmountBasedOnProductPrice($productPrice)['instalmentMinAmount'] <= $productPrice
2220 && $this->populatePayInXsLimitAmountBasedOnProductPrice($productPrice)['instalmentMaxAmount'] >= $productPrice)
2221 || ($this->populatePayNextMonthMinLimit() <= $productPrice
2222 && $this->populatePayNextMonthMaxLimit() >= $productPrice);
2223 }
2224 } else {
2225 return true;
2226 }
2227 }
2228
2229 /**
2230 * Modify which total amount should be used to display on checkout page
2231 *
2232 * @param $amount
2233 *
2234 * @return mixed
2235 */
2236 public function getTotalToCalculate($amount)
2237 {
2238 $wcOrder = $this->getOrderPayOrder();
2239 if ($wcOrder) {
2240 return $wcOrder->get_total();
2241 }
2242
2243 return $amount;
2244 }
2245
2246 /**
2247 * Safely read the current cart/order total for display and eligibility checks.
2248 * WC()->cart can be null outside storefront requests (REST, cron, admin, early hooks).
2249 *
2250 * @return float|int|string
2251 */
2252 public function getCartTotal()
2253 {
2254 $cartTotal = 0;
2255 if (function_exists('WC') && WC() && WC()->cart) {
2256 $cartTotal = WC()->cart->total;
2257 }
2258
2259 return $this->getTotalToCalculate($cartTotal);
2260 }
2261
2262 /** @noinspection PhpFullyQualifiedNameUsageInspection */
2263 /**
2264 * Update Tamara checkout data to order meta data created via rest api
2265 *
2266 * @param \WP_REST_Response $response The response object.
2267 * @param \WP_Post $post Post object.
2268 * @param \WP_REST_Request $request Request object.
2269 *
2270 * @return mixed
2271 *
2272 * @throws \Illuminate\Contracts\Container\BindingResolutionException
2273 */
2274 public function updateTamaraCheckoutDataToOrder($response, $post, $request)
2275 {
2276 $wcOrder = wc_get_order($post);
2277
2278 if (!empty($wcOrder) && $this->isTamaraGateway($wcOrder->get_payment_method())) {
2279 $wcOrderId = $wcOrder->get_id();
2280 $hasTamaraCheckoutUrl = !!get_post_meta($wcOrderId, 'tamara_checkout_url', true);
2281 $hasTamaraCheckoutSessionId = !!get_post_meta($wcOrderId, 'tamara_checkout_session_id', true);
2282 if (!$hasTamaraCheckoutUrl && !$hasTamaraCheckoutSessionId) {
2283 $tamaraCheckoutResponse = $this->getWCTamaraGatewayService()->tamaraCheckoutSession($wcOrderId);
2284 if ($tamaraCheckoutResponse) {
2285 $response_data = $response->get_data();
2286 $metaData = [
2287 [
2288 'key' => 'tamara_checkout_session_id',
2289 'value' => $tamaraCheckoutResponse['tamaraCheckoutSessionId'] ?: null,
2290 ],
2291 [
2292 'key' => 'tamara_checkout_url',
2293 'value' => $tamaraCheckoutResponse['tamaraCheckoutUrl'] ?: null,
2294 ],
2295 ];
2296 $response_data['meta_data'] = $response_data['meta_data'] + $metaData;
2297 $response->set_data($response_data);
2298 }
2299 }
2300 }
2301
2302 return $response;
2303 }
2304
2305 /**
2306 * Check a request a Rest API request or not
2307 * @return bool
2308 */
2309 public static function isRestRequest()
2310 {
2311 $requestUri = !empty($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : null;
2312
2313 return $requestUri && strpos($requestUri, 'wp-json') !== false;
2314 }
2315
2316 /**
2317 * Set Rest Api Request to $restApiRequest variable
2318 *
2319 * @param $restApiRequest
2320 */
2321 public function setRestApiRequest($restApiRequest)
2322 {
2323 $this->restApiRequest = $restApiRequest;
2324 }
2325
2326 /**
2327 * Return the Rest Api Request
2328 *
2329 * @return \WP_REST_Request
2330 */
2331 public function getRestApiRequest()
2332 {
2333 return $this->restApiRequest;
2334 }
2335
2336 /**
2337 * Redirect Pay By Instalments settings page to Pay By Later settings page
2338 */
2339 public function updatePayByInstalmentSettingUrl()
2340 {
2341 if (is_admin() && isset($_GET['page'], $_GET['tab'], $_GET['section'])
2342 && ('wc-settings' === $_GET['page'])
2343 && ('checkout' === $_GET['tab'])
2344 && (in_array($_GET['section'], $this->getPayInXIds()))) {
2345 wp_redirect(admin_url('admin.php?page=wc-settings&tab=checkout&section='.strtolower(static::TAMARA_GATEWAY_ID)));
2346 }
2347 }
2348
2349 /**
2350 * @param $fields
2351 * @param $country
2352 *
2353 * @return mixed
2354 */
2355 public function forceRequireBillingPhone($fields, $country)
2356 {
2357 if (is_wc_endpoint_url('edit-address') || !$this->isForceBillingPhoneEnabled() || !empty($fields['billing_phone'])) {
2358 return $fields;
2359 } elseif ($this->isForceBillingPhoneEnabled() && empty($fields['billing_phone'])) {
2360 $fields['billing_phone'] = [
2361 'label' => __('Phone', 'tamara-checkout'),
2362 'required' => true,
2363 ];
2364
2365 return $fields;
2366 }
2367
2368 return $fields;
2369 }
2370
2371 /**
2372 * Fire an ajax request without waiting for response
2373 */
2374 public function addCronJobTriggerScript()
2375 {
2376 $ajaxCronjobUrl = esc_attr(add_query_arg([
2377 'action' => 'tamara_perform_cron',
2378 '_wpnonce' => wp_create_nonce('tamara_perform_cron'),
2379 ], admin_url('admin-ajax.php')));
2380
2381 $sectionSlug = isset($_GET['section']) ? esc_attr(sanitize_text_field(wp_unslash($_GET['section']))) : '';
2382 echo '<script type="text/javascript">
2383 var data = {
2384 \'action\': \'tamara_perform_cron\'
2385 };
2386 var sectionSlug = "' . esc_js($sectionSlug) . '";
2387 var randomNumber = Math.floor(Math.random() * 20) + 1;
2388 if (randomNumber === 1 || (typeof pagenow !== \'undefined\' && pagenow === \'woocommerce_page_wc-settings\' && sectionSlug === \'tamara-gateway\')) {
2389 fetch("' . esc_js($ajaxCronjobUrl) . '", {
2390 credentials: \'same-origin\',
2391 method: \'GET\',
2392 headers: {
2393 \'Content-Type\': \'application/json\',
2394 },
2395 });
2396 }
2397 </script>';
2398 }
2399
2400 /** @noinspection PhpFullyQualifiedNameUsageInspection */
2401 /**
2402 * @return false|string
2403 * @throws \Illuminate\Contracts\Container\BindingResolutionException
2404 */
2405 public function performCron()
2406 {
2407 if (
2408 !current_user_can('manage_woocommerce') ||
2409 !isset($_GET['_wpnonce']) ||
2410 !wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'])), 'tamara_perform_cron')
2411 ) {
2412 wp_send_json_error(['message' => 'forbidden'], 403);
2413 }
2414
2415 $this->forceAuthoriseTamaraOrder();
2416 $this->forceCaptureTamaraOrder();
2417
2418 return json_encode(true);
2419 }
2420
2421 /**
2422 * @param string $url
2423 *
2424 * @return string
2425 */
2426 public function removeTrailingSlashes($url)
2427 {
2428 return rtrim(trim($url), '/');
2429 }
2430
2431 /**
2432 * Get the array of Pay In X Ids
2433 *
2434 * @return array
2435 */
2436 public function getPayInXIds()
2437 {
2438 return [
2439 static::TAMARA_GATEWAY_CHECKOUT_ID,
2440 static::TAMARA_GATEWAY_PAY_IN_2,
2441 static::TAMARA_GATEWAY_PAY_IN_3,
2442 static::TAMARA_GATEWAY_PAY_IN_4,
2443 static::TAMARA_GATEWAY_PAY_IN_5,
2444 static::TAMARA_GATEWAY_PAY_IN_6,
2445 static::TAMARA_GATEWAY_PAY_IN_7,
2446 static::TAMARA_GATEWAY_PAY_IN_8,
2447 static::TAMARA_GATEWAY_PAY_IN_9,
2448 static::TAMARA_GATEWAY_PAY_IN_10,
2449 static::TAMARA_GATEWAY_PAY_IN_11,
2450 static::TAMARA_GATEWAY_PAY_IN_12,
2451 ];
2452 }
2453
2454 /** @noinspection PhpFullyQualifiedNameUsageInspection */
2455 /**
2456 * Populate the array of enabled Pay In Xs min amount
2457 *
2458 * @throws \Illuminate\Contracts\Container\BindingResolutionException
2459 */
2460 public function populateMinAmountArrayOfEnabledPayInXs()
2461 {
2462 $countryCode = $this->getWCTamaraGatewayService()->getCurrentCountryCode();
2463 $payInXMinAmount = [];
2464 for ($i = 12; $i >= 2; $i--) {
2465 if ($this->getWCTamaraGatewayService()->isSingleCheckoutEnabled()) {
2466 $payInXMaxAmount[$i] = $this->populateInstalmentPayInXMaxLimit($i, $countryCode);
2467 } else {
2468 if ($this->isPayInXEnabled($i, $countryCode)) {
2469 $payInXMinAmount[$i] = $this->populateInstalmentPayInXMinLimit($i, $countryCode);
2470 }
2471 }
2472 }
2473
2474 return $payInXMinAmount;
2475 }
2476
2477 /** @noinspection PhpFullyQualifiedNameUsageInspection */
2478 /**
2479 * Populate the array of enabled Pay In Xs max amount
2480 *
2481 * @throws \Illuminate\Contracts\Container\BindingResolutionException
2482 */
2483 public function populateMaxAmountArrayOfEnabledPayInXs()
2484 {
2485 $countryCode = $this->getWCTamaraGatewayService()->getCurrentCountryCode();
2486 $payInXMaxAmount = [];
2487 for ($i = 12; $i >= 2; $i--) {
2488 if ($this->getWCTamaraGatewayService()->isSingleCheckoutEnabled()) {
2489 $payInXMaxAmount[$i] = $this->populateInstalmentPayInXMaxLimit($i, $countryCode);
2490 } else {
2491 if ($this->isPayInXEnabled($i, $countryCode)) {
2492 $payInXMaxAmount[$i] = $this->populateInstalmentPayInXMaxLimit($i, $countryCode);
2493 }
2494 }
2495 }
2496
2497 return $payInXMaxAmount;
2498 }
2499
2500 /** @noinspection PhpFullyQualifiedNameUsageInspection */
2501 /**
2502 * Get min amount priority instalment period
2503 *
2504 * @return int | null
2505 * @throws \Illuminate\Contracts\Container\BindingResolutionException
2506 */
2507 public function getMinAmountOfEnabledPriorityInstalment()
2508 {
2509 $minAmountArr = $this->populateMinAmountArrayOfEnabledPayInXs() ?? [];
2510 if (!empty($minAmountArr)) {
2511 $filterNullVarArr = array_filter($minAmountArr, function ($v) {
2512 return !is_null($v);
2513 });
2514 if (!empty($filterNullVarArr)) {
2515 return $minAmountArr[max(array_keys($filterNullVarArr))];
2516 }
2517 }
2518
2519 return null;
2520 }
2521
2522 /** @noinspection PhpFullyQualifiedNameUsageInspection */
2523 /**
2524 * Get max amount priority instalment period
2525 *
2526 * @return int | null
2527 * @throws \Illuminate\Contracts\Container\BindingResolutionException
2528 */
2529 public function getMaxAmountOfEnabledPriorityInstalment()
2530 {
2531 $maxAmountArr = $this->populateMaxAmountArrayOfEnabledPayInXs() ?? [];
2532 if (!empty($maxAmountArr)) {
2533 $filterNullVarArr = array_filter($maxAmountArr, function ($v) {
2534 return !is_null($v);
2535 });
2536 if (!empty($filterNullVarArr)) {
2537 return $maxAmountArr[max(array_keys($filterNullVarArr))];
2538 }
2539 }
2540
2541 return null;
2542 }
2543
2544 /** @noinspection PhpFullyQualifiedNameUsageInspection */
2545 /**
2546 * Get priority instalment period amongs enabled Pay In Xs
2547 *
2548 * @return int | null
2549 * @throws \Illuminate\Contracts\Container\BindingResolutionException
2550 */
2551 public function getPriorityInstalmentPeriod()
2552 {
2553 $maxAmountArr = $this->populateMaxAmountArrayOfEnabledPayInXs() ?? [];
2554 if (!empty($maxAmountArr)) {
2555 $filterNullVarArr = array_filter($maxAmountArr, function ($v) {
2556 return !is_null($v);
2557 });
2558
2559 return max(array_keys($filterNullVarArr));
2560 }
2561
2562 return null;
2563 }
2564
2565 /**
2566 * Check if current screen is Tamara Admin Settings page
2567 *
2568 * @return bool
2569 */
2570 public function isTamaraAdminSettingsScreen()
2571 {
2572 return (is_admin() && isset($_GET['page'], $_GET['tab'], $_GET['section'])
2573 && ('wc-settings' === $_GET['page'])
2574 && ('checkout' === $_GET['tab'])
2575 && (static::TAMARA_GATEWAY_ID === $_GET['section']));
2576 }
2577
2578 /**
2579 * Return the array of Tamara Gateway Ids
2580 *
2581 * @return array
2582 */
2583 public function getAllTamaraGatewayIds()
2584 {
2585 return [
2586 static::TAMARA_GATEWAY_ID,
2587 static::TAMARA_GATEWAY_PAY_NOW,
2588 static::TAMARA_GATEWAY_PAY_NEXT_MONTH,
2589 static::TAMARA_GATEWAY_PAY_BY_INSTALMENTS_ID,
2590 static::TAMARA_GATEWAY_PAY_IN_2,
2591 static::TAMARA_GATEWAY_PAY_IN_3,
2592 static::TAMARA_GATEWAY_PAY_IN_4,
2593 static::TAMARA_GATEWAY_PAY_IN_5,
2594 static::TAMARA_GATEWAY_PAY_IN_6,
2595 static::TAMARA_GATEWAY_PAY_IN_7,
2596 static::TAMARA_GATEWAY_PAY_IN_8,
2597 static::TAMARA_GATEWAY_PAY_IN_9,
2598 static::TAMARA_GATEWAY_PAY_IN_10,
2599 static::TAMARA_GATEWAY_PAY_IN_11,
2600 static::TAMARA_GATEWAY_PAY_IN_12,
2601 static::TAMARA_GATEWAY_CHECKOUT_ID,
2602 ];
2603 }
2604
2605 /**
2606 * Check if a payment method is Tamara
2607 *
2608 * @param $paymentMethodId
2609 *
2610 * @return bool
2611 */
2612 public function isTamaraGateway($paymentMethodId)
2613 {
2614 return !!in_array($paymentMethodId, $this->getAllTamaraGatewayIds());
2615 }
2616
2617 /**
2618 * Check if a WooCommerce order belongs to Tamara (gateway id or Tamara meta).
2619 *
2620 * @param WC_Order $wcOrder
2621 *
2622 * @return bool
2623 */
2624 public function isTamaraOrder($wcOrder)
2625 {
2626 if (!$wcOrder instanceof \WC_Order) {
2627 return false;
2628 }
2629
2630 if ($this->isTamaraGateway($wcOrder->get_payment_method())) {
2631 return true;
2632 }
2633
2634 return !empty($wcOrder->get_meta('_tamara_checkout_session_id'))
2635 || !empty($wcOrder->get_meta('_tamara_order_id'))
2636 || !empty($wcOrder->get_meta('tamara_order_id'))
2637 || !empty(get_post_meta($wcOrder->get_id(), 'tamara_authorized', true));
2638 }
2639
2640 /**
2641 * Get all product ids of items in cart, including parent and child ids.
2642 *
2643 * @return array
2644 */
2645 public function getAllProductIdsInCart()
2646 {
2647 $productIds = [];
2648 if (empty(WC()->cart)) {
2649 return $productIds;
2650 }
2651
2652 $allCartItems = WC()->cart->get_cart();
2653 foreach ($allCartItems as $item => $values) {
2654 $itemId = $values['data']->get_id() ?? null;
2655 $productIds[] = $itemId;
2656 $product = wc_get_product($itemId);
2657 // Check if a product is a variation add add its parent id to the list.
2658 if ($product instanceof \WC_Product_Variation) {
2659 $productParentId = $product->get_parent_id() ?? null;
2660 if (!in_array($productParentId, $productIds)) {
2661 $productIds[] = $productParentId;
2662 }
2663 }
2664 }
2665
2666 return $productIds;
2667 }
2668
2669 /**
2670 * Get all category ids of items in cart, including ancestors and subcategories.
2671 *
2672 * @return array
2673 */
2674 public function getAllProductCategoryIdsInCart()
2675 {
2676 $allCartItems = WC()->cart->get_cart();
2677 $allProductCategoryIds = [];
2678
2679 foreach ($allCartItems as $item => $values) {
2680 $productId = $values['data']->get_id() ?? null;
2681 $allProductCategoryIds = array_merge($allProductCategoryIds, wc_get_product_cat_ids($productId));
2682 }
2683
2684 return $allProductCategoryIds;
2685 }
2686
2687 /**
2688 * Get the array of Tamara excluded product ids
2689 *
2690 * @return array
2691 */
2692 public function getExcludedProductIds()
2693 {
2694 $tamaraExcludedProductsOption = $this->getWCTamaraGatewayOptions()['excluded_products'] ?? '';
2695
2696 return array_map('trim', explode(',',
2697 $tamaraExcludedProductsOption));
2698 }
2699
2700 /**
2701 * Get the array of Tamara excluded product category ids
2702 *
2703 * @return array
2704 */
2705 public function getExcludedProductCategoryIds()
2706 {
2707 $tamaraExcludedProductCategoriesOption = $this->getWCTamaraGatewayOptions()['excluded_product_categories'] ?? '';
2708
2709 return array_map('trim', explode(',',
2710 $tamaraExcludedProductCategoriesOption));
2711 }
2712
2713 /**
2714 * Check if a product is excluded from using Tamara
2715 *
2716 * @param int $productId
2717 *
2718 * @return bool
2719 */
2720 public function isExcludedProduct($productId)
2721 {
2722 return !!(in_array($productId, $this->getExcludedProductIds()));
2723 }
2724
2725 /**
2726 * Check if there's any product category id is excluded from using Tamara
2727 *
2728 * @param array $productCategoryIds
2729 *
2730 * @return bool
2731 */
2732 public function isExcludedProductCategory($productCategoryIds)
2733 {
2734 return !!(count(array_intersect($productCategoryIds, $this->getExcludedProductCategoryIds())));
2735 }
2736
2737 /**
2738 * Get the base url of plugin
2739 *
2740 * @return string
2741 */
2742 public function getBaseUrl()
2743 {
2744 return $this->baseUrl;
2745 }
2746
2747 /**
2748 * Get appropriate instalment plan according to current product variation price
2749 * and return the value through ajax call
2750 */
2751 public function getInstalmentPlanAccordingToProductVariation()
2752 {
2753 $variationPrice = isset($_POST['variationPrice']) ? sanitize_text_field(wp_unslash($_POST['variationPrice'])) : '';
2754 if (!empty($variationPrice) && !$this->isWidgetPopupDisabled()) {
2755 $currency = get_woocommerce_currency();
2756 // Todo: Handle and re-generate if PDP is not initialized when there is no plan for the smallest variation price
2757 $PDPWidgetData = $this->populatePDPWidgetBasedOnPrice($variationPrice, $currency) ?? [];
2758 if (!empty($PDPWidgetData['paymentType'])) {
2759 wp_send_json([
2760 'message' => 'success',
2761 'data' => $PDPWidgetData,
2762 ]);
2763 }
2764 }
2765
2766 wp_send_json(
2767 [
2768 'message' => 'No payment type found for this price',
2769 ]
2770 );
2771 }
2772
2773 /**
2774 * Update Tamara Checkout Params on updated_checkout event
2775 * and return the value through ajax call
2776 */
2777 public function updateTamaraCheckoutParams()
2778 {
2779 $storeCurrency = get_woocommerce_currency();
2780 $countryCode = $this->getWCTamaraGatewayService()->getCurrentCountryCode();
2781
2782 wp_send_json(
2783 [
2784 'message' => 'success',
2785 'country' => $countryCode,
2786 'currency' => $storeCurrency,
2787 ]
2788 );
2789 }
2790
2791 /**
2792 * Return the first value in variation product price array that meets the smallest instalment plan
2793 *
2794 * @param array $variationPriceArr
2795 *
2796 * @return mixed
2797 */
2798 public function getAppropriateVariationProductPrice($variationPriceArr)
2799 {
2800 return array_values($variationPriceArr)[0] ?? null;
2801 foreach ($variationPriceArr as $item => $variationPrice) {
2802 if (($this->getWCTamaraGatewayService()->populateMinLimit() <= $variationPrice
2803 && $this->getWCTamaraGatewayService()->populateMaxLimit() >= $variationPrice)
2804 || ($this->populatePayInXsLimitAmountBasedOnProductPrice($variationPrice)['instalmentMinAmount'] <= $variationPrice
2805 && $this->populatePayInXsLimitAmountBasedOnProductPrice($variationPrice)['instalmentMaxAmount'] >= $variationPrice)) {
2806 return $variationPrice;
2807 break;
2808 } elseif ($this->isAlwaysShowWidgetPopupEnabled() && !$this->isWidgetPopupDisabled()) {
2809 return $variationPrice;
2810 break;
2811 }
2812 }
2813
2814 return null;
2815 }
2816
2817 /**
2818 * Override Wc Clear Cart function and keep cart for orders with cancelled/failed payments from Tamara
2819 */
2820 public function overrideWcClearCart()
2821 {
2822 if (!empty(WC()->session->order_awaiting_payment)) {
2823 $order = wc_get_order(WC()->session->order_awaiting_payment);
2824 if ($order && $order->get_id() > 0) {
2825 // If the order has not failed, or is not pending, the order must have gone through.
2826 if ($this->isTamaraGateway($order->get_payment_method())) {
2827
2828 if ($order->has_status(array('tamara-p-canceled', 'tamara-p-failed'))) {
2829 remove_action('template_redirect', 'wc_clear_cart_after_payment');
2830 remove_action('template_redirect', 'wc_clear_cart_after_payment', 20);
2831 }
2832
2833 }
2834 }
2835 }
2836 }
2837
2838 /**
2839 * Cancel a pending order and add Tamara payment cancelled/failed notice.
2840 */
2841 public function cancelOrder()
2842 {
2843 if (
2844 isset($_GET['cancel_order']) &&
2845 isset($_GET['order']) &&
2846 isset($_GET['order_id']) &&
2847 (isset($_GET['_wpnonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'])), 'woocommerce-cancel_order'))
2848 ) {
2849 wc_nocache_headers();
2850 $order_key = wp_unslash($_GET['order']); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
2851 $order_id = absint($_GET['order_id']);
2852 $order = wc_get_order($order_id);
2853 $paymentMethod = $order->get_payment_method();
2854 $user_can_cancel = current_user_can('cancel_order', $order_id);
2855 $order_can_cancel = $order->has_status(apply_filters('woocommerce_valid_order_statuses_for_cancel', array('pending', 'failed'), $order));
2856 $redirect = isset($_GET['redirect']) ? wp_unslash($_GET['redirect']) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
2857
2858 if ($user_can_cancel && !$order_can_cancel && $this->isTamaraGateway($paymentMethod)) {
2859 wc_clear_notices();
2860 wc_add_notice(__('Your payment via Tamara has failed, please try again with a different payment method.', 'tamara-checkout'), 'error');
2861 }
2862
2863 if ($redirect) {
2864 wp_safe_redirect($redirect);
2865 exit;
2866 }
2867 }
2868 }
2869
2870 /**
2871 * Update phone number and billing country on every ajax call on checkout
2872 *
2873 * @param $postedData
2874 *
2875 * @return void
2876 */
2877 public function getUpdatedPhoneNumberOnCheckout($postedData)
2878 {
2879 // Parsing posted data on checkout
2880 $post = array();
2881 $vars = explode('&', $postedData);
2882 foreach ($vars as $k => $value) {
2883 $v = explode('=', urldecode($value));
2884 $post[$v[0]] = $v[1] ?? '';
2885 }
2886
2887 // Update phone number and billing country from posted data
2888 $this->customerPhoneNumber = $post['billing_phone'] ?? '';
2889 $this->customerBillingCountry = isset($post['billing_country']) ? strtoupper($post['billing_country']) : '';
2890 }
2891
2892 /**
2893 * Return customer phone number from checkout POST, order-pay order, or WC customer.
2894 *
2895 * @return string|null
2896 */
2897 public function getCustomerPhoneNumber()
2898 {
2899 if (!empty($this->customerPhoneNumber)) {
2900 return $this->customerPhoneNumber;
2901 }
2902
2903 $order = $this->getOrderPayOrder();
2904 if ($order && $order->get_billing_phone()) {
2905 return $order->get_billing_phone();
2906 }
2907
2908 if (function_exists('WC') && WC()->customer && WC()->customer->get_billing_phone()) {
2909 return WC()->customer->get_billing_phone();
2910 }
2911
2912 return $this->customerPhoneNumber;
2913 }
2914
2915 /**
2916 * Return customer billing country from checkout POST, order-pay order, or WC customer.
2917 *
2918 * @return string
2919 */
2920 public function getCustomerBillingCountry()
2921 {
2922 if (!empty($this->customerBillingCountry)) {
2923 return strtoupper($this->customerBillingCountry);
2924 }
2925
2926 $order = $this->getOrderPayOrder();
2927 if ($order && $order->get_billing_country()) {
2928 return strtoupper($order->get_billing_country());
2929 }
2930
2931 if (function_exists('WC') && WC()->customer && WC()->customer->get_billing_country()) {
2932 return strtoupper(WC()->customer->get_billing_country());
2933 }
2934
2935 return '';
2936 }
2937
2938 /**
2939 * Resolve the WC order being paid on the order-pay / pay-for-order flow, if any.
2940 *
2941 * Query vars (and is_wc_endpoint_url('order-pay') / is_checkout_pay_page()) are only
2942 * populated after parse_request. Pay-for-order still includes the secret order key
2943 * in GET/POST, so that key is used to load the order when the endpoint is not ready.
2944 *
2945 * @return \WC_Order|null
2946 */
2947 protected function getOrderPayOrder()
2948 {
2949 $orderId = $this->getOrderPayOrderIdFromRequest();
2950 $orderKey = $this->getOrderPayOrderKeyFromRequest();
2951
2952 // Fallback: resolve from order key when the order-pay query var is not available yet.
2953 if (!$orderId && $orderKey && function_exists('wc_get_order_id_by_order_key')) {
2954 $orderId = absint(wc_get_order_id_by_order_key($orderKey));
2955 }
2956
2957 if (!$orderId) {
2958 return null;
2959 }
2960
2961 $order = wc_get_order($orderId);
2962 if (!($order instanceof \WC_Order)) {
2963 return null;
2964 }
2965
2966 if ($orderKey && !hash_equals((string) $order->get_order_key(), (string) $orderKey)) {
2967 return null;
2968 }
2969
2970 // Numeric order-pay IDs without a matching key must not be trusted.
2971 if (!$orderKey) {
2972 return null;
2973 }
2974
2975 return $order;
2976 }
2977
2978 /**
2979 * Read the order-pay ID from query vars, request, or pretty permalink path.
2980 *
2981 * @return int
2982 */
2983 protected function getOrderPayOrderIdFromRequest()
2984 {
2985 if (!empty($GLOBALS['wp']->query_vars['order-pay'])) {
2986 return absint($GLOBALS['wp']->query_vars['order-pay']);
2987 }
2988
2989 if (function_exists('get_query_var')) {
2990 $orderId = absint(get_query_var('order-pay'));
2991 if ($orderId) {
2992 return $orderId;
2993 }
2994 }
2995
2996 if (isset($_GET['order-pay'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
2997 return absint(wp_unslash($_GET['order-pay']));
2998 }
2999
3000 if (!empty($_SERVER['REQUEST_URI']) && preg_match('#/order-pay/(\d+)/?#', (string) wp_unslash($_SERVER['REQUEST_URI']), $matches)) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
3001 return absint($matches[1]);
3002 }
3003
3004 return 0;
3005 }
3006
3007 /**
3008 * Read the WooCommerce order key from pay-for-order / order-received request data.
3009 *
3010 * @return string
3011 */
3012 protected function getOrderPayOrderKeyFromRequest()
3013 {
3014 if (isset($_GET['key'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
3015 return wc_clean(wp_unslash($_GET['key']));
3016 }
3017
3018 if (isset($_POST['key'])) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
3019 return wc_clean(wp_unslash($_POST['key']));
3020 }
3021
3022 return '';
3023 }
3024
3025 /**
3026 * Update WC Order Payment method according to Tamara order
3027 *
3028 * @param $wcOrderId
3029 * @param $wcOrder
3030 *
3031 * @throws \Illuminate\Contracts\Container\BindingResolutionException
3032 */
3033 public function updateWcOrderPaymentMethodAccordingToTamaraOrder($wcOrderId, $wcOrder)
3034 {
3035 $tamaraOrder = $this->getTamaraOrderByWcOrderId($wcOrderId);
3036 $paymentType = $tamaraOrder->getPaymentType();
3037 update_post_meta($wcOrderId, 'tamara_payment_type', $paymentType);
3038
3039 if ($paymentType === $this->getWCTamaraGatewayService()->getPaymentTypeMapping()[static::TAMARA_GATEWAY_ID]) {
3040 update_post_meta($wcOrderId, 'payment_method', static::TAMARA_GATEWAY_ID);
3041 delete_post_meta($wcOrderId, 'tamara_payment_type_instalment');
3042 $wcOrder->set_payment_method(static::TAMARA_GATEWAY_ID);
3043 $wcOrder->set_payment_method_title($this->getWCTamaraGatewayService()::TAMARA_GATEWAY_DEFAULT_TITLE);
3044 $wcOrder->save();
3045 } elseif ($paymentType === WCTamaraGateway::PAYMENT_TYPE_PAY_NEXT_MONTH) {
3046 update_post_meta($wcOrderId, 'payment_method', static::TAMARA_GATEWAY_ID);
3047 delete_post_meta($wcOrderId, 'tamara_payment_type_instalment');
3048 $wcOrder->set_payment_method(static::TAMARA_GATEWAY_ID);
3049 $wcOrder->set_payment_method_title($this->getWCTamaraGatewayService()::TAMARA_GATEWAY_PAY_NEXT_MONTH_DEFAULT_TITLE);
3050 $wcOrder->save();
3051 } elseif ($paymentType === WCTamaraGateway::PAYMENT_TYPE_PAY_NOW) {
3052 update_post_meta($wcOrderId, 'payment_method', static::TAMARA_GATEWAY_ID);
3053 delete_post_meta($wcOrderId, 'tamara_payment_type_instalment');
3054 $wcOrder->set_payment_method(static::TAMARA_GATEWAY_ID);
3055 $wcOrder->set_payment_method_title($this->getWCTamaraGatewayService()::TAMARA_GATEWAY_PAY_NOW_DEFAULT_TITLE);
3056 $wcOrder->save();
3057 } else {
3058 $instalment = $tamaraOrder->getInstalments();
3059 update_post_meta($wcOrderId, 'payment_method', static::TAMARA_GATEWAY_PAY_IN_X.$instalment);
3060 update_post_meta($wcOrderId, 'tamara_payment_type_instalment', $instalment);
3061 $wcOrder->set_payment_method(static::TAMARA_GATEWAY_PAY_IN_X.$instalment);
3062 $wcOrder->set_payment_method_title($this->getWCTamaraGatewayService()->getPayInXTitle($instalment));
3063 $wcOrder->save();
3064 }
3065 }
3066
3067 /**
3068 * Add Tamara Single Checkout to existing available gateways
3069 *
3070 * @param $availableGateways
3071 *
3072 * @return array
3073 * @throws \Illuminate\Contracts\Container\BindingResolutionException
3074 */
3075 protected function possiblyAddTamaraSingleCheckout($availableGateways)
3076 {
3077 $singleCheckoutService = [static::TAMARA_GATEWAY_CHECKOUT_ID => $this->getWCTamaraGatewayCheckoutService()];
3078 $availableGateways = $this->mergeTamaraPaymentMethodsAfterPayLaterOffset($singleCheckoutService, $availableGateways);
3079 $payNowService = [static::TAMARA_GATEWAY_PAY_NOW => $this->getWCTamaraGatewayPayNowService()];
3080 $availableGateways = $this->mergeTamaraPaymentMethodsAfterPayLaterOffset($payNowService, $availableGateways);
3081 unset($availableGateways[static::TAMARA_GATEWAY_ID]);
3082 return $availableGateways;
3083 }
3084
3085 /**
3086 * Add other Tamara payment methods right after Pay Later offset on checkout
3087 *
3088 * @param $array
3089 * @param $availableGateways
3090 *
3091 * @return array
3092 */
3093 protected function mergeTamaraPaymentMethodsAfterPayLaterOffset($array, $availableGateways)
3094 {
3095 $tamaraPayLaterKey = static::TAMARA_GATEWAY_ID;
3096 $tamaraPayLaterOffset = array_search($tamaraPayLaterKey, array_keys(WC()->payment_gateways->payment_gateways()));
3097
3098 return array_merge(
3099 array_slice($availableGateways, 0, $tamaraPayLaterOffset),
3100 $array,
3101 array_slice($availableGateways, $tamaraPayLaterOffset, null)
3102 );
3103 }
3104
3105 protected function isAfterTamaraAuthorised($orderStatus)
3106 {
3107 return !!in_array($orderStatus, $this->getAfterTamaraAuthorisedStatuses());
3108 }
3109
3110 protected function getAfterTamaraAuthorisedStatuses()
3111 {
3112 return [
3113 static::TAMARA_CANCELED_STATUS,
3114 static::TAMARA_PARTIALLY_CAPTURED_STATUS,
3115 static::TAMARA_FULLY_CAPTURED_STATUS,
3116 static::TAMARA_PARTIALLY_REFUNDED_STATUS,
3117 static::TAMARA_FULLY_REFUNDED_STATUS
3118 ];
3119 }
3120
3121 protected function isSupportedCountry($countryCode)
3122 {
3123 $supportedCountries = ['SA', 'AE', 'KW', 'BH'];
3124
3125 return !!in_array($countryCode, $supportedCountries);
3126 }
3127
3128 /**
3129 * Get Tamara order status from remote
3130 *
3131 * @param $wcOrderId
3132 *
3133 * @return string
3134 * @throws \Illuminate\Contracts\Container\BindingResolutionException
3135 */
3136 public function getTamaraOrderStatus($wcOrderId)
3137 {
3138 $tamaraOrder = $this->getTamaraOrderByWcOrderId($wcOrderId);
3139 if ($tamaraOrder) {
3140 return $tamaraOrder->getStatus() ?? null;
3141 }
3142
3143 return null;
3144 }
3145
3146 }
3147