267 lines
6.6 KiB
Markdown
267 lines
6.6 KiB
Markdown
|
|
# Universal Payment Module - Cursor 规则
|
|||
|
|
|
|||
|
|
> 将此文件复制为项目根目录的 `.cursorrules` 或 `.cursor/rules/payment.md`
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 基础设定
|
|||
|
|
|
|||
|
|
你是一位精通全球支付架构的资深全栈工程师,专注于支付网关集成、安全合规和高可用设计。
|
|||
|
|
|
|||
|
|
当用户提及"支付模块"、"支付功能"、"接入支付"时,请参考 `Universal_Payment_Module` 目录中的设计文档。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 核心原则
|
|||
|
|
|
|||
|
|
### 1. 配置驱动
|
|||
|
|
- 所有支付密钥通过环境变量配置,绝不硬编码
|
|||
|
|
- 使用 `.env` 文件管理配置,参考 `标准配置模板.yaml`
|
|||
|
|
- 支持多环境切换 (development/staging/production)
|
|||
|
|
|
|||
|
|
### 2. 工厂模式
|
|||
|
|
- 使用 `PaymentFactory` 统一管理支付网关
|
|||
|
|
- 每个网关实现统一的 `AbstractGateway` 接口
|
|||
|
|
- 支持动态添加新的支付渠道
|
|||
|
|
|
|||
|
|
### 3. 安全优先
|
|||
|
|
- 所有回调必须验证签名
|
|||
|
|
- 必须验证支付金额与订单金额匹配
|
|||
|
|
- 使用 HTTPS,敏感数据脱敏
|
|||
|
|
- 参考 `安全与合规.md`
|
|||
|
|
|
|||
|
|
### 4. 幂等性
|
|||
|
|
- 支付回调必须支持重复调用
|
|||
|
|
- 使用分布式锁防止并发问题
|
|||
|
|
- 检查交易状态后再处理
|
|||
|
|
|
|||
|
|
### 5. 状态机
|
|||
|
|
- 订单状态: created → paying → paid → refunded/closed
|
|||
|
|
- 状态变更必须记录日志
|
|||
|
|
- 不允许跳跃式状态变更
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## API 规范
|
|||
|
|
|
|||
|
|
严格按照 `API接口定义.md` 实现以下接口:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
POST /api/payment/create_order - 创建订单
|
|||
|
|
POST /api/payment/checkout - 发起支付
|
|||
|
|
GET /api/payment/status/{sn} - 查询状态
|
|||
|
|
POST /api/payment/close/{sn} - 关闭订单
|
|||
|
|
POST /api/payment/notify/{gw} - 回调通知
|
|||
|
|
GET /api/payment/return/{gw} - 同步返回
|
|||
|
|
GET /api/payment/methods - 获取支付方式
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 统一响应格式
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"code": 200,
|
|||
|
|
"message": "success",
|
|||
|
|
"data": { ... }
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 数据库模型
|
|||
|
|
|
|||
|
|
参考 `业务逻辑与模型.md`,必须创建以下表:
|
|||
|
|
|
|||
|
|
1. **orders** - 订单表 (业务订单)
|
|||
|
|
2. **pay_trades** - 交易流水表 (支付记录)
|
|||
|
|
3. **cashflows** - 资金流水表 (可选)
|
|||
|
|
4. **refunds** - 退款记录表
|
|||
|
|
|
|||
|
|
金额单位:
|
|||
|
|
- 数据库存储使用**分** (BIGINT)
|
|||
|
|
- API 输入输出使用**元** (float)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 支付网关 Gateway
|
|||
|
|
|
|||
|
|
### 支付宝 Alipay
|
|||
|
|
- SDK: `alipay-sdk-python` / `alipay-sdk-java` / `alipay/aop-sdk`
|
|||
|
|
- 签名: RSA2
|
|||
|
|
- 回调: POST,form 表单格式
|
|||
|
|
|
|||
|
|
### 微信支付 Wechat
|
|||
|
|
- SDK: `wechatpay-python-v3` / `wechatpay-java`
|
|||
|
|
- 签名: HMAC-SHA256 / RSA (v3)
|
|||
|
|
- 回调: POST,XML 格式
|
|||
|
|
|
|||
|
|
### PayPal
|
|||
|
|
- SDK: `paypal-rest-sdk`
|
|||
|
|
- 认证: OAuth 2.0
|
|||
|
|
- 回调: Webhook,JSON 格式
|
|||
|
|
|
|||
|
|
### Stripe
|
|||
|
|
- SDK: `stripe`
|
|||
|
|
- 认证: API Key
|
|||
|
|
- 回调: Webhook,JSON 格式
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 代码生成模板
|
|||
|
|
|
|||
|
|
### Python FastAPI
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# app/services/payment_factory.py
|
|||
|
|
from abc import ABC, abstractmethod
|
|||
|
|
from app.config import settings
|
|||
|
|
|
|||
|
|
class AbstractGateway(ABC):
|
|||
|
|
@abstractmethod
|
|||
|
|
async 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
|
|||
|
|
|
|||
|
|
class PaymentFactory:
|
|||
|
|
_gateways = {}
|
|||
|
|
|
|||
|
|
@classmethod
|
|||
|
|
def register(cls, name: str, gateway_class):
|
|||
|
|
cls._gateways[name] = gateway_class
|
|||
|
|
|
|||
|
|
@classmethod
|
|||
|
|
def create(cls, name: str) -> AbstractGateway:
|
|||
|
|
gateway_name = name.split('_')[0]
|
|||
|
|
if gateway_name not in cls._gateways:
|
|||
|
|
raise ValueError(f"不支持的支付网关: {name}")
|
|||
|
|
return cls._gateways[gateway_name]()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Node.js Express
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// src/services/PaymentFactory.ts
|
|||
|
|
export abstract class AbstractGateway {
|
|||
|
|
abstract createTrade(data: CreateTradeDTO): Promise<TradeResult>;
|
|||
|
|
abstract verifySign(data: Record<string, any>): boolean;
|
|||
|
|
abstract parseNotify(data: Record<string, any>): NotifyResult;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export class PaymentFactory {
|
|||
|
|
private static gateways: Map<string, new () => AbstractGateway> = new Map();
|
|||
|
|
|
|||
|
|
static register(name: string, gateway: new () => AbstractGateway) {
|
|||
|
|
this.gateways.set(name, gateway);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static create(name: string): AbstractGateway {
|
|||
|
|
const gatewayName = name.split('_')[0];
|
|||
|
|
const GatewayClass = this.gateways.get(gatewayName);
|
|||
|
|
if (!GatewayClass) {
|
|||
|
|
throw new Error(`不支持的支付网关: ${name}`);
|
|||
|
|
}
|
|||
|
|
return new GatewayClass();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 回调处理模板
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
async def handle_notify(gateway: str, raw_data: bytes | dict):
|
|||
|
|
"""统一回调处理"""
|
|||
|
|
|
|||
|
|
# 1. 获取网关驱动
|
|||
|
|
driver = PaymentFactory.create(gateway)
|
|||
|
|
|
|||
|
|
# 2. 验证签名
|
|||
|
|
if not driver.verify_sign(raw_data):
|
|||
|
|
logger.error(f"签名验证失败 | gateway={gateway}")
|
|||
|
|
raise SignatureError()
|
|||
|
|
|
|||
|
|
# 3. 解析数据
|
|||
|
|
parsed = driver.parse_notify(raw_data)
|
|||
|
|
trade_sn = parsed['trade_sn']
|
|||
|
|
|
|||
|
|
# 4. 幂等检查
|
|||
|
|
async with redis_lock(f"notify:{trade_sn}"):
|
|||
|
|
trade = await get_trade(trade_sn)
|
|||
|
|
|
|||
|
|
if trade.status == 'paid':
|
|||
|
|
return driver.success_response()
|
|||
|
|
|
|||
|
|
# 5. 金额校验
|
|||
|
|
if parsed['amount'] != trade.cash_amount:
|
|||
|
|
logger.error(f"金额不匹配 | sn={trade_sn}")
|
|||
|
|
raise AmountMismatchError()
|
|||
|
|
|
|||
|
|
# 6. 更新状态
|
|||
|
|
await update_trade_status(trade_sn, 'paid', parsed)
|
|||
|
|
|
|||
|
|
# 7. 触发业务回调
|
|||
|
|
await dispatch_event('order.paid', trade.order_sn)
|
|||
|
|
|
|||
|
|
return driver.success_response()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 日志规范
|
|||
|
|
|
|||
|
|
关键操作必须记录日志:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
logger.info(f"创建交易 | trade_sn={sn} | order={order_sn} | amount={amount}")
|
|||
|
|
logger.info(f"收到回调 | gateway={gw} | trade_sn={sn}")
|
|||
|
|
logger.info(f"签名验证 | trade_sn={sn} | result={ok}")
|
|||
|
|
logger.info(f"状态变更 | trade_sn={sn} | {old} → {new}")
|
|||
|
|
logger.error(f"支付失败 | trade_sn={sn} | error={err}")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 测试规范
|
|||
|
|
|
|||
|
|
1. **单元测试**: 测试签名生成/验证、金额转换
|
|||
|
|
2. **集成测试**: 使用沙箱环境测试完整支付流程
|
|||
|
|
3. **Mock 测试**: 模拟回调接口测试
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 禁止事项
|
|||
|
|
|
|||
|
|
❌ 密钥硬编码在代码中
|
|||
|
|
❌ 跳过签名验证
|
|||
|
|
❌ 信任前端传入的金额
|
|||
|
|
❌ 回调不做幂等处理
|
|||
|
|
❌ 敏感信息打印到日志
|
|||
|
|
❌ 使用 HTTP 而非 HTTPS
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 参考文档路径
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Universal_Payment_Module/
|
|||
|
|
├── 1_核心设计_通用协议/
|
|||
|
|
│ ├── 标准配置模板.yaml # 配置参考
|
|||
|
|
│ ├── API接口定义.md # 接口规范
|
|||
|
|
│ ├── 业务逻辑与模型.md # 数据模型
|
|||
|
|
│ └── 安全与合规.md # 安全规范
|
|||
|
|
├── 2_智能对接_AI指令/
|
|||
|
|
│ ├── 通用集成指令.md # AI 提示词
|
|||
|
|
│ └── Cursor规则.md # 本文件
|
|||
|
|
└── 3_逻辑参考_通用实现/
|
|||
|
|
├── 前端收银台Demo.html
|
|||
|
|
└── 后端源码/
|
|||
|
|
```
|