PHP开发微信公众号的问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PHP开发微信公众号的问题相关的知识,希望对你有一定的参考价值。

我把我自己的服务器启用了HTTPS后,服务器上的微信公众号不能用了,后来发现微信公众号里面说明不支持HTTPS,只支持HTTP,我的web server是nginx,然后做了80端口跳转到443端口,请问如何解决?

微信是不支持https,为了解决这个问题,通常是不进行https验证,使用curl句柄模拟php请求 参考技术A 微信公众号只支持80端口 参考技术B 是你的配置有问题吧追问

微信公众平台上面写的不支持HTTPS

php开发微信APP支付接口

  简介: 之前做APP微信支付接口的时候总结的一些经验,在这里分享给大家。第一次在微信公众号发表文章,排版不好还请谅解。

开发流程

 1:用户在商户APP中选择商品,提交订单,选择微信支付。

 2:商户后台收到用户支付单,调用微信支付统一下单接口。参见                                         【https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1】。

 3:统一下单接口返回正常的prepay_id,再按签名规范重新生成签名后,将数据传输给           APP。参与签名的字段名为appid,partnerid,prepayid,noncestr,                             timestamp,   package。注意:package的值格式为Sign=WXPay。

 4:商户APP调起微信支付。api参见本章节                                                                           【https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_5

 5:商户后台接收支付通知。api参见                                                                                    【https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_7

 6:商户后台查询支付结果。,api参见                                                                                 https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_2

开发中

首先呢我们需要拿到三个参数(appid,mch_id,key),这三个参数分别对应的是 在微信开发平台中创建的移动应用appid,微信支付商户号商户支付秘钥,详情看参考【https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_1
然后我们先把统一下单所需要的参数列出来

 
   
   
 
  1. $request_data = array(

  2.      'appid' => C('WX_APPID'),   #应用APPID

  3.       'mch_id' => C('WX_MCHID'), #商户号

  4.       'trade_type' => 'APP',     #支付类型

  5.       'nonce_str' => \Org\Util\String::randString(30),  #随机字符串 不长于32位

  6.       'body' => '商品名称',   #商品名称

  7.       'out_trade_no' => '12345678912456', #商户后台订单号

  8.       'total_fee' => '1',                 #商品价格

  9.       'spbill_create_ip' => get_client_ip(), #用户端实际ip

  10.  );

这些都是请求参数必填项,其他参数请查看文档【https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1
下来我们就要使用这些参数生成签名了

 
   
   
 
  1. $request_data['sign'] = $this -> get_sign($request_data);

