# 서버 간 보상 지급 콜백 구현

> 서버 간 콜백을 설정하여 보상형 광고 시청 완료를 안전하게 확인하고, 사기를 방지하고, 정확한 보상 배포를 보장합니다.

[플레이어에게 보상을 지급](/grow/ads/unity-sdk/rewarded-ads.md)할 때 S2S(서버 간) 콜백을 사용하여 부정 행위를 감지 및 방지할 수 있습니다.

## 자세히 알아보기##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)하시기 바랍니다. 유니티에서 콜백 서명 및 검증에 사용되는 비밀 해시를 보내 드립니다.

### 예시##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 주소/네트워크에서 발생합니다. 이 목록은 매월 초에 업데이트될 것입니다. 퍼블리셔는 여기에 기재되지 않은 출처로부터 발생하는 모든 콜백을 안전하게 차단하거나 무시할 수 있습니다.

### 콜백 URL 형식##callback-url

다음 포맷의 URL에 대한 HTTP/1.1 `GET` 요청입니다.

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

쿼리 파라미터에 대한 자세한 내용은 다음 표를 참조하십시오.

| **파라미터**       | **콘텐츠**                                                                                                                                                           |
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `CALLBACK_URL` | 콜백의 기본 URL이며 예를 들면 다음과 같습니다. `https://developer.example.com/award.php?productid=1234`이를 설정하려면 [Unity Ads 지원 팀에 문의하십시오](https://support-ads.unity.com/s/ContactUs) |
| `SEPARATOR1`   | `?`가 아직 URL에 없으면 `?`를 사용합니다. 이외에는 `&#x26;` is가 사용됩니다.                                                                                                             |
| `SID`          | 사용자 ID 또는 엔드포인트에 전송할 모든 커스텀 데이터입니다.                                                                                                                               |
| `SEPARATOR`    | `&#x26;`                                                                                                                                                          |
| `OID`          | Unity Ads 서버에서 생성된 고유의 Offer 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 파라미터에 서명(Signature)이 첨부됩니다. 서명은 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`을 넣어서 `HTTP/1.1 200 OK` 응답으로 회신해야 합니다. 예제는 아래와 같습니다.

```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. 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의 콜백##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";
?>
```
