• php 微信支付V3 APP支付


    前言:微信支付现在分为v2版和v3版,2014年9月10号之前申请的为v2版,之后申请的为v3版。V3版的微信支付没有paySignKey参数.

    php 微信支付类

    <?php
    
    class Wechat {
    
        protected $wechat_config;
    
        //https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1
        public function __construct($wechat_config = [])
        {
            if(empty($wechat_config)){
                $wechat_config = [
                    'appid'=>'',
                    'mch_id'=>'',
                    'api_key'=>'',
                    'appsecret'=>'',
                    'notify_url'=>'',
                ];
            }
            $this->wechat_config = $wechat_config;
    
        }
    
    
        //生成预支付订单
        public function wx_pay($body,$total_fee,$out_trade_no,$attach = 'charge_order') {
            $nonce_str = $this->rand_code();        //调用随机字符串生成方法获取随机字符串
            $data['appid'] = $this->wechat_config['appid'];   //appid
            $data['mch_id'] = $this->wechat_config['mch_id'] ;        //商户号
            $data['body'] = $body;
            $data['spbill_create_ip'] = $this->get_client_ip();   //ip地址
            $data['total_fee'] = $total_fee;                         //金额
            $data['out_trade_no'] = $out_trade_no;    //商户订单号,不能重复
            $data['nonce_str'] = $nonce_str;                   //随机字符串
            $data['notify_url'] = $this->wechat_config['notify_url'];   //回调地址,用户接收支付后的通知,必须为能直接访问的网址,不能跟参数
            $data['trade_type'] = 'APP';      //支付方式
            $data['attach'] = $attach;      //商户携带订单的自定义数据
            //将参与签名的数据保存到数组  注意:以上几个参数是追加到$data中的,$data中应该同时包含开发文档中要求必填的剔除sign以外的所有数据
    
            $data['sign'] = self::getSign($data,$this->wechat_config['api_key']);        //获取签名
    
            $xml = self::ToXml($data);            //数组转xml
            //curl 传递给微信方
            $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
            //header("Content-type:text/xml");
            $ch = curl_init();
            curl_setopt($ch,CURLOPT_URL, $url);
            if(stripos($url,"https://")!==FALSE){
                curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
                curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
                curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
            }    else    {
                curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE);
                curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//严格校验
            }
            //设置header
            curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
            curl_setopt($ch, CURLOPT_HEADER, FALSE);
            //要求结果为字符串且输出到屏幕上
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
            //设置超时
            curl_setopt($ch, CURLOPT_TIMEOUT, 30);
            curl_setopt($ch, CURLOPT_POST, TRUE);
            //传输文件
            curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
            //运行curl
            $data = curl_exec($ch);
            //返回结果
            if($data){
    
                curl_close($ch);
                //返回成功,将xml数据转换为数组.
                $re = self::FromXml($data);
    
                if($re['return_code'] != 'SUCCESS'){
                    return [
                        'code'=>'201',
                        'msg'=>$re['return_msg']
                    ];
                }
                else{
                    //接收微信返回的数据,传给APP!
                    $arr =array(
                        'prepayid' =>$re['prepay_id'],
                        'appid' => $this->wechat_config['appid'],
                        'partnerid' => $this->wechat_config['mch_id'],
                        'package' => 'Sign=WXPay',
                        'noncestr' => $nonce_str,
                        'timestamp' =>strval(time()),
                    );
                    //第二次生成签名
                    $sign = self::getSign($arr,$this->wechat_config['api_key']);
                    $arr['sign'] = $sign;
                    $arr['code'] = '200';
                    $arr['msg'] = '签名成功';
                    return $arr;
                }
            } else {
                $error = curl_errno($ch);
                curl_close($ch);
                return [
                    'code'=>'201',
                    'msg'=>"curl出错,错误码:$error"
                ];
    
            }
        }
    
    
    
    
        //参数要组装成xml格式
        public static function ToXml($data = array()){
            if (!is_array($data) || count($data) <= 0) {
                return '数组异常';
            }
    
            $xml = "<xml>";
            foreach ($data as $key => $val) {
                if (is_numeric($val)) {
                    $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
                } else {
                    $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
                }
            }
            $xml .= "</xml>";
            return $xml;
        }
    
    
    
        public static function FromXml($xml)
        {
            if(!$xml){
                echo "xml数据异常!";
            }
            //将XML转为array
            //禁止引用外部xml实体
            libxml_disable_entity_loader(true);
            $data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
            return $data;
        }
    
        //生成随机字符串
        private function rand_code(){
            $str = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';//62个字符
            $str = str_shuffle($str);
            $str = substr($str,0,32);
            return  $str;
        }
    
        /*
        获取当前服务器的IP
        */
        private function get_client_ip(){
            if ($_SERVER['REMOTE_ADDR']) {
                $cip = $_SERVER['REMOTE_ADDR'];
            } elseif (getenv("REMOTE_ADDR")) {
                $cip = getenv("REMOTE_ADDR");
            } elseif (getenv("HTTP_CLIENT_IP")) {
                $cip = getenv("HTTP_CLIENT_IP");
            } else {
                $cip = "unknown";
            }
            return $cip;
        }
    
        //生成签名
        public static function getSign($params,$api_key) {
            ksort($params);        //将参数数组按照参数名ASCII码从小到大排序
            foreach ($params as $key => $item) {
                if (!empty($item)) {         //剔除参数值为空的参数
                    $newArr[] = $key.'='.$item;     // 整合新的参数数组
                }
            }
    
            $stringA = implode("&", $newArr);         //使用 & 符号连接参数
            $stringSignTemp = $stringA."&key=".$api_key;        //拼接key
            // key是在商户平台API安全里自己设置的
            $stringSignTemp = MD5($stringSignTemp);       //将字符串进行MD5加密
            $sign = strtoupper($stringSignTemp);      //将所有字符转换为大写
            return $sign;
        }
    
    }

    调起微信支付

            //生成微信支付预订单信息
            $wechat_config = [
                'appid'=>$this->config->openWeixin->appid,
                'mch_id'=>$this->config->openWeixin->mch_id,
                'api_key'=>$this->config->openWeixin->api_key,
                'appsecret'=>$this->config->openWeixin->appsecret,
                'notify_url'=>$this->config->openWeixin->notify_url,
            ];
            $order_no = date('YmdHis').rand(1000,9999);
            $money = 1;
            $wechat = new Wechat($wechat_config);
            $wechat_data = $wechat->wx_pay('test',$money,$order_no,'aid_order');
    
            if($wechat_data['code'] != 200){
            echo $wechat_data['msg'];die;
            } 
            $result = [ 'order_no'=>$order_no , 'money'=>$money, 'wechat_data'=>$wechat_data, ]; 
            print_r($result);die;

    微信支付回调

     // 微信支付回调
        public function wx_notifyAction(){
            defined('BASE_PATH') || define('BASE_PATH', getenv('BASE_PATH') ?: realpath(dirname(__FILE__) . '/../..'));
            //接收微信返回的数据数据,返回的xml格式
            $xmlData = file_get_contents('php://input');
            //将xml格式转换为数组
            $data = Wechat::FromXml($xmlData);
            //用日志记录检查数据是否接受成功,验证成功一次之后,可删除。
            $file_name = BASE_PATH.'/log'.date('Ymd').'.txt';
            //$file = fopen($file_name, 'a+') or die("Unable to open file!");;
            //fwrite($file,'test:'.json_encode($data));die;
            //为了防止假数据,验证签名是否和返回的一样。
            //记录一下,返回回来的签名,生成签名的时候,必须剔除sign字段。
            $sign = $data['sign'];
            unset($data['sign']);
            if($sign == Wechat::getSign($data,$this->config->openWeixin->api_key)){
                //签名验证成功后,判断返回微信返回的
                if ($data['result_code'] == 'SUCCESS') {
                    //根据返回的订单号做业务逻辑
                    $res = false;
                    if($data['attach'] == 'aid_order'){
                        $model = new PetAidOrder();
                        //宠物援助
                        $PetAidOrderData = $model::findFirst([
                            'order_no = :out_trade_no: AND pay_type = 2 AND status = 2',
                            'bind'=>[
                                'out_trade_no'=>$data['out_trade_no'],
                            ]
                        ]);
                        $getPetAidData = PetAid::findFirst([
                            'id = :pet_aid_id:',
                            'bind'=>[
                                'pet_aid_id'=>$PetAidOrderData->pet_aid_id
                            ]
                        ]);
                        if($PetAidOrderData && $getPetAidData){
                            /*===================事务开启================================*/
                            $connection = $model->getWriteConnection();
                            $connection->begin();
                            //更新订单数据
                            $PetAidOrderData->status = 1;
                            $PetAidOrderData->pay_time = time();
                            //更新项目数据
                            $getPetAidData->get_money += $PetAidOrderData->money;
                            if($PetAidOrderData->update() && $a = $getPetAidData->update()){$connection->commit();
                                $res = true;
                            }
    
                        }
    
                    }elseif($data['attach'] == 'rescue_order'){
                        $model = new UserRescueOrder();
                        //爱心捐献
                        $UserRescueOrderData = $model::findFirst([
                            'order_no = :out_trade_no: AND pay_type = 2 AND status = 2',
                            'bind'=>[
                                'out_trade_no'=>$data['out_trade_no'],
                            ]
                        ]);
    
                        $getUserRescueAuthData = UserRescueAuth::findFirst([
                            'id = :user_resuce_auth_id:',
                            'bind'=>[
                                'user_resuce_auth_id'=>$UserRescueOrderData->user_resuce_auth_id
                            ]
                        ]);
    
                        if($UserRescueOrderData && $getUserRescueAuthData){
                            /*===================事务开启================================*/
                            $connection = $model->getWriteConnection();
                            $connection->begin();
                            //更新订单数据
                            $UserRescueOrderData->status = 1;
                            $UserRescueOrderData->pay_time = time();
                            //更新救助中心数据
                            $getUserRescueAuthData->donation_money += $UserRescueOrderData->money;
    
                            if($UserRescueOrderData->update() && $getUserRescueAuthData->update()){
                                $connection->commit();
                                $res = true;
                            }
                        }
    
                    }
    
                    //处理完成之后,告诉微信成功结果!
                    if($res == true){
                        echo '<xml>
                          <return_code><![CDATA[SUCCESS]]></return_code>
                          <return_msg><![CDATA[OK]]></return_msg>
                          </xml>';
                        exit();
                    }else{
                        $file = fopen($file_name, 'a+');
                        fwrite($file,date("Y-m-d H:i:s")."错误信息:数据修改失败,".json_encode($data).PHP_EOL);
                    }
                }
                //支付失败,输出错误信息
                else{
                    $file = fopen($file_name, 'a+');
                    fwrite($file,date("Y-m-d H:i:s")."错误信息:支付失败,".json_encode($data).PHP_EOL);
                }
            }
            else{
                $file = fopen($file_name, 'a+');
                fwrite($file,date("Y-m-d H:i:s")."错误信息:签名验证失败,".json_encode($data).PHP_EOL);
            }
    
        }

      

  • 相关阅读:
    swift5.x 错误处理
    iOS--iPhoneX设备判断
    swift5.x 多线程的应用场景
    Swift5.x 多线程编程
    Swift5.x 范型
    swift5.x 类初始化、反初始化, 指定初始化器,便利初始化器
    swift5.x 扩展(extension)
    swift5.x 多态,类型检查,类型转换
    Effective java笔记(十),序列化
    Effective java笔记(九),并发
  • 原文地址:https://www.cnblogs.com/-mrl/p/8392735.html
Copyright © 2020-2023  润新知