本文用于让读者快速上手微信小程序支付

提前准备

1:appid

2:商户号

3:商户密钥

大致的流程:就是小程序发起https请求将启动时获得的openId传递给后端的处理程序,返回微信函数wx.requestPayment必要的字段,然后填进去,调用函数wx.requestPayment即可

首先我们来看看微信开发者文档中的wx.requestPayment需要的必要字段

,共有5个,都是需要与我们的后端进行通信,后端再生成订单,一系列的验证打包数据进行填入,所以这里着重讲后端的处理过程,前端小程序就调用程序,填入参数就完事了。

 

后端实现生成订单(PHP)

①初始化配置(initWx.php)

//接受参数
$openid=$_POST['openid'];

//配置
$appid='wxxxxxxxxx';                    //appid
$secret= 'xxxxxxxx';                    //app密钥
$key = 'xxxxxxxx';                      //商户密钥
$mch_id ='12345678';                    //商户id
$fee='1';                               //支付金额,单位(分)

//url
$notifyURL='https://xxx/notify.php'; //回调程序地址,用于用户支付成功后,微信会与这个notify文件进行通信,告诉结果
$unifiedorderURL='https://api.mch.weixin.qq.com/pay/unifiedorder';//统一订单地址
$description='xxx消费';      

//商品号,10位现在时间,22位随机数,自己随便创建,不长于32位
$out_trade_no=time();
$out_trade_no.=WxPay::createNoncestr(22);

$weixinpay=new WxPay($appid,$openid,$mch_id,$key,$out_trade_no,$description,$fee,$notifyURL,$unifiedorderURL,'JSAPI');//新建一个对象,最后一个参数JSAPI是怎么回事呢。
<img class="alignnone size-medium wp-image-494" src="http://bayaojiu.com/blog/wp-content/uploads/2018/01/微信图片_20180107135806-300x43.png" alt="" width="300" height="43" data-mce-src="http://bayaojiu.com/blog/wp-content/uploads/2018/01/微信图片_20180107135806-300x43.png">根据微微信接口说明,公众号支付,小程序支付均是JSAPI类型,
$return =$weixinpay->pay();//调用对象中的pay()方法
echo  json_encode($return);//将pay方法的数据返回给小程序

WxPay类里面有什么呢,不想看流程的同学可以直接跳到下面的终WxPay类。

——————————–流程分解

第一个肯定是将数据存储是吧

  protected $appid;
    protected $mch_id;
    protected $key;
    protected $openid;
    protected $out_trade_no;
    protected $body;
    protected $total_fee;
    protected $notifyURL;
    protected $unfiedorderURL;
    protected $trade_type;

    public function __construct($appid, $openid, $mch_id, $key,$out_trade_no,$body,$total_fee,$notifyURL,$unifiedorderURL,$trade_type){
        $this->appid=$appid;
        $this->mch_id=$mch_id;
        $this->openid = $openid;
        $this->key = $key;
        $this->out_trade_no = $out_trade_no;
        $this->body = $body;
        $this->total_fee = $total_fee;
        $this->notifyURL=$notifyURL;
        $this->unfiedorderURL=$unifiedorderURL;
        $this->trade_type=$trade_type;
    }

OK,构造函数写完,就开始写我们刚才所说的pay()函数了,根据微信的文档,首先,要需要进行统一下单,即不管你是扫码支付,小程序支付,公众号支付,刷卡都必须需要这个过程,统一下单的URL是就是上面的$unifiedorderURL=’https://api.mch.weixin.qq.com/pay/unifiedorder’;(统一订单地址),我们要将统一下单必要的参数传入即可

微信统一下单必要参数,共有10个

 

 

其他参数都基本具有了,随机字符串就使用字符串数组随机发生成就OK了,简单。剩下比较容易出错的就是签名,sign,其实所谓的签名就是将上面的一些数据进行键名键值对应法,就是我们HTTP传递查询部件(name=yaojiu&sex=male)这样组合而已,按照微信规定组合一串东西后,进行MD5加密,然后这串就是sign签名,说白了就是防止数据丢失,然后检验数据的MD5的完整性的意思。

开工

//将除了sign外的所有字段放入数组,若不想进行排序,则放入数组的时候自己按字段名称a-z小到大放入数组即可
 $parameters=array(
            'appid'=>$this->appid,
            'mch_id'=>$this->mch_id,
            'nonce_str'=>$this->createNoncestr(),
            'body'=>$this->body,
            'out_trade_no'=>$this->out_trade_no,
            'total_fee'=>$this->total_fee,
            'spbill_create_ip'=>$_SERVER['REMOTE_ADDR'],
            'notify_url'=> $this->notifyURL,
            'trade_type'=>$this->trade_type
        );
//我们的支付类型是小程序,是JSAPI,其他两种支付方式不需要绑定oepnid,所以我们动态加入openid
if($this->trade_type=='JSAPI')
            $parameters['openid']=$this->openid;

