JAVA实现的微信扫描二维码支付

吐槽一下

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

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

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

前期酝酿准备

扫码支付,目前来讲我的是不能够申请的,包括如今支付宝的即时到账我的相关业务也取消了。因此这里必须有一个微信支付商户平台,具体怎么申请的,我也不清楚,只是拿来用的。api

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

什么是扫码支付?

场景介绍微信

用户扫描商户展现在各类场景的二维码进行支付。架构

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

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

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

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

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

原文地址:http://blog.52itstyle.com/archives/180/

相关文章
相关标签/搜索