initial docker setup

This commit is contained in:
GotPPay
2018-06-14 16:49:28 +02:00
parent bc80b7342e
commit b5f87f27f8
3023 changed files with 985078 additions and 1 deletions

View File

@@ -0,0 +1,98 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
/**
* Returns error messages depending on
*
* @class Collector_Checkout_Admin_Notices
* @version 1.0
* @package Collector_Checkout/Classes
* @category Class
* @author Krokedil
*/
class Klarna_Checkout_For_WooCommerce_Admin_Notices {
/**
* The reference the *Singleton* instance of this class.
*
* @var $instance
*/
protected static $instance;
/**
* Checks if KCO gateway is enabled.
*
* @var $enabled
*/
protected $enabled;
/**
* Returns the *Singleton* instance of this class.
*
* @return self::$instance The *Singleton* instance.
*/
public static function get_instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Collector_Checkout_Admin_Notices constructor.
*/
public function __construct() {
$settings = get_option( 'woocommerce_kco_settings' );
$this->enabled = $settings['enabled'];
add_action( 'admin_init', array( $this, 'check_settings' ) );
}
/**
* Checks the settings.
*/
public function check_settings() {
if ( ! empty( $_POST ) ) {
add_action( 'woocommerce_settings_saved', array( $this, 'check_terms' ) );
} else {
add_action( 'admin_notices', array( $this, 'check_terms' ) );
add_action( 'admin_notices', array( $this, 'check_https' ) );
}
}
/**
* Check if terms page is set.
*/
public function check_terms() {
if ( 'yes' !== $this->enabled ) {
return;
}
// Terms page.
if ( ! wc_get_page_id( 'terms' ) || wc_get_page_id( 'terms' ) < 0 ) {
echo '<div class="notice notice-error">';
echo '<p>' . esc_html( __( 'You need to specify a terms page in WooCommerce Settings to be able to use Klarna Checkout.', 'klarna-checkout-for-woocommerce' ) ) . '</p>';
echo '</div>';
}
}
/**
* Check if https is configured.
*/
public function check_https() {
if ( 'yes' !== $this->enabled ) {
return;
}
if ( ! is_ssl() ) {
echo '<div class="notice notice-error">';
echo '<p>' . esc_html( __( 'You need to enable and configure https to be able to use Klarna Checkout.', 'klarna-checkout-for-woocommerce' ) ) . '</p>';
echo '</div>';
}
}
}
Klarna_Checkout_For_WooCommerce_Admin_Notices::get_instance();

View File

@@ -0,0 +1,308 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Klarna_Checkout_For_WooCommerce_AJAX class.
*
* Registers AJAX actions for Klarna Checkout for WooCommerce.
*
* @extends WC_AJAX
*/
class Klarna_Checkout_For_WooCommerce_AJAX extends WC_AJAX {
/**
* Hook in ajax handlers.
*/
public static function init() {
self::add_ajax_events();
}
/**
* Hook in methods - uses WordPress ajax handlers (admin-ajax).
*/
public static function add_ajax_events() {
$ajax_events = array(
'kco_wc_update_cart' => true,
'kco_wc_update_shipping' => true,
'kco_wc_update_extra_fields' => true,
'kco_wc_change_payment_method' => true,
'kco_wc_update_klarna_order' => true,
'kco_wc_iframe_shipping_address_change' => true,
'kco_wc_checkout_error' => true,
'kco_wc_save_form_data' => true,
);
foreach ( $ajax_events as $ajax_event => $nopriv ) {
add_action( 'wp_ajax_woocommerce_' . $ajax_event, array( __CLASS__, $ajax_event ) );
if ( $nopriv ) {
add_action( 'wp_ajax_nopriv_woocommerce_' . $ajax_event, array( __CLASS__, $ajax_event ) );
// WC AJAX can be used for frontend ajax requests.
add_action( 'wc_ajax_' . $ajax_event, array( __CLASS__, $ajax_event ) );
}
}
}
/**
* Cart quantity update function.
*/
public static function kco_wc_update_cart() {
if ( ! wp_verify_nonce( $_POST['nonce'], 'kco_wc_update_cart' ) ) {
wp_send_json_error( 'bad_nonce' );
exit;
}
$values = array();
parse_str( $_POST['checkout'], $values );
$cart = $values['cart'];
foreach ( $cart as $cart_key => $cart_value ) {
$new_quantity = (int) $cart_value['qty'];
WC()->cart->set_quantity( $cart_key, $new_quantity, false );
}
WC()->cart->calculate_shipping();
WC()->cart->calculate_fees();
WC()->cart->calculate_totals();
KCO_WC()->api->request_pre_update_order();
wp_die();
}
/**
* Update shipping method function.
*/
public static function kco_wc_update_shipping() {
if ( ! wp_verify_nonce( $_POST['nonce'], 'kco_wc_update_shipping' ) ) {
wp_send_json_error( 'bad_nonce' );
exit;
}
wc_maybe_define_constant( 'WOOCOMMERCE_CART', true );
$chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods' );
if ( isset( $_POST['shipping'] ) && is_array( $_POST['shipping'] ) ) {
foreach ( $_POST['shipping'] as $i => $value ) {
$chosen_shipping_methods[ $i ] = wc_clean( $value );
}
}
WC()->session->set( 'chosen_shipping_methods', $chosen_shipping_methods );
WC()->cart->calculate_shipping();
WC()->cart->calculate_fees();
WC()->cart->calculate_totals();
KCO_WC()->api->request_pre_update_order();
wp_die();
}
/**
* Save order notes value to session and use it when creating the order.
*/
public static function kco_wc_update_extra_fields() {
if ( ! wp_verify_nonce( $_POST['nonce'], 'kco_wc_update_extra_fields' ) ) {
wp_send_json_error( 'bad_nonce' );
exit;
}
if ( is_array( $_POST['extra_fields_values'] ) ) {
$update_values = array_map( 'sanitize_textarea_field', $_POST['extra_fields_values'] );
$session_values = WC()->session->get( 'kco_wc_extra_fields_values', array() );
// Update session array, instead of overwriting it.
foreach ( $update_values as $update_key => $update_value ) {
$session_values[ $update_key ] = $update_value;
}
WC()->session->set( 'kco_wc_extra_fields_values', $session_values );
}
wp_die();
}
/**
* Refresh checkout fragment.
*/
public static function kco_wc_change_payment_method() {
if ( ! wp_verify_nonce( $_POST['nonce'], 'kco_wc_change_payment_method' ) ) {
wp_send_json_error( 'bad_nonce' );
exit;
}
$available_gateways = WC()->payment_gateways()->get_available_payment_gateways();
if ( 'false' === $_POST['kco'] ) {
// Set chosen payment method to first gateway that is not Klarna Checkout for WooCommerce.
$first_gateway = reset( $available_gateways );
if ( 'kco' !== $first_gateway->id ) {
WC()->session->set( 'chosen_payment_method', $first_gateway->id );
} else {
$second_gateway = next( $available_gateways );
WC()->session->set( 'chosen_payment_method', $second_gateway->id );
}
} else {
WC()->session->set( 'chosen_payment_method', 'kco' );
}
WC()->payment_gateways()->set_current_gateway( $available_gateways );
$redirect = wc_get_checkout_url();
$data = array(
'redirect' => $redirect,
);
wp_send_json_success( $data );
wp_die();
}
/**
* Updates Klarna order.
*/
public static function kco_wc_update_klarna_order() {
if ( 'kco' === WC()->session->get( 'chosen_payment_method' ) ) {
$klarna_order_id = KCO_WC()->api->get_order_id_from_session();
$klarna_order = KCO_WC()->api->request_pre_retrieve_order( $klarna_order_id );
if ( 'checkout_incomplete' === $klarna_order->status ) {
WC()->cart->calculate_shipping();
WC()->cart->calculate_fees();
WC()->cart->calculate_totals();
KCO_WC()->api->request_pre_update_order();
}
}
wp_send_json_success();
wp_die();
}
/**
* Iframe change callback function.
*/
public static function kco_wc_iframe_shipping_address_change() {
if ( ! wp_verify_nonce( $_POST['nonce'], 'kco_wc_iframe_shipping_address_change' ) ) {
wp_send_json_error( 'bad_nonce' );
exit;
}
if ( isset( $_REQUEST['data'] ) && is_array( $_REQUEST['data'] ) ) {
$address = array_map( 'sanitize_text_field', $_REQUEST['data'] );
}
$customer_data = array();
if ( isset( $address['email'] ) ) {
$customer_data['email'] = $address['email'];
$customer_data['billing_email'] = $address['email'];
}
if ( isset( $address['postal_code'] ) ) {
$customer_data['postcode'] = $address['postal_code'];
$customer_data['billing_postcode'] = $address['postal_code'];
$customer_data['shipping_postcode'] = $address['postal_code'];
}
if ( isset( $address['given_name'] ) ) {
$customer_data['first_name'] = $address['given_name'];
$customer_data['billing_first_name'] = $address['given_name'];
$customer_data['shipping_first_name'] = $address['given_name'];
}
if ( isset( $address['family_name'] ) ) {
$customer_data['last_name'] = $address['family_name'];
$customer_data['billing_last_name'] = $address['family_name'];
$customer_data['shipping_last_name'] = $address['family_name'];
}
if ( isset( $address['region'] ) ) {
$customer_data['state'] = $address['region'];
$customer_data['billing_state'] = $address['region'];
$customer_data['shipping_state'] = $address['region'];
}
if ( isset( $address['country'] ) && kco_wc_country_code_converter( $address['country'] ) ) {
$country = kco_wc_country_code_converter( $address['country'] );
$customer_data['country'] = $country;
$customer_data['billing_country'] = $country;
$customer_data['shipping_country'] = $country;
}
WC()->customer->set_props( $customer_data );
WC()->customer->save();
WC()->cart->calculate_shipping();
WC()->cart->calculate_totals();
KCO_WC()->api->request_pre_update_order();
ob_start();
woocommerce_order_review();
$html = ob_get_clean();
wp_send_json_success( array( 'html' => $html ) );
wp_die();
}
/**
* Handles WooCommerce checkout error, after Klarna order has already been created.
*/
public static function kco_wc_checkout_error() {
if ( ! wp_verify_nonce( $_POST['nonce'], 'kco_wc_checkout_error' ) ) { // Input var okay.
wp_send_json_error( 'bad_nonce' );
exit;
}
if ( ! empty( $_POST['error_message'] ) ) { // Input var okay.
$error_message = 'Error message: ' . sanitize_text_field( trim( $_POST['error_message'] ) );
} else {
$error_message = 'Error message could not be retreived';
}
KCO_WC()->logger->log( 'Checkout form submission failed. Starting fallback order creation.... ' . $error_message );
krokedil_log_events( null, 'Checkout form submission failed', $error_message );
if ( ! empty( $_GET['kco_wc_order_id'] ) ) { // Input var okay.
$klarna_order_id = $_GET['kco_wc_order_id'];
} else {
$klarna_order_id = KCO_WC()->api->get_order_id_from_session();
}
// Create order via fallback sequence
$order = Klarna_Checkout_For_WooCommerce_Create_Local_Order_Fallback::create( $klarna_order_id, $error_message );
if( is_object( $order ) ) {
KCO_WC()->logger->log( 'Fallback order creation done. Redirecting customer to thank you page.' );
krokedil_log_events( null, 'Fallback order creation done. Redirecting customer to thank you page.', '' );
$note = sprintf( __( 'This order was made as a fallback due to an error in the checkout (%s). Please verify the order with Klarna.', 'klarna-checkout-for-woocommerce' ), $error_message );
$order->add_order_note( $note );
$redirect_url = $order->get_checkout_order_received_url();
} else {
KCO_WC()->logger->log( 'Fallback order creation ERROR. Redirecting customer to simplified thank you page.' . json_decode( $order ) );
krokedil_log_events( null, 'Fallback order creation ERROR. Redirecting customer to simplified thank you page.', $order );
$redirect_url = wc_get_endpoint_url( 'order-received', '', wc_get_page_permalink( 'checkout' ) );
$redirect_url = add_query_arg( 'kco_wc', 'true', $redirect_url );
}
wp_send_json_success( array( 'redirect' => $redirect_url ) );
wp_die();
}
/**
* Handles the saving of form data to transient.
*/
public static function kco_wc_save_form_data() {
if ( ! wp_verify_nonce( $_POST['nonce'], 'kco_wc_save_form_data' ) ) { // Input var okay.
wp_send_json_error( 'bad_nonce' );
exit;
}
$form = $_POST['form'];
set_transient( WC()->session->get( 'kco_wc_order_id' ), $form, 60 * 60 * 24 );
wp_send_json_success();
wp_die();
}
}
Klarna_Checkout_For_WooCommerce_AJAX::init();

View File

