Analytics

You can connect to Tipser analytics by implementing the onAnalyticsEvent event handler on your page:

const tipserElementsConfig = {
  eventsHandlers: {
    onAnalyticsEvent: (e) => {
       const {detail} = e;
       //Logging out some useful stuff
       console.log('Tipser analytics event: ', e);
       console.log('Action',detail.action);
       console.log('Target',detail.target);
       console.log('Description',detail.description);
       console.log('Object',detail.object);
       //Your custom mapping function goes here
       const internalEvent = transformTipserEventToInternalEvent(e);
       //Pushing analytics event to the analytics system of your choice (GA, FB Pixel, in-house analytics)
       fireInternalAnalyticsEvent(internalEvent);
    }
  }
  ...
}
...
<TipserElementsProvider config={tipserElementsConfig}>
...
</TipserElementsProvider>

Example events intercepted this way are:

  • the user viewed a product on your page
  • a product was added to cart
  • the checkout view was presented to the user
  • a transaction was completed

For complete list of supported events refer to the Supported events section.

What you do with those events is up to you. Typical usage examples are:

  • forwarding them to your analytics system (e.g. Google Analytics, as described here)
  • logging them to the browser console to debug your integration with Tipser Elements

Typically, some conditional logic may be needed to filter out specific types of events. E.g. to only push PayPal transactions events to your analytics, you can implement the following check:

onAnalyticsEvent: (e) => {
       const {detail} = e;
       //Filtering out PayPal transactions
       if(detail.action === 'Purchase' && detail.target === 'Order' && detail.object[0].paymentMethod === 
'PayPal') {
            const {Products, OrderId} = detail.object[0]
            const internalEvent = createInternalPayPalTransactionEvent(Products, OrderId);
            fireInternalPayPalPaymentEvent(internalEvent);
       }       
}

Events structure

Each event passed to tipserEventsCallback function follows the following structure:

{
  detail: {
    action: string, # what action is taken on the target, mandatory
    target: string, # where this action is taken, mandatory
    description: string, # describing what is being logged
    object: # details about the tracked object, e.g. product name, product price, etc.
    [    
      {  
        # some properties here               
      },     
      ..., # this array can contain more than 1 object
    ]
  } 
}

As you can see, all the useful data is contained in the top-level detail field of the event object.

Typical use case: Google Analytics

In case you want to forward Tipser Analytics events to your Google Analytics, you can use this code snippet:

document.addEventListener('tw-track', function(e) {
    // ga() function coming from analytics.js library
    ga('send', {
        hitType: 'event',
        eventCategory: e.detail.description,
        eventAction: e.detail.action,
        eventLabel: e.detail.target,
    });
});

The code above assumes that you use analytics.js GA client library on your page. For other libraries, like gtag.js, that code needs to be slightly adjusted.

For the instructions how to setup analytics.js script on your site and connect it to your GA account, refer to the official documentation.

The above example is just a very trivial example of integrating Tipser events with GA. For a more thorough implementation involving Enhanced Ecommerce extension, please refer to this gist.

For example, if you're using analytics.js script to integrate with Google Analytics, you need to add the line below (as described here) at the end of the snippet copied from your GA account:

ga('require', 'ec');

and make sure that Enhanced Ecommerce extension is enabled in you GA settings:

27802780

Typical use case: Facebook Pixel

In case you want to forward Tipser Analytics events to your Facebook Pixel account, you can use the following code snippet:

document.addEventListener('tw-track', function(e) {
    // fbq() function coming from Facebook Pixel analytics script
    fbq('trackCustom', e.detail.action, {
        content_name: e.detail.target, 
        content_category: e.detail.description,
        content_type: e.detail.action,
    });
});

