Skip to content

Commit

Permalink
#91 Google analytics plugin. Reverse an ecommerce transaction. And pr…
Browse files Browse the repository at this point in the history
…ocess only already paid orders (not just placed)
  • Loading branch information
AndreiMaz authored and RomanovM committed Sep 12, 2017
1 parent 15ff6ae commit 4c6445e
Show file tree
Hide file tree
Showing 13 changed files with 578 additions and 159 deletions.
197 changes: 197 additions & 0 deletions src/Plugins/Nop.Plugin.Widgets.GoogleAnalytics/Api/GoogleRequest.cs
@@ -0,0 +1,197 @@
//Contributor: https://www.codeproject.com/Articles/493455/Server-side-Google-Analytics-Transactions

using System;
using System.Net;
using Nop.Core;

namespace Nop.Plugin.Widgets.GoogleAnalytics.Api
{
public class GoogleRequest
{
#region Fields

private const string BASE_URL = "http://www.google-analytics.com/__utm.gif?";

private const string ANALYTICS_VERSION = "5.3.7";
private const string BROWSER_JAVA_ENABLED = "0";

//Required parameters but not necessary for us, so just post a default
private const string SCREEN_RESOLUTION = "1680x1050";
private const string SCREEN_COLOR_DEPTH = "32-bit";
private const string FLASH_VERSION = "11.5%20r31";

//Internal request counter. Max requests = 500 per session
private int _requestCount;
private int? _domainHash;

#endregion

#region Utilities

private void FireRequest(string url)
{
if (_requestCount < 500)
{
_requestCount++;

var gaRequest = WebRequest.Create(url);

try
{
// we don't need the response so this is the end of the request
var response = gaRequest.GetResponse();
}
catch (Exception exc)
{
//eat the error
}

//TODO comment the code above and uncomment the code below when the issue with multiple products is fixed
//gaRequest.BeginGetResponse(r =>
// {
// try
// {
// // we don't need the response so this is the end of the request
// var response = gaRequest.EndGetResponse(r);
// }
// catch
// {
// //eat the error
// }
// }, null);
}
}

private string CreateParameterString()
{
return string.Format("utmwv={0}&utms={1}&utmn={2}&utmhn={3}&utmsr{4}&utmvp={5}&utmsc={6}&utmul={7}&utmje={8}&utmfl={9}&utmhid={10}&utmr={11}&utmp={12}&utmac={13}&utmcc={14}",
ANALYTICS_VERSION,
_requestCount,
CommonHelper.GenerateRandomInteger(),
HostName,
SCREEN_RESOLUTION,
SCREEN_RESOLUTION,
SCREEN_COLOR_DEPTH,
Culture,
BROWSER_JAVA_ENABLED,
FLASH_VERSION,
CommonHelper.GenerateRandomInteger(),
"-",
PageTitle,
AccountCode,
GetUtmcCookieString());
}

private int DomainHash
{
get
{
if (!_domainHash.HasValue)
{
if (HostName != null)
{
int a;
int c;
int h;
char chrCharacter;
int intCharacter;

a = 0;
for (h = HostName.Length - 1; h >= 0; h--)
{
chrCharacter = char.Parse(HostName.Substring(h, 1));
intCharacter = (int)chrCharacter;
a = (a << 6 & 268435455) + intCharacter + (intCharacter << 14);
c = a & 266338304;
a = c != 0 ? a ^ c >> 21 : a;
}

_domainHash = a;
}
_domainHash = 0;
}

return _domainHash.Value;
}
}

private string _UtmcCookieString = null;
//The cookie collection string
private string GetUtmcCookieString()
{
if (_UtmcCookieString == null)
{
//create the unix timestamp
TimeSpan span = (DateTime.Now - new DateTime(1970, 1, 1, 0, 0, 0, 0).ToLocalTime());
int timeStampCurrent = (int)span.TotalSeconds;

//fake the utma
string utma = String.Format("{0}.{1}.{2}.{3}.{4}.{5}",
DomainHash,
CommonHelper.GenerateRandomInteger(),
timeStampCurrent,
timeStampCurrent,
timeStampCurrent,
"2");
string utmz = String.Format("{0}.{1}.{2}.{3}.utmcsr={4}|utmccn={5}|utmcmd={6}",
DomainHash,
timeStampCurrent,
"1",
"1",
"(direct)",
"(direct)",
"(none)");


_UtmcCookieString = Uri.EscapeDataString(String.Format("__utma={0};+__utmz={1};", utma, utmz));
}

return _UtmcCookieString;
}

#endregion

#region Methods

/// <summary>
/// Send the request to the Google servers
/// </summary>
/// <param name="transaction">A corresponding transaction</param>
public void SendRequest(Transaction transaction)
{
string requestUrl = BASE_URL + CreateParameterString() + "&" + transaction.CreateParameterString();
FireRequest(requestUrl);

foreach (var transItem in transaction.Items)
{
FireRequest(BASE_URL + CreateParameterString() + "&" + transItem.CreateParameterString());
}
}

#endregion

#region Properties

/// <summary>
/// Your Google tracking code (e.g. UA-12345678-1)
/// </summary>
public string AccountCode { get; set; }

/// <summary>
/// The language of the customer (e.g. en-US)
/// </summary>
public string Culture { get; set; }

/// <summary>
/// The hostname of the website making the request (e.g. www.google.com)
/// </summary>
public string HostName { get; set; }

/// <summary>
/// The title of the page making the request
/// </summary>
public string PageTitle { get; set; }

#endregion
}
}
21 changes: 21 additions & 0 deletions src/Plugins/Nop.Plugin.Widgets.GoogleAnalytics/Api/Helpers.cs
@@ -0,0 +1,21 @@
//Contributor: https://www.codeproject.com/Articles/493455/Server-side-Google-Analytics-Transactions

