美文网首页
PHP 微信H5支付

PHP 微信H5支付

作者: cuihaomaster | 来源:发表于2019-04-29 13:52 被阅读0次

什么是微信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&notify_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 = "<xml>
                    <appid>$appid</appid>
                    <mch_id>$mch_id</mch_id>
                    <body>$body</body>
                    <out_trade_no>$out_trade_no</out_trade_no>
                    <total_fee>$total_fee</total_fee>
                    <spbill_create_ip>$spbill_create_ip</spbill_create_ip>
                    <notify_url>$notify_url</notify_url>
                    <trade_type>$trade_type</trade_type>
                    <scene_info>$scene_info</scene_info>
                    <attach>$out_trade_no</attach>
                    <nonce_str>$nonce_str</nonce_str>
                    <sign>$sign</sign>
            </xml>";//拼接成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><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>');//通知微信成功
                }

            }

        }

    }
    //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 "<script>alert('支付成功');</script>";
            }else{
                echo "<script>alert('支付失败');</script>";
            }
        }else{

            echo "<script>alert('订单不存在');</script>";
        }

    }
    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"."<br>";
        }
    }
    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代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>微信支付</title>
    {load href="__STATIC__/js/jquery.min.js" /}
    <style type="text/css">
        body{
            font-family: "Microsoft YaHei";
        }
        .pay-box{
            position: absolute;
            top: 50%;
            margin-top: -516px;
            left: 50%;
            margin-left: -320px;
        }
        .ico{
            width: 240px;
            height: 240px;
            border-radius: 120px;
            background: #3FB837;
            color: #fff;
            display: inline-block;
            font-size: 160px;
            line-height: 240px;
        }
        .txt{
            font-size: 42px;
            padding-top: 30px;
            color: #333;
        }
        .val{
            font-size: 80px;
            font-weight: bold;
        }
        .pay{
            width: 640px;
            height: 100px;
            margin-top: 100px;
            padding: 20px;
            border-radius: 10px;
            font-size:42px;
            color: #fff;
            background: #07BF05;
            border: 0px;
            text-align: center;
        }
        a{
            color: #fff;
            background: transparent !important;
        }
    </style>
</head>
<body>
<div class="pay-box" style="text-align: center;">
    <div class="ico">
        ¥
    </div>
    <div class="txt">
        支付金额
    </div>
    <div class="val">
        ¥<span>{$money/100}</span>
    </div>
    <a class="pay" href="{$mweb_url}&redirect_url={$return_Url}"><button class="pay">确认支付</button></a>
</div>
</body>
</html>

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


image.png

相关文章

  • 微信支付

    微信支付包括微信H5支付和微信公众号支付 微信H5支付 官方api:https://pay.weixin.qq.c...

  • iOS WKWebView H5微信支付跳转

    iOS WKWebView H5微信支付跳转 需求:iOS客户端实现嵌入H5进行微信支付跳转到微信客户端,支付完成...

  • vue实现webapp支付

    最近在使用vue写webapp,app中要求可以实现线上支付,研究了微信H5支付与支付宝H5支付。其中微信H5支付...

  • 使用UIWebView中遇到的问题

    1、webView中使用H5微信支付 在webView中使用H5微信支付跳转到微信收银台页面时,点击取消或者支付完...

  • Android端 支付宝&微信H5支付调用

    一、支付宝H5支付调用 支付宝H5支付官方开发文档 代码示例: 二、微信H5支付调用 微信H5支付官方开发文档 代...

  • h5仿微信、支付宝支付键盘

    h5仿微信、支付宝键盘|仿微信支付数字键盘|仿支付宝输入法键盘 h5仿微信支付键盘实例 头部引入 HTML片段 效...

  • 微信支付

    H5支付 微信 h5 支付,后端向微信请求支付参数的时候会要求填写一个 return_url 参数,用于 支付成功...

  • 微信H5支付(JSAPI支付)---前端篇

    微信H5支付(JSAPI支付)---前端篇 在开发微信h5支付查了资料说是有两种唤起微信支付的方法:一种是利用内置...

  • 微信支付

    说明:最近公司要求做微信支付,而且还是从h5 界面 调取微信支付,支付成功之后结果返回h5 的需求,(使用WebV...

  • android微信H5支付

    微信除了微信sdk支付还有的就是h5支付,h5支付可以让你再非微信浏览器上进行支付的动作.支付的前提我就不详细的说...

网友评论

      本文标题:PHP 微信H5支付

      本文链接:https://www.haomeiwen.com/subject/flqynqtx.html