@@ -0,0 +1,462 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Klarna_Checkout_For_WooCommerce_API_Callbacks class.
*
* Class that handles KCO API callbacks.
*/
class Klarna_Checkout_For_WooCommerce_API_Callbacks {
/**
* The reference the *Singleton* instance of this class.
*
* @var $instance
*/
protected static $instance;
/**
* Returns the *Singleton* instance of this class.
*
* @return self::$instance The *Singleton* instance.
*/
public static function get_instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Klarna_Checkout_For_WooCommerce_API_Callbacks constructor.
*/
public function __construct() {
add_action( 'woocommerce_api_kco_wc_push', array( $this, 'push_cb' ) );
add_action( 'woocommerce_api_kco_wc_notification', array( $this, 'notification_cb' ) );
// add_action( 'woocommerce_api_kco_wc_country_change', array( $this, 'country_change_cb' ) );
add_action( 'woocommerce_api_kco_wc_validation', array( $this, 'validation_cb' ) );
add_action( 'woocommerce_api_kco_wc_shipping_option_update', array( $this, 'shipping_option_update_cb' ) );
add_action( 'woocommerce_api_kco_wc_address_update', array( $this, 'address_update_cb' ) );
add_action( 'kco_wc_punted_notification', array( $this, 'kco_wc_punted_notification_cb' ), 10, 2 );
}
/**
* Push callback function.
*/
public function push_cb() {
/**
* 1. Handle POST request
* 2. Request the order from Klarna
* 3. Backup order creation
* 4. Acknowledge the order
* 5. Send merchant_reference1
*/
// Do nothing if there's no Klarna Checkout order ID.
if ( ! $_GET['kco_wc_order_id'] ) {
return;
}
$klarna_order_id = sanitize_key( $_GET['kco_wc_order_id'] );
$query_args = array(
'fields' => 'ids',
'post_type' => wc_get_order_types(),
'post_status' => array_keys( wc_get_order_statuses() ),
'meta_key' => '_wc_klarna_order_id',
'meta_value' => $klarna_order_id,
);
$orders = get_posts( $query_args );
// If zero matching orders were found, create backup order.
if ( empty( $orders ) ) {
// Backup order creation.
$this->backup_order_creation( $klarna_order_id );
return;
}
$order_id = $orders[0];
$order = wc_get_order( $order_id );
if ( $order ) {
// The order was already created. Check if order status was set (in thankyou page).
if ( ! $order->has_status( array( 'on-hold', 'processing', 'completed' ) ) ) {
$response = KCO_WC()->api->request_post_get_order( $klarna_order_id );
$klarna_order = json_decode( $response['body'] );
krokedil_log_events( $order_id, 'Klarna push callback. Updating order status.', $klarna_order );
if ( 'ACCEPTED' === $klarna_order->fraud_status ) {
$order->payment_complete( $klarna_order_id );
// translators: Klarna order ID.
$note = sprintf( __( 'Payment via Klarna Checkout, order ID: %s', 'klarna-checkout-for-woocommerce' ), sanitize_key( $klarna_order->order_id ) );
$order->add_order_note( $note );
} elseif ( 'REJECTED' === $klarna_order->fraud_status ) {
$order->update_status( 'on-hold', __( 'Klarna Checkout order was rejected.', 'klarna-checkout-for-woocommerce' ) );
} elseif ( 'PENDING' === $klarna_order->fraud_status ) {
// translators: Klarna order ID.
$note = sprintf( __( 'Klarna order is under review, order ID: %s.', 'klarna-checkout-for-woocommerce' ), sanitize_key( $klarna_order->order_id ) );
$order->update_status( 'on-hold', $note );
}
KCO_WC()->api->request_post_acknowledge_order( $klarna_order_id );
KCO_WC()->api->request_post_set_merchant_reference(
$klarna_order_id,
array(
'merchant_reference1' => $order->get_order_number(),
'merchant_reference2' => $order->get_id(),
)
);
} else {
krokedil_log_events( $order_id, 'Klarna push callback. Order status already set to On hold/Processing/Completed.', $klarna_order );
}
} else {
// Backup order creation.
$this->backup_order_creation( $klarna_order_id );
}
}
/**
* Notification callback function, used for pending orders.
*/
public function notification_cb() {
/**
* Notification callback URL has Klarna Order ID (kco_wc_order_id) in it.
*
* 1. Get Klarna Order ID
* 2. Try to find matching WooCommerce order, to see if it was created
* 3. If WooCommerce order does not exist, that means regular creation failed AND confirmation callback
* either hasn't happened yet or failed. In this case, schedule a single event, 5 minutes from now
* and try to get WooCommerce order then.
* 4. If WooCommerce order does exist, fire the hook.
*/
$order_id = '';
if ( $_GET['kco_wc_order_id'] ) { // KCO.
$klarna_order_id = sanitize_key( $_GET['kco_wc_order_id'] );
$query_args = array(
'fields' => 'ids',
'post_type' => wc_get_order_types(),
'post_status' => array_keys( wc_get_order_statuses() ),
'meta_key' => '_wc_klarna_order_id',
'meta_value' => $klarna_order_id,
);
$orders = get_posts( $query_args );
// If zero matching orders were found, return.
if ( ! empty( $orders ) ) {
$order_id = $orders[0];
}
}
if ( '' !== $order_id ) {
do_action( 'wc_klarna_notification_listener' );
} else {
$post_body = file_get_contents( 'php://input' );
$data = json_decode( $post_body, true );
krokedil_log_events( $order_id, 'Klarna notification callback data', $data );
wp_schedule_single_event( time() + 300, 'kco_wc_punted_notification', array( $klarna_order_id, $data ) );
}
}
/**
* Punted notification callback.
*
* @param string $klarna_order_id Klarna order ID.
*/
public function kco_wc_punted_notification_cb( $klarna_order_id, $data ) {
do_action( 'wc_klarna_notification_listener', $klarna_order_id, $data );
}
/**
* Order validation callback function.
* Response must be sent to Klarna API.
*
* @link https://developers.klarna.com/api/#checkout-api-callbacks-order-validation
*/
public function validation_cb() {
$post_body = file_get_contents( 'php://input' );
$data = json_decode( $post_body, true );
krokedil_log_events( null, 'Klarna validation callback data', $data );
$all_in_stock = true;
$shipping_chosen = false;
$form_data = get_transient( $data['order_id'] );
$has_required_data = true;
$failed_required_check = array();
foreach ( $form_data as $form_row ) {
if ( isset( $form_row['required'] ) && '' === $form_row['value'] ) {
$has_required_data = false;
wc_add_notice( 'test', 'error' );
$failed_required_check[] = $form_row['name'];
}
}
// Check stock for each item and shipping method.
$cart_items = $data['order_lines'];
foreach ( $cart_items as $cart_item ) {
if ( 'physical' === $cart_item['type'] ) {
// Get product by SKU or ID.
if ( wc_get_product_id_by_sku( $cart_item['reference'] ) ) {
$cart_item_product = wc_get_product( wc_get_product_id_by_sku( $cart_item['reference'] ) );
} else {
$cart_item_product = wc_get_product( $cart_item['reference'] );
}
if ( $cart_item_product ) {
if ( ! $cart_item_product->has_enough_stock( $cart_item['quantity'] ) ) {
$all_in_stock = false;
}
if( ! $cart_item_product->is_virtual() ) {
$needs_shipping = true;
}
}
} elseif ( 'shipping_fee' === $cart_item['type'] ) {
$shipping_chosen = true;
}
}
do_action( 'kco_validate_checkout', $data, $all_in_stock, $shipping_chosen );
if ( $all_in_stock && $shipping_chosen && $has_required_data ) {
header( 'HTTP/1.0 200 OK' );
} else {
header( 'HTTP/1.0 303 See Other' );
if ( ! $all_in_stock ) {
$logger = new WC_Logger();
$logger->add( 'klarna-checkout-for-woocommerce', 'Stock validation failed for SKU ' . $cart_item['reference'] );
header( 'Location: ' . wc_get_cart_url() . '?stock_validate_failed' );
} elseif ( ! $shipping_chosen && $needs_shipping ) {
header( 'Location: ' . wc_get_checkout_url() . '?no_shipping' );
} elseif ( ! $has_required_data ) {
$validation_hash = base64_encode( json_encode( $failed_required_check ) );
header( 'Location: ' . wc_get_checkout_url() . '?required_fields=' . $validation_hash );
}
}
}
/**
* Shipping option update callback function.
* Response must be sent to Klarna API.
*
* @link https://developers.klarna.com/api/#checkout-api-callbacks-shipping-option-update
*/
public function shipping_option_update_cb() {
// Send back order amount, order tax amount, order lines, purchase currency and status 200
}
/**
* Address update callback function.
* Response must be sent to Klarna API.
*
* @link https://developers.klarna.com/api/#checkout-api-callbacks-address-update
* @ref https://github.com/mmartche/coach/blob/30022c266089fc7499c54e149883e951c288dc9f/catalog/controller/extension/payment/klarna_checkout.php#L509
*/
public function address_update_cb() {
// Currently disabled, because of how response body needs to be calculated in WooCommerce.
}
/**
* Backup order creation, in case checkout process failed.
*
* @param string $klarna_order_id Klarna order ID.
*
* @throws Exception WC_Data_Exception.
*/
public function backup_order_creation( $klarna_order_id ) {
$response = KCO_WC()->api->request_post_get_order( $klarna_order_id );
$klarna_order = json_decode( $response['body'] );
// Process customer data.
$this->process_customer_data( $klarna_order );
// Process customer data.
$this->process_cart( $klarna_order );
// Process order.
$this->process_order( $klarna_order );
}
/**
* Processes customer data on backup order creation.
*
* @param Klarna_Checkout_Order $klarna_order Klarna order.
*
* @throws Exception WC_Data_Exception.
*/
private function process_customer_data( $klarna_order ) {
// First name.
WC()->customer->set_billing_first_name( sanitize_text_field( $klarna_order->billing_address->given_name ) );
WC()->customer->set_shipping_first_name( sanitize_text_field( $klarna_order->shipping_address->given_name ) );
// Last name.
WC()->customer->set_billing_last_name( sanitize_text_field( $klarna_order->billing_address->family_name ) );
WC()->customer->set_shipping_last_name( sanitize_text_field( $klarna_order->shipping_address->family_name ) );
// Country.
WC()->customer->set_billing_country( sanitize_text_field( $klarna_order->billing_address->country ) );
WC()->customer->set_shipping_country( sanitize_text_field( $klarna_order->shipping_address->country ) );
// Street address 1.
WC()->customer->set_billing_address_1( sanitize_text_field( $klarna_order->billing_address->street_address ) );
WC()->customer->set_shipping_address_1( sanitize_text_field( $klarna_order->shipping_address->street_address ) );
// Street address 2.
WC()->customer->set_billing_address_2( sanitize_text_field( $klarna_order->billing_address->street_address2 ) );
WC()->customer->set_shipping_address_2( sanitize_text_field( $klarna_order->shipping_address->street_address2 ) );
// City.
WC()->customer->set_billing_city( sanitize_text_field( $klarna_order->billing_address->city ) );
WC()->customer->set_shipping_city( sanitize_text_field( $klarna_order->shipping_address->city ) );
// County/State.
WC()->customer->set_billing_state( sanitize_text_field( $klarna_order->billing_address->region ) );
WC()->customer->set_shipping_state( sanitize_text_field( $klarna_order->shipping_address->region ) );
// Postcode.
WC()->customer->set_billing_postcode( sanitize_text_field( $klarna_order->billing_address->postal_code ) );
WC()->customer->set_shipping_postcode( sanitize_text_field( $klarna_order->shipping_address->postal_code ) );
// Phone.
WC()->customer->set_billing_phone( sanitize_text_field( $klarna_order->billing_address->phone ) );
// Email.
WC()->customer->set_billing_email( sanitize_text_field( $klarna_order->billing_address->email ) );
WC()->customer->save();
}
/**
* Processes cart contents on backup order creation.
*
* @param Klarna_Checkout_Order $klarna_order Klarna order.
*
* @throws Exception WC_Data_Exception.
*/
private function process_cart( $klarna_order ) {
WC()->cart->empty_cart();
foreach ( $klarna_order->order_lines as $cart_item ) {
if ( 'physical' === $cart_item->type ) {
if ( wc_get_product_id_by_sku( $cart_item->reference ) ) {
$id = wc_get_product_id_by_sku( $cart_item->reference );
} else {
$id = $cart_item->reference;
}
try {
WC()->cart->add_to_cart( $id, $cart_item->quantity );
} catch ( Exception $e ) {
$logger = new WC_Logger();
$logger->add( 'klarna-checkout-for-woocommerce', 'Backup order creation error add to cart error: ' . $e->getCode() . ' - ' . $e->getMessage() );
}
}
}
WC()->cart->calculate_shipping();
WC()->cart->calculate_fees();
WC()->cart->calculate_totals();
// Check cart items (quantity, coupon validity etc).
if ( ! WC()->cart->check_cart_items() ) {
return;
}
WC()->cart->check_cart_coupons();
}
/**
* Processes WooCommerce order on backup order creation.
*
* @param Klarna_Checkout_Order $klarna_order Klarna order.
*
* @throws Exception WC_Data_Exception.
*/
private function process_order( $klarna_order ) {
try {
$order = new WC_Order();
$order->set_billing_first_name( sanitize_text_field( $klarna_order->billing_address->given_name ) );
$order->set_billing_last_name( sanitize_text_field( $klarna_order->billing_address->family_name ) );
$order->set_billing_country( sanitize_text_field( $klarna_order->billing_address->country ) );
$order->set_billing_address_1( sanitize_text_field( $klarna_order->billing_address->street_address ) );
$order->set_billing_address_2( sanitize_text_field( $klarna_order->billing_address->street_address2 ) );
$order->set_billing_city( sanitize_text_field( $klarna_order->billing_address->city ) );
$order->set_billing_state( sanitize_text_field( $klarna_order->billing_address->region ) );
$order->set_billing_postcode( sanitize_text_field( $klarna_order->billing_address->postal_code ) );
$order->set_billing_phone( sanitize_text_field( $klarna_order->billing_address->phone ) );
$order->set_billing_email( sanitize_text_field( $klarna_order->billing_address->email ) );
$order->set_shipping_first_name( sanitize_text_field( $klarna_order->shipping_address->given_name ) );
$order->set_shipping_last_name( sanitize_text_field( $klarna_order->shipping_address->family_name ) );
$order->set_shipping_country( sanitize_text_field( $klarna_order->shipping_address->country ) );
$order->set_shipping_address_1( sanitize_text_field( $klarna_order->shipping_address->street_address ) );
$order->set_shipping_address_2( sanitize_text_field( $klarna_order->shipping_address->street_address2 ) );
$order->set_shipping_city( sanitize_text_field( $klarna_order->shipping_address->city ) );
$order->set_shipping_state( sanitize_text_field( $klarna_order->shipping_address->region ) );
$order->set_shipping_postcode( sanitize_text_field( $klarna_order->shipping_address->postal_code ) );
$order->set_created_via( 'klarna_checkout_backup_order_creation' );
$order->set_currency( sanitize_text_field( $klarna_order->purchase_currency ) );
$order->set_prices_include_tax( 'yes' === get_option( 'woocommerce_prices_include_tax' ) );
$order->set_payment_method( 'kco' );
$order->set_shipping_total( WC()->cart->get_shipping_total() );
$order->set_discount_total( WC()->cart->get_discount_total() );
$order->set_discount_tax( WC()->cart->get_discount_tax() );
$order->set_cart_tax( WC()->cart->get_cart_contents_tax() + WC()->cart->get_fee_tax() );
$order->set_shipping_tax( WC()->cart->get_shipping_tax() );
$order->set_total( WC()->cart->get_total( 'edit' ) );
WC()->checkout()->create_order_line_items( $order, WC()->cart );
WC()->checkout()->create_order_fee_lines( $order, WC()->cart );
WC()->checkout()->create_order_shipping_lines( $order, WC()->session->get( 'chosen_shipping_methods' ), WC()->shipping->get_packages() );
WC()->checkout()->create_order_tax_lines( $order, WC()->cart );
WC()->checkout()->create_order_coupon_lines( $order, WC()->cart );
$order->save();
if ( 'ACCEPTED' === $klarna_order->fraud_status ) {
$order->payment_complete( $klarna_order->order_id );
// translators: Klarna order ID.
$note = sprintf( __( 'Payment via Klarna Checkout, order ID: %s', 'klarna-checkout-for-woocommerce' ), sanitize_key( $klarna_order->order_id ) );
$order->add_order_note( $note );
} elseif ( 'REJECTED' === $klarna_order->fraud_status ) {
$order->update_status( 'on-hold', __( 'Klarna Checkout order was rejected.', 'klarna-checkout-for-woocommerce' ) );
} elseif ( 'PENDING' === $klarna_order->fraud_status ) {
// translators: Klarna order ID.
$note = sprintf( __( 'Klarna order is under review, order ID: %s.', 'klarna-checkout-for-woocommerce' ), sanitize_key( $klarna_order->order_id ) );
$order->update_status( 'on-hold', $note );
}
KCO_WC()->api->request_post_acknowledge_order( $klarna_order->order_id );
KCO_WC()->api->request_post_set_merchant_reference(
$klarna_order->order_id,
array(
'merchant_reference1' => $order->get_order_number(),
'merchant_reference2' => $order->get_id(),
)
);
if ( (int) round( $order->get_total() * 100 ) !== (int) $klarna_order->order_amount ) {
$order->update_status( 'on-hold', sprintf(__( 'Order needs manual review, WooCommerce total and Klarna total do not match. Klarna order total: %s.', 'klarna-checkout-for-woocommerce' ), $klarna_order->order_amount ) );
}
} catch ( Exception $e ) {
$logger = new WC_Logger();
$logger->add( 'klarna-checkout-for-woocommerce', 'Backup order creation error: ' . $e->getCode() . ' - ' . $e->getMessage() );
}
}
}
Klarna_Checkout_For_WooCommerce_API_Callbacks::get_instance();

View File

