Version: 2020.2
言語: 日本語
Android アプリ内課金 (IAP) ストアにおける、ストア間を横断してプロダクトをインストールする場合の問題
ユニバーサル Windows プラットフォーム

iOS & Mac App Store

拡張機能

App Store のレシートを読む

アプリストアのレシート はデバイスのローカルストレージに保管されており、以下のようにして読むことができます。

var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
string receipt = builder.Configure<IAppleConfiguration>().appReceipt;

支払い制限の確認

アプリ内課金はデバイスの設定で制限されている場合があります。以下のようにして、確認できます。

var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
bool canMakePayments = builder.Configure<IAppleConfiguration>().canMakePayments;

トランザクションのリストア

Apple のプラットフォーム上で、前回のトランザクションを取得するには、パスワードを入力する必要があります。そのため、アプリケーションにそのためのボタンを表示する必要があります。この過程で、ユーザーが既に所有しているどのアイテムに対しても IStoreListenerProcessPurchase メソッドを呼び出すことができます。

/// <summary>
/// OnInitialized の IStoreListener 実装  
/// </summary>
public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
{
    extensions.GetExtension<IAppleExtensions> ().RestoreTransactions (result => {
        if (result) {
            // なにかがリストアされたというわけではありません
            // 単にリストアの処理が終了したということです
        } else {
            // リストア失敗
        }
    });
}

App Store のレシートのリフレッシュ

Apple はサーバーから App Store の新しいレシートを取得する方法 SKReceiptRefreshRequest を提供しています。これは通常、ローカルストレージに現在レシートがキャッシュされていない場合に使用されます。

この方法にはパスワードが必要だということに気を付けてください。

Unity IAP では以下のようにこのメソッドを使用します。

/// <summary>
/// OnInitialized の IStoreListener 実装 
/// </summary>
public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
{
    extensions.GetExtension<IAppleExtensions> ().RefreshAppReceipt (receipt => {
        // リクエストが成功したら、このハンドラーが呼び出されます
        // レシートは最新の App Store レシート
        Console.WriteLine(receipt);
    },
    () => {
        // リクエストが失敗したら、このハンドラーが呼び出されます。
        // 例えば、ネットワークが使用不可であったり、
        // ユーザーが誤ったパスワードを入力した場合など。
    });
}

Ask to Buy (承認と購入のリクエスト)

iOS 8 からペアレンタルコントロールに Ask to Buy (承認と購入のリクエスト) と呼ばれる新しい機能が追加されました。

Ask to Buy を使った購入では、保護者の承認を待機します。このとき、Unity IAP は以下のようにアプリケーションに通知を送信します。

/// Unity IAP の初期化が終了すると、以下が呼び出されます。
public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
{
    extensions.GetExtension<IAppleExtensions>().RegisterPurchaseDeferredListener(product => {
        Console.WriteLine(product.definition.id);
    });
}

Sandbox の App Store で Ask-to-Buy シミュレーションを使用可能にする

下のサンプルは Sandbox の App Store で Ask-to-Buy シミュレーションを使用可能にするために IAppleExtensions にアクセスする方法を示しています。

using UnityEngine;
using UnityEngine.Purchasing;

public class AppleSimulateAskToBuy : MonoBehaviour {
    public void SetSimulateAskToBuy(bool shouldSimulateAskToBuy) {
        if (Application.platform == RuntimePlatform.IPhonePlayer) {
            IAppleExtensions extensions = IAPButton.IAPButtonStoreManager.Instance.ExtensionProvider.GetExtension<IAppleExtensions>();
            extensions.simulateAskToBuy = shouldSimulateAskToBuy;
        }
    }
}

購入が承認/拒否されると、ストアの通常の ProcessPurchase / OnPurchaseFailed リスナーメソッドが呼び出されます。

トランザクションレシート

消耗品の Ask to Buy 購入がアプリのレシートに表示されないことがあります。その場合は、レシートを使用してそれらを検証できません。ただし、iOSは、Ask to Buy を含むすべての購入のトランザクションレシートを提供します。IAppleExtensions を使用して、Product(プロダクト) の最新のトランザクションレシートの文字列にアクセスします。

注意: トランザクションレシートは Mac ビルドには使用できません。Mac ビルドでトランザクションレシートを要求すると、空の文字列が返されます。

# if UNITY_PURCHASING

using System;
using UnityEngine;
using UnityEngine.Purchasing;

public class AskToBuy : MonoBehaviour, IStoreListener
{
    // Unity IAP オブジェクト
    private IStoreController m_Controller;
    private IAppleExtensions m_AppleExtensions;

