微信小程序調微信支付

今天寫小程序的支付接口,參照的固然是微信支付API了。(結尾附上第二步所有代碼php版)javascript

另外,我也參照了簡書上的這篇文章,淺顯易懂:https://www.jianshu.com/p/72f5c1e3f8a5php

其實小程序中喚起微信支付不外乎如下幾個步驟:前端

1.獲取openidjava

小程序獲取openid是分兩個步驟的web

首先小程序前端經過wx.login獲取code,而後用這個code經過後臺接口內部訪問微信官方API獲取openidsession_key數據庫

https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=CODE&grant_type=authorization_codejson

紅字爲所需參數,appid和secret手動配置(開發者平臺申請),code就是小程序前端獲取的code了小程序

2.獲取prepay_idpaySign微信小程序

拿着剛纔的openid和訂單號(自定義訂單號)、訂單金額(手動輸入金額)以及其餘平臺參數拼接成一個xml文件做爲請求體api

經過後臺接口內部訪問微信API接口:https://api.mch.weixin.qq.com/pay/unifiedorder

若是參數無誤,將如期返回prepay_idpaySign

3.前端拉起支付

拿到了所需參數,前端就能夠發起:wx.requestPayment 來拉起支付了。

            wx.requestPayment({
                          'timeStamp':timeStamp,
                          'nonceStr': nonceStr,
                          'package': 'prepay_id='+res.data.prepay_id,
                          'signType': 'MD5',
                          'paySign': res.data._paySignjs,
                          'success':function(res){
                              console.log(res);
                          },
                          'fail':function(res){
                              console.log('fail:'+JSON.stringify(res));
                          }
                   })

如此就完成了小程序的支付,下面就要說一下今天踩的坑了。

 

1.appid問題

前期前端用的appid是用的他我的的appid,要知道小程序用戶受權小程序所生成的openid,是須要用appid來參與驗籤

的,這直接致使,後期將appid更換爲線上appid後,全部的測試用戶openid(這個openid在用戶受權以後直接存到數據

庫了,因此實際上咱們省略了第一步,直接從數據庫拿當前用戶的數據庫存儲的openid)走如上第二步的時候,都會報

openid和appid不匹配的錯誤,將前期用戶數據刪除,並更正appid以及其餘參數,從新受權後的openid就不會產生不匹

配的錯誤了。

 

第二步獲取prepay_idpaySign代碼(php版本):

<?php

if (!defined('BASEPATH')) exit('No direct script access allowed');

define(APPID, 'wx74bmn4nbf81f1593e');
define(MCHID, '1510771171');
define(KEY, 'WlUprCqVM53L4MnI6Dz2Nmz7f44');
define(APPSECRET, '3b8e3202c39c67712879f6724fc8b42');
define(NOTIFY_URL, SITE_URL.'web/dayrui/controllers/return_url.php');

require_once FCPATH . 'branch/fqb/D_Wxapp.php';
require 'WxPay.class.php';  


class Wxapp extends D_Wxapp {
...

  //支付,紅字參數依次是openid、訂單號、訂單說明、訂單金額
    public function pay(){
        $pay = $this->req;
        $weixinpay = new WxPay(APPID, $pay['openid'], MCHID, KEY, $pay['out_trade_no'], $pay['body'], $pay['total_fee']);  
        exit_json(1, $weixinpay->pay());
    }



}
WxPay.class.php能夠直接用,只是注意get_ip()函數是用來獲取客戶端ip的,這裏稍加封裝了一下,會在後面給出封裝函數代碼
//WxPay.class.php

<?php
class WxPay {
    
    protected $appid;
    protected $mch_id;
    protected $key;
    protected $openid;
    protected $out_trade_no;
    protected $body;
    protected $total_fee;
    
    function  __construct ($appid, $openid, $mch_id, $key, $out_trade_no, $body,$total_fee){
        $this->appid = $appid;
        $this->openid = $openid;
        $this->mch_id = $mch_id;
        $this->key = $key;
        $this->out_trade_no = $out_trade_no;
        $this->body = $body;
        $this->total_fee = $total_fee;
    }
    
    public function  pay (){
        //統一下單接口  
        
        $return=$this->weixinapp ();
        return $return;
    }
    
