PHP 微信H5支付

什么是微信H5支付

H5支付是指商户在微信客户端外的移动端网页展示商品或服务,用户在前述页面确认使用微信支付时,商户发起本服务呼起微信客户端进行支付
主要用于触屏版的手机浏览器请求微信支付的场景。可以方便的从外部浏览器唤起微信支付
微信官方也提供了一个体验链接,请在微信外浏览器打开

开发流程

1、用户在商户侧完成下单,使用微信支付进行支付

2、由商户后台向微信支付发起下单请求(调用统一下单接口)注:交易类型trade_type=MWEB

3、统一下单接口返回支付相关参数给商户后台,如支付跳转url(参数名“mweb_url”),商户通过mweb_url调起微信支付中间页

4、中间页进行H5权限的校验,安全性检查(此处常见错误请见下文)

5、如支付成功,商户后台会接收到微信侧的异步通知

6、用户在微信支付收银台完成支付或取消支付,返回商户页面(默认为返回支付发起页面)

7、商户在展示页面,引导用户主动发起支付结果的查询

8、商户后台判断是否接到收微信侧的支付结果通知,如没有,后台调用订单查询接口确认订单状态

9、展示最终的订单支付结果给用户

php代码

class WxPay extends Base {
   
    private $appid;//应用APPID
    private $mch_id;//微信支付商户号
    private $key;//微信商户API密钥
    private $wap_url;//场景信息url
    private $notify_url;//回调地址
    private $wx_url;//微信h5支付地址
    private $order_status_url;//支付完成查询订单状态地址
    public function __construct()
    {
        $this->appid = '';
        $this->mch_id = '';
        $this->key = '';
        $this->wap_url = "http://".$_SERVER['HTTP_HOST']."/tp5/public/index.php/WxPay/index";
        $this->notify_url = "http://".$_SERVER['HTTP_HOST']."/tp5/public/index.php/WxPay/notify";
        $this->wx_url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
        $this->order_status_url =   "http://".$_SERVER['HTTP_HOST']."/tp5/public/index.php/WxPay/order_status"; 
            
    }
    /*
     * 微信支付接口
     * @param userid 用户id goodsid 商品id money 如果传入money,goodis就为0
     */
      public function index() {
        if (!isset($_GET['userid']) || (!isset($_GET['goodsid'])&&!isset($_GET['money']))) {
            $this->error('充值信息错误');
        }
        $user = db_user::table("t_base")->field('nickname')->where(['userid' => $_GET['userid']])->find();
        if (!$user) {
            $this->error('用户信息错误');
        }
        if(isset($_GET['money'])){
            $_GET['goodsid'] = 0;
        }else{
            $goods = db('goods')->where(['goodsid' => $_GET['goodsid']])->find();
            if (!$goods) {
                $this->error('商品信息错误');
            }
            $_GET['money'] = $goods['money'];
        }
   
        $money = $_GET['money']*100;//充值金额 微信支付单位为分
        //$money  =1;
        $userip = $this->get_client_ip();     //获得用户设备IP
        $appid  = $this->appid;                  //应用APPID
        $mch_id = $this->mch_id;                  //微信支付商户号
        $key    = $this->key;                 //微信商户API密钥
        $out_trade_no = "PHP" . date("YmdHis", time()).rand(1000, 9999);//平台内部订单号
        $nonce_str = $this->createNoncestr();//随机字符串
        $body = "H5充值";//内容
        $total_fee = $money; //金额
        $spbill_create_ip = $userip; //IP
        $notify_url = $this->notify_url; //回调地址
        $trade_type = 'MWEB';//交易类型 具体看API 里面有详细介绍
        $scene_info ='{"h5_info":{"type":"Wap","wap_url":$this->wap_url,"wap_name":"支付"}}';//场景信息 必要参数
        $signA ="appid=$appid&attach=$out_trade_no&body=$body&mch_id=$mch_id&nonce_str=$nonce_str¬ify_url=$notify_url&out_trade_no=$out_trade_no&scene_info=$scene_info&spbill_create_ip=$spbill_create_ip&total_fee=$total_fee&trade_type=$trade_type";
        $strSignTmp = $signA."&key=$key"; //拼接字符串  注意顺序微信有个测试网址 顺序按照他的来 直接点下面的校正测试 包括下面XML  是否正确
        $sign = strtoupper(MD5($strSignTmp)); // MD5 后转换成大写
        $post_data = "
                    $appid
                    $mch_id
                    $body
                    $out_trade_no
                    $total_fee
                    $spbill_create_ip
                    $notify_url
                    $trade_type
                    $scene_info
                    $out_trade_no
                    $nonce_str
                    $sign
            ";//拼接成XML 格式
        $url = $this->wx_url;//微信传参地址
        $dataxml = $this->postXmlCurl($post_data,$url); //后台POST微信传参地址  同时取得微信返回的参数
        $objectxml = (array)simplexml_load_string($dataxml, 'SimpleXMLElement', LIBXML_NOCDATA); //将微信返回的XML 转换成数组
        //var_dump($out_trade_no);die;
        if($objectxml['return_code']=='SUCCESS'){
            //var_dump($objectxml);die;
            $res = db_user::table('t_pay')->data(['userid' => $_GET['userid'], 'orderid' => $out_trade_no, 'money' => $_GET['money'], 'status' => 0, 'createtime' => time(), 'type' => 'wxpay', 'goodsid' => $_GET['goodsid']])->insert();
            if($res){
                $returnUrl = $this->order_status_url.'?ordernum='.$out_trade_no;
                $return_Url = urlencode($returnUrl);//支付完成后要跳转的url。例如做订单结果验证之类的
                return view('index',['money'=>$money,'mweb_url'=>$objectxml['mweb_url'],'return_Url'=>$return_Url]);

            }
        }

    }
    /*
     * 微信h5支付回调
     */
    public function notify()
    {
        $postXml = $GLOBALS["HTTP_RAW_POST_DATA"]; //接收微信参数
        if (empty($postXml)) {
            return false;
        }
        wxpay_log('原数据'.PHP_EOL.var_export($postXml,true).PHP_EOL);//支付日志
        //将xml格式转换成数组

        $attr = $this->xmlToArray($postXml);
        if($attr['result_code']=='SUCCESS'){
            //验证签名
            foreach( $attr as $k=>$v) {
                if($k == 'sign') {
                    $xmlSign = $attr[$k];
                    unset($attr[$k]);
                };
            }

            $sign = http_build_query($attr);
            //md5处理
            $sign = md5($sign.'&key='.$this->key);
            //转大写
            $sign = strtoupper($sign);
            if ( $sign === $xmlSign){
                //签名相等
                $out_trade_no = $attr['out_trade_no'];//订单号
                $order = db_user::table('t_pay')->where(['orderid' => $out_trade_no, 'status' => 0])->find();
                if($order){
                    try{
                        db_user::table('t_pay')->where(['orderid' => $out_trade_no, 'status' => 0])->data(['status' => 1])->update();//订单完成
                        $goods = db('goods')->where(['goodsid' => $order['goodsid']])->find();
                        $userporo = db_user::table('t_userprops')->where(['userid' => $order['userid'], 'propid' => $goods['propid']])->field('userid,propnum')->find();
                        if (empty($userporo)) {
                            $userporo['propnum'] = 0;
                            db_user::table('t_userprops')->data(['userid' => $order['userid'], 'propid' => $goods['propid'], 'propnum' => $goods['propnum']])->insert();
                        } else {
                            db_user::table('t_userprops')->where(['userid' => $order['userid'], 'propid' => $goods['propid']])->setInc('propnum', $goods['propnum']);
                        }
                        //写入日志
                        db_log::table('t_prop_log')->data(['oper' => $goods['oper'], 'userid' => $order['userid'], 'time' => time(), 'oldnumber' => $userporo['propnum'], 'chgnumber' => $userporo['propnum'] + $goods['propnum']])->insert();
                    }catch(\Exception $e){
                        $this->error('执行错误');
                    }
                    echo exit('');//通知微信成功
                }

            }

        }

    }
    //xml转数组
    public function xmlToArray($xml)
    {
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        $xmlstring = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
        $val = json_decode(json_encode($xmlstring), true);
        return $val;
    }
    /*
     * 订单状态查询页面
     */
    public function order_status()
    {
        $ordernum = $_GET['ordernum'];
        if($ordernum){
            $order = db_user::table('t_pay')->where(['orderid' =>$ordernum, 'status' => 1])->find();
            if($order){
                echo "";
            }else{
                echo "";
            }
        }else{

            echo "";
        }

    }
    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;
    }
    public function postXmlCurl($xml,$url,$second = 30){
        $ch = curl_init();
        //设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);
        curl_setopt($ch,CURLOPT_URL, $url);
        curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
        curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
        //post提交方式
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        //运行curl
        $data = curl_exec($ch);
        //返回结果
        if($data){
            curl_close($ch);
            return $data;
        }else{
            $error = curl_errno($ch);
            curl_close($ch);
            echo "curl出错,错误码:$error"."
"; } } public function get_client_ip($type = 0) { $type = $type ? 1 : 0; $ip = 'unknown'; if ($ip !== 'unknown') return $ip[$type]; if($_SERVER['HTTP_X_REAL_IP']){//nginx 代理模式下,获取客户端真实IP $ip=$_SERVER['HTTP_X_REAL_IP']; }elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {//客户端的ip $ip = $_SERVER['HTTP_CLIENT_IP']; }elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {//浏览当前页面的用户计算机的网关 $arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); $pos = array_search('unknown',$arr); if(false !== $pos) unset($arr[$pos]); $ip = trim($arr[0]); }elseif (isset($_SERVER['REMOTE_ADDR'])) { $ip = $_SERVER['REMOTE_ADDR'];//浏览当前页面的用户计算机的ip地址 }else{ $ip=$_SERVER['REMOTE_ADDR']; } // IP地址合法验证 $long = sprintf("%u",ip2long($ip)); $ip = $long ? array($ip, $long) : array('0.0.0.0', 0); return $ip[$type]; } }

html代码




    
    微信支付
    {load href="__STATIC__/js/jquery.min.js" /}
    


支付金额
{$money/100}

支付页面,点确认支付会跳转到微信客户端,支付完成后会返回浏览器的redirect_url地址


image.png

你可能感兴趣的:(PHP 微信H5支付)