NAV
javascript

Introduction

You can integrate Tipser in your website in multiple ways. The fastest way is to use Tipser Script or Tipser Elements - a complete toolkit of native commerce experience that requires less than 5 minutes of tech setup.

Tipser Script

Tipser Script is a set of shoppable elements built on top of Tipser REST API and Tipser Script. It introduces support for several special attributes in your HTML code, and a runtime HTML scanner that will replace nodes marked with these attributes with the corresponding E-commerce elements.

A few basic examples:

Tipser Script


Quick start

This quick guide explains how to initialize and render Tipser Script on your page. It requires you to have a publisher account created in order to get the posId, as well as have some collections created in your shop here.

If you're all set up, follow these three steps to install Tipser Script on your site!


Installation of Tipser Script

To use Tipser Script on your site, add the following script to your page. This is an entry point to Tipser Script that exposes a global tipser object, that you will use later to initialize Elements and customize its behavior.

<script src="https://cdn.tipser.com/tipser-script/latest.js"></script>

Inserting Store Element

Insert this HTML snippet on your page in the place where you want the Store element to be rendered. Typically, this will be a new blank subpage created in your CMS, as the Store is best displayed in the full page mode.

<div id="tipser_store"></div>

Initializing Tipser Script

You can initialise Tipser Script with the following line of JavaScript code:

tipser.elements("posId");

Make sure that the posId is replaced with the actual id corresponding to your account.

If you need to pass custom settings (see: configuration), you can pass an options object as second argument.

tipser.elements("posId", options);

A complete working example could look like below:

<!DOCTYPE html>
<html>
  <body>
    <div id="tipser_store"></div>
    <script src="https://cdn.tipser.com/tipser-script/latest.js"></script>
    <script>
      tipser.elements("posId", {lang: "en-US", openOldDialog: false});
    </script>
  </body>
</html>

If everything was setup correctly, you should see the Store element populated with all your collections in place of <div id="tipser_store">.

Open this snippet on Code Pen


Configuration

The elements initialization function accepts two arguments.

tipser.elements(posId: string, config?: object)

For example:

window.tipserScript = tipser.elements();

And then at some other place in the code:

const onButtonClicked = () => {
    window.tipserScript.goToProduct(productId);
}

Store tag

The Store is the best way to present a group of collections on a separate page. It is best to place it on a page where sufficient space is available, however, the script automatically blends in and adapts to the given space.

Collections are created using the app.tipser.com (when logged in to a publisher account).

Before you insert Store on your page, make sure there is at least one collection created in your store, otherwise no content will be rendered.

Insert the following HTML snippet in your code in the place where you want the Store element to be rendered.

<div id="tipser_store"></div>

For more fitting mobile experience you can choose between two types of mobile menu. The default one is a dropdown with categories. The optional menu renders inline menu items, and is applied by adding data-tipser-inlineMenu attribute to the HTML snippet:

<div id="tipser_store" data-tipser-inlineMenu></div>

Product tag

In order to insert a Product Element in your content, insert the following code snippet in your content:

<div data-tipser-pid="5ba2334a781baa0001ccdf61"></div>

Elements with attribute data-tipser-pid will be replaced with Product Element. Product ID is taken from the value of the attribute. The code snippet for a certain product can be generated in app.tipser.com. By default, a full inline product component is displayed (with product details and variant selection).


To display Product in a compact view, add the data-tipser-view="compact" attribute to above tag.

<div
  data-tipser-pid="57233dac89862012f8ec1001"
  data-tipser-view="compact"
></div>

To display a list of products (rendered as small product tiles), add data-tipser-pids attribute to the snippet together with the productIds of the products you would like to display.

<div
  data-tipser-pids="5a8ac10d9d2580326ca4cf47,5a9735d99d25801620c3d3fc,5a8af4909d2580132ca4c1f9"
></div>


Modular Product tag

A modular product is a product that can be constructed from lower level components.

Below is an example HTML snippet that renders a modular product with an image, title, price and variant selector and buy button.

<div
  data-tipser-modular-product-id="5bc6e1c7df2ac60001158814"
>
  <div data-tipser-modular-product-image></div>
  <div data-tipser-modular-product-title></div>
  <div data-tipser-modular-product-price></div>
  <div data-tipser-modular-product-variant-selector></div>
  <div data-tipser-modular-product-buy-button></div>
</div>

The complete list of HTML attributes related to modular product:

Product Image

Displays the full-size version of the currently selected product image.

<div
  data-tipser-modular-product-id="5bc6e1c7df2ac60001158814"
>
  <div data-tipser-modular-product-image style=“width:400px,height:500px”></div>
</div>
Attribute Type Description Default
data-tipser-enable-swipe HTML boolean enables swipe functionality (recommended for touch devices) false
data-tipser-enable-arrows HTML boolean show left and right arrows for changing images false
data-tipser-enable-dots HTML boolean show slider bullet dots false
data-tipser-class-name string custom CSS class name to apply none

Product Thumbnails

Displays the product thumbnails.

<div
  data-tipser-modular-product-id="5bc6e1c7df2ac60001158814"
>
  <div data-tipser-modular-product-thumbnails style=“width:400px,height:500px”></div>
</div>
Attribute Type Possible values Description Default
data-tipser-image-fit string 'contain' | 'cover' changes the background-size property 'cover'
data-tipser-direction string 'vertical' | 'horizontal' changes the orientation of the thumbnails container 'horizontal'
data-tipser-class-name string custom CSS class name to apply none

Product page tag

A full-sized product component to be used on a dedicated page.

<div data-tipser-product-page="5bc6e1c7df2ac60001158814"></div>

Element with attribute data-tipser-product-page will be replaced with Product Page Element. Product ID is taken from the value of the attribute. The productId for a certain product can be generated in app.tipser.com.

Example:


Collection tag

Collection is a group of simple product tiles. Clicking on any title displays a product dialog with more product details and add to cart button. Products displayed in a collection are defined in app.tipser.com.

<p name="My collection" data-tipser-cid="5b2788909d25801adcb23f4f"></p>
<p
  name="My collection"
  data-tipser-cid="5b2788909d25801adcb23f4f"
  data-tipser-imgsize="small"
></p>
<p
  name="My collection"
  data-tipser-cid="5b2788909d25801adcb23f4f"
  data-tipser-carousel
></p>

Elements with attribute data-tipser-cid will be replaced with Collection element of given id (value of data-tipser-cid).

To make the collection items smaller / larger use the data-tipser-imgsize attribute with values small for smaller and large for lager product tiles. The default value for imgSize parameter is medium. When changing the value to small you get slightly smaller product-cards:

If you'd like the collection of more than several products to take less space, you can display it as one-row only carousel element. To do that, please use data-tipser-carousel attribute.

Open this snippet on Code Pen


Cart tag

To keep the user informed about the state of their shopping cart and make it possible to finalize the checkout process at any time, you can attach a live shopping cart icon on your page.

tipser.elements("posId").mountCart(".my-cart-container");

To activate the Cart, you need to dedicate an element on your page to host a shopping cart and pass a CSS selector to that element to the mountCart function, as in the example snippet.

The cart icon can be placed anywhere on your website. If you want to keep it visible at all times, please follow the instructions.


Checkout tag

Then use this HTML tag to render Checkout on your page.

<div id="tipser_checkout"></div>

Modular Checkout

If you need more flexibility, you can use an element with data-tipser-modular-checkout attribute. The children of this element will define a set of checkout modules to be displayed. For example, a children element with data-tipser-modular-checkout-cart-products attribute (nested under the master element with data-tipser-modular-checkout attribute) will render the list of items that are being purchased by the user.

A working example of the checkout view consisting of: items list, delivery address form and the payment form:

<div data-tipser-modular-checkout>
  <div data-tipser-modular-checkout-cart-products></div>
  <div data-tipser-modular-checkout-customer-address-delivery></div>
  <div data-tipser-modular-checkout-payment></div>
</div>

Following modules can be nested within <div data-tipser-modular-checkout></div>:

Checkout Cart Products

This section displays a list of items in the current checkout.

<div data-tipser-modular-checkout-cart-products></div>

Checkout Customer Delivery Address form

This form accepts User's delivery address details.

<div data-tipser-modular-checkout-customer-address-delivery></div>

It also accepts attributes that change the appearance and/or functionality of the component:

attr name description type values default value
data-tipser-hide-submit-button hides the "submit" button that collapses the form after filling it with correct data HTML boolean
data-tipser-submit-behaviour the behaviour of the form after submitting it string 'collapse', 'none' 'collapse'
data-tipser-hide-use-as-billing-address-checkbox hides the checkbox allowing to copy delivery address as billing address HTML boolean

