首先就是申请支付宝第三方接口,获取到APPID,应用发布前也可以用支付宝的沙箱环境,服务器要开启外网访问权限,因为支付宝需要返回异步通知(notify_url)和同步通知(return_url)
支付宝最近推出的支付宝接口2.0,网上有很多教程都是之前版本的,但支付宝给出了兼容方案
以上简要说明了支付过程,当然其中省略了一些步骤(比如购物车,订单等),我们重点来看支付流程。
2.1用户向商城网站发起确认订单的请求
2.2商城网站接收到请求保存订单数据到数据库或其他存储介质
2.3返回订单确认页面,页面上应该显示订单金额等信息
2.4用户确认支付,发起支付请求。注:支付请求是发送到支付网关(比如支付宝、网银在线)而不是发送到商城网站。
2.5显示支付页面
2.6用户填写认证信息(账号密码等)提交
2.7这里有两个步骤一个是扣款成功后页面跳转到支付结果页面(展示给用户),另一个是支付通知,这两步没有先后顺序可能同时执行,商城网站接收到支付通知后根据验证规则验证信息的有效性,并作出相应的更改操作(例:有效则更改订单为已付款状态,无效则记录非法请求信息)。
以支付宝为例:如果实现在网站中集成支付宝接口,首先要有一个支付宝账号,接下来向支付宝申请在线支付业务,签署协议。协议生效后有支付宝一方会给网站方一个合作伙伴ID,和安全校验码,有了这两样东西就可以按照支付宝接口文档开发支付宝接口了,在上图的几个步骤中只有4和7两个步骤在商城与支付网关之间有信息交互。在步骤4中指将数据发送到支付网关(支付宝),在步骤7中是的通知验证部分,验证网关请求网站某地址,网站按验证规则对信息进行验证记录并作出响应
(1).第一步要先能够跳转到支付宝的支付界面(可以本地测试)。
(2).第二部完成支付跳转回商家界面(必须线上测试,因为返回回来,支付宝找不到你的localhost:8080;所以必须要有域名(地址))。
支付宝开放平台下载demo和SDK(Java版本),然后根据自己所需要,拷贝进它的源代码,也可以全部拷入,但是不一定都用得到。
数据交互方式可以用jsp,也可以用控制器。支付宝给的demo是用的jsp实现。
首先导入支付宝的两个jar包:
但是感觉用控制器(Controller)更好做一些:alipay-sdk-java20170307171631.jar;commons-logging-1.1.1.jar
配置参数
package com.alipay.config;
public class AlipayConfig {
// 商户appid这是从支付宝开放平台获取的APPID
public static String APPID = "";
// 私钥 pkcs8格式的
public static String RSA_PRIVATE_KEY = "";
// 服务器异步通知页面路径 需http://或者https://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
public static String notify_url = "http://www.****.cn/PayTest/notify_url";
// 页面跳转同步通知页面路径 需http://或者https://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问 商户可以自定义同步跳转地址
public static String return_url = "http://www.****.cn/PayTest/return_url";
// 请求网关地址
public static String URL = "https://openapi.alipay.com/gateway.do";
// 编码
public static String CHARSET = "UTF-8";
// 返回格式
public static String FORMAT = "json";
// 支付宝公钥
public static String ALIPAY_PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoUg6+XycCWdQRp2qpHN5b8mw1+juv4dbcYFA61dVivDHH2sqdcL2EFfFO1Msa66T9IeFqJFyod8bVmG8vF3iql12zpceY+eHEZHmkxCBqlivLTJIfChkDmiBlARlnsZ8vK8A01VvlNdgq3g/o6+OzY/frei5bVcMVyIFBjg0fdV9vzOzb4CrQn390JLJ80aA8o5TsvlCeUHg4zAMDUyOm2suAuvgiqekEVS/qvRO+I2O1GTp3dJjPNsdmRVxNsxoU8DEApMLNYmxHiWZpFhRxRdH54ugHWkNmFGyT5y/S1g7dq/nfVgriycZ3BoKtoG4eep7cpK1rnRk+aEpLm9FmQIDAQAB";
// 日志记录目录
public static String log_path = "/log";
// RSA2
public static String SIGNTYPE = "RSA2";
}
支付
if(request.getParameter("WIDout_trade_no")!=null){
// 商户订单号,商户网站订单系统中唯一订单号,必填
String out_trade_no = new String(request.getParameter("WIDout_trade_no").getBytes("ISO-8859-1"),"UTF-8");
// 订单名称,必填
//String subject = new String(request.getParameter("WIDsubject").getBytes("ISO-8859-1"),"UTF-8");
String subject = request.getParameter("WIDsubject");
//System.out.println(subject);
// 付款金额,必填
String total_amount=new String(request.getParameter("WIDtotal_amount").getBytes("ISO-8859-1"),"UTF-8");
// 商品描述,可空
String body = new String(request.getParameter("WIDbody").getBytes("ISO-8859-1"),"UTF-8");
// 超时时间 可空
String timeout_express="2m";
// 销售产品码 必填
String product_code="QUICK_WAP_PAY";
/**********************/
// SDK 公共请求类,包含公共请求参数,以及封装了签名与验签,开发者无需关注签名与验签
//调用RSA签名方式
AlipayClient client = new DefaultAlipayClient(AlipayConfig.URL, AlipayConfig.APPID, AlipayConfig.RSA_PRIVATE_KEY, AlipayConfig.FORMAT, AlipayConfig.CHARSET, AlipayConfig.ALIPAY_PUBLIC_KEY,AlipayConfig.SIGNTYPE);
AlipayTradeWapPayRequest alipay_request=new AlipayTradeWapPayRequest();
// 封装请求支付信息
AlipayTradeWapPayModel model=new AlipayTradeWapPayModel();
model.setOutTradeNo(out_trade_no);
model.setSubject(subject);
model.setTotalAmount(total_amount);
model.setBody(body);
model.setTimeoutExpress(timeout_express);
model.setProductCode(product_code);
alipay_request.setBizModel(model);
// 设置异步通知地址
alipay_request.setNotifyUrl(AlipayConfig.notify_url);
// 设置同步地址
alipay_request.setReturnUrl(AlipayConfig.return_url);
// form表单生产
String form = "";
try {
// 调用SDK生成表单
form = client.pageExecute(alipay_request).getBody();
response.setContentType("text/html;charset=" + AlipayConfig.CHARSET);
response.getWriter().write(form);//直接将完整的表单html输出到页面
response.getWriter().flush();
response.getWriter().close();
} catch (AlipayApiException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Controller
异步通知处理支付宝会以json格式返回相应参数
支付状态trade_status的值有四种
@RequestMapping(value="notify_url")
public String notify_url(HttpServletRequest request,HttpServletResponse response) throws AlipayApiException{
Map params = new HashMap();
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"), "gbk");
params.put(name, valueStr);
}
//获取支付宝的通知返回参数,可参考技术文档中页面跳转同步通知参数列表(以下仅供参考)//
//商户订单号
String out_trade_no = request.getParameter("out_trade_no");
//支付宝交易号
String trade_no = request.getParameter("trade_no");
//交易状态
String trade_status = request.getParameter("trade_status");
//获取支付宝的通知返回参数,可参考技术文档中页面跳转同步通知参数列表(以上仅供参考)//
//计算得出通知验证结果
//boolean AlipaySignature.rsaCheckV1(Map params, String publicKey, String charset, String sign_type)
boolean verify_result = AlipaySignature.rsaCheckV1(params, AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.CHARSET, AlipayConfig.SIGNTYPE);
if(verify_result){//验证成功
//////////////////////////////////////////////////////////////////////////////////////////
//请在这里加上商户的业务逻辑程序代码
//——请根据您的业务逻辑来编写程序(以下代码仅作参考)——
if(trade_status.equals("TRADE_FINISHED")){
//判断该笔订单是否在商户网站中已经做过处理
//注意:
//如果签约的是可退款协议,退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知
//如果没有签约可退款协议,那么付款完成后,支付宝系统发送该交易状态通知。
} else if (trade_status.equals("TRADE_SUCCESS")){
//判断该笔订单是否在商户网站中已经做过处理
service.updatePayStatus(out_trade_no);
//注意:
//如果签约的是可退款协议,那么付款完成后,支付宝系统发送该交易状态通知。
}
//——请根据您的业务逻辑来编写程序(以上代码仅作参考)——
System.out.println("success"); //请不要修改或删除
}else{//验证失败
System.out.println("fail");
}
return trade_status;
}
理论上异步方法要快于同步方法,因为中间还有一个跳转流程。所以如果要将支付宝返回来的信息存入数据库,逻辑一般是写在异步方法里面,同步方法只作为页面跳转(支付完成后的跳转)一般不需要验签。如果把逻辑写在同步方法里面,客户在支付成功直接关闭窗口,活动会被关闭,就访问不到控制器了。
而异步方法访问的方法是要在浏览器上直接访问到的!但是一般我们网站都做了权限过滤的,要先去判断是否登录,没登录一般跳转登录界面。但是不论你是用的过滤器还是 shiro,还是其它的,总之你要暴露出这个方法要让支付宝能访问!