diff --git a/app/Http/Controllers/Api/WxpayController.php b/app/Http/Controllers/Api/WxpayController.php index 9fbe692..7fe08b4 100644 --- a/app/Http/Controllers/Api/WxpayController.php +++ b/app/Http/Controllers/Api/WxpayController.php @@ -17,8 +17,6 @@ class WxpayController //微信支付 支付结果通知网址 public function notify() { - // {"sign": "3F99E6044C503B0E0131F95A1410B630", "appid": "wxb35ef055a4dd8ad4", "mch_id": "1606181693", "openid": "oBYj55W0gLv5MYUnsYUuJfzYzmsg", "cash_fee": "1", "fee_type": "CNY", "time_end": "20210623222330", "bank_type": "OTHERS", "nonce_str": "60d343d820e94", "total_fee": "1", "trade_type": "JSAPI", "result_code": "SUCCESS", "return_code": "SUCCESS", "is_subscribe": "N", "out_trade_no": "2842908479209865216", "transaction_id": "4200001210202106237487333085"} - $agent_id = request()->route('agent_id'); $agent = Agent::find($agent_id); @@ -31,16 +29,28 @@ class WxpayController $app = Factory::payment($config); try { $response = $app->handlePaidNotify(function ($message, $fail) { + $this->log($message); // 请求成功 if ($message['return_code'] === 'SUCCESS') { - //订单号带有pay_type后缀,主要是为了区分定金支付和尾款支付,前24位才是真正的订单号 + //订单号带有status后缀,主要是为了区分定金支付和尾款支付,前24位才是真正的订单号 $order_no = substr($message['out_trade_no'], 0, 24); $order = Order::query() ->where(['order_no' => $order_no]) ->first(); //已经处理过的订单直接返回true - if ($order && in_array($order->status, [OrderStatus::PAID, OrderStatus::PAID_RETAINAGE])) { + if ($order && in_array($order->status, [OrderStatus::PAID, OrderStatus::PAID_RETAINAGE, OrderStatus::SUCCESS])) { + return true; + } + + //判断该微信支付订单号有没有处理过 + $exist_log = UserMoneyLog::where([ + 'user_id' => $order->user_id, + 'order_id' => $order->id, + 'type' => 1, + 'transaction_id' => $message['transaction_id'], + ])->first(); + if ($exist_log) { return true; } @@ -50,18 +60,21 @@ class WxpayController try { $status = $order->status; $pay_type = $order->pay_type; + $money = $message['total_fee'] / 100; //定金支付和首付款支付 if (in_array($pay_type, [1, 2])) { - if ($pay_type == OrderStatus::UNPAID) { + if ($status == OrderStatus::UNPAID) { $order->status = OrderStatus::PAY_EARNEST; } else if ($status == OrderStatus::PAY_EARNEST) { - $order->status = OrderStatus::PAID_RETAINAGE; + //只有支付的金额大于等于订单总价,才将订单状态修改为PAID_RETAINAGE + if ($order->paid_money + $money >= $order->price) { + $order->status = OrderStatus::PAID_RETAINAGE; + } } } else if ($pay_type == 0) { $order->status = OrderStatus::PAID; } - $money = $message['total_fee'] / 100; $order->paid_at = time(); $order->paid_money = DB::raw('`paid_money` + ' . $money); $order->save(); @@ -81,7 +94,8 @@ class WxpayController 'money' => -$money, 'order_id' => $order->id, 'type' => 1, - 'desc' => '购买产品:' . $order->title, + 'desc' => DB::raw("LEFT('购买产品:{$order->title}', 250)"), + 'transaction_id' => $message['transaction_id'], //微信支付订单号 'created_at' => time(), //模型没有updated_at,无法自动写入时间 ]); @@ -98,14 +112,10 @@ class WxpayController } // 希望微信重试 - $fail('Order not exists.'); + $fail('Unknown error 2'); }); } catch (InvalidSignException | Exception $e) { - $time = time(); - $filename = storage_path('logs/wxpay_notify_') . date('Y-m-d-H', $time) . '.log'; - $data = '[' . date('Y-m-d H:i:s', $time) . ']' . PHP_EOL; - $data .= '[message]:' . $e->getMessage() . $e->getFile() . $e->getLine() . PHP_EOL . PHP_EOL; - file_put_contents($filename, $data, FILE_APPEND); + $this->log($e->getMessage() . $e->getFile() . $e->getLine()); return 'error'; } @@ -115,9 +125,6 @@ class WxpayController //退款通知 public function refund() { - - // {"sign": "3F99E6044C503B0E0131F95A1410B630", "appid": "wxb35ef055a4dd8ad4", "mch_id": "1606181693", "openid": "oBYj55W0gLv5MYUnsYUuJfzYzmsg", "cash_fee": "1", "fee_type": "CNY", "time_end": "20210623222330", "bank_type": "OTHERS", "nonce_str": "60d343d820e94", "total_fee": "1", "trade_type": "JSAPI", "result_code": "SUCCESS", "return_code": "SUCCESS", "is_subscribe": "N", "out_trade_no": "2842908479209865216", "transaction_id": "4200001210202106237487333085"} - $agent_id = request()->route('agent_id'); $agent = Agent::find($agent_id); @@ -131,43 +138,81 @@ class WxpayController try { $response = $app->handleRefundedNotify(function ($message, $reqInfo, $fail) { // 记录一下本地调试 - file_put_contents(storage_path('/wxpay/refund' . date('Y-m-d-H') . '.log'), date('Y-m-d H:i:s') . PHP_EOL . json_encode($message) . PHP_EOL . json_encode($reqInfo), FILE_APPEND); + $this->log(['message' => $message, 'reqInfo' => $reqInfo], 'refund'); + /*$reqInfo = [ + 'out_refund_no' => '131811191610442717309', + 'out_trade_no' => '20210722164514946742983', + 'refund_account' => 'REFUND_SOURCE_RECHARGE_FUNDS', + 'refund_fee' => 8800, + 'refund_id' => '50000408942018111907145868882', + 'refund_recv_accout' => '支付用户零钱', + 'refund_request_source' => 'API', + 'refund_status' => 'SUCCESS', + 'settlement_refund_fee' => 8800, + 'settlement_total_fee' => 8800, + 'success_time' => date('Y-m-d H:i:s'), + 'total_fee' => 8800, + 'transaction_id' => '1004400740201409030005092168', + ];*/ // 请求成功 if ($message['return_code'] === 'SUCCESS') { - //订单号带有pay_type后缀,主要是为了区分定金支付和尾款支付,前24位才是真正的订单号 - $order_no = substr($message['out_trade_no'], 0, 24); + //订单号带有status后缀,主要是为了区分定金支付和尾款支付,前24位才是真正的订单号 + $order_no = substr($reqInfo['out_trade_no'], 0, 24); $order = Order::query() - ->where(['order_sn' => $order_no]) + ->where(['order_no' => $order_no]) ->first(); - // 退款成功 - if ($reqInfo['refund_status'] === 'SUCCESS') { - DB::beginTransaction(); - try { + //如果已经处理过则不再处理 + if ($order->status == OrderStatus::REFUNDED) { + return true; + } - $apply = RefundApply::query() - ->where('refund_sn', $reqInfo['out_refund_no']) - ->first(); + //如果已经存在相关的退款记录,也不再处理 + $exist_log = UserMoneyLog::where([ + 'user_id' => $order->user_id, + 'order_id' => $order->id, + 'type' => 2, + 'transaction_id' => $reqInfo['transaction_id'], + ])->first(); + if ($exist_log) { + return true; + } - if (!$apply) { - $fail('退款单没找到'); - } + $log = UserMoneyLog::query() + ->where([ + 'user_id' => $order->user_id, + 'order_id' => $order->id, + 'type' => 1, + 'transaction_id' => $reqInfo['transaction_id'], + ])->first(); - $apply->state = 2; - $apply->save(); + if (!$log) { + $fail('找不到交易信息'); + } - $order->state = UserGoodsOrder::REFUNDED; + // 退款成功 + if ($reqInfo['refund_status'] === 'SUCCESS') { + DB::beginTransaction(); + try { + //更新订单状态为退款成功 + $order->status = OrderStatus::REFUNDED; $order->save(); + //记录日志 + UserMoneyLog::create([ + 'user_id' => $order->user_id, + 'agent_id' => $order->agent_id, + 'money' => $reqInfo['settlement_refund_fee'] / 100, + 'order_id' => $order->id, + 'type' => 2, + 'desc' => DB::raw("LEFT('退款:{$order->title}', 250)"), + 'transaction_id' => $reqInfo['transaction_id'], + 'created_at' => time(), + ]); // 退库存 - GoodsSpecs::query() - ->where([ - 'id' => $order->goods_specs_id, - 'goods_id' => $order->goods_id, - ]) - ->update([ - 'stock' => DB::raw('stock+' . $order->number . ''), - ]); + Product::query() + ->where('id', $order->product_id) + ->increment('stock', $order->num); DB::commit(); return true; @@ -178,7 +223,7 @@ class WxpayController } } - $fail('Unknown error'); + $fail('Unknown error 2'); }); } catch (Exception $e) { return 'error'; @@ -186,4 +231,17 @@ class WxpayController return $response; } + + //保存消息,用于调试 + private function log($write_data, $type = 'notify') + { + $dir = storage_path('wxpay'); + if (!is_dir($dir)) { + mkdir($dir); + } + $filename = $dir . '/' . $type . '_' . date('Y-m-d-H') . '.log'; + $data = '[' . date('Y-m-d H:i:s') . ']' . PHP_EOL; + $data .= '[message]: ' . (is_array($write_data) ? json_encode($write_data) : $write_data) . PHP_EOL . PHP_EOL; + file_put_contents($filename, $data, FILE_APPEND); + } }