微信掃碼支付開發
一.QR Code(二維碼)
二維條碼/二維碼(2-dimensional bar code)是用某種特定的幾何圖形按一定規律在平面(二維方向上)分佈的黑白相間的圖形記錄資料符號資訊的;在程式碼編制上巧妙地利用構成計算機內部邏輯基礎的“0”、“1”位元流的概念,用若干個與二進位制相對應的幾何形體來表示文字數值資訊。
二.維碼生成外掛qrious
qrious是一款基於HTML5 Canvas的純JS二維碼生成外掛。通過qrious.js可以快速生成各種二維碼,你可以控制二維碼的尺寸顏色,還可以將生成的二維碼進行Base64編碼。
下面的程式碼即可生成一張二維碼
<html> <head> <title>二維碼入門小demo</title> </head> <body> <img id="qrious"> <script src="qrious.min.js"></script> <script> var qr = new QRious({ element:document.getElementById('qrious'), size:250, level:'H', value:'http://www.baidu.cn' }); </script> </body> </html>
其中的JS外掛地址 https://neocotic.com/qrious/
qrious.js二維碼外掛的可用配置引數如下:
三.微信掃碼支付
1.微信掃碼支付申請
微信掃碼支付是商戶系統按微信支付協議生成支付二維碼,使用者再用微信“掃一掃”完成支付的模式。該模式適用於PC 網站支付、實體店單品或訂單支付、媒體廣告支付等場景。
具體步驟參考微信支付官網。
2.需求
1)在支付頁面上生成支付二維碼,並顯示訂單號和金額
2)使用者拿出手機,開啟微信掃描頁面上的二維碼,然後在微信中完成支付
2.實現思路
商戶後臺系統先呼叫微信支付的統一下單介面,微信後臺系統返回連結引數code_url,商戶後臺系統將code_url值生成二維碼圖片,使用者使用微信客戶端掃碼後發起支付。注意:code_url有效期為2小時,過期後掃碼不能再發起支付。
官方API 詳細說明了呼叫流程,並配有時序圖。
3.微信支付SDK
使用微信支付SDK,在maven工程中引入依賴
<dependency> <groupId>com.github.wxpay</groupId> <artifactId>wxpay-sdk</artifactId> <version>0.0.3</version> </dependency>
我們主要會用到微信支付SDK的以下功能:
1)獲取隨機字串
WXPayUtil.generateNonceStr()
2)MAP轉換為XML字串(自動添加簽名)
WXPayUtil.generateSignedXml(param, partnerkey)
3)XML字串轉換為MAP
WXPayUtil.xmlToMap(result)
4.HttpClient工具類
import java.io.IOException; import java.security.GeneralSecurityException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.text.ParseException; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLException; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import org.apache.http.Consts; import org.apache.http.HttpEntity; import org.apache.http.NameValuePair; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.SSLContextBuilder; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.conn.ssl.TrustStrategy; import org.apache.http.conn.ssl.X509HostnameVerifier; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; /** * http請求客戶端 * * @author Administrator * */ public class HttpClient { private String url; private Map<String, String> param; private int statusCode; private String content; private String xmlParam; private boolean isHttps; public boolean isHttps() { return isHttps; } public void setHttps(boolean isHttps) { this.isHttps = isHttps; } public String getXmlParam() { return xmlParam; } public void setXmlParam(String xmlParam) { this.xmlParam = xmlParam; } public HttpClient(String url, Map<String, String> param) { this.url = url; this.param = param; } public HttpClient(String url) { this.url = url; } public void setParameter(Map<String, String> map) { param = map; } public void addParameter(String key, String value) { if (param == null) param = new HashMap<String, String>(); param.put(key, value); } public void post() throws ClientProtocolException, IOException { HttpPost http = new HttpPost(url); setEntity(http); execute(http); } public void put() throws ClientProtocolException, IOException { HttpPut http = new HttpPut(url); setEntity(http); execute(http); } public void get() throws ClientProtocolException, IOException { if (param != null) { StringBuilder url = new StringBuilder(this.url); boolean isFirst = true; for (String key : param.keySet()) { if (isFirst) url.append("?"); else url.append("&"); url.append(key).append("=").append(param.get(key)); } this.url = url.toString(); } HttpGet http = new HttpGet(url); execute(http); } /** * set http post,put param */ private void setEntity(HttpEntityEnclosingRequestBase http) { if (param != null) { List<NameValuePair> nvps = new LinkedList<NameValuePair>(); for (String key : param.keySet()) nvps.add(new BasicNameValuePair(key, param.get(key))); // 引數 http.setEntity(new UrlEncodedFormEntity(nvps, Consts.UTF_8)); // 設定引數 } if (xmlParam != null) { http.setEntity(new StringEntity(xmlParam, Consts.UTF_8)); } } private void execute(HttpUriRequest http) throws ClientProtocolException, IOException { CloseableHttpClient httpClient = null; try { if (isHttps) { SSLContext sslContext = new SSLContextBuilder() .loadTrustMaterial(null, new TrustStrategy() { // 信任所有 public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { return true; } }).build(); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( sslContext); httpClient = HttpClients.custom().setSSLSocketFactory(sslsf) .build(); } else { httpClient = HttpClients.createDefault(); } CloseableHttpResponse response = httpClient.execute(http); try { if (response != null) { if (response.getStatusLine() != null) statusCode = response.getStatusLine().getStatusCode(); HttpEntity entity = response.getEntity(); // 響應內容 content = EntityUtils.toString(entity, Consts.UTF_8); } } finally { response.close(); } } catch (Exception e) { e.printStackTrace(); } finally { httpClient.close(); } } public int getStatusCode() { return statusCode; } public String getContent() throws ParseException, IOException { return content; } }View Code
HttpClient通俗的講就是模擬了瀏覽器的行為,如果我們需要在後端向某一地址提交資料獲取結果,就可以使用HttpClient。
HttpClient工具類使用的步驟:
HttpClient client=new HttpClient(請求的url地址); client.setHttps(true);//是否是https協議 client.setXmlParam(xmlParam);//傳送的xml資料 client.post();//執行post請求 String result = client.getContent(); //獲取結果
三.關鍵程式碼
1.加入工具類HttpClient.java ,並新增依賴
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency>
配置檔案weixinpay.properties
appid=
partner=
partnerkey=
notifyurl=
appid: 微信公眾賬號或開放平臺APP的唯一標識
partner:財付通平臺的商戶賬號
partnerkey:財付通平臺的商戶金鑰
notifyurl: 回撥地址
2.服務層程式碼
@Service public class WeixinPayServiceImpl implements WeixinPayService { @Value("${appid}") private String appid; @Value("${partner}") private String partner; @Value("${partnerkey}") private String partnerkey; /** * 生成二維碼 * @return */ public Map createNative(String out_trade_no,String total_fee){ //1.建立引數 Map<String,String> param=new HashMap();//建立引數 param.put("appid", appid);//公眾號 param.put("mch_id", partner);//商戶號 param.put("nonce_str", WXPayUtil.generateNonceStr());//隨機字串 param.put("body", "xiangmu");//商品描述 param.put("out_trade_no", out_trade_no);//商戶訂單號 param.put("total_fee",total_fee);//總金額(分) param.put("spbill_create_ip", "127.0.0.1");//IP param.put("notify_url", "http://baidu.com");//回撥地址(隨便寫) param.put("trade_type", "NATIVE");//交易型別 try { //2.生成要傳送的xml String xmlParam = WXPayUtil.generateSignedXml(param, partnerkey); System.out.println(xmlParam); HttpClient client=new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder"); client.setHttps(true); client.setXmlParam(xmlParam); client.post(); //3.獲得結果 String result = client.getContent(); System.out.println(result); Map<String, String> resultMap = WXPayUtil.xmlToMap(result); Map<String, String> map=new HashMap<>(); map.put("code_url", resultMap.get("code_url"));//支付地址 map.put("total_fee", total_fee);//總金額 map.put("out_trade_no",out_trade_no);//訂單號 return map; } catch (Exception e) { e.printStackTrace(); return new HashMap<>(); } } }View Code
注:簽名sign在SDK工具中自動生成
3.控制層
/** * 支付控制層 * @author Administrator * */ @RestController @RequestMapping("/pay") public class PayController { @Reference private WeixinPayService weixinPayService; /** * 生成二維碼 * @return */ @RequestMapping("/createNative") public Map createNative(){ IdWorker idworker=new IdWorker(); return weixinPayService.createNative(idworker.nextId()+"","1"); } }View Code
4.前端呼叫
app.service('payService',function($http){ //本地支付 this.createNative=function(){ return $http.get('pay/createNative.do'); } });View Code
app.controller('payController' ,function($scope ,payService){ //本地生成二維碼 $scope.createNative=function(){ payService.createNative().success( function(response){ $scope.money= (response.total_fee/100).toFixed(2) ; //金額 $scope.out_trade_no= response.out_trade_no;//訂單號 //二維碼 var qr = new QRious({ element:document.getElementById('qrious'), size:250, level:'H', value:response.code_url }); } ); } });
設定二維碼圖片的ID
<p class="red"></p> <div class="fl code"> <img id="qrious"> <div class="saosao"> <p>請使用微信掃一掃</p> <p>掃描二維碼支付</p> </div> </div>
顯示訂單號
訂單號:{{out_trade_no}}
顯示金額
<em class="orange money">¥{{money}}</em>元