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/

相關文章
相關標籤/搜索