TwainSport Iframe integration

From BetgamesTV
Jump to navigation Jump to search

Using integration script

Add this block of JavaScript code to your page and change the script parameters.

  1. Load the script tag from `<CLIENT_URL>/public/ts-loader.js`;
  2. Add timestamp to invalidate any cache: `<CLIENT_URL>/public/ts-loader.js?${Date.now()}`;

Using inside the iframe

<div id="<IFRAME_HTML_CONTAINER_ID>"></div>
<script>
    const clientUrl = '<CLIENT_URL>';
    const script = document.createElement('script');
 
    script.src = `${clientUrl}/public/ts-loader.js?${Date.now()}`;
    script.onload = () => {
        window.TwainSports.setup({
            containerElement: document.getElementById('<IFRAME_HTML_CONTAINER_ID>'),
            clientUrl: '<CLIENT_URL>',
            apiUrl: '<API_URL>',
            wsUrl: '<WS_URL>',
            partnerCode: '<PARTNER_CODE>',
            token: '<TOKEN>',
            timezone: '<TIMEZONE>',
            locale: '<LOCALE>',
            oddsFormat: '<ODDS_FORMAT>',
            showBalance: '<SHOW_BALANCE>',
            showLogo: '<SHOW_LOGO>',
            theme: '<THEME>',
            gameId:'<GAME_ID>',
            gui: '<GUI>',
            dynamicHeight: '<DYNAMIC_HEIGHT>',
            platformID: '<PLATFORM_ID>',
        });
    };
 
    document.head.appendChild(script);
</script>
  • <CONTAINER_ELEMENT>: should be valid HTML node `document.getElementById('twainsport-iframe')` or valid CSS selector `#twainsport_iframe`;
  • <CLIENT_URL>: should be used the same one from which the script was fetched;


Opening in new tab

<script>
    const clientUrl = '<CLIENT_URL>';
    const script = document.createElement('script');
 
    script.src = `${clientUrl}/public/ts-loader.js?${Date.now()}`;
 
    document.head.appendChild(script);
 
    document.getElementById('<TWAIN_SPORT_BUTTON>').addEventListener('click', (e) => {
        e.preventDefault();
 
        window.TwainSports.setup({
            containerElement: '<CONTAINER_ELEMENT>',
            clientUrl: '<CLIENT_URL>',
            apiUrl: '<API_URL>',
            wsUrl: '<WS_URL>',
            partnerCode: '<PARTNER_CODE>',
            token: '<TOKEN>',
            timezone: '<TIMEZONE>',
            locale: '<LOCALE>',
            oddsFormat: '<ODDS_FORMAT>',
            showBalance: '<SHOW_BALANCE>',
            showLogo: '<SHOW_LOGO>',
            theme: '<THEME>',
            gameId:'<GAME_ID>',
            gui: '<GUI>',
            dynamicHeight: '<DYNAMIC_HEIGHT>',
            platformID: '<PLATFORM_ID>',
            integration: 'tab', // <-- Just provide additional parameter `integration: 'tab'`
        });
    });
</script>
  • <CONTAINER_ELEMENT>: should be valid HTML node `document.getElementById('twainsport-iframe')` or valid CSS selector `#twainsport_iframe`;
  • <CLIENT_URL>: should be used the same one from which the script was fetched;


Direct integration via URL

Instead of using integration script, another option is to integrate using directly the URL of TwainSports.

<!DOCTYPE html>
<html>
<head>
    ...
</head>
<body>
    <div id="iframe-content" style="margin: 0 auto; padding-bottom: 100px">
        <iframe
            frameborder="0"
            allow="autoplay; fullscreen; encrypted-media; clipboard-write self <CLIENT_URL>"
            scrolling="no"
            src="<CLIENT_URL>/#/?apiUrl=<API_URL>&wsUrl=<WS_URL>&partnerCode=<PARTNER_CODE>&token=<TOKEN>&timezone=<TIMEZONE>&locale=<LOCALE>&oddsFormat=<ODDS_FORMAT>&showBalance=<SHOW_BALANCE>&showLogo=<SHOW_LOGO>&theme=<THEME>&gameId=<GAME_ID>&gui=<GUI>&dynamicHeight=<DYNAMIC_HEIGHT>"
            style="min-width: 100%; height: 1410px;">
        </iframe>
    </div>