    public AskToBuy ()
    {
        var builder = ConfigurationBuilder.Instance (StandardPurchasingModule.Instance ());
        builder.AddProduct ("100_gold_coins", ProductType.Consumable, new IDs {
            { "100_gold_coins_google", GooglePlay.Name },
            { "100_gold_coins_mac", MacAppStore.Name }
        });

        UnityPurchasing.Initialize (this, builder);
    }

    /// <summary>
    /// Unity IAP の初期化が終了すると以下が呼び出されます
    /// </summary>
    public void OnInitialized (IStoreController controller, IExtensionProvider extensions)
    {
        m_Controller = controller;
        m_AppleExtensions = extensions.GetExtension<IAppleExtensions> ();

        // Apple プラットフォームでは、Apple の Ask to Buy 機能を使った待機後の購入を処理するに対応する必要があります
        // Apple 以外のプラットフォームでは、 何も効力がありません。OnDeferred が呼び出されることはありません。 
        m_AppleExtensions.RegisterPurchaseDeferredListener (OnDeferred);
    }

    /// <summary>
    /// 購入が終了すると以下が呼び出されます
    /// </summary>
    public PurchaseProcessingResult ProcessPurchase (PurchaseEventArgs e)
    {
        if (Application.platform == RuntimePlatform.IPhonePlayer ||
            Application.platform == RuntimePlatform.tvOS) {
            string transactionReceipt = m_AppleExtensions.GetTransactionReceiptForProduct (e.purchasedProduct);
            Console.WriteLine (transactionReceipt);
            // 検証のために、トランザクションレシートをサーバーに送信します
        }    
        return PurchaseProcessingResult.Complete;
    }

    /// <summary>
    /// Unity IAP に解決できない初期化エラーが発生すると呼び出されます
    ///
    /// インターネットが使用できない場合は、これは呼び出されません。 
    ///Unity IAP は使用可能になるまで、初期化を試みます。 
    /// </summary>
    public void OnInitializeFailed (InitializationFailureReason error)
    {
    }

    /// <summary>
    /// 購入が失敗すると呼び出されます
    /// </summary>
    public void OnPurchaseFailed (Product i, PurchaseFailureReason p)
    {
    }

    /// <summary>
    /// iOS 特有
    ///購入が子供によってリクエストされ、保護者の承認が必要な場合に
    /// Apple の Ask to Buy 機能の一部として
    /// 呼び出されます
    ///
    ///購入が承認か拒否をされると、
    /// 通常のイベントが発生します
    /// </summary>
    /// <param name="item">item</param>
    private void OnDeferred (Product item)
    {
        Debug.Log ("Purchase deferred: " + item.definition.id);
    }
}

# endif // UNITY_PURCHASING

アプリストアのレシートとは異なり、トランザクションレシートをローカルで検証することはできません。代わりに、検証のためにレシートの文字列をリモートサーバーに送信する必要があります。 すでにリモートサーバーを使用してアプリストアのレシートを検証している場合は、トランザクションレシートを同じ Apple エンドポイントに送信して JSON 応答を受信します。

JSON 応答の例

{
    "receipt": {
        "original_purchase_date_pst": "2017-11-15 15:25:20 America/Los_Angeles",
        "purchase_date_ms": "1510788320209",
        "unique_identifier": "0ea7808637555b2c633eb07aa1cb0894c821a6f9",
        "original_transaction_id": "1000000352597239",
        "bvrs": "0",
        "transaction_id": "1000000352597239",
        "quantity": "1",
        "unique_vendor_identifier": "01B57C2E-9E91-42FF-9B0D-4983175D6694",
        "item_id": "1141751870",
        "original_purchase_date": "2017-11-15 23:25:20 Etc/GMT",
        "product_id": "100.gold.coins",
        "purchase_date": "2017-11-15 23:25:20 Etc/GMT",
        "is_trial_period": "false",
        "purchase_date_pst": "2017-11-15 15:25:20 America/Los_Angeles",
        "bid": "com.unity3d.unityiap.demo",
        "original_purchase_date_ms": "1510788320209"
    },
    "status": 0
}

Apple のプロモーション購入の遮断

Appleは、開発者がアプリケーションのプロダクトページを通して ゲーム内購入 を促進することを許可しています。従来のアプリ内購入とは異なり、Apple のプロモーション購入は iOS および tvOS の App Store から直接開始されます。その後、App Store はアプリケーションを起動してトランザクションを完了するか、アプリケーションがインストールされていない場合はそれをダウンロードするようにユーザーに促します。

IAppleConfigurationSetApplePromotionalPurchaseInterceptorCallback メソッドは、Apple のプロモーション購入を遮ります。購入を Apple に送信する前に、このコールバックを使用してペアレンタルゲートを設けたり、分析イベントを送信したり、他の機能を実行することができます。コールバックは、ユーザーが購入しようとした Product (プロダクト) を使用します。プロモーション購入を続行するには、IAppleExtensions.ContinuePromotionalPurchases() を呼び出す必要があります。これにより、待機中の支払いが開始されます。

