mirror of
https://gitee.com/technical-laohu/mpay_v2_webman.git
synced 2026-05-09 18:34:26 +08:00
1. 维护代码健壮
2. 更新项目结构文档
This commit is contained in:
63
doc/INDEX.md
Normal file
63
doc/INDEX.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# 文档索引
|
||||
|
||||
这份索引只做导航,不重复正文。
|
||||
|
||||
## 总览
|
||||
|
||||
- [工作区 README](../README.md)
|
||||
- [文档中心](./README.md)
|
||||
- [项目总览](./overview.md)
|
||||
- [架构与请求流](./architecture.md)
|
||||
- [稳定口径](./standards.md)
|
||||
|
||||
## 分领域文档
|
||||
|
||||
- [后端总说明](./backend/README.md)
|
||||
- [后端路由](./backend/routing.md)
|
||||
- [后端服务层](./backend/services.md)
|
||||
- [后端命令](./backend/commands.md)
|
||||
- [文件资产](./backend/files.md)
|
||||
- [支付插件模板](./backend/payment-plugin-template.md)
|
||||
- [支付运行时数据契约](./backend/payment-runtime-contract.md)
|
||||
- [ePay 兼容层](./backend/compat.md)
|
||||
|
||||
- [前端总说明](./frontend/README.md)
|
||||
- [管理后台前端](./frontend/admin.md)
|
||||
- [商户后台前端](./frontend/mer.md)
|
||||
- [收银台前端](./frontend/cashier.md)
|
||||
- [菜单说明](./frontend/menu.md)
|
||||
|
||||
- [接口总说明](./api/README.md)
|
||||
- [管理后台接口](./api/admin.md)
|
||||
- [商户后台接口](./api/mer.md)
|
||||
- [收银台与开放接口](./api/cashier.md)
|
||||
- [ePay 兼容协议](./api/legacy/epay.md)
|
||||
|
||||
- [数据库总说明](./db/README.md)
|
||||
- [数据表目录](./db/tables.md)
|
||||
- [当前 DDL](./db/payment-middle-ddl.sql)
|
||||
|
||||
- [部署总说明](./deployment/README.md)
|
||||
- [后端部署](./deployment/backend.md)
|
||||
- [管理后台部署](./deployment/admin.md)
|
||||
- [商户后台部署](./deployment/mer.md)
|
||||
- [收银台部署](./deployment/cashier.md)
|
||||
- [环境变量](./deployment/env.md)
|
||||
|
||||
## 项目 README
|
||||
|
||||
- [admin](../admin/README.md)
|
||||
- [mer](../mer/README.md)
|
||||
- [cashier](../cashier/README.md)
|
||||
- [mpay](../mpay/README.md)
|
||||
|
||||
## 协作与记录
|
||||
|
||||
- [接手指南](./agent-handoff.md)
|
||||
- [阶段计划](./plan.md)
|
||||
- [常见问题](./faq.md)
|
||||
- [变更记录](./changelog.md)
|
||||
|
||||
## 归档
|
||||
|
||||
- [旧版后端文档目录](../mpay/doc/INDEX.md)
|
||||
32
doc/README.md
Normal file
32
doc/README.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# 文档中心
|
||||
|
||||
`docs/` 是 `MPAY_V2` 工作区的统一文档入口。文档只记录当前代码和配置能支撑的事实;历史资料保留在 `mpay/doc/`,不作为最新口径。
|
||||
|
||||
## 快速阅读
|
||||
|
||||
1. [总入口](../README.md)
|
||||
2. [文档索引](./INDEX.md)
|
||||
3. [项目总览](./overview.md)
|
||||
4. [架构与请求流](./architecture.md)
|
||||
5. [后端说明](./backend/README.md)
|
||||
6. [前端说明](./frontend/README.md)
|
||||
7. [接口说明](./api/README.md)
|
||||
8. [部署说明](./deployment/README.md)
|
||||
|
||||
## 文档边界
|
||||
|
||||
- `overview.md`:项目定位、应用组成、核心链路。
|
||||
- `architecture.md`:工作区结构、请求入口、后端分层。
|
||||
- `standards.md`:开发与业务稳定口径。
|
||||
- `backend/`:后端路由、服务、命令、文件与插件运行时。
|
||||
- `frontend/`:三套前端的职责、命令、接口前缀和页面入口。
|
||||
- `api/`:按 `/adminapi`、`/merapi`、`/api`、旧版 ePay 兼容入口整理接口面。
|
||||
- `db/`:当前 DDL 与表目录。
|
||||
- `deployment/`:启动、构建、部署和环境变量。
|
||||
|
||||
## 维护原则
|
||||
|
||||
- 文档和代码冲突时,以 `mpay/app/route`、前端 `src/api`、`package.json`、`composer.json`、DDL 为准。
|
||||
- 总文档只写关键事实,不复制接口字段和业务实现细节。
|
||||
- 新增或改名路由时,同步更新对应的 `api/`、`frontend/` 或 `backend/routing.md`。
|
||||
- 新增环境变量时,先改模板文件,再更新 `deployment/env.md`。
|
||||
72
doc/agent-handoff.md
Normal file
72
doc/agent-handoff.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# 协作接手指南
|
||||
|
||||
这份文档给新协作者快速接手使用。稳定事实先看 [稳定口径](./standards.md)。
|
||||
|
||||
## 先看什么
|
||||
|
||||
1. [工作区 README](../README.md)
|
||||
2. [项目总览](./overview.md)
|
||||
3. [架构与请求流](./architecture.md)
|
||||
4. [后端总说明](./backend/README.md)
|
||||
5. [前端总说明](./frontend/README.md)
|
||||
6. [接口总说明](./api/README.md)
|
||||
7. [数据库总说明](./db/README.md)
|
||||
|
||||
## 常用命令
|
||||
|
||||
后端:
|
||||
|
||||
```bash
|
||||
cd mpay
|
||||
composer install
|
||||
php webman start
|
||||
php webman mpay:test --all
|
||||
php webman system:config-sync
|
||||
```
|
||||
|
||||
前端:
|
||||
|
||||
```bash
|
||||
cd admin
|
||||
pnpm dev
|
||||
pnpm build:prod
|
||||
|
||||
cd ../mer
|
||||
pnpm dev
|
||||
pnpm build:prod
|
||||
|
||||
cd ../cashier
|
||||
pnpm dev
|
||||
pnpm build
|
||||
```
|
||||
|
||||
## 不要搞混的边界
|
||||
|
||||
- `admin`:页面 `/admin`,接口 `/adminapi`。
|
||||
- `mer`:页面 `/mer`,接口 `/merapi`。
|
||||
- `cashier`:页面 `/cashier`、`/payment`,接口 `/api/cashier`。
|
||||
- ePay V1:`/submit.php`、`/mapi.php`、`/api.php`。
|
||||
- ePay V2:`/api/pay`、`/api/merchant`、`/api/transfer`。
|
||||
- `mpay/doc/` 是旧资料归档,最新文档在 `docs/`。
|
||||
|
||||
## 优先查看的代码
|
||||
|
||||
- `mpay/app/route/admin.php`
|
||||
- `mpay/app/route/mer.php`
|
||||
- `mpay/app/route/api.php`
|
||||
- `mpay/app/service/payment/order/PayOrderService.php`
|
||||
- `mpay/app/service/payment/order/RefundService.php`
|
||||
- `mpay/app/service/payment/runtime/PaymentRouteService.php`
|
||||
- `mpay/app/service/payment/runtime/PaymentPluginManager.php`
|
||||
- `mpay/app/service/payment/cashier/CashierService.php`
|
||||
- `mpay/app/service/merchant/portal/MerchantPortalService.php`
|
||||
- `admin/src/api/modules`
|
||||
- `mer/src/api/modules`
|
||||
- `cashier/src/api/cashier.ts`
|
||||
|
||||
## 协作原则
|
||||
|
||||
- 文档和代码冲突时,先以代码为准,再修正文档。
|
||||
- 接口入口以 `mpay/app/route` 为准。
|
||||
- 前端 API 前缀以各项目 `src/api/index.ts` 为准。
|
||||
- 不在总文档重复接口字段,字段细节看控制器、校验器、协议文档和 DDL。
|
||||
21
doc/api/README.md
Normal file
21
doc/api/README.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# 接口总说明
|
||||
|
||||
接口按消费方和协议分组,真实路由以 `mpay/app/route/*.php` 为准。
|
||||
|
||||
## 当前路由面
|
||||
|
||||
| 路由 | 用途 | 文档 |
|
||||
| --- | --- | --- |
|
||||
| `/adminapi` | 管理后台接口 | [admin.md](./admin.md) |
|
||||
| `/merapi` | 商户后台接口 | [mer.md](./mer.md) |
|
||||
| `/api/cashier` | 收银台前端 JSON 接口 | [cashier.md](./cashier.md) |
|
||||
| `/api/pay`、`/api/merchant`、`/api/transfer` | ePay V2 / 开放 API | [cashier.md](./cashier.md)、[legacy/epay.md](./legacy/epay.md) |
|
||||
| `/submit.php`、`/mapi.php`、`/api.php` | ePay V1 兼容入口 | [legacy/epay.md](./legacy/epay.md) |
|
||||
|
||||
## 通用约束
|
||||
|
||||
- HTTP 成功态使用 `200`。
|
||||
- 业务成功由响应体 `code=200` 表示。
|
||||
- 后台类接口通过登录 token 鉴权。
|
||||
- 开放支付接口通过商户 API 凭证和 ePay 签名规则鉴权。
|
||||
- 接口字段不要在总文档重复铺开,按具体协议文档或控制器/校验器查看。
|
||||
38
doc/api/admin.md
Normal file
38
doc/api/admin.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# 管理后台接口
|
||||
|
||||
`admin` 前端调用 `/adminapi`,接口定义在 `mpay/app/route/admin.php`。
|
||||
|
||||
## 基本信息
|
||||
|
||||
- 页面入口:`/admin`
|
||||
- API 前缀:`/adminapi`
|
||||
- 登录接口:`POST /login`
|
||||
- 保护接口:`AdminAuthMiddleware`
|
||||
- 前端封装:`admin/src/api/modules/*`
|
||||
|
||||
## 模块速览
|
||||
|
||||
| 模块 | 主要路径 |
|
||||
| --- | --- |
|
||||
| 认证 | `/login`、`/logout`、`/user/profile` |
|
||||
| 管理员 | `/admin-users` |
|
||||
| 商户 | `/merchants`、`/merchants/{id}/overview`、`/merchants/{id}/reset-password`、`/merchants/{id}/issue-credential` |
|
||||
| 商户 API 凭证 | `/merchant-api-credentials` |
|
||||
| 商户分组与策略 | `/merchant-groups`、`/merchant-policies` |
|
||||
| 支付方式 | `/payment-types` |
|
||||
| 支付插件 | `/payment-plugins`、`/payment-plugins/refresh`、`/payment-plugins/{code}/schema` |
|
||||
| 插件配置 | `/payment-plugin-confs` |
|
||||
| 支付通道 | `/payment-channels`、`/payment-channels/route-options` |
|
||||
| 轮询组 | `/payment-poll-groups`、`/payment-poll-group-channels`、`/payment-poll-group-binds` |
|
||||
| 路由预览 | `/routes/resolve` |
|
||||
| 文件资产 | `/file-asset`、`/file-asset/upload`、`/file-asset/import-remote`、`/file-asset/{id}/preview`、`/file-asset/{id}/download` |
|
||||
| 交易 | `/pay-orders`、`/refund-orders`、`/refund-orders/{refundNo}/retry`、`/settlement-orders` |
|
||||
| 资金 | `/merchant-accounts`、`/merchant-accounts/summary`、`/account-ledgers` |
|
||||
| 运维 | `/channel-daily-stats`、`/channel-notify-logs`、`/pay-callback-logs`、`/merchant-notify-tasks`、`/merchant-notify-tasks/{notifyNo}/retry` |
|
||||
| 系统 | `/system/menu-tree`、`/system/dict-items`、`/system-config-pages` |
|
||||
|
||||
## 关联代码
|
||||
|
||||
- 控制器:`mpay/app/http/admin/controller`
|
||||
- 校验器:`mpay/app/http/admin/validation`
|
||||
- 前端接口:`admin/src/api/modules`
|
||||
42
doc/api/cashier.md
Normal file
42
doc/api/cashier.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# 收银台与开放接口
|
||||
|
||||
本文覆盖 `cashier` 前端使用的收银台接口,以及后端在 `/api` 下暴露的 ePay V2 / 开放接口。
|
||||
|
||||
## 页面入口
|
||||
|
||||
| 页面前缀 | 说明 |
|
||||
| --- | --- |
|
||||
| `/cashier` | 收银台首页和业务单入口 |
|
||||
| `/payment` | 支付页、中转页、结果页 |
|
||||
|
||||
后端在 `mpay/app/route/api.php` 中读取 `public/cashier/index.html` 返回前端入口。
|
||||
|
||||
## 收银台 JSON API
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `GET` | `/api/cashier/context` | 根据 `biz_no` 获取收银台上下文 |
|
||||
| `POST` | `/api/cashier/confirm` | 确认支付,参数包含 `biz_no` 和支付类型 |
|
||||
| `GET` | `/api/cashier/pay-order` | 根据 `pay_no` 获取支付单详情 |
|
||||
|
||||
对应前端封装:`cashier/src/api/cashier.ts`。对应后端控制器:`CashierController`。
|
||||
|
||||
## ePay V2 / 开放 API
|
||||
|
||||
| 分组 | 方法与路径 |
|
||||
| --- | --- |
|
||||
| 支付 | `ANY /api/pay/submit`、`POST /api/pay/create`、`POST /api/pay/query`、`POST /api/pay/refund`、`POST /api/pay/refundquery`、`POST /api/pay/close`、`ANY /api/pay/{payNo}/callback` |
|
||||
| 商户 | `POST /api/merchant/info`、`POST /api/merchant/orders` |
|
||||
| 转账 | `POST /api/transfer/submit`、`POST /api/transfer/query`、`POST /api/transfer/balance` |
|
||||
|
||||
对应控制器:`EpayV2Controller`。签名与参数以 [ePay 兼容协议](./legacy/epay.md) 和校验器为准。
|
||||
|
||||
## ePay V1 兼容入口
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `ANY` | `/submit.php` | 页面跳转支付 |
|
||||
| `POST` | `/mapi.php` | 接口支付 |
|
||||
| `ANY` | `/api.php` | 标准 API |
|
||||
|
||||
对应控制器:`EpayV1Controller`。
|
||||
14
doc/api/legacy/epay.md
Normal file
14
doc/api/legacy/epay.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# ePay 兼容协议归档
|
||||
|
||||
这里是 ePay 兼容协议的总入口索引。
|
||||
|
||||
## 版本列表
|
||||
|
||||
- [ePay V1 文档](./epay_v1.md)
|
||||
- [ePay V2 文档](./epay_v2.md)
|
||||
|
||||
## 说明
|
||||
|
||||
- `epay_v1.md` 对应原站点旧版 `doc_old.html`,协议以 `MD5` 为主。
|
||||
- `epay_v2.md` 对应原站点新版 `doc/index.html`,协议以 `RSA` 为主。
|
||||
- 其他文档仍可继续引用这个索引页,避免入口分散。
|
||||
332
doc/api/legacy/epay_v1.md
Normal file
332
doc/api/legacy/epay_v1.md
Normal file
@@ -0,0 +1,332 @@
|
||||
# ePay V1 接口文档整理
|
||||
|
||||
> 本文件整理自原版文档 [https://epay.qcjy.cc/doc_old.html](https://epay.qcjy.cc/doc_old.html)
|
||||
>
|
||||
> 用途:作为 V1 兼容层和老接口迁移时的协议参考,不包含本项目当前实现细节。
|
||||
|
||||
## 1. 协议规则
|
||||
|
||||
| 项目 | 值 |
|
||||
| --- | --- |
|
||||
| 请求数据格式 | `application/x-www-form-urlencoded` |
|
||||
| 返回数据格式 | `JSON` |
|
||||
| 签名算法 | `MD5` |
|
||||
| 字符编码 | `UTF-8` |
|
||||
|
||||
## 2. 接口总览
|
||||
|
||||
| 入口 | 说明 |
|
||||
| --- | --- |
|
||||
| `submit.php` | 页面跳转支付 |
|
||||
| `mapi.php` | API 接口支付 |
|
||||
| `api.php?act=query` | 查询商户信息 |
|
||||
| `api.php?act=settle` | 查询结算记录 |
|
||||
| `api.php?act=order` | 查询单个订单 |
|
||||
| `api.php?act=orders` | 批量查询订单 |
|
||||
| `api.php?act=refund` | 提交订单退款 |
|
||||
|
||||
## 3. 页面跳转支付
|
||||
|
||||
### 3.1 接口说明
|
||||
|
||||
- 用途:用户前台直接发起支付
|
||||
- 常见调用方式:`form` 表单提交,或拼成跳转链接
|
||||
- URL:`http://epay.qcjy.cc/submit.php`
|
||||
- 请求方式:`POST` 或 `GET`
|
||||
- 推荐方式:`POST`
|
||||
- `type` 不传时,默认进入收银台流程
|
||||
|
||||
### 3.2 请求参数
|
||||
|
||||
| 字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 商户ID | `pid` | 是 | `Int` | `1001` | 商户ID |
|
||||
| 支付方式 | `type` | 否 | `String` | `alipay` | 支付方式列表 |
|
||||
| 商户订单号 | `out_trade_no` | 是 | `String` | `20160806151343349` | 商户系统内部订单号 |
|
||||
| 异步通知地址 | `notify_url` | 是 | `String` | `http://www.pay.com/notify_url.php` | 服务器异步通知地址 |
|
||||
| 跳转通知地址 | `return_url` | 是 | `String` | `http://www.pay.com/return_url.php` | 页面跳转通知地址 |
|
||||
| 商品名称 | `name` | 是 | `String` | `VIP会员` | 超过 127 个字节会自动截取 |
|
||||
| 商品金额 | `money` | 是 | `String` | `1.00` | 单位:元,最多 2 位小数 |
|
||||
| 业务扩展参数 | `param` | 否 | `String` | `没有请留空` | 支付后原样返回 |
|
||||
| 签名字符串 | `sign` | 是 | `String` | `202cb962ac59075b964b07152d234b70` | 签名结果 |
|
||||
| 签名类型 | `sign_type` | 是 | `String` | `MD5` | 默认为 `MD5` |
|
||||
|
||||
### 3.3 说明
|
||||
|
||||
- `notify_url` 用于异步通知
|
||||
- `return_url` 用于支付完成后的前端跳转
|
||||
- `param` 会在支付完成后原样返回
|
||||
|
||||
## 4. API 接口支付
|
||||
|
||||
### 4.1 接口说明
|
||||
|
||||
- 用途:服务器后端发起支付请求
|
||||
- URL:`http://epay.qcjy.cc/mapi.php`
|
||||
- 请求方式:`POST`
|
||||
- 响应通常返回跳转链接、二维码链接或小程序跳转链接中的一种
|
||||
|
||||
### 4.2 请求参数
|
||||
|
||||
| 字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 商户ID | `pid` | 是 | `Int` | `1001` | 商户ID |
|
||||
| 支付方式 | `type` | 是 | `String` | `alipay` | 支付方式列表 |
|
||||
| 商户订单号 | `out_trade_no` | 是 | `String` | `20160806151343349` | 商户系统内部订单号 |
|
||||
| 异步通知地址 | `notify_url` | 是 | `String` | `http://www.pay.com/notify_url.php` | 服务器异步通知地址 |
|
||||
| 跳转通知地址 | `return_url` | 否 | `String` | `http://www.pay.com/return_url.php` | 页面跳转通知地址 |
|
||||
| 商品名称 | `name` | 是 | `String` | `VIP会员` | 超过 127 个字节会自动截取 |
|
||||
| 商品金额 | `money` | 是 | `String` | `1.00` | 单位:元,最多 2 位小数 |
|
||||
| 用户IP地址 | `clientip` | 是 | `String` | `192.168.1.100` | 用户发起支付的 IP |
|
||||
| 设备类型 | `device` | 否 | `String` | `pc` | 根据 UA 判断,默认 `pc` |
|
||||
| 业务扩展参数 | `param` | 否 | `String` | `没有请留空` | 支付后原样返回 |
|
||||
| 签名字符串 | `sign` | 是 | `String` | `202cb962ac59075b964b07152d234b70` | 签名结果 |
|
||||
| 签名类型 | `sign_type` | 是 | `String` | `MD5` | 默认为 `MD5` |
|
||||
|
||||
### 4.3 返回结果
|
||||
|
||||
| 字段名 | 变量名 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 返回状态码 | `code` | `Int` | `1` | `1` 为成功,其它值为失败 |
|
||||
| 返回信息 | `msg` | `String` | | 失败时返回原因 |
|
||||
| 订单号 | `trade_no` | `String` | `20160806151343349` | 支付订单号 |
|
||||
| 支付跳转url | `payurl` | `String` | `http://epay.qcjy.cc/pay/wxpay/202010903/` | 直接跳转该 URL 支付 |
|
||||
| 二维码链接 | `qrcode` | `String` | `weixin://wxpay/bizpayurl?pr=04IPMKM` | 按链接生成二维码 |
|
||||
| 小程序跳转url | `urlscheme` | `String` | `weixin://dl/business/?ticket=xxx` | 使用 JS 跳转该 URL |
|
||||
|
||||
### 4.4 说明
|
||||
|
||||
- `payurl`、`qrcode`、`urlscheme` 三者只会返回其中一个
|
||||
- `device` 和支付方式会影响最终返回值类型
|
||||
- 返回值为 JSON,由调用方自行决定跳转、展示二维码或拉起小程序
|
||||
|
||||
## 5. 支付结果通知
|
||||
|
||||
### 5.1 通知类型
|
||||
|
||||
- 服务器异步通知:`notify_url`
|
||||
- 页面跳转通知:`return_url`
|
||||
|
||||
### 5.2 请求方式
|
||||
|
||||
- `GET`
|
||||
|
||||
### 5.3 请求参数
|
||||
|
||||
| 字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 商户ID | `pid` | 是 | `Int` | `1001` | 商户ID |
|
||||
| 易支付订单号 | `trade_no` | 是 | `String` | `20160806151343349021` | 平台订单号 |
|
||||
| 商户订单号 | `out_trade_no` | 是 | `String` | `20160806151343349` | 商户系统内部订单号 |
|
||||
| 支付方式 | `type` | 是 | `String` | `alipay` | 支付方式列表 |
|
||||
| 商品名称 | `name` | 是 | `String` | `VIP会员` | 商品名称 |
|
||||
| 商品金额 | `money` | 是 | `String` | `1.00` | 商品金额 |
|
||||
| 支付状态 | `trade_status` | 是 | `String` | `TRADE_SUCCESS` | 只有 `TRADE_SUCCESS` 才表示成功 |
|
||||
| 业务扩展参数 | `param` | 否 | `String` | | 业务参数 |
|
||||
| 签名字符串 | `sign` | 是 | `String` | `202cb962ac59075b964b07152d234b70` | 签名结果 |
|
||||
| 签名类型 | `sign_type` | 是 | `String` | `MD5` | 默认为 `MD5` |
|
||||
|
||||
### 5.4 回调响应
|
||||
|
||||
- 收到异步通知后,需返回 `success`
|
||||
- 页面跳转通知主要用于前端展示,不代表后台最终确认逻辑
|
||||
|
||||
## 6. MD5 签名算法
|
||||
|
||||
1. 将发送或接收到的所有参数按照参数名 ASCII 码从小到大排序。
|
||||
2. `sign`、`sign_type` 和空值不参与签名。
|
||||
3. 将排序后的参数拼接成 `a=b&c=d&e=f` 的形式,参数值不要进行 URL 编码。
|
||||
4. 将拼接字符串与商户密钥 `KEY` 进行 MD5 加密,得到签名。
|
||||
|
||||
```text
|
||||
sign = md5(a=b&c=d&e=f + KEY)
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
- `+` 代表字符串拼接,不是字符本身
|
||||
- MD5 结果为小写
|
||||
- 具体示例以 SDK 为准
|
||||
|
||||
## 7. 支付方式列表
|
||||
|
||||
| 调用值 | 描述 |
|
||||
| --- | --- |
|
||||
| `alipay` | 支付宝 |
|
||||
| `wxpay` | 微信支付 |
|
||||
| `qqpay` | QQ 钱包 |
|
||||
|
||||
## 8. 设备类型列表
|
||||
|
||||
| 调用值 | 描述 |
|
||||
| --- | --- |
|
||||
| `pc` | 电脑浏览器 |
|
||||
| `mobile` | 手机浏览器 |
|
||||
| `qq` | 手机 QQ 内浏览器 |
|
||||
| `wechat` | 微信内浏览器 |
|
||||
| `alipay` | 支付宝客户端 |
|
||||
| `jump` | 仅返回支付跳转 URL |
|
||||
|
||||
## 9. [API]查询商户信息
|
||||
|
||||
### 9.1 接口说明
|
||||
|
||||
- URL:`http://epay.qcjy.cc/api.php?act=query&pid={商户ID}&key={商户密钥}`
|
||||
- 操作类型:`query`
|
||||
|
||||
### 9.2 请求参数
|
||||
|
||||
| 字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 操作类型 | `act` | 是 | `String` | `query` | 固定值 |
|
||||
| 商户ID | `pid` | 是 | `Int` | `1001` | 商户ID |
|
||||
| 商户密钥 | `key` | 是 | `String` | `89unJUB8HZ54Hj7x4nUj56HN4nUzUJ8i` | 商户密钥 |
|
||||
|
||||
### 9.3 返回结果
|
||||
|
||||
| 字段名 | 变量名 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 返回状态码 | `code` | `Int` | `1` | `1` 为成功,其它值为失败 |
|
||||
| 商户ID | `pid` | `Int` | `1001` | 商户ID |
|
||||
| 商户密钥 | `key` | `String(32)` | `89unJUB8HZ54Hj7x4nUj56HN4nUzUJ8i` | 商户密钥 |
|
||||
| 商户状态 | `active` | `Int` | `1` | `1` 为正常,`0` 为封禁 |
|
||||
| 商户余额 | `money` | `String` | `0.00` | 商户所拥有的余额 |
|
||||
| 结算方式 | `type` | `Int` | `1` | `1:支付宝,2:微信,3:QQ,4:银行卡` |
|
||||
| 结算账号 | `account` | `String` | `admin@pay.com` | 结算账号 |
|
||||
| 结算姓名 | `username` | `String` | `张三` | 结算姓名 |
|
||||
| 订单总数 | `orders` | `Int` | `30` | 订单总数统计 |
|
||||
| 今日订单 | `order_today` | `Int` | `15` | 今日订单数量 |
|
||||
| 昨日订单 | `order_lastday` | `Int` | `15` | 昨日订单数量 |
|
||||
|
||||
## 10. [API]查询结算记录
|
||||
|
||||
### 10.1 接口说明
|
||||
|
||||
- URL:`http://epay.qcjy.cc/api.php?act=settle&pid={商户ID}&key={商户密钥}`
|
||||
- 操作类型:`settle`
|
||||
|
||||
### 10.2 请求参数
|
||||
|
||||
| 字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 操作类型 | `act` | 是 | `String` | `settle` | 固定值 |
|
||||
| 商户ID | `pid` | 是 | `Int` | `1001` | 商户ID |
|
||||
| 商户密钥 | `key` | 是 | `String` | `89unJUB8HZ54Hj7x4nUj56HN4nUzUJ8i` | 商户密钥 |
|
||||
|
||||
### 10.3 返回结果
|
||||
|
||||
| 字段名 | 变量名 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 返回状态码 | `code` | `Int` | `1` | `1` 为成功,其它值为失败 |
|
||||
| 返回信息 | `msg` | `String` | `查询结算记录成功!` | 提示信息 |
|
||||
| 结算记录 | `data` | `Array` | 结算记录列表 | 结算记录数组 |
|
||||
|
||||
> 原版文档对 `data` 内部结构只给出“结算记录列表”的说明,没有展开逐字段定义。
|
||||
|
||||
## 11. [API]查询单个订单
|
||||
|
||||
### 11.1 接口说明
|
||||
|
||||
- URL:`http://epay.qcjy.cc/api.php?act=order&pid={商户ID}&key={商户密钥}&out_trade_no={商户订单号}`
|
||||
- 操作类型:`order`
|
||||
|
||||
### 11.2 请求参数
|
||||
|
||||
| 字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 操作类型 | `act` | 是 | `String` | `order` | 固定值 |
|
||||
| 商户ID | `pid` | 是 | `Int` | `1001` | 商户ID |
|
||||
| 商户密钥 | `key` | 是 | `String` | `89unJUB8HZ54Hj7x4nUj56HN4nUzUJ8i` | 商户密钥 |
|
||||
| 系统订单号 | `trade_no` | 选择 | `String` | `20160806151343312` | 平台订单号 |
|
||||
| 商户订单号 | `out_trade_no` | 选择 | `String` | `20160806151343349` | 商户自定义订单号 |
|
||||
|
||||
说明:
|
||||
|
||||
- `trade_no` 和 `out_trade_no` 二选一即可
|
||||
- 如果都传入,以 `trade_no` 为准
|
||||
|
||||
### 11.3 返回结果
|
||||
|
||||
| 字段名 | 变量名 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 返回状态码 | `code` | `Int` | `1` | `1` 为成功,其它值为失败 |
|
||||
| 返回信息 | `msg` | `String` | `查询订单号成功!` | 提示信息 |
|
||||
| 易支付订单号 | `trade_no` | `String` | `2016080622555342651` | 平台订单号 |
|
||||
| 商户订单号 | `out_trade_no` | `String` | `20160806151343349` | 商户系统内部订单号 |
|
||||
| 第三方订单号 | `api_trade_no` | `String` | `20160806151343349` | 支付渠道订单号 |
|
||||
| 支付方式 | `type` | `String` | `alipay` | 支付方式列表 |
|
||||
| 商户ID | `pid` | `Int` | `1001` | 发起支付的商户ID |
|
||||
| 创建订单时间 | `addtime` | `String` | `2016-08-06 22:55:52` | 创建时间 |
|
||||
| 完成交易时间 | `endtime` | `String` | `2016-08-06 22:55:52` | 完成时间 |
|
||||
| 商品名称 | `name` | `String` | `VIP会员` | 商品名称 |
|
||||
| 商品金额 | `money` | `String` | `1.00` | 商品金额 |
|
||||
| 支付状态 | `status` | `Int` | `0` | `1` 表示支付成功,`0` 表示未支付 |
|
||||
| 业务扩展参数 | `param` | `String` | | 默认留空 |
|
||||
| 支付者账号 | `buyer` | `String` | | 默认留空 |
|
||||
|
||||
## 12. [API]批量查询订单
|
||||
|
||||
### 12.1 接口说明
|
||||
|
||||
- URL:`http://epay.qcjy.cc/api.php?act=orders&pid={商户ID}&key={商户密钥}`
|
||||
- 操作类型:`orders`
|
||||
|
||||
### 12.2 请求参数
|
||||
|
||||
| 字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 操作类型 | `act` | 是 | `String` | `orders` | 固定值 |
|
||||
| 商户ID | `pid` | 是 | `Int` | `1001` | 商户ID |
|
||||
| 商户密钥 | `key` | 是 | `String` | `89unJUB8HZ54Hj7x4nUj56HN4nUzUJ8i` | 商户密钥 |
|
||||
| 查询订单数量 | `limit` | 否 | `Int` | `20` | 返回订单数量,最大 `50` |
|
||||
| 页码 | `page` | 否 | `Int` | `1` | 当前查询页码 |
|
||||
|
||||
### 12.3 返回结果
|
||||
|
||||
| 字段名 | 变量名 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 返回状态码 | `code` | `Int` | `1` | `1` 为成功,其它值为失败 |
|
||||
| 返回信息 | `msg` | `String` | `查询结算记录成功!` | 提示信息 |
|
||||
| 订单列表 | `data` | `Array` | 订单列表 | 订单数组 |
|
||||
|
||||
> `data` 中每一项与“查询单个订单”返回结构基本一致。
|
||||
|
||||
## 13. [API]提交订单退款
|
||||
|
||||
### 13.1 接口说明
|
||||
|
||||
- 需要先在商户后台开启订单退款 API 接口开关
|
||||
- URL:`http://epay.qcjy.cc/api.php?act=refund`
|
||||
- 请求方式:`POST`
|
||||
|
||||
### 13.2 请求参数
|
||||
|
||||
| 字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 商户ID | `pid` | 是 | `Int` | `1001` | 商户ID |
|
||||
| 商户密钥 | `key` | 是 | `String` | `89unJUB8HZ54Hj7x4nUj56HN4nUzUJ8i` | 商户密钥 |
|
||||
| 易支付订单号 | `trade_no` | 特殊可选 | `String` | `20160806151343349021` | 平台订单号 |
|
||||
| 商户订单号 | `out_trade_no` | 特殊可选 | `String` | `20160806151343349` | 下单时传入的商户订单号 |
|
||||
| 退款金额 | `money` | 是 | `String` | `1.50` | 少数通道需要与原订单金额一致 |
|
||||
|
||||
说明:
|
||||
|
||||
- `trade_no` 和 `out_trade_no` 不能同时为空
|
||||
- 如果都传了,以 `trade_no` 为准
|
||||
|
||||
### 13.3 返回结果
|
||||
|
||||
| 字段名 | 变量名 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 返回状态码 | `code` | `Int` | `1` | `1` 为成功,其它值为失败 |
|
||||
| 返回信息 | `msg` | `String` | `退款成功` | 提示信息 |
|
||||
|
||||
## 14. SDK 下载
|
||||
|
||||
- 下载地址:[`SDK.zip`](https://epay.qcjy.cc/assets/files/SDK.zip?_v=1.3)
|
||||
- 版本:`V1.3`
|
||||
|
||||
## 15. 备注
|
||||
|
||||
- 原版文档页面还包含“产品”“关于我们”“联系我们”等站点导航内容,这里不做展开
|
||||
- 如果需要继续对照更多说明,优先以原站点文档为准
|
||||
518
doc/api/legacy/epay_v2.md
Normal file
518
doc/api/legacy/epay_v2.md
Normal file
@@ -0,0 +1,518 @@
|
||||
# ePay V2 接口文档整理
|
||||
|
||||
> 本文件整理自原版文档 [https://epay.qcjy.cc/doc/index.html](https://epay.qcjy.cc/doc/index.html)
|
||||
>
|
||||
> 用途:作为 V2 兼容层和新接口迁移时的协议参考,不包含本项目当前实现细节。
|
||||
|
||||
## 1. 协议规则
|
||||
|
||||
| 项目 | 值 |
|
||||
| --- | --- |
|
||||
| 请求数据格式 | `application/x-www-form-urlencoded` |
|
||||
| 返回数据格式 | `JSON` |
|
||||
| 签名算法 | `SHA256WithRSA` |
|
||||
| 字符编码 | `UTF-8` |
|
||||
|
||||
## 2. V2 升级说明
|
||||
|
||||
1. V2 全面使用 RSA 签名,V1 保留 MD5 签名。
|
||||
2. V2 改用新的接口地址,支持退款、代付等能力。
|
||||
3. V2 增加 `timestamp` 参数和返回值,用于时间戳校验。
|
||||
|
||||
## 3. RSA 密钥对
|
||||
|
||||
- 在商户后台的 API 信息页面生成商户 RSA 密钥对。
|
||||
- 商户需要妥善保管私钥。
|
||||
- 对接时通常会同时使用平台公钥和商户私钥。
|
||||
|
||||
## 4. 签名规则
|
||||
|
||||
### 4.1 请求签名
|
||||
|
||||
1. 取所有非空参数。
|
||||
2. 排除 `sign`、`sign_type`。
|
||||
3. 排除数组、文件、二进制等非普通字段。
|
||||
4. 按参数名 ASCII 升序排序。
|
||||
5. 以 `k=v&k2=v2...` 的方式拼接原文。
|
||||
6. 使用商户私钥进行 `SHA256WithRSA` 签名。
|
||||
|
||||
### 4.2 验签规则
|
||||
|
||||
1. 按相同规则整理原文。
|
||||
2. 使用平台公钥验证签名。
|
||||
3. 请求和响应都应做签名校验。
|
||||
|
||||
## 5. 支付方式列表
|
||||
|
||||
| 调用值 | 描述 |
|
||||
| --- | --- |
|
||||
| `alipay` | 支付宝 |
|
||||
| `wxpay` | 微信支付 |
|
||||
| `qqpay` | QQ 钱包 |
|
||||
|
||||
## 6. 设备类型列表
|
||||
|
||||
| 调用值 | 描述 |
|
||||
| --- | --- |
|
||||
| `pc` | 电脑浏览器 |
|
||||
| `mobile` | 手机浏览器 |
|
||||
| `qq` | 手机 QQ 内浏览器 |
|
||||
| `wechat` | 微信内浏览器 |
|
||||
| `alipay` | 支付宝客户端 |
|
||||
|
||||
## 7. 接口总览
|
||||
|
||||
| 入口 | 说明 |
|
||||
| --- | --- |
|
||||
| `submit.php` / `/api/pay/submit` | 页面跳转支付 |
|
||||
| `/api/pay/create` | API 创建订单 |
|
||||
| `/api/pay/query` | 查询订单 |
|
||||
| `/api/pay/notify` | 支付结果通知 |
|
||||
| `/api/pay/refund` | 退款 |
|
||||
| `/api/pay/refundquery` | 退款查询 |
|
||||
| `/api/pay/close` | 关闭订单 |
|
||||
| `/api/merchant/info` | 查询商户信息 |
|
||||
| `/api/merchant/orders` | 查询商户订单 |
|
||||
| `/api/transfer/submit` | 提交转账 |
|
||||
| `/api/transfer/query` | 查询转账 |
|
||||
| `/api/transfer/balance` | 查询转账余额 |
|
||||
|
||||
## 8. 页面跳转支付
|
||||
|
||||
### 8.1 接口说明
|
||||
|
||||
- URL:`http://epay.qcjy.cc/api/pay/submit`
|
||||
- 用途:前台页面跳转支付
|
||||
- 请求方式:`POST`
|
||||
- `type` 可不传,不传时进入收银台
|
||||
|
||||
### 8.2 请求参数
|
||||
|
||||
| 字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 商户ID | `pid` | 是 | `Int` | `1001` | 商户ID |
|
||||
| 支付方式 | `type` | 否 | `String` | `alipay` | 支付方式列表 |
|
||||
| 商户订单号 | `out_trade_no` | 是 | `String` | `20160806151343349` | 商户系统内部订单号 |
|
||||
| 异步通知地址 | `notify_url` | 是 | `String` | `http://www.pay.com/notify_url.php` | 异步通知地址 |
|
||||
| 跳转通知地址 | `return_url` | 是 | `String` | `http://www.pay.com/return_url.php` | 页面跳转通知地址 |
|
||||
| 商品名称 | `name` | 是 | `String` | `VIP会员` | 商品名称 |
|
||||
| 商品金额 | `money` | 是 | `String` | `1.00` | 单位:元 |
|
||||
| 业务扩展参数 | `param` | 否 | `String` | | 支付后原样返回 |
|
||||
| 渠道ID | `channel_id` | 否 | `String` | `1001` | 指定支付渠道 |
|
||||
| 时间戳 | `timestamp` | 是 | `String` | `1713660000` | 用于时间校验 |
|
||||
| 签名字符串 | `sign` | 是 | `String` | `...` | 签名结果 |
|
||||
| 签名类型 | `sign_type` | 是 | `String` | `SHA256WithRSA` | 签名类型 |
|
||||
|
||||
## 9. API 创建订单
|
||||
|
||||
### 9.1 接口说明
|
||||
|
||||
- URL:`http://epay.qcjy.cc/api/pay/create`
|
||||
- 用途:服务器端统一创建订单
|
||||
- 请求方式:`POST`
|
||||
|
||||
### 9.2 请求参数
|
||||
|
||||
| 字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 商户ID | `pid` | 是 | `Int` | `1001` | 商户ID |
|
||||
| 接口类型 | `method` | 是 | `String` | `web` | `web`、`jump`、`jsapi`、`app`、`scan`、`applet` |
|
||||
| 设备类型 | `device` | 否 | `String` | `pc` | `pc`、`mobile`、`qq`、`wechat`、`alipay` |
|
||||
| 支付方式 | `type` | 是 | `String` | `alipay` | 支付方式列表 |
|
||||
| 商户订单号 | `out_trade_no` | 是 | `String` | `20160806151343349` | 商户系统内部订单号 |
|
||||
| 异步通知地址 | `notify_url` | 是 | `String` | `http://www.pay.com/notify_url.php` | 异步通知地址 |
|
||||
| 跳转通知地址 | `return_url` | 否 | `String` | `http://www.pay.com/return_url.php` | 页面跳转通知地址 |
|
||||
| 商品名称 | `name` | 是 | `String` | `VIP会员` | 商品名称 |
|
||||
| 商品金额 | `money` | 是 | `String` | `1.00` | 单位:元 |
|
||||
| 用户IP地址 | `clientip` | 否 | `String` | `127.0.0.1` | 用户发起支付的 IP |
|
||||
| 业务扩展参数 | `param` | 否 | `String` | | 支付后原样返回 |
|
||||
| 授权码 | `auth_code` | 否 | `String` | | JSAPI / 刷脸类场景使用 |
|
||||
| 子用户 OPENID | `sub_openid` | 否 | `String` | | 微信相关场景使用 |
|
||||
| 子应用 APPID | `sub_appid` | 否 | `String` | | 微信相关场景使用 |
|
||||
| 渠道ID | `channel_id` | 否 | `String` | `1001` | 指定通道 |
|
||||
| 时间戳 | `timestamp` | 是 | `String` | `1713660000` | 用于时间校验 |
|
||||
| 签名字符串 | `sign` | 是 | `String` | `...` | 签名结果 |
|
||||
| 签名类型 | `sign_type` | 是 | `String` | `SHA256WithRSA` | 签名类型 |
|
||||
|
||||
### 9.3 返回结果
|
||||
|
||||
| 字段名 | 变量名 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 返回状态码 | `code` | `Int` | `0` | `0` 为成功,其它值为失败 |
|
||||
| 返回信息 | `msg` | `String` | `success` | 提示信息 |
|
||||
| 平台订单号 | `trade_no` | `String` | `2016080622555342651` | 易支付订单号 |
|
||||
| 返回类型 | `pay_type` | `String` | `qrcode` | 返回内容类型 |
|
||||
| 支付内容 | `pay_info` | `Mixed` | | 跳转链接、二维码内容或 JSAPI 参数 |
|
||||
| 时间戳 | `timestamp` | `String` | `1713660000` | 返回时间戳 |
|
||||
| 签名字符串 | `sign` | `String` | `...` | 签名结果 |
|
||||
| 签名类型 | `sign_type` | `String` | `SHA256WithRSA` | 签名类型 |
|
||||
|
||||
### 9.4 `pay_type` 说明
|
||||
|
||||
| 值 | 说明 |
|
||||
| --- | --- |
|
||||
| `jump` | 跳转链接 |
|
||||
| `html` | HTML 片段 |
|
||||
| `qrcode` | 二维码内容 |
|
||||
| `urlscheme` | 小程序 URL Scheme |
|
||||
| `jsapi` | JSAPI 参数 |
|
||||
| `app` | APP 调起参数 |
|
||||
| `scan` | 扫码支付结果信息 |
|
||||
| `wxplugin` | 小程序插件参数 |
|
||||
| `wxapp` | APP 拉起小程序参数 |
|
||||
|
||||
## 10. 订单查询
|
||||
|
||||
### 10.1 接口说明
|
||||
|
||||
- URL:`http://epay.qcjy.cc/api/pay/query`
|
||||
- 请求方式:`POST`
|
||||
|
||||
### 10.2 请求参数
|
||||
|
||||
| 字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 商户ID | `pid` | 是 | `Int` | `1001` | 商户ID |
|
||||
| 平台订单号 | `trade_no` | 二选一 | `String` | `2016080622555342651` | 易支付订单号 |
|
||||
| 商户订单号 | `out_trade_no` | 二选一 | `String` | `20160806151343349` | 商户系统内部订单号 |
|
||||
| 时间戳 | `timestamp` | 是 | `String` | `1713660000` | 用于时间校验 |
|
||||
| 签名字符串 | `sign` | 是 | `String` | `...` | 签名结果 |
|
||||
| 签名类型 | `sign_type` | 是 | `String` | `SHA256WithRSA` | 签名类型 |
|
||||
|
||||
### 10.3 返回结果
|
||||
|
||||
| 字段名 | 变量名 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 返回状态码 | `code` | `Int` | `0` | `0` 为成功,其它值为失败 |
|
||||
| 返回信息 | `msg` | `String` | `success` | 提示信息 |
|
||||
| 平台订单号 | `trade_no` | `String` | | 平台订单号 |
|
||||
| 商户订单号 | `out_trade_no` | `String` | | 商户订单号 |
|
||||
| 第三方订单号 | `api_trade_no` | `String` | | 渠道订单号 |
|
||||
| 支付方式 | `type` | `String` | `alipay` | 支付方式 |
|
||||
| 支付状态 | `status` | `Int` | `1` | `0` 未支付,`1` 已支付,`2` 已退款,`3` 已冻结,`4` 预授权 |
|
||||
| 商户ID | `pid` | `Int` | `1001` | 商户ID |
|
||||
| 创建时间 | `addtime` | `String` | `2016-08-06 22:55:52` | 创建时间 |
|
||||
| 完成时间 | `endtime` | `String` | `2016-08-06 22:55:52` | 完成时间 |
|
||||
| 商品名称 | `name` | `String` | `VIP会员` | 商品名称 |
|
||||
| 商品金额 | `money` | `String` | `1.00` | 商品金额 |
|
||||
| 退款金额 | `refundmoney` | `String` | `0.00` | 已退款金额 |
|
||||
| 业务扩展参数 | `param` | `String` | | 扩展参数 |
|
||||
| 支付者账号 | `buyer` | `String` | | 支付者账号 |
|
||||
| 用户IP | `clientip` | `String` | | 下单 IP |
|
||||
| 时间戳 | `timestamp` | `String` | `1713660000` | 返回时间戳 |
|
||||
| 签名字符串 | `sign` | `String` | `...` | 签名结果 |
|
||||
| 签名类型 | `sign_type` | `String` | `SHA256WithRSA` | 签名类型 |
|
||||
|
||||
## 11. 支付通知
|
||||
|
||||
### 11.1 通知类型
|
||||
|
||||
- 异步通知:`notify_url`
|
||||
- 页面跳转通知:`return_url`
|
||||
|
||||
### 11.2 请求参数
|
||||
|
||||
| 字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 商户ID | `pid` | 是 | `Int` | `1001` | 商户ID |
|
||||
| 平台订单号 | `trade_no` | 是 | `String` | `2016080622555342651` | 易支付订单号 |
|
||||
| 商户订单号 | `out_trade_no` | 是 | `String` | `20160806151343349` | 商户系统内部订单号 |
|
||||
| 支付方式 | `type` | 是 | `String` | `alipay` | 支付方式 |
|
||||
| 支付状态 | `trade_status` | 是 | `String` | `TRADE_SUCCESS` | 成功状态固定为该值 |
|
||||
| 创建时间 | `addtime` | 否 | `String` | `2016-08-06 22:55:52` | 创建时间 |
|
||||
| 完成时间 | `endtime` | 否 | `String` | `2016-08-06 22:55:52` | 完成时间 |
|
||||
| 商品名称 | `name` | 是 | `String` | `VIP会员` | 商品名称 |
|
||||
| 商品金额 | `money` | 是 | `String` | `1.00` | 商品金额 |
|
||||
| 业务扩展参数 | `param` | 否 | `String` | | 业务参数 |
|
||||
| 支付者账号 | `buyer` | 否 | `String` | | 支付者账号 |
|
||||
| 时间戳 | `timestamp` | 是 | `String` | `1713660000` | 用于时间校验 |
|
||||
| 签名字符串 | `sign` | 是 | `String` | `...` | 签名结果 |
|
||||
| 签名类型 | `sign_type` | 是 | `String` | `SHA256WithRSA` | 签名类型 |
|
||||
|
||||
### 11.3 回调响应
|
||||
|
||||
- 收到异步通知后,返回 `success`
|
||||
- 通知时要校验签名,并确认 `trade_status == TRADE_SUCCESS`
|
||||
|
||||
## 12. 退款
|
||||
|
||||
### 12.1 接口说明
|
||||
|
||||
- URL:`http://epay.qcjy.cc/api/pay/refund`
|
||||
- 请求方式:`POST`
|
||||
|
||||
### 12.2 请求参数
|
||||
|
||||
| 字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 商户ID | `pid` | 是 | `Int` | `1001` | 商户ID |
|
||||
| 平台订单号 | `trade_no` | 二选一 | `String` | `2016080622555342651` | 易支付订单号 |
|
||||
| 商户订单号 | `out_trade_no` | 二选一 | `String` | `20160806151343349` | 商户系统内部订单号 |
|
||||
| 退款金额 | `money` | 是 | `String` | `1.50` | 退款金额 |
|
||||
| 商户退款单号 | `out_refund_no` | 否 | `String` | `R202604210001` | 商户侧退款流水号 |
|
||||
| 时间戳 | `timestamp` | 是 | `String` | `1713660000` | 用于时间校验 |
|
||||
| 签名字符串 | `sign` | 是 | `String` | `...` | 签名结果 |
|
||||
| 签名类型 | `sign_type` | 是 | `String` | `SHA256WithRSA` | 签名类型 |
|
||||
|
||||
### 12.3 返回结果
|
||||
|
||||
| 字段名 | 变量名 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 返回状态码 | `code` | `Int` | `0` | `0` 为成功,其它值为失败 |
|
||||
| 返回信息 | `msg` | `String` | `success` | 提示信息 |
|
||||
| 退款单号 | `refund_no` | `String` | `202604210001` | 平台退款单号 |
|
||||
| 商户退款单号 | `out_refund_no` | `String` | `R202604210001` | 商户退款单号 |
|
||||
| 平台订单号 | `trade_no` | `String` | `2016080622555342651` | 易支付订单号 |
|
||||
| 退款金额 | `money` | `String` | `1.50` | 退款金额 |
|
||||
| 已退金额 | `reducemoney` | `String` | `1.50` | 累计退款金额 |
|
||||
| 时间戳 | `timestamp` | `String` | `1713660000` | 返回时间戳 |
|
||||
| 签名字符串 | `sign` | `String` | `...` | 签名结果 |
|
||||
| 签名类型 | `sign_type` | `String` | `SHA256WithRSA` | 签名类型 |
|
||||
|
||||
## 13. 退款查询
|
||||
|
||||
### 13.1 接口说明
|
||||
|
||||
- URL:`http://epay.qcjy.cc/api/pay/refundquery`
|
||||
- 请求方式:`POST`
|
||||
|
||||
### 13.2 请求参数
|
||||
|
||||
| 字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 商户ID | `pid` | 是 | `Int` | `1001` | 商户ID |
|
||||
| 退款单号 | `refund_no` | 二选一 | `String` | `202604210001` | 平台退款单号 |
|
||||
| 商户退款单号 | `out_refund_no` | 二选一 | `String` | `R202604210001` | 商户退款单号 |
|
||||
| 时间戳 | `timestamp` | 是 | `String` | `1713660000` | 用于时间校验 |
|
||||
| 签名字符串 | `sign` | 是 | `String` | `...` | 签名结果 |
|
||||
| 签名类型 | `sign_type` | 是 | `String` | `SHA256WithRSA` | 签名类型 |
|
||||
|
||||
### 13.3 返回结果
|
||||
|
||||
| 字段名 | 变量名 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 返回状态码 | `code` | `Int` | `0` | `0` 为成功,其它值为失败 |
|
||||
| 返回信息 | `msg` | `String` | `success` | 提示信息 |
|
||||
| 退款单号 | `refund_no` | `String` | `202604210001` | 平台退款单号 |
|
||||
| 商户退款单号 | `out_refund_no` | `String` | `R202604210001` | 商户退款单号 |
|
||||
| 平台订单号 | `trade_no` | `String` | `2016080622555342651` | 易支付订单号 |
|
||||
| 商户订单号 | `out_trade_no` | `String` | `20160806151343349` | 商户订单号 |
|
||||
| 退款金额 | `money` | `String` | `1.50` | 退款金额 |
|
||||
| 已退金额 | `reducemoney` | `String` | `1.50` | 累计退款金额 |
|
||||
| 退款状态 | `status` | `Int` | `1` | `0` 失败,`1` 成功 |
|
||||
| 创建时间 | `addtime` | `String` | `2016-08-06 22:55:52` | 创建时间 |
|
||||
| 时间戳 | `timestamp` | `String` | `1713660000` | 返回时间戳 |
|
||||
| 签名字符串 | `sign` | `String` | `...` | 签名结果 |
|
||||
| 签名类型 | `sign_type` | `String` | `SHA256WithRSA` | 签名类型 |
|
||||
|
||||
## 14. 关闭订单
|
||||
|
||||
### 14.1 接口说明
|
||||
|
||||
- URL:`http://epay.qcjy.cc/api/pay/close`
|
||||
- 请求方式:`POST`
|
||||
- 仅支持部分支付插件
|
||||
|
||||
### 14.2 请求参数
|
||||
|
||||
| 字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 商户ID | `pid` | 是 | `Int` | `1001` | 商户ID |
|
||||
| 平台订单号 | `trade_no` | 二选一 | `String` | `2016080622555342651` | 易支付订单号 |
|
||||
| 商户订单号 | `out_trade_no` | 二选一 | `String` | `20160806151343349` | 商户订单号 |
|
||||
| 时间戳 | `timestamp` | 是 | `String` | `1713660000` | 用于时间校验 |
|
||||
| 签名字符串 | `sign` | 是 | `String` | `...` | 签名结果 |
|
||||
| 签名类型 | `sign_type` | 是 | `String` | `SHA256WithRSA` | 签名类型 |
|
||||
|
||||
### 14.3 返回结果
|
||||
|
||||
| 字段名 | 变量名 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 返回状态码 | `code` | `Int` | `0` | `0` 为成功,其它值为失败 |
|
||||
| 返回信息 | `msg` | `String` | `success` | 提示信息 |
|
||||
| 时间戳 | `timestamp` | `String` | `1713660000` | 返回时间戳 |
|
||||
| 签名字符串 | `sign` | `String` | `...` | 签名结果 |
|
||||
| 签名类型 | `sign_type` | `String` | `SHA256WithRSA` | 签名类型 |
|
||||
|
||||
## 15. 商户信息
|
||||
|
||||
### 15.1 接口说明
|
||||
|
||||
- URL:`http://epay.qcjy.cc/api/merchant/info`
|
||||
- 请求方式:`POST`
|
||||
|
||||
### 15.2 请求参数
|
||||
|
||||
| 字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 商户ID | `pid` | 是 | `Int` | `1001` | 商户ID |
|
||||
| 时间戳 | `timestamp` | 是 | `String` | `1713660000` | 用于时间校验 |
|
||||
| 签名字符串 | `sign` | 是 | `String` | `...` | 签名结果 |
|
||||
| 签名类型 | `sign_type` | 是 | `String` | `SHA256WithRSA` | 签名类型 |
|
||||
|
||||
### 15.3 返回结果
|
||||
|
||||
| 字段名 | 变量名 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 返回状态码 | `code` | `Int` | `0` | `0` 为成功,其它值为失败 |
|
||||
| 返回信息 | `msg` | `String` | `success` | 提示信息 |
|
||||
| 商户ID | `pid` | `Int` | `1001` | 商户ID |
|
||||
| 商户状态 | `status` | `Int` | `1` | 商户状态 |
|
||||
| 支付状态 | `pay_status` | `Int` | `1` | 支付开关 |
|
||||
| 结算状态 | `settle_status` | `Int` | `1` | 结算开关 |
|
||||
| 商户余额 | `money` | `String` | `0.00` | 可用余额 |
|
||||
| 结算类型 | `settle_type` | `Int` | `1` | `1` 支付宝,`2` 微信,`3` QQ,`4` 银行卡 |
|
||||
| 结算账号 | `settle_account` | `String` | `admin@pay.com` | 结算账号 |
|
||||
| 结算姓名 | `settle_name` | `String` | `张三` | 结算姓名 |
|
||||
| 订单总数 | `order_num` | `Int` | `30` | 订单总数 |
|
||||
| 今日订单 | `order_num_today` | `Int` | `15` | 今日订单数量 |
|
||||
| 昨日订单 | `order_num_lastday` | `Int` | `15` | 昨日订单数量 |
|
||||
| 今日交易额 | `order_money_today` | `String` | `100.00` | 今日交易金额 |
|
||||
| 昨日交易额 | `order_money_lastday` | `String` | `90.00` | 昨日交易金额 |
|
||||
| 时间戳 | `timestamp` | `String` | `1713660000` | 返回时间戳 |
|
||||
| 签名字符串 | `sign` | `String` | `...` | 签名结果 |
|
||||
| 签名类型 | `sign_type` | `String` | `SHA256WithRSA` | 签名类型 |
|
||||
|
||||
## 16. 商户订单
|
||||
|
||||
### 16.1 接口说明
|
||||
|
||||
- URL:`http://epay.qcjy.cc/api/merchant/orders`
|
||||
- 请求方式:`POST`
|
||||
|
||||
### 16.2 请求参数
|
||||
|
||||
| 字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 商户ID | `pid` | 是 | `Int` | `1001` | 商户ID |
|
||||
| 偏移量 | `offset` | 否 | `Int` | `0` | 起始偏移 |
|
||||
| 数量 | `limit` | 否 | `Int` | `20` | 返回条数 |
|
||||
| 状态 | `status` | 否 | `Int` | `1` | 订单状态筛选 |
|
||||
| 时间戳 | `timestamp` | 是 | `String` | `1713660000` | 用于时间校验 |
|
||||
| 签名字符串 | `sign` | 是 | `String` | `...` | 签名结果 |
|
||||
| 签名类型 | `sign_type` | 是 | `String` | `SHA256WithRSA` | 签名类型 |
|
||||
|
||||
### 16.3 返回结果
|
||||
|
||||
| 字段名 | 变量名 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 返回状态码 | `code` | `Int` | `0` | `0` 为成功,其它值为失败 |
|
||||
| 返回信息 | `msg` | `String` | `success` | 提示信息 |
|
||||
| 订单数据 | `data` | `Array` | 订单列表 | 订单数组 |
|
||||
| 时间戳 | `timestamp` | `String` | `1713660000` | 返回时间戳 |
|
||||
| 签名字符串 | `sign` | `String` | `...` | 签名结果 |
|
||||
| 签名类型 | `sign_type` | `String` | `SHA256WithRSA` | 签名类型 |
|
||||
|
||||
## 17. 转账提交
|
||||
|
||||
### 17.1 接口说明
|
||||
|
||||
- URL:`http://epay.qcjy.cc/api/transfer/submit`
|
||||
- 请求方式:`POST`
|
||||
|
||||
### 17.2 请求参数
|
||||
|
||||
| 字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 商户ID | `pid` | 是 | `Int` | `1001` | 商户ID |
|
||||
| 转账类型 | `type` | 是 | `String` | `alipay` | `alipay`、`wxpay`、`qqpay`、`bank` |
|
||||
| 收款账号 | `account` | 是 | `String` | `admin@pay.com` | 收款账号 |
|
||||
| 收款姓名 | `name` | 是 | `String` | `张三` | 收款姓名 |
|
||||
| 金额 | `money` | 是 | `String` | `1.00` | 转账金额 |
|
||||
| 备注 | `remark` | 否 | `String` | `测试转账` | 转账备注 |
|
||||
| 商户转账单号 | `out_biz_no` | 否 | `String` | `T202604210001` | 商户侧流水号 |
|
||||
| 书签 ID | `bookid` | 否 | `String` | `1` | 账本或预设项标识 |
|
||||
| 时间戳 | `timestamp` | 是 | `String` | `1713660000` | 用于时间校验 |
|
||||
| 签名字符串 | `sign` | 是 | `String` | `...` | 签名结果 |
|
||||
| 签名类型 | `sign_type` | 是 | `String` | `SHA256WithRSA` | 签名类型 |
|
||||
|
||||
### 17.3 返回结果
|
||||
|
||||
| 字段名 | 变量名 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 返回状态码 | `code` | `Int` | `0` | `0` 为成功,其它值为失败 |
|
||||
| 返回信息 | `msg` | `String` | `success` | 提示信息 |
|
||||
| 转账状态 | `status` | `Int` | `0` | `0` 待处理,后续查询 |
|
||||
| 平台业务号 | `biz_no` | `String` | `202604210001` | 平台转账业务号 |
|
||||
| 商户转账单号 | `out_biz_no` | `String` | `T202604210001` | 商户侧流水号 |
|
||||
| 订单号 | `orderid` | `String` | `202604210001` | 平台订单号 |
|
||||
| 支付时间 | `paydate` | `String` | `2016-08-06 22:55:52` | 完成时间 |
|
||||
| 手续费 | `cost_money` | `String` | `0.10` | 扣除费用 |
|
||||
| 时间戳 | `timestamp` | `String` | `1713660000` | 返回时间戳 |
|
||||
| 签名字符串 | `sign` | `String` | `...` | 签名结果 |
|
||||
| 签名类型 | `sign_type` | `String` | `SHA256WithRSA` | 签名类型 |
|
||||
|
||||
## 18. 转账查询
|
||||
|
||||
### 18.1 接口说明
|
||||
|
||||
- URL:`http://epay.qcjy.cc/api/transfer/query`
|
||||
- 请求方式:`POST`
|
||||
|
||||
### 18.2 请求参数
|
||||
|
||||
| 字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 商户ID | `pid` | 是 | `Int` | `1001` | 商户ID |
|
||||
| 平台业务号 | `biz_no` | 二选一 | `String` | `202604210001` | 平台转账业务号 |
|
||||
| 商户转账单号 | `out_biz_no` | 二选一 | `String` | `T202604210001` | 商户侧流水号 |
|
||||
| 时间戳 | `timestamp` | 是 | `String` | `1713660000` | 用于时间校验 |
|
||||
| 签名字符串 | `sign` | 是 | `String` | `...` | 签名结果 |
|
||||
| 签名类型 | `sign_type` | 是 | `String` | `SHA256WithRSA` | 签名类型 |
|
||||
|
||||
### 18.3 返回结果
|
||||
|
||||
| 字段名 | 变量名 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 返回状态码 | `code` | `Int` | `0` | `0` 为成功,其它值为失败 |
|
||||
| 返回信息 | `msg` | `String` | `success` | 提示信息 |
|
||||
| 转账状态 | `status` | `Int` | `1` | `0` 处理中,`1` 成功,`2` 失败 |
|
||||
| 错误信息 | `errmsg` | `String` | | 失败原因 |
|
||||
| 平台业务号 | `biz_no` | `String` | `202604210001` | 平台转账业务号 |
|
||||
| 商户转账单号 | `out_biz_no` | `String` | `T202604210001` | 商户侧流水号 |
|
||||
| 订单号 | `orderid` | `String` | `202604210001` | 平台订单号 |
|
||||
| 支付时间 | `paydate` | `String` | `2016-08-06 22:55:52` | 完成时间 |
|
||||
| 金额 | `amount` | `String` | `1.00` | 转账金额 |
|
||||
| 手续费 | `cost_money` | `String` | `0.10` | 扣除费用 |
|
||||
| 备注 | `remark` | `String` | `测试转账` | 备注信息 |
|
||||
| 时间戳 | `timestamp` | `String` | `1713660000` | 返回时间戳 |
|
||||
| 签名字符串 | `sign` | `String` | `...` | 签名结果 |
|
||||
| 签名类型 | `sign_type` | `String` | `SHA256WithRSA` | 签名类型 |
|
||||
|
||||
## 19. 转账余额
|
||||
|
||||
### 19.1 接口说明
|
||||
|
||||
- URL:`http://epay.qcjy.cc/api/transfer/balance`
|
||||
- 请求方式:`POST`
|
||||
|
||||
### 19.2 请求参数
|
||||
|
||||
| 字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 商户ID | `pid` | 是 | `Int` | `1001` | 商户ID |
|
||||
| 时间戳 | `timestamp` | 是 | `String` | `1713660000` | 用于时间校验 |
|
||||
| 签名字符串 | `sign` | 是 | `String` | `...` | 签名结果 |
|
||||
| 签名类型 | `sign_type` | 是 | `String` | `SHA256WithRSA` | 签名类型 |
|
||||
|
||||
### 19.3 返回结果
|
||||
|
||||
| 字段名 | 变量名 | 类型 | 示例值 | 描述 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 返回状态码 | `code` | `Int` | `0` | `0` 为成功,其它值为失败 |
|
||||
| 返回信息 | `msg` | `String` | `success` | 提示信息 |
|
||||
| 可用余额 | `available_money` | `String` | `100.00` | 可转账余额 |
|
||||
| 转账费率 | `transfer_rate` | `String` | `0.01` | 转账费率 |
|
||||
| 时间戳 | `timestamp` | `String` | `1713660000` | 返回时间戳 |
|
||||
| 签名字符串 | `sign` | `String` | `...` | 签名结果 |
|
||||
| 签名类型 | `sign_type` | `String` | `SHA256WithRSA` | 签名类型 |
|
||||
|
||||
## 20. SDK 下载
|
||||
|
||||
- 下载地址:[`SDK_2.0.zip`](https://epay.qcjy.cc/assets/files/SDK_2.0.zip)
|
||||
- 版本:`V2.0`
|
||||
|
||||
## 21. 备注
|
||||
|
||||
- V2 文档页面还包含更细的接口说明、页面示例和菜单导航,这里只整理对接最关键的协议信息。
|
||||
- 如果后续需要补充每个接口的示例请求与返回样例,可以继续在本目录补充,但建议保持版本归档方式一致。
|
||||
60
doc/api/legacy/sdk/v1/epayapi.php
Normal file
60
doc/api/legacy/sdk/v1/epayapi.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>正在为您跳转到支付页面,请稍候...</title>
|
||||
<style type="text/css">
|
||||
body{margin:0;padding:0}
|
||||
p{position:absolute;left:50%;top:50%;height:35px;margin:-35px 0 0 -160px;padding:20px;font:bold 16px/30px "宋体",Arial;text-indent:40px;border:1px solid #c5d0dc}
|
||||
#waiting{font-family:Arial}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<?php
|
||||
|
||||
require_once("lib/epay.config.php");
|
||||
require_once("lib/EpayCore.class.php");
|
||||
|
||||
/**************************请求参数**************************/
|
||||
$notify_url = "http://127.0.0.1/SDK/notify_url.php";
|
||||
//需http://格式的完整路径,不能加?id=123这类自定义参数
|
||||
|
||||
//页面跳转同步通知页面路径
|
||||
$return_url = "http://127.0.0.1/SDK/return_url.php";
|
||||
//需http://格式的完整路径,不能加?id=123这类自定义参数
|
||||
|
||||
//商户订单号
|
||||
$out_trade_no = $_POST['WIDout_trade_no'];
|
||||
//商户网站订单系统中唯一订单号,必填
|
||||
|
||||
//支付方式(可传入alipay,wxpay,qqpay,bank,jdpay)
|
||||
$type = $_POST['type'];
|
||||
//商品名称
|
||||
$name = $_POST['WIDsubject'];
|
||||
//付款金额
|
||||
$money = $_POST['WIDtotal_fee'];
|
||||
|
||||
|
||||
/************************************************************/
|
||||
|
||||
//构造要请求的参数数组,无需改动
|
||||
$parameter = array(
|
||||
"pid" => $epay_config['pid'],
|
||||
"type" => $type,
|
||||
"notify_url" => $notify_url,
|
||||
"return_url" => $return_url,
|
||||
"out_trade_no" => $out_trade_no,
|
||||
"name" => $name,
|
||||
"money" => $money,
|
||||
);
|
||||
|
||||
//建立请求
|
||||
$epay = new EpayCore($epay_config);
|
||||
$html_text = $epay->pagePay($parameter);
|
||||
echo $html_text;
|
||||
|
||||
?>
|
||||
<p>正在为您跳转到支付页面,请稍候...</p>
|
||||
</body>
|
||||
</html>
|
||||
205
doc/api/legacy/sdk/v1/index.php
Normal file
205
doc/api/legacy/sdk/v1/index.php
Normal file
@@ -0,0 +1,205 @@
|
||||
<?php
|
||||
/* *
|
||||
* 以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
|
||||
*/
|
||||
|
||||
?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>彩虹易支付接口测试</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<style>
|
||||
*{
|
||||
margin:0;
|
||||
padding:0;
|
||||
}
|
||||
ul,ol{
|
||||
list-style:none;
|
||||
}
|
||||
.title{
|
||||
color: #ADADAD;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
padding: 8px 16px 5px 10px;
|
||||
}
|
||||
.hidden{
|
||||
display:none;
|
||||
}
|
||||
|
||||
.new-btn-login-sp{
|
||||
border:1px solid #D74C00;
|
||||
padding:1px;
|
||||
display:inline-block;
|
||||
}
|
||||
|
||||
.new-btn-login{
|
||||
background-color: #ff8c00;
|
||||
color: #FFFFFF;
|
||||
font-weight: bold;
|
||||
border: medium none;
|
||||
width:82px;
|
||||
height:28px;
|
||||
}
|
||||
.new-btn-login:hover{
|
||||
background-color: #ffa300;
|
||||
width: 82px;
|
||||
color: #FFFFFF;
|
||||
font-weight: bold;
|
||||
height: 28px;
|
||||
}
|
||||
.bank-list{
|
||||
overflow:hidden;
|
||||
margin-top:5px;
|
||||
}
|
||||
.bank-list li{
|
||||
float:left;
|
||||
width:153px;
|
||||
margin-bottom:5px;
|
||||
}
|
||||
|
||||
#main{
|
||||
width:750px;
|
||||
margin:0 auto;
|
||||
font-size:14px;
|
||||
font-family:'宋体';
|
||||
}
|
||||
#logo{
|
||||
background-color: transparent;
|
||||
background-image: url("images/new-btn-fixed.png");
|
||||
border: medium none;
|
||||
background-position:0 0;
|
||||
width:166px;
|
||||
height:35px;
|
||||
float:left;
|
||||
}
|
||||
.red-star{
|
||||
color:#f00;
|
||||
width:10px;
|
||||
display:inline-block;
|
||||
}
|
||||
.null-star{
|
||||
color:#fff;
|
||||
}
|
||||
.content{
|
||||
margin-top:5px;
|
||||
}
|
||||
|
||||
.content dt{
|
||||
width:160px;
|
||||
display:inline-block;
|
||||
text-align:right;
|
||||
float:left;
|
||||
|
||||
}
|
||||
.content dd{
|
||||
margin-left:100px;
|
||||
margin-bottom:5px;
|
||||
}
|
||||
#foot{
|
||||
margin-top:10px;
|
||||
}
|
||||
.foot-ul li {
|
||||
text-align:center;
|
||||
}
|
||||
.note-help {
|
||||
color: #999999;
|
||||
font-size: 12px;
|
||||
line-height: 130%;
|
||||
padding-left: 3px;
|
||||
}
|
||||
|
||||
.cashier-nav {
|
||||
font-size: 14px;
|
||||
margin: 15px 0 10px;
|
||||
text-align: left;
|
||||
height:30px;
|
||||
border-bottom:solid 2px #CFD2D7;
|
||||
}
|
||||
.cashier-nav ol li {
|
||||
float: left;
|
||||
}
|
||||
.cashier-nav li.current {
|
||||
color: #AB4400;
|
||||
font-weight: bold;
|
||||
}
|
||||
.cashier-nav li.last {
|
||||
clear:right;
|
||||
}
|
||||
.alipay_link {
|
||||
text-align:right;
|
||||
}
|
||||
.alipay_link a:link{
|
||||
text-decoration:none;
|
||||
color:#8D8D8D;
|
||||
}
|
||||
.alipay_link a:visited{
|
||||
text-decoration:none;
|
||||
color:#8D8D8D;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body text=#000000 bgColor=#ffffff leftMargin=0 topMargin=4>
|
||||
<div id="main">
|
||||
<div id="head">
|
||||
<dl class="alipay_link">
|
||||
</dl>
|
||||
<span class="title">彩虹易支付接口测试</span>
|
||||
</div>
|
||||
<div class="cashier-nav">
|
||||
<ol>
|
||||
<li class="current">1、确认信息 →</li>
|
||||
<li>2、点击确认 →</li>
|
||||
<li class="last">3、确认完成</li>
|
||||
</ol>
|
||||
</div>
|
||||
<form name=alipayment action=epayapi.php method=post target="_blank">
|
||||
<div id="body" style="clear:left">
|
||||
<dl class="content">
|
||||
<dt>商户订单号:</dt>
|
||||
<dd>
|
||||
<span class="null-star">*</span>
|
||||
<input size="30" name="WIDout_trade_no" value="<?php echo date("YmdHis").mt_rand(100,999); ?>"/>
|
||||
<span>商户网站订单系统中唯一订单号,必填
|
||||
</span>
|
||||
</dd>
|
||||
<dt>商品名称:</dt>
|
||||
<dd>
|
||||
<span class="null-star">*</span>
|
||||
<input size="30" name="WIDsubject" value="测试商品"/>
|
||||
<span>必填
|
||||
</span>
|
||||
</dd>
|
||||
<dt>付款金额:</dt>
|
||||
<dd>
|
||||
<span class="null-star">*</span>
|
||||
<input size="30" name="WIDtotal_fee" value="1"/>
|
||||
<span>必填
|
||||
</span>
|
||||
</dd>
|
||||
<dt>支付方式:</dt>
|
||||
<dd>
|
||||
<label><input type="radio" name="type" value="alipay" checked="">支付宝</label> <label><input type="radio" name="type" value="qqpay">QQ钱包</label> <label><input type="radio" name="type" value="wxpay">微信支付</label> <label><input type="radio" name="type" value="bank">云闪付</label>
|
||||
</dd>
|
||||
|
||||
|
||||
<dt></dt>
|
||||
<dd>
|
||||
<span class="new-btn-login-sp">
|
||||
<button class="new-btn-login" type="submit" style="text-align:center;">确 认</button>
|
||||
</span>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</form>
|
||||
<div id="foot">
|
||||
<ul class="foot-ul">
|
||||
<li><font class="note-help">如果您点击“确认”按钮,即表示您同意该次的执行操作。 </font></li>
|
||||
<li>
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
154
doc/api/legacy/sdk/v1/lib/EpayCore.class.php
Normal file
154
doc/api/legacy/sdk/v1/lib/EpayCore.class.php
Normal file
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
/* *
|
||||
* 彩虹易支付SDK服务类
|
||||
* 说明:
|
||||
* 包含发起支付、查询订单、回调验证等功能
|
||||
*/
|
||||
|
||||
class EpayCore
|
||||
{
|
||||
private $pid;
|
||||
private $key;
|
||||
private $submit_url;
|
||||
private $mapi_url;
|
||||
private $api_url;
|
||||
private $sign_type = 'MD5';
|
||||
|
||||
function __construct($config){
|
||||
$this->pid = $config['pid'];
|
||||
$this->key = $config['key'];
|
||||
$this->submit_url = $config['apiurl'].'submit.php';
|
||||
$this->mapi_url = $config['apiurl'].'mapi.php';
|
||||
$this->api_url = $config['apiurl'].'api.php';
|
||||
}
|
||||
|
||||
// 发起支付(页面跳转)
|
||||
public function pagePay($param_tmp, $button='正在跳转'){
|
||||
$param = $this->buildRequestParam($param_tmp);
|
||||
|
||||
$html = '<form id="dopay" action="'.$this->submit_url.'" method="post">';
|
||||
foreach ($param as $k=>$v) {
|
||||
$html.= '<input type="hidden" name="'.$k.'" value="'.$v.'"/>';
|
||||
}
|
||||
$html .= '<input type="submit" value="'.$button.'"></form><script>document.getElementById("dopay").submit();</script>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
// 发起支付(获取链接)
|
||||
public function getPayLink($param_tmp){
|
||||
$param = $this->buildRequestParam($param_tmp);
|
||||
$url = $this->submit_url.'?'.http_build_query($param);
|
||||
return $url;
|
||||
}
|
||||
|
||||
// 发起支付(API接口)
|
||||
public function apiPay($param_tmp){
|
||||
$param = $this->buildRequestParam($param_tmp);
|
||||
$response = $this->getHttpResponse($this->mapi_url, http_build_query($param));
|
||||
$arr = json_decode($response, true);
|
||||
return $arr;
|
||||
}
|
||||
|
||||
// 异步回调验证
|
||||
public function verifyNotify(){
|
||||
if(empty($_GET)) return false;
|
||||
|
||||
$sign = $this->getSign($_GET);
|
||||
|
||||
if($sign === $_GET['sign']){
|
||||
$signResult = true;
|
||||
}else{
|
||||
$signResult = false;
|
||||
}
|
||||
|
||||
return $signResult;
|
||||
}
|
||||
|
||||
// 同步回调验证
|
||||
public function verifyReturn(){
|
||||
if(empty($_GET)) return false;
|
||||
|
||||
$sign = $this->getSign($_GET);
|
||||
|
||||
if($sign === $_GET['sign']){
|
||||
$signResult = true;
|
||||
}else{
|
||||
$signResult = false;
|
||||
}
|
||||
|
||||
return $signResult;
|
||||
}
|
||||
|
||||
// 查询订单支付状态
|
||||
public function orderStatus($trade_no){
|
||||
$result = $this->queryOrder($trade_no);
|
||||
if($result['status']==1){
|
||||
return true;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 查询订单
|
||||
public function queryOrder($trade_no){
|
||||
$url = $this->api_url.'?act=order&pid=' . $this->pid . '&key=' . $this->key . '&trade_no=' . $trade_no;
|
||||
$response = $this->getHttpResponse($url);
|
||||
$arr = json_decode($response, true);
|
||||
return $arr;
|
||||
}
|
||||
|
||||
// 订单退款
|
||||
public function refund($trade_no, $money){
|
||||
$url = $this->api_url.'?act=refund';
|
||||
$post = 'pid=' . $this->pid . '&key=' . $this->key . '&trade_no=' . $trade_no . '&money=' . $money;
|
||||
$response = $this->getHttpResponse($url, $post);
|
||||
$arr = json_decode($response, true);
|
||||
return $arr;
|
||||
}
|
||||
|
||||
private function buildRequestParam($param){
|
||||
$mysign = $this->getSign($param);
|
||||
$param['sign'] = $mysign;
|
||||
$param['sign_type'] = $this->sign_type;
|
||||
return $param;
|
||||
}
|
||||
|
||||
// 计算签名
|
||||
private function getSign($param){
|
||||
ksort($param);
|
||||
reset($param);
|
||||
$signstr = '';
|
||||
|
||||
foreach($param as $k => $v){
|
||||
if($k != "sign" && $k != "sign_type" && $v!=''){
|
||||
$signstr .= $k.'='.$v.'&';
|
||||
}
|
||||
}
|
||||
$signstr = substr($signstr,0,-1);
|
||||
$signstr .= $this->key;
|
||||
$sign = md5($signstr);
|
||||
return $sign;
|
||||
}
|
||||
|
||||
// 请求外部资源
|
||||
private function getHttpResponse($url, $post = false, $timeout = 10){
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
$httpheader[] = "Accept: */*";
|
||||
$httpheader[] = "Accept-Language: zh-CN,zh;q=0.8";
|
||||
$httpheader[] = "Connection: close";
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader);
|
||||
curl_setopt($ch, CURLOPT_HEADER, false);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
if($post){
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
|
||||
}
|
||||
$response = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
13
doc/api/legacy/sdk/v1/lib/epay.config.php
Normal file
13
doc/api/legacy/sdk/v1/lib/epay.config.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
/* *
|
||||
* 配置文件
|
||||
*/
|
||||
|
||||
//支付接口地址
|
||||
$epay_config['apiurl'] = 'http://pay.www.com/';
|
||||
|
||||
//商户ID
|
||||
$epay_config['pid'] = '1000';
|
||||
|
||||
//商户密钥
|
||||
$epay_config['key'] = 'WWc3Z2jkK7jhNGPALcGKjHLPK47wRK85';
|
||||
45
doc/api/legacy/sdk/v1/notify_url.php
Normal file
45
doc/api/legacy/sdk/v1/notify_url.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
/* *
|
||||
* 功能:彩虹易支付异步通知页面
|
||||
* 说明:
|
||||
* 以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
|
||||
*/
|
||||
|
||||
require_once("lib/epay.config.php");
|
||||
require_once("lib/EpayCore.class.php");
|
||||
|
||||
//计算得出通知验证结果
|
||||
$epay = new EpayCore($epay_config);
|
||||
$verify_result = $epay->verifyNotify();
|
||||
|
||||
if($verify_result) {//验证成功
|
||||
|
||||
//商户订单号
|
||||
$out_trade_no = $_GET['out_trade_no'];
|
||||
|
||||
//彩虹易支付交易号
|
||||
$trade_no = $_GET['trade_no'];
|
||||
|
||||
//交易状态
|
||||
$trade_status = $_GET['trade_status'];
|
||||
|
||||
//支付方式
|
||||
$type = $_GET['type'];
|
||||
|
||||
//支付金额
|
||||
$money = $_GET['money'];
|
||||
|
||||
if ($_GET['trade_status'] == 'TRADE_SUCCESS') {
|
||||
//判断该笔订单是否在商户网站中已经做过处理
|
||||
//如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
|
||||
//如果有做过处理,不执行商户的业务程序
|
||||
}
|
||||
|
||||
//验证成功返回
|
||||
echo "success";
|
||||
}
|
||||
else {
|
||||
//验证失败
|
||||
echo "fail";
|
||||
}
|
||||
?>
|
||||
55
doc/api/legacy/sdk/v1/return_url.php
Normal file
55
doc/api/legacy/sdk/v1/return_url.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/* *
|
||||
* 功能:彩虹易支付页面跳转同步通知页面
|
||||
* 说明:
|
||||
* 以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
|
||||
*/
|
||||
|
||||
require_once("lib/epay.config.php");
|
||||
require_once("lib/EpayCore.class.php");
|
||||
?>
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>支付返回页面</title>
|
||||
</head>
|
||||
<body>
|
||||
<?php
|
||||
//计算得出通知验证结果
|
||||
$epay = new EpayCore($epay_config);
|
||||
$verify_result = $epay->verifyReturn();
|
||||
|
||||
if($verify_result) {//验证成功
|
||||
|
||||
//商户订单号
|
||||
$out_trade_no = $_GET['out_trade_no'];
|
||||
|
||||
//支付宝交易号
|
||||
$trade_no = $_GET['trade_no'];
|
||||
|
||||
//交易状态
|
||||
$trade_status = $_GET['trade_status'];
|
||||
|
||||
//支付方式
|
||||
$type = $_GET['type'];
|
||||
|
||||
|
||||
if($_GET['trade_status'] == 'TRADE_SUCCESS') {
|
||||
//判断该笔订单是否在商户网站中已经做过处理
|
||||
//如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
|
||||
//如果有做过处理,不执行商户的业务程序
|
||||
}
|
||||
else {
|
||||
echo "trade_status=".$_GET['trade_status'];
|
||||
}
|
||||
|
||||
echo "<h3>验证成功</h3><br />";
|
||||
}
|
||||
else {
|
||||
//验证失败
|
||||
echo "<h3>验证失败</h3>";
|
||||
}
|
||||
?>
|
||||
</body>
|
||||
</html>
|
||||
59
doc/api/legacy/sdk/v2/epayapi.php
Normal file
59
doc/api/legacy/sdk/v2/epayapi.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>正在为您跳转到支付页面,请稍候...</title>
|
||||
<style type="text/css">
|
||||
body{margin:0;padding:0}
|
||||
p{position:absolute;left:50%;top:50%;height:35px;margin:-35px 0 0 -160px;padding:20px;font:bold 16px/30px "宋体",Arial;text-indent:40px;border:1px solid #c5d0dc}
|
||||
#waiting{font-family:Arial}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<?php
|
||||
|
||||
require_once("lib/epay.config.php");
|
||||
require_once("lib/EpayCore.class.php");
|
||||
|
||||
/**************************请求参数**************************/
|
||||
$notify_url = "http://127.0.0.1/SDK/notify_url.php";
|
||||
//需http://格式的完整路径,不能加?id=123这类自定义参数
|
||||
|
||||
//页面跳转同步通知页面路径
|
||||
$return_url = "http://127.0.0.1/SDK/return_url.php";
|
||||
//需http://格式的完整路径,不能加?id=123这类自定义参数
|
||||
|
||||
//商户订单号
|
||||
$out_trade_no = $_POST['out_trade_no'];
|
||||
//商户网站订单系统中唯一订单号,必填
|
||||
|
||||
//支付方式(可传入alipay,wxpay,qqpay,bank,jdpay)
|
||||
$type = $_POST['type'];
|
||||
//商品名称
|
||||
$name = $_POST['name'];
|
||||
//付款金额
|
||||
$money = $_POST['money'];
|
||||
|
||||
|
||||
/************************************************************/
|
||||
|
||||
//构造要请求的参数数组,无需改动
|
||||
$parameter = array(
|
||||
"type" => $type,
|
||||
"notify_url" => $notify_url,
|
||||
"return_url" => $return_url,
|
||||
"out_trade_no" => $out_trade_no,
|
||||
"name" => $name,
|
||||
"money" => $money,
|
||||
);
|
||||
|
||||
//建立请求
|
||||
$epay = new EpayCore($epay_config);
|
||||
$html_text = $epay->pagePay($parameter);
|
||||
echo $html_text;
|
||||
|
||||
?>
|
||||
<p>正在为您跳转到支付页面,请稍候...</p>
|
||||
</body>
|
||||
</html>
|
||||
61
doc/api/legacy/sdk/v2/index.php
Normal file
61
doc/api/legacy/sdk/v2/index.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<body>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
|
||||
<title>彩虹易支付接口测试</title>
|
||||
<link href="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"/>
|
||||
<link rel="stylesheet" href="./assets/css/captcha.css" type="text/css" />
|
||||
<style>.form-group{margin-bottom:18px} #captcha{margin: auto;margin-bottom:16px}</style>
|
||||
</head>
|
||||
<div class="container">
|
||||
<div class="col-xs-12 col-sm-10 col-lg-8 center-block" style="float: none;">
|
||||
<div class="page-header">
|
||||
<h4>彩虹易支付接口测试</h4>
|
||||
</div>
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-body">
|
||||
|
||||
<form name="alipayment" method="POST" action="epayapi.php" class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">商户订单号</label>
|
||||
<div class="col-sm-8">
|
||||
<input class="form-control" type="text" name="out_trade_no" value="<?php echo date("YmdHis").mt_rand(100,999); ?>" autocomplete="off">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">商品名称</label>
|
||||
<div class="col-sm-8">
|
||||
<input class="form-control" type="text" name="name" value="支付测试" autocomplete="off">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">支付金额</label>
|
||||
<div class="col-sm-8">
|
||||
<input class="form-control" type="text" name="money" value="1" autocomplete="off">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">支付方式</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="radio">
|
||||
<label class="i-checks"><input type="radio" name="type" value="alipay" checked="checked"><i></i>支付宝</label>
|
||||
<label class="i-checks"><input type="radio" name="type" value="wxpay"><i></i>微信支付</label>
|
||||
<label class="i-checks"><input type="radio" name="type" value="qqpay"><i></i>QQ钱包</label>
|
||||
<label class="i-checks"><input type="radio" name="type" value="bank"><i></i>云闪付</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-3 col-sm-8"><input type="submit" value="确 认" class="btn btn-primary form-control"><br>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="panel-footer text-center">
|
||||
<span class="text-muted">此页面只是为了方便商户测试而提供的样例页面,商户可以根据自己网站的需要,按照技术文档编写</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
184
doc/api/legacy/sdk/v2/lib/EpayCore.class.php
Normal file
184
doc/api/legacy/sdk/v2/lib/EpayCore.class.php
Normal file
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
/* *
|
||||
* 彩虹易支付SDK服务类
|
||||
* 说明:
|
||||
* 包含发起支付、查询订单、回调验证等功能
|
||||
*/
|
||||
|
||||
class EpayCore
|
||||
{
|
||||
private $apiurl;
|
||||
private $pid;
|
||||
private $platform_public_key;
|
||||
private $merchant_private_key;
|
||||
|
||||
private $sign_type = 'RSA';
|
||||
|
||||
function __construct($config){
|
||||
$this->apiurl = $config['apiurl'];
|
||||
$this->pid = $config['pid'];
|
||||
$this->platform_public_key = $config['platform_public_key'];
|
||||
$this->merchant_private_key = $config['merchant_private_key'];
|
||||
}
|
||||
|
||||
// 发起支付(页面跳转)
|
||||
public function pagePay($param_tmp, $button='正在跳转'){
|
||||
$requrl = $this->apiurl.'api/pay/submit';
|
||||
$param = $this->buildRequestParam($param_tmp);
|
||||
|
||||
$html = '<form id="dopay" action="'.$requrl.'" method="post">';
|
||||
foreach ($param as $k=>$v) {
|
||||
$html.= '<input type="hidden" name="'.$k.'" value="'.$v.'"/>';
|
||||
}
|
||||
$html .= '<input type="submit" value="'.$button.'"></form><script>document.getElementById("dopay").submit();</script>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
// 发起支付(获取链接)
|
||||
public function getPayLink($param_tmp){
|
||||
$requrl = $this->apiurl.'api/pay/submit';
|
||||
$param = $this->buildRequestParam($param_tmp);
|
||||
$url = $requrl.'?'.http_build_query($param);
|
||||
return $url;
|
||||
}
|
||||
|
||||
// 发起支付(API接口)
|
||||
public function apiPay($params){
|
||||
return $this->execute('api/pay/create', $params);
|
||||
}
|
||||
|
||||
// 发起API请求
|
||||
public function execute($path, $params){
|
||||
$path = ltrim($path, '/');
|
||||
$requrl = $this->apiurl.$path;
|
||||
$param = $this->buildRequestParam($params);
|
||||
$response = $this->getHttpResponse($requrl, http_build_query($param));
|
||||
$arr = json_decode($response, true);
|
||||
if($arr && $arr['code'] == 0){
|
||||
if(!$this->verify($arr)){
|
||||
throw new \Exception('返回数据验签失败');
|
||||
}
|
||||
return $arr;
|
||||
}else{
|
||||
throw new \Exception($arr ? $arr['msg'] : '请求失败');
|
||||
}
|
||||
}
|
||||
|
||||
// 回调验证
|
||||
public function verify($arr){
|
||||
if(empty($arr) || empty($arr['sign'])) return false;
|
||||
|
||||
if(empty($arr['timestamp']) || abs(time() - $arr['timestamp']) > 300) return false;
|
||||
|
||||
$sign = $arr['sign'];
|
||||
|
||||
return $this->rsaPublicVerify($this->getSignContent($arr), $sign);
|
||||
}
|
||||
|
||||
// 查询订单支付状态
|
||||
public function orderStatus($trade_no){
|
||||
$result = $this->queryOrder($trade_no);
|
||||
if($result && $result['status']==1){
|
||||
return true;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 查询订单
|
||||
public function queryOrder($trade_no){
|
||||
$params = [
|
||||
'trade_no' => $trade_no,
|
||||
];
|
||||
return $this->execute('api/pay/query', $params);
|
||||
}
|
||||
|
||||
// 订单退款
|
||||
public function refund($out_refund_no, $trade_no, $money){
|
||||
$params = [
|
||||
'trade_no' => $trade_no,
|
||||
'money' => $money,
|
||||
'out_refund_no' => $out_refund_no,
|
||||
];
|
||||
return $this->execute('api/pay/refund', $params);
|
||||
}
|
||||
|
||||
private function buildRequestParam($params){
|
||||
$params['pid'] = $this->pid;
|
||||
$params['timestamp'] = time().'';
|
||||
$mysign = $this->getSign($params);
|
||||
$params['sign'] = $mysign;
|
||||
$params['sign_type'] = $this->sign_type;
|
||||
return $params;
|
||||
}
|
||||
|
||||
// 生成签名
|
||||
private function getSign($params){
|
||||
return $this->rsaPrivateSign($this->getSignContent($params));
|
||||
}
|
||||
|
||||
// 获取待签名字符串
|
||||
private function getSignContent($params){
|
||||
ksort($params);
|
||||
$signstr = '';
|
||||
foreach ($params as $k => $v) {
|
||||
if(is_array($v) || $this->isEmpty($v) || $k == 'sign' || $k == 'sign_type') continue;
|
||||
$signstr .= '&' . $k . '=' . $v;
|
||||
}
|
||||
$signstr = substr($signstr, 1);
|
||||
return $signstr;
|
||||
}
|
||||
|
||||
private function isEmpty($value)
|
||||
{
|
||||
return $value === null || trim($value) === '';
|
||||
}
|
||||
|
||||
// 商户私钥签名
|
||||
private function rsaPrivateSign($data){
|
||||
$key = "-----BEGIN PRIVATE KEY-----\n" .
|
||||
wordwrap($this->merchant_private_key, 64, "\n", true) .
|
||||
"\n-----END PRIVATE KEY-----";
|
||||
$privatekey = openssl_get_privatekey($key);
|
||||
if(!$privatekey){
|
||||
throw new \Exception('签名失败,商户私钥错误');
|
||||
}
|
||||
openssl_sign($data, $sign, $privatekey, OPENSSL_ALGO_SHA256);
|
||||
return base64_encode($sign);
|
||||
}
|
||||
|
||||
// 平台公钥验签
|
||||
private function rsaPublicVerify($data, $sign){
|
||||
$key = "-----BEGIN PUBLIC KEY-----\n" .
|
||||
wordwrap($this->platform_public_key, 64, "\n", true) .
|
||||
"\n-----END PUBLIC KEY-----";
|
||||
$publickey = openssl_get_publickey($key);
|
||||
if (!$publickey) {
|
||||
throw new \Exception("验签失败,平台公钥错误");
|
||||
}
|
||||
$result = openssl_verify($data, base64_decode($sign), $publickey, OPENSSL_ALGO_SHA256);
|
||||
return $result === 1;
|
||||
}
|
||||
|
||||
// 请求外部资源
|
||||
private function getHttpResponse($url, $post = false, $timeout = 10){
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
$httpheader[] = "Accept: */*";
|
||||
$httpheader[] = "Accept-Language: zh-CN,zh;q=0.8";
|
||||
$httpheader[] = "Connection: close";
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader);
|
||||
curl_setopt($ch, CURLOPT_HEADER, false);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
if($post){
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
|
||||
}
|
||||
$response = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
19
doc/api/legacy/sdk/v2/lib/epay.config.php
Normal file
19
doc/api/legacy/sdk/v2/lib/epay.config.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
/* *
|
||||
* 配置文件
|
||||
*/
|
||||
|
||||
$epay_config = [
|
||||
//支付接口地址
|
||||
'apiurl' => 'http://pay.www.com/',
|
||||
|
||||
//商户ID
|
||||
'pid' => '1000',
|
||||
|
||||
//平台公钥
|
||||
'platform_public_key' => 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApHG7SIN16fd9uZfjZunZuAReemVQe5YNxBhbkogsRkZ86xuDVDCmhRXEzw7Ta3tXPnMIFRJFdjOCfFVarqcOLICtBiiZZ7Y4D6aIMhmOSliIJ3qWUnU75Wr2WMTIJ1o2pnPmczQ2YjAAy1DtQCc/qs35j24zuNYZw2WluSdiMckPFgge93RK6cq/Feqfuzq7y+m87x02gxbbTGVf24YH2f7H9qZSKCxRXHQoVIWTlyHULcY3OY+1CVdU2SKlIWHJ31eoPznXBLUo0UB0rNZnYrHG2mIlD2S119UTwZwx9WTG/v7Cb2lHVybjfL5/KLitddfqcLjJsYXh6KhEtsO6CwIDAQAB',
|
||||
|
||||
//商户私钥
|
||||
'merchant_private_key' => 'MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCBIB1e5lAYtFyXq5I8UIQ6KidYZcWkn0SwVS8Rk0SNZVrvL/UJk6Q1zkJs4pUCykTBS/tTrP2rNPOsK1VO/AQHIzhvAujsv7UK2LptcsuNRPCF5GYxndQnOawAGKNQKsMuNcDzyuyTMbZIBEYSRWIoU3dMz4wWEFso/VdVS4uKTZWZnBOeCDdzDAJ7TwbmaOkT919DfZbXAoMH9n3sG4BMpqQExTDoFY6dq6EPXCWVZgoUfecAgNKSfX5TagSUaAxq4eF5vsUfvj+LFpYIrIssmSVErtZuRXLHWVSEbsNxdDPNuS3BtxWEY7GRPF9RJevtoC5L5LN7Gn+RYCqZNZv7AgMBAAECggEAEA6ZTb11hQzwsrUAM1s5MNkgbsABIDk6BnTMAfMpRC1awyxYhqoDHTnFTYWuTVwvyUW/PtGKnelbdTPSS5x6jRSr0N+GGDgNYF2Wbpkm3Ni6Jubsb7ZrtRED5Y3Vc9j4JTKZXaJaDEJ9+LNSBLWiFi0C7zH5U/O8ElB8CrxL4ZUaZv0JgV9NcDpS5jAtpPSyBLrdhbEheertJiHQU0V+FaaXq8taNcYIA/Xim6+vqcFFtUA3PBBTXHn/NE5uasXi+N+De4IT+dBmirzVSZjviDPr9RSBUi6KPUSXx6eDa26SKeEqJZvBtlASDM+ZC0yhDz0eyV49tMjk7eF5fnCIwQKBgQC2nEiR2t5Q02tHaKesZMRGOwxEyMFQj6viDW+Yffg59Tu6QYuqdR558/zmzWcJFMH3DVQzTXpzPNU9TA3/yT/Q42iKBP70K8O9tJO+gd/jLHLqgw90Wyh2b4FJXXQqVQMkxGBQKRfNi6krWigJNBs8Z8IhczorQHYNbBIUI05poQKBgQC1BRI8zKf+85GuJXTxJ93RXbkOQMUIhT/6eyFTZvCLC9Qqba1/1ouNbtmxNsFFIC+n+rHRN9btKt90m9YFvXD90m3y34M88QjvaQcA1Kng9Q6Xia8DizpVIYGAR/Pfn36BZQeHHVz9te6QJ9hVOgZO3GG62Echd9M/rwOzuU14GwKBgFGtS2Q5khByz9wLuluIYqXLCWzGoninGkksm0qIpXs+7e0cHh0q72u6rtaI7toH983Jn2ym7esXPYWCPAy5dhq3bG23WFXcMVvrpd2i94IDwo6T+lif4VRAAYLQEwJQLezHDREtoCDmo87pL1kWfkwhWJpfkJgB6AuO1/M763mhAoGBAIPEGj9plcwOzndeSp6UL3IMb/1BBmuqWyTgZiTIpMYCKUFtLsMEj/a2vv2xZsQDpsz2vmMV63weHiRKn2L0QABzIZeOPYCpz6A96lwfcT0QBLwn+95vhVmclyCiv5GDDtnviag/poYD3ZDPgDihkR/sabNRZY2mJH6RzfcQJqULAoGALkSkqr0bplhfyAA6bO42l64th4YUqwouTEgp7rE36wQ28THj0a88HLU4CeiCR6LQAEGpKk04Vst97C1Q5ZeD5rc4xKINl8K5HUH8SsdMDq3r22xur2qr4kanW4hf2P/ehOeEKGuhSL+ZWeApvt1c0rqH4MQT1/7qR/dO2MikkMg=',
|
||||
];
|
||||
|
||||
45
doc/api/legacy/sdk/v2/notify_url.php
Normal file
45
doc/api/legacy/sdk/v2/notify_url.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
/* *
|
||||
* 功能:彩虹易支付异步通知页面
|
||||
* 说明:
|
||||
* 以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
|
||||
*/
|
||||
|
||||
require_once("lib/epay.config.php");
|
||||
require_once("lib/EpayCore.class.php");
|
||||
|
||||
//计算得出通知验证结果
|
||||
$epay = new EpayCore($epay_config);
|
||||
$verify_result = $epay->verify($_GET);
|
||||
|
||||
if($verify_result) {//验证成功
|
||||
|
||||
//商户订单号
|
||||
$out_trade_no = $_GET['out_trade_no'];
|
||||
|
||||
//彩虹易支付交易号
|
||||
$trade_no = $_GET['trade_no'];
|
||||
|
||||
//交易状态
|
||||
$trade_status = $_GET['trade_status'];
|
||||
|
||||
//支付方式
|
||||
$type = $_GET['type'];
|
||||
|
||||
//支付金额
|
||||
$money = $_GET['money'];
|
||||
|
||||
if ($_GET['trade_status'] == 'TRADE_SUCCESS') {
|
||||
//判断该笔订单是否在商户网站中已经做过处理
|
||||
//如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
|
||||
//如果有做过处理,不执行商户的业务程序
|
||||
}
|
||||
|
||||
//验证成功返回
|
||||
echo "success";
|
||||
}
|
||||
else {
|
||||
//验证失败
|
||||
echo "fail";
|
||||
}
|
||||
?>
|
||||
17
doc/api/legacy/sdk/v2/query.php
Normal file
17
doc/api/legacy/sdk/v2/query.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
/**
|
||||
* 查询订单
|
||||
*/
|
||||
require_once("lib/epay.config.php");
|
||||
require_once("lib/EpayCore.class.php");
|
||||
|
||||
$trade_no = '2024071519404366151';
|
||||
$epay = new EpayCore($epay_config);
|
||||
try{
|
||||
$result = $epay->queryOrder($trade_no);
|
||||
}catch(Exception $e){
|
||||
echo $e->getMessage();
|
||||
exit;
|
||||
}
|
||||
|
||||
print_r($result);
|
||||
19
doc/api/legacy/sdk/v2/refund.php
Normal file
19
doc/api/legacy/sdk/v2/refund.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
/**
|
||||
* 退款
|
||||
*/
|
||||
require_once("lib/epay.config.php");
|
||||
require_once("lib/EpayCore.class.php");
|
||||
|
||||
$out_refund_no = date("YmdHis").rand(111,999);
|
||||
$trade_no = '2024071519404366151';
|
||||
$money = '1.00';
|
||||
$epay = new EpayCore($epay_config);
|
||||
try{
|
||||
$result = $epay->refund($out_refund_no, $trade_no, $money);
|
||||
}catch(Exception $e){
|
||||
echo $e->getMessage();
|
||||
exit;
|
||||
}
|
||||
|
||||
print_r($result);
|
||||
55
doc/api/legacy/sdk/v2/return_url.php
Normal file
55
doc/api/legacy/sdk/v2/return_url.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/* *
|
||||
* 功能:彩虹易支付页面跳转同步通知页面
|
||||
* 说明:
|
||||
* 以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
|
||||
*/
|
||||
|
||||
require_once("lib/epay.config.php");
|
||||
require_once("lib/EpayCore.class.php");
|
||||
?>
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>支付返回页面</title>
|
||||
</head>
|
||||
<body>
|
||||
<?php
|
||||
//计算得出通知验证结果
|
||||
$epay = new EpayCore($epay_config);
|
||||
$verify_result = $epay->verify($_GET);
|
||||
|
||||
if($verify_result) {//验证成功
|
||||
|
||||
//商户订单号
|
||||
$out_trade_no = $_GET['out_trade_no'];
|
||||
|
||||
//支付宝交易号
|
||||
$trade_no = $_GET['trade_no'];
|
||||
|
||||
//交易状态
|
||||
$trade_status = $_GET['trade_status'];
|
||||
|
||||
//支付方式
|
||||
$type = $_GET['type'];
|
||||
|
||||
|
||||
if($_GET['trade_status'] == 'TRADE_SUCCESS') {
|
||||
//判断该笔订单是否在商户网站中已经做过处理
|
||||
//如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
|
||||
//如果有做过处理,不执行商户的业务程序
|
||||
}
|
||||
else {
|
||||
echo "trade_status=".$_GET['trade_status'];
|
||||
}
|
||||
|
||||
echo "<h3>验证成功</h3><br />";
|
||||
}
|
||||
else {
|
||||
//验证失败
|
||||
echo "<h3>验证失败</h3>";
|
||||
}
|
||||
?>
|
||||
</body>
|
||||
</html>
|
||||
37
doc/api/mer.md
Normal file
37
doc/api/mer.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# 商户后台接口
|
||||
|
||||
`mer` 前端调用 `/merapi`,接口定义在 `mpay/app/route/mer.php`。
|
||||
|
||||
## 基本信息
|
||||
|
||||
- 页面入口:`/mer`
|
||||
- API 前缀:`/merapi`
|
||||
- 登录接口:`POST /login`
|
||||
- 登录主体:`ma_merchant`
|
||||
- 保护接口:`MerchantAuthMiddleware`
|
||||
- 前端封装:`mer/src/api/modules/*`
|
||||
|
||||
## 模块速览
|
||||
|
||||
| 模块 | 主要路径 |
|
||||
| --- | --- |
|
||||
| 认证 | `/login`、`/logout`、`/user/profile` |
|
||||
| 商户资料 | `/merchant/profile`、`/merchant/change-password` |
|
||||
| 通道与路由 | `/my-channels`、`/my-channels/create-meta`、`/plugin-configs`、`/plugin-configs/options`、`/payment-plugins/{code}/schema`、`/route-preview` |
|
||||
| API 凭证 | `/api-credential`、`/api-credential/issue-credential` |
|
||||
| 订单 | `/pay-orders`、`/refund-orders`、`/refund-orders/{refundNo}`、`/refund-orders/{refundNo}/retry` |
|
||||
| 清算 | `/settlement-records`、`/settlement-records/{settleNo}` |
|
||||
| 资金 | `/withdrawable-balance`、`/balance-flows` |
|
||||
| 系统 | `/system/menu-tree`、`/system/dict-items` |
|
||||
|
||||
## 关联代码
|
||||
|
||||
- 控制器:`mpay/app/http/mer/controller`
|
||||
- 校验器:`mpay/app/http/mer/validation`
|
||||
- 前端接口:`mer/src/api/modules`
|
||||
|
||||
## 商户自助通道
|
||||
|
||||
- 商户可新增、修改、删除 `merchant_id=当前商户` 的自有通道。
|
||||
- 商户新增插件配置时只能选择管理后台标记为“允许商户端自助使用”的启用插件。
|
||||
- 商户通道绑定的 `api_config_id` 必须属于当前商户,不能引用平台配置或其它商户配置。
|
||||
109
doc/api/pages/alipay_h5.php
Normal file
109
doc/api/pages/alipay_h5.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
// 支付宝H5支付页面
|
||||
|
||||
if (!defined('IN_PLUGIN'))
|
||||
exit();
|
||||
?>
|
||||
<html lang="zh-cn">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
|
||||
<meta name="renderer" content="webkit" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<title>支付宝支付手机版</title>
|
||||
<link href="<?php echo $cdnpublic ?>twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet" />
|
||||
<link href="/assets/pay/css/mobile-style.css" rel="stylesheet" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="main">
|
||||
<div class="bg-ali"></div>
|
||||
<div class="payment-logo">
|
||||
<img src="/assets/pay/icon/alipay-white.svg" alt="logo">
|
||||
<span class="logo-tile">支付宝支付</span>
|
||||
</div>
|
||||
<div class="payment-content">
|
||||
<div class="content-info">
|
||||
<h1>¥<?php echo $order['realmoney'] ?></h1>
|
||||
<ul class="nk-activity">
|
||||
<li class="nk-activity-item">
|
||||
<span>商品名称:<?php echo $order['name'] ?></span>
|
||||
</li>
|
||||
<li class="nk-activity-item">
|
||||
<span>商户订单号:<?php echo $order['trade_no'] ?></span>
|
||||
</li>
|
||||
<li class="nk-activity-item">
|
||||
<span>创建时间:<?php echo $order['addtime'] ?></span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="content-footer">
|
||||
<a href="javascript:;" id="openUrl" class="btn btn-primary btn-block btn-lg">跳转到支付宝支付</a>
|
||||
<a href="javascript:checkresult()" onclick="" class="btn btn-info btn-block btn-lg">检测支付状态</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="<?php echo $cdnpublic ?>jquery/1.12.4/jquery.min.js"></script>
|
||||
<script src="<?php echo $cdnpublic ?>layer/3.1.1/layer.js"></script>
|
||||
<script>
|
||||
var url_scheme = '<?php echo $code_url ?>';
|
||||
function loadmsg() {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
url: "/getshop.php",
|
||||
data: { type: "wxpay", trade_no: "<?php echo $order['trade_no'] ?>" },
|
||||
success: function (data) {
|
||||
if (data.code == 1) {
|
||||
layer.msg('支付成功,正在跳转中...', { icon: 16, shade: 0.01, time: 15000 });
|
||||
setTimeout(window.location.href = <?php echo $redirect_url ?>, 1000);
|
||||
} else {
|
||||
setTimeout("loadmsg()", 2000);
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
setTimeout("loadmsg()", 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
function checkresult() {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
url: "/getshop.php",
|
||||
data: { type: "wxpay", trade_no: "<?php echo $order['trade_no'] ?>" },
|
||||
success: function (data) {
|
||||
if (data.code == 1) {
|
||||
layer.msg('支付成功,正在跳转中...', { icon: 16, shade: 0.01, time: 15000 });
|
||||
setTimeout(window.location.href = <?php echo $redirect_url ?>, 1000);
|
||||
} else {
|
||||
layer.msg('您还未完成付款,请继续付款', { shade: 0, time: 1500 });
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
layer.msg('服务器错误');
|
||||
}
|
||||
});
|
||||
}
|
||||
window.onload = function () {
|
||||
window.onpopstate = function (e) {
|
||||
if (e.state == 'forward' || confirm('是否取消支付并返回?')) {
|
||||
window.history.back();
|
||||
} else {
|
||||
e.preventDefault();
|
||||
window.history.pushState('forward', null, '');
|
||||
}
|
||||
};
|
||||
window.history.pushState('forward', null, '');
|
||||
|
||||
document.getElementById("openUrl").href = url_scheme;
|
||||
if (!url_scheme.startsWith('http://') && !url_scheme.startsWith('https://') && navigator.userAgent.indexOf('EdgA/') == -1) {
|
||||
window.location.href = url_scheme;
|
||||
}
|
||||
setTimeout("loadmsg()", 3000);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
84
doc/api/pages/alipay_jspay.php
Normal file
84
doc/api/pages/alipay_jspay.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
// 支付宝JS支付页面
|
||||
|
||||
if(!defined('IN_PLUGIN'))exit();
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
|
||||
<title>支付宝支付</title>
|
||||
<link href="/assets/pay/css/weui.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="container js_container">
|
||||
<div class="page msg">
|
||||
<div class="weui_msg">
|
||||
<div class="weui_icon_area"><i class="weui_icon_info weui_icon_msg"></i></div>
|
||||
<div class="weui_text_area">
|
||||
<h2 class="weui_msg_title">正在跳转支付...</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="<?php echo $cdnpublic?>jquery/1.12.4/jquery.min.js"></script>
|
||||
<script src="<?php echo $cdnpublic?>layer/3.1.1/layer.js"></script>
|
||||
<script>
|
||||
document.body.addEventListener('touchmove', function (event) {
|
||||
event.preventDefault();
|
||||
},{ passive: false });
|
||||
|
||||
var tradeNO = '<?php echo $alipay_trade_no?>';
|
||||
|
||||
function Alipayready(callback) {
|
||||
if (window.AlipayJSBridge) {
|
||||
callback && callback();
|
||||
} else {
|
||||
document.addEventListener('AlipayJSBridgeReady', callback, false);
|
||||
}
|
||||
}
|
||||
function AlipayJsPay() {
|
||||
Alipayready(function(){
|
||||
AlipayJSBridge.call("tradePay",{
|
||||
tradeNO: tradeNO
|
||||
}, function(result){
|
||||
var msg = "";
|
||||
if(result.resultCode == "9000"){
|
||||
loadmsg();
|
||||
}else if(result.resultCode == "8000"){
|
||||
msg = "正在处理中";
|
||||
}else if(result.resultCode == "4000"){
|
||||
msg = "订单支付失败";
|
||||
}else if(result.resultCode == "6002"){
|
||||
msg = "网络连接出错";
|
||||
}
|
||||
if (msg!="") {
|
||||
layer.msg(msg);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
function loadmsg() {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
url: "/getshop.php",
|
||||
data: {type: "wxpay", trade_no: "<?php echo TRADE_NO?>"},
|
||||
success: function (data) {
|
||||
if (data.code == 1) {
|
||||
layer.msg('支付成功,正在跳转中...', {icon: 16,shade: 0.01,time: 15000});
|
||||
window.location.href=<?php echo $redirect_url?>;
|
||||
}else{
|
||||
setTimeout("loadmsg()", 2000);
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
setTimeout("loadmsg()", 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
window.onload = AlipayJsPay();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
169
doc/api/pages/alipay_qrcode.php
Normal file
169
doc/api/pages/alipay_qrcode.php
Normal file
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
// 支付宝扫码支付页面
|
||||
|
||||
if(!defined('IN_PLUGIN'))exit();
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta http-equiv="Content-Language" content="zh-cn">
|
||||
<meta name="renderer" content="webkit">
|
||||
<title>支付宝扫码支付</title>
|
||||
<link href="/assets/css/alipay_pay.css?v=3" rel="stylesheet" media="screen">
|
||||
</head>
|
||||
<body>
|
||||
<img src="/assets/img/guide1.png" alt="引导图" class="top-guide" style="max-width: 100%;margin: auto;display:none">
|
||||
<div class="guide" style="display:none"><img src="/assets/img/guide2.png" alt="引导图" style="width: 96%;"></div>
|
||||
<div class="body">
|
||||
<h1 class="mod-title">
|
||||
<span class="ico-wechat"></span><span class="text">支付宝扫码支付</span>
|
||||
</h1>
|
||||
<div class="mod-ct">
|
||||
<div class="order">
|
||||
</div>
|
||||
<div class="amount">¥<?php echo $order['realmoney']?></div>
|
||||
<div class="qr-image" id="qrcode">
|
||||
</div>
|
||||
<div class="open_app" style="display: none;">
|
||||
<a class="btn-open-app">打开支付宝APP继续付款</a><br/><br/><br/>
|
||||
<a onclick="checkresult()" class="btn-check">我已付款,返回查看订单</a>
|
||||
</div>
|
||||
<div class="detail" id="orderDetail">
|
||||
<dl class="detail-ct" style="display: none;">
|
||||
<dt>购买物品</dt>
|
||||
<dd id="productName"><?php echo $order['name']?></dd>
|
||||
<dt>商户订单号</dt>
|
||||
<dd id="billId"><?php echo $order['trade_no']?></dd>
|
||||
<dt>创建时间</dt>
|
||||
<dd id="createTime"><?php echo $order['addtime']?></dd>
|
||||
</dl>
|
||||
<a href="javascript:void(0)" class="arrow"><i class="ico-arrow"></i></a>
|
||||
</div>
|
||||
<div class="tip">
|
||||
<span class="dec dec-left"></span>
|
||||
<span class="dec dec-right"></span>
|
||||
<div class="ico-scan"></div>
|
||||
<div class="tip-text">
|
||||
<p>请使用支付宝扫一扫</p>
|
||||
<p>扫描二维码完成支付</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tip-text">
|
||||
</div>
|
||||
</div>
|
||||
<script src="<?php echo $cdnpublic?>jquery/1.12.4/jquery.min.js"></script>
|
||||
<script src="<?php echo $cdnpublic?>layer/3.1.1/layer.js"></script>
|
||||
<script src="<?php echo $cdnpublic?>jquery.qrcode/1.0/jquery.qrcode.min.js"></script>
|
||||
<script>
|
||||
var code_url = '<?php echo $code_url?>';
|
||||
var code_type = code_url.indexOf('data:image/')>-1?1:0;
|
||||
if(code_type == 0){
|
||||
var url_scheme = 'alipays://platformapi/startapp?appId=20000067&url=' + encodeURIComponent(code_url);
|
||||
$('#qrcode').qrcode({
|
||||
text: code_url,
|
||||
width: 230,
|
||||
height: 230,
|
||||
foreground: "#000000",
|
||||
background: "#ffffff",
|
||||
typeNumber: -1
|
||||
});
|
||||
}else{
|
||||
$('#qrcode').html('<img src="'+code_url+'"/>');
|
||||
}
|
||||
// 订单详情
|
||||
$('#orderDetail .arrow').click(function (event) {
|
||||
if ($('#orderDetail').hasClass('detail-open')) {
|
||||
$('#orderDetail .detail-ct').slideUp(500, function () {
|
||||
$('#orderDetail').removeClass('detail-open');
|
||||
});
|
||||
} else {
|
||||
$('#orderDetail .detail-ct').slideDown(500, function () {
|
||||
$('#orderDetail').addClass('detail-open');
|
||||
});
|
||||
}
|
||||
});
|
||||
function loadmsg() {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
url: "/getshop.php",
|
||||
data: {type: "alipay", trade_no: "<?php echo $order['trade_no']?>"},
|
||||
success: function (data) {
|
||||
if (data.code == 1) {
|
||||
layer.msg('支付成功,正在跳转中...', {icon: 16,shade: 0.1,time: 15000});
|
||||
setTimeout(window.location.href=data.backurl, 1000);
|
||||
}else{
|
||||
setTimeout("loadmsg()", 2000);
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
setTimeout("loadmsg()", 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
function checkresult() {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
url: "/getshop.php",
|
||||
data: {type: "alipay", trade_no: "<?php echo $order['trade_no']?>"},
|
||||
success: function (data) {
|
||||
if (data.code == 1) {
|
||||
layer.msg('支付成功,正在跳转中...', {icon: 16,shade: 0.1,time: 15000});
|
||||
setTimeout(window.location.href=data.backurl, 1000);
|
||||
}else{
|
||||
layer.msg('您还未完成付款,请继续付款', {shade: 0,time: 1500});
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
layer.msg('服务器错误');
|
||||
}
|
||||
});
|
||||
}
|
||||
var isMobile = function (){
|
||||
var ua = navigator.userAgent;
|
||||
var ipad = ua.match(/(iPad).*OS\s([\d_]+)/),
|
||||
isIphone =!ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/),
|
||||
isAndroid = ua.match(/(Android)\s+([\d.]+)/);
|
||||
return isIphone || isAndroid;
|
||||
}
|
||||
function wx_open(){
|
||||
$(".guide").show();
|
||||
//layer.alert('请点击屏幕右上角,<b>在浏览器打开</b>即可跳转支付。<br/><font color="red">支付成功后,回到微信查看结果</font>', {title:'支付提示'});
|
||||
}
|
||||
window.onload = function(){
|
||||
if(isMobile()){
|
||||
window.onpopstate=function (e) {
|
||||
if(e.state=='forward' || confirm('是否取消支付并返回?')){
|
||||
window.history.back();
|
||||
}else{
|
||||
e.preventDefault();
|
||||
window.history.pushState('forward', null, '');
|
||||
}
|
||||
};
|
||||
window.history.pushState('forward', null, '');
|
||||
}
|
||||
if(isMobile() && code_type==0){
|
||||
$('.open_app').show();
|
||||
if(navigator.userAgent.indexOf('MicroMessenger/')>0){
|
||||
$(".top-guide").show();
|
||||
$('.btn-open-app').attr('href', 'javascript:wx_open()');
|
||||
}else{
|
||||
$('.btn-open-app').attr('href', url_scheme)
|
||||
if(navigator.userAgent.indexOf('EdgA/')==-1 && $(window).height() > $(window).width()){
|
||||
setTimeout(window.location.href = url_scheme, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
$("body").click(function(){
|
||||
if (!$(".guide").is(":hidden")) {
|
||||
$(".guide").hide();
|
||||
}
|
||||
});
|
||||
setTimeout("loadmsg()", 2000);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
91
doc/api/pages/alipay_qrcodepc.php
Normal file
91
doc/api/pages/alipay_qrcodepc.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
// 支付宝扫码支付页面
|
||||
|
||||
if(!defined('IN_PLUGIN'))exit();
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta http-equiv="Content-Language" content="zh-cn">
|
||||
<meta name="renderer" content="webkit">
|
||||
<title>支付宝扫码支付</title>
|
||||
<link href="/assets/css/alipay_pay.css?v=2" rel="stylesheet" media="screen">
|
||||
</head>
|
||||
<body>
|
||||
<div class="body">
|
||||
<h1 class="mod-title">
|
||||
<span class="ico-wechat"></span><span class="text">支付宝扫码支付</span>
|
||||
</h1>
|
||||
<div class="mod-ct">
|
||||
<div class="order">
|
||||
</div>
|
||||
<div class="amount">¥<?php echo $order['realmoney']?></div>
|
||||
<div class="qr-image" id="qrcode">
|
||||
<iframe src="<?php echo $code_url?>" width="230px" height="230px" frameborder="0" scrolling="no" seamless></iframe>
|
||||
</div>
|
||||
<div class="detail" id="orderDetail">
|
||||
<dl class="detail-ct" style="display: none;">
|
||||
<dt>购买物品</dt>
|
||||
<dd id="productName"><?php echo $order['name']?></dd>
|
||||
<dt>商户订单号</dt>
|
||||
<dd id="billId"><?php echo $order['trade_no']?></dd>
|
||||
<dt>创建时间</dt>
|
||||
<dd id="createTime"><?php echo $order['addtime']?></dd>
|
||||
</dl>
|
||||
<a href="javascript:void(0)" class="arrow"><i class="ico-arrow"></i></a>
|
||||
</div>
|
||||
<div class="tip">
|
||||
<span class="dec dec-left"></span>
|
||||
<span class="dec dec-right"></span>
|
||||
<div class="ico-scan"></div>
|
||||
<div class="tip-text">
|
||||
<p>请使用支付宝扫一扫</p>
|
||||
<p>扫描二维码完成支付</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tip-text">
|
||||
</div>
|
||||
</div>
|
||||
<script src="<?php echo $cdnpublic?>jquery/1.12.4/jquery.min.js"></script>
|
||||
<script src="<?php echo $cdnpublic?>layer/3.1.1/layer.js"></script>
|
||||
<script src="<?php echo $cdnpublic?>jquery.qrcode/1.0/jquery.qrcode.min.js"></script>
|
||||
<script>
|
||||
// 订单详情
|
||||
$('#orderDetail .arrow').click(function (event) {
|
||||
if ($('#orderDetail').hasClass('detail-open')) {
|
||||
$('#orderDetail .detail-ct').slideUp(500, function () {
|
||||
$('#orderDetail').removeClass('detail-open');
|
||||
});
|
||||
} else {
|
||||
$('#orderDetail .detail-ct').slideDown(500, function () {
|
||||
$('#orderDetail').addClass('detail-open');
|
||||
});
|
||||
}
|
||||
});
|
||||
function loadmsg() {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
url: "/getshop.php",
|
||||
data: {type: "alipay", trade_no: "<?php echo $order['trade_no']?>"},
|
||||
success: function (data) {
|
||||
if (data.code == 1) {
|
||||
layer.msg('支付成功,正在跳转中...', {icon: 16,shade: 0.1,time: 15000});
|
||||
setTimeout(window.location.href=data.backurl, 1000);
|
||||
}else{
|
||||
setTimeout("loadmsg()", 2000);
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
setTimeout("loadmsg()", 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
window.onload = function(){
|
||||
setTimeout("loadmsg()", 2000);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
149
doc/api/pages/bank_qrcode.php
Normal file
149
doc/api/pages/bank_qrcode.php
Normal file
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
// 银联云闪付扫码支付页面
|
||||
|
||||
if(!defined('IN_PLUGIN'))exit();
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta http-equiv="Content-Language" content="zh-cn">
|
||||
<meta name="renderer" content="webkit">
|
||||
<title>银联云闪付扫码支付</title>
|
||||
<link href="/assets/css/bank_pay.css?v=3" rel="stylesheet" media="screen">
|
||||
</head>
|
||||
<body>
|
||||
<div class="body">
|
||||
<h1 class="mod-title">
|
||||
<span class="ico-wechat"></span><span class="text">银联云闪付扫码支付</span>
|
||||
</h1>
|
||||
<div class="mod-ct">
|
||||
<div class="order">
|
||||
</div>
|
||||
<div class="amount">¥<?php echo $order['realmoney']?></div>
|
||||
<div class="qr-image" id="qrcode">
|
||||
</div>
|
||||
<div class="open_app" style="display: none;">
|
||||
<a class="btn-open-app">打开云闪付APP继续付款</a><br/><br/><br/>
|
||||
<a onclick="checkresult()" class="btn-check">我已付款,返回查看订单</a>
|
||||
</div>
|
||||
<div class="detail" id="orderDetail">
|
||||
<dl class="detail-ct" style="display: none;">
|
||||
<dt>购买物品</dt>
|
||||
<dd id="productName"><?php echo $order['name']?></dd>
|
||||
<dt>商户订单号</dt>
|
||||
<dd id="billId"><?php echo $order['trade_no']?></dd>
|
||||
<dt>创建时间</dt>
|
||||
<dd id="createTime"><?php echo $order['addtime']?></dd>
|
||||
</dl>
|
||||
<a href="javascript:void(0)" class="arrow"><i class="ico-arrow"></i></a>
|
||||
</div>
|
||||
<div class="tip">
|
||||
<span class="dec dec-left"></span>
|
||||
<span class="dec dec-right"></span>
|
||||
<div class="ico-scan"></div>
|
||||
<div class="tip-text">
|
||||
<p>请使用银联云闪付扫一扫</p>
|
||||
<p>扫描二维码完成支付</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tip-text">
|
||||
</div>
|
||||
</div>
|
||||
<script src="<?php echo $cdnpublic?>jquery/1.12.4/jquery.min.js"></script>
|
||||
<script src="<?php echo $cdnpublic?>layer/3.1.1/layer.js"></script>
|
||||
<script src="<?php echo $cdnpublic?>jquery.qrcode/1.0/jquery.qrcode.min.js"></script>
|
||||
<script>
|
||||
var code_url = '<?php echo $code_url?>';
|
||||
var code_type = code_url.indexOf('data:image/')>-1?1:0;
|
||||
if(code_type == 0){
|
||||
var url_scheme = 'upwallet://html/' + code_url.replace('https://', '').replace('http://', '');
|
||||
$('#qrcode').qrcode({
|
||||
text: code_url,
|
||||
width: 230,
|
||||
height: 230,
|
||||
foreground: "#000000",
|
||||
background: "#ffffff",
|
||||
typeNumber: -1
|
||||
});
|
||||
}else{
|
||||
$('#qrcode').html('<img src="'+code_url+'"/>');
|
||||
}
|
||||
// 订单详情
|
||||
$('#orderDetail .arrow').click(function (event) {
|
||||
if ($('#orderDetail').hasClass('detail-open')) {
|
||||
$('#orderDetail .detail-ct').slideUp(500, function () {
|
||||
$('#orderDetail').removeClass('detail-open');
|
||||
});
|
||||
} else {
|
||||
$('#orderDetail .detail-ct').slideDown(500, function () {
|
||||
$('#orderDetail').addClass('detail-open');
|
||||
});
|
||||
}
|
||||
});
|
||||
function loadmsg() {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
url: "/getshop.php",
|
||||
data: {type: "bank", trade_no: "<?php echo $order['trade_no']?>"},
|
||||
success: function (data) {
|
||||
if (data.code == 1) {
|
||||
layer.msg('支付成功,正在跳转中...', {icon: 16,shade: 0.1,time: 15000});
|
||||
setTimeout(window.location.href=data.backurl, 1000);
|
||||
}else{
|
||||
setTimeout("loadmsg()", 2000);
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
setTimeout("loadmsg()", 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
function checkresult() {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
url: "/getshop.php",
|
||||
data: {type: "bank", trade_no: "<?php echo $order['trade_no']?>"},
|
||||
success: function (data) {
|
||||
if (data.code == 1) {
|
||||
layer.msg('支付成功,正在跳转中...', {icon: 16,shade: 0.1,time: 15000});
|
||||
setTimeout(window.location.href=data.backurl, 1000);
|
||||
}else{
|
||||
layer.msg('您还未完成付款,请继续付款', {shade: 0,time: 1500});
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
layer.msg('服务器错误');
|
||||
}
|
||||
});
|
||||
}
|
||||
var isMobile = function (){
|
||||
var ua = navigator.userAgent;
|
||||
var ipad = ua.match(/(iPad).*OS\s([\d_]+)/),
|
||||
isIphone =!ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/),
|
||||
isAndroid = ua.match(/(Android)\s+([\d.]+)/);
|
||||
return isIphone || isAndroid;
|
||||
}
|
||||
function wx_open(){
|
||||
layer.alert('请点击屏幕右上角,<b>在浏览器打开</b>即可跳转支付。<br/><font color="red">支付成功后,回到微信查看结果</font>', {title:'支付提示'});
|
||||
}
|
||||
window.onload = function(){
|
||||
if(isMobile() && code_type==0){
|
||||
$('.open_app').show();
|
||||
if(navigator.userAgent.indexOf('MicroMessenger/')>0){
|
||||
$('.btn-open-app').attr('onclick', 'wx_open()');
|
||||
}else{
|
||||
$('.btn-open-app').attr('href', url_scheme);
|
||||
if(navigator.userAgent.indexOf('EdgA/')==-1 && $(window).height() > $(window).width()){
|
||||
window.location.href = url_scheme;
|
||||
}
|
||||
}
|
||||
}
|
||||
setTimeout("loadmsg()", 2000);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
79
doc/api/pages/certok.php
Normal file
79
doc/api/pages/certok.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
/**
|
||||
* 实名认证成功页面
|
||||
**/
|
||||
if(!defined('IN_CRONLITE'))exit();
|
||||
?>
|
||||
<html class="weui-msg">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta id="viewport" name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
|
||||
<title>实名认证成功</title>
|
||||
<link href="/assets/css/weui.min.css" rel="stylesheet">
|
||||
<style>.page{position:absolute;top:0;right:0;bottom:0;left:0;overflow-y:auto;-webkit-overflow-scrolling:touch;box-sizing:border-box}</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="page">
|
||||
<div class="weui-msg">
|
||||
<div class="weui-msg__icon-area">
|
||||
<i class="weui-icon-success weui-icon_msg"></i>
|
||||
</div>
|
||||
<div class="weui-msg__text-area">
|
||||
<h2 class="weui-msg__title">实名认证成功</h2>
|
||||
<p class="weui-msg__desc">请返回浏览器查看结果</p>
|
||||
</div>
|
||||
<div class="weui-msg__opr-area">
|
||||
<p class="weui-btn-area">
|
||||
<a href="javascript:;" class="weui-btn weui-btn_default" id="Close">关闭</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="weui-msg__extra-area">
|
||||
<div class="weui-footer"><p class="weui-footer__links"></p></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="<?php echo $cdnpublic?>jquery/1.12.4/jquery.min.js"></script>
|
||||
<script>
|
||||
document.body.addEventListener('touchmove', function (event) {
|
||||
event.preventDefault();
|
||||
},{ passive: false });
|
||||
|
||||
if(navigator.userAgent.indexOf("AlipayClient") > -1){
|
||||
function Alipayready(callback) {
|
||||
if (window.AlipayJSBridge) {
|
||||
callback && callback();
|
||||
} else {
|
||||
document.addEventListener('AlipayJSBridgeReady', callback, false);
|
||||
}
|
||||
}
|
||||
Alipayready(function(){
|
||||
$('#Close').click(function() {
|
||||
AlipayJSBridge.call('popWindow');
|
||||
});
|
||||
})
|
||||
}else if(navigator.userAgent.indexOf("MicroMessenger") > -1){
|
||||
if (typeof WeixinJSBridge == "undefined") {
|
||||
if (document.addEventListener) {
|
||||
document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
|
||||
} else if (document.attachEvent) {
|
||||
document.attachEvent('WeixinJSBridgeReady', jsApiCall);
|
||||
document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
|
||||
}
|
||||
} else {
|
||||
jsApiCall();
|
||||
}
|
||||
function jsApiCall() {
|
||||
$('#Close').click(function() {
|
||||
WeixinJSBridge.call('closeWindow');
|
||||
});
|
||||
}
|
||||
}else{
|
||||
$('#Close').click(function() {
|
||||
window.opener=null;window.close();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
38
doc/api/pages/error.php
Normal file
38
doc/api/pages/error.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/*
|
||||
* 支付失败提示页面
|
||||
*/
|
||||
if(!defined('IN_PLUGIN'))exit();
|
||||
?>
|
||||
<html class="weui-msg">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta id="viewport" name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
|
||||
<title>错误提示</title>
|
||||
<link href="/assets/css/weui.min.css" rel="stylesheet">
|
||||
<style>.page{position:absolute;top:0;right:0;bottom:0;left:0;overflow-y:auto;-webkit-overflow-scrolling:touch;box-sizing:border-box}</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="page">
|
||||
<div class="weui-msg">
|
||||
<div class="weui-msg__icon-area">
|
||||
<i class="weui-icon-warn weui-icon_msg"></i>
|
||||
</div>
|
||||
<div class="weui-msg__text-area">
|
||||
<h2 class="weui-msg__title">错误提示</h2>
|
||||
<p class="weui-msg__desc">支付失败或支付超时,请返回重新发起支付</p>
|
||||
</div>
|
||||
<div class="weui-msg__extra-area">
|
||||
<div class="weui-footer"><p class="weui-footer__links"></p></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
document.body.addEventListener('touchmove', function (event) {
|
||||
event.preventDefault();
|
||||
},{ passive: false });
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
108
doc/api/pages/jdpay_qrcode.php
Normal file
108
doc/api/pages/jdpay_qrcode.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
// 京东扫码支付页面
|
||||
|
||||
if(!defined('IN_PLUGIN'))exit();
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta http-equiv="Content-Language" content="zh-cn">
|
||||
<meta name="renderer" content="webkit">
|
||||
<title>京东扫码支付</title>
|
||||
<link href="/assets/css/jd_pay.css" rel="stylesheet" media="screen">
|
||||
</head>
|
||||
<body>
|
||||
<div class="body">
|
||||
<h1 class="mod-title">
|
||||
<span class="ico-wechat"></span><span class="text">京东扫码支付</span>
|
||||
</h1>
|
||||
<div class="mod-ct">
|
||||
<div class="order">
|
||||
</div>
|
||||
<div class="amount">¥<?php echo $order['realmoney']?></div>
|
||||
<div class="qr-image" id="qrcode">
|
||||
</div>
|
||||
|
||||
<div class="detail" id="orderDetail">
|
||||
<dl class="detail-ct" style="display: none;">
|
||||
<dt>商家</dt>
|
||||
<dd id="storeName"><?php echo $sitename?></dd>
|
||||
<dt>购买物品</dt>
|
||||
<dd id="productName"><?php echo $order['name']?></dd>
|
||||
<dt>商户订单号</dt>
|
||||
<dd id="billId"><?php echo $order['trade_no']?></dd>
|
||||
<dt>创建时间</dt>
|
||||
<dd id="createTime"><?php echo $order['addtime']?></dd>
|
||||
</dl>
|
||||
<a href="javascript:void(0)" class="arrow"><i class="ico-arrow"></i></a>
|
||||
</div>
|
||||
<div class="tip">
|
||||
<span class="dec dec-left"></span>
|
||||
<span class="dec dec-right"></span>
|
||||
<div class="ico-scan"></div>
|
||||
<div class="tip-text">
|
||||
<p>请使用京东APP扫一扫</p>
|
||||
<p>扫描二维码完成支付</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tip-text">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="<?php echo $cdnpublic?>jquery/1.12.4/jquery.min.js"></script>
|
||||
<script src="<?php echo $cdnpublic?>layer/3.1.1/layer.js"></script>
|
||||
<script src="<?php echo $cdnpublic?>jquery.qrcode/1.0/jquery.qrcode.min.js"></script>
|
||||
<script>
|
||||
var code_url = '<?php echo $code_url?>';
|
||||
var code_type = code_url.indexOf('data:image/')>-1?1:0;
|
||||
if(code_type == 0){
|
||||
$('#qrcode').qrcode({
|
||||
text: code_url,
|
||||
width: 230,
|
||||
height: 230,
|
||||
foreground: "#000000",
|
||||
background: "#ffffff",
|
||||
typeNumber: -1
|
||||
});
|
||||
}else{
|
||||
$('#qrcode').html('<img src="'+code_url+'"/>');
|
||||
}
|
||||
// 订单详情
|
||||
$('#orderDetail .arrow').click(function (event) {
|
||||
if ($('#orderDetail').hasClass('detail-open')) {
|
||||
$('#orderDetail .detail-ct').slideUp(500, function () {
|
||||
$('#orderDetail').removeClass('detail-open');
|
||||
});
|
||||
} else {
|
||||
$('#orderDetail .detail-ct').slideDown(500, function () {
|
||||
$('#orderDetail').addClass('detail-open');
|
||||
});
|
||||
}
|
||||
});
|
||||
function loadmsg() {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
url: "/getshop.php",
|
||||
data: {type: "jdpay", trade_no: "<?php echo $order['trade_no']?>"},
|
||||
success: function (data) {
|
||||
if (data.code == 1) {
|
||||
layer.msg('支付成功,正在跳转中...', {icon: 16,shade: 0.1,time: 15000});
|
||||
setTimeout(window.location.href=data.backurl, 1000);
|
||||
}else{
|
||||
setTimeout("loadmsg()", 2000);
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
setTimeout("loadmsg()", 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
window.onload = function(){
|
||||
setTimeout("loadmsg()", 2000);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
67
doc/api/pages/jump.php
Normal file
67
doc/api/pages/jump.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
if(!defined('IN_PLUGIN'))exit();
|
||||
$useragent = strtolower($_SERVER['HTTP_USER_AGENT']);
|
||||
if(strpos($useragent, 'iphone')!==false || strpos($useragent, 'ipod')!==false){
|
||||
$alert = '<img src="//puep.qpic.cn/coral/Q3auHgzwzM4fgQ41VTF2rLrNvRzmibibqrjTFj5g2kzGyoQj3ViartAEQ/0" class="icon-safari" /> <span id="openm">Safari打开</span>';
|
||||
}elseif(strpos($useragent, 'micromessenger')!==false){
|
||||
$alert = '<img src="//puep.qpic.cn/coral/Q3auHgzwzM4fgQ41VTF2rLbNVmztN9ia6GPRJ0IFicucFTr4Pp8xzibsw/0" class="icon-safari" /> <span id="openm">浏览器打开</span>';
|
||||
}else{
|
||||
$alert = '<img src="//puep.qpic.cn/coral/Q3auHgzwzM4fgQ41VTF2rOCTm6gtUeQKX7m84xg47iaVosibGckrP0JQ/0" class="icon-safari" /> <span id="openm">浏览器打开</span>';
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>请使用浏览器打开</title>
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
|
||||
<meta content="yes" name="apple-mobile-web-app-capable"/>
|
||||
<meta content="black" name="apple-mobile-web-app-status-bar-style"/>
|
||||
<meta name="format-detection" content="telephone=no"/>
|
||||
<meta content="false" name="twcClient" id="twcClient"/>
|
||||
<meta name="aplus-touch" content="1"/>
|
||||
<style>
|
||||
body,html{width:100%;height:100%}
|
||||
*{margin:0;padding:0}
|
||||
body{background-color:#fff}
|
||||
.top-bar-guidance{font-size:15px;color:#fff;height:70%;line-height:1.8;padding-left:20px;padding-top:20px;background:url(//gw.alicdn.com/tfs/TB1eSZaNFXXXXb.XXXXXXXXXXXX-750-234.png) center top/contain no-repeat}
|
||||
.top-bar-guidance .icon-safari{width:25px;height:25px;vertical-align:middle;margin:0 .2em}
|
||||
.app-download-tip{margin:0 auto;width:290px;text-align:center;font-size:15px;color:#2466f4;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAcAQMAAACak0ePAAAABlBMVEUAAAAdYfh+GakkAAAAAXRSTlMAQObYZgAAAA5JREFUCNdjwA8acEkAAAy4AIE4hQq/AAAAAElFTkSuQmCC) left center/auto 15px repeat-x}
|
||||
.app-download-tip .guidance-desc{background-color:#fff;padding:0 5px}
|
||||
.app-download-btn{display:block;width:214px;height:40px;line-height:40px;margin:18px auto 0 auto;text-align:center;font-size:18px;color:#2466f4;border-radius:20px;border:.5px #2466f4 solid;text-decoration:none}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="top-bar-guidance">
|
||||
<p>点击右上角<?php echo $alert?></p>
|
||||
<p>可以继续浏览本站哦~</p>
|
||||
</div>
|
||||
<div class="app-download-tip">
|
||||
<span class="guidance-desc">您也可以复制本站网址,到其它浏览器打开</span>
|
||||
</div>
|
||||
<a class="app-download-btn" id="J_BtnDowanloadApp">点此继续访问</a>
|
||||
<a style="display: none;" href="" id="vurl" rel="noreferrer"></a>
|
||||
|
||||
<script src="//lib.baomitu.com/jquery/1.12.4/jquery.min.js"></script>
|
||||
<script>
|
||||
function openu(u){
|
||||
document.getElementById("vurl").href= u;
|
||||
document.getElementById("vurl").click();
|
||||
}
|
||||
var url = window.location.href;
|
||||
document.querySelector('body').addEventListener('touchmove', function (event) {
|
||||
event.preventDefault();
|
||||
});
|
||||
if(navigator.userAgent.indexOf("QQ/") > -1){
|
||||
openu("ucbrowser://"+url);
|
||||
openu("mttbrowser://url="+url);
|
||||
openu("googlechrome://"+url);
|
||||
$("html").on("click",function(){
|
||||
openu("ucbrowser://"+url);
|
||||
openu("mttbrowser://url="+url);
|
||||
openu("googlechrome://"+url);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
73
doc/api/pages/ok.php
Normal file
73
doc/api/pages/ok.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
// 支付成功页面
|
||||
|
||||
if(!defined('IN_PLUGIN'))exit();
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
|
||||
<title>支付结果</title>
|
||||
<link href="/assets/pay/css/weui.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="container js_container">
|
||||
<div class="page msg">
|
||||
<div class="weui_msg">
|
||||
<div class="weui_icon_area"><i class="weui_icon_success weui_icon_msg"></i></div>
|
||||
<div class="weui_text_area">
|
||||
<h2 class="weui_msg_title">支付成功</h2>
|
||||
<p class="weui_msg_desc">支付成功,请回到浏览器查看订单</p>
|
||||
</div>
|
||||
<div class="weui_opr_area">
|
||||
<p class="weui_btn_area">
|
||||
<a href="javascript:;" class="weui_btn weui_btn_primary" id="Close">关闭</a>
|
||||
<!--a href="javascript:;" class="weui_btn weui_btn_default">返回</a-->
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="<?php echo $cdnpublic?>jquery/1.12.4/jquery.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
document.body.addEventListener('touchmove', function (event) {
|
||||
event.preventDefault();
|
||||
},{ passive: false });
|
||||
if(navigator.userAgent.indexOf("AlipayClient") > -1){
|
||||
function Alipayready(callback) {
|
||||
if (window.AlipayJSBridge) {
|
||||
callback && callback();
|
||||
} else {
|
||||
document.addEventListener('AlipayJSBridgeReady', callback, false);
|
||||
}
|
||||
}
|
||||
Alipayready(function(){
|
||||
$('.weui_opr_area #Close').click(function() {
|
||||
AlipayJSBridge.call('popWindow');
|
||||
});
|
||||
})
|
||||
}else if(navigator.userAgent.indexOf("MicroMessenger") > -1){
|
||||
if (typeof WeixinJSBridge == "undefined") {
|
||||
if (document.addEventListener) {
|
||||
document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
|
||||
} else if (document.attachEvent) {
|
||||
document.attachEvent('WeixinJSBridgeReady', jsApiCall);
|
||||
document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
|
||||
}
|
||||
} else {
|
||||
jsApiCall();
|
||||
}
|
||||
function jsApiCall() {
|
||||
$('.weui_opr_area #Close').click(function() {
|
||||
WeixinJSBridge.call('closeWindow');
|
||||
});
|
||||
}
|
||||
}else{
|
||||
$('.weui_opr_area #Close').click(function() {
|
||||
window.opener=null;window.close();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
101
doc/api/pages/openid.php
Normal file
101
doc/api/pages/openid.php
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
/*
|
||||
* 获取openid结果页面
|
||||
*/
|
||||
if(!defined('IN_CRONLITE'))exit();
|
||||
?>
|
||||
<html class="weui-msg">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta id="viewport" name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
|
||||
<title>获取<?php echo $openid_name?></title>
|
||||
<link href="/assets/css/weui.min.css" rel="stylesheet">
|
||||
<style>.page{position:absolute;top:0;right:0;bottom:0;left:0;overflow-y:auto;-webkit-overflow-scrolling:touch;box-sizing:border-box}</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="page">
|
||||
<div class="weui-form">
|
||||
<div class="weui-msg__icon-area">
|
||||
<i class="weui-icon-success weui-icon_msg"></i>
|
||||
</div>
|
||||
<div class="weui-form__text-area">
|
||||
<h2 class="weui-form__title">获取<?php echo $openid_name?>成功</h2>
|
||||
</div>
|
||||
<div class="weui-form__control-area">
|
||||
<div class="weui-cells__group weui-cells__group_form">
|
||||
<div class="weui-cells__title">如未自动填写,请手动复制下方<?php echo $openid_name?>:</div>
|
||||
<div class="weui-cells weui-cells_form">
|
||||
<div class="weui-cell weui-cell_active">
|
||||
<div class="weui-cell__bd">
|
||||
<textarea class="weui-textarea" rows="2" style="text-align:center"><?php echo $openid_content?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="weui-form__opr-area">
|
||||
<a role="button" class="weui-btn weui-btn_default copy-btn" href="javascript:" data-clipboard-text="<?php echo $openid_content?>">点击复制</a>
|
||||
<a href="javascript:;" class="weui-btn weui-btn_warn" id="Close">关闭</a>
|
||||
</div>
|
||||
<div class="weui-form__extra-area">
|
||||
<div class="weui-footer"><p class="weui-footer__links"></p><p class="weui-footer__text">Copyright © <?php echo date("Y")?> <?php echo $conf['sitename']?></p></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="<?php echo $cdnpublic?>jquery/1.12.4/jquery.min.js"></script>
|
||||
<script src="<?php echo $cdnpublic?>layer/3.1.1/layer.js"></script>
|
||||
<script src="<?php echo $cdnpublic?>clipboard.js/1.7.1/clipboard.min.js"></script>
|
||||
<script>
|
||||
document.body.addEventListener('touchmove', function (event) {
|
||||
event.preventDefault();
|
||||
},{ passive: false });
|
||||
$(document).ready(function(){
|
||||
var clipboard = new Clipboard('.copy-btn');
|
||||
clipboard.on('success', function (e) {
|
||||
layer.msg('复制成功!', {icon: 1});
|
||||
});
|
||||
clipboard.on('error', function (e) {
|
||||
layer.msg('复制失败,请长按链接后手动复制', {icon: 2});
|
||||
});
|
||||
});
|
||||
if(navigator.userAgent.indexOf("AlipayClient/") > -1){
|
||||
function Alipayready(callback) {
|
||||
if (window.AlipayJSBridge) {
|
||||
callback && callback();
|
||||
} else {
|
||||
document.addEventListener('AlipayJSBridgeReady', callback, false);
|
||||
}
|
||||
}
|
||||
Alipayready(function(){
|
||||
$('#Close').click(function() {
|
||||
AlipayJSBridge.call('popWindow');
|
||||
});
|
||||
})
|
||||
}else if(navigator.userAgent.indexOf("MicroMessenger/") > -1){
|
||||
if (typeof WeixinJSBridge == "undefined") {
|
||||
if (document.addEventListener) {
|
||||
document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
|
||||
} else if (document.attachEvent) {
|
||||
document.attachEvent('WeixinJSBridgeReady', jsApiCall);
|
||||
document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
|
||||
}
|
||||
} else {
|
||||
jsApiCall();
|
||||
}
|
||||
function jsApiCall() {
|
||||
$('#Close').click(function() {
|
||||
WeixinJSBridge.call('closeWindow');
|
||||
});
|
||||
}
|
||||
}else if(navigator.userAgent.indexOf("QQ/") > -1){
|
||||
$('#Close').hide();
|
||||
}else {
|
||||
$('#Close').click(function() {
|
||||
window.opener=null;window.close();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
68
doc/api/pages/qqpay_jspay.php
Normal file
68
doc/api/pages/qqpay_jspay.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
// QQ公众号支付页面
|
||||
|
||||
if(!defined('IN_PLUGIN'))exit();
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
|
||||
<title>支付宝支付</title>
|
||||
<link href="/assets/pay/css/weui.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="container js_container">
|
||||
<div class="page msg">
|
||||
<div class="weui_msg">
|
||||
<div class="weui_icon_area"><i class="weui_icon_info weui_icon_msg"></i></div>
|
||||
<div class="weui_text_area">
|
||||
<h2 class="weui_msg_title">正在跳转支付...</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="<?php echo $cdnpublic?>jquery/1.12.4/jquery.min.js"></script>
|
||||
<script src="<?php echo $cdnpublic?>layer/3.1.1/layer.js"></script>
|
||||
<script src="//open.mobile.qq.com/sdk/qqapi.js?_bid=152"></script>
|
||||
<script>
|
||||
document.body.addEventListener('touchmove', function (event) {
|
||||
event.preventDefault();
|
||||
},{ passive: false });
|
||||
|
||||
function callpay()
|
||||
{
|
||||
mqq.tenpay.pay({
|
||||
tokenId: '<?php echo $tokenId; ?>',
|
||||
appInfo: "<?php echo $appInfo; ?>"
|
||||
}, function(result, resultCode){
|
||||
if(result.resultCode == 0){ //支付成功
|
||||
loadmsg();
|
||||
}
|
||||
});
|
||||
}
|
||||
function loadmsg() {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
url: "/getshop.php",
|
||||
data: {type: "qqpay", trade_no: "<?php echo $order['trade_no']?>"},
|
||||
success: function (data) {
|
||||
if (data.code == 1) {
|
||||
layer.msg('支付成功,正在跳转中...', {icon: 16,shade: 0.1,time: 15000});
|
||||
setTimeout(window.location.href=data.backurl, 1000);
|
||||
}else{
|
||||
setTimeout("loadmsg()", 2000);
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
setTimeout("loadmsg()", 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
window.onload = callpay();
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
112
doc/api/pages/qqpay_qrcode.php
Normal file
112
doc/api/pages/qqpay_qrcode.php
Normal file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
// QQ钱包扫码支付页面
|
||||
|
||||
if(!defined('IN_PLUGIN'))exit();
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta http-equiv="Content-Language" content="zh-cn">
|
||||
<meta name="renderer" content="webkit">
|
||||
<title>QQ钱包扫码支付</title>
|
||||
<link href="/assets/css/mqq_pay.css?v=1" rel="stylesheet" media="screen">
|
||||
</head>
|
||||
<body>
|
||||
<div class="body">
|
||||
<h1 class="mod-title">
|
||||
<span class="ico-wechat"></span><span class="text">QQ钱包扫码支付</span>
|
||||
</h1>
|
||||
<div class="mod-ct">
|
||||
<div class="order">
|
||||
</div>
|
||||
<div class="amount">¥<?php echo $order['realmoney']?></div>
|
||||
<div class="qr-image" id="qrcode">
|
||||
</div>
|
||||
|
||||
<div class="detail" id="orderDetail">
|
||||
<dl class="detail-ct" style="display: none;">
|
||||
<dt>商家</dt>
|
||||
<dd id="storeName"><?php echo $sitename?></dd>
|
||||
<dt>购买物品</dt>
|
||||
<dd id="productName"><?php echo $order['name']?></dd>
|
||||
<dt>商户订单号</dt>
|
||||
<dd id="billId"><?php echo $order['trade_no']?></dd>
|
||||
<dt>创建时间</dt>
|
||||
<dd id="createTime"><?php echo $order['addtime']?></dd>
|
||||
</dl>
|
||||
<a href="javascript:void(0)" class="arrow"><i class="ico-arrow"></i></a>
|
||||
</div>
|
||||
<div class="tip">
|
||||
<span class="dec dec-left"></span>
|
||||
<span class="dec dec-right"></span>
|
||||
<div class="ico-scan"></div>
|
||||
<div class="tip-text">
|
||||
<p>请使用手机QQ扫一扫</p>
|
||||
<p>扫描二维码完成支付</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tip-text">
|
||||
</div>
|
||||
</div>
|
||||
<div class="foot">
|
||||
<div class="inner">
|
||||
<p>手机用户可保存上方二维码到手机中</p>
|
||||
<p>在手机QQ扫一扫中选择“相册”即可</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="<?php echo $cdnpublic?>jquery/1.12.4/jquery.min.js"></script>
|
||||
<script src="<?php echo $cdnpublic?>layer/3.1.1/layer.js"></script>
|
||||
<script src="<?php echo $cdnpublic?>jquery.qrcode/1.0/jquery.qrcode.min.js"></script>
|
||||
<script>
|
||||
var code_url = '<?php echo $code_url?>';
|
||||
var code_type = code_url.indexOf('data:image/')>-1?1:0;
|
||||
if(code_type == 0){
|
||||
$('#qrcode').qrcode({
|
||||
text: code_url,
|
||||
width: 230,
|
||||
height: 230,
|
||||
foreground: "#000000",
|
||||
background: "#ffffff",
|
||||
typeNumber: -1
|
||||
});
|
||||
}else{
|
||||
$('#qrcode').html('<img src="'+code_url+'"/>');
|
||||
}
|
||||
// 订单详情
|
||||
$('#orderDetail .arrow').click(function (event) {
|
||||
if ($('#orderDetail').hasClass('detail-open')) {
|
||||
$('#orderDetail .detail-ct').slideUp(500, function () {
|
||||
$('#orderDetail').removeClass('detail-open');
|
||||
});
|
||||
} else {
|
||||
$('#orderDetail .detail-ct').slideDown(500, function () {
|
||||
$('#orderDetail').addClass('detail-open');
|
||||
});
|
||||
}
|
||||
});
|
||||
function loadmsg() {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
url: "/getshop.php",
|
||||
data: {type: "qqpay", trade_no: "<?php echo $order['trade_no']?>"},
|
||||
success: function (data) {
|
||||
if (data.code == 1) {
|
||||
layer.msg('支付成功,正在跳转中...', {icon: 16,shade: 0.1,time: 15000});
|
||||
setTimeout(window.location.href=data.backurl, 1000);
|
||||
}else{
|
||||
setTimeout("loadmsg()", 2000);
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
setTimeout("loadmsg()", 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
window.onload = loadmsg();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
111
doc/api/pages/qqpay_wap.php
Normal file
111
doc/api/pages/qqpay_wap.php
Normal file
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
// QQ钱包手机扫码页面
|
||||
|
||||
if (!defined('IN_PLUGIN'))
|
||||
exit();
|
||||
?>
|
||||
<html lang="zh-cn">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
|
||||
<meta name="renderer" content="webkit" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<title>QQ钱包支付手机版</title>
|
||||
<link href="<?php echo $cdnpublic ?>twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet" />
|
||||
<link href="/assets/pay/css/mobile-style.css" rel="stylesheet" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="main">
|
||||
<div class="bg-qq"></div>
|
||||
<div class="payment-logo">
|
||||
<img src="/assets/pay/icon/qqpay.svg" alt="logo">
|
||||
<span class="logo-tile">QQ支付</span>
|
||||
</div>
|
||||
<div class="payment-content">
|
||||
<div class="content-info">
|
||||
<h1>¥<?php echo $order['realmoney'] ?></h1>
|
||||
<ul class="nk-activity">
|
||||
<li class="nk-activity-item">
|
||||
<span>商品名称:<?php echo $order['name'] ?></span>
|
||||
</li>
|
||||
<li class="nk-activity-item">
|
||||
<span>商户订单号:<?php echo $order['trade_no'] ?></span>
|
||||
</li>
|
||||
<li class="nk-activity-item">
|
||||
<span>创建时间:<?php echo $order['addtime'] ?></span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="content-footer">
|
||||
<a href="javascript:;" id="openUrl" class="btn btn-warning btn-block btn-lg">跳转到QQ支付</a>
|
||||
<a href="javascript:checkresult()" onclick="" class="btn btn-info btn-block btn-lg">检测支付状态</a>
|
||||
<a href="?qrcode=1" class="btn btn-default btn-block btn-lg">切换扫码支付</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="<?php echo $cdnpublic ?>jquery/1.12.4/jquery.min.js"></script>
|
||||
<script src="<?php echo $cdnpublic ?>layer/3.1.1/layer.js"></script>
|
||||
<script>
|
||||
var code_url = '<?php echo $code_url ?>';
|
||||
var url_scheme = 'mqqapi://forward/url?src_type=web&style=default&=1&version=1&url_prefix=' + window.btoa(code_url);
|
||||
function loadmsg() {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
url: "/getshop.php",
|
||||
data: { type: "qqpay", trade_no: "<?php echo $order['trade_no'] ?>" },
|
||||
success: function (data) {
|
||||
if (data.code == 1) {
|
||||
layer.msg('支付成功,正在跳转中...', { icon: 16, shade: 0.1, time: 15000 });
|
||||
setTimeout(window.location.href = data.backurl, 1000);
|
||||
} else {
|
||||
setTimeout("loadmsg()", 2000);
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
setTimeout("loadmsg()", 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
function checkresult() {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
url: "/getshop.php",
|
||||
data: { type: "qqpay", trade_no: "<?php echo $order['trade_no'] ?>" },
|
||||
success: function (data) {
|
||||
if (data.code == 1) {
|
||||
layer.msg('支付成功,正在跳转中...', { icon: 16, shade: 0.1, time: 15000 });
|
||||
setTimeout(window.location.href = data.backurl, 1000);
|
||||
} else {
|
||||
layer.msg('您还未完成付款,请继续付款', { shade: 0, time: 1500 });
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
layer.msg('服务器错误');
|
||||
}
|
||||
});
|
||||
}
|
||||
window.onload = function () {
|
||||
window.onpopstate = function (e) {
|
||||
if (e.state == 'forward' || confirm('是否取消支付并返回?')) {
|
||||
window.history.back();
|
||||
} else {
|
||||
e.preventDefault();
|
||||
window.history.pushState('forward', null, '');
|
||||
}
|
||||
};
|
||||
window.history.pushState('forward', null, '');
|
||||
|
||||
document.getElementById("openUrl").href = url_scheme;
|
||||
if (navigator.userAgent.indexOf('EdgA/') == -1 && $(window).height() > $(window).width()) {
|
||||
window.location.href = url_scheme;
|
||||
}
|
||||
setTimeout("loadmsg()", 3000);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
54
doc/api/pages/return.php
Normal file
54
doc/api/pages/return.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
// 支付返回页面
|
||||
|
||||
if(!defined('IN_PLUGIN'))exit();
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
|
||||
<title>支付结果</title>
|
||||
<link href="/assets/pay/css/weui.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="container js_container">
|
||||
<div class="page msg">
|
||||
<div class="weui_msg">
|
||||
<div class="weui_icon_area"><i class="weui_icon_info weui_icon_msg"></i></div>
|
||||
<div class="weui_text_area">
|
||||
<h2 class="weui_msg_title">正在检测付款结果...</h2>
|
||||
<p class="weui_msg_desc">稍后页面将自动跳转</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="<?php echo $cdnpublic?>jquery/1.12.4/jquery.min.js"></script>
|
||||
<script src="<?php echo $cdnpublic?>layer/3.1.1/layer.js"></script>
|
||||
<script>
|
||||
document.body.addEventListener('touchmove', function (event) {
|
||||
event.preventDefault();
|
||||
},{ passive: false });
|
||||
function loadmsg() {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
url: "/getshop.php",
|
||||
data: {type: "wxpay", trade_no: "<?php echo $order['trade_no']?>"},
|
||||
success: function (data) {
|
||||
if (data.code == 1) {
|
||||
layer.msg('支付成功,正在跳转中...', {icon: 16,shade: 0.1,time: 15000});
|
||||
setTimeout(window.location.href=data.backurl, 1000);
|
||||
}else{
|
||||
setTimeout("loadmsg()", 2000);
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
setTimeout("loadmsg()", 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
window.onload = loadmsg();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
75
doc/api/pages/verify_invisible.php
Normal file
75
doc/api/pages/verify_invisible.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
if (!defined('IN_CRONLITE')) exit();
|
||||
|
||||
$html = '<form id="dopay" action="'.$siteurl.'submit.php" method="post">';
|
||||
foreach ($query_arr as $k=>$v) {
|
||||
$html.= '<input type="hidden" name="'.$k.'" value="'.$v.'"/>';
|
||||
}
|
||||
$html .= '<input type="submit" value="Loading"></form>';
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<title>正在进行支付安全验证,请稍候...</title>
|
||||
<style type="text/css">
|
||||
body{margin:0;padding:0}
|
||||
#waiting{position:absolute;left:50%;top:50%;height:35px;margin:-35px 0 0 -160px;padding:20px;font:16px/30px "Helvetica Neue",Helvetica,Arial,sans-serif;background:#f9fafc url(/assets/img/loading.gif) no-repeat 20px 20px;text-indent:40px;border:1px solid #c5d0dc}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p id="waiting">正在进行支付安全验证,请稍候...</p>
|
||||
<?php echo $html?>
|
||||
<script src="<?php echo $cdnpublic?>jquery/1.12.4/jquery.min.js"></script>
|
||||
<script src="https://static.geetest.com/v4/gt4.js"></script>
|
||||
<script>
|
||||
window.appendChildOrg = Element.prototype.appendChild;
|
||||
Element.prototype.appendChild = function() {
|
||||
if(arguments[0].tagName == 'SCRIPT'){
|
||||
arguments[0].setAttribute('referrerpolicy', 'no-referrer');
|
||||
}
|
||||
return window.appendChildOrg.apply(this, arguments);
|
||||
};
|
||||
initGeetest4({
|
||||
captchaId: "99b142aaece96330d0f3ffb565ffb3ef",
|
||||
product: 'bind',
|
||||
protocol: 'https://',
|
||||
riskType: 'ai',
|
||||
},function (captcha) {
|
||||
captcha.onReady(function(){
|
||||
captcha.showCaptcha();
|
||||
}).onSuccess(function(){
|
||||
var result = captcha.getValidate();
|
||||
result.pid = '<?php echo $query_arr['pid']?>';
|
||||
result.trade_no = '<?php echo $query_arr['out_trade_no']?>';
|
||||
$.ajax({
|
||||
url: 'getshop.php?act=captcha_verify',
|
||||
type: 'post',
|
||||
dataType: 'json',
|
||||
data: result,
|
||||
cache: false,
|
||||
success: function (data) {
|
||||
if(data.code == 0){
|
||||
var elem = document.getElementById("dopay");
|
||||
var input = document.createElement("input");
|
||||
input.type="hidden";
|
||||
input.name="__defend";
|
||||
input.value=data.key;
|
||||
elem.appendChild(input);
|
||||
elem.submit();
|
||||
}else{
|
||||
alert(data.msg);
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
alert('服务器错误');
|
||||
}
|
||||
});
|
||||
}).onError(function(){
|
||||
alert('验证码加载失败,请刷新页面重试');
|
||||
})
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
39
doc/api/pages/verify_jump.php
Normal file
39
doc/api/pages/verify_jump.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
if (!defined('IN_CRONLITE')) exit();
|
||||
$x = new \lib\hieroglyphy();
|
||||
$key_enc = $x->hieroglyphyString($key);
|
||||
|
||||
$html = '<form id="dopay" action="'.$siteurl.'submit.php" method="post">';
|
||||
foreach ($query_arr as $k=>$v) {
|
||||
$html.= '<input type="hidden" name="'.$k.'" value="'.$v.'"/>';
|
||||
}
|
||||
$html .= '<input type="submit" value="Loading"></form>';
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<title>正在进行支付安全验证,请稍候...</title>
|
||||
<style type="text/css">
|
||||
body{margin:0;padding:0}
|
||||
#waiting{position:absolute;left:50%;top:50%;height:35px;margin:-35px 0 0 -160px;padding:20px;font:16px/30px "Helvetica Neue",Helvetica,Arial,sans-serif;background:#f9fafc url(/assets/img/loading.gif) no-repeat 20px 20px;text-indent:40px;border:1px solid #c5d0dc}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p id="waiting">正在进行支付安全验证,请稍候...</p>
|
||||
<?php echo $html?>
|
||||
<script>
|
||||
var key = <?php echo $key_enc;?>;
|
||||
window.onload=function(){
|
||||
var elem = document.getElementById("dopay");
|
||||
var input=document.createElement("input");
|
||||
input.type="hidden";
|
||||
input.name="__defend";
|
||||
input.value=key;
|
||||
elem.appendChild(input);
|
||||
elem.submit();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
90
doc/api/pages/verify_slide.php
Normal file
90
doc/api/pages/verify_slide.php
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
if (!defined('IN_CRONLITE')) exit();
|
||||
|
||||
$html = '<form id="dopay" action="'.$siteurl.'submit.php" method="post">';
|
||||
foreach ($query_arr as $k=>$v) {
|
||||
$html.= '<input type="hidden" name="'.$k.'" value="'.$v.'"/>';
|
||||
}
|
||||
$html .= '<input type="submit" value="Loading" style="display:none"></form>';
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<title>支付环境安全验证</title>
|
||||
<style type="text/css">
|
||||
body{font-family:"微软雅黑";height:auto!important;height:555px;min-height:555px;margin:0}
|
||||
.container{margin:0 auto;margin-top:100px;background:#fff;text-align:center}
|
||||
.header>p{margin:0;margin-top:24px;font-size:18px;line-height:1.7;color:#5d5d5d}
|
||||
strong{color:#3190e6}
|
||||
@media screen and (max-width:767px){.container{margin-top:10px}
|
||||
.header>p{margin:0;padding:20px;font-size:20px;line-height:1.7;color:#5d5d5d}
|
||||
}
|
||||
@media screen and (max-width:320px){.container{margin-top:0}
|
||||
.header>p{margin:0;padding:20px;font-size:18px;line-height:1.7;color:#5d5d5d}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<p>
|
||||
很抱歉,当前支付人数过多,请完成<strong>“滑动验证”</strong>后继续支付
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<?php echo $html?>
|
||||
<script src="<?php echo $cdnpublic?>jquery/1.12.4/jquery.min.js"></script>
|
||||
<script src="https://static.geetest.com/v4/gt4.js"></script>
|
||||
<script>
|
||||
window.appendChildOrg = Element.prototype.appendChild;
|
||||
Element.prototype.appendChild = function() {
|
||||
if(arguments[0].tagName == 'SCRIPT'){
|
||||
arguments[0].setAttribute('referrerpolicy', 'no-referrer');
|
||||
}
|
||||
return window.appendChildOrg.apply(this, arguments);
|
||||
};
|
||||
initGeetest4({
|
||||
captchaId: "54088bb07d2df3c46b79f80300b0abbe",
|
||||
product: 'bind',
|
||||
protocol: 'https://',
|
||||
riskType: 'slide',
|
||||
hideSuccess: true
|
||||
},function (captcha) {
|
||||
captcha.onReady(function(){
|
||||
captcha.showCaptcha();
|
||||
}).onSuccess(function(){
|
||||
var result = captcha.getValidate();
|
||||
result.pid = '<?php echo $query_arr['pid']?>';
|
||||
result.trade_no = '<?php echo $query_arr['out_trade_no']?>';
|
||||
$.ajax({
|
||||
url: 'getshop.php?act=captcha_verify',
|
||||
type: 'post',
|
||||
dataType: 'json',
|
||||
data: result,
|
||||
cache: false,
|
||||
success: function (data) {
|
||||
if(data.code == 0){
|
||||
var elem = document.getElementById("dopay");
|
||||
var input = document.createElement("input");
|
||||
input.type="hidden";
|
||||
input.name="__defend";
|
||||
input.value=data.key;
|
||||
elem.appendChild(input);
|
||||
elem.submit();
|
||||
}else{
|
||||
alert(data.msg);
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
alert('服务器错误');
|
||||
}
|
||||
});
|
||||
}).onError(function(){
|
||||
alert('验证码加载失败,请刷新页面重试');
|
||||
})
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
72
doc/api/pages/wxopen.php
Normal file
72
doc/api/pages/wxopen.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
if(!defined('IN_PLUGIN'))exit();
|
||||
$useragent = strtolower($_SERVER['HTTP_USER_AGENT']);
|
||||
if(strpos($useragent, 'iphone')!==false || strpos($useragent, 'ipod')!==false){
|
||||
$background_img = '/assets/img/ios.png';
|
||||
}else{
|
||||
$background_img = '/assets/img/android.png';
|
||||
}
|
||||
?><!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>支付提示</title>
|
||||
<meta name="apple-mobile-web-app-capable" content="yes"/>
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black"/>
|
||||
<meta name="format-detection" content="telephone=no"/>
|
||||
<meta name="format-detection" content="email=no"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=0"/>
|
||||
<style>
|
||||
*,:after,:before{-webkit-tap-highlight-color:transparent}
|
||||
blockquote,body,dd,div,dl,dt,fieldset,form,h1,h2,h3,h4,h5,h6,input,legend,li,ol,p,td,textarea,th,ul{margin:0;padding:0}
|
||||
table{border-collapse:collapse;border-spacing:0}
|
||||
fieldset,img{border:0}
|
||||
li{list-style:none}
|
||||
caption,th{text-align:left}
|
||||
q:after,q:before{content:""}
|
||||
input:password{ime-mode:disabled}
|
||||
:focus{outline:0}
|
||||
body,html{-webkit-touch-callout:none;touch-callout:none;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;tap-highlight-color:transparent;height:100%;margin:0;padding:0;text-align:center;font-size:15px;font-weight:300;font-family:"Helvetica Neue",Helvetica,Arial,"Lucida Grande",sans-serif}
|
||||
a{text-decoration:none}
|
||||
body{background:#F4F4F8}
|
||||
.weixin-tip{-webkit-box-sizing:border-box;box-sizing:border-box;position:absolute;top:15px;right:20px;width:265px;padding:55px 0 0;text-align:left;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFoAAACICAMAAABQgAwUAAAAMFBMVEUAAADY2NjY2NjY2NjY2NjY2NjY2NjY2NjX19fY2NjY2NjY2NjY2NjY2NjY2NjY2Njr/TvvAAAAD3RSTlMAxy89c9CdTRyG7lvcD7FzqbJAAAACFklEQVR42uWYy4rkMBAErZdlPdzx/3+7LAw0tH0Y2orDsnnyKQlSVaWytoc6xrEpigFoinUAIBnWABAE5woW9o6GPbGwI1jYGSzsgoV9goU9wMLe0bA7FnYCC7uBhV2wsE+wsAdY2AENGyzsBBZ2Q8MuWNgH94pLbgELO6Bhg4VdwcJuaNgTCzuChZ3Bwg5o2GBhV7CwdzTsjoUdwcLOYGEXLOwTLOwBFvaOht2xsBNY2I1f6lhaenvhrfpkAblab+k9b/OD0iuX2F9/x8D+7ZL2pmpbuj+6o3Vg//oWmPU9p65VkXL6+oIJ8S738nwj62Pb1lvHACH+fBs7sG59U3yrVD3rce3GVcp8qGkPAGTprQUYy6xfaE8i82b6S7/pfZnzdYQIHeOXdfYKpHoFcmrvWlM8RW+CDO8JMWoNM/+FeyB4UfMpL48g5qG1Iqc29YI3mqq2knXvEJu2onJoQy9ok4mkQZf/GjqitUvQyqN6SU8NOvOhHq25xNCWj6LFQdLiyKuaZWpxBC2OrFVHxdryElbQsVtBx6KN0qAd4a71yo610uxa2b0s5xg052I5p26d4MCqusZFwzrAnqQhSogSMnkNcr+GUS3kEKWS62NJFlNCToWLZpWMe14RReGqdjz2PfNECbkGbrQ/Nj5q5y7j8/HRTW5UhvHfA7Mdzitji8rfWsgX3gVZ91eO22odKed6LLf9A/sRnc74RV7lAAAAAElFTkSuQmCC) no-repeat right top;background-size:45px 68px}
|
||||
.weixin-tip-img{padding:110px 0 0}
|
||||
.weixin-tip-img::after{display:block;margin:15px auto;content:' ';background-size:cover;width:150px;height:150px;background-image:url('<?php echo $background_img?>')}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="J-weixin-tip weixin-tip">
|
||||
<div class="weixin-tip-content">
|
||||
请在菜单中选择在浏览器中打开,<br/>
|
||||
以完成支付
|
||||
</div>
|
||||
</div>
|
||||
<div class="J-weixin-tip-img weixin-tip-img"></div>
|
||||
<script src="<?php echo $cdnpublic?>jquery/1.12.4/jquery.min.js"></script>
|
||||
<script src="<?php echo $cdnpublic?>layer/3.1.1/layer.js"></script>
|
||||
<script>
|
||||
function loadmsg() {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
url: "/getshop.php",
|
||||
data: {type: "alipay", trade_no: "<?php echo $order['trade_no']?>"},
|
||||
success: function (data) {
|
||||
if (data.code == 1) {
|
||||
layer.msg('支付成功,正在跳转中...', {icon: 16,shade: 0.1,time: 15000});
|
||||
setTimeout(window.location.href=data.backurl, 1000);
|
||||
}else{
|
||||
setTimeout("loadmsg()", 2000);
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
setTimeout("loadmsg()", 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
window.onload = function(){
|
||||
setTimeout("loadmsg()", 5000);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
109
doc/api/pages/wxpay_h5.php
Normal file
109
doc/api/pages/wxpay_h5.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
// 微信H5支付页面
|
||||
|
||||
if (!defined('IN_PLUGIN'))
|
||||
exit();
|
||||
?>
|
||||
<html lang="zh-cn">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
|
||||
<meta name="renderer" content="webkit" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<title>微信支付手机版</title>
|
||||
<link href="<?php echo $cdnpublic ?>twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet" />
|
||||
<link href="/assets/pay/css/mobile-style.css" rel="stylesheet" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="main">
|
||||
<div class="bg-weixin"></div>
|
||||
<div class="payment-logo">
|
||||
<img src="/assets/pay/icon/wxpay-white.svg" alt="logo">
|
||||
<span class="logo-tile">微信支付</span>
|
||||
</div>
|
||||
<div class="payment-content">
|
||||
<div class="content-info">
|
||||
<h1>¥<?php echo $order['realmoney'] ?></h1>
|
||||
<ul class="nk-activity">
|
||||
<li class="nk-activity-item">
|
||||
<span>商品名称:<?php echo $order['name'] ?></span>
|
||||
</li>
|
||||
<li class="nk-activity-item">
|
||||
<span>商户订单号:<?php echo $order['trade_no'] ?></span>
|
||||
</li>
|
||||
<li class="nk-activity-item">
|
||||
<span>创建时间:<?php echo $order['addtime'] ?></span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="content-footer">
|
||||
<a href="javascript:;" id="openUrl" class="btn btn-success btn-block btn-lg">跳转到微信支付</a>
|
||||
<a href="javascript:checkresult()" onclick="" class="btn btn-info btn-block btn-lg">检测支付状态</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="<?php echo $cdnpublic ?>jquery/1.12.4/jquery.min.js"></script>
|
||||
<script src="<?php echo $cdnpublic ?>layer/3.1.1/layer.js"></script>
|
||||
<script>
|
||||
var url_scheme = '<?php echo $code_url ?>';
|
||||
function loadmsg() {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
url: "/getshop.php",
|
||||
data: { type: "wxpay", trade_no: "<?php echo $order['trade_no'] ?>" },
|
||||
success: function (data) {
|
||||
if (data.code == 1) {
|
||||
layer.msg('支付成功,正在跳转中...', { icon: 16, shade: 0.01, time: 15000 });
|
||||
setTimeout(window.location.href = data.backurl, 1000);
|
||||
} else {
|
||||
setTimeout("loadmsg()", 2000);
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
setTimeout("loadmsg()", 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
function checkresult() {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
url: "/getshop.php",
|
||||
data: { type: "wxpay", trade_no: "<?php echo $order['trade_no'] ?>" },
|
||||
success: function (data) {
|
||||
if (data.code == 1) {
|
||||
layer.msg('支付成功,正在跳转中...', { icon: 16, shade: 0.01, time: 15000 });
|
||||
setTimeout(window.location.href = data.backurl, 1000);
|
||||
} else {
|
||||
layer.msg('您还未完成付款,请继续付款', { shade: 0, time: 1500 });
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
layer.msg('服务器错误');
|
||||
}
|
||||
});
|
||||
}
|
||||
window.onload = function () {
|
||||
window.onpopstate = function (e) {
|
||||
if (e.state == 'forward' || confirm('是否取消支付并返回?')) {
|
||||
window.history.back();
|
||||
} else {
|
||||
e.preventDefault();
|
||||
window.history.pushState('forward', null, '');
|
||||
}
|
||||
};
|
||||
window.history.pushState('forward', null, '');
|
||||
|
||||
document.getElementById("openUrl").href = url_scheme;
|
||||
if (!url_scheme.startsWith('http://') && !url_scheme.startsWith('https://') && navigator.userAgent.indexOf('EdgA/') == -1) {
|
||||
window.location.href = url_scheme;
|
||||
}
|
||||
setTimeout("loadmsg()", 3000);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
102
doc/api/pages/wxpay_jspay.php
Normal file
102
doc/api/pages/wxpay_jspay.php
Normal file
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
// 微信公众号支付页面
|
||||
|
||||
if(!defined('IN_PLUGIN'))exit();
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
|
||||
<title>微信支付手机版</title>
|
||||
<style>
|
||||
body{
|
||||
margin: 0px !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body style="background-color:#f6f6f6">
|
||||
<div style="display: flex;justify-content: center; padding-top: 20px;border-radius: 15px;height: 100px;text-align: center;align-items: center;">
|
||||
<span style="font-size: 15px;font-weight:800;color:#020202;"><?php echo $order['name']?><br>
|
||||
<div style="display: flex;justify-content: center;">
|
||||
<strong style="font-size: 22px;color: #000000;padding-top: 6px;margin-right: 3px;">¥</strong>
|
||||
<strong style="font-size: 40px;color: #000000;"><?php echo $order['realmoney']?></strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="background: #fff;padding: 16px;border-top: 1px solid #d8d8d8;border-bottom: 1px solid #d8d8d8;">
|
||||
<div style="display: flex;">
|
||||
<span style="font-weight: 400;color: #a1a1a1;width: 40px;">商家</span>
|
||||
<span style="flex:1;text-align: right;color: black;font-weight: 600;font-size: 14px;">微信支付平台商户</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 1px;border-radius: 1px;">
|
||||
<div style="display: flex; justify-content: center; padding-top: 20px;">
|
||||
<a class="immediate_pay" style="width:100%;max-width:600px;border-radius: 10px;margin: 0 4px;background: #05c160;padding: 12px 0px;text-align:center;color: #fff;" onclick="callpay()"><font size="4">立即支付</font></a>
|
||||
</div>
|
||||
|
||||
<div style="position: fixed;width: 100%;text-align: center;color: #a1a1a1;bottom: 17px;font-size: 12px;">
|
||||
支付安全由中国人民财产保险股份有限公司承保
|
||||
</div>
|
||||
<script src="<?php echo $cdnpublic?>jquery/1.12.4/jquery.min.js"></script>
|
||||
<script src="<?php echo $cdnpublic?>layer/3.1.1/layer.js"></script>
|
||||
<script>
|
||||
document.body.addEventListener('touchmove', function (event) {
|
||||
event.preventDefault();
|
||||
},{ passive: false });
|
||||
//调用微信JS api 支付
|
||||
function jsApiCall()
|
||||
{
|
||||
WeixinJSBridge.invoke(
|
||||
'getBrandWCPayRequest',
|
||||
<?php echo $jsApiParameters; ?>,
|
||||
function(res){
|
||||
if(res.err_msg == "get_brand_wcpay_request:ok" ) {
|
||||
loadmsg();
|
||||
}
|
||||
//WeixinJSBridge.log(res.err_msg);
|
||||
//alert(res.err_code+res.err_desc+res.err_msg);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function callpay()
|
||||
{
|
||||
if (typeof WeixinJSBridge == "undefined"){
|
||||
if( document.addEventListener ){
|
||||
document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
|
||||
}else if (document.attachEvent){
|
||||
document.attachEvent('WeixinJSBridgeReady', jsApiCall);
|
||||
document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
|
||||
}
|
||||
}else{
|
||||
jsApiCall();
|
||||
}
|
||||
}
|
||||
function loadmsg() {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
url: "/getshop.php",
|
||||
data: {type: "wxpay", trade_no: "<?php echo TRADE_NO?>"},
|
||||
success: function (data) {
|
||||
if (data.code == 1) {
|
||||
layer.msg('支付成功,正在跳转中...', {icon: 16,shade: 0.01,time: 15000});
|
||||
window.location.href=<?php echo $redirect_url?>;
|
||||
}else{
|
||||
setTimeout("loadmsg()", 2000);
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
setTimeout("loadmsg()", 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
window.onload = callpay();
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
137
doc/api/pages/wxpay_qrcode.php
Normal file
137
doc/api/pages/wxpay_qrcode.php
Normal file
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
// 微信扫码支付页面
|
||||
|
||||
if(!defined('IN_PLUGIN'))exit();
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta http-equiv="Content-Language" content="zh-cn">
|
||||
<meta name="renderer" content="webkit">
|
||||
<title>微信扫码支付</title>
|
||||
<link href="/assets/css/wechat_pay.css?v=2" rel="stylesheet" media="screen">
|
||||
</head>
|
||||
<body>
|
||||
<div class="body">
|
||||
<h1 class="mod-title">
|
||||
<span class="ico-wechat"></span><span class="text">微信扫码支付</span>
|
||||
</h1>
|
||||
<div class="mod-ct">
|
||||
<div class="order">
|
||||
</div>
|
||||
<div class="mobile-tip" style="display: none;">提示:二维码会风控,请复制下方链接支付</div>
|
||||
<div class="amount">¥<?php echo $order['realmoney']?></div>
|
||||
<div class="qr-image" id="qrcode">
|
||||
</div>
|
||||
<div class="mobile-btn" style="display: none;">
|
||||
<div class="mobile-tip">操作流程:复制链接→打开微信搜索自己微信名→打开聊天对话框→粘贴链接→发送→点击发送出来的蓝色链接→进入付款页面→完成付款</div>
|
||||
<a class="btn-copy-link" id="copy-btn" data-clipboard-text="<?php echo $code_url?>">点我复制链接</a>
|
||||
</div>
|
||||
<div class="detail" id="orderDetail">
|
||||
<dl class="detail-ct" style="display: none;">
|
||||
<dt>商家</dt>
|
||||
<dd id="storeName"><?php echo $sitename?></dd>
|
||||
<dt>购买物品</dt>
|
||||
<dd id="productName"><?php echo $order['name']?></dd>
|
||||
<dt>商户订单号</dt>
|
||||
<dd id="billId"><?php echo $order['trade_no']?></dd>
|
||||
<dt>创建时间</dt>
|
||||
<dd id="createTime"><?php echo $order['addtime']?></dd>
|
||||
</dl>
|
||||
<a href="javascript:void(0)" class="arrow"><i class="ico-arrow"></i></a>
|
||||
</div>
|
||||
<div class="tip">
|
||||
<span class="dec dec-left"></span>
|
||||
<span class="dec dec-right"></span>
|
||||
<div class="ico-scan"></div>
|
||||
<div class="tip-text">
|
||||
<p>请使用微信扫一扫</p>
|
||||
<p>扫描二维码完成支付</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tip-text">
|
||||
</div>
|
||||
</div>
|
||||
<div class="foot">
|
||||
<div class="inner">
|
||||
<p>手机用户可保存上方二维码到手机中</p>
|
||||
<p>在微信扫一扫中选择“相册”即可</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="<?php echo $cdnpublic?>jquery/1.12.4/jquery.min.js"></script>
|
||||
<script src="<?php echo $cdnpublic?>layer/3.1.1/layer.js"></script>
|
||||
<script src="<?php echo $cdnpublic?>jquery.qrcode/1.0/jquery.qrcode.min.js"></script>
|
||||
<script src="<?php echo $cdnpublic?>clipboard.js/1.7.1/clipboard.min.js"></script>
|
||||
<script>
|
||||
var clipboard = new Clipboard('#copy-btn');
|
||||
clipboard.on('success', function(e) {
|
||||
layer.msg('复制成功,请到微信里面粘贴');
|
||||
});
|
||||
clipboard.on('error', function(e) {
|
||||
layer.msg('复制失败');
|
||||
});
|
||||
var code_url = '<?php echo $code_url?>';
|
||||
var code_type = code_url.indexOf('data:image/')>-1?1:0;
|
||||
if(code_type == 0){
|
||||
$('#qrcode').qrcode({
|
||||
text: code_url,
|
||||
width: 230,
|
||||
height: 230,
|
||||
foreground: "#000000",
|
||||
background: "#ffffff",
|
||||
typeNumber: -1
|
||||
});
|
||||
}else{
|
||||
$('#qrcode').html('<img src="'+code_url+'"/>');
|
||||
}
|
||||
// 订单详情
|
||||
$('#orderDetail .arrow').click(function (event) {
|
||||
if ($('#orderDetail').hasClass('detail-open')) {
|
||||
$('#orderDetail .detail-ct').slideUp(500, function () {
|
||||
$('#orderDetail').removeClass('detail-open');
|
||||
});
|
||||
} else {
|
||||
$('#orderDetail .detail-ct').slideDown(500, function () {
|
||||
$('#orderDetail').addClass('detail-open');
|
||||
});
|
||||
}
|
||||
});
|
||||
function loadmsg() {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
url: "/getshop.php",
|
||||
data: {type: "wxpay", trade_no: "<?php echo $order['trade_no']?>"},
|
||||
success: function (data) {
|
||||
if (data.code == 1) {
|
||||
layer.msg('支付成功,正在跳转中...', {icon: 16,shade: 0.1,time: 15000});
|
||||
window.location.href=data.backurl;
|
||||
}else{
|
||||
setTimeout("loadmsg()", 2000);
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
setTimeout("loadmsg()", 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
var isMobile = function (){
|
||||
var ua = navigator.userAgent;
|
||||
var ipad = ua.match(/(iPad).*OS\s([\d_]+)/),
|
||||
isIphone =!ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/),
|
||||
isAndroid = ua.match(/(Android)\s+([\d.]+)/);
|
||||
return isIphone || isAndroid;
|
||||
}
|
||||
window.onload = function(){
|
||||
if(isMobile()){
|
||||
$('.mobile-btn').show();
|
||||
$('.mobile-tip').show();
|
||||
}
|
||||
setTimeout("loadmsg()", 2000);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
145
doc/api/pages/wxpay_wap.php
Normal file
145
doc/api/pages/wxpay_wap.php
Normal file
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
// 微信手机扫码支付页面
|
||||
|
||||
if (!defined('IN_PLUGIN'))
|
||||
exit();
|
||||
?>
|
||||
<html lang="zh-cn">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
|
||||
<meta name="renderer" content="webkit" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<title>微信支付手机版</title>
|
||||
<link href="<?php echo $cdnpublic ?>twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet" />
|
||||
<link href="/assets/pay/css/mobile-style.css?v=7" rel="stylesheet" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="main">
|
||||
<div class="bg-weixin"></div>
|
||||
<div class="payment-logo payment-logo-wxwap">
|
||||
<img src="/assets/pay/icon/wxpay-white.svg" alt="logo">
|
||||
<span class="logo-tile">微信支付</span>
|
||||
</div>
|
||||
<div class="payment-content">
|
||||
<h1 style="margin-top: 8px;margin-bottom: 16px;">¥<?php echo $order['realmoney']; ?></h1>
|
||||
<div class="scan-the-code">
|
||||
<ul class="nav nav-group" role="tablist">
|
||||
<li class="active">
|
||||
<a href="JavaScript:;"><i class="icon-qrcode"></i>扫码支付</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="JavaScript:downloadCanvas();"><i class="icon-download"></i>保存二维码</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="list-group">
|
||||
<div class="list-group-item text-center">
|
||||
<h5 class="qr-title">请使用微信APP扫描二维码支付</h5>
|
||||
<div class="qr-image" id="qrcode"></div>
|
||||
<div class="operate">
|
||||
<a href="weixin://" class="btn btn-default">
|
||||
<span class="glyphicon glyphicon-new-window" aria-hidden="true"></span>
|
||||
<span>打开微信APP</span>
|
||||
</a>
|
||||
<a href="javascript:checkresult()" class="btn btn-default">
|
||||
<span class="glyphicon glyphicon-repeat" aria-hidden="true"></span>
|
||||
<span>检测支付状态</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-group-item">
|
||||
<span>二维码链接:<a href="<?php echo $code_url ?>"><?php echo $code_url ?></a></span>
|
||||
<span><button id="copy-btn" data-clipboard-text="<?php echo $code_url ?>" class="btn btn-info btn-sm">复制</button></span>
|
||||
</div>
|
||||
<div class="list-group-item">
|
||||
<small>提示:你可将以上二维码链接发到自己微信的聊天框(在微信顶部搜索框可以搜到自己的微信),点击即可进入支付!</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="<?php echo $cdnpublic ?>jquery/1.12.4/jquery.min.js"></script>
|
||||
<script src="<?php echo $cdnpublic ?>layer/3.1.1/layer.js"></script>
|
||||
<script src="<?php echo $cdnpublic ?>jquery.qrcode/1.0/jquery.qrcode.min.js"></script>
|
||||
<script src="<?php echo $cdnpublic ?>clipboard.js/1.7.1/clipboard.min.js"></script>
|
||||
<script>
|
||||
var clipboard = new Clipboard('#copy-btn');
|
||||
clipboard.on('success', function (e) {
|
||||
layer.msg('复制成功,请到微信里面粘贴');
|
||||
});
|
||||
clipboard.on('error', function (e) {
|
||||
layer.msg('复制失败,请长按链接后手动复制');
|
||||
});
|
||||
$('#qrcode').qrcode({
|
||||
text: "<?php echo $code_url ?>",
|
||||
width: 230,
|
||||
height: 230,
|
||||
foreground: "#000000",
|
||||
background: "#ffffff",
|
||||
typeNumber: -1
|
||||
});
|
||||
function downloadCanvas() {
|
||||
var canvas = document.getElementsByTagName('canvas')[0];
|
||||
var url = canvas.toDataURL('image/png');
|
||||
var a = document.createElement('a');
|
||||
var event = new MouseEvent('click');
|
||||
a.download = '微信支付二维码.png';
|
||||
a.href = url;
|
||||
a.dispatchEvent(event);
|
||||
};
|
||||
function loadmsg() {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
url: "/getshop.php",
|
||||
data: {type: "wxpay", trade_no: "<?php echo $order['trade_no'] ?>"},
|
||||
success: function (data) {
|
||||
if (data.code == 1) {
|
||||
layer.msg('支付成功,正在跳转中...', {icon: 16,shade: 0.1,time: 15000});
|
||||
setTimeout(window.location.href=data.backurl, 1000);
|
||||
}else{
|
||||
setTimeout("loadmsg()", 2000);
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
setTimeout("loadmsg()", 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
function checkresult() {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
url: "/getshop.php",
|
||||
data: { type: "wxpay", trade_no: "<?php echo $order['trade_no'] ?>" },
|
||||
success: function (data) {
|
||||
if (data.code == 1) {
|
||||
layer.msg('支付成功,正在跳转中...', { icon: 16, shade: 0.1, time: 15000 });
|
||||
setTimeout(window.location.href = data.backurl, 1000);
|
||||
} else {
|
||||
layer.msg('您还未完成付款,请继续付款', { shade: 0, time: 1500 });
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
layer.msg('服务器错误');
|
||||
}
|
||||
});
|
||||
}
|
||||
window.onload = function () {
|
||||
window.onpopstate = function (e) {
|
||||
if (e.state == 'forward' || confirm('是否取消支付并返回?')) {
|
||||
window.history.back();
|
||||
} else {
|
||||
e.preventDefault();
|
||||
window.history.pushState('forward', null, '');
|
||||
}
|
||||
};
|
||||
window.history.pushState('forward', null, '');
|
||||
setTimeout("loadmsg()", 3000);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
64
doc/architecture.md
Normal file
64
doc/architecture.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# 架构与请求流
|
||||
|
||||
## 工作区结构
|
||||
|
||||
```text
|
||||
MPAY_V2/
|
||||
admin/ 管理后台前端
|
||||
mer/ 商户后台前端
|
||||
cashier/ 收银台前端
|
||||
mpay/ Webman 后端
|
||||
docs/ 当前文档中心
|
||||
```
|
||||
|
||||
## 请求入口
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Admin["admin 前端"] --> AdminApi["/adminapi"]
|
||||
Mer["mer 前端"] --> MerApi["/merapi"]
|
||||
Cashier["cashier 前端"] --> CashierApi["/api/cashier"]
|
||||
Merchant["商户系统 / SDK"] --> EpayV1["/submit.php / /mapi.php / /api.php"]
|
||||
Merchant --> EpayV2["/api/pay / /api/merchant / /api/transfer"]
|
||||
|
||||
AdminApi --> AdminRoute[app/route/admin.php]
|
||||
MerApi --> MerRoute[app/route/mer.php]
|
||||
CashierApi --> ApiRoute[app/route/api.php]
|
||||
EpayV1 --> ApiRoute
|
||||
EpayV2 --> ApiRoute
|
||||
|
||||
AdminRoute --> Http[app/http]
|
||||
MerRoute --> Http
|
||||
ApiRoute --> Http
|
||||
Http --> Service[app/service]
|
||||
Service --> Repo[app/repository]
|
||||
Repo --> Model[app/model]
|
||||
Model --> DB[(MySQL)]
|
||||
Service --> Redis[(Redis)]
|
||||
Service --> Plugin["app/common/payment 插件"]
|
||||
Plugin --> Channel["第三方支付通道"]
|
||||
```
|
||||
|
||||
## 后端分层
|
||||
|
||||
| 层 | 目录 | 职责 |
|
||||
| --- | --- | --- |
|
||||
| 路由 | `app/route`、`config/route.php` | 绑定 URL、页面入口和中间件 |
|
||||
| HTTP | `app/http` | 控制器、鉴权中间件、参数校验 |
|
||||
| 服务 | `app/service` | 业务规则、状态流转、插件调用、通知、清算 |
|
||||
| 仓库 | `app/repository` | 数据库读写封装 |
|
||||
| 模型 | `app/model` | 表映射、类型转换、时间序列化 |
|
||||
| 公共能力 | `app/common` | 基类、常量、工具、中间件、支付插件 |
|
||||
|
||||
## 关键进程
|
||||
|
||||
- `webman`:HTTP 服务,监听 `0.0.0.0:8787`。
|
||||
- `payment-runtime`:支付运行时维护进程,负责商户通知重试、支付单超时扫描和支付中订单主动查单。
|
||||
- `monitor`:开发环境文件监控和自动重载。
|
||||
|
||||
## 关键约束
|
||||
|
||||
- `config/route.php` 显式加载 `admin.php`、`mer.php`、`api.php`,并关闭默认路由。
|
||||
- 管理后台和商户后台分别使用 `AdminAuthMiddleware`、`MerchantAuthMiddleware`。
|
||||
- CORS 由 `app/common/middleware/Cors` 处理。
|
||||
- 前端请求前缀在各自 `src/api/index.ts` 中集中拼接,不在页面里散写。
|
||||
72
doc/backend/README.md
Normal file
72
doc/backend/README.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# 后端总说明
|
||||
|
||||
`mpay` 是支付中台后端服务,基于 Webman。命令默认在 `mpay/` 目录执行。
|
||||
|
||||
## 技术栈
|
||||
|
||||
- PHP `>=8.1`
|
||||
- `workerman/webman-framework ^2.1`
|
||||
- MySQL、Redis
|
||||
- JWT、Webman validation/cache/event/redis/database
|
||||
- OSS/COS SDK 用于对象存储
|
||||
|
||||
## 快速启动
|
||||
|
||||
```bash
|
||||
composer install
|
||||
Copy-Item .env.example .env
|
||||
php webman start
|
||||
```
|
||||
|
||||
Windows 开发环境如需启动自定义进程,可使用:
|
||||
|
||||
```bash
|
||||
php windows.php
|
||||
```
|
||||
|
||||
## 主要目录
|
||||
|
||||
```text
|
||||
app/
|
||||
command/ 命令与烟雾测试
|
||||
common/ 基类、常量、工具、中间件、支付插件
|
||||
http/ admin、mer、api 三类 HTTP 入口
|
||||
model/ 数据模型
|
||||
repository/ 数据访问
|
||||
route/ 显式路由
|
||||
service/ 业务服务
|
||||
config/ Webman 与业务配置
|
||||
public/ 静态资源与前端构建产物
|
||||
support/ Webman 支撑代码
|
||||
```
|
||||
|
||||
## 关键入口
|
||||
|
||||
- 路由:`config/route.php`、`app/route/admin.php`、`app/route/mer.php`、`app/route/api.php`
|
||||
- 支付:`app/service/payment/order/PayOrderService.php`
|
||||
- 退款:`app/service/payment/order/RefundService.php`
|
||||
- 清算:`app/service/payment/settlement/SettlementService.php`
|
||||
- 路由:`app/service/payment/runtime/PaymentRouteService.php`
|
||||
- 插件:`app/service/payment/runtime/PaymentPluginManager.php`
|
||||
- 商户:`app/service/merchant/MerchantService.php`
|
||||
- 商户后台:`app/service/merchant/portal/MerchantPortalService.php`
|
||||
- 文件:`app/service/file/FileRecordService.php`
|
||||
|
||||
## 常用命令
|
||||
|
||||
```bash
|
||||
php webman start
|
||||
php webman restart
|
||||
php webman mpay:test --all
|
||||
php webman epay:mapi
|
||||
php webman system:config-sync
|
||||
```
|
||||
|
||||
## 关联文档
|
||||
|
||||
- [后端路由](./routing.md)
|
||||
- [后端服务层](./services.md)
|
||||
- [后端命令](./commands.md)
|
||||
- [文件资产](./files.md)
|
||||
- [接口总说明](../api/README.md)
|
||||
- [部署说明](../deployment/backend.md)
|
||||
41
doc/backend/commands.md
Normal file
41
doc/backend/commands.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# 后端命令
|
||||
|
||||
命令默认在 `mpay/` 目录执行。
|
||||
|
||||
## Webman 启动
|
||||
|
||||
| 命令 | 作用 |
|
||||
| --- | --- |
|
||||
| `php webman start` | 启动后端 HTTP 服务 |
|
||||
| `php webman restart` | 重启后端服务 |
|
||||
| `php windows.php` | Windows 开发环境启动 Webman 与自定义进程 |
|
||||
| `php start.php start` | 生产/Linux 常见启动入口 |
|
||||
|
||||
`payment-runtime` 是自定义进程,不是 Console 命令;它随 Webman 进程启动,负责通知重试、支付超时扫描和支付中订单主动查单。
|
||||
|
||||
## 业务命令
|
||||
|
||||
| 命令 | 作用 |
|
||||
| --- | --- |
|
||||
| `php webman mpay:test --all` | 支付、退款、清结算、余额、追踪链路烟雾测试 |
|
||||
| `php webman epay:mapi` | ePay V1 `mapi.php` 兼容接口烟雾测试 |
|
||||
| `php webman epay:v2-api` | ePay V2 创建、查询、关闭、退款、商户信息等核心 API 烟雾测试 |
|
||||
| `php webman epay:mock-chain` | 自动写入 mock 配置并跑 ePay V1/V2 全链路 |
|
||||
| `php webman epay:v2-bootstrap` | 生成开发联调用平台和商户 RSA 密钥 |
|
||||
| `php webman payment:notify-retry` | 手动重试到期商户通知任务 |
|
||||
| `php webman system:config-sync` | 将 `config/system_config.php` 默认配置同步到数据库 |
|
||||
| `php webman test` | 基础命令注册检查 |
|
||||
|
||||
## 常用参数
|
||||
|
||||
- `mpay:test`:`--payment`、`--refund`、`--settlement`、`--balance`、`--trace`、`--all`、`--live`
|
||||
- `epay:mapi`:`--live`、`--merchant-id`、`--merchant-no`、`--type`、`--money`、`--refund-trade-no`
|
||||
- `epay:v2-api`:`--live`、`--merchant-id`、`--merchant-no`、`--merchant-private-key-file`、`--type`、`--method`、`--money`
|
||||
- `epay:mock-chain`:`--only=v1|v2|all`
|
||||
- `payment:notify-retry`:`--limit`
|
||||
|
||||
## 关联文档
|
||||
|
||||
- [后端总说明](./README.md)
|
||||
- [支付运行时数据契约](./payment-runtime-contract.md)
|
||||
- [ePay 兼容协议](../api/legacy/epay.md)
|
||||
39
doc/backend/compat.md
Normal file
39
doc/backend/compat.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# ePay 兼容层
|
||||
|
||||
后端同时保留 ePay V1 旧入口和 ePay V2 新接口。兼容层只负责协议适配,不作为后台管理或商户后台的新能力入口。
|
||||
|
||||
## 当前实现
|
||||
|
||||
| 协议 | 控制器 | 服务 |
|
||||
| --- | --- | --- |
|
||||
| ePay V1 | `app/http/api/controller/epay/EpayV1Controller.php` | `app/service/payment/epay/EpayV1ProtocolService.php` |
|
||||
| ePay V2 | `app/http/api/controller/epay/EpayV2Controller.php` | `app/service/payment/epay/EpayV2ProtocolService.php` |
|
||||
|
||||
签名实现:
|
||||
|
||||
- `Md5Signer`
|
||||
- `RsaSigner`
|
||||
- `EpaySignerManager`
|
||||
|
||||
## V1 路由
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `ANY` | `/submit.php` | 页面跳转支付 |
|
||||
| `POST` | `/mapi.php` | 接口支付 |
|
||||
| `ANY` | `/api.php` | 旧版标准 API |
|
||||
|
||||
## V2 路由
|
||||
|
||||
| 分组 | 路径 |
|
||||
| --- | --- |
|
||||
| 支付 | `/api/pay/submit`、`/api/pay/create`、`/api/pay/query`、`/api/pay/refund`、`/api/pay/refundquery`、`/api/pay/close`、`/api/pay/{payNo}/callback` |
|
||||
| 商户 | `/api/merchant/info`、`/api/merchant/orders` |
|
||||
| 转账 | `/api/transfer/submit`、`/api/transfer/query`、`/api/transfer/balance` |
|
||||
|
||||
## 关联文档
|
||||
|
||||
- [接口总说明](../api/README.md)
|
||||
- [收银台与开放接口](../api/cashier.md)
|
||||
- [ePay 兼容协议](../api/legacy/epay.md)
|
||||
- [后端路由](./routing.md)
|
||||
46
doc/backend/files.md
Normal file
46
doc/backend/files.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# 文件资产
|
||||
|
||||
文件资产由后端记录元数据,实际文件交给本地、远程 URL、OSS 或 COS 驱动处理。
|
||||
|
||||
## 当前入口
|
||||
|
||||
- API 前缀:`/adminapi/file-asset`
|
||||
- 控制器:`FileRecordController`
|
||||
- 模型:`FileRecord`
|
||||
- 数据表:`ma_file_asset`
|
||||
|
||||
## 接口
|
||||
|
||||
| 方法 | 路径 | 作用 |
|
||||
| --- | --- | --- |
|
||||
| `GET` | `/file-asset/options` | 文件选项 |
|
||||
| `GET` | `/file-asset` | 文件列表 |
|
||||
| `POST` | `/file-asset/upload` | 上传文件 |
|
||||
| `POST` | `/file-asset/import-remote` | 导入远程文件 |
|
||||
| `GET` | `/file-asset/{id}/preview` | 文件预览 |
|
||||
| `GET` | `/file-asset/{id}/download` | 文件下载 |
|
||||
| `GET` | `/file-asset/{id}` | 文件详情 |
|
||||
| `DELETE` | `/file-asset/{id}` | 删除文件 |
|
||||
|
||||
## 服务与驱动
|
||||
|
||||
- `FileRecordService`
|
||||
- `FileRecordQueryService`
|
||||
- `FileRecordCommandService`
|
||||
- `StorageConfigService`
|
||||
- `StorageManager`
|
||||
- `LocalStorageDriver`
|
||||
- `RemoteUrlStorageDriver`
|
||||
- `OssStorageDriver`
|
||||
- `CosStorageDriver`
|
||||
|
||||
## 字段口径
|
||||
|
||||
- `object_key`:站点相对路径或对象存储 key。
|
||||
- `url`:公开文件的完整访问地址,私有文件可以为空。
|
||||
- `preview_url`:后台预览使用,不作为长期业务访问地址。
|
||||
- `previewable`:列表返回给前端判断是否允许在线预览。
|
||||
|
||||
## 表单上传
|
||||
|
||||
系统配置和插件配置中的上传字段仍使用 `type: "upload"`。需要走项目定制选择器时,在 `props.fileUpload` 中声明 `selectorType`、`scene`、`isLocal`、`isPublic`、`getKey` 等配置。前端说明见 [管理后台前端](../frontend/admin.md)。
|
||||
261
doc/backend/payment-plugin-template.md
Normal file
261
doc/backend/payment-plugin-template.md
Normal file
@@ -0,0 +1,261 @@
|
||||
# 支付插件模板说明
|
||||
|
||||
这份说明对应 `mpay/app/common/payment/TemplatePayment.php`。
|
||||
|
||||
它的作用不是提供一个可直接上线的插件,而是提供一份可以复制、改名、接第三方网关的起点模板。
|
||||
|
||||
## 1. 这个模板解决什么问题
|
||||
|
||||
支付插件开发最容易踩坑的地方,不是第三方接口调用本身,而是这些基础约定容易不统一:
|
||||
|
||||
- 插件元信息怎么写
|
||||
- 配置表单怎么定义
|
||||
- `class_name` 怎么配置
|
||||
- `pay()` 返回什么结构
|
||||
- 回调怎么收口
|
||||
- 哪些字段该放在插件配置里,哪些字段该放在订单入参里
|
||||
|
||||
`TemplatePayment` 把这些骨架先搭好,后面新增插件时可以直接复制,再替换为真实渠道逻辑。
|
||||
|
||||
## 2. 模板在系统里怎么被加载
|
||||
|
||||
插件并不是直接被业务代码 `new` 出来的,而是先经过插件注册表,再由工厂服务实例化。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[后台维护插件定义] --> B[ma_payment_plugin]
|
||||
B --> C[ma_payment_plugin_conf]
|
||||
C --> D[ma_payment_channel]
|
||||
D --> E[PaymentPluginFactoryService]
|
||||
E --> F[实例化插件类]
|
||||
F --> G[init(配置)]
|
||||
G --> H[pay / query / close / refund / notify]
|
||||
```
|
||||
|
||||
关键点有两个:
|
||||
|
||||
- `ma_payment_plugin.class_name` 可以填短类名,也可以填完整类名
|
||||
- 如果是短类名,工厂会自动补成 `app\\common\\payment\\{class_name}`
|
||||
|
||||
也就是说,`TemplatePayment` 这种类既可以直接写成 `TemplatePayment`,也可以写成完整命名空间。
|
||||
|
||||
## 3. 复制时要改哪些地方
|
||||
|
||||
复制这个模板后,优先改下面几块:
|
||||
|
||||
1. `paymentInfo.code`
|
||||
2. `paymentInfo.name`
|
||||
3. `paymentInfo.pay_types`
|
||||
4. `paymentInfo.transfer_types`
|
||||
5. `paymentInfo.config_schema`
|
||||
6. `init()`
|
||||
7. `pay()`
|
||||
8. `query()`
|
||||
9. `close()`
|
||||
10. `refund()`
|
||||
11. `notify()`
|
||||
|
||||
其中最重要的是:
|
||||
|
||||
- `paymentInfo` 决定后台怎么展示这个插件
|
||||
- `config_schema` 决定后台怎么维护插件配置
|
||||
- `init()` 决定插件怎么吃到 `ma_payment_plugin_conf.config` 里的运行时参数
|
||||
- `pay()` 决定第三方下单时返回给系统什么支付参数
|
||||
- `notify()` 决定第三方回调回来后怎么验签和归一化结果
|
||||
|
||||
## 4. 这个模板里的返回结构
|
||||
|
||||
### 4.1 `pay()` 的返回值
|
||||
|
||||
模板已经按项目当前口径返回了这几个字段:
|
||||
|
||||
- `pay_product`
|
||||
- `pay_action`
|
||||
- `pay_params`
|
||||
- `chan_order_no`
|
||||
- `chan_trade_no`
|
||||
- `ext_json`
|
||||
|
||||
其中 `pay_params` 是给收银台前端或业务调用方用的,常见类型包括:
|
||||
|
||||
- `html`
|
||||
- `qrcode`
|
||||
- `jump`
|
||||
- `h5`
|
||||
- `jsapi`
|
||||
- `urlscheme`
|
||||
- `mini`
|
||||
- `json`
|
||||
- `error`
|
||||
|
||||
后端会在支付单拉起后立即校验这份返回值。校验失败会把支付单收口为失败态并抛出 `PaymentException`,所以新插件不要返回旧字段名或半结构化内容,必须直接返回标准结构。
|
||||
|
||||
不同 `pay_params.type` 的必要字段:
|
||||
|
||||
- `jump` / `web` / `h5`:`redirect_url`、`payurl`、`pay_url`、`mweb_url` 或 `url`
|
||||
- `qrcode`:`qrcode_text`、`qrcode_data`、`qrcode_url` 或 `qrcode`
|
||||
- `html`:`html` 或 `action`
|
||||
- `jsapi`:`jsapi_params`,或 `order_str` / `order_string` 等拉起参数
|
||||
- `urlscheme`:`urlscheme`、`redirect_url`、`order_str` 或 `order_string`
|
||||
- `mini`:`path`、`scheme`、`urlscheme`、`trade_no` 或 `mini_params`
|
||||
|
||||
实际接第三方时,你只需要把 `pay_params` 换成真实可渲染的数据结构即可。`ext_json` 只能放插件私有的轻量补充信息,完整请求、响应和通知记录不要放在这里。
|
||||
|
||||
标准示例:
|
||||
|
||||
```php
|
||||
[
|
||||
'pay_product' => 'alipay',
|
||||
'pay_action' => 'jump',
|
||||
'pay_params' => [
|
||||
'type' => 'jump',
|
||||
'redirect_url' => 'https://...',
|
||||
],
|
||||
'chan_order_no' => '渠道订单号',
|
||||
'chan_trade_no' => '渠道交易号,可选;未生成时返回空字符串',
|
||||
'ext_json' => [],
|
||||
]
|
||||
```
|
||||
|
||||
### 4.2 `query()` 的返回值
|
||||
|
||||
主动查单依赖插件 `query()`。新插件建议直接返回下面的标准结构,便于定时维护进程统一推进订单状态:
|
||||
|
||||
```php
|
||||
[
|
||||
'success' => true,
|
||||
'status' => 'success|failed|closed|pending',
|
||||
'channel_order_no' => '渠道订单号',
|
||||
'channel_trade_no' => '渠道交易号',
|
||||
'channel_status' => '渠道原始状态',
|
||||
'message' => '查询说明,可选',
|
||||
'paid_at' => '2026-04-25 12:00:00',
|
||||
'failed_at' => null,
|
||||
'ext_json' => [],
|
||||
]
|
||||
```
|
||||
|
||||
`status=success` 会推进支付成功,`failed` 会推进失败,`closed` 会推进关闭。`pending`、`unknown` 或查询异常只记录轻量查单快照,不改变支付单终态。
|
||||
|
||||
### 4.3 `notify()` 的返回值
|
||||
|
||||
回调处理建议返回这些语义字段:
|
||||
|
||||
- `status`
|
||||
- `message`
|
||||
- `channel_order_no`
|
||||
- `channel_trade_no`
|
||||
- `channel_status`
|
||||
- `paid_at`
|
||||
- `failed_at`
|
||||
- `fee_actual_amount`
|
||||
- `ext_json`
|
||||
|
||||
如果是失败回调,也可以补充:
|
||||
|
||||
- `channel_error_code`
|
||||
- `channel_error_msg`
|
||||
|
||||
后端回调链路会根据 `status` 统一推进支付单状态。完整回调原文和插件解析结果会保存到 `ma_pay_callback_log`,不要再塞进支付单 `ext_json`。
|
||||
|
||||
标准示例:
|
||||
|
||||
```php
|
||||
[
|
||||
'status' => 'success',
|
||||
'message' => 'TRADE_SUCCESS',
|
||||
'channel_order_no' => '渠道订单号',
|
||||
'channel_trade_no' => '渠道交易号',
|
||||
'channel_status' => 'TRADE_SUCCESS',
|
||||
'paid_at' => '2026-04-25 12:00:00',
|
||||
'fee_actual_amount' => null,
|
||||
'ext_json' => [],
|
||||
]
|
||||
```
|
||||
|
||||
### 4.4 订单扩展字段
|
||||
|
||||
业务单和支付单的 `ext_json` 使用分区结构:
|
||||
|
||||
- 顶层 `_protocol_version`:协议版本,方便后台查询。
|
||||
- `merchant`:商户透传字段,如 `param`、`buyer`。
|
||||
- `payment`:本次支付载体字段,如 `method`、`auth_code`、`sub_openid`。
|
||||
- `presentation`:插件返回给收银台承接的支付参数快照。
|
||||
- `plugin`:插件私有轻量信息,以及主动查单的 `active_query` 快照。
|
||||
- `lifecycle`:关单、超时等生命周期原因。
|
||||
|
||||
详细契约见 [支付运行时数据契约](./payment-runtime-contract.md)。
|
||||
|
||||
## 5. 模板里的占位逻辑
|
||||
|
||||
`TemplatePayment` 里有意保留了一些占位实现:
|
||||
|
||||
- `query()`
|
||||
- `close()`
|
||||
- `refund()`
|
||||
- `notify()`
|
||||
|
||||
这些方法现在会直接抛出 `PaymentException`,避免被误当成真实插件投入使用。
|
||||
|
||||
`pay()` 里也保留了示例结构:
|
||||
|
||||
- 默认支付形态可选 `html`、`qrcode`、`jump`、`jsapi`
|
||||
- `buildAutoSubmitForm()` 只是表单跳转的通用模板
|
||||
- `sign` 目前是 `TODO`
|
||||
|
||||
所以它更像“开发脚手架”,不是上线成品。
|
||||
|
||||
## 6. 推荐的开发步骤
|
||||
|
||||
建议按这个顺序做新插件:
|
||||
|
||||
1. 复制 `TemplatePayment.php`,改成新的类名
|
||||
2. 修改 `paymentInfo`,让插件代码和后台展示名称唯一
|
||||
3. 根据第三方网关要求补齐 `config_schema`
|
||||
4. 在 `init()` 里读取配置、初始化 SDK
|
||||
5. 在 `pay()` 里实现真实下单
|
||||
6. 在 `notify()` 里实现真实回调验签
|
||||
7. 把 `query()`、`close()`、`refund()` 按第三方能力补齐
|
||||
8. 在后台创建 `ma_payment_plugin` 记录
|
||||
9. 再创建对应的 `ma_payment_plugin_conf` 记录
|
||||
10. 把通道的 `plugin_code` 和 `api_config_id` 绑定起来
|
||||
|
||||
## 7. 后台配置关系
|
||||
|
||||
从数据库角度看,通常会涉及三张表:
|
||||
|
||||
- `ma_payment_plugin`:插件注册表,保存 `code`、`name`、`class_name`、`config_schema`、`pay_types`
|
||||
- `ma_payment_plugin_conf`:插件运行配置表,保存 `config`
|
||||
- `ma_payment_channel`:支付通道表,保存 `plugin_code` 和 `api_config_id`
|
||||
|
||||
也就是说:
|
||||
|
||||
- `ma_payment_plugin` 负责“这个插件是什么”
|
||||
- `ma_payment_plugin_conf` 负责“这个插件怎么运行”
|
||||
- `ma_payment_channel` 负责“这个插件被哪个通道使用”
|
||||
|
||||
## 8. 适合复制的场景
|
||||
|
||||
这个模板特别适合下面几类插件:
|
||||
|
||||
- 表单跳转类支付
|
||||
- 二维码类支付
|
||||
- 链接跳转类支付
|
||||
- 需要自定义回调验签的第三方网关
|
||||
- 先接通主链路、后逐步补齐查单退款的渠道
|
||||
|
||||
如果是像支付宝这种已经有成熟 SDK 的渠道,也可以直接参考现有 `AlipayPayment` 的实现,再用模板做新的落地版本。
|
||||
|
||||
## 9. 使用建议
|
||||
|
||||
- 新插件先保证 `pay()` 和 `notify()` 跑通,再补 `query()`、`refund()`、`close()`
|
||||
- 插件配置只放运行时必需信息,不要把订单级入参混进去
|
||||
- `pay_types` 存的是支付方式编码,不是支付方式 ID
|
||||
- 新插件类名和 `code` 一定要唯一,避免和已有插件冲突
|
||||
- 真正上线前,必须把占位异常和 `TODO` 字段全部替换掉
|
||||
|
||||
## 10. 相关代码
|
||||
|
||||
- `mpay/app/common/payment/TemplatePayment.php`
|
||||
- `mpay/app/service/payment/runtime/PaymentPluginFactoryService.php`
|
||||
- `docs/db/payment-middle-ddl.sql`
|
||||
243
doc/backend/payment-runtime-contract.md
Normal file
243
doc/backend/payment-runtime-contract.md
Normal file
@@ -0,0 +1,243 @@
|
||||
# 支付运行时数据契约
|
||||
|
||||
本文档约定支付链路里订单扩展字段、插件入参和插件返回值的结构。当前项目仍处于开发阶段,不保留旧平铺结构兼容。
|
||||
|
||||
## 1. `ext_json` 职责
|
||||
|
||||
`ext_json` 只保存单据恢复、页面承接、插件运行所需的轻量扩展信息。通知、回调、重试、原始报文不进入 `ext_json`。
|
||||
|
||||
专门表职责如下:
|
||||
|
||||
- `ma_pay_callback_log`:保存每一次渠道回调原始参数、请求摘要、验签状态、处理状态、插件解析结果。
|
||||
- `ma_notify_task`:按 `event_type + ref_no` 保存商户通知内容、通知状态、重试次数、最后响应。
|
||||
- `ma_channel_notify_log`:保存渠道通知或查单类日志。
|
||||
- `ma_pay_order`:保存支付尝试当前状态、渠道单号、回调状态、错误码和页面承接快照。
|
||||
|
||||
## 2. `ma_biz_order.ext_json`
|
||||
|
||||
业务单只保存稳定业务上下文。同一商户订单号复用时会校验这些字段,支付载体参数不得放在业务单里。
|
||||
|
||||
```php
|
||||
[
|
||||
'_protocol_version' => 'v1', // 顶层强语义字段,方便后台查询和排障
|
||||
'merchant' => [
|
||||
'param' => '商户透传参数',
|
||||
'buyer' => '商户侧买家标识,可选',
|
||||
],
|
||||
]
|
||||
```
|
||||
|
||||
## 3. `ma_pay_order.ext_json`
|
||||
|
||||
支付单是一笔支付尝试的快照,可以保存本次支付载体和页面承接信息。
|
||||
|
||||
```php
|
||||
[
|
||||
'_protocol_version' => 'v2',
|
||||
'merchant' => [
|
||||
'param' => '商户透传参数',
|
||||
'buyer' => '商户侧买家标识,可选',
|
||||
],
|
||||
'payment' => [
|
||||
'method' => 'web|jump|jsapi|app|scan|applet',
|
||||
'auth_code' => '条码/付款码支付时的付款码',
|
||||
'sub_openid' => 'JSAPI 支付所需 openid',
|
||||
'sub_appid' => '服务商模式子应用 ID',
|
||||
],
|
||||
'presentation' => [
|
||||
'params_type' => 'jump|qrcode|html|jsapi|urlscheme|json|error',
|
||||
'product' => 'alipay|wxpay|unionpay|...',
|
||||
'action' => '插件动作名',
|
||||
'params_snapshot' => [
|
||||
'type' => 'qrcode',
|
||||
'qrcode_url' => 'https://...',
|
||||
],
|
||||
],
|
||||
'plugin' => [
|
||||
'pay_result' => [],
|
||||
'close_result' => [],
|
||||
'active_query' => [
|
||||
'queried_at' => '2026-04-25 12:00:00',
|
||||
'status' => 'success|failed|closed|pending|unknown|error',
|
||||
'raw_status' => '渠道原始状态',
|
||||
'channel_status' => '渠道状态码',
|
||||
'message' => '查单说明或异常信息',
|
||||
'success' => true,
|
||||
'channel_order_no' => '渠道订单号',
|
||||
'channel_trade_no' => '渠道交易号',
|
||||
'query_count' => 1,
|
||||
],
|
||||
],
|
||||
'lifecycle' => [
|
||||
'close_reason' => '关闭原因',
|
||||
'timeout_reason' => '超时原因',
|
||||
],
|
||||
]
|
||||
```
|
||||
|
||||
## 4. 插件 `pay()` 入参
|
||||
|
||||
系统调用插件下单时会传入结构化 `extra`。插件读取商户透传、支付载体或协议字段时,从对应分区取值。
|
||||
|
||||
```php
|
||||
[
|
||||
'pay_no' => 'PAY...',
|
||||
'biz_no' => 'BIZ...',
|
||||
'channel_request_no' => 'REQ...',
|
||||
'merchant_id' => 1,
|
||||
'merchant_no' => 'M...',
|
||||
'pay_type_code' => 'alipay',
|
||||
'amount' => 100,
|
||||
'subject' => '订单标题',
|
||||
'callback_url' => 'https://platform/api/pay/PAY.../callback',
|
||||
'notify_url' => 'https://merchant/notify',
|
||||
'return_url' => 'https://merchant/return',
|
||||
'client_ip' => '127.0.0.1',
|
||||
'_env' => 'pc',
|
||||
'extra' => [
|
||||
'_protocol_version' => 'v2',
|
||||
'merchant' => [],
|
||||
'payment' => [],
|
||||
],
|
||||
]
|
||||
```
|
||||
|
||||
## 5. 插件 `pay()` 返回值
|
||||
|
||||
插件下单必须返回系统可承接的标准结构。后端会在 `PayOrderChannelDispatchService` 中严格校验字段和 `pay_params.type` 所需载荷,校验通过后才会写入 `ma_pay_order.ext_json.presentation`。
|
||||
|
||||
```php
|
||||
[
|
||||
'pay_product' => 'alipay',
|
||||
'pay_action' => 'jump',
|
||||
'pay_params' => [
|
||||
'type' => 'jump',
|
||||
'redirect_url' => 'https://...',
|
||||
],
|
||||
'chan_order_no' => '渠道订单号',
|
||||
'chan_trade_no' => '渠道交易号,可选;未生成时返回空字符串',
|
||||
'ext_json' => [
|
||||
// 插件私有轻量信息,可选
|
||||
],
|
||||
]
|
||||
```
|
||||
|
||||
`pay_params.type` 决定收银台如何承接:
|
||||
|
||||
- `jump` / `web` / `h5`:必须提供 `redirect_url`、`payurl`、`pay_url`、`mweb_url` 或 `url`。
|
||||
- `qrcode`:必须提供 `qrcode_text`、`qrcode_data`、`qrcode_url` 或 `qrcode`。
|
||||
- `html` / `form`:必须提供 `html` 或 `action`;`form` 会归一为 `html`。
|
||||
- `jsapi` / `urlscheme` / `mini`:必须提供对应拉起参数、跳转参数或小程序参数。
|
||||
- `pos` / `transfer`:展示结构化参数,适合收银设备或转账类场景。
|
||||
- `json`:直接展示结构化参数,由业务端继续处理。
|
||||
- `error`:展示插件返回的错误信息。
|
||||
|
||||
`pay_params.type` 的兼容别名只在平台内部归一化使用,插件文档和新插件代码应直接返回标准值。常见别名包括:`scan|qr|code -> qrcode`、`redirect|url -> jump`、`wap -> h5`、`form -> html`、`app -> urlscheme`、`applet|wxplugin -> mini`。
|
||||
|
||||
## 6. 插件 `query()` 返回值
|
||||
|
||||
主动查单由 `PaymentRuntimeProcess` 定时触发,只处理 `status=支付中` 且已超过最小等待时间的支付单。插件 `query()` 应尽量返回标准字段:
|
||||
|
||||
```php
|
||||
[
|
||||
'success' => true,
|
||||
'status' => 'success|failed|closed|pending',
|
||||
'channel_order_no' => '渠道订单号',
|
||||
'channel_trade_no' => '渠道交易号',
|
||||
'channel_status' => 'TRADE_SUCCESS',
|
||||
'message' => '渠道说明,可选',
|
||||
'paid_at' => '2026-04-25 12:00:00',
|
||||
'failed_at' => null,
|
||||
'ext_json' => [
|
||||
// 插件私有轻量补充信息,可选
|
||||
],
|
||||
]
|
||||
```
|
||||
|
||||
处理规则:
|
||||
|
||||
- `success`:推进支付单成功,并发出 `payment.pay_order.succeeded` 事件。
|
||||
- `failed`:推进支付单失败。
|
||||
- `closed`:推进支付单关闭。
|
||||
- `pending` / `unknown` / 查单异常:不推进终态,只把轻量快照写入 `ma_pay_order.ext_json.plugin.active_query`。
|
||||
|
||||
主动查单快照只保存状态、说明、渠道单号、时间和次数,不保存完整上游响应;完整回调仍以 `ma_pay_callback_log` 为准。
|
||||
|
||||
## 7. 插件 `notify()` 返回值
|
||||
|
||||
插件回调只负责验签和归一化结果。完整回调原文和该返回值会写入 `ma_pay_callback_log`,不会再回灌支付单 `ext_json`。
|
||||
|
||||
```php
|
||||
[
|
||||
'status' => 'success|failed|pending',
|
||||
'message' => '渠道状态说明',
|
||||
'channel_order_no' => '渠道订单号',
|
||||
'channel_trade_no' => '渠道交易号',
|
||||
'channel_status' => 'TRADE_SUCCESS',
|
||||
'channel_error_code' => '',
|
||||
'channel_error_msg' => '',
|
||||
'paid_at' => '2026-04-25 12:00:00',
|
||||
'failed_at' => null,
|
||||
'fee_actual_amount' => null,
|
||||
'ext_json' => [
|
||||
// 插件私有轻量补充信息,可选
|
||||
],
|
||||
]
|
||||
```
|
||||
|
||||
`status=pending` 只记录回调日志,不推进支付单终态。`success` 和 `failed` 会推进支付单状态,并按需要创建商户通知任务。
|
||||
|
||||
## 8. 商户通知任务事件模型
|
||||
|
||||
`ma_notify_task` 不再以 `pay_no` 作为唯一业务键。通知任务统一按事件建模:
|
||||
|
||||
```php
|
||||
[
|
||||
'event_type' => 'PAY_SUCCESS|REFUND_SUCCESS|SETTLEMENT_SUCCESS',
|
||||
'ref_no' => '事件引用单号,例如 pay_no/refund_no/settle_no',
|
||||
'biz_no' => '业务单号',
|
||||
'pay_no' => '支付单号,支付相关事件保留',
|
||||
'notify_data' => [
|
||||
// 发送给商户的已签名参数
|
||||
],
|
||||
]
|
||||
```
|
||||
|
||||
这样同一笔支付后续可以同时存在支付成功、退款成功、清算完成等多类通知,不会因为复用 `pay_no` 被唯一键挡住。
|
||||
|
||||
## 9. 回调日志留痕规则
|
||||
|
||||
渠道回调日志按“每次请求一条”写入,不做 `pay_no + callback_type` 覆盖或复用。
|
||||
|
||||
`request_hash` 是原始请求载荷的 SHA-256 摘要,用来在后台识别重复通知;重复通知是否推进业务状态,由支付单生命周期幂等控制。
|
||||
|
||||
## 10. 支付运行时维护
|
||||
|
||||
运行时维护使用 Webman 自定义进程 `payment-runtime`,对应 `app/process/PaymentRuntimeProcess.php`。进程只负责定时调度,具体业务由 `PaymentRuntimeMaintenanceService` 完成。
|
||||
|
||||
当前维护任务:
|
||||
|
||||
- 商户通知重试:扫描到期 `ma_notify_task` 并重新派发。
|
||||
- 支付单超时:扫描已过期且未终态的支付单,推进为超时并释放冻结手续费。
|
||||
- 主动查单:扫描支付中订单,调用插件 `query()` 补偿异步通知丢失或延迟。
|
||||
|
||||
当前阶段采用自定义进程直接执行,代码路径更短、部署依赖更少。后续如果商户通知量明显增大,建议引入 Redis 队列:支付成功监听器只负责投递队列,队列消费者负责 HTTP 通知派发和重试。
|
||||
|
||||
可在管理后台“支付配置”中维护以下系统配置:
|
||||
|
||||
- `pay_runtime_enabled`:运行时维护总开关。
|
||||
- `pay_notify_retry_scan_interval_seconds` / `pay_notify_retry_batch_size`:通知重试扫描间隔和批量。
|
||||
- `pay_order_timeout_scan_interval_seconds` / `pay_order_timeout_batch_size`:超时订单扫描间隔和批量。
|
||||
- `pay_active_query_enabled` / `pay_active_query_interval_seconds` / `pay_active_query_min_age_seconds` / `pay_active_query_batch_size`:主动查单开关、间隔、等待时间和批量。
|
||||
|
||||
系统配置保存后会触发 `system.config.changed` 事件刷新运行时缓存,维护进程下一轮心跳读取新值。
|
||||
|
||||
## 11. 支付域事件
|
||||
|
||||
支付生命周期服务只负责状态推进和资金动作。支付单首次进入成功态后,会发送事件:
|
||||
|
||||
```php
|
||||
PaymentEventConstant::PAY_ORDER_SUCCEEDED // payment.pay_order.succeeded
|
||||
```
|
||||
|
||||
当前监听器 `PayOrderSucceededListener` 负责创建并派发商户支付成功通知。后续如果引入 Redis 队列,可以只替换监听器内部实现,把通知派发入队,而不用改订单生命周期服务。
|
||||
47
doc/backend/routing.md
Normal file
47
doc/backend/routing.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# 后端路由
|
||||
|
||||
后端只使用显式路由。`config/route.php` 加载三份路由文件后关闭默认路由。
|
||||
|
||||
## 路由文件
|
||||
|
||||
| 文件 | 覆盖范围 |
|
||||
| --- | --- |
|
||||
| `app/route/admin.php` | `/admin` 页面入口、`/adminapi` 管理后台 API |
|
||||
| `app/route/mer.php` | `/mer` 页面入口、`/merapi` 商户后台 API |
|
||||
| `app/route/api.php` | 收银台页面、收银台 API、ePay V1/V2 与开放 API |
|
||||
|
||||
## 当前入口
|
||||
|
||||
| 前缀 | 说明 | 中间件 |
|
||||
| --- | --- | --- |
|
||||
| `/admin` | 管理后台静态页面入口 | 无业务鉴权 |
|
||||
| `/adminapi` | 管理后台接口 | `Cors`,保护接口再走 `AdminAuthMiddleware` |
|
||||
| `/mer` | 商户后台静态页面入口 | 无业务鉴权 |
|
||||
| `/merapi` | 商户后台接口 | `Cors`,保护接口再走 `MerchantAuthMiddleware` |
|
||||
| `/cashier` | 收银台入口页 | 无业务鉴权 |
|
||||
| `/payment` | 支付页、中转页、结果页 | 无业务鉴权 |
|
||||
| `/api/cashier` | 收银台上下文、确认支付、支付单详情 | `Cors` |
|
||||
| `/api/pay` | ePay V2 支付、查询、退款、关闭、通道回调 | `Cors` |
|
||||
| `/api/merchant` | ePay V2 商户信息与订单查询 | `Cors` |
|
||||
| `/api/transfer` | ePay V2 转账提交、查询、余额 | `Cors` |
|
||||
| `/submit.php`、`/mapi.php`、`/api.php` | ePay V1 兼容入口 | `Cors` |
|
||||
|
||||
## 流转
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Req[HTTP 请求] --> Route[显式路由]
|
||||
Route --> Middleware[中间件]
|
||||
Middleware --> Controller[控制器]
|
||||
Controller --> Validator[参数校验]
|
||||
Validator --> Service[服务层]
|
||||
Service --> Repository[仓库层]
|
||||
Repository --> Model[模型层]
|
||||
Model --> DB[(MySQL)]
|
||||
```
|
||||
|
||||
## 维护要求
|
||||
|
||||
- 新增接口先改 `app/route/*`,再补对应 `docs/api/*`。
|
||||
- 页面兜底路由只返回前端入口,不承载业务逻辑。
|
||||
- 业务规则放服务层;路由文件只做 URL 到控制器方法的绑定。
|
||||
51
doc/backend/services.md
Normal file
51
doc/backend/services.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# 后端服务层
|
||||
|
||||
服务层承载业务规则,控制器只负责入口、参数和响应包装。
|
||||
|
||||
## 目录职责
|
||||
|
||||
| 目录 | 主要职责 |
|
||||
| --- | --- |
|
||||
| `account/funds`、`account/ledger` | 商户账户、余额和资金流水 |
|
||||
| `bootstrap` | 启动期初始化 |
|
||||
| `file`、`file/storage` | 文件资产、上传、导入、预览、下载和存储驱动 |
|
||||
| `merchant` | 商户主体、总览、分组、策略、API 凭证 |
|
||||
| `merchant/auth` | 商户后台登录认证 |
|
||||
| `merchant/portal` | 商户后台资料、通道、路由预览、订单资金查询 |
|
||||
| `ops/log`、`ops/stat` | 通道通知、支付回调、商户通知任务和通道日统计 |
|
||||
| `payment/cashier` | 收银台上下文、确认支付、支付单展示 |
|
||||
| `payment/config` | 支付方式、插件、插件配置、通道、轮询组、绑定 |
|
||||
| `payment/epay` | ePay V1/V2 协议、MD5/RSA 签名 |
|
||||
| `payment/order` | 支付单、退款单、费用、派单、回调、生命周期 |
|
||||
| `payment/runtime` | 路由解析、插件装配、商户通知、运行时维护 |
|
||||
| `payment/settlement` | 清算单和清算生命周期 |
|
||||
| `payment/trace` | 交易追踪与报表 |
|
||||
| `payment/transfer` | 转账能力 |
|
||||
| `system/access`、`system/config`、`system/user` | 管理员认证、系统配置、管理员用户 |
|
||||
|
||||
## 命名规则
|
||||
|
||||
- `*Service`:对外门面。
|
||||
- `*QueryService`:查询与展示拼装。
|
||||
- `*CommandService`:写入、修改、删除。
|
||||
- `*LifecycleService`:状态流转。
|
||||
- `*CallbackService`:第三方回调。
|
||||
- `*SyncService`:同步、扫描、刷新。
|
||||
- `*SupportService`:业务辅助能力。
|
||||
|
||||
## 关键服务
|
||||
|
||||
- 支付:`PayOrderService`、`PayOrderLifecycleService`、`PayOrderChannelDispatchService`
|
||||
- 退款:`RefundService`、`RefundCreationService`、`RefundLifecycleService`
|
||||
- 清算:`SettlementService`、`SettlementLifecycleService`
|
||||
- 路由与插件:`PaymentRouteService`、`PaymentRouteResolverService`、`PaymentPluginManager`
|
||||
- 收银台:`CashierService`
|
||||
- 通知:`NotifyService`、`MerchantNotifyDispatcherService`
|
||||
- 商户:`MerchantService`、`MerchantPortalService`、`MerchantApiCredentialService`
|
||||
- 文件:`FileRecordService`、`StorageManager`
|
||||
|
||||
## 维护要求
|
||||
|
||||
- 查询和写入不要混在一个越来越胖的类里。
|
||||
- 回调、路由解析、插件装配、通知重试要保持独立职责。
|
||||
- `SupportService` 只放有业务含义的复用逻辑,不做基础工具的空转发。
|
||||
22
doc/changelog.md
Normal file
22
doc/changelog.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# 变更记录
|
||||
|
||||
## 2026-04-26
|
||||
|
||||
- 重新核对 `mpay/app/route`、前端 `src/api`、`package.json`、`composer.json` 后整理文档。
|
||||
- 修正收银台真实入口为 `/cashier`、`/payment`,收银台 JSON API 为 `/api/cashier/*`。
|
||||
- 修正 ePay V1/V2 控制器、服务、命令和开放 API 路由说明。
|
||||
- 精简 `docs/` 中重复的写法建议和过细示例,保留入口、模块、命令、变量和关键业务口径。
|
||||
- 更新 `admin`、`mer`、`cashier`、`mpay` 项目 README,使命令和路径与当前代码一致。
|
||||
|
||||
## 2026-04-20
|
||||
|
||||
- 补充项目注释与封装边界说明。
|
||||
- 统一商户 API 凭证相关文案。
|
||||
- 补充管理后台上传字段和文件资产说明。
|
||||
|
||||
## 2026-04-17
|
||||
|
||||
- 建立 `docs/` 作为新的总文档中心。
|
||||
- 将根目录定位为多项目工作区入口。
|
||||
- 补齐总览、架构、后端、前端、接口、数据库、部署和 FAQ 文档。
|
||||
- 将旧版后端文档降级为归档参考。
|
||||
61
doc/db/README.md
Normal file
61
doc/db/README.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# 数据库总说明
|
||||
|
||||
这里放数据库相关的稳定说明和 DDL 入口。
|
||||
|
||||
## 当前 DDL
|
||||
|
||||
当前数据库事实源已经迁移到 [`payment-middle-ddl.sql`](./payment-middle-ddl.sql)。
|
||||
|
||||
它覆盖的核心表分组包括:
|
||||
|
||||
- 商户与登录主体
|
||||
- 支付方式、支付插件、支付通道
|
||||
- 轮询组、轮询组通道、轮询组绑定
|
||||
- 商户策略与商户 API 凭证
|
||||
- 支付单、退款单、转账单、清算单
|
||||
- 商户资金账户与流水
|
||||
- 通知日志、回调日志、日统计
|
||||
- 文件资产、系统配置、后台用户
|
||||
|
||||
## 关键表分组
|
||||
|
||||
| 分组 | 代表表 |
|
||||
| --- | --- |
|
||||
| 基础字典 | `ma_payment_type`、`ma_system_config` |
|
||||
| 商户主体 | `ma_merchant`、`ma_merchant_group`、`ma_merchant_api_credential`、`ma_merchant_policy` |
|
||||
| 支付编排 | `ma_payment_plugin`、`ma_payment_plugin_conf`、`ma_payment_channel`、`ma_payment_poll_group`、`ma_payment_poll_group_channel`、`ma_payment_poll_group_bind` |
|
||||
| 交易订单 | `ma_biz_order`、`ma_pay_order`、`ma_refund_order`、`ma_transfer_order`、`ma_settlement_order`、`ma_settlement_item` |
|
||||
| 资金账户 | `ma_merchant_account`、`ma_merchant_account_ledger` |
|
||||
| 运维日志 | `ma_channel_notify_log`、`ma_pay_callback_log`、`ma_channel_daily_stat`、`ma_notify_task` |
|
||||
| 文件与后台 | `ma_file_asset`、`ma_admin_user` |
|
||||
|
||||
## 说明原则
|
||||
|
||||
- DDL 是事实源
|
||||
- 表结构变化要先更新 DDL,再补说明
|
||||
- 文档里不要重复贴大段 SQL,尽量只解释结构和用途
|
||||
- `ma_merchant` 是商户主体,也是后台登录主体
|
||||
- `ma_merchant_api_credential` 只用于开放接口签名和兼容层,不参与后台登录
|
||||
- `ma_merchant_api_credential` 同时承载 V1 的 MD5 凭证值和 V2 的 RSA 公钥
|
||||
- `ma_transfer_order` 负责承接 V2 转账单据
|
||||
- 路由链路优先遵循“商户分组 -> 轮询组 -> 支付通道”
|
||||
- `ext_json` 使用分区结构保存轻量运行上下文;`_protocol_version` 这类强语义字段可放顶层,商户透传放 `merchant`,支付载体放 `payment`,收银台承接放 `presentation`
|
||||
- 通知、回调、重试、原始报文进入 `ma_pay_callback_log`、`ma_notify_task`、`ma_channel_notify_log`,不要塞进订单扩展槽
|
||||
- `ma_pay_order` 不再保留 `request_method` 这类 HTTP 快照字段
|
||||
|
||||
## 建议写法
|
||||
|
||||
以后每个表说明都按下面的结构写:
|
||||
|
||||
1. 表用途
|
||||
2. 主键与索引
|
||||
3. 核心字段
|
||||
4. 状态值含义
|
||||
5. 与哪些业务链路相关
|
||||
|
||||
## 继续阅读
|
||||
|
||||
- [数据表目录](./tables.md)
|
||||
- [项目稳定口径](../standards.md)
|
||||
- [项目总览](../overview.md)
|
||||
- [架构与请求流](../architecture.md)
|
||||
609
doc/db/payment-middle-ddl.sql
Normal file
609
doc/db/payment-middle-ddl.sql
Normal file
@@ -0,0 +1,609 @@
|
||||
-- 支付中台最新总 DDL
|
||||
-- 说明:
|
||||
-- 1. 本文件汇总当前最新表结构,作为后续 Navicat 调整和代码对齐的参考基线。
|
||||
-- 2. 当前以 `ma_merchant` 为商户主体和后台登录主体,登录口径是 `merchant_no + password_hash`。
|
||||
-- 3. `ma_merchant_api_credential` 仅用于开放接口签名与兼容层,不参与商户后台登录。
|
||||
-- 4. 已删除 `channel_code` 业务编码,所有关联一律使用主键 `id`。
|
||||
-- 5. 路由链路采用:商户分组 -> 轮询组 -> 支付通道。
|
||||
-- 6. `ma_merchant_policy` 当前仅保留预留表结构,不作为正式后台能力暴露。
|
||||
-- 7. 所有状态、模式、类型类字段统一使用 `tinyint`,语义映射请见 `app/common/constant`。
|
||||
-- 8. `ext_json` 使用分区结构保存轻量运行上下文;`_protocol_version` 可放顶层便于查询,通知/回调/重试/原始报文进入专门日志表。
|
||||
-- 9. `ma_pay_order` 不再保留 `request_method` 这类 HTTP 快照字段。
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
SET FOREIGN_KEY_CHECKS = 0;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `ma_payment_type` (
|
||||
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`code` varchar(32) NOT NULL DEFAULT '' COMMENT '支付方式编码',
|
||||
`name` varchar(50) NOT NULL DEFAULT '' COMMENT '支付方式名称',
|
||||
`icon` varchar(255) NOT NULL DEFAULT '' COMMENT '图标',
|
||||
`sort_no` int NOT NULL DEFAULT 0 COMMENT '排序',
|
||||
`status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:0-禁用,1-启用',
|
||||
`remark` varchar(500) NOT NULL DEFAULT '' COMMENT '备注',
|
||||
`created_at` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`updated_at` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_code` (`code`),
|
||||
KEY `idx_status_sort` (`status`, `sort_no`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='支付方式字典表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `ma_merchant_group` (
|
||||
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`group_name` varchar(100) NOT NULL DEFAULT '' COMMENT '商户分组名称',
|
||||
`status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:0-禁用,1-启用',
|
||||
`remark` varchar(500) NOT NULL DEFAULT '' COMMENT '备注',
|
||||
`created_at` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`updated_at` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_group_name` (`group_name`),
|
||||
KEY `idx_status` (`status`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商户分组表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `ma_merchant` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`merchant_no` varchar(32) NOT NULL DEFAULT '' COMMENT '商户号(唯一,对外标识,同时作为后台登录账号)',
|
||||
`password_hash` varchar(255) NOT NULL DEFAULT '' COMMENT '商户后台登录密码哈希',
|
||||
`merchant_name` varchar(100) NOT NULL DEFAULT '' COMMENT '商户名称',
|
||||
`merchant_short_name` varchar(60) NOT NULL DEFAULT '' COMMENT '商户简称',
|
||||
`merchant_type` tinyint NOT NULL DEFAULT 0 COMMENT '商户类型:0-个人,1-企业,2-其他',
|
||||
`group_id` int unsigned NOT NULL DEFAULT 0 COMMENT '商户分组ID',
|
||||
`risk_level` tinyint NOT NULL DEFAULT 0 COMMENT '风控等级:0-低,1-中,2-高',
|
||||
`contact_name` varchar(50) NOT NULL DEFAULT '' COMMENT '联系人姓名',
|
||||
`contact_phone` varchar(20) NOT NULL DEFAULT '' COMMENT '联系人手机号',
|
||||
`contact_email` varchar(100) NOT NULL DEFAULT '' COMMENT '联系人邮箱',
|
||||
`settlement_account_name` varchar(100) NOT NULL DEFAULT '' COMMENT '结算账户名',
|
||||
`settlement_account_no` varchar(100) NOT NULL DEFAULT '' COMMENT '结算账户号',
|
||||
`settlement_bank_name` varchar(100) NOT NULL DEFAULT '' COMMENT '结算银行名称',
|
||||
`settlement_bank_branch` varchar(100) NOT NULL DEFAULT '' COMMENT '结算支行名称',
|
||||
`status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:0-禁用, 1-启用',
|
||||
`pay_status` tinyint NOT NULL DEFAULT 1 COMMENT '支付开关:0-关闭,1-开启',
|
||||
`settle_status` tinyint NOT NULL DEFAULT 1 COMMENT '结算开关:0-关闭,1-开启',
|
||||
`settle_type` tinyint NOT NULL DEFAULT 4 COMMENT '结算类型:1-支付宝,2-微信,3-QQ,4-银行卡',
|
||||
`last_login_at` datetime DEFAULT NULL COMMENT '最后登录时间',
|
||||
`last_login_ip` varchar(45) NOT NULL DEFAULT '' COMMENT '最后登录IP',
|
||||
`password_updated_at` datetime DEFAULT NULL COMMENT '密码更新时间',
|
||||
`remark` varchar(500) NOT NULL DEFAULT '' COMMENT '备注',
|
||||
`created_at` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`updated_at` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_merchant_no` (`merchant_no`),
|
||||
KEY `idx_group_id` (`group_id`),
|
||||
KEY `idx_contact_phone` (`contact_phone`),
|
||||
KEY `idx_status` (`status`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商户表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `ma_payment_plugin` (
|
||||
`code` varchar(32) NOT NULL DEFAULT '' COMMENT '插件编码',
|
||||
`name` varchar(50) NOT NULL DEFAULT '' COMMENT '插件名称',
|
||||
`class_name` varchar(255) NOT NULL DEFAULT '' COMMENT '插件类名',
|
||||
`config_schema` json DEFAULT NULL COMMENT '插件配置Schema(JSON)',
|
||||
`pay_types` json DEFAULT NULL COMMENT '支持的支付方式编码(JSON)',
|
||||
`transfer_types` json DEFAULT NULL COMMENT '支持的转账方式编码(JSON)',
|
||||
`version` varchar(20) NOT NULL DEFAULT '' COMMENT '版本',
|
||||
`author` varchar(50) NOT NULL DEFAULT '' COMMENT '作者',
|
||||
`link` varchar(255) NOT NULL DEFAULT '' COMMENT '链接',
|
||||
`allow_merchant` tinyint NOT NULL DEFAULT 0 COMMENT '是否允许商户端自助使用:0-否,1-是',
|
||||
`status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:0-禁用, 1-启用',
|
||||
`remark` varchar(500) NOT NULL DEFAULT '' COMMENT '备注',
|
||||
`created_at` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`updated_at` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`code`),
|
||||
KEY `idx_status` (`status`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='支付插件注册表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `ma_payment_plugin_conf` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`merchant_id` bigint unsigned NOT NULL DEFAULT 0 COMMENT '所属商户ID,0表示平台配置',
|
||||
`plugin_code` varchar(32) NOT NULL DEFAULT '' COMMENT '插件编码',
|
||||
`config` json DEFAULT NULL COMMENT '插件初始化配置',
|
||||
`settlement_cycle_type` tinyint unsigned NOT NULL DEFAULT 1 COMMENT '结算周期类型:0-D0,1-D1,2-D7,3-T1,4-OTHER',
|
||||
`settlement_cutoff_time` varchar(8) NOT NULL DEFAULT '23:59:59' COMMENT '结算截止时间',
|
||||
`remark` varchar(500) NOT NULL DEFAULT '' COMMENT '备注',
|
||||
`created_at` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`updated_at` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_plugin_code` (`plugin_code`),
|
||||
KEY `idx_merchant_plugin` (`merchant_id`, `plugin_code`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='支付插件API配置表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `ma_payment_channel` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`merchant_id` bigint unsigned NOT NULL DEFAULT 0 COMMENT '所属商户ID,0表示平台通道',
|
||||
`name` varchar(100) NOT NULL DEFAULT '' COMMENT '通道名称',
|
||||
`split_rate_bp` int unsigned NOT NULL DEFAULT 10000 COMMENT '分成比例(bp)',
|
||||
`cost_rate_bp` int unsigned NOT NULL DEFAULT 0 COMMENT '通道成本(bp)',
|
||||
`channel_mode` tinyint NOT NULL DEFAULT 0 COMMENT '通道模式:0-代收,1-自收',
|
||||
`pay_type_id` int unsigned NOT NULL DEFAULT 0 COMMENT '支付方式ID',
|
||||
`plugin_code` varchar(32) NOT NULL DEFAULT '' COMMENT '支付插件编码',
|
||||
`api_config_id` bigint unsigned NOT NULL DEFAULT 0 COMMENT 'API配置ID,关联插件配置表主键ID',
|
||||
`daily_limit_amount` bigint unsigned NOT NULL DEFAULT 0 COMMENT '单日限额(分,0表示不限制)',
|
||||
`daily_limit_count` int unsigned NOT NULL DEFAULT 0 COMMENT '单日限笔(0表示不限制)',
|
||||
`min_amount` bigint unsigned NOT NULL DEFAULT 0 COMMENT '单笔最小金额(分)',
|
||||
`max_amount` bigint unsigned NOT NULL DEFAULT 0 COMMENT '单笔最大金额(分)',
|
||||
`remark` varchar(500) NOT NULL DEFAULT '' COMMENT '备注',
|
||||
`status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:0-禁用,1-启用',
|
||||
`sort_no` int NOT NULL DEFAULT 0 COMMENT '排序,越小优先级越高',
|
||||
`created_at` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`updated_at` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_name` (`name`),
|
||||
KEY `idx_merchant_id_status` (`merchant_id`, `status`),
|
||||
KEY `idx_pay_type_id_status` (`pay_type_id`, `status`),
|
||||
KEY `idx_plugin_code_status` (`plugin_code`, `status`),
|
||||
KEY `idx_channel_mode_status` (`channel_mode`, `status`),
|
||||
KEY `idx_sort_no` (`sort_no`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='支付通道表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `ma_payment_poll_group` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`group_name` varchar(100) NOT NULL DEFAULT '' COMMENT '轮询组名称',
|
||||
`pay_type_id` int unsigned NOT NULL DEFAULT 0 COMMENT '支付方式ID',
|
||||
`route_mode` tinyint NOT NULL DEFAULT 0 COMMENT '路由模式:0-顺序依次轮询,1-权重随机轮询,2-默认启用通道',
|
||||
`status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:0-禁用,1-启用',
|
||||
`remark` varchar(500) NOT NULL DEFAULT '' COMMENT '备注',
|
||||
`created_at` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`updated_at` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_group_name` (`group_name`),
|
||||
KEY `idx_pay_type_status` (`pay_type_id`, `status`),
|
||||
KEY `idx_status` (`status`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='支付轮询组表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `ma_payment_poll_group_channel` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`poll_group_id` bigint unsigned NOT NULL DEFAULT 0 COMMENT '轮询组ID',
|
||||
`channel_id` bigint unsigned NOT NULL DEFAULT 0 COMMENT '支付通道ID',
|
||||
`sort_no` int NOT NULL DEFAULT 0 COMMENT '排序,越小优先级越高',
|
||||
`weight` int NOT NULL DEFAULT 100 COMMENT '权重',
|
||||
`is_default` tinyint NOT NULL DEFAULT 0 COMMENT '是否默认通道:0-否,1-是',
|
||||
`status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:0-禁用,1-启用',
|
||||
`remark` varchar(500) NOT NULL DEFAULT '' COMMENT '备注',
|
||||
`created_at` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`updated_at` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_poll_group_channel` (`poll_group_id`, `channel_id`),
|
||||
KEY `idx_poll_group_status_sort` (`poll_group_id`, `status`, `sort_no`),
|
||||
KEY `idx_channel_status` (`channel_id`, `status`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='支付轮询组-通道编排表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `ma_payment_poll_group_bind` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`merchant_group_id` int unsigned NOT NULL DEFAULT 0 COMMENT '商户分组ID',
|
||||
`pay_type_id` int unsigned NOT NULL DEFAULT 0 COMMENT '支付方式ID',
|
||||
`poll_group_id` bigint unsigned NOT NULL DEFAULT 0 COMMENT '轮询组ID',
|
||||
`status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:0-禁用,1-启用',
|
||||
`remark` varchar(500) NOT NULL DEFAULT '' COMMENT '备注',
|
||||
`created_at` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`updated_at` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_group_pay_type` (`merchant_group_id`, `pay_type_id`),
|
||||
KEY `idx_poll_group_status` (`poll_group_id`, `status`),
|
||||
KEY `idx_group_status` (`merchant_group_id`, `status`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商户分组-轮询组绑定表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `ma_merchant_policy` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`merchant_id` bigint unsigned NOT NULL DEFAULT 0 COMMENT '商户ID(唯一)',
|
||||
`settlement_cycle_override` tinyint unsigned NOT NULL DEFAULT 1 COMMENT '结算周期覆盖:0-D0,1-D1,2-D7,3-T1,4-OTHER',
|
||||
`auto_payout` tinyint NOT NULL DEFAULT 0 COMMENT '是否自动入账/自动处理:0-否,1-是',
|
||||
`min_settlement_amount` bigint unsigned NOT NULL DEFAULT 0 COMMENT '最小结算金额(分)',
|
||||
`retry_policy_json` json DEFAULT NULL COMMENT '失败重试策略(JSON)',
|
||||
`route_policy_json` json DEFAULT NULL COMMENT '路由策略(JSON)',
|
||||
`fee_rule_override_json` json DEFAULT NULL COMMENT '费率/计费覆盖策略(JSON)',
|
||||
`risk_policy_json` json DEFAULT NULL COMMENT '风控策略(JSON)',
|
||||
`remark` varchar(500) NOT NULL DEFAULT '' COMMENT '备注',
|
||||
`created_at` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`updated_at` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_merchant_id` (`merchant_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商户策略预留表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `ma_biz_order` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`biz_no` varchar(32) NOT NULL DEFAULT '' COMMENT '业务订单号',
|
||||
`trace_no` varchar(32) NOT NULL DEFAULT '' COMMENT '统一追踪号',
|
||||
`merchant_id` bigint unsigned NOT NULL DEFAULT 0 COMMENT '商户ID',
|
||||
`merchant_order_no` varchar(64) NOT NULL DEFAULT '' COMMENT '商户订单号(幂等)',
|
||||
`subject` varchar(255) NOT NULL DEFAULT '' COMMENT '订单标题',
|
||||
`body` varchar(500) NOT NULL DEFAULT '' COMMENT '订单描述',
|
||||
`notify_url` varchar(255) NOT NULL DEFAULT '' COMMENT '异步通知地址',
|
||||
`return_url` varchar(255) NOT NULL DEFAULT '' COMMENT '同步返回地址',
|
||||
`client_ip` varchar(45) NOT NULL DEFAULT '' COMMENT '客户端IP',
|
||||
`device` varchar(32) NOT NULL DEFAULT '' COMMENT '设备类型',
|
||||
`order_amount` bigint unsigned NOT NULL DEFAULT 0 COMMENT '订单金额(分)',
|
||||
`paid_amount` bigint unsigned NOT NULL DEFAULT 0 COMMENT '已支付金额(分)',
|
||||
`refund_amount` bigint unsigned NOT NULL DEFAULT 0 COMMENT '已退款金额(分)',
|
||||
`status` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '状态:0-待创建,1-支付中,2-成功,3-失败,4-关闭,5-超时',
|
||||
`active_pay_no` varchar(32) DEFAULT NULL COMMENT '当前正在支付的支付单号',
|
||||
`attempt_count` int unsigned NOT NULL DEFAULT 0 COMMENT '支付尝试次数',
|
||||
`expire_at` datetime DEFAULT NULL COMMENT '过期时间',
|
||||
`paid_at` datetime DEFAULT NULL COMMENT '支付成功时间',
|
||||
`closed_at` datetime DEFAULT NULL COMMENT '关闭时间',
|
||||
`failed_at` datetime DEFAULT NULL COMMENT '失败时间',
|
||||
`timeout_at` datetime DEFAULT NULL COMMENT '超时时间',
|
||||
`ext_json` json DEFAULT NULL COMMENT '商户扩展字段(JSON)',
|
||||
`created_at` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`updated_at` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_merchant_order` (`merchant_id`, `merchant_order_no`),
|
||||
UNIQUE KEY `uk_biz_no` (`biz_no`),
|
||||
KEY `idx_trace_no` (`trace_no`),
|
||||
KEY `idx_merchant_status_created` (`merchant_id`, `status`, `created_at`),
|
||||
KEY `idx_active_pay_no` (`active_pay_no`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='业务订单表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `ma_pay_order` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`pay_no` varchar(32) NOT NULL DEFAULT '' COMMENT '支付单号',
|
||||
`biz_no` varchar(32) NOT NULL DEFAULT '' COMMENT '业务订单号',
|
||||
`trace_no` varchar(32) NOT NULL DEFAULT '' COMMENT '统一追踪号',
|
||||
`merchant_id` bigint unsigned NOT NULL DEFAULT 0 COMMENT '商户ID',
|
||||
`merchant_group_id` int unsigned NOT NULL DEFAULT 0 COMMENT '商户分组ID快照',
|
||||
`poll_group_id` bigint unsigned NOT NULL DEFAULT 0 COMMENT '轮询组ID快照',
|
||||
`attempt_no` int unsigned NOT NULL DEFAULT 1 COMMENT '支付尝试序号',
|
||||
`channel_id` bigint unsigned NOT NULL DEFAULT 0 COMMENT '支付通道ID',
|
||||
`pay_type_id` int unsigned NOT NULL DEFAULT 0 COMMENT '支付方式ID',
|
||||
`plugin_code` varchar(32) NOT NULL DEFAULT '' COMMENT '支付插件编码',
|
||||
`channel_type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '通道类型:0-平台代收,1-商户自有',
|
||||
`channel_mode` tinyint NOT NULL DEFAULT 0 COMMENT '通道模式:0-代收,1-自收',
|
||||
`pay_amount` bigint unsigned NOT NULL DEFAULT 0 COMMENT '支付金额(分)',
|
||||
`notify_url` varchar(255) NOT NULL DEFAULT '' COMMENT '异步通知地址',
|
||||
`return_url` varchar(255) NOT NULL DEFAULT '' COMMENT '跳转通知地址',
|
||||
`client_ip` varchar(45) NOT NULL DEFAULT '' COMMENT '客户端 IP',
|
||||
`device` varchar(32) NOT NULL DEFAULT '' COMMENT '设备类型',
|
||||
`fee_rate_bp_snapshot` int unsigned NOT NULL DEFAULT 0 COMMENT '费率bp快照',
|
||||
`split_rate_bp_snapshot` int unsigned NOT NULL DEFAULT 10000 COMMENT '分成比例bp快照',
|
||||
`fee_estimated_amount` bigint unsigned NOT NULL DEFAULT 0 COMMENT '预估手续费(分)',
|
||||
`fee_actual_amount` bigint unsigned NOT NULL DEFAULT 0 COMMENT '实际手续费(分)',
|
||||
`status` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '状态:0-待创建,1-支付中,2-成功,3-失败,4-关闭,5-超时',
|
||||
`fee_status` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '手续费状态:0-无,1-冻结,2-已扣,3-已释放',
|
||||
`settlement_status` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '清算状态:0-无,1-待清算,2-已清算,3-已冲正',
|
||||
`channel_request_no` varchar(64) NOT NULL DEFAULT '' COMMENT '渠道请求号(幂等)',
|
||||
`channel_order_no` varchar(64) DEFAULT NULL COMMENT '渠道订单号',
|
||||
`channel_trade_no` varchar(64) DEFAULT NULL COMMENT '渠道交易号',
|
||||
`channel_error_code` varchar(64) NOT NULL DEFAULT '' COMMENT '渠道错误码',
|
||||
`channel_error_msg` varchar(255) NOT NULL DEFAULT '' COMMENT '渠道错误信息',
|
||||
`request_at` datetime DEFAULT NULL COMMENT '请求时间',
|
||||
`paid_at` datetime DEFAULT NULL COMMENT '支付成功时间',
|
||||
`expire_at` datetime DEFAULT NULL COMMENT '过期时间',
|
||||
`closed_at` datetime DEFAULT NULL COMMENT '关闭时间',
|
||||
`failed_at` datetime DEFAULT NULL COMMENT '失败时间',
|
||||
`timeout_at` datetime DEFAULT NULL COMMENT '超时时间',
|
||||
`callback_status` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '回调处理状态:0-未回调,1-成功,2-失败',
|
||||
`callback_times` int unsigned NOT NULL DEFAULT 0 COMMENT '回调次数',
|
||||
`ext_json` json DEFAULT NULL COMMENT '商户扩展字段(JSON)',
|
||||
`created_at` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`updated_at` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_pay_no` (`pay_no`),
|
||||
UNIQUE KEY `uk_biz_attempt` (`biz_no`, `attempt_no`),
|
||||
UNIQUE KEY `uk_channel_request_no` (`merchant_id`, `channel_request_no`),
|
||||
KEY `idx_trace_no` (`trace_no`),
|
||||
KEY `idx_merchant_status_created` (`merchant_id`, `status`, `created_at`),
|
||||
KEY `idx_channel_trade_no` (`channel_trade_no`),
|
||||
KEY `idx_channel_id_status` (`channel_id`, `status`),
|
||||
KEY `idx_poll_group_id` (`poll_group_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='支付单表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `ma_refund_order` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`refund_no` varchar(32) NOT NULL DEFAULT '' COMMENT '退款单号',
|
||||
`merchant_id` bigint unsigned NOT NULL DEFAULT 0 COMMENT '商户ID',
|
||||
`merchant_group_id` int unsigned NOT NULL DEFAULT 0 COMMENT '商户分组ID快照',
|
||||
`biz_no` varchar(32) NOT NULL DEFAULT '' COMMENT '业务订单号',
|
||||
`trace_no` varchar(32) NOT NULL DEFAULT '' COMMENT '统一追踪号',
|
||||
`pay_no` varchar(32) NOT NULL DEFAULT '' COMMENT '支付单号',
|
||||
`merchant_refund_no` varchar(64) NOT NULL DEFAULT '' COMMENT '商户退款号(幂等)',
|
||||
`channel_id` bigint unsigned NOT NULL DEFAULT 0 COMMENT '支付通道ID',
|
||||
`refund_amount` bigint unsigned NOT NULL DEFAULT 0 COMMENT '退款金额(分)',
|
||||
`fee_reverse_amount` bigint unsigned NOT NULL DEFAULT 0 COMMENT '手续费冲回金额(分)',
|
||||
`status` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '状态:0-待创建,1-处理中,2-成功,3-失败,4-关闭',
|
||||
`channel_request_no` varchar(64) NOT NULL DEFAULT '' COMMENT '渠道退款请求号(幂等)',
|
||||
`channel_refund_no` varchar(64) DEFAULT NULL COMMENT '渠道退款号',
|
||||
`reason` varchar(255) NOT NULL DEFAULT '' COMMENT '退款原因',
|
||||
`request_at` datetime DEFAULT NULL COMMENT '申请时间',
|
||||
`processing_at` datetime DEFAULT NULL COMMENT '处理时间',
|
||||
`succeeded_at` datetime DEFAULT NULL COMMENT '成功时间',
|
||||
`failed_at` datetime DEFAULT NULL COMMENT '失败时间',
|
||||
`retry_count` int unsigned NOT NULL DEFAULT 0 COMMENT '重试次数',
|
||||
`last_error` varchar(255) NOT NULL DEFAULT '' COMMENT '最近错误信息',
|
||||
`ext_json` json DEFAULT NULL COMMENT '商户扩展字段(JSON)',
|
||||
`created_at` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`updated_at` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_refund_no` (`refund_no`),
|
||||
UNIQUE KEY `uk_merchant_refund_no` (`merchant_id`, `merchant_refund_no`),
|
||||
UNIQUE KEY `uk_channel_request_no` (`merchant_id`, `channel_request_no`),
|
||||
KEY `idx_trace_no` (`trace_no`),
|
||||
KEY `idx_biz_status_created` (`biz_no`, `status`, `created_at`),
|
||||
KEY `idx_merchant_status_created` (`merchant_id`, `status`, `created_at`),
|
||||
KEY `idx_pay_no` (`pay_no`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='退款单表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `ma_transfer_order` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`biz_no` varchar(32) NOT NULL DEFAULT '' COMMENT '业务号',
|
||||
`trace_no` varchar(32) NOT NULL DEFAULT '' COMMENT '统一追踪号',
|
||||
`merchant_id` bigint unsigned NOT NULL DEFAULT 0 COMMENT '商户ID',
|
||||
`merchant_group_id` int unsigned NOT NULL DEFAULT 0 COMMENT '商户分组ID快照',
|
||||
`out_biz_no` varchar(64) NOT NULL DEFAULT '' COMMENT '商户转账单号',
|
||||
`type` varchar(32) NOT NULL DEFAULT '' COMMENT '转账类型',
|
||||
`account` varchar(100) NOT NULL DEFAULT '' COMMENT '收款账号',
|
||||
`name` varchar(100) NOT NULL DEFAULT '' COMMENT '收款姓名',
|
||||
`amount` bigint unsigned NOT NULL DEFAULT 0 COMMENT '转账金额(分)',
|
||||
`cost_amount` bigint unsigned NOT NULL DEFAULT 0 COMMENT '手续费(分)',
|
||||
`remark` varchar(255) NOT NULL DEFAULT '' COMMENT '备注',
|
||||
`bookid` varchar(64) NOT NULL DEFAULT '' COMMENT '书签ID',
|
||||
`channel_id` bigint unsigned NOT NULL DEFAULT 0 COMMENT '通道ID',
|
||||
`channel_request_no` varchar(64) NOT NULL DEFAULT '' COMMENT '渠道请求号',
|
||||
`channel_order_no` varchar(64) DEFAULT NULL COMMENT '渠道订单号',
|
||||
`channel_trade_no` varchar(64) DEFAULT NULL COMMENT '渠道交易号',
|
||||
`channel_error_code` varchar(64) NOT NULL DEFAULT '' COMMENT '渠道错误码',
|
||||
`channel_error_msg` varchar(255) NOT NULL DEFAULT '' COMMENT '渠道错误信息',
|
||||
`status` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '状态:0-待处理,1-成功,2-失败',
|
||||
`request_at` datetime DEFAULT NULL COMMENT '请求时间',
|
||||
`processing_at` datetime DEFAULT NULL COMMENT '处理中时间',
|
||||
`succeeded_at` datetime DEFAULT NULL COMMENT '成功时间',
|
||||
`failed_at` datetime DEFAULT NULL COMMENT '失败时间',
|
||||
`ext_json` json DEFAULT NULL COMMENT '商户扩展字段(JSON)',
|
||||
`created_at` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`updated_at` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_biz_no` (`biz_no`),
|
||||
UNIQUE KEY `uk_merchant_channel_request_no` (`merchant_id`, `channel_request_no`),
|
||||
UNIQUE KEY `uk_merchant_out_biz_no` (`merchant_id`, `out_biz_no`),
|
||||
KEY `idx_trace_no` (`trace_no`),
|
||||
KEY `idx_merchant_status_created` (`merchant_id`, `status`, `created_at`),
|
||||
KEY `idx_channel_trade_no` (`channel_trade_no`),
|
||||
KEY `idx_channel_id_status` (`channel_id`, `status`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='转账单表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `ma_merchant_account` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`merchant_id` bigint unsigned NOT NULL DEFAULT 0 COMMENT '商户ID(唯一)',
|
||||
`available_balance` bigint unsigned NOT NULL DEFAULT 0 COMMENT '可提现余额(分)',
|
||||
`frozen_balance` bigint unsigned NOT NULL DEFAULT 0 COMMENT '冻结余额(分)',
|
||||
`created_at` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`updated_at` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_merchant_id` (`merchant_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商户余额账户表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `ma_merchant_account_ledger` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`ledger_no` varchar(32) NOT NULL DEFAULT '' COMMENT '流水号',
|
||||
`merchant_id` bigint unsigned NOT NULL DEFAULT 0 COMMENT '商户ID',
|
||||
`biz_type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '业务类型:0-支付冻结,1-支付扣费,2-支付释放,3-清算入账,4-退款冲正,5-人工调整',
|
||||
`biz_no` varchar(32) NOT NULL DEFAULT '' COMMENT '业务单号',
|
||||
`trace_no` varchar(32) NOT NULL DEFAULT '' COMMENT '统一追踪号',
|
||||
`event_type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '事件类型:0-创建,1-成功,2-失败,3-冲正',
|
||||
`direction` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '方向:0-入账,1-出账',
|
||||
`amount` bigint unsigned NOT NULL DEFAULT 0 COMMENT '变动金额(分)',
|
||||
`available_before` bigint unsigned NOT NULL DEFAULT 0 COMMENT '变动前可用余额(分)',
|
||||
`available_after` bigint unsigned NOT NULL DEFAULT 0 COMMENT '变动后可用余额(分)',
|
||||
`frozen_before` bigint unsigned NOT NULL DEFAULT 0 COMMENT '变动前冻结余额(分)',
|
||||
`frozen_after` bigint unsigned NOT NULL DEFAULT 0 COMMENT '变动后冻结余额(分)',
|
||||
`idempotency_key` varchar(100) NOT NULL DEFAULT '' COMMENT '幂等键',
|
||||
`remark` varchar(255) NOT NULL DEFAULT '' COMMENT '备注',
|
||||
`ext_json` json DEFAULT NULL COMMENT '账务扩展字段(JSON)',
|
||||
`created_at` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_idempotency_key` (`idempotency_key`),
|
||||
KEY `idx_trace_no` (`trace_no`),
|
||||
KEY `idx_merchant_biz` (`merchant_id`, `biz_type`, `biz_no`),
|
||||
KEY `idx_merchant_created` (`merchant_id`, `created_at`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商户余额流水表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `ma_settlement_order` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`settle_no` varchar(32) NOT NULL DEFAULT '' COMMENT '清算单号',
|
||||
`trace_no` varchar(32) NOT NULL DEFAULT '' COMMENT '统一追踪号',
|
||||
`merchant_id` bigint unsigned NOT NULL DEFAULT 0 COMMENT '商户ID',
|
||||
`merchant_group_id` int unsigned NOT NULL DEFAULT 0 COMMENT '商户分组ID快照',
|
||||
`channel_id` bigint unsigned NOT NULL DEFAULT 0 COMMENT '支付通道ID',
|
||||
`cycle_type` tinyint unsigned NOT NULL DEFAULT 1 COMMENT '周期类型:0-D0,1-D1,2-D7,3-T1,4-OTHER',
|
||||
`cycle_key` varchar(32) NOT NULL DEFAULT '' COMMENT '周期标识,如日期或批次号',
|
||||
`status` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '状态:0-待处理,1-处理中,2-成功,3-失败,4-已冲正',
|
||||
`gross_amount` bigint unsigned NOT NULL DEFAULT 0 COMMENT '清算前总额(分)',
|
||||
`fee_amount` bigint unsigned NOT NULL DEFAULT 0 COMMENT '手续费总额(分)',
|
||||
`refund_amount` bigint unsigned NOT NULL DEFAULT 0 COMMENT '退款本金(分)',
|
||||
`fee_reverse_amount` bigint unsigned NOT NULL DEFAULT 0 COMMENT '手续费冲回金额(分)',
|
||||
`net_amount` bigint unsigned NOT NULL DEFAULT 0 COMMENT '净额(分)',
|
||||
`accounted_amount` bigint unsigned NOT NULL DEFAULT 0 COMMENT '实际入账金额(分)',
|
||||
`generated_at` datetime DEFAULT NULL COMMENT '生成时间',
|
||||
`accounted_at` datetime DEFAULT NULL COMMENT '入账时间',
|
||||
`completed_at` datetime DEFAULT NULL COMMENT '完成时间',
|
||||
`failed_at` datetime DEFAULT NULL COMMENT '失败时间',
|
||||
`fail_reason` varchar(255) NOT NULL DEFAULT '' COMMENT '失败原因',
|
||||
`ext_json` json DEFAULT NULL COMMENT '清算扩展字段(JSON)',
|
||||
`created_at` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`updated_at` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_cycle` (`merchant_id`, `channel_id`, `cycle_type`, `cycle_key`),
|
||||
UNIQUE KEY `uk_settle_no` (`settle_no`),
|
||||
KEY `idx_trace_no` (`trace_no`),
|
||||
KEY `idx_merchant_status_created` (`merchant_id`, `status`, `created_at`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='清算单表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `ma_settlement_item` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`settle_no` varchar(32) NOT NULL DEFAULT '' COMMENT '清算单号',
|
||||
`merchant_id` bigint unsigned NOT NULL DEFAULT 0 COMMENT '商户ID',
|
||||
`merchant_group_id` int unsigned NOT NULL DEFAULT 0 COMMENT '商户分组ID快照',
|
||||
`channel_id` bigint unsigned NOT NULL DEFAULT 0 COMMENT '支付通道ID',
|
||||
`pay_no` varchar(32) NOT NULL DEFAULT '' COMMENT '支付单号',
|
||||
`refund_no` varchar(32) DEFAULT NULL COMMENT '退款单号',
|
||||
`pay_amount` bigint unsigned NOT NULL DEFAULT 0 COMMENT '支付金额(分)',
|
||||
`fee_amount` bigint unsigned NOT NULL DEFAULT 0 COMMENT '手续费金额(分)',
|
||||
`refund_amount` bigint unsigned NOT NULL DEFAULT 0 COMMENT '退款本金(分)',
|
||||
`fee_reverse_amount` bigint unsigned NOT NULL DEFAULT 0 COMMENT '手续费冲回金额(分)',
|
||||
`net_amount` bigint unsigned NOT NULL DEFAULT 0 COMMENT '净额(分)',
|
||||
`item_status` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '明细状态:0-待处理,1-成功,2-失败',
|
||||
`created_at` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`updated_at` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_settle_pay` (`settle_no`, `pay_no`),
|
||||
KEY `idx_pay_no` (`pay_no`),
|
||||
KEY `idx_channel_id` (`channel_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='清算明细表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `ma_channel_notify_log` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`notify_no` varchar(32) NOT NULL DEFAULT '' COMMENT '通知日志号',
|
||||
`channel_id` bigint unsigned NOT NULL DEFAULT 0 COMMENT '通道ID',
|
||||
`notify_type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '通知类型:0-异步通知,1-查单',
|
||||
`biz_no` varchar(32) NOT NULL DEFAULT '' COMMENT '业务单号',
|
||||
`pay_no` varchar(32) DEFAULT NULL COMMENT '支付单号',
|
||||
`channel_request_no` varchar(64) DEFAULT NULL COMMENT '渠道请求号',
|
||||
`channel_trade_no` varchar(64) DEFAULT NULL COMMENT '渠道交易号',
|
||||
`raw_payload` longtext COMMENT '原始报文',
|
||||
`verify_status` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '验签状态:0-未知,1-成功,2-失败',
|
||||
`process_status` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '处理状态:0-待处理,1-成功,2-失败',
|
||||
`retry_count` int unsigned NOT NULL DEFAULT 0 COMMENT '重试次数',
|
||||
`next_retry_at` datetime DEFAULT NULL COMMENT '下次重试时间',
|
||||
`last_error` varchar(255) NOT NULL DEFAULT '' COMMENT '最近错误信息',
|
||||
`created_at` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`updated_at` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_notify_no` (`notify_no`),
|
||||
UNIQUE KEY `uk_channel_notify` (`channel_id`, `notify_type`, `biz_no`),
|
||||
KEY `idx_pay_status` (`pay_no`, `process_status`),
|
||||
KEY `idx_trade_no` (`channel_trade_no`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='通道通知日志表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `ma_pay_callback_log` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`pay_no` varchar(32) NOT NULL DEFAULT '' COMMENT '支付单号',
|
||||
`channel_id` bigint unsigned NOT NULL DEFAULT 0 COMMENT '通道ID',
|
||||
`callback_type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '回调类型:0-异步通知,1-同步返回',
|
||||
`request_data` longtext COMMENT '请求原始数据(完整回调参数)',
|
||||
`verify_status` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '验签状态:0-未知,1-成功,2-失败',
|
||||
`process_status` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '处理状态:0-待处理,1-成功,2-失败',
|
||||
`process_result` longtext COMMENT '处理结果(JSON或文本)',
|
||||
`created_at` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_pay_created` (`pay_no`, `created_at`),
|
||||
KEY `idx_channel_created` (`channel_id`, `created_at`),
|
||||
KEY `idx_callback_type` (`callback_type`),
|
||||
KEY `idx_verify_status` (`verify_status`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='支付回调日志表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `ma_channel_daily_stat` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`merchant_id` bigint unsigned NOT NULL DEFAULT 0 COMMENT '商户ID',
|
||||
`merchant_group_id` int unsigned NOT NULL DEFAULT 0 COMMENT '商户分组ID快照',
|
||||
`channel_id` bigint unsigned NOT NULL DEFAULT 0 COMMENT '支付通道ID',
|
||||
`stat_date` date NOT NULL COMMENT '统计日期',
|
||||
`pay_success_count` int unsigned NOT NULL DEFAULT 0 COMMENT '支付成功笔数',
|
||||
`pay_fail_count` int unsigned NOT NULL DEFAULT 0 COMMENT '支付失败笔数',
|
||||
`pay_amount` bigint unsigned NOT NULL DEFAULT 0 COMMENT '支付成功金额(分)',
|
||||
`refund_count` int unsigned NOT NULL DEFAULT 0 COMMENT '退款笔数',
|
||||
`refund_amount` bigint unsigned NOT NULL DEFAULT 0 COMMENT '退款金额(分)',
|
||||
`avg_latency_ms` int unsigned NOT NULL DEFAULT 0 COMMENT '平均耗时(毫秒)',
|
||||
`success_rate_bp` int unsigned NOT NULL DEFAULT 0 COMMENT '成功率(bp)',
|
||||
`health_score` int NOT NULL DEFAULT 0 COMMENT '健康度评分',
|
||||
`created_at` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`updated_at` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_channel_stat_date` (`channel_id`, `stat_date`),
|
||||
KEY `idx_merchant_date` (`merchant_id`, `stat_date`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='通道日统计表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `ma_notify_task` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`notify_no` varchar(32) NOT NULL DEFAULT '' COMMENT '通知任务号',
|
||||
`merchant_id` bigint unsigned NOT NULL DEFAULT 0 COMMENT '商户ID',
|
||||
`merchant_group_id` int unsigned NOT NULL DEFAULT 0 COMMENT '商户分组ID快照',
|
||||
`biz_no` varchar(32) NOT NULL DEFAULT '' COMMENT '业务单号',
|
||||
`pay_no` varchar(32) NOT NULL DEFAULT '' COMMENT '支付单号',
|
||||
`notify_url` varchar(255) NOT NULL DEFAULT '' COMMENT '通知地址',
|
||||
`notify_data` longtext COMMENT '通知内容',
|
||||
`status` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '状态:0-待通知,1-成功,2-失败',
|
||||
`retry_count` int unsigned NOT NULL DEFAULT 0 COMMENT '重试次数',
|
||||
`next_retry_at` datetime DEFAULT NULL COMMENT '下次重试时间',
|
||||
`last_notify_at` datetime DEFAULT NULL COMMENT '最后通知时间',
|
||||
`last_response` longtext COMMENT '最后响应内容',
|
||||
`created_at` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`updated_at` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_notify_no` (`notify_no`),
|
||||
UNIQUE KEY `uk_pay_no` (`pay_no`),
|
||||
KEY `idx_status_retry` (`status`, `next_retry_at`),
|
||||
KEY `idx_merchant_group` (`merchant_id`, `merchant_group_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商户通知任务表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `ma_system_config` (
|
||||
`config_key` varchar(100) NOT NULL COMMENT '配置项键名',
|
||||
`config_value` longtext COMMENT '配置项值',
|
||||
`group_code` varchar(50) NOT NULL DEFAULT '' COMMENT '配置分组',
|
||||
`created_at` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`updated_at` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`config_key`),
|
||||
KEY `idx_group_code` (`group_code`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统配置表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `ma_file_asset` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '文件ID',
|
||||
`scene` tinyint unsigned NOT NULL DEFAULT 4 COMMENT '文件场景:1-图片,2-证书,3-文本,4-其他',
|
||||
`source_type` tinyint unsigned NOT NULL DEFAULT 1 COMMENT '来源类型:1-上传,2-远程导入',
|
||||
`visibility` tinyint unsigned NOT NULL DEFAULT 2 COMMENT '可见性:1-公开,2-私有',
|
||||
`storage_engine` tinyint unsigned NOT NULL DEFAULT 1 COMMENT '存储引擎:1-本地,2-阿里云OSS,3-腾讯云COS,4-远程引用',
|
||||
`original_name` varchar(255) NOT NULL DEFAULT '' COMMENT '原始文件名',
|
||||
`file_name` varchar(255) NOT NULL DEFAULT '' COMMENT '存储文件名',
|
||||
`file_ext` varchar(32) NOT NULL DEFAULT '' COMMENT '文件扩展名',
|
||||
`mime_type` varchar(128) NOT NULL DEFAULT '' COMMENT '文件 MIME 类型',
|
||||
`size` bigint unsigned NOT NULL DEFAULT 0 COMMENT '文件大小,单位字节',
|
||||
`md5` char(32) NOT NULL DEFAULT '' COMMENT '文件 MD5',
|
||||
`object_key` varchar(512) NOT NULL DEFAULT '' COMMENT '对象存储键或站点相对路径,如 storage/uploads/...',
|
||||
`url` varchar(1024) NOT NULL DEFAULT '' COMMENT '默认访问地址',
|
||||
`source_url` varchar(1024) NOT NULL DEFAULT '' COMMENT '远程来源地址',
|
||||
`created_by` bigint unsigned NOT NULL DEFAULT 0 COMMENT '创建人ID',
|
||||
`created_by_name` varchar(50) NOT NULL DEFAULT '' COMMENT '创建人名称',
|
||||
`remark` varchar(255) NOT NULL DEFAULT '' COMMENT '备注',
|
||||
`created_at` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`updated_at` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_object_key` (`object_key`),
|
||||
KEY `idx_scene` (`scene`),
|
||||
KEY `idx_source_type` (`source_type`),
|
||||
KEY `idx_visibility` (`visibility`),
|
||||
KEY `idx_storage_engine` (`storage_engine`),
|
||||
KEY `idx_created_by` (`created_by`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文件表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `ma_admin_user` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '管理员ID',
|
||||
`username` varchar(32) NOT NULL DEFAULT '' COMMENT '登录用户名',
|
||||
`password_hash` varchar(255) NOT NULL DEFAULT '' COMMENT '密码哈希',
|
||||
`real_name` varchar(50) NOT NULL DEFAULT '' COMMENT '真实姓名',
|
||||
`mobile` varchar(20) NOT NULL DEFAULT '' COMMENT '手机号',
|
||||
`email` varchar(100) NOT NULL DEFAULT '' COMMENT '邮箱',
|
||||
`is_super` tinyint NOT NULL DEFAULT 0 COMMENT '是否超级管理员',
|
||||
`status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:0-禁用,1-启用',
|
||||
`last_login_at` datetime DEFAULT NULL COMMENT '最后登录时间',
|
||||
`last_login_ip` varchar(45) NOT NULL DEFAULT '' COMMENT '最后登录IP',
|
||||
`remark` varchar(500) NOT NULL DEFAULT '' COMMENT '备注',
|
||||
`created_at` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`updated_at` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_username` (`username`),
|
||||
KEY `idx_status` (`status`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='管理员用户表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `ma_merchant_api_credential` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`merchant_id` bigint unsigned NOT NULL DEFAULT 0 COMMENT '商户ID',
|
||||
`api_key` varchar(128) NOT NULL DEFAULT '' COMMENT '商户 API 凭证值(V1 MD5 签名使用)',
|
||||
`merchant_public_key` text DEFAULT NULL COMMENT '商户 RSA 公钥(V2 验签使用)',
|
||||
`sign_type` tinyint NOT NULL DEFAULT 0 COMMENT '签名类型:0-MD5(V1)',
|
||||
`status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:0-禁用,1-启用',
|
||||
`last_used_at` datetime DEFAULT NULL COMMENT '最后使用时间',
|
||||
`created_at` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`updated_at` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_merchant_id` (`merchant_id`),
|
||||
KEY `idx_status` (`status`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商户API凭证表';
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
||||
73
doc/db/tables.md
Normal file
73
doc/db/tables.md
Normal file
@@ -0,0 +1,73 @@
|
||||
# 数据表目录
|
||||
|
||||
这份目录页只负责“表去哪看、表大概干什么”,详细结构仍以 [`payment-middle-ddl.sql`](./payment-middle-ddl.sql) 为准。
|
||||
|
||||
## 基础字典
|
||||
|
||||
| 表名 | 用途 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `ma_payment_type` | 支付方式字典 | 维护支付方式编码、名称、图标和启用状态 |
|
||||
| `ma_system_config` | 系统配置表 | 维护全局配置键值 |
|
||||
|
||||
## 商户主体
|
||||
|
||||
| 表名 | 用途 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `ma_merchant_group` | 商户分组表 | 作为路由绑定和商户归类的输入条件 |
|
||||
| `ma_merchant` | 商户表 | 商户主体资料,也是后台登录主体 |
|
||||
| `ma_merchant_api_credential` | 商户 API 凭证表 | 开放接口签名凭证,V1 使用 MD5 key,V2 保存 RSA 公钥,与后台登录分离 |
|
||||
| `ma_merchant_policy` | 商户策略预留表 | 预留的商户策略结构 |
|
||||
|
||||
## 支付编排
|
||||
|
||||
| 表名 | 用途 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `ma_payment_plugin` | 支付插件注册表 | 扫描和注册支付插件定义 |
|
||||
| `ma_payment_plugin_conf` | 支付插件 API 配置表 | 插件初始化配置和结算周期配置 |
|
||||
| `ma_payment_channel` | 支付通道表 | 维护平台通道和商户自有通道 |
|
||||
| `ma_payment_poll_group` | 支付轮询组表 | 承载轮询策略和候选通道编排 |
|
||||
| `ma_payment_poll_group_channel` | 支付轮询组-通道编排表 | 轮询组内的通道顺序和权重配置 |
|
||||
| `ma_payment_poll_group_bind` | 商户分组-轮询组绑定表 | 商户分组与轮询组的映射关系 |
|
||||
|
||||
## 交易订单
|
||||
|
||||
| 表名 | 用途 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `ma_biz_order` | 业务订单表 | 统一业务订单入口,只承载业务事实、收银台恢复所需信息(subject/body/notify_url/return_url/client_ip/device)和业务扩展参数 |
|
||||
| `ma_pay_order` | 支付单表 | 记录支付发起、状态推进和回调信息,扩展字段只留商户附加参数 |
|
||||
| `ma_refund_order` | 退款单表 | 记录退款发起、状态推进和结果,扩展字段只留商户附加参数 |
|
||||
| `ma_transfer_order` | 转账单表 | 记录转账发起、状态推进和渠道结果,扩展字段只留商户附加参数 |
|
||||
| `ma_settlement_order` | 清算单表 | 记录清算批次和清算状态,扩展字段只留清算附加信息 |
|
||||
| `ma_settlement_item` | 清算明细表 | 记录清算单内的明细拆分 |
|
||||
|
||||
## 资金账户
|
||||
|
||||
| 表名 | 用途 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `ma_merchant_account` | 商户余额账户表 | 记录商户余额、冻结、可用等账户信息 |
|
||||
| `ma_merchant_account_ledger` | 商户余额流水表 | 记录账户变更流水和账务明细 |
|
||||
|
||||
## 运维日志
|
||||
|
||||
| 表名 | 用途 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `ma_channel_notify_log` | 通道通知日志表 | 记录通道侧通知、重试和失败原因 |
|
||||
| `ma_pay_callback_log` | 支付回调日志表 | 记录支付回调处理和幂等结果 |
|
||||
| `ma_channel_daily_stat` | 通道日统计表 | 记录通道成功率、耗时和健康度数据 |
|
||||
| `ma_notify_task` | 商户通知任务表 | 记录商户异步通知任务和重试情况 |
|
||||
|
||||
## 文件与后台
|
||||
|
||||
| 表名 | 用途 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `ma_file_asset` | 文件表 | 记录上传文件、预览、下载和存储位置 |
|
||||
| `ma_admin_user` | 管理员用户表 | 记录后台管理员账号信息 |
|
||||
|
||||
## 使用建议
|
||||
|
||||
- 先看 `tables.md`,再看 DDL
|
||||
- 如果字段定义变了,以 DDL 为准
|
||||
- 如果表用途变了,先补 DDL 注释,再补这里的目录说明
|
||||
- `ext_json` 使用分区结构保存轻量运行上下文:顶层可放 `_protocol_version`,商户透传放 `merchant`,支付载体放 `payment`,收银台承接放 `presentation`
|
||||
- 回调、通知、重试和原始报文使用专门日志/任务表,不进入订单扩展字段
|
||||
- 表级说明后面可以按业务域继续拆成独立文档
|
||||
26
doc/deployment/README.md
Normal file
26
doc/deployment/README.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# 部署总说明
|
||||
|
||||
四个项目独立启动、构建和发布。后端负责 API 与页面兜底路由,前端负责生成静态产物。
|
||||
|
||||
## 命令速览
|
||||
|
||||
| 项目 | 开发/启动 | 构建 |
|
||||
| --- | --- | --- |
|
||||
| `mpay` | `php webman start` 或 `php windows.php` | 无前端构建 |
|
||||
| `admin` | `pnpm dev` | `pnpm build:prod` |
|
||||
| `mer` | `pnpm dev` | `pnpm build:prod` |
|
||||
| `cashier` | `pnpm dev` | `pnpm build` |
|
||||
|
||||
## 默认端口与路径
|
||||
|
||||
- 后端默认监听 `http://127.0.0.1:8787`。
|
||||
- 管理后台页面入口是 `/admin`,接口是 `/adminapi`。
|
||||
- 商户后台页面入口是 `/mer`,接口是 `/merapi`。
|
||||
- 收银台页面入口是 `/cashier`、`/payment`,接口是 `/api/cashier` 和 `/api/pay`。
|
||||
|
||||
## 部署建议
|
||||
|
||||
- 后端生产环境使用守护进程或进程管理工具托管。
|
||||
- `admin`、`mer`、`cashier` 的 `dist/` 可独立托管,也可发布到 `mpay/public` 下对应目录。
|
||||
- 如果前后端同域部署,确保 `/adminapi`、`/merapi`、`/api`、ePay V1 兼容入口都能转发到 `mpay`。
|
||||
- 环境变量说明见 [env.md](./env.md)。
|
||||
18
doc/deployment/admin.md
Normal file
18
doc/deployment/admin.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# 管理后台部署
|
||||
|
||||
命令默认在 `admin/` 目录执行。
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
pnpm build:prod
|
||||
```
|
||||
|
||||
产物在 `dist/`。
|
||||
|
||||
## 路径
|
||||
|
||||
- 开发公共路径:`/admin`
|
||||
- 生产默认公共路径:`/`
|
||||
- 生产接口基址:`VITE_APP_BASE_URL=/`,运行时请求 `/adminapi`
|
||||
|
||||
部署到子路径时,同步调整 `VITE_PUBLIC_PATH` 和网关重写规则。
|
||||
31
doc/deployment/backend.md
Normal file
31
doc/deployment/backend.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# 后端部署
|
||||
|
||||
命令默认在 `mpay/` 目录执行。
|
||||
|
||||
## 前置条件
|
||||
|
||||
- PHP 8.1+
|
||||
- Composer
|
||||
- MySQL
|
||||
- Redis
|
||||
|
||||
## 启动
|
||||
|
||||
```bash
|
||||
composer install
|
||||
Copy-Item .env.example .env
|
||||
php webman start
|
||||
```
|
||||
|
||||
Windows 开发环境需要同时启动自定义进程时:
|
||||
|
||||
```bash
|
||||
php windows.php
|
||||
```
|
||||
|
||||
## 生产要点
|
||||
|
||||
- 配置数据库、Redis、JWT 密钥和支付运行时心跳。
|
||||
- 使用进程管理工具托管 `webman` 和 `payment-runtime`。
|
||||
- 执行 `php webman system:config-sync` 同步系统配置默认值。
|
||||
- 如使用 OSS/COS,先在系统配置中补齐存储参数。
|
||||
19
doc/deployment/cashier.md
Normal file
19
doc/deployment/cashier.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# 收银台部署
|
||||
|
||||
命令默认在 `cashier/` 目录执行。
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
pnpm build
|
||||
```
|
||||
|
||||
产物在 `dist/`。
|
||||
|
||||
## 路径
|
||||
|
||||
- 页面入口:`/cashier`、`/payment`
|
||||
- 收银台 JSON API:`/api/cashier`
|
||||
- ePay V2 / 开放支付 API:`/api/pay`、`/api/merchant`、`/api/transfer`
|
||||
- ePay V1 兼容入口:`/submit.php`、`/mapi.php`、`/api.php`
|
||||
|
||||
如果前端不走同域代理,配置 `VITE_API_BASE_URL` 指向后端;否则保持为空,通过相对路径访问 `/api`。
|
||||
65
doc/deployment/env.md
Normal file
65
doc/deployment/env.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# 环境变量
|
||||
|
||||
本文只列当前代码读取或模板中声明的关键变量,不记录敏感值。
|
||||
|
||||
## 后端 `mpay`
|
||||
|
||||
环境文件:
|
||||
|
||||
- `.env`
|
||||
- `.env.example`
|
||||
|
||||
关键变量:
|
||||
|
||||
| 变量 | 用途 |
|
||||
| --- | --- |
|
||||
| `DB_HOST`、`DB_PORT`、`DB_DATABASE`、`DB_USERNAME`、`DB_PASSWORD` | MySQL 连接 |
|
||||
| `REDIS_HOST`、`REDIS_PORT`、`REDIS_PASSWORD`、`REDIS_DATABASE` | Redis 连接 |
|
||||
| `CACHE_DRIVER` | 缓存驱动 |
|
||||
| `AUTH_JWT_ISSUER`、`AUTH_JWT_LEEWAY` | JWT 通用参数 |
|
||||
| `AUTH_ADMIN_JWT_SECRET`、`AUTH_ADMIN_JWT_TTL`、`AUTH_ADMIN_JWT_REDIS_PREFIX` | 管理后台登录 token |
|
||||
| `AUTH_MERCHANT_JWT_SECRET`、`AUTH_MERCHANT_JWT_TTL`、`AUTH_MERCHANT_JWT_REDIS_PREFIX` | 商户后台登录 token |
|
||||
| `PAY_RUNTIME_HEARTBEAT_SECONDS` | 支付运行时进程心跳间隔 |
|
||||
|
||||
系统配置、站点 URL、支付运行时开关和存储参数主要由 `config/system_config.php` 定义,并可同步到数据库。
|
||||
|
||||
## 管理后台 `admin`
|
||||
|
||||
环境文件:
|
||||
|
||||
- `.env`
|
||||
- `.env.development`
|
||||
- `.env.production`
|
||||
- `.env.test`
|
||||
|
||||
关键变量:
|
||||
|
||||
| 变量 | 用途 |
|
||||
| --- | --- |
|
||||
| `VITE_USER_NODE_ENV` | 环境标识 |
|
||||
| `VITE_ROUTER_MODE` | 路由模式,开发默认为 `history`,生产默认为 `hash` |
|
||||
| `VITE_PUBLIC_PATH` | 构建公共路径,开发默认为 `/admin` |
|
||||
| `VITE_APP_BASE_URL` | 后端基址,前端会拼接 `/adminapi` |
|
||||
| `VITE_APP_OPEN_MOCK` | 是否使用本地 mock,代码中有读取 |
|
||||
|
||||
## 商户后台 `mer`
|
||||
|
||||
环境文件和变量与 `admin` 基本一致;前端会在 `VITE_APP_BASE_URL` 后拼接 `/merapi`。
|
||||
|
||||
## 收银台 `cashier`
|
||||
|
||||
环境文件:
|
||||
|
||||
- `.env`
|
||||
- `.env.example`
|
||||
|
||||
关键变量:
|
||||
|
||||
| 变量 | 用途 |
|
||||
| --- | --- |
|
||||
| `VITE_USE_MOCK` | 是否启用 mock 演示 |
|
||||
| `VITE_API_PREFIX` | API 路径前缀,默认 `api` |
|
||||
| `VITE_API_PROXY_TARGET` | Vite 开发代理目标,默认 `http://127.0.0.1:8787` |
|
||||
| `VITE_API_BASE_URL` | 直连后端 API 基址,留空则走相对路径/开发代理 |
|
||||
|
||||
注意:`cashier/.env.example` 中仍有 `VITE_ROUTE_PREFIX`,但当前 `cashier/src/config/index.ts` 未读取它;页面路由前缀实际固定为 `/cashier` 和 `/payment`。
|
||||
18
doc/deployment/mer.md
Normal file
18
doc/deployment/mer.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# 商户后台部署
|
||||
|
||||
命令默认在 `mer/` 目录执行。
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
pnpm build:prod
|
||||
```
|
||||
|
||||
产物在 `dist/`。
|
||||
|
||||
## 路径
|
||||
|
||||
- 开发公共路径:`/mer`
|
||||
- 生产默认公共路径:`/`
|
||||
- 生产接口基址:`VITE_APP_BASE_URL=/`,运行时请求 `/merapi`
|
||||
|
||||
部署到子路径时,同步调整 `VITE_PUBLIC_PATH` 和网关重写规则。
|
||||
25
doc/faq.md
Normal file
25
doc/faq.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# 常见问题
|
||||
|
||||
## 根目录为什么不是 Git 仓库?
|
||||
|
||||
根目录只是工作区容器。`admin`、`mer`、`cashier`、`mpay` 各自是独立 Git 仓库,建议打开 `MPAY_V2.code-workspace` 查看。
|
||||
|
||||
## 最新文档看哪里?
|
||||
|
||||
看 `docs/`。`mpay/doc/` 是旧资料归档,不作为最新事实源。
|
||||
|
||||
## 文档和代码冲突怎么办?
|
||||
|
||||
以当前代码、路由、前端 API 封装、环境模板和 DDL 为准,然后修正文档。
|
||||
|
||||
## 收银台入口到底是什么?
|
||||
|
||||
页面入口是 `/cashier` 和 `/payment`;收银台 JSON API 是 `/api/cashier/*`;开放支付 API 是 `/api/pay/*`。
|
||||
|
||||
## 后端默认端口是什么?
|
||||
|
||||
`config/process.php` 中 HTTP 服务默认监听 `0.0.0.0:8787`。
|
||||
|
||||
## 新文档写到哪里?
|
||||
|
||||
跨项目说明写到 `docs/`;单个项目的启动、构建、联调说明写到对应项目的 `README.md`。
|
||||
32
doc/frontend/README.md
Normal file
32
doc/frontend/README.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# 前端总说明
|
||||
|
||||
当前有三套独立前端,均可单独安装、启动、构建和发布。
|
||||
|
||||
| 项目 | 技术栈 | 页面入口 | API 前缀 | 主要职责 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `admin` | Vue 3 + Vite + Arco Design | `/admin` | `/adminapi` | 平台运营与配置 |
|
||||
| `mer` | Vue 3 + Vite + Arco Design | `/mer` | `/merapi` | 商户自助后台 |
|
||||
| `cashier` | Vue 3 + Vite + Tailwind CSS | `/cashier`、`/payment` | `/api/cashier` | 用户侧收银台 |
|
||||
|
||||
## 共性
|
||||
|
||||
- 每个前端都有自己的 `package.json` 和环境文件。
|
||||
- `admin`、`mer` 的 axios 实例会在 `VITE_APP_BASE_URL` 后拼接 `/adminapi` 或 `/merapi`。
|
||||
- `cashier` 的 axios 实例使用 `VITE_API_BASE_URL` 作为基址,并在请求路径中拼接 `VITE_API_PREFIX`,默认是 `/api`。
|
||||
- 本地开发默认代理或直连 `http://127.0.0.1:8787`。
|
||||
|
||||
## 命令
|
||||
|
||||
| 项目 | 开发 | 构建 | 预览 |
|
||||
| --- | --- | --- | --- |
|
||||
| `admin` | `pnpm dev` | `pnpm build:dev` / `pnpm build:prod` / `pnpm build:test` | `pnpm preview` |
|
||||
| `mer` | `pnpm dev` | `pnpm build:dev` / `pnpm build:prod` / `pnpm build:test` | `pnpm preview` |
|
||||
| `cashier` | `pnpm dev` | `pnpm build` | `pnpm preview` |
|
||||
|
||||
## 阅读入口
|
||||
|
||||
- [管理后台前端](./admin.md)
|
||||
- [商户后台前端](./mer.md)
|
||||
- [收银台前端](./cashier.md)
|
||||
- [接口总说明](../api/README.md)
|
||||
- [部署总说明](../deployment/README.md)
|
||||
54
doc/frontend/admin.md
Normal file
54
doc/frontend/admin.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# 管理后台前端
|
||||
|
||||
`admin` 是平台管理后台,服务于运营、配置、财务和运维人员。
|
||||
|
||||
## 基本信息
|
||||
|
||||
- 目录:`admin/`
|
||||
- 技术栈:Vue 3、Vite、TypeScript、Arco Design、Pinia、axios
|
||||
- 页面入口:`/admin`
|
||||
- API 前缀:`VITE_APP_BASE_URL + /adminapi`
|
||||
- 开发默认后端:`http://127.0.0.1:8787`
|
||||
- 开发公共路径:`/admin`
|
||||
- 生产公共路径:`/`
|
||||
|
||||
## 主要模块
|
||||
|
||||
- 商户、商户分组、商户 API 凭证、商户策略
|
||||
- 支付方式、支付插件、插件配置、支付通道、轮询组与绑定
|
||||
- 路由解析预览
|
||||
- 支付订单、退款订单、清算订单
|
||||
- 商户账户、资金流水
|
||||
- 通道日统计、通道通知日志、支付回调日志、商户通知任务
|
||||
- 文件资产、系统菜单、字典、系统配置页面、管理员用户
|
||||
|
||||
## 关键目录
|
||||
|
||||
```text
|
||||
src/api/modules/ 接口封装
|
||||
src/router/ 静态路由和动态路由处理
|
||||
src/views/ 页面
|
||||
src/store/ Pinia 状态
|
||||
src/components/ 业务与通用组件
|
||||
```
|
||||
|
||||
## 文件上传
|
||||
|
||||
管理后台的文件资产接口是 `/adminapi/file-asset`。系统配置和插件配置中的上传字段仍使用 `type: "upload"`;需要走项目定制的图片/文件选择器时,在 `props.fileUpload` 中声明:
|
||||
|
||||
- `selectorType`:`image` 或 `file`
|
||||
- `scene`:图片、证书、文本或其他场景
|
||||
- `isLocal`:是否强制本地存储
|
||||
- `isPublic`:是否公开访问
|
||||
- `getKey`:上传成功后回填的响应字段,常用 `url`、`object_key`、`preview_url`、`id`
|
||||
|
||||
完整文件资产行为见 [文件资产](../backend/files.md)。
|
||||
|
||||
## 常用命令
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
pnpm dev
|
||||
pnpm build:prod
|
||||
pnpm preview
|
||||
```
|
||||
47
doc/frontend/cashier.md
Normal file
47
doc/frontend/cashier.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# 收银台前端
|
||||
|
||||
`cashier` 是用户侧支付前端,负责收银台上下文展示、确认支付、跳转和支付结果页。
|
||||
|
||||
## 基本信息
|
||||
|
||||
- 目录:`cashier/`
|
||||
- 技术栈:Vue 3、Vite、TypeScript、Tailwind CSS、axios、qrcode
|
||||
- 页面入口:`/cashier`、`/payment`
|
||||
- JSON API:默认 `/api/cashier/*`
|
||||
- 开放支付 API:后端同时提供 `/api/pay/*`
|
||||
- 本地代理:`/api` 默认代理到 `http://127.0.0.1:8787`
|
||||
|
||||
## 页面路由
|
||||
|
||||
| 路由 | 页面 |
|
||||
| --- | --- |
|
||||
| `/cashier` | 首页 |
|
||||
| `/cashier/:bizNo` | 收银台入口页 |
|
||||
| `/payment/:payNo` | 支付页 |
|
||||
| `/payment/:payNo/redirect` | 支付中转页 |
|
||||
| `/payment/:payNo/result` | 结果页 |
|
||||
| `/payment/:payNo/success` | 成功结果页 |
|
||||
| `/payment/:payNo/return` | 支付返回页 |
|
||||
| `/payment/:payNo/error` | 支付错误页 |
|
||||
| `/payment/:payNo/back` | 返回中转页 |
|
||||
|
||||
页面路由前缀在 `src/config/index.ts` 中固定为 `CASHIER_PATH_PREFIX=/cashier`、`PAYMENT_PATH_PREFIX=/payment`。当前 `.env` 中的 `VITE_ROUTE_PREFIX` 只是遗留示例变量,代码没有读取它。
|
||||
|
||||
## 接口调用
|
||||
|
||||
`src/api/cashier.ts` 当前封装三类接口:
|
||||
|
||||
- `GET /api/cashier/context?biz_no=...`
|
||||
- `POST /api/cashier/confirm`
|
||||
- `GET /api/cashier/pay-order?pay_no=...`
|
||||
|
||||
`VITE_API_PREFIX` 会影响请求路径前缀,默认是 `api`;`VITE_API_BASE_URL` 用于直连后端,不填时本地通过 Vite 代理访问 `/api`。
|
||||
|
||||
## 常用命令
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
pnpm dev
|
||||
pnpm build
|
||||
pnpm preview
|
||||
```
|
||||
146
doc/frontend/menu.md
Normal file
146
doc/frontend/menu.md
Normal file
@@ -0,0 +1,146 @@
|
||||
# 菜单说明
|
||||
|
||||
> 真实菜单以 [`../../mpay/config/menu.php`](../../mpay/config/menu.php) 为准;稳定口径见 [`../standards.md`](../standards.md)。
|
||||
|
||||
这份文档只说明当前页面和菜单职责,不重复项目事实。
|
||||
|
||||
## 1. 管理后台当前菜单
|
||||
|
||||
```text
|
||||
管理后台
|
||||
├─ 首页
|
||||
├─ 商户管理
|
||||
│ ├─ 商户列表
|
||||
│ └─ 商户分组
|
||||
├─ 通道中心
|
||||
│ ├─ 支付方式
|
||||
│ ├─ 支付插件
|
||||
│ ├─ 插件配置
|
||||
│ ├─ 通道列表
|
||||
│ └─ 通道日统计
|
||||
├─ 路由中心
|
||||
│ ├─ 轮询组
|
||||
│ ├─ 路由编排
|
||||
│ └─ 路由预览
|
||||
├─ 交易中心
|
||||
│ ├─ 支付订单
|
||||
│ ├─ 退款订单
|
||||
│ ├─ 清算订单
|
||||
│ ├─ 通道通知日志
|
||||
│ └─ 支付回调日志
|
||||
├─ 资金中心
|
||||
│ ├─ 资金账户
|
||||
│ └─ 资金流水
|
||||
├─ 系统管理
|
||||
│ ├─ 管理员用户
|
||||
│ └─ 系统配置
|
||||
└─ 文件中心
|
||||
└─ 文件管理
|
||||
```
|
||||
|
||||
## 2. 商户后台当前菜单
|
||||
|
||||
```text
|
||||
商户后台
|
||||
├─ 首页
|
||||
├─ 通道中心
|
||||
│ ├─ 我的通道
|
||||
│ ├─ 路由预览
|
||||
│ └─ 商户 API 凭证
|
||||
├─ 交易中心
|
||||
│ ├─ 支付订单
|
||||
│ ├─ 退款订单
|
||||
│ └─ 清算记录
|
||||
├─ 资金中心
|
||||
│ ├─ 可提现余额
|
||||
│ └─ 余额流水
|
||||
└─ 基础设置
|
||||
├─ 商户资料
|
||||
└─ 登录认证
|
||||
```
|
||||
|
||||
## 3. 页面职责
|
||||
|
||||
### 3.1 商户管理
|
||||
|
||||
| 页面 | 当前职责 |
|
||||
|---|---|
|
||||
| 商户列表 | 商户主体资料维护 + 通过操作台抽屉查看接入、商户 API 凭证、资金等摘要 |
|
||||
| 商户分组 | 商户分组主数据维护,是路由绑定的输入条件 |
|
||||
|
||||
说明:
|
||||
|
||||
- 不再单独暴露“商户用户”页面
|
||||
- 商户更多能力通过商户列表内的操作台处理
|
||||
|
||||
### 3.2 通道中心
|
||||
|
||||
| 页面 | 当前职责 |
|
||||
|---|---|
|
||||
| 支付方式 | 支付方式字典维护 |
|
||||
| 支付插件 | 扫描插件类并同步插件定义 |
|
||||
| 插件配置 | 维护插件初始化配置 |
|
||||
| 通道列表 | 维护平台通道和商户自有通道 |
|
||||
| 通道日统计 | 查看通道健康度、成功率、耗时等 |
|
||||
|
||||
### 3.3 路由中心
|
||||
|
||||
| 页面 | 当前职责 |
|
||||
|---|---|
|
||||
| 轮询组 | 维护轮询组本身,并在组内编排候选通道 |
|
||||
| 路由编排 | 维护“商户分组 + 支付方式 -> 轮询组”的绑定关系 |
|
||||
| 路由预览 | 按真实链路预览某次支付会命中哪个通道 |
|
||||
|
||||
当前路由模式:
|
||||
|
||||
- 顺序依次轮询
|
||||
- 权重随机轮询
|
||||
- 默认启用通道
|
||||
|
||||
### 3.4 交易中心
|
||||
|
||||
| 页面 | 当前职责 |
|
||||
|---|---|
|
||||
| 支付订单 | 平台支付单查询、详情、回调和状态跟踪 |
|
||||
| 退款订单 | 退款单查询、退款推进和退款结果跟踪 |
|
||||
| 清算订单 | 平台代收清算批次、明细和结算结果查看 |
|
||||
| 通道通知日志 | 查看通道侧通知记录和失败原因 |
|
||||
| 支付回调日志 | 查看支付回调记录和幂等处理结果 |
|
||||
|
||||
### 3.5 资金中心
|
||||
|
||||
| 页面 | 当前职责 |
|
||||
|---|---|
|
||||
| 资金账户 | 商户账户余额、冻结余额、可用余额查看 |
|
||||
| 资金流水 | 商户账户入账、出账、冻结、解冻流水查看 |
|
||||
|
||||
### 3.6 系统管理
|
||||
|
||||
| 页面 | 当前职责 |
|
||||
|---|---|
|
||||
| 管理员用户 | 平台管理员账号维护 |
|
||||
| 系统配置 | 平台级配置维护与同步 |
|
||||
|
||||
### 3.7 文件中心
|
||||
|
||||
| 页面 | 当前职责 |
|
||||
|---|---|
|
||||
| 文件管理 | 统一管理图片、证书、文本和其他附件,支持本地存储和对象存储切换 |
|
||||
|
||||
### 3.8 商户后台
|
||||
|
||||
| 页面 | 当前职责 |
|
||||
|---|---|
|
||||
| 我的通道 | 只读查看当前商户可见通道 |
|
||||
| 路由预览 | 输入支付方式和金额,查看当前商户会命中的通道 |
|
||||
| 商户 API 凭证 | 查看当前商户的商户 API 凭证相关信息 |
|
||||
| 商户资料 | 维护联系人与结算信息 |
|
||||
| 登录认证 | 修改登录密码,查看登录相关信息 |
|
||||
|
||||
## 4. 口径差异
|
||||
|
||||
- 轮询组已从“通道中心”移动到“路由中心”
|
||||
- 商户管理不再拆“商户用户”
|
||||
- 商户更多能力仍收在列表页内的操作台
|
||||
- 商户后台登录已改为“商户号 + 密码”
|
||||
- 商户 API 凭证与商户后台登录彻底分离
|
||||
46
doc/frontend/mer.md
Normal file
46
doc/frontend/mer.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# 商户后台前端
|
||||
|
||||
`mer` 是商户自助后台,服务于商户查看自身资料、凭证、订单、退款和资金信息。
|
||||
|
||||
## 基本信息
|
||||
|
||||
- 目录:`mer/`
|
||||
- 技术栈:Vue 3、Vite、TypeScript、Arco Design、Pinia、axios
|
||||
- 页面入口:`/mer`
|
||||
- API 前缀:`VITE_APP_BASE_URL + /merapi`
|
||||
- 开发默认后端:`http://127.0.0.1:8787`
|
||||
- 开发公共路径:`/mer`
|
||||
- 生产公共路径:`/`
|
||||
|
||||
## 主要模块
|
||||
|
||||
- 商户登录、退出、当前用户资料
|
||||
- 商户资料维护、修改登录密码
|
||||
- 我的通道维护、商户插件配置、路由预览
|
||||
- 商户 API 凭证查看与重置
|
||||
- 支付订单、退款订单、退款重试
|
||||
- 清算记录、可提现余额、资金流水
|
||||
- 菜单树和字典项
|
||||
|
||||
## 自助通道配置
|
||||
|
||||
商户端通道中心包含“我的通道”和“插件配置”。“插件配置”只展示当前商户自己的配置;“我的通道”新增或编辑时只能绑定当前商户的插件配置,并且插件来源受管理后台“商户端自助使用”开关控制。
|
||||
|
||||
## 关键目录
|
||||
|
||||
```text
|
||||
src/api/modules/ 接口封装
|
||||
src/router/ 路由
|
||||
src/views/ 页面
|
||||
src/store/ 状态
|
||||
src/components/ 组件
|
||||
```
|
||||
|
||||
## 常用命令
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
pnpm dev
|
||||
pnpm build:prod
|
||||
pnpm preview
|
||||
```
|
||||
62
doc/overview.md
Normal file
62
doc/overview.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# 项目总览
|
||||
|
||||
`MPAY_V2` 是一个支付中台工作区,后端基于 Webman,前端拆成管理后台、商户后台和收银台三套独立应用。
|
||||
|
||||
## 工作区组成
|
||||
|
||||
| 目录 | 类型 | 职责 |
|
||||
| --- | --- | --- |
|
||||
| `mpay` | PHP / Webman 后端 | 支付、退款、路由、插件、回调、商户、资金、清算、文件与系统配置 |
|
||||
| `admin` | Vue 3 管理后台 | 商户、通道、插件、轮询、订单、退款、清算、资金、日志、文件、系统配置 |
|
||||
| `mer` | Vue 3 商户后台 | 商户资料、API 凭证、可用通道、路由预览、订单、退款、清算、余额和流水 |
|
||||
| `cashier` | Vue 3 收银台 | 收银台上下文、确认支付、支付跳转、支付单状态和结果页 |
|
||||
| `docs` | 文档中心 | 当前项目事实、接口、部署和协作说明 |
|
||||
|
||||
根目录只是工作区容器;`admin`、`mer`、`cashier`、`mpay` 各自保留独立 Git 仓库。
|
||||
|
||||
## 核心链路
|
||||
|
||||
```text
|
||||
商户系统/ePay 请求
|
||||
-> 后端校验商户与签名
|
||||
-> 创建业务单/支付单
|
||||
-> 商户分组路由解析
|
||||
-> 轮询组选择支付通道
|
||||
-> 支付插件调用第三方
|
||||
-> 收银台展示或跳转
|
||||
-> 回调/查单推进状态
|
||||
-> 通知商户
|
||||
-> 清算后写入商户资金与流水
|
||||
```
|
||||
|
||||
## 当前入口
|
||||
|
||||
| 场景 | 页面入口 | API 入口 | 后端路由文件 |
|
||||
| --- | --- | --- | --- |
|
||||
| 管理后台 | `/admin` | `/adminapi` | `mpay/app/route/admin.php` |
|
||||
| 商户后台 | `/mer` | `/merapi` | `mpay/app/route/mer.php` |
|
||||
| 收银台 | `/cashier`、`/payment` | `/api/cashier`、`/api/pay` | `mpay/app/route/api.php` |
|
||||
| ePay V1 兼容 | `/submit.php`、`/mapi.php`、`/api.php` | 同左 | `mpay/app/route/api.php` |
|
||||
| ePay V2 / 开放 API | 无固定页面 | `/api/pay`、`/api/merchant`、`/api/transfer` | `mpay/app/route/api.php` |
|
||||
|
||||
## 后端重点模块
|
||||
|
||||
- `app/http`:管理后台、商户后台、开放 API 的控制器、中间件和参数校验。
|
||||
- `app/route`:显式路由;默认路由已关闭。
|
||||
- `app/service/payment`:支付、退款、清算、路由、插件、通知、追踪和 ePay 协议。
|
||||
- `app/service/merchant`:商户主体、登录、分组、策略、商户后台能力和 API 凭证。
|
||||
- `app/service/account`:商户资金账户和流水。
|
||||
- `app/service/file`:文件资产、上传、预览、下载和存储驱动。
|
||||
- `app/common/payment`:支付插件实现,当前包含 Alipay、ePay V1、ePay V2 和模板插件。
|
||||
|
||||
## 数据范围
|
||||
|
||||
当前 DDL 包含支付配置、商户、订单、退款、转账、资金、清算、日志、通知、文件、系统配置和管理员用户表。完整表结构以 [当前 DDL](./db/payment-middle-ddl.sql) 为准。
|
||||
|
||||
## 推荐阅读
|
||||
|
||||
1. [架构与请求流](./architecture.md)
|
||||
2. [后端总说明](./backend/README.md)
|
||||
3. [前端总说明](./frontend/README.md)
|
||||
4. [接口总说明](./api/README.md)
|
||||
5. [部署总说明](./deployment/README.md)
|
||||
47
doc/plan.md
Normal file
47
doc/plan.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# 当前阶段计划
|
||||
|
||||
当前重点是联调稳定、自动化测试、通知补偿、运维监控和安全收边。稳定事实见 [稳定口径](./standards.md)。
|
||||
|
||||
## 已具备的主干能力
|
||||
|
||||
- 管理后台、商户后台、收银台三端独立运行。
|
||||
- 支付、退款、清算、资金流水、通道路由和插件运行时已经形成主链路。
|
||||
- ePay V1 兼容入口与 ePay V2 开放 API 已在后端路由中显式维护。
|
||||
- 文件资产支持本地、远程 URL、OSS、COS。
|
||||
- `payment-runtime` 已承担通知重试、支付超时扫描和支付中订单主动查单。
|
||||
- 文档中心已收口到 `docs/`。
|
||||
|
||||
## P0 联调闭环
|
||||
|
||||
- [ ] 用真实第三方配置跑通支付创建、收银台展示、回调、查单和商户通知。
|
||||
- [ ] 跑通退款创建、退款查询和退款状态推进。
|
||||
- [ ] 跑通平台代收清算入账链路。
|
||||
- [ ] 验证商户自有通道余额扣费、冻结和释放逻辑。
|
||||
- [ ] 验证路由命中与轮询策略符合预期。
|
||||
|
||||
## P1 测试补齐
|
||||
|
||||
- [ ] 补支付、退款、清算、资金、路由、插件装配的自动化测试。
|
||||
- [ ] 补回调幂等、重复通知、通知重试测试。
|
||||
- [ ] 将关键检查沉淀到 `app/command` 烟雾测试命令。
|
||||
|
||||
## P2 运维与补偿
|
||||
|
||||
- [ ] 为通知重试超限、回调失败、路由无命中、通道不可用增加告警。
|
||||
- [ ] 支持商户通知人工补发。
|
||||
- [ ] 统一通知、回调、通道统计日志字段。
|
||||
- [ ] 梳理数据库备份、恢复和第三方通道异常应急流程。
|
||||
|
||||
## P3 安全与配置
|
||||
|
||||
- [ ] 检查 `.env.example` 是否覆盖必填配置。
|
||||
- [ ] 清理默认密码、默认商户凭证、默认数据库账号等生产风险。
|
||||
- [ ] 增加后台登录限流和开放接口限流。
|
||||
- [ ] 补齐敏感数据脱敏和关键操作审计。
|
||||
|
||||
## P4 文档与接入
|
||||
|
||||
- [ ] 补开放支付 API 联调说明。
|
||||
- [ ] 补回调协议、签名规则和金额口径说明。
|
||||
- [ ] 完善支付插件开发约定。
|
||||
- [ ] 持续清理文档、注释、页面中的旧口径。
|
||||
73
doc/standards.md
Normal file
73
doc/standards.md
Normal file
@@ -0,0 +1,73 @@
|
||||
# 稳定口径
|
||||
|
||||
本文只记录当前项目需要长期保持一致的事实和约束。更细的字段、表结构和接口参数以代码、路由文件和 DDL 为准。
|
||||
|
||||
## 项目边界
|
||||
|
||||
- `mpay` 是 Webman 后端,提供管理后台、商户后台、收银台、ePay 兼容和开放 API。
|
||||
- `admin`、`mer`、`cashier` 是三套独立 Vue 前端,分别构建和发布。
|
||||
- 根目录不是业务仓库,只作为多项目工作区。
|
||||
- 新文档统一维护在 `docs/`;`mpay/doc/` 只作为旧资料归档。
|
||||
|
||||
## 返回格式
|
||||
|
||||
- 成功态统一使用 `HTTP 200`。
|
||||
- 业务状态由响应体 `code` 判断,前端拦截器按 `code` 处理成功、失败和登录过期。
|
||||
- 控制器负责参数接收和响应包装;服务层和仓库层遇到业务错误直接抛项目异常。
|
||||
- 金额统一使用“分”作为存储和接口计算单位。
|
||||
- 模型时间序列化统一输出 `Y-m-d H:i:s`。
|
||||
|
||||
## 鉴权与签名
|
||||
|
||||
- 管理后台走 `/adminapi`,登录后通过 `Authorization` 头传管理员 token。
|
||||
- 商户后台走 `/merapi`,登录主体是 `ma_merchant`,登录字段是 `merchant_no + password`。
|
||||
- 商户开放 API 凭证在 `ma_merchant_api_credential`,只用于接口签名,不参与商户后台登录。
|
||||
- ePay V1/V2 兼容接口的签名规则独立维护,不能反向污染后台登录逻辑。
|
||||
|
||||
## 支付路由
|
||||
|
||||
支付选路固定围绕以下数据关系展开:
|
||||
|
||||
```text
|
||||
商户 -> 商户分组 -> 路由绑定 -> 轮询组 -> 轮询组通道编排 -> 支付通道 -> 支付插件配置
|
||||
```
|
||||
|
||||
路由模式当前为:
|
||||
|
||||
- `0`:顺序轮询
|
||||
- `1`:权重随机
|
||||
- `2`:默认启用通道
|
||||
|
||||
`pay_types`、`transfer_types` 存支付方式代码数组;支付插件类由运行时根据插件配置解析。
|
||||
|
||||
## 资金与清算
|
||||
|
||||
- 商户资金账户表是 `ma_merchant_account`。
|
||||
- 资金流水表是 `ma_merchant_account_ledger`。
|
||||
- 平台代收成功后不直接进入商户可提现余额,清算成功后才入账。
|
||||
- 支付回调、通道通知日志、商户通知任务、通道日统计和清算记录都属于核心链路。
|
||||
|
||||
## 服务层命名
|
||||
|
||||
- `*Service`:对控制器或外部调用方暴露的门面。
|
||||
- `*QueryService`:查询、列表、详情和展示拼装。
|
||||
- `*CommandService`:新增、修改、删除、状态变更。
|
||||
- `*LifecycleService`:支付、退款、清算等状态推进。
|
||||
- `*CallbackService`:第三方回调和通知处理。
|
||||
- `*SyncService`:配置同步、插件扫描、目录同步。
|
||||
- `*SupportService`:有业务语义的共享辅助能力;不要只转发基础工具方法。
|
||||
|
||||
## 文件资产
|
||||
|
||||
- 文件资产模型名是 `FileRecord`,数据表是 `ma_file_asset`。
|
||||
- 管理后台文件接口前缀是 `/adminapi/file-asset`。
|
||||
- 支持本地存储、远程 URL 导入、阿里云 OSS、腾讯云 COS。
|
||||
- 上传字段仍使用 `type: "upload"`;需要走项目定制选择器时,在 `props.fileUpload` 中声明场景、可见性、存储偏好和回填字段。
|
||||
- `object_key` 是站点相对路径或对象键;`url` 只保存公开可访问地址;私有文件可通过预览/下载接口临时读取。
|
||||
|
||||
## 运行与配置
|
||||
|
||||
- 后端 HTTP 进程默认监听 `0.0.0.0:8787`。
|
||||
- `payment-runtime` 进程负责商户通知重试、支付超时扫描和支付中订单主动查单。
|
||||
- 站点 URL、支付运行时开关、存储配置等长期配置优先进入系统配置,不在业务代码中临时拼接。
|
||||
- 新增系统配置后执行 `php webman system:config-sync` 同步默认值。
|
||||
Reference in New Issue
Block a user