[ Avaa Bypassed ]




Upload:

Command:

hmhc3928@18.117.152.156: ~ $
<?php
/**
 * Manage Checkout
 *
 * @package Tutor\Ecommerce
 * @author Themeum
 * @link https://themeum.com
 * @since 3.0.0
 */

namespace Tutor\Ecommerce;

use Tutor\Helpers\ValidationHelper;
use TUTOR\Input;
use Tutor\Models\BillingModel;
use Tutor\Traits\JsonResponse;
use Tutor\Models\CartModel;
use Tutor\Models\CouponModel;
use Tutor\Models\OrderModel;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Checkout Controller class
 *
 * @since 3.0.0
 */
class CheckoutController {

	use JsonResponse;

	/**
	 * Page slug for checkout page
	 *
	 * @since 3.0.0
	 *
	 * @var string
	 */
	const PAGE_SLUG = 'checkout';

	/**
	 * Page slug for checkout page
	 *
	 * @since 3.0.0
	 *
	 * @var string
	 */
	const PAGE_ID_OPTION_NAME = 'tutor_checkout_page_id';

	/**
	 * Pay now error transient key
	 *
	 * @since 3.0.0
	 *
	 * @var string
	 */
	const PAY_NOW_ERROR_TRANSIENT_KEY = 'tutor_pay_now_errors_';

	/**
	 * Pay now alert transient key
	 *
	 * @since 3.0.0
	 *
	 * @var string
	 */
	const PAY_NOW_ALERT_MSG_TRANSIENT_KEY = 'tutor_pay_now_alert_msg_';

	/**
	 * Coupon model instance.
	 *
	 * @since 3.0.0
	 *
	 * @var CouponModel
	 */
	public $coupon_model;

	/**
	 * Constructor.
	 *
	 * Initializes the Checkout class, sets the page title, and optionally registers
	 * hooks for handling AJAX requests related to cart data, bulk actions, cart updates,
	 * and cart deletions.
	 *
	 * @param bool $register_hooks Whether to register hooks for handling requests. Default is true.
	 *
	 * @since 3.0.0
	 *
	 * @return void
	 */
	public function __construct( $register_hooks = true ) {
		$this->coupon_model = new CouponModel();

		if ( $register_hooks ) {
			add_action( 'tutor_action_tutor_pay_now', array( $this, 'pay_now' ) );
			add_action( 'tutor_action_tutor_pay_incomplete_order', array( $this, 'pay_incomplete_order' ) );
			add_action( 'template_redirect', array( $this, 'restrict_checkout_page' ) );
			add_action( 'wp_ajax_tutor_get_checkout_html', array( $this, 'ajax_get_checkout_html' ) );
		}
	}

	/**
	 * Get cart page url
	 *
	 * @since 3.0.0
	 *
	 * @return string
	 */
	public static function get_page_url() {
		return get_post_permalink( self::get_page_id() );
	}

	/**
	 * Get cart page ID
	 *
	 * @since 3.0.0
	 *
	 * @return string
	 */
	public static function get_page_id() {
		return (int) tutor_utils()->get_option( self::PAGE_ID_OPTION_NAME );
	}

	/**
	 * Create checkout page
	 *
	 * @since 3.0.0
	 *
	 * @return void
	 */
	public static function create_checkout_page() {
		$page_id = self::get_page_id();
		if ( ! $page_id ) {
			$args = array(
				'post_title'   => ucfirst( self::PAGE_SLUG ),
				'post_content' => '',
				'post_type'    => 'page',
				'post_status'  => 'publish',
			);

			$page_id = wp_insert_post( $args );
			tutor_utils()->update_option( self::PAGE_ID_OPTION_NAME, $page_id );
		}
	}

	/**
	 * Get checkout HTML
	 *
	 * @since 3.0.0
	 *
	 * @return void
	 */
	public function ajax_get_checkout_html() {
		tutor_utils()->check_nonce();

		ob_start();
		tutor_load_template( 'ecommerce/checkout-details' );
		$content = ob_get_clean();

		$this->json_response(
			__( 'Success', 'tutor' ),
			$content
		);
	}