我们下来需要把微信请求的数据拼装成 xml格式,注意:xml数据要使用<![CDATA[]]>包括

 
   
   
 
  1. $xml_data = $this -> set_xmldata($request_data);

  2. 打印$xml_data结果如下

  3. <xml>

  4.    <appid><![CDATA[wx7ad3cc6c6111111]]></appid>

  5.    <mch_id><![CDATA[1494741111]]></mch_id>

  6.    <trade_type><![CDATA[APP]]></trade_type>

  7.    <nonce_str><![CDATA[WXXWkMDOgLIqhUnITfNrBbJEVGQdRO]]></nonce_str>

  8.    <body><![CDATA[u5546u54c1u540du79f0]]></body>

  9.    <out_trade_no><![CDATA[12345678912456]]></out_trade_no>

  10.    <total_fee><![CDATA[1]]></total_fee>

  11.    <spbill_create_ip><![CDATA[1.86.242.193]]></spbill_create_ip>

  12.    <notify_url><![CDATA[http://***/app/index.php/Home/Wxpay/wx_notify]]></notify_url>

  13.    <sign><![CDATA[EC0BFB3434A72F20C2CA3378BF07264C]]></sign>

  14. </xml>

现在就可以向微信发送请求了

 
   
   
 
  1. $res = $this -> send_prePaycurl($xml_data);

  2. 这是请求的返回值

  3. {

  4.  return_code: "SUCCESS",       #业务结果 只有这里返回SUCCESS才会有prepay_id

  5.  return_msg: "OK",             #返回结果描述

  6.  appid: "wx7ad3cc6c6111111",  #应用APPID

  7.  mch_id: "1494741111",         #商户号

  8.  nonce_str: "jkh9mmRlmSHBJxO0",   #随机字符串

  9.  sign: "AF3B26B1E58591D6565E61DDFBB7837B",  #签名

  10.  result_code: "SUCCESS",    #也是业务结果

  11.  prepay_id: "wx20171226005556c5c65b325a0132782836", #预支付交易会话标识,用于APP请求微信支付调用,有效期两小时

  12.  trade_type: "APP"  #支付类型

  13. }

到这里拿到prepay_id还没完我们还需要对返回的数据进行二次签名

 
   
   
 
  1. if($res['return_code'] == 'SUCCESS' && $res['result_code'] == 'SUCCESS'){

  2.       $two_data['appid'] = C('WX_APPID');  #APPID

  3.       $two_data['partnerid'] = C('WX_MCHID');  #商户号

  4.       $two_data['prepayid'] = $res['prepay_id'];  //预支付交易会话标识

  5.       $two_data['noncestr'] = \Org\Util\String::randString(30);  

  6.       $two_data['timestamp'] = time();   #时间戳

  7.       $two_data['package'] = "Sign=WXPay";   #固定值

  8.       $two_data['sign'] = $this -> get_twosign($two_data);  #二次签名

  9.       $this->ajaxReturn(array('code'=>200,'info'=>$two_data));

  10. }else{

  11.       $this->ajaxReturn(array('code'=>201,'info'=>$res['err_code_des']));

  12. }

然后就可以在商户APP端通过prepayid进行支付了
下面我们来列出上面调用的几个公共方法

 
   
   
 
  1.    //一次签名的函数

  2.    private function get_sign($data){

  3.        ksort($data);

  4.        $str = '';

  5.        foreach ($data as $key => $value) {

  6.            $str .= !$str ? $key . '=' . $value : '&' . $key . '=' . $value;

  7.        }

  8.        $str.='&key='.C('WX_KEY');

  9.        $sign = strtoupper(md5($str));

  10.        return $sign;

  11.    }

  12.    //二次签名的函数

  13.    private function get_twosign($data){

  14.        $sign_data = array(

  15.            "appid"=>$data['appid'],

  16.            "partnerid"=>$data['partnerid'],

  17.            "prepayid"=>$data['prepayid'],

  18.            "noncestr"=>$data['noncestr'],

  19.            "timestamp"=>$data['timestamp'],

  20.            "package"=>$data['package'],

  21.        );

  22.        return $this -> get_sign($sign_data);

  23.    }

  24.    //生成xml格式的函数

  25.    private function set_xmldata($data) {

  26.        $xmlData = "<xml>";

  27.          foreach ($data as $key => $value) {

  28.           $xmlData.="<".$key."><![CDATA[".$value."]]></".$key.">";

  29.          }

  30.          $xmlData = $xmlData."</xml>";

  31.          return $xmlData;

  32.    }

  33.    //通过curl发送数据给微信接口的函数

  34.    private function send_prePaycurl($xmlData) {

  35.        $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";

  36.        $header[] = "Content-type: text/xml";

  37.        $curl = curl_init();

  38.        curl_setopt($curl, CURLOPT_HTTPHEADER, $header);

  39.        curl_setopt($curl, CURLOPT_URL, $url);

  40.        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);

  41.        curl_setopt($curl, CURLOPT_POST, 1);

  42.        curl_setopt($curl, CURLOPT_POSTFIELDS, $xmlData);

  43.        $data = curl_exec($curl);

  44.        if (curl_errno($curl)) {

  45.            print curl_error($curl);

  46.        }

  47.        curl_close($curl);

  48.        return $this -> _xmldataparse($data);

  49.    }

  50.    //xml数据解析函数

  51.    private function _xmldataparse($data){

  52.        $msg = array();

  53.        $msg = (array)simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA);

  54.        return $msg;

  55.    }

微信回调

支付有了,肯定还得有回调

 
   
   
 
  1.    //微信回调

  2.    public function wx_notify(){  

  3.       //允许从外部加载XML实体(防止XML注入攻击)

  4.        libxml_disable_entity_loader(true);  

  5.        $postStr = $this -> post_data();  #接收微信返回数据xml格式

  6.        $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);   #xml格式数据转换成对象

  7.        $arr = $this -> object_toarray($postObj); #对象转成数组  

  8.        ksort($arr);   # 对数据进行排序  

  9.        $str = $this -> params_tourl($arr);  #对数据拼接成字符串

  10.        $user_sign = strtoupper(md5($str));   //把微信返回的数据进行再次签名

  11.       //验证签名

  12.        if($user_sign == $arr['sign']){

  13.            //验证签名成功  处理商户订单逻辑

  14.            //给微信返回接收到数据通知

  15.            return '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';

  16.        }else{

  17.            //签名验证失败   微信会再次访问回调方法

  18.            return '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';

  19.        }

  20.    }

