JAVA实现的微信扫描二维码支付_不是太高的手的博客-程序员宅基地_java 微信付款码支付

技术标签: Java  spring mvc  支付  微信  dubbo  二维码  

吐槽一下



支付项目采用springMvc+Dubbo架构实现,只对外提供接口。

话说,为什么微信支付比支付宝来的晚了那么一点,一句话,那一阵挺忙的,然后就没有时间整理,最近做完支付宝支付,顺便也把微信支付的也整理一下。

这里再吐槽一下,微信支付的DEMO基本为零,很多代码都是从网上查找的(也可能我么有仔细找API)。

前期酝酿准备

扫码支付,目前来说个人是不可以申请的,包括现在支付宝的即时到帐个人相关业务也取消了。所以这里必须有一个微信支付商户平台,具体怎么申请的,我也不清楚,只是拿来用的。

商户平台是要配合绑定微信公众账号使用的,具体操作申请下来已经绑定了,这里你也只管用就是了。

什么是扫码支付?

场景介绍

用户扫描商户展示在各种场景的二维码进行支付。

步骤1:商户根据微信支付的规则,为不同商品生成不同的二维码(如图6.1),展示在各种场景,用于用户扫描购买。

步骤2:用户使用微信“扫一扫”(如图6.2)扫描二维码后,获取商品支付信息,引导用户完成支付(如图6.3)。
chapter6_1_1.png
chapter6_1_2.jpgchapter6_1_3.jpg
支付二维码
图6.1 支付二维码
打开微信扫一扫二维码
图6.2 打开微信扫一扫二维码
确认支付页面
图6.3 确认支付页面

步骤(3):用户确认支付,输入支付密码(如图6.4)。

步骤(4):支付完成后会提示用户支付成功(如图6.5),商户后台得到支付成功的通知,然后进行发货处理。

用户确认支付,输入密码
图6.4 用户确认支付,输入密码
支付成功提示
图6.5 支付成功提示
chapter6_1_4.jpgchapter6_1_5.jpg

如何集成到项目中去?

