Files
old-new-wiaas/backend/wp-content/plugins/klarna-checkout-for-woocommerce/includes/class-klarna-checkout-for-woocommerce-api-callbacks.php
2018-06-14 16:49:28 +02:00

463 lines
18 KiB
PHP

<?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();