Sign in

Web

This document will guide you on your way to integrate SCILL into you web site or web app. Of course, this will also apply to all web based native apps created with Electron, Cordova and RectNative.

In this guide we will only briefly describe each step. If things are unclear, then please have a look at Getting started with SCILL where we describe each step in more detail.

Let’s get started!

Adding SCILL to the backend

What you want, is challenges that track progress in real time. The basic concept of a challenge is pretty simple: You set a goal and ask the user to reach that goal within a certain period. Goals must be measurable with one simple integer. Every active challenge has a current state which is also an integer. A simple challenge for example is: “Invite 5 other users”. The goal here is 5, the current state will be 0 when the challenge is activated. After the user invited one other user, the current state will be 1.

It’s important to send events from your backend - i.e. a server or cloud functions. You should not send challenge progress from your client, because this can easily be decoded by any developer experienced with the Web console and debugging JavaScript. As your business logic most likely will be integrated in some sort of server, this should not be an issue at all in most circumstances.

Please note

The example code provided in this guide already contains our public demo API-Key that you can use to get started. The SCILL team has preconfigured some basic challenges that are used in this guide to get you started quickly. At any time you can sign up for free to create your own products, API keys and customize your challenges.

NodeJS

If your backend runs on NodeJS you can integrate sending events like this:

Step 1: Install SDK

npm install @scillgame/scill-js --save

You will now have installed our NPM module and can use it in your NodeJS based code. Please upgrade the package regularly to get updates and bug fixes.

Step 2: Initialize module

Add this to the head of your NodeJS code or cloud function.

const scill = require('@scillgame/scill-js');

Congratulations! You have configured a SCILL class that can be used to send events and generate access tokens. Refer to the API-Reference to learn what you can do with it.

Step 3: Send events

Now all you have to do is send an event once a user has accepted an invitation. You will most likely have some console.log outputs in your code to indicate that certain things happened. An event is basically the same. A structured log entry is sent to the SCILL cloud.

Have a look at the Events API Reference to learn more about this function. In this step we just briefly go through this step by step.

In your business logic you will have a reference to your user. This can be anything identifying your user. It can be a primary key in your relational database, a collection key in your NoSQL database or an id of an auth provider. Please don’t use personal data like email adresses as a user id!. You should even think about creating a special user id just for SCILL.

const userId = getUserId();
const sessionId = new Date().getTime();

Another thing is the session id. Some events require a session id in order to group events. Think of a match in tennis. Then the session id can be used to group all events to a match or session. This is up to you how to manage those. In this basic example of user invites we just create a random number (based on time).

Next, you send an event of type user-invite via the sendEvent function. Last but not least, you can add a metadata object which is used to send additional data - like in this case how many users have been invited - that depends on the event type and is fully optional.

// Get an instance of the events API with our test API key
const eventsApi = scill.getEventsApi("ai728S-1aSdgb9GP#R]Po[P!1Z(HSSTpdULDMUAlYX");
// Send user invite event
eventsApi.sendEvent({
    event_name: "user-invite",
    event_type: "single",
    user_id: userId,
    session_id: sessionId,
    meta_data: {
        amount: 1
    }
}).then(result => {
    console.log("Sending event: ", result);
}).catch(error => {
    console.warn("Failed to send event", error);
});

That’s it for the backend side of things. Whenever a user accepts an invite, a SCILL event is sent to the SCILL cloud that refers your user with userId and is of type user-invite.

Step 4: Getting access token

Please note

This step is only required if your user facing code is running in an unsecure client environment like a game, an iOS or Android app or a JavaScript based client web application (SPA with Angular, React, Vue, …). If your web site is created on the server side - i.e. with PHP you don’t need to create an access token. In this case, just use your API key (or the API key provided in this example).

You don’t want to expose User IDs and/or the API-key in unsecure environments as this would allow anyone with some basic knowledge of the browsers web console to extract those and being able to send requests without your permission. To secure this, you create an access token for each user for each client session.

In your NodeJS backend you need to provide a webservice that your client can call to get an access token. You will most likely already have some sort of webservice so this is just a very basic example code based on a simple express server.

// This example implements a NodeJS backend receiving sessions from your client and asking SCILL
// to generate an access token for the userId "encoded" in/via the session.

const express = require('express');
const bodyParser = require('body-parser');
const scill = require('scill');

// Generate an instance of the SCILL SDK with example API-Key
const auth = scill.getAuthApi('ai728S-1aSdgb9GP#R]Po[P!1Z(HSSTpdULDMUAlYX');

