搞定支付接口(一) 支付宝即时到账支付接口详细流程和java代码
为避免你们和我一样被支付接口搞得焦头烂额,写一个从申请开始到能收到钱为止的详细教程,实际上各个语言都可以用来集成支付接口,我用java来举例。
正所谓知己知彼,百战不殆。首先,我们来看一看支付宝平台给我们的说明。
- 打开支付宝
- 选择商家平台
- 选择电脑支付
进行如上操作后,来到如下图所示的页面
没有商家支付宝账号的需要注册,需要营业执照,经营信息,网址信息,联系人等等数据(图里说的很详细)
服务开通后,我们就可以集成了,我们点击如何集成查看文档
如图所示,我们可以直接下载demo,进行快速集成,这是最方便的办法了,我会采用这种方法,但使用即时到账接口首先需要签约,点击如何签约,支付宝就教你怎么签,无非就是填表,审核。但这一步很重要,因为我们需要生成的密钥组成参数向支付宝发出请求(下面会详细说)。
签约成功之后,我们需要合作伙伴PID和MD5密钥,在如下页面获取(图我从官网文档截得)
前期所有准备都做好了,再总结一下前期需要的东西:
- 开通商家账户和即时到账服务
- 下载demo
- pid和md5密钥
我们来继续,解压demo,选择java utf-8版本,导入项目
支付的流程为
- 买家点击提交订单
- 商家生成订单,以key=value的形式向支付宝发送请求
- 支付宝接到请求后生成订单
- 买家选择扫码或密码支付完成后,支付宝同步或异步向商家发送请求,提示订单完成
商家要传递给支付宝的参数列表在前面给的开发文档中也能找到,支付宝提示的参数有必填和不必填两种,可以自己选择。
在demo src的com\alipay\config包下有AlipayConfig类。大部分参数可以在其中配置,在使用时直接用就可以了,为了维护方便,我们可以用配置文件的方法写到文件里,动态读取。但是有一些参数需要注意:
订单号需要自己随机生成, sign签名是动态生成的。
package com.alipay.config;
/* *
*类名:AlipayConfig
*功能:基础配置类
*详细:设置帐户有关信息及返回路径
*版本:3.4
*修改日期:2016-03-08
*说明:
*以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
*该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
*/
public class AlipayConfig {
//↓↓↓↓↓↓↓↓↓↓请在这里配置您的基本信息↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
// 合作身份者ID,签约账号,以2088开头由16位纯数字组成的字符串,查看地址:https://b.alipay.com/order/pidAndKey.htm
public static String partner = "2088好几个数字";
// 收款支付宝账号,以2088开头由16位纯数字组成的字符串,一般情况下收款账号就是签约账号
public static String seller_id = partner;
// MD5密钥,安全检验码,由数字和字母组成的32位字符串,查看地址:https://b.alipay.com/order/pidAndKey.htm
public static String key = "好长一串数字和字母";
// 服务器异步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
public static String notify_url = "http://www.wechat.com/AliPayTest/pay_notify_url"; // 体会到我的幽默感了吗
// 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
public static String return_url = "http://www.wechat.com/AliPayTest/pay_return_url";
// 签名方式
public static String sign_type = "MD5";
// 调试用,创建TXT日志文件夹路径,见AlipayCore.java类中的logResult(String sWord)打印方法。
public static String log_path = "F:\\";
// 字符编码格式 目前支持 gbk 或 utf-8
public static String input_charset = "utf-8";
// 支付类型 ,无需修改
public static String payment_type = "1";
// 调用的接口名,无需修改
public static String service = "create_direct_pay_by_user";
//↑↑↑↑↑↑↑↑↑↑请在这里配置您的基本信息↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
//↓↓↓↓↓↓↓↓↓↓ 请在这里配置防钓鱼信息,如果没开通防钓鱼功能,为空即可 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
// 防钓鱼时间戳 若要使用请调用类文件submit中的query_timestamp函数
public static String anti_phishing_key = "";
// 客户端的IP地址 非局域网的外网IP地址,如:221.0.0.1
public static String exter_invoke_ip = "";
//↑↑↑↑↑↑↑↑↑↑请在这里配置防钓鱼信息,如果没开通防钓鱼功能,为空即可 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
}
写一个配置类获取配置
public class Property {
private static Properties p = new Properties();
static {
InputStream in = AlipayConfig.class.getResourceAsStream("alipay.properties");
try {
p.load(in);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
static public String getProperty(String property) {
return p.getProperty(property);
}
}
然后将参数配置写到alipay.properties的文件,有参数要修改的时候不用重新编译代码,只要修改配置文件就可以了。
前面主要是配置,按支付宝提供的文档配置好各种参数后,配置这一步就完成了。
后面我们来讲一讲下订单。
买家下订单之后,我们接收请求参数,再加上配置的参数生成要发送给支付宝的订单Map集合。
// 订单号:
String out_trade_no = "test20170213145553";
//把请求参数打包成Map
Map<String, String> sParaTemp = new HashMap<>();
sParaTemp.put("service", AlipayConfig.service);
sParaTemp.put("partner", AlipayConfig.partner);
sParaTemp.put("seller_id", AlipayConfig.seller_id);
sParaTemp.put("_input_charset", AlipayConfig.input_charset);
sParaTemp.put("payment_type", AlipayConfig.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", desc);
调用支付宝demo给的类的buildRequest静态方法,将刚才的sParaTemp传递过去,生成String类型的字符串,这个字符串其实是个超链接,直接放地址栏上就直接将参数发送给支付宝了。
注:支付宝demo在幕后做的工作有很多,分为以下几步,若不想用demo的可以自己在官网查阅文档实现
- 组装待签名字符串
- 筛选
大部分支付宝接口中要剔除sign_type、sign两个参数,个别接口只剔除sign参数。存在空值的参数必须剔除。
- 排序
在参数集合中,根据参数(不是参数对应的值)的第一个字符的键值ASCII码递增排序,如果遇到相同字符则按照第二个字符的键值ASCII码递增排序,以此类推。
- 拼接
在参数集合中,把每个参数及其值组合成“参数=参数值”的格式(在无线产品手机安全支付中,每个参数的组合格式是“参数=”参数值””),并且把这些参数用&字符连接起来
- 调用签名函数
- 签名函数
调用md5加密函数,对已经与MD5密钥拼接好的新字符串做加密运算
- 签名结果的用途
- 得到的签名结果也是一串字符串,这串字符串便是sign参数的值,把这串字符串赋值于sign参数。
String sHtmlText = AlipaySubmit.buildRequest(sParaTemp, "post", "确认"); // 这里使用支付宝demo给的类来生成请求参数,这点支付宝比微信的文档方便太多了
直接用response.write()或者我用的struts,用action跳转到页面再用字符串链接跳转就行了。
PrintWriter out = response.getWriter();
out.print(sHtmlText);
struts 支付action:
context.put("jump", sHtmlText);
return "send"; // 跳转给支付宝
struts.xml
<result name="send">/WEB-INF/jump.jsp</result>
jump.jsp:
${requestScope.jump }
这样就会直接跳转了。
支付宝接到参数就会生成二维码,客户支付后商户后台就会收到支付宝发送的返回参数,有sign验证签名,订单号,价格等等信息,若出错的话支付宝也会返回错误码
ILLEGAL_SIGN
等,可在支付宝历史公共错误码
页面查看。
若支付成功,支付宝有同步和异步两种方式回调商户网站,这个看当初怎么配置的,配置的时候会把回调地址写在请求参数里发给支付宝,若在自己的电脑上调试,一般会调用同步方法,在服务器上会调用异步方法。
// 服务器异步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
public static String notify_url = "http://www.wechat.com/pay_notify_url"; // 请再次感受我的幽默感
// 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
public static String return_url = "http://www.wechat.com/pay_return_url";
假如你的配置是这样写的,支付宝就会调用同步urlhttp://www.wechat.com/pay_notify_url
或者异步urlhttp://www.wechat.com/pay_return_url
并附带请求参数,你在接到请求参数后,需要和你自己的信息比对,若成功的话,就算这笔订单完成了。
异步回调
public void notifyUrl() {
Map<String, Object> requestParams = ActionContext.getContext().getParameters();
Map<String, String> params = null;
HttpServletResponse response = ServletActionContext.getResponse();
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = null;
try {
writer = response.getWriter();
} catch (IOException e) {
log.warn("支付宝异步支付获取输出流失败:" + e.getMessage());
return;
}
try {
params = tradeService.splitParam(requestParams);// 分割参数
} catch (UnsupportedEncodingException e) {
log.warn("alipay sign convert failed: " + e.getMessage());
return ;
}
//获取支付宝的通知返回参数,可参考技术文档中页面跳转同步通知参数列表(以下仅供参考)//
if (AlipayNotify.verify(params)){ //验证成功
if (trade_status.equals("TRADE_FINISHED") || trade_status.equals("TRADE_SUCCESS")) {
Order verifiedAlipay = tradeService.verifiedAlipay(seller_id, out_trade_no, total_fee);
if (verifiedAlipay == null) {
log.warn("参数验证失败");
return; // 跳转支付失败页
}
writer.print("success"); //请不要修改或删除
return;
} else {
log.warn("没有完成订单");
return;
}
} else {
log.warn("支付宝异步支付验证失败");
writer.print("fail"); //请不要修改或删除
}
}
同步回调
public String returnUrl() {
Map<String, Object> requestParams = ActionContext.getContext().getParameters();
Map<String, String> params = null;
try {
params = tradeService.splitParam(requestParams);
if (params == null) {
log.warn("签名验证失败-参数列表为空");
return TRADEERROR; // 跳转支付失败页
}
} catch (UnsupportedEncodingException e) {
log.warn("alipay sign convert failed: " + e.getMessage());
}
if (AlipayNotify.verify(params)) {// 验证成功
if (trade_status.equals("TRADE_FINISHED") || trade_status.equals("TRADE_SUCCESS")) {
Order verifiedAlipay = tradeService.verifiedAlipay(seller_id, out_trade_no, total_fee);
if (verifiedAlipay == null) {
log.warn("参数验证失败");
return TRADEERROR; // 跳转支付失败页
}
putSession(S_TRADE_FINISHED, verifiedAlipay);// 将支付详情放入session域中,到页面显示
} else {
log.warn("没有完成订单");
return TRADEERROR; // 跳转支付失败页
}
} else {// 验证失败
log.warn("签名验证失败");
return TRADEERROR; // 跳转支付失败页
}
return TRADESUCCESS;
}
然后就全部完毕了。
这个文章写的并不太满意,这是我做过支付宝接口很久以后才写的文章,好多东西都记不起来了,之前记得还在官网看过支付宝支付流程的序列图,那个很清晰,现在我也没找到。
这几天也一直在忙,每次都是写几个字就不得不忙别的了,断断续续前后也有点连不起来,以后有时间我会再把这篇文章细化修改一下,看了有不懂的地方也欢迎留言,我看到会尽量解答
原文地址:https://www.cnblogs.com/jpfss/p/9942601.html