Summary
We have a Magento 2 store that we are having trouble setting up Google Analytics to track eccommerce correctly.
We have followed the Magento docs for setting up GA & GTM, but it is not collecting the correct amount of Sessions with Add to Basket in the Conversions > Ecommerce > Shopping Behavior.
From the image above you can see that it is not tracking the sessions correctly. 27 Sessions with Checkout but only 1 Session with Add to Basket, this does not add up, a user must add to cart before getting to the checkout.
Also, when I check the no of orders on Magento I see there are actually 29 orders, 27 of which are from different users. So it seems it's not capturing the number of orders correctly too.
Debugging/Setup
We followed the Magento docs for setting up GA & GTM as I show below. If you click on an image below you should see a better quality one.
We have E-commerce setup in GA:
At first I discovered with the GTM preview mode that the addToCart tag which was set up with the recommended Magento configuration was not firing. It seems the data layer event was not firing for the GTM trigger.
So I set up a new trigger which fired on Click and added it to the :
I can now see the tag firing in GTM preview mode:
And I can see the Add to Cart event in GAs Real-time Event report.
But still it's not showing the correct data in the Sessions with Add to Basket in the Conversions > Ecommerce > Shopping Behavior.
Questions
What could I be missing?
Should I set Non-Interaction Hit to false in the GTM tag setting?
Should I set use data layer to false for the tag in GTM, as the
addToCart custom event is not firing? Or maybe this is still needed
for something.
Any tips on how I can debug why the addToCart custom event is nor
firing on Magento while GTM is in preview mode?
I noted that the session in Magento is 3.5 hours, while in GA the Session timeout is only 30min. Perhaps this could be it? we changed the GA session timeout to match Magento's, and this was not it :(
Thank you in advance, any help appreciated :)
Based on the comment from #lossleader to my question, I was able to identify and fix the issue. I'll answer it here in case anyone else finds it helpful.
Basically as #lossleader said in his comment the main thing is:
The custom event should also have contained the e-commerce data for the use dataLayer tag.
So triggering the addToCart tag with a simple click event is not enough as it does not contain the e-commerce data, I needed to fix how the enhance ecommerce addToCart event was triggered on Magento.
I discovered that Magento Commerce changed/fixed how they end up calling the function which trigger this event in recent upgrades, i.e. commit MAGETWO-69210 & commit MAGETWO-87437
Our Magento did receive these changes as the list.phtml template and the catalog-add-to-cart.js file were being overridden in it's theme.
Updating the theme files as the below diff shows resolved the issue in our case:
diff --git a/app/code/Namespace/CategoryPages/view/frontend/templates/product/list.phtml b/app/code/Namespace/CategoryPages/view/frontend/templates/product/list.phtml
index 6771e863..2ab8905c 100644
--- a/app/design/frontend/Namespace/theme/Magento_Catalog/templates/product/list.phtml
+++ b/app/design/frontend/Namespace/theme/Magento_Catalog/templates/product/list.phtml
## -91,7 +91,7 ## $_helper = $this->helper('Magento\Catalog\Helper\Output');
<div class="actions-primary"<?php echo strpos($pos, $viewMode . '-primary') ? $position : ''; ?>>
<?php if ($_product->isSaleable()): ?>
<?php $postParams = $block->getAddToCartPostParams($_product); ?>
- <form data-role="tocart-form" action="<?php /* #escapeNotVerified */ echo $postParams['action']; ?>" method="post">
+ <form data-role="tocart-form" data-product-sku="<?= $block->escapeHtml($_product->getSku()) ?>" action="<?= /* #NoEscape */ $postParams['action'] ?>" method="post">
<input type="hidden" name="product" value="<?php /* #escapeNotVerified */ echo $postParams['data']['product']; ?>">
<input type="hidden" name="<?php /* #escapeNotVerified */ echo Action::PARAM_NAME_URL_ENCODED; ?>" value="<?php /* #escapeNotVerified */ echo $postParams['data'][Action::PARAM_NAME_URL_ENCODED]; ?>">
<?php echo $block->getBlockHtml('formkey')?>
diff --git a/app/design/frontend/Namespace/theme/Magento_Catalog/web/js/catalog-add-to-cart.js b/app/design/frontend/Namespace/theme/Magento_Catalog/web/js/catalog-add-to-cart.js
index fae6f0fa..34978ec4 100644
--- a/app/design/frontend/Namespace/theme/Magento_Catalog/web/js/catalog-add-to-cart.js
+++ b/app/design/frontend/Namespace/theme/Magento_Catalog/web/js/catalog-add-to-cart.js
## -6,8 +6,10 ##
define([
'jquery',
'mage/translate',
+ 'underscore',
+ 'Magento_Catalog/js/product/view/product-ids-resolver',
'jquery/ui'
-], function ($, $t) {
+], function ($, $t, _, idsResolver) {
'use strict';
$.widget('mage.catalogAddToCart', {
## -75,7 +77,9 ## define([
* #param {String} form
*/
ajaxSubmit: function (form) {
- var self = this;
+ var self = this,
+ productIds = idsResolver(form),
+ formData = new FormData(form[0]);
$(self.options.minicartSelector).trigger('contentLoading');
self.disableAddToCartButton(form);
## -97,6 +101,13 ## define([
success: function (res) {
var eventData, parameters;
+ $(document).trigger('ajax:addToCart', {
+ 'sku': form.data().productSku,
+ 'productIds': productIds,
+ 'form': form,
+ 'response': res
+ });
+
if (self.isLoaderEnabled()) {
$('body').trigger(self.options.processStop);
}
The custom event that was pushed should also have contained the e-commerce data referred to in the "use dataLayer" tag, so you really needed to fix the magento configuration or detection of the user action rather than emulate it. I.e. look for customizations to the store's html around these inputs/forms and try reverting them to stock.
It looks like you found the complete answer by looking into updates to magento for the addToCart event that were being overridden by the installed theme.
Related
I'm working on a WordPress site and in order to have a custom tracking, I've tried a bunch of things but always encounter issues no matter what.
So it's an eShop (WooCommerce) and to start my tracking, I just try as a first step to add the Product Name as a custom parameter in my custom events.
Firstly, I tried to create a tag in GTM which uses a custom variable that gets its value from a data layer pushed in a JS file enqueued in my child theme.
Here's how I enqueued and populated the script, at the end of a function I called "tracking_process" :
wp_register_script('data_layer', get_stylesheet_directory_uri() . '/scripts/tracking.js', array('jquery'), null, false );
// "data" contains my product name
wp_add_inline_script('data_layer', 'var data = ' . wp_json_encode($data), 'after');
And here's the action :
add_action('wp_footer', 'tracking_process');
The script correctly loads on the page.
The product name is correctly sent to the script.
The data layer is correctly pushed as I can see in the Tag Assistant my product name in the data layer tab.
The issue is...
My data layer seems to be pushed after the tag is sent to GA4, resulting in my custom parameter always being empty in the custom event, in the Debug View.
add_action('wp_enqueue_scripts', 'tracking_process', 0);
I also tried something I found on another thread, to put the script at the beginning of the queue :
(I execute this line after "wp_register_script")
array_unshift(wp_scripts()->queue, 'data_layer');
All of this without any success, my data layer is always pushed after my tag is triggered.
So I tried another approach...
I tried to send my custom event directly from the JS file.
jQuery(document).ready( function() {
gtag('event', 'test_product', {
'product_name' : data.product_name,
});
});
Half of the time, the custom event doesn't show up in the Debug View, without any apparent reason.
When it does, it seems the "page_view" automatic event doesn't trigger.
And when I don't see the custom event, "page_view" shows up instead.
Note that in Tag Assistant, I always correctly see my custom event, so I really don't get why it doesn't in the Debug View.
The issue persists even after 48h (I've seen there can be some delay).
And that's a staging site, there's no traffic on it. The live site is brand new and has low traffic as well at the moment.
The complete PHP function :
function tracking_process(){
global $product;
if( is_product() ) {
if ( ! is_a( $product, 'WC_Product' ) ) {
$product = wc_get_product( get_the_id() );
}
$product_name = $product->get_name();
$data['product_name'] = $product_name;
}
wp_register_script('data_layer', get_stylesheet_directory_uri() . '/scripts/tracking.js', array('jquery'), null, false );
array_unshift(wp_scripts()->queue, 'data_layer');
wp_add_inline_script('data_layer', 'var data = ' . wp_json_encode($data), 'after');
};
add_action('wp_enqueue_scripts', 'tracking_process', 0);
I searched a lot online but found nothing that fixed this issue, and even tried with ChatGPT by giving him precise details but nothing worked so far.
Also note that I already created the custom dimensions in GA4 so it recognizes my custom parameters, and that I don't get any error in the console.
Anyone having an idea of how I can fix this issue ?
Thanks in advance !
Auto-updating the cart on the site using this action works well:
add_action( 'wp_footer', 'cart_update_qty_script' );
function cart_update_qty_script() {
if (is_cart()) :
?>
<script>
jQuery('div.woocommerce').on('change', '.qty', function(){
jQuery("[name='update_cart']").removeAttr("disabled").trigger("click");
});
</script>
<?php
endif;
}
The cart and the ordering page have been united into one (just "cart")
When switching from the "cart" page to the "checkout/order-received" page, the mini-cart does not become zero. Only updating manually makes the mini-basket null. (Basically, you have already finished with the order, getting thanked for your order, yet the miniature is still not refreshed.)
Is there any way to fix it? I use WooCommerce 6.5.1. Payment is disabled on the site.
I'm trying to implement the checkout fuctionality (+ stripe with WooCommerce Stripe Payment Gateway plugin) in a modal window, and I'm using the ajax for this. Here is my backend code to get/refresh the checkout:
add_action('wp_ajax_refresh_checkout', 'getCheckoutPageContentCallBack');
add_action('wp_ajax_nopriv_refresh_checkout', 'getCheckoutPageContentCallBack');
function getCheckoutPageContentCallBack() {
define('WOOCOMMERCE_CHECKOUT', true);
echo do_shortcode('[woocommerce_checkout]');
if (class_exists('WooCommerce')) {
$wcurl = WooCommerce::plugin_url();
$credit_card_form_script = file_get_contents($wcurl . '/assets/js/frontend/credit-card-form.min.js');
if ($credit_card_form_script) {
echo "<script>";
echo $credit_card_form_script;
echo "</script>";
}
$checkout_script = file_get_contents($wcurl . '/assets/js/frontend/checkout.min.js');
if ($checkout_script) {
echo "<script>";
echo $checkout_script;
echo "</script>";
}
}
wp_die();
}
I included all the scripts that checkout page has, but still getting the error:
Please enter your card details to make a payment. Developers: Please make sure that you are including jQuery and there are no JavaScript errors on the page.
jQuery is included and there are no js errors on the page. The original checkout page works just fine. I thought the problem was that I removed the payment part from the original part by this code:
remove_action('woocommerce_checkout_order_review', 'woocommerce_checkout_payment', 20);
add_action('woocommerce_checkout_order_payment', 'woocommerce_checkout_payment', 20);
But commenting these lines gave me nothing. I edited the page, so this is not even a shop page right now (just a page with a loop).
I noticed that when making payments via the checkout page, the first request goes to the https://api.stripe.com/v1/tokens with card credentials, the next one goes to /checkout/?wc-ajax=checkout with stripe_token and so When I try to checkout from my modal, there is no request to the Stripe API, just to the checkout. Maybe there is some script I need to include every time I refresh the checkout? I can't find any info about it.
In my wordpress website, i am using a gravity form to complete one online application process with about 10 to 12 pages of form filling. Once a user fills out all the process and make it to payment method page (which is the last before page), he sees display of details about payment method he has chosen in previous page.
In the previous page, i had 3 options to choose like credit card, check, and money order. Now selecting credit card, works fine and displays its details in the next page. But for the other two - check and money order, anything among these are selected, the next page displays a black details area. I need to display of details according to the selection.
This is mainly because of the Jquery issue in displaying content.
In this page, there seems to be code like the below to display the contents and to stop them from displaying.
in Plugins/gravity_forms/form_display.php
<div id='gform_page_{$form["id"]}_{$field["pageNumber"]}' class='gform_page{$custom_class}' {$style}>
<div class='gform_page_fields'>
<ul class='gform_fields {$form['labelPlacement']}'>";
and li like
$style = !empty($form) && !IS_ADMIN && RGFormsModel::is_field_hidden($form, $field, $field_values) ? "style='display:none;'" : "";
$field_id = IS_ADMIN || empty($form) ? "field_$id" : "field_" . $form["id"] . "_$id";
return "<li id='$field_id' class='$css_class' $style>" . self::get_field_content($field, $value, $force_frontend_label, $form == null ? 0 : $form["id"]) . "</li>";
}
when form current page came then it show that fields other fields are display :none
I need to know how these process of display:block and none works accordingly in this situation. Which file is causing these all actions?
Here
i faced one more issue
for few id it does not show fields
so then for this i coded Themes/function-application.php
add_filter('gform_pre_render_'.$form_id, 'populate_checkbox',10);
function populate_checkbox($form)
{
in this ....
my code
if($current_page==12){
require_once 'includes/application_form/payment.php';
?>
<script type="text/javascript">
alert('inside');
jQuery("#field_1_212").css("display","block");
jQuery("#field_1_129").css("display","block");
alert('after');
</script>
<?php
}
?>
here i find only first alert inside worked fine .. and then jquery, alert after are all not worked..
so i need to know which file i need to modify ??
Thanks in advance..
I have a client who has signed up for a sagepay account. His current website runs off wordpress 3.0 and currently doesn't have any sort of ecommerce functionality.
He's needing a button that lets users submit a deposit of £300 through sagepay (this amount never changes).
(Usually, I would suggest using paypal for something like this, but apparently due to the travel nature of his business, paypal won't let my client have a pro account)
I've looked at the method described in a similar thread on here back in March (How Do I Make a SagePay BuyNow Button?), but I'm not really sure how to implement this within a page on wordpress, not hugely knowledgeable on php bar basic template editing, so I got totally lost at the $PAYMENT_CRYPT part.
If anyone could provide the steps I need to take to implement a basic button that submits the same amount each time, and then collects all the card details/customer details once it's sent them to sagepay gateway, it would be hugely appreciated!
In short, no. These is no easy way to approach this. Unless you link to a Payment form to SagePay and use the new IFRAME feature. You can have certain information within WordPress that allows PHP code on your template pages or your template files.
1 - IFRAME the form within your PHP server and code the form on its own that way the CSS will become like the CSS on the WordPress page
2 - Create a payment module for it
3 - Use an existing Payment eCommerce server module for WordPress - there are plenty of plugins already
4 - Create a payment button hyper link, once clicked, it goes to a PHP form on your server for the £300 amount..
5 - Use Nochex or another payment vendor, Google Wallet etc (this is not an easy option for the client)
With the FORM, you could have:
<?
# Define your vars
$serverLive="https://live.sagepay.com/gateway/service/vspform-register.vsp"
//$serverLive="https://test.sagepay.com/gateway/service/vspform-register.vsp"
$YOUR_VENDOR_LOGIN_NAME="";
$VendorTxCode="406227821909";
$Amount="350.00";
$Currency="GBP";
$Description="1 ACME Widget";
$SuccessURL="http://example.com/success.php";
$FailureURL="http://example.com/fail.php";
$BillingSurname="Smith";
$BillingFirstnames="John";
$BillingAddress1="123 Main Street";
$BillingCity="Anywhere";
$BillingPostCode="29555";
$BillingCountry="USA";
$DeliverySurname="Smith";
$DeliveryFirstnames="John";
$DeliverAddress1="123 Main Street";
$DeliveryCity="Anywhere";
$DeliveryPostCode="29555";
$DeliveryCountry="GBP";
# The address information can be done via jQuery on your page or get some defaults
?>
<form action="<?=$serverLive?>" method="POST" id="SagePayForm" name="SagePayForm">
<input type="hidden" name="VPSProtocol" value="2.23" />
<input type="hidden" name="TxType" value="PAYMENT" />
<input type="hidden" name="Vendor" value="<?= $YOUR_VENDOR_LOGIN_NAME ?>" />
<input type="hidden" name="Crypt" value="<?= $PAYMENT_CRYPT ?>">
<input type="image" src="images/buynow-sagepay.png" />
</form>
<script type="text/javascript">
function submitform()
{
document.SagePayForm.submit();
}
submitform();
</script>
Even with this code you would still need to use some SagePay libraries, such as the XOR and Crypt functions:
// Crypt and XOR functions
private function simpleXor($string, $password) {
$data=array();
for ($i=0; $i < utf8_strlen($password); $i++) {
$data[$i]=ord(substr($password, $i, 1));
}
$output='';
for ($i=0; $i < utf8_strlen($string); $i++) {
$output .= chr(ord(substr($string, $i, 1)) ^ ($data[$i % utf8_strlen($password)]));
}
return $output;
}