Checkout Customer Billing Address form

This form accepts User's billing address details.

<div data-tipser-modular-checkout-customer-address-billing></div>

It also accepts attributes that change the appearance and/or functionality of the component:

attr name description type values default value
data-tipser-hide-submit-button hides the "submit" button that collapses the form after filling it with correct data HTML boolean
data-tipser-submit-behavior the behavior of the form after submitting it string 'collapse', 'none' 'collapse'
data-tipser-depends-on lets you render the component depending on the delivery form being valid string 'none', 'validDeliveryAddress' 'none'

Checkout Cart Summary

A section with a summary of the total costs resulting from the checkout.

<div data-tipser-modular-checkout-cart-summary></div>

Checkout Payment

A payment section, accepting user's payment input (e.g. credit card number). In case of Klarna integrations, this component will additionally contain delivery and billing address forms.

<div data-tipser-modular-checkout-payment></div>

It also accepts attributes that change the functionality of the component:

attr name description type values default value
data-tipser-hide-pay-button hides the "pay" button in Stripe payment provider form HTML boolean
data-tipser-depends-on lets you render the component depending on the delivery form being valid string 'none', 'validDeliveryAddress' 'none'

Checkout Promotion Code

An element for entering promotion codes

<div data-tipser-modular-checkout-cart-promo-code></div>

Order Confirmation Page

A confirmation page displaying a summary of the completed order.

<div data-tipser-modular-checkout-order-confirmation></div>

Checkout Order Processing

A loading animation for checkout processing.

<div data-tipser-modular-checkout-order-processing></div>

An element with text explaining legal terms of the purchase.

<div data-tipser-modular-checkout-legal></div>

Versioning

Tipser Script follows Semantic Versioning. This means that an increase in the major number in our version indicates potential breaking changes. Please be aware of that! For the react version, it is recommended to auto-update to the latest version with the same major number (see the caret (^) character in package.json file described here).

Imperative script functions

In case you need to open Tipser dialogs from the code or perform operations like adding a Tipser product to cart, we provide a set of JavaScript functions that serve that purpose.

All the below functions are accessible from the Tipser Script instance:

const script = tipser.elements(posId, options);
script.goToProduct(productId);

goToProduct() function

script.goToProduct(productId);

Opens the product modal dialog for a product with a given Tipser product id. Alternatively, redirects to the URL defined in customUrls.baseProductUrl configuration option if specified.

goToCheckout() function

script.goToCheckout();

Opens the checkout modal dialog. Alternatively, redirects to the URL defined in customUrls.checkoutUrl configuration option if specified.

addToCart(productId) function

script.addToCart(productId).then(() => {
 console.log("adding to cart successful");
}).catch((e) => {
 console.log("adding to cart failed", e)
});

Adds to cart a product with a given Tipser product id. Returns a promise that will succeed or reject depending on the status of that operation.

removeFromCart(productId) function

script.removeFromCart(productId).then(() => {
    console.log("removing from cart successful");
}).catch((e) => {
    console.log("removing from cart failed", e)
});

Adds to cart a product with a given Tipser product id. Returns a promise that will succeed or reject depending on the status of that operation.

getCartItems() function

script.getCartItems().then((cartItems) => {
    console.log("cart items: ", cartItems)
}).catch((e) => {
    console.log("failed to get cart items", e)
});

Returns a Promise that will eventually return a list of all Tipser products currently in the shopping cart.

addToCartAndGoToCheckout(productId) function

script.addToCartAndGoToCheckout(productId).then(() => {
    console.log("add to cart and go to checkout successful");
}).catch((e) => {
    console.log("add to cart and go to checkout  failed", e)
});

Adds to cart a product with a given Tipser product id and then opens the checkout modal dialog. Alternatively, redirects to the URL defined in customUrls.checkoutUrl configuration option if specified. Returns a promise that will succeed or reject depending on the status of that operation.

API reference

All configuration supported by Tipser Script is listed below.

Parameter Default Description Example
lang 'en-US' a locale to be used by the Tipser content. Possible values: 'en-US', 'de-DE', 'fr-FR' and 'sv-SE'. More info at Language and localeEnvironment 'de-DE'
env 'prod' Tipser environment to be used by the Tipser content. Possible values: 'stage' and 'prod'. More info at Environment 'stage'
openOldDialog true Enables the new implementation of modal dialogs. It is much recommended for the new users to set it to false. false
defaultAddedToCartPopup true Controls default Added To Cart Popup. It appears when user adds a product to the cart. It improves UX by highlighting the action and allowing to navigate quickly to the cart modal window. true or false
useDefaultErrorHandler true when set to false and error happens, default message won't be displayed see Adding onError handler
eventsHandlers {} the object of event handlers. See Event handlers { onError: console.error.bind(console) }
useDeepLinking true Makes Shop element to use hash navigation when switching between categories. More info at Use Deep Linking false
modalUi {} Customization of Tipser Dialog. More info at Parameters for dialog customization { hideSearchIcon : true}
primaryColor #333 Hex color code, affecting eg. buy-button color and Cart indicator #5F9F9F
disableDialog false If set to true, a redirect to the product page is done instead of opening the product dialog (read more at: Embedding Elements in native apps section) false
disableAnalytics false If set to true, all Tipser analytics requests will be blocked (no events to Analytics and stats.tipser.com will be sent) true

Event handlers

Event handlers can be passed as part of configuration. There is a number of event exposed by the Tipser Script that can be listened to programatically, such as technical events, shopping behavior, errors and analytics. You may hook in your event listener into Tipser Script via eventsHandlers option.

tipser.elements("posId", {
  eventsHandlers: {
    onAddToCart: (payload) => {
      console.log("Hurray, you have added item to cart. ", payload.product);
      console.log("Your cart size is now. ", payload.cartSize);
    },
  },
});

Whenever an event occurs, Tipser Script will call your event listener, passing only one argument - payload - which will hold event data (different to each event type). Above example demonstrates how to listen to the add to cart event and log current cart size and newly added product. Currently supported handlers are: onAddToCart and onError.


onAddToCart

onAddToCart: (cartSize: number, product: TipserProductModel)

TipserProductModel interface is as follows:

interface TipserProductModel {
  id: string;
  title: string;
  description: string;
  brand: string;
  images: any[];
  isInStock: boolean;
  deliveryTime: string;
  priceIncVat: PriceModel;
  deliveryCost: PriceModel;
  variants: TipserProductModel[];
  discountPriceIncVat: PriceModel;
  freeReturn: boolean;
}

onError

By default, in case of an unexpected error happening (connection issues or unhandled runtime exceptions), an error popup will appear. If you want to disable the deafult error messages, set useDefaultErrorHandler option to false, and listen to error messages via onError event handler.

tipser.elements("posId", {
  useDefaultErrorHandler: true,
  eventsHandlers: {
    onError: (error) => {
      console.log(error);
    },
  },
});

The payload of error event is as follows:

The onError event handler is used with useDefaultErrorHandler config option. When that option is set to false (default to true) the error will not be shown on the screen.

onStockCountChange

This handler takes care of the edge case, when while in the checkout process and before payment, the stock count of an item in the cart becomes lower than the number of items in the cart. By default, in such a situation, we display an overlay with an information about the stock count change and the button for reloading the checkout. If you wish to customize this behaviour, you can use a callback onStockCountChange(items: CartItemModel[]) => void, which will prevent the default behavior.

Customizing the styles

Our e-commerce components are the "building blocks" designed to fit your page as much as possible. We created the styling in a way that delivers a nice look & feel from the start, but also allows you to change them easily to fit your unique sense of style. For example, all elements' font-family and font-size attributes are set to inherit them from the host page. If you need to change some other styles, please overwrite the CSS classes corresponding to the elements that you customize (listed below).

Product Card

The Product Card is an item used for displaying a single tile in a Collection or Store component, among others. The font-family used in the description section of the Product Card is inherited from your website's styles, and the font-size is expressed in the relative em units controlled in .te-product-card class. The default value used there is 12px, which you can easily change by adding to your CSS the following style:

.te-product-card {
  font-size: 14px;
}

All the description elements (product name, brand and price) will become bigger / smaller according to the value you specify in the px unit. If you wish to change single description element, please use its specific class names:

.te-product-card-name
.te-product-card-brand
.te-product-card-price
and:
.te-product-card-sales-price
.te-product-card-price-regular-price
for products on sale.

