はじめに
今回はUnity IAPのCodelessIAPという機能を用いて、簡単に非消費型(買い切り型)のアプリ内課金を実装してみるという記事になります。
先に言っておくと、Unityでの実装はすごく簡単ですが割とストア関連・テスト関連で躓くことの方が多いかもしれません。
Unity IAP SDKのインストール
Windos -> General -> ServiceからServiceウィンドウを立ち上げます。

In-App PurchasingをONにします。

プロジェクトIDを作成していない場合
プロジェクトIDをまだ作成していなかった場合は以下の操作を行う必要があります。
組織・プロジェクトIDをリンクさせ、COPPAコンプライアンスという13歳未満を対象としているかどうかを選択します。

無事に以下の画像の画面が開けたら、Welcomeという欄の中にImportボタンがあるので、こちらからPluginsをインストール。

するとPlugins/UnityPurchasingがインポートされているはずです。
実装
IAP Buttonsを配置
Hierarchy上で右クリックをし、Unity IAP -> IAP Buttonを選択し、IAP ButtonコンポーネントがアタッチされたButtonを作成します。

今回実装するのは広告削除・追加コンテンツといった買い切り型のアプリ内課金なので、以下の二つのボタンが必要になります。
- Non-Consumable(非消費型)Button:購入をするボタン
- Restore Button:購入データ復元のボタン(Androidの場合は不要だが、iOSの場合は必要)

IAP CatalogでProductsを定義
IAP Buttonコンポーネントから、IAP Catalog...というボタンを押し、IAP Catalog Windowを開きます。

プロダクトの定義の仕方については以下の公式ドキュメントを参照してみてください。
docs.unity3d.com
ざっと要点をまとめてみるとこんな感じ。
IDはcom.<company name>.<game name&>.<product name>のように表記します。文字は全て小文字で表記。(例.com.hanachiru.novelgame.removeads)TypeはNon ConsumableGoogle Configuration・Apple ConfigurationのPriceを設定(GoogleのPriceは日本円,Appleは選択する)Automatically inititalize Unity Purchasingを有効にする

※ 画像では誤ってProduct IDに大文字を含んでしまっていますが、全て小文字にしてください。
※ GooglePlayのPriceはドル表記ではなく、日本円表記で記載するみたいでした
またiOSのPriceと日本円の対応は以下になるそうです。(2022/2/4時)


IAP Buttonにプロダクトを設定する
購入用のIAP Buttonをインスペクターからいじります。
Product IDの設定ButtonTypeをPurchaseにTitle Text・Description Text・Price Textを用いるとIAP Catalogで設定したものを反映させることができる(別に使わなくても良い)

次にRestore用のIAP Buttonのインスペクターを設定。
Button TypeをRestoreに設定

