支付完成後,微信會把相關支付結果和用戶信息發送給商戶,商戶須要接收處理,並返回應答。php
對後臺通知交互時,若是微信收到商戶的應答不是成功或超時,微信認爲通知失敗,微信會經過必定的策略按期從新發起通知,儘量提升通知的成功率,但微信不保證通知最終能成功。 (通知頻率爲15/15/30/180/1800/1800/1800/1800/3600,單位:秒)html
注意:一樣的通知可能會屢次發送給商戶系統。商戶系統必須可以正確處理重複的通知。thinkphp
推薦的作法是,當收到通知進行處理時,首先檢查對應業務數據的狀態,判斷該通知是否已經處理過,若是沒有處理過再進行處理,若是處理過直接返回結果成功。在對業務數據進行狀態檢查和處理以前,要採用數據鎖進行併發控制,以免函數重入形成的數據混亂。數據庫
特別提醒:商戶系統對於支付結果通知的內容必定要作簽名驗證,並校驗返回的訂單金額是否與商戶側的訂單金額一致,防止數據泄漏致使出現「假通知」,形成資金損失。apache
微信支付回調處理分爲json
1.同步
2.異步api
這裏微信官方推薦使用 第二種數組
php對微信回調異步處理瀏覽器
微信不管是微信內置JSAPI支付、H5外部瀏覽器支付、掃碼支付,都須要經過異步回調接收支付結果。安全
本文簡介如何獲取微信支付通知。
僅須要一個在以前設置好的回調地址的方法裏寫上以下:
//處理微信支付回調 public function notify(){ $testxml = file_get_contents("php://input"); $jsonxml = json_encode(simplexml_load_string($testxml, 'SimpleXMLElement', LIBXML_NOCDATA)); $result = json_decode($jsonxml, true);//轉成數組, if($result){ //若是成功返回了 $out_trade_no = $result['out_trade_no']; if($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS'){ //執行業務邏輯改變訂單狀態等操做 //查詢建立訂單表 where("out_trade_no='".$out_trade_no."' and status=1") status爲1表示待支付狀態 1 待支付 //查詢出來有該訂單 就改變支付狀態 status=2 2表示支付成功 } } }
對後臺通知交互時,若是微信收到商戶的應答不是成功或超時,微信認爲通知失敗,
微信會經過必定的策略按期從新發起通知,儘量提升通知的成功率,但微信不保證通知最終能成功。
(通知頻率爲15/15/30/180/1800/1800/1800/1800/3600,單位:秒)
結束微信從新通知用代碼:echo 'SUCCESS';
附上:
微信支付返回的xml轉化爲json格式以下:
{ "appid": "12345", "attach": "pay", "bank_type": "CFT", "cash_fee": "1", "fee_type": "CNY", "is_subscribe": "Y", "mch_id": "12345", "nonce_str": "dZYFpaDYRpF5rwhv", "openid": "onhwF1hiutUySKCsrV21A6MCtT5Q", "out_trade_no": "SH201808222055598628", "result_code": "SUCCESS", "return_code": "SUCCESS", "sign": "5A019F52BEF1C3A98AE0F1FF29D01574", "time_end": "20180822205606", "total_fee": "1", "trade_type": "MWEB", "transaction_id": "4200000171201808221550954201" }
其中
"result_code": "SUCCESS", "return_code": "SUCCESS",
是判斷用戶是否已經支付的依據
由於微信在後臺通知交互時,若是微信收到商戶的應答不符合規範或超時,微信會斷定本次通知失敗,從新發送通知,直到成功爲止(在通知一直不成功的狀況下,微信總共會發起10次通知,每次通知時間距離最近一次的間隔爲15/15/30/180/1800/1800/1800/1800/3600,單位:秒),但微信不保證通知最終必定能成功。
因此進來回調方法就要判斷這筆訂單在咱們後臺數據庫是否支付成功了!
php對微信回調異步處理
微信不管是微信內置JSAPI支付、H5外部瀏覽器支付、掃碼支付,都須要經過異步回調接收支付結果。
本文簡介如何獲取微信支付通知。
僅須要一個在以前設置好的回調地址的方法裏寫上以下:
//處理微信支付回調
public function notify(){
$testxml = file_get_contents("php://input"); $jsonxml = json_encode(simplexml_load_string($testxml, 'SimpleXMLElement', LIBXML_NOCDATA)); $result = json_decode($jsonxml, true);//轉成數組, if($result){ //若是成功返回了 $out_trade_no = $result['out_trade_no']; if($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS'){ //執行業務邏輯改變訂單狀態等操做 //查詢建立訂單表 where("out_trade_no='".$out_trade_no."' and status=1") status爲1表示待支付狀態 1 待支付 //查詢出來有該訂單 就改變支付狀態 status=2 2表示支付成功 } } }
須要特別注意的地方:微信支付結果通知,會按必定規則重複發送通知,所以執行業務邏輯以前,須要判斷是不是重複通知。
若是本身的業務邏輯執行成功後,能夠return一個規定格式的XML,來告訴微信你已成功收到通知,並作好了相應處理。(固然不return也行。。。作好重複驗證機制就好)
如下是本身實際項目中的處理支付回調的詳細流程,不完善(好比並發問題沒處理),你們有更好的建議歡迎提出來以使得處理邏輯更嚴密
(本身處理過程當中遇到一些關於jsapi的demo的小問題,在下面給你們羅列出來)
public function wycz(){//我要充值
if(session('uid') ==NULL || session('uid') == "" || session('uid') == false){
//沒有登陸 跳轉到登陸頁
$this->redirect('/Login/login');exit; }else { //接受參數 if($_POST){ $Total_fee = trim(I("post.fotal_fee")); if(!empty($Total_fee)){ if($Total_fee<=0){ //提示輸入正整數金額 $arr['code'] = "301"; $arr['msg'] = "輸入正整數"; echo json_encode($arr);exit(); }else{ session("total_fee",$Total_fee); $arr['code'] = "200"; $arr['msg'] = "確認充值嗎?"; echo json_encode($arr);exit(); } }else{ //提示輸入正整數金額 $arr['code'] = "302"; $arr['msg'] = "輸入充值金額"; echo json_encode($arr);exit(); } } $this->display(); } } //充值確認頁面 public function confirmPay(){ if(session('uid') ==NULL || session('uid') == "" || session('uid') == false){ //沒有登陸 跳轉到登陸頁 $this->redirect('/Login/login');exit; }else { $result = $this->wapPay(); $this->assign('jsApiParameters',$result['jsApiParameters']); $this->assign('editAddress', $result['editAddress']); $this->display(); } } //操做充值 public function wapPAy($Total_fee) { /** * * example目錄下爲簡單的支付樣例,僅能用於搭建快速體驗微信支付使用 * 樣例的做用僅限於指導如何使用sdk,在安全上面僅作了簡單處理, 複製使用樣例代碼時請慎重 * 請勿直接直接使用樣例對外提供服務 * **/ Vendor('Wxpaysdk.lib.WxPay#Api'); Vendor('Wxpaysdk.example.WxPay#JsApiPay'); // Vendor('Wxpaysdk.example.WxPay#Config'); //①、獲取用戶openid $tools = new \JsApiPay(); $openId = $tools->GetOpenid(); $Total_fee = session("total_fee")*100; //$Total_fee = session("total_fee"); //②、統一下單 $input = new \WxPayUnifiedOrder(); $input->SetBody("vip充值"); $input->SetAttach("vip充值"); $input->SetOut_trade_no("klkq".date("YmdHis")); $input->SetTotal_fee($Total_fee); $input->SetTime_start(date("YmdHis")); $input->SetTime_expire(date("YmdHis", time() + 600)); $input->SetGoods_tag("vip充值"); //$input->SetNotify_url("http://paysdk.weixin.qq.com/notify.php"); $input->SetNotify_url("http://wap.yuming.com/Member/notify"); $input->SetTrade_type("JSAPI"); $input->SetOpenid($openId); $config = new \WxPayConfig(); $order = \WxPayApi::unifiedOrder($config, $input); //echo '<font color="#f00"><b>統一下單支付單信息</b></font><br/>'; //printf_info($order); $jsApiParameters = $tools->GetJsApiParameters($order); //獲取共享收貨地址js函數參數 $editAddress = $tools->GetEditAddressParameters(); $data['jsApiParameters'] = $jsApiParameters; $data['editAddress'] = $editAddress; //寫入訂單表對應數據 $arr = array( 'w_mId'=>session('uid'), 'w_openid'=>$openId, 'w_title'=>'vip充值', 'w_time_create'=>date("YmdHis"), 'w_times_create'=>date("Y-m-d H:i:s"), 'w_out_trade_no'=>"klkq".date("YmdHis"), ); M('wxpay_order')->add($arr); return $data; } //處理微信支付回調 public function notify(){ $testxml = file_get_contents("php://input"); $jsonxml = json_encode(simplexml_load_string($testxml,'SimpleXMLElement',LIBXML_NOCDATA)); $result = json_decode($jsonxml,true); if($result){ $out_trade_no = $result['out_trade_no']; if($result['return_code'] == 'SUCCESS' && $result['result_code'] =='SUCCESS'){ //先判斷訂單狀態是否已經改變 $orderData = M("wxpay_order")->where("w_out_trade_no='".$result['out_trade_no']."' and w_status=1")->find(); if(!empty($orderData)){ //支付成功改變支付狀態 $arr = array( 'w_id'=>$orderData['w_id'], 'w_openid'=>$result['openid'], 'w_transaction_id'=>$result['transaction_id'], 'w_time_end'=>$result['time_end'], 'w_times_end'=>$result['time_end'], 'w_total_fee'=>$result['total_fee']/100, 'w_status'=>2,//支付成功 1 待支付 2支付成功 3 支付失敗 ); M('wxpay_order')->save($arr); //操做積分 $uid = $orderData['w_mId']; //對該人增長$result['total_fee']積分 $zz_grade = M('grade'); $g_Integral = $zz_grade->where("g_mId=".$uid)->getField('g_Integral'); $zz_grade->g_Integral = $g_Integral + intval($result['total_fee']/100); //更新積分 $giddd = $zz_grade->where("g_mId=".$uid)->save(); //操做積分日誌表 $zz_grade_log = M('grade_log'); $zz_grade_log->g_gContent = '充值'.intval($result['total_fee']/100).'積分'; $zz_grade_log->g_mId = $uid;//邀請者會員id $zz_grade_log->g_gNum = intval($result['total_fee']/100);//充值積分 $zz_grade_log->g_gDate = date('Y-m-d');//註冊日期 $zz_grade_log->g_gTime = date('Y-m-d H:i:s');//註冊時間 //寫入數據庫 $liddd = $zz_grade_log->add(); } } } }
如下是關於微信jsapi的demo出現的小問題:
一、WxPay.Config
官方demo用的是require_once引入,以下圖
你若是用Vendor引入就會報WxPay.Config.php被重複引用的錯誤,因此這裏根據你的實際狀況是徹底按照demo寫,仍是變通下
這裏是放在thinkphp3.2框架下接入微信支付的,因此這裏實例化類 就要在 前面加 \
二、若是在類文件裏發現進入不正確,建議採起以下引入方式
a、定義根目錄常量
b、絕對路徑引入文件
三、WxPay.JsApiPay.php文件裏受權獲取openid問題
圖中紅框白箭頭是官方demo給的,可是在個人這個場景中不合適,老是造出這樣的url http://wap.abc.com/index.php/Member/cz.htmls=/Member/cz.html?callback=.....
程序報非法訪問cz.htmls,當我用紅箭頭指定的那個即是成功的獲取正確的url,獲取openid即是成功的。
狀況不止於此:下面是對
$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'].$_SERVER['QUERY_STRING']
的搜索結果以下:
$_SERVER['HTTP_HOST']:獲取當前域名
$_SERVER['REQUEST_URI']:這是取得當前URL的 路徑地址 好比: http://zhidao.baidu.com/question/469321857.html?push=core&group=1 他得到是這個路徑 question/469321857.html?push=core&group=1
$_SERVER['QUERY_STRING']:獲取的是?後面的值
上述問題解決完,支付流程就能夠走通了
延伸:
相同點:
當知足如下三個條件時,二者會輸出相同信息。
1. 服務器爲80端口
2. apache的conf中ServerName設置正確
3. HTTP/1.1協議規範
不一樣點:
1. 一般狀況:
_SERVER["HTTP_HOST"] 在HTTP/1.1協議規範下,會根據客戶端的HTTP請求輸出信息。
_SERVER["SERVER_NAME"] 默認狀況下直接輸出apache的配置文件httpd.conf中的ServerName值。
2. 當服務器爲非80端口時:
_SERVER["HTTP_HOST"] 會輸出端口號,例如:mimiz.cn:8080
_SERVER["SERVER_NAME"] 會直接輸出ServerName值
所以在這種狀況下,能夠理解爲:HTTP_HOST = SERVER_NAME : SERVER_PORT
3. 當配置文件httpd.conf中的ServerName與HTTP/1.0請求的域名不一致時:
httpd.conf配置以下:
<virtualhost *>
ServerName mimiz.cn
ServerAlias www.mimiz.cn
</virtualhost>
客戶端訪問域名www.mimiz.cn
_SERVER["HTTP_HOST"] 輸出 www.mimiz.cn
_SERVER["SERVER_NAME"] 輸出 mimiz.cn
因此,在實際程序中,應儘可能使用_SERVER["HTTP_HOST"] ,比較保險和可靠。