微信支付之H5支付

HoJe
男孩子你要加油阿php

前言準備材料H5支付請求的參數返回結果統一下單回調接口用到的工具類886html

. 前言


你們好,分享是快樂的,也見證了我的成長曆程,文章大多都是工做經驗總結以及平時學習積累,基於自身認知不足之處在所不免,也請你們指正,共同進步。這次微信支付也是我剛入這個行業作的第一個功能,在這裏作總結以及分享給須要的人.
微信支付之二維碼支付(總結已經更新)
微信支付之JSAPI支付(總結尚未更新呢)
微信支付之退款(總結尚未更新呢)
微信支付之查詢訂單(總結尚未更新呢)

. 準備材料

首先你得看微信支付的H5開發文檔
H5開發文檔
至於如何申請帳號,那是公司層面的操做,這裏我不清楚,也不作相關的介紹了,我只清楚拿到帳號以後的操做。
微信服務公衆平臺
微信商戶平臺
而後就是必要的配置了
java


公衆平臺的appid:基本配置-公衆號開發信息-開發者ID(AppID)
簽名密鑰:基本配置-公衆號開發信息-開發者密碼(AppSecret),本身生成,保證惟一便可,生成以後妥善保存,二次不可見
商戶號:帳戶中心-帳戶設置-商戶信息-商戶號
微信支付證書:帳戶中心-->帳戶設置-->API安全-->證書下載
這個是涉及到退款業務,必須使用證書(之後有用的)

再而後就是微信的SDK
web

. H5支付請求的參數

開發文檔大家應該都看熟了吧,讓咱們一塊兒看看須要哪些參數吧.
數據庫




一共有十一個參數,知道須要哪些參數了,獲取到傳給微信就能夠召喚神龍了.
下面是參數打包好的舉例.

. 返回結果

知道參數了讓咱們看看返回的結果
json




介紹的很詳細,我就很少說了.

. 統一下單

首頁你得有統一下單是url:https://api.mch.weixin.qq.com/pay/unifiedorder
讓咱們一塊兒召喚神龍吧.
代碼以下api

 1@RequestMapping(value = "/h5Pay")
2    @ResponseBody
3    public Object h5Pay(HttpServletRequest request,HttpServletResponse response,String orderNumber) {
4            PayOrderDO payOrderDO = this.payOrderService.getOrder(orderNumber);//訂單信息
5            logger.info("訂單號:{}發起H5支付",product.getOutTradeNo());
6            String  mweb_url = "";
7            String ip= "127.0.0.1";//IPUtils.getIpAddr(request); 上線獲取Ip
8            Map<String, String> request_data = new HashMap<>();
9            try {
10                Map<String, String> data = new HashMap<String, String>();
11                data.put("appid", WxConstants.APP_ID);//公衆帳號ID
12                data.put("mch_id",WxConstants.PARTNER);//商戶號
13                data.put("body""測試");//商品詳情
14                data.put("out_trade_no",orderNumber);//訂單號
15                data.put("nonce_str", UuidUtil.get32UUID());//32位字符
16                //轉換微信中存在最小計算單位是分的問題
17                BigDecimal payMoney = payOrderDO.getPayMoney();
18                BigDecimal bigDecimal = new BigDecimal(100);
19                BigDecimal amount = payMoney.multiply(bigDecimal).setScale(0, BigDecimal.ROUND_DOWN);
20                data.put("total_fee", String.valueOf(amount));//總金額
21                data.put("spbill_create_ip", ip);//用戶終端IP
22                data.put("trade_type""MWEB");  // H5支付的交易類型爲MWEB
23                data.put("notify_url", WxConstants.NOTIFY_URL);//通知地址 
24                data.put("scene_info""{\"h5_info\": {\"type\":\"Wap\",\"wap_url\": \"https://xxx.com\",\"wap_name\": \"打賞給HoJe\"}}");
25                String sign = createSign(data, WxConstants.PARTNER_KEY, WxConstants.CHARSET);
26                //調用生成簽名的方法,用以Map集合中的相關參數生成簽名
27                data.put("sign", sign);//簽名
28                String xml = WXPayUtil.generateSignedXml(data, WxConstants.PARTNER_KEY);//轉Xml格式 微信SDK自帶
29
30                System.out.println("request - XML:" + xml);
31
32                String resultXML = HttpsClientUtil.doPost(WxConstants.BAUSE_URL, xml);//發送post請求  如請求成功返回的數據是xml格式的
33
34                System.out.println("result - XML:" + resultXML);
35                Map<String, String> map = xmlToMap(resultXML);//xml轉map 微信SDK自帶
36                String returnCode = map.get("return_code");
37                if("SUCCESS".equals(returnCode)){
38                    String resultCode = map.get("result_code");
39                    if("SUCCESS".equals(resultCode)){
40                        logger.info("訂單號:{}發起H5支付成功",product.getOutTradeNo());
41                        mweb_url = (String) map.get("mweb_url");
42                    }else{
43                        String errCodeDes = map.get("err_code_des");
44                        logger.info("訂單號:{}發起H5支付(系統)失敗:{}",product.getOutTradeNo(),errCodeDes);
45                    }
46                }else{
47                    String returnMsg = map.get("return_msg");
48                    logger.info("(訂單號:{}發起H5支付(通訊)失敗:{}",product.getOutTradeNo(),returnMsg);
49                }
50            } catch (Exception e) {
51                logger.error("訂單號:{}發起H5支付失敗(系統異常))",product.getOutTradeNo(),e);
52            }
53        return mweb_url;
54    }