	/**
	 * Prepare items
	 *
	 * @since 3.0.0
	 *
	 * @param array       $item_ids items.
	 * @param string      $order_type order type.
	 * @param object|null $coupon coupon.
	 *
	 * @return array
	 */
	private function prepare_items( $item_ids, $order_type = OrderModel::TYPE_SINGLE_ORDER, $coupon = null ) {
		$items     = array();
		$plan_info = null;

		foreach ( $item_ids as $item_id ) {
			$item_name    = get_the_title( $item_id );
			$course_price = tutor_utils()->get_raw_course_price( $item_id );
			if ( OrderModel::TYPE_SINGLE_ORDER !== $order_type ) {
				$plan_info = apply_filters( 'tutor_get_plan_info', null, $item_id );
				if ( $plan_info ) {
					$item_name                   = $plan_info->plan_name;
					$course_price->regular_price = $plan_info->regular_price;
					$course_price->sale_price    = $plan_info->in_sale_price ? $plan_info->sale_price : 0;
				}
			}

			$regular_price = $course_price->regular_price;
			$sale_price    = $course_price->sale_price;

			$item = array(
				'item_id'           => (int) $item_id,
				'item_name'         => $item_name,
				'regular_price'     => $regular_price,
				'sale_price'        => $sale_price ? $sale_price : null,
				'is_coupon_applied' => false,
				'coupon_code'       => null,
			);

			$is_coupon_applicable = false;
			if ( Settings::is_coupon_usage_enabled() && is_object( $coupon ) ) {
				$is_coupon_applicable = ! $sale_price && $this->coupon_model->is_coupon_applicable( $coupon, $item_id );
				if ( $is_coupon_applicable ) {
					$item['is_coupon_applied'] = $is_coupon_applicable;
					$item['coupon_code']       = $coupon->coupon_code;
				}
			}

			$items[] = $item;
		}

		return array( $items, $plan_info );
	}

	/**
	 * Calculate discount.
	 *
	 * @since 3.0.0
	 *
	 * @param array  $items item array.
	 * @param string $discount_type discount type. like percentage or fixed.
	 * @param float  $discount_value value of discount.
	 *
	 * @return array
	 */
	public function calculate_discount( $items, $discount_type, $discount_value ) {
		// Filter products without a sale price.
		$items_without_sale = array_filter(
			$items,
			function( $item ) {
				return empty( $item['sale_price'] );
			}
		);

		// Calculate the total regular price of products without a sale price.
		$total_regular_price = array_sum( array_column( $items_without_sale, 'regular_price' ) );

		$final = array();
		foreach ( $items as $item ) {
			// If product already has a sale price, no discount is applied.
			if ( ! empty( $item['sale_price'] ) || ( isset( $item['is_coupon_applied'] ) && ! $item['is_coupon_applied'] ) ) {
				$item['discount_amount'] = 0;
				$final[]                 = $item;
			} else {
				if ( 'percentage' === $discount_type ) {
					// Apply percentage discount.
					$discount       = $item['regular_price'] * ( $discount_value / 100 );
					$discount_price = $item['regular_price'] - $discount;

					$item['discount_amount'] = round( $discount, 2 );
					$item['discount_price']  = $discount_price;

				} elseif ( 'flat' === $discount_type && $total_regular_price > 0 ) {
					// Apply a proportional fixed discount based on the regular price.
					$proportion     = $item['regular_price'] / $total_regular_price;
					$discount       = $discount_value * $proportion;
					$discount_price = $item['regular_price'] - $discount;

					$item['discount_amount'] = round( $discount, 2 );
					$item['discount_price']  = round( $discount_price, 2 );
				}
				$final[] = $item;
			}
		}

		return $final;
	}

