如下业务流程对于单机应用同样适用。在单机应用中,应用服务器和应用客户端的交互放在应用客户端完成,应用服务器和IAP服务器交互的部分可不处理。
如下业务流程对于单机应用同样适用。在单机应用中,应用服务器和应用客户端的交互放在应用客户端完成,应用服务器和IAP服务器交互的部分可不处理。
如请求失败,应用需隐藏相关IAP功能入口。
此步骤也可放到应用服务器处理。应用服务器可通过请求服务端订阅确认发货接口来确认发货,完成购买流程。
具体请参见对生效中的订阅发放权益。
方式一:通过客户端接收购买结果
如果购买失败,请参见确保权益发放处理,及时发放权益。
发放权益
建议单机应用将用户权益和订阅状态关联。如果订阅处于生效状态,始终为用户发放权益。
此步骤也可放到应用服务器处理。应用服务器可通过请求服务端订阅确认发货接口来确认发货,完成购买流程。
在使用应用内支付之前,应用客户端需要向IAP Kit发送queryEnvironmentStatus请求,以此判断用户当前登录的华为账号所在的服务地是否在IAP Kit支持结算的国家/地区中。如请求失败,则隐藏相关IAP功能入口。
当前IAP Kit支持结算的国家/地区仅有中国大陆。
import { iap } from '@kit.IAPKit'; import { common } from '@kit.AbilityKit'; import { BusinessError } from '@kit.BasicServicesKit'; queryEnvironmentStatus() { const context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; iap.queryEnvironmentStatus(context).then(() => { // 请求成功 console.info('Succeeded in querying environment status.'); }).catch((err: BusinessError) => { // 请求失败 console.error(`Failed to query environment status. Code is ${err.code}, message is ${err.message}`); }); }
应用客户端通过queryProducts来获取在AppGallery Connect上配置的商品信息。发起请求时,需在请求参数QueryProductsParameter中携带相关的商品ID,并指定其productType为iap.ProductType.AUTORENEWABLE。
queryProducts每次只能查询一种商品类型的商品,每次最多查询200个商品,否则请求将报错。
import { iap } from '@kit.IAPKit'; import { common } from '@kit.AbilityKit'; import { BusinessError } from '@kit.BasicServicesKit'; queryProducts() { const queryProductParam: iap.QueryProductsParameter = { productType: iap.ProductType.AUTORENEWABLE, // productIds中的商品需要替换成开发者在AppGallery Connect网站配置的商品 productIds: ['product1', 'product2', 'product3'] }; const context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; iap.queryProducts(context, queryProductParam).then((result) => { // 请求成功 console.info('Succeeded in querying products.'); // 展示商品信息 // ... }).catch((err: BusinessError) => { // 请求失败 console.error(`Failed to query products. Code is ${err.code}, message is ${err.message}`); }); }
具体可参见对生效中的订阅发放权益。
用户发起购买时,应用可通过向IAP Kit发送createPurchase请求来拉起IAP Kit收银台。发起请求时,应用需在请求参数PurchaseParameter中携带此前已在华为AppGallery Connect网站上配置并生效的自动续期订阅的商品ID,并指定其productType为iap.ProductType.AUTORENEWABLE。
开发过程中易出现频繁调用接口的现象,建议控制接口调用频度,具体可参见1001860004 接口访问过频。
import { iap } from '@kit.IAPKit'; import { common } from '@kit.AbilityKit'; import { BusinessError } from '@kit.BasicServicesKit'; subscribe() { const createPurchaseParam: iap.PurchaseParameter = { productType: iap.ProductType.AUTORENEWABLE, // productId需要替换成开发者在AppGallery Connect网站配置商品信息时设置的“商品ID” productId: 'test001' }; const context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; iap.createPurchase(context, createPurchaseParam).then(async (result) => { console.info('Succeeded in creating purchase.'); // 购买成功,处理购买结果 // dealPurchaseResult实现请参见下一步 this.dealPurchaseResult(result); }).catch((err: BusinessError) => { // 购买失败 console.error(`Failed to create purchase. Code is ${err.code}, message is ${err.message}`); // dealPurchaseError实现请参见下一步 this.dealPurchaseError(err); }) }
【结果1:购买成功】
以下内容为通过客户端接收购买结果及处理的步骤说明。
建议先检查此笔订单权益的发放状态,未发放则发放权益,成功后记录SubGroupStatusPayload.lastSubscriptionStatus.lastPurchaseOrder等信息,用于后续检查权益发放状态。
发放权益后,应用需发送finishPurchase请求确认发货,以此通知IAP服务器更新商品的发货状态,完成购买流程。发送finishPurchase请求时,需在请求参数FinishPurchaseParameter中携带PurchaseOrderPayload中的productType、purchaseToken、purchaseOrderId,其中PurchaseOrderPayload为SubGroupStatusPayload.lastSubscriptionStatus.lastPurchaseOrder。请求成功后,IAP服务器会将相应商品标记为已发货。
import { iap } from '@kit.IAPKit'; import { common } from '@kit.AbilityKit'; import { BusinessError } from '@kit.BasicServicesKit'; // JWSUtil为自定义类,可参见示例代码 import { JWSUtil } from '../common/JWSUtil'; dealPurchaseResult(result: iap.CreatePurchaseResult) { const jwsSubscriptionStatus: string = JSON.parse(result.purchaseData).jwsSubscriptionStatus; if (!jwsSubscriptionStatus) { return; } const subscriptionStatus: string = JWSUtil.decodeJwsObj(jwsSubscriptionStatus); if (!subscriptionStatus) { return; } // 需自定义SubGroupStatusPayload类,包含的信息请参见SubGroupStatusPayload const subGroupStatusPayload: SubGroupStatusPayload = JSON.parse(subscriptionStatus); const lastSubscriptionStatus = subGroupStatusPayload.lastSubscriptionStatus; if (!lastSubscriptionStatus || lastSubscriptionStatus.status !== '1') { return; } const purchaseOrderPayload = lastSubscriptionStatus.lastPurchaseOrder; if (purchaseOrderPayload === undefined) { return; } // 处理发货 // ... // 发货成功后向IAP Kit发送finishPurchase请求,确认发货,完成购买 this.finishPurchase(purchaseOrderPayload); } /** * 确认发货,完成购买 * * @param purchaseOrder 订单信息,来源于购买请求 */ finishPurchase(purchaseOrder: PurchaseOrderPayload) { const finishPurchaseParam: iap.FinishPurchaseParameter = { productType: Number(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 finish purchase. Code is ${err.code}, message is ${err.message}`); }); }
【结果2:购买失败】
当用户购买失败时,需要针对code为iap.IAPErrorCode.PRODUCT_OWNED和iap.IAPErrorCode.SYSTEM_ERROR的场景,检查是否需要补发货,确保权益发放,具体请参见确保权益发放。
import { iap } from '@kit.IAPKit'; import { BusinessError } from '@kit.BasicServicesKit'; dealPurchaseError(err: BusinessError) { if (err.code === iap.IAPErrorCode.PRODUCT_OWNED || err.code === iap.IAPErrorCode.SYSTEM_ERROR) { // 参见确保权益发放检查是否需要补发货,确保权益发放 // ... } }