前言
这篇文章主要讲诉系统调用支付宝手机网页即时交易接口后支付宝返回的异步通知。
支付宝对商户的请求数据处理完成后,会将处理的结果数据通过服务器主动通知的方式通知给商户网站。这些处理结果数据就是服务器异步通知参数。
特性
服务器异步通知参数说明
notify_data通知业务参数列表
http://商户自定义地址/alipay/notify_url.php?service=alipay.wap.trade.create.direct&sign=Rw/y4ROnNicXhaj287Fiw5pvP6viSyg53H3iNiJ61D3YVi7zGniG2680pZv6rakMCeXX++q9XRLw8Rj6I1//qHrwMAHS1hViNW6hQYsh2TqemuL/xjXRCY3vjm1HCoZOUa5zF2jU09yG23MsMIUx2FAWCL/rgbcQcOjLe5FugTc=&sec_id=0001&v=2.0¬ify_data=g3ivqicRwI9rI5jgmSHSU2osBXV1jcxohapSAPjx4f6qiqsoAzstaRWuPuutE0gxQwzMOtwL3npZqWO3Z89J4w4dXIY/fvOLoTNn8FjExAf7OozoptUS6suBhdMyo/YJyS3lVALfCeT3s27pYWihHgQgna6cTfgi67H2MbX40xtexIpUnjgxBkmOLai8DPOUI58y4UrVwoXQgdcwnXsfn2OthhUFiFPfpINgEphUAq1nC/EPymP6ciHdTCWRI6l1BgWuCzdFy0MxJLliPSnuLyZTou7f+Z5Mw24FgOacaISB+1/G+c4XIJVKJwshCDw9Emz+NAWsPvq34FEEQXVAeQRDOphJx8bDqLK75CGZX+6fx88m5ztq4ykuRUcrmoxZLJ+PiABvYFzi5Yx2uBMP/PmknRmj1HUKEhuVWsXR0t6EWpJFXlyQA4uxbShzncWDigndD7wbfNtkNLg5xMSFFIKay+4YzJK68H9deW4xqk4JYTKsv8eom9Eg9MrJZiIrFkFpVYPuaw0y/n61UEFYdzEQZz+garCmMYehEAQCGibYUQXBlf1iwTOZdqJIxdgCpSX21MIa9N9jicmFu8OXWZJkdN+UrSyvIcpzRori+U6522ovMz5Z8EzVTfcUENu+d
http://商户自定义地址/alipay/notify_url.php?service=alipay.wap.trade.create.direct%20&sign=Rw/y4ROnNicXhaj287Fiw5pvP6viSyg53H3iNiJ61D3YVi7zGniG2680pZv6rakMCeXX++q9XRLw8Rj6I1//qHrwMAHS1hViNW6hQYsh2TqemuL/xjXRCY3vjm1HCoZOUa5zF2jU09yG23MsMIUx2FAWCL/rgbcQcOjLe5FugTc=&v=2.0&sec_id=MD5¬ify_data=%3Cnotify%3E%3Cpayment_type%3E1%3C/payment_type%3E%3Csubject%3E%E6%94%B6%E9%93%B6%E5%8F%B0{1283134629741}%3C/subject%3E%3Ctrade_no%3E2014040311001004370000361525%3C/trade_no%3E%3Cbuyer_email%3Edinglang@a.com%3C/buyer_email%3E%3Cgmt_create%3E2010-08-3010:17:24%3C/gmt_create%3E%3Cnotify_type%3Etrade_status_sync%3C/notify_type%3E%3Cquantity%3E1%3C/quantity%3E%3Cout_trade_no%3E1283134629741%3C/out_trade_no%3E%3Cnotify_time%3E2010-08-3010:18:15%3C/notify_time%3E%3Cseller_id%3E2088101000137799%3C/seller_id%3E%3Ctrade_status%3ETRADE_FINISHED%3C/trade_status%3E%3Cis_total_fee_adjust%3EN%3C/is_total_fee_adjust%3E%3Ctotal_fee%3E1.00%3C/total_fee%3E%3Cgmt_payment%3E2010-08-3010:18:26%3C/gmt_payment%3E%3Cseller_email%3Echenf003@yahoo.cn%3C/seller_email%3E%3Cgmt_close%3E2010-08-3010:18:26%3C/gmt_close%3E%3Cprice%3E1.00%3C/price%3E%3Cbuyer_id%3E2088102001172352%3C/buyer_id%3E%3Cnotify_id%3E509ad84678759176212c247c46bec05303%3C/notify_id%3E%3Cuse_coupon%3EN%3C/use_coupon%3E%3C/notify%3E
http://商户自定义地址/alipay/notify_url.php?service=alipay.wap.trade.create.direct&sign=Rw/y4ROnNicXhaj287Fiw5pvP6viSyg53H3iNiJ61D3YVi7zGniG2680pZv6rakMCeXX++q9XRLw8Rj6I1//qHrwMAHS1hViNW6hQYsh2TqemuL/xjXRCY3vjm1HCoZOUa5zF2jU09yG23MsMIUx2FAWCL/rgbcQcOjLe5FugTc=&v=1.0&sec_id=0001¬ify_data=1
service=alipay.wap.trade.create.direct&v=1.0&sec_id=0001¬ify_data=…
代码示例
@RequestMapping(value="/notify",method=RequestMethod.POST)
@ResponseBody
public Object notifyUrl(HttpServletRequest request, HttpServletResponse response){
System.out.println("支付提示");
//获取支付宝POST过来反馈信息
Mapparams = new HashMap ();
Map requestParams = request.getParameterMap();
for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
String name = (String) iter.next();
String[] values = (String[]) requestParams.get(name);
String valueStr = "";
for (int i = 0; ivalueStr = (i == values.length - 1) ? valueStr + values[i]
: valueStr + values[i] + ",";
}
//乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
//valueStr = new String(valueStr.getBytes("ISO-8859-1"), "gbk");
params.put(name, valueStr);
}
//验证
AlipayAuth alipayAuth = new AlipayAuth();
alipayAuth.setKey(orderBusiness.getAliAttributes("key"));
alipayAuth.setPartner(orderBusiness.getAliAttributes("partner"));
boolean flag = false;
try {
flag = AliPayUtils.verifyNotify(params, alipayAuth);
//获取支付宝的通知返回参数
//XML解析notify_data数据
Document doc_notify_data = DocumentHelper.parseText(params.get("notify_data"));
//商户订单号
String out_trade_no = doc_notify_data.selectSingleNode( "//notify/out_trade_no" ).getText();
//支付宝交易号
String trade_no = doc_notify_data.selectSingleNode( "//notify/trade_no" ).getText();
//交易状态
String trade_status = doc_notify_data.selectSingleNode( "//notify/trade_status" ).getText();
//交易总金额
String total_fee = doc_notify_data.selectSingleNode( "//notify/total_fee" ).getText();
if(flag){
//付款成功后
//——请根据您的业务逻辑来编写程序(以下代码仅作参考)——
if(trade_status.equals("TRADE_FINISHED")){
//判断该笔订单是否在商户网站中已经做过处理
//如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
//如果有做过处理,不执行商户的业务程序
//注意:
//该种交易状态只在两种情况下出现
//1、开通了普通即时到账,买家付款成功后。
//2、开通了高级即时到账,从该笔交易成功时间算起,过了签约时的可退款时限(如:三个月以内可退款、一年以内可退款等)后。
out.println("success"); //请不要修改或删除
} else if (trade_status.equals("TRADE_SUCCESS")){
//判断该笔订单是否在商户网站中已经做过处理
//如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
//如果有做过处理,不执行商户的业务程序
//注意:
//该种交易状态只在一种情况下出现——开通了高级即时到账,买家付款成功后。
out.println("success"); //请不要修改或删除
}
//——请根据您的业务逻辑来编写程序(以上代码仅作参考)——
} else {//
return "fail";
}
} catch (Exception e) {
// TODO Auto-generated catch block
log.error(e.getMessage());
e.printStackTrace();
return "fail";
}
}
public class AlipayAuth {
// 支付宝网关
private final static String alipayGatewayNew = "http://wappaygw.alipay.com/service/rest.htm?";
private final static String inputCharset = "utf-8";
private String key = "";//从支付宝获取的密钥
//接口名称
private final static String service = "alipay.wap.trade.create.direct";
//请求参数格式
private final static String format = "xml";
//接口版本号
private final static String v = "2.0";
//合作者身份id
public String partner = "2088000000000000";//填写从支付宝得到的id
//请求号
private String reqId;
//签名方式
private final static String secId = "MD5";
//签名
private String sign;
//请求业务参数
private String reqData;
//商品名称
private String subject;
//商务网站唯一订单号
private String outTradeNo;
//交易金额
private String totalFee;
//卖家支付宝账号
private String sellerAccountName;
//支付成功跳转路径
private String callBackUrl = "callBack";
//服务器异步通知页面路径 (可空)
private String notifyUrl = "notify";
//商户系统唯一标示(可空)
private String outUser;
//操作中断返回地址(可空)
private String merchantUrl;
//交易自动关闭时间(可空)
private String payExpire;
//代理人id(可空)
private String agentId;
public String getBasePath() {
return basePath;
}
public void setBasePath(String basePath) {
this.basePath = basePath;
}
public String getAlipayGatewayNew() {
return alipayGatewayNew;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getInputCharset() {
return inputCharset;
}
public String getService() {
return service;
}
public String getFormat() {
return format;
}
public String getV() {
return v;
}
public String getPartner() {
return partner;
}
public void setPartner(String partner) {
this.partner = partner;
}
public String getReqId() {
return reqId;
}
public void setReqId(String reqId) {
this.reqId = reqId;
}
public String getSecId() {
return secId;
}
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public String getOutTradeNo() {
return outTradeNo;
}
public void setOutTradeNo(String outTradeNo) {
this.outTradeNo = outTradeNo;
}
public String getTotalFee() {
return totalFee;
}
public void setTotalFee(String totalFee) {
this.totalFee = totalFee;
}
public String getSellerAccountName() {
return sellerAccountName;
}
public void setSellerAccountName(String sellerAccountName) {
this.sellerAccountName = sellerAccountName;
}
public String getCallBackUrl() {
return callBackUrl;
}
public void setCallBackUrl(String callBackUrl) {
this.callBackUrl = callBackUrl;
}
public String getNotifyUrl() {
return notifyUrl;
}
public void setNotifyUrl(String notifyUrl) {
this.notifyUrl = notifyUrl;
}
public String getOutUser() {
return outUser;
}
public void setOutUser(String outUser) {
this.outUser = outUser;
}
public String getMerchantUrl() {
return merchantUrl;
}
public void setMerchantUrl(String merchantUrl) {
this.merchantUrl = merchantUrl;
}
public String getPayExpire() {
return payExpire;
}
public void setPayExpire(String payExpire) {
this.payExpire = payExpire;
}
public String getAgentId() {
return agentId;
}
public void setAgentId(String agentId) {
this.agentId = agentId;
}
public void setReqData(String reqData) {
this.reqData = reqData;
}
public String getReqData() {
reqData = "" ";
+ "" + subject + " "//商品名称
+ "" + outTradeNo + " "//商户网站唯一订单号
+ "" + totalFee + " "//交易金额
+ "" + sellerAccountName + " "//卖家支付宝账号
+ "" + callBackUrl + " "//支付成功跳转页面
+ "" + notifyUrl + " "//异步通知页面
//+ "" + merchantUrl + " "//操作终端返回地址(可空)
+ "
return reqData;
}
}
AliPayUtils.verifyNotify(params, alipayAuth)方法
/**
* 验证消息是否是支付宝发出的合法消息,验证服务器异步通知
* @param params 通知返回来的参数数组
* @return 验证结果
*/
public static boolean verifyNotify(Mapparams,AlipayAuth alipayAuth) throws Exception {
//获取是否是支付宝服务器发来的请求的验证结果
String respOnseTxt= "true";
try {
//XML解析notify_data数据,获取notify_id
Document document = DocumentHelper.parseText(params.get("notify_data"));
String notify_id = document.selectSingleNode( "//notify/notify_id" ).getText();
respOnseTxt= verifyResponse(notify_id,alipayAuth);
} catch(Exception e) {
respOnseTxt= e.toString();
}
//获取返回时的签名验证结果
String sign = "";
if(params.get("sign") != null) {sign = params.get("sign");}
boolean isSign = getSignVeryfy(params, sign,false,alipayAuth);
//写日志记录(若要调试,请取消下面两行注释)
//String sWord = "respOnseTxt=" + responseTxt + "\n isSign=" + isSign + "\n 返回回来的参数:" + AlipayCore.createLinkString(params);
//AlipayCore.logResult(sWord);
//判断responsetTxt是否为true,isSign是否为true
//responsetTxt的结果不是true,与服务器设置问题、合作身份者ID、notify_id一分钟失效有关
//isSign不是true,与安全校验码、请求时的参数格式(如:带自定义参数等)、编码格式有关
if (isSign && responseTxt.equals("true")) {
return true;
} else {
return false;
}
}
verifyResponse(notify_id,alipayAuth)代码块
/**
* 获取远程服务器ATN结果,验证返回URL
* @param notify_id 通知校验ID
* @return 服务器ATN结果
* 验证结果集:
* invalid命令参数不对 出现这个错误,请检测返回处理中partner和key是否为空
* true 返回正确信息
* false 请检查防火墙或者是服务器阻止端口问题以及验证时间是否超过一分钟
*/
private static String verifyResponse(String notify_id,AlipayAuth alipayAuth) {
//获取远程服务器ATN结果,验证是否是支付宝服务器发来的请求
String partner = alipayAuth.getPartner();
String veryfy_url = HTTPS_VERIFY_URL + "partner=" + partner + "¬ify_id=" + notify_id;
return checkUrl(veryfy_url);
}
/**
* 支付宝消息验证地址
*/
private static final String HTTPS_VERIFY_URL = "https://mapi.alipay.com/gateway.do?service=notify_verify&";
checkUrl(veryfy_url);
/**
* 获取远程服务器ATN结果
* @param urlvalue 指定URL路径地址
* @return 服务器ATN结果
* 验证结果集:
* invalid命令参数不对 出现这个错误,请检测返回处理中partner和key是否为空
* true 返回正确信息
* false 请检查防火墙或者是服务器阻止端口问题以及验证时间是否超过一分钟
*/
private static String checkUrl(String urlvalue) {
String inputLine = "";
try {
URL url = new URL(urlvalue);
HttpURLConnection urlCOnnection= (HttpURLConnection) url.openConnection();
BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection
.getInputStream()));
inputLine = in.readLine().toString();
} catch (Exception e) {
e.printStackTrace();
inputLine = "";
}
return inputLine;
}
getSignVeryfy(params, sign,false,alipayAuth)
/**
* 根据反馈回来的信息,生成签名结果
* @param Params 通知返回来的参数数组
* @param sign 比对的签名结果
* @param isSort 是否排序
* @return 生成的签名结果
*/
private static boolean getSignVeryfy(MapParams, String sign, boolean isSort,AlipayAuth alipayAuth) {
//过滤空值、sign与sign_type参数
MapsParaNew = paraFilter(Params);
//获取待签名字符串
String preSignStr = "";
if(isSort) {
preSignStr = createLinkString(sParaNew);
} else {
preSignStr = createLinkStringNoSort(sParaNew);
}
//获得签名验证结果
boolean isSign = false;
isSign = verify(preSignStr, sign, alipayAuth.getKey(), alipayAuth.getInputCharset());
return isSign;
}