本人本身寫的也可能不是很完美歡迎大家指出 送上更完美的demo 謝謝!!!安全

. 回調接口


我踩的坑阿!
注意:
1丶回調url必須得填寫正確要否則微信訪問不到會一直調用,就是支付成功後微信異步通知咱們的服務器地址加項目加路徑.
2丶必定要驗證簽名,要否則微信不知道是那個商戶的會以爲不合法.
3丶給微信的數據必定是xml格式的,要否則微信解析不到就會一直調用(這就是微信比支付寶坑的地方)
話很少說上代碼

 1/**
2     * 微信支付回調函數
3     * 支付成功後微信服務器會調用此方法,修改數據庫訂單狀態
4     */

5    @RequestMapping(value = "/notify")
6    public void wxPayCallBack(HttpServletRequest request, HttpServletResponse response) {
7        try {
8            InputStream inStream = request.getInputStream();
9            ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
10            byte[] buffer = new byte[1024];
11            int len = 0;
12            while ((len = inStream.read(buffer)) != -1) {
13                outSteam.write(buffer, 0, len);
14            }
15            outSteam.close();
16            inStream.close();
17            String result = new String(outSteam.toByteArray(), WxConstants.CHARSET);
18            Map<String, String> map = xmlToMap(result);//xml轉map 微信SDK自帶
19            // 判斷簽名是否正確 微信SDK自帶的方法
20            if (WXPayUtil.isSignatureValid(map, WxConstants.PARTNER_KEY)) {
21                  logger.info("微信支付成功回調");
22                // ------------------------------
23                // 處理業務開始
24                // ------------------------------
25                String resXml = "";
26                if ("SUCCESS".equals((String) map.get("result_code"))) {
27                    // 這裏是支付成功
28                    String orderNo = (String) map.get("out_trade_no");
29                     logger.info("微信訂單號{}付款成功",orderNo);
30                    //這裏 根據實際業務場景 作相應的操做
31                    // 通知微信.異步確認成功.必寫.否則會一直通知後臺.八次以後就認爲交易失敗了.
32                    resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
33                } else {
34                    logger.info("支付失敗,錯誤信息:{}",packageParams.get("err_code"));
35                    resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[報文爲空]]></return_msg>" + "</xml> ";
36                }
37                // ------------------------------
38                // 處理業務完畢
39                // ------------------------------
40                BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
41                out.write(resXml.getBytes());
42                out.flush();
43                out.close();
44            } else {
45                System.out.println("通知簽名驗證失敗");
46                logger.info("通知簽名驗證失敗");
47            }
48
49
50        } catch (Exception e) {
51           e.printStackTrace();
52        }
53
54    }

其實你弄懂了,微信支付也沒有那麼難的.
我看過一個博客的簽名分享給你們:再牛逼的技術也抵擋不住你傻逼式的堅持.服務器

. 用到的工具類

1丶32位字符串微信

1public class UuidUtil {
2
3    public static String get32UUID() {
4        String uuid = UUID.randomUUID().toString().trim().replaceAll("-""");
5        return uuid;
6    }
7}

2丶獲取用戶的終端ip

 1/**
2 * IP地址
3 *
4 * @author HoJe
5 */