using System;

namespace Nop.Plugin.Widgets.GoogleAnalytics.Api
{
public class Helpers
{
/// <summary>
/// Converts a DateTime to a UNIX timestamp
/// </summary>
public static int ConvertToUnixTimestamp(DateTime value)
{
//create Timespan by subtracting the value provided from the Unix Epoch
TimeSpan span = (value - new DateTime(1970, 1, 1, 0, 0, 0, 0).ToLocalTime());

//return the total seconds (which is a UNIX timestamp)
return (int)span.TotalSeconds;
}
}
}
65 changes: 65 additions & 0 deletions src/Plugins/Nop.Plugin.Widgets.GoogleAnalytics/Api/Transaction.cs
@@ -0,0 +1,65 @@
//Contributor: https://www.codeproject.com/Articles/493455/Server-side-Google-Analytics-Transactions

using System;
using System.Collections.Generic;
using System.Globalization;

namespace Nop.Plugin.Widgets.GoogleAnalytics.Api
{
public class Transaction
{
private readonly string _utmt = "tran";

private string _orderId; //(utmtid)
private string _utmtci; //Billing city
private string _utmtco; //Billing country
private string _utmtrg; //Billing region
private string _utmtst; //Store name / affiliation
private string _utmtsp; //Shipping costs
private string _utmtto; //Order total
private string _utmttx; //Tax costs

/// <summary>
/// Create a new E-commerce Transaction
/// </summary>
/// <param name="orderId">Order identifier</param>
/// <param name="billingCity">Billing city</param>
/// <param name="country">Country</param>
/// <param name="region">Region</param>
/// <param name="storeName">Store name</param>
/// <param name="shipping">Shipping</param>
/// <param name="tax">Tax</param>
/// <param name="orderTotal">Order total</param>
public Transaction(string orderId, string billingCity, string country, string region, string storeName, decimal shipping, decimal tax, decimal orderTotal)
{
Items = new List<TransactionItem>();

_orderId = Uri.EscapeDataString(orderId);

var usCulture = new CultureInfo("en-US");
_utmtci = Uri.EscapeDataString(billingCity);
_utmtco = Uri.EscapeDataString(country);
_utmtrg = Uri.EscapeDataString(region);
_utmtst = Uri.EscapeDataString(storeName);
_utmtsp = shipping.ToString("0.00", usCulture);
_utmttx = tax.ToString("0.00", usCulture);
_utmtto = orderTotal.ToString("0.00", usCulture);
}

public string CreateParameterString()
{
return string.Format("utmt={0}&utmtci={1}&utmtco={2}&utmtrg={3}&utmtid={4}&utmtst={5}&utmtsp={6}&utmtto={7}&utmttx={8}",
_utmt,
_utmtci,
_utmtco,
_utmtrg,
_orderId,
_utmtst,
_utmtsp,
_utmtto,
_utmttx);
}

public List<TransactionItem> Items { get; set; }
}
}
@@ -0,0 +1,51 @@
//Contributor: https://www.codeproject.com/Articles/493455/Server-side-Google-Analytics-Transactions

using System;
using System.Globalization;

namespace Nop.Plugin.Widgets.GoogleAnalytics.Api
{
public class TransactionItem
{
private readonly string _utmt = "item";

private string _utmtid; //OrderId
private string _utmipc; //Product code
private string _utmipn; //Product name
private string _utmipr; //Product price (unit price)
private string _utmiqt; //Quantity
private string _utmiva; //Product category

/// <summary>
/// Create a new TransactionItem
/// </summary>
/// <param name="orderId">Order number</param>
/// <param name="productName">Product name</param>
/// <param name="productPrice">Unit price</param>
/// <param name="quantity">Quantity</param>
/// <param name="category">The product category</param>
public TransactionItem(string orderId, string productCode, string productName, decimal productPrice, int quantity, string category)
{
var usCulture = new CultureInfo("en-US");

_utmtid = Uri.EscapeDataString(orderId);
_utmipc = Uri.EscapeDataString(productCode);
_utmipn = Uri.EscapeDataString(productName);
_utmipr = productPrice.ToString("0.00", usCulture);
_utmiqt = quantity.ToString();
_utmiva = Uri.EscapeDataString(category);
}

public string CreateParameterString()
{
return string.Format("utmt={0}&utmtid={1}&utmipc={2}&utmipn={3}&utmipr={4}&utmiqt={5}&utmiva={6}",
_utmt,
_utmtid,
_utmipc,
_utmipn,
_utmipr,
_utmiqt,
_utmiva);
}
}
}
Expand Up @@ -127,54 +127,8 @@ private string GetEcommerceScript(Order order)
var googleAnalyticsSettings = _settingService.LoadSetting<GoogleAnalyticsSettings>(_storeContext.CurrentStore.Id);
var analyticsTrackingScript = googleAnalyticsSettings.TrackingScript + "\n";
analyticsTrackingScript = analyticsTrackingScript.Replace("{GOOGLEID}", googleAnalyticsSettings.GoogleId);

