【业务场景架构实战】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)
}
相关推荐
curd_boy8 小时前
【AI】生产级 Graph RAG 落地架构
人工智能·架构
alexhilton8 小时前
面向Android开发者的Google I/O 2026
android·kotlin·android jetpack
解局易否结局9 小时前
从架构视角看 ops-transformer:一个解决分层系统设计问题的算子仓库
深度学习·架构·transformer
hz567899 小时前
智慧政务视频会议系统技术架构解析:从场景需求到国产化落地的完整方案
架构·政务
生成论实验室9 小时前
通用人工智能(AGI)完整技术方案:以字序生命模型(WOLM)为认知内核的双脑协同架构
人工智能·语言模型·架构·创业创新·agi
刀法如飞10 小时前
DDD 与 Ontology 对比分析:哪一种更适合AI时代复杂系统构建?
java·架构·领域驱动设计
2601_9545267510 小时前
底层架构与并发性能:多态胶原饮“竞品对比”的技术评估报告
架构
5008411 小时前
Conv + BN + ReLU 融合:省掉两次显存读写
flutter·架构·开源·wpf·音视频
计算机魔术师13 小时前
【AI面试八股文 Vol.3.4:训练微调部署选型】从预训练到量化部署:LLM 工程落地如何做模型选择
人工智能·后端·面试·架构·moe·vol.3.3·vol.3.4