【Unity】Unity IAP : アプリ内課金実装方法【iOS】【Android】

kakin

Unityでは非常に簡単にアプリ内課金を実装することが出来ます。Asset等が有料で公開されていたりするのですが、今回はIAPというものを利用します。
無料で利用出来る上に、ひとつのコードで両OSに対応出来ます。

Unity version5.3.5を利用しています。

itunes Connectでアプリ内課金の作成

アプリ内課金を実装するためには、itunes connectでアプリを追加します。
スクリーンショット 2016-06-21 15.54.07
機能 > App内課金でプラスマークを押してアプリ内課金を作成しましょう。
先ずは課金タイプを選択します。タイプ種類は、説明の通りです。
スクリーンショット 2016-06-21 15.56.35

課金タイプを設定したら適宜情報を追加していきます。
製品IDは、アプリで呼び出す際に利用するので、わかりやすい名前にしましょう。

審査のためのスクリーンショットは、課金後の挙動がわかる画面をなんでも良いので設定しておきましょう。

スクリーンショット 2016-06-21 16.00.45 スクリーンショット 2016-06-21 16.00.55

設定ができたらSaveして、itunesConnect側での設定は完了です。

GooglePlay Developer Console(Android)でアプリ内課金の作成

Google Play Developer Consoleにアクセスして、+新しいアプリを追加からアプリ内課金を実装したいアプリを追加します。
スクリーンショット 2016-06-23 14.32.42

Androidでは、課金アイテムを作成するためには、apkファイルを追加していなければいけません。なので先に課金のコードを書いてapkを作成するか、とりあえずの段階でapkを作成するかはどちらかやりやすい方を選択してください。

apkを追加出来たら、アプリ内アイテムに移動して、+新しいアイテムを追加をクリックします。
スクリーンショット 2016-06-23 14.42.07
スクリーンショット 2016-06-23 14.43.19

そしてら、アイテムのタイプと、アイテムのIDを選択します。
スクリーンショット 2016-06-23 14.43.51

消費型も非消費型も、管理対象アイテムに当たります。定期購入型は、一定期間が過ぎたら自動で課金されるタイプです。

iosとandroidで同じ内容のアイテムがあるなら、アイテムIDも合わせておけば実装が楽になるぞ。
 
あとは、課金の情報や、価格を好きに設定しましょう。
スクリーンショット 2016-06-23 14.50.02

課金のステータスを無効から有効にするのを忘れるなよ!

Unity内でIAPのImport

参考https://unity3d.com/jp/learn/tutorials/topics/analytics/integrating-unity-iap-your-game

Unityを開いて、MenuのWindow < Servicesをクリックします。
何も設定していなければ、In-App PurchasingがOFFになっていると思います。
services_iap_off_2

In-App Purchasingをクリックしたら、課金を有効にしたいのでEnableを押下します。
enable_iap

次に、アプリが13歳未満を対象にしているかそうでないかを聞かれるので選択します。子供用のアプリなら上を、そうでないなら下にチェックを入れます。
5_copy_0

完了したら、Importします。
import_iap_package (1)

Unity内でIAPの実装

C#でプログラムを書いていきます。基本的公式のドキュメントを参考にしています。より詳しいプログラムの解説は公式のドキュメントを御覧ください。(全英語ですが…)

Purchaser.cs

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Purchasing;

namespace CompleteProject
{
	public class Purchaser : MonoBehaviour, IStoreListener
	{
		private static IStoreController m_StoreController;
		private static IExtensionProvider m_StoreExtensionProvider;

                // 消費型アイテムのID
		public static string kProductIDConsumable =    "life_max";

                // 非消費型アイテムのID   
		public static string kProductIDNonConsumable = "unlimitedLife";

		// ios AppのBundleId
		private static string kProductNameAppleSubscription =  "com.test.Game";

		// Android AppのBundleId
		private static string kProductNameGooglePlaySubscription =  "com.test.Game"; 

		void Start()
		{
			if (m_StoreController == null)
			{
				InitializePurchasing();
			}
		}

		public void InitializePurchasing() 
		{
			if (IsInitialized())
			{
				return;
			}

			var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());

			#if UNITY_ANDROID
			builder.Configure().SetPublicKey("公開鍵");
			kProductIDNonConsumable = "unlimited_life";
			#endif

			builder.AddProduct(kProductIDConsumable, ProductType.Consumable);

