File "class-kco-checkout.php"

Full Path: /home/siazco/grocery.siazco.se/wp-content/plugins/klarna-checkout-for-woocommerce/classes/class-kco-checkout.php
File size: 7 KB
MIME-type: text/x-php
Charset: utf-8

<?php
/**
 * Class for managing actions during the checkout process.
 *
 * @package Klarna_Checkout/Classes
 */

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

/**
 * Class for managing actions during the checkout process.
 */
class KCO_Checkout {
	/**
	 * Class constructor.
	 */
	public function __construct() {
		add_filter( 'woocommerce_checkout_fields', array( $this, 'add_shipping_data_input' ) );
		add_action( 'woocommerce_before_calculate_totals', array( $this, 'update_shipping_method' ), 9999 );
		add_action( 'woocommerce_after_calculate_totals', array( $this, 'update_klarna_order' ), 99999 );

		// Handle potential shipping selection errors.
		add_filter( 'woocommerce_shipping_chosen_method', array( __CLASS__, 'maybe_register_shipping_error' ), 9999, 3 );
		add_action( 'woocommerce_shipping_method_chosen', array( __CLASS__, 'maybe_throw_shipping_error' ), 9999 );
	}

	/**
	 * Add a hidden input field for the shipping data from Klarna.
	 *
	 * @param array $fields The WooCommerce checkout fields.
	 * @return array
	 */
	public function add_shipping_data_input( $fields ) {
		$default = '';

		if ( is_checkout() && isset( WC()->session ) ) {
			$klarna_order_id = WC()->session->get( 'kco_wc_order_id' );
			$shipping_data   = get_transient( 'kss_data_' . $klarna_order_id );
			$default         = wp_json_encode( $shipping_data );
		}

		$fields['billing']['kco_shipping_data'] = array(
			'type'    => 'hidden',
			'class'   => array( 'kco_shipping_data' ),
			'default' => $default,
		);

		return $fields;
	}

	/**
	 * Update the shipping method in WooCommerce based on what Klarna has sent us.
	 *
	 * @return void
	 */
	public function update_shipping_method() {
		if ( ! is_checkout() ) {
			return;
		}

		if ( 'kco' !== WC()->session->get( 'chosen_payment_method' ) ) {
			return;
		}

		$data = false;

		/*
		 * If - During the normal checkout flow.
		 * Else If - During the placing of the order.
		 */
		if ( isset( $_POST['post_data'] ) ) { // phpcs:ignore
			wp_parse_str( $_POST['post_data'], $post_data ); // phpcs:ignore
			if ( isset( $post_data['kco_shipping_data'] ) ) {
				$data = $post_data['kco_shipping_data'];
			}
		} elseif ( isset( $_POST['kco_shipping_data'] ) ) { // phpcs:ignore
			$data = $_POST['kco_shipping_data']; // phpcs:ignore
		}

		// If we have data, update the shipping.
		if ( ! empty( $data ) ) {
			WC()->session->set( 'kco_shipping_data', $data );
			kco_update_wc_shipping( json_decode( $data, true ) );
		}
	}

	/**
	 * Update the Klarna order after calculations from WooCommerce has run.
	 *
	 * @return void
	 */
	public function update_klarna_order() {
		if ( ! is_checkout() ) {
			return;
		}

		if ( 'kco' !== WC()->session->get( 'chosen_payment_method' ) ) {
			return;
		}
		$klarna_order_id = WC()->session->get( 'kco_wc_order_id' );

		if ( empty( $klarna_order_id ) ) {
			KCO_Logger::log( 'Missing WC session kco_wc_order_id during update Klarna order sequence.' );
			return;
		}

		$klarna_order = KCO_WC()->api->get_klarna_order( $klarna_order_id );

		if ( $klarna_order && 'checkout_incomplete' === $klarna_order['status'] ) {
			// If it is, update order.
			$klarna_order = KCO_WC()->api->update_klarna_order( $klarna_order_id );
		}

		// If cart doesn't need payment anymore - reload the checkout page.
		if ( apply_filters( 'kco_check_if_needs_payment', true ) ) {
			if ( ! WC()->cart->needs_payment() && 'checkout_incomplete' === $klarna_order['status'] ) {
				WC()->session->reload_checkout = true;
			}
		}
	}

	/**
	 * Maybe registers an error if we are attempting to set a new shipping method during the checkout process.
	 * WooCommerce will in some cases reset the shipping selection, instead of throwing an error if shipping options
	 * have changed. In our case its better to throw an error for the customer to see, so they can try again
	 * or select another shipping option.
	 *
	 * @param string $default The shipping method id that would be set as the default method.
	 * @param array  $rates The rates calculated when getting the default shipping method.
	 * @param string $chosen_method The shipping method id that was chosen by the customer.
	 *
	 * @return string
	 */
	public static function maybe_register_shipping_error( $default, $rates, $chosen_method ) {
		// Only do this if we are during the checkout process.
		if ( did_action( 'woocommerce_checkout_process' ) <= 0 ) {
			return $default;
		}

		// Only do this if KCO is the selected payment method and shipping in the iframe is selected.
		if ( 'kco' !== WC()->session->get( 'chosen_payment_method' ) ) {
			return $default;
		}

		$chosen_method = trim( $chosen_method );
		if ( empty( $chosen_method ) ) {
			return $default;
		}

		$options = get_option( 'woocommerce_kco_settings', array() );
		if ( 'yes' !== $options['shipping_methods_in_iframe'] ?? 'no' ) {
			return $default;
		}

		// The Klarna Shipping Service sets the chosen shipping method to the method without the instance ID, so if $chosen method === 'klarna_kss' return $default.
		if ( 'klarna_kss' === $chosen_method ) {
			return $default;
		}

		// This covers for situations where the shipping rate packages may be changed through a hook, which may result in an incorrect shipping method change assessment.
		if ( $default === $chosen_method ) {
			return $default;
		}

		KCO_Logger::log( "Checkout error - Shipping methods where changed during the checkout process by WooCommerce. Chosen shipping method by the customer was $chosen_method, WooCommerce wanted to set $default instead" );

		/*
		 * Add a filter to allow people to set if they want to automatically correct shipping discrepancies instead of throwing an error.
		 * Note however that this is not recommended. If you do this, and the shipping method that the customer selected is no longer available,
		 * then unexpected issues might happen. Only do this if you are sure the chosen method actually exists and is available.
		 */
		if ( apply_filters( 'kco_shipping_auto_correct', false, $default, $rates, $chosen_method ) ) {
			KCO_Logger::log( "Checkout error - Correcting the shipping method to the customers chosen method: $chosen_method" );
			return $chosen_method;
		}

		// If we are not auto-correcting the shipping method, we return the default, but trigger our action. This is so we can throw the error at a later time.
		do_action( 'kco_checkout_shipping_error' );
		return $default;
	}

	/**
	 * Actually throws the error registered previously.
	 * This is moved to happen on a separate action instead, since we need to allow WooCommerce to set a couple sessions.
	 * This prevents customers needing to reload the page.
	 *
	 * @return void
	 * @throws Exception Exception with the error message.
	 */
	public static function maybe_throw_shipping_error() {
		if ( did_action( 'kco_checkout_shipping_error' ) <= 0 ) {
			return;
		}

		KCO_Logger::log( 'Checkout error - Printing shipping error message to the customer.' );
		throw new Exception( __( 'The shipping methods have been changed during the checkout process. Please verify your selected shipping method and try again.', 'klarna-checkout-for-woocommerce' ) );
	}
} new KCO_Checkout();