国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 編程 > Java > 正文

JAVA微信掃碼支付模式二線上支付功能實(shí)現(xiàn)以及回調(diào)

2019-11-26 13:35:15
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

 一、準(zhǔn)備工作

首先吐槽一下微信關(guān)于支付這塊,本身支持的支付模式就好幾種,但是官方文檔特別零散,連像樣的Java相關(guān)的demo也沒(méi)幾個(gè)。本人之前沒(méi)有搞過(guò)微信支付,一開(kāi)始真是被它搞暈,折騰兩天終于調(diào)通了,特此寫(xiě)下來(lái),以享后人吧!

關(guān)于準(zhǔn)備工作,就“微信掃碼支付模式二”官方文檔地址在這 https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_1 可以先看看,實(shí)際上需要準(zhǔn)備的東西有以下幾個(gè):

其中APP_ID和APP_SECRET可以在公眾平臺(tái)找著,MCH_ID和API_KEY則在商戶平臺(tái)找到,特別是API_KEY要在商戶平臺(tái)設(shè)置好,對(duì)于“微信掃碼支付模式二”(支付與回調(diào))實(shí)際只會(huì)用到APP_ID、MCH_ID和API_KEY,其他的都不用。

關(guān)于開(kāi)發(fā)環(huán)境,我就不羅嗦了,不管你是springMVC、struts2又或者直接serverlet,都差不多,只要你能保證對(duì)應(yīng)的方法能調(diào)用起來(lái)就行。關(guān)于引用第三方的jar包,我這里只用到了一個(gè)xml操作的jdom,記住是1.*的版本,不是官網(wǎng)上最新的2.*,兩者不兼容。具體是jdom-1.1.3.jar,依賴包jaxen-1.1.6.jar,就這兩個(gè)包,我沒(méi)用到有些例子中使用的httpclient,感覺(jué)沒(méi)必要,而且依賴包特別繁雜,當(dāng)然你是maven當(dāng)我沒(méi)說(shuō)。

二、開(kāi)發(fā)實(shí)戰(zhàn)

1、首先是接入微信接口,獲取微信支付二維碼。

