<?php
/**
* WooCommerceKlaviyo Order Functions
*
* Functions for order specific things.
*
* @package WooCommerceKlaviyo/Functions
* @version 2.0.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
/**
* Builds composite products for cart.
*
* @param array $composite_products Composite products to add to cart.
* @return void
*/
function add_composite_products_cart( $composite_products ) {
foreach ( $composite_products as $product ) {
$container = array();
foreach ( $product as $i => $v ) {
$item = $v['item'];
$container_id = $item['container_id'];
if ( isset( $item['attributes'] ) ) {
$container[ $container_id ] = array(
'product_id' => $item['product_id'],
'quantity' => $item['quantity'],
'variation_id' => $item['variation_id'],
'attributes' => $item['attributes'],
);
} else {
$container[ $container_id ] = array(
'product_id' => $item['product_id'],
'quantity' => $item['quantity'],
);
}
}
$added = WC_CP()->cart->add_composite_to_cart( $v['composite_id'], $v['composite_quantity'], $container );
}
}
/**
* Gets email of current user or commenter.
*
* @param WP_User $current_user The current WordPress user.
* @return mixed|string
*/
function get_email( $current_user ) {
$email = '';
if ( $current_user->user_email ) {
$email = $current_user->user_email;
} else {
// See if current user is a commenter.
$commenter = wp_get_current_commenter();
if ( $commenter['comment_author_email'] ) {
$email = $commenter['comment_author_email'];
}
}
return $email;
}
/**
* Rebuild cart from Abandoned Checkout query param.
*
* @return void
*/
function wck_rebuild_cart() {
// Exit if in back-end.
if ( is_admin() ) {
return;
}
global $woocommerce;
// Exit if not on cart page or no wck_rebuild_cart parameter.
$current_url = build_current_url();
$utm_wck_rebuild_cart = isset( $_GET['wck_rebuild_cart'] ) ? sanitize_text_field( wp_unslash( $_GET['wck_rebuild_cart'] ) ) : '';
if ( wc_get_cart_url() !== $current_url[0] || '' === $utm_wck_rebuild_cart ) {
return;
}
// Rebuild cart.
$woocommerce->cart->empty_cart( true );
$woocommerce->cart->get_cart();
$kl_cart = json_decode( base64_decode( $utm_wck_rebuild_cart ), true );
/**
* Allow developers to customise the payload before the cart is rebuilt.
*
* @since 3.0.12
*
* @param mixed $kl_cart The Klaviyo added to cart payload
*/
$kl_cart = apply_filters( 'kl_cart_rebuild', $kl_cart );
$composite_products = $kl_cart['composite'];
$normal_products = $kl_cart['normal_products'];
foreach ( $normal_products as $product ) {
$cart_key = $woocommerce->cart->add_to_cart( $product['product_id'], $product['quantity'], $product['variation_id'], $product['variation'] );
}
if ( class_exists( 'WC_Composite_Products' ) ) {
add_composite_products_cart( $composite_products );
}
/**
* Allow developers to call a callback on cart rebuild.
*
* The `kl_cart_rebuild_complete` allows you to perform an action after Klaviyo has recovered and rebuilt the customers cart.
*
* @since 3.0.12
*
* @param mixed $kl_cart The Klaviyo added to cart payload
*/
do_action( 'kl_cart_rebuild_complete', $kl_cart );
$carturl = wc_get_cart_url();
if ( wc_get_cart_url() === $current_url[0] ) {
header( 'Refresh:0; url=' . $carturl );
}
}
/**
* Construct current url.
*
* @return false|string[]
*/
function build_current_url() {
$server_protocol = isset( $_SERVER['HTTPS'] ) ? 'https' : 'http';
$server_host = isset( $_SERVER['HTTP_HOST'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ) : '';
$server_uri = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
return explode( '?', $server_protocol . '://' . $server_host . $server_uri );
}
/**
* Insert tracking code for tracking started checkout.
*
* @return void
*/
function wck_started_checkout_tracking() {
global $current_user;
wp_reset_query();
wp_get_current_user();
$cart = WC()->cart;
$event_data = wck_build_cart_data( $cart );
if ( empty( $event_data['$extra']['Items'] ) ) {
return;
}
$event_data['$service'] = 'woocommerce';
// Remove top level properties to maintain consistent Started Checkout event data in 2.5.0.
unset( $event_data['Tags'] );
unset( $event_data['Quantity'] );
$email = get_email( $current_user );
/** Adding apply_filter hook to modify the $event_data array which is passed to wck-started-checkout.js
*
* The `kl_started_checkout` filter allows you to add additional top level properties to the
* [Started Checkout](https://help.klaviyo.com/hc/en-us/articles/360030732832#started-checkout1) event.
*
* The example below will add a "ReferralCode" property to the Started Checkout event
*
* add_filter('kl_started_checkout','kl_modify_started_checkout', 1, 2);
*
* function kl_modify_started_checkout($checkout_data, $cart){
* $referrer = htmlspecialchars($_COOKIE['referral_code']);
* if(isset($referrer)){
* $referrer = "Direct";
* }
* $checkout_data['ReferralCode'] = $referrer;
* return $checkout_data;
* }
*
* @since 3.0.12
*
* @param array $event_data
* @param WC_Cart $cart
*/
$event_data = apply_filters( 'kl_started_checkout', $event_data, $cart );
$started_checkout_data = array(
'email' => $email,
'event_data' => $event_data,
);
// Pass Started Checkout event data to javascript attaching to 'wck_started_checkout' handle.
wp_localize_script( 'wck_started_checkout', 'kl_checkout', $started_checkout_data );
}
// Load javascript file for Started Checkout events.
add_action( 'wp_enqueue_scripts', 'load_started_checkout' );
/**
* Check if page is a checkout page, if so load the Started Checkout javascript file.
*/
function load_started_checkout() {
require_once __DIR__ . '/class-wck-api.php';
/**
* Override whether a page is a checkout page for purposes of whether to load the started checkout js.
*
* @since 3.0.8
*/
$should_add_started_checkout = apply_filters( 'wck_should_add_started_checkout', is_checkout() );
if ( $should_add_started_checkout ) {
$token = WCK()->options->get_klaviyo_option( 'klaviyo_public_api_key' );
wp_enqueue_script( 'wck_started_checkout', plugins_url( '/js/wck-started-checkout.js', __FILE__ ), null, WCK_API::VERSION, true );
wp_localize_script( 'wck_started_checkout', 'public_key', array( 'token' => $token ) );
wp_localize_script( 'wck_started_checkout', 'plugin_meta_data', array('data' => kl_get_plugin_usage_meta_data()));
// Build started checkout event data and add inline script to html.
wck_started_checkout_tracking();
}
}
add_action( 'wp_loaded', 'wck_rebuild_cart' );
/**
* Add checkbox to subscribe profiles to email list during checkout.
*
* @param array[] $fields Checkout form fields.
* @return array[] $fields
*/
function kl_checkbox_custom_checkout_field( $fields ) {
$klaviyo_settings = get_option( 'klaviyo_settings' );
$fields['billing']['kl_newsletter_checkbox'] = array(
'type' => 'checkbox',
'class' => array( 'kl_newsletter_checkbox_field' ),
'label' => $klaviyo_settings['klaviyo_newsletter_text'],
'value' => true,
'default' => 0,
'required' => false,
);
return $fields;
}
/**
* Add checkbox to subscribe profiles to SMS list during checkout.
*
* @param array[] $fields Checkout form fields.
* @return array[] $fields
*/
function kl_sms_consent_checkout_field( $fields ) {
$klaviyo_settings = get_option( 'klaviyo_settings' );
$fields['billing']['kl_sms_consent_checkbox'] = array(
'type' => 'checkbox',
'class' => array( 'kl_sms_consent_checkbox_field' ),
'label' => $klaviyo_settings['klaviyo_sms_consent_text'],
'value' => true,
'default' => 0,
'required' => false,
);
return $fields;
}
/**
* Echo compliance text.
*
* @return void
*/
function kl_sms_compliance_text() {
$klaviyo_settings = get_option( 'klaviyo_settings' );
echo esc_html($klaviyo_settings['klaviyo_sms_consent_disclosure_text']);
}
/**
* Send consent settings to Klaviyo.
*
* @return void
*/
function kl_add_to_list() {
// This method is called from within WC_Checkout::process_checkout where nonce validation is done. Ignoring here.
// phpcs:disable WordPress.Security.NonceVerification.Missing
$klaviyo_settings = get_option( 'klaviyo_settings' );
$email = isset( $_POST['billing_email'] ) ? sanitize_text_field( wp_unslash( $_POST['billing_email'] ) ) : null;
$phone = isset( $_POST['billing_phone'] ) ? sanitize_text_field( wp_unslash( $_POST['billing_phone'] ) ) : null;
$country = isset( $_POST['billing_country'] ) ? sanitize_text_field( wp_unslash( $_POST['billing_country'] ) ) : null;
$url = 'https://a.klaviyo.com/api/webhook/integration/woocommerce?c=' . $klaviyo_settings['klaviyo_public_api_key'];
$body = array(
'data' => array(),
);
if ( isset( $_POST['kl_sms_consent_checkbox'] ) && sanitize_text_field( wp_unslash( $_POST['kl_sms_consent_checkbox'] ) ) ) {
array_push(
$body['data'],
array(
'customer' => array(
'email' => $email,
'country' => $country,
'phone' => $phone,
),
'consent' => true,
'updated_at' => gmdate( DATE_ATOM, date_timestamp_get( date_create() ) ),
'consent_type' => 'sms',
'group_id' => $klaviyo_settings['klaviyo_sms_list_id'],
)
);
}
if ( isset( $_POST['kl_newsletter_checkbox'] ) && sanitize_text_field( wp_unslash( $_POST['kl_newsletter_checkbox'] ) ) ) {
array_push(
$body['data'],
array(
'customer' => array(
'email' => $email,
'phone' => $phone,
),
'consent' => true,
'updated_at' => gmdate( DATE_ATOM, date_timestamp_get( date_create() ) ),
'consent_type' => 'email',
'group_id' => $klaviyo_settings['klaviyo_newsletter_list_id'],
)
);
}
// phpcs:enable WordPress.Security.NonceVerification.Missing
wp_remote_post(
$url,
array(
'method' => 'POST',
'httpversion' => '1.0',
'blocking' => false,
'headers' => array(
'X-WC-Webhook-Topic' => 'custom/consent',
'Content-Type' => 'application/json',
),
'body' => json_encode( $body ),
'data_format' => 'body',
)
);
}
$klaviyo_settings = get_option( 'klaviyo_settings' );
if (
isset( $klaviyo_settings['klaviyo_subscribe_checkbox'] )
&& $klaviyo_settings['klaviyo_subscribe_checkbox']
&& ! empty( $klaviyo_settings['klaviyo_newsletter_list_id'] )
) {
// Add the checkbox field.
add_filter( 'woocommerce_checkout_fields', 'kl_checkbox_custom_checkout_field', 11 );
// Post list request to Klaviyo.
add_action( 'woocommerce_checkout_update_order_meta', 'kl_add_to_list' );
}
if (
isset( $klaviyo_settings['klaviyo_sms_subscribe_checkbox'] )
&& $klaviyo_settings['klaviyo_sms_subscribe_checkbox']
&& ! empty( $klaviyo_settings['klaviyo_sms_list_id'] )
) {
// Add the checkbox field.
add_filter( 'woocommerce_checkout_fields', 'kl_sms_consent_checkout_field', 11 );
// Add data compliance messaging to checkout page.
add_filter( 'woocommerce_after_checkout_billing_form', 'kl_sms_compliance_text' );
// Post SMS request to Klaviyo.
add_action( 'woocommerce_checkout_update_order_meta', 'kl_add_to_list' );
}