app = new Application(config('wechat.pay.default')); } /** * [微信]获取支付配置 * * @param int $orderId 订单编号 * @return array * @throws Exception */ public function getPaymentConfig($orderId) { // 获取当前用户 $userId = Auth::id(); $user = MemberUser::where('state', UserStatus::OPEN->value)->findOrFail($userId); // 查询订单 $order = Order::where('id', $orderId) ->where('user_id', $userId) ->where('state', OrderStatus::CREATED->value) ->firstOrFail(); // 生成微信支付配置 $outTradeNo = $this->generateOutTradeNo(); $config = $this->generateWxPayConfig($order, $outTradeNo); // 更新订单号(使用 order_no 而不是 out_trade_no) $order->order_no = $outTradeNo; $order->save(); // 发送抢单通知 $this->sendGrabOrderNotification($order); return $config; } /** * 生成外部交易号 */ private function generateOutTradeNo() { return date('YmdHis') . mt_rand(1000, 9999); } /** * 生成微信支付配置 */ private function generateWxPayConfig($order, $outTradeNo) { $appId = config('wechat.official_account.default.app_id'); // 创建支付订单 $result = $this->app->getClient()->postJson('v3/pay/transactions/jsapi', [ 'appid' => $appId, 'mchid' => config('wechat.pay.default.mch_id'), 'out_trade_no' => $outTradeNo, 'description' => "订单支付#{$order->id}", 'notify_url' => config('wechat.pay.default.notify_url'), 'amount' => [ 'total' => intval($order->pay_amount * 100), // 单位:分 'currency' => 'CNY' ], 'payer' => [ 'openid' => $order->member->socialAccounts() ->where('platform', 'WECHAT') ->value('social_id') ], ]); // 获取预支付交易会话标识 $prepayId = $result['prepay_id']; // 使用 EasyWeChat 的工具方法生成支付配置 $config = $this->app->getUtils()->buildBridgeConfig($prepayId, $appId, 'RSA'); // 确保 appId 存在 $config['appId'] = $appId; return $config; } /** * 发送抢单通知 */ private function sendGrabOrderNotification($order) { // TODO: 对接极光推送,发送抢单通知 } /** * [微信]处理支付回调通知 */ public function handleNotify(Request $request) { try { // 记录请求头信息 $headers = [ 'Wechatpay-Serial' => $request->header('Wechatpay-Serial'), 'Wechatpay-Signature' => $request->header('Wechatpay-Signature'), 'Wechatpay-Timestamp' => $request->header('Wechatpay-Timestamp'), 'Wechatpay-Nonce' => $request->header('Wechatpay-Nonce'), ]; Log::info('微信支付回调请求头', $headers); try { // 获取并记录原始请求内容 $content = $request->getContent(); Log::info('原始请求内容', ['content' => $content]); // 解析 JSON $message = json_decode($content, true); if (json_last_error() !== JSON_ERROR_NONE) { throw new \Exception('JSON 解析失败: ' . json_last_error_msg()); } Log::info('解析后的请求数据', ['message' => $message]); // 验证必要字段 if ( empty($message['resource']) || empty($message['resource']['ciphertext']) || empty($message['resource']['nonce']) || empty($message['resource']['associated_data']) ) { throw new \Exception('回调数据缺少必要字段'); } // 获取解密器 $aesKey = $this->app->getConfig()['secret_key'] ?? ''; if (empty($aesKey)) { throw new \Exception('未配置 API v3 密钥'); } Log::info('获取到密钥'); // 解密数据 try { $ciphertext = $message['resource']['ciphertext']; $nonce = $message['resource']['nonce']; $associatedData = $message['resource']['associated_data']; // 使用 AEAD_AES_256_GCM 算法解密 $decryptedData = \EasyWeChat\Kernel\Support\AesGcm::decrypt( $ciphertext, $aesKey, $nonce, $associatedData ); Log::info('解密成功', ['raw' => $decryptedData]); // 解析解密后的 JSON $decryptedData = json_decode($decryptedData, true); if (json_last_error() !== JSON_ERROR_NONE) { throw new \Exception('解密后的 JSON 解析失败: ' . json_last_error_msg()); } Log::info('解密后的数据', ['decrypted' => $decryptedData]); } catch (\Exception $e) { Log::error('解密失败', [ 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); throw $e; } // 处理支付结果 if ($decryptedData['trade_state'] === 'SUCCESS') { $result = $this->handlePaymentResult([ 'out_trade_no' => $decryptedData['out_trade_no'], 'transaction_id' => $decryptedData['transaction_id'], 'trade_state' => $decryptedData['trade_state'], 'amount' => [ 'total' => $decryptedData['amount']['total'] ?? 0, 'payer_total' => $decryptedData['amount']['payer_total'] ?? 0, 'currency' => $decryptedData['amount']['currency'] ?? 'CNY', ], 'success_time' => $decryptedData['success_time'], 'payer' => [ 'openid' => $decryptedData['payer']['openid'] ?? '' ] ]); Log::info('支付结果处理完成', [ 'out_trade_no' => $decryptedData['out_trade_no'], 'result' => $result ]); } else { Log::warning('支付状态不是成功', [ 'trade_state' => $decryptedData['trade_state'] ]); } // 返回成功响应 return response()->json([ 'code' => 'SUCCESS', 'message' => 'OK' ]); } catch (\Exception $e) { Log::error('回调处理过程出错', [ 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); throw $e; } } catch (\Exception $e) { Log::error('微信支付回调处理失败', [ 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString(), 'request' => [ 'headers' => $request->headers->all(), 'content' => $request->getContent() ] ]); return response()->json([ 'code' => 'FAIL', 'message' => '处理失败: ' . $e->getMessage() ]); } } /** * 处理支付结果 */ private function handlePaymentResult(array $data): bool { Log::info('开始处理支付结果', $data); // 1. 校验订单信息 $order = Order::where('order_no', $data['out_trade_no'])->first(); if (!$order) { Log::error('订单不存在', ['out_trade_no' => $data['out_trade_no']]); throw new \Exception('订单不存在'); } // 2. 校验订单金额(注意微信支付金额单位是分) $orderAmount = intval($order->pay_amount * 100); if ($orderAmount !== $data['amount']['total']) { Log::error('订单金额不匹配', [ 'order_amount' => $orderAmount, 'paid_amount' => $data['amount']['total'] ]); throw new \Exception('订单金额不匹配'); } // 3. 检查订单状态 if ($order->state !== OrderStatus::CREATED->value) { Log::warning('订单状态异常', [ 'order_no' => $order->order_no, 'current_state' => $order->state ]); return true; // 已处理过的订单直接返回成功 } // 4. 更新订单状态 if ($data['trade_state'] === 'SUCCESS') { return DB::transaction(function () use ($order, $data) { // 更新订单状态为已支付 $order->update([ 'state' => OrderStatus::PAID->value, 'payment_time' => $data['success_time'], 'transaction_id' => $data['transaction_id'] ]); // 创建支付记录 $order->records()->create([ 'object_id' => $order->user_id, 'object_type' => MemberUser::class, 'state' => OrderRecordStatus::PAID->value, 'remark' => '微信支付成功' ]); // TODO: 发送支付成功通知 // event(new OrderPaidEvent($order)); Log::info('订单支付成功', [ 'order_no' => $order->order_no, 'transaction_id' => $data['transaction_id'] ]); return true; }); } return false; } /** * 申请退款 * * @param string $transactionId 微信支付交易号 * @param float $refundAmount 退款金额 * @param float $totalAmount 原订单金额 * @param string $reason 退款原因 * @return array * @throws \Exception */ public function refund(string $transactionId, float $refundAmount, float $totalAmount, string $reason = '订单退款'): array { try { // 生成退款单号 $refundNo = 'RF' . date('YmdHis') . mt_rand(1000, 9999); // 调用微信退款接口 $response = $this->app->getClient()->postJson('v3/refund/domestic/refunds', [ 'transaction_id' => $transactionId, // 微信支付交易号 'out_refund_no' => $refundNo, // 商户退款单号 'amount' => [ 'refund' => intval($refundAmount * 100), // 退款金额,单位:分 'total' => intval($totalAmount * 100), // 原订单金额,单位:分 'currency' => 'CNY' ], 'reason' => $reason ]); // 获取响应内容 $result = json_decode($response->getContent(), true); // 记录退款日志 Log::info('微信退款请求结果', [ 'transaction_id' => $transactionId, 'refund_no' => $refundNo, 'refund_amount' => $refundAmount, 'total_amount' => $totalAmount, 'result' => $result ]); return [ 'success' => true, 'refund_no' => $refundNo, 'result' => $result ]; } catch (\Exception $e) { Log::error('微信退款请求异常', [ 'transaction_id' => $transactionId, 'refund_amount' => $refundAmount, 'error' => $e->getMessage() ]); // 如果是400错误,说明请求已经发送到微信服务器 // 此时退款可能已经成功,我们返回成功状态并记录退款号 if (str_contains($e->getMessage(), '400 Bad Request')) { return [ 'success' => true, 'refund_no' => $refundNo, 'message' => '退款请求已发送,请等待处理结果' ]; } return [ 'success' => false, 'message' => $e->getMessage() ]; } } /** * 处理微信退款回调通知 * @return array 解密后的回调数据 */ public function handleRefundNotify() { $app = $this->getApp(); $server = $app->getServer(); // 处理回调 $server->handleRefunded(function ($message) { Log::info('微信退款回调原始数据', ['message' => $message]); return true; }); // 获取解密后的回调数据 $data = $server->getRequestMessage(); try { Log::info('微信退款回调数据', ['data' => $data]); // 根据商户退款单号查找退款记录 $refundRecord = OrderRefundRecord::where('refund_no', $data['out_refund_no'])->first(); if (!$refundRecord) { Log::error('未找到对应的退款记录', ['refund_no' => $data['out_refund_no']]); return true; } // 更新退款记录状态 DB::transaction(function () use ($refundRecord, $data) { // 更新退款记录 $refundRecord->update([ 'state' => $data['refund_status'], 'transaction_id' => $data['transaction_id'], 'refund_time' => Carbon::parse($data['success_time']) ]); // 如果退款成功,更新订单状态为已退款 if ($data['refund_status'] === 'SUCCESS') { $order = $refundRecord->order; $order->update([ 'state' => OrderStatus::REFUNDED->value, 'refund_time' => Carbon::parse($data['success_time']) ]); // 创建订单记录 $order->records()->create([ 'object_id' => $order->user_id, 'object_type' => MemberUser::class, 'state' => OrderRecordStatus::REFUNDED->value, 'remark' => '订单退款成功' ]); } }); return true; } catch (\Exception $e) { Log::error('处理退款回调异常', [ 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); return true; // 即使处理失败也要返回true,避免微信重复通知 } } /** * 获取退款成功响应 */ public function getRefundSuccessResponse() { return [ 'code' => 'SUCCESS', 'message' => 'OK' ]; } }