nonce_str就是随机字符串而已,我们写个生成随机字符串的方法给它

  public static function createNoncestr($strlength=32){
        //创建随机字符串
        $strings='abcdefghijklmnopqrstuvwxyz0123456789';
        $noncestr='';
        for($i=0;$i<$strlength;$i++)
            $noncestr.=$strings[mt_rand(0,35)];//mt_rand的效率是rand的4倍
        return $noncestr;
    }

到了这里,数组已经装好了,然后要按照(键值键名对应)再组装,前面数组排好了就很简单了,我们创建一个方法给它

   private function formatBizQueryParaMap($paraMap,$urlencode){//是否需要urlencode转义
        //格式化参数
        $buff="";
        ksort($paraMap);
        foreach ($paraMap as $k=>$v){
            if($urlencode){
                $v=urlencode($v);
            }
            $buff.=$k.'='.$v.'&';          //组合成键值键名对应
        }
        return $buff;
    }

到这步后,记得还要加上key字段,加上key字段,加上key字段,重要的事情说三遍,key字段就是商户密钥,然后调用MD5函数生成的字符串再进行大写转化即可得到sign字段

        //步骤一:按字典排序参数
        $string=$this->formatBizQueryParaMap($copyParameters,false);
        //步骤二:在string后面加key
        $string.='key='.$this->key;
        //步骤三:md5加密
        $string=md5($string);
        //步骤四:转大小写
        return strtoupper($string);

至此,10个字段全部获得,进行对微信同一订单接口进行通信

不过还需要将数组里10个字段的信息组合成XML的结构,即下面的机构,也是简单,直接取出去字符组合就可以了

<xml>
     <appid><appid/>
</xml>

写个函数给它

   public static function arrayToXml($arr){
        //数组转换成xml
        $xml="<xml>";
        foreach ($arr as $k=>$v){
                $xml.='<'.$k.'>'.$v.'</'.$k.'>';
        }
        $xml.='</xml>';
        return $xml;
    }

然后组合好后,我是使用CURL进行通信的

private 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); //严格校验
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);

        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20);
        curl_setopt($ch, CURLOPT_TIMEOUT, 40);
        set_time_limit(0);
        $data=curl_exec($ch);
        if($data){
            curl_close($ch);
            return $data;//成功后返回$data
        }
        else{
            $error=curl_errno($ch);
            curl_close($ch);
            throw new Exception("出错,出错码".$error);
        }
    }

然后微信返回的结果也是xml格式的,我们需要转换为数组

    public static function xmlToArray($xml){
        //xml转Array
        //禁止引用外部实体
        libxml_disable_entity_loader(true);
        $xmlstring = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
        $val=json_decode(json_encode($xmlstring),true);//将simplexml对象转换为json,再又json转换为数组
        return $val;
    }

数组里面prepay_id字段就是前面所有步骤的最终结果。

继续

微信规定,最终还必须要appId,timeStamp,nonceStr,package,signType这五个字段进行MD5加密

,package字段就是上面的prepay_id,其他的填上去就完事了

 $parameters=array(
            'appId'=>$this->appid,
            'timeStamp'=>''.time().'',
            'nonceStr'=>$this->createNoncestr(),
            'package' =>'prepay_id='.$unifiedorader['prepay_id'],
            'signType'=>'MD5'
        );
        //添加签名字符串
        $parameters['paySign']=$this->getSign($parameters);
        //添加商户订单号,用于以后查询订单是否完成支付之类的查询
        $parameters['out_trade_no']=$this->out_trade_no;
        return $parameters;

然后就将这些数据进行json_encode转换,发给前端小程序就OK了

——————————–流程分解

②最终WxPay类(WxPay.php)

<?php
/**
 * Created by PhpStorm.
 * User: Liang
 * Date: 12/7/2017
 * Time: 11:53 PM
 */
class WxPay{
    protected $appid;
    protected $mch_id;
    protected $key;
    protected $openid;
    protected $out_trade_no;
    protected $body;
    protected $total_fee;
    protected $notifyURL;
    protected $unfiedorderURL;
    protected $trade_type;

    public function __construct($appid, $openid, $mch_id, $key,$out_trade_no,$body,$total_fee,$notifyURL,$unifiedorderURL,$trade_type){
        $this->appid=$appid;
        $this->mch_id=$mch_id;
        $this->openid = $openid;
        $this->key = $key;
        $this->out_trade_no = $out_trade_no;
        $this->body = $body;
        $this->total_fee = $total_fee;
        $this->notifyURL=$notifyURL;
        $this->unfiedorderURL=$unifiedorderURL;
        $this->trade_type=$trade_type;
    }
    
