mirror of
https://gitee.com/technical-laohu/mpay_v2_webman.git
synced 2026-04-22 18:14:27 +08:00
codex基础代码更新
This commit is contained in:
@@ -1,145 +0,0 @@
|
||||
# 认证策略设计说明
|
||||
|
||||
## 设计理念
|
||||
|
||||
采用**策略模式**替代中间件方式处理认证,具有以下优势:
|
||||
|
||||
1. **灵活扩展**:可以轻松添加新的接口标准(如易支付、OpenAPI、自定义标准等)
|
||||
2. **按需使用**:控制器可以根据需要选择认证策略,而不是在路由层面强制
|
||||
3. **易于测试**:策略类可以独立测试,不依赖中间件
|
||||
4. **代码复用**:不同接口可以共享相同的认证逻辑
|
||||
|
||||
## 架构设计
|
||||
|
||||
### 1. 核心接口
|
||||
|
||||
**`AuthStrategyInterface`** - 认证策略接口
|
||||
```php
|
||||
interface AuthStrategyInterface
|
||||
{
|
||||
public function authenticate(Request $request): MerchantApp;
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 策略实现
|
||||
|
||||
#### EpayAuthStrategy(易支付认证)
|
||||
- 使用 `pid` + `key` + `MD5签名`
|
||||
- 参数格式:`application/x-www-form-urlencoded`
|
||||
- 签名算法:MD5(排序后的参数字符串 + KEY)
|
||||
|
||||
#### OpenApiAuthStrategy(OpenAPI认证)
|
||||
- 使用 `app_id` + `timestamp` + `nonce` + `HMAC-SHA256签名`
|
||||
- 支持请求头或参数传递
|
||||
- 签名算法:HMAC-SHA256(签名字符串, app_secret)
|
||||
|
||||
### 3. 认证服务
|
||||
|
||||
**`AuthService`** - 认证服务,负责:
|
||||
- 自动检测接口标准类型
|
||||
- 根据类型选择对应的认证策略
|
||||
- 支持手动注册新的认证策略
|
||||
|
||||
```php
|
||||
// 自动检测
|
||||
$app = $authService->authenticate($request);
|
||||
|
||||
// 指定策略类型
|
||||
$app = $authService->authenticate($request, 'epay');
|
||||
|
||||
// 注册新策略
|
||||
$authService->registerStrategy('custom', CustomAuthStrategy::class);
|
||||
```
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 控制器中使用
|
||||
|
||||
```php
|
||||
class PayController extends BaseController
|
||||
{
|
||||
public function __construct(
|
||||
protected PayOrderService $payOrderService,
|
||||
protected AuthService $authService
|
||||
) {
|
||||
}
|
||||
|
||||
public function submit(Request $request)
|
||||
{
|
||||
// 自动检测或指定策略类型
|
||||
$app = $this->authService->authenticate($request, 'epay');
|
||||
|
||||
// 使用 $app 进行后续业务处理
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 添加新的认证策略
|
||||
|
||||
1. **实现策略接口**
|
||||
```php
|
||||
class CustomAuthStrategy implements AuthStrategyInterface
|
||||
{
|
||||
public function authenticate(Request $request): MerchantApp
|
||||
{
|
||||
// 实现自定义认证逻辑
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. **注册策略**
|
||||
```php
|
||||
// 在服务提供者或启动文件中
|
||||
$authService = new AuthService();
|
||||
$authService->registerStrategy('custom', CustomAuthStrategy::class);
|
||||
```
|
||||
|
||||
3. **在控制器中使用**
|
||||
```php
|
||||
$app = $this->authService->authenticate($request, 'custom');
|
||||
```
|
||||
|
||||
## 自动检测机制
|
||||
|
||||
`AuthService` 会根据请求特征自动检测接口标准:
|
||||
|
||||
- **易支付**:检测到 `pid` 参数
|
||||
- **OpenAPI**:检测到 `X-App-Id` 请求头或 `app_id` 参数
|
||||
|
||||
如果无法自动检测,可以手动指定策略类型。
|
||||
|
||||
## 优势对比
|
||||
|
||||
### 中间件方式(旧方案)
|
||||
- ❌ 路由配置复杂,每个接口标准需要不同的中间件
|
||||
- ❌ 难以在同一路由支持多种认证方式
|
||||
- ❌ 扩展新标准需要修改路由配置
|
||||
|
||||
### 策略模式(新方案)
|
||||
- ✅ 控制器按需选择认证策略
|
||||
- ✅ 同一路由可以支持多种认证方式(通过参数区分)
|
||||
- ✅ 扩展新标准只需实现策略接口并注册
|
||||
- ✅ 代码更清晰,职责分离
|
||||
|
||||
## 路由配置
|
||||
|
||||
由于不再使用中间件,路由配置更简洁:
|
||||
|
||||
```php
|
||||
// 易支付接口
|
||||
Route::any('/submit.php', [PayController::class, 'submit']);
|
||||
Route::post('/mapi.php', [PayController::class, 'mapi']);
|
||||
Route::get('/api.php', [PayController::class, 'queryOrder']);
|
||||
|
||||
// 所有接口都在控制器内部进行认证,无需中间件
|
||||
```
|
||||
|
||||
## 总结
|
||||
|
||||
通过策略模式重构认证逻辑,系统具备了:
|
||||
- **高扩展性**:轻松添加新的接口标准
|
||||
- **高灵活性**:控制器可以自由选择认证方式
|
||||
- **高可维护性**:代码结构清晰,易于理解和维护
|
||||
|
||||
@@ -1,214 +0,0 @@
|
||||
# 支付订单表设计说明
|
||||
|
||||
## 一、订单表设计原因
|
||||
|
||||
### 1.1 订单号设计(双重订单号)
|
||||
|
||||
**系统订单号 (`pay_order_id`)**
|
||||
- **作用**:系统内部唯一标识,用于查询、对账、退款等操作
|
||||
- **生成规则**:`P` + `YYYYMMDDHHmmss` + `6位随机数`(如:P20240101120000123456)
|
||||
- **唯一性**:通过 `uk_pay_order_id` 唯一索引保证
|
||||
- **优势**:
|
||||
- 全局唯一,不受商户影响
|
||||
- 便于系统内部查询和关联
|
||||
- 对账时作为主键
|
||||
|
||||
**商户订单号 (`mch_order_no`)**
|
||||
- **作用**:商户传入的订单号,用于幂等性校验
|
||||
- **唯一性**:通过 `uk_mch_order_no(merchant_id, mch_order_no)` 联合唯一索引保证
|
||||
- **优势**:
|
||||
- 同一商户下订单号唯一,防止重复提交
|
||||
- 商户侧可以自定义订单号规则
|
||||
- 支持商户订单号查询订单
|
||||
|
||||
**为什么需要两个订单号?**
|
||||
- 系统订单号:保证全局唯一,便于系统内部管理
|
||||
- 商户订单号:保证商户侧唯一,防止重复支付(幂等性)
|
||||
|
||||
### 1.2 关联关系设计
|
||||
|
||||
**商户与应用关联 (`merchant_id` + `app_id`)**
|
||||
- **作用**:标识订单所属商户和应用
|
||||
- **用途**:
|
||||
- 权限控制(商户只能查询自己的订单)
|
||||
- 对账统计(按商户/应用维度)
|
||||
- 通知路由(根据应用配置的通知地址)
|
||||
|
||||
**支付通道关联 (`channel_id`)**
|
||||
- **作用**:记录实际使用的支付通道
|
||||
- **用途**:
|
||||
- 退款时找到对应的插件和配置
|
||||
- 对账时关联通道信息
|
||||
- 统计通道使用情况
|
||||
|
||||
**支付方式与产品 (`method_code` + `product_code`)**
|
||||
- **method_code**:支付方式(alipay/wechat/unionpay)
|
||||
- 用于统计、筛选、报表
|
||||
- **product_code**:支付产品(alipay_h5/alipay_life/wechat_jsapi等)
|
||||
- 由插件根据用户环境自动选择
|
||||
- 用于记录实际使用的支付产品
|
||||
|
||||
### 1.3 金额字段设计
|
||||
|
||||
**订单金额 (`amount`)**
|
||||
- 商户实际收款金额(扣除手续费前)
|
||||
- 用于退款金额校验、对账
|
||||
|
||||
**手续费 (`fee`)**
|
||||
- 可选字段,记录通道手续费
|
||||
- 用于对账、结算、利润统计
|
||||
- 如果不需要详细记录手续费,可以留空或通过 `extra` 存储
|
||||
|
||||
**币种 (`currency`)**
|
||||
- 默认 CNY,支持国际化扩展
|
||||
- 预留字段,便于后续支持多币种
|
||||
|
||||
### 1.4 状态流转设计
|
||||
|
||||
```
|
||||
PENDING(待支付)
|
||||
├─> SUCCESS(支付成功)← 收到渠道回调并验签通过
|
||||
├─> FAIL(支付失败)← 用户取消、超时、渠道返回失败
|
||||
└─> CLOSED(已关闭)← 全额退款后
|
||||
```
|
||||
|
||||
**状态说明**:
|
||||
- **PENDING**:订单创建后,等待用户支付
|
||||
- **SUCCESS**:支付成功,已收到渠道回调并验签通过
|
||||
- **FAIL**:支付失败(用户取消、订单超时、渠道返回失败等)
|
||||
- **CLOSED**:已关闭(全额退款后)
|
||||
|
||||
### 1.5 渠道信息设计
|
||||
|
||||
**渠道订单号 (`channel_order_no`)**
|
||||
- 渠道返回的订单号
|
||||
- 用于查询订单状态、退款等操作
|
||||
|
||||
**渠道交易号 (`channel_trade_no`)**
|
||||
- 部分渠道有交易号概念(如支付宝的 trade_no)
|
||||
- 用于对账、查询等
|
||||
|
||||
### 1.6 通知机制设计
|
||||
|
||||
**通知状态 (`notify_status`)**
|
||||
- 0:未通知
|
||||
- 1:已通知成功
|
||||
|
||||
**通知次数 (`notify_count`)**
|
||||
- 记录通知次数,用于重试控制
|
||||
- 配合 `ma_notify_task` 表实现异步通知
|
||||
|
||||
### 1.7 扩展性设计
|
||||
|
||||
**扩展字段 (`extra`)**
|
||||
- JSON 格式,存储:
|
||||
- 支付参数(`pay_params`):前端支付所需的参数
|
||||
- 退款信息(`refund_info`):退款结果
|
||||
- 自定义字段:业务扩展字段
|
||||
|
||||
**订单过期时间 (`expire_time`)**
|
||||
- 用于自动关闭超时订单
|
||||
- 默认 30 分钟,可配置
|
||||
|
||||
## 二、索引设计说明
|
||||
|
||||
### 2.1 唯一索引
|
||||
|
||||
- **`uk_pay_order_id`**:保证系统订单号唯一
|
||||
- **`uk_mch_order_no(merchant_id, mch_order_no)`**:保证同一商户下商户订单号唯一(幂等性)
|
||||
|
||||
### 2.2 普通索引
|
||||
|
||||
- **`idx_merchant_app(merchant_id, app_id)`**:商户/应用维度查询
|
||||
- **`idx_channel_id`**:通道维度查询
|
||||
- **`idx_method_code`**:支付方式维度统计
|
||||
- **`idx_status`**:状态筛选
|
||||
- **`idx_pay_time`**:按支付时间查询(对账、统计)
|
||||
- **`idx_created_at`**:按创建时间查询(分页、统计)
|
||||
|
||||
## 三、可能遗漏的字段(后续扩展)
|
||||
|
||||
### 3.1 退款相关字段
|
||||
|
||||
如果后续需要支持**部分退款**或**多次退款**,可以考虑添加:
|
||||
|
||||
```sql
|
||||
`refund_amount` decimal(10,2) NOT NULL DEFAULT 0.00 COMMENT '已退款金额(累计)',
|
||||
`refund_status` varchar(20) NOT NULL DEFAULT '' COMMENT '退款状态:PENDING-退款中, SUCCESS-退款成功, FAIL-退款失败',
|
||||
`refund_time` datetime DEFAULT NULL COMMENT '最后退款时间',
|
||||
```
|
||||
|
||||
**当前设计**:
|
||||
- 退款信息存储在 `extra['refund_info']` 中
|
||||
- 全额退款后订单状态改为 `CLOSED`
|
||||
- 如果只需要全额退款,当前设计已足够
|
||||
|
||||
### 3.2 结算相关字段
|
||||
|
||||
如果后续需要**分账/结算**功能,可以考虑添加:
|
||||
|
||||
```sql
|
||||
`settlement_status` varchar(20) NOT NULL DEFAULT '' COMMENT '结算状态:PENDING-待结算, SUCCESS-已结算, FAIL-结算失败',
|
||||
`settlement_time` datetime DEFAULT NULL COMMENT '结算时间',
|
||||
`settlement_amount` decimal(10,2) NOT NULL DEFAULT 0.00 COMMENT '结算金额',
|
||||
```
|
||||
|
||||
**当前设计**:
|
||||
- 结算信息可以通过 `extra` 存储
|
||||
- 如果不需要复杂的结算流程,当前设计已足够
|
||||
|
||||
### 3.3 风控相关字段
|
||||
|
||||
如果需要**风控功能**,可以考虑添加:
|
||||
|
||||
```sql
|
||||
`risk_level` varchar(20) NOT NULL DEFAULT '' COMMENT '风险等级:LOW-低, MEDIUM-中, HIGH-高',
|
||||
`risk_score` int(11) NOT NULL DEFAULT 0 COMMENT '风险评分',
|
||||
`risk_reason` varchar(255) NOT NULL DEFAULT '' COMMENT '风险原因',
|
||||
```
|
||||
|
||||
**当前设计**:
|
||||
- 风控信息可以通过 `extra` 存储
|
||||
- 如果不需要复杂的风控系统,当前设计已足够
|
||||
|
||||
### 3.4 其他扩展字段
|
||||
|
||||
- **`user_id`**:用户ID(如果需要关联用户)
|
||||
- **`device_info`**:设备信息(用于风控)
|
||||
- **`remark`**:备注(管理员备注)
|
||||
- **`close_reason`**:关闭原因(用户取消/超时/管理员关闭等)
|
||||
|
||||
## 四、设计原则总结
|
||||
|
||||
1. **幂等性**:通过 `uk_mch_order_no` 保证同一商户下订单号唯一
|
||||
2. **可追溯性**:记录完整的订单信息、渠道信息、时间信息
|
||||
3. **可扩展性**:通过 `extra` JSON 字段存储扩展信息
|
||||
4. **性能优化**:合理的索引设计,支持常见查询场景
|
||||
5. **业务完整性**:覆盖订单全生命周期(创建→支付→退款→关闭)
|
||||
|
||||
## 五、与代码的对应关系
|
||||
|
||||
| SQL 字段 | 代码字段 | 说明 |
|
||||
|---------|---------|------|
|
||||
| `pay_order_id` | `pay_order_id` | 系统订单号 |
|
||||
| `merchant_id` | `merchant_id` | 商户ID |
|
||||
| `app_id` | `app_id` | 应用ID |
|
||||
| `mch_order_no` | `mch_order_no` | 商户订单号 |
|
||||
| `method_code` | `method_code` | 支付方式 |
|
||||
| `product_code` | `product_code` | 支付产品 |
|
||||
| `channel_id` | `channel_id` | 通道ID |
|
||||
| `amount` | `amount` | 订单金额 |
|
||||
| `currency` | `currency` | 币种 |
|
||||
| `status` | `status` | 订单状态 |
|
||||
| `channel_order_no` | `channel_order_no` | 渠道订单号 |
|
||||
| `channel_trade_no` | `channel_trade_no` | 渠道交易号 |
|
||||
| `extra` | `extra` | 扩展字段(JSON) |
|
||||
|
||||
## 六、注意事项
|
||||
|
||||
1. **字段命名统一**:SQL 和代码中的字段名必须一致
|
||||
2. **索引维护**:定期检查索引使用情况,优化慢查询
|
||||
3. **数据归档**:历史订单数据量大时,考虑归档策略
|
||||
4. **JSON 字段**:`extra` 字段使用 JSON 类型,便于扩展但查询性能略低
|
||||
5. **时间字段**:`pay_time`、`expire_time` 等时间字段使用 `datetime` 类型,便于查询和统计
|
||||
|
||||
@@ -1,485 +0,0 @@
|
||||
# 支付订单发起流程说明
|
||||
|
||||
## 一、业务系统调用统一下单接口
|
||||
|
||||
### 1. 接口地址
|
||||
```
|
||||
POST /api/pay/unifiedOrder
|
||||
```
|
||||
|
||||
### 2. 请求头(签名认证)
|
||||
```
|
||||
X-App-Id: app001 # 应用ID
|
||||
X-Timestamp: 1704067200 # 时间戳(Unix秒)
|
||||
X-Nonce: abc123xyz # 随机字符串
|
||||
X-Signature: calculated_signature # 签名(HMAC-SHA256)
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
### 3. 签名算法
|
||||
|
||||
**待签名字符串**:
|
||||
```
|
||||
app_id={app_id}×tamp={timestamp}&nonce={nonce}&method=POST&path=/api/pay/unifiedOrder&body_sha256={body_sha256}
|
||||
```
|
||||
|
||||
**计算签名**:
|
||||
```php
|
||||
$bodySha256 = hash('sha256', json_encode($requestBody));
|
||||
$signString = "app_id={app_id}×tamp={timestamp}&nonce={nonce}&method=POST&path=/api/pay/unifiedOrder&body_sha256={bodySha256}";
|
||||
$signature = hash_hmac('sha256', $signString, $appSecret);
|
||||
```
|
||||
|
||||
### 4. 请求体示例
|
||||
|
||||
```json
|
||||
{
|
||||
"mch_order_no": "ORDER202401011200001",
|
||||
"pay_method": "alipay",
|
||||
"amount": 100.00,
|
||||
"currency": "CNY",
|
||||
"subject": "测试商品",
|
||||
"body": "测试商品描述"
|
||||
}
|
||||
```
|
||||
|
||||
**字段说明**:
|
||||
- `mch_order_no`:商户订单号(必填,唯一,用于幂等)
|
||||
- `pay_method`:支付方式(必填,如:alipay、wechat、unionpay)
|
||||
- `amount`:订单金额(必填,单位:元)
|
||||
- `currency`:币种(可选,默认:CNY)
|
||||
- `subject`:订单标题(必填)
|
||||
- `body`:订单描述(可选)
|
||||
|
||||
### 5. 调用示例(cURL)
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8787/api/pay/unifiedOrder \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-App-Id: app001" \
|
||||
-H "X-Timestamp: 1704067200" \
|
||||
-H "X-Nonce: abc123xyz" \
|
||||
-H "X-Signature: calculated_signature" \
|
||||
-d '{
|
||||
"mch_order_no": "ORDER202401011200001",
|
||||
"pay_method": "alipay",
|
||||
"amount": 100.00,
|
||||
"subject": "测试商品",
|
||||
"body": "测试商品描述"
|
||||
}'
|
||||
```
|
||||
|
||||
### 6. PHP调用示例
|
||||
|
||||
```php
|
||||
<?php
|
||||
$appId = 'app001';
|
||||
$appSecret = 'your_app_secret';
|
||||
$baseUrl = 'http://localhost:8787';
|
||||
|
||||
// 准备请求数据
|
||||
$requestBody = [
|
||||
'mch_order_no' => 'ORDER202401011200001',
|
||||
'pay_method' => 'alipay',
|
||||
'amount' => 100.00,
|
||||
'subject' => '测试商品',
|
||||
'body' => '测试商品描述'
|
||||
];
|
||||
|
||||
// 计算签名
|
||||
$timestamp = time();
|
||||
$nonce = uniqid();
|
||||
$bodyJson = json_encode($requestBody);
|
||||
$bodySha256 = hash('sha256', $bodyJson);
|
||||
$signString = "app_id={$appId}×tamp={$timestamp}&nonce={$nonce}&method=POST&path=/api/pay/unifiedOrder&body_sha256={$bodySha256}";
|
||||
$signature = hash_hmac('sha256', $signString, $appSecret);
|
||||
|
||||
// 发送请求
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $baseUrl . '/api/pay/unifiedOrder');
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $bodyJson);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Content-Type: application/json',
|
||||
"X-App-Id: {$appId}",
|
||||
"X-Timestamp: {$timestamp}",
|
||||
"X-Nonce: {$nonce}",
|
||||
"X-Signature: {$signature}",
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
$result = json_decode($response, true);
|
||||
if ($httpCode === 200 && $result['code'] === 200) {
|
||||
echo "支付订单号:" . $result['data']['pay_order_id'] . "\n";
|
||||
echo "支付参数:" . json_encode($result['data']['pay_params'], JSON_UNESCAPED_UNICODE) . "\n";
|
||||
} else {
|
||||
echo "错误:" . $result['msg'] . "\n";
|
||||
}
|
||||
```
|
||||
|
||||
## 二、服务端处理流程
|
||||
|
||||
### 流程图
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant BizSystem as 业务系统
|
||||
participant OpenAPI as OpenAPI接口
|
||||
participant AuthMW as 签名中间件
|
||||
participant PayService as 订单服务
|
||||
participant ChannelRouter as 通道路由
|
||||
participant PluginFactory as 插件工厂
|
||||
participant Plugin as 支付插件
|
||||
participant Channel as 第三方渠道
|
||||
|
||||
BizSystem->>OpenAPI: POST /api/pay/unifiedOrder
|
||||
OpenAPI->>AuthMW: 验证签名
|
||||
AuthMW->>PayService: 调用统一下单
|
||||
PayService->>PayService: 1. 验证商户应用
|
||||
PayService->>PayService: 2. 幂等校验
|
||||
PayService->>PayService: 3. 创建支付订单
|
||||
PayService->>ChannelRouter: 4. 选择通道
|
||||
ChannelRouter-->>PayService: 返回通道信息
|
||||
PayService->>PluginFactory: 5. 实例化插件
|
||||
PluginFactory-->>PayService: 返回插件实例
|
||||
PayService->>Plugin: 6. 初始化插件(init)
|
||||
PayService->>Plugin: 7. 环境检测
|
||||
PayService->>Plugin: 8. 调用统一下单(unifiedOrder)
|
||||
Plugin->>Plugin: 8.1 根据环境选择产品
|
||||
Plugin->>Channel: 8.2 调用第三方接口
|
||||
Channel-->>Plugin: 返回支付参数
|
||||
Plugin-->>PayService: 返回支付结果
|
||||
PayService->>PayService: 9. 更新订单信息
|
||||
PayService-->>OpenAPI: 返回结果
|
||||
OpenAPI-->>BizSystem: 返回支付参数
|
||||
```
|
||||
|
||||
### 详细步骤说明
|
||||
|
||||
#### 步骤1:签名验证(中间件)
|
||||
- `OpenApiAuthMiddleware` 验证请求头中的签名
|
||||
- 验证时间戳(5分钟内有效)
|
||||
- 验证签名是否正确
|
||||
- 将应用信息注入到请求对象
|
||||
|
||||
#### 步骤2:验证商户应用
|
||||
- 根据 `app_id` 查询 `ma_merchant_app` 表
|
||||
- 检查应用状态是否启用
|
||||
|
||||
#### 步骤3:幂等校验
|
||||
- 根据 `merchant_id + mch_order_no` 查询是否已存在订单
|
||||
- 如果存在,直接返回已有订单信息(支持幂等)
|
||||
|
||||
#### 步骤4:创建支付订单
|
||||
- 生成支付订单号(格式:`P20240101120000123456`)
|
||||
- 创建 `ma_pay_order` 记录
|
||||
- 状态:`PENDING`(待支付)
|
||||
- 过期时间:30分钟后
|
||||
|
||||
#### 步骤5:通道路由选择
|
||||
- 根据 `merchant_id + app_id + method_code` 查找可用通道
|
||||
- 从 `ma_pay_channel` 表中查询
|
||||
- 选择第一个可用的通道(后续可扩展权重、容灾策略)
|
||||
|
||||
#### 步骤6:实例化插件
|
||||
- 在 `PayService` 中根据 `ma_pay_plugin` 注册表解析插件:优先使用表中的 `class_name`,否则按约定使用 `app\common\payment\{Code}Payment` 实例化插件
|
||||
- 例如:`plugin_code = 'lakala'` → 实例化 `LakalaPlugin`
|
||||
|
||||
#### 步骤7:初始化插件
|
||||
- 调用 `$plugin->init($methodCode, $channelConfig)`
|
||||
- 插件内部切换到指定支付方式的配置和逻辑
|
||||
- 例如:拉卡拉插件初始化到 `alipay` 模式
|
||||
|
||||
#### 步骤8:环境检测
|
||||
- 从请求头 `User-Agent` 判断用户环境
|
||||
- 环境类型:
|
||||
- `PC`:PC桌面浏览器
|
||||
- `H5`:H5手机浏览器
|
||||
- `WECHAT`:微信内浏览器
|
||||
- `ALIPAY_CLIENT`:支付宝客户端
|
||||
|
||||
#### 步骤9:调用插件统一下单
|
||||
- 调用 `$plugin->unifiedOrder($orderData, $channelConfig, $env)`
|
||||
- 插件内部处理:
|
||||
1. **产品选择**:从通道的 `enabled_products` 中,根据环境自动选择一个产品
|
||||
- 例如:H5环境 → 选择 `alipay_h5`
|
||||
- 例如:支付宝客户端 → 选择 `alipay_life`
|
||||
2. **调用第三方接口**:根据产品和支付方式,调用对应的第三方支付接口
|
||||
- 例如:拉卡拉插件的支付宝H5接口
|
||||
3. **返回支付参数**:返回给业务系统的支付参数
|
||||
|
||||
#### 步骤10:更新订单
|
||||
- 更新订单的 `product_code`(实际使用的产品)
|
||||
- 更新订单的 `channel_id`
|
||||
- 更新订单的 `channel_order_no`(渠道订单号)
|
||||
- 保存 `pay_params` 到 `extra` 字段
|
||||
|
||||
## 三、响应数据格式
|
||||
|
||||
### 成功响应
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"data": {
|
||||
"pay_order_id": "P20240101120000123456",
|
||||
"status": "PENDING",
|
||||
"pay_params": {
|
||||
"type": "redirect",
|
||||
"url": "https://mapi.alipay.com/gateway.do?..."
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 支付参数类型
|
||||
|
||||
根据不同的支付产品和环境,`pay_params` 的格式不同:
|
||||
|
||||
#### 1. 跳转支付(H5/PC扫码)
|
||||
```json
|
||||
{
|
||||
"type": "redirect",
|
||||
"url": "https://mapi.alipay.com/gateway.do?xxx"
|
||||
}
|
||||
```
|
||||
业务系统需要:**跳转到该URL**
|
||||
|
||||
#### 2. 表单提交(H5)
|
||||
```json
|
||||
{
|
||||
"type": "form",
|
||||
"method": "POST",
|
||||
"action": "https://mapi.alipay.com/gateway.do",
|
||||
"fields": {
|
||||
"app_id": "xxx",
|
||||
"method": "alipay.trade.wap.pay",
|
||||
"biz_content": "{...}"
|
||||
}
|
||||
}
|
||||
```
|
||||
业务系统需要:**自动提交表单**
|
||||
|
||||
#### 3. JSAPI支付(微信内/支付宝生活号)
|
||||
```json
|
||||
{
|
||||
"type": "jsapi",
|
||||
"appId": "wx1234567890",
|
||||
"timeStamp": "1704067200",
|
||||
"nonceStr": "abc123",
|
||||
"package": "prepay_id=wx1234567890",
|
||||
"signType": "MD5",
|
||||
"paySign": "calculated_signature"
|
||||
}
|
||||
```
|
||||
业务系统需要:**调用微信/支付宝JSAPI**
|
||||
|
||||
#### 4. 二维码支付(PC扫码)
|
||||
```json
|
||||
{
|
||||
"type": "qrcode",
|
||||
"qrcode_url": "https://qr.alipay.com/xxx",
|
||||
"qrcode_data": "data:image/png;base64,..."
|
||||
}
|
||||
```
|
||||
业务系统需要:**展示二维码**
|
||||
|
||||
## 四、用户支付流程
|
||||
|
||||
### 1. 业务系统处理支付参数
|
||||
|
||||
根据 `pay_params.type` 进行不同处理:
|
||||
|
||||
```javascript
|
||||
// 前端处理示例
|
||||
const payParams = response.data.pay_params;
|
||||
|
||||
switch (payParams.type) {
|
||||
case 'redirect':
|
||||
// 跳转支付
|
||||
window.location.href = payParams.url;
|
||||
break;
|
||||
|
||||
case 'form':
|
||||
// 表单提交
|
||||
const form = document.createElement('form');
|
||||
form.method = payParams.method;
|
||||
form.action = payParams.action;
|
||||
Object.keys(payParams.fields).forEach(key => {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'hidden';
|
||||
input.name = key;
|
||||
input.value = payParams.fields[key];
|
||||
form.appendChild(input);
|
||||
});
|
||||
document.body.appendChild(form);
|
||||
form.submit();
|
||||
break;
|
||||
|
||||
case 'jsapi':
|
||||
// 微信JSAPI支付
|
||||
WeixinJSBridge.invoke('getBrandWCPayRequest', {
|
||||
appId: payParams.appId,
|
||||
timeStamp: payParams.timeStamp,
|
||||
nonceStr: payParams.nonceStr,
|
||||
package: payParams.package,
|
||||
signType: payParams.signType,
|
||||
paySign: payParams.paySign
|
||||
}, function(res) {
|
||||
if (res.err_msg === "get_brand_wcpay_request:ok") {
|
||||
// 支付成功
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'qrcode':
|
||||
// 展示二维码
|
||||
document.getElementById('qrcode').src = payParams.qrcode_data;
|
||||
break;
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 用户完成支付
|
||||
|
||||
- 用户在第三方支付平台完成支付
|
||||
- 第三方平台异步回调到支付中心
|
||||
|
||||
### 3. 支付中心处理回调
|
||||
|
||||
- 接收回调:`POST /api/notify/alipay` 或 `/api/notify/wechat`
|
||||
- 验签:使用插件验证回调签名
|
||||
- 更新订单状态:`PENDING` → `SUCCESS` 或 `FAIL`
|
||||
- 创建通知任务:异步通知业务系统
|
||||
|
||||
### 4. 业务系统接收通知
|
||||
|
||||
- 支付中心异步通知业务系统的 `notify_url`
|
||||
- 业务系统验证签名并处理订单
|
||||
|
||||
## 五、查询订单接口
|
||||
|
||||
### 接口地址
|
||||
```
|
||||
GET /api/pay/query?pay_order_id=P20240101120000123456
|
||||
```
|
||||
|
||||
### 请求头(需要签名)
|
||||
```
|
||||
X-App-Id: app001
|
||||
X-Timestamp: 1704067200
|
||||
X-Nonce: abc123xyz
|
||||
X-Signature: calculated_signature
|
||||
```
|
||||
|
||||
### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"data": {
|
||||
"pay_order_id": "P20240101120000123456",
|
||||
"mch_order_no": "ORDER202401011200001",
|
||||
"status": "SUCCESS",
|
||||
"amount": 100.00,
|
||||
"pay_time": "2024-01-01 12:00:30"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 六、完整调用示例(Node.js)
|
||||
|
||||
```javascript
|
||||
const crypto = require('crypto');
|
||||
const axios = require('axios');
|
||||
|
||||
class PaymentClient {
|
||||
constructor(appId, appSecret, baseUrl) {
|
||||
this.appId = appId;
|
||||
this.appSecret = appSecret;
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
// 计算签名
|
||||
calculateSignature(method, path, body, timestamp, nonce) {
|
||||
const bodySha256 = crypto.createHash('sha256').update(JSON.stringify(body)).digest('hex');
|
||||
const signString = `app_id=${this.appId}×tamp=${timestamp}&nonce=${nonce}&method=${method}&path=${path}&body_sha256=${bodySha256}`;
|
||||
return crypto.createHmac('sha256', this.appSecret).update(signString).digest('hex');
|
||||
}
|
||||
|
||||
// 统一下单
|
||||
async unifiedOrder(orderData) {
|
||||
const timestamp = Math.floor(Date.now() / 1000);
|
||||
const nonce = Math.random().toString(36).substring(7);
|
||||
const method = 'POST';
|
||||
const path = '/api/pay/unifiedOrder';
|
||||
|
||||
const signature = this.calculateSignature(method, path, orderData, timestamp, nonce);
|
||||
|
||||
const response = await axios.post(`${this.baseUrl}${path}`, orderData, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-App-Id': this.appId,
|
||||
'X-Timestamp': timestamp,
|
||||
'X-Nonce': nonce,
|
||||
'X-Signature': signature
|
||||
}
|
||||
});
|
||||
|
||||
return response.data;
|
||||
}
|
||||
|
||||
// 查询订单
|
||||
async queryOrder(payOrderId) {
|
||||
const timestamp = Math.floor(Date.now() / 1000);
|
||||
const nonce = Math.random().toString(36).substring(7);
|
||||
const method = 'GET';
|
||||
const path = `/api/pay/query?pay_order_id=${payOrderId}`;
|
||||
|
||||
const signature = this.calculateSignature(method, path, {}, timestamp, nonce);
|
||||
|
||||
const response = await axios.get(`${this.baseUrl}${path}`, {
|
||||
headers: {
|
||||
'X-App-Id': this.appId,
|
||||
'X-Timestamp': timestamp,
|
||||
'X-Nonce': nonce,
|
||||
'X-Signature': signature
|
||||
}
|
||||
});
|
||||
|
||||
return response.data;
|
||||
}
|
||||
}
|
||||
|
||||
// 使用示例
|
||||
const client = new PaymentClient('app001', 'your_app_secret', 'http://localhost:8787');
|
||||
|
||||
// 统一下单
|
||||
client.unifiedOrder({
|
||||
mch_order_no: 'ORDER202401011200001',
|
||||
pay_method: 'alipay',
|
||||
amount: 100.00,
|
||||
subject: '测试商品',
|
||||
body: '测试商品描述'
|
||||
}).then(result => {
|
||||
console.log('支付参数:', result.data.pay_params);
|
||||
// 根据 pay_params.type 处理支付
|
||||
}).catch(err => {
|
||||
console.error('下单失败:', err.message);
|
||||
});
|
||||
```
|
||||
|
||||
## 七、注意事项
|
||||
|
||||
1. **幂等性**:相同的 `mch_order_no` 多次调用,返回同一订单信息
|
||||
2. **签名有效期**:时间戳5分钟内有效
|
||||
3. **订单过期**:订单默认30分钟过期
|
||||
4. **环境检测**:系统自动根据UA判断环境,选择合适的产品
|
||||
5. **异步通知**:支付成功后,系统会异步通知业务系统的 `notify_url`
|
||||
6. **订单查询**:业务系统可通过查询接口主动查询订单状态
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
# 支付系统核心实现说明
|
||||
|
||||
## 概述
|
||||
|
||||
已实现支付系统核心功能,包括:
|
||||
- 插件化支付通道系统(支持一个插件多个支付方式)
|
||||
- OpenAPI统一支付网关
|
||||
- 通道管理与配置
|
||||
- 订单管理与状态机
|
||||
- 异步通知机制
|
||||
|
||||
## 数据库初始化
|
||||
|
||||
执行以下SQL脚本创建表结构:
|
||||
|
||||
```bash
|
||||
mysql -u用户名 -p 数据库名 < database/mvp_payment_tables.sql
|
||||
```
|
||||
|
||||
## 核心架构
|
||||
|
||||
### 1. 插件系统
|
||||
|
||||
- **插件接口**:`app/common/contracts/PayPluginInterface.php`
|
||||
- **抽象基类**:`app/common/contracts/AbstractPayPlugin.php`(提供环境检测、产品选择等通用功能)
|
||||
- **插件类示例**:`app/common/payment/LakalaPayment.php`(命名规范:`XxxPayment`)
|
||||
- **插件解析**:由 `PayService`、`PayOrderService`、`PluginService` 直接根据 `ma_pay_plugin` 注册表中配置的 `plugin_code` / `class_name` 解析并实例化插件(默认约定类名为 `app\common\payment\{Code}Payment`)
|
||||
|
||||
**插件特点**:
|
||||
- 一个插件可以支持多个支付方式(如拉卡拉插件支持 alipay/wechat/unionpay)
|
||||
- **支付产品由插件内部定义**,不需要数据库字典表
|
||||
- 插件根据用户环境(PC/H5/微信内/支付宝客户端)自动选择已开通的产品
|
||||
- 通道配置中,用户只需勾选确认开启了哪些产品(产品编码由插件定义)
|
||||
- 有些支付平台不区分产品,插件会根据通道配置自行处理
|
||||
- 通道配置表单由插件动态生成
|
||||
|
||||
### 2. 数据模型
|
||||
|
||||
- `Merchant`:商户
|
||||
- `MerchantApp`:商户应用(AppId/AppSecret)
|
||||
- `PayMethod`:支付方式(alipay/wechat等)
|
||||
- `PayChannel`:支付通道(绑定到"插件+支付方式",配置已开通的产品列表)
|
||||
- `PayOrder`:支付订单
|
||||
- `NotifyTask`:商户通知任务
|
||||
|
||||
**注意**:支付产品不由数据库管理,而是由插件通过 `getSupportedProducts()` 方法定义。通道配置中的 `enabled_products` 字段存储的是用户勾选的产品编码数组。
|
||||
|
||||
### 3. 服务层
|
||||
|
||||
- `PayOrderService`:订单业务编排(统一下单、查询)
|
||||
- `ChannelRouterService`:通道路由选择
|
||||
- `NotifyService`:商户通知服务
|
||||
|
||||
### 4. API接口
|
||||
|
||||
#### OpenAPI(对外支付网关)
|
||||
|
||||
- `POST /api/pay/unifiedOrder`:统一下单(需要签名认证)
|
||||
- `GET /api/pay/query`:查询订单(需要签名认证)
|
||||
- `POST /api/notify/alipay`:支付宝回调
|
||||
- `POST /api/notify/wechat`:微信回调
|
||||
|
||||
#### 管理后台API
|
||||
|
||||
- `GET /adminapi/channel/plugins`:获取所有可用插件
|
||||
- `GET /adminapi/channel/plugin/config-schema`:获取插件配置表单Schema
|
||||
- `GET /adminapi/channel/plugin/products`:获取插件支持的支付产品
|
||||
- `GET /adminapi/channel/list`:通道列表
|
||||
- `GET /adminapi/channel/detail`:通道详情
|
||||
- `POST /adminapi/channel/save`:保存通道
|
||||
|
||||
## 使用流程
|
||||
|
||||
### 1. 创建商户和应用
|
||||
|
||||
```sql
|
||||
INSERT INTO ma_merchant (merchant_no, merchant_name, funds_mode, status)
|
||||
VALUES ('M001', '测试商户', 'direct', 1);
|
||||
|
||||
INSERT INTO ma_merchant_app (merchant_id, app_id, app_secret, app_name, notify_url, status)
|
||||
VALUES (1, 'app001', 'secret_key_here', '测试应用', 'https://example.com/notify', 1);
|
||||
```
|
||||
|
||||
### 2. 配置支付通道
|
||||
|
||||
**配置流程**:
|
||||
1. 创建通道:选择支付方式、支付插件,配置通道基本信息(显示名称、分成比例、通道成本、通道模式、限额等)
|
||||
2. 配置插件参数:通道创建后,再配置该通道的插件参数信息(通过插件的配置表单动态生成)
|
||||
|
||||
通过管理后台或直接操作数据库:
|
||||
|
||||
```sql
|
||||
INSERT INTO ma_pay_channel (
|
||||
merchant_id, app_id, channel_code, channel_name,
|
||||
plugin_code, method_code, enabled_products, config_json,
|
||||
split_ratio, channel_cost, channel_mode,
|
||||
daily_limit, daily_count, min_amount, max_amount,
|
||||
status
|
||||
) VALUES (
|
||||
1, 1, 'CH001', '拉卡拉-支付宝通道',
|
||||
'lakala', 'alipay',
|
||||
'["alipay_h5", "alipay_life"]',
|
||||
'{"merchant_id": "lakala_merchant", "secret_key": "xxx", "api_url": "https://api.lakala.com"}',
|
||||
100.00, 0.00, 'wallet',
|
||||
0.00, 0, NULL, NULL,
|
||||
1
|
||||
);
|
||||
```
|
||||
|
||||
**通道字段说明**:
|
||||
- `split_ratio`: 分成比例(%),默认100.00
|
||||
- `channel_cost`: 通道成本(%),默认0.00
|
||||
- `channel_mode`: 通道模式,`wallet`-支付金额扣除手续费后加入商户余额,`direct`-直连到商户
|
||||
- `daily_limit`: 单日限额(元),0表示不限制
|
||||
- `daily_count`: 单日限笔,0表示不限制
|
||||
- `min_amount`: 单笔最小金额(元),NULL表示不限制
|
||||
- `max_amount`: 单笔最大金额(元),NULL表示不限制
|
||||
|
||||
### 3. 调用统一下单接口
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8787/api/pay/unifiedOrder \
|
||||
-H "X-App-Id: app001" \
|
||||
-H "X-Timestamp: 1234567890" \
|
||||
-H "X-Nonce: abc123" \
|
||||
-H "X-Signature: calculated_signature" \
|
||||
-d '{
|
||||
"mch_order_no": "ORDER001",
|
||||
"pay_method": "alipay",
|
||||
"amount": 100.00,
|
||||
"subject": "测试订单",
|
||||
"body": "测试订单描述"
|
||||
}'
|
||||
```
|
||||
|
||||
### 4. 签名算法
|
||||
|
||||
```
|
||||
signString = "app_id={app_id}×tamp={timestamp}&nonce={nonce}&method={method}&path={path}&body_sha256={body_sha256}"
|
||||
signature = HMAC-SHA256(signString, app_secret)
|
||||
```
|
||||
|
||||
## 扩展新插件
|
||||
|
||||
1. 创建插件类,继承 `AbstractPayPlugin`,并按照 `XxxPayment` 命名放在 `app/common/payment` 目录:
|
||||
|
||||
```php
|
||||
namespace app\common\payment;
|
||||
|
||||
use app\common\contracts\AbstractPayPlugin;
|
||||
|
||||
class AlipayPayment extends AbstractPayPlugin
|
||||
{
|
||||
public static function getCode(): string { return 'alipay'; }
|
||||
public static function getName(): string { return '支付宝直连'; }
|
||||
public static function getSupportedMethods(): array { return ['alipay']; }
|
||||
// ... 实现其他方法
|
||||
}
|
||||
```
|
||||
|
||||
2. 在 `ma_pay_plugin` 表中注册插件信息(也可通过后台管理界面维护):
|
||||
|
||||
```sql
|
||||
INSERT INTO ma_pay_plugin (plugin_code, plugin_name, class_name, status)
|
||||
VALUES ('alipay', '支付宝直连', 'app\\common\\payment\\AlipayPayment', 1);
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **支付产品定义**:支付产品由插件内部通过 `getSupportedProducts()` 方法定义,不需要数据库字典表。通道配置时,用户只需勾选已开通的产品编码。
|
||||
2. **环境检测**:插件基类提供 `detectEnvironment()` 方法,可根据UA判断环境
|
||||
3. **产品选择**:插件根据环境从通道已开通产品中自动选择。如果通道配置为空或不区分产品,插件会根据配置自行处理。
|
||||
4. **通知重试**:使用 `NotifyMerchantJob` 异步重试通知,支持指数退避
|
||||
5. **幂等性**:统一下单接口支持幂等,相同 `mch_order_no` 返回已有订单
|
||||
|
||||
## 后续扩展
|
||||
|
||||
- 账务系统(账户、分录、余额)
|
||||
- 结算系统(可结算金额、结算批次、打款)
|
||||
- 对账系统(渠道账单导入、差异处理)
|
||||
- 风控系统(规则引擎、风险预警)
|
||||
|
||||
414
doc/project_overview.md
Normal file
414
doc/project_overview.md
Normal file
@@ -0,0 +1,414 @@
|
||||
# MPay V2 Project Overview
|
||||
|
||||
更新日期:2026-03-13
|
||||
|
||||
## 1. 项目定位
|
||||
|
||||
这是一个基于 Webman 的多商户支付中台项目,当前主要目标是:
|
||||
|
||||
- 提供后台管理能力,维护商户、应用、支付方式、支付插件、支付通道、订单与系统配置
|
||||
- 为商户应用提供统一支付能力
|
||||
- 当前已优先兼容 `epay` 协议,后续可继续扩展更多外部支付协议
|
||||
- 通过“支付插件 + 通道配置”的方式对接第三方渠道
|
||||
|
||||
结合当前代码与数据库,项目已经具备“多商户 -> 多应用 -> 多通道 -> 多插件”的基础骨架。
|
||||
|
||||
## 2. 技术栈与运行环境
|
||||
|
||||
### 后端技术栈
|
||||
|
||||
- PHP `>= 8.1`
|
||||
- Webman `^2.1`
|
||||
- webman/database
|
||||
- webman/redis
|
||||
- webman/cache
|
||||
- webman/console
|
||||
- webman/captcha
|
||||
- webman/event
|
||||
- webman/redis-queue
|
||||
- firebase/php-jwt
|
||||
- yansongda/pay `~3.7.0`
|
||||
|
||||
### 当前环境配置要点
|
||||
|
||||
- HTTP 服务监听:`0.0.0.0:8787`
|
||||
- 数据库:MySQL
|
||||
- 缓存与队列:Redis
|
||||
- 管理后台认证:JWT
|
||||
- 当前 `.env` 已配置远程 MySQL / Redis 地址,开发前需要确认本机网络可达
|
||||
|
||||
## 3. 当前环境可调用的 MCP 能力
|
||||
|
||||
本次会话中,已确认可以直接用于本项目的 MCP / 环境能力如下:
|
||||
|
||||
### MySQL MCP
|
||||
|
||||
- 可直接执行 SQL
|
||||
- 可读取当前开发库表结构与数据
|
||||
- 已确认能访问 `mpay_admin` 相关表,例如:
|
||||
- `ma_merchant`
|
||||
- `ma_merchant_app`
|
||||
- `ma_pay_channel`
|
||||
- `ma_pay_order`
|
||||
- `ma_notify_task`
|
||||
- `ma_callback_inbox`
|
||||
- `ma_pay_callback_log`
|
||||
|
||||
适合后续继续做:
|
||||
|
||||
- 表结构核对
|
||||
- 初始化数据检查
|
||||
- 回调与订单状态排查
|
||||
- 开发联调时快速确认通道配置
|
||||
|
||||
### Playwright MCP
|
||||
|
||||
- 可进行浏览器打开、点击、表单填写、快照、截图、网络请求分析
|
||||
- 适合后续验证:
|
||||
- 管理后台登录流程
|
||||
- 通道配置页面交互
|
||||
- 提交支付后的跳转页/表单页
|
||||
- 回调相关前端可视流程
|
||||
|
||||
### MCP 资源浏览
|
||||
|
||||
- 可列出 MCP 资源
|
||||
- 可读取资源内容
|
||||
- 当前未返回资源模板
|
||||
|
||||
### 非 MCP 但对开发有用的本地能力
|
||||
|
||||
- Shell 命令执行
|
||||
- 工作区文件读写
|
||||
- 代码补丁编辑
|
||||
|
||||
## 4. 业务模型总览
|
||||
|
||||
### 4.1 商户模型
|
||||
|
||||
- 表:`ma_merchant`
|
||||
- 作用:定义商户主体
|
||||
- 关键字段:
|
||||
- `merchant_no`
|
||||
- `merchant_name`
|
||||
- `funds_mode`
|
||||
- `status`
|
||||
|
||||
### 4.2 商户应用模型
|
||||
|
||||
- 表:`ma_merchant_app`
|
||||
- 作用:商户可创建多个应用,每个应用具备独立 `app_id` / `app_secret`
|
||||
- 关键字段:
|
||||
- `merchant_id`
|
||||
- `api_type`
|
||||
- `app_id`
|
||||
- `app_secret`
|
||||
- `app_name`
|
||||
- `status`
|
||||
|
||||
当前代码中,`app_id` 既是应用标识,也是外部协议鉴权入口;`epay` 兼容链路直接用它作为 `pid`。
|
||||
|
||||
### 4.3 支付方式模型
|
||||
|
||||
- 表:`ma_pay_method`
|
||||
- 作用:维护支付方式字典
|
||||
- 当前库内数据:
|
||||
- `alipay`
|
||||
- `wechat`
|
||||
- `unionpay`
|
||||
|
||||
### 4.4 支付插件模型
|
||||
|
||||
- 表:`ma_pay_plugin`
|
||||
- 作用:把“支付通道配置”与“PHP 插件实现类”解耦
|
||||
- 插件需要同时实现:
|
||||
- `PaymentInterface`
|
||||
- `PayPluginInterface`
|
||||
|
||||
当前代码里已有两个插件类:
|
||||
|
||||
- `app/common/payment/LakalaPayment.php`
|
||||
- `app/common/payment/AlipayPayment.php`
|
||||
|
||||
但当前数据库只注册了 `lakala`,还没有把 `alipay` 作为活动插件注册进现网开发库。
|
||||
|
||||
### 4.5 支付通道模型
|
||||
|
||||
- 表:`ma_pay_channel`
|
||||
- 作用:把“商户应用 + 支付方式 + 插件 + 参数配置”绑定起来
|
||||
- 关键字段:
|
||||
- `merchant_id`
|
||||
- `merchant_app_id`
|
||||
- `plugin_code`
|
||||
- `method_id`
|
||||
- `config_json`
|
||||
- `split_ratio`
|
||||
- `chan_cost`
|
||||
- `chan_mode`
|
||||
- `daily_limit`
|
||||
- `daily_cnt`
|
||||
- `min_amount`
|
||||
- `max_amount`
|
||||
- `status`
|
||||
- `sort`
|
||||
|
||||
这正对应你描述的核心业务特点:一个应用下可配置多个支付通道,每个通道可挂接不同插件与参数。
|
||||
|
||||
### 4.6 支付订单模型
|
||||
|
||||
- 表:`ma_pay_order`
|
||||
- 作用:统一存放系统支付订单
|
||||
- 关键特性:
|
||||
- 系统订单号:`order_id`
|
||||
- 商户订单号:`mch_order_no`
|
||||
- 幂等唯一键:`(merchant_id, merchant_app_id, mch_order_no)`
|
||||
- `extra` JSON 用于存放 `notify_url`、`return_url`、`pay_params`、退款信息等
|
||||
|
||||
### 4.7 回调与通知模型
|
||||
|
||||
- `ma_callback_inbox`:回调幂等收件箱
|
||||
- `ma_pay_callback_log`:回调日志
|
||||
- `ma_notify_task`:商户异步通知任务
|
||||
|
||||
这三张表说明项目已经为“渠道回调幂等 + 日志留痕 + 商户通知补偿”预留了比较完整的基础设施。
|
||||
|
||||
## 5. 代码分层与关键入口
|
||||
|
||||
### 外部接口入口
|
||||
|
||||
- `app/http/api/controller/EpayController.php`
|
||||
- `app/http/api/controller/PayController.php`
|
||||
|
||||
### 支付主流程服务
|
||||
|
||||
- `app/services/api/EpayProtocolService.php`
|
||||
- `app/services/api/EpayService.php`
|
||||
- `app/services/PayService.php`
|
||||
- `app/services/PayOrderService.php`
|
||||
- `app/services/ChannelRouterService.php`
|
||||
- `app/services/PluginService.php`
|
||||
- `app/services/PayNotifyService.php`
|
||||
- `app/services/NotifyService.php`
|
||||
- `app/services/PaymentStateService.php`
|
||||
|
||||
### 支付插件契约
|
||||
|
||||
- `app/common/contracts/PaymentInterface.php`
|
||||
- `app/common/contracts/PayPluginInterface.php`
|
||||
- `app/common/base/BasePayment.php`
|
||||
|
||||
### 管理后台接口
|
||||
|
||||
- 商户:`MerchantController`
|
||||
- 商户应用:`MerchantAppController`
|
||||
- 支付方式:`PayMethodController`
|
||||
- 插件注册:`PayPluginController`
|
||||
- 通道:`ChannelController`
|
||||
- 订单:`OrderController`
|
||||
- 系统配置:`SystemController`
|
||||
- 登录认证:`AuthController`
|
||||
|
||||
## 6. 当前已落地的对外接口
|
||||
|
||||
### 路由现状
|
||||
|
||||
当前 `app/routes/api.php` 实际挂载的对外接口为:
|
||||
|
||||
- `GET|POST /submit.php`
|
||||
- `POST /mapi.php`
|
||||
- `GET /api.php`
|
||||
- `ANY /notify/{pluginCode}`
|
||||
|
||||
### 兼容协议现状
|
||||
|
||||
当前真正已打通的是 `epay` 风格接口:
|
||||
|
||||
- `submit.php`:页面跳转支付
|
||||
- `mapi.php`:API 下单
|
||||
- `api.php?act=order`:查单
|
||||
- `api.php?act=refund`:退款
|
||||
|
||||
### OpenAPI 现状
|
||||
|
||||
`PayController` 中存在以下方法:
|
||||
|
||||
- `create`
|
||||
- `query`
|
||||
- `close`
|
||||
- `refund`
|
||||
|
||||
但当前都还是 `501 not implemented`,并且对应路由尚未挂载,因此“通用 OpenAPI”目前仍是预留骨架,不是已上线能力。
|
||||
|
||||
## 7. 核心支付链路
|
||||
|
||||
### 7.1 Epay 下单链路
|
||||
|
||||
1. 商户调用 `submit.php` 或 `mapi.php`
|
||||
2. `EpayProtocolService` 负责参数提取与校验
|
||||
3. `EpayService` 使用 `app_secret` 做 MD5 验签
|
||||
4. 构造统一内部订单数据
|
||||
5. `PayOrderService` 创建订单,并通过联合唯一键保证幂等
|
||||
6. `ChannelRouterService` 根据 `merchant_id + merchant_app_id + method_id` 选取通道
|
||||
7. `PluginService` 从注册表解析插件类并实例化
|
||||
8. 插件执行 `pay()`
|
||||
9. `PayService` 回写:
|
||||
- `channel_id`
|
||||
- `chan_order_no`
|
||||
- `chan_trade_no`
|
||||
- `fee`
|
||||
- `real_amount`
|
||||
- `extra.pay_params`
|
||||
10. 转换成 `epay` 所需返回结构给调用方
|
||||
|
||||
### 7.2 回调处理链路
|
||||
|
||||
1. 第三方渠道回调 `/notify/{pluginCode}`
|
||||
2. `PayNotifyService` 调插件 `notify()` 验签与解析
|
||||
3. 通过 `ma_callback_inbox` 做幂等去重
|
||||
4. 状态机更新订单状态
|
||||
5. 写入回调日志
|
||||
6. 创建商户通知任务
|
||||
|
||||
### 7.3 商户通知链路
|
||||
|
||||
1. `NotifyService` 根据订单 `extra.notify_url` 创建通知任务
|
||||
2. 通知内容写入 `ma_notify_task`
|
||||
3. `sendNotify()` 使用 HTTP POST JSON 回调商户
|
||||
4. 若商户返回 HTTP 200 且 body 为 `success`,视为通知成功
|
||||
|
||||
## 8. 插件与通道现状
|
||||
|
||||
### `LakalaPayment`
|
||||
|
||||
状态:示例插件 / mock 插件
|
||||
|
||||
现状:
|
||||
|
||||
- `pay()` 已实现,但只是返回模拟二维码字符串
|
||||
- `query()` 未实现
|
||||
- `close()` 未实现
|
||||
- `refund()` 未实现
|
||||
- `notify()` 未实现
|
||||
|
||||
这意味着当前库里虽然已经能“创建订单并拿到拉起参数”,但还不能完成真实的拉卡拉闭环。
|
||||
|
||||
### `AlipayPayment`
|
||||
|
||||
状态:代码层面相对完整
|
||||
|
||||
已实现:
|
||||
|
||||
- `pay()`
|
||||
- `query()`
|
||||
- `close()`
|
||||
- `refund()`
|
||||
- `notify()`
|
||||
|
||||
特点:
|
||||
|
||||
- 基于 `yansongda/pay`
|
||||
- 支持产品类型:
|
||||
- `alipay_web`
|
||||
- `alipay_h5`
|
||||
- `alipay_scan`
|
||||
- `alipay_app`
|
||||
- 可根据环境自动选产品
|
||||
|
||||
注意:
|
||||
|
||||
- 当前开发库没有注册 `alipay` 插件记录
|
||||
- 当前通道也没有指向 `AlipayPayment`
|
||||
|
||||
所以它虽然写在代码里,但当前数据库并没有真正启用它。
|
||||
|
||||
## 9. 管理后台现状
|
||||
|
||||
后台已经覆盖以下核心维护能力:
|
||||
|
||||
- 验证码登录 + JWT 鉴权
|
||||
- 商户管理
|
||||
- 商户应用管理
|
||||
- 支付方式管理
|
||||
- 支付插件注册管理
|
||||
- 支付通道管理
|
||||
- 订单列表 / 详情 / 退款
|
||||
- 系统基础配置管理
|
||||
|
||||
这部分说明“支付中心后台”已经不是空架子,而是可以承接后续运营配置的。
|
||||
|
||||
## 10. 当前开发库快照(基于 2026-03-13 实际查询)
|
||||
|
||||
### 数据量
|
||||
|
||||
- `ma_admin`: 1
|
||||
- `ma_merchant`: 1
|
||||
- `ma_merchant_app`: 1
|
||||
- `ma_pay_method`: 3
|
||||
- `ma_pay_plugin`: 1
|
||||
- `ma_pay_channel`: 2
|
||||
- `ma_pay_order`: 1
|
||||
- `ma_notify_task`: 0
|
||||
- `ma_callback_inbox`: 0
|
||||
- `ma_pay_callback_log`: 0
|
||||
|
||||
### 当前商户与应用
|
||||
|
||||
- 商户:`M001 / 测试商户`
|
||||
- 应用:`1001 / 测试应用-易支付`
|
||||
- 应用类型:`epay`
|
||||
|
||||
### 当前活动插件
|
||||
|
||||
- `lakala -> app\\common\\payment\\LakalaPayment`
|
||||
|
||||
### 当前通道
|
||||
|
||||
- `lakala_alipay`
|
||||
- `lakala_wechat`
|
||||
|
||||
### 当前示例订单
|
||||
|
||||
- 订单号:`P20260312160833644578`
|
||||
- 商户单号:`TEST123`
|
||||
- 状态:`PENDING`
|
||||
- 通道:`channel_id = 1`
|
||||
- `extra.pay_params` 为 mock 二维码
|
||||
|
||||
## 11. 当前代码与需求的对应关系
|
||||
|
||||
你给出的项目特点,与当前实现的对应情况如下:
|
||||
|
||||
### 已匹配的部分
|
||||
|
||||
- 多商户:已支持
|
||||
- 一个商户多个应用:已支持
|
||||
- 一个应用多个支付通道:已支持
|
||||
- 通道可绑定支付方式:已支持
|
||||
- 通道可绑定支付插件:已支持
|
||||
- 通道可存储插件参数:已支持
|
||||
- 通道可配置手续费:已支持,当前会参与 `fee` / `real_amount` 计算
|
||||
- 商户通过 `APPID` 发起支付:已支持,当前主要在 `epay` 兼容链路中落地
|
||||
- 创建订单并调用第三方插件:已支持
|
||||
|
||||
### 仅完成“数据建模”,尚未完全落地执行的部分
|
||||
|
||||
- 每日限额:字段已存在,但当前下单/路由流程未校验
|
||||
- 每日笔数限制:字段已存在,但当前未校验
|
||||
- 最小/最大金额限制:字段已存在,但当前未校验
|
||||
- 更复杂的路由策略:当前仅按 `sort` 取第一条可用通道
|
||||
- 多协议统一 OpenAPI:控制器骨架存在,但未真正接入
|
||||
|
||||
## 12. 后续阅读建议
|
||||
|
||||
如果下一次继续开发,建议优先从以下文件继续进入:
|
||||
|
||||
- 支付入口:`app/http/api/controller/EpayController.php`
|
||||
- 协议适配:`app/services/api/EpayProtocolService.php`
|
||||
- 业务主流程:`app/services/PayService.php`
|
||||
- 订单创建:`app/services/PayOrderService.php`
|
||||
- 回调处理:`app/services/PayNotifyService.php`
|
||||
- 插件管理:`app/services/PluginService.php`
|
||||
- 拉卡拉插件:`app/common/payment/LakalaPayment.php`
|
||||
- 支付宝插件:`app/common/payment/AlipayPayment.php`
|
||||
- 通道配置:`app/http/admin/controller/ChannelController.php`
|
||||
|
||||
320
doc/project_progress.md
Normal file
320
doc/project_progress.md
Normal file
@@ -0,0 +1,320 @@
|
||||
# MPay V2 Development Progress
|
||||
|
||||
更新日期:2026-03-13
|
||||
|
||||
本文档用于记录当前项目完成度、明显缺口和建议推进顺序,方便后续继续开发时快速接手。
|
||||
|
||||
## 1. 当前总体判断
|
||||
|
||||
项目已经完成了“支付中台基础骨架 + 后台配置能力 + Epay 协议首条链路”的主体搭建。
|
||||
|
||||
更准确地说:
|
||||
|
||||
- 数据模型已经比较完整
|
||||
- 后台配置能力已经具备可用性
|
||||
- 支付流程主链路已经跑通到“下单并返回拉起参数”
|
||||
- 真正需要继续补的是“真实渠道闭环、规则执行、异步补偿、通用协议扩展”
|
||||
|
||||
## 2. 已完成
|
||||
|
||||
### 2.1 基础框架与环境
|
||||
|
||||
- Webman 项目骨架已搭建
|
||||
- MySQL / Redis / JWT / Cache / Event / Redis Queue 依赖已接入
|
||||
- 管理后台与 API 路由已拆分
|
||||
|
||||
### 2.2 管理后台能力
|
||||
|
||||
- 验证码登录
|
||||
- JWT 鉴权中间件
|
||||
- 管理员信息查询
|
||||
- 菜单与系统配置读取
|
||||
- 商户 CRUD
|
||||
- 商户应用 CRUD
|
||||
- 支付方式 CRUD
|
||||
- 支付插件注册 CRUD
|
||||
- 支付通道 CRUD
|
||||
- 订单列表 / 详情 / 后台发起退款
|
||||
|
||||
### 2.3 核心支付数据结构
|
||||
|
||||
已建表并落地:
|
||||
|
||||
- 商户
|
||||
- 商户应用
|
||||
- 支付方式
|
||||
- 插件注册
|
||||
- 支付通道
|
||||
- 支付订单
|
||||
- 回调日志
|
||||
- 商户通知任务
|
||||
- 回调幂等收件箱
|
||||
|
||||
### 2.4 下单主链路
|
||||
|
||||
已打通:
|
||||
|
||||
- Epay 参数校验
|
||||
- Epay MD5 验签
|
||||
- 商户应用识别
|
||||
- 幂等订单创建
|
||||
- 通道路由
|
||||
- 插件实例化
|
||||
- 插件下单
|
||||
- 订单回写支付参数
|
||||
- 返回兼容 Epay 的响应结构
|
||||
|
||||
### 2.5 支付状态基础设施
|
||||
|
||||
- 订单状态机服务已存在
|
||||
- 成功 / 失败 / 全额退款关单状态迁移已定义
|
||||
- 回调日志记录能力已存在
|
||||
- 回调幂等收件箱已存在
|
||||
- 商户通知任务创建逻辑已存在
|
||||
|
||||
### 2.6 插件体系
|
||||
|
||||
已建立统一插件契约:
|
||||
|
||||
- `PaymentInterface`
|
||||
- `PayPluginInterface`
|
||||
- `BasePayment`
|
||||
|
||||
说明后续继续接入新渠道时,整体扩展方式已经明确。
|
||||
|
||||
## 3. 部分完成
|
||||
|
||||
### 3.1 Epay 兼容是“主路径”,但还不是“全量兼容”
|
||||
|
||||
当前已实现:
|
||||
|
||||
- `submit.php`
|
||||
- `mapi.php`
|
||||
- `api.php?act=order`
|
||||
- `api.php?act=refund`
|
||||
|
||||
但 `doc/epay.md` 中提到的一些能力,如 `query`、`settle`、`orders` 等,代码中暂未实现。
|
||||
|
||||
### 3.2 支付宝插件代码较完整,但未在当前数据库启用
|
||||
|
||||
现状:
|
||||
|
||||
- `AlipayPayment.php` 已实现
|
||||
- 当前开发库 `ma_pay_plugin` 中只有 `lakala`
|
||||
|
||||
这意味着支付宝更多处于“代码已写好、配置未接入”的状态。
|
||||
|
||||
### 3.3 回调后通知商户的基础逻辑存在,但补偿闭环还不完整
|
||||
|
||||
已完成:
|
||||
|
||||
- 创建通知任务
|
||||
- 发送通知
|
||||
- 失败重试时间计算
|
||||
|
||||
待确认 / 待补齐:
|
||||
|
||||
- 当前没有看到明确的任务投递入口
|
||||
- 也没有看到定时调度 `NotifyMerchantJob` 的配置闭环
|
||||
- `NotifyMerchantJob` 虽然存在,但尚未形成明确的可运行消费链路
|
||||
|
||||
更保守地说,商户通知补偿链路还没有真正闭环。
|
||||
|
||||
## 4. 待完成
|
||||
|
||||
### 4.1 通用 OpenAPI
|
||||
|
||||
当前状态:
|
||||
|
||||
- `PayController` 只有骨架
|
||||
- `create/query/close/refund` 都返回 `501`
|
||||
- `OpenApiAuthMiddleware` 已存在,但未挂到路由
|
||||
|
||||
建议判断:这是下一阶段最适合补完的能力之一。
|
||||
|
||||
### 4.2 拉卡拉真实对接
|
||||
|
||||
当前状态:
|
||||
|
||||
- `LakalaPayment::pay()` 只返回 mock 二维码
|
||||
- `query/close/refund/notify` 全部未实现
|
||||
|
||||
影响:
|
||||
|
||||
- 现在只能用于打通订单创建流程
|
||||
- 还不能进行真实线上支付联调
|
||||
|
||||
### 4.3 通道路由规则执行
|
||||
|
||||
数据库已设计的字段很多,但运行期并未全部生效:
|
||||
|
||||
- `daily_limit` 未校验
|
||||
- `daily_cnt` 未校验
|
||||
- `min_amount` 未校验
|
||||
- `max_amount` 未校验
|
||||
- `split_ratio` 当前只存储,未看到清算分账逻辑
|
||||
- 路由策略目前只是“按排序取第一条可用通道”
|
||||
|
||||
这块是项目从“能下单”走向“可运营”的关键缺口。
|
||||
|
||||
### 4.4 Epay 协议映射细节
|
||||
|
||||
当前内部支付方式代码使用:
|
||||
|
||||
- `alipay`
|
||||
- `wechat`
|
||||
|
||||
但传统 Epay 常见值通常还有:
|
||||
|
||||
- `wxpay`
|
||||
- `qqpay`
|
||||
|
||||
当前代码里没有看到统一别名映射层,说明“协议兼容”仍偏接口形态兼容,而不是完整字段语义兼容。
|
||||
|
||||
### 4.5 插件注册与初始化数据同步
|
||||
|
||||
代码、SQL、数据库现状存在轻微偏差:
|
||||
|
||||
- `database/dev_seed.sql` 里准备了 `alipay` 和 `lakala`
|
||||
- 当前开发库只看到 `lakala`
|
||||
|
||||
建议后续把“代码存在但数据库未启用”的状态统一起来,减少联调歧义。
|
||||
|
||||
### 4.6 通道安全与敏感配置
|
||||
|
||||
当前通道配置直接存在 `config_json` 中,后续建议补充:
|
||||
|
||||
- 敏感字段加密存储
|
||||
- 后台展示脱敏
|
||||
- 配置变更审计日志
|
||||
|
||||
### 4.7 测试体系
|
||||
|
||||
当前仓库里没有看到成体系的:
|
||||
|
||||
- 单元测试
|
||||
- 协议测试
|
||||
- 插件对接测试
|
||||
- 回调幂等测试
|
||||
- 退款回归测试
|
||||
|
||||
这会让后续迭代的回归成本越来越高。
|
||||
|
||||
## 5. 风险与注意点
|
||||
|
||||
### 5.1 当前“多通道”能力更偏配置层,而不是调度层
|
||||
|
||||
虽然表结构和后台已经支持多通道,但运行时路由还比较简单,不能完全体现:
|
||||
|
||||
- 限额控制
|
||||
- 金额区间控制
|
||||
- 通道健康度切换
|
||||
- 优先级与容灾
|
||||
|
||||
### 5.2 退款能力目前偏基础版
|
||||
|
||||
当前退款服务已存在,但从实现上看:
|
||||
|
||||
- 更适合单次退款 / 全额退款
|
||||
- 全额退款后直接把订单关闭
|
||||
- 没有独立退款单模型
|
||||
- 没有完整的部分退款累计能力
|
||||
|
||||
### 5.3 回调成功后的订单与通知一致性要继续加强
|
||||
|
||||
当前已经有:
|
||||
|
||||
- 幂等收件箱
|
||||
- 状态机
|
||||
- 通知任务表
|
||||
|
||||
这是很好的基础。
|
||||
|
||||
但真正生产级还建议再补:
|
||||
|
||||
- 事务边界说明
|
||||
- 异常重放工具
|
||||
- 回调人工补单工具
|
||||
- 通知签名
|
||||
|
||||
## 6. 建议优先级
|
||||
|
||||
### P0:优先补完,直接影响可用性
|
||||
|
||||
1. 实现真实渠道插件,至少先补完一个可联调通道
|
||||
2. 补完 OpenAPI 主链路
|
||||
3. 在路由阶段执行金额限制 / 限额 / 笔数规则
|
||||
4. 打通商户通知任务的实际调度与重试闭环
|
||||
|
||||
### P1:补齐可运营能力
|
||||
|
||||
1. 增加支付方式别名映射,提升 Epay 兼容度
|
||||
2. 把 `AlipayPayment` 正式接入插件注册与通道配置
|
||||
3. 增加后台对通道能力、产品、环境的可视化说明
|
||||
4. 增加日志检索与问题排查手段
|
||||
|
||||
### P2:走向平台化
|
||||
|
||||
1. 增加更多协议兼容层
|
||||
2. 增加清算 / 分账 / 对账
|
||||
3. 增加风控规则
|
||||
4. 增加监控、告警、报表
|
||||
|
||||
## 7. 建议后续开发方向
|
||||
|
||||
### 方向一:先做“一个真实可用通道”
|
||||
|
||||
建议优先把某一个通道做成完整闭环:
|
||||
|
||||
- 下单
|
||||
- 回调
|
||||
- 查单
|
||||
- 关单
|
||||
- 退款
|
||||
|
||||
这样项目就能从“框架完成”升级为“真实可上线联调”。
|
||||
|
||||
### 方向二:补通用 OpenAPI
|
||||
|
||||
原因:
|
||||
|
||||
- 你已经明确后续可能兼容更多接口
|
||||
- 当前通用控制器和鉴权中间件已经有雏形
|
||||
- 补完之后,项目会从“单协议适配器”升级为“统一支付网关”
|
||||
|
||||
### 方向三:把通道路由做成真正的策略引擎
|
||||
|
||||
建议把下面这些字段从“仅存储”升级为“真实执行”:
|
||||
|
||||
- 金额范围
|
||||
- 单日限额
|
||||
- 单日限笔
|
||||
- 通道优先级
|
||||
- 通道健康状态
|
||||
- 权重或降级策略
|
||||
|
||||
### 方向四:补测试与排障工具
|
||||
|
||||
优先建议增加:
|
||||
|
||||
- 下单幂等测试
|
||||
- 回调幂等测试
|
||||
- 退款状态测试
|
||||
- 协议字段兼容测试
|
||||
- 一键重发通知工具
|
||||
|
||||
## 8. 推荐继续开发顺序
|
||||
|
||||
如果下一次直接继续往下做,我建议按这个顺序推进:
|
||||
|
||||
1. 选定一个真实渠道作为首个闭环目标
|
||||
2. 补完该插件的 `notify/query/refund/close`
|
||||
3. 接入并验证商户通知补偿链路
|
||||
4. 在 `ChannelRouterService` 前后补齐通道规则校验
|
||||
5. 正式实现 `PayController`
|
||||
6. 抽象协议适配层,准备支持更多接口
|
||||
7. 增加测试与后台排障能力
|
||||
|
||||
## 9. 当前一句话结论
|
||||
|
||||
这是一个“骨架已经成型、第一条协议已打通、非常适合继续往生产级推进”的支付中台项目;下一阶段的重点不是重写,而是把已有设计真正补成闭环。
|
||||
Reference in New Issue
Block a user