一文快速實(shí)現(xiàn)微信公眾號(hào)支付功能(詳細(xì)版函荣,建議收藏備用)

進(jìn)階架構(gòu)精品專題

Mysql優(yōu)化專題(★★★★)

網(wǎng)絡(luò)協(xié)議專題(★★★★)

其余18大專題显押,請(qǐng)?jiān)谥黜?yè)菜單欄查看

后臺(tái)回復(fù)【加群】,獲取BAT真實(shí)面經(jīng)

微信支付類型

微信支付實(shí)際上有很多種不同的類型傻挂,具體要使用哪一種就需要根據(jù)不同的應(yīng)用場(chǎng)景來(lái)選擇乘碑,官方給出的參考例子:

刷卡支付:用戶打開(kāi)微信錢包的刷卡的界面,商戶掃碼后提交完成支付金拒。

公眾號(hào)支付:用戶在微信內(nèi)進(jìn)入商家H5頁(yè)面兽肤,頁(yè)面內(nèi)調(diào)用JSSDK完成支付

掃碼支付:用戶打開(kāi)"微信掃一掃“,掃描商戶的二維碼后完成支付

APP支付:商戶APP中集成微信SDK殖蚕,用戶點(diǎn)擊后跳轉(zhuǎn)到微信內(nèi)完成支付

H5支付:用戶在微信以外的手機(jī)瀏覽器請(qǐng)求微信支付的場(chǎng)景喚起微信支付

小程序支付:用戶在微信小程序中使用微信支付的場(chǎng)景

本篇文章實(shí)現(xiàn)的是公眾號(hào)支付,會(huì)使用到網(wǎng)頁(yè)授權(quán)及微信JS-SDK相關(guān)知識(shí)沉迹,但不再詳細(xì)介紹

建議大家先閱讀以下文章了解相關(guān)內(nèi)容:

網(wǎng)頁(yè)授權(quán):http://www.reibang.com/p/94b0e53cccc3

微信JS-SDK:http://www.reibang.com/p/b3c4450f845e

實(shí)現(xiàn)效果如下動(dòng)圖:

公眾號(hào)支付相關(guān)配置

本篇文章中實(shí)現(xiàn)的是公眾號(hào)支付睦疫,實(shí)現(xiàn)條件如下:

1.需要一個(gè)已經(jīng)進(jìn)行微信認(rèn)證的公眾號(hào)

2.該公眾號(hào)需要開(kāi)通微信支付功能

3.到微信商戶平臺(tái)https://pay.weixin.qq.com 注冊(cè)一個(gè)商戶賬號(hào),并關(guān)聯(lián)你的公眾號(hào)鞭呕,如果需要實(shí)現(xiàn)小程序支付的蛤育,需要關(guān)聯(lián)小程序。

4.擁有一個(gè)正式的應(yīng)用服務(wù)器葫松,并且注冊(cè)域名

微信支付涉及的私密數(shù)據(jù)比較多瓦糕,不允許使用natapp纷责,花生殼之類的內(nèi)網(wǎng)穿透工具實(shí)現(xiàn)鉴腻,需要有正式的服務(wù)器環(huán)境绒净,并且要注冊(cè)域名兄墅,不能使用IP蚊丐。

比如:http://www.baidu.com

5.相關(guān)配置

5.1 配置支付授權(quán)目錄诈泼,登錄商戶平臺(tái)——>產(chǎn)品中心——>開(kāi)發(fā)配置

圖中配置的例子膝迎,代表在項(xiàng)目根路徑下攘须,以及web目錄下的頁(yè)面都有支付權(quán)限,如果不在該路徑的頁(yè)面圣贸,則無(wú)法調(diào)用支付功能挚歧。

若頁(yè)面地址為:http://mywexx.xxxx.com/web/pay.html

則需要配置為:http://mywexx.xxxx.com/web/

5.2 ?設(shè)置API密鑰,登錄商戶平臺(tái)——>賬戶中心——>API安全——>API密鑰

