Unity Asset "EasyIAP"の使い方 (和訳)

Unityの有料アセット｢EasyIAP｣のDocumentationを和訳した記事になります。

https://assetstore.unity.com/packages/tools/integration/easy-iap-in-app-purchase-128902

こちらのセットにも同梱されています。

https://assetstore.unity.com/packages/tools/integration/mobile-tools-complete-game-132284

# このアセットでできること
- 最低限のコーディング労力でゲーム内にIAP(In App Purchase)を導入できる
- ビルド目的のプラットフォームが複数あっても、同じコードのままでOK
- EasyIAPアセットをインポートしてIAP製品IDを入力するだけで設定完了(←別途自前Scriptも要るのでは？)
- Unity Editor上でテストできる
- 当Assetオリジナルの設定画面から、IAP商品IDなど簡単にセットアップできる

|対応状況|サポート対象|
|---|---|
|IAP商品タイプ|消耗型、非消耗型、サブスクリプション型|
|プラットフォーム|AppStore(iOS)、Google Play(Android)、Amazon（追加予定？）|
|Visual Scripting Tool|Bolt、GameFlow、Playmaker|

# 初期設定
1. Package Managerで "EasyIAP" アセットをインポートする。
2. Window -> Gley -> EasyIAP を開く。
デバッグメッセージを見たい場合はDebugにチェックしておく。
![Window1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1313392/d5233a2f-8241-e15a-c30e-fc2741bad01d.png)
3. "Import Unity IAP SDK"ボタンを押すか、Window > Package Manager > Packages: Unity Registry を開いてIn App Purchasingパッケージをインストールする。
4. Window > General > Services を開いて In-App Purchasing をONにする
5. EasyIAPウィンドウに戻り、"Add new product"ボタンを押し、各IAP商品の情報を入力していく。
![Window2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1313392/823a40d4-3642-f7ca-be46-5d48bcc4c456.png)

|項目|説明|
|---|---|
|Product Name|IAP商品の名前。スクリプト上で使用する。命名規則として、スペース･特殊文字の使用禁止、数字始まり禁止。|
|Product Type|IAP商品のタイプ。消耗型(Consumable)、非消耗型(NonConsumable)、サブスクリプション型(Subscription) から1つ選ぶ。|
|Reward Value|消耗型の場合は報酬の値を入力する。非消耗型の場合は"0"と入力しておく。|
|Google Play ID|Google Play Console上で確認できる、IAP商品固有のID。|
|App Store (iOS) ID|iTunes Connect上で確認できる、IAP商品固有のID。|
|Amazon SKU|Amazon developer portal上で確認できる、IAP商品固有のID。|

### 各ストアでのIAP商品IDの探し方
- Google Play ID の場合
    Google Play Console > すべてのアプリ > (当該アプリを選択) > 商品 > アプリ内アイテム
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1313392/8e9a1e4e-7981-d7a0-aa9a-75ccba25992b.png)
- App Store (iOS) ID の場合
    App Store Connect > アプリ > (当該アプリを選択) > 機能 > アプリ内課金 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1313392/7525f3c6-9a62-af81-ef36-3bbe3f6bcbdb.png)

### レシート検証の設定 (Google Playのみ)
レシート検証とは、ゲームユーザーが購入前のコンテンツにアクセスするのを防止するための機能。

レシート検証を有効にする方法：
1. Window > Unity IAP > IAP Receipt Validation Obfuscator を開く。
2. [Google Play Developer Console](https://play.google.com/console/u/0/developers)で現在のプロジェクトを選択し、"Services and API"を開くと、ライセンスキー(Google Play public key)が表示される。
3. 先程のライセンスキーを入力して "Obfuscate Google Play Licence Key" ボタンを押す。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1313392/c615b6bc-6b48-1056-be8c-b85ce6dabf9b.png)

# Script作成
## 1. 初期化