    //微信小程序接口  
    private function  weixinapp (){
        //統一下單接口  
        $unifiedorder=$this->unifiedorder ();
        $parameters=array ('appId'=>$this->appid ,//小程序ID  
        'timeStamp'=>''.time().'',//時間戳  
        'nonceStr'=>$this->createNoncestr (),//隨機串  
        'package'=>'prepay_id='.$unifiedorder['prepay_id'],//數據包  
        'signType'=>'MD5'//簽名方式  
        );
        $parameters['paySign']=$this->getSign ($parameters);
        return $parameters;
    }
    
    //統一下單接口  
    private function  unifiedorder (){
        $url='https://api.mch.weixin.qq.com/pay/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'=>get_ip(),
            'notify_url'=> NOTIFY_URL,
            'openid'=>$this->openid,
            'trade_type'=>'JSAPI'
        );
        //統一下單簽名  
        //pre($parameters);
        $parameters['sign']=$this->getSign ($parameters);
        $xmlData=$this->arrayToXml ($parameters);
        $return=$this->xmlToArray ($this->postXmlCurl ($xmlData,$url,60));
        return $return;
    }
    
    private static 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_setopt($ch,CURLOPT_CONNECTTIMEOUT ,20);
        curl_setopt($ch,CURLOPT_TIMEOUT ,40);
        set_time_limit(0);
        //運行curl  
        $data=curl_exec($ch);
        //返回結果  
        if ($data){
            curl_close($ch);
            return $data;
        }else {
            $error=curl_errno($ch);
            curl_close($ch);
            throw new WxPayException("curl出錯,錯誤碼:$error");
        }
    }
    //數組轉換成xml  
    private function  arrayToXml ($arr){
        $xml="<root>";
        foreach ($arr as $key=>$val){
            if (is_array($val)){
                $xml.="<".$key.">".arrayToXml ($val)."</".$key.">";
            }else {
                $xml.="<".$key.">".$val."</".$key.">";
            }
        }
        $xml.="</root>";
        return $xml;
    }
    
    //xml轉換成數組  
    private 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;
    }
    
    
    //做用:產生隨機字符串,不長於32位  
    private 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;
    }
    //做用:生成簽名  
    private function  getSign ($Obj){
        foreach ($Obj as $k=>$v){
            $Parameters[$k]=$v;
        }
        //簽名步驟一:按字典序排序參數  
        ksort($Parameters);
        $String=$this->formatBizQueryParaMap ($Parameters,false);
        //簽名步驟二:在string後加入KEY  
        $String=$String."&key=".$this->key ;
        //簽名步驟三:MD5加密  
        $String=md5($String);
        //簽名步驟四:全部字符轉爲大寫  
        $result_=strtoupper($String);
        return $result_;
    }
    ///做用:格式化參數,簽名過程須要使用  
    private function  formatBizQueryParaMap ($paraMap,$urlencode){
        $buff="";
        ksort($paraMap);
        foreach ($paraMap as $k=>$v){
            if ($urlencode){
                $v=urlencode($v);
            }
            $buff.=$k."=".$v."&";
        }
        $reqPar;
        if (strlen($buff)>0){
            $reqPar=substr($buff,0,strlen($buff)-1);
        }
        return $reqPar;
    }
}

get_ip():

function get_ip(){
    //判斷服務器是否容許$_SERVER
    if(isset($_SERVER)){    
        if(isset($_SERVER[HTTP_X_FORWARDED_FOR])){
            $realip = $_SERVER[HTTP_X_FORWARDED_FOR];
        }elseif(isset($_SERVER[HTTP_CLIENT_IP])) {
            $realip = $_SERVER[HTTP_CLIENT_IP];
        }else{
            $realip = $_SERVER[REMOTE_ADDR];
        }
    }else{
        //不容許就使用getenv獲取  
        if(getenv("HTTP_X_FORWARDED_FOR")){
              $realip = getenv( "HTTP_X_FORWARDED_FOR");
        }elseif(getenv("HTTP_CLIENT_IP")) {
              $realip = getenv("HTTP_CLIENT_IP");
        }else{
              $realip = getenv("REMOTE_ADDR");
        }
    }
    return $realip;
}      
相關文章
相關標籤/搜索