The example above doesn't translate Tipser events to standard Pixel events, you will have to make a correlation in the Pixel dashboard or you can write a custom mapping function.

  1. Expand and copy the snippet script.

  2. Paste it above addEventListener for tw-track event.

  3. Use it the same way as in the example below

    Snippet

    ```javascript
    const callPixelEvent = function(e) {
      const action = e.detail.action;
      const target = e.detail.target;
      const object = e.detail.object;
    
      switch (true) {
        case action === 'Click' && target === 'Product tile': {
          const product = object[0];
          fbq('track', 'ViewContent', {
            currency: (product.salesPrice || product.listPrice || product.priceIncVat).currency,
            value: (product.salesPrice || product.listPrice || product.priceIncVat).value,
            content_name: product.name || product.title,
            content_ids: [product.id],
          });
          break;
        }
        case action === 'Cart' && target === 'Product': {
          const product = object[0];
          if ((product.name || product.title) && (product.salesPrice || product.listPrice || product.priceIncVat)) {
            fbq('track', 'AddToCart', {
              currency: (product.salesPrice || product.listPrice || product.priceIncVat).currency,
              value: (product.salesPrice || product.listPrice || product.priceIncVat).value,
              content_name: product.name || product.title,
              content_ids: [product.id],
            });
          } else {
            fbq('track', 'AddToCart', {
              content_ids: [product.id],
            });
          }
          break;
        }
        case action === 'Cart' && target === 'Payment': {
          const products = object;
          fbq('track', 'InitiateCheckout', {
            content_ids: products.map((p) => p.id),
            contents: products.map((p) => p.name).join(', '),
            currency: (products[0].salesPrice || products[0].listPrice || products[0].priceIncVat).currency,
            num_items: products.reduce((totalQuantity, product) => product.quantity + totalQuantity, 0),
            value: products.reduce((totalPrice, product) => product.quantity * (product.salesPrice || product.listPrice).value + totalPrice, 0),
          });
          break;
        }
        case action === 'Purchase' && target === 'Order': {
          const products = object.map((order) => order.Products).flat();
          fbq('track', 'Purchase', {
            value: products.reduce(
              (totalPrice, product) => totalPrice + (product.salesPrice || product.listPrice || product.priceIncVat).value,
              0
            ),
            currency: (products[0].salesPrice || products[0].listPrice || products[0].priceIncVat).currency,
            content_ids: products.map((product) => product.id),
            contents: products,
            content_type: 'product',
          });
          break;
        }
        default:
          break;
      }
    };
    

</p>

</details>

```javascript
document.addEventListener('tw-track', callPixelEvent);

The code above assumes that you use pixel.js facebook client library on your page.

For the instructions how to setup pixel.js script on your site and connect it to your Facebook for Developers account, refer to the official documentation.

Supported events

View events

View cart

The user is presented with the cart phase.

detail object structure

{
  description: 'View Cart'
  action: 'View'
  target: 'Cart'
  object: []
}

View cart-tab

A cart tab component was rendered on the page visited by the user (we don’t really check if it’s in the viewport)

detail object structure

{
  description: ‘View cart-tab',
  action: 'View',
  target: 'Cart-tab',
  object: []
}

View collection

When a collection component (or a part of it) appears in the viewport of the user's browser.

detail object structure

{
 description: ‘Collection viewed’,
 action: 'View',
 target: 'Collection',
 object:  {
   id: string,
   ownerUserId: string,
   created: string, #e.g. DateTime ISO 2019-06-11T08:40:29.377Z
   modified: string, #e.g. DateTime ISO 2019-06-11T08:40:29.377Z
   postComment: null | ?
   product: Product[]     
 }
}

Quick links to object structures: Product

View product details

It is emitted every time product detail page was viewed by the user (as a modal or as an embedded product page). In the current implementation it’s always sent together with the click event (can be changed upon request)

detail object structure

{
  description: 'Product detail page viewed',
  action: 'View',
  target: 'Product',
  object: Product[] # Product which interests us is the first element of an Array 
}

Quick links to object structures: Product

View product tile

A product tile component was in the viewport of the user's browser. If it disappears and reappears in the viewport, this generates another instance of the event.

detail object structure

{
  description: 'View product tile'
  action: 'View'
  target: 'Product tile'
  object: Product[]
}

Quick links to object structures: Product

View shop

A store component (or part of it) was in the viewport of the user’s browser.

detail object structure

{
  description: 'Shop viewed',
  action: 'View',
  target: 'Shop',
  object: [
    { 
      id: string, # collection id
      items: Product[], # products
      url_slug: string # store url slug 
    }
  ]
}

When a category is changed in the store, this event will be sent again with a different set of products and collection id.

Click events

Click payment button

User has clicked the payment button.

detail object structure

{
  description: 'Click payment button'
  action: 'Click'
  target: 'Payment button'
  object: [
    {  # Describes how the user is paying and where they clicked the button
        from: string, # Possible values are 
            # `Checkout`, `Cart`
        paymentMethod: string, # Possible values are
            # `APPLE`, `BROWSER`, `GOOGLE`, `KlarnaRest`, `PayPal`, `StripePaymentIntents`
    }
  ]
}

Click product

When a product tile component is clicked by the user (either a standalone product tile or from a collection).

detail object structure

{
  description: 'Product tile clicked',
  action: 'Click',
  target: 'Product tile',
  object: Product[]
}

Quick links to object structures: Product

Cart events

Add product to cart

A product was added to cart or the quantity of the product in the cart was increased (increasing a quantity by X will always generate only one “add to cart” event but with a proper quantity parameter).

detail object structure

{
  description: ‘Product added to cart’,
  action: 'Cart',
  target: 'Product',
  object: ProductLegacy[]
}

Quick links to object structures: ProductLegacy

