Native tallytoo button integration with javascript

You can natively integrate the tallytoo button into your website, giving you the maximum flexibility in terms of setting up your monetisation strategy.

Before you start, you will need to create a publisher account. To do so, you can follow this step-by-step guide

Latest version embed code:

Minified version:

<script src="https://code.app.tallytoo.com/tallybutton-latest.min.js" integrity="sha256-xoKu+wHkZGrTKzRqJAR4x8J9MHDGCa/I/eOjw1J1bIM=" crossorigin="anonymous"></script>

Uncompressed version:

<script src="https://code.app.tallytoo.com/tallybutton-latest.js" integrity="sha256-SiHiVoXJgo1O5a2tzLmbEXFcOEem24c/Rb7kSlU0QmI=" crossorigin="anonymous"></script>

Current version: 0.0.1

  • Minified:
      • Url: https://code.app.tallytoo.com/tallybutton-0.0.1.min.js
      • Integrity hash (SHA256): sha256-xoKu+wHkZGrTKzRqJAR4x8J9MHDGCa/I/eOjw1J1bIM=

  • Uncompressed:
      • Url: https://code.app.tallytoo.com/tallybutton-0.0.1.js
      • Integrity hash (SHA256): sha256-SiHiVoXJgo1O5a2tzLmbEXFcOEem24c/Rb7kSlU0QmI=

The following is the most basic example of including the tallybutton in your page:

<body>
  <div class="my_container">
    <!-- This empty element will serve as the parent for a newly created tallybutton -->
    <tallybutton content-id="2" cost="1" mode="pay" data-item=''></tallybutton>
  </div>

  <!-- Include the tallybutton script from the CDN -->
  <script src="https://code.app.tallytoo.com/tallybutton-latest.min.js" integrity="sha256-xoKu+wHkZGrTKzRqJAR4x8J9MHDGCa/I/eOjw1J1bIM=" crossorigin="anonymous"></script>

  <script> 
    // Initialise the tallybutton instance
    var tb = window.tallytoo.TallyButton.Create({ 
      apiKey: PUBLISHER_API_KEY,
      onSuccess: function(access_token, contentId, element) { 
        // Note: the following is not a real address, but shows how the access token
        // can be attached as a query parameter when reloading the page
        window.location.href = "https://news.nguni.fr/posts/" + contentId " + "?token=" + access_token;
      } 
    }); 

    // Call the load button when your page is ready. 
    // We recommend after page load so as not to slow your page load times.
    window.addEventListener("load", 
      function()
      { 
        tb.Load();
      }
    );
  </script>
</body>

The first thing you need to do after including the tallybutton script into your page is to initialise the tallybutton instance. The script will attach the object tallytoo to the window object.

The minimum configuration is as follows:

<script>
  var tb = window.tallytoo.TallyButton.Create(
    {
      apiKey: PUBLISHER_API_KEY,
      onSuccess: function(access_token, contentId, element) { ... }
    }
  );
</script>

The value PUBLISHER_API_KEY must be one of the api keys that you generate via the tallytoo publisher portal.

The onSuccess callback is a function that will be invoked by the tallybutton when the user spends points on your website. More on this later.

The Create function is executed immediately and does not invoke any asynchronous communications, so it will not slow page load.

