feat: 完整重构小程序匹配功能 + 修复UI对齐 + 文章数据API

主要更新:
1. 按H5网页端完全重构匹配功能(match页面)
   - 4种匹配类型: 创业合伙/资源对接/导师顾问/团队招募
   - 资源对接等类型弹出手机号/微信号输入框
   - 去掉重新匹配按钮,改为返回按钮

2. 修复所有卡片对齐和宽度问题
   - 目录页附录卡片居中
   - 首页阅读进度卡片满宽度
   - 我的页面菜单卡片对齐
   - 推广中心分享卡片统一宽度

3. 修复目录页图标和文字对齐
   - section-icon固定40rpx宽高
   - section-title与图标垂直居中

4. 更新真实完整文章标题(62篇)
   - 从book目录读取真实markdown文件名
   - 替换之前的简化标题

5. 新增文章数据API
   - /api/db/chapters - 获取完整书籍结构
   - 支持按ID获取单篇文章内容
This commit is contained in:
卡若
2026-01-21 15:49:12 +08:00
parent 1ee25e3dab
commit b60edb3d47
197 changed files with 34430 additions and 7345 deletions

View File

@@ -0,0 +1,382 @@
# 通用支付模块 API 接口定义 (Universal Payment API) v4.0
> 无论后端使用何种语言Python/Node/Go/Java/PHP请严格实现以下 RESTful 接口
## 🎯 设计原则
1. **RESTful 风格**: 资源命名统一,动词语义清晰
2. **统一响应格式**: 所有接口返回 `{code, message, data}` 结构
3. **幂等性**: 重复请求不产生副作用
4. **安全性**: 敏感操作需签名验证
---
## 📦 统一响应格式
### 成功响应
```json
{
"code": 200,
"message": "success",
"data": { ... }
}
```
### 错误响应
```json
{
"code": 400,
"message": "参数错误order_sn 不能为空",
"data": null
}
```
### 常用错误码
| Code | 含义 |
|:---|:---|
| 200 | 成功 |
| 400 | 请求参数错误 |
| 401 | 未授权/登录过期 |
| 403 | 无权限 |
| 404 | 资源不存在 |
| 409 | 状态冲突 (如重复支付) |
| 500 | 服务器内部错误 |
---
## 1. 核心交易接口 (Core Transaction)
### 1.1 创建订单
业务系统调用,创建一个待支付订单。
```http
POST /api/payment/create_order
Content-Type: application/json
Authorization: Bearer {token}
```
**Request Body**:
```json
{
"user_id": "u1001", // [必填] 用户ID
"title": "VIP会员月卡", // [必填] 订单标题 (≤30字符)
"amount": 99.00, // [必填] 金额 (单位: 元)
"currency": "CNY", // [可选] 币种,默认 CNY
"product_id": "vip_monthly", // [可选] 商品ID
"product_type": "membership", // [可选] 商品类型
"extra_params": { // [可选] 扩展参数 (会透传到回调)
"coupon_id": "C001",
"referrer": "user_123"
}
}
```
**Response**:
```json
{
"code": 200,
"message": "success",
"data": {
"order_sn": "202401170001", // 系统生成的订单号
"status": "created", // 订单状态
"amount": 99.00,
"expire_at": "2024-01-17T11:30:00Z" // 订单过期时间
}
}
```
---
### 1.2 发起支付 (收银台)
用户选择支付方式后,获取支付参数。
```http
POST /api/payment/checkout
Content-Type: application/json
Authorization: Bearer {token}
```
**Request Body**:
```json
{
"order_sn": "202401170001", // [必填] 订单号
"gateway": "wechat_jsapi", // [必填] 支付网关 (见下方枚举)
"return_url": "https://...", // [可选] 支付成功后跳转地址
"openid": "oXxx...", // [条件] 微信JSAPI必填
"coin_amount": 0 // [可选] 使用虚拟币抵扣金额
}
```
**Gateway 支付网关枚举**:
| Gateway | 说明 | 返回类型 |
|:---|:---|:---|
| `alipay_web` | 支付宝PC网页 | url (跳转) |
| `alipay_wap` | 支付宝H5 | url (跳转) |
| `alipay_qr` | 支付宝扫码 | qrcode |
| `wechat_native` | 微信扫码 | qrcode |
| `wechat_jsapi` | 微信公众号/小程序 | json (SDK参数) |
| `wechat_h5` | 微信H5 | url (跳转) |
| `wechat_app` | 微信APP | json (SDK参数) |
| `paypal` | PayPal | url (跳转) |
| `stripe` | Stripe | url (Checkout Session) |
| `usdt` | USDT-TRC20 | address (钱包地址) |
| `coin` | 纯虚拟币支付 | direct (直接完成) |
**Response**:
```json
{
"code": 200,
"message": "success",
"data": {
"trade_sn": "T20240117100001", // 交易流水号
"type": "qrcode", // 响应类型: url/qrcode/json/address/direct
"payload": "weixin://wxpay/...",// 支付数据 (根据type不同)
"expiration": 1800, // 过期时间 (秒)
"amount": 99.00, // 实际支付金额 (扣除抵扣后)
"coin_deducted": 0 // 虚拟币抵扣金额
}
}
```
**不同 type 的 payload 格式**:
```javascript
// type: "url" - 跳转链接
payload: "https://openapi.alipay.com/gateway.do?..."
// type: "qrcode" - 二维码内容
payload: "weixin://wxpay/bizpayurl?pr=xxx"
// type: "json" - SDK调起参数 (微信JSAPI)
payload: {
"appId": "wx...",
"timeStamp": "1705470600",
"nonceStr": "xxx",
"package": "prepay_id=wx...",
"signType": "RSA",
"paySign": "xxx"
}
// type: "address" - 加密货币地址
payload: {
"address": "TXxx...",
"amount_usdt": 13.88,
"memo": "202401170001"
}
// type: "direct" - 直接完成 (纯虚拟币支付)
payload: { "status": "paid" }
```
---
### 1.3 查询订单状态
前端轮询使用,判断支付是否完成。
```http
GET /api/payment/status/{order_sn}
Authorization: Bearer {token}
```
**Response**:
```json
{
"code": 200,
"message": "success",
"data": {
"order_sn": "202401170001",
"status": "paid", // created/paying/paid/closed/refunded
"paid_amount": 99.00,
"paid_at": "2024-01-17T10:05:00Z",
"payment_method": "wechat_jsapi",
"trade_sn": "T20240117100001"
}
}
```
**订单状态机**:
```
created → paying → paid → (refunded)
↓ ↓
closed closed
```
---
### 1.4 关闭订单
主动关闭未支付的订单。
```http
POST /api/payment/close/{order_sn}
Authorization: Bearer {token}
```
**Response**:
```json
{
"code": 200,
"message": "success",
"data": {
"order_sn": "202401170001",
"status": "closed",
"closed_at": "2024-01-17T10:10:00Z"
}
}
```
---
## 2. 回调通知接口 (Webhook)
### 2.1 统一回调入口
接收第三方支付平台的异步通知。
```http
POST /api/payment/notify/{gateway}
```
**Path Params**:
- `gateway`: `alipay` / `wechat` / `paypal` / `stripe` / `nowpayments`
**处理逻辑**:
1. 根据 gateway 加载对应驱动
2. 验签 (Verify Signature)
3. 幂等性检查 (防重复处理)
4. 更新订单状态
5. 触发业务回调 (发货/开通权限等)
6. 返回平台所需响应
**返回格式**:
```
# 支付宝
success
# 微信
<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>
# Stripe
HTTP 200 OK
# PayPal
HTTP 200 OK
```
---
### 2.2 同步返回 (Return)
用户支付完成后的页面跳转。
```http
GET /api/payment/return/{gateway}
```
**Query Params**: 各平台不同,由平台自动附加
**处理逻辑**:
1. 解析回传参数
2. 验签
3. 重定向到成功页面
---
## 3. 辅助接口
### 3.1 获取可用支付方式
```http
GET /api/payment/methods
```
**Response**:
```json
{
"code": 200,
"data": {
"methods": [
{
"gateway": "wechat_jsapi",
"name": "微信支付",
"icon": "/icons/wechat.png",
"enabled": true,
"available": true // 当前环境是否可用 (如微信内)
},
{
"gateway": "alipay_wap",
"name": "支付宝",
"icon": "/icons/alipay.png",
"enabled": true,
"available": true
}
]
}
}
```
### 3.2 获取汇率
```http
GET /api/payment/exchange_rate?from=CNY&to=USD
```
**Response**:
```json
{
"code": 200,
"data": {
"from": "CNY",
"to": "USD",
"rate": 0.139,
"updated_at": "2024-01-17T00:00:00Z"
}
}
```
---
## 4. 管理接口 (Admin)
### 4.1 订单列表
```http
GET /api/admin/payment/orders?page=1&limit=20&status=paid
Authorization: Bearer {admin_token}
```
### 4.2 交易流水列表
```http
GET /api/admin/payment/trades?page=1&limit=20
Authorization: Bearer {admin_token}
```
### 4.3 发起退款
```http
POST /api/admin/payment/refund
Authorization: Bearer {admin_token}
Content-Type: application/json
{
"trade_sn": "T20240117100001",
"amount": 99.00,
"reason": "用户申请退款"
}
```
---
## 5. 接口签名规范 (可选)
对于安全要求高的场景,可启用接口签名:
```javascript
// 请求头
X-Sign: sha256(timestamp + nonce + body + secret)
X-Timestamp: 1705470600
X-Nonce: abc123
```
---
## 📌 注意事项
1. **金额单位**: 所有金额均以**元**为单位小数点后2位
2. **时间格式**: ISO 8601 格式 `YYYY-MM-DDTHH:mm:ssZ`
3. **字符编码**: UTF-8
4. **HTTPS**: 生产环境必须使用 HTTPS
5. **幂等性**: 相同订单号重复请求返回相同结果

