File "class-kco-gateway.php"
Full Path: /home/siazco/grocery.siazco.se/wp-content/plugins/klarna-checkout-for-woocommerce/classes/class-kco-gateway.php
File size: 33.25 KB
MIME-type: text/x-php
Charset: utf-8
<?php
/**
* Class file for KCO_Gateway class.
*
* @package Klarna_Checkout/Classes
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( class_exists( 'WC_Payment_Gateway' ) ) {
/**
* KCO_Gateway class.
*
* @extends WC_Payment_Gateway
*/
class KCO_Gateway extends WC_Payment_Gateway {
public $testmode = false;
public $logging = false;
public $shipping_methods_in_iframe = false;
/**
* KCO_Gateway constructor.
*/
public function __construct() {
$this->id = 'kco';
$this->method_title = __( 'Klarna Checkout', 'klarna-checkout-for-woocommerce' );
$this->method_description = __( 'The current Klarna Checkout replaces standard WooCommerce checkout page.', 'klarna-checkout-for-woocommerce' );
$this->has_fields = false;
$this->supports = apply_filters(
'kco_wc_supports',
array(
'products',
'subscriptions',
'subscription_cancellation',
'subscription_suspension',
'subscription_reactivation',
'subscription_amount_changes',
'subscription_date_changes',
'multiple_subscriptions',
'subscription_payment_method_change_customer',
'subscription_payment_method_change_admin',
'upsell',
)
);
// Load the form fields.
$this->init_form_fields();
// Load the settings.
$this->init_settings();
$this->title = $this->get_option( 'title' );
$this->description = $this->get_option( 'description' );
$this->enabled = $this->get_option( 'enabled' );
$this->testmode = 'yes' === $this->get_option( 'testmode' );
$this->logging = 'yes' === $this->get_option( 'logging' );
$this->shipping_methods_in_iframe = $this->get_option( 'shipping_methods_in_iframe' );
add_action(
'woocommerce_update_options_payment_gateways_' . $this->id,
array(
$this,
'process_admin_options',
)
);
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
add_action( 'woocommerce_admin_order_data_after_billing_address', array( $this, 'add_billing_org_nr' ) );
add_action( 'woocommerce_admin_order_data_after_billing_address', array( $this, 'add_billing_reference' ) );
add_action( 'woocommerce_admin_order_data_after_billing_address', array( $this, 'address_notice' ) );
add_action( 'woocommerce_admin_order_data_after_shipping_address', array( $this, 'add_shipping_reference' ) );
add_action( 'woocommerce_checkout_init', array( $this, 'prefill_consent' ) );
add_action( 'woocommerce_thankyou_' . $this->id, array( $this, 'show_thank_you_snippet' ) );
add_action( 'woocommerce_thankyou', 'kco_unset_sessions', 100, 1 );
// Remove WooCommerce footer text from our settings page.
add_filter( 'admin_footer_text', array( $this, 'admin_footer_text' ), 999 );
// Body class for KSS.
add_filter( 'body_class', array( $this, 'add_body_class' ) );
add_filter( 'woocommerce_order_needs_payment', array( $this, 'maybe_change_needs_payment' ), 999, 3 );
add_filter( 'kco_wc_api_request_args', array( $this, 'maybe_remove_kco_epm' ), 9999 );
// Prevent the Woo validation from proceeding if there is a discrepancy between Woo and Klarna.
add_action( 'woocommerce_after_checkout_validation', array( $this, 'validate_checkout' ), 10, 2 );
}
/**
* Validate the data of the checkout fields matches the Klarna order.
*
* @param array $data An array of posted data.
* @param WP_Error $errors Validation errors.
* @return void
*/
public function validate_checkout( $data, $errors ) {
if ( 'kco' !== WC()->session->get( 'chosen_payment_method' ) ) {
return;
}
$klarna_order_id = WC()->session->get( 'kco_wc_order_id', 'missing' );
$klarna_order = KCO_WC()->api->get_klarna_order( $klarna_order_id );
if ( is_wp_error( $klarna_order ) ) {
KCO_Logger::log( "[CHECKOUT VALIDATION]: Error getting Klarna order: {$klarna_order->get_error_message()}. For Klarna order ID: '$klarna_order_id'. Will not proceed with order." );
$errors->add( 'klarna_order', __( 'The Klarna order could not be retrieved from the session. Please try again.', 'klarna-checkout-for-woocommerce' ) );
return;
}
// Mapping of the Woo/Klarna address fields.
$address_fields_key = array(
'first_name' => 'given_name',
'last_name' => 'family_name',
'company' => 'organization_name',
'address_1' => 'street_address',
'address_2' => 'street_address2',
'city' => 'city',
// 'state' => 'region',
'postcode' => 'postal_code',
'country' => 'country',
);
$billing_address = array_filter(
$data,
function ( $field ) use ( $address_fields_key ) {
return strpos( $field, 'billing_' ) === 0 && in_array( substr( $field, strlen( 'billing_' ) ), array_keys( $address_fields_key ), true );
},
ARRAY_FILTER_USE_KEY
);
$shipping_address = array_filter(
$data,
function ( $field ) use ( $address_fields_key ) {
return strpos( $field, 'shipping_' ) === 0 && in_array( substr( $field, strlen( 'shipping_' ) ), array_keys( $address_fields_key ), true );
},
ARRAY_FILTER_USE_KEY
);
$ship_to_different_address = $data['ship_to_different_address'];
foreach ( $address_fields_key as $wc_name => $klarna_name ) {
$billing_field = 'billing_' . $wc_name;
$shipping_field = 'shipping_' . $wc_name;
if ( 'country' === $wc_name ) {
$base_location = wc_get_base_location();
$country = $base_location['country'];
if ( ! isset( $billing_address[ $billing_field ] ) ) {
$billing_address[ $billing_field ] = $country;
}
if ( ! isset( $shipping_address[ $shipping_field ] ) ) {
$shipping_address[ $shipping_field ] = $country;
}
}
if ( isset( $klarna_order['billing_address'][ $klarna_name ] ) ) {
// Remove all whitespace and convert to lowercase.
$billing_address[ $billing_field ] = strtolower( preg_replace( '/\s+/', '', $billing_address[ $billing_field ] ) );
$klarna_order['billing_address'][ $klarna_name ] = strtolower( preg_replace( '/\s+/', '', $klarna_order['billing_address'][ $klarna_name ] ) );
if ( $billing_address[ $billing_field ] !== ( $klarna_order['billing_address'][ $klarna_name ] ?? '' ) ) {
$errors->add( $billing_field, __( 'Billing ' . str_replace( '_', ' ', $wc_name ) . ' does not match Klarna order.', 'klarna-checkout-for-woocommerce' ) );
}
}
if ( $ship_to_different_address ) {
// Remove all whitespace and convert to lowercase.
$shipping_address[ $shipping_field ] = strtolower( preg_replace( '/\s+/', '', $shipping_address[ $shipping_field ] ) );
$klarna_order['shipping_address'][ $klarna_name ] = strtolower( preg_replace( '/\s+/', '', $klarna_order['shipping_address'][ $klarna_name ] ?? '' ) );
if ( $shipping_address[ $shipping_field ] !== ( $klarna_order['shipping_address'][ $klarna_name ] ?? '' ) ) {
$errors->add( $shipping_field, __( 'Shipping ' . str_replace( '_', ' ', $wc_name ) . ' does not match Klarna order.', 'klarna-checkout-for-woocommerce' ) );
}
}
}
}
/**
* Get gateway icon.
*
* @return string
*/
public function get_icon() {
$icon_src = 'https://cdn.klarna.com/1.0/shared/image/generic/logo/en_us/basic/logo_black.png?width=100';
$icon_html = '<img src="' . $icon_src . '" alt="Klarna Checkout" style="border-radius:0px" width="100"/>';
return apply_filters( 'wc_klarna_checkout_icon_html', $icon_html );
}
/**
* Process the payment and return the result.
*
* @param int $order_id WooCommerce order ID.
*
* @return array
*/
public function process_payment( $order_id ) {
$change_payment_method = filter_input( INPUT_GET, 'change_payment_method', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
// Order-pay purchase (or subscription payment method change)
// 1. Redirect to receipt page.
// 2. Process the payment by displaying the KCO iframe via woocommerce_receipt_kco hook.
if ( ! empty( $change_payment_method ) ) {
$klarna_order = KCO_WC()->api->create_klarna_order( $order_id, 'redirect' );
if ( is_wp_error( $klarna_order ) ) {
wc_add_notice( $klarna_order->get_error_message(), 'error' );
return array(
'result' => 'error',
);
}
return $this->process_redirect_handler( $order_id, $klarna_order );
}
// Order pay or redirect flow.
if ( is_wc_endpoint_url( 'order-pay' ) || 'redirect' === ( $this->settings['checkout_flow'] ?? 'embedded' ) ) {
$klarna_order = KCO_WC()->api->create_klarna_order( $order_id, 'redirect' );
if ( is_wp_error( $klarna_order ) ) {
wc_add_notice( $klarna_order->get_error_message(), 'error' );
return array(
'result' => 'error',
);
}
return $this->process_redirect_handler( $order_id, $klarna_order );
}
// Regular embedded purchase.
// 1. Save Klarna data to the pending order.
// 2. Approve process payment sequence to customer can continue/complete payment.
return $this->process_embedded_payment_handler( $order_id );
}
/**
* This plugin doesn't handle order management, but it allows Klarna Order Management plugin to process refunds
* and then return true or false.
*
* @param int $order_id WooCommerce order ID.
* @param null|int $amount Refund amount.
* @param string $reason Reason for refund.
*
* @return bool
*/
public function process_refund( $order_id, $amount = null, $reason = '' ) {
return apply_filters( 'wc_klarna_checkout_process_refund', false, $order_id, $amount, $reason );
}
/**
* Initialise settings fields.
*/
public function init_form_fields() {
$this->form_fields = KCO_Fields::fields();
}
/**
* Checks if method should be available.
*
* @return bool
*/
public function is_available() {
if ( 'yes' !== $this->enabled ) {
return false;
}
// If we can't retrieve a set of credentials, disable KCO.
if ( is_checkout() && ! KCO_WC()->credentials->get_credentials_from_session() ) {
return false;
}
// If we have a subscription product in cart and the customer isn't from SE, NO, FI, DE, DK, AT or NL, disable KCO.
if ( is_checkout() && class_exists( 'WC_Subscriptions_Cart' ) && WC_Subscriptions_Cart::cart_contains_subscription() ) {
$available_recurring_countries = array( 'SE', 'NO', 'FI', 'DK', 'DE', 'AT', 'NL' );
$country = WC()->customer->get_billing_country();
if ( empty( $country ) ) {
// If the billing country is not available, the "No location by default" setting is set.
// By default, if there is exactly one country the store sells to, it will be used by default.
// However, it still won't be set as the billing country until the customer has filled their billing address.
// In practice, the customer doesn't really have any other choice, so we can assume that it is selected country.
$countries = WC()->countries->get_allowed_countries();
if ( 1 === count( $countries ) ) {
$country = array_key_first( $countries );
} elseif ( 1 < count( $countries ) ) {
// If there is at least more than one allowed country, WC will let the customer pick a country on the checkout page.
// We'll wait until the customer has made a choice.
return false;
}
}
if ( ! in_array( $country, $available_recurring_countries, true ) ) {
return false;
}
}
return true;
}
/**
* Add sidebar to the settings page.
*/
public function admin_options() {
ob_start();
parent::admin_options();
$parent_options = ob_get_contents();
ob_end_clean();
KCO_Settings_Saved::maybe_show_errors();
WC_Klarna_Banners::settings_sidebar( $parent_options );
}
/**
* Enqueue payment scripts.
*
* @hook wp_enqueue_scripts
*/
public function enqueue_scripts() {
if ( 'yes' !== $this->enabled ) {
return;
}
/* On the 'order-pay' page we redirect the customer to a hosted payment page, and therefore don't need need to enqueue any of the following assets. */
if ( ! is_checkout() || is_wc_endpoint_url( 'order-pay' ) ) {
return;
}
// If the redirect flow is selected, we do not need to load any custom scripts.
if ( 'redirect' === ( $this->settings['checkout_flow'] ?? 'embedded' ) ) {
return;
}
$pay_for_order = false;
if ( is_wc_endpoint_url( 'order-pay' ) ) {
$pay_for_order = true;
}
if ( ! kco_wc_prefill_allowed() ) {
add_thickbox();
}
$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
wp_register_script(
'kco',
plugins_url( 'assets/js/klarna-checkout-for-woocommerce' . $suffix . '.js', KCO_WC_MAIN_FILE ),
array( 'jquery', 'jquery-blockui' ),
KCO_WC_VERSION,
true
);
wp_register_style(
'kco',
plugins_url( 'assets/css/klarna-checkout-for-woocommerce' . $suffix . '.css', KCO_WC_MAIN_FILE ),
array(),
KCO_WC_VERSION
);
$email_exists = 'no';
if ( null !== WC()->customer && method_exists( WC()->customer, 'get_billing_email' ) && ! empty( WC()->customer->get_billing_email() ) ) {
if ( email_exists( WC()->customer->get_billing_email() ) ) {
// Email exist in a user account.
$email_exists = 'yes';
}
}
$standard_woo_checkout_fields = apply_filters( 'kco_ignored_checkout_fields', array( 'billing_first_name', 'billing_last_name', 'billing_address_1', 'billing_address_2', 'billing_postcode', 'billing_city', 'billing_phone', 'billing_email', 'billing_state', 'billing_country', 'billing_company', 'shipping_first_name', 'shipping_last_name', 'shipping_address_1', 'shipping_address_2', 'shipping_postcode', 'shipping_city', 'shipping_state', 'shipping_country', 'shipping_company', 'terms', 'terms-field', '_wp_http_referer', 'ship_to_different_address' ) );
$checkout_localize_params = array(
'update_cart_url' => WC_AJAX::get_endpoint( 'kco_wc_update_cart' ),
'update_cart_nonce' => wp_create_nonce( 'kco_wc_update_cart' ),
'update_shipping_url' => WC_AJAX::get_endpoint( 'kco_wc_update_shipping' ),
'update_shipping_nonce' => wp_create_nonce( 'kco_wc_update_shipping' ),
'change_payment_method_url' => WC_AJAX::get_endpoint( 'kco_wc_change_payment_method' ),
'change_payment_method_nonce' => wp_create_nonce( 'kco_wc_change_payment_method' ),
'get_klarna_order_url' => WC_AJAX::get_endpoint( 'kco_wc_get_klarna_order' ),
'get_klarna_order_nonce' => wp_create_nonce( 'kco_wc_get_klarna_order' ),
'log_to_file_url' => WC_AJAX::get_endpoint( 'kco_wc_log_js' ),
'log_to_file_nonce' => wp_create_nonce( 'kco_wc_log_js' ),
'submit_order' => WC_AJAX::get_endpoint( 'checkout' ),
'customer_type_changed_url' => WC_AJAX::get_endpoint( 'kco_customer_type_changed' ),
'logging' => $this->logging,
'standard_woo_checkout_fields' => $standard_woo_checkout_fields,
'is_confirmation_page' => ( is_kco_confirmation() ) ? 'yes' : 'no',
'is_order_received_page' => is_order_received_page() ? 'yes' : 'no',
'shipping_methods_in_iframe' => $this->shipping_methods_in_iframe,
'required_fields_text' => __( 'Please fill in all required checkout fields.', 'klarna-checkout-for-woocommerce' ),
'email_exists' => $email_exists,
'must_login_message' => apply_filters( 'woocommerce_registration_error_email_exists', __( 'An account is already registered with your email address. Please log in.', 'woocommerce' ) ),
'timeout_message' => __( 'Please try again, something went wrong with processing your order.', 'klarna-checkout-for-woocommerce' ),
'timeout_time' => apply_filters( 'kco_checkout_timeout_duration', 20 ),
'countries' => kco_get_country_codes(),
'pay_for_order' => $pay_for_order,
'no_shipping_message' => apply_filters( 'woocommerce_no_shipping_available_html', __( 'There are no shipping options available. Please ensure that your address has been entered correctly, or contact us if you need any help.', 'woocommerce' ) ),
'woocommerce_ship_to_destination' => get_option( 'woocommerce_ship_to_destination' ),
);
if ( version_compare( WC_VERSION, '3.9', '>=' ) ) {
$checkout_localize_params['force_update'] = true;
}
wp_localize_script( 'kco', 'kco_params', $checkout_localize_params );
wp_enqueue_script( 'kco' );
if ( ! $pay_for_order ) {
wp_enqueue_style( 'kco' );
}
}
/**
* Enqueue admin scripts.
*
* @param string $hook Admin page hook.
*
* @hook admin_enqueue_scripts
*/
public function admin_enqueue_scripts( $hook ) {
if ( 'woocommerce_page_wc-settings' !== $hook ) {
return;
}
$section = filter_input( INPUT_GET, 'section', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
if ( empty( $section ) || 'kco' !== $section ) {
return;
}
$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
$store_base_location = wc_get_base_location();
if ( 'US' === $store_base_location['country'] ) {
$location = 'US';
} else {
$location = $this->check_if_eu( $store_base_location['country'] );
}
wp_register_script(
'kco_admin',
plugins_url( 'assets/js/klarna-checkout-for-woocommerce-admin' . $suffix . '.js', KCO_WC_MAIN_FILE ),
array(),
KCO_WC_VERSION,
false
);
$admin_localize_params = array(
'location' => $location,
);
wp_localize_script( 'kco_admin', 'kco_admin_params', $admin_localize_params );
wp_enqueue_script( 'kco_admin' );
}
/**
* Detect if EU country.
*
* @param string $store_base_location The WooCommerce stores base country.
*/
private function check_if_eu( $store_base_location ) {
$eu_countries = array(
'AL',
'AD',
'AM',
'AT',
'BY',
'BE',
'BA',
'BG',
'CH',
'CY',
'CZ',
'DE',
'DK',
'EE',
'ES',
'FO',
'FI',
'FR',
'GB',
'GE',
'GI',
'GR',
'HU',
'HR',
'IE',
'IS',
'IT',
'LT',
'LU',
'LV',
'MC',
'MK',
'MT',
'NO',
'NL',
'PL',
'PT',
'RO',
'RU',
'SE',
'SI',
'SK',
'SM',
'TR',
'UA',
'VA',
);
if ( in_array( $store_base_location, $eu_countries, true ) ) {
return 'EU';
} else {
return '';
}
}
/**
* Process the payment with information from Klarna and return the result - for regular embedded checkout.
*
* @param int $order_id WooCommerce order ID.
*
* @return mixed
*/
public function process_embedded_payment_handler( $order_id ) {
// Get the Klarna order ID.
$order = wc_get_order( $order_id );
// For the initial subscription, the Klarna order ID should always exist in the session.
// This also applies to (pending) renewal subscription since existing Klarna order ID is no longer valid for the renewal, we must retrieve it from the session, not the order.
$is_subscription = function_exists( 'wcs_order_contains_subscription' ) && wcs_order_contains_subscription( $order, array( 'parent', 'resubscribe', 'switch', 'renewal' ) );
if ( ! empty( $order ) && ! $is_subscription ) {
$klarna_order_id = $order->get_meta( '_wc_klarna_order_id', true );
}
$klarna_order_id = ! empty( $klarna_order_id ) ? $klarna_order_id : WC()->session->get( 'kco_wc_order_id' );
$klarna_order = KCO_WC()->api->get_klarna_order( $klarna_order_id );
// ----- Extra Debug Logging Start ----- //
try {
$shipping_debug_log = array(
'kco_order_id' => $klarna_order_id,
'wc_order_shipping' => $order->get_shipping_method(),
'wc_session_shipping' => WC()->session->get( 'chosen_shipping_methods' ),
// selected_shipping_option is only available if shipping is displayed in iframe.
'kco_order_shipping' => $klarna_order['selected_shipping_option'] ?? 'N/A',
'kco_shipping_transient' => get_transient( "kss_data_$klarna_order_id" ),
);
$data = json_encode( $shipping_debug_log );
KCO_Logger::log( "Extra shipping debug: $data" );
} catch ( Exception $e ) {
KCO_Logger::log( 'Extra shipping debug: Error generating log due to ' . $e->getMessage() );
}
// ----- Extra Debug Logging End ----- //
$order_number = $order->get_order_number() ?? $order_id ?? 'N/A';
if ( ! $klarna_order ) {
KCO_Logger::log( "Order {$order_number} ({$klarna_order_id}) associated with [{$order->get_billing_email()}] failed to be processed due to: could not retrieve the Klarna order." );
return array(
'result' => 'error',
);
}
if ( $order_id && $klarna_order ) {
$this->save_metadata_to_order( $order_id, $klarna_order, 'embedded' );
// Update the order with new confirmation page url.
$klarna_order = KCO_WC()->api->update_klarna_confirmation( $klarna_order_id, $klarna_order, $order_id );
$order->save();
// Let other plugins hook into this sequence.
do_action( 'kco_wc_process_payment', $order_id, $klarna_order );
KCO_Logger::log( "Order {$order_number} ({$klarna_order_id}) associated with [{$order->get_billing_email()}] was successfully processed." );
return array(
'result' => 'success',
);
}
// Return false if we get here. Something went wrong.
KCO_Logger::log( "Order {$order_number} ({$klarna_order_id}) associated with [{$order->get_billing_email()}] failed to be processed due to: missing order_id or klarna_order." );
return array(
'result' => 'error',
);
}
/**
* Process the payment for HPP/redirect checkout flow.
*
* @param int $order_id The WooCommerce order id.
* @param array $klarna_order The response from payment.
*
* @return array|string[]
*/
protected function process_redirect_handler( $order_id, $klarna_order ) {
$order = wc_get_order( $order_id );
$this->save_metadata_to_order( $order_id, $klarna_order, 'redirect' );
// Create a HPP url.
$hpp = KCO_WC()->api->create_klarna_hpp_url( $klarna_order['order_id'], $order_id );
if ( is_wp_error( $hpp ) ) {
wc_add_notice( 'Failed to create a HPP session with Klarna.', 'error' );
KCO_Logger::log( sprintf( 'Failed to create a HPP session with Klarna. Order %s|%s (Klarna ID: %s) OK. Redirecting to hosted payment page.', $order_id, $order->get_order_number(), $klarna_order['order_id'] ) );
return array(
'result' => 'error',
);
}
$hpp_redirect = $hpp['redirect_url'];
// Save Klarna HPP url & Session ID.
$order->update_meta_data( '_wc_klarna_hpp_url', sanitize_text_field( $hpp_redirect ) );
$order->update_meta_data( '_wc_klarna_hpp_session_id', sanitize_key( $hpp['session_id'] ) );
$order->save();
KCO_Logger::log( sprintf( 'Processing order %s|%s (Klarna ID: %s) OK. Redirecting to hosted payment page.', $order_id, $order->get_order_number(), $klarna_order['order_id'] ) );
// All good. Redirect customer to Klarna Hosted payment page.
$order->add_order_note( __( 'Customer redirected to Klarna Hosted Payment Page.', 'klarna-checkout-for-woocommerce' ) );
return array(
'result' => 'success',
'redirect' => $hpp_redirect,
);
}
/**
* Save metadata to Woo order.
*
* @param int $order_id The WooCommerce order id.
* @param array $klarna_order The response from payment.
* @param string $checkout_flow The type of checkout flow used by customer.
*
* @return void.
*/
public function save_metadata_to_order( $order_id, $klarna_order, $checkout_flow = 'embedded' ) {
$order = wc_get_order( $order_id );
// Set Klarna checkout flow.
$order->update_meta_data( '_wc_klarna_checkout_flow', sanitize_text_field( $checkout_flow ) );
// Set Klarna order ID.
$order->update_meta_data( '_wc_klarna_order_id', sanitize_key( $klarna_order['order_id'] ) );
// Set recurring order.
$kco_recurring_order = isset( $klarna_order['recurring'] ) && true === $klarna_order['recurring'] ? 'yes' : 'no';
$order->update_meta_data( '_kco_recurring_order', sanitize_key( $kco_recurring_order ) );
// Set recurring token if it exists.
if ( isset( $klarna_order['recurring_token'] ) ) {
$order->update_meta_data( '_kco_recurring_token', sanitize_key( $klarna_order['recurring_token'] ) );
}
$environment = $this->testmode ? 'test' : 'live';
$order->update_meta_data( '_wc_klarna_environment', $environment );
$klarna_country = wc_get_base_location()['country'];
$order->update_meta_data( '_wc_klarna_country', $klarna_country );
// NOTE: Since we declare support for WC v4+, and WC_Order::set_shipping_phone was only added in 5.6.0, we need to use update_meta_data instead. There is no default shipping email field in WC.
if ( defined( 'WC_VERSION' ) && version_compare( WC_VERSION, '5.6.0', '>=' ) ) {
$order->set_shipping_phone( sanitize_text_field( $klarna_order['shipping_address']['phone'] ) );
} else {
$order->update_meta_data( '_shipping_phone', sanitize_text_field( $klarna_order['shipping_address']['phone'] ) );
}
$order->update_meta_data( '_shipping_email', sanitize_text_field( $klarna_order['shipping_address']['email'] ) );
$order->save();
}
/**
* Displays Klarna Checkout thank you iframe and clears Klarna order ID value from WC session.
*
* @param int $order_id WooCommerce order ID.
*/
public function show_thank_you_snippet( $order_id = null ) {
if ( $order_id ) {
$order = wc_get_order( $order_id );
$upsell_uuids = $order->get_meta( '_ppu_upsell_ids', true );
$has_been_upsold = ! empty( $upsell_uuids );
if ( is_object( $order ) && $order->get_transaction_id() ) {
$klarna_order_id = $order->get_transaction_id();
$klarna_order = KCO_WC()->api->get_klarna_order( $klarna_order_id );
if ( $klarna_order && ! $has_been_upsold ) { // Don't show the snippet for upsold orders, since the iFrame wont be updated with the new orders lines.
echo kco_extract_script( $klarna_order['html_snippet'] ); // phpcs:ignore WordPress.Security.EscapeOutput -- Cant escape since this is the iframe snippet.
}
// Check if we need to finalize purchase here. Should already been done in process_payment.
if ( ! $order->has_status( array( 'on-hold', 'processing', 'completed' ) ) ) {
KCO_Logger::log( $klarna_order_id . ': Confirm the klarna order from the thankyou page.' );
kco_confirm_klarna_order( $order_id, $klarna_order_id );
WC()->cart->empty_cart();
}
}
}
}
/**
* Changes footer text in KCO settings page.
*
* @param string $text Footer text.
*
* @return string
*/
public function admin_footer_text( $text ) {
$section = filter_input( INPUT_GET, 'section', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
if ( ! empty( $section ) && 'kco' === $section ) {
$text = 'If you like Klarna Checkout for WooCommerce, please consider <strong>assigning Krokedil as your integration partner.</strong>.';
}
return $text;
}
/**
* Adds can't edit address notice to KP EU orders.
*
* @param WC_Order $order WooCommerce order object.
*/
public function address_notice( $order ) {
if ( $this->id === $order->get_payment_method() ) {
echo '<div style="clear:both; margin: 10px 0; padding: 10px; border: 1px solid #B33A3A; font-size: 12px">';
esc_html_e( 'Order address should not be changed and any changes you make will not be reflected in Klarna system.', 'klarna-checkout-for-woocommerce' );
echo '</div>';
}
}
/**
* Adds prefill consent to WC session.
*/
public function prefill_consent() {
$prefill_consent = filter_input( INPUT_GET, 'prefill_consent', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
if ( ! empty( $prefill_consent ) ) {
if ( 'yes' === $prefill_consent ) {
WC()->session->set( 'kco_wc_prefill_consent', true );
}
}
}
/**
* Add kco-shipping-display body class.
*
* @param array $class Array of classes.
*
* @return array
*/
public function add_body_class( $class ) {
if ( is_checkout() && 'yes' === $this->shipping_methods_in_iframe ) {
// Don't display KCO Shipping Display body classes if we have a cart that doesn't needs payment.
if ( null !== WC()->cart && method_exists( WC()->cart, 'needs_payment' ) && ! WC()->cart->needs_payment() ) {
return $class;
}
$first_gateway = '';
if ( WC()->session->get( 'chosen_payment_method' ) ) {
$first_gateway = WC()->session->get( 'chosen_payment_method' );
} else {
$available_payment_gateways = WC()->payment_gateways->get_available_payment_gateways();
reset( $available_payment_gateways );
$first_gateway = key( $available_payment_gateways );
}
if ( 'kco' === $first_gateway ) {
$class[] = 'kco-shipping-display';
}
}
return $class;
}
/**
* Maybe adds the billing org number to the address in an order.
*
* @param WC_Order $order The WooCommerce order.
* @return void
*/
public function add_billing_org_nr( $order ) {
if ( $this->id === $order->get_payment_method() ) {
$org_nr = $order->get_meta( '_billing_org_nr', true );
if ( $org_nr ) {
?>
<p>
<strong>
<?php esc_html_e( 'Organisation number:', 'klarna-checkout-for-woocommerce' ); ?>
</strong>
<?php echo esc_html( $org_nr ); ?>
</p>
<?php
}
}
}
/**
* Maybe adds the billing reference to the address in an order.
*
* @param WC_Order $order The WooCommerce order.
* @return void
*/
public function add_billing_reference( $order ) {
if ( $this->id === $order->get_payment_method() ) {
$reference = $order->get_meta( '_billing_reference', true );
if ( $reference ) {
?>
<p>
<strong>
<?php esc_html_e( 'Reference:', 'klarna-checkout-for-woocommerce' ); ?>
</strong>
<?php echo esc_html( $reference ); ?>
</p>
<?php
}
}
}
/**
* Maybe adds the shipping reference to the address in an order.
*
* @param WC_Order $order The WooCommerce order.
* @return void
*/
public function add_shipping_reference( $order ) {
if ( $this->id === $order->get_payment_method() ) {
$reference = $order->get_meta( '_shipping_reference', true );
if ( $reference ) {
?>
<p>
<strong>
<?php esc_html_e( 'Reference:', 'klarna-checkout-for-woocommerce' ); ?>
</strong>
<?php echo esc_html( $reference ); ?>
</p>
<?php
}
}
}
/**
* Maybe change the needs payment for a WooCommerce order.
*
* @param bool $wc_result The result WooCommerce had.
* @param WC_Order $order The WooCommerce order.
* @param array $valid_order_statuses The valid order statuses.
* @return bool
*/
public function maybe_change_needs_payment( $wc_result, $order, $valid_order_statuses ) {
// Only change for KCO orders.
if ( 'kco' !== $order->get_payment_method() ) {
return $wc_result;
}
// Only if our filter is active and is set to false.
if ( apply_filters( 'kco_check_if_needs_payment', true ) ) {
return $wc_result;
}
return true;
}
/**
* Remove any external payment method from pay for order.
*
* @param array $request_args The request args.
*
* @return array
*/
public function maybe_remove_kco_epm( $request_args ) {
if ( isset( $request_args['external_payment_methods'] ) && is_wc_endpoint_url( 'order-pay' ) ) {
unset( $request_args['external_payment_methods'] );
}
return $request_args;
}
/**
* Check if upsell should be available for the Klarna order or not.
*
* @param int $order_id The WooCommerce order id.
* @return bool
*/
public function upsell_available( $order_id ) {
$order = wc_get_order( $order_id );
$klarna_order_id = $order->get_meta( '_wc_klarna_order_id', true );
if ( empty( $klarna_order_id ) ) {
return false;
}
$klarna_order = KCO_WC()->api->get_klarna_om_order( $klarna_order_id );
if ( is_wp_error( $klarna_order ) ) {
return false;
}
// If the needed keys are not set, return false.
if ( ! isset( $klarna_order['initial_payment_method'] ) || ! isset( $klarna_order['initial_payment_method']['type'] ) ) {
return false;
}
// Set allowed payment methods for upsell based on country. https://developers.klarna.com/documentation/order-management/integration-guide/pre-delivery/#update-order-amount.
$allowed_payment_methods = array( 'INVOICE', 'B2B_INVOICE', 'BASE_ACCOUNT', 'DIRECT_DEBIT' );
switch ( $klarna_order['billing_address']['country'] ) {
case 'AT':
case 'DE':
case 'DK':
case 'FI':
case 'FR':
case 'NL':
case 'NO':
case 'SE':
$allowed_payment_methods[] = 'FIXED_AMOUNT';
break;
case 'CH':
$allowed_payment_methods = array();
break;
}
return in_array( $klarna_order['initial_payment_method']['type'], $allowed_payment_methods, true );
}
/**
* Make an upsell request to Klarna.
*
* @param int $order_id The WooCommerce order id.
* @param string $upsell_uuid The unique id for the upsell request.
* @return bool
*/
public function upsell( $order_id, $upsell_uuid ) {
$klarna_upsell_order = KCO_WC()->api->upsell_klarna_order( $order_id, $upsell_uuid );
if ( is_wp_error( $klarna_upsell_order ) ) {
$error = new WP_Error( '401', __( 'Klarna did not accept the new order amount, the order has not been updated' ) );
return $error;
}
return true;
}
}
}