找回密码
 立即注册

QQ登录

只需一步,快速开�

微信公众号开发资源

关注:848

所属分类: 微信开发 微信公众号开发资源

本版块为微信公众号开发教程与公众号开发相关技术分享板块,技术提问请到其他对应的问答板块发帖:)

[公众号开发博客] 200行代码学会微信H5支付,附php可用代码

[复制链接]
查看: 15374|回复: 40
最佳答案
0 

7

主题

8

帖子

386

积分

略知一二

积分
386
 楼主| 发表于 2016-8-3 21:22:37 | 显示全部楼层 |阅读模式
本帖最后由 930415915 于 2016-8-3 22:13 编辑

好久没更新文章了,最近两天在捣鼓微信支付,写下来以便于自己以后更快的熟悉。网上搜的一些微信支付的文章有的让人摸不着头脑,有的十几个文件下下来不知道怎么用。我把官方的代码整合到一个文件200来行代码,新手看到这篇文章也能快速入门微信支付。
读完本篇文章你能得到的是:微信H5的支付流程。能快速做出能对接微信支付的页面。
注意:1、必须为认证的服务号才能使用微信支付
2、此代码必须在微信内部打开
一、老规矩,先上代码
  1. <?php
  2. const APPID = 'xxxxxxxxxxxxxxxxxx';
  3. const MCHID = 'xxxxxxxxxxx';
  4. const KEY = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
  5. const APPSECRET = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
  6.     /**
  7.      *
  8.      * 拼接签名字符串
  9.      * @param array $urlObj
  10.      * [url=home.php?mod=space&uid=67594]@Return[/url] 返回已经拼接好的字符串
  11.      */
  12.     function ToUrlParams($urlObj)
  13.         {
  14.             $buff = "";
  15.             foreach ($urlObj as $k => $v)
  16.             {
  17.                 if($k != "sign"){
  18.                     $buff .= $k . "=" . $v . "&";
  19.                 }
  20.             }
  21.             
  22.             $buff = trim($buff, "&");
  23.             return $buff;
  24.     }
  25.     /**
  26.      * 统一下单,WxPayUnifiedOrder中out_trade_no、body、total_fee、trade_type必填
  27.      * appid、mchid、spbill_create_ip、nonce_str不需要填入
  28.      * @param WxPayUnifiedOrder $inputObj
  29.      * @param int $timeOut
  30.      * @throws WxPayException
  31.      * @return 成功时返回,其他抛异常
  32.      */
  33.     function unifiedOrder( $timeOut = 6)
  34.     {   
  35.         $datas = array();
  36.         $datas['body'] = '卖王文晓啦,一分一个';
  37.         $datas['out_trade_no'] = '1234567890123456789012';//订单号
  38.         $datas['total_fee'] = '1';
  39.         $datas['time_start'] = date("YmdHis");
  40.         $datas['time_expire'] = date("YmdHis", time() + 600);
  41.         $datas['notify_url'] = 'http://p154v65220.iask.in/demo/example/write.php';
  42.         $datas['trade_type'] = 'JSAPI';
  43.         $datas['openid'] = 'oXhDqwVLStq9FBw_eThzUF5IQ_mM';
  44.         $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";  
  45.         $datas['appid'] = APPID;//公众账号ID
  46.         $datas['mch_id'] = MCHID;//商户号
  47.         $datas['spbill_create_ip'] = $_SERVER['REMOTE_ADDR'];//ip  
  48.         $datas['nonce_str'] = getNonceStr();//随机字符串
  49.         //签名步骤一:按字典序排序参数
  50.         ksort($datas);
  51.         $string = ToUrlParamss($datas);
  52.         //签名步骤二:在string后加入KEY
  53.         $string = $string . "&key=".KEY;
  54.         //签名步骤三:MD5加密
  55.         $string = md5($string);
  56.         //签名步骤四:所有字符转为大写
  57.         $result = strtoupper($string);
  58.         $datas['sign'] = $result;//签名
  59.         $xml = ToXml($datas);
  60.         $response = postXmlCurl($xml, $url, false, $timeOut);
  61.         $data = FromXml($response);
  62.         var_dump($data);
  63.         return $data;
  64.     }
  65.     /**
  66.      * 产生随机字符串,不长于32位
  67.      * @param int $length
  68.      * @return 产生的随机字符串
  69.      */
  70.     function getNonceStr($length = 32)
  71.     {
  72.         $chars = "abcdefghijklmnopqrstuvwxyz0123456789";  
  73.         $str ="";
  74.         for ( $i = 0; $i < $length; $i++ )  {  
  75.             $str .= substr($chars, mt_rand(0, strlen($chars)-1), 1);  
  76.         }
  77.         return $str;
  78.     }
  79.     /**
  80.      * 输出xml字符
  81.     **/
  82.     function ToXml($datas)
  83.     {   
  84.         $xml = "<xml>";
  85.         foreach ($datas as $key=>$val)
  86.         {
  87.             if (is_numeric($val)){
  88.                 $xml.="<".$key.">".$val."</".$key.">";
  89.             }else{
  90.                 $xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
  91.             }
  92.         }
  93.         $xml.="</xml>";
  94.         return $xml;
  95.     }
  96.     /**
  97.      * 格式化参数格式化成url参数
  98.      */
  99.     function ToUrlParamss($datas)
  100.     {
  101.         $buff = "";
  102.         foreach ($datas as $k => $v)
  103.         {
  104.             if($k != "sign" && $v != "" && !is_array($v)){
  105.                 $buff .= $k . "=" . $v . "&";
  106.             }
  107.         }
  108.         $buff = trim($buff, "&");
  109.         return $buff;
  110.     }
  111.     /**
  112.      * 以post方式提交xml到对应的接口url
  113.      *
  114.      * @param string $xml  需要post的xml数据
  115.      * @param string $url  url
  116.      * @param bool $useCert 是否需要证书,默认不需要
  117.      * @param int $second   url执行超时时间,默认30s
  118.      */
  119.     function postXmlCurl($xml, $url, $useCert = false, $second = 30)
  120.     {      
  121.         $ch = curl_init();
  122.         //设置超时
  123.         curl_setopt($ch, CURLOPT_TIMEOUT, $second);
  124.         curl_setopt($ch,CURLOPT_URL, $url);
  125.         curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false);
  126.         curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,false);//严格校验
  127.         //设置header
  128.         curl_setopt($ch, CURLOPT_HEADER, FALSE);
  129.         //要求结果为字符串且输出到屏幕上
  130.         curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

  131.         //post提交方式
  132.         curl_setopt($ch, CURLOPT_POST, TRUE);
  133.         curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
  134.         //运行curl
  135.         $data = curl_exec($ch);
  136.         //返回结果
  137.         curl_close($ch);
  138.         return $data;
  139.     }
  140.     /**
  141.      * 将xml转为array
  142.      * @param string $xml
  143.      */
  144.     function FromXml($xml)
  145.     {   
  146.         if(!$xml){
  147.             echo "xml数据异常!";
  148.         }
  149.         //将XML转为array
  150.         //禁止引用外部xml实体
  151.         libxml_disable_entity_loader(true);
  152.         $data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);      
  153.         return $data;
  154.     }
  155.     /**
  156.      * 获取jsapi支付的参数
  157.      * @param array $UnifiedOrderResult 统一支付接口返回的数据
  158.      * @return json数据,可直接填入js函数作为参数
  159.      */
  160.     function GetJsApiParameters(){
  161.         $UnifiedOrderResult = unifiedOrder();
  162.         if(!array_key_exists("appid", $UnifiedOrderResult)
  163.         || !array_key_exists("prepay_id", $UnifiedOrderResult)
  164.         || $UnifiedOrderResult['prepay_id'] == "")
  165.         {
  166.             echo $UnifiedOrderResult['err_code_des'];
  167.             exit;
  168.         }
  169.         $da = array();
  170.         $da['appId'] = $UnifiedOrderResult["appid"];
  171.         $timeStamp = time();
  172.         $da['timeStamp'] = "$timeStamp";
  173.         $da['nonceStr'] = getNonceStr();
  174.         $da['package'] = "prepay_id=" . $UnifiedOrderResult['prepay_id'];
  175.         $da['signType'] = 'MD5';
  176.         //签名步骤一:按字典序排序参数
  177.         ksort($da);
  178.         $string = ToUrlParamss($da);
  179.         //签名步骤二:在string后加入KEY
  180.         $string = $string . "&key=".KEY;
  181.         //签名步骤三:MD5加密
  182.         $string = md5($string);
  183.         //签名步骤四:所有字符转为大写
  184.         $result = strtoupper($string);
  185.         $da['paySign'] = $result;
  186.         $parameters = json_encode($da);
  187.         return $parameters;
  188.     }
  189.     $da = GetJsApiParameters();
  190.     var_dump($da);
  191. ?>
  192. <html>
  193. <head>
  194.      <meta http-equiv="content-type" content="text/html;charset=utf-8"/>
  195.      <meta name="viewport" content="width=device-width, initial-scale=1"/>
  196.      <title>微信h5支付-王文晓</title>
  197.      <script type="text/javascript">
  198.    //调用微信JS api 支付
  199.    function jsApiCall()
  200.    {
  201.        WeixinJSBridge.invoke(
  202.            'getBrandWCPayRequest',
  203.            <?php echo $da; ?>,
  204.            function(res){
  205.                WeixinJSBridge.log(res.err_msg);
  206.                alert(res.err_code+res.err_desc+res.err_msg);
  207.            }
  208.        );
  209.    }
  210.    function callpay()
  211.    {
  212.        if (typeof WeixinJSBridge == "undefined"){
  213.            if( document.addEventListener ){
  214.                document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
  215.            }else if (document.attachEvent){
  216.                document.attachEvent('WeixinJSBridgeReady', jsApiCall);
  217.                document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
  218.            }
  219.        }else{
  220.            jsApiCall();
  221.        }
  222.    }
  223.    </script>
  224. </head>
  225. <body>
  226.      <br/>
  227.      <font color="#9ACD32"><b>该笔订单支付金额为<span style="color:#f00;font-size:50px">1分</span>钱</b></font><br/><br/>
  228.    <div align="center">
  229.        <button style="width:210px; height:50px; border-radius: 15px;background-color:#FE6714; border:0px #FE6714 solid; cursor: pointer;  color:white;  font-size:16px;" type="button" onclick="callpay()" >立即支付</button>
  230.    </div>
  231. </body>
  232. </html>
