一:前期微信支付掃盲知識
前提條件是已經(jīng)有申請了微信支付功能的公眾號,然后我們需要得到公眾號APPID和微信商戶號,這個分別在微信公眾號和微信支付商家平臺上面可以發(fā)現(xiàn)。其實在你申請成功支付功能之后,微信會通過郵件把Mail轉(zhuǎn)給你的,有了這些信息之后,我們就可以去微信支付服務(wù)支持頁面:https://pay.weixin.qq.com/service_provider/index.shtml
打開這個頁面,點擊右上方的鏈接【開發(fā)文檔】會進入到API文檔說明頁面,看起來如下
?
選擇紅色圓圈的掃碼支付就是我們要做接入方式,鼠標移動到上面會提示你去查看開發(fā)文檔,如果這個都不知道怎么查看,可以洗洗睡了,你真的不合適做程序員,地址如下:
https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_1在瀏覽器中打開之后會看到
?
我們重點要關(guān)注和閱讀的內(nèi)容我已經(jīng)用紅色橢圓標注好了,首先閱讀【接口規(guī)則】里面的協(xié)議規(guī)范,開玩笑這個都不讀你就想做微信支付,這個就好比你要去泡妞,得先收集點基本背景信息,了解對方特點,不然下面還怎么溝通。事實證明只有會泡妞得程序員才是好銷售。跑題了我們接下來要看一下【場景介紹】中的案例與規(guī)范,只看一下記得一定要微信支付的LOGO下載下來,是為了最后放到我們自己的掃碼支付網(wǎng)頁上,這樣看上去比較專業(yè)一點。之后重點關(guān)注【模式二】
我們這里就是要采用模式二的方式實現(xiàn)PC端頁面掃碼支付功能。
??? 微信官方對模式二的解釋是這樣的“商戶后臺系統(tǒng)先調(diào)用微信支付的統(tǒng)一下單接口,微信后臺系統(tǒng)返回鏈接參數(shù)code_url,商戶后臺系統(tǒng)將code_url值生成二維碼圖片,用戶使用微信客戶端掃碼后發(fā)起支付。注意:code_url有效期為2小時,過期后掃碼不能再發(fā)起支付”。看明白了吧就是我們首先要調(diào)用微信提供統(tǒng)一下單接口,得到一個關(guān)鍵信息code_url(至于這個code_url是什么鬼,我也不知道),然后我們通過自己的程序把這個URL生成一個二維碼,生成二維碼我這里用了Google的zxing庫。然后把這個二維碼顯示在你的PC端網(wǎng)頁上就行啦。這樣終端用戶一掃碼就支付啦,支付就完成啦,看到這里你肯定很激動,發(fā)現(xiàn)微信支付如此簡單,等等還有個事情我們還不知道,客戶知道付錢了,我們服務(wù)器端還不知道呢,以微信開發(fā)人員的智商他們早就想到這個問題了,所以讓你在調(diào)用統(tǒng)一下單接口的時候其中有個必填的參數(shù)就是回調(diào)URL,就是如果客戶端付款成功之后微信會通過這個URL向我們自己的服務(wù)器提交一些數(shù)據(jù),然后我們后臺解析這些數(shù)據(jù),完成我們自己操作。這樣我們才知道客戶是否真的已經(jīng)通過微信付款了。這樣整個流程才結(jié)束,這個就是模式二。微信用一個時序圖示這樣表示這個過程的。
?
表達起來比較復(fù)雜,看上去比較吃力,總結(jié)一下其實我們服務(wù)器該做的事情就如下件:
1. 通過統(tǒng)一下單接口傳入正確的參數(shù)(當然要包括我們的回調(diào)URL)與簽名驗證,從返回數(shù)據(jù)中得到code_url的對應(yīng)數(shù)據(jù)
2. 根據(jù)code_url的數(shù)據(jù)我們自己生成一個二維碼圖片,顯示在瀏覽器網(wǎng)頁上
3. 在回調(diào)的URL中添加我們自己業(yè)務(wù)邏輯處理。
?
至此掃盲結(jié)束了,你終于知道掃碼支付什么個什么樣的流程了,下面我們就一起來扒扒它的相關(guān)API使用,做好每步處理。
二:開發(fā)過程
在開發(fā)代碼之前,請先準備幾件事情。
1. 添加ZXing的maven依賴
2. 添加jdom的maven依賴
3.下載Java版本SDK演示程序,地址在這里
https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=11_1
我們需要MD5Util.java和XMLUtil.java兩個文件
4. 我們使用HttpClient版本是4.5.1,記得添加Maven依賴
上面準備工作做好以后,繼續(xù)往下看:
首先我們要調(diào)用微信的統(tǒng)一下單接口,我們點擊【API列表】中的統(tǒng)一下單會看到這樣頁面:
?
以本人調(diào)用實際情況為例,如下的參數(shù)是必須要有的,為了大家的方便我已經(jīng)把它變成一個POJO的對象, 代碼如下:
[java]?view plaincopy
public?class?UnifiedorderDto?implements?WeiXinConstants?{????????private?String?appid;??????private?String?body;??????private?String?device_info;??????private?String?mch_id;??????private?String?nonce_str;??????private?String?notify_url;??????private?String?openId;??????private?String?out_trade_no;??????private?String?spbill_create_ip;??????private?int?total_fee;??????private?String?trade_type;??????private?String?product_id;??????private?String?sign;????????????public?UnifiedorderDto()?{??????????this.appid?=?APPID;??????????this.mch_id?=?WXPAYMENTACCOUNT;??????????this.device_info?=?DEVICE_INFO_WEB;??????????this.notify_url?=?CALLBACK_URL;??????????this.trade_type?=?TRADE_TYPE_NATIVE;??????}????????public?String?getAppid()?{??????????return?appid;??????}????????public?void?setAppid(String?appid)?{??????????this.appid?=?appid;??????}????????public?String?getBody()?{??????????return?body;??????}????????public?void?setBody(String?body)?{??????????this.body?=?body;??????}????????public?String?getDevice_info()?{??????????return?device_info;??????}????????public?void?setDevice_info(String?device_info)?{??????????this.device_info?=?device_info;??????}????????public?String?getMch_id()?{??????????return?mch_id;??????}????????public?void?setMch_id(String?mch_id)?{??????????this.mch_id?=?mch_id;??????}????????public?String?getNonce_str()?{??????????return?nonce_str;??????}????????public?void?setNonce_str(String?nonce_str)?{??????????this.nonce_str?=?nonce_str;??????}????????public?String?getNotify_url()?{??????????return?notify_url;??????}????????public?void?setNotify_url(String?notify_url)?{??????????this.notify_url?=?notify_url;??????}????????public?String?getOpenId()?{??????????return?openId;??????}????????public?void?setOpenId(String?openId)?{??????????this.openId?=?openId;??????}????????public?String?getOut_trade_no()?{??????????return?out_trade_no;??????}????????public?void?setOut_trade_no(String?out_trade_no)?{??????????this.out_trade_no?=?out_trade_no;??????}????????public?String?getSpbill_create_ip()?{??????????return?spbill_create_ip;??????}????????public?void?setSpbill_create_ip(String?spbill_create_ip)?{??????????this.spbill_create_ip?=?spbill_create_ip;??????}????????public?int?getTotal_fee()?{??????????return?total_fee;??????}????????public?void?setTotal_fee(int?total_fee)?{??????????this.total_fee?=?total_fee;??????}????????public?String?getTrade_type()?{??????????return?trade_type;??????}????????public?void?setTrade_type(String?trade_type)?{??????????this.trade_type?=?trade_type;??????}????????public?String?getSign()?{??????????return?sign;??????}????????public?void?setSign(String?sign)?{??????????this.sign?=?sign;??????}????????public?String?getProduct_id()?{??????????return?product_id;??????}????????public?void?setProduct_id(String?product_id)?{??????????this.product_id?=?product_id;??????}??????public?String?generateXMLContent()?{??????????String?xml?=?"<xml>"?+?????????????"<appid>"?+?this.appid?+?"</appid>"?+??????????????"<body>"?+?this.body?+?"</body>"?+??????????????"<device_info>WEB</device_info>"?+??????????????"<mch_id>"?+?this.mch_id?+?"</mch_id>"?+??????????????"<nonce_str>"?+?this.nonce_str?+?"</nonce_str>"?+?????????????"<notify_url>"?+?this.notify_url?+?"</notify_url>"?+??????????????"<out_trade_no>"?+?this.out_trade_no?+?"</out_trade_no>"?+??????????????"<product_id>"?+?this.product_id?+?"</product_id>"?+?????????????"<spbill_create_ip>"?+?this.spbill_create_ip+?"</spbill_create_ip>"?+?????????????"<total_fee>"?+?String.valueOf(this.total_fee)?+?"</total_fee>"?+??????????????"<trade_type>"?+?this.trade_type?+?"</trade_type>"?+??????????????"<sign>"?+?this.sign?+?"</sign>"?+???????????"</xml>";??????????return?xml;??????}????????????public?String?makeSign()?{??????????String?content?="appid="?+?this.appid?+??????????????????????"&body="?+?this.body?+??????????????????????"&device_info=WEB"?+??????????????????????"&mch_id="?+?this.mch_id?+??????????????????????"&nonce_str="?+?this.nonce_str?+??????????????????????"?ify_url="?+?this.notify_url?+?????????????????????"&out_trade_no="?+?this.out_trade_no?+??????????????????????"&product_id="?+?this.product_id?+??????????????????????"&spbill_create_ip="?+?this.spbill_create_ip+?????????????????????"&total_fee="?+?String.valueOf(this.total_fee)?+?????????????????????"&trade_type="?+?this.trade_type;??????????content?=?content?+?"&key="?+?WeiXinConstants.MD5_API_KEY;??????????String?esignature?=?WeiXinPaymentUtil.MD5Encode(content,?"utf-8");??????????return?esignature.toUpperCase();??????}????????}?? 其中各個成員變量的解釋可以參見【統(tǒng)一下單接口】的說明即可。
有這個之后我們就要要設(shè)置的內(nèi)容填寫進去,去調(diào)用該接口得到返回數(shù)據(jù),從中拿到code_url的數(shù)據(jù)然后據(jù)此生成一個二維圖片,把圖片的地址返回給PC端網(wǎng)頁,然后它就會顯示出來,這里要特別說明一下,我們自己PC端網(wǎng)頁在點擊微信支付的時候就會通過ajax方式調(diào)用我們自己后臺的SpringMVC Controller然后在Controller的對應(yīng)方法中通過HTTPClient完成對微信統(tǒng)一下單接口調(diào)用解析返回的XML數(shù)據(jù)得到code_url的值,生成二維碼之后返回給前臺網(wǎng)頁。Controller中實現(xiàn)的代碼如下:
?
[java]?view plaincopy
Map<String,Object>?result=new?HashMap<String,Object>();??????????UnifiedorderDto?dto?=?new?UnifiedorderDto();??????????if(cash?==?null?||?"".equals(cash))?{??????????????result.put("error",?"cash?could?not?be?zero");??????????????return?result;??????????}??????????int?totalfee?=?100*Integer.parseInt(cash);??????????logger.info("total?recharge?cash?:?"?+?totalfee);??????????dto.setProduct_id(String.valueOf(System.currentTimeMillis()));??????????dto.setBody("repair");??????????dto.setNonce_str(String.valueOf(System.nanoTime()));??????????LoginInfo?loginInfo?=?LoginInfoUtil.getLoginInfo();??????????????????dto.setOut_trade_no("你的訂單號+關(guān)鍵信息,微信回調(diào)之后傳回,你可以驗證");??????????dto.setTotal_fee(totalfee);??????????dto.setSpbill_create_ip("127.0.0.1");??????????????????dto.setSign(dto.makeSign());??????????logger.info("sign?:?"?+?dto.makeSign());??????????logger.info("xml?content?:?"?+?dto.generateXMLContent());??????????try?{??????????????HttpClient?httpClient?=?HttpClientBuilder.create().build();???????????????HttpPost?post?=?new?HttpPost(WeiXinConstants.UNIFIEDORDER_URL);??????????????post.addHeader("Content-Type",?"text/xml;?charset=UTF-8");??????????????StringEntity?xmlEntity?=?new?StringEntity(dto.generateXMLContent(),?ContentType.TEXT_XML);??????????????post.setEntity(xmlEntity);??????????????HttpResponse?httpResponse?=?httpClient.execute(post);??????????????String?responseXML?=?EntityUtils.toString(httpResponse.getEntity(),?"UTF-8");??????????????logger.info("response?xml?content?:?"?+?responseXML);??????????????????????????Map<String,?String>?resultMap?=?(Map<String,?String>)XMLUtil.doXMLParse(responseXML);??????????????logger.info("response?code_url?:?"?+?resultMap.get("code_url"));??????????????String?codeurl?=?resultMap.get("code_url");??????????????if(codeurl?!=?null?&&?!"".equals(codeurl))?{??????????????????String?imageurl?=?generateQrcode(codeurl);??????????????????result.put("QRIMAGE",?imageurl);??????????????}??????????????post.releaseConnection();??????????}?catch(Exception?e)?{??????????????e.printStackTrace();??????????}??????????result.put("success",?"1");??????????return?result;?? ?
生成二維碼的代碼如下:
[java]?view plaincopy
private?String?generateQrcode(String?codeurl)?{??????File?foldler?=?new?File(basePath?+?"qrcode");??????if(!foldler.exists())?{??????????foldler.mkdirs();??????}????????????String?f_name?=?UUIDUtil.uuid()?+?".png";?????????try?{??????????File?f?=?new?File(basePath?+?"qrcode",?f_name);??????????FileOutputStream?fio?=?new?FileOutputStream(f);??????????MultiFormatWriter?multiFormatWriter?=?new?MultiFormatWriter();??????????Map?hints?=?new?HashMap();??????????hints.put(EncodeHintType.CHARACTER_SET,?"UTF-8");?????????BitMatrix?bitMatrix?=?null;?????????????bitMatrix?=?multiFormatWriter.encode(codeurl,?BarcodeFormat.QR_CODE,?300,?300,hints);?????????????BufferedImage?image?=?toBufferedImage(bitMatrix);????????????????????????ImageIO.write(image,?"png",?fio);?????????????return?("qrcode/"?+?f_name);?????????}?catch?(Exception?e1)?{?????????????e1.printStackTrace();?????????????return?null;?????????}???????}?? 此時如何客戶端微信掃碼之后,微信就會通過回調(diào)我們制定URL返回數(shù)據(jù)給我們。在回調(diào)方法中完成我們自己的處理,這里要特別注意的是你的回調(diào)接口必須通過HTTP POST方法實現(xiàn),否則無法接受到XML數(shù)據(jù)。回調(diào)處理的代碼如下:
?
[java]?view plaincopy
@RequestMapping(value?=?"/your_callback_url",?method?=?RequestMethod.POST)??@ResponseBody??public?void?finishPayment(HttpServletRequest?request,?HttpServletResponse?response)?{??????try?{??????????logger.info("start?to?callback?from?weixin?server:?"?+?request.getRemoteHost());??????????Map<String,?String>?resultMap?=?new?HashMap<String,?String>();??????????InputStream?inputStream?=?request.getInputStream();??????????????????SAXBuilder?saxBuilder=?new?SAXBuilder();??????????Document?document?=?saxBuilder.build(inputStream);??????????????????Element?root?=?document.getRootElement();??????????????????List?list?=?root.getChildren();??????????Iterator?it?=?list.iterator();??????????while(it.hasNext())?{??????????????Element?e?=?(Element)?it.next();??????????????String?k?=?e.getName();??????????????String?v?=?"";??????????????List?children?=?e.getChildren();??????????????if(children.isEmpty())?{??????????????????v?=?e.getTextNormalize();??????????????}?else?{??????????????????v?=?XMLUtil.getChildrenText(children);??????????????}??????????????resultMap.put(k,?v);??????????}??????????????????????????????????????????????????????????????inputStream.close();??????????inputStream?=?null;??????????String?returnCode?=?resultMap.get("return_code");??????????String?outtradeno?=?resultMap.get("out_trade_no");??????????????????int?nfee?=?Integer.parseInt(resultMap.get("total_fee"));??????????logger.info("out?trade?no?:?"?+?outtradeno);??????????logger.info("total_fee?:?"?+?nfee);??????????????????if("SUCCESS".equals(returnCode))?{????????????????????????????????????????response.getWriter().print(XMLUtil.getRetResultXML(resultMap.get("return_code"),?resultMap.get("return_code")));??????????}?else?{??????????????response.getWriter().print(XMLUtil.getRetResultXML(resultMap.get("return_code"),?resultMap.get("return_msg")));??????????}??????}??????catch(IOException?ioe)?{??????????ioe.printStackTrace();??????}?catch?(JDOMException?e1)?{??????????e1.printStackTrace();??????}??}?? ?
微信官方Java版Demo用到的XMLUtil和MD5Util的兩個類記得拿過來改一下,演示代碼可以在它的官方演示頁面找到,相關(guān)maven依賴如下:
?
[html]?view plaincopy
<dependency>??????<groupId>jdom</groupId>??????<artifactId>jdom</artifactId>??????<version>1.1</version>??</dependency>??<dependency>??????<groupId>com.google.zxing</groupId>??????<artifactId>core</artifactId>??????<version>3.3.0</version>??</dependency>?? ?
最后要特別注意的是關(guān)于簽名,簽名生成MD5的類我是從微信官網(wǎng)直接下載Java版Demo程序獲取的,建議你也是,因為這個是確保MD5簽名是一致的最佳選擇。具體的生成簽名的算法可以查看微信官方文檔,這里也強烈建議大家一定要官方API說明,你開發(fā)中所遇到各種問題90%都是因為不看官方文檔,而是輕信某人博客!這個才是我寫這篇文章的真正目的和用意,根據(jù)官方文檔,用我的Java代碼實現(xiàn),微信PC端網(wǎng)頁掃碼支付必定在你的WEB應(yīng)用中飛起來。
轉(zhuǎn)載于:https://www.cnblogs.com/wanghuaijun/p/6249185.html
總結(jié)
以上是生活随笔為你收集整理的Java SpringMVC实现PC端网页微信扫码支付完整版的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。