Cart

The Cart component with the cart icon can be placed anywhere on your website. (It is highly advisable to place it in your navigation element among other icons such as search, home etc.) However, if you want to keep it visible at all times, attached to the right side of the viewport, you can use these styles:

.cart-icon {
  position: fixed;
  right: 0;
  top: 121px;
  background: #fff;
  padding: 10px;
  box-shadow: -2px 2px 7px rgba(0, 0, 0, 0.3);
  z-index: 10;
}

Adding Primary color

If you'd like to unify our design with your own color-theme, you can use our primary-color configuration option. If your primary color is a bright one, you might also want to change the text color of the elements that use the primary color as the background, e.g. add to cart buttons. For example in buttons by default the color we use is white.

/* if your primary color is bright,
    you may also consider changing the text color for elements like buttons: */
.te-button-text {
  color: #333;
}

If you'd like to change other elements' color as well, please use specific classes to override the styles.

Starter projects

A working examples of page based on Tipser Script can be found on Tipser Script Bootstrap page.

The code of that page is available as a GitHub Tipser Script Bootstrap project. Feel free to checkout it and play with it on your local machine!

Tipser Elements

Tipser Elements is a React library provided by Tipser enabling you to add shoppable content to your React app connected to Tipser e-commerce system with minimal effort.


Live demo

A working examples of page based on Tipser Elements can be found on Tipser Elements Bootstrap page.

The code of that page is available as a GitHub Tipser Elements Bootstrap project. Feel free to checkout it and play with it on your local environment!


Tipser Elements: Quick Start

This quick guide explains how to intialize and render Tipser React Elements on your React app. It requires you to have a publisher account created in order to get the posId, as well as have some collections created in your shop here.

If you're all set up, follow this three steps to rock on your app with Tipser React Elements!


Instalation

Add the library to your project:

npm install --save @tipser/tipser-elements@latest-v2


Import Tipser React Elements

The following lines of code import Tipser Elements to your project:

import { Product, TipserElementsProvider } from "@tipser/tipser-elements"; // Imports all the needed React components
import "@tipser/tipser-elements/dist/index.css"; // Imports the base set of CSS styles (can be overriden)

Below is a full example illustrating how can you combine Tipser Elements with your own application:

import React from "react";
import ReactDOM from "react-dom";
import {
  Product,
  Cart,
  Store,
  TipserElementsProvider,
} from "@tipser/tipser-elements";
import history from "path/to/your/history"; // path to your history object that has been passed to react-router'
// import CSS files for Tipser Elements
import "@tipser/tipser-elements/dist/index.css";

// simple configuration
const config = {
  lang: "en-US",
  primaryColor: "#0000FF",
};

ReactDOM.render(
  <TipserElementsProvider
    posId="59e86b79b8f3f60a94ecd26a"
    config={config}
    history={history}
  >
    <header>
      <nav>
        Welcome to my store!
        <Cart />
      </nav>
    </header>
    <main>
      <Product productId="5ba2334a781baa0001ccdffc" />
      <Store />
    </main>
    <footer>
      <span>This is the footer</span>
    </footer>
  </TipserElementsProvider>,
  document.getElementById("root")
);

Please make sure:

Also, check our configuration options.


Tipser Elements API

Configuration options of Tipser Elements

Configuration is an object, which should be inserted into TipserElementsProvider as config prop.

All properties are optional:

Parameter Default Description Example
lang 'en-US' a locale to be used by the Tipser content. Possible values: 'en-US', 'de-DE', 'fr-FR' and 'sv-SE'. More info at Language and localeEnvironment 'de-DE'
env 'prod' Tipser environment to be used by the Tipser content. Possible values: 'stage' and 'prod'. More info at Environment 'stage'
defaultAddedToCartPopup true Controls default Added To Cart Popup. It appears when user adds a product to the cart. It improves UX by highlighting the action and allowing to navigate quickly to the cart modal window. true or false
useDefaultErrorHandler true when set to false and error happens, default message won't be displayed see Adding onError handler
eventsHandlers {} the object of event handlers. See Event handlers { onError: console.error.bind(console) }
useDeepLinking true Makes Shop element to use hash navigation when switching between categories. More info at Use Deep Linking: false
modalUi {} Customization of Tipser Dialog. More info at Parameters for dialog customization { hideSearchIcon : true}
disableDialog false If set to true, a redirect to the product page is done instead of opening product dialogs (read more at: Embedding Elements in native apps section) false
disableAnalytics false If set to true, all Tipser analytics requests will be blocked (no events to Analytics and stats.tipser.com will be sent) true
enableCheckoutV2 false If set to true, the more robust v2 version of Tipser Checkout API is used (recommended) true

Event Handlers

let tipserConfig = {
  lang: "en",
  primaryColor: "#FF0000",
  // ----- EVENT HANDLING START
  useDefaultErrorHandler: true,
  eventsHandlers: {
    onError: (error) => {
      console.log(error);
    },
    onAddToCart: ({ cartSize, product }) => {
      console.log("Hurray, you have added item to cart. ", product);
      console.log("Your cart size is now. ", cartSize);
    },
  },
  // -- EVENT HANDLING END
};

// ... as in example above class App extends Component { ... }

Event handlers may be passed as a part of config option of TipserElementsProvider. There is a number of events exposed for developer.

