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;@Controllerpublic 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" };}