Sign in

Rewarding users

Challenges and Battle Pass levels can have rewards. Rewards can be anything, and it depends on your application and business model which rewards make sense to users.

Types of rewards

Overall, rewards can be separated into two basic groups:

Status

In communities, many people are interested in setting themselves apart from the crowd. They want status symbols to express either their personality or to stand out. Many games make use of this human nature and provide rewards like:

  • Fashion - skins, cloths, etc. (Fortnite)
  • Badges (Web communities)

In most (multiplayer) games and communities rewards don’t have any influence on the gameplay at all. It’s all purely esthetics nature.

Utility

Depending on the type of game rewards can also unlock items that change gameplay or unlock new features within your application or game. If your business logic has some quoters, you can reward users by pushing those quoters. This is often a very cheap solution as most users don’t make use of their volumes, so giving them more of that feels rewarding.

A few examples of utility rewards:

  • More storage
  • Special items
  • Secret levels
  • New features
  • Coins

Rewards in SCILL

Implementing rewards is up to you, SCILL does not provide any APIs on this topic, as rewards are so tightly integrated into your business or game logic. However, SCILL handles the state process, i.e. it will keep track of users challenges and levels and will notify you once a user wants to claim a reward.

Setting rewards in Admin Panel

In the Admin Panel you can set a reward type and a value for challenges and battle passes. This looks like this:

Reward settings in the Admin Panel

Reward settings in the Admin Panel

We use the same UI in Admin Panel for challenges and battle passes, however, the data structures are slightly different in naming.

The Challenge object has these properties:

challenge_reward string

Set a reward for this challenge. This is a string value that you can map to anything in your code. Use in combination with challenge_reward_type.

challenge_reward_type string

The reward type can be set to various different settings. Use it to implement different reward types on your side and use challenge_reward to set the value or amount of this reward.

In the BattlePassLevel object has these properties:

reward_amount string

In the Admin Panel you can set different types of rewards. You can also set an identifier of an in-game-item or anything you like. Use this to include the reward into your own business logic.

reward_type_name string

There are different types of rewards available. Possible values are Coins, Voucher, Money and Experience.

In Admin Panel you can choose the reward_type from a list which offers these values:

  • Nothing
  • Coins
  • Money
  • Experience
  • Item

There is no logic behind these values, you can use them as you like. Please let us know if you need more options that fit your business logic. We want to keep it as short as possible, however, they should also help documenting your code and it’s also easy to see in Admin Panel.

As reward_amount you can set a string. You can use that to identify the reward in your own business logic or game. For example if you want to unlock a skin you could set the value to BlueJacketWithGold. This can also be the IDs in your database, etc.

Triggers

SCILL implements different triggers to inform your application or backend once it’s time to unlock a reward to your user.

Realtime Update Notifications
In your client you listen to notifications that will be sent by the SCILL backend. Our SDKs offer a simple, often one line of code solution to listen on notifications. You just need to call a function and provide a callback function which will be called whenever a new message is sent.
Webhooks
You can create a webservice/endpoint in your backend and set a Webhook URL in Admin Panel for Challenges and Battle Passes which is called whenever something happens in the SCILL backend.

It depends on the architecture of your application which way is best to implement. In a single player mobile application without any servers, it makes sense to implement the Realtime Update API into your client and unlock items or coins directly in the client.

In a multiplayer game with user accounts, you might have a single source of trust for all your users like a users database. In this case, it’s better to implement a Webhook making it much harder to cheat or hack the system as everything is done purely in the backend. Use the Webhook system for this.

Unlocking rewards

The Realtime Update Notification system, and the Webhook system share the same data structures, but they are a bit different as shown above between Challenges and Battle Passes. However, the concept is the same for both.

Challenges

The payload you receive in a real time update message, or the webhook looks like this. Please note, that we have removed some properties so that it is easier to see what is important for this topic. The whole payload can be seen in the Playground application or this documentation: ChallengeWebhookPayload.

