概要
Unity In-App Purchasing(IAP)v5 は v4 からのメジャーアップデートで、初期化フロー・商品取得・購入処理・購入確定(Confirm)・レシート検証の考え方が大きく変わりました。
この記事では、v4 の UnityPurchasing.Initialize / IStoreListener 中心の実装から、v5 の StoreController / ProductService / PurchaseService による段階的な購入フローへ移行する際の要点(何が変わったか、どこを直すべきか、Confirm とサーバー検証をどう設計するか)をまとめました。
この記事をまとめた背景として、Unity Discussionsの記事より、IAP 4 のサポート終了と IAP 5 への移行推奨について以下のように案内されています。
Unity IAP 4: End of Support Announcement(英語記事より翻訳して引用)
2026年6月8日付で Unity IAP 4 は非推奨(deprecated)となり、今後は IAP 5 が移行先になります。
2026年6月8日以降、IAP 4 には新機能追加・バグ修正・カスタマーサポートが提供されず、Package Manager 上でも deprecated として表示され始めます。
ただし、既存の IAP 4 プロジェクトが即座に動作しなくなるわけではなく、パッケージ自体も削除されません。
Unity 6.3 より後の次期 LTS(2026年12月頃想定)では、エディターに同梱されるパッケージ解決のデフォルトが IAP 4 ではなく IAP 5 を指す予定です。
そのため、既存の IAP 4 プロジェクトでも、次の開発サイクルで IAP 5 への移行を計画することが推奨されています。
そのため、IAP 4 はすぐに使用不可になるわけではありませんが、2026年6月8日以降は新機能・修正・サポートが止まり、次期 LTS 以降では IAP 5 がデフォルト解決される見込みであるため、継続運用するプロジェクトでは IAP 5 への移行計画が必要です。
主要な変更点
Unity In-App Purchasing(IAP)v5 は、v4 からのメジャーアップデートとして、初期化・商品取得・購入処理・購入確定・レシート取得/検証まわりの API と責務分担が大きく変更されています。
v5 では、従来の UnityPurchasing.Initialize / IStoreListener 中心の実装から、StoreController のイベントと非同期処理を使って、ストア接続・商品取得・購入取得・保留中購入処理を明示的に制御する構成へ移行します。
1. アーキテクチャの変更
v4 では UnityPurchasing.Initialize と IStoreListener を中心に、初期化・商品取得・購入イベントをまとめて扱っていました。
v5 では責務が分離され、主に以下のサービスを使う構成になります。
-
StoreController- ストア接続
- 商品取得
- 購入開始
- 購入完了確認
-
ProductService- 商品情報の取得
-
PurchaseService- 購入処理・購入状態の監視
これにより、購入フローの各ステップをより細かく制御できます。
2. 初期化フローの変更
v4 では ConfigurationBuilder を作成し、UnityPurchasing.Initialize(listener, builder) を呼ぶ形でした。
v5 では、概ね以下の流れになります。
-
StoreControllerを生成 - 購入関連イベントを登録
- ストアへ接続
- 商品情報を取得
- 保留中の購入を処理
v5 では「初期化時にストア接続・商品取得・購入取得までまとめて完了する」構成ではなく、接続・商品取得・購入取得を分けて実行し、それぞれの成功/失敗イベントを処理する実装へ変わります。
3. 商品定義・商品取得の変更
v4 では ConfigurationBuilder.AddProduct(...) で商品を登録していました。
v5 では、ProductDefinition / ProductFetchRequest などを使って商品情報を取得します。
影響点:
- 商品 ID と商品タイプの定義方法が変わる
- 商品一覧の取得処理を明示的に呼ぶ必要がある
- 取得成功・失敗時のハンドリングを v5 のイベント/API に合わせて書き換える必要がある
4. 購入処理の変更
v4 では IStoreController.InitiatePurchase(product) を使っていました。
v5 では StoreController.PurchaseProduct(...) など、v5 の購入 API に置き換わります。
また、購入結果の受け取り方も ProcessPurchase ベースから、v5 の購入イベント・購入状態に合わせた実装へ変更されます。
5. 購入完了処理の変更
v4 では ProcessPurchase の戻り値として PurchaseProcessingResult.Complete / Pending を返すことで、購入完了または保留を表現していました。
v5 では、購入を完了させるために 明示的に Confirm 処理を行う形になります。
重要な考え方:
- サーバー検証やアイテム付与が完了するまでは購入を Confirm しない
- 検証・付与が成功した後に Confirm する
- 未 Confirm の購入は pending purchase として扱われる
そのため、従来 ProcessPurchase 内で完結していた処理は、v5 では 購入受信 → レシート検証 → サーバー反映 → Confirm という流れに整理する必要があります。
レシート検証まわりの変更
1. レシート取得元が Product.receipt から Order.Info.Receipt へ変わる
Unity のアップグレードガイドでは、IAP v5 でレシートを取得する場合、従来の Product.receipt ではなく、Order.Info.Receipt を使うよう案内されています。
また、Apple App Store のレシート検証は deprecated とされており、Apple では StoreKit 2 の jwsRepresentation を使う方向が示されています。
2. サーバーサイド検証が推奨
IAP v5 では、購入処理を受け取ったあと、必要に応じて Order.Info.Receipt や Apple の OrderInfo.Apple.jwsRepresentation など、ストアごとに必要な情報を使って検証します。
有償通貨やアイテム付与をサーバー側で管理している場合は、クライアントで受け取った購入情報をサーバーへ送信し、検証と付与を完了してから購入を Confirm する構成が安全です。
推奨フロー:
- Unity IAP v5 で購入完了イベントを受け取る
- 購入情報・レシート情報を自社サーバーへ送信
- サーバー側で Apple / Google に問い合わせて検証
- 検証成功後、サーバー側でアイテム・通貨・サブスク状態などを反映
- クライアント側で購入を Confirm
3. Apple / Google で扱うレシート情報に注意
Unity のアップグレードガイド上では、ストアごとに扱う情報が異なります。
- Google
- Google Play のレシート検証では
Order.Info.Receiptを使用
- Google Play のレシート検証では
- Apple
- Apple App Store の従来のレシート検証は deprecated
- StoreKit 2 の
jwsRepresentationを使う方向が示されている - Unity Discussion では、StoreKit 1 を利用するケースでは JWS ではなくレシートアクセスが必要になる旨も補足されている
つまり、v5 移行時は「v4 と同じレシート JSON をそのままサーバーへ投げれば十分」とは限らず、各ストアで必要な値・検証方式を確認する必要があります。
Unity Discussion の補足ポイント
Unity Discussion のアップグレードチュートリアルでは、v4 から v5 にパッケージを更新すると、型定義や interface の廃止により コンパイルエラーが発生することが説明されています。
主な示唆:
- 単純なパッケージ更新だけでは移行できない
-
IStoreListener/IStoreController/ConfigurationBuilder前提の実装は大きく書き換えが必要 - 初期化、商品取得、購入、購入完了確認を段階的に v5 API へ置き換える必要がある
- 既存の独自 IAP Manager 実装では、
UnityIAPServices.StoreController()を中心にした実装へ移行する必要がある
既存 InAppPurchase Manager への影響
影響が大きい箇所
既存の InAppPurchase Manager が v4 ベースの場合、特に以下の箇所が影響を受けます。
-
IStoreListenerの実装 UnityPurchasing.Initialize(...)ConfigurationBuilder.AddProduct(...)-
IStoreController/IExtensionProvider ProcessPurchase(...)OnPurchaseFailed(...)PurchaseProcessingResult.Pending / CompleteCrossPlatformValidator- レシート JSON の取り扱い
- サーバー側の
check_receiptAPI に渡すパラメータ
改修方針
1. IAP Manager の責務を整理する
v5 では、以下の責務を明確に分けると実装しやすくなります。
- IAP 初期化・ストア接続
- 商品情報取得
- 購入開始
- 購入成功・失敗イベント処理
- レシートをサーバーへ送信
- サーバー検証結果の反映
- 購入 Confirm
- pending purchase の再処理
2. 購入完了はサーバー検証後に Confirm する
アイテム付与や通貨反映がサーバー側で行われるプロジェクトでは、以下の順序を守るのが安全です。
3. レシート形式の差分を確認する
既存サーバーが v4 のレシート JSON を前提にしている場合、v5 で取得できる購入情報・レシート情報と互換性があるか確認が必要です。
確認ポイント:
- Product ID
- Transaction ID / Order ID
- Store 名
- Receipt / Payload
- Signature
- Apple / Google それぞれの検証に必要な値
- サンドボックス環境でのレスポンス差分
実装の改修例
以下は、v4 ベースの実装から v5 ベースの実装へ移行する際に、特に変わる箇所を比較するためのサンプルです。
実際の API 名や型は利用する Unity IAP v5 のバージョンにより差分が出る可能性があるため、実装方針を理解するための擬似コード寄りのサンプルとして扱ってください。
v4 旧実装のイメージ
v4 では IStoreListener を実装し、初期化・商品取得・購入結果・購入失敗を同じクラスで受け取る構成が一般的でした。
using UnityEngine;
using UnityEngine.Purchasing;
public class InAppPurchaseManagerV4 : MonoBehaviour, IStoreListener
{
private IStoreController storeController;
private IExtensionProvider extensionProvider;
private const string ProductIdGem480 = "dev.example.gem480";
public void Initialize()
{
var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
builder.AddProduct(ProductIdGem480, ProductType.Consumable);
UnityPurchasing.Initialize(this, builder);
}
public void BuyGem480()
{
if (storeController == null)
{
Debug.LogError("IAP is not initialized.");
return;
}
storeController.InitiatePurchase(ProductIdGem480);
}
public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
{
storeController = controller;
extensionProvider = extensions;
Debug.Log("IAP initialized.");
}
public void OnInitializeFailed(InitializationFailureReason error)
{
Debug.LogError($"IAP initialize failed: {error}");
}
public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
{
var product = args.purchasedProduct;
// v4 ではここでレシートを取り出し、サーバー検証へ投げる実装が多い
var receipt = product.receipt;
// サーバー検証が非同期の場合、Pending を返す
SendReceiptToServer(
product.definition.id,
receipt,
onSuccess: () =>
{
// サーバー側で付与成功後に ConfirmPendingPurchase
storeController.ConfirmPendingPurchase(product);
});
return PurchaseProcessingResult.Pending;
}
public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason)
{
Debug.LogError($"Purchase failed: {product.definition.id}, reason: {failureReason}");
}
private void SendReceiptToServer(string productId, string receipt, System.Action onSuccess)
{
// TODO: 既存の check_receipt API へ送信
// 成功したら onSuccess を呼ぶ
}
}
v4 の特徴
-
UnityPurchasing.Initialize(...)が初期化の中心 -
IStoreListenerにイベントが集約される -
ConfigurationBuilder.AddProduct(...)で商品を登録 -
ProcessPurchase(...)の戻り値でComplete/Pendingを制御 -
ConfirmPendingPurchase(...)で保留中の購入を完了する
v5 新実装のイメージ
v5 では、ストア接続、商品取得、購入、購入確認をより明示的に分けて実装します。
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Purchasing;
public class InAppPurchaseManagerV5 : MonoBehaviour
{
private StoreController storeController;
private const string ProductIdGem480 = "dev.example.gem480";
public async void Initialize()
{
storeController = UnityIAPServices.StoreController();
// 購入成功・失敗・保留中購入などのイベントを登録
storeController.OnPurchasePending += OnPurchasePending;
storeController.OnProductsFetched += OnProductsFetched;
storeController.OnPurchasesFetched += OnPurchasesFetched;
storeController.OnPurchaseConfirmed += OnPurchaseConfirmed;
storeController.OnPurchaseFailed += OnPurchaseFailed;
// 1. ストアへ接続
await storeController.Connect();
// 2. 商品情報を取得
var productDefinitions = new List<ProductDefinition>
{
new ProductDefinition(ProductIdGem480, ProductType.Consumable),
};
storeController.FetchProducts(productDefinitions);
}
private void OnProductsFetched(List<Product> products)
{
// 3. 商品取得後、未完了・復元対象の購入を取得する
storeController.FetchPurchases();
Debug.Log("IAP v5 initialized.");
}
private void OnPurchasesFetched(Orders orders)
{
// restored purchase / pending purchase / completed order を確認する
// 非消費型・サブスクリプションの権利確認もここで行う
}
public void BuyGem480()
{
if (storeController == null)
{
Debug.LogError("IAP is not initialized.");
return;
}
storeController.PurchaseProduct(ProductIdGem480);
}
private async void OnPurchasePending(PendingOrder pendingOrder)
{
// v5 では、購入を受け取った後にサーバー検証を行い、
// 成功してから Confirm する流れを明示的に実装する
var productId = pendingOrder.Info.PurchasedProductInfo.productId;
var receipt = pendingOrder.Info.Receipt;
var result = await SendReceiptToServer(productId, receipt);
if (result.IsSuccess)
{
// サーバー検証とアイテム付与が成功した後に Confirm
storeController.ConfirmPurchase(pendingOrder);
}
else
{
// Confirm しなければ pending として残り、次回起動時などに再処理できる
Debug.LogError($"Receipt validation failed: {productId}");
}
}
private void OnPurchaseConfirmed(Order order)
{
Debug.Log($"Purchase confirmed: {order.Info.TransactionID}");
}
private void OnPurchaseFailed(FailedOrder failedOrder)
{
Debug.LogError($"Purchase failed: {failedOrder.Info}");
}
private async System.Threading.Tasks.Task<ReceiptValidationResult> SendReceiptToServer(
string productId,
string receipt)
{
// TODO: v5 で取得できる receipt / payload / transactionId などを
// 既存の check_receipt API に合わせて送信する
//
// サーバー側では Apple / Google の検証 API に問い合わせ、
// 成功した場合のみアイテム・通貨を付与する
await System.Threading.Tasks.Task.CompletedTask;
return new ReceiptValidationResult { IsSuccess = true };
}
private class ReceiptValidationResult
{
public bool IsSuccess;
}
}
旧実装と新実装の比較
| 項目 | v4 旧実装 | v5 新実装 |
|---|---|---|
| 初期化 | UnityPurchasing.Initialize(...) |
UnityIAPServices.StoreController() を取得し、Connect() を実行 |
| 商品登録 | ConfigurationBuilder.AddProduct(...) |
ProductDefinition を作成して商品取得 |
| 商品取得 | 初期化時にまとめて取得されるイメージ |
FetchProducts(...) で明示的に取得 |
| 購入開始 | storeController.InitiatePurchase(...) |
storeController.PurchaseProduct(...) |
| 購入成功 | ProcessPurchase(...) |
新規購入は OnPurchasePending、復元/取得済み購入は OnPurchasesFetched
|
| 購入完了 |
PurchaseProcessingResult.Complete または ConfirmPendingPurchase(...)
|
サーバー検証後に ConfirmPurchase(...)
|
| 保留中購入 | PurchaseProcessingResult.Pending |
Confirm されていない購入として再処理 |
| レシート検証 |
product.receipt と CrossPlatformValidator またはサーバー検証 |
Order.Info.Receipt や Apple の jwsRepresentation など、ストアごとの情報を使う |
実装時のポイント
1. v5 では「購入成功 = 付与完了」ではない
OnPurchasePending を受け取った時点では、まだサーバー検証やアイテム付与が完了していません。
そのため、以下の順序を守る必要があります。
- 購入イベントを受け取る
- レシートをサーバーへ送信する
- サーバーで Apple / Google に検証する
- サーバーでアイテム・通貨を付与する
- クライアントへ成功を返す
- Unity IAP 側で Confirm する
2. Confirm は最後に行う
サーバー検証や付与処理が完了する前に Confirm すると、途中で失敗した場合に購入の再処理が難しくなります。
そのため、v5 の実装では **Confirm は「サーバー側の付与完了後」**に限定するのが安全です。
3. 既存 check_receipt API の入力を見直す
既存 API が v4 の product.receipt だけを前提にしている場合、v5 で取得する Order.Info.Receipt や Apple の jwsRepresentation などに合わせて、入力仕様を見直す必要があります。
最低限、以下をログ出力して、v4 と v5 の差分を確認するのが良いです。
Debug.Log($"ProductId: {productId}");
Debug.Log($"TransactionId: {transactionId}");
Debug.Log($"Receipt: {receipt}");
Debug.Log($"Store: {storeName}");
Debug.Log($"Payload: {payload}");
4. pending purchase の再処理を必ず確認する
アプリ終了、通信失敗、サーバーエラーなどにより、Confirm 前の購入が残る可能性があります。
v5 移行時は、通常購入だけでなく以下もテスト対象に含めるべきです。
- 購入直後にアプリを落とす
- サーバー検証 API を一時的に失敗させる
- 通信エラー状態で購入する
- 次回起動時に pending purchase が再処理されるか確認する
移行時のチェックリスト
- Unity IAP パッケージを v5 系へ更新
- 既存の v4 API 利用箇所を洗い出す
-
IStoreListenerベースの実装を v5 API へ置き換える - 商品定義・商品取得処理を v5 方式へ変更
- 購入開始処理を v5 方式へ変更
- 購入成功・失敗イベント処理を v5 方式へ変更
- pending purchase の扱いを実装
- サーバー検証完了後に Confirm する流れへ変更
-
CrossPlatformValidator依存をなくす - Apple / Google それぞれのレシート検証値を確認
-
既存
check_receiptAPI の入力仕様を v5 に合わせて確認・改修 - Sandbox / TestFlight / Google Play Internal testing で購入・復元・再起動時 pending をテスト
- サブスクリプションがある場合、更新・解約・期限切れ・復元の検証を追加
注意点
コンパイルエラーは正常な移行作業の一部
v4 から v5 へ更新すると、型定義や interface の廃止により既存コードがコンパイルエラーになる可能性が高いです。これは単純なメソッド名変更ではなく、IAP の構造自体が変わっているためです。
レシート検証はクライアント完結にしない
有償通貨や課金アイテムをサーバー側で管理している場合、クライアント側だけで検証・付与を完結させる構成は避けるべきです。
IAP v5 では、Google Play は Order.Info.Receipt、Apple は StoreKit 2 の jwsRepresentation など、ストアごとの検証情報を確認して設計する必要があります。
Confirm のタイミングに注意
購入を Confirm する前にアプリが終了した場合、購入は pending として再処理対象になります。
逆に、サーバー検証やアイテム付与が完了する前に Confirm してしまうと、失敗時のリカバリが難しくなります。
そのため、Confirm は必ず以下の後に行います。
- レシート検証成功
- サーバー側で付与成功
- クライアント側で成功レスポンス確認
まとめ
Unity IAP v5 への移行は、単なるパッケージ更新ではなく、IAP Manager の再設計に近いメジャーアップデートです。
特に重要なのは以下です。
- v4 の
UnityPurchasing.Initialize/IStoreListener中心の構成から、v5 のサービス分離型 API へ移行する - 商品取得・購入・購入完了処理を明示的に管理する
- 購入完了はサーバー検証と付与完了後に Confirm する
-
Product.receipt前提の実装を見直し、Order.Info.Receiptや Apple のjwsRepresentationなど v5 の購入情報に合わせる - Apple / Google で必要な検証情報の違いを確認する
- 既存の
check_receiptAPI が v5 の購入情報に対応できるか検証する
既存プロジェクトでは、まず現在の InAppPurchase Manager が v4 API のどこに依存しているかを洗い出し、初期化・商品取得・購入・検証・Confirm の順に段階的に置き換えるのが安全です。
引用
-
Unity Docs: Upgrade from IAP v4 to v5
https://docs.unity.com/en-us/iap/upgrade-to-iap-v5
https://docs.unity.com/en-us/iap/upgrade-to-iap-v5#receipt-validation
-
Unity Discussion: IAP tutorial series: Upgrading from Unity IAP4 to IAP5
https://discussions.unity.com/t/iap-tutorial-series-upgrading-from-unity-iap4-to-iap5/1723788
-
Unity Discussion: Unity IAP 4: End of Support Announcement
https://discussions.unity.com/t/unity-iap-4-end-of-support-announcement/1722241
-
Unity Docs: Receipt validation