Handler name description params
onError Main goal of this handler is to add additional behavior when the error appears. object of type EventError (see EventError
onAddToCart when product is being added to cart event is triggered object of type {cartSize, product} where cartSize is a current size of cart after adding to cart and product is a product object with properties see (see TipserProductModel)

Event Error interface

export interface EventError {
  type?: string;
  id?: string;
  message?: string;
  stack?: string;
}

Param of onError handlers

Tipser Product Model interface

export interface TipserProductModel {
  id: string;
  title: string;
  description: string;
  brand: string;
  images: any[];
  isInStock: boolean;
  deliveryTime: string;
  priceIncVat: PriceModel;
  deliveryCost: PriceModel;
  variants: TipserProductModel[];
  discountPriceIncVat: PriceModel;
  freeReturn: boolean;
}

When onAddToCart is being dispatched the handlers are triggered with object with cart size of type string and product of type TipserProductModel.

TipserElementsProvider

Entry point to Tipser Elements (providing a context for other elements living inside it). Every other element documented here needs to be below TipserElementsProvider in the React elements hierarchy.

prop name description type required default value
posId id of Point of sale string true
config configuration object (see definition here) object false {}
history a history implementation specific for your web framework, needed for soft redirects instead of full page reloads object false window.history

Product

Displays a tile or a listing for the product specified by the productId prop. Comes in three different flavours, described below.

Product tile - a small, rectangular product preview with the title, the price and the buy button. When clicked (over the buy button or anywhere else), it opens a dialog with product details.

Activated by viewMode="compact".

Example:

Full product view - a larger, more detailed and better exposed product view, typically occupying the full width of the article. Allows the user to add the product to cart without opening the product dialog (which means less steps needed by the user to purchase the product). With

Activated by viewMode="full" (or skipping the viewMode prop, as this is the default value).

Example:

prop name description type required default value
productId where to find string true none
viewMode enables full or compact product display string ('full', 'compact') false 'full'

example:

<Product productId="5c751cf82d3f3b0001bcec8c" viewMode={"compact"} />

Learn more.

Collection

Renders a collection of product tiles based on collectionId prop.

If the collection has many elements and you want to display it in one row as a collection, you need to add carousel prop. You can also use 'imgSize' prop to control the size of displayed product tiles. Learn more.

example:

<Collection
  collectionId={"5b2788909d25801adcb23f4f"}
  carousel
  imgSize={"small"}
/>
prop name description type required default value
collectionId where to find string true none
carousel enables carousel display boolean false false
imgSize changes the size of single product tile string ('small', 'medium' or 'large') false none

ProductPage

A full-sized product component to be used on a dedicated page.

<ProductPage productId="5c751cf82d3f3b0001bcec8c">

Example:

Properties:

prop name description type required default value
productId the Tipser id of the product to render string true

ModularProduct

A more customisable and feature-rich version Product component. ModularProduct component allows you to mix and match the elements that are included in your product view. In other words, you can build you own version of product view from the existing components like from Lego pieces. And you can even mix in your own components in between.

A minimal working example:

<ModularProduct>
    <ProductTitle />
    <ProductPrice />
    <ProductImage />
    <ProductVariantSelector />
    <ProductBuyButton />
</ModularProduct>

And a more sophisticated one:

<ModularProduct productId="5c751cf82d3f3b0001bcec8c">
  <div className="top-container">
    <div className="left-column">
      <ProductThumbnails direction="vertical">
      <ProductImage />
    </div>
    <div className="right-column">
      <ProductTitle />
      <ProductPrice />
      <ProductColorRelations />
      <ProductVariantSelector />
      <ProductAvailabilityInfo />
      <ProductBuyButton />
    </div>
  </div>
  <div className="bottom-container">
    <ProductDescription />
    <ProductSimilarProducts />
    <ProductStyleWithProducts />
  </div>
</ModularProduct>

ProductContainer

The default implementation of the main part of the product view, consisting of ProductImage, ProductTitle, ProductColorRelations, ProductVariantSelector, ProductAvailabilityInfo and ProductBuyButton. It may come handy if you don't want to mess with the main part of the product view, and just need to customize the remaining sections.

<ModularProduct productId="5c751cf82d3f3b0001bcec8c">
  <ProductContainer />
  <ProductStyleWithProducts />
  <ProductDescription />
  <ProductSimilarProducts />
</ModularProduct>

Product container

ProductTitle

Displays the name and the brand of the product

<ModularProduct productId="5c751cf82d3f3b0001bcec8c">
  <ProductTitle />
</ModularProduct>

Similar Products Component

ProductPrice

Displays the price, discount price and unit price ( eg. 2$/100ml, if applicable ) for the product.

<ModularProduct productId="5c751cf82d3f3b0001bcec8c">
  <ProductPrice />
</ModularProduct>

Similar Products Component

ProductImage

Displays the full-size version of the active product image. With some configuration options it can be also used to change the active product image.

<ModularProduct productId="5c751cf82d3f3b0001bcec8c">
  <div>
    <ProductImage enableDots enableArrows />
  </div>
</ModularProduct>

Product Image

prop name type description default
enableSwipe boolean enables swipe functionality (recommended for touch devices) false
enableArrows boolean show left and right arrows for changing images false
enableDots boolean show slider bullet dots false
className string custom CSS class name to apply none

ProductThumbnails

Displays the product thumbnails.

<ModularProduct productId="5c751cf82d3f3b0001bcec8c">
  <div>
    <ProductThumbnails />
  </div>
</ModularProduct>

Product Thumbnails horizontal

prop name type description default
imageFit 'contain' | 'cover' changes the background-size property 'cover'
direction 'vertical' | 'horizontal' changes the orientation of the thumbnails container 'horizontal'
className string custom CSS class name to apply none

Product Thumbnails vertical

ProductVariantSelector

A dropdown listing all variants of the product. When a variant is selected from the list, all the displayed product information will be updated accordingly (only available variants are selectable).

<ModularProduct productId="5c751cf82d3f3b0001bcec8c">
  <ProductVariantSelector />
</ModularProduct>

Similar Products Component

ProductAvailabilityInfo

Displays information related to product availability and delivery, such as:

<ModularProduct productId="5c751cf82d3f3b0001bcec8c">
  <ProductAvailabilityInfo />
</ModularProduct>

Product availability component

ProductDescription

Displays the product text description.

<ModularProduct productId="5c751cf82d3f3b0001bcec8c">
  <ProductDescription />
</ModularProduct>

Product description Component

ProductBuyButton

A button adding the product to the shopping cart.

<ModularProduct productId="5c751cf82d3f3b0001bcec8c">
  <ProductBuyButton />
</ModularProduct>

Product buy button component

ProductSimilarProducts

An automatically generated list of similar products (basing on text similarity, image similarity, more from the same brand, etc).

<ModularProduct productId="5c751cf82d3f3b0001bcec8c">
  <ProductSimilarProducts />
</ModularProduct>

Similar Products Component

ProductColorRelations

Displays the list of color variants of the product and switches the product view to any of them, when clicked.

<ModularProduct productId="5c751cf82d3f3b0001bcec8c">
  <ProductColorRelations />
</ModularProduct>

Similar Products Component

ProductStyleWithProducts

A hand-picked list of other products that go well together with the current product.

<ModularProduct productId="5c751cf82d3f3b0001bcec8c">
  <ProductStyleWithProducts />
</ModularProduct>

Similar Products Component

Checkout

A predefined checkout component with all necessary elements (product list, user address form, payment widget, etc) to make the purchase possible.

import { Checkout } from '@tipser/tipser-elements';
 ...
<Checkout />

The above code will render more or less the following output:

Properties:

prop name description type required default value
className custom CSS class name to apply string false none

For more flexibility use ModularCheckout component.

ModularCheckout

The main element providing the checkout context for all of the checkout modules nested under it.

import {
  ModularCheckout,
  CheckoutCartProducts,
  CheckoutCustomerAddressDelivery,
  CheckoutPayment,
  CheckoutOrderProcessing,
  CheckoutOrderConfirmation,
} from "@tipser/tipser-elements";

<ModularCheckout>
  <ModularCheckout.New>
    <CheckoutCartProducts />
    <CheckoutCustomerAddressDelivery />
    <CheckoutPayment />
  </ModularCheckout.New>

  <ModularCheckout.Processing>
    <CheckoutOrderProcessing />
  </ModularCheckout.Processing>

  <ModularCheckout.Empty>
    <div>Your cart is empty!</div>
  </ModularCheckout.Empty>

  <ModularCheckout.Confirmed>
    <CheckoutOrderConfirmation />
  </ModularCheckout.Confirmed>
</ModularCheckout>

A list of supported modules that can be nested under ModularCheckout:

CheckoutProductList

A list of items in the current checkout. By default, (unless readOnly prop is set to true) comes toger with controls allowing the user to remove products and change their quantities in the current checkout.

Properties:

prop name description type required default value
className custom CSS class name to apply string false none
readOnly should removing from cart and changing quantities be blocked? boolean false false

CheckoutCustomerAddressDelivery

A form accepting user’s delivery address

Properties:

prop name description type values required default value
className custom CSS class name to apply string false none
hideUseAsBillingAddressCheckbox hides the checkbox allowing to copy delivery address as billing address boolean false false
submitBehavior the behaviour of the form after submitting it enum 'collapse', 'none' false 'none'
hideSubmitButton hides the "submit" button that collapses the form after filling it with correct data boolean false false

CheckoutCustomerAddressBilling

A form accepting user’s billing address

Properties:

prop name description type values required default value
className custom CSS class name to apply string false none
submitBehavior the behaviour of the form after submitting it enum 'collapse', 'none' false none
hideSubmitButton hides the "submit" button that collapses the form after filling it with correct data boolean false false
submitBehavior the behavior of the form after submitting it string 'collapse', 'none' false 'collapse'

CheckoutSummary

A summary of the total costs and taxes resulting from the checkout

Properties:

prop name description type required default value
className custom CSS class name to apply string false none

CheckoutPayment

A payment section, accepting user's payment input (e.g. credit card number). In case of Klarna integrations, this component will additionally contain delivery and billing address forms.

Properties:

prop name description type values required default value
className custom CSS class name to apply string false none
hidePayButton hides the "pay" button in Stripe payment provider form boolean false false
dependsOn lets you render the component depending on the delivery form being valid string 'none', 'validDeliveryAddress' false 'none'

CheckoutCartPromoCode

A widget for entering promotion codes

Properties:

prop name description type required default value
className custom CSS class name to apply string false none

CheckoutLegal

A text explaining legal terms of the purchase

Properties:

prop name description type required default value
className custom CSS class name to apply string false none

CheckoutOrderProcessing

A loading animation for checkout processing

Properties:

prop name description type required default value
className custom CSS class name to apply string false none

CheckoutOrderConfirmation

A confirmation page displaying a summary of the completed order

Properties:

prop name description type required default value
className custom CSS class name to apply string false none

ModularCheckout.Empty, ModularCheckout.New, ModularCheckout.Processing and ModularCheckout.Confirmed

The elements ModularCheckout.Empty, ModularCheckout.New, ModularCheckout.Processing and ModularCheckout.Confirmed are helper elements that are used to conditionally render their children only for a given checkout status. If none of these elements, will be used, all the elements passed to ModularCheckout will be rendered for every checkout status.

For example:

<ModularCheckout.Confirmed>
  <CheckoutOrderConfirmation />
</ModularCheckout.Confirmed>

This will guarantee that the CheckoutOrderConfirmation module is only displayed if the current checkout status is confirmed, that is, that the payment has been successfully processed.

Properties:

No properties supported.

Multi-step ModularCheckout

It is possible to spread the modular checkout over several pages. The only requirement is to keep the Checkout element as a parent for all of the routes that are using any of the checkout modules described before.

The example below illustrates how to do it with the react-router library.

import React from "react";
import { Checkout } from "@tipser/tipser-elements";
import { Route, Switch, withRouter } from "react-router";

export const CheckoutMultipage = withRouter(({ match }) => (
  <div>
    <div className="te-multipage-label">Checkout multipage</div>
    <ModularCheckout>
      <Switch>
        <Route path={`${match.url}/step-1`}>
          <CheckoutPage1 />
        </Route>
        <Route path={`${match.url}/step-2`}>
          <CheckoutPage2 />
        </Route>
      </Switch>
    </ModularCheckout>
  </div>
));

const CheckoutPage1 = () => (
  <>
    <h2>Step 1</h2>
    <CartProducts />
    <CustomerAddressDelivery />
    <CartSummary />
  </>
);

const CheckoutPage2 = ({ checkout }) => (
  <>
    <h2>Step 2</h2>
    <CartPromoCode />
    <CheckoutPayment />
  </>
);

ModularCart

The main element providing the shopping cart context for all of the shopping cart modules nested under it.

import {
  ModularCart,
  CartProductList,
  CartSummary,
} from "@tipser/tipser-elements";

<ModularCart>
    <CartProductList />
    <CartSummary />
</ModularCart>

A list of supported modules that can be nested under ModularCart:

CartProductList

A list of items in the shopping cart. By default, (unless readOnly prop is set to true) comes together with controls allowing the user to remove products and change their quantities in the shopping cart.

Properties:

prop name description type required default value
className custom CSS class name to apply string false none
readOnly should removing from cart and changing quantities be blocked? boolean false false

CartSummary

A summary of the total costs and taxes for the products in the shopping cart.

Properties:

prop name description type required default value
className custom CSS class name to apply string false none

ModularCart.Empty and ModularCart.NonEmpty

ModularCart.Empty and ModularCart.NonEmpty are helper elements that are used to conditionally render their children only for a given shopping cart status. If none of these elements is be used, all the elements passed to ModularCart will be rendered for every shopping cart status.

For example, to provide a custom empty cart information:

<ModularCart>
    <ModularCart.Empty>The shopping cart is empty, please add some products first!</ModularCart.Empty>
    <ModularCart.NonEmpty>
        <CartProductList />
        <CartSummary />
    </ModularCart.NonEmpty>
</ModularCart>

ProductList

A list of products, looking the same as Collection component, but instead of the collectionId, you need to pass the array of productIds. and optional carousel and imgSize attributes.

example:

<ProductList
  productIds={[
    "5911c26c8aa0ce3d70cd607b",
    "5c878cc5a6e96d00012e1771",
    "5c878cc5a6e96d00012e1775",
  ]}
/>
prop name description type required default value
productIds array of single productIds array of strings true none
carousel enables carousel display boolean false false
imgSize changes the size of single product tile string ('small', 'medium' or 'large') false none

Learn more.

Cart

A cart icon element that displays the number of items in your cart and brings the user to the checkout when clicked (either by opening the checkout dialog or by redirecting to the checkout embedded page).

Store

Displays the store view consisting of all your store collections. We recommend to use it on a dedicated store subpage on your site.

The Store element accepts additional inlineMenu prop, which renders mobile menu items inline, as opposed to the default dropdown one.

import { Store } from '@tipser/tipser-elements';

<Store />

Imprerative elements functions

In case you need to open Tipser dialogs from the code or perform operations like adding a Tipser product to cart, we provide a set of JavaScript functions that serve that purpose.

All the below functions are accessible from useInternalFunctions hook to every component living in the context of TipserElementsProvider.

const { goToProduct, goToCheckout, addToCart, removeFromCart, addToCartAndGoToCheckout } = useInternalFunctions();

goToProduct() function

const { goToProduct } = useInternalFunctions();
goToProduct(productId);

Opens the product modal dialog for a product with a given Tipser product id. Alternatively, redirects to the URL defined in customUrls.baseProductUrl configuration option if specified.

goToCheckout() function

const { goToCheckout } = useInternalFunctions();
goToCheckout();

Opens the checkout modal dialog. Alternatively, redirects to the URL defined in customUrls.checkoutUrl configuration option if specified.

addToCart(productId) function

const { addToCart } = useInternalFunctions();
addToCart(productId).then(() => {
  console.log("adding to cart successful");
}).catch((e) => {
  console.log("adding to cart failed", e)
});

Adds to cart a product with a given Tipser product id. Return a promise

removeFromCart(productId) function

const { removeFromCart } = useInternalFunctions();
removeFromCart(productId).then(() => {
    console.log("removing from cart successful");
}).catch((e) => {
    console.log("removing from cart failed", e)
});

Adds to cart a product with a given Tipser product id. Returns a promise that will succeed or reject depending on the status of that operation.

getCartItems() function

const { getCartItems } = useInternalFunctions();
getCartItems().then((cartItems) => {
    console.log("cart items: ", cartItems)
}).catch((e) => {
    console.log("failed to get cart items", e)
});

Returns a Promise that will eventually return a list of all Tipser products currently in the shopping cart.

addToCartAndGoToCheckout(productId) function

const { addToCartAndGoToCheckout } = useInternalFunctions();
addToCartAndGoToCheckout(productId).then(() => {
   console.log("add to cart and go to checkout successful");
}).catch((e) => {
   console.log("add to cart and go to checkout  failed", e)
});

Adds to cart a product with a given Tipser product id and then opens the checkout modal dialog. Alternatively, redirects to the URL defined in customUrls.checkoutUrl configuration option if specified. Returns a promise that will succeed or reject depending on the status of that operation.

Advanced usage

Getting Tipser ids

Publisher data

In order to associate a piece of data owned by the publisher with an order item in Tipser, you can use a concept called posData. A posData is an arbitrary string that can be used to store additional information (e.g. session id, user id in your system, etc) attached to order in Tipser's database. After the transaction is finalized, the string passed as posData will be available back in the response from the Commissions API that can be consumed by your backend code (e.g. reporting systems).

There are three ways to enable posData:

Option 1: As a global configuration setting that is passed to Elements/SDK initialization (good for static data, like the release number):

const tipserConfig = { posData: "release_2.1.5" };

Option 2: After Elements/Script initialization with updateConfig(posData: string) function (useful for the data that is not yet available at the time of initialization):

in Tipser Script:

const someData = JSON.stringify({sessionId: "5fa01be88b51", userId: "5fa01bfd3be2"});

tipserScript.updateConfig({posData: someData});

in Elements:

Just update the value passed to the config prop of TipserElementsProvider in the next render cycle:

const someData = JSON.stringify({sessionId: "5fa01be88b51", userId: "5fa01bfd3be2"});
const elementsConfigWithPosData = {...baseElementsConfig, posData: someData};

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

This will apply for the next and all the subsequent products added to cart.

Option 3: In the second parameter of addToCart or openDirectToCheckoutDialog function (convenient if each product added to cart needs to have a different value of posData):

const addToCartOptions = {
  posData: JSON.stringify({sessionId: "5fa01be88b51", userId: "5fa01bfd3be2"})
};
tipserSdk.addToCart(productId, addToCartOptions);

Checkout submit functions

You can access the checkout context and create custom functions for submitting delivery and billing address forms as well as Stripe payment details.

in Tipser Elements:

If you're using React in your project, you can apply useCheckoutContext hook:

const CustomSubmitButton = () => {
  const checkoutContext = useCheckoutContext();
  const handleClick = useCallback(() => {
    // for delivery addres submition use:
    checkoutContext.addresses.deliveryAddressForm.submit()
    // for billing address submition use:
    checkoutContext.addresses.billingAddressForm.submit();
    // for payment submition use:
    checkoutContext.payment.paymentForm.submit();
  }, [checkoutContext]);
  return <button onClick={handleClick}>Submit delivery address</button>
}

in Tipser Script:

Even if you don't use React in your project, we allow you to access the checkout context and create custom functions for submitting delivery and billing address forms as well as Stripe payment details.

To access the context, create an Event Listener attached to your checkout wrapper (the element with data-tipser-modular-checkout attribute):

const modularCheckoutDiv = document.querySelector('#modular_checkout_root');
let getCheckoutContext = null;

modularCheckoutDiv.addEventListener('checkout-context-ready', (evt) => {
  getCheckoutContext = evt.detail.getCheckoutContext;
});

Now create your custom Submit button, eg:

<button id="submit-button" onclick="customSubmit()"></button>

and define your submit delivery address function:

const customSubmit = () => {
    const checkoutContext = getCheckoutContext();
  // for delivery addres submition use:
    checkoutContext.addresses.deliveryAddressForm.submit();
  // for billing address submition use:
    checkoutContext.addresses.billingAddressForm.submit();
  // for payment submition use:
    checkoutContext.payment.paymentForm.submit();
};

Accessing Tipser SDK

Low level operations, like programatically opening Tipser dialogs can be done by using the underlying Tipser SDK instance, which is a part of the Tipser Elements library.

If you use Tipser Elements:

const elements = tipser.elements(posId, config);
elements.sdkInstance.openProductDialog(productId);

If you use Tipser React Elements:

  1. Create a custom React component and mount it anywhere under TipserElementsProvider in React elements hierarchy.
  2. Inside that component you can use tipserSdk instance from the context TipserContext. This how it can be done with React Hooks:
import { useTipserSdk } from `@tipser/tipser-elements`;

const DialogOpener = () => {
  const sdk = useTipserSdk();

  useEffect(() => { sdk.openProductDialog(productId); }, []);
}

This will only work if the DialogOpener component is under TipserElementsProvider in React components hierarchy:

<TipserElementsProvider>
  <!-- Any number of other components -->
  <DialogOpener />
</TipserElementsProvider>

Buyable banners

Tipser dialogs can be opened by the banners on your site. That way you can use your banners for E-commerce.

To connect your banner to Tipser, you need to do the following two steps:

  1. follow the instructions from Initializing Tipser Elements
  2. Add the line of javascript posted below to you banner. It should be invoked when the banner is clicked.

To display product dialog:

 window.top.postMessage({command: 'tipser.api.displayProduct', productId: tipserProductId}, '*')

To open the checkout view for a product directly:

  window.top.postMessage({command: 'tipser.api.directToCheckout', productId: tipserProductId}, '*')

Check this section to learn how to obtain the tipserProductId.

Live demo

 window.postMessage({command: 'tipser.api.displayProduct', productId: '5b59bfa4ca60310e30c9ac37'}, '*')


Server side rendering

To render Tipser Elements components on the server side, you can choose between two methods:

  1. Basic, in which you need to provide the ids of the Tipser Elements components that are used on the website.
  2. Smart, where you don't need to know the ids, but there would be two subsequent renders. In the first one, 'dry run', we collect information about components used on the website and the proper rendering, that uses the previously collected data.

Server-side rendering takes place in several steps:

Basic method

In the basic method, you should use the StateBuilder class to build the state. Start with creating its instances (which can be global):

import { StateBuilder } from '@tipser/tipser-elements';

const stateBuilder = new StateBuilder(POS_ID);

Then, while handling a specific request, use the stateBuilder.buildState method, passing an object containing information about which elements are placed on the website, i.e. what products (PRODUCT_IDS), collections (COLLECTION_IDS) and whether there is the shop component (IS_SHOP_ON_PAGE).

stateBuilder.buildState({
    productIds: PRODUCT_IDS,
    collectionIds: COLLECTION_IDS,
    shouldFetchStore: IS_SHOP_ON_PAGE,
})

This method returns a promise in which the ready state will be available, which should then be passed to SsrTipserElementsProvider:

stateBuilder.buildState({
    productIds: dataToFetch.productIds,
    collectionIds: dataToFetch.collectionIds,
    shouldFetchStore: dataToFetch.shouldFetchStore
}).then((initialState) => {
    const markup = renderToString(
        <TipserElementsProvider posId={POS_ID}>
            <SsrTipserElementsProvider initialState={initialState}>
                <App/>
            </SsrTipserElementsProvider>
        </TipserElementsProvider>
    );
    ...
 });

The initialState should then be transferred to the browser. For this purpose, it can be assigned to the global window object and placed in the returned html document:

<script>window.TIPSER_STATE = ${JSON.stringify(initialState)}</script>

On the client side, use window.TIPSER_STATE as the initialState of SsrTipserElementsProvider:

<TipserElementsProvider posId={POS_ID}>
    <SsrTipserElementsProvider initialState={window.TIPSER_STATE}>
        <App/>
    </SsrTipserElementsProvider>
</TipserElementsProvider>

That's all! The complete example is available here.

Smart method

In the smart version, the ComponentsStateSsrManager class should be used to build the state. For this purpose, you should create an instance of this class, which cannot be global, i.e. a new instance should be created for each request:

const componentsStateSsrManager = new ComponentsStateSsrManager(POS_ID);

Then perform the first rendering that will collect information about the components on the page:

const componentsStateSsrManager = new ComponentsStateSsrManager(POS_ID, 'prod')

const toRender = (
    <TipserElementsProvider posId={POS_ID}>
        <SsrTipserElementsProvider componentsStateManager={componentsStateSsrManager}>
            <App/>
        </SsrTipserElementsProvider>
    </TipserElementsProvider>
);
//first render
renderToString(toRender);

Next, use the buildState method available on the components instance of StateSsrManager, which returns a promise:

const componentsStateSsrManager = new ComponentsStateSsrManager(POS_ID)

const toRender = (
    <TipserElementsProvider posId={POS_ID}>
        <SsrTipserElementsProvider componentsStateManager={componentsStateSsrManager}>
            <App/>
        </SsrTipserElementsProvider>
    </TipserElementsProvider>
);
//first render
renderToString(toRender);

componentsStateSsrManager.buildState().then(() => {
    //second render
    const markup = renderToString(toRender);
    ...
});

In this case, you no longer need to manually pass the state to the SsrTipserElementsProvider, it is done automatically. The last step is to transfer the state to the browser in a similar way as it was done in the basic version, but this time the state should be extracted from componentsStateSsrManager using the getState method:

<script>window.TIPSER_STATE = ${JSON.stringify(componentsStateSsrManager.getState())}</script>

The state should be passed to the SsrTipserElementsProvider on the browser side:

<TipserElementsProvider posId={POS_ID}>
    <SsrTipserElementsProvider initialState={window.TIPSER_STATE}>
        <App/>
    </SsrTipserElementsProvider>
</TipserElementsProvider>,

Native apps

Tipser Elements is a web-based library and currently we don't provide a native version for Android or iOS (let us know if you'd like us to build it for you!)