The configuration object can contain a number of optional values, changing the behaviour of the tallybuttons on the page:

  • apiKey: The api key copied from the tallytoo publisher portal
  • onSuccess: a function callback with three parameters:
    • access_token: the JWT token granting access to the content
    • contentId: the unique id of the content to grant access to
    • element: the original html element to which you attached the tallybutton
  • popoverZ: The z-index of the tallytoo ad overlay. This is useful to make sure that the tallytoo overlay appears above all other content on your page.
  • alwaysPopup: By default, the tallytoo overlay will appear as an iframe above your content on desktops, and as a popup on mobiles (width less than 768 pixels). However, you can force the overlay to appear as a popup by setting this value to true.
  • popupMaxWidth: The width under which a popup will always display (default: 768 pixels)
  • searchCriteria: (default: “tag”) When the Load function is called, the module will find all elements on the page matching a certain criterion, and create a tallybutton iframe as a direct child. By default the plugin tries to find <tallybutton> tags. Setting this option to “class” will change the search to look for all elements with the class “tallybutton”.
  • searchValue: (default: “tallybutton”) Whether the search criteria is “tag” or “class”, in both cases the default value to search for is “tallybutton”. You can specify a different value here.
  • allowFreeAccess: (default: false) If set to true, in cases where no monetisation option is available for the user, the onSuccess callback will be called with a special JWT token permitting free access.
  • onContentAlreadyBought: callback that is called just after tallytoo button initialisation to signal that the user has already bought this content. Useful for removing your paywall and going directly to the content, for example. Two parameters are passed:
    • contentId: the unique id of the content to grant access to
    • element: the original html element to which you attached the tallybutton
  • onResized: The tallytoo button will automatically fill the space given to it. However, sometimes it cannot fully expand to the full height provided. This callback is called when the button is resized, passing the parameters:
    • height: the height of the visible tallytoo button
    • element: the original html element to which you attached the tallytoo button
  • display: a configuration object that will customise the appearance of the tallytoo button. The object has the following fields:
    • backgroundColor: the color of the main button (can be a html color name or a HEX value starting with #)
    • backgroundHoverColor: the color of the main button when the mouse is hovering
    • backgroundActiveColor: the color of the main button when the mouse is clicked
    • titleColor: the color of the first line of text
    • subtitleColor: the color of the second line of text
    • socialBackgroundColor: the background color of the footer band indicating how many others used the button.
    • socialColor: the text color of the footer band indicating how many others used the button.
    • socialBorderColor: the border color of the footer band (if borders are enabled)
    • linkColor: the color of the text in the links band
    • border: (true or false) If true, borders will be activated
    • borderRadius: the radius of the border, in pixels
    • borderWidth: the width of the border line, in pixels
    • borderColor: the color of the border line

The following example shows the tallybutton embed in a webpage

<body>
  <div class="container">
    <div class="row mb-4">
      <div class="col-md-4">
        <tallybutton content-id="2" cost="1" mode="pay"></tallybutton>
      </div>
    </div>
  </div>
</body>

The tallybutton element must have three obligatory elements:

  1. content-id: this is a unique ID that you use to identify the content being protected by the tallytoo button. This ID will appear in subsequent reporting, and also allows tallytoo to authorise subsequent access to this article if the user has already “purchased” it using tallytoo points.
  2. cost: this is the number of points you wish to charge for the content.
  3. mode: [pay (default) | donate] This is the mode of the tallytoo button. If “donate” is specified, the texts associated with a donation will be used.

Remember, adding this tag to your page does nothing until the Load function is called, at which time the tallybutton will be created automatically.

The Load function performs a number of asynchronous operations:

  1. Your API key is validated against the domain of the webpage. Be sure to list the correct domain in the publisher control panel.
  2. A primary invisible iframe is created on the tallytoo.com domain, allowing us to identify the user and recover the number of points they have already earned.
  3. The DOM is scanned for all <tallybutton> elements and register and create a new tallybutton as a direct child of each <tallybutton> found.

Since this process requires a certain amount of data exchange with our server, and the creation of multiple iframes in your page, we recommend that you call Load after the initial load of your page:

<script> 
  window.addEventListener("load", 
   function()
   { 
     tb.Load();
   }
  );
</script>

When the user clicks and spends a point on your site, the onSuccess javascript callback is invoked by the tallybutton, passing an access token generated by tallytoo for the particular item of content.

var success = function(access_token, contentId, element) {
  console.log("Authorised for content: ", contentId);
  console.log("Token: ", access_token);

  // Do something with the token...
}

This access token is only valid for a very short period of time, and is signed with tallytoo’s private key using the RS256 algorithm.

You can decide how to respond to this event.

In the case where you are unlocking content in response to a point spend, your page can react in multiple different ways, according to the architecture of your website:

  • Reload the page, attaching the access token as a query parameter (or cookie). The server can then extract the token, verify it, and serve the full content.
  • Load the missing content using AJAX, attaching the token as a parameter in the request. Your server can validate the token and respond with the additional content.

Here is an example of a token payload:

{
 "sub": "3",
 "audience": "https://news.nguni.fr",
 "points": 1,
 "motive": "purchase",
 "iat": 1539939831,
 "exp": 1539939951,
 "iss": "tallytoo"
}

The fields are as follows:

  • sub: The content-id to which this token refers, granting access
  • audience: The domain on which this token was generated, your website domain
  • points: The number of points spent by the user to access this content
  • motive: The reason for the issue of this token. Can be:
    • purchase: a successful purchase was performed. You will received the monetary equivalent at the end of the period.
    • revisit: your user is access content that he/she has already paid for
    • free: tallytoo granted free access because no other monetisation solution exists (and the allowFreeAccess parameter was set to true)
  • iss: will always be “tallytoo”

It is also common practice to attach a data-item field to the tallybutton element that you may use to attach additional information concerning the article or the transaction.

For the most security, you should validate the token you receive via the onSuccess callback. Since this token is signed by the tallytoo private key, you can verify that it was not generated by a third party.

To validate the token, you will need the tallytoo public key, which is available at https://app.tallytoo.com/api/v1/tallybutton/public_key

Validating with PHP

We recommend using the php-jwt package from Firebase. The following sample code illustrates how the token could be validated.

<?php
  use \Firebase\JWT\JWT;
  
  // Fetch the public key from the above URL
  $publicKey = file_get_contents("https://app.tallytoo.com/api/v1/tallybutton/public_key")

  function have_access($token){
    $have_access = false;
    try {
      // Decode the JWT 
      $decoded = JWT::decode($token, $publicKey, array('RS256')); 
      $decoded_array = (array) $decoded;

      // Validate the JWT payload items.
      // Here we compare 'sub' with the post ID currently being loaded, to make sure that the token 
      // is indeed for the article being accessed. 
      if ( $decoded_array['sub'] == $post->ID && $decoded_array['iss'] == 'tallytoo') { 
        $have_access = true; 
      } else { 
        throw new Exception("Sub or iss were not valid in the token"); 
      }
    } catch (Exception $e) { 
      $have_access = false; 
    }

    return $have_access;
  }
?>

Validating with javascript

The jsonwebtoken is an excellent way to validate tokens in the nodejs environment.

var jwt = require('jsonwebtoken');

var public_key = ... // Fetch the public key from the above URL

jwt.verify(token, public_key,   
  (err, decoded) => { 
    if (err) {  
      // Token was not valid... throw error 
    } else {   
      // Token was valid, "decoded" is an object that contains the payload 
    }  
  }
);