复制代码
二、代码使用方法
0、首先把代码复制到新建的h5pay.php中。
1、第二行APPID跟第五行APPSECRET改成自己微信公众号的(如下图,在后台基本配置里找)

2、第三行的MCHID是微信支付商户号。在你申请微信支付的时候,在往你的邮件里发送了两封邮件,里面就有这个,八位数字
3、第四行的KEY在pay.weixin.qq.com中设置,32位,位置如下图

4、把测试目录跟测试白名单填好,如果你的文件放在http://域名/abc/def/h5pay.php那么测试目录填写
http://域名/abc/def/,白名单填写要测试的微信号,记得要关注公众号

5、第43行改成自己微信号在公众号的的openid。(上面代码是我的openid)在h5网页中一般用oauth获取openid。不清楚的可以看我另一篇文章

微信公众平台oauth2.0网页授权
至此,上面的代码配置完毕,访问次代码你会得到如下截图。

上面类似乱码的是我var_dump出来看有没有错误的。
点击支付,弹出微信支付

打上密码,支付成功


怎么样,照着做到现在,你应该成功了吧?微信h5是不是很简单?就算自己一行一行看上面的代码,也很快就看懂了。当然,这只是教你怎么用上面的代码,下面说说流程跟每个方法的作用。



三、H5微信支付流程1、大体的流程
(1)、把自己生成的订单号、名称、openid等相关信息以XML的方式POST到微信指定的网址中(统一下单)。
(2)、微信服务器返回XML格式的信息,保存一堆信息。取出其中
prepay_id。
(3)、把
prepay_id与一堆信息以微信指定的方式排序,按照顺序转换为json对象,放在前端JS代码微信指定的地方。
(4)、点击支付时、如果上面的排序没有错,执行支付操作。

