# 从 IAP 版本 4 升级到版本 5

> 按照本指南从 Unity 应用内购版本 4 迁移到版本 5。

应用内购 (IAP) 版本 5 (v5) 引入了重大的架构改进，可更好地控制连接和购买流程的每个步骤。虽然迁移要求对现有 IAP 实现进行重大更新，但本指南提供了分步说明和代码示例来帮助您过渡。

> **Note:**
>
> 从 SDK v5.4 开始，Unity IAP 将开发者数据处理为[开发者数据框架](/cloud/developer-data.md.md)的一部分。因此，您有责任使用 [UnityConsent](https://docs.unity3d.com/ScriptReference/UnityConsent.ConsentState.html) 模块[管理用户同意](/cloud/developer-data/user-consent.md.md)。

> **Note:**
>
> 从 IAP 5.3.0 版本开始，您可以访问 IAP AI 技能来帮助将项目从版本 4 迁移到版本 5。
>
> 在 **Project Settings** > **Services** > **In-App Purchasing** 的底部，选择以下选项之一：
>
> * **打开技能文件夹**：打开包含技能文件的文件夹。使用此选项可在首选 AI 工具中复制或安装 IAP AI 技能。
> * **安装到 Claude Code**：将技能直接添加到 Claude Code。这将安装一个名为 `in-app-purchases` 的技能。
>
> 安装该技能后，请从 AI 工具运行 `in-app-purchases`，并提示它将项目从版本 4 迁移到版本 5。

## 更改概述##overview-of-changes

下表总结了 IAP v5 中的更改以及在实现中需要更新的内容。有关升级的好处的更多信息，请参阅为[什么应该升级到 Unity 应用内购 (IAP) v5.x](https://support.unity.com/hc/articles/47757890052372-Why-you-should-upgrade-to-Unity-In-App-Purchasing-IAP-v5-x) 支持文章。

| 更改                                                                                                | 所需更新                                                                                                                   |
| ------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| 初始化分为商店连接、商品获取和购买获取的单独异步调用。                                                                       | [替换 UnityPurchasing.Initialize()](./upgrade-to-iap-v5.md#replace-unitypurchasing.initialize\(\))                       |
| `ConfigurationBuilder` 被删除。产品现在通过`CatalogProvider`或`ProductDefinitions`列表进行定义。                    | [替换 ConfigurationBuilder](./upgrade-to-iap-v5.md#replace-configurationbuilder)                                         |
| 商店扩展将替换为商店扩展服务。                                                                                   | [替换 ConfigurationBuilder](./upgrade-to-iap-v5.md#replace-configurationbuilder)                                         |
| 在 `StoreController` 和各个商店服务上，`IDetailedStoreListener` 和 `IStoreListener` 替换为可选的事件处理程序。            | [替换 IDetailedStoreListener 和 IStoreListener](./upgrade-to-iap-v5.md#replace-idetailedstorelistener-and-istorelistener) |
| `IStoreController` 替换为 `StoreController`，可随时通过 `UnityIAPServices.StoreController()` 获取。           | [替换 IStoreController](./upgrade-to-iap-v5.md#replace-istorecontroller)                                                 |
| `ProcessPurchase` 回调替换为新购买的`OnPurchasePending`和恢复购买的`OnPurchasesFetched`。                         | [更换购买流程](./upgrade-to-iap-v5.md#replace-purchase-flow)                                                                 |
| `RestoreTransactions` 已从 Store Extensions 移至 `StoreController` 和 `PurchaseService`。               | [恢复交易](./upgrade-to-iap-v5.md#restore-transactions)                                                                    |
| 权利检查现在基于事件，使用 `FetchPurchases` 或 `CheckEntitlement`。                                              | [替换权利检查](./upgrade-to-iap-v5.md#replace-entitlement-checks)                                                            |
| Apple App Store 收据验证已弃用。Google Play 收据验证现在使用 `Order.Info.Receipt`。                                | [更新收据验证](./upgrade-to-iap-v5.md#receipt-validation)                                                                    |
| `CodelessIAPStoreListener.initializationComplete` 替换为 `CodelessIAPStoreListener.IsInitialized()`。 | [更新 Codeless IAP](./upgrade-to-iap-v5.md#codeless-iap-specifics)                                                       |

## 替换 UnityPurchasing.Initialize()##replace-unitypurchasing.initialize()

从 IAP v5 开始，您可以更灵活地初始化 Unity IAP 包。您可以独立和异步连接到商店、获取商品和处理购买。此方法可以帮助您识别和解决可能阻止成功初始化的问题。

> **Note:**
>
> 在版本 4 和更早版本中，Unity IAP 包连接到商店，获取商品，并在包初始化时同步获取购买。只有在完成所有这些步骤后，包才会报告初始化成功。

按照以下步骤替换 `UnityPurchasing.Initialize()` 的行为：

1. 调用并等待`StoreController.Connect()`：
   * 此调用完成后，IAP 将连接到您当前的应用商店。
2. 调用：
   * 您可以将商品添加到 `CatalogProvider` 实例，类似于将商品添加到`ConfigurationBuilder`。您还可以将`ProductDefinitions`列表传递给`ProductService.FetchProducts()`或`StoreController.FetchProducts()`。有关更多信息，请参阅[新初始化过程](./upgrade-to-iap-v5.md#code-sample-of-new-initialization-process-using-storecontroller)的代码示例。
   * 当请求成功完成时调用 `OnProductsFetched` 事件处理程序。如果不能获取指定商品，则会调用 `OnProductsFetchFailed`。
3. 成功获取商品后调用 `FetchPurchases()`：
   * 当请求成功完成时调用 `OnPurchasesFetched` 事件处理程序。`Orders` 对象包含应用商店返回的所有延迟、待处理和已完成订单的可过滤集合。失败时会调用 `OnPurchasesFetchFailed`。

> **Note:**
>
> 默认情况下，调用 `FetchPurchases` 会为会话中尚未处理的任何待处理购买调用 `OnPurchasePending`。您可以使用 `StoreController.ProcessPendingOrdersOnPurchasesFetched(false)` 禁用此行为。

### 使用 StoreController 的新初始化过程的代码示例##code-sample-of-new-initialization-process-using-storecontroller

以下示例展示了如何初始化 IAP v5：

```cs
StoreController m_StoreController;  
  
async void InitializeIAP()  
{  
    m_StoreController = UnityIAPServices.StoreController();  
  
    m_StoreController.OnPurchasePending += OnPurchasePending;  
  
    await m_StoreController.Connect();  
  
    m_StoreController.OnProductsFetch += OnProductsFetch;
    m_StoreController.OnPurchasesFetch += OnPurchasesFetch;  
  
    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  
}
```

## 替换 IDetailedStoreListener 和 IStoreListener##replace-idetailedstorelistener-and-istorelistener

IAP v5 不再需要实现用于处理购买或初始化的`IDetailedStoreListener`或`IStoreListener`。通过将事件处理程序附加到 `StoreController` 或各个 `ProductService`、`PurchaseService` 和 `StoreService` 服务，替换以前由 `IDetailedStoreListener` 处理的功能。

有关向 `StoreController` 添加事件处理程序的示例，请参阅[新初始化过程](./upgrade-to-iap-v5.md#code-sample-of-new-initialization-process-using-storecontroller)的代码示例。

> **Note:**
>
> 您可以随时添加或删除事件处理程序，但在调用某些方法之前，建议使用某些事件处理程序。在附加建议的事件处理程序之前调用函数时，Unity IAP 会显示警告。

要迁移，请将每个 `IDetailedStoreListener` 函数替换为以下函数：

| 函数                                                                             | IAP v5 替换                                                                                                                                                                                                                                                    |
| ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `OnPurchaseFailed(Product, PurchaseFailureDescription)`                        | 将事件处理程序添加到 `StoreController.OnPurchaseFailed(FailedOrder)` 或 `PurchaseService.OnPurchaseFailed(FailedOrder)`                                                                                                                                                 |
| `OnInitialized(IStoreController controller, IExtensionProvider extensions)`    | 在`StoreController.Connect()`或`StoreService.Connect()`完成后继续执行。                                                                                                                                                                                                |
| `OnInitializeFailed(InitializationFailureReason error)`                        | 将事件处理程序添加到 `StoreService.OnStoreDisconnected(StoreConnectionFailureDescription)`、`ProductService.OnProductsFetchFailed(ProductFetchFailed)` 和 `PurchaseService.OnPurchasesFetchFailed(PurchasesFetchFailureDescription)`。还可以通过 `StoreController` 添加这些事件处理程序。 |
| `OnInitializeFailed(InitializationFailureReason error, string message = null)` | 将事件处理程序添加到 `StoreController.OnStoreDisconnected(StoreConnectionFailureDescription)` 或 `StoreService.OnStoreDisconnected(StoreConnectionFailureDescription)`                                                                                                  |
| `PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)`             | 将事件处理程序添加到 `StoreController.OnPurchasePending(PendingOrder)` 或 `PurchaseService.OnPurchasePending(PendingOrder)`                                                                                                                                             |

## 替换 ConfigurationBuilder##replace-configurationbuilder

将以下 `ConfigurationBuilder` 函数替换为以下行为：

| 函数                                  | IAP v5 替换                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ConfigurationBuilder.AddProduct()` | 您可以通过 `CatalogProvider.AddProduct()` 或 `CatalogProvider.AddProducts()` 将商品添加到 `CatalogProvider` 实例，而不是调用 `ConfigurationBuilder.AddProduct()`。要获取商品，可以调用 `CatalogProvider.FetchProducts` 并传递回调（例如 `UnityIAPServices.DefaultProduct().FetchProductsWithNoRetries`）。有关产品和`CatalogProvider`的更多信息，请参阅[在 Editor 中创建目录](./create-catalog-in-editor.md)。还可以创建`ProductDefinitions`并将其直接传递给 `StoreController` 或 `ProductService` 上的 `FetchProducts()` 调用。 |
| 4 和 5                               | 您可以在扩展商店服务中的 `IAppleConfiguration` 和 `IGoogleConfiguration` 中找到功能，例如 `AppleStoreExtendedService`。                                                                                                                                                                                                                                                                                                                                               |

> **Note:**
>
> 如果在不支持的平台上获取应用商店扩展服务（例如在 Editor 或 Android 上获取`AppleStoreExtendedService`），则该服务将为 null。在添加事件处理程序或更改设置之前，请务必检查服务未为空。

## 替换 IStoreController##replace-istorecontroller

将 `IStoreController` 替换为 `StoreController`。`StoreController` 公开了类似于 `IStoreListener` 的 IAP 功能，但可以随时通过调用 `UnityIAPServices.StoreController()` 来获取`StoreController`。

将这些来自 `IStoreController` 的方法替换为以下来自 `StoreController` 的方法：

| IStoreController          | StoreController                | 注意                                                                  |
| ------------------------- | ------------------------------ | ------------------------------------------------------------------- |
| `products`                | `GetProducts`                  | `GetProducts` 返回商品列表。                                               |
| `InitiatePurchase`        | `Purchase` 或 `PurchaseProduct` | `Purchase` 函数不再具有 `payload` 参数。在 Google Billing v3 中移除了对开发者有效负载的支持。 |
| `FetchAdditionalProducts` | `FetchProducts`                | `FetchProducts` 还用于获取初始商品。                                          |
| `ConfirmPendingPurchase`  | `ConfirmPurchase`              | `ConfirmPurchase` 需要`PendingOrder`。                                 |

## 替换购买流程##replace-purchase-flow

要发起购买，请致电`Purchase()`或`PurchaseProduct()``StoreController`或`PurchaseService`。要确认待定购买，请致电`StoreController.ConfirmPurchase(PendingOrder)`或`PurchaseService.ConfirmPurchase(PendingOrder)`，告知要确认的`PendingOrder`。有关购买流程的更多信息，请参阅[购买](./purchases.md)。

在 IAP v4 及更早版本中，`ProcessPurchase` 方法会自动处理所有购买事件。在 IAP v5 中，购买处理使用以下回调：

* `OnPurchasePending`：为新购买调用。
* `OnPurchasesFetched`：为恢复的购买调用。

在这两个新回调中使用现有的`ProcessPurchase`逻辑。

## 还原交易##restore-transactions

`RestoreTransactions` 已从 Store Extensions 移至 `StoreController` 和 `PurchaseService`。

当您调用 `FetchPurchases()` 或通过调用 `CheckEntitlement()` 时，已确认的购买将自动恢复。

## 替换授权检查##replace-entitlement-checks

要检查用户对商品的权限，IAP 依赖于事件。您可以通过调用 `FetchPurchases` 并处理 `OnPurchasesFetched` 事件来检查所有获取的商品的权利。要检查单个商品的权限，请调用 `CheckEntitlement` 并处理 `OnCheckEntitlement` 事件。

## 获取其他商品##fetch-additional-products

在 `StoreController` 或 `ProductService` 上调用 `FetchProducts` 并提供其他商品列表以触发 `OnPurchasesFetched` 事件处理程序。或者，也可以在 `CatalogProvider` 实例上调用 `FetchProducts`。有关通过 `ProductService` 获取产品的示例，请参阅[新初始化过程](./upgrade-to-iap-v5.md#code-sample-of-new-initialization-process-using-storecontroller)的代码示例。有关在 `CatalogProvider` 上调用 `FetchProducts` 的示例，请参阅[在 Editor 中创建目录](./create-catalog-in-editor.md)。

## 收据验证##receipt-validation

Apple App Store 收据的收据验证已弃用。收据验证仅适用于 Google Play 应用商店。要获取收据，请使用 `Order` 中的`Order.Info.Receipt`而不是 `Product.Receipt`。

> **Note:**
>
> 尽快从 StoreKit 1 收据移至 [StoreKit 2 jwsRepresentation](https://developer.apple.com/documentation/storekit/verificationresult/jwsrepresentation-21vgo)，以提高可靠性、安全性和性能，最终为用户提供更好的体验。

> **Note:**
>
> 使用 [OrderInfo.Apple.jwsRepresentation](https://docs.unity3d.com/Packages/com.unity.purchasing@latest?subfolder=/api/UnityEngine.Purchasing.IAppleOrderInfo.html#UnityEngine_Purchasing_IAppleOrderInfo_jwsRepresentation) 进行服务器端验证。

## Codeless IAP 特性##codeless-iap-specifics

将以下 `CodelessIAPStoreListener` 函数替换为：

| `函数`CodelessIAPStoreListener\`                    | IAP v5 替换                                  |
| ------------------------------------------------- | ------------------------------------------ |
| `CodelessIAPStoreListener.initializationComplete` | `CodelessIAPStoreListener.IsInitialized()` |
