Knowledge base

Server-to-Server (S2S) Redeem Callbacks

S2S callback illustration

Server-to-server callbacks are sent to your server when a user has watched an ad. You can use these callbacks to reward players with virtual goods and to e.g. detect and prevent cheating.

Note: By default the server-to-server redeem callbacks are not available. If you want to enable them for your game, please contact our support by emailing unityads-support@unity3d.com. Provide your game ID(s) and their respective callback URL(s) in the message. Then we'll send you a secret hash used to sign and validate the callbacks.

After your player has completed the video view, the Unity Ads server will send a signed callback to the URL you've specified. This is done before the actual end of the video, so that the awarding cycle can be completed before user comes back to the game. Show the reward notification after the ad view is finished. This is to avoid any distraction for the user until the video has actually ended.

Depending on the traffic, it can take some time for the callbacks to arrive. To ensure a smooth gameplay experience, reward the players immediately and use the S2S callbacks for sanity checks against cheating.

To use S2S callbacks, you need to set server ID (sid) before showing an ad.

Unity example:

using UnityEngine;
using System.Collections;
using UnityEngine.Advertisements;

public class UnityAdsManager : MonoBehaviour
{
    // an integer string of usually 5-7 digits, example: "12345"
    public string gameId;
    public string placement = "rewardedVideo"

    // Call this function when Advertisement.IsReady == true
    public void showAd() {

        ShowOptions options = new ShowOptions();

        // setting the server ID
        options.gamerSid = "example";

        Advertisement.Show (placement, options);
    }
}

In the native Unity Ads SDKs, this is done with PlayerMetaData API class.

Android example:

    if(UnityAds.isReady()) {
        PlayerMetaData playerMetaData = new PlayerMetaData(context);
        playerMetaData.setServerId("example");
        playerMetaData.commit();

        UnityAds.show(activity);
    }

iOS example:

    if([UnityAds isReady]) {
        id playerMetaData = [[UADSPlayerMetaData alloc] init];
        [playerMetaData setServerId:@"example"];
        [playerMetaData commit];

        [UnityAds show:self];
    }

Callback Origin

The callback will come from the IP addresses/networks listed in http://static.applifier.com/public_ips.json - the list will be updated on the first of each month. Publisher can safely ignore or block callbacks from everywhere else.

Callback URL Format

The request is a HTTP/1.1 GET request to an URL of the following format: >

[CALLBACK_URL][SEPARATOR1]sid=[SID][SEPARATOR]oid=[OID][SEPARATOR]hmac=[SIGNATURE]

where the parameters and the contents can be explained as follows.

Parameter Content
CALLBACK_URL The base URL of the callback URL, for example: http://developer.example.com/award.php?productid=1234. To configure this, contact unityads-support@unity3d.com
SEPARATOR1 & or ?: If ? does not exist in the URL yet, ? is used, otherwise & is used.
SID the User ID or any custom data you want to send to your endpoint. Above examples illustrate setting the Server ID on different platforms. For example 1234567890
SEPARATOR &
OID The unique Offer ID generated by Unity Ads servers, for example 0987654321
SEPARATOR &
SIGNATURE a HDMAC-MD5 hash of a parameter string, as described below, for example 106ed4300f91145aff6378a355fced73

For example: https://developer.example.com/award.php?productid=1234&sid=1234567890&oid=0987654321&hmac=106ed4300f91145aff6378a355fced73.

Signing the Callback URL

The Callback URL request will have a signature attached to the URL parameters. The signature is a HDMAC-MD5 hash of a parameter string created by concatenating the all the URL parameters in key=value form except the hmac in alphabetical order separated by commas.

For example, a callback URL with SID and OID of https://developer.example.com/award.php?productid=1234&sid=1234567890&oid=0987654321 will have a parameter string ‘oid=0987654321,productid=1234,sid=1234567890‘ which hashed with the secret hash key ‘xyzKEY‘ (you'll get the secret hash from one of our support engineers when they enable the callbacks for you) gives a hash of ‘106ed4300f91145aff6378a355fced73‘. As the result, the award callback will be fired to a URL:

