微信支付,你這磨人的小妖精

凡是和錢(qián)打交道的事听系,沒(méi)有一樣是容易的猫胁。這是我第一次接觸微信支付,發(fā)現(xiàn)網(wǎng)上還是有很多同學(xué)在求助跛锌,XXX了怎么辦?XXX是什么情況届惋?為了幫助更多的小伙伴脫離“苦核杳保”,我決定寫(xiě)下這次的踩坑之旅脑豹,給更多的人幫助郑藏。

介紹

微信支付方式分為刷卡支付、公眾號(hào)支付瘩欺、掃碼支付必盖、APP支付、H5支付俱饿、小程序支付歌粥。

先從應(yīng)用場(chǎng)景來(lái)各自說(shuō)一說(shuō),這樣拍埠,能夠最快的判斷出應(yīng)該選擇哪一種支付失驶。

  • 刷卡支付:使用掃描設(shè)備(掃描槍?zhuān)┒嘁?jiàn)于超市、便利店使用
  • 公眾號(hào)支付:嵌入公眾號(hào)的H5頁(yè)面
  • 掃碼支付:用戶(hù)打開(kāi)“微信掃一掃”枣购,掃描商戶(hù)的二維碼并支付
  • APP支付:外部APP應(yīng)用嬉探,用戶(hù)觸發(fā)支付時(shí)擦耀,轉(zhuǎn)到微信內(nèi)完成支付
  • H5支付:非微信內(nèi)置瀏覽器請(qǐng)求微信支付
  • 小程序支付:用戶(hù)在微信小程序中使用微信支付

背景

我們公司申請(qǐng)的是微信服務(wù)號(hào),需要微信支付的是嵌入服務(wù)號(hào)內(nèi)部的網(wǎng)頁(yè)涩堤,所以根據(jù)介紹眷蜓,應(yīng)該選擇“公眾號(hào)支付”。

開(kāi)發(fā)步驟

首先不要被微信支付的開(kāi)發(fā)嚇著胎围,其實(shí)它很簡(jiǎn)單吁系。先仔細(xì)看公眾號(hào)支付的文檔,看不懂的多看幾遍痊远,還看不懂的垮抗,動(dòng)手操作一下,試一試碧聪。

文檔在此:
pay.weixin.qq.com/wiki/doc/ap…

  • 步驟一:統(tǒng)一下單

跟著文檔冒版,咱一點(diǎn)點(diǎn)來(lái),搞明白每一步是為什么逞姿,就不會(huì)迷迷糊糊搞不清楚了辞嗡。

首先說(shuō)一下,這個(gè)接口是后臺(tái)需要完成的滞造,這個(gè)接口的目的就是獲取prepay_id,它是預(yù)支付交易回話(huà)標(biāo)識(shí)续室。將prepay_id傳給前臺(tái),前臺(tái)調(diào)用js-sdk谒养,這屬于步驟二的范圍了挺狰,一會(huì)講。

接口鏈接
URL地址:api.mch.weixin.qq.com/pay/unified…

在文檔中說(shuō)明了买窟,必須使用post 方法請(qǐng)求微信給的接口鏈接丰泊,傳入的數(shù)據(jù)也必須是xml格式,返回的也是xml的始绍。醉了瞳购?不要醉,微信是這樣的亏推,支付寶也是這樣的学赛。手動(dòng)微笑,接受吧吞杭。

接著來(lái)盏浇。

簡(jiǎn)單粗暴貼代碼:

