PHP前端开发

分享微信支付v3版 php解密解密代码

百变鹏仔 2周前 (03-12) #前端问答
文章标签 代码

微信支付v3版本小程序支付 php签名,验签,数据解密代码分享

微信支付v3版 php解密解密代码

数据解密需要用到sodium扩展 大部分php版本需要安装

证书序列号可以在这里查看https://myssl.com/cert_decode.html

我用的php7.4版本

立即学习“PHP免费学习笔记(深入)”;

直接上代码:

//微信原生支付class Wxpay{    /*     * 支付(小程序支付)     * @param type $sn        订单编号     * @param type $money  金额     * @param type $openid  用户小程序openid     * @return type     */    public static function getPayParam($sn, $money, $openid)    {        $url = 'https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi';        $notify_url = url('/api/weixin/notify');        $data = [];        $data['appid'] = Action::config(CONFIG_WXXCX, 'app_id');        $data['mchid'] = Action::config(CONFIG_WXXCX, 'mchid'); //商户号        $data['description'] = 'xxx'; //描述?        $data['out_trade_no'] = $sn; //商户系统内部订单号        $data['time_expire'] = date('Y-m-d') . 'T' . date('H:i:s', (time() + 1800)) . '+08:00'; //订单失效时间2018-06-08T10:34:56+08:00        $data['notify_url'] = $notify_url; //异步通知接口地址        $data['amount'] = ['total' => $money * 100, 'currency' => 'CNY']; //金额        $data['payer'] = ['openid' => $openid]; //用户        $re = self::wxCurl($url, $data, 'POST');        if (!isset($re['prepay_id'])) {            api_fail('参数获取失败');        }        $result = [];        $result['appId'] = Action::config(CONFIG_WXXCX, 'app_id');        $result['timeStamp'] = (string)time();        $result['nonceStr'] = uniqid();        $result['package'] = 'prepay_id=' . $re['prepay_id'];        $result['signType'] = 'RSA';        $result['paySign'] = self::getPaySign($result);        return $result;    }    /**     * 查询订单     * @param type $sn     */    public static function select($sn, $return = false)    {        $mchid = Action::config(CONFIG_WXXCX, 'mchid'); //商户号        $url = 'https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/' . $sn . '?mchid=' . $mchid;        $re = self::wxCurl($url, [], 'GET');        if ($return) {            return $re;        }        if (isset($re['trade_state']) && $re['trade_state'] == 'SUCCESS') {            return true;        }        return false;    }    /**     * 关闭订单     * @param type $sn     */    public static function close($sn)    {        $mchid = Action::config(CONFIG_WXXCX, 'mchid'); //商户号        $url = 'https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/' . $sn . '/close';        $re = self::wxCurl($url, ['mchid'=>$mchid], 'POST');        return true;    }    /**     * 退款     * @param type $sn     */    public static function refund($order_sn,$refund_sn,$total,$refund,$msg='退款')    {        $url='https://api.mch.weixin.qq.com/v3/refund/domestic/refunds';        $data=[];        $data['notify_url']=url('ag/weixin/notify_refund');        $data['out_trade_no']=$order_sn;//订单号        $data['out_refund_no']=$refund_sn;//退款单号        $data['reason']=$msg;        $data['amount']=['refund'=>$refund*100,'total'=>$total*100,'currency'=>'CNY'];        $re = self::wxCurl($url, $data, 'POST');        return $re;    }    //请求    public static function wxCurl($url, $data = [], $method = 'GET')    {        $Authorization = self::getReSign($url, $data, $method);        $header = [            'Content-Type: application/json',            'Accept: application/json',            'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36 Edg/89.0.774.63',            'Authorization: ' . $Authorization        ];        $redata = $data ? json_encode($data) : '';        $res = reCurl($url, $redata, $header);        return $res ? json_decode($res, true) : [];    }    //后端请求签名    public static function getReSign($url, $data, $method = 'GET')    {        $url_parts = parse_url($url);        $canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));        $http_method = $method;        $timestamp = time();        $nonce = uniqid();        $body = $data ? json_encode($data) : '';        $mchid = Action::config(CONFIG_WXXCX, 'mchid'); //商户id        $serial_no = Action::config(CONFIG_WXXCX, 'serial_no'); //证书编号        $private_key = self::getPrivateKey(BASE_PATH . 'cert/apiclient_key.pem'); //商户私钥        $message = $http_method . "" .            $canonical_url . "" .            $timestamp . "" .            $nonce . "" .            $body . "";        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"', $mchid, $nonce, $timestamp, $serial_no, $sign);        return 'WECHATPAY2-SHA256-RSA2048 ' . $token;    }    //前端小程序签名    public static function getPaySign($result)    {        $private_key = self::getPrivateKey(BASE_PATH . 'cert/apiclient_key.pem'); //商户私钥        $message = $result['appId'] . "" .            $result['timeStamp'] . "" .            $result['nonceStr'] . "" .            $result['package'] . "";        openssl_sign($message, $raw_sign, $private_key, 'sha256WithRSAEncryption');        $sign = base64_encode($raw_sign);        return $sign;    }    //验证签名    public static function checkSign()    {        $header = Context::get('header');        $serial_no = $header['wechatpay-serial'] ?? ''; //微信平台序列号        $timeStamp = $header['wechatpay-timestamp'] ?? '';        $nonce = $header['wechatpay-nonce'] ?? '';        $body = Context::get('raw');        $wx_sign = $header['wechatpay-signature'] ?? '';        $wx_serial_no = Action::config(CONFIG_WXXCX, 'wx_serial_no');//保存的序列号        if (!$serial_no || $wx_serial_no != $serial_no) {            sffLog::write('签名过期');            return false;        }        $message = $timeStamp . "" .            $nonce . "" .            $body . "";                $wx_sign = base64_decode($wx_sign);        $public_key = self::getPublicKey(BASE_PATH . 'cert/wx_public_cert.pem'); //平台公钥        $res = openssl_verify($message, $wx_sign, $public_key, OPENSSL_ALGO_SHA256);        if ($res == 1) {            return true;        }        sffLog::write('验签失败');        return false;    }    //获取私钥    public static function getPrivateKey($filepath)    {        return openssl_get_privatekey(file_get_contents($filepath));    }    //获取公钥    public static function getPublicKey($filepath)    {        return openssl_pkey_get_public(file_get_contents($filepath));    }    //加密数据    public static function getEncrypt($str)    {//$str是待加密字符串         $public_key_path = BASE_PATH . 'cert/wx_public_cert.pem'; //'平台证书路径';        $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;    }    //解密数据    public static function decryptToString($ciphertext, $associatedData, $nonceStr)    {        $aesKey = Action::config(CONFIG_WXXCX, 'mch_keyv3'); //商户apiv3密钥解密        $str = base64_decode($ciphertext);        if (strlen($str) = PHP 7.2)        return sodium_crypto_aead_aes256gcm_decrypt($str, $associatedData, $nonceStr, $aesKey);    }    //下载平台证书    public static function downCert()    {        $url = 'https://api.mch.weixin.qq.com/v3/certificates';        $re = self::wxCurl($url, [], 'GET');        if (!isset($re['data'])) {            api_fail('获取证书失败');        }        $ciphertext = $re['data'][0]['encrypt_certificate']['ciphertext'];        $associatedData = $re['data'][0]['encrypt_certificate']['associated_data'];        $nonceStr = $re['data'][0]['encrypt_certificate']['nonce'];        $data = self::decryptToString($ciphertext, $associatedData, $nonceStr);        if (!$data) {            api_fail('获取证书解密失败');        }        file_put_contents(BASE_PATH . '/cert/wx_public_cert.pem', $data);        return $data;    }}