https://developer.example.com/award.php?productid=1234&sid=1234567890&oid=0987654321&hmac=106ed4300f91145aff6378a355fced73

Note: All the parameters included in the callback url must be included in the signature calculation in alphabetical order. Otherwise the signatures will not match.

Response to the Callback

If the request passes all the checks and user was awarded the item, the URL must reply with a HTTP/1.1 200 OK and include character “1″ in the body of the HTTP request, e.g.

HTTP/1.1 200 OK
Date: Wed, 22 Feb 2012 23:59:59 GMT
Content-Length: 8

1

If there’s an error, the server should return a HTTP error in the 400- or the 500-range with a human readable error, e.g. when the OID has already been used, or signature does not match, or any other error when user does not end up having the promised item, e.g.

HTTP/1.1 400 ERROR
Date: Wed, 22 Feb 2012 23:59:59 GMT
Content-Length: 12

Duplicate order

Callback example in node.js

The following example shows how to verify the signature using node.js + express.

// NODE.js S2S callback endpoint sample implementation
// Unity Ads

var express = require('express');
var crypto = require('crypto')
var app = express();


app.listen(process.env.PORT || 3412);

function getHMAC(parameters, secret) {
    var sortedParameterString = sortParams(parameters);
    return crypto.createHmac('md5', secret).update(sortedParameterString).digest('hex');
}

function sortParams(parameters) {
    var params = parameters || {};
    return Object.keys(params)
        .filter(key => key !== 'hmac')
        .sort()
        .map(key => params[key] === null ? `${key}=` : `${key}=${params[key]}`)
        .join(',');
}

app.get('/', function (req, res) {

    var sid = req.query.sid;
    var oid = req.query.oid;
    var hmac = req.query.hmac;

    // Save the secret as an environment variable. If none is set, default to xyzKEY
    var secret = process.env.UNITYADSSECRET || 'xyzKEY';

    var newHmac = getHMAC(req.query, secret);

    if (hmac === newHmac) {
        // Signatures match

        // Check for duplicate oid here (player already received reward) and return 403 if it exists

        // If there's no duplicate - give virtual goods to player. Return 500 if it fails.

        // Save the oid for duplicate checking. Return 500 if it fails.

        // Callback passed, return 200 and include '1' in the message body
        res.status(200).send('1');

    } else {
        // no match
        res.sendStatus(403);
    }

});

Callback example in PHP

The following example shows how to verify the signature in PHP.

<?php
function generate_hash($params, $secret) {
   ksort($params); // All parameters are always checked in alphabetical order
   $s = '';
   foreach ($params as $key => $value) {
     $s .= "$key=$value,";
   }
   $s = substr($s, 0, -1);
   $hash = hash_hmac('md5', $s, $secret);
   return $hash;
}

$hash = $_GET['hmac'];
unset($_GET['hmac']);
$signature = generate_hash($_GET, 'xyzKEY'); // insert here the secret hash key you received from Unity Ads support
error_log("req hmac".$hash);
error_log("sig hmac".$signature);

// check signature
if($hash != $signature) { header('HTTP/1.1 403 Forbidden'); echo "Signature did not match"; exit; }

// check duplicate orders
if(check_duplicate_orders($_GET['oid']) { header('HTTP/1.1 403 Forbidden'); echo "Duplicate order"; exit; }

// if not then give the player the item and check that it succeeds.
if(!give_item_to_player($_GET['sid'], $_GET['product']) { header('HTTP/1.1 500 Internal Server Error'); echo "Failed to give item to the player"; exit; }

// save the order ID for duplicate checking
if(save_order_number($_GET['oid']) { header('HTTP/1.1 500 Internal Server Error'); echo "Order ID saving failed, user granted item"; exit; }

// everything OK, return "1"
header('HTTP/1.1 200 OK');
echo "1";
?>
Still need help? Get in touch!
Last updated on 23rd May 2017