编辑 | blame | 历史 | 原始文档

支付模块设计方案(DDD架构)

一、整体架构与技术栈

技术栈

  • Java: 1.8
  • 框架: Spring Boot 2.5.15 + ruoyi-framework
  • 持久化: MyBatis / MyBatis-Plus
  • 架构模式: DDD(领域驱动设计)
  • 支付渠道: 微信支付v2、支付宝当面付

服务定位

  • 独立服务,可单独运行
  • 单商户模式
  • 配置统一在 application.yml 管理

二、DDD分层与包结构

分层映射至 RuoYi 结构

1. Interfaces 层(接口层)

包路径: com.ruoyi.payment.interfaces.controller

职责:
- 对外 REST API(发起支付、查询订单)
- 渠道异步回调 Webhook(微信/支付宝)
- 管理端操作接口(关闭、重发回调、对账查询)

2. Application 层(应用层)

包路径: com.ruoyi.payment.application.service

职责:
- 用例编排(发起支付、处理渠道回调、触发业务回调)
- 事务控制与跨聚合协调
- 定时任务(每日对账、订单过期处理)

3. Domain 层(领域层)

包路径:
- com.ruoyi.payment.domain.model - 聚合根、实体、值对象
- com.ruoyi.payment.domain.service - 领域服务
- com.ruoyi.payment.domain.repository - 仓储接口

职责:
- 核心业务规则与状态机
- 聚合根封装与不变性约束
- 领域事件定义

4. Infrastructure 层(基础设施层)

包路径:
- com.ruoyi.payment.infrastructure.persistence - Mapper 实现
- com.ruoyi.payment.infrastructure.channel.wechat - 微信客户端
- com.ruoyi.payment.infrastructure.channel.alipay - 支付宝客户端
- com.ruoyi.payment.infrastructure.config - 配置加载
- com.ruoyi.payment.infrastructure.schedule - 定时调度
- com.ruoyi.payment.infrastructure.audit - 操作审计

职责:
- 仓储接口实现(数据库访问)
- 渠道客户端封装与适配
- 证书与密钥管理
- 定时任务与消息发布


三、核心领域模型

1. PaymentOrder(支付订单 - 聚合根)

字段:
| 字段 | 类型 | 说明 |
|------|------|------|
| id | Long | 内部订单ID(主键,雪花ID) |
| bizOrderId | String | 外部业务订单号 |
| amount | Integer | 金额(单位:分) |
| currency | String | 币种(固定 CNY) |
| channel | Enum | 支付渠道(WECHAT/ALIPAY) |
| status | Enum | 订单状态 |
| subject | String | 订单标题 |
| description | String | 订单描述 |
| callbackUrl | String | 业务回调地址 |
| expireAt | DateTime | 过期时间(创建后+2小时) |
| latestTransactionId | Long | 最新交易ID |
| channelTradeNo | String | 渠道交易号(支付成功后) |
| paidAt | DateTime | 支付成功时间 |
| version | Integer | 乐观锁版本号 |
| createdAt | DateTime | 创建时间 |
| updatedAt | DateTime | 更新时间 |

订单状态枚举 (status):
- INIT: 初始化
- PENDING: 待支付
- SUCCEEDED: 支付成功
- FAILED: 支付失败
- CANCELED: 已关闭
- EXPIRED: 已过期

业务规则:
1. 金额必须 > 0
2. 币种固定为 CNY
3. 同一订单同时只允许存在**一笔进行中的交易**(PENDING)
4. 订单超过2小时未支付自动标记为 EXPIRED
5. 订单成功后触发外部业务回调

2. PaymentTransaction(支付交易流水 - 实体)

字段:
| 字段 | 类型 | 说明 |
|------|------|------|
| id | Long | 交易ID(主键) |
| orderId | Long | 所属订单ID |
| channel | Enum | 支付渠道 |
| clientType | Enum | 客户端类型(NATIVE/ALIPAY_PRECREATE) |
| status | Enum | 交易状态 |
| codeOrQr | String | 渠道返回的二维码内容(code_url/qr_code) |
| qrBase64 | Text | 服务端生成的Base64二维码图片 |
| requestParams | Text | 渠道请求参数快照 |
| responseSnapshot | Text | 渠道响应快照 |
| channelTradeNo | String | 渠道交易号 |
| createdAt | DateTime | 创建时间 |
| paidAt | DateTime | 支付完成时间 |