スクリプトを実装する
IAP Buttonが押され、購入成功・失敗したときの処理を実装します。
public class IAPManager : MonoBehaviour { [SerializeField] private IAPButton buyButton; private void Awake() { // 購入ボタンを押されたとき、成功・失敗したときのイベントを登録する buyButton.onPurchaseComplete.AddListener(OnPurchaseComplete); buyButton.onPurchaseFailed.AddListener(OnPurchaseFailed); } private void OnDestroy() { // イベント解除 buyButton.onPurchaseComplete.RemoveListener(OnPurchaseComplete); buyButton.onPurchaseFailed.RemoveListener(OnPurchaseFailed); } private static void OnPurchaseComplete(Product product) { // 購入に成功した時とき Debug.Log("Remove Ads"); } private static void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason) { // 購入処理に失敗したとき Debug.Log($"{product.definition.id} failed : {failureReason}"); } }

正しく設定をすると、以下のようにフェイクの購入画面が立ち上がるはずです。

Google Playで公開される場合はRestoreボタンを非表示にする
iOSの場合はRestore用のボタンが必須ですが、逆にAndriodの場合は表示しないようにしないといけません。(Andriodの場合は起動時に自動でRestoreされる)
コードを少し追加します。
public class IAPManager : MonoBehaviour { [SerializeField] private IAPButton buyButton; [SerializeField] private IAPButton restoreButton; private void Awake() { // Androidの場合はRestore用のボタンを非表示にする if (Application.platform != RuntimePlatform.IPhonePlayer) { restoreButton.gameObject.SetActive(false); } buyButton.onPurchaseComplete.AddListener(OnPurchaseComplete); buyButton.onPurchaseFailed.AddListener(OnPurchaseFailed); } private void OnDestroy() { buyButton.onPurchaseComplete.RemoveListener(OnPurchaseComplete); buyButton.onPurchaseFailed.RemoveListener(OnPurchaseFailed); } private static void OnPurchaseComplete(Product product) { // 購入に成功した時とき Debug.Log("Remove Ads"); } private static void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason) { // 購入処理に失敗したとき Debug.Log($"{product.definition.id} failed : {failureReason}"); } }

ストアの設定
Google Play Consoleで設定を行う
まずGoogle Playに提出するGooglePlayProductCatalog.csvを生成します。
IAP Catalogを立ち上げ、App Store Export -> Google Play CSVを選択。

無事にファイルが出力できたら、Google Play Consoleを開いてください。
対象アプリ(まだ作成していない場合は新規作成)の商品 -> アプリ内アイテムを選択し、設定を行います。
また以下のようにアイテム設定ができない場合は、必要情報を記入、アプリをビルドしてクローズドテストとして公開等すればいけるはずです。

無事に以下のような画面が表示されたら、作成したGooglePlayProductCatalog.csvをアップロードすれば完了です。



App Store Connectで設定を行う
App Store ConnectのApp内課金 -> 管理からApp内課金の項目を追加していきます。
- App内の課金のタイプは非消費型
- 参照名は自由に
- 製品IDは
IAP Catalogに書いたID(com.<company name>.<game name&>.<product name>) - 価格は
IAP Catalogに記載した値段 - App情報はストアの表示名
- スクリーンショットを添付

テスト課金
Andriod
Google Play Consoleのダッシュボードを見てみると、クローズドテストに関するToDOリストがあるはずです。(なければクローズドテストにaab(apk)をアップロードすれば出てくるはず)

こちらのタスクをまずはこなしてください。
テスターを指定すれば、対象者はアプリをダウンロードできますが、テスト課金でなく実際に課金されてしまうことに注意してください。

課金テストをする場合は、すべてのアプリが見える画面にて設定 -> ライセンス テストを選択、対象者のメールアドレスを記載すればOKです。

iOS
App Store Connectの契約 / 税金 / 口座情報にて口座情報等を設定していない場合は設定します。(私はこれを忘れてずっと悩んでました)
App Store Connectを開き、上のメニューバーにあるユーザーとアクセスを選択。

sandbox -> テスターを選択し、プラスマークよりテスターを追加します。

UnityでiOSビルドを行い、xcodeprojを開きます。
あとはお手元のiPhoneにビルドを行い、テスターとして設定したアカウントにて購入を行えばOKです。
(補足) レシート検証
Codeless IAPではレシート検証が行われないとのことなので注意してください。(必ずやらなければならないわけではないです)
今回は例として、CrossPlatformValidatorを用いた簡易なローカル検証を実装してみます。
難読化
メニューバーよりWindow -> Unity IAP -> IAP Receipt Validation Obfuscatorを選択。

提示されている手法を上から実行していきます。
- Google Play Consoleを開き、対象アプリを選択、収益化のセットアップのライセンスからキーをコピー

Receipt Validation Obfuscatorにキーをペースト

Obfuscate Google Play License KeyをクリックOpen Analytics Dashboardからリンクを飛び、Google Play License Keyをペースト

購入処理のコードに追記する
公式ドキュメントにサンプルコードがあるので、そちらに少し修正を加えて利用させていただきます。
private static void OnPurchaseComplete(Product product) { bool validPurchase = true; // R.V. のないプラットフォームに有効です // Unity IAP の検証ロジックはこれらのプラットフォームにのみ含まれます。 # if UNITY_ANDROID || UNITY_IOS || UNITY_STANDALONE_OSX // エディターの難読化ウィンドウで準備した機密を持つ // バリデーターを準備します。 var validator = new CrossPlatformValidator(GooglePlayTangle.Data(), AppleTangle.Data(), Application.identifier); try { // Google Play で、result は 1 つの product ID を取得します // Apple stores で、receipts には複数のプロダクトが含まれます var result = validator.Validate(product.receipt); // 情報提供の目的で、ここにレシートをリストします Debug.Log("Receipt is valid. Contents:"); foreach (IPurchaseReceipt productReceipt in result) { Debug.Log(productReceipt.productID); Debug.Log(productReceipt.purchaseDate); Debug.Log(productReceipt.transactionID); } } catch (IAPSecurityException) { Debug.Log("Invalid receipt, not unlocking content"); validPurchase = false; } # endif if (validPurchase) { // 購入に成功した時とき Debug.Log("Remove Ads"); } }
具体的にはサンプルの以下の箇所を変更しています。
Application.bundleIdentifierは廃止されたようなので、Application.identifierに書き換えe.purchasedProduct.receiptをproduct.receiptに書き換え
またこのコードではレシートが有効であるかどうかの確認しかしていないので、レシートの中身を検証したいなどありましたら公式ドキュメント等を参照してください。
レシート検証 - Unity マニュアル