1.授權登錄需要獲取到用戶的唯一標識openid以及session_key會話密鑰 /** * @Description: 獲取openid用戶唯一標識以及session_key會話密鑰 */ @PostMapping("/getOpenId") public Map<String,Object> getOpenId(String code){ Map<String,Object> map = new HashMap<>(); //登錄憑證不能爲空 if (null == code || code.length() == 0){ map.put("errMessage","code 不能爲空!"); return map; } //1.向微信服務器 試用登錄憑證 code 獲取session_key 和openid //請求參數 Map<String,String> params = new HashMap<>(); params.put("appid",StringInfo.APPID); params.put("secret",StringInfo.APPSECRET); params.put("js_code",code); params.put("grant_type","authorization_code"); String result = HttpClientUtil.doGet("https://api.weixin.qq.com/sns/jscode2session?",params); JSONObject json = JSON.parseObject(result); System.out.println(json); if (json != null) { //當請求成功 if (json.getString("errcode")==null) { map.put("openid",json.getString("openid"));//用戶的唯一標識 map.put("sessionKey",json.getString("session_key"));//會話密鑰 if (json.getString("unionid") != null) { map.put("unionid", json.getString("unionid"));//用戶在開放平臺的唯一標識符 } }else{ //當請求不成功的時候,將錯誤信息 map.put("errMessage",json.getString("errmsg")); } } System.out.println(JSON.toJSON(map)); return map; }
2.獲取AccessToken
/**
* @Description: 獲取AccessToken
*/
@PostMapping("/getAccessToken")
public Map<String,Object> getAccessToken(){
Map<String,Object> map = new HashMap<>();
Map<String,String> params = new HashMap<>();
//請求參數
params.put("grant_type","client_credential");
params.put("appid", StringInfo.APPID);
params.put("secret",StringInfo.APPSECRET);
//向微信服務端發起請求
String result = HttpClientUtil.doGet("https://api.weixin.qq.com/cgi-bin/token?",params);
//將結果轉爲json對象
JSONObject json = JSON.parseObject(result);
if (json != null) {
if (json.getString("errcode") == null){
map.put("accessToken",json.getString("access_token"));
map.put("expiresIn",json.getString("expires_in"));
}else {
map.put("errMessage",json.getString("errmsg"));
}
}
return map;
}
3.微信付款
/** * 支付接口 * @param openid 用戶的唯一標識 * @param body1 商品描述信息 * @param total_fee1 金額 * @return */ @RequestMapping("/WeiXinPay") public @ResponseBody Object WeiXinPay(String openid,String body1,int total_fee1 ) { try { String appid = userService.getUser().getAppid(); // 微信小程序--》“開發者ID” String mch_id = userService.getUser().getMchId(); // 商戶號,將該值賦值給partner String key = userService.getUser().getMchKey(); // 微信支付商戶平臺登錄)--》“API安全”--》“API密鑰”--“設置密鑰”(設置之後的那個值就是partnerkey,32位) LOGGER.debug(appid); LOGGER.debug(mch_id); LOGGER.debug(key); String body = body1; // 描述 int total_fee = total_fee1; // 支付金額 Date data = new Date(); String notify_url = "https://www.baidu.com/weixinpay/notify"; // 自己發佈在公網回調鏈接,不然無法訪問 String out_trade_no = IdUtils.genOrderName();//IdUtils-->見下面 LOGGER.debug(out_trade_no); Map<Object, Object> map = WeiXinAtcion.me.weixinPlay(mch_id, appid, key, openid, total_fee, out_trade_no, notify_url, body); return map; } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return TTResult.fail(); }
6.微信退款
/** * * @param request * @param database * @param out_trade_no * @param all_total_fee * @param refund_fee * @return */ @RequestMapping("/WeiXinRefund") public @ResponseBody Object WeiXinRefund(HttpServletRequest request, @RequestParam("out_trade_no") String out_trade_no, @RequestParam("all_total_fee") int all_total_fee, @RequestParam("refund_fee") int refund_fee) { try { LOGGER.debug("訂單號是--->"+out_trade_no); LOGGER.debug("總金額--->"+all_total_fee); LOGGER.debug("剩餘金額--->"+refund_fee); // out_trade_no 退款訂單號 // all_total_fee 訂單金額 // refund_fee 輸0 all_total_fee-refund_fee=退款的金額 String appid = userService.getUser().getAppid(); // 微信小程序--》“開發者ID” String mch_id = userService.getUser().getMchId(); // 商戶號,將該值賦值給partner String key = userService.getUser().getMchKey(); // 微信支付商戶平臺登錄)--》“API安全”--》“API密鑰”--“設置密鑰”(設置之後的那個值就是partnerkey,32位) Map<String, String> refundmap = WeiXinAtcion.me.wechatRefund( request, mch_id, appid, key, out_trade_no, all_total_fee, refund_fee, userService.getUser().getCert()); if (refundmap.get("return_code").equals("SUCCESS")) { if (refundmap.get("result_code").equals("FAIL")) { LOGGER.debug("退款失敗:原因" + refundmap.get("err_code_des")); } else { LOGGER.debug("退款成功"); return TTResult.ok(); } } else { LOGGER.debug("退款失敗:原因" + refundmap.get("return_ms")); return TTResult.fail(); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return TTResult.fail(); }
5.將調用微信付款、退款封裝
import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import com.mj.mall.user.service.UserService; import com.mj.pay.util.WXUtil; @Controller public class WeiXinAtcion { public static final WeiXinAtcion me = new WeiXinAtcion(); private static final Logger LOGGER = LoggerFactory .getLogger(WeiXinAtcion.class); @Autowired private UserService userService; /** * 退款 */ public static Map<String, String> wechatRefund(HttpServletRequest request, String mch_id, String appid, String key, String out_trade_no, int all_total_fee, int refund_fee, String apiclient_certLocationp12) throws Exception { SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>(); LOGGER.debug("走到退款函數了"); packageParams.put("appid", appid); packageParams.put("mch_id", mch_id); packageParams.put("op_user_id", mch_id); packageParams.put("nonce_str", WXUtil.generate()); packageParams.put("out_trade_no", out_trade_no); packageParams.put("out_refund_no", WXUtil.generate()); packageParams.put("total_fee", String.valueOf(all_total_fee)); packageParams.put("refund_fee", String.valueOf(all_total_fee - refund_fee)); String sign = WXUtil.createSign_ChooseWXPay("UTF-8", packageParams, key); packageParams.put("sign", sign); String XML = WXUtil.getRequestXml(packageParams); LOGGER.debug("退款函數結束,接下來執行退款操作"); return WXUtil.doRefund(request, "https://api.mch.weixin.qq.com/secapi/pay/refund", XML, mch_id, apiclient_certLocationp12); } /** * 生成微信訂單 */ public SortedMap<Object, Object> weixinPlay(String mch_id, String appid, String key, String openid, int total_fee, String out_trade_no, String notify_url, String body) throws UnsupportedEncodingException, DocumentException { SortedMap<Object, Object> paymentPo = new TreeMap<Object, Object>(); paymentPo.put("appid", appid); paymentPo.put("mch_id", mch_id); paymentPo.put("nonce_str", WXUtil.generate()); paymentPo.put("body", body); paymentPo.put("out_trade_no", out_trade_no); paymentPo.put("total_fee", String.valueOf(total_fee)); paymentPo.put("spbill_create_ip", "120.77.171.156");//此處是公網ip paymentPo.put("notify_url", notify_url); paymentPo.put("trade_type", "JSAPI"); paymentPo.put("openid", openid); String sign = WXUtil.createSign_ChooseWXPay("UTF-8", paymentPo, key); paymentPo.put("sign", sign); String param = WXUtil.getRequestXml(paymentPo); ; String request = WXUtil.httpRequest("https://api.mch.weixin.qq.com/pay/unifiedorder", "POST", param); Map<String, String> map = new HashMap<String, String>(); // 將解析結果存儲在HashMap中 InputStream in = new ByteArrayInputStream(request.getBytes()); SAXReader reader = new SAXReader(); // 讀取輸入流 Document document = reader.read(in); Element root = document.getRootElement(); // 得到xml根元素 @SuppressWarnings("unchecked") // 得到根元素的所有子節點 List<Element> elementList = root.elements(); for (Element element : elementList) { map.put(element.getName(), element.getText()); } SortedMap<Object, Object> result = new TreeMap<Object, Object>(); LOGGER.debug("第一次簽名返回碼" + map.get("return_code")); LOGGER.debug("第一次簽名返回結果" + map.get("return_msg")); //第一次簽名成功 if (map.get("return_code").equals("SUCCESS")) { // 業務結果 String nonceStr = WXUtil.generate(); Long timeStamp = System.currentTimeMillis() / 1000; SortedMap<Object, Object> params = new TreeMap<Object, Object>(); params.put("appId", appid); params.put("nonceStr", nonceStr); params.put("package", "prepay_id=" + map.get("prepay_id")); params.put("signType", "MD5"); params.put("timeStamp", timeStamp); //第二次簽名成功 LOGGER.debug("開始第二次簽名"); String paySign = WXUtil.createSign_ChooseWXPay("UTF-8", params, key); result.put("paySign", paySign); result.put("timeStamp", timeStamp + ""); result.put("nonceStr", nonceStr); result.put("out_trade_no", paymentPo.get("out_trade_no")); result.put("package", "prepay_id=" + map.get("prepay_id")); result.put("return_code", "SUCCESS"); } else { result.put("return_code", "Fail"); result.put("return_msg", map.get("return_msg")); } return result; } /** * 支付成功回調函數 */ @RequestMapping("/weixinpay/notify") public void weixinpay_notify(HttpServletRequest request, HttpServletResponse response) throws Exception { 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(); Map<String, String> m = new HashMap<String, String>(); m = WXUtil.doXMLParse(sb.toString()); 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); } //String key = ""; //祕鑰 String key = userService.getUser().getMchKey(); //祕鑰 if (WXUtil.isTenpaySign("UTF-8", packageParams, key)) { String resXml = ""; if ("SUCCESS".equals((String) packageParams.get("result_code"))) { //得到返回的參數 String openid = (String) packageParams.get("openid"); String transaction_id = (String) packageParams.get("transaction_id"); String out_trade_no = (String) packageParams.get("out_trade_no"); String total_fee = (String) packageParams.get("total_fee"); Float fee = Float.parseFloat(total_fee) / 100; //這裏可以寫你需要的業務 System.out.println("openid---->" + openid); System.out.println("transaction_id---->" + transaction_id); System.out.println("out_trade_no---->" + out_trade_no); System.out.println("total_fee---->" + total_fee); System.out.println("fee---->" + fee); resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> "; BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream()); out.write(resXml.getBytes()); out.flush(); out.close(); } else { System.out.println("回調失敗"); } } else { System.out.println("回調失敗"); } } }
8.WXUtil工具類
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.OutputStream; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.URI; import java.net.URL; import java.security.KeyStore; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.SortedMap; import java.util.UUID; import javax.net.ssl.SSLContext; import javax.servlet.http.HttpServletRequest; import org.apache.http.HttpEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.SSLContexts; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.input.SAXBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class WXUtil { private static final Logger LOGGER = LoggerFactory.getLogger(WXUtil.class); /** * 隨機字符串 * @return */ public static String generate() { return UUID.randomUUID().toString().trim().replaceAll("-", ""); } /** * 解析xml,返回第一級元素鍵值對。如果第一級元素有子節點,則此節點的值是子節點的xml數據。 */ 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 = WXUtil.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(WXUtil.getChildrenText(list)); } sb.append(value); sb.append("</" + name + ">"); } } return sb.toString(); } /** * 將請求參數轉換爲xml格式的string字符串,微信服務器接收的是xml格式的字符串 */ public static String getRequestXml(SortedMap<Object, Object> parameters) { StringBuffer sb = new StringBuffer(); sb.append("<xml>"); Set<Entry<Object, Object>> es = parameters.entrySet(); Iterator<Entry<Object, Object>> it = es.iterator(); while (it.hasNext()) { Map.Entry<Object, Object> entry = (Map.Entry<Object, Object>) 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(); } /** * sign簽名,必須使用MD5簽名,且編碼爲UTF-8 */ public static String createSign_ChooseWXPay(String characterEncoding, SortedMap<Object, Object> parameters, String key) { StringBuffer sb = new StringBuffer(); Set<Entry<Object, Object>> es = parameters.entrySet(); Iterator<Entry<Object, Object>> it = es.iterator(); while (it.hasNext()) { Map.Entry<Object, Object> entry = (Map.Entry<Object, Object>) it.next(); String k = (String) entry.getKey(); Object v = entry.getValue(); if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) { sb.append(k + "=" + v + "&"); } } /** 支付密鑰必須參與加密,放在字符串最後面 */ sb.append("key=" + key); String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase(); return sign; } /** * * @param requestUrl 請求地址 * @param requestMethod 請求方法 * @param outputStr 參數 */ public static String httpRequest(String requestUrl,String requestMethod,String outputStr){ // 創建SSLContext StringBuffer buffer=null; try{ URL url = new URL(requestUrl); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod(requestMethod); conn.setDoOutput(true); conn.setDoInput(true); conn.connect(); //往服務器端寫內容 if(null !=outputStr){ OutputStream os=conn.getOutputStream(); os.write(outputStr.getBytes("utf-8")); os.close(); } // 讀取服務器端返回的內容 InputStream is = conn.getInputStream(); InputStreamReader isr = new InputStreamReader(is, "utf-8"); BufferedReader br = new BufferedReader(isr); buffer = new StringBuffer(); String line = null; while ((line = br.readLine()) != null) { buffer.append(line); } }catch(Exception e){ e.printStackTrace(); } return buffer.toString(); } public static String urlEncodeUTF8(String source){ String result=source; try { result=java.net.URLEncoder.encode(source, "UTF-8"); } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } return result; } /** * 退款 */ public static Map<String, String> doRefund(HttpServletRequest request,String url,String data,String partner,String apiclient_certLocation) throws Exception { // p12證書的位置 // 微信公衆平臺:“微信支付”--》“商戶信息”--》“交易數據”--》“詳情請登錄微信支付商戶平臺查看”(登錄)--》“API安全”--》“API證書”--》“下載證書” // 下載證書後將apiclient_cert.p12放在src目錄下面(出於安全考慮,請自行下載自己的證書) KeyStore keyStore = KeyStore.getInstance("PKCS12"); String url2 = request.getSession().getServletContext().getRealPath("/") + "images/cert/" + apiclient_certLocation; LOGGER.debug("url2--->"+url2); File file=new File(url2); LOGGER.debug("new了一個file"); FileInputStream instream = new FileInputStream(file);// P12文件目錄 LOGGER.debug("退款路徑結束"); try { keyStore.load(instream, partner.toCharArray()); } finally { instream.close(); } SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, partner.toCharArray()).build(); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext,new String[] { "TLSv1" }, null,SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build(); try { HttpPost httpost = new HttpPost(url); // 設置響應頭信息 httpost.addHeader("Connection", "keep-alive"); httpost.addHeader("Accept", "*/*"); httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); httpost.addHeader("Host", "api.mch.weixin.qq.com"); httpost.addHeader("X-Requested-With", "XMLHttpRequest"); httpost.addHeader("Cache-Control", "max-age=0"); httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) "); httpost.setEntity(new StringEntity(data, "UTF-8")); CloseableHttpResponse response = httpclient.execute(httpost); try { HttpEntity entity = response.getEntity(); String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8"); EntityUtils.consume(entity); return WXUtil.doXMLParse(jsonStr); } finally { response.close(); } } finally { httpclient.close(); } } /** * 是否簽名正確,規則是:按參數名稱a-z排序,遇到空值的參數不參加簽名。 */ 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); } }
9.IdUtils工具類
import java.util.Random; public class IdUtils { /** * 圖片名生成 */ public static String genImageName() { //取當前時間的長整形值包含毫秒 long millis = System.currentTimeMillis(); //long millis = System.nanoTime(); //加上三位隨機數 Random random = new Random(); int end3 = random.nextInt(999); //如果不足三位前面補0 String str = millis + String.format("%03d", end3); return str; } /** * 訂單號生成 * @return */ public static String genOrderName() { //取當前時間的長整形值包含毫秒 long millis = System.currentTimeMillis(); //long millis = System.nanoTime(); //加上六位隨機數 Random random = new Random(); long end6 = random.nextInt(999999); //如果不足三位前面補0 String str = millis + String.format("%06d", end6); return str; } }
10.MD5Util工具類
import java.security.MessageDigest;
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" };
}