View File

@@ -0,0 +1,396 @@
# 业务逻辑与数据模型 (Business Logic & Data Model) v4.0
> 定义支付系统的核心数据结构和业务流程
## 📊 数据库表结构
### 1. 订单表 (orders)
存储业务订单信息,与支付解耦。
```sql
CREATE TABLE `orders` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`sn` VARCHAR(32) NOT NULL COMMENT '订单号 (业务唯一)',
`user_id` VARCHAR(64) NOT NULL COMMENT '用户ID',
`title` VARCHAR(128) NOT NULL COMMENT '订单标题',
`price_amount` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '订单原价 (分)',
`pay_amount` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '应付金额 (分)',
`currency` VARCHAR(8) NOT NULL DEFAULT 'CNY' COMMENT '货币类型',
`status` VARCHAR(20) NOT NULL DEFAULT 'created' COMMENT '状态: created/paying/paid/closed/refunded',
`product_id` VARCHAR(64) DEFAULT NULL COMMENT '商品ID',
`product_type` VARCHAR(32) DEFAULT NULL COMMENT '商品类型',
`extra_data` JSON DEFAULT NULL COMMENT '扩展数据',
`paid_at` DATETIME DEFAULT NULL COMMENT '支付时间',
`closed_at` DATETIME DEFAULT NULL COMMENT '关闭时间',
`expired_at` DATETIME DEFAULT NULL COMMENT '过期时间',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_sn` (`sn`),
KEY `idx_user_id` (`user_id`),
KEY `idx_status` (`status`),
KEY `idx_created_at` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';
```
### 2. 交易流水表 (pay_trades)
记录每一次支付尝试,一个订单可能有多次交易。
```sql
CREATE TABLE `pay_trades` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`trade_sn` VARCHAR(32) NOT NULL COMMENT '交易流水号 (系统生成)',
`order_sn` VARCHAR(32) NOT NULL COMMENT '关联订单号',
`user_id` VARCHAR(64) NOT NULL COMMENT '用户ID',
`title` VARCHAR(128) NOT NULL COMMENT '交易标题',
`amount` BIGINT UNSIGNED NOT NULL COMMENT '交易金额 (分)',
`cash_amount` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '现金支付金额 (分)',
`coin_amount` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '虚拟币抵扣金额',
`currency` VARCHAR(8) NOT NULL DEFAULT 'CNY' COMMENT '货币类型',
`platform` VARCHAR(32) NOT NULL COMMENT '支付平台: alipay/wechat/paypal/stripe/usdt/coin',
`platform_type` VARCHAR(32) DEFAULT NULL COMMENT '平台子类型: web/wap/jsapi/native/h5/app',
`platform_sn` VARCHAR(64) DEFAULT NULL COMMENT '平台交易号',
`platform_created_params` JSON DEFAULT NULL COMMENT '发送给平台的参数',
`platform_created_result` JSON DEFAULT NULL COMMENT '平台返回的结果',
`status` VARCHAR(20) NOT NULL DEFAULT 'paying' COMMENT '状态: paying/paid/closed/refunded',
`type` VARCHAR(20) NOT NULL DEFAULT 'purchase' COMMENT '类型: purchase(购买)/recharge(充值)',
`pay_time` DATETIME DEFAULT NULL COMMENT '支付时间',
`notify_data` JSON DEFAULT NULL COMMENT '回调原始数据',
`seller_id` VARCHAR(64) DEFAULT NULL COMMENT '卖家ID (多商户场景)',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_trade_sn` (`trade_sn`),
KEY `idx_order_sn` (`order_sn`),
KEY `idx_platform_sn` (`platform_sn`),
KEY `idx_user_id` (`user_id`),
KEY `idx_status` (`status`),
KEY `idx_created_at` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='交易流水表';
```
### 3. 资金流水表 (cashflows)
记录账户资金变动(可选,用于虚拟币/钱包场景)。
```sql
CREATE TABLE `cashflows` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`sn` VARCHAR(32) NOT NULL COMMENT '流水号',
`user_id` VARCHAR(64) NOT NULL COMMENT '用户ID',
`type` VARCHAR(20) NOT NULL COMMENT '类型: inflow(入账)/outflow(出账)',
`action` VARCHAR(32) NOT NULL COMMENT '动作: recharge/purchase/refund/transfer',
`amount` BIGINT NOT NULL COMMENT '金额 (分,正数入账负数出账)',
`currency` VARCHAR(8) NOT NULL DEFAULT 'CNY',
`balance_before` BIGINT NOT NULL DEFAULT 0 COMMENT '变动前余额',
`balance_after` BIGINT NOT NULL DEFAULT 0 COMMENT '变动后余额',
`trade_sn` VARCHAR(32) DEFAULT NULL COMMENT '关联交易流水号',
`order_sn` VARCHAR(32) DEFAULT NULL COMMENT '关联订单号',
`remark` VARCHAR(256) DEFAULT NULL COMMENT '备注',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_sn` (`sn`),
KEY `idx_user_id` (`user_id`),
KEY `idx_trade_sn` (`trade_sn`),
KEY `idx_created_at` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='资金流水表';
```
### 4. 退款记录表 (refunds)
```sql
CREATE TABLE `refunds` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`refund_sn` VARCHAR(32) NOT NULL COMMENT '退款单号',
`trade_sn` VARCHAR(32) NOT NULL COMMENT '原交易流水号',
`order_sn` VARCHAR(32) NOT NULL COMMENT '原订单号',
`amount` BIGINT UNSIGNED NOT NULL COMMENT '退款金额 (分)',
`reason` VARCHAR(256) DEFAULT NULL COMMENT '退款原因',
`status` VARCHAR(20) NOT NULL DEFAULT 'pending' COMMENT '状态: pending/processing/success/failed',
`platform_refund_sn` VARCHAR(64) DEFAULT NULL COMMENT '平台退款单号',
`refunded_at` DATETIME DEFAULT NULL COMMENT '退款完成时间',
`operator_id` VARCHAR(64) DEFAULT NULL COMMENT '操作人ID',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_refund_sn` (`refund_sn`),
KEY `idx_trade_sn` (`trade_sn`),
KEY `idx_order_sn` (`order_sn`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='退款记录表';
```
---
## 🔄 状态机定义
### 订单状态 (Order Status)
```
┌─────────────────────────────────────────────────┐
│ created │
│ │ │
│ ┌───────────┼───────────┐ │
│ ▼ │ ▼ │
│ paying ────────┼───────► closed │
│ │ │ │
│ ▼ │ │
│ paid ─────────┼───────► refunded │
│ │ │
└─────────────────────────────────────────────────┘
状态说明:
- created: 订单已创建,等待支付
- paying: 支付中 (已发起支付请求)
- paid: 已支付
- closed: 已关闭 (超时/主动取消)
- refunded: 已退款
```
### 交易状态 (Trade Status)
```
paying → paid
↓ ↓
closed refunded
状态说明:
- paying: 支付中
- paid: 支付成功
- closed: 交易关闭
- refunded: 已退款
```
---
## 🔢 编号规则
### 订单号 (order_sn)
```
格式: YYYYMMDD + 6位随机数
示例: 202401170001
生成规则:
1. 日期前缀保证每日唯一空间
2. 随机数使用分布式ID生成器
3. 支持前缀自定义 (如区分业务线)
```
### 交易流水号 (trade_sn)
```
格式: T + YYYYMMDD + HHmmss + 5位随机数
示例: T20240117100530123456
生成规则:
1. 前缀 T 标识交易类型
2. 精确到秒的时间戳
3. 5位随机数防碰撞
```
---
## 📋 核心业务流程
### 1. 标准支付流程
```sequence
用户 -> 业务系统: 1. 提交订单
业务系统 -> 支付模块: 2. 创建订单 (create_order)
支付模块 -> 业务系统: 3. 返回 order_sn
用户 -> 支付模块: 4. 选择支付方式并支付 (checkout)
支付模块 -> 支付平台: 5. 创建平台交易
支付平台 -> 支付模块: 6. 返回支付参数
支付模块 -> 用户: 7. 返回支付数据 (二维码/跳转链接)
用户 -> 支付平台: 8. 完成支付
支付平台 -> 支付模块: 9. 异步回调 (notify)
支付模块 -> 支付模块: 10. 验签 + 更新状态
支付模块 -> 业务系统: 11. 触发业务回调 (发货/开通)
```
### 2. 支付回调处理流程
```python
def handle_notify(gateway, data):
# 1. 加载对应的支付网关驱动
driver = PaymentFactory.create(gateway)
# 2. 验证签名
if not driver.verify_sign(data):
raise SignatureError("签名验证失败")
# 3. 解析回调数据
parsed = driver.parse_notify(data)
trade_sn = parsed['trade_sn']
# 4. 幂等性检查
trade = Trade.get_by_sn(trade_sn)
if trade.status == 'paid':
return driver.success_response() # 已处理过,直接返回成功
# 5. 金额校验
if parsed['amount'] != trade.cash_amount:
raise AmountMismatchError("金额不匹配")
# 6. 更新交易状态
trade.update({
'status': 'paid',
'platform_sn': parsed['platform_sn'],
'pay_time': parsed['pay_time'],
'notify_data': data
})
# 7. 更新订单状态
order = Order.get_by_sn(trade.order_sn)
order.update({'status': 'paid', 'paid_at': now()})
# 8. 触发业务回调
dispatch_event('order.paid', order)
# 9. 返回成功响应
return driver.success_response()
```
### 3. 退款流程
```python
def apply_refund(trade_sn, amount, reason):
trade = Trade.get_by_sn(trade_sn)
# 1. 状态检查
if trade.status != 'paid':
raise InvalidStatusError("只有已支付的交易可以退款")
# 2. 创建退款记录
refund = Refund.create({
'refund_sn': generate_refund_sn(),
'trade_sn': trade_sn,
'amount': amount,
'reason': reason,
'status': 'pending'
})
# 3. 调用平台退款接口
driver = PaymentFactory.create(trade.platform)
result = driver.refund({
'trade_sn': trade_sn,
'refund_sn': refund.refund_sn,
'amount': amount
})
# 4. 更新状态
if result.success:
refund.update({'status': 'success', 'refunded_at': now()})
trade.update({'status': 'refunded'})
else:
refund.update({'status': 'failed'})
return refund
```
---
## 🏭 工厂模式设计
```python
class PaymentFactory:
"""支付网关工厂"""
_drivers = {
'alipay': AlipayGateway,
'wechat': WechatGateway,
'paypal': PayPalGateway,
'stripe': StripeGateway,
'usdt': USDTGateway,
'coin': CoinGateway,
}
@classmethod
def create(cls, gateway: str) -> AbstractGateway:
gateway_name = gateway.split('_')[0] # wechat_jsapi -> wechat
if gateway_name not in cls._drivers:
raise ValueError(f"不支持的支付网关: {gateway}")
driver_class = cls._drivers[gateway_name]
return driver_class(config=get_payment_config(gateway_name))
```
```python
class AbstractGateway(ABC):
"""支付网关抽象基类"""
@abstractmethod
def create_trade(self, data: dict) -> dict:
"""创建交易"""
pass
@abstractmethod
def verify_sign(self, data: dict) -> bool:
"""验证签名"""
pass
@abstractmethod
def parse_notify(self, data: dict) -> dict:
"""解析回调数据"""
pass
@abstractmethod
def refund(self, data: dict) -> RefundResult:
"""发起退款"""
pass
@abstractmethod
def query_trade(self, trade_sn: str) -> dict:
"""查询交易"""
pass
@abstractmethod
def close_trade(self, trade_sn: str) -> bool:
"""关闭交易"""
pass
def success_response(self) -> str:
"""回调成功响应"""
return "success"
```
---
## 💰 金额处理规范
### 1. 存储规则
- 数据库统一使用**分**为单位 (BIGINT)
- 避免浮点数精度问题
### 2. 接口规则
- API 输入输出统一使用**元**为单位
- 内部转换: `分 = 元 × 100`
### 3. 转换示例
```python
# 元转分 (API输入 -> 数据库)
def yuan_to_fen(yuan: float) -> int:
return int(round(yuan * 100))
# 分转元 (数据库 -> API输出)
def fen_to_yuan(fen: int) -> float:
return round(fen / 100, 2)
```
---
## 🔐 幂等性设计
### 1. 订单创建幂等
- 使用 `(user_id, product_id, created_date)` 组合判断
- 或使用客户端传入的幂等键 `idempotency_key`
### 2. 支付回调幂等
- 检查交易状态,已支付则直接返回成功
- 使用数据库事务 + 行锁保证并发安全
### 3. 退款幂等
- 同一笔交易只能退款一次 (或限制总退款金额)