// '/addOrder'是留給前臺(tái)的調(diào)用接口
router.post('/addOrder',(req,res)=>{    
const addOrderUrl = 'https://api.mch.weixin.qq.com/pay/unifiedorder';    
var client_ip = "";    
client_ip = req.body.ipaddr;    
var appid = "1234567890"; // 服務(wù)號(hào)|公眾號(hào)的appid    
var body = "商品簡(jiǎn)單描述-測(cè)試"; // 商品簡(jiǎn)單描述    
var mch_id= "1234567890"; // 商戶(hù)號(hào),申請(qǐng)微信支付芽狗,騰訊給的商戶(hù)號(hào)    
// var device_info = "WEB";  
var nonce_str = getRanId(32); // 隨機(jī)字符串 
var out_trade_no = "" +new Date().getTime() + Math.floor( Math.random() * 10 ); //商戶(hù)訂單號(hào)
var total_fee = req.body.total_fee; //支付金額缠捌,單位:分    
var sign = "";    
var notify_url = "http://123.456.789"; //異步接收微信支付結(jié)果通知的回調(diào)地址    
var trade_type = "JSAPI"; // 交易類(lèi)型    
var openid = req.session.openId;    
console.log(openid);    
var stringA = `appid=${appid}&body=${body}&mch_id=${mch_id}&nonce_str=${nonce_str}&notify_url=${notify_url}&openid=${openid}&out_trade_no=${out_trade_no}&spbill_create_ip=${client_ip}&total_fee=${total_fee}&trade_type=${trade_type}`;    
var stringSighTemp = stringA+"&key=****#####jiaoyuguihuayuan----***"; //32位的商戶(hù)key,自定義的,這里為了隱私,我用的特殊符號(hào)給你們展示   
sign = md5(stringSighTemp).toUpperCase();    
var xml = `<xml>      
<appid>${appid}</appid>      
<body>${body}</body>      
<mch_id>${mch_id}</mch_id>      
<nonce_str>${nonce_str}</nonce_str>      
<notify_url>${notify_url}</notify_url>     
<openid>${openid}</openid>     
<out_trade_no>${out_trade_no}</out_trade_no>    
<spbill_create_ip>${client_ip}</spbill_create_ip>     
<total_fee>${total_fee}</total_fee>  
<trade_type>${trade_type}</trade_type>    
<sign>${sign}</sign>    
</xml>`;
var Res = res;axios({  
    method: 'post',   
    url: addOrderUrl,   
    data: xml,   
    responseType: 'text/xml',   
    headers: {     
       'Content-Type': 'text/xml' 
    }
 }).then( res=>{   
    console.log(res)  
    Res.send(res.data)
 }).catch( err=>{   
    console.log( err)})
})

說(shuō)明

client_ip 參數(shù) 是客戶(hù)端的ip地址曼月,本來(lái)我是在后臺(tái)獲取客戶(hù)端ip地址的谊却,因?yàn)槲覀兪褂昧薾ginx代理,req.ip 返回的都是 ::ffff:127.0.0.1 這是IPV6格式的字符串哑芹。網(wǎng)上有一個(gè)答案對(duì)此做出了解釋?zhuān)?stackoverflow.com/questions/2…

在這里炎辨,我用的一個(gè)網(wǎng)上的腳本在前臺(tái)獲取的, http://pv.sohu.com/cityjson?ie=utf-8
使用方法: window.ipaddr = returnCitySN[‘cip’];
其他的參數(shù)聪姿,都是參考微信支付的要求去寫(xiě)的碴萧。

出現(xiàn)的錯(cuò)誤

XML格式錯(cuò)誤

xml格式錯(cuò)誤.png

而查看文檔,原因是這樣的
xml錯(cuò)誤情況.png

我:#&(%#@+%)末购,也不給個(gè)詳細(xì)點(diǎn)的說(shuō)明…

這種錯(cuò)誤需要“頓悟”破喻,我突然發(fā)現(xiàn)了我的錯(cuò)誤。是我理解錯(cuò)了盟榴!我給body標(biāo)簽加了一個(gè) <![CDATA[]]> 導(dǎo)致我的xml格式錯(cuò)誤曹质,其實(shí)是有detail字段才需要添加 <[CDATA[]]>, 其他的不需要擎场。

我:咳咳羽德,低級(jí)錯(cuò)誤。注意看文檔迅办,按照要求來(lái)宅静,既不多添什么,也不要少什么站欺。

我把<[CDATA[]]>去掉之后姨夹,發(fā)現(xiàn)果真是這個(gè)原因,不再出現(xiàn)XML格式錯(cuò)誤了矾策,然而匀伏,還是高興的太早,因?yàn)樗鼒?bào)了簽名錯(cuò)誤蝴韭。呵呵呵~

簽名錯(cuò)誤

簽名錯(cuò)誤.png

文檔中說(shuō)的簽名計(jì)算很?chē)?yán)格:
特別注意以下重要規(guī)則:

第一步,設(shè)所有發(fā)送或者接收到的數(shù)據(jù)為集合M熙侍,將集合M內(nèi)非空參數(shù)值的參數(shù)按照參數(shù)名ASCII碼從小到大排序(字典序)榄鉴,使用URL鍵值對(duì)的格式(即key1=value1&key2=value2…)拼接成字符串stringA。

◆ 參數(shù)名ASCII碼從小到大排序(字典序)蛉抓;
◆ 如果參數(shù)的值為空不參與簽名庆尘;
◆ 參數(shù)名區(qū)分大小寫(xiě); (公眾號(hào)支付全是小寫(xiě))
◆ 驗(yàn)證調(diào)用返回或微信主動(dòng)通知簽名時(shí)巷送,傳送的sign參數(shù)不參與簽名驶忌,將生成的簽名與該sign值作校驗(yàn)。
◆ 微信接口可能增加字段,驗(yàn)證簽名時(shí)必須支持增加的擴(kuò)展字段

