<?php
/**
* WooCommerceKlaviyo Webhook Service
*
* Handles outgoing requests to Klaviyo's webhook endpoint.
*
* @package WooCommerceKlaviyo/Webhook
* @since 2.4.2
*/
// The last woocommerce_add_to_cart hook has priority of 20 so we need to make sure we
// we fire after it. The higher the number, the later the function executes.
add_action( 'woocommerce_add_to_cart', 'kl_added_to_cart_event', 25, 3 );
/**
* If the param is an instance of a WP_Error, returns
* an empty array. If the param is not a WP_Error then
* runs strip_tags and explode to return an array of strings.
*
* @param string $list String of product terms.
* @return array
*/
function kl_strip_explode( $list ) {
if ( $list instanceof WP_Error ) {
return array();
}
return explode( ', ', strip_tags( $list ) );
}
/**
* Creates a Request argument that can be used within
* wp_remote_post method for added to cart event
*
* @param array $payload of the added to cart event.
* @return array
*/
function kl_added_to_cart_options( $payload ) {
return array(
'blocking' => false,
'headers' => array(
'Content-Type' => 'application/json',
'X-Klaviyo-User-Agent' => kl_get_plugin_usage_meta_data(),
'revision' => '2023-08-15',
),
'body' => wp_json_encode( $payload ),
);
}
/**
* Set wck_cart data then build the Added Item and return the array
* of the full cart data.
*
* @param object $added_product Added product data.
* @param int $quantity Quantity of item added to cart.
* @param object $cart Cart data.
* @return array
*/
function kl_build_add_to_cart_data( $added_product, $quantity, $cart ) {
$wck_cart = wck_build_cart_data( $cart );
$added_product_id = $added_product->get_id();
$added_to_cart = array(
'value' => (float) $cart->total,
'AddedItemCategories' => (array) kl_strip_explode( wc_get_product_category_list( $added_product_id ) ),
'AddedItemImageURL' => (string) wp_get_attachment_url( get_post_thumbnail_id( $added_product_id ) ),
'AddedItemPrice' => (float) $added_product->get_price(),
'AddedItemQuantity' => (int) $quantity,
'AddedItemProductID' => (int) $added_product_id,
'AddedItemProductName' => (string) $added_product->get_name(),
'AddedItemSKU' => (string) $added_product->get_sku(),
'AddedItemTags' => (array) kl_strip_explode( wc_get_product_tag_list( $added_product_id ) ),
'AddedItemURL' => (string) $added_product->get_permalink(),
'ItemNames' => (array) $wck_cart['ItemNames'],
'Categories' => isset( $wck_cart['Categories'] ) ? (array) $wck_cart['Categories'] : array(),
'ItemCount' => (int) $wck_cart['Quantity'],
'Tags' => isset( $wck_cart['Tags'] ) ? (array) $wck_cart['Tags'] : array(),
'extra' => $wck_cart['$extra'],
);
/**
* Allow developers to customise the payload before it is sent to Klaviyo.
*
* The `kl_added_to_cart` filter allows you to add additional properties to the [Added to Cart]
* (https://help.klaviyo.com/hc/en-us/articles/360030732832#added-to-cart2) event.
*
* This example below shows you how to add custom fields to the event
*
* add_filter('kl_added_to_cart','kl_modify_added_to_cart', 1, 4);
*
* function kl_modify_added_to_cart($added_to_cart, $added_product, $quantity, $wck_cart) {
* $product_lead_time = get_field('leadtime',$added_product->get_id());
* $product_designer = get_field('designer', $added_product->get_id());
* $added_to_cart['LeadTime'] = $product_lead_time;
* $added_to_cart['Designer'] = $product_designer;
* return $added_to_cart;
* }
*
* @since 3.0.12
*
* @param array $added_to_cart The Klaviyo added to cart payload
* @param WC_Product $added_product The product being added to the cart
* @param integer $quantity The quantity of the item being added
* @param array $wck_cart The entire Klaviyo cart object.
*/
$added_to_cart = apply_filters( 'kl_added_to_cart', $added_to_cart, $added_product, $quantity, $wck_cart );
return $added_to_cart;
}
/**
* Check that the Public API token is set, build Added to Cart event payload,
* create an options request array using kl_added_to_cart_options function and
* send the request.
*
* @param array $customer_identify Identifies the customer based on email or exchange_id.
* @param array $data Cart and AddedItem data.
* @returns null
*/
function kl_track_request( $customer_identify, $data ) {
$public_api_key = WCK()->options->get_klaviyo_option( 'klaviyo_public_api_key' );
if ( ! $public_api_key ) {
return;
}
$metric_data = array(
'data' => array(
'type' => 'metric',
'attributes' => array(
'name' => 'Added to Cart',
),
),
);
$profile_data = array(
'data' => array(
'type' => 'profile',
'attributes' => $customer_identify
),
);
// 'value' should be a key under 'attributes'
$value = $data['value'];
unset($data['value']);
$attributes = array(
'properties' => $data,
'metric' => $metric_data,
'profile' => $profile_data,
'value' => $value,
);
$atc_data = array(
'data' => array(
'type' => 'event',
'attributes' => $attributes
),
);
$url = 'https://a.klaviyo.com/client/events/?company_id=' . $public_api_key;
$options = kl_added_to_cart_options( $atc_data );
wp_remote_post( $url, $options );
}
/**
* Set customer identity, call kl_build_add_to_cart_data and then call kl_track_request
* to trigger the event.
*
* @param string $cart_item_key Unique key for item in cart.
* @param int $product_id ID of item added to cart.
* @param int $quantity Quantity of item added to cart.
* @returns null
*/
function kl_added_to_cart_event( $cart_item_key, $product_id, $quantity ) {
if ( ! isset( $_COOKIE['__kla_id'] ) ) {
return;
}
$kl_cookie = sanitize_text_field( wp_unslash( $_COOKIE['__kla_id'] ) );
$kl_decoded_cookie = json_decode( base64_decode( $kl_cookie ), true );
$has_exchange_id = isset( $kl_decoded_cookie['$exchange_id'] );
$has_email = isset( $kl_decoded_cookie['email'] );
$customer_identify = array();
if ( $has_exchange_id ) {
$customer_identify['_kx'] = $kl_decoded_cookie['$exchange_id'];
}
if ( $has_email ) {
$customer_identify['email'] = $kl_decoded_cookie['email'];
}
if (!$has_exchange_id && !$has_email) {
return;
}
$added_product = wc_get_product( $product_id );
if ( ! $added_product instanceof WC_Product ) {
return;
}
kl_track_request( $customer_identify, kl_build_add_to_cart_data( $added_product, $quantity, WC()->cart ) );
}