为避免大家和我同样被支付接口搞得焦头烂额,写一个从申请开始到能收到钱为止的详细教程,实际上各个语言均可以用来集成支付接口,我用java来举例。html
正所谓知己知彼,百战不殆。首先,咱们来看一看支付宝平台给咱们的说明。java
进行如上操做后,来到以下图所示的页面web
没有商家支付宝帐号的须要注册,须要营业执照,经营信息,网址信息,联系人等等数据(图里说的很详细)安全
服务开通后,咱们就能够集成了,咱们点击如何集成查看文档bash
如图所示,咱们能够直接下载demo,进行快速集成,这是最方便的办法了,我会采用这种方法,但使用即时到帐接口首先须要签约,点击如何签约,支付宝就教你怎么签,无非就是填表,审核。但这一步很重要,由于咱们须要生成的密钥组成参数向支付宝发出请求(下面会详细说)。服务器
签约成功以后,咱们须要合做伙伴PID和MD5密钥,在以下页面获取(图我从官网文档截得)
微信
前期全部准备都作好了,再总结一下前期须要的东西:markdown
咱们来继续,解压demo,选择java utf-8版本,导入项目session
支付的流程为异步
商家要传递给支付宝的参数列表在前面给的开发文档中也能找到,支付宝提示的参数有必填和没必要填两种,能够本身选择。
在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;
}
而后就所有完毕了。
这个文章写的并不太满意,这是我作过支付宝接口好久之后才写的文章,好多东西都记不起来了,以前记得还在官网看过支付宝支付流程的序列图,那个很清晰,如今我也没找到。 这几天也一直在忙,每次都是写几个字就不得不忙别的了,断断续续先后也有点连不起来,之后有时间我会再把这篇文章细化修改一下,看了有不懂的地方也欢迎留言,我看到会尽可能解答