2、流程与代码详细解说
(1)、本次用户买商品的订单相关信息收集好。关于订单必须要填写的是:36行的商品描述、37行的自己生成的订单号、38行本次支付的费用。分为单位。我写的1就是1分。其他必须的支付参数有 appid(微信appid),mch_id(商户号),trade_type(根据使用场景不同,该值也是不同的,微信外部为”NATIVE“,微信内部为”JSAPI“),nonce_str(32位随机字符串),spbill_create_ip(我的IP),notify_url(支付回调地址,支付完成后微信会POST XML类型的信息给此地址),sign(签名,签名的组成为50-57行。四步,代码注释中有解释,不再重复)。没解释的是不是必须的。
(2)、59行用toXNL函数把这些信息转换为XML,60行把XML发送到微信指定的统一下单地址中。61行把微信返回XML信息转化为数组。
(3)、进入161行的GetJsApiParameters函数。163-169是无关紧要的验证,171-176是下一步要用到的信息。其中175的package要用到上面微信返回来的XML中的prepay_id。177-185按照微信的规定生成数据。把上面这些数组转换为json对象。放在前端页面JS代码中。在204行。当点击“立即支付”后触发callpay()函数,即可启动微信支付。


本章结束语可能详细解说大家有点晕,毕竟代码也没有多少、大家认真琢磨下很快会掌握的。
本文写作用时:7小时

