1:必须由公司去支付宝申请签约(申请地址:https://b.alipay.com/order/productDetail.htm?productId=2015110218012942);
2:申请好后就可以获取到PID(以2088开头的16位纯数字),下载RSA密钥,解密后结果就是后面代码里的KEY;
3:在代码里拼装好接口需要的参数,发送一个post请求到支付宝网关(https://mapi.alipay.com/gateway.do)就OK
4:支付宝响应包含同步和异步方式(按时间段最多6次,称为最大补偿策略),同步方式可以在你本地调试,而异步回调要求你的环境在外网,仅支持https,而且不要用.do?methodname 这种方式的url,建议用restful风格的url,要不接收不到的(当时这个我算是入坑了)
下面我贴上比较核心代码分享:
1)支付宝操作工具类
package com.vstecs.system.core.pay.alipay; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.security.MessageDigest; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * 支付宝工具类 <br> * * @function Service Api. * @author zhangyi * @date 2017年2月6日 下午5:01:31 * @version 1.0.0 * @since JDK 1.8 */ public class AlipayCore { private static Log log = LogFactory.getLog(AlipayCore.class); // 支付宝提供给商户的服务接入网关URL(新) public static final String GATEWAY = "https://mapi.alipay.com/gateway.do"; // 商户的私钥 public static final String KEY = "不给看"; // 合作身份者ID,以2088开头由16位纯数字组成的字符串 public static final String PARTNER = "2088不给看"; // 字符编码格式 目前支持 gbk 或 utf-8 public static final String INPUT_CHARSET = "utf-8"; // 收款支付宝账号,一般情况下收款账号就是签约账号 public static final String SELLER_EMAIL = "不给看"; // 签名方式 不需修改 public static final String SIGN_TYPE = "MD5"; // 签名时需要忽略的参数集合 private static List<String> ignore; // 初始化 static { ignore = new ArrayList<String>(); ignore.add("sign"); ignore.add("sign_type"); } /** * * 签名 * * @param map * @return * */ public static String sign(Map<String, String> map) { log.debug("签名参数(map):" + map); // 过滤空参 if (map == null || map.isEmpty()) { return null; } // 排序参数 List<String> keys = new ArrayList<String>(map.keySet()); Collections.sort(keys); // 过滤空参,组装参数键值 StringBuffer sb = new StringBuffer(); for (int i = 0; i < keys.size(); i++) { String key = keys.get(i); String value = map.get(key); if (isEmpty(key, value) || ignore.contains(key)) { continue; } sb.append(sb.length() > 0 ? "&" : ""); sb.append(key).append("=").append(value); } sb.append(KEY); log.debug("待签名字符串:" + sb.toString()); // 获取MD5签名 try { return MD5Encode(sb.toString(), "UTF-8"); } catch (Exception e) { log.error("签名发生异常:", e); return null; } } /** * * 空值校验 * * @param input * @return * */ private static boolean isEmpty(String... input) { if (input == null || input.length == 0) { return true; } for (String s : input) { if (s == null || s.length() == 0) { return true; } } return false; } /** * * 校验 * * @param map * @return * */ public static boolean verify(Map<String, String> map) { log.debug("校验参数(map):" + map); // 过滤空参 if (map == null || map.isEmpty()) { return false; } String sign = map.get("sign"); String signType = map.get("sign_type"); String notifyId = map.get("notify_id"); // 过滤不匹配参数 if (isEmpty(sign, notifyId) || !SIGN_TYPE.equals(signType)) { log.debug("参数不匹配!"); return false; } // 校验签名 if (!sign.equals(sign(map))) { log.debug("签名不匹配!"); return false; } // 校验 notify_id String result = "false"; try { URL url = new URL(GATEWAY + "?service=notify_verify&partner=" + PARTNER + "¬ify_id=" + notifyId); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream())); result = br.readLine(); br.close(); conn.disconnect(); } catch (Exception e) { log.error("校验(notify_id)发生异常:" + e); return false; } log.debug("校验(notify_id)结果:" + result); return "true".equals(result); } 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 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]; } private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" }; }
2)业务操作
alipay(params, request, response); // 支付宝支付 private void alipay(Map<String, String> params, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Map<String, String> map = new HashMap<String, String>(); map.put("_input_charset", AlipayCore.INPUT_CHARSET); // 编码 map.put("service", "create_direct_pay_by_user"); map.put("partner", AlipayCore.PARTNER); // 合作身份者ID map.put("seller_email", AlipayCore.SELLER_EMAIL); // 收款支付宝账号 map.put("subject", "订单号:" + params.get("orderNo")); // 订单名称 map.put("body", "订单号:" + params.get("orderNo")); // 订单描述 map.put("out_trade_no", params.get("orderNo")); // 商户订单号 map.put("total_fee", params.get("totalPrice")); // 付款金额 map.put("payment_type", "1"); // 支付类型,必填,不能修改 map.put("show_url", getBasePath(request) + "/pay/book"); // 这个是你支付成功后需要展示的页面 map.put("notify_url", getBasePath(request) + "/pay/Notify"); // 服务器异步通知 map.put("return_url", getBasePath(request) + "/pay/Return"); // 页面跳转同步通知 // 注意:以下参数需在签名生成后加入 map.put("sign", AlipayCore.sign(map)); map.put("sign_type", AlipayCore.SIGN_TYPE); // 签名方式 不需修改 map.put("gateway", AlipayCore.GATEWAY); // 支付宝提供给商户的服务接入网关URL(新) /* ----- 跳转 ----- */ request.setAttribute("alipay", map); request.getRequestDispatcher("/WEB-INF/pay/alipay/alipay.jsp").forward(request, response); }
3)页面
<c:if test="${empty alipay}"><c:redirect url="${basePath}/pay/book" /></c:if> <c:if test="${not empty alipay}"> <c:redirect url="${alipay['gateway']}"> <c:param name="_input_charset" value="${alipay['_input_charset']}" /> <c:param name="service" value="${alipay['service']}" /> <c:param name="partner" value="${alipay['partner']}" /> <c:param name="seller_email" value="${alipay['seller_email']}" /> <c:param name="subject" value="${alipay['subject']}" /> <c:param name="body" value="${alipay['body']}" /> <c:param name="out_trade_no" value="${alipay['out_trade_no']}" /> <c:param name="total_fee" value="${alipay['total_fee']}" /> <c:param name="payment_type" value="${alipay['payment_type']}" /> <c:param name="show_url" value="${alipay['show_url']}" /> <c:param name="notify_url" value="${alipay['notify_url']}" /> <c:param name="return_url" value="${alipay['return_url']}" /> <c:param name="sign" value="${alipay['sign']}" /> <c:param name="sign_type" value="${alipay['sign_type']}" /> </c:redirect> </c:if>
4)页面的包结构,其中notify.jsp和return.jsp都是空文件
到这一步,应该就差不多了,里面的MD5加密未提供,这个简单应该都会,如果有不会的我再贴:>