JezK
Edit File: class-cartflows-pro-checkout-rules.php
<?php /** * Cartflows Checkout Rules * * @package cartflows */ if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } /** * Class Cartflows_Pro_Checkout_Rules. */ class Cartflows_Pro_Checkout_Rules { /** * Member Variable * * @var instance */ private static $instance; /** * Member Variable * * @var order_data */ private static $order_data = array(); /** * Member Variable * * @var order_data */ private static $dynamic_step_id = null; /** * Initiator */ public static function get_instance() { if ( ! isset( self::$instance ) ) { self::$instance = new self(); } return self::$instance; } /** * Constructor */ public function __construct() { add_filter( 'cartflows_checkout_next_step_id', array( $this, 'conditional_redirection_after_checkout' ), 10, 3 ); } /** * Conditional redirection. * * @param string $next_step_id next step. * @param object $order order data. * @param int $checkout_id id. */ public function conditional_redirection_after_checkout( $next_step_id, $order, $checkout_id ) { wcf()->logger->log( 'Start - ' . __CLASS__ . '::' . __FUNCTION__ ); wcf()->logger->log( 'Before returning next step: ' . $next_step_id ); $cache = true; if ( null === self::$dynamic_step_id ) { $is_rules_enabled = wcf()->options->get_checkout_meta_value( $checkout_id, 'wcf-checkout-rules-option' ); if ( 'no' === $is_rules_enabled ) { $step_id = $next_step_id; $cache = false; } else { $this->set_order_data( $order ); $group_result = false; $conditions = wcf()->options->get_checkout_meta_value( $checkout_id, 'wcf-checkout-rules' ); foreach ( $conditions as $group_index => $group_data ) { if ( ! empty( $group_data['rules'] ) ) { $group_result = $this->get_group_rules_result( $group_data['rules'], $order ); // If group result is true then return step_id. if ( $group_result ) { $step_id = intval( $group_data['step_id'] ); $next_step_id = ! empty( $group_data['step_id'] ) && get_post_status( $step_id ) ? $group_data['step_id'] : $next_step_id; wcf()->logger->log( 'Group Result is true, Next Step ID : ' . $next_step_id ); break; } } } // If all group are false then refirect to step selected in default step field. if ( false === $group_result ) { $default_step = wcf()->options->get_checkout_meta_value( $checkout_id, 'wcf-checkout-rules-default-step' ); if ( ! empty( $default_step ) ) { $next_step_id = $default_step; } elseif ( wcf()->utils->check_is_offer_page( $next_step_id ) ) { // No explicit default configured and the natural next step is an offer page. // Use the same offer-skip pattern as the unsupported-gateway block below // to land on the first non-offer step in the flow. $wcf_step_obj = wcf_pro_get_step( $next_step_id ); $step_to_redirect = $wcf_step_obj->get_next_step_id(); $next_step_id = $wcf_step_obj->get_next_step_from_given_step( $next_step_id, $step_to_redirect ); if ( wcf()->utils->check_is_offer_page( $next_step_id ) ) { $next_step_id = $wcf_step_obj->get_next_step_from_given_step( $next_step_id, $step_to_redirect ); } } wcf()->logger->log( 'Group Result is false, Next Step: ' . $next_step_id ); } self::$order_data = null; $step_id = $next_step_id; } } $step_id = null !== self::$dynamic_step_id ? self::$dynamic_step_id : $step_id; // Current step is offer step but the supported gateway is not used then don't redirect to the offer step. if ( wcf()->utils->check_is_offer_page( $step_id ) && ! $this->is_supported_gateway_used( $order ) ) { $order_gateway = ! empty( $order->get_payment_method() ) ? $order->get_payment_method() : __( 'No gateway found', 'cartflows-pro' ); wcf()->logger->log( 'Skipping the dynamic condition for ' . $step_id . ' step. Reason: The gateway "' . $order_gateway . '" is not in the supported for upsell/downsell offers.' ); // Get the next step for the current step. $wcf_step_obj = wcf_pro_get_step( $step_id ); $step_to_redirect = $wcf_step_obj->get_next_step_id(); /* Get next step redirect URL */ $step_id = $wcf_step_obj->get_next_step_from_given_step( $step_id, $step_to_redirect ); // Skip all of the next offers steps in the flow till it reaches to the thank yo page. if ( wcf()->utils->check_is_offer_page( $step_id ) ) { $step_id = $wcf_step_obj->get_next_step_from_given_step( $step_id, $step_to_redirect ); } } if ( wcf()->utils->check_is_offer_page( $step_id ) ) { $step_id = Cartflows_Pro_Base_Offer_Markup::get_instance()->maybe_skip_offer( $step_id, $order ); } if ( $cache ) { self::$dynamic_step_id = $step_id; } wcf()->logger->log( 'Final returning next step: ' . $step_id ); wcf()->logger->log( 'End - ' . __CLASS__ . '::' . __FUNCTION__ ); return $step_id; } /** * Set order data. * * @param object $order order data. */ public function set_order_data( $order ) { $order_items = $order->get_items(); $products_ids = array(); foreach ( $order_items as $item_id => $item ) { $product_id = $item->get_product_id(); $variation_id = $item->get_variation_id(); $products_ids[] = ! empty( $variation_id ) ? $variation_id : $product_id; } self::$order_data['product_ids'] = $products_ids; self::$order_data['order_obj'] = $order; } /** * Check if the supported payment gateway is used on the checkout page or not. * * @param WC_Order $order The Current order object. * @since x.x.x * @return bool $is_supported Is supported gateway used or not */ public function is_supported_gateway_used( $order ) { $is_supported = false; $order_gateway = $order->get_payment_method(); $supported_gateways = array_keys( Cartflows_Pro_Gateways::get_instance()->get_supported_gateways() ); if ( in_array( $order_gateway, $supported_gateways, true ) ) { $is_supported = true; } // Check for supported payment methods of Stripe Payment Element and exclude unsupported ones. if ( 'cpsw_stripe_element' === $order_gateway ) { $payment_method = $order->get_meta( 'wcf_cpsw_selected_payment_type', true ); // Define an extensible array of supported payment methods for Stripe Payment Element. $supported_payment_methods = array( 'card' ); if ( $payment_method && ! in_array( $payment_method, $supported_payment_methods, true ) ) { $is_supported = false; } } return $is_supported; } /** * Get Group result. * * @param array $rules rules. */ public function get_group_rules_result( $rules ) { $result = true; foreach ( $rules as $rule_index => $rule_data ) { $operator = $rule_data['operator']; $value = $rule_data['value']; switch ( $rule_data['condition'] ) { case 'cart_item': $result = $this->compare_string_values( self::$order_data['product_ids'], $value, $operator ); break; case 'cart_item_category': $item_terms = $this->get_order_items_categories(); $result = $this->compare_string_values( $item_terms, $value, $operator ); break; case 'cart_item_tag': $item_terms = $this->get_order_items_tags(); $result = $this->compare_string_values( $item_terms, $value, $operator ); break; case 'cart_total': $order_total = $this->get_order_total(); $result = $this->compare_number_values( $order_total, $value, $operator ); break; case 'cart_coupons': $coupon_used = $this->get_order_coupons(); $coupon_used = ! empty( $coupon_used ) && is_array( $coupon_used ) ? array_map( 'strtolower', $coupon_used ) : $coupon_used; $value = ! empty( $value ) && is_array( $value ) ? array_map( 'strtolower', $value ) : $value; $result = $this->compare_string_values( $coupon_used, $value, $operator ); break; case 'cart_shipping_method': $shipping_methods = $this->get_order_shipping_method(); $result = $this->compare_string_values( $shipping_methods, $value, $operator ); break; case 'cart_shipping_country': $shipping_country = $this->get_order_shipping_country(); $result = $this->compare_string_values( $shipping_country, $value, $operator ); break; case 'cart_billing_country': $billing_country = $this->get_order_billing_country(); $result = $this->compare_string_values( $billing_country, $value, $operator ); break; case 'cart_payment_method': $payment_method = $this->get_order_payment_method(); $result = $this->compare_string_values( $payment_method, $value, $operator ); break; case 'order_custom_field': $meta_key_to_check = $this->get_order_custom_meta_key_value( $value ); $meta_value = is_array( $value ) && isset( $value['meta_value'] ) ? array( $value['meta_value'] ) : $value; $result = $this->compare_string_values( $meta_key_to_check, $meta_value, $operator ); break; case 'user_login_status': $login_status = $this->get_customer_login_status(); $result = $this->compare_string_values( $login_status, $value, $operator ); break; case 'user_role': $customer_roles = $this->get_customer_roles(); $result = $this->compare_string_values( $customer_roles, $value, $operator ); break; default: $result = false; } // If one of rule is false break the loop. if ( false === $result ) { break; } } return $result; } /** * Get order item categories. */ public function get_order_items_categories() { $item_terms = array(); if ( isset( self::$order_data['item_categories'] ) ) { $item_terms = self::$order_data['item_categories']; } else { $products_ids = self::$order_data['product_ids']; foreach ( $products_ids as $index => $product_id ) { // Get Product object. $product = wc_get_product( $product_id ); /** * Check the type of the product. * * This condition is added to get the product categories. * As varation of a product is getting purchased and it does not contains the categories. */ if ( $product->is_type( 'variation' ) ) { $product_id = $product->get_parent_id(); } $cat_terms = wp_get_object_terms( $product_id, 'product_cat', array( 'fields' => 'ids' ) ); $item_terms = array_merge( $item_terms, $cat_terms ); } self::$order_data['item_categories'] = $item_terms; } return $item_terms; } /** * Get order item tags. */ public function get_order_items_tags() { $item_terms = array(); if ( isset( self::$order_data['item_tags'] ) ) { $item_terms = self::$order_data['item_tags']; } else { $products_ids = self::$order_data['product_ids']; foreach ( $products_ids as $index => $product_id ) { // Get Product object. $product = wc_get_product( $product_id ); /** * Check the type of the product. * * This condition is added to get the product categories. * As varation of a product is getting purchased and it does not contains the tags. */ if ( $product->is_type( 'variation' ) ) { $product_id = $product->get_parent_id(); } $tag_terms = wp_get_object_terms( $product_id, 'product_tag', array( 'fields' => 'ids' ) ); $item_terms = array_merge( $item_terms, $tag_terms ); } self::$order_data['item_tags'] = $item_terms; } return $item_terms; } /** * Get order shipping method. */ public function get_order_shipping_method() { $shipping_methods = array(); if ( isset( self::$order_data['shipping_method'] ) ) { $shipping_methods = self::$order_data['shipping_method']; } else { foreach ( self::$order_data['order_obj']->get_shipping_methods() as $method ) { // extract method slug only, discard instance id. $split = strpos( $method['method_id'], ':' ); if ( $split ) { $shipping_methods[] = substr( $method['method_id'], 0, $split ); } else { $shipping_methods[] = $method['method_id']; } } self::$order_data['shipping_method'] = $shipping_methods; } return $shipping_methods; } /** * Get order coupons. */ public function get_order_coupons() { $coupon_used = array(); if ( isset( self::$order_data['coupon_used'] ) ) { $coupon_used = self::$order_data['coupon_used']; } else { $coupon_used = self::$order_data['order_obj']->get_used_coupons(); self::$order_data['coupon_used'] = $coupon_used; } return $coupon_used; } /** * Get order total. */ public function get_order_total() { $order_total = array(); if ( isset( self::$order_data['order_total'] ) ) { $order_total = self::$order_data['order_total']; } else { $order_total = self::$order_data['order_obj']->get_total(); self::$order_data['order_total'] = $order_total; } return $order_total; } /** * Get order billing country. */ public function get_order_billing_country() { $billing_country = array(); if ( isset( self::$order_data['billing_country'] ) ) { $billing_country = self::$order_data['billing_country']; } else { $billing_country = array( self::$order_data['order_obj']->get_billing_country() ); self::$order_data['billing_country'] = $billing_country; } return $billing_country; } /** * Get order shipping country. */ public function get_order_shipping_country() { $shipping_country = array(); if ( isset( self::$order_data['shipping_country'] ) ) { $shipping_country = self::$order_data['shipping_country']; } else { $shipping_country = array( self::$order_data['order_obj']->get_shipping_country() ); self::$order_data['shipping_country'] = $shipping_country; } return $shipping_country; } /** * Get order payment method. */ public function get_order_payment_method() { $payment_method = array(); if ( isset( self::$order_data['payment_method'] ) ) { $payment_method = self::$order_data['payment_method']; } else { $payment_method = array( self::$order_data['order_obj']->get_payment_method() ); self::$order_data['payment_method'] = $payment_method; } return $payment_method; } /** * Get custom field value from the order. * * @param array $value array of values. */ public function get_order_custom_meta_key_value( $value ) { $order_custom_field = array(); $meta_key = is_array( $value ) && isset( $value['meta_key'] ) ? '_' . $value['meta_key'] : $value; if ( self::$order_data['order_obj'] ) { $order_custom_field = array( self::$order_data['order_obj']->get_meta( $meta_key, true ) ); } return $order_custom_field; } /** * Get customer login status. * * @since 2.2.3 * @return array */ public function get_customer_login_status() { if ( isset( self::$order_data['login_status'] ) ) { return self::$order_data['login_status']; } $login_status = is_user_logged_in() ? array( 'logged_in' ) : array( 'not_logged_in' ); self::$order_data['login_status'] = $login_status; return $login_status; } /** * Get customer roles. * * @since 2.2.3 * @return array */ public function get_customer_roles() { if ( isset( self::$order_data['customer_roles'] ) ) { return self::$order_data['customer_roles']; } $customer_id = self::$order_data['order_obj']->get_customer_id(); $customer_roles = array(); if ( $customer_id > 0 ) { $user = get_userdata( $customer_id ); if ( $user ) { $customer_roles = $user->roles; } } else { $customer_roles = array( 'guest' ); } self::$order_data['customer_roles'] = $customer_roles; return $customer_roles; } /** * Compare string values. * * @param array $order_values order values. * @param array $rule_values rules values. * @param object $operator rule operator. */ public function compare_string_values( $order_values, $rule_values, $operator ) { switch ( $operator ) { case 'all': $result = count( array_intersect( $rule_values, $order_values ) ) === count( $rule_values ); break; case 'any': $result = count( array_intersect( $rule_values, $order_values ) ) >= 1; break; case 'none': $result = ( count( array_intersect( $rule_values, $order_values ) ) === 0 ); break; case 'exist': $result = ( count( $order_values ) >= 1 ); break; case 'not_exist': $result = ( count( $order_values ) === 0 ); break; case '===': $result = count( array_intersect( $rule_values, $order_values ) ) === 1; break; case '!==': $result = count( array_intersect( $rule_values, $order_values ) ) === 0; break; case 'is': $result = count( array_intersect( (array) $rule_values, (array) $order_values ) ) >= 1; break; case 'is_not': $result = count( array_intersect( (array) $rule_values, (array) $order_values ) ) === 0; break; default: $result = false; break; } return $result; } /** * Compare string values. * * @param string $order_value order values. * @param array $rule_value rules values. * @param string $operator rule operator. */ public function compare_number_values( $order_value, $rule_value, $operator ) { // Convert the encoded operator at the time of sanitizing back to HTML entity for comparison. $operator = htmlspecialchars_decode( $operator ); switch ( $operator ) { case '==': $result = $order_value === $rule_value; break; case '!=': $result = $order_value !== $rule_value; break; case '>': $result = $order_value > $rule_value; break; case '<': $result = $order_value < $rule_value; break; case '<=': $result = $order_value <= $rule_value; break; case '>=': $result = $order_value >= $rule_value; break; default: $result = false; break; } return $result; } } /** * Prepare if class 'Cartflows_Pro_Checkout_Rules' exist. * Kicking this off by calling 'get_instance()' method */ Cartflows_Pro_Checkout_Rules::get_instance();