	/**
	 * Prepare checkout item with applying coupon if required.
	 *
	 * @since 3.0.0
	 *
	 * @param int|array $item_ids Required, course ids or plan id.
	 * @param string    $order_type order type.
	 * @param string    $coupon_code coupon code.
	 *
	 * @return object
	 */
	public function prepare_checkout_items( $item_ids, $order_type = OrderModel::TYPE_SINGLE_ORDER, $coupon_code = null ) {
		$item_ids = is_array( $item_ids ) ? $item_ids : array( $item_ids );
		$response = array();

		$coupon_type       = empty( $coupon_code ) ? 'automatic' : 'manual';
		$is_coupon_applied = false;
		$coupon_title      = '';

		$total_price     = 0;
		$subtotal_price  = 0;
		$coupon_discount = 0;
		$sale_discount   = 0;

		$coupon                  = null;
		$is_coupon_applied       = false;
		$is_meet_min_requirement = false;
		$selected_coupon         = null;

		if ( Settings::is_coupon_usage_enabled() ) {
			$selected_coupon = $this->coupon_model->get_coupon_details_for_checkout( $coupon_code );
		}

		$is_valid = is_object( $selected_coupon ) && $this->coupon_model->is_coupon_valid( $selected_coupon );
		if ( $is_valid ) {
			$is_meet_min_requirement = $this->coupon_model->is_coupon_requirement_meet( $item_ids, $selected_coupon, $order_type );
			if ( $is_meet_min_requirement ) {
				$coupon            = $selected_coupon;
				$is_coupon_applied = true;
			}
		}

		list($items, $plan_info) = $this->prepare_items( $item_ids, $order_type, $coupon );

		if ( $is_coupon_applied ) {
			$items        = $this->calculate_discount( $items, $coupon->discount_type, $coupon->discount_amount );
			$coupon_title = $coupon->coupon_title;
		}

		// Keep calculated price for each item.
		foreach ( $items as $item ) {
			$discount_amount        = isset( $item['discount_amount'] ) ? $item['discount_amount'] : 0;
			$has_discount_amount    = $discount_amount > 0;
			$item['discount_price'] = $has_discount_amount ? max( 0, $item['discount_price'] ) : null;

			$display_price         = isset( $item['sale_price'] ) ? $item['sale_price'] : $item['regular_price'];
			$display_price         = $has_discount_amount ? $item['discount_price'] : $display_price;
			$item['display_price'] = $display_price;

			$sale_discount_amount         = isset( $item['sale_price'] ) ? $item['regular_price'] - $item['sale_price'] : 0;
			$item['sale_discount_amount'] = $sale_discount_amount;

			$response['items'][] = (object) $item;

			$subtotal_price  += $item['regular_price'];
			$coupon_discount += $discount_amount;
			$sale_discount   += $sale_discount_amount;
		}

		if ( $plan_info && $plan_info->enrollment_fee > 0 ) {
			$subtotal_price += $plan_info->enrollment_fee;
		}

		$total_price = $subtotal_price - ( $coupon_discount + $sale_discount );
		$tax_rate    = Tax::get_user_tax_rate();
		$tax_amount  = Tax::calculate_tax( $total_price, $tax_rate );

		$total_price_without_tax = $total_price;
		if ( ! Tax::is_tax_included_in_price() ) {
			$total_price += $tax_amount;
		}

		// Total price should not negative.
		$total_price = max( 0, $total_price );

		$response['coupon_type']       = $coupon_type;
		$response['coupon_title']      = $coupon_title;
		$response['is_coupon_applied'] = $is_coupon_applied;

		$response['subtotal_price']          = $subtotal_price;
		$response['coupon_discount']         = $coupon_discount;
		$response['sale_discount']           = $sale_discount;
		$response['tax_rate']                = $tax_rate;
		$response['total_price_without_tax'] = $total_price_without_tax;
		$response['tax_amount']              = $tax_amount;
		$response['total_price']             = $total_price;

		return (object) $response;
	}

