一文快速實現微信公衆號支付功能

4.擁有一個正式的應用服務器,而且註冊域名
微信支付涉及的私密數據比較多,不容許使用natapp,花生殼之類的內網穿透工具實現,須要有正式的服務器環境,而且要註冊域名,不能使用IP。
好比:http://www.baidu.com
5.相關配置
5.1 配置支付受權目錄,登陸商戶平臺——>產品中心——>開發配置
圖中配置的例子,表明在項目根路徑下,以及web目錄下的頁面都有支付權限,若是不在該路徑的頁面,則沒法調用支付功能。
若頁面地址爲:http://mywexx.xxxx.com/web/pa...
則須要配置爲:http://mywexx.xxxx.com/web/javascript

5.2 設置API密鑰,登陸商戶平臺——>帳戶中心——>API安全——>API密鑰
該密鑰在後面的代碼中計算支付簽名的時候須要使用到。php

5.3 配置JS接口安全域名與網頁受權域名,登陸公衆平臺——>公衆號設置——>功能設置
配置網頁受權域名:主要用於獲取用戶的openId,須要識別這是哪一個人。
若對openID不瞭解的同窗可先參考微信公衆號開發文檔:https://mp.weixin.qq.com/wiki
配置JS接口安全域名:要讓咱們的頁面中彈出輸入密碼的窗口,須要使用微信提供的JS-SDK工具,若是不配置JS接口安全域名,你的頁面沒法使用JS-SDK。html

公衆號支付實現流程
大體流程參考官方提供的時序圖:java

流程有不少,不一一演示,咱們選取核心部分來實現。
1.提供商城主頁,用戶進入後經過網頁受權獲取openidjquery

若是對網頁受權不熟悉的同窗先參考這篇文章:
https://www.jianshu.com/p/94b...
訪問主頁的地址:http://www.wolfcode.cn/index.do
當用戶第一次打開主頁,默認沒有code參數,此時會先重定向到獲取受權的地址
(若是隻須要獲取openid,能夠使用scope爲snsapi_base靜默受權的方式)
通過受權地址再重定向到咱們的index.do時,會帶上code參數,此時便可經過接口獲取用戶的openid
[AppleScript] 純文本查看 複製代碼
?web

/sizealign=left@Controller/sizealign=leftpublic class IndexController { /sizealign=left @RequestMapping("index")/sizealign=left public void index(String code, Model model, HttpServletResponse response,HttpServletRequest request) {/sizealign=left //若是有code就能夠去獲取用戶的open id/size[/b] 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 方法在網頁受權的文章中有介紹。json

  1. WEB_REDIRECT_URL 是網頁受權的地址常量:

public static final String WEB_REDIRECT_URL = "https://open.weixin.qq.com/connect/oauth2/authorize?" +api

"appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_base#wechat_redirect";

2.點擊商品後跳轉到商品詳細頁面
具體頁面根據本身的項目添加,主要是頁面須要提供一個能夠立刻下訂單的按鈕便可。(這裏不演示加入購物車功能)

點擊當即購買按鈕跳轉到後臺下單地址,並帶上當前商品的id。
[AppleScript] 純文本查看 複製代碼

<script>
$(function () { //當即購買按鈕

$("#orderBtn").click(function(){            //獲取商品id
      var id = $("#productId").val();            //提交到下單
       window.location.href = "/order.do?productId="+id;
   })

})</script>

3.接收商品參數並調用微信支付統一下單接口
正常的業務流程是在該方法中,獲取商品id,再經過id去查詢數據庫該商品的相關屬性,好比名稱,價格等等,而後再建立業務訂單,再去調用微信支付的統一下單接口(讓微信生成預支付單,後續才能夠進行支付)。
但此處重點在支付流程,商品的屬性值和訂單相關值,暫且先使用假數據。
接口以及參數可參考微信官方提供的統一下單文檔:https://pay.weixin.qq.com/wik...
根據文檔介紹,咱們調用統一下單接口時須要帶上相關必填的參數以下:

1.PNG
把必填的參數封裝成對應的實體類:

調用接口成功後返回的結果也封裝成實體類:

該結果中最重要的是prepay_id參數,在頁面中彈出支付窗口時須要用到。

注意:下單的業務邏輯,正常是須要抽取到業務層的,可是此處爲了方便閱讀代碼,直接寫到了控制器上。
[Java] 純文本查看 複製代碼

[/b]
[b]@Controller[/b]
[b]public class OrderController { [/b]
[b] @RequestMapping("order") [/b]
[b] 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<Object, Object> map = new TreeMap<>();
  map.put("appId",WeChatUtil.APPID);            long timestamp = new Date().getTime();
  map.put("timestamp",timestamp);//全小寫
  map.put("nonceStr",uuid);
  map.put("signature",WeChatUtil.getSignature(timestamp,uuid,RequestUtil.getUrl(request)));
  model.addAttribute("configMap",map);            //微信支付權限驗證參數
  String prepayId = entity.getPrepay_id();
  TreeMap<Object, Object> payMap = new TreeMap<>();
  payMap.put("appId",WeChatUtil.APPID);
   payMap.put("timeStamp",timestamp);//駝峯
   payMap.put("nonceStr",uuid);
   payMap.put("package","prepay_id="+prepayId);
  payMap.put("signType","MD5");
  payMap.put("paySign",WeChatUtil.getPaySign(payMap));
   payMap.put("packageStr","prepay_id="+prepayId);
   model.addAttribute("payMap",payMap);
 }        //跳轉到查看訂單頁面
   return  "order";

}
}

