mirror of
https://gitee.com/liuxioabin/fengketrade.git
synced 2026-04-17 21:03:17 +08:00
推送
This commit is contained in:
parent
a4b8b710d5
commit
184be5a1d1
@ -7,12 +7,19 @@ use think\Exception;
|
|||||||
/**
|
/**
|
||||||
* 建行生活加密解密核心类
|
* 建行生活加密解密核心类
|
||||||
*
|
*
|
||||||
* 功能:
|
* ⚠️ 已废弃:请使用 CcbRSA、CcbMD5、CcbHttpClient 类替代
|
||||||
* - RSA加密与解密
|
|
||||||
* - MD5签名生成与验证
|
|
||||||
* - 报文构造与解析
|
|
||||||
* - 交易流水号生成
|
|
||||||
*
|
*
|
||||||
|
* 废弃原因:
|
||||||
|
* 1. formatKey() 方法存在密钥格式化错误(PKCS#1 vs PKCS#8 混淆)
|
||||||
|
* 2. chunk_split() 使用不当导致 OpenSSL ASN1 解析错误
|
||||||
|
* 3. 与 CcbRSA 类功能重复,维护成本高
|
||||||
|
*
|
||||||
|
* 迁移指南:
|
||||||
|
* - RSA加密/解密 → 使用 CcbRSA::encrypt() / CcbRSA::decrypt()
|
||||||
|
* - MD5签名 → 使用 CcbMD5::signApiMessage() / CcbMD5::verifyApiSignature()
|
||||||
|
* - 加密商户公钥 → 在 CcbPaymentService 中使用 encryptPublicKeyLast30()
|
||||||
|
*
|
||||||
|
* @deprecated 2025-01-21 统一使用 CcbRSA、CcbMD5 类
|
||||||
* @author Billy
|
* @author Billy
|
||||||
* @date 2025-01-16
|
* @date 2025-01-16
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -34,50 +34,13 @@ class CcbOrderService
|
|||||||
throw new \Exception('建行生活配置文件不存在');
|
throw new \Exception('建行生活配置文件不存在');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理BASE64格式的密钥
|
// ✅ 修复: 删除processPemKeys()调用
|
||||||
$this->config = $this->processPemKeys($this->config);
|
// 密钥格式化统一由CcbRSA类处理,避免重复格式化导致OpenSSL ASN1解析错误
|
||||||
|
// CcbRSA::formatPublicKey/formatPrivateKey 会在加密/解密时自动处理密钥格式
|
||||||
|
|
||||||
$this->httpClient = new CcbHttpClient($this->config);
|
$this->httpClient = new CcbHttpClient($this->config);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理PEM格式密钥
|
|
||||||
*
|
|
||||||
* @param array $config
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function processPemKeys($config)
|
|
||||||
{
|
|
||||||
if (!empty($config['private_key']) && strpos($config['private_key'], '-----BEGIN') === false) {
|
|
||||||
$config['private_key'] = "-----BEGIN PRIVATE KEY-----\n"
|
|
||||||
. chunk_split($config['private_key'], 64, "\n")
|
|
||||||
. "-----END PRIVATE KEY-----";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($config['public_key']) && strpos($config['public_key'], '-----BEGIN') === false) {
|
|
||||||
$config['public_key'] = "-----BEGIN PUBLIC KEY-----\n"
|
|
||||||
. chunk_split($config['public_key'], 64, "\n")
|
|
||||||
. "-----END PUBLIC KEY-----";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($config['merchant_public_key'])) {
|
|
||||||
$config['merchant_public_key'] = $config['public_key'];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理平台公钥
|
|
||||||
if (!empty($config['platform_public_key'])) {
|
|
||||||
if (strpos($config['platform_public_key'], '-----BEGIN') === false) {
|
|
||||||
$config['platform_public_key'] = "-----BEGIN PUBLIC KEY-----\n"
|
|
||||||
. chunk_split($config['platform_public_key'], 64, "\n")
|
|
||||||
. "-----END PUBLIC KEY-----";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$config['platform_public_key'] = $config['public_key'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $config;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 推送订单到建行生活平台
|
* 推送订单到建行生活平台
|
||||||
* 当用户下单后调用此方法同步订单信息
|
* 当用户下单后调用此方法同步订单信息
|
||||||
@ -112,23 +75,10 @@ class CcbOrderService
|
|||||||
// 获取订单商品列表
|
// 获取订单商品列表
|
||||||
$orderItems = Db::name('shopro_order_item')
|
$orderItems = Db::name('shopro_order_item')
|
||||||
->where('order_id', $orderId)
|
->where('order_id', $orderId)
|
||||||
->select()
|
->select();
|
||||||
->toArray();
|
|
||||||
|
|
||||||
// 获取支付流水号(PAY_FLOW_ID必填字段)
|
|
||||||
$payInfo = Db::name('shopro_pay')
|
|
||||||
->where('order_id', $orderId)
|
|
||||||
->where('status', 'paid')
|
|
||||||
->find();
|
|
||||||
|
|
||||||
if (!$payInfo || empty($payInfo['pay_sn'])) {
|
|
||||||
throw new \Exception('订单未支付或支付流水号不存在');
|
|
||||||
}
|
|
||||||
|
|
||||||
$payFlowId = $payInfo['pay_sn'];
|
|
||||||
|
|
||||||
// 构建订单数据(符合A3341TP01接口规范)
|
// 构建订单数据(符合A3341TP01接口规范)
|
||||||
$orderData = $this->buildOrderData($order, $orderItems, $ccbUserId, $payFlowId);
|
$orderData = $this->buildOrderData($order, $orderItems, $ccbUserId);
|
||||||
|
|
||||||
// 记录请求数据(同步日志)
|
// 记录请求数据(同步日志)
|
||||||
$txSeq = CcbMD5::generateTransactionSeq();
|
$txSeq = CcbMD5::generateTransactionSeq();
|
||||||
@ -355,10 +305,9 @@ class CcbOrderService
|
|||||||
* @param array $order 订单数组
|
* @param array $order 订单数组
|
||||||
* @param array $orderItems 订单商品列表
|
* @param array $orderItems 订单商品列表
|
||||||
* @param string $ccbUserId 建行用户ID
|
* @param string $ccbUserId 建行用户ID
|
||||||
* @param string $payFlowId 支付流水号(从shopro_pay表获取)
|
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
private function buildOrderData($order, $orderItems, $ccbUserId, $payFlowId)
|
private function buildOrderData($order, $orderItems, $ccbUserId)
|
||||||
{
|
{
|
||||||
// 构建SKU商品列表(JSON字符串格式)
|
// 构建SKU商品列表(JSON字符串格式)
|
||||||
$skuList = $this->buildSkuList($orderItems);
|
$skuList = $this->buildSkuList($orderItems);
|
||||||
@ -382,7 +331,7 @@ class CcbOrderService
|
|||||||
'REFUND_STATUS' => $this->mapRefundStatus($order['refund_status'] ?? 0), // 退款状态
|
'REFUND_STATUS' => $this->mapRefundStatus($order['refund_status'] ?? 0), // 退款状态
|
||||||
'MCT_NM' => $this->config['merchant']['name'] ?? '商户名称', // 商户名称
|
'MCT_NM' => $this->config['merchant']['name'] ?? '商户名称', // 商户名称
|
||||||
'CUS_ORDER_URL' => $this->config['merchant']['order_detail_url'] . $order['id'], // 订单详情链接
|
'CUS_ORDER_URL' => $this->config['merchant']['order_detail_url'] . $order['id'], // 订单详情链接
|
||||||
'PAY_FLOW_ID' => $payFlowId, // 支付流水号(必填!)
|
'PAY_FLOW_ID' => $order['ccb_pay_flow_id'], // 支付流水号(必填!)
|
||||||
'PAY_MRCH_ID' => $this->config['merchant_id'], // 支付商户号(必填!)
|
'PAY_MRCH_ID' => $this->config['merchant_id'], // 支付商户号(必填!)
|
||||||
'SKU_LIST' => $skuList, // 商品信息JSON字符串(必填!)
|
'SKU_LIST' => $skuList, // 商品信息JSON字符串(必填!)
|
||||||
// ========== 非必填字段 ==========
|
// ========== 非必填字段 ==========
|
||||||
|
|||||||
@ -35,56 +35,13 @@ class CcbPaymentService
|
|||||||
throw new \Exception('建行生活配置文件不存在');
|
throw new \Exception('建行生活配置文件不存在');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理BASE64格式的密钥,添加PEM包装
|
// ✅ 修复: 删除processPemKeys()调用
|
||||||
$this->config = $this->processPemKeys($this->config);
|
// 密钥格式化统一由CcbRSA类处理,避免重复格式化导致OpenSSL ASN1解析错误
|
||||||
|
// CcbRSA::formatPublicKey/formatPrivateKey 会在加密/解密时自动处理密钥格式
|
||||||
|
|
||||||
$this->orderService = new CcbOrderService();
|
$this->orderService = new CcbOrderService();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理PEM格式密钥
|
|
||||||
* 如果密钥是BASE64格式(不含-----BEGIN-----),则添加PEM包装
|
|
||||||
*
|
|
||||||
* @param array $config 配置数组
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function processPemKeys($config)
|
|
||||||
{
|
|
||||||
// 处理私钥
|
|
||||||
if (!empty($config['private_key']) && strpos($config['private_key'], '-----BEGIN') === false) {
|
|
||||||
$config['private_key'] = "-----BEGIN PRIVATE KEY-----\n"
|
|
||||||
. chunk_split($config['private_key'], 64, "\n")
|
|
||||||
. "-----END PRIVATE KEY-----";
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理公钥
|
|
||||||
if (!empty($config['public_key']) && strpos($config['public_key'], '-----BEGIN') === false) {
|
|
||||||
$config['public_key'] = "-----BEGIN PUBLIC KEY-----\n"
|
|
||||||
. chunk_split($config['public_key'], 64, "\n")
|
|
||||||
. "-----END PUBLIC KEY-----";
|
|
||||||
}
|
|
||||||
|
|
||||||
// 兼容merchant_public_key字段
|
|
||||||
if (empty($config['merchant_public_key'])) {
|
|
||||||
$config['merchant_public_key'] = $config['public_key'];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理平台公钥
|
|
||||||
if (!empty($config['platform_public_key'])) {
|
|
||||||
// 如果有配置平台公钥且是BASE64格式,添加PEM包装
|
|
||||||
if (strpos($config['platform_public_key'], '-----BEGIN') === false) {
|
|
||||||
$config['platform_public_key'] = "-----BEGIN PUBLIC KEY-----\n"
|
|
||||||
. chunk_split($config['platform_public_key'], 64, "\n")
|
|
||||||
. "-----END PUBLIC KEY-----";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 如果没有配置平台公钥,使用商户公钥作为默认值
|
|
||||||
$config['platform_public_key'] = $config['public_key'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $config;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成建行支付串
|
* 生成建行支付串
|
||||||
* 用于前端JSBridge调用建行收银台
|
* 用于前端JSBridge调用建行收银台
|
||||||
@ -155,9 +112,9 @@ class CcbPaymentService
|
|||||||
$platformPubKey = $this->config['public_key']; // 服务方公钥
|
$platformPubKey = $this->config['public_key']; // 服务方公钥
|
||||||
$mac = strtoupper(md5($signString . '&PLATFORMPUB=' . $platformPubKey));
|
$mac = strtoupper(md5($signString . '&PLATFORMPUB=' . $platformPubKey));
|
||||||
|
|
||||||
// 使用RSA加密商户公钥后30位(用于ENCPUB字段)
|
// ✅ 修复:使用 CcbRSA 加密商户公钥后30位(用于ENCPUB字段)
|
||||||
$encryption = new CcbEncryption($this->config);
|
// 删除 CcbEncryption 类,统一使用 CcbRSA 处理密钥格式化
|
||||||
$encpub = $encryption->encryptMerchantPublicKeyLast30();
|
$encpub = $this->encryptPublicKeyLast30();
|
||||||
|
|
||||||
// 组装最终支付串
|
// 组装最终支付串
|
||||||
$finalPaymentString = $signString . '&MAC=' . $mac . '&PLATFORMID=' . $this->config['service_id'] . '&ENCPUB=' . urlencode($encpub);
|
$finalPaymentString = $signString . '&MAC=' . $mac . '&PLATFORMID=' . $this->config['service_id'] . '&ENCPUB=' . urlencode($encpub);
|
||||||
@ -247,6 +204,43 @@ class CcbPaymentService
|
|||||||
return implode(',', $orderItems);
|
return implode(',', $orderItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密商户公钥后30位(用于支付串的ENCPUB字段)
|
||||||
|
*
|
||||||
|
* 根据建行文档v2.2规范:
|
||||||
|
* "使用服务方公钥对商户公钥后30位进行RSA加密并base64后的密文"
|
||||||
|
*
|
||||||
|
* @return string BASE64编码的加密密文
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
private function encryptPublicKeyLast30()
|
||||||
|
{
|
||||||
|
$publicKey = $this->config['public_key'] ?? '';
|
||||||
|
|
||||||
|
if (empty($publicKey)) {
|
||||||
|
throw new \Exception('服务方公钥未配置');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 去除 PEM 格式的头尾和空白字符,获取纯 BASE64 内容
|
||||||
|
$publicKeyContent = str_replace([
|
||||||
|
'-----BEGIN PUBLIC KEY-----',
|
||||||
|
'-----END PUBLIC KEY-----',
|
||||||
|
'-----BEGIN RSA PUBLIC KEY-----',
|
||||||
|
'-----END RSA PUBLIC KEY-----',
|
||||||
|
"\r", "\n", " ", "\t"
|
||||||
|
], '', $publicKey);
|
||||||
|
|
||||||
|
// 取后30位
|
||||||
|
$last30Chars = substr($publicKeyContent, -30);
|
||||||
|
|
||||||
|
if (strlen($last30Chars) < 30) {
|
||||||
|
throw new \Exception('商户公钥长度不足30位');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ 使用 CcbRSA 类进行加密(统一密钥格式化逻辑)
|
||||||
|
return CcbRSA::encrypt($last30Chars, $publicKey);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理支付回调
|
* 处理支付回调
|
||||||
* 建行支付完成后的同步回调
|
* 建行支付完成后的同步回调
|
||||||
|
|||||||
@ -140,9 +140,10 @@ class CcbRSA
|
|||||||
return $publicKey;
|
return $publicKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 格式化为PEM格式
|
// ✅ 修复: chunk_split()会在末尾添加换行符,需要用rtrim()去除
|
||||||
|
// 否则会导致PEM格式中密钥内容和尾部之间有多余空行,OpenSSL解析失败
|
||||||
$pem = "-----BEGIN PUBLIC KEY-----\n";
|
$pem = "-----BEGIN PUBLIC KEY-----\n";
|
||||||
$pem .= chunk_split($publicKey, 64, "\n");
|
$pem .= rtrim(chunk_split($publicKey, 64, "\n"), "\n") . "\n";
|
||||||
$pem .= "-----END PUBLIC KEY-----\n";
|
$pem .= "-----END PUBLIC KEY-----\n";
|
||||||
|
|
||||||
return $pem;
|
return $pem;
|
||||||
@ -166,9 +167,10 @@ class CcbRSA
|
|||||||
return $privateKey;
|
return $privateKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 格式化为PEM格式
|
// ✅ 修复: chunk_split()会在末尾添加换行符,需要用rtrim()去除
|
||||||
|
// 否则会导致PEM格式中密钥内容和尾部之间有多余空行,OpenSSL解析失败
|
||||||
$pem = "-----BEGIN RSA PRIVATE KEY-----\n";
|
$pem = "-----BEGIN RSA PRIVATE KEY-----\n";
|
||||||
$pem .= chunk_split($privateKey, 64, "\n");
|
$pem .= rtrim(chunk_split($privateKey, 64, "\n"), "\n") . "\n";
|
||||||
$pem .= "-----END RSA PRIVATE KEY-----\n";
|
$pem .= "-----END RSA PRIVATE KEY-----\n";
|
||||||
|
|
||||||
return $pem;
|
return $pem;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user