微信扫码支付springboot版本

发布时间:2018-11-06
技术:springboot+freemarker

概述

该项目是一个采用springboot构建的web项目,主要实现了微信扫码支付功能。包含最基本的创建订单,生成二维码,接收微信回调功能。实测可行,可在其基本上根据需要自行添加业务逻辑。

详细

一、环境搭建

 首先要想支持微信支付,必须拥有两个账号:①微信公众已认证的服务号,并且需要开通微信支付该能(必须是企业才有资格申请,请你找你家产品去申请吧),②微信商户平台账号;这两个账号一个不能少。此处已默认你已有上两个账号。

a   搭建springboot项目,可以自己在eclipse中新建maven项目导入springboot依赖,也可以直接从springboot官网中下载项目模板。如下图

QQ截图20181106141435.png

在依赖框中填写需要引入的模块,我这里因为需要页面展示,所以选择了freemarker,填入首字母自然就有提示,然后点击下方的GenerateProject按钮即可,eclips导入该maven项目。把各种包和文件先建起来。如下图

QQ截图20181106141107.png

二、程序实现

payconfig.properties文件是自己新建的,里面填写如下信息:

#公众号appleId
APPID=
#商户号
MCH_ID=
#商户密钥
KEY=
#APP和网页支付提交用户端ip, Native支付填调用微信支付API的机器IP, 即:服务器ip地址
SPBILL_CREATE_IP=127.0.0.1
#接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。(需要配置)
NOTIFY_URL=http://225m5x.natappfree.cc/page/notify
#支付方式,取值如下:JSAPI,NATIVE,APP
TRADE_TYPE=NATIVE
# 微信支付 - 统一下单地址
PLACEANORDER_URL=https://api.mch.weixin.qq.com/pay/unifiedorder


最上面三个参数改成自己的。NOTIFY_URL 填写自己的接口地址,需要能外网访问的(自己测试可用内网穿透工具,这里一起上传了),这是别人支付成功后,微信服务端会回调这个接口,这样我们才能知道支付成功与否。对于自定义的配置文件,我们需要自己去配置,让springboot加载进来。这里添加相应注解即可。

@Component
@ConfigurationProperties
@PropertySource("classpath:/payconfig.properties")
public class WxPayConfig {
	
	@Value("${APPID}")
	private String APPID;
	
	@Value("${MCH_ID}")
	private String MCH_ID;
	
	@Value("${KEY}")
	private String KEY;
	
	@Value("${SPBILL_CREATE_IP}")
	private String SPBILL_CREATE_IP;
	
	@Value("${NOTIFY_URL}")
	private String NOTIFY_URL;
	
	@Value("${TRADE_TYPE}")
	private String TRADE_TYPE;
	
	@Value("${PLACEANORDER_URL}")
	private String PLACEANORDER_URL;


freemarker配置,新建页面,freemarker配置比较固定,具体可自行百度这里不是重点,页面也就三个基本页面,分别是index.ftl 用于提交订单信息,payQrCode.ftl 用于展示支付二维码信息,paySuccess.ftl 支付成功后的通知页面,页面很简单,主要是完成功能。

生成订单信息并向前端返回支付二维码

@RequestMapping(value = "/createPreOrder")
	public String createPreOrder(String amount, String title,
			HttpServletRequest request,
			HttpServletResponse response,
			Map<String, Object> model
			) throws Exception {
		System.out.println(config.getAPPID());
		// 商品描述
		String body = title;
		// 商户订单号
		String out_trade_no =String.valueOf(System.currentTimeMillis());
		// 订单总金额,单位为分 
		String total_fee = amount;
		// 统一下单
		PreOrderResult preOrderResult = wxOrderService.placeOrder(body, out_trade_no, total_fee);
		model.put("qrCodeUrl", preOrderResult.getCode_url());
		return "payQrCode";
	}


这是控制层方法,逻辑在placeOrder方法中。