	/**
	 * Pay now ajax handler
	 * Create pending order, prepare payment data & proceed to payment gateway
	 *
	 * @since 3.0.0
	 *
	 * @return void
	 */
	public function pay_now() {
		tutor_utils()->check_nonce();

		$errors     = array();
		$order_data = null;

		$billing_model   = new BillingModel();
		$current_user_id = get_current_user_id();
		$request = Input::sanitize_array( $_POST ); //phpcs:ignore --sanitized.

		$billing_fillable_fields = array_intersect_key( $request, array_flip( $billing_model->get_fillable_fields() ) );

		$order_payment_fields = array(
			'object_ids',
			'coupon_code',
			'payment_method',
			'payment_type',
			'order_type',
		);

		$request = array_intersect_key( $request, array_flip( $order_payment_fields ) );
		// Set required.
		foreach ( $order_payment_fields as $field ) {
			if ( ! isset( $request[ $field ] ) ) {
				$request[ $field ] = '';
			}
		}

		// Validate data.
		$validate = $this->validate_pay_now_req( $request );

		if ( ! $validate->success ) {
			foreach ( $validate->errors as $error ) {
				if ( is_array( $error ) ) {
					foreach ( $error as $err ) {
						array_push( $errors, $err );
					}
				} else {
					array_push( $errors, $error );
				}
			}
		}

		// Return if validation failed.
		if ( ! empty( $errors ) ) {
			set_transient( self::PAY_NOW_ERROR_TRANSIENT_KEY . $current_user_id, $errors );
			return;
		}

		$object_ids     = array_filter( explode( ',', $request['object_ids'] ), 'is_numeric' );
		$coupon_code    = isset( $request['coupon_code'] ) ? $request['coupon_code'] : '';
		$payment_method = $request['payment_method'];
		$payment_type   = $request['payment_type'];
		$order_type     = $request['order_type'];

		if ( empty( $object_ids ) ) {
			array_push( $errors, __( 'Invalid cart items', 'tutor' ) );
		}

		// if ( ! Ecommerce::is_payment_gateway_configured( $payment_method ) ) {
		// array_push( $errors, Ecommerce::get_incomplete_payment_setup_error_message( $payment_method ) );
		// }

		$billing_info = $billing_model->get_info( $current_user_id );
		if ( $billing_info ) {
			$update_billing = $billing_model->update( $billing_fillable_fields, array( 'user_id' => $current_user_id ) );
			if ( ! $update_billing ) {
				array_push( $errors, __( 'Billing information update failed!', 'tutor' ) );
			}
		} else {
			// Save billing info.
			$billing_fillable_fields['user_id'] = $current_user_id;

			$save = $billing_model->insert( $billing_fillable_fields );
			if ( ! $save ) {
				array_push( $errors, __( 'Billing info save failed!', 'tutor' ) );
			}
		}

		$checkout_data = $this->prepare_checkout_items( $object_ids, $order_type, $coupon_code );
		$items         = array();
		foreach ( $checkout_data->items as $item ) {
			$items[] = array(
				'item_id'        => $item->item_id,
				'regular_price'  => $item->regular_price,
				'sale_price'     => $item->sale_price,
				'discount_price' => $item->discount_price,
				'coupon_code'    => $item->is_coupon_applied ? $item->coupon_code : null,
			);
		}

		$args = array(
			'payment_method'  => $payment_method,
			'coupon_amount'   => $checkout_data->coupon_discount,
			'discount_amount' => $checkout_data->sale_discount,
		);

		if ( empty( $errors ) ) {
			$order_data = ( new OrderController( false ) )->create_order( $current_user_id, $items, OrderModel::PAYMENT_UNPAID, $order_type, $coupon_code, $args, false );
			if ( ! empty( $order_data ) ) {
				if ( 'automate' === $payment_type ) {
					try {
						$payment_data = self::prepare_payment_data( $order_data );
						$this->proceed_to_payment( $payment_data, $payment_method, $order_type );
					} catch ( \Throwable $th ) {
						tutor_log( $th );
						tutor_redirect_after_payment( OrderModel::ORDER_PLACEMENT_FAILED, $order_data['id'], $th->getMessage() );
					}
				} else {
					// Set alert message session.
					$this->set_pay_now_alert_msg( $order_data );
					tutor_redirect_after_payment( OrderModel::ORDER_PLACEMENT_SUCCESS, $order_data['id'] );
				}
			} else {
				array_push( $errors, __( 'Failed to place order!', 'tutor' ) );
				set_transient( self::PAY_NOW_ERROR_TRANSIENT_KEY . $current_user_id, $errors );
				$this->set_pay_now_alert_msg( $order_data );
			}
		} else {
			set_transient( self::PAY_NOW_ERROR_TRANSIENT_KEY . $current_user_id, $errors );
			$this->set_pay_now_alert_msg( $order_data );
		}
	}

