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;
|
||||
/**
|
||||
* 建行生活加密解密核心类
|
||||
*
|
||||
* 功能:
|
||||
* - RSA加密与解密
|
||||
* - MD5签名生成与验证
|
||||
* - 报文构造与解析
|
||||
* - 交易流水号生成
|
||||
* ⚠️ 已废弃:请使用 CcbRSA、CcbMD5、CcbHttpClient 类替代
|
||||
*
|
||||
* 废弃原因:
|
||||
* 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
|
||||
* @date 2025-01-16
|
||||
*/
|
||||
|
||||
@ -34,50 +34,13 @@ class CcbOrderService
|
||||
throw new \Exception('建行生活配置文件不存在');
|
||||
}
|
||||
|
||||
// 处理BASE64格式的密钥
|
||||
$this->config = $this->processPemKeys($this->config);
|
||||
// ✅ 修复: 删除processPemKeys()调用
|
||||
// 密钥格式化统一由CcbRSA类处理,避免重复格式化导致OpenSSL ASN1解析错误
|
||||
// CcbRSA::formatPublicKey/formatPrivateKey 会在加密/解密时自动处理密钥格式
|
||||
|
||||
$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')
|
||||
->where('order_id', $orderId)
|
||||
->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'];
|
||||
->select();
|
||||
|
||||
// 构建订单数据(符合A3341TP01接口规范)
|
||||
$orderData = $this->buildOrderData($order, $orderItems, $ccbUserId, $payFlowId);
|
||||
$orderData = $this->buildOrderData($order, $orderItems, $ccbUserId);
|
||||
|
||||
// 记录请求数据(同步日志)
|
||||
$txSeq = CcbMD5::generateTransactionSeq();
|
||||
@ -355,10 +305,9 @@ class CcbOrderService
|
||||
* @param array $order 订单数组
|
||||
* @param array $orderItems 订单商品列表
|
||||
* @param string $ccbUserId 建行用户ID
|
||||
* @param string $payFlowId 支付流水号(从shopro_pay表获取)
|
||||
* @return array
|
||||
*/
|
||||
private function buildOrderData($order, $orderItems, $ccbUserId, $payFlowId)
|
||||
private function buildOrderData($order, $orderItems, $ccbUserId)
|
||||
{
|
||||
// 构建SKU商品列表(JSON字符串格式)
|
||||
$skuList = $this->buildSkuList($orderItems);
|
||||
@ -382,7 +331,7 @@ class CcbOrderService
|
||||
'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' => $payFlowId, // 支付流水号(必填!)
|
||||
'PAY_FLOW_ID' => $order['ccb_pay_flow_id'], // 支付流水号(必填!)
|
||||
'PAY_MRCH_ID' => $this->config['merchant_id'], // 支付商户号(必填!)
|
||||
'SKU_LIST' => $skuList, // 商品信息JSON字符串(必填!)
|
||||
// ========== 非必填字段 ==========
|
||||
|
||||
@ -35,56 +35,13 @@ class CcbPaymentService
|
||||
throw new \Exception('建行生活配置文件不存在');
|
||||
}
|
||||
|
||||
// 处理BASE64格式的密钥,添加PEM包装
|
||||
$this->config = $this->processPemKeys($this->config);
|
||||
// ✅ 修复: 删除processPemKeys()调用
|
||||
// 密钥格式化统一由CcbRSA类处理,避免重复格式化导致OpenSSL ASN1解析错误
|
||||
// CcbRSA::formatPublicKey/formatPrivateKey 会在加密/解密时自动处理密钥格式
|
||||
|
||||
$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调用建行收银台
|
||||
@ -155,9 +112,9 @@ class CcbPaymentService
|
||||
$platformPubKey = $this->config['public_key']; // 服务方公钥
|
||||
$mac = strtoupper(md5($signString . '&PLATFORMPUB=' . $platformPubKey));
|
||||
|
||||
// 使用RSA加密商户公钥后30位(用于ENCPUB字段)
|
||||
$encryption = new CcbEncryption($this->config);
|
||||
$encpub = $encryption->encryptMerchantPublicKeyLast30();
|
||||
// ✅ 修复:使用 CcbRSA 加密商户公钥后30位(用于ENCPUB字段)
|
||||
// 删除 CcbEncryption 类,统一使用 CcbRSA 处理密钥格式化
|
||||
$encpub = $this->encryptPublicKeyLast30();
|
||||
|
||||
// 组装最终支付串
|
||||
$finalPaymentString = $signString . '&MAC=' . $mac . '&PLATFORMID=' . $this->config['service_id'] . '&ENCPUB=' . urlencode($encpub);
|
||||
@ -247,6 +204,43 @@ class CcbPaymentService
|
||||
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;
|
||||
}
|
||||
|
||||
// 格式化为PEM格式
|
||||
// ✅ 修复: chunk_split()会在末尾添加换行符,需要用rtrim()去除
|
||||
// 否则会导致PEM格式中密钥内容和尾部之间有多余空行,OpenSSL解析失败
|
||||
$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";
|
||||
|
||||
return $pem;
|
||||
@ -166,9 +167,10 @@ class CcbRSA
|
||||
return $privateKey;
|
||||
}
|
||||
|
||||
// 格式化为PEM格式
|
||||
// ✅ 修复: chunk_split()会在末尾添加换行符,需要用rtrim()去除
|
||||
// 否则会导致PEM格式中密钥内容和尾部之间有多余空行,OpenSSL解析失败
|
||||
$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";
|
||||
|
||||
return $pem;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user