			builder.AddProduct(kProductIDNonConsumable, ProductType.NonConsumable);

			UnityPurchasing.Initialize(this, builder);
		}


		private bool IsInitialized()
		{
			return m_StoreController != null && m_StoreExtensionProvider != null;
		}


		public void BuyConsumable()
		{
			BuyProductID(kProductIDConsumable);
		}

		public void BuyNonConsumable()
		{
			BuyProductID(kProductIDNonConsumable);
		}



		void BuyProductID(string productId)
		{
			if (IsInitialized())
			{
				Product product = m_StoreController.products.WithID(productId);

				if (product != null && product.availableToPurchase)
				{
					Debug.Log(string.Format("Purchasing product asychronously: '{0}'", product.definition.id));
					m_StoreController.InitiatePurchase(product);
				}
				else
				{
					Debug.Log("BuyProductID: FAIL. Not purchasing product, either is not found or is not available for purchase");
				}
			}
			// Otherwise ...
			else
			{

				Debug.Log("BuyProductID FAIL. Not initialized.");
			}
		}


		public void RestorePurchases()
		{
			if (!IsInitialized ()) {
				return;
			}

			if (Application.platform == RuntimePlatform.IPhonePlayer || 
				Application.platform == RuntimePlatform.OSXPlayer)
			{

				var apple = m_StoreExtensionProvider.GetExtension();
				apple.RestoreTransactions((result) => {
			

					Debug.Log("RestorePurchases continuing: " + result + ". If no further messages, no purchases available to restore.");
				});
			}
			// Otherwise ...
			else
			{
				Debug.Log("RestorePurchases FAIL. Not supported on this platform. Current = " + Application.platform);
			}
		}
		

		//  
		// --- IStoreListener
		//
		public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
		{
			Debug.Log("OnInitialized: PASS");
			m_StoreController = controller;
			m_StoreExtensionProvider = extensions;
		}

                // 接続処理失敗の時(ネットーワーク不良等)
		public void OnInitializeFailed(InitializationFailureReason error)
		{
			Debug.Log("OnInitializeFailed InitializationFailureReason:" + error);
		}

                // 決済成功時
		public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args) 
		{
			if (String.Equals (args.purchasedProduct.definition.id, kProductIDNonConsumable, StringComparison.Ordinal)) {
				Debug.Log (string.Format ("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
			} else {
				Debug.Log("PurchaseProcessingResult Else");
			}
			return PurchaseProcessingResult.Complete;
		}

                // 決済失敗時
		public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason)
		{
			Debug.Log(string.Format("OnPurchaseFailed: FAIL. Product: '{0}', PurchaseFailureReason: {1}", product.definition.storeSpecificId, failureReason));}
	}
}

スクリプトが作り終えたら、決済が発生するボタンにアタッチします。そして、OnClickに消費型ならBuyConsumable、非消費型ならBuyNonConsumableを適用させてください。
スクリーンショット 2016-06-23 15.23.47

以上で実装は終了です。

アプリ内課金テスト方法

iosの場合

itunes connectでテストユーザーの登録をします。topからユーザーと役割に移動してください。
スクリーンショット 2016-06-23 15.32.36

SandBoxテスターを追加します。追加したら、確認メールが届いているので必ず確認してください。
スクリーンショット 2016-06-23 15.33.48

実機の設定からログインされているAppIDをSandBoxで登録したテストユーザーに変更します。
ログイン出来れば、テストが出来る状態なのでアプリを実行して見てください。

Androidの場合

Androidでテストするのは少し面倒で、β版で公開してからではないとテスト出来ません。今回は、クローズド ベータ版テストを行います。クローズドベータ版テストを設定をクリックしてください。
スクリーンショット 2016-06-23 15.49.24

テスターのリストを作成します。
スクリーンショット 2016-06-23 15.51.21

β版が公開されると、apkをアプうロードした場所にオプトインURLというものが発行されるので、そこからアプリをインストールしてテストを実行してください。
スクリーンショット 2016-06-23 15.52.23

さいごに

アプリ内課金を調べると有料のAssetsの使い方ばかり出てきて、手を出しがちですががんばれば何でも無料で出来るということを忘れないで下さい。そして、これならそんなに頑張んなくてもできるので一度挑戦してみてください!出来なかったらコメントや問い合わせフォームからでも質問してください。