回调用到的方法如下

 
   
   
 
  1. // 接收post数据  

  2.    /*  

  3.    *  微信是用$GLOBALS['HTTP_RAW_POST_DATA'];这个函数接收post数据的  

  4.    */  

  5.    public function post_data(){  

  6.        $receipt = $_REQUEST;  

  7.        if($receipt==null){  

  8.            $receipt = file_get_contents("php://input");  

  9.            if($receipt == null){  

  10.                $receipt = $GLOBALS['HTTP_RAW_POST_DATA'];  

  11.            }  

  12.        }  

  13.        return $receipt;  

  14.    }  


  15.    //把对象转成数组  

  16.    public function object_toarray($arr) {  

  17.        if(is_object($arr)) {  

  18.            $arr = (array)$arr;  

  19.        } if(is_array($arr)) {  

  20.            foreach($arr as $key=>$value) {  

  21.                $arr[$key] = $this->object_toarray($value);  

  22.            }  

  23.        }  

  24.        return $arr;  

  25.    }  



  26.     /**  

  27.     * 格式化参数格式化成url参数  

  28.     */  

  29.    private function params_tourl($arr)  

  30.    {  

  31.        $weipay_key = C('WX_KEY');//微信的key,这个是微信支付给你的key,不要瞎填。  

  32.        $buff = "";  

  33.        foreach ($arr as $k => $v)  

  34.        {  

  35.            if($k != "sign" && $v != "" && !is_array($v)){  

  36.                $buff .= $k . "=" . $v . "&";  

  37.            }  

  38.        }  

  39.        $buff = trim($buff, "&");  

  40.        return $buff.'&key='.$weipay_key;  

  41.    }

这是最后完成的功能

