代码优化

This commit is contained in:
Billy 2025-10-21 20:48:28 +08:00
parent 866ca34023
commit 95d2a437d2
4 changed files with 38 additions and 22 deletions

View File

@ -44,8 +44,8 @@ return [
/** /**
* 服务方私钥 (自己生成的RSA私钥) * 服务方私钥 (自己生成的RSA私钥)
* 用途: * 用途:
* - API请求签名MD5(明文 + 私钥)
* - 解密建行返回的加密数据(ccbParamSJ等) * - 解密建行返回的加密数据(ccbParamSJ等)
* - 注意: 不直接参与签名,仅用于解密
* 格式: BASE64格式(不含PEM头尾) PEM格式(含头尾) * 格式: BASE64格式(不含PEM头尾) PEM格式(含头尾)
*/ */
'private_key' => $envVars['private_key'] ?? '', 'private_key' => $envVars['private_key'] ?? '',
@ -53,7 +53,8 @@ return [
/** /**
* 服务方公钥 (自己生成的RSA公钥,对应上面的私钥) * 服务方公钥 (自己生成的RSA公钥,对应上面的私钥)
* 用途: * 用途:
* - 参与支付下单的MD5签名计算(PLATFORMPUB字段) * - 加密API请求报文建行用相同的公钥解密
* - 支付下单的MD5签名计算(PLATFORMPUB字段)
* - 加密商户公钥(ENCPUB字段) * - 加密商户公钥(ENCPUB字段)
* 格式: BASE64格式(不含PEM头尾) PEM格式(含头尾) * 格式: BASE64格式(不含PEM头尾) PEM格式(含头尾)
*/ */
@ -63,7 +64,6 @@ return [
* 建行生活支付验签公钥 (建行生活平台分配的) * 建行生活支付验签公钥 (建行生活平台分配的)
* 用途: * 用途:
* - 验证异步通知中的SIGN字段(NT_TYPE=YS时) * - 验证异步通知中的SIGN字段(NT_TYPE=YS时)
* - ⚠️ 重要: 这不是你自己的公钥!需要联系建行生活技术支持获取
* 获取方式: 联系建行生活平台运营人员或技术支持 * 获取方式: 联系建行生活平台运营人员或技术支持
* 格式: PEM格式RSA公钥(2048) * 格式: PEM格式RSA公钥(2048)
* *

View File

@ -54,13 +54,13 @@ class CcbHttpClient
Log::info('建行生活API原始请求报文 [txCode=' . $txCode . '] [txSeq=' . $txSeq . ']'); Log::info('建行生活API原始请求报文 [txCode=' . $txCode . '] [txSeq=' . $txSeq . ']');
Log::info('原始报文内容: ' . $message); Log::info('原始报文内容: ' . $message);
// 加密报文 // 使用服务方公钥加密报文
$encryptedMessage = CcbRSA::encryptForCcb($message, $this->config['public_key']); $encryptedMessage = CcbRSA::encryptForCcb($message, $this->config['public_key']);
// 移除BASE64中的换行符 // 移除BASE64中的换行符
$encryptedMessage = str_replace(["\r", "\n", "\r\n"], '', $encryptedMessage); $encryptedMessage = str_replace(["\r", "\n", "\r\n"], '', $encryptedMessage);
// 生成签名 // 使用服务方私钥签名
$mac = CcbMD5::signApiMessage($message, $this->config['private_key']); $mac = CcbMD5::signApiMessage($message, $this->config['private_key']);
// 发送HTTP请求 // 发送HTTP请求
@ -118,26 +118,27 @@ class CcbHttpClient
'svcid' => $this->config['service_id'] 'svcid' => $this->config['service_id']
]; ];
// ✅ 将请求参数转为JSON格式按照建行报文示例要求
$jsonParams = json_encode($params, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
// 📝 记录请求参数(加密后的完整内容) // 📝 记录请求参数(加密后的完整内容)
Log::info('建行生活API加密请求参数 [txCode=' . $txCode . '] [url=' . $apiUrl . '] [timeout=' . self::DEFAULT_TIMEOUT . 's]'); Log::info('建行生活API加密请求 [txCode=' . $txCode . '] [url=' . $apiUrl . '] [timeout=' . self::DEFAULT_TIMEOUT . 's]');
Log::info('加密参数 cnt: ' . $cnt); Log::info('JSON请求体: ' . $jsonParams);
Log::info('加密参数 mac: ' . $mac);
Log::info('加密参数 svcid: ' . $this->config['service_id']);
// 初始化CURL // 初始化CURL
$ch = curl_init(); $ch = curl_init();
// 设置CURL选项 // 设置CURL选项按照建行要求发送JSON格式
curl_setopt_array($ch, [ curl_setopt_array($ch, [
CURLOPT_URL => $apiUrl, CURLOPT_URL => $apiUrl,
CURLOPT_POST => true, CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($params), CURLOPT_POSTFIELDS => $jsonParams, // ✅ 发送JSON格式不是URL编码
CURLOPT_RETURNTRANSFER => true, CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => self::DEFAULT_TIMEOUT, CURLOPT_TIMEOUT => self::DEFAULT_TIMEOUT,
CURLOPT_SSL_VERIFYPEER => true, CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_SSL_VERIFYHOST => 2, CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_HTTPHEADER => [ CURLOPT_HTTPHEADER => [
'Content-Type: application/x-www-form-urlencoded', 'Content-Type: application/json; charset=utf-8', // ✅ JSON格式
'Accept: application/json' 'Accept: application/json'
] ]
]); ]);

View File

@ -333,12 +333,22 @@ class CcbOrderService
$discountAmount = number_format($order['total_discount_fee'] ?? 0, 2, '.', ''); $discountAmount = number_format($order['total_discount_fee'] ?? 0, 2, '.', '');
$totalRefundAmount = number_format($order['refund_fee'] ?? 0, 2, '.', ''); $totalRefundAmount = number_format($order['refund_fee'] ?? 0, 2, '.', '');
// 处理订单时间(Shopro的createtime是毫秒时间戳需要除以1000 // 处理订单时间(自动判断秒级或毫秒级时间戳
$createTimeValue = $order['createtime'] ?? null; $createTimeValue = $order['createtime'] ?? null;
if (empty($createTimeValue) || !is_numeric($createTimeValue)) { if (empty($createTimeValue) || !is_numeric($createTimeValue)) {
$createTimeValue = time() * 1000; $createTimeValue = time(); // 使用当前秒级时间戳
} }
$orderDt = date('YmdHis', intval($createTimeValue / 1000));
// 判断时间戳类型大于9999999999说明是毫秒级13位数否则是秒级10位数
if ($createTimeValue > 9999999999) {
// 毫秒级时间戳除以1000转为秒
$timestamp = intval($createTimeValue / 1000);
} else {
// 秒级时间戳,直接使用
$timestamp = intval($createTimeValue);
}
$orderDt = date('YmdHis', $timestamp);
// 处理订单过期时间 // 处理订单过期时间
$invDt = ''; $invDt = '';

View File

@ -255,26 +255,31 @@ https://yunbusiness.ccb.com/tp_service/txCtrl/server?txcode=XX
| 字段 | 说明 | | 字段 | 说明 |
|------|------| |------|------|
| cnt | 使用服务方公钥对明文报文进行RSA加密后的BASE64编码内容 | | cnt | 使用服务方公钥对明文报文进行RSA加密后的BASE64编码内容 |
| mac | MD5(明文报文 + 服务方私钥) 的32位小写签名 | | mac | MD5(明文报文 + 服务方私钥) 的32位**大写**签名API接口使用大写 |
| svcid | 服务方编号(建行分配) | | svcid | 服务方编号(建行分配) |
**请求方式** **请求方式**
``` ```http
POST https://yunbusiness.ccb.com/tp_service/txCtrl/server?txcode=A3341TP01 POST https://yunbusiness.ccb.com/tp_service/txCtrl/server?txcode=A3341TP01
Content-Type: application/x-www-form-urlencoded Content-Type: application/json; charset=utf-8
cnt=BASE64加密内容&mac=MD5签名&svcid=服务方编号 {"cnt":"BASE64加密内容","mac":"大写MD5签名","svcid":"服务方编号"}
``` ```
**完整示例**(参考): **完整示例**(参考 - 来自建行提供的示例文件
```json ```json
{ {
"cnt": "Y2tFMDFJd2RGMGg5aFdXUGtjVVdaSmo4NHBKQzNNZE1wQTRRSXZVRlhBSWhqVEdXNE1LcE9MOXdxY0hhNUlIZndUU0RLK3NrZ1hpTytJUitpREEwSUp0bktRcWMxRG5hN1R0OEtjcUkxTUFDVE5FY2Z0b3lCeTVTaEo3cmNjSnBOUVFsSjRBR2htSzRheEhNb0p6N215eFViK1ZjeGd5WjVTTjJQcHUxQlBnZXJsQXE2Q1lrQ2VuSmZEYUxVSks5RGx2Yk9YWDlDczJiVVllYjlHSHQrUkFuYTljc2hucGhqVWNwNDgrcThNcGhQOElBL20xNVk5NG9lZEV4SXpmc0pDcDExZjFvQ0E5YkwwOWJOZjM4VHR3TkJkTmhqM3lKSVpWeWVpT0FucGhjS3JpOEs5RnlZbXlNVHF1UER3UjhmQ0p5dk5vYkNMS1BPRmQ3WFdXTVczZ29kSWpLaG5OUnhnaFA3N2txdDU3K2Rkd3hGbDgxUEdYbXJWN1ZKWDFOeXRVUFg2dWp3ZzdsUU1OSTlubU1kVE9nbHZJUHRoS205aEludFc2ZFBVTG1DUlNLNzZDc05qTUIyb1hTR2M2cHBNazMxNDJSa05KR0hvY1ZBNFUzcmc4SVk4ZFlYaTUzZmF3cHRES3pHY2JYVFI0SldRVzRNU2ZmSUxvNFpxTkY=", "cnt": "Y2tFMDFJd2RGMGg5aFdXUGtjVVdaSmo4NHBKQzNNZE1wQTRRSXZVRlhBSWhqVEdXNE1LcE9MOXdxY0hhNUlIZndUU0RLK3NrZ1hpTytJUitpREEwSUp0bktRcWMxRG5hN1R0OEtjcUkxTUFDVE5FY2Z0b3lCeTVTaEo3cmNjSnBOUVFsSjRBR2htSzRheEhNb0p6N215eFViK1ZjeGd5WjVTTjJQcHUxQlBnZXJsQXE2Q1lrQ2VuSmZEYUxVSks5RGx2Yk9YWDlDczJiVVllYjlHSHQrUkFuYTljc2hucGhqVWNwNDgrcThNcGhQOElBL20xNVk5NG9lZEV4SXpmc0pDcDExZjFvQ0E5YkwwOWJOZjM4VHR3TkJkTmhqM3lKSVpWeWVpT0FucGhjS3JpOEs5RnlZbXlNVHF1UER3UjhmQ0p5dk5vYkNMS1BPRmQ3WFdXTVczZ29kSWpLaG5OUnhnaFA3N2txdDU3K2Rkd3hGbDgxUEdYbXJWN1ZKWDFOeXRVUFg2dWp3ZzdsUU1OSTlubU1kVE9nbHZJUHRoS205aEludFc2ZFBVTG1DUlNLNzZDc05qTUIyb1hTR2M2cHBNazMxNDJSa05KR0hvY1ZBNFUzcmc4SVk4ZFlYaTUzZmF3cHRES3pHY2JYVFI0SldRVzRNU2ZmSUxvNFpxTkY=",
"mac": "947cab4dfebe59265dd28246e4465157", "mac": "947CAB4DFEBE59265DD28246E4465157",
"svcid": "YS44000009001853" "svcid": "YSTEST"
} }
``` ```
**⚠️ 重要说明**
- 请求体必须是**JSON格式**不是URL编码格式
- MAC签名必须是**32位大写MD5**API接口
- Content-Type必须设置为`application/json; charset=utf-8`
--- ---
### 3.2 A3341TP02 - 服务方订单更新 ### 3.2 A3341TP02 - 服务方订单更新