```C#
// IAP商品情報を初期化するメソッド(ゲーム開始時に1度だけ呼び出して下さい)：
IAPManager.Instance.InitializeIAPManager(InitializeResultCallback);


// 上記メソッド完了後に呼ばれるコールバック(例)：
private void InitializeResultCallback(Status status, string message, List<StoreProduct> shopProducts)
{
     if (status == Status.Success)  // IAPの初期化に成功
     {
          // 全てのIAP商品についてループ開始：
          for (int i = 0; i < shopProducts.Count; i++)
          {
               if (shopProducts[i].productName == "YourProductName")
               {
                    // 上記のflagがtrueなら、ユーザーがIAP商品を購入したことを意味する
                    // 報酬へのアクセスを許可する処理：
                    if (shopProducts[i].active)
                    {
                         yourBoolVariable = true;
                    }
               }
          }
     }
     else
     {
          Debug.Log("Error occurred "+ message);
     }
}
```

## 2. IAP購入時の処理
```C#
// IAP商品が購入された時に呼び出すメソッド：
IAPManager.Instance.BuyProduct(ShopProductNames.YourProductName, ProductBoughtCallback);


// 上記メソッド完了後に呼ばれるコールバック(例)：
private void ProductBoughtCallback(Status status, string message, StoreProduct product)
{
     if (status == Status.Success)
     {
          // 消耗型の例。1購入につきRewardValue個のコインが与えられる：
          if (product.productType == ProductType.Consumable)
          {
               coins += product.value;
          }

          // 非消耗型の例。UnlockLevel1フラグをtrueにする：
          if (product.productName == "UnlockLevel1")
          {
               unlockLevel1 = true;
          }
 
          // サブスクリプション型の例。Subscriptionフラグをtrueにする：
          if (product.productName == "Subscription")
          {
               subscription = true;
          }
     }
     else
     {
          // IAP購入処理中にエラーが発生した場合、詳細なエラーログを吐く：
          Debug.Log("Buy product failed: " + message);
     }
}
```

## 3. 過去の購入を復元する処理 (iOSのみ必要)
```C#
// 過去に購入したIAP商品を復元するメソッド(初期化時にも毎回自動で呼ばれる)：
IAPManager.Instance.RestorePurchases(ProductRestoredCallback);


// 上記メソッド完了後に呼ばれるコールバック(例)：
// 購入メソッドのコールバックとほぼ同じ内容。
private void ProductRestoredCallback(Status status, string message, StoreProduct product)
{
     if (status == Status.Success)
     {
          // 消耗型は復元する必要なし

          // 非消耗型の例。UnlockLevel1フラグをtrueにする：
          if (product.productName == "UnlockLevel1")
          {
               unlockLevel1 = true;
          }
 
          // サブスクリプション型の例。Subscriptionフラグをtrueにする：
          if (product.productName == "Subscription")
          {
               subscription = true;
          }
     }
     else
     {
          // IAP購入処理中にエラーが発生した場合、詳細なエラーログを吐く：
          Debug.Log("Buy product failed: " + message);
     }
}
```

### 復元に失敗した場合の処理も追加したい場合
```C#
// 上と同じ｢購入復元メソッド｣だが、2番目のコールバックも引数に登録できる
IAPManager.Instance.RestorePurchases(ProductRestoredCallback, RestoreDone);


// このコールバックは｢購入の復元に失敗した時｣や｢復元するものが何も無い時｣に呼ばれる
// 購入復元中のUI表示などに使える
private void RestoreDone()
{
    Debug.Log("Restore done");
}
```
## 4. その他の便利なメソッド
### 初期化済みかどうか確認する
```C#
// 初期化済みかどうかboolを返すメソッド：
public bool IsInitialized()

// 実装例：
IAPManager.Instance.IsInitialized()
```

### 報酬累計額をUI表示する 
```C#
// 1つのIAP商品について、ユーザーが受領したゲーム内通貨の累計額を返すメソッド：
public int GetValue(ShopProductNames product)

// 実装例：
IAPManager.Instance.GetValue(ShopProductNames.YourProductName)
```

