auth->id; $orderId = $this->request->post('order_id', 0); $payMethod = $this->request->post('pay_method', 'wechat'); if (!$userId || $orderId <= 0) { $this->error('参数错误'); } // 验证支付方式 if (!in_array($payMethod, ['wechat', 'alipay'])) { $this->error('不支持的支付方式'); } // 查询订单信息 $order = Db::table('ba_order') ->where('id', $orderId) ->where('user_id', $userId) ->where('pay_status', 0) // 待支付 ->find(); if (!$order) { $this->error('订单不存在或已支付'); } // 更新订单支付方式 Db::table('ba_order') ->where('id', $orderId) ->update([ 'pay_method' => $payMethod, 'update_time' => date('Y-m-d H:i:s') ]); // 检查是否已有支付记录 $paymentRecord = Db::table('ba_payment_record') ->where('order_id', $orderId) ->where('status', 0) // 待支付 ->find(); if ($paymentRecord) { // 检查支付记录是否过期(15分钟) $createTime = strtotime($paymentRecord['create_time']); if (time() - $createTime < 900) { // 15分钟 $this->success('', [ 'order_no' => $order['order_no'], 'pay_amount' => $order['pay_amount'], 'qr_code_url' => $paymentRecord['qr_code_url'], 'qr_code_content' => $paymentRecord['qr_code_content'], 'payment_id' => $paymentRecord['id'], ]); } } // 查询订单商品信息 $orderItem = Db::table('ba_order_item') ->where('order_id', $orderId) ->find(); $subject = $orderItem['item_name'] ?? '商品购买'; $body = $orderItem['spec_name'] ?? ''; // 创建支付服务实例 $paymentService = PaymentFactory::create($payMethod); // 生成支付二维码 $qrCodeResult = $paymentService->createQrCode( $order['order_no'], $order['pay_amount'], $subject, $body ); // 创建支付记录 $payConfig = $paymentService->getPayConfig($payMethod); // 需要添加这个方法到BasePaymentService $paymentData = [ 'order_id' => $orderId, 'order_no' => $order['order_no'], 'pay_config_id' => $payConfig['id'], 'pay_method' => $payMethod, 'pay_amount' => $order['pay_amount'], 'qr_code_url' => $qrCodeResult['qr_code_url'], 'qr_code_content' => $qrCodeResult['qr_code_content'], 'status' => 0, 'create_time' => date('Y-m-d H:i:s'), 'update_time' => date('Y-m-d H:i:s'), ]; if ($paymentRecord) { // 更新现有记录 Db::table('ba_payment_record') ->where('id', $paymentRecord['id']) ->update($paymentData); $paymentId = $paymentRecord['id']; } else { // 创建新记录 $paymentId = Db::table('ba_payment_record')->insertGetId($paymentData); } $this->success('创建成功', [ 'order_no' => $order['order_no'], 'pay_amount' => $order['pay_amount'], 'qr_code_url' => $qrCodeResult['qr_code_url'], 'qr_code_content' => $qrCodeResult['qr_code_content'], 'payment_id' => $paymentId, 'expire_time' => 900, // 15分钟过期 ]); } /** * 查询支付状态 */ public function queryStatus() { try { $orderNo = $this->request->get('order_no', ''); if (empty($orderNo)) { $this->error('订单号错误'); } // 查询支付记录 $paymentRecord = Db::table('ba_payment_record') ->where('order_no', $orderNo) ->find(); if (!$paymentRecord) { $this->error('支付记录不存在'); } // 如果已支付,直接返回 if ($paymentRecord['status'] == 1) { $this->success('', [ 'pay_status' => 1, 'pay_time' => $paymentRecord['pay_time'], 'trade_no' => $paymentRecord['trade_no'], 'message' => '支付成功', ]); } // 检查是否过期 $createTime = strtotime($paymentRecord['create_time']); if (time() - $createTime > 900) { // 15分钟 // 更新为已关闭 Db::table('ba_payment_record') ->where('id', $paymentRecord['id']) ->update([ 'status' => 3, 'update_time' => date('Y-m-d H:i:s'), ]); $this->success('', [ 'pay_status' => 3, 'message' => '支付已超时', ]); } // 查询支付配置 $payConfig = Db::table('ba_pay_config') ->where('id', $paymentRecord['pay_config_id']) ->find(); if (!$payConfig) { $this->error('支付配置不存在'); } // 查询支付平台 $paymentService = PaymentFactory::create($paymentRecord['pay_method'], $payConfig['id']); $queryResult = $paymentService->queryOrder($orderNo); // 如果支付成功,处理支付成功逻辑 if ($queryResult['pay_status'] == 1) { $this->processPaymentSuccess( $orderNo, $queryResult['trade_no'], $queryResult['pay_amount'], $queryResult['pay_time'] ); } $this->success('', [ 'pay_status' => $queryResult['pay_status'], 'pay_time' => $queryResult['pay_time'] ?? null, 'trade_no' => $queryResult['trade_no'] ?? null, 'message' => $this->getStatusMessage($queryResult['pay_status']), ]); } catch (\Throwable $e) { \think\facade\Log::error('查询支付状态失败: ' . $e->getMessage()); $this->error('查询支付状态失败: ' . $e->getMessage()); } } /** * 微信支付回调 */ public function wechatNotify() { try { // 获取原始输入 $input = file_get_contents('php://input'); if (empty($input)) { throw new \Exception('回调数据为空'); } Log::info('微信支付回调数据: ' . $input); // 解析XML $data = $this->xmlToArray($input); $orderNo = $data['out_trade_no'] ?? ''; if (empty($orderNo)) { throw new \Exception('未找到订单号'); } // 使用事务和锁处理回调 Db::startTrans(); try { // 使用行锁检查支付记录 $paymentRecord = Db::table('ba_payment_record') ->where('order_no', $orderNo) ->lock(true) ->find(); if (!$paymentRecord) { throw new \Exception('支付记录不存在: ' . $orderNo); } // 如果已处理,直接返回成功响应(幂等性) if ($paymentRecord['status'] == 1) { Db::rollback(); Log::info('微信支付回调幂等性检查:订单已处理,直接返回成功', [ 'order_no' => $orderNo, 'payment_record_id' => $paymentRecord['id'] ]); $response = [ 'return_code' => 'SUCCESS', 'return_msg' => 'OK', ]; echo $this->arrayToXml($response); return; } // 查询支付配置 $payConfig = Db::table('ba_pay_config') ->where('id', $paymentRecord['pay_config_id']) ->find(); if (!$payConfig) { throw new \Exception('支付配置不存在'); } // 处理回调 $paymentService = PaymentFactory::create('wechat', $payConfig['id']); $result = $paymentService->handleNotify($input); // 处理支付成功 $this->processPaymentSuccess( $result['order_no'], $result['trade_no'], $result['pay_amount'], $result['pay_time'] ); Db::commit(); // 返回成功响应给微信 $response = [ 'return_code' => 'SUCCESS', 'return_msg' => 'OK', ]; echo $this->arrayToXml($response); } catch (\Throwable $e) { Db::rollback(); throw $e; } } catch (\Throwable $e) { Log::error('微信支付回调处理失败: ' . $e->getMessage()); $response = [ 'return_code' => 'FAIL', 'return_msg' => $e->getMessage(), ]; echo $this->arrayToXml($response); } } /** * 支付宝支付回调 */ public function alipayNotify() { try { $input = $_POST; if (empty($input)) { throw new \Exception('回调数据为空'); } // 记录回调日志 \think\facade\Log::info('支付宝支付回调数据: ' . json_encode($input)); $orderNo = $input['out_trade_no'] ?? ''; if (empty($orderNo)) { throw new \Exception('未找到订单号'); } // 幂等性检查:检查订单是否已处理 $processedOrder = Db::table('ba_payment_record') ->where('order_no', $orderNo) ->where('status', 1) // 已支付 ->find(); if ($processedOrder) { // 如果已处理,直接返回成功响应 \think\facade\Log::info('支付宝支付回调幂等性检查:订单已处理,直接返回成功', [ 'order_no' => $orderNo, 'payment_record_id' => $processedOrder['id'] ]); echo 'success'; return; } // 查询支付记录 $paymentRecord = Db::table('ba_payment_record') ->where('order_no', $orderNo) ->find(); if (!$paymentRecord) { throw new \Exception('支付记录不存在: ' . $orderNo); } // 查询支付配置 $payConfig = Db::table('ba_pay_config') ->where('id', $paymentRecord['pay_config_id']) ->find(); if (!$payConfig) { throw new \Exception('支付配置不存在'); } // 处理回调 $paymentService = PaymentFactory::create('alipay', $payConfig['id']); $result = $paymentService->handleNotify($input); // 处理支付成功 $this->processPaymentSuccess( $result['order_no'], $result['trade_no'], $result['pay_amount'], $result['pay_time'] ); echo 'success'; } catch (\Throwable $e) { \think\facade\Log::error('支付宝支付回调处理失败: ' . $e->getMessage()); echo 'fail'; } } /** * 处理支付成功 */ private function processPaymentSuccess($orderNo, $tradeNo, $payAmount, $payTime) { // 使用数据库事务和锁来确保幂等性 Db::startTrans(); try { // 1. 使用行锁检查订单是否已处理 $order = Db::table('ba_order') ->where('order_no', $orderNo) ->lock(true) // 行锁 ->find(); if (!$order) { Db::rollback(); Log::error('订单不存在: ' . $orderNo); throw new \Exception('订单不存在'); } // 2. 如果订单已支付,直接返回(幂等性) if ($order['pay_status'] == 1) { Db::rollback(); Log::info('订单已支付,跳过处理(幂等性)', ['order_no' => $orderNo]); return; } // 3. 更新支付记录 Db::table('ba_payment_record') ->where('order_no', $orderNo) ->where('status', 0) // 只更新待支付的记录 ->update([ 'status' => 1, 'trade_no' => $tradeNo, 'pay_time' => $payTime, 'notify_time' => date('Y-m-d H:i:s'), 'update_time' => date('Y-m-d H:i:s'), ]); // 4. 更新订单状态 Db::table('ba_order') ->where('order_no', $orderNo) ->where('pay_status', 0) // 只更新未支付的订单 ->update([ 'pay_status' => 1, 'pay_time' => $payTime, 'order_status' => 1, 'update_time' => date('Y-m-d H:i:s'), ]); // 5. 获取更新后的订单 $order = Db::table('ba_order') ->where('order_no', $orderNo) ->find(); // 6. 获取订单商品 $orderItems = Db::table('ba_order_item') ->where('order_id', $order['id']) ->select(); // 7. 创建用户产品服务实例 $userProductService = new \app\common\service\UserProductService(); $companyPackageService = new \app\common\service\CompanyPackageService(); foreach ($orderItems as $item) { // 8. 使用事务和锁检查是否已存在使用记录 $existingUsage = Db::table('ba_user_product_usage') ->where('order_item_id', $item['id']) ->where('user_id', $order['user_id']) ->lock(true) ->find(); if ($existingUsage) { Log::info('订单商品已处理,跳过', [ 'order_item_id' => $item['id'], 'order_id' => $order['id'] ]); continue; } // 9. 获取套餐信息 $package = Db::table('ba_package_price') ->where('id', $item['package_price_id']) ->find(); if (!$package) { throw new \Exception('套餐不存在: ' . $item['package_price_id']); } Log::info('处理订单商品', [ 'order_item_id' => $item['id'], 'product_id' => $item['product_id'], 'package_id' => $item['package_price_id'], 'package_type' => $package['type'], 'card_type' => $package['card_type'], 'quantity' => $item['quantity'] ]); if ($package['type'] == 1) { // 个人套餐:立即创建使用记录 $userProductService->createUserProductRecord($order['user_id'], $item, $package); } else { // 企业套餐:创建席位记录 // 首先验证企业购买 $companyInfo = $companyPackageService->getUserCompany($order['user_id']); if (!$companyInfo) { throw new \Exception('企业套餐购买失败:用户不属于任何企业'); } // 创建企业套餐席位记录 $companyPackageService->processCompanyPackagePurchase( $order['id'], $companyInfo['id'], $package['id'], $item['quantity'], $package['card_type'], $package['unit_value'], $package['points_value'] ); // 企业套餐购买后,自动分配一个席位给购买者 $companyPackageService->autoAllocateToBuyer( $order['user_id'], $companyInfo['id'], $package['id'], $order['id'] ); // 注意:这里不再重复创建用户产品记录! // 因为 autoAllocateToBuyer 中已经会创建 // 只有次卡/积分卡需要在购买时也创建使用记录 // if ($package['card_type'] == 1 || $package['card_type'] == 2) { // // 使用事务安全的方式创建 // $this->createUserProductRecordSafe($order['user_id'], $item, $package); // } } // 10. 扣减库存 $this->deductStockAfterPayment($order['id']); } // 11. 处理分销佣金 if ($order['is_commission'] == 0 && $order['distributor_id'] && $order['commission_amount'] > 0) { $this->processCommission($order); } Db::commit(); Log::info('支付成功处理完成', ['order_no' => $orderNo]); } catch (\Throwable $e) { Db::rollback(); Log::error('处理支付成功失败: ' . $e->getMessage()); throw $e; } } /** * 安全创建用户产品记录(防止重复) */ private function createUserProductRecordSafe($userId, $orderItem, $package) { try { // 检查是否已存在 $existing = Db::table('ba_user_product_usage') ->where('order_item_id', $orderItem['id']) ->where('user_id', $userId) ->find(); if ($existing) { Log::info('用户产品记录已存在,跳过创建', [ 'order_item_id' => $orderItem['id'], 'user_id' => $userId ]); return $existing['id']; } // 创建用户产品服务实例 $userProductService = new \app\common\service\UserProductService(); return $userProductService->createUserProductRecord($userId, $orderItem, $package); } catch (\Throwable $e) { Log::error('创建用户产品记录失败: ' . $e->getMessage()); // 不抛出异常,避免影响主流程 return false; } } /** * 支付成功后扣减库存 */ private function deductStockAfterPayment($orderId) { try { // 查询订单商品 $orderItem = Db::table('ba_order_item') ->where('order_id', $orderId) ->find(); if (!$orderItem) { return; } // 扣减套餐库存 if ($orderItem['package_price_id']) { $result = Db::table('ba_package_price') ->where('id', $orderItem['package_price_id']) ->where('stock', '>=', $orderItem['quantity']) // 再次检查库存 ->dec('stock', $orderItem['quantity']) ->inc('sold_count', $orderItem['quantity']) ->update(); if (!$result) { // 库存不足,需要处理异常(退款或补货) \think\facade\Log::error('支付成功后库存不足,订单ID:' . $orderId); // 这里可以触发退款逻辑或人工处理 } } // 如果有产品库存,也可以在这里扣减 if ($orderItem['product_id']) { // 如果产品表有库存字段,在这里扣减 // Db::table('ba_product')... } } catch (\Throwable $e) { \think\facade\Log::error('扣减库存失败: ' . $e->getMessage()); } } /** * 处理分销佣金 */ private function processCommission($order) { try { // 调用佣金服务 $commissionService = new \app\common\service\CommissionService(); $result = $commissionService->updateDistributionRecord($order['id'], 2, $order['pay_time']); if ($result) { // 2. 更新分销人的佣金统计 $commissionService->updateUserCommission($order['distributor_id']); // 3. 标记订单佣金已处理 Db::table('ba_order') ->where('id', $order['id']) ->update([ 'is_commission' => 1, 'update_time' => date('Y-m-d H:i:s') ]); } } catch (\Throwable $e) { \think\facade\Log::error('处理分销佣金失败: ' . $e->getMessage()); } } /** * 确认订单库存 */ private function confirmOrderStock($orderId) { try { // 查询订单商品 $orderItems = Db::table('ba_order_item') ->where('order_id', $orderId) ->select(); foreach ($orderItems as $item) { if ($item['package_price_id']) { // 可以在这里记录库存确认,或者更新相关状态 // 由于创建订单时已经扣减库存,这里主要做确认处理 } } } catch (\Throwable $e) { \think\facade\Log::error('确认订单库存失败: ' . $e->getMessage()); } } /** * 发送支付成功通知 */ private function sendPaymentSuccessNotify($order) { // 这里可以实现发送微信模板消息、短信、站内信等通知 // 根据业务需求实现 \think\facade\Log::info('支付成功通知: 订单号=' . $order['order_no'] . ', 用户ID=' . $order['user_id']); } /** * 获取状态消息 */ private function getStatusMessage($status) { $messages = [ 0 => '等待支付', 1 => '支付成功', 2 => '支付失败', 3 => '支付已关闭', ]; return $messages[$status] ?? '未知状态'; } /** * XML转数组 */ private function xmlToArray($xml) { if (empty($xml)) { return []; } try { // 使用DOMDocument方式,兼容PHP 8.0+ $dom = new \DOMDocument(); if (version_compare(PHP_VERSION, '8.0.0', '>=')) { // PHP 8.0+ 直接加载 $dom->loadXML($xml, LIBXML_NOENT | LIBXML_NOCDATA); } else { // PHP 8.0以下需要禁用实体加载器 $previous = libxml_disable_entity_loader(true); $dom->loadXML($xml, LIBXML_NOENT | LIBXML_NOCDATA); libxml_disable_entity_loader($previous); } // 简单的转换方法 $data = []; foreach ($dom->documentElement->childNodes as $node) { if ($node->nodeType == XML_ELEMENT_NODE) { $data[$node->nodeName] = $node->nodeValue; } } return $data; } catch (\Exception $e) { \think\facade\Log::error('XML解析失败: ' . $e->getMessage()); return []; } } /** * 数组转XML */ private function arrayToXml($arr) { $xml = ''; foreach ($arr as $key => $val) { if (is_numeric($val)) { $xml .= "<{$key}>{$val}"; } else { $xml .= "<{$key}>"; } } $xml .= ''; return $xml; } }