微信支付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>