系列生态共建篇·第53篇。跨端篇后,有开源爱好者问:“我在电商Demo里写了很多通用组件(如SKU选择器、地址联动),能不能抽离出来给社区用?怎么做成标准的OpenHarmony三方库?” 这正是开源生态的魅力。今天我们将电商Demo中的通用支付模块和SKU选择组件抽离、封装,发布为一个标准的OpenHarmony三方库(HAR包),并上架到OHPM(OpenHarmony Package Manager)仓库。我们将覆盖库工程搭建、API设计、文档撰写、单元测试、CI发布全流程。全程基于API23,含官方文档未涉及的“多目标构建”和“语义化版本控制”技巧。
一、前言:为什么“造轮子”也要讲姿势?
很多开发者写过“工具类”,但那只是“代码片段”。真正的三方库需要具备:
独立性:不依赖具体业务(如电商Demo),可独立编译和运行。
通用性:API设计抽象,能适应多种场景(如支付模块支持支付宝、微信、银联)。
稳定性:经过充分测试,版本迭代不破坏兼容性。
易用性:文档齐全,示例清晰,一键集成。
OHPM是OpenHarmony的官方包管理器,类似于npm(Node.js)或Maven(Android)。今天,我们将把电商Demo中的“支付功能”提炼成一个名为@harmony/payment-kit的高质量三方库,并贡献给开源社区。
二、核心概念辨析(代码片段 vs 三方库)
维度 | 代码片段 (Utils/Snippets) | 三方库 (Library/HAR) |
|---|---|---|
复用性 | 低,需复制粘贴修改 | 高,一键集成 ( |
维护性 | 差,分散在各项目中 | 好,集中维护,版本化管理 |
测试 | 无或简陋 | 完善,包含单元测试、集成测试 |
文档 | 注释为主 | 独立文档、API参考、示例工程 |
依赖 | 隐式依赖项目环境 | 显式声明依赖,自动解决 |
发布 | 口头分享 | OHPM中央仓库,可检索 |
三、代码实现:从“业务代码”到“开源库”
3.1 创建HAR库工程
步骤1:新建Library Module
在DevEco Studio中:File->New->Module->Static Library (HAR)。
命名为payment-kit。
步骤2:工程结构规划
payment-kit/ ├── src/main/ets/ │ ├── components/ # UI组件(如支付密码弹窗) │ │ └── PayPasswordDialog.ets │ ├── core/ # 核心逻辑 │ │ ├── PaymentManager.ets │ │ └── ChannelAdapter.ets │ ├── models/ # 数据模型 │ │ └── PaymentInfo.ets │ ├── utils/ # 工具类 │ │ └── SignUtil.ets │ ├── index.ets # 对外暴露的API入口(关键!) │ └── resources/ # 资源文件 ├── src/test/ets/ # 单元测试 ├── oh-package.json5 # 库配置文件(类似package.json) └── README.md # 项目说明文档3.2 抽离核心逻辑:支付管理器
创建src/main/ets/core/PaymentManager.ets:
// 定义支付渠道枚举 export enum PayChannel { ALIPAY = 'alipay', WECHAT = 'wechat', UNIONPAY = 'unionpay', HUAWEI_IAP = 'huawei_iap' // 华为IAP } // 定义支付结果回调 export interface PaymentCallback { onSuccess?(result: PaymentResult): void onFailed?(code: number, msg: string): void onCancel?(): void } // 支付管理器(单例) export class PaymentManager { private static instance: PaymentManager private channels: Map<PayChannel, ChannelAdapter> = new Map() private currentCallback: PaymentCallback | null = null static getInstance(): PaymentManager { if (!PaymentManager.instance) { PaymentManager.instance = new PaymentManager() } return PaymentManager.instance } /** * 注册支付渠道适配器 */ registerChannel(channel: PayChannel, adapter: ChannelAdapter): void { this.channels.set(channel, adapter) console.log(`支付渠道注册成功: ${channel}`) } /** * 发起支付 */ pay(info: PaymentInfo, callback: PaymentCallback): void { this.currentCallback = callback const adapter = this.channels.get(info.channel) if (!adapter) { callback.onFailed?.(-1, `支付渠道 ${info.channel} 未注册`) return } // 参数校验 if (!this.validateParams(info)) { callback.onFailed?.(-2, '支付参数校验失败') return } // 调用具体渠道的支付逻辑 adapter.pay(info, { onSuccess: (result) => { this.handleSuccess(result) }, onFailed: (code, msg) => { this.handleFailed(code, msg) }, onCancel: () => { this.handleCancel() } }) } /** * 参数校验 */ private validateParams(info: PaymentInfo): boolean { if (!info.orderId || !info.amount || info.amount <= 0) { return false } return true } private handleSuccess(result: PaymentResult): void { console.log('支付成功:', result) this.currentCallback?.onSuccess?.(result) } private handleFailed(code: number, msg: string): void { console.error('支付失败:', code, msg) this.currentCallback?.onFailed?.(code, msg) } private handleCancel(): void { console.log('支付取消') this.currentCallback?.onCancel?.() } } // 渠道适配器接口(策略模式) export interface ChannelAdapter { pay(info: PaymentInfo, callback: PaymentCallback): void }3.3 实现具体渠道:华为IAP适配器
创建src/main/ets/core/adapters/HuaweiIAPAdapter.ets:
import { iap } from '@kit.IAPKit' import { PaymentCallback, ChannelAdapter, PaymentInfo, PaymentResult } from '../PaymentManager' export class HuaweiIAPAdapter implements ChannelAdapter { async pay(info: PaymentInfo, callback: PaymentCallback): Promise<void> { try { // 1. 创建订单 const order = await iap.createPurchaseOrder({ productId: info.productId!, quantity: info.quantity || 1 }) // 2. 发起支付 const payResult = await iap.pay(order) // 3. 处理支付结果 if (payResult.returnCode === 0) { const result: PaymentResult = { orderId: info.orderId, transactionId: payResult.inAppPurchaseData?.inAppPurchaseData?.orderId || '', channel: 'huawei_iap', rawData: JSON.stringify(payResult) } callback.onSuccess?.(result) } else { callback.onFailed?.(payResult.returnCode, payResult.errMsg || '支付失败') } } catch (err) { console.error('华为IAP支付异常:', err) callback.onFailed?.(-3, '支付过程发生异常') } } }3.4 定义对外API(入口文件)
关键:src/main/ets/index.ets是库的“脸面”,必须清晰、简洁。
// 核心类 export { PaymentManager } from './core/PaymentManager' export { HuaweiIAPAdapter } from './core/adapters/HuaweiIAPAdapter' // 导出枚举和接口,方便使用者 export { PayChannel } from './core/PaymentManager' export type { PaymentCallback, PaymentResult } from './core/PaymentManager' export type { PaymentInfo } from './models/PaymentInfo' // 提供便捷的初始化函数 import { PaymentManager } from './core/PaymentManager' import { HuaweiIAPAdapter } from './core/adapters/HuaweiIAPAdapter' export function initPaymentKit(): PaymentManager { const manager = PaymentManager.getInstance() // 默认注册华为IAP渠道 manager.registerChannel(PayChannel.HUAWEI_IAP, new HuaweiIAPAdapter()) return manager }3.5 配置库信息(oh-package.json5)
{ "name": "@harmony/payment-kit", "version": "1.0.0", "description": "A universal payment kit for HarmonyOS, supporting multiple channels.", "main": "src/main/ets/index.ets", "author": "listening777", "license": "Apache-2.0", "keywords": ["harmonyos", "payment", "iap", "alipay", "wechat"], "repository": { "type": "git", "url": "https://gitee.com/your_repo/payment-kit.git" }, "dependencies": { "@ohos/iap": "^1.0.0" // 声明对IAP Kit的依赖 }, "devDependencies": { "@ohos/hypium": "^1.0.0" // 单元测试框架 }, "ohos": { "minAPIVersion": 11, // 支持的最低API版本 "targetAPIVersion": 12 // 目标API版本 } }3.6 编写README.md(门面担当)
# @harmony/payment-kit 一个用于HarmonyOS的通用支付聚合库,旨在简化多支付渠道的集成流程。 ## 特性 - 🚀 **一键集成**:一行代码初始化,支持链式调用。 - 🔌 **可扩展**:通过适配器模式轻松接入新支付渠道。 - 🛡️ **类型安全**:完整的TypeScript类型定义。 - 📱 **跨端支持**:基于ArkUI-X,支持HarmonyOS、Android、iOS。 ## 安装bash
ohpm install @harmony/payment-kit
## 快速开始typescript
import { initPaymentKit, PayChannel, PaymentInfo } from '@harmony/payment-kit'
// 1. 初始化
const paymentKit = initPaymentKit()
// 2. 构建支付信息
const info: PaymentInfo = {
orderId: 'ORDER_123456',
amount: 99.8,
currency: 'CNY',
channel: PayChannel.HUAWEI_IAP,
productId: 'product_001', // 华为IAP商品ID
subject: '测试商品'
}
// 3. 发起支付
paymentKit.pay(info, {
onSuccess: (result) => {
console.log('支付成功:', result.transactionId)
},
onFailed: (code, msg) => {
console.error('支付失败:', code, msg)
},
onCancel: () => {
console.log('用户取消支付')
}
})
## API文档 ### PaymentManager - `registerChannel(channel: PayChannel, adapter: ChannelAdapter)`: 注册支付渠道。 - `pay(info: PaymentInfo, callback: PaymentCallback)`: 发起支付。 ### PaymentInfo | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | orderId | string | 是 | 商户订单号 | | amount | number | 是 | 支付金额 | | channel | PayChannel | 是 | 支付渠道 | | productId | string | 否 | 商品ID(IAP需要) | ## 贡献指南 欢迎PR!请确保: 1. 代码通过`ohpm run lint`检查。 2. 新增功能包含单元测试。 3. 更新README文档。 ## 许可证 Apache License 2.0
四、踩坑记录(官方文档没写的开源细节)
API设计的“洁癖”:三方库的API一旦发布,修改成本极高。原则:宁缺毋滥。不要在1.0.0版本暴露过多的内部方法。使用
export严格控制对外API,内部类使用internal或文件夹隔离。资源命名的“隔离”:如果库中使用了图片、字符串等资源,务必添加前缀(如
pk_),防止与主工程资源冲突。例如$r('app.media.pk_pay_icon')。多目标构建(Multi-target Build):如果库需要支持HarmonyOS和OpenHarmony(社区版),需要注意API差异。使用条件编译:
// 条件编译:仅HarmonyOS支持 // @ts-ignore if (canIUse('SystemCapability.ArkUI.ArkUI.Full')) { // HarmonyOS特有逻辑 }版本号的“敬畏”:严格遵守语义化版本(SemVer):
主版本.次版本.修订号。主版本:不兼容的API修改(如重构了支付流程)。
次版本:向后兼容的功能新增(如增加了新的支付渠道)。
修订号:向后兼容的问题修正(如修复了某个NullPointerException)。
OHPM发布的“门槛”:首次发布需要实名认证(个人或企业)。包名(
name)必须全局唯一,且不能以@ohos/开头(那是官方包)。建议使用@组织名/包名的格式。