App微信支付 php後臺接口
本問將介紹App微信支付(2016.10.11)的php(7.0)後臺支付和回調接口,框架是Thinkphp5.0:
- 賬戶的各種參數
- 訂單信息
- 請求prepay_id
- *返回APP數據處理
- 微信回調
- 修改訂單狀態
賬戶的各種參數
賬戶的各種參數就是像微信申請app支付的時候會給你的賬戶郵箱發郵件,裏面會有對應的微信支付分配的商戶號(MCHID),APPID和APPSECRET是在申請app支付權限的時候返回的,還有KEY需要自己在用戶微信的商家後臺裏面自己設置的,這個很重要!避免外泄!
訂單信息
- **客戶端會把購物車裏面的商品數據傳到後臺,還包括用戶的信息等,拿到數據後首先需要驗證這些數據,這裏的驗證一般都會在項目初期就和客戶端定好傳輸規則,比如傳輸方式,參數名,驗證參數的方式,這裏的重點就是驗證,一般都會採用簽名認證的方式:
- //簽名步驟一:按字典序排序參數;
- 這裏給出(格式化參數格式化成url參數)代碼實例:
/**
* 格式化參數格式化成url參數
*/
public function ToUrlParams()
{
$buff = "";
foreach ($this->values as $k => $v)
{
if($k != "sign" && $v != "" && !is_array($v)){
$buff .= $k . "=" . $v . "&";
}
}
$buff = trim($buff, "&");
return $buff;
}
- //簽名步驟二:在string後加入KEY(這個KEY與前端人員協商而定);
- //簽名步驟三:MD5加密;
- //簽名步驟四:所有字符轉爲大寫
這些最好在項目初期就封裝好,後期會只要調用就好,例:
(後期只需要, $param = $this->request('參數名'));
即可), 然後將訂單信息預存儲.
請求prepay_id
這裏我就直接上代碼吧,(這裏給出接口文檔下載地址:),雖然網上也有很多自己寫請求接口,但是既然微信已經封裝好了,用就行了:
$input = new \app\wxpay\WxPayUnifiedOrder();//這裏引用微信的統一下單接口
$input->SetBody($data['gname']['g_name']);//商品或支付單簡要描述
$input->SetAttach($data['gname']['g_name']);//置附加數據
$input->SetOut_trade_no($order_sn); // 商戶訂單號
$input->SetTotal_fee(intval($data['data']['order_price']*100));
$input->SetTime_start(date("YmdHis"));//訂單生成時間
$input->SetTime_expire(date("YmdHis", time() + 600));//訂單失效時間
$input->SetGoods_tag($data['gname']['g_name']); //商品標記
$input->SetNotify_url("http://www.weixin.qq.com/wxpay/notify.php"); // 支付成功後的回調地址,
$input->SetTrade_type("APP");
$order = \app\wxpay\WxPayApi::unifiedOrder($input);
return $order['prepay_id'];
這裏給出微信官方統一下單接口說明地址:
https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1
返回客戶端信息
$info = array();
//賬號的信息一般都放在配置文件裏面,用到的地方也很多
$info['appid'] = config('APP_APPID');
$info['partnerid'] = config('APP_MCHID');
$info['package'] = config('APP_PACKAGE');
$info['noncestr'] = $this->random_number();//生成隨機數,下面有生成實例,統一下單接口需要
$info['timestamp'] = time();
$info['prepayid'] = $prepay_id;
$info['sign'] = self::_makeSign($info);//生成簽名
return $info;
$info
就是客戶端需要的信息啦
生成隨機數實例
//生成隨機數
public function random_number($len=21,$format='ALL' ){
$is_abc = $is_numer = 0;
$password = $tmp ='';
switch($format){
case 'ALL':
$chars='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
break;
case 'CHAR':
$chars='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
break;
case 'NUMBER':
$chars='0123456789';
break;
default :
$chars='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
break;
} // www.jb51.net
mt_srand((double)microtime()*1000000*getmypid());
while(strlen($password)<$len){
$tmp =substr($chars,(mt_rand()%strlen($chars)),1);
if(($is_numer <> 1 && is_numeric($tmp) && $tmp >0 )|| $format == 'CHAR'){
$is_numer = 1;
}
if(($is_abc <> 1 && preg_match('/[a-zA-Z]/',$tmp)) || $format == 'NUMBER'){
$is_abc = 1;
}
$password.= $tmp;
}
if($is_numer <> 1 || $is_abc <> 1 || empty($password) ){
$password = $this->random_number($len,$format);
}
return $password;
}
微信回調
支付結果通知 notify.php(這裏的地址就是統一下單時填寫的回調地址,微信已經封裝好),文檔下載地址
http://mch.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1
其實這個頁面最主要的代碼就兩行
[php] view plain copy
$notify = new PayNotifyCallBack();
$notify->Handle(false);
其中大部分邏輯在 Handle 函數中處理 文件 WxPay.Notify.php
[php] view plain copy
final public function Handle($needSign = true)
{
$msg = "OK";
//當返回false的時候,表示notify中調用NotifyCallBack回調失敗獲取簽名校驗失敗,此時直接回復失敗
$result = WxpayApi::notify(array($this, 'NotifyCallBack'), $msg);
if($result == false){
$this->SetReturn_code("FAIL");
$this->SetReturn_msg($msg);
$this->ReplyNotify(false);
return;
} else {
//該分支在成功回調到NotifyCallBack方法,處理完成之後流程
$this->SetReturn_code("SUCCESS");
$this->SetReturn_msg("OK");
}
$this->ReplyNotify($needSign);
}
主要代碼:
[php] view plain copy
$result = WxpayApi::notify(array($this, 'NotifyCallBack'), $msg);
跟蹤函數 notify 文件WxPay.Api.php
[php] view plain copy
public static function notify($callback, &$msg)
{
//獲取通知的數據
$xml = $GLOBALS['HTTP_RAW_POST_DATA'];
//如果返回成功則驗證簽名
try {
$result = WxPayResults::Init($xml);
} catch (WxPayException $e){
$msg = $e->errorMessage();
return false;
}
return call_user_func($callback, $result);
}
通過 $GLOBALS[‘HTTP_RAW_POST_DATA’]; 獲取同志數據 然後 Init 函數驗證簽名等。驗籤成功運行代碼.
這裏需要說明一下,php7本身不支持$GLOBALS[‘HTTP_RAW_POST_DATA’],需要下載一個插件,具體什麼可自行百度,我想說的是可以用file_get_contents(‘php://input’),具體原因可參照下面的博客,寫的很詳細(https://my.oschina.net/jiec/blog/485359)
[php] view plain copy
return call_user_func($callback, $result);
即調用了一個回調函數,NotifyCallBack() 函數並傳遞參數 $result 在NotifyCallBack函數中會調用我們重寫的NotifyProcess()函數(此函數在notify.php 中被重寫)
NotifyProcess() 判斷也沒有問題就會 設置返回 success的xml信息
[php] view plain copy
$this->SetReturn_code("SUCCESS");
$this->SetReturn_msg("OK");
並最終調用函數
函數ReplyNotify 需要修改一處代碼:
[php] view plain copy
final private function ReplyNotify($needSign = true)
{
//如果需要簽名
if($needSign == true &&
$this->GetReturn_code($return_code) == "SUCCESS")
{
$this->SetSign();
}
WxpayApi::replyNotify($this->ToXml());
}
[php] view plain copy
$this->GetReturn_code($return_code) == "SUCCESS")
改爲
[php] view plain copy
$this->GetReturn_code() == "SUCCESS")
即可。
然後是根據返回信息修改訂單狀態,主要就是,在什麼地方修改,我是在notify.php裏面新建了一個方法
//修改訂單狀態
public function updateState($data){
if($data){
$order_sn = $data['out_trade_no'];\
$data = array();
$data['order_id'] = $order_id;
//修改訂單狀態(用curlpost方法請求至thinkphp目錄下的Controller裏面控制器裏面的方法,修改狀態)
$url = 'www.test.com';
header('content-type:text/html;charset=utf8');
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
$result = curl_exec($curl);
curl_close($curl);
if($result == 'success'){
return true;
}else{
return false;
}
}
}
然後在notify.php 的
$notify = new PayNotifyCallBack();
$notify->Handle(false);
下面加上
//接受參數,修改狀態
$xml = file_get_contents("php://input");
$data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
$notify->updateState($data);
可能這種修改狀態的方法不是最優的,如果還有更好的方法,請留言告知,謝謝!
這樣整個流程就結束了。
最後給出一個我在回調的問題上糾結很久之後看到的一篇令我豁然開朗的博客地址:
http://blog.csdn.net/m0sh1/article/details/45199711
(再次向前輩致敬!)