		// 生成预付单对象
		PreOrder o = new PreOrder();
		// 生成随机字符串
		String nonce_str = UUID.randomUUID().toString().trim().replaceAll("-", "");
		o.setAppid(config.getAPPID());
		o.setBody(body);
		o.setMch_id(config.getMCH_ID());
		o.setNotify_url(config.getNOTIFY_URL());
		o.setOut_trade_no(out_trade_no);
		// 判断有没有输入订单总金额,没有输入默认1分钱
		if (total_fee != null && !total_fee.equals("")) {
			o.setTotal_fee(Integer.parseInt(total_fee));
		} else {
			o.setTotal_fee(1);
		}
		o.setNonce_str(nonce_str);
		o.setTrade_type(config.getTRADE_TYPE());
		o.setSpbill_create_ip(config.getSPBILL_CREATE_IP());
		SortedMap<Object, Object> p = new TreeMap<Object, Object>();
		p.put("appid", config.getAPPID());
		p.put("mch_id", config.getMCH_ID());
		p.put("body", body);
		p.put("nonce_str", nonce_str);
		p.put("out_trade_no", out_trade_no);
		p.put("total_fee", total_fee);
		p.put("spbill_create_ip", config.getSPBILL_CREATE_IP());
		p.put("notify_url", config.getNOTIFY_URL());
		p.put("trade_type", config.getTRADE_TYPE());
		// 获得签名
		String sign = Sign.createSign("utf-8", p, config.getKEY());
		o.setSign(sign);
		// Object转换为XML
		String xml = XmlUtil.object2Xml(o, PreOrder.class);
		// 统一下单地址
		String url = config.getPLACEANORDER_URL();
		// 调用微信统一下单地址
		String returnXml = HttpUtil.sendPost(url, xml);
		// XML转换为Object
		PreOrderResult preOrderResult = (PreOrderResult) XmlUtil.xml2Object(returnXml, PreOrderResult.class);
		return preOrderResult;	

这里的功能就是,将数据封装成对象,进行签名,由于微信采用的是xml的交换格式,所以需要将对象转为xml,通过统一下单地址进行提交,然后根据微信服务端的返回值将xml组装成对象。

前端接收二维码信息,利用juery的二维码插件生成支付二维码,然后每隔3秒轮询后台,看用户是否已完成支付。

		// 查询是否支付成功 
		function checkPayResult() {
			$.get("/page/wxPayIsSuccess", function(data) {
// 				debugger;
				console.log(data);
				if (data) {
					window.location.href = "/page/paySuccess";
				}
			});
		}
		
		$(function() {
			// 每个3秒调用后台方法,查看订单是否已经支付成功
			window.setInterval("checkPayResult()", 3000);
		});

接收微信回调通知

@RequestMapping(value = "/notify",method = RequestMethod.POST)
	public void Mynotify(HttpServletRequest request,HttpServletResponse response) throws Exception {
		if (request==null||response==null) {
			System.out.println("请求出错");
		}
		PayResult payResult = wxOrderService.getWxPayResult(request);
		boolean isPaid = payResult.getReturn_code().equals("SUCCESS") ? true : false;
		// 查询该笔订单在微信那边是否成功支付
		// 支付成功,商户处理后同步返回给微信参数
		PrintWriter writer = response.getWriter();
		if (isPaid) {
			System.out.println("================================= 支付成功 =================================");
			
			// ====================== 操作商户自己的业务,比如修改订单状态,生成支付流水等 start ==========================
			// TODO
			this.isOrderPaid = true;
			// ============================================ 业务结束, end ==================================
			// 通知微信已经收到消息,不要再给我发消息了,否则微信会8连击调用本接口
			String noticeStr = setXML("SUCCESS", "");
			writer.write(noticeStr);
			writer.flush();
						
		} else {
			System.out.println("================================= 支付失败 =================================");
			
			// 支付失败
			String noticeStr = setXML("FAIL", "");
			writer.write(noticeStr);
			writer.flush();
		}
		
	}

由于是post方式提交,数据在request的body中,所以可以通过request.getInputStream()来获取。最后通过xml字符串转为java对象

	@Override
	public PayResult getWxPayResult(HttpServletRequest request) throws Exception {
		InputStream inStream = request.getInputStream();
		BufferedReader in = null;
		String result = "";
		in = new BufferedReader(
				new InputStreamReader(inStream));
		String line;
		while ((line = in.readLine()) != null) {
			result += line;
		}
		PayResult pr = (PayResult)XmlUtil.xml2Object(result, PayResult.class);
		System.out.println(pr.toString());
		return pr;
	}


微信对调请求方式是post,数据放在body里面,所以可以通过request.getInputStream()的方法获取回调信息,回调信息是xml格式的,也需要将其转为我们需要的对象方便后续处理。

三、运行效果

QQ截图20181106122549.png

QQ截图20181106122542.png

QQ截图20181106122536.png

四、其他补充

建议结合官方文档一起看,这样更能了解整个过程。

关于回调下面文字摘自微信支付文档:

支付完成后,微信会把相关支付结果和用户信息发送给商户,商户需要接收处理,并返回应答。

对后台通知交互时,如果微信收到商户的应答不是成功或超时,微信认为通知失败,微信会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但微信不保证通知最终能成功。 (通知频率为15/15/30/180/1800/1800/1800/1800/3600,单位:秒)

注意:同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。

推荐的做法是,当收到通知进行处理时,首先检查对应业务数据的状态,判断该通知是否已经处理过,如果没有处理过再进行处理,如果处理过直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。

有疑问请QQ联系:740393778

本实例支付的费用只是购买源码的费用,如有疑问欢迎在文末留言交流,如需作者在线代码指导、定制等,在作者开启付费服务后,可以点击“购买服务”进行实时联系,请知悉,谢谢
手机上随时阅读、收藏该文章 ?请扫下方二维码