thinkphp微信支付-JSAPI
阿新 • • 發佈:2019-01-23
我只是一個小小前端,剛好專案需要,接受到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());
}