权益发放
====

#### 场景介绍

应用在收到用户购买消耗型/非消耗型商品成功的结果后，需要发放相关权益，并在权益发放后，向IAP Kit确认发货，完成购买流程，具体实现请参见[接入购买](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/iap-integrate-purchase)。此外，还需要补充如下处理，确保权益发放：

*   若应用提供消耗型商品，需要按照[确保权益发放](#确保权益发放)处理消耗型商品的权益发放。
    
*   若应用提供非消耗型商品，且为单机应用，则需要按照[单机应用权益发放（非消耗型商品）](#单机应用权益发放非消耗型商品)处理非消耗型商品的权益发放。其他场景需要按照[确保权益发放](#确保权益发放)处理非消耗型商品的权益发放。
    

#### 确保权益发放

用户购买商品后，开发者需要及时发放相关权益。但实际应用场景中，若出现异常（网络错误、进程被中止等）将导致应用无法知道用户实际是否支付成功，从而无法及时发放权益，即出现掉单情况。为了确保权益发放，需要在以下场景检查用户是否存在已购未发货的商品：

1.  应用启动时。
    
2.  购买请求（[createPurchase](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-iap#iapcreatepurchase)）返回[iap.IAPErrorCode.PRODUCT\_OWNED](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-iap#iaperrorcode)或[iap.IAPErrorCode.SYSTEM\_ERROR](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-iap#iaperrorcode)时。
    

如果存在已购未发货商品，则发放相关权益，然后向IAP Kit确认发货，完成购买。

#### \[h2\]业务流程

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/70/v3/GgLGUMM_S9a8tRkxF3D1HQ/zh-cn_image_0000002625188593.png?HW-CC-KV=V1&HW-CC-Date=20260612T073618Z&HW-CC-Expire=86400&HW-CC-Sign=4D7B34C14BD6253C6C3B719EDADE747149866B81E6C9420632B6B47CC0A90862)

1.  应用客户端向IAP Kit发起[queryPurchases](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-iap#iapquerypurchases)请求，查询用户已购买但未确认发货的订单信息。
    
2.  IAP Kit返回[PurchaseData](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-data-model#purchasedata)列表。[PurchaseData](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-data-model#purchasedata)为JWS格式的字符串，承载了相关的订单信息。
    
3.  应用客户端向应用服务器上报[PurchaseData](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-data-model#purchasedata)列表。
    
4.  应用服务器需对每个[PurchaseData](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-data-model#purchasedata).jwsPurchaseOrder进行[解码验签](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-verifying-signature#jws解码和验签)，验证成功可得到对应的[PurchaseOrderPayload](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-data-model#purchaseorderpayload)的JSON字符串。
    
5.  处理权益发放。检查当前[PurchaseOrderPayload](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-data-model#purchaseorderpayload)是否已发放权益，未发放则发放相关权益，并记录对应的订单信息（[PurchaseOrderPayload](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-data-model#purchaseorderpayload)）。
    
6.  应用客户端向应用服务器查询订单的发货状态。
    
7.  应用服务器返回对应的发货状态以及订单信息（[PurchaseOrderPayload](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-data-model#purchaseorderpayload)）。
    
8.  发货成功后应用客户端向IAP Kit发送[finishPurchase](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-iap#iapfinishpurchase)请求，以此通知IAP服务器更新商品的发货状态，完成购买流程。应用成功执行[finishPurchase](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-iap#iapfinishpurchase)之后，IAP服务器会将相应商品标记为已发货状态。此步骤也可放到应用服务器处理。应用服务器可通过请求服务端[订单确认发货](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-confirm-purchase-for-order)接口来确认发货，完成购买流程。
    
    ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/3a/v3/TskGRTP1SGqQvhnTFUZLBQ/note_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260612T073618Z&HW-CC-Expire=86400&HW-CC-Sign=DD8B5E03EB9ED794BDC35685F14639588B2E073E35BD32FAB63F70BD1D3D6325)
    
    *   对于消耗型商品，IAP服务器会将相应商品重新设置为可购买状态，用户即可再次购买该商品。如果不执行此步骤，会导致用户无法再次购买该商品。
        
    *   确保在发货成功之后再执行此步骤，否则可能导致IAP服务器已经确认发货但是应用没有发货的问题。
        
    

#### \[h2\]开发步骤

1.  应用客户端向IAP Kit发起[queryPurchases](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-iap#iapquerypurchases)请求，获取用户已购买但未确认发货的订单信息。
    
    在请求参数[QueryPurchasesParameter](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-iap#querypurchasesparameter)中指定对应的productType，同时指定queryType为[iap.PurchaseQueryType.UNFINISHED](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-iap#purchasequerytype)。当接口请求成功时，IAP Kit将返回一个[QueryPurchaseResult](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-iap#querypurchaseresult)对象，该对象包含承载了订单信息的[PurchaseData](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-data-model#purchasedata)的列表。
    
2.  对[purchaseData](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-data-model#purchasedata).jwsPurchaseOrder进行[解码验签](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-verifying-signature#jws解码和验签)。建议应用客户端将[purchaseData](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-data-model#purchasedata)发送至应用服务器，在应用服务器执行此操作。
    
3.  验证成功可得到对应的[PurchaseOrderPayload](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-data-model#purchaseorderpayload)的JSON字符串，如果[PurchaseOrderPayload](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-data-model#purchaseorderpayload).purchaseOrderRevocationReasonCode为空，则代表购买成功，需要进行补发货处理。
    
    建议先检查此笔订单权益的发放状态，未发放则发放权益，成功后记录[PurchaseOrderPayload](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-data-model#purchaseorderpayload)等信息，用于后续检查权益发放状态。
    
    ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/65/v3/0Rx_bBgbSICaqCbub_oPWA/caution_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260612T073618Z&HW-CC-Expire=86400&HW-CC-Sign=6F34565E914996ED66E9414C600C776DBDC3FFA88BCA8BC90FA548B9C0956CD5)
    
    如果开发者在[发起购买](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/iap-integrate-purchase#发起购买)时支持消耗型商品的批量购买，则需要在发货时校验下单的商品数量和[PurchaseOrderPayload](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-data-model#purchaseorderpayload).quantity是否一致，避免造成漏发、多发的情况。
    
4.  发货成功后，应用需调用[finishPurchase](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-iap#iapfinishpurchase)接口确认发货，以此通知IAP服务器更新商品的发货状态，完成购买流程。
    
    发起请求时，需在请求参数[FinishPurchaseParameter](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-iap#finishpurchaseparameter)中携带[PurchaseOrderPayload](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-data-model#purchaseorderpayload)中的productType、purchaseToken、purchaseOrderId。
    
    请求成功后，IAP服务器会将相应商品标记为已发货状态。对于消耗型商品，IAP服务器会将相应商品重新设置为可购买状态，用户即可再次购买该商品。对于非消耗型商品，用户购买后永久拥有，无法再次购买该商品。
    
    ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/5c/v3/LK13rUUvR06GzBkBrcVjsw/note_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260612T073618Z&HW-CC-Expire=86400&HW-CC-Sign=00BB7BFA96020A2A7098BE3CE580B84C29B662DE7E634D48E7F85EB75035AB3D)
    
    JWSUtil为自定义类，可参见[示例代码](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/iap-dev-guide#示例代码)。
    
    import { iap } from '@kit.IAPKit';
    import { common } from '@kit.AbilityKit';
    import { BusinessError } from '@kit.BasicServicesKit';
    // JWSUtil为自定义类
    import { JWSUtil } from '../common/JWSUtil';
    
    @Entry
    @Component
    struct Index {
      queryPurchases(context: common.UIAbilityContext,) {
        const param: iap.QueryPurchasesParameter = {
          // iap.ProductType.CONSUMABLE：消耗型商品
          // iap.ProductType.NONCONSUMABLE：非消耗型商品
          productType: iap.ProductType.CONSUMABLE,
          queryType: iap.PurchaseQueryType.UNFINISHED
        };
        iap.queryPurchases(context, param).then((res: iap.QueryPurchaseResult) => {
          console.info('Succeeded in querying purchases.');
          const purchaseDataList: string\[\] = res.purchaseDataList;
          if (purchaseDataList === undefined || purchaseDataList.length <= 0) {
            return;
          }
          for (let i = 0; i < purchaseDataList.length; i++) {
            const jwsPurchaseOrder: string = JSON.parse(purchaseDataList\[i\]).jwsPurchaseOrder;
            if (!jwsPurchaseOrder) {
              continue;
            }
            const purchaseStr = JWSUtil.decodeJwsObj(jwsPurchaseOrder);
            // 需自定义PurchaseOrderPayload类，包含的信息请参见PurchaseOrderPayload
            const purchaseOrderPayload = JSON.parse(purchaseStr) as PurchaseOrderPayload;
            // 处理发货
            // ...
            // 发货成功后向IAP Kit发送finishPurchase请求，确认发货，完成购买
            this.finishPurchase(context, purchaseOrderPayload);
          }
        }).catch((err: BusinessError) => {
          // 请求失败
          console.error(\`Failed to query purchases. Code is ${err.code}, message is ${err.message}\`);
        });
      }
    
      finishPurchase(context: common.UIAbilityContext, purchaseOrder: PurchaseOrderPayload) {
        const finishPurchaseParam: iap.FinishPurchaseParameter = {
          productType: Number(purchaseOrder.productType),
          purchaseToken: purchaseOrder.purchaseToken,
          purchaseOrderId: purchaseOrder.purchaseOrderId
        };
        iap.finishPurchase(context, finishPurchaseParam).then(() => {
          // 请求成功
          console.info('Succeeded in finishing purchase.');
        }).catch((err: BusinessError) => {
          // 请求失败
          console.error(\`Failed to finish purchase. Code is ${err.code}, message is ${err.message}\`);
        });
      }
    
      build() {}
    }
    

#### 单机应用权益发放（非消耗型商品）

用户在购买非消耗型商品后，将永久拥有该商品的权益。应用需要在用户购买非消耗型商品后，始终为其发放相关权益。

请在以下场景获取用户已购非消耗型商品的信息，并发放相关权益。

1.  应用启动时。
    
2.  购买请求（[createPurchase](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-iap#iapcreatepurchase)）返回[iap.IAPErrorCode.PRODUCT\_OWNED](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-iap#iaperrorcode)或[iap.IAPErrorCode.SYSTEM\_ERROR](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-iap#iaperrorcode)时。
    

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/2a/v3/057cDR2-SHOGI1fHRZEt5w/caution_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260612T073618Z&HW-CC-Expire=86400&HW-CC-Sign=0878644DA90C46591C53F47463763449E2A441DCA2668AC8486685F9F185C496)

为了在卸载重装、更换设备安装等场景下保障用户权益，需要在应用首次打开时，应用客户端向IAP Kit发起[queryPurchases](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-iap#iapquerypurchases)请求，查询用户已购非消耗型商品，完成权益恢复。

#### \[h2\]开发步骤

1.  应用客户端向IAP Kit发起[queryPurchases](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-iap#iapquerypurchases)请求，获取用户已购非消耗型商品的订单状态信息。
    
    在请求参数[QueryPurchasesParameter](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-iap#querypurchasesparameter)中指定productType为[iap.ProductType.NONCONSUMABLE](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-iap#producttype)，同时指定queryType为[iap.PurchaseQueryType.CURRENT\_ENTITLEMENT](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-iap#purchasequerytype)。当接口请求成功时，IAP Kit将返回一个[QueryPurchaseResult](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-iap#querypurchaseresult)对象，该对象包含承载了订单信息的[PurchaseData](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-data-model#purchasedata)的列表。
    
2.  对每个[PurchaseData](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-data-model#purchasedata).jwsPurchaseOrder进行[解码验签](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-verifying-signature#jws解码和验签)。
    
3.  验证成功可得到对应的[PurchaseOrderPayload](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-data-model#purchaseorderpayload)的JSON字符串，此时需要发放相关权益。
    
4.  发放权益后，应用需调用[finishPurchase](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-iap#iapfinishpurchase)接口确认发货，以此通知IAP服务器更新商品的发货状态，完成购买流程。
    
    发起请求时，需在请求参数[FinishPurchaseParameter](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-iap#finishpurchaseparameter)中携带[PurchaseOrderPayload](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/iap-data-model#purchaseorderpayload)中的productType、purchaseToken、purchaseOrderId。
    
    请求成功后，IAP服务器会将相应商品标记为已发货。对于非消耗型商品，用户购买后永久拥有，无法再次购买该商品。
    
    ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/56/v3/0pLU7sjdRbaSMdDvnc49ew/note_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260612T073618Z&HW-CC-Expire=86400&HW-CC-Sign=250795E6F27A93A0E6A6EFD122CF0AEDF5F15E42253749AEF8BBCD2BA29CBA8A)
    
    JWSUtil为自定义类，可参见[示例代码](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/iap-dev-guide#示例代码)。
    

import { iap } from '@kit.IAPKit';
import { common } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
// JWSUtil为自定义类
import { JWSUtil } from '../common/JWSUtil';

@Entry
@Component
struct Index {
  queryPurchases(context: common.UIAbilityContext) {
    const param: iap.QueryPurchasesParameter = {
      productType: iap.ProductType.NONCONSUMABLE,
      queryType: iap.PurchaseQueryType.CURRENT\_ENTITLEMENT
    };
    iap.queryPurchases(context, param).then((res: iap.QueryPurchaseResult) => {
      console.info('Succeeded in querying purchases.');
      const purchaseDataList: string\[\] = res.purchaseDataList;
      if (purchaseDataList === undefined || purchaseDataList.length <= 0) {
        return;
      }
      for (let i = 0; i < purchaseDataList.length; i++) {
        const jwsPurchaseOrder: string = JSON.parse(purchaseDataList\[i\]).jwsPurchaseOrder;
        if (!jwsPurchaseOrder) {
          continue;
        }
        // 对jwsPurchaseOrder进行解码验签
        const purchaseStr = JWSUtil.decodeJwsObj(jwsPurchaseOrder);
        // 需自定义PurchaseOrderPayload类，包含的信息请参见PurchaseOrderPayload
        const purchaseOrderPayload = JSON.parse(purchaseStr) as PurchaseOrderPayload;
        // 处理权益发放
        // ...
        // 发放权益后向IAP Kit发送finishPurchase请求，确认发货，完成购买
        if (purchaseOrderPayload && purchaseOrderPayload.finishStatus !== '1') {
          this.finishPurchase(context, purchaseOrderPayload);
        }
      }
    }).catch((err: BusinessError) => {
      // 请求失败
      console.error(\`Failed to query purchases. Code is ${err.code}, message is ${err.message}\`);
    });
  }

  finishPurchase(context: common.UIAbilityContext, purchaseOrder: PurchaseOrderPayload) {
    const finishPurchaseParam: iap.FinishPurchaseParameter = {
      productType: Number(purchaseOrder.productType),
      purchaseToken: purchaseOrder.purchaseToken,
      purchaseOrderId: purchaseOrder.purchaseOrderId
    };
    iap.finishPurchase(context, finishPurchaseParam).then(() => {
      // 请求成功
      console.info('Succeeded in finishing purchase.');
    }).catch((err: BusinessError) => {
      // 请求失败
      console.error(\`Failed to finish purchase. Code is ${err.code}, message is ${err.message}\`);
    });
  }

  build() {}
}