交易状态枚举 (status):
- PENDING: 待支付
- SUCCEEDED: 支付成功
- FAILED: 支付失败
- CANCELED: 已取消

业务规则:
1. 每次发起支付时,若订单已有 PENDING 交易,则复用该交易并返回其二维码
2. 若无 PENDING 交易,则创建新交易
3. 一旦交易成功,订单状态同步为 SUCCEEDED

3. NotifyLog(渠道回调日志 - 实体)

字段:
| 字段 | 类型 | 说明 |
|------|------|------|
| id | Long | 日志ID |
| channel | Enum | 支付渠道 |
| notifyIdOrSerial | String | 渠道通知唯一标识 |
| orderId | Long | 订单ID |
| transactionId | Long | 交易ID |
| payload | Text | 回调原始报文 |
| verified | Boolean | 签名验证是否通过 |
| processed | Boolean | 是否已处理 |
| result | String | 处理结果 |
| createdAt | DateTime | 接收时间 |

幂等控制:
- 唯一索引:channel + notifyIdOrSerial
- 重复通知直接返回成功应答

4. BizCallbackLog(业务回调日志 - 实体)

字段:
| 字段 | 类型 | 说明 |
|------|------|------|
| id | Long | 日志ID |
| orderId | Long | 订单ID |
| transactionId | Long | 交易ID |
| callbackUrl | String | 回调地址 |
| payload | Text | 回调请求体 |
| httpStatus | Integer | HTTP 状态码 |
| response | Text | 响应内容 |
| success | Boolean | 是否成功 |
| retryCount | Integer | 重试次数 |
| lastRetryAt | DateTime | 最后重试时间 |
| createdAt | DateTime | 创建时间 |

重试策略:
- 间隔:0/1/5/15/60 分钟
- 最多重试:10次
- 支持管理端手工重发

5. OperationAudit(操作审计 - 实体)

字段:
| 字段 | 类型 | 说明 |
|------|------|------|
| id | Long | 审计ID |
| operator | String | 操作人 |
| operationType | Enum | 操作类型 |
| orderId | Long | 订单ID(可选) |
| transactionId | Long | 交易ID(可选) |
| params | Text | 操作参数 |
| approved | Boolean | 是否通过(记录,不走审批流程) |
| createdAt | DateTime | 操作时间 |

操作类型:
- CLOSE_ORDER: 关闭订单
- RESEND_CALLBACK: 重发业务回调
- MANUAL_FIX: 手工修正对账差异
- UPDATE_CHANNEL_ACCOUNT: 更新渠道账号

6. 值对象

  • Money: { amountInCents, currency }
  • BizOrderId: 外部业务订单号封装
  • CallbackUrl: 业务回调地址封装
  • ChannelTradeNo: 渠道交易号封装

四、核心用例与流程

1. 发起二维码支付

入参:
- bizOrderId: 外部业务订单号
- amount: 金额(分)
- subject: 订单标题
- description: 订单描述
- channel: 支付渠道(WECHAT/ALIPAY)
- callbackUrl: 业务回调地址

流程:
```
1. 查询订单是否存在
- 不存在 → 创建订单(状态 PENDING,expireAt = now + 2小时)
- 存在 → 加载订单

  1. 检查订单是否有进行中的交易(PENDING)
  • 有 → 直接返回已有交易的二维码 Base64
  • 无 → 创建新交易
  1. 调用渠道下单接口
    【微信】

【支付宝】
- 接口:alipay.trade.precreate
- notify_url: https://api.966120.com/api/pay/notify/alipay
- 返回:qr_code

  1. 生成二维码
  • 将 code_url/qr_code 生成 300×300 PNG 二维码
  • 编码为 Base64 字符串
  • 保存到交易记录(qrBase64)
  1. 返回给前端
    {
    "orderId": xxx,
    "transactionId": xxx,
    "status": "PENDING",
    "qrBase64": "data:image/png;base64,iVBORw0KG...",
    "expireAt": "2025-11-23T12:00:00"
    }
    ```

幂等控制:
- 同一订单仅允许一笔 PENDING 交易存在
- 重复请求直接返回已有交易数据

2. 渠道回调处理(Webhook)

微信v2回调:
1. 接收 XML 报文 2. 验证签名(MD5) 3. 解析报文,提取交易号与支付结果 4. 查询订单与交易 5. 状态转换 - 成功 → 交易状态 SUCCEEDED,订单状态 SUCCEEDED - 失败 → 交易状态 FAILED 6. 持久化更新(乐观锁) 7. 记录 NotifyLog(幂等控制) 8. 触发外部业务回调 9. 返回微信要求的 XML 应答

