DEV Community

SameX
SameX

Posted on

鸿蒙 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 的权限机制为我们提供了坚实的安全保障,确保应用在合法、安全的前提下获取用户授权,保护用户数据和资金安全。

(二)用户授权的应用

  1. 读取剪贴板权限(用于粘贴银行卡号) 当用户在银行卡绑定界面选择粘贴银行卡号时,应用需要申请读取剪贴板权限(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();
Enter fullscreen mode Exit fullscreen mode

(三)安全控件的使用

  1. 粘贴控件(读取银行卡号) 为了方便用户输入银行卡号,我们使用粘贴控件。当用户在银行卡号输入框点击粘贴按钮时,应用调用粘贴控件读取剪贴板中的银行卡号。例如:
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%')
  }
}
Enter fullscreen mode Exit fullscreen mode

用户点击粘贴控件后,应用将读取到的银行卡号填充到输入框中,方便用户操作。

三、核心功能实现

(一)银行卡绑定功能

  1. 信息验证
    在用户输入银行卡号、开户行、持卡人姓名等信息后,应用在应用层进行初步的信息格式验证,如银行卡号的位数、开户行的有效性等。然后在领域层,通过与银行系统的接口(假设通过网络通信 API 与银行服务器进行交互)进行更严格的验证,确保银行卡信息的准确性和可用性。

  2. 数据加密与存储
    验证通过后,在数据层对银行卡信息进行加密处理,然后存储到本地安全存储区域(使用鸿蒙 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;
}
Enter fullscreen mode Exit fullscreen mode

(二)支付密码设置功能

  1. 密码强度验证 用户设置支付密码时,在应用层对密码强度进行验证,要求密码包含字母、数字、特殊字符等,且长度符合要求。例如:
function validatePasswordStrength(password: string): boolean {
  // 密码强度验证逻辑,返回 true 或 false
  return password.length >= 8 && /[a-zA-Z]/.test(password) && /\d/.test(password) && /[^\w\s]/.test(password);
}
Enter fullscreen mode Exit fullscreen mode
  1. 密码加密与存储 支付密码同样需要进行加密处理后存储到本地安全存储区域。加密方式与银行卡信息加密类似,确保密码的安全性。例如:
// 加密并存储支付密码
async function storePaymentPassword(password: string): Promise<void> {
  let encryptedPassword = encryptData(password);
  await secureStorage.put('payment_password', encryptedPassword);
}
Enter fullscreen mode Exit fullscreen mode

(三)支付订单功能

  1. 订单信息生成与加密 当用户发起支付订单时,应用在应用层生成订单信息,包括商品信息、金额、订单号等,然后在领域层对订单信息进行加密处理(可以使用与银行卡信息不同的加密密钥)。例如:
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;
}
Enter fullscreen mode Exit fullscreen mode
  1. 发送支付请求与接收结果 使用网络通信 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);
  }
}
Enter fullscreen mode Exit fullscreen mode

(四)安全功能

  1. 防止数据泄露
    除了对敏感数据进行加密存储外,应用在运行过程中要确保数据在内存中的安全。例如,在处理银行卡信息和支付密码时,尽量减少数据在内存中的停留时间,使用后及时清除。同时,防止应用被调试或反编译,通过鸿蒙 Next 的安全机制,如代码混淆、加固等手段,增加攻击者获取数据的难度。

  2. 防止恶意攻击
    在网络通信方面,使用安全的网络协议(如 HTTPS),对网络请求进行签名和验证,防止中间人攻击和数据篡改。在应用层和领域层,对用户输入进行严格的验证和过滤,防止 SQL 注入、XSS 攻击等常见的网络攻击手段。例如,在处理用户输入的订单信息时,对特殊字符进行转义处理,防止 SQL 注入攻击。

四、总结与展望

通过本次实战,我们成功构建了一个基于鸿蒙 Next 系统的在线支付应用,涵盖了银行卡绑定、支付密码设置和支付订单等核心功能。在开发过程中,我们深入运用了鸿蒙 Next 的 Clean Architecture 架构、权限机制、安全控件、剪贴板操作和网络操作等关键技术,确保了应用的安全性、稳定性和功能性。

展望未来,随着金融科技的不断发展和用户需求的日益多样化,我们可以进一步优化在线支付应用的功能。例如,引入生物识别技术(如指纹识别、人脸识别),提升支付的便捷性和安全性。同时,加强与更多金融机构和商家的合作,拓展支付场景,为用户提供更加全面、便捷的支付服务。希望本文能够为鸿蒙 Next 同行者在金融应用开发领域提供有益的参考和借鉴,共同推动鸿蒙 Next 在金融科技领域的应用发展。

Top comments (0)