昨天搞定了微信支付,没有想象中的难,主要是官方的demo不全好多东西要自己琢磨,mui端的就不写了支付宝的有了一模一样.上java端的首先是jar包
一个是用来解析xml文件
一个是用来解析json
当然可以替代
然后是工具类当然并不是全都用的到.
===============================================================================================================
public class ConfigUtil {
/**
* 服务号相关信息 */ public final static String APPID = "****";// 服务号的应用号 public final static String MCH_ID = "*****";// 商户号 public final static String API_KEY = "******";// API密钥 public final static String SIGN_TYPE = "MD5";// 签名加密方式public final static String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
}
===============================================================================================================
/**
* md5加密 * * @author Administrator * */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" };}
===============================================================================================================
/** * 信任管理器 */public class MyX509TrustManager implements X509TrustManager {// 检查客户端证书
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { }// 检查服务器端证书
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { }// 返回受信任的X509证书数组
public X509Certificate[] getAcceptedIssuers() { return null; }===============================================================================================================
/**
* 解析mxl * * @author Administrator * */public class XMLUtil { /** * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。 * * @param strxml * @return * @throws JDOMException * @throws IOException */ public static Map doXMLParse(String strxml) throws JDOMException, IOException { strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");if (null == strxml || "".equals(strxml)) {
return null; }Map m = new HashMap();
InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
SAXBuilder builder = new SAXBuilder(); Document doc = builder.build(in); Element root = doc.getRootElement(); List list = root.getChildren(); Iterator it = list.iterator(); while (it.hasNext()) { Element e = (Element) it.next(); String k = e.getName(); String v = ""; List children = e.getChildren(); if (children.isEmpty()) { v = e.getTextNormalize(); } else { v = XMLUtil.getChildrenText(children); }m.put(k, v);
}// 关闭流
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(XMLUtil.getChildrenText(list)); } sb.append(value); sb.append("</" + name + ">"); } }return sb.toString();
}}
===============================================================================================================
/**
* 支付公共工具 * * @author Administrator * */public class PayCommonUtil {/**
* 创建临时字符串 * * @param length * 创建长度 * @return 该长度的随机字符串 */ public static String CreateNoncestr(int length) { String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; String res = ""; for (int i = 0; i < length; i++) { Random rd = new Random(); res += chars.indexOf(rd.nextInt(chars.length() - 1)); } return res; }/**
* 创建随机字符串16位 * * @return */ public static String CreateNoncestr() { String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; String res = ""; for (int i = 0; i < 16; i++) { Random rd = new Random(); res += chars.charAt(rd.nextInt(chars.length() - 1)); } return res; }/**
* 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。 * * @return boolean */ public static boolean isTenpaySign(String characterEncoding, SortedMap<Object, Object> packageParams) { 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=" + ConfigUtil.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); }/**
* @Description:sign签名 * @param characterEncoding * 编码格式 * @param parameters * 请求参数 * @return */ public static String createSign(String characterEncoding, SortedMap<Object, Object> parameters) { // 签名字符串 StringBuffer sb = new StringBuffer(); // 遍历map集合 Set es = parameters.entrySet(); Iterator it = es.iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) 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=" + ConfigUtil.API_KEY); System.out.println(sb + "=================sign签名字符串"); String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding) .toUpperCase(); return sign; }/**
* @Description:将请求参数转换为xml格式的string * @param parameters * 请求参数 * @return */ public static String getRequestXml(SortedMap<Object, Object> parameters) { StringBuffer sb = new StringBuffer(); sb.append("<xml>"); Set es = parameters.entrySet(); Iterator it = es.iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); String k = (String) entry.getKey(); String v = (String) entry.getValue(); if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k)) { sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">"); } else { sb.append("<" + k + ">" + v + "</" + k + ">"); } } sb.append("</xml>"); return sb.toString(); }/**
* @Description:返回给微信的参数 * @param return_code * 返回编码 * @param return_msg * 返回信息 * @return */ public static String setXML(String return_code, String return_msg) { return "<xml><return_code><![CDATA[" + return_code + "]]></return_code><return_msg><![CDATA[" + return_msg + "]]></return_msg></xml>"; }/**
* 发送https请求 * * @param requestUrl * 请求地址 * @param requestMethod * 请求方式(GET、POST) * @param outputStr * 提交的数据 * @return 返回微信服务器响应的信息 */ public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) { try { // 创建SSLContext对象,并使用我们指定的信任管理器初始化 TrustManager[] tm = { new MyX509TrustManager() }; SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE"); sslContext.init(null, tm, new java.security.SecureRandom()); // 从上述SSLContext对象中得到SSLSocketFactory对象 SSLSocketFactory ssf = sslContext.getSocketFactory(); URL url = new URL(requestUrl); HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); // conn.setSSLSocketFactory(ssf); conn.setDoOutput(true); conn.setDoInput(true); conn.setUseCaches(false); // 设置请求方式(GET/POST) conn.setRequestMethod(requestMethod); conn.setRequestProperty("content-type", "application/x-www-form-urlencoded"); // 当outputStr不为null时向输出流写数据 if (null != outputStr) { OutputStream outputStream = conn.getOutputStream(); // 注意编码格式 outputStream.write(outputStr.getBytes("UTF-8")); outputStream.close(); } // 从输入流读取返回内容 InputStream inputStream = conn.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; conn.disconnect(); return buffer.toString(); } catch (ConnectException ce) { // log.error("连接超时:{}", ce); } catch (Exception e) { // log.error("https请求异常:{}", e); } return null; }/**
* 发送https请求 * * @param requestUrl * 请求地址 * @param requestMethod * 请求方式(GET、POST) * @param outputStr * 提交的数据 * @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值) */ public static JSONObject httpsRequest(String requestUrl, String requestMethod) { JSONObject jsonObject = null; try { // 创建SSLContext对象,并使用我们指定的信任管理器初始化 TrustManager[] tm = { new MyX509TrustManager() }; SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE"); sslContext.init(null, tm, new java.security.SecureRandom()); // 从上述SSLContext对象中得到SSLSocketFactory对象 SSLSocketFactory ssf = sslContext.getSocketFactory(); URL url = new URL(requestUrl); HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); // conn.setSSLSocketFactory(ssf); conn.setDoOutput(true); conn.setDoInput(true); conn.setUseCaches(false); conn.setConnectTimeout(3000); // 设置请求方式(GET/POST) conn.setRequestMethod(requestMethod); // conn.setRequestProperty("content-type", // "application/x-www-form-urlencoded"); // 当outputStr不为null时向输出流写数据 // 从输入流读取返回内容 InputStream inputStream = conn.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; conn.disconnect();jsonObject = JSONObject.parseObject(buffer.toString());
} catch (ConnectException ce) { // log.error("连接超时:{}", ce); } catch (Exception e) { System.out.println(e); // log.error("https请求异常:{}", e); } return jsonObject; }public static String urlEncodeUTF8(String source) {
String result = source; try { result = java.net.URLEncoder.encode(source, "utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return result; }/**
* 获取ip地址 * * @param request * @return */ public static String getIpAddr(HttpServletRequest request) { InetAddress addr = null; try { addr = InetAddress.getLocalHost(); } catch (UnknownHostException e) { return request.getRemoteAddr(); } byte[] ipAddr = addr.getAddress(); String ipAddrStr = ""; for (int i = 0; i < ipAddr.length; i++) { if (i > 0) { ipAddrStr += "."; } ipAddrStr += ipAddr[i] & 0xFF; } return ipAddrStr; }}
===============================================================================================================
最后是java请求接口
/**
* 微信支付接口 * * @param req * @param resp * @return 返回字符串给app由app调用微信支付功能 */ @ResponseBody @RequestMapping(value = "/wxPrePay", produces = "text/html;charset=UTF-8", method = { RequestMethod.GET }) public String wxPrePay(HttpServletRequest request, HttpServletResponse response) { String str = null;// 金额单位转换成元
int price100 = new BigDecimal(0.01).multiply(new BigDecimal(100)) .intValue(); if (price100 <= 0) {return "付款金额错误";
} // 设置回调地址-获取当前的地址拼接回调地址 String url = request.getRequestURL().toString(); String domain = url.substring(0, url.length() - 13); // 生产环境 String notify_url = domain + "wxNotify.html"; // 测试环境 // String notify_url = // "http://1f504p5895.51mypc.cn/cia/app/wxNotify.html";SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
parameters.put("appid", ConfigUtil.APPID);// appid parameters.put("mch_id", ConfigUtil.MCH_ID);// 商户id parameters.put("nonce_str", PayCommonUtil.CreateNoncestr());// 随机字符串 parameters.put("body", "购买测试");// 商品描述 parameters.put("out_trade_no", PayCommonUtil.CreateNoncestr(6)); // 订单唯一id parameters.put("fee_type", "CNY");// 支付方式 parameters.put("total_fee", String.valueOf(price100));// 总金额 parameters.put("spbill_create_ip", PayCommonUtil.getIpAddr(request));// 获取ip地址 parameters.put("notify_url", notify_url);// 回调地址 parameters.put("trade_type", "APP");// 交易类型 // 设置签名 String sign = PayCommonUtil.createSign("UTF-8", parameters); parameters.put("sign", sign); // 封装请求参数成xml字符串 String requestXML = PayCommonUtil.getRequestXml(parameters); // 调用统一下单接口返回prepay_id String result = PayCommonUtil.httpsRequest( ConfigUtil.UNIFIED_ORDER_URL, "POST", requestXML); System.out.println("\n" + result); try { /** * 统一下单接口返回正常的prepay_id,再按签名规范重新生成签名后,将数据传输给APP。参与签名的字段名为appId, * partnerId * ,prepayId,nonceStr,timeStamp,package。注意:package的值格式为Sign=WXPay **/ Map<String, String> map = XMLUtil.doXMLParse(result);SortedMap<Object, Object> parameterMap2 = new TreeMap<Object, Object>();
parameterMap2.put("appid", ConfigUtil.APPID); parameterMap2.put("partnerid", ConfigUtil.MCH_ID); parameterMap2.put("prepayid", map.get("prepay_id")); parameterMap2.put("package", "Sign=WXPay"); parameterMap2.put("noncestr", PayCommonUtil.CreateNoncestr()); // 本来生成的时间戳是13位,但是ios必须是10位,所以截取了一下 parameterMap2.put( "timestamp", Long.parseLong(String.valueOf(System.currentTimeMillis()) .toString().substring(0, 10))); String sign2 = PayCommonUtil.createSign("UTF-8", parameterMap2); parameterMap2.put("sign", sign2);str = JSON.toJSONString(parameterMap2);
} catch (JDOMException e) {
e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return str; }