How to push Ecommerce Transactions to GTM Data Layer - google-analytics

I'm pushing some tags to the data layer and they all seem to be working as expected except for ecommerce transaction data. I have confirmed that the transaction data is being pushed to the data layer but for some reason I'm not seeing any results in Google Analytics—even after waiting 48 hours.
I should mention that this is an SPA so there is no page refreshing. The data is pushed to the data layer after the payment is successfully processed.
This is the data I'm sending:
{
event: 'subscribe',
user: <USER_ID>,
subscribe: {
transactionId: <CUSTOMER>,
transactionAffiliation: <AFFILIATION>,
transactionTotal: <PRICE>,
transactionProducts: [
{
id: <CUSTOMER>,
sku: <SUBSCRIPTION>,
name: <PLAN>,
price: <PRICEPER>,
quantity: <QUANTITY>
}
]
}
};
Is there something wrong with the way I'm formatting this? Any ideas why this doesn't seem to be working?

For just regular NOT enchanced ecomm, you need to make some adjustments to your datalayer first:
{
event: 'subscribe',
user: <USER_ID>,
transactionId: <CUSTOMER>,
transactionAffiliation: <AFFILIATION>,
transactionTotal: <PRICE>,
transactionProducts: [{
id: <CUSTOMER>,
sku: <SUBSCRIPTION>,
name: <PLAN>,
price: <PRICEPER>,
quantity: <QUANTITY>
}]
};
Then create a custom event trigger referencing the subscribe event:
Then create a GA tag and select Track Type of Transaction, elect your GA settings variable and set the trigger to that custom event trigger we created above:

Related

NextJs Stripe Subscription with subscription end date

I have generated some products in stripe configured as recurring payment and i can subscribe to them without problem.
I would like to know if it is possible from the same call that is made to generate the subscription it is possible to indicate the end date of this subscription.
Researching the documentation I see that they talk about "cancel_at" but I can't find where to indicate this parameter in the call. I haven't been able to locate any examples either.
const session = await stripe.checkout.sessions.create({
payment_method_types: ["card"],
line_items: [
{
price: "price_1LaMHQH4AS5eXWlNhznAR7JA",
quantity: 1
},
],
mode: 'subscription',
success_url:`${req.headers.origin}/public/payment?success=true`,
cancel_url: `${req.headers.origin}/public/payment?cancelled=true`
});
You cannot provide an end date for a Subscription in a Checkout Session.
What you can do instead is to listen for the checkout.session.completed webhook event. This event will contain the subscription id and upon receipt of the event, you can make a request to update that Subscription's cancel_at.

Custom metric/dimension not visible in GA4 DebugView on item/product-list level

We're trying to configure GA4 to gather same data that we have in UA.
The problem is that we cant see custom metrics data on item-level in DebugView.
In TagAssistant we can see that data is sent:
{
event: "purchase",
gtm: {uniqueEventId: 10, start: 1651XXX},
gtag: {
targets: {
G-XXX: {_ee: true, _uei: 3, _upi: 2},
UA-XXX: {
currency: "PLN",
country: "PL",
custom_map: {
dimension2: "referrer",
...
dimension13: "ecomm_totalvalue",
metric1: "metric1",
...
metric5: "metric5"
},
_uei: 4
},
AW-106XXX: {}
}
},
eventModel: {
transaction_id: "40XXX",
affiliation: "XXX",
value: "9.99",
currency: "PLN",
tax: "1.87",
shipping: "0",
coupon: "",
items: [
{
id: "670",
name: "Tusz Zamiennik T0711 do Epson (C13T07114010) (Czarny)",
brand: "XXX",
category: "Wkłady/Atramentowe/Zamienniki/Tusze/XXX -> Do:E" +
"pson",
quantity: 1,
price: "9.99",
coupon: "",
metric1: "9.99",
metric2: "9.99",
metric3: "9.99",
metric4: "9.99",
metric5: "9.99"
}
],
send_to: ""
}
}
However, on DebugView side we can see purchase event that:
contains all orginal parameters on event level (like transaction_id, shipping etc.)
contains all orginal parameters on item level (like item_id, quantity, etc.)
But our custom metrics are missing
In the other hand, we tried (with different event) push some custom-dumension data and:
they are visible on GA4 DebugView
we can create custom dimension in GA4 basing on this data and it works
I am thinking about:
Creating event-level custom metric to test if it will be passed to GA4 DebugView
Creating item-level custom dimension to test if it will be passed to GA4 DebugView
However, due to fact that GA requires 24h to make conclusions i decided to ask here for any sugestions why it is not working and what else may i test.

Can I use "dataLayer" code to capture "purchase" events from backend of website when batch processing orders / membership fees?

