1. 程式人生 > >thinkphp微信支付-JSAPI

thinkphp微信支付-JSAPI

我只是一個小小前端,剛好專案需要,接受到thinkphp相關的專案,記錄一下,也希望能有幫助

在下載的demo中

WxpayAPI/lib/WxPay.Api.php 介面訪問類;
WxpayAPI/lib/WxPay.Config.php 配置賬號資訊;
WxpayAPI/lib/WxPay.Data.php 資料物件基礎類;
WxpayAPI/lib/WxPay.Exception.php 微信支付API異常類;
WxpayAPI/lib/WxPay.Notify.php 回撥基礎類
WxpayAPI/example/WxPay.JsApiPay.php JSAPI支付實現類

在正式使用php載入第三方庫前,需要將以下注釋掉

// WxpayAPI/lib/WxPay.Api.php 中
require_once "WxPay.Exception.php";
require_once "WxPay.Config.php";
require_once "WxPay.Data.php";

// WxpayAPI/lib/WxPay.Data.php 中
require_once "WxPay.Config.php";
require_once "WxPay.Exception.php";

// WxpayAPI/example/WxPay.JsApiPay.php中
require_once "../lib/WxPay.Api.php"
;

配置公眾號相關資訊WxpayAPI/lib/WxPay.Config.php

//微信公眾號身份的唯一標識。稽核通過後,在微信傳送的郵件中檢視
const APPID = '';

//受理商ID,身份標識
const MCHID = '';

//商戶支付金鑰Key。稽核通過後,在微信傳送的郵件中檢視
const KEY = '';

//JSAPI介面中獲取openid,稽核後在公眾平臺開啟開發模式後可檢視
const APPSECRET = '';

開搞,首先~需要將官方的第三方庫引入

<?php
namespace Home\Controller;
use Think\Controller
; use Org\Util\Email; // 載入第三方庫 // 注: 引入第三方類庫中的微信介面檔案,對於檔名含有.的,皆用#代替連線才能引入,字尾名不寫。 Vendor("WxpayAPI.example.log"); Vendor("WxpayAPI.example.WxPay#JsApiPay"); Vendor("WxpayAPI.lib.WxPay#Config"); Vendor("WxpayAPI.lib.WxPay#Data"); Vendor("WxpayAPI.lib.WxPay#Exception"); Vendor("WxpayAPI.lib.WxPay#Notify"); Vendor("WxpayAPI.lib.WxPay#Api");

然後我們開始寫支付相關的controller

class WxpayController extends Controller {
    public function pay() {
        // 這裡你可以處理從前端表單submit過來的資料
        $inputData = I('post.');
        // 對你的資料進行處理
        $data['name'] = $inputData['name'];
        $data['price'] = $inputData['price'];
        $data['email'] = $inputData['email'];
        $data['isPay'] = 0;
        // 然後儲存到你的資料庫,這裡需要獲取儲存到資料庫返回的ID值,因為我們需要通過這個ID值,在支付完成後,去更新這條資料的狀態,比如支付狀態isPay,在支付完成後,我們就需要在notify中去更新它的值為已經支付(1)
        $dataId = M('orders').add($data);

        // 這裡就開始走微信的支付流程了,坑比較多,一步一步說
        // 1、獲取使用者openid
        $tools = new \JsApiPay();
        $openId = $tools->GetOpenid();
    }
}

在上面的$openId = $tools->GetOpenid();中,你會發現,只要執行了這一句,$dataId就獲取不到了,我們觀察一下Wxpay.JsApiPay.php中的GetOpenid()這個函式

public function GetOpenid()
{
    // 通過code獲得openid
    if (!isset($_GET['code'])){
            // 觸發微信返回code碼
            $baseUrl = urlencode('http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'].$_SERVER['QUERY_STRING']);
            $url = $this->__CreateOauthUrlForCode($baseUrl);
            Header("Location: $url");
            exit();
    } else {
            // 獲取code碼,以獲取openid
            $code = $_GET['code'];
            $openid = $this->getOpenidFromMp($code);
            return $openid;
    }
}

可以觀察得到,在我們第一次開啟這個支付頁面的時候,它會嘗試獲取code,並且會進行頁面的跳轉,所以這個支付頁面其實是載入了兩次,因此$dataId</code>會獲取不到,但是這個<code>$dataId是必須的,否則無法進行資料的更新,我有試過在/Wxpay/pay 頁面把$dataId存進session,但是貌似不行,所以換了一種方法,我通過給GetOpenid()函式傳引數,然後通過該函式return回來

public function pay() {
    // 1、獲取使用者openid
    $tools = new \JsApiPay();
    $returnData = $tools->GetOpenid($dataId);
    // 獲取openid
    $openid = $returnData['openid'];
    // 獲取丟失的資料ID
    $dataId = $returnData['dataId'];
}

