天小萌同學(xué)跟大家探討一下PHP流行的開源商城平臺(tái)-----
ecshop!
估計(jì)很多聽過。
小萌同學(xué)其實(shí)是一個(gè)程序猿。
哈哈!很意外吧!(如果有項(xiàng)目想合作或外包的話,可以找我哦,嘻嘻!)
言歸正傳!
近期在做一個(gè)基于ecshop的商城項(xiàng)目,剛好有一個(gè)功能需要增加支付密碼。
咋一看,好像挺簡(jiǎn)單的,但又有些不知從何入手,認(rèn)真看了一個(gè)支付寶等插件的支付流程
發(fā)現(xiàn)支付插件類上都有g(shù)et_code和respond兩個(gè)方法,靈感來了,就用get_code返回一個(gè)支付密碼支付的HTML代碼,不就能實(shí)現(xiàn)。
在購物提交訂單時(shí),進(jìn)入支付頁面效果如下:
然后在user.php上加上act=balance_pay分支,驗(yàn)證支付密碼,驗(yàn)證通過后扣減錢包余額。
這樣就實(shí)現(xiàn)了錢包通過支付密碼完成支付了。當(dāng)然,還需要設(shè)置支付密碼。
這個(gè)代碼實(shí)現(xiàn)比較簡(jiǎn)單,小萌同學(xué)就不貼代碼出來了。
技術(shù)分享就到這里,謝謝支持啟萌分享!
貼后如果對(duì)本文章有疑問,又或者想合作、外包項(xiàng)目的,歡迎加小萌同學(xué)的微信besky987交流!
階架構(gòu)精品專題
微信支付類型
微信支付實(shí)際上有很多種不同的類型,具體要使用哪一種就需要根據(jù)不同的應(yīng)用場(chǎng)景來選擇,官方給出的參考例子:
刷卡支付:用戶打開微信錢包的刷卡的界面,商戶掃碼后提交完成支付。
公眾號(hào)支付:用戶在微信內(nèi)進(jìn)入商家H5頁面,頁面內(nèi)調(diào)用JSSDK完成支付
掃碼支付:用戶打開"微信掃一掃“,掃描商戶的二維碼后完成支付
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)頁授權(quán)及微信JS-SDK相關(guān)知識(shí),但不再詳細(xì)介紹
建議大家先閱讀以下文章了解相關(guān)內(nèi)容:
網(wǎng)頁授權(quán):https://www.jianshu.com/p/94b0e53cccc3
微信JS-SDK:https://www.jianshu.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)需要開通微信支付功能
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)品中心——>開發(fā)配置
圖中配置的例子,代表在項(xiàng)目根路徑下,以及web目錄下的頁面都有支付權(quán)限,如果不在該路徑的頁面,則無法調(diào)用支付功能。
若頁面地址為: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)頁授權(quán)域名,登錄公眾平臺(tái)——>公眾號(hào)設(shè)置——>功能設(shè)置
配置網(wǎng)頁授權(quán)域名:主要用于獲取用戶的openId,需要識(shí)別這是哪個(gè)人。
若對(duì)openID不了解的同學(xué)可先參考微信公眾號(hào)開發(fā)文檔:https://mp.weixin.qq.com/wiki
配置JS接口安全域名:要讓我們的頁面中彈出輸入密碼的窗口,需要使用微信提供的JS-SDK工具,如果不配置JS接口安全域名,你的頁面無法使用JS-SDK。
公眾號(hào)支付實(shí)現(xiàn)流程
大致流程參考官方提供的時(shí)序圖:
流程有很多,不一一演示,我們選取核心部分來實(shí)現(xiàn)。
1.提供商城主頁,用戶進(jìn)入后通過網(wǎng)頁授權(quán)獲取openid
如果對(duì)網(wǎng)頁授權(quán)不熟悉的同學(xué)先參考這篇文章:
https://www.jianshu.com/p/94b0e53cccc3
訪問主頁的地址:http://www.wolfcode.cn/index.do
當(dāng)用戶第一次打開主頁,默認(rèn)沒有code參數(shù),此時(shí)會(huì)先重定向到獲取授權(quán)的地址
(如果只需要獲取openid,可以使用scope為snsapi_base靜默授權(quán)的方式)
經(jīng)過授權(quán)地址再重定向到我們的index.do時(shí),會(huì)帶上code參數(shù),此時(shí)即可通過接口獲取用戶的openid
@Controllerpublic class IndexController { @RequestMapping("index") public void index(String code, Model model, HttpServletResponse response,HttpServletRequest request) { //如果有code就可以去獲取用戶的openid if(code!=null) { //通過code來換取access_token JSONObject json=WeChatUtil.getWebAccessToken(code); //獲取用戶openid String openid=json.getString("openid"); //設(shè)置到會(huì)話中 request.getSession().setAttribute("openid",openid); //重定向到主頁 response.sendRedirect("/index.html"); }else{ //重定向到授權(quán)頁面 response.sendRedirect(WeChatUtil.WEB_REDIRECT_URL.replace("APPID",WeChatUtil.APPID) .replace("REDIRECT_URI", RequestUtil.getUrl(request))); } } } 注意: 1. WeChatUtil.getWebAccessToken 方法在網(wǎng)頁授權(quán)的文章中有介紹。 2. WEB_REDIRECT_URL 是網(wǎng)頁授權(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ì)頁面
具體頁面根據(jù)自己的項(xiàng)目添加,主要是頁面需要提供一個(gè)可以馬上下訂單的按鈕即可。(這里不演示加入購物車功能)
點(diǎn)擊立即購買按鈕跳轉(zhuǎn)到后臺(tái)下單地址,并帶上當(dāng)前商品的id。
<script> $(function () { //立即購買按鈕 $("#orderBtn").click(function(){ //獲取商品id var id=$("#productId").val(); //提交到下單 window.location.href="/order.do?productId="+id; }) })</script>
3.接收商品參數(shù)并調(diào)用微信支付統(tǒng)一下單接口
正常的業(yè)務(wù)流程是在該方法中,獲取商品id,再通過id去查詢數(shù)據(jù)庫該商品的相關(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ù),在頁面中彈出支付窗口時(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)到支付訂單頁進(jìn)行支付 if(entity.getReturn_code().equals("SUCCESS")&&entity.getResult_code().equals("SUCCESS")){ //jssdk權(quán)限驗(yàn)證參數(shù) TreeMap<Object, Object> 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<Object, Object> 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)到查看訂單頁面 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<String,Object> 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<String> 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<String, Object> map; if(!(obj instanceof Map)) { map=ObjectUtils.objectToMap(obj); }else{ map=(TreeMap)obj; } Set<Map.Entry<String, Object>> entrySet=map.entrySet(); //遍歷鍵值對(duì) for (Map.Entry<String, Object> 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.提供訂單展示頁面
若對(duì)微信JS-SDK不了解的同學(xué)可先參考該文章:
https://www.jianshu.com/p/b3c4450f845e
在頁面中調(diào)用微信JS-SDK,通過config接口注入權(quán)限驗(yàn)證配置,并且添加支付功能。
<!--jquery--> <script src="/js/jquery.min.js" type="text/javascript" charset="utf-8"></script> <!--微信的JSSDK--> <script src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script> <script> $(function() { <!--通過config接口注入權(quán)限驗(yàn)證配置--> wx.config({ debug: true, // 開啟調(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("支付成功!"); } }); }) }); }); </script>
點(diǎn)擊馬上付款后可彈出支付窗口:
支付完成:
5.支付結(jié)果的處理
當(dāng)用戶支付后,微信會(huì)把支付結(jié)果發(fā)送到我們前面指定的notify_url地址,我們可以根據(jù)支付結(jié)果的參數(shù)來做相關(guān)的業(yè)務(wù)邏輯,但這里暫不實(shí)現(xiàn),具體支付通知結(jié)果的參數(shù)可參考官方文章:
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7&index=8
作者:陳惠
鏈接:https://www.jianshu.com/p/9c322b1a5274
哥說前端,不太喜歡形而上的理論知識(shí),或者高大上的架構(gòu)理念,專注一些實(shí)用的方法或者實(shí)戰(zhàn)經(jīng)驗(yàn),堅(jiān)定的應(yīng)用主義者。第一次發(fā)文,先來個(gè)近期遇到的小交互。
一個(gè)小程序密碼框的制作,有些前端會(huì)去選擇一些開源插件,其實(shí)實(shí)現(xiàn)起來并不復(fù)雜,話不多說上截圖。
本次交互是基于小程序?qū)崿F(xiàn),其實(shí)無論是vue,angular,react或者原生H5都是同理,內(nèi)置方法不一樣而已
步驟一:
div|view模擬六個(gè)密碼框,同時(shí)內(nèi)置長(zhǎng)寬為0的輸入框,點(diǎn)擊密碼框時(shí)模擬觸發(fā)輸入框的focus事件
<view class="relative pasWapw" bindtap="pasWapclick">
<view class="pasWap">
<view wx:for="{tlfzlhx}" class="d font3">{{item}}</view>
</view>
<input focus="{{focus}}" value="{{password}}" bindinput="inputchange" maxlength='6' style="width: 0; height: 0" type="password"></input>
</view>
div|view模擬六個(gè)密碼框,同時(shí)內(nèi)置長(zhǎng)寬為0的輸入框,點(diǎn)擊密碼框時(shí)模擬觸發(fā)輸入框的focus事件
步驟二
點(diǎn)擊密碼框模擬輸入框的focus事件
點(diǎn)擊密碼框模擬輸入框的focus事件
步驟三
監(jiān)聽輸入框的值改變,動(dòng)態(tài)修改密碼框內(nèi)的html
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。