1. 程式人生 > >呼叫微信掃碼介面動態生成支付二維碼(java)

呼叫微信掃碼介面動態生成支付二維碼(java)

**

前言

**
剛入職的時候接到一個需求,做一個動態生成微信支付二維碼的網路介面,網上有許多很好的例子,官方文件也寫的很詳細,最後成功的實現了,當然後來想了一下,既然是網路通用介面,也許介面呼叫返回的不是一張二維碼圖片,而是一個字串比較好。後面這個我會慢慢解釋,我們來看一下具體是怎麼實現的。

實現步驟

一、開發前準備
工具 eclipse 開源免費
專案型別 maven
參考實現連結:http://www.demodashi.com/demo/10268.html 我就是參考這個連結做出來的。
要實現這個支付介面的呼叫,需要用到已認證的服務號。個人的訂閱號和公眾測試號是沒有這個許可權的。

2、開發過程中需要的引數如下圖所示,我也會解釋這些引數在哪裡可以找到。
開發過程需要的引數

appid 服務號公眾平臺的appid
登入微信公眾平臺https://mp.weixin.qq.com/
開發——基本配置——公眾號開發資訊——開發者ID(AppID)

商戶號
微信公眾平臺——微信支付——商戶資訊——基本資訊——商戶號

notify_url 掃碼支付回撥介面
微信商戶平臺——產品中心——開發配置——支付配置——掃碼支付

trade_type 交易型別
這個一般寫個 NATIVE 既可以

api_key 金鑰
微信商戶平臺(pay.weixin.qq.com)–>賬戶設定–>API安全–>金鑰設定

secertKey 網路介面呼叫金鑰 增加其安全性。
作為一個網路介面,要被呼叫不僅僅需要一些基本引數,還需要用於驗證身份的加密串和時間戳,避免被別人抓取資料包,解析請求地址和引數,從而不斷的發起請求,不僅浪費我們的伺服器資源,而且容易搞掛伺服器。

以上這七個引數基本是固定的。基本是不會改變的,商品名稱和金額這裡可以寫多個,一個商品名稱和金額一一對應,每次生成一個二維碼取一個組合即可。

個人建議:在編碼的時候,對於一些常數引數最好寫進配置檔案,然後呼叫的時候把他們寫成全域性靜態變數,這樣一來可以方便修改,比如說部署到生產伺服器的時候,如果你需要修改引數值,只需要修改配置檔案即可,也不需要重新編譯和重啟伺服器,但是你如果是硬編碼(在程式碼中寫死)的話,那麼你每次修改都得替換class檔案和重啟伺服器;二來可以提高伺服器響應速度。比如說要用到notify_url 掃碼支付回撥介面這個引數值,可以寫成


private static String NOTIFYURL = Constants.getProValue(“notify_url “);

3、配置log4j
以前我們本地除錯,輸出列印的時候,一般使用System.out.println()這樣的方式,但是部署到線上伺服器的時候,就沒有這種待遇了。所以建議大家養成使用log4j寫日誌的習慣,一旦發現問題,可以通過檢視當天的日誌,檢視具體的引數,從而定位和解決問題。這裡有兩個比較好的文章log4j.properties配置詳解與例項
我是這樣配置log4j的
log4j

我的建議是配置檔案最好都放在conf這個包下,規範,統一,不過當時抄別人的程式碼沒改。
這裡寫圖片描述

這是web.xml中的配置,webAppRootKey 和 log4jConfigLocation就是設定生成的檔案放置位置的。
這裡寫圖片描述

log4j還有個特性,這也是我測試的效果,如果你設定的是按天生成日誌,那麼他當天先會生成一個臨時檔案,我這裡生成的是weixin_pay_,等到了第二天 這個檔名字會變成20180102(weixin_pay_yyyy-MM-dd’.log’ )。
還有,以前是每個類定義日誌屬性我是使用
private static Logger logger = Logger.getLogger(UserController.class);也就是裡面放置類名,現在有一種通用的寫法: private static Logger Log = Logger.getLogger(“sysLog”);這樣不管你是哪個類,都適用。

三、程式碼執行邏輯