//ensure that ecommerce tracking code is renderred only once (avoid duplicated data in Google Analytics)
if (order != null && !order.GetAttribute<bool>(ORDER_ALREADY_PROCESSED_ATTRIBUTE_NAME))
{
var usCulture = new CultureInfo("en-US");

var analyticsEcommerceScript = googleAnalyticsSettings.EcommerceScript + "\n";
analyticsEcommerceScript = analyticsEcommerceScript.Replace("{GOOGLEID}", googleAnalyticsSettings.GoogleId);
analyticsEcommerceScript = analyticsEcommerceScript.Replace("{ORDERID}", order.Id.ToString());
analyticsEcommerceScript = analyticsEcommerceScript.Replace("{SITE}", _storeContext.CurrentStore.Url.Replace("https://", "").Replace("http://", "").Replace("/", ""));
analyticsEcommerceScript = analyticsEcommerceScript.Replace("{TOTAL}", order.OrderTotal.ToString("0.00", usCulture));
analyticsEcommerceScript = analyticsEcommerceScript.Replace("{TAX}", order.OrderTax.ToString("0.00", usCulture));
var orderShipping = googleAnalyticsSettings.IncludingTax ? order.OrderShippingInclTax : order.OrderShippingExclTax;
analyticsEcommerceScript = analyticsEcommerceScript.Replace("{SHIP}", orderShipping.ToString("0.00", usCulture));
analyticsEcommerceScript = analyticsEcommerceScript.Replace("{CITY}", order.BillingAddress == null ? "" : FixIllegalJavaScriptChars(order.BillingAddress.City));
analyticsEcommerceScript = analyticsEcommerceScript.Replace("{STATEPROVINCE}", order.BillingAddress == null || order.BillingAddress.StateProvince == null ? "" : FixIllegalJavaScriptChars(order.BillingAddress.StateProvince.Name));
analyticsEcommerceScript = analyticsEcommerceScript.Replace("{COUNTRY}", order.BillingAddress == null || order.BillingAddress.Country == null ? "" : FixIllegalJavaScriptChars(order.BillingAddress.Country.Name));

var sb = new StringBuilder();
foreach (var item in order.OrderItems)
{
string analyticsEcommerceDetailScript = googleAnalyticsSettings.EcommerceDetailScript;
//get category
string category = "";
var defaultProductCategory = _categoryService.GetProductCategoriesByProductId(item.ProductId).FirstOrDefault();
if (defaultProductCategory != null)
category = defaultProductCategory.Category.Name;
analyticsEcommerceDetailScript = analyticsEcommerceDetailScript.Replace("{ORDERID}", item.OrderId.ToString());
//The SKU code is a required parameter for every item that is added to the transaction
analyticsEcommerceDetailScript = analyticsEcommerceDetailScript.Replace("{PRODUCTSKU}", FixIllegalJavaScriptChars(item.Product.FormatSku(item.AttributesXml, _productAttributeParser)));
analyticsEcommerceDetailScript = analyticsEcommerceDetailScript.Replace("{PRODUCTNAME}", FixIllegalJavaScriptChars(item.Product.Name));
analyticsEcommerceDetailScript = analyticsEcommerceDetailScript.Replace("{CATEGORYNAME}", FixIllegalJavaScriptChars(category));
var unitPrice = googleAnalyticsSettings.IncludingTax ? item.UnitPriceInclTax : item.UnitPriceExclTax;
analyticsEcommerceDetailScript = analyticsEcommerceDetailScript.Replace("{UNITPRICE}", unitPrice.ToString("0.00", usCulture));
analyticsEcommerceDetailScript = analyticsEcommerceDetailScript.Replace("{QUANTITY}", item.Quantity.ToString());
sb.AppendLine(analyticsEcommerceDetailScript);
}

analyticsEcommerceScript = analyticsEcommerceScript.Replace("{DETAILS}", sb.ToString());

analyticsTrackingScript = analyticsTrackingScript.Replace("{ECOMMERCE}", analyticsEcommerceScript);

_genericAttributeService.SaveAttribute(order, ORDER_ALREADY_PROCESSED_ATTRIBUTE_NAME, true);
}
else
{
analyticsTrackingScript = analyticsTrackingScript.Replace("{ECOMMERCE}", "");
}
//remove {ECOMMERCE} (used in previous versions of the plugin)
analyticsTrackingScript = analyticsTrackingScript.Replace("{ECOMMERCE}", "");

return analyticsTrackingScript;
}
Expand Down

0 comments on commit 4c6445e

Please sign in to comment.