Java SpringMVC实现PC端网页微信扫码支付完整版

一:前期微信支付扫盲知识

前提条件是已经有申请了微信支付功能的公众号,然后我们需要得到公众号APPID和微信商户号,这个分别在微信公众号和微信支付商家平台上面可以发现。其实在你申请成功支付功能之后,微信会通过邮件把Mail转给你的,有了这些信息之后,我们就可以去微信支付服务支持页面:https://pay.weixin.qq.com/service_provider/index.shtml

打开这个页面,点击右上方的链接【开发文档】会进入到API文档说明页面,看起来如下

选择红色圆圈的扫码支付就是我们要做接入方式,鼠标移动到上面会提示你去查看开发文档,如果这个都不知道怎么查看,可以洗洗睡了,你真的不合适做程序员,地址如下:

https://pay.weixin.qq.com/wiki/doc/api/native.PHP?chapter=6_1在浏览器中打开之后会看到


 

我们重点要关注和阅读的内容我已经用红色椭圆标注好了,首先阅读【接口规则】里面的协议规范,开玩笑这个都不读你就想做微信支付,这个就好比你要去泡妞,得先收集点基本背景信息,了解对方特点,不然下面还怎么沟通。事实证明只有会泡妞得程序员才是好销售。跑题了我们接下来要看一下【场景介绍】中的案例与规范,只看一下记得一定要微信支付的LOGO下载下来,是为了最后放到我们自己的扫码支付网页上,这样看上去比较专业一点。之后重点关注【模式二】

我们这里就是要采用模式二的方式实现PC端页面扫码支付功能。

微信官方对模式二的解释是这样的“商户后台系统先调用微信支付的统一下单接口,微信后台系统返回链接参数code_url,商户后台系统将code_url值生成二维码图片,用户使用微信客户端扫码后发起支付。注意:code_url有效期为2小时,过期后扫码不能再发起支付”。看明白了吧就是我们首先要调用微信提供统一下单接口,得到一个关键信息code_url(至于这个code_url是什么鬼,我也不知道),然后我们通过自己的程序把这个URL生成一个二维码,生成二维码我这里用了Google的zxing库。然后把这个二维码显示在你的PC端网页上就行啦。这样终端用户一扫码就支付啦,支付就完成啦,看到这里你肯定很激动,发现微信支付如此简单,等等还有个事情我们还不知道,客户知道付钱了,我们服务器端还不知道呢,以微信开发人员的智商他们早就想到这个问题了,所以让你在调用统一下单接口的时候其中有个必填的参数就是回调URL,就是如果客户端付款成功之后微信会通过这个URL向我们自己的服务器提交一些数据,然后我们后台解析这些数据,完成我们自己操作。这样我们才知道客户是否真的已经通过微信付款了。这样整个流程才结束,这个就是模式二。微信用一个时序图示这样表示这个过程的。


 

表达起来比较复杂,看上去比较吃力,总结一下其实我们服务器该做的事情就如下件:

1. 通过统一下单接口传入正确的参数(当然要包括我们的回调URL)与签名验证,从返回数据中得到code_url的对应数据

2. 根据code_url的数据我们自己生成一个二维码图片,显示在浏览器网页上

3. 在回调的URL中添加我们自己业务逻辑处理。

至此扫盲结束了,你终于知道扫码支付什么个什么样的流程了,下面我们就一起来扒扒它的相关API使用,做好每步处理。

二:开发过程

在开发代码之前,请先准备几件事情。

1. 添加ZXing的maven依赖

2. 添加jdom的maven依赖

3.下载Java版本SDK演示程序,地址在这里

https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=11_1

我们需要MD5Util.java和XMLUtil.java两个文件

4. 我们使用HttpClient版本是4.5.1,记得添加Maven依赖

上面准备工作做好以后,继续往下看:

首先我们要调用微信的统一下单接口,我们点击【API列表】中的统一下单会看到这样页面:


 

以本人调用实际情况为例,如下的参数是必须要有的,为了大家的方便我已经把它变成一个POJO的对象, 代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
public class UnifiedorderDto implements WeiXinConstants {
 
