Documentation

Upgrade from IAP version 4 to version 5

Follow this guide to migrate from Unity In-App Purchasing version 4 to version 5.
Read time 14 minutesLast updated 10 days ago

In-App Purchasing (IAP) version 5 (v5) introduces significant architectural improvements that give you better control over each step of your connection and purchase flow. While migration requires that you make significant updates to your existing IAP implementation, this guide provides step-by-step instructions and code samples to help you transition.
Note
From SDK v5.4, Unity IAP processes Developer Data as part of the Developer Data framework. As such, it is your responsibility to manage user consent using the UnityConsent module.
Note
Starting in IAP version 5.3.0, you can access IAP AI skills to help migrate your project from version 4 to version 5.
At the bottom of Project Settings > Services > In-App Purchasing, choose one of the following options:
  • Open Skills Folder: Opens the folder that contains the skill files. Use this option to copy or install the IAP AI Skill in your preferred AI tool.
  • Install to Claude Code: Adds the skill directly to Claude Code. This installs a skill named
    in-app-purchases
    .
After you install the skill, run
in-app-purchases
from your AI tool, and prompt it to migrate your project from version 4 to version 5.

Overview of changes

The following table summarizes the changes in IAP v5 and what you need to update in your implementation. For more information on the benefits of upgrading, refer to the Why you should upgrade to Unity In-App Purchasing (IAP) v5.x support article.

Change

Required update

The Apple App Store implementation moves from StoreKit 1 to StoreKit 2, and Google Play Billing Library moves from version 8.3.0 to version 9.0.0.Review native store SDK changes
Initialization is split into separate async calls for store connection, product fetching, and purchase fetching.Replace UnityPurchasing.Initialize()
ConfigurationBuilder
is removed. Products are now defined via a
CatalogProvider
or a list of
ProductDefinitions
.
Replace ConfigurationBuilder
Store Extensions are replaced with Store Extended Services.Replace ConfigurationBuilder
IDetailedStoreListener
and
IStoreListener
are replaced with optional event handlers on
StoreController
and the individual store services.
Replace IDetailedStoreListener and IStoreListener
IStoreController
is replaced with
StoreController
, which can be fetched at any time via
UnityIAPServices.StoreController()
.
Replace IStoreController
The
ProcessPurchase
callback is replaced with
OnPurchasePending
for new purchases and
OnPurchasesFetched
for restored purchases.
Replace purchase flow
RestoreTransactions
has moved from Store Extensions to
StoreController
and
PurchaseService
.
Restore transactions
Entitlement checks are now event-based, using
FetchPurchases
or
CheckEntitlement
.
Replace entitlement checks
SubscriptionManager
is obsolete. Subscription details are now available from
Order.Info.PurchasedProductInfo
.
Replace SubscriptionManager
SubscriptionManager.UpdateSubscription
is obsolete. Subscription tier changes are now specific to each store.
Replace SubscriptionManager
Apple App Store receipt validation is deprecated. Google Play receipt validation now uses
Order.Info.Receipt
.
Update receipt validation
CodelessIAPStoreListener.initializationComplete
is replaced with
CodelessIAPStoreListener.IsInitialized()
.
Update Codeless IAP

Review native store SDK changes

IAP v5 updates the native store SDKs that the package wraps. These updates change store behavior, receipt formats, and store-specific fields, regardless of how you restructure your C# code. Review them before you migrate.
Important
The move to StoreKit 2 changes the format of Apple purchase data. On a device that uses StoreKit 2, a back-end service or third-party SDK that expects the StoreKit 1 app receipt stops receiving iOS purchase data, and most backend services and third-party SDKs report no error when this happens. Check each of these integrations as described in Check third-party analytics and attribution SDKs.
The following table lists the native SDK version that each IAP version uses:

Store

IAP v4 (4.15.1)

IAP v5 (5.4)

Apple App StoreStoreKit 1StoreKit 2
Google Play StoreGoogle Play Billing Library 8.3.0Google Play Billing Library 9.0.0

Update your Apple App Store implementation

IAP v5 uses StoreKit 2 instead of StoreKit 1 on Apple platforms. StoreKit 2 requires the following minimum operating system versions:

Platform