Remove product from cart

A product was removed from the cart or the quantity of the product in the cart was decreased (decreasing a quantity by X will always generate only one "remove from cart” event but with a proper quantity parameter).

detail object structure

{
  description: ‘Product removed from cart’,
  action: 'Cart',
  target: 'Product',
  object: ProductLegacy[]
}

Quick links to object structures: ProductLegacy

View cart with intent to check out

The user opened the checkout view (every time the user enters this view or refreshes this page will generate another instance of the event)

detail object structure

{
  description: 'View cart - payment in viewport',
  action: 'View',
  target: 'Payment',
  object: OrderedProductLegacy[]
}

Quick links to object structures: OrderedProductLegacy

Purchase events

Product purchased

When a product was bought (thank you page).

detail object structure

{
  description: ‘Product purchased’,
  action: 'Purchase',
  target: 'Order',
  object: [
    {  # Describes the order, the purchased items, and how the user completed their purchase
      OrderId: string, 
      Products: ProductLegacy[]
      from: string, # Possible values are 
            # `Checkout`, `Cart`
      paymentMethod: string, # Possible values are
            # `APPLE`, `BROWSER`, `GOOGLE`, `KlarnaRest`, `PayPal`, `StripePaymentIntents`
    }
  ]
}

Quick links to object structures: ProductLegacy

Data policy events

Consent to data policy

User has accepted the data policy.

detail object structure

{
  description: 'Accepted data policy'
  action: 'Consent'
  target: 'Data policy'
  object: []
}

Reject data policy

User rejected the data policy.

detail object structure

{
  description: 'Rejected data policy'
  action: 'Reject'
  target: 'Data policy'
  object: []
}

Objects structures

Categories structure

  department: string,
  section: string,
  productType: string

Image structure

{
  [key]: string 
  id: string,
  original: string
}

Each value is url for the certain variation of an image. key determines size, and it is one of following: 250x, 450x, 960x, 50x50.

Price structure

{
  value: number,
  currency: string, #ISO 4217
  formatted: string, # human readable price string    
}

Product structure

{
  id: string,
  title: string,
  images: Image[],
  brand: string,
  catgories: Categories
  currency: string, #ISO 4217
  description: string,
  priceInVat: Price,
  isInStock: boolean,
  variants: [],
  vat: { # percentage of VAT
    value: number,
    formatted: string, # human readable value percentage string
  }      
  categoriesValue: string
}

Quick links to object structures: Categories, Price, Image

Legacy object structures

These objects are replaced by newer versions, but may still be encountered in the system.

CategoriesLegacy structure

{
  [key]: string
}[]

OrderedProductLegacy structure

{ # Representation of product, but slightly different schema than Product
  id: string,
  name: string,
  brand: string,
  campaign: undefined,
  categories: CategoriesLegacy 
  merchant: string
  image: string, # url
  listPrice: Price,
  salesPrice: Price,
  variant: [],
  posId: string,
  quantity: number
}

Quick links to object structures: CategoriesLegacy, Price

ProductLegacy structure

{ # Representation of product, but slightly different schema than Product
  id: string,
  name: string,
  brand: string,
  campaign: undefined,
  categories: CategoriesLegacy
  image: string, # url
  listPrice: Price,
  salesPrice: Price,
  variant: [],
  merchant: undefined
}

Quick links to object structures: CategoriesLegacy, Price

Disabling analytics

In case analytics needs to be disabled (e.g. as a result of the user rejecting the GDPR terms), the disableAnalytics: true configuration option can be used. Doing so will prevent the following from happening:

  • sending web requests to Tipser's Google Analytics property
  • sending web requests to Tipser's internal analytics system (stats.tipser.com)
  • placing Google Analytics tracking cookies in the user's browser by Tipser's Google Analytics script

Disabling analytics statically vs dynamically

You can decide to disable analytics from the start when disableAnalytics: true setting is a part of your initial configuration.

Or, instead, you may want to initially keep it enabled (by setting disableAnalytics: false setting in your initial configuration or simply skipping that setting) and only disable it dynamically at the later time by updating the value of disableAnalytics config to true (e.g. at the moment when the user declines the GDPR terms).

To change the analytics settings, just update the value passed to the config prop of TipserElementsProvider in the next render cycle:

const elementsConfigWithAnalytics = {...baseElementsConfig, disableAnalytics: shouldDisableAnalytics()};

return <TipserElementsProvider config={elementsConfigWithAnalytics}>
...
</TipserElementsProvider>

❗️

The configuration setting disableAnalytics: true does not block tw-track events from being emitted. In case you are translating tw-track events to analytics events (e.g. as described in Typical use case: Google Analytics section), then it's your responsibility
to block that sending your own analytics events when necessary.