該密鑰在后面的代碼中計(jì)算支付簽名的時(shí)候需要使用到吁峻。

5.3 配置JS接口安全域名與網(wǎng)頁(yè)授權(quán)域名滑负,登錄公眾平臺(tái)——>公眾號(hào)設(shè)置——>功能設(shè)置

配置網(wǎng)頁(yè)授權(quán)域名:主要用于獲取用戶的openId,需要識(shí)別這是哪個(gè)人用含。

若對(duì)openID不了解的同學(xué)可先參考微信公眾號(hào)開(kāi)發(fā)文檔:https://mp.weixin.qq.com/wiki

配置JS接口安全域名:要讓我們的頁(yè)面中彈出輸入密碼的窗口矮慕,需要使用微信提供的JS-SDK工具,如果不配置JS接口安全域名耕餐,你的頁(yè)面無(wú)法使用JS-SDK凡傅。

公眾號(hào)支付實(shí)現(xiàn)流程

大致流程參考官方提供的時(shí)序圖:

流程有很多,不一一演示肠缔,我們選取核心部分來(lái)實(shí)現(xiàn)夏跷。

1.提供商城主頁(yè),用戶進(jìn)入后通過(guò)網(wǎng)頁(yè)授權(quán)獲取openid

如果對(duì)網(wǎng)頁(yè)授權(quán)不熟悉的同學(xué)先參考這篇文章:

http://www.reibang.com/p/94b0e53cccc3

訪問(wèn)主頁(yè)的地址:http://www.wolfcode.cn/index.do

當(dāng)用戶第一次打開(kāi)主頁(yè)明未,默認(rèn)沒(méi)有code參數(shù)槽华,此時(shí)會(huì)先重定向到獲取授權(quán)的地址

(如果只需要獲取openid,可以使用scope為snsapi_base靜默授權(quán)的方式)

經(jīng)過(guò)授權(quán)地址再重定向到我們的index.do時(shí)趟妥,會(huì)帶上code參數(shù)猫态,此時(shí)即可通過(guò)接口獲取用戶的openid

@Controllerpublic?class?IndexController {@RequestMapping("index")public?void?index(String?code, Model model, HttpServletResponse response,HttpServletRequest request) ?{//如果有code就可以去獲取用戶的openid

if(code!=null) {//通過(guò)code來(lái)?yè)Q取access_token

JSONObject json = WeChatUtil.getWebAccessToken(code);//獲取用戶openid

String?openid = json.getString("openid");//設(shè)置到會(huì)話中

request.getSession().setAttribute("openid",openid);//重定向到主頁(yè)

response.sendRedirect("/index.html");

}else{//重定向到授權(quán)頁(yè)面

response.sendRedirect(WeChatUtil.WEB_REDIRECT_URL.replace("APPID",WeChatUtil.APPID)

.replace("REDIRECT_URI", RequestUtil.getUrl(request)));

}

}

}

注意:?1.?WeChatUtil.getWebAccessToken 方法在網(wǎng)頁(yè)授權(quán)的文章中有介紹。?

2.?WEB_REDIRECT_URL 是網(wǎng)頁(yè)授權(quán)的地址常量:

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.點(diǎn)擊商品后跳轉(zhuǎn)到商品詳細(xì)頁(yè)面

具體頁(yè)面根據(jù)自己的項(xiàng)目添加披摄,主要是頁(yè)面需要提供一個(gè)可以馬上下訂單的按鈕即可亲雪。(這里不演示加入購(gòu)物車功能)

點(diǎn)擊立即購(gòu)買按鈕跳轉(zhuǎn)到后臺(tái)下單地址,并帶上當(dāng)前商品的id疚膊。

$(function?()?{//立即購(gòu)買按鈕

$("#orderBtn").click(function(){//獲取商品id

var?id = $("#productId").val();//提交到下單

window.location.href =?"/order.do?productId="+id;

})

})