Minimum version for StoreKit 2

iOS15.0
iPadOS15.0
tvOS15.0
macOS12.0
visionOS1.0
IAP v5.0 doesn't support devices below these versions, because it removed StoreKit 1 entirely. As of IAP v5.1.0, a device below these versions automatically falls back to StoreKit 1. The one exception is devices running visionOS, which never fall back automatically. Unless you force StoreKit 1, visionOS uses StoreKit 2.
To use StoreKit 1 regardless of the operating system version, set
StoreKitSelector.forceStoreKit1
to
true
before you initialize Unity IAP. This setting overrides the automatic selection on every Apple platform, including visionOS.
Note
StoreKitSelector.forceStoreKit1
exists to help you test StoreKit 1. Unity doesn't recommend changing this setting in production.
Replace the following Apple-specific fields and APIs:

IAP v4 (StoreKit 1)

IAP v5 (StoreKit 2)

Product.receipt
, which contains the app receipt as its payload
Order.Info.Receipt
for the unified receipt, or
Order.Info.Apple.jwsRepresentation
for the JSON Web Signature (JWS) of a single transaction.
The App Store
verifyReceipt
endpoint for server-side validation
Send
Order.Info.Apple.jwsRepresentation
to the App Store Server API. Keep your existing app receipt path for devices that use StoreKit 1.
Local validation with
CrossPlatformValidator
Not needed under StoreKit 2, which validates transactions locally. Still required for Apple on a device that uses StoreKit 1.
Receipt obfuscation for AppleNot needed under StoreKit 2. Still required for Apple local validation on a device that uses StoreKit 1, through Services > In-App Purchasing > Receipt Validation Obfuscator.
Product.appleProductIsRestored
Obsolete. Restored purchases arrive through
OnPurchasesFetched
.
Note
Order.Info.Apple.AppReceipt
still returns the app receipt, but it can be
null
, for example, after a user reinstalls your application. Use
jwsRepresentation
for new implementations on StoreKit 2.

Update your Google Play Store implementation

IAP v5.4 uses Google Play Billing Library 9.0.0. IAP v5.0 uses version 8.0.0, and IAP v4.15.1 used version 8.3.0.
Replace the following Google Play-specific fields and APIs:

IAP v4

IAP v5

Product.receipt
Order.Info.Receipt
.
Product.hasReceipt
Refer to Replace entitlement checks.
The
payload
argument on purchase methods (developer payload)
No replacement. Google Play Billing removed developer payloads in version 3.
GooglePlayProrationMode
GooglePlayReplacementMode
. Refer to Change a subscription tier.
Local receipt validation with
CrossPlatformValidator
is still supported for Google Play.

Check for removed platform support

IAP v5 removes support for the following platforms and modules:

Removed

Details

Universal Windows Platform (Windows Store)Removed in IAP v5.0.
Unity Distribution PortalRemoved from the IAP package in v5.0. You can still implement the Unity Distribution Portal SDK directly, or as a custom store.
Amazon AppstoreNot supported in IAP v5.
Legacy Analytics (
com.unity.modules.unityanalytics
)
Removed in IAP v5.0. Use Unity Analytics (
com.unity.services.analytics
) instead.
IAP v5.4 requires Unity Editor 2022.3 or later, and IAP v5.0 requires Unity Editor 2021.3 or later.

Check third-party analytics and attribution SDKs

External analytics, attribution, and subscription-management SDKs often read Apple purchase data directly rather than through Unity IAP. These SDKs report no error when they stop receiving purchase data, so a loss of iOS purchase events isn't obvious until your reporting is already incomplete.
If you pass Apple purchase data to an SDK or a back-end service, update that handoff before you release:
  1. Pass
    Order.Info.Apple.jwsRepresentation
    where you previously passed the app receipt, and keep your app receipt path for a device where
    StoreKitSelector.UseStoreKit1()
    returns
    true
    .
  2. Check the SDK's documentation for the configuration that StoreKit 2 requires.
  3. Make a test purchase on a device, then confirm that the purchase appears in the SDK's reporting.