However it is possible to embed Elements in a native app if you are using WebViews. We recommend you to follow one of the patterns described below.

Pattern 1: Full web integration

In case your articles are managed by a Web CMS and are displayed in WebView you can simply install and use Tipser Elements in your web articles. Just follow the instructions from Tipser Elements or Tipser Elements React sections, depending on your technology of choice.

The only customization that we recommend is using disableDialog option, as described below.

Replacing dialogs with redirects

By default, when a product tile is clicked, it opens a full screen product dialog. It may not be desired behavior in your mobile app. Set the configuration option: disableDialog to true to replace the dialog with a redirect to the product page. That way pressing the back button will bring the user back to your article.

Pattern 2: API integration

If you want to deliver native experience to your users, you can build your custom native e-commerce components (product tiles, shopping cart icon, etc) and use Tipser Rest API to populate them. With this pattern, only the checkout part needs to be displayed in a WebView.

Below is a basic example of a native view with a single product that opens Tipser checkout view when clicked.

Step 1: Rendering a product on your page

  1. Use the Tipser product API to get the product that you want to sell, e.g: https://t3-stage-api.tipser.com/v3/products/5d932be284da04000116ae3c?pos=59e86b79b8f3f60a94ecd26a (please, replace the example product id with the desired product and the pos parameter with your own POS id)
  2. Render your custom product component based on the response data