3.接收商品參數(shù)并調(diào)用微信支付統(tǒng)一下單接口

正常的業(yè)務(wù)流程是在該方法中义辕,獲取商品id,再通過(guò)id去查詢數(shù)據(jù)庫(kù)該商品的相關(guān)屬性寓盗,比如名稱灌砖,價(jià)格等等,然后再創(chuàng)建業(yè)務(wù)訂單傀蚌,再去調(diào)用微信支付的統(tǒng)一下單接口(讓微信生成預(yù)支付單基显,后續(xù)才可以進(jìn)行支付)。

但此處重點(diǎn)在支付流程善炫,商品的屬性值和訂單相關(guān)值撩幽,暫且先使用假數(shù)據(jù)。

接口以及參數(shù)可參考微信官方提供的統(tǒng)一下單文檔:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1

根據(jù)文檔介紹箩艺,我們調(diào)用統(tǒng)一下單接口時(shí)需要帶上相關(guān)必填的參數(shù)如下:

1.PNG

把必填的參數(shù)封裝成對(duì)應(yīng)的實(shí)體類:

調(diào)用接口成功后返回的結(jié)果也封裝成實(shí)體類:

該結(jié)果中最重要的是prepay_id參數(shù)摸航,在頁(yè)面中彈出支付窗口時(shí)需要用到制跟。

注意:下單的業(yè)務(wù)邏輯,正常是需要抽取到業(yè)務(wù)層的酱虎,但是此處為了方便閱讀代碼雨膨,直接寫到了控制器上。

@Controllerpublic?class?OrderController {@RequestMapping("order")public?String?save(Long productId,Model model,HttpServletRequest request) throws Exception {//根據(jù)商品id查詢商品詳細(xì)信息(假數(shù)據(jù))

//productService.getProductById(productId)

double price =?0.01;//(0.01元)

String?productName =?"SweetCity";//生成訂單編號(hào)

int?number?= (int)((Math.random()*9)*1000);//隨機(jī)數(shù)

DateFormat dateFormat =?new?SimpleDateFormat("yyyyMMddHHmmss");//時(shí)間

String?orderNumber = dateFormat.format(new?Date()) +?number;//獲取openId

String?openId = (String) request.getSession().getAttribute("openid");//準(zhǔn)備調(diào)用接口需要的參數(shù)

WxOrderEntity order =?new?WxOrderEntity();//公眾號(hào)appid

order.setAppid(WeChatUtil.APPID);//商戶號(hào)

order.setMch_id(WeChatUtil.MCH_ID);//商品描述

order.setBody(productName);//設(shè)備號(hào)读串,公眾號(hào)支付直接填WEB

order.setDevice_info("WEB");//交易類型

order.setTrade_type("JSAPI");//商戶訂單號(hào)

order.setOut_trade_no(orderNumber);//支付金額(單位:分)

order.setTotal_fee((int)(price*100));//用戶ip地址

order.setSpbill_create_ip(RequestUtil.getIPAddress(request));//用戶openid

order.setOpenid(openId);//接收支付結(jié)果的地址

order.setNotify_url("http://www.wolfcode.com/receive.do");//32位隨機(jī)數(shù)(UUID去掉-就是32位的)

String?uuid = UUID.randomUUID().toString().replace("-",?"");

order.setNonce_str(uuid);//生成簽名

String?sign = WeChatUtil.getPaySign(order);

order.setSign(sign);//調(diào)用微信支付統(tǒng)一下單接口,讓微信也生成一個(gè)預(yù)支付訂單

String?xmlResult = HttpUtil.post(GET_PAY_URL,XMLUtil.toXmlString(order));//把返回的xml字符串轉(zhuǎn)成對(duì)象

WxOrderResultEntity entity = XMLUtil.toObject(xmlResult,WxOrderResultEntity.class);//如果微信預(yù)支付單成功創(chuàng)建聊记,就跳轉(zhuǎn)到支付訂單頁(yè)進(jìn)行支付

if(entity.getReturn_code().equals("SUCCESS")&&entity.getResult_code().equals("SUCCESS")){//jssdk權(quán)限驗(yàn)證參數(shù)

TreeMap 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);//微信支付權(quán)限驗(yàn)證參數(shù)

String?prepayId = entity.getPrepay_id();

TreeMap 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);

}//跳轉(zhuǎn)到查看訂單頁(yè)面

