18 Commits

Author SHA1 Message Date
技术老胡
55fd7f9335 修复微信监听配置不回调问题 2024-12-11 11:20:58 +08:00
技术老胡
36b6cdba8c 更新结构 2024-12-10 13:57:24 +08:00
技术老胡
45d26dc524 文档更新 2024-12-10 11:24:30 +08:00
技术老胡
fb025542f2 UI交互优化 2024-12-09 15:59:09 +08:00
技术老胡
513b0f521d 二维码生成优化 2024-12-09 15:31:19 +08:00
技术老胡
27867c65c5 优化二维码生成交互逻辑 2024-12-09 15:23:17 +08:00
技术老胡
6b3f6c5448 一些UI交互处理 2024-12-09 14:40:21 +08:00
技术老胡
6c3106a6aa 文档更新 2024-12-07 17:16:17 +08:00
技术老胡
8f1d6ce013 文档更新 2024-12-07 11:50:03 +08:00
技术老胡
40a287ad41 更新后端查询收银平台订单明细功能 2024-12-06 17:20:28 +08:00
技术老胡
37cfebd7eb 更新文档 2024-12-05 15:19:48 +08:00
技术老胡
2cd6eafb02 修复首次安装程序报错问题 2024-12-05 14:55:56 +08:00
技术老胡
cd9d416bc1 文档更新 2024-12-05 12:43:52 +08:00
技术老胡
2cd77c824c 更新文档 2024-12-05 11:44:47 +08:00
技术老胡
6d0e7ce296 文档更新 2024-12-05 09:38:37 +08:00
技术老胡
1f02d14a80 更新文档 2024-12-05 09:28:11 +08:00
技术老胡
98a405c745 更新文档 2024-12-05 09:17:56 +08:00
技术老胡
edeebfdfef 更新文档信息 2024-12-04 18:06:12 +08:00
50 changed files with 515 additions and 110 deletions

283
README.md
View File