const app = express();
const port = 80;

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

app.get('/scill/generate-access-token', (req, res) => {
    // In this example we use a session sent to this endpoint from the client to extract a user id from the session
    // How you implement that is up to you and depends on your user system
    const session = req.body.session;
    const userId = '1234';

    // Call SCILL backend to generate an access token encoding user and API-Key
    return auth.generateAccessToken({
        userId: userId
    }).then(accessToken => {
        // return access token
        return res.send(accessToken);
    });
});

app.listen(port, () => {
    console.log('Example app listening at http://localhost:${port}');
});

The SCILL JavaScript SDK function generateAccessToken is used to generate an access token based on the user id you are providing. What happens here is that SCILL receives your API-Key together with the user id and then creates as well as signs a JWT token.

More info on this topic can be found in the Authentication document.

Please note: A cheater/hacker only needs to get his hands on userid and your api key to send events “on your behalf”. Everything else is public information and can be found in this documentation (i.e. SCILL endpoints to send events to).

Whatever your implementation looks like, make sure that you follow these basic rules:

  • Don’t expose any part of the access token generation to the user, especially the api key and the userid
  • Don’t think that any sort of client side “obfuscation” does help in any way - hackers overcome these things in minutes
  • Your design must be secure by design. If you follow this guide it definitely will be.
  • All communication must be done via HTTPS

PHP

If your backend runs on PHP you can integrate sending events like this:

Step 1: Install PHP SDK

composer require scillgame/scill-php

If you don’t use composer in your project you can also download the PHP SDK from Github and import the classes manually into your project.

Step 2: Initialize library

Use autoload.php to initialize PHP library and setup an instance of the SCILLClient class by providing your API key.

<?php
    require_once(__DIR__ . '/vendor/autoload.php');

    // Setup SCILL Events client instance    
    $scillClient = new \SCILL\SCILLClient('ai728S-1aSdgb9GP#R]Po[P!1Z(HSSTpdULDMUAlYX');
    $scillEvents = $scillClient->getEventsClient();    
?>

Congratulations! You have set up an instance of the SCILL SDK class, that can be used to send events and generate access tokens. Refer to the API-Reference to learn what you can do with it.

Step 3: Sending events

Now all you have to do is send an event once a user has accepted an invitation. You will most likely have some console.log outputs in your code to indicate that certain things happened. An event is basically the same. A structured log entry is sent to the SCILL cloud.

Have a look at the Events API Reference to learn more about this function. In this step we just briefly go through this step by step.

In your business logic you will have a reference to your user. This can be anything identifying your user. It can be a primary key in your relational database, a collection key in your NoSQL database or an id of an auth provider. Please don’t use personal data like email adresses as a user id!. You should even think about creating a special user id just for SCILL.

<?php
    // Get User Id (this is what you need to know)
    $userId = getUserId();
    
    // We also need to have a session id, in this case we just use a random string as we don't want to group events
    $sessionId = uniqid();
?>

Another thing is the session id. Some events require a session id in order to group events. Think of a match in tennis. Then the session id can be used to group all events to a match or session. This is up to you how to manage those. In this basic example of user invites we just create a random number (based on time).

Next, you send an event of type user-invite via the sendEvent function. Last but not least, you can add a metadata object which is used to send additional data - like in this case how many users have been invited - that depends on the event type and is fully optional.

<?php
    // Setup the event to send
    $payload = new \SCILL\Model\EventPayload(array(
        "user_id" => $userId,
        "session_id" => $sessionId,
        "event_name" => "user-invire",
        "event_type" => "single",
        "meta_data" => array(
            "amount" => 1
        )
    ));
    
    $status = $scillEvents->sendEvent($payload)->getStatus();
    if ($status !== 200) {
        // Sending event failed ...
    }   
?>

That’s it for the backend side of things. Whenever a user accepts an invite, a SCILL event is sent to the SCILL cloud that refers your user with userId and is of type user-invite.

Step 4: Generate access token

Please note

This step is only required if your user facing code is running in an unsecure client environment like a game, an iOS, Android app or a JavaScript based client web application (SPA with Angular, React, Vue, …). Of course this also applies to games. If your web site is created on the server side - i.e. with PHP you don’t need to create an access token. In this case, just use your API key (or the API key provided in this example).

You don’t want to expose User IDs and/or the API-key in unsecure environments as this would allow anyone with some basic knowledge of the browsers web console to extract those and being able to send requests without your permission. To secure this, you create an access token for each user for each client session.

This example is based on the Slim Framework to quickly create REST-APIs with PHP to get you an idea how to implement it.

