文章熱詞:PHP,APP端微信支付

            日期:2019-04-18 10:01 by 楊國偉 1141 0 收藏
            我要分享

            摘要:微信支付很簡單,你可以參考微信支付開發文檔,一定要仔細閱讀開發文檔,可以讓你少踩點坑;準備工作完成後就是配置參數,調用統一下單接口,支付後異步回調三步。

            微信開發文檔:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=2_1

            第一部分:調用下單API,返回預支付訂單,簽名之後再返回信息 
            第二部分:異步通知
            第三部分:最後的判斷支付結果

            最需要注意的就是第一部分:調用下單API,返回預支付訂單,簽名之後再返回信息

            這裏封裝好的一個支付類文件,多餘的東西都去除掉了,并且把配置參數放到了這個支付類中,隻需要修改Weixinpayandroid方法内的幾個參數就可以直接複制使用:

            class Wxpayandroid
            {
             //參數配置
             public $config = array(
                appid => "", /*微信開放平台上的應用id*/
                mch_id => "", /*微信申請成功之後郵件中的商戶id*/
                api_key => "", /*在微信商戶平台上自己設定的api密鑰 32位*/
               );
             //服務器異步通知頁面路徑(必填)
             public $notify_url = ;
             //商戶訂單号(必填,商戶網站訂單系統中唯一訂單号)
             public $out_trade_no = ;
             //商品描述(必填,不填則爲商品名稱)
             public $body = ;
             //付款金額(必填)
             public $total_fee = 0;
             //自定義超時(選填,支持dhmc)
             public $time_expire = ;
             private $WxPayHelper;
             public function Weixinpayandroid($total_fee,$tade_no)
             {
              $this->total_fee = intval($total_fee * 100);//訂單的金額 1元
              $this->out_trade_no = $tade_no;// date(YmdHis) . substr(time(), - 5) . substr(microtime(), 2, 5) . sprintf(%02d, rand(0, 99));//訂單号
              $this->body = wxpay;//支付描述信息
              $this->time_expire = date(YmdHis, time() + 86400);//訂單支付的過期時間(eg:一天過期)
              $this->notify_url = "http://www.ceshi.com/notifyandroid";//異步通知URL(更改支付狀态)
              //數據以JSON的形式返回給APP
              $app_response = $this->doPay(); 
              if (isset($app_response[return_code]) && $app_response[return_code] == FAIL) {
               $errorCode = 100;
               $errorMsg = $app_response[return_msg];
               $this->echoResult($errorCode, $errorMsg);
              } else {
               $errorCode = 0;
               $errorMsg = success;
               $responseData = array(
                notify_url => $this->notify_url,
                app_response => $app_response,
               );
               $this->echoResult($errorCode, $errorMsg, $responseData);
              }
             }
             //接口輸出
             function echoResult($errorCode = 0, $errorMsg = success, $responseData = array())
             {
              $arr = array(
               errorCode => $errorCode,
               errorMsg => $errorMsg,
               responseData => $responseData,
              );
               exit(json_encode($arr));  //exit可以正常發送給APP json數據
              // return json_encode($arr); //在TP5中return這個json數據,APP接收到的是null,無法正常吊起微信支付
             }
             function getVerifySign($data, $key)
             {
              $String = $this->formatParameters($data, false);
              //簽名步驟二:在string後加入KEY
              $String = $String . "&key=" . $key;
              //簽名步驟三:MD5加密
              $String = md5($String);
              //簽名步驟四:所有字符轉爲大寫
              $result = strtoupper($String);
              return $result;
             }
             function formatParameters($paraMap, $urlencode)
             {
              $buff = "";
              ksort($paraMap);
              foreach ($paraMap as $k => $v) {
               if($k=="sign"){
                continue;
               }
               if ($urlencode) {
                $v = urlencode($v);
               }
               $buff .= $k . "=" . $v . "&";
              }
              $reqPar;
              if (strlen($buff) > 0) {
               $reqPar = substr($buff, 0, strlen($buff) - 1);
              }
              return $reqPar;
             }
             /**
              * 得到簽名
              * @param object $obj
              * @param string $api_key
              * @return string
              */
             function getSign($obj, $api_key)
             {
              foreach ($obj as $k => $v)
              {
               $Parameters[strtolower($k)] = $v;
              }
              //簽名步驟一:按字典序排序參數
              ksort($Parameters);
              $String = $this->formatBizQueryParaMap($Parameters, false);
              //簽名步驟二:在string後加入KEY
              $String = $String."&key=".$api_key;
              //簽名步驟三:MD5加密
              $result = strtoupper(md5($String));
              return $result;
             }
             /**
              * 獲取指定長度的随機字符串
              * @param int $length
              * @return Ambigous <NULL, string>
              */
             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
              * @param array $arr
              * @return string
              */
             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方式提交xml到對應的接口url
              *
              * @param string $xml 需要post的xml數據
              * @param string $url url
              * @param bool $useCert 是否需要證書,默認不需要
              * @param int $second url執行超時時間,默認30s
              * @throws WxPayException
              */
             function postXmlCurl($xml, $url, $second=30, $useCert=false, $sslcert_path=, $sslkey_path=)
             {
              $ch = curl_init();
              //設置超時
              curl_setopt($ch, CURLOPT_TIMEOUT, $second);
              curl_setopt($ch,CURLOPT_URL, $url);
              //設置header
              curl_setopt($ch, CURLOPT_HEADER, FALSE);
              //要求結果爲字符串且輸出到屏幕上
              curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
              curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
              curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
              if($useCert == true){
               curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE);
               curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//嚴格校驗
               //設置證書
               //使用證書:cert 與 key 分别屬于兩個.pem文件
               curl_setopt($ch,CURLOPT_SSLCERTTYPE,PEM);
               curl_setopt($ch,CURLOPT_SSLCERT, $sslcert_path);
               curl_setopt($ch,CURLOPT_SSLKEYTYPE,PEM);
               curl_setopt($ch,CURLOPT_SSLKEY, $sslkey_path);
              }
              //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);
               curl_close($ch);
               return false;
              }
             }
             /**
              * 獲取當前服務器的IP
              * @return Ambigous <string, unknown>
              */
             function get_client_ip()
             {
              if (isset($_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 = "127.0.0.1";
              }
              return $cip;
             }
             /**
              * 将數組轉成uri字符串
              * @param array $paraMap
              * @param bool $urlencode
              * @return string
              */
             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 unknown $xml
              * @return mixed
              */
             function xmlToArray($xml)
             {
              //将XML轉爲array
              $array_data = json_decode(json_encode(simplexml_load_string($xml, SimpleXMLElement, LIBXML_NOCDATA)), true);
              return $array_data;
             }
             public function chkParam()
             {
              //用戶網站訂單号
              if (empty($this->out_trade_no)) {
               die(out_trade_no error);
              } 
              //商品描述
              if (empty($this->body)) {
               die(body error);
              }
              if (empty($this->time_expire)){
               die(time_expire error);
              }
              //檢測支付金額
              if (empty($this->total_fee) || !is_numeric($this->total_fee)) {
               die(total_fee error);
              }
              //異步通知URL
              if (empty($this->notify_url)) {
               die(notify_url error);
              }
              if (!preg_match("#^http://#i", $this->notify_url)) {
               $this->notify_url = "http://" . $_SERVER[HTTP_HOST] . $this->notify_url;
              }
              return true;
             }
             /**
              * 生成支付(返回給APP)
              * @return boolean|mixed
              */
             public function doPay() {
              //檢測構造參數
              $this->chkParam();
              return $this->createAppPara();
             }
             /**
              * APP統一下單
              */
             private function createAppPara()
             {
              $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
              $data["appid"]  = $this->config[appid];//微信開放平台審核通過的應用APPID
              $data["body"]   = $this->body;//商品或支付單簡要描述
              $data["mch_id"]  = $this->config[mch_id];//商戶号
              $data["nonce_str"] = $this->getRandChar(32);//随機字符串
              $data["notify_url"] = $this->notify_url;//通知地址
              $data["out_trade_no"] = $this->out_trade_no;//商戶訂單号
              $data["spbill_create_ip"] = $this->get_client_ip();//終端IP
              $data["total_fee"]  = $this->total_fee;//總金額
              $data["time_expire"]  = $this->time_expire;//交易結束時間
              $data["trade_type"]  = "APP";//交易類型
              $data["sign"]    = $this->getSign($data, $this->config[api_key]);//簽名
              $xml  = $this->arrayToXml($data);
              $response = $this->postXmlCurl($xml, $url);
              //将微信返回的結果xml轉成數組
              $responseArr = $this->xmlToArray($response);
              if(isset($responseArr["return_code"]) && $responseArr["return_code"]==SUCCESS){
               return $this->getOrder($responseArr[prepay_id]);
              }
              return $responseArr;
             }
             /**
              * 執行第二次簽名,才能返回給客戶端使用
              * @param int $prepayId:預支付交易會話标識
              * @return array
              */
             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();
              $data["sign"]  = $this->getSign($data, $this->config[api_key]);
              $data["packagestr"] = "Sign=WXPay";
              return $data;
             }
             /**
              * 異步通知信息驗證
              * @return boolean|mixed
              */
             public function verifyNotify()
             {
              $xml = isset($GLOBALS[HTTP_RAW_POST_DATA]) ? $GLOBALS[HTTP_RAW_POST_DATA] : ; 
              if(!$xml){
               return false;
              }
              $wx_back = $this->xmlToArray($xml);
              if(empty($wx_back)){
               return false;
              }
              $checkSign = $this->getVerifySign($wx_back, $this->config[api_key]); 
              if($checkSign=$wx_back[sign]){
               return $wx_back;
              }else{
               return false;
              } 
             }
            }

            2.創建控制器定義統一下單接口和支付後的異步回調接口:

            //異步通知接口
             public function notifyandroid()
             {
              $wxpayandroid = new Wxpayandroid;  //實例化微信支付類
              $verify_result = $wxpayandroid->verifyNotify();
              if ($verify_result[return_code]==SUCCESS && $verify_result[result_code]==SUCCESS) {
                //商戶訂單号
                $out_trade_no = $verify_result[out_trade_no];
                //交易号
                $trade_no  = $verify_result[transaction_id];
                //交易狀态
                $trade_status = $verify_result[result_code];
                //支付金額
                $total_fee = $verify_result[total_fee]/100;
                //支付過期時間
                $pay_date  = $verify_result[time_end];
                $order = new Order();
                $ret = $order->getOrderN2($out_trade_no); //獲取訂單信息
                $total_amount=$ret[money];
                if ($total_amount==$total_fee) {
                 // 驗證成功 修改數據庫的訂單狀态等 $result[out_trade_no]爲訂單号
                 //此處寫自己的邏輯代碼
                }
               exit(<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>);
              }else{
               exit(<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[ERROR]]></return_msg></xml>);
              }
             }
             //調用統一下單接口生成預支付訂單并把數據返回給APP
             public function wxpayandroid(Request $request)
             {
              $param = $request->param(); //接收值
              $tade_no = $param[orderCode];
              $order = new Order(); //實例化訂單
              $ret = $order->getOrderN2($tade_no); //查詢訂單信息
              $total_fee = $ret[money]; //訂單總金額
              $wxpayandroid = new Wxpayandroid;  //實例化微信支付類
              $res = $wxpayandroid->Weixinpayandroid($total_fee,$tade_no); //調用weixinpay方法
             }

            封裝一個支付類文件,并把配置參數放到支付類内,再定義控制器創建兩個方法,這樣三步走都在了。

            上一篇:php與微信公衆号支付

            下一篇:php+redis實現注冊、删除、編輯等功能


            評論