返回金融系统设计
支付网关 · 主设计文档

支付网关系统设计文档

01-payment-gateway/design-note.md

支付网关系统设计文档

项目类型:金融系统设计实战
设计级别:高级架构师 / 系统设计面试
日期:2026-04-13
作者:MomoWeb3 架构实战


一、需求分析

1.1 面试场景还原

面试官:请设计一个在线支付网关系统,日均百万笔交易,支持多支付方式(信用卡/借记卡/支付宝/微信),需要对账和退款功能。你会怎么设计?

这是一道典型的系统设计面试题。好的候选人不会直接画图,而是先做需求澄清

1.2 需求澄清问题表

#澄清问题假设回答对设计的影响
1日均交易量级?峰值是日均的几倍?日均100万笔,峰值10倍(大促场景)需要弹性扩容能力,数据库分片策略
2单笔交易金额范围?0.01元~100万元大额交易需要额外风控审核流程
3支持哪些支付方式?未来是否扩展?信用卡/借记卡/支付宝/微信,未来扩展银联云闪付、Apple Pay通道适配层需要可插拔设计
4商户数量和规模?1000+商户,Top 10商户占80%交易量需要热点商户隔离,商户级限流
5延迟要求?支付请求到返回结果 < 3秒异步化非关键路径,缓存路由配置
6可用性要求?99.99%(年停机 < 53分钟)多机房部署,通道故障自动降级
7资金安全要求?不能多扣、不能少扣、不能重复扣幂等机制 + 对账兜底 + 状态机严格校验
8是否需要分账/结算?需要T+1结算给商户结算服务独立设计,对账驱动结算
9合规要求?PCI-DSS(卡数据),央行备付金管理卡号脱敏、加密存储、审计日志
10跨境支付是否在scope内?第一期不含,但架构需要预留多币种字段预留,汇率服务接口预留

1.3 功能需求清单

模块功能优先级说明
收单统一下单P0商户提交支付请求,返回支付凭证
收单支付查询P0商户主动查询支付结果
收单异步通知P0支付结果异步回调商户
退款全额退款P0原路退回全部金额
退款部分退款P1退回部分金额,支持多次退款
对账日终对账P0T+1与各通道核对交易明细
对账差错处理P0对账差异的自动/人工处理
路由通道路由P0根据规则选择最优支付通道
路由通道管理P1通道上线/下线/维护窗口管理
结算商户结算P1T+1将资金结算给商户
监控交易监控P0成功率、延迟、异常告警
风控基础风控P0限额、黑名单、频次控制

1.4 非功能需求

维度指标说明
性能延迟 < 3秒(P99)从商户请求到返回支付结果
性能吞吐量 ≥ 1000 TPS日均100万笔 ÷ 86400 ≈ 12 TPS,峰值需支撑10倍
可用性99.99%年停机时间 < 53分钟
可靠性资金零差错不多扣、不少扣、不重复扣
扩展性100万→10亿笔/天水平扩展,分库分表
安全性PCI-DSS Level 1卡号加密存储、传输加密、访问控制
幂等性重复请求安全同一请求多次提交只产生一笔交易
可审计全链路日志满足央行监管和审计要求

1.5 约束条件和范围

  • 范围内:收单、退款、对账、路由、通知、基础风控
  • 范围外:跨境支付(预留接口)、银行直连清算、会员体系
  • 技术约束:Java/Spring生态、MySQL + Redis、RocketMQ
  • 合规约束:PCI-DSS、支付业务许可证、备付金管理

二、高层架构设计

2.1 C4 Context — 系统上下文

支付网关系统处于商户与金融基础设施之间的核心位置:

  • 商户系统(Merchant System):通过统一API提交支付/退款请求,接收异步通知
  • 终端用户(End User):通过商户的收银台间接与支付网关交互(跳转支付页面、输入密码等)
  • PSP通道(Payment Service Providers):支付宝、微信支付、银联等第三方支付机构,处理实际的资金划转
  • 银行系统(Bank):通过PSP间接交互,或通过银行直连通道处理银行卡交易
  • 风控系统(Risk Control):独立的风控引擎,提供实时风险评估(可以是内部子系统或外部服务)
  • 记账系统(Ledger):维护资金账本,记录每笔交易的借贷方向,确保资金平衡
  • 通知系统(Notification):负责将支付结果通过HTTP回调推送给商户