</body>
</html>

Environments

Environment CLIENT_URL API_URL WS_URL
Integrations / Testing https://integrations-ag-iframe.betgames.tv https://integrations-ag-bp.betgames.tv https://integrations-ag-ws.betgames.tv
Production https://web.twainsports.com https://yrouw9o6.twainsports.com https://ws.twainsports.com

Parameters

Parameter Is required Description Default value
clientUrl Yes Url of iFrame client application. Provided by the Betgames Integration team.
partnerCode Yes Provided by the Betgames Integration team.
token Yes Generated token for the authorized player, for non-loged player ' - ' should be provided.
apiUrl Yes Provided by the BetGames Integration team. https://integrations-ag-bp.betgames.tv
wsUrl Yes Provided by the BetGames Integration team. https://integrations-ag-ws.betgames.tv
locale No A list of supported languages can be found - https://wiki.betgames.tv/index.php?title=List_of_supported_languages en
gameId No Used to open specific games during the launch instead of the lobby.

Values:

  • 24 - T-Kick 24/7;
  • 25 - T-Basket 24/7;
timezone Yes Timezone UTC.

Values: any numerical value in the range from -12 to 14.

0
oddsFormat No Used to show add values in different formats.

Values:

  • decimal;
  • american;
  • fractional;
  • hongkong;
decimal
showBalance No Used to show, or hide balance in the top header.

Values:

  • 0 (false - not show it);
  • 1 (true - show it);
1
showLogo No Used to show, or hide logo in the top header.

Values:

  • 0 (false - not show it);
  • 1 (true - show it);
1
theme No Used for colors customization.

Values:

  • default;
  • dark;
  • light;
default
dynamicHeight No Used to disable some functionality that doesn't work when we don't have access to the scroll and enables the listener for height update via post messages.
  • 0 - (false - static iframe height);
  • 1 - (true - enabled dynamic height);
0
height No Used to set the height of iframe in case we have dynamicHeight=0. 100%
integration No Integration type. Embedded with <iframe> element or in new window (tab).

Values:

  • embedded;
  • tab;
0
homeUrl No Used to redirect the player to the partner home page when the player decides to leave TwainSport. The logout/home button is only visible on mobile resolutions.
gui No Used to show Graphical User Interface (GUI) by default.

Values:

  • 0 (false - video mode);
  • 1 (true);
0
platformID No Used to send platformID when placing bet. Values can be from 0 to any number according to partner customization.

Example:

  • 0 (desktop);
  • 1 (mobile);
  • 2 (android);
  • 3 (ios);
0
customMethods No Used when partner uses our ts-loader.js file, but wants to skip postMessages initialisation from it.

For ex. he has its own messaging methods and uses iframe inside iframe. In that case they should attach all eventListener to the top window (which has scroll)

0

Messaging

For communication between parent and iframe, we're using window.postMessage.

Events

emit - emits message from iframe to partner page.
receive - sends message from partner page to iframe.

Event name Type Payload Description REQUIRED
realityCheck emit
{
    "windowName": "twainsport_iframe",
    "type": "realityCheck"
}
Sending each time user clicks place bet button. YES
newHeight emit
{
    "windowName": "twainsport_iframe",
    "type": "newHeight",
    "payload": {
        "height": 500
    }
}
  • height 500 is the min value.
iFrame does not contain a scroll this is because it needs to be updated by a parent.

This event will be sent:

  • Each time when iFrame detects an update of the content height (Sending 5 times with 300ms throttling to have smoother height updates).
YES (only when `dynamicHeight` is enabled)
balanceCheck emit
{
    "windowName": "twainsport_iframe",
    "type": "balanceCheck"
}
This event will be sent:
  • Each successful pay-in;
  • After winning;
YES
scrollTo emit
{
    "windowName": "twainsport_iframe",
    "type": "scrollTo",
    "payload": {
        "scrollTo": 34
    }
}
Sending when iframe needs to update partners page scroll position. YES (only when `dynamicHeight` is enabled)
iframeTop emit
{
    "windowName": "twainsport_iframe",
    "type": "iframeTop",
    "payload": {
        "topOffset": 0
    }
}
  • topOffset 0 is the default value.
