华为HarmonyOS应用内支付服务 -- 2 权限发放

场景介绍

应用在收到用户购买消耗型/非消耗型商品成功的结果后,需要发放相关权益,并在权益发放后,向IAP Kit确认发货,完成购买流程,具体实现请参见接入购买。此外,还需要补充如下处理,确保权益发放:

确保权益发放

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

  1. 应用启动时。
  2. 购买请求(createPurchase)返回iap.IAPErrorCode.PRODUCT_OWNEDiap.IAPErrorCode.SYSTEM_ERROR时。

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

业务流程

  1. 应用客户端向IAP Kit发起queryPurchases请求,查询用户已购买但未确认发货的订单信息。
  2. IAP Kit返回PurchaseData列表。PurchaseData为JWS格式的字符串,承载了相关的订单信息。
  3. 应用客户端向应用服务器上报PurchaseData列表。
  4. 应用服务器需对每个PurchaseData.jwsPurchaseOrder进行解码验签,验证成功可得到对应的PurchaseOrderPayload的JSON字符串。
  5. 处理权益发放。检查当前PurchaseOrderPayload是否已发放权益,未发放则发放相关权益,并记录对应的订单信息(PurchaseOrderPayload)。
  6. 应用客户端向应用服务器查询订单的发货状态。
  7. 应用服务器返回对应的发货状态以及订单信息(PurchaseOrderPayload)。
  8. 发货成功后应用客户端向IAP Kit发送finishPurchase请求,以此通知IAP服务器更新商品的发货状态,完成购买流程。应用成功执行finishPurchase之后,IAP服务器会将相应商品标记为已发货状态。对于消耗型商品,IAP服务器会将相应商品重新设置为可购买状态,用户即可再次购买该商品。此步骤也可放到应用服务器处理。应用服务器可通过请求服务端订单确认发货接口来确认发货,完成购买流程。

    注意

    确保在发货成功之后再执行此步骤,否则可能导致IAP服务器已经确认发货但是应用没有发货的问题。

开发步骤

  1. 应用客户端向IAP Kit发起queryPurchases请求,获取用户已购买但未确认发货的订单信息。

    在请求参数QueryPurchasesParameter中指定对应的productType,同时指定queryType为iap.PurchaseQueryType.UNFINISHED。当接口请求成功时,IAP Kit将返回一个QueryPurchaseResult对象,该对象包含承载了订单信息的PurchaseData的列表。

  2. purchaseData.jwsPurchaseOrder进行解码验签。建议应用客户端将purchaseData发送至应用服务器,在应用服务器执行此操作。
  3. 验证成功可得到对应的PurchaseOrderPayload的JSON字符串,如果PurchaseOrderPayload.purchaseOrderRevocationReasonCode为空,则代表购买成功,需要进行补发货处理。

    建议先检查此笔订单权益的发放状态,未发放则发放权益,成功后记录PurchaseOrderPayload等信息,用于后续检查权益发放状态。

  4. 发货成功后,应用需调用finishPurchase接口确认发货,以此通知IAP服务器更新商品的发货状态,完成购买流程。

    发起请求时,需在请求参数FinishPurchaseParameter中携带PurchaseOrderPayload中的productType、purchaseToken、purchaseOrderId。

    请求成功后,IAP服务器会将相应商品标记为已发货状态。对于消耗型商品,IAP服务器会将相应商品重新设置为可购买状态,用户即可再次购买该商品。对于非消耗型商品,用户购买后永久拥有,无法再次购买该商品。

     
      
    1. import { iap } from '@kit.IAPKit';
    2. import { common } from '@kit.AbilityKit';
    3. import { BusinessError } from '@kit.BasicServicesKit';
    4. // JWTUtil为自定义类,可参见示例代码
    5. import { JWTUtil } from '../common/JWTUtil';
    6. queryPurchases() {
    7. const param: iap.QueryPurchasesParameter = {
    8. productType: iap.ProductType.CONSUMABLE,
    9. queryType: iap.PurchaseQueryType.UNFINISHED
    10. };
    11. const context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
    12. iap.queryPurchases(context, param).then((res: iap.QueryPurchaseResult) => {
    13. console.info('Succeeded in querying purchases.');
    14. const purchaseDataList: string[] = res.purchaseDataList;
    15. if (purchaseDataList === undefined || purchaseDataList.length <= 0) {
    16. return;
    17. }
    18. for (let i = 0; i < purchaseDataList.length; i++) {
    19. const jwsPurchaseOrder: string = JSON.parse(purchaseDataList[i]).jwsPurchaseOrder;
    20. if (!jwsPurchaseOrder) {
    21. continue;
    22. }
    23. const purchaseStr = JWTUtil.decodeJwtObj(jwsPurchaseOrder);
    24. // 需自定义PurchaseOrderPayload类,包含的信息请参见PurchaseOrderPayload
    25. const purchaseOrderPayload = JSON.parse(purchaseStr) as PurchaseOrderPayload;
    26. // 处理发货
    27. // ...
    28. // 发货成功后向IAP Kit发送finishPurchase请求,确认发货,完成购买
    29. this.finishPurchase(purchaseOrderPayload);
    30. }
    31. }).catch((err: BusinessError) => {
    32. // 请求失败
    33. console.error(`Failed to query purchases. Code is ${err.code}, message is ${err.message}`);
    34. });
    35. }
    36. finishPurchase(purchaseOrder: PurchaseOrderPayload) {
    37. const finishPurchaseParam: iap.FinishPurchaseParameter = {
    38. productType: purchaseOrder.productType,
    39. purchaseToken: purchaseOrder.purchaseToken,
    40. purchaseOrderId: purchaseOrder.purchaseOrderId
    41. };
    42. const context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
    43. iap.finishPurchase(context, finishPurchaseParam).then(() => {
    44. // 请求成功
    45. console.info('Succeeded in finishing purchase.');
    46. }).catch((err: BusinessError) => {
    47. // 请求失败
    48. console.error(`Failed to query purchases. Code is ${err.code}, message is ${err.message}`);
    49. });
    50. }

