<?php
// +----------------------------------------------------------------------
// +----------------------------------------------------------------------
// |
// +----------------------------------------------------------------------
// | Author: zhao
// Date: 2019-7-26 9:59
// +----------------------------------------------------------------------
namespace app\api\controller;
use think\Db;
use think\Log;php
/**html
*/
class Wxpayjson
{api
private $notify_url='********************************************'; //異步請求地址 private $wx_url = "https://api.mch.weixin.qq.com/pay/unifiedorder"; //微信支付官方接口 /* 配置參數 */ private $config = array( 'appid' => "", /*微信開放平臺上的應用id*/ 'mch_id' => "",/*微信申請成功以後郵件中的商戶id*/ 'api_key' => ""/*在微信商戶平臺上本身設定的api密鑰 32位*/ ); /** * @name pay() * @desc 微信支付方法 * @param string $attach 附加數據 1表明購物付款,2爲充值 * @param string $body 訂單描述 * @param string $out_trade_no 訂單號 * @param string $total_fee 總金額 * @return json code 1 二次簽名返回參數,前臺喚起微信 * @author zhaoliang * @updatetime 2019-8-10 * @addtime 2019-8-10 */ public function pay($attach, $body, $out_trade_no, $total_fee) { $notify_url = $this->notify_url;//回調地址 $total_fee = $total_fee * 100;//單位爲分,因此乘100 $order = $this->getPrePayOrder($attach, $body, $out_trade_no, $total_fee, $notify_url);//調用微信支付的方法 file_put_contents('wx1.txt', json_encode($order), FILE_APPEND); if (isset($order['prepay_id'])){//判斷返回參數中是否有prepay_id $res['order_arr'] = $this->getOrder($order['prepay_id']);//執行二次簽名返回參數 if (!empty($res['order_arr'])) { $res['code'] = 1; $res['msg'] = '驗籤成功'; } } else { $res['code'] = 0; $res['msg'] = '驗籤失敗'; } return json_encode($res); } /** * @name getPrePayOrder() * @desc 獲取預支付訂單 * @param string $attach 附加描述,1位購物,2爲充值 * @param string $body 訂單描述 * @param string $out_trade_no 訂單號 * @param string $total_fee 總金額 * @param [type] $notify_url 異步地址 * @return array 微信接口返回一次簽名 * @author zhaoliang * @updatetime 2019-8-13 * @addtime 2019-8-13 */ private function getPrePayOrder($attach, $body, $out_trade_no, $total_fee, $notify_url) { $url = $this->wx_url; $onoce_str = $this->getRandChar(32); $data['attach'] = $attach; $data["appid"] = $this->config["appid"]; $data["body"] = $body; $data["mch_id"] = $this->config['mch_id']; $data["nonce_str"] = $onoce_str; $data["notify_url"] = $notify_url; $data["out_trade_no"] = $out_trade_no; $data["spbill_create_ip"] = $this->get_client_ip(); $data["total_fee"] = $total_fee; $data["trade_type"] = "APP"; $s = $this->getSign($data, false); $data["sign"] = $s; $xml = $this->arrayToXml($data); $response = $this->postXmlCurl($xml, $url); //將微信返回的結果xml轉成數組 return $this->xmlToArray($response); } //執行第二次簽名,才能返回給客戶端使用 public function getOrder($prepayId) { $data["appid"] = $this->config["appid"]; $data["noncestr"] = $this->getRandChar(32);; $data["package"] = "Sign=WXPay"; $data["partnerid"] = $this->config['mch_id']; $data["prepayid"] = $prepayId; $data["timestamp"] = time(); $s = $this->getSign($data, false); $data["sign"] = $s; return $data; } /* * 生成一次簽名 */ private function getSign($Obj) { foreach ($Obj as $k => $v) { $Parameters[strtolower($k)] = $v; } //簽名步驟一:按字典序排序參數 ksort($Parameters); $String = $this->formatBizQueryParaMap($Parameters, false); //簽名步驟二:在string後加入KEY $String = $String."&key=".$this->config['api_key']; //簽名步驟三:MD5加密 $result_ = strtoupper(md5($String)); return $result_; } //獲取指定長度的隨機字符串 private function getRandChar($length){ $str = null; $strPol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz"; $max = strlen($strPol)-1; for($i=0;$i<$length;$i++){ $str.=$strPol[rand(0,$max)];//rand($min,$max)生成介於min和max兩個數之間的一個隨機整數 } return $str; } //數組轉xml private function arrayToXml($arr) { $xml = "<xml>"; foreach ($arr as $key=>$val) { if (is_numeric($val)) { $xml.="<".$key.">".$val."</".$key.">"; } else $xml.="<".$key."><![CDATA[".$val."]]></".$key.">"; } $xml.="</xml>"; return $xml; } //post https請求,CURLOPT_POSTFIELDS xml格式 private function postXmlCurl($xml,$url,$second=30) { //初始化curl $ch = curl_init(); //超時時間 curl_setopt($ch,CURLOPT_TIMEOUT,$second); curl_setopt($ch,CURLOPT_URL, $url); curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE); curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE); //設置header curl_setopt($ch, CURLOPT_HEADER, FALSE); //要求結果爲字符串且輸出到屏幕上 curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); //post提交方式 curl_setopt($ch, CURLOPT_POST, TRUE); curl_setopt($ch, CURLOPT_POSTFIELDS, $xml); //運行curl $data = curl_exec($ch); //返回結果 if($data) { curl_close($ch); return $data; } else { $error = curl_errno($ch); echo "curl出錯,錯誤碼:$error"."<br>"; echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>錯誤緣由查詢</a></br>"; curl_close($ch); return false; } } /* 獲取當前服務器的IP */ private function get_client_ip() { if ($_SERVER['REMOTE_ADDR']) { $cip = $_SERVER['REMOTE_ADDR']; } elseif (getenv("REMOTE_ADDR")) { $cip = getenv("REMOTE_ADDR"); } elseif (getenv("HTTP_CLIENT_IP")) { $cip = getenv("HTTP_CLIENT_IP"); } else { $cip = "unknown"; } return $cip; } //將數組轉成uri字符串 private function formatBizQueryParaMap($paraMap, $urlencode) { $buff = ""; ksort($paraMap); foreach ($paraMap as $k => $v) { if($urlencode) { $v = urlencode($v); } $buff .= strtolower($k) . "=" . $v . "&"; } $reqPar; if (strlen($buff) > 0) { $reqPar = substr($buff, 0, strlen($buff)-1); } return $reqPar; } /** * XML轉爲數組 * @param [type] $data [description] * @return [type] [description] */ private function xmlToArray($data){ $arr = json_decode(json_encode(simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA)), true); return $arr; } /** * 異步方法 * @return [type] [description] */ public function notify() { //容許從外部加載XML實體(防止XML注入攻擊) libxml_disable_entity_loader(true); $postStr = $this->post_data();//接收post數據 $arr = $this->xmlToArray($postStr); ksort($arr); # 對數據進行排序 $str = $this->params_tourl($arr); #對數據拼接成字符串 $user_sign = strtoupper(md5($str)); //把微信返回的數據進行再次簽名 Log::write(json_encode($arr),'log');//記入日誌 //驗證簽名 if($user_sign == $arr['sign']){ //驗證簽名成功 處理商戶訂單邏輯 if ($arr['return_code'] == 'SUCCESS' && $arr['result_code'] == 'SUCCESS') { if ($arr['attach'] == 1) {//表明購物 $return_arr = [ 'pay_type' => 1,//表明微信支付 'out_trade_no' => $arr['out_trade_no'], 'total_amount' => ($arr['total_fee'] / 100)//單位爲分,除100,得支付金額 ]; $res = setOrderPayDoneInfo($return_arr);//購物處理函數,true爲處理成功 if ($res) {//防止接口出問題 //給微信返回接收到數據通知 return '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>'; } } elseif ($arr['attach'] == 2) {//表明充值 $return_arr = [ 'payin_sn' => $arr['out_trade_no'], ]; $payin_url = 'http://traceplatform.macdapp.com/api/User_Payin_Log/save_user_notify'; $res = $this->valiAddress($payin_url,$return_arr); if ($res['code'] == 1) { return '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>'; } } } }else{ //簽名驗證失敗 微信會再次訪問回調方法 return '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>'; } } /* *@name post_data() *@desc 微信接收異步數據方法 * 微信是用$GLOBALS['HTTP_RAW_POST_DATA'];這個函數接收post數據的 */ private function post_data(){ $receipt = $_REQUEST; if($receipt==null){ $receipt = file_get_contents("php://input"); if($receipt == null){ $receipt = $GLOBALS['HTTP_RAW_POST_DATA']; } } return $receipt; } /** * 格式化參數格式化成url參數 */ private function params_tourl($arr) { $weipay_key = $this->config['api_key'];//微信key $buff = ""; foreach ($arr as $k => $v) { if($k != "sign" && $v != "" && !is_array($v)){ $buff .= $k . "=" . $v . "&"; } } $buff = trim($buff, "&"); return $buff.'&key='.$weipay_key; }
}數組