進階架構精品專題
- Mysql優化專題(★★★★)
- 網絡協議專題(★★★★)
- 其餘18大專題,請在主頁菜單欄查看
- 後臺回覆【加群】,獲取BAT真實面經
![一文快速實現微信公眾號支付功能(詳細版,建議收藏備用)](http://p2.ttnews.xyz/loading.gif)
微信支付實際上有很多種不同的類型,具體要使用哪一種就需要根據不同的應用場景來選擇,官方給出的參考例子:
刷卡支付:用戶打開微信錢包的刷卡的界面,商戶掃碼後提交完成支付。
掃碼支付:用戶打開"微信掃一掃“,掃描商戶的二維碼後完成支付
APP支付:商戶APP中集成微信SDK,用戶點擊後跳轉到微信內完成支付
H5支付:用戶在微信以外的手機瀏覽器請求微信支付的場景喚起微信支付
小程序支付:用戶在微信小程序中使用微信支付的場景
建議大家先閱讀以下文章瞭解相關內容:
網頁授權:https://www.jianshu.com/p/94b0e53cccc3
微信JS-SDK:https://www.jianshu.com/p/b3c4450f845e
實現效果如下動圖:
![一文快速實現微信公眾號支付功能(詳細版,建議收藏備用)](http://p2.ttnews.xyz/loading.gif)
本篇文章中實現的是公眾號支付,實現條件如下:
3.到微信商戶平臺https://pay.weixin.qq.com 註冊一個商戶賬號,並關聯你的公眾號,如果需要實現小程序支付的,需要關聯小程序。
4.擁有一個正式的應用服務器,並且註冊域名
微信支付涉及的私密數據比較多,不允許使用natapp,花生殼之類的內網穿透工具實現,需要有正式的服務器環境,並且要註冊域名,不能使用IP。
比如:http://www.baidu.com
5.相關配置
5.1 配置支付授權目錄,登錄商戶平臺——>產品中心——>開發配置
圖中配置的例子,代表在項目根路徑下,以及web目錄下的頁面都有支付權限,如果不在該路徑的頁面,則無法調用支付功能。
若頁面地址為:http://mywexx.xxxx.com/web/pay.html
則需要配置為:http://mywexx.xxxx.com/web/
5.2 設置API密鑰,登錄商戶平臺——>賬戶中心——>API安全——>API密鑰
該密鑰在後面的代碼中計算支付簽名的時候需要使用到。
5.3 配置JS接口安全域名與網頁授權域名,登錄公眾平臺——>公眾號設置——>功能設置
配置網頁授權域名:主要用於獲取用戶的openId,需要識別這是哪個人。
若對openID不瞭解的同學可先參考微信公眾號開發文檔:https://mp.weixin.qq.com/wiki
配置JS接口安全域名:要讓我們的頁面中彈出輸入密碼的窗口,需要使用微信提供的JS-SDK工具,如果不配置JS接口安全域名,你的頁面無法使用JS-SDK。
大致流程參考官方提供的時序圖:
流程有很多,不一一演示,我們選取核心部分來實現。
1.提供商城主頁,用戶進入後通過網頁授權獲取openid
如果對網頁授權不熟悉的同學先參考這篇文章:
https://www.jianshu.com/p/94b0e53cccc3
訪問主頁的地址:http://www.wolfcode.cn/index.do
當用戶第一次打開主頁,默認沒有code參數,此時會先重定向到獲取授權的地址
(如果只需要獲取openid,可以使用scope為snsapi_base靜默授權的方式)
經過授權地址再重定向到我們的index.do時,會帶上code參數,此時即可通過接口獲取用戶的openid
@Controllerpublic class IndexController { @RequestMapping("index") public void index(String code, Model model, HttpServletResponse response,HttpServletRequest request) { //如果有code就可以去獲取用戶的openid
if(code!=null) { //通過code來換取access_token
JSONObject json = WeChatUtil.getWebAccessToken(code); //獲取用戶openid
String openid = json.getString("openid"); //設置到會話中
request.getSession().setAttribute("openid",openid); //重定向到主頁
response.sendRedirect("/index.html");
}else{ //重定向到授權頁面
response.sendRedirect(WeChatUtil.WEB_REDIRECT_URL.replace("APPID",WeChatUtil.APPID)
.replace("REDIRECT_URI", RequestUtil.getUrl(request)));
}
}
}
注意: 1. WeChatUtil.getWebAccessToken 方法在網頁授權的文章中有介紹。
2. WEB_REDIRECT_URL 是網頁授權的地址常量:
public static final String WEB_REDIRECT_URL = "https://open.weixin.qq.com/connect/oauth2/authorize?" +
"appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_base#wechat_redirect";
2.點擊商品後跳轉到商品詳細頁面
具體頁面根據自己的項目添加,主要是頁面需要提供一個可以馬上下訂單的按鈕即可。(這裡不演示加入購物車功能)
點擊立即購買按鈕跳轉到後臺下單地址,並帶上當前商品的id。
3.接收商品參數並調用微信支付統一下單接口
正常的業務流程是在該方法中,獲取商品id,再通過id去查詢數據庫該商品的相關屬性,比如名稱,價格等等,然後再創建業務訂單,再去調用微信支付的統一下單接口(讓微信生成預支付單,後續才可以進行支付)。
但此處重點在支付流程,商品的屬性值和訂單相關值,暫且先使用假數據。
接口以及參數可參考微信官方提供的統一下單文檔:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
根據文檔介紹,我們調用統一下單接口時需要帶上相關必填的參數如下:
1.PNG
把必填的參數封裝成對應的實體類:
調用接口成功後返回的結果也封裝成實體類:
該結果中最重要的是prepay_id參數,在頁面中彈出支付窗口時需要用到。
注意:下單的業務邏輯,正常是需要抽取到業務層的,但是此處為了方便閱讀代碼,直接寫到了控制器上。
@Controllerpublic class OrderController { @RequestMapping("order") public String save(Long productId,Model model,HttpServletRequest request) throws Exception { //根據商品id查詢商品詳細信息(假數據)
//productService.getProductById(productId)
double price = 0.01;//(0.01元)
String productName = "SweetCity"; //生成訂單編號
int number = (int)((Math.random()*9)*1000);//隨機數
DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");//時間
String orderNumber = dateFormat.format(new Date()) + number; //獲取openId
String openId = (String) request.getSession().getAttribute("openid"); //準備調用接口需要的參數
WxOrderEntity order = new WxOrderEntity(); //公眾號appid
order.setAppid(WeChatUtil.APPID); //商戶號
order.setMch_id(WeChatUtil.MCH_ID); //商品描述
order.setBody(productName); //設備號,公眾號支付直接填WEB
order.setDevice_info("WEB"); //交易類型
order.setTrade_type("JSAPI"); //商戶訂單號
order.setOut_trade_no(orderNumber); //支付金額(單位:分)
order.setTotal_fee((int)(price*100)); //用戶ip地址
order.setSpbill_create_ip(RequestUtil.getIPAddress(request)); //用戶openid
order.setOpenid(openId); //接收支付結果的地址
order.setNotify_url("http://www.wolfcode.com/receive.do"); //32位隨機數(UUID去掉-就是32位的)
String uuid = UUID.randomUUID().toString().replace("-", "");
order.setNonce_str(uuid); //生成簽名
String sign = WeChatUtil.getPaySign(order);
order.setSign(sign); //調用微信支付統一下單接口,讓微信也生成一個預支付訂單
String xmlResult = HttpUtil.post(GET_PAY_URL,XMLUtil.toXmlString(order)); //把返回的xml字符串轉成對象
WxOrderResultEntity entity = XMLUtil.toObject(xmlResult,WxOrderResultEntity.class); //如果微信預支付單成功創建,就跳轉到支付訂單頁進行支付
if(entity.getReturn_code().equals("SUCCESS")&&entity.getResult_code().equals("SUCCESS")){ //jssdk權限驗證參數
TreeMap
下面是jssdk中config權限使用到的的簽名,以及微信支付使用的簽名的算法代碼。
官方文檔參考:
config簽名:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115
pay簽名:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3
/**
* 計算jssdk-config的簽名
* @param timestamp
* @param noncestr
* @param url
* @return
*/
public static String getSignature(Long timestamp,String noncestr,String url ){ //對所有待簽名參數按照字段名的ASCII 碼從小到大排序(字典序)
Mapmap = new TreeMap<>();
map.put("jsapi_ticket",getTicket());
map.put("timestamp",timestamp);
map.put("noncestr",noncestr);
map.put("url",url); //使用URL鍵值對的格式(即key1=value1&key2=value2…)拼接成字符串string1
StringBuilder sb = new StringBuilder();
Setset = map.keySet(); for (String key : set) {
sb.append(key+"="+map.get(key)).append("&");
} //去掉最後一個&符號
String temp = sb.substring(0,sb.length()-1); //使用sha1加密
return SecurityUtil.SHA1(temp);
} /**
* 計算微信支付的簽名
* @param obj
* @return
* @throws IllegalAccessException
*/
public static String getPaySign(Object obj) throws IllegalAccessException, IOException {
StringBuilder sb = new StringBuilder(); //把對象轉為TreeMap集合(按照key的ASCII 碼從小到大排序)
TreeMapmap; if(!(obj instanceof Map)) {
map = ObjectUtils.objectToMap(obj);
}else{
map = (TreeMap)obj;
}
Set> entrySet = map.entrySet(); //遍歷鍵值對
for (Map.Entryentry : entrySet) { //如果值為空,不參與簽名
if(entry.getValue()!=null) { //格式key1=value1&key2=value2…
sb.append(entry.getKey() + "=" + entry.getValue() + "&");
}
} //最後拼接商戶的API密鑰
String stringSignTemp = sb.toString()+"key="+WeChatUtil.KEY; //進行md5加密並轉為大寫
return SecurityUtil.MD5(stringSignTemp).toUpperCase();
}
4.提供訂單展示頁面
若對微信JS-SDK不瞭解的同學可先參考該文章:
https://www.jianshu.com/p/b3c4450f845e
在頁面中調用微信JS-SDK,通過config接口注入權限驗證配置,並且添加支付功能。
點擊馬上付款後可彈出支付窗口:
支付完成:
5.支付結果的處理
當用戶支付後,微信會把支付結果發送到我們前面指定的notify_url地址,我們可以根據支付結果的參數來做相關的業務邏輯,但這裡暫不實現,具體支付通知結果的參數可參考官方文章:
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7&index=8
鏈接:https://www.jianshu.com/p/9c322b1a5274
閱讀更多 Java進階架構師 的文章