package com.kk.controller.notify;
import com.alibaba.fastjson.JSON;
import com.google.gson.Gson;
import com.kk.api.service.PayService;
import com.kk.api.service.RefundService;
import com.kk.platform.enums.PayTypeCode;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* 支付宝支付成功后 回调接口
* <p/>
* 1.必须保证服务器异步通知页面(notify_url)上无任何字符,如空格、HTML标签、开发系统自带抛出的异常提示信息等;
* 2.支付宝是用POST方式发送通知信息,因此该页面中获取参数的方式,如:request.Form(“out_trade_no”)、$_POST[‘out_trade_no’];
* 3.支付宝主动发起通知,该方式才会被启用;
* 4.只有在支付宝的交易管理中存在该笔交易,且发生了交易状态的改变,支付宝才会通过该方式发起服务器通知(即时到账中交易状态为“等待买家付款”的状态默认是不会发送通知的);
* 5.服务器间的交互,不像页面跳转同步通知可以在页面上显示出来,这种交互方式是不可见的;
* 第一次交易状态改变(即时到账中此时交易状态是交易完成)时,不仅会返回同步处理结果,而且服务器异步通知页面也会收到支付宝发来的处理结果通知;
* 6.程序执行完后必须打印输出“success”(不包含引号)。如果商户反馈给支付宝的字符不是success这7个字符,支付宝服务器会不断重发通知,直到超过24小时22分钟。一般情况下,25小时以内完成8次通知(通知的间隔频率一般是:4m,10m,10m,1h,2h,6h,15h);
* 7.程序执行完成后,该页面不能执行页面跳转。如果执行页面跳转,支付宝会收不到success字符,会被支付宝服务器判定为该页面程序运行出现异常,而重发处理结果通知;
* 8.cookies、session等在此页面会失效,即无法获取这些数据;
* 9.该方式的调试与运行必须在服务器上,即互联网上能访问;
* 10.该方式的作用主要防止订单丢失,即页面跳转同步通知没有处理订单更新,它则去处理;
* 11.当商户收到服务器异步通知并打印出success时,服务器异步通知参数notify_id才会失效。也就是说在支付宝发送同一条异步通知时(包含商户并未成功打印出success导致支付宝重发数次通知),服务器异步通知参数notify_id是不变的。
* <p/>
* <p/>
* 签名规则:
* 1.在通知返回参数列表中,除去sign、sign_type两个参数外,凡是通知返回回来的参数皆是待验签的参数。
* 2.将剩下参数进行url_decode, 然后进行字典排序,组成字符串,得到待签名字符串:
* 3.将签名参数(sign)使用base64解码为字节码串。
* 4.使用RSA的验签方法,通过签名字符串、签名参数(经过base64解码)及支付宝公钥验证签名。
* 5.商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,并判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
* 同时需要校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email),
* 上述有任何一个验证不通过,则表明本次通知是异常通知,务必忽略。在上述验证通过后商户必须根据支付宝不同类型的业务通知,正确的进行不同的业务处理,
* 并且过滤重复的通知结果数据。在支付宝的业务通知中,只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时,支付宝才会认定为买家付款成功。
*/
@RestController
@RequestMapping("/notify/ali")
public class AliNotifyController {
private Log logger = LogFactory.getLog(this.getClass());
@Autowired
private PayService payService;
@Autowired
RefundService refundService;
// ali支付成功 异步通知 结构为:key1=valu1&key2=val2 格式, 签名:sign_type=RSA,sign字段
@RequestMapping(value = "", method = {RequestMethod.GET, RequestMethod.POST})
@ResponseBody
public Object receiveNotify(HttpServletRequest request) {
try {
String data = getRequestData(request); // data json格式
logger.info("ali pay notify result is:" + data);
payService.handlePayNotify(PayTypeCode.ALI_PAY, data);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return "success";
}
/**
* 即时到账有密退款接口,异步通知url
*
* @param request
* @return
*/
@RequestMapping(value = "/refund", method = {RequestMethod.GET, RequestMethod.POST})
@ResponseBody
public Object receiveRefundNotify(HttpServletRequest request) {
try {
String data = getRequestData(request); // data json格式
logger.info("ali pay notify result is:" + data);
refundService.handleRefundNotify(PayTypeCode.ALI_PAY, data);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return "success";
}
/**
* 转为json格式数据
*
* @param request
* @return
*/
public String getRequestData(HttpServletRequest request) {
try {
Map requestParams = request.getParameterMap();
Map<String, String> responseData = new HashMap<String, String>();
for (Iterator iterator = requestParams.keySet().iterator(); iterator.hasNext(); ) {
String name = (String) iterator.next();
String[] valueArray = (String[]) requestParams.get(name);
String values = "";
for (int i = 0; i < valueArray.length; i++) {
values = (i == valueArray.length - 1) ? values + valueArray[i]
: values + valueArray[i] + ",";
}
//乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
//values = new String(values.getBytes("ISO-8859-1"), "gbk");
responseData.put(name, values);
}
return new Gson().toJson(responseData);
} catch (Exception e) {
logger.error(e.getMessage(), e);
return null;
}
}
}