觸屏刷卡機(jī)怎么刷卡
網(wǎng)上關(guān)于觸屏刷卡機(jī)怎么刷卡, 一步一步教你在SpringBoot中集成微信刷卡支付的刷卡知識(shí)比較多,也有關(guān)于觸屏刷卡機(jī)怎么刷卡的問(wèn)題,今天第一pos網(wǎng)(www.fzog.com.cn)為大家整理刷卡常見(jiàn)知識(shí),未來(lái)的我們終成一代卡神。
本文目錄一覽:
觸屏刷卡機(jī)怎么刷卡
一:準(zhǔn)備工作
使用微信支付需要先開(kāi)通服務(wù)號(hào),然后還要開(kāi)通微信支付,最后還要配置一些開(kāi)發(fā)參數(shù),過(guò)程比較多。
申請(qǐng)服務(wù)號(hào)(企業(yè))開(kāi)通微信支付開(kāi)發(fā)配置 \x08 具體準(zhǔn)備工作請(qǐng)參考Spring Boot入門(mén)教程(三十九):微信支付集成-申請(qǐng)服務(wù)號(hào)和微信支付二:支付方式刷卡支付(MICROPAY) :刷卡支付是用戶(hù)展示微信錢(qián)包內(nèi)的“刷卡條碼/二維碼”給商戶(hù)系統(tǒng)掃描后直接完成支付的模式。主要應(yīng)用線(xiàn)下面對(duì)面收銀的場(chǎng)景。相當(dāng)于支付寶的條碼支付掃碼支付:掃碼支付是商戶(hù)系統(tǒng)按微信支付協(xié)議生成支付二維碼,用戶(hù)再用微信“掃一掃”完成支付的模式。該模式適用于PC網(wǎng)站支付、實(shí)體店單品或訂單支付、媒體廣告支付等場(chǎng)景。相當(dāng)于支付寶的電腦網(wǎng)站支付H5支付:H5支付是指商戶(hù)在微信客戶(hù)端外的移動(dòng)端網(wǎng)頁(yè)展示商品或服務(wù),用戶(hù)在前述頁(yè)面確認(rèn)使用微信支付時(shí),商戶(hù)發(fā)起本服務(wù)呼起微信客戶(hù)端進(jìn)行支付。主要用于觸屏版的手機(jī)瀏覽器請(qǐng)求微信支付的場(chǎng)景。可以方便的從外部瀏覽器喚起微信支付。相當(dāng)于支付寶的手機(jī)網(wǎng)站支付公眾號(hào)支付(JSAPI):商戶(hù)已有H5商城網(wǎng)站,用戶(hù)通過(guò)消息或掃描二維碼在微信內(nèi)打開(kāi)網(wǎng)頁(yè)時(shí),可以調(diào)用微信支付完成下單購(gòu)買(mǎi)的流程。App支付:APP支付又稱(chēng)移動(dòng)端支付,是商戶(hù)通過(guò)在移動(dòng)端應(yīng)用APP中集成開(kāi)放SDK調(diào)起微信支付模塊完成支付的模式。小程序支付:在小程序中使用H5支付: 是應(yīng)用在微信客戶(hù)端外,是一種WAP支付。 公眾號(hào)支付(JSAPI): 是應(yīng)用在微信內(nèi)的一種支付。 兩種方式的應(yīng)用場(chǎng)景不一樣。
三:刷卡支付場(chǎng)景介紹1. 場(chǎng)景介紹步驟1:用戶(hù)選擇刷卡支付付款并打開(kāi)微信,進(jìn)入“我”->“錢(qián)包”->“收付款”條碼界面;步驟2:收銀員在商戶(hù)系統(tǒng)操作生成支付訂單,用戶(hù)確認(rèn)支付金額;步驟3:商戶(hù)收銀員用掃碼設(shè)備掃描用戶(hù)的條碼/二維碼,商戶(hù)收銀系統(tǒng)提交支付;步驟4:微信支付后臺(tái)系統(tǒng)收到支付請(qǐng)求,根據(jù)驗(yàn)證密碼規(guī)則判斷是否驗(yàn)證用戶(hù)的支付密碼,不需要驗(yàn)證密碼的交易直接發(fā)起扣款,需要驗(yàn)證密碼的交易會(huì)彈出密碼輸入框。支付成功后微信端會(huì)彈出成功頁(yè)面,支付失敗會(huì)彈出錯(cuò)誤提示。2. 驗(yàn)證密碼規(guī)則:支付金額>1000元的交易需要驗(yàn)證用戶(hù)支付密碼用戶(hù)賬號(hào)每天最多有5筆交易可以免密,超過(guò)后需要驗(yàn)證密碼微信支付后臺(tái)判斷用戶(hù)支付行為有異常情況,符合免密規(guī)則的交易也會(huì)要求驗(yàn)證密碼3. 免密支付流程:(1)收銀員在商戶(hù)收銀臺(tái)生成支付訂單,向用戶(hù)展示支付金額;
(2)用戶(hù)打開(kāi)微信客戶(hù)端,點(diǎn)擊“我的錢(qián)包”,選擇“刷卡”,進(jìn)入條碼界面;
(3)收銀員使用掃碼設(shè)備讀取用戶(hù)手機(jī)屏幕上的條碼;
(4)掃碼設(shè)備將讀取的信息上傳給門(mén)店收銀臺(tái);
(5)門(mén)店收銀臺(tái)得到支付信息后,向商戶(hù)收銀后臺(tái)發(fā)起支付請(qǐng)求。
(6)商戶(hù)后臺(tái)對(duì)門(mén)店收銀臺(tái)的支付請(qǐng)求進(jìn)行處理,生成簽名后調(diào)用【提交刷卡支付API】向微信支付系統(tǒng)發(fā)起支付請(qǐng)求。
(7)微信支付系統(tǒng)得到商戶(hù)側(cè)的支付請(qǐng)求之后會(huì)對(duì)請(qǐng)求進(jìn)行驗(yàn)證,驗(yàn)證通過(guò)之后會(huì)對(duì)請(qǐng)求數(shù)據(jù)進(jìn)行處理,最后將處理后的支付結(jié)果返回給商戶(hù)收銀后臺(tái)。如果支付成功,微信支付系統(tǒng)會(huì)將支付結(jié)果返回給商戶(hù),同時(shí)把支付結(jié)果通知給用戶(hù)(以短信、微信消息的形式通知)。
(8)商戶(hù)收銀后臺(tái)對(duì)得到的支付結(jié)果進(jìn)行簽名驗(yàn)證和處理,再將支付結(jié)果返回給門(mén)店收銀臺(tái)。
(9)收銀員看到門(mén)店收銀臺(tái)的支付結(jié)果后給用戶(hù)發(fā)貨。
4. 驗(yàn)密支付流程驗(yàn)密支付流程文檔
在商戶(hù)調(diào)用【提交刷卡支付API】發(fā)起支付請(qǐng)求之后,微信支付后臺(tái)提示用戶(hù)輸入密碼確認(rèn)支付,接口同步返回USERPAYING狀態(tài),商戶(hù)系統(tǒng)再輪詢(xún)調(diào)用查詢(xún)訂單接口來(lái)確認(rèn)當(dāng)前用戶(hù)是否已經(jīng)支付成功。
以下時(shí)序圖說(shuō)明驗(yàn)密支付流程:
由于在商戶(hù)收銀后臺(tái)向微信支付系統(tǒng)發(fā)起支付請(qǐng)求之前的流程是完全一樣的,所以這里只介紹商戶(hù)發(fā)起支付請(qǐng)求之后的邏輯。
(1)商戶(hù)門(mén)店生成訂單后,收銀臺(tái)向后臺(tái)系統(tǒng)發(fā)起支付請(qǐng)求。
(2)后臺(tái)調(diào)用微信支付【提交刷卡支付API】生成支付交易。
(3)微信支付系統(tǒng)對(duì)商戶(hù)請(qǐng)求進(jìn)行驗(yàn)證,驗(yàn)證通過(guò)后判斷當(dāng)前用戶(hù)需要輸入密碼。
(4)微信支付系統(tǒng)返回USERPAYING狀態(tài),商戶(hù)后臺(tái)系統(tǒng)將應(yīng)答結(jié)果返回給商戶(hù)門(mén)店收銀臺(tái)。
(5)微信支付系統(tǒng)通知用戶(hù)微信客戶(hù)端輸入密碼。
(6)用戶(hù)得到輸入密碼提示后,確認(rèn)支付并輸入密碼。
(7)完成密碼輸入,提交微信支付。
(8)微信客戶(hù)端在用戶(hù)完成支付后提示微信支付后臺(tái)系統(tǒng)返回的支付結(jié)果,而且微信支付系統(tǒng)會(huì)通過(guò)短信、微信消息給用戶(hù)發(fā)送支付結(jié)果提醒。
(9)商戶(hù)收銀臺(tái)得到USERPAYING狀態(tài)后,經(jīng)過(guò)商戶(hù)后臺(tái)系統(tǒng)調(diào)用【查詢(xún)訂單API】查詢(xún)實(shí)際支付結(jié)果。
(10)如果支付結(jié)果仍為USERPAYING,則每隔5秒循環(huán)調(diào)用【查詢(xún)訂單API】判斷實(shí)際支付結(jié)果,如果用戶(hù)取消支付或累計(jì)30秒用戶(hù)都未支付,商戶(hù)收銀臺(tái)退出查詢(xún)流程后繼續(xù)調(diào)用【撤銷(xiāo)訂單API】撤銷(xiāo)支付交易。
5. 異常處理用戶(hù)遇到支付異常,請(qǐng)按如下說(shuō)明處理
(1)用戶(hù)微信端彈出系統(tǒng)錯(cuò)誤提示框,用戶(hù)可在交易列表查看交易情況,如果未找到訂單,需要商戶(hù)重新發(fā)起支付交易;如果訂單顯示成功支付,商戶(hù)收銀系統(tǒng)再次調(diào)用【查詢(xún)訂單API】查詢(xún)實(shí)際支付結(jié)果;
(2)用戶(hù)微信端彈出支付失敗提示,例如:余額不足,信用卡失效。需要重新發(fā)起支付;
(3)當(dāng)交易超時(shí)或支付交易失敗,商戶(hù)收銀系統(tǒng)必須調(diào)用【撤銷(xiāo)訂單API】,撤銷(xiāo)此交易。
(4)由于銀行系統(tǒng)異常、用戶(hù)余額不足、不支持用戶(hù)卡種等原因使當(dāng)前支付交易失敗,商戶(hù)收銀系統(tǒng)應(yīng)該把錯(cuò)誤提示明確展示給收銀員。
(5)根據(jù)返回的錯(cuò)誤碼,判斷是否需要撤銷(xiāo)交易,具體詳見(jiàn)API返回錯(cuò)誤碼列表
public Map<String, String> microPayWithPos(Map<String, String> reqData) throws Exception { return this.microPayWithPos(reqData, this.config.getHttpConnectTimeoutMs());}/** * 提交刷卡支付,針對(duì)軟POS,盡可能做成功 * 內(nèi)置重試機(jī)制,最多60s * @param reqData * @param connectTimeoutMs * @return * @throws Exception */public Map<String, String> microPayWithPos(Map<String, String> reqData, int connectTimeoutMs) throws Exception { int remainingTimeMs = 60*1000; long startTimestampMs = 0; Map<String, String> lastResult = null; Exception lastException = null; while (true) { startTimestampMs = WXPayUtil.getCurrentTimestampMs(); int readTimeoutMs = remainingTimeMs - connectTimeoutMs; if (readTimeoutMs > 1000) { try { lastResult = this.microPay(reqData, connectTimeoutMs, readTimeoutMs); String returnCode = lastResult.get("return_code"); if (returnCode.equals("SUCCESS")) { String resultCode = lastResult.get("result_code"); String errCode = lastResult.get("err_code"); if (resultCode.equals("SUCCESS")) { break; } else { // 看錯(cuò)誤碼,若支付結(jié)果未知,則重試提交刷卡支付 if (errCode.equals("SYSTEMERROR") || errCode.equals("BANKERROR") || errCode.equals("USERPAYING")) { remainingTimeMs = remainingTimeMs - (int)(WXPayUtil.getCurrentTimestampMs() - startTimestampMs); if (remainingTimeMs <= 100) { break; } else { WXPayUtil.getLogger().info("microPayWithPos: try micropay again"); if (remainingTimeMs > 5*1000) { Thread.sleep(5*1000); } else { Thread.sleep(1*1000); } continue; } } else { break; } } } else { break; } } catch (Exception ex) { lastResult = null; lastException = ex; } } else { break; } } if (lastResult == null) { throw lastException; } else { return lastResult; }}
看Demo中的方法,整個(gè)方法也沒(méi)有查詢(xún)訂單,也沒(méi)有撤銷(xiāo)訂單的操作,而是不停的重復(fù)調(diào)用microPay,這和上面文檔的(9)、(10)條邏輯不一樣
注意:微信的刷卡支付并沒(méi)有支付通知接口,只有退款通知接口;支付寶的條碼支付是有支付通知接口的,兩家是不一樣的。四:開(kāi)發(fā)文檔在集成之前,一定要熟悉業(yè)務(wù),熟悉文檔
微信支付開(kāi)發(fā)文檔
刷卡文檔,開(kāi)發(fā)前請(qǐng)?jiān)敿?xì)認(rèn)證的看完該文檔。
刷卡支付流程圖 必須看
刷卡文檔中有demo,SDK與DEMO下載,需要下載下來(lái),熟悉一下項(xiàng)目結(jié)構(gòu),以及READEME.md
五:集成步驟1. 引入SDKwxpay-sdk 項(xiàng)目中的src就是要引入的sdk,可以直接將src的所有7個(gè)源文件拖入到自己項(xiàng)目中wxpay-sdk 的READEME.md中說(shuō)可以通過(guò)maven來(lái)引入sdk現(xiàn)在有兩種方式,選擇其中一種,究竟選哪一種?
wxpay-sdk的src下面有7個(gè)Java文件,通過(guò)maven引入可以看到的sdk中就4個(gè)java文件,兩種方式文件個(gè)數(shù)不一致,多出來(lái)的3個(gè)文件是demo中用到的文件,并不是原始sdk中的文件,通過(guò)查看sdk中的源碼可以看到,這4個(gè)文件其實(shí)就是使用httpclient來(lái)調(diào)用微信支付的支付接口,其中很多重要的邏輯并沒(méi)有按照官方文檔中說(shuō)的那樣缺少重要的邏輯實(shí)現(xiàn),這個(gè)需要自己去完善一些邏輯
開(kāi)發(fā)中發(fā)現(xiàn)WXPay這個(gè)類(lèi)和maven中的類(lèi)并不完全一樣,多了兩個(gè)方法microPayWithPos,其中這兩個(gè)方法是demo作者自己基于內(nèi)部方法microPay的一個(gè)封裝,完善了部分邏輯, 但是完善的邏輯和官網(wǎng)文檔的描述不一致,有重大邏輯問(wèn)題
查看demo中的test發(fā)現(xiàn)竟然有一個(gè)類(lèi)叫test,命名不清晰,語(yǔ)義太籠統(tǒng),而且還是小寫(xiě)字母開(kāi)頭,不符合Java的基本命名規(guī)范,關(guān)于java中的測(cè)試的命名規(guī)則一般類(lèi)名以Test作為后綴,而該demo以Test作為前綴,一般測(cè)試類(lèi)方法的命名以test作為前綴,但是demo中的測(cè)試并沒(méi)有什么test,一般測(cè)試類(lèi)都要使用測(cè)試框架如Junit等,但是demo中的測(cè)試并沒(méi)有使用測(cè)試框架,而是使用main方法來(lái)運(yùn)行的。
對(duì)wxpay-sdk的評(píng)價(jià):wxpay-sdk只是使用httpclient來(lái)調(diào)用微信支付的接口,只管調(diào)用微信支付接口,然后解析一下響應(yīng),并不處理支付中的業(yè)務(wù)邏輯,盡管一些和支付密切相關(guān)的重要邏輯也不會(huì)處理,甚至解密的工具方法都沒(méi)有現(xiàn)成的,需要開(kāi)發(fā)者自己去處理??磀emo可以知道,寫(xiě)的不是一般的爛,沒(méi)想到微信那么大的廠(chǎng)竟然sdk寫(xiě)的這么懶,demo寫(xiě)的這么low,真是丟人,和支付寶的sdk比一個(gè)地下一個(gè)天上。
罵完了,還是要集成的,這里我選擇不相信demo的源文件,使用README.md中的maven來(lái)引入sdk,一個(gè)是demo寫(xiě)的太爛,多出來(lái)的文件也不是不要不行的,多出來(lái)的方法有重大邏輯缺陷,萬(wàn)一哪天微信哪天良心發(fā)現(xiàn)完善sdk了,更新了maven版本,自己只需要修改一下maven的版本號(hào)就行了。
<dependency> <groupId>com.github.wxpay</groupId> <artifactId>wxpay-sdk</artifactId> <version>0.0.3</version></dependency>2. application.yml
配置微信支付參數(shù),其中appID、mchID、key、certPath是必須的,notifyUrl是可選的,sandboxKey和useSandbox是自己配置的,便于正式環(huán)境和沙箱環(huán)境的切換,關(guān)于參數(shù)的值去微信的開(kāi)發(fā)配置中查看
useSandbox:true表示使用沙箱環(huán)境,如果為false為正式環(huán)境sandboxKey: 沙箱環(huán)境API秘鑰,需要通過(guò)下面的WXPayClient#getSignKey方法獲取pay: wxpay: appID: xxx mchID: xxx key: xxx sandboxKey: xxx certPath: /var/local/cert/apiclient_cert.p12 notifyUrl: http://65ta5j.natappfree.cc/wxpay/refund/notify useSandbox: false3. MyWXPayConfig
配置微信參數(shù),其實(shí)就是一種Properties類(lèi),需要實(shí)現(xiàn)WXPayConfig中的方法 注意:demo中的WXPayConfig是一種抽象類(lèi)abstract class,可以看到maven與demo的不統(tǒng)一
/** * 微信支付的參數(shù)配置 * * @author mengday zhang */@Data@Slf4j@ConfigurationProperties(prefix = "pay.wxpay")public class MyWXPayConfig implements WXPayConfig { /** 公眾賬號(hào)ID */ private String appID; /** 商戶(hù)號(hào) */ private String mchID; /** API 密鑰 */ private String key; /** API 沙箱環(huán)境密鑰 */ private String sandboxKey; /** API證書(shū)絕對(duì)路徑 */ private String certPath; /** 退款異步通知地址 */ private String notifyUrl; private Boolean useSandbox; /** HTTP(S) 連接超時(shí)時(shí)間,單位毫秒 */ private int httpConnectTimeoutMs = 8000; /** HTTP(S) 讀數(shù)據(jù)超時(shí)時(shí)間,單位毫秒 */ private int httpReadTimeoutMs = 10000; /** * 獲取商戶(hù)證書(shū)內(nèi)容 * * @return 商戶(hù)證書(shū)內(nèi)容 */ @Override public InputStream getCertStream() { File certFile = new File(certPath); InputStream inputStream = null; try { inputStream = new FileInputStream(certFile); } catch (FileNotFoundException e) { log.error("cert file not found, path={}, exception is:{}", certPath, e); } return inputStream; } @Override public String getKey(){ if (useSandbox) { return sandboxKey; } return key; }}4. WXPayClient
WXPayClient 是對(duì)WXPay的一個(gè)封裝,增加了microPayWithPOS方法,內(nèi)部調(diào)用WXPay#microPay,但是sdk中的microPay并沒(méi)有處理當(dāng)微信支付時(shí)微信提示用戶(hù)輸入密碼,這時(shí)sdk直接返回的錯(cuò)誤,這里處理的邏輯就是在指定時(shí)間內(nèi)去輪詢(xún)支付結(jié)果,然后將輪詢(xún)的結(jié)果返回出去,而不是就直接返回錯(cuò)誤了,demo項(xiàng)目也有個(gè)microPayWithPOS實(shí)現(xiàn),但是它的邏輯是當(dāng)用戶(hù)輸入密碼的情況會(huì)輪詢(xún)的去下單去調(diào)用WXPay#microPay,這種做法是不符合微信的官方文檔的。另外增加了獲取沙箱環(huán)境API秘鑰和解密退換通知的方法。
吐槽一下:感覺(jué)像這種解密方法WXPayUtil中竟然沒(méi)有,這是必須的不可少的啊,怎么會(huì)沒(méi)有呢,這也太爛了吧,這還稱(chēng)為sdk啊
/** * WXPayClient * <p> * 對(duì)WXPay的簡(jiǎn)單封裝,處理支付密切相關(guān)的邏輯. * * @author Mengday Zhang * @version 1.0 * @since 2018/6/16 */@Slf4jpublic class WXPayClient extends WXPay { /** 密鑰算法 */ private static final String ALGORITHM = "AES"; /** 加解密算法/工作模式/填充方式 */ private static final String ALGORITHM_MODE_PADDING = "AES/ECB/PKCS5Padding"; /** 用戶(hù)支付中,需要輸入密碼 */ private static final String ERR_CODE_USERPAYING = "USERPAYING"; private static final String ERR_CODE_AUTHCODEEXPIRE = "AUTHCODEEXPIRE"; /** 交易狀態(tài): 未支付 */ private static final String TRADE_STATE_NOTPAY = "NOTPAY"; /** 用戶(hù)輸入密碼,嘗試30秒內(nèi)去查詢(xún)支付結(jié)果 */ private static Integer remainingTimeMs = 10000; private WXPayConfig config; public WXPayClient(WXPayConfig config, WXPayConstants.SignType signType, boolean useSandbox) { super(config, signType, useSandbox); this.config = config; } /** * * 刷卡支付 * * 對(duì)WXPay#microPay(Map)增加了當(dāng)支付結(jié)果為USERPAYING時(shí)去輪詢(xún)查詢(xún)支付結(jié)果的邏輯處理 * * 注意:該方法沒(méi)有處理return_code=FAIL的情況,暫時(shí)不考慮網(wǎng)絡(luò)問(wèn)題,這種情況直接返回錯(cuò)誤 * * @param reqData * @return * @throws Exception */ public Map<String, String> microPayWithPOS(Map<String, String> reqData) throws Exception { // 開(kāi)始時(shí)間(毫秒) long startTimestampMs = System.currentTimeMillis(); Map<String, String> responseMapForPay = super.microPay(reqData); log.info(responseMapForPay.toString()); // // 先判斷 協(xié)議字段返回(return_code),再判斷 業(yè)務(wù)返回,最后判斷 交易狀態(tài)(trade_state) // 通信標(biāo)識(shí),非交易標(biāo)識(shí) String returnCode = responseMapForPay.get("return_code"); if (WXPayConstants.SUCCESS.equals(returnCode)) { String errCode = responseMapForPay.get("err_code"); // 余額不足,信用卡失效 if (ERR_CODE_USERPAYING.equals(errCode) || "SYSTEMERROR".equals(errCode) || "BANKERROR".equals(errCode)) { Map<String, String> orderQueryMap = null; Map<String, String> requestData = new HashMap<>(); requestData.put("out_trade_no", reqData.get("out_trade_no")); // 用戶(hù)支付中,需要輸入密碼或系統(tǒng)錯(cuò)誤則去重新查詢(xún)訂單API err_code, result_code, err_code_des // 每次循環(huán)時(shí)的當(dāng)前系統(tǒng)時(shí)間 - 開(kāi)始時(shí)記錄的時(shí)間 > 設(shè)定的30秒時(shí)間就退出 while (System.currentTimeMillis() - startTimestampMs < remainingTimeMs) { // 商戶(hù)收銀臺(tái)得到USERPAYING狀態(tài)后,經(jīng)過(guò)商戶(hù)后臺(tái)系統(tǒng)調(diào)用【查詢(xún)訂單API】查詢(xún)實(shí)際支付結(jié)果。 orderQueryMap = super.orderQuery(requestData); String returnCodeForQuery = orderQueryMap.get("return_code"); if (WXPayConstants.SUCCESS.equals(returnCodeForQuery)) { // 通訊成功 String tradeState = orderQueryMap.get("trade_state"); if (WXPayConstants.SUCCESS.equals(tradeState)) { // 如果成功了直接將查詢(xún)結(jié)果返回 return orderQueryMap; } // 如果支付結(jié)果仍為USERPAYING,則每隔5秒循環(huán)調(diào)用【查詢(xún)訂單API】判斷實(shí)際支付結(jié)果 Thread.sleep(1000); } } // 如果用戶(hù)取消支付或累計(jì)30秒用戶(hù)都未支付,商戶(hù)收銀臺(tái)退出查詢(xún)流程后繼續(xù)調(diào)用【撤銷(xiāo)訂單API】撤銷(xiāo)支付交易。 String tradeState = orderQueryMap.get("trade_state"); if (TRADE_STATE_NOTPAY.equals(tradeState) || ERR_CODE_USERPAYING.equals(tradeState) || ERR_CODE_AUTHCODEEXPIRE.equals(tradeState)) { Map<String, String> reverseMap = this.reverse(requestData); String returnCodeForReverse = reverseMap.get("return_code"); String resultCode = reverseMap.get("result_code"); if (WXPayConstants.SUCCESS.equals(returnCodeForReverse) && WXPayConstants.SUCCESS.equals(resultCode)) { // 如果撤銷(xiāo)成功,需要告訴客戶(hù)端已經(jīng)撤銷(xiāo)訂單了 responseMapForPay.put("err_code_des", "用戶(hù)取消支付或尚未支付,后臺(tái)已經(jīng)撤銷(xiāo)該訂單,請(qǐng)重新支付!"); } } } } return responseMapForPay; } /** * 解密退款通知 * * <a href="https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_16&index=11>退款結(jié)果通知文檔</a> * @param reqInfo * @return * @throws Exception */ public Map<String, String> decodeRefundNotify(String reqInfo) throws Exception { //(1)對(duì)加密串A做base64解碼,得到加密串B byte[] bytes = new BASE64Decoder().decodeBuffer(reqInfo); //(2)對(duì)商戶(hù)key做md5,得到32位小寫(xiě)key* ( key設(shè)置路徑:微信商戶(hù)平臺(tái)(pay.weixin.qq.com)-->賬戶(hù)設(shè)置-->API安全-->密鑰設(shè)置 ) Cipher cipher = Cipher.getInstance(ALGORITHM_MODE_PADDING); SecretKeySpec key = new SecretKeySpec(WXPayUtil.MD5(config.getKey()).toLowerCase().getBytes(), ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, key); // (3)用key*對(duì)加密串B做AES-256-ECB解密(PKCS7Padding) // java.security.InvalidKeyException: Illegal key size or default parameters // https://www.cnblogs.com/yaks/p/5608358.html String responseXml = new String(cipher.doFinal(bytes),"UTF-8"); Map<String, String> responseMap = WXPayUtil.xmlToMap(responseXml); return responseMap; } /** * 獲取沙箱環(huán)境驗(yàn)簽秘鑰API * <a href="https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=23_1">獲取驗(yàn)簽秘鑰API文檔</a> * @return * @throws Exception */ public Map<String, String> getSignKey() throws Exception { Map<String, String> reqData = new HashMap<>(); reqData.put("appid", config.getAppID()); reqData.put("mch_id", config.getMchID()); reqData.put("nonce_str", WXPayUtil.generateNonceStr()); String sign = WXPayUtil.generateSignature(reqData, config.getKey(), WXPayConstants.SignType.MD5); reqData.put("sign", sign); String responseXml = this.requestWithoutCert("https://api.mch.weixin.qq.com/sandboxnew/pay/getsignkey", reqData, config.getHttpConnectTimeoutMs(), config.getHttpReadTimeoutMs()); Map<String, String> responseMap = WXPayUtil.xmlToMap(responseXml); return responseMap; }}5. WXPayConfiguration
/** * 微信支付配置 * * @author mengday zhang */@Configuration@EnableConfigurationProperties(MyWXPayConfig.class)public class WXPayConfiguration { @Autowired private MyWXPayConfig wxPayConfig; /** * useSandbox true為沙盒環(huán)境 * @return */ @Bean public WXPay wxPay() { return new WXPay(wxPayConfig, WXPayConstants.SignType.MD5, wxPayConfig.getUseSandbox() ); } @Bean public WXPayClient wxPayClient() { return new WXPayClient(wxPayConfig, WXPayConstants.SignType.MD5, wxPayConfig.getUseSandbox()); }}6. WXPayMicroPayController
/** * 微信支付-刷卡支付. * <p> * detailed description * * @author Mengday Zhang * @version 1.0 * @since 2018/6/18 */@Slf4j@RestController@RequestMapping("/wxpay/microPay")public class WXPayMicroPayController { @Autowired private WXPayClient wxPayClient; /** * 刷卡支付(類(lèi)似支付寶的條碼支付) * * 和支付寶的好像不一樣,支付寶有支付通知,但是微信好像沒(méi),微信有退款通知 * * 微信支付后臺(tái)系統(tǒng)收到支付請(qǐng)求,根據(jù)驗(yàn)證密碼規(guī)則判斷是否驗(yàn)證用戶(hù)的支付密碼,不需要驗(yàn)證密碼的交易直接發(fā)起扣款, * 需要驗(yàn)證密碼的交易會(huì)彈出密碼輸入框。支付成功后微信端會(huì)彈出成功頁(yè)面,支付失敗會(huì)彈出錯(cuò)誤提示 * 注意該接口有可能返回錯(cuò)誤碼為USERPAYING用戶(hù)支付中 * * 驗(yàn)證密碼規(guī)則 * ◆ 支付金額>1000元的交易需要驗(yàn)證用戶(hù)支付密碼 * ◆ 用戶(hù)賬號(hào)每天最多有5筆交易可以免密,超過(guò)后需要驗(yàn)證密碼 * ◆ 微信支付后臺(tái)判斷用戶(hù)支付行為有異常情況,符合免密規(guī)則的交易也會(huì)要求驗(yàn)證密碼 * * 用戶(hù)刷卡條形碼規(guī)則:18位純數(shù)字,以10、11、12、13、14、15開(kāi)頭 */ @PostMapping("") public Object microPay(String authCode) throws Exception { Map<String, String> reqData = new HashMap<>(); // 商戶(hù)訂單號(hào) reqData.put("out_trade_no", String.valueOf(System.nanoTime())); // 訂單總金額,單位為分,只能為整數(shù) reqData.put("total_fee", "2"); // 授權(quán)碼 reqData.put("auth_code", authCode); // 商品描述 reqData.put("body", "測(cè)試"); Map<String, String> resultMap = wxPayClient.microPayWithPOS(reqData); log.info(resultMap.toString()); return resultMap; }}7. WXPayController
/** * 微信支付 - 通用API. * * <p> * 類(lèi)似支付寶中的條碼支付. * * @author Mengday Zhang * @version 1.0 * @since 2018/6/15 */@Slf4j@RestController@RequestMapping("/wxpay")public class WXPayController { @Autowired private WXPay wxPay; @Autowired private WXPayClient wxPayClient; @Autowired private MyWXPayConfig wxPayConfig; /** * 訂單查詢(xún) * @param orderNo * @return * @throws Exception */ @GetMapping("/orderQuery") public Object orderQuery(String orderNo) throws Exception { Map<String, String> data = new HashMap<>(); data.put("out_trade_no", orderNo); Map<String, String> result = wxPay.orderQuery(data); return result; } /** * 退款 * 注意:調(diào)用申請(qǐng)退款、撤銷(xiāo)訂單接口需要商戶(hù)證書(shū) * 注意:沙箱環(huán)境響應(yīng)結(jié)果可能會(huì)是"沙箱支付金額不正確,請(qǐng)確認(rèn)驗(yàn)收case",但是正式環(huán)境不會(huì)報(bào)這個(gè)錯(cuò)誤 * 微信支付的最小金額是0.1元,所以在測(cè)試支付時(shí)金額必須大于0.1元,否則會(huì)提示微信支付配置錯(cuò)誤,可以將microPay的total_fee大于1再退款 */ @PostMapping("/refund") public Object refund(String orderNo) throws Exception { Map<String, String> reqData = new HashMap<>(); // 商戶(hù)訂單號(hào) reqData.put("out_trade_no", orderNo); // 授權(quán)碼 reqData.put("out_refund_no", orderNo); // 訂單總金額,單位為分,只能為整數(shù) reqData.put("total_fee", "2"); //退款金額 reqData.put("refund_fee", "2"); // 退款異步通知地址 reqData.put("notify_url", wxPayConfig.getNotifyUrl()); reqData.put("refund_fee_type", "CNY"); reqData.put("op_user_id", wxPayConfig.getMchID()); Map<String, String> resultMap = wxPay.refund(reqData); log.info(resultMap.toString()); return resultMap; } /** * 退款結(jié)果通知 * * 特別說(shuō)明:退款結(jié)果對(duì)重要的數(shù)據(jù)進(jìn)行了加密,商戶(hù)需要用商戶(hù)秘鑰進(jìn)行解密后才能獲得結(jié)果通知的內(nèi)容 * @param request * @throws Exception */ @RequestMapping("/refund/notify") public String refundNotify(HttpServletRequest request) throws Exception {// Map<String, String> notifyMap = new HashMap<>();// notifyMap.put("nonce_str", "9b4e428ae262d5dca96178027e849fa9");// notifyMap.put("req_info", "VKGj8c81RwQ45LcyWEVBE9/HsNfADGbgmbIAQZ2ydcbIFhIIcJFKFQwGfcSGgFGtQlWvg6KDNsRjmCjN+PvipJ3roynJ7cME0LOFG50VGtk4EYHqdjFzUVANI7GpT+i6Ok+ZWivH0MwoGK2fsz3WG+bYs2XJBwav/K89tKjFhZGitCKKBeGqcP99fa/gAL0swNXXNQHmL806Zi+QcACzL3E89BtP9FlXM2Gi+wPQafvPr+/iE+LrPdMlNUa5LiZnenZXUF24kMdhaTafczcKL4sZjRXQHEfEyc/pIZPbIjcNIETvHsskyzKuHVr/SAFkxaM6RR1Kl9pyWZGUjkH5SOeqsT8uL7YQmTlDXrnXmno3AvZdnepTGL5w69yIOmQNPeMqsd01ES9WX36GZYOigfi2+BJ9RRXjIffmpB/MFF+zryyvLTaJE2obCwFSHrmOD8YbaJqrZXOUvWZQrn7wIQgaCypo8V57cD3w5d2RSgIHNrdnEDYlbRcLNYgKuL+T9+1HPhU/frowZgwPN9IB53OahZV3p1Yvos23kvhqPCLn3BYgUihRbey6QhEtL2QyifiQ9e8WVLzWpRZ+DOa8VrhYvSuTfjRdjoNanqHFvXGP6uEsEa+DETqnexpB7xOS9m/CdmlNCwbdUplPEVzNQQdzYT4kybi00Y8A+EdairxfVyK9A7MAYAMtAO9yxV2ht0bn3SofFyZe/YSzdJgxdtcxBf1CVYN6x+yHcSueCSgq4cM/2VCwh4J1+pUVmNpEm0OVcdKbV5USkaxJR0h7Yd+n5FTz5Q2S/qvyDo202cUzLFPI5UqQm5X+FOrWDAkmmr5yVcDQIm3dAdb31jkz0X2TPYt5g7ciQ1h9heyVxJ67FexKvEM4pKCCubtWx6nyxcOUghHMrh8DSoBtewtNjbnwGVIbLsSb6X9MIYAkWIDbqNVP1f63GiZU+cJlhBmvcb8aeQUdTTj7EX5pOTIVSVv5D6SkKmpGU4FGvV+WjufuGX4ZRZo+01p6xl0sfZVmucG1UtxhX6bMCJb06yDwxpv7tGwkwS4TCK4SQp40Xe0=");// notifyMap.put("appid", "xxx");// notifyMap.put("mch_id", "xxx");// notifyMap.put("return_code", "SUCCESS"); // 注意:同樣的通知可能會(huì)多次發(fā)送給商戶(hù)系統(tǒng)。商戶(hù)系統(tǒng)必須能夠正確處理重復(fù)的通知。 // 推薦的做法是,當(dāng)收到通知進(jìn)行處理時(shí),首先檢查對(duì)應(yīng)業(yè)務(wù)數(shù)據(jù)的狀態(tài),判斷該通知是否已經(jīng)處理過(guò),如果沒(méi)有處理過(guò)再進(jìn)行處理,如果處理過(guò)直接返回結(jié)果成功。 // 在對(duì)業(yè)務(wù)數(shù)據(jù)進(jìn)行狀態(tài)檢查和處理之前,要采用數(shù)據(jù)鎖進(jìn)行并發(fā)控制,以避免函數(shù)重入造成的數(shù)據(jù)混亂。 // TODO 處理業(yè)務(wù) Map<String, String> requstInfoMap = wxPayClient.decodeRefundNotify(request); // 商戶(hù)處理退款通知參數(shù)后同步返回給微信參數(shù) Map<String, String> responseMap = new HashMap<>(); responseMap.put("return_code", "SUCCESS"); responseMap.put("return_msg", "OK"); String responseXml = WXPayUtil.mapToXml(responseMap); return responseXml; } /** * 退款查詢(xún) * @param orderNo * @return * @throws Exception */ @GetMapping("/refundQuery") public Object refundQuery(String orderNo) throws Exception { Map<String, String> reqData = new HashMap<>(); reqData.put("out_trade_no", orderNo); Map<String, String> result = wxPay.refundQuery(reqData); return result; } /** * 下載對(duì)賬單 * 注意: * 微信在次日9點(diǎn)啟動(dòng)生成前一天的對(duì)賬單,建議商戶(hù)10點(diǎn)后再獲??; * 對(duì)賬單接口只能下載三個(gè)月以?xún)?nèi)的賬單。 * @throws Exception */ @PostMapping("/downloadBill") public Object downloadBill(String billDate) throws Exception { Map<String, String> reqData = new HashMap<>(); reqData.put("bill_date", billDate); reqData.put("bill_type", "ALL"); Map<String, String> resultMap = wxPay.downloadBill(reqData); return resultMap; } /** * 獲取沙箱環(huán)境API秘鑰, * * 這里只是為了獲取,可以放在main方法下運(yùn)行,這里作為api來(lái)運(yùn)行的,實(shí)際情況下不應(yīng)該暴露出來(lái) * @return * @throws Exception */ @GetMapping("/sandbox/getSignKey") public Object getSignKey() throws Exception { Map<String, String> signKey = wxPayClient.getSignKey(); log.info(signKey.toString()); return signKey; }}獲取源碼
關(guān)注并私信“微信刷卡支付”獲取源代碼。
以上就是關(guān)于觸屏刷卡機(jī)怎么刷卡, 一步一步教你在SpringBoot中集成微信刷卡支付的知識(shí),后面我們會(huì)繼續(xù)為大家整理關(guān)于觸屏刷卡機(jī)怎么刷卡的知識(shí),希望能夠幫助到大家!
轉(zhuǎn)載請(qǐng)帶上網(wǎng)址:http://www.fzog.com.cn/shuaka/51512.html