微信扫码支付springboot版本
概述
详细
一、环境搭建
首先要想支持微信支付,必须拥有两个账号:①微信公众已认证的服务号,并且需要开通微信支付该能(必须是企业才有资格申请,请你找你家产品去申请吧),②微信商户平台账号;这两个账号一个不能少。此处已默认你已有上两个账号。
a 搭建springboot项目,可以自己在eclipse中新建maven项目导入springboot依赖,也可以直接从springboot官网中下载项目模板。如下图
在依赖框中填写需要引入的模块,我这里因为需要页面展示,所以选择了freemarker,填入首字母自然就有提示,然后点击下方的GenerateProject按钮即可,eclips导入该maven项目。把各种包和文件先建起来。如下图
二、程序实现
payconfig.properties文件是自己新建的,里面填写如下信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #公众号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加载进来。这里添加相应注解即可。
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 | @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 支付成功后的通知页面,页面很简单,主要是完成功能。
生成订单信息并向前端返回支付二维码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | @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方法中。
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 | // 生成预付单对象 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秒轮询后台,看用户是否已完成支付。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // 查询是否支付成功 function checkPayResult() { $.get( "/page/wxPayIsSuccess" , function (data) { // debugger; console.log(data); if (data) { window.location.href = "/page/paySuccess" ; } }); } $( function () { // 每个3秒调用后台方法,查看订单是否已经支付成功 window.setInterval( "checkPayResult()" , 3000); }); |
接收微信回调通知
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 | @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对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | @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格式的,也需要将其转为我们需要的对象方便后续处理。
三、运行效果
四、其他补充
建议结合官方文档一起看,这样更能了解整个过程。
关于回调下面文字摘自微信支付文档:
支付完成后,微信会把相关支付结果和用户信息发送给商户,商户需要接收处理,并返回应答。
对后台通知交互时,如果微信收到商户的应答不是成功或超时,微信认为通知失败,微信会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但微信不保证通知最终能成功。 (通知频率为15/15/30/180/1800/1800/1800/1800/3600,单位:秒)
注意:同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。
推荐的做法是,当收到通知进行处理时,首先检查对应业务数据的状态,判断该通知是否已经处理过,如果没有处理过再进行处理,如果处理过直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。
有疑问请QQ联系:740393778
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?