微信 支付即服务 服务人员注册 和 AEAD_AES_256_GCM 的使用 (sodium_crypto_aead_aes256gcm_decrypt 或者 sodium_crypto_aead_aes256gcm_is_available() )无法使用 的解决方案
sodium_crypto_aead_aes256gcm_decrypt 无法使用需要安装 sodium
其实在 >=php7.2 以上的版本都自带了 这个模块 不用重新安装只需 将
extension=sodium.so 前的 ;分号去掉保存 就可以了
如果您使用的是 windows phpstudy 只需切换到 php >=7.2 勾选模块后重启即可
直接上代码
<?php class Test { public $mch_id; public $api_serial_no; public $serial_no_obj; const KEY_LENGTH_BYTE = 32; const AUTH_TAG_LENGTH_BYTE = 16; public $aesKey; public function __construct() { $this->mch_id = '16xxxxxxx6'; //商户号 $this->api_serial_no = '321xxxxxxxxxx347'; //证书的序号 $this->aesKey = 'zih3xxxxxxxxxxxxxxxxxxxxxnae'; //密钥 } /** * 获取平台证书内容 */ public function get_Certificates($type) { if ($this->serial_no_obj) { $serial_no_obj = $this->serial_no_obj; } else { $sign = $this->get_Sign("https://api.mch.weixin.qq.com/v3/certificates", "GET", "", $this->get_Privatekey(), $this->mch_id, $this->api_serial_no);//$http_method要大写 $header[] = 'User-Agent: */*'; $header[] = 'Accept: application/json'; $header[] = 'Authorization: WECHATPAY2-SHA256-RSA2048 ' . $sign; $back = $this->http_Request("https://api.mch.weixin.qq.com/v3/certificates", $header); $serial_no_obj = json_decode($back); $this->serial_no_obj = $serial_no_obj; } if ($type == 1) { $serial_no_new = $serial_no_obj->data[0]->serial_no; } else if ($type == 2) { $serial_no_new = $serial_no_obj->data[0]->encrypt_certificate; } else { return $serial_no_obj; } return $serial_no_new; } /** * 获取sign * @param $url * @param $http_method [POST GET 必读大写] * @param $body [请求报文主体(必须进行json编码)] * @param $mch_private_key [商户私钥] * @param $merchant_id [商户号] * @param $serial_no [证书编号] * @return string */ private function get_Sign($url, $http_method, $body, $mch_private_key, $merchant_id, $serial_no) { $timestamp = time();//时间戳 $nonce = $timestamp . rand(10000, 99999);//随机字符串 $url_parts = parse_url($url); $canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : "")); $message = $http_method . "\n" . $canonical_url . "\n" . $timestamp . "\n" . $nonce . "\n" . $body . "\n"; openssl_sign($message, $raw_sign, $mch_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; } /** * 读取商户私钥 * @return false|resource */ public function get_Privatekey() { $private_key_file = 'apiclient_key.pem';//私钥文件路径 如linux服务器秘钥地址地址:/www/wwwroot/...../apiclient_key.pem" $mch_private_key = openssl_get_privatekey(file_get_contents($private_key_file));//获取私钥 return $mch_private_key; } /** * 数据请求 * @param $url * @param array $header 获取头部 * @param string $post_data POST数据,不填写默认以GET方式请求 * @return bool|string */ public function http_Request($url, $header = array(), $post_data = "") { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HTTPHEADER, $header); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 2); if ($post_data != "") { curl_setopt($ch, CURLOPT_POST, TRUE); curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); //设置post提交数据 } //判断当前是不是有post数据的发 $output = curl_exec($ch); if ($output === FALSE) { $output = "curl 错误信息: " . curl_error($ch); } curl_close($ch); return $output; } /** *敏感信息加密 **/ public function getEncrypt($str) { //返回的是加密后的平台证书,需要对证书进行解密 $public_key_all = $this->get_Certificates(2); //解密证书,获取到平台证书得公钥 $public_key = $this->decryptToString($public_key_all->associated_data, $public_key_all->nonce, $public_key_all->ciphertext); $encrypted = ''; //$sign是加密后的字符串 if (openssl_public_encrypt($str, $encrypted, $public_key, OPENSSL_PKCS1_OAEP_PADDING)) { //base64编码 $sign = base64_encode($encrypted); } else { $sign = ''; } return $sign; } /** * Decrypt AEAD_AES_256_GCM ciphertext * * @param string $associatedData AES GCM additional authentication data * @param string $nonceStr AES GCM nonce * @param string $ciphertext AES GCM cipher text * * @return string|bool Decrypted string on success or FALSE on failure */ public function decryptToString($associatedData, $nonceStr, $ciphertext) { $ciphertext = \base64_decode($ciphertext); if (strlen($ciphertext) <= self::AUTH_TAG_LENGTH_BYTE) { 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, $this->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, $this->aesKey); } // openssl (PHP >= 7.1 support AEAD) if (PHP_VERSION_ID >= 70100 && in_array('aes-256-gcm', \openssl_get_cipher_methods())) { $ctext = substr($ciphertext, 0, -self::AUTH_TAG_LENGTH_BYTE); $authTag = substr($ciphertext, -self::AUTH_TAG_LENGTH_BYTE); return \openssl_decrypt($ctext, 'aes-256-gcm', $this->aesKey, \OPENSSL_RAW_DATA, $nonceStr, $authTag, $associatedData); } throw new \RuntimeException('AEAD_AES_256_GCM需要PHP 7.1以上或者安装libsodium-php'); } //生成v3 Authorization public function createAuthorization($url, $body, $method = 'GET') { if (!in_array('sha256WithRSAEncryption', \openssl_get_md_methods(true))) { throw new \RuntimeException("当前PHP环境不支持SHA256withRSA"); } $url_parts = parse_url($url); $canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : "")); //API证书私钥地址 $mch_private_key = $this->get_Privatekey(); //当前时间戳 $timestamp = time(); //随机字符串 $nonce = $timestamp . rand(10000, 99999);; //POST请求时 需要 转JSON字符串 $message = "{$method}\n" . $canonical_url . "\n" . $timestamp . "\n" . $nonce . "\n" . $body . "\n"; //生成签名 openssl_sign($message, $raw_sign, openssl_get_privatekey($mch_private_key), 'sha256WithRSAEncryption'); $sign = base64_encode($raw_sign); //生成签名信息 $token = sprintf('mchid="%s",serial_no="%s",nonce_str="%s",timestamp="%d",signature="%s"', $this->mch_id, $this->api_serial_no, $nonce, $timestamp, $sign); $header = [ 'Content-Type: application/json', 'Accept: application/json', 'User-Agent: */*', 'Authorization: WECHATPAY2-SHA256-RSA2048' . ' ' . $token, 'Wechatpay-Serial: ' . $this->get_Certificates(1) ]; return $header; } /** * 服务人员注册 * @return array */ function addWaiter() { //组合post信息,并对敏感信息加密 $register = array( "corpid" => 'wwxxxxxxxxxxxxxx72', "store_id" => 100000000, //必须int类型 "name" => $this->getEncrypt('小明明'), "mobile" => $this->getEncrypt('15xxxxxxxxx1'), "qr_code" => 'https://open.work.weixin.qq.com/wwopen/userQRCode?vcode=xxxxxxxx1', "avatar" => "https://wework.qpic.cn/wwhead/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx2qNxnJiboNbZHp7hU/0", "userid" => '15xxxxxxxxx1' ); $registerJson = json_encode($register, JSON_UNESCAPED_UNICODE); //接口API $url = 'https://api.mch.weixin.qq.com/v3/smartguide/guides'; //获取请求头(签名) $hearder = $this->createAuthorization($url, $registerJson, 'POST'); $returnData = $this->http_Request($url, $hearder, $registerJson); //返回结果 {"guide_id" : "LLA3WJ6DSZUfiaZDS79FH5Wm5m4X69TBic"} exit($returnData); } } $aa = new Test(); echo $aa->addWaiter();