PHP微信H5支付开发实例


Posted in PHP onJuly 25, 2018

最近由于业务所需,对接了微信H5支付,然而微信支付对这块并没有现成的demo可用,所以就必须自己老老实实对照开发文档去写咯!但这对于刚接触的童鞋来说,坑多多少少还是有的,所以寻思着把自己的经验分享出来,毕竟现成的用的还是多巴适的嘛!

好了,官方文档的那一套就不多说了,详情见官方文档。

在这里,我主要分成了三个文件:WxPay.Config.php(支付配置文件)、Weixin.class.php(支付类)以及PayMentController.class.php(支付文件)。

首先,WxPay.Config.php配置文件主要包含了商户appId、商户号、商家key、异步回调URL、支付场景信息,如下:

class WxPayConfig
{
  public static $appid = '微信支付的公众号appid';
  public static $mchid = '微信支付分配的商户号';
  public static $key = '微信商户自己设置的安全key';
  public static $notify_url = '商户侧接收微信支付异步通知的URL';
  public static $scene_info = '{"h5_info":{"type":"Wap","wap_url":" 发起微信H5支付H5的URL","wap_name":"支付"}}'; 
}

然后,封装Weixin.class.php支付类,主要调用统一下单Api,这里不多说了,直接上代码:

<?php
require_once "lib/WxPay.Config.php";
class Weixin
{
     /**
   * 微信H5下单付款
   *   @order 付款信息
     *   @bodys 付款内容
   * */
     function getCode($order,$bodys){
          $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";//微信传参地址
          //1.获取调用统一下单接口所需必备参数
    $appid =WxPayConfig::$appid;//微信公众号appid
    $mch_id = WxPayConfig::$mchid;//微信支付商户号
    $key = WxPayConfig::$key;//自己设置的微信商家key
    $out_trade_no = $order['order_sn'];//平台内部订单号
    $nonce_str=MD5($out_trade_no);//随机字符串
    $body = $bodys;//付款内容
    $total_fee = $order['order_amount']*100;//付款金额,单位为分
    $spbill_create_ip = getIP(); //获得用户设备IP
    $attach = 'weixinh5';//附加数据(自定义,在支付通知中原样返回)
    $notify_url = WxPayConfig::$notify_url; //异步回调地址,需外网可以直接访问
    $trade_type = 'MWEB';//交易类型,微信H5支付时固定为MWEB
    $scene_info =WxPayConfig::$scene_info;//场景信息
          //2.将参数按照key=value的格式,并按照参数名ASCII字典序排序生成字符串
    $signA ="appid=$appid&attach=$attach&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";
    //3.拼接字符串
          $strSignTmp = $signA."&key=$key";
          //4.MD5加密后转换成大写
    $sign = strtoupper(MD5($strSignTmp));
          //5.拼接成所需XML格式
    $post_data = "<xml> 
            <appid>$appid</appid> 
            <attach>$attach</attach> 
            <body>$body</body> 
            <mch_id>$mch_id</mch_id> 
            <nonce_str>$nonce_str</nonce_str> 
            <notify_url>$notify_url</notify_url> 
            <out_trade_no>$out_trade_no</out_trade_no> 
            <spbill_create_ip>$spbill_create_ip</spbill_create_ip> 
            <total_fee>$total_fee</total_fee> 
            <trade_type>$trade_type</trade_type>
            <scene_info>$scene_info</scene_info>
            <sign>$sign</sign> 
          </xml>";
          //6.以POST方式向微信传参,并取得微信返回的支付参数
    $dataxml = httpRequest($url,'POST',$post_data);
    $objectxml = (array)simplexml_load_string($dataxml, 'SimpleXMLElement', LIBXML_NOCDATA); //将微信返回的XML转换成数组
    return $objectxml;
  }
}

最后,PayMentController.class.php支付文件,支付文件接收前端发起支付的请求并处理后,调用Weixin.class.php支付类并接受结果后返回给前端(此处分享已经去掉接口验证等系列代码逻辑):

