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

概述

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

详细

一、环境搭建

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

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

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

二、程序实现

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

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

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

  1. @Component
  2. @ConfigurationProperties
  3. @PropertySource("classpath:/payconfig.properties")
  4. public class WxPayConfig {
  5.  
  6. @Value("${APPID}")
  7. private String APPID;
  8.  
  9. @Value("${MCH_ID}")
  10. private String MCH_ID;
  11.  
  12. @Value("${KEY}")
  13. private String KEY;
  14.  
  15. @Value("${SPBILL_CREATE_IP}")
  16. private String SPBILL_CREATE_IP;
  17.  
  18. @Value("${NOTIFY_URL}")
  19. private String NOTIFY_URL;
  20.  
  21. @Value("${TRADE_TYPE}")
  22. private String TRADE_TYPE;
  23.  
  24. @Value("${PLACEANORDER_URL}")
  25. private String PLACEANORDER_URL;

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

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

  1. @RequestMapping(value = "/createPreOrder")
  2. public String createPreOrder(String amount, String title,
  3. HttpServletRequest request,
  4. HttpServletResponse response,
  5. Map<String, Object> model
  6. ) throws Exception {
  7. System.out.println(config.getAPPID());
  8. // 商品描述
  9. String body = title;
  10. // 商户订单号
  11. String out_trade_no =String.valueOf(System.currentTimeMillis());
  12. // 订单总金额,单位为分
  13. String total_fee = amount;
  14. // 统一下单
  15. PreOrderResult preOrderResult = wxOrderService.placeOrder(body, out_trade_no, total_fee);
  16. model.put("qrCodeUrl", preOrderResult.getCode_url());
  17. return "payQrCode";
  18. }

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

  1. // 生成预付单对象
  2. PreOrder o = new PreOrder();
  3. // 生成随机字符串
  4. String nonce_str = UUID.randomUUID().toString().trim().replaceAll("-", "");
  5. o.setAppid(config.getAPPID());
  6. o.setBody(body);
  7. o.setMch_id(config.getMCH_ID());
  8. o.setNotify_url(config.getNOTIFY_URL());
  9. o.setOut_trade_no(out_trade_no);
  10. // 判断有没有输入订单总金额,没有输入默认1分钱
  11. if (total_fee != null && !total_fee.equals("")) {
  12. o.setTotal_fee(Integer.parseInt(total_fee));
  13. } else {
  14. o.setTotal_fee(1);
  15. }
  16. o.setNonce_str(nonce_str);
  17. o.setTrade_type(config.getTRADE_TYPE());
  18. o.setSpbill_create_ip(config.getSPBILL_CREATE_IP());
  19. SortedMap<Object, Object> p = new TreeMap<Object, Object>();
  20. p.put("appid", config.getAPPID());
  21. p.put("mch_id", config.getMCH_ID());
  22. p.put("body", body);
  23. p.put("nonce_str", nonce_str);
  24. p.put("out_trade_no", out_trade_no);
  25. p.put("total_fee", total_fee);
  26. p.put("spbill_create_ip", config.getSPBILL_CREATE_IP());
  27. p.put("notify_url", config.getNOTIFY_URL());
  28. p.put("trade_type", config.getTRADE_TYPE());
  29. // 获得签名
  30. String sign = Sign.createSign("utf-8", p, config.getKEY());
  31. o.setSign(sign);
  32. // Object转换为XML
  33. String xml = XmlUtil.object2Xml(o, PreOrder.class);
  34. // 统一下单地址
  35. String url = config.getPLACEANORDER_URL();
  36. // 调用微信统一下单地址
  37. String returnXml = HttpUtil.sendPost(url, xml);
  38. // XML转换为Object
  39. PreOrderResult preOrderResult = (PreOrderResult) XmlUtil.xml2Object(returnXml, PreOrderResult.class);
  40. return preOrderResult;

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

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

  1. // 查询是否支付成功
  2. function checkPayResult() {
  3. $.get("/page/wxPayIsSuccess", function(data) {
  4. // debugger;
  5. console.log(data);
  6. if (data) {
  7. window.location.href = "/page/paySuccess";
  8. }
  9. });
  10. }
  11.  
  12. $(function() {
  13. // 每个3秒调用后台方法,查看订单是否已经支付成功
  14. window.setInterval("checkPayResult()", 3000);
  15. });

接收微信回调通知

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

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

  1. @Override
  2. public PayResult getWxPayResult(HttpServletRequest request) throws Exception {
  3. InputStream inStream = request.getInputStream();
  4. BufferedReader in = null;
  5. String result = "";
  6. in = new BufferedReader(
  7. new InputStreamReader(inStream));
  8. String line;
  9. while ((line = in.readLine()) != null) {
  10. result += line;
  11. }
  12. PayResult pr = (PayResult)XmlUtil.xml2Object(result, PayResult.class);
  13. System.out.println(pr.toString());
  14. return pr;
  15. }

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

三、运行效果

四、其他补充

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

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

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

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

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

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

有疑问请QQ联系:740393778

注:本文著作权归作者,由demo大师发表,拒绝转载,转载需要作者授权

微信扫码支付springboot版本的更多相关文章

  1. 微信扫码支付.net版本

    微信扫码支付有两个坑 1.模式一已经过时,不能使用了 2.HttpService类的POST 和 GET方法内的 //设置代理WebProxy proxy = new WebProxy();proxy ...

  2. C# 微信扫码支付API (微信扫码支付模式二)

    一.SDK下载地址:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=11_1,下载.NET C#版本: 二.微信相关设置:(微信扫码 ...

  3. 微信公众号支付|微信H5支付|微信扫码支付|小程序支付|APP微信支付解决方案总结

    最近负责的一些项目开发,都用到了微信支付(微信公众号支付.微信H5支付.微信扫码支付.APP微信支付).在开发的过程中,在调试支付的过程中,或多或少都遇到了一些问题,今天总结下,分享,留存. 先说注意 ...

  4. ASP.NET Core Web 支付功能接入 微信-扫码支付篇

    这篇文章将介绍ASP.NET Core中使用 开源项目 Payment,实现接入微信-扫码支付及异步通知功能. 开发环境:Win 10 x64.VS2017 15.6.4..NET Core SDK ...

  5. 【转载】ASP.NET Core Web 支付功能接入 微信-扫码支付篇

    转自:http://www.cnblogs.com/essenroc/p/8630730.html 这篇文章将介绍ASP.NET Core中使用 开源项目 Payment,实现接入微信-扫码支付及异步 ...

  6. asp.net core 微信扫码支付(扫码支付,H5支付,公众号支付,app支付)之1

    2018-08-13更新生成二维码的方法 在做微信支付前,首先要了解你需要什么方式的微信支付,目前本人做过的支付包含扫码支付.H5支付.公众号支付.App支付等,本人使用的是asp.net mvc c ...

  7. thinkphp.2 thinkphp5微信支付 微信公众号支付 thinkphp 微信扫码支付 thinkphp 微信企业付款5

    前面已经跑通了微信支付的流程,接下来吧微信支付和微信企业付款接入到thinkphp中,版本是3.2 把微信支付类.企业付款类整合到一起放到第三方类库,这里我把微信支付帮助类和企业付款类放到同一个文件了 ...

  8. 【移动支付】.NET微信扫码支付接入(模式二-NATIVE)

    一.前言       经过两三天的琢磨总算完成了微信扫码支付功能,不得不感叹几句: 微信提供的DEMO不错,直接复制粘贴就可以跑起来了: 微信的配置平台我真是服了.公众平台.商户平台.开放平台,一个平 ...

  9. java微信扫码支付Native(模式二)

    官方开发文档模式二的地址:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_5 pom文件的依赖: <?xml versio ...

随机推荐

  1. N体运动的程序模拟

    这依然是与<三体>有关的一篇文章.空间中三个星体在万有引力作用下的运动被称之为三体问题,参见我的上一篇文章:三体运动的程序模拟.而这一节,对三体问题进行了扩展,实现了空间中N个星体在万有引 ...

  2. caffe添加python数据层

    caffe添加python数据层(ImageData) 在caffe中添加自定义层时,必须要实现这四个函数,在C++中是(LayerSetUp,Reshape,Forward_cpu,Backward ...

  3. TCP UDP Socket 即时通讯 API 示例 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  4. TextureView SurfaceView 简介 案例

    简介 Android普通窗口的视图绘制机制是一层一层的,任何一个子元素或者是局部的刷新都会导致整个视图结构全部重绘一次,因此效率相对较低.视频或者opengl内容往往是显示在SurfaceView中的 ...

  5. LigerUi之Grid使用详解(二)——数据编辑

    一.问题概述 在开发web信息管理系统时,使用Web前端框架可以帮助我们快速搭建一组风格统一的界面效果,而且能够解决大多数浏览器兼容问题,提升开发效率.所以上一篇文章为大家介绍了LigerGrid的显 ...

  6. 【API规范】OpenAPI规范

    OpenAPI规范 openAPI 3.0_百度搜索 OpenAPI Specification 2.0 - CSDN博客 APP相关_API 列表_OpenAPI 2.0_开发指南_移动推送-阿里云 ...

  7. MFC如何获取硬盘的序列号

    要把如下的两篇文章结合起来看: qt怎么获取硬盘序列号,是不是没戏? http://www.qtcn.org/bbs/simple/?t65637.html system("wmic pat ...

  8. JavaScript 将行结构数据转化为树形结构,可提供给常用的tree插件直接使用(高效转化方案)

    前台接收到的数据格式 var rows=[{ parent: 'root', id: 'DC', title: '集团' }, { parent: 'DC', id: '01', title: '上海 ...

  9. java interface 默认值

    /* * Hibernate, Relational Persistence for Idiomatic Java * * Copyright (c) 2010, Red Hat Inc. or th ...

  10. 如果使用xutils出现了ExceptionInInitializerError这个错误

    看看是否初始化 1)在Application初始化   x.Ext.init(this); // 在application的onCreate中初始化 /** * 初始化xUtils3 */ publi ...