    public function pay(){
        return $this->weixinapp();
    }
    private function weixinapp(){
        //统一下单接口
        $unifiedorader=$this->unifiedorder();
        $parameters=array(
            'appId'=>$this->appid,
            'timeStamp'=>''.time().'',
            'nonceStr'=>$this->createNoncestr(),
            'package' =>'prepay_id='.$unifiedorader['prepay_id'],
            'signType'=>'MD5'
        );
        $parameters['paySign']=$this->getSign($parameters);
        //返回商户号
        $parameters['out_trade_no']=$this->out_trade_no;
        return $parameters;
    }
    public function unifiedorder(){
        $parameters=array(
            'appid'=>$this->appid,
            'mch_id'=>$this->mch_id,
            'nonce_str'=>$this->createNoncestr(),
            'body'=>$this->body,
            'out_trade_no'=>$this->out_trade_no,
            'total_fee'=>$this->total_fee,
            'spbill_create_ip'=>$_SERVER['REMOTE_ADDR'],
            'notify_url'=> $this->notifyURL,
            'trade_type'=>$this->trade_type
        );
        if($this->trade_type=='JSAPI')
            $parameters['openid']=$this->openid;
        ksort($parameters);
        $parameters['sign']=$this->getSign($parameters);
        $xmlData=$this->arrayToXml($parameters);
        $return=$this->xmlToArray($this->postXmlCurl($xmlData,$this->unfiedorderURL,60));
        return $return;
    }
    private 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); //严格校验
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);

        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20);
        curl_setopt($ch, CURLOPT_TIMEOUT, 40);
        set_time_limit(0);
        $data=curl_exec($ch);
        if($data){
            curl_close($ch);
            return $data;
        }
        else{
            $error=curl_errno($ch);
            curl_close($ch);
            throw new Exception("出错,出错码".$error);
        }
    }
    public static function xmlToArray($xml){
        //xml转Array
        libxml_disable_entity_loader(true);
        $xmlstring = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
        $val=json_decode(json_encode($xmlstring),true);
        return $val;
    }
    public static function arrayToXml($arr){
        //数组转换成xml
        $xml="<xml>";
        foreach ($arr as $k=>$v){
                $xml.='<'.$k.'>'.$v.'</'.$k.'>';
        }
        $xml.='</xml>';
        return $xml;
    }
    public static function createNoncestr($strlength=32){
        //创建随机字符串
        $strings='abcdefghijklmnopqrstuvwxyz0123456789';
        $noncestr='';
        for($i=0;$i<$strlength;$i++)
            $noncestr.=$strings[mt_rand(0,35)];
        return $noncestr;
    }



    private function getSign($obj){
        //生成签名
        foreach ($obj as $k=>$v){
            $copyParameters[$k]=$v;
        }
        //步骤一:按字典排序参数
        $string=$this->formatBizQueryParaMap($copyParameters,false);
        //步骤二:在string后面加key
        $string.='key='.$this->key;
        //步骤三:md5加密
        $string=md5($string);
        //步骤四:转大小写
        return strtoupper($string);
    }
    private function formatBizQueryParaMap($paraMap,$urlencode){
        //格式化参数
        $buff="";
        ksort($paraMap);
        foreach ($paraMap as $k=>$v){
            if($urlencode){
                $v=urlencode($v);
            }
            $buff.=$k.'='.$v.'&';
        }
        return $buff;
    }
}

 

③回调地址(notify.php)

//获取参数,且转换成数组
$postXml = file_get_contents('php://input');
$arr =WxPay::xmlToArray($postXml);
if($redis->exists($arr['out_trade_no']))
    return;

//返回微信SUCCESS,不然微信会一天内再次进入这个地址多次,直到你发送SUCCESS
$returnArr=array();
if(checkSign($arr,$key)&&$arr['total_fee']==$fee){
    $returnArr['return_code']='SUCCESS';
    $returnArr['return_msg']='OK';
}
else{
    if($arr['total_fee']!=$fee)
        logWrite('fee is not equals original fee,now fee is '.$fee.',the base is '.$arr['total_fee']);
    $returnArr['return_code']='FAIL';
    $returnArr['return_msg']='签名失败';
}
$xml=WxPay::arrayToXml($returnArr);
echo $xml;


//后面就是你自己的业务流程了,支付成功该干嘛就干嘛

④最后说一下前端小程序

clickMe: function (event) {
   //事件触发,调起request请求,与php文件通信
   wx.request({
     //上面php文件的地址
     url: 'https://xxx/initWx.php',
     method:'POST',
     header:{'content-type':'application/x-www-form-urlencoded'},
     data: {openid:app.globalData.openid},//传递openid
     success:function(res){
       //成功的话,就将返回的数据取出
       console.log(res)
       wx.requestPayment({//调用函数,取出返回的数据填入字段即可
         timeStamp: res.data.timeStamp,
         nonceStr: res.data.nonceStr,
         package: res.data.package,
         signType: res.data.signType,
         paySign: res.data.paySign,
         success:function(){
             console.log(res)
         },
         fail:function(res){
            console.log(res)
       
         }
       })
     }
   })
  }

参考文档:

https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_1

https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7&index=5

https://mp.weixin.qq.com/debug/wxadoc/dev/api/api-pay.html

您或许感兴趣

[2017-12-20]微信开发-微信小程序获取openid(php实现)
[2017-12-20]微信开发-接触微信小程序(入门)

发表评论

电子邮件地址不会被公开。