Step2: Opening the checkout view

  1. Create a WebView screen in your app project
  2. When your product is clicked, open the WebView with the URL like: https://www.tipser.com/direct-checkout?productId=productId&posId=59e86b79b8f3f60a94ecd26a (again, please make sure to replace the posId with your POS id and productId with the Tipser id of the product that was clicked)

Merchant signup on your site

It is possible to let merchants sign-up to Tipser directly from your site.

To make it possible, you need to have Tipser Elements installed on your page and you need to prepare a dedicated subpage for merchants, with a "Sign up" button that invokes the following code:

const elements = tipser.elements(posId, config);
elements.sdkInstance.openDialog(`https://merchsignup.netlify.app/#/start/${posId}`);

Embedded product page

By default, Tipser Elements opens product details and checkout views in modal overlays. If this is not the desired user experience, then you can replace it with an embedded product page. This way you will be able to show the same product view as the one normally displayed in modal but this time embedded on a standalone page.

The final result of implementing embedded product page may look like this:

Setting up embedded product page

To create an embedded product page you will need to implement the steps described below. The examples are using the typical way to do it in a React application, which is with react-router library. However, any other client-side or server-side routing implementation should be fine when used in a similar way.

Step 1: Create a route (sub-page) on your site which ends with Tipser product id (as a dynamic parameter). An example URL may look like this: https://www.example.com/product/12345678.

