php對微信支付回調處理的方法(合集)


支付完成后,微信會把相關支付結果和用戶信息發送給商戶,商戶需要接收處理,並返回應答。

對后台通知交互時,如果微信收到商戶的應答不是成功或超時,微信認為通知失敗,微信會通過一定的策略定期重新發起通知,盡可能提高通知的成功率,但微信不保證通知最終能成功。 (通知頻率為15/15/30/180/1800/1800/1800/1800/3600,單位:秒)

注意:同樣的通知可能會多次發送給商戶系統。商戶系統必須能夠正確處理重復的通知。

推薦的做法是,當收到通知進行處理時,首先檢查對應業務數據的狀態,判斷該通知是否已經處理過,如果沒有處理過再進行處理,如果處理過直接返回結果成功。在對業務數據進行狀態檢查和處理之前,要采用數據鎖進行並發控制,以避免函數重入造成的數據混亂。

特別提醒:商戶系統對於支付結果通知的內容一定要做簽名驗證,並校驗返回的訂單金額是否與商戶側的訂單金額一致,防止數據泄漏導致出現“假通知”,造成資金損失。

微信支付回調處理分為

1.同步
2.異步

這里微信官方推薦使用 第二種

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出現的小問題:

1、WxPay.Config

官方demo用的是require_once引入,如下圖

你如果用Vendor引入就會報WxPay.Config.php被重復引用的錯誤,所以這里根據你的實際情況是完全按照demo寫,還是變通下

這里是放在thinkphp3.2框架下接入微信支付的,所以這里實例化類 就要在 前面加 \ 

 2、如果在類文件里發現進入不正確,建議采取如下引入方式

  a、定義根目錄常量

  b、絕對路徑引入文件

 

3、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"] ,比較保險和可靠。

 

 

 

詳解 $_SERVER 函數中QUERY_STRING和REQUEST_URI區別

 
實例:

1,http://localhost/aaa/ (打開aaa中的index.php)
結果:
$_SERVER['QUERY_STRING'] = "";
$_SERVER['REQUEST_URI']  = "/aaa/";
$_SERVER['SCRIPT_NAME']  = "/aaa/index.php";
$_SERVER['PHP_SELF']     = "/aaa/index.php";

2,http://localhost/aaa/?p=222 (附帶查詢)
結果:
$_SERVER['QUERY_STRING'] = "p=222";
$_SERVER['REQUEST_URI']  = "/aaa/?p=222";
$_SERVER['SCRIPT_NAME']  = "/aaa/index.php";
$_SERVER['PHP_SELF']     = "/aaa/index.php";

3,http://localhost/aaa/index.php?p=222&q=333
結果:
$_SERVER['QUERY_STRING'] = "p=222&q=333";
$_SERVER['REQUEST_URI']  = "/aaa/index.php?p=222&q=333";
$_SERVER['SCRIPT_NAME']  = "/aaa/index.php";
$_SERVER['PHP_SELF']     = "/aaa/index.php";

由實例可知:
$_SERVER["QUERY_STRING"]  獲取查詢 語句,實例中可知,獲取的是?后面的值
$_SERVER["REQUEST_URI"]   獲取 http://localhost 后面的值,包括/
$_SERVER["SCRIPT_NAME"]   獲取當前腳本的路徑,如:index.php
$_SERVER["PHP_SELF"]      當前正在執行腳本的文件名

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM