サーバー間のアイテム授受コールバックの実装

サーバー間 (S2S) コールバックを使用すると、プレイヤーに報酬を与える 際に、チートを検出して防ぐことができます。

しくみ

プレイヤーが動画広告を最後まで視聴すると、Unity Ads サーバーから、指定した URL に署名付きコールバックが送信されます。これは動画が実際に終わる前に発生するため、プレイヤーがゲームプレイに戻る前に授与サイクルを終わらせることができます。

ヒント: トラフィックによっては、コールバックが届くまでに時間がかかる場合があります。スムーズなゲームプレイ体験を実現するために、プレイヤーにすぐに報酬を与え、チートに対するサニティチェックに S2S コールバックを使用してください。動画が終わるまでプレイヤーの注意をそらさないようにするために、報酬の通知は広告の視聴が完了した後に表示します。

実装

S2S コールバックを使用するには、広告を表示する前にサーバー ID (sid) を設定する必要があります。デフォルトでは、S2S のアイテム授受コールバックは利用できません。プロジェクトでこのコールバックを有効にするには、ゲーム 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 アドレス/ネットワークから発信されます。このリストは毎月 1 日に更新されます。パブリッシャーはその他の場所からのコールバックを安全に無視またはブロックできます。

コールバック URL の形式

このリクエストは、HTTP/1.1 GET リクエストで、以下の形式の URL に送信されます。

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

クエリパラメーターについては、以下の表を参照してください。

パラメーター

内容

CALLBACK_URL

コールバック URL のベース URL。以下に例を示します。

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

これを設定するには、サポートにお問い合わせ ください。

SEPARATOR1

URL に ? がまだ存在しない場合は、? を使用します。それ以外の場合は、& が使用されます。

SID

エンドポイントに送信するユーザー ID またはカスタムデータ。

SEPARATOR&
OID

Unity Ads サーバーによって生成された一意のオファー ID。

SEPARATOR&
SIGNATURE

パラメーター文字列の HDMAC-MD5 ハッシュ。

例: 106ed4300f91145aff6378a355fced73

コールバック URL の例

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

コールバック URL への署名

コールバック URL リクエストでは、URL パラメーターに署名が付けられます。この署名は、HMAC (ハッシュベースメッセージ認証コード) を除くすべての URL パラメーターを "キー=値" の形でカンマ区切りでアルファベット順に並べて連結して作成されたパラメーター文字列の 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 がすでに使用されている、署名が一致しない、ユーザーが最終的に約束のアイテムを入手できないその他のエラーなど)、サーバーは人間が判読可能なエラーを含む、400 番台または 500 番台の 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";
?>