View File

@@ -0,0 +1,383 @@
# 支付安全与合规指南 (Security & Compliance) v4.0
> 支付系统安全最佳实践,保护你的资金和用户数据
## 🔐 密钥安全
### 1. 密钥存储原则
```
❌ 错误做法:
- 将密钥硬编码在代码中
- 将密钥提交到 Git 仓库
- 通过即时通讯工具传输密钥
- 使用弱密码作为 API Key
✅ 正确做法:
- 使用环境变量存储密钥
- 使用专业密钥管理服务 (AWS KMS, HashiCorp Vault)
- 定期轮换密钥
- 最小权限原则
```
### 2. .gitignore 必须包含
```gitignore
# 支付密钥相关
.env
.env.local
.env.*.local
*.pem
*.key
cert/
config/payment.yml
secrets/
```
### 3. 密钥轮换
- 定期更换 API 密钥 (建议每 90 天)
- 发现泄露立即作废并重新生成
- 保留旧密钥短暂过渡期
---
## 🔒 通信安全
### 1. HTTPS 强制
```nginx
# Nginx 配置示例
server {
listen 80;
server_name your-domain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name your-domain.com;
ssl_certificate /path/to/fullchain.pem;
ssl_certificate_key /path/to/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
# HSTS
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}
```
### 2. 证书管理
- 使用受信任的 CA 签发证书
- 定期检查证书有效期
- 推荐 Let's Encrypt 自动续期
---
## ✅ 签名验证
### 1. 支付宝签名验证
```python
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
import base64
def verify_alipay_sign(params: dict, sign: str, public_key: str) -> bool:
"""验证支付宝签名"""
# 1. 参数排序
sorted_params = sorted([(k, v) for k, v in params.items() if k != 'sign' and v])
# 2. 拼接待签名字符串
sign_str = '&'.join([f'{k}={v}' for k, v in sorted_params])
# 3. RSA2 验签
key = RSA.import_key(f"-----BEGIN PUBLIC KEY-----\n{public_key}\n-----END PUBLIC KEY-----")
verifier = PKCS1_v1_5.new(key)
hash_obj = SHA256.new(sign_str.encode('utf-8'))
try:
verifier.verify(hash_obj, base64.b64decode(sign))
return True
except (ValueError, TypeError):
return False
```
### 2. 微信签名验证
```python
import hashlib
def verify_wechat_sign(params: dict, sign: str, api_key: str) -> bool:
"""验证微信支付签名"""
# 1. 参数排序
sorted_params = sorted([(k, v) for k, v in params.items() if k != 'sign' and v])
# 2. 拼接待签名字符串
sign_str = '&'.join([f'{k}={v}' for k, v in sorted_params])
sign_str += f'&key={api_key}'
# 3. MD5 签名
calculated_sign = hashlib.md5(sign_str.encode('utf-8')).hexdigest().upper()
return calculated_sign == sign
```
---
## 💰 金额校验
### 1. 回调金额必须验证
```python
def handle_payment_notify(trade_sn: str, paid_amount: int):
"""处理支付回调时必须验证金额"""
trade = get_trade_by_sn(trade_sn)
# 金额必须严格匹配
if paid_amount != trade.cash_amount:
log.error(f"金额不匹配! 订单:{trade.cash_amount}, 回调:{paid_amount}")
raise AmountMismatchError()
# 继续处理...
```
### 2. 防止金额篡改
```python
# 前端传入的金额仅用于展示,实际金额从后端订单读取
def checkout(order_sn: str, gateway: str):
order = get_order(order_sn)
# 金额从数据库读取,不信任前端
amount = order.pay_amount
return create_trade(order_sn, amount, gateway)
```
---
## 🛡️ 回调安全
### 1. IP 白名单
```python
# 支付平台回调 IP 白名单
PAYMENT_IP_WHITELIST = {
'alipay': [
'110.75.0.0/16',
'203.209.0.0/16'
],
'wechat': [
'101.226.0.0/16',
'140.207.0.0/16'
]
}
def verify_callback_ip(gateway: str, client_ip: str) -> bool:
"""验证回调来源 IP"""
import ipaddress
whitelist = PAYMENT_IP_WHITELIST.get(gateway, [])
client = ipaddress.ip_address(client_ip)
for cidr in whitelist:
if client in ipaddress.ip_network(cidr):
return True
return False
```
### 2. 防重放攻击
```python
import time
def check_notify_timestamp(timestamp: int) -> bool:
"""检查回调时间戳,防止重放攻击"""
now = int(time.time())
# 允许 5 分钟的时间差
if abs(now - timestamp) > 300:
log.warning(f"回调时间戳异常: {timestamp}")
return False
return True
```
### 3. 幂等性处理
```python
def process_notify_idempotent(trade_sn: str, notify_data: dict):
"""幂等性处理回调"""
# 使用分布式锁
lock_key = f"payment_notify:{trade_sn}"
with redis_lock(lock_key, timeout=10):
trade = get_trade_by_sn(trade_sn)
# 已处理过,直接返回成功
if trade.status == 'paid':
return success_response()
# 处理支付成功逻辑
update_trade_to_paid(trade, notify_data)
return success_response()
```
---
## 📝 日志审计
### 1. 必须记录的日志
```python
import logging
payment_logger = logging.getLogger('payment')
# 创建交易日志
payment_logger.info(f"创建交易 | trade_sn={trade_sn} | order_sn={order_sn} | amount={amount} | gateway={gateway}")
# 回调日志
payment_logger.info(f"收到回调 | gateway={gateway} | trade_sn={trade_sn} | raw_data={raw_data[:500]}")
# 签名验证日志
payment_logger.info(f"签名验证 | trade_sn={trade_sn} | result={verify_result}")
# 状态变更日志
payment_logger.info(f"状态变更 | trade_sn={trade_sn} | from={old_status} | to={new_status}")
# 退款日志
payment_logger.info(f"发起退款 | refund_sn={refund_sn} | trade_sn={trade_sn} | amount={amount}")
```
### 2. 敏感信息脱敏
```python
def mask_sensitive(data: dict) -> dict:
"""敏感信息脱敏"""
sensitive_keys = ['card_no', 'id_card', 'phone', 'bank_account']
masked = data.copy()
for key in sensitive_keys:
if key in masked:
value = str(masked[key])
if len(value) > 4:
masked[key] = value[:2] + '*' * (len(value) - 4) + value[-2:]
return masked
```
---
## 🚨 异常处理
### 1. 支付异常分类
```python
class PaymentError(Exception):
"""支付基础异常"""
pass
class SignatureError(PaymentError):
"""签名验证失败"""
pass
class AmountMismatchError(PaymentError):
"""金额不匹配"""
pass
class OrderExpiredError(PaymentError):
"""订单已过期"""
pass
class DuplicatePaymentError(PaymentError):
"""重复支付"""
pass
class RefundError(PaymentError):
"""退款失败"""
pass
```
### 2. 统一异常处理
```python
@app.exception_handler(PaymentError)
async def payment_exception_handler(request, exc):
return JSONResponse(
status_code=400,
content={
"code": 400,
"message": str(exc),
"data": None
}
)
```
---
## ✅ 合规要求
### 1. PCI DSS 合规 (信用卡)
- 不存储完整卡号、CVV、PIN
- 使用 Stripe/PayPal 等符合 PCI DSS 的支付网关
- 定期安全评估
### 2. GDPR 合规 (欧盟用户)
- 明确告知用户数据用途
- 提供数据删除功能
- 用户同意授权
### 3. 中国支付合规
- 接入持牌支付机构
- 实名认证
- 交易限额管理
---
## 📋 安全检查清单
```markdown
## 上线前安全检查
### 密钥管理
- [ ] 所有密钥通过环境变量配置
- [ ] 密钥未提交到代码仓库
- [ ] 生产环境密钥与测试环境隔离
### 通信安全
- [ ] 启用 HTTPS
- [ ] 证书有效且受信任
- [ ] 启用 HSTS
### 签名验证
- [ ] 所有回调验签
- [ ] 验签失败拒绝处理
### 金额校验
- [ ] 回调金额与订单金额比对
- [ ] 金额从后端读取
### 日志审计
- [ ] 关键操作有日志
- [ ] 敏感信息脱敏
### 异常处理
- [ ] 异常不泄露敏感信息
- [ ] 有统一异常处理
### 回调安全
- [ ] IP 白名单验证 (可选)
- [ ] 幂等性处理
- [ ] 防重放攻击
```

