mirror of
https://github.com/crmeb/CRMEB.git
synced 2026-10-28 13:31:04 +00:00
docs(支付驱动): 新增支付驱动二开指南并登记文档索引
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
1d87201680
commit
4deb518aa3
567
help/docs/支付驱动二开指南.md
Normal file
567
help/docs/支付驱动二开指南.md
Normal file
@ -0,0 +1,567 @@
|
||||
# CRMEB v8 支付驱动二开指南
|
||||
|
||||
> 面向需要在 CRMEB v8 中新增或改造第三方支付通道的研发同学。
|
||||
> 本文以当前仓库代码为准,说明支付驱动的定位、清单、接口契约、配置存储、回调/退款链路与验收清单。
|
||||
> 精简操作条目亦可对照 `backend/driver/pay/DEVELOPMENT.md`;机制总览见 `.crmeb/skills/crmeb-dev/references/02-驱动机制/driver-mechanism.md`。
|
||||
|
||||
---
|
||||
|
||||
## 1. 为什么要做「支付驱动」二开
|
||||
|
||||
电商系统里,「下单 → 收款 → 回调入账 → 退款」是固定业务,真正会变的是**跟哪家支付服务商说话**。CRMEB 把这部分差异抽成 **支付驱动(`backend/driver/pay/`)**:
|
||||
|
||||
| 不变(业务层) | 变化(驱动层) |
|
||||
|---|---|
|
||||
| 统一订单状态机、支付单、退款单 | 下单请求字段、签名算法、证书 |
|
||||
| `PayServices` 发起支付 / 退款 / 回调路由 | 各端支付形态(JSAPI / H5 / NATIVE / APP) |
|
||||
| `OrderPaySuccess` 等业务事件 | 异步通知验签与响应格式 |
|
||||
| 管理端「支付方式」配置页 | 商户号、密钥、证书等配置项 |
|
||||
|
||||
因此:接入银联、汇付、易宝、自建聚合通道等,**正确姿势是新增一个支付驱动包**,而不是改订单中台或在业务 Service 里硬编码渠道枚举。
|
||||
|
||||
---
|
||||
|
||||
## 2. 架构定位:驱动在哪一层
|
||||
|
||||
```
|
||||
modules/(业务扩展)
|
||||
↓
|
||||
app/(统一管理中台:PayServices、PayChannelServices、订单支付)
|
||||
↓
|
||||
crmeb/(核心门面:Pay / PayInterface / BasePay / DriverRegistry)
|
||||
↓
|
||||
driver/pay/{DriverName}/(第三方协议与 SDK)
|
||||
```
|
||||
|
||||
硬约束(违反即架构错误):
|
||||
|
||||
1. **依赖单向**:`modules → app → crmeb → driver`。驱动包**禁止**访问数据库、禁止调用 `app/` / `modules/` 业务 Service。
|
||||
2. **零侵入二开**:新驱动只落在 `backend/driver/pay/{YourDriver}/`,禁止改 `app/`、`crmeb/`、核心前端、安装 SQL、composer 依赖清单(除非走正式评审的框架能力变更)。
|
||||
3. **配置注入**:商户凭据由上层在实例化门面时注入,驱动只在 `initialize(array $config)` 或通过既有 Service/SDK 读取上层已装好的配置,**禁止**把密钥写进 `plugin.json` 常量。
|
||||
|
||||
判定口诀:
|
||||
|
||||
- 「怎么和第三方说话」→ `driver/pay/`
|
||||
- 「付完款业务上怎么办」→ 订单中台 / 模块事件(如 `OrderPaySuccess`)
|
||||
|
||||
---
|
||||
|
||||
## 3. 支付与通用驱动字典的差异(必读)
|
||||
|
||||
多数驱动类型(短信、上传、邮件等)元数据进统一表 `eb_driver`,由 `DriverRegistry` 读取。
|
||||
|
||||
**支付是例外**:支付方式独立走渠道字典:
|
||||
|
||||
| 能力 | 存储 / 组件 |
|
||||
|---|---|
|
||||
| 驱动实现与清单 | `backend/driver/pay/*/plugin.json` + `Driver.php` |
|
||||
| 渠道字典 | `eb_pay_channel`(`channel_key` 唯一,含 `driver`、`config_fields`) |
|
||||
| 各端支付形态 | `eb_pay_channel_platform` |
|
||||
| 后台管理 | `app/services/driver/pay/PayChannelServices` 等 |
|
||||
| 运行时编排 | `app/services/pay/PayServices` + 门面 `crmeb\services\pay\Pay` |
|
||||
|
||||
排查时若在 `eb_driver` 里找支付驱动记录,会查不到——这是设计如此,不是安装失败。
|
||||
|
||||
余额支付(`yue`)、线下支付(`offline`)等是**系统固有渠道**,不是支付驱动插件;第三方在线渠道由驱动扫描结果动态纳入,业务侧不应再硬编码「微信/支付宝」枚举。
|
||||
|
||||
---
|
||||
|
||||
## 4. 现有支付驱动一览
|
||||
|
||||
| 目录 | `driver_key` | `channel_key` | 平台 | 说明 |
|
||||
|---|---|---|---|---|
|
||||
| `WechatPay` | `wechat_pay` | `weixin` | wechat | 微信支付 APIv2 |
|
||||
| `V3WechatPay` | `v3_wechat_pay` | `weixin_v3` | wechat | 微信支付 APIv3(含商家转账等扩展能力) |
|
||||
| `AliPay` | `ali_pay` | `alipay` | alipay | 支付宝(EasySDK) |
|
||||
| `AllinPay` | `allin_pay` | `allinpay` | allinpay | 通联支付 |
|
||||
|
||||
推荐对照实现:
|
||||
|
||||
- **标准最小实现**:`backend/driver/pay/AliPay/`
|
||||
- **多端映射 / 服务商 / 转账**:`backend/driver/pay/V3WechatPay/`
|
||||
|
||||
---
|
||||
|
||||
## 5. 驱动包标准结构
|
||||
|
||||
```
|
||||
backend/driver/pay/{DriverName}/
|
||||
├── Driver.php # 必须:继承 BasePay,实现 PayInterface
|
||||
├── plugin.json # 必须:双层清单(发现、安装、后台表单)
|
||||
├── XxxSdk.php # 可选:协议/SDK 适配(大逻辑建议拆出 Driver)
|
||||
├── Client.php # 可选:HTTP 客户端封装
|
||||
├── Installer.php # 可选:安装期动作(绝大多数支付驱动用通用 ManifestDriverInstaller 即可)
|
||||
└── extend/ # 可选:大体积第三方 SDK 隔离目录
|
||||
```
|
||||
|
||||
命名约定:
|
||||
|
||||
- 目录名使用大驼峰,如 `UnionPay`、`HuifuPay`
|
||||
- 命名空间:`driver\pay\{DriverName}`
|
||||
- 实现类默认名:`Driver`(与 `plugin.json` 中 `class_name` 一致,缺省即为 `Driver`)
|
||||
- 门面解析:`Pay` 对传入的 `driver_key` 做 `Studly` 后优先加载 `driver\pay\{Studly}\Driver`
|
||||
例如 `ali_pay` → `AliPay\Driver`,`v3_wechat_pay` → `V3WechatPay\Driver`
|
||||
|
||||
---
|
||||
|
||||
## 6. 从零接入:完整步骤
|
||||
|
||||
### 6.1 创建目录
|
||||
|
||||
```bash
|
||||
mkdir -p backend/driver/pay/YourPay
|
||||
```
|
||||
|
||||
### 6.2 编写 `plugin.json`
|
||||
|
||||
支付驱动的清单是**双层结构**:
|
||||
|
||||
- **顶层**:插件生命周期元信息(安装、版本、依赖)
|
||||
- **`driver` 子对象**:支付专用描述(渠道、端映射、配置表单等),注册/扫描侧**全量透传**
|
||||
|
||||
最小可用示例(请按实际渠道改名):
|
||||
|
||||
```json
|
||||
{
|
||||
"key": "your_pay",
|
||||
"name": "某某支付",
|
||||
"description": "某某支付驱动",
|
||||
"version": "1.0.0",
|
||||
"author": "YourTeam",
|
||||
"type": "driver",
|
||||
"path": "driver/pay/YourPay",
|
||||
"default_install": false,
|
||||
"require": {
|
||||
"crmeb": "^8.0",
|
||||
"php": "^8.0"
|
||||
},
|
||||
"driver": {
|
||||
"name": "某某支付",
|
||||
"channel_key": "yourpay",
|
||||
"driver_key": "your_pay",
|
||||
"platform": "yourpay",
|
||||
"icon": "icon-xxx",
|
||||
"sort": 90,
|
||||
"capabilities": ["pay", "refund", "notify"],
|
||||
"required_credentials": {},
|
||||
"pay_type_map": {
|
||||
"h5": "H5",
|
||||
"app": "APP",
|
||||
"pc": "NATIVE"
|
||||
},
|
||||
"pay_status_type": {
|
||||
"default": "yourpay_pay",
|
||||
"h5": "yourpay_h5_pay",
|
||||
"pc": "yourpay_pc_pay"
|
||||
},
|
||||
"supported_platforms": {
|
||||
"h5": "H5浏览器",
|
||||
"app": "APP",
|
||||
"pc": "PC端"
|
||||
},
|
||||
"config_fields": [
|
||||
{
|
||||
"field": "your_mch_id",
|
||||
"label": "商户号",
|
||||
"type": "text",
|
||||
"required": true,
|
||||
"group": "商户",
|
||||
"tip": "支付平台分配的商户号"
|
||||
},
|
||||
{
|
||||
"field": "your_api_key",
|
||||
"label": "API密钥",
|
||||
"type": "password",
|
||||
"required": true,
|
||||
"group": "商户"
|
||||
},
|
||||
{
|
||||
"field": "your_cert",
|
||||
"label": "商户证书",
|
||||
"type": "upload",
|
||||
"group": "证书"
|
||||
}
|
||||
],
|
||||
"help_doc": {
|
||||
"title": "某某支付使用说明",
|
||||
"steps": [
|
||||
"在支付平台注册商户并完成进件",
|
||||
"获取商户号与 API 密钥",
|
||||
"配置异步通知地址为:站点域名 + /api/pay/notify/{channel_key}",
|
||||
"将凭据填入上方配置并开启对应客户端平台"
|
||||
],
|
||||
"link": "https://example.com/docs"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 顶层字段
|
||||
|
||||
| 字段 | 必填 | 说明 |
|
||||
|---|---|---|
|
||||
| `key` | 是 | 与 `driver.driver_key` **必须一致** |
|
||||
| `name` / `description` / `version` / `author` | 是 | 展示与版本管理 |
|
||||
| `type` | 是 | 固定 `"driver"` |
|
||||
| `path` | 是 | 相对 backend 的路径,如 `driver/pay/YourPay` |
|
||||
| `require` | 建议 | `crmeb` / `php` 版本约束 |
|
||||
| `default_install` | 否 | `true` 时随系统捆绑安装;需单独申请密钥的通道建议 `false`,后台按需安装 |
|
||||
|
||||
#### `driver` 子对象(支付扩展)
|
||||
|
||||
| 字段 | 必填 | 说明 |
|
||||
|---|---|---|
|
||||
| `name` | 是 | 后台显示名 |
|
||||
| `channel_key` | 是 | 业务侧支付方式标识(回调 URL 的 `:type`、退款路由都认它) |
|
||||
| `driver_key` | 是 | 驱动唯一标识,与顶层 `key` 一致 |
|
||||
| `platform` | 是 | 支付平台归类,如 `wechat` / `alipay` / `allinpay` |
|
||||
| `icon` | 否 | 管理端图标类名 |
|
||||
| `capabilities` | 否 | 能力声明:`pay` / `refund` / `notify` / `merchant_pay` 等 |
|
||||
| `required_credentials` | 否 | 各客户端发起支付前需要的前端凭证,如小程序/公众号要 `openid` |
|
||||
| `pay_type_map` | 否 | 客户端平台 → 供应商支付类型(如 `wechat_mini` → `JSAPI`) |
|
||||
| `pay_status_type` | 否 | 前端拿到支付结果后的跳转/调起形态;可为字符串或按端区分的对象 |
|
||||
| `default_options` | 否 | 发起支付时自动并入的配置开关字段名列表 |
|
||||
| `supported_platforms` | 否 | 支持的客户端及中文名;后台可按端开关 |
|
||||
| `config_fields` | 否 | 动态表单定义(见下节) |
|
||||
| `help_doc` | 否 | 配置页帮助文案 |
|
||||
|
||||
`config_fields` 常用属性:
|
||||
|
||||
| 属性 | 说明 |
|
||||
|---|---|
|
||||
| `field` | 配置键名(最终写入 `config_fields[].value`) |
|
||||
| `label` | 表单项标题 |
|
||||
| `type` | `text` / `password` / `textarea` / `upload` / `radio` 等 |
|
||||
| `group` | 分组标题 |
|
||||
| `required` | 是否必填 |
|
||||
| `default` | 默认值 |
|
||||
| `options` | `radio` 等选项列表 `{value,label}` |
|
||||
| `show_if` | 条件显示 `{field, value}` |
|
||||
| `tip` | 表单项说明 |
|
||||
|
||||
### 6.3 实现 `Driver.php`
|
||||
|
||||
必须继承 `crmeb\services\pay\BasePay` 并实现 `crmeb\services\pay\PayInterface`:
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace driver\pay\YourPay;
|
||||
|
||||
use crmeb\services\pay\BasePay;
|
||||
use crmeb\services\pay\PayInterface;
|
||||
|
||||
class Driver extends BasePay implements PayInterface
|
||||
{
|
||||
protected function initialize(array $config): void
|
||||
{
|
||||
// 从 $config 读取商户号、密钥等(由 PayChannel 配置注入)
|
||||
}
|
||||
|
||||
public function create(
|
||||
string $orderId,
|
||||
string $totalFee,
|
||||
string $attach,
|
||||
string $body,
|
||||
string $detail,
|
||||
array $options = []
|
||||
) {
|
||||
// $options 常见键:platform、pay_type、openid、quitUrl、returnUrl 等
|
||||
// 按供应商 API 下单,返回前端调起支付所需数据结构
|
||||
}
|
||||
|
||||
public function merchantPay(
|
||||
string $openid,
|
||||
string $orderId,
|
||||
string $amount,
|
||||
array $options = []
|
||||
) {
|
||||
// 不支持企业付款时返回 false 即可
|
||||
return false;
|
||||
}
|
||||
|
||||
public function refund(string $outTradeNo, array $options = [])
|
||||
{
|
||||
// $options 常见:totalAmount、refund_id、trade_no、payment_context 等
|
||||
}
|
||||
|
||||
public function queryRefund(
|
||||
string $outTradeNo,
|
||||
string $outRequestNo,
|
||||
array $other = []
|
||||
) {
|
||||
// 查询退款进度,返回结构尽量与同平台既有驱动对齐
|
||||
}
|
||||
|
||||
public function handleNotify(string $type = '')
|
||||
{
|
||||
// 验签 → 提取商户订单号 → 触发业务成功处理 → 返回 success/fail
|
||||
// 详见第 8 节
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`BasePay` 提供 `setPayType()` / `authSetPayType()`:`pay_type` 由 `PayServices` 根据 `pay_type_map` 写入 `$options`,驱动侧不要自己猜客户端平台。
|
||||
|
||||
### 6.4 安装与出现在后台
|
||||
|
||||
1. 管理端安装该驱动插件(或 `default_install=true` 随捆绑安装)。
|
||||
2. 在「设置 → 支付方式」中应能看到对应渠道;填写 `config_fields`、按端开启平台。
|
||||
3. 手动校验(开发环境):
|
||||
|
||||
```php
|
||||
use crmeb\services\driver\DriverRegistry;
|
||||
|
||||
DriverRegistry::scan('pay');
|
||||
DriverRegistry::getDriverClass('pay', 'your_pay');
|
||||
// 期望:driver\pay\YourPay\Driver
|
||||
```
|
||||
|
||||
支付渠道配置**只保存在** `eb_pay_channel.config_fields`(字段定义 + 各字段 `value`),**禁止**回写 `system_config` / `eb_system_config`。
|
||||
|
||||
---
|
||||
|
||||
## 7. 运行时调用链(业务如何找到你的驱动)
|
||||
|
||||
### 7.1 发起支付(简化)
|
||||
|
||||
```
|
||||
用户端下单支付
|
||||
→ app 层订单/支付服务
|
||||
→ PayServices(按 channel_key 解析 driver_key、合并 pay_type_map / 配置)
|
||||
→ app()->make(Pay::class, [$driverKey, $configValues])
|
||||
→ BaseManager 解析 driver\pay\{Studly}\Driver
|
||||
→ Driver::create(...)
|
||||
→ 返回给前端的调起参数(JSAPI prepay、H5 URL、扫码码等)
|
||||
```
|
||||
|
||||
`PayServices` 会把已扫描到的支付驱动动态当成「第三方在线渠道」;**新增驱动后调用方不必再改一处渠道枚举**。
|
||||
|
||||
### 7.2 退款
|
||||
|
||||
```
|
||||
售后/后台退款
|
||||
→ PayServices::refund($channelKey, $orderId, $refundData, ...)
|
||||
→ resolveDriverName($channelKey)
|
||||
→ Pay 门面 → Driver::refund()
|
||||
→ (可选)Driver::queryRefund() 轮询或对账
|
||||
```
|
||||
|
||||
注意:退款必须走**原支付渠道**对应驱动;微信等场景会校验支付上下文中的 `channel_key` 与退款渠道一致。
|
||||
|
||||
### 7.3 异步回调
|
||||
|
||||
路由(用户端 API):
|
||||
|
||||
```text
|
||||
ANY /api/pay/notify/:type
|
||||
```
|
||||
|
||||
`:type` 语义上对应 **`channel_key`**(历史别名例外见下)。
|
||||
|
||||
```
|
||||
PayController::notify($type)
|
||||
→ PayServices::handleNotify($type)
|
||||
1. resolveChannelKeyByType($type)
|
||||
2. resolveDriverName($channelKey)
|
||||
3. getChannelConfigValues($channelKey)
|
||||
4. Pay 门面 → Driver::handleNotify($type)
|
||||
```
|
||||
|
||||
已知别名映射(写新驱动时尽量让 notify URL 直接使用 `channel_key`,少加特殊分支):
|
||||
|
||||
| URL 中的 type | 解析后的 channel_key |
|
||||
|---|---|
|
||||
| 与渠道表一致的 key | 原样 |
|
||||
| `v3wechat` | `weixin_v3`(兼容历史 notify_url) |
|
||||
| `allin*` 前缀 | `allinpay` |
|
||||
|
||||
商户平台应配置:
|
||||
|
||||
```text
|
||||
{站点域名}/api/pay/notify/{你的 channel_key}
|
||||
```
|
||||
|
||||
例如支付宝现网使用 `/api/pay/notify/alipay`。
|
||||
|
||||
---
|
||||
|
||||
## 8. 回调实现规范(安全红线)
|
||||
|
||||
支付回调是驱动最容易出错、也最危险的环节。强制要求:
|
||||
|
||||
1. **验签**:校验通知来自支付平台,失败返回 `fail` / 非 2xx,让对方重试。
|
||||
2. **幂等**:同一订单重复通知不得重复发货、入账、改状态。业务侧通过统一支付成功处理与订单状态机保证;驱动侧不要绕过事件私自改业务表。
|
||||
3. **金额以服务端为准**:禁止信任客户端或通知体里未校验的金额去改价。
|
||||
|
||||
推荐流程(示意):
|
||||
|
||||
```php
|
||||
public function handleNotify(string $type = '')
|
||||
{
|
||||
// 1. 读取原始报文(JSON / form / XML 按供应商而定)
|
||||
$payload = /* ... */;
|
||||
|
||||
// 2. 验签;失败必须让支付平台感知并重试
|
||||
if (!$this->verifySign($payload)) {
|
||||
return 'fail';
|
||||
}
|
||||
|
||||
// 3. 提取商户订单号(与下单时的 out_trade_no 一致)
|
||||
$orderNo = $payload['out_trade_no'] ?? '';
|
||||
|
||||
// 4. 交给业务层完成「支付成功」闭环
|
||||
// 实际项目中通常由既有支付成功服务 / 事件完成:
|
||||
// event('OrderPaySuccess', [$orderInfo]);
|
||||
// 请对照 AliPay / V3WechatPay 的成熟写法,勿自建第二套入账逻辑
|
||||
|
||||
// 5. 按供应商要求返回成功应答(字符串或 Think Response)
|
||||
return 'success';
|
||||
}
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
- `PayServices::handleNotify` 若驱动返回 `think\Response`,会**保留 HTTP 状态码与响应头**,便于验签失败时触发平台重试。
|
||||
- 业务事件使用**字符串 key**(如 `OrderPaySuccess`),不要带 `Listener` 后缀。
|
||||
- 驱动内不要直接操作订单表;复用既有支付成功闭环,避免状态机分叉。
|
||||
|
||||
---
|
||||
|
||||
## 9. 前端与多端要点
|
||||
|
||||
支付驱动二开往往不止后端:
|
||||
|
||||
1. **`supported_platforms`**:声明 H5 / 小程序 / 公众号 / APP / PC 等;后台按端开关写入 `eb_pay_channel_platform`。
|
||||
2. **`pay_type_map`**:决定传给驱动的 `pay_type`(如微信 `JSAPI` / `NATIVE`)。
|
||||
3. **`pay_status_type`**:决定用户端拿到支付接口响应后走哪种调起逻辑(微信内 JSAPI、H5 跳转、PC 二维码等)。新增类型时,需确认 `frontend/uni-app`(及必要时管理端)是否已有对应分支;没有则要补前端处理,否则后台能配、端上调不起。
|
||||
4. **`required_credentials`**:例如微信小程序/公众号需要 `openid`,业务在调 `create` 前必须带上,否则驱动侧无法下单。
|
||||
|
||||
不要在用户端写死「只有微信/支付宝」;以接口返回的可用渠道与 `pay_status_type` 为准。
|
||||
|
||||
---
|
||||
|
||||
## 10. 配置与数据表
|
||||
|
||||
| 表 | 作用 |
|
||||
|---|---|
|
||||
| `eb_pay_channel` | 支付方式字典:名称、图标、`channel_key`、绑定 `driver`、`config_fields` JSON、启停等 |
|
||||
| `eb_pay_channel_platform` | 渠道在各客户端平台的启用与 pay_type 关系 |
|
||||
|
||||
配置读写入口:`PayChannelServices`(列表、详情、保存配置、驱动扫描状态等)。
|
||||
管理端页面参考:`frontend/admin/src/pages/driver/pay/index.vue`。
|
||||
管理端路由:`backend/app/adminapi/route/driver.php` 中 `pay_channel/*` 一组接口。
|
||||
|
||||
原则:
|
||||
|
||||
- 配置值写在 `config_fields` 各字段的 `value` 上
|
||||
- 同步/升级驱动字段定义时,应**保留已有 value 与启用状态**
|
||||
- 密钥、证书路径属于运行时机密,**禁止提交进 Git**;日志中脱敏
|
||||
|
||||
---
|
||||
|
||||
## 11. 与订单中台的边界
|
||||
|
||||
支付驱动只负责「钱有没有从用户到商户 / 原路退回」。以下属于订单中台或模块,**不要塞进驱动**:
|
||||
|
||||
- 库存锁定与扣减、自动发货、虚拟商品发卡
|
||||
- 分销记佣、营销核销、积分成长值
|
||||
- 售后退款审批流、退款单状态机、退款副作用幂等表
|
||||
|
||||
正确扩展方式:监听 `OrderPaySuccess`、退款相关事件,或在产品类型 / 交易模式模块内实现。驱动保持「薄适配器」。
|
||||
|
||||
---
|
||||
|
||||
## 12. 二开红线清单
|
||||
|
||||
| 序号 | 红线 |
|
||||
|---|---|
|
||||
| 1 | 不修改 `app/`、`crmeb/`、核心前端与安装 SQL 来「硬挂」新渠道 |
|
||||
| 2 | 驱动内不访问数据库、不直接改订单/退款状态 |
|
||||
| 3 | 支付配置不写 `system_config`,只写 `eb_pay_channel.config_fields` |
|
||||
| 4 | `plugin.json` 顶层 `key` 与 `driver.driver_key` 必须一致且全局唯一 |
|
||||
| 5 | 回调必须验签 + 幂等;金额以服务端订单为准 |
|
||||
| 6 | 退款走原渠道驱动,禁止跨渠道乱退 |
|
||||
| 7 | 商户密钥、证书内容不入库、不打明文日志 |
|
||||
| 8 | 同级模块禁止互引;跨业务用字符串事件或核心契约 |
|
||||
|
||||
完整插件边界见 `.crmeb/rules/插件开发规范.md`;安全条款见 `.crmeb/rules/安全红线.md`。
|
||||
|
||||
---
|
||||
|
||||
## 13. 验收清单
|
||||
|
||||
开发自测建议按下列勾选:
|
||||
|
||||
**发现与配置**
|
||||
|
||||
- [ ] `DriverRegistry::scan('pay')` 能看到新 `driver_key`
|
||||
- [ ] `getDriverClass('pay', ...)` 返回正确类名,类可被自动加载
|
||||
- [ ] 管理端支付方式列表出现渠道,动态表单可保存并回显
|
||||
- [ ] 各目标客户端平台可独立开关
|
||||
|
||||
**支付主路径**
|
||||
|
||||
- [ ] 各支持端能拉起支付(参数完整:openid、returnUrl 等)
|
||||
- [ ] 支付成功后订单进入已支付,库存/履约/通知符合预期
|
||||
- [ ] 重复回调不会重复履约
|
||||
|
||||
**退款路径**
|
||||
|
||||
- [ ] 全额 / 部分退款调用到正确驱动
|
||||
- [ ] `queryRefund` 结果可被上层判定(避免「未知状态」死循环)
|
||||
- [ ] 退款失败有明确错误信息与日志(脱敏)
|
||||
|
||||
**安全与运维**
|
||||
|
||||
- [ ] 伪造回调(错误签名)被拒绝
|
||||
- [ ] 商户平台 notify URL、证书、时钟偏差已核对
|
||||
- [ ] 生产密钥仅存在于运行环境配置,不在仓库中
|
||||
|
||||
---
|
||||
|
||||
## 14. 常见问题排障
|
||||
|
||||
| 现象 | 可能原因 | 处理方向 |
|
||||
|---|---|---|
|
||||
| 后台看不到新驱动 | 未安装插件 / `plugin.json` 损坏 / `type` 非 `driver` | 检查安装日志;修正清单后重装或同步 |
|
||||
| `class not exists` | 目录名与 Studly(`driver_key`) 不一致;未 `composer dump-autoload` | 对齐命名空间;重建自动加载 |
|
||||
| 能下单无回调 | notify URL 配错;`channel_key` 与 URL `:type` 不一致;验签失败被当成功吞掉 | 对照第 7.3 节;看支付平台重试与服务器日志 |
|
||||
| 回调了订单未变 | 未走统一支付成功逻辑;订单号不一致;幂等误判 | 核对 `out_trade_no`;对照既有驱动事件触发方式 |
|
||||
| 退款报渠道不一致 | 支付时 channel 与退款 channel 不同;V2/V3 混用 | 以支付记录中的 `channel_key` 为准 |
|
||||
| 改了配置不生效 | 门面实例缓存了旧 config;渠道信息 Cache | 重新 make 并传入新 config;清 `PayServices` 渠道缓存 |
|
||||
| 在 `eb_driver` 查不到 | 支付不走该表 | 查 `eb_pay_channel` |
|
||||
|
||||
---
|
||||
|
||||
## 15. 推荐学习路径
|
||||
|
||||
1. 读本文第 2~3 节,搞清分层与支付字典特例。
|
||||
2. 对照 `AliPay` 走通「最小四方法」实现。
|
||||
3. 对照 `V3WechatPay` 学习 `pay_type_map`、`capabilities`、服务商与转账扩展。
|
||||
4. 跟一次真实支付:下单 → 调起 → 回调 → `OrderPaySuccess` → 退款。
|
||||
5. 用第 13 节清单做回归后再交付。
|
||||
|
||||
相关文档:
|
||||
|
||||
| 文档 | 用途 |
|
||||
|---|---|
|
||||
| `backend/driver/README.md` | 驱动底座总览与选型 |
|
||||
| `backend/driver/pay/DEVELOPMENT.md` | 支付类型字段速查 |
|
||||
| `help/docs/模块与驱动.md` | 模块/驱动清单与边界 |
|
||||
| `help/docs/项目架构.md` | 订单与退款链路 |
|
||||
| `.crmeb/skills/crmeb-plugin-dev/references/driver-plugin.md` | 驱动插件开发要点 |
|
||||
| `.crmeb/skills/crmeb-order-dev/SKILL.md` | 订单全生命周期(含支付/退款) |
|
||||
|
||||
---
|
||||
|
||||
## 16. 小结
|
||||
|
||||
支付驱动二开的本质是:**用标准包替换「供应商通信细节」,同时把业务状态机留在中台**。
|
||||
|
||||
做好三件事即可稳定落地:
|
||||
|
||||
1. **合规落位**:`driver/pay/{Name}/` + 完整 `plugin.json` + `PayInterface` 实现。
|
||||
2. **接好三线**:下单 `create`、回调 `handleNotify`(验签/幂等)、退款 `refund`/`queryRefund`。
|
||||
3. **配齐两端**:`eb_pay_channel` 配置与用户端对 `pay_status_type` 的调起逻辑。
|
||||
|
||||
按此扩展,升级主线框架时支付通道可随驱动包独立演进,避免改核心、避免渠道枚举散落全库。
|
||||
@ -31,7 +31,7 @@
|
||||
| 角色 | 推荐文档 |
|
||||
|---|---|
|
||||
| 产品/项目经理 | [项目说明](项目说明.md)、[项目功能](项目功能.md) |
|
||||
| 后端开发 | [项目架构](项目架构.md)、[模块说明](模块与驱动.md)、[数据库设计](数据库设计.md)、[API 总览](api/接口总览.md) |
|
||||
| 后端开发 | [项目架构](项目架构.md)、[模块说明](模块与驱动.md)、[支付驱动二开](支付驱动二开指南.md)、[数据库设计](数据库设计.md)、[API 总览](api/接口总览.md) |
|
||||
| 管理端开发 | [开发指南](开发指南.md)、[管理端 API](api/管理端接口.md)、[模块说明](模块与驱动.md) |
|
||||
| uni-app 开发 | [开发指南](开发指南.md)、[用户端 API](api/用户端接口.md)、[项目功能](项目功能.md) |
|
||||
| 产品/活动扩展开发 | [模块说明](模块与驱动.md)、[开发指南](开发指南.md) |
|
||||
@ -47,6 +47,7 @@
|
||||
| [项目功能](项目功能.md) | 管理端、用户端、交易、营销、渠道、客服、开放平台等功能 |
|
||||
| [项目架构](项目架构.md) | 技术栈、逻辑分层、核心链路、扩展机制、运行时组件 |
|
||||
| [模块说明](模块与驱动.md) | 22 个模块、30 个驱动、默认安装清单、安装生命周期与扩展规则 |
|
||||
| [支付驱动二开指南](支付驱动二开指南.md) | 支付驱动定位、清单字段、PayInterface、下单/回调/退款链路、配置表、红线与验收排障 |
|
||||
| [AI Agent 模块](AI%20Agent%20模块.md) | `aiagent` 模块功能详述:对话与工具循环、模型/MCP/Skill 配置、自动化任务、数据表、接口与权限、已知限制 |
|
||||
| [数据库设计](数据库设计.md) | 数据来源、核心实体、表域划分、迁移与一致性原则 |
|
||||
| [开发指南](开发指南.md) | 环境准备、启动、目录约定、开发步骤、质量检查 |
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user