	/**
	 * Prepare payment data
	 *
	 * @since 3.0.0
	 *
	 * @param array $order Order object.
	 *
	 * @return mixed
	 */
	public static function prepare_payment_data( array $order ) {
		$site_name     = get_bloginfo( 'name' );
		$order_user_id = $order['user_id'];
		$user_data     = get_userdata( $order_user_id );

		$items          = array();
		$subtotal_price = $order['subtotal_price'];
		$total_price    = $order['total_price'];
		$grand_total    = $total_price;
		$order_type     = $order['order_type'];

		$currency_code   = tutor_utils()->get_option( OptionKeys::CURRENCY_CODE, 'USD' );
		$currency_symbol = tutor_get_currency_symbol_by_code( $currency_code );
		$currency_info   = tutor_get_currencies_info_by_code( $currency_code );

		$billing_info = ( new BillingModel() )->get_info( $order_user_id );

		$country_info = tutor_get_country_info_by_name( $billing_info->billing_country );

		$country = (object) array(
			'name'         => $country_info['name'],
			'numeric_code' => $country_info['numeric_code'],
			'alpha_2'      => $country_info['alpha_2'],
			'alpha_3'      => $country_info['alpha_3'],
			'phone_code'   => $country_info['phone_code'],
		);

		$billing_name = $billing_info ? trim( $billing_info->billing_first_name . ' ' . $billing_info->billing_last_name ) : $user_data->display_name;

		$shipping_and_billing = array(
			'name'         => $billing_name,
			'address1'     => $billing_info->billing_address ?? '',
			'address2'     => $billing_info->billing_address ?? '',
			'city'         => $billing_info->billing_city ?? '',
			'state'        => $billing_info->billing_state ?? '',
			'region'       => '',
			'postal_code'  => $billing_info->billing_zip_code ?? '',
			'country'      => $country,
			'phone_number' => $billing_info->billing_phone ?? '',
			'email'        => $billing_info->billing_email ?? '',
		);

		$customer_info = $shipping_and_billing;

		foreach ( $order['items'] as $item ) {
			$item            = (object) $item;
			$item_name       = '';
			$enrollment_item = null;

			// Support for both item_id & id added.
			$item_id = $item->item_id ?? $item->id;

			if ( OrderModel::TYPE_SUBSCRIPTION === $order_type ) {
				$plan_id   = $item_id;
				$plan_info = apply_filters( 'tutor_get_plan_info', new \stdClass(), $plan_id );
				$item_name = $plan_info->plan_name ?? '';

				$items[] = array(
					'item_id'          => $item_id,
					'item_name'        => $item_name,
					'regular_price'    => $item->sale_price > 0 ? $item->sale_price : $item->regular_price,
					'quantity'         => 1,
					'discounted_price' => is_null( $item->discount_price ) || '' === $item->discount_price ? null : $item->discount_price,
				);

				if ( $plan_info && property_exists( $plan_info, 'enrollment_fee' ) && $plan_info->enrollment_fee > 0 ) {
					$enrollment_item = array(
						'item_id'          => 0,
						'item_name'        => 'Enrollment Fee',
						'regular_price'    => floatval( $plan_info->enrollment_fee ),
						'quantity'         => 1,
						'discounted_price' => null,
					);

					$items[] = $enrollment_item;
				}
			} else {
				// Single order item.
				$items[] = array(
					'item_id'          => $item_id,
					'item_name'        => get_the_title( $item_id ),
					'regular_price'    => tutor_get_locale_price( $item->sale_price > 0 ? $item->sale_price : $item->regular_price ),
					'quantity'         => 1,
					'discounted_price' => is_null( $item->discount_price ) || '' === $item->discount_price ? null : tutor_get_locale_price( $item->discount_price ),
				);
			}
		}

		if ( isset( $order['tax_amount'] ) && ! Tax::is_tax_included_in_price() ) {
			$grand_total += $order['tax_amount'];

			/* translators: %s: tax rate */
			$tax_item = sprintf( __( 'Tax (%s)', 'tutor' ), $order['tax_rate'] . '%' );
			$items[]  = array(
				'item_id'          => 'tax',
				'item_name'        => $tax_item,
				'regular_price'    => $order['tax_amount'],
				'quantity'         => 1,
				'discounted_price' => null,
			);
		}

		return (object) array(
			'items'              => (object) $items,
			'subtotal'           => floatval( $subtotal_price ),
			'total_price'        => floatval( $total_price ),
			'order_id'           => $order['id'],
			'store_name'         => $site_name,
			'order_description'  => 'Tutor Order',
			'tax'                => 0,
			'currency'           => (object) array(
				'code'         => $currency_code,
				'symbol'       => $currency_symbol,
				'name'         => $currency_info['name'] ?? '',
				'locale'       => $currency_info['locale'] ?? '',
				'numeric_code' => $currency_info['numeric_code'] ?? '',
			),
			'country'            => $country,
			'shipping_charge'    => 0,
			'coupon_discount'    => 0,
			'shipping_address'   => (object) $shipping_and_billing,
			'billing_address'    => (object) $shipping_and_billing,
			'decimal_separator'  => tutor_utils()->get_option( OptionKeys::DECIMAL_SEPARATOR, '.' ),
			'thousand_separator' => tutor_utils()->get_option( OptionKeys::THOUSAND_SEPARATOR, '.' ),
			'customer'           => (object) $customer_info,
		);
	}