@@ -0,0 +1,703 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Klarna_Checkout_For_WooCommerce_API class.
*
* Class that talks to KCO API, wrapper for V2 and V3.
*/
class Klarna_Checkout_For_WooCommerce_API {
/**
* Klarna Checkout for WooCommerce settings.
*
* @var array
*/
private $settings = array();
/**
* Klarna_Checkout_For_WooCommerce_API constructor.
*/
public function __construct() {
$this->settings = get_option( 'woocommerce_kco_settings' );
}
/**
* Creates Klarna Checkout order.
*
* @return array|mixed|object|WP_Error
*/
public function request_pre_create_order() {
$request_url = $this->get_api_url_base() . 'checkout/v3/orders';
$request_args = array(
'headers' => $this->get_request_headers(),
'user-agent' => $this->get_user_agent(),
'body' => $this->get_request_body( 'create' ),
);
$log_array = array(
'headers' => $request_args['headers'],
'user-agent' => $request_args['user-agent'],
'body' => json_decode( $request_args['body'] ),
);
KCO_WC()->logger->log( 'Create Klarna order (' . $request_url . ') ' . json_encode( $request_args ) );
krokedil_log_events( null, 'Pre Create Order request args', $log_array );
$response = wp_safe_remote_post( $request_url, $request_args );
if ( is_wp_error( $response ) ) {
return $this->extract_error_messages( $response );
}
if ( $response['response']['code'] >= 200 && $response['response']['code'] <= 299 ) {
$klarna_order = json_decode( $response['body'] );
$this->save_order_id_to_session( sanitize_key( $klarna_order->order_id ) );
$this->save_order_api_to_session( $klarna_order );
$log_order = clone $klarna_order;
$log_order->html_snippet = '';
krokedil_log_events( null, 'Pre Create Order response', $log_order );
return $klarna_order;
} else if( $response['response']['code'] === 405 ) {
$error = $response['response']['message'];
return $error;
} else {
$error = $this->extract_error_messages( $response );
krokedil_log_events( null, 'Pre Create Order response', $error );
return $error;
}
}
/**
* Retrieve ongoing Klarna order.
*
* @param string $klarna_order_id Klarna order ID.
*
* @return object $klarna_order Klarna order.
*/
public function request_pre_retrieve_order( $klarna_order_id ) {
$request_url = $this->get_api_url_base() . 'checkout/v3/orders/' . $klarna_order_id;
$request_args = array(
'headers' => $this->get_request_headers(),
'user-agent' => $this->get_user_agent(),
);
krokedil_log_events( null, 'Pre Retrieve Order request args', $request_args );
KCO_WC()->logger->log( 'Retrieve ongoing Klarna order (' . $request_url . ') ' . json_encode( $request_args ) );
$response = wp_safe_remote_get( $request_url, $request_args );
if ( $response['response']['code'] >= 200 && $response['response']['code'] <= 299 ) {
$klarna_order = json_decode( $response['body'] );
$log_order = clone $klarna_order;
$log_order->html_snippet = '';
krokedil_log_events( null, 'Pre Retrieve Order response', $log_order );
return $klarna_order;
} else {
$error = $this->extract_error_messages( $response );
krokedil_log_events( null, 'Pre Retrieve Order response', $error );
return $error;
}
}
/**
* Update ongoing Klarna order.
*
* @return object $klarna_order Klarna order.
*/
public function request_pre_update_order() {
$klarna_order_id = $this->get_order_id_from_session();
$request_url = $this->get_api_url_base() . 'checkout/v3/orders/' . $klarna_order_id;
$request_args = array(
'headers' => $this->get_request_headers(),
'user-agent' => $this->get_user_agent(),
'body' => $this->get_request_body(),
);
$log_array = array(
'headers' => $request_args['headers'],
'user-agent' => $request_args['user-agent'],
'body' => json_decode( $request_args['body'] ),
);
// No update if nothing changed in data being sent to Klarna.
if ( WC()->session->get( 'kco_wc_update_md5' ) && WC()->session->get( 'kco_wc_update_md5' ) === md5( serialize( $request_args ) ) ) {
return;
}
krokedil_log_events( null, 'Pre Update Order request args', $log_array );
KCO_WC()->logger->log( 'Update ongoing Klarna order (' . $request_url . ') ' . json_encode( $request_args ) );
$response = wp_safe_remote_post( $request_url, $request_args );
if ( $response['response']['code'] >= 200 && $response['response']['code'] <= 299 ) {
WC()->session->set( 'kco_wc_update_md5', md5( serialize( $request_args ) ) );
$klarna_order = json_decode( $response['body'] );
$log_order = clone $klarna_order;
$log_order->html_snippet = '';
krokedil_log_events( null, 'Pre Update Order response', $log_order );
return $klarna_order;
} else {
WC()->session->__unset( 'kco_wc_update_md5' );
WC()->session->__unset( 'kco_wc_order_id' );
$error = $this->extract_error_messages( $response );
krokedil_log_events( null, 'Pre Update Order response', $error );
return $error;
}
}
/**
* Acknowledges Klarna Checkout order.
*
* @param string $klarna_order_id Klarna order ID.
*
* @return WP_Error|array $response
*/
public function request_post_get_order( $klarna_order_id ) {
$request_url = $this->get_api_url_base() . 'ordermanagement/v1/orders/' . $klarna_order_id;
$request_args = array(
'headers' => $this->get_request_headers(),
'user-agent' => $this->get_user_agent(),
);
$response = wp_safe_remote_get( $request_url, $request_args );
krokedil_log_events( null, 'Post Get Order response', stripslashes_deep( $response ) );
KCO_WC()->logger->log( 'Post Get Order response (' . $request_url . ') ' . stripslashes_deep( json_encode( $response ) ) );
return $response;
}
/**
* Acknowledges Klarna Checkout order.
*
* @param string $klarna_order_id Klarna order ID.
*
* @return WP_Error|array $response
*/
public function request_post_acknowledge_order( $klarna_order_id ) {
$request_url = $this->get_api_url_base() . 'ordermanagement/v1/orders/' . $klarna_order_id . '/acknowledge';
$request_args = array(
'headers' => $this->get_request_headers(),
'user-agent' => $this->get_user_agent(),
);
$response = wp_safe_remote_post( $request_url, $request_args );
return $response;
}
/**
* Adds WooCommerce order ID to Klarna order as merchant_reference. And clear Klarna order ID value from WC session.
*
* @param string $klarna_order_id Klarna order ID.
* @param array $merchant_references Array of merchant references.
*
* @return WP_Error|array $response
*/
public function request_post_set_merchant_reference( $klarna_order_id, $merchant_references ) {
$request_url = $this->get_api_url_base() . 'ordermanagement/v1/orders/' . $klarna_order_id . '/merchant-references';
$request_args = array(
'headers' => $this->get_request_headers(),
'user-agent' => $this->get_user_agent(),
'method' => 'PATCH',
'body' => wp_json_encode( array(
'merchant_reference1' => $merchant_references['merchant_reference1'],
'merchant_reference2' => $merchant_references['merchant_reference2'],
) ),
);
$response = wp_safe_remote_request( $request_url, $request_args );
return $response;
}
/**
* Gets Klarna API URL base.
*/
public function get_api_url_base() {
$base_location = wc_get_base_location();
$country_string = 'US' === $base_location['country'] ? '-na' : '';
$test_string = 'yes' === $this->settings['testmode'] ? '.playground' : '';
return 'https://api' . $country_string . $test_string . '.klarna.com/';
}
/**
* Gets Klarna order from WC_Session
*
* @return array|string
*/
public function get_order_id_from_session() {
return WC()->session->get( 'kco_wc_order_id' );
}
/**
* Saves Klarna order ID to WooCommerce session.
*
* @param string $order_id Klarna order ID.
*/
public function save_order_id_to_session( $order_id ) {
WC()->session->set( 'kco_wc_order_id', $order_id );
}
/**
* Saves Klarna order API to WooCommerce session.
*
* 'na' or 'eu', used for order updates, to avoid trying to switch API when updating KCO Global orders.
*
* @param string $klarna_order Klarna order.
*/
public function save_order_api_to_session() {
if ( 'US' === $this->get_purchase_country() ) {
$api = 'US';
} else {
$api = 'EU';
}
WC()->session->set( 'kco_wc_order_api', $api );
}
/**
* Gets Klarna Checkout order.
*
* If WC_Session value for Klarna order ID exists, attempt to retrieve that order.
* If this fails, create a new one and retrieve it.
* If WC_Session value for Klarna order ID does not exist, create a new order and retrieve it.
*
* @return Klarna_Checkout_Order $order Klarna order.
*/
public function get_order() {
$order_id = $this->get_order_id_from_session();
if ( $order_id ) {
$order = $this->request_pre_retrieve_order( $order_id );
if ( ! $order || is_wp_error( $order ) ) {
$order = $this->request_pre_create_order();
} elseif ( 'checkout_incomplete' === $order->status ) {
// Only update order if its status is incomplete.
$this->request_pre_update_order();
}
} else {
if ( is_order_received_page() ) {
return;
}
$order = $this->request_pre_create_order();
}
return $order;
}
/**
* Gets KCO iframe snippet from KCO order.
*
* @param Klarna_Order $order Klarna Checkout order.
*
* @return mixed
*/
public function get_snippet( $order ) {
if ( ! is_wp_error( $order ) ) {
$this->maybe_clear_session_values( $order );
return $order->html_snippet;
}
return $order->get_error_message();
}
/**
* Clear WooCommerce session values if Klarna Checkout order is completed.
*
* @param Klarna_Order $order Klarna Checkout order.
*/
public function maybe_clear_session_values( $order ) {
if ( 'checkout_complete' === $order->status ) {
WC()->session->__unset( 'kco_wc_update_md5' );
WC()->session->__unset( 'kco_wc_order_id' );
WC()->session->__unset( 'kco_wc_order_notes' );
WC()->session->__unset( 'kco_wc_order_api' );
WC()->session->__unset( 'kco_wc_extra_fields_values' );
WC()->session->__unset( 'kco_wc_prefill_consent' );
}
}
/**
* Gets Klarna merchant ID.
*
* @return string
*/
public function get_merchant_id() {
$credentials = KCO_WC()->credentials->get_credentials_from_session();
return $credentials['merchant_id'];
}
/**
* Gets Klarna shared secret.
*
* @return string
*/
public function get_shared_secret() {
$credentials = KCO_WC()->credentials->get_credentials_from_session();
return $credentials['shared_secret'];
}
/**
* Gets country for Klarna purchase.
*
* @return string
*/
public function get_purchase_country() {
$base_location = wc_get_base_location();
$country = $base_location['country'];
return $country;
}
/**
* Gets currency for Klarna purchase.
*
* @return string
*/
public function get_purchase_currency() {
return get_woocommerce_currency();
}
/**
* Gets locale for Klarna purchase.
*
* @return string
*/
public function get_purchase_locale() {
if ( defined( 'ICL_LANGUAGE_CODE' ) ) {
$locale = ICL_LANGUAGE_CODE;
} else {
$locale = explode('_', get_locale());
$locale = $locale[0];
}
switch ( WC()->checkout()->get_value( 'billing_country' ) ) {
case 'AT':
if ( 'de' == $locale ) {
$klarna_locale = 'de-at';
} else {
$klarna_locale = 'en-at';
}
break;
case 'DE':
if ( 'de' == $locale ) {
$klarna_locale = 'de-de';
} else {
$klarna_locale = 'en-de';
}
break;
case 'DK':
if ( 'da' == $locale ) {
$klarna_locale = 'da-dk';
} else {
$klarna_locale = 'en-dk';
}
break;
case 'FI':
if ( 'fi' == $locale ) {
$klarna_locale = 'fi-fi';
} elseif( 'sv' == $locale ) {
$klarna_locale = 'sv-fi';
} else {
$klarna_locale = 'en-fi';
}
break;
case 'NL':
if ( 'nl' == $locale ) {
$klarna_locale = 'nl-nl';
} else {
$klarna_locale = 'en-nl';
}
break;
case 'NO':
if ( 'nb' == $locale || 'nn' == $locale ) {
$klarna_locale = 'nb-no';
} else {
$klarna_locale = 'en-no';
}
break;
case 'SE':
if ( 'sv' == $locale ) {
$klarna_locale = 'sv-se';
} else {
$klarna_locale = 'en-se';
}
break;
case 'GB':
$klarna_locale = 'en-gb';
break;
case 'US':
$klarna_locale = 'en-us';
break;
default:
$klarna_locale = 'en-us';
} // End switch().
return $klarna_locale;
}
/**
* Gets merchant URLs for Klarna purchase.
*
* @return array
*/
public function get_merchant_urls() {
return KCO_WC()->merchant_urls->get_urls();
}
/**
* Gets Klarna API request headers.
*
* @return array
*/
public function get_request_headers() {
$request_headers = array(
'Authorization' => 'Basic ' . base64_encode( $this->get_merchant_id() . ':' . $this->get_shared_secret() ),
'Content-Type' => 'application/json',
);
return $request_headers;
}
/**
* Gets Klarna API request headers.
*
* @return string
*/
public function get_user_agent() {
$user_agent = apply_filters( 'http_headers_useragent', 'WordPress/' . get_bloginfo( 'version' ) . '; ' . get_bloginfo( 'url' ) ) . ' - KCO:' . KCO_WC_VERSION . ' - PHP Version: ' . phpversion() . ' - Krokedil';
return $user_agent;
}
/**
* Gets Klarna API request body.
*
* @TODO: Clean this up by using maybe_add functions.
*
* @param string $request_type Type of request.
*
* @return false|string
*/
public function get_request_body( $request_type = null ) {
KCO_WC()->order_lines->process_data();
$request_args = array(
'purchase_country' => $this->get_purchase_country(),
'purchase_currency' => $this->get_purchase_currency(),
'locale' => $this->get_purchase_locale(),
'merchant_urls' => $this->get_merchant_urls(),
'order_amount' => KCO_WC()->order_lines->get_order_amount(),
'order_tax_amount' => KCO_WC()->order_lines->get_order_tax_amount(),
'order_lines' => KCO_WC()->order_lines->get_order_lines(),
'shipping_countries' => $this->get_shipping_countries(),
);
if ( kco_wc_prefill_allowed() ) {
$request_args['billing_address'] = array(
'email' => WC()->checkout()->get_value( 'billing_email' ),
'postal_code' => WC()->checkout()->get_value( 'billing_postcode' ),
'country' => WC()->checkout()->get_value( 'billing_country' ),
'phone' => WC()->checkout()->get_value( 'billing_phone' ),
'given_name' => WC()->checkout()->get_value( 'billing_first_name' ),
'family_name' => WC()->checkout()->get_value( 'billing_last_name' ),
'street_address' => WC()->checkout()->get_value( 'billing_address_1' ),
'street_address2' => WC()->checkout()->get_value( 'billing_address_2' ),
'city' => WC()->checkout()->get_value( 'billing_city' ),
'region' => WC()->checkout()->get_value( 'billing_state' ),
);
}
$request_args['options']['title_mandatory'] = $this->get_title_mandatory();
$request_args['options'] = array();
$request_args['options']['allow_separate_shipping_address'] = $this->get_allow_separate_shipping_address();
$request_args['options']['date_of_birth_mandatory'] = $this->get_dob_mandatory();
$request_args['options']['national_identification_number_mandatory'] = $this->get_dob_mandatory();
$request_args['options']['title_mandatory'] = $this->get_title_mandatory();
if ( $this->get_iframe_colors() ) {
$request_args['options'] = array_merge( $request_args['options'], $this->get_iframe_colors() );
}
if ( $this->get_shipping_details() ) {
$request_args['options']['shipping_details'] = $this->get_shipping_details();
}
// Allow external payment method plugin to do its thing.
// @TODO: Extract this into a hooked function.
if ( 'create' === $request_type ) {
if ( in_array( $this->get_purchase_country(), array( 'SE', 'NO', 'FI' ), true ) ) {
if ( isset( $this->settings['allowed_customer_types'] ) ) {
$customer_types_setting = $this->settings['allowed_customer_types'];
switch ( $customer_types_setting ) {
case 'B2B':
$allowed_customer_types = array( 'organization' );
$customer_type = 'organization';
break;
case 'B2BC':
$allowed_customer_types = array( 'person', 'organization' );
$customer_type = 'organization';
break;
case 'B2CB':
$allowed_customer_types = array( 'person', 'organization' );
$customer_type = 'person';
break;
default:
$allowed_customer_types = array( 'person' );
$customer_type = 'person';
}
$request_args['options']['allowed_customer_types'] = $allowed_customer_types;
$request_args['customer']['type'] = $customer_type;
}
}
$request_args = apply_filters( 'kco_wc_create_order', $request_args );
}
$request_body = wp_json_encode( apply_filters( 'kco_wc_api_request_args', $request_args ) );
return $request_body;
}
/**
* Gets shipping countries formatted for Klarna.
*
* @return array
*/
public function get_shipping_countries() {
$wc_countries = new WC_Countries();
return array_keys( $wc_countries->get_shipping_countries() );
}
/**
* Gets allowed separate shipping details option.
*
* @return bool
*/
public function get_allow_separate_shipping_address() {
$allow_separate_shipping = array_key_exists( 'allow_separate_shipping', $this->settings ) && 'yes' === $this->settings['allow_separate_shipping'];
return $allow_separate_shipping;
}
/**
* Gets date of birth mandatory option.
*
* @return bool
*/
public function get_dob_mandatory() {
$dob_mandatory = array_key_exists( 'dob_mandatory', $this->settings ) && 'yes' === $this->settings['dob_mandatory'];
return $dob_mandatory;
}
/**
* Gets date of birth mandatory option.
*
* @return bool
*/
public function get_title_mandatory() {
$title_mandatory = array_key_exists( 'title_mandatory', $this->settings ) && 'yes' === $this->settings['title_mandatory'];
return $title_mandatory;
}
/**
* Gets shipping details note.
*
* @return bool
*/
public function get_shipping_details() {
if ( array_key_exists( 'shipping_details', $this->settings ) ) {
return $this->settings['shipping_details'];
}
return false;
}
/**
* Gets iframe color settings.
*
* @return array|bool
*/
private function get_iframe_colors() {
$color_settings = array();
if ( $this->check_option_field( 'color_button' ) ) {
$color_settings['color_button'] = $this->check_option_field( 'color_button' );
}
if ( $this->check_option_field( 'color_button_text' ) ) {
$color_settings['color_button_text'] = $this->check_option_field( 'color_button_text' );
}
if ( $this->check_option_field( 'color_checkbox' ) ) {
$color_settings['color_checkbox'] = $this->check_option_field( 'color_checkbox' );
}
if ( $this->check_option_field( 'color_checkbox_checkmark' ) ) {
$color_settings['color_checkbox_checkmark'] = $this->check_option_field( 'color_checkbox_checkmark' );
}
if ( $this->check_option_field( 'color_header' ) ) {
$color_settings['color_header'] = $this->check_option_field( 'color_header' );
}
if ( $this->check_option_field( 'color_link' ) ) {
$color_settings['color_link'] = $this->check_option_field( 'color_link' );
}
if ( $this->check_option_field( 'radius_border' ) ) {
$color_settings['radius_border'] = $this->check_option_field( 'radius_border' );
}
if ( count( $color_settings ) > 0 ) {
return $color_settings;
}
return false;
}
private function check_option_field( $field ) {
if ( array_key_exists( $field, $this->settings ) && '' !== $this->settings[ $field ] ) {
return $this->settings[ $field ];
}
return false;
}
/**
* Extracts error messages from Klarna's response.
*
* @param mixed $response Klarna API response.
*
* @return mixed
*/
private function extract_error_messages( $response ) {
if ( is_wp_error( $response ) ) {
return $response;
}
$response_body = json_decode( $response['body'] );
$error = new WP_Error();
if ( ! empty( $response_body->error_messages ) && is_array( $response_body->error_messages ) ) {
KCO_WC()->logger->log( var_export( $response_body, true ) );
foreach ( $response_body->error_messages as $error_message ) {
$error->add( 'kco', $error_message );
}
}
return $error;
}
}

View File

@@ -0,0 +1,285 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Klarna_Checkout_For_WooCommerce_Confirmation class.
*
* Handles Klarna Checkout confirmation page.
*/
class Klarna_Checkout_For_WooCommerce_Confirmation {
/**
* The reference the *Singleton* instance of this class.
*
* @var $instance
*/
protected static $instance;
/**
* Returns the *Singleton* instance of this class.
*
* @return self::$instance The *Singleton* instance.
*/
public static function get_instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Klarna_Checkout_For_WooCommerce_Confirmation constructor.
*/
public function __construct() {
add_action( 'wp_head', array( $this, 'maybe_hide_checkout_form' ) );
add_action( 'woocommerce_before_checkout_form', array( $this, 'maybe_populate_wc_checkout' ) );
add_action( 'wp_footer', array( $this, 'maybe_submit_wc_checkout' ), 999 );
add_filter( 'the_title', array( $this, 'confirm_page_title' ) );
add_filter( 'woocommerce_checkout_fields', array( $this, 'unrequire_fields' ), 99 );
add_filter( 'woocommerce_checkout_posted_data', array( $this, 'unrequire_posted_data' ), 99 );
add_action( 'woocommerce_checkout_after_order_review', array( $this, 'add_kco_order_id_field' ) );
add_action( 'woocommerce_checkout_create_order', array( $this, 'save_kco_order_id_field' ), 10, 2 );
}
/**
* Filter Checkout page title in confirmation page.
*
* @param $title
*
* @return string
*/
public function confirm_page_title( $title ) {
if ( ! is_admin() && is_main_query() && in_the_loop() && is_page() && is_checkout() && isset( $_GET['confirm'] ) && 'yes' === $_GET['confirm'] ) {
$title = __( 'Please wait while we process your order.', 'klarna-checkout-for-woocommerce' );
remove_filter( 'the_title', array( $this, 'confirm_page_title' ) );
}
return $title;
}
/**
* Hides WooCommerce checkout form in KCO confirmation page.
*/
public function maybe_hide_checkout_form() {
if ( ! $this->is_kco_confirmation() ) {
return;
}
echo '<style>form.woocommerce-checkout,div.woocommerce-info{display:none!important}</style>';
}
/**
* Populates WooCommerce checkout form in KCO confirmation page.
*/
public function maybe_populate_wc_checkout( $checkout ) {
if ( ! $this->is_kco_confirmation() ) {
return;
}
echo '<div id="kco-confirm-loading"></div>';
$klarna_order_id = WC()->session->get( 'kco_wc_order_id' );
$response = KCO_WC()->api->request_post_get_order( $klarna_order_id );
$klarna_order = apply_filters( 'kco_wc_klarna_order_pre_submit', json_decode( $response['body'] ) );
$this->save_customer_data( $klarna_order );
}
/**
* Submits WooCommerce checkout form in KCO confirmation page.
*/
public function maybe_submit_wc_checkout() {
if ( ! $this->is_kco_confirmation() ) {
return;
}
?>
<script>
jQuery(function ($) {
$('input#terms').prop('checked', true);
// If order value = 0, payment method fields will not be in the page, so we need to
if (!$('input#payment_method_kco').length) {
$('#order_review').append('<input id="payment_method_kco" type="radio" class="input-radio" name="payment_method" value="kco" checked="checked" />');
}
$('input#payment_method_kco').prop('checked', true);
<?php
$extra_field_values = WC()->session->get( 'kco_wc_extra_fields_values', array() );
foreach ( $extra_field_values as $field_name => $field_value ) { ?>
var elementName = "<?php echo $field_name; ?>";
var elementValue = <?php echo wp_json_encode( $field_value ); ?>;
var element = $('*[name="' + elementName + '"]');
console.log(elementName);
console.log(elementValue);
console.log(element);
console.log(element.type);
if (element.length) {
if (element.is('select')) { // Select.
var selectedOption = element.find('option[value="' + elementValue + '"]');
selectedOption.prop('selected', true);
} else if ('radio' === element.get(0).type) { // Radio.
var checkedRadio = $('*[name="' + elementName + '"][value="' + elementValue + '"]');
checkedRadio.prop('checked', true);
} else if ('checkbox' === element.get(0).type) { // Checkbox.
if (elementValue) {
element.prop('checked', true);
}
} else { // Text and textarea.
element.val(elementValue);
}
}
<?php
}
do_action( 'kco_wc_before_submit' );
?>
$('.validate-required').removeClass('validate-required');
$('form.woocommerce-checkout').submit();
});
</script>
<?php
}
/**
* Checks if in KCO confirmation page.
*
* @return bool
*/
private function is_kco_confirmation() {
if ( isset( $_GET['confirm'] ) && 'yes' === $_GET['confirm'] && isset( $_GET['kco_wc_order_id'] ) ) {
return true;
}
return false;
}
/**
* Saves customer data from Klarna order into WC()->customer.
*
* @param $klarna_order
*/
private function save_customer_data( $klarna_order ) {
// First name.
WC()->customer->set_billing_first_name( sanitize_text_field( $klarna_order->billing_address->given_name ) );
WC()->customer->set_shipping_first_name( sanitize_text_field( $klarna_order->shipping_address->given_name ) );
// Last name.
WC()->customer->set_billing_last_name( sanitize_text_field( $klarna_order->billing_address->family_name ) );
WC()->customer->set_shipping_last_name( sanitize_text_field( $klarna_order->shipping_address->family_name ) );
// Country.
WC()->customer->set_billing_country( strtoupper( sanitize_text_field( $klarna_order->billing_address->country ) ) );
WC()->customer->set_shipping_country( strtoupper( sanitize_text_field( $klarna_order->shipping_address->country ) ) );
// Street address 1.
WC()->customer->set_billing_address_1( sanitize_text_field( $klarna_order->billing_address->street_address ) );
WC()->customer->set_shipping_address_1( sanitize_text_field( $klarna_order->shipping_address->street_address ) );
// Street address 2.
if ( isset( $klarna_order->billing_address->street_address2 ) ) {
WC()->customer->set_billing_address_2( sanitize_text_field( $klarna_order->billing_address->street_address2 ) );
WC()->customer->set_shipping_address_2( sanitize_text_field( $klarna_order->shipping_address->street_address2 ) );
}
// City.
WC()->customer->set_billing_city( sanitize_text_field( $klarna_order->billing_address->city ) );
WC()->customer->set_shipping_city( sanitize_text_field( $klarna_order->shipping_address->city ) );
// County/State.
WC()->customer->set_billing_state( sanitize_text_field( $klarna_order->billing_address->region ) );
WC()->customer->set_shipping_state( sanitize_text_field( $klarna_order->shipping_address->region ) );
// Postcode.
WC()->customer->set_billing_postcode( sanitize_text_field( $klarna_order->billing_address->postal_code ) );
WC()->customer->set_shipping_postcode( sanitize_text_field( $klarna_order->shipping_address->postal_code ) );
// Phone.
WC()->customer->set_billing_phone( sanitize_text_field( $klarna_order->billing_address->phone ) );
// Email.
WC()->customer->set_billing_email( sanitize_text_field( $klarna_order->billing_address->email ) );
WC()->customer->save();
}
/**
* When checking out using KCO, we need to make sure none of the WooCommerce are required, in case Klarna
* does not return info for some of them.
*
* @param array $fields WooCommerce checkout fields.
*
* @return mixed
*/
public function unrequire_fields( $fields ) {
if ( 'kco' === WC()->session->get( 'chosen_payment_method' ) ) {
foreach ( $fields as $fieldset_key => $fieldset ) {
foreach ( $fieldset as $key => $field ) {
$fields[ $fieldset_key ][ $key ]['required'] = '';
$fields[ $fieldset_key ][ $key ]['wooccm_required'] = '';
}
}
}
return $fields;
}
/**
* Makes sure there's no empty data sent for validation.
*
* @param array $data Posted data.
*
* @return mixed
*/
public function unrequire_posted_data( $data ) {
if ( 'kco' === WC()->session->get( 'chosen_payment_method' ) ) {
foreach ( $data as $key => $value ) {
if ( '' === $value ) {
unset( $data[ $key ] );
}
}
}
return $data;
}
/**
* Adds hidden field to WooCommerce checkout form, holding Klarna Checkout order ID.
*/
public function add_kco_order_id_field() {
if ( 'kco' === WC()->session->get( 'chosen_payment_method' ) && isset( $_GET['confirm'] ) && 'yes' === $_GET['confirm'] ) {
if ( isset( $_GET['kco_wc_order_id'] ) ) { // Input var okay.
$klarna_order_id = esc_attr( sanitize_text_field( $_GET['kco_wc_order_id'] ) );
echo '<input type="hidden" id="kco_order_id" name="kco_order_id" value="' . $klarna_order_id . '" />';
}
}
}
/**
* Saves KCO order ID to WooCommerce order as meta field.
*
* @param WC_Order $order WooCommerce order.
* @param array $data Posted data.
*/
public function save_kco_order_id_field( $order, $data ) {
if ( isset( $_POST['kco_order_id'] ) ) {
$kco_order_id = sanitize_text_field( $_POST['kco_order_id'] );
update_post_meta( $order->get_id(), '_wc_klarna_order_id', sanitize_key( $kco_order_id ) );
update_post_meta( $order->get_id(), '_transaction_id', sanitize_key( $kco_order_id ) );
}
}
}
Klarna_Checkout_For_WooCommerce_Confirmation::get_instance();