1、訪問控制器controller/UserController.java,下面是引數獲取
String productId = request.getParameter(“productId”);
String phone = request.getParameter(“phone”);
String isnotify = request.getParameter(“isnotify”);
String timestamp = request.getParameter(“timestamp”);
String authenticator = request.getParameter(“auth”);
其實如果只為了顯示一個二維碼,是不需要那麼多引數的,不過作為一個網路介面,安全性是不得不考慮的問題。phone 號碼isnotify 通知標誌,是用來發送成功簡訊的,也許對於你來說用不上。productId 產品id,用來獲取對應的商品名稱和金額,比如說001來獲取 #商品名稱title_wx_51talk_001,fee_wx_51talk_001 在配置檔案中對應的值。timestamp ,authenticator 是用來鑑權的,timestamp 時間戳,你可以生成11位毫秒數或者13位微妙數用來判斷引數中的時間戳是否過時,比如說超過兩小時,那麼你這個就是無效請求,不會往下執行。authenticator身份驗證加密串,我這裡是使用下面的方式加密,先用先將引數按一定順序排列,中間用 $ 隔開,再加上金鑰,進行md5加密,然後再用base64加密,這樣生成的字串安全性還是比較好的。到時候我們只要對比字串是否一樣就可以了。
String beforeAuth = productId + “符號(比如%)” + phone + “符號(比如%)” + isnotify + “符號(比如%)” + timestamp + “符號(比如%)” + secertKey;
String auth = Tools.getBase64Code(Tools.GetMD5Codes(beforeAuth));

2、請求引數的校驗。
我是使用 if (productId != null && timestamp != null && isnotify != null && authenticator != null) 這樣的方式來校驗是否為null避免空指標的,不過這樣還需要判斷是否為”“,比較麻煩。推薦大家使用下面這個方法,
public static String GetString(Object Obj, String defaultVal) {
try {
if (Obj == null || StringUtil.isEmptyString(Obj.toString()))
return defaultVal;
return Obj.toString();
} catch (Exception e) {
return defaultVal;
}
}

String phoneNum = Tools.GetString(request.getParameter(“phoneNum”), “”);
使用這個工具類就無需每次都去都要判斷是否為null,主要是用於避免空指標異常。

3、請求響應
我這裡使用的是 String resResult = “{\”returnCode\”:\”-1\”,\”returnMsg\”:\”productId is error\”}”;和 outPrint(response, resResult);這種方式來響應請求,其實這樣不是很好。我覺得我這個網路介面還可以早做一次優化,其實如果呼叫微信支付介面成功,他將會給你返回一個字串,然後你在製作成二維碼。那麼響應這裡就可以統一響應一個字串,讓呼叫介面的專案自己去製作二維碼,這樣一方面可以提高響應速度(返回一個字串應該比返回一張圖片速度更快),一方面程式碼也可以得到優化。
請求響應的話,建議可以使用實體,然後封裝成json資料,這是我目前覺得最好的選擇。比如,當然這裡沒有get set方法,也不是很好,不過可以用。
public class ResultEntity {

public int resultCode = 0;  

public int id = 0;

public T resultData;

public String errorMsg = "";

}
使用這個包
import com.alibaba.fastjson.JSON;
寫法:
ResultEntity result = new ResultEntity();
result.resultCode = 9999;
result.errorMsg = “操作程式碼為空”;
返回型別 String
return JSON.toJSONString(result);
使用上面的寫法,比我在專案的寫法好的多,也方便修改。

4、請求引數校驗通過
請求通過後,獲取配置檔案的引數值,呼叫 PayToolUtil.createSign(“UTF-8”, packageParams, key) 生成簽名。
這裡寫圖片描述

最後,製作二維碼和返回結果通知已經完成,成功生成二維碼。其實這裡還有個小問題,那就是支付成功之後,如果在客戶端進行頁面跳轉提示使用者,我參考的連結使用的是ajax輪詢的方式,不過考慮到使用者可能會關閉瀏覽器啥的;類似於京東或者其他公司,他們是使用支付後自動跳轉的方式,不過聽說這種對伺服器有要求,所以我這裡最後使用了將jar包製作成windows服務的方式,也就是啟動一個執行緒,不斷去查詢資料庫,如果有滿足條件的使用者,就給他傳送支付成功簡訊,同時將通知標誌改為1(已通知)。後面我將會寫一遍如何製作windows服務的部落格,歡迎大家來指點指點。

大家只需要將資料庫配置和微信相關配置改為自己的即可。