	/**
	 * Prepare payment data
	 *
	 * @since 3.0.0
	 *
	 * @param int $order_id Order id.
	 *
	 * @throws \Exception Throw exception if order not found.
	 *
	 * @return mixed
	 */
	public static function prepare_recurring_payment_data( int $order_id ) {
		$order_data = ( new OrderModel() )->get_order_by_id( $order_id );
		if ( ! $order_data ) {
			throw new \Exception( __( 'Order not found!', 'tutor' ) );
		}

		$amount = $order_data->total_price;

		$order_user_id = $order_data->student->id;
		$user_data     = get_userdata( $order_user_id );

		$currency_code   = tutor_utils()->get_option( OptionKeys::CURRENCY_CODE, 'USD' );
		$currency_symbol = tutor_get_currency_symbol_by_code( $currency_code );
		$currency_info   = tutor_get_currencies_info_by_code( $currency_code );

		$billing_info = ( new BillingModel() )->get_info( $order_user_id );

		$country_info = tutor_get_country_info_by_name( $billing_info->billing_country );

		$country = (object) array(
			'name'         => $country_info['name'],
			'numeric_code' => $country_info['numeric_code'],
			'alpha_2'      => $country_info['alpha_2'],
			'alpha_3'      => $country_info['alpha_3'],
			'phone_code'   => $country_info['phone_code'],
		);

		$billing_name = $billing_info ? trim( $billing_info->billing_first_name . ' ' . $billing_info->billing_last_name ) : $user_data->display_name;

		$shipping_and_billing = array(
			'name'         => $billing_name,
			'address1'     => $billing_info->billing_address ?? '',
			'address2'     => $billing_info->billing_address ?? '',
			'city'         => $billing_info->billing_city ?? '',
			'state'        => $billing_info->billing_state ?? '',
			'region'       => '',
			'postal_code'  => $billing_info->billing_zip_code ?? '',
			'country'      => $country,
			'phone_number' => $billing_info->billing_phone ?? '',
			'email'        => $billing_info->billing_email ?? '',
		);

		$customer_info = $shipping_and_billing;

		return (object) array(
			'type'             => 'recurring',
			'previous_payload' => $order_data->payment_payloads,
			'total_amount'     => floatval( $amount ),
			'sub_total_amount' => floatval( $amount ),
			'currency'         => (object) array(
				'code'         => $currency_code,
				'symbol'       => $currency_symbol,
				'name'         => $currency_info['name'] ?? '',
				'locale'       => $currency_info['locale'] ?? '',
				'numeric_code' => $currency_info['numeric_code'] ?? '',
			),
			'order_id'         => $order_id,
			'customer'         => (object) $customer_info,
			'shipping_address' => (object) $shipping_and_billing,
		);
	}