// 改造GetOpenid
public function GetOpenid($dataId)
{
    // 通過code獲得openid
    if (!isset($_GET['code'])){
            // 存進session
            session('dataId', $dataId);
            // 觸發微信返回code碼
            $baseUrl = urlencode('http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'].$_SERVER['QUERY_STRING']);
            $url = $this->__CreateOauthUrlForCode($baseUrl);
            Header("Location: $url");
            exit();
    } else {
            // 獲取code碼,以獲取openid
            $code = $_GET['code'];
            // 組裝返回資料
            $data['dataId'] = session('dataId');
            $data['openid'] = $this->getOpenidFromMp($code);
            return $data;
    }
}

能正確獲取新增資料的ID後,可以進行統一下單的操作了

public function pay() {
    // 通過ID去獲取訂單對應的資訊
    $orderData = M('orders')->where(array('id' => $dataId))->field('*')->limit(1)->find();

    // 2、統一下單
    // 統一下單,WxPayUnifiedOrder中out_trade_no、body、total_fee、trade_type必填
    // out_trade_no -> 訂單號
    // body -> 訂單描述
    // total_fee -> 訂單金額
    // trade_type -> 交易型別
    // 訂單號(隨機字串)
    $out_trade_no = C('WxPayConf_pub.APPID').time();
    // 訂單描述
    $body = '買買買';
    // 訂單金額
    $total_fee = 1000; // 單位為分
    $pay = new \WxPayUnifiedOrder();
    $pay->SetOut_trade_no($out_trade_no);
    $pay->SetBody($body);
    $pay->SetTotal_fee($total_fee);
    $pay->SetTrade_type("JSAPI");
    // 交易起始時間   
    $pay->SetTime_start(date("YmdHis"));
    // 交易結束時間
    $pay->SetTime_expire(date("YmdHis", time() + 600));
    // 原始資料,傳進去的是什麼,在notify中回撥的就是什麼,可以用來儲存資料庫對應的商品id,在notify中取出可以進行對資料庫的操作
    $pay->SetAttach($dataId);
    // 支付後會回撥頁面notify_url
    $pay->SetNotify_url("http://xxx.xxx.com/Wxpay/notify/");
    $pay->SetOpenid($openId);
    $order = \WxPayApi::unifiedOrder($pay);
    $jsApiParameters = $tools->GetJsApiParameters($order);
    // assign到前端
    $this->assign('jsApiParameters', $jsApiParameters);
    $this->display();
}

有關notify_url

支付完成後回撥URL,這個回撥是非同步的,意思大概是當你支付完成後,微信會返回一個支付狀態,前端通過這個狀態可以去渲染不同的頁面,如支付成功,支付失敗。
而這個notify是後續由微信自行呼叫,然後我們在裡面進行對資料的更新,如銷量、支付狀態等

這個時候應該可以正常支付了,但是,notify才是這個微信支付的巨坑,這個時候我們去看官方demo提供的notify.php,在這個檔案中,示範瞭如何去使用微信的notify

// 首先它新建了一個類PayNotifyCallBack,並且它繼承了WxPayNotify類
class PayNotifyCallBack extends WxPayNotify
{
    // 查詢訂單
    public function Queryorder($transaction_id) {}
    // 這個函式會在支付成功後被呼叫,就是在這裡進行資料的更新
    public function NotifyProcess($data, &$msg) {}
}
// 這個檔案中,最為關鍵的就是這兩句程式碼
$notify = new PayNotifyCallBack();
$notify->Handle(false);

回到WxpayController,我們仿造notify.php去實現自己的notify函式

class WxpayController extends Controller {
    public function pay() {
    }
    public function notify() {
        $notify = new Nbnotify();
        $notify->Handle(false);
    }
}
class Nbnotify extends \WxPayNotify {
    // 重寫回調處理函式 $data為微信返回的資料
    // 返回的資料為統一下單時提供的資料
    public function NotifyProcess($data, &$msg) {
        if(!array_key_exists("transaction_id", $data)){
            $msg = "輸入引數不正確";
            return false;
        }
        //查詢訂單,判斷訂單真實性
        if(!$this->Queryorder($data["transaction_id"])){
            $msg = "訂單查詢失敗";
            return false;
        } else {
            // 在這裡進行對資料的更新,比如更新支付狀態
            $updateData['isPay'] = 1;
            $dataId = $data['attach'];
            M('ticketinfos')->where(array('id' => $dataId))->save($updateData);

            return true;
        }
    }
}

在WxPay.Notify.php中,函式ReplyNotify有個地方修改

final private function ReplyNotify($needSign = true)
{
    //如果需要簽名
    if($needSign == true && 
        $this->GetReturn_code($return_code) == "SUCCESS")
    {
        $this->SetSign();
    }
    WxpayApi::replyNotify($this->ToXml());
}
// 微信提供的api多了一個$return_code引數,修改為
final private function ReplyNotify($needSign = true)
{
    //如果需要簽名
    if($needSign == true && 
        $this->GetReturn_code() == "SUCCESS")
    {
        $this->SetSign();
    }
    WxpayApi::replyNotify($this->ToXml());
}