mirror of
https://gitee.com/zoujingli/ThinkAdmin.git
synced 2025-04-05 19:41:44 +08:00
同步更新微信模块
This commit is contained in:
parent
0a47af5138
commit
1a51281a3c
@ -19,7 +19,10 @@ namespace app\wechat;
|
||||
use app\wechat\command\Auto;
|
||||
use app\wechat\command\Fans;
|
||||
use app\wechat\service\AutoService;
|
||||
use app\wechat\service\PaymentService;
|
||||
use think\admin\extend\CodeExtend;
|
||||
use think\admin\Plugin;
|
||||
use think\Request;
|
||||
|
||||
/**
|
||||
* 组件注册服务
|
||||
@ -53,6 +56,16 @@ class Service extends Plugin
|
||||
$this->app->event->listen('WechatFansSubscribe', function ($openid) {
|
||||
AutoService::register($openid);
|
||||
});
|
||||
|
||||
// 注册支付通知路由
|
||||
$this->app->route->any('/plugin-wxpay-notify/:vars', function (Request $request) {
|
||||
try {
|
||||
$data = json_decode(CodeExtend::deSafe64($request->param('vars')), true);
|
||||
return PaymentService::notify($data);
|
||||
} catch (\Exception|\Error $exception) {
|
||||
return 'Error: ' . $exception->getMessage();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -80,6 +93,13 @@ class Service extends Plugin
|
||||
['name' => '关注自动回复', 'icon' => 'layui-icon layui-icon-release', 'node' => "wechat/auto/index"],
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => '微信支付',
|
||||
'subs' => [
|
||||
['name' => '微信支付行为', 'icon' => 'layui-icon layui-icon-rmb', 'node' => "wechat/payment.record/index"],
|
||||
['name' => '微信退款管理', 'icon' => 'layui-icon layui-icon-engine', 'node' => "wechat/payment.refund/index"],
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
@ -17,6 +17,7 @@
|
||||
namespace app\wechat\controller\api;
|
||||
|
||||
use app\wechat\service\MediaService;
|
||||
use app\wechat\service\PaymentService;
|
||||
use app\wechat\service\WechatService;
|
||||
use think\admin\Controller;
|
||||
use think\admin\extend\CodeExtend;
|
||||
@ -32,6 +33,7 @@ class Test extends Controller
|
||||
{
|
||||
/**
|
||||
* 微信JSAPI支付二维码
|
||||
* @login true
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function jsapiQrc(): Response
|
||||
@ -42,6 +44,7 @@ class Test extends Controller
|
||||
|
||||
/**
|
||||
* 显示网页授权二维码
|
||||
* @login true
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function oauthQrc(): Response
|
||||
@ -52,6 +55,7 @@ class Test extends Controller
|
||||
|
||||
/**
|
||||
* 显示网页授权二维码
|
||||
* @login true
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function jssdkQrc(): Response
|
||||
@ -62,6 +66,7 @@ class Test extends Controller
|
||||
|
||||
/**
|
||||
* 微信扫码支付模式一二维码显示
|
||||
* @login true
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function scanOneQrc(): Response
|
||||
@ -72,21 +77,24 @@ class Test extends Controller
|
||||
|
||||
/**
|
||||
* 扫码支付模式二测试二维码
|
||||
* @login true
|
||||
* @return \think\Response
|
||||
* @throws \WeChat\Exceptions\InvalidResponseException
|
||||
* @throws \WeChat\Exceptions\LocalCacheException
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public function scanTwoQrc(): Response
|
||||
{
|
||||
$result = WechatService::WePayOrder()->create([
|
||||
'body' => '测试商品',
|
||||
'total_fee' => '1',
|
||||
'trade_type' => 'NATIVE',
|
||||
'notify_url' => sysuri('wechat/api.test/notify', [], false, true),
|
||||
'out_trade_no' => CodeExtend::uniqidNumber(18),
|
||||
'spbill_create_ip' => $this->request->ip(),
|
||||
]);
|
||||
return $this->_buildQrcResponse($result['code_url']);
|
||||
$code = CodeExtend::uniqidDate(18, 'TX');
|
||||
$result = PaymentService::create('', $code, "扫码支付测试 {$code}", '0.01', PaymentService::WECHAT_QRC, '0.01');
|
||||
return $this->_buildQrcResponse($result['params']['code_url']);
|
||||
// $result = WechatService::WePayOrder()->create([
|
||||
// 'body' => '测试商品',
|
||||
// 'total_fee' => '1',
|
||||
// 'trade_type' => 'NATIVE',
|
||||
// 'notify_url' => sysuri('wechat/api.test/notify', [], false, true),
|
||||
// 'out_trade_no' => CodeExtend::uniqidNumber(18),
|
||||
// 'spbill_create_ip' => $this->request->ip(),
|
||||
// ]);
|
||||
// return $this->_buildQrcResponse($result['code_url']);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -156,50 +164,22 @@ class Test extends Controller
|
||||
|
||||
/**
|
||||
* 微信JSAPI支付测试
|
||||
* @return string
|
||||
* @return void|string
|
||||
* @throws \WeChat\Exceptions\InvalidResponseException
|
||||
* @throws \WeChat\Exceptions\LocalCacheException
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public function jsapi(): string
|
||||
public function jsapi()
|
||||
{
|
||||
$this->url = $this->request->url(true);
|
||||
$this->pay = WechatService::WePayOrder();
|
||||
$user = WechatService::getWebOauthInfo($this->url);
|
||||
if (empty($user['openid'])) return '<h3>网页授权获取OPENID失败!</h3>';
|
||||
// 生成预支付码
|
||||
$result = $this->pay->create([
|
||||
'body' => '测试商品',
|
||||
'openid' => $user['openid'],
|
||||
'total_fee' => '1',
|
||||
'trade_type' => 'JSAPI',
|
||||
'notify_url' => sysuri('wechat/api.test/notify', [], false, true),
|
||||
'out_trade_no' => CodeExtend::uniqidDate(18),
|
||||
'spbill_create_ip' => $this->request->ip(),
|
||||
]);
|
||||
// 数据参数格式化
|
||||
$resultJson = var_export($result, true);
|
||||
$optionJson = json_encode($this->pay->jsapiParams($result['prepay_id']), JSON_UNESCAPED_UNICODE);
|
||||
$configJson = json_encode(WechatService::getWebJssdkSign(), JSON_UNESCAPED_UNICODE);
|
||||
return <<<HTML
|
||||
<pre>
|
||||
当前用户OPENID: {$user['openid']}
|
||||
\n\n--- 创建微信预支付码结果 ---\n {$resultJson}
|
||||
\n\n--- JSAPI 及 H5 支付参数 ---\n {$optionJson}
|
||||
</pre>
|
||||
<button id='paytest' type='button'>JSAPI支付测试</button>
|
||||
<script src='//res.wx.qq.com/open/js/jweixin-1.6.0.js'></script>
|
||||
<script>
|
||||
wx.config({$configJson});
|
||||
document.getElementById('paytest').onclick = function(){
|
||||
var options = {$optionJson};
|
||||
options.success = function(){
|
||||
alert('支付成功');
|
||||
}
|
||||
wx.chooseWXPay(options);
|
||||
}
|
||||
</script>
|
||||
HTML;
|
||||
// 微信用户信息
|
||||
$this->user = WechatService::getWebOauthInfo($this->request->url(true));
|
||||
if (empty($this->user['openid'])) return '<h3>网页授权获取OPENID失败!</h3>';
|
||||
// 生成支付参数
|
||||
$oCode = CodeExtend::uniqidDate(18, 'TX');
|
||||
$this->result = PaymentService::create($this->user['openid'], $oCode, "JSAPI 支付测试 {$oCode}", '0.01', PaymentService::WECHAT_GZH);
|
||||
$this->optionJson = json_encode($this->result['params'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
$this->configJson = json_encode(WechatService::getWebJssdkSign(), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
$this->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
|
76
app/wechat/controller/payment/Record.php
Normal file
76
app/wechat/controller/payment/Record.php
Normal file
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Wechat Plugin for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2023 Anyon <zoujingli@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免责声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat
|
||||
// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\wechat\controller\payment;
|
||||
|
||||
use app\wechat\model\WechatFans;
|
||||
use app\wechat\model\WechatPaymentRecord;
|
||||
use app\wechat\service\PaymentService;
|
||||
use think\admin\Controller;
|
||||
use think\admin\helper\QueryHelper;
|
||||
use think\exception\HttpResponseException;
|
||||
|
||||
/**
|
||||
* 微信支付行为管理
|
||||
* @class Record
|
||||
* @package app\wechat\controller
|
||||
*/
|
||||
class Record extends Controller
|
||||
{
|
||||
/**
|
||||
* 微信支付行为管理
|
||||
* @auth true
|
||||
* @menu true
|
||||
* @return void
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
WechatPaymentRecord::mQuery()->layTable(function () {
|
||||
$this->title = '支付行为管理';
|
||||
}, function (QueryHelper $query) {
|
||||
$db = WechatFans::mQuery()->like('openid|nickname#nickname')->db();
|
||||
if ($db->getOptions('where')) $query->whereRaw("openid in {$db->field('openid')->buildSql()}");
|
||||
$query->like('order_code|order_name#order')->dateBetween('create_time');
|
||||
$query->with(['bindFans'])->equal('payment_status');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建退款申请
|
||||
* @auth true
|
||||
* @return void
|
||||
*/
|
||||
public function refund()
|
||||
{
|
||||
try {
|
||||
$data = $this->_vali(['code.require' => '支付号不能为空!']);
|
||||
$recode = WechatPaymentRecord::mk()->where($data)->findOrEmpty();
|
||||
if ($recode->isEmpty()) $this->error('支付单不存在!');
|
||||
if ($recode->getAttr('payment_status') < 1) $this->error('支付单未完成支付!');
|
||||
$desc = "来自订单 {$recode['order_code']} 的退款!";
|
||||
sysoplog('微信支付退款', "支付单 {$data['code']} 发起退款!");
|
||||
[$state, $message] = PaymentService::refund($data['code'], $recode->getAttr('payment_amount'), $desc);
|
||||
$state ? $this->success($message) : $this->error($message);
|
||||
} catch (HttpResponseException $exception) {
|
||||
throw $exception;
|
||||
} catch (\Exception $exception) {
|
||||
$this->error($exception->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
55
app/wechat/controller/payment/Refund.php
Normal file
55
app/wechat/controller/payment/Refund.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Wechat Plugin for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2023 Anyon <zoujingli@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免责声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat
|
||||
// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\wechat\controller\payment;
|
||||
|
||||
use app\wechat\model\WechatFans;
|
||||
use app\wechat\model\WechatPaymentRecord;
|
||||
use app\wechat\model\WechatPaymentRefund;
|
||||
use think\admin\Controller;
|
||||
use think\admin\helper\QueryHelper;
|
||||
|
||||
/**
|
||||
* 支付退款管理
|
||||
* @class Refund
|
||||
* @package app\wechat\controller
|
||||
*/
|
||||
class Refund extends Controller
|
||||
{
|
||||
/**
|
||||
* 支付退款管理
|
||||
* @auth true
|
||||
* @menu true
|
||||
* @return void
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
WechatPaymentRefund::mQuery()->layTable(function () {
|
||||
$this->title = '支付退款管理';
|
||||
}, function (QueryHelper $query) {
|
||||
$query->with(['record'])->like('code|refund_trade#refund');
|
||||
if (($this->get['order'] ?? '') . ($this->get['nickname'] ?? '') . ($this->get['payment'] ?? '') . ($this->get['refund'] ?? '') !== '') {
|
||||
$db1 = WechatFans::mQuery()->field('openid')->like('openid|nickname#nickname')->db();
|
||||
$db2 = WechatPaymentRecord::mQuery()->like('order_code|order_name#order,code|payment_trade#payment');
|
||||
$db2->whereRaw("openid in {$db1->buildSql()}");
|
||||
$query->whereRaw("record_code in {$db2->field('code')->db()->buildSql()}");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
87
app/wechat/model/WechatPaymentRecord.php
Normal file
87
app/wechat/model/WechatPaymentRecord.php
Normal file
@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Wechat Plugin for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2023 Anyon <zoujingli@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免责声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat
|
||||
// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\wechat\model;
|
||||
|
||||
use app\wechat\service\PaymentService;
|
||||
use think\admin\Model;
|
||||
use think\model\relation\HasOne;
|
||||
|
||||
/**
|
||||
* 微信支付行为模型
|
||||
* @class WechatPaymentRecord
|
||||
* @package app\wechat\model
|
||||
*/
|
||||
class WechatPaymentRecord extends Model
|
||||
{
|
||||
/**
|
||||
* 关联用户粉丝数据
|
||||
* @return \think\model\relation\HasOne
|
||||
*/
|
||||
public function fans(): HasOne
|
||||
{
|
||||
return $this->hasOne(WechatFans::class, 'openid', 'openid');
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定用户粉丝数据
|
||||
* @return \think\model\relation\HasOne
|
||||
*/
|
||||
public function bindFans(): HasOne
|
||||
{
|
||||
return $this->fans()->bind([
|
||||
'fans_headimg' => 'headimgurl',
|
||||
'fans_nickname' => 'nickname',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化输出时间格式
|
||||
* @param mixed $value
|
||||
* @return string
|
||||
*/
|
||||
public function getCreateTimeAttr($value): string
|
||||
{
|
||||
return $value ? format_datetime($value) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化输出时间格式
|
||||
* @param mixed $value
|
||||
* @return string
|
||||
*/
|
||||
public function getUpdateTimeAttr($value): string
|
||||
{
|
||||
return $value ? format_datetime($value) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化输出时间格式
|
||||
* @param mixed $value
|
||||
* @return string
|
||||
*/
|
||||
public function getPaymentTimeAttr($value): string
|
||||
{
|
||||
return $value ? format_datetime($value) : '';
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
$data = parent::toArray();
|
||||
$data['type_name'] = PaymentService::tradeTypeNames[$data['type']] ?? $data['type'];
|
||||
return $data;
|
||||
}
|
||||
}
|
67
app/wechat/model/WechatPaymentRefund.php
Normal file
67
app/wechat/model/WechatPaymentRefund.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Wechat Plugin for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2023 Anyon <zoujingli@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免责声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat
|
||||
// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\wechat\model;
|
||||
|
||||
use think\admin\Model;
|
||||
use think\model\relation\HasOne;
|
||||
|
||||
/**
|
||||
* 微信支付退款模型
|
||||
* @class WechatPaymentRefund
|
||||
* @package app\wechat\model
|
||||
*/
|
||||
class WechatPaymentRefund extends Model
|
||||
{
|
||||
/**
|
||||
* 关联支付订单
|
||||
* @return \think\model\relation\HasOne
|
||||
*/
|
||||
public function record(): HasOne
|
||||
{
|
||||
return $this->hasOne(WechatPaymentRecord::class, 'code', 'record_code')->with('bindfans');
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化输出时间格式
|
||||
* @param mixed $value
|
||||
* @return string
|
||||
*/
|
||||
public function getCreateTimeAttr($value): string
|
||||
{
|
||||
return $value ? format_datetime($value) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化输出时间格式
|
||||
* @param mixed $value
|
||||
* @return string
|
||||
*/
|
||||
public function getUpdateTimeAttr($value): string
|
||||
{
|
||||
return $value ? format_datetime($value) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化输出时间格式
|
||||
* @param mixed $value
|
||||
* @return string
|
||||
*/
|
||||
public function getRefundTimeAttr($value): string
|
||||
{
|
||||
return $value ? format_datetime($value) : '';
|
||||
}
|
||||
}
|
@ -39,13 +39,13 @@ class MediaService extends Service
|
||||
/**
|
||||
* 通过图文ID读取图文信息
|
||||
* @param mixed $id 本地图文ID
|
||||
* @param array $map 额外的查询条件
|
||||
* @param mixed $map 额外的查询条件
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public static function news($id, array $map = []): array
|
||||
public static function news($id, $map = []): array
|
||||
{
|
||||
// 文章主体数据
|
||||
$data = WechatNews::mk()->where(['id' => $id, 'is_deleted' => 0])->where($map)->findOrEmpty()->toArray();
|
||||
|
398
app/wechat/service/PaymentService.php
Normal file
398
app/wechat/service/PaymentService.php
Normal file
@ -0,0 +1,398 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Wechat Plugin for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2023 Anyon <zoujingli@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免责声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat
|
||||
// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\wechat\service;
|
||||
|
||||
use app\wechat\model\WechatPaymentRecord;
|
||||
use app\wechat\model\WechatPaymentRefund;
|
||||
use think\admin\Exception;
|
||||
use think\admin\extend\CodeExtend;
|
||||
use think\admin\Library;
|
||||
use think\Response;
|
||||
use WePayV3\Order;
|
||||
|
||||
/**
|
||||
* 微信V3支付服务
|
||||
* @class PaymentService
|
||||
* @package app\wechat\service
|
||||
*/
|
||||
class PaymentService
|
||||
{
|
||||
// 微信支付类型
|
||||
public const WECHAT_APP = 'wechat_app';
|
||||
public const WECHAT_GZH = 'wechat_gzh';
|
||||
public const WECHAT_XCX = 'wechat_xcx';
|
||||
public const WECHAT_WAP = 'wechat_wap';
|
||||
public const WECHAT_QRC = 'wechat_qrc';
|
||||
|
||||
// 微信支付类型转换
|
||||
private const tradeTypes = [
|
||||
self::WECHAT_APP => 'APP',
|
||||
self::WECHAT_WAP => 'MWEB',
|
||||
self::WECHAT_GZH => 'JSAPI',
|
||||
self::WECHAT_XCX => 'JSAPI',
|
||||
self::WECHAT_QRC => 'NATIVE',
|
||||
];
|
||||
|
||||
// 微信支付类型名称
|
||||
public const tradeTypeNames = [
|
||||
self::WECHAT_APP => '微信APP支付',
|
||||
self::WECHAT_WAP => '微信H5支付',
|
||||
self::WECHAT_GZH => '服务号支付',
|
||||
self::WECHAT_XCX => '小程序支付',
|
||||
self::WECHAT_QRC => '二维码支付',
|
||||
];
|
||||
|
||||
/**
|
||||
* 创建微信支付订单
|
||||
* @param string $openid 用户标识
|
||||
* @param string $oCode 订单单号
|
||||
* @param string $oName 订单标题
|
||||
* @param string $oAmount 订单金额(元)
|
||||
* @param string $pType 支付类型
|
||||
* @param ?string $pAmount 支付金额(元)
|
||||
* @param ?string $pRemark 支付描述
|
||||
* @return array
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public static function create(string $openid, string $oCode, string $oName, string $oAmount, string $pType, ?string $pAmount = null, ?string $pRemark = null): array
|
||||
{
|
||||
try {
|
||||
// 检查订单是否完成
|
||||
if (self::isPayed($oCode, $oAmount)) {
|
||||
return ['code' => 1, 'info' => '已完成支付!', 'data' => [], 'params' => []];
|
||||
}
|
||||
// 检查剩余支付金额
|
||||
$leave = self::leave($oCode);
|
||||
$pAmount = floatval(is_null($pAmount) ? (floatval($oAmount) - $leave) : $pAmount);
|
||||
if ($leave + $pAmount > floatval($oAmount)) {
|
||||
return ['code' => 0, 'info' => '支付总额超出!', 'data' => [], 'params' => []];
|
||||
}
|
||||
$config = WechatService::getConfig();
|
||||
$pCode = CodeExtend::uniqidNumber(16, 'P');
|
||||
$data = [
|
||||
'appid' => $config['appid'],
|
||||
'mchid' => $config['mch_id'],
|
||||
'payer' => ['openid' => $openid],
|
||||
'amount' => ['total' => intval($pAmount * 100), 'currency' => 'CNY'],
|
||||
'notify_url' => static::withNotifyUrl($pCode),
|
||||
'description' => empty($pRemark) ? $oName : ($oName . '-' . $pRemark),
|
||||
'out_trade_no' => $pCode,
|
||||
];
|
||||
$tradeType = static::tradeTypes[$pType] ?? '';
|
||||
if (in_array($pType, [static::WECHAT_WAP, static::WECHAT_QRC])) {
|
||||
unset($data['payer']);
|
||||
}
|
||||
if ($pType === static::WECHAT_WAP) {
|
||||
$tradeType = 'h5';
|
||||
$data['scene_info'] = ['h5_info' => ['type' => 'Wap'], 'payer_client_ip' => request()->ip()];
|
||||
}
|
||||
$params = static::withPayment($config)->create(strtolower($tradeType), $data);
|
||||
// 创建支付记录
|
||||
static::createPaymentAction($openid, $oCode, $oName, $oAmount, $pType, $pCode, strval($pAmount));
|
||||
// 返回支付参数
|
||||
return ['code' => 1, 'info' => '创建支付成功', 'data' => $data, 'params' => $params];
|
||||
} catch (Exception $exception) {
|
||||
throw $exception;
|
||||
} catch (\Exception $exception) {
|
||||
throw new Exception($exception->getMessage(), $exception->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询微信支付订单
|
||||
* @param string $pCode 订单单号
|
||||
* @return array
|
||||
*/
|
||||
public static function query(string $pCode): array
|
||||
{
|
||||
try {
|
||||
$result = static::withPayment()->query($pCode);
|
||||
if (isset($result['trade_state']) && $result['trade_state'] === 'SUCCESS') {
|
||||
$extra = [
|
||||
'openid' => $result['payer']['openid'] ?? null,
|
||||
'payment_bank' => $result['bank_type'],
|
||||
'payment_time' => date('Y-m-d H:i:s', strtotime($result['success_time'])),
|
||||
'payment_remark' => $result['trade_state_desc'] ?? null,
|
||||
'payment_notify' => json_encode($result, 64 | 256),
|
||||
];
|
||||
$pAmount = strval($result['amount']['total'] / 100);
|
||||
static::updatePaymentAction($result['out_trade_no'], $result['transaction_id'], $pAmount, $extra);
|
||||
}
|
||||
return $result;
|
||||
} catch (\Exception $exception) {
|
||||
return ['trade_state' => 'ERROR', 'trade_state_desc' => $exception->getMessage()];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付结果处理
|
||||
* @param array|null $data
|
||||
* @return \think\Response
|
||||
*/
|
||||
public static function notify(?array $data = null): Response
|
||||
{
|
||||
try {
|
||||
$notify = static::withPayment()->notify();
|
||||
$result = empty($notify['result']) ? [] : json_decode($notify['result'], true);
|
||||
if (empty($result) || !is_array($result)) return response('error', 500);
|
||||
//订单支付通知处理
|
||||
if ($data['scen'] === 'order' && isset($result['trade_state']) && $result['trade_state'] == 'SUCCESS') {
|
||||
if ($data['order'] !== $result['out_trade_no']) return response('error', 500);
|
||||
$extra = [
|
||||
'openid' => $result['payer']['openid'] ?? null,
|
||||
'payment_bank' => $result['bank_type'],
|
||||
'payment_time' => date('Y-m-d H:i:s', strtotime($result['success_time'])),
|
||||
'payment_remark' => $result['trade_state_desc'] ?? null,
|
||||
'payment_notify' => json_encode($result, 64 | 256),
|
||||
];
|
||||
$pAmount = strval($result['amount']['payer_total'] / 100);
|
||||
if (!static::updatePaymentAction($result['out_trade_no'], $result['transaction_id'], $pAmount, $extra)) {
|
||||
return response('error', 500);
|
||||
}
|
||||
} elseif ($data['scen'] === 'refund' && isset($result['refund_status']) && $result['refund_status'] == 'SUCCESS') {
|
||||
if ($data['order'] !== $result['out_refund_no']) return response('error', 500);
|
||||
$refund = WechatPaymentRefund::mk()->where(['code' => $result['out_refund_no']])->findOrEmpty();
|
||||
if ($refund->isEmpty()) return response('error', 500);
|
||||
$refund->save([
|
||||
'refund_time' => date('Y-m-d H:i:s', strtotime($result['success_time'])),
|
||||
'refund_trade' => $result['refund_id'],
|
||||
'refund_scode' => $result['refund_status'],
|
||||
'refund_status' => 1,
|
||||
'refund_notify' => json_encode($result, 64 | 256),
|
||||
'refund_account' => $result['user_received_account'] ?? '',
|
||||
]);
|
||||
static::refundSync($refund->getAttr('record_code'));
|
||||
}
|
||||
return response('success');
|
||||
} catch (\Exception $exception) {
|
||||
return json(['code' => 'FAIL', 'message' => $exception->getMessage()])->code(500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建支付单退款
|
||||
* @param string $pcode 支付单号
|
||||
* @param string $amount 退款金额
|
||||
* @param string $reason 退款原因
|
||||
* @return array
|
||||
* @throws \WeChat\Exceptions\InvalidResponseException
|
||||
* @throws \WeChat\Exceptions\LocalCacheException
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public static function refund(string $pcode, string $amount, string $reason = ''): array
|
||||
{
|
||||
// 同步已退款状态
|
||||
$record = static::refundSync($pcode);
|
||||
if ($record->getAttr('refund_amount') >= $record->getAttr('payment_amount')) {
|
||||
return [1, '该订单已完成退款!'];
|
||||
}
|
||||
if ($record->getAttr('refund_amount') + floatval($amount) > $record->getAttr('payment_amount')) {
|
||||
return [0, '退款大于支付金额!'];
|
||||
}
|
||||
// 创建支付退款申请
|
||||
do $rcode = CodeExtend::uniqidNumber(16, 'R');
|
||||
while (($model = WechatPaymentRefund::mk()->where(['code' => $rcode])->findOrEmpty())->isExists());
|
||||
// 初始化退款申请记录
|
||||
$model->save(['code' => $rcode, 'record_code' => $pcode, 'refund_amount' => $amount, 'refund_remark' => $reason]);
|
||||
$options = [
|
||||
'out_trade_no' => $pcode,
|
||||
'out_refund_no' => $rcode,
|
||||
'notify_url' => static::withNotifyUrl($rcode, 'refund'),
|
||||
'amount' => [
|
||||
'refund' => intval(floatval($amount) * 100),
|
||||
'total' => intval($record->getAttr('payment_amount') * 100),
|
||||
'currency' => 'CNY'
|
||||
]
|
||||
];
|
||||
if (strlen($reason) > 0) $options['reason'] = $reason;
|
||||
$result = static::withPayment()->createRefund($options);
|
||||
if (in_array($result['code'] ?? $result['status'], ['SUCCESS', 'PROCESSING'])) {
|
||||
return self::refundSyncByQuery($rcode);
|
||||
} else {
|
||||
return [0, $result['message'] ?? $result['status']];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步退款统计状态
|
||||
* @param string $pCode
|
||||
* @return \app\wechat\model\WechatPaymentRecord
|
||||
* @throws \WeChat\Exceptions\InvalidResponseException
|
||||
* @throws \WeChat\Exceptions\LocalCacheException
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public static function refundSync(string $pCode): WechatPaymentRecord
|
||||
{
|
||||
$record = WechatPaymentRecord::mk()->where(['code' => $pCode])->findOrEmpty();
|
||||
if ($record->isEmpty()) throw new Exception('支付单不存在!');
|
||||
if ($record->getAttr('payment_status') < 1) throw new Exception("支付未完成!");
|
||||
// 最近一条记录,同步查询刷新
|
||||
$map = ['record_code' => $pCode];
|
||||
$last = WechatPaymentRefund::mk()->where($map)->order('id desc')->findOrEmpty();
|
||||
if ($last->isExists() && $last->getAttr('refund_status') === 0) {
|
||||
static::refundSyncByQuery($last->getAttr('code'));
|
||||
}
|
||||
// 统计刷新退款金额
|
||||
$where = ['record_code' => $pCode, 'refund_status' => 1];
|
||||
$amount = WechatPaymentRefund::mk()->where($where)->sum('refund_amount');
|
||||
$record->save(['refund_amount' => $amount, 'refund_status' => intval($amount > 0)]);
|
||||
return $record;
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步退款状态
|
||||
* @param string $rCode
|
||||
* @return array
|
||||
* @throws \WeChat\Exceptions\InvalidResponseException
|
||||
* @throws \WeChat\Exceptions\LocalCacheException
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public static function refundSyncByQuery(string $rCode): array
|
||||
{
|
||||
$refund = WechatPaymentRefund::mk()->where(['code' => $rCode])->findOrEmpty();
|
||||
if ($refund->isEmpty()) return [0, '退款不存在!'];
|
||||
if ($refund->getAttr('refund_status')) return [1, '退款已完成!'];
|
||||
$result = static::withPayment()->queryRefund($rCode);
|
||||
$extra = [
|
||||
'refund_trade' => $result['refund_id'],
|
||||
'refund_scode' => $result['status'],
|
||||
'refund_status' => intval($result['status'] === 'SUCCESS'),
|
||||
'refund_notify' => json_encode($result, 64 | 256),
|
||||
'refund_account' => $result['user_received_account'] ?? '',
|
||||
];
|
||||
if (isset($result['success_time'])) {
|
||||
$extra['refund_time'] = date('Y-m-d H:i:s', strtotime($result['success_time']));
|
||||
}
|
||||
$refund->save($extra);
|
||||
// 同步支付订单
|
||||
static::refundSync($refund->getAttr('record_code'));
|
||||
if ($result['status'] === 'SUCCESS') return [1, '退款已完成!'];
|
||||
if ($result['status'] === 'PROCESSING') return [1, '退款处理中!'];
|
||||
return [0, $result['message'] ?? $result['status']];
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否完成支付
|
||||
* @param string $oCode 原订单单号
|
||||
* @param string $oAmount 需支付金额
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isPayed(string $oCode, string $oAmount): bool
|
||||
{
|
||||
return self::leave($oCode) >= $oAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取已支付金额
|
||||
* @param string $oCode
|
||||
* @return float
|
||||
*/
|
||||
public static function leave(string $oCode): float
|
||||
{
|
||||
$where = ['order_code' => $oCode, 'payment_status' => 1];
|
||||
return WechatPaymentRecord::mk()->where($where)->sum('payment_amount');
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化支付实现
|
||||
* @param array|null $config
|
||||
* @return \WePayV3\Order
|
||||
* @throws \WeChat\Exceptions\InvalidResponseException
|
||||
* @throws \WeChat\Exceptions\LocalCacheException
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
protected static function withPayment(?array $config = null): Order
|
||||
{
|
||||
return Order::instance($config ?: WechatService::getConfig());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取支付通知地址
|
||||
* @param string $order 订单单号
|
||||
* @param string $scene 支付场景
|
||||
* @param array $extra
|
||||
* @return string
|
||||
*/
|
||||
protected static function withNotifyUrl(string $order, string $scene = 'order', array $extra = []): string
|
||||
{
|
||||
$data = ['scen' => $scene, 'order' => $order];
|
||||
$vars = CodeExtend::enSafe64(json_encode($extra + $data, 64 | 256));
|
||||
return sysuri('@plugin-wxpay-notify', [], false, true) . "/{$vars}";
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建支付行为
|
||||
* @param string $openid 用户编号
|
||||
* @param string $oCode 订单单号
|
||||
* @param string $oName 订单标题
|
||||
* @param string $oAmount 订单总金额
|
||||
* @param string $pType 支付平台
|
||||
* @param string $pCode 子支付单号
|
||||
* @param string $pAmount 子支付金额
|
||||
* @return array
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
protected static function createPaymentAction(string $openid, string $oCode, string $oName, string $oAmount, string $pType, string $pCode, string $pAmount): array
|
||||
{
|
||||
// 检查是否已经支付
|
||||
$leave = static::leave($oCode);
|
||||
if ($leave >= floatval($oAmount)) {
|
||||
throw new Exception("订单 {$oCode} 已经完成支付!", 1);
|
||||
}
|
||||
if ($leave + floatval($pAmount) > floatval($oAmount)) {
|
||||
throw new Exception('总支付金额大于订单金额!', 0);
|
||||
}
|
||||
$map = ['order_code' => $oCode, 'payment_status' => 1];
|
||||
$model = WechatPaymentRecord::mk()->where($map)->findOrEmpty();
|
||||
if ($model->isExists()) throw new Exception("订单 {$oCode} 已经完成支付!", 1);
|
||||
// 写入订单支付行为
|
||||
$model->save([
|
||||
'type' => $pType,
|
||||
'code' => $pCode,
|
||||
'appid' => WechatService::getAppid(),
|
||||
'openid' => $openid,
|
||||
'order_code' => $oCode,
|
||||
'order_name' => $oName,
|
||||
'order_amount' => $oAmount,
|
||||
]);
|
||||
return $model->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新创建支付行为
|
||||
* @param string $pCode 商户订单单号
|
||||
* @param string $pTrade 平台交易单号
|
||||
* @param string $pAmount 实际到账金额
|
||||
* @param array $extra 订单扩展数据
|
||||
* @return boolean|array
|
||||
*/
|
||||
protected static function updatePaymentAction(string $pCode, string $pTrade, string $pAmount, array $extra = [])
|
||||
{
|
||||
// 更新支付记录
|
||||
$model = WechatPaymentRecord::mk()->where(['code' => $pCode])->findOrEmpty();
|
||||
if ($model->isEmpty()) return false;
|
||||
// 更新支付行为
|
||||
foreach ($extra as $k => $v) if (is_null($v)) unset($extra[$k]);
|
||||
$model->save($extra + ['payment_trade' => $pTrade, 'payment_status' => 1, 'payment_amount' => $pAmount]);
|
||||
// 触发支付成功事件
|
||||
Library::$sapp->event->trigger('WechatPaymentSuccess', $model->refresh()->toArray());
|
||||
// 返回支付行为数据
|
||||
return $model->toArray();
|
||||
}
|
||||
}
|
66
app/wechat/view/api/test/jsapi.html
Normal file
66
app/wechat/view/api/test/jsapi.html
Normal file
@ -0,0 +1,66 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>微信 JSAPI 支付测试</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
|
||||
<link rel="stylesheet" href="__ROOT__/static/theme/css/mobile.css">
|
||||
<style>
|
||||
.test-show {
|
||||
padding: 10px;
|
||||
font-size: 14px;
|
||||
margin-top: 10px;
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.test-show pre {
|
||||
overflow: auto;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.test-payment {
|
||||
padding: 30px 0 10px 0;
|
||||
text-align: center
|
||||
}
|
||||
|
||||
.test-payment button {
|
||||
border: none;
|
||||
color: #fff;
|
||||
padding: 15px;
|
||||
background: #a233c6;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div style="padding:15px">
|
||||
<h3>1. 用户 OPENID</h3>
|
||||
<div class="test-show">
|
||||
<pre>{$user['openid']}</pre>
|
||||
</div>
|
||||
<br>
|
||||
<h3>2. 微信 JSAPI 支付参数</h3>
|
||||
<div class="test-show">
|
||||
<pre>{:json_encode($result,64|128|256)}</pre>
|
||||
</div>
|
||||
<div class="test-payment">
|
||||
<button id='paytest' type='button'>发起 JSAPI 支付 0.01 元</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src='//res.wx.qq.com/open/js/jweixin-1.6.0.js'></script>
|
||||
<script>
|
||||
wx.config(JSON.parse('{$configJson|raw}'));
|
||||
document.getElementById('paytest').onclick = function () {
|
||||
let options = JSON.parse('{$optionJson|raw}');
|
||||
options.success = function () {
|
||||
alert('支付成功');
|
||||
};
|
||||
console.log("OPTIONS:", options);
|
||||
wx.chooseWXPay(options);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -13,7 +13,7 @@
|
||||
|
||||
<form action="{:url('payment_save')}" method="post" data-auto="true" class='layui-form layui-card' lay-filter="payment">
|
||||
|
||||
<div class="layui-card-body padding-left-40">
|
||||
<div class="layui-card-body padding-left-40" style="max-width:850px">
|
||||
|
||||
<label class="layui-form-item relative block">
|
||||
<span class="help-label"><b>微信商户账号</b>MCH_ID</span>
|
||||
|
91
app/wechat/view/payment/record/index.html
Normal file
91
app/wechat/view/payment/record/index.html
Normal file
@ -0,0 +1,91 @@
|
||||
{extend name="table"}
|
||||
|
||||
{block name="button"}
|
||||
<!--{if auth("queue")}-->
|
||||
<!--<button class='layui-btn layui-btn-sm layui-btn-primary' data-queue='{:url("queue")}'>刷新领取次数</button>-->
|
||||
<!--{/if}-->
|
||||
{/block}
|
||||
|
||||
{block name="content"}
|
||||
<div class="think-box-shadow">
|
||||
{include file='payment/record/index_search'}
|
||||
<table id="PaymentRecord" data-url="{:request()->url()}" data-line="2" data-target-search="form.form-search"></table>
|
||||
</div>
|
||||
{/block}
|
||||
|
||||
{block name='script'}
|
||||
<script>
|
||||
$(function () {
|
||||
$('#PaymentRecord').layTable({
|
||||
even: true, height: 'full',
|
||||
sort: {field: 'id', type: 'desc'},
|
||||
cols: [[
|
||||
{field: 'id', hide: true},
|
||||
{field: 'headimg', title: '头像', width: 70, align: 'center', templet: '<div>{{-showTableImage(d.fans_headimg,true,"sm")}}</div>'},
|
||||
{
|
||||
field: 'id', title: '用户信息', minWidth: 170, templet: function (d) {
|
||||
let tpls = [];
|
||||
tpls.push('用户昵称:{{d.fans_nickname||"-"}}');
|
||||
tpls.push('用户标识:{{d.openid||"-"}}');
|
||||
return laytpl(tpls.join('<br>')).render(d);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: '', title: '订单内容', minWidth: 200, width: '18%', templet: function (d) {
|
||||
let tpls = [];
|
||||
tpls.push('订单编号:<b class="color-blue font-code">{{d.order_code}}</b>');
|
||||
tpls.push('订单名称:<span>{{d.order_name||"-"}}</span>');
|
||||
return laytpl(tpls.join('<br>')).render(d);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'order_amount', title: '交易金额', sort: true, minWidth: 200, width: '18%', templet: function (d) {
|
||||
let lines = [];
|
||||
lines.push('交易单号:<b class="color-blue font-code">{{d.code||"-"}}</b>');
|
||||
if (d.payment_status) {
|
||||
lines.push('订单需支付 <b class="color-blue">{{d.order_amount}}</b> 元 ( 已支付 <b class="color-blue">{{d.payment_amount}}</b> 元 )');
|
||||
} else {
|
||||
lines.push('订单金额:<b class="color-blue">{{d.order_amount}}</b> 元');
|
||||
}
|
||||
return laytpl(lines.join('<br>')).render(d);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'unlock', title: '交易状态', minWidth: 190, width: '17%', templet: function (d) {
|
||||
if (d.payment_status) {
|
||||
d.typeInfo = '<b class="color-green">已支付</b>';
|
||||
d.typeLabel = '支付';
|
||||
d.typeDatetime = d.payment_time || '-';
|
||||
} else {
|
||||
d.typeInfo = '<b class="color-blue">待支付</b>';
|
||||
d.typeLabel = '生成';
|
||||
d.typeDatetime = d.create_time || '-';
|
||||
}
|
||||
if (d.payment_amount > 0 && d.payment_amount <= d.refund_amount) {
|
||||
d.typeInfo += ' <b class="color-red">已全额退款</b>';
|
||||
} else if (d.payment_amount > 0 && d.refund_amount > 0) {
|
||||
d.typeInfo += '<b class="color-red"> 已退款 ' + d.refund_amount + ' 元</b>'
|
||||
}
|
||||
return laytpl('<div>当前状态:{{-d.typeInfo}}<br>{{d.typeLabel}}时间:{{d.typeDatetime}}</div>').render(d);
|
||||
}
|
||||
},
|
||||
{field: 'name', title: '支付描述', minWidth: 100, templet: '<div>支付类型:{{d.type_name||"-"}}<br>操作描述:{{d.payment_remark||"-"}}</div>'},
|
||||
/* {if auth('refund') } */
|
||||
{toolbar: '#toolbar', title: '操作面板', minWidth: 80, width: '10%', align: 'center', fixed: 'right'}
|
||||
/* {/if} */
|
||||
// {field: 'id', title: '操作时间', minWidth: 235, sort: true, templet: '<div>更新时间:{{d.update_time||"-"}}<br>创建时间:{{d.create_time||"-"}}</div>'},
|
||||
]]
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="toolbar">
|
||||
<!--{if auth("refund")}-->
|
||||
{{# if(d.payment_status && d.refund_amount < d.payment_amount){ }}
|
||||
<a class="layui-btn layui-btn-sm layui-btn-danger" data-action="{:url('refund')}" data-value="code#{{d.code}}" data-confirm="确认要提交退款吗?">退 款</a>
|
||||
{{# }else{ }}
|
||||
<a class="layui-btn layui-btn-sm layui-disabled">退 款</a>
|
||||
{{# } }}
|
||||
<!--{/if}-->
|
||||
</script>
|
||||
{/block}
|
72
app/wechat/view/payment/record/index_search.html
Normal file
72
app/wechat/view/payment/record/index_search.html
Normal file
@ -0,0 +1,72 @@
|
||||
<fieldset>
|
||||
<legend>条件搜索</legend>
|
||||
<form class="layui-form layui-form-pane form-search" action="{:request()->url()}" onsubmit="return false" method="get" autocomplete="off">
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">用户账号</label>
|
||||
<label class="layui-input-inline">
|
||||
<input name="nickname" value="{$get.nickname|default=''}" placeholder="请输入用户账号" class="layui-input">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">订单内容</label>
|
||||
<label class="layui-input-inline">
|
||||
<input name="order" value="{$get.order|default=''}" placeholder="请输入订单内容" class="layui-input">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">支付状态</label>
|
||||
<label class="layui-input-inline">
|
||||
<select class="layui-select" name="payment_status">
|
||||
<option value=''>-- 全部 --</option>
|
||||
{foreach ['未支付','已支付'] as $k=>$v}
|
||||
{if isset($get.payment_status) and $get.payment_status eq $k.""}
|
||||
<option selected value="{$k}">{$v}</option>
|
||||
{else}
|
||||
<option value="{$k}">{$v}</option>
|
||||
{/if}{/foreach}
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">创建时间</label>
|
||||
<label class="layui-input-inline">
|
||||
<input data-date-range name="create_time" value="{$get.create_time|default=''}" placeholder="请选择创建时间" class="layui-input">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<button type="submit" class="layui-btn layui-btn-primary"><i class="layui-icon"></i> 搜 索</button>
|
||||
<button type="button" data-form-export="{:url('index')}?type={$type|default=''}" class="layui-btn layui-btn-primary">
|
||||
<i class="layui-icon layui-icon-export"></i> 导 出
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</fieldset>
|
||||
|
||||
<script>
|
||||
window.form.render();
|
||||
require(['excel'], function (excel) {
|
||||
excel.bind(function (data) {
|
||||
data.forEach(function (item, index) {
|
||||
data[index] = [
|
||||
item.id || 0,
|
||||
item.order_name || '',
|
||||
item.order_code || '',
|
||||
item.order_amount || '',
|
||||
item.payment_status ? '已支付' : '未支付',
|
||||
item.type || '',
|
||||
item.payment_time || '',
|
||||
item.create_time || '',
|
||||
];
|
||||
});
|
||||
data.unshift(['ID', '订单名称', '订单编号', '订单金额', '支付状态', '支付类型', '支付时间', '创建时间']);
|
||||
|
||||
return this.withStyle(data, {G: 160});
|
||||
|
||||
}, '支付行为数据' + layui.util.toDateString(Date.now(), '_yyyyMMdd_HHmmss'));
|
||||
});
|
||||
</script>
|
94
app/wechat/view/payment/refund/index.html
Normal file
94
app/wechat/view/payment/refund/index.html
Normal file
@ -0,0 +1,94 @@
|
||||
{extend name="table"}
|
||||
|
||||
{block name="content"}
|
||||
<div class="think-box-shadow">
|
||||
{include file='payment/refund/index_search'}
|
||||
<table id="PaymentRecord" data-url="{:request()->url()}" data-line="3" data-target-search="form.form-search"></table>
|
||||
</div>
|
||||
{/block}
|
||||
|
||||
{block name='script'}
|
||||
<script>
|
||||
$(function () {
|
||||
$('#PaymentRecord').layTable({
|
||||
even: true, height: 'full',
|
||||
sort: {field: 'id', type: 'desc'},
|
||||
cols: [[
|
||||
{field: 'id', hide: true},
|
||||
{field: 'headimg', title: '头像', width: 95, align: 'center', templet: '<div>{{-showTableImage(d.record.fans_headimg,true,"md")}}</div>'},
|
||||
{
|
||||
field: '', title: '用户账号', minWidth: 170, templet: function (d) {
|
||||
let tpls = [];
|
||||
tpls.push('<div class="padding-top-10"></div>用户昵称:{{d.record.fans_nickname||"-"}}');
|
||||
tpls.push('用户标识:{{d.record.openid||"-"}}');
|
||||
return laytpl(tpls.join('<br>')).render(d);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: '', title: '订单内容', minWidth: 180, width: '18%', templet: function (d) {
|
||||
let tpls = [];
|
||||
tpls.push('<b class="color-blue font-code">{{d.record.order_code}}</b>');
|
||||
tpls.push('{{d.record.order_name||"-"}}');
|
||||
tpls.push('需支付 <b class="color-blue">{{d.record.order_amount||"-"}}</b> 元');
|
||||
return laytpl(tpls.join('<br>')).render(d);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: '', title: '支付交易', minWidth: 180, width: '20%', templet: function (d) {
|
||||
let lines = [];
|
||||
lines.push('<b class="color-blue font-code">{{d.record.code||"-"}}</b>');
|
||||
lines.push('<b class="color-blue font-code">{{d.record.payment_trade||"-"}}</b>');
|
||||
if (d.record.payment_status) {
|
||||
lines.push('{{d.record.type_name}},已支付 <b class="color-blue">{{d.record.payment_amount}}</b> 元');
|
||||
} else {
|
||||
lines.push('还未支付');
|
||||
}
|
||||
return laytpl(lines.join('<br>')).render(d);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'refund_amount', title: '退款金额', sort: true, minWidth: 180, width: '20%', templet: function (d) {
|
||||
let lines = [];
|
||||
lines.push('<b class="color-blue font-code">{{d.code||"-"}}</b>');
|
||||
lines.push('<b class="color-blue font-code">{{d.refund_trade||"-"}}</b>');
|
||||
if (d.refund_status) {
|
||||
lines.push('退回{{d.refund_account||"-"}},已退款 <b class="color-blue">{{d.refund_amount}}</b> 元');
|
||||
} else {
|
||||
lines.push('申请退款 <b class="color-blue">{{d.refund_amount}}</b> 元');
|
||||
}
|
||||
return laytpl(lines.join('<br>')).render(d);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: '', title: '交易状态', minWidth: 190, width: '20%', templet: function (d) {
|
||||
if (d.refund_status) {
|
||||
d.typeInfo = '<b class="color-green">已退款</b>';
|
||||
d.typeLabel = '退款';
|
||||
d.typeDatetime = d.refund_time || '-';
|
||||
} else {
|
||||
d.typeInfo = '<b class="color-blue">待退款</b>';
|
||||
d.typeLabel = '生成';
|
||||
d.typeDatetime = d.create_time || '-';
|
||||
}
|
||||
d.typeInfo += " <span class='font-code'>" + d.refund_scode + "</span>";
|
||||
let tpls = [];
|
||||
tpls.push('当前状态:{{-d.typeInfo}}');
|
||||
tpls.push('{{d.typeLabel}}时间:{{d.typeDatetime}}');
|
||||
tpls.push('创建时间:{{d.create_time}}');
|
||||
return laytpl(tpls.join('<br>')).render(d);
|
||||
}
|
||||
},
|
||||
// {
|
||||
// field: '', title: '支付描述', minWidth: 100, templet: function (d) {
|
||||
// let tpls = [];
|
||||
// tpls.push('<div class="padding-top-10"></div>退回方式:{{d.refund_account||"-"}}');
|
||||
// tpls.push('操作描述:{{d.refund_remark||"-"}}');
|
||||
// return laytpl(tpls.join('<br>')).render(d);
|
||||
// }
|
||||
// },
|
||||
// {field: 'id', title: '操作时间', minWidth: 235, sort: true, templet: '<div>更新时间:{{d.update_time||"-"}}<br>创建时间:{{d.create_time||"-"}}</div>'},
|
||||
]]
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{/block}
|
44
app/wechat/view/payment/refund/index_search.html
Normal file
44
app/wechat/view/payment/refund/index_search.html
Normal file
@ -0,0 +1,44 @@
|
||||
<fieldset>
|
||||
<legend>条件搜索</legend>
|
||||
<form class="layui-form layui-form-pane form-search" action="{:request()->url()}" onsubmit="return false" method="get" autocomplete="off">
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">用户账号</label>
|
||||
<label class="layui-input-inline">
|
||||
<input name="nickname" value="{$get.nickname|default=''}" placeholder="请输入用户账号" class="layui-input">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">订单内容</label>
|
||||
<label class="layui-input-inline">
|
||||
<input name="order" value="{$get.order|default=''}" placeholder="请输入订单内容" class="layui-input">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">支付交易</label>
|
||||
<label class="layui-input-inline">
|
||||
<input name="payment" value="{$get.payment|default=''}" placeholder="请输入交易内容" class="layui-input">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">退款单号</label>
|
||||
<label class="layui-input-inline">
|
||||
<input name="refund" value="{$get.refund|default=''}" placeholder="请输入退款单号" class="layui-input">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">创建时间</label>
|
||||
<label class="layui-input-inline">
|
||||
<input data-date-range name="create_time" value="{$get.create_time|default=''}" placeholder="请选择创建时间" class="layui-input">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<button type="submit" class="layui-btn layui-btn-primary"><i class="layui-icon"></i> 搜 索</button>
|
||||
</div>
|
||||
</form>
|
||||
</fieldset>
|
129
database/migrations/20221013045831_update_wechat20230628.php
Normal file
129
database/migrations/20221013045831_update_wechat20230628.php
Normal file
@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Wechat Plugin for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2023 Anyon <zoujingli@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免责声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat
|
||||
// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
use think\migration\Migrator;
|
||||
|
||||
@set_time_limit(0);
|
||||
@ini_set('memory_limit', -1);
|
||||
|
||||
/**
|
||||
* 微信模块数据表
|
||||
*/
|
||||
class UpdateWechat20230628 extends Migrator
|
||||
{
|
||||
|
||||
/**
|
||||
* 创建数据库
|
||||
*/
|
||||
public function change()
|
||||
{
|
||||
$this->_create_wechat_payment_record();
|
||||
$this->_create_wechat_payment_refund();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建数据对象
|
||||
* @class WechatPaymentRecord
|
||||
* @table wechat_payment_record
|
||||
* @return void
|
||||
*/
|
||||
private function _create_wechat_payment_record()
|
||||
{
|
||||
|
||||
// 当前数据表
|
||||
$table = 'wechat_payment_record';
|
||||
|
||||
// 存在则跳过
|
||||
if ($this->hasTable($table)) return;
|
||||
|
||||
// 创建数据表
|
||||
$this->table($table, [
|
||||
'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '微信-支付-行为',
|
||||
])
|
||||
->addColumn('type', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '交易方式'])
|
||||
->addColumn('code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '发起支付号'])
|
||||
->addColumn('appid', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '发起APPID'])
|
||||
->addColumn('openid', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '用户OPENID'])
|
||||
->addColumn('order_code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '原订单编号'])
|
||||
->addColumn('order_name', 'string', ['limit' => 255, 'default' => '', 'null' => true, 'comment' => '原订单标题'])
|
||||
->addColumn('order_amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '原订单金额'])
|
||||
->addColumn('payment_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '支付完成时间'])
|
||||
->addColumn('payment_trade', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '平台交易编号'])
|
||||
->addColumn('payment_status', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '支付状态(0未付,1已付,2取消)'])
|
||||
->addColumn('payment_amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '实际到账金额'])
|
||||
->addColumn('payment_bank', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '支付银行类型'])
|
||||
->addColumn('payment_notify', 'text', ['default' => NULL, 'null' => true, 'comment' => '支付结果通知'])
|
||||
->addColumn('payment_remark', 'string', ['limit' => 999, 'default' => '', 'null' => true, 'comment' => '支付状态备注'])
|
||||
->addColumn('refund_status', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '退款状态(0未退,1已退)'])
|
||||
->addColumn('refund_amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '退款金额'])
|
||||
->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间'])
|
||||
->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间'])
|
||||
->addIndex('type', ['name' => 'idx_wechat_payment_record_type'])
|
||||
->addIndex('code', ['name' => 'idx_wechat_payment_record_code'])
|
||||
->addIndex('appid', ['name' => 'idx_wechat_payment_record_appid'])
|
||||
->addIndex('openid', ['name' => 'idx_wechat_payment_record_openid'])
|
||||
->addIndex('order_code', ['name' => 'idx_wechat_payment_record_order_code'])
|
||||
->addIndex('create_time', ['name' => 'idx_wechat_payment_record_create_time'])
|
||||
->addIndex('payment_trade', ['name' => 'idx_wechat_payment_record_payment_trade'])
|
||||
->addIndex('payment_status', ['name' => 'idx_wechat_payment_record_payment_status'])
|
||||
->create();
|
||||
|
||||
// 修改主键长度
|
||||
$this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建数据对象
|
||||
* @class WechatPaymentRefund
|
||||
* @table wechat_payment_refund
|
||||
* @return void
|
||||
*/
|
||||
private function _create_wechat_payment_refund()
|
||||
{
|
||||
|
||||
// 当前数据表
|
||||
$table = 'wechat_payment_refund';
|
||||
|
||||
// 存在则跳过
|
||||
if ($this->hasTable($table)) return;
|
||||
|
||||
// 创建数据表
|
||||
$this->table($table, [
|
||||
'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '微信-支付-退款',
|
||||
])
|
||||
->addColumn('code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '发起支付号'])
|
||||
->addColumn('record_code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '子支付编号'])
|
||||
->addColumn('refund_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '支付完成时间'])
|
||||
->addColumn('refund_trade', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '平台交易编号'])
|
||||
->addColumn('refund_status', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '支付状态(0未付,1已付,2取消)'])
|
||||
->addColumn('refund_amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '实际到账金额'])
|
||||
->addColumn('refund_account', 'string', ['limit' => 180, 'default' => '', 'null' => true, 'comment' => '退款目标账号'])
|
||||
->addColumn('refund_scode', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '退款状态码'])
|
||||
->addColumn('refund_remark', 'string', ['limit' => 999, 'default' => '', 'null' => true, 'comment' => '支付状态备注'])
|
||||
->addColumn('refund_notify', 'text', ['default' => NULL, 'null' => true, 'comment' => '退款交易通知'])
|
||||
->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间'])
|
||||
->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间'])
|
||||
->addIndex('code', ['name' => 'idx_wechat_payment_refund_code'])
|
||||
->addIndex('create_time', ['name' => 'idx_wechat_payment_refund_create_time'])
|
||||
->addIndex('record_code', ['name' => 'idx_wechat_payment_refund_record_code'])
|
||||
->addIndex('refund_trade', ['name' => 'idx_wechat_payment_refund_refund_trade'])
|
||||
->addIndex('refund_status', ['name' => 'idx_wechat_payment_refund_refund_status'])
|
||||
->create();
|
||||
|
||||
// 修改主键长度
|
||||
$this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]);
|
||||
}
|
||||
}
|
@ -664,7 +664,7 @@ $(function () {
|
||||
res.data = params.filter(res.data, res);
|
||||
}
|
||||
if (!this.page || !this.page.curr) return res;
|
||||
let curp = this.page.curr, maxp = Math.ceil(res.count / option.limit);
|
||||
let curp = this.page.curr, maxp = Math.ceil(res.count / (this.page.limit || option.limit));
|
||||
if (curp > maxp && maxp > 1) $table.trigger('reload', {page: {curr: maxp}});
|
||||
return res;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user