1. 程式人生 > >PHP實現微信支付(微信小程式版)

PHP實現微信支付(微信小程式版)

先解答上一篇部落格中form_id的獲取方法: js程式碼


Page({
  formSubmit: function (e) {
    console.log("formid:"+e.detail.formId)
  },
})

wxml程式碼

<form bindsubmit="formSubmit" report-submit="ture">
    <button formType="submit">submit</button>
</form>

結果: 在這裡插入圖片描述

注意兩點

  1. 表單wxml中需要加入,普通表單是不攜帶formId。
  2. formId只在真器中才能獲取,開發者工具無法獲取。

下面開始講解PHP實現微信小程式支付

準備工作:

1.你的小程式做過微信認證並且開通微信支付(相信能學程式設計的都不是愚笨之人,跟著微信指引填寫資料就行,剩下就等他客服打電話給你) 2.微信小程式appid 3.微信商戶號mch_id(開戶郵件裡面有) 4.微信API金鑰key(登入商戶平臺->賬戶中心->API安全自行設定)

開發步驟:

1.獲取prepay_id 2.獲取paySign

封裝方法程式碼:

<?php
namespace App\libs\Pay;
use App\libs\HttpUtils\HttpUtils;
class WxPay
{
    const url = "https://api.mch.weixin.qq.com/pay/unifiedorder";  
    const key = "";                                                                           
    const appid = "";                                                                       
    const mch_id = "";
    const total_fee = "";
    const notify_url = "";
    const body = "";

    /**
     * 微信統一下單 所有引數為必填項 資料傳輸格式為xml 提交方式POST 其他可選項參照微信官方文件
     * @param url 統一下單地址
     * @param key API金鑰 微信支付平臺自定義
     * @param appid 小程式appid 微信公眾平臺獲取
     * @param mch_id 商戶號 微信支付平臺獲取
     * @param total_fee 金額 單位:分  作者寫的東西金額是固定的,可作為引數傳入函式
     * @param notify_url 非同步接收微信支付結果通知的回撥地址
     * @param nonce_str  隨機字串,長度要求在32位以內 下方已給出方法str_rand()
     * @param sign 通過簽名演算法計算得出的簽名值 簽名演算法getSignTemp():1.引數名ASCII碼從小到大排序(字典序)2.如果引數的值為空不參與簽名;3.引數名區分大小寫 4.驗證呼叫返回或微信主動通知簽名時,傳送的sign引數不參與簽名,將生成的簽名與該sign值作校驗。
     * @param out_trade_no 商戶系統內部訂單號,要求32個字元內,只能是數字、大小寫字母_-|*且在同一個商戶號下唯一
     * @param openid 使用者對應小程式的openid
     * @param body 商品描述 如:騰訊充值中心-QQ會員充值
     * @param trade_type 小程式取值如下:JSAPI 其他支付參照官方文件
     */
    public static function PrePay($out_trade_no,$openid){
        $nonce_str=self::str_rand();      //生成隨機32位字串
        $SignTemp=self::getSignTemp($nonce_str,$openid,$out_trade_no);  // 生成簽名
        $data=[
            "appid"=>self::appid,     //appid
            "mch_id"=>self::mch_id, //商戶號
            "nonce_str"=>$nonce_str,  //隨機字串
            "sign"=>$SignTemp,    //簽名
            "body"=>self::body,   //商品描述
            "out_trade_no"=>$out_trade_no, //訂單號
            "total_fee"=>self::total_fee,   //金額
            "trade_type"=>"JSAPI",  //支付型別 小程式取值如下:JSAPI 其他支付參照官方文件
            "notify_url"=>self::notify_url,  //非同步接收微信支付結果通知的回撥地址
            "openid"=>$openid   //使用者對應小程式的openid
        ];
        $result=HttpUtils::curl(self::url,$params=self::XmlAndArray($data,"array"),$ispost=1,$https=1);//請求介面得到預支付資訊,curl方法在前一篇文章已經貼出,引數必須為xml格式,方法已給出XmlAndArray()
        return self::XmlAndArray($result,"xml"); //轉化為陣列方便取值,返回結果
    }
    public static function getPaySign($prepay_id,$nonce_str){
        $timeStamp=time();//時間戳
        $PaySign=md5("appId=".self::appid."&nonceStr=".$nonce_str."&package=prepay_id=".$prepay_id."&signType=MD5&timeStamp=".(string)$timeStamp."&key=".self::key);//生成支付簽名,嚴格按照官方要求填寫引數
        return ["PaySign"=>$PaySign,"timeStamp"=>$timeStamp];//返回簽名以及時間戳
    }