public function getPay(){
     //1.引入支付类文件
     include_once "plugins/Payment/weixin/Weixin.class.php";
     $payment = new \Weixin();
     $order_id = I('order_id');
     //2.判断参数是否为空
     if (!empty($order_id)){
          //3.根据订单id查询订单是否存在
          $order = M('Order')->where(array('id'=>$order_id))->find();
          if ($order){//订单存在
              //4.判断该笔订单是否已经支付,如已支付则返回支付失败并给出相应提示
              if ($order['pay_status'] == '1'){
                   exit(json_encode(array('status'=>'205','msg'=>'该订单已支付,请勿重复提交!')));
              }
              $bodys = '订单:'.$order['order_sn'] . '支付';
              //5.调用支付类中封装的支付方法并对应传参
              $result = $payment->getCode($order,$bodys);
              //6.当return_code和result_code均为SUCCESS,代表下单成功,将支付参数返回
              if($result['return_code'] == 'SUCCESS'){
                   if($result['result_code'] == 'SUCCESS'){
                        exit(json_encode(array('status'=>'0','msg'=>'下单成功,请支付!','result'=>$result['mweb_url'])));
                   }elseif($result['result_code'] == 'FAIL'){
                        exit(json_encode(array('status'=>'-201','msg'=>$result['err_code_des'])));
                   }
              }else{
        exit(json_encode(array('status'=>'-1','msg'=>'未知错误,请稍后重试!')));
                  }
          }else{
              //报错:数据不存在
              exit(json_encode(array('status'=>'-200','msg'=>'订单不存在,请核实后再提交!')));
          }
     }else{
          //报错:缺少参数
          exit(json_encode(array('status'=>'-204','msg'=>'参数缺失,请核实!')));
     }
}

前端在接收到支付URL后执行即可唤醒微信支付。

附一:获取用户终端设备ip方法

function getIP(){      
    if (getenv("HTTP_CLIENT_IP"))
       $ip = getenv("HTTP_CLIENT_IP");
    else if(getenv("HTTP_X_FORWARDED_FOR"))
        $ip = getenv("HTTP_X_FORWARDED_FOR");
    else if(getenv("REMOTE_ADDR"))
       $ip = getenv("REMOTE_ADDR");
    else $ip = "Unknow";
    return $ip;
}

######附二:CURL请求方法

