【HarmonyOS 6.0】IAP Kit重大改进:商品查询仅需商品ID,接入更便捷
华为鸿蒙6.0 IAP Kit商品查询接口优化摘要: 鸿蒙6.0对IAP Kit商品查询接口进行了重要改进,新增productIds属性支持仅通过商品ID列表查询商品信息,简化了参数传递。开发者不再需要强制传递商品类型参数,系统会自动匹配商品信息。该优化特别适用于包含多类型商品的复杂应用场景,提升了接口调用的简洁性。本文详细介绍了接口使用方法,包括参数配置、错误处理及代码示例,并对比了新旧版本的差

1 -> 概述
1.1 -> 什么是IAP Kit
IAP Kit(应用内支付服务)是华为鸿蒙系统为开发者提供的系统级支付能力解决方案。通过IAP Kit,开发者可以在应用中快速集成支付功能,让用户便捷地购买各类虚拟商品,从而完成商业变现。IAP Kit的核心优势在于其统一的API接口设计——开发者无需关心底层支付渠道的差异,IAP Kit会自动处理华为支付、支付宝、微信等支付方式的调用与路由,大大降低了支付接入的复杂度。
IAP Kit支持的交易模式覆盖了主流应用场景,主要包括以下三种商品类型:
- 消耗型商品(
CONSUMABLE,值为0):可以重复购买、多次使用的虚拟物品,典型场景如游戏币、体力值、道具等。 - 非消耗型商品(
NONCONSUMABLE,值为1):一次购买、永久拥有的虚拟权益,典型场景如付费解锁关卡、永久会员等。 - 自动续期订阅商品(
AUTORENEWABLE,值为2):用户购买后在一段时间内可访问增值内容,到期自动续费,典型场景如VIP月卡、会员订阅等。
1.2 -> HarmonyOS 6.0 IAP Kit的核心改进
在之前的IAP Kit版本中,开发者调用商品查询接口时,需要同时传递商品ID列表和商品类型等多个参数。这种设计虽然有助于后端进行针对性的数据查询,但在实际开发中存在一些不便之处——商品类型是开发者事先已知的信息,将其作为查询参数反复传递,既增加了参数维护的负担,也使得接口调用略显冗余。
HarmonyOS 6.0对IAP Kit的商品查询接口进行了重要优化:QueryProductsParameter对象中新增了productIds属性,支持仅传递商品ID列表即可查询商品信息,商品类型参数变成了可选参数。这一改动看似微小,实则简化了调用链路上的参数管理,使接口语义更加清晰——只需要告诉IAP Kit“我想查哪些商品”,剩下的信息由系统自动补全。
需要特别说明的是:在HarmonyOS 6.0中,productIds参数无论在调用queryProducts时是否被单独传入,它都是QueryProductsParameter中唯一必填的参数。这意味着开发者必须提供至少一个商品ID才能发起查询请求,不能只传商品类型而不传商品ID。IAP Kit会根据传入的商品ID列表,自动定位对应的商品信息并返回。
这一改进最核心的价值在于参数的精简化和语义的清晰化,尤其对于包含多类型商品的复杂应用场景,开发者不再需要人为区分哪些商品属于哪一类,只需传入商品ID列表即可。
1.3 -> 本文内容概览
本文将围绕HarmonyOS 6.0 IAP Kit的这项核心改进展开,首先介绍前置准备工作,包括开通商户服务、配置商品信息等环节;随后详细讲解商品查询接口的使用方法,涵盖接口参数详解、Promise和Callback两种调用方式,并提供完整的代码示例;接着讲解如何基于查询到的商品信息构建商品列表界面;最后总结实际开发中的最佳实践和常见问题,帮助开发者高效、安全地接入IAP Kit。
2 -> 前置准备工作
2.1 -> 开通商户服务
在正式接入IAP Kit之前,开发者需要先在AppGallery Connect中开通商户服务。这是因为商户服务决定了收益分成的收款方式——你需要配置用于接收华为分成收益的银行卡账号和币种。
开通商户服务所需的基本信息包括:收款银行卡信息(开户行国家、开户银行、开户行支行、开户名等)、税务信息(税务地点、税务注册地址、税票类型)、以及市场、财务、法务等联系人信息。
2.2 -> 在AppGallery Connect中配置商品
这是IAP Kit接入中最重要的一步。所有商品信息——包括商品ID、商品类型、各国家/地区的价格和商品名称——都需要在AppGallery Connect中提前录入。客户端调用购买接口时只需传入商品ID和商品类型,IAP Kit会根据用户当前账号的服务地自动展示对应的商品信息,开发者无需自行处理多国家/地区的价格适配问题。
商品ID的命名规则如下:
- 必须以大小写字母或数字开头;
- 只能由大小写字母(A-Z、a-z)、数字(0-9)、下划线(_)或句点(.)组成;
- 最多148个字符;
- 同一应用内商品ID不能重复,且保存后无法修改,删除后也无法再次使用。
配置商品的步骤:登录AppGallery Connect,选择“APP与元服务”,在应用列表中点击需要新增商品的应用,在“运营”页签下选择“产品运营 > 商品管理”,然后选择“商品列表”页签并点击“添加商品”。如果应用还未设置分发国家/地区,系统会弹出警告提示,需要先完成设置。
2.3 -> 配置应用签名
在HarmonyOS应用开发中,需要在工程中配置bundleName和Client ID。如果应用的compatibleSdkVersion >= 14,接入IAP Kit不再需要开发者额外添加公钥指纹和配置应用身份信息。
2.4 -> 导入IAP Kit模块
在所有准备工作完成后,在需要调用IAP Kit能力的ArkTS文件中导入模块:
import { iap } from '@kit.IAPKit';
import { common } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
@kit.IAPKit是鸿蒙官方提供的应用内支付服务Kit包,包含所有商品查询、购买创建、订单管理等核心API。
2.5 -> 商品类型定义
在使用IAP Kit的过程中,开发者需要了解商品类型的枚举定义:
| 名称 | 值 | 说明 |
|---|---|---|
| CONSUMABLE | 0 | 消耗型商品 |
| NONCONSUMABLE | 1 | 非消耗型商品 |
| AUTORENEWABLE | 2 | 自动续期订阅商品(不支持可穿戴设备) |
| NONRENEWABLE | 3 | 非续期订阅商品(不支持可穿戴设备) |
3 -> 核心API详解:商品查询接口
3.1 -> 接口签名说明
HarmonyOS 6.0 IAP Kit提供了两种调用方式的商品查询接口:
Promise方式:
function queryProducts(context: common.UIAbilityContext, parameter: QueryProductsParameter): Promise<Array<Product>>
Callback方式:
function queryProducts(context: common.UIAbilityContext, parameter: QueryProductsParameter, callback: AsyncCallback<Array<Product>>): void
两种方式的区别在于异步结果的处理方式——Promise方式更适合现代化的async/await编程模式,代码可读性更好;Callback方式则保持了与传统回调风格的兼容性。
3.2 -> QueryProductsParameter参数详解
QueryProductsParameter是商品查询请求的参数对象。在HarmonyOS 6.0中,该对象的参数设计发生了重要变化:
- productIds:字符串数组类型,必填。需要查询的商品ID列表,商品ID必须已经在AppGallery Connect中创建且唯一。一次最多可查询200个商品,如果商品数量较多建议分批查询。
- productType:数值类型,变为可选。用于指定要查询的商品类型,与productIds共同约束返回结果。
新旧版本对比:早期版本的QueryProductsParameter要求必须同时提供productId和productType,且缺少其中一个参数都会导致查询失败。而在HarmonyOS 6.0中,通过productIds参数仅传递商品ID即可完成查询,productType参数是可选的。这一改进的关键优势在于:当应用需要同时展示消耗型和非消耗型商品时,开发者不再需要分两次调用接口(一次传消耗型+商品ID、一次传非消耗型+商品ID),只需将所有商品ID一次性传入即可。
但在实际开发场景中,仍然有几种情况下建议使用productType参数:其一,当调用方需要明确限定查询结果的类型时,通过productType可以起到过滤作用;其二,当应用需要统计不同类型商品的查询成功率或展示数据时,productType的区分具有重要的业务意义。
3.3 -> 返回值详解
接口成功调用后,返回Array<Product>,即Product对象的数组。Product对象中包含商品的完整信息,开发者可以利用这些信息在UI上展示商品。主要字段包括:
- productId:商品ID,与请求中的ID一一对应;
- productName:商品名称,支持多语言,IAP Kit会自动根据用户当前语言环境返回对应语言的名称;
- productType:商品类型,反映该商品属于消耗型、非消耗型还是订阅型;
- price:商品价格(含税),已根据用户账号所在地自动换算;
- currencyCode:货币代码(如CNY、USD);
- localPrice:本地化价格字符串,可直接用于UI展示;
- description:商品简介;
- iconUrl:商品图标URL。
3.4 -> 错误码说明
商品查询过程中可能会遇到的常见错误码:
| 错误码 | 说明 | 处理建议 |
|---|---|---|
| 1001860001 | 内部错误 | 检查网络连接,稍后重试 |
| 1001860004 | 请求频率过高 | 降低调用频率,避免1秒内超过3次 |
| 商品未审核通过 | 商品状态无效 | 确认商品已在AGC中提交并通过审核 |
其中错误码1001860001——内部错误,是在查询商品信息时较为常见的问题之一,通常表现为商品ID尚未在AGC中通过审核,或者网络层面出现异常导致请求失败。在开发调试阶段,可通过沙盒测试来排除这些问题。
4 -> 代码实现示例
4.1 -> 导入依赖模块
import { iap } from '@kit.IAPKit';
import { common } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
4.2 -> 基础示例:仅使用productIds查询(鸿蒙6.0推荐方式)
这是鸿蒙6.0推荐的查询方式,仅传入商品ID列表即可:
// 在UIAbility或自定义组件中调用
async queryProductList(): Promise<void> {
// 获取UIContext并转换为UIAbilityContext
const context: common.UIAbilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext;
// 构建参数:仅传入商品ID列表
const parameter: iap.QueryProductsParameter = {
productIds: ['premium_monthly', 'coin_pack_100', 'ad_remove'] // 直接提供商品ID列表
// productType 为可选参数,此处不传
};
try {
const products: Array<iap.Product> = await iap.queryProducts(context, parameter);
console.info(`查询成功,共获取到 ${products.length} 个商品`);
// 遍历处理商品信息
products.forEach((product: iap.Product) => {
console.info(`商品ID: ${product.productId}`);
console.info(`商品名称: ${product.productName}`);
console.info(`商品价格: ${product.localPrice}`);
console.info(`商品类型: ${product.productType}`);
});
} catch (err) {
const error = err as BusinessError;
console.error(`查询失败: code=${error.code}, message=${error.message}`);
}
}
这段代码中的parameter参数并未包含productType字段,queryProducts接口会根据productIds中的商品ID自动匹配对应的商品类型。当应用需要展示混合类型的商品时,这个特性可以大幅简化调用逻辑——开发者无需再根据商品类型拆分成多次查询请求。
4.3 -> 同时使用productIds和productType(带类型过滤)
如果开发者仍然需要按商品类型过滤查询结果,可以同时传递productType参数:
async queryProductsByType(): Promise<void> {
const context = this.getUIContext().getHostContext() as common.UIAbilityContext;
const parameter: iap.QueryProductsParameter = {
productType: iap.ProductType.CONSUMABLE, // 限定只查询消耗型商品
productIds: ['coin_pack_100', 'coin_pack_500'] // 指定要查询的商品ID
};
try {
const products = await iap.queryProducts(context, parameter);
console.info(`找到 ${products.length} 个消耗型商品`);
} catch (err) {
console.error(`查询失败: ${(err as BusinessError).message}`);
}
}
productType与productIds同时指定时,接口会返回两者交集的结果——即传入的商品ID中,只有类型匹配的那些才会被返回。例如,如果某个商品ID实际配置为非消耗型商品,但在请求中指定了productType: CONSUMABLE,则该商品不会被返回。
4.4 -> 使用Callback方式
对于偏好回调风格的开发者,也可以使用Callback方式调用:
queryProductsWithCallback(): void {
const context = this.getUIContext().getHostContext() as common.UIAbilityContext;
const parameter: iap.QueryProductsParameter = {
productIds: ['premium_monthly', 'coin_pack_100']
};
iap.queryProducts(context, parameter, (err: BusinessError, data: Array<iap.Product>) => {
if (err) {
console.error(`查询失败: code=${err.code}, message=${err.message}`);
return;
}
console.info(`查询成功,共获取到 ${data.length} 个商品`);
// 处理商品数据
data.forEach((product: iap.Product) => {
console.info(`${product.productName}: ${product.localPrice}`);
});
});
}
两种调用方式各有适用场景:Promise方式更加现代化,便于使用async/await进行错误处理,代码逻辑更清晰;Callback方式则在不支持Promise的环境下使用,或者在与旧代码保持一致的场景下选择。
4.5 -> 完整示例:带环境检查的商品查询
在实际生产环境中,建议在执行商品查询前先检查环境状态:
import { iap } from '@kit.IAPKit';
import { common } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
class IAPManager {
private context: common.UIAbilityContext;
constructor(context: common.UIAbilityContext) {
this.context = context;
}
// 1. 首先检查环境是否支持支付
async checkEnvironmentAndQuery(): Promise<Array<iap.Product> | null> {
try {
// 检查环境状态
await iap.queryEnvironmentStatus(this.context);
console.info('环境检查通过,支持应用内支付');
// 环境通过后再查询商品
return await this.queryProducts();
} catch (err) {
const error = err as BusinessError;
console.error(`环境检查失败或支付环境不支持: ${error.message}`);
// 环境不支持时,可以考虑隐藏支付相关按钮
return null;
}
}
// 2. 查询商品信息(鸿蒙6.0简化方式)
async queryProducts(): Promise<Array<iap.Product>> {
const parameter: iap.QueryProductsParameter = {
productIds: [
'consumable_gem_pack', // 消耗型商品
'nonconsumable_ad_free', // 非消耗型商品
'subscription_vip' // 自动续期订阅商品
]
// productType 为可选参数,此处不传即可一次性获取所有类型商品
};
try {
const products = await iap.queryProducts(this.context, parameter);
console.info(`查询成功,返回商品数量: ${products.length}`);
return products;
} catch (err) {
const error = err as BusinessError;
console.error(`查询商品失败: code=${error.code}, message=${error.message}`);
throw error;
}
}
// 批量查询场景:当商品数量超过200时需要分批
async queryProductsBatch(productIds: string[], batchSize: number = 200): Promise<Array<iap.Product>> {
const allProducts: Array<iap.Product> = [];
for (let i = 0; i < productIds.length; i += batchSize) {
const batch = productIds.slice(i, i + batchSize);
const parameter: iap.QueryProductsParameter = { productIds: batch };
try {
const products = await iap.queryProducts(this.context, parameter);
allProducts.push(...products);
} catch (err) {
console.error(`第${Math.floor(i / batchSize) + 1}批查询失败: ${(err as BusinessError).message}`);
// 可以根据业务需求决定是中断还是继续
}
}
return allProducts;
}
}
4.6 -> 构建商品列表界面
查询到商品信息后,可以使用这些数据构建商品列表UI:
import { Product } from '@kit.IAPKit';
@Entry
@Component
struct ShopPage {
@State productList: Product[] = [];
@State loading: boolean = true;
private iapManager: IAPManager = new IAPManager(getContext(this) as common.UIAbilityContext);
async aboutToAppear(): Promise<void> {
await this.loadProducts();
}
async loadProducts(): Promise<void> {
this.loading = true;
const result = await this.iapManager.checkEnvironmentAndQuery();
if (result) {
this.productList = result;
}
this.loading = false;
}
build() {
Column() {
if (this.loading) {
LoadingProgress().width(48).height(48);
} else {
List() {
ForEach(this.productList, (product: Product) => {
ListItem() {
Row() {
Image(product.iconUrl)
.width(60).height(60)
.borderRadius(8)
Column({ space: 4 }) {
Text(product.productName)
.fontSize(16).fontWeight(FontWeight.Medium)
Text(product.description)
.fontSize(12).fontColor('#666')
Text(product.localPrice)
.fontSize(18).fontColor('#FF6B6B')
.fontWeight(FontWeight.Bold)
}
.alignItems(HorizontalAlign.Start)
.margin({ left: 12 })
.layoutWeight(1)
}
.width('100%')
.padding(16)
.onClick(() => this.purchaseProduct(product))
}
})
}
}
}
}
purchaseProduct(product: Product): void {
// 跳转到购买逻辑
console.info(`开始购买: ${product.productId}`);
}
}
4.7 -> 购买商品的基础示例
查询到商品信息后,用户在商品列表中选择某个商品时,可以调用购买接口:
async purchaseProduct(productId: string, productType: number): Promise<void> {
const context = this.getUIContext().getHostContext() as common.UIAbilityContext;
try {
const result = await iap.createPurchase(context, {
productId: productId,
productType: productType
});
console.info(`购买成功: orderId=${result.orderId}`);
// 处理购买成功后的发货逻辑
await this.deliverProduct(result);
} catch (err) {
const error = err as BusinessError;
console.error(`购买失败: ${error.message}`);
}
}
createPurchase请求中只需要携带商品ID和商品类型,IAP Kit会自动创建订单并展示收银台。
5 -> 最佳实践与避坑指南
5.1 -> 查询商品数量的限制
queryProducts接口一次最多可查询200个商品。如果应用的商品数量较多(例如游戏有数百种道具),应当在客户端实现分批查询逻辑,每批不超过200个商品。上面4.5节的queryProductsBatch方法已经提供了分批查询的参考实现。
5.2 -> 商品配置与查询失败的常见原因
查询商品信息时接口报错是开发过程中最常见的问题。即便后续购买逻辑正常,查询商品信息这一步也可能先失败。根据社区中开发者遇到的实际情况,商品未审核通过是导致查询失败的最常见原因——即便沙盒测试环境下支付功能可以正常调起,商品信息仍然可能因为尚未通过审核而查不到。
因此在开发调试阶段,务必确认以下几点:商品是否已在AppGallery Connect中提交并审核通过;商品销售范围是否已配置至少一个国家/地区;商品状态是否为“已上架”。
5.3 -> 支付频率控制
调用购买接口时需要注意频率控制。IAP Kit系统侧存在风控机制,如果调用过于频繁会触发风控限制,导致接口报错。实际生产环境中,应当避免在短时间内连续发起购买请求。例如,用户连续点击购买按钮时应当进行防抖处理,通过节流或增加冷却时间来控制请求频率。
5.4 -> 发货确认流程
购买成功后,有一个极其重要但容易被忽视的环节——发货确认。finishPurchase方法必须在权益成功发放后才能调用,调用顺序绝对不能颠倒。错误的做法是先调用finishPurchase再发货,这样会造成用户未收到货但钱已经扣了的严重后果。正确的做法是在服务器端或客户端确认权益发放成功后,再调用finishPurchase完成订单的最终确认。
5.5 -> 补货机制
在支付过程中可能遇到一些边界场景,例如用户支付成功后应用意外闪退、网络异常导致发货确认未完成等。针对这种情况,建议在应用启动时调用queryPurchases方法查询未完成发货的订单,并进行补发处理。这种机制可以有效防止掉单,确保用户权益在任何情况下都能得到保障。
5.6 -> 数据验证
支付数据的安全性至关重要。建议采用服务端通知的方式接收购买结果,而非完全依赖客户端回调。服务端收到华为发送的支付通知后,应当进行签名验签,确认订单的真实性和完整性后再发放权益。这种方案比纯客户端的可靠性更高,适用于对安全性要求较高的场景。
6 -> 总结
HarmonyOS 6.0 IAP Kit在商品查询接口上的优化,表面上看只是一个参数的调整,但其背后反映的是一种简化开发者体验的设计哲学。productIds参数的引入使查询逻辑更加直观自然,尤其对于需要混合展示多种类型商品的应用场景,原本需要多次调用的代码可以简化为单次调用。这正是IAP Kit持续演进的方向——让支付能力的接入成本不断降低,让开发者能够将更多精力聚焦于应用的核心业务本身。
从整体接入流程来看,IAP Kit的核心环节可以归纳为五个步骤:开通商户服务 → 配置商品信息 → 查询商品展示 → 发起购买 → 确认发货。其中商品查询是连接配置与交易的关键桥梁。鸿蒙6.0对商品查询接口的精简,使得这座桥梁的搭建更加顺畅。
对于正在接入或计划接入IAP Kit的开发者而言,本文介绍的最佳实践和避坑指南尤其值得关注——查询失败排查、支付频率控制、发货顺序确认以及补发机制的设计,往往是项目上线后最容易出现问题的环节。提前在这些方面做好防御性设计,可以大幅提升应用的支付稳定性和用户体验。
更多推荐




所有评论(0)