第二步付魔,在stringA最后拼接上key得到stringSignTemp字符串聊品,并對(duì)stringSignTemp進(jìn)行MD5運(yùn)算,再將得到的字符串所有字符轉(zhuǎn)換為大寫(xiě)几苍,得到sign值signValue翻屈。

◆ key設(shè)置路徑:微信商戶(hù)平臺(tái)(pay.weixin.qq.com)–>賬戶(hù)設(shè)置–>API安全–>密鑰設(shè)置

這里我查閱了一些資料,看到過(guò)有這樣幾種錯(cuò)誤情況:
key 看錯(cuò)了妻坝,這里應(yīng)該寫(xiě)商戶(hù)的key,而這個(gè)key 是用戶(hù)手動(dòng)設(shè)置的伸眶,長(zhǎng)32位。注意:自己保存一份刽宪,因?yàn)樵O(shè)置好了之后是沒(méi)法打開(kāi)查看的厘贼。

設(shè)置api密鑰.png

還有一種錯(cuò)誤,我覺(jué)得很離譜啊圣拄,body里面不能有中文嘴秸,然而,我把body中的文字改為英文售担,發(fā)現(xiàn)并不能改變現(xiàn)狀赁遗,其實(shí)用中文是可以的。

總之族铆,找到的這些錯(cuò)誤岩四,通通對(duì)我的情況沒(méi)用!

然后這又需要“頓悟”哥攘,原來(lái)stringA字符串我用了換行符把很長(zhǎng)的字符隔開(kāi)剖煌,這導(dǎo)致?lián)Q行符被轉(zhuǎn)換為Ascall碼中的 \n 寫(xiě)進(jìn)了簽名里面,所以逝淹,sign錯(cuò)誤耕姊,所以要么,把換行符通通去掉栅葡,要么用“”連接茉兰,舍棄。我把換行都去掉之后欣簇,就沒(méi)有簽名錯(cuò)誤了规脸。

當(dāng)當(dāng)當(dāng)當(dāng) ~


終于完成了第一步,后臺(tái)成功的返回了我們需要的prepay_id

這里為了安全熊咽,對(duì)于返回sign莫鸭,和發(fā)送的sign進(jìn)行對(duì)比,完全相等之后横殴,才能把結(jié)果返回給前臺(tái)被因。

  • 步驟二:調(diào)用微信js-sdk接口

微信支付
發(fā)起一個(gè)微信支付請(qǐng)求
前臺(tái)收到的是xml數(shù)據(jù),要先解析一下,得到prepay_id
然后調(diào)用微信支付js-sdk梨与,為了大家少走一些彎路堕花,我先來(lái)正確的寫(xiě)法,關(guān)鍵步驟如下:

var {prepay_id,appid} = getInfo(res.data); //從后臺(tái)數(shù)據(jù)中獲取appid 和 prepay_id
nonceStr = getRanId(32);
timeStamp = new Date().getTime();
var stringA = "appId="+appid+"&nonceStr="+nonceStr+"&package=prepay_id="+prepay_id+"&signType=MD5&timeStamp="+timeStamp;
var stringSignTemp = stringA+"&key=****#####jiaoyuguihuayuan----***";
paySign = md5(stringSignTemp).toUpperCase();
window.wx.chooseWXPay({    
timestamp: timeStamp, // 支付簽名時(shí)間戳蛋欣,注意微信jssdk中的所有使用timestamp字段均為小寫(xiě)航徙。但最新版的支付后臺(tái)生成簽名使用的timeStamp字段名需大寫(xiě)其中的S字符    
nonceStr: nonceStr, // 支付簽名隨機(jī)串,不長(zhǎng)于 32 位    
package: "prepay_id=" + prepay_id, // 統(tǒng)一支付接口返回的prepay_id參數(shù)值陷虎,提交格式如:prepay_id=\*\*\*)    
signType: 'MD5', // 簽名方式到踏,默認(rèn)為'SHA1',使用新版支付需傳入'MD5'    
paySign: paySign, // 支付簽名 
success: function (res) {        
// 支付成功后的回調(diào)函數(shù)        
    console.log(res)   
 },   
fail: function(err){     
    console.log(err)  
}});

備注:prepay_id 通過(guò)微信支付統(tǒng)一下單接口拿到尚猿,paySign 采用統(tǒng)一的微信支付 Sign 簽名生成方法窝稿,注意這里 appId 也要參與簽名,appId 與 config 中傳入的
appId 一致凿掂,即最后參與簽名的參數(shù)有appId, timeStamp, nonceStr,
package, signType伴榔。

注意,我要講個(gè)坑點(diǎn)~

