parseAndVerifyCallback(); $eventType = strtoupper($parsed['event_type']); $data = $parsed['data']; if ($eventType === 'TRANSACTION.SUCCESS' && !empty($data['out_trade_no'])) { PaymentService::confirmOrderPay((string)$data['out_trade_no'], $data); } return $this->wxResponse(true); } catch (\Throwable $e) { Log::error('[Wxpay::notify] ' . $e->getMessage()); return $this->wxResponse(false, $e->getMessage()); } } /** * 2. 微信原路退款结果异步通知 (/api/wxpay/refundNotify) */ public function refundNotify() { try { $parsed = $this->parseAndVerifyCallback(); $data = $parsed['data']; $outRefundNo = trim((string)($data['out_refund_no'] ?? '')); $outTradeNo = trim((string)($data['out_trade_no'] ?? '')); $refStatus = strtoupper((string)($data['refund_status'] ?? '')); if ($outRefundNo === '' && $outTradeNo === '') { throw new \RuntimeException('退款回调缺少商户退款单号与商户支付单号'); } $q = Db::name('pay_order'); if ($outRefundNo !== '') { $q->where('refund_no', $outRefundNo); } else { $q->where('out_trade_no', $outTradeNo); } $po = $q->find(); if (!$po) { Log::error("[Wxpay::refundNotify] 未找到本地支付流水单 out_refund_no={$outRefundNo}, out_trade_no={$outTradeNo}"); throw new \RuntimeException('未匹配到本地支付交易流水单'); } try { PaymentService::applyWxRefundResult($po, $data); } catch (\Throwable $refundEx) { // 若微信已下发明确退款异常/关闭终态,系统已完整落库记录原因并写入告警日志,向微信回传成功避免重复轰炸 if (in_array($refStatus, ['ABNORMAL', 'CLOSED'], true)) { Log::error("[Wxpay::refundNotify] 退款终态异常已落库 out_trade_no={$po['out_trade_no']}, status={$refStatus}, reason=" . $refundEx->getMessage()); return $this->wxResponse(true); } throw $refundEx; } return $this->wxResponse(true); } catch (\Throwable $e) { Log::error('[Wxpay::refundNotify] ' . $e->getMessage()); return $this->wxResponse(false, $e->getMessage()); } } /** * 3. 微信新版「商家转账」终态结果异步通知 (/api/wxpay/transferNotify) */ public function transferNotify() { try { $parsed = $this->parseAndVerifyCallback(); $data = $parsed['data']; $outBillNo = trim((string)($data['out_bill_no'] ?? '')); if ($outBillNo === '') { throw new \RuntimeException('商家转账回调缺少商户转账单号 out_bill_no'); } $wd = Db::name('withdraw')->where('out_bill_no', $outBillNo)->find(); if (!$wd) { Log::error("[Wxpay::transferNotify] 未找到本地提现单记录 out_bill_no={$outBillNo}"); throw new \RuntimeException('未找到匹配的本地提现单'); } PaymentService::syncWithdrawTransfer((int)$wd['id'], $data); return $this->wxResponse(true); } catch (\Throwable $e) { Log::error('[Wxpay::transferNotify] ' . $e->getMessage()); return $this->wxResponse(false, $e->getMessage()); } } /** * 读取原始报文并使用微信支付公钥验签 + AES-256-GCM 解密 */ protected function parseAndVerifyCallback() { $rawBody = file_get_contents('php://input'); if ($rawBody === false || $rawBody === '') { $rawBody = (string)$this->request->getInput(); } $headers = $this->request->header(); $wxSvc = new WxPayV3Service(); return $wxSvc->verifyAndDecryptNotify((string)$rawBody, is_array($headers) ? $headers : []); } /** * 输出符合微信支付 APIv3 规范的 HTTP 响应 */ protected function wxResponse($ok, $msg = '') { $code = $ok ? 200 : 500; $body = [ 'code' => $ok ? 'SUCCESS' : 'FAIL', 'message' => $ok ? '成功' : ($msg ?: '处理失败'), ]; return Response::create($body, 'json', $code); } }