Note
Order.Info.Apple.jwsRepresentation
is
null
on a device that uses StoreKit 1, because only StoreKit 2 provides a JWS-signed transaction. Use
Order.Info.Apple.AppReceipt
for devices that use StoreKit 1, or require the minimum operating system versions in the preceding table so that every device uses StoreKit 2.

Replace UnityPurchasing.Initialize()

As of IAP v5, you can initialize the Unity IAP package with greater flexibility. You can connect to the store, fetch products, and handle purchases independently and asynchronously. This approach can help you identify and resolve issues that might block a successful initialization.
Note
In versions 4 and earlier, the Unity IAP package connects to the store, fetches products, and fetches purchases synchronously at package initialization. The package reports a successful initialization only after completing all these steps.
Follow these steps to replace the behavior of
UnityPurchasing.Initialize()
:
  1. Call and await
    StoreController.Connect()
    :
    • When this call completes, IAP is connected to your current app store.
  2. Call
    FetchProducts()
    :
    • You can add products to an instance of
      CatalogProvider
      , similar to adding products to the
      ConfigurationBuilder
      . You can also pass a list of
      ProductDefinitions
      to
      ProductService.FetchProducts()
      or
      StoreController.FetchProducts()
      . For more information, refer to Code sample of new initialization process.
    • Your
      OnProductsFetched
      event handler is called when the request has successfully completed. If no specified products could be fetched,
      OnProductsFetchFailed
      is invoked.
  3. Call
    FetchPurchases()
    after your products have been successfully fetched:
    • Your
      OnPurchasesFetched
      event handler is called when the request has successfully completed. The
      Orders
      object contains a filterable collection of all deferred, pending, and completed orders returned by the app store. On failure,
      OnPurchasesFetchFailed
      is invoked.
Note
By default, calling
FetchPurchases
invokes
OnPurchasePending
for any pending purchases which have not yet been handled in the session. You can disable this behaviour with
StoreController.ProcessPendingOrdersOnPurchasesFetched(false)
.

Code sample of new initialization process using StoreController