public String weixin_pay() throws Exception {     // 賬號(hào)信息     String appid = PayConfigUtil.APP_ID; // appid     //String appsecret = PayConfigUtil.APP_SECRET; // appsecret     String mch_id = PayConfigUtil.MCH_ID; // 商業(yè)號(hào)     String key = PayConfigUtil.API_KEY; // key      String currTime = PayCommonUtil.getCurrTime();     String strTime = currTime.substring(8, currTime.length());     String strRandom = PayCommonUtil.buildRandom(4) + "";     String nonce_str = strTime + strRandom;          String order_price = 1; // 價(jià)格  注意:價(jià)格的單位是分     String body = "goodssssss";  // 商品名稱     String out_trade_no = "11338"; // 訂單號(hào)          // 獲取發(fā)起電腦 ip     String spbill_create_ip = PayConfigUtil.CREATE_IP;     // 回調(diào)接口      String notify_url = PayConfigUtil.NOTIFY_URL;     String trade_type = "NATIVE";          SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>();     packageParams.put("appid", appid);     packageParams.put("mch_id", mch_id);     packageParams.put("nonce_str", nonce_str);     packageParams.put("body", body);     packageParams.put("out_trade_no", out_trade_no);     packageParams.put("total_fee", order_price);     packageParams.put("spbill_create_ip", spbill_create_ip);     packageParams.put("notify_url", notify_url);     packageParams.put("trade_type", trade_type);      String sign = PayCommonUtil.createSign("UTF-8", packageParams,key);     packageParams.put("sign", sign);          String requestXML = PayCommonUtil.getRequestXml(packageParams);     System.out.println(requestXML);       String resXml = HttpUtil.postData(PayConfigUtil.UFDODER_URL, requestXML);           Map map = XMLUtil.doXMLParse(resXml);     //String return_code = (String) map.get("return_code");     //String prepay_id = (String) map.get("prepay_id");     String urlCode = (String) map.get("code_url");          return urlCode; } 

如果不出意外的話,這里就從微信服務(wù)器獲取了一個(gè)支付url,形如weixin://wxpay/bizpayurl?pr=pIxXXXX,之后我們就需要把這個(gè)url生成一個(gè)二維碼,然后就可以使用自己手機(jī)微信端掃碼支付了。關(guān)于二維碼生成有很多種方法,各位各取所需吧,我這里提供一個(gè)google的二維碼生成接口:

public static String QRfromGoogle(String chl) throws Exception {   int widhtHeight = 300;   String EC_level = "L";   int margin = 0;   chl = UrlEncode(chl);   String QRfromGoogle = "http://chart.apis.google.com/chart?chs=" + widhtHeight + "x" + widhtHeight       + "&cht=qr&chld=" + EC_level + "|" + margin + "&chl=" + chl;    return QRfromGoogle; } 
// 特殊字符處理 public static String UrlEncode(String src) throws UnsupportedEncodingException {   return URLEncoder.encode(src, "UTF-8").replace("+", "%20"); } 

上面代碼中涉及到幾個(gè)工具類:PayConfigUtil、PayCommonUtil、HttpUtil和XMLUtil,其中PayConfigUtil放的就是上面提到一些配置及路徑,PayCommonUtil涉及到了獲取當(dāng)前事件、產(chǎn)生隨機(jī)字符串、獲取參數(shù)簽名和拼接xml幾個(gè)方法,代碼如下:

public class PayCommonUtil {      /**    * 是否簽名正確,規(guī)則是:按參數(shù)名稱a-z排序,遇到空值的參數(shù)不參加簽名。    * @return boolean    */   public static boolean isTenpaySign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {     StringBuffer sb = new StringBuffer();     Set es = packageParams.entrySet();     Iterator it = es.iterator();     while(it.hasNext()) {       Map.Entry entry = (Map.Entry)it.next();       String k = (String)entry.getKey();       String v = (String)entry.getValue();       if(!"sign".equals(k) && null != v && !"".equals(v)) {         sb.append(k + "=" + v + "&");       }     }          sb.append("key=" + API_KEY);          //算出摘要     String mysign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toLowerCase();     String tenpaySign = ((String)packageParams.get("sign")).toLowerCase();          //System.out.println(tenpaySign + "  " + mysign);     return tenpaySign.equals(mysign);   }    /**    * @author    * @date 2016-4-22    * @Description:sign簽名    * @param characterEncoding    *      編碼格式    * @param parameters    *      請(qǐng)求參數(shù)    * @return    */   public static String createSign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {     StringBuffer sb = new StringBuffer();     Set es = packageParams.entrySet();     Iterator it = es.iterator();     while (it.hasNext()) {       Map.Entry entry = (Map.Entry) it.next();       String k = (String) entry.getKey();       String v = (String) entry.getValue();       if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {         sb.append(k + "=" + v + "&");       }     }     sb.append("key=" + API_KEY);     String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();     return sign;   }    /**    * @author    * @date 2016-4-22    * @Description:將請(qǐng)求參數(shù)轉(zhuǎn)換為xml格式的string    * @param parameters    *      請(qǐng)求參數(shù)    * @return    */   public static String getRequestXml(SortedMap<Object, Object> parameters) {     StringBuffer sb = new StringBuffer();     sb.append("<xml>");     Set es = parameters.entrySet();     Iterator it = es.iterator();     while (it.hasNext()) {       Map.Entry entry = (Map.Entry) it.next();       String k = (String) entry.getKey();       String v = (String) entry.getValue();       if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) {         sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");       } else {         sb.append("<" + k + ">" + v + "</" + k + ">");       }     }     sb.append("</xml>");     return sb.toString();   }    /**    * 取出一個(gè)指定長(zhǎng)度大小的隨機(jī)正整數(shù).    *    * @param length    *      int 設(shè)定所取出隨機(jī)數(shù)的長(zhǎng)度。length小于11    * @return int 返回生成的隨機(jī)數(shù)。    */   public static int buildRandom(int length) {     int num = 1;     double random = Math.random();     if (random < 0.1) {       random = random + 0.1;     }     for (int i = 0; i < length; i++) {       num = num * 10;     }     return (int) ((random * num));   }    /**    * 獲取當(dāng)前時(shí)間 yyyyMMddHHmmss    *    * @return String    */   public static String getCurrTime() {     Date now = new Date();     SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss");     String s = outFormat.format(now);     return s;   }  } 

HttpUtil和XMLUtil如下:

public class HttpUtil {    private static final Log logger = Logs.get();   private final static int CONNECT_TIMEOUT = 5000; // in milliseconds   private final static String DEFAULT_ENCODING = "UTF-8";      public static String postData(String urlStr, String data){     return postData(urlStr, data, null);   }      public static String postData(String urlStr, String data, String contentType){     BufferedReader reader = null;     try {       URL url = new URL(urlStr);       URLConnection conn = url.openConnection();       conn.setDoOutput(true);       conn.setConnectTimeout(CONNECT_TIMEOUT);       conn.setReadTimeout(CONNECT_TIMEOUT);       if(contentType != null)         conn.setRequestProperty("content-type", contentType);       OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream(), DEFAULT_ENCODING);       if(data == null)         data = "";       writer.write(data);        writer.flush();       writer.close();         reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), DEFAULT_ENCODING));       StringBuilder sb = new StringBuilder();       String line = null;       while ((line = reader.readLine()) != null) {         sb.append(line);         sb.append("/r/n");       }       return sb.toString();     } catch (IOException e) {       logger.error("Error connecting to " + urlStr + ": " + e.getMessage());     } finally {       try {         if (reader != null)           reader.close();       } catch (IOException e) {       }     }     return null;   } } 
public class XMLUtil {   /**    * 解析xml,返回第一級(jí)元素鍵值對(duì)。如果第一級(jí)元素有子節(jié)點(diǎn),則此節(jié)點(diǎn)的值是子節(jié)點(diǎn)的xml數(shù)據(jù)。    * @param strxml    * @return    * @throws JDOMException    * @throws IOException    */   public static Map doXMLParse(String strxml) throws JDOMException, IOException {     strxml = strxml.replaceFirst("encoding=/".*/"", "encoding=/"UTF-8/"");      if(null == strxml || "".equals(strxml)) {       return null;     }          Map m = new HashMap();          InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));     SAXBuilder builder = new SAXBuilder();     Document doc = builder.build(in);     Element root = doc.getRootElement();     List list = root.getChildren();     Iterator it = list.iterator();     while(it.hasNext()) {       Element e = (Element) it.next();       String k = e.getName();       String v = "";       List children = e.getChildren();       if(children.isEmpty()) {         v = e.getTextNormalize();       } else {         v = XMLUtil.getChildrenText(children);       }              m.put(k, v);     }          //關(guān)閉流     in.close();          return m;   }      /**    * 獲取子結(jié)點(diǎn)的xml    * @param children    * @return String    */   public static String getChildrenText(List children) {     StringBuffer sb = new StringBuffer();     if(!children.isEmpty()) {       Iterator it = children.iterator();       while(it.hasNext()) {         Element e = (Element) it.next();         String name = e.getName();         String value = e.getTextNormalize();         List list = e.getChildren();         sb.append("<" + name + ">");         if(!list.isEmpty()) {           sb.append(XMLUtil.getChildrenText(list));         }         sb.append(value);         sb.append("</" + name + ">");       }     }          return sb.toString();   }    } 

當(dāng)然還有一個(gè)MD5計(jì)算工具類

public class MD5Util {    private static String byteArrayToHexString(byte b[]) {     StringBuffer resultSb = new StringBuffer();     for (int i = 0; i < b.length; i++)       resultSb.append(byteToHexString(b[i]));      return resultSb.toString();   }    private static String byteToHexString(byte b) {     int n = b;     if (n < 0)       n += 256;     int d1 = n / 16;     int d2 = n % 16;     return hexDigits[d1] + hexDigits[d2];   }    public static String MD5Encode(String origin, String charsetname) {     String resultString = null;     try {       resultString = new String(origin);       MessageDigest md = MessageDigest.getInstance("MD5");       if (charsetname == null || "".equals(charsetname))         resultString = byteArrayToHexString(md.digest(resultString             .getBytes()));       else         resultString = byteArrayToHexString(md.digest(resultString             .getBytes(charsetname)));     } catch (Exception exception) {     }     return resultString;   }    private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",       "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };  } 

2、支付回調(diào)

支付完成后,微信會(huì)把相關(guān)支付結(jié)果和用戶信息發(fā)送到我們上面指定的那個(gè)回調(diào)地址,我們需要接收處理,并返回應(yīng)答。對(duì)后臺(tái)通知交互時(shí),如果微信收到商戶的應(yīng)答不是成功或超時(shí),微信認(rèn)為通知失敗,微信會(huì)通過(guò)一定的策略定期重新發(fā)起通知,盡可能提高通知的成功率,但微信不保證通知最終能成功。 (通知頻率為15/15/30/180/1800/1800/1800/1800/3600,單位:秒)

關(guān)于支付回調(diào)接口,我們首先要對(duì)于支付結(jié)果通知的內(nèi)容進(jìn)行簽名驗(yàn)證,然后根據(jù)支付結(jié)果進(jìn)行相應(yīng)的處理流程即可。

public void weixin_notify(HttpServletRequest request,HttpServletResponse response) throws Exception{          //讀取參數(shù)     InputStream inputStream ;     StringBuffer sb = new StringBuffer();     inputStream = request.getInputStream();     String s ;     BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));     while ((s = in.readLine()) != null){       sb.append(s);     }     in.close();     inputStream.close();      //解析xml成map     Map<String, String> m = new HashMap<String, String>();     m = XMLUtil.doXMLParse(sb.toString());          //過(guò)濾空 設(shè)置 TreeMap     SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>();        Iterator it = m.keySet().iterator();     while (it.hasNext()) {       String parameter = (String) it.next();       String parameterValue = m.get(parameter);              String v = "";       if(null != parameterValue) {         v = parameterValue.trim();       }       packageParams.put(parameter, v);     }          // 賬號(hào)信息     String key = PayConfigUtil.API_KEY; // key      logger.info(packageParams);     //判斷簽名是否正確     if(PayCommonUtil.isTenpaySign("UTF-8", packageParams,key)) {       //------------------------------       //處理業(yè)務(wù)開(kāi)始       //------------------------------       String resXml = "";       if("SUCCESS".equals((String)packageParams.get("result_code"))){         // 這里是支付成功         //////////執(zhí)行自己的業(yè)務(wù)邏輯////////////////         String mch_id = (String)packageParams.get("mch_id");         String openid = (String)packageParams.get("openid");         String is_subscribe = (String)packageParams.get("is_subscribe");         String out_trade_no = (String)packageParams.get("out_trade_no");                  String total_fee = (String)packageParams.get("total_fee");                  logger.info("mch_id:"+mch_id);         logger.info("openid:"+openid);         logger.info("is_subscribe:"+is_subscribe);         logger.info("out_trade_no:"+out_trade_no);         logger.info("total_fee:"+total_fee);                  //////////執(zhí)行自己的業(yè)務(wù)邏輯////////////////                  logger.info("支付成功");         //通知微信.異步確認(rèn)成功.必寫(xiě).不然會(huì)一直通知后臺(tái).八次之后就認(rèn)為交易失敗了.         resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"             + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";                } else {         logger.info("支付失敗,錯(cuò)誤信息:" + packageParams.get("err_code"));         resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"             + "<return_msg><![CDATA[報(bào)文為空]]></return_msg>" + "</xml> ";       }       //------------------------------       //處理業(yè)務(wù)完畢       //------------------------------       BufferedOutputStream out = new BufferedOutputStream(           response.getOutputStream());       out.write(resXml.getBytes());       out.flush();       out.close();     } else{       logger.info("通知簽名驗(yàn)證失敗");     }        } 

    簽名驗(yàn)證算法和簽名生成的算法類似,在上面PayCommonUtil工具類中提供。
三、后話

感覺(jué)微信掃描支付體驗(yàn)效果還是挺好的,唯一缺點(diǎn)就是相關(guān)文檔零散,官方的demo居然沒(méi)有java編寫(xiě)的,希望之后微信官方能夠逐步完善吧!

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持武林網(wǎng)。

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 沿河| 资中县| 阿图什市| 定陶县| 兰溪市| 大安市| 临夏市| 晋江市| 肃宁县| 敦煌市| 原阳县| 洪江市| 基隆市| 开原市| 漯河市| 资溪县| 泰宁县| 大安市| 瓦房店市| 房产| 加查县| 科尔| 武川县| 三门县| 徐水县| 璧山县| 玉环县| 莱西市| 湘乡市| 巴里| 滕州市| 长岛县| 竹山县| 贵阳市| 乐都县| 安丘市| 南江县| 邵武市| 黄陵县| 利川市| 常德市|