<?php
    $app->get('/scill/generate-access-token', function (Request $request, Response $response) {
    
        // In this example we expect that the client sends us a session id that we decode to receive a user id
        // This could also be a JWT token or a Steam Session. Whatever it is, it's your job to receive an encoded
        // user auth from the client, to decode it into a user id
        $params = $request->getQueryParams();
        $userId = null;
        if (!$params || !$params["session"]) {
            $response->getBody()->write('No session provided');
            return $response;
        } else {
            // Decode the session - in this example we just hardcode it
            $userId = "1234";
        }
    
        // Create the request to generate an access token
        /** @var \SCILL\SCILLClient $scill */
        $scill = $this->get('scill');
    
        $payload = new \SCILL\Model\ForeignUserIdentifier();
        $payload->setUserId($userId);
    
        // Run the request and if everything worked fine we can return the access token to the client
        try {
            $result = $scill->getAuthClient()->generateAccessToken($payload);
            $accessToken = $result->getToken();
            $result = array(
                "success" => true,
                "token" => $accessToken
            );
            $response->getBody()->write(json_encode($result));
        } catch(\SCILL\ApiException $exception) {
            $result = array(
                "success" => false,
                "error" => $exception->getMessage()
            );
            $response->getBody()->write(json_encode($result));
        }
        return $response;
    });
?>

The function generateAccessToken is used to generate an access token based on the user id you are providing. What happens here is that SCILL receives your API-Key together with the user id and then creates as well as signs a JWT token.

More info on this topic can be found in the Authentication document.

Please note: A cheater/hacker only needs to get his hands on userid and your api key to send events “on your behalf”. Everything else is public information and can be found in this documentation (i.e. SCILL endpoints to send events to).

Whatever your implementation looks like, make sure that you follow these basic rules:

  • Don’t expose any part of the access token generation to the user, especially the api key and the userid
  • Don’t think that any sort of client side “obfuscation” does help in any way - hackers overcome these things in minutes
  • Your design must be secure by design. If you follow this guide it definitely will be.
  • All communication must be done via HTTPS

Adding SCILL to the client

We have now prepared your backend:

  • It sends events if users accept invitations
  • It provides a secure endpoint for the client to get an access token

Step 1: Add SDK

Add this script to the head of your web application:

<html>
  <head>
    <script type="text/javascript" src="https://scill-cdn.web.app/scill.js"></script>  
  </head>
</html>

We also provide an NPM package for the client SDK if you want to include it that way:

npm install @scillgame/scill-js --save

Step 2: Retrieve access token

Before you can use the SCILL SDK in your client, you have to retrieve an access token. In this guide, you already have prepared your backend for this in the previous step that we now consume in the client. Please note: This example should just give you an idea how this could look like and that it is consistent to the example backend created in this guide before. Depending on your framework and codebase your actual implementation might be different - in some cases more, in some cases less.

// Define appId (see Admin Panel)
const appId = "597737952688570369";

// Define global scill client instances which is initialized with the access token after user signed in
let challengesApi = null;

// Your client will have some sort of token or session, this is up to you but has to be 
// consistent with this guide we'll send a session id to the backend
generateAccessToken = async function (session) {  
  // Call the endpoint we previously created in the backend
  return fetch("https://www.example.com/scill/generate-access-token", {
    method: "POST", 
    headers: {
     'Content-Type': 'application/json'
    },
    body: JSON.stringify({session: session})
  }).then(accessToken => {
    return accessToken;
  }).catch(error => {
    console.error('Failed to receive access token', error);
    return null;
  });
}

onUserLoggedIn = function(session) {
  getSCILLInstance(session).then(accessToken => {
    challengesApi = SCILL.getChallengesApi(accessToken);
  });
}

This code basically just calls your backend sending the session id after a user signed up. The backend we provided earlier decodes the session id to retrieve the user id and sends that to SCILL to generate an access token. Then a SCILL client SDK instance is created using this access token and a product identifier (see Admin Panel).

Please note: As you can see in this example code, on client side neither the userid nor the api key is used in the code and therefore cannot be extracted in any way from the client. All things that need to be secured are running on servers and not on the client side.

Step 3: Retrieve challenges

You finally have an instance of the SCILL client sdk prepared, ready to go in your web app. Let’s query the challenges available for the product referenced earlier. Once you have signed up for a free account, you can create your own challenges and battle passes.

updatePersonalChallenges = function() {
  challengesApi.getPersonalChallenges(appId).then(categories => {
    console.log("CHALLENGES", categories);
  });
}