     private String appid;
     private String body;
     private String device_info;
     private String mch_id;
     private String nonce_str;
     private String notify_url;
     private String openId;
     private String out_trade_no;
     private String spbill_create_ip;
     private int total_fee;
     private String trade_type;
     private String product_id;
     private String sign;
     
     public UnifiedorderDto() {
         this .appid = APPID;
         this .mch_id = WXPAYMENTACCOUNT;
         this .device_info = DEVICE_INFO_WEB;
         this .notify_url = CALLBACK_URL;
         this .trade_type = TRADE_TYPE_NATIVE;
     }
 
     public String getAppid() {
         return appid;
     }
 
     public void setAppid(String appid) {
         this .appid = appid;
     }
 
     public String getBody() {
         return body;
     }
 
     public void setBody(String body) {
         this .body = body;
     }
 
     public String getDevice_info() {
         return device_info;
     }
 
     public void setDevice_info(String device_info) {
         this .device_info = device_info;
     }
 
     public String getMch_id() {
         return mch_id;
     }
 
     public void setMch_id(String mch_id) {
         this .mch_id = mch_id;
     }
 
     public String getNonce_str() {
         return nonce_str;
     }
 
     public void setNonce_str(String nonce_str) {
         this .nonce_str = nonce_str;
     }
 
     public String getNotify_url() {
         return notify_url;
     }
 
     public void setNotify_url(String notify_url) {
         this .notify_url = notify_url;
     }
 
     public String getOpenId() {
         return openId;
     }
 
     public void setOpenId(String openId) {
         this .openId = openId;
     }
 
     public String getOut_trade_no() {
         return out_trade_no;
     }
 
     public void setOut_trade_no(String out_trade_no) {
         this .out_trade_no = out_trade_no;
     }
 
     public String getSpbill_create_ip() {
         return spbill_create_ip;
     }
 
     public void setSpbill_create_ip(String spbill_create_ip) {
         this .spbill_create_ip = spbill_create_ip;
     }
 
     public int getTotal_fee() {
         return total_fee;
     }
 
     public void setTotal_fee( int total_fee) {
         this .total_fee = total_fee;
     }
 
     public String getTrade_type() {
         return trade_type;
     }
 
     public void setTrade_type(String trade_type) {
         this .trade_type = trade_type;
     }
 
     public String getSign() {
         return sign;
     }
 
     public void setSign(String sign) {
         this .sign = sign;
     }
 
     public String getProduct_id() {
         return product_id;
     }
 
     public void setProduct_id(String product_id) {
         this .product_id = product_id;
     }
     public String generateXMLContent() {
         String xml = "<xml>" +
            "" + this .appid + "</appid>" +
            "" + this .body + "" +
            "<device_info>WEB</device_info>" +
            "<mch_id>" + this .mch_id + "</mch_id>" +
            "<nonce_str>" + this .nonce_str + "</nonce_str>" +
            "<notify_url>" + this .notify_url + "</notify_url>" +
            "<out_trade_no>" + this .out_trade_no + "</out_trade_no>" +
            "<product_id>" + this .product_id + "</product_id>" +
            "<spbill_create_ip>" + this .spbill_create_ip+ "</spbill_create_ip>" +
            "<total_fee>" + String.valueOf( this .total_fee) + "</total_fee>" +
            "<trade_type>" + this .trade_type + "</trade_type>" +
            "<sign>" + this .sign + "</sign>" +
         "</xml>" ;
         return xml;
     }
     
     public String makeSign() {
         String content = "appid=" + this .appid +
                    "&body=" + this .body +
                    "&device_info=WEB" +
                    "&mch_id=" + this .mch_id +
                    "&nonce_str=" + this .nonce_str +
                    "?ify_url=" + this .notify_url +
                    "&out_trade_no=" + this .out_trade_no +
                    "&product_id=" + this .product_id +
                    "&spbill_create_ip=" + this .spbill_create_ip+
                    "&total_fee=" + String.valueOf( this .total_fee) +
                    "&trade_type=" + this .trade_type;
         content = content + "&key=" + WeiXinConstants.MD5_API_KEY;
         String esignature = WeiXinPaymentUtil.MD5Encode(content, "utf-8" );
         return esignature.toUpperCase();
     }
     
}

其中各个成员变量的解释可以参见【统一下单接口】的说明即可。

