diff --git a/README.txt b/README.txt
index dfedf11..bdac595 100644
--- a/README.txt
+++ b/README.txt
@@ -3,7 +3,7 @@ Contributors: gtmserver,bukashk0zzz
Tags: google tag manager, google tag manager server side, gtm, gtm server side, tag manager, tagmanager, analytics, google, serverside, server-side, gtag
Requires at least: 5.2.0
Tested up to: 6.7.0
-Stable tag: 2.1.23
+Stable tag: 2.1.24
License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
@@ -67,6 +67,10 @@ Yes. How t
== Changelog ==
+= 2.1.24 =
+* Added Order paid completed webhook
+* Added cart_hash key to webhooks
+
= 2.1.23 =
* Fix purchase event
diff --git a/assets/js/admin-javascript.js b/assets/js/admin-javascript.js
index 871bcaa..115a574 100644
--- a/assets/js/admin-javascript.js
+++ b/assets/js/admin-javascript.js
@@ -54,12 +54,13 @@ jQuery( document ).ready(
}
var isPurchaseChecked = jQuery( '#gtm_server_side_webhooks_purchase' ).is( ':checked' );
- var isRefundChecked = jQuery( '#gtm_server_side_webhooks_refund' ).is( ':checked' );
var isProcessingChecked = jQuery( '#gtm_server_side_webhooks_processing' ).is( ':checked' );
+ var isCompletedChecked = jQuery( '#gtm_server_side_webhooks_completed' ).is( ':checked' );
+ var isRefundChecked = jQuery( '#gtm_server_side_webhooks_refund' ).is( ':checked' );
- return isPurchaseChecked || isRefundChecked || isProcessingChecked;
+ return isPurchaseChecked || isProcessingChecked || isCompletedChecked || isRefundChecked;
},
- 'Select purchase and/or refund webhook'
+ 'Select one or more webhooks'
);
// Tab "General".
diff --git a/bootstrap.php b/bootstrap.php
index 965a2c2..a38552b 100644
--- a/bootstrap.php
+++ b/bootstrap.php
@@ -32,6 +32,7 @@
define( 'GTM_SERVER_SIDE_FIELD_WEBHOOKS_CONTAINER_URL', 'gtm_server_side_webhooks_container_url' );
define( 'GTM_SERVER_SIDE_FIELD_WEBHOOKS_PURCHASE', 'gtm_server_side_webhooks_purchase' );
define( 'GTM_SERVER_SIDE_FIELD_WEBHOOKS_PROCESSING', 'gtm_server_side_webhooks_processing' );
+define( 'GTM_SERVER_SIDE_FIELD_WEBHOOKS_COMPLETED', 'gtm_server_side_webhooks_completed' );
define( 'GTM_SERVER_SIDE_FIELD_WEBHOOKS_REFUND', 'gtm_server_side_webhooks_refund' );
define( 'GTM_SERVER_SIDE_FIELD_PLACEMENT_VALUE_CODE', 'code' );
diff --git a/gtm-server-side.php b/gtm-server-side.php
index 7138e2e..6f05819 100644
--- a/gtm-server-side.php
+++ b/gtm-server-side.php
@@ -10,7 +10,7 @@
* Plugin Name: GTM Server Side
* Plugin URI: https://wordpress.org/plugins/gtm-server-side/
* Description: Enhance conversion tracking by implementing server-side tagging using server Google Tag Manager container. Effortlessly configure data layer events in web GTM, send webhooks, set up custom loader, and extend cookie lifetime.
- * Version: 2.1.23
+ * Version: 2.1.24
* Author: Stape
* Author URI: https://stape.io
* License: GPL-2.0+
@@ -32,6 +32,7 @@
add_action( 'gtm_server_side', array( GTM_Server_Side_I18n::class, 'instance' ) );
add_action( 'gtm_server_side', array( GTM_Server_Side_Webhook_Purchase::class, 'instance' ) );
add_action( 'gtm_server_side', array( GTM_Server_Side_Webhook_Processing::class, 'instance' ) );
+add_action( 'gtm_server_side', array( GTM_Server_Side_Webhook_Completed::class, 'instance' ) );
add_action( 'gtm_server_side', array( GTM_Server_Side_Webhook_Refund::class, 'instance' ) );
add_action( 'gtm_server_side_admin', array( GTM_Server_Side_Admin_Settings::class, 'instance' ) );
add_action( 'gtm_server_side_admin', array( GTM_Server_Side_Admin_Ajax::class, 'instance' ) );
diff --git a/includes/class-gtm-server-side-admin-ajax.php b/includes/class-gtm-server-side-admin-ajax.php
index 6e91f63..03492fe 100644
--- a/includes/class-gtm-server-side-admin-ajax.php
+++ b/includes/class-gtm-server-side-admin-ajax.php
@@ -54,13 +54,19 @@ public function gtm_server_side_webhook_test() {
);
}
- $is_refund = GTM_Server_Side_Helpers::get_option( GTM_SERVER_SIDE_FIELD_WEBHOOKS_REFUND );
$is_purchase = GTM_Server_Side_Helpers::get_option( GTM_SERVER_SIDE_FIELD_WEBHOOKS_PURCHASE );
$is_processing = GTM_Server_Side_Helpers::get_option( GTM_SERVER_SIDE_FIELD_WEBHOOKS_PROCESSING );
- if ( empty( $is_purchase ) && empty( $is_refund ) && empty( $is_processing ) ) {
+ $is_completed = GTM_Server_Side_Helpers::get_option( GTM_SERVER_SIDE_FIELD_WEBHOOKS_COMPLETED );
+ $is_refund = GTM_Server_Side_Helpers::get_option( GTM_SERVER_SIDE_FIELD_WEBHOOKS_REFUND );
+ if (
+ empty( $is_purchase ) &&
+ empty( $is_processing ) &&
+ empty( $is_completed ) &&
+ empty( $is_refund )
+ ) {
wp_send_json_error(
array(
- 'message' => __( 'Purchase or order paid or refund webhook is required.', 'gtm-server-side' ),
+ 'message' => __( 'Purchase or order paid processing or order paid completed or refund webhook is required.', 'gtm-server-side' ),
)
);
}
@@ -74,6 +80,10 @@ public function gtm_server_side_webhook_test() {
$answer[] = $this->send_webhook_processing();
}
+ if ( ! empty( $is_completed ) ) {
+ $answer[] = $this->send_webhook_completed();
+ }
+
if ( ! empty( $is_refund ) ) {
$answer[] = $this->send_webhook_refund();
}
@@ -102,28 +112,7 @@ public function gtm_server_side_webhook_test() {
private function send_webhook_purchase() {
$request = array(
'event' => 'purchase',
- 'ecommerce' => array(
- 'transaction_id' => '358',
- 'affiliation' => 'test',
- 'value' => 18.00,
- 'tax' => 0,
- 'shipping' => 0,
- 'currency' => 'USD',
- 'coupon' => 'test_coupon',
- 'items' => array(
- array(
- 'item_name' => 'Beanie',
- 'item_brand' => 'Stape',
- 'item_id' => '15',
- 'item_sku' => 'woo-beanie',
- 'price' => 18.00,
- 'item_category' => 'Clothing',
- 'item_category2' => 'Accessories',
- 'quantity' => 1,
- 'index' => 1,
- ),
- ),
- ),
+ 'ecommerce' => $this->get_ecommerce_data(),
);
$result = $this->send_request( $request );
@@ -139,47 +128,49 @@ private function send_webhook_purchase() {
}
/**
- * Send webhooks processing (order paid).
+ * Send webhooks processing (order paid processing).
*
* @return string
*/
private function send_webhook_processing() {
$request = array(
'event' => 'order_paid',
- 'ecommerce' => array(
- 'transaction_id' => '358',
- 'affiliation' => 'test',
- 'value' => 18.00,
- 'tax' => 0,
- 'shipping' => 0,
- 'currency' => 'USD',
- 'coupon' => 'test_coupon',
- 'items' => array(
- array(
- 'item_name' => 'Beanie',
- 'item_brand' => 'Stape',
- 'item_id' => '15',
- 'item_sku' => 'woo-beanie',
- 'price' => 18.00,
- 'item_category' => 'Clothing',
- 'item_category2' => 'Accessories',
- 'quantity' => 1,
- 'index' => 1,
- ),
- ),
- ),
+ 'ecommerce' => $this->get_ecommerce_data(),
);
$result = $this->send_request( $request );
if ( is_wp_error( $result ) ) {
wp_send_json_error(
array(
- 'message' => __( 'Some problem with Purchase webhook.', 'gtm-server-side' ),
+ 'message' => __( 'Some problem with order paid processing webhook.', 'gtm-server-side' ),
+ )
+ );
+ }
+
+ return __( 'Order paid processing webhook sent.', 'gtm-server-side' );
+ }
+
+ /**
+ * Send webhooks completed (order paid completed).
+ *
+ * @return string
+ */
+ private function send_webhook_completed() {
+ $request = array(
+ 'event' => 'order_completed',
+ 'ecommerce' => $this->get_ecommerce_data(),
+ );
+
+ $result = $this->send_request( $request );
+ if ( is_wp_error( $result ) ) {
+ wp_send_json_error(
+ array(
+ 'message' => __( 'Some problem with order paid completed webhook.', 'gtm-server-side' ),
)
);
}
- return __( 'Order paid webhook sent.', 'gtm-server-side' );
+ return __( 'Order paid completed webhook sent.', 'gtm-server-side' );
}
/**
@@ -244,7 +235,7 @@ private function send_request( $body ) {
}
/**
- * Return user request test data
+ * Return user request test data.
*
* @return array
*/
@@ -275,4 +266,34 @@ private function get_request_user_data() {
'new_customer' => 'false',
);
}
+
+ /**
+ * Return ecommerce test data.
+ *
+ * @return array
+ */
+ private function get_ecommerce_data() {
+ return array(
+ 'transaction_id' => '358',
+ 'affiliation' => 'test',
+ 'value' => 18.00,
+ 'tax' => 0,
+ 'shipping' => 0,
+ 'currency' => 'USD',
+ 'coupon' => 'test_coupon',
+ 'items' => array(
+ array(
+ 'item_name' => 'Beanie',
+ 'item_brand' => 'Stape',
+ 'item_id' => '15',
+ 'item_sku' => 'woo-beanie',
+ 'price' => 18.00,
+ 'item_category' => 'Clothing',
+ 'item_category2' => 'Accessories',
+ 'quantity' => 1,
+ 'index' => 1,
+ ),
+ ),
+ );
+ }
}
diff --git a/includes/class-gtm-server-side-admin-settings.php b/includes/class-gtm-server-side-admin-settings.php
index cd5c648..39e1448 100644
--- a/includes/class-gtm-server-side-admin-settings.php
+++ b/includes/class-gtm-server-side-admin-settings.php
@@ -441,7 +441,7 @@ function() {
);
add_settings_field(
GTM_SERVER_SIDE_FIELD_WEBHOOKS_PROCESSING,
- __( 'Order paid webhook', 'gtm-server-side' ),
+ __( 'Order paid webhook - processing', 'gtm-server-side' ),
function() {
echo '';
echo '
';
- printf( __( 'Order paid event will be sent whenever an order is paid (has "Processing" status as per Woocommerce documentation).', 'gtm-server-side' ), 'https://woocommerce.com/document/managing-orders/order-statuses/' ); // phpcs:ignore
+ printf( __( 'order_paid event will be sent whenever an order is paid (has "Processing" status as per Woocommerce documentation).', 'gtm-server-side' ), 'https://woocommerce.com/document/managing-orders/order-statuses/' ); // phpcs:ignore
+ },
+ GTM_SERVER_SIDE_ADMIN_SLUG,
+ GTM_SERVER_SIDE_ADMIN_GROUP_WEBHOOKS
+ );
+
+ register_setting(
+ GTM_SERVER_SIDE_ADMIN_GROUP,
+ GTM_SERVER_SIDE_FIELD_WEBHOOKS_COMPLETED,
+ array(
+ 'sanitize_callback' => 'GTM_Server_Side_Helpers::sanitize_bool',
+ )
+ );
+ add_settings_field(
+ GTM_SERVER_SIDE_FIELD_WEBHOOKS_COMPLETED,
+ __( 'Order paid webhook - completed', 'gtm-server-side' ),
+ function() {
+ echo '';
+ echo '
';
+ printf( __( 'order_completed event will be sent whenever order status becomes completed (has "Completed" status as per Woocommerce documentation).', 'gtm-server-side' ), 'https://woocommerce.com/document/managing-orders/order-statuses/' ); // phpcs:ignore
},
GTM_SERVER_SIDE_ADMIN_SLUG,
GTM_SERVER_SIDE_ADMIN_GROUP_WEBHOOKS
diff --git a/includes/class-gtm-server-side-wc-helpers.php b/includes/class-gtm-server-side-wc-helpers.php
index 34a5b67..44ece2f 100644
--- a/includes/class-gtm-server-side-wc-helpers.php
+++ b/includes/class-gtm-server-side-wc-helpers.php
@@ -96,6 +96,10 @@ public function get_order_data_layer_items( $items ) {
foreach ( $items as $item_loop ) {
$product = $item_loop->get_product();
+ if ( ! ( $product instanceof WC_Product ) ) {
+ continue;
+ }
+
$array = $this->get_data_layer_item( $product );
$array['quantity'] = intval( $item_loop->get_quantity() );
$array['index'] = $index++;
@@ -118,6 +122,10 @@ public function get_cart_data_layer_items( $cart ) {
foreach ( $cart as $product_loop ) {
$product = $product_loop['data'];
+ if ( ! ( $product instanceof WC_Product ) ) {
+ continue;
+ }
+
$array = $this->get_data_layer_item( $product );
$array['quantity'] = intval( $product_loop['quantity'] );
$array['index'] = $index++;
diff --git a/includes/class-gtm-server-side-webhook-completed.php b/includes/class-gtm-server-side-webhook-completed.php
new file mode 100644
index 0000000..080444e
--- /dev/null
+++ b/includes/class-gtm-server-side-webhook-completed.php
@@ -0,0 +1,87 @@
+ 'order_completed',
+ 'cart_hash' => $order->get_cart_hash(),
+ 'ecommerce' => array(
+ 'transaction_id' => esc_attr( $order->get_order_number() ),
+ 'affiliation' => '',
+ 'value' => GTM_Server_Side_WC_Helpers::instance()->formatted_price( $order->get_total() ),
+ 'tax' => GTM_Server_Side_WC_Helpers::instance()->formatted_price( $order->get_total_tax() ),
+ 'shipping' => GTM_Server_Side_WC_Helpers::instance()->formatted_price( $order->get_shipping_total() ),
+ 'currency' => esc_attr( $order->get_currency() ),
+ 'coupon' => esc_attr( join( ',', $order->get_coupon_codes() ) ),
+ 'items' => GTM_Server_Side_WC_Helpers::instance()->get_order_data_layer_items( $order->get_items() ),
+ ),
+ 'user_data' => GTM_Server_Side_WC_Helpers::instance()->get_order_user_data( $order ),
+ );
+
+ $request_cookies = GTM_Server_Side_Helpers::get_request_cookies();
+
+ if ( ! empty( $request_cookies ) ) {
+ $request['cookies'] = $request_cookies;
+
+ if ( isset( $request_cookies['_dcid'] ) ) {
+ $request['client_id'] = $request_cookies['_dcid'];
+ }
+ }
+
+ /**
+ * Allows modification of processing order webhook payload.
+ *
+ * @param array $request Webhook payload data.
+ * @param object $order WC_Order instance.
+ */
+ $request = apply_filters( 'gtm_server_side_processing_webhook_payload', $request, $order );
+
+ GTM_Server_Side_Helpers::send_webhook_request( $request );
+ }
+}
diff --git a/includes/class-gtm-server-side-webhook-processing.php b/includes/class-gtm-server-side-webhook-processing.php
index 907d3f9..a64f4b4 100644
--- a/includes/class-gtm-server-side-webhook-processing.php
+++ b/includes/class-gtm-server-side-webhook-processing.php
@@ -29,7 +29,7 @@ public function init() {
}
/**
- * Order change status to processing (Order paid).
+ * Order change status to processing.
*
* @param int $order_id Order id.
* @return void
@@ -50,6 +50,7 @@ public function woocommerce_order_status_processing( $order_id ) {
$request = array(
'event' => 'order_paid',
+ 'cart_hash' => $order->get_cart_hash(),
'ecommerce' => array(
'transaction_id' => esc_attr( $order->get_order_number() ),
'affiliation' => '',
diff --git a/includes/class-gtm-server-side-webhook-purchase.php b/includes/class-gtm-server-side-webhook-purchase.php
index 2b001d6..23d3af3 100644
--- a/includes/class-gtm-server-side-webhook-purchase.php
+++ b/includes/class-gtm-server-side-webhook-purchase.php
@@ -50,6 +50,7 @@ public function woocommerce_new_order( $order_id, $order ) {
$request = array(
'event' => 'purchase',
+ 'cart_hash' => $order->get_cart_hash(),
'ecommerce' => array(
'transaction_id' => esc_attr( $order->get_order_number() ),
'affiliation' => '',
diff --git a/includes/class-gtm-server-side-webhook-refund.php b/includes/class-gtm-server-side-webhook-refund.php
index 8bd700e..7af9915 100644
--- a/includes/class-gtm-server-side-webhook-refund.php
+++ b/includes/class-gtm-server-side-webhook-refund.php
@@ -51,8 +51,9 @@ public function woocommerce_order_refunded( $order_id, $refund_id ) {
$request = array(
'event' => 'refund',
+ 'cart_hash' => $order->get_cart_hash(),
'ecommerce' => array(
- 'transaction_id' => $refund_id,
+ 'transaction_id' => esc_attr( $order->get_order_number() ),
'value' => GTM_Server_Side_WC_Helpers::instance()->formatted_price( $order->get_total() ),
'currency' => esc_attr( $order->get_currency() ),
'items' => GTM_Server_Side_WC_Helpers::instance()->get_order_data_layer_items( $order->get_items() ),