ChallengeWebhookPayload data structure
{
  "new_challenge": {
    ...
    "challenge_reward": "5GB",
    "challenge_reward_type": "Item",
    "challenge_type": "user-invite",
    "type": "finished",
    "user_id": "1234"
  },
  "old_challenge": {
    ...
    "challenge_reward": "5GB",
    "challenge_reward_type": "Item",
    "challenge_type": "user-invite",
    "type": "unclaimed",
    "user_id": "1234"    
  }
}

As you can see, in each notification you receive the state of the challenge before the change and after the change. In this case we are only interested in the type property. If it changed from in-progress to finished then the challenge has been rewarded.

Here is some example code for you to get started quickly with rewarding users if they achieved challenges.

Handling payloads in client
const SCILL = require("@scillgame/scill-js");

// ... accessToken being generated before...

const monitor = SCILL.startMonitorChallengeUpdates(accessToken, (payload) => {
    if (payload.old_challenge.type === 'unclaimed' && payload.new_challenge.type === 'finished') {
        // A challenge reward has been claimed
        if (payload.new_challenge.challenge_reward_type === 'Item') {
            unlockItem(payload.new_challenge.challenge_reward_amount);
        }
    }
});

function unlockItem(itemId) {
    // Do whatever is required to unlock the itemId for the user, i.e.
}
// ... accessToken generated before ...
var scillClient = new SCILLClient(accessToken, AppId);
scillClient.StartChallengeUpdateNotifications(payload => {
    // Important: This will be called from a worker thread. If you need to access other APIs you might need to dispatch
    // code on the "main thread". See SCILLThreadSafety class in Unity SDK to understand how to do that in Unity.
    if (payload.old_challenge.type === "unclaimed" && payload.new_challenge.type === "finished") {
        // A challenge reward has been claimed
        if (challenge.challenge_reward_type === "Item") {
            // For example call an InventoryManager instance to unlock the item defined in Admin Panel
            InventoryManager.UnlockItem(challenge.challenge_reward_amount);
        }
    }
});

If you want to handle rewards in your backend, you could do it this way - an example backend implementation with NodeJS:

Handling payloads in Backend
// This example implements a NodeJS backend listening on webhook requests from SCILL to unlock items for users
const express = require('express');
const bodyParser = require('body-parser');
const scill = require('@scillgame/scill-js');

// Generate an instance of the SCILL SDK with example API-Key
const auth = scill.getAuthApi('__YOUR_API_KEY__');

const app = express();
const port = 80;
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

unlockItem = function(itemId, userId) {
    // Unlock an item, for example enter a row in a database, etc.
}

// Implement a route that you can set in Admin Panel. This is what you would have to set in the admin panel:
// https://www.yourdomain.com/scill/webhook/challenges
// When creating the webhook a secret will be created by SCILL and will be sent as a GET parameter. Use the secret
// to make sure nobody can call your webhook without permission.
app.post('/scill/webhook/challenges', (req, res) => {
    const data = req.body;
    let secret = req.query.secret;

    if (!secret || secret !== "__YOUR_WEBHOOK_SECRET__") {
        return res.status(401).send({'error': 'You dont have permission to call this route'});
    }

    // If payload is available
    if (data) {
        // Check if challenge has been claimed
        if (payload.old_challenge.type === 'unclaimed' && payload.new_challenge.type === 'finished') {
            // A challenge reward has been claimed
            if (payload.new_challenge.challenge_reward_type === 'Item') {
                unlockItem(payload.new_challenge.challenge_reward_amount, payload.new_challenge.user_id);
            }
        } else {
            // Challenge action like unlock, activate, progress, etc.
        }
    }

    // Return a 200 status code so that SCILL knows everything went fine
    res.send({message: 'OK'});
});

app.listen(port, () => {
    console.log('Example app listening at http://localhost:${port}');
});
No example code available for PHP

Battle Passes