6public class IPUtils {
7    private static Logger logger = LoggerFactory.getLogger(IPUtils.class);
8
9    /**
10     * 獲取IP地址
11     *
12     * 使用Nginx等反向代理軟件, 則不能經過request.getRemoteAddr()獲取IP地址
13     * 若是使用了多級反向代理的話,X-Forwarded-For的值並不止一個,而是一串IP地址,X-Forwarded-For中第一個非unknown的有效IP字符串,則爲真實IP地址
14     */

15    public static String getIpAddr(HttpServletRequest request) {
16        String ip = null;
17        try {
18            ip = request.getHeader("x-forwarded-for");
19            if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
20                ip = request.getHeader("Proxy-Client-IP");
21            }
22            if (StringUtils.isEmpty(ip) || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
23                ip = request.getHeader("WL-Proxy-Client-IP");
24            }
25            if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
26                ip = request.getHeader("HTTP_CLIENT_IP");
27            }
28            if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
29                ip = request.getHeader("HTTP_X_FORWARDED_FOR");
30            }
31            if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
32                ip = request.getRemoteAddr();
33            }
34        } catch (Exception e) {
35            logger.error("IPUtils ERROR ", e);
36        }
37
38        //使用代理,則獲取第一個IP地址
39        if(StringUtils.isEmpty(ip) && ip.length() > 15) {
40            if(ip.indexOf(",") > 0) {
41                ip = ip.substring(0, ip.indexOf(","));
42            }
43        }
44
45        return ip;
46    }
47
48}

3丶生成簽名

 1/**
2     * 生成簽名
3     * 這個方法是從微信sdk裏copy過來的,本身也能夠寫,要注意生成簽名後UTF-8的轉換,要否則容易報簽名Body UTF-8錯誤
4     *
5     * @param data 待簽名數據
6     * @param key  API密鑰
7     * @param charset UTF-8
8     */

9    public static String createSign(final Map<String, String> data, String key, String charset) throws Exception {
10        return createSign(data, key, WXPayConstants.SignType.MD5, charset);
11    }
12
13    /**
14     * 生成簽名. 注意,若含有sign_type字段,必須和signType參數保持一致。
15     *
16     * @param data     待簽名數據
17     * @param key      API密鑰
18     * @param signType 簽名方式
19     * @param charset UTF-8
20     * @return 簽名
21     */

22    private static String createSign(final Map<String, String> data, String key, WXPayConstants.SignType signType, String charset) throws Exception {
23        //根據規則建立可排序的map集合
24        Set<String> keySet = data.keySet();
25        String[] keyArray = keySet.toArray(new String[keySet.size()]);
26        Arrays.sort(keyArray);
27        StringBuilder sb = new StringBuilder();
28        for (String k : keyArray) {
29            if (k.equals(WXPayConstants.FIELD_SIGN)) {
30                continue;
31            }
32            if (data.get(k).trim().length() > 0){
33                sb.append(k).append("=").append(data.get(k).trim()).append("&");
34            } // 參數值爲空,則不參與簽名
35        }
36        sb.append("key=").append(key);
37        //轉換UTF-8
38        String str = new String(sb.toString().getBytes(charset));
39        if (WXPayConstants.SignType.MD5.equals(signType)) {
40            return MD5(sb.toString()).toUpperCase();
41        } else if (WXPayConstants.SignType.HMACSHA256.equals(signType)) {
42            return HMACSHA256(sb.toString(), key);
43        } else {
44            throw new Exception(String.format("Invalid sign_type: %s", signType));
45        }
46    }
47}

