共计 3497 个字符,预计需要花费 9 分钟才能阅读完成。
背景痛点分析
ChatGPT 类应用的充值业务有几个显著特点,这些特点给支付系统设计带来了不小的挑战。

-
高频小额支付 :用户往往购买的是小额服务包,比如 10 美元 / 月的订阅,但交易频率极高。这要求支付系统能承受高并发压力。
-
跨国支付 :用户遍布全球,需要支持多种货币结算。汇率转换和当地支付方式适配成为必须解决的问题。
-
支付通道稳定性问题 :特别是接入第三方支付渠道时,可能会遇到通道临时不可用、限流等情况。
-
异步通知丢单 :支付成功后的异步回调可能因为网络问题丢失,导致系统状态不一致。
架构设计方案
DDD 分层架构
我们采用领域驱动设计(DDD)来构建支付系统,主要分为四层:
- 用户接口层 :处理 HTTP 请求,负责参数校验和 DTO 转换
- 应用层 :协调领域对象完成业务逻辑
- 领域层 :核心业务逻辑所在,包含订单、支付等聚合根
- 基础设施层 :提供持久化、消息队列等技术支持
支付系统时序图
sequenceDiagram
participant User
participant App
participant RiskControl
participant PaymentGateway
participant Channel
User->>App: 发起支付请求
App->>RiskControl: 风控校验
RiskControl-->>App: 校验结果
App->>PaymentGateway: 路由支付通道
PaymentGateway->>Channel: 调用支付接口
Channel-->>PaymentGateway: 返回支付 URL
PaymentGateway-->>App: 返回支付信息
App-->>User: 跳转支付页面
Channel->>PaymentGateway: 异步回调通知
PaymentGateway->>App: 更新订单状态
支付网关模式对比
- 直连模式
- 优点:延迟低,没有中间商抽成
-
缺点:需要对接每个支付渠道,维护成本高
-
聚合支付网关
- 优点:统一接口,降低接入复杂度
- 缺点:存在额外手续费,可能有性能瓶颈
代码实现细节
支付订单状态机
// 使用 Spring StateMachine 实现订单状态流转
@Configuration
@EnableStateMachineFactory(name = "paymentStateMachineFactory")
public class PaymentStateMachineConfig {
// 定义状态和事件
public enum States {CREATED, PROCESSING, PAID, FAILED}
public enum Events {PAY_REQUEST, PAY_SUCCESS, PAY_FAILURE}
@Bean
public StateMachine<States, Events> stateMachine() {StateMachineBuilder.Builder<States, Events> builder = StateMachineBuilder.builder();
builder.configureStates()
.withStates()
.initial(States.CREATED)
.states(EnumSet.allOf(States.class));
builder.configureTransitions()
.withExternal()
.source(States.CREATED).target(States.PROCESSING)
.event(Events.PAY_REQUEST)
.and()
.withExternal()
.source(States.PROCESSING).target(States.PAID)
.event(Events.PAY_SUCCESS)
.and()
.withExternal()
.source(States.PROCESSING).target(States.FAILED)
.event(Events.PAY_FAILURE);
return builder.build();}
}
JPA 实体设计
@Entity
@Table(name = "payment_order")
public class PaymentOrder {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(precision = 10, scale = 2)
private BigDecimal amount;
@Enumerated(EnumType.STRING)
private Currency currency;
@Version
private Integer version; // 乐观锁
@CreatedDate
private LocalDateTime createTime;
@LastModifiedDate
private LocalDateTime updateTime;
// 其他字段和方法...
}
RabbitMQ 延迟队列配置
@Configuration
public class RabbitMQConfig {
// 定义支付超时队列
@Bean
public Queue paymentTimeoutQueue() {return QueueBuilder.durable("payment.timeout.queue")
.withArgument("x-dead-letter-exchange", "payment.dlx.exchange")
.withArgument("x-dead-letter-routing-key", "payment.timeout")
.build();}
// 延迟交换机
@Bean
public DirectExchange paymentDelayExchange() {return new DirectExchange("payment.delay.exchange");
}
// 绑定关系
@Bean
public Binding paymentDelayBinding() {return BindingBuilder.bind(paymentTimeoutQueue())
.to(paymentDelayExchange())
.with("payment.delay");
}
}
生产级优化方案
通道健康度检测
我们实现了一个基于滑动窗口的通道健康度检测算法:
- 记录每个通道最近 10 次调用结果
- 计算成功率:成功次数 / 总调用次数
- 根据成功率动态调整权重
- 低于阈值时自动隔离故障通道
分布式对账方案
采用 TCC(Try-Confirm-Cancel)模式解决长短款问题:
- Try 阶段 :预留资源,记录预对账日志
- Confirm 阶段 :确认对账成功,更新余额
- Cancel 阶段 :对账失败,回滚预留资源
监控指标埋点
关键监控指标包括:
- 支付耗时 P99/P95
- 各通道成功率
- 通道切换次数
- 对账差异率
避坑指南
-
金额计算 :永远不要使用 float/double 表示金额,使用 BigDecimal 并设置正确的精度
-
回调验签 :支付渠道的回调验签要注意线程安全,避免使用共享变量
-
日志脱敏 :使用 AOP 统一处理敏感信息
@Aspect
@Component
public class SensitiveDataAspect {@Around("execution(* com..payment..*.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {Object[] args = pjp.getArgs();
// 对参数进行脱敏处理
for (int i = 0; i < args.length; i++) {if (args[i] instanceof String) {String arg = (String) args[i];
if (arg.contains("cardNo") || arg.contains("cvv")) {args[i] = maskSensitiveData(arg);
}
}
}
return pjp.proceed(args);
}
private String maskSensitiveData(String data) {
// 实现脱敏逻辑
return data.replaceAll("(?<=\\d{4})\\d(?=\\d{4})", "*");
}
}
开放性问题
跨境支付的实时风控规则设计需要考虑多个维度:
- 用户行为特征(如常用设备、IP 地域)
- 交易模式(金额、频率、时间分布)
- 支付渠道风险评级
- 实时黑名单检查
- 机器学习模型评分
如何平衡风控严格度和用户体验,是值得深入探讨的问题。
正文完
发表至: 未分类
近一天内