有这个之后我们就要要设置的内容填写进去,去调用该接口得到返回数据,从中拿到code_url的数据然后据此生成一个二维图片,把图片的地址返回给PC端网页,然后它就会显示出来,这里要特别说明一下,我们自己PC端网页在点击微信支付的时候就会通过ajax方式调用我们自己后台的SpringMVC Controller然后在Controller的对应方法中通过HTTPClient完成对微信统一下单接口调用解析返回的XML数据得到code_url的值,生成二维码之后返回给前台网页。Controller中实现的代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
Map<string,object> result= new HashMap<string,object>();
         UnifiedorderDto dto = new UnifiedorderDto();
         if (cash == null || "" .equals(cash)) {
             result.put( "error" , "cash could not be zero" );
             return result;
         }
         int totalfee = 100 *Integer.parseInt(cash);
         logger.info( "total recharge cash : " + totalfee);
         dto.setProduct_id(String.valueOf(System.currentTimeMillis()));
         dto.setBody( "repair" );
         dto.setNonce_str(String.valueOf(System.nanoTime()));
         LoginInfo loginInfo = LoginInfoUtil.getLoginInfo();
         // 通过我们后台订单号+UUID为身份识别标志
         dto.setOut_trade_no( "你的订单号+关键信息,微信回调之后传回,你可以验证" );
         dto.setTotal_fee(totalfee);
         dto.setSpbill_create_ip( "127.0.0.1" );
         // generate signature
         dto.setSign(dto.makeSign());
         logger.info( "sign : " + dto.makeSign());
         logger.info( "xml content : " + dto.generateXMLContent());
         try {
             HttpClient httpClient = HttpClientBuilder.create().build();
             HttpPost post = new HttpPost(WeiXinConstants.UNIFIEDORDER_URL);
             post.addHeader( "Content-Type" , "text/xml; charset=UTF-8" );
             StringEntity xmlEntity = new StringEntity(dto.generateXMLContent(), ContentType.TEXT_XML);
             post.setEntity(xmlEntity);
             HttpResponse httpResponse = httpClient.execute(post);
             String responseXML = EntityUtils.toString(httpResponse.getEntity(), "UTF-8" );
             logger.info( "response xml content : " + responseXML);
             // parse CODE_URL CONTENT
             Map<string, string= "" > resultMap = (Map<string, string= "" >)XMLUtil.doXMLParse(responseXML);
             logger.info( "response code_url : " + resultMap.get( "code_url" ));
             String codeurl = resultMap.get( "code_url" );
             if (codeurl != null && ! "" .equals(codeurl)) {
                 String imageurl = generateQrcode(codeurl);
                 result.put( "QRIMAGE" , imageurl);
             }
             post.releaseConnection();
         } catch (Exception e) {
             e.printStackTrace();
         }
         result.put( "success" , "1" );
         return result;</string,></string,></string,object></string,object>