Sending when iframe needs to scroll parent pages to the top.

topOffset is used to additional add a top scroll when scrolling the parent page to the top.
YES (only when `dynamicHeight` is enabled)
getViewPort emit
{
    "windowName": "twainsport_iframe",
    "type": "getViewPort"
}
Sending request to get view port dimensions from partner page. YES (only when `dynamicHeight` is enabled)
getViewPort receive
{
    "type": "getViewPort",
    "payload": {
        "width": window.innerWidth,
        "height": window.innerHeight,
        "iFrameTopOffset": iFrame.offsetTop,
        "scrollY": window.scrollY
    }
}
Sending request to get view port dimensions from partner page. YES (only when `dynamicHeight` is enabled)
onScroll emit
{
    "type": "onScroll",
    "payload": {
        "height": 1000,
        "scrollY": 0,
        "iFrameTopOffset": 100
    }
}
Sending from partner's page on each window scroll event after receiving 'subscribeScroll' event. Example below YES (only when `dynamicHeight` is enabled)
subscribeScroll receive
{
    "windowName": "twainsport_iframe",
    "type": "subscribeScroll"
}
Sending to partner to subscribe to window.scroll events and start sending 'onScroll' events YES (only when `dynamicHeight` is enabled)
unsubscribeScroll receive
{
    "windowName": "twainsport_iframe",
    "type": "unsubscribeScroll"
}
Sending to partner to unsubscribe from window.scroll events and stop sending `onScroll` events YES (only when `dynamicHeight` is enabled)

Receiving messages

Code below could be inserted into the browser console to see incoming post messages

<script>
     window.addEventListener('message', (message) => {
        try {
            const parsedData = JSON.parse(message.data);
            console.log(parsedData);
        } catch (e) {}
    });
</script>

"onScroll" implementation

<script>
    const iFrame = document.getElementById('twainsport_iframe');
 
    const scrollSubscribe = () => {
        iFrame.contentWindow.postMessage(
            JSON.stringify({
                type: 'onScroll',
                payload: {
                    height: window.innerHeight,
                    scrollY: window.scrollY,
                    iFrameTopOffset: iFrame.offsetTop,
                },
            }),
            '*',
        );
    };
 
    window.addEventListener('message', (message) => {
        try {
            const parsedData = JSON.parse(message.data);
  
            switch (parsedData.type) {
                case 'subscribeScroll': {
                    window.addEventListener('scroll', scrollSubscribe);
                    break;
                }
 
                case 'unsubscribeScroll': {
                    window.removeEventListener('scroll', scrollSubscribe);
                    break;
                }
            }
        } catch (e) {}
    });
</script>

Examples

For partners without ts-loader.js implementation

<script>
    const iFrame = document.getElementById('twainsport_iframe');
 
    const scrollSubscribe = () => {
        iFrame.contentWindow.postMessage(
            JSON.stringify({
                type: 'onScroll',
                payload: {
                    height: window.innerHeight,
                    scrollY: window.scrollY,
                    iFrameTopOffset: iFrame.offsetTop,
                },
            }),
            '*',
        );
    };
 
    window.addEventListener('message', (message) => {
        try {
            const parsedData = JSON.parse(message.data);
 
            switch (parsedData.type) {
                case 'subscribeScroll': {
                    window.addEventListener('scroll', scrollSubscribe);
                    break;
                }
 
                case 'unsubscribeScroll': {
                    window.removeEventListener('scroll', scrollSubscribe);
                    break;
                }
 
                case 'newHeight': {
                    iFrame.style.height = `${parsedData.payload.height}px`;
                    break;
                }
 
                case 'getViewPort': {
                    iFrame.contentWindow.postMessage(
                        JSON.stringify({
                            type: 'getViewPort',
                            payload: {
                                width: window.innerWidth,
                                height: window.innerHeight,
                                iFrameTopOffset: iFrame.offsetTop,
                                scrollY: window.scrollY,
                            },
                        }),
                        '*',
                    );
                    break;
                }
 
                case 'scrollTo': {
                    window.scroll(0, Number(parsedData.payload.scrollTo));
                    break;
                }
 
                case 'iframeTop': {
                    window.scroll(0, Number(message.payload.topOffset) + iFrame.offsetTop);
                    break;
                }
            }
        } catch (e) {}
    });