Is it possible to use dataLayer code on my website to capture monthly membership plan fees? My website processes this automatically and charges my customers credit card, so would I be able to have my developer add this code so when each customer's credit card is charged this dataLayer will execute?
<script>
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: "purchase",
ecommerce: {
transaction_id: "{invoice_id}",
value: {billable_amount},
currency: "USD",
items: [
{
item_id: "{invoice_id}",
item_name: "Membership Plan Fee",
price: {billable_amount}
}]
}
});
</script>
Would this also work if I batch process orders on the backend of my website, where a similar dataLayer would fire for each order that is processed?
Would I need to add GTM tracking code to the admin/backend of my website for this to work?
Thank you!
I ended up finding what I need to use to accomplish this; I guess Google calls it Measurement Protocols for sending events server-to-server.
Measurement Protocols - Sending Events Server to Server

Adding more fields to Meteor user accounts

I am using mrt add accounts-ui-bootstrap-dropdown and mrt add accounts-password to get a simple login page running on my app.
The accounts users gives me a nice hash containing ids, createdAt, emails, etc.
If I wanted to add other fields in this hash so I can make use of them later, how would I do that? For example, I want then to also enter their given name and surname:
"given_name": "John", "surname": "Doe"
Users are special objects in meteor ; you don't want to add fields in the user but in the users profile.
From the doc :
By default the server publishes username, emails, and profile.
If you want to add properties like surname when you create the account, you should use in the Account.onCreateUser server-side hook : http://docs.meteor.com/#accounts_oncreateuser
Accounts.onCreateUser(function(options, user) {
//pass the surname in the options
user.profile['surname'] = options.surname
return user
}
If you want to update a user after, you can do it from the client that way :
Meteor.users.update({_id:Meteor.user()._id}, { $set: {what you want to update} });
By default, the users base will allow that (the current user may update itself). If you don't trust your users and want to ensure that everything is properly update, you can also forbid any updates from the client and make them via a Meteor.call() and proceed to the checkings server-side. But this would be sad.
Edit :
As said in the comments, adding options via the standard account-ui won't be possible. You'll only be able to update the user after the registration. To add options when you subscribe, you'll have to make you own form.
I won't insult you by writing html markup, but here is what you want to have after the submit event (and after the various checking) :
var options = {
username: $('input#username')[0].value,
emails: [{
address: $('input#email')[0].value,
verified: false
}],
password: $('input#password')[0].value,
profile: {
surname: $('input#surname')
},
};
Accounts.createUser( options , function(err){
if( err ) $('div#errors').html( err.message );
});
You only need the account-base package ; not the account-ui.
Login with the social networks is cake :
Meteor.loginWithFacebook({
requestPermissions: ['email', 'user_birthday', 'user_location']
}, function(error){loginCallBack(error);});
About the answer ram1 made :
This is not the way meteor works. You do not "POST" a form. You want all your client / server communication done via the websocket. The equivalent of what you are talking about is making a "Meteor.call('myserverfunction', myarguments, mycallback)" of a server method from the client and you pass the arguments you want the server to use.
But this is not the way you will get the best of meteor. There is the philosophy you want to work with :
you have datas in your local mini mongo you got from the server
you update locally those datas in your base / view
meteor do his magic to transmit those updates to the server
there the server can answer : ok, updates saved, this is seamless for you. Or answer : nop ! reverse the changes (and you can implement an error notification system)
(it can answer no because you don't have the permission to update this field, because this update break a rule you did set up...)
All you do is setting permissions and controls on the databases server-side. That way, when an honest client make an update, he sees the result instantly ; way before it has been pushed to the server and send to the other clients. This is latency compensation, one of the seven principles of meteor.
If you modify a data via Meteor.call, you will do that :
send an update to the server
the server checks and update the base
the server send the update to the clients (including you)
your local base updates and your view update => you see your update
=> this is what you had in yesterday app ; meteor allow you to build a today app. Don't apply the old recipes :)
The accepted answer has the HOW right, but the WHERE is outdated information. (Yes, this would be better as a comment on the answer, but I can't do that yet.)
From the Meteor 1.2 documentation:
The best way to store your custom data onto the Meteor.users collection is to add a new uniquely-named top-level field on the user document.
And regarding using Meteor.user.profile to store custom information:
🔗Don’t use profile
There’s a tempting existing field called profile that is added by
default when a new user registers. This field was historically
intended to be used as a scratch pad for user-specific data - maybe
their image avatar, name, intro text, etc. Because of this, the
profile field on every user is automatically writeable by that user
from the client. It’s also automatically published to the client for
that particular user.
Basically, it's probably fine to store basic information such as name, address, dob, etc in the profile field, but not a good idea to store anything beyond that as it will, by default, be writeable by the client and vulnerable to malicious users.
I had the same problem and managed to do it only with Accounts.createUser:
Accounts.createUser({
email: email,
password: password,
profile: {
givenName: 'John',
surname: 'Doe',
gender: 'M'
}
}
Thats very simple way and it works. Just add your desired variables in the profile section and it should be ready. Hope it helps someone.
I ended up using https://atmospherejs.com/joshowens/accounts-entry which offers an extraSignUpFields config option.
From the documentation (https://github.com/ianmartorell/meteor-accounts-ui-bootstrap-3/blob/master/README.md):
Custom signup options
You can define additional input fields to appear in the signup form, and you can decide wether to save these values to the profile object of the user document or not. Specify an array of fields using Accounts.ui.config like so:
Accounts.ui.config({
requestPermissions: {},
extraSignupFields: [{
fieldName: 'first-name',
fieldLabel: 'First name',
inputType: 'text',
visible: true,
validate: function(value, errorFunction) {
if (!value) {
errorFunction("Please write your first name");
return false;
} else {
return true;
}
}
}, {
fieldName: 'last-name',
fieldLabel: 'Last name',
inputType: 'text',
visible: true,
}, {
fieldName: 'gender',
showFieldLabel: false, // If true, fieldLabel will be shown before radio group
fieldLabel: 'Gender',
inputType: 'radio',
radioLayout: 'vertical', // It can be 'inline' or 'vertical'
data: [{ // Array of radio options, all properties are required
id: 1, // id suffix of the radio element
label: 'Male', // label for the radio element
value: 'm' // value of the radio element, this will be saved.
}, {
id: 2,
label: 'Female',
value: 'f',
checked: 'checked'
}],
visible: true
}, {
fieldName: 'country',
fieldLabel: 'Country',
inputType: 'select',
showFieldLabel: true,
empty: 'Please select your country of residence',
data: [{
id: 1,
label: 'United States',
value: 'us'
}, {
id: 2,
label: 'Spain',
value: 'es',
}],
visible: true
}, {
fieldName: 'terms',
fieldLabel: 'I accept the terms and conditions',
inputType: 'checkbox',
visible: true,
saveToProfile: false,
validate: function(value, errorFunction) {
if (value) {
return true;
} else {
errorFunction('You must accept the terms and conditions.');
return false;
}
}
}]
});
The official Meteor Guide provides a comprehensive answer with an example code:
The best way to store your custom data onto the Meteor.users collection is to add a new uniquely-named top-level field on the user document.
https://guide.meteor.com/accounts.html#custom-user-data

Fullcalendar with JSON feed returning eventSource in extended form

I'm using the excellent fullcalendar by arshaw with Angular UI and right now I'm having a problem with eventSource objects in extended form not rendering when fetched as JSON feeds.
The data is fetched using the following code in the controller:
$scope.events = [
'/api/v1/users/104/events?format=cal&type=9',
'/api/v1/users/104/events?format=cal&type=10'
];
$scope.eventSources = $scope.events;
When the JSON feed returns an array with event objects it actually works:
// returned by - /api/v1/users/104/events?format=cal&type=9
[
{
url: "/#/events/86893",
start: "2013-03-15",
title: ": Event - 86893"
},
{
url: "/#/events/31348",
start: "2013-03-27T09:30Z",
title: "Event - 31348"
}
],
// returned by - /api/v1/users/104/events?format=cal&type=10
[
{
url: "/#/events/86899",
start: "2013-03-25",
title: ": Event - 86899"
},
{
url: "/#/events/31349",
start: "2013-03-17T09:30Z",
title: "Event - 31349"
}
]
However I would like to specify some options along with the event data, for example different colors for different JSON feeds. Hence I settled for the API to return the event source in its extended form . This is what it API returns.
// returned by - /api/v1/users/104/events?format=cal&type=9
{
events: [
{
url: "/#/events/86893",
start: "2013-03-15",
title: "Event - 86893"
},
{
url: "/#/events/31348",
start: "2013-03-27T09:30Z",
title: "Event - 31348"
}
],
color: "#ff9900",
allDayDefault: false,
ignoreTimezone: true
},
// returned by - /api/v1/users/104/events?format=cal&type=10
{
events: [
{
url: "/#/events/86899",
start: "2013-03-25",
title: "Event - 86899"
},
{
url: "/#/events/31349",
start: "2013-03-17T09:30Z",
title: "Event - 31349"
}
],
color: "#3366FF",
allDayDefault: false,
ignoreTimezone: true
}
Unfortunately this format isn't rendered when fetched as JSON. When fetching the extended format I changed the eventSources assignment somewhat to look like this:
$scope.eventSources = [ $scope.events ];
If I cut and paste the raw JSON response (with the event source in its extended form) from the API straight into the eventSources method it works. Isn't it possible to consume the event source in extended form when it is returned as a JSON feed?
Could it be that the eventSource being returned from the API is not where the extended form options should be used, but rather in the event functions used as the eventSource's.
The documentation states that the extended form should be used inside of the Json object used to call the API.
http://arshaw.com/fullcalendar/docs/event_data/events_json_feed/
If the API was too return the extra options for the events, then it would need to set each one individually.
Currently in this post, The only place I see where the extended options are called is from the returned api data.
I think this should be the other way around. And the calendar should know about the extended form before it even requests the data at all. This would mean the server will not need to return any extra options and the calendar will organize the event objects by feed and extended options.
If you could post a plunk, then we could nail this.

Resources