HarmonyOs 华为支付收银台全链路集成指南:从商户模型到支付落地
本文详解华为支付收银台集成方案,涵盖核心支付能力(单次支付、合单支付等四类)及直连商户、平台类商户、服务商三种适配模型,支持分账功能。解析全流程:商户客户端创建订单,服务器获取 prepayId 生成 orderStr,客户端调起收银台,支付结果通过同步返回与异步回调处理,强调服务端验签重要性。提供 ArkTS 客户端与 Java 服务端代码示例,说明错误处理。介绍 SM2 加密等安全机制,给出接
华为支付作为 HarmonyOS 生态中的核心支付能力,为开发者提供了安全、便捷的支付解决方案。本文将系统解析华为支付的商户模型、支付能力、接入流程及代码实现,通过图表、流程图和实战代码,帮助开发者快速完成支付功能集成。
一、华为支付能力全景概览
华为支付收银台是一个集订单管理、支付渠道整合、安全验证于一体的综合支付解决方案,其核心优势体现在:
- 多场景适配:支持电商、娱乐、零售、交通等全品类业务
- 灵活的商户模型:满足直连商户、平台类商户、服务商等不同业务模式
- 全链路安全保障:基于 SM2 加密算法和华为终端安全能力,确保支付信息安全
- 极简接入流程:提供标准化 API,减少开发成本,加快上线速度
1.1 支付能力矩阵
华为支付提供四类核心支付能力,覆盖不同业务场景:
支付类型 | 核心功能 | 支持商户类型 | 典型应用场景 |
---|---|---|---|
单次支付 | 完成单一订单的支付流程 | 直连商户、平台类商户、服务商 | 商品购买、服务付费 |
合单支付 | 合并多个商户的订单为一个支付单 | 平台类商户 | 电商平台多店铺联合结算 |
支付并签约 | 支付完成后自动签订代扣协议 | 直连商户、服务商 | 会员订阅、服务套餐购买 |
签约代扣 | 预先签约后自动完成扣款 | 直连商户、服务商 | 水电费代缴、自动充值 |
说明:"签约代扣" 需用户预先授权,适用于周期性扣款场景,可大幅减少用户操作步骤。
二、商户模型深度解析
华为支付支持三种商户模型,开发者需根据自身业务模式选择适配的接入方式:
2.1 商户模型对比表
模型类型 | 定义 | 核心能力 | 接入主体 | 典型案例 |
---|---|---|---|---|
直连商户 | 直接与华为支付签约的商户 | 支持单次支付、支付并签约、签约代扣 | 企业或个体工商户 | 独立电商 APP、品牌官网 |
平台类商户 | 聚合多个子商户的平台型企业 | 支持合单支付,可配置分账规则 | 电商平台、服务聚合平台 | 综合电商平台、生活服务平台 |
服务商 | 为子商户提供支付技术服务的企业 | 代子商户发起支付,支持分账 | 支付解决方案提供商 | 收银系统服务商、SaaS 平台 |
2.2 分账能力说明
平台类商户和合单支付支持灵活分账,可通过 API 设置分账接收方及比例:
- 支持最多 10 个分账接收方
- 分账比例精确到 0.01%
- 支持实时分账和延迟分账两种模式
三、支付全流程解析(含流程图)
华为支付收银台的接入流程涉及商户客户端、商户服务器、华为支付服务器三方交互,核心步骤包括订单创建、预下单、拉起收银台、支付结果处理等。
3.1 完整业务流程图
3.2 关键步骤说明
- 订单创建:商户客户端向自身服务器提交商品信息,生成商户侧订单号(需确保唯一性)。
- 预下单:商户服务器调用华为支付预下单接口,获取
prepayId
(华为支付侧唯一订单标识)。 - 订单字符串组装:商户服务器将
prepayId
、app_id
、timestamp
等信息签名后,生成orderStr
返回给客户端。 - 拉起收银台:客户端调用
requestPayment
接口,传入orderStr
,调起华为支付客户端。 - 支付结果处理:
- 同步结果:华为支付客户端直接返回支付状态(适用于 UI 展示)。
- 异步回调:华为支付服务器向商户服务器发送带签名的支付结果(用于业务最终确认)。
- 签名验证:商户服务器必须验证回调结果的签名,防止数据篡改。
四、客户端集成实战(ArkTS 代码)
4.1 环境准备
-
依赖配置:在
module.json5
中添加支付权限和依赖:
{
"module": {
"reqPermissions": [
{ "name": "ohos.permission.PAYMENT" }
],
"dependencies": {
"@kit.PaymentKit": { "version": "1.0.0" }
}
}
}
-
参数说明:
orderStr
需包含以下核心字段(由商户服务器生成):app_id
:应用在华为开发者联盟的唯一标识merc_no
:商户号(华为支付签约后分配)prepay_id
:预下单接口返回的预订单号timestamp
:时间戳(秒级)noncestr
:随机字符串(32 位以内)sign
:签名结果(SM2 算法)
4.2 拉起收银台代码实现
import { BusinessError } from '@kit.BasicServicesKit';
import { paymentService } from '@kit.PaymentKit';
import { common } from '@kit.AbilityKit';
import promptAction from '@ohos.promptAction';
@Entry
@Component
struct PaymentPage {
// 获取应用上下文
private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
// 商户订单号(从商户服务器获取)
@State merchantOrderNo: string = '';
// 支付状态
@State payStatus: string = '未支付';
build() {
Column() {
Text('商品:华为Mate 60 Pro')
.fontSize(18)
.margin(10)
Text('金额:5999.00元')
.fontSize(16)
.fontColor('#ff4d4f')
.margin(10)
Text(`支付状态:${this.payStatus}`)
.fontSize(14)
.margin(20)
Button('确认支付')
.type(ButtonType.Capsule)
.width('60%')
.height(48)
.backgroundColor('#1677ff')
.onClick(() => this.startPayment())
}
.width('100%')
.height('100%')
.padding(20)
}
/**
* 发起支付流程
*/
private async startPayment() {
try {
// 1. 向商户服务器请求创建订单
this.merchantOrderNo = await this.createMerchantOrder();
if (!this.merchantOrderNo) {
promptAction.showToast({ message: '创建订单失败' });
return;
}
// 2. 获取orderStr(由商户服务器生成)
const orderStr = await this.getOrderStr();
if (!orderStr) {
promptAction.showToast({ message: '获取支付参数失败' });
return;
}
// 3. 调用华为支付收银台
this.payStatus = '支付中...';
await this.requestPayment(orderStr);
// 4. 支付成功后查询最终结果(可选,建议依赖服务端回调)
const result = await this.queryPaymentResult();
if (result === 'SUCCESS') {
this.payStatus = '支付成功';
promptAction.showToast({ message: '支付成功' });
}
} catch (error) {
const err = error as BusinessError;
this.payStatus = `支付失败:${err.message}`;
promptAction.showToast({ message: `支付失败:${err.code}-${err.message}` });
}
}
/**
* 调用华为支付API拉起收银台
*/
private requestPayment(orderStr: string): Promise<void> {
return new Promise((resolve, reject) => {
paymentService.requestPayment(this.context, orderStr)
.then(() => {
// 同步结果:支付流程完成(需以服务端回调为准)
resolve();
})
.catch((error: BusinessError) => {
reject(error);
});
});
}
/**
* 向商户服务器请求创建订单
*/
private createMerchantOrder(): Promise<string> {
// 实际开发中替换为商户服务器接口
return new Promise(resolve => {
setTimeout(() => {
// 生成模拟订单号
resolve(`MERCH_${Date.now()}`);
}, 500);
});
}
/**
* 从商户服务器获取orderStr
*/
private getOrderStr(): Promise<string> {
// 实际开发中替换为商户服务器接口
return new Promise(resolve => {
setTimeout(() => {
// 注意:以下为模拟数据,实际需由商户服务器生成并签名
const mockOrderStr = JSON.stringify({
app_id: '1000000001',
merc_no: '202405120001',
prepay_id: `PREPAY_${Date.now()}`,
timestamp: Math.floor(Date.now() / 1000).toString(),
noncestr: 'a1b2c3d4e5f6g7h8i9j0',
sign: '模拟签名(实际为SM2加密结果)'
});
resolve(mockOrderStr);
}, 800);
});
}
/**
* 查询支付最终结果(向商户服务器查询)
*/
private queryPaymentResult(): Promise<string> {
// 实际开发中替换为商户服务器查询接口
return new Promise(resolve => {
setTimeout(() => {
resolve('SUCCESS'); // 模拟支付成功
}, 1000);
});
}
}
4.3 错误码处理机制
支付过程中可能遇到多种异常,需针对性处理:
错误码 | 含义 | 处理建议 |
---|---|---|
6001 | 用户取消支付 | 提示用户 "已取消支付,可重新发起" |
6002 | 支付参数错误 | 检查 orderStr 格式及签名有效性 |
6003 | 网络异常 | 提示用户检查网络,并重试支付 |
6004 | 支付渠道不可用 | 切换其他支付方式(如支持) |
6005 | 订单已支付 | 跳转到支付成功页面,避免重复支付 |
五、服务端集成关键步骤(Java 示例)
商户服务端需完成预下单、签名生成、支付结果验签三个核心步骤,确保支付流程的安全性和准确性。
5.1 预下单接口调用
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.client.RestTemplate;
public class HuaweiPayService {
// 华为支付预下单接口地址(正式环境)
private static final String PREPAY_URL = "https://pay.cloud.huawei.com/pay/v1/prepay";
// 商户号(华为支付签约后分配)
private String merchantNo;
// 应用ID(华为开发者联盟申请)
private String appId;
// 商户私钥(用于签名)
private String privateKey;
/**
* 调用华为支付预下单接口
*/
public PrepayResponse createPrepay(PrepayRequest request) {
// 1. 组装请求参数
HuaweiPrepayRequest huaweiRequest = new HuaweiPrepayRequest();
huaweiRequest.setAppId(appId);
huaweiRequest.setMercNo(merchantNo);
huaweiRequest.setOutTradeNo(request.getMerchantOrderNo());
huaweiRequest.setTotalAmount(request.getTotalAmount()); // 单位:分
huaweiRequest.setCurrency("CNY");
huaweiRequest.setSubject(request.getProductName());
huaweiRequest.setNotifyUrl("https://商户域名/pay/callback"); // 支付结果回调地址
huaweiRequest.setTimestamp(String.valueOf(System.currentTimeMillis() / 1000));
huaweiRequest.setNonceStr(generateNonceStr());
// 2. 生成签名
String sign = Sm2Utils.sign(privateKey, buildSignStr(huaweiRequest));
huaweiRequest.setSign(sign);
// 3. 发送请求
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<HuaweiPrepayRequest> httpRequest = new HttpEntity<>(huaweiRequest, headers);
return restTemplate.postForObject(PREPAY_URL, httpRequest, PrepayResponse.class);
}
/**
* 生成随机字符串
*/
private String generateNonceStr() {
return java.util.UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
}
/**
* 构建签名原文(按ASCII排序)
*/
private String buildSignStr(HuaweiPrepayRequest request) {
// 签名参数需按key的ASCII顺序排序,拼接格式:key=value&key=value
return String.format("appId=%s¤cy=%s&mercNo=%s&nonceStr=%s¬ifyUrl=%s&outTradeNo=%s&subject=%s×tamp=%s&totalAmount=%s",
request.getAppId(),
request.getCurrency(),
request.getMercNo(),
request.getNonceStr(),
request.getNotifyUrl(),
request.getOutTradeNo(),
request.getSubject(),
request.getTimestamp(),
request.getTotalAmount()
);
}
}
5.2 支付结果回调与验签
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class PaymentCallbackController {
// 华为支付公钥(用于验签,从华为商户平台获取)
private static final String HUAWEI_PUBLIC_KEY = "华为支付公钥";
/**
* 华为支付结果回调接口
*/
@PostMapping("/pay/callback")
public String handleCallback(@RequestBody PaymentCallback callback) {
try {
// 1. 验证签名
boolean verifySuccess = verifySign(callback);
if (!verifySuccess) {
// 签名验证失败,返回错误
return "fail";
}
// 2. 处理支付结果
String tradeStatus = callback.getTradeStatus();
if ("SUCCESS".equals(tradeStatus)) {
// 支付成功:更新商户订单状态
updateOrderStatus(callback.getOutTradeNo(), "PAID");
// 处理分账(如需要)
if (needSplit(callback)) {
processSplit(callback);
}
} else {
// 支付失败:记录失败原因
logPaymentFailure(callback.getOutTradeNo(), callback.getFailReason());
}
// 3. 返回处理结果(必须返回"success",否则华为支付会重试回调)
return "success";
} catch (Exception e) {
// 异常处理:记录日志,返回失败(华为支付会重试)
return "fail";
}
}
/**
* 验证回调签名
*/
private boolean verifySign(PaymentCallback callback) {
// 1. 构建签名原文(按ASCII排序)
String signStr = buildCallbackSignStr(callback);
// 2. 使用华为公钥验证签名
return Sm2Utils.verify(HUAWEI_PUBLIC_KEY, signStr, callback.getSign());
}
/**
* 构建回调签名原文
*/
private String buildCallbackSignStr(PaymentCallback callback) {
// 按华为支付文档要求的字段顺序拼接
return String.format("appId=%s&mercNo=%s&outTradeNo=%s&tradeNo=%s&tradeStatus=%s&totalAmount=%s×tamp=%s",
callback.getAppId(),
callback.getMercNo(),
callback.getOutTradeNo(),
callback.getTradeNo(),
callback.getTradeStatus(),
callback.getTotalAmount(),
callback.getTimestamp()
);
}
}
六、安全机制详解
华为支付采用多层次安全保障机制,确保支付全流程的安全性:
6.1 数据传输安全
- 所有接口通信强制使用 HTTPS 加密通道
- 敏感信息(如银行卡号)传输时额外加密
6.2 签名机制(SM2 非对称加密)
- 商户侧使用私钥对请求参数签名
- 华为支付侧使用商户公钥验证签名
- 回调结果使用华为私钥签名,商户使用华为公钥验签
6.3 订单安全
prepayId
有效期为 30 分钟,超时后需重新预下单- 订单号(
outTradeNo
)唯一,防止重复支付 - 支付金额与预下单金额严格校验,防止金额篡改
6.4 终端安全
- 依赖华为终端的 TEE(可信执行环境)存储支付密码
- 支持指纹、人脸等生物识别验证,减少密码泄露风险
七、接入 Checklist 与最佳实践
7.1 接入前检查项
- 已在华为开发者联盟完成应用注册并获取
app_id
- 已完成华为支付商户签约,获取
merc_no
和密钥 - 服务端已部署 SM2 加解密算法(推荐使用华为提供的 SDK)
- 回调接口已部署并能正确返回 "success"(区分大小写)
- 已准备测试环境的商户账号和测试金额(支持 0.01 元测试)
7.2 最佳实践建议
- 支付结果确认:客户端同步结果仅用于 UI 展示,最终以服务端回调为准
- 订单状态管理:建议设置订单超时时间(如 15 分钟),超时后自动取消
- 异常处理:实现支付结果查询接口,用于解决支付状态不一致问题
- 用户体验:支付过程中显示明确的加载状态,支付完成后提供清晰的结果页和后续操作指引
- 日志记录:详细记录支付各环节日志,便于问题排查
八、常见问题与解决方案
问题场景 | 可能原因 | 解决方案 |
---|---|---|
预下单接口返回 "签名错误" | 1. 签名参数顺序错误 2. 私钥与商户号不匹配 3. 包含空值参数 |
1. 按 ASCII 顺序排序参数 2. 核对商户号与密钥是否匹配 3. 移除值为 null 的参数 |
收银台调起失败 | 1. orderStr 格式错误2. 应用未声明支付权限 3. 设备未安装华为支付客户端 |
1. 检查orderStr 是否为标准 JSON2. 确认已添加 ohos.permission.PAYMENT 权限3. 提示用户安装华为支付 |
回调接口未收到通知 | 1. 回调 URL 不可达 2. 接口返回非 "success" 3. 网络防火墙拦截 |
1. 确保 URL 公网可访问 2. 验证通过后返回 "success" 3. 开放华为支付服务器 IP 白名单 |
支付成功但订单状态未更新 | 1. 回调处理失败 2. 商户服务器宕机 |
1. 实现定时任务查询订单状态 2. 部署高可用服务架构 |
通过本文的指南,开发者可系统掌握华为支付收银台的集成要点,从商户模型选择到支付流程落地,再到安全机制保障,全方位覆盖支付功能开发需求。华为支付的标准化接口和完善的安全机制,将帮助开发者快速构建稳定、安全的支付体验,助力业务增长。
更多推荐
所有评论(0)