/**
   * CURL请求
   * @param $url 请求url地址
   * @param $method 请求方法 get post
   * @param null $postfields post数据数组
   * @param array $headers 请求header信息
   * @param bool|false $debug 调试开启 默认false
   * @return mixed
   */
  function httpRequest($url, $method, $postfields = null, $headers = array(), $debug = false) {
    $method = strtoupper($method);
    $ci = curl_init();
    /* Curl settings */
    curl_setopt($ci, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    curl_setopt($ci, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 6.2; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0");
    curl_setopt($ci, CURLOPT_CONNECTTIMEOUT, 60); /* 在发起连接前等待的时间,如果设置为0,则无限等待 */
    curl_setopt($ci, CURLOPT_TIMEOUT, 7); /* 设置cURL允许执行的最长秒数 */
    curl_setopt($ci, CURLOPT_RETURNTRANSFER, true);
    switch ($method) {
      case "POST":
        curl_setopt($ci, CURLOPT_POST, true);
        if (!empty($postfields)) {
          $tmpdatastr = is_array($postfields) ? http_build_query($postfields) : $postfields;
          curl_setopt($ci, CURLOPT_POSTFIELDS, $tmpdatastr);
        }
        break;
      default:
        curl_setopt($ci, CURLOPT_CUSTOMREQUEST, $method); /* //设置请求方式 */
        break;
    }
    $ssl = preg_match('/^https:\/\//i',$url) ? TRUE : FALSE;
    curl_setopt($ci, CURLOPT_URL, $url);
    if($ssl){
      curl_setopt($ci, CURLOPT_SSL_VERIFYPEER, FALSE); // https请求 不验证证书和hosts
      curl_setopt($ci, CURLOPT_SSL_VERIFYHOST, FALSE); // 不从证书中检查SSL加密算法是否存在
    }
    curl_setopt($ci, CURLOPT_FOLLOWLOCATION, 1);
    curl_setopt($ci, CURLOPT_MAXREDIRS, 2);/*指定最多的HTTP重定向的数量,这个选项是和CURLOPT_FOLLOWLOCATION一起使用的*/
    curl_setopt($ci, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($ci, CURLINFO_HEADER_OUT, true);
    $response = curl_exec($ci);
    $requestinfo = curl_getinfo($ci);
    if ($debug) {
      echo "=====post data======\r\n";
      var_dump($postfields);
      echo "=====info===== \r\n";
      print_r($requestinfo);
      echo "=====response=====\r\n";
      print_r($response);
    }
    curl_close($ci);
    return $response;
}

好了,一点点菜鸟心得,有不当之处欢迎留言指证交流,一起成长!

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

PHP 相关文章推荐
PHP中的错误处理、异常处理机制分析
May 07 PHP
解析阿里云ubuntu12.04环境下配置Apache+PHP+PHPmyadmin+MYsql
Jun 26 PHP
php利用反射实现插件机制的方法
Mar 14 PHP
php正则匹配文章中的远程图片地址并下载图片至本地
Sep 29 PHP
php验证码的制作思路和实现方法
Nov 12 PHP
反射调用private方法实践(php、java)
Dec 21 PHP
利用php抓取蜘蛛爬虫痕迹的示例代码
Sep 30 PHP
php基于websocket搭建简易聊天室实践
Oct 24 PHP
PHP实现递归目录的5种方法
Oct 27 PHP
PHP超低内存遍历目录文件和读取超大文件的方法
May 01 PHP
Yii2框架配置文件(Application属性)与调试技巧实例分析
May 27 PHP
thinkphp整合系列之极验滑动验证码geetest功能
Jun 18 PHP
php app支付宝回调(异步通知)详解
Jul 25 #PHP
php支付宝APP支付功能
Jul 29 #PHP
PHP多个图片压缩成ZIP的方法
Aug 18 #PHP
PHP上传文件及图片到七牛的方法
Jul 25 #PHP
详解PHP版本兼容之openssl调用参数
Jul 25 #PHP
PHP实现的多维数组去重操作示例
Jul 21 #PHP
php实现生成PDF文件的方法示例【基于FPDF类库】
Jul 21 #PHP
You might like
基于Zend的Captcha机制的应用
2013/05/02 PHP
php猜单词游戏
2015/09/29 PHP
PHP Yii框架之表单验证规则大全
2015/11/16 PHP
PHP使用星号替代用户名手机和邮箱的实现代码
2018/02/07 PHP
PHP+MySQL实现在线测试答题实例
2020/01/02 PHP
Javascript日期对象的dateAdd与dateDiff方法
2008/11/18 Javascript
jQuery 遍历json数组的实现代码
2020/09/22 Javascript
瀑布流布局并自动加载实现代码
2013/03/12 Javascript
node.js中的fs.symlink方法使用说明
2014/12/15 Javascript
纯js实现无限空间大小的本地存储
2015/06/18 Javascript
JavaScript小技巧整理
2015/12/30 Javascript
详解jquery事件delegate()的使用方法
2016/01/25 Javascript
JavaScript中清空数组的方法总结
2016/12/02 Javascript
JavaScript正则表达式小结(test|match|search|replace|split|exec)
2016/12/08 Javascript
使用contextMenu插件实现Bootstrap table弹出右键菜单
2017/02/20 Javascript
原生js更改css样式的两种方式
2017/03/15 Javascript
详解Vue中状态管理Vuex
2017/05/11 Javascript
vue中的inject学习教程
2019/04/24 Javascript
javascript随机变色实例代码
2019/10/15 Javascript
js实现数字滚动特效
2019/12/16 Javascript
[43:33]EG vs Spirit Supermajor 败者组 BO3 第一场 6.4
2018/06/05 DOTA
Python算法应用实战之队列详解
2017/02/04 Python
关于Python面向对象编程的知识点总结
2017/02/14 Python
Python中出现IndentationError:unindent does not match any outer indentation level错误的解决方法
2020/04/18 Python
Python递归调用实现数字累加的代码
2020/02/25 Python
详解Python设计模式之策略模式
2020/06/15 Python
介绍CSS3使用技巧5个
2009/04/02 HTML / CSS
波兰办公用品和学校用品在线商店:Dlabiura24.pl
2020/11/18 全球购物
编程实现当输入某产品代码则打印出该产品记录的功能
2014/05/03 面试题
托管代码(Managed Code)和非托管代码(Unmanaged Code)有什么区别
2014/09/29 面试题
四风问题个人剖析材料
2014/10/07 职场文书
外贸采购员岗位职责
2015/04/03 职场文书
铁人观后感
2015/06/16 职场文书
golang switch语句的灵活写法介绍
2021/05/06 Golang
Python面向对象编程之类的概念
2021/11/01 Python
MYSQL事务的隔离级别与MVCC
2022/05/25 MySQL