首先感谢这位大佬的博客提供的帮助,按步骤做下来基本没什么问题.(https://blog.csdn.net/javaYouCome/article/details/79473743)
说一说我在对接过程中遇到的坑吧。
1、获取到预下单prepay_id之后,把5个参数放进map里面,还要额外给前端一个sign,获取调用接口的权限。生成paySign,然后给前端调用支付接口,一直出现签名错误,搞了大半天,终于解决了,主要是检查参数。
1)、生成签名时的timeStamp,S大写,前台访问接口时小写,毫秒数需要除以1000,把获取到的13位毫秒数变成10位。
2)、如果参数确实没问题那就要用很玄乎了的解决办法了, 重置API秘钥,可能多重置几次就好了,在公众平台重置之后记得还需要在商户平台->账户中心->API安全中设置一下秘钥。
2、调用申请退款接口,一直提示证书密码错误,简直要崩溃,最后发现是在网上找的读取证书的方法有问题,改了一下就成功了,最后会把代码贴出来,供大家参考。还有个小细节,申请退款接口是可以直接在本地IDE里面调用退款接口的,能打断点简直太爽了有木有,这也是解决后尝试才发现的,一遍一遍往服务器上放太浪费时间。
3、付款成功后的回调处理,退款成功后一直收不到回调,最后发现调用申请退款接口时忘了传notify_url参数了... 然后就是解密了, 需要替换掉sdk里面的两个jar包, 这个网上都有的,然后具体的解密代码最后也会贴出来,是用的网上大佬写好的,很感谢大家无私的分享,我贴出来也只是想帮助更多的人,如有不妥,还请联系我删除。
微信支付后台代码:
package com.zxf.service.impl; import com.zxf.common.utils.RedisUtil; import com.zxf.common.utils.WXPayConstants; import com.zxf.common.utils.WxUtil; import com.zxf.domain.OrderInfo; import com.zxf.service.IWxService; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.math.BigDecimal; import java.util.HashMap; import java.util.Map; @Service public class WxServiceImpl implements IWxService { @Resource private RedisUtil redisUtil; @Override public MapwxPay(String code, OrderInfo orderInfo,String url) throws Exception { String jsonStr = WxUtil.getOpenid(code); String openId = WxUtil.getJson(jsonStr,"openid"); //获取access_token String accessToken = WxUtil.getJson(jsonStr,"access_token"); System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>微信accessToken:"+accessToken); System.out.println(">>>>>>>>>>>>>>>>>openid:"+openId); System.out.println(">>>>>>>>>>>>>>>>>orderInfo:"+orderInfo); Map data = new HashMap<>(); data.put("appid",WxUtil.APPID); //商品描述 data.put("body","订单"); // data.put("body","zhenxiufang"); //商户号 data.put("mch_id",WxUtil.MCH_ID); //随机字符串 data.put("nonce_str",WxUtil.generateNonceStr()); //订单号 data.put("out_trade_no",orderInfo.getOrderNo()); BigDecimal branch = new BigDecimal("100"); BigDecimal orderAmountTotal = orderInfo.getOrderAmountTotal(); String total_fee = orderAmountTotal.multiply(branch).toString(); System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>total_fee:"+total_fee.substring(0,total_fee.indexOf("."))); //付款金额(单位:分,且不能含小数) data.put("total_fee",total_fee.substring(0,total_fee.indexOf("."))); //支持IPV4和IPV6两种格式的IP地址。调用微信支付API的机器IP data.put("spbill_create_ip",WxUtil.SPBILL_CREATE_IP); //回调地址 data.put("notify_url",WxUtil.NOTIFY_URL); //交易类型:公众号支付 data.put("trade_type",WxUtil.TRADE_TYPE); data.put("openid",openId); //获取签名 String sign = WxUtil.generateSignature(data,WxUtil.APPSECET, WXPayConstants.SignType.MD5); data.put("sign",sign); String xmlParam = WxUtil.mapToXml(data); System.out.println(">>>>>>>>>>>>>>>>>xmlParam:"+xmlParam); //调用统一下单接口 String xmlResult = WxUtil.httpsRequest(WxUtil.PLACE_ORDER_URL,"POST",xmlParam); System.out.println(">>>>>>>>>>>>>>>>>xmlResult:"+xmlResult); System.out.println(xmlResult); //xml解析成map Map resultMap = WxUtil.xmlToMap(xmlResult); //给前台请求,需要的参数 Map responseMap = new HashMap<>(); responseMap.put("appId",WxUtil.APPID); System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>currentTimeMillis:"+System.currentTimeMillis()/1000); responseMap.put("timeStamp",System.currentTimeMillis()/1000+""); responseMap.put("nonceStr",WxUtil.generateNonceStr()); responseMap.put("package","prepay_id="+resultMap.get("prepay_id")); responseMap.put("signType","MD5"); //获取签名 String paySign = WxUtil.generateSignature(responseMap,WxUtil.APPSECET, WXPayConstants.SignType.MD5); System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>paySign:"+paySign); responseMap.put("paySign",paySign); String testResponseXml = WxUtil.mapToXml(responseMap); System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>testResponseXml:"+testResponseXml); Object ticket = redisUtil.get("ticket"); if (ticket==null){ //根据access_token获取jsapi_ticket,有效期7200s String jsapiTicketStr = WxUtil.getJsapiTicket(accessToken); System.out.println(">>>>>>>>>>>>>>>>>>>>>>>jsapiTicketStr:"+jsapiTicketStr); ticket = WxUtil.getJson(jsapiTicketStr,"ticket"); redisUtil.set("ticket",ticket,7200); } //config接口注入权限验证需要的签名 String signature = WxUtil.getJsSdkSign(WxUtil.generateNonceStr(),ticket.toString(),System.currentTimeMillis()+"",url); System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>前台js接口注入需要的签名signature:"+signature); responseMap.put("signature",signature); return responseMap; } }
微信申请退款代码:
public void wxRefund(OrderInfo orderInfo) throws Exception { BigDecimal branch = new BigDecimal("100"); Mapdata = new HashMap<>(); data.put("appid", WxUtil.APPID); //商户号 data.put("mch_id", WxUtil.MCH_ID); //随机字符串 data.put("nonce_str", WxUtil.generateNonceStr()); //订单编号 data.put("out_trade_no", orderInfo.getOrderNo()); //退款编号 data.put("out_refund_no", WxUtil.generateNonceStr()); BigDecimal orderAmountTotal = orderInfo.getOrderAmountTotal(); String total_fee = orderAmountTotal.multiply(branch).toString(); //订单金额(单位:分,不能有小数点) data.put("total_fee", total_fee.substring(0,total_fee.indexOf("."))); //退款金额(分) data.put("refund_fee", total_fee.substring(0,total_fee.indexOf("."))); //微信退款回调 data.put("notify_url", WxUtil.NOTIFY_URL); //获取签名 String sign = WxUtil.generateSignature(data,WxUtil.APPSECET, WXPayConstants.SignType.MD5); data.put("sign",sign); String xmlParam = WxUtil.mapToXml(data); // String xmlResult = WxUtil.postData(xmlParam,WxUtil.MCH_ID,WxUtil.REFUND_URL,WxUtil.APICLIENT_CERT); String xmlResult = WxUtil.doRefund(WxUtil.REFUND_URL,xmlParam); System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>微信申请退款接口返回xmlResult:"+xmlResult); }
微信申请退款读取证书和发请求代码:
/** * 退款认证 * @param url 微信退款路径 * @param data 字符串xml参数 * @return * @throws Exception */ public static String doRefund(String url,String data) throws Exception { /** * 注意PKCS12证书 是从微信商户平台-》账户设置-》 API安全 中下载的 */ KeyStore keyStore = KeyStore.getInstance("PKCS12"); // File certfile = ResourceUtils.getFile("classpath:wx_certificate"+ SF_FILE_SEPARATOR + APICLIENT_CERT); File certfile = new File(APICLIENT_CERT); FileInputStream instream = new FileInputStream(certfile); try { keyStore.load(instream, MCH_ID.toCharArray()); } finally { instream.close(); } SSLContext sslcontext = SSLContexts.custom() .loadKeyMaterial(keyStore, MCH_ID.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.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 jsonStr; } finally { response.close(); } } finally { httpclient.close(); } }
微信申请退款回调处理代码:
/** * 微信退款回调 * @param request */ @RequestMapping("/wxRefundNotifyUrl") public void wxRefundNotifyUrl(HttpServletRequest request){ System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>进入微信退款回调"); InputStream inStream = null; try { inStream = request.getInputStream(); ByteArrayOutputStream outSteam = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len = 0; while ((len = inStream.read(buffer)) != -1) { outSteam.write(buffer, 0, len); } String resultxml = new String(outSteam.toByteArray(), "utf-8"); System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>微信退款回调resultByteXml:"+resultxml); MaprefundMap = WxUtil.xmlToMap(resultxml); if (refundMap.get("return_code").equals("SUCCESS")) { //解码,先base64解码, 然后AES解码 String aesXml = AESUtil.decryptData(refundMap.get("req_info")); System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>微信退款回调解密后的串,应该是aesXml:" + aesXml); Map aesMap = WxUtil.xmlToMap(aesXml); //订单号 String out_trade_no = aesMap.get("out_trade_no"); //金额 BigDecimal total_fee = new BigDecimal(aesMap.get("total_fee")).divide(new BigDecimal(100)); OrderInfo orderInfo = orderInfoService.selectOne(new EntityWrapper ().eq("order_no", out_trade_no)); //验证订单是否存在,付款金额,状态是否为退款中 if (orderInfo != null && orderInfo.getOrderAmountTotal().compareTo(total_fee) == 0 && orderInfo.getOrderStatus().equals(9)) { orderInfo.setOrderStatus(6); orderInfoService.updateById(orderInfo); //传输退款销货单 tService.refundSalesOrder(orderInfo, TUtil.WX_PAY, TUtil.WX_ACCOUNT_ID); } } } catch (Exception e) { e.printStackTrace(); } }
微信退款AES解密工具类:
package com.zxf.common.utils; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import java.security.Security; public class AESUtil { /** * 密钥算法 */ private static final String ALGORITHM = "AES"; /** * 加解密算法/工作模式/填充方式 */ private static final String ALGORITHM_MODE_PADDING = "AES/ECB/PKCS7Padding"; /** * 生成key */ private static final String key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx";//此处为测试key,正式环境请替换成商户密钥 private static SecretKeySpec secretKey = new SecretKeySpec(MD5Util.MD5Encode(key, "UTF-8").toLowerCase().getBytes(), ALGORITHM); /** * AES加密 * * @param data * @return * @throws Exception */ public static String encryptData(String data) throws Exception { /** * 这个地方调用BouncyCastleProvider *让java支持PKCS7Padding */ Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); // 创建密码器 Cipher cipher = Cipher.getInstance(ALGORITHM_MODE_PADDING); // 初始化 cipher.init(Cipher.ENCRYPT_MODE, secretKey); return Base64Util.encode(cipher.doFinal(data.getBytes())); } /** * AES解密 * * @param base64Data * @return * @throws Exception */ public static String decryptData(String base64Data) throws Exception { /** * 这个地方调用BouncyCastleProvider *让java支持PKCS7Padding */ Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); // 创建密码器 Cipher cipher = Cipher.getInstance(ALGORITHM_MODE_PADDING); // 初始化 cipher.init(Cipher.DECRYPT_MODE, secretKey); return new String(cipher.doFinal(Base64Util.decode(base64Data))); } /* public static void main(String[] args) throws Exception { //String A 为测试字符串,是微信返回过来的退款通知字符串 String A = "oSWxqqMF5lk2EF+gdrdt5wPrOru854za5XjHq5cUXs74zF9+jlxGOo7DHQIuntolVF3kQAruoMoNK5lLRsCulgG2hAT+6sNen8f/f3drMxfsTFOj3aBTKkIHs2p3AVJA4fXpGRCpejq3JJplSQnnSwFljzcxvqe7rU3y/H0KpFyBuYUSEf+msbkHEnHnIHQi4p9JDlLPWoKHramM7R65Qd13GdUU41scNybWCkwl+q/cY2Nv6KUt490JXTbTEgZNE6ArJKGg9woRMUdJEimTnv2OSY16yjo8dlIiozEoHcoQsvSFuMA5DHfHmtk5gbn8y6FVLHbt8XmmOIkfl/CVCXGQ+fGJmazxmqpTLBnAxXogFX2c2h8ZFqrWHW0wWZNSqpRX8HnMBw4V5hUMCiN9ASP3AzkpbtxdkDaeJYagVFgpB7oXxNUlQMy7pCqWCqbhoeLlZtzACx3qNqf57cQLn06T8wrYddf3f78oIYceVWMBses6wcJW2uTUdci4hYOQn5G+iVGLRzMuI8xwQSeBtdrWBor842tEsg4/wgFRxiEgjN+Jl+pCbwULjzt870OwC/UKD9mM3bhyay1jxeKNfkqgks0TH9eZXT1mR6IBfIUipgk9nTrGLFQwt4AAAf7/KoW7A3d1eYGY1vo/QkinixiZsxOJhzw95X6wiiARPa8oe0180lCuhLtIrNRlxyVMbbwA8GQVuCCE6w+/yKIF+el+Gcf7Gm2ljQzV7PEwiomW/DsBqUb5mwGfI52NLRa70kJ8vgaXeMN1xhwWYLzg02muvGGwS2P4kgGO0Sg0L5ycpN7Vp421+HnAPdcW6y/pQi03BKAR6fZT5JQYAIoNN4K8K6ZbgfZiuG32q0q4bwVWrg4jBlyPmj8JwHtbikbAgoJ9sUwWYi7P+Btk1ZHCPLW90p+1mIL8eVpneOaon3mSW0R4JDiIJK8oYLD/1n4NTKRTg9c6OMdSHnK8BUnodw=="; String B = AESUtil.decryptData(A); System.out.println(B); }*/ }
微信退款Base64解密工具类:
package com.zxf.common.utils; import java.io.IOException; import java.io.OutputStream; import java.io.Writer; /** * Base64工具类 */ public class Base64Util { private static final char S_BASE64CHAR[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' }; private static final byte S_DECODETABLE[]; static { S_DECODETABLE = new byte[128]; for (int i = 0; i < S_DECODETABLE.length; i++) S_DECODETABLE[i] = 127; for (int i = 0; i < S_BASE64CHAR.length; i++) S_DECODETABLE[S_BASE64CHAR[i]] = (byte) i; } /** * * @param ibuf * @param obuf * @param wp * @return */ private static int decode0(char ibuf[], byte obuf[], int wp) { int outlen = 3; if (ibuf[3] == '=') outlen = 2; if (ibuf[2] == '=') outlen = 1; int b0 = S_DECODETABLE[ibuf[0]]; int b1 = S_DECODETABLE[ibuf[1]]; int b2 = S_DECODETABLE[ibuf[2]]; int b3 = S_DECODETABLE[ibuf[3]]; switch (outlen) { case 1: // '\001' obuf[wp] = (byte) (b0 << 2 & 252 | b1 >> 4 & 3); return 1; case 2: // '\002' obuf[wp++] = (byte) (b0 << 2 & 252 | b1 >> 4 & 3); obuf[wp] = (byte) (b1 << 4 & 240 | b2 >> 2 & 15); return 2; case 3: // '\003' obuf[wp++] = (byte) (b0 << 2 & 252 | b1 >> 4 & 3); obuf[wp++] = (byte) (b1 << 4 & 240 | b2 >> 2 & 15); obuf[wp] = (byte) (b2 << 6 & 192 | b3 & 63); return 3; } throw new RuntimeException("Internal error"); } /** * * @param data * @param off * @param len * @return */ public static byte[] decode(char data[], int off, int len) { char ibuf[] = new char[4]; int ibufcount = 0; byte obuf[] = new byte[(len / 4) * 3 + 3]; int obufcount = 0; for (int i = off; i < off + len; i++) { char ch = data[i]; if (ch != '=' && (ch >= S_DECODETABLE.length || S_DECODETABLE[ch] == 127)) continue; ibuf[ibufcount++] = ch; if (ibufcount == ibuf.length) { ibufcount = 0; obufcount += decode0(ibuf, obuf, obufcount); } } if (obufcount == obuf.length) { return obuf; } else { byte ret[] = new byte[obufcount]; System.arraycopy(obuf, 0, ret, 0, obufcount); return ret; } } /** * * @param data * @return */ public static byte[] decode(String data) { char ibuf[] = new char[4]; int ibufcount = 0; byte obuf[] = new byte[(data.length() / 4) * 3 + 3]; int obufcount = 0; for (int i = 0; i < data.length(); i++) { char ch = data.charAt(i); if (ch != '=' && (ch >= S_DECODETABLE.length || S_DECODETABLE[ch] == 127)) continue; ibuf[ibufcount++] = ch; if (ibufcount == ibuf.length) { ibufcount = 0; obufcount += decode0(ibuf, obuf, obufcount); } } if (obufcount == obuf.length) { return obuf; } else { byte ret[] = new byte[obufcount]; System.arraycopy(obuf, 0, ret, 0, obufcount); return ret; } } /** * * @param data * @param off * @param len * @param ostream * @throws IOException */ public static void decode(char data[], int off, int len, OutputStream ostream) throws IOException { char ibuf[] = new char[4]; int ibufcount = 0; byte obuf[] = new byte[3]; for (int i = off; i < off + len; i++) { char ch = data[i]; if (ch != '=' && (ch >= S_DECODETABLE.length || S_DECODETABLE[ch] == 127)) continue; ibuf[ibufcount++] = ch; if (ibufcount == ibuf.length) { ibufcount = 0; int obufcount = decode0(ibuf, obuf, 0); ostream.write(obuf, 0, obufcount); } } } /** * * @param data * @param ostream * @throws IOException */ public static void decode(String data, OutputStream ostream) throws IOException { char ibuf[] = new char[4]; int ibufcount = 0; byte obuf[] = new byte[3]; for (int i = 0; i < data.length(); i++) { char ch = data.charAt(i); if (ch != '=' && (ch >= S_DECODETABLE.length || S_DECODETABLE[ch] == 127)) continue; ibuf[ibufcount++] = ch; if (ibufcount == ibuf.length) { ibufcount = 0; int obufcount = decode0(ibuf, obuf, 0); ostream.write(obuf, 0, obufcount); } } } /** * * @param data * @return */ public static String encode(byte data[]) { return encode(data, 0, data.length); } /** * * @param data * @param off * @param len * @return */ public static String encode(byte data[], int off, int len) { if (len <= 0) return ""; char out[] = new char[(len / 3) * 4 + 4]; int rindex = off; int windex = 0; int rest; for (rest = len - off; rest >= 3; rest -= 3) { int i = ((data[rindex] & 255) << 16) + ((data[rindex + 1] & 255) << 8) + (data[rindex + 2] & 255); out[windex++] = S_BASE64CHAR[i >> 18]; out[windex++] = S_BASE64CHAR[i >> 12 & 63]; out[windex++] = S_BASE64CHAR[i >> 6 & 63]; out[windex++] = S_BASE64CHAR[i & 63]; rindex += 3; } if (rest == 1) { int i = data[rindex] & 255; out[windex++] = S_BASE64CHAR[i >> 2]; out[windex++] = S_BASE64CHAR[i << 4 & 63]; out[windex++] = '='; out[windex++] = '='; } else if (rest == 2) { int i = ((data[rindex] & 255) << 8) + (data[rindex + 1] & 255); out[windex++] = S_BASE64CHAR[i >> 10]; out[windex++] = S_BASE64CHAR[i >> 4 & 63]; out[windex++] = S_BASE64CHAR[i << 2 & 63]; out[windex++] = '='; } return new String(out, 0, windex); } /** * * @param data * @param off * @param len * @param ostream * @throws IOException */ public static void encode(byte data[], int off, int len, OutputStream ostream) throws IOException { if (len <= 0) return; byte out[] = new byte[4]; int rindex = off; int rest; for (rest = len - off; rest >= 3; rest -= 3) { int i = ((data[rindex] & 255) << 16) + ((data[rindex + 1] & 255) << 8) + (data[rindex + 2] & 255); out[0] = (byte) S_BASE64CHAR[i >> 18]; out[1] = (byte) S_BASE64CHAR[i >> 12 & 63]; out[2] = (byte) S_BASE64CHAR[i >> 6 & 63]; out[3] = (byte) S_BASE64CHAR[i & 63]; ostream.write(out, 0, 4); rindex += 3; } if (rest == 1) { int i = data[rindex] & 255; out[0] = (byte) S_BASE64CHAR[i >> 2]; out[1] = (byte) S_BASE64CHAR[i << 4 & 63]; out[2] = 61; out[3] = 61; ostream.write(out, 0, 4); } else if (rest == 2) { int i = ((data[rindex] & 255) << 8) + (data[rindex + 1] & 255); out[0] = (byte) S_BASE64CHAR[i >> 10]; out[1] = (byte) S_BASE64CHAR[i >> 4 & 63]; out[2] = (byte) S_BASE64CHAR[i << 2 & 63]; out[3] = 61; ostream.write(out, 0, 4); } } /** * * @param data * @param off * @param len * @param writer * @throws IOException */ public static void encode(byte data[], int off, int len, Writer writer) throws IOException { if (len <= 0) return; char out[] = new char[4]; int rindex = off; int rest = len - off; int output = 0; do { if (rest < 3) break; int i = ((data[rindex] & 255) << 16) + ((data[rindex + 1] & 255) << 8) + (data[rindex + 2] & 255); out[0] = S_BASE64CHAR[i >> 18]; out[1] = S_BASE64CHAR[i >> 12 & 63]; out[2] = S_BASE64CHAR[i >> 6 & 63]; out[3] = S_BASE64CHAR[i & 63]; writer.write(out, 0, 4); rindex += 3; rest -= 3; if ((output += 4) % 76 == 0) writer.write("\n"); } while (true); if (rest == 1) { int i = data[rindex] & 255; out[0] = S_BASE64CHAR[i >> 2]; out[1] = S_BASE64CHAR[i << 4 & 63]; out[2] = '='; out[3] = '='; writer.write(out, 0, 4); } else if (rest == 2) { int i = ((data[rindex] & 255) << 8) + (data[rindex + 1] & 255); out[0] = S_BASE64CHAR[i >> 10]; out[1] = S_BASE64CHAR[i >> 4 & 63]; out[2] = S_BASE64CHAR[i << 2 & 63]; out[3] = '='; writer.write(out, 0, 4); } } }
微信退款MD5解密工具类:
package com.zxf.common.utils; import java.security.MessageDigest; public class MD5Util { public final static String MD5(String s) { char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; try { byte[] btInput = s.getBytes(); // 获得MD5摘要算法的 MessageDigest 对象 MessageDigest mdInst = MessageDigest.getInstance("MD5"); // 使用指定的字节更新摘要 mdInst.update(btInput); // 获得密文 byte[] md = mdInst.digest(); // 把密文转换成十六进制的字符串形式 int j = md.length; char str[] = new char[j * 2]; int k = 0; for (int i = 0; i < j; i++) { byte byte0 = md[i]; str[k++] = hexDigits[byte0 >>> 4 & 0xf]; str[k++] = hexDigits[byte0 & 0xf]; } return new String(str); } catch (Exception e) { e.printStackTrace(); return null; } } 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" }; /*public static void main(String[] asd) { String con = "hello kitty"; String str = MD5Encode(con, "UTF-8"); System.out.println(str.toUpperCase()); }*/ }