1. 程式人生 > >微信掃碼支付開發

微信掃碼支付開發

一.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>元