    private static function getSignTemp($nonce_str,$openid,$out_trade_no){
        $SignTemp="appid=".self::appid."&body=".self::body."&mch_id=".self::mch_id."&nonce_str=".$nonce_str."&notify_url=".self::notify_url."&openid=".$openid."&out_trade_no=".$out_trade_no."&total_fee=".self::total_fee."&trade_type=JSAPI";//生成下單簽名,嚴格按照官方要求填寫引數
        $SignTemp=md5($SignTemp."&key=".self::key);
        return $SignTemp; //返回簽名,統一下單需要呼叫
    }

    /**
     * 陣列與XML互轉 方便取資料
     * @param $data
     * @param $type 轉換物件
     * return 陣列或XML
     */

    private static function XmlAndArray($data,$type)
    {
        if(!$data){
            return false;
        }
        else if($type=="xml"){
            libxml_disable_entity_loader(true);
            $values = json_decode(json_encode(simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
            return $values;
        }
        else if($type=="array"){
            $xml="<xml>";
            foreach ($data as $key=>$val){

                $xml.="<".$key.">".$val."</".$key.">";

            }
            $xml.="</xml>";
            return $xml;
        }
    }
    /**
     * 隨機字串生成
     * @param $length 字串長度
     * @param $char  字符集
     * return $string 隨機字串
     */

    private static function str_rand($length = 32, $char = 'abcdefghijklmnopqrstuvwxyz0123456789') {
        $str ="";
        for ( $i = 0; $i < $length; $i++ )  {
            $str .= substr($char, mt_rand(0, strlen($char)-1), 1);
        }
        return $str;
    }
}

呼叫程式碼,我用的laravel框架,可以自行重構

<?php


namespace App\Http\Controllers\Pay;
use App\Http\Controllers\Controller;
use App\libs\Pay\WxPay;
use Illuminate\Http\Request;


class PayController extends Controller
{
    public function getPrePay(Request $request){
        $openid=$request->get("openid");    //接收openid
        $out_trade_no="";  //訂單號
        $PrePay=WxPay::PrePay($out_trade_no,$openid);  //統一下單
        if($PrePay["return_code"]=="FAIL"){
            /*返回錯誤*/
        }
        else{
            $PrePayId=$PrePay["prepay_id"];  // 獲取預支付id
            $PaySign=WxPay::getPaySign($PrePayId,$PrePay["nonce_str"]); 生成支付簽名
            echo json_encode(["prepay_id"=>$PrePayId,"paySign"=>$PaySign["PaySign"],"nonce_str"=>$PrePay["nonce_str"],"timeStamp"=>$PaySign["timeStamp"],"order_no"=>$out_trade_no]); //返回小程式中拉起支付所需要的資訊
        }
    }
}

小程式呼叫程式碼:

 wx.request({
      url: "你的伺服器端介面地址",
      data: {
        openid: "使用者的openid",
      },
      header: {
        'content-type': 'application/json'
      },
      success: function (res) {
        let prepay_id = res.data.prepay_id  //預支付id
        let order_no = res.data.order_no    //訂單號
        let timeStamp = String(res.data.timeStamp)  //伺服器端返回的時間戳,必須強轉字串,微信要求的
        let nonceStr = res.data.nonce_str //伺服器返回的隨機字串
        let package = 'prepay_id=' + res.data.prepay_id //package需要這麼填寫,自己注意
        let paySign = res.data.paySign //支付簽名
        if (res.data.code == "") {  
         //伺服器端預支付處理失敗提示 
          wx.showToast({
            title: '下單失敗',
            icon: 'none',
            duration: 1000
          })
        }
        else {
          // 拉起微信支付
          wx.requestPayment({
            'timeStamp': timeStamp,
            'nonceStr': nonceStr,
            'package': package,
            'signType': 'MD5',
            'paySign': paySign,
             success: function (res) {
              //介面呼叫成功的回撥函式
             },
             fail:function(res){
               //介面呼叫失敗的回撥函式
             },
             complete:function(res){
               //介面呼叫結束的回撥函式(呼叫成功、失敗都會執行)
             }
          })
        }
      }
    })

總結

  1. 最重要的就是生成簽名一定要按照官方給出的文件,引數順序不能搞錯!!!
  2. 所有介面返回的都是xml,為了方便我都轉化成陣列,方法也在程式碼中給出。
  3. 隨機字串生成後,會在統一下單介面返回值中給出,生成PaySign簽名時延用就行,第一次開發就卡在這,能拉起支付,然後閃退說PaySign錯誤
  4. 統一下單介面返回的prepay_id就是模板訊息需要的引數,支付成功後就可以用此引數呼叫模板訊息提示使用者
  5. 作者自己寫了個小程式BadBoy,有興趣的可以搜尋來玩玩,其中的程式碼可找作者索取
  6. 微信小程式交流群:895964328 php交流群:165728481