Battle Passes don’t have rewards for single challenges, but for levels. For each level a reward type and reward value can be set like in the Challenges system.

As battle passes are more complex products, this system sends three different types of notifications:

As you might have guessed already, we are interested in that battlepass-level-reward-claimed notification to reward users. The same as for challenges apply here. Either listen on the realtime notifications or implement a webservice for example in a cloud function that you set as a Webhook in Admin Panel.

The payload you receive in this notification looks like this:

BattlePassLevelRewardClaimed data structure
{
  "webhook_type": "battlepass-level-reward-claimed",
  "battle_pass_level_reward_claimed":   {
    "app_id": "597737952688570369",
    "battle_pass_id": "603693723277918210",
    "level_id": "563006391671062538",
    "user_id": "d290f1ee-6c54-4b01-90e6-d701748f0851",
    "level_position_index": 0,
    "reward_amount": "100",
    "reward_type_name": "Coins"
  }
}

Some example code to get you started using Realtime Notifications System:

Handling payloads in client
const SCILL = require("@scillgame/scill-js");

// ... accessToken being generated before...

const monitor = SCILL.startMonitorBattlePassUpdates(accessToken, battlePass.battle_pass_id, (payload) => {
    if (payload.webhook_type === 'battlepass-level-reward-claimed') {
        if (payload.battlepass_level_reward_claimed.reward_type_name === 'Item') {
            unlockItem(payload.battlepass_level_reward_claimed.reward_amount);
        }
    }
});

function unlockItem(itemId) {
    // Do whatever is required to unlock the itemId for the user, i.e.
}
// ... accessToken generated before ...
var scillClient = new SCILLClient(accessToken, AppId);
scillClient.StartBattlePassUpdateNotifications(battlePassId, payload => {
    // Important: This will be called from a worker thread. If you need to access other APIs you might need to dispatch
    // code on the "main thread". See SCILLThreadSafety class in Unity SDK to understand how to do that in Unity.
    if (payload.webhook_type === "battlepass-level-reward-claimed") {
        if (payload.battlepass_level_reward_claimed.reward_type_name === "Item") {
            InventoryManager.UnlockItem(payload.battlepass_level_reward_claimed.reward_amount);
        }
    }
});

And this is some NodeJS example code on how to handle battle pass rewards in backend:

Handling payloads in backend
// This example implements a NodeJS backend listening on webhook requests from SCILL to unlock items for users
const express = require('express');
const bodyParser = require('body-parser');
const scill = require('@scillgame/scill-js');

// Generate an instance of the SCILL SDK with example API-Key
const auth = scill.getAuthApi('__YOUR_API_KEY__');

const app = express();
const port = 80;
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

unlockItem = function(itemId, userId) {
    // Unlock an item, for example enter a row in a database, etc.
}

// Implement a route that you can set in Admin Panel. This is what you would have to set in the admin panel:
// https://www.yourdomain.com/scill/webhook/challenges
// When creating the webhook a secret will be created by SCILL and will be sent as a GET parameter. Use the secret
// to make sure nobody can call your webhook without permission.
app.post('/scill/webhook/challenges', (req, res) => {
    const data = req.body;
    let secret = req.query.secret;

    if (!secret || secret !== "__YOUR_WEBHOOK_SECRET__") {
        return res.status(401).send({'error': 'You dont have permission to call this route'});
    }

    // If payload is available
    if (data) {
        // Check if battle pass level reward has been claimed
        if (payload.webhook_type === 'battlepass-level-reward-claimed') {
            if (payload.battlepass_level_reward_claimed.reward_type_name === 'Item') {
                unlockItem(payload.battlepass_level_reward_claimed.reward_amount);
            }
        }
    }

    // Return a 200 status code so that SCILL knows everything went fine
    res.send({message: 'OK'});
});

app.listen(port, () => {
    console.log('Example app listening at http://localhost:${port}');
});
No example code available for PHP