2.2 C4 Container — 容器架构

支付网关内部划分为以下容器(Container),每个容器是一个独立部署单元:

Container职责技术选型数据存储
API Gateway统一入口、鉴权、限流、签名验签Spring Cloud Gateway / NginxRedis(限流计数)
Payment Core Service支付核心:下单、状态管理、幂等控制Spring BootMySQL(payment_order, payment_transaction)
Route Engine通道路由选择:根据规则和实时数据选择最优通道Spring BootRedis(通道健康度)+ MySQL(channel_config)
Risk Gateway风控网关:调用风控引擎做实时风险评估Spring BootRedis(频次计数)
Channel Adapter Layer通道适配:封装各PSP的差异,提供统一调用接口Spring BootMySQL(channel_log)
Ledger Client记账客户端:调用记账系统记录资金变动Spring Boot调用记账系统API
Reconcile Service对账服务:T+1核对交易,发现差异Spring Boot + XXL-JOBMySQL(reconcile_record)+ 文件存储
Settlement Service结算服务:根据对账结果计算商户应结金额Spring Boot + XXL-JOBMySQL(settlement_record)
Notification Service通知服务:异步推送支付结果给商户Spring Boot + RocketMQMySQL(notify_record)+ Redis
Message Queue异步解耦:支付结果→通知/记账/对账RocketMQ-

2.3 核心流程 — Happy Path