View File

@@ -0,0 +1,327 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Klarna_Checkout_For_WooCommerce_Create_Local_Order_Fallback class.
*
* Class that handles fallback order creation in Woocommerce if checkout form submission fails.
*/
class Klarna_Checkout_For_WooCommerce_Create_Local_Order_Fallback {
/**
* The reference the *Singleton* instance of this class.
*
* @var $instance
*/
protected static $instance;
/**
* Returns the *Singleton* instance of this class.
*
* @return self::$instance The *Singleton* instance.
*/
public static function get_instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Klarna_Checkout_For_WooCommerce_Create_Local_Order_Fallback constructor.
*/
public function __construct() {
}
/**
* Fallbck order creation, in case checkout process failed.
*
* @param string $klarna_order_id Klarna order ID.
*
* @throws Exception WC_Data_Exception.
*/
public static function create( $klarna_order_id ) {
$response = KCO_WC()->api->request_post_get_order( $klarna_order_id );
$klarna_order = json_decode( $response['body'] );
// Create order
$order = wc_create_order();
try {
$order_id = $order->get_id();
$cart_hash = md5( json_encode( wc_clean( WC()->cart->get_cart_for_session() ) ) . WC()->cart->total );
$order->set_created_via( 'checkout' );
$order->set_cart_hash( $cart_hash );
$order->set_customer_id( apply_filters( 'woocommerce_checkout_customer_id', get_current_user_id() ) );
$order->set_currency( get_woocommerce_currency() );
$order->set_prices_include_tax( 'yes' === get_option( 'woocommerce_prices_include_tax' ) );
$order->set_customer_ip_address( WC_Geolocation::get_ip_address() );
$order->set_customer_user_agent( wc_get_user_agent() );
$order->set_shipping_total( WC()->cart->shipping_total );
$order->set_discount_total( WC()->cart->get_cart_discount_total() );
$order->set_discount_tax( WC()->cart->get_cart_discount_tax_total() );
$order->set_cart_tax( WC()->cart->tax_total );
$order->set_shipping_tax( WC()->cart->shipping_tax_total );
$order->set_total( WC()->cart->total );
update_post_meta( $order_id, '_created_via_klarna_fallback', 'yes' );
//Add payment method
self::add_order_payment_method( $order );
// Process customer data.
self::process_customer_data( $order, $klarna_order );
// Process customer data.
self::create_order_line_items( $order, WC()->cart );
//Add fees to order.
self::create_order_fee_lines( $order, WC()->cart );
// Add shipping
self::create_order_shipping_lines( $order );
// Tax
self::create_order_tax_lines( $order, WC()->cart );
// Coupons
self::create_order_coupon_lines( $order, WC()->cart );
// Save the order.
$order->save();
return $order;
} catch ( Exception $e ) {
return new WP_Error( 'checkout-error', $e->getMessage() );
}
}
/**
* Set payment method.
*
* @param WC_Order $order WooCommerce order.
*
*/
public static function add_order_payment_method( $order ) {
$available_gateways = WC()->payment_gateways->payment_gateways();
$payment_method = $available_gateways['kco'];
$order->set_payment_method( $payment_method );
}
/**
* Processes customer data on fallback order creation.
*
* @param WC_Order $order WooCommerce order.
* @param Klarna_Checkout_Order $klarna_order Klarna order.
*
* @throws Exception WC_Data_Exception.
*/
private static function process_customer_data( $order, $klarna_order ) {
// First name.
$order->set_billing_first_name( sanitize_text_field( $klarna_order->billing_address->given_name ) );
$order->set_shipping_first_name( sanitize_text_field( $klarna_order->shipping_address->given_name ) );
// Last name.
$order->set_billing_last_name( sanitize_text_field( $klarna_order->billing_address->family_name ) );
$order->set_shipping_last_name( sanitize_text_field( $klarna_order->shipping_address->family_name ) );
// Country.
$order->set_billing_country( sanitize_text_field( $klarna_order->billing_address->country ) );
$order->set_shipping_country( sanitize_text_field( $klarna_order->shipping_address->country ) );
// Street address 1.
$order->set_billing_address_1( sanitize_text_field( $klarna_order->billing_address->street_address ) );
$order->set_shipping_address_1( sanitize_text_field( $klarna_order->shipping_address->street_address ) );
// Street address 2.
$order->set_billing_address_2( sanitize_text_field( $klarna_order->billing_address->street_address2 ) );
$order->set_shipping_address_2( sanitize_text_field( $klarna_order->shipping_address->street_address2 ) );
// City.
$order->set_billing_city( sanitize_text_field( $klarna_order->billing_address->city ) );
$order->set_shipping_city( sanitize_text_field( $klarna_order->shipping_address->city ) );
// County/State.
$order->set_billing_state( sanitize_text_field( $klarna_order->billing_address->region ) );
$order->set_shipping_state( sanitize_text_field( $klarna_order->shipping_address->region ) );
// Postcode.
$order->set_billing_postcode( sanitize_text_field( $klarna_order->billing_address->postal_code ) );
$order->set_shipping_postcode( sanitize_text_field( $klarna_order->shipping_address->postal_code ) );
// Phone.
$order->set_billing_phone( sanitize_text_field( $klarna_order->billing_address->phone ) );
// Email.
$order->set_billing_email( sanitize_text_field( $klarna_order->billing_address->email ) );
}
/**
* Processes cart contents on fallback order creation.
*
* @paramWC_Order $order WooCommerce order.
*
* @throws Exception WC_Data_Exception.
*/
private static function create_order_line_items( &$order, $cart ) {
// Remove items as to stop the item lines from being duplicated.
$order->remove_order_items();
foreach ( $cart->get_cart() as $cart_item_key => $values ) {
$product = $values['data'];
$item = new WC_Order_Item_Product();
$item->legacy_values = $values; // @deprecated For legacy actions.
$item->legacy_cart_item_key = $cart_item_key; // @deprecated For legacy actions.
$item->set_props( array(
'quantity' => $values['quantity'],
'variation' => $values['variation'],
'subtotal' => $values['line_subtotal'],
'total' => $values['line_total'],
'subtotal_tax' => $values['line_subtotal_tax'],
'total_tax' => $values['line_tax'],
'taxes' => $values['line_tax_data'],
) );
if ( $product ) {
$item->set_props( array(
'name' => $product->get_name(),
'tax_class' => $product->get_tax_class(),
'product_id' => $product->is_type( 'variation' ) ? $product->get_parent_id() : $product->get_id(),
'variation_id' => $product->is_type( 'variation' ) ? $product->get_id() : 0,
) );
}
$item->set_backorder_meta();
/**
* Action hook to adjust item before save.
* @since 3.0.0
*/
do_action( 'woocommerce_checkout_create_order_line_item', $item, $cart_item_key, $values, $order );
// Add item to order and save.
$order->add_item( $item );
}
}
/**
* Add fees to the order.
*
* @param WC_Order $order
*/
public static function create_order_fee_lines( &$order, $cart ) {
foreach ( $cart->get_fees() as $fee_key => $fee ) {
$item = new WC_Order_Item_Fee();
$item->legacy_fee = $fee; // @deprecated For legacy actions.
$item->legacy_fee_key = $fee_key; // @deprecated For legacy actions.
$item->set_props( array(
'name' => $fee->name,
'tax_class' => $fee->taxable ? $fee->tax_class : 0,
'total' => $fee->amount,
'total_tax' => $fee->tax,
'taxes' => array(
'total' => $fee->tax_data,
),
) );
/**
* Action hook to adjust item before save.
* @since 3.0.0
*/
do_action( 'woocommerce_checkout_create_order_fee_item', $item, $fee_key, $fee, $order );
// Add item to order and save.
$order->add_item( $item );
}
}
/**
* Add shipping lines to the order.
*
* @param WC_Order $order
*/
public static function create_order_shipping_lines( &$order ) {
if ( ! defined( 'WOOCOMMERCE_CART' ) ) {
define( 'WOOCOMMERCE_CART', true );
}
$order_id = $order->get_id() ;
$this_shipping_methods = WC()->session->get( 'chosen_shipping_methods' );
WC()->cart->calculate_shipping();
// Store shipping for all packages.
foreach ( WC()->shipping->get_packages() as $package_key => $package ) {
if ( isset( $package['rates'][ $this_shipping_methods[ $package_key ] ] ) ) {
$item_id = $order->add_shipping( $package['rates'][ $this_shipping_methods[ $package_key ] ] );
if ( ! $item_id ) {
KCO_WC()->logger->log( 'Error: Unable to add shipping item in Create Local Order Fallback.' );
krokedil_log_events( null, 'Error: Unable to add shipping item in Create Local Order Fallback.', '' );
}
// Allows plugins to add order item meta to shipping.
do_action( 'woocommerce_add_shipping_order_item', $order_id, $item_id, $package_key );
}
}
}
/**
* Add tax lines to the order.
*
* @param WC_Order $order
*/
public static function create_order_tax_lines( &$order, $cart ) {
foreach ( array_keys( $cart->taxes + $cart->shipping_taxes ) as $tax_rate_id ) {
if ( $tax_rate_id && apply_filters( 'woocommerce_cart_remove_taxes_zero_rate_id', 'zero-rated' ) !== $tax_rate_id ) {
$item = new WC_Order_Item_Tax();
$item->set_props( array(
'rate_id' => $tax_rate_id,
'tax_total' => $cart->get_tax_amount( $tax_rate_id ),
'shipping_tax_total' => $cart->get_shipping_tax_amount( $tax_rate_id ),
'rate_code' => WC_Tax::get_rate_code( $tax_rate_id ),
'label' => WC_Tax::get_rate_label( $tax_rate_id ),
'compound' => WC_Tax::is_compound( $tax_rate_id ),
) );
/**
* Action hook to adjust item before save.
* @since 3.0.0
*/
do_action( 'woocommerce_checkout_create_order_tax_item', $item, $tax_rate_id, $order );
// Add item to order and save.
$order->add_item( $item );
}
}
}
/**
* Add coupon lines to the order.
*
* @param WC_Order $order
*/
public static function create_order_coupon_lines( &$order, $cart ) {
foreach ( $cart->get_coupons() as $code => $coupon ) {
$item = new WC_Order_Item_Coupon();
$item->set_props( array(
'code' => $code,
'discount' => $cart->get_coupon_discount_amount( $code ),
'discount_tax' => $cart->get_coupon_discount_tax_amount( $code ),
) );
/**
* Action hook to adjust item before save.
* @since 3.0.0
*/
do_action( 'woocommerce_checkout_create_order_coupon_item', $item, $code, $coupon, $order );
// Add item to order and save.
$order->add_item( $item );
}
}
}
Klarna_Checkout_For_WooCommerce_Create_Local_Order_Fallback::get_instance();

View File

@@ -0,0 +1,65 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Klarna_Checkout_For_WooCommerce_Credentials class.
*
* Gets correct credentials based on customer country, store country and test/live mode.
*/
class Klarna_Checkout_For_WooCommerce_Credentials {
/**
* Klarna Checkout for WooCommerce settings.
*
* @var $settings
*/
public $settings = array();
/**
* Klarna_Checkout_For_WooCommerce_Credentials constructor.
*/
public function __construct() {
$this->settings = get_option( 'woocommerce_kco_settings' );
}
/**
* Gets Klarna API credentials (merchant ID and shared secret) from user session.
*
* @return bool|array $credentials
*/
public function get_credentials_from_session() {
$base_location = wc_get_base_location();
if ( 'US' === $base_location['country'] ) {
$country_string = 'us';
} else {
$country_string = 'eu';
}
$test_string = 'yes' === $this->settings['testmode'] ? 'test_' : '';
$merchant_id = $this->settings[ $test_string . 'merchant_id_' . $country_string ];
$shared_secret = $this->settings[ $test_string . 'shared_secret_' . $country_string ];
// Merchant id and/or shared secret not found for matching country.
if ( '' === $merchant_id || '' === $shared_secret ) {
return false;
}
$credentials = array(
'merchant_id' => $this->settings[ $test_string . 'merchant_id_' . $country_string ],
'shared_secret' => htmlspecialchars_decode( $this->settings[ $test_string . 'shared_secret_' . $country_string ] ),
);
return $credentials;
}
/**
* Gets Klarna API credentials (merchant ID and shared secret) from a completed WC order.
*/
public function get_credentials_from_order() {
}
}

View File

@@ -0,0 +1,88 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Klarna_Checkout_For_WooCommerce_Extra_Checkout_Fields class.
*
* Class that handles extra checkout fields displayed when Klarna Checkout is the selected payment option.
*/
class Klarna_Checkout_For_WooCommerce_Extra_Checkout_Fields {
/**
* Returns array of all WooCommerce Checkout fields.
*/
public function get_all_checkout_fields() {
$default_billing_fields = WC()->checkout()->get_checkout_fields( 'billing' );
$default_shipping_fields = WC()->checkout()->get_checkout_fields( 'shipping' );
$default_account_fields = WC()->checkout()->get_checkout_fields( 'account' );
$default_order_fields = WC()->checkout()->get_checkout_fields( 'order' );
return array(
'billing' => $default_billing_fields,
'shipping' => $default_shipping_fields,
'account' => $default_account_fields,
'order' => $default_order_fields,
);
}
/**
* Returns list of WooCommerce checkout fields that can be populated using information from Klarna.
*/
public function get_klarna_checkout_fields() {
$klarna_fields = array(
'billing' => array(
'billing_first_name',
'billing_last_name',
'billing_country',
'billing_address_1',
'billing_address_2',
'billing_city',
'billing_state',
'billing_postcode',
'billing_phone',
'billing_email',
),
'shipping' => array(
'shipping_first_name',
'shipping_last_name',
'shipping_country',
'shipping_address_1',
'shipping_address_2',
'shipping_city',
'shipping_state',
'shipping_postcode',
),
);
return apply_filters( 'kco_wc_klarna_checkout_fields', $klarna_fields );
}
/**
* Returns list of WooCommerce checkout fields that can NOT be populated using information from Klarna.
*/
public function get_remaining_checkout_fields() {
$all_fields = $this->get_all_checkout_fields();
$klarna_fields = $this->get_klarna_checkout_fields();
foreach ( $klarna_fields['billing'] as $field ) {
if ( array_key_exists( $field, $all_fields['billing'] ) ) {
unset( $all_fields['billing'][ $field ] );
}
unset( $all_fields['billing']['billing_company'] ); // B2C only for now.
}
foreach ( $klarna_fields['shipping'] as $field ) {
if ( array_key_exists( $field, $all_fields['shipping'] ) ) {
unset( $all_fields['shipping'][ $field ] );
}
unset( $all_fields['shipping']['shipping_company'] ); // B2C only for now.
}
return $all_fields;
}
}

View File

