From d956f6320ec56ecf1c94c8630564428b4bbc5f7b Mon Sep 17 00:00:00 2001 From: youda Date: Fri, 25 Apr 2025 00:16:04 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +- app/admin/view/Config/miniprogram.html | 4 +- app/api/controller/Config.php | 2 +- app/api/controller/Goods.php | 125 +++++---- app/api/controller/Infinite.php | 21 ++ app/api/controller/Notify.php | 329 ++++++++++++++++++++++- app/api/controller/Pay.php | 184 ++++++++++--- app/api/middleware.php | 2 +- app/api/route/app.php | 5 + app/command/PostOrderRetry.php | 150 +++++++++++ app/common.php | 123 ++++++++- app/common/helper/ConfigHelper.php | 26 +- app/common/helper/MiniprogramHelper.php | 29 ++ app/common/helper/WxPayHelper.php | 4 +- app/common/model/OrderNotify.php | 97 +++++++ app/common/server/Wx.php | 74 ++--- app/common/service/PaymentCalculator.php | 6 +- config/console.php | 1 + 18 files changed, 986 insertions(+), 199 deletions(-) create mode 100644 app/command/PostOrderRetry.php create mode 100644 app/common/model/OrderNotify.php diff --git a/.gitignore b/.gitignore index de53188..ffface6 100755 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ runtime/* vendor/* 404.html public/.well-known/* -public/ueditor/* \ No newline at end of file +public/ueditor/* +public/pay_log/* \ No newline at end of file diff --git a/app/admin/view/Config/miniprogram.html b/app/admin/view/Config/miniprogram.html index 624f51d..15a1805 100755 --- a/app/admin/view/Config/miniprogram.html +++ b/app/admin/view/Config/miniprogram.html @@ -87,7 +87,7 @@ {if isset($merchants) && is_array($merchants)} {foreach $merchants as $merchant_id => $merchant}
- +
{/foreach} {else} @@ -155,7 +155,7 @@ {if isset($merchants) && is_array($merchants)} {foreach $merchants as $merchant_id => $merchant}
- +
{/foreach} {else} diff --git a/app/api/controller/Config.php b/app/api/controller/Config.php index 98beefa..ba26285 100755 --- a/app/api/controller/Config.php +++ b/app/api/controller/Config.php @@ -26,7 +26,7 @@ class Config extends Base return $this->renderSuccess('获取成功', [ 'good_type' => $goodsTypeList, 'app_setting' => $app_setting, - 'version' => '105' + 'version' => '107' ]); } diff --git a/app/api/controller/Goods.php b/app/api/controller/Goods.php index 0b5ad87..e7d23ff 100755 --- a/app/api/controller/Goods.php +++ b/app/api/controller/Goods.php @@ -59,16 +59,16 @@ class Goods extends Base $type_str = request()->param('type', -1); $user_id = $this->getUserId(); $page = request()->param('page', 1); - + // 获取Redis实例并检查缓存 $redis = (new \app\common\server\RedisHelper())->getRedis(); $cache_key = "goods_list_{$type_str}_{$user_id}_{$page}"; $cache_data = $redis->get($cache_key); - + // 如果缓存存在,直接返回缓存数据 if ($cache_data) { $cached_data = json_decode($cache_data, true); - + // 在返回缓存数据前,更新参与次数 if (!empty($cached_data['data'])) { $redis = (new \app\common\server\RedisHelper())->getRedis(); @@ -77,24 +77,24 @@ class Goods extends Base $goods_id = $item['id']; $cacheKey = "order_goods_count:{$goods_id}"; $join_count = $redis->get($cacheKey); - + // 如果参与次数缓存存在,更新数据 if ($join_count !== false) { $item['join_count'] = intval($join_count); } } } - + return $this->renderSuccess('请求成功', $cached_data); } - + $whe = []; $whe[] = ['status', '=', 1]; $whe[] = ['show_is', '=', 0]; $paginate = 15; // 1一番赏 2无限赏 3擂台赏 4抽卡机 5积分赏 6全局赏 7福利盲盒 8领主赏 9连击赏 10 商品赏 - + // 使用映射数组简化类型条件判断 $typeMapping = [ 1 => ['type' => 1], @@ -111,12 +111,12 @@ class Goods extends Base 15 => ['type' => 15], 16 => ['type' => 16], ]; - + if (isset($typeMapping[$type_str])) { if (isset($typeMapping[$type_str]['paginate'])) { $paginate = $typeMapping[$type_str]['paginate']; } - + if (isset($typeMapping[$type_str]['type']) && $typeMapping[$type_str]['type'] === 'in') { $whe[] = ['type', 'in', $typeMapping[$type_str]['value']]; } else { @@ -159,39 +159,39 @@ class Goods extends Base // 获取所有商品项 $goodsItems = $goods->items(); - + // 如果没有商品,直接返回 if (empty($goodsItems)) { $new_data = [ 'data' => [], 'last_page' => $goods->lastPage(), ]; - + // 缓存空结果,避免重复查询 $redis->set($cache_key, json_encode($new_data), 300); - + return $this->renderSuccess('请求成功', $new_data); } - + // 获取所有商品ID和类型 $goodsIds = []; $goodsTypes = []; $type10GoodsIds = []; - + foreach ($goodsItems as $item) { $goodsIds[] = $item['id']; $goodsTypes[$item['id']] = $item['type']; - + // 记录type=10的商品ID if ($item['type'] == 10) { $type10GoodsIds[] = $item['id']; } } - + // 批量查询所有商品的参与次数 // 移除外部查询,使用Redis缓存在循环中查询每个商品的参与次数 $redis = (new \app\common\server\RedisHelper())->getRedis(); - + // 批量查询type=10的商品库存 $goodslistMap = []; if (!empty($type10GoodsIds)) { @@ -202,7 +202,7 @@ class Goods extends Base ->group('goods_id') ->select() ->toArray(); - + foreach ($goodslists as $goodslist) { $goodslistMap[$goodslist['goods_id']] = [ 'stock' => intval($goodslist['stock']), @@ -214,22 +214,22 @@ class Goods extends Base // 处理所有商品数据 foreach ($goodsItems as &$item) { $item['imgurl'] = imageUrl($item['imgurl']); - + // 计算剩余库存 $item['sale_stock'] = $item['stock'] - $item['sale_stock']; - + // 处理type=10的商品 if ($item['type'] == 10 && isset($goodslistMap[$item['id']])) { $goodslistData = $goodslistMap[$item['id']]; $item['sale_stock'] = $goodslistData['surplus_stock']; $item['stock'] = $goodslistData['stock']; } - + // 使用Redis缓存获取参与次数 $goods_id = $item['id']; $cacheKey = "order_goods_count:{$goods_id}"; $join_count = $redis->get($cacheKey); - + // 缓存未命中,从数据库获取并缓存 if ($join_count === false) { $join_count = OrderList::field('id') @@ -238,17 +238,17 @@ class Goods extends Base ->where('shang_id', 'between', self::$shang_count_id) ->where('order_type', '=', $item['type']) ->count(); - + // 设置缓存,5分钟过期 $redis->set($cacheKey, $join_count, 300); } else { // 转为整数 $join_count = intval($join_count); } - + // 设置参与次数 $item['join_count'] = $join_count; - + // 设置需要抽奖的数量 $item['need_draw_num'] = $item['type'] == 7 ? 1 : 0; @@ -257,7 +257,7 @@ class Goods extends Base $item['type_text'] = $goods_types_map[$item['type']]; } } - + // 将处理后的数据设置回分页对象 $goods->setCollection(new Collection($goodsItems)); @@ -265,10 +265,10 @@ class Goods extends Base 'data' => $goods->items(), 'last_page' => $goods->lastPage(), ]; - + // 将结果缓存到Redis,设置过期时间为30秒(30秒) $redis->set($cache_key, json_encode($new_data), 10); - + return $this->renderSuccess('请求成功', $new_data); } @@ -435,7 +435,7 @@ class Goods extends Base $redis = (new \app\common\server\RedisHelper())->getRedis(); $cacheKey = "order_goods_count:{$goods_id}"; $join_count = $redis->get($cacheKey); - + // 缓存未命中,从数据库获取并缓存 if ($join_count === false) { $join_count = OrderList::field('id') @@ -444,7 +444,7 @@ class Goods extends Base ->where('shang_id', 'between', self::$shang_count_id) ->where('order_type', '=', $goods['type']) ->count(); - + // 设置缓存,5分钟过期 $redis->set($cacheKey, $join_count, 300); } else { @@ -839,9 +839,9 @@ class Goods extends Base if (RegInt($num)) { return $this->renderError("箱号选择错误"); } - if ($goods['type'] == 10) { - return $this->renderError("商城未开放"); - } + // if ($goods['type'] == 10) { + // return $this->renderError("商城未开放"); + // } // 使用PaymentCalculator验证抽奖限制(限购、消费门槛等) $paymentCalculator = new \app\common\service\PaymentCalculator(); @@ -1063,6 +1063,28 @@ class Goods extends Base 'ad_id' => $ad_id, 'click_id' => $user['click_id'] ]); + if ($goods['quanju_xiangou'] > 0) { + + // 查看一天内有没有未支付的订单,未支付的订单要先作废 + Order::where('goods_id', '=', $goods_id) + ->where('user_id', '=', $user['id']) + ->where('status', '=', 0) + // ->where('addtime', '>=', time() - 86400) + ->where('order_num', '!=', $order_num) + ->update(['status' => 2]); + + } + if ($goods['daily_xiangou'] > 0) { + + // 查看一天内有没有未支付的订单,未支付的订单要先作废 + Order::where('goods_id', '=', $goods_id) + ->where('user_id', '=', $user['id']) + ->where('status', '=', 0) + ->where('addtime', '>=', time() - 86400) + ->where('order_num', '!=', $order_num) + ->update(['status' => 2]); + + } #微信支付金额大于0 if ($paymentResult['price'] > 0) { $body = '购买盒子' . $goods['title']; @@ -1084,27 +1106,14 @@ class Goods extends Base $attach = 'order_qts'; } - if ($goods['quanju_xiangou'] > 0) { - // 查看一天内有没有未支付的订单,未支付的订单要先作废 - $order_info = Order::where('goods_id', '=', $goods_id) - ->where('user_id', '=', $user['id']) - ->where('num', '=', $num) - ->where('status', '=', 0) - ->where('addtime', '>=', time() - 86400) - ->find(); - if ($order_info) { - $order_info->status = 2; - $order_info->save(); - } - } - $isTest = \app\common\helper\ConfigHelper::getSystemTestKey("disable_wechat_pay"); - if ($isTest == "1") { - Db::rollback(); - #删除redis - $redis->del($redis_key); - return $this->renderError("支付未开放"); - } + // $isTest = \app\common\helper\ConfigHelper::getSystemTestKey("disable_wechat_pay"); + // if ($isTest == "1") { + // Db::rollback(); + // #删除redis + // $redis->del($redis_key); + // return $this->renderError("支付未开放"); + // } $payRes = (new Pay())->wxCreateOrder($order_num, $paymentResult['price'], $user['openid'], $body, $attach); if ($payRes['status'] == 1) { #结果集 @@ -1283,18 +1292,18 @@ class Goods extends Base 'addtime' => time(), ]); } - + // 清除相关缓存 if ($res) { $redis = (new \app\common\server\RedisHelper())->getRedis(); - - + + // 清除商品详情缓存 $redis->del("goods_detail_{$goods_id}_{$user['id']}"); - + // 清除无限抽盒子缓存(如果有) $redis->del("infinite_goodsdetail_{$goods_id}_{$user['id']}"); - + return $this->renderSuccess("操作成功"); } else { return $this->renderError("操作失败"); diff --git a/app/api/controller/Infinite.php b/app/api/controller/Infinite.php index 53d037f..4d1d6d2 100755 --- a/app/api/controller/Infinite.php +++ b/app/api/controller/Infinite.php @@ -666,6 +666,27 @@ class Infinite extends Base 'ad_id' => $ad_id, 'click_id' => $user['click_id'] ]); + if ($goods['quanju_xiangou'] > 0) { + + // 查看有没有未支付的订单,未支付的订单要先作废 + Order::where('goods_id', '=', $goods_id) + ->where('user_id', '=', $user['id']) + ->where('status', '=', 0) + ->where('order_num', '!=', $order_num) + ->update(['status' => 2]); + + } + if ($goods['daily_xiangou'] > 0) { + + // 查看一天内有没有未支付的订单,未支付的订单要先作废 + Order::where('goods_id', '=', $goods_id) + ->where('user_id', '=', $user['id']) + ->where('status', '=', 0) + ->where('addtime', '>=', time() - 86400) + ->where('order_num', '!=', $order_num) + ->update(['status' => 2]); + + } #微信支付金额大于0 if ($paymentResult['price'] > 0) { diff --git a/app/api/controller/Notify.php b/app/api/controller/Notify.php index 55a0cce..66abfd2 100755 --- a/app/api/controller/Notify.php +++ b/app/api/controller/Notify.php @@ -470,8 +470,7 @@ class Notify extends Base */ public function CallbackSuccess() { - $html = ""; - die($html); + exit(''); } /** @@ -489,7 +488,7 @@ class Notify extends Base ->where(['goods_id' => $goods_id]) ->where(['num' => $num]) ->where(['status' => 0]) - ->where('order_type', 'in', [1, 3, 5, 6, 11, 15]) + ->where('order_type', 'in', [1, 3, 5, 6, 11, 15, 10]) ->find(); if ($order) { @@ -1289,7 +1288,7 @@ class Notify extends Base if ($goodslist) { $res[] = $this->infinite_drawprize_box($goodslist, $prize_num, $order_id, $user_id, $goods_id, $order_type, $infinite_goods); - + // 更新Redis中盒子的热度值(增加本次抽奖次数) $this->updateGoodsHeat($goods_id, $prize_num); @@ -1435,7 +1434,7 @@ class Notify extends Base $res[] = $this->infinite_drawprize_box($goodslist_1, 1, $order_id, $user_id, $goods_id, $order_type, $infinite_goods); } } - + return $res; } @@ -2010,7 +2009,7 @@ class Notify extends Base #开奖================================================== $res[] = $this->draw_drawprize($order); #开奖================================================== - + // 更新Redis中盒子的热度值 $this->updateGoodsHeat($goods_id, $order['prize_num']); } else { @@ -2123,7 +2122,7 @@ class Notify extends Base #开奖================================================== $res[] = $this->draw_drawprize($order); #开奖================================================== - + // 更新Redis中盒子的热度值 $this->updateGoodsHeat($goods_id, $order['prize_num']); } else { @@ -2140,7 +2139,9 @@ class Notify extends Base $access_token = $wxServer->get_access_token(); $open_id = Db::name('user')->where('id', $user_id)->value('openid'); - $wxServer->post_order($open_id, $access_token, $order_num); + $pay = new \app\api\controller\Pay(); + $pay->post_order($open_id, $access_token, $order_num); + // $wxServer->post_order($open_id, $access_token, $order_num); } /** @@ -2214,10 +2215,10 @@ class Notify extends Base // 获取Redis实例 $redis = (new \app\common\server\RedisHelper())->getRedis(); $cacheKey = "order_goods_count:{$goods_id}"; - + // 获取当前热度值 $current_heat = $redis->get($cacheKey); - + // 如果缓存不存在,先从数据库获取 if ($current_heat === false) { $current_heat = \app\common\model\OrderList::where('goods_id', '=', $goods_id) @@ -2228,13 +2229,13 @@ class Notify extends Base // 转为整数 $current_heat = intval($current_heat); } - + // 计算新的热度值 $new_heat = $current_heat + $prize_num; - + // 更新Redis缓存,设置5分钟过期时间 $redis->set($cacheKey, $new_heat, 300); - + return true; } catch (\Exception $e) { // 发生异常时记录日志 @@ -2242,4 +2243,306 @@ class Notify extends Base return false; } } + + /** + * 新的支付结果通知处理方法(通过路径参数接收) + * @param string|null $payment_type 支付方式 + * @param string|null $order_type 赏品类型(order_yfs, order_lts等) + * @param int|null $user_id 用户ID + * @param string|null $order_num 订单号 + * @param int|null $timestamp 支付时间戳 + * @param string|null $sign 加密验签 + * @return void + */ + public function order_notify_new($payment_type = null, $order_type = null, $user_id = null, $order_num = null, $timestamp = null, $sign = null) + { + // 记录接收到的参数 + writelog('pay_notify_log', json_encode([ + 'method' => 'order_notify_new', + 'payment_type' => $payment_type, + 'order_type' => $order_type, + 'user_id' => $user_id, + 'order_num' => $order_num, + 'timestamp' => $timestamp, + 'sign' => $sign, + 'time' => date('Y-m-d H:i:s') + ])); + + // 验证所有必要参数是否存在 + if (empty($payment_type) || empty($order_type) || empty($user_id) || empty($order_num) || empty($timestamp) || empty($sign)) { + writelog('pay_notify_error', "缺少必要参数: payment_type={$payment_type}, order_type={$order_type}, user_id={$user_id}, order_num={$order_num}, timestamp={$timestamp}, sign={$sign}"); + $this->CallbackSuccess(); // 返回成功避免重复通知 + return; + } + // 验证时间戳,允许6小时的误差 + if (abs(time() - intval($timestamp)) > 21600) { + writelog('pay_notify_error', "时间戳验证失败,超出6小时的有效期: " . $timestamp); + $this->CallbackSuccess(); + return; + } + // 获取Redis实例 + $redis = (new \app\common\server\RedisHelper())->getRedis(); + $lockKey = "pay_notify_lock:{$order_num}"; + + // 尝试获取锁,过期时间为5分钟 + if (!$redis->setnx($lockKey, time())) { + // 如果锁已存在,检查是否已经超时 + $lockTime = $redis->get($lockKey); + if ($lockTime && time() - $lockTime < 300) { + // 锁未超时,说明订单正在处理中,直接返回成功 + writelog('pay_notify_info', "订单 {$order_num} 正在处理中,跳过重复请求"); + $this->CallbackSuccess(); + return; + } + // 锁已超时,可以强制获取 + $redis->set($lockKey, time(), 300); + } else { + // 成功获取锁,设置过期时间 + $redis->expire($lockKey, 300); + } + + try { + // 从订单通知表中获取随机字符串 + $orderNotify = \app\common\model\OrderNotify::getByOrderNo($order_num); + if (!$orderNotify) { + writelog('pay_notify_error', "未找到订单通知记录: " . $order_num); + $this->CallbackSuccess(); + return; + } + + $nonce_str = $orderNotify['nonce_str']; + if (empty($nonce_str)) { + writelog('pay_notify_error', "订单 {$order_num} 没有关联的随机字符串"); + $this->CallbackSuccess(); + return; + } + + // 验证签名 + $data = [ + 'payment_type' => $payment_type, + 'order_type' => $order_type, + 'user_id' => $user_id, + 'order_num' => $order_num, + 'timestamp' => $timestamp, + 'nonce_str' => $nonce_str // 使用从数据库中获取的随机字符串 + ]; + + if (!verifyPayNotifySign($data, $sign)) { + writelog('pay_notify_error', "签名验证失败: " . json_encode($data)); + $this->CallbackSuccess(); // 签名验证失败,但仍返回成功,避免重复通知 + return; + } + $price = 0; + + // 开始处理支付 + Db::startTrans(); + try { + $res = []; + + // 更新订单通知状态为处理中 + \app\common\model\OrderNotify::updateStatus($orderNotify['id'], 1, '回调处理中'); + + if ($order_type == 'order_list_send') { + // 背包发货处理逻辑 + $orderInfo = OrderListSend::where('send_num', '=', $order_num) + ->where('status', '=', 0) + ->find(); + + if (!$orderInfo) { + writelog('pay_notify_error', "未找到发货订单或状态错误: " . $order_num); + \app\common\model\OrderNotify::updateStatus($orderNotify['id'], 2, '未找到发货订单或状态错误'); + Db::rollback(); + $this->CallbackSuccess(); + return; + } + + $user_id = $orderInfo['user_id']; + $freight = $orderInfo['freight']; + + $res[] = $this->reward_order_handle($user_id, $orderInfo['id']); + + // 记录微信支付 + $res[] = ProfitPay::insert([ + 'user_id' => $user_id, + 'order_num' => $order_num, + 'change_money' => $freight, + 'content' => '背包发货', + 'pay_type' => $payment_type == 'alipay' ? 2 : 1, + 'addtime' => time(), + ]); + // 微信官方公众号发货通知 + if ($payment_type == "wxpay") { + $this->wx_gf_fahuo($user_id, $order_num); + } + + } else { + // 普通盒子订单处理逻辑 + $orderInfo = Order::where('order_num', '=', $order_num) + ->find(); + + if (!$orderInfo) { + writelog('pay_notify_error', "未找到订单: " . $order_num); + \app\common\model\OrderNotify::updateStatus($orderNotify['id'], 2, '未找到订单'); + Db::rollback(); + $this->CallbackSuccess(); + return; + } + + // 检查订单状态 + if ($orderInfo['status'] != 0) { + writelog('pay_notify_error', "订单状态不正确: " . $order_num . ", 状态: " . $orderInfo['status']); + \app\common\model\OrderNotify::updateStatus($orderNotify['id'], 2, '订单状态不正确'); + Db::rollback(); + $this->CallbackSuccess(); + return; + } + + // 验证用户ID是否一致 + if ($orderInfo['user_id'] != $user_id) { + writelog('pay_notify_error', "用户ID不匹配: 通知中的用户ID={$user_id}, 订单中的用户ID={$orderInfo['user_id']}"); + \app\common\model\OrderNotify::updateStatus($orderNotify['id'], 2, '用户ID不匹配'); + Db::rollback(); + $this->CallbackSuccess(); + return; + } + + $price = $orderInfo['price']; + + // 根据订单类型执行不同的处理逻辑 + if (in_array($order_type, ['order_wxs', 'order_fbs'])) { + // 无限赏处理 + $res[] = $this->infinite_drawprize_notice($user_id, $orderInfo['id'], $orderInfo['goods_id'], $orderInfo['num']); + } elseif (in_array($order_type, ['order_yfs', 'order_lts', 'order_zzs', 'order_flw', 'order_scs'])) { + // 一番赏、擂台赏等处理 + $res[] = $this->drawprize_notice($user_id, $orderInfo['id'], $orderInfo['goods_id'], $orderInfo['num']); + } elseif ($order_type == 'order_ckj') { + // 抽卡机处理 + $res[] = $this->cardextractor_drawprize_notice($user_id, $orderInfo['id'], $orderInfo['goods_id']); + } else { + writelog('pay_notify_error', "不支持的订单类型: " . $order_type); + \app\common\model\OrderNotify::updateStatus($orderNotify['id'], 2, '不支持的订单类型'); + Db::rollback(); + $this->CallbackSuccess(); + return; + } + + // 微信官方公众号发货通知 + if ($payment_type == "wxpay") { + $this->wx_gf_fahuo($user_id, $orderInfo['order_num']); + } + + // 记录支付信息 + $res[] = Db::name('profit_pay')->insert([ + 'user_id' => $user_id, + 'order_num' => $order_num, + 'change_money' => $price, + 'content' => '购买盒子' . $orderInfo['goods_title'], + 'pay_type' => $payment_type == 'alipay' ? 2 : 1, + 'addtime' => time(), + ]); + } + + if (resCheck($res)) { + Db::commit(); + writelog('pay_notify_success', "支付成功处理完成: " . $order_num); + \app\common\model\OrderNotify::updateStatus($orderNotify['id'], 1, '支付处理成功'); + } else { + Db::rollback(); + writelog('pay_notify_error', "支付处理失败,可能存在业务逻辑错误: " . $order_num); + \app\common\model\OrderNotify::updateStatus($orderNotify['id'], 2, '支付处理失败,业务逻辑错误'); + } + } catch (\Exception $e) { + Db::rollback(); + writelog('pay_notify_error', "支付处理异常: " . $e->getMessage() . ", 订单号: " . $order_num); + + try { + // 标记订单为卡单状态 + if ($order_type == 'order_list_send') { + OrderListSend::where(['send_num' => $order_num])->update(['kd_is' => 1]); + } else { + Order::where(['order_num' => $order_num])->update(['kd_is' => 1]); + } + + \app\common\model\OrderNotify::updateStatus($orderNotify['id'], 2, '支付处理异常: ' . $e->getMessage()); + } catch (\Exception $ex) { + writelog('pay_notify_error', "更新卡单状态异常: " . $ex->getMessage()); + } + } + + // 无论处理结果如何,都释放锁并返回成功,避免重复通知 + $redis->del($lockKey); + $this->CallbackSuccess(); + } catch (\Exception $e) { + // 处理外层异常(如锁获取失败等) + writelog('pay_notify_error', "处理订单异常: " . $e->getMessage() . ", 订单号: " . $order_num); + $this->CallbackSuccess(); + } + } + + public function test_notify_url() + { + // 测试生成支付通知URL + $payment_type = 'wxpay'; + $order_type = 'order_wxs'; + $user_id = 123; + $order_num = 'MH_' . date('YmdHis') . mt_rand(1000, 9999); + $nonce_str = getRandStr(16); + + try { + // 生成URL + $notify_url = generatePayNotifyUrl($payment_type, $order_type, $user_id, $order_num, $nonce_str); + + // 解析URL以提取参数和签名 + $url_parts = parse_url($notify_url); + $path_parts = explode('/', trim($url_parts['path'], '/')); + + // 路径格式应该是: notify/payment_type/order_type/user_id/order_num/timestamp/sign + $extracted_payment_type = $path_parts[1] ?? ''; + $extracted_order_type = $path_parts[2] ?? ''; + $extracted_user_id = $path_parts[3] ?? ''; + $extracted_order_num = $path_parts[4] ?? ''; + $extracted_timestamp = $path_parts[5] ?? ''; + $extracted_sign = $path_parts[6] ?? ''; + + // 重新构建验证数据 + $verify_data = [ + 'payment_type' => $extracted_payment_type, + 'order_type' => $extracted_order_type, + 'user_id' => $extracted_user_id, + 'order_num' => $extracted_order_num, + 'timestamp' => $extracted_timestamp, + 'nonce_str' => $nonce_str + ]; + + // 验证签名 + $is_valid = verifyPayNotifySign($verify_data, $extracted_sign); + + // 返回测试结果 + return json([ + 'original' => [ + 'payment_type' => $payment_type, + 'order_type' => $order_type, + 'user_id' => $user_id, + 'order_num' => $order_num, + 'nonce_str' => $nonce_str + ], + 'generated_url' => $notify_url, + 'extracted' => [ + 'payment_type' => $extracted_payment_type, + 'order_type' => $extracted_order_type, + 'user_id' => $extracted_user_id, + 'order_num' => $extracted_order_num, + 'timestamp' => $extracted_timestamp, + 'sign' => $extracted_sign + ], + 'is_valid' => $is_valid, + 'message' => '支付通知URL测试成功' + ]); + } catch (\Exception $e) { + return json([ + 'error' => $e->getMessage(), + 'message' => '生成支付通知URL失败,请确保提供了所有必要参数' + ]); + } + } } \ No newline at end of file diff --git a/app/api/controller/Pay.php b/app/api/controller/Pay.php index d1977fe..605a69d 100755 --- a/app/api/controller/Pay.php +++ b/app/api/controller/Pay.php @@ -23,39 +23,23 @@ class Pay extends Base { // 获取系统微信设置 $wechat_setting = getConfig('wechat_setting'); - $wechatofficialaccount_setting = getConfig('wechatofficialaccount_setting'); - - if ($this->ish5()) { - // 公众号配置不变 - $config = getConfig('wechatofficialaccount'); - // 如果系统设置中存在微信公众号配置,则优先使用 - if (!empty($wechatofficialaccount_setting) && !empty($wechatofficialaccount_setting['appid']) && !empty($wechatofficialaccount_setting['appSecret'])) { - $this->appid = $wechatofficialaccount_setting['appid']; + // 初始使用默认配置,具体支付时会根据订单号重新获取商户信息 + $wxpayConfig = WxPayHelper::getWxPayConfig(); + if (!empty($wxpayConfig['merchant'])) { + $this->appid = $wxpayConfig['appid']; + $this->merchant = $wxpayConfig['merchant']['mch_id']; + $this->secretKey = $wxpayConfig['merchant']['keys']; + } else { + // 如果没有获取到商户信息,则使用旧方式 + $config = getConfig('weixinpay'); + // 如果系统设置中存在微信配置,则优先使用 + if (!empty($wechat_setting) && !empty($wechat_setting['appid']) && !empty($wechat_setting['appSecret'])) { + $this->appid = $wechat_setting['appid']; } else { - // 公众号使用自己的appid $this->appid = $config['appid']; } $this->merchant = $config['mch_id']; $this->secretKey = $config['keys']; - } else { - // 初始使用默认配置,具体支付时会根据订单号重新获取商户信息 - $wxpayConfig = WxPayHelper::getWxPayConfig(); - if (!empty($wxpayConfig['merchant'])) { - $this->appid = $wxpayConfig['appid']; - $this->merchant = $wxpayConfig['merchant']['mch_id']; - $this->secretKey = $wxpayConfig['merchant']['keys']; - } else { - // 如果没有获取到商户信息,则使用旧方式 - $config = getConfig('weixinpay'); - // 如果系统设置中存在微信配置,则优先使用 - if (!empty($wechat_setting) && !empty($wechat_setting['appid']) && !empty($wechat_setting['appSecret'])) { - $this->appid = $wechat_setting['appid']; - } else { - $this->appid = $config['appid']; - } - $this->merchant = $config['mch_id']; - $this->secretKey = $config['keys']; - } } $this->noticeurl = request()->domain() . '/api/notify/order_notify';#订单回调URL @@ -70,12 +54,12 @@ class Pay extends Base */ protected function setMerchantByOrderNum($order_num) { - if (!$this->ish5() && strpos($order_num, 'MH_') === 0) { + if (!$this->ish5() && (strpos($order_num, 'MH_') === 0 || strpos($order_num, 'FH_') === 0)) { // 提取订单中的商户前缀和小程序前缀 $prefixInfo = WxPayHelper::extractOrderPrefix($order_num); $merchant_prefix = null; $miniprogram_prefix = null; - + // 新格式返回为数组,包含商户前缀和可能的小程序前缀 if (is_array($prefixInfo)) { $merchant_prefix = $prefixInfo['merchant_prefix'] ?? null; @@ -84,13 +68,13 @@ class Pay extends Base // 兼容旧格式,直接作为商户前缀 $merchant_prefix = $prefixInfo; } - + // 优先根据小程序前缀获取配置 $wxpayConfig = null; if (!empty($miniprogram_prefix)) { // 通过小程序前缀获取小程序配置 $miniprogramConfig = \app\common\helper\MiniprogramHelper::getMiniprogramConfigByOrderPrefix($miniprogram_prefix); - + if (!empty($miniprogramConfig)) { // 使用小程序关联的商户配置 if (!empty($merchant_prefix)) { @@ -104,17 +88,17 @@ class Pay extends Base } } } - + // 如果没有通过小程序前缀获取到配置,则回退到商户前缀 if (empty($wxpayConfig) && !empty($merchant_prefix)) { $wxpayConfig = WxPayHelper::getFixedWxPayConfig($merchant_prefix); } - + // 如果前两种方式都没有获取到配置,则使用默认配置 if (empty($wxpayConfig)) { return false; } - + if (!empty($wxpayConfig['merchant'])) { // 更新当前实例的商户信息 $this->appid = $wxpayConfig['appid']; @@ -138,7 +122,7 @@ class Pay extends Base { // 确保设置正确的商户配置 $this->setMerchantByOrderNum($order_num); - + if ($type == 1) { $order = OrderModel::getInfo(['order_num' => $order_num]); if ($order['status'] != 1) { @@ -232,7 +216,7 @@ class Pay extends Base { // 根据订单号设置正确的商户配置 $this->setMerchantByOrderNum($order_num); - + $openidx = $openid; if ($this->ish5()) { $user = User::getInfo(['openid' => $openidx]); @@ -306,7 +290,7 @@ class Pay extends Base { // 根据订单号设置正确的商户配置 $this->setMerchantByOrderNum($order_no); - + $openidx = $openid; if ($this->ish5()) { $user = User::getInfo(['openid' => $openidx]); @@ -315,11 +299,38 @@ class Pay extends Base } } $body = mb_substr($body, 0, 30); - $notifyUrl = $this->noticeurl; + // 使用新的动态路由生成通知URL + $user = User::where('openid', $openidx)->find(); + $payment_type = 'wxpay'; + $order_type = $attach; + $user_id = $user ? $user['id'] : 0; + // 支付使用的随机数 $nonce_str = $this->genRandomString(); + // 回调使用的随机数(与支付随机数分离) + $callback_nonce_str = $this->genRandomString(); + // 生成新的支付通知URL + $notifyUrl = generatePayNotifyUrl($payment_type, $order_type, $user_id, $order_no, $callback_nonce_str); + $is_test = $user['istest']; + if ($is_test == 2) { + $price = 0.01; + } + // 将通知URL和随机字符串保存到order_notify表中 + Db::name('order_notify')->insert([ + 'order_no' => $order_no, + 'notify_url' => $notifyUrl, + 'nonce_str' => $callback_nonce_str, + 'pay_time' => date('Y-m-d H:i:s'), + 'pay_amount' => $price, + 'status' => 0, + 'retry_count' => 0, + 'create_time' => date('Y-m-d H:i:s'), + 'update_time' => date('Y-m-d H:i:s') + ]); + + $params['appid'] = $this->appid; $params['mch_id'] = $this->merchant; - $params['nonce_str'] = $this->genRandomString(); + $params['nonce_str'] = $nonce_str; $params['body'] = $body; $params['attach'] = $attach; $params['out_trade_no'] = $order_no; @@ -327,7 +338,7 @@ class Pay extends Base $params['total_fee'] = round($price * 100, 2); $params['spbill_create_ip'] = $this->get_client_ip(); $params['trade_type'] = 'JSAPI'; - $params['openid'] = $openidx ; + $params['openid'] = $openidx; $params['sign'] = $this->MakeSign($params); $xml = $this->data_to_xml($params); $url = "https://api.mch.weixin.qq.com/pay/unifiedorder"; @@ -347,6 +358,97 @@ class Pay extends Base } } + public function post_order($openid, $access_token, $order_num, $title = '订单发货') + { + $msg = "本单购买商品已发放至[小程序盒柜]"; + if (strpos($order_num, 'FH_') === 0) { + $msg = "本单购买的商品正在打包,请联系客服获取物流信息"; + } + // 根据订单号设置正确的商户配置 + $this->setMerchantByOrderNum($order_num); + $date = new \DateTime(); + // $this->appid + //订单发货时间 + $formattedDate = $date->format('Y-m-d\TH:i:s'); + $request_url = "https://api.weixin.qq.com/wxa/sec/order/upload_shipping_info?access_token=" . $access_token; + $param = '{ + "order_key": { + "order_number_type": 1, + "mchid":"' . $this->merchant . '", + "out_trade_no":"' . $order_num . '" + }, + "logistics_type": 4, + "delivery_mode": 1, + "shipping_list": [ + { + "item_desc": "'.$msg.'" + } + ], + "upload_time": "' . $formattedDate . '+08:00", + "payer": { + "openid":"' . $openid . '" + } + }'; + // 记录请求参数日志 + writelog('post_order_log', json_encode([ + 'method' => 'post_order', + 'order_num' => $order_num, + 'openid' => $openid, + 'merchant' => $this->merchant, + 'request_url' => $request_url, + 'param' => $param, + 'time' => date('Y-m-d H:i:s') + ])); + + $res = curlPost($request_url, $param); + $res = json_decode($res, true); + + // 记录响应结果日志 + writelog('post_order_log', json_encode([ + 'method' => 'post_order_response', + 'order_num' => $order_num, + 'response' => $res, + 'time' => date('Y-m-d H:i:s') + ])); + + if ($res['errcode'] == 0 && $res['errmsg'] == 'ok') { + return 1; + } else { + // 发货失败,将订单信息存入Redis,等待定时任务重试 + $redis = (new \app\common\server\RedisHelper())->getRedis(); + $key = 'post_order:' . $order_num; + + // 存储发货失败的订单信息 + $orderData = [ + 'openid' => $openid, + 'appid' => $this->appid, + // 不存储access_token,因为可能过期 + 'order_num' => $order_num, + 'title' => $title, + 'merchant' => $this->merchant, + 'error_code' => $res['errcode'] ?? 'unknown', + 'error_msg' => $res['errmsg'] ?? 'unknown', + 'retry_count' => 0, + 'last_retry_time' => time(), + 'create_time' => time() + ]; + + // 存入Redis,设置过期时间为3天 + $redis->set($key, json_encode($orderData)); + $redis->expire($key, 3 * 24 * 3600); + + // 记录存入Redis的日志 + writelog('post_order_log', json_encode([ + 'method' => 'post_order_redis_save', + 'order_num' => $order_num, + 'redis_key' => $key, + 'data' => $orderData, + 'time' => date('Y-m-d H:i:s') + ])); + + return 2; + } + } /** * 生成签名 diff --git a/app/api/middleware.php b/app/api/middleware.php index e6c794b..66196b3 100755 --- a/app/api/middleware.php +++ b/app/api/middleware.php @@ -11,7 +11,7 @@ return [ \app\api\middleware\OptionsRequestMiddleware::class, // GET请求签名验证中间件 - // \app\api\middleware\SignatureVerifyMiddleware::class, + \app\api\middleware\SignatureVerifyMiddleware::class, // 注意:原来的Allow中间件已被拆分为上面三个专门的中间件,不再需要 // \app\api\middleware\Allow::class, ]; diff --git a/app/api/route/app.php b/app/api/route/app.php index 2afe856..c685419 100755 --- a/app/api/route/app.php +++ b/app/api/route/app.php @@ -156,6 +156,11 @@ Route::any('seckill_order_detail', 'Seckill/seckill_order_detail'); Route::any('seckill_order_confirm', 'Seckill/seckill_order_confirm'); Route::any('seckill_order_logistics', 'Seckill/seckill_order_logistics'); +// 添加新的支付通知动态路由(所有参数必选) +Route::any('notify//////', 'Notify/order_notify_new'); +//https://testapi.zfunbox.cn/notify/wxpay/order_wxs/21623/MH_DEAMH202504245256999793456/1745435988/6154CF0189BBDF73C7AD0E5246B5397B +// 添加测试路由 +Route::any('test_notify_url', 'Notify/test_notify_url'); //generate_urllink Route::any('generate_urllink', 'Index/generate_urllink'); diff --git a/app/command/PostOrderRetry.php b/app/command/PostOrderRetry.php new file mode 100644 index 0000000..f91bd20 --- /dev/null +++ b/app/command/PostOrderRetry.php @@ -0,0 +1,150 @@ +setName('PostOrderRetry') + ->setDescription('重试微信发货失败的订单'); + } + + protected function execute(Input $input, Output $output) + { + $output->writeln('开始处理发货失败的订单...'); + + // 获取Redis实例 + $redis = (new RedisHelper())->getRedis(); + + // 获取所有发货失败的订单键 + $failedOrderKeys = $redis->keys('post_order:*'); + + if (empty($failedOrderKeys)) { + $output->writeln('没有发现发货失败的订单,任务结束'); + return; + } + + $output->writeln('发现' . count($failedOrderKeys) . '个发货失败的订单'); + + // 重试限制 + $maxRetryCount = 10; // 最大重试次数 + $retryInterval = 10; // 重试间隔(秒) + $nowTime = time(); + + // 处理每个失败的订单 + foreach ($failedOrderKeys as $key) { + // 获取订单数据 + $orderDataJson = $redis->get($key); + if (empty($orderDataJson)) { + continue; + } + + $orderData = json_decode($orderDataJson, true); + if (!is_array($orderData)) { + // 数据格式错误,删除此键 + $redis->del($key); + continue; + } + + // 检查重试次数和时间间隔 + if ($orderData['retry_count'] >= $maxRetryCount) { + // 超过最大重试次数,记录日志并删除 + writelog('post_order_retry_log', json_encode([ + 'method' => 'post_order_retry_max_reached', + 'order_num' => $orderData['order_num'], + 'retry_count' => $orderData['retry_count'], + 'time' => date('Y-m-d H:i:s') + ])); + + $redis->del($key); + $output->writeln("订单 {$orderData['order_num']} 超过最大重试次数({$maxRetryCount}),已移除"); + continue; + } + + // 检查是否达到重试间隔 + if (($nowTime - $orderData['last_retry_time']) < $retryInterval) { + $output->writeln("订单 {$orderData['order_num']} 未达到重试间隔,跳过"); + continue; + } + + // 尝试重新发货 + try { + $access_token = ""; + $appid = $orderData['appid']; + //获取对应的小程序 + $app = \app\common\helper\MiniprogramHelper::getMiniprogramConfigByAppid($appid); + $wx_secret = $app['app_secret']; + // 获取access_token - 优先使用微信服务器类获取 + $wxServer = new \app\common\server\Wx($this->app); + $access_token = $wxServer->get_access_appid_token($appid, $wx_secret); + if (empty($access_token)) { + throw new \Exception("无法获取有效的access_token"); + } + // 调用发货接口 + $payController = new \app\api\controller\Pay(); + + $retryResult = $payController->post_order( + $orderData['openid'], + $access_token, + $orderData['order_num'], + $orderData['title'] ?? '订单发货' + ); + + // 记录重试日志 + writelog('post_order_retry_log', json_encode([ + 'method' => 'post_order_retry_attempt', + 'order_num' => $orderData['order_num'], + 'retry_count' => $orderData['retry_count'] + 1, + 'result' => $retryResult, + 'time' => date('Y-m-d H:i:s') + ])); + + // 处理重试结果 + if ($retryResult == 1) { + // 发货成功,删除Redis中的记录 + $redis->del($key); + $output->writeln("订单 {$orderData['order_num']} 重试发货成功,已从重试队列移除"); + } else { + // 发货失败,更新重试信息 + $orderData['retry_count'] += 1; + $orderData['last_retry_time'] = $nowTime; + $redis->set($key, json_encode($orderData)); + + $output->writeln("订单 {$orderData['order_num']} 第 {$orderData['retry_count']} 次重试发货失败,将在下次重试"); + } + + } catch (\Exception $e) { + // 记录异常日志 + writelog('post_order_retry_log', json_encode([ + 'method' => 'post_order_retry_exception', + 'order_num' => $orderData['order_num'], + 'error' => $e->getMessage(), + 'time' => date('Y-m-d H:i:s') + ])); + + // 更新重试信息 + $orderData['retry_count'] += 1; + $orderData['last_retry_time'] = $nowTime; + $orderData['last_error'] = $e->getMessage(); + $redis->set($key, json_encode($orderData)); + + $output->writeln("订单 {$orderData['order_num']} 重试时发生异常:" . $e->getMessage()); + } + } + + $output->writeln('发货失败订单处理完成!'); + } +} \ No newline at end of file diff --git a/app/common.php b/app/common.php index 46801c3..978b6e9 100755 --- a/app/common.php +++ b/app/common.php @@ -153,21 +153,21 @@ if (!function_exists('getConfig')) { // // 生成缓存键 // $redis = (new \app\common\server\RedisHelper())->getRedis(); // $cache_key = "config:{$type}"; - + // // 尝试从缓存获取数据 // $cached_data = $redis->get($cache_key); // if ($cached_data) { // return json_decode($cached_data, true); // } - + $content = Db::name('config')->where(['key' => $type])->value('value'); $config = json_decode($content, true); - + // 将数据存入缓存,设置10分钟过期时间 // if ($config) { // $redis->set($cache_key, json_encode($config), 600); // } - + return $config; } } @@ -287,7 +287,7 @@ if (!function_exists('create_order_no')) { function create_order_no($pre = 'NO_', $tale = 'order', $field = 'order_num') { // 检查是否包含多商户前缀标记,处理商户随机选择并获取前缀 - if (strpos($pre, 'MH_') !== false) { + if (strpos($pre, 'MH_') !== false || strpos($pre, 'FH_') !== false) { // 引入微信支付帮助类 $wxpayConfig = \app\common\helper\WxPayHelper::getWxPayConfig(); @@ -295,7 +295,7 @@ if (!function_exists('create_order_no')) { if (!empty($wxpayConfig['merchant']) && !empty($wxpayConfig['merchant']['order_prefix'])) { $merchant_prefix = $wxpayConfig['merchant']['order_prefix']; // 在MH_后面添加商户前缀,而不是替换MH_ - $pre = 'MH_' . $merchant_prefix; + $pre = $pre . $merchant_prefix; } // 获取当前域名的微信小程序配置 @@ -701,9 +701,30 @@ if (!function_exists('time_jian')) { } } + +if (!function_exists('order_callback')) { + /** + * 订单回调方法,设置通用的订单方法 + * @param mixed $order_no + * @param mixed $notifyUrl + * @param mixed $callback_nonce_str + * @return void + */ + function order_callback($order_no, $notifyUrl, $callback_nonce_str) + { + // 将通知URL和随机字符串保存到订单表中 + Db::name('order')->where('order_num', $order_no)->update([ + 'nonce_str' => $callback_nonce_str, + 'order_notify' => $notifyUrl + ]); + } +} + + // 订单支付日志 function writelog($filename, $content) { + try { $date = date('Y-m-d'); $file = "./pay_log/" . $date; if (!is_dir($file)) { @@ -714,6 +735,9 @@ function writelog($filename, $content) $open = fopen($file, "a"); fwrite($open, $content); fclose($open); + } catch (\Throwable $th) { + + } } @@ -814,4 +838,91 @@ if (!function_exists('removeTrailingZeros')) { } return $numStr; } +} + +/** + * 生成支付通知URL + * @param string $payment_type 支付方式(wxpay, alipay等) + * @param string $order_type 赏品类型(order_yfs, order_lts等) + * @param int $user_id 用户ID + * @param string $order_num 订单号 + * @param string $nonce_str 随机字符串,用于签名 + * @return string 完整的回调URL + */ +function generatePayNotifyUrl($payment_type = null, $order_type = null, $user_id = null, $order_num = null, $nonce_str = null) +{ + // 验证所有必须参数 + if (empty($payment_type) || empty($order_type) || empty($user_id) || empty($order_num)) { + throw new \Exception("支付通知URL的所有参数都是必选的"); + } + + // 如果没有传入随机字符串,则生成一个 + if (empty($nonce_str)) { + $nonce_str = getRandStr(16, false); + } + + $timestamp = time(); + $data = [ + 'payment_type' => $payment_type, + 'order_type' => $order_type, + 'user_id' => $user_id, + 'order_num' => $order_num, + 'timestamp' => $timestamp, + 'nonce_str' => $nonce_str + ]; + + // 生成签名 + $sign = generatePayNotifySign($data); + + // 返回完整URL,所有参数都是必选的 + return request()->domain() . "/api/notify/{$payment_type}/{$order_type}/{$user_id}/{$order_num}/{$timestamp}/{$sign}"; +} + +/** + * 生成支付通知URL的签名 + * @param array $data 签名数据 + * @return string 签名字符串 + */ +function generatePayNotifySign($data) +{ + // 移除空值 + foreach ($data as $key => $value) { + if (empty($value)) { + unset($data[$key]); + } + } + + // 按键名排序 + ksort($data); + + // 拼接字符串 + $stringA = ''; + foreach ($data as $key => $value) { + $stringA .= "{$key}={$value}&"; + } + + // 获取密钥 + $wxpay_config = getConfig('wxpay'); + $key = $wxpay_config['key'] ?? 'l044imysi1vsmobyrkkfnaniu5bhiupd'; // 默认密钥 + + // 加上密钥 + $stringA .= "key=" . $key; + + // MD5加密并转大写 + return strtoupper(md5($stringA)); +} + +/** + * 验证支付通知URL的签名 + * @param array $data 待验证的数据 + * @param string $sign 传入的签名 + * @return bool 验证是否通过 + */ +function verifyPayNotifySign($data, $sign) +{ + // 生成签名 + $newSign = generatePayNotifySign($data); + + // 比较签名是否一致 + return $sign === $newSign; } \ No newline at end of file diff --git a/app/common/helper/ConfigHelper.php b/app/common/helper/ConfigHelper.php index 329c8e6..a8ec88f 100755 --- a/app/common/helper/ConfigHelper.php +++ b/app/common/helper/ConfigHelper.php @@ -294,19 +294,19 @@ class ConfigHelper return self::$systemTest; } - // 实例化Redis助手 - $redis = new RedisHelper(); + // // 实例化Redis助手 + // $redis = new RedisHelper(); - // 设置Redis键名 - $redisKey = 'config:system_test'; + // // 设置Redis键名 + // $redisKey = 'config:system_test'; - // 尝试从Redis获取 - $cachedValue = $redis->get($redisKey); - if ($cachedValue !== false) { - // 缓存结果到静态属性 - self::$systemTest = json_decode($cachedValue, true); - return self::$systemTest; - } + // // 尝试从Redis获取 + // $cachedValue = $redis->get($redisKey); + // if ($cachedValue !== false) { + // // 缓存结果到静态属性 + // self::$systemTest = json_decode($cachedValue, true); + // return self::$systemTest; + // } // Redis中不存在,从数据库获取 $config = Db::name('config') @@ -317,7 +317,7 @@ class ConfigHelper $configArray = json_decode($config, true) ?: []; // 存入Redis,过期时间为1小时(3600秒) - $redis->set($redisKey, json_encode($configArray), 3600); + // $redis->set($redisKey, json_encode($configArray), 3600); // 缓存结果到静态属性 self::$systemTest = $configArray; @@ -342,7 +342,7 @@ class ConfigHelper return "0"; } if ($key == "disable_wechat_pay") { - return "1"; + return "0"; } return $system_test['enable_test']; diff --git a/app/common/helper/MiniprogramHelper.php b/app/common/helper/MiniprogramHelper.php index 8b7a237..e4112ec 100755 --- a/app/common/helper/MiniprogramHelper.php +++ b/app/common/helper/MiniprogramHelper.php @@ -117,6 +117,33 @@ class MiniprogramHelper return self::getCompatibleConfig(); } + /** + * 根据appid获取小程序配置 + * + * @param string $appid 小程序appid + * @return array 小程序配置 + */ + public static function getMiniprogramConfigByAppid($appid) + { + // 加载所有小程序配置 + self::loadMiniprogramConfigs(); + + // 如果没有配置,返回空数组 + if (empty(self::$cache) || empty(self::$cache['miniprograms'])) { + return self::getCompatibleConfig(); + } + + // 遍历所有小程序配置,匹配appid + foreach (self::$cache['miniprograms'] as $miniprogram) { + if (isset($miniprogram['appid']) && $miniprogram['appid'] === $appid) { + return $miniprogram; + } + } + + // 如果没有匹配的appid,返回默认小程序配置 + return self::getDefaultMiniprogramConfig(); + } + /** * 获取与商户ID关联的小程序配置 * @@ -215,4 +242,6 @@ class MiniprogramHelper { self::$cache = null; } + + } \ No newline at end of file diff --git a/app/common/helper/WxPayHelper.php b/app/common/helper/WxPayHelper.php index 9764a46..8941108 100755 --- a/app/common/helper/WxPayHelper.php +++ b/app/common/helper/WxPayHelper.php @@ -65,7 +65,7 @@ class WxPayHelper // 过滤出关联的商户 $associatedMerchants = []; foreach ($weixinpay_setting['merchants'] as $m) { - if (in_array($m['id'] ?? '', $miniprogram['merchants'])) { + if (in_array($m['mch_id'] ?? '', $miniprogram['merchants'])) { $associatedMerchants[] = $m; } } @@ -177,7 +177,7 @@ class WxPayHelper */ public static function extractOrderPrefix($order_no) { - if (strpos($order_no, 'MH_') === 0) { + if (strpos($order_no, 'MH_') === 0||strpos($order_no, 'FH_') === 0) { // 尝试提取MH_后的字符作为商户前缀 // 商户前缀长度为3,如果有小程序前缀则总长度为5(3+2) $totalPrefix = substr($order_no, 3, 5); diff --git a/app/common/model/OrderNotify.php b/app/common/model/OrderNotify.php new file mode 100644 index 0000000..c181c1d --- /dev/null +++ b/app/common/model/OrderNotify.php @@ -0,0 +1,97 @@ +field($field) + ->order($order) + ->paginate(['list_rows' => $pageSize, 'query' => request()->param()]); + $page = $list->render(); + $data['list'] = $list->toArray()['data']; + $data['count'] = $list->total(); + $data['last_page'] = $list->toArray()['last_page']; + $data['page'] = $page; + return $data; + } + + /** + * 获取列表 不分页 + */ + public static function getAllList($where = [], $field = '*', $order = '', $limit = '0') + { + $data = self::where($where) + ->field($field) + ->order($order) + ->limit($limit) + ->select(); + return $data; + } + + /** + * 获取单条数据 + */ + public static function getInfo($where = [], $field = '*') + { + $data = self::where($where) + ->field($field) + ->find(); + return $data; + } + + /** + * 根据订单号获取回调信息 + */ + public static function getByOrderNo($orderNo, $field = '*') + { + return self::where('order_no', $orderNo) + ->field($field) + ->find(); + } + + /** + * 更新回调状态 + */ + public static function updateStatus($id, $status, $remark = null) + { + $data = [ + 'status' => $status, + 'last_notify_time' => date('Y-m-d H:i:s') + ]; + + if (!is_null($remark)) { + $data['remark'] = $remark; + } + + return self::update($data, ['id' => $id]); + } + + /** + * 增加重试次数 + */ + public static function increaseRetryCount($id) + { + return self::where('id', $id) + ->inc('retry_count') + ->update([ + 'last_notify_time' => date('Y-m-d H:i:s') + ]); + } +} \ No newline at end of file diff --git a/app/common/server/Wx.php b/app/common/server/Wx.php index 9c21763..0dce470 100755 --- a/app/common/server/Wx.php +++ b/app/common/server/Wx.php @@ -16,30 +16,13 @@ class Wx extends MyController { // 使用新的MiniprogramHelper获取当前域名对应的小程序配置 $miniprogram = \app\common\helper\MiniprogramHelper::getMiniprogramConfig(); - + // 设置appid和secret if (!empty($miniprogram) && !empty($miniprogram['appid'])) { self::$wx_appid = $miniprogram['appid']; self::$wx_secret = $miniprogram['app_secret']; - } else { - // 旧的兼容代码,逐步过渡,最终应该删除 - $wechat_setting = getConfig("wechat_setting"); - - // 获取微信支付配置 - $wxpayConfig = \app\common\helper\WxPayHelper::getWxPayConfig(); - - // 如果系统设置中存在微信配置,则优先使用 - if (!empty($wechat_setting) && !empty($wechat_setting['appid']) && !empty($wechat_setting['appSecret'])) { - self::$wx_appid = $wechat_setting['appid']; - self::$wx_secret = $wechat_setting['appSecret']; - } else { - self::$wx_appid = $wxpayConfig['appid']; - // 从旧配置或商户配置中获取appSecret - $weixinpay = getConfig("weixinpay"); - self::$wx_secret = !empty($weixinpay['appSecret']) ? $weixinpay['appSecret'] : ''; - } } - + // 获取对应的商户信息 // 如果小程序配置了关联商户,则使用关联商户中的第一个 if (!empty($miniprogram) && !empty($miniprogram['merchants']) && is_array($miniprogram['merchants']) && count($miniprogram['merchants']) > 0) { @@ -55,7 +38,7 @@ class Wx extends MyController } } } - + // 如果没有找到关联商户或没有配置,则使用默认商户配置 $wxpayConfig = \app\common\helper\WxPayHelper::getWxPayConfig(); $merchant = $wxpayConfig['merchant']; @@ -212,9 +195,19 @@ class Wx extends MyController * 获取access_token */ public function get_access_token() + { + return $this->get_access_appid_token(self::$wx_appid, self::$wx_secret); + + } + + + /** + * 获取access_token + */ + public function get_access_appid_token($appid, $wx_secret) { $redis = (new \app\common\server\RedisHelper())->getRedis(); - $redis_key = 'wx_access_token:' . self::$wx_appid; + $redis_key = 'wx_access_token:' . $appid; $access_token_info = $redis->get($redis_key); if ($access_token_info) { @@ -224,7 +217,7 @@ class Wx extends MyController } } - $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" . self::$wx_appid . "&secret=" . self::$wx_secret; + $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" . $appid . "&secret=" . $wx_secret; $res_access_token = $this->get_curl_data($url); if (isset($res_access_token['errcode'])) { return $this->renderError('获取失败'); @@ -244,7 +237,6 @@ class Wx extends MyController return $access_token; } - /** * @param $url 请求链接 */ @@ -318,41 +310,7 @@ class Wx extends MyController $return_content = curl_exec($ch); return $return_content; } - - public function post_order($openid, $access_token, $order_num, $title = '订单发货') - { - $date = new \DateTime(); - //2023-08-07T17:16:31 - //2025-03-04T21:06:59 - // 格式化时间为 yyyy-MM-dd HH:mm:ss - $formattedDate = $date->format('Y-m-d\TH:i:s'); - $request_url = "https://api.weixin.qq.com/wxa/sec/order/upload_shipping_info?access_token=" . $access_token; - $param = '{ - "order_key": { - "order_number_type": 1, - "mchid":"' . self::$mch . '", - "out_trade_no":"' . $order_num . '" - }, - "logistics_type": 3, - "delivery_mode": 1, - "shipping_list": [ - { - "item_desc": "本单购买商品已发放至[小程序盒柜]" - } - ], - "upload_time": "' . $formattedDate . '+08:00", - "payer": { - "openid":"' . $openid . '" - } - }'; - $res = curlPost($request_url, $param); - $res = json_decode($res, true); - if ($res['errcode'] == 0 && $res['errmsg'] == 'ok') { - return 1; - } else { - return 2; - } - } + public function post_order_one($openid, $access_token, $order_num, $title = '订单发货') diff --git a/app/common/service/PaymentCalculator.php b/app/common/service/PaymentCalculator.php index 32d7c71..02985b3 100644 --- a/app/common/service/PaymentCalculator.php +++ b/app/common/service/PaymentCalculator.php @@ -155,7 +155,7 @@ class PaymentCalculator } else { // 支付模式下余额不足无法抵扣 $use_money = 0; - return ['status' => 0, 'msg' => '金额不足']; + return ['status' => 0, 'msg' => '钻石不足']; } } } @@ -181,7 +181,7 @@ class PaymentCalculator } else { // 支付模式下货币不足无法抵扣 $use_integral = 0; - return ['status' => 0, 'msg' => '金额不足']; + return ['status' => 0, 'msg' => 'UU币不足']; } } } @@ -207,7 +207,7 @@ class PaymentCalculator } else { // 支付模式下货币2不足无法抵扣 $use_money2 = 0; - return ['status' => 0, 'msg' => '金额不足']; + return ['status' => 0, 'msg' => '达达卷不足']; } } } diff --git a/config/console.php b/config/console.php index de88344..5774744 100755 --- a/config/console.php +++ b/config/console.php @@ -9,5 +9,6 @@ return [ 'CreateOffshelfLogTable' => 'app\command\CreateOffshelfLogTable',#创建盒子自动下架日志表 'FlwOpen' => 'app\command\FlwOpen',#福利屋开奖 'UpdateGoodsHeat' => 'app\command\UpdateGoodsHeat',#更新盒子热度值到Redis + 'PostOrderRetry'=>'app\command\PostOrderRetry' ], ];