基于SpringBoot实现的微信支付(JSAPI)

没看过官方文档先去简单看一下官方文档 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_4

wx支付前端官方文档  https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#4

1.先设置支付目录:登录微信支付商户平台(pay.weixin.qq.com)-->产品中心-->开发配置(我这里都是用的是公司设置好的)

2.设置授权域名,因为我这是测试所以通过小米球的方式注册了一个域名填写(免费)   http://ngrok.ciqiuwl.cn/

         ---------------------------------------    前面准备工作大概就是这样,下面开始实战   ---------------------------------------   

3.引入相关依赖

<dependencies>
        <!-- 微信支付需要的jar包(主要是用里面的工具类) -->
		<dependency>
			<groupId>com.github.wxpay</groupId>
			<artifactId>wxpay-sdk</artifactId>
			<version>0.0.3</version>
		</dependency>
        
        <!-- web模块 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		
		<!-- 导入配置文件处理器,配置文件进行绑定就会有提示 --> 
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-configuration-processor</artifactId>
			<optional>true</optional>
		</dependency>
		
		<!-- 热启动 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<optional>true</optional>
		</dependency>
		
		 <!-- json转换工具 -->
         <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.28</version>
        </dependency>
        
        <!-- thymeleaf模板引擎 -->
        <dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
</dependencies>

 

4.编写配置类(填写自己的)

#小程序ID
pay.appid=xx

#appId对应的接口密码
pay.secret=xx

#域名
pay.DomainNameUrl=xxx

#商户Id
pay.mch_id=xxx

#商户平台设置的密钥key
pay.key=xxx
package com.borui.util;


import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;



/**
   * 将配置文件中配置得每一个属性得值,映射到这个组件中
 *@ConfigurationProperties:告诉springboot将本类中得所有属性和配置文件中相关得配置进行绑定
 *prefix = "person" 将配置文件中那个下面得所有属性一一映射
 */
@Component
@PropertySource(value="classpath:pay.properties")
@ConfigurationProperties(prefix = "pay")
//@Configuration //@Configuration指明当前类是一个配置类,代替之前的spring配置文件
public class Pay {
	
	private String appid;
	
	private String secret;
		
	private String DomainNameUrl;
	
	private String mch_id;
	
	private String key;

	public String getAppid() {
		return appid;
	}
	public void setAppid(String appid) {
		this.appid = appid;
	}

	public String getSecret() {
		return secret;
	}

	public void setSecret(String secret) {
		this.secret = secret;
	}

	public String getDomainNameUrl() {
		return DomainNameUrl;
	}

	public void setDomainNameUrl(String domainNameUrl) {
		DomainNameUrl = domainNameUrl;
	}

	public String getMch_id() {
		return mch_id;
	}

	public void setMch_id(String mch_id) {
		this.mch_id = mch_id;
	}

	public String getKey() {
		return key;
	}

	public void setKey(String key) {
		this.key = key;
	}
}

5. https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1(统一下单官方文档)先看文档得知我们必须传的参数有哪些

6.得知openid是必须传的,所以在用户访问公众号的时候得到openid,而我是测试所以通过用户手动授权获取openid

  获取openid官方文档、先大概了解下             https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html

7.用户点击授权获取到openid

 <div th:if="${session.openid==null}" style="margin-left:200px;margin-top:50px">
	<a 
        style="width: 500px;height: 60px;font-size: 22px;line-height: 45px" 
        class="btn btn-primary"  
        <!-- 微信提供的地址访问就可以(我是采用前端访问) -->
        href="http://open.weixin.qq.com/connect/oauth2/authorize? 
        appid=填写自己的appid&

        <!-- 微信将code返回到这个controller路径(前面是我的域名) -->
        redirect_uri=http://test.ngrok2.xiaomiqiu.cn/wx/getOpenid& 

        <!--返回类型,请填写code-->
        response_type=code

        <!-- 应用授权作用域,snsapi_base 不弹出授权页面,直接跳转,只能获取用户openid 
                            snsapi_userinfo (弹出授权页面,可通过openid也能获取其信息 ) 
         -->
        &scope=snsapi_userinfo

        &state=STATE#wechat_redirect">

       点击授权
   </a>
</div>

 

