Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f9c804c1e5 | ||
|
|
2515515182 | ||
|
|
87563d01d7 | ||
|
|
16f4438a16 | ||
|
|
29c894faae | ||
|
|
a5d09d0a26 | ||
|
|
504aa6c284 | ||
|
|
e65c0e9173 | ||
|
|
2c1ece3eb2 | ||
|
|
ebcac8a9aa | ||
|
|
4eef7f0797 | ||
|
|
dfb8edff22 | ||
|
|
05c1b08e95 | ||
|
|
de848ab32b | ||
|
|
3f5866ea16 | ||
|
|
fb2f8f2f2c | ||
|
|
62989e4100 | ||
|
|
eeb877bd87 | ||
|
|
e9f64988a9 | ||
|
|
f62d4cd103 | ||
|
|
331b0b2d56 | ||
|
|
e5871d91c5 | ||
|
|
898daee64c | ||
|
|
b266e1ad7c | ||
|
|
e85e71f8d9 | ||
|
|
6a8ec71b8d | ||
|
|
7f77c5a1dd | ||
|
|
d61ec241f1 | ||
|
|
4aa9feb5be | ||
|
|
16d4b989d8 | ||
|
|
8e5981f640 | ||
|
|
8a52346425 | ||
|
|
21db31e8a0 | ||
|
|
b5d128d6b4 | ||
|
|
55fd7f9335 | ||
|
|
36b6cdba8c | ||
|
|
45d26dc524 | ||
|
|
fb025542f2 | ||
|
|
513b0f521d | ||
|
|
27867c65c5 | ||
|
|
6b3f6c5448 | ||
|
|
6c3106a6aa | ||
|
|
8f1d6ce013 | ||
|
|
40a287ad41 | ||
|
|
37cfebd7eb | ||
|
|
2cd6eafb02 | ||
|
|
cd9d416bc1 | ||
|
|
2cd77c824c | ||
|
|
6d0e7ce296 | ||
|
|
1f02d14a80 | ||
|
|
98a405c745 | ||
|
|
edeebfdfef | ||
|
|
7757d51ae3 | ||
|
|
40f07d9023 | ||
|
|
16513960ad | ||
|
|
a9df43fd04 |
12
!!.env
@@ -1,12 +0,0 @@
|
||||
APP_DEBUG = true
|
||||
|
||||
DB_TYPE = mysql
|
||||
DB_HOST = 127.0.0.1
|
||||
DB_NAME = test
|
||||
DB_USER = test
|
||||
DB_PASS = 123456
|
||||
DB_PORT = 3306
|
||||
DB_CHARSET = utf8
|
||||
DB_PREFIX = mpay_
|
||||
|
||||
DEFAULT_LANG = zh-cn
|
||||
371
README.md
@@ -1,13 +1,10 @@
|
||||
|
||||
|
||||
|
||||
# 码支付
|
||||
|
||||

|
||||
|
||||
**码支付[mpay]是一款便捷收款工具,专注于个人免签收款,通过普通收款码即可实现收款通知自动回调,支持绝大多数商城系统**
|
||||
|
||||
## 项目地址
|
||||
# 项目地址
|
||||
|
||||
| gitee | github |
|
||||
| :----------: | :------------: |
|
||||
@@ -15,44 +12,56 @@
|
||||
|
||||
点以上图标进入项目页面,可查看最新发行版
|
||||
|
||||
## 项目说明
|
||||
# 项目说明
|
||||
|
||||
### 原因
|
||||
## 原因
|
||||
|
||||
之前在工作之余,尝试过很多副业项目,基本都是跟互联网相关的。例如使用**Wordpress和zibll主题**建资源站点,用**微擎**和**微课堂V2**卖课程,使用**异次元发卡**和**独角数卡**程序卖代理的**微信营销软件**,其中遇到的让我最头痛的问题就是收款问题。
|
||||
|
||||
正规官方渠道,如微信支付宝申请相关支付接口,必须要签约,且有营业执照等相关资质,这对于只想搞想副业的我实在是没必要,也麻烦,后期的事情也多。于是在网上找到了一些解决方案来解决我的在线收款问题。
|
||||
|
||||
### 方案
|
||||
---
|
||||
|
||||
## 方案
|
||||
|
||||
市场也有很多针对此类需求的平台,也有不少合适的解决方案
|
||||
|
||||
#### 虎皮椒/迅虎/蓝兔支付
|
||||
### 虎皮椒/迅虎/蓝兔支付
|
||||
|
||||
这些平台都是微信支付宝等官方平台的支付服务商,能签约个人商户,实现在线收款,一些API支付接口可以直接调用,跟官方自己申请的几乎差不多。
|
||||
优点是跟官方申请的支付接口差不多,N+1到账
|
||||
缺点就是审核比较严,动不动投诉封商户号,一些羊毛党就爱整你,你是一点办法没有,另外开户基本都要交开户费,50-200不等,且还需要额外收取手续费。
|
||||
|
||||
#### 彩虹易支付
|
||||
### 彩虹易支付
|
||||
|
||||
彩虹易支付是一套收款程序,有专门的公司或平台用这套程序搭建一个收款平台,使用自己的微信或支付宝等官方账户来进行收款,你自己注商户号,运营方提供代收款服务,然后给你打款结算。
|
||||
优点是审核不严,处理比较灵活,适合个人,技术支持比较好,注册可使用(有些需要注册费)
|
||||
缺点就是平台容易卷款跑路,也没有什么有效监管,钱收不回来就亏大了(这也是最大的问题)
|
||||
|
||||
#### 源支付
|
||||
### 源支付/V免签
|
||||
|
||||
源支付也是一套收款程序,有个人版和商户版,市面上能搜到的大部分都是商户版,可以入驻,使用自己的个人微信支付宝二维码收款。
|
||||
源支付程序的设计思路主要是通过在手机或电脑上安装消息监听软件,用来监听获取微信和支付宝的收款到账通知来实现的支付成功回调的。**方法很实用,本程序也添加了该功能插件,免费**。
|
||||
|
||||
V免签是一款开源免费适用于个人收款使用的收款程序,原理同源支付类似。
|
||||
程序的设计思路主要是通过在手机或电脑上安装消息监听软件,用来监听获取微信和支付宝的收款到账通知来实现的支付成功回调的。**方法很实用,本程序也添加了该功能插件,免费**。
|
||||
|
||||

|
||||
|
||||
只是这种思路,有一些小问题:
|
||||
|
||||
* 平台容易因为资质问题导致关站;
|
||||
* 收取的手续费价格偏高;
|
||||
* 个人码在微信H5环境无法长按识别付款,能只技术PC端相机扫码付款。
|
||||
* 个人码在微信H5环境无法长按识别付款,只能通过PC端,相机扫码付款。
|
||||
* 挂机监听容易掉线,导致收款通知无法回调
|
||||
|
||||
#### 码支付(mpay)
|
||||
### 🚀️ 码支付(mpay)
|
||||
|
||||
**本程序暂只提供个人版,开源免费使用。**
|
||||
码支付是在源支付的设计思路基础上进行的改进,利用第四方**聚合收款码**来进行收款,保证收款稳定和便捷。聚合收款码个人可以申请,不需求相关资质,不用申请API接口,收银服务平台众多且实力雄厚(如拉卡拉、收钱吧等),不怕跑路。
|
||||
|
||||
码支付是在源支付的设计思路基础上进行的改进,利用第四方**聚合收款码**来进行收款,保证收款稳定和便捷不掉线。
|
||||
|
||||
聚合收款码个人可以申请,不需求相关资质,不用申请API接口,收银服务平台众多且实力雄厚(如拉卡拉、收钱吧等),不怕跑路。
|
||||
|
||||
特点如下:
|
||||
|
||||
* 免监听,不需要手机或电脑挂机监听消息,即可实现支付回调,只需要设置一个定时任务就行
|
||||
@@ -60,7 +69,9 @@
|
||||
* 个人搭建的收款系统,收款稳定,安全可控,不需要额外手续费
|
||||
* 支持多平台(聚合码服务商),多账号(聚合码商户),多渠道(门店码/店员码/桌号码等),降低异地线上收款风控风险
|
||||
|
||||
### 思路
|
||||
---
|
||||
|
||||
## 思路
|
||||
|
||||
码支付说到底就是通过二维码来进行收款,日常使用的除了微信支付宝生成的二维码外,还有一类二维码是由收款服务商提供的,它能通过一张收款二维码,同时支持**微信**、**支付宝**、**云闪付**等多渠道付款,一般称为**聚合收款码**。
|
||||
|
||||
@@ -84,12 +95,19 @@
|
||||
|
||||
当用户付款成功,并且后台检测到收款成功消息后,收钱台就会提示收款成功,并最终确认收款。
|
||||
|
||||

