实现服务器到服务器兑换回调

奖励玩家时可使用服务器到服务器 (S2S) 回调来检测和防止作弊。

工作原理

当玩家完整观看视频广告时,Unity 广告服务器会向您指定的 URL 发送已签名的回调。这个过程发生在视频实际结束之前,因此可以在玩家返回游戏之前完成奖励循环。

提示:根据流量,回调可能需要一些时间才能到达。为确保顺畅的游戏体验,请立即奖励玩家,然后使用 S2S 回调进行完整性检查以防作弊。为避免在视频结束之前分散玩家的注意力,应在广告观看结束之后再显示奖励通知。

实现

要使用 S2S 回调,您需要在展示广告之前设置服务器 ID (sid)。默认情况下,S2S 兑换回调不可用。如果您需要为项目启用这些回调,请联系支持团队,并在消息中提供您的 Game ID(游戏 ID)及其各自的回调 URL。Unity 将向您发送用于签名和验证回调的密钥哈希值。

Unity 示例

要在 C# 代码中实现回调,请将 ShowOptions.gamerSid 值设置为您的服务器 ID,然后通过 Show 方法传递 options 对象。

using UnityEngine;
using System.Collections;
using UnityEngine.Advertisements;
 
public class UnityAdsManager : MonoBehaviour
{
    public string gameId;
    public string placement = "rewardedVideo"
 
    public void ShowAd() {
 
        ShowOptions options = new ShowOptions();
 
        // setting the server ID
        options.gamerSid = "your-side-id";
 
        Advertisement.Show(placementID, options);
    }
}

Android 示例

要在 Java 代码中实现回调,请将 PlayerMetaData.setServerId 值设置为您的服务器 ID。

PlayerMetaData playerMetaData = new PlayerMetaData(context);
        playerMetaData.setServerId("example");
        playerMetaData.commit();
 
        UnityAds.show(activity);

iOS 示例

要在 Objective-C 代码中实现回调,请将 playerMetaData.setServerId 值设置为您的服务器 ID。

id playerMetaData = [[UADSPlayerMetaData alloc] init];
        [playerMetaData setServerId:@"example"];
        [playerMetaData commit];
 
        [UnityAds show:self
          placementId: placementId
   	      showDelegate: showDelegate];

回调信息

回调来源

回调将来自于此处列出的 IP 地址/网络。该列表将在每个月的第一天更新。发行商可以放心忽略或阻止来自任何其他地方的回调。

回调 URL 格式

该请求是对以下格式的 URL 的 HTTP/1.1 GET 请求:

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

有关 query 参数的信息,请参阅下表:

参数

内容

CALLBACK_URL

回调 URL 的基 URL,例如:

https://developer.example.com/award.php?productid=1234

要配置此参数,请联系支持团队

SEPARATOR1

如果 URL 中不存在 ?,请使用 ?。否则,使用 &

SID

用户 ID 或要发送到终端的任何自定义数据。

SEPARATOR&
OID

Unity Ads 服务器生成的唯一 Offer ID(优惠 ID)。

SEPARATOR&
SIGNATURE

参数字符串的 HDMAC-MD5 哈希值

例如:106ed4300f91145aff6378a355fced73

回调 URL 示例

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

回调 URL 的签名

回调 URL 请求会将一个签名附加到 URL 参数。该签名是通过以键值形式连接所有 URL 参数(HMAC 除外,按字母顺序排列并以逗号分隔)创建的参数字符串的 HDMAC-MD5 哈希值。

例如,一个包含 SID 和 OID 的回调 URL

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

将具有以下参数字符串:

oid=0987654321,productid=1234,sid=1234567890

此字符串与您即将从支持团队收到的密钥进行哈希处理后,返回一个由奖励回调触发为 URL 的哈希值。例如:

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

**重要:**回调 URL 中包含的所有参数必须按字母顺序用于签名计算。否则,签名将不匹配。

回调响应

如果该请求通过了所有检查并且用户获得了奖励物品,则 URL 必须以 HTTP/1.1 200 OK 响应作为应答并在 HTTP 请求的正文中包含字符 1。例如:

回调响应示例

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

如果出现错误(例如,OID 已被使用,或签名不匹配,或者当用户未能获得承诺物品时的任何其他错误),服务器应返回 400500 范围内的 HTTP 错误,并显示人类可读的错误。例如:

回调错误响应示例

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

node.js 中的回调示例

以下示例演示了如何使用 node.js + express 来验证签名:

node.js 中的回调示例

// 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);
    }
 
});

PHP 中的回调示例

以下示例演示了如何在 PHP 中验证签名:

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";
?>