調(diào)用js-sdk時(shí)庄萎,簽名中的字段都是小駝峰的寫(xiě)法踪少,timeStamp是這樣寫(xiě)的,但是wx.config中糠涛,timestamp 是全小寫(xiě)的援奢,所以,親們忍捡,千萬(wàn)不要搞錯(cuò)了集漾,我在這里就被坑了好一會(huì)呢。

寫(xiě)完簽名之后砸脊,當(dāng)你用微信web開(kāi)發(fā)者工具去測(cè)試的話(huà)具篇,就會(huì)看到,“不支持模擬”這樣的提示凌埂。這個(gè)時(shí)候驱显,不要猶豫,直接上真機(jī)去測(cè)試瞳抓,這并不是我們的程序出現(xiàn)了問(wèn)題埃疫。

小tips: 在真機(jī)上,我們是沒(méi)有辦法看到console出的一些調(diào)試信息挨下,所以,要想個(gè)辦法脐湾,可以用alert臭笆,也可以把調(diào)試信息打印在屏幕上面,我選擇打印在屏幕上。這里要說(shuō)一些愁铺,微信給的文檔沒(méi)有那么齊全鹰霍,有一些是要試試才指導(dǎo)的,比如wx.config中的success和fail函數(shù)茵乱,參數(shù)信息怎么打印茂洒,其實(shí)是res.errMsg和err.errMsg.

上面我說(shuō)的這些你都注意到了,但是微信支付的控件你依然調(diào)動(dòng)不起來(lái)的話(huà)瓶竭,可能是微信商戶(hù)平臺(tái)的開(kāi)發(fā)配置出現(xiàn)了問(wèn)題督勺,在 產(chǎn)品中心-開(kāi)發(fā)配置-支付配置-公眾號(hào)支付 中進(jìn)行配置,配置的時(shí)候斤贰,注意一定要到最后一級(jí)目錄智哀,比如我要在cms.123.456/book/list/index.html頁(yè)面中去進(jìn)行微信支付,那么你的配置應(yīng)該是 cms.123.456/book/list/

好了荧恍,開(kāi)發(fā)中基本上所有的坑都提到了瓷叫,這是建立在你配置沒(méi)有出錯(cuò)的情況下。接下來(lái)送巡,看看到底能不能真的支付摹菠。


841906394647077587.jpg
微信圖片_20180212224848.png

大功告成,讓我想起了骗爆,最近流行的一句話(huà)次氨,你這磨人的小妖精,微信支付淮腾!

希望能給你們帶來(lái)幫助~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末糟需,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子谷朝,更是在濱河造成了極大的恐慌洲押,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件圆凰,死亡現(xiàn)場(chǎng)離奇詭異杈帐,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)专钉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)挑童,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人跃须,你說(shuō)我怎么就攤上這事站叼。” “怎么了菇民?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,878評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵尽楔,是天一觀的道長(zhǎng)投储。 經(jīng)常有香客問(wèn)我,道長(zhǎng)阔馋,這世上最難降的妖魔是什么玛荞? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,306評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮呕寝,結(jié)果婚禮上勋眯,老公的妹妹穿的比我還像新娘。我一直安慰自己下梢,他們只是感情好客蹋,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著怔球,像睡著了一般嚼酝。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上竟坛,一...
    開(kāi)封第一講書(shū)人閱讀 49,071評(píng)論 1 285
  • 那天闽巩,我揣著相機(jī)與錄音,去河邊找鬼担汤。 笑死涎跨,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的崭歧。 我是一名探鬼主播隅很,決...
    沈念sama閱讀 38,382評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼率碾!你這毒婦竟也來(lái)了叔营?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,006評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤所宰,失蹤者是張志新(化名)和其女友劉穎绒尊,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體仔粥,經(jīng)...
    沈念sama閱讀 43,512評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡婴谱,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了躯泰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谭羔。...
    茶點(diǎn)故事閱讀 38,094評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖麦向,靈堂內(nèi)的尸體忽然破棺而出瘟裸,到底是詐尸還是另有隱情,我是刑警寧澤诵竭,帶...
    沈念sama閱讀 33,732評(píng)論 4 323
  • 正文 年R本政府宣布话告,位于F島的核電站十办,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏超棺。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評(píng)論 3 307
  • 文/蒙蒙 一呵燕、第九天 我趴在偏房一處隱蔽的房頂上張望棠绘。 院中可真熱鬧,春花似錦再扭、人聲如沸氧苍。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,286評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)让虐。三九已至,卻和暖如春罢荡,著一層夾襖步出監(jiān)牢的瞬間赡突,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,512評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工区赵, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留惭缰,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,536評(píng)論 2 354
  • 正文 我出身青樓笼才,卻偏偏與公主長(zhǎng)得像漱受,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子骡送,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評(píng)論 2 345

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