Advanced Concepts

Getting Tipser ids

📘

To run Tipser Elements components you will need to pass productId or collectionId. To get productId, log in to app.tipser.com using your publisher's account, find a product in "Insert Products" section, click on the "<>" sign and copy it's productId.
To curate a collection click "+" sign on a product tile and type the name of the collection. Then click "add". Your product is added to the collection. When all the products are added to the collection, click "<" on the right side of the site and then "<>" to copy the the collectionId.

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).

📘

Because posData is treated as a string in the Tipser system, then if you need to store a structured data (a common use case), please call JSON.stringify() function on a JS object before passing it to Tipser (see: the examples below) and parse it back to JS object when receiving it back.

Setting up posData for the upcoming add to carts

There are two ways to setup posData. All the upcoming add to cart requests will use that posData value but this will not affect the posData for the items already added to the cart.

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: By dynamically updating the elements config (useful for
the data that is not yet available at the time of initialization):

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

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

Setting up posData for the existing items in the cart

In case the pos data needs to be updated for the items already in the cart, use the updatePosData internal functions.

Basic example:

const PosDataUpdater = () => {
  const { updatePosData } = useInternalFunctions();
  const onClick = () => {
    updatePosData('pos data for all items in the cart');
  };
  return <button onClick={onClick}>Update pos data</button>;
};

The above call will set or update the posData for all the items currently in the cart.

Advanced example:

const PosDataUpdater = () => {
  const { updatePosData } = useInternalFunctions();
  const onClick = () => {
    updatePosData({ ['603fc1f55d24f81b603fa44c_603fc1f55d24f81b603fa44c']: 'product-specific pos data' });
  };
  return <button onClick={onClick}>Update pos data</button>;
};

The above call will set or update the posData the specific item in the cart, leaving the posData of the remaining items intact.

❗️

Warning: for performance reasons, the number of characters in posData is limited to 4000. Longer strings will be truncated down to 4000 characters.

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.

To do that, use 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>
}

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:

  • building a state containing the data needed for rendering

  • passing the state to the SsrTipserElementsProvider

  • rendering the application

  • sending the same state to the browser and passing it back to the SsrTipserElementsProvider on the client side.

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 Script or Tipser Elements sections, depending on your technology of choice.

The only customization that we recommend is using customUrls.productUrl configuration, 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: customUrls.productUrl to a URL containing your product page to 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

📘

Here we describe displaying a single product. If you'd be interested to render multiple products, it's best to use the collections API.

Step 2: 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)

📘

If your use case is not covered in this section, please discuss it with your Tipser integration support person.

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