	/**
	 * Proceed to payment
	 *
	 * @since 3.0.0
	 *
	 * @param mixed  $payment_data Payment data for making order.
	 * @param string $payment_method Payment method name.
	 * @param string $order_type Order type.
	 *
	 * @throws \Throwable Throw throwable if error occur.
	 * @throws \Exception Throw exception if payment gateway is invalid.
	 *
	 * @return void
	 */
	public function proceed_to_payment( $payment_data, $payment_method, $order_type ) {
		$payment_gateways = apply_filters( 'tutor_gateways_with_class', Ecommerce::payment_gateways_with_ref(), $payment_method );

		$payment_gateway_class = isset( $payment_gateways[ $payment_method ] )
								? $payment_gateways[ $payment_method ]['gateway_class']
								: null;

		if ( $payment_gateway_class ) {
			try {

				add_filter(
					'tutor_ecommerce_webhook_url',
					function ( $url ) use ( $payment_method ) {
						$url = add_query_arg( array( 'payment_method' => $payment_method ), $url );
						return $url;
					}
				);

				add_filter(
					'tutor_ecommerce_payment_success_url_args',
					function ( $args ) use ( $payment_data ) {
						$args['order_id'] = $payment_data->order_id;
						return $args;
					}
				);
				add_filter(
					'tutor_ecommerce_payment_cancelled_url_args',
					function ( $args ) use ( $payment_data ) {
						$args['order_id'] = $payment_data->order_id;
						return $args;
					}
				);

				$gateway_instance = Ecommerce::get_payment_gateway_object( $payment_gateway_class );
				$gateway_instance->setup_payment_and_redirect( $payment_data );
			} catch ( \Throwable $th ) {
				throw $th;
			}
		} else {
			throw new \Exception( 'Invalid payment gateway class' );
		}
	}

	/**
	 * Restrict checkout page
	 *
	 * @return void
	 */
	public function restrict_checkout_page() {
		$page_id = self::get_page_id();
		$plan_id = Input::get( 'plan' );

		if ( is_page( $page_id ) && ! $plan_id ) {
			$cart_controller = new CartController();
			$cart_model      = new CartModel();

			$user_id       = tutils()->get_user_id();
			$has_cart_item = $cart_model->has_item_in_cart( $user_id );

			if ( ! $has_cart_item ) {
				wp_safe_redirect( $cart_controller::get_page_url() );
				exit;
			}
		}
	}

