self::$appid, 'cusid' => self::$cusid, 'version'=>11, 'trxamt'=>$orderInfo['true_price'],//交易金额 'reqsn'=> $orderInfo['show'],//订单号 'paytype'=>'W06',//交易方式 W06小程序 'randomstr'=>bin2hex(random_bytes(32 / 2)), 'body'=>'微信小程序体检订单',//订单标题 'notify_url'=>env('APP_URL') . '/api/Pay/TongLian/callback', //'sub_appid'=>env('WX_APP_ID'), 'acct'=>$orderInfo['openid'],//openid 'signtype' => 'RSA', // 'sign' => '', // 可有可无,下面会排除 ]; // 商户私钥(注意:需使用 PKCS#8 格式) $privateKey=$this->PrivatedKey(self::$key); $sign = $this->rsaSign($data, $privateKey, 'UTF-8'); $data['sign'] = $sign; Log::info('支付请求参数', $data); $response=Http::asForm()->post(self::$PayUrl,$data); if ($response->successful()) { $res = $response->json(); $check=self::ValidSign($res); if($check===1){ $payInfo=json_decode($res['payinfo'],true); $wc_chat_pay = new WeChatPay(); $wc_chat_pay->out_trade_no = $orderInfo['show']; $wc_chat_pay->post_data = json_encode($data, JSON_UNESCAPED_UNICODE); $wc_chat_pay->params = json_encode($payInfo, JSON_UNESCAPED_UNICODE); $wc_chat_pay->save(); return $payInfo; }else{ Yo::error_echo(999999, ['验证接口返回签名失败']); } }else{ Yo::error_echo(999999, ['请求失败']); } } public function PayCheck($orderNumber) { $data = [ 'appid' => self::$appid, 'cusid' => self::$cusid, 'version'=>11, 'reqsn'=> $orderNumber,//订单号 'randomstr'=>bin2hex(random_bytes(32 / 2)), 'signtype' => 'RSA', // 'sign' => '', // 可有可无,下面会排除 ]; $privateKey=$this->PrivatedKey(self::$key); $sign = $this->rsaSign($data, $privateKey, 'UTF-8'); $data['sign'] = $sign; $response=Http::asForm()->post(self::$PayCheckUrl,$data); if ($response->successful()) { $res = $response->json(); $check=self::ValidSign($res); if($check===1){ return $res; }else{ Yo::error_echo(999999, ['验证接口返回签名失败']); } } else{ Yo::error_echo(999999, ['请求失败']); } } public function CallBack($inBodyArray) { Log::info('通联支付回调参数:', $inBodyArray); if(!empty($inBodyArray)){ $check=self::ValidSign($inBodyArray); if($check===1){ return ['status'=>true,'body'=>$inBodyArray]; }else{ Log::info('通联支付回调参数,验签失败'); return ['status'=>false,'msg'=>'验签失败']; } } } public function Refund($orderInfo) { $data = [ 'appid' => self::$appid, 'cusid' => self::$cusid, 'version'=>11, 'reqsn'=> $orderInfo['show'],//订单号 'randomstr'=>bin2hex(random_bytes(32 / 2)), 'trxamt'=>$orderInfo['true_price'],//交易金额 'oldtrxid'=>$orderInfo['transaction'],//原交易流水号 'signtype' => 'RSA', // 'sign' => '', // 可有可无,下面会排除 ]; $privateKey=$this->PrivatedKey(self::$key); $sign = $this->rsaSign($data, $privateKey, 'UTF-8'); $data['sign'] = $sign; $response=Http::asForm()->post(self::$RefundUrl,$data); if ($response->successful()) { $res = $response->json(); $check=self::ValidSign($res); if($check===1){ return $res; }else{ Yo::error_echo(999999, ['退款失败,验证接口返回签名失败']); } }else{ Yo::error_echo(999999, ['请求失败']); } } /** * RSA with SHA1 签名 * * @param array $data 要签名的参数(自动排除空值和 sign 字段) * @param string $privateKey RSA私钥(PEM格式) * @param string $charset 字符编码 * @return string Base64编码的签名值 */ private function rsaSign(array $data, string $privateKey, string $charset = 'UTF-8'): string { // 1. 过滤掉 sign 字段和空值字段 $filtered = array_filter($data, function ($value, $key) { return $key !== 'sign' && $value !== null && $value !== ''; }, ARRAY_FILTER_USE_BOTH); // 2. 按 key 的 ASCII 升序排序 ksort($filtered); // 3. 拼接成 key=value&key=value 字符串 $stringToSign = http_build_query($filtered, '', '&', PHP_QUERY_RFC3986); // 注意:http_build_query 默认使用 UTF-8,并且会 urlencode,但有些场景要求不 urlencode // 如果不需要 urlencode,使用下面方式: // 手动拼接(不进行 urlencode) $pairs = []; foreach ($filtered as $key => $value) { $pairs[] = "$key=$value"; } $stringToSign = implode('&', $pairs); // 转为指定编码 $stringToSign = mb_convert_encoding($stringToSign, $charset, 'auto'); // 4. 使用 openssl_sign 进行 SHA1WithRSA 签名 $res = openssl_pkey_get_private($privateKey); if (!$res) { throw new \Exception('Invalid private key.'); } openssl_sign($stringToSign, $signature, $res, OPENSSL_ALGO_SHA1); openssl_free_key($res); // 5. Base64 编码 return base64_encode($signature); } //验签 public static function ValidSign(array $array){ Log::info('验签:', $array); $sign =$array['sign']; unset($array['sign']); ksort($array); $bufSignSrc = self::ToUrlParams($array); $public_key='MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCm9OV6zH5DYH/ZnAVYHscEELdCNfNTHGuBv1nYYEY9FrOzE0/4kLl9f7Y9dkWHlc2ocDwbrFSm0Vqz0q2rJPxXUYBCQl5yW3jzuKSXif7q1yOwkFVtJXvuhf5WRy+1X5FOFoMvS7538No0RpnLzmNi3ktmiqmhpcY/1pmt20FHQQIDAQAB'; $public_key = chunk_split($public_key , 64, "\n"); $key = "-----BEGIN PUBLIC KEY-----\n$public_key-----END PUBLIC KEY-----\n"; $result= openssl_verify($bufSignSrc,base64_decode($sign), $key ); return $result; } public static function ToUrlParams(array $array) { $buff = ""; foreach ($array as $k => $v) { if($v != "" && !is_array($v)){ $buff .= $k . "=" . $v . "&"; } } $buff = trim($buff, "&"); return $buff; } }