4丶http請求的工具 實際沒有用到不少方法有的是退款須要用到的

 1public class HttpsClientUtil {
2    private static PoolingHttpClientConnectionManager connMgr;
3    private static RequestConfig requestConfig;
4    private static final int MAX_TIMEOUT = 7000;
5
6    static {
7        // 設置鏈接池
8        connMgr = new PoolingHttpClientConnectionManager();
9        // 設置鏈接池大小
10        connMgr.setMaxTotal(100);
11        connMgr.setDefaultMaxPerRoute(connMgr.getMaxTotal());
12
13        RequestConfig.Builder configBuilder = RequestConfig.custom();
14        // 設置鏈接超時
15        configBuilder.setConnectTimeout(MAX_TIMEOUT);
16        // 設置讀取超時
17        configBuilder.setSocketTimeout(MAX_TIMEOUT);
18        // 設置從鏈接池獲取鏈接實例的超時
19        configBuilder.setConnectionRequestTimeout(MAX_TIMEOUT);
20        // 在提交請求以前 測試鏈接是否可用
21        configBuilder.setStaleConnectionCheckEnabled(true);
22        requestConfig = configBuilder.build();
23    }
24
25    /**
26     * 發送 GET 請求(HTTP),不帶輸入數據
27     *
28     * @param url
29     * @return
30     */

31    public static String doGet(String url) {
32        return doGet(url, new HashMap<String, Object>());
33    }
34
35    /**
36     * 發送 GET 請求(HTTP),K-V形式
37     *
38     * @param url
39     * @param params
40     * @return
41     */

42    public static String doGet(String url, Map<String, Object> params) {
43        String apiUrl = url;
44        StringBuffer param = new StringBuffer();
45        int i = 0;
46        for (String key : params.keySet()) {
47            if (i == 0)
48                param.append("?");
49            else
50                param.append("&");
51            param.append(key).append("=").append(params.get(key));
52            i++;
53        }
54        apiUrl += param;
55        String result = null;
56        HttpClient httpclient = new DefaultHttpClient();
57        try {
58            HttpGet httpPost = new HttpGet(apiUrl);
59            HttpResponse response = httpclient.execute(httpPost);
60            int statusCode = response.getStatusLine().getStatusCode();
61
62            System.out.println("執行狀態碼 : " + statusCode);
63
64            HttpEntity entity = response.getEntity();
65            if (entity != null) {
66                InputStream instream = entity.getContent();
67                result = IOUtils.toString(instream);
68            }
69        } catch (IOException e) {
70            e.printStackTrace();
71        }
72        return result;
73    }
74
75    /**
76     * 發送 POST 請求(HTTP),不帶輸入數據
77     *
78     * @param apiUrl
79     * @return
80     */

81    public static String doPost(String apiUrl) {
82        return doPost(apiUrl, new HashMap<String, Object>());
83    }
84
85    /**
86     * 發送 POST 請求(HTTP),K-V形式
87     *
88     * @param apiUrl API接口URL
89     * @param params 參數map
90     * @return
91     */

92    public static String doPost(String apiUrl, Map<String, Object> params) {
93        CloseableHttpClient httpClient = HttpClients.createDefault();
94        String httpStr = null;
95        HttpPost httpPost = new HttpPost(apiUrl);
96        CloseableHttpResponse response = null;
97
98        try {
99            httpPost.setConfig(requestConfig);
100            List<NameValuePair> pairList = new ArrayList<>(params.size());
101            for (Map.Entry<String, Object> entry : params.entrySet()) {
102                NameValuePair pair = new BasicNameValuePair(entry.getKey(), entry
103                        .getValue().toString());
104                pairList.add(pair);
105            }
106            httpPost.setEntity(new UrlEncodedFormEntity(pairList, Charset.forName("UTF-8")));
107            response = httpClient.execute(httpPost);
108            System.out.println(response.toString());
109            HttpEntity entity = response.getEntity();
110            httpStr = EntityUtils.toString(entity, "UTF-8");
111        } catch (IOException e) {
112            e.printStackTrace();
113        } finally {
114            if (response != null) {
115                try {
116                    EntityUtils.consume(response.getEntity());
117                } catch (IOException e) {
118                    e.printStackTrace();
119                }
120            }
121        }
122        return httpStr;
123    }
124
125    /**
126     * 發送 POST 請求(HTTP),JSON形式
127     *
128     * @param apiUrl
129     * @param json   json對象
130     * @return
131     */

132    public static String doPost(String apiUrl, Object json) {
133        CloseableHttpClient httpClient = HttpClients.createDefault();
134        String httpStr = null;
135        HttpPost httpPost = new HttpPost(apiUrl);
136        CloseableHttpResponse response = null;
137
138        try {
139            httpPost.setConfig(requestConfig);
140            StringEntity stringEntity = new StringEntity(json.toString(), "UTF-8");//解決中文亂碼問題
141            stringEntity.setContentEncoding("UTF-8");
142            stringEntity.setContentType("application/json");
143            httpPost.setEntity(stringEntity);
144            response = httpClient.execute(httpPost);
145            HttpEntity entity = response.getEntity();
146            System.out.println(response.getStatusLine().getStatusCode());
147            httpStr = EntityUtils.toString(entity, "UTF-8");
148        } catch (IOException e) {
149            e.printStackTrace();
150        } finally {
151            if (response != null) {
152                try {
153                    EntityUtils.consume(response.getEntity());
154                } catch (IOException e) {
155                    e.printStackTrace();
156                }
157            }
158        }
159        return httpStr;
160    }
161
162    /**
163     * 發送 SSL POST 請求(HTTPS),K-V形式
164     *
165     * @param apiUrl API接口URL
166     * @param params 參數map
167     * @return
168     */

169    public static String doPostSSL(String apiUrl, Map<String, Object> params) {
170        CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(createSSLConnSocketFactory()).setConnectionManager(connMgr).setDefaultRequestConfig(requestConfig).build();
171        HttpPost httpPost = new HttpPost(apiUrl);
172        CloseableHttpResponse response = null;
173        String httpStr = null;
174
175        try {
176            httpPost.setConfig(requestConfig);
177            List<NameValuePair> pairList = new ArrayList<NameValuePair>(params.size());
178            for (Map.Entry<String, Object> entry : params.entrySet()) {
179                NameValuePair pair = new BasicNameValuePair(entry.getKey(), entry
180                        .getValue().toString());
181                pairList.add(pair);
182            }
183            httpPost.setEntity(new UrlEncodedFormEntity(pairList, Charset.forName("utf-8")));
184            response = httpClient.execute(httpPost);
185            int statusCode = response.getStatusLine().getStatusCode();
186            if (statusCode != HttpStatus.SC_OK) {
187                return null;
188            }
189            HttpEntity entity = response.getEntity();
190            if (entity == null) {
191                return null;
192            }
193            httpStr = EntityUtils.toString(entity, "utf-8");
194        } catch (Exception e) {
195            e.printStackTrace();
196        } finally {
197            if (response != null) {
198                try {
199                    EntityUtils.consume(response.getEntity());
200                } catch (IOException e) {
201                    e.printStackTrace();
202                }
203            }
204        }
205        return httpStr;
206    }
207
208    /**
209     * 發送 SSL POST 請求(HTTPS),JSON形式
210     *
211     * @param apiUrl API接口URL
212     * @param json   JSON對象
213     * @return
214     */

215    public static String doPostSSL(String apiUrl, Object json) {
216        CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(createSSLConnSocketFactory()).setConnectionManager(connMgr).setDefaultRequestConfig(requestConfig).build();
217        HttpPost httpPost = new HttpPost(apiUrl);
218        CloseableHttpResponse response = null;
219        String httpStr = null;
220
221        try {
222            httpPost.setConfig(requestConfig);
223            StringEntity stringEntity = new StringEntity(json.toString(), "UTF-8");//解決中文亂碼問題
224            stringEntity.setContentEncoding("UTF-8");
225            stringEntity.setContentType("application/json");
226            httpPost.setEntity(stringEntity);
227            response = httpClient.execute(httpPost);
228            int statusCode = response.getStatusLine().getStatusCode();
229            if (statusCode != HttpStatus.SC_OK) {
230                return null;
231            }
232            HttpEntity entity = response.getEntity();
233            if (entity == null) {
234                return null;
235            }
236            httpStr = EntityUtils.toString(entity, "utf-8");
237        } catch (Exception e) {
238            e.printStackTrace();
239        } finally {
240            if (response != null) {
241                try {
242                    EntityUtils.consume(response.getEntity());
243                } catch (IOException e) {
244                    e.printStackTrace();
245                }
246            }
247        }
248        return httpStr;
249    }
250
251    /**
252     * 建立SSL安全鏈接
253     *
254     * @return
255     */

256    private static SSLConnectionSocketFactory createSSLConnSocketFactory() {
257        SSLConnectionSocketFactory sslsf = null;
258        try {
259            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(nullnew TrustStrategy() {
260
261                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
262                    return true;
263                }
264            }).build();
265            sslsf = new SSLConnectionSocketFactory(sslContext, new X509HostnameVerifier() {
266
267                @Override
268                public boolean verify(String arg0, SSLSession arg1) {
269                    return true;
270                }
271
272                @Override
273                public void verify(String host, SSLSocket ssl) throws IOException {
274                }
275
276                @Override
277                public void verify(String host, X509Certificate cert) throws SSLException {
278                }
279
280                @Override
281                public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException {
282                }
283            });
284        } catch (GeneralSecurityException e) {
285            e.printStackTrace();
286        }
287        return sslsf;
288    }
289}

好了效果就不展現了,有什麼問題能夠提出噢.
一塊兒學習一塊兒加油.

886

又到說再見的時候了,好了今天就到這裏了.拜拜

相關文章
相關標籤/搜索