PlayFab (レガシ エコノミー)、Unity IAP、Android の概要
Important
Economy v1 API はメンテナンス モードであり、新機能は受け取らず、バグ修正のみを受け取ります。 v1 API は、当面維持されます。 次のバージョンの PlayFab エコノミーの詳細については、「エコノミー v2 の概要」を参照してください。
このチュートリアルでは、PlayFab、Unity + IAP サービス、Android 課金 API を使用して、アプリ内購入 (IAP) を設定する方法を説明します。
開始の前に
特に異なるサービスがどのように統合および連動するかを把握していない場合、IAP の設定作業は退屈な場合があります。
以下の図は、Android 課金 API と PlayFab がどのように連動し、顧客に安定した IAP 体験を提供するかを示しています。
まず、PlayMarket で製品 ID と価格を設定します。 最初は、すべての製品が特定化されていません。プレイヤーが購入可能なデジタル エンティティであり、PlayFab プレイヤーにとって何の意味もありません。
これらのエンティティを有益化するには、PlayFab アイテム カタログでミラー化する必要があります。 これによって、特定化されていないエンティティがバンドル、コンテナー、個別のアイテムとなります。
それぞれに次のような独自の側面があります:
- タイトル
- 説明
- Tags
- types
- Images
- ビヘイビア によって特徴付けられます。
ID を共有することで、これらすべてがマーケットの製品にリンクされます。
購入可能な実際の金額のアイテムにアクセスする最適な方法は、GetCatalogItemsと GetStoreItems を使用することです。 これらは自由通貨ストアで使用される API メソッドと同じであるため、プロセスには馴染みがあるはずです。
アイテムの ID は、PlayFab と外部 IAP システム間のリンクです。 したがって、IAP サービスにアイテム ID を渡します。
この時点で、購入プロセスが開始します。 プレイヤーが IAP インターフェイスと通信し、購入が成功すると、領収書を取得できます。
PlayFab はその後その領収書を検証し、購入を登録して、購入アイテムをプレイヤーに付与します。
これが、IAP 統合の仕組みの概要です。以下の例は、実際の動作の大部分を示しています。
クライアント アプリケーションを設定する
このセクションでは、非常に簡素なアプリケーションを構成し、PlayFab、UnityIAP、 Android 課金 API を使用した IAP をテストする方法を説明します。
前提条件:
- Unity プロジェクト。
- PlayFab SDK がインポートされ、タイトルに対して動作するように構成されていること。
まずは UnityIAP を設定します。
- [サービス] に移動します。
- [サービス] タブが選択されていることを確認してください。
- 自分の Unity サービスのプロフィールまたは組織を選択します。
- [作成] を選択します。
- 次に、[アプリ内購入 (IAP)] サービスに移動します。
[Simplify cross-platform IAP (プラットフォーム間 IAP の簡素化)] の切り替えをオンにして、サービス を有効にします。
次に、[Continue (続ける)] を選択します。
プラグインの一覧ページが表示されます。
- [Import (インポート)] を選択します。
Uすべてのプラグインがインポートされる段階まで Unity のインストールとインポート手順を続行します。
- プラグインが揃っていることを確認してください。
- 次に AndroidIAPExample.cs という名前の新しいスクリプトを作成します。
AndroidIAPExample.cs
以下に示すコードが含まれています (詳しくは、コードのコメントを参照してください)。
using PlayFab;
using PlayFab.ClientModels;
using PlayFab.Json;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Purchasing;
public class AndroidIAPExample : MonoBehaviour, IStoreListener {
// Items list, configurable via inspector
private List<CatalogItem> Catalog;
// The Unity Purchasing system
private static IStoreController m_StoreController;
// Bootstrap the whole thing
public void Start() {
// Make PlayFab log in
Login();
}
public void OnGUI() {
// This line just scales the UI up for high-res devices
// Comment it out if you find the UI too large.
GUI.matrix = Matrix4x4.TRS(new Vector3(0, 0, 0), Quaternion.identity, new Vector3(3, 3, 3));
// if we are not initialized, only draw a message
if (!IsInitialized) {
GUILayout.Label("Initializing IAP and logging in...");
return;
}
// Draw menu to purchase items
foreach (var item in Catalog) {
if (GUILayout.Button("Buy " + item.DisplayName)) {
// On button click buy a product
BuyProductID(item.ItemId);
}
}
}
// This is invoked manually on Start to initiate login ops
private void Login() {
// Login with Android ID
PlayFabClientAPI.LoginWithAndroidDeviceID(new LoginWithAndroidDeviceIDRequest() {
CreateAccount = true,
AndroidDeviceId = SystemInfo.deviceUniqueIdentifier
}, result => {
Debug.Log("Logged in");
// Refresh available items
RefreshIAPItems();
}, error => Debug.LogError(error.GenerateErrorReport()));
}
private void RefreshIAPItems() {
PlayFabClientAPI.GetCatalogItems(new GetCatalogItemsRequest(), result => {
Catalog = result.Catalog;
// Make UnityIAP initialize
InitializePurchasing();
}, error => Debug.LogError(error.GenerateErrorReport()));
}
// This is invoked manually on Start to initialize UnityIAP
public void InitializePurchasing() {
// If IAP is already initialized, return gently
if (IsInitialized) return;
// Create a builder for IAP service
var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance(AppStore.GooglePlay));
// Register each item from the catalog
foreach (var item in Catalog) {
builder.AddProduct(item.ItemId, ProductType.Consumable);
}
// Trigger IAP service initialization
UnityPurchasing.Initialize(this, builder);
}
// We are initialized when StoreController and Extensions are set and we are logged in
public bool IsInitialized {
get {
return m_StoreController != null && Catalog != null;
}
}
// This is automatically invoked automatically when IAP service is initialized
public void OnInitialized(IStoreController controller, IExtensionProvider extensions) {
m_StoreController = controller;
}
// This is automatically invoked automatically when IAP service failed to initialized
public void OnInitializeFailed(InitializationFailureReason error) {
Debug.Log("OnInitializeFailed InitializationFailureReason:" + error);
}
// This is automatically invoked automatically when purchase failed
public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason) {
Debug.Log(string.Format("OnPurchaseFailed: FAIL. Product: '{0}', PurchaseFailureReason: {1}", product.definition.storeSpecificId, failureReason));
}
// This is invoked automatically when successful purchase is ready to be processed
public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs e) {
// NOTE: this code does not account for purchases that were pending and are
// delivered on application start.
// Production code should account for such case:
// More: https://docs.unity3d.com/ScriptReference/Purchasing.PurchaseProcessingResult.Pending.html
if (!IsInitialized) {
return PurchaseProcessingResult.Complete;
}
// Test edge case where product is unknown
if (e.purchasedProduct == null) {
Debug.LogWarning("Attempted to process purchase with unknown product. Ignoring");
return PurchaseProcessingResult.Complete;
}
// Test edge case where purchase has no receipt
if (string.IsNullOrEmpty(e.purchasedProduct.receipt)) {
Debug.LogWarning("Attempted to process purchase with no receipt: ignoring");
return PurchaseProcessingResult.Complete;
}
Debug.Log("Processing transaction: " + e.purchasedProduct.transactionID);
// Deserialize receipt
var googleReceipt = GooglePurchase.FromJson(e.purchasedProduct.receipt);
// Invoke receipt validation
// This will not only validate a receipt, but will also grant player corresponding items
// only if receipt is valid.
PlayFabClientAPI.ValidateGooglePlayPurchase(new ValidateGooglePlayPurchaseRequest() {
// Pass in currency code in ISO format
CurrencyCode = e.purchasedProduct.metadata.isoCurrencyCode,
// Convert and set Purchase price
PurchasePrice = (uint)(e.purchasedProduct.metadata.localizedPrice * 100),
// Pass in the receipt
ReceiptJson = googleReceipt.PayloadData.json,
// Pass in the signature
Signature = googleReceipt.PayloadData.signature
}, result => Debug.Log("Validation successful!"),
error => Debug.Log("Validation failed: " + error.GenerateErrorReport())
);
return PurchaseProcessingResult.Complete;
}
// This is invoked manually to initiate purchase
void BuyProductID(string productId) {
// If IAP service has not been initialized, fail hard
if (!IsInitialized) throw new Exception("IAP Service is not initialized!");
// Pass in the product id to initiate purchase
m_StoreController.InitiatePurchase(productId);
}
}
// The following classes are used to deserialize JSON results provided by IAP Service
// Please, note that JSON fields are case-sensitive and should remain fields to support Unity Deserialization via JsonUtilities
public class JsonData {
// JSON Fields, ! Case-sensitive
public string orderId;
public string packageName;
public string productId;
public long purchaseTime;
public int purchaseState;
public string purchaseToken;
}
public class PayloadData {
public JsonData JsonData;
// JSON Fields, ! Case-sensitive
public string signature;
public string json;
public static PayloadData FromJson(string json) {
var payload = JsonUtility.FromJson<PayloadData>(json);
payload.JsonData = JsonUtility.FromJson<JsonData>(payload.json);
return payload;
}
}
public class GooglePurchase {
public PayloadData PayloadData;
// JSON Fields, ! Case-sensitive
public string Store;
public string TransactionID;
public string Payload;
public static GooglePurchase FromJson(string json) {
var purchase = JsonUtility.FromJson<GooglePurchase>(json);
purchase.PayloadData = PayloadData.FromJson(purchase.Payload);
return purchase;
}
}
- Code という名前の新しいゲームオブジェクトを作成します。
- そこに
AndroidIAPExample
コンポーネントを追加します (2 段階)。 - そこに
AndroidIAPExample
コンポーネントを追加します。 - 必ずシーンを保存してください。
最後に、[Build Settings (ビルド設定)] に移動します。
- シーンが [Scenes In Build (ビルド中のシーン)] エリアに追加されたことを確認します。
- [Android] プラットフォームが選択されていることを確認します。
- [プレイヤーの設定] エリアに移動します。
- [パッケージ名] を割り当てます。
注意
PlayMarket の競合を避けるために、独自のパッケージ名を付けてください。
最後に、通常どおりにアプリケーションをビルドし、APK が安全で適切であることを確認します。
まだテストをする方法はありません。 最初に、以下のセクションで説明するように、PlayMarket と PlayFab を構成する必要があります。
IAP に向けて PlayMarket アプリケーションを設定する
このセクションでは、PlayMarket アプリケーションで IAP を有効にする方法の詳細を説明します。
注意
アプリケーション自体の設定は、このチュートリアルの範囲外です。 すでにアプリケーションがあり、少なくともアルファ版のリリースとして公開できるように構成されていると想定しています。
便利なメモ
- 実行するには APK をアップロードする必要があります。 前のセクションで構築した APK を使用してください。
- APK のアップロードを求められたら、アルファ版またはベータ版のアプリケーションとしてそれをアップロードし、IAP サンドボックスを有効にします。
- [コンテンツの規則] の構成には、アプリケーションでの IAP の有効化方法に関する質問が含まれます。
- PlayMarket では、パブリッシャーによる IAP の使用またはテストが許容されません。そのため、テストの目的では別の Google アカウントを選択し、アルファ版/ベータ版のビルドのためのテスターとして追加してください。
アプリケーションのビルドを公開したら、以下を実行します。
- メニューから [In-app products (アプリ内製品)] を選択します。
- [Merchant Account (販売者アカウント)] を求められた場合は、1 つをリンクするか作成します。
- [Add New Product (新しい製品の追加)] を選択します。
- 次に示す画面で、[Managed Product (管理されている製品)] を選択します。
- わかりやすい [製品 ID] を付けます。
- [続行] を選択します。
PlayMarket で、[Title (タイトル)] (1) と [Description (説明)] (2) を入力するように求められます。 ただし、今回はあまり使用しません。
データ アイテムのデータのみを PlayFab サービスから取得し、一致する ID のみを必要とします。
- さらにスクロールし、[Add a price (価格を追加)] を選択します。
- 有効な価格を入力します (各国/地域に応じて、価格がどのように変換されるかに注意してください)。
- [Apply (適用)] を選択します。
- 最後に、画面の一番上までスクロールし、アイテムのステータスを [Active (アクティブ)] に変更します。
これでアプリの構成は完了ですが、いくつかの微調整が必要です。 まず、ライセンス キーを保存します (これは、PlayFab と PlayMarket とをリンクするために役立ちます)。
- メイン メニューの [Services & APIs (サービスと API)] に移動します。
- 次に、キー の Base64 バージョンを見つけて保存します。
次の手順では、IAP のテストを有効にします。 アルファ版およびベータ版のビルドに対してサンドボックスは自動的に有効になりますが、アプリのテストのために承認されたアカウントを設定する必要があります。
- [ホーム] に移動します。
- 左側のメニューで、[Account details (アカウントの詳細)] を見つけて選択します。
- [License Testing (ライセンスのテスト)] エリアを見つけます。
- テスト アカウントが一覧にあることを確認します。
- [License Test Response (ライセンス テストの応答)]が RESPOND_NORMALLY に設定されていることを確認します。
設定を適用することを忘れないでください。
PlayMarket 側の統合は、この時点で設定する必要があります。
PlayFab タイトルを設定する
最後の手順では、PlayFab タイトルを構成して製品を反映させ、Google 課金 API と統合します。
- [アドオン] を選択します。
- 次に、Google 追加コンテンツを選択します。
- [パッケージ ID] に入力します。
- 前のセクションで取得した [Google App License Key (Google アプリのライセンス キー)] を入力します。
- [Install Google (Google のインストール)] を選択して変更を確定します。
次の手順では、PlayFab でゴールデン ソードのアイテムを反映させます。
- [Economy (エコノミー)] を選択します。
- [カタログ] サブタブが選択されていることを確認します。
- [New Catalog (新しいカタログ)] を選択します。
- カタログ バージョン名を指定します。
- [Save Catalog (カタログの保存)] を選択します。
カタログにアイテムがない場合、自動的に削除されます。 そのため、すべてのカタログには「One」というアイテムが事前に追加されています。
常に新しいアイテムを作成できますが、簡単にするために、「One」アイテムを変更します。
- アイテムを選択して、PlayMarket の ID に合わせて [アイテム ID] を編集します。
- 次に、[表示名] を編集し、[説明] を追加します。
注意
このデータは、PlayMarket アイテム タイトルと 説明 とは何の関係もないことに注意してください。完全に独立しています。
- アイテムに [価格] を追加します。
- [Save Item (アイテムの保存)] を選択して変更を確定します。
注意
このチュートリアルでは、IAP は主に実際のお金での購入に言及しています。 そのため、特別な実際のお金の通貨である「RM」を使用します。 PlayFab の金額は US セントで定義されます。
- [アイテム ID] リストでアイテムを確認します。
これで、PlayFab タイトルの設定が完了しました。
テスト
テスト目的で、アルファ版/ベータ版 リリースを使用してアプリをダウンロードします。
- テスト アカウントと実際の Android デバイスを使用してください。
- アプリを開始すると、IAP が初期化されたことがわかり、アイテムを表す 1 つのボタンが表示されます。
- そのボタンを選択します。
IAP の購入が開始されます。 正常に購入が完了するまで、Google Play の指示に従います。
最後に、PlayFab ゲーム マネージャーのダッシュボードでタイトルに移動し、[New Events (新しいイベント)] を見つけます。
これは、購入が正常に提供、検証され、PlayFab エコシステムに接続されたことを意味します。
これで、UnityIAP と Android 課金 API を PlayFab アプリケーションに正常に統合できました。