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

> サーバー間のコールバックを設定して、リワード型広告の完了を安全に検証し、不正防止と正確なゲーム内報酬の配布を行います。

サーバー間 (S2S) コールバックを使用すると、[プレイヤーに報酬を与える](/grow/ads/unity-sdk/rewarded-ads.md) 際に、チートを検出して防ぐことができます。

## しくみ##how-it-works

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

![S2Sの引き換えコールバック プロセスの図。](/api/media?file=/grow/media/images/s-2-s-redeem-callback-process.png)

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

## 実装##implementation

S2S コールバックを使用するには、広告を表示する前にサーバー ID (sid) を設定する必要があります。デフォルトでは、S2S のアイテム授受コールバックは利用できません。プロジェクトでこのコールバックを有効にするには、[ゲーム ID](/grow/dashboard/get-started/project/settings.md) とそれぞれのコールバック URL をメッセージに記載して、[Unity Ads サポートに連絡](https://support-ads.unity.com/s/ContactUs) してください。Unity からコールバックの署名と検証に使用する秘密ハッシュ (シークレット) をお送りします。

### 例##examples

1. **Unity (C#)**

   C# コードにコールバックを実装するには、 値をサーバー ID に設定し、`ShowOptions.gamerSid`(/grow/ads/unity-sdk/unity-api)`ShowOptions.gamerSid` メソッドを介して options オブジェクトを渡します。

   ```cs
   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);
     }
   }
   ```

2. **Android (Java)**

   Java コードにコールバックを実装するには、`PlayerMetaData.setServerId` 値をサーバー ID に設定します。

   ```java
   PlayerMetaData playerMetaData = new PlayerMetaData(context);
   playerMetaData.setServerId("example");
   playerMetaData.commit();

   UnityAds.show(activity);
   ```

3. **iOS (Objective-C)**

   Objective-C コードにコールバックを実装するには、`playerMetaData.setServerId` 値をサーバー ID に設定します。

   ```objective-c
   id playerMetaData = [[UADSPlayerMetaData alloc] init];
   [playerMetaData setServerId:@"example"];
   [playerMetaData commit];

   [UnityAds show: self
     placementId: placementId
     showDelegate: showDelegate];
   ```

## コールバックに関する情報##callback-information

### コールバック発信元##callback-origin

コールバックは、[こちら](https://static.applifier.com/public_ips.json) に掲載されている IP アドレス/ネットワークから発信されます。このリストは毎月 1 日に更新されます。パブリッシャーはその他の場所からのコールバックを安全に無視またはブロックできます。

### コールバック URL 形式##callback-url

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

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

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

| **パラメーター**     | **コンテンツ**                                                                                                                                                              |
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `CALLBACK_URL` | コールバック URL のベース URL。以下に例を示します。`https://developer.example.com/award.php?productid=1234`これを設定するには、[Unity Ads サポートに問い合わせてください](https://support-ads.unity.com/s/ContactUs) |
| `SEPARATOR1`   | URL に `?` がまだ存在しない場合は、`?` を使用します。それ以外の場合は、`&#x26;` が使用されます。                                                                                                            |
| `SID`          | エンドポイントに送信するユーザー ID またはカスタムデータ。                                                                                                                                        |
| `SEPARATOR`    | `&#x26;`                                                                                                                                                               |
| `OID`          |  Unity Ads サーバーによって生成された一意のオファー ID。                                                                                                                                    |
| `SEPARATOR`    | `&#x26;`                                                                                                                                                               |
| `SIGNATURE`    | パラメーター文字列の HDMAC-MD5 ハッシュ。例えば `106ed4300f91145aff6378a355fced73`。                                                                                                      |

```text
http title="Example callback URL"
https://developer.example.com/award.php?productid=1234&amp;sid=1234567890&amp;oid=0987654321&amp;hmac=106ed4300f91145aff6378a355fced73
```

### コールバック URL への署名##sign-the-callback-url

コールバック URL リクエストでは、URL パラメーターに署名が付けられます。この署名は、HMAC (ハッシュベースメッセージ認証コード) を除くすべての URL パラメーターを "キー=値" の形でカンマ区切りでアルファベット順に並べて連結して作成されたパラメーター文字列の HDMAC-MD5 ハッシュです。

例えば、SID と OID を含む以下のコールバック URL の場合

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

パラメーター文字列は以下のようになります。

```text
oid=0987654321,productid=1234,sid=1234567890
```

サポートから受け取る秘密鍵でこれがハッシュ化されることで、授与コールバックで URL に送信されるハッシュが返されます。例を次に示します。

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

> **Important:**
>
> コールバック URL に含まれるすべてのパラメーターを、署名の計算にアルファベット順で含める必要があります。そうしないと署名は一致しません。

### コールバックのレスポンス##callback-response

リクエストがすべてのチェックを通過し、ユーザーにアイテムが授与された場合、URL は `HTTP/1.1 200 OK` のレスポンスを返し、HTTP リクエストの本文に文字 `1` を含める必要があります。例を次に示します。

```text
http title="Callback response example"
HTTP/1.1 200 OK
Date: Wed, 22 Feb 2012 23:59:59 GMT
Content-Length: 8
1
```

エラーが発生した場合 (その OID がすでに使用されている、署名が一致しない、ユーザーが最終的に約束のアイテムを入手できないその他のエラーなど)、サーバーは人間が判読可能なエラーを含む、`400` 番台または `500` 番台の HTTP エラーを返します。例を次に示します。

```text
http title="Callback error response example"
HTTP/1.1 400 ERROR
Date: Wed, 22 Feb 2012 23:59:59 GMT
Content-Length: 12

Duplicate order
```

#### node.js のコールバック##callbacks-in-node.js

以下の例は、node.js と express を使用して署名を検証する方法を示しています。

```text
js title="node.js callback example"
// 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.設定されていない場合、デフォルトで 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.失敗した場合は 500 を返します。

    // Save the oid for duplicate checking.失敗した場合は 500 を返します。

    // Callback passed, return 200 and include '1' in the message body
    res.status(200).send("1");
  } else {
    // no match
    res.sendStatus(403);
  }
});
```

## PHP のコールバック##callbacks-in-php

以下の例は、PHP で署名を検証する方法を示しています。

```text
php title="PHP callback example"
<?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";
?>
```
