Huawei Adsの実装方法－リワード広告編

# Huawei Adsの実装方法－リワード広告編
[リワード広告](https://developer.huawei.com/consumer/jp/doc/development/HMSCore-Guides-V5/publisher-service-reward-0000001050066917-V5)はこのような広告です。

<img width="300" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/704548/65fe38dc-c080-3a58-4ce6-995bd29bd17f.png">

### Ad slot ID
[Huawei Adsの実装方法－準備編](https://qiita.com/Rei_2020/items/9f9eb35f781051eba811)の[Ad slot ID](https://qiita.com/Rei_2020/items/9f9eb35f781051eba811#ad-slot-id)をご参照ください。
オフィシャルのテスト用Ad slot IDは
<b>testx9dtjwj8hp</b>
です。

### レイアウト
リワード広告の場合、XMLレイアウトの用意がいりません。リワード広告を表示するときに、Huawei AdsはActivityの上にビューを生成します。

### Android
ActivityまたはFragmentに、次の種類のソースコードを入れます。

```kotlin
private var rewardAd: RewardAd? = null

private fun loadRewardAd(context: Context) {
    // リワード広告オブジェクトの生成
    rewardAd = RewardAd(context, "Your Ad slot ID").apply {
        // 必須ではないが、リワード検証のために設定しておいたほうがよい
        setRewardVerifyConfig(RewardVerifyConfig.Builder()
            .setData("Your reward ads data")         // リワード検証用など
            .setUserId("Your reward ads user id")    // リワード検証用など
            .build())
    }
    // リワード広告をロード
    rewardAd?.loadAd(
        AdParam.Builder().build(),
        object : RewardAdLoadListener() {
            override fun onRewardedLoaded() {
                // ロード完了後に表示する
                showRewardAd(context)
            }

            override fun onRewardAdFailedToLoad(errorCode: Int) {
                // ロード失敗時の処理
                super.onRewardAdFailedToLoad(errorCode)
            }
        }
    )
}

private fun showRewardAd(context: Context) {
    rewardAd?.let { it ->
        // ロード完了チェック
        if (it.isLoaded) {
            // リワード広告を表示する
            it.show(this.activity, object : RewardAdStatusListener() {
                override fun onRewardAdClosed() {
                    // リワード広告が閉じられたときの処理
                    super.onRewardAdClosed()
                }

                override fun onRewardAdFailedToShow(errorCode: Int) {
                    // リワード広告が表示できないときの処理
                    super.onRewardAdFailedToShow(errorCode)
                }

                override fun onRewardAdOpened() {
                    // リワード広告が開かれたときの処理
                    super.onRewardAdOpened()
                }

                override fun onRewarded(reward: Reward?) {
                    // リワードが与えられたときの処理
                    // リワードのスコア：reward?.amount
                }
            })
        }
    }
}
```

### サーバー

リワードの検証を行う場合のみ、サーバー側の実装が発生します。

<b>Huawei Adsサーバーから送られてきた検証データを受け取るためのAPIの実装</b>
Huawei Adsサーバーは次のデータをユーザーが設定したコールバックAPIで返します。

|データ|型|オプショナル|説明|
|:---|:---:|:---:|:---|
|adId|String||リワード広告のAd slot ID|
|data|String|〇|RewardVerifyConfig.Builder().setData()に渡した値|
|keyId|String||検証用のキーのID|
|rewardAmount|String|〇|リワードの金額|
|rewardName|String|〇|リワード名|
|sign|String||検証用signature|
|uniqueId|String||ユニークなID|
|userId|String|〇|RewardVerifyConfig.Builder().setUserId()に渡した値|

<b>ユーザーが設定する検証用コールバックAPI</b>
1．[HUAWEI Ads Publisher Console](https://developer.huawei.com/consumer/en/service/ads/publisher/html/index.html#/mainContent/dashbordContent)を開きます。
2．[My apps] -> [Add ad unit] -> [Next] -> [Advanced settings]を開きます。
3．Server-side verificationに検証用コールバックAPIを設定します。
<img width="600" alt="image.png" src="https://alliance-communityfile-drcn.dbankcdn.com/FileServer/getFile/cmtyPub/011/111/111/0000000000011111111.20210218145614.92043562306783146435237546902852:50520218075851:2800:BADEB4BE93ADC74920E17A183988CBFA1F8235EBCB2C7BDB11548A4C92F869D6.png">

<b>Publisher IDと鍵</b>
1．[HUAWEI Ads Publisher Console](https://developer.huawei.com/consumer/en/service/ads/publisher/html/index.html#/mainContent/dashbordContent)を開きます。
2．[Settings]を開きます。

<img width="600" alt="image.png" src="https://alliance-communityfile-drcn.dbankcdn.com/FileServer/getFile/cmtyPub/011/111/111/0000000000011111111.20210218145614.43982700388743569899549468868348:50520218075851:2800:C52D1ED8F4D1D7F14CDCE26AFDD7D8D3044F9661E7C93F2435DB065FE49A0036.png">

3．[Obtain Key]をクリックします。

<img width="600" alt="image.png" src="https://alliance-communityfile-drcn.dbankcdn.com/FileServer/getFile/cmtyPub/011/111/111/0000000000011111111.20210218145614.48501737907251594004721195993011:50520218075851:2800:22252FDEE5929F1D34282D3FEEC7DF92FA5DB60975B426EEC2A2935E4DB7B2F8.png">

<b>検証用コールバックAPIのサンプル</b>
まず、Publisher IDとKeyを使って、公開鍵を取得します。

```java
String data = "";
String url = "https://ppscrowd-dre.op.dbankcloud.com/action-lib-track/publickeys";
String authorization = "Digest validTime=\"{0}\", response=\"{1}\"";
// Developer ID
String userId = "YOUR_PUBLISHER_ID"; 
// Key
String key = "YOUR_KEY"; 
        
HttpClient httpclient = HttpClients.createDefault();
HttpGet request = new HttpGet();
try {
    String validTime = String.valueOf(System.currentTimeMillis());
    String body = validTime + ":/publickeys";
    byte[] keyBytes = Base64.decodeBase64(key);
    byte[] bodyBytes = body.getBytes(Charsets.UTF_8);

    Mac mac = Mac.getInstance("HmacSHA256");
    SecretKey secretKey = new SecretKeySpec(keyBytes, "HmacSHA256");
    mac.init(secretKey);
    byte[] signatureBytes = mac.doFinal(bodyBytes);

    String signature = (signatureBytes == null) ? null : Hex.encodeHexString(signatureBytes);
    authorization = MessageFormat.format(authorization, validTime, signature);
    request.setURI(new URI(url));
    request.setHeader("userId", userId);
    request.setHeader("Authorization", authorization);
    HttpResponse response = httpclient.execute(request);
    data = EntityUtils.toString(response.getEntity());
    } catch (Exception e) {
    }
}
```

上のコードを実行すれば、次のようなレスポンスが返ってきます。

```json
{    
    "keys": [       
        {          
            "keyId":"12345678",          
            "publicKey":"LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS1cbk1GLi4uc3c9PVxkQgUFVCTElt"       
        },      
        {          
            "keyId": "22345678",                               
            "publicKey":"LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS1cbk1GLi4uc3c9PVxkQgUFVCTElt"       
        }     
    ]
}
```

検証用コールバックAPIに渡されたkeyIdと同じkeyIdを持つ要素のpublicKeyが公開鍵になります。

次は検証ロジックのメソッドを作ります。

```java
public static boolean verify(byte[] data, String publicKey, String sign) {
    try {
        byte[] keyBytes = base64Decode(publicKey);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicK = keyFactory.generatePublic(keySpec);
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initVerify(publicK);
        signature.update(data);
        return signature.verify(base64Decode(sign));
    } catch (InvalidKeyException | SignatureException | UnsupportedEncodingException | InvalidKeySpecException | NoSuchAlgorithmException e) {
        return false;
    }
}
private static byte[] base64Decode(String encoded) throws UnsupportedEncodingException {
    return Base64.decodeBase64(encoded.getBytes("UTF-8"));
}
```

最後はverify()を使って、検証します。

```java
String param = "adId=" + adId + "&data=" + data + "&rewardAmount=" + rewardAmount + "&rewardName=" + rewardName + "&uniqueId=" + uniqueId + "&userId=" + userId;
String sha256Value = Sha256Util.digest(param);
byte[] paramContentData = sha256Value.getBytes(Charset.forName("UTF-8"));

bool result = verify(paramContentData, {公開鍵}, {検証用コールバックAPIのsign})
```

# Huawei Adsシリーズ
- [Huawei Adsの機能のまとめ](https://qiita.com/Rei_2020/items/87804f2057d4b143fc12)
- [Huawei Adsの実装方法－準備編](https://qiita.com/Rei_2020/items/9f9eb35f781051eba811)
- [Huawei Adsの実装方法－広告用識別子（OAID）編](https://qiita.com/Rei_2020/items/f5446a2bbbb4d5d3df67)
- [Huawei Adsの実装方法－インストールリファラー編](https://qiita.com/Rei_2020/items/977eabb3a514ba5c2476)
- [Huawei Adsの実装方法－バナー広告編](https://qiita.com/Rei_2020/items/308ebf626c1423a7cb1f)
- [Huawei Adsの実装方法－ネイティブ広告編](https://qiita.com/Rei_2020/items/f60976c23c1268261d5f)
- [Huawei Adsの実装方法－リワード広告編](https://qiita.com/Rei_2020/items/06744db6572c1603b0bd)
- [Huawei Adsの実装方法－インタースティシャル広告編](https://qiita.com/Rei_2020/items/3c9d9a19861a2d139737)
- [Huawei Adsの実装方法－スプラッシュ広告編](https://qiita.com/Rei_2020/items/5a138945d7b343602df1)

# GitHub
HMS Ads Kit Demo : [https://github.com/Rei2020GitHub/MyPublicProject/tree/master/AdsDemo](https://github.com/Rei2020GitHub/MyPublicProject/tree/master/AdsDemo)

# 参考
* HMS：[https://developer.huawei.com/consumer/jp/](https://developer.huawei.com/consumer/jp/)
* HMS Ads Kitの紹介：[https://developer.huawei.com/consumer/jp/hms/huawei-adskit/](https://developer.huawei.com/consumer/jp/hms/huawei-adskit/)
* HMS Ads Kitのドキュメント：[https://developer.huawei.com/consumer/jp/doc/development/HMSCore-Guides/publisher-service-introduction-0000001070671805](https://developer.huawei.com/consumer/jp/doc/development/HMSCore-Guides/publisher-service-introduction-0000001070671805)
* HUAWEI Ads Publisher Service：[https://developer.huawei.com/consumer/jp/monetize/](https://developer.huawei.com/consumer/jp/monetize/)
* HUAWEI Ads Publisher Serviceのドキュメント：[https://developer.huawei.com/consumer/jp/doc/distribution/monetize/advantage-0000001051201913](https://developer.huawei.com/consumer/jp/doc/distribution/monetize/advantage-0000001051201913)
* Huawei Developers：[https://forums.developer.huawei.com/forumPortal/en/home](https://forums.developer.huawei.com/forumPortal/en/home)
* Facebook Huawei Developersグループ：[https://www.facebook.com/Huaweidevs/](https://www.facebook.com/Huaweidevs/)