欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

微信支付之H5支付

程序员文章站 2022-03-25 18:00:32
HoJe男孩子你要加油阿 前言准备材料H5支付请求的参数返回结果统一下单回调接口用到的工具类886 . 前言 大家好,分享是快乐的,也见证了个人成长历程,文章大多都是工作经验总结以及平时学习积累,基于自身认知不足之处在所难免,也请大家指正,共同进步。此次微信支付也是我刚入这个行业做的第一个功能,在这 ......

hoje
男孩子你要加油阿

h5支付请求的参数

. 前言

微信支付之H5支付

大家好,分享是快乐的,也见证了个人成长历程,文章大多都是工作经验总结以及平时学习积累,基于自身认知不足之处在所难免,也请大家指正,共同进步。此次微信支付也是我刚入这个行业做的第一个功能,在这里做总结以及分享给需要的人.
微信支付之二维码支付(总结已经更新)
微信支付之jsapi支付(总结还没有更新呢)
微信支付之退款(总结还没有更新呢)
微信支付之查询订单(总结还没有更新呢)

. 准备材料

首先你得看微信支付的h5开发文档
h5开发文档
至于如何申请账号,那是公司层面的操作,这里我不清楚,也不做相关的介绍了,我只清楚拿到账号之后的操作。


然后就是必要的配置了

微信支付之H5支付

公众平台的appid:基本配置-公众号开发信息-开发者id(appid)
签名密钥:基本配置-公众号开发信息-开发者密码(appsecret),自己生成,保证唯一即可,生成之后妥善保存,二次不可见
商户号:账户中心-账户设置-商户信息-商户号
微信支付证书:账户中心-->账户设置-->api安全-->证书下载
这个是涉及到退款业务,必须使用证书(以后有用的)

再然后就是微信的sdk

微信支付之H5支付

. h5支付请求的参数

开发文档你们应该都看熟了吧,让我们一起看看需要哪些参数吧.

微信支付之H5支付

微信支付之H5支付

微信支付之H5支付

一共有十一个参数,知道需要哪些参数了,获取到传给微信就可以召唤神龙了.
下面是参数打包好的举例.
微信支付之H5支付

. 返回结果

知道参数了让我们看看返回的结果

微信支付之H5支付

微信支付之H5支付

微信支付之H5支付

介绍的很详细,我就不多说了.

. 统一下单

首页你得有统一下单是url:https://api.mch.weixin.qq.com/pay/unifiedorder
让我们一起召唤神龙吧.
代码如下

 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 谢谢!!!

. 回调接口

微信支付之H5支付

我踩的坑阿!
注意:
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(null, new 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

又到说再见的时候了,好了今天就到这里了.拜拜

微信支付之H5支付