return?"order";

}

}

下面是jssdk中config權(quán)限使用到的的簽名,以及微信支付使用的簽名的算法代碼恢暖。

官方文檔參考:

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

/**

* 計(jì)算jssdk-config的簽名

* @param timestamp

* @param noncestr

* @param url

* @return

*/

public?static?String?getSignature(Long timestamp,String?noncestr,String?url ){//對(duì)所有待簽名參數(shù)按照字段名的ASCII 碼從小到大排序(字典序)

Map map =?new?TreeMap<>();

map.put("jsapi_ticket",getTicket());

map.put("timestamp",timestamp);

map.put("noncestr",noncestr);

map.put("url",url);//使用URL鍵值對(duì)的格式(即key1=value1&key2=value2…)拼接成字符串string1

StringBuilder sb =?new?StringBuilder();

Set set = map.keySet();for?(String?key : set) {

sb.append(key+"="+map.get(key)).append("&");

}//去掉最后一個(gè)&符號(hào)

String?temp = sb.substring(0,sb.length()-1);//使用sha1加密

return?SecurityUtil.SHA1(temp);

}/**

* 計(jì)算微信支付的簽名

* @param obj

* @return

* @throws IllegalAccessException

*/

public?static?String?getPaySign(Object?obj) throws IllegalAccessException, IOException {

StringBuilder sb =?new?StringBuilder();//把對(duì)象轉(zhuǎn)為TreeMap集合(按照key的ASCII 碼從小到大排序)

TreeMap map;if(!(obj?instanceof?Map)) {

map = ObjectUtils.objectToMap(obj);

}else{

map = (TreeMap)obj;

}

Set> entrySet = map.entrySet();//遍歷鍵值對(duì)

for?(Map.Entry entry : entrySet) {//如果值為空排监,不參與簽名

if(entry.getValue()!=null) {//格式key1=value1&key2=value2…

sb.append(entry.getKey() +?"="?+ entry.getValue() +?"&");

}

}//最后拼接商戶的API密鑰

String?stringSignTemp = sb.toString()+"key="+WeChatUtil.KEY;//進(jìn)行md5加密并轉(zhuǎn)為大寫

return?SecurityUtil.MD5(stringSignTemp).toUpperCase();

}

4.提供訂單展示頁(yè)面

若對(duì)微信JS-SDK不了解的同學(xué)可先參考該文章:

http://www.reibang.com/p/b3c4450f845e

在頁(yè)面中調(diào)用微信JS-SDK,通過(guò)config接口注入權(quán)限驗(yàn)證配置杰捂,并且添加支付功能舆床。

<!--jquery-->

<!--微信的JSSDK-->

$(function()?{


wx.config({debug:?true,?// 開(kāi)啟調(diào)試模式

appId:?'${configMap.appId}',?// 公眾號(hào)的唯一標(biāo)識(shí)

timestamp:?'${configMap.timestamp}',?// 生成簽名的時(shí)間戳

nonceStr:?'${configMap.nonceStr}',?// 生成簽名的隨機(jī)串

signature:?'${configMap.signature}',// 簽名

jsApiList: ['chooseWXPay']?// 填入需要使用的JS接口列表,這里是先聲明我們要用到支付的JS接口

});<!-- config驗(yàn)證成功后會(huì)調(diào)用ready中的代碼 -->

wx.ready(function(){

//點(diǎn)擊馬上付款按鈕

$("#payBtn").click(function(){

//彈出支付窗口

wx.chooseWXPay({

timestamp: '${payMap.timeStamp}', // 支付簽名時(shí)間戳嫁佳,

nonceStr: '${payMap.nonceStr}', // 支付簽名隨機(jī)串挨队,不長(zhǎng)于 32 位

package: '${payMap.packageStr}', // 統(tǒng)一支付接口返回的prepay_id參數(shù)值,提交格式如:prepay_id=xxxx)

signType: '${payMap.signType}', // 簽名方式蒿往,默認(rèn)為'SHA1'盛垦,使用新版支付需傳入'MD5'

paySign: '${payMap.paySign}', // 支付簽名

success: function (res) {

// 支付成功后的回調(diào)函數(shù)

alert("支付成功!");

}

});

})

});

});