@@ -4,7 +4,7 @@
**码支付[mpay]是一款便捷收款工具,专注于个人免签收款,通过普通收款码即可实现收款通知自动回调,支持绝大多数商城系统**
## 项目地址
# 项目地址
| gitee | github |
| :----------: | :------------: |
@@ -12,44 +12,56 @@
点以上图标进入项目页面,可查看最新发行版
## 项目说明
# 项目说明
### 原因
## 原因
之前在工作之余,尝试过很多副业项目,基本都是跟互联网相关的。例如使用**Wordpress和zibll主题**建资源站点,用**微擎**和**微课堂V2**卖课程,使用**异次元发卡**和**独角数卡**程序卖代理的**微信营销软件**,其中遇到的让我最头痛的问题就是收款问题。
正规官方渠道,如微信支付宝申请相关支付接口,必须要签约,且有营业执照等相关资质,这对于只想搞想副业的我实在是没必要,也麻烦,后期的事情也多。于是在网上找到了一些解决方案来解决我的在线收款问题。
### 方案
---
## 方案
市场也有很多针对此类需求的平台,也有不少合适的解决方案
#### 虎皮椒/迅虎/蓝兔支付
### 虎皮椒/迅虎/蓝兔支付
这些平台都是微信支付宝等官方平台的支付服务商能签约个人商户实现在线收款一些API支付接口可以直接调用跟官方自己申请的几乎差不多。
优点是跟官方申请的支付接口差不多N+1到账
缺点就是审核比较严动不动投诉封商户号一些羊毛党就爱整你你是一点办法没有另外开户基本都要交开户费50-200不等且还需要额外收取手续费。
#### 彩虹易支付
### 彩虹易支付
彩虹易支付是一套收款程序,有专门的公司或平台用这套程序搭建一个收款平台,使用自己的微信或支付宝等官方账户来进行收款,你自己注商户号,运营方提供代收款服务,然后给你打款结算。
优点是审核不严,处理比较灵活,适合个人,技术支持比较好,注册可使用(有些需要注册费)
缺点就是平台容易卷款跑路,也没有什么有效监管,钱收不回来就亏大了(这也是最大的问题)
#### 源支付
### 源支付/V免签
源支付也是一套收款程序,有个人版和商户版,市面上能搜到的大部分都是商户版,可以入驻,使用自己的个人微信支付宝二维码收款。
源支付程序的设计思路主要是通过在手机或电脑上安装消息监听软件,用来监听获取微信和支付宝的收款到账通知来实现的支付成功回调的。**方法很实用,本程序也添加了该功能插件,免费**。
V免签是一款开源免费适用于个人收款使用的收款程序原理同源支付类似。
程序的设计思路主要是通过在手机或电脑上安装消息监听软件,用来监听获取微信和支付宝的收款到账通知来实现的支付成功回调的。**方法很实用,本程序也添加了该功能插件,免费**。
![](assets/20241210_112331_e8d2c4043a3c57ad887aef92df1c253.png)
只是这种思路,有一些小问题:
* 平台容易因为资质问题导致关站;
* 收取的手续费价格偏高;
* 个人码在微信H5环境无法长按识别付款只能通过PC端相机扫码付款。
* 挂机监听容易掉线,导致收款通知无法回调
#### 码支付(mpay)
### 🚀️ 码支付(mpay)
**本程序暂只提供个人版,开源免费使用。**
码支付是在源支付的设计思路基础上进行的改进,利用第四方**聚合收款码**来进行收款保证收款稳定和便捷。聚合收款码个人可以申请不需求相关资质不用申请API接口收银服务平台众多且实力雄厚如拉卡拉、收钱吧等不怕跑路。
码支付是在源支付的设计思路基础上进行的改进,利用第四方**聚合收款码**来进行收款,保证收款稳定和便捷不掉线。
聚合收款码个人可以申请不需求相关资质不用申请API接口收银服务平台众多且实力雄厚如拉卡拉、收钱吧等不怕跑路。
特点如下:
* 免监听,不需要手机或电脑挂机监听消息,即可实现支付回调,只需要设置一个定时任务就行
@@ -57,7 +69,9 @@
* 个人搭建的收款系统,收款稳定,安全可控,不需要额外手续费
* 支持多平台(聚合码服务商),多账号(聚合码商户),多渠道(门店码/店员码/桌号码等),降低异地线上收款风控风险
### 思路
---
## 思路
码支付说到底就是通过二维码来进行收款,日常使用的除了微信支付宝生成的二维码外,还有一类二维码是由收款服务商提供的,它能通过一张收款二维码,同时支持**微信**、**支付宝**、**云闪付**等多渠道付款,一般称为**聚合收款码**。
@@ -81,13 +95,15 @@
当用户付款成功,并且后台检测到收款成功消息后,收钱台就会提示收款成功,并最终确认收款。
![](assets/20241210_112301_6f2fef2a7aaee96790eb86f90e3b107.png)
> 只有存在新订单时,且该订单与当前收款账号一致时,码支付后台才会主动登陆该账号,查询收款流水,减少频繁查询导致的可能风险
> 另外,在账号设置里也有两个模式可选,`单次监听`和`连续监听`,根据业务场景可以自行选择,具体使用,下面有介绍
## 开源声明
# 开源声明
### 内置插件
## 内置插件
码支付收款管理系统开源免费使用,支付插件有**免费**和**付费**版本,按需使用购买
@@ -95,22 +111,25 @@
2. **支付宝**插件`alipay`默认安装,支持`收钱码``经营码`2个通道需挂机监听
3. **收钱吧**聚合码插件`sqbpay`默认安装,无需挂机,设置定时任务即可
### 技术架构
## 技术架构
使用`Thinkphp8`框架PHP版本 > 8.0推荐8.2前端UI使用`Layui 2.9`+`PearAdmin`后台
## 安装和使用
---
### 全新安装
# 安装和使用
## 全新安装
以下演示基于**云服务器**环境+**宝塔面板**安装,云服务器购买可以去阿里云、腾讯云等平台,宝塔面板安装教程参考[宝塔面板安装教程](https://www.bt.cn/new/download.html)
#### 源码下载
### 🚀️ 源码下载
点击下载 [码支付 v1.0.0版本](https://gitee.com/technical-laohu/mpay/releases/tag/v1.0.0)
更多版本请关注发行版更新记录
**点击下载 [码支付 v1版本](https://gitee.com/technical-laohu/mpay/releases)**
#### 安装配置
**更多版本请关注发行版更新记录**
### 安装配置
以**宝塔面板**示例,其他服务器管理面板可以参考
@@ -128,11 +147,12 @@
点击上传文件,将源码压缩包上传到该文件夹,并解压到前文件夹
![](assets/20241203_162646_image.png)
**将`mpay`文件夹里面的所有文件,复制到当前根目录下**,返回网站列表管理页面
> 注意,压缩包文件打包的是一个名为`mpay`的文件夹,需要将代码文件夹里面的所有文件复制出来,放到创建的网站根目录下,
#### 运行目录&仿静态 配置
### 运行目录&仿静态 配置
点击网站名,进入网站配置设置页面
@@ -146,7 +166,7 @@
![](assets/20241203_163507_image.png)
#### 安装步骤
### 安装步骤
在浏览器输入`http://你的域名/install`,进入程序安装界面,按照提示进行填写提交
@@ -156,11 +176,13 @@
![](assets/20241203_165507_image.png)
### 聚合码使用
---
## ❤️ 聚合码使用
如果本身就有聚合码收钱码最好,没有就需要提前去各收银服务商申请,申请也不复杂
#### 申请收款码
### 申请收款码
以下列出一些常见收款服务平台,可以按需申请,个人直接申请小微商户即可
@@ -174,39 +196,220 @@
> 申请可以去官方平台注册账号等客服电话,或者在社群里询问(有很多人有代办资质),实在找不到的,可以去淘宝上的官方店买个二维码卡牌贴纸,然后询问客服如何开通账号就行,会有专员联系你开通。
#### 安装插件
### 安装插件
#### 添加账号
程序默认安装有**微信支付**`wxpay`、**支付宝**`alipay`、**收钱吧**`sqbpay`三个插件,基本能满足大家的日常收款需求。
#### 添加收款码
如果需要其他收银平台插件,可以在插件中心自行安装,插件中心没有的,也可以联系作者定制开发。
#### 设置监听
### 添加账号
##### 订单监听
除**微信支付**`wxpay`、**支付宝**`alipay`之外,所有的收款平台均为聚合码收款平台
##### 账号收款监听
以**收钱吧**`sqbpay`为例,添加账号时,需要填写**收钱吧**商户管理中心的**登陆账号**和**登陆密码**,需要使用时,插件会自动在后台登陆账号并查询相关订单流水信息。
#### 支付测试
![](assets/20241205_102856_image.png)
![](assets/20241205_103718_image.png)
### 微信/支付宝使用
**监听模式**说明,分为`单次监听``连续监听`
![](assets/20241205_104101_image.png)
`单次监听`是在收银台页面,用户扫码支付成功后,需要手动点击**确认支付**,后台才会登陆查询该账号的收款流水情况,**点击一次,查询一次,不点击,则不会查询**,降低密集查询可能导致的账户风控
![](assets/20241205_104306_image.png)
`连续监听`是在创建订单之后,**在订单有效期内且订单未完成状态时**,插件会一直连续不断的查询账户流水详情,直到过期或者成功收款,用户不需要主动点击确认
![](assets/20241205_105844_image.png)
> 使用哪种监听模式,视应用场景自行选择,一般选择**连续监听**即可,如果日常订单比较密集,可以选择**单次监听**,或多添加几个账号,减少密集查询风控
### 添加收款码
点击可以查看当前账号的所有收款码
![](assets/20241205_110432_image.png)
点击**添加收款码**后,可以正常填写二维码信息
**终端编号**需要填写当前收款码在收银服务商系统内的编码,有的可以直接在收款二维码解析的**链接里找到**,有的需要**登陆商户管理中心**,去订单详情里查询才能知道
🚀️ 具体各个平台的终端编号如何获取,可以去**程序后端控制台主页**的`项目文档`查看🚀️
![](assets/20241205_110508_image.png)
![](assets/20241205_111720_image.png)
收款样式有两个选项`付款链接``图片地址`,根据实际情况选择
**付款链接:** 将二维码的内容解析成字符串保存,使用时再生成二维码
**图片地址:** 上传二维码图片到服务器或引用http远程图片地址
> 一般的**聚合收款码都解析成文字保存**即可,前端展示时会重新生成二维码,无法解析的就上传图片
### 🚀️ 设置监听
#### 宝塔任务计划
等会儿需要在这里设置任务监听
![](assets/20241205_112415_image.png)
#### 新订单监听
一个站点只需要设置一个定时任务,每次访问都会查询一次数据库,并生成新订单数据缓存
![](assets/20241205_112033_image.png)
![](assets/20241205_112517_image.png)
#### 账号监听
**每个账号都需要单独设置一个定时任务**,每次访问都会检索新订单缓存数据,如果存在该账户的新订单,插件就会去查询服务商后台的订单流水,并通知相关收款处理程序
![](assets/20241205_112116_image.png)
![](assets/20241205_113629_image.png)
### 支付测试
只开启一个账号,然后点击`支付测试`,正常支付后,观察是否成功回调,如果不能回调请重新检查配置,或再次详细查看**程序后端控制台主页**的`项目文档`
![](assets/20241205_113835_image.png)
---
## ❤️ 微信/支付宝使用
微信/支付宝生成的收款码,需要挂机监听收款消息,基本情况上面有介绍,因为使用广泛,所以也单独添加进来,可以实现正常收款回调
#### 添加账号
### 添加账号
#### 添加收钱码
收款平台选择`微信支付``支付宝`,收款账号就填写`微信支付``支付宝`的账号,与**聚合码支付**不同,此处填写的账号主要用来做区分,不会登陆后台
#### 挂机监听收款通知
![](assets/20241204_164128_image.png)
##### 软件下软(安卓)
### 添加收钱码
##### 功能配置
点击可以查看当前账号的所有收款码
##### 收款通知测试
![](assets/20241204_164617_image.png)
### 补充说明
点击**添加收款码**后,可以正常填写二维码信息
### 页面展示
`微信支付``支付宝`的终端编号是自动生成的,不可手动填写,分别对应当前账号的不同收款通道,支持`个人码``赞赏码`
![](assets/20241204_164650_image.png)
收款样式有两个选项`付款链接``图片地址`,根据实际情况选择
**付款链接:** 将二维码的内容解析成字符串保存,使用时再生成二维码
**图片地址:** 上传二维码图片到服务器或引用http远程图片地址
> `赞赏码`等收款方式,采用的不是标准二维码编码格式,不能解析成文字保存,只能展示图片,建议一般**二维码都解析成文字保存**,前端展示时会根据内容自动再生成二维码,无法解析的就上传图片
---
### 挂机监听收款通知
`微信支付``支付宝`需要手机挂机监听收款通知,并通知服务器收款信息,也是最常见的解决方案
#### 软件下软(安卓)
码支付使用的是开源工具**短信转发器**`SmsForwarder`来监听收款通知,`SmsForwarder`功能非常强大,喜欢*搞机* 的朋友可以多钻研,这是使用文档[短信转发器](https://gitee.com/pp/SmsForwarder/wikis/pages)
* **开源项目地址:[SmsForwarder](https://gitee.com/pp/SmsForwarder)**
* **发布地址,尽量下载最新版,兼职更多机型:[SmsForwarder](https://gitee.com/pp/SmsForwarder/releases)**
* **网盘地址,访问密码:`pppscn`[SmsForwarder](https://wws.lanzoui.com/b025yl86h)**
![](assets/20241204_171340_image.png)
---
#### 🚀️ 功能配置
找一台不常用的安卓系统手机,下载安装好`SmsForwarder`之后,点开进行配置,同时打开码支付后台**用户中心**页面,查看相关配置参数
![](assets/20241204_175759_image.png)
##### **通用设置**
1. 开启转发应用通知,只针对应用软件才会转发
2. 保活措施全部开启(实际情况看手机环境)
3. 通知栏文案可以自定义,方便自己运维,可参考图片
4. 其他默认即可,也可以参考实际情况自行设置
![](assets/20241204_172346_6c9c6f4d97d5850e7890633edd7e5d5.jpg)
##### **发送通道**
添加发送通道,选择`Webhook`类型
![](assets/20241204_172532_image.png)
**编辑规则**
1. 通道名称自行命名,方便区别就行
2. 请求方式选择`POST`方式
3. Webhook Server 地址**用户中心**查看
4. **消息模版**在**用户中心**查看
5. **Secert**密钥在**用户中心**查看
6. 应签关键字`200`
填写完成点击**保存**即可
![](assets/20241204_172612_image.png)
##### **转发规则**
添加应用转发规则,**微信**和**支付宝**需要分别设置
![](assets/20241204_172656_image.png)
**具体设置**
1. 选择发送通道
2. **匹配字段**选择**多重匹配****匹配的值**去**用户中心**复制,然后粘贴过来
3. 开启**启用自定义模版**,内容填写去码支付后台**账号列表**里复制,粘贴过来
**注意:** 微信支付规则里,第三行的`[空格]`需要替换成真实的` `空格
![](assets/20241204_172718_c313070e899cc93cc2fc9fe25a1ff17.jpg)
![](assets/20241204_175634_image.png)
---
##### **转发日志**
这里可以查看所有的转发记录,点击也可以查看消息详情
![](assets/20241204_172819_2d569802058d4d1b0f92135dcc30469.jpg)
##### **注意事项**
用户中心后台里的配置APP包名不一定与手机应用里的包名一致需要查看一下如果有区别请修改成实际本机显示的APP包名下面有图片演示操作
![](assets/20241204_180022_image.png)
![](assets/20241204_173010_image.png)
![](assets/20241204_173046_image.png)
#### 收款通知测试
---
# 补充说明
* 该软件仅适用于个人线上免签收款,避免人工审核确认收款的繁琐步骤。请勿用于诈骗、黑灰产业,如有此类行为,后果自负。
* 插件中心目前还在开发中不支持在线安装插件如果有购买插件或定制其他收银平台插件的需求可以联系作者Wechat:**K103516**
# 页面展示
![首页](assets/20241203_153935_image.png)

View File

@@ -29,7 +29,21 @@ class InstallController
$dbConfig = $request->post();
// 保存数据库配置信息到配置文件
$this->saveDbConfig($dbConfig);
if ($this->saveDbConfig($dbConfig) === false) {
return json(backMsg(1, '配置保存失败'));
} else {
return json(backMsg(0, '配置保存成功'));
};
}
// 初始化数据库
public function init(Request $request)
{
// 检查是否已经安装过
if ($this->checkLock()) {
return backMsg(1, '已经安装');
};
// 获取表单提交的数据库配置信息
$dbConfig = $request->post();
// 连接数据库并建表
$is_succ_tb = $this->createTables();
@@ -66,7 +80,7 @@ DB_PREFIX = mpay_
DEFAULT_LANG = zh-cn
EOT;
file_put_contents($envPath, $envContent);
return file_put_contents($envPath, $envContent);
}
private function createTables()

View File

@@ -159,6 +159,42 @@ class PayManageController extends BaseController
return json(backMsg(1, '上传失败'));
}
}
// 获取账号交易流水
public function getAccountTrade()
{
$req_info = $this->request->get();
$req_pid = $req_info['pid'];
$req_aid = $req_info['aid'];
// 加载配置文件
$config = \think\facade\Config::load("payconfig/{$req_pid}_{$req_aid}", 'payconfig');
// 收款平台账号配置
$pay_config = isset($config['pay']) ? $config['pay'] : [];
// 配置检查
if ($pay_config) {
// 账号配置信息
$aid = $pay_config['aid'];
if ($req_aid != $aid) return '监听收款配置不一致';
} else {
return '监听收款配置文件名错误';
}
// 登陆账号
$config = ['username' => $pay_config['account'], 'password' => $pay_config['password']];
// 收款查询
$query = $pay_config['query'];
// 实例监听客户端
$payclient_name = $pay_config['payclass'];
$payclient_path = "\\payclient\\{$payclient_name}";
$Payclient = new $payclient_path($config);
// 获取支付明细
$records = $Payclient->getOrderInfo($query);
if ($records) {
// 收款流水
return json(backMsg(0, '查询成功', $records));
} else {
return json(backMsg(1, '查询空订单'));
}
}
// 生成账号配置
private function createAccountConfig($acc)
{

View File

@@ -10,7 +10,7 @@ use app\model\User;
class UserController extends BaseController
{
protected $middleware = ['Auth' => ['except' => ['login', 'resetKey']]];
protected $middleware = ['Auth' => ['except' => ['login']]];
public function login()
{

View File

@@ -101,14 +101,16 @@ class Order extends BaseModel
public static function showOrderDetail($id)
{
$order = self::find($id);
$a_list = PayAccount::with('payChannel')->hasWhere('payChannel', ['id' => $order->cid])->where('PayAccount.id', $order->aid)->find();
$a_list = PayAccount::find($order->aid);
$c_list = PayChannel::find($order->cid);
if (!$order) {
return [];
}
$order->platform = $a_list->platform ?? '···';
$order->account = $a_list->account ?? '···';
$order->channel = $a_list->payChannel[0]->channel ?? '···';
$order->qrcode = $a_list->payChannel[0]->qrcode ?? '···';
$order->platform = $a_list['platform'] ?? '···';
$order->account = $a_list['account'] ?? '···';
$order->channel = $c_list['channel'] ?? '···';
$order->qrcode = $c_list['qrcode'] ?? '···';
$order->url_type = $c_list['type'] ?? '···';
return $order->toArray();
}
// 选择收款通道

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 475 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 311 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

View File

@@ -35,7 +35,7 @@ return array (
'platform' => 'sqbpay',
'name' => '收钱吧',
'class_name' => 'ShouQianBa',
'price' => '49.00',
'price' => '0.00',
'describe' => '主流移动支付全能收 信用卡,花呗都能用,生意帮手收钱吧,移动收款就用它!',
'website' => 'https://www.shouqianba.com/',
'state' => 1,

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

@@ -0,0 +1,78 @@
// 登陆配置
const logins = [
{
name: '拉卡拉',
host: 'm2.lakala.com',
method: 'POST',
orderQuery: '/m/lamsmerdash/account/pwdLogin',
accPath: 'account',
pswPath: 'pwd',
},
{
name: '收钱吧',
host: 'web-platforms-msp.shouqianba.com',
method: 'POST',
orderQuery: '/api/login/ucUser/login',
accPath: 'username',
pswPath: 'password',
}
];
// 提取登陆信息
function extractLoginInfo(request, logins) {
logins.forEach((login) => {
const urlObj = isHttp(request.url, login.host);
if (login.host.toLowerCase() === urlObj.hostname.toLowerCase() &&
login.orderQuery.toLowerCase() === urlObj.pathname.toLowerCase() &&
login.method.toLowerCase() === request.method.toLowerCase()) {
const jsonData = JSON.parse(request.request);
const acc = eval(`jsonData.${login.accPath}`);
const psw = eval(`jsonData.${login.pswPath}`);
const data = {
'账号': acc,
'密码': psw
};
console.log('----- ' + login.name + ' -----');
console.table(data);
alert('账号:' + acc + '\n密码' + psw);
}
})
}
function isHttp(url, host) {
if (url.startsWith('http') || url.startsWith('https')) {
return new URL(url);
} else {
url = 'https://' + host + url;
return new URL(url);
}
}
var oldOpen = XMLHttpRequest.prototype.open;
var oldSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open = function (method, url) {
this._url = url;
this._method = method;
return oldOpen.apply(this, arguments);
};
XMLHttpRequest.prototype.send = function (body) {
this._body = body;
const res = {
url: this._url,
method: this._method,
request: this._body
};
extractLoginInfo(res, logins);
return oldSend.apply(this, arguments);
};
window.au_fetch = window.fetch;
window.fetch = function (url, options) {
const res = {
url: url,
method: options.method,
request: options.body
};
extractLoginInfo(res, logins);
return window.au_fetch.apply(window, [url, options]).then((response) => {
return response;
})
};

View File

@@ -6,9 +6,9 @@
<title>程序安装</title>
<link rel="stylesheet" href="/component/pear/css/pear.css" />
<style>
.logo1 {
width: 180px !important;
}
.logo1 {
width: 180px !important;
}
.title {
font-size: 30px !important;
@@ -124,32 +124,27 @@
// 监听提交
form.on('submit(formSubmit)', function (data) {
const field = data.field;
// 提交表单
fetch('/install/install', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(field)
}).then(res => {
if (res.ok) {
return res.json();
}
throw new Error('数据库配置错误');
}).then(res => {
if (res.code == 0) {
layer.msg(res.msg, {
time: 1000,
end: () => {
window.location.href = '/User/login';
}
});
(async () => {
const saveRes = await fetch('/install/install', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(field) });
const saveJson = await saveRes.json();
if (saveJson.code == 0) {
const initRes = await fetch('/install/init', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(field) });
if (initRes.status != 200) { layer.msg('初始化失败,请检查配置'); return; }
const initJson = await initRes.json();
if (initJson.code == 0) {
layer.msg(initJson.msg, {
time: 1000,
end: () => {
window.location.href = '/User/login';
}
});
} else {
layer.msg(initJson.msg);
}
} else {
layer.msg(res.msg);
layer.msg(saveJson.msg);
}
}).catch(err => {
layer.msg(err.message);
})
})()
return false;
});
});

View File

@@ -483,7 +483,7 @@
// 点击事件
util.on({
'load': () => {
util.openWin({ url: 'https://gitee.com/technical-laohu/mpay' });
util.openWin({ url: 'https://gitee.com/technical-laohu/mpay/releases' });
},
'doc': () => {
util.openWin({ url: 'https://f0bmwzqjtq2.feishu.cn/docx/HBVrdrsACo36bzxUCSPcjOBNnyb?from=from_copylink' });

View File

@@ -198,7 +198,8 @@
<div class="layui-col-xs9">
<div class="list">
<a href="javascript:;" class="layui-font-blue" lay-on="getQrcode"
data-qrcode="<?php echo $qrcode ?>"><span class="icon pear-icon">&#xe6cc;</span></a>
data-qrcode="<?php echo $qrcode ?>" data-type="<?php echo $url_type ?>"><span
class="icon pear-icon">&#xe6cc;</span></a>
<?php echo $qrcode ?>
</div>
</div>
@@ -215,16 +216,28 @@
util.on({
getQrcode: function () {
(async () => {
const qrcode_data = this.getAttribute("data-qrcode")
const qrcode_img = await getQrcode(qrcode_data, QR);
layer.open({
type: 1,
area: ['200px', '200px'],
title: false,
closeBtn: 0,
shadeClose: true,
content: `<img width="100%" src="${qrcode_img}">`
});
const type_data = this.getAttribute("data-type");
const qrcode_data = this.getAttribute("data-qrcode");
if (type_data == 1) {
layer.open({
type: 1,
area: ['200px', '200px'],
title: false,
closeBtn: 0,
shadeClose: true,
content: `<img width="100%" src="${qrcode_data}">`
});
} else if (type_data == 0) {
const qrcode_img = await getQrcode(qrcode_data, QR);
layer.open({
type: 1,
area: ['200px', '200px'],
title: false,
closeBtn: 0,
shadeClose: true,
content: `<img width="100%" src="${qrcode_img}">`
});
}
})()
}
});

View File

@@ -46,6 +46,11 @@
</div>
</div>
</div>
<div class="main-container">
<div class="layui-text">
<p>部分平台的账户密码填写比较特殊,<strong><a href="https://f0bmwzqjtq2.feishu.cn/docx/HBVrdrsACo36bzxUCSPcjOBNnyb?from=from_copylink" target="_blank"><strong>请查看文档</strong></a></p>
</div>
</div>
</div>
<div class="bottom">
<div class="button-container">

View File

@@ -40,6 +40,11 @@
</div>
</div>
</div>
<div class="main-container">
<div class="layui-text">
<p>终端编号如何填写,<strong><a href="https://f0bmwzqjtq2.feishu.cn/docx/HBVrdrsACo36bzxUCSPcjOBNnyb?from=from_copylink" target="_blank"><strong>请查看文档</strong></a></p>
</div>
</div>
</div>
<div class="bottom">
<div class="button-container">

View File

@@ -35,7 +35,7 @@
<!-- <?php } ?> -->
<!-- <?php if ($platform == 'alipay') { ?> -->
<option value="alipay1">收钱码</option>
<option value="alipay2">经营码</option>
<!-- <option value="alipay2">经营码</option> -->
<!-- <?php } ?> -->
</select>
</div>

View File

@@ -40,16 +40,28 @@
util.on({
getQrcode: function () {
(async () => {
const type_data = this.getAttribute("data-type");
const qrcode_data = this.getAttribute("data-qrcode")
const qrcode_img = await getQrcode(qrcode_data, QR);
layer.open({
type: 1,
area: ['200px', '200px'],
title: false,
closeBtn: 0,
shadeClose: true,
content: `<img width="100%" src="${qrcode_img}">`
});
if (type_data == 1) {
layer.open({
type: 1,
area: ['200px', '200px'],
title: false,
closeBtn: 0,
shadeClose: true,
content: `<img width="100%" src="${qrcode_data}">`
});
} else if (type_data == 0) {
const qrcode_img = await getQrcode(qrcode_data, QR);
layer.open({
type: 1,
area: ['200px', '200px'],
title: false,
closeBtn: 0,
shadeClose: true,
content: `<img width="100%" src="${qrcode_img}">`
});
}
})()
},
edit: function () {
@@ -104,7 +116,7 @@
<div class="layui-card-body">
<span class="layui-badge layui-bg-cyan">终端编号</span><span class="layui-badge layui-bg-gray">${item.channel}</span><br>
<span class="layui-badge layui-bg-cyan">收款地址</span><span class="layui-badge layui-bg-gray">${qrcode}</span>
<a href="javascript:;" class="layui-font-blue" lay-on="getQrcode"data-qrcode="${item.qrcode}"><span class="icon pear-icon">&#xe6cc;</span></a><br>
<a href="javascript:;" class="layui-font-blue" lay-on="getQrcode" data-qrcode="${item.qrcode}" data-type="${item.type}"><span class="icon pear-icon">&#xe6cc;</span></a><br>
<span class="layui-badge layui-bg-cyan">最后使用</span><span class="layui-badge layui-bg-gray">${item.last_time}</span><br>
<span class="layui-badge layui-bg-cyan">启用状态</span><span class="layui-badge layui-bg-${item.state == 1 ? 'green' : 'gray'}">${item.state == 1 ? '启用' : '禁用'}</span><br>
<div class="layui-btn-group edit">

View File

@@ -5,6 +5,11 @@
<meta charset="utf-8">
<title>收款管理</title>
<link rel="stylesheet" href="/component/pear/css/pear.css" />
<style>
.account-trade {
padding: 15px;
}
</style>
</head>
<body class="pear-container">
@@ -91,6 +96,13 @@
}
}}
</script>
<script type="text/html" id="account-trade">
{{# if (d.platform == '微信支付' || d.platform == '支付宝') { }}
<span class="layui-badge layui-bg-gray"></span>
{{# } else { }}
<a href="javascript:;" lay-event="queryTrade" data-url="/api/PayManage/getAccountTrade?pid={{= d.pid }}&aid={{= d.id }}"><span class="layui-badge layui-bg-green">查询</span></a>
{{# } }}
</script>
<script src="/component/layui/layui.js"></script>
<script src="/component/pear/pear.js"></script>
<script>
@@ -119,6 +131,7 @@
{ title: '启用状态', field: 'state', align: 'center', templet: '#account-state' },
{ title: '监听模式', field: 'pattern', align: 'center' },
{ title: '监听地址 / 自定义模版', field: 'checkUrl', align: 'center', minWidth: 300, event: 'copy', templet: '#account-checkUrl' },
{ title: '收款平台流水', field: 'trade', align: 'center', templet: '#account-trade' },
{ title: '收款码数量', field: 'channel', align: 'center', templet: '<div><a href="javascript:;" lay-event="channelList"><span class="layui-badge layui-bg-green">{{= d.channel }}</span></a></div>' },
{ title: '操作', align: 'center', fixed: 'right', templet: '<div><a href="javascript:;" class="layui-font-green" lay-event="edit"><strong>编辑</strong></a></div>' }
]]
@@ -147,18 +160,21 @@
account.editAccount(id);
} else if (obj.event === 'channelList') {
account.channelList(id);
} else if (obj.event === 'queryTrade') {
const url = this.getAttribute('data-url');
account.getAccountTrade(url);
}
});
table.on('toolDouble(account-table)', function (obj) {
if (obj.event === 'copy') {
const text = obj.tr[0].querySelector('td[data-field="checkUrl"]>div').innerText;
if (navigator.clipboard) {
navigator.clipboard.writeText(text).then(() => {
layer.msg('复制成功');
});
} else {
layer.msg('复制失败,请手动复制');
}
navigator.clipboard.writeText(text).then(() => {
layer.msg('复制成功');
});
} else {
layer.msg('复制失败,请手动复制');
}
}
});
// 表格头部按钮事件
@@ -274,6 +290,32 @@
content: `/Order/testPay`,
});
}
// 查询平台收银流水
account.getAccountTrade = async function (url) {
const res = await fetch(url);
if (res.status !== 200) {
layer.msg('请求失败,请重试!', { tips: 2, time: 1200 });
return false;
}
const rec_info = await res.json();
if (rec_info.code === 0) {
const data = rec_info.data;
let html = '';
data.forEach(order => {
html += `<tr><td>${order.order_no}</td><td>${order.channel}</td><td>${order.price}</td><td>${order.payway}</td></tr>`;
});
layer.open({
type: 1,
title: '收银流水(近3分钟)',
area: ['720px', '480px'],
content: `<div class="account-trade"><table class="layui-table"><thead><tr><th>订单流水</th><th>终端编号</th><th>收款金额</th><th>支付方式</th></tr></thead><tbody>${html}</tbody></table></div>`
});
} else {
layer.msg(rec_info.msg, { icon: 2, time: 1200 });
}
}
// 请求封装
async function httpJSON(url, info) {
const res = await fetch(url, { method: 'post', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(info) });

View File

@@ -90,11 +90,11 @@
// 表头工具事件
table.on('toolbar(plugin-table)', function (obj) {
if (obj.event === 'showAllPlugin') {
table.reload('plugin-table', { where: { show: 0 }, done: () => { plugin.changClass(obj.event) } });
table.reloadData('plugin-table', { where: { show: 0 }, done: () => { plugin.changClass(obj.event) } });
} else if (obj.event === 'showInstalled') {
table.reload('plugin-table', { where: { show: 1 }, done: () => { plugin.changClass(obj.event) } });
table.reloadData('plugin-table', { where: { show: 1 }, done: () => { plugin.changClass(obj.event) } });
} else if (obj.event === 'showWaitInstall') {
table.reload('plugin-table', { where: { show: 2 }, done: () => { plugin.changClass(obj.event) } });
table.reloadData('plugin-table', { where: { show: 2 }, done: () => { plugin.changClass(obj.event) } });
}
});
// 单元格事件

View File

@@ -181,9 +181,9 @@
<div class="layui-input-block">
<div class="layui-form-mid layui-elip"
style="margin-left: 10px;color: #5f5f5f;float: none;">
<?php echo '并且 是 APP包名 相等 com.tencent.mm<br />并且 是 通知标题 相等 微信支付<br />&nbsp;或者 是 通知标题 相等 微信收款助手' ?>
<?php echo '并且 是 APP包名 相等 com.tencent.mm<br />并且 是 通知标题 相等 微信支付<br />[空格]或者 是 通知标题 相等 微信收款助手' ?>
<a href="javascript:;" lay-on="copyinfo"
data-info='<?php echo "并且 是 APP包名 相等 com.tencent.mm\n并且 是 通知标题 相等 微信支付\n 或者 是 通知标题 相等 微信收款助手" ?>'
data-info='<?php echo "并且 是 APP包名 相等 com.tencent.mm\n并且 是 通知标题 相等 微信支付\n[空格]或者 是 通知标题 相等 微信收款助手" ?>'
style="float: right;" title="复制"><span
class="icon pear-icon pear-icon-survey"></span></a>
</div>