单机应用权益发放(非消耗型商品)

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

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

  1. 应用启动时。
  2. 购买请求(createPurchase)返回iap.IAPErrorCode.PRODUCT_OWNEDiap.IAPErrorCode.SYSTEM_ERROR时。

开发步骤

  1. 应用客户端向IAP Kit发起queryPurchases请求,获取用户已购非消耗型商品的订单状态信息。

    在请求参数QueryPurchasesParameter中指定productType为iap.ProductType.NONCONSUMABLE,同时指定queryType为iap.PurchaseQueryType.CURRENT_ENTITLEMENT。当接口请求成功时,IAP Kit将返回一个QueryPurchaseResult对象,该对象包含承载了订单信息的PurchaseData的列表。

  2. 对每个PurchaseData.jwsPurchaseOrder进行解码验签
  3. 验证成功可得到对应的PurchaseOrderPayload的JSON字符串,需要发放相关权益。
  4. 发放权益后,应用需调用finishPurchase接口确认发货,以此通知IAP服务器更新商品的发货状态,完成购买流程。

    发起请求时,需在请求参数FinishPurchaseParameter中携带PurchaseOrderPayload中的productType、purchaseToken、purchaseOrderId。

    请求成功后,IAP服务器会将相应商品标记为已发货。对于非消耗型商品,用户购买后永久拥有,无法再次购买该商品。

 
  1. import { iap } from '@kit.IAPKit';
  2. import { common } from '@kit.AbilityKit';
  3. import { BusinessError } from '@kit.BasicServicesKit';
  4. // JWTUtil为自定义类,可参见示例代码
  5. import { JWTUtil } from '../common/JWTUtil';
  6. queryPurchases() {
  7. const param: iap.QueryPurchasesParameter = {
  8. productType: iap.ProductType.NONCONSUMABLE,
  9. queryType: iap.PurchaseQueryType.CURRENT_ENTITLEMENT
  10. };
  11. const context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
  12. iap.queryPurchases(context, param).then((res: iap.QueryPurchaseResult) => {
  13. console.info('Succeeded in querying purchases.');
  14. const purchaseDataList: string[] = res.purchaseDataList;
  15. if (purchaseDataList === undefined || purchaseDataList.length <= 0) {
  16. return;
  17. }
  18. for (let i = 0; i < purchaseDataList.length; i++) {
  19. const jwsPurchaseOrder: string = JSON.parse(purchaseDataList[i]).jwsPurchaseOrder;
  20. if (!jwsPurchaseOrder) {
  21. continue;
  22. }
  23. // 对jwsPurchaseOrder进行解码验签
  24. const purchaseStr = JWTUtil.decodeJwtObj(jwsPurchaseOrder);
  25. // 需自定义PurchaseOrderPayload类,包含的信息请参见PurchaseOrderPayload
  26. const purchaseOrderPayload = JSON.parse(purchaseStr) as PurchaseOrderPayload;
  27. // 处理权益发放
  28. // ...
  29. // 发放权益后向IAP Kit发送finishPurchase请求,确认发货,完成购买
  30. if (purchaseOrderPayload && purchaseOrderPayload.finishStatus !== '1') {
  31. this.finishPurchase(purchaseOrderPayload);
  32. }
  33. }
  34. }).catch((err: BusinessError) => {
  35. // 请求失败
  36. console.error(`Failed to query purchases. Code is ${err.code}, message is ${err.message}`);
  37. });
  38. }
  39. finishPurchase(purchaseOrder: PurchaseOrderPayload) {
  40. const finishPurchaseParam: iap.FinishPurchaseParameter = {
  41. productType: purchaseOrder.productType,
  42. purchaseToken: purchaseOrder.purchaseToken,
  43. purchaseOrderId: purchaseOrder.purchaseOrderId
  44. };
  45. const context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
  46. iap.finishPurchase(context, finishPurchaseParam).then(() => {
  47. // 请求成功
  48. console.info('Succeeded in finishing purchase.');
  49. }).catch((err: BusinessError) => {
  50. // 请求失败
  51. console.error(`Failed to query purchases. Code is ${err.code}, message is ${err.message}`);
  52. });
  53. }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

青瓷代码世界

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值