點(diǎn)擊馬上付款后可彈出支付窗口:

支付完成:

5.支付結(jié)果的處理

當(dāng)用戶支付后瓤漏,微信會(huì)把支付結(jié)果發(fā)送到我們前面指定的notify_url地址腾夯,我們可以根據(jù)支付結(jié)果的參數(shù)來(lái)做相關(guān)的業(yè)務(wù)邏輯,但這里暫不實(shí)現(xiàn)蔬充,具體支付通知結(jié)果的參數(shù)可參考官方文章:

https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7&index=8

作者:陳惠

鏈接:http://www.reibang.com/p/9c322b1a5274

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蝶俱,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子饥漫,更是在濱河造成了極大的恐慌榨呆,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,865評(píng)論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件趾浅,死亡現(xiàn)場(chǎng)離奇詭異愕提,居然都是意外死亡馒稍,警方通過(guò)查閱死者的電腦和手機(jī)皿哨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,296評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)纽谒,“玉大人证膨,你說(shuō)我怎么就攤上這事」那” “怎么了央勒?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,631評(píng)論 0 364
  • 文/不壞的土叔 我叫張陵不见,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我崔步,道長(zhǎng)稳吮,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,199評(píng)論 1 300
  • 正文 為了忘掉前任井濒,我火速辦了婚禮灶似,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘瑞你。我一直安慰自己酪惭,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,196評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布者甲。 她就那樣靜靜地躺著春感,像睡著了一般。 火紅的嫁衣襯著肌膚如雪虏缸。 梳的紋絲不亂的頭發(fā)上鲫懒,一...
    開(kāi)封第一講書(shū)人閱讀 52,793評(píng)論 1 314
  • 那天,我揣著相機(jī)與錄音寇钉,去河邊找鬼刀疙。 笑死,一個(gè)胖子當(dāng)著我的面吹牛扫倡,可吹牛的內(nèi)容都是我干的谦秧。 我是一名探鬼主播,決...
    沈念sama閱讀 41,221評(píng)論 3 423
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼撵溃,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼疚鲤!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起缘挑,我...
    開(kāi)封第一講書(shū)人閱讀 40,174評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤集歇,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后语淘,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體诲宇,經(jīng)...
    沈念sama閱讀 46,699評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,770評(píng)論 3 343
  • 正文 我和宋清朗相戀三年惶翻,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了姑蓝。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,918評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡吕粗,死狀恐怖纺荧,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤宙暇,帶...
    沈念sama閱讀 36,573評(píng)論 5 351
  • 正文 年R本政府宣布输枯,位于F島的核電站,受9級(jí)特大地震影響占贫,放射性物質(zhì)發(fā)生泄漏桃熄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,255評(píng)論 3 336
  • 文/蒙蒙 一型奥、第九天 我趴在偏房一處隱蔽的房頂上張望蜻拨。 院中可真熱鬧,春花似錦桩引、人聲如沸缎讼。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,749評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)血崭。三九已至,卻和暖如春厘灼,著一層夾襖步出監(jiān)牢的瞬間夹纫,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,862評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工设凹, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留舰讹,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,364評(píng)論 3 379
  • 正文 我出身青樓闪朱,卻偏偏與公主長(zhǎng)得像月匣,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子奋姿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,926評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容