鸿蒙数字人民币试点(线下商户扫码支付)
【摘要】 一、引言随着数字经济的快速发展,数字货币作为法定货币的数字化形态,正逐步成为全球金融创新的重点方向。数字人民币(DCEP,Digital Currency Electronic Payment)是由中国人民银行发行的法定数字货币,具有 可控匿名、双离线支付、高安全性 等核心优势,旨在提升支付效率、降低交易成本,并推动金融普惠。在线下消费场景中,商户与消费者通过 扫码支付 完成...
一、引言
二、技术背景
1. 数字人民币的核心特性
-
法定货币地位:由中国人民银行发行,与纸币硬币等价,具有无限法偿性,任何商户不得拒收。 -
可控匿名性:基于钱包分级分类设计,小额交易可保护用户隐私(如不绑定银行卡),大额交易需实名认证并反洗钱监控。 -
双离线支付:支持在无网络环境下(如地下停车场、偏远山区)通过NFC或二维码完成交易,交易数据后续同步至央行清算系统。 -
高安全性:采用国密算法(如SM2、SM4)加密交易数据,钱包密钥存储于安全芯片(如鸿蒙的TEE可信执行环境),防止信息泄露与篡改。
2. 鸿蒙的技术能力支撑
-
安全芯片与TEE:通过鸿蒙的TEE(Trusted Execution Environment)环境存储钱包私钥和敏感交易数据,确保密钥不出芯片,防止恶意软件窃取。 -
NFC与二维码:支持商户端通过NFC感应(如POS机)或二维码扫描(如用户出示付款码)完成支付交互,兼容主流支付终端设备。 -
分布式软总线:在多设备场景下(如手机+智能手表),支持支付指令的跨设备协同(如手表靠近POS机直接支付)。 -
系统级权限管理:通过鸿蒙的权限控制(如 ohos.permission.PAYMENT
),确保只有授权应用可调用数字人民币支付接口,保障交易合法性。
3. 数字人民币支付的核心流程(线下扫码)
|
|
|
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
三、应用使用场景
1. 小微商户日常收款
2. 连锁商超快速结账
3. 交通出行场景
4. 政府补贴发放
四、不同场景下详细代码实现
场景1:商户扫码收款(生成收款码)
4.1 商户端数据模型(models/MerchantPayment.ets)
// src/main/ets/models/MerchantPayment.ets
export interface MerchantPaymentRequest {
merchantId: string; // 商户唯一ID(如"MER_001")
amount: number; // 交易金额(单位:元,如10.5)
orderId: string; // 订单唯一ID(如"ORDER_20250120_001")
currency: string; // 货币类型(固定为"CNY")
timestamp: number; // 请求时间戳(毫秒)
}
export interface MerchantPaymentResponse {
success: boolean; // 支付是否成功
transactionId: string; // 交易唯一ID(如"TXN_20250120_001")
message: string; // 结果描述(如"支付成功"或"余额不足")
timestamp: number; // 响应时间戳(毫秒)
}
4.2 商户支付服务工具(utils/MerchantPaymentUtil.ets)
// src/main/ets/utils/MerchantPaymentUtil.ets
import { MerchantPaymentRequest, MerchantPaymentResponse } from '../models/MerchantPayment';
import qr from '@ohos.qrcode'; // 鸿蒙二维码生成模块(假设存在,实际需集成第三方库如zxing)
// 生成收款二维码(模拟逻辑,实际需调用数字人民币商户SDK)
export async function generateMerchantQRCode(request: MerchantPaymentRequest): Promise<string> {
try {
// 实际项目中:调用数字人民币商户SDK生成包含商户ID、金额、订单号的加密二维码数据
// const sdk = new DCEPMerchantSDK();
// const qrData = sdk.generateQR(request);
// return qrData;
// 模拟生成二维码内容(JSON字符串,实际应为加密数据)
const qrContent = JSON.stringify({
merchantId: request.merchantId,
amount: request.amount,
orderId: request.orderId,
currency: request.currency,
timestamp: request.timestamp
});
// 调用鸿蒙二维码生成API(模拟)
const qrBitmap = await qr.createQRCode(qrContent, 300, 300); // 生成300x300像素的二维码图片
return qrBitmap.toBase64String(); // 返回Base64编码的二维码图片(用于UI展示)
} catch (error) {
console.error('生成收款二维码失败:', error);
throw new Error('二维码生成异常');
}
}
// 模拟接收支付结果(实际应通过WebSocket或长轮询监听数字人民币后台回调)
export async function simulatePaymentResult(orderId: string): Promise<MerchantPaymentResponse> {
// 模拟异步处理(实际支付结果由数字人民币后台推送)
return new Promise((resolve) => {
setTimeout(() => {
// 模拟90%成功率
const success = Math.random() > 0.1;
resolve({
success,
transactionId: `TXN_${Date.now()}`,
message: success ? '支付成功' : '余额不足,请重试',
timestamp: Date.now()
});
}, 2000); // 模拟2秒后返回结果
});
}
4.3 商户收款页面(pages/MerchantPage.ets)
// src/main/ets/pages/MerchantPage.ets
import { MerchantPaymentRequest, MerchantPaymentResponse } from '../models/MerchantPayment';
import { generateMerchantQRCode, simulatePaymentResult } from '../utils/MerchantPaymentUtil';
@Entry
@Component
struct MerchantPage {
@State paymentRequest: MerchantPaymentRequest = {
merchantId: 'MER_001',
amount: 10.5,
orderId: `ORDER_${Date.now()}`,
currency: 'CNY',
timestamp: Date.now()
};
@State qrCodeBase64: string = ''; // 生成的二维码Base64数据
@State paymentResult: MerchantPaymentResponse | null = null; // 支付结果
@State isLoading: boolean = false;
// 生成新的收款二维码
private async generateQRCode() {
this.isLoading = true;
try {
this.qrCodeBase64 = await generateMerchantQRCode(this.paymentRequest);
console.log('二维码生成成功,订单号:', this.paymentRequest.orderId);
} catch (error) {
console.error('二维码生成失败:', error);
} finally {
this.isLoading = false;
}
}
// 模拟监听支付结果(实际应通过后台推送)
private async checkPaymentResult() {
if (!this.paymentRequest.orderId) return;
this.isLoading = true;
try {
this.paymentResult = await simulatePaymentResult(this.paymentRequest.orderId);
if (this.paymentResult?.success) {
console.log('支付成功,交易ID:', this.paymentResult.transactionId);
} else {
console.log('支付失败:', this.paymentResult?.message);
}
} catch (error) {
console.error('获取支付结果失败:', error);
} finally {
this.isLoading = false;
}
}
aboutToAppear() {
this.generateQRCode(); // 页面加载时生成初始二维码
// 模拟每隔3秒检查一次支付结果(实际应使用WebSocket实时监听)
setInterval(() => {
this.checkPaymentResult();
}, 3000);
}
build() {
Column() {
Text('商户数字人民币收款')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 30 });
// 二维码展示区域
if (this.qrCodeBase64) {
Image($r('app.media.qr_placeholder')) // 实际应使用 this.qrCodeBase64 解码为图片(需Base64转Bitmap)
.width(250)
.height(250)
.margin({ bottom: 20 })
.border({ width: 2, color: Color.Gray, radius: 10 });
} else {
Text('二维码生成中...')
.fontSize(16)
.fontColor(Color.Gray);
}
// 订单信息
Text(`订单号: ${this.paymentRequest.orderId}`)
.fontSize(14)
.fontColor(Color.Gray)
.margin({ bottom: 10 });
Text(`金额: ¥${this.paymentRequest.amount}`)
.fontSize(18)
.fontWeight(FontWeight.Medium)
.margin({ bottom: 20 });
// 刷新二维码按钮
Button('刷新二维码')
.onClick(() => {
this.paymentRequest.orderId = `ORDER_${Date.now()}`; // 生成新订单号
this.generateQRCode();
this.paymentResult = null; // 清空旧结果
})
.margin({ bottom: 20 });
// 支付结果展示
if (this.paymentResult) {
Text(this.paymentResult.message)
.fontSize(16)
.fontWeight(FontWeight.Medium)
.fontColor(this.paymentResult.success ? Color.Green : Color.Red);
if (this.paymentResult.success) {
Text(`交易ID: ${this.paymentResult.transactionId}`)
.fontSize(14)
.fontColor(Color.Gray);
}
} else if (!this.isLoading) {
Text('等待用户扫码支付...')
.fontSize(14)
.fontColor(Color.Gray);
}
}
.width('100%')
.height('100%')
.padding(20)
.justifyContent(FlexAlign.Center);
}
}
4.4 原理解释
-
数据模型: MerchantPaymentRequest
定义了商户发起收款请求的核心字段(商户ID、金额、订单号、货币类型),MerchantPaymentResponse
用于接收支付结果(成功/失败、交易ID、提示信息)。 -
二维码生成: generateMerchantQRCode
函数模拟调用数字人民币商户SDK生成包含交易信息的加密二维码(实际应为JSON数据加密后转为二维码图片),用户扫描后跳转至数字人民币钱包完成支付。 -
支付结果监听:通过定时任务(每3秒一次)模拟监听数字人民币后台的支付结果回调(实际应使用WebSocket或长轮询实时接收推送),更新UI展示支付状态(成功/失败)。 -
UI交互:商户端界面展示生成的二维码、订单金额和订单号,支持刷新二维码(生成新订单),并实时显示支付结果(如“支付成功”或“余额不足”)。
场景2:用户扫码付款(扫描商户收款码)
4.5 用户端数据模型(models/UserPayment.ets)
// src/main/ets/models/UserPayment.ets
export interface UserPaymentRequest {
qrData: string; // 扫描到的商户二维码数据(包含商户ID、金额、订单号)
userWalletId: string; // 用户数字人民币钱包ID(如"WALLET_001")
password: string; // 支付密码(或指纹验证标记)
timestamp: number; // 支付请求时间戳(毫秒)
}
export interface UserPaymentResponse {
success: boolean; // 支付是否成功
transactionId: string; // 交易唯一ID(如"TXN_20250120_001")
message: string; // 结果描述(如“支付成功”或“密码错误”)
remainingBalance: number; // 支付后剩余余额(单位:元)
timestamp: number; // 响应时间戳(毫秒)
}
4.6 用户支付服务工具(utils/UserPaymentUtil.ets)
// src/main/ets/utils/UserPaymentUtil.ets
import { UserPaymentRequest, UserPaymentResponse } from '../models/UserPayment';
import camera from '@ohos.multimedia.camera'; // 鸿蒙摄像头模块(用于扫描二维码)
// 模拟扫描二维码(实际应调用鸿蒙摄像头API或集成二维码扫描库)
export async function scanQRCode(): Promise<string> {
try {
// 实际项目中:使用鸿蒙摄像头API或第三方库(如zxing)扫描二维码
// const cameraController = await camera.createCameraController({ deviceId: 'default' });
// const qrResult = await cameraController.scanQRCode();
// return qrResult;
// 模拟扫描结果(返回商户生成的二维码数据)
return JSON.stringify({
merchantId: 'MER_001',
amount: 10.5,
orderId: 'ORDER_20250120_001',
currency: 'CNY',
timestamp: Date.now()
});
} catch (error) {
console.error('二维码扫描失败:', error);
throw new Error('扫描异常,请重试');
}
}
// 模拟用户支付验证(实际应调用数字人民币钱包SDK进行密码/指纹验证)
export async function verifyUserAuth(password: string): Promise<boolean> {
// 模拟验证逻辑(实际应与钱包的安全模块交互)
return password === '123456'; // 假设密码为123456
}
// 模拟提交支付请求(实际应调用数字人民币钱包SDK与央行后台交互)
export async function submitUserPayment(request: UserPaymentRequest): Promise<UserPaymentResponse> {
try {
// 实际项目中:调用数字人民币钱包SDK,传入qrData、userWalletId、password
// const sdk = new DCEPUserWalletSDK();
// const response = await sdk.pay(request);
// 模拟支付处理(延迟1秒,90%成功率)
return new Promise((resolve) => {
setTimeout(() => {
const success = Math.random() > 0.1;
resolve({
success,
transactionId: `TXN_${Date.now()}`,
message: success ? '支付成功' : '余额不足或密码错误',
remainingBalance: success ? 100.0 - request.qrData.amount : 100.0, // 模拟初始余额100元
timestamp: Date.now()
});
}, 1000);
});
} catch (error) {
console.error('支付提交失败:', error);
throw new Error('支付异常,请检查网络');
}
}
4.7 用户付款页面(pages/UserPage.ets)
// src/main/ets/pages/UserPage.ets
import { UserPaymentRequest, UserPaymentResponse } from '../models/UserPayment';
import { scanQRCode, verifyUserAuth, submitUserPayment } from '../utils/UserPaymentUtil';
@Entry
@Component
struct UserPage {
@State qrData: string = ''; // 扫描到的二维码数据
@State paymentResponse: UserPaymentResponse | null = null; // 支付结果
@State isLoading: boolean = false;
@State inputPassword: string = ''; // 用户输入的支付密码
// 扫描商户二维码
private async scanMerchantQR() {
this.isLoading = true;
try {
this.qrData = await scanQRCode();
console.log('二维码扫描成功:', this.qrData);
} catch (error) {
console.error('二维码扫描失败:', error);
} finally {
this.isLoading = false;
}
}
// 提交支付请求
private async submitPayment() {
if (!this.qrData || !this.inputPassword) {
return;
}
this.isLoading = true;
try {
// 解析二维码数据(实际应为加密数据,此处简化为JSON字符串)
const qrInfo = JSON.parse(this.qrData);
const request: UserPaymentRequest = {
qrData: this.qrData,
userWalletId: 'WALLET_001',
password: this.inputPassword,
timestamp: Date.now()
};
// 验证用户身份(密码/指纹)
const isAuthValid = await verifyUserAuth(this.inputPassword);
if (!isAuthValid) {
this.paymentResponse = {
success: false,
transactionId: '',
message: '密码错误',
remainingBalance: 100.0,
timestamp: Date.now()
};
return;
}
// 提交支付
this.paymentResponse = await submitUserPayment(request);
if (this.paymentResponse?.success) {
console.log('支付成功,交易ID:', this.paymentResponse.transactionId);
} else {
console.log('支付失败:', this.paymentResponse?.message);
}
} catch (error) {
console.error('支付处理失败:', error);
} finally {
this.isLoading = false;
}
}
aboutToAppear() {
// 页面加载时可自动扫描(或用户手动点击扫描按钮)
// this.scanMerchantQR();
}
build() {
Column() {
Text('数字人民币扫码付款')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 30 });
// 扫描二维码按钮
Button('扫描商户收款码')
.width('80%')
.height(50)
.onClick(() => {
this.scanMerchantQR();
})
.margin({ bottom: 20 });
// 二维码数据展示(调试用)
if (this.qrData) {
Text('扫描到的二维码数据(调试用):')
.fontSize(14)
.fontColor(Color.Gray)
.margin({ bottom: 10 });
Text(this.qrData.substring(0, 50) + '...') // 截取部分数据避免过长
.fontSize(12)
.fontColor(Color.Black);
}
// 支付密码输入
if (this.qrData) {
TextInput({ placeholder: '请输入支付密码', text: this.inputPassword })
.type(InputType.Password)
.onChange((value: string) => {
this.inputPassword = value;
})
.width('80%')
.height(50)
.margin({ bottom: 20 });
// 支付提交按钮
Button('确认支付')
.width('80%')
.height(50)
.onClick(() => {
this.submitPayment();
})
.margin({ bottom: 20 });
}
// 支付结果展示
if (this.paymentResponse) {
Text(this.paymentResponse.message)
.fontSize(16)
.fontWeight(FontWeight.Medium)
.fontColor(this.paymentResponse.success ? Color.Green : Color.Red);
if (this.paymentResponse.success) {
Text(`交易ID: ${this.paymentResponse.transactionId}`)
.fontSize(14)
.fontColor(Color.Gray);
Text(`剩余余额: ¥${this.paymentResponse.remainingBalance.toFixed(2)}`)
.fontSize(14)
.fontColor(Color.Gray);
}
} else if (!this.isLoading && this.qrData) {
Text('请输入支付密码并确认支付')
.fontSize(14)
.fontColor(Color.Gray);
} else if (this.isLoading) {
Text('处理中...')
.fontSize(16)
.fontColor(Color.Blue);
}
}
.width('100%')
.height('100%')
.padding(20)
.justifyContent(FlexAlign.Center);
}
}
4.8 原理解释
-
数据模型: UserPaymentRequest
定义了用户支付请求的核心字段(扫描的二维码数据、钱包ID、密码、时间戳),UserPaymentResponse
用于接收支付结果(成功/失败、交易ID、剩余余额)。 -
二维码扫描: scanQRCode
函数模拟调用鸿蒙摄像头API或集成第三方二维码扫描库(如zxing),获取商户展示的收款二维码数据(包含商户ID、金额、订单号)。 -
支付验证: verifyUserAuth
函数模拟用户身份验证(如密码或指纹),实际应与数字人民币钱包的安全模块交互(如调用TEE验证指纹或加密密码)。 -
支付提交: submitUserPayment
函数模拟将支付请求提交至数字人民币钱包SDK与央行后台,完成资金扣除与交易记录,返回支付结果(成功/失败)及剩余余额。 -
UI交互:用户端界面提供“扫描商户收款码”按钮,扫描后展示二维码数据(调试用),用户输入支付密码并确认支付,实时反馈支付结果(如“支付成功”及交易ID)。
五、原理解释
1. 数字人民币线下扫码支付的核心流程
-
商户端生成收款码:商户通过鸿蒙POS机或商户APP输入交易金额,调用数字人民币商户SDK生成包含商户ID、金额、订单号的加密二维码(或NFC感应数据),用户扫描后跳转至支付流程。 -
用户端扫描与验证:用户使用手机数字人民币钱包扫描商户二维码,钱包解析二维码数据,提示用户确认支付金额,用户输入支付密码或验证指纹(身份验证)。 -
交易请求提交:验证通过后,用户钱包将支付请求(包含二维码数据、钱包ID、密码)提交至数字人民币后台,后台验证商户合法性、用户余额及反洗钱规则。 -
资金清算与结果反馈:后台完成资金扣除(从用户数字人民币账户)与商户账户划转(实时或延时),并向商户和用户同步支付结果(成功/失败、交易ID)。
2. 关键技术点
-
安全加密:二维码数据包含交易敏感信息(如金额、订单号),需通过国密算法(如SM4)加密传输,防止中间人篡改;钱包私钥存储于鸿蒙TEE,确保密钥安全。 -
双离线支持:在无网络环境下,用户与商户设备通过NFC或二维码交换离线交易数据(如交易ID、金额哈希),后续网络恢复时同步至央行清算系统完成最终结算。 -
实时交互:通过WebSocket或长轮询机制,商户端实时监听支付结果回调,用户端即时展示支付状态,提升用户体验的流畅性。
六、核心特性
|
|
---|---|
|
|
|
|
|
|
|
|
|
|
七、原理流程图及原理解释
原理流程图(数字人民币线下扫码支付执行流程)
+-----------------------+ +-----------------------+ +-----------------------+
| 用户扫描商户二维码 | | 钱包解析交易信息 | | 用户身份验证 |
| (摄像头/NFC感应) | ----> | (商户ID/金额/订单号) | ----> | (密码/指纹) |
+-----------------------+ +-----------------------+ +-----------------------+
| | |
|
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)