支付宝回调:
1. 接收表单参数 2. 验证签名(RSA2) 3. 解析交易状态 4. 同步订单与交易状态 5. 记录 NotifyLog 6. 触发外部业务回调 7. 返回 "success"

幂等保障:
- 基于 channel + notifyIdOrSerial 唯一索引
- 重复通知直接返回成功应答,不重复处理

3. 外部业务回调

触发时机: 订单状态变更为 SUCCEEDED 后

协议: HTTP POST JSON

请求头:
- Content-Type: application/json
- X-Signature: HMAC-SHA256(payload + nonce + timestamp, callbackSignSecret)
- X-Nonce: 随机字符串
- X-Timestamp: 毫秒时间戳

请求体:
json { "tradeId": 1234567890, "orderId": 9876543210, "bizOrderId": "BIZ20251123001", "channel": "WECHAT", "amount": 10000, "currency": "CNY", "status": "SUCCEEDED", "channelTradeNo": "4200001234567890", "paidAt": "2025-11-23T10:30:00", "subject": "商品订单支付", "description": "订单详情" }

鉴权方式:
- 使用 HMAC-SHA256 签名
- 密钥:配置文件中的 callbackSignSecret
- 签名内容:payload + nonce + timestamp
- 外部系统使用相同密钥验证签名

重试策略:
- 失败重试间隔:0/1/5/15/60 分钟
- 最多重试:10次
- 支持管理端手工重发

4. 订单过期处理

触发方式: 定时任务(建议每10分钟)

逻辑:
1. 查询状态为 PENDING 且 expireAt < now 的订单 2. 将订单状态更新为 EXPIRED 3. 记录操作日志

过期时间:
- 从订单首次创建时间起算 +2小时
- 发起新交易不刷新过期时间

5. 每日对账(自动修复)

调度时间: 每天 02:00

流程:
1. 确定对账日期(T-1) 2. 查询本地该日所有订单与交易 3. 调用渠道查询接口(批量/单笔) - 微信:orderquery - 支付宝:alipay.trade.query 4. 对比本地状态与渠道状态 5. 自动修复 - 本地 PENDING,渠道已成功 → 修正为 SUCCEEDED - 本地 PENDING,渠道已关闭 → 修正为 FAILED 6. 差异记录 - 金额不一致 - 渠道无记录但本地存在 - 其他异常情况 7. 生成对账报告 - 总订单数 - 成功数 - 失败数 - 差异数 - 自动修复数 8. 保存对账任务与结果明细

差异处理:
- 状态差异:自动修复并记录
- 金额差异:仅记录,不自动修复,需人工介入
- 严重差异:告警通知


五、渠道适配与客户端封装

1. 微信支付 v2(Native)

配置项 (application.yml):
yaml payment: wechat: appId: wx70f6a7346ee842xx mchId: 1573728xxx mchKey: Xz0ClPK3f5sCeT6SGa1vpVmyUFcbpxxx notifyUrl: https://api.966120.com/api/pay/notify/wechat signType: MD5

接口封装 (WxPayV2Client):
- createNativeOrder(): 统一下单,返回 code_url
- queryOrder(): 查询订单状态
- closeOrder(): 关闭订单
- verifyNotify(): 验证回调签名
- buildNotifyResponse(): 构造应答报文

签名方式: MD5

关键流程:
1. 参数按 ASCII 排序拼接
2. 追加 &key=mchKey
3. MD5 加密并转大写

2. 支付宝当面付(Precreate)

配置项 (application.yml):
yaml payment: alipay: appId: 2021xxxxxxxxxxxxx privateKey: MIIEvQIBADANBgkqhki...(商户RSA2私钥) alipayPublicKey: MIIBIjANBgkqhki...(支付宝RSA2公钥) serverUrl: https://openapi.alipay.com/gateway.do notifyUrl: https://api.966120.com/api/pay/notify/alipay signType: RSA2

接口封装 (AlipayF2FClient):
- precreate(): 当面付下单,返回 qr_code
- query(): 查询订单状态
- close(): 关闭订单
- verifyNotify(): 验证回调签名

签名方式: RSA2

SDK: 使用官方 alipay-sdk-java

