1. 程式人生 > >微信支付Java開發服務端

微信支付Java開發服務端

想要呼叫微信支付第一就是獲取openid:
獲取openid的方法:
首先你需要在前臺獲取到code,

scope兩種引數的說明:
1、以snsapi_base為scope發起的網頁授權,是用來獲取進入頁面的使用者的openid的,並且是靜默授權並自動跳轉到回撥頁的。使用者感知的就是直接進入了回撥頁(往往是業務頁面)
2、以snsapi_userinfo為scope發起的網頁授權,是用來獲取使用者的基本資訊的。但這種授權需要使用者手動同意,並且由於使用者同意過,所以無須關注,就可在授權後獲取該使用者的基本資訊。
3、使用者管理類介面中的“獲取使用者基本資訊介面”,是在使用者和公眾號產生訊息互動或關注後事件推送後,才能根據使用者OpenID來獲取使用者基本資訊。這個介面,包括其他微信介面,都是需要該使用者(即openid)關注了公眾號後,才能呼叫成功的。

如果使用者同意授權,頁面將跳轉至 redirect_uri/?code=CODE&state=STATE。若使用者禁止授權,則重定向後不會帶上code引數,僅會帶上state引數redirect_uri?state=STATE

code說明 :
code作為換取access_token的票據,每次使用者授權帶上的code將不一樣,code只能使用一次,5分鐘未被使用自動過期。

這樣在前臺就可以直接獲取到url當中的code值,

var code = getQueryString("code");

function getQueryString(name) {
            var
reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i"); var r = window.location.search.substr(1).match(reg); if (r != null) return unescape(r[2]); return null; }

獲取到了code的值後,就可以去請求openid了。
尤其注意:由於公眾號的secret和獲取到的access_token安全級別都非常高,必須只儲存在伺服器,不允許傳給客戶端。後續重新整理access_token、通過access_token獲取使用者資訊等步驟,也必須從伺服器發起。

正確時返回的JSON資料包如下:

{
“access_token”:”ACCESS_TOKEN”,
“expires_in”:7200,
“refresh_token”:”REFRESH_TOKEN”,
“openid”:”OPENID”,
“scope”:”SCOPE”
}

其中注意attach引數,這個是附加資料,在查詢API和支付通知中原樣返回,可作為自定義引數使用。這個欄位可以傳我們的業務資料,用來後續的業務處理。

notify_url引數,這個回撥地址一定要寫外網能直接訪問的地址(可以直接把網址放到瀏覽器上,看看是不是能直接訪問),如果寫內網地址是收不到回撥資訊的哦。

spbill_create_ip引數,這個引數筆者也不清楚應該怎麼傳,筆者就傳的192.168.1.1,如果你們傳這個不好使我要找我。

public Map<String, Object> toMap()
    {
        Map<String, Object> map = new HashMap<String, Object>();
        Field[] fields = this.getClass().getDeclaredFields();
        for (Field field : fields)
        {
            Object obj;
            try
            {
                obj = field.get(this);
                if (obj != null)
                {
                    map.put(field.getName(), obj);
                }
            }
            catch (IllegalArgumentException e)
            {
                e.printStackTrace();
            }
            catch (IllegalAccessException e)
            {
                e.printStackTrace();
            }
        }
        return map;
    }

String sign = Signature.getSign(toMap());

public static String getSign(Map<String,Object> map){
        ArrayList<String> list = new ArrayList<String>();
        for(Map.Entry<String,Object> entry:map.entrySet()){
            if(entry.getValue()!=""){
                list.add(entry.getKey() + "=" + entry.getValue() + "&");
            }
        }
        int size = list.size();
        String [] arrayToSort = list.toArray(new String[size]);
        Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < size; i ++) {
            sb.append(arrayToSort[i]);
        }
        String result = sb.toString();
        result += "key=" + Configure.getKey();//自己保管的私鑰
        //Util.log("Sign Before MD5:" + result);
        result = MD5.MD5Encode(result).toUpperCase();
        //Util.log("Sign Result:" + result);
        return result;
    }

然後把這個sign放入引數中就可以啦。

請求成功後,微信會返回一段xml資料資訊,例如:

