php微信支付JSAPI(不使用sdk)

【提示(認真臉)】支付文章僅爲參考,請不要盲目相信各種攻略

語言:php

接入類型:jsapi

工作流程:

流程圖

  1. 前期準備:配置回調地址等信息,配置公衆號支付相關信息去這裏,獲取商戶key的id和secret,開通微信支付相關功能
    1. 拿到用戶的openid
    2. 商戶號和商戶的支付密鑰(注意正確性,密鑰在重新生成後會使之前的失效
  2. 前端調起後端處理支付信息的接口,傳入用戶、商品等項目相關信息,這些信息會是你支付的必要信息,例如商品價格,數量等
  3. 後端整合請求參數,需根據微信的要求整合成合適的形式作爲參數作爲數據傳給獲取預支付url
    1. 參數列表
    2. 隨機數生成 
      1. md5(time() . mt_rand(0,1000))

         

    3. 簽名(整合必填參數和需要的參數即可)
      /**
      *    KEY爲商戶key,MCHID爲商戶號
      $para = array(
                  'appid'             => APP_ID,
                  'body'              => $body,
                  'detail'            => $detail,
                  'mch_id'            => MCHID,
                  'nonce_str'         => $nonce_str,
                  'notify_url'        => NOTIFY_URL,    //接收支付成功的回調函數
                  'openid'            => $user['open_id'],
                  'out_trade_no'      => $orderNumber,    //自定義訂單號
                  'spbill_create_ip'  => $userIp,
                  'total_fee'         => $money,
                  'trade_type'        => 'JSAPI'
              );
      */
      
      
      function getSign($para){
          $config = config();
          $str = '';
          foreach ((array)$para as $key => $value){
              $str .= $key.'='.$value.'&';
          }
          $str .= 'key='.KEY;
          $sign = MD5($str);
          return strtoupper($sign);
      }

       

  4. 通過請求預支付api(https://api.mch.weixin.qq.com/pay/unifiedorder)獲取預支付id等信息,支付回調函數路徑在此處作爲參數之一傳給微信端
    1. 整合參數(向用於生成簽名的數組添加簽名)
      $para = array(
                  'appid'             => APP_ID,
                  'body'              => $body,
                  'detail'            => $detail,
                  'mch_id'            => MCHID,
                  'nonce_str'         => $nonce_str,
                  //接收支付成功的回調函數,注意應可被其他ip訪問,url不能攜帶參數,不能有誤導性字符(例如?)
                  'notify_url'        => NOTIFY_URL,    
                  'openid'            => $user['open_id'],
                  'out_trade_no'      => $orderNumber,    //自定義訂單號
                  'spbill_create_ip'  => $userIp,
                  'total_fee'         => $money,
                  'trade_type'        => 'JSAPI',
                  'sign'              => $sign
              );

       

    2. 根據要求將參數轉化成xml形式
      //參數data爲整合好的數組
      function ToXml($data){
          $xml = "<xml>";
          foreach ($data as $key=>$val)
          {
              $xml .= "<".$key."><![CDATA[".$val."]]></".$key.">";
          }
          $xml.="</xml>";
          return $xml;
      }

       

    3. 發起訪問
              $ch = curl_init();
              curl_setopt($ch,CURLOPT_URL, 'https://api.mch.weixin.qq.com/pay/unifiedorder');
              curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE);
              curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//嚴格校驗
              //設置header
              curl_setopt($ch, CURLOPT_HEADER, FALSE);
              //要求結果爲字符串且輸出到屏幕上
              curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
              curl_setopt($ch, CURLOPT_POST, TRUE);
              curl_setopt($ch, CURLOPT_POSTFIELDS, $para);
              $data = curl_exec($ch);
              if($data){
                  curl_close($ch);
                  $data = simplexml_load_string($data,'SimpleXMLElement', LIBXML_NOCDATA);
                  $data = (array)$data;
                  $return['data'] = $data;
                  return $return;
              } else {
                  $error = curl_errno($ch);
                  curl_close($ch);
                  throw new WxPayException("curl出錯,錯誤碼:$error");
              }

      $data就是你獲取的結果,根據微信統一下單接口的描述,分別有兩個狀態碼return_code(返回狀態),result_code(業務狀態),你需要根據狀態碼來判斷不同的情況,從而更新用戶支付流程的進展

    4. 更新進展後需要將獲得的預支付id(prepay_id)及其他參數再次簽名打包給前端,用於確保數據的安全性

                  //數組的值都應爲string,若爲其他類型可以用strval()轉化
                  $config = array(
                      'appId'     => APP_ID,
                      'nonceStr'  => $noncestr,
                      'package'   => 'prepay_id=' . $prepay_id,
                      'signType'  => 'MD5',
                      'timeStamp' => strval($time)
                  );
                  $sign = getSign($config);
                  $config['paySign'] = $sign;
                  //config數組是用來調起內置對象WeixinJSBridge的參數

       

  5. 拼接前端調起WeixinJSBridge的參數參數文檔,連同上一步獲得的信息返回給前端
  6. 前端在WeixinJSBridge的第三個參數的函數中查詢訂單信息
    1. 因爲前端WeixinJSBridge的回調函數中不保證返回值即付款狀態的正確性,因此有以下幾種方法參考:
      1. 開發後臺提供訂單查詢接口
      2. 不對前端接收的支付結果進行判斷,都去請求後臺的訂單查詢接口
      3. 後臺配置回調接口的邏輯完整性,保證接收到微信的返回信息(如果有)並加以處理
      4. 在生成預支付id成功後返回前端前存入本地訂單信息爲未支付或其他狀態,接收到下一步正確信息後再予以更新
if (typeof WeixinJSBridge == "undefined"){
                    if( document.addEventListener ){
                        document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
                    }else if (document.attachEvent){
                        document.attachEvent('WeixinJSBridgeReady', onBridgeReady); 
                        document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
                    }
                }else{
                    let sn = res.data.para.out_trade_no
                    WeixinJSBridge.invoke(
                        'getBrandWCPayRequest', {
                            "appId": res.data.config.appId, //公衆號名稱,由商戶傳入 ok
                            "timeStamp":res.data.config.timeStamp, //時間戳,自1970年以來的秒數 ok    
                            "nonceStr": res.data.config.nonceStr, //隨機串     
                            "package": res.data.config.package,
                            "signType": res.data.config.signType, //微信簽名方式    
                            "paySign": res.data.config.paySign //微信簽名 
                        },
                        function(payres){
                            
                            if(payres.err_msg == "get_brand_wcpay_request:ok"){
                                // 使用以上方式判斷前端返回,微信團隊鄭重提示:
                                //res.err_msg將在用戶支付成功後返回ok,但並不保證它絕對可靠。
                                that.checkOrder(sn)    
                            }else if(payres.err_msg == "get_brand_wcpay_request:cancel"){
                                alert('取消支付')
                            }else if(payres.err_msg == "get_brand_wcpay_request:fail"){
                                alert('支付失敗')
                            }
                        }
                    ); 
                }

 

7.回調函數

public function index()
    {
        $str = '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
        $this->load->database();
        $post = $_REQUEST;
        if ($post == null) {
            $post = file_get_contents("php://input");
        }
        if ($post == null) {
            $post = isset($GLOBALS['HTTP_RAW_POST_DATA']) ? $GLOBALS['HTTP_RAW_POST_DATA'] : '';
        }
        $data = simplexml_load_string($post, 'SimpleXMLElement', LIBXML_NOCDATA);
        $post = (array)$data;
        if (empty($post) || $post == null || $post == '') {
            //阻止微信接口反覆回調接口
            echo $str;
            exit('非法調用');
        }
        //訂單號
        $out_trade_no = array_key_exists('out_trade_no',$post) && !empty($post['out_trade_no']) ? $post['out_trade_no'] : 0;
        //你的訂單信息
        //$order_info = 
        if (!empty($order_info)) {
            //接收到的簽名
            $post_sign = $post['sign'];
            //重新生成簽名
            $newSign = getSign($post);
            //簽名統一,則更新數據庫
            if ($post_sign == $newSign && $post['total_fee'] == $order_info['amount']*100) {
                //你的操作
            } else {
                //你的操作
            }
        }
        echo $str;
    }

注意事項:

  1. 整合請求參數時,將請求參數按參數名ascii碼值從小到大拼接爲字符串並生成簽名簽名算法,再在參數中拼接上生成的簽名,一同拼接成xml格式的請求參數,並初始化一個新的會話curl_init()向微信接口提交參數,接收返回的預支付id
  2. 生成的簽名可以先在簽名校驗工具中校驗,成功後再進行下一步,大多數都是在參數按序排列出錯
  3. 查詢狀態時,只依靠回調參數並不可靠(網絡,付款延遲等意外狀態),需要配合主動查詢接口更新付款狀態

其他:看文檔之前建議先弄懂流程(同時也是給自己的建議),再去有目的性的參考文檔,盲目拿來確實會有不少坑

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章