3. 渠道客户端统一接口

public interface ChannelClient {
    /**
     * 创建二维码订单
     */
    QrOrderResponse createQrOrder(QrOrderRequest request);
    
    /**
     * 查询订单状态
     */
    OrderQueryResponse queryOrder(String channelTradeNo);
    
    /**
     * 关闭订单
     */
    void closeOrder(String channelTradeNo);
    
    /**
     * 验证回调签名
     */
    boolean verifyNotify(Map<String, String> params);
}

工厂模式选择实现:
- WxPayV2Client implements ChannelClient
- AlipayF2FClient implements ChannelClient


六、二维码生成规范

技术方案: 使用 ZXing 库

参数:
- 尺寸:300 × 300
- 格式:PNG
- 纠错等级:M(默认)
- 编码:UTF-8

输出格式:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...

实现:
java // 伪代码 BufferedImage qrImage = QRCodeGenerator.generate(codeUrl, 300, 300); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(qrImage, "PNG", baos); String base64 = "data:image/png;base64," + Base64.getEncoder().encodeToString(baos.toByteArray());


七、数据表设计

1. pay_order(支付订单表)

CREATE TABLE `pay_order` (
  `id` BIGINT NOT NULL PRIMARY KEY COMMENT '订单ID',
  `biz_order_id` VARCHAR(64) NOT NULL COMMENT '业务订单号',
  `amount` INT NOT NULL COMMENT '金额(分)',
  `currency` VARCHAR(8) NOT NULL DEFAULT 'CNY' COMMENT '币种',
  `channel` VARCHAR(16) NOT NULL COMMENT '支付渠道',
  `status` VARCHAR(16) NOT NULL COMMENT '订单状态',
  `subject` VARCHAR(128) NOT NULL COMMENT '订单标题',
  `description` VARCHAR(512) COMMENT '订单描述',
  `callback_url` VARCHAR(512) NOT NULL COMMENT '业务回调地址',
  `expire_at` DATETIME NOT NULL COMMENT '过期时间',
  `latest_transaction_id` BIGINT COMMENT '最新交易ID',
  `channel_trade_no` VARCHAR(64) COMMENT '渠道交易号',
  `paid_at` DATETIME COMMENT '支付成功时间',
  `version` INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
  `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  INDEX `idx_biz_order_id` (`biz_order_id`),
  INDEX `idx_channel_trade_no` (`channel_trade_no`),
  INDEX `idx_status` (`status`),
  INDEX `idx_expire_at` (`expire_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='支付订单表';

2. pay_transaction(支付交易流水表)

CREATE TABLE `pay_transaction` (
  `id` BIGINT NOT NULL PRIMARY KEY COMMENT '交易ID',
  `order_id` BIGINT NOT NULL COMMENT '订单ID',
  `channel` VARCHAR(16) NOT NULL COMMENT '支付渠道',
  `client_type` VARCHAR(32) NOT NULL COMMENT '客户端类型',
  `status` VARCHAR(16) NOT NULL COMMENT '交易状态',
  `code_or_qr` VARCHAR(512) COMMENT '二维码内容',
  `qr_base64` TEXT COMMENT 'Base64二维码图片',
  `request_params` TEXT COMMENT '请求参数快照',
  `response_snapshot` TEXT COMMENT '响应快照',
  `channel_trade_no` VARCHAR(64) COMMENT '渠道交易号',
  `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `paid_at` DATETIME COMMENT '支付完成时间',
  INDEX `idx_order_id` (`order_id`),
  INDEX `idx_channel_trade_no` (`channel_trade_no`),
  INDEX `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='支付交易流水表';

3. notify_log(渠道回调日志表)

CREATE TABLE `notify_log` (
  `id` BIGINT NOT NULL PRIMARY KEY COMMENT '日志ID',
  `channel` VARCHAR(16) NOT NULL COMMENT '支付渠道',
  `notify_id_or_serial` VARCHAR(64) NOT NULL COMMENT '渠道通知唯一标识',
  `order_id` BIGINT COMMENT '订单ID',
  `transaction_id` BIGINT COMMENT '交易ID',
  `payload` TEXT NOT NULL COMMENT '回调原始报文',
  `verified` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '签名验证是否通过',
  `processed` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否已处理',
  `result` VARCHAR(128) COMMENT '处理结果',
  `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '接收时间',
  UNIQUE KEY `uk_channel_notify` (`channel`, `notify_id_or_serial`),
  INDEX `idx_order_id` (`order_id`),
  INDEX `idx_transaction_id` (`transaction_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='渠道回调日志表';

4. biz_callback_log(业务回调日志表)

CREATE TABLE `biz_callback_log` (
  `id` BIGINT NOT NULL PRIMARY KEY COMMENT '日志ID',
  `order_id` BIGINT NOT NULL COMMENT '订单ID',
  `transaction_id` BIGINT NOT NULL COMMENT '交易ID',
  `callback_url` VARCHAR(512) NOT NULL COMMENT '回调地址',
  `payload` TEXT NOT NULL COMMENT '回调请求体',
  `http_status` INT COMMENT 'HTTP状态码',
  `response` TEXT COMMENT '响应内容',
  `success` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否成功',
  `retry_count` INT NOT NULL DEFAULT 0 COMMENT '重试次数',
  `last_retry_at` DATETIME COMMENT '最后重试时间',
  `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  INDEX `idx_order_id` (`order_id`),
  INDEX `idx_success` (`success`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='业务回调日志表';

5. operation_audit(操作审计表)

CREATE TABLE `operation_audit` (
  `id` BIGINT NOT NULL PRIMARY KEY COMMENT '审计ID',
  `operator` VARCHAR(64) NOT NULL COMMENT '操作人',
  `operation_type` VARCHAR(32) NOT NULL COMMENT '操作类型',
  `order_id` BIGINT COMMENT '订单ID',
  `transaction_id` BIGINT COMMENT '交易ID',
  `params` TEXT COMMENT '操作参数',
  `approved` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否通过',
  `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '操作时间',
  INDEX `idx_operator` (`operator`),
  INDEX `idx_operation_type` (`operation_type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='操作审计表';

6. reconciliation_task(对账任务表)

CREATE TABLE `reconciliation_task` (
  `id` BIGINT NOT NULL PRIMARY KEY COMMENT '任务ID',
  `task_date` DATE NOT NULL COMMENT '对账日期',
  `status` VARCHAR(16) NOT NULL COMMENT '任务状态',
  `total_count` INT NOT NULL DEFAULT 0 COMMENT '总订单数',
  `success_count` INT NOT NULL DEFAULT 0 COMMENT '成功数',
  `failed_count` INT NOT NULL DEFAULT 0 COMMENT '失败数',
  `diff_count` INT NOT NULL DEFAULT 0 COMMENT '差异数',
  `fixed_count` INT NOT NULL DEFAULT 0 COMMENT '自动修复数',
  `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `finished_at` DATETIME COMMENT '完成时间',
  UNIQUE KEY `uk_task_date` (`task_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='对账任务表';

7. reconciliation_result(对账差异明细表)

CREATE TABLE `reconciliation_result` (
  `id` BIGINT NOT NULL PRIMARY KEY COMMENT '明细ID',
  `task_id` BIGINT NOT NULL COMMENT '任务ID',
  `order_id` BIGINT NOT NULL COMMENT '订单ID',
  `transaction_id` BIGINT COMMENT '交易ID',
  `local_status` VARCHAR(16) COMMENT '本地状态',
  `channel_status` VARCHAR(16) COMMENT '渠道状态',
  `diff_type` VARCHAR(32) NOT NULL COMMENT '差异类型',
  `fixed` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否已修复',
  `note` VARCHAR(512) COMMENT '备注',
  `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  INDEX `idx_task_id` (`task_id`),
  INDEX `idx_order_id` (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='对账差异明细表';

八、RESTful API 接口清单

1. 发起微信 Native 支付

POST /api/pay/wechat/native

请求体:
json { "bizOrderId": "BIZ20251123001", "amount": 10000, "subject": "商品订单支付", "description": "购买商品A", "callbackUrl": "https://your-business.com/notify" }
响应:
json { "code": 200, "msg": "success", "data": { "orderId": 1234567890, "transactionId": 9876543210, "status": "PENDING", "qrBase64": "data:image/png;base64,iVBORw0KG...", "expireAt": "2025-11-23T12:00:00" } }

2. 发起支付宝当面付

POST /api/pay/alipay/precreate

请求体/响应: 同上

3. 微信回调通知

POST /api/pay/notify/wechat

说明: 接收微信 XML 报文,验签后处理,返回 XML 应答

4. 支付宝回调通知

POST /api/pay/notify/alipay

说明: 接收表单参数,验签后处理,返回 "success"

5. 查询订单

GET /api/pay/orders/{orderId}

响应:
json { "code": 200, "msg": "success", "data": { "orderId": 1234567890, "bizOrderId": "BIZ20251123001", "amount": 10000, "currency": "CNY", "channel": "WECHAT", "status": "SUCCEEDED", "subject": "商品订单支付", "channelTradeNo": "4200001234567890", "paidAt": "2025-11-23T10:30:00", "expireAt": "2025-11-23T12:00:00", "createdAt": "2025-11-23T10:00:00" } }

6. 查询最新交易

GET /api/pay/orders/{orderId}/transactions/latest

响应:
json { "code": 200, "msg": "success", "data": { "transactionId": 9876543210, "orderId": 1234567890, "status": "PENDING", "qrBase64": "data:image/png;base64,iVBORw0KG...", "createdAt": "2025-11-23T10:00:00" } }

7. 关闭订单

POST /api/pay/orders/{orderId}/close

响应:
json { "code": 200, "msg": "订单已关闭" }

8. 重发业务回调

POST /api/pay/orders/{orderId}/callback/resend

响应:
json { "code": 200, "msg": "回调已重新发送" }

9. 查询对账任务列表

GET /api/pay/reconciliation/tasks?date=2025-11-22

响应:
json { "code": 200, "msg": "success", "data": { "taskId": 12345, "taskDate": "2025-11-22", "status": "FINISHED", "totalCount": 1000, "successCount": 980, "failedCount": 15, "diffCount": 5, "fixedCount": 3, "createdAt": "2025-11-23T02:00:00", "finishedAt": "2025-11-23T02:15:00" } }

10. 查询对账差异明细

GET /api/pay/reconciliation/results?taskId=12345

响应:
json { "code": 200, "msg": "success", "data": [ { "orderId": 1234567890, "transactionId": 9876543210, "localStatus": "PENDING", "channelStatus": "SUCCEEDED", "diffType": "STATUS_DIFF", "fixed": true, "note": "已自动修复" } ] }


九、配置文件示例

application.yml:
```yaml
spring:
application:
name: dryad-payment

支付配置

payment:
# 微信支付配置
wechat:
appId: wx70f6a7346ee842xx
mchId: 1573728xxx
mchKey: Xz0ClPK3f5sCeT6SGa1vpVmyUFcbpxxx
notifyUrl: https://api.966120.com/api/pay/notify/wechat
signType: MD5

# 支付宝配置
alipay:
appId: 2021xxxxxxxxxxxxx
privateKey: MIIEvQIBADANBgkqhki...
alipayPublicKey: MIIBIjANBgkqhki...
serverUrl: https://openapi.alipay.com/gateway.do
notifyUrl: https://api.966120.com/api/pay/notify/alipay
signType: RSA2

# 业务回调配置
business:
callbackSignSecret: your-hmac-secret-key-here
callbackRetryMaxCount: 10
callbackRetryIntervals: 0,1,5,15,60 # 分钟

# 二维码配置
qrcode:
size: 300
format: PNG

# 对账配置
reconciliation:
enabled: true
cron: "0 0 2 * * ?" # 每天凌晨2点
```


十、管理端页面功能清单

1. 支付订单管理

路径: /payment/orders

功能:
- 列表展示(支持分页)
- 条件检索:业务订单号、渠道、状态、时间范围
- 详情查看:订单信息、交易记录、回调日志
- 操作:手工关闭订单

2. 交易流水管理

路径: /payment/transactions

功能:
- 列表展示(支持分页)
- 查看二维码(Base64图片)
- 查看渠道请求/响应快照
- 按订单过滤

3. 渠道回调日志

路径: /payment/notify-logs

功能:
- 列表展示(支持分页)
- 查看原始报文
- 验签结果展示
- 处理状态与结果

4. 业务回调日志

路径: /payment/callback-logs

功能:
- 列表展示(支持分页)
- 查看回调请求/响应
- 重试次数与状态
- 手工重发回调

5. 对账管理

路径: /payment/reconciliation

功能:
- 对账任务列表(按日期)
- 查看对账统计
- 差异明细列表
- 自动修复记录查看

6. 操作审计

路径: /payment/audit

功能:
- 审计日志列表(支持分页)
- 按操作人、操作类型过滤
- 查看操作参数与时间

说明: 不提供导出功能,所有数据仅在线查看


十一、安全与合规

1. 密钥管理

  • 配置文件权限受控(仅服务器可读)
  • 日志脱敏(密钥、证书内容不输出)
  • 定期更换密钥机制(预留接口)

2. 签名验证

  • 所有渠道回调必须验签
  • 外部业务回调采用 HMAC-SHA256
  • 签名失败记录告警

3. 幂等与防重

  • 渠道回调:notify 唯一键
  • 发起支付:订单级别控制
  • 业务回调:重试机制 + 日志记录

4. 数据安全

  • 敏感字段加密存储(如有需要)
  • 传输层 HTTPS 强制
  • 回调 URL 白名单校验(可选)

5. 操作审计

  • 所有关键操作记录审计日志
  • 操作人、时间、参数完整记录
  • 支持回溯与追责

十二、监控与告警

1. 关键指标

  • 订单支付成功率
  • 渠道回调验签失败率
  • 业务回调成功率
  • 对账差异数量
  • 订单过期率

2. 告警规则

  • 回调验签失败 > 阈值
  • 业务回调失败率 > 阈值
  • 对账差异数 > 阈值
  • 渠道接口异常(超时/错误码)

3. 日志

  • 统一 traceId 贯穿全链路
  • 关键节点日志输出
  • 错误堆栈完整记录

十三、后续扩展规划

1. 退款功能

  • RefundOrder 聚合根
  • 部分退款支持
  • 退款对账

2. 分账功能

  • 支持微信/支付宝分账
  • 分账接收方管理
  • 分账明细与对账

3. 多商户支持

  • ChannelAccount 实体激活
  • 按商户维度管理密钥
  • 多租户隔离

4. 更多支付方式

  • 微信 H5/APP/小程序
  • 支付宝 APP/WAP
  • 银行卡快捷支付

5. 对账优化

  • 支持下载渠道对账文件
  • 自动解析与比对
  • 差异自动修复策略升级

十四、开发与交付计划

阶段一:核心支付功能(预计 2 周)

  • 领域模型与数据库表
  • 微信 Native 支付
  • 支付宝当面付
  • 渠道回调处理
  • 二维码生成与 Base64 返回

阶段二:业务回调与审计(预计 1 周)

  • 外部业务回调逻辑
  • HMAC 鉴权实现
  • 重试机制
  • 操作审计记录

阶段三:对账与管理端(预计 1.5 周)

  • 每日对账任务
  • 自动修复逻辑
  • 管理端页面开发
  • 权限菜单集成

阶段四:测试与优化(预计 1 周)

  • 单元测试
  • 集成测试(沙箱环境)
  • 性能测试与优化
  • 安全测试

十五、技术风险与应对

1. 并发控制

风险: 同一订单并发创建多笔交易
应对: 订单级别乐观锁 + 数据库唯一约束

2. 回调丢失

风险: 渠道回调未成功送达
应对: 每日对账补偿 + 主动查询机制

3. 业务回调失败

风险: 外部系统不可用导致回调失败
应对: 重试机制 + 手工重发

4. 对账差异

风险: 本地与渠道状态不一致
应对: 自动修复 + 差异告警 + 人工介入

5. 密钥泄露

风险: 配置文件泄露导致密钥暴露
应对: 文件权限控制 + 日志脱敏 + 定期更换


十六、总结

本设计方案基于 DDD 架构,充分考虑了支付业务的复杂性与稳健性需求:

  1. 清晰的领域模型: 聚合根、实体、值对象职责明确
  2. 严格的状态控制: 订单与交易状态机、过期机制
  3. 可靠的回调机制: 幂等、重试、鉴权
  4. 自动化对账: 每日修复、差异告警
  5. 完整的审计追溯: 操作日志、渠道日志、业务日志
  6. 可扩展架构: 预留退款、分账、多商户等扩展点

该方案已对齐:
- ✅ Java 1.8 + Spring Boot 2.5.15 + ruoyi-framework
- ✅ 微信支付 v2 统一下单(MD5签名)
- ✅ 支付宝当面付(RSA2签名)
- ✅ 服务端返回 300×300 Base64 二维码
- ✅ 外部业务回调 HMAC 鉴权
- ✅ 同一订单仅一笔进行中交易
- ✅ 订单 2 小时自动过期
- ✅ 每日 2 点自动对账修复
- ✅ 单商户配置文件加载
- ✅ 管理端页面(不导出)

方案已确认,可进入代码实现阶段。