|
||||
|
||||
> 只有存在新订单时,且该订单与当前收款账号一致时,码支付后台才会主动登陆该账号,查询收款流水,减少频繁查询导致的可能风险
|
||||
|
||||
> 另外,在账号设置里也有两个模式可选,`单次监听`和`连续监听`,根据业务场景可以自行选择,具体使用,下面有介绍
|
||||
|
||||
## 开源声明
|
||||
# 开源声明
|
||||
|
||||
### 内置插件
|
||||
## 演示站点
|
||||
|
||||
[码支付](http://demo.stspwsc.com/) http://demo.stspwsc.com/
|
||||
|
||||
## 内置插件
|
||||
|
||||
码支付收款管理系统开源免费使用,支付插件有**免费**和**付费**版本,按需使用购买
|
||||
|
||||
@@ -97,75 +115,324 @@
|
||||
2. **支付宝**插件`alipay`默认安装,支持`收钱码`、`经营码`,2个通道,需挂机监听
|
||||
3. **收钱吧**聚合码插件`sqbpay`默认安装,无需挂机,设置定时任务即可
|
||||
|
||||
### 技术架构
|
||||
## 技术架构
|
||||
|
||||
须用`Thinkphp8`框架,前端UI使用`Layui 2.9`+`PearAdmin`后台
|
||||
使用`Thinkphp8`框架,PHP版本 > 8.0(推荐8.2),前端UI使用`Layui 2.9`+`PearAdmin`后台
|
||||
|
||||
## 安装和使用
|
||||
---
|
||||
|
||||
### 全新安装
|
||||
# 安装和使用
|
||||
|
||||
#### 源码下载
|
||||
## 全新安装
|
||||
|
||||
#### 安装配置
|
||||
以下演示基于**云服务器**环境+**宝塔面板**安装,云服务器购买可以去阿里云、腾讯云等平台,宝塔面板安装教程参考[宝塔面板安装教程](https://www.bt.cn/new/download.html)
|
||||
|
||||
### 🚀️ 源码下载
|
||||
|
||||
**点击下载 [码支付 v1版本](https://gitee.com/technical-laohu/mpay/releases)**
|
||||
|
||||
**更多版本请关注发行版更新记录**
|
||||
|
||||
### 安装配置
|
||||
|
||||
以**宝塔面板**示例,其他服务器管理面板可以参考
|
||||
|
||||
##### 新建站点与数据库
|
||||
通过宝塔面板登陆管理后台,新建PHP站点和数据库,并确认创建
|
||||
|
||||
#### 仿静态配置
|
||||

|
||||
|
||||
#### 安装步骤
|
||||
在网站列表页面,点击创建的网站的根目录,进入文件管理
|
||||
|
||||
### 聚合码使用
|
||||

|
||||
|
||||
#### 申请收款码
|
||||
文件夹里面有一些默认文件,不用管他
|
||||
|
||||
#### 安装插件
|
||||

|
||||
点击上传文件,将源码压缩包上传到该文件夹,并解压到前文件夹
|
||||
|
||||
#### 添加账号
|
||||

|
||||
|
||||
#### 添加收款码
|
||||
**将`mpay`文件夹里面的所有文件,复制到当前根目录下**,返回网站列表管理页面
|
||||
|
||||
#### 设置监听
|
||||
> 注意,压缩包文件打包的是一个名为`mpay`的文件夹,需要将代码文件夹里面的所有文件复制出来,放到创建的网站根目录下,
|
||||
|
||||
##### 订单监听
|
||||
### 运行目录&仿静态 配置
|
||||
|
||||
##### 账号收款监听
|
||||
点击网站名,进入网站配置设置页面
|
||||
|
||||
#### 支付测试
|
||||

|
||||
|
||||
#### 服务商支持
|
||||
选择**网站目录**,运行目录选择`public`,保存
|
||||
|
||||
### 微信/支付宝使用
|
||||

|
||||
|
||||
#### 添加账号
|
||||
选择**伪静态**,模版选择`thinkphp`,即可自动填写,保存
|
||||
|
||||
#### 添加收钱码
|
||||

|
||||
|
||||
#### 挂机监听收款通知
|
||||
### 安装步骤
|
||||
|
||||
##### 软件下软(安卓)
|
||||
在浏览器输入`http://你的域名/install`,进入程序安装界面,按照提示进行填写提交
|
||||
|
||||
##### 功能配置
|
||||

|
||||
|
||||
##### 收款通知测试
|
||||
数据库配置相关信息,在服务器管理面板里查找
|
||||
|
||||
### 补充说明
|
||||

|
||||
|
||||
### 页面展示
|
||||
---
|
||||
|
||||

|
||||
## ❤️ 聚合码使用
|
||||
|
||||

|
||||
如果本身就有聚合码收钱码最好,没有就需要提前去各收银服务商申请,申请也不复杂
|
||||
|
||||

|
||||
### 申请收款码
|
||||
|
||||

|
||||
以下列出一些常见收款服务平台,可以按需申请,个人直接申请小微商户即可
|
||||
|
||||

|
||||
| 平台 | 官网 |
|
||||
|--------|--------------------------------------|
|
||||
| 收钱吧 | https://www.shouqianba.com/ |
|
||||
| 小Y经营 | https://xym.ysepay.com/ |
|
||||
| 码钱 | https://m.hkrt.cn/ |
|
||||
| 拉卡拉 | https://customer.lakala.com/ |
|
||||
| 盛付通 | https://b.shengpay.com/ |
|
||||
|
||||

|
||||
> 申请可以去官方平台注册账号等客服电话,或者在社群里询问(有很多人有代办资质),实在找不到的,可以去淘宝上的官方店买个二维码卡牌贴纸,然后询问客服如何开通账号就行,会有专员联系你开通。
|
||||
|
||||

|
||||
### 安装插件
|
||||
|
||||
程序默认安装有**微信支付**`wxpay`、**支付宝**`alipay`、**收钱吧**`sqbpay`三个插件,基本能满足大家的日常收款需求。
|
||||
|
||||
如果需要其他收银平台插件,可以在插件中心自行安装,插件中心没有的,也可以联系作者定制开发。
|
||||
|
||||
### 添加账号
|
||||
|
||||
除**微信支付**`wxpay`、**支付宝**`alipay`之外,所有的收款平台均为聚合码收款平台
|
||||
|
||||
以**收钱吧**`sqbpay`为例,添加账号时,需要填写**收钱吧**商户管理中心的**登陆账号**和**登陆密码**,需要使用时,插件会自动在后台登陆账号并查询相关订单流水信息。
|
||||
|
||||

|
||||

|
||||
|
||||
**监听模式**说明,分为`单次监听`和`连续监听`,
|
||||
|
||||

|
||||
|
||||
`单次监听`是在收银台页面,用户扫码支付成功后,需要手动点击**确认支付**,后台才会登陆查询该账号的收款流水情况,**点击一次,查询一次,不点击,则不会查询**,降低密集查询可能导致的账户风控
|
||||
|
||||

|
||||
|
||||
`连续监听`是在创建订单之后,**在订单有效期内且订单未完成状态时**,插件会一直连续不断的查询账户流水详情,直到过期或者成功收款,用户不需要主动点击确认
|
||||
|
||||

|
||||
|
||||
> 使用哪种监听模式,视应用场景自行选择,一般选择**连续监听**即可,如果日常订单比较密集,可以选择**单次监听**,或多添加几个账号,减少密集查询风控
|
||||
|
||||
### 添加收款码
|
||||
|
||||
点击可以查看当前账号的所有收款码
|
||||
|
||||

|
||||
|
||||
点击**添加收款码**后,可以正常填写二维码信息
|
||||
|
||||
**终端编号**需要填写当前收款码在收银服务商系统内的编码,有的可以直接在收款二维码解析的**链接里找到**,有的需要**登陆商户管理中心**,去订单详情里查询才能知道
|
||||
|
||||
🚀️ 具体各个平台的终端编号如何获取,可以去**程序后端控制台主页**的`项目文档`查看🚀️
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
收款样式有两个选项`付款链接`和`图片地址`,根据实际情况选择
|
||||
|
||||
**付款链接:** 将二维码的内容解析成字符串保存,使用时再生成二维码
|
||||
|
||||
**图片地址:** 上传二维码图片到服务器,或引用http远程图片地址
|
||||
|
||||
> 一般的**聚合收款码都解析成文字保存**即可,前端展示时会重新生成二维码,无法解析的就上传图片
|
||||
|
||||
### 🚀️ 设置监听
|
||||
|
||||
#### 宝塔任务计划
|
||||
|
||||
等会儿需要在这里设置任务监听
|
||||
|
||||

|
||||
|
||||
#### 新订单监听
|
||||
|
||||
一个站点只需要设置一个定时任务,每次访问都会查询一次数据库,并生成新订单数据缓存
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
#### 账号监听
|
||||
|
||||
**每个账号都需要单独设置一个定时任务**,每次访问都会检索新订单缓存数据,如果存在该账户的新订单,插件就会去查询服务商后台的订单流水,并通知相关收款处理程序
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
### 支付测试
|
||||
|
||||
只开启一个账号,然后点击`支付测试`,正常支付后,观察是否成功回调,如果不能回调请重新检查配置,或再次详细查看**程序后端控制台主页**的`项目文档`
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## ❤️ 微信/支付宝使用
|
||||
|
||||
微信/支付宝生成的收款码,需要挂机监听收款消息,基本情况上面有介绍,因为使用广泛,所以也单独添加进来,可以实现正常收款回调
|
||||
|
||||
### 添加账号
|
||||
|
||||
收款平台选择`微信支付`或`支付宝`,收款账号就填写`微信支付`或`支付宝`的账号,与**聚合码支付**不同,此处填写的账号主要用来做区分,不会登陆后台
|
||||
|
||||

|
||||
|
||||
### 添加收钱码
|
||||
|
||||
点击可以查看当前账号的所有收款码
|
||||
|
||||

|
||||
|
||||
点击**添加收款码**后,可以正常填写二维码信息
|
||||
|
||||
`微信支付`和`支付宝`的终端编号是自动生成的,不可手动填写,分别对应当前账号的不同收款通道,支持`个人码`和`赞赏码`
|
||||
|
||||

|
||||
|
||||
收款样式有两个选项`付款链接`和`图片地址`,根据实际情况选择
|
||||
|
||||
**付款链接:** 将二维码的内容解析成字符串保存,使用时再生成二维码
|
||||
|
||||
**图片地址:** 上传二维码图片到服务器,或引用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)**
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
#### 🚀️ 功能配置
|
||||
|
||||
找一台不常用的安卓系统手机,下载安装好`SmsForwarder`之后,点开进行配置,同时打开码支付后台**用户中心**页面,查看相关配置参数
|
||||
|
||||

|
||||
|
||||
##### **通用设置**
|
||||
|
||||
1. 开启转发应用通知,只针对应用软件才会转发
|
||||
2. 保活措施全部开启(实际情况看手机环境)
|
||||
3. 通知栏文案可以自定义,方便自己运维,可参考图片
|
||||
4. 其他默认即可,也可以参考实际情况自行设置
|
||||
|
||||

|
||||
|
||||
##### **发送通道**
|
||||
|
||||
添加发送通道,选择`Webhook`类型
|
||||
|
||||

|
||||
|
||||
**编辑规则**
|
||||
|
||||
1. 通道名称自行命名,方便区别就行
|
||||
2. 请求方式选择`POST`方式
|
||||
3. Webhook Server 地址**用户中心**查看
|
||||
4. **消息模版**在**用户中心**查看
|
||||
5. **Secert**密钥在**用户中心**查看
|
||||
6. 应签关键字`200`
|
||||
|
||||
填写完成点击**保存**即可
|
||||
|
||||

|
||||
|
||||
##### **转发规则**
|
||||
|
||||
添加应用转发规则,**微信**和**支付宝**需要分别设置,请注意选择**应用**规则
|
||||
|
||||

|
||||
|
||||
**具体设置**
|
||||
|
||||
1. 选择发送通道
|
||||
2. **匹配字段**选择**多重匹配**,**匹配的值**去**用户中心**复制,然后粘贴过来
|
||||
3. 开启**启用自定义模版**,内容填写去码支付后台**账号列表**里复制,粘贴过来
|
||||
|
||||
**注意:** 微信支付规则里,第三行的`[空格]`需要替换成真实的` `空格
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
##### **转发日志**
|
||||
|
||||
这里可以查看所有的转发记录,点击也可以查看消息详情
|
||||
|
||||

|
||||
|
||||
##### **注意事项**
|
||||
|
||||
用户中心后台里的配置APP包名不一定与手机应用里的包名一致,需要查看一下,如果有区别,请修改成实际本机显示的APP包名,下面有图片演示操作
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
#### 收款通知测试
|
||||
|
||||
---
|
||||
|
||||
# 补充说明
|
||||
|
||||
* 该软件仅适用于个人线上免签收款,避免人工审核确认收款的繁琐步骤。请勿用于诈骗、黑灰产业,如有此类行为,后果自负。
|
||||
* 插件中心目前还在开发中,不支持在线安装插件,如果有购买插件或定制其他收银平台插件的需求,可以联系作者,Wechat:**K103516**
|
||||
|
||||
# 页面展示
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
deny from all
|
||||
@@ -13,13 +13,14 @@ class ConsoleController extends BaseController
|
||||
// 后台主页
|
||||
public function index()
|
||||
{
|
||||
View::assign('version', 'v1');
|
||||
return View::fetch();
|
||||
}
|
||||
// 管理菜单
|
||||
public function menu()
|
||||
{
|
||||
// 加载菜单配置
|
||||
$menu = \think\facade\Config::load("extendconfig/menu", 'extendconfig');
|
||||
$menu = \think\facade\Config::load("extend/menu", 'extend');
|
||||
return json($menu);
|
||||
}
|
||||
// 管理菜单
|
||||
|
||||
@@ -22,7 +22,6 @@ class IndexController
|
||||
}
|
||||
public function test()
|
||||
{
|
||||
|
||||
return app()->getBasePath();
|
||||
return View::fetch();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,27 @@ class InstallController
|
||||
}
|
||||
|
||||
public function install(Request $request)
|
||||
{
|
||||
// 检查是否已经安装过
|
||||
if ($this->checkLock()) {
|
||||
return json(backMsg(1, '已经安装'));
|
||||
};
|
||||
// 检查环境
|
||||
$envCheck = $this->checkEnvironment();
|
||||
if ($envCheck !== true) {
|
||||
return json(backMsg(1, $envCheck));
|
||||
};
|
||||
// 获取表单提交的数据库配置信息
|
||||
$dbConfig = $request->post();
|
||||
// 保存数据库配置信息到配置文件
|
||||
if ($this->saveDbConfig($dbConfig) === false) {
|
||||
return json(backMsg(1, '配置保存失败'));
|
||||
} else {
|
||||
return json(backMsg(0, '配置保存成功'));
|
||||
};
|
||||
}
|
||||
// 初始化数据库
|
||||
public function init(Request $request)
|
||||
{
|
||||
// 检查是否已经安装过
|
||||
if ($this->checkLock()) {
|
||||
@@ -28,9 +49,6 @@ class InstallController
|
||||
// 获取表单提交的数据库配置信息
|
||||
$dbConfig = $request->post();
|
||||
|
||||
// 保存数据库配置信息到配置文件
|
||||
$this->saveDbConfig($dbConfig);
|
||||
|
||||
// 连接数据库并建表
|
||||
$is_succ_tb = $this->createTables();
|
||||
|
||||
@@ -48,12 +66,27 @@ class InstallController
|
||||
$this->setLock();
|
||||
return json(backMsg(0, '安装成功'));
|
||||
}
|
||||
|
||||
private function checkEnvironment()
|
||||
{
|
||||
// 检查PHP版本
|
||||
if (version_compare(PHP_VERSION, '8.0', '<')) {
|
||||
return 'PHP版本必须大于等于8.0';
|
||||
}
|
||||
// 检查文件上传写入权限
|
||||
if (!is_writable(sys_get_temp_dir())) {
|
||||
return '文件上传目录没有写入权限';
|
||||
}
|
||||
// 检查Fileinfo扩展是否安装
|
||||
if (!extension_loaded('fileinfo')) {
|
||||
return 'Fileinfo扩展未安装';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
private function saveDbConfig($dbConfig)
|
||||
{
|
||||
$envPath = app()->getRootPath() . '.env';
|
||||
$envContent = <<<EOT
|
||||
APP_DEBUG = true
|
||||
APP_DEBUG = false
|
||||
|
||||
DB_TYPE = mysql
|
||||
DB_HOST = {$dbConfig['host']}
|
||||
@@ -66,7 +99,7 @@ DB_PREFIX = mpay_
|
||||
|
||||
DEFAULT_LANG = zh-cn
|
||||
EOT;
|
||||
file_put_contents($envPath, $envContent);
|
||||
return file_put_contents($envPath, $envContent);
|
||||
}
|
||||
|
||||
private function createTables()
|
||||
|
||||
@@ -6,6 +6,7 @@ use think\Request;
|
||||
use think\facade\View;
|
||||
use app\model\User;
|
||||
use app\model\Order;
|
||||
use app\model\PayAccount;
|
||||
use app\model\PayChannel;
|
||||
|
||||
class PayController
|
||||
@@ -19,70 +20,41 @@ class PayController
|
||||
'POST' => $request->post(),
|
||||
default => []
|
||||
};
|
||||
if (!$req_data) {
|
||||
return '参数错误';
|
||||
}
|
||||
if (!$req_data) return '参数错误';
|
||||
// 验证签名
|
||||
$key = User::where('pid', $req_data['pid'])->where('state', 1)->value('secret_key');
|
||||
if (!$key) {
|
||||
return '用户禁用或不存在';
|
||||
}
|
||||
if (!$key) return '用户禁用或不存在';
|
||||
$sign_str = self::getSign($req_data, $key);
|
||||
if ($req_data['sign'] === $sign_str) {
|
||||
if ($req_data['sign'] !== $sign_str) return '签名错误';
|
||||
// 检查商户订单
|
||||
$out_trade_no = Order::where('out_trade_no', $req_data['out_trade_no'])->value('out_trade_no');
|
||||
if (!$out_trade_no) {
|
||||
if ($out_trade_no) return '订单提交重复';
|
||||
// 创建新订单
|
||||
$order_id = Order::createOrder($req_data);
|
||||
if ($order_id) {
|
||||
if (!$order_id) return '创建订单失败';
|
||||
return redirect("/Pay/console/{$order_id}");
|
||||
} else {
|
||||
return '创建订单失败';
|
||||
}
|
||||
} else {
|
||||
return '订单提交重复';
|
||||
}
|
||||
} else {
|
||||
return '签名错误';
|
||||
}
|
||||
}
|
||||
// api提交订单
|
||||
public function mapi(Request $request)
|
||||
{
|
||||
if ($request->isPost()) {
|
||||
if (!$request->isPost()) return '请使用POST方式提交';
|
||||
$req_data = $request->post();
|
||||
if (!$req_data) {
|
||||
$req_data = $request->get();
|
||||
if (!$req_data) {
|
||||
return '参数错误';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return '请使用POST方式提交';
|
||||
}
|
||||
if (!$req_data) $req_data = $request->get();
|
||||
if (!$req_data) return '参数错误';
|
||||
// 验证签名
|
||||
$key = User::where('pid', $req_data['pid'])->where('state', 1)->value('secret_key');
|
||||
if (!$key) {
|
||||
return '用户禁用或不存在';
|
||||
}
|
||||
if (!$key) return '用户禁用或不存在';
|
||||
$sign_str = self::getSign($req_data, $key);
|
||||
if ($req_data['sign'] === $sign_str) {
|
||||
if ($req_data['sign'] !== $sign_str) return '签名错误';
|
||||
// 检查商户订单
|
||||
$out_trade_no = Order::where('out_trade_no', $req_data['out_trade_no'])->value('out_trade_no');
|
||||
if (!$out_trade_no) {
|
||||
if ($out_trade_no) return '订单提交重复';
|
||||
// 创建新订单
|
||||
$order_id = Order::createOrder($req_data);
|
||||
if ($order_id) {
|
||||
if (!$order_id) return '创建订单失败';
|
||||
$payurl = $request->domain() . "/Pay/console/{$order_id}";
|
||||
$info = ['code' => 1, 'msg' => '订单创建成功', 'trade_no' => $order_id, 'qrcode' => $payurl];
|
||||
return json($info);
|
||||
} else {
|
||||
return '创建订单失败';
|
||||
}
|
||||
} else {
|
||||
return '订单提交重复';
|
||||
}
|
||||
} else {
|
||||
return '签名错误';
|
||||
}
|
||||
}
|
||||
// 收银台
|
||||
public function console($order_id = '')
|
||||
@@ -125,7 +97,12 @@ class PayController
|
||||
$sign = self::getSign($notify, $user_key);
|
||||
$notify['sign'] = $sign;
|
||||
// 跳转通知URL
|
||||
$res_return_url = '';
|
||||
if (strpos($act_order->return_url, '?')) {
|
||||
$res_return_url = $act_order->return_url . '&' . http_build_query($notify);
|
||||
} else {
|
||||
$res_return_url = $act_order->return_url . '?' . http_build_query($notify);
|
||||
}
|
||||
// 响应消息
|
||||
$data['order_id'] = $act_order->order_id;
|
||||
$data['passtime'] = $passtime > 0 ? $passtime : 0;
|
||||
@@ -157,31 +134,20 @@ class PayController
|
||||
}
|
||||
}
|
||||
// 处理收款通知
|
||||
public function payHeart(Request $request)
|
||||
private function payHeart(array $records, array $config)
|
||||
{
|
||||
$pid = $request->get('pid');
|
||||
$aid = $request->get('aid');
|
||||
$sign = $request->get('sign');
|
||||
// 检测请求参数
|
||||
if (!($pid && $aid && $sign)) {
|
||||
return json(['code' => 0, 'msg' => '参数错误']);
|
||||
}
|
||||
$pid = $config['pid'];
|
||||
$aid = $config['aid'];
|
||||
// 检测收款通知
|
||||
$payList = $request->post();
|
||||
if (!$payList) {
|
||||
if (!$records) {
|
||||
return json(['code' => 0, 'msg' => '空收款通知']);
|
||||
}
|
||||
// 签名验证
|
||||
$is_user = User::checkUser($pid, $sign);
|
||||
if (!$is_user) {
|
||||
return json(['code' => 0, 'msg' => '签名错误']);
|
||||
}
|
||||
// 当前用户账号
|
||||
$query = ['pid' => $pid, 'aid' => $aid];
|
||||
// 排除已支付订单
|
||||
$doneOrders = Order::scope('dealOrder')->where($query)->column('platform_order');
|
||||
$new_orders = [];
|
||||
foreach ($payList as $order) {
|
||||
foreach ($records as $order) {
|
||||
if (!in_array($order['order_no'], $doneOrders)) $new_orders[] = $order;
|
||||
}
|
||||
if (!count($new_orders)) return json(['code' => 0, 'msg' => '收款通知无新消息']);
|
||||
@@ -200,7 +166,7 @@ class PayController
|
||||
// 支付渠道核对
|
||||
$is_channel = $cids[$order->cid] == $new_order['channel'];
|
||||
// 金额核对
|
||||
$is_money = $order->money == $new_order['price'];
|
||||
$is_money = $order->really_price == $new_order['price'];
|
||||
// 订单核对
|
||||
if ($is_payway && $is_channel && $is_money) {
|
||||
$res = $this->updateOrderState($order, $new_order['order_no']);
|
||||
@@ -226,107 +192,86 @@ class PayController
|
||||
$sign = self::getSign($notify, $user_key);
|
||||
$notify['sign'] = $sign;
|
||||
// 异步通知
|
||||
$res_notify = self::getHttpResponse($order->notify_url . '?' . http_build_query($notify));
|
||||
$notify_url = '';
|
||||
if (strpos($order->notify_url, '?')) {
|
||||
$notify_url = $order->notify_url . '&' . http_build_query($notify);
|
||||
} else {
|
||||
$notify_url = $order->notify_url . '?' . http_build_query($notify);
|
||||
}
|
||||
$res_notify = self::getHttpResponse($notify_url);
|
||||
if ($res_notify === 'success') {
|
||||
return ['order' => $order->order_id, 'code' => 1, 'msg' => 'notify success'];
|
||||
} else {
|
||||
return ['order' => $order->order_id, 'code' => 0, 'msg' => 'notify fail'];
|
||||
}
|
||||
}
|
||||
// [定时任务]获取收款明细,提交收款通知[本地版]
|
||||
// [定时任务]获取收款明细,提交收款通知
|
||||
public function checkPayResult(Request $request)
|
||||
{
|
||||
$req_info = $request->get();
|
||||
$req_pid = $req_info['pid'];
|
||||
$req_aid = $req_info['aid'];
|
||||
// 加载配置文件
|
||||
$config = \think\facade\Config::load("payconfig/{$req_pid}_{$req_aid}", 'payconfig');
|
||||
// 用户账号配置
|
||||
$user_config = isset($config['user']) ? $config['user'] : [];
|
||||
// 收款平台账号配置
|
||||
$pay_config = isset($config['pay']) ? $config['pay'] : [];
|
||||
// 配置检查
|
||||
if ($user_config && $pay_config) {
|
||||
// 账号配置信息
|
||||
$pid = $user_config['pid'];
|
||||
$aid = $pay_config['aid'];
|
||||
if (!($req_pid == $pid && $req_aid == $aid)) {
|
||||
return '监听收款配置不一致';
|
||||
}
|
||||
} else {
|
||||
return '监听收款配置文件名错误';
|
||||
}
|
||||
// 当前站点
|
||||
$user_config['host'] = \request()->domain();
|
||||
// 实例化支付类
|
||||
$Mpay = new \MpayClass($user_config);
|
||||
// 获取订单
|
||||
$res_new_order = $Mpay->orderHeart();
|
||||
$new_order = json_decode($res_new_order, true);
|
||||
$new_order = cache('order');
|
||||
if (!$new_order) return json(['code' => 3, 'msg' => '没有找到新订单缓存']);
|
||||
// 检测新订单
|
||||
if ($new_order['code'] !== 1) return $res_new_order;
|
||||
if ($new_order['code'] !== 1) return json($new_order);
|
||||
// 订单列表
|
||||
$order_list = $new_order['orders'];
|
||||
// 检测本账号订单
|
||||
$orders = [];
|
||||
foreach ($order_list as $key => $val) {
|
||||
if ($pid == $val['pid'] && $aid == $val['aid'] && $val['patt'] == 1) {
|
||||
if ($req_pid == $val['pid'] && $req_aid == $val['aid'] && $val['patt'] == 1) {
|
||||
$orders[] = $order_list[$key];
|
||||
}
|
||||
}
|
||||
if (!$orders) {
|
||||
return \json(['code' => 0, 'msg' => '非本账号订单或监听模式不对']);
|
||||
}
|
||||
if (!$orders) return json(['code' => 0, 'msg' => '非本账号订单或监听模式不对']);
|
||||
// 加载配置文件
|
||||
$config = PayAccount::getAccountConfig($req_aid);
|
||||
if ($config === false) return json(['code' => 4, 'msg' => '监听收款配置错误']);
|
||||
// 登陆账号
|
||||
$config = ['username' => $pay_config['account'], 'password' => $pay_config['password']];
|
||||
$pay_config = ['username' => $config['account'], 'password' => $config['password']];
|
||||
// 收款查询
|
||||
$query = $pay_config['query'];
|
||||
$query = $config['query'];
|
||||
// 实例监听客户端
|
||||
$payclient_name = $pay_config['payclass'];
|
||||
$payclient_name = $config['payclass'];
|
||||
$payclient_path = "\\payclient\\{$payclient_name}";
|
||||
$Payclient = new $payclient_path($config);
|
||||
$Payclient = new $payclient_path($pay_config);
|
||||
// 获取支付明细
|
||||
$records = $Payclient->getOrderInfo($query);
|
||||
if ($records) {
|
||||
// 提交收款记录
|
||||
$upres = $Mpay->upRecords($records, $aid);
|
||||
$upres = $this->payHeart($records, $config);
|
||||
return $upres;
|
||||
} else {
|
||||
return \json(['code' => 0, 'msg' => '查询空订单'], 320);
|
||||
return json(['code' => 0, 'msg' => '查询空订单'], 320);
|
||||
}
|
||||
}
|
||||
// [定时任务]监听新订单,生成JSON文件信息
|
||||
// [定时任务]监听新订单,生成缓存
|
||||
public function checkOrder($pid = '', $sign = '')
|
||||
{
|
||||
if (!($pid && $sign)) {
|
||||
return '参数错误';
|
||||
}
|
||||
if (!($pid && $sign)) return '参数错误';
|
||||
$is_user = User::checkUser($pid, $sign);
|
||||
$path = runtime_path() . 'order.json';
|
||||
if ($is_user) {
|
||||
$orders = Order::scope('activeOrder')->field('id,pid,aid,cid,patt')->select();
|
||||
if (!file_exists($path)) {
|
||||
file_put_contents($path, '[]');
|
||||
}
|
||||
$old_info = file_get_contents($path);
|
||||
$old_info = cache('order');
|
||||
$num = count($orders);
|
||||
if ($num > 0) {
|
||||
$info = ['code' => 1, 'msg' => "有{$num}个新订单"];
|
||||
$order_list = ['code' => 1, 'msg' => "有{$num}个新订单", 'orders' => $orders];
|
||||
if ($old_info !== json_encode($order_list)) {
|
||||
file_put_contents($path, json_encode($order_list));
|
||||
if ($old_info !== $order_list) {
|
||||
cache('order', $order_list);
|
||||
}
|
||||
return json($info);
|
||||
} else {
|
||||
$info = ['code' => 0, 'msg' => '没有新订单'];
|
||||
if ($old_info !== json_encode($info, 320)) {
|
||||
file_put_contents($path, json_encode($info, 320));
|
||||
if ($old_info !== $info) {
|
||||
cache('order', $info);
|
||||
}
|
||||
return json($info);
|
||||
}
|
||||
} else {
|
||||
$info = ['code' => 2, 'msg' => '签名错误'];
|
||||
file_put_contents($path, json_encode($info, 320));
|
||||
return json($info);
|
||||
}
|
||||
}
|
||||
@@ -337,10 +282,14 @@ class PayController
|
||||
$action = isset($info['action']) ? $info['action'] : '';
|
||||
if ($action === 'mpay') {
|
||||
$data = json_decode($info['data'], true);
|
||||
$config = \think\facade\Config::load("payconfig/{$data['pid']}_{$data['aid']}", 'payconfig');
|
||||
$payclient_path = "\\payclient\\{$config['pay']['payclass']}";
|
||||
$Payclient = new $payclient_path($info, $config, $request->domain());
|
||||
$Payclient->notify();
|
||||
if (!is_array($data)) return 200;
|
||||
if (!isset($data['aid']) || !isset($data['pid'])) return 202;
|
||||
$config = PayAccount::getAccountConfig($data['aid'], $data['pid']);
|
||||
$payclient_path = "\\payclient\\{$config['payclass']}";
|
||||
$Payclient = new $payclient_path($info, $config);
|
||||
$res = $Payclient->notify();
|
||||
if (is_int($res)) return $res;
|
||||
$this->payHeart($res, $config);
|
||||
return 200;
|
||||
} else {
|
||||
return 202;
|
||||
@@ -376,7 +325,8 @@ class PayController
|
||||
'sign_type' => 'MD5',
|
||||
];
|
||||
// 添加扩展参数
|
||||
$notify = array_merge($notify, unserialize($param->param));
|
||||
// $notify = array_merge($notify, unserialize($param->param));
|
||||
$notify['param'] = unserialize($param->param);
|
||||
return $notify;
|
||||
}
|
||||
// 请求外部资源
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
<?php
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace app\controller;
|
||||
|
||||
use think\Request;
|
||||
|
||||
class SystemController
|
||||
{
|
||||
/**
|
||||
* 显示资源列表
|
||||
*
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return '系统设置';
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示创建资源表单页.
|
||||
*
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存新建的资源
|
||||
*
|
||||
* @param \think\Request $request
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function save(Request $request)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示指定的资源
|
||||
*
|
||||
* @param int $id
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function read($id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示编辑资源表单页.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function edit($id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存更新的资源
|
||||
*
|
||||
* @param \think\Request $request
|
||||
* @param int $id
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定资源
|
||||
*
|
||||
* @param int $id
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function delete($id)
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ namespace app\controller\api;
|
||||
use app\BaseController;
|
||||
use app\model\PayAccount;
|
||||
use app\model\PayChannel;
|
||||
use app\model\User;
|
||||
|
||||
class PayManageController extends BaseController
|
||||
{
|
||||
@@ -28,9 +27,9 @@ class PayManageController extends BaseController
|
||||
$aid = $this->request->post('aid');
|
||||
$res = PayChannel::where(['account_id' => $aid])->order('last_time', 'desc')->select();
|
||||
if ($res) {
|
||||
return \json(\backMsg(0, '获取成功', $res));
|
||||
return json(backMsg(0, '获取成功', $res));
|
||||
} else {
|
||||
return \json(\backMsg(1, '失败'));
|
||||
return json(backMsg(1, '失败'));
|
||||
}
|
||||
}
|
||||
// 账号状态
|
||||
@@ -39,9 +38,9 @@ class PayManageController extends BaseController
|
||||
$info = $this->request->post();
|
||||
$up_res = PayAccount::update($info);
|
||||
if ($up_res) {
|
||||
return json(\backMsg(0, '成功'));
|
||||
return json(backMsg(0, '成功'));
|
||||
} else {
|
||||
return json(\backMsg(1, '失败'));
|
||||
return json(backMsg(1, '失败'));
|
||||
}
|
||||
}
|
||||
// 添加账号
|
||||
@@ -53,17 +52,13 @@ class PayManageController extends BaseController
|
||||
$info['params'] = '{}';
|
||||
$check_acc = PayAccount::where(['account' => $info['account'], 'platform' => $info['platform'], 'pid' => $pid])->find();
|
||||
if ($check_acc) {
|
||||
return \json(\backMsg(1, '账号已存在'));
|
||||
return json(backMsg(1, '账号已存在'));
|
||||
}
|
||||
$acc = PayAccount::create($info);
|
||||
if ($acc) {
|
||||
$state = $this->createAccountConfig($acc);
|
||||
if (!$state) {
|
||||
return json(\backMsg(1, '自字义参数错误'));
|
||||
}
|
||||
return \json(\backMsg(0, '添加成功'));
|
||||
return json(backMsg(0, '添加成功'));
|
||||
} else {
|
||||
return \json(\backMsg(1, '添加失败'));
|
||||
return json(backMsg(1, '添加失败'));
|
||||
}
|
||||
}
|
||||
// 编辑账号
|
||||
@@ -72,14 +67,9 @@ class PayManageController extends BaseController
|
||||
$info = $this->request->post();
|
||||
$up_res = PayAccount::update($info);
|
||||
if ($up_res) {
|
||||
$acc = PayAccount::find($info['id']);
|
||||
$state = $this->createAccountConfig($acc);
|
||||
if (!$state) {
|
||||
return json(\backMsg(1, '自字义参数错误'));
|
||||
}
|
||||
return json(\backMsg(0, '修改成功'));
|
||||
return json(backMsg(0, '修改成功'));
|
||||
} else {
|
||||
return json(\backMsg(1, '修改失败'));
|
||||
return json(backMsg(1, '修改失败'));
|
||||
}
|
||||
}
|
||||
// 删除账号
|
||||
@@ -89,13 +79,9 @@ class PayManageController extends BaseController
|
||||
$res = PayAccount::destroy($ids);
|
||||
$res2 = PayChannel::whereIn('account_id', $ids)->select()->delete();
|
||||
if ($res && $res2) {
|
||||
$accs = PayAccount::whereIn('id', $ids)->withTrashed()->select();
|
||||
foreach ($accs as $acc) {
|
||||
$this->delAccountConfig($acc);
|
||||
}
|
||||
return \json(\backMsg(0, '已删除'));
|
||||
return json(backMsg(0, '已删除'));
|
||||
} else {
|
||||
return \json(\backMsg(1, '失败'));
|
||||
return json(backMsg(1, '失败'));
|
||||
}
|
||||
}
|
||||
// 添加收款终端
|
||||
@@ -104,13 +90,13 @@ class PayManageController extends BaseController
|
||||
$info = $this->request->post();
|
||||
$check = PayChannel::where(['account_id' => $info['account_id'], 'channel' => $info['channel']])->count();
|
||||
if ($check) {
|
||||
return \json(\backMsg(1, '编号已存在'));
|
||||
return json(backMsg(1, '编号已存在'));
|
||||
}
|
||||
$res = PayChannel::create($info);
|
||||
if ($res) {
|
||||
return \json(\backMsg(0, '添加成功'));
|
||||
return json(backMsg(0, '添加成功'));
|
||||
} else {
|
||||
return \json(\backMsg(1, '添加失败'));
|
||||
return json(backMsg(1, '添加失败'));
|
||||
}
|
||||
}
|
||||
// 编辑收款终端
|
||||
@@ -119,9 +105,9 @@ class PayManageController extends BaseController
|
||||
$info = $this->request->post();
|
||||
$up_res = PayChannel::update($info);
|
||||
if ($up_res) {
|
||||
return json(\backMsg(0, '修改成功'));
|
||||
return json(backMsg(0, '修改成功'));
|
||||
} else {
|
||||
return json(\backMsg(1, '修改失败'));
|
||||
return json(backMsg(1, '修改失败'));
|
||||
}
|
||||
}
|
||||
// 删除收款终端
|
||||
@@ -130,64 +116,65 @@ class PayManageController extends BaseController
|
||||
$cid = $this->request->post('id');
|
||||
$res = PayChannel::destroy($cid);
|
||||
if ($res) {
|
||||
return \json(\backMsg(0, '已删除'));
|
||||
return json(backMsg(0, '已删除'));
|
||||
} else {
|
||||
return \json(\backMsg(1, '失败'));
|
||||
return json(backMsg(1, '失败'));
|
||||
}
|
||||
}
|
||||
// 删除账号配置
|
||||
public function delAccountConfig($acc)
|
||||
// 上传二维码图片
|
||||
public function uploadQrcode()
|
||||
{
|
||||
$path = config_path() . "/payconfig/{$acc->pid}_{$acc->id}.php";
|
||||
if (file_exists($path)) {
|
||||
unlink($path);
|
||||
$img = $this->request->file('codeimg');
|
||||
if (!$img) {
|
||||
return json(backMsg(1, '请选择要上传的文件'));
|
||||
}
|
||||
// 验证文件类型
|
||||
$allowedTypes = ['image/png', 'image/jpeg', 'image/gif'];
|
||||
$fileMimeType = $img->getMime();
|
||||
if (!in_array($fileMimeType, $allowedTypes)) {
|
||||
return json(backMsg(1, '只允许上传PNG、JPEG或GIF格式的图片'));
|
||||
}
|
||||
// 生成唯一文件名
|
||||
$filename = 'img_' . time() . '_' . uniqid() . '.' . $img->getOriginalExtension();
|
||||
// 设置文件保存路径
|
||||
$path = public_path() . '/files/qrcode/';
|
||||
if (!is_dir($path)) {
|
||||
mkdir($path, 0755, true);
|
||||
}
|
||||
// 移动文件到指定目录
|
||||
$info = $img->move($path, $filename);
|
||||
if ($info) {
|
||||
$imgpath = '/files/qrcode/' . $filename;
|
||||
return json(backMsg(0, '上传成功', ['imgpath' => $imgpath]));
|
||||
} else {
|
||||
return json(backMsg(1, '上传失败'));
|
||||
}
|
||||
}
|
||||
|
||||
// 生成账号配置
|
||||
private function createAccountConfig($acc)
|
||||
// 获取账号交易流水
|
||||
public function getAccountTrade()
|
||||
{
|
||||
$params = \json_decode($acc->params, \true);
|
||||
if ($params === null) {
|
||||
return false; // 自定义参数错误
|
||||
}
|
||||
$platform = \app\controller\api\PluginController::getPluginInfo($acc->getData('platform'));
|
||||
$user = User::where('pid', $acc->pid)->find();
|
||||
$query_tpl = $platform['query'];
|
||||
$query = var_export(\array_merge($query_tpl, $params), \true);
|
||||
$config = <<<EOF
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 支付监听配置,一个文件,一个账号
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
return [
|
||||
// 用户账号配置
|
||||
'user' => [
|
||||
'pid' => {$user->pid},
|
||||
'key' => '{$user->secret_key}'
|
||||
],
|
||||
// 收款平台账号配置
|
||||
'pay' => [
|
||||
// 账号id
|
||||
'aid' => $acc->id,
|
||||
// 收款平台
|
||||
'platform' => '{$acc->getData('platform')}',
|
||||
// 插件类名
|
||||
'payclass' => '{$platform['class_name']}',
|
||||
// 账号
|
||||
'account' => '{$acc->account}',
|
||||
// 密码
|
||||
'password' => '{$acc->password}',
|
||||
// 订单查询参数配置
|
||||
'query' => {$query},
|
||||
]
|
||||
];
|
||||
|
||||
EOF;
|
||||
$name = "{$user->pid}_{$acc->id}";
|
||||
$path = config_path() . "/payconfig/{$name}.php";
|
||||
\file_put_contents($path, $config);
|
||||
return true;
|
||||
$req_info = $this->request->get();
|
||||
$req_pid = $req_info['pid'];
|
||||
$req_aid = $req_info['aid'];
|
||||
// 加载配置文件
|
||||
$config = PayAccount::getAccountConfig($req_aid);
|
||||
if ($config === false) return json(backMsg(1, '账号配置文件错误'));
|
||||
if ($req_aid != $config['aid'] || $req_pid != session('pid')) return json(backMsg(1, '监听收款配置不一致'));
|
||||
// 登陆账号
|
||||
$pay_config = ['username' => $config['account'], 'password' => $config['password']];
|
||||
// 收款查询
|
||||
$query = $config['query'];
|
||||
// 实例监听客户端
|
||||
$payclient_name = $config['payclass'];
|
||||
$payclient_path = "\\payclient\\{$payclient_name}";
|
||||
$Payclient = new $payclient_path($pay_config);
|
||||
// 获取支付明细
|
||||
$records = $Payclient->getOrderInfo($query);
|
||||
if ($records) {
|
||||
// 收款流水
|
||||
return json(backMsg(0, '查询成功', $records));
|
||||
} else {
|
||||
return json(backMsg(1, '查询空订单'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ class PluginController extends BaseController
|
||||
// 获取插件配置
|
||||
private static function getPluginConfig(): array
|
||||
{
|
||||
$payplugin_path = config_path() . '/extendconfig/payplugin.php';
|
||||
$payplugin_path = config_path() . '/extend/payplugin.php';
|
||||
if (!file_exists($payplugin_path)) {
|
||||
return [];
|
||||
}
|
||||
@@ -182,7 +182,7 @@ class PluginController extends BaseController
|
||||
// 保存插件配置
|
||||
private function savePluginConfig(array $config, string $note = '说明')
|
||||
{
|
||||
$payplugin_path = config_path() . '/extendconfig/payplugin.php';
|
||||
$payplugin_path = config_path() . '/extend/payplugin.php';
|
||||
$note_tpl = <<<EOF
|
||||
// +----------------------------------------------------------------------
|
||||
// | $note
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -13,10 +13,11 @@ class Order extends BaseModel
|
||||
// 订单有效期
|
||||
private static $activity_time = 180;
|
||||
// 新建订单
|
||||
public static function createOrder($data)
|
||||
public static function createOrder($data): string|false
|
||||
{
|
||||
$my_time = time();
|
||||
$channel = self::setChannel($data['pid'], $data['type']);
|
||||
if (!$channel) return false;
|
||||
$new_order = [
|
||||
// 订单号
|
||||
'order_id' => self::createOrderID('H'),
|
||||
@@ -41,7 +42,7 @@ class Order extends BaseModel
|
||||
// 设备类型
|
||||
'device' => isset($data['device']) ? $data['device'] : '',
|
||||
// 业务扩展参数
|
||||
'param' => serialize(self::getParams($data)),
|
||||
'param' => serialize(isset($data['param']) ? $data['param'] : ''),
|
||||
// 等待/过期:0, 支付成功:1
|
||||
'state' => 0,
|
||||
// 开启监听:1, 关闭监听:0
|
||||
@@ -101,14 +102,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();
|
||||
}
|
||||
// 选择收款通道
|
||||
@@ -119,6 +122,7 @@ class Order extends BaseModel
|
||||
$channel_infos = PayChannel::whereIn('account_id', $aids)->where('state', 1)->order('last_time', 'asc')->select();
|
||||
if (!$channel_infos || !$aids) return [];
|
||||
// 微信/支付宝收款处理
|
||||
$channel_info = null;
|
||||
foreach ($channel_infos as $key => $value) {
|
||||
$check_wx = preg_match('/^wxpay\d+#/i', $value->channel);
|
||||
$check_ali = preg_match('/^alipay\d+#/i', $value->channel);
|
||||
@@ -136,6 +140,7 @@ class Order extends BaseModel
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$channel_info) return [];
|
||||
// 选取收款通道
|
||||
$patt = PayAccount::find($channel_info->account_id);
|
||||
$channel = ['aid' => $channel_info->account_id, 'cid' => $channel_info->id, 'patt' => $patt->getData('pattern')];
|
||||
@@ -143,17 +148,17 @@ class Order extends BaseModel
|
||||
return $channel;
|
||||
}
|
||||
// 获取扩展参数数组
|
||||
private static function getParams(array $data): array
|
||||
{
|
||||
$keys = ['pid', 'type', 'out_trade_no', 'notify_url', 'return_url', 'name', 'money', 'sign', 'sign_type'];
|
||||
$params = [];
|
||||
foreach ($data as $key => $value) {
|
||||
if (!in_array($key, $keys)) {
|
||||
$params[$key] = $value;
|
||||
}
|
||||
}
|
||||
return $params;
|
||||
}
|
||||
// private static function getParams(array $data): array
|
||||
// {
|
||||
// $keys = ['pid', 'type', 'out_trade_no', 'notify_url', 'return_url', 'name', 'money', 'sign', 'sign_type'];
|
||||
// $params = [];
|
||||
// foreach ($data as $key => $value) {
|
||||
// if (!in_array($key, $keys)) {
|
||||
// $params[$key] = $value;
|
||||
// }
|
||||
// }
|
||||
// return $params;
|
||||
// }
|
||||
// 检查金额
|
||||
private static function checkMoney($money, $type, $aid, $cid): float
|
||||
{
|
||||
|
||||
@@ -5,6 +5,8 @@ declare(strict_types=1);
|
||||
namespace app\model;
|
||||
|
||||
use app\BaseModel;
|
||||
use app\model\User;
|
||||
use app\controller\api\PluginController;
|
||||
|
||||
class PayAccount extends BaseModel
|
||||
{
|
||||
@@ -24,10 +26,44 @@ class PayAccount extends BaseModel
|
||||
}
|
||||
return self::withCount(['payChannel' => 'channel'])->where($select);
|
||||
}
|
||||
// 获取账号配置
|
||||
public static function getAccountConfig($aid, $pid = null): array|bool
|
||||
{
|
||||
$aid_info = self::find($aid);
|
||||
// 插件配置
|
||||
$platform = PluginController::getPluginInfo($aid_info->getData('platform'));
|
||||
// 查询参数
|
||||
$params = json_decode($aid_info->params, true);
|
||||
$query = array_merge($platform['query'], $params);
|
||||
if ($aid_info && $platform) {
|
||||
$config = [
|
||||
'pid' => $aid_info->pid,
|
||||
// 账号id
|
||||
'aid' => $aid_info->id,
|
||||
// 收款平台
|
||||
'platform' => $aid_info->getData('platform'),
|
||||
// 插件类名
|
||||
'payclass' => $platform['class_name'],
|
||||
// 账号
|
||||
'account' => $aid_info->account,
|
||||
// 密码
|
||||
'password' => $aid_info->password,
|
||||
// 订单查询参数配置
|
||||
'query' => $query,
|
||||
];
|
||||
if ($pid !== null) {
|
||||
$pid_info = User::where('pid', $pid)->find();
|
||||
$config['key'] = $pid_info->secret_key;
|
||||
}
|
||||
return $config;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// 获取器
|
||||
public function getPlatformAttr($value)
|
||||
{
|
||||
$payplugin_path = config_path() . '/extendconfig/payplugin.php';
|
||||
$payplugin_path = config_path() . '/extend/payplugin.php';
|
||||
if (!file_exists($payplugin_path)) {
|
||||
return [];
|
||||
}
|
||||
@@ -37,7 +73,7 @@ class PayAccount extends BaseModel
|
||||
foreach ($payplugin_config as $config) {
|
||||
$option[$config['platform']] = $config['name'];
|
||||
}
|
||||
return $option[$value];
|
||||
return isset($option[$value]) ? $option[$value] : '[已卸载,请停用]';
|
||||
}
|
||||
public function getPatternAttr($value)
|
||||
{
|
||||
|
||||
|
Before Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 113 KiB |
|
Before Width: | Height: | Size: 130 KiB |
|
Before Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 123 KiB |
BIN
assets/20241203_153935_image.png
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
assets/20241203_154034_image.png
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
assets/20241203_154108_image.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
assets/20241203_154141_image.png
Normal file
|
After Width: | Height: | Size: 135 KiB |
BIN
assets/20241203_154218_image.png
Normal file
|
After Width: | Height: | Size: 139 KiB |
BIN
assets/20241203_154307_image.png
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
assets/20241203_154420_image.png
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
assets/20241203_154505_image.png
Normal file
|
After Width: | Height: | Size: 130 KiB |
BIN
assets/20241203_154755_image.png
Normal file
|
After Width: | Height: | Size: 108 KiB |
BIN
assets/20241203_154918_image.png
Normal file
|
After Width: | Height: | Size: 123 KiB |
BIN
assets/20241203_155123_image.png
Normal file
|
After Width: | Height: | Size: 162 KiB |
BIN
assets/20241203_161723_image.png
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
assets/20241203_162102_image.png
Normal file
|
After Width: | Height: | Size: 79 KiB |
BIN
assets/20241203_162231_image.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
assets/20241203_162646_image.png
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
assets/20241203_163259_image.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
assets/20241203_163507_image.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
assets/20241203_164321_image.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
assets/20241203_165327_image.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
assets/20241203_165507_image.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
assets/20241204_164128_image.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
assets/20241204_164617_image.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
assets/20241204_164650_image.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
assets/20241204_171340_image.png
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
assets/20241204_172346_6c9c6f4d97d5850e7890633edd7e5d5.jpg
Normal file
|
After Width: | Height: | Size: 475 KiB |
BIN
assets/20241204_172532_image.png
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
assets/20241204_172612_image.png
Normal file
|
After Width: | Height: | Size: 83 KiB |
BIN
assets/20241204_172656_image.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
assets/20241204_172718_c313070e899cc93cc2fc9fe25a1ff17.jpg
Normal file
|
After Width: | Height: | Size: 311 KiB |
BIN
assets/20241204_172819_2d569802058d4d1b0f92135dcc30469.jpg
Normal file
|
After Width: | Height: | Size: 179 KiB |
BIN
assets/20241204_173010_image.png
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
assets/20241204_173046_image.png
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
assets/20241204_175634_image.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
assets/20241204_175759_image.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
assets/20241204_180022_image.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
assets/20241205_102856_image.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
assets/20241205_103718_image.png
Normal file
|
After Width: | Height: | Size: 175 KiB |
BIN
assets/20241205_104101_image.png
Normal file
|
After Width: | Height: | Size: 9.7 KiB |
BIN
assets/20241205_104306_image.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
assets/20241205_105844_image.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
assets/20241205_110432_image.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
assets/20241205_110508_image.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
assets/20241205_111720_image.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
assets/20241205_112033_image.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
assets/20241205_112116_image.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
assets/20241205_112415_image.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
assets/20241205_112517_image.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
assets/20241205_113629_image.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
assets/20241205_113835_image.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
assets/20241210_112301_6f2fef2a7aaee96790eb86f90e3b107.png
Normal file
|
After Width: | Height: | Size: 155 KiB |
BIN
assets/20241210_112331_e8d2c4043a3c57ad887aef92df1c253.png
Normal file
|
After Width: | Height: | Size: 81 KiB |
@@ -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,
|
||||
1
extend/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
payclient
|
||||
@@ -23,7 +23,7 @@ class ImgCaptcha
|
||||
return $captcha;
|
||||
}
|
||||
// 请求外部资源
|
||||
private function getHttpResponse($url, $header = [], $post = null, $timeout = 10)
|
||||
private static function getHttpResponse($url, $header = [], $post = null, $timeout = 10)
|
||||
{
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
class MpayClass
|
||||
{
|
||||
private $pid;
|
||||
private $key;
|
||||
private $host;
|
||||
private $check_neworder_url;
|
||||
private $submit_records_url;
|
||||
function __construct($config)
|
||||
{
|
||||
$this->pid = $config['pid'];
|
||||
$this->key = $config['key'];
|
||||
$this->host = $config['host'];
|
||||
$this->check_neworder_url = $this->host . '/order.php';
|
||||
$this->submit_records_url = $this->host . '/payHeart';
|
||||
}
|
||||
// 查询新订单
|
||||
public function orderHeart()
|
||||
{
|
||||
$url = $this->check_neworder_url . "?pid={$this->pid}&sign={$this->getSign()}";
|
||||
$res = $this->getHttpResponse($url);
|
||||
return $res;
|
||||
}
|
||||
// 提交收款明细
|
||||
public function upRecords($records, $aid)
|
||||
{
|
||||
$header = ['Content-Type: application/json;charset=UTF-8'];
|
||||
$url = $this->submit_records_url . "?pid={$this->pid}&aid={$aid}&sign={$this->getSign()}";
|
||||
$res = $this->getHttpResponse($url, $header, json_encode($records));
|
||||
return $res;
|
||||
}
|
||||
|
||||
// 签名方法
|
||||
private function getSign()
|
||||
{
|
||||
return md5($this->pid . $this->key);
|
||||
}
|
||||
// 请求外部资源
|
||||
private function getHttpResponse($url, $header = [], $post = null, $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);
|
||||
if ($header) {
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
|
||||
} else {
|
||||
$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;
|
||||
}
|
||||
}
|
||||
@@ -49,13 +49,23 @@ class Plugin
|
||||
// 获取平台所有支持插件
|
||||
public static function getAllPlugin(): array
|
||||
{
|
||||
$app_plugin = cache('app_plugin');
|
||||
if ($app_plugin) {
|
||||
return json_decode($app_plugin, true);
|
||||
}
|
||||
$app_plugin = self::getHttpResponse(self::$siteUrl . '/mpay/getplugins');
|
||||
cache('app_plugin', $app_plugin, 36000);
|
||||
return json_decode($app_plugin, true);
|
||||
}
|
||||
// 获取通知消息
|
||||
public static function getNotifyMessage(): array
|
||||
{
|
||||
$message = cache('message');
|
||||
if ($message) {
|
||||
return json_decode($message, true);
|
||||
}
|
||||
$message = self::getHttpResponse(self::$siteUrl . '/mpay/message');
|
||||
cache('message', $message, 36000);
|
||||
return json_decode($message, true);
|
||||
}
|
||||
// 请求外部资源
|
||||
|
||||
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
@@ -1,9 +0,0 @@
|
||||
<?php
|
||||
|
||||
header('content-type: application/json; charset=utf-8');
|
||||
$path = '../runtime/order.json';
|
||||
if (!file_exists($path)) {
|
||||
exit('{"code":3,"msg":"文件不存在"}');
|
||||
} else {
|
||||
exit(file_get_contents($path));
|
||||
}
|
||||
BIN
public/static/img/unionpay.ico
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
public/static/img/unionpay.jpg
Normal file
|
After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
78
public/static/js/helper.js
Normal 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;
|
||||
})
|
||||
};
|
||||
@@ -1,14 +0,0 @@
|
||||
<?php
|
||||
function encrypt($string, $key) {
|
||||
$method = "AES-256-CBC";
|
||||
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($method));
|
||||
$encrypted = openssl_encrypt($string, $method, $key, 0, $iv);
|
||||
// 将iv和加密字符串拼接起来
|
||||
return base64_encode($iv . $encrypted);
|
||||
}
|
||||
|
||||
$key = "your-encryption-key"; // 这里应该是一个安全的密钥
|
||||
$string = "Hello, World!";
|
||||
$encryptedString = encrypt($string, $key);
|
||||
|
||||
echo $encryptedString; // 输出加密字符串
|
||||
2
runtime/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
@@ -1 +0,0 @@
|
||||
1732883162
|
||||
@@ -1 +0,0 @@
|
||||
1733190738
|
||||
@@ -1 +0,0 @@
|
||||
{"code":0,"msg":"没有新订单"}
|
||||
@@ -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, {
|
||||
(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(res.msg);
|
||||
layer.msg(initJson.msg);
|
||||
}
|
||||
}).catch(err => {
|
||||
layer.msg(err.message);
|
||||
})
|
||||
} else {
|
||||
layer.msg(saveJson.msg);
|
||||
}
|
||||
})()
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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' });
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
<div class="layui-side-scroll">
|
||||
<div id="sideMenu"></div>
|
||||
</div>
|
||||
<div id="version"><p>版本号:v1.0.0</p><p>by techhaha</p></div>
|
||||
<div id="version"><p>版本号:<?php echo $version ?></p><p>by techhaha</p></div>
|
||||
</div>
|
||||
<!-- 视 图 页 面 -->
|
||||
<div class="layui-body">
|
||||
|
||||
@@ -751,6 +751,10 @@
|
||||
<td>wxpay</td>
|
||||
<td>微信支付</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>unionpay</td>
|
||||
<td>云闪付</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3 id="pay5"><a name="设备类型列表" class="reference-link"></a><span
|
||||
|
||||
@@ -5,6 +5,19 @@
|
||||
<meta charset="utf-8">
|
||||
<title>订单明细</title>
|
||||
<link rel="stylesheet" href="/component/pear/css/pear.css" />
|
||||
<style>
|
||||
.paytype {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
.paytype>span {
|
||||
margin-left: 3px;
|
||||
line-height: normal;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="pear-container">
|
||||
@@ -56,6 +69,7 @@
|
||||
<option value="">请选择</option>
|
||||
<option value="wxpay">微信支付</option>
|
||||
<option value="alipay">支付宝</option>
|
||||
<option value="unionpay">云闪付</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@@ -207,7 +221,7 @@
|
||||
{ title: '成交金额', field: 'really_price', align: 'center', minWidth: 85 },
|
||||
{ title: '支付状态', field: 'state', align: 'center', minWidth: 85, templet: '<div>{{# if(d.state==1){return`<span class="layui-badge layui-bg-green">成功</span>`}else{if(new Date(d.close_time)>new Date("<?php echo $servertime ?>")){return`<span class="layui-badge layui-bg-orange">等待</span>`}else{return`<span class="layui-badge layui-bg-gray">过期</span>`} } }}</div>' },
|
||||
{ title: '支付时间', field: 'pay_time', align: 'center', minWidth: 160, templet: '<div>{{= d.pay_time == d.create_time ? "- -" : d.pay_time}}</div>' },
|
||||
{ title: '支付平台', field: 'type', align: 'center', width: 120, templet: '<div>{{# if(d.type=="wxpay"){return`<img src="/static/img/wxpay.ico"width="15"><span>微信支付</span>`}if(d.type=="alipay"){return`<img src="/static/img/alipay.ico"width="15"><span>支付宝</span>`} }}</div>' },
|
||||
{ title: '支付平台', field: 'type', align: 'center', width: 120, templet: '<div>{{# if(d.type=="wxpay"){return`<div class="paytype"><img src="/static/img/wxpay.ico"width="15"><span>微信支付</span></div>`}if(d.type=="alipay"){return`<div class="paytype"><img src="/static/img/alipay.ico"width="15"><span>支付宝</span></div>`}if(d.type=="unionpay"){return`<div class="paytype"><img src="/static/img/unionpay.ico"width="15"><span>云闪付</span></div>`} }}</div>' },
|
||||
{ title: '收款平台[账号:终端]', field: 'platform', align: 'center', minWidth: 160, templet: '<div>{{# return`${d.payAccount.platform} [${d.aid}:${d.cid}]` }}</div>' },
|
||||
{ title: '操作', align: 'center', width: 120, fixed: 'right', templet: '<div><strong><a href="javascript:;" data-id="{{= d.id }}" class="layui-font-green {{= d.state==1 ? "orderSet-paid" : "orderSet-paying" }}">设置</a></strong></div>' }
|
||||
]]
|
||||
|
||||
@@ -23,6 +23,15 @@
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.paytype {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.paytype>strong {
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
.alipay {
|
||||
color: #1677ff;
|
||||
}
|
||||
@@ -30,6 +39,10 @@
|
||||
.wxpay {
|
||||
color: #1AAD19;
|
||||
}
|
||||
|
||||
.unionpay {
|
||||
color: #d81e06;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
@@ -54,8 +67,8 @@
|
||||
<div class="layui-row">
|
||||
<div class="layui-col-xs3"><label class="layui-form-label"><strong>收款方式</strong></label></div>
|
||||
<div class="layui-col-xs9">
|
||||
<div class="list">
|
||||
<?php $payway=['alipay'=>'支付宝','wxpay'=>'微信支付'];$payway_img=['alipay'=>'/static/img/alipay.ico','wxpay'=>'/static/img/wxpay.ico'];echo "<img src='{$payway_img[$type]}'width='16'><strong class='{$type}'>{$payway[$type]}</strong>" ?>
|
||||
<div class="list paytype">
|
||||
<?php $payway=['alipay'=>'支付宝','wxpay'=>'微信支付','unionpay'=>'云闪付'];$payway_img=['alipay'=>'/static/img/alipay.ico','wxpay'=>'/static/img/wxpay.ico','unionpay'=>'/static/img/unionpay.ico'];echo "<img src='{$payway_img[$type]}'width='16'><strong class='{$type}'>{$payway[$type]}</strong>" ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -198,7 +211,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"></span></a>
|
||||
data-qrcode="<?php echo $qrcode ?>" data-type="<?php echo $url_type ?>"><span
|
||||
class="icon pear-icon"></span></a>
|
||||
<?php echo $qrcode ?>
|
||||
</div>
|
||||
</div>
|
||||
@@ -215,7 +229,18 @@
|
||||
util.on({
|
||||
getQrcode: function () {
|
||||
(async () => {
|
||||
const qrcode_data = this.getAttribute("data-qrcode")
|
||||
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,
|
||||
@@ -225,6 +250,7 @@
|
||||
shadeClose: true,
|
||||
content: `<img width="100%" src="${qrcode_img}">`
|
||||
});
|
||||
}
|
||||
})()
|
||||
}
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>订单明细</title>
|
||||
<title>支付测试</title>
|
||||
<link rel="stylesheet" href="/component/pear/css/pear.css" />
|
||||
<style>
|
||||
.paybtn {
|
||||
@@ -60,6 +60,10 @@
|
||||
<img src="/static/img/alipay.ico" width="16">
|
||||
<span>支付宝</span>
|
||||
</button>
|
||||
<button class="pear-btn unionpay">
|
||||
<img src="/static/img/unionpay.ico" width="16">
|
||||
<span>云闪付</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -91,6 +95,9 @@
|
||||
document.querySelector('.alipay').addEventListener('click', function () {
|
||||
crateOrder('alipay');
|
||||
});
|
||||
document.querySelector('.unionpay').addEventListener('click', function () {
|
||||
crateOrder('unionpay');
|
||||
});
|
||||
// 创建订单
|
||||
function crateOrder(paytype) {
|
||||
form.submit('paytest', function (data) {
|
||||
|
||||
@@ -150,6 +150,35 @@
|
||||
</div>
|
||||
<script src="/component/layui/layui.js"></script>
|
||||
<script src="/static/js/awesome-qr.min.js"></script>
|
||||
<script>
|
||||
function detectBrowserEnvironment() {
|
||||
const userAgent = navigator.userAgent.toLowerCase();
|
||||
let environment = 'other';
|
||||
if (userAgent.includes('micromessenger')) {
|
||||
if (userAgent.includes('miniprogram')) {
|
||||
environment = 'wxapp'; // 微信小程序
|
||||
} else if (userAgent.includes('android') || userAgent.includes('iphone') || userAgent.includes('ipad')) {
|
||||
environment = 'wxphone'; // 手机微信
|
||||
} else {
|
||||
environment = 'wxpc'; // PC微信
|
||||
}
|
||||
} else if (userAgent.includes('aliapp') || userAgent.includes('alipayclient')) {
|
||||
if (userAgent.includes('android') || userAgent.includes('iphone') || userAgent.includes('ipad')) {
|
||||
environment = 'aliphone'; // 手机支付宝
|
||||
} else {
|
||||
environment = 'alipc'; // PC支付宝
|
||||
}
|
||||
} else if (userAgent.includes('android') || userAgent.includes('iphone') || userAgent.includes('ipad')) {
|
||||
// 先判断是否是已知的手机APP内置浏览器,如果不是则认为是手机浏览器
|
||||
if (!userAgent.includes('micromessenger') && !userAgent.includes('aliapp') && !userAgent.includes('qq')) {
|
||||
environment = 'phone'; // 手机浏览器
|
||||
}
|
||||
} else {
|
||||
environment = 'pc'; // 剩下的情况认为是PC浏览器
|
||||
}
|
||||
return environment;
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
const payCode = '<?php echo htmlentities($payUrl); ?>';
|
||||
const codeType = '<?php echo htmlentities($code_type); ?>';
|
||||
@@ -166,6 +195,9 @@
|
||||
} else if (payType === 'alipay') {
|
||||
payTpeyImg.src = '/static/img/alipay.jpg';
|
||||
payTypeText.innerText = '请使用支付宝扫码支付'
|
||||
} else if (payType === 'unionpay') {
|
||||
payTpeyImg.src = '/static/img/unionpay.jpg';
|
||||
payTypeText.innerText = '请使用云闪付扫码支付'
|
||||
}
|
||||
// 生成二维码
|
||||
if (codeType == 0) {
|
||||
@@ -244,6 +276,14 @@
|
||||
return info;
|
||||
}
|
||||
/* <?php } ?> */
|
||||
|
||||
// 环境判断
|
||||
const environment = detectBrowserEnvironment();
|
||||
if (payType === 'wxpay' && environment === 'aliphone') {
|
||||
layer.alert('请使用微信打开此页面');
|
||||
} else if (payType === 'alipay' && environment === 'wxphone') {
|
||||
layer.alert('请使用支付宝打开此页面');
|
||||
}
|
||||
// 生成二维码
|
||||
async function getQrcode(text, QR) {
|
||||
const qrcodeUrl = await new Promise((resolve) => {
|
||||
|
||||