import { Router, Switch, Route } from 'react-router-dom';

<Router>
    <Switch>
        <Route exact path="/product/:productId">
          <ProductPageRoute />
        </Route>
    </Switch>
</Router>

Step 2: When the product page is visited, read the product id ("12345678" in the earlier example) from the URL and pass it to Tipser’s ProductPage component.

import { useParams } from 'react-router';
import { ProductPage } from '@tipser/tipser-elements';

const ProductPageRoute = () => {
  const { productId } = useParams();
  return (
    <ProductPage productId={productId} />
  );
};

Step 3: Set customUrls.productBaseUrl setting Elements configuration, so that we know where to redirect the user when a product title is clicked (instead of opening a modal).

const tipserElementsConfig = {
  customUrls: {
      productBaseUrl: '/product' //This is the base url to which the product id will be appended
  }
}

<TipserElementsProvider config={tipserElementsConfig} {...otherProps}>

</TipserElementsProvider>

This will cause all interactions that would normally open the modal to redirect to this base URL instead.

Step 4: This step is not required but is recommended for projects implementing client-side routing. Pass the implementation of the history object used by your application, so that Elements can do client-side redirects instead of full-page reloads.

import { Router } from 'react-router-dom';
import { createBrowserHistory } from 'history';

const history = createBrowserHistory();

<TipserElementsProvider history={history} {...otherProps}>
    <Router history={history}>

    </Router>
</TipserElementsProvider>

Voilà! This concludes the embedded product page setup.

Tipser SDK

Tipser SDK is a modular version of the Tipser script that provides only the core functionality necessary for integration with Tipser, specifically built for customers who have their own development resources and plan to build their shopping experience on their own.

Installation

Using npm or yarn

You can add the Tipser SDK to your project by adding it through npm or yarn.

Yarn:

yarn add @tipser/tipser-sdk

NPM:

npm install --save @tipser/tipser-sdk

Package instructions

Using a script tag in your page HTML source

Add a script to your page:

<script src="https://cdn.tipser.com/tipser-sdk/latest.js"></script>

Initialization

To initialize you need to create an instance of Tipser SDK:

const tipser = TipserSDK(posId: string, options: TipserSdkConfig): TipserSDKInstance;

Arguments:

posId (string) - id of shop's account in Tipser (required)

options (object) - an object of options (optional).

Returns:

Initialized Tipser SDK object that can be used to perform API calls further in this document.

Example:

const tipser = TipserSDK("59e86b79b8f3f60a94ecd26a", {primaryColor: "#FFFF00"});

The example starts Tipser SDK instance in which Tipser shop has the primary color set to yellow.

For configuration options see the configuration chapter of our documentation.

Customization of Tipser environment

By default, Tipser SDK connects to the production Tipser environment. Yet, if the testing environment is preferred (e.g. in order to do test purchase), then it can be customized with env parameter in TipserSDK options, which accepts the following values:

prod or production

stage or staging

dev or development

Example:

var tipser = TipserSDK("5aa12d639d25800ff0e56fc5", {env: "stage"});

The format of product ids

productId: string passed in product-related API calls. It consists of the masterProductId and, if the product has variants, the variantId, concatenated with an underscore "_". Then the productId should look like this:

<masterProductId>_<variantId>

Pos data

A configuration option posData, setPosData() function and the options parameter in addToCart() and openDirectToCheckoutDialog() can be used to configure publisher's data to be associated with Tipser transaction. Please refer to Publisher data section for a complete explanation.

Adding product to cart

addToCart(productId: string, options: {posData?: string}): Promise<void>

Adds a product with a given productId (specific to Tipser) to a cart.

productId: string, with the structure described here.

options: currently the only supported option is posData, which is a string to be attached to a product order.

Returns:

A Promise that resolves to no value but gives a chance to check if the request was successful or not (using .then()/.catch() semantics)

Example:

tipser.addToCart("5a98be67c0bdfb0c30865609_5a98be67c0bdfb0c30866509")
  .then(() => console.log("successfuly added to cart"))
  .catch((err) => console.log("failed to add to cart", err))

Opening product dialog

openProductDialog(productId: string): void

Opens Tipser product dialog for a product with given tipser product id.

Example:

tipser.openProductDialog("5a16534aa5af9b3af450c33d_5a16537da5af9b3af450c33f");

Opening purchase dialog

openPurchaseDialog(): void

Opens Tipser purchase (checkout) dialog with products that are currently in the shopping cart.

Getting cart size:

getCurrentCartSize(): Promise<number>;

Returns current Tipser cart size (including items added to cart from other shops). That value can change over time, so it may be necessary to call it repeatedly (e.g. on every page load or polling with a certain interval).

Returns:

A Promise returning current Tipser cart size.

Example:

tipser.getCurrentCartSize().then((cartSize) => {
   console.log("Tipser cart size is: ", cartSize);
   useCartSize(cartSize);
});

Direct to Checkout

By using the method Direct To Checkout, you add a product to the cart, at the same time as you open the checkout dialog window.

 openDirectToCheckoutDialog(productId: string, options: {posData?: string}): void

Adds the product with a given id to the shopping cart and then opens Tipser purchase (checkout) dialog with products that are currently in the shopping cart (including the newly added product).

productId: string, with the stucture described here.

options: currently the only supported option is posData, which is a string to be attached to a product order.

Opening search dialog

openSearchDialog(searchTerm: string): void

Opens Tipser product search dialog with using a given search term.

Opening collection dialog

openCollectionDialog(shopName: string, collectionName: string): void

Opens Tipser collection browsing dialog for a given Tipser shop name and collection name.

Closing Tipser dialog

closeDialog(): void

Closes the active Tipser dialog.

Listening to analytics events

addTrackEventListener(listener: (event) => void)
removeTrackEventListener(listener: (event) => void)

The listener callback is called every time payment analytics event (like product dialog opened or product added to cart) is fired by Tipser code.

Listening to dialog closed event

addDialogClosedListener(listener: (event) => void)
removeDialogClosedListener(listener: (event) => void)

The listener callback is called when every type of Tipser Dialog is closed (including "Thank You page" dialog - in this case, both dialogClosed and thankYouPageClosed listeners will be called).

Listening to thank you page closed event

addThankYouPageClosedListener(listener: (orderId: number) => void)
removeThankYouPageClosedListener(listener: (orderId: number) => void)

The listener callback is called when payment confirmation dialog (thank you page) is closed by the user. orderId of the order is passed as a callback argument.

Complete Tipser SDK example

var tipser = TipserSDK("5aa12d639d25800ff0e56fc5", {
   primaryColor: "yellow",
   modalUi: {
      hideSearchIcon: true,
      hideFavouritesIcon: true,
      hideCartIcon: true,
      hideMoreIcon: true,
      useCustomCss: true
   }
});
tipser.getCurrentCartSize().then((cartSize) => {
   console.log("cartSize = ", cartSize);
});
tipser.openProductDialog("5a16534aa5af9b3af450c33d_5a16537da5af9b3af450c33f");

tipser.addTrackEventListener(function(evt) {
    console.log("analytics event: ", evt);
});
tipser.addThankYouPageClosedListener(function() {
    console.log("thank you page closed event");
});

setTimeout(() => {
   tipser.closeDialog();
}, 5000);

The code snippet above initializes tipser SDK with custom primary color and some modal customizations, gets and prints the current cart size, opens a tipser dialog and then closes it after 5 seconds.

Configuration options

Both Tipser Elements and Tipser Script initialization functions accept configuration object as a second parameter.

Tipser Elements example:

const tipserScript = tipser.elements("59e86b79b8f3f60a94ecd26a", {primaryColor: "#222"});

Tipser SDK Example:

const tipserSdk = TipserSDK("59e86b79b8f3f60a94ecd26a", {primaryColor: "#222"});

The example connects Tipser SDK and sets primary color to #0000FF.

All the available configuaration options are:

const configurationOptions = {
  primaryColor: "#0000FF",
  env: "stage",
  lang: 'en-US',
  openOldDialog: false,
  addToCartSettings: {
      directToCheckoutMode: false,
  },
  useDeepLinking: false,
  customLabels: {
    buy: "buy now!",
    unavailable: "not available",
    add_to_cart: "add to shopping bag",
    choose_variant: "available variants",
    out_of_stock: "magazine supplies finished",
  },
  posData: {
    key: "value"
  },
  customUrls: {
    productBaseUrl: (productId) => `/product-page?productId=${productId}`,
    checkoutConfirmationUrl: "/checkout-confirmation",
    checkoutUrl: "/checkout",
  },
  prePopulatedAddress: {
      delivery: {
        firstName: 'John',
        lastName: 'Doe',
        email: 'youremail@email.com',
        zipCode: '12345',
        city: 'Ankeborg',
        street: 'Stårgatan 1',
        country: 'Sweden',
        state: '',
        phoneNumber: '0765260000'
      },
      billing: {
        firstName: 'Jane',
        lastName: 'Doe',
        email: 'billing@w.pl',
        zipCode: '12345',
        city: 'Ankeborgen',
        street: 'Stårgatan 1',
        country: 'Sweden',
        state: '',
      },
  },
  modalUi: {
    hideSearchIcon: true,
    hideFavouritesIcon: true,
    hideCartIcon: false,
    hideMoreIcon: true,
    hideSimilarProducts: false,
  },
  addToCartPopup: {
    className: "custom-className",
    duration: 5000;
  };
}

They will be described in the following sections.


Primary Color

If you'd like to unify our design with your own color-theme, you can use our primary color configuration option to change the color of buy buttons in Product and indicator of items number in Cart. You only need to make sure to use the right hex color code.

const tipserSdk = TipserSDK("59e86b79b8f3f60a94ecd26a", {primaryColor: "#5F9F9F"});


Environment

const tipser = tipser.elements("59e86b79b8f3f60a94ecd26a", {env: "stage"});

By default, Tipser Elements connect to production Tipser environment. Yet if testing environment is preferred (e.g. in order to do test purchase), then it can be customized with env parameter in Tipser options, which accept the following values:


Language and locale

lang configuration option specifies the language to be used. Supported languages are currently: en-US, en-GB, de-DE, de-DE-formal, fr-FR, fi-FI and sv-SE.

  tipser.elements('posId', {
    lang: 'en-US'
  })

It affects all the localizable texts in the UI - buy buttons, store, shopping cart and checkout. It does not affect the currency in which the customer will pay for the product.


Open Old Dialog

If you set this option to false, you will have an early access the new Product Dialog (after clicking product tile eg. in a collection), which is based entirely on Tipser Elements and not opened in an iframe. Therefore, it is much easier to style via CSS overwrite. New product dialog will allow some new features and better User Experience compared to the one used previously. Consequently, the old product dialog will be deprecated soon, which will be communicated ahead to every POS through an appropriate channels.


Direct to checkout mode

When theaddToCartSettings.directToCheckoutMode option is set to true, after clicking the buy button the user goes directly to checkout.


Add to cart popup

const tipserOptions={
  addToCartPopup: {
    className: "custom-className",
    duration: 5000;
  }
};

Deep linking

By default the Store component saves the active collection in the browser's URL hash part (everything after the # symbol in the URL). It means that your shopping page may be bookmarked by the user or shared with other users by sending a browser link (the same collection will be active in the Store when opening that link).

If you wish to turn this behaviour off (e.g. because it interferes with the routing system of your site), set the useDeepLinking parameter to false.


Custom Labels

If you want to override our default text with your own, you can do it via customLabels option. At the moment we allow to change following labels for buttons and product information:

const tipserOptions = {
    customLabels: {
      buy: "buy now!",
      unavailable: "not available",
      add_to_cart: "add to shopping bag",
      choose_variant: "available variants",
      out_of_stock: "magazine supplies finished",
    }
};

Example:


POS data

Go to this section to see the usage.


Custom Urls

To fully embed the Tipser Product Page or the Checkout on your site, please use these configuration options to determine the relative path directing to them.

const tipserOptions = {
    customUrls: {
       productBaseUrl: (productId) => `/product-page?productId=${productId}`,
       checkoutUrl: "/checkout",
       checkoutConfirmationUrl: "/checkout-confirmation",
    }
}

Prepopulated address data

If you would like to use your logged-in User's data to prepopulate address data of the payment provider, you can pass it on via Tipser config prePopulatedAddress option.

const tipserOptions = {
    prePopulatedAddress: {
      delivery: {
        firstName: 'John',
        lastName: 'Doe',
        email: 'youremail@email.com',
        zipCode: '12345',
        city: 'Ankeborg',
        street: 'Stårgatan 1',
        country: 'Sweden',
        state: '',
        phoneNumber: '0765260000'
      },
      billing: {
        firstName: 'Jane',
        lastName: 'Doe',
        email: 'youremail@email.com',
        zipCode: '',
        city: 'Ankeborgen',
        street: 'Stårgatan 1',
        country: 'Sweden',
        state: '',
      },
    },
}

To add the address data dynamically, please use the updateConfigfunction.

Parameters for old dialog customization

Sometimes Tipser dialogue styling and functionality needs to be customized. It is possible with modalUi parameters group.

Complete example of dialog customizations:

const tipserOptions = {
    modalUi: {
      hideSearchIcon: true,
      hideFavouritesIcon: true,
      hideCartIcon: false,
      hideMoreIcon: true,
      hideSimilarProducts: false,
      useCustomCss: true
    }
};

Hiding icons on menu bar

The following parameters under modalUi can be used to selectively hide tipser icons on the dialog menu bar:

hideSearchIcon
hideFavouritesIcon
hideCartIcon
hideMoreIcon

Hiding Similar Products Module

Similarly, hideSimilarProducts parameter, if set to true, can be used to hide Similar Products Module on product page

updateConfig()

To update any of the configuration options dynamically, please use the updateConfig() function.

window.elements = tipser.updateConfig({
  primaryColor: "#333333",
})

Analytics

You can connect to Tipser analytics by listening to the tw-track event on your page:

<script>
const analyticsHandler = function (e) {
  //put here your own code handling the event, example below
  const detail = e.detail; 
  console.log('Tipser analytics event: ', e);
  console.log('Action',detail.action); //e.g. 'Cart'
  console.log('Target',detail.target); //e.g. 'Product'
  console.log('Description',detail.description); //e.g. 'Product added to cart'
  console.log('Object',detail.object); //payload of the event (e.g. product id, price, etc)
}
document.addEventListener('tw-track', analyticsHandler);
</script>

Example events intercepted this way are:

For complete list of supported events refer to the List of supported interactions section.

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

Events structure

Each event passed to analyticsHandler 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:

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

    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;
      }
    };
    

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.

List of supported interactions

View collection

When a collection appears in the viewport.

detail object structure

{
  description: ‘Collection viewed’,
  action: 'View',
  target: 'List',
  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

Click product in collection

When a product in a collection is clicked.

detail object structure

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

Quick links to object structures: Product

View product details

It is emitted every time product tile is clicked and product dialog is displayed.

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 the Store

When the Tipser store appears in the viewport.

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 cart tab

When the shopping cart tab is clicked.

detail object structure

{
  description: 'Cart clicked',
  action: 'Click',
  target: 'Cart-tab',
  object: []
}

Add product to cart

When a product is added to a shopping cart.

detail object structure

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

Quick links to object structures: ProductLegacy

View cart - with purchase intent

When cart appears in the viewport, and getting payment in view.

detail object structure

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

Quick links to object structures: OrderedProductLegacy

Product purchased

When a product was bought (thank you page).

detail object structure

{
  description: ‘Product purchased’,
  action: 'Purchase',
  target: 'Order',
  object: [
    {
      OrderId: string, 
      Products: ProductLegacy[]
    }
  ]
}

Quick links to object structures: ProductLegacy

View cart

When cart icon appears in the viewport.

detail object structure

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

Objects structures

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

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

CategoriesLegacy structure

{

}[]

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

Image structure

{

  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.

Categories structure

  department: string,
  section: string,
  productType: string

Price structure

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

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:

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 (if necessary) by updating the value of disableAnalytics config to true (e.g. at the moment when the user declines the GDPR terms).

If you're using Tipser Script, you can dynamically update disableAnalytics value with the following line:

tipserScript.updateConfig({disableAnalytics: true});

If you're using Tipser Elements, 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>

Rest API

Tipser provides a RESTful API you can use to fully integrate your platform with Tipser.

You can find the full documentation here.