	/**
	 * Set alert message on the session based on
	 * order data
	 *
	 * @since 3.0.0
	 *
	 * @param mixed $order_data Order data or null. If order
	 * data is falsy then failed message will be set.
	 *
	 * @return void
	 */
	private function set_pay_now_alert_msg( $order_data ) {
		$user_id = $order_data ? $order_data['user_id'] : get_current_user_id();
		if ( empty( $order_data ) ) {
			set_transient(
				self::PAY_NOW_ALERT_MSG_TRANSIENT_KEY . $user_id,
				array(
					'alert'   => 'danger',
					'message' => __( 'Failed to place order!', 'tutor' ),
				),
			);
		} else {
			set_transient(
				self::PAY_NOW_ALERT_MSG_TRANSIENT_KEY . $user_id,
				array(
					'alert'   => 'success',
					'message' => __( 'Your order has been placed successfully!', 'tutor' ),
				),
			);
		}
	}

	/**
	 * Pay for the incomplete order
	 *
	 * Redirect to the payment gateway to complete the order
	 * After completing the process it will redirect user to
	 * order placement page
	 *
	 * @since 3.0.0
	 *
	 * @return void
	 */
	public function pay_incomplete_order() {
		$order_id = Input::post( 'order_id', 0, Input::TYPE_INT );
		if ( ! tutor_utils()->is_nonce_verified() ) {
			tutor_utils()->redirect_to( tutor_utils()->tutor_dashboard_url( 'purchase_history' ), tutor_utils()->error_message( 'nonce' ), 'error' );
			exit;
		}
		if ( $order_id ) {
			$order_data = ( new OrderModel() )->get_order_by_id( $order_id );
			if ( $order_data ) {
				try {
					$payment_data = $this->prepare_payment_data( (array) $order_data, $order_data->payment_method, $order_data->order_type );
					$this->proceed_to_payment( $payment_data, $order_data->payment_method, $order_data->order_type );
				} catch ( \Throwable $th ) {
					tutor_log( $th );
					tutor_redirect_after_payment( OrderModel::ORDER_PLACEMENT_FAILED, $order_data->id, $th->getMessage() );
				}
			} else {
				$error_msg = __( 'Order not found!', 'tutor' );
				tutor_redirect_after_payment( OrderModel::ORDER_PLACEMENT_FAILED, $order_id, $error_msg );
			}
		} else {
			$error_msg = __( 'Invalid order ID!', 'tutor' );
			tutor_redirect_after_payment( OrderModel::ORDER_PLACEMENT_FAILED, $order_id, $error_msg );
		}
	}

	/**
	 * Validate pay now request
	 *
	 * @since 3.0.0
	 *
	 * @param array $data The data array to validate.
	 *
	 * @return object The validation result. It returns validation object.
	 */
	protected function validate_pay_now_req( array $data ) {

		$order_types = array(
			OrderModel::TYPE_SINGLE_ORDER,
			OrderModel::TYPE_SUBSCRIPTION,
			OrderModel::TYPE_RENEWAL,
		);
		$order_types = implode( ',', $order_types );

		$validation_rules = array(
			'object_ids'     => 'required',
			'payment_method' => 'required',
			'order_type'     => "required|match_string:{$order_types}",
		);

		// Skip validation rules for not available fields in data.
		foreach ( $validation_rules as $key => $value ) {
			if ( ! array_key_exists( $key, $data ) ) {
				unset( $validation_rules[ $key ] );
			}
		}

		return ValidationHelper::validate( $validation_rules, $data );
	}
}

Filemanager

Name Type Size Permission Actions
PaymentGateways Folder 0755
AdminMenu.php File 2.34 KB 0644
BillingController.php File 5.57 KB 0644
CartController.php File 5.46 KB 0644
CheckoutController.php File 26.97 KB 0644
CouponController.php File 21.48 KB 0644
Ecommerce.php File 5.12 KB 0644
EmailController.php File 24.96 KB 0644
HooksHandler.php File 11.75 KB 0644
OptionKeys.php File 1.77 KB 0644
OrderActivitiesController.php File 4.57 KB 0644
OrderController.php File 33.34 KB 0644
PaymentHandler.php File 4.25 KB 0644
Settings.php File 12.7 KB 0644
Tax.php File 6.35 KB 0644
currency.php File 27.51 KB 0644