'1000140001',// 微信小程序 // '' => '1000100003',// 付款码-银联-借记 // '' => '1000100003',// 付款码-银联-贷记 // '' => '1000100002',// 付款码-支付宝 // '' => '1000110003',// 扫码-银联-借记 // '' => '1000110003',// 扫码-银联-贷记 // '' => '1000340005',// 分账 // '' => '1000110001',// 扫码-微信 'chanpay.merchanttrade.appletpay.cashier' => '1000160001',// 微信小程序收银台 // '' => '1000160002',// 支付宝小程序收银台 ]; // 畅捷公钥 RSA private string $publicKey = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQedhgpTUxuWDof5uiHWszPP/462Q/ORos8uV11g8nYmchcyE3x8jOXE5ZKRAua5HUDNkLQG/8wu4+tPVicKe3r9DOefZf+zxZgHB/uddA+Sl54h9BzjPB8RscBtDM2DWRZhdnVu4LMRb9Lp9eRYTLbviPazCEm13FqttHT35oAQIDAQAB'; // 畅捷公钥 RSA2 private string $publicKeyTwo = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsNU7PbTNvy16IF5sjzMxS3wQo0kvkq3iNmIIbcLnhatlbdeqdTG+P4wnSM+28Eums/izjL2TKdshoPcuvZ18/rGOUprCeeL7VfTtKu/mlS5H+FCXkdG0a0kstvjloqwRcksV2IiAiFttdB85zI4stu4NBMh4oFd5+nO8Jt7JMPI1J/4zd6VGn4bJ6DEcKVfXn+xpZ9fp0LRRREB5AbqJKpSLS7G+cQE1YT9kRHnAvCZcE+WPwwv24lIOiEXcNacz1vuDC4kZf3ahweuHAGlWve00/O9iIzSkB0FazUGLjOf6WcQiugEz1DtL8wKW2OlAwuk8VU3sSuLbDmzLdYG84QIDAQAB'; // 配置信息 private $config; public function __construct($siteId) { // 配置获取 $this->config = (new Config())->getConfig($siteId); } /** * Common: 请求发送 * Author: wu-hui * Time: 2024/07/23 13:32 * @param string $method * @param array $bizContent * @param string $apiLink * @return array */ private function requestApi(string $method, array $bizContent, string $apiLink = '') { try { // 判断:如果不存在指定请求地址 使用默认地址 if (!$apiLink) $apiLink = $this->apiLink; // 处理请求内容 $bizContent = $this->handleBizContent($bizContent); // 请求数组 $data = [ 'method' => $method,// 请求接口 'acq_sp_id' => $this->institutionNumber,// 合作机构号,由畅捷支付分配 'product_code' => $this->productCode,// 营销产品编码,由畅捷支付分配 'function_code' => $this->functionCode[$method],// 基础产品编码,由畅捷支付分配 商户管理-商户维护-商户详情 'format' => 'JSON', 'charset' => 'UTF-8', 'sign_type' => 'RSA2', 'timestamp' => date("YmdHis"), 'version' => '1.0', 'biz_content' => $bizContent ]; var_dump($apiLink, $data); return 1111; $data['sign'] = $this->getSign($data); // 发起请求 $result = curlPost($apiLink, $data); // 结果处理 if ($result['sub_code'] == 'FAIL') { throw new ValidateException($result['sub_msg']); } else { // 返回成功 信息解密 $content = $result['content'] ?? []; if ($content) $content = $this->handleResultContent($content); return $this->success($content); } } catch (\Exception $e) { trace(['msg' => $e->getMessage()], '好收银 - 接口请求错误'); return $this->error([], $e->getMessage()); } } /** * Common: 请求数据主要参数加密 * Author: wu-hui * Time: 2024/07/22 14:44 * @param $bizContent * @return string */ private function handleBizContent($bizContent) { // 基本参数 $bizContentJson = json_encode($bizContent); $publicKey = "-----BEGIN PUBLIC KEY-----\n" . trim($this->publicKeyTwo) . "\n-----END PUBLIC KEY-----"; // 获取秘钥详细信息 if (strlen($bizContentJson) > 255) { // 加密每段最大245,解密每段最大256 $bizContentArray = mb_str_split($bizContentJson, 245, 'UTF-8'); $useBizContent = ''; foreach ($bizContentArray as $bizContentString) { openssl_public_encrypt($bizContentString, $encryptedChunk, $publicKey); $useBizContent .= $encryptedChunk; } } else { openssl_public_encrypt($bizContentJson, $useBizContent, $publicKey); } return base64_encode($useBizContent); } /** * Common: 生成签名 * Author: wu-hui * Time: 2024/07/22 16:04 * @param $data * @return string */ private function getSign($data) { // 请求参数转字符串 $signStr = 'method' . '=' . $data['method'] . '&'; $signStr .= 'acq_sp_id' . '=' . $this->institutionNumber . '&'; $signStr .= 'function_code' . '=' . $data['function_code'] . '&'; $signStr .= 'product_code' . '=' . $data['product_code'] . '&'; $signStr .= 'biz_content' . '=' . $data['biz_content']; $signStr = trim($signStr, '&'); // 生成签名 $privateKey = "-----BEGIN RSA PRIVATE KEY-----\n" . $this->config['private_key'] . "\n-----END RSA PRIVATE KEY-----"; $key = openssl_get_privatekey($privateKey); openssl_sign($signStr, $signature, $key, "SHA256"); return base64_encode($signature); } /** * Common: 返回数据解密 * Author: wu-hui * Time: 2024/07/23 11:51 * @param $content * @return array|mixed */ public function handleResultContent($content) { // 私钥信息 $privateKey = "-----BEGIN RSA PRIVATE KEY-----\n" . $this->config['private_key'] . "\n-----END RSA PRIVATE KEY-----"; $privateKey = openssl_get_privatekey($privateKey); // 信息解密 $bizContentArray = str_split(base64_decode($content), 256); $content = ''; foreach ($bizContentArray as $bizContentString) { openssl_private_decrypt($bizContentString, $decrypted, $privateKey, OPENSSL_PKCS1_PADDING); $content .= $decrypted; } return is_json($content) ? json_decode($content, true) : []; } // 异步通知 签名验证 public function signValidate($params) { // 是否开启收银台 $isUseCashier = $this->config['is_isppay'] ?? 0; $data = [ 'function_code' => $params['function_code'], 'product_code' => $params['product_code'], 'biz_content' => $params['biz_content'], ]; if ($isUseCashier == 1) $data['method'] = 'chanpay.merchanttrade.appletpay.cashier'; else $data['method'] = 'chanpay.merchanttrade.weixin.appletpay'; $sign = $this->getSign($data); // debug([ // 'sign' => $sign, // 'noti' => $params['sign'] // ]); return true;//$sign == $params['sign']; } /** * Common: 微信小程序支付 - 创建支付 * Author: wu-hui * Time: 2024/07/23 13:31 * @param $params * @return array */ public function getWeAppPayInfo($params) { $isUseCashier = $this->config['is_cashier'] ?? 0; // 小程序支付 公共请求参数生成 $outTradeNo = $params['out_trade_no'] ?? ''; $outTradeNo = $outTradeNo . '_' . rand(1000, 9999); $bizContent = [ 'merchant_id' => $this->config['mch_id'],// 商户编号,由畅捷支付分配 'out_trade_no' => $outTradeNo,// 商户订单号,商户系统的内部订单号。5~32个字符、可包含字母、数字、下划线商户系统需保证不重复 // 'total_amount' => (int)sprintf("%.0f", $params['pay_money'] * 100),// 订单总金额,单位为分,取值范围[1,10000000000],不允许包含任何文字、符号 'total_amount' => 10,// 订单总金额,单位为分,取值范围[1,10000000000],不允许包含任何文字、符号 'body' => $params['pay_body'],// 商品描述,订单的标题信息,鉴于风控等因素,建议该字段严格按照规范传递,可参见【参数规定】 // 'goods_tag' => '',// 订单优惠标记,代金券或立减优惠功能的参数 // 'attach' => '',// 订单附加数据,该字段主要用于商户携带订单的自定义数据,在查询API和支付通知中原样返回 // 'limit_credit_pay' => '',// 是否限制信用卡,限定用户使用时能否使用信用卡,值为1=禁用信用卡;值为0或者不传此参数则不禁用 'mch_create_ip' => $params['mch_create_ip']??"127.0.0.1",// 终端IP,支持IPV4和IPV6两种格式的IP地址。调用API的机器IP // 'device_info' => '',// 终端序列号,收银设备的唯一产品序列号(SN) ,多用于鉴别交易场景、风控合规等因素 // 'op_shop_id' => '',// 门店编号,由商户自定义,若有门店则必传 // 'op_user_id' => '',// 收银员,操作员帐号,或者默认为商户号 // 'need_receipt' => '',// 电子发票【微信】,需要和微信公众平台的发票功能联合,传入Y时,微信支付成功消息和支付详情页将出现开票入口 // 'time_start' => '',// 订单生成时间,格式为yyyymmddhhmmss,该时间取自商户服务器。备注:订单生成时间与超时时间需要同时传入才会生效。 // 'time_expire' => '',// 订单超时/时间,格式为yyyymmddhhmmss,该时间取自商户服务器。备注:订单生成时间与超时时间需要同时传入才会生效 'notify_url' => $params['notify_url'],// 异步通知地址 ]; // 根据是否开启收银台 进行对应的操作 if ($isUseCashier == 1) { // 启用收银台 $result = $this->requestApi('chanpay.merchanttrade.appletpay.cashier', $bizContent); return $result; $result['data']['is_cashier'] = 1; } else { // 未启用收银台 $weAppConfig = (new weAppConfig())->getWeappConfig($params['site_id'])['data']['value']; // 参数补齐 $bizContent['sub_openid'] = $params['sub_openid'];// 子用户标识,此参数为微信用户在商户对应sub_appid下的唯一标识 $bizContent['sub_appid'] = $weAppConfig['appid'] ?? '';// 子用户标识,此参数为微信用户在商户对应sub_appid下的唯一标识 $result = $this->requestApi('chanpay.merchanttrade.weixin.appletpay', $bizContent); $result['data']['is_cashier'] = 0; } return $result ?? []; } }