【业务场景架构实战】2. 对聚合支付 SDK 的封装

天下事在局外呐喊议论,总是无益,必须躬身入局,挺膺负责,方有成事之可冀。

业务场景

已知有一个 聚合支付 SDK ------ 可以把它理解成,封装了支付宝、微信等支付渠道 SDK,提供统一的完成支付和结果通知接口。

当前业务上新增支付需求,需要把聚合支付 SDK 集成到当前的项目里,从而赋予项目内业务模块以支付的能力,例如集成到运动课程模块,就可以支持下单购买专业运动课程;集成到睡眠模块,就可以支持购买专业睡眠报告分析等等。

设计目标

这是在项目里首次引入支付能力,希望不仅可以实现当前的产品需求(即在运动课程里添加购买功能),还能够支撑未来更多模块使用支付功能。

设计目标梳理如下:

  • 安全性:由于涉及用户财产,设计支付相关模块时首当其冲的就是安全性。
  • 通用性:应用类的任意模块,都可以使用封装后的支付能力。
  • 可测试性:能够自由切换底层实现,例如,APP开发过程中提供 mock 的下单、支付接口,以便在启动前后端联调前,就可以验证前端 UI 逻辑。
  • 伸缩性:未来聚合支付 SDK 进行升级时,对已接入的业务不产生影响。
  • 隐藏实现细节:上层业务无需关心底层支付流程细节。
  • 与 Jetpack 无缝集成 :采用 StateFlow 模式,利用 Hilt 管理对象生命周期。

设计思路

将上述设计目标分门别类后,思考对应的技术选型。

目标 技术选型 说明
安全性 独立 module + 网络传输加密 将支付模块作为独立模块,未来可以抽出 aar 集成,也便于混淆控制。下单、查询订单状态等接口,进行二次加密从而提升安全等级
通用性,伸缩性,隐藏实现细节 Activity-ViewModel-Repository-DataSource 的分层设计 PaySdkClient(聚合支付 SDK)作为 DataSource 对象,只对 Repository 可见
可测试性 PayRepository 声明为接口,并提供 PayRepositoryImplFakePayRepository 两个实现 APP 开发阶段使用 FakePayRepository,联调阶段切换为 PayRepositoryImpl
与 Jetpack 无缝集成 Hilt + Flow 使用 Hilt 进行单例作用域管理,使用 Flow 实现支付状态优雅流转

架构图

  • Domain 层可选,当业务场景复杂度高、有复用的需求时,会将其封装出 Domain 层的 UseCase
  • ViewModel 既可以引用 UseCase,也可以引用 Repository
  • Repository 层的每个模块,提供了真实实现和 Fake 实现,后者可用于开发阶段自测、自动化测试。
  • 层与层之间单向依赖,通过 Hilt 进行实例管理和注入。

时序图

  • 两层订单号设计
    • 业务订单号:由业务(例如健身课程)后台生成,是业务系统内标识,描述用户买了哪门课、多少钱等。
    • 支付交易号:由支付公司生成,在支付渠道内唯一定位这笔支付。
    • 两层订单号要做绑定关系,方便对账查账。
  • 支付后台 ---支付结果通知--> 业务后台
    • 回调:支付后台在用户完成支付后,会向商户预先配置的回调地址(notify URL)发送一条 HTTP POST 请求,里面包含订单号、支付结果、签名等信息。
    • 校验:健身课程后台收到后需要做校验(验签、防重放)并更新订单状态。
    • 重试:如果商户后台返回失败或超时,支付后台会按策略重试几次(指数退避、定时重发等)。
  • 业务后台 ---订单最新状态--> 前端APP页面
    • 常见做法(轮询):APP 拿到支付 SDK 支付结果后,立即查询一次健身课程后台。为了等到最终一致性,会做2~3次短时间轮询,每次间隔数秒。
    • 推送通知:后台通过移动推送告知 APP 订单状态变更,不保证实时与可靠,一般用作辅助手段。
    • 长轮询:前端发起长连接,等后台主动推送订单状态更新。实时性更好,但后台需要具备推送能力,对网络环境要求高。