@RequestMapping("/wx/getOpenid")
public ModelAndView getCode() throws Exception {
    //获取code
    String code = request.getParameter("code");
    //wx提供的地址
    String url = "https://api.weixin.qq.com/sns/oauth2/access_token? 
                  appid="+pay.getAppid()+
                  "&secret="+pay.getSecret()+
                  "&code="+code+
                  "&grant_type=authorization_code";
    //发送请求
	String resp = wxMethods.requestWithCert(url,"POST",6000, 6000, null);
    //将数据转换成json格式
	JSONObject json = JSONObject.parseObject(resp);
    //获取openid
	String openid = (String)json.get("openid");
    //放到session
	request.getSession().setAttribute("openid", openid);
    //跳转页面
    return new ModelAndView("pay");
	}
 <!-- pay页面 -->
 <script type="text/javascript" src="/bootstrap/js/jquery.min.js"></script>
 <!-- 微信官方提供的js -->
 <script type="text/javascript" src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"> 
 </script>

<div th:if="${session.openid!=null}" style="margin-left:200px;margin-top:50px">
	<button type="button" id="btn"  
            th:url="@{/wx/pay}"  
            style="width: 500px;height: 60px;font-size: 22px;line-height: 60px"
            class="btn btn-success">支付
    </button>
 </div> 

<script type="text/javascript">
   $("#btn").click(function(){
	   var url = $(this).attr("url");
	   $.ajax({
		   type:'POST',
		   url:url,
		   success:function(res){
				 wx.config({
					   debug: false, 
					   appId: res.appId,
					   timestamp: res.timeStamp,
					   nonceStr: res.nonceStr,
					   signature: res.signType,
					   jsApiList: ['chooseWXPay']
					 }); 
                 //成功执行的方法
				 wx.ready(function(){
					 wx.chooseWXPay({
						   timestamp: res.timeStamp,
						   nonceStr: res.nonceStr,
						   package: res.package,
						   signType: res.signType,
						   paySign: res.paySign,
						   success: function (res) {	
							  // location.href="/wx/success";
						   }
						 }); 
					});  	 
				 wx.error(function(res){
                          alert("支付异常,请联系管理员!")
				 });
		   }
	   })
   })
</script>
    @RequestMapping("/wx/pay")
	@ResponseBody
	public Map<String, String> pay() throws Exception {
		String openid = (String)request.getSession().getAttribute("openid");
		Map<String, String> map = new HashMap<String, String>();
		map.put("appid", pay.getAppid());
		map.put("body", "JSAPI测试");
		map.put("mch_id",pay.getMch_id());
		map.put("nonce_str",WXPayUtil.generateNonceStr());
		map.put("notify_url",pay.getDomainNameUrl()+"/paySuccess");
		map.put("openid",openid);
		map.put("out_trade_no", UU.uu());
		map.put("spbill_create_ip", UU.getIp(request));
		map.put("total_fee", "1");
		map.put("trade_type", "JSAPI");
		String currentTimeMillis = String.valueOf(System.currentTimeMillis()/1000);
        //一次加密并返回xml格式(sign字段需要加密) 
        //加密规则
        //https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3
		String xml =WXPayUtil.generateSignedXml(map, pay.getKey());
		String url1 = "https://api.mch.weixin.qq.com/pay/unifiedorder";
		String xm = wxMethods.requestWithCert(url1, "POST", 6000, 6000, xml);
		
		Map<String, String> payMap = new HashMap<String, String>();
		@SuppressWarnings("unused")
		String prepay_id = "";//预支付id
		Map<String, String> map2 = WXPayUtil.xmlToMap(xm);  
		prepay_id = map2.get("prepay_id");  	
		payMap.put("appId", pay.getAppid());  
		payMap.put("timeStamp", currentTimeMillis);  
		payMap.put("nonceStr", WXPayUtil.generateNonceStr());  
		payMap.put("signType", "MD5");  
		payMap.put("package", "prepay_id=" + prepay_id);  
        //二次加密 前端需要paySign这个字段加密
        //加密规则 
        //https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#62
		String paySign = WXPayUtil.generateSignature(payMap, pay.getKey());  
		payMap.put("paySign", paySign);
		return payMap;
	}

当用户支付成功后微信会以流的方式通知商家是否支付成功,商家需要进行签名验证后通知微信,否则微信会一直回调此方法