That’s it! You now have a collection of ChallengeCategory objects.

A typical rendering of challenges as we do in SCILL Play might look like this:

As only activated challenges will track progress and count against your included volume, they first need to be unlocked and activated. In this implementation the user has to unlock and activate a challenge manually. However, depending on your product you might also activate challenges automatically.

Step 5: Monitor challenge progress in real time

The SCILL JavaScript SDK exposes an API to listen on updates of users challenges and battle passes. The idea here is to load personal challenges once, then listen on updates of those challenges to update the user interface.

// Use the startMonitorChallengeUpdates function and provide the access token and a callback function
this.challengeMonitor = scill.startMonitorChallengeUpdates(accessToken, (data) => {
  // data is an object of type ChallengeWebhookPayload. Use the methos below to find out what happened to the challenge
  if (data) {        
    if (data.new_challenge.user_challenge_current_score !== data.old_challenge.user_challenge_current_score) {
      // The challenge made progress
    } else if (data.new_challenge.type !== data.old_challenge.type) {
      if (data.new_challenge.type === 'finished' && data.old_challenge.type !== 'finished') {
        // This challenge got claimed, give reward to user
      } else {
        // Challenge action (got unlocked, activated, claimed, etc)      
      }      
    }
  }    
});

Step 6: Unlock and activate a challenge

async function unlockChallenge(challengeId) {
  // Unlock a challenge and auto unlock it (last parameter true)
  return challengesApi.unlockPersonalChallenge(appId, challengeId);
}

async function activateChallenge(challengeId) {
  // Unlock a challenge and auto unlock it (last parameter true)
  return challengesApi.activatePersonalChallenge(appId, challengeId);
}

onButtonPresses = function(challengeId, event) {
  unlockChallenge(challengeId).then(result => {
    // No need to reload data if you monitor changes, as a web socket notification is triggered right now which
    // triggers a reload of the challenges list.      
  });
}

In this example, the challenge that the user selected is unlocked and automatically activated. Now, this challenge tracks progress, and it will show the current status (i.e. counter is 0).

Now comes the fun part: Send an event using the NodeJS backend that we prepared. And within 1-3 seconds, the counter in your application for this challenge will be +1. Proceed with sending events until you reached the goal of the challenge (in this demo it’s 5). Once you have send 5 events, your challenge will change its type to unclaimed.

Step 7: Claiming rewards

Once a challenge has been successfully achieved, it is ready to be claimed. You can claim the reward using this API.

onClaimButtonPresses = function(challengeId, event) {
  challengesApi.claimPersonalChallengeReward(appId, challengeId).then(result => {
    if (result) {
      // Unlock client side - not recommended but possible      
    }

    // It's better to implement Webhook in the backend and to unlock reward there
  });
}

When the claim API is called, three things happen:

  1. It returns with a boolean value to indicate that the challenge is successfully claimed
  2. If you have set up a Webhook this is called

In either of these places you can now unlock the reward for the user. If you have set up a Webhook, the Websocket notification shoule be sent after the Webhook returns. That means, that you can use the Webhook to unlock items for the user in the backend. Once the request is completed, you can safely update the UI for the user by reloading the users' status from the backend.

An example: The user has successfully completed a challenge and should get a new weapon added to his inventory as a reward. Let’s walk trough the process step by step:

  • The user presses the “Claim” button, and your client sends a claim request to the SCILL backend.
  • SCILL sets the challenge to be claimed, calls your Webhook and waits until this is processed
  • Your backend receives the Webhook and adds the weapon to the users inventory (server side) and then returns HTTP status 200
  • SCILL received Webhook status 200 - which means everything is fine and returns the claim challenge request
  • The client now reloads the users inventory from the server (which should now show the new weapon)

Step 8: Sign up to create challenges

You have completed the loop! Your users will now be motivated to invite other users and will be rewarded if they do so. Wasn’t that easy? But that’s just the beginning. The possibilities with challenges are endless. And they are an easy solution to motivate your users to continue doing actions in your app or game, thus prolonging the time they spend in your app or game.

Sign up for free to create or modify challenges and battle passes.

Fully working JavaScript example

We have created a barebones, very simple yet fully functional Vanilla JavaScript/NodeJS application. https://example.release.app.scillplay.com/

The source code to this example can be found here: https://github.com/scillgame/scill-js/tree/main/examples/Example%20Backend

Game example: Tetris

Tetris (JavaScript)

We integrated various challenges into this React based browser Tetris game. Check out how we completely changed the game by just adding a couple of challenges to the game.

Learn more