最近公司項目急着測試,須要開發微信小程序+微信支付+微信退款,本着這幾天的一些研究,決定記錄一下開發的過程。html
本着知識分享的原則,但願對你們有所幫助。java
本篇針對的是微信小程序的支付開發,若是有對微信公衆號的支付開發須要的,能夠去個人github上看看,有個sell的項目很好的完成了公衆號方面的支付與退款,代碼很全,用的是最優秀的sdk,確定對大家學習和工做有幫助,下面貼一下github連接: https://github.com/wenbingshen/springbootgit
也能夠關注個人微信公衆號:《小沈乾貨》不迷路。github
廢話很少說,開始咱們的小程序支付開發之旅:web
首先呢,開發以前,須要交代的是,有關微信支付的開發須要有本身的商戶號和密鑰,這在微信支付開發文檔上面講的很清楚,有過支付開發經驗的對這一點很清楚。算法
瞭解了上面的狀況後我們就開始着手開發吧!spring
先編寫一個常量類Constant,將有關的配置常量配在裏面:數據庫
public class Constant { public static final String DOMAIN = "http://sellbin.natapp1.cc";//配置本身的域名 public static final String APP_ID = "填寫本身的"; public static final String APP_SECRET = "填寫本身的"; public static final String APP_KEY = "填寫本身的"; public static final String MCH_ID = "填寫本身的"; //商戶號 public static final String URL_UNIFIED_ORDER = "https://api.mch.weixin.qq.com/pay/unifiedorder"; public static final String URL_NOTIFY = Constant.DOMAIN + "/wxpay/views/payInfo.jsp"; public static final String TIME_FORMAT = "yyyyMMddHHmmss"; public static final int TIME_EXPIRE = 2; //單位是day }
支付的時候,咱們須要利用發起支付的用戶code去微信接口獲取用戶的openid,只有獲得了openid才能去申請預付單得到prepayId,而後去喚起微信支付。apache
微信支付文檔上面也寫的很清楚:json
接下來咱們編寫PayController類去調用微信支付的接口:
package luluteam.wxpay.controller; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import luluteam.wxpay.constant.Constant; import luluteam.wxpay.entity.PayInfo; import luluteam.wxpay.util.*; import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; import org.codehaus.jackson.map.ObjectMapper; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest; import java.util.*; @Controller public class PayController { private static Logger log = Logger.getLogger(PayController.class); @ResponseBody @RequestMapping(value = "/prepay", produces = "text/html;charset=UTF-8") public String prePay(String code, ModelMap model, HttpServletRequest request) { System.out.println("code:"+code); String content = null; Map map = new HashMap(); ObjectMapper mapper = new ObjectMapper(); boolean result = true; String info = ""; log.error("\n======================================================"); log.error("code: " + code); String openId = getOpenId(code); System.out.println("獲取openid啊"+openId); if(StringUtils.isBlank(openId)) { result = false; info = "獲取到openId爲空"; } else { openId = openId.replace("\"", "").trim(); String clientIP = CommonUtil.getClientIp(request); log.error("openId: " + openId + ", clientIP: " + clientIP); String randomNonceStr = RandomUtils.generateMixString(32); String prepayId = unifiedOrder(openId, clientIP, randomNonceStr); log.error("prepayId: " + prepayId); if(StringUtils.isBlank(prepayId)) { result = false; info = "出錯了,未獲取到prepayId"; } else { map.put("prepayId", prepayId); map.put("nonceStr", randomNonceStr); } } try { map.put("result", result); map.put("info", info); content = mapper.writeValueAsString(map); } catch (Exception e) { e.printStackTrace(); } return content; } private String getOpenId(String code) { String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + Constant.APP_ID + "&secret=" + Constant.APP_SECRET + "&js_code=" + code + "&grant_type=authorization_code"; HttpUtil httpUtil = new HttpUtil(); try { HttpResult httpResult = httpUtil.doGet(url, null, null); if(httpResult.getStatusCode() == 200) { JsonParser jsonParser = new JsonParser(); JsonObject obj = (JsonObject) jsonParser.parse(httpResult.getBody()); log.error("getOpenId: " + obj.toString()); if(obj.get("errcode") != null) { log.error("getOpenId returns errcode: " + obj.get("errcode")); return ""; } else { return obj.get("openid").toString(); } //return httpResult.getBody(); } } catch (Exception e) { e.printStackTrace(); } return ""; } /** * 調用統一下單接口 * @param openId */ private String unifiedOrder(String openId, String clientIP, String randomNonceStr) { try { String url = Constant.URL_UNIFIED_ORDER; PayInfo payInfo = createPayInfo(openId, clientIP, randomNonceStr); String md5 = getSign(payInfo); payInfo.setSign(md5); log.error("md5 value: " + md5); String xml = CommonUtil.payInfoToXML(payInfo); xml = xml.replace("__", "_").replace("<![CDATA[1]]>", "1"); //xml = xml.replace("__", "_").replace("<![CDATA[", "").replace("]]>", ""); log.error(xml); StringBuffer buffer = HttpUtil.httpsRequest(url, "POST", xml); log.error("unifiedOrder request return body: \n" + buffer.toString()); Map<String, String> result = CommonUtil.parseXml(buffer.toString()); String return_code = result.get("return_code"); if(StringUtils.isNotBlank(return_code) && return_code.equals("SUCCESS")) { String return_msg = result.get("return_msg"); if(StringUtils.isNotBlank(return_msg) && !return_msg.equals("OK")) { //log.error("統一下單錯誤!"); return ""; } String prepay_Id = result.get("prepay_id"); return prepay_Id; } else { return ""; } } catch (Exception e) { e.printStackTrace(); } return ""; } private PayInfo createPayInfo(String openId, String clientIP, String randomNonceStr) { Date date = new Date(); String timeStart = TimeUtils.getFormatTime(date, Constant.TIME_FORMAT); String timeExpire = TimeUtils.getFormatTime(TimeUtils.addDay(date, Constant.TIME_EXPIRE), Constant.TIME_FORMAT); String randomOrderId = CommonUtil.getRandomOrderId(); PayInfo payInfo = new PayInfo(); payInfo.setAppid(Constant.APP_ID); payInfo.setMch_id(Constant.MCH_ID); payInfo.setDevice_info("WEB"); payInfo.setNonce_str(randomNonceStr); payInfo.setSign_type("MD5"); //默認即爲MD5 payInfo.setBody("JSAPI支付測試"); payInfo.setAttach("支付測試4luluteam"); payInfo.setOut_trade_no(randomOrderId); payInfo.setTotal_fee(1); payInfo.setSpbill_create_ip(clientIP); payInfo.setTime_start(timeStart); payInfo.setTime_expire(timeExpire); payInfo.setNotify_url(Constant.URL_NOTIFY); payInfo.setTrade_type("JSAPI"); payInfo.setLimit_pay("no_credit"); payInfo.setOpenid(openId); return payInfo; } private String getSign(PayInfo payInfo) throws Exception { StringBuffer sb = new StringBuffer(); sb.append("appid=" + payInfo.getAppid()) .append("&attach=" + payInfo.getAttach()) .append("&body=" + payInfo.getBody()) .append("&device_info=" + payInfo.getDevice_info()) .append("&limit_pay=" + payInfo.getLimit_pay()) .append("&mch_id=" + payInfo.getMch_id()) .append("&nonce_str=" + payInfo.getNonce_str()) .append("¬ify_url=" + payInfo.getNotify_url()) .append("&openid=" + payInfo.getOpenid()) .append("&out_trade_no=" + payInfo.getOut_trade_no()) .append("&sign_type=" + payInfo.getSign_type()) .append("&spbill_create_ip=" + payInfo.getSpbill_create_ip()) .append("&time_expire=" + payInfo.getTime_expire()) .append("&time_start=" + payInfo.getTime_start()) .append("&total_fee=" + payInfo.getTotal_fee()) .append("&trade_type=" + payInfo.getTrade_type()) .append("&key=" + Constant.APP_KEY); log.error("排序後的拼接參數:" + sb.toString()); return CommonUtil.getMD5(sb.toString().trim()).toUpperCase(); } }
小程序端經過wx.request發起網絡請求,經過服務器發起預支付,獲取prepayId以及其餘支付須要簽名的參數後,利用wx.requestPayment發起支付。
// 1. 完成頁面結構、佈局、樣式 // 2. 設計數據結構 // 3. 完成數據綁定 // 4. 設計交互操做事件 // 5. 數據存儲 var app = getApp() //實例化小程序,從而獲取全局數據或者使用全局函數 // console.log(app.globalData) var MD5Util = require('../../utils/md5.js'); Page({ // ===== 頁面數據對象 ===== data: { input: '', todos: [], leftCount: 0, allCompleted: false, logs: [], price: 0.01, number: 18820000000, deviceNo: 10080925 }, // ===== 頁面生命週期方法 ===== onLoad: function () { }, // ===== 事件處理函數 ===== wxPay: function (e) { var code = '' //傳給服務器以得到openId var timestamp = String(Date.parse(new Date())) //時間戳 var nonceStr = '' //隨機字符串,後臺返回 var prepayId = '' //預支付id,後臺返回 var paySign = '' //加密字符串 //獲取用戶登陸狀態 wx.login({ success: function (res) { if (res.code) { code = res.code //發起網絡請求,發起的是HTTPS請求,向服務端請求預支付 wx.request({ url: 'http://sellbin.natapp1.cc/prepay', data: { code: res.code }, success: function (res) { console.log(res.data); if (res.data.result == true) { nonceStr = res.data.nonceStr prepayId = res.data.prepayId // 按照字段首字母排序組成新字符串 var payDataA = "appId=" + app.globalData.appId + "&nonceStr=" + res.data.nonceStr + "&package=prepay_id=" + res.data.prepayId + "&signType=MD5&timeStamp=" + timestamp; var payDataB = payDataA + "&key=" + app.globalData.key; // 使用MD5加密算法計算加密字符串 paySign = MD5Util.MD5(payDataB).toUpperCase(); // 發起微信支付 wx.requestPayment({ 'timeStamp': timestamp, 'nonceStr': nonceStr, 'package': 'prepay_id=' + prepayId, 'signType': 'MD5', 'paySign': paySign, 'success': function (res) { // 保留當前頁面,跳轉到應用內某個頁面,使用wx.nevigeteBack能夠返回原頁面 wx.navigateTo({ url: '../pay/pay' }) }, 'fail': function (res) { console.log(res.errMsg) } }) } else { console.log('請求失敗' + res.data.info) } } }) } else { console.log('獲取用戶登陸態失敗!' + res.errMsg) } } }); }, formSubmit: function (e) { console.log('form發生了submit事件,攜帶數據爲:', e.detail.value) }, formReset: function () { console.log('form發生了reset事件') } })
接下來,咱們貼一下微信退款相關的代碼RefundController
package luluteam.wxpay.controller; import luluteam.wxpay.constant.Constant; import luluteam.wxpay.entity.WxRefundInfoEntity; import luluteam.wxpay.service.WxRefundInfoService; import luluteam.wxpay.util.common.PayUtils; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import java.text.DecimalFormat; import java.util.*; @Controller public class RefundController extends HttpServlet { private static Logger log = Logger.getLogger(PayController.class); @Autowired private WxRefundInfoService wxRefundInfoService; @RequestMapping(params = "refund", method = RequestMethod.POST) @Transactional public @ResponseBody Map<String, Object> refund(String openid, String orderId, HttpServletRequest request) { Map<String, Object> result = new HashMap<String, Object>(); String currTime = PayUtils.getCurrTime(); String strTime = currTime.substring(8, currTime.length()); String strRandom = PayUtils.buildRandom(4) + ""; String nonceStr = strTime + strRandom; String outRefundNo = "wx@re@" + PayUtils.getTimeStamp(); String outTradeNo = ""; String transactionId = ""; String unionId = openid; String appid = Constant.APP_ID; String mchid = Constant.MCH_ID; String key = Constant.APP_KEY;//mch_key // String key = ResourceUtil.getConfigByName("wx.application.mch_key"); if (StringUtils.isNotEmpty(orderId)) { int total_fee = 1; //商戶側傳給微信的訂單號32位 outTradeNo = "115151sdasdsadsadsadas"; DecimalFormat df = new DecimalFormat("0.00"); //String fee = String.valueOf(df.format((float)total_fee/100)); String fee = String.valueOf(total_fee); SortedMap<String, String> packageParams = new TreeMap<String, String>(); packageParams.put("appid", appid); packageParams.put("mch_id", mchid);//微信支付分配的商戶號 packageParams.put("nonce_str", nonceStr);//隨機字符串,不長於32位 packageParams.put("op_user_id", mchid);//操做員賬號, 默認爲商戶號 //out_refund_no只能含有數字、字母和字符_-|*@ packageParams.put("out_refund_no", outRefundNo);//商戶系統內部的退款單號,商戶系統內部惟一,同一退款單號屢次請求只退一筆 packageParams.put("out_trade_no", outTradeNo);//商戶側傳給微信的訂單號32位 packageParams.put("refund_fee", fee); packageParams.put("total_fee", fee); packageParams.put("transaction_id", transactionId);//微信生成的訂單號,在支付通知中有返回 String sign = PayUtils.createSign(packageParams, key); String refundUrl = "https://api.mch.weixin.qq.com/secapi/pay/refund"; String xmlParam = "<xml>" + "<appid>" + appid + "</appid>" + "<mch_id>" + mchid + "</mch_id>" + "<nonce_str>" + nonceStr + "</nonce_str>" + "<op_user_id>" + mchid + "</op_user_id>" + "<out_refund_no>" + outRefundNo + "</out_refund_no>" + "<out_trade_no>" + outTradeNo + "</out_trade_no>" + "<refund_fee>" + fee + "</refund_fee>" + "<total_fee>" + fee + "</total_fee>" + "<transaction_id>" + transactionId + "</transaction_id>" + "<sign>" + sign + "</sign>" + "</xml>"; log.info("---------xml返回:" + xmlParam); String resultStr = PayUtils.post(refundUrl, xmlParam); log.info("---------退款返回:" + resultStr); //解析結果 try { Map map = PayUtils.doXMLParse(resultStr); String returnCode = map.get("return_code").toString(); if (returnCode.equals("SUCCESS")) { String resultCode = map.get("result_code").toString(); if (resultCode.equals("SUCCESS")) { //保存退款記錄,可在數據庫建一個退款表記錄 WxRefundInfoEntity refundInfoEntity = new WxRefundInfoEntity(); refundInfoEntity.setCreateDate(new Date()); refundInfoEntity.setAppid(appid); refundInfoEntity.setMchId(mchid); refundInfoEntity.setNonceStr(nonceStr); refundInfoEntity.setSign(sign); refundInfoEntity.setOutRefundNo(outRefundNo); refundInfoEntity.setOutTradeNo(outTradeNo); refundInfoEntity.setTotalFee(total_fee); refundInfoEntity.setRefundFee(total_fee); refundInfoEntity.setUnionid(unionId); wxRefundInfoService.save(refundInfoEntity); result.put("status", "success"); } else { result.put("status", "fail"); } } else { result.put("status", "fail"); } } catch (Exception e) { e.printStackTrace(); result.put("status", "fail"); } } return result; } }
支付與退款有關的工具類和實體類一併貼在下面,若是有不清楚的,能夠去個人github上面下載源碼:
https://github.com/wenbingshen/wechatpay
entity包:
package luluteam.wxpay.entity; import java.io.Serializable; public class PayInfo implements Serializable { private String appid; private String mch_id; private String device_info; //設備號,小程序傳"WEB" private String nonce_str; private String sign; private String sign_type; //簽名類型 private String body; //private String detail; private String attach; private String out_trade_no; private int total_fee; private String spbill_create_ip; private String time_start; private String time_expire; private String notify_url; private String trade_type; //交易類型,JSAPI private String limit_pay; //指定支付方式,no_credit private String openid; public String getAppid() { return appid; } public void setAppid(String appid) { this.appid = appid; } public String getMch_id() { return mch_id; } public void setMch_id(String mch_id) { this.mch_id = mch_id; } public String getDevice_info() { return device_info; } public void setDevice_info(String device_info) { this.device_info = device_info; } public String getNonce_str() { return nonce_str; } public void setNonce_str(String nonce_str) { this.nonce_str = nonce_str; } public String getSign() { return sign; } public void setSign(String sign) { this.sign = sign; } public String getSign_type() { return sign_type; } public void setSign_type(String sign_type) { this.sign_type = sign_type; } public String getBody() { return body; } public void setBody(String body) { this.body = body; } public String getAttach() { return attach; } public void setAttach(String attach) { this.attach = attach; } public String getOut_trade_no() { return out_trade_no; } public void setOut_trade_no(String out_trade_no) { this.out_trade_no = out_trade_no; } public int getTotal_fee() { return total_fee; } public void setTotal_fee(int total_fee) { this.total_fee = total_fee; } public String getSpbill_create_ip() { return spbill_create_ip; } public void setSpbill_create_ip(String spbill_create_ip) { this.spbill_create_ip = spbill_create_ip; } public String getTime_start() { return time_start; } public void setTime_start(String time_start) { this.time_start = time_start; } public String getTime_expire() { return time_expire; } public void setTime_expire(String time_expire) { this.time_expire = time_expire; } public String getNotify_url() { return notify_url; } public void setNotify_url(String notify_url) { this.notify_url = notify_url; } public String getTrade_type() { return trade_type; } public void setTrade_type(String trade_type) { this.trade_type = trade_type; } public String getLimit_pay() { return limit_pay; } public void setLimit_pay(String limit_pay) { this.limit_pay = limit_pay; } public String getOpenid() { return openid; } public void setOpenid(String openid) { this.openid = openid; } }
WxRefundInfoEntity類,用來退款後向數據添加退款的記錄,方面查帳:
package luluteam.wxpay.entity; import javax.persistence.Entity; import java.io.Serializable; import java.util.Date; @Entity public class WxRefundInfoEntity implements Serializable { private Date createDate; private String appid; private String mchId; private String nonceStr; private String sign; private String outRefundNo; private String outTradeNo; private int totalFee; private int refundFee; private String unionid; public Date getCreateDate() { return createDate; } public void setCreateDate(Date createDate) { this.createDate = createDate; } public String getAppid() { return appid; } public void setAppid(String appid) { this.appid = appid; } public String getMchId() { return mchId; } public void setMchId(String mchId) { this.mchId = mchId; } public String getNonceStr() { return nonceStr; } public void setNonceStr(String nonceStr) { this.nonceStr = nonceStr; } public String getSign() { return sign; } public void setSign(String sign) { this.sign = sign; } public String getOutRefundNo() { return outRefundNo; } public void setOutRefundNo(String outRefundNo) { this.outRefundNo = outRefundNo; } public String getOutTradeNo() { return outTradeNo; } public void setOutTradeNo(String outTradeNo) { this.outTradeNo = outTradeNo; } public int getTotalFee() { return totalFee; } public void setTotalFee(int totalFee) { this.totalFee = totalFee; } public int getRefundFee() { return refundFee; } public void setRefundFee(int refundFee) { this.refundFee = refundFee; } public String getUnionid() { return unionid; } public void setUnionid(String unionid) { this.unionid = unionid; } }
util包:
package luluteam.wxpay.util.common; import java.security.MessageDigest; public class MD5 { private final static String[] hexDigits = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"}; /** * 轉換字節數組爲16進制字串 * @param b 字節數組 * @return 16進制字串 */ public static String byteArrayToHexString(byte[] b) { StringBuilder resultSb = new StringBuilder(); for (byte aB : b) { resultSb.append(byteToHexString(aB)); } return resultSb.toString(); } /** * 轉換byte到16進制 * @param b 要轉換的byte * @return 16進制格式 */ private static String byteToHexString(byte b) { int n = b; if (n < 0) { n = 256 + n; } int d1 = n / 16; int d2 = n % 16; return hexDigits[d1] + hexDigits[d2]; } /** * MD5編碼 * @param origin 原始字符串 * @return 通過MD5加密以後的結果 */ public static String MD5Encode(String origin) { String resultString = null; try { resultString = origin; MessageDigest md = MessageDigest.getInstance("MD5"); resultString = byteArrayToHexString(md.digest(resultString.getBytes("utf-8"))); } catch (Exception e) { e.printStackTrace(); } return resultString; } }
package luluteam.wxpay.util.common; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.security.KeyStore; import java.security.MessageDigest; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import javax.net.ssl.SSLContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import luluteam.wxpay.constant.Constant; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.ssl.SSLContexts; import org.apache.http.util.EntityUtils; import org.jdom.Document; import org.jdom.Element; import org.jdom.input.SAXBuilder; @SuppressWarnings("deprecation") public class PayUtils { private static Object Server; @SuppressWarnings("deprecation") public static DefaultHttpClient httpclient; private static SortedMap parameters; static { httpclient = new DefaultHttpClient(); // httpclient = (DefaultHttpClient) HttpClientConnectionManager.getSSLInstance(httpclient); parameters = new TreeMap(); } public static final String KEY_PATH = "E:/wxzf/cert/apiclient_cert.p12"; /** * 把對象轉換成字符串 * * @param obj * @return String 轉換成字符串,若對象爲null,則返回空字符串. */ public static String toString(Object obj) { if (obj == null) return ""; return obj.toString(); } /** * 把對象轉換爲int數值. * * @param obj * 包含數字的對象. * @return int 轉換後的數值,對不能轉換的對象返回0。 */ public static int toInt(Object obj) { int a = 0; try { if (obj != null) { a = Integer.parseInt(obj.toString()); } } catch (Exception e) { e.printStackTrace(); } return a; } /** * 獲取從1970年開始到如今的秒數 * * @param * @return */ public static String getTimeStamp() { long seconds = System.currentTimeMillis() / 1000; return String.valueOf(seconds); } /** * 獲取當前時間 yyyyMMddHHmmss * @return String */ public static String getCurrTime() { Date now = new Date(); SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss"); String s = outFormat.format(now); return s; } /** * 獲取當前日期 yyyyMMdd * @param date * @return String */ public static String formatDate(Date date) { SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd"); String strDate = formatter.format(date); return strDate; } /** * 取出一個指定長度大小的隨機正整數. * @param length int 設定所取出隨機數的長度。length小於11 * @return int 返回生成的隨機數。 */ 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)); } /** * 獲取編碼字符集 * @param request * @param response * @return String */ public static String getCharacterEncoding(HttpServletRequest request, HttpServletResponse response) { if (null == request || null == response) { return "utf-8"; } String enc = request.getCharacterEncoding(); if (null == enc || "".equals(enc)) { enc = response.getCharacterEncoding(); } if (null == enc || "".equals(enc)) { enc = "utf-8"; } return enc; } public static String URLencode(String content) { String URLencode; URLencode = replace(Server.equals(content), "+", "%20"); return URLencode; } private static String replace(boolean equals, String string, String string2) { return null; } /** * 獲取unix時間,從1970-01-01 00:00:00開始的秒數 * @param date * @return long */ public static long getUnixTime(Date date) { if (null == date) { return 0; } return date.getTime() / 1000; } public static String QRfromGoogle(String chl) { int widhtHeight = 300; String EC_level = "L"; int margin = 0; String QRfromGoogle; chl = URLencode(chl); QRfromGoogle = "http://chart.apis.google.com/chart?chs=" + widhtHeight + "x" + widhtHeight + "&cht=qr&chld=" + EC_level + "|" + margin + "&chl=" + chl; return QRfromGoogle; } /** * 時間轉換成字符串 * @param date 時間 * @param formatType 格式化類型 * @return String */ public static String date2String(Date date, String formatType) { SimpleDateFormat sdf = new SimpleDateFormat(formatType); return sdf.format(date); } /** * 建立簽名SHA1 * @param signParams * @return * @throws Exception */ public static String createSHA1Sign(SortedMap<String, String> signParams) throws Exception { StringBuffer sb = new StringBuffer(); Set es = signParams.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(); sb.append(k + "=" + v + "&"); // 要採用URLENCODER的原始值! } String params = sb.substring(0, sb.lastIndexOf("&")); return getSha1(params); } /** * Sha1簽名 * @param str * @return */ public static String getSha1(String str) { if (str == null || str.length() == 0) { return null; } char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; try { MessageDigest mdTemp = MessageDigest.getInstance("SHA1"); mdTemp.update(str.getBytes("UTF-8")); byte[] md = mdTemp.digest(); int j = md.length; char buf[] = new char[j * 2]; int k = 0; for (int i = 0; i < j; i++) { byte byte0 = md[i]; buf[k++] = hexDigits[byte0 >>> 4 & 0xf]; buf[k++] = hexDigits[byte0 & 0xf]; } return new String(buf); } catch (Exception e) { e.printStackTrace(); return null; } } /** * 得到預支付訂單號 * @param url * @param xmlParam * @return */ public static String getPayNo(String url, String xmlParam) { String prepay_id = ""; try { String jsonStr = postWithXmlParams(url, xmlParam); if (jsonStr.indexOf("FAIL") != -1) { return prepay_id; } Map<String, Object> map = doXMLParse(jsonStr); prepay_id = (String) map.get("prepay_id"); System.out.println("prepay_id:" + prepay_id); } catch (Exception e) { e.printStackTrace(); } return prepay_id; } /** * 發送請求 * @param url 請求路徑 * @param xmlParams xml字符串 * @return */ public static String postWithXmlParams(String url, String xmlParams) { HttpPost httpost = new HttpPost(url); try { httpost.setEntity(new StringEntity(xmlParams, "UTF-8")); HttpResponse response = httpclient.execute(httpost); return EntityUtils.toString(response.getEntity(), "UTF-8"); } catch (Exception e) { e.printStackTrace(); return ""; } } /** * 解析xml,返回第一級元素鍵值對。若是第一級元素有子節點,則此節點的值是子節點的xml數據。 * @param strxml * @return * @throws IOException */ public static Map doXMLParse(String strxml) throws Exception { if (null == strxml || "".equals(strxml)) { return null; } Map m = new HashMap(); InputStream in = String2Inputstream(strxml); 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 = PayUtils.getChildrenText(children); } m.put(k, v); } // 關閉流 in.close(); return m; } /** * 獲取子結點的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(PayUtils.getChildrenText(list)); } sb.append(value); sb.append("</" + name + ">"); } } return sb.toString(); } public static InputStream String2Inputstream(String str) { return new ByteArrayInputStream(str.getBytes()); } public String getParameter(String parameter) { String s = (String) this.parameters.get(parameter); return (null == s) ? "" : s; } /** * 特殊字符處理 * @param src * @return * @throws UnsupportedEncodingException */ public String UrlEncode(String src) throws UnsupportedEncodingException { return URLEncoder.encode(src, "UTF-8").replace("+", "%20"); } /** * 獲取package的簽名包 * @param packageParams * @param key * @return * @throws UnsupportedEncodingException */ public String genPackage(SortedMap<String, String> packageParams, String key) throws UnsupportedEncodingException { String sign = createSign(packageParams, 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(); sb.append(k + "=" + UrlEncode(v) + "&"); } // 去掉最後一個& String packageValue = sb.append("sign=" + sign).toString(); return packageValue; } /** * 建立md5摘要,規則是:按參數名稱a-z排序,遇到空值的參數不參加簽名。 */ public static String createSign(SortedMap<String, String> packageParams, String 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=" + key); System.out.println("md5:" + sb.toString()); String sign = MD5.MD5Encode(sb.toString()).toUpperCase(); System.out.println("packge簽名:" + sign); return sign; } /** * 建立package簽名 */ public boolean createMd5Sign(String signParams) { StringBuffer sb = new StringBuffer(); Set es = this.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 (!"sign".equals(k) && null != v && !"".equals(v)) { sb.append(k + "=" + v + "&"); } } // 算出摘要 String sign = MD5.MD5Encode(sb.toString()).toUpperCase(); String paySign = this.getParameter("sign").toLowerCase(); return paySign.equals(sign); } /** * 輸出XML * @return */ public String parseXML() { StringBuffer sb = new StringBuffer(); sb.append("<xml>"); Set es = this.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 (null != v && !"".equals(v) && !"appkey".equals(k)) { sb.append("<" + k + ">" + getParameter(k) + "</" + k + ">\n"); } } sb.append("</xml>"); return sb.toString(); } public static String post(String url, String xmlParam) { StringBuilder sb = new StringBuilder(); try { KeyStore keyStore = KeyStore.getInstance("PKCS12"); FileInputStream instream = new FileInputStream(new File(KEY_PATH)); // String mchid = ResourceUtil.getConfigByName("wx.application.mch_id"); String mchid = Constant.MCH_ID; try { keyStore.load(instream, mchid.toCharArray()); } finally { instream.close(); } // 證書 SSLContext sslcontext = SSLContexts.custom() .loadKeyMaterial(keyStore, mchid.toCharArray()) .build(); // 只容許TLSv1協議 SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( sslcontext, new String[]{"TLSv1"}, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); //建立基於證書的httpClient,後面要用到 CloseableHttpClient client = HttpClients.custom() .setSSLSocketFactory(sslsf) .build(); HttpPost httpPost = new HttpPost(url);//退款接口 StringEntity reqEntity = new StringEntity(xmlParam); // 設置類型 reqEntity.setContentType("application/x-www-form-urlencoded"); httpPost.setEntity(reqEntity); CloseableHttpResponse response = client.execute(httpPost); try { HttpEntity entity = response.getEntity(); System.out.println(response.getStatusLine()); if (entity != null) { BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent(), "UTF-8")); String text = ""; while ((text = bufferedReader.readLine()) != null) { sb.append(text); } } EntityUtils.consume(entity); } catch (Exception e) { e.printStackTrace(); } finally { try { response.close(); } catch (IOException e) { e.printStackTrace(); } } /*try { HttpGet httpget = new HttpGet("https://api.mch.weixin.qq.com/secapi/pay/refund"); System.out.println("executing request" + httpget.getRequestLine()); CloseableHttpResponse response = httpclient.execute(httpget); try { HttpEntity entity = response.getEntity(); System.out.println("----------------------------------------"); System.out.println(response.getStatusLine()); if (entity != null) { System.out.println("Response content length: " + entity.getContentLength()); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent())); String text; while ((text = bufferedReader.readLine()) != null) { System.out.println(text); sb.append(text); } } EntityUtils.consume(entity); } finally { response.close(); } } finally { httpclient.close(); }*/ } catch (Exception e) { e.printStackTrace(); } return sb.toString(); } }
package luluteam.wxpay.util; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.core.util.QuickWriter; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import com.thoughtworks.xstream.io.xml.PrettyPrintWriter; import com.thoughtworks.xstream.io.xml.XppDriver; import luluteam.wxpay.entity.PayInfo; import org.apache.commons.lang3.StringUtils; import org.dom4j.Document; import org.dom4j.DocumentHelper; import org.dom4j.Element; import javax.servlet.http.HttpServletRequest; import java.io.*; import java.math.BigInteger; import java.security.MessageDigest; import java.util.*; /** * Created by Hyman on 2017/2/28. */ public class CommonUtil { public static String getRandomOrderId() { // UUID.randomUUID().toString().replace("-","") Random random = new Random(System.currentTimeMillis()); int value = random.nextInt(); while (value < 0) { value = random.nextInt(); } return value + ""; } private static XStream xstream = new XStream(new XppDriver() { public HierarchicalStreamWriter createWriter(Writer out) { return new PrettyPrintWriter(out) { //增長CDATA標記 boolean cdata = true; @SuppressWarnings("rawtypes") public void startNode(String name, Class clazz) { super.startNode(name, clazz); } protected void writeText(QuickWriter writer, String text) { if (cdata) { writer.write("<![CDATA["); writer.write(text); writer.write("]]>"); } else { writer.write(text); } } }; } }); public static String payInfoToXML(PayInfo pi) { xstream.alias("xml", pi.getClass()); return xstream.toXML(pi); } @SuppressWarnings("unchecked") public static Map<String, String> parseXml(String xml) throws Exception { Map<String, String> map = new HashMap<String, String>(); Document document = DocumentHelper.parseText(xml); Element root = document.getRootElement(); List<Element> elementList = root.elements(); for (Element e : elementList) map.put(e.getName(), e.getText()); return map; } public static String getClientIp(HttpServletRequest request) { String ip = request.getHeader("X-Forwarded-For"); if(StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)){ //屢次反向代理後會有多個ip值,第一個ip纔是真實ip int index = ip.indexOf(","); if(index != -1){ return ip.substring(0,index); }else{ return ip; } } ip = request.getHeader("X-Real-IP"); if(StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)){ return ip; } return request.getRemoteAddr(); } /** * 對字符串md5加密 * * @param str * @return */ public static String getMD5(String str) throws Exception { try { // 生成一個MD5加密計算摘要 MessageDigest md = MessageDigest.getInstance("MD5"); // 計算md5函數 md.update(str.getBytes()); // digest()最後肯定返回md5 hash值,返回值爲8爲字符串。由於md5 hash值是16位的hex值,實際上就是8位的字符 // BigInteger函數則將8位的字符串轉換成16位hex值,用字符串來表示;獲得字符串形式的hash值 return new BigInteger(1, md.digest()).toString(16); } catch (Exception e) { throw new Exception("MD5加密出現錯誤"); } } }
package luluteam.wxpay.util; import java.util.HashMap; import java.util.List; import org.apache.http.Header; import org.apache.http.cookie.Cookie; public class HttpResult { private List<Cookie> cookies; private HashMap<String, Header> headers; private int statusCode; private String body; public List<Cookie> getCookies() { return cookies; } public void setCookies(List<Cookie> cookies) { this.cookies = cookies; } public HashMap<String, Header> getHeaders() { return headers; } public void setHeaders(Header[] headerAll) { headers = new HashMap<String, Header>(); for (Header header : headerAll) { headers.put(header.getName(), header); } } public int getStatusCode() { return statusCode; } public void setStatusCode(int statusCode) { this.statusCode = statusCode; } public String getBody() { return body; } public void setBody(String body) { this.body = body; } @Override public String toString() { StringBuffer sb = new StringBuffer(); sb.append("======================= HttpResult toString start ========================\n"); sb.append("----- statusCode: " + statusCode + "\n"); if(headers != null) { sb.append("----- headers:\n"); for(String key : headers.keySet()) { sb.append("\t" + key + " : " + headers.get(key) + "\n"); } } if(cookies != null) { sb.append("----- cookies:\n"); for(Cookie cookie : cookies) { sb.append("\t" + cookie.getName() + " : " + cookie.getValue() + "\n"); } } sb.append("======================= body start ========================\n"); sb.append(body); sb.append("======================= body end ========================\n"); sb.append("======================= HttpResult toString end ======================="); return sb.toString(); } public String getCookieValue(String cookieName) { if(cookies.isEmpty()) { return null; } for(Cookie cookie : cookies) { if(cookie.getName().equals(cookieName)) { return cookie.getValue(); } } return null; } }
package luluteam.wxpay.util; import java.io.*; import java.net.URL; import java.security.KeyManagementException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.apache.http.Consts; import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.SSLContextBuilder; import org.apache.http.conn.ssl.TrustSelfSignedStrategy; import org.apache.http.cookie.Cookie; import org.apache.http.impl.client.BasicCookieStore; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.message.BasicHeader; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; import javax.net.ssl.HttpsURLConnection; public class HttpUtil { // User-Agent public static final String USERAGENT_FIREFOX = "Mozilla/5.0 (Windows NT 6.3; WOW64; rv:39.0) Gecko/20100101 Firefox/39.0"; public static final String USERAGENT_IE = "Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko"; private CloseableHttpClient httpClient; private BasicCookieStore cookieStore; private HttpGet get; private HttpPost post; public static StringBuffer httpsRequest(String requestUrl, String requestMethod, String output) throws IOException { URL url = new URL(requestUrl); HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); connection.setDoOutput(true); connection.setDoInput(true); connection.setUseCaches(false); connection.setRequestMethod(requestMethod); if (null != output) { OutputStream outputStream = connection.getOutputStream(); outputStream.write(output.getBytes("UTF-8")); outputStream.close(); } // 從輸入流讀取返回內容 InputStream inputStream = connection.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String str = null; StringBuffer buffer = new StringBuffer(); while ((str = bufferedReader.readLine()) != null) { buffer.append(str); } bufferedReader.close(); inputStreamReader.close(); inputStream.close(); inputStream = null; connection.disconnect(); return buffer; } public HttpResult doGet(String url, Map<String, String> headers, Map<String, String> params) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException, ClientProtocolException, IOException { if (url == null|| url.equals("")) { return null; } SSLContextBuilder builder = new SSLContextBuilder(); builder.loadTrustMaterial(null, new TrustSelfSignedStrategy()); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build()); cookieStore = new BasicCookieStore(); CloseableHttpClient httpclient = HttpClients.custom().setDefaultCookieStore(cookieStore) .setSSLSocketFactory(sslsf).build(); HttpResult result = null; try { url = url + "?" + parseParams(params); HttpGet httpget = new HttpGet(url); httpget.setHeaders(parseHeader(headers)); CloseableHttpResponse response = httpclient.execute(httpget); try { HttpEntity entity = response.getEntity(); if (entity != null) { result = new HttpResult(); result.setCookies(cookieStore.getCookies()); result.setStatusCode(response.getStatusLine().getStatusCode()); result.setHeaders(response.getAllHeaders()); result.setBody(EntityUtils.toString(entity)); } } finally { response.close(); } } finally { httpclient.close(); } return result; } public HttpResult doPost(String url, Map<String, String> headers, Map<String, String> postData, String encoding) throws Exception { if (url == null|| url.equals("")) { return null; } if (encoding == null|| encoding.equals("")) { encoding = "utf-8"; } SSLContextBuilder builder = new SSLContextBuilder(); builder.loadTrustMaterial(null, new TrustSelfSignedStrategy()); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build()); cookieStore = new BasicCookieStore(); CloseableHttpClient httpClient = HttpClients.custom().setDefaultCookieStore(cookieStore) .setSSLSocketFactory(sslsf).build(); post = new HttpPost(url); List<NameValuePair> list = new ArrayList<NameValuePair>(); for (String tmp : postData.keySet()) { list.add(new BasicNameValuePair(tmp, postData.get(tmp))); } post.setEntity(new UrlEncodedFormEntity(list, encoding)); post.setHeaders(parseHeader(headers)); CloseableHttpResponse response = httpClient.execute(post); HttpEntity entity = response.getEntity(); HttpResult result = new HttpResult(); result.setCookies(cookieStore.getCookies()); result.setStatusCode(response.getStatusLine().getStatusCode()); result.setHeaders(response.getAllHeaders()); result.setBody(EntityUtils.toString(entity, encoding)); close(entity, response); return result; } private String parseParams(Map<String, String> params) { if (params == null || params.isEmpty()) { return ""; } StringBuffer sb = new StringBuffer(); for (String key : params.keySet()) { sb.append(key + "=" + params.get(key) + "&"); } return sb.substring(0, sb.length() - 1); } private Header[] parseHeader(Map<String, String> headers) { if (headers == null || headers.isEmpty()) { return getDefaultHeaders(); } Header[] retHeader = new BasicHeader[headers.size()]; int i = 0; for (String str : headers.keySet()) { retHeader[i++] = new BasicHeader(str, headers.get(str)); } return retHeader; } private Header[] getDefaultHeaders() { Header[] headers = new BasicHeader[3]; headers[0] = new BasicHeader("User-Agent", USERAGENT_IE); headers[1] = new BasicHeader("Accept-Encoding", "gzip, deflate"); headers[2] = new BasicHeader("Accept-Language", "en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3"); return headers; } private void close(HttpEntity entity, CloseableHttpResponse response) { try { if (entity != null) { InputStream input = entity.getContent(); input.close(); } } catch (Exception e) { e.printStackTrace(); } finally { try { response.close(); } catch (IOException e) { //e.printStackTrace(); } } } /** * 下載文件 * @param url 下載文件的連接 * @param destFile 包含路徑的目標文件名 * @param headers 請求頭 * @return */ public HttpResult downloadFile(String url, String destFile, Map<String, String> headers) throws Exception { SSLContextBuilder builder = new SSLContextBuilder(); builder.loadTrustMaterial(null, new TrustSelfSignedStrategy()); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build()); BasicCookieStore cookieStore = new BasicCookieStore(); CloseableHttpClient httpclient = HttpClients.custom().setDefaultCookieStore(cookieStore).setSSLSocketFactory(sslsf).build(); HttpGet get = new HttpGet(url); get.setHeaders(parseHeader(headers)); InputStream input = null; CloseableHttpResponse response = null; HttpResult result = null; try { response = httpclient.execute(get); HttpEntity entity = response.getEntity(); input = entity.getContent(); File file = new File(destFile); FileOutputStream fos = new FileOutputStream(file); int len = -1; byte[] tmp = new byte[1024]; while((len=input.read(tmp)) != -1) { fos.write(tmp, 0, len); } fos.flush(); fos.close(); result = new HttpResult(); result.setCookies(cookieStore.getCookies()); result.setStatusCode(response.getStatusLine().getStatusCode()); result.setHeaders(response.getAllHeaders()); result.setBody(EntityUtils.toString(entity, Consts.UTF_8)); } catch (IOException e) { e.printStackTrace(); } finally { try { if(input != null) { input.close(); } if(response != null) { response.close(); } } catch (IOException e) { e.printStackTrace(); } } return result; } }
package luluteam.wxpay.util; import java.util.Random; /** * 隨機數、隨即字符串工具 */ public class RandomUtils { public static final String allChar = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; public static final String letterChar = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; public static final String numberChar = "0123456789"; /** * 返回一個定長的隨機字符串(只包含大小寫字母、數字) * * @param length 隨機字符串長度 * @return 隨機字符串 */ public static String generateString(int length) { StringBuffer sb = new StringBuffer(); Random random = new Random(); for (int i = 0; i < length; i++) { sb.append(allChar.charAt(random.nextInt(allChar.length()))); } return sb.toString(); } /** * 返回一個定長的隨機純字母字符串(只包含大小寫字母) * * @param length 隨機字符串長度 * @return 隨機字符串 */ public static String generateMixString(int length) { StringBuffer sb = new StringBuffer(); Random random = new Random(); for (int i = 0; i < length; i++) { sb.append(allChar.charAt(random.nextInt(letterChar.length()))); } return sb.toString(); } /** * 返回一個定長的隨機純小寫字母字符串(只包含小寫字母) * * @param length 隨機字符串長度 * @return 隨機字符串 */ public static String generateLowerString(int length) { return generateMixString(length).toLowerCase(); } /** * 返回一個定長的隨機純大寫字母字符串(只包含大寫字母) * * @param length 隨機字符串長度 * @return 隨機字符串 */ public static String generateUpperString(int length) { return generateMixString(length).toUpperCase(); } public static void main(String[] args) { System.out.println(generateString(32)); System.out.println(generateMixString(32)); System.out.println(generateLowerString(32)); System.out.println(generateUpperString(32)); } }
這篇博客的主要意義是給你們提供一個開發小程序微信支付和微信退款的思路,上面的支付代碼和退款代碼在我看來寫的很難看,你們有想法的能夠本身簡化,多作一些處理,也能夠利用一些較好sdk簡化開發,凡是對代碼有問題的,能夠去個人github下載源碼(配有小程序源碼):https://github.com/wenbingshen/wechatpay
學習微信支付在理解了微信支付開發文檔之後,利用一些優秀的開源sdk是幫助咱們開發的最好利器,個人公衆號支付開發就利用優秀的sdk,有想學習創建開發思路的,能夠戳下面的連接下載源碼:
https://github.com/wenbingshen/springboot
微信支付的開發文檔對java語言真的很不利,有時候利用一些前人的成果能夠有效簡化咱們的開發任務。
祝你們學習快樂。