鸿蒙 Next 实战:构建安全高效的在线支付应用
本文旨在深入探讨华为鸿蒙HarmonyOS Next系统(截止目前 API12)在开发多语言电商平台方面的技术细节,基于实际开发实践进行总结。主要作为技术分享与交流载体,难免错漏,欢迎各位同仁提出宝贵意见和问题,以便共同进步。本文为原创内容,任何形式的转载必须注明出处及原作者。
在数字化金融蓬勃发展的今天,在线支付应用的安全性和稳定性至关重要。本次,我们将基于鸿蒙 Next 系统,深入探讨如何开发一个功能完备的在线支付应用,涵盖从架构设计到核心功能实现的全过程,全面展现鸿蒙 Next 在金融科技领域的强大实力。
一、架构设计:Clean Architecture 的应用
(一)Clean Architecture 概述
Clean Architecture 是一种分层架构模式,将应用划分为多个独立的层次,每个层次都有明确的职责,使得代码结构清晰、易于维护和测试。在在线支付应用中,这种架构能够有效隔离业务逻辑、数据存储和用户界面,提高应用的安全性和稳定性。
(二)表现层(Presentation Layer)
表现层负责与用户进行交互,展示应用的界面和接收用户输入。在在线支付应用中,包括登录界面、银行卡绑定界面、支付密码设置界面、支付订单界面等。我们将使用 ArkUI 框架构建美观、易用的用户界面,确保用户能够流畅地进行操作。例如,通过TextField
组件接收用户输入的银行卡号、密码等信息,Button
组件实现各种操作按钮的功能。
(三)应用层(Application Layer)
应用层作为表现层和领域层之间的桥梁,负责协调业务逻辑的执行。它接收表现层的用户请求,调用领域层的业务逻辑处理方法,并将处理结果返回给表现层。例如,当用户点击银行卡绑定按钮时,应用层负责验证用户输入的信息格式,然后调用领域层的银行卡绑定方法进行实际的绑定操作。
(四)领域层(Domain Layer)
领域层包含了应用的核心业务逻辑,如银行卡绑定逻辑、支付密码验证逻辑、支付订单处理逻辑等。它不依赖于任何外部框架或技术,只专注于业务规则的实现。例如,在银行卡绑定逻辑中,验证银行卡号的有效性、与银行系统进行交互验证等操作都在领域层完成。
(五)数据层(Data Layer)
数据层负责数据的存储和获取,与数据库、网络服务等进行交互。在在线支付应用中,它负责存储用户的银行卡信息、支付密码等敏感数据(经过加密处理),以及与支付服务器进行通信,获取支付结果等信息。我们将使用鸿蒙 Next 的安全存储 API 和网络通信 API 来实现数据层的功能。
(六)各层之间的依赖关系
Clean Architecture 强调各层之间的单向依赖,表现层依赖于应用层,应用层依赖于领域层,领域层依赖于数据层。这种依赖关系确保了各层的独立性和可测试性,使得应用的维护和扩展更加容易。
二、权限申请与安全机制
(一)权限机制与安全原则
在线支付应用涉及用户的敏感信息和资金交易,安全是首要任务。鸿蒙 Next 的权限机制为我们提供了坚实的安全保障,确保应用在合法、安全的前提下获取用户授权,保护用户数据和资金安全。
(二)用户授权的应用
- 读取剪贴板权限(用于粘贴银行卡号)
当用户在银行卡绑定界面选择粘贴银行卡号时,应用需要申请读取剪贴板权限(ohos.permission.READ_PASTEBOARD
)。在用户点击粘贴按钮时,应用应动态申请该权限。例如:
import { abilityAccessCtrl, common, Permissions } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
const clipboardPermission: Permissions = 'ohos.permission.READ_PASTEBOARD';
async function checkClipboardPermissionGrant(): Promise<abilityAccessCtrl.GrantStatus> {
let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
let grantStatus: abilityAccessCtrl.GrantStatus = abilityAccessCtrl.GrantStatus.PERMISSION_DENIED;
// 获取应用程序的 accessTokenID
let tokenId: number = 0;
try {
let bundleInfo: bundleManager.BundleInfo = await bundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);
let appInfo: bundleManager.ApplicationInfo = bundleInfo.appInfo;
tokenId = appInfo.accessTokenId;
} catch (error) {
const err: BusinessError = error as BusinessError;
console.error(`Failed to get bundle info for self. Code is ${err.code}, message is ${err.message}`);
}
// 校验应用是否被授予权限
try {
grantStatus = await atManager.checkAccessToken(tokenId, clipboardPermission);
} catch (error) {
const err: BusinessError = error as BusinessError;
console.error(`Failed to check access token. Code is ${err.code}, message is ${err.message}`);
}
return grantStatus;
}
async function requestClipboardPermission(): Promise<void> {
let grantStatus: abilityAccessCtrl.GrantStatus = await checkClipboardPermissionGrant();
if (grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
// 已获得读取剪贴板权限,可以进行粘贴操作
console.log('已获得读取剪贴板权限,可以继续操作。');
} else {
// 申请读取剪贴板权限
const permissions: Array<Permissions> = [clipboardPermission];
let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
atManager.requestPermissionsFromUser(globalThis.context as common.UIAbilityContext, permissions).then((data) => {
let grantStatus: Array<number> = data.authResults;
let length: number = grantStatus.length;
for (let i = 0; i < length; i++) {
if (grantStatus[i] === 0) {
// 用户授权,可以继续访问目标操作
console.log('用户已授权读取剪贴板权限,可以继续操作。');
} else {
// 用户拒绝授权,提示用户必须授权才能访问相关功能,并引导用户到系统设置中打开相应权限
console.log('用户拒绝授权读取剪贴板权限,请前往系统设置中手动授予权限。');
return;
}
}
// 授权成功
}).catch((err: BusinessError) => {
console.error(`Failed to request clipboard permission from user. Code is ${err.code}, message is ${err.message}`);
});
}
}
// 在用户点击粘贴银行卡号按钮时调用 requestClipboardPermission() 函数
requestClipboardPermission();
(三)安全控件的使用
- 粘贴控件(读取银行卡号)
为了方便用户输入银行卡号,我们使用粘贴控件。当用户在银行卡号输入框点击粘贴按钮时,应用调用粘贴控件读取剪贴板中的银行卡号。例如:
import { pasteboard, BusinessError } from '@kit.BasicServicesKit';
import { Component, State, ClickEvent, PasteButtonOnClickResult } from '@ohos.arkui.extension.component';
@Entry
@Component
struct Index {
@State cardNumber: string = '';
build() {
Row() {
Column({ space: 10 }) {
TextField({ placeholder: '请输入银行卡号', text: this.cardNumber })
PasteButton()
.padding({ top: 12, bottom: 12, left: 24, right: 24 })
.onClick((event: ClickEvent, result: PasteButtonOnClickResult) => {
if (PasteButtonOnClickResult.SUCCESS === result) {
pasteboard.getSystemPasteboard().getData((err: BusinessError, pasteData: pasteboard.PasteData) => {
if (err) {
console.error(`Failed to get paste data. Code is ${err.code}, message is ${err.message}`);
return;
}
this.cardNumber = pasteData.getPrimaryText();
});
}
})
}
.width('100%')
}
.height('100%')
}
}
用户点击粘贴控件后,应用将读取到的银行卡号填充到输入框中,方便用户操作。
三、核心功能实现
(一)银行卡绑定功能
-
信息验证
在用户输入银行卡号、开户行、持卡人姓名等信息后,应用在应用层进行初步的信息格式验证,如银行卡号的位数、开户行的有效性等。然后在领域层,通过与银行系统的接口(假设通过网络通信 API 与银行服务器进行交互)进行更严格的验证,确保银行卡信息的准确性和可用性。 -
数据加密与存储
验证通过后,在数据层对银行卡信息进行加密处理,然后存储到本地安全存储区域(使用鸿蒙 Next 的安全存储 API)。加密算法可以选择行业标准的加密算法,如 AES 等,确保用户银行卡信息的安全性。例如:
import { secureStorage } from '@kit.SecurityKit';
// 加密并存储银行卡信息
async function storeBankCardInfo(cardNumber: string, bankName: string, cardholderName: string): Promise<void> {
let encryptedCardNumber = encryptData(cardNumber);
let encryptedBankName = encryptData(bankName);
let encryptedCardholderName = encryptData(cardholderName);
await secureStorage.put('bank_card_number', encryptedCardNumber);
await secureStorage.put('bank_name', encryptedBankName);
await secureStorage.put('cardholder_name', encryptedCardholderName);
}
// 加密数据的函数(假设使用 AES 加密算法)
function encryptData(data: string): string {
// 这里实现 AES 加密逻辑,返回加密后的字符串
return encryptedData;
}
(二)支付密码设置功能
- 密码强度验证
用户设置支付密码时,在应用层对密码强度进行验证,要求密码包含字母、数字、特殊字符等,且长度符合要求。例如:
function validatePasswordStrength(password: string): boolean {
// 密码强度验证逻辑,返回 true 或 false
return password.length >= 8 && /[a-zA-Z]/.test(password) && /\d/.test(password) && /[^\w\s]/.test(password);
}
- 密码加密与存储
支付密码同样需要进行加密处理后存储到本地安全存储区域。加密方式与银行卡信息加密类似,确保密码的安全性。例如:
// 加密并存储支付密码
async function storePaymentPassword(password: string): Promise<void> {
let encryptedPassword = encryptData(password);
await secureStorage.put('payment_password', encryptedPassword);
}
(三)支付订单功能
- 订单信息生成与加密
当用户发起支付订单时,应用在应用层生成订单信息,包括商品信息、金额、订单号等,然后在领域层对订单信息进行加密处理(可以使用与银行卡信息不同的加密密钥)。例如:
import { uuid } from '@kit.UtilKit';
// 生成支付订单
async function createPaymentOrder(productInfo: string, amount: number): Promise<string> {
let orderId = uuid.generate();
let orderInfo = {
orderId,
productInfo,
amount
};
let encryptedOrderInfo = encryptData(JSON.stringify(orderInfo));
return encryptedOrderInfo;
}
- 发送支付请求与接收结果
使用网络通信 API 将加密后的订单信息发送到支付服务器,支付服务器进行解密和验证后,返回支付结果。应用接收支付结果并在表现层展示给用户。例如:
import { net } from '@kit.NetworkKit';
// 发送支付请求
async function sendPaymentRequest(encryptedOrderInfo: string): Promise<void> {
let request = net.createHttpRequest();
request.method = 'POST';
request.url = 'https://your-payment-server-url/pay';
request.headers = { 'Content-Type': 'application/json' };
request.requestBody = encryptedOrderInfo;
try {
await request.send();
if (request.responseCode === 200) {
let paymentResult = JSON.parse(request.responseData);
if (paymentResult.success) {
console.log('支付成功。');
// 在表现层展示支付成功信息
} else {
console.error('支付失败,原因:', paymentResult.errorMessage);
// 在表现层展示支付失败信息
}
} else {
console.error('支付请求失败,错误码:', request.responseCode);
}
} catch (error) {
console.error('支付请求过程中出错:', error);
}
}
(四)安全功能
-
防止数据泄露
除了对敏感数据进行加密存储外,应用在运行过程中要确保数据在内存中的安全。例如,在处理银行卡信息和支付密码时,尽量减少数据在内存中的停留时间,使用后及时清除。同时,防止应用被调试或反编译,通过鸿蒙 Next 的安全机制,如代码混淆、加固等手段,增加攻击者获取数据的难度。 -
防止恶意攻击
在网络通信方面,使用安全的网络协议(如 HTTPS),对网络请求进行签名和验证,防止中间人攻击和数据篡改。在应用层和领域层,对用户输入进行严格的验证和过滤,防止 SQL 注入、XSS 攻击等常见的网络攻击手段。例如,在处理用户输入的订单信息时,对特殊字符进行转义处理,防止 SQL 注入攻击。
四、总结与展望
通过本次实战,我们成功构建了一个基于鸿蒙 Next 系统的在线支付应用,涵盖了银行卡绑定、支付密码设置和支付订单等核心功能。在开发过程中,我们深入运用了鸿蒙 Next 的 Clean Architecture 架构、权限机制、安全控件、剪贴板操作和网络操作等关键技术,确保了应用的安全性、稳定性和功能性。
展望未来,随着金融科技的不断发展和用户需求的日益多样化,我们可以进一步优化在线支付应用的功能。例如,引入生物识别技术(如指纹识别、人脸识别),提升支付的便捷性和安全性。同时,加强与更多金融机构和商家的合作,拓展支付场景,为用户提供更加全面、便捷的支付服务。希望本文能够为鸿蒙 Next 同行者在金融应用开发领域提供有益的参考和借鉴,共同推动鸿蒙 Next 在金融科技领域的应用发展。