</script>

For partners with iframe inside iframe without "ts-loader.js" implementation

<!-- inner-iframe.html -->
 
<!DOCTYPE html>
<html>
<head>
    <!-- ... -->
    <style>
        html,
        body {
            height: 100%;
        }
 
        html {
            overflow: hidden;
        }
 
        body {
            margin: 0;
        }
    </style>
</head>
<body>
    <iframe
        name="twainsport_iframe"
        allow="autoplay; fullscreen; encrypted-media; clipboard-write self <CLIENT_URL>"
        src="<CLIENT_URL>/#/?apiUrl=<API_URL>&wsUrl=<WS_URL>&partnerCode=<PARTNER_CODE>&token=<TOKEN>&timezone=<TIMEZONE>&locale=<LOCALE>&oddsFormat=<ODDS_FORMAT>&showBalance=<SHOW_BALANCE>&showLogo=<SHOW_LOGO>&theme=<THEME>&gameId=<GAME_ID>&gui=<GUI>&dynamicHeight=<DYNAMIC_HEIGHT>"
        style="
            width: 100%;
            height: 100%;
            min-height: 500px;
            border: 0;
        ">
    </iframe>
</body>
</html>
 
 
 
<!-- partner-page.html -->
 
<!DOCTYPE html>
<html>
<head>
    <!-- ... -->
    <script>
        const innerIFrame = document.getElementById('inner_iframe');
 
        const scrollSubscribe = () => {
            innerIFrame.contentWindow.frames['twainsport_iframe'].postMessage(
                JSON.stringify({
                    type: 'onScroll',
                    payload: {
                        height: window.innerHeight,
                        scrollY: window.scrollY,
                        iFrameTopOffset: innerIFrame.offsetTop,
                    },
                }),
                '*',
            );
        };
 
        window.addEventListener('message', (message) => {
            try {
                const parsedData = JSON.parse(message.data);
 
                switch (parsedData.type) {
                    case 'subscribeScroll': {
                        window.addEventListener('scroll', scrollSubscribe);
                        break;
                    }
 
                    case 'unsubscribeScroll': {
                        window.removeEventListener('scroll', scrollSubscribe);
                        break;
                    }
 
                    case 'newHeight': {
                        innerIFrame.style.height = `${parsedData.payload.height}px`;
                        break;
                    }
 
                    case 'getViewPort': {
                        innerIFrame.contentWindow.frames['twainsport_iframe'].postMessage(
                            JSON.stringify({
                                type: 'getViewPort',
                                payload: {
                                    width: window.innerWidth,
                                    height: window.innerHeight,
                                    iFrameTopOffset: innerIFrame.offsetTop,
                                    scrollY: window.scrollY,
                                },
                            }),
                            '*',
                        );
                        break;
                    }
 
                    case 'scrollTo': {
                        window.scroll(0, Number(parsedData.payload.scrollTo));
                        break;
                    }
 
                    case 'iframeTop': {
                        window.scroll(0, Number(message.payload.topOffset) + innerIFrame.offsetTop);
                        break;
                    }
                }
            } catch (e) {}
        });
    </script>
</head>
<body>
    <iframe
        id="inner_iframe"
        src="<URL_TO_INNER_HTML>"
        style="
            width: 100%;
            height: 100%;
            min-height: 500px;
            border: 0;
        ">
    </iframe>
</body>
</html>

For partners with iframe inside iframe with "ts-loader.js" implementation

<!-- inner-iframe.html -->
 
<!DOCTYPE html>
<html>
<head>
    <!-- ... -->
    <style>
        html,
        body {
            height: 100%;
        }
 
        html {
            overflow: hidden;
        }
 
        body {
            margin: 0;
        }
    </style>
