# 购买

> 获取购买信息并确定玩家购买的商品的购买状态。

Unity IAP 从商店获取购买信息，以便您的应用程序可以识别和满足玩家的购买需求。这样可以确保您的游戏可以根据用户的购买历史记录向用户交付内容或授权，即使用户在应用外部或其他设备上购买了物品也是如此。

购买表示为[`Order`](https://docs.unity3d.com/Packages/com.unity.purchasing@latest?subfolder=/api/UnityEngine.Purchasing.Order.html)对象。该`Order`包含有关购买的所有相关详细信息，并提供在商店中进行跟踪和管理所需的信息。

## 获取购买

可以从商店中检索用户进行的购买。但是，消耗品在使用后必须由应用程序进行跟踪，因为商店不会退回已用完的消耗品。对于非消耗品和订阅，商店会在您调用 [`FetchPurchases`](https://docs.unity3d.com/Packages/com.unity.purchasing@latest?subfolder=/api/UnityEngine.Purchasing.IPurchaseService.html#UnityEngine_Purchasing_IPurchaseService_FetchPurchases) 或 [`CheckEntitlement`](https://docs.unity3d.com/Packages/com.unity.purchasing@latest?subfolder=/api/UnityEngine.Purchasing.IPurchaseService.html#UnityEngine_Purchasing_IPurchaseService_CheckEntitlement_UnityEngine_Purchasing_Product_) 时准确返回这些购买。

## 确定购买状态

您可以通过两种方式确定购买状态：

* 使用 [`Order`](https://docs.unity3d.com/Packages/com.unity.purchasing@latest?subfolder=/api/UnityEngine.Purchasing.Orders.html) 确定`Order`是`PendingOrder`、`ConfirmedOrder`、`DeferredOrder`还是`FailedOrder`。
* 使用 `CheckEntitlement` 接收[`EntitlementStatus`](https://docs.unity3d.com/Packages/com.unity.purchasing@latest?subfolder=/api/UnityEngine.Purchasing.Entitlement.html#UnityEngine_Purchasing_Entitlement_Status)，返回 `EntitledButNotFinished`、`EntitledUntilConsumed`、`FullyEntitled`、`NotEntitled` 或 `Unknown`。

### 购买属性

| 属性              | 描述              |
| --------------- | --------------- |
| `transactionId` | 购买的唯一标识符。       |
| `product`       | 购买的商品。          |
| `quantity`      | 购买的商品数量。        |
| `receipt`       | 用于向商店验证购买的收据数据。 |

### 购买状态

| State       | 描述          |
| ----------- | ----------- |
| `Pending`   | 购买已付款但尚未完成。 |
| `Confirmed` | 购买已完成并确认。   |
| `Failed`    | 由于错误，购买失败。  |
| `Deferred`  | 购买正在等待付款。   |

## 处理购买

进行购买并等待实现时调用 [`OnPurchasePending`](https://docs.unity3d.com/Packages/com.unity.purchasing@latest?subfolder=/api/UnityEngine.Purchasing.IPurchaseService.html#UnityEngine_Purchasing_IPurchaseService_OnPurchasePending) 回调。此时，应用程序应该会完成购买，例如解锁本地内容或将购买收据发送到服务器以更新服务器端游戏模型。

请注意，在成功初始化后的任何时间都可以调用 `OnPurchasePending`。如果应用程序在执行`OnPurchasePending`处理程序期间崩溃，则在 Unity IAP 下次初始化时再次调用。考虑实现您自己的重复数据消除逻辑。

> **Note:**
>
> 如果未确认购买，商店会退回购买，有些商店甚至会自动退款以保护用户。

```cs
// Handle restore on initialization
private async void Start()
{
    // Setup, e.g. add listeners to your StoreController...
    m_StoreController.OnPurchasePending += OnPurchasePending;
    m_StoreController.OnPurchasesFetch += OnPurchasesFetch;
    await m_StoreController.Connect();

    // Fetch previous purchases (includes confirmed orders)
    m_StoreController.FetchPurchases();
}

// Handle new purchases and pending transactions
private void OnPurchasePending(PendingOrder order)
{
    ProcessPurchase(order);
}

// Handle fetched purchases (includes previously confirmed orders)
private void OnPurchasesFetch(Orders orders)
{
    foreach（在 orders.ConfirmedOrders 中更改 confirmedOrder）
    {
        if (confirmedOrder.CartOrdered.Items().FirstOrDefault()?.Product.definition.type != ProductType.Consumable)
        {
            // Mark non-consumable and subscription products as entitled on fetch, as they only need to be granted once
            MarkAsEntitled(confirmedOrder.CartOrdered.Items().FirstOrDefault().Product);
        }
    }
}

// Your ProcessPurchase logic
private void ProcessPurchase(PendingOrder order)
{
    foreach（var product in order.CartOrdered.Items()）
    {
        // Grant product
        GrantProduct(product);
    }
    // Confirm the order to finalize the transaction
    m_StoreController.ConfirmPurchase(order);
}
```

## 购买确认和可靠性

Unity IAP 要求您显式确认购买，以确保可靠地完成购买，即使在网络中断或应用程序崩溃期间也是如此。如果购买已付款但未兑现，Unity IAP 会在应用程序下次初始化时将购买内容传递给应用程序。此过程可防止购买流程中断时或在应用程序离线时完成购买时丢失购买。

成功执行购买后，请致电具有相关`PendingOrder`的[`ConfirmPurchase`](https://docs.unity3d.com/Packages/com.unity.purchasing@latest?subfolder=/api/UnityEngine.Purchasing.IPurchaseService.html#UnityEngine_Purchasing_IPurchaseService_ConfirmPurchase_UnityEngine_Purchasing_PendingOrder_)以向商店确认购买。

> **Warning:**
>
> 始终确认购买。否则，可能会出现以下情况：
>
> * Unity IAP 在每次后续`FetchPurchases`调用或会话开始时重新调用订单的`OnPurchasePending`，直到您确认为止。如果没有您自己的重复数据消除逻辑，则可以多次授予同一项目。
> * 某些商店会自动反转购买。例如，Google Play 会在三天后退回未确认的购买，Apple App Store 会在较不严格的时间轴上应用类似的保护逻辑。
> * 对于玩家来说，购买看起来会成功，然后消失，这看起来像是失败的购买。

> **Note:**
>
> 对于消耗品，一旦您确认购买，商店不会再次退回。始终远程保存消耗品奖励。如果在本地存储消耗品奖励，可能会丢失无法恢复的数据。

## 确认购买已保留到云端

如果要将消耗品购买保存到云端，必须在成功保存购买后调用 [`ConfirmPurchase`](https://docs.unity3d.com/Packages/com.unity.purchasing@latest?subfolder=/api/UnityEngine.Purchasing.IPurchaseService.html#UnityEngine_Purchasing_IPurchaseService_ConfirmPurchase_UnityEngine_Purchasing_PendingOrder_)。

返回 `Pending` 时，Unity IAP 会保持底层商店上的交易处于打开状态，直到确认为已处理。这样可确保即使用户在消耗品等待期间重新安装了应用程序，也不会丢失消耗品购买。
