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 函数 存储过程 事件(event) job 模板
    protobuf 无proto 解码 decode 语言 java python
    mitmproxy fiddler 抓包 填坑
    android adb 常用命令
    android机器人 模拟 踩坑过程
    RabbitMQ添加新用户并支持远程访问
    Windows下RabbitMQ安装及配置
    Java mybatis mysql 常用数据类型对应关系
    easyExcel 踩坑
    linux防火墙查看状态firewall、iptable
  • 原文地址:https://www.cnblogs.com/smallSevens/p/6102335.html
Copyright © 2011-2022 走看看