下面是jssdk中config權限使用到的的簽名,以及微信支付使用的簽名的算法代碼。
官方文檔參考:
config簽名:https://mp.weixin.qq.com/wiki...
pay簽名:https://pay.weixin.qq.com/wik...

[Java] 純文本查看 複製代碼
?

/**

* 計算jssdk-config的簽名
* @param timestamp
* @param noncestr
* @param url
* @return
*/

public static String getSignature(Long timestamp,String noncestr,String url ){ [/b]
[b] //對全部待簽名參數按照字段名的ASCII 碼從小到大排序(字典序)
Map<String,Object> map = 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();
Set<String> set = 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 碼從小到大排序)
TreeMap<String, Object> map; if(!(obj instanceof Map)) {
map = ObjectUtils.objectToMap(obj);

}else{

map = (TreeMap)obj;

}

Set<Map.Entry<String, Object>> entrySet = map.entrySet(); //遍歷鍵值對
for (Map.Entry<String, Object> entry : 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/b3c...
在頁面中調用微信JS-SDK,經過config接口注入權限驗證配置,而且添加支付功能

[AppleScript] 純文本查看 複製代碼
?

<!--jquery-->
<script src="/js/jquery.min.js" type="text/javascript" charset="utf-8"></script>
<!--微信的JSSDK-->
<script src="http://res.wx.qq.com/open/js/...;></script>
<script>
$(function() {
<!--經過config接口注入權限驗證配置-->

wx.config({                debug: true, // 開啓調試模式
appId: '${configMap.appId}', // 公衆號的惟一標識
timestamp: '${configMap.timestamp}', // 生成簽名的時間戳
nonceStr: '${configMap.nonceStr}', // 生成簽名的隨機串

signature: '${configMap.signature}',// 簽名
jsApiList: ['chooseWXPay'] // 填入須要使用的JS接口列表,這裏是先聲明咱們要用到支付的JS接口

});            <!-- config驗證成功後會調用ready中的代碼 -->

wx.ready(function(){
//點擊立刻付款按鈕
$("#payBtn").click(function(){
//彈出支付窗口
wx.chooseWXPay({
timestamp: '${payMap.timeStamp}', // 支付簽名時間戳,
nonceStr: '${payMap.nonceStr}', // 支付簽名隨機串,不長於 32 位
package: '${payMap.packageStr}', // 統一支付接口返回的prepay_id參數值,提交格式如:prepay_id=xxxx)
signType: '${payMap.signType}', // 簽名方式,默認爲'SHA1',使用新版支付需傳入'MD5'
paySign: '${payMap.paySign}', // 支付簽名
success: function (res) {
// 支付成功後的回調函數
alert("支付成功!");
}
});
})
});
}); </script>

點擊立刻付款後可彈出支付窗口:

支付完成:

5.支付結果的處理
當用戶支付後,微信會把支付結果發送到咱們前面指定的notify_url地址,咱們能夠根據支付結果的參數來作相關的業務邏輯,但這裏暫不實現,具體支付通知結果的參數可參考官方文章:
https://pay.weixin.qq.com/wik...

做者:陳惠
連接:https://www.jianshu.com/p/9c3...

相關文章
相關標籤/搜索