diff --git a/addons/shopro/library/ccblife/CcbPaymentService.php b/addons/shopro/library/ccblife/CcbPaymentService.php index c2b782a..d98003e 100644 --- a/addons/shopro/library/ccblife/CcbPaymentService.php +++ b/addons/shopro/library/ccblife/CcbPaymentService.php @@ -82,139 +82,178 @@ class CcbPaymentService throw new \Exception('支付流水号不能为空'); } - // ✅ 构建完整的48个支付参数(按照建行文档5.4完整参数定义) - // 基础商户参数(必须二选一:建行商户号组合 或 外部平台商户号) - $paymentParams = [ - 'MERCHANTID' => $this->config['merchant_id'], // 商户代码(F=可选,但不用外部商户号时必填) - 'POSID' => $this->config['pos_id'], // 柜台代码(F) - 'BRANCHID' => $this->config['branch_id'], // 分行代码(F) - 'ORDERID' => $payFlowId, // 支付流水号(T=必送) - 'USER_ORDERID' => $order['order_sn'], // 用户订单号(T) - 'PAYMENT' => number_format($order['pay_fee'], 2, '.', ''), // 支付金额(T) - 'CURCODE' => '01', // 币种(T,01=人民币) - 'TXCODE' => '520100', // 交易码(T,520100=即时支付) - 'REMARK1' => '', // 备注1(T) - 'REMARK2' => $this->config['service_id'], // 备注2(T,服务方编号) - 'TYPE' => '1', // 接口类型(T,1=防钓鱼) - 'GATEWAY' => '0', // 网关类型(T) - 'CLIENTIP' => $this->getClientIp(), // 客户端IP(T) - 'REGINFO' => '', // 客户注册信息(T,中文需escape编码) - 'PROINFO' => $this->buildProductInfo($order), // 商品信息(T,中文已escape编码) - 'REFERER' => '', // 商户URL(T) - 'THIRDAPPINFO' => 'comccbpay1234567890cloudmerchant', // 客户端标识(T,固定值) - 'TIMEOUT' => date('YmdHis', strtotime('+30 minutes')), // 超时时间(F,格式YYYYMMDDHHmmss) - ]; + // ⚠️ 关键:建行要求参数按照文档表格定义的顺序拼接,不是ASCII排序! + // 根据建行文档4.1和4.2,必须严格按照参数表顺序构建签名字符串 - // ✅ 可选参数(根据实际场景添加) - // 外部平台商户号(与建行商户号二选一) - if (!empty($this->config['plat_mct_id'])) { - $paymentParams['PLATMCTID'] = $this->config['plat_mct_id']; - // 使用外部商户号时,删除建行商户号 - unset($paymentParams['MERCHANTID'], $paymentParams['POSID'], $paymentParams['BRANCHID']); + // 1. 定义参与MAC签名的参数数组(按文档表格顺序) + $macParams = []; + + // 1.1 商户信息(必填,二选一:建行商户号组合 或 外部平台商户号) + $usePlatMctId = !empty($this->config['plat_mct_id']); + if ($usePlatMctId) { + // 使用外部平台商户号 + $macParams['PLATMCTID'] = $this->config['plat_mct_id']; + } else { + // 使用建行商户号组合 + $macParams['MERCHANTID'] = $this->config['merchant_id']; + $macParams['POSID'] = $this->config['pos_id']; + $macParams['BRANCHID'] = $this->config['branch_id']; } - // 微信支付19位终端号 - if (!empty($this->config['pos_id_19'])) { - $paymentParams['POSID19'] = $this->config['pos_id_19']; - } - - // 支付位图(控制支付方式:生活钱包/龙支付/微信/数币/信用付/快贷) - if (!empty($this->config['pay_bitmap'])) { - $paymentParams['PAYBITMAP'] = $this->config['pay_bitmap']; - } + // 1.2 订单信息(必填) + $macParams['ORDERID'] = $payFlowId; // 支付流水号 + $macParams['USER_ORDERID'] = $order['order_sn']; // 用户订单号 + $macParams['PAYMENT'] = number_format($order['pay_fee'], 2, '.', ''); // 支付金额 + $macParams['CURCODE'] = '01'; // 币种(01=人民币) + $macParams['TXCODE'] = '520100'; // 交易码 + $macParams['REMARK1'] = ''; // 备注1(空字符串也要传) + $macParams['REMARK2'] = $this->config['service_id']; // 备注2(服务方编号) + $macParams['TYPE'] = '1'; // 接口类型(1=防钓鱼) + $macParams['GATEWAY'] = '0'; // 网关类型 + $macParams['CLIENTIP'] = ''; // 客户端IP(建行生活环境送空) + $macParams['REGINFO'] = ''; // 客户注册信息(空字符串) + $macParams['PROINFO'] = $this->buildProductInfo($order); // 商品信息(escape编码) + $macParams['REFERER'] = ''; // 商户URL(空字符串) + $macParams['THIRDAPPINFO'] = 'comccbpay1234567890cloudmerchant'; // 客户端标识(固定值) - // 账户位图(控制支付账户:建行借记卡/贷记卡/他行借记卡/贷记卡/建行钱包) - if (!empty($this->config['account_bitmap'])) { - $paymentParams['ACCOUNTBITMAP'] = $this->config['account_bitmap']; - } + // 1.3 可选参数(按文档表格顺序,有值才参与MAC) + // ⚠️ 注意:根据文档4.2,橙色字段有值时才参与MAC,空值不参与 - // 分期期数 + // 分期期数(在THIRDAPPINFO之后) if (!empty($this->config['install_num'])) { - $paymentParams['INSTALLNUM'] = $this->config['install_num']; + $macParams['INSTALLNUM'] = $this->config['install_num']; } - // 积分二级活动编号 + // 超时时间 + if (!empty($this->config['timeout'])) { + $macParams['TIMEOUT'] = $this->config['timeout']; + } else { + // 默认30分钟超时 + $macParams['TIMEOUT'] = date('YmdHis', strtotime('+30 minutes')); + } + + // 中国建设银行App环境参数 + if (!empty($this->config['user_id'])) { + $macParams['USERID'] = $this->config['user_id']; + } + if (!empty($this->config['token'])) { + $macParams['TOKEN'] = $this->config['token']; + } + if (!empty($this->config['pay_success_url'])) { + $macParams['PAYSUCCESSURL'] = urlencode($this->config['pay_success_url']); + } + + // 支付位图和账户位图 + if (!empty($this->config['pay_bitmap'])) { + $macParams['PAYBITMAP'] = $this->config['pay_bitmap']; + } + if (!empty($this->config['account_bitmap'])) { + $macParams['ACCOUNTBITMAP'] = $this->config['account_bitmap']; + } + + // 积分相关 if (!empty($this->config['point_avy_id'])) { - $paymentParams['POINTAVYID'] = $this->config['point_avy_id']; + $macParams['POINTAVYID'] = $this->config['point_avy_id']; + } + if (!empty($this->config['fixed_point_val'])) { + $macParams['FIXEDPOINTVAL'] = $this->config['fixed_point_val']; + } + if (!empty($this->config['min_point_limit'])) { + $macParams['MINPOINTLIMIT'] = $this->config['min_point_limit']; + } + + // 有价券相关 + if (!empty($this->config['coupon_avy_id'])) { + $macParams['COUPONAVYID'] = $this->config['coupon_avy_id']; + } + if (!empty($this->config['only_credit_pay_flag'])) { + $macParams['ONLY_CREDIT_PAY_FLAG'] = $this->config['only_credit_pay_flag']; } // 数字人民币参数 if (!empty($this->config['dcep_mct_type'])) { - $paymentParams['DCEP_MCT_TYPE'] = $this->config['dcep_mct_type']; + $macParams['DCEP_MCT_TYPE'] = $this->config['dcep_mct_type']; if ($this->config['dcep_mct_type'] == '2') { // 非融合商户需要填写数币商户号 - $paymentParams['DCEP_MERCHANTID'] = $this->config['dcep_merchant_id'] ?? ''; - $paymentParams['DCEP_POSID'] = $this->config['dcep_pos_id'] ?? ''; - $paymentParams['DCEP_BRANCHID'] = $this->config['dcep_branch_id'] ?? ''; + if (!empty($this->config['dcep_merchant_id'])) { + $macParams['DCEP_MERCHANTID'] = $this->config['dcep_merchant_id']; + } + if (!empty($this->config['dcep_pos_id'])) { + $macParams['DCEP_POSID'] = $this->config['dcep_pos_id']; + } + if (!empty($this->config['dcep_branch_id'])) { + $macParams['DCEP_BRANCHID'] = $this->config['dcep_branch_id']; + } } if (!empty($this->config['dcep_dep_acc_no'])) { - $paymentParams['DCEPDEPACCNO'] = $this->config['dcep_dep_acc_no']; + $macParams['DCEPDEPACCNO'] = $this->config['dcep_dep_acc_no']; } } - // 有价券活动编号 - if (!empty($this->config['coupon_avy_id'])) { - $paymentParams['COUPONAVYID'] = $this->config['coupon_avy_id']; - } - - // 限制信用卡支付标志 - if (!empty($this->config['only_credit_pay_flag'])) { - $paymentParams['ONLY_CREDIT_PAY_FLAG'] = $this->config['only_credit_pay_flag']; - } - - // 固定抵扣积分值 - if (!empty($this->config['fixed_point_val'])) { - $paymentParams['FIXEDPOINTVAL'] = $this->config['fixed_point_val']; - } - - // 最小使用积分抵扣限制 - if (!empty($this->config['min_point_limit'])) { - $paymentParams['MINPOINTLIMIT'] = $this->config['min_point_limit']; - } - - // 扩展域(JSON格式,需encodeURI) - if (!empty($this->config['extend_params'])) { - $paymentParams['EXTENDPARAMS'] = urlencode($this->config['extend_params']); - } - - // 二级商户参数(平台类服务方使用) + // 二级商户参数 if (!empty($this->config['sub_mct_id'])) { - $paymentParams['SUB_MCT_ID'] = $this->config['sub_mct_id']; + $macParams['SUB_MCT_ID'] = $this->config['sub_mct_id']; } if (!empty($this->config['sub_mct_name'])) { - $paymentParams['SUB_MCT_NAME'] = $this->config['sub_mct_name']; + $macParams['SUB_MCT_NAME'] = $this->config['sub_mct_name']; } if (!empty($this->config['sub_mct_mcc'])) { - $paymentParams['SUB_MCT_MCC'] = $this->config['sub_mct_mcc']; + $macParams['SUB_MCT_MCC'] = $this->config['sub_mct_mcc']; + } + + // 扩展域 + if (!empty($this->config['extend_params'])) { + $macParams['EXTENDPARAMS'] = urlencode($this->config['extend_params']); + } + + // 特殊字段(中石化专用) + if (!empty($this->config['identity_code'])) { + $macParams['IDENTITYCODE'] = $this->config['identity_code']; + } + if (!empty($this->config['notify_url'])) { + $macParams['NOTIFY_URL'] = urlencode($this->config['notify_url']); + } + + // 2. 构建签名字符串(按照定义顺序拼接,不排序!) + $signString = http_build_query($macParams); + + // 3. 添加PLATFORMPUB参与MD5签名(但不作为HTTP参数传递) + $platformPubKey = $this->config['public_key']; // 服务方公钥 + $macSignString = $signString . '&PLATFORMPUB=' . $platformPubKey; + + // 4. 生成MAC签名(32位小写MD5) + $mac = strtolower(md5($macSignString)); + + // 5. 构建不参与MAC的参数 + $nonMacParams = []; + + // 微信支付19位终端号(不参与MAC校验) + if (!empty($this->config['pos_id_19'])) { + $nonMacParams['POSID19'] = $this->config['pos_id_19']; } // 场景编号(埋点使用,不参与MAC校验) if (!empty($this->config['scn_id'])) { - $paymentParams['SCNID'] = $this->config['scn_id']; + $nonMacParams['SCNID'] = $this->config['scn_id']; } if (!empty($this->config['scn_pltfrm_id'])) { - $paymentParams['SCN_PLTFRM_ID'] = $this->config['scn_pltfrm_id']; + $nonMacParams['SCN_PLTFRM_ID'] = $this->config['scn_pltfrm_id']; } - // 按ASCII排序 - ksort($paymentParams); - - // 生成签名字符串 - $signString = http_build_query($paymentParams); - - // ⚠️ 建行支付串签名规则(v2.2版本): - // 1. PLATFORMPUB字段仅参与MD5计算,不作为HTTP参数传递 - // 2. 签名 = MD5(参数字符串 + &PLATFORMPUB= + 服务方公钥内容) - // 3. 生成32位小写MD5字符串(根据建行文档5.4.1要求) - $platformPubKey = $this->config['public_key']; // 服务方公钥 - $mac = strtolower(md5($signString . '&PLATFORMPUB=' . $platformPubKey)); - - // ✅ 修复:使用 CcbRSA 加密商户公钥后30位(用于ENCPUB字段) - // 删除 CcbEncryption 类,统一使用 CcbRSA 处理密钥格式化 + // 6. 生成ENCPUB(商户公钥密文,不参与MAC校验) $encpub = $this->encryptPublicKeyLast30(); - // 组装最终支付串 - $finalPaymentString = $signString . '&MAC=' . $mac . '&PLATFORMID=' . $this->config['service_id'] . '&ENCPUB=' . urlencode($encpub); + // 7. 组装最终支付串 + // 格式:参与MAC的参数 + 不参与MAC的参数 + MAC + PLATFORMID + ENCPUB + $finalPaymentString = $signString; + + // 添加不参与MAC的参数 + if (!empty($nonMacParams)) { + $finalPaymentString .= '&' . http_build_query($nonMacParams); + } + + // 添加MAC、PLATFORMID、ENCPUB + $finalPaymentString .= '&MAC=' . $mac . '&PLATFORMID=' . $this->config['service_id'] . '&ENCPUB=' . urlencode($encpub); // 保存支付流水号到订单 Order::where('id', $orderId)->update([