1. 新版的支付宝支付简化了代码,省去了自己去签名配置参数,拼接参数的烦恼,简单明了。
public static String buildAlipayRequest(AlipayVo alipayVo) { String alipayInfo = null; //实例化客户端 AlipayClient alipayClient = new DefaultAlipayClient(ALIPAY_URL, alipayVo.getAppId(), alipayVo.getAppPrivateKey(), FORMAT, CHARSET_UTF8, alipayVo.getAlipayPublicKey(), SIGN_TYPE); //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest(); //SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。 AlipayTradeAppPayModel model = new AlipayTradeAppPayModel(); model.setBody("AlipayTrade"); model.setSubject(alipayVo.getSubject()); model.setOutTradeNo(alipayVo.getOutTradeNo()); model.setTimeoutExpress("30m"); model.setTotalAmount(alipayVo.getTotalAmount()); model.setProductCode("QUICK_MSECURITY_PAY"); request.setBizModel(model); request.setNotifyUrl(alipayVo.getNotifyUrl()); try { //这里和普通的接口调用不同,使用的是sdkExecute AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request); log.info(response.getBody());//就是orderString 可以直接给客户端请求,无需再做处理。 alipayInfo = response.getBody(); } catch (AlipayApiException e) { e.printStackTrace(); } return alipayInfo; }
2. 因为项目使用到了过滤器,导致签名的sign数据中“+”被替换成了“ ”,无法获取原本的sign
java.security.SignatureException: Signature length not correct: got 253 but was expecting 256
简单处理,就是替换,将空格替换成加号
sign = sign.replace(" ", "+");
3. 相对于支付宝文档,微信的开发比较繁琐,很多都需要自己开发实现。微信工具类,仅供参考。

public class WxpayUtil { private static final Logger log = LoggerFactory.getLogger(WxpayUtil.class); private static final String GATEURL = "https://api.mch.weixin.qq.com/pay/unifiedorder"; /** * 获取微信预支付prepayid * @param weixinpayVo * @return */ public static JSONObject buildWxpayRequest(WeixinpayVo weixinpayVo){ String noncestr = getNonceStr(); SortedMap<Object, Object> parameters = new TreeMap<Object, Object>(); parameters.put("appid", weixinpayVo.getAppid());// 分车企--微信分配的公众账号ID(企业号corpid即为此appId) parameters.put("mch_id", weixinpayVo.getMchid());// 分车企--微信商户号 parameters.put("nonce_str", noncestr);// 随机字符串 parameters.put("body", weixinpayVo.getSubject());// 支付内容 parameters.put("out_trade_no", weixinpayVo.getOutTradeNo());// 商户订单号 parameters.put("total_fee", change2Fen(weixinpayVo.getTotalAmount()));// 支付金额,单位为分 parameters.put("spbill_create_ip", weixinpayVo.getSpbillCreateIp());// 终端IP parameters.put("notify_url", weixinpayVo.getNotifyUrl());// 异步请求地址 parameters.put("trade_type", "APP");// 交易类型App支付 String sign = creatingSign(weixinpayVo.getPartnerKey(), parameters);//分车企-- parameters.put("sign", sign); // 调用统一接口返回的值转换为XML格式 log.info("微信支付请求参数:"+parameters.toString()); String xmlString = sendPost(GATEURL, parameters); Map<String, String> map = doXMLParse(xmlString); log.info("微信支付请求参数:"+(map==null?null:map.toString())); String prepayid = map.get("prepay_id");// 获取prepayId JSONObject jsonObject = new JSONObject(); jsonObject.put("prepayid", prepayid); jsonObject.put("noncestr", noncestr); return jsonObject; } /** * POST请求,Map形式数据 * @param url 请求地址 * @param param 请求数据 * @param charset 编码方式 */ public static String sendPost(String url, Map<Object, Object> param) { StringBuffer buffer = new StringBuffer(); buffer.append("<xml>"); if (param != null && !param.isEmpty()) { for (Map.Entry<Object, Object> entry : param.entrySet()) { if(entry.getKey().equals("attach") || entry.getKey().equals("body") || entry.getKey().equals("sign")){ buffer.append("<"+entry.getKey()+">"); buffer.append("<![CDATA["+entry.getValue()+"]]>"); buffer.append("</"+entry.getKey()+">"); }else{ buffer.append("<"+entry.getKey()+">"); buffer.append(entry.getValue()); buffer.append("</"+entry.getKey()+">"); } } } buffer.append("</xml>"); PrintWriter out = null; BufferedReader in = null; String result = ""; try { URL realUrl = new URL(url); // 打开和URL之间的连接 URLConnection conn = realUrl.openConnection(); // 设置通用的请求属性 conn.setRequestProperty("accept", "*/*"); conn.setRequestProperty("connection", "Keep-Alive"); conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); // 发送POST请求必须设置如下两行 conn.setDoOutput(true); conn.setDoInput(true); // 获取URLConnection对象对应的输出流 out = new PrintWriter(conn.getOutputStream()); // 发送请求参数 out.print(buffer); // flush输出流的缓冲 out.flush(); // 定义BufferedReader输入流来读取URL的响应 in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8")); String line; while ((line = in.readLine()) != null) { result += line; } } catch (Exception e) { e.printStackTrace(); } // 使用finally块来关闭输出流、输入流 finally { try { if (out != null) { out.close(); } if (in != null) { in.close(); } } catch (IOException ex) { ex.printStackTrace(); } } return result; } /** * 生成签名 * @param PartnerKey * @param characterEncoding * @param parameters * @return https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=4_3 */ public static String creatingSign(String PartnerKey, SortedMap<Object, Object> parameters) { StringBuffer sb = new StringBuffer(); Iterator it = parameters.entrySet().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=" + PartnerKey); return MD5Util.MD5Encode(sb.toString(), "UTF-8").toUpperCase(); } /** * 金额元-> 分 * @param money * @return */ public static int change2Fen(String money){ if(null==money){ return 0; } return new BigDecimal(money).multiply(new BigDecimal(100)).intValue(); } /** * <P>(将分为单位的转换为元 (除100) )</P> */ /**金额为分的格式 */ public static final String CURRENCY_FEN_REGEX = "\-?[0-9]+"; public static String change2Yuan(String amount) throws Exception{ return String.valueOf(new BigDecimal(amount).divide(new BigDecimal(100))); } /** * <P>(32位内的随机串,防重发)</P> */ public static String getNonceStr() { Random random = new Random(); return MD5Util.MD5Encode(String.valueOf(random.nextInt(10000)), "UTF-8"); } /** * 获取时间戳 * @return */ public static String getTimeStamp() { return String.valueOf(System.currentTimeMillis() / 1000); } /** * 获取ip地址 * @param request * @return */ public static String getIpAddr(HttpServletRequest request) { String ip = request.getHeader("x-forwarded-for"); if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } return ip; } /** * 解析String类型的xml流对象InputStream * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。 * @param strxml * @return * @throws JDOMException * @throws IOException */ public static Map<String,String> doXMLParse(String strxml){ if(null == strxml || "".equals(strxml)) { return null; } Map<String,String> map = new HashMap<String,String>(); InputStream in =null; try{ in = new ByteArrayInputStream(strxml.getBytes("UTF-8")); SAXBuilder builder = new SAXBuilder(); builder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); builder.setFeature("http://xml.org/sax/features/external-general-entities", false); builder.setFeature("http://xml.org/sax/features/external-parameter-entities", false); builder.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); 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 = getChildrenText(children); } map.put(k, v); } }catch (Exception e){ e.printStackTrace(); }finally { if(in!=null){ //关闭流 IOUtils.closeQuietly(in); } } return map; } /** * 获取子结点的xml * @param children * @return String */ private 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(getChildrenText(list)); } sb.append(value); sb.append("</" + name + ">"); } } return sb.toString(); } public static String setResultXML(String return_code, String return_msg) { return "<xml><return_code><![CDATA[" + return_code + "]]></return_code><return_msg><![CDATA[" + return_msg + "]]></return_msg></xml>"; } }
4. 微信支付的过程,统一下单获取prepayid,然后组装请求参数返回手机端发起支付。
- 需要注意的是,noncestr随机字符串参数在两次请求中是一样的
- 微信签名参数partnerkey, key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
- 微信的金额是以分为单位的