目录
本篇介绍本人在实际项目中接入支付宝支付接口过程中遇到的问题及相应的解决办法,在此做总结加深印象,以便日后查阅。
1.获取支付宝支付参数
- 首先到支付宝开放平台注册一个账号,并登录进入开发者中心
- 正式接入可通过创建应用功能填写应用的信息获得APPID,然后生成密钥对并上传应用公钥到平台,生成密钥的操作可参照开放平台文档
- 密钥配置成功后便获得三个关键支付参数APPID,应用私钥,支付宝公钥
- 最后还要配置上应用的网关和授权回调地址,注意这里的授权回调地址要求公网能访问得到,如果没有的话使用一些内网映射软件一样可以达到效果
- 结果图如下(这里用了沙箱环境的图代替)
2.添加支付宝支付SDK依赖
maven添加支付宝sdk依赖
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>3.7.4.ALL</version>
</dependency>
3.编写支付宝支付配置类
/**
* 支付宝支付主要参数
* @author csk
*/
public class AlipayConfig {
//APPID
public static String appid = "xxxx";
//支付宝公钥
public static String publickey = "xxxx";
//应用私钥
public static String privatekey = "xxxx";
// 服务器异步回调地址,必须外网可以正常访问
public static String notify_url = “http://xxxx/alipay/alipayNotifyURL”;
// 支付宝同步回调地址,必须外网可以正常访问
public static String return_url = “http://xxxx/alipay/alipayReturnURL”;
// 请求网关地址(这里用的是沙箱环境的支付测试接口)
public static String URL = "https://openapi.alipaydev.com/gateway.do";
// 编码
public static String CHARSET = "text/html;charset=UTF-8";
// 返回格式
public static String FORMAT = "json";
// 日志记录目录
public static String log_path = "/log";
// RSA2
public static String SIGNTYPE = "RSA2";
}
4.订单发起支付宝支付逻辑处理
Controller层
/**
* 调用支付宝接口
* @throws Exception
*/
public void alipay(HttpServletRequest request, HttpServletResponse response) throws Exception{
String res = alipayService.payment(request,response);
response.setContentType(AlipayConfig.CHARSET);
PrintWriter writer = response.getWriter();
writer.write(res);//直接将完整的表单html输出到页面
writer.flush();
}
Service层
/**
* 构建支付主要参数
* @param request
* @param response
* @return
* @throws Exception
*/
public String payment(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 商户订单号,商户网站订单系统中唯一订单号,必填
String out_trade_no = request.getParameter("orderid");
Order order = orderService.findById(out_trade_no);
// 订单名称,必填
String subject = "xx系统订单";
// 付款金额,必填
String total_amount = order.getTotalAmoount();
// 商品描述,可空
String body = order.getDescription();
// 超时时间 可空
String timeout_express = "30m";
// 销售产品码 必填
String product_code = "FAST_INSTANT_TRADE_PAY";
return this.alipayWebChannel(out_trade_no, subject, total_amount, body, timeout_express, product_code, response);
}
/**
* 调起电脑网站支付宝支付,返回支付页面
* @param out_trade_no 订单编号
* @param subject 订单名称
* @param total_amount 付款金额
* @param body 商品描述
* @param timeout_express 超时时间
* @param product_code 产品码
* @param response
* @return
* @throws Exception
*/
private String alipayWebChannel(String out_trade_no, String subject,
String total_amount, String body, String timeout_express,
String product_code, HttpServletResponse response) throws Exception {
// SDK 公共请求类,包含公共请求参数,以及封装了签名与验签,开发者无需关注签名与验签
AlipayClient client = new DefaultAlipayClient(AlipayConfig.URL,AlipayConfig.appid,AlipayConfig.privatekey,
AlipayConfig.FORMAT,AlipayConfig.CHARSET,AlipayConfig.publickey,AlipayConfig.SIGNTYPE);
// 设置请求参数
AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
AlipayTradePagePayModel model = new AlipayTradePagePayModel();
model.setOutTradeNo(out_trade_no);
model.setSubject(subject);
model.setTotalAmount(total_amount);
model.setBody(body);
model.setTimeoutExpress(timeout_express);
model.setProductCode(product_code);
alipayRequest.setBizModel(model);
alipayRequest.setReturnUrl(AlipayConfig.return_url);
alipayRequest.setNotifyUrl(AlipayConfig.notify_url);
// 请求
String result = null;
try {
result = client.pageExecute(alipayRequest).getBody();
} catch (AlipayApiException e) {
e.printStackTrace();
}
response.setContentType("text/html; charset=gbk");
return result;
}
这里是调起电脑网站支付宝支付,返回支付宝的支付页面,此时如果是沙箱环境则可以用支付宝沙箱扫码模拟支付。
5.支付成功后的同步回调
/**
* 支付宝同步回调
* @throws Exception
*/
public void alipayReturnURL(HttpServletRequest request, HttpServletResponse response) throws Exception{
logger.info("----------alipayReturn----------");
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] + ",";
}
//乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
//valueStr = new String(valueStr.getBytes("ISO-8859-1"), "gbk");
params.put(name, valueStr);
}
//验签
boolean verify_result = AlipaySignature.rsaCheckV1(params, AlipayConfig.publickey, AlipayConfig.CHARSET, AlipayConfig.SIGNTYPE);
if(verify_result){
logger.info("----------verify-success----------");
//验签通过返回页面
String html = "";
response.getWriter().print(html);
response.getWriter().flush();
}else{
logger.info("----------verify-fail----------");
}
}
6.支付成功后的异步回调
/**
* 支付宝异步回调
* @throws Exception
*/
@Transaction
public void alipayNotifyURL(HttpServletRequest request, HttpServletResponse response) throws Exception{
logger.info("**********alipayNotify**********");
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] + ",";
}
//乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
//valueStr = new String(valueStr.getBytes("ISO-8859-1"), "gbk");
params.put(name, valueStr);
}
//验签
boolean verify_result = AlipaySignature.rsaCheckV1(params, AlipayConfig.publickey, AlipayConfig.CHARSET, AlipayConfig.SIGNTYPE);
if(verify_result){
logger.info("**********verify-success**********");
//交易状态
if ("TRADE_SUCCESS".equals(params.get("trade_status"))){
//支付成功
//此处写订单状态改变及生成收款单之类的逻辑,具体视业务而定
}
//此处返回成功的标志给支付宝服务器,如果没有返回,支付宝服务器会多次调用异步回调接口
response.getWriter().print("success");
response.getWriter().flush();
}else{
logger.info("**********verify-fail**********");
response.getWriter().print("fail");
response.getWriter().flush();
}
}
总结
在本项目中的业务也包括手机端支付的功能,但是实际需求一个公司及旗下多个子公司都有各自的收款账户,这也使得无法使用原生的APP支付,最终决定网页端采用网页扫码支付,手机端采用H5支付的方法,这两种处理方法类似不再赘述,涉及业务、隐私的代码已经过处理删减。