コールバックを設定しない場合は、プロモーション購入は即座に通過し、ProcessPurchase が呼びだされます。

注意: 他のプラットフォームでこれらの API を呼び出しても効果はありません。

private IAppleExtensions m_AppleExtensions;

public void Awake() {
    var module = StandardPurchasingModule.Instance();
    var builder = ConfigurationBuilder.Instance(module);
        // iOS と tvOS で、App Store から直接行われるプロモーション購入を
        // 遮ることができます。
        // Apple 以外のプラットフォームでは、 何も効力がありません。
        // OnPromotionalPurchase は呼び出されることはありません。
    builder.Configure<IAppleConfiguration>().
     SetApplePromotionalPurchaseInterceptorCallback(OnPromotionalPurchase);
    Debug.Log("Setting Apple promotional purchase interceptor callback");
}

public void OnInitialized(IStoreController controller, IExtensionProvider extensions) {
    m_AppleExtensions = extensions.GetExtension<IAppleExtensions>();
    foreach (var item in controller.products.all) {
        if (item.availableToPurchase) {                
            // ユーザーの App Store でこれらすべてのプロダクトを表示します
            m_AppleExtensions.SetStorePromotionVisibility(item, AppleStorePromotionVisibility.Show);
        }
    }
}

private void OnPromotionalPurchase(Product item) {
    Debug.Log("Attempted promotional purchase: " + item.definition.id);
    // プロモーション購入が検知されます。 
    // このイベントは、例えばペアレンタルゲートなどを
    // 設けることによって処理します。
    // ここでは、デモの目的で、購入を継続する前に 5秒間待機することにします。
    StartCoroutine(ContinuePromotionalPurchases());
}

private IEnumerator ContinuePromotionalPurchases() {
    Debug.Log("Continuing promotional purchases in 5 seconds");
    yield return new WaitForSeconds(5);
    Debug.Log("Continuing promotional purchases now");
    m_AppleExtensions.ContinuePromotionalPurchases (); // iOS と tvOS のみ
}

テスト

Apple ストアでテストをするには ITunes Connect でテストアカウントを作成し、そのテストアカウントで iTunes に接続しなければいけません。

iOS デバイスやラップトップで App Store からサインアウトしてアプリを起動すると、課金やリストアを行うときにログインするよう求められます。

NoProductsAvailable が原因で初期化に失敗した時は、以下の項目を確認してください。

  • iTunes Connect で作成したプロダクト ID が Unity IAP のプロダクト ID と一致しているかどうか。
  • アプリ内課金の設定が iTunes Connect で有効になっているかどうか。
  • プロダクトが iTunes Connect で販売可能な状態になっているかどうか。
  • iTunes Connect で作成したプロダクトが購入可能になるまで数時間かかることがあります。
  • iTunes Connect の最新のデベロッパ契約に同意して、有効な振込先の情報を記入しているかどうか。

Mac App Store

Unity の Build Settings > Player Settings で Mac の Mac App Store Validation にチェックを入れておく必要があります。

アプリケーションをビルドしたら、バンドル ID とバージョン文字列で info.plist ファイルを更新します。.app ファイル上で右クリックし、show package contents を選択し、info.plist ファイルを探し、CFBundleIdentifier 文字列をアプリケーションのバンドル ID に更新します。

その後、アプリケーションに署名をし、パッケージ化し、インストールします。そのための操作は Mac OSX 上のターミナルで行います。

codesign -f --deep -s "3rd Party Mac Developer Application: " your.app/Contents/Plugins/unitypurchasing.bundle
codesign -f --deep -s "3rd Party Mac Developer Application: " your.app
productbuild --component your.app /Applications --sign "3rd Party Mac Developer Installer: " your.pkg

バンドルに署名するには、Contents.meta ファイルが存在する場合、まずこれを削除する必要があります。your.app/Contents/Plugins/unitypurchasing.bundle/Contents.meta

正しくパッケージをインストールするには、新しく作成したパッケージを実行する前にパッケージ化していない .app ファイルを削除しておく必要があります。

そして、アプリケーションフォルダーからアプリを起動します。初回は iTunes アカウントの詳細を入力するよう求められるので、iTunes Connect のテストアカウントでログインします。これでサンドボックス環境でテスト購入を行うことができます。


  • 2018–05–30 公開ページ
  • Ask to Buy、トランザクションレシート、レシートの遮断は 2017.3 で追加NewIn20173
Android アプリ内課金 (IAP) ストアにおける、ストア間を横断してプロダクトをインストールする場合の問題
ユニバーサル Windows プラットフォーム