添加支付日志

This commit is contained in:
Dev
2026-09-23 14:46:56 +08:00
parent 5cc504bb44
commit a30d534c3d
3 changed files with 193 additions and 28 deletions
+22 -3
View File
@@ -250,6 +250,19 @@ class Order extends Api
public function payNotify()
{
header('Content-Type: application/json; charset=utf-8');
// 原始报文(用于排查微信是否真的回调到本地址)
$rawBody = file_get_contents('php://input');
$notifyHeaders = [];
foreach ($_SERVER as $key => $value) {
if (stripos($key, 'HTTP_WECHATPAY') !== false || in_array($key, ['CONTENT_TYPE', 'REQUEST_METHOD'])) {
$notifyHeaders[$key] = $value;
}
}
Log::info('微信支付回调[进入] uri=' . ($_SERVER['REQUEST_URI'] ?? '') . ' ' . json_encode([
'headers' => $notifyHeaders,
'body' => is_string($rawBody) ? $rawBody : '',
], JSON_UNESCAPED_UNICODE));
try {
// PSR-7 响应(SDK 已完成验签/解密,业务失败返回 500 由微信重试)
$response = OrderService::payNotify();
@@ -257,10 +270,16 @@ class Order extends Api
foreach ($response->getHeaders() as $name => $values) {
header($name . ': ' . implode(', ', $values));
}
echo $response->getBody()->getContents();
$body = $response->getBody()->getContents();
Log::info('微信支付回调[应答] http_code=' . $response->getStatusCode() . ' body=' . $body);
echo $body;
exit;
} catch (\Exception $e) {
Log::info('微信回调信息信息_result_error:' . json_encode(['msg' => $e->getMessage()], JSON_UNESCAPED_UNICODE));
} catch (\Throwable $e) {
// 注意:EasyWeChat 的 serve() 只捕获 Exception,TypeError/Error 会冒泡到这里
Log::error('微信回调信息_result_error:' . json_encode([
'msg' => $e->getMessage(),
'file' => $e->getFile() . ':' . $e->getLine(),
], JSON_UNESCAPED_UNICODE));
http_response_code(500);
echo json_encode(['code' => 'FAIL', 'message' => $e->getMessage()], JSON_UNESCAPED_UNICODE);
exit;
@@ -232,15 +232,69 @@ class EasyWeChatService
*/
public function handlePayNotify(\Closure $paidHandler)
{
$request = $this->payApp->getRequest();
$traceId = date('YmdHis') . substr(md5(uniqid('', true)), 0, 6);
// 【日志1】回调原始报文:确认微信有没有真的打过来、打到哪个地址
Log::info('微信支付回调[收到请求] trace=' . $traceId . ' ' . json_encode([
'method' => $request->getMethod(),
'uri' => (string)$request->getUri(),
'headers' => self::getNotifyHeaders($request),
'body' => (string)$request->getBody(),
], JSON_UNESCAPED_UNICODE));
$server = $this->payApp->getServer();
$server->handlePaid(function (PayMessage $message, \Closure $next) use ($paidHandler) {
$server->handlePaid(function (PayMessage $message, \Closure $next) use ($paidHandler, $traceId) {
// 【日志2】解密后的业务数据(含 out_trade_no / trade_state / amount)
Log::info('微信支付回调[解密数据] trace=' . $traceId . ' ' . json_encode($message->toArray(), JSON_UNESCAPED_UNICODE));
// 回调签名验证(官方推荐,验签失败抛异常 -> serve 返回失败,微信将重试)
$this->payApp->getValidator()->validate($this->payApp->getRequest());
$paidHandler($message);
try {
$this->payApp->getValidator()->validate($this->payApp->getRequest());
Log::info('微信支付回调[验签通过] trace=' . $traceId);
} catch (\Throwable $e) {
Log::error('微信支付回调[验签失败] trace=' . $traceId . ' msg=' . $e->getMessage());
throw $e;
}
try {
$paidHandler($message, $traceId);
} catch (\Throwable $e) {
Log::error('微信支付回调[业务处理异常] trace=' . $traceId . ' msg=' . $e->getMessage()
. ' file=' . $e->getFile() . ':' . $e->getLine());
throw $e;
}
Log::info('微信支付回调[业务处理完成] trace=' . $traceId);
return $next($message);
});
return $server->serve();
$response = $server->serve();
// 注意:先转字符串(内部会 seek(0))并 rewind,保证控制器后续仍能读取 body 回给微信
$responseBody = (string)$response->getBody();
$response->getBody()->rewind();
// 【日志3】应答给微信的结果:status=200 且 code=SUCCESS 才算回调成功
Log::info('微信支付回调[响应微信] trace=' . $traceId . ' status=' . $response->getStatusCode() . ' body=' . $responseBody);
return $response;
}
/**
* 提取回调请求中与验签相关的请求头(便于排查签名/证书问题)
* @param \Psr\Http\Message\ServerRequestInterface $request
* @return array
*/
private static function getNotifyHeaders($request): array
{
$headers = [];
foreach ($request->getHeaders() as $name => $values) {
if (stripos($name, 'wechatpay') !== false || in_array(strtolower($name), ['content-type', 'user-agent'])) {
$headers[$name] = implode(', ', $values);
}
}
return $headers;
}
}
@@ -5,6 +5,7 @@ namespace app\common\service;
use think\Cache;
use think\Db;
use think\Log;
use EasyWeChat\Pay\Message as PayMessage;
use app\common\model\Orders;
use app\common\traits\ServiceTrait;
use think\Exception;
@@ -271,6 +272,14 @@ class OrderService
throw new \Exception('该订单不存在');
}
$orderData = $orderInfo->toArray();
Log::info('微信下单[发起支付] ' . json_encode([
'order_no' => $orderNo,
'user_id' => $userId,
'order_status' => $orderData['order_status'] ?? null,
'status_text' => self::ORDER_STATUS_MAP[$orderData['order_status'] ?? -1] ?? '未知',
'payment_amount' => $orderData['payment_amount'] ?? 0,
'total_amount' => $orderData['total_amount'] ?? 0,
], JSON_UNESCAPED_UNICODE));
$orderData += [
'channel' => 'wechat',
'tradeNo' => $orderData['order_no'],
@@ -278,6 +287,10 @@ class OrderService
'transactionId' => 0,
'payInfo' => self::pay($orderInfo->order_no, $orderInfo->total_amount, $openid), //统一下单接口
];
Log::info('微信下单[下单成功] ' . json_encode([
'order_no' => $orderNo,
'payInfo' => $orderData['payInfo'],
], JSON_UNESCAPED_UNICODE));
return $orderData;
@@ -393,46 +406,117 @@ class OrderService
{
$service = new EasyWeChatService();
return $service->handlePayNotify(function (PayMessage $message) {
Log::info('微信回调信息_request:' . json_encode($message->getOriginalAttributes() ?? [], JSON_UNESCAPED_UNICODE));
return $service->handlePayNotify(function (PayMessage $message, $traceId = '') {
$prefix = '微信支付回调[业务] trace=' . $traceId . ' ';
$order_no = $message->out_trade_no ?? '';
$total = $message->amount['total'] ?? 0;
Log::info($prefix . '支付成功通知: ' . json_encode([
'order_no' => $order_no,
'transaction_id' => $message->transaction_id ?? '',
'trade_state' => $message->trade_state ?? '',
'trade_type' => $message->trade_type ?? '',
'amount_fen' => $total,
'amount_yuan' => sprintf('%.2f', $total / 100),
'success_time' => $message->success_time ?? '',
], JSON_UNESCAPED_UNICODE));
$orderInfo = OrderService::getOne(['order_no' => $order_no]);
if (!$orderInfo) {
Log::error($prefix . '订单不存在: ' . json_encode(['order_no' => $order_no], JSON_UNESCAPED_UNICODE));
throw new \Exception('获取订单数据失败');
}
Log::info($prefix . '订单当前状态: ' . json_encode([
'order_id' => $orderInfo->id,
'order_no' => $order_no,
'order_status' => $orderInfo->order_status,
'status_text' => self::ORDER_STATUS_MAP[$orderInfo->order_status] ?? '未知',
'order_type' => $orderInfo->order_type,
'parent_no' => $orderInfo->parent_order_no ?? '',
'payment_amount'=> $orderInfo->payment_amount,
], JSON_UNESCAPED_UNICODE));
//金额校验(v3 通知金额单位为分)
$total = $message->amount['total'] ?? 0;
$checkMoney = sprintf('%.1f', $total / 100) == sprintf('%.1f', $orderInfo->payment_amount);
if (!$checkMoney) {
Log::error($prefix . '金额校验失败: ' . json_encode([
'notify_amount' => sprintf('%.2f', $total / 100),
'order_amount' => sprintf('%.2f', $orderInfo->payment_amount),
], JSON_UNESCAPED_UNICODE));
throw new \Exception('金额错误:' . sprintf('%.2f', $total / 100) . '----' . sprintf('%.2f', $orderInfo->payment_amount));
}
if (($message->trade_state ?? '') != 'SUCCESS') {
Log::error($prefix . '支付状态异常: trade_state=' . ($message->trade_state ?? ''));
throw new \Exception('支付失败:' . ($message->trade_state ?? ''));
}
Log::info('微信回调信息_result:' . json_encode(['order_no' => $order_no, 'trade_state' => $message->trade_state], JSON_UNESCAPED_UNICODE));
//如果为待支付,才可更新为已支付。防止重复更新状态,导致重复发货。
if ($orderInfo->order_status == self::ORDER_STATUS_WAIT_PAY) {
$paymentData = [
'order_no' => $order_no,
'pay_status' => 'SUCCESS',
'amount' => $total / 100,
'currency' => $message->amount['currency'] ?? 'CNY',
'pay_type' => '1', // 1微信 2支付宝
'pay_way' => $message->trade_type ?? 'JSAPI',
'trace_no' => $message->transaction_id ?? '',
'pay_time' => time(),
'pay_success_time' => strtotime((string)($message->success_time ?? '')) ?: time(),
];
Db::transaction(function () use ($order_no, $paymentData, $orderInfo) {
self::where('order_no', $order_no)->update(['order_status' => self::ORDER_STATUS_WAIT_SHIP]);
OrderPaymentService::create($paymentData);
if ($orderInfo->order_type == 1) {
if ($orderInfo->order_status != self::ORDER_STATUS_WAIT_PAY) {
Log::info($prefix . '订单已处理过,跳过状态更新(幂等): ' . json_encode([
'order_no' => $order_no,
'order_status' => $orderInfo->order_status,
'status_text' => self::ORDER_STATUS_MAP[$orderInfo->order_status] ?? '未知',
], JSON_UNESCAPED_UNICODE));
return true;
}
$paymentData = [
'order_no' => $order_no,
'pay_status' => 'SUCCESS',
'amount' => $total / 100,
'currency' => $message->amount['currency'] ?? 'CNY',
'pay_type' => '1', // 1微信 2支付宝
'pay_way' => $message->trade_type ?? 'JSAPI',
'trace_no' => $message->transaction_id ?? '',
'pay_time' => time(),
'pay_success_time' => strtotime((string)($message->success_time ?? '')) ?: time(),
];
Log::info($prefix . '开始更新订单状态: ' . json_encode($paymentData, JSON_UNESCAPED_UNICODE));
try {
Db::transaction(function () use ($order_no, $paymentData, $orderInfo, $prefix) {
$updateRows = self::where('order_no', $order_no)->update(['order_status' => self::ORDER_STATUS_WAIT_SHIP]);
Log::info($prefix . '订单状态更新: order_no=' . $order_no
. ' 10(待支付) -> ' . self::ORDER_STATUS_WAIT_SHIP . '(待发货) 影响行数=' . $updateRows);
$payment = OrderPaymentService::create($paymentData);
Log::info($prefix . '支付流水写入: ' . json_encode([
'payment_id' => $payment->id ?? 0,
'order_no' => $payment->order_no ?? '',
'pay_status' => $payment->pay_status ?? '',
'amount' => $payment->amount ?? 0,
'trace_no' => $payment->trace_no ?? '',
], JSON_UNESCAPED_UNICODE));
if ($orderInfo->order_type == self::ORDER_TYPE_FIRST_PAYMENT) {
Log::info($prefix . '需求首付款订单,处理合同成交: parent_order_no=' . $orderInfo->parent_order_no);
DemandService::customerContractFinishedDeal($orderInfo->parent_order_no);
} elseif ($orderInfo->order_type == 2) {
} elseif ($orderInfo->order_type == self::ORDER_TYPE_FINAL_PAYMENT) {
Log::info($prefix . '需求尾款订单,处理验收结算: parent_order_no=' . $orderInfo->parent_order_no);
DemandService::customerWorkAcceptFinishedDeal($orderInfo->parent_order_no);
}
});
} catch (\Throwable $e) {
Log::error($prefix . '事务执行失败已回滚: msg=' . $e->getMessage()
. ' file=' . $e->getFile() . ':' . $e->getLine());
throw $e;
}
// 复核:确认状态与支付流水真的落库了
$newOrder = OrderService::getOne(['order_no' => $order_no]);
$payment = OrderPaymentService::getOne(['order_no' => $order_no]);
Log::info($prefix . '回调处理成功: ' . json_encode([
'order_no' => $order_no,
'new_order_status'=> $newOrder->order_status ?? null,
'new_status_text' => self::ORDER_STATUS_MAP[$newOrder->order_status ?? -1] ?? '未知',
'payment_id' => $payment->id ?? 0,
'payment_status' => $payment->pay_status ?? '',
'trace_no' => $payment->trace_no ?? '',
], JSON_UNESCAPED_UNICODE));
return true;
});
}
@@ -460,14 +544,22 @@ class OrderService
public static function pay($order_sn, $total_fee, $openid, string $body = '软件产品服务')
{
if (!($body && $order_sn && $total_fee && $openid)) {
Log::error('微信下单[参数错误] ' . json_encode([
'body' => $body,
'order_sn' => $order_sn,
'total_fee' => $total_fee,
'openid' => $openid,
], JSON_UNESCAPED_UNICODE));
throw new \Exception('支付参数错误2');
}
$EasyWeChatService = new EasyWeChatService();
$payRes = $EasyWeChatService->unify($body, $order_sn, $total_fee, $openid);
if (!($payRes && !empty($payRes['prepay_id']))) {
Log::error('微信下单[统一下单失败] order_sn=' . $order_sn . ' res=' . json_encode($payRes, JSON_UNESCAPED_UNICODE));
throw new \Exception('统一下单接口调用失败:' . ($payRes['message'] ?? ($payRes['errmsg'] ?? '未知错误')));
}
Log::info('微信下单[prepay_id] order_sn=' . $order_sn . ' prepay_id=' . $payRes['prepay_id']);
$payData = $EasyWeChatService->sdkConfig($payRes['prepay_id']);
return $payData;