</head>
<body>
    <div id="<IFRAME_HTML_CONTAINER_ID>"></div>
    <script>
        const clientUrl = '<CLIENT_URL>';
        const script = document.createElement('script');
 
        script.src = `${clientUrl}/public/ts-loader.js?${Date.now()}`;
        script.onload = () => {
            window.TwainSports.setup({
                containerElement: document.getElementById('<IFRAME_HTML_CONTAINER_ID>'),
                clientUrl: '<CLIENT_URL>',
                apiUrl: '<API_URL>',
                wsUrl: '<WS_URL>',
                partnerCode: '<PARTNER_CODE>',
                token: '<TOKEN>',
                timezone: '<TIMEZONE>',
                locale: '<LOCALE>',
                oddsFormat: '<ODDS_FORMAT>',
                showBalance: '<SHOW_BALANCE>',
                showLogo: '<SHOW_LOGO>',
                theme: '<THEME>',
                gameId:'<GAME_ID>',
                gui: '<GUI>',
                dynamicHeight: '<DYNAMIC_HEIGHT>',
                customMethods: 1, // <-- "customMethods" disables default post messaging methods from loader. Needs to be implemented in "partner-page.html"
            });
        };
 
        document.head.appendChild(script);
    </script>
</body>
</html>
 
 
 
<!-- partner-page.html -->
 
<!DOCTYPE html>
<html>
<head>
    <!-- ... -->
    <script>
        const innerIFrame = document.getElementById('inner_iframe');
 
        const scrollSubscribe = () => {
            innerIFrame.contentWindow.frames['twainsport_iframe'].postMessage(
                JSON.stringify({
                    type: 'onScroll',
                    payload: {
                        height: window.innerHeight,
                        scrollY: window.scrollY,
                        iFrameTopOffset: innerIFrame.offsetTop,
                    },
                }),
                '*',
            );
        };
 
        window.addEventListener('message', (message) => {
            try {
                const parsedData = JSON.parse(message.data);
 
                switch (parsedData.type) {
                    case 'subscribeScroll': {
                        window.addEventListener('scroll', scrollSubscribe);
                        break;
                    }
 
                    case 'unsubscribeScroll': {
                        window.removeEventListener('scroll', scrollSubscribe);
                        break;
                    }
 
                    case 'newHeight': {
                        innerIFrame.style.height = `${parsedData.payload.height}px`;
                        break;
                    }
 
                    case 'getViewPort': {
                        innerIFrame.contentWindow.frames['twainsport_iframe'].postMessage(
                            JSON.stringify({
                                type: 'getViewPort',
                                payload: {
                                    width: window.innerWidth,
                                    height: window.innerHeight,
                                    iFrameTopOffset: innerIFrame.offsetTop,
                                    scrollY: window.scrollY,
                                },
                            }),
                            '*',
                        );
                        break;
                    }
 
                    case 'scrollTo': {
                        window.scroll(0, Number(parsedData.payload.scrollTo));
                        break;
                    }
 
                    case 'iframeTop': {
                        window.scroll(0, Number(message.payload.topOffset) + innerIFrame.offsetTop);
                        break;
                    }
                }
            } catch (e) {}
        });
    </script>
</head>
<body>
    <iframe
        id="inner_iframe"
        src="<URL_TO_INNER_HTML>"
        style="
            width: 100%;
            height: 100%;
            min-height: 500px;
            border: 0;
        ">
    </iframe>
</body>
</html>

Custom CSS

To customize the look of the games, you need to edit the value of the predefined CSS variables.
List of possible variables with example values:

:root {
    /* Main */
    --primary: ...;
    --secondary: ...;
    --secondary-light: ...;
    --error: ...;
    --success: ...;

    /* Text */
    --text-light: ...;
    --text-dark: ...;
    --text: ...;
    --text-neutral: ...;
    --text-neutral-light: ...;

    /* Font family */
    --font-main: ...;
    --font-accent: ...;

    /* Generic elements */
    --input: ...;
    --input-border: ...;
    --border: ...;
    --page-background: ...;
    --entity-background: ...;
    --entity-item-background: ...;
    --entity-item-border: ...;

    /* Elements */
    --participant-row: ...;
    --participant-row-active: ...;
    --participant-info-box: ...;
    --color-chart-gradient: ...;
    --video-elements-background: ...;
    --scroll-to-top-size: ...;
    --scroll-to-top-bottom-offset: ...;
    --scroll-to-top-right-offset: ...;
}