支付宝提供的接口前提都是基于用户对商家的概念,就是说支付都是打到商家的账户上的.没有个人之间的转账.
支付其实就是生成一个单号,相当于在商家那里买了一个商品.
在 支付宝开发平台有提供各种形式的demo;java版本的包名
create_direct_pay_by_user-JAVA-UTF-8;这个后面需要.
看看支付宝提供的几个接口类:
1配置类:
提示:如何获取安全校验码和合作身份者ID
*1.用您的签约支付宝账号登录支付宝网站(
www.alipay.com)
*2.点击“商家服务”(
https://b.alipay.com/order/myOrder.htm)
*3.点击“查询合作者身份(PID)”、“查询安全校验码(Key)”
//支付的流程就是本地根据配置好的参数和参数生成的签名,通过form表单,自动提交,生成链接提交给支付宝,支付宝验证处理完后,回调给return_url的地址,
//然后在本地通过上传前的参数和回调来的参数再次生成签名对比,来看是否数值有变化,这样双向签名认证后保证成功后用户在进行自己的业务逻辑处理
public class AlipayConfig {
/**
* 合作身份者ID,以2088开头由16位纯数字组成的字符串
*/
public static String partner = Global.getConfig("partner");//我是参数内容我是写在了配置文件里面
/**
* 收款支付宝账号,一般情况下收款账号就是签约账号
*/
public static String seller_email =Global.getConfig("seller_email");
/**
* 商户的私钥
*/
public static String key = Global.getConfig("key");
/**
* notify_url 交易过程中服务器通知的页面 要用 http://格式的完整路径,不允许加?id=123这类自定义参数
*这里不需要支付宝主动提供订单状态变化的回调的话,是暂时没有用的,我这里没用到
*/
public static String notify_url = Global.getConfig("notify_url");
/**
* 付完款后跳转的页面 要用 http://格式的完整路径,不允许加?id=123这类自定义参数
* 不能写成
http://localhost/
*/
public static String return_url = Global.getConfig("return_url");
// 网站商品的展示地址,不允许加?id=123这类自定义参数
//public static String show_url = Global.getConfig("");//这里我也没用到
//↑↑↑↑↑↑↑↑↑↑请在这里配置您的基本信息↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
//访问模式,根据自己的服务器是否支持ssl访问,若支持请选择https;若不支持请选择http
public static String transport = "http";
// 调试用,创建TXT日志文件夹路径;没用到
// public static String log_path = Global.getConfig("");//"D:\\";
public static String log_path = "D:\\";
/**
* 字符编码格式 目前支持 gbk 或 utf-8
* 好像必须是小写的(没试过)
*/
public static String input_charset = "utf-8";
/**
* 签名方式 不需修改
*/
public static String sign_type = "MD5";
//所有没用的配置信息,可以不用删掉,但是不能为空,不然支付宝会报错
}
2支付宝通知返回类(不用修改就能用):
public class AlipayNotify {
//支付宝传递通过双向签名认证的方式,防止用户恶意改变上传回调参数,本地将所有参数通过md5加密生成一个签名,附带传给支付宝,
//支付宝处理完后,也会传一个签名结果回来,本地在进行比对,必须相同才可以通过.
/**
* 支付宝消息验证地址
*所有的参数会跟在这里url后面传给支付宝
*/
private static final String HTTPS_VERIFY_URL = "
https://mapi.alipay.com/gateway.do?service=notify_verify&";
/**
* 验证消息是否是支付宝发出的合法消息
* @param params 通知返回来的参数数组
* @return 验证结果
*/
public static boolean verify(Map<String, String> params) {
//判断responsetTxt是否为true,isSign是否为true
//responsetTxt的结果不是true,与服务器设置问题、合作身份者ID、notify_id一分钟失效有关
//isSign不是true,与安全校验码、请求时的参数格式(如:带自定义参数等)、编码格式有关
String responseTxt = "false";
if(params.get("notify_id") != null) {
String notify_id = params.get("notify_id");
responseTxt = verifyResponse(notify_id);
}
String sign = "";
if(params.get("sign") != null) {sign = params.get("sign");}
System.out.println(sign);
boolean isSign = getSignVeryfy(params, sign);
//写日志记录(若要调试,请取消下面两行注释)
String sWord = "responseTxt=" + responseTxt + "\n isSign=" + isSign + "\n 返回回来的参数:" + AlipayCore.createLinkString(params);
System.out.println(sWord);
//AlipayCore.logResult(sWord);
if (isSign && responseTxt.equals("true")) {
return true;
} else {
return false;
}
}
/**
* 根据反馈回来的信息,生成签名结果
* @param Params 通知返回来的参数数组
* @param sign 比对的签名结果
* @return 生成的签名结果
*/
private static boolean getSignVeryfy(Map<String, String> Params, String sign) {
//过滤空值、sign与sign_type参数
Map<String, String> sParaNew = AlipayCore.paraFilter(Params);
//获取待签名字符串
String preSignStr = AlipayCore.createLinkString(sParaNew);
//获得签名验证结果
boolean isSign = false;
if(AlipayConfig.sign_type.equals("MD5") ) {
isSign = MD5.verify(preSignStr, sign, AlipayConfig.key, AlipayConfig.input_charset);
}
return isSign;
}
/**
* 获取远程服务器ATN结果,验证返回URL
* @param notify_id 通知校验ID
* @return 服务器ATN结果
* 验证结果集:
* invalid命令参数不对 出现这个错误,请检测返回处理中partner和key是否为空
* true 返回正确信息
* false 请检查防火墙或者是服务器阻止端口问题以及验证时间是否超过一分钟
*/
private static String verifyResponse(String notify_id) {
//获取远程服务器ATN结果,验证是否是支付宝服务器发来的请求
String partner = AlipayConfig.partner;
String veryfy_url = HTTPS_VERIFY_URL + "partner=" + partner + "¬ify_id=" + notify_id;
return checkUrl(veryfy_url);
}
}
3支付宝各接口请求提交类(不用修改就能用):
public class AlipaySubmit {
/**
* 支付宝提供给商户的服务接入网关URL(新)
*/
private static final String ALIPAY_GATEWAY_NEW = "
https://mapi.alipay.com/gateway.do?";
/**
* 生成签名结果
* @param sPara 要签名的数组
* @return 签名结果字符串
*/
public static String buildRequestMysign(Map<String, String> sPara) {
String prestr = AlipayCore.createLinkString(sPara); //把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
String mysign = "";
if(AlipayConfig.sign_type.equals("MD5") ) {
mysign = MD5.sign(prestr, AlipayConfig.key, AlipayConfig.input_charset);
}
return mysign;
}
/**
* 生成要请求给支付宝的参数数组
* @param sParaTemp 请求前的参数数组
* @return 要请求的参数数组
*/
private static Map<String, String> buildRequestPara(Map<String, String> sParaTemp) {
//除去数组中的空值和签名参数
Map<String, String> sPara = AlipayCore.paraFilter(sParaTemp);
//生成签名结果
String mysign = buildRequestMysign(sPara);
//签名结果与签名方式加入请求提交参数组中
sPara.put("sign", mysign);
sPara.put("sign_type", AlipayConfig.sign_type);
return sPara;
}
/**
* 建立请求,以表单HTML形式构造(默认)
* @param sParaTemp 请求参数数组
* @param strMethod 提交方式。两个值可选:post、get
* @param strButtonName 确认按钮显示文字
* @return 提交表单HTML文本
* 这里就是提交页面的form表单
*/
public static String buildRequest(Map<String, String> sParaTemp, String strMethod, String strButtonName) {
//待请求参数数组
Map<String, String> sPara = buildRequestPara(sParaTemp);
List<String> keys = new ArrayList<String>(sPara.keySet());
StringBuffer sbHtml = new StringBuffer();
sbHtml.append("<form id=\"alipaysubmit\" name=\"alipaysubmit\" action=\"" + ALIPAY_GATEWAY_NEW
+ "_input_charset=" + AlipayConfig.input_charset + "\" method=\"" + strMethod
+ "\">");
for (int i = 0; i < keys.size(); i++) {
String name = (String) keys.get(i);
String value = (String) sPara.get(name);
sbHtml.append("<input type=\"hidden\" name=\"" + name + "\" value=\"" + value + "\"/>");
}
//submit按钮控件请不要含有name属性
sbHtml.append("<input type=\"submit\" value=\"" + strButtonName + "\" style=\"display:none;\"></form>");
sbHtml.append("<script>document.forms['alipaysubmit'].submit();</script>");//这里设置自动提交,可以注释掉这些,来自己手动提交测
//试
return sbHtml.toString();
}
/**
* 建立请求,以表单HTML形式构造,带文件上传功能
* @param sParaTemp 请求参数数组
* @param strMethod 提交方式。两个值可选:post、get
* @param strButtonName 确认按钮显示文字
* @param strParaFileName 文件上传的参数名
* @return 提交表单HTML文本
*/
public static String buildRequest(Map<String, String> sParaTemp, String strMethod, String strButtonName, String strParaFileName) {
//待请求参数数组
Map<String, String> sPara = buildRequestPara(sParaTemp);
List<String> keys = new ArrayList<String>(sPara.keySet());
StringBuffer sbHtml = new StringBuffer();
sbHtml.append("<form id=\"alipaysubmit\" name=\"alipaysubmit\" enctype=\"multipart/form-data\" action=\"" + ALIPAY_GATEWAY_NEW
+ "_input_charset=" + AlipayConfig.input_charset + "\" method=\"" + strMethod
+ "\">");
for (int i = 0; i < keys.size(); i++) {
String name = (String) keys.get(i);
String value = (String) sPara.get(name);
sbHtml.append("<input type=\"hidden\" name=\"" + name + "\" value=\"" + value + "\"/>");
}
sbHtml.append("<input type=\"file\" name=\"" + strParaFileName + "\" />");
//submit按钮控件请不要含有name属性
sbHtml.append("<input type=\"submit\" value=\"" + strButtonName + "\" style=\"display:none;\"></form>");
return sbHtml.toString();
}
/**
* 建立请求,以模拟远程HTTP的POST请求方式构造并获取支付宝的处理结果
* 如果接口中没有上传文件参数,那么strParaFileName与strFilePath设置为空值
* 如:buildRequest("", "",sParaTemp)
* @param strParaFileName 文件类型的参数名
* @param strFilePath 文件路径
* @param sParaTemp 请求参数数组
* @return 支付宝处理结果
* @throws Exception
*/
public static String buildRequest(String strParaFileName, String strFilePath,Map<String, String> sParaTemp) throws Exception {
//待请求参数数组
Map<String, String> sPara = buildRequestPara(sParaTemp);
HttpProtocolHandler httpProtocolHandler = HttpProtocolHandler.getInstance();
HttpRequest request = new HttpRequest(HttpResultType.BYTES);
//设置编码集
request.setCharset(AlipayConfig.input_charset);
request.setParameters(generatNameValuePair(sPara));
request.setUrl(ALIPAY_GATEWAY_NEW+"_input_charset="+AlipayConfig.input_charset);
HttpResponse response = httpProtocolHandler.execute(request,strParaFileName,strFilePath);
if (response == null) {
return null;
}
String strResult = response.getStringResult();
return strResult;
}
/**
* MAP类型数组转换成NameValuePair类型
* @param properties MAP类型数组
* @return NameValuePair类型数组
*/
private static NameValuePair[] generatNameValuePair(Map<String, String> properties) {
NameValuePair[] nameValuePair = new NameValuePair[properties.size()];
int i = 0;
for (Map.Entry<String, String> entry : properties.entrySet()) {
nameValuePair[i++] = new NameValuePair(entry.getKey(), entry.getValue());
}
return nameValuePair;
}
/**
* 用于防钓鱼,调用接口query_timestamp来获取时间戳的处理函数
* 注意:远程解析XML出错,与服务器是否支持SSL等配置有关
* @return 时间戳字符串
* @throws IOException
* @throws DocumentException
* @throws MalformedURLException
*/
public static String query_timestamp() throws MalformedURLException,
DocumentException, IOException {
//构造访问query_timestamp接口的URL串
String strUrl = ALIPAY_GATEWAY_NEW + "service=query_timestamp&partner=" + AlipayConfig.partner + "&_input_charset" +AlipayConfig.input_charset;
StringBuffer result = new StringBuffer();
SAXReader reader = new SAXReader();
Document doc = reader.read(new URL(strUrl).openStream());
List<Node> nodeList = doc.selectNodes("//alipay/*");
for (Node node : nodeList) {
// 截取部分不需要解析的信息
if (node.getName().equals("is_success") && node.getText().equals("T")) {
// 判断是否有成功标示
List<Node> nodeList1 = doc.selectNodes("//response/timestamp/*");
for (Node node1 : nodeList1) {
result.append(node1.getText());
}
}
}
return result.toString();
}
}
4支付宝业务接口类(自己二次封装,用于处理业务逻辑):
@Controller
@RequestMapping("${apiPath}/hc/app/pay")
public class AlipayController {
//账户记录
@Autowired
private HcAccountRecordService accountService;
/**向对方(商户)支付宝支付款项
*@author xiaoyu
*@param request
*@param response
*@param money 金额
*@param remarks 描述(备注)
*@param userId 用户id(回调)
* @return
*@return
* @throws UnsupportedEncodingException
*@time 2015年12月2日下午7:34:54
*/
@RequestMapping("payMoney")
//@ResponseBody//页面跳转 不要
public String payMoney(HttpServletRequest request ,HttpServletResponse response
,String money,String remarks,String userId) throws UnsupportedEncodingException{
//支付类型
String payment_type = "1";
//必填,不能修改
//商户订单号 网站订单系统中唯一订单号,必填
String number = UtilDate.getOrderNum();//获取一个时间戳代表的唯一订单号
String out_trade_no = new String(number.getBytes("ISO-8859-1"),"UTF-8");
//订单名称必填(用userId作为名称,作为回调使用)
String subject = new String(userId.getBytes("ISO-8859-1"),"UTF-8");
//付款金额必填
String total_fee = new String(money.getBytes("ISO-8859-1"),"UTF-8");
//订单描述,作为回调用
String body = remarks;//new String(remarks.getBytes("ISO-8859-1"),"UTF-8");
//把请求参数打包成数组
Map<String, String> sParaTemp = new HashMap<String, String>();
sParaTemp.put("service", "create_direct_pay_by_user");//必填 自我认为应该是支付宝作为判别作为那种服务的标识
sParaTemp.put("partner", AlipayConfig.partner);
sParaTemp.put("seller_email", AlipayConfig.seller_email);
sParaTemp.put("_input_charset", AlipayConfig.input_charset);
sParaTemp.put("payment_type", payment_type);
sParaTemp.put("notify_url", AlipayConfig.notify_url);
sParaTemp.put("return_url", AlipayConfig.return_url);
sParaTemp.put("out_trade_no", out_trade_no);
sParaTemp.put("subject", subject);
sParaTemp.put("total_fee", total_fee);
sParaTemp.put("body", body);
//建立请求采用get模式
String sHtmlText = AlipaySubmit.buildRequest(sParaTemp,"get","确认");
request.setAttribute("sHtmlText", sHtmlText);
return "modules/hc/alipay";//跳转页面 ,生成form表单 自动提交
}
/**支付宝post回调接口
* 用于支付宝主动通知商户对订单一些状态变化的通知(我这里额业务暂时无用)
*@author xiaoyu
*@param request
*@param response
*@return
*@throws UnsupportedEncodingException
*@time 2015年12月2日下午3:07:26
*/
@SuppressWarnings("rawtypes")
@RequestMapping("aliPostCallBack")
@ResponseBody
public String aliPostCallBack(HttpServletRequest request ,HttpServletResponse response) throws UnsupportedEncodingException {
//获取支付宝POST过来反馈信息
Map<String,String> params = new HashMap<String,String>();
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; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i]
: valueStr + values[i] + ",";
}
//乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
params.put(name, valueStr);
}
//获取支付宝的通知返回参数
//商户订单号
String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"),"UTF-8");
//用户id
String userId=new String(request.getParameter("subject").getBytes("ISO-8859-1"),"UTF-8");
//描述
String remarks=new String(request.getParameter("body").getBytes("ISO-8859-1"),"UTF-8");
//支付宝交易号
String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"),"UTF-8");
//金额
String money = new String(request.getParameter("total_fee").getBytes("ISO-8859-1"),"UTF-8");
//交易状态
String trade_status = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"),"UTF-8");
System.out.println("回调参数列表:"+"out_trade_no:"+out_trade_no+
"remarks"+remarks+"trade_no"+trade_no+"money"+money+"trade_status"+trade_status);
System.out.println("这个是>?:"+params);
//获取支付宝的通知返回参数,可参考技术文档中页面跳转同步通知参数列表
if(AlipayNotify.verify(params)){//验证成功
//
//记录用户的消费记录入库
HcAccountRecord record = new HcAccountRecord();
record.setMoney(money);
record.setRemarks(remarks);
record.setUserId(userId);
record.setTypeId("1");
this.accountService.save(record);
//——请根据您的业务逻辑来编写程序(以下代码仅作参考)——
if(trade_status.equals("TRADE_FINISHED")){
//判断该笔订单是否在商户网站中已经做过处理
//如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
//请务必判断请求时的total_fee、seller_id与通知时获取的total_fee、seller_id为一致的
//如果有做过处理,不执行商户的业务程序
System.out.println("post:TRADE_FINISHED");
//注意:
//退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知
} else if (trade_status.equals("TRADE_SUCCESS")){
//判断该笔订单是否在商户网站中已经做过处理
//如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
//请务必判断请求时的total_fee、seller_id与通知时获取的total_fee、seller_id为一致的
//如果有做过处理,不执行商户的业务程序
System.out.println("post:TRADE_SUCCESS");
//注意:
//付款完成后,支付宝系统发送该交易状态通知
}
//——请根据您的业务逻辑来编写程序(以上代码仅作参考)——
return "success"; //请不要修改或删除
//
}else{//验证失败
//out.println("fail");
return "fail";
}
}
/**支付宝回调接口
*@author xiaoyu
*@param request
*@param response
*@return
*@throws UnsupportedEncodingException
*@time 2015年12月2日下午3:07:26
*/
@SuppressWarnings("rawtypes")
@RequestMapping("aliGetCallBack")
@ResponseBody
public Map<String,Object> aliGetCallBack(HttpServletRequest request ,HttpServletResponse response) throws UnsupportedEncodingException {
//获取支付宝GET过来反馈信息
ResponseMapper<Object> mapper = new ResponseMapper<Object>();
Map<String,String> params = new HashMap<String,String>();
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; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i]
: valueStr + values[i] + ",";
}
//乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
params.put(name, valueStr);
}
//获取支付宝的通知返回参数
//商户订单号
//String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"),"UTF-8");
//回调过来的用户id
String userId=new String(request.getParameter("subject").getBytes("ISO-8859-1"),"UTF-8");
//描述
String remarks=new String(request.getParameter("body").getBytes("ISO-8859-1"),"UTF-8");
//支付宝交易号
String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"),"UTF-8");
//金额
String money = new String(request.getParameter("total_fee").getBytes("ISO-8859-1"),"UTF-8");
//交易状态
String trade_status = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"),"UTF-8");
//System.out.println("这个是回调参数列表:"+params);
//计算得出通知验证结果
boolean verify_result = AlipayNotify.verify(params);
if(verify_result){//验证成功验证是为了防止用户修改get参数,这样回调验证时必须的
if(trade_status.equals("TRADE_FINISHED") || trade_status.equals("TRADE_SUCCESS")){//这里也你自己的业务逻辑就行了
//判断该笔订单是否在商户网站中已经做过处理
//如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
//如果有做过处理,不执行商户的业务程
HcAccountRecord record = new HcAccountRecord();
record.setMoney(money);
record.setRemarks(remarks);
record.setUserId(userId);
record.setTradeNo(trade_no);
int total = this.accountService.saveAndChangeAccount(record);
if(total > 0) {
mapper.setCode(SystemConstant.APP_RETURN_SUCESS);
mapper.setMessage("验证成功");
}
else {
mapper.setCode(SystemConstant.APP_RETURN_SUCESS);
mapper.setMessage("验证成功但是保存数据库失败");
}
}
}else{
mapper.setCode(SystemConstant.APP_RETURN_EXCEPTION);
mapper.setMessage("验证失败");
}
return mapper.getResultMap();
}
6.jsp页面:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>支付宝即时到账交易接口</title>
</head>
<%
String s = (String)request.getAttribute("sHtmlText");
out.println(s);
%>
<body>
</body>
</html>
如果在代码里面注释掉自动提交,这里可以看到按钮和一些参数