以前一直使用v2,格式是xml格式,但是微信最新推出v3之后,只有以前的老用户才能用v2,而新用户只能用v3。阅读了一下文档,发现v3采用的是json格式和以前的xml大有不同。官方的demo又不清不白,用的是guzzle(关键我不会啊,研究又要太久时间,项目赶,没办法只有自己编写一个demo了,希望可以帮助大家)
首先创建一个文件和类,如WxPayv3.php,请勿直接复制,因为可以看到这个类继承了另一个类,功能有请求,加密,解密,我的环境为Tp6.0+,所以3.2的用户需要做下小小的修改。
WxPayv3.php如下
<?php namespace wx; use wx\WxExtend; class WxPayv3 extends WxExtend{ /** * # +======================================================================== * # | - @name 执行 * # | - @author cq <just_leaf@foxmail.com> * # | - @copyright zmtek 2021-01-20 * # +======================================================================== */ public function execute($url,$body = null,$http_method = 'POST') { # 转化大写 $http_method = strtoupper($http_method); if($http_method == 'POST') { $body = json_encode($body); }else{ $url = $url . '?' . http_build_query($body); $body = null; } # 分割符号 $sign = $this -> getV3Sign($url,$http_method,$body); $header[] = 'User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.108 Safari/537.36'; $header[] = 'Accept: application/json'; $header[] = 'Content-Type: application/json;'; $header[] = 'Authorization: WECHATPAY2-SHA256-RSA2048 '.$sign; $exc = $this -> Curl($url,$body,$http_method,$header); return $exc; } /** * # +======================================================================== * # | - @name 调起小程序支付的签名 * # | - @author cq <just_leaf@foxmail.com> * # | - @copyright zmtek 2021-01-20 * # +======================================================================== */ function getWechartSign($data){ if(!isset($data['appid']) || $data['appid'] == '') {echo 'appid 不能为空';die;} if(!isset($data['timeStamp']) || $data['timeStamp'] == '') {echo 'timeStamp 不能为空';die;} if(!isset($data['package']) || $data['package'] == '') {echo 'package 不能为空';die;} if(!isset($data['nonceStr']) || $data['nonceStr'] == '') {echo 'nonceStr 不能为空';die;} $str = $data['appid']."\n".$data['timeStamp']."\n".$data['nonceStr']."\n".$data['package']."\n"; $str = $this -> getSha256WithRSA($str); return $str; } /** * # +======================================================================== * # | - @name 计算签名 * # | - @author cq <just_leaf@foxmail.com> * # | - @copyright zmtek 2021-01-20 * # +======================================================================== */ function getSha256WithRSA($content){ $privateKey = file_get_contents($this -> SSLKEY_PATH); $binary_signature = ""; $algo = "SHA256"; openssl_sign($content, $binary_signature, $privateKey, $algo); $sign = base64_encode($binary_signature); return $sign; } /** * # +======================================================================== * # | - @name 请求 * # | - @author cq <just_leaf@foxmail.com> * # | - @copyright zmtek 2021-01-20 * # +======================================================================== */ public function Curl($url, $data, $http_method,$header = array(), $timeout = 30) { $opts = array( CURLOPT_TIMEOUT => $timeout, CURLOPT_RETURNTRANSFER => 1, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_HTTPHEADER => $header, CURLOPT_URL => $url, CURLOPT_HEADER => 1 , ); # 根据请求类型设置特定参数 if($http_method == 'POST') { # 判断是否传输文件 $opts[CURLOPT_POST] = 1; $opts[CURLOPT_POSTFIELDS] = $data; } # 初始化并执行curl请求 $ch = curl_init(); curl_setopt_array($ch, $opts); $data = curl_exec($ch); $error = curl_error($ch); $httpCode = curl_getinfo($ch,CURLINFO_HTTP_CODE); curl_close($ch); if($error) throw new Exception('请求发生错误:' . $error); list($headers, $body) = explode("\r\n\r\n", $data, 2); $result['data'] = json_decode($body,true); $result['httpcode'] = $httpCode; $result['headers'] = explode("\r\n", $headers); return $result; } /** * # +======================================================================== * # | - @name 签名 * # | - @author cq <just_leaf@foxmail.com> * # | - @copyright zmtek 2021-01-20 * # +======================================================================== */ public function getV3Sign($url,$http_method,$body) { # 商户号 $merchant_id = $this -> MCHID; # 随机字符串 $nonce = strtoupper($this -> getNoncestr()); # 商户序列号 $serial_no = $this -> merchantSerialNumber; # 时间搓 $timestamp = time(); # url $url_parts = parse_url($url); # 获取绝对路径 $canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : "")); # 密钥key $private_key = $this -> getPrivateKey($this -> SSLKEY_PATH); # 拼接参数 $message = $http_method."\n". $canonical_url."\n". $timestamp."\n". $nonce."\n". $body."\n"; # 获取签名 openssl_sign($message, $raw_sign, $private_key, 'sha256WithRSAEncryption'); $sign = base64_encode($raw_sign); $token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"', $merchant_id, $nonce, $timestamp, $serial_no, $sign); return $token; } /** * # +======================================================================== * # | - @name 获取私钥 * # | - @author cq <just_leaf@foxmail.com> * # | - @copyright zmtek 2019-12-26 * # +======================================================================== */ public function getPrivateKey($filepath) { return openssl_get_privatekey(file_get_contents($filepath)); } /** * # +======================================================================== * # | - @name 获取随机数 * # | - @author cq <just_leaf@foxmail.com> * # | - @copyright zmtek 2021-01-20 * # +======================================================================== */ public function getNoncestr($length = 32) { $chars = "abcdefghijklmnopqrstuvwxyz0123456789"; $str =""; for ( $i = 0; $i < $length; $i++ ) { $str.= substr($chars, mt_rand(0, strlen($chars)-1), 1); } return $str; } /** * # +======================================================================== * # | - @name 解密 * # | - @author cq <just_leaf@foxmail.com> * # | - @copyright zmtek 2021-01-20 * # +======================================================================== */ public function decryptToString($associatedData, $nonceStr, $ciphertext) { $aesKey = $this -> v3key; $ciphertext = \base64_decode($ciphertext , true); if (strlen($ciphertext) <= 16) { return false; } // ext-sodium (default installed on >= PHP 7.2) if(function_exists('\sodium_crypto_aead_aes256gcm_is_available') && \sodium_crypto_aead_aes256gcm_is_available() ){ return \sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $aesKey); } // ext-libsodium (need install libsodium-php 1.x via pecl) if(function_exists('\Sodium\crypto_aead_aes256gcm_is_available') && \Sodium\crypto_aead_aes256gcm_is_available()){ return \Sodium\crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $aesKey); } // PHP >= 7.1 if(PHP_VERSION_ID >= 70100 && in_array('aes-256-gcm', \openssl_get_cipher_methods()) ){ $ctext = substr($ciphertext, 0, -16); $authTag = substr($ciphertext, -16); return \openssl_decrypt($ctext, 'aes-256-gcm', $aesKey, \OPENSSL_RAW_DATA, $nonceStr,$authTag, $associatedData); } throw new \RuntimeException('AEAD_AES_256_GCM需要PHP 7.1以上或者安装libsodium-php'); } }
WxExtend.php如下
<?php namespace wx; class WxExtend { //=======【基本信息设置】===================================== //微信公众号身份的唯一标识。审核通过后,在微信发送的邮件中查看 public $APPID; //受理商ID,身份标识 public $MCHID; //商户支付密钥Key。审核通过后,在微信发送的邮件中查看 public $KEY; //微信v3 public $v3key; //JSAPI接口中获取openid,审核后在公众平台开启开发模式后可查看 public $APPSECRET; //=======【证书路径设置】===================================== //证书路径,注意应该填写绝对路径 public $SSLCERT_PATH; public $SSLKEY_PATH; //商户API证书序列号 public $merchantSerialNumber; //小程序appid public $appletappid; //小程序密钥 public $appletsecret; //=======【curl超时设置】=================================== //本例程通过curl使用HTTP POST方法,此处可修改其超时时间,默认为30秒 public $CURL_TIMEOUT = 30; //服务service_id,微信分特有 public $service_id; function __construct() { //小程序appid $this->appletappid ='000000'; //小程序密钥 $this->appletsecret ='000'; //微信公众号身份的唯一标识。审核通过后,在微信发送的邮件中查看 $this->APPID = '000' ; //JSAPI接口中获取openid,审核后在公众平台开启开发模式后可查看 $this->APPSECRET = '000' ; //受理商ID,身份标识 $this->MCHID = '000' ; //商户支付密钥Key。审核通过后,在微信发送的邮件中查看 $this->KEY = '000'; $this->v3key = '000'; //商户API证书序列号 $this->merchantSerialNumber = '000'; //服务service_id,微信分特有 $this->service_id = '000'; //=======【证书路径设置】===================================== //证书路径,注意应该填写绝对路径 $this->SSLCERT_PATH = app()->getRootPath().'extend/wx/cacert/apiclient_cert.pem' ; $this->SSLKEY_PATH = app()->getRootPath().'extend/wx/cacert/apiclient_key.pem'; } }
然后再new再调用就完事了
/** * # +======================================================================== * # | - @name 查询用户 * # | - @author cq <just_leaf@foxmail.com> * # | - @copyright zmtek 2020-11-24 * # +------------------------------------------------------------------------ * # | - 1.msg * # +======================================================================== */ public function permissions_openid(WxPayv3 $WxPayv3,WxExtend $WxExtend) { $url = 'https://api.mch.weixin.qq.com/v3/payscore/permissions/openid/xxxx'; $authorization_code = $WxPayv3 -> getNoncestr(); $data = array( 'appid' => $WxExtend -> appletappid , 'service_id' => $WxExtend -> service_id, ); $result = $WxPayv3 -> execute($url,$data,'GET'); pf($result); // $data = json_decode($result['data'],true); // return $this -> success($data); }
好,我们来看调用结果
啊呀,好家伙,是不是我这个方法有问题呢?结果赶紧看下官方文档,原来是微信那边挂了。不信?看下图
好啦,之后就可以愉快的调用任何接口了。