// 支付成功后微信回调此方法
	@RequestMapping("/paySuccess")
	@ResponseBody
	public String paySuccess(HttpServletRequest request, HttpServletResponse response) {
		String notifyXml = "";
		BufferedReader reader;
		String line = null;
		String resXml = "";
		try {
			reader = request.getReader();
			while ((line = reader.readLine()) != null) {
				notifyXml += line;
			}
			request.getReader().close();

			if (StringUtils.isEmpty(notifyXml)) {
				throw new NullPointerException("xml为空");
			}
			System.out.println(notifyXml);
			Map<String, String> xmlToMap = WXPayUtil.xmlToMap(notifyXml);

			if (!WXPayUtil.isSignatureValid(xmlToMap, "C5FDE9DE2D29789A81D1BC0F16292048")
					|| !"SUCCESS".equals(xmlToMap.get("result_code"))
					|| !"SUCCESS".equals(xmlToMap.get("return_code"))) {
				System.out.println("today" + "验证签名失败或返回错误结果码");
				resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
						+ "<return_msg><![CDATA[FAIL]]></return_msg>" + "</xml> ";
			} else {
				System.out.println("支付成功");
				resXml = "<xml>\n" +
                                "<return_code><![CDATA[SUCCESS]]></return_code>\n" +
                                "<return_msg><![CDATA[OK]]></return_msg>\n" +
                                "</xml>";; // 通知微信
			
			}
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}

		return resXml;
	}

8.上面需要用的工具类

package com.wisdom.cc.util;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;

public class UU {
	  
	  public static String uu() {
		  int hashCodeV =  UUID.randomUUID().toString().hashCode(); 
		  if(hashCodeV < 0) { 
			  hashCodeV = -hashCodeV;
				   } 
		  SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMddHHmmssSSS");
		  Date date=new Date(); 
		  String format = sdf.format(date);
		  StringBuilder str=new StringBuilder();
		  str.append(hashCodeV); 
		  str.append(format); 
		  String strr = str.toString();
		  return strr;
	  }
	  
	  public static String getIp(HttpServletRequest request) { 
	        String ip = request.getHeader("x-forwarded-for"); 
	        if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 
	            ip = request.getHeader("Proxy-Client-IP"); 
	        } 
	        if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 
	            ip = request.getHeader("WL-Proxy-Client-IP"); 
	        } 
	        if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
		        ip = request.getRemoteAddr();
		    }
		    if(ip.indexOf(",")!=-1){
		    	String[] ips = ip.split(",");
		    	ip = ips[0].trim();
		    }
	        return ip; 
	    } 
}
package com.wisdom.cc.util;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;


public class WxMethods {
	public static String requestWithCert(String url,String method,int connectTimeoutMs, int readTimeoutMs,String reqBody)throws Exception {
		
		URL httpUrl = new URL(url);
	    //打开连接 得到 HttpURLConnection对象
	    HttpURLConnection httpURLConnection = (HttpURLConnection)httpUrl.openConnection();
	    //setDoOutput()默认是false,需要手动设置为true,然后就可以调用getOutputStream()方法从服务器端获得字节输出流
	    httpURLConnection.setDoOutput(true);
	    //设置请求方式
	    httpURLConnection.setRequestMethod(method);
	    //设置连接超时时间
	    httpURLConnection.setConnectTimeout(60000);
	    //读取数据的超时时间
	    httpURLConnection.setReadTimeout(60000);
	    //进行连接
	    httpURLConnection.connect();
	    String UTF8 = "UTF-8";
	    OutputStream outputStream = null;
	    if(reqBody!=null) {
	    	  outputStream = httpURLConnection.getOutputStream();
	          outputStream.write(reqBody.getBytes(UTF8));
	    }
      
	    InputStream inputStream = httpURLConnection.getInputStream();
	    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, UTF8));
	    StringBuffer stringBuffer = new StringBuffer();
	    String line = null;
	    while ((line = bufferedReader.readLine()) != null)
	      stringBuffer.append(line);

	    String resp = stringBuffer.toString();
	    bufferedReader.close();
        inputStream.close();
        if(outputStream!=null) {
            outputStream.close();
        }
	    return resp;
	}
	

}

 

支付一定要在真机测试支付,金额单位是分也就是你输入1实际支付一分钱       

我这个前端调起支付方法是v3版本 wx.chooseWXPay  v2版本WeixinJSBridge   

两张区别可参考 两种js方法发起微信支付

我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=odt8ufxh6m4k

                                                                See you next time

  • 8
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值