點選上方[全棧開發者社群]→右上角[...]→[設為星标⭐]

♪ 點選上方綠标 收聽微信小程式-登入+支付
介紹一個可運作的微信小程式登入+支付的demo。接觸了小程式簡易教程的,想必都知道我們必然有自己的背景應用伺服器,來處理我們自己的業務邏輯、請求微信服務完成一定的功能。在此,我們的背景采用java環境,本文将首先介紹環境搭建的過程,随後介紹登入+支付的流程及代碼。
一、背景web服務環境搭建
1. 安裝jdk、tomcat,ICP備案的域名準備。
Linux安裝jdk:https://blog.csdn.net/zhang918784312/article/details/79751283
Linux 安裝tomcat:https://www.cnblogs.com/EasonJim/p/7202844.html
經過icp備案的域名,請自行準備。
2.配置https,由于小程式請求url必須是https,故而必須配置支援https請求。本人采用的是在阿裡雲購買的域名,故而采用的證書也是阿裡雲生成的ssl證書,可參考如下兩篇博文進行配置。當然,你也可以通過别的方式生成證書,更或者通過nginx作反向代理到你的伺服器。
https://blog.csdn.net/qq_28189091/article/details/75078164
https://blog.csdn.net/z_xuewen/article/details/78176509
同時,務必将您的小程式域名綁定在小程式後端。登入小程式背景,【設定】-【開發設定】-【伺服器域名】
3. 部署web服務
如上兩步完成後,請務必确認通過你的域名(https://...)可以展示tomcat的預設頁之後,開始部署我們的web服務。在此,就簡單粗暴的在webapps下建立小程式的根目錄,我命名為wechatserver,在此目錄下,建立WEB-INFO,下面的目錄結構如下:
classes存放自己寫的類的classes檔案,lib存放我們項目依賴的jar包,logs用于存放我們的日志輸出,web.xml是我們這個項目的配置。 demo中,我們隻有一個servlet接收小程式前端請求,web.xml中增加配置如下:
<servlet> <servlet-name>WechatServletservlet-name> <servlet-class>com.icbc.servlet.WechatServletservlet-class> servlet> <servlet-mapping> <servlet-name>WechatServletservlet-name> <url-pattern>/servlet/WechatServleturl-pattern> servlet-mapping>
4. log4j 應用 在開發調試中,我們免不了需要通過列印日志進行調試,是以在此增加了日志的使用。web.xml中增加配置:
<context-param> <param-name>log4jConfigLocationparam-name> <param-value>classes/log4j.propertiesparam-value> context-param>
在classes增加檔案,log4j.properties,内容如下:
log4j.rootLogger = INFO,toConsole,D,Elog4j.appender.toConsole=org.apache.log4j.ConsoleAppender log4j.appender.toConsole.Target=System.out log4j.appender.toConsole.layout=org.apache.log4j.PatternLayout log4j.appender.toConsole.layout.ConversionPattern=[%d{yyyy-MM-dd HH:mm:ss}] [%p] %m%nlog4j.appender.D = org.apache.log4j.DailyRollingFileAppenderlog4j.appender.D.file = 你的目錄/common.loglog4j.appender.D.Append = truelog4j.appender.D.Threshold = info log4j.appender.D.layout = org.apache.log4j.PatternLayoutlog4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%nlog4j.appender.D.DatePattern='.'yyyy-MM-dd'.log'log4j.appender.E = org.apache.log4j.DailyRollingFileAppenderlog4j.appender.E.file = 你的目錄/error.log log4j.appender.E.Append = truelog4j.appender.E.Threshold = ERROR log4j.appender.E.layout = org.apache.log4j.PatternLayoutlog4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%nlog4j.appender.D.DatePattern='.'yyyy-MM-dd'.log'
log4j 在java中的引用、使用: public static Logger logger = Logger.getLogger(WechatServlet.class); logger.info(“列印資訊”);
二、微信小程式登入+支付
1. 小程式前端目錄準備
基于微信小程式工具生成的預設hello world程式,pages下先建立目錄order,随後在order目錄生成一個新的page,命名為order,結構如下圖:
在index中增加按鈕,進入order。 index.wxml
<view> <navigator class="index-intro__btn btn btn-danger btn-md" url="/pages/order/order">進入商城navigator> view>
order.xml中描述商品資訊,增加支付按鈕,order.js 支付事件處理。
2. 登入+支付 code
流程大概分為幾步:
1)登入,擷取code(一個code隻能用一次)
2)通過code擷取openid(通過請求伺服器,由伺服器請求微信擷取并傳回小程式)。微信登入+擷取openid接口。
3)小程式請求伺服器進行預下單,上送商品詳情、金額、openid。
4)伺服器端接收請求,根據請求訂單資料、生成第三方訂單号,調用微信的統一下單接口。
5)伺服器收到預下單資訊後,簽名并組裝支付資料,傳回給小程式。所需資料見:小程式支付接口
6)小程式前端發起支付,并支付完成
7)伺服器收到回調。
2.1 登入,擷取code。
onLoad: function (options) { // 登入 wx.login({ success: function (res) { // 發送 res.code 到背景換取 openId, sessionKey, unionId var that = this; if (res.code) { console.log('擷取使用者登入态success!' + res.code) app.globalData.code = res.code } else { console.log('擷取使用者登入态失敗!' + res.errMsg) } } }) },
2.2 通過code 擷取openid(前端)
getOpenId:function(that, code){ console.log(code); let operFlag = "getOpenid"; console.log(operFlag); wx.request({ url: 'https://xxx/wechatserver/servlet/WechatServlet', data: { code:code, operFlag:operFlag }, header: { 'content-type': 'application/json' }, success: function (res) { console.log(res); var openid = res.data.openid; console.log(openid); that.paypay(that, openid); //預下單并支付 }, fail: function (res) { console.log(res.data.errmsg); console.log(res.data.errcode); }, complete:function(res){ } }) },
2.2 伺服器端servlet(複寫HttpServlet的doGet doPost函數)doPost的代碼片段:
//擷取操作類型,根據類型執行不同操作 String operFlag = request.getParameter("operFlag"); logger.info("operFlag=" + operFlag); String results = ""; if("getOpenid".equals(operFlag)){ String code = request.getParameter("code"); logger.info("code=" + code); String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + appid + "&secret=" + secretKey + "&js_code=" + code + "&grant_type=authorization_code"; logger.info("url=" + url); results = sendGetReq(url);//發送http請求 } logger.info("results = " + results); response.setContentType("application/json;charset=UTF-8"); response.setHeader("catch-control", "no-catch"); PrintWriter out = response.getWriter(); out.write(results); out.flush(); out.close();
2.3 前端上送訂單資訊、openid請求預下單(在此,為友善,訂單資訊直接寫死在伺服器端了),若成功,則根據伺服器端傳回資料發起支付。
paypay: function (that, openid) { let operFlag = 'pay'; wx.request({ url: 'https://xxx/wechatserver/servlet/WechatServlet', data: { openid: openid, operFlag: operFlag }, header: { 'content-type': 'application/json' }, success: function (res) { console.log(res); wx.requestPayment({ 'timeStamp': res.data.timeStamp, 'nonceStr': res.data.nonceStr, 'package': res.data.package, 'signType': 'MD5', 'paySign': res.data.sign, 'success': function (res) { if (res.errMsg == "requestPayment:ok") { wx.showToast({ title: '支付成功' }) } }, 'fail': function (res) { } }) }, fail: function (res) { console.log(res.data.errmsg); console.log(res.data.errcode); }, complete: function (res) { } }) },
2.4 伺服器端預下單,2.5并簽名傳回支付請求資料。
if("pay".equals(operFlag)){ String openid = request.getParameter("openid"); logger.info("openid = " + openid); String url = "https://api.mch.weixin.qq.com/pay/unifiedorder"; String reqStr = getReqStr(openid); //組裝預下單的請求資料 logger.info("reqStr=" + reqStr); results = sendPost(url,reqStr);//發送post資料到微信預下單 logger.info("prepay from weixin: \n " + results); Map<String,String> return_data = null; try { return_data = WXPayUtil.xmlToMap(results);//微信的一個工具類 } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); logger.error(e.getMessage()); } String return_code = return_data.get("return_code"); logger.info("return_code=" + return_code); if("SUCCESS".equals(return_code)){ String prepay_id = return_data.get("prepay_id"); results = conPayParam(prepay_id); //組裝傳回資料 }else{ results ="{\"return_code\":\"fail\"}"; } }
三、實戰中遇到的問題
預下單和支付請求中,簽名的密鑰使用的是商戶密鑰,但是用code擷取openid是使用小程式對應的secret key,這個可以在小程式的背景看到。
微信小程式前端發起post請求到伺服器端時,伺服器端收不到請求參數。原因是:微信API接口wx.request中:
a) 對于 GET 方法的資料,會将資料轉換成 query string(encodeURIComponent(k)=encodeURIComponent(v)&encodeURIComponent(k)=encodeURIComponent(v)…)
b1) 對于 POST 方法且 header[‘content-type’] 為 application/json 的資料,會對資料進行 JSON 序列化
b2) 對于 POST 方法且 header[‘content-type’] 為 application/x-www-form-urlencoded 的資料,會将資料轉換成 query string (encodeURIComponent(k)=encodeURIComponent(v)&encodeURIComponent(k)=encodeURIComponent(v)…)
是以,如果post請求,為省去伺服器端反序列化的操作時,可使用header[‘content-type’] 為 application/x-www-form-urlencoded 的資料。
3. 如果部署了servlet後,tomcat重新開機後,需要等幾分鐘才能生效(原因是我的機器記憶體比較小,而tomcat又很占用記憶體資源),待熟悉tomcat 調優。
留言打卡第16天,早日脫單
。
覺得本文對你有幫助?請分享給更多人
關注「全棧開發者社群」加星标,提升全棧技能
本公衆号會不定期給大家發福利,包括送書、學習資源等,敬請期待吧!
如果感覺推送内容不錯,不妨右下角點個在看轉發朋友圈或收藏,感謝支援。
好文章,我在看❤️