Version: 2017.3
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 Receipt を取得する方法を提供しています。通常、ローカルストレージに現在キャッシュされているレシートがない場合に使用されます: SKReceiptRefreshRequest

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

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

/// <summary>
/// OnInitialized の IStoreListener 実装
/// </summary>
public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
{
    extensions.GetExtension<IAppleExtensions> ().RefreshAppReceipt (receipt => {
        // リクエストが成功したら、このハンドラーが呼び出されます
        // レシートは最新のアプリレシート
        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);
    });
}

When the purchase is approved or rejected, your store’s normal ProcessPurchase or OnPurchaseFailed listener methods are invoked.

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

Sometimes consumable Ask to Buy purchases don’t show up in the App Receipt, in which case you cannot validate them using that receipt. However, iOS provides a Transaction Receipt that contains all purchases, including Ask to Buy. Access the most recent Transaction Receipt string for a given Product using IAppleExtensions.

注意: トランザクションレシートは 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 はアプリケーションを起動してトランザクションを完了するか、アプリケーションがインストールされていない場合はそれをダウンロードするようにユーザーに促します。

The IAppleConfiguration SetApplePromotionalPurchaseInterceptor callback method intercepts Apple promotional purchases. Use this callback to present parental gates, send analytics events, or perform other functions before sending the purchase to Apple. The callback uses the Product that the user attempted to purchase. You must call IAppleExtensions.ContinuePromotionalPurchases() to continue with the promotional purchase. This will initiate any queued-up payments.

If you do not set the callback, promotional purchases go through immediately and call ProcessPurchase with the result.

注意: 他のプラットフォームでこれらの 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 のビルド設定で Mac のスタンドアロン用に Mac App Store validation にチェックを入れておく必要があります。

アプリをビルドしたら、info.plist のバンドル ID とバージョン設定を更新する必要があります。.app ファイル上で右クリックして パッケージの内容を表示 を選択し、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–25–01 編集レビュー を行ってパブリッシュされたページ
  • Ask to Buy、トランザクションレシート、レシートの遮断は 2017.3 で追加NewIn20173
Android アプリ内課金 (IAP) ストアにおける、ストア間を横断してプロダクトをインストールする場合の問題
ユニバーサル Windows プラットフォーム