<xml>
   <return_code><![CDATA[SUCCESS]]></return_code>
   <return_msg><![CDATA[OK]]></return_msg>
   <appid><![CDATA[wx2421b1c4370ec43b]]></appid>
   <mch_id><![CDATA[10000100]]></mch_id>
   <nonce_str><![CDATA[IITRi8Iabbblz1Jc]]></nonce_str>
   <openid><![CDATA[oUpF8uMuAJO_M2pxb1Q9zNjWeS6o]]></openid>
   <sign><![CDATA[7921E432F65EB8ED0CE9755F0E86D72F]]></sign>
   <result_code><![CDATA[SUCCESS]]></result_code>
   <prepay_id><![CDATA[wx201411101639507cbf6ffd8b0779950874]]></prepay_id>
   <trade_type><![CDATA[JSAPI]]></trade_type>
</xml>

這裡提供一個把XML轉換成Map的方法:

public static Map<String,Object> getMapFromXML(String xmlString) throws ParserConfigurationException, IOException, SAXException {

        //這裡用Dom的方式解析回包的最主要目的是防止API新增回包欄位
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        InputStream is =  Util.getStringStream(xmlString);
        Document document = builder.parse(is);

        //獲取到document裡面的全部結點
        NodeList allNodes = document.getFirstChild().getChildNodes();
        Node node;
        Map<String, Object> map = new HashMap<String, Object>();
        int i=0;
        while (i < allNodes.getLength()) {
            node = allNodes.item(i);
            if(node instanceof Element){
                map.put(node.getNodeName(),node.getTextContent());
            }
            i++;
        }
        return map;

    }

到這裡我們拿到了prepay_id,這裡順便說一下,如果是掃碼支付前面的trade_type引數為NATIVE,再返回的時候會有一個code_url,在前臺直接用這個url生成二維碼圖片就可以了。

這裡什麼時間戳,隨機字串最後在服務端生成再傳到前臺來。

String timestamp = Long.toString(System.currentTimeMillis() / 1000); // 必填,生成簽名的時間戳  
String nonceStr = UUID.randomUUID().toString(); // 必填,生成簽名的隨機串  

注意package引數為prepay_id=PRERAY_ID一定要帶上prepay_id=。
同時生成簽名的時候也注意要帶著prepay_id=,這塊算個坑。

具體前臺調起微信支付的程式碼直接複製微信文件

function onBridgeReady(){
   WeixinJSBridge.invoke(
       'getBrandWCPayRequest', {
           "appId":"wx2421b1c4370ec43b",     //公眾號名稱,由商戶傳入     
           "timeStamp":"1395712654",         //時間戳,自1970年以來的秒數     
           "nonceStr":"e61463f8efa94090b1f366cccfbbb444", //隨機串     
           "package":"prepay_id=u802345jgfjsdfgsdg888",     
           "signType":"MD5",         //微信簽名方式:     
           "paySign":"70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信簽名 
       },
       function(res){     
           if(res.err_msg == "get_brand_wcpay_request:ok" ) {}     // 使用以上方式判斷前端返回,微信團隊鄭重提示:res.err_msg將在使用者支付成功後返回    ok,但並不保證它絕對可靠。 
       }
   ); 
}
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{
   onBridgeReady();
}

這裡注意支付成功後那裡的判斷res.err_msg == “get_brand_wcpay_request:ok”,坑爹的是那裡的:是中文的分號,我說回撥咋一直都不進呢!!
還有補充,要注意在商戶後臺配置支付授權目錄,否則支付頁面是出不來的,我當時沒配是一閃而過。

至此能夠成功調起微信支付,支付成功後會向notify_url傳送支付資訊。例如:

<xml>
  <appid><![CDATA[wx2421b1c4370ec43b]]></appid>
  <attach><![CDATA[支付測試]]></attach>
  <bank_type><![CDATA[CFT]]></bank_type>
  <fee_type><![CDATA[CNY]]></fee_type>
  <is_subscribe><![CDATA[Y]]></is_subscribe>
  <mch_id><![CDATA[10000100]]></mch_id>
  <nonce_str><![CDATA[5d2b6c2a8db53831f7eda20af46e531c]]></nonce_str>
  <openid><![CDATA[oUpF8uMEb4qRXf22hE3X68TekukE]]></openid>
  <out_trade_no><![CDATA[1409811653]]></out_trade_no>
  <result_code><![CDATA[SUCCESS]]></result_code>
  <return_code><![CDATA[SUCCESS]]></return_code>
  <sign><![CDATA[B552ED6B279343CB493C5DD0D78AB241]]></sign>
  <sub_mch_id><![CDATA[10000100]]></sub_mch_id>
  <time_end><![CDATA[20140903131540]]></time_end>
  <total_fee>1</total_fee>
  <trade_type><![CDATA[JSAPI]]></trade_type>
  <transaction_id><![CDATA[1004400740201409030005092168]]></transaction_id>
</xml>