• tp3.2 企业转账到零钱


    • 不同于企业付款到零钱,转账功能更丰富化,支持批量付款,付款可以手机上再确定下
    • php5.6 7.2均可
    • action
                $res = D('WxTransToPerson')->tixian($trade_no,$openid,(int)$flag['withdraw_money']);
                
                if($res['num'] <= 0){
                    $maindb->rollback();
                    $this->json->err('微信打款出错-' . $res['desc']);    
                }
    
    
    • WxTransToPersonModel
    <?php
    
    namespace Common\Model;
    use Think\Model;
    
    use Vendor\Func\Http;
    
    /**
     * 小程序之企业转账到零钱
     */
    class WxTransToPersonModel extends Model
    {
    
        //其中 wx_ciphertext 方法是获取平台证书和平台证书序列号使用,获取平台证书需要安装php7.1以上
        /***
         * out_trade_no 订单号
         * money 支付金额,单位:分
         */
        public function tixian($out_trade_no,$openid,$money){
            setlog([$out_trade_no,$openid,$money],[],'','trans_start.log');
    
            if(!$out_trade_no || !$openid){
                $result_arr = [
                    'num'     =>      -999,
                    'desc'   =>      '缺少参数',
                ];
    
                return $result_arr;
            }
    
            if($money < 30){
                $result_arr = [
                    'num'     =>      -99,
                    'desc'   =>      '最低0.3元',
                ];
    
                return $result_arr;
            }
    
            if(10000 < $money){
                $result_arr = [
                    'num'     =>      -939,
                    'desc'   =>      '最多一次100元',
                ];
    
                return $result_arr;
            }
    
            //查询用户授权记录
            Vendor('wxpay3.WxPayJsApiPay');
            $input = new \JsApiPay();
            $url='https://api.mch.weixin.qq.com/v3/transfer/batches';
    
            $body['appid'] = C('WX_APP_ID');
            $body['out_batch_no'] = 'Y' . $out_trade_no;// 批次单号由单个单号演变而来
            $body['batch_name'] = '用户提现';
            $body['batch_remark'] = '提现到账';
            $body['total_amount'] = $money;
            $body['total_num'] = 1;
    
            //订单风险金
            $detail = [
                'out_detail_no'     =>  $out_trade_no,
                'transfer_amount'   =>  $money,
                'transfer_remark'   =>  '用户提现到账',
                'openid'            =>  $openid,
                //'user_name'       =>  $input->getEncrypt($user_name),  //noted zb敏感信息加密
            ];
    
            $body['transfer_detail_list'][] = $detail;
            $responseData = $input->Unite($url,'POST',json_encode($body));
            $res = json_decode($responseData,true);
            setlog([$out_trade_no,$openid,$money],$res,'','trans.log');
    
            if($res['httpCode'] != 200){
                $result_arr = [
                    'num'     =>      -299,
                    'desc'   =>      '发生错误-' . $res['message'],
                ];
                return $result_arr;
            }
    
            $result_arr = [
                'num'           =>      1,
                'desc'          =>      '处理成功-',
                'batch_id'      =>      $res['batch_id'],
                'out_batch_no'  =>      $res['out_batch_no'],
            ];
    
            return $result_arr;
        }
        /*
        *array(3) {
            ["code"] => string(10) "NOT_ENOUGH"
            ["message"] => string(30) "账户余额不足,请充值"
            ["httpCode"] => int(403)
        }
        *
        array(4) {
            ["batch_id"] => string(40) "1030000005401361716282022060500845809117"// 微信明细单号
            ["create_time"] => string(25) "2022-06-05T12:54:50+08:00"
            ["out_batch_no"] => string(15) "Y20220605125450" // 微信批次单号
            ["httpCode"] => int(200)
        }
        https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter4_3_1.shtml
        */
    
    
    }
    
    
    • WxPayJsApiPay.php
    <?php
    class JsApiPay
    {
        public $data = null;
        public $serial_no = '****************************************';  //商户证书序列号 和 平台证书序列号 是两回事---由函数根据参数抓取
        public $mchid = '*****';   //商户号
        public $mch_key_v3 = '*******';   //v3秘钥
    
        public function Unite($url,$http_method='GET',$body){
            $Authorization = $this->getSign($url,$http_method,$body);
            return $this->curl_request($url, $body, $http_method, $Authorization);
        }
    
        public function JsapiSign($prepay_id,$appid,$timestamp,$nonce){
            $mch_private_key = file_get_contents('./apiclient_key.pem'); //获取密钥文件内容
            $message = $appid."\n".
            $timestamp."\n".
            $nonce."\n".
            $prepay_id."\n";
            openssl_sign($message, $raw_sign, $mch_private_key, 'sha256WithRSAEncryption');
            $sign = base64_encode($raw_sign);
            return $sign;
        }
        
        public function getSign($url,$http_method='GET',$body){
            $serial_no=$this->serial_no;
            $merchant_id=$this->mchid;
            $timestamp=time();
            $nonce=$this->getNonceStr();
            $mch_private_key = file_get_contents('./apiclient_key.pem'); //获取密钥文件内容
            $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);
            $schema = 'WECHATPAY2-SHA256-RSA2048';
            $token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
            $merchant_id, $nonce, $timestamp, $serial_no, $sign);
            return $schema.' '.$token;
        }
    
        /*验证签名*/
        public function verifysign($serial_no,$TIMESTAMP,$NONCE,$BODY,$SIGNATURE){
            $SIGNATURE = base64_decode($SIGNATURE);
            $message = $TIMESTAMP."\n".
            $NONCE."\n".
            $BODY."\n";
    
            $mch_private_key = $this->wx_ciphertext($serial_no);
            $pubkeyid = openssl_pkey_get_public($mch_private_key);
            $ok = openssl_verify($message, $SIGNATURE, $pubkeyid,OPENSSL_ALGO_SHA256);
            openssl_free_key($pubkeyid);
            if($ok == 1){
                return true;
            }else{
                return false;
            }
        }
    
        /*获取微信支付平台证书
          serial_no:平台证书序列号
        */
        public function wx_ciphertext(){
            $url = 'https://api.mch.weixin.qq.com/v3/certificates';
            $certificates = $this->Unite($url,'GET','');
            $certificates = json_decode($certificates,true);
        
            // foreach ($certificates['data'] as $key => $value) {
            //     if($value['serial_no']==$this->serial_no){
            //         $associatedData=$value['encrypt_certificate']['associated_data'];
            //         $nonceStr=$value['encrypt_certificate']['nonce'];
            //         $ciphertext=$value['encrypt_certificate']['ciphertext'];
            //         break;
            //     }
            // }
    
            $data = $certificates['data'][0]['encrypt_certificate'];
            $associatedData = $data['associated_data'];
            $nonceStr = $data['nonce'];
            $ciphertext = $data['ciphertext'];
            $AesUtil = new \AesUtil($this->mch_key_v3);
            return $AesUtil->decryptToString($associatedData, $nonceStr, $ciphertext);  //获取平台证书。复制到文本里改为pem后缀
        }
    
        /**
         * 产生随机字符串,不长于32位
         * @param int $length
         * @return 产生的随机字符串
         */
        public static 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;
        }
    
    
        /**获取平台证书序列号**/
        public static function getCertificate($filepath) {
            $cert = openssl_x509_read(file_get_contents($filepath));
            $cert_data = openssl_x509_parse($cert);
            dump($cert_data);
            array_walk($cert_data, 'print_element');
    
            // Free the resource
            openssl_x509_free($cert);
            dump($cert);
            // return openssl_x509_read(file_get_contents($filepath));  
        }
    
        /**
         * @Description: curl请求
         * @Author: Yang
         * @param $url
         * @param null $data
         * @param string $method
         * @param array $header
         * @param bool $https
         * @param int $timeout
         * @return mixed
         */
        function curl_request($url,$data=null,$method='get',$Authorization,$https=true,$timeout = 5){
            $header = array(
                'Content-Type: application/json',
                'Accept: application/json',
                'Wechatpay-Serial:******',//平台序列号
                'Authorization: '.$Authorization
            );
            
            $user_agent = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.146 Safari/537.36";
    
            $method = strtoupper($method);
            $ch = curl_init();//初始化
            curl_setopt($ch, CURLOPT_URL, $url);//访问的URL
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);//只获取页面内容,但不输出
            if($https){
                curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);//https请求 不验证证书
                curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);//https请求 不验证HOST
            }
    
            if ($method != "GET") {
                if($method == 'POST'){
                    curl_setopt($ch, CURLOPT_POST, true);//请求方式为post请求
                }
    
                if ($method == 'PUT' || strtoupper($method) == 'DELETE') {
                    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); //设置请求方式
                }
                curl_setopt($ch, CURLOPT_POSTFIELDS, $data);//请求数据
            }
    
            curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
            curl_setopt($ch, CURLOPT_HTTPHEADER, $header); //模拟的header头
            curl_setopt($ch, CURLOPT_USERAGENT,$user_agent);
            //curl_setopt($ch, CURLOPT_HEADER, false);//设置不需要头信息
            $result = curl_exec($ch);//执行请求
            $httpCode = curl_getinfo($ch,CURLINFO_HTTP_CODE);
            if($result){
    //            file_put_contents('./text.txt', '请求返回:'.$result.PHP_EOL, FILE_APPEND);
                $result=json_decode($result,true);
                $result['httpCode']=$httpCode;
                $result = json_encode($result,JSON_UNESCAPED_UNICODE);
            } else {
                $result = json_encode(['httpCode'=>$httpCode]);
            }
    
            curl_close($ch);//关闭curl,释放资源
            return $result;
        }
    
        /**
         * 敏感信息加密算法
         * @param $str
         * @return string
         */
        public function getEncrypt($str)
        {
            //$str是待加密字符串
            $public_key = file_get_contents('../vendor/wxpay3/cacert/pingtai_cert.pem'); //此处证书文件为平台证书
            $encrypted  = '';
            openssl_public_encrypt($str, $encrypted, $public_key, OPENSSL_PKCS1_OAEP_PADDING);//这里 类型要设置为OPENSSL_PKCS1_OAEP_PADDING
            //base64编码
            $sign = base64_encode($encrypted);
            return $sign;
        }
    }
    
    
    class AesUtil{
        /**
        * AES key
        * @var string
        */
        private $aesKey;
        const KEY_LENGTH_BYTE = 32;
        const AUTH_TAG_LENGTH_BYTE = 16;
    
        /**
        * Constructor
        */
        public function __construct($aesKey)
        {
            if (strlen($aesKey) != self::KEY_LENGTH_BYTE) {
                throw new InvalidArgumentException('无效的ApiV3Key,长度应为32个字节');
            }
            $this->aesKey = $aesKey;
        }
    
        /**
        * 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');
          }
    }
    
    
  • 相关阅读:
    HTML学习笔记
    JSP与Servlet的跳转及得到路径方法整理(转)
    Servlet 学习笔记6:Cookie
    工作=娱乐=爱[龙]
    幸福的方法[龙]
    10张海报,激励人生[龙]
    8个方法让你安然度过低效率的日子[龙]
    使用空余时间的20个有效途径
    人生三点钟
    2013计划
  • 原文地址:https://www.cnblogs.com/pansidong/p/16357319.html
Copyright © 2020-2023  润新知