sequenceDiagram Note left of APP健身课程页: 1.用户发起支付 APP健身课程页->>APP健身课程页: 发起支付 activate APP健身课程页 Note left of APP健身课程页: 2.创建订单 APP健身课程页->>健身课程后台: 请求订单号 activate 健身课程后台 健身课程后台-->>APP健身课程页: 业务订单号 deactivate 健身课程后台 Note left of APP健身课程页: 3.SDK收银 APP健身课程页->>支付SDK: 拉起收银台 activate 支付SDK 支付SDK->>支付后台: 统一下单接口 activate 支付后台 支付后台-->>支付SDK: 支付交易号 deactivate 支付后台 支付SDK->>支付SDK: 完成支付 Note left of APP健身课程页: 4.返回支付结果 支付SDK -->>APP健身课程页: 支付结果 deactivate 支付SDK Note left of APP健身课程页: 5.支付后台落账 支付SDK->>支付后台: 发送订单号&支付结果 activate 支付后台 支付后台->>支付后台: 标记该订单已支付 支付后台 -->> 健身课程后台: 通知支付结果(通过提前注册的回调地址) deactivate 支付后台 Note left of APP健身课程页: 6.确认订单状态 APP健身课程页->>健身课程后台: 查询订单最新状态(同步or轮询) activate 健身课程后台 健身课程后台-->>APP健身课程页: 已支付订单 deactivate 健身课程后台 deactivate APP健身课程页 Note left of APP健身课程页: 7.刷新界面 APP健身课程页->>APP健身课程页: 刷新页面

开发细节

是分层使用不同的数据类,还是把同一个数据类跨层传输

分层使用不同的数据 Bean

  • 关注点分离:数据层-贴近外部协议,领域层-关注业务语义,展示层-关注界面状态。
  • 解耦:底层数据类型发生变化时,分层设计可以缓冲这种变化,降低对上层页面逻辑影响。反之亦然。

共用一个 Bean

  • 短期省事,但长期可能会让层次之间耦合、边界模糊。

跨层之间 Bean 转换

使用 Mapper 扩展函数,进行 底层->上层 的转换。

kotlin 复制代码
// DTO -> Domain
fun OrderDto.toDomain(): Order {
    val status = when (statusCode) {
        0 -> OrderStatus.CREATED
        1 -> OrderStatus.PAID
        else -> OrderStatus.FAILED
    }
    return Order(
        id = orderId,
        price = amount,
        status = status
    )
}

// Domain -> UI
fun Order.toUiModel(): OrderUiModel {
    val statusText = when (status) {
        OrderStatus.CREATED -> "待支付"
        OrderStatus.PAID -> "已支付"
        OrderStatus.FAILED -> "支付失败"
    }
    return OrderUiModel(
        displayId = "订单号: $id",
        priceLabel = "¥${price / 100.0}", // 转换为金额字符串
        statusText = statusText
    )
}

ViewModel 里面则通过 FLow.map 进行转换。

kotlin 复制代码
class OrderViewModel(
    private val repository: OrderRepository
) : ViewModel() {

    val orderUiState: StateFlow<OrderUiModel?> =
        repository.getOrder() // Flow<OrderDto>
            .map { dto -> dto.toDomain() }
            .map { domain -> domain.toUiModel() }
            .stateIn(viewModelScope, SharingStarted.Eagerly, null)
}
相关推荐
a努力。2 小时前
腾讯Java面试被问:String、StringBuffer、StringBuilder区别
java·开发语言·后端·面试·职场和发展·架构
麻辣兔变形记2 小时前
深入理解微服务下的 Saga 模式——以电商下单为例
微服务·云原生·架构
腾讯云中间件5 小时前
腾讯云 RocketMQ 5.x:如何兼容 Remoting 全系列客户端
架构·消息队列·rocketmq
代码AI弗森6 小时前
构建超级个体:AI Agent核心架构与落地实践全景解析
人工智能·架构
檐下翻书1736 小时前
互联网企业组织结构图在线设计 扁平化架构模板
论文阅读·人工智能·信息可视化·架构·流程图·论文笔记
CinzWS6 小时前
基于Cortex-M3的PMU架构--关键设计点
架构·pmu
白帽子黑客罗哥6 小时前
AI与零信任架构协同构建下一代智能防御体系
人工智能·架构
早睡的叶子7 小时前
VM / IREE 的调度器架构
linux·运维·架构
狗哥哥7 小时前
Vue 3 页面缓存机制深度实践:从原理到落地
前端·vue.js·架构
狗哥哥7 小时前
Vite 插件开发实战:从业务痛点到优雅解决方案
vue.js·架构·前端框架