更多重量级教程,我的博客http://www.wangwenxiao.com



回复

使用道具 举报

最佳答案
0 

0

主题

2

帖子

43

积分

新人求带

积分
43
发表于 2016-8-3 21:41:06 | 显示全部楼层
谢大神分享,唉!最近被支付搞得头都大了,稍不注意就是各种问题
回复 支持 反对

使用道具 举报

最佳答案
0 

0

主题

88

帖子

1021

积分

专家路上

积分
1021
发表于 2016-8-4 03:35:29 | 显示全部楼层
小白路过,
回复

使用道具 举报

最佳答案
14 

68

主题

244

帖子

3144

积分

专家路上

积分
3144
发表于 2016-8-4 09:41:04 | 显示全部楼层
这么详细~真心非常感谢
回复 支持 反对

使用道具 举报

最佳答案
0 

0

主题

11

帖子

326

积分

略知一二

积分
326
发表于 2016-8-4 09:46:45 | 显示全部楼层
感谢分享
回复

使用道具 举报

最佳答案
0 

3

主题

59

帖子

158

积分

新人求带

积分
158
发表于 2016-8-4 09:55:39 | 显示全部楼层
挺好的。不错的。
回复 支持 反对

使用道具 举报

最佳答案
0 

0

主题

6

帖子

48

积分

新人求带

积分
48
发表于 2016-8-4 14:21:28 | 显示全部楼层
不错 感谢分享
回复 支持 反对

使用道具 举报

最佳答案
0 

0

主题

2

帖子

43

积分

新人求带

积分
43
发表于 2016-8-5 11:26:32 | 显示全部楼层
3Q,谢谢楼主分享。。。。
回复 支持 反对

使用道具 举报

最佳答案
0 

0

主题

11

帖子

420

积分

略知一二

积分
420
发表于 2016-8-9 22:48:33 | 显示全部楼层
怎么接收是否已经付款
回复 支持 反对

使用道具 举报

最佳答案
0 

1

主题

40

帖子

556

积分

略知一二

积分
556
发表于 2016-8-10 10:16:09 | 显示全部楼层
这个很好,谢谢楼主!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



www.henkuai.com—微信开发者的分享交流平台,专注微信开发生态。

天津市滨海新区
中新生态城中成大道生态建设公寓9号楼3层301

微信公众号

广告推广
QQ:805874290

市场合作
zhongcong@henkuai.com