### 商品価格を現地通貨でUI表示する
#### 現地価格 ＋ 通貨コード
```C#
// 1つのIAP商品について、現地価格と国別通貨コードをstring型で返すメソッド：
public string GetLocalizedPriceString(ShopProductNames product)

// 実装例：
IAPManager.Instance.GetLocalizedPriceString(ShopProductNames.YourProductName)
```
#### 現地価格のみ
```C#
// 1つのIAP商品について、現地価格に換算してint型で返すメソッド：
// (訳者注：int型なので、例えば米ドルだとセント部分が切れてしまう。実用的でない)
public int GetPrice(ShopProductNames product)

// 実装例：
IAPManager.Instance.GetPrice(ShopProductNames.YourProductName)
```
#### 通貨コードのみ
```C#
// 1つのIAP商品について、国別通貨コードをISO4217に準拠した書式(GBP,USD等)で返すメソッド：
public string GetIsoCurrencyCode(ShopProductNames product)

// 実装例：
IAPManager.Instance.GetIsoCurrencyCode(ShopProductNames.YourProductName)
```
### 商品情報の取得
#### 説明文の取得
```C#
// 1つのIAP商品の説明文を、Google Play/AppStore/AmazonStoreから取得するメソッド：
public string GetLocalizedDescription(ShopProductNames product)

// 実装例：
IAPManager.Instance.GetLocalizedDescription(ShopProductNames.YourProductName)
```
#### 商品タイトルの取得
```C#
// 1つのIAP商品のタイトルを、Google Play/AppStore/AmazonStoreから取得するメソッド：
public string GetLocalizedTitle(ShopProductNames product)

// 実装例：
IAPManager.Instance.GetLocalizedTitle(ShopProductNames.YourProductName)
```
### サブスクリプション商品の追加情報の取得
```C#
// サブスクリプション型商品に関する追加情報を取得するメソッド：
public SubscriptionInfo GetSubscriptionInfo(ShopProductNames product)


// 実装例：
SubscriptionInfo info = IAPManager.Instance.GetSubscriptionInfo(ShopProductNames.YourProductName);
if (info != null) 
{
    Debug.Log(info.getExpireDate());
}
```
### 購入状況の確認
```C#
// 1つのIAP商品について、現在のステータスを取得するメソッド（trueなら購入済み）：
public bool IsActive(ShopProductNames product)

// 実装例：
IAPManager.Instance.IsActive(ShopProductNames.YourProductName)
```

### その他のメソッド一覧
https://docs.unity3d.com/Manual/UnityIAPSubscriptionProducts.html

---

### 私の実装例: 商品価格を現地通貨でUI表示する
GetPriceメソッドだと米ドルのセント部分が消えてしまったので、GetLocalPriceStringメソッドを使ってstring切り出ししてました。
```C#
// ｢1.00 USD｣｢100 JPY｣のようなTextmeshPro用のstringを返すメソッド：
public string LocalPrice(string product)
{
    ShopProductNames shopProductName = ShopProductNames.RemoveAds;

    // 数字だけ抽出
    string price = System.Text.RegularExpressions.Regex.Replace
        (IAPManager.Instance.GetLocalizedPriceString(shopProductName), @"[^0-9,.]", "");

    // 通貨だけ抽出
    string currency = IAPManager.Instance.GetIsoCurrencyCode(shopProductName);
    
    // 文字列を結合し、フォントサイズを調整
    string tmptext = System.String.Join("", new string[] { price," <size=80%>",currency,"</size>" });

    return tmptext;
}
```

# トラブルシューティング
EasyIAPをUnity Editor上で動かす場合、支払いは発生しません。
初めてEasyIAPをデバイス上で動かす場合、下記事項をよく確認して下さい。

- インターネット接続が有効であること。
- デバイスの日時設定が正しいこと。
- iOSの場合、銀行口座をiTunes Connectに登録してあること。これをしないとIAP商品を返品できません。
- iOSの場合、XCode > Capabilites > In-App PurchaseがONになっていること。
- EasyIAPウィンドウで登録したIDが、それぞれの命名規則に従っていること。
- AppStore (iOS)やGoogle Play上には未公開のアプリを、テストユーザーとして起動していること。
- Google Play上にアルファ版またはベータ版として公開済みのアプリであること。
- アプリのバンドルIDが、Google Play developer consoleやiTuens Connect上で作成したものと同一であること。
- Google Play developer consoleにアップロードするのに使ったリリースキーと同じキーでアプリが署名されていること。

# 参考
Documentation

https://gleygames.com/documentation/Gley-EasyIAP-Documentation.pdf

Forum

https://forum.unity.com/threads/easy-iap-make-money-from-your-game.570037/