iOS&Android集成微信支付-Server篇(PHP)
在上一篇文章《iOS集成微信支付-Swift版》中雾鬼,我介紹了如何在iOS中集成微信支付猿挚。在支付過程中有一步是需要獲取PrePay數(shù)據(jù)硝清,至于如何獲取,我會在本文后面詳細介紹寒跳。在文章《手機App集成微信支付&支付寶-iOS&Android完整版》中硕淑,我介紹了整個支付流程,在App支付成功返回后挟鸠,你的服務器并不知道相關的訂單已經(jīng)支付成功叉信。這時候微信服務器會向你設置的服務器Notify URL發(fā)送POST請求,告訴你支付成功了艘希。所以服務端主要需要實現(xiàn)的工作就是驗證簽名以及驗證是否是微信發(fā)來的通知硼身。
獲取PrePay
在微信支付(統(tǒng)一下單)文檔中是這樣描述的:
除被掃支付場景以外,商戶系統(tǒng)先調用該接口在微信支付服務后臺生成預支付交易單覆享,返回正確的預支付交易回話標識后再按掃碼佳遂、JSAPI、APP等不同場景生成交易串調起支付撒顿。
關于獲取預支付接口的請求參數(shù)丑罪、返回結果以及錯誤碼請查看官方文檔。有一點需要注意的是微信支付的接口參數(shù)用的是XML格式凤壁。下面就是獲取PrePay的代碼:
$wxpay_config = $this->wxpay_config = $this->my_config['wxpay_config'];
//var_dump($wxpay_config);
$APP_ID = $wxpay_config['app_id']; //APPID
$APP_SECRET = $wxpay_config['app_secret']; //appsecret
$MCH_ID=$wxpay_config['mch_id'];
$PARTNER_ID = $wxpay_config['partner_id'];
$NOTIFY_URL = $wxpay_config['notify_url'];
if (!$out_trade_no) {
$this->display_error(400,'請求是無效的');
}
$order = $this->order_model->get_order_info($out_trade_no);
if (!$order) {
$this->display_error(1,'請求是無效的');
}
//STEP 1. 構造一個訂單吩屹。
$order=array(
"body" => $order['Subject'],
"appid" => $APP_ID,
"device_info" => "APP-001",
"mch_id" => $MCH_ID,
"nonce_str" => mt_rand(),
"notify_url" => $NOTIFY_URL,
"out_trade_no" => $out_trade_no,
"spbill_create_ip" => $this->input->ip_address(),
"total_fee" => intval($order['TotalFee'] * 100),//注意:前方有坑!E《丁煤搜!最小單位是分,跟支付寶不一樣唧席。1表示1分錢擦盾。只能是整形。
"trade_type" => "APP"
);
ksort($order);
//STEP 2. 簽名
$sign="";
foreach ($order as $key => $value) {
if($value&&$key!="sign"&&$key!="key"){
$sign.=$key."=".$value."&";
}
}
$sign.="key=".$PARTNER_ID;
$sign=strtoupper(md5($sign));//echo $sign.'
';exit;
//STEP 3. 請求服務器
$xml="n";
foreach ($order as $key => $value) {
$xml.="<".$key.">".$value."n";
}
$xml.="".$sign."n";
$xml.="";
$opts = array(
'http' =>
array(
'method' => 'POST',
'header' => 'Content-type: text/xml',
'content' => $xml
),
"ssl"=>array(
"verify_peer"=>false,
"verify_peer_name"=>false,
)
);
$context = stream_context_create($opts);
$result = file_get_contents('https://api.mch.weixin.qq.com/pay/unifiedorder', false, $context);
$result = simplexml_load_string($result,null, LIBXML_NOCDATA);
if ($result->return_code == 'SUCCESS' && $result->result_code == 'SUCCESS') {
$prepay=array(
"noncestr"=>"".$result->nonce_str,
"prepayid"=>"".$result->prepay_id,//上一步請求微信服務器得到nonce_str和prepay_id參數(shù)淌哟。
"appid"=>$APP_ID,
"package"=>"Sign=WXPay",
"partnerid"=>$MCH_ID,
"timestamp"=>"".time(),
"sign"=>""
);
ksort($prepay);
$sign="";
foreach ($prepay as $key => $value) {
if($value&&$key!="sign"&&$key!="key"){
$sign.=$key."=".$value."&";
}
}
$sign.="key=".$PARTNER_ID;
$sign=strtoupper(md5($sign));
$prepay['sign'] = $sign;
$prepay['success'] = true;
} else {
$prepay=array(
"success" => false,
"noncestr"=>"",
"prepayid"=>"",
"appid"=>$APP_ID,
"package"=>"Sign=WXPay",
"partnerid"=>$MCH_ID,
"timestamp"=>"".time(),
"sign"=>"",
"return_msg"=>$result->return_msg
);
}
$this->response($prepay, 200);
此處有一個坑需要注意迹卢,微信支付接口的金額最小單位是分,跟支付寶不一樣徒仓,1表示1分錢婶希,且只能是整形。
客戶端App通過該API獲取到PrePay信息有就可以發(fā)起支付請求了蓬衡∮麒荆客戶端的實現(xiàn)代碼請查看文章《iOS&Android集成微信支付-Server篇》彤枢。
Notify URL
前面說過,這個URL是支付成功后微信調用的筒饰。那么微信Server是如何知道這個URL的呢缴啡?其實是在前面請求PrePay的時候通過參數(shù)notify_url傳過去的。
微信調用支付結果通知的詳細參數(shù)請訪問文檔「支付結果通用通知」瓷们。同樣业栅,參數(shù)格式也是XML。
所有這些參數(shù)中谬晕,我們最關心的應該是下面幾個:
return_code :返回狀態(tài)碼碘裕。SUCCESS/FAIL
result_code : 交易標識,交易是否成功 SUCCESS/FAIL
out_trade_no : 商戶訂單號, 需要在你的數(shù)據(jù)庫能夠唯一標志訂單
transaction_id :微信支付訂單號攒钳,流水號帮孔。記錄到你的數(shù)據(jù)庫方便對賬用
獲取到這些信息后就可以從自己系統(tǒng)DB里面通過out_trade_no查詢出相應的訂單,標記為支付成功不撑,并記錄下相應的交易流水號transaction_id. 需要注意的是文兢,處理玩業(yè)務邏輯后需要返回"return_code"為"success"給微信服務器。
Notify實現(xiàn)代碼如下:
/**
* 微信支付Notify
*/
function wxpay_notify(){
//使用通用通知接口
$notify = new Notify_pub();
//存儲微信的回調
$xml = $GLOBALS['HTTP_RAW_POST_DATA'];
$notify->saveData($xml);
//驗證簽名焕檬,并回應微信姆坚。
//對后臺通知交互時,如果微信收到商戶的應答不是成功或超時实愚,微信認為通知失敗兼呵,
//微信會通過一定的策略(如30分鐘共8次)定期重新發(fā)起通知,
//盡可能提高通知的成功率腊敲,但微信不保證通知最終能成功击喂。
if($notify->checkSign() == FALSE){
$notify->setReturnParameter("return_code","FAIL");//返回狀態(tài)碼
$notify->setReturnParameter("return_msg","簽名失敗");//返回信息
}else{
$notify->setReturnParameter("return_code","SUCCESS");//設置返回碼
}
$returnXml = $notify->returnXml();
echo $returnXml;
//==商戶根據(jù)實際情況設置相應的處理流程,此處僅作舉例=======
//以log文件形式記錄回調信息
$log_type="wxpay_notify";//log文件路徑
$this->log_result($log_type,"【接收到的notify通知】:n".$xml."n");
if($notify->checkSign() == TRUE) {
if ($notify->data["return_code"] == "FAIL") {
//此處應該更新一下訂單狀態(tài)兔仰,商戶自行增刪操作
$this->log_result($log_type,"【通信出錯】:n".$xml."n");
} elseif ($notify->data["result_code"] == "FAIL") {
//此處應該更新一下訂單狀態(tài),商戶自行增刪操作
$this->log_result($log_type,"【業(yè)務出錯】:n".$xml."n");
} else {
//此處應該更新一下訂單狀態(tài)蕉鸳,商戶自行增刪操作
$this->log_result($log_type,"【支付成功】:n".$xml."n");
$out_trade_no = $notify->data['out_trade_no'];
$trade_no = $notify->data['transaction_id'];
$order = $this->order_model->get_order_info($out_trade_no);
if($order['TradeStatus'] != 'TRADE_FINISHED' && $order['TradeStatus'] != 'TRADE_SUCCESS'){
$data = array('TradeStatus'=>'TRADE_SUCCESS','TradeNo'=>$trade_no,'PayTime'=>time(),'PayType'=>'wxpay');
$this->order_model->update_order_info($out_trade_no,$data);
}
}
}
}
完整的實現(xiàn)代碼請移步Github下載乎赴。
Server端是基于CI框架實現(xiàn)的,如果你不熟悉的話也沒有關系,下載demo后打開 /application/config/myconfig.php 文件潮尝,將數(shù)組 wxpay_config 中的 app_id榕吼,app_secret,mch_id, partner_id勉失,notify_url,替換成你自己的配置即可運行羹蚣。
其中 app_id,app_secret 是你在微信開放平臺創(chuàng)建的應用乱凿。
mch_id為「微信支付商戶號」顽素,登錄微信支付商戶平臺即可看到咽弦。
在本文章Demo實現(xiàn)過程中,我沒有采用任何安全驗證措施胁出,在實際開放過程中Server端API是需要根據(jù)自身情況采取相應安全策略的型型。