日志记录

This commit is contained in:
Billy 2025-10-21 11:11:59 +08:00
parent 2dc5ff423f
commit 8de5d88091
3 changed files with 134 additions and 17 deletions

View File

@ -108,7 +108,7 @@ return [
// 商户信息
'merchant' => [
'name' => Env::get('ccb.merchant_name', '商户名称'),
'name' => Env::get('ccb.merchant_name', '丰科贸易(荷西嘉园店)'),
'logo_url' => Env::get('ccb.merchant_logo', ''),
'order_detail_url' => Env::get('app_url', 'http://fengketrade.test') . '/pages/order/detail?id=',
],

View File

@ -2,6 +2,8 @@
namespace addons\shopro\library\ccblife;
use think\Log;
/**
* 建行生活HTTP客户端
* 处理与建行API的通信包括加密、签名、发送请求和解密响应
@ -48,6 +50,10 @@ class CcbHttpClient
// 构建请求报文
$message = $this->buildMessage($txCode, $body, $txSeq);
// 📝 记录原始请求报文(加密前)
Log::info('建行生活API原始请求报文 [txCode=' . $txCode . '] [txSeq=' . $txSeq . ']');
Log::info('原始报文内容: ' . $message);
// 加密报文
$encryptedMessage = CcbRSA::encryptForCcb($message, $this->config['public_key']);
@ -99,6 +105,9 @@ class CcbHttpClient
*/
private function sendHttpRequest($txCode, $cnt, $mac)
{
// 记录请求开始时间
$startTime = microtime(true);
// 构建完整的API URL基础URL + ?txcode=交易代码)
$apiUrl = $this->config['api_base_url'] . '?txcode=' . $txCode;
@ -108,6 +117,11 @@ class CcbHttpClient
'mac' => $mac
];
// 📝 记录请求参数(加密后的完整内容)
Log::info('建行生活API加密请求参数 [txCode=' . $txCode . '] [url=' . $apiUrl . '] [timeout=' . self::DEFAULT_TIMEOUT . 's]');
Log::info('加密参数 cnt: ' . $cnt);
Log::info('加密参数 mac: ' . $mac);
// 初始化CURL
$ch = curl_init();
@ -130,17 +144,32 @@ class CcbHttpClient
$response = curl_exec($ch);
$error = curl_error($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlInfo = curl_getinfo($ch);
curl_close($ch);
// 计算请求耗时
$costTime = round((microtime(true) - $startTime) * 1000, 2); // 毫秒
// 检查错误
if ($error) {
// 📝 记录错误日志
Log::error('建行生活API请求失败 [txCode=' . $txCode . '] [url=' . $apiUrl . '] [error=' . $error . '] [cost_time=' . $costTime . 'ms] [total_time=' . ($curlInfo['total_time'] ?? 0) . 's] [connect_time=' . ($curlInfo['connect_time'] ?? 0) . 's]');
throw new \Exception('HTTP请求失败: ' . $error);
}
if ($httpCode !== 200) {
// 📝 记录HTTP状态码异常日志
$responsePreview = mb_substr($response, 0, 300);
Log::error('建行生活API响应状态码异常 [txCode=' . $txCode . '] [url=' . $apiUrl . '] [http_code=' . $httpCode . '] [cost_time=' . $costTime . 'ms] [response=' . $responsePreview . ']');
throw new \Exception('HTTP状态码异常: ' . $httpCode . ', 响应内容: ' . $response);
}
// 📝 记录成功响应日志
$totalTime = round(($curlInfo['total_time'] ?? 0) * 1000, 2);
$connectTime = round(($curlInfo['connect_time'] ?? 0) * 1000, 2);
$responseLength = mb_strlen($response);
Log::info('建行生活API请求成功 [txCode=' . $txCode . '] [url=' . $apiUrl . '] [http_code=' . $httpCode . '] [response_length=' . $responseLength . '] [cost_time=' . $costTime . 'ms] [total_time=' . $totalTime . 'ms] [connect_time=' . $connectTime . 'ms]');
return $response;
}
@ -153,6 +182,9 @@ class CcbHttpClient
*/
private function handleResponse($response)
{
// 📝 记录原始响应内容
Log::info('建行生活API原始响应内容: ' . $response);
// 解析JSON响应
$responseData = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
@ -164,15 +196,25 @@ class CcbHttpClient
throw new \Exception('响应格式错误缺少cnt或mac字段');
}
// 📝 记录加密响应参数
Log::info('加密响应参数 cnt: ' . $responseData['cnt']);
Log::info('加密响应参数 mac: ' . $responseData['mac']);
// 解密响应内容
$decryptedContent = CcbRSA::decryptFromCcb($responseData['cnt'], $this->config['private_key']);
// 📝 记录解密后的响应内容
Log::info('解密后响应内容: ' . $decryptedContent);
// 验证签名
$isValid = CcbMD5::verifyApiSignature($decryptedContent, $responseData['mac'], $this->config['private_key']);
if (!$isValid) {
Log::error('响应签名验证失败 [expected_mac=' . $responseData['mac'] . ']');
throw new \Exception('响应签名验证失败');
}
Log::info('响应签名验证成功');
// 解析解密后的内容
$decryptedData = json_decode($decryptedContent, true);
if (json_last_error() !== JSON_ERROR_NONE) {

View File

@ -272,7 +272,7 @@ class CcbOrderService
/**
* 构建符合建行 A3341TP01 接口规范的订单数据
*
* 📋 建行生活订单推送接口规范说明
* 📋 建行生活订单推送接口规范说明v1.1.6
*
* 必填字段11个
* - USER_ID: 客户编号建行用户ID
@ -280,27 +280,30 @@ class CcbOrderService
* - ORDER_DT: 订单日期yyyyMMddHHmmss格式
* - TOTAL_AMT: 订单原金额
* - ORDER_STATUS: 订单状态
* - REFUND_STATUS: 退款状态
* - MCT_NM: 商户名称
* - CUS_ORDER_URL: 订单详情链接
* - PAY_FLOW_ID: 支付流水号(从 shopro_pay.pay_sn 获取)
* - PRPFTL_MRCH_ID: 门店商户号(使用 merchant_id
* - PAY_MRCH_ID: 支付商户号(使用 merchant_id
* - PAY_FLOW_ID: 支付流水号
* - PAY_MRCH_ID: 支付商户号
* - SKU_LIST: 商品信息JSON字符串
*
* 非必填但推荐字段
* - PAY_AMT: 订单实际支付金额
* - DISCOUNT_AMT: 第三方平台优惠金额
* 重要可选字段(建议必填)
* - PAY_AMT: 订单实际支付金额(文档要求:如为空必须在状态变更时推送)
* - DISCOUNT_AMT: 第三方平台优惠金额(文档要求:如为空必须在状态变更时推送)
* - DISCOUNT_AMT_DESC: 第三方平台优惠说明
* - REFUND_STATUS: 退款状态
* - PAY_MODE: 支付方式
* - INV_DT: 订单过期日期
* - GOODS_NM: 商品名称
* - PREFTL_MRCH_ID: 门店商户号
* - PLAT_MCT_ID: 服务商门店编号
* - PLAT_ORDER_TYPE: 服务方订单类型
* - COUPON_AMT: 优惠券金额
* - PLATFORM: 下单场景
*
* ⚠️ 注意Shopro字段映射
* - pay_fee PAY_AMT实际支付金额
* - order_amount TOTAL_AMT订单总金额
* - total_discount_fee DISCOUNT_AMT优惠总金额
* - createtime ORDER_DT毫秒时间戳需除以1000
* - expiry_time INV_DT过期时间
*
* @param array $order 订单数组
* @param array $orderItems 订单商品列表
@ -309,10 +312,21 @@ class CcbOrderService
*/
private function buildOrderData($order, $orderItems, $ccbUserId)
{
// ⚠️ 验证必填字段PAY_FLOW_ID支付流水号
// 这个字段在 createPayment 时设置,推送订单前必须存在
if (empty($order['ccb_pay_flow_id'])) {
throw new \Exception('订单支付流水号(ccb_pay_flow_id)不存在请先调用createPayment生成支付串');
}
// 构建SKU商品列表JSON字符串格式
$skuList = $this->buildSkuList($orderItems);
// 计算各项金额保留2位小数
$totalAmount = number_format($order['order_amount'] ?? 0, 2, '.', '');
$payAmount = number_format($order['pay_fee'] ?? $order['order_amount'] ?? 0, 2, '.', '');
$discountAmount = number_format($order['total_discount_fee'] ?? 0, 2, '.', '');
$totalRefundAmount = number_format($order['refund_fee'] ?? 0, 2, '.', '');
// 处理订单时间Shopro的createtime是毫秒时间戳需要除以1000
$createTimeValue = $order['createtime'] ?? null;
if (empty($createTimeValue) || !is_numeric($createTimeValue)) {
@ -320,8 +334,42 @@ class CcbOrderService
}
$orderDt = date('YmdHis', intval($createTimeValue / 1000));
// 处理订单过期时间
$invDt = '';
if (!empty($order['expiry_time'])) {
// Shopro 的 expiry_time 可能是时间戳或日期字符串
if (is_numeric($order['expiry_time'])) {
// 如果是毫秒时间戳需要除以1000
$timestamp = intval($order['expiry_time']);
if ($timestamp > 9999999999) {
$timestamp = intval($timestamp / 1000);
}
$invDt = date('YmdHis', $timestamp);
} else {
$invDt = date('YmdHis', strtotime($order['expiry_time']));
}
}
// 获取商品名称(取第一个商品)
$goodsName = '';
if (!empty($orderItems)) {
$goodsName = $orderItems[0]['goods_title'] ?? '';
// 如果有多个商品,可以拼接
if (count($orderItems) > 1) {
$goodsName .= ' 等' . count($orderItems) . '件商品';
}
}
// 构建优惠说明(如果有优惠金额)
$discountAmtDesc = '';
if ($discountAmount > 0) {
// 格式:名称=金额|@|名称=金额
// 这里简化处理,实际应该根据具体优惠券信息构建
$discountAmtDesc = '平台优惠=' . $discountAmount;
}
// 构建符合A3341TP01接口规范的订单数据
return [
$orderData = [
// ========== 必填字段 ==========
'USER_ID' => $ccbUserId, // 客户编号
'ORDER_ID' => $order['order_sn'], // 订单号
@ -330,13 +378,40 @@ class CcbOrderService
'ORDER_STATUS' => $this->mapOrderStatus($order['status']), // 订单状态
'REFUND_STATUS' => $this->mapRefundStatus($order['refund_status'] ?? 0), // 退款状态
'MCT_NM' => $this->config['merchant']['name'] ?? '商户名称', // 商户名称
'CUS_ORDER_URL' => $this->config['merchant']['order_detail_url'] . $order['id'], // 订单详情链接
'PAY_FLOW_ID' => $order['ccb_pay_flow_id'], // 支付流水号(必填!)
'PAY_FLOW_ID' => $order['ccb_pay_flow_id'], // 支付流水号(必填!已在上方验证)
'PAY_MRCH_ID' => $this->config['merchant_id'], // 支付商户号(必填!)
'SKU_LIST' => $skuList, // 商品信息JSON字符串必填
// ========== 非必填字段 ==========
'PLAT_ORDER_TYPE' => "T0000", // 服务方订单类型
// ========== 重要可选字段(强烈建议填写) ==========
'PAY_AMT' => $payAmount, // 订单实际支付金额
'DISCOUNT_AMT' => $discountAmount, // 第三方平台优惠金额
'PLAT_ORDER_TYPE' => 'T0000', // 服务方订单类型T0000-普通类型)
'PLATFORM' => '99', // 下单场景99-建行生活APP
];
// ========== 条件可选字段(有值才添加) ==========
// 优惠说明
if (!empty($discountAmtDesc)) {
$orderData['DISCOUNT_AMT_DESC'] = $discountAmtDesc;
}
// 订单过期时间
if (!empty($invDt)) {
$orderData['INV_DT'] = $invDt;
}
// 商品名称
if (!empty($goodsName)) {
$orderData['GOODS_NM'] = mb_substr($goodsName, 0, 200); // 限制长度200字符
}
// 累计退款金额(如果有退款)
if ($totalRefundAmount > 0) {
$orderData['TOTAL_REFUND_AMT'] = $totalRefundAmount;
}
return $orderData;
}
/**