課金実装 UnityIAP – 実装事例 –

はじめに

以前に「Unity IAPを試してみた」という記事がありましたが、今回は実際に導入してみた事例を紹介します。

※ Codeless IAPではないです

※ レシート検証などはサーバで行っています

※ Consumable型商品を対象としています

 

対象環境

  • Unity 2017.2.1f1
  • UnityIAP 1.15.0

ドキュメント

購入完了までの流れ

初期化

まずUntiyIAPを初期化する必要があります。

  • 初期化時に必要なもの
    • IStoreListener実装クラス
    • 各プラットフォームの商品ID
    • android supportの場合
      • googlePlayPublicKey
var googlePlayPublicKey = "ライセンスキー"
var module = StandardPurchasingModule.Instance();
var builder = ConfigurationBuilder.Instance(module);
if (!String.IsNullOrEmpty(googlePlayPublicKey))
{
    builder.Configure<IGooglePlayConfiguration>().SetPublicKey(googlePlayPublicKey);
}
// ぞれぞれの商品idを設定
builder.AddProduct("ApplicationProductId", ProductType.Consumable, new IDs
{
    { "ApplicationProductId", "StoreProductIdGoogle", "StoreProductIdApple"}
});
// thisはIStoreListener実装クラス
UnityPurchasing.Initialize(this, builder);

初期化結果によりIStoreListenerの以下が呼ばれます。

ストアの状態により、完了に時間がかかる場合があるので、

完了待ちすると進行不能になる可能性があるので注意が必要です。

また、未処理のトランザクションがある場合は初期化完了後からIStoreListener経由でトランザクションが流れ始めるので

そのまま購入処理を実装するとユーザ登録前やインゲーム中など予期せぬタイミングで購入処理が走るのでハンドリングする必要があります。

// 成功
void OnInitialized(IStoreController controller, IExtensionProvider extensions);
// 失敗
void OnInitializeFailed(InitializationFailureReason error);

商品情報の取得

初期化完了後のIStoreControllerのproductsにvalidate済みの商品一覧があるのでこちらを使用しました。

metadataにそのユーザアカウントでローカライズされた通貨、金額で取得できます。

foreach (var product in _controller.products.all)
{
    // $0.99, ¥120...
    UnityEngine.Debug.Log(product.metadata.localizedPriceString);
}

購入

課金実装のフローに関しては、基本的なものと変わりません。

基本的な購入フロー(成功)

image

購入要求

IStoreControllerからUnityIAP経由でStoreに購入リクエストします。

第2引数のpayloadはandroidのレシート内のdeveloperPayloadの内容です。

今回はレシート検証用途でuseridを入れています。

var payload = "userId";
// validateされた商品一覧から対象の商品オブジェクトを取得
var product = _controller.products.WithID("productId");
if (product != null && product.availableToPurchase)
{
    // 購入リクエスト
    _controller.InitiatePurchase(product, payload);
}

購入リクエスト後PW入力などのフローを踏み、

購入が完了するとIStoreListenerの以下が呼ばれます。

// 成功時 (図でいう⑥)
// 初期化時、未処理のトランザクションの場合もこちらから流れてくるので実装注意
public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
{
    // アプリ側購入処理
    // トランザクションid : args.purchasedProduct.transactionID
    // レシート(json文字列) : args.purchasedProduct.receipt
    return PurchaseProcessingResult.Pending;
}
// 失敗時
public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason)
{
    // cancelなどのストア側での購入失敗
}

成功時にProcessPurchaseが呼ばれるので

サーバに必要情報をリクエストしアプリケーション側の購入処理に入ります、図⑥

一度Pendingを返却しておきます。

サーバ側のレシート検証などは割愛します。

アプリケーション側の購入処理がすべて正常に終わったことをストア側に通知します。図⑩

ConfirmPendingPurchaseを呼ばない場合は未処理として購入トランザクションは残ります。

// 購入完了
_controller.ConfirmPendingPurchase(product);

基本的な購入処理は以上です。

実装時トラブル

Case1

タイトルから次の画面への遷移時に課金初期化に時間がかかり進行不能

こちらは前述の通り、課金初期化が完了するまで遷移できない作りになっていたため、

課金初期化完了待たずに遷移できるように対応し、

課金メニュー表示時に初期化が終わっていなければ商品を表示しない対応を行いました。

商品の表示に関しては、世界対応でローカライズ表記が必要な場合を考慮し、ゲームサーバのMasterDataからの表示はしませんでした。

Case2

完全にエラーハンドリングできない場合があった

当初は課金実装時にUnityIAP 1.6.0を使用していました。

テスト時、プラットフォーム側からの「このApp内課金はすでに購入済みです」エラーが出る際に

OnPurchaseFailedが呼ばれない不具合がありました。

購入要求後、アプリ側はローディング待ち表示でユーザの操作を制御していたため、進行不能になっていました。

この問題はUnityIAPのversion1.11.0のリリースにて修正され正常にOnPurchaseFailedが呼ばれ解消されました。

この後、テスト・リリース後にOnPurchaseFailed呼ばれない問題は起きていません。

まとめ

最初はハンドリングできていない部分があったりなど不安定なのかなと思いましたが、

最終的にどうにかなったので使えなくないっといった感想です。

ネイティブコードを書かなくていいのでコスト面では楽ですが、

すでに実績のあるネイティブプラグインの実装がある場合は、そちらを利用したほうがよいかもしれません。

参考リンク