一、准备工作
- 首先要到 蚂蚁金服开发者中心 https://openhome.alipay.com/platform/home.htm 注册商家账户,并认证。
- 下载java版的sdk和demo sdk&demo https://docs.open.alipay.com/270/106291/ 下载地址
- 将sdk加入到项目中,在项目根路径下新建libs文件夹,将jar包复制进去,我使用的是maven,用maven打包并上传到公司的jar包管理。
- 利用RSARSA签名验签工具生成公钥、私钥并保存。生成公钥放到如图应用公钥的位置 https://docs.open.alipay.com/291/105971/ 。详细操作按照官网教程操作,很简单的,这里就不上图片了。
二、开发接口
- 因为开发环境是使用沙箱环境,上线后会使用真实环境,所以支付宝的一些参数我们放到配置文件里 alipay.properties appid和支付宝公钥上面图片中有,直接在网页上复制就好。
- 支付宝配置可以选择配置类也可以选择配置参数,我用的是配置文件。
# 将你自己从支付宝获取的相关信息填写
# 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号
alipay.app_id=
#商户私钥,您的PKCS8格式RSA2私钥
alipay.merchant_private_key=
# 支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm
alipay.alipay_public_key=
# 服务器异步通知页面路径
alipay.notify_url=
# 页面跳转同步通知页面路径
alipay.return_url=
# 签名方式,一般默认即可
alipay.sign_type=RSA2
# 字符编码格式,一般默认即可
alipay.charset=utf-8
# 支付宝网关
alipay.gatewayUrl=
3、开始编写写接口
这里支付宝要用的一些参数,我是通过@Configuration @PropertySource(value = {"classpath:alipay.properties"}) @ConfigurationProperties(prefix = "alipay") 三个注解将配置文件的配置信息一次性自动绑定元素值,也可以通过@Value实现注入。官方给的demo是,定义个AlipayConfig类,然后全部定义成静态变量,根据个人喜好问题选择,官方的demo中有,可以直接复制,然后修改为你自己的参数即可
注意:并非配置文件中写全部参数,具体参数设置可以参开官网参数列表
@Configuration
@PropertySource(value = {"classpath:alipay.properties"})
@ConfigurationProperties(prefix = "alipay")
public class AlipayConfig {
// 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号
public String app_id;
// 商户私钥,您的PKCS8格式RSA2私钥
public String merchant_private_key;
// 支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。
public String alipay_public_key;
// 服务器异步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
public String notify_url;
// 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
public String return_url;
// 签名方式
public String sign_type;
// 字符编码格式
public String charset = "utf-8";
// 支付宝网关
public String gatewayUrl;
public String getApp_id() {
return app_id;
}
public void setApp_id(String app_id) {
this.app_id = app_id;
}
public String getMerchant_private_key() {
return merchant_private_key;
}
public void setMerchant_private_key(String merchant_private_key) {
this.merchant_private_key = merchant_private_key;
}
public String getAlipay_public_key() {
return alipay_public_key;
}
public void setAlipay_public_key(String alipay_public_key) {
this.alipay_public_key = alipay_public_key;
}
public String getNotify_url() {
return notify_url;
}
public void setNotify_url(String notify_url) {
this.notify_url = notify_url;
}
public String getReturn_url() {
return return_url;
}
public void setReturn_url(String return_url) {
this.return_url = return_url;
}
public String getSign_type() {
return sign_type;
}
public void setSign_type(String sign_type) {
this.sign_type = sign_type;
}
public String getCharset() {
return charset;
}
public void setCharset(String charset) {
this.charset = charset;
}
public String getGatewayUrl() {
return gatewayUrl;
}
public void setGatewayUrl(String gatewayUrl) {
this.gatewayUrl = gatewayUrl;
}
}
Controller:
@Controller
public class OrderController {
@Resource
private AlipayService alipayService;
/**
* 用户请求后返回支付页面
* @param alipayParam
* @param result
* @return
*/
@RequestMapping(value = "/order")
public String alipay(AlipayParam alipayParam, ModelMap map,HttpServletRequest request,HttpServletResponse reponse) {
String alipay = alipayService.alipay(alipayParam,request,reponse);
map.addAttribute("form",alipay);
return "pay";
}
/**
* 页面跳转同步通知页面路径(由于是搭建支付环境,所以不涉及通知和数据库实际操作)
* @param request
* @return
*/
@RequestMapping(value = "/getReturnUrlInfo",method = RequestMethod.GET)
public String alipayReturnUrlInfo(HttpServletRequest request) {
return alipayService.synchronous(request);
}
/**
* 服务器异步通知页面路径(由于是搭建支付环境,所以不涉及通知和数据库实际操作)
* @param request
* @param response
*/
@RequestMapping(value = "/getNotifyUrlInfo",method = RequestMethod.POST)
public void alipayNotifyUrlInfo(HttpServletRequest request,HttpServletResponse response){
alipayService.notify(request,response);
}
}
service:
在service通过Autowired标签将前边提到的AlipayConfig 配置类注入,获取支付宝相关参数。
@Service
public class AlipayServiceImpl implements AlipayService {
Logger logger = LoggerFactory.getLogger(AlipayServiceImpl.class);
@Autowired
AlipayConfig config;
@Override
public String alipay(AlipayParam alipayParam, HttpServletRequest requests, HttpServletResponse reponse) {
// 向支付宝发送支付请求
AlipayClient alipayClient = new DefaultAlipayClient(config.gatewayUrl, config.app_id,
config.merchant_private_key, "json", config.charset, config.alipay_public_key,
config.sign_type);
// 创建Alipay支付请求对象
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
//request.setReturnUrl(AlipayConfig.return_url); // 同步通知url
//request.setNotifyUrl(AlipayConfig.notify_url);// 异步通知url
AlipayOrderParam alipayOrderParam = new AlipayOrderParam();
System.out.println(JSON.toJSONString(config.alipay_public_key));
alipayOrderParam.setOut_trade_no(UUID.randomUUID().toString().replaceAll("-", ""));// 唯一标识,订单编号
alipayOrderParam.setProduct_code("FAST_INSTANT_TRADE_PAY");
alipayOrderParam.setSubject("沙箱测试订单");// 订单名称,必填
alipayOrderParam.setTotal_amount("8888");// 付款金额
alipayOrderParam.setBody("这里是本次商品的描述");
System.out.println(JSON.toJSONString(alipayOrderParam));
request.setBizContent(JSON.toJSONString(alipayOrderParam));// 设置参数
String webForm = "";// 输出页面的表单
try {
webForm = alipayClient.pageExecute(request).getBody(); // 调用SDK生成表单
} catch (Exception e) {
logger.info("支付请求发送失败",e);
}
return webForm;
}
@Override
public String synchronous(HttpServletRequest request) {
try {
// 获取支付宝GET过来反馈信息
Map<String, String> params = new HashMap<String, String>();
Map<String, String[]> requestParams = request.getParameterMap();
for (Iterator<String> 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] + ",";
}
// 乱码解决,这段代码在出现乱码时使用
valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
params.put(name, valueStr);
}
boolean signVerified = AlipaySignature.rsaCheckV1(params, config.alipay_public_key, config.charset,config.sign_type); // 调用SDK验证签名
// ——请在这里编写您的程序(以下代码仅作参考)——
if (signVerified) {
// 商户订单号
String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"), "UTF-8");
// 支付宝交易号
String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"), "UTF-8");
// 付款金额
String total_amount = new String(request.getParameter("total_amount").getBytes("ISO-8859-1"), "UTF-8");
return "trade_no:"+trade_no+"<br/>out_trade_no:"+out_trade_no+"<br/>total_amount:"+total_amount;
} else {
return "验签失败";
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (AlipayApiException e) {
e.printStackTrace();
}
return "";
}
@Override
public void notify(HttpServletRequest request, HttpServletResponse response) {
try {
// 获取支付宝POST过来反馈信息
Map<String, String> params = new HashMap<String, String>();
Map<String, String[]> requestParams = request.getParameterMap();
for (Iterator<String> 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] + ",";
}
// 乱码解决,这段代码在出现乱码时使用
valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
params.put(name, valueStr);
}
boolean signVerified = AlipaySignature.rsaCheckV1(params, config.alipay_public_key,
config.charset, config.sign_type); // 调用SDK验证签名
// ——请在这里编写您的程序(以下代码仅作参考)——
/*
* 实际验证过程建议商户务必添加以下校验: 1、需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
* 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额), 3、校验通知中的seller_id(或者seller_email)
* 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email)
* 4、验证app_id是否为该商户本身。
*/
if (signVerified) {// 验证成功
// 商户订单号
String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"), "UTF-8");
// 支付宝交易号
String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"), "UTF-8");
// 交易状态
String trade_status = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"), "UTF-8");
if (trade_status.equals("TRADE_FINISHED")) {
// 判断该笔订单是否在商户网站中已经做过处理
// 如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
// 如果有做过处理,不执行商户的业务程序
// 注意:
// 退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知
} else if (trade_status.equals("TRADE_SUCCESS")) {
// 判断该笔订单是否在商户网站中已经做过处理
// 如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
// 如果有做过处理,不执行商户的业务程序
// 注意:
// 付款完成后,支付宝系统发送该交易状态通知
}
System.out.println("success");
} else {// 验证失败
System.out.println("error");
// 调试用,写文本函数记录程序运行情况是否正常
// String sWord = AlipaySignature.getSignCheckContentV1(params);
// AlipayConfig.logResult(sWord);
}
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (AlipayApiException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
注:沙箱环境支付,搭建环境并不涉及真是数据库操作,具体dao层和service层数据操作根据自己业务需求涉及即可。
总结:与支付宝对接,本地生成订单进行提交到支付宝同步处理完支付,支付宝会通过异步来获取用户给支付宝反馈的信息会最终确定是否支付成功,在开发过程中注意支付确认(异步)的逻辑处理即可,具体返回参数可以参开
4、进行沙箱环境的测试
访问接口地址 项目地址/alipay 我的是 127.0.0.1/alipay/order
如果后台没有报错的话,他会自动重定向到,支付宝的付款页面,如下图所示。这时候我们下载安装沙箱版的app,然后使用官方提供的账户扫描然后直接付款,付款成功后会回调后面那两个接口,在通知的那个接口里处理你的业务逻辑。查看沙箱app的登录帐户名和密码.
总结:沙箱环境每个人都可以开通支付账户并可以查看相应的买家和商家的账号,商家用户需要公司走相关流程大致一周多的时间。