zoukankan      html  css  js  c++  java
  • JAVA实现的微信扫描二维码支付

    吐槽一下

    支付项目采用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. }

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

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

  • 相关阅读:
    mysql 语法
    mycat 配置简介
    redis sentinel 配置
    Spark SQL 读到的记录数与 hive 读到的不一致
    HDP3.1 中 YRAN 和 MR2 的内存大小配置的计算方式
    在 windows 下搭建 IDEA + Spark 连接 Hive 的环境
    HDP3.1 中配置 YARN 的 timeline server 使用外部的 HBase
    大规模使用 Apache Kafka 的20个最佳实践
    卸载mac版本的GlobalProtect
    js解决约瑟夫问题
  • 原文地址:https://www.cnblogs.com/smallSevens/p/6102335.html
Copyright © 2011-2022 走看看