@@ -0,0 +1,273 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Klarna_Checkout_For_WooCommerce_Fields class.
*
* Klarna Checkout for WooCommerce settings fields.
*/
class Klarna_Checkout_For_WooCommerce_Fields {
/**
* Returns the fields.
*/
public static function fields() {
$settings = array(
'enabled' => array(
'title' => __( 'Enable/Disable', 'klarna-checkout-for-woocommerce' ),
'label' => __( 'Enable Klarna Checkout', 'klarna-checkout-for-woocommerce' ),
'type' => 'checkbox',
'description' => '',
'default' => 'no',
),
'title' => array(
'title' => __( 'Title', 'klarna-checkout-for-woocommerce' ),
'type' => 'text',
'description' => __( 'Payment method title.', 'klarna-checkout-for-woocommerce' ),
'default' => 'Klarna',
'desc_tip' => true,
),
'description' => array(
'title' => __( 'Description', 'klarna-checkout-for-woocommerce' ),
'type' => 'textarea',
'description' => __( 'Payment method description.', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => true,
),
'allow_separate_shipping' => array(
'title' => __( 'Separate shipping address', 'klarna-checkout-for-woocommerce' ),
'label' => __( 'Allow separate shipping address', 'klarna-checkout-for-woocommerce' ),
'type' => 'checkbox',
'description' => __( 'If this option is checked, customers will be able to enter shipping address different than their billing address in checkout.', 'klarna-checkout-for-woocommerce' ),
'default' => 'no',
'desc_tip' => true,
),
'select_another_method_text' => array(
'title' => __( 'Other payment method button text', 'klarna-checkout-for-woocommerce' ),
'type' => 'text',
'description' => __( 'Customize the <em>Select another payment method</em> button text that is displayed in checkout if using other payment methods than Klarna Checkout. Leave blank to use the default (and translatable) text.', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => true,
),
'shipping_details' => array(
'title' => __( 'Shipping details', 'klarna-checkout-for-woocommerce' ),
'label' => __( 'Shipping details note shown to customer', 'klarna-checkout-for-woocommerce' ),
'type' => 'text',
'description' => __( 'Will be shown to customer in thank you page.', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => false,
),
'allowed_customer_types' => array(
'title' => __( 'Allowed Customer Types', 'klarna-checkout-for-woocommerce' ),
'type' => 'select',
'options' => array(
'B2C' => __( 'B2C only', 'klarna-checkout-for-woocommerce' ),
'B2B' => __( 'B2B only', 'klarna-checkout-for-woocommerce' ),
'B2CB' => __( 'B2C & B2B (defaults to B2C)', 'klarna-checkout-for-woocommerce' ),
'B2BC' => __( 'B2B & B2C (defaults to B2B)', 'klarna-checkout-for-woocommerce' ),
),
'description' => sprintf( __( 'Select if you want to sell both to consumers and companies or only to one of them (available for SE, NO and FI). Learn more and <a href="%s" target="_blank">sign up for Klarna Checkout B2B here</a>.', 'klarna-checkout-for-woocommerce' ), 'https://www.klarna.com/se/foretag/klarna-checkout/klarna-checkout-foretag-form' ),
'default' => 'B2C',
'desc_tip' => false
),
'send_product_urls' => array(
'title' => __( 'Product URLs', 'klarna-checkout-for-woocommerce' ),
'type' => 'checkbox',
'label' => __( 'Send product and product image URLs to Klarna', 'klarna-checkout-for-woocommerce' ),
'default' => 'yes',
'desc_tip' => true,
),
'logging' => array(
'title' => __( 'Logging', 'klarna-checkout-for-woocommerce' ),
'label' => __( 'Log debug messages', 'klarna-checkout-for-woocommerce' ),
'type' => 'checkbox',
'description' => __( 'Save debug messages to the WooCommerce System Status log.', 'klarna-checkout-for-woocommerce' ),
'default' => 'no',
'desc_tip' => true,
),
'testmode' => array(
'title' => __( 'Test mode', 'klarna-checkout-for-woocommerce' ),
'label' => __( 'Enable Test Mode', 'klarna-checkout-for-woocommerce' ),
'type' => 'checkbox',
'description' => __( 'Place the payment gateway in test mode using test API keys.', 'klarna-checkout-for-woocommerce' ),
'default' => 'yes',
'desc_tip' => true,
),
'dob_mandatory' => array(
'title' => __( 'Date of birth mandatory', 'klarna-checkout-for-woocommerce' ),
'label' => __( 'Make customer date of birth mandatory', 'klarna-checkout-for-woocommerce' ),
'type' => 'checkbox',
'description' => __( 'If checked, the customer cannot skip date of birth.', 'klarna-checkout-for-woocommerce' ),
'default' => 'no',
'desc_tip' => true,
),
// EU.
'credentials_eu' => array(
'title' => '<img src="' . plugins_url( 'assets/img/flags/eu.svg', KCO_WC_MAIN_FILE ) . '" height="12" /> API Credentials Europe',
'type' => 'title',
),
'merchant_id_eu' => array(
'title' => __( 'Production Username (UID)', 'klarna-checkout-for-woocommerce' ),
'type' => 'text',
'description' => __( 'Get your API keys from your Klarna Checkout merchant account for Europe.', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => true,
),
'shared_secret_eu' => array(
'title' => __( 'Production Password', 'klarna-checkout-for-woocommerce' ),
'type' => 'text',
'description' => __( 'Get your API keys from your Klarna Checkout merchant account for Europe.', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => true,
),
'test_merchant_id_eu' => array(
'title' => __( 'Test Username (UID)', 'klarna-checkout-for-woocommerce' ),
'type' => 'text',
'description' => __( 'Get your API keys from your Klarna Checkout merchant account for Europe.', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => true,
),
'test_shared_secret_eu' => array(
'title' => __( 'Test Password', 'klarna-checkout-for-woocommerce' ),
'type' => 'text',
'description' => __( 'Get your API keys from your Klarna Checkout merchant account for Europe.', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => true,
),
'title_mandatory' => array(
'title' => __( 'Title mandatory (GB)', 'klarna-checkout-for-woocommerce' ),
'label' => __( 'Make customer title mandatory', 'klarna-checkout-for-woocommerce' ),
'type' => 'checkbox',
'description' => __( 'If unchecked, title becomes optional. Only available for orders for country GB.', 'klarna-checkout-for-woocommerce' ),
'default' => 'yes',
'desc_tip' => true,
),
'prefill_consent' => array(
'title' => __( 'Show prefill consent notice', 'klarna-checkout-for-woocommerce' ),
'label' => __( 'Only applicable for stores based in Germany and Austria', 'klarna-checkout-for-woocommerce' ),
'type' => 'checkbox',
'default' => 'yes',
),
// US.
'credentials_us' => array(
'title' => '<img src="' . plugins_url( 'assets/img/flags/us.svg', KCO_WC_MAIN_FILE ) . '" height="12" /> API Credentials United States',
'type' => 'title',
),
'merchant_id_us' => array(
'title' => __( 'Production Username (UID)', 'klarna-checkout-for-woocommerce' ),
'type' => 'text',
'description' => __( 'Get your API keys from your Klarna Checkout merchant account for US.', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => true,
),
'shared_secret_us' => array(
'title' => __( 'Production Password', 'klarna-checkout-for-woocommerce' ),
'type' => 'text',
'description' => __( 'Get your API keys from your Klarna Checkout merchant account for US.', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => true,
),
'test_merchant_id_us' => array(
'title' => __( 'Test Username (UID)', 'klarna-checkout-for-woocommerce' ),
'type' => 'text',
'description' => __( 'Get your API keys from your Klarna Checkout merchant account for US.', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => true,
),
'test_shared_secret_us' => array(
'title' => __( 'Test Password', 'klarna-checkout-for-woocommerce' ),
'type' => 'text',
'description' => __( 'Get your API keys from your Klarna Checkout merchant account for US.', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => true,
),
// Checkout iframe settings.
'color_settings_title' => array(
'title' => __( 'Color Settings', 'klarna-checkout-for-woocommerce' ),
'type' => 'title',
),
'color_button' => array(
'title' => __( 'Checkout button color', 'klarna-checkout-for-woocommerce' ),
'type' => 'color',
'description' => __( 'Checkout page button color', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => true,
),
'color_button_text' => array(
'title' => __( 'Checkout button text color', 'klarna-checkout-for-woocommerce' ),
'type' => 'color',
'description' => __( 'Checkout page button text color', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => true,
),
'color_checkbox' => array(
'title' => __( 'Checkout checkbox color', 'klarna-checkout-for-woocommerce' ),
'type' => 'color',
'description' => __( 'Checkout page checkbox color', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => true,
),
'color_checkbox_checkmark' => array(
'title' => __( 'Checkout checkbox checkmark color', 'klarna-checkout-for-woocommerce' ),
'type' => 'color',
'description' => __( 'Checkout page checkbox checkmark color', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => true,
),
'color_header' => array(
'title' => __( 'Checkout header color', 'klarna-checkout-for-woocommerce' ),
'type' => 'color',
'description' => __( 'Checkout page header color', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => true,
),
'color_link' => array(
'title' => __( 'Checkout link color', 'klarna-checkout-for-woocommerce' ),
'type' => 'color',
'description' => __( 'Checkout page link color', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => true,
),
'radius_border' => array(
'title' => __( 'Checkout radius border (px)', 'klarna-checkout-for-woocommerce' ),
'type' => 'number',
'description' => __( 'Checkout page radius border in pixels', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => true,
),
);
$wc_version = defined( 'WC_VERSION' ) && WC_VERSION ? WC_VERSION : null;
if ( version_compare( $wc_version, '3.4', '>=' ) ) {
$new_settings = array();
foreach ( $settings as $key => $value ) {
$new_settings[ $key ] = $value;
if ( 'dob_mandatory' === $key ) {
$new_settings['display_privacy_policy_text'] = array(
'title' => __( 'Checkout privacy policy text', 'klarna-checkout-for-woocommerce' ),
'label' => __( 'Select if you want to show the <em>Checkout privacy policy</em> text on the checkout page, and where you want to display it.', 'klarna-checkout-for-woocommerce' ),
'type' => 'select',
'default' => 'no',
'options' => array(
'no' => __( 'Do not display', 'klarna-checkout-for-woocommerce' ),
'above' => __( 'Display above checkout', 'klarna-checkout-for-woocommerce' ),
'below' => __( 'Display below checkout', 'klarna-checkout-for-woocommerce' ),
),
);
$new_settings['add_terms_and_conditions_checkbox'] = array(
'title' => __( 'Terms and conditions checkbox', 'klarna-checkout-for-woocommerce' ),
'label' => __( 'Add a terms and conditions checkbox inside Klarna checkout iframe', 'klarna-checkout-for-woocommerce' ),
'type' => 'checkbox',
'description' => __( 'To change the text navigate to → Appearance → Customize → WooCommerce → Checkout.', 'klarna-checkout-for-woocommerce' ),
'default' => 'no',
'desc_tip' => false,
);
}
}
$settings = $new_settings;
}
return apply_filters( 'kco_wc_gateway_settings', $settings );
}
}

View File

@@ -0,0 +1,366 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Klarna_Checkout_For_WooCommerce_Gateway class.
*
* @extends WC_Payment_Gateway
*/
class Klarna_Checkout_For_WooCommerce_Gateway extends WC_Payment_Gateway {
/**
* Klarna_Checkout_For_WooCommerce_Gateway constructor.
*/
public function __construct() {
$this->id = 'kco';
$this->method_title = __( 'Klarna Checkout', 'klarna-checkout-for-woocommerce' );
$this->method_description = __( 'Klarna Checkout replaces standard WooCommerce checkout page.', 'klarna-checkout-for-woocommerce' );
$this->has_fields = false;
$this->supports = apply_filters( 'kco_wc_supports', array( 'products' ) );
// 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' );
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_thankyou_' . $this->id, array( $this, 'show_thank_you_snippet' ) );
add_action( 'woocommerce_admin_order_data_after_billing_address', array( $this, 'address_notice' ) );
add_action( 'woocommerce_checkout_init', array( $this, 'prefill_consent' ) );
add_action( 'woocommerce_checkout_init', array( $this, 'show_log_in_notice' ) );
// Remove WooCommerce footer text from our settings page.
add_filter( 'admin_footer_text', array( $this, 'admin_footer_text' ), 999 );
}
/**
* 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"/>';
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 ) {
$order = wc_get_order( $order_id );
krokedil_set_order_gateway_version( $order_id, KCO_WC_VERSION );
return array(
'result' => 'success',
'redirect' => $this->get_return_url( $order ),
);
}
/**
* 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 = Klarna_Checkout_For_WooCommerce_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;
}
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();
WC_Klarna_Banners::settings_sidebar( $parent_options );
}
/**
* Enqueue payment scripts.
*
* @hook wp_enqueue_scripts
*/
public function enqueue_scripts() {
if ( ! is_checkout() ) {
return;
}
if ( is_order_received_page() ) {
return;
}
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', 'wc-cart' ),
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
);
$form = array();
if ( false !== get_transient( WC()->session->get( 'kco_wc_order_id' ) ) ) {
$form = get_transient( WC()->session->get( 'kco_wc_order_id' ) );
}
$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' ),
'update_extra_fields_url' => WC_AJAX::get_endpoint( 'kco_wc_update_extra_fields' ),
'update_extra_fields_nonce' => wp_create_nonce( 'kco_wc_update_extra_fields' ),
'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' ),
'update_klarna_order_url' => WC_AJAX::get_endpoint( 'kco_wc_update_klarna_order' ),
'update_klarna_order_nonce' => wp_create_nonce( 'kco_wc_update_klarna_order' ),
'iframe_shipping_address_change_url' => WC_AJAX::get_endpoint( 'kco_wc_iframe_shipping_address_change' ),
'iframe_shipping_address_change_nonce' => wp_create_nonce( 'kco_wc_iframe_shipping_address_change' ),
'checkout_error_url' => WC_AJAX::get_endpoint( 'kco_wc_checkout_error' ),
'checkout_error_nonce' => wp_create_nonce( 'kco_wc_checkout_error' ),
'logging' => $this->logging,
'save_form_data' => WC_AJAX::get_endpoint( 'kco_wc_save_form_data' ),
'save_form_data_nonce' => wp_create_nonce( 'kco_wc_save_form_data' ),
'form' => $form,
);
wp_localize_script( 'kco', 'kco_params', $checkout_localize_params );
wp_enqueue_script( 'kco' );
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;
}
if ( ! isset( $_GET['section'] ) || 'kco' !== $_GET['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.js', KCO_WC_MAIN_FILE ),
array(),
KCO_WC_VERSION
);
$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 ) ) {
return 'EU';
} else {
return '';
}
}
/**
* 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 ( ! WC()->session->get( 'kco_wc_order_id' ) ) {
return;
}
$klarna_order = KCO_WC()->api->get_order();
echo KCO_WC()->api->get_snippet( $klarna_order );
if ( $order_id ) {
// Set WC order transaction ID.
$order = wc_get_order( $order_id );
update_post_meta( $order_id, '_wc_klarna_order_id', sanitize_key( $klarna_order->order_id ) );
update_post_meta( $order_id, '_transaction_id', sanitize_key( $klarna_order->order_id ) );
$environment = $this->testmode ? 'test' : 'live';
update_post_meta( $order_id, '_wc_klarna_environment', $environment );
$klarna_country = WC()->checkout()->get_value( 'billing_country' );
update_post_meta( $order_id, '_wc_klarna_country', $klarna_country );
krokedil_log_events( $order_id, 'Klarna order in show_thank_you_snippet', $klarna_order );
$response = KCO_WC()->api->request_post_get_order( $klarna_order->order_id );
$klarna_post_order = json_decode( $response['body'] );
krokedil_log_events( $order_id, 'Klarna post_order in show_thank_you_snippet', $klarna_post_order );
if ( 'ACCEPTED' === $klarna_post_order->fraud_status ) {
$order->payment_complete();
// translators: Klarna order ID.
$note = sprintf( __( 'Payment via Klarna Checkout, order ID: %s', 'klarna-checkout-for-woocommerce' ), sanitize_key( $klarna_order->order_id ) );
$order->add_order_note( $note );
} elseif ( 'REJECTED' === $klarna_post_order->fraud_status ) {
$order->update_status( 'on-hold', __( 'Klarna Checkout order was rejected.', 'klarna-checkout-for-woocommerce' ) );
} elseif ( 'PENDING' === $klarna_post_order->fraud_status ) {
// translators: Klarna order ID.
$note = sprintf( __( 'Klarna order is under review, order ID: %s.', 'klarna-checkout-for-woocommerce' ), sanitize_key( $klarna_order->order_id ) );
$order->update_status( 'on-hold', $note );
}
KCO_WC()->api->request_post_acknowledge_order( $klarna_order->order_id );
KCO_WC()->api->request_post_set_merchant_reference(
$klarna_order->order_id,
array(
'merchant_reference1' => $order->get_order_number(),
'merchant_reference2' => $order->get_id(),
)
);
}
}
/**
* Changes footer text in KCO settings page.
*
* @TODO: Get a URL to link to.
*
* @param string $text Footer text.
*
* @return string
*/
public function admin_footer_text( $text ) {
if ( isset( $_GET['section'] ) && 'kco' === $_GET['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="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>';
}
}
/**
* Show notice that tells customers they need to log in.
*/
public function show_log_in_notice() {
if ( isset( $_GET['login_required'] ) && 'yes' === $_GET['login_required'] ) {
wc_add_notice(
sanitize_textarea_field( __(
'An account is already registered with your email address. Please log in.',
'klarna-checkout-for-woocommerce'
) ),
'error'
);
}
}
/**
* Adds prefill consent to WC session.
*/
public function prefill_consent() {
if ( isset( $_GET['prefill_consent'] ) ) { // Input var okay.
if ( 'yes' === sanitize_text_field( $_GET['prefill_consent'] ) ) {
WC()->session->set( 'kco_wc_prefill_consent', true );
}
}
}
}

View File

@@ -0,0 +1,91 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
/**
* Compliance with European Union's General Data Protection Regulation.
*
* @class Klarna_Checkout_For_Woocommerce_GDPR
* @version 1.0.0
* @package Klarna_Checkout/Classes
* @category Class
* @author Krokedil
*/
class Klarna_Checkout_For_Woocommerce_GDPR {
/**
* Class constructor.
*/
public function __construct() {
add_action( 'admin_init', array( $this, 'privacy_declarations' ) );
add_action( 'init', array( $this, 'maybe_add_privacy_policy_text' ) );
add_filter( 'kco_wc_api_request_args', array( $this, 'maybe_add_checkbox' ) );
}
/**
* Privacy declarations.
*
* @return void
*/
public function privacy_declarations() {
if ( function_exists( 'wp_add_privacy_policy_content' ) ) {
$content =
__(
'When you place an order in the webstore with Klarna Checkout as the choosen payment method, ' .
'information about the products in the order (name, price, quantity, SKU) is sent to Klarna. ' .
'When the purchase is finalized Klarna sends your billing and shipping address back to the webstore. ' .
'This data plus an unique identifier for the purchase is then stored as billing and shipping data in the order in WooCommerce.',
'klarna-checkout-for-woocommerce'
);
wp_add_privacy_policy_content(
'Klarna Checkout for WooCommerce',
wp_kses_post( wpautop( $content ) )
);
}
}
/**
* Maybe adds the terms checkbox to the checkout.
*
* @return void
*/
public function maybe_add_privacy_policy_text() {
$settings = get_option( 'woocommerce_kco_settings' );
$display_privacy_policy_text = $settings['display_privacy_policy_text'];
if ( 'above' == $display_privacy_policy_text ) {
add_action( 'kco_wc_before_snippet', array( $this, 'kco_wc_display_privacy_policy_text' ) );
} elseif ( 'below' == $display_privacy_policy_text ) {
add_action( 'kco_wc_after_snippet', array( $this, 'kco_wc_display_privacy_policy_text' ) );
}
}
/**
* Gets the terms template.
*
* @return void
*/
public function kco_wc_display_privacy_policy_text() {
if ( function_exists( 'wc_checkout_privacy_policy_text' ) ) {
echo wc_checkout_privacy_policy_text();
}
}
/**
* Maybe adds a checkbox to the Klarna iFrame.
*
* @param array $args The arguments array for the Klarna request.
* @return array $args The arguments array for the Klarna request.
*/
public function maybe_add_checkbox( $args ) {
if ( function_exists( 'wc_terms_and_conditions_checkbox_enabled' ) ) {
$settings = get_option( 'woocommerce_kco_settings' );
$add_checkbox = $settings['add_terms_and_conditions_checkbox'];
if ( 'yes' === $add_checkbox && wc_terms_and_conditions_checkbox_enabled() ) {
$args['options']['additional_checkbox']['text'] = wc_replace_policy_page_link_placeholders( wc_get_terms_and_conditions_checkbox_text() );
$args['options']['additional_checkbox']['checked'] = false;
$args['options']['additional_checkbox']['required'] = true;
}
}
return $args;
}
}
new Klarna_Checkout_For_Woocommerce_GDPR();

View File

@@ -0,0 +1,45 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Klarna_Checkout_For_WooCommerce_AJAX class.
*
* Registers AJAX actions for Klarna Checkout for WooCommerce.
*
* @extends WC_AJAX
*/
class Klarna_Checkout_For_WooCommerce_Logging {
/** @var WC_Logger Logger instance */
private $logger = false;
/**
* Logging function.
*
* @param string $message Error message.
* @param string $level Error level.
*/
public function log( $message, $level = 'info' ) {
if ( $this->log_enabled() ) {
if ( empty( $this->logger ) ) {
$this->logger = wc_get_logger();
}
$this->logger->log( $level, $message, array( 'source' => 'klarna-checkout-for-woocommerce' ) );
}
}
/**
* Checks if logging is enabled in plugin settings.
*
* @return bool
*/
private function log_enabled() {
$settings = get_option( 'woocommerce_kco_settings' );
return ( null !== $settings['logging'] && 'yes' === $settings['logging'] );
}
}

View File

@@ -0,0 +1,141 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Klarna_Checkout_For_WooCommerce_Merchant_URLs class.
*
* Class that formats gets merchant URLs Klarna API.
*/
class Klarna_Checkout_For_WooCommerce_Merchant_URLs {
/**
* Gets formatted merchant URLs array.
*
* @return array
*/
public function get_urls() {
$merchant_urls = array(
'terms' => $this->get_terms_url(), // Required.
'checkout' => $this->get_checkout_url(), // Required.
'confirmation' => $this->get_confirmation_url(), // Required.
'push' => $this->get_push_url(), // Required.
'validation' => $this->get_validation_url(), // HTTPS.
'shipping_option_update' => $this->get_shipping_option_update_url(), // HTTPS.
// 'address_update' => $this->get_address_update_url(), // HTTPS.
'notification' => $this->get_notification_url(),
// 'country_change' => $this->get_country_change_url(), // HTTPS.
);
return $merchant_urls;
}
/**
* Terms URL.
*
* Required. URL of merchant terms and conditions. Should be different than checkout, confirmation and push URLs.
*
* @return string
*/
private function get_terms_url() {
$terms_url = get_permalink( wc_get_page_id( 'terms' ) );
return $terms_url;
}
/**
* Checkout URL.
*
* Required. URL of merchant checkout page. Should be different than terms, confirmation and push URLs.
*
* @return string
*/
private function get_checkout_url() {
$checkout_url = wc_get_checkout_url();
return $checkout_url;
}
/**
* Confirmation URL.
*
* Required. URL of merchant confirmation page. Should be different than checkout and confirmation URLs.
*
* @return string
*/
private function get_confirmation_url() {
$confirmation_url = wc_get_checkout_url() . '?confirm=yes&kco_wc_order_id={checkout.order.id}';
return $confirmation_url;
}
/**
* Push URL.
*
* Required. URL of merchant confirmation page. Should be different than checkout and confirmation URLs.
*
* @return string
*/
private function get_push_url() {
$push_url = get_home_url() . '/wc-api/KCO_WC_Push/?kco_wc_order_id={checkout.order.id}';
return $push_url;
}
/**
* Validation URL.
*
* URL that will be requested for final merchant validation, must be https.
*
* @return string
*/
private function get_validation_url() {
$validation_url = get_home_url() . '/wc-api/KCO_WC_Validation/?kco_wc_order_id={checkout.order.id}';
return str_replace( 'http:', 'https:', $validation_url );
}
/**
* Shipping option update URL.
*
* URL for shipping option update, must be https.
*
* @return string
*/
private function get_shipping_option_update_url() {
$shipping_option_update_url = get_home_url() . '/wc-api/KCO_WC_Shipping_Option_Update/';
return str_replace( 'http:', 'https:', $shipping_option_update_url );
}
/**
* Address update URL.
*
* URL for shipping, tax and purchase currency updates. Will be called on address changes, must be https.
*
* @return string
*/
private function get_address_update_url() {
$address_update_url = get_home_url() . '/wc-api/KCO_WC_Address_Update/?kco_wc_order_id={checkout.order.id}';
return str_replace( 'http:', 'https:', $address_update_url );
}
/**
* Notification URL.
*
* URL for notifications on pending orders.
*
* @return string
*/
private function get_notification_url() {
$notification_url = get_home_url() . '/wc-api/KCO_WC_Notification/?kco_wc_order_id={checkout.order.id}';
return $notification_url;
}
/**
* Country change URL.
*
* URL for shipping, tax and purchase currency updates. Will be called on purchase country changes, must be https.
*
* @return string
*/
private function get_country_change_url() {
$country_change_url = get_home_url() . '/wp-json/kcowc/v1/address/{checkout.order.id}';
return str_replace( 'http:', 'https:', $country_change_url );
}
}

View File

@@ -0,0 +1,592 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Klarna_Checkout_For_WooCommerce_Order_Lines class.
*
* Class that formats WooCommerce cart contents for Klarna API.
*/
class Klarna_Checkout_For_WooCommerce_Order_Lines {
/**
* Formatted order lines.
*
* @var $order_lines
*/
public $order_lines = array();
/**
* Shop country.
*
* @var string
*/
public $shop_country;
/**
* Send sales tax as separate item (US merchants).
*
* @var bool
*/
public $separate_sales_tax = false;
/**
* WC_Klarna_Payments_Order_Lines constructor.
*
* @param bool|string $shop_country Shop country.
*/
public function __construct( $shop_country = null ) {
if ( ! $shop_country ) {
$base_location = wc_get_base_location();
$shop_country = $base_location['country'];
}
$this->shop_country = $shop_country;
if ( 'US' === $this->shop_country ) {
$this->separate_sales_tax = true;
}
}
/**
* Processes cart data
*/
public function process_data() {
// @TODO: Process fees
$this->process_cart();
$this->process_shipping();
$this->process_sales_tax();
$this->process_coupons();
$this->process_fees();
}
/**
* Gets formatted order lines from WooCommerce cart.
*
* @return array
*/
public function get_order_lines() {
return $this->order_lines;
}
/**
* Gets order amount for Klarna API.
*
* @return int
*/
public function get_order_amount() {
return round( WC()->cart->total * 100 );
}
/**
* Gets order tax amount for Klarna API.
*
* @return int
*/
public function get_order_tax_amount() {
return round( ( WC()->cart->tax_total + WC()->cart->shipping_tax_total ) * 100 );
}
/**
* Process WooCommerce cart to Klarna Payments order lines.
*/
public function process_cart() {
foreach ( WC()->cart->get_cart() as $cart_item ) {
if ( $cart_item['quantity'] ) {
if ( $cart_item['variation_id'] ) {
$product = wc_get_product( $cart_item['variation_id'] );
} else {
$product = wc_get_product( $cart_item['product_id'] );
}
$klarna_item = array(
'reference' => $this->get_item_reference( $product ),
'name' => $this->get_item_name( $cart_item ),
'quantity' => $this->get_item_quantity( $cart_item ),
'unit_price' => $this->get_item_price( $cart_item ),
'tax_rate' => $this->get_item_tax_rate( $cart_item, $product ),
'total_amount' => $this->get_item_total_amount( $cart_item ),
'total_tax_amount' => $this->get_item_tax_amount( $cart_item ),
'total_discount_amount' => $this->get_item_discount_amount( $cart_item ),
);
// Add images.
$klarna_checkout_settings = get_option( 'woocommerce_kco_settings' );
if ( array_key_exists( 'send_product_urls', $klarna_checkout_settings ) && 'yes' === $klarna_checkout_settings['send_product_urls'] ) {
$klarna_item['product_url'] = $this->get_item_product_url( $product );
if ( $this->get_item_image_url( $product ) ) {
$klarna_item['image_url'] = $this->get_item_image_url( $product );
}
}
$this->order_lines[] = $klarna_item;
}
}
}
/**
* Process WooCommerce shipping to Klarna Payments order lines.
*/
public function process_shipping() {
if ( WC()->shipping->get_packages() && WC()->session->get( 'chosen_shipping_methods' )[0] ) {
$shipping = array(
'type' => 'shipping_fee',
'reference' => $this->get_shipping_reference(),
'name' => $this->get_shipping_name(),
'quantity' => 1,
'unit_price' => $this->get_shipping_amount(),
'tax_rate' => $this->get_shipping_tax_rate(),
'total_amount' => $this->get_shipping_amount(),
'total_tax_amount' => $this->get_shipping_tax_amount(),
);
$this->order_lines[] = $shipping;
}
}
/**
* Process sales tax for US.
*/
public function process_sales_tax() {
if ( $this->separate_sales_tax ) {
$sales_tax_amount = round( ( WC()->cart->tax_total + WC()->cart->shipping_tax_total ) * 100 );
// Add sales tax line item.
$sales_tax = array(
'type' => 'sales_tax',
'reference' => __( 'Sales Tax', 'klarna-checkout-for-woocommerce' ),
'name' => __( 'Sales Tax', 'klarna-checkout-for-woocommerce' ),
'quantity' => 1,
'unit_price' => $sales_tax_amount,
'tax_rate' => 0,
'total_amount' => $sales_tax_amount,
'total_discount_amount' => 0,
'total_tax_amount' => 0,
);
$this->order_lines[] = $sales_tax;
}
}
/**
* Process smart coupons.
*/
public function process_coupons() {
if ( ! empty( WC()->cart->get_coupons() ) ) {
foreach ( WC()->cart->get_coupons() as $coupon_key => $coupon ) {
$coupon_reference = '';
$coupon_amount = 0;
$coupon_tax_amount = '';
// Smart coupons are processed as real line items, cart and product discounts sent for reference only.
if ( 'smart_coupon' === $coupon->get_discount_type() ) {
$coupon_amount = - WC()->cart->get_coupon_discount_amount( $coupon_key ) * 100;
$coupon_tax_amount = - WC()->cart->get_coupon_discount_tax_amount( $coupon_key ) * 100;
$coupon_reference = 'Discount';
} else {
if ( 'US' === $this->shop_country ) {
$coupon_amount = 0;
$coupon_tax_amount = 0;
if ( $coupon->is_type( 'fixed_cart' ) || $coupon->is_type( 'percent' ) ) {
$coupon_type = 'Cart discount';
} elseif ( $coupon->is_type( 'fixed_product' ) || $coupon->is_type( 'percent_product' ) ) {
$coupon_type = 'Product discount';
} else {
$coupon_type = 'Discount';
}
$coupon_reference = $coupon_type . ' (amount: ' . WC()->cart->get_coupon_discount_amount( $coupon_key ) . ', tax amount: ' . WC()->cart->get_coupon_discount_tax_amount( $coupon_key ) . ')';
}
}
// Add separate discount line item, but only if it's a smart coupon or country is US.
if ( 'US' === $this->shop_country || 'smart_coupon' === $coupon->get_discount_type() ) {
$discount = array(
'type' => 'discount',
'reference' => $coupon_reference,
'name' => $coupon_key,
'quantity' => 1,
'unit_price' => $coupon_amount,
'tax_rate' => 0,
'total_amount' => $coupon_amount,
'total_discount_amount' => 0,
'total_tax_amount' => $coupon_tax_amount,
);
$this->order_lines[] = $discount;
}
} // End foreach().
} // End if().
}
/**
* Process cart fees.
*/
public function process_fees() {
if ( ! empty( WC()->cart->get_fees() ) ) {
foreach ( WC()->cart->get_fees() as $fee_key => $fee ) {
if ( $this->separate_sales_tax ) {
$fee_tax_rate = 0;
$fee_tax_amount = 0;
$fee_amount = round( $fee->amount * 100 );
} else {
$fee_tax_amount = round( $fee->tax * 100 );
$fee_amount = round( ( $fee->amount + $fee->tax ) * 100 );
$_tax = new WC_Tax();
$tmp_rates = $_tax->get_rates( $fee->tax_class );
$vat = array_shift( $tmp_rates );
if ( isset( $vat['rate'] ) ) {
$fee_tax_rate = round( $vat['rate'] * 100 );
} else {
$fee_tax_rate = 0;
}
}
// Add separate discount line item, but only if it's a smart coupon or country is US.
$fee_item = array(
'type' => 'shipping_fee',
'reference' => $fee->id,
'name' => $fee->name,
'quantity' => 1,
'unit_price' => $fee_amount,
'tax_rate' => $fee_tax_rate,
'total_amount' => $fee_amount,
'total_discount_amount' => 0,
'total_tax_amount' => $fee_tax_amount,
);
$this->order_lines[] = $fee_item;
} // End foreach().
} // End if().
}
// Helpers.
/**
* Get cart item name.
*
* @since 1.0
* @access public
*
* @param array $cart_item Cart item.
*
* @return string $item_name Cart item name.
*/
public function get_item_name( $cart_item ) {
$cart_item_data = $cart_item['data'];
$item_name = $cart_item_data->get_name();
return strip_tags( $item_name );
}
/**
* Calculate item tax percentage.
*
* @since 1.0
* @access public
*
* @param array $cart_item Cart item.
*
* @return integer $item_tax_amount Item tax amount.
*/
public function get_item_tax_amount( $cart_item ) {
if ( $this->separate_sales_tax ) {
$item_tax_amount = 0;
} else {
$item_tax_amount = $cart_item['line_tax'] * 100;
}
return round( $item_tax_amount );
}
/**
* Calculate item tax percentage.
*
* @since 1.0
* @access public
*
* @param array $cart_item Cart item.
* @param object $product Product object.
*
* @return integer $item_tax_rate Item tax percentage formatted for Klarna.
*/
public function get_item_tax_rate( $cart_item, $product ) {
if ( $product->is_taxable() && $cart_item['line_subtotal_tax'] > 0 ) {
// Calculate tax rate.
if ( $this->separate_sales_tax ) {
$item_tax_rate = 0;
} else {
$_tax = new WC_Tax();
$tmp_rates = $_tax->get_rates( $product->get_tax_class() );
$vat = array_shift( $tmp_rates );
if ( isset( $vat['rate'] ) ) {
$item_tax_rate = round( $vat['rate'] * 100 );
} else {
$item_tax_rate = 0;
}
}
} else {
$item_tax_rate = 0;
}
return round( $item_tax_rate );
}
/**
* Get cart item price.
*
* @since 1.0
* @access public
*
* @param array $cart_item Cart item.
*
* @return integer $item_price Cart item price.
*/
public function get_item_price( $cart_item ) {
if ( $this->separate_sales_tax ) {
$item_subtotal = $cart_item['line_subtotal'];
} else {
$item_subtotal = $cart_item['line_subtotal'] + $cart_item['line_subtotal_tax'];
}
$item_price = $item_subtotal * 100 / $cart_item['quantity'];
return round( $item_price );
}
/**
* Get cart item quantity.
*
* @since 1.0
* @access public
*
* @param array $cart_item Cart item.
*
* @return integer $item_quantity Cart item quantity.
*/
public function get_item_quantity( $cart_item ) {
return round( $cart_item['quantity'] );
}
/**
* Get cart item reference.
*
* Returns SKU or product ID.
*
* @since 1.0
* @access public
*
* @param object $product Product object.
*
* @return string $item_reference Cart item reference.
*/
public function get_item_reference( $product ) {
if ( $product->get_sku() ) {
$item_reference = $product->get_sku();
} else {
$item_reference = $product->get_id();
}
return substr( (string) $item_reference, 0, 64 );
}
/**
* Get cart item discount.
*
* @since 1.0
* @access public
*
* @param array $cart_item Cart item.
*
* @return integer $item_discount_amount Cart item discount.
*/
public function get_item_discount_amount( $cart_item ) {
if ( $cart_item['line_subtotal'] > $cart_item['line_total'] ) {
if ( $this->separate_sales_tax ) {
$item_discount_amount = $cart_item['line_subtotal'] - $cart_item['line_total'];
} else {
$item_discount_amount = $cart_item['line_subtotal'] + $cart_item['line_subtotal_tax'] - $cart_item['line_total'] - $cart_item['line_tax'];
}
$item_discount_amount = $item_discount_amount * 100;
} else {
$item_discount_amount = 0;
}
return round( $item_discount_amount );
}
/**
* Get cart item product URL.
*
* @since 1.1
* @access public
*
* @param WC_Product $product Product.
*
* @return string $item_product_url Cart item product URL.
*/
public function get_item_product_url( $product ) {
return $product->get_permalink();
}
/**
* Get cart item product image URL.
*
* @since 1.1
* @access public
*
* @param WC_Product $product Product.
*
* @return string $item_product_image_url Cart item product image URL.
*/
public function get_item_image_url( $product ) {
$image_url = false;
if ( $product->get_image_id() > 0 ) {
$image_id = $product->get_image_id();
$image_url = wp_get_attachment_image_url( $image_id, 'shop_thumbnail', false );
}
return $image_url;
}
/**
* Get cart item discount rate.
*
* @since 1.0
* @access public
*
* @param array $cart_item Cart item.
*
* @return integer $item_discount_rate Cart item discount rate.
*/
public function get_item_discount_rate( $cart_item ) {
$item_discount_rate = ( 1 - ( $cart_item['line_total'] / $cart_item['line_subtotal'] ) ) * 100 * 100;
return round( $item_discount_rate );
}
/**
* Get cart item total amount.
*
* @since 1.0
* @access public
*
* @param array $cart_item Cart item.
*
* @return integer $item_total_amount Cart item total amount.
*/
public function get_item_total_amount( $cart_item ) {
if ( $this->separate_sales_tax ) {
$item_total_amount = ( $cart_item['line_total'] * 100 );
} else {
$item_total_amount = ( ( $cart_item['line_total'] + $cart_item['line_tax'] ) * 100 );
}
return round( $item_total_amount );
}
/**
* Get shipping method name.
*
* @since 1.0
* @access public
*
* @return string $shipping_name Name for selected shipping method.
*/
public function get_shipping_name() {
$shipping_packages = WC()->shipping->get_packages();
foreach ( $shipping_packages as $i => $package ) {
$chosen_method = isset( WC()->session->chosen_shipping_methods[ $i ] ) ? WC()->session->chosen_shipping_methods[ $i ] : '';
if ( '' !== $chosen_method ) {
$package_rates = $package['rates'];
foreach ( $package_rates as $rate_key => $rate_value ) {
if ( $rate_key === $chosen_method ) {
$shipping_name = $rate_value->label;
}
}
}
}
if ( ! isset( $shipping_name ) ) {
$shipping_name = __( 'Shipping', 'klarna-checkout-for-woocommerce' );
}
return (string) $shipping_name;
}
/**
* Get shipping reference.
*
* @since 1.0
* @access public
*
* @return string $shipping_reference Reference for selected shipping method.
*/
public function get_shipping_reference() {
$shipping_packages = WC()->shipping->get_packages();
foreach ( $shipping_packages as $i => $package ) {
$chosen_method = isset( WC()->session->chosen_shipping_methods[ $i ] ) ? WC()->session->chosen_shipping_methods[ $i ] : '';
if ( '' !== $chosen_method ) {
$package_rates = $package['rates'];
foreach ( $package_rates as $rate_key => $rate_value ) {
if ( $rate_key === $chosen_method ) {
$shipping_reference = $rate_value->id;
}
}
}
}
if ( ! isset( $shipping_reference ) ) {
$shipping_reference = __( 'Shipping', 'klarna-checkout-for-woocommerce' );
}
return (string) $shipping_reference;
}
/**
* Get shipping method amount.
*
* @since 1.0
* @access public
*
* @return integer $shipping_amount Amount for selected shipping method.
*/
public function get_shipping_amount() {
if ( $this->separate_sales_tax ) {
$shipping_amount = (int) number_format( WC()->cart->shipping_total * 100, 0, '', '' );
} else {
$shipping_amount = (int) number_format( ( WC()->cart->shipping_total + WC()->cart->shipping_tax_total ) * 100, 0, '', '' );
}
return $shipping_amount;
}
/**
* Get shipping method tax rate.
*
* @since 1.0
* @access public
*
* @return integer $shipping_tax_rate Tax rate for selected shipping method.
*/
public function get_shipping_tax_rate() {
if ( WC()->cart->shipping_tax_total > 0 && ! $this->separate_sales_tax ) {
$shipping_tax_rate = round( WC()->cart->shipping_tax_total / WC()->cart->shipping_total, 2 ) * 100 * 100;
} else {
$shipping_tax_rate = 0;
}
return round( $shipping_tax_rate );
}
/**
* Get shipping method tax amount.
*
* @since 1.0
* @access public
*
* @return integer $shipping_tax_amount Tax amount for selected shipping method.
*/
public function get_shipping_tax_amount() {
if ( $this->separate_sales_tax ) {
$shipping_tax_amount = 0;
} else {
$shipping_tax_amount = WC()->cart->shipping_tax_total * 100;
}
return round( $shipping_tax_amount );
}
}

View File

@@ -0,0 +1,22 @@
<?php
/**
* WooCommerce status page extension
*
* @class Klarna_Checkout_For_WooCommerce_Status
* @version 0.8.0
* @package Klarna_Checkout_For_WooCommerce/Classes
* @category Class
* @author Krokedil
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class Klarna_Checkout_For_WooCommerce_Status {
public function __construct() {
add_action( 'woocommerce_system_status_report', array( $this, 'add_status_page_box' ) );
}
public function add_status_page_box() {
include_once( KCO_WC_PLUGIN_PATH . '/includes/klarna-checkout-for-woocommerce-status-report.php' );
}
}
$wc_collector_checkout_status = new Klarna_Checkout_For_WooCommerce_Status();

View File

@@ -0,0 +1,116 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Klarna_Checkout_For_WooCommerce_Templates class.
*/
class Klarna_Checkout_For_WooCommerce_Templates {
/**
* The reference the *Singleton* instance of this class.
*
* @var $instance
*/
protected static $instance;
/**
* Returns the *Singleton* instance of this class.
*
* @return self::$instance The *Singleton* instance.
*/
public static function get_instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Plugin actions.
*/
public function __construct() {
// Override template if Klarna Checkout page.
add_filter( 'woocommerce_locate_template', array( $this, 'override_template' ), 10, 3 );
// Template hooks.
add_action( 'kco_wc_before_checkout_form', 'kco_wc_print_notices' );
add_action( 'kco_wc_before_checkout_form', 'kco_wc_calculate_totals', 1 );
add_action( 'kco_wc_before_checkout_form', 'woocommerce_checkout_login_form', 10 );
add_action( 'kco_wc_before_checkout_form', 'woocommerce_checkout_coupon_form', 20 );
add_action( 'kco_wc_after_order_review', 'kco_wc_show_extra_fields', 10 );
add_action( 'kco_wc_after_order_review', 'kco_wc_show_another_gateway_button', 20 );
add_action( 'kco_wc_before_snippet', 'kco_wc_prefill_consent', 10 );
add_action( 'kco_wc_after_snippet', 'kco_wc_show_payment_method_field', 10 );
}
/**
* Override checkout form template if Klarna Checkout is the selected payment method.
*
* @param string $template Template.
* @param string $template_name Template name.
* @param string $template_path Template path.
*
* @return string
*/
public function override_template( $template, $template_name, $template_path ) {
if ( is_checkout() ) {
// Klarna Checkout.
if ( 'checkout/form-checkout.php' === $template_name ) {
$available_gateways = WC()->payment_gateways()->get_available_payment_gateways();
if ( locate_template( 'woocommerce/klarna-checkout.php' ) ) {
$klarna_checkout_template = locate_template( 'woocommerce/klarna-checkout.php' );
} else {
$klarna_checkout_template = KCO_WC_PLUGIN_PATH . '/templates/klarna-checkout.php';
}
// Klarna checkout page.
if ( array_key_exists( 'kco', $available_gateways ) ) {
// If chosen payment method exists.
if ( 'kco' === WC()->session->get( 'chosen_payment_method' ) ) {
if ( ! isset( $_GET['confirm'] ) ) {
$template = $klarna_checkout_template;
}
}
// If chosen payment method does not exist and KCO is the first gateway.
if ( null === WC()->session->get( 'chosen_payment_method' ) || '' === WC()->session->get( 'chosen_payment_method' ) ) {
reset( $available_gateways );
if ( 'kco' === key( $available_gateways ) ) {
if ( ! isset( $_GET['confirm'] ) ) {
$template = $klarna_checkout_template;
}
}
}
// If another gateway is saved in session, but has since become unavailable.
if ( WC()->session->get( 'chosen_payment_method' ) ) {
if ( ! array_key_exists( WC()->session->get( 'chosen_payment_method' ), $available_gateways ) ) {
reset( $available_gateways );
if ( 'kco' === key( $available_gateways ) ) {
if ( ! isset( $_GET['confirm'] ) ) {
$template = $klarna_checkout_template;
}
}
}
}
}
}
}
// Fallback Klarna Order Received, used when WooCommerce checkout form submission fails.
if ( 'checkout/thankyou.php' === $template_name ) {
if ( isset( $_GET['kco_wc'] ) && 'true' === $_GET['kco_wc'] ) {
$template = KCO_WC_PLUGIN_PATH . '/templates/klarna-checkout-order-received.php';
}
}
return $template;
}
}
Klarna_Checkout_For_WooCommerce_Templates::get_instance();

View File

@@ -0,0 +1,157 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'WC_Klarna_Banners' ) ) {
/**
* Displays merchant information in the backend.
*/
class WC_Klarna_Banners {
/**
* WC_Klarna_Banners constructor.
*/
public function __construct() {
add_action( 'in_admin_header', array( $this, 'klarna_banner' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'load_admin_css' ) );
add_action( 'wp_ajax_hide_klarna_banner', array( $this, 'hide_klarna_banner' ) );
add_action( 'wp_ajax_nopriv_hide_klarna_banner', array( $this, 'hide_klarna_banner' ) );
}
/**
* Loads admin CSS file, has to be done here instead of gateway class, because
* it is required in all admin pages.
*/
public function load_admin_css() {
wp_enqueue_style(
'klarna_payments_admin',
plugins_url( 'assets/css/klarna-checkout-admin.css?v=120320182113', KCO_WC_MAIN_FILE )
);
}
/**
* Loads Klarna banner in admin pages.
*/
public function klarna_banner() {
$kco_settings = get_option( 'woocommerce_kco_settings' );
$show_banner = false;
// Always show banner in testmode.
if ( isset( $kco_settings['testmode'] ) && 'yes' === $kco_settings['testmode'] ) {
$show_banner = true;
}
// Go through countries and check if at least one has credentials configured.
$countries = array( 'eu', 'us' );
$country_set = false;
foreach ( $countries as $country ) {
if ( '' !== $kco_settings[ 'merchant_id_' . $country ] && '' !== $kco_settings[ 'shared_secret_' . $country ] ) {
$country_set = true;
}
}
if ( ! $country_set ) {
$show_banner = true;
}
if ( $show_banner && false === get_transient( 'klarna_hide_banner' ) ) {
?>
<div id="kb-spacer"></div>
<div id="klarna-banner">
<div id="kb-left">
<h1>Go live</h1>
<p>Before you can start to sell with Klarna you need your store to be approved by Klarna. When the installation is done and you are ready to go live, Klarna will need to verify the integration. Then you can go live with your store! If you wish to switch Klarna products then youll need the Klarna team to approve your store again.</p>
<a class="kb-button"
href="https://www.klarna.com/international/business/woocommerce/?utm_source=woo-backend&utm_medium=referral&utm_campaign=woo&utm_content=banner"
target="_blank">Go live with Klarna</a>
</div>
<div id="kb-right">
<h1>Currently using Klarna?</h1>
<p>Pay now, Pay later and Slice it. Klarna is entering a new world of smoooth. We would love for you to join us on the ride and to do so, you will need to upgrade your Klarna products to a new integration. You will then always get the latest features that Klarna develops and youll keep your current agreement along with your price settings.</p>
<a class="kb-button"
href="https://hello.klarna.com/product-upgrade?utm_source=woo-backend&utm_medium=referral&utm_campaign=woo&utm_content=banner"
target="_blank">Upgrade your contract with Klarna</a>
</div>
<img id="kb-image"
src="<?php echo esc_url( KCO_WC_PLUGIN_URL ); ?>/assets/img/klarna_logo_white.png"
alt="Klarna logo" width="110"/>
<span class="kb-dismiss dashicons dashicons-dismiss"></span>
</div>
<script type="text/javascript">
jQuery(document).ready(function($){
jQuery('.kb-dismiss').click(function(){
jQuery('#klarna-banner').slideUp();
jQuery.post(
ajaxurl,
{
action : 'hide_klarna_banner',
_wpnonce : '<?php echo wp_create_nonce('hide-klarna-banner'); ?>',
},
function(response){
console.log('Success hide kco banner');
}
);
});
});
</script>
<?php
}
}
/**
* @param $parent_options
*/
public static function settings_sidebar( $parent_options ) {
?>
<img id="klarna-settings-logo"
src="<?php echo esc_url( KCO_WC_PLUGIN_URL ); ?>/assets/img/klarna_logo_black.png" width="200"/>
<div id="klarna-wrapper">
<div id="klarna-main">
<?php echo $parent_options; ?>
</div>
<div id="klarna-sidebar">
<div class="kb-sidebar-section">
<img src="<?php echo esc_url( KCO_WC_PLUGIN_URL ); ?>/assets/img/icon_reminder.png" width="64"/>
<h3>Go live</h3>
<p>Before you can start to sell with Klarna you need your store to be approved by Klarna. When the installation is done and you are ready to go live, Klarna will need to verify the integration. Then you can go live with your store! If you wish to switch Klarna products then youll need the Klarna team to approve your store again.</p>
<a class="kb-button"
href="https://www.klarna.com/international/business/woocommerce/?utm_source=woo-backend&utm_medium=referral&utm_campaign=woo&utm_content=kco"
target="_blank">Go live with Klarna</a>
</div>
<div class="kb-sidebar-section">
<div>
<img src="<?php echo esc_url( KCO_WC_PLUGIN_URL ); ?>/assets/img/klarna_icons.png"
width="192"/>
</div>
<h3>Currently using Klarna?</h3>
<p>Pay now, Pay later and Slice it. Klarna is entering a new world of smoooth. We would love for you to join us on the ride and to do so, you will need to upgrade your Klarna products to a new integration. You will then always get the latest features that Klarna develops and youll keep your current agreement along with your price settings.</p>
<a class="kb-button"
href="https://hello.klarna.com/product-upgrade?utm_source=woo-backend&utm_medium=referral&utm_campaign=woo&utm_content=kco"
target="_blank">Upgrade your contract with Klarna</a>
</div>
</div>
</div>
<?php
}
/**
* Hide Klarna banner in admin pages for.
*/
public function hide_klarna_banner() {
set_transient( 'klarna_hide_banner', '1', 5 * DAY_IN_SECONDS );
wp_send_json_success( 'Hejä' );
wp_die();
}
}
new WC_Klarna_Banners();
}

View File

@@ -0,0 +1,476 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Echoes Klarna Checkout iframe snippet.
*/
function kco_wc_show_snippet() {
$klarna_order = KCO_WC()->api->get_order();
echo KCO_WC()->api->get_snippet( $klarna_order );
}
/**
* Shows order notes field in Klarna Checkout page.
*/
function kco_wc_show_order_notes() {
$order_fields = WC()->checkout()->get_checkout_fields( 'order' );
$key = 'order_comments';
if ( array_key_exists( $key, $order_fields ) ) {
$order_notes_field = $order_fields[ $key ];
woocommerce_form_field( $key, $order_notes_field, WC()->checkout()->get_value( $key ) );
}
}
/**
* Shows extra fields in Klarna Checkout page.
*/
function kco_wc_show_extra_fields() {
// Clear extra fields session values on reload.
// WC()->session->__unset( 'kco_wc_extra_fields_values' );
echo '<div id="kco-extra-fields">';
$extra_fields_values = WC()->session->get( 'kco_wc_extra_fields_values', array() );
$kco_wc_extra_checkout_fields = new Klarna_Checkout_For_WooCommerce_Extra_Checkout_Fields;
$extra_fields = $kco_wc_extra_checkout_fields->get_remaining_checkout_fields();
// Billing.
do_action( 'woocommerce_before_checkout_billing_form', WC()->checkout() );
foreach ( $extra_fields['billing'] as $key => $field ) {
if ( isset( $field['country_field'], $default_billing_fields[ $field['country_field'] ] ) ) {
$field['country'] = WC()->checkout()->get_value( $field['country_field'] );
}
$key_value = array_key_exists( $key, $extra_fields_values ) ? $extra_fields_values[ $key ] : '';
woocommerce_form_field( $key, $field, $key_value );
}
do_action( 'woocommerce_after_checkout_billing_form', WC()->checkout() );
if ( ! is_user_logged_in() && WC()->checkout()->is_registration_enabled() ) { ?>
<div class="woocommerce-account-fields">
<?php if ( ! WC()->checkout()->is_registration_required() ) { ?>
<p class="form-row form-row-wide create-account">
<label class="woocommerce-form__label woocommerce-form__label-for-checkbox checkbox">
<input class="woocommerce-form__input woocommerce-form__input-checkbox input-checkbox"
id="createaccount" <?php checked( ( true === WC()->checkout()->get_value( 'createaccount' ) || ( true === apply_filters( 'woocommerce_create_account_default_checked', false ) ) ), true ) ?>
type="checkbox" name="createaccount" value="1"/>
<span><?php _e( 'Create an account?', 'klarna-checkout-for-woocommerce' ); ?></span>
</label>
</p>
<?php } ?>
<?php do_action( 'woocommerce_before_checkout_registration_form', WC()->checkout() ); ?>
<?php if ( WC()->checkout()->get_checkout_fields( 'account' ) ) { ?>
<div class="create-account">
<?php foreach ( WC()->checkout()->get_checkout_fields( 'account' ) as $key => $field ) { ?>
<?php woocommerce_form_field( $key, $field, WC()->checkout()->get_value( $key ) ); ?>
<?php } ?>
<div class="clear"></div>
</div>
<?php } ?>
<?php do_action( 'woocommerce_after_checkout_registration_form', WC()->checkout() ); ?>
</div>
<?php
}
// Shipping.
do_action( 'woocommerce_before_checkout_shipping_form', WC()->checkout() );
foreach ( $extra_fields['shipping'] as $key => $field ) {
if ( isset( $field['country_field'], $default_shipping_fields[ $field['country_field'] ] ) ) {
$field['country'] = WC()->checkout()->get_value( $field['country_field'] );
}
$key_value = array_key_exists( $key, $extra_fields_values ) ? $extra_fields_values[ $key ] : '';
woocommerce_form_field( $key, $field, $key_value );
}
do_action( 'woocommerce_after_checkout_shipping_form', WC()->checkout() );
// Order.
do_action( 'woocommerce_before_order_notes', WC()->checkout() );
if ( apply_filters( 'woocommerce_enable_order_notes_field', true ) ) {
foreach ( $extra_fields['order'] as $key => $field ) {
$key_value = array_key_exists( $key, $extra_fields_values ) ? $extra_fields_values[ $key ] : '';
woocommerce_form_field( $key, $field, $key_value );
}
}
do_action( 'woocommerce_after_order_notes', WC()->checkout() );
echo '</div>';
}
/**
* Shows select another payment method button in Klarna Checkout page.
*/
function kco_wc_show_another_gateway_button() {
$available_gateways = WC()->payment_gateways()->get_available_payment_gateways();
if ( count( $available_gateways ) > 1 ) {
$settings = get_option( 'woocommerce_kco_settings' );
$select_another_method_text = isset( $settings['select_another_method_text'] ) && '' !== $settings['select_another_method_text'] ? $settings['select_another_method_text'] : __( 'Select another payment method', 'klarna-checkout-for-woocommerce' );
?>
<p style="margin-top:30px">
<a class="checkout-button button" href="#" id="klarna-checkout-select-other">
<?php echo $select_another_method_text; ?>
</a>
</p>
<?php
}
}
/**
* Is it OK to prefill customer data?
*/
function kco_wc_prefill_allowed() {
$base_location = wc_get_base_location();
if ( 'DE' === $base_location['country'] || 'AT' === $base_location['country'] ) {
$settings = get_option( 'woocommerce_kco_settings' );
$consent_setting_checked = ( isset( $settings['prefill_consent'] ) && 'yes' === $settings['prefill_consent'] );
if ( $consent_setting_checked && is_user_logged_in() && WC()->session->get( 'kco_wc_prefill_consent', false ) ) {
return true;
}
return false;
}
return true;
}
/**
* Calculates cart totals.
*/
function kco_wc_calculate_totals() {
WC()->cart->calculate_fees();
WC()->cart->calculate_shipping();
WC()->cart->calculate_totals();
}
function kco_wc_show_payment_method_field() {
?>
<input style="display:none" type="radio" name="payment_method" value="kco"/>
<?php
}
/**
* Shows prefill consent text.
*/
function kco_wc_prefill_consent() {
if ( ! kco_wc_prefill_allowed() && is_user_logged_in() ) {
$consent_url = add_query_arg(
[ 'prefill_consent' => 'yes' ],
wc_get_checkout_url()
);
$credentials = KCO_WC()->credentials->get_credentials_from_session();
$merchant_id = $credentials['merchant_id'];
if ( 'de_DE' === get_locale() ) {
$button_text = 'Meine Adressdaten vorausfüllen';
$link_text = 'Es gelten die Nutzungsbedingungen zur Datenübertragung';
$popup_text = 'In unserem Kassenbereich nutzen wir Klarna Checkout. Dazu werden Ihre Daten, wie E-Mail-Adresse, Vor- und
Nachname, Geburtsdatum, Adresse und Telefonnummer, soweit erforderlich, automatisch an Klarna AB übertragen,
sobald Sie in den Kassenbereich gelangen. Die Nutzungsbedingungen für Klarna Checkout finden Sie hier:
<a href="https://cdn.klarna.com/1.0/shared/content/legal/terms/' . $merchant_id . '/de_de/checkout" target="_blank">https://cdn.klarna.com/1.0/shared/content/legal/terms/' . $merchant_id . '/de_de/checkout</a>';
} else {
$button_text = 'Meine Adressdaten vorausfüllen';
$link_text = 'Es gelten die Nutzungsbedingungen zur Datenübertragung';
$popup_text = 'We use Klarna Checkout as our checkout, which offers a simplified purchase experience. When you choose to go to the checkout, your email address, first name, last name, date of birth, address and phone number may be automatically transferred to Klarna AB, enabling the provision of Klarna Checkout. These User Terms apply for the use of Klarna Checkout is available here:
<a target="_blank" href="https://cdn.klarna.com/1.0/shared/content/legal/terms/' . $merchant_id . '/en_us/checkout">https://cdn.klarna.com/1.0/shared/content/legal/terms/' . $merchant_id . '/en_us/checkout</a>';
}
?>
<p><a class="button" href="<?php echo $consent_url; ?>"><?php echo $button_text; ?></a></p>
<p><a href="#TB_inline?width=600&height=550&inlineId=consent-text"
class="thickbox"><?php echo $link_text; ?></a>
</p>
<div id="consent-text" style="display:none;">
<p><?php echo $popup_text; ?></p>
</div>
<?php
}
}
/**
* Converts 3-letter ISO returned from Klarna to 2-letter code used in WooCommerce.
*
* @param $country
*/
function kco_wc_country_code_converter( $country ) {
$countries = array(
'AF' => 'AFG', // Afghanistan.
'AX' => 'ALA', // Aland Islands.
'AL' => 'ALB', // Albania.
'DZ' => 'DZA', // Algeria.
'AS' => 'ASM', // American Samoa.
'AD' => 'AND', // Andorra.
'AO' => 'AGO', // Angola.
'AI' => 'AIA', // Anguilla.
'AQ' => 'ATA', // Antarctica.
'AG' => 'ATG', // Antigua and Barbuda.
'AR' => 'ARG', // Argentina.
'AM' => 'ARM', // Armenia.
'AW' => 'ABW', // Aruba.
'AU' => 'AUS', // Australia.
'AT' => 'AUT', // Austria.
'AZ' => 'AZE', // Azerbaijan.
'BS' => 'BHS', // Bahamas.
'BH' => 'BHR', // Bahrain.
'BD' => 'BGD', // Bangladesh.
'BB' => 'BRB', // Barbados.
'BY' => 'BLR', // Belarus.
'BE' => 'BEL', // Belgium.
'BZ' => 'BLZ', // Belize.
'BJ' => 'BEN', // Benin.
'BM' => 'BMU', // Bermuda.
'BT' => 'BTN', // Bhutan.
'BO' => 'BOL', // Bolivia.
'BQ' => 'BES', // Bonaire, Saint Estatius and Saba.
'BA' => 'BIH', // Bosnia and Herzegovina.
'BW' => 'BWA', // Botswana.
'BV' => 'BVT', // Bouvet Islands.
'BR' => 'BRA', // Brazil.
'IO' => 'IOT', // British Indian Ocean Territory.
'BN' => 'BRN', // Brunei.
'BG' => 'BGR', // Bulgaria.
'BF' => 'BFA', // Burkina Faso.
'BI' => 'BDI', // Burundi.
'KH' => 'KHM', // Cambodia.
'CM' => 'CMR', // Cameroon.
'CA' => 'CAN', // Canada.
'CV' => 'CPV', // Cape Verde.
'KY' => 'CYM', // Cayman Islands.
'CF' => 'CAF', // Central African Republic.
'TD' => 'TCD', // Chad.
'CL' => 'CHL', // Chile.
'CN' => 'CHN', // China.
'CX' => 'CXR', // Christmas Island.
'CC' => 'CCK', // Cocos (Keeling) Islands.
'CO' => 'COL', // Colombia.
'KM' => 'COM', // Comoros.
'CG' => 'COG', // Congo.
'CD' => 'COD', // Congo, Democratic Republic of the.
'CK' => 'COK', // Cook Islands.
'CR' => 'CRI', // Costa Rica.
'CI' => 'CIV', // Côte d\'Ivoire.
'HR' => 'HRV', // Croatia.
'CU' => 'CUB', // Cuba.
'CW' => 'CUW', // Curaçao.
'CY' => 'CYP', // Cyprus.
'CZ' => 'CZE', // Czech Republic.
'DK' => 'DNK', // Denmark.
'DJ' => 'DJI', // Djibouti.
'DM' => 'DMA', // Dominica.
'DO' => 'DOM', // Dominican Republic.
'EC' => 'ECU', // Ecuador.
'EG' => 'EGY', // Egypt.
'SV' => 'SLV', // El Salvador.
'GQ' => 'GNQ', // Equatorial Guinea.
'ER' => 'ERI', // Eritrea.
'EE' => 'EST', // Estonia.
'ET' => 'ETH', // Ethiopia.
'FK' => 'FLK', // Falkland Islands.
'FO' => 'FRO', // Faroe Islands.
'FJ' => 'FIJ', // Fiji.
'FI' => 'FIN', // Finland.
'FR' => 'FRA', // France.
'GF' => 'GUF', // French Guiana.
'PF' => 'PYF', // French Polynesia.
'TF' => 'ATF', // French Southern Territories.
'GA' => 'GAB', // Gabon.
'GM' => 'GMB', // Gambia.
'GE' => 'GEO', // Georgia.
'DE' => 'DEU', // Germany.
'GH' => 'GHA', // Ghana.
'GI' => 'GIB', // Gibraltar.
'GR' => 'GRC', // Greece.
'GL' => 'GRL', // Greenland.
'GD' => 'GRD', // Grenada.
'GP' => 'GLP', // Guadeloupe.
'GU' => 'GUM', // Guam.
'GT' => 'GTM', // Guatemala.
'GG' => 'GGY', // Guernsey.
'GN' => 'GIN', // Guinea.
'GW' => 'GNB', // Guinea-Bissau.
'GY' => 'GUY', // Guyana.
'HT' => 'HTI', // Haiti.
'HM' => 'HMD', // Heard Island and McDonald Islands.
'VA' => 'VAT', // Holy See (Vatican City State).
'HN' => 'HND', // Honduras.
'HK' => 'HKG', // Hong Kong.
'HU' => 'HUN', // Hungary.
'IS' => 'ISL', // Iceland.
'IN' => 'IND', // India.
'ID' => 'IDN', // Indonesia.
'IR' => 'IRN', // Iran.
'IQ' => 'IRQ', // Iraq.
'IE' => 'IRL', // Republic of Ireland.
'IM' => 'IMN', // Isle of Man.
'IL' => 'ISR', // Israel.
'IT' => 'ITA', // Italy.
'JM' => 'JAM', // Jamaica.
'JP' => 'JPN', // Japan.
'JE' => 'JEY', // Jersey.
'JO' => 'JOR', // Jordan.
'KZ' => 'KAZ', // Kazakhstan.
'KE' => 'KEN', // Kenya.
'KI' => 'KIR', // Kiribati.
'KP' => 'PRK', // Korea, Democratic People's Republic of.
'KR' => 'KOR', // Korea, Republic of (South).
'KW' => 'KWT', // Kuwait.
'KG' => 'KGZ', // Kyrgyzstan.
'LA' => 'LAO', // Laos.
'LV' => 'LVA', // Latvia.
'LB' => 'LBN', // Lebanon.
'LS' => 'LSO', // Lesotho.
'LR' => 'LBR', // Liberia.
'LY' => 'LBY', // Libya.
'LI' => 'LIE', // Liechtenstein.
'LT' => 'LTU', // Lithuania.
'LU' => 'LUX', // Luxembourg.
'MO' => 'MAC', // Macao S.A.R., China.
'MK' => 'MKD', // Macedonia.
'MG' => 'MDG', // Madagascar.
'MW' => 'MWI', // Malawi.
'MY' => 'MYS', // Malaysia.
'MV' => 'MDV', // Maldives.
'ML' => 'MLI', // Mali.
'MT' => 'MLT', // Malta.
'MH' => 'MHL', // Marshall Islands.
'MQ' => 'MTQ', // Martinique.
'MR' => 'MRT', // Mauritania.
'MU' => 'MUS', // Mauritius.
'YT' => 'MYT', // Mayotte.
'MX' => 'MEX', // Mexico.
'FM' => 'FSM', // Micronesia.
'MD' => 'MDA', // Moldova.
'MC' => 'MCO', // Monaco.
'MN' => 'MNG', // Mongolia.
'ME' => 'MNE', // Montenegro.
'MS' => 'MSR', // Montserrat.
'MA' => 'MAR', // Morocco.
'MZ' => 'MOZ', // Mozambique.
'MM' => 'MMR', // Myanmar.
'NA' => 'NAM', // Namibia.
'NR' => 'NRU', // Nauru.
'NP' => 'NPL', // Nepal.
'NL' => 'NLD', // Netherlands.
'AN' => 'ANT', // Netherlands Antilles.
'NC' => 'NCL', // New Caledonia.
'NZ' => 'NZL', // New Zealand.
'NI' => 'NIC', // Nicaragua.
'NE' => 'NER', // Niger.
'NG' => 'NGA', // Nigeria.
'NU' => 'NIU', // Niue.
'NF' => 'NFK', // Norfolk Island.
'MP' => 'MNP', // Northern Mariana Islands.
'NO' => 'NOR', // Norway.
'OM' => 'OMN', // Oman.
'PK' => 'PAK', // Pakistan.
'PW' => 'PLW', // Palau.
'PS' => 'PSE', // Palestinian Territory.
'PA' => 'PAN', // Panama.
'PG' => 'PNG', // Papua New Guinea.
'PY' => 'PRY', // Paraguay.
'PE' => 'PER', // Peru.
'PH' => 'PHL', // Philippines.
'PN' => 'PCN', // Pitcairn.
'PL' => 'POL', // Poland.
'PT' => 'PRT', // Portugal.
'PR' => 'PRI', // Puerto Rico.
'QA' => 'QAT', // Qatar.
'RE' => 'REU', // Reunion.
'RO' => 'ROU', // Romania.
'RU' => 'RUS', // Russia.
'RW' => 'RWA', // Rwanda.
'BL' => 'BLM', // Saint Bartholemy.
'SH' => 'SHN', // Saint Helena.
'KN' => 'KNA', // Saint Kitts and Nevis.
'LC' => 'LCA', // Saint Lucia.
'MF' => 'MAF', // Saint Martin (French part).
'SX' => 'SXM', // Sint Maarten / Saint Martin (Dutch part).
'PM' => 'SPM', // Saint Pierre and Miquelon.
'VC' => 'VCT', // Saint Vincent and the Grenadines.
'WS' => 'WSM', // Samoa.
'SM' => 'SMR', // San Marino.
'ST' => 'STP', // Sso Tome and Principe.
'SA' => 'SAU', // Saudi Arabia.
'SN' => 'SEN', // Senegal.
'RS' => 'SRB', // Serbia.
'SC' => 'SYC', // Seychelles.
'SL' => 'SLE', // Sierra Leone.
'SG' => 'SGP', // Singapore.
'SK' => 'SVK', // Slovakia.
'SI' => 'SVN', // Slovenia.
'SB' => 'SLB', // Solomon Islands.
'SO' => 'SOM', // Somalia.
'ZA' => 'ZAF', // South Africa.
'GS' => 'SGS', // South Georgia/Sandwich Islands.
'SS' => 'SSD', // South Sudan.
'ES' => 'ESP', // Spain.
'LK' => 'LKA', // Sri Lanka.
'SD' => 'SDN', // Sudan.
'SR' => 'SUR', // Suriname.
'SJ' => 'SJM', // Svalbard and Jan Mayen.
'SZ' => 'SWZ', // Swaziland.
'SE' => 'SWE', // Sweden.
'CH' => 'CHE', // Switzerland.
'SY' => 'SYR', // Syria.
'TW' => 'TWN', // Taiwan.
'TJ' => 'TJK', // Tajikistan.
'TZ' => 'TZA', // Tanzania.
'TH' => 'THA', // Thailand.
'TL' => 'TLS', // Timor-Leste.
'TG' => 'TGO', // Togo.
'TK' => 'TKL', // Tokelau.
'TO' => 'TON', // Tonga.
'TT' => 'TTO', // Trinidad and Tobago.
'TN' => 'TUN', // Tunisia.
'TR' => 'TUR', // Turkey.
'TM' => 'TKM', // Turkmenistan.
'TC' => 'TCA', // Turks and Caicos Islands.
'TV' => 'TUV', // Tuvalu.
'UG' => 'UGA', // Uganda.
'UA' => 'UKR', // Ukraine.
'AE' => 'ARE', // United Arab Emirates.
'GB' => 'GBR', // United Kingdom.
'US' => 'USA', // United States.
'UM' => 'UMI', // United States Minor Outlying Islands.
'UY' => 'URY', // Uruguay.
'UZ' => 'UZB', // Uzbekistan.
'VU' => 'VUT', // Vanuatu.
'VE' => 'VEN', // Venezuela.
'VN' => 'VNM', // Vietnam.
'VG' => 'VGB', // Virgin Islands, British.
'VI' => 'VIR', // Virgin Island, U.S..
'WF' => 'WLF', // Wallis and Futuna.
'EH' => 'ESH', // Western Sahara.
'YE' => 'YEM', // Yemen.
'ZM' => 'ZMB', // Zambia.
'ZW' => 'ZWE', // Zimbabwe.
);
return array_search( strtoupper( $country ), $countries, true );
}
/**
* Prints error notices if needed.
*/
function kco_wc_print_notices() {
if ( isset( $_GET['stock_validate_failed'] ) ) {
wc_add_notice( __( 'Not all products are in stock.', 'klarna-checkout-for-woocommerce' ), 'error' );
} elseif ( isset( $_GET['no_shipping'] ) ) {
wc_add_notice( __( 'No shipping was selected.', 'klarna-checkout-for-woocommerce' ), 'error' );
} elseif ( isset( $_GET['required_fields'] ) ) {
$failed_fields = json_decode( base64_decode( $_GET['required_fields'] ) );
$fields_string = '';
foreach ( $failed_fields as $field ) {
$fields_string = $fields_string . ' ' . $field;
}
wc_add_notice( __( sprintf( 'The following fields are required:%s.', $fields_string ), 'klarna-checkout-for-woocommerce' ), 'error' );
}
}

View File

@@ -0,0 +1,59 @@
<?php
/**
* Admin View: Page - Status Report.
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
?>
<table class="wc_status_table widefat" cellspacing="0">
<thead>
<tr>
<th colspan="3" data-export-label="Klarna Checkout">
<h2><?php _e( 'Klarna Checkout', 'klarna-checkout-for-woocommerce' ); ?><?php echo wc_help_tip( __( 'Klarna Checkout System Status.', 'klarna-checkout-for-woocommerce' ) ); ?></h2>
</th>
</tr>
</thead>
<tbody>
<tr>
<td data-export-label="Orders created via API callback"><?php _e( 'Orders created via API callback', 'klarna-checkout-for-woocommerce' ); ?>:</td>
<td class="help"><?php echo wc_help_tip( __( 'Displays the number of orders created via the API callback feature during the last month.', 'klarna-checkout-for-woocommerce' ) ); ?></td>
<td>
<?php
$query = new WC_Order_Query( array(
'limit' => -1,
'orderby' => 'date',
'order' => 'DESC',
'return' => 'ids',
'payment_method' => 'kco',
'date_created' => '>' . ( time() - MONTH_IN_SECONDS )
) );
$orders = $query->get_orders();
$amont_of_klarna_orders = count( $orders );
$amont_of_api_callback_orders = 0;
foreach( $orders as $order_id ) {
if( 'klarna_checkout_backup_order_creation' == get_post_meta( $order_id, '_created_via', true ) ) {
$amont_of_api_callback_orders++;
}
}
if( $amont_of_api_callback_orders > 0 ) {
$percent_of_orders = round( ($amont_of_api_callback_orders/$amont_of_klarna_orders) * 100 );
} else {
$percent_of_orders = 0;
}
if( $percent_of_orders >= 10 ) {
$status = 'error';
} else {
$status = 'yes';
}
echo '<strong><mark class="' . $status . '">' . $percent_of_orders . '% (' . $amont_of_api_callback_orders . ' of ' . $amont_of_klarna_orders . ')</mark></strong> of all orders payed via Klarna Checkout was created via API callback during the last month. This is a fallback order creation feature. You should aim for 0%.';
?>
</td>
</tr>
</tbody>
</table>