View File

@@ -0,0 +1,133 @@
# ============================================================================
# 全球支付模块标准配置模板 (Universal Payment Config Template) v4.0
# ============================================================================
# 适用于: Python, Node.js, Go, Java, PHP 等任意后端语言
# 使用方法: 将此配置映射到你项目的环境变量 (.env, config.py, application.yml)
# ============================================================================
# ----------------------------------------------------------------------------
# 1. 基础环境 (Environment)
# ----------------------------------------------------------------------------
APP_ENV: "production" # development / staging / production
APP_NAME: "MyApp" # 应用名称 (用于日志/标题)
APP_URL: "https://your-site.com" # 你的网站域名 (用于回调地址生成)
APP_CURRENCY: "CNY" # 默认货币: CNY, USD, EUR
# ----------------------------------------------------------------------------
# 2. 数据库 (Database) - 存储订单和交易流水
# ----------------------------------------------------------------------------
DB_CONNECTION: "mysql" # mysql / postgres / mongodb / sqlite
DB_HOST: "127.0.0.1"
DB_PORT: "3306"
DB_DATABASE: "payment_db"
DB_USERNAME: "root"
DB_PASSWORD: "your_password"
# 自动创建的表:
# - orders (订单表)
# - pay_trades (交易流水表)
# - cashflows (资金流水表)
# ----------------------------------------------------------------------------
# 3. 支付宝 (Alipay) - 中国市场
# ----------------------------------------------------------------------------
ALIPAY_ENABLED: true
ALIPAY_MODE: "production" # sandbox / production
ALIPAY_APP_ID: "" # 开放平台应用 AppID
ALIPAY_PID: "" # 商户 PID (合作伙伴ID)
ALIPAY_SELLER_EMAIL: "" # 收款支付宝账号
ALIPAY_PRIVATE_KEY: "" # 商户私钥 (RSA2)
ALIPAY_PUBLIC_KEY: "" # 支付宝公钥
ALIPAY_MD5_KEY: "" # MD5 密钥 (旧版接口)
# 回调地址 (系统自动拼接 APP_URL)
# 同步回调: ${APP_URL}/api/payment/return/alipay
# 异步回调: ${APP_URL}/api/payment/notify/alipay
# ----------------------------------------------------------------------------
# 4. 微信支付 (Wechat Pay) - 中国市场
# ----------------------------------------------------------------------------
WECHAT_ENABLED: true
WECHAT_MODE: "production" # sandbox / production
# 公众号/网站支付
WECHAT_APPID: "" # 公众号/网站 AppID
WECHAT_APP_SECRET: "" # AppSecret
# 服务号 (如果有独立服务号)
WECHAT_SERVICE_APPID: "" # 服务号 AppID
WECHAT_SERVICE_SECRET: "" # 服务号 AppSecret
# 商户信息
WECHAT_MCH_ID: "" # 商户号
WECHAT_MCH_KEY: "" # 商户平台 API 密钥 (32位)
WECHAT_MCH_KEY_V3: "" # APIv3 密钥 (如使用v3接口)
# 证书路径 (相对于项目根目录)
WECHAT_CERT_PATH: "./cert/wechat/apiclient_cert.pem"
WECHAT_KEY_PATH: "./cert/wechat/apiclient_key.pem"
WECHAT_CERT_SERIAL: "" # 证书序列号 (v3接口需要)
# 小程序 (如果有)
WECHAT_MINI_APPID: "" # 小程序 AppID
WECHAT_MINI_SECRET: "" # 小程序 AppSecret
# 回调地址
# 异步回调: ${APP_URL}/api/payment/notify/wechat
# ----------------------------------------------------------------------------
# 5. PayPal - 全球市场
# ----------------------------------------------------------------------------
PAYPAL_ENABLED: true
PAYPAL_MODE: "live" # sandbox / live
PAYPAL_CLIENT_ID: "" # Client ID
PAYPAL_CLIENT_SECRET: "" # Client Secret
PAYPAL_WEBHOOK_ID: "" # Webhook ID (用于验证回调)
# 回调地址: ${APP_URL}/api/payment/notify/paypal
# ----------------------------------------------------------------------------
# 6. Stripe - 全球市场
# ----------------------------------------------------------------------------
STRIPE_ENABLED: true
STRIPE_MODE: "live" # test / live
STRIPE_PUBLIC_KEY: "" # pk_live_xxx 或 pk_test_xxx
STRIPE_SECRET_KEY: "" # sk_live_xxx 或 sk_test_xxx
STRIPE_WEBHOOK_SECRET: "" # whsec_xxx
# 回调地址: ${APP_URL}/api/payment/notify/stripe
# ----------------------------------------------------------------------------
# 7. USDT (加密货币) - Web3 / 抗审查支付
# ----------------------------------------------------------------------------
USDT_ENABLED: false
USDT_GATEWAY_TYPE: "nowpayments" # nowpayments / native
# 选项 A: NOWPayments (第三方托管)
NOWPAYMENTS_API_KEY: ""
NOWPAYMENTS_IPN_SECRET: ""
# 选项 B: Native (原生 TRC20 监听)
TRON_NODE_API: "https://api.trongrid.io"
TRON_WALLET_ADDRESS: "" # 你的 USDT-TRC20 收款地址
TRON_API_KEY: "" # TronGrid API Key
TRON_CHECK_INTERVAL: 60 # 轮询间隔 (秒)
# ----------------------------------------------------------------------------
# 8. 高级配置 (Advanced)
# ----------------------------------------------------------------------------
# 虚拟币/积分系统
COIN_ENABLED: false # 是否启用虚拟币抵扣
COIN_RATE: 100 # 1元 = 100虚拟币
# 订单配置
ORDER_EXPIRE_MINUTES: 30 # 订单过期时间 (分钟)
TRADE_SN_PREFIX: "T" # 交易流水号前缀
# 日志配置
PAYMENT_LOG_LEVEL: "info" # debug / info / warning / error
PAYMENT_LOG_PATH: "./logs/payment.log"
# 安全配置
PAYMENT_IP_WHITELIST: "" # 回调IP白名单 (逗号分隔)
PAYMENT_SIGN_TYPE: "RSA2" # 签名类型: RSA2, MD5