zoukankan      html  css  js  c++  java
  • php微信支付v3版签名生成,代金券、微信支付分、支付即服务等

    对接微信新版SDKv3版

    签名生成规则,微信的官方文档里面说明的还算可以吧,不过个人觉得不太理想- -。  自己调试的时候调试了半天才找了错误原因。

    https://wechatpay-api.gitbook.io/wechatpay-api-v3

    微信v3接口更换了新版签名方式

      商户需要使用自身的私钥对API URL、消息体等关键数据的组合进行SHA-256 with RSA签名。
           请求的签名信息通过HTTP头Authorization传递,具体说明请见签名生成指南
           没有携带签名或者签名验证不通过的请求,都不会被执行,并返回401 Unauthorized
     
     
     
    官方有给提供了类库和SDK
         不过我这里是旧项目没有用着composer, 使用官方提供的话  需要改动太大,所以这里就自己做了一个简单的封装, 所以不清楚官方提供的库具体如何。
     
     
    //生成v3 Authorization
        protected function createAuthorization( $url , $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']}" : ""));
    
            //私钥地址
            $mch_private_key = $this->mch_private_key;
            //商户号
            $merchant_id = $this->mch_id;
            //当前时间戳
            $timestamp =  time();
            //随机字符串
            $nonce = $this->createNoncestr();
            //POST请求时 需要 转JSON字符串
            $body =  $this->body ;
            $message = "{$method}
    ".
                $canonical_url."
    ".
                $timestamp."
    ".
                $nonce."
    ".
                $body."
    ";
    
            //生成签名
            openssl_sign($message, $raw_sign, openssl_get_privatekey(file_get_contents($mch_private_key)), 'sha256WithRSAEncryption');
            $sign = base64_encode($raw_sign);
            //Authorization 类型  
            $schema = 'WECHATPAY2-SHA256-RSA2048';
            //生成token
            $token = sprintf('mchid="%s",serial_no="%s",nonce_str="%s",timestamp="%d",signature="%s"', $merchant_id,$this->serial_no, $nonce, $timestamp,  $sign);
    
            $header = [
                'Content-Type:application/json',
                'Accept:application/json',
                'User-Agent:*/*',
                'Authorization: '.  $schema . ' ' . $token
            ];
            return $header;
        }
    /**
         *  作用:产生随机字符串,不长于32位
         */
        public function createNoncestr( $length = 32 )
        {
            $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
            $str ="";
            for ( $i = 0; $i < $length; $i++ )  {
                $str.= substr($chars, mt_rand(0, strlen($chars)-1), 1);
            }
            return $str;
        }

        获取平台证书及平台证书序列号,加密解密,个别V3接口需要用到。

    官方地址:https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/wei-xin-zhi-fu-ping-tai-zheng-shu-geng-xin-zhi-yin
        

    /**
    * 支付及服务 - 服务人员注册
    * 1. 获取平台证书序列号 serial_no与 商户支付证书不是一个
    * 2. 解密平台证书,拿到平台证书信息
    * 3. 加密请求参数时 需要用户 平台证书进行加密
    */ public function regguide( $post ,$serial_no){ $url = "https://api.mch.weixin.qq.com/v3/smartguide/guides"; $this->setBody( $post ); //生成V3请求 header认证信息 $header = $this->createAuthorization( $url , 'POST' ); //增加平台证书序列号 , 平台证书序列号方法 getcertificates()
    $header[] = 'Wechatpay-Serial:' . $serial_no; $data = $this->postXmlCurl(json_encode($post , JSON_UNESCAPED_UNICODE) , $url , 30 , $header ); return json_decode($data , true); }
        /**
         * 获取平台证书, 与商户证书不是一个内容
         */
        public function getcertificates(){
    
            $url="https://api.mch.weixin.qq.com/v3/certificates";
    
            //生成V3请求 header认证信息
            $header = $this->createAuthorization( $url );
    
            $header[] = 'User-Agent : https://zh.wikipedia.org/wiki/User_agent';
    
            $data = $this->getXmlCurl( $url  , $this->curl_timeout , $header );
    
            return json_decode($data , true);
    
        }
       /**
         * V3加密
         */
        public function getEncrypt($str){ 
            //$str是待加密字符串 
            $public_key_path = '证书地址'; //看情况使用证书, 个别接口证书 使用的是 平台证书而不是 api证书
            $public_key = file_get_contents($public_key_path); 
            $encrypted = ''; 
            
            if (openssl_public_encrypt($str,$encrypted,$public_key,OPENSSL_PKCS1_OAEP_PADDING)) { 
                //base64编码 
                $sign = base64_encode($encrypted);
            } else {
                throw new Exception('encrypt failed');
            }
            return $sign;
        }
    
    
    /**
         * Decrypt AEAD_AES_256_GCM ciphertext  V3签名解密
         * @param stingr    $aesKey             V3签名
         * @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($aesKey ,$associatedData, $nonceStr, $ciphertext)
        {
    
            if (strlen($aesKey) != 32 ) {
                throw new InvalidArgumentException('无效的ApiV3Key,长度应为32个字节');
            }
    
            $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('Sodiumcrypto_aead_aes256gcm_is_available') && Sodiumcrypto_aead_aes256gcm_is_available()){
    
                return Sodiumcrypto_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');
        }
    注意:
         我这里缺少代码直接获取商户序列号的方法,serial_no
         可以自己查看官方文档或者 我这里是直接 获取的证书信息里面的序列号, 也可以通过  https://myssl.com/cert_decode.html   去查看证书的序列号。
         如果是GET请求方式的$url 需要是完整地址 host+path+query  。
         如果是POST等其他请求方式 时, 请求BODY需要与query参数一致,且转JSON字符串
     
  • 相关阅读:
    Cider扩展架构:AttributeTable和AttributeTableBuilder
    VS编译时自动引用Debug|Release版本的dll
    扩展Oozie
    使用正则表达式找出不包含特定字符串的条目
    重构时机和重构方法之间的对应关系
    Javascript 果然存在所谓的预编译,例证:
    程序员眼中的UML(2)克服用例图的恐惧
    Hortonworks宣布一款Hadoop数据平台
    .NET Framework 4现已支持LocalDB
    REST API用得也痛苦
  • 原文地址:https://www.cnblogs.com/inkwhite/p/11686115.html
Copyright © 2011-2022 走看看