添加支付日志
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user