From 51ac063af8e6efd86625ae63545c4be2fb87e146 Mon Sep 17 00:00:00 2001 From: Alagesan Date: Thu, 4 Jul 2024 15:30:37 +0530 Subject: [PATCH 1/2] - order sync when block checkout enabled - order token update when import order --- src/Api/AbandonedCart/Checkout.php | 1067 ++++++++++--------- src/Api/AbandonedCart/RestApi.php | 1555 ++++++++++++++-------------- src/Api/Imports/Imports.php | 549 +++++----- src/Main.php | 1113 ++++++++++---------- 4 files changed, 2241 insertions(+), 2043 deletions(-) diff --git a/src/Api/AbandonedCart/Checkout.php b/src/Api/AbandonedCart/Checkout.php index d49e6bab..a2fc79a7 100644 --- a/src/Api/AbandonedCart/Checkout.php +++ b/src/Api/AbandonedCart/Checkout.php @@ -4,502 +4,573 @@ use Exception; -class Checkout extends RestApi -{ - function __construct() - { - parent::__construct(); - } - - function recoverHeldOrders() - { - $recover_held_orders = apply_filters('rnoc_recover_held_orders', 'no'); - return ($recover_held_orders == "no"); - } - - /** - * set retainful related data to order - */ - function setRetainfulOrderData() - { - $draft_order = self::$woocommerce->getSession('store_api_draft_order'); - if (!empty($draft_order) && intval($draft_order) > 0) { - $cart_token = $this->retrieveCartToken(); - $draft_order_cart_token = self::$woocommerce->getPostMeta(intval($draft_order), $this->cart_token_key_for_db); - if (empty($draft_order_cart_token) && empty($cart_token)) { - $cart_token = $this->getCartToken(); - } - $this->purchaseComplete(intval($draft_order)); - } - } - - /** - * purchase complete - * @param $order_id - * @return null - */ - function purchaseComplete($order_id) - { - if (empty($order_id)) { - return NULL; - } - //TODO remove carthash from session after success place order - $cart_token = $this->retrieveCartToken(); - self::$settings->logMessage(array("cart_token" => $cart_token, "order_id" => $order_id), 'purchaseComplete'); - if (!empty($cart_token)) { - $cart_created_at = $this->userCartCreatedAt(); - $user_ip = $this->retrieveUserIp(); - $is_buyer_accepts_marketing = ($this->isBuyerAcceptsMarketing()) ? 1 : 0; - //$cart_hash = self::$storage->getValue('rnoc_current_cart_hash'); - $cart_hash = $this->generateCartHash(); - $recovered_at = self::$storage->getValue('rnoc_recovered_at'); - $recovered_by = self::$storage->getValue('rnoc_recovered_by_retainful'); - $recovered_cart_token = self::$storage->getValue('rnoc_recovered_cart_token'); - $user_agent = $this->getUserAgent(); - $user_accept_language = $this->getUserAcceptLanguage(); - - $order_object = self::$woocommerce->getOrder($order_id); - if (is_object($order_object) && !empty($order_object)) { - $order_object->update_meta_data($this->cart_token_key_for_db, $cart_token); - $order_object->update_meta_data($this->cart_hash_key_for_db, $cart_hash); - $order_object->update_meta_data($this->cart_tracking_started_key_for_db, $cart_created_at); - $order_object->update_meta_data($this->user_ip_key_for_db, $user_ip); - $order_object->update_meta_data($this->accepts_marketing_key_for_db, $is_buyer_accepts_marketing); - $order_object->update_meta_data('_rnoc_recovered_at', $recovered_at); - $order_object->update_meta_data('_rnoc_recovered_by', $recovered_by); - $order_object->update_meta_data('_rnoc_recovered_cart_token', $recovered_cart_token); - $order_object->update_meta_data('_rnoc_get_http_user_agent', $user_agent); - $order_object->update_meta_data('_rnoc_get_http_accept_language', $user_accept_language); - $order_object->update_meta_data($this->pending_recovery_key_for_db, true); - $order_object->save(); - } - - //$this->markOrderAsPendingRecovery($order_id); - //$this->unsetOrderTempData(); - } - return NULL; - } - - /** - * Order had some changes - * @param $order_id - * @return void|null - */ - function orderUpdated($order_id) - { - self::$settings->logMessage(array("order_id" => $order_id), 'OrderUpdated '); - - if ($this->needInstantOrderSync()) { - $this->syncOrder($order_id); - } else { - $this->scheduleCartSync($order_id); - } - } - - - /** - * Order has been updated from the shop backend. - * @param $order_id - * @return void|null - */ - function OrderUpdatedShopBackend($order_id) - { - self::$settings->logMessage(array("order_id" => $order_id), 'OrderUpdatedShopBackend '); - if (is_admin()) { - if ($this->needInstantOrderSync()) { - $this->syncOrder($order_id); - } else { - $this->scheduleCartSync($order_id); - } - } - } - - /** - * @return mixed|void - */ - function generateNocCouponForManualOrders() - { - $is_enabled = self::$settings->isNextOrderCouponEnabled(); - $has_backorder_coupon = self::$settings->autoGenerateCouponsForOldOrders(); - $need_noc_coupon = ($is_enabled && $has_backorder_coupon && is_admin()); - return apply_filters('rnoc_generate_noc_coupon_for_manual_orders', $need_noc_coupon, $this); - } - - function changeWebHookHeader($http_args, $order_id, $webhook_id) - { - - if ($webhook_id <= 0 || !class_exists('WC_Webhook') || !self::$settings->isConnectionActive()) return $http_args; - - try { - $webhook = new \WC_Webhook($webhook_id); - $topic = $webhook->get_topic(); - $topic_status = self::$settings->getWebHookStatus(); - if (!isset($topic_status[$topic]) || !$topic_status[$topic]) { - return $http_args; - } - $delivery_url = $webhook->get_delivery_url(); - $site_delivery_url = self::$settings->getDeliveryUrl(); - if ($delivery_url != $site_delivery_url || $order_id <= 0) { - return $http_args; - } - - $order = self::$woocommerce->getOrder($order_id); - $order_obj = new Order(); - $cart_token = self::$woocommerce->getOrderMeta($order, $this->cart_token_key_for_db); - self::$settings->logMessage(array("cart_token" => $cart_token), 'Cart Token'); - - if (empty($cart_token)) { - //Usually we should not force generate the cart token as this would sync all the old orders otherwise, if their status changes. - $force_generate_cart_token = apply_filters('rnoc_force_generate_cart_token', false, $http_args, $order_id, $webhook_id); - - if ($force_generate_cart_token === true) { - //Let's generate a token and set to the order meta - $cart_token = $this->generateCartToken(); - self::$woocommerce->setOrderMeta($order_id, $this->cart_token_key_for_db, $cart_token); - self::$settings->logMessage(array("cart_token" => $cart_token), 'Force Generated Cart Token'); - } - } - - if (empty($cart_token)) { - //bail on empty cart token - return $http_args; - } - - //self::$settings->logMessage(array("order_id" => $order_id), 'Triggering Webhook ID:'.$stored_webhook_id); - $order_data = $order_obj->getOrderData($order); - // $logger->add('Retainful','Order Data:'.json_encode($order_data)); - if (!empty($cart_token) && $order_data) { - $client_ip = self::$woocommerce->getOrderMeta($order, $this->user_ip_key_for_db); - $token = self::$woocommerce->getOrderMeta($order, $this->cart_token_key_for_db); - $app_id = self::$settings->getApiKey(); - $extra_headers = array( - "X-Client-Referrer-IP" => (!empty($client_ip)) ? $client_ip : null, - "X-Retainful-Version" => RNOC_VERSION, - "X-Cart-Token" => $token, - "Cart-Token" => $token, - "app-id" => $app_id, - "app_id" => $app_id, - "Content-Type" => 'application/json' - ); - foreach ($extra_headers as $key => $value) { - $http_args['headers'][$key] = $value; - } - //$logger->add('Retainful','App id:'.$app_id); - $cart_hash = $this->encryptData($order_data); - $body = array( - 'data' => $cart_hash - ); - $http_args['body'] = trim(wp_json_encode($body)); - } - } catch (Exception $e) { - - } - return $http_args; - } - - /** - * Sync the order with API - * @param $order_id - * @return void|null - */ - function syncOrder($order_id) - { - if (empty($order_id) || self::$settings->isBackgroundOrderSyncEnabled()) { - return null; - } - $order = self::$woocommerce->getOrder($order_id); - $order_obj = new Order(); - $cart_token = self::$woocommerce->getOrderMeta($order, $this->cart_token_key_for_db); - $cart_token = apply_filters('rnoc_sync_order_change_order_token', $cart_token, $order_id, $this); - if (empty($cart_token)) { - return; - } - $order_status = self::$woocommerce->getStatus($order); - $order_cancelled_at = self::$woocommerce->getOrderMeta($order, $this->order_cancelled_date_key_for_db); - // handle order cancellation - if (!$order_cancelled_at && 'cancelled' === $order_status) { - $order_cancelled_at = current_time('timestamp', true); - self::$woocommerce->setOrderMeta($order_id, $this->order_cancelled_date_key_for_db, $order_cancelled_at); - $this->unsetOrderTempData(); - } - $order_data = $order_obj->getOrderData($order); - if (empty($order_data)) { - return null; - } - $order_data['cancelled_at'] = (!empty($order_cancelled_at)) ? $this->formatToIso8601($order_cancelled_at) : NULL; - self::$settings->logMessage(array("order_id" => $order_id), 'syncOrder'); - $cart_hash = $this->encryptData($order_data); - $client_ip = self::$woocommerce->getOrderMeta($order, $this->user_ip_key_for_db); - if (!empty($cart_hash)) { - $token = self::$woocommerce->getOrderMeta($order, $this->cart_token_key_for_db); - $extra_headers = array( - "X-Client-Referrer-IP" => (!empty($client_ip)) ? $client_ip : null, - "X-Retainful-Version" => RNOC_VERSION, - "X-Cart-Token" => $token, - "Cart-Token" => $token - ); - $this->syncCart($cart_hash, $extra_headers); - } - } - - /** - * Sync the order - * @param $order_id - */ - function syncOrderByScheduler($order_id) - { - $this->syncOrder($order_id); - } - - /** - * schedule the sync of the cart - * @param $order_id - */ - function scheduleCartSync($order_id) - { - if (!apply_filters('rnoc_schedule_cart_sync', true)) { - return; - } - $hook = 'retainful_sync_abandoned_cart_order'; - $meta_key = '_rnoc_order_id'; - if (self::$settings->hasAnyActiveScheduleExists($hook, $order_id, $meta_key) == false) { - self::$settings->scheduleEvents($hook, current_time('timestamp') + 60, array($meta_key => $order_id)); - } - } - - /** - * Clear any persistent cart session data for logged in customers - * @param int $order_id order ID - * @param string $old_status - * @param string $new_status - */ - public function orderStatusChanged($order_id, $old_status, $new_status) - { - global $wp; - self::$settings->logMessage(array("order_id" => $order_id), 'orderStatusChanged'); - try { - // PayPal IPN request - if (isset($wp->query_vars['wc-api']) && !empty($wp->query_vars['wc-api']) && ('WC_Gateway_Paypal' === $wp->query_vars['wc-api'])) { - $order = self::$woocommerce->getOrder($order_id); - // PayPal order is completed or authorized: clear any user session - // data so that we don't have to rely on the thank-you page rendering - if ((self::$woocommerce->isOrderPaid($order) || $new_status == 'on-hold') && ($user_id = self::$woocommerce->getOrderUserId($order))) { - delete_user_meta($user_id, '_woocommerce_persistent_cart_' . get_current_blog_id()); - if ($this->isPendingRecovery($user_id)) { - self::$woocommerce->setOrderMeta($order_id, $this->pending_recovery_key_for_db, true); - } - if ($this->retrieveCartToken($user_id)) { - $this->removeTempDataForUser($user_id); - } - } - } - } catch (Exception $e) { - } - } - - /** - * Check the order is placed or not - * @param $order - * @param $old_status - * @param $new_status - * @return bool|mixed|void - */ - function isPlaced($order, $old_status, $new_status) - { - $placed = self::$woocommerce->isOrderPaid($order) || ($new_status === 'on-hold' && !$this->recoverHeldOrders()); - $placed = apply_filters('rnoc_abandoned_cart_is_order_get_placed', $placed, $order, $old_status, $new_status, $this); - return $placed; - } - - /** - * HAndle order completion in order page - * @param $order_id - */ - function payPageOrderCompletion($order_id) - { - $this->unsetOrderTempData(); - } - - /** - * @param $order_id - */ - function checkoutOrderProcessed($order_id) - { - self::$settings->logMessage(array("order_id" => $order_id), 'checkoutOrderProcessed'); - /* if ($this->isPendingRecovery()) { - $this->markOrderAsPendingRecovery($order_id); - }*/ - try { - $cart_token = $this->retrieveCartToken(); - if (!empty($cart_token)) { - $order = self::$woocommerce->getOrder($order_id); - $this->purchaseComplete($order_id); - $this->syncOrderToAPI($order, $order_id); - //$this->unsetOrderTempData(); - } - } catch (Exception $e) { - } - } - - function apiCheckoutOrderProcessed($order) - { - if (!is_object($order)) { - return; - } - $order_id = self::$woocommerce->getOrderId($order); - self::$settings->logMessage(array("order_id" => $order_id), 'checkoutOrderProcessed'); - /* if ($this->isPendingRecovery()) { - $this->markOrderAsPendingRecovery($order_id); - }*/ - try { - $cart_token = $this->retrieveCartToken(); - if (!empty($cart_token)) { - //$order = self::$woocommerce->getOrder($order_id); - $this->purchaseComplete($order_id); - $this->syncOrderToAPI($order, $order_id); - //$this->unsetOrderTempData(); - } - } catch (Exception $e) { - } - } - - /** - * sync order to api - * @param $order - * @param $order_id - */ - function syncOrderToAPI($order, $order_id) - { - if (self::$settings->isBackgroundOrderSyncEnabled()) { - return; - } - if ($this->needInstantOrderSync()) { - $order_obj = new Order(); - $cart = $order_obj->getOrderData($order); - if (!empty($cart)) { - $cart_hash = $this->encryptData($cart); - //Reduce the loading speed - $client_ip = self::$woocommerce->getOrderMeta($order, $this->user_ip_key_for_db); - $token = self::$woocommerce->getOrderMeta($order, $this->cart_token_key_for_db); - self::$settings->logMessage(array('order_id' => $order_id, 'token' => $token, 'client_ip' => $client_ip), 'syncOrderToAPI'); - if (!empty($cart_hash)) { - - $extra_headers = array( - "X-Client-Referrer-IP" => (!empty($client_ip)) ? $client_ip : null, - "X-Retainful-Version" => RNOC_VERSION, - "X-Cart-Token" => $token, - "Cart-Token" => $token - ); - $this->syncCart($cart_hash, $extra_headers); - } - } else { - self::$settings->logMessage(array('order_id' => $order_id), 'Failed syncOrderToAPI'); - } - } else { - $this->scheduleCartSync($order_id); - } - } - - /** - * need the instant sync or not - * @return mixed|void - */ - function needInstantOrderSync() - { - return apply_filters('rnoc_sync_order_data_instantly_to_api', true); - } - - /** - * Assign cart token for Order - * @param $cart_token - * @param $order_id - */ - function setOrderCartToken($cart_token, $order_id) - { - if (empty($cart_token)) { - $cart_token = $this->generateCartToken(); - } - self::$woocommerce->setOrderMeta($order_id, $this->cart_token_key_for_db, $cart_token); - } - - /** - * Update order on successful payment - * @param $result - * @param $order_id - * @return mixed - */ - function maybeUpdateOrderOnSuccessfulPayment($result, $order_id) - { - self::$settings->logMessage(array("order_id" => $order_id), 'maybeUpdateOrderOnSuccessfulPayment'); - $order = self::$woocommerce->getOrder($order_id); - if (!$cart_token = self::$woocommerce->getOrderMeta($order, $this->cart_token_key_for_db)) { - return $result; - } - $this->syncOrderToAPI($order, $order_id); - //$this->unsetOrderTempData(); - return $result; - } - - /** - * Payment completed - * @param $order_id - */ - function paymentCompleted($order_id) - { - self::$settings->logMessage(array("order_id" => $order_id), 'paymentCompleted'); - if (self::$woocommerce->getOrder($order_id)) { - self::$woocommerce->setCustomerPayingForOrder($order_id); - } - $cart_token = $this->retrieveCartToken(); - if (!empty($cart_token)) { - $this->unsetOrderTempData(); - } - } - - /** - * Unset the temporary cart token and order data - * @param null $user_id - */ - function unsetOrderTempData($user_id = NULL) - { - self::$storage->removeValue($this->cart_token_key); - self::$storage->removeValue($this->pending_recovery_key); - self::$storage->removeValue($this->cart_tracking_started_key); - self::$storage->removeValue($this->previous_cart_hash_key); - //This was set in plugin since 2.0.4 - self::$storage->removeValue('rnoc_force_refresh_cart'); - self::$storage->removeValue('rnoc_recovered_at'); - self::$storage->removeValue('rnoc_current_cart_hash'); - self::$storage->removeValue('rnoc_recovered_by_retainful'); - self::$storage->removeValue('rnoc_recovered_cart_token'); - if ($user_id || ($user_id = get_current_user_id())) { - $this->removeTempDataForUser($user_id); - } - } - - /** - * Delete temp data of the user - * @param $user_id - */ - function removeTempDataForUser($user_id) - { - delete_user_meta($user_id, $this->cart_token_key_for_db); - delete_user_meta($user_id, $this->pending_recovery_key_for_db); - delete_user_meta($user_id, $this->cart_tracking_started_key_for_db); - delete_user_meta($user_id, $this->user_ip_key_for_db); - } - - /** - * Mark order as pending recovery - * @param $order_id - */ - function markOrderAsPendingRecovery($order_id) - { - self::$settings->logMessage(array("order_id" => $order_id), 'markOrderAsPendingRecovery'); - /*$order = self::$woocommerce->getOrder($order_id); - if (!$order instanceof \WC_Order) { - return; - }*/ - self::$woocommerce->setOrderMeta($order_id, $this->pending_recovery_key_for_db, true); - } +class Checkout extends RestApi { + function __construct() { + parent::__construct(); + } + + function recoverHeldOrders() { + $recover_held_orders = apply_filters( 'rnoc_recover_held_orders', 'no' ); + + return ( $recover_held_orders == "no" ); + } + + /** + * set retainful related data to order + */ + function setRetainfulOrderData() { + $draft_order = self::$woocommerce->getSession( 'store_api_draft_order' ); + if ( ! empty( $draft_order ) && intval( $draft_order ) > 0 ) { + $order = self::$woocommerce->getOrder( $draft_order ); + $cart_token = self::$woocommerce->getOrderMeta( $order, $this->cart_token_key_for_db ); + if ( empty( $cart_token ) ) { + $logger = wc_get_logger(); + $logger->add( 'Retainful', 'Draft order id:' . $draft_order ); + $cart_token = $this->retrieveCartToken(); + $logger->add( 'Retainful', 'Draft retrive token:' . $cart_token ); + $draft_order_cart_token = self::$woocommerce->getPostMeta( intval( $draft_order ), $this->cart_token_key_for_db ); + if ( empty( $draft_order_cart_token ) && empty( $cart_token ) ) { + $cart_token = $this->getCartToken(); + $logger->add( 'Retainful', 'Draft regenerate token:' . $cart_token ); + } + $logger->add( 'Retainful', 'Draft order token:' . $cart_token ); + } + + $this->purchaseComplete( intval( $draft_order ) ); + } + } + + /** + * purchase complete + * + * @param $order_id + * + * @return null + */ + function purchaseComplete( $order_id ) { + if ( empty( $order_id ) ) { + return null; + } + //TODO remove carthash from session after success place order + $order_object = self::$woocommerce->getOrder( $order_id ); + $cart_token = self::$woocommerce->getOrderMeta( $order_object, $this->cart_token_key_for_db ); + $logger = wc_get_logger(); + $logger->add( 'Retainful', 'Purchase order id:' . $order_id ); + $logger->add( 'Retainful', 'Purchase order token:' . $cart_token ); + if ( empty( $cart_token ) ) { + $cart_token = $this->retrieveCartToken(); + $logger->add( 'Retainful', 'Purchase retrive order token:' . $cart_token ); + } + + self::$settings->logMessage( array( + "cart_token" => $cart_token, + "order_id" => $order_id + ), 'purchaseComplete' ); + if ( ! empty( $cart_token ) ) { + $cart_created_at = $this->userCartCreatedAt(); + $user_ip = $this->retrieveUserIp(); + $is_buyer_accepts_marketing = ( $this->isBuyerAcceptsMarketing() ) ? 1 : 0; + //$cart_hash = self::$storage->getValue('rnoc_current_cart_hash'); + $cart_hash = $this->generateCartHash(); + $recovered_at = self::$storage->getValue( 'rnoc_recovered_at' ); + $recovered_by = self::$storage->getValue( 'rnoc_recovered_by_retainful' ); + $recovered_cart_token = self::$storage->getValue( 'rnoc_recovered_cart_token' ); + $user_agent = $this->getUserAgent(); + $user_accept_language = $this->getUserAcceptLanguage(); + + //$order_object = self::$woocommerce->getOrder( $order_id ); + if ( is_object( $order_object ) && ! empty( $order_object ) ) { + $order_object->update_meta_data( $this->cart_token_key_for_db, $cart_token ); + $order_object->update_meta_data( $this->cart_hash_key_for_db, $cart_hash ); + $order_object->update_meta_data( $this->cart_tracking_started_key_for_db, $cart_created_at ); + $order_object->update_meta_data( $this->user_ip_key_for_db, $user_ip ); + $order_object->update_meta_data( $this->accepts_marketing_key_for_db, $is_buyer_accepts_marketing ); + $order_object->update_meta_data( '_rnoc_recovered_at', $recovered_at ); + $order_object->update_meta_data( '_rnoc_recovered_by', $recovered_by ); + $order_object->update_meta_data( '_rnoc_recovered_cart_token', $recovered_cart_token ); + $order_object->update_meta_data( '_rnoc_get_http_user_agent', $user_agent ); + $order_object->update_meta_data( '_rnoc_get_http_accept_language', $user_accept_language ); + $order_object->update_meta_data( $this->pending_recovery_key_for_db, true ); + $order_object->save(); + $logger->add( 'Retainful', 'Purchase update order token:' . $order_object->get_meta( $this->cart_token_key_for_db ) ); + self::$woocommerce->setSession( 'store_api_draft_order', 0 ); + /*if ( $user_id = get_current_user_id() ) { + delete_user_meta( $user_id, $this->cart_token_key_for_db ); + }*/ + } + //$this->markOrderAsPendingRecovery($order_id); + //$this->unsetOrderTempData(); + } + + return null; + } + + /** + * Order had some changes + * + * @param $order_id + * + * @return void|null + */ + function orderUpdated( $order_id ) { + self::$settings->logMessage( array( "order_id" => $order_id ), 'OrderUpdated ' ); + + if ( $this->needInstantOrderSync() ) { + $this->syncOrder( $order_id ); + } else { + $this->scheduleCartSync( $order_id ); + } + } + + + /** + * Order has been updated from the shop backend. + * + * @param $order_id + * + * @return void|null + */ + function OrderUpdatedShopBackend( $order_id ) { + self::$settings->logMessage( array( "order_id" => $order_id ), 'OrderUpdatedShopBackend ' ); + if ( is_admin() ) { + if ( $this->needInstantOrderSync() ) { + $this->syncOrder( $order_id ); + } else { + $this->scheduleCartSync( $order_id ); + } + } + } + + /** + * @return mixed|void + */ + function generateNocCouponForManualOrders() { + $is_enabled = self::$settings->isNextOrderCouponEnabled(); + $has_backorder_coupon = self::$settings->autoGenerateCouponsForOldOrders(); + $need_noc_coupon = ( $is_enabled && $has_backorder_coupon && is_admin() ); + + return apply_filters( 'rnoc_generate_noc_coupon_for_manual_orders', $need_noc_coupon, $this ); + } + + function changeWebHookHeader( $http_args, $order_id, $webhook_id ) { + + if ( $webhook_id <= 0 || ! class_exists( 'WC_Webhook' ) || ! self::$settings->isConnectionActive() ) { + return $http_args; + } + + try { + $webhook = new \WC_Webhook( $webhook_id ); + $topic = $webhook->get_topic(); + $topic_status = self::$settings->getWebHookStatus(); + if ( ! isset( $topic_status[ $topic ] ) || ! $topic_status[ $topic ] ) { + return $http_args; + } + $delivery_url = $webhook->get_delivery_url(); + $site_delivery_url = self::$settings->getDeliveryUrl(); + if ( $delivery_url != $site_delivery_url || $order_id <= 0 ) { + return $http_args; + } + $logger = wc_get_logger(); + $logger->add( 'Retainful', 'Webhook order id:' . $order_id ); + $order = self::$woocommerce->getOrder( $order_id ); + if ( self::$woocommerce->getStatus( $order ) == 'checkout-draft' ) { + return $http_args; + } + $order_obj = new Order(); + $cart_token = self::$woocommerce->getOrderMeta( $order, $this->cart_token_key_for_db ); + self::$settings->logMessage( array( "cart_token" => $cart_token ), 'Cart Token' ); + $logger = wc_get_logger(); + $logger->add( 'Retainful', 'order id:' . $order_id ); + $logger->add( 'Retainful', 'Token:' . $cart_token ); + if ( empty( $cart_token ) ) { + //Usually we should not force generate the cart token as this would sync all the old orders otherwise, if their status changes. + $force_generate_cart_token = apply_filters( 'rnoc_force_generate_cart_token', false, $http_args, $order_id, $webhook_id ); + $logger->add( 'Retainful', 'Force:' . $force_generate_cart_token ); + if ( $force_generate_cart_token === true ) { + //Let's generate a token and set to the order meta + $cart_token = $this->generateCartToken(); + self::$woocommerce->setOrderMeta( $order_id, $this->cart_token_key_for_db, $cart_token ); + self::$settings->logMessage( array( "cart_token" => $cart_token ), 'Force Generated Cart Token' ); + } + } + + if ( empty( $cart_token ) ) { + //bail on empty cart token + return $http_args; + } + + //self::$settings->logMessage(array("order_id" => $order_id), 'Triggering Webhook ID:'.$stored_webhook_id); + $order_data = $order_obj->getOrderData( $order ); + // $logger->add('Retainful','Order Data:'.json_encode($order_data)); + if ( ! empty( $cart_token ) && $order_data ) { + $client_ip = self::$woocommerce->getOrderMeta( $order, $this->user_ip_key_for_db ); + $token = self::$woocommerce->getOrderMeta( $order, $this->cart_token_key_for_db ); + $app_id = self::$settings->getApiKey(); + $extra_headers = array( + "X-Client-Referrer-IP" => ( ! empty( $client_ip ) ) ? $client_ip : null, + "X-Retainful-Version" => RNOC_VERSION, + "X-Cart-Token" => $token, + "Cart-Token" => $token, + "app-id" => $app_id, + "app_id" => $app_id, + "Content-Type" => 'application/json' + ); + foreach ( $extra_headers as $key => $value ) { + $http_args['headers'][ $key ] = $value; + } + //$logger->add('Retainful','App id:'.$app_id); + $cart_hash = $this->encryptData( $order_data ); + $body = array( + 'data' => $cart_hash + ); + $http_args['body'] = trim( wp_json_encode( $body ) ); + } + } + catch ( Exception $e ) { + + } + + return $http_args; + } + + /** + * Sync the order with API + * + * @param $order_id + * + * @return void|null + */ + function syncOrder( $order_id ) { + if ( empty( $order_id ) || self::$settings->isBackgroundOrderSyncEnabled() ) { + return; + } + $order = self::$woocommerce->getOrder( $order_id ); + if ( self::$woocommerce->getStatus( $order ) == 'checkout-draft' ) { + return; + } + $logger = wc_get_logger(); + $logger->add( 'Retainful', 'SyncOrder order id:' . $order_id ); + $order_obj = new Order(); + $cart_token = self::$woocommerce->getOrderMeta( $order, $this->cart_token_key_for_db ); + $cart_token = apply_filters( 'rnoc_sync_order_change_order_token', $cart_token, $order_id, $this ); + if ( empty( $cart_token ) ) { + return; + } + $order_status = self::$woocommerce->getStatus( $order ); + $order_cancelled_at = self::$woocommerce->getOrderMeta( $order, $this->order_cancelled_date_key_for_db ); + // handle order cancellation + if ( ! $order_cancelled_at && 'cancelled' === $order_status ) { + $order_cancelled_at = current_time( 'timestamp', true ); + self::$woocommerce->setOrderMeta( $order_id, $this->order_cancelled_date_key_for_db, $order_cancelled_at ); + $this->unsetOrderTempData(); + } + $order_data = $order_obj->getOrderData( $order ); + if ( empty( $order_data ) ) { + return; + } + $order_data['cancelled_at'] = ( ! empty( $order_cancelled_at ) ) ? $this->formatToIso8601( $order_cancelled_at ) : null; + self::$settings->logMessage( array( "order_id" => $order_id ), 'syncOrder' ); + $cart_hash = $this->encryptData( $order_data ); + $client_ip = self::$woocommerce->getOrderMeta( $order, $this->user_ip_key_for_db ); + if ( ! empty( $cart_hash ) ) { + $token = self::$woocommerce->getOrderMeta( $order, $this->cart_token_key_for_db ); + $extra_headers = array( + "X-Client-Referrer-IP" => ( ! empty( $client_ip ) ) ? $client_ip : null, + "X-Retainful-Version" => RNOC_VERSION, + "X-Cart-Token" => $token, + "Cart-Token" => $token + ); + + $logger->add( 'Retainful', 'SyncOrder token:' . $token ); + $this->syncCart( $cart_hash, $extra_headers ); + } + } + + /** + * Sync the order + * + * @param $order_id + */ + function syncOrderByScheduler( $order_id ) { + $this->syncOrder( $order_id ); + } + + /** + * schedule the sync of the cart + * + * @param $order_id + */ + function scheduleCartSync( $order_id ) { + if ( ! apply_filters( 'rnoc_schedule_cart_sync', true ) ) { + return; + } + $hook = 'retainful_sync_abandoned_cart_order'; + $meta_key = '_rnoc_order_id'; + if ( self::$settings->hasAnyActiveScheduleExists( $hook, $order_id, $meta_key ) == false ) { + self::$settings->scheduleEvents( $hook, current_time( 'timestamp' ) + 60, array( $meta_key => $order_id ) ); + } + } + + /** + * Clear any persistent cart session data for logged in customers + * + * @param int $order_id order ID + * @param string $old_status + * @param string $new_status + */ + public function orderStatusChanged( $order_id, $old_status, $new_status ) { + global $wp; + self::$settings->logMessage( array( "order_id" => $order_id ), 'orderStatusChanged' ); + try { + // PayPal IPN request + if ( isset( $wp->query_vars['wc-api'] ) && ! empty( $wp->query_vars['wc-api'] ) && ( 'WC_Gateway_Paypal' === $wp->query_vars['wc-api'] ) ) { + $order = self::$woocommerce->getOrder( $order_id ); + // PayPal order is completed or authorized: clear any user session + // data so that we don't have to rely on the thank-you page rendering + if ( ( self::$woocommerce->isOrderPaid( $order ) || $new_status == 'on-hold' ) && ( $user_id = self::$woocommerce->getOrderUserId( $order ) ) ) { + delete_user_meta( $user_id, '_woocommerce_persistent_cart_' . get_current_blog_id() ); + if ( $this->isPendingRecovery( $user_id ) ) { + self::$woocommerce->setOrderMeta( $order_id, $this->pending_recovery_key_for_db, true ); + } + if ( $this->retrieveCartToken( $user_id ) ) { + $this->removeTempDataForUser( $user_id ); + } + } + } + } + catch ( Exception $e ) { + } + } + + /** + * Check the order is placed or not + * + * @param $order + * @param $old_status + * @param $new_status + * + * @return bool|mixed|void + */ + function isPlaced( $order, $old_status, $new_status ) { + $placed = self::$woocommerce->isOrderPaid( $order ) || ( $new_status === 'on-hold' && ! $this->recoverHeldOrders() ); + $placed = apply_filters( 'rnoc_abandoned_cart_is_order_get_placed', $placed, $order, $old_status, $new_status, $this ); + + return $placed; + } + + /** + * HAndle order completion in order page + * + * @param $order_id + */ + function payPageOrderCompletion( $order_id ) { + $logger = wc_get_logger(); + $logger->add( 'Retainful', 'Cleared order temp data:' . $order_id ); + $this->unsetOrderTempData(); + } + + /** + * @param $order_id + */ + function checkoutOrderProcessed( $order_id ) { + self::$settings->logMessage( array( "order_id" => $order_id ), 'checkoutOrderProcessed' ); + /* if ($this->isPendingRecovery()) { + $this->markOrderAsPendingRecovery($order_id); + }*/ + try { + $order = self::$woocommerce->getOrder( $order_id ); + $cart_token = self::$woocommerce->getOrderMeta( $order, $this->cart_token_key_for_db ); + if ( empty( $cart_token ) ) { + $cart_token = $this->retrieveCartToken(); + } + if ( ! empty( $cart_token ) ) { + $order = self::$woocommerce->getOrder( $order_id ); + $this->purchaseComplete( $order_id ); + $this->syncOrderToAPI( $order, $order_id ); + //$this->unsetOrderTempData(); + + } + } + catch ( Exception $e ) { + } + } + + function apiCheckoutOrderProcessed( $order ) { + if ( ! is_object( $order ) ) { + return; + } + $order_id = self::$woocommerce->getOrderId( $order ); + self::$settings->logMessage( array( "order_id" => $order_id ), 'checkoutOrderProcessed' ); + /* if ($this->isPendingRecovery()) { + $this->markOrderAsPendingRecovery($order_id); + }*/ + try { + $order = self::$woocommerce->getOrder( $order_id ); + $cart_token = self::$woocommerce->getOrderMeta( $order, $this->cart_token_key_for_db ); + if ( empty( $cart_token ) ) { + $cart_token = $this->retrieveCartToken(); + } + if ( ! empty( $cart_token ) ) { + //$order = self::$woocommerce->getOrder($order_id); + $this->purchaseComplete( $order_id ); + $this->syncOrderToAPI( $order, $order_id ); + //$this->unsetOrderTempData(); + } + } + catch ( Exception $e ) { + } + } + + /** + * sync order to api + * + * @param $order + * @param $order_id + */ + function syncOrderToAPI( $order, $order_id ) { + if ( self::$settings->isBackgroundOrderSyncEnabled() || self::$woocommerce->getStatus( $order ) == 'checkout-draft' ) { + return; + } + $logger = wc_get_logger(); + $logger->add( 'Retainful', 'SyncOrderToApi order id:' . $order_id ); + if ( $this->needInstantOrderSync() ) { + $order_obj = new Order(); + $cart = $order_obj->getOrderData( $order ); + if ( ! empty( $cart ) ) { + $cart_hash = $this->encryptData( $cart ); + //Reduce the loading speed + $client_ip = self::$woocommerce->getOrderMeta( $order, $this->user_ip_key_for_db ); + $token = self::$woocommerce->getOrderMeta( $order, $this->cart_token_key_for_db ); + self::$settings->logMessage( array( + 'order_id' => $order_id, + 'token' => $token, + 'client_ip' => $client_ip + ), 'syncOrderToAPI' ); + if ( ! empty( $cart_hash ) ) { + + $extra_headers = array( + "X-Client-Referrer-IP" => ( ! empty( $client_ip ) ) ? $client_ip : null, + "X-Retainful-Version" => RNOC_VERSION, + "X-Cart-Token" => $token, + "Cart-Token" => $token + ); + + $logger->add( 'Retainful', 'SyncOrderToApi token:' . $token ); + $this->syncCart( $cart_hash, $extra_headers ); + } + } else { + self::$settings->logMessage( array( 'order_id' => $order_id ), 'Failed syncOrderToAPI' ); + } + } else { + $this->scheduleCartSync( $order_id ); + } + } + + /** + * need the instant sync or not + * @return mixed|void + */ + function needInstantOrderSync() { + return apply_filters( 'rnoc_sync_order_data_instantly_to_api', true ); + } + + /** + * Assign cart token for Order + * + * @param $cart_token + * @param $order_id + */ + function setOrderCartToken( $cart_token, $order_id ) { + if ( empty( $cart_token ) ) { + $cart_token = $this->generateCartToken(); + } + self::$woocommerce->setOrderMeta( $order_id, $this->cart_token_key_for_db, $cart_token ); + } + + /** + * Update order on successful payment + * + * @param $result + * @param $order_id + * + * @return mixed + */ + function maybeUpdateOrderOnSuccessfulPayment( $result, $order_id ) { + self::$settings->logMessage( array( "order_id" => $order_id ), 'maybeUpdateOrderOnSuccessfulPayment' ); + $order = self::$woocommerce->getOrder( $order_id ); + if ( ! $cart_token = self::$woocommerce->getOrderMeta( $order, $this->cart_token_key_for_db ) ) { + return $result; + } + $this->syncOrderToAPI( $order, $order_id ); + + //$this->unsetOrderTempData(); + return $result; + } + + /** + * Payment completed + * + * @param $order_id + */ + function paymentCompleted( $order_id ) { + self::$settings->logMessage( array( "order_id" => $order_id ), 'paymentCompleted' ); + if ( self::$woocommerce->getOrder( $order_id ) ) { + self::$woocommerce->setCustomerPayingForOrder( $order_id ); + } + $cart_token = $this->retrieveCartToken(); + if ( ! empty( $cart_token ) ) { + $logger = wc_get_logger(); + $logger->add( 'Retainful', 'Cleared order payment complete:' . $order_id ); + $this->unsetOrderTempData(); + } + } + + /** + * Unset the temporary cart token and order data + * + * @param null $user_id + */ + function unsetOrderTempData( $user_id = null ) { + self::$storage->removeValue( $this->cart_token_key ); + self::$storage->removeValue( $this->pending_recovery_key ); + self::$storage->removeValue( $this->cart_tracking_started_key ); + self::$storage->removeValue( $this->previous_cart_hash_key ); + //This was set in plugin since 2.0.4 + self::$storage->removeValue( 'rnoc_force_refresh_cart' ); + self::$storage->removeValue( 'rnoc_recovered_at' ); + self::$storage->removeValue( 'rnoc_current_cart_hash' ); + self::$storage->removeValue( 'rnoc_recovered_by_retainful' ); + self::$storage->removeValue( 'rnoc_recovered_cart_token' ); + if ( $user_id || ( $user_id = get_current_user_id() ) ) { + $logger = wc_get_logger(); + $logger->add( 'Retainful', 'Cleared user temp:' . $user_id ); + $this->removeTempDataForUser( $user_id ); + } + } + + /** + * Delete temp data of the user + * + * @param $user_id + */ + function removeTempDataForUser( $user_id ) { + delete_user_meta( $user_id, $this->cart_token_key_for_db ); + delete_user_meta( $user_id, $this->pending_recovery_key_for_db ); + delete_user_meta( $user_id, $this->cart_tracking_started_key_for_db ); + delete_user_meta( $user_id, $this->user_ip_key_for_db ); + } + + /** + * Mark order as pending recovery + * + * @param $order_id + */ + function markOrderAsPendingRecovery( $order_id ) { + self::$settings->logMessage( array( "order_id" => $order_id ), 'markOrderAsPendingRecovery' ); + /*$order = self::$woocommerce->getOrder($order_id); + if (!$order instanceof \WC_Order) { + return; + }*/ + self::$woocommerce->setOrderMeta( $order_id, $this->pending_recovery_key_for_db, true ); + } } \ No newline at end of file diff --git a/src/Api/AbandonedCart/RestApi.php b/src/Api/AbandonedCart/RestApi.php index c722eca0..6149490e 100644 --- a/src/Api/AbandonedCart/RestApi.php +++ b/src/Api/AbandonedCart/RestApi.php @@ -11,756 +11,807 @@ use Rnoc\Retainful\library\RetainfulApi; use Rnoc\Retainful\WcFunctions; -class RestApi -{ - public static $cart, $checkout, $settings, $api, $woocommerce, $storage; - protected $cart_token_key = "rnoc_user_cart_token", $cart_token_key_for_db = "_rnoc_user_cart_token"; - protected $user_ip_key = "rnoc_user_ip_address", $user_ip_key_for_db = "_rnoc_user_ip_address"; - protected $order_placed_date_key_for_db = "_rnoc_order_placed_at", $order_cancelled_date_key_for_db = "_rnoc_order_cancelled_at"; - protected $pending_recovery_key = "rnoc_is_pending_recovery", $pending_recovery_key_for_db = "_rnoc_is_pending_recovery"; - protected $cart_tracking_started_key = "rnoc_cart_created_at", $cart_tracking_started_key_for_db = "_rnoc_cart_tracking_started_at"; - protected $order_note_key = "rnoc_order_note", $order_note_key_for_db = "_rnoc_order_note"; - protected $order_recovered_key = "rnoc_order_recovered", $order_recovered_key_for_db = "_rnoc_order_recovered"; - protected $accepts_marketing_key_for_db = "_rnoc_is_buyer_accepts_marketing"; - protected $previous_cart_hash_key = "rnoc_previous_cart_hash"; - protected $cart_hash_key_for_db = "_rnoc_cart_hash"; - /** The cipher method name to use to encrypt the cart data */ - const CIPHER_METHOD = 'AES256'; - /** The HMAC hash algorithm to use to sign the encrypted cart data */ - const HMAC_ALGORITHM = 'sha256'; - - function __construct() - { - self::$settings = !empty(self::$settings) ? self::$settings : new Settings(); - self::$api = !empty(self::$api) ? self::$api : new RetainfulApi(); - self::$woocommerce = !empty(self::$woocommerce) ? self::$woocommerce : new WcFunctions(); - $this->initStorage(); - } - - /** - * init the storage classes - */ - function initStorage() - { - $storage_handler = self::$settings->getStorageHandler(); - switch ($storage_handler) { - case "php"; - self::$storage = new PhpSession(); - break; - case "cookie"; - self::$storage = new Cookie(); - break; - default: - case "woocommerce": - self::$storage = new WooSession(); - break; - } - } - - /** - * Get the current user's cart token - * @return array|string|null - */ - function getCartToken() - { - $cart_token = $this->retrieveCartToken(); - if (empty($cart_token)) { - $cart_token = $this->generateCartToken(); - $this->setCartToken($cart_token); - } - return apply_filters('rnoc_get_cart_token', $cart_token, $this); - } - - /** - * Set the cart token for the session - * @param $cart_token - * @param $user_id - */ - function setCartToken($cart_token, $user_id = null) - { - $cart_token = apply_filters('rnoc_before_set_cart_token', $cart_token, $user_id, $this); - $old_cart_token = self::$storage->getValue($this->cart_token_key); - if (empty($old_cart_token)) { - self::$settings->logMessage($cart_token, 'setting cart token'); - $current_time = current_time('timestamp', true); - self::$storage->setValue($this->cart_token_key, $cart_token); - self::$storage->setValue($this->cart_tracking_started_key, $current_time); - if (!empty($user_id) || $user_id = get_current_user_id()) { - update_user_meta($user_id, $this->cart_token_key_for_db, $cart_token); - $this->setCartCreatedDate($user_id, $current_time); - } - } - } - - /** - * @param $price - * @return string - */ - function formatDecimalPrice($price) - { - $decimals = self::$woocommerce->priceDecimals(); - $price = floatval($price); - return round($price, $decimals); - } - - /** - * @param $price - * @return string - */ - function formatDecimalPriceRemoveTrailingZeros($price) - { - $price = (float)$price; - $decimals = self::$woocommerce->priceDecimals(); - $rounded_price = round($price, $decimals); - return number_format($rounded_price, $decimals, '.', ''); - } - - - /** - * Line item total - * @param $item_details - * @return int - */ - function getLineItemTotal($item_details) - { - $line_total = (isset($item_details['line_total']) && !empty($item_details['line_total'])) ? $item_details['line_total'] : 0; - if (!self::$woocommerce->isPriceExcludingTax()) { - $line_total_tax = (isset($item_details['line_tax']) && !empty($item_details['line_tax'])) ? $item_details['line_tax'] : 0; - } else { - $line_total_tax = 0; - } - $total = $line_total + $line_total_tax; - return apply_filters('retainful_get_line_item_total', $total, $line_total, $line_total_tax, $item_details, $this); - } - - /** - * Set the session shipping details - * @param $shipping_address - */ - function setSessionShippingDetails($shipping_address) - { - if (!empty($shipping_address)) { - foreach ($shipping_address as $key => $value) { - $method = 'set_' . $key; - if (is_callable(array(WC()->customer, $method))) { - WC()->customer->$method($value); - } - } - } - } - - /** - * Remove the session shipping details - */ - function removeSessionShippingDetails() - { - self::$storage->removeValue('rnoc_shipping_address'); - } - - /** - * generate cart hash - * @return string - */ - function generateCartHash() - { - $cart = self::$woocommerce->getCart(); - $cart_session = array(); - if (!empty($cart)) { - foreach ($cart as $key => $values) { - $cart_session[$key] = $values; - unset($cart_session[$key]['data']); // Unset product object. - } - } - return $cart_session ? md5(wp_json_encode($cart_session) . self::$woocommerce->getCartTotalForEdit()) : ''; - } - - /** - * Set the customer billing details - * @param $billing_address - */ - function setCustomerBillingDetails($billing_address) - { - if (!empty($billing_address)) { - foreach ($billing_address as $key => $value) { - $method = 'set_' . $key; - if (is_callable(array(WC()->customer, $method))) { - WC()->customer->$method($value); - } - } - } - } - - /** - * Customer address mapping fields - * @return array - */ - function getAddressMapFields() - { - $fields = array( - 'first_name', - 'last_name', - 'state', - 'phone', - 'postcode', - 'city', - 'country', - 'address_1', - 'address_2', - 'company' - ); - return apply_filters('rnoc_get_checkout_mapping_fields', $fields); - } - - /** - * Get the customer billing details - * @param $type - * @return array - */ - function getCustomerCheckoutDetails($type = "billing") - { - $fields = $this->getAddressMapFields(); - $checkout_field_values = array(); - if (!empty($fields)) { - foreach ($fields as $key) { - $method = 'get_' . $type . '_' . $key; - if (is_callable(array(WC()->customer, $method))) { - $checkout_field_values[$type . '_' . $key] = WC()->customer->$method(); - } - } - } - return $checkout_field_values; - } - - /** - * Remove the session billing details - */ - function removeSessionBillingDetails() - { - self::$storage->removeValue('rnoc_billing_address'); - } - - /** - * Check the cart is in pending recovery - * @param null $user_id - * @return array|mixed|string|null - */ - function isPendingRecovery($user_id = NULL) - { - if ($user_id || ($user_id = get_current_user_id())) { - return (bool) get_user_meta($user_id, $this->pending_recovery_key_for_db, true); - } else { - return (bool) self::$storage->getValue($this->pending_recovery_key); - } - } - - /** - * retrieve cart token from session - * @param $user_id - * @return array|mixed|string|null - */ - function retrieveCartToken($user_id = null) - { - if ($user_id == null) { - $user_id = get_current_user_id(); - } - if (!empty($user_id)) { - $token = get_user_meta($user_id, $this->cart_token_key_for_db, true); - } else { - $token = self::$storage->getValue($this->cart_token_key); - } - return apply_filters('rnoc_retrieve_cart_token', $token, $user_id, $this); - } - - /** - * Recovery link to recover the user cart - * - * @param $cart_token - * @return string - */ - function getRecoveryLink($cart_token) - { - $data = array('cart_token' => $cart_token); - // encode - $data = base64_encode(wp_json_encode($data)); - // add hash for easier verification that the checkout URL hasn't been tampered with - $hash = $this->hashTheData($data); - $url = self::getRetainfulApiUrl(); - // returns URL like: - // pretty permalinks enabled - https://example.com/wc-api/retainful?token=abc123&hash=xyz - // pretty permalinks disabled - https://example.com?wc-api=retainful&token=abc123&hash=xyz - return esc_url_raw(add_query_arg(array('token' => rawurlencode($data), 'hash' => $hash), $url)); - } - - /** - * Return the WC API URL for handling Retainful recovery links by accounting - * for whether pretty permalinks are enabled or not. - * - * @return string - * @since 1.1.0 - */ - private static function getRetainfulApiUrl() - { - $scheme = wc_site_is_https() ? 'https' : 'http'; - return get_option('permalink_structure') - ? get_home_url(null, 'wc-api/retainful', $scheme) - : add_query_arg('wc-api', 'retainful', get_home_url(null, null, $scheme)); - } - - /** - * Hash the data - * @param $data - * @return false|string - */ - function hashTheData($data) - { - $secret = self::$settings->getSecretKey(); - return hash_hmac(self::HMAC_ALGORITHM, $data, $secret); - } - - /** - * Get the client IP address - * @return mixed|string - */ - function getClientIp() - { - if (isset($_SERVER['HTTP_X_REAL_IP'])) { - $client_ip = $_SERVER['HTTP_X_REAL_IP']; - } elseif (isset($_SERVER['HTTP_CLIENT_IP'])) { - $client_ip = $_SERVER['HTTP_CLIENT_IP']; - } elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { - $client_ip = $_SERVER['HTTP_X_FORWARDED_FOR']; - } elseif (isset($_SERVER['HTTP_X_FORWARDED'])) { - $client_ip = $_SERVER['HTTP_X_FORWARDED']; - } elseif (isset($_SERVER['HTTP_FORWARDED_FOR'])) { - $client_ip = $_SERVER['HTTP_FORWARDED_FOR']; - } elseif (isset($_SERVER['HTTP_FORWARDED'])) { - $client_ip = $_SERVER['HTTP_FORWARDED']; - } elseif (isset($_SERVER['REMOTE_ADDR'])) { - $client_ip = $_SERVER['REMOTE_ADDR']; - } else { - $client_ip = ''; - } - return $client_ip; - } - - /** - * retrieve User IP address - * @param null $user_id - * @return array|mixed|string|null - */ - function retrieveUserIp($user_id = NULL) - { - if ($user_id) { - $ip = get_user_meta($user_id, $this->user_ip_key_for_db); - } else { - $ip = $this->getClientIp(); - } - return $this->formatUserIP($ip); - } - - /** - * Sometimes the IP address returne is not formatted quite well. - * So it requires a basic formating. - * @param $ip - * @return String - */ - function formatUserIP($ip) - { - //check for commas in the IP - $ip = trim(current(preg_split('/,/', sanitize_text_field(wp_unslash($ip))))); - return (string)$ip; - } - - /** - * generate the random cart token - * @return string - */ - function generateCartToken() - { - try { - $data = random_bytes(16); - $data[6] = chr(ord($data[6]) & 0x0f | 0x40); // set version to 0100 - $data[8] = chr(ord($data[8]) & 0x3f | 0x80); // set bits 6-7 to 10 - $token = vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4)); - } catch (Exception $e) { - // fall back to mt_rand if random_bytes is unavailable - $token = sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x', - // 32 bits for "time_low" - mt_rand(0, 0xffff), mt_rand(0, 0xffff), - // 16 bits for "time_mid" - mt_rand(0, 0xffff), - // 16 bits for "time_hi_and_version", - // four most significant bits holds version number 4 - mt_rand(0, 0x0fff) | 0x4000, - // 16 bits, 8 bits for "clk_seq_hi_res", - // 8 bits for "clk_seq_low", - // two most significant bits holds zero and one for variant DCE1.1 - mt_rand(0, 0x3fff) | 0x8000, - // 48 bits for "node" - mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff) - ); - } - return md5($token.time()); - } - - /** - * Check that the order status is valid to clear temp data - * @param $order_status - * @return bool - */ - function isValidOrderStatusToResetCartToken($order_status) - { - $to_clear_order_status = apply_filters('rnoc_to_clear_temp_data_order_status', array('failed', 'pending')); - return in_array($order_status, $to_clear_order_status); - } - - /** - * Check the order has valid order statuses - * @param $order_status - * @return bool - */ - function isOrderHasValidOrderStatus($order_status) - { - $invalid_order_status = apply_filters('rnoc_abandoned_cart_invalid_order_statuses', array('pending', 'failed', 'checkout-draft', 'trash', 'cancelled', 'refunded')); - $consider_on_hold_order_as_ac = $this->considerOnHoldAsAbandoned(); - if ($consider_on_hold_order_as_ac == 1) { - $invalid_order_status[] = 'on-hold'; - } - - $invalid_order_status = array_unique($invalid_order_status); - return (!in_array($order_status, $invalid_order_status)); - } - - /** - * Checks whether an order is pending recovery. - * - * @param int|string $order_id order ID - * @return bool - * @since 2.1.0 - * - */ - public function isOrderInPendingRecovery($order_id) - { - $order = self::$woocommerce->getOrder($order_id); - if (!$order instanceof \WC_Order) { - return false; - } - return (bool) self::$woocommerce->getOrderMeta($order, $this->pending_recovery_key_for_db); - } - - /** - * Checks whether an order is recovered. - * @param int|string $order_id order ID - * @return bool - */ - public function isOrderRecovered($order_id) - { - $order = wc_get_order($order_id); - if (!$order instanceof \WC_Order) { - return false; - } - return (bool) self::$woocommerce->getOrderMeta($order, $this->order_recovered_key_for_db); - } - - /** - * Mark order as recovered - * @param $order_id - */ - function markOrderAsRecovered($order_id) - { - $order = self::$woocommerce->getOrder($order_id); - if (!$order instanceof \WC_Order || $this->isOrderRecovered($order_id)) { - return; - } - if (self::$woocommerce->getOrderMeta($order, '_rnoc_recovered_by', 0) == 1) { - self::$woocommerce->deleteOrderMeta($order_id, $this->pending_recovery_key_for_db); - self::$woocommerce->setOrderMeta($order_id, $this->order_recovered_key_for_db, true); - self::$woocommerce->setOrderNote($order, __('Order recovered by Retainful.', RNOC_TEXT_DOMAIN)); - do_action('rnoc_abandoned_order_recovered', $order); - } - } - - function changeOrderStatus($order_status) - { - $changable_order_status = array('checkout-draft'); - if ($this->considerCancelledAsAbandoned() == 1) { - $changable_order_status[] = "cancelled"; - } - if ($this->considerOnHoldAsAbandoned() == 1) { - $changable_order_status[] = "on-hold"; - } - if ($this->considerFailedAsAbandoned() == 1) { - $changable_order_status[] = "failed"; - } - if (in_array($order_status, $changable_order_status)) { - $order_status = "pending"; - } - return $order_status; - } - - /** - * Consider on hold payment as abandoned - * @return int - */ - function considerOnHoldAsAbandoned() - { - $settings = self::$settings->getAdminSettings(); - return isset($settings[RNOC_PLUGIN_PREFIX . 'consider_on_hold_as_abandoned_status']) ? $settings[RNOC_PLUGIN_PREFIX . 'consider_on_hold_as_abandoned_status'] : 0; - } - - /** - * Consider cancelled order as abandoned - * @return int - */ - function considerCancelledAsAbandoned() - { - $settings = self::$settings->getAdminSettings(); - return isset($settings[RNOC_PLUGIN_PREFIX . 'consider_cancelled_as_abandoned_status']) ? $settings[RNOC_PLUGIN_PREFIX . 'consider_cancelled_as_abandoned_status'] : 1; - } - - /** - * Consider failed order as abandoned - * @return int - */ - function considerFailedAsAbandoned() - { - $settings = self::$settings->getAdminSettings(); - return isset($settings[RNOC_PLUGIN_PREFIX . 'consider_failed_as_abandoned_status']) ? $settings[RNOC_PLUGIN_PREFIX . 'consider_failed_as_abandoned_status'] : 0; - } - - /** - * refresh fragments on page load - * @return int - */ - function refreshFragmentsOnPageLoad() - { - $settings = self::$settings->getAdminSettings(); - return isset($settings[RNOC_PLUGIN_PREFIX . 'refresh_fragments_on_page_load']) ? $settings[RNOC_PLUGIN_PREFIX . 'refresh_fragments_on_page_load'] : 0; - } - - /** - * Format the date to ISO8601 - * @param $timestamp - * @return string|null - */ - function formatToIso8601($timestamp) - { - if (empty($timestamp)) { - $timestamp = current_time('timestamp', true); - } - if(is_object($timestamp) && $timestamp instanceof \WC_DateTime) { - $timestamp = $timestamp->getTimestamp(); - } - - try { - $date = date('Y-m-d H:i:s', $timestamp); - $date_time = new DateTime($date); - return $date_time->format(DateTime::ATOM); - } catch (Exception $e) { - return NULL; - } - } - - /** - * Convert price to another price as per currency rate - * @param $price - * @param $rate - * @return float|int - */ - function convertToCurrency($price, $rate) - { - if (!empty($price) && !empty($rate)) { - return $price / $rate; - } - return $price; - } - - /** - * Encrypt the cart - * @param $data - * @param $secret - * @return string - */ - function encryptData($data, $secret = NULL) - { - if (extension_loaded('openssl')) { - if (is_array($data) || is_object($data)) { - $data = wp_json_encode($data); - } - try { - if (empty($secret)) { - $secret = self::$settings->getSecretKey(); - } - $iv_len = openssl_cipher_iv_length(self::CIPHER_METHOD); - $iv = openssl_random_pseudo_bytes($iv_len); - $cipher_text_raw = openssl_encrypt($data, self::CIPHER_METHOD, $secret, OPENSSL_RAW_DATA, $iv); - $hmac = hash_hmac(self::HMAC_ALGORITHM, $cipher_text_raw, $secret, true); - return base64_encode(bin2hex($iv) . ':retainful:' . bin2hex($hmac) . ':retainful:' . bin2hex($cipher_text_raw)); - } catch (Exception $e) { - return NULL; - } - } - return NULL; - } - - /** - * Decrypt the user cart - * @param $data_hash - * @return string - */ - function decryptData($data_hash) - { - $secret = self::$settings->getSecretKey(); - $string = base64_decode($data_hash); - list($iv, $hmac, $cipher_text_raw) = explode(':retainful:', $string); - $reverse_hmac = hash_hmac(self::HMAC_ALGORITHM, $cipher_text_raw, $secret, true); - if (hash_equals($reverse_hmac, $hmac)) { - return openssl_decrypt($cipher_text_raw, self::CIPHER_METHOD, $secret, OPENSSL_RAW_DATA, $iv); - } - return NULL; - } - - /** - * get the active currency code - * @return String|null - */ - function getCurrentCurrencyCode() - { - $default_currency = self::$settings->getBaseCurrency(); - return apply_filters('rnoc_get_current_currency_code', $default_currency); - } - - /** - * Get the date of cart tracing started - * @param $user_id - * @return array|mixed|string|null - */ - function userCartCreatedAt($user_id = NULL) - { - if ($user_id || $user_id = get_current_user_id()) { - $cart_created_at = get_user_meta($user_id, $this->cart_tracking_started_key_for_db, true); - } else { - $cart_created_at = self::$storage->getValue($this->cart_tracking_started_key); - } - return $cart_created_at; - } - - /** - * When user start adding to cart - * @param null $user_id - * @param null $time - * @return array|mixed|string|null - */ - function setCartCreatedDate($user_id = NULL, $time = NULL) - { - if (empty($time)) { - $time = current_time('timestamp', true); - } - if (!empty($user_id) || $user_id = get_current_user_id()) { - update_user_meta($user_id, $this->cart_tracking_started_key_for_db, $time); - } - return $time; - } - - /** - * Synchronize cart with SaaS - * @param $cart_details - * @param $extra_headers - * @return array|bool|mixed|object|string - */ - function syncCart($cart_details, $extra_headers) - { - $app_id = self::$settings->getApiKey(); - $response = false; - if (!empty($cart_details)) { - self::$settings->logMessage('PHP', 'synced by'); - $response = self::$api->syncCartDetails($app_id, $cart_details, $extra_headers); - } - return $response; - } - - /** - * Check is buyer accepts marketing - * @return bool - */ - function isBuyerAcceptsMarketing() - { - $settings = self::$settings->getAdminSettings(); - $enable_gdpr_compliance = (isset($settings[RNOC_PLUGIN_PREFIX . 'enable_gdpr_compliance'])) ? $settings[RNOC_PLUGIN_PREFIX . 'enable_gdpr_compliance'] : 0; - if($enable_gdpr_compliance){ - return in_array(self::$woocommerce->getSession('is_buyer_accepting_marketing'), array(1,'true')); - } - return true; - /*if (is_user_logged_in()) { - return true; - } else { - $is_buyer_accepts_marketing = self::$woocommerce->getSession('is_buyer_accepting_marketing'); - if ($is_buyer_accepts_marketing == 1) { - return true; - } - } - return false;*/ - } - - /** - * need to track carts or not - * @param string $ip_address - * @param $order null | \WC_Order | \WC_Cart - * @return bool - */ - function canTrackAbandonedCarts($ip_address = NULL, $order = null) - { - if (apply_filters('rnoc_is_cart_has_valid_ip', true, $ip_address) && apply_filters('rnoc_can_track_abandoned_carts', true, $order)) { - return true; - } - return false; - } - - /** - * get the client details - * @param null $order - * @return mixed|void - */ - function getClientDetails($order = null) - { - $client_details = array( - 'accept_language' => $this->getUserAcceptLanguage($order) - ); - return apply_filters('rnoc_get_client_details', $client_details, $order); - } - - /** - * get the user agent of client - * @param null $order - * @return mixed|string|null - */ - function getUserAgent($order = null) - { - if (!empty($order)) { - return self::$woocommerce->getOrderMeta($order, '_rnoc_get_http_user_agent'); - } else { - if (isset($_SERVER['HTTP_USER_AGENT']) && !empty($_SERVER['HTTP_USER_AGENT'])) { - return $_SERVER['HTTP_USER_AGENT']; - } - } - return ''; - } - - /** - * get the user accept language - * @param null $order - * @return mixed|string|null - */ - function getUserAcceptLanguage($order = null) - { - if (!empty($order)) { - return self::$woocommerce->getOrderMeta($order, '_rnoc_get_http_accept_language'); - } else { - if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) && !empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { - $lang = trim($_SERVER['HTTP_ACCEPT_LANGUAGE']); - return substr($lang, 0, 2); - } - } - return ''; - } +class RestApi { + public static $cart, $checkout, $settings, $api, $woocommerce, $storage; + protected $cart_token_key = "rnoc_user_cart_token", $cart_token_key_for_db = "_rnoc_user_cart_token"; + protected $user_ip_key = "rnoc_user_ip_address", $user_ip_key_for_db = "_rnoc_user_ip_address"; + protected $order_placed_date_key_for_db = "_rnoc_order_placed_at", $order_cancelled_date_key_for_db = "_rnoc_order_cancelled_at"; + protected $pending_recovery_key = "rnoc_is_pending_recovery", $pending_recovery_key_for_db = "_rnoc_is_pending_recovery"; + protected $cart_tracking_started_key = "rnoc_cart_created_at", $cart_tracking_started_key_for_db = "_rnoc_cart_tracking_started_at"; + protected $order_note_key = "rnoc_order_note", $order_note_key_for_db = "_rnoc_order_note"; + protected $order_recovered_key = "rnoc_order_recovered", $order_recovered_key_for_db = "_rnoc_order_recovered"; + protected $accepts_marketing_key_for_db = "_rnoc_is_buyer_accepts_marketing"; + protected $previous_cart_hash_key = "rnoc_previous_cart_hash"; + protected $cart_hash_key_for_db = "_rnoc_cart_hash"; + /** The cipher method name to use to encrypt the cart data */ + const CIPHER_METHOD = 'AES256'; + /** The HMAC hash algorithm to use to sign the encrypted cart data */ + const HMAC_ALGORITHM = 'sha256'; + + function __construct() { + self::$settings = ! empty( self::$settings ) ? self::$settings : new Settings(); + self::$api = ! empty( self::$api ) ? self::$api : new RetainfulApi(); + self::$woocommerce = ! empty( self::$woocommerce ) ? self::$woocommerce : new WcFunctions(); + $this->initStorage(); + } + + /** + * init the storage classes + */ + function initStorage() { + $storage_handler = self::$settings->getStorageHandler(); + switch ( $storage_handler ) { + case "php"; + self::$storage = new PhpSession(); + break; + case "cookie"; + self::$storage = new Cookie(); + break; + default: + case "woocommerce": + self::$storage = new WooSession(); + break; + } + } + + /** + * Get the current user's cart token + * @return array|string|null + */ + function getCartToken() { + $cart_token = $this->retrieveCartToken(); + if ( empty( $cart_token ) ) { + $cart_token = $this->generateCartToken(); + $this->setCartToken( $cart_token ); + $logger = wc_get_logger(); + $logger->add( 'Retainful', 'Cart token gen:' . $cart_token ); + } + + return apply_filters( 'rnoc_get_cart_token', $cart_token, $this ); + } + + /** + * Set the cart token for the session + * + * @param $cart_token + * @param $user_id + */ + function setCartToken( $cart_token, $user_id = null ) { + $cart_token = apply_filters( 'rnoc_before_set_cart_token', $cart_token, $user_id, $this ); + $old_cart_token = self::$storage->getValue( $this->cart_token_key ); + if ( empty( $old_cart_token ) ) { + self::$settings->logMessage( $cart_token, 'setting cart token' ); + $current_time = current_time( 'timestamp', true ); + self::$storage->setValue( $this->cart_token_key, $cart_token ); + self::$storage->setValue( $this->cart_tracking_started_key, $current_time ); + if ( ! empty( $user_id ) || $user_id = get_current_user_id() ) { + update_user_meta( $user_id, $this->cart_token_key_for_db, $cart_token ); + $this->setCartCreatedDate( $user_id, $current_time ); + } + } + } + + /** + * @param $price + * + * @return string + */ + function formatDecimalPrice( $price ) { + $decimals = self::$woocommerce->priceDecimals(); + $price = floatval( $price ); + + return round( $price, $decimals ); + } + + /** + * @param $price + * + * @return string + */ + function formatDecimalPriceRemoveTrailingZeros( $price ) { + $price = (float) $price; + $decimals = self::$woocommerce->priceDecimals(); + $rounded_price = round( $price, $decimals ); + + return number_format( $rounded_price, $decimals, '.', '' ); + } + + + /** + * Line item total + * + * @param $item_details + * + * @return int + */ + function getLineItemTotal( $item_details ) { + $line_total = ( isset( $item_details['line_total'] ) && ! empty( $item_details['line_total'] ) ) ? $item_details['line_total'] : 0; + if ( ! self::$woocommerce->isPriceExcludingTax() ) { + $line_total_tax = ( isset( $item_details['line_tax'] ) && ! empty( $item_details['line_tax'] ) ) ? $item_details['line_tax'] : 0; + } else { + $line_total_tax = 0; + } + $total = $line_total + $line_total_tax; + + return apply_filters( 'retainful_get_line_item_total', $total, $line_total, $line_total_tax, $item_details, $this ); + } + + /** + * Set the session shipping details + * + * @param $shipping_address + */ + function setSessionShippingDetails( $shipping_address ) { + if ( ! empty( $shipping_address ) ) { + foreach ( $shipping_address as $key => $value ) { + $method = 'set_' . $key; + if ( is_callable( array( WC()->customer, $method ) ) ) { + WC()->customer->$method( $value ); + } + } + } + } + + /** + * Remove the session shipping details + */ + function removeSessionShippingDetails() { + self::$storage->removeValue( 'rnoc_shipping_address' ); + } + + /** + * generate cart hash + * @return string + */ + function generateCartHash() { + $cart = self::$woocommerce->getCart(); + $cart_session = array(); + if ( ! empty( $cart ) ) { + foreach ( $cart as $key => $values ) { + $cart_session[ $key ] = $values; + unset( $cart_session[ $key ]['data'] ); // Unset product object. + } + } + + return $cart_session ? md5( wp_json_encode( $cart_session ) . self::$woocommerce->getCartTotalForEdit() ) : ''; + } + + /** + * Set the customer billing details + * + * @param $billing_address + */ + function setCustomerBillingDetails( $billing_address ) { + if ( ! empty( $billing_address ) ) { + foreach ( $billing_address as $key => $value ) { + $method = 'set_' . $key; + if ( is_callable( array( WC()->customer, $method ) ) ) { + WC()->customer->$method( $value ); + } + } + } + } + + /** + * Customer address mapping fields + * @return array + */ + function getAddressMapFields() { + $fields = array( + 'first_name', + 'last_name', + 'state', + 'phone', + 'postcode', + 'city', + 'country', + 'address_1', + 'address_2', + 'company' + ); + + return apply_filters( 'rnoc_get_checkout_mapping_fields', $fields ); + } + + /** + * Get the customer billing details + * + * @param $type + * + * @return array + */ + function getCustomerCheckoutDetails( $type = "billing" ) { + $fields = $this->getAddressMapFields(); + $checkout_field_values = array(); + if ( ! empty( $fields ) ) { + foreach ( $fields as $key ) { + $method = 'get_' . $type . '_' . $key; + if ( is_callable( array( WC()->customer, $method ) ) ) { + $checkout_field_values[ $type . '_' . $key ] = WC()->customer->$method(); + } + } + } + + return $checkout_field_values; + } + + /** + * Remove the session billing details + */ + function removeSessionBillingDetails() { + self::$storage->removeValue( 'rnoc_billing_address' ); + } + + /** + * Check the cart is in pending recovery + * + * @param null $user_id + * + * @return array|mixed|string|null + */ + function isPendingRecovery( $user_id = null ) { + if ( $user_id || ( $user_id = get_current_user_id() ) ) { + return (bool) get_user_meta( $user_id, $this->pending_recovery_key_for_db, true ); + } else { + return (bool) self::$storage->getValue( $this->pending_recovery_key ); + } + } + + /** + * retrieve cart token from session + * + * @param $user_id + * + * @return array|mixed|string|null + */ + function retrieveCartToken( $user_id = null ) { + if ( $user_id == null ) { + $user_id = get_current_user_id(); + } + if ( ! empty( $user_id ) ) { + $token = get_user_meta( $user_id, $this->cart_token_key_for_db, true ); + } else { + $token = self::$storage->getValue( $this->cart_token_key ); + } + + return apply_filters( 'rnoc_retrieve_cart_token', $token, $user_id, $this ); + } + + /** + * Recovery link to recover the user cart + * + * @param $cart_token + * + * @return string + */ + function getRecoveryLink( $cart_token ) { + $data = array( 'cart_token' => $cart_token ); + // encode + $data = base64_encode( wp_json_encode( $data ) ); + // add hash for easier verification that the checkout URL hasn't been tampered with + $hash = $this->hashTheData( $data ); + $url = self::getRetainfulApiUrl(); + // returns URL like: + // pretty permalinks enabled - https://example.com/wc-api/retainful?token=abc123&hash=xyz + // pretty permalinks disabled - https://example.com?wc-api=retainful&token=abc123&hash=xyz + return esc_url_raw( add_query_arg( array( 'token' => rawurlencode( $data ), 'hash' => $hash ), $url ) ); + } + + /** + * Return the WC API URL for handling Retainful recovery links by accounting + * for whether pretty permalinks are enabled or not. + * @return string + * @since 1.1.0 + */ + private static function getRetainfulApiUrl() { + $scheme = wc_site_is_https() ? 'https' : 'http'; + + return get_option( 'permalink_structure' ) + ? get_home_url( null, 'wc-api/retainful', $scheme ) + : add_query_arg( 'wc-api', 'retainful', get_home_url( null, null, $scheme ) ); + } + + /** + * Hash the data + * + * @param $data + * + * @return false|string + */ + function hashTheData( $data ) { + $secret = self::$settings->getSecretKey(); + + return hash_hmac( self::HMAC_ALGORITHM, $data, $secret ); + } + + /** + * Get the client IP address + * @return mixed|string + */ + function getClientIp() { + if ( isset( $_SERVER['HTTP_X_REAL_IP'] ) ) { + $client_ip = $_SERVER['HTTP_X_REAL_IP']; + } elseif ( isset( $_SERVER['HTTP_CLIENT_IP'] ) ) { + $client_ip = $_SERVER['HTTP_CLIENT_IP']; + } elseif ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) { + $client_ip = $_SERVER['HTTP_X_FORWARDED_FOR']; + } elseif ( isset( $_SERVER['HTTP_X_FORWARDED'] ) ) { + $client_ip = $_SERVER['HTTP_X_FORWARDED']; + } elseif ( isset( $_SERVER['HTTP_FORWARDED_FOR'] ) ) { + $client_ip = $_SERVER['HTTP_FORWARDED_FOR']; + } elseif ( isset( $_SERVER['HTTP_FORWARDED'] ) ) { + $client_ip = $_SERVER['HTTP_FORWARDED']; + } elseif ( isset( $_SERVER['REMOTE_ADDR'] ) ) { + $client_ip = $_SERVER['REMOTE_ADDR']; + } else { + $client_ip = ''; + } + + return $client_ip; + } + + /** + * retrieve User IP address + * + * @param null $user_id + * + * @return array|mixed|string|null + */ + function retrieveUserIp( $user_id = null ) { + if ( $user_id ) { + $ip = get_user_meta( $user_id, $this->user_ip_key_for_db ); + } else { + $ip = $this->getClientIp(); + } + + return $this->formatUserIP( $ip ); + } + + /** + * Sometimes the IP address returne is not formatted quite well. + * So it requires a basic formating. + * + * @param $ip + * + * @return String + */ + function formatUserIP( $ip ) { + //check for commas in the IP + $ip = trim( current( preg_split( '/,/', sanitize_text_field( wp_unslash( $ip ) ) ) ) ); + + return (string) $ip; + } + + /** + * generate the random cart token + * @return string + */ + function generateCartToken() { + try { + $data = random_bytes( 16 ); + $data[6] = chr( ord( $data[6] ) & 0x0f | 0x40 ); // set version to 0100 + $data[8] = chr( ord( $data[8] ) & 0x3f | 0x80 ); // set bits 6-7 to 10 + $token = vsprintf( '%s%s-%s-%s-%s-%s%s%s', str_split( bin2hex( $data ), 4 ) ); + } + catch ( Exception $e ) { + // fall back to mt_rand if random_bytes is unavailable + $token = sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x', + // 32 bits for "time_low" + mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), + // 16 bits for "time_mid" + mt_rand( 0, 0xffff ), + // 16 bits for "time_hi_and_version", + // four most significant bits holds version number 4 + mt_rand( 0, 0x0fff ) | 0x4000, + // 16 bits, 8 bits for "clk_seq_hi_res", + // 8 bits for "clk_seq_low", + // two most significant bits holds zero and one for variant DCE1.1 + mt_rand( 0, 0x3fff ) | 0x8000, + // 48 bits for "node" + mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ) + ); + } + + return md5( $token . time() ); + } + + /** + * Check that the order status is valid to clear temp data + * + * @param $order_status + * + * @return bool + */ + function isValidOrderStatusToResetCartToken( $order_status ) { + $to_clear_order_status = apply_filters( 'rnoc_to_clear_temp_data_order_status', array( 'failed', 'pending' ) ); + + return in_array( $order_status, $to_clear_order_status ); + } + + /** + * Check the order has valid order statuses + * + * @param $order_status + * + * @return bool + */ + function isOrderHasValidOrderStatus( $order_status ) { + $invalid_order_status = apply_filters( 'rnoc_abandoned_cart_invalid_order_statuses', array( + 'pending', + 'failed', + 'checkout-draft', + 'trash', + 'cancelled', + 'refunded' + ) ); + $consider_on_hold_order_as_ac = $this->considerOnHoldAsAbandoned(); + if ( $consider_on_hold_order_as_ac == 1 ) { + $invalid_order_status[] = 'on-hold'; + } + + $invalid_order_status = array_unique( $invalid_order_status ); + + return ( ! in_array( $order_status, $invalid_order_status ) ); + } + + /** + * Checks whether an order is pending recovery. + * + * @param int|string $order_id order ID + * + * @return bool + * @since 2.1.0 + */ + public function isOrderInPendingRecovery( $order_id ) { + $order = self::$woocommerce->getOrder( $order_id ); + if ( ! $order instanceof \WC_Order ) { + return false; + } + + return (bool) self::$woocommerce->getOrderMeta( $order, $this->pending_recovery_key_for_db ); + } + + /** + * Checks whether an order is recovered. + * + * @param int|string $order_id order ID + * + * @return bool + */ + public function isOrderRecovered( $order_id ) { + $order = wc_get_order( $order_id ); + if ( ! $order instanceof \WC_Order ) { + return false; + } + + return (bool) self::$woocommerce->getOrderMeta( $order, $this->order_recovered_key_for_db ); + } + + /** + * Mark order as recovered + * + * @param $order_id + */ + function markOrderAsRecovered( $order_id ) { + $order = self::$woocommerce->getOrder( $order_id ); + if ( ! $order instanceof \WC_Order || $this->isOrderRecovered( $order_id ) ) { + return; + } + if ( self::$woocommerce->getOrderMeta( $order, '_rnoc_recovered_by', 0 ) == 1 ) { + self::$woocommerce->deleteOrderMeta( $order_id, $this->pending_recovery_key_for_db ); + self::$woocommerce->setOrderMeta( $order_id, $this->order_recovered_key_for_db, true ); + self::$woocommerce->setOrderNote( $order, __( 'Order recovered by Retainful.', RNOC_TEXT_DOMAIN ) ); + do_action( 'rnoc_abandoned_order_recovered', $order ); + } + } + + function changeOrderStatus( $order_status ) { + $changable_order_status = array( 'checkout-draft' ); + if ( $this->considerCancelledAsAbandoned() == 1 ) { + $changable_order_status[] = "cancelled"; + } + if ( $this->considerOnHoldAsAbandoned() == 1 ) { + $changable_order_status[] = "on-hold"; + } + if ( $this->considerFailedAsAbandoned() == 1 ) { + $changable_order_status[] = "failed"; + } + if ( in_array( $order_status, $changable_order_status ) ) { + $order_status = "pending"; + } + + return $order_status; + } + + /** + * Consider on hold payment as abandoned + * @return int + */ + function considerOnHoldAsAbandoned() { + $settings = self::$settings->getAdminSettings(); + + return isset( $settings[ RNOC_PLUGIN_PREFIX . 'consider_on_hold_as_abandoned_status' ] ) ? $settings[ RNOC_PLUGIN_PREFIX . 'consider_on_hold_as_abandoned_status' ] : 0; + } + + /** + * Consider cancelled order as abandoned + * @return int + */ + function considerCancelledAsAbandoned() { + $settings = self::$settings->getAdminSettings(); + + return isset( $settings[ RNOC_PLUGIN_PREFIX . 'consider_cancelled_as_abandoned_status' ] ) ? $settings[ RNOC_PLUGIN_PREFIX . 'consider_cancelled_as_abandoned_status' ] : 1; + } + + /** + * Consider failed order as abandoned + * @return int + */ + function considerFailedAsAbandoned() { + $settings = self::$settings->getAdminSettings(); + + return isset( $settings[ RNOC_PLUGIN_PREFIX . 'consider_failed_as_abandoned_status' ] ) ? $settings[ RNOC_PLUGIN_PREFIX . 'consider_failed_as_abandoned_status' ] : 0; + } + + /** + * refresh fragments on page load + * @return int + */ + function refreshFragmentsOnPageLoad() { + $settings = self::$settings->getAdminSettings(); + + return isset( $settings[ RNOC_PLUGIN_PREFIX . 'refresh_fragments_on_page_load' ] ) ? $settings[ RNOC_PLUGIN_PREFIX . 'refresh_fragments_on_page_load' ] : 0; + } + + /** + * Format the date to ISO8601 + * + * @param $timestamp + * + * @return string|null + */ + function formatToIso8601( $timestamp ) { + if ( empty( $timestamp ) ) { + $timestamp = current_time( 'timestamp', true ); + } + if ( is_object( $timestamp ) && $timestamp instanceof \WC_DateTime ) { + $timestamp = $timestamp->getTimestamp(); + } + + try { + $date = date( 'Y-m-d H:i:s', $timestamp ); + $date_time = new DateTime( $date ); + + return $date_time->format( DateTime::ATOM ); + } + catch ( Exception $e ) { + return null; + } + } + + /** + * Convert price to another price as per currency rate + * + * @param $price + * @param $rate + * + * @return float|int + */ + function convertToCurrency( $price, $rate ) { + if ( ! empty( $price ) && ! empty( $rate ) ) { + return $price / $rate; + } + + return $price; + } + + /** + * Encrypt the cart + * + * @param $data + * @param $secret + * + * @return string + */ + function encryptData( $data, $secret = null ) { + if ( extension_loaded( 'openssl' ) ) { + if ( is_array( $data ) || is_object( $data ) ) { + $data = wp_json_encode( $data ); + } + try { + if ( empty( $secret ) ) { + $secret = self::$settings->getSecretKey(); + } + $iv_len = openssl_cipher_iv_length( self::CIPHER_METHOD ); + $iv = openssl_random_pseudo_bytes( $iv_len ); + $cipher_text_raw = openssl_encrypt( $data, self::CIPHER_METHOD, $secret, OPENSSL_RAW_DATA, $iv ); + $hmac = hash_hmac( self::HMAC_ALGORITHM, $cipher_text_raw, $secret, true ); + + return base64_encode( bin2hex( $iv ) . ':retainful:' . bin2hex( $hmac ) . ':retainful:' . bin2hex( $cipher_text_raw ) ); + } + catch ( Exception $e ) { + return null; + } + } + + return null; + } + + /** + * Decrypt the user cart + * + * @param $data_hash + * + * @return string + */ + function decryptData( $data_hash ) { + $secret = self::$settings->getSecretKey(); + $string = base64_decode( $data_hash ); + list( $iv, $hmac, $cipher_text_raw ) = explode( ':retainful:', $string ); + $reverse_hmac = hash_hmac( self::HMAC_ALGORITHM, $cipher_text_raw, $secret, true ); + if ( hash_equals( $reverse_hmac, $hmac ) ) { + return openssl_decrypt( $cipher_text_raw, self::CIPHER_METHOD, $secret, OPENSSL_RAW_DATA, $iv ); + } + + return null; + } + + /** + * get the active currency code + * @return String|null + */ + function getCurrentCurrencyCode() { + $default_currency = self::$settings->getBaseCurrency(); + + return apply_filters( 'rnoc_get_current_currency_code', $default_currency ); + } + + /** + * Get the date of cart tracing started + * + * @param $user_id + * + * @return array|mixed|string|null + */ + function userCartCreatedAt( $user_id = null ) { + if ( $user_id || $user_id = get_current_user_id() ) { + $cart_created_at = get_user_meta( $user_id, $this->cart_tracking_started_key_for_db, true ); + } else { + $cart_created_at = self::$storage->getValue( $this->cart_tracking_started_key ); + } + + return $cart_created_at; + } + + /** + * When user start adding to cart + * + * @param null $user_id + * @param null $time + * + * @return array|mixed|string|null + */ + function setCartCreatedDate( $user_id = null, $time = null ) { + if ( empty( $time ) ) { + $time = current_time( 'timestamp', true ); + } + if ( ! empty( $user_id ) || $user_id = get_current_user_id() ) { + update_user_meta( $user_id, $this->cart_tracking_started_key_for_db, $time ); + } + + return $time; + } + + /** + * Synchronize cart with SaaS + * + * @param $cart_details + * @param $extra_headers + * + * @return array|bool|mixed|object|string + */ + function syncCart( $cart_details, $extra_headers ) { + $app_id = self::$settings->getApiKey(); + $response = false; + if ( ! empty( $cart_details ) ) { + self::$settings->logMessage( 'PHP', 'synced by' ); + $response = self::$api->syncCartDetails( $app_id, $cart_details, $extra_headers ); + } + + return $response; + } + + /** + * Check is buyer accepts marketing + * @return bool + */ + function isBuyerAcceptsMarketing() { + $settings = self::$settings->getAdminSettings(); + $enable_gdpr_compliance = ( isset( $settings[ RNOC_PLUGIN_PREFIX . 'enable_gdpr_compliance' ] ) ) ? $settings[ RNOC_PLUGIN_PREFIX . 'enable_gdpr_compliance' ] : 0; + if ( $enable_gdpr_compliance ) { + return in_array( self::$woocommerce->getSession( 'is_buyer_accepting_marketing' ), array( 1, 'true' ) ); + } + + return true; + /*if (is_user_logged_in()) { + return true; + } else { + $is_buyer_accepts_marketing = self::$woocommerce->getSession('is_buyer_accepting_marketing'); + if ($is_buyer_accepts_marketing == 1) { + return true; + } + } + return false;*/ + } + + /** + * need to track carts or not + * + * @param string $ip_address + * @param $order null | \WC_Order | \WC_Cart + * + * @return bool + */ + function canTrackAbandonedCarts( $ip_address = null, $order = null ) { + if ( apply_filters( 'rnoc_is_cart_has_valid_ip', true, $ip_address ) && apply_filters( 'rnoc_can_track_abandoned_carts', true, $order ) ) { + return true; + } + + return false; + } + + /** + * get the client details + * + * @param null $order + * + * @return mixed|void + */ + function getClientDetails( $order = null ) { + $client_details = array( + 'accept_language' => $this->getUserAcceptLanguage( $order ) + ); + + return apply_filters( 'rnoc_get_client_details', $client_details, $order ); + } + + /** + * get the user agent of client + * + * @param null $order + * + * @return mixed|string|null + */ + function getUserAgent( $order = null ) { + if ( ! empty( $order ) ) { + return self::$woocommerce->getOrderMeta( $order, '_rnoc_get_http_user_agent' ); + } else { + if ( isset( $_SERVER['HTTP_USER_AGENT'] ) && ! empty( $_SERVER['HTTP_USER_AGENT'] ) ) { + return $_SERVER['HTTP_USER_AGENT']; + } + } + + return ''; + } + + /** + * get the user accept language + * + * @param null $order + * + * @return mixed|string|null + */ + function getUserAcceptLanguage( $order = null ) { + if ( ! empty( $order ) ) { + return self::$woocommerce->getOrderMeta( $order, '_rnoc_get_http_accept_language' ); + } else { + if ( isset( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) && ! empty( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ) { + $lang = trim( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ); + + return substr( $lang, 0, 2 ); + } + } + + return ''; + } } \ No newline at end of file diff --git a/src/Api/Imports/Imports.php b/src/Api/Imports/Imports.php index c8ce112a..e016112a 100644 --- a/src/Api/Imports/Imports.php +++ b/src/Api/Imports/Imports.php @@ -4,255 +4,314 @@ use Rnoc\Retainful\Api\AbandonedCart\Order; -class Imports extends Order -{ - /** - * Hash verification - * @param $data - * @param $hash_value - * @return bool - */ - protected function hashVerification($data, $hash_value) - { - if (!is_array($data) || !is_string($hash_value)) { - return false; - } - $data = json_encode($data); - $secret = self::$settings->getSecretKey(); - $reverse_hmac = hash_hmac('sha256', $data, $secret); - return hash_equals($reverse_hmac, $hash_value); - } +class Imports extends Order { + /** + * Hash verification + * + * @param $data + * @param $hash_value + * + * @return bool + */ + protected function hashVerification( $data, $hash_value ) { + if ( ! is_array( $data ) || ! is_string( $hash_value ) ) { + return false; + } + $data = json_encode( $data ); + $secret = self::$settings->getSecretKey(); + $reverse_hmac = hash_hmac( 'sha256', $data, $secret ); - /** - * get orders - * @param $params - * @return array - */ - protected function getOrders($params) - { - if (!is_array($params) || !isset($params['since_id']) || !isset($params['limit'])) { - return array(); - } - global $wpdb; - if (self::isHPOSEnabled()) { - $query = $wpdb->prepare("SELECT id FROM {$wpdb->prefix}wc_orders LEFT JOIN {$wpdb->prefix}woocommerce_order_items ON {$wpdb->prefix}wc_orders.id = {$wpdb->prefix}woocommerce_order_items.order_id - WHERE type = %s AND id > %s AND status != %s AND {$wpdb->prefix}woocommerce_order_items.order_id > 0 AND {$wpdb->prefix}woocommerce_order_items.order_item_type = %s GROUP BY id ORDER BY id ASC LIMIT %d", array('shop_order', (int)$params['since_id'], 'trash', 'line_item', (int)$params['limit'])); - } else { - $query = $wpdb->prepare("SELECT ID FROM {$wpdb->prefix}posts LEFT JOIN {$wpdb->prefix}woocommerce_order_items ON {$wpdb->prefix}posts.ID = {$wpdb->prefix}woocommerce_order_items.order_id - WHERE post_type IN ('shop_order') AND ID > %d AND post_status != %s AND {$wpdb->prefix}woocommerce_order_items.order_id > 0 AND {$wpdb->prefix}woocommerce_order_items.order_item_type = %s GROUP BY ID ORDER BY ID ASC LIMIT %d", array((int)$params['since_id'], 'trash', 'line_item', (int)$params['limit'])); - } - return $wpdb->get_col($query); - } + return hash_equals( $reverse_hmac, $hash_value ); + } - /** - * get order count - * @return string|null - */ - protected function getOrderCount() - { - global $wpdb; - if (self::isHPOSEnabled()) { - $query = $wpdb->prepare("SELECT COUNT(DISTINCT {$wpdb->prefix}wc_orders.id) FROM {$wpdb->prefix}wc_orders LEFT JOIN {$wpdb->prefix}woocommerce_order_items ON {$wpdb->prefix}wc_orders.id = {$wpdb->prefix}woocommerce_order_items.order_id - WHERE type = %s AND id > 0 AND status != %s AND {$wpdb->prefix}woocommerce_order_items.order_id > 0 AND {$wpdb->prefix}woocommerce_order_items.order_item_type = %s", array('shop_order', 'trash', 'line_item')); - } else { - $query = $wpdb->prepare("SELECT COUNT(DISTINCT {$wpdb->prefix}posts.ID) FROM {$wpdb->prefix}posts LEFT JOIN {$wpdb->prefix}woocommerce_order_items ON {$wpdb->prefix}posts.ID = {$wpdb->prefix}woocommerce_order_items.order_id - WHERE post_type = %s AND ID > 0 AND post_status != %s AND {$wpdb->prefix}woocommerce_order_items.order_id > 0 AND {$wpdb->prefix}woocommerce_order_items.order_item_type = %s", array('shop_order', 'trash', 'line_item')); - } - return $wpdb->get_var($query); - } + /** + * get orders + * + * @param $params + * + * @return array + */ + protected function getOrders( $params ) { + if ( ! is_array( $params ) || ! isset( $params['since_id'] ) || ! isset( $params['limit'] ) ) { + return array(); + } + global $wpdb; + if ( self::isHPOSEnabled() ) { + $query = $wpdb->prepare( "SELECT id FROM {$wpdb->prefix}wc_orders LEFT JOIN {$wpdb->prefix}woocommerce_order_items ON {$wpdb->prefix}wc_orders.id = {$wpdb->prefix}woocommerce_order_items.order_id + WHERE type = %s AND id > %s AND status != %s AND {$wpdb->prefix}woocommerce_order_items.order_id > 0 AND {$wpdb->prefix}woocommerce_order_items.order_item_type = %s GROUP BY id ORDER BY id ASC LIMIT %d", array( + 'shop_order', + (int) $params['since_id'], + 'trash', + 'line_item', + (int) $params['limit'] + ) ); + } else { + $query = $wpdb->prepare( "SELECT ID FROM {$wpdb->prefix}posts LEFT JOIN {$wpdb->prefix}woocommerce_order_items ON {$wpdb->prefix}posts.ID = {$wpdb->prefix}woocommerce_order_items.order_id + WHERE post_type IN ('shop_order') AND ID > %d AND post_status != %s AND {$wpdb->prefix}woocommerce_order_items.order_id > 0 AND {$wpdb->prefix}woocommerce_order_items.order_item_type = %s GROUP BY ID ORDER BY ID ASC LIMIT %d", array( + (int) $params['since_id'], + 'trash', + 'line_item', + (int) $params['limit'] + ) ); + } - /** - * create coupons - * @param \WP_REST_Request $request - * @return \WP_REST_Response - */ - function getSyncOrders(\WP_REST_Request $request) - { - $request_params = $request->get_params(); - $default_request_params = array( - 'limit' => 10, - 'since_id' => 0, - 'status' => 'any', - 'digest' => '' - ); - $params = wp_parse_args($request_params, $default_request_params); - self::$settings->logMessage($params, 'API Orders get request'); - if (is_array($params['limit']) || empty($params['digest']) || !is_string($params['digest']) || empty($params['limit']) || $params['since_id'] < 0 || $params['status'] != 'any') { - self::$settings->logMessage($params, 'API Orders data missing'); - $status = 400; - $response = array('success' => false, 'RESPONSE_CODE' => 'DATA_MISSING', 'message' => 'Invalid data!'); - return new \WP_REST_Response($response, $status); - } - self::$settings->logMessage($params, 'API Orders data matched'); - if (!$this->hashVerification(array('limit' => (int)$params['limit'], 'since_id' => (int)$params['since_id'], 'status' => (string)$params['status']), $params['digest'])) { - self::$settings->logMessage($params, 'API Orders request digest not matched'); - $status = 400; - $response = array('success' => false, 'RESPONSE_CODE' => 'SECURITY_BREACH', 'message' => 'Security validation failed'); - return new \WP_REST_Response($response, $status); - } - $orders = $this->getOrders($params); - //Do like his response - $response = array( - 'success' => true, - 'RESPONSE_CODE' => 'Ok', - 'items' => array() - ); - foreach ($orders as $order_id) { - $order = wc_get_order($order_id); - $response['items'][] = $this->getOrderData($order); - } - $status = 200; - return new \WP_REST_Response($response, $status); - } + return $wpdb->get_col( $query ); + } - /** - * check is HPOS enabled - * @return bool - */ - public static function isHPOSEnabled() - { - if (!class_exists('\Automattic\WooCommerce\Utilities\OrderUtil')) { - return false; - } - if (\Automattic\WooCommerce\Utilities\OrderUtil::custom_orders_table_usage_is_enabled()) { - return true; - } - return false; - } + /** + * get order count + * @return string|null + */ + protected function getOrderCount() { + global $wpdb; + if ( self::isHPOSEnabled() ) { + $query = $wpdb->prepare( "SELECT COUNT(DISTINCT {$wpdb->prefix}wc_orders.id) FROM {$wpdb->prefix}wc_orders LEFT JOIN {$wpdb->prefix}woocommerce_order_items ON {$wpdb->prefix}wc_orders.id = {$wpdb->prefix}woocommerce_order_items.order_id + WHERE type = %s AND id > 0 AND status != %s AND {$wpdb->prefix}woocommerce_order_items.order_id > 0 AND {$wpdb->prefix}woocommerce_order_items.order_item_type = %s", array( + 'shop_order', + 'trash', + 'line_item' + ) ); + } else { + $query = $wpdb->prepare( "SELECT COUNT(DISTINCT {$wpdb->prefix}posts.ID) FROM {$wpdb->prefix}posts LEFT JOIN {$wpdb->prefix}woocommerce_order_items ON {$wpdb->prefix}posts.ID = {$wpdb->prefix}woocommerce_order_items.order_id + WHERE post_type = %s AND ID > 0 AND post_status != %s AND {$wpdb->prefix}woocommerce_order_items.order_id > 0 AND {$wpdb->prefix}woocommerce_order_items.order_item_type = %s", array( + 'shop_order', + 'trash', + 'line_item' + ) ); + } - /** - * Get Order Count via Rest api - * @param \WP_REST_Request $request - * @return \WP_REST_Response - */ - public function getSyncOrderCount(\WP_REST_Request $request) - { - $request_params = $request->get_params(); - $default_request_params = array( - 'status' => 'any', - 'digest' => '' - ); - $params = wp_parse_args($request_params, $default_request_params); - self::$settings->logMessage($params, 'API Orders get request'); - if (empty($params['digest']) || !is_string($params['digest']) || $params['status'] != 'any') { - self::$settings->logMessage($params, 'API Order Count data missing'); - $status = 400; - $response = array('success' => false, 'RESPONSE_CODE' => 'DATA_MISSING', 'message' => 'Invalid data!'); - return new \WP_REST_Response($response, $status); - } - self::$settings->logMessage($params, 'API Order Count data matched'); - if (!$this->hashVerification(array('status' => $params['status']), $params['digest'])) { - self::$settings->logMessage($params, 'API Order Count request digest not matched'); - $status = 400; - $response = array('success' => false, 'RESPONSE_CODE' => 'SECURITY_BREACH', 'message' => 'Security validation failed!'); - return new \WP_REST_Response($response, $status); - } - $response = array( - 'success' => true, - 'RESPONSE_CODE' => 'Ok', - 'total_count' => (int)$this->getOrderCount() - //'total_count' => is_array($orders) ? count($orders) : 0 - ); - $status = 200; - return new \WP_REST_Response($response, $status); - } + return $wpdb->get_var( $query ); + } - /** - * Get order related data - * @param $order - * @return array - */ - public function getOrderData($order) - { - if (!is_object($order)) { // bool|WC_Order|WC_Order_Refund - return array(); - } - $order_id = self::$woocommerce->getOrderId($order); - if (empty($order_id)) return array(); - $cart_token = self::$woocommerce->getOrderMeta($order, $this->cart_token_key_for_db); - if (empty($cart_token)) $cart_token = $this->generateCartToken(); - //still Cart token empty - if (empty($cart_token)) { - return array(); - } - $user_ip = self::$woocommerce->getOrderMeta($order, $this->user_ip_key_for_db); - if (empty($user_ip)) $user_ip = $order->get_customer_ip_address(); - $cart_hash = self::$woocommerce->getOrderMeta($order, $this->cart_hash_key_for_db); - if (empty($cart_hash)) $cart_hash = $order->get_cart_hash(); - $is_buyer_accepts_marketing = self::$woocommerce->getOrderMeta($order, $this->accepts_marketing_key_for_db); - if (!in_array($is_buyer_accepts_marketing, array(0, 1))) $is_buyer_accepts_marketing = $order->get_customer_id() > 0 ? 1 : 0; - $cart_created_at = self::$woocommerce->getOrderMeta($order, $this->cart_tracking_started_key_for_db); - if (empty($cart_created_at)) $cart_created_at = $order->get_date_created(); - if (is_null($cart_created_at)) { - $cart_created_at = current_time('timestamp', true); - } - $updated_at = $order->get_date_modified(); - if (is_null($updated_at)) { - $updated_at = current_time('timestamp', true); - } - //completed_at if available need to do - $consider_on_hold_order_as_ac = $this->considerOnHoldAsAbandoned(); - $customer_details = $this->getCustomerDetails($order); - $default_currency_code = self::$settings->getBaseCurrency(); - $cart_total = $this->formatDecimalPrice(self::$woocommerce->getOrderTotal($order)); - $order_placed_at = self::$woocommerce->getOrderPlacedDate($order); - $order_status = self::$woocommerce->getStatus($order); - $order_status = $this->changeOrderStatus($order_status); - $current_currency_code = self::$woocommerce->getOrderCurrency($order); - $excluding_tax = self::$woocommerce->isPriceExcludingTax(); - $recovered_at = self::$woocommerce->getOrderMeta($order, '_rnoc_recovered_at'); - $user_agent = $this->getUserAgent($order); - if (empty($user_agent)) $user_agent = $order->get_customer_user_agent(); - $customer_language = $this->getOrderLanguage($order); - $order_data = array( - 'cart_type' => 'order', - 'treat_on_hold_as_complete' => ($consider_on_hold_order_as_ac == 0), - 'r_order_id' => $order_id, - 'order_number' => $order_id, - 'order_date' => $this->formatToIso8601(self::$woocommerce->getOrderDate($order)), - 'woo_r_order_number' => self::$woocommerce->getOrderNumber($order), - 'cart_hash' => $cart_hash, - 'ip' => $user_ip, - 'id' => $cart_token, - 'email' => (isset($customer_details['email'])) ? $customer_details['email'] : NULL, - 'token' => $cart_token, - 'currency' => $default_currency_code, - 'customer' => $customer_details, - 'tax_lines' => $this->getOrderTaxDetails(), - 'total_tax' => $this->formatDecimalPrice(self::$woocommerce->getOrderTotalTax($order)), - 'cart_token' => $cart_token, - 'created_at' => $this->formatToIso8601($cart_created_at), - 'line_items' => $this->getOrderLineItemsDetails($order), - 'updated_at' => $this->formatToIso8601($updated_at), - 'source_name' => 'web', - 'total_price' => $cart_total, - 'completed_at' => !empty($order_placed_at) ? $this->formatToIso8601($order_placed_at) : NULL, - 'total_weight' => 0, - 'discount_codes' => self::$woocommerce->getAppliedDiscounts($order), - 'order_status' => apply_filters('rnoc_abandoned_cart_order_status', $order_status, $order), - 'shipping_lines' => array(), - 'subtotal_price' => $this->formatDecimalPrice(self::$woocommerce->getOrderSubTotal($order)), - 'total_price_set' => $this->getCurrencyDetails($cart_total, $current_currency_code, $default_currency_code), - 'taxes_included' => (!self::$woocommerce->isPriceExcludingTax()), - 'customer_locale' => $customer_language, - 'total_discounts' => $this->formatDecimalPrice(self::$woocommerce->getOrderDiscount($order, $excluding_tax)), - 'shipping_address' => $this->getCustomerShippingAddressDetails($order), - 'billing_address' => $this->getCustomerBillingAddressDetails($order), - 'presentment_currency' => $current_currency_code, - 'abandoned_checkout_url' => $this->getRecoveryLink($cart_token), - 'total_line_items_price' => $this->formatDecimalPrice($this->getOrderItemsTotal($order)), - 'buyer_accepts_marketing' => true, - 'cancelled_at' => self::$woocommerce->getOrderMeta($order, $this->order_cancelled_date_key_for_db), - 'woocommerce_totals' => $this->getOrderTotals($order, $excluding_tax), - 'recovered_by_retainful' => (bool)self::$woocommerce->getOrderMeta($order, '_rnoc_recovered_by'), - 'recovered_cart_token' => self::$woocommerce->getOrderMeta($order, '_rnoc_recovered_cart_token'), - 'recovered_at' => (!empty($recovered_at)) ? $this->formatToIso8601($recovered_at) : NULL, - 'client_details' => $this->getClientDetails($order), - 'payment_method' => array( - 'value' => $order->get_payment_method(), - 'name' => $order->get_payment_method_title(), - ) - ); - return apply_filters('rnoc_import_order_data', $order_data, $order); - } + /** + * create coupons + * + * @param \WP_REST_Request $request + * + * @return \WP_REST_Response + */ + function getSyncOrders( \WP_REST_Request $request ) { + $request_params = $request->get_params(); + $default_request_params = array( + 'limit' => 10, + 'since_id' => 0, + 'status' => 'any', + 'digest' => '' + ); + $params = wp_parse_args( $request_params, $default_request_params ); + self::$settings->logMessage( $params, 'API Orders get request' ); + if ( is_array( $params['limit'] ) || empty( $params['digest'] ) || ! is_string( $params['digest'] ) || empty( $params['limit'] ) || $params['since_id'] < 0 || $params['status'] != 'any' ) { + self::$settings->logMessage( $params, 'API Orders data missing' ); + $status = 400; + $response = array( 'success' => false, 'RESPONSE_CODE' => 'DATA_MISSING', 'message' => 'Invalid data!' ); + + return new \WP_REST_Response( $response, $status ); + } + self::$settings->logMessage( $params, 'API Orders data matched' ); + if ( ! $this->hashVerification( array( + 'limit' => (int) $params['limit'], + 'since_id' => (int) $params['since_id'], + 'status' => (string) $params['status'] + ), $params['digest'] ) ) { + self::$settings->logMessage( $params, 'API Orders request digest not matched' ); + $status = 400; + $response = array( + 'success' => false, + 'RESPONSE_CODE' => 'SECURITY_BREACH', + 'message' => 'Security validation failed' + ); + + return new \WP_REST_Response( $response, $status ); + } + $orders = $this->getOrders( $params ); + //Do like his response + $response = array( + 'success' => true, + 'RESPONSE_CODE' => 'Ok', + 'items' => array() + ); + foreach ( $orders as $order_id ) { + $order = wc_get_order( $order_id ); + $response['items'][] = $this->getOrderData( $order ); + } + $status = 200; + + return new \WP_REST_Response( $response, $status ); + } + + /** + * check is HPOS enabled + * @return bool + */ + public static function isHPOSEnabled() { + if ( ! class_exists( '\Automattic\WooCommerce\Utilities\OrderUtil' ) ) { + return false; + } + if ( \Automattic\WooCommerce\Utilities\OrderUtil::custom_orders_table_usage_is_enabled() ) { + return true; + } + + return false; + } + + /** + * Get Order Count via Rest api + * + * @param \WP_REST_Request $request + * + * @return \WP_REST_Response + */ + public function getSyncOrderCount( \WP_REST_Request $request ) { + $request_params = $request->get_params(); + $default_request_params = array( + 'status' => 'any', + 'digest' => '' + ); + $params = wp_parse_args( $request_params, $default_request_params ); + self::$settings->logMessage( $params, 'API Orders get request' ); + if ( empty( $params['digest'] ) || ! is_string( $params['digest'] ) || $params['status'] != 'any' ) { + self::$settings->logMessage( $params, 'API Order Count data missing' ); + $status = 400; + $response = array( 'success' => false, 'RESPONSE_CODE' => 'DATA_MISSING', 'message' => 'Invalid data!' ); + + return new \WP_REST_Response( $response, $status ); + } + self::$settings->logMessage( $params, 'API Order Count data matched' ); + if ( ! $this->hashVerification( array( 'status' => $params['status'] ), $params['digest'] ) ) { + self::$settings->logMessage( $params, 'API Order Count request digest not matched' ); + $status = 400; + $response = array( + 'success' => false, + 'RESPONSE_CODE' => 'SECURITY_BREACH', + 'message' => 'Security validation failed!' + ); + + return new \WP_REST_Response( $response, $status ); + } + $response = array( + 'success' => true, + 'RESPONSE_CODE' => 'Ok', + 'total_count' => (int) $this->getOrderCount() + //'total_count' => is_array($orders) ? count($orders) : 0 + ); + $status = 200; + + return new \WP_REST_Response( $response, $status ); + } + + /** + * Get order related data + * + * @param $order + * + * @return array + */ + public function getOrderData( $order ) { + if ( ! is_object( $order ) ) { // bool|WC_Order|WC_Order_Refund + return array(); + } + $order_id = self::$woocommerce->getOrderId( $order ); + if ( empty( $order_id ) ) { + return array(); + } + $cart_token = self::$woocommerce->getOrderMeta( $order, $this->cart_token_key_for_db ); + if ( empty( $cart_token ) ) { + $cart_token = $this->generateCartToken(); + self::$woocommerce->setOrderMeta( $order_id, $this->cart_token_key_for_db, $cart_token ); + } + //still Cart token empty + if ( empty( $cart_token ) ) { + return array(); + } + $user_ip = self::$woocommerce->getOrderMeta( $order, $this->user_ip_key_for_db ); + if ( empty( $user_ip ) ) { + $user_ip = $order->get_customer_ip_address(); + } + $cart_hash = self::$woocommerce->getOrderMeta( $order, $this->cart_hash_key_for_db ); + if ( empty( $cart_hash ) ) { + $cart_hash = $order->get_cart_hash(); + } + $is_buyer_accepts_marketing = self::$woocommerce->getOrderMeta( $order, $this->accepts_marketing_key_for_db ); + if ( ! in_array( $is_buyer_accepts_marketing, array( 0, 1 ) ) ) { + $is_buyer_accepts_marketing = $order->get_customer_id() > 0 ? 1 : 0; + } + $cart_created_at = self::$woocommerce->getOrderMeta( $order, $this->cart_tracking_started_key_for_db ); + if ( empty( $cart_created_at ) ) { + $cart_created_at = $order->get_date_created(); + } + if ( is_null( $cart_created_at ) ) { + $cart_created_at = current_time( 'timestamp', true ); + } + $updated_at = $order->get_date_modified(); + if ( is_null( $updated_at ) ) { + $updated_at = current_time( 'timestamp', true ); + } + //completed_at if available need to do + $consider_on_hold_order_as_ac = $this->considerOnHoldAsAbandoned(); + $customer_details = $this->getCustomerDetails( $order ); + $default_currency_code = self::$settings->getBaseCurrency(); + $cart_total = $this->formatDecimalPrice( self::$woocommerce->getOrderTotal( $order ) ); + $order_placed_at = self::$woocommerce->getOrderPlacedDate( $order ); + $order_status = self::$woocommerce->getStatus( $order ); + $order_status = $this->changeOrderStatus( $order_status ); + $current_currency_code = self::$woocommerce->getOrderCurrency( $order ); + $excluding_tax = self::$woocommerce->isPriceExcludingTax(); + $recovered_at = self::$woocommerce->getOrderMeta( $order, '_rnoc_recovered_at' ); + $user_agent = $this->getUserAgent( $order ); + if ( empty( $user_agent ) ) { + $user_agent = $order->get_customer_user_agent(); + } + $customer_language = $this->getOrderLanguage( $order ); + $order_data = array( + 'cart_type' => 'order', + 'treat_on_hold_as_complete' => ( $consider_on_hold_order_as_ac == 0 ), + 'r_order_id' => $order_id, + 'order_number' => $order_id, + 'order_date' => $this->formatToIso8601( self::$woocommerce->getOrderDate( $order ) ), + 'woo_r_order_number' => self::$woocommerce->getOrderNumber( $order ), + 'cart_hash' => $cart_hash, + 'ip' => $user_ip, + 'id' => $cart_token, + 'email' => ( isset( $customer_details['email'] ) ) ? $customer_details['email'] : null, + 'token' => $cart_token, + 'currency' => $default_currency_code, + 'customer' => $customer_details, + 'tax_lines' => $this->getOrderTaxDetails(), + 'total_tax' => $this->formatDecimalPrice( self::$woocommerce->getOrderTotalTax( $order ) ), + 'cart_token' => $cart_token, + 'created_at' => $this->formatToIso8601( $cart_created_at ), + 'line_items' => $this->getOrderLineItemsDetails( $order ), + 'updated_at' => $this->formatToIso8601( $updated_at ), + 'source_name' => 'web', + 'total_price' => $cart_total, + 'completed_at' => ! empty( $order_placed_at ) ? $this->formatToIso8601( $order_placed_at ) : null, + 'total_weight' => 0, + 'discount_codes' => self::$woocommerce->getAppliedDiscounts( $order ), + 'order_status' => apply_filters( 'rnoc_abandoned_cart_order_status', $order_status, $order ), + 'shipping_lines' => array(), + 'subtotal_price' => $this->formatDecimalPrice( self::$woocommerce->getOrderSubTotal( $order ) ), + 'total_price_set' => $this->getCurrencyDetails( $cart_total, $current_currency_code, $default_currency_code ), + 'taxes_included' => ( ! self::$woocommerce->isPriceExcludingTax() ), + 'customer_locale' => $customer_language, + 'total_discounts' => $this->formatDecimalPrice( self::$woocommerce->getOrderDiscount( $order, $excluding_tax ) ), + 'shipping_address' => $this->getCustomerShippingAddressDetails( $order ), + 'billing_address' => $this->getCustomerBillingAddressDetails( $order ), + 'presentment_currency' => $current_currency_code, + 'abandoned_checkout_url' => $this->getRecoveryLink( $cart_token ), + 'total_line_items_price' => $this->formatDecimalPrice( $this->getOrderItemsTotal( $order ) ), + 'buyer_accepts_marketing' => true, + 'cancelled_at' => self::$woocommerce->getOrderMeta( $order, $this->order_cancelled_date_key_for_db ), + 'woocommerce_totals' => $this->getOrderTotals( $order, $excluding_tax ), + 'recovered_by_retainful' => (bool) self::$woocommerce->getOrderMeta( $order, '_rnoc_recovered_by' ), + 'recovered_cart_token' => self::$woocommerce->getOrderMeta( $order, '_rnoc_recovered_cart_token' ), + 'recovered_at' => ( ! empty( $recovered_at ) ) ? $this->formatToIso8601( $recovered_at ) : null, + 'client_details' => $this->getClientDetails( $order ), + 'payment_method' => array( + 'value' => $order->get_payment_method(), + 'name' => $order->get_payment_method_title(), + ) + ); + + return apply_filters( 'rnoc_import_order_data', $order_data, $order ); + } } \ No newline at end of file diff --git a/src/Main.php b/src/Main.php index 17921944..9bf4230c 100644 --- a/src/Main.php +++ b/src/Main.php @@ -1,7 +1,9 @@ rnoc = ($this->rnoc == NULL) ? new OrderCoupon() : $this->rnoc; - $this->admin = ($this->admin == NULL) ? new Settings() : $this->admin; - add_filter('woocommerce_set_cookie_options', array($this, 'changeIdentityPath'), 10, 3); - add_action('init', array($this, 'activateEvents')); - add_action('woocommerce_init', array($this, 'includePluginFiles')); - //add_action('woocommerce_init',array($this->admin,'createWebhook')); - add_action('woocommerce_init', array($this->admin, 'setIdentityData')); - //init the retainful premium - new \Rnoc\Retainful\Premium\RetainfulPremiumMain(); - } - - /** - * Change identity path. - * - * @param $option - * @param $name - * @param $value - * @return mixed - */ - function changeIdentityPath($option, $name, $value) - { - if ($name == '_wc_rnoc_tk_session') { - $option['path'] = $this->admin->getIdentityPath(); - } - return $option; - } - - function includePluginFiles() - { - $rnoc_varnish_check = $this->admin->getRetainfulSettingValue('rnoc_varnish_check', 'no'); - if ($rnoc_varnish_check === 'no') { - $woocommerce_functions = new WcFunctions(); - $woocommerce_functions->initWoocommerceSession(); - do_action('rnoc_after_including_plugin_files', $woocommerce_functions, $this); - } - } - - /** - * Register all the required end points - */ - function registerEndPoints() - { - //Register custom endpoint for API - register_rest_route('retainful-api/v1', '/verify', array( - 'methods' => 'POST', - 'permission_callback' => '__return_true', - 'callback' => array($this, 'verifyAppId') - )); - register_rest_route('retainful-api/v1', '/coupon', array( - 'methods' => 'POST', - 'permission_callback' => '__return_true', - 'callback' => 'Rnoc\Retainful\Api\NextOrderCoupon\CouponManagement::createRestCoupon' - )); - register_rest_route('retainful-api/v1', '/customer', array( - 'methods' => 'GET', - 'permission_callback' => '__return_true', - 'callback' => 'Rnoc\Retainful\Api\Referral\ReferralManagement::getCustomer' - )); - } - - function registerSyncEndPoints() - { - $import = new Imports(); - register_rest_route('retainful-api/v1', '/orders', array( - 'methods' => 'GET', - 'permission_callback' => '__return_true', - 'callback' => array($import, 'getSyncOrders') - )); - register_rest_route('retainful-api/v1', '/orders/count', array( - 'methods' => 'GET', - 'permission_callback' => '__return_true', - 'callback' => array($import, 'getSyncOrderCount') - )); - } - - /** - * verify the app id - * @param $data - * @return \WP_REST_Response - */ - function verifyAppId($data) - { - $app_id = sanitize_text_field($data->get_param('app_id')); - $app_secret = sanitize_text_field($data->get_param('app_secret')); - $site_url = site_url(); - $entered_app_id = $this->admin->getApiKey(); - $entered_secret_key = $this->admin->getSecretKey(); - $is_app_connected = $this->admin->isAppConnected(); - $response_code = NULL; - if (empty($entered_secret_key) && empty($entered_app_id)) { - $response_code = 'INSTALLED_NO_APP_ID_AND_NO_SECRET_KEY_FOUND'; - } elseif (empty($entered_app_id)) { - $response_code = 'INSTALLED_NO_APP_ID_FOUND'; - } elseif (empty($entered_secret_key)) { - $response_code = 'INSTALLED_NO_SECRET_KEY_FOUND'; - } elseif (!empty($entered_app_id) && $app_id != $entered_app_id) { - $response_code = 'INSTALLED_DIFFERENT_APP_ID'; - } elseif (!empty($entered_secret_key) && $app_secret != $entered_secret_key) { - $response_code = 'INSTALLED_DIFFERENT_SECRET_KEY'; - } elseif (!empty($entered_secret_key) && !empty($entered_secret_key) && !$is_app_connected) { - $response_code = 'INSTALLED_NOT_CONNECTED'; - } elseif (!empty($entered_app_id) && !empty($entered_secret_key) && $app_secret == $entered_secret_key && $app_id == $entered_app_id && $is_app_connected) { - $response_code = 'INSTALLED_CONNECTED'; - } else { - $response_code = 'UNKNOWN_ERROR'; - } - $response = array( - 'success' => ($response_code == 'INSTALLED_CONNECTED') ? true : false, - 'message' => '', - 'code' => $response_code, - 'data' => array( - 'domain' => $site_url - ) - ); - $response_object = new \WP_REST_Response($response); - $response_object->set_status(200); - return $response_object; - } - - /** - * Check the woocommerce ac need to run externally - * @param $need_ac_externally - * @return bool|mixed|void - */ - function needToRunAbandonedCartExternally($need_ac_externally) - { - $need_ac_externally = $this->admin->runAbandonedCartExternally(); - return $need_ac_externally; - } - - /** - * Activate the required events - */ - function activateEvents() - { - //Register deactivation hook - register_deactivation_hook(RNOC_FILE, array($this, 'onPluginDeactivation')); - //add_action('retainful_plugin_activated', array($this, 'createRequiredTables')); - //add end points - add_action('rest_api_init', array($this, 'registerEndPoints')); - //Detect woocommerce plugin deactivation - add_action('deactivated_plugin', array($this, 'detectPluginDeactivation'), 10, 2); - //Check for dependencies - add_action('plugins_loaded', array($this, 'checkDependencies')); - add_action('rnocp_activation_trigger', array($this, 'checkUserPlan')); - add_filter('rnoc_need_to_run_ac_in_cloud', array($this, 'needToRunAbandonedCartExternally')); - if (is_admin()) { - //Deactivation survey form - add_action('admin_init', array($this->admin, 'setupSurveyForm'), 10); - $coupon_api = new CouponManagement(); - add_filter('views_edit-shop_coupon', array($coupon_api, 'viewsEditShopCoupon')); - add_action('manage_posts_extra_tablenav', array($coupon_api, 'showDeleteButton')); - add_filter('woocommerce_coupon_options', array($coupon_api, 'showCouponOrderDetails')); - add_filter('request', array($coupon_api, 'requestQuery')); - add_action('admin_menu', array($this->admin, 'registerMenu')); - $this->admin->initAdminPageStyles(); - //Validate key - add_action('wp_ajax_validate_app_key', array($this->admin, 'validateAppKey')); - add_action('wp_ajax_rnoc_get_search_coupon', array($this->admin, 'getSearchedCoupons')); - add_action('wp_ajax_rnoc_disconnect_license', array($this->admin, 'disconnectLicense')); - add_action('wp_ajax_rnoc_save_settings', array($this->admin, 'saveAcSettings')); - //add_filter('wp_ajax_rnoc_create_order_update_webhook',array($this->admin,'saveNewWebhook'),10); - add_action('wp_ajax_rnoc_save_noc_settings', array($this->admin, 'saveNocSettings')); - add_action('wp_ajax_rnoc_save_premium_addon_settings', array($this->admin, 'savePremiumAddOnSettings')); - add_action('wp_ajax_rnoc_delete_expired_coupons', array($this->admin, 'deleteUnusedExpiredCoupons')); - //Settings link - add_filter('plugin_action_links_' . RNOC_BASE_FILE, array($this->rnoc, 'pluginActionLinks')); - if (apply_filters('rnoc_show_order_token_in_order', false)) { - add_action('add_meta_boxes', array($this->admin, 'addOrderDetailMetaBoxes'), 20); - } - } - //initialise currency helper - new Currency(); - $can_hide_next_order_coupon = get_option('retainful_hide_next_order_coupon', 'no'); - $show_deprecate_message = isset($_REQUEST['page']) && in_array($_REQUEST['page'], array('retainful_license', 'retainful_settings', 'retainful', 'retainful_premium')); - if (is_admin() && $show_deprecate_message && $this->admin->isNextOrderCouponEnabled() && $can_hide_next_order_coupon == 'no') { - $notice = '

' . __("The Next Order Coupon feature inside the plugin and its tab/menu will soon be removed from the Retainful plugin. Migrate your Next Order Coupon campaign to the Automations now. A detailed guide here", RNOC_TEXT_DOMAIN) . '

'; - $this->showAdminNotice($notice); - } - if ($this->admin->isNextOrderCouponEnabled()) { - //Get events - add_action('woocommerce_checkout_update_order_meta', array($this->rnoc, 'createNewCoupon'), 10, 2); - add_action('woocommerce_order_status_changed', array($this->rnoc, 'onAfterPayment'), 10, 1); - add_action('woocommerce_get_shop_coupon_data', array($this->rnoc, 'addVirtualCoupon'), 10, 2); - add_action('rnoc_create_new_next_order_coupon', array($this->rnoc, 'createNewCoupon'), 10, 2); - add_action('rnoc_initiated', array($this->rnoc, 'setCouponToSession')); - add_action('wp_loaded', array($this->rnoc, 'addCouponToCheckout'), 10); - //Attach coupon to email - $hook = $this->admin->couponMessageHook(); - if (!empty($hook) && $hook != "none") { - add_action($hook, array($this->rnoc, 'attachOrderCoupon'), 10, 4); - } - //add action for filter - add_action('rnoc_show_order_coupon', array($this->rnoc, 'attachOrderCoupon'), 10, 4); - //Sync the coupon details with retainful - add_action('retainful_cron_sync_coupon_details', array($this->rnoc, 'cronSendCouponDetails'), 1); - //Remove coupon code after placing order - add_action('woocommerce_thankyou', array($this->rnoc, 'removeCouponFromSession'), 10, 1); - // Show coupon in order thankyou page - add_action('woocommerce_thankyou', array($this->rnoc, 'showCouponInThankYouPage'), 10, 1); - //Remove Code from session - add_action('woocommerce_removed_coupon', array($this->rnoc, 'removeCouponFromCart')); - /* - * Support for woocommerce email customizer - */ - add_filter('woo_email_drag_and_drop_builder_retainful_settings_url', array($this->rnoc, 'wooEmailCustomizerRetainfulSettingsUrl')); - //Tell Email customizes about handling coupons.. - add_filter('woo_email_drag_and_drop_builder_handling_retainful', '__return_true'); - //set coupon details for Email customizer - add_filter('woo_email_drag_and_drop_builder_retainful_next_order_coupon_data', array($this->rnoc, 'wooEmailCustomizerRetainfulCouponContent'), 10, 3); - //sent retainful additional short codes - add_filter('woo_email_drag_and_drop_builder_load_additional_shortcode', array($this->rnoc, 'wooEmailCustomizerRegisterRetainfulShortCodes'), 10); - add_filter('woo_email_drag_and_drop_builder_load_additional_shortcode_data', array($this->rnoc, 'wooEmailCustomizerRetainfulShortCodesValues'), 10, 3); - add_filter('wp_footer', array($this->rnoc, 'showAppliedCouponPopup')); - } - /** - * Ip filtering - */ - $this->canActivateIPFilter(); - $is_app_connected = $this->admin->isAppConnected(); - $secret_key = $this->admin->getSecretKey(); - $app_id = $this->admin->getApiKey(); - - $run_installation_externally = $this->admin->runAbandonedCartExternally(); - if ($run_installation_externally) { - //If the user is old user then ask user to run abandoned cart to - /*$is_app_connected = $this->admin->isAppConnected(); - $secret_key = $this->admin->getSecretKey(); - $app_id = $this->admin->getApiKey();*/ - if ($is_app_connected && !empty($secret_key) && !empty($app_id)) { - add_action('rest_api_init', array($this, 'registerSyncEndPoints')); - if (is_admin()) { - add_action('wp_after_admin_bar_render', array($this->admin, 'schedulePlanChecker')); - } - /* - * Retainful abandoned cart api - */ - $cart = new Cart(); - $checkout = new Checkout(); - $need_referral_program = $this->admin->needReferralWidget(); - $need_popup_widget = $this->admin->needPopupWidget(); - if ($this->admin->isProPlan() && ($need_referral_program || $need_popup_widget)) { - if ($need_referral_program) { - $referral_program = new ReferralManagement(); - add_action('wp_footer', array($referral_program, 'printReferralPopup')); - $need_embeded_referral_program = $this->admin->needEmbededReferralWidget(); - if ($need_embeded_referral_program) { - add_action('woocommerce_account_dashboard', array($referral_program, 'printEmbededReferralPopup')); - } - } - if ($need_popup_widget) { - $popup = new Popup(); - add_action('user_register', array($popup, 'userRegister')); - add_action('wp_login', array($popup, 'userLogin'), 10, 2); - add_action('wp_enqueue_scripts', array($popup, 'addPopupScripts')); - add_action('wp_footer', array($popup, 'printPopup')); - } - } - add_filter('script_loader_tag', array($cart, 'addCloudFlareAttrScript'), 10, 3); - //add_filter('clean_url', array($cart, 'uncleanUrl'), 10, 3); - //Sync the order by the scheduled events - add_action('retainful_sync_abandoned_cart_order', array($checkout, 'syncOrderByScheduler'), 1); - add_action('wp_ajax_rnoc_track_user_data', array($cart, 'setCustomerData')); - add_action('wp_ajax_nopriv_rnoc_track_user_data', array($cart, 'setCustomerData')); - add_action('wp_ajax_rnoc_ajax_get_encrypted_cart', array($cart, 'ajaxGetEncryptedCart')); - add_action('wp_ajax_nopriv_rnoc_ajax_get_encrypted_cart', array($cart, 'ajaxGetEncryptedCart')); - add_action('woocommerce_cart_loaded_from_session', array($cart, 'handlePersistentCart')); - //add_action('wp_login', array($cart, 'userLoggedIn')); - add_action('woocommerce_api_retainful', array($cart, 'recoverUserCart')); - add_action('wp_loaded', array($cart, 'applyAbandonedCartCoupon')); - add_action('woocommerce_removed_coupon', array($cart, 'removeNextOrderCouponFromCart')); - //Add tracking message - /*if (is_user_logged_in()) { - add_action('woocommerce_after_add_to_cart_button', array($cart, 'userGdprMessage'), 10); - add_action('woocommerce_before_shop_loop', array($cart, 'userGdprMessage'), 10); - }*/ - add_filter('woocommerce_checkout_fields', array($cart, 'guestGdprMessage'), 10, 1); - add_action('woocommerce_checkout_after_terms_and_conditions', array($cart, 'guestTermGdprMessage')); - add_action('wp_footer', array($checkout, 'setRetainfulOrderData')); - add_filter('rnoc_can_track_abandoned_carts', array($cart, 'isZeroValueCart'), 15, 2); - $cart_tracking_engine = $this->admin->getCartTrackingEngine(); - if ($cart_tracking_engine == "php") { - //PHP tracking - add_action('woocommerce_after_calculate_totals', array($cart, 'syncCartData')); - } else { - //Js tracking - add_action('wp_footer', array($cart, 'renderAbandonedCartTrackingDiv')); - add_filter('woocommerce_add_to_cart_fragments', array($cart, 'addToCartFragments')); - } - add_action('wp_footer', array($cart, 'printRefreshFragmentScript')); - add_action('wp_enqueue_scripts', array($cart, 'addCartTrackingScripts')); - add_action('wp_authenticate', array($cart, 'userLoggedOn')); - add_action('user_register', array($cart, 'userSignedUp')); - add_action('wp_logout', array($cart, 'userLoggedOut')); - //Set order as recovered - // handle payment complete, from a direct gateway - //add_action('woocommerce_new_order', array($checkout, 'purchaseComplete')); - add_action('woocommerce_thankyou', array($checkout, 'payPageOrderCompletion')); - add_action('woocommerce_payment_complete', array($checkout, 'paymentCompleted')); - add_action('woocommerce_checkout_update_order_meta', array($checkout, 'checkoutOrderProcessed')); - add_action('woocommerce_store_api_checkout_update_order_meta', array($checkout, 'apiCheckoutOrderProcessed')); - // add_filter('woocommerce_payment_successful_result', array($checkout, 'maybeUpdateOrderOnSuccessfulPayment'), 10, 2); - // handle updating Retainful order data after a successful payment, for certain gateways - add_action('woocommerce_order_status_changed', array($checkout, 'orderStatusChanged'), 15, 3); - // handle placed orders - add_action('woocommerce_order_status_changed', array($checkout, 'orderUpdated'), 11, 1); - //triggers when admin pdate the order - add_action('woocommerce_process_shop_order_meta', array($checkout, 'OrderUpdatedShopBackend'), 50, 2); - - //add_action('woocommerce_update_order', array($checkout, 'orderUpdated'), 10, 1); - add_filter('woocommerce_webhook_http_args', array($checkout, 'changeWebHookHeader'), 10, 3); - //Todo: multi currency and multi lingual - //add_action('wp_login', array($this->abandoned_cart_api, 'userCartUpdated')); - if ($this->admin->isAfterPayEnabled()) { - new AfterPay(); - } - - } else { - if (is_admin()) { - $connect_txt = (!empty($secret_key) && !empty($app_id)) ? __('connect', RNOC_TEXT_DOMAIN) : __('re-connect', RNOC_TEXT_DOMAIN); - $notice = '

' . sprintf(__("Please %s with Retainful to track and manage abandoned carts. ", RNOC_TEXT_DOMAIN), $connect_txt) . '

'; - $this->showAdminNotice($notice); - } - } - } else { - //remove - } - //Premium check - add_action('rnocp_check_user_plan', array($this, 'checkUserPlan')); - do_action('rnoc_initiated'); - if (is_admin()) { - $is_retainful_v2_0_1_migration_completed = get_option('is_retainful_v2_0_1_migration_completed', 0); - if (!$is_retainful_v2_0_1_migration_completed) { - $this->migrationV201(); - } - $this->checkApi(); - } - } - - function canActivateIPFilter() - { - $settings = $this->admin->getAdminSettings(); - if (isset($settings[RNOC_PLUGIN_PREFIX . 'enable_ip_filter']) && !empty($settings[RNOC_PLUGIN_PREFIX . 'enable_ip_filter']) && isset($settings[RNOC_PLUGIN_PREFIX . 'ignored_ip_addresses']) && !empty($settings[RNOC_PLUGIN_PREFIX . 'ignored_ip_addresses'])) { - $ip = $settings[RNOC_PLUGIN_PREFIX . 'ignored_ip_addresses']; - if (!empty($ip)) { - $ip_filter = new IpFiltering($ip); - add_filter('rnoc_is_cart_has_valid_ip', array($ip_filter, 'trackAbandonedCart'), 10, 2); - } - } - } - - /** - * Migration for 2.1.0 - */ - function migrationV201() - { - $premium_settings = get_option($this->admin->slug . '_premium'); - $admin_settings = $this->admin->getAdminSettings(); - $admin_settings[RNOC_PLUGIN_PREFIX . 'enable_ip_filter'] = isset($premium_settings[RNOC_PLUGIN_PREFIX . 'enable_ip_filter']) ? $premium_settings[RNOC_PLUGIN_PREFIX . 'enable_ip_filter'] : 0; - $admin_settings[RNOC_PLUGIN_PREFIX . 'ignored_ip_addresses'] = isset($premium_settings[RNOC_PLUGIN_PREFIX . 'ignored_ip_addresses']) ? $premium_settings[RNOC_PLUGIN_PREFIX . 'ignored_ip_addresses'] : ''; - update_option($this->admin->slug . '_settings', $admin_settings); - update_option('is_retainful_v2_0_1_migration_completed', 1); - } - - /** - * Run when our plugin get deactivated - */ - function onPluginDeactivation() - { - $this->removeAllScheduledActions(); - $this->admin->removeWebhook(); - } - - - /** - * Remove all actions without any knowledge - */ - function removeAllScheduledActions() - { - $this->admin->removeFinishedHooks('rnoc_abandoned_clear_abandoned_carts'); - $this->admin->removeFinishedHooks('rnoc_abandoned_cart_send_email'); - $this->admin->removeFinishedHooks('rnocp_check_user_plan'); - } - - /** - * check api is valid or not on 3 days once - */ - function checkApi() - { - $last_checked = get_option('rnoc_last_plan_checked', NULL); - if (empty($last_checked) || (current_time('timestamp') > intval($last_checked) + 259200)) { - $this->checkUserPlan(); - } - } - - /** - * Check and update the user plan - */ - function checkUserPlan() - { - $api_key = $this->admin->getApiKey(); - $secret_key = $this->admin->getSecretKey(); - if (!empty($api_key) && !empty($secret_key)) { - $api_obj = new RestApi(); - $store_data = array( - 'secret_key' => $api_obj->encryptData($api_key, $secret_key)); - $this->admin->isApiEnabled($api_key, $secret_key, $store_data); - } else { - $this->admin->updateUserAsFreeUser(); - } - $this->admin->removeFinishedHooks('rnocp_check_user_plan', 'publish'); - } - - /** - * Insert default email template - * @param $table - */ - function insertDefaultEmailTemplate($table) - { - ob_start(); - include(RNOC_PLUGIN_PATH . 'src/admin/templates/default-1.html'); - $content = ob_get_clean(); - $email_body = addslashes($content); - ob_start(); - include(RNOC_PLUGIN_PATH . 'src/admin/templates/default-2.html'); - $content1 = ob_get_clean(); - $email_body1 = addslashes($content1); - ob_start(); - include(RNOC_PLUGIN_PATH . 'src/admin/templates/default-3.html'); - $content2 = ob_get_clean(); - $email_body2 = addslashes($content2); - global $wpdb; - $default_template = $wpdb->get_row('SELECT id FROM ' . $table . ' WHERE default_template = "1"'); - if (empty($default_template)) { - $template_subject = "Hey {{customer_name}}!! You left something in your cart"; - $query = 'INSERT INTO `' . $table . '` ( subject, body, is_active, frequency, day_or_hour, default_template,template_name )VALUES ( "' . $template_subject . '","' . $email_body . '","1","1","Hours","1","initial"),( "' . $template_subject . '","' . $email_body1 . '","0","1","Hours","6","After 6 hours"),( "' . $template_subject . '","' . $email_body2 . '","0","1","Days","1","After 1 day")'; - $wpdb->query($query); - } - } - - /** - * Initiate the plugin - * @return Main - */ - public static function instance() - { - return self::$init = (self::$init == NULL) ? new self() : self::$init; - } - - function removeDependentTables() - { - } - - /** - * All tables required for retainful abandoned cart - * @return array - */ - function getAbandonedCartTables() - { - return array( - RNOC_PLUGIN_PREFIX . 'abandoned_cart_history', - RNOC_PLUGIN_PREFIX . 'guest_abandoned_cart_history' - ); - } - - /** - * detect woocommerce have been deactivated - * @param $plugin - * @param $network_activation - */ - function detectPluginDeactivation($plugin, $network_activation) - { - if (in_array($plugin, array('woocommerce/woocommerce.php'))) { - deactivate_plugins(plugin_basename(__FILE__)); - //Todo - Deactivate this plugin - } - } - - /** - * Dependency check for our plugin - */ - function checkDependencies() - { - if (!defined('WC_VERSION')) { - $this->showAdminNotice(__('Woocommerce must be activated for Retainful-Woocommerce to work', RNOC_TEXT_DOMAIN)); - } else { - if (version_compare(WC_VERSION, '2.5', '<')) { - $this->showAdminNotice(__('Your woocommerce version is ', RNOC_TEXT_DOMAIN) . WC_VERSION . __('. Some of the features of Retainful-Woocommerce will not work properly on this woocommerce version.', RNOC_TEXT_DOMAIN)); - } - } - if (is_admin()) { - $this->doMigration(); - } - } - - /** - * Migrate data required for v 1.1.3 - */ - function doMigration() - { - $is_migrated = get_option('retainful_v_1_1_3_migration_completed', 0); - if (!$is_migrated) { - $slug = $this->admin->slug; - $retainful_page = get_option($slug, array()); - $licence_page = get_option($slug . '_license', array()); - $usage_restriction_page = get_option($slug . '_usage_restriction', array()); - if (empty($licence_page)) { - $licence_data = array( - RNOC_PLUGIN_PREFIX . 'is_retainful_connected' => (isset($retainful_page[RNOC_PLUGIN_PREFIX . 'is_retainful_connected'])) ? $retainful_page[RNOC_PLUGIN_PREFIX . 'is_retainful_connected'] : 0, - RNOC_PLUGIN_PREFIX . 'retainful_app_id' => (isset($retainful_page[RNOC_PLUGIN_PREFIX . 'retainful_app_id'])) ? $retainful_page[RNOC_PLUGIN_PREFIX . 'retainful_app_id'] : '', - RNOC_PLUGIN_PREFIX . 'retainful_app_secret' => (isset($retainful_page[RNOC_PLUGIN_PREFIX . 'retainful_app_secret'])) ? $retainful_page[RNOC_PLUGIN_PREFIX . 'retainful_app_secret'] : '' - ); - update_option($slug . '_license', $licence_data); - } - unset($retainful_page[RNOC_PLUGIN_PREFIX . 'is_retainful_connected'], $retainful_page[RNOC_PLUGIN_PREFIX . 'retainful_app_id']); - $retainful_data = array_merge($retainful_page, $usage_restriction_page); - update_option($slug, $retainful_data); - delete_option($slug . '_usage_restriction'); - $abandoned_cart_data = get_option($slug . '_abandoned_cart_settings', array()); - update_option($slug . '_settings', $abandoned_cart_data); - delete_option($slug . '_abandoned_cart_settings'); - update_option('retainful_v_1_1_3_migration_completed', 1); - } - } - - /** - * Show notices for user..if anything unusually happen in our plugin - * @param string $message - message to notice users - */ - function showAdminNotice($message = "") - { - if (!empty($message)) { - add_action('admin_notices', function () use ($message) { - echo '

' . $message . '

'; - }); - } - } +class Main { + public static $init; + public $rnoc, $admin, $abandoned_cart_api; + + /** + * Main constructor. + */ + function __construct() { + $this->rnoc = ( $this->rnoc == null ) ? new OrderCoupon() : $this->rnoc; + $this->admin = ( $this->admin == null ) ? new Settings() : $this->admin; + add_filter( 'woocommerce_set_cookie_options', array( $this, 'changeIdentityPath' ), 10, 3 ); + add_action( 'init', array( $this, 'activateEvents' ) ); + add_action( 'woocommerce_init', array( $this, 'includePluginFiles' ) ); + //add_action('woocommerce_init',array($this->admin,'createWebhook')); + add_action( 'woocommerce_init', array( $this->admin, 'setIdentityData' ) ); + //init the retainful premium + new \Rnoc\Retainful\Premium\RetainfulPremiumMain(); + } + + /** + * Change identity path. + * + * @param $option + * @param $name + * @param $value + * + * @return mixed + */ + function changeIdentityPath( $option, $name, $value ) { + if ( $name == '_wc_rnoc_tk_session' ) { + $option['path'] = $this->admin->getIdentityPath(); + } + + return $option; + } + + function includePluginFiles() { + $rnoc_varnish_check = $this->admin->getRetainfulSettingValue( 'rnoc_varnish_check', 'no' ); + if ( $rnoc_varnish_check === 'no' ) { + $woocommerce_functions = new WcFunctions(); + $woocommerce_functions->initWoocommerceSession(); + do_action( 'rnoc_after_including_plugin_files', $woocommerce_functions, $this ); + } + } + + /** + * Register all the required end points + */ + function registerEndPoints() { + //Register custom endpoint for API + register_rest_route( 'retainful-api/v1', '/verify', array( + 'methods' => 'POST', + 'permission_callback' => '__return_true', + 'callback' => array( $this, 'verifyAppId' ) + ) ); + register_rest_route( 'retainful-api/v1', '/coupon', array( + 'methods' => 'POST', + 'permission_callback' => '__return_true', + 'callback' => 'Rnoc\Retainful\Api\NextOrderCoupon\CouponManagement::createRestCoupon' + ) ); + register_rest_route( 'retainful-api/v1', '/customer', array( + 'methods' => 'GET', + 'permission_callback' => '__return_true', + 'callback' => 'Rnoc\Retainful\Api\Referral\ReferralManagement::getCustomer' + ) ); + } + + function registerSyncEndPoints() { + $import = new Imports(); + register_rest_route( 'retainful-api/v1', '/orders', array( + 'methods' => 'GET', + 'permission_callback' => '__return_true', + 'callback' => array( $import, 'getSyncOrders' ) + ) ); + register_rest_route( 'retainful-api/v1', '/orders/count', array( + 'methods' => 'GET', + 'permission_callback' => '__return_true', + 'callback' => array( $import, 'getSyncOrderCount' ) + ) ); + } + + /** + * verify the app id + * + * @param $data + * + * @return \WP_REST_Response + */ + function verifyAppId( $data ) { + $app_id = sanitize_text_field( $data->get_param( 'app_id' ) ); + $app_secret = sanitize_text_field( $data->get_param( 'app_secret' ) ); + $site_url = site_url(); + $entered_app_id = $this->admin->getApiKey(); + $entered_secret_key = $this->admin->getSecretKey(); + $is_app_connected = $this->admin->isAppConnected(); + $response_code = null; + if ( empty( $entered_secret_key ) && empty( $entered_app_id ) ) { + $response_code = 'INSTALLED_NO_APP_ID_AND_NO_SECRET_KEY_FOUND'; + } elseif ( empty( $entered_app_id ) ) { + $response_code = 'INSTALLED_NO_APP_ID_FOUND'; + } elseif ( empty( $entered_secret_key ) ) { + $response_code = 'INSTALLED_NO_SECRET_KEY_FOUND'; + } elseif ( ! empty( $entered_app_id ) && $app_id != $entered_app_id ) { + $response_code = 'INSTALLED_DIFFERENT_APP_ID'; + } elseif ( ! empty( $entered_secret_key ) && $app_secret != $entered_secret_key ) { + $response_code = 'INSTALLED_DIFFERENT_SECRET_KEY'; + } elseif ( ! empty( $entered_secret_key ) && ! empty( $entered_secret_key ) && ! $is_app_connected ) { + $response_code = 'INSTALLED_NOT_CONNECTED'; + } elseif ( ! empty( $entered_app_id ) && ! empty( $entered_secret_key ) && $app_secret == $entered_secret_key && $app_id == $entered_app_id && $is_app_connected ) { + $response_code = 'INSTALLED_CONNECTED'; + } else { + $response_code = 'UNKNOWN_ERROR'; + } + $response = array( + 'success' => ( $response_code == 'INSTALLED_CONNECTED' ) ? true : false, + 'message' => '', + 'code' => $response_code, + 'data' => array( + 'domain' => $site_url + ) + ); + $response_object = new \WP_REST_Response( $response ); + $response_object->set_status( 200 ); + + return $response_object; + } + + /** + * Check the woocommerce ac need to run externally + * + * @param $need_ac_externally + * + * @return bool|mixed|void + */ + function needToRunAbandonedCartExternally( $need_ac_externally ) { + $need_ac_externally = $this->admin->runAbandonedCartExternally(); + + return $need_ac_externally; + } + + /** + * Activate the required events + */ + function activateEvents() { + //Register deactivation hook + register_deactivation_hook( RNOC_FILE, array( $this, 'onPluginDeactivation' ) ); + //add_action('retainful_plugin_activated', array($this, 'createRequiredTables')); + //add end points + add_action( 'rest_api_init', array( $this, 'registerEndPoints' ) ); + //Detect woocommerce plugin deactivation + add_action( 'deactivated_plugin', array( $this, 'detectPluginDeactivation' ), 10, 2 ); + //Check for dependencies + add_action( 'plugins_loaded', array( $this, 'checkDependencies' ) ); + add_action( 'rnocp_activation_trigger', array( $this, 'checkUserPlan' ) ); + add_filter( 'rnoc_need_to_run_ac_in_cloud', array( $this, 'needToRunAbandonedCartExternally' ) ); + if ( is_admin() ) { + //Deactivation survey form + add_action( 'admin_init', array( $this->admin, 'setupSurveyForm' ), 10 ); + $coupon_api = new CouponManagement(); + add_filter( 'views_edit-shop_coupon', array( $coupon_api, 'viewsEditShopCoupon' ) ); + add_action( 'manage_posts_extra_tablenav', array( $coupon_api, 'showDeleteButton' ) ); + add_filter( 'woocommerce_coupon_options', array( $coupon_api, 'showCouponOrderDetails' ) ); + add_filter( 'request', array( $coupon_api, 'requestQuery' ) ); + add_action( 'admin_menu', array( $this->admin, 'registerMenu' ) ); + $this->admin->initAdminPageStyles(); + //Validate key + add_action( 'wp_ajax_validate_app_key', array( $this->admin, 'validateAppKey' ) ); + add_action( 'wp_ajax_rnoc_get_search_coupon', array( $this->admin, 'getSearchedCoupons' ) ); + add_action( 'wp_ajax_rnoc_disconnect_license', array( $this->admin, 'disconnectLicense' ) ); + add_action( 'wp_ajax_rnoc_save_settings', array( $this->admin, 'saveAcSettings' ) ); + //add_filter('wp_ajax_rnoc_create_order_update_webhook',array($this->admin,'saveNewWebhook'),10); + add_action( 'wp_ajax_rnoc_save_noc_settings', array( $this->admin, 'saveNocSettings' ) ); + add_action( 'wp_ajax_rnoc_save_premium_addon_settings', array( $this->admin, 'savePremiumAddOnSettings' ) ); + add_action( 'wp_ajax_rnoc_delete_expired_coupons', array( $this->admin, 'deleteUnusedExpiredCoupons' ) ); + //Settings link + add_filter( 'plugin_action_links_' . RNOC_BASE_FILE, array( $this->rnoc, 'pluginActionLinks' ) ); + if ( apply_filters( 'rnoc_show_order_token_in_order', false ) ) { + add_action( 'add_meta_boxes', array( $this->admin, 'addOrderDetailMetaBoxes' ), 20 ); + } + } + //initialise currency helper + new Currency(); + $can_hide_next_order_coupon = get_option( 'retainful_hide_next_order_coupon', 'no' ); + $show_deprecate_message = isset( $_REQUEST['page'] ) && in_array( $_REQUEST['page'], array( + 'retainful_license', + 'retainful_settings', + 'retainful', + 'retainful_premium' + ) ); + if ( is_admin() && $show_deprecate_message && $this->admin->isNextOrderCouponEnabled() && $can_hide_next_order_coupon == 'no' ) { + $notice = '

' . __( "The Next Order Coupon feature inside the plugin and its tab/menu will soon be removed from the Retainful plugin. Migrate your Next Order Coupon campaign to the Automations now. A detailed guide here", RNOC_TEXT_DOMAIN ) . '

'; + $this->showAdminNotice( $notice ); + } + if ( $this->admin->isNextOrderCouponEnabled() ) { + //Get events + add_action( 'woocommerce_checkout_update_order_meta', array( $this->rnoc, 'createNewCoupon' ), 10, 2 ); + add_action( 'woocommerce_order_status_changed', array( $this->rnoc, 'onAfterPayment' ), 10, 1 ); + add_action( 'woocommerce_get_shop_coupon_data', array( $this->rnoc, 'addVirtualCoupon' ), 10, 2 ); + add_action( 'rnoc_create_new_next_order_coupon', array( $this->rnoc, 'createNewCoupon' ), 10, 2 ); + add_action( 'rnoc_initiated', array( $this->rnoc, 'setCouponToSession' ) ); + add_action( 'wp_loaded', array( $this->rnoc, 'addCouponToCheckout' ), 10 ); + //Attach coupon to email + $hook = $this->admin->couponMessageHook(); + if ( ! empty( $hook ) && $hook != "none" ) { + add_action( $hook, array( $this->rnoc, 'attachOrderCoupon' ), 10, 4 ); + } + //add action for filter + add_action( 'rnoc_show_order_coupon', array( $this->rnoc, 'attachOrderCoupon' ), 10, 4 ); + //Sync the coupon details with retainful + add_action( 'retainful_cron_sync_coupon_details', array( $this->rnoc, 'cronSendCouponDetails' ), 1 ); + //Remove coupon code after placing order + add_action( 'woocommerce_thankyou', array( $this->rnoc, 'removeCouponFromSession' ), 10, 1 ); + // Show coupon in order thankyou page + add_action( 'woocommerce_thankyou', array( $this->rnoc, 'showCouponInThankYouPage' ), 10, 1 ); + //Remove Code from session + add_action( 'woocommerce_removed_coupon', array( $this->rnoc, 'removeCouponFromCart' ) ); + /* + * Support for woocommerce email customizer + */ + add_filter( 'woo_email_drag_and_drop_builder_retainful_settings_url', array( + $this->rnoc, + 'wooEmailCustomizerRetainfulSettingsUrl' + ) ); + //Tell Email customizes about handling coupons.. + add_filter( 'woo_email_drag_and_drop_builder_handling_retainful', '__return_true' ); + //set coupon details for Email customizer + add_filter( 'woo_email_drag_and_drop_builder_retainful_next_order_coupon_data', array( + $this->rnoc, + 'wooEmailCustomizerRetainfulCouponContent' + ), 10, 3 ); + //sent retainful additional short codes + add_filter( 'woo_email_drag_and_drop_builder_load_additional_shortcode', array( + $this->rnoc, + 'wooEmailCustomizerRegisterRetainfulShortCodes' + ), 10 ); + add_filter( 'woo_email_drag_and_drop_builder_load_additional_shortcode_data', array( + $this->rnoc, + 'wooEmailCustomizerRetainfulShortCodesValues' + ), 10, 3 ); + add_filter( 'wp_footer', array( $this->rnoc, 'showAppliedCouponPopup' ) ); + } + /** + * Ip filtering + */ + $this->canActivateIPFilter(); + $is_app_connected = $this->admin->isAppConnected(); + $secret_key = $this->admin->getSecretKey(); + $app_id = $this->admin->getApiKey(); + + $run_installation_externally = $this->admin->runAbandonedCartExternally(); + if ( $run_installation_externally ) { + //If the user is old user then ask user to run abandoned cart to + /*$is_app_connected = $this->admin->isAppConnected(); + $secret_key = $this->admin->getSecretKey(); + $app_id = $this->admin->getApiKey();*/ + if ( $is_app_connected && ! empty( $secret_key ) && ! empty( $app_id ) ) { + add_action( 'rest_api_init', array( $this, 'registerSyncEndPoints' ) ); + if ( is_admin() ) { + add_action( 'wp_after_admin_bar_render', array( $this->admin, 'schedulePlanChecker' ) ); + } + /* + * Retainful abandoned cart api + */ + $cart = new Cart(); + $checkout = new Checkout(); + $need_referral_program = $this->admin->needReferralWidget(); + $need_popup_widget = $this->admin->needPopupWidget(); + if ( $this->admin->isProPlan() && ( $need_referral_program || $need_popup_widget ) ) { + if ( $need_referral_program ) { + $referral_program = new ReferralManagement(); + add_action( 'wp_footer', array( $referral_program, 'printReferralPopup' ) ); + $need_embeded_referral_program = $this->admin->needEmbededReferralWidget(); + if ( $need_embeded_referral_program ) { + add_action( 'woocommerce_account_dashboard', array( + $referral_program, + 'printEmbededReferralPopup' + ) ); + } + } + if ( $need_popup_widget ) { + $popup = new Popup(); + add_action( 'user_register', array( $popup, 'userRegister' ) ); + add_action( 'wp_login', array( $popup, 'userLogin' ), 10, 2 ); + add_action( 'wp_enqueue_scripts', array( $popup, 'addPopupScripts' ) ); + add_action( 'wp_footer', array( $popup, 'printPopup' ) ); + } + } + add_filter( 'script_loader_tag', array( $cart, 'addCloudFlareAttrScript' ), 10, 3 ); + //add_filter('clean_url', array($cart, 'uncleanUrl'), 10, 3); + //Sync the order by the scheduled events + add_action( 'retainful_sync_abandoned_cart_order', array( $checkout, 'syncOrderByScheduler' ), 1 ); + add_action( 'wp_ajax_rnoc_track_user_data', array( $cart, 'setCustomerData' ) ); + add_action( 'wp_ajax_nopriv_rnoc_track_user_data', array( $cart, 'setCustomerData' ) ); + add_action( 'wp_ajax_rnoc_ajax_get_encrypted_cart', array( $cart, 'ajaxGetEncryptedCart' ) ); + add_action( 'wp_ajax_nopriv_rnoc_ajax_get_encrypted_cart', array( $cart, 'ajaxGetEncryptedCart' ) ); + add_action( 'woocommerce_cart_loaded_from_session', array( $cart, 'handlePersistentCart' ) ); + //add_action('wp_login', array($cart, 'userLoggedIn')); + add_action( 'woocommerce_api_retainful', array( $cart, 'recoverUserCart' ) ); + add_action( 'wp_loaded', array( $cart, 'applyAbandonedCartCoupon' ) ); + add_action( 'woocommerce_removed_coupon', array( $cart, 'removeNextOrderCouponFromCart' ) ); + //Add tracking message + /*if (is_user_logged_in()) { + add_action('woocommerce_after_add_to_cart_button', array($cart, 'userGdprMessage'), 10); + add_action('woocommerce_before_shop_loop', array($cart, 'userGdprMessage'), 10); + }*/ + add_filter( 'woocommerce_checkout_fields', array( $cart, 'guestGdprMessage' ), 10, 1 ); + add_action( 'woocommerce_checkout_after_terms_and_conditions', array( $cart, 'guestTermGdprMessage' ) ); + add_action( 'wp_footer', array( $checkout, 'setRetainfulOrderData' ) ); + add_filter( 'rnoc_can_track_abandoned_carts', array( $cart, 'isZeroValueCart' ), 15, 2 ); + $cart_tracking_engine = $this->admin->getCartTrackingEngine(); + if ( $cart_tracking_engine == "php" ) { + //PHP tracking + add_action( 'woocommerce_after_calculate_totals', array( $cart, 'syncCartData' ) ); + } else { + //Js tracking + add_action( 'wp_footer', array( $cart, 'renderAbandonedCartTrackingDiv' ) ); + add_filter( 'woocommerce_add_to_cart_fragments', array( $cart, 'addToCartFragments' ) ); + } + add_action( 'wp_footer', array( $cart, 'printRefreshFragmentScript' ) ); + add_action( 'wp_enqueue_scripts', array( $cart, 'addCartTrackingScripts' ) ); + add_action( 'wp_authenticate', array( $cart, 'userLoggedOn' ) ); + add_action( 'user_register', array( $cart, 'userSignedUp' ) ); + add_action( 'wp_logout', array( $cart, 'userLoggedOut' ) ); + //Set order as recovered + // handle payment complete, from a direct gateway + //add_action('woocommerce_new_order', array($checkout, 'purchaseComplete')); + add_action( 'woocommerce_thankyou', array( $checkout, 'payPageOrderCompletion' ) ); + add_action( 'woocommerce_payment_complete', array( $checkout, 'paymentCompleted' ) ); + add_action( 'woocommerce_checkout_update_order_meta', array( $checkout, 'checkoutOrderProcessed' ) ); + add_action( 'woocommerce_store_api_checkout_update_order_meta', array( + $checkout, + 'apiCheckoutOrderProcessed' + ) ); + // add_filter('woocommerce_payment_successful_result', array($checkout, 'maybeUpdateOrderOnSuccessfulPayment'), 10, 2); + // handle updating Retainful order data after a successful payment, for certain gateways + add_action( 'woocommerce_order_status_changed', array( $checkout, 'orderStatusChanged' ), 15, 3 ); + // handle placed orders + add_action( 'woocommerce_order_status_changed', array( $checkout, 'orderUpdated' ), 11, 1 ); + //triggers when admin pdate the order + add_action( 'woocommerce_process_shop_order_meta', array( + $checkout, + 'OrderUpdatedShopBackend' + ), 50, 2 ); + + //add_action('woocommerce_update_order', array($checkout, 'orderUpdated'), 10, 1); + add_filter( 'woocommerce_webhook_http_args', array( $checkout, 'changeWebHookHeader' ), 10, 3 ); + //Todo: multi currency and multi lingual + //add_action('wp_login', array($this->abandoned_cart_api, 'userCartUpdated')); + if ( $this->admin->isAfterPayEnabled() ) { + new AfterPay(); + } + + } else { + if ( is_admin() ) { + $connect_txt = ( ! empty( $secret_key ) && ! empty( $app_id ) ) ? __( 'connect', RNOC_TEXT_DOMAIN ) : __( 're-connect', RNOC_TEXT_DOMAIN ); + $notice = '

' . sprintf( __( "Please %s with Retainful to track and manage abandoned carts. ", RNOC_TEXT_DOMAIN ), $connect_txt ) . '

'; + $this->showAdminNotice( $notice ); + } + } + } else { + //remove + } + //Premium check + add_action( 'rnocp_check_user_plan', array( $this, 'checkUserPlan' ) ); + do_action( 'rnoc_initiated' ); + if ( is_admin() ) { + $is_retainful_v2_0_1_migration_completed = get_option( 'is_retainful_v2_0_1_migration_completed', 0 ); + if ( ! $is_retainful_v2_0_1_migration_completed ) { + $this->migrationV201(); + } + $this->checkApi(); + } + } + + function canActivateIPFilter() { + $settings = $this->admin->getAdminSettings(); + if ( isset( $settings[ RNOC_PLUGIN_PREFIX . 'enable_ip_filter' ] ) && ! empty( $settings[ RNOC_PLUGIN_PREFIX . 'enable_ip_filter' ] ) && isset( $settings[ RNOC_PLUGIN_PREFIX . 'ignored_ip_addresses' ] ) && ! empty( $settings[ RNOC_PLUGIN_PREFIX . 'ignored_ip_addresses' ] ) ) { + $ip = $settings[ RNOC_PLUGIN_PREFIX . 'ignored_ip_addresses' ]; + if ( ! empty( $ip ) ) { + $ip_filter = new IpFiltering( $ip ); + add_filter( 'rnoc_is_cart_has_valid_ip', array( $ip_filter, 'trackAbandonedCart' ), 10, 2 ); + } + } + } + + /** + * Migration for 2.1.0 + */ + function migrationV201() { + $premium_settings = get_option( $this->admin->slug . '_premium' ); + $admin_settings = $this->admin->getAdminSettings(); + $admin_settings[ RNOC_PLUGIN_PREFIX . 'enable_ip_filter' ] = isset( $premium_settings[ RNOC_PLUGIN_PREFIX . 'enable_ip_filter' ] ) ? $premium_settings[ RNOC_PLUGIN_PREFIX . 'enable_ip_filter' ] : 0; + $admin_settings[ RNOC_PLUGIN_PREFIX . 'ignored_ip_addresses' ] = isset( $premium_settings[ RNOC_PLUGIN_PREFIX . 'ignored_ip_addresses' ] ) ? $premium_settings[ RNOC_PLUGIN_PREFIX . 'ignored_ip_addresses' ] : ''; + update_option( $this->admin->slug . '_settings', $admin_settings ); + update_option( 'is_retainful_v2_0_1_migration_completed', 1 ); + } + + /** + * Run when our plugin get deactivated + */ + function onPluginDeactivation() { + $this->removeAllScheduledActions(); + $this->admin->removeWebhook(); + } + + + /** + * Remove all actions without any knowledge + */ + function removeAllScheduledActions() { + $this->admin->removeFinishedHooks( 'rnoc_abandoned_clear_abandoned_carts' ); + $this->admin->removeFinishedHooks( 'rnoc_abandoned_cart_send_email' ); + $this->admin->removeFinishedHooks( 'rnocp_check_user_plan' ); + } + + /** + * check api is valid or not on 3 days once + */ + function checkApi() { + $last_checked = get_option( 'rnoc_last_plan_checked', null ); + if ( empty( $last_checked ) || ( current_time( 'timestamp' ) > intval( $last_checked ) + 259200 ) ) { + $this->checkUserPlan(); + } + } + + /** + * Check and update the user plan + */ + function checkUserPlan() { + $api_key = $this->admin->getApiKey(); + $secret_key = $this->admin->getSecretKey(); + if ( ! empty( $api_key ) && ! empty( $secret_key ) ) { + $api_obj = new RestApi(); + $store_data = array( + 'secret_key' => $api_obj->encryptData( $api_key, $secret_key ) + ); + $this->admin->isApiEnabled( $api_key, $secret_key, $store_data ); + } else { + $this->admin->updateUserAsFreeUser(); + } + $this->admin->removeFinishedHooks( 'rnocp_check_user_plan', 'publish' ); + } + + /** + * Insert default email template + * + * @param $table + */ + function insertDefaultEmailTemplate( $table ) { + ob_start(); + include( RNOC_PLUGIN_PATH . 'src/admin/templates/default-1.html' ); + $content = ob_get_clean(); + $email_body = addslashes( $content ); + ob_start(); + include( RNOC_PLUGIN_PATH . 'src/admin/templates/default-2.html' ); + $content1 = ob_get_clean(); + $email_body1 = addslashes( $content1 ); + ob_start(); + include( RNOC_PLUGIN_PATH . 'src/admin/templates/default-3.html' ); + $content2 = ob_get_clean(); + $email_body2 = addslashes( $content2 ); + global $wpdb; + $default_template = $wpdb->get_row( 'SELECT id FROM ' . $table . ' WHERE default_template = "1"' ); + if ( empty( $default_template ) ) { + $template_subject = "Hey {{customer_name}}!! You left something in your cart"; + $query = 'INSERT INTO `' . $table . '` ( subject, body, is_active, frequency, day_or_hour, default_template,template_name )VALUES ( "' . $template_subject . '","' . $email_body . '","1","1","Hours","1","initial"),( "' . $template_subject . '","' . $email_body1 . '","0","1","Hours","6","After 6 hours"),( "' . $template_subject . '","' . $email_body2 . '","0","1","Days","1","After 1 day")'; + $wpdb->query( $query ); + } + } + + /** + * Initiate the plugin + * @return Main + */ + public static function instance() { + return self::$init = ( self::$init == null ) ? new self() : self::$init; + } + + function removeDependentTables() { + } + + /** + * All tables required for retainful abandoned cart + * @return array + */ + function getAbandonedCartTables() { + return array( + RNOC_PLUGIN_PREFIX . 'abandoned_cart_history', + RNOC_PLUGIN_PREFIX . 'guest_abandoned_cart_history' + ); + } + + /** + * detect woocommerce have been deactivated + * + * @param $plugin + * @param $network_activation + */ + function detectPluginDeactivation( $plugin, $network_activation ) { + if ( in_array( $plugin, array( 'woocommerce/woocommerce.php' ) ) ) { + deactivate_plugins( plugin_basename( __FILE__ ) ); + //Todo - Deactivate this plugin + } + } + + /** + * Dependency check for our plugin + */ + function checkDependencies() { + if ( ! defined( 'WC_VERSION' ) ) { + $this->showAdminNotice( __( 'Woocommerce must be activated for Retainful-Woocommerce to work', RNOC_TEXT_DOMAIN ) ); + } else { + if ( version_compare( WC_VERSION, '2.5', '<' ) ) { + $this->showAdminNotice( __( 'Your woocommerce version is ', RNOC_TEXT_DOMAIN ) . WC_VERSION . __( '. Some of the features of Retainful-Woocommerce will not work properly on this woocommerce version.', RNOC_TEXT_DOMAIN ) ); + } + } + if ( is_admin() ) { + $this->doMigration(); + } + } + + /** + * Migrate data required for v 1.1.3 + */ + function doMigration() { + $is_migrated = get_option( 'retainful_v_1_1_3_migration_completed', 0 ); + if ( ! $is_migrated ) { + $slug = $this->admin->slug; + $retainful_page = get_option( $slug, array() ); + $licence_page = get_option( $slug . '_license', array() ); + $usage_restriction_page = get_option( $slug . '_usage_restriction', array() ); + if ( empty( $licence_page ) ) { + $licence_data = array( + RNOC_PLUGIN_PREFIX . 'is_retainful_connected' => ( isset( $retainful_page[ RNOC_PLUGIN_PREFIX . 'is_retainful_connected' ] ) ) ? $retainful_page[ RNOC_PLUGIN_PREFIX . 'is_retainful_connected' ] : 0, + RNOC_PLUGIN_PREFIX . 'retainful_app_id' => ( isset( $retainful_page[ RNOC_PLUGIN_PREFIX . 'retainful_app_id' ] ) ) ? $retainful_page[ RNOC_PLUGIN_PREFIX . 'retainful_app_id' ] : '', + RNOC_PLUGIN_PREFIX . 'retainful_app_secret' => ( isset( $retainful_page[ RNOC_PLUGIN_PREFIX . 'retainful_app_secret' ] ) ) ? $retainful_page[ RNOC_PLUGIN_PREFIX . 'retainful_app_secret' ] : '' + ); + update_option( $slug . '_license', $licence_data ); + } + unset( $retainful_page[ RNOC_PLUGIN_PREFIX . 'is_retainful_connected' ], $retainful_page[ RNOC_PLUGIN_PREFIX . 'retainful_app_id' ] ); + $retainful_data = array_merge( $retainful_page, $usage_restriction_page ); + update_option( $slug, $retainful_data ); + delete_option( $slug . '_usage_restriction' ); + $abandoned_cart_data = get_option( $slug . '_abandoned_cart_settings', array() ); + update_option( $slug . '_settings', $abandoned_cart_data ); + delete_option( $slug . '_abandoned_cart_settings' ); + update_option( 'retainful_v_1_1_3_migration_completed', 1 ); + } + } + + /** + * Show notices for user..if anything unusually happen in our plugin + * + * @param string $message - message to notice users + */ + function showAdminNotice( $message = "" ) { + if ( ! empty( $message ) ) { + add_action( 'admin_notices', function () use ( $message ) { + echo '

' . $message . '

'; + } ); + } + } } \ No newline at end of file From 0c1e718409842b122e7b607c6937a478965058bf Mon Sep 17 00:00:00 2001 From: Alagesan Date: Tue, 9 Jul 2024 14:16:13 +0530 Subject: [PATCH 2/2] - readme --- readme.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readme.txt b/readme.txt index 7c0941ad..1224611d 100644 --- a/readme.txt +++ b/readme.txt @@ -1,6 +1,6 @@ === Email Marketing for WooCommerce by Retainful - WooCommerce Abandoned Cart Recovery, Email Campaigns, Newsletters, Signup Forms, Popups and Email Automations === Contributors: retainful -Tags: email marketing, newsletter, woocommerce, marketing, popups, abandoned cart +Tags: abandoned cart, email marketing, newsletter, forms, popups Requires at least: 4.6.1 Tested up to: 6.5 WC tested up to: 9.0 @@ -9,7 +9,7 @@ Stable tag: 2.6.35 License: GPLv3 or later License URI: http://www.gnu.org/licenses/gpl-3.0.html -Grow revenue with Email Marketing, Newsletters, Email Marketing Automation, Optin forms, popups, WooCommerce Abandoned Cart recovery with Retainful +Grow revenue with WooCommerce Abandoned Cart recovery, Email campaigns, Newsletters, Email Marketing Automation, Optin forms, popups with Retainful == Description ==