同步更新微信模块

This commit is contained in:
邹景立 2023-06-28 20:24:11 +08:00
parent 0a47af5138
commit 1a51281a3c
16 changed files with 1233 additions and 54 deletions

View File

@ -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"],
]
]
];
}
}

View File

@ -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();
}
/**

View 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());
}
}
}

View 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()}");
}
});
}
}

View 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;
}
}

View 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) : '';
}
}

View File

@ -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();

View 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();
}
}

View 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>

View File

@ -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>

View 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}

View 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">&#xe615;</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>

View 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}

View 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">&#xe615;</i> 搜 索</button>
</div>
</form>
</fieldset>

View 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]);
}
}

View File

@ -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;
};