下面分享一下全部的代码

 
   
   
 
  1. <?php

  2. namespace Home\Controller;

  3. use Think\Controller;

  4. /**

  5. * php开发微信app支付接口

  6. * @global  WX_APPID  开放平台->移动应用appid

  7. * @global  WX_MCHID  微信支付商户号            

  8. * @global  WX_KEY    商户支付秘钥              

  9. * @author cnavaslh <admin@lihui521.cn> 2017-12-23

  10. */

  11. class WxpayController extends Controller

  12. {

  13.    /**

  14.     * 微信支付统一下单 >>> 生成预支付交易单

  15.     */

  16.    public function wx_pay(){


  17.       $request_data = array(

  18.             'appid' => C('WX_APPID'),  #应用APPID

  19.             'mch_id' => C('WX_MCHID'), #商户号

  20.             'trade_type' => 'APP',     #支付类型

  21.             'nonce_str' => \Org\Util\String::randString(30),  #随机字符串 不长于32位

  22.             'body' => '商品名称',       #商品名称

  23.             'out_trade_no' => '12345678912456',                    #商户后台订单号

  24.             'total_fee' => '1',        #商品价格

  25.             'spbill_create_ip' => get_client_ip(),#用户端实际ip

  26.        );        

  27.        // 获取签名

  28.        $request_data['sign'] = $this -> get_sign($request_data);

  29.        // 拼装数据

  30.        $xml_data = $this -> set_xmldata($request_data);



  31.        // 发送请求

  32.        $res = $this -> send_prePaycurl($xml_data);

  33.        $this->ajaxReturn($res);

  34.        if($res['return_code'] == 'SUCCESS' && $res['result_code'] == 'SUCCESS'){

  35.            $two_data['appid'] = C('WX_APPID');  #APPID

  36.            $two_data['partnerid'] = C('WX_MCHID');  #商户号

  37.            $two_data['prepayid'] = $res['prepay_id'];  //预支付交易会话标识

  38.            $two_data['noncestr'] = \Org\Util\String::randString(30);  

  39.            $two_data['timestamp'] = time();

  40.            $two_data['package'] = "Sign=WXPay";

  41.            $two_data['sign'] = $this->get_twosign($two_data);

  42.            $this->ajaxReturn(array('code'=>200,'info'=>$two_data));

  43.        }else{

  44.            $this->ajaxReturn(array('code'=>201,'info'=>$res['err_code_des']));

  45.        }

  46.    }


  47.    //通过curl发送数据给微信接口的函数

  48.    private function send_prePaycurl($xmlData) {

  49.        $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";

  50.        $header[] = "Content-type: text/xml";

  51.        $curl = curl_init();

  52.        curl_setopt($curl, CURLOPT_HTTPHEADER, $header);

  53.        curl_setopt($curl, CURLOPT_URL, $url);

  54.        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);

  55.        curl_setopt($curl, CURLOPT_POST, 1);

  56.        curl_setopt($curl, CURLOPT_POSTFIELDS, $xmlData);

  57.        $data = curl_exec($curl);

  58.        if (curl_errno($curl)) {

  59.            print curl_error($curl);

  60.        }

  61.        curl_close($curl);

  62.        return $this->_xmldataparse($data);

  63.    }


  64.    //xml数据解析函数

  65.    private function _xmldataparse($data){

  66.        $msg = array();

  67.        $msg = (array)simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA);

  68.        return $msg;

  69.    }


  70.    //生成xml格式的函数

  71.    private function set_xmldata($data) {

  72.        $xmlData = "<xml>";

  73.          foreach ($data as $key => $value) {

  74.           $xmlData.="<".$key."><![CDATA[".$value."]]></".$key.">";

  75.          }

  76.          $xmlData = $xmlData."</xml>";

  77.          return $xmlData;

  78.    }


  79.    //一次签名的函数

  80.    private function get_sign($data){

  81.        ksort($data);

  82.        $str = '';

  83.        foreach ($data as $key => $value) {

  84.            $str .= !$str ? $key . '=' . $value : '&' . $key . '=' . $value;

  85.        }

  86.        $str.='&key='.C('WX_KEY');

  87.        $sign = strtoupper(md5($str));

  88.        return $sign;

  89.    }


  90.    //二次签名的函数

  91.    private function get_twosign($data){

  92.        $sign_data = array(

  93.            "appid"=>$data['appid'],

  94.            "partnerid"=>$data['partnerid'],

  95.            "prepayid"=>$data['prepayid'],

  96.            "noncestr"=>$data['noncestr'],

  97.            "timestamp"=>$data['timestamp'],

  98.            "package"=>$data['package'],

  99.        );

  100.        return $this->get_sign($sign_data);

  101.    }


  102.    //微信回调

  103.    public function wx_notify(){  

  104.       //允许从外部加载XML实体(防止XML注入攻击)

  105.        libxml_disable_entity_loader(true);  

  106.        $postStr = $this -> post_data();//接收post数据  

  107.        $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);  

  108.        $arr = $this -> object_toarray($postObj);//对象转成数组  

  109.        ksort($arr);// 对数据进行排序  

  110.        $str = $this -> params_tourl($arr);//对数据拼接成字符串

  111.        $user_sign = strtoupper(md5($str));  

  112.        if($user_sign == $arr['sign']){//验证签名

  113.            return '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';

  114.        }else{

  115.            return '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';

  116.        }

  117.    }  


  118.    // 接收post数据  

  119.    /*  

  120.    *  微信是用$GLOBALS['HTTP_RAW_POST_DATA'];这个函数接收post数据的  

  121.    */  

  122.    public function post_data(){  

  123.        $receipt = $_REQUEST;  

  124.        if($receipt==null){  

  125.            $receipt = file_get_contents("php://input");  

  126.            if($receipt == null){  

  127.                $receipt = $GLOBALS['HTTP_RAW_POST_DATA'];  

  128.            }  

  129.        }  

  130.        return $receipt;  

  131.    }  


  132.    //把对象转成数组  

  133.    public function object_toarray($arr) {  

  134.        if(is_object($arr)) {  

  135.            $arr = (array)$arr;  

  136.        } if(is_array($arr)) {  

  137.            foreach($arr as $key=>$value) {  

  138.                $arr[$key] = $this->object_toarray($value);  

  139.            }  

  140.        }  

  141.        return $arr;  

  142.    }  



  143.     /**  

  144.     * 格式化参数格式化成url参数  

  145.     */  

  146.    private function params_tourl($arr)  

  147.    {  

  148.        $weipay_key = C('WX_KEY');//微信的key,这个是微信支付给你的key,不要瞎填。  

  149.        $buff = "";  

  150.        foreach ($arr as $k => $v)  

  151.        {  

  152.            if($k != "sign" && $v != "" && !is_array($v)){  

  153.                $buff .= $k . "=" . $v . "&";  

  154.            }  

  155.        }  

  156.        $buff = trim($buff, "&");  

  157.        return $buff.'&key='.$weipay_key;  

  158.    }      


  159. }


以上是关于PHP开发微信公众号的问题的主要内容,如果未能解决你的问题,请参考以下文章

开发微信公众号后台,原来这么简单?[微信公众号开发]

教你3分钟快速开发微信公众号[订阅号][服务号]

vux+vuex+vue+Es6开发微信公众号的坑

NodeJs 开发微信公众号微信事件交互

微信开发公众号1

在新浪SAE上搭建微信公众号的python应用