如何实现应用内支付零掉单?

在电子商务的快速发展中,用户体验成为了衡量平台服务质量的重要标准。当用户完成商品购买后,开发者需要及时发放相关权益。但在实际应用场景中,由于网络波动、进程被中止等异常因素,可能会出现支付状态无法及时确认的情况,从而无法及时发放权益,即出现"掉单"情况。

掉单不仅会严重影响用户支付体验,还可能引发退款、投诉等,容易产生负面舆情,从而增加开发者和运营人员额外的操作成本。

HarmonyOS SDK应用内支付服务(IAP Kit)为了确保权益发放,需要在以下2种场景检查用户是否存在已购未发货的商品,以防止掉单情况。

1.应用启动时。

2.购买请求(createPurchase)返回iap.IAPErrorCode.PRODUCT_OWNEDiap.IAPErrorCode.SYSTEM_ERROR时

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

业务流程

开发步骤

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

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

3.对purchaseData.jwsPurchaseOrder进行解码验签。建议应用客户端将purchaseData发送至应用服务器,在应用服务器执行此操作。

4.验证成功可得到对应的PurchaseOrderPayload的JSON字符串,如果PurchaseOrderPayload.purchaseOrderRevocationReasonCode为空,则代表购买成功,需要进行补发货处理。

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

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

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

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

typescript 复制代码
import { iap } from '@kit.IAPKit';
import { common } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
// JWTUtil为自定义类,可参见示例代码
import { JWSUtil } from '../common/JWSUtil';

queryPurchases() {
  const param: iap.QueryPurchasesParameter = {
    productType: iap.ProductType.CONSUMABLE,
    queryType: iap.PurchaseQueryType.UNFINISHED
  };
  const context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
  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.decodeJwtObj(jwsPurchaseOrder);
      // 需自定义PurchaseOrderPayload类,包含的信息请参见PurchaseOrderPayload
      const purchaseOrderPayload = JSON.parse(purchaseStr) as PurchaseOrderPayload;
      // 处理发货
      // ...
      // 发货成功后向IAP Kit发送finishPurchase请求,确认发货,完成购买
      this.finishPurchase(purchaseOrderPayload);
    }
  }).catch((err: BusinessError) => {
    // 请求失败
    console.error(`Failed to query purchases. Code is ${err.code}, message is ${err.message}`);
  });
}

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

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

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

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

1.应用启动时。

2.购买请求(createPurchase)返回iap.IAPErrorCode.PRODUCT_OWNED或iap.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服务器会将相应商品标记为已发货。对于非消耗型商品,用户购买后永久拥有,无法再次购买该商品。

typescript 复制代码
import { iap } from '@kit.IAPKit';
import { common } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
// JWTUtil为自定义类,可参见示例代码
import { JWSUtil } from '../common/JWSUtil';

queryPurchases() {
  const param: iap.QueryPurchasesParameter = {
    productType: iap.ProductType.NONCONSUMABLE,
    queryType: iap.PurchaseQueryType.CURRENT_ENTITLEMENT
  };
  const context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
  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.decodeJwtObj(jwsPurchaseOrder);
      // 需自定义PurchaseOrderPayload类,包含的信息请参见PurchaseOrderPayload
      const purchaseOrderPayload = JSON.parse(purchaseStr) as PurchaseOrderPayload;
      // 处理权益发放
      // ...
      // 发放权益后向IAP Kit发送finishPurchase请求,确认发货,完成购买
      if (purchaseOrderPayload && purchaseOrderPayload.finishStatus !== '1') {
        this.finishPurchase(purchaseOrderPayload);
      }
    }
  }).catch((err: BusinessError) => {
    // 请求失败
    console.error(`Failed to query purchases. Code is ${err.code}, message is ${err.message}`);
  });
}

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

了解更多详情>>

访问应用内支付服务联盟官网

获取权益发放的开发指导文档

相关推荐
烬头88216 分钟前
React Native鸿蒙跨平台实现二维码联系人APP(QRCodeContactApp)
javascript·react native·react.js·ecmascript·harmonyos
xiaoqi9222 小时前
React Native鸿蒙跨平台如何实现分类页面组件通过searchQuery状态变量管理搜索输入,实现了分类的实时过滤功能
javascript·react native·react.js·ecmascript·harmonyos
听麟2 小时前
HarmonyOS 6.0+ 智慧出行导航APP开发实战:离线地图与多设备位置协同落地
华为·wpf·harmonyos
qq_177767373 小时前
React Native鸿蒙跨平台实现应用介绍页,实现了应用信息卡片展示、特色功能网格布局、权限/联系信息陈列、评分展示、模态框详情交互等通用场景
javascript·react native·react.js·ecmascript·交互·harmonyos
jin1233224 小时前
基于React Native鸿蒙跨平台地址管理是许多电商、外卖、物流等应用的重要功能模块,实现了地址的添加、编辑、删除和设置默认等功能
javascript·react native·react.js·ecmascript·harmonyos
2501_920931704 小时前
React Native鸿蒙跨平台医疗健康类的血压记录,包括收缩压、舒张压、心率、日期、时间、备注和状态
javascript·react native·react.js·ecmascript·harmonyos
2501_920931706 小时前
React Native鸿蒙跨平台使用useState管理健康记录和过滤状态,支持多种健康数据类型(血压、体重等)并实现按类型过滤功能
javascript·react native·react.js·ecmascript·harmonyos
2501_921930836 小时前
高级进阶 React Native 鸿蒙跨平台开发:InteractionManager 交互优化
react native·harmonyos
前端不太难6 小时前
HarmonyOS PC 文档模型完整范式
华为·状态模式·harmonyos
ITUnicorn8 小时前
【HarmonyOS6】从零实现自定义计时器:掌握TextTimer组件与计时控制
华为·harmonyos·arkts·鸿蒙·harmonyos6