The following example shows how to initialize IAP v5:
StoreController m_StoreController; async void InitializeIAP() { m_StoreController = UnityIAPServices.StoreController(); m_StoreController.OnPurchasePending += OnPurchasePending; await m_StoreController.Connect(); m_StoreController.OnProductsFetched += OnProductsFetched; m_StoreController.OnPurchasesFetched += OnPurchasesFetched; var initialProductsToFetch = new List<ProductDefinition> { new(goldProductId, ProductType.Consumable), new(diamondProductId, ProductType.Consumable) }; m_StoreController.FetchProducts(initialProductsToFetch); } void OnProductsFetched(List<Product> products) { // Handle fetched products m_StoreController.FetchPurchases(); } void OnPurchasesFetched(Orders orders) { // Process purchases, for example, check for entitlements from completed orders }

Replace IDetailedStoreListener and IStoreListener

IAP v5 no longer requires an implementation of
IDetailedStoreListener
or
IStoreListener
for handling purchases or initialization. Replace functionality previously handled by
IDetailedStoreListener
by attaching event handlers to
StoreController
, or to the individual
ProductService
,
PurchaseService
, and
StoreService
services.
For an example of adding event handlers to
StoreController
, refer to Code sample of new initialization process.
Note
You can add or remove event handlers at any time, however certain event handlers are recommended before calling certain methods. Unity IAP displays a warning when you call a function before the recommended event handlers have been attached.
To migrate, replace each
IDetailedStoreListener
function with the following:

IDetailedStoreListener
functions

IAP v5 replacement

OnPurchaseFailed(Product, PurchaseFailureDescription)
Add an event handler to
StoreController.OnPurchaseFailed(FailedOrder)
or
PurchaseService.OnPurchaseFailed(FailedOrder)
OnInitialized(IStoreController controller, IExtensionProvider extensions)
Continue execution after
StoreController.Connect()
or
StoreService.Connect()
completes.
OnInitializeFailed(InitializationFailureReason error)
Add event handlers to
StoreService.OnStoreDisconnected(StoreConnectionFailureDescription)
,
ProductService.OnProductsFetchFailed(ProductFetchFailed)
, and
PurchaseService.OnPurchasesFetchFailed(PurchasesFetchFailureDescription)
. You can also add these event handlers via
StoreController
.
OnInitializeFailed(InitializationFailureReason error, string message = null)
Add an event handler to
StoreController.OnStoreDisconnected(StoreConnectionFailureDescription)
or
StoreService.OnStoreDisconnected(StoreConnectionFailureDescription)
PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
Add an event handler to
StoreController.OnPurchasePending(PendingOrder)
or
PurchaseService.OnPurchasePending(PendingOrder)

Replace ConfigurationBuilder

Replace the following
ConfigurationBuilder
functions with the following behaviours:

ConfigurationBuilder
functions

IAP v5 replacement

ConfigurationBuilder.AddProduct()
Rather than calling
ConfigurationBuilder.AddProduct()
, you can add products to an instance of a
CatalogProvider
via
CatalogProvider.AddProduct()
or
CatalogProvider.AddProducts()
. To fetch products, you can call
CatalogProvider.FetchProducts
and pass a callback, such as
UnityIAPServices.DefaultProduct().FetchProductsWithNoRetries
. For more information on products and
CatalogProvider
, refer to Create a catalog in the Editor. You can also create
ProductDefinitions
and pass them directly to a
FetchProducts()
call on
StoreController
or
ProductService
.
IAppleConfiguration
and
IGoogleConfiguration
You can find functionality in
IAppleConfiguration
and
IGoogleConfiguration
in the Extended Store Services, such as
AppleStoreExtendedService
.
Note
If you fetch a Store Extended Service on an unsupported platform (for example, fetching
AppleStoreExtendedService
in the Editor or on Android), the service will be null. Always check that the service isn't null before adding event handlers or changing settings.

Replace IStoreController

Replace
IStoreController
with
StoreController
.
StoreController
exposes IAP functionality similar to
IStoreListener
, but you can fetch
StoreController
by calling
UnityIAPServices.StoreController()
at any time.
Replace these methods from
IStoreController
with the following methods from
StoreController
:

IStoreController

StoreController

Notes

products
GetProducts
GetProducts
returns a list of products.
InitiatePurchase
Purchase
or
PurchaseProduct
Purchase
functions no longer have a
payload
argument. Support for developer payloads was removed in Google Billing v3.
FetchAdditionalProducts
FetchProducts
FetchProducts
is also used to fetch initial products.
ConfirmPendingPurchase
ConfirmPurchase
ConfirmPurchase
requires a
PendingOrder
.

Replace purchase flow

To initiate a purchase, call
Purchase()
or
PurchaseProduct()
on
StoreController
or
PurchaseService
. To confirm a pending purchase, call
StoreController.ConfirmPurchase(PendingOrder)
or
PurchaseService.ConfirmPurchase(PendingOrder)
with the
PendingOrder
you want to confirm. For more information about the purchase flow, refer to Purchases.
In IAP v4 and earlier, the
ProcessPurchase
method automatically handled all purchase events. In IAP v5, purchase handling uses the following callbacks:
  • OnPurchasePending
    : Called for new purchases.
  • OnPurchasesFetched
    : Called for restored purchases.
Use your existing
ProcessPurchase
logic within both of these new callbacks.
Confirmation can also fail.
OnPurchaseConfirmed
delivers an
Order
that is either a
ConfirmedOrder
or a
FailedOrder
, so check the type of the order before you treat a purchase as complete. For more information, refer to Handle a failed confirmation.

Restore transactions

RestoreTransactions
has moved from Store Extensions to
StoreController
and
PurchaseService
.
Confirmed purchases are automatically restored when you call
FetchPurchases()
or through calling
CheckEntitlement()
.

Replace entitlement checks

To check a user's entitlement to a product, IAP relies on events. You can check the entitlements for all your fetched products by calling
FetchPurchases
and handling the
OnPurchasesFetched
event. To check the entitlement for a single product, call
CheckEntitlement
and handle the
OnCheckEntitlement
event.
Product.hasReceipt
has no direct replacement. To determine ownership, read the
ConfirmedOrders
and
PendingOrders
collections on the
Orders
that
OnPurchasesFetched
delivers, or call
CheckEntitlement
. For more information, refer to Determine purchase status.

Replace SubscriptionManager

The
SubscriptionManager
class is obsolete in IAP v5. It did two unrelated jobs, and each one migrates differently:
  • Reading subscription information.
  • Moving a user between subscription tiers.

Read subscription information

In IAP v4, you construct a
SubscriptionManager
from a purchased product, then call
getSubscriptionInfo()
. In IAP v5, the order carries this information. Each order exposes a list of
IPurchasedProductInfo
objects through
Order.Info.PurchasedProductInfo
, and each of those objects has a
subscriptionInfo
property. For a product that isn't a subscription,
subscriptionInfo
is
null
.
Replace the following calls:

IAP v4

IAP v5

new SubscriptionManager(product, introJson).getSubscriptionInfo()
Order.Info.PurchasedProductInfo[n].subscriptionInfo
new SubscriptionManager(receipt, id, introJson).getSubscriptionInfo()
new SubscriptionInfoHelper(receipt, id, introJson).GetSubscriptionInfo()
SubscriptionInfoHelper
is the direct class replacement when you need to build a
SubscriptionInfo
from a receipt rather than read one from an order. It takes the same constructor arguments as
SubscriptionManager
.
The following example reads the subscription status of a product from a confirmed order in the
OnPurchasesFetched
event handler:
void OnPurchasesFetched(Orders orders) { foreach (var order in orders.ConfirmedOrders) { var purchasedProductInfo = order.Info.PurchasedProductInfo .FirstOrDefault(productInfo => productInfo.productId == vipSubscriptionId); if (purchasedProductInfo?.subscriptionInfo?.IsSubscribed() == Result.True) { // Grant access to the subscription content } } }
Note
The
SubscriptionInfo
methods use PascalCase in IAP v5. For example,
isSubscribed()
is now
IsSubscribed()
. These methods still return a
Result
value rather than a boolean, so compare the return value to
Result.True
. For the full list of methods, refer to SubscriptionInfo class reference.

Change a subscription tier

SubscriptionManager.UpdateSubscription
,
SubscriptionManager.UpdateSubscriptionInGooglePlayStore
, and
SubscriptionManager.UpdateSubscriptionInAppleStore
are obsolete, and there's no cross-store replacement. Replace each call according to the store:

Store

IAP v5 replacement

Google Play StoreCall
GooglePlayStoreExtendedPurchaseService.UpgradeDowngradeSubscription(Order, Product, GooglePlayReplacementMode)
with the user's current subscription order and the new product. For how each mode affects billing, refer to Set the replacement mode in the Google Play Billing documentation.
Apple App StorePurchase the new product with
StoreController.PurchaseProduct
or
PurchaseService.PurchaseProduct
. This matches IAP v4 behavior, because
UpdateSubscriptionInAppleStore
never performed a store-side change: it only invoked the callback that you passed to it, and the Unity sample wired that callback to
InitiatePurchase
.
The
developerPayload
argument has no replacement in IAP v5.
Note
The
GooglePlayProrationMode
enum is obsolete. Use
GooglePlayReplacementMode
instead.

Fetch additional products

Call
FetchProducts
on either
StoreController
or
ProductService
with a list of additional products to trigger your
OnPurchasesFetched
event handler. Alternatively, you can call
FetchProducts
on a
CatalogProvider
instance. For an example of fetching products via
ProductService
, refer to Code sample of new initialization process. For an example of calling
FetchProducts
on
CatalogProvider
, refer to Create a catalog in the Editor.

Receipt validation

Receipt validation for Apple App Store receipts has been deprecated. Receipt validation is supported only for Google Play Store. To fetch receipts, use
Order.Info.Receipt
from
Order
instead of
Product.Receipt
.
For more information about how to confirm that a purchase is legitimate, refer to Transaction verification.
Note
Move from StoreKit 1 receipts to StoreKit 2 jwsRepresentation as soon as possible to improve reliability, security, performance, and ultimately deliver a better experience for your users.
Note
Use OrderInfo.Apple.jwsRepresentation for server-side validation.

Codeless IAP specifics

Replace the following
CodelessIAPStoreListener
function with the following:

CodelessIAPStoreListener
function

IAP v5 replacement

CodelessIAPStoreListener.initializationComplete
CodelessIAPStoreListener.IsInitialized()