一笔正常支付的完整流程如下:

  1. 商户下单:商户系统调用 POST /api/v1/payment/create,携带商户订单号、金额、支付方式等参数
  2. 签名验证:API Gateway 验证商户签名、检查限流,转发到 Payment Core
  3. 幂等检查:Payment Core 根据「商户ID + 商户订单号」做幂等校验,防止重复下单
  4. 创建订单:生成平台支付订单号,状态设为 CREATED,写入 payment_order
  5. 风控检查:调用 Risk Gateway 做实时风控(限额、黑名单、频次),风控通过则继续
  6. 路由选择:调用 Route Engine,根据支付方式、通道成功率、成本、限额选择最优通道
  7. 通道调用:Channel Adapter 将请求转换为目标PSP的格式,发送到支付宝/微信/银联
  8. 状态更新:收到PSP同步返回后,更新订单状态为 PROCESSING(或直接 SUCCESS
  9. 异步回调:PSP异步通知支付结果,Channel Adapter 解析后更新订单状态为 SUCCESS
  10. 记账:发送MQ消息,Ledger Client 异步记录借贷分录
  11. 通知商户:发送MQ消息,Notification Service 异步通知商户(指数退避重试)
  12. 返回结果:向商户返回支付凭证(如支付链接或二维码URL)

三、数据模型设计

3.1 核心表结构

payment_order(支付订单表)

CREATE TABLE payment_order (
    id                  BIGINT PRIMARY KEY AUTO_INCREMENT,
    payment_order_no    VARCHAR(32) NOT NULL COMMENT '平台支付订单号',
    merchant_id         VARCHAR(32) NOT NULL COMMENT '商户ID',
    merchant_order_no   VARCHAR(64) NOT NULL COMMENT '商户订单号',
    amount              BIGINT NOT NULL COMMENT '支付金额(分)',
    currency            VARCHAR(3) DEFAULT 'CNY' COMMENT '币种ISO-4217',
    payment_method      VARCHAR(20) NOT NULL COMMENT '支付方式:ALIPAY/WECHAT/BANK_CARD',
    channel_code        VARCHAR(32) COMMENT '实际使用的通道编码',
    status              VARCHAR(20) NOT NULL DEFAULT 'CREATED' COMMENT '订单状态',
    notify_url          VARCHAR(256) COMMENT '商户回调地址',
    expire_time         DATETIME COMMENT '订单过期时间',
    paid_time           DATETIME COMMENT '支付成功时间',
    channel_order_no    VARCHAR(64) COMMENT '通道方订单号',
    extra_info          JSON COMMENT '扩展信息',
    created_at          DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at          DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    
    UNIQUE KEY uk_merchant_order (merchant_id, merchant_order_no),
    UNIQUE KEY uk_payment_order_no (payment_order_no),
    INDEX idx_status_created (status, created_at),
    INDEX idx_merchant_id (merchant_id, created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='支付订单表';

payment_transaction(支付交易流水表)

CREATE TABLE payment_transaction (
    id                  BIGINT PRIMARY KEY AUTO_INCREMENT,
    transaction_no      VARCHAR(32) NOT NULL COMMENT '交易流水号',
    payment_order_no    VARCHAR(32) NOT NULL COMMENT '关联支付订单号',
    channel_code        VARCHAR(32) NOT NULL COMMENT '通道编码',
    transaction_type    VARCHAR(20) NOT NULL COMMENT '交易类型:PAY/REFUND',
    amount              BIGINT NOT NULL COMMENT '交易金额(分)',
    status              VARCHAR(20) NOT NULL COMMENT '交易状态',
    channel_request     TEXT COMMENT '通道请求报文(脱敏)',
    channel_response    TEXT COMMENT '通道响应报文(脱敏)',
    channel_order_no    VARCHAR(64) COMMENT '通道方流水号',
    error_code          VARCHAR(32) COMMENT '错误码',
    error_msg           VARCHAR(256) COMMENT '错误信息',
    created_at          DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at          DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

    UNIQUE KEY uk_transaction_no (transaction_no),
    INDEX idx_payment_order (payment_order_no),
    INDEX idx_channel_order (channel_code, channel_order_no)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='支付交易流水表';

channel_config(通道配置表)

CREATE TABLE channel_config (
    id                  BIGINT PRIMARY KEY AUTO_INCREMENT,
    channel_code        VARCHAR(32) NOT NULL COMMENT '通道编码:ALIPAY_APP/WECHAT_JSAPI等',
    channel_name        VARCHAR(64) NOT NULL COMMENT '通道名称',
    payment_method      VARCHAR(20) NOT NULL COMMENT '支付方式',
    fee_rate            DECIMAL(6,4) NOT NULL COMMENT '费率(如0.0060表示0.6%)',
    min_amount          BIGINT DEFAULT 1 COMMENT '单笔最小金额(分)',
    max_amount          BIGINT DEFAULT 99999999 COMMENT '单笔最大金额(分)',
    daily_limit         BIGINT COMMENT '日限额(分)',
    priority            INT DEFAULT 100 COMMENT '静态优先级(越小越优先)',
    weight              INT DEFAULT 100 COMMENT '动态权重',
    status              VARCHAR(10) DEFAULT 'ACTIVE' COMMENT '状态:ACTIVE/INACTIVE/MAINTENANCE',
    config_json         JSON COMMENT '通道私有配置(密钥/URL等,加密存储)',
    created_at          DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at          DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

    UNIQUE KEY uk_channel_code (channel_code),
    INDEX idx_payment_method (payment_method, status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='通道配置表';

reconcile_record(对账记录表)

CREATE TABLE reconcile_record (
    id                  BIGINT PRIMARY KEY AUTO_INCREMENT,
    reconcile_date      DATE NOT NULL COMMENT '对账日期',
    channel_code        VARCHAR(32) NOT NULL COMMENT '通道编码',
    payment_order_no    VARCHAR(32) COMMENT '平台订单号',
    channel_order_no    VARCHAR(64) COMMENT '通道订单号',
    our_amount          BIGINT COMMENT '我方金额(分)',
    channel_amount      BIGINT COMMENT '通道方金额(分)',
    our_status          VARCHAR(20) COMMENT '我方状态',
    channel_status      VARCHAR(20) COMMENT '通道方状态',
    reconcile_result    VARCHAR(20) NOT NULL COMMENT '对账结果:MATCHED/MISMATCH/OUR_MISSING/CHANNEL_MISSING',
    handle_status       VARCHAR(20) DEFAULT 'PENDING' COMMENT '处理状态:PENDING/AUTO_FIXED/MANUAL_REVIEW/RESOLVED',
    handle_remark       VARCHAR(512) COMMENT '处理备注',
    created_at          DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at          DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

    UNIQUE KEY uk_date_channel_order (reconcile_date, channel_code, payment_order_no),
    INDEX idx_result (reconcile_result, handle_status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='对账记录表';

refund_order(退款订单表)

CREATE TABLE refund_order (
    id                  BIGINT PRIMARY KEY AUTO_INCREMENT,
    refund_order_no     VARCHAR(32) NOT NULL COMMENT '退款订单号',
    payment_order_no    VARCHAR(32) NOT NULL COMMENT '原支付订单号',
    merchant_id         VARCHAR(32) NOT NULL COMMENT '商户ID',
    merchant_refund_no  VARCHAR(64) NOT NULL COMMENT '商户退款单号',
    refund_amount       BIGINT NOT NULL COMMENT '退款金额(分)',
    original_amount     BIGINT NOT NULL COMMENT '原支付金额(分)',
    refund_reason       VARCHAR(256) COMMENT '退款原因',
    channel_code        VARCHAR(32) NOT NULL COMMENT '通道编码',
    channel_refund_no   VARCHAR(64) COMMENT '通道退款流水号',
    status              VARCHAR(20) NOT NULL DEFAULT 'CREATED' COMMENT '退款状态',
    refunded_time       DATETIME COMMENT '退款成功时间',
    created_at          DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at          DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

    UNIQUE KEY uk_merchant_refund (merchant_id, merchant_refund_no),
    UNIQUE KEY uk_refund_order_no (refund_order_no),
    INDEX idx_payment_order (payment_order_no)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='退款订单表';

3.2 表关系

payment_order (1) ──→ (N) payment_transaction  (一笔订单可能多次尝试不同通道)
payment_order (1) ──→ (N) refund_order          (一笔订单可能多次部分退款)
payment_order (1) ──→ (N) reconcile_record       (对账关联)
channel_config (1) ──→ (N) payment_transaction   (通道配置关联交易)

3.3 分库分表策略

分片键分片数量策略
payment_ordermerchant_id64库 × 16表保证同一商户的数据在同一分片
payment_transactionpayment_order_no与payment_order相同分片保证订单与流水在同一库
refund_orderpayment_order_no与payment_order相同分片保证退款与原单同库查询
reconcile_recordreconcile_date + channel_code按天分区对账是按天批量处理
channel_config不分片全量缓存数据量小,使用Redis缓存

分片键选择理由:以 merchant_id 为分片键,保证同一商户的所有数据落在同一分片,避免跨库查询。订单号中嵌入商户分片路由信息(如订单号前4位为merchant_id哈希值),支持通过订单号直接路由。

3.4 状态机定义

支付订单状态机

CREATED ──→ PROCESSING ──→ SUCCESS
   │            │
   │            ├──→ FAILED
   │            │
   │            └──→ TIMEOUT ──→ (对账补单)→ SUCCESS / CLOSED
   │
   └──→ CLOSED(超时未支付关闭)

SUCCESS ──→ REFUNDING ──→ REFUNDED
                │
                └──→ REFUND_FAILED

状态转换规则

当前状态事件目标状态前置条件
CREATED提交通道PROCESSING风控通过、路由成功
CREATED超时关闭CLOSED超过expire_time
PROCESSING通道成功SUCCESS通道返回成功
PROCESSING通道失败FAILED通道返回明确失败
PROCESSING通道超时TIMEOUT超过通道超时时间
TIMEOUT对账补单SUCCESS / CLOSEDT+1对账确认结果
SUCCESS发起退款REFUNDING退款金额 ≤ 可退金额
REFUNDING退款成功REFUNDED(全额)/ SUCCESS(部分)通道返回退款成功
REFUNDING退款失败REFUND_FAILED通道返回退款失败

四、核心组件详细设计

4.1 通道路由引擎

路由因子

因子权重数据来源更新频率
通道成功率40%最近1小时交易成功率实时计算(滑动窗口)
通道成本(费率)25%channel_config表配置变更时更新
通道限额20%已用额度/剩余额度实时计算
通道可用性15%熔断器状态实时(Sentinel/自建)

路由算法 — 加权评分

Score(channel) = W_success × normalize(success_rate)
              + W_cost × normalize(1 - fee_rate)
              + W_limit × normalize(remaining_limit)
              + W_availability × availability_flag

选择 Score 最高的通道。
  • normalize(x):归一化到 [0, 1] 区间
  • availability_flag:0(熔断中)或 1(正常)

降级策略 — 熔断器模式

采用三态熔断器(Closed → Open → Half-Open):

  • Closed(正常):错误率 < 50% 或 最近5分钟成功 ≥ 10笔
  • Open(熔断):最近1分钟错误率 > 50% 且 请求 ≥ 20笔,自动切换到备选通道
  • Half-Open(探测):熔断60秒后放行10%流量探测,成功则恢复,失败继续熔断

降级顺序:主通道 → 备选通道 → 兜底通道 → 拒绝并告警

4.2 幂等机制

幂等键设计

  • 幂等键merchant_id + merchant_order_no(商户维度唯一)
  • 存储:利用 payment_order 表的唯一索引 uk_merchant_order 做天然去重
  • 辅助:Redis Set 做前置快速判断(缓存已处理的幂等键,TTL = 24小时)

幂等处理流程

1. 接收请求 → 提取幂等键(merchant_id + merchant_order_no)
2. Redis SISMEMBER 快速判断:
   - 存在 → 查询已有订单状态,返回已有结果(幂等返回)
   - 不存在 → 继续
3. MySQL INSERT(依赖唯一索引):
   - 成功 → 新订单,继续流程
   - 唯一键冲突 → 并发重复请求,查询已有结果返回
4. 流程结束后 → Redis SADD 加入幂等键缓存

状态机防重入

除了幂等键,还通过状态机防止重复处理:

  • 状态更新使用乐观锁UPDATE payment_order SET status = 'SUCCESS' WHERE payment_order_no = ? AND status = 'PROCESSING'
  • 只有 affected_rows = 1 才继续后续处理(记账、通知)
  • 保证即使收到重复回调,也只处理一次

4.3 对账引擎

T+1 对账流程

Step 1: 文件获取(每天凌晨2:00)
  → 从各PSP下载前一天的交易对账文件(CSV/TXT/Excel)
  → 解析为统一格式:[通道订单号, 金额, 状态, 时间]

Step 2: 逐笔比对
  → 遍历通道文件的每笔交易
  → 用通道订单号关联我方 payment_transaction 记录
  → 比对金额、状态是否一致

Step 3: 差异标记
  → MATCHED:双方一致
  → MISMATCH:金额或状态不一致
  → OUR_MISSING:通道有而我方无(漏单)
  → CHANNEL_MISSING:我方有而通道无(掉单)

Step 4: 差错处理(见 ADR-003)
  → 小额自动处理 + 大额人工审核 + 异常升级

Step 5: 报表生成
  → 输出对账报表:总笔数、匹配率、差异明细
  → 发送告警(匹配率 < 99.9% 时)

自动平账规则

差异类型金额阈值自动处理方式
OUR_MISSING(我方漏单)≤ 100元自动补单,状态设为SUCCESS
CHANNEL_MISSING(我方多单)≤ 100元自动冲正,状态设为CLOSED
MISMATCH(金额差异)差额 ≤ 1元以通道方金额为准,调整我方记录
其他任意标记为 MANUAL_REVIEW,通知运营

4.4 通道适配层

适配器模式设计

ChannelAdapter(统一接口)
├── AlipayAdapter     — 支付宝通道适配
├── WechatAdapter     — 微信支付通道适配
├── UnionPayAdapter   — 银联通道适配
└── BankCardAdapter   — 银行卡直连通道适配

统一接口抽象

public interface ChannelAdapter {
    /** 发起支付 */
    ChannelPayResponse pay(ChannelPayRequest request);
    
    /** 支付查询 */
    ChannelQueryResponse query(ChannelQueryRequest request);
    
    /** 发起退款 */
    ChannelRefundResponse refund(ChannelRefundRequest request);
    
    /** 解析异步通知 */
    ChannelNotifyResult parseNotify(HttpServletRequest request);
    
    /** 下载对账文件 */
    ReconcileFile downloadBill(String date);
}

每个适配器负责:

  • 请求参数转换(统一格式 → PSP特有格式)
  • 签名生成(各PSP签名算法不同)
  • 响应解析(PSP返回 → 统一格式)
  • 异常处理(PSP错误码 → 统一错误码)

通道管理

  • 上线:新增 channel_config 记录,先设为 INACTIVE,测试通过后 ACTIVE
  • 下线:设为 INACTIVE,Route Engine 不再选择该通道
  • 维护窗口:设为 MAINTENANCE + 设置维护时间窗,窗口期内自动跳过

五、深度问题

5.1 性能优化

热点商户处理

  • Top 10 商户占 80% 交易量,可能导致分库分表热点
  • 方案:热点商户独立分片(VIP分片),或采用「商户ID + 时间」混合分片键分散热点

数据库优化

  • 读写分离:支付写主库,查询走从库(注意同步延迟)
  • 连接池:HikariCP,核心参数 maxPoolSize = CPU核数 × 2 + 磁盘数
  • 索引:覆盖索引减少回表,避免 SELECT *

缓存策略

  • 通道配置:全量缓存到 Redis + 本地缓存(Caffeine),变更时 MQ 广播刷新
  • 幂等键:Redis Set 前置判断,减少数据库查询
  • 路由结果:短期缓存(10秒),同一商户同一支付方式不反复计算

异步化

  • 记账:MQ 异步(不影响支付响应时间)
  • 通知:MQ 异步 + 指数退避重试(1s/2s/4s/8s/.../最大24小时)
  • 对账/结算:定时任务离线处理

5.2 高可用

多机房部署

  • 同城双活 + 异地灾备(两地三中心)
  • 数据库:同城 Semi-Sync Replication,异地异步复制
  • 流量调度:DNS + GSLB 实现机房级切换

通道故障降级

  • 熔断器自动降级到备选通道(用户无感知)
  • 所有通道不可用时:返回「支付繁忙,请稍后重试」,而非报错
  • 降级期间持续探测,恢复后自动切回

限流熔断

  • 全局限流:API Gateway 层限制总 QPS
  • 商户级限流:防止单个商户打满系统
  • 通道级限流:不超过PSP允许的 QPS
  • 工具:Sentinel / 自建滑动窗口

5.3 数据一致性 — 三重保障

这是支付系统最核心的设计要点:

第一重:本地事务
  → 支付订单状态更新 + 交易流水写入在同一个数据库事务中
  → 保证单库内的数据一致性

第二重:异步通知 + 主动查询
  → PSP 异步通知 + 定时轮询(每5分钟查询 PROCESSING 状态超过10分钟的订单)
  → 保证最终一致性

第三重:T+1 对账
  → 日终对账发现所有差异,是最后的安全网
  → 理论上只要对账100%覆盖,就不会有资金差错

为什么需要三重

  • 第一重解决正常场景
  • 第二重解决通知丢失/延迟的场景
  • 第三重解决所有异常场景的兜底(包括系统bug导致的状态不一致)

5.4 安全合规

PCI-DSS 核心要求

  • 卡号传输:TLS 1.2+
  • 卡号存储:AES-256 加密,密钥分段管理(HSM)
  • 卡号展示:只显示后4位(**** **** **** 1234
  • 日志:禁止记录完整卡号和CVV

防重放

  • 每个请求携带 nonce(一次性随机串)+ timestamp
  • 服务端校验:timestamp 与服务器时间差 < 5分钟
  • nonce 存入 Redis(TTL = 5分钟),重复 nonce 直接拒绝

签名验签

  • 商户请求:RSA-SHA256 签名(商户用私钥签,平台用公钥验)
  • 平台通知:同样签名(平台用私钥签,商户用公钥验)
  • 防止请求篡改

5.5 监控告警

指标采集方式告警阈值优先级
支付成功率实时计算(1分钟窗口)< 95%P0
支付延迟 P99Prometheus Histogram> 5秒P1
通道健康度熔断器状态任一通道熔断P0
资金对账率日终对账报表< 99.99%P0
QPSAPI Gateway 统计> 阈值 80%P1
通知成功率Notification Service< 99%P1
数据库连接池HikariCP Metrics使用率 > 80%P2

告警升级机制

  • P0:立即电话 + 短信通知值班人员,15分钟未响应升级到主管
  • P1:短信 + 企业微信/钉钉通知,30分钟未响应升级
  • P2:企业微信/钉钉通知

5.6 演进路线

Phase 1(当前):单体应用
  → 单机房,MySQL主从,支持日均100万笔
  → 适用于创业期,快速迭代

Phase 2:微服务拆分
  → Payment Core / Route / Channel / Reconcile 独立部署
  → 引入 RocketMQ 解耦,Redis 缓存
  → 支持日均1000万笔

Phase 3:多机房
  → 同城双活,分库分表(ShardingSphere)
  → 通道适配层独立集群
  → 支持日均1亿笔

Phase 4:全球化
  → 多地域部署(中国/东南亚/欧洲)
  → 多币种支持,接入当地PSP
  → 合规适配(GDPR/PSD2/MiCA)
  → 支持日均10亿笔

六、架构决策记录(摘要)

ADR标题决策详见
ADR-001幂等方案选择商户订单号去重 + Redis辅助ADR-001
ADR-002通道路由策略动态权重路由ADR-002
ADR-003对账差错处理分级处理(小额自动+大额人工)ADR-003

七、面试口述版

2分钟版本(电梯演讲)

支付网关是商户与支付渠道之间的中间层。我的设计核心思路是"三层保障":

第一层是通道路由引擎,基于成功率、成本、限额的加权评分动态选择最优通道,配合熔断器实现故障自动降级。第二层是幂等和状态机,通过商户订单号唯一索引做天然幂等,乐观锁状态机防止重复处理,确保"不多扣、不重扣"。第三层是对账兜底,T+1日终对账作为最后安全网,分级处理差异——小额自动冲正、大额人工审核。

架构上采用分层设计:API Gateway负责鉴权限流,Payment Core处理核心流程,Channel Adapter层用适配器模式封装各PSP差异,通过MQ异步解耦记账和通知。数据层以merchant_id分库分表,支持从百万到十亿的扩展。

5分钟版本

在2分钟版本基础上,补充以下内容:

架构容器:系统分为9个独立部署的容器。API Gateway做统一入口和限流,Payment Core是核心,内含订单管理、幂等检查、状态机。Route Engine独立部署,支持实时调整路由权重。Channel Adapter是适配器模式,每接入一个新PSP只需实现统一接口。Reconcile Service是定时任务驱动的对账引擎。中间用RocketMQ解耦。

核心流程:商户下单 → 签名验签 → 幂等检查 → 创建订单 → 风控 → 路由 → 通道调用 → 状态更新 → 异步记账+通知。整个Happy Path延迟控制在3秒内。

关键决策:幂等选择商户订单号而非全局UUID,因为语义清晰、不增加集成复杂度。路由选择动态权重而非ML预测,因为可解释性和运维友好。对账选择分级处理而非全自动,因为大额差异需要人工确认保障资金安全。

15分钟版本

在5分钟版本基础上,展开以下深度话题:

  1. 数据模型(2分钟):五张核心表的设计,分库分表策略,状态机定义
  2. 一致性保障(2分钟):本地事务+异步通知+定时查询+对账兜底的三重保障
  3. 性能优化(2分钟):热点商户隔离、缓存策略、异步化
  4. 高可用(2分钟):多机房部署、通道降级、限流熔断
  5. 安全合规(1分钟):PCI-DSS、签名验签、防重放
  6. 演进路线(1分钟):单体→微服务→多机房→全球化

八、自评与反思

做得好的地方

  1. 需求澄清充分:10个澄清问题覆盖了功能、非功能、约束三个维度,展示了架构师的全局思维
  2. 层次分明:从Context到Container到Component逐层深入,C4方法论运用得当
  3. 核心问题有深度:幂等机制、对账引擎、路由策略都有具体的方案设计和trade-off分析
  4. 三重保障体系:事务+通知+对账的分层保障是支付系统的精髓

可以改进的地方

  1. 国际化/多币种:虽然预留了字段,但具体的汇率管理、多币种记账没有展开
  2. 分布式事务:跨服务的事务一致性(如支付成功但记账失败)可以更深入讨论Saga模式
  3. 安全:HSM硬件安全模块、密钥轮转策略可以更详细
  4. 测试策略:混沌工程、全链路压测、资金安全测试没有涉及

面试中容易被追问的点

  • "你说用乐观锁,如果并发更新失败怎么重试?" → 回答重试策略和最终一致性
  • "对账发现多扣了怎么办?" → 回答自动退款或人工退款的流程
  • "如果Redis挂了,幂等还能保证吗?" → 回答MySQL唯一索引是兜底
  • "怎么测试支付系统的正确性?" → 回答Mock通道 + 对账验证 + 影子流量

附件