ConfigUtil参数配置:

 
  
  1. import java.util.Map;
  2. import java.util.ResourceBundle;
  3. import java.util.SortedMap;
  4. import java.util.TreeMap;
  5. /**
  6. * 相关配置参数
  7. * 创建者 张志朋
  8. * 创建时间 2016年9月28日
  9. *
  10. */
  11. public class ConfigUtil {
  12. /**
  13. * 服务号相关信息
  14. */
  15. public final static String APP_ID = "2016";//服务号的应用ID
  16. public final static String APP_SECRET = "2016";//服务号的应用密钥
  17. public final static String TOKEN = "weixinCourse";//服务号的配置token
  18. public final static String MCH_ID = "2016";//商户号
  19. public final static String API_KEY = "2016";//API密钥
  20. public final static String SIGN_TYPE = "MD5";//签名加密方式
  21. public final static String CERT_PATH = "apiclient_cert.p12";//微信支付证书存放路径地址
  22. static ResourceBundle resource = ResourceBundle.getBundle("config");
  23. //微信支付统一接口的回调action
  24. public final static String NOTIFY_URL = resource.getString("WEIXIN_NOTIFY_URL");
  25. /**
  26. * 微信基础接口地址
  27. */
  28. //获取token接口(GET)
  29. public final static String TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
  30. //oauth2授权接口(GET)
  31. public final static String OAUTH2_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
  32. //刷新access_token接口(GET)
  33. public final static String REFRESH_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN";
  34. // 菜单创建接口(POST)
  35. public final static String MENU_CREATE_URL = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";
  36. // 菜单查询(GET)
  37. public final static String MENU_GET_URL = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN";
  38. // 菜单删除(GET)
  39. public final static String MENU_DELETE_URL = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=ACCESS_TOKEN";
  40. /**
  41. * 微信支付接口地址
  42. */
  43. //微信支付统一接口(POST)
  44. public final static String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
  45. //微信退款接口(POST)
  46. public final static String REFUND_URL = "https://api.mch.weixin.qq.com/secapi/pay/refund";
  47. //订单查询接口(POST)
  48. public final static String CHECK_ORDER_URL = "https://api.mch.weixin.qq.com/pay/orderquery";
  49. //关闭订单接口(POST)
  50. public final static String CLOSE_ORDER_URL = "https://api.mch.weixin.qq.com/pay/closeorder";
  51. //退款查询接口(POST)
  52. public final static String CHECK_REFUND_URL = "https://api.mch.weixin.qq.com/pay/refundquery";
  53. //对账单接口(POST)
  54. public final static String DOWNLOAD_BILL_URL = "https://api.mch.weixin.qq.com/pay/downloadbill";
  55. //短链接转换接口(POST)
  56. public final static String SHORT_URL = "https://api.mch.weixin.qq.com/tools/shorturl";
  57. //接口调用上报接口(POST)
  58. public final static String REPORT_URL = "https://api.mch.weixin.qq.com/payitil/report";
  59. public static void commonParams(SortedMap<Object, Object> packageParams){
  60. // 账号信息
  61. String appid = ConfigUtil.APP_ID; // appid
  62. String mch_id = ConfigUtil.MCH_ID; // 商业号
  63. // 生成随机字符串
  64. String currTime = PayCommonUtil.getCurrTime();
  65. String strTime = currTime.substring(8, currTime.length());
  66. String strRandom = PayCommonUtil.buildRandom(4) + "";
  67. String nonce_str = strTime + strRandom;
  68. packageParams.put("appid", appid);// 公众账号ID
  69. packageParams.put("mch_id", mch_id);// 商户号
  70. packageParams.put("nonce_str", nonce_str);// 随机字符串
  71. }
  72. /**
  73. * 该接口主要用于扫码原生支付模式一中的二维码链接转成短链接(weixin://wxpay/s/XXXXXX),减小二维码数据量,提升扫描速度和精确度。
  74. * @Author 张志朋
  75. * @param urlCode void
  76. * @Date 2016年10月26日
  77. * 更新日志
  78. * 2016年10月26日 张志朋 首次创建
  79. *
  80. */
  81. @SuppressWarnings("rawtypes")
  82. public static void shorturl(String urlCode){
  83. try {
  84. String key = ConfigUtil.API_KEY; // key
  85. SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
  86. ConfigUtil.commonParams(packageParams);
  87. packageParams.put("long_url", urlCode);// URL链接
  88. String sign = PayCommonUtil.createSign("UTF-8", packageParams, key);
  89. packageParams.put("sign", sign);// 签名
  90. String requestXML = PayCommonUtil.getRequestXml(packageParams);
  91. String resXml = HttpUtil.postData(ConfigUtil.SHORT_URL, requestXML);
  92. Map map = XMLUtil.doXMLParse(resXml);
  93. String returnCode = (String) map.get("return_code");
  94. if("SUCCESS".equals(returnCode)){
  95. String resultCode = (String) map.get("return_code");
  96. if("SUCCESS".equals(resultCode)){
  97. urlCode = (String) map.get("short_url");
  98. }
  99. }
  100. } catch (Exception e) {
  101. e.printStackTrace();
  102. }
  103. }

参数必填项 APP_ID 和APP_SECRET 是从微信公众号里面获取的,而MCH_ID和API_KEY是从商户平台获取的。CERT_PATH 证书可选,但是如果做退款接口必须要使用证书。NOTIFY_URL 为回调地址,自定义路径,但是一定要微信平台可以调用到你的url。

如何生成二维码订单?

API文档地址

文档有详细的参数说明,具体生成需要xml解析,这里就不放了,好多的说,有需要的可以留言。

支付回调:

 
  
  1. /**
  2. * 二维码支付
  3. * 创建者 张志朋
  4. * 创建时间 2016年10月31日
  5. *
  6. */
  7. @Controller
  8. @RequestMapping(value = "weixin")
  9. public class WeixinPayController {
  10. @Autowired
  11. private IWeixinPayService weixinpayBack;
  12. /**
  13. * 微信支付回调
  14. * @Author 张志朋
  15. * @param request
  16. * @param response
  17. * @throws Exception void
  18. * @Date 2016年9月28日
  19. * 更新日志
  20. * 2016年9月28日 张志朋 首次创建
  21. *
  22. */
  23. @SuppressWarnings({ "unchecked", "rawtypes" })
  24. @RequestMapping(value = "pay")
  25. public void weixin_notify(HttpServletRequest request, HttpServletResponse response) throws Exception {
  26. LogUtil.info("支付成功回调");
  27. // 读取参数
  28. InputStream inputStream = request.getInputStream();
  29. StringBuffer sb = new StringBuffer();
  30. String s;
  31. BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
  32. while ((s = in.readLine()) != null) {
  33. sb.append(s);
  34. }
  35. in.close();
  36. inputStream.close();
  37. // 解析xml成map
  38. Map<String, String> m = new HashMap<String, String>();
  39. m = XMLUtil.doXMLParse(sb.toString());
  40. // 过滤空 设置 TreeMap
  41. SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
  42. Iterator it = m.keySet().iterator();
  43. while (it.hasNext()) {
  44. String parameter = (String) it.next();
  45. String parameterValue = m.get(parameter);
  46. String v = "";
  47. if (null != parameterValue) {
  48. v = parameterValue.trim();
  49. }
  50. packageParams.put(parameter, v);
  51. }
  52. // 账号信息
  53. String key = ConfigUtil.API_KEY; // key
  54. // 判断签名是否正确
  55. if (PayCommonUtil.isTenpaySign("UTF-8", packageParams, key)) {
  56. // ------------------------------
  57. // 处理业务开始
  58. // ------------------------------
  59. String resXml = "";
  60. if ("SUCCESS".equals((String) packageParams.get("result_code"))) {
  61. // 这里是支付成功
  62. String orderNo = (String) packageParams.get("out_trade_no");
  63. String attach = (String) packageParams.get("attach");
  64. //回调K12
  65. LogUtil.info(attach+"(订单号:"+orderNo+"付款成功)");
  66. // 通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.
  67. resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
  68. weixinpayBack.updateAccOrder(orderNo);
  69. } else {
  70. LogUtil.info("支付失败,错误信息:" + packageParams.get("err_code"));
  71. resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
  72. }
  73. // ------------------------------
  74. // 处理业务完毕
  75. // ------------------------------
  76. BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
  77. out.write(resXml.getBytes());
  78. out.flush();
  79. out.close();
  80. } else {
  81. LogUtil.info("通知签名验证失败");
  82. }
  83. }
  84. }

大体就这个样子,后续的可能就是安全优化了。涉及到钱可不是小问题。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/zhulin2012/article/details/53467570

智能推荐

【电脑讲解】联想电脑你不知道的事联:想怎么进入bios(联想电脑进入bios图文教程)_我是肖某人的博客-程序员宅基地_联想电脑bios

都知道品牌不同进入bios的操作方式有所不同,那么联想怎么进入bios呢?其实很简单,联想电脑开机LOGO出现那个界面实际已经提示了进入BIOS的按键,就是在当前页面按F2就行。具体的可以看下面的进入...都知道品牌不同进入bios的操作方式有所不同,那么联想怎么进入bios呢?其实很简单,联想电脑开机LOGO出现那个界面实际已经提示了进入BIOS的按键,就是在当前页面按F2就行。具体的可以看下面的进入bios的图文介绍,顺便介绍下联想bios里面各个界面的信息,帮助想更改bios设置的你。联想电脑

Java面向对象小项目 慕课网Java入门第二季答答租车系统_BlueSky_USC的博客-程序员宅基地

一、项目背景       编写一个控制台程序,要求实现如下功能:       1.展示所有可租车辆       2.选择车型,租车量       3.显示租车清单,包括:总载货量,总载客量,总金额等;二、车的类别       客车:只能载客       货车:只能载货       皮卡:既能载人,有能载货下面给出源代码,仅供参考:父类

Linux下map hash_map和unordered_map效率比较_weixin_34008784的博客-程序员宅基地

2019独角兽企业重金招聘Python工程师标准&gt;&gt;&gt; ...

Servlet和HTTP请求协议-学习笔记02【Servlet_体系结构与urlpartten配置、HTTP请求协议】_延锋L的博客-程序员宅基地

Servlet和HTTP请求协议-学习笔记02【【Servlet_体系结构与urlpartten配置、HTTP请求协议】】

网络基础(一)_Moua的博客-程序员宅基地

目录一、计算机网络的背景和发展1、网络的发展历程二、初识网络协议1、什么是协议?2、计算机网络体系结构三、网络传输(TCP/IP通信过程)1、同一网段内两台主机间的文件传输2、跨网段的主机文件传输3、数据报的封装和分用4、网络中的地址管理一、计算机网络的背景和发展1、网络的发展历程独立模式:计算机之间是相互独立的,每个终端独自拥有客户数据。 网络互联:多台计算机连接在一起,完成数据的共享。各个终端之间可以自由切换,共享数据由服务器集中管理。 局域网LA

Android 新闻客户端案例_Monster_GT的博客-程序员宅基地

实现新闻客户端案例首先需要注意XML文件的在线解析,适配器问题和ListView控件的使用。根据自己的需要和要求,准备好素材。1、NewsInfo.xml文件如下: http://10.51.9.168:8080/images1/a.jpg 科技温暖世界 进

随便推点

面对初学者的CAN总线入门教程(三)_CAN通信中帧、优先级、位填充、错误、位时序以及同步的介绍_吮指原味张的博客-程序员宅基地_can总线优先级

目录1. 数据链路层中的帧1.1 数据帧1.1.1 帧起始(标准、扩展格式相同)1.1.2 仲裁段1.1.3 控制段1.1.4 数据段(标准、扩展格式相同)1.1.5 CRC 段(标准/扩展格式相同)1.1.6 ACK 段(标准/扩展格式相同)1.1.7 帧结束(标准/扩展格式相同)1.2 遥控帧1.3 错误帧1.4 过载帧1.5 帧间隔2. 优先级的确定3. 位填充4. 错误4.1 错误的种类4.2 错误帧的输出5. 位时序6. 同步6.1 取得同步的方法6.2 硬件同步6.3 再同步6.4 调整同步的规

Liferay7开发文档_1.3.1LIFERAY PORTAL中的JAVASCRIPT_chongchou1789的博客-程序员宅基地

LIFERAY PORTAL中的JAVASCRIPT Liferay Portal前端具备可扩展与灵活性的特点,可适应未来发展。 与以前版本一样,许多组件都使用AlloyUI编写。AlloyUI基于YUI,但不再积极发展。我们已经引入了jQuery,并且开发了一个名为MetalJS的新框...

war包发布找不见路径--weblogic --------getResource("/")与getRealPath("/")_tianhaimo的博客-程序员宅基地

在部署项目时出现了找不到路径的问题。到网上搜了下,做个总结。首先分析下这次错误(找不见路径)的原因:我获取路径的代码:request.getSession().getServletContext().getRealPath("/")经验证,此方法获取的路径为绝对路径。在发布项目时,把项目打成war发布到weblogic上,出现了找不见路径的问题。解决方法:使用以下代码获取路

Spring Boot Admin 集成自定义监控告警(2.0.1版本)------钉钉机器人_yuancao24的博客-程序员宅基地

Spring Boot Admin 集成自定义监控告警(2.0.1版本)------钉钉机器人说明1.本文基于 Spring Boot Admin 2.0.1 和1.5.7版本有所出入,通过查看别的集成源码,新增集成钉钉机器人2.钉钉自定义机器人,参考文档 https://open-doc.dingtalk.com/docs/doc.htm?spm=a219a.7629140.0.0.64D...

python3 csv读写_python3 csv_weixin_39850062的博客-程序员宅基地

一、python3 csv 的中文乱码解决方案将文件保存为 csv 格式的话,用记事本打开是没有问题的,但用excel 打开就会乱码,在网上找了些解决方法都是适用python2这里提供下一个解决方案>>> import csv>>> import codecs>>> data = [('小河', '25', '1234567'),('小芳', '18', '789456')]>>> csvfile...

Splinter使用中遇到的问题集锦_windanchaos的博客-程序员宅基地

已经解决1、selenium.common.exceptions.ElementNotVisibleException: Message: element not visible 2、selenium.common.exceptions.InvalidElementStateException: Message: invalid element state: Element is not c

推荐文章

热门文章

相关标签