生成二维码的代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private String generateQrcode(String codeurl) {
     File foldler = new File(basePath + "qrcode" );
     if (!foldler.exists()) {
         foldler.mkdirs();
     }
     
     String f_name = UUIDUtil.uuid() + ".png" ;
     try {
         File f = new File(basePath + "qrcode" , f_name);
         FileOutputStream fio = new FileOutputStream(f);
         MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
         Map hints = new HashMap();
         hints.put(EncodeHintType.CHARACTER_SET, "UTF-8" ); //设置字符集编码类型
         BitMatrix bitMatrix = null ;
         bitMatrix = multiFormatWriter.encode(codeurl, BarcodeFormat.QR_CODE, 300 , 300 ,hints);
         BufferedImage image = toBufferedImage(bitMatrix);
         //输出二维码图片流
         ImageIO.write(image, "png" , fio);
         return ( "qrcode/" + f_name);
     } catch (Exception e1) {
         e1.printStackTrace();
         return null ;
     }    
}
此时如何客户端微信扫码之后,微信就会通过回调我们制定URL返回数据给我们。在回调方法中完成我们自己的处理,这里要特别注意的是你的回调接口必须通过HTTP POST方法实现,否则无法接受到XML数据。回调处理的代码如下:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@RequestMapping (value = "/your_callback_url" , method = RequestMethod.POST)
@ResponseBody
public void finishPayment(HttpServletRequest request, HttpServletResponse response) {
     try {
         logger.info( "start to callback from weixin server: " + request.getRemoteHost());
         Map<string, string= "" > resultMap = new HashMap<string, string= "" >();
         InputStream inputStream = request.getInputStream();
         // 读取输入流
         SAXBuilder saxBuilder= new SAXBuilder();
         Document document = saxBuilder.build(inputStream);
         // 得到xml根元素
         Element root = document.getRootElement();
         // 得到根元素的所有子节点
         List list = root.getChildren();
         Iterator it = list.iterator();
         while (it.hasNext()) {
             Element e = (Element) it.next();
             String k = e.getName();
             String v = "" ;
             List children = e.getChildren();
             if (children.isEmpty()) {
                 v = e.getTextNormalize();
             } else {
                 v = XMLUtil.getChildrenText(children);
             }
             resultMap.put(k, v);
         }
         
         // 验证签名!!!
         /*
         String[] keys = resultMap.keySet().toArray( new String[ 0 ]);
         Arrays.sort(keys);
         String kvparams = "" ;
         for ( int i= 0 ; i<keys.length; i++)= "" {= "" if (keys[i].equals( "esign" ))= "" continue ;= "" }= "" 签名算法= "" if (i= "=" 0 )= "" kvparams= "" += "(keys[i]" "=" + resultMap.get(keys[i]));
             } else {
                 kvparams += ( " &" = "" keys[i]= "" &key=" + WeiXinConstants.MD5_API_KEY;
         String md5esign = WeiXinPaymentUtil.MD5Encode(esign, " utf-8" );= "" if (!md5esign.equals(resultmap.get( "sign" )))= "" return ;= "" }*= "" 关闭流= "" 释放资源= "" inputstream.close();= "" inputstream= "null;" string= "" returncode= "resultMap.get(" return_code ");" outtradeno= "resultMap.get(" out_trade_no ");" 以分为单位= "" int = "" nfee= "Integer.parseInt(resultMap.get(" total_fee "));" logger.info( "out=" " trade=" " no=" " :=" " outtradeno);=" " logger.info(" total_fee= "" nfee);= "" 业务处理流程= "" if ( "success" .equals(returncode))= "" todo:= "" your= "" business= "" process= "" add= "" here= "" response.getwriter().print(xmlutil.getretresultxml(resultmap.get( "return_code" ),= "" resultmap.get( "return_code" )));= "" else = "" resultmap.get( "return_msg" )));= "" catch (ioexception= "" ioe)= "" ioe.printstacktrace();= "" catch = "" (jdomexception= "" e1)= "" e1.printstacktrace();= "" }
微信官方java版demo用到的xmlutil和md5util的两个类记得拿过来改一下,演示代码可以在它的官方演示页面找到,相关maven依赖如下:

?
1
2
3
4
5
6
7
8
9
10
<dependency>
     <groupid>jdom</groupid>
     jdom</artifactid>
     <version> 1.1 </version>
</dependency>
<dependency>
     <groupid>com.google.zxing</groupid>
     core</artifactid>
     <version> 3.3 . 0 </version>
</dependency>

最后要特别注意的是关于签名,签名生成MD5的类我是从微信官网直接下载Java版Demo程序获取的,建议你也是,因为这个是确保MD5签名是一致的最佳选择。具体的生成签名的算法可以查看微信官方文档,这里也强烈建议大家一定要官方API说明,你开发中所遇到各种问题90%都是因为不看官方文档,而是轻信某人博客!这个才是我写这篇文章的真正目的和用意,根据官方文档,用我的Java代码实现,微信PC端网页扫码支付必定在你的WEB应用中飞起来。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Java SpringMVC 可以通过以下步骤实现登录功能: 1. 创建一个登录页面,包含用户名和密码输入框以及登录按钮。 2. 在 SpringMVC 中创建一个控制器,用于处理登录请求。 3. 在控制器中,使用 @RequestMapping 注解来映射登录请求的 URL。 4. 在控制器中,使用 @ModelAttribute 注解来获取登录页面提交的用户名和密码。 5. 在控制器中,使用业务逻辑来验证用户名和密码是否正确。 6. 如果验证通过,将用户信息存储到 session 中,并跳转到登录成功页面。 7. 如果验证失败,返回登录页面,并提示用户输入的用户名或密码错误。 以上是 Java SpringMVC 实现登录的基本步骤,具体实现可以根据具体需求进行调整。 ### 回答2: Java SpringMVC 是一个基于Java 的MVC框架,它允许开发者通过使用注释来实现一个模块化的应用程序,以此来简化代码的开发,提高代码的可读性和可维护性,并且提高整个项目的开发效率。 实现登录功能,在Java SpringMVC框架下,需要遵循以下步骤: 1. 建立一个控制器 在控制器类中,我们可以定义一个处理器来处理用户请求,比如一个登录请求。在控制器的方法中,我们可以使用@RequestMapping 注解来处理请求,并返回一个视图名称或者一个json字符串。 @Controller public class LoginController { //登录请求处理 @RequestMapping(value="/login", method=RequestMethod.POST) public String login(@RequestParam("username") String userName,@RequestParam("password") String password,Model model) { //处理请求 if(userName.equals("admin") && password.equals("admin")) { //登录成功,返回主页视图名称 return "main"; } else { //登录失败,返回登陆页视图名称 model.addAttribute("fail", "用户名或密码错误"); return "login"; } } } 2. 创建一个视图 在视图中展示用户界面,以引导用户进行登录操作。在视图页面中,我们可以使用form标签来定义一个表单,用于接收用户的登录信息。 <form action="login" method="post" > <label>用户名:</label> <input type="text" name="username"><br/> <label>密码:</label> <input type="password" name="password"><br/> <input type="submit" value="登录" /> </form> 3. 配置SpringMVC 在配置文件中开启SpringMVC的注解驱动,同时配置视图解析器,并引入需要扫描注解的包路径。 <!--开启注解驱动--> <mvc:annotation-driven /> <!--视图解析器--> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/views/" /> <property name="suffix" value=".jsp" /> </bean> <!--扫描包路径--> <context:component-scan base-package="com.example.controller" /> 4. 运行 运行程序后,在浏览器中访问登录页面,输入正确的用户名和密码即可登录成功或失败。 综上所述,实现登录功能的步骤比较简单,请开发者参考上述步骤进行开发。需要注意的是,实际开发中可能会涉及到更加详细的实现及安全性问题,需要更加细致的控制,开发者需要具备一定的开发经验及相关的知识概念。 ### 回答3: Java Spring MVC 是一种用于开发 Web 应用程序的框架,它提供了一种模型视图控制(MVC)的架构来分离应用程序的组成部分。 Spring MVC 实现登录是指在用户访问 Web 应用程序时需要进行身份验证时,以编程方式验证用户身份并为其提供访问授权。此过程涉及许多不同的组成部分,其中包括 Spring Security 和 Controller 层。 要实现登录,需要在设计时确定验证的方式。可以使用基于表单的验证和基于 HTTP 认证的验证。基于表单的验证需要在表单中获取用户名和密码,然后在后台进行验证。基于 HTTP 认证的验证则需要将用户名和密码附加到 HTTP 请求的请求头中,并将它们发送到后台进行验证。 在 Spring MVC 中,可以使用 Spring Security 框架来处理登录和授权逻辑。 Spring Security 的主要功能是处理身份验证和授权。要使用 Spring Security 实现登录,需要在 Spring 配置文件中对其进行配置,并将其添加到应用程序中。 在 Controller 层中,可以使用 @RequestMapping 注释将 URL 映射到处理请求的方法上。在登录请求中,需要检查传递的用户名和密码是否与用户的实际用户名和密码匹配。如果匹配,则在服务器上创建会话以在用户访问其他页面时跟踪其状态。 如果不匹配,则将用户重定向到登录页面。 在进行身份验证时,还需要考虑安全性问题。必须确保在传递密码时使用安全协议,例如 HTTPS。还需要防止 SQL 注入攻击,可以使用预处理语句来防止此类攻击。 在总结方面,Java Spring MVC 实现登录的主要步骤包括:设计验证方式,配置 Spring Security,使用 @RequestMapping 注释将 URL 映射到相应的 Controller 方法,进行身份验证并创建会话,确保安全性。以上述步骤实现登录可以增强应用程序安全性,防止未授权访问和数据泄漏等问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值