《支付宝沙箱链接》
《电脑网站支付快速接入文档》
引入SKD:
Maven依赖
代码:
public class AlipayVO implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 订单名称
*/
private String subject;
/**
* 商户网站唯一订单号
*/
private String out_trade_no;
/**
* 该笔订单允许的最晚付款时间
*/
private String timeout_express;
/**
* 付款金额
*/
private String total_amount;
/**
* 销售产品码,与支付宝签约的产品码名称
*/
private String product_code;
// 省略getter和setter
}
/* *
*类名:AlipayConfig
*功能:基础配置类
*详细:设置帐户有关信息及返回路径
*修改日期:2017-04-05
*说明:
*以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
*该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
*/
public class AlipayConfig {
//↓↓↓↓↓↓↓↓↓↓请在这里配置您的基本信息↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
// 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号
public static String app_id = "";
// 商户私钥,您的PKCS8格式RSA2私钥
public static String merchant_private_key = "";
// 支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。
public static String alipay_public_key = "";
// 服务器异步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
public static String notify_url = "http://vz6snm.natappfree.cc/ocPortal/alipay/notify";
// 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
public static String return_url = "http://localhost/ocPortal/alipay/return";
// 签名方式
public static String sign_type = "RSA2";
// 字符编码格式
public static String charset = "UTF-8";
// 支付宝网关
// 正式环境
// public static String gateway_url = "https://openapi.alipay.com/gateway.do";
// 沙箱环境
public static String gateway_url = "https://openapi.alipaydev.com/gateway.do";
//↑↑↑↑↑↑↑↑↑↑请在这里配置您的基本信息↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
}
@RequestMapping("/alipay")
@Controller
public class AlipayController {
/**
* 支付网站扫码支付接口-统一下单支付接口
* @return
* @throws AlipayApiException
*/
@RequestMapping(value = "/pay", method = RequestMethod.GET)
@ResponseBody
private String alipayPay(@RequestParam("subscribeId") Long subscribeId) throws AlipayApiException {
UserSubscribe userSubscribe = subscribeService.getById(subscribeId);
AlipayVO vo = new AlipayVO();
vo.setOut_trade_no(UUID.randomUUID().toString().replace("-", ""));
vo.setTotal_amount("0.01");
vo.setSubject("商品名称");
vo.setProduct_code("FAST_INSTANT_TRADE_PAY"); //这个是固定的
String json = new Gson().toJson(vo);
AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gateway_url, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);
// 设置请求参数
AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
alipayRequest.setReturnUrl(AlipayConfig.return_url);
alipayRequest.setNotifyUrl(AlipayConfig.notify_url);
alipayRequest.setBizContent(json);
String result = alipayClient.pageExecute(alipayRequest).getBody();
return result; //这里生成一个表单,会自动提交
}
/**
* 支付宝服务器异步通知页面
* @param request
* @param out_trade_no 商户订单号
* @param trade_no 支付宝交易凭证号
* @param trade_status 交易状态
* @return
* @throws AlipayApiException
*/
@RequestMapping("/notify")
@ResponseBody
public String alipayNotify(HttpServletRequest request, String out_trade_no, String trade_no, String trade_status) throws AlipayApiException {
Map<String, String> params = getParamsMap(request);
System.out.println("notify out_trade_no:" + out_trade_no);
System.out.println("notify trade_no:" + trade_no);
System.out.println("notify trade_status:" + trade_status);
System.out.println("notify subject" + params.get("subject"));
// 验证签名
boolean signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type);
System.out.println("notify signVerified:" + signVerified);
if (signVerified) {
// 处理业务逻辑,更细订单状态等
// 支付成功
if("TRADE_SUCCESS".equals(trade_status)){
// return success可阻止支付宝继续回调
return "success";
}
return "fail";
} else {
return "fail";
}
}
/**
* 支付宝服务器同步通知页面
* @param request
* @param out_trade_no 商户订单号
* @param trade_no 支付宝交易凭证号
* @param total_amount 交易状态
* @return
* @throws AlipayApiException
*/
@RequestMapping("/return")
public String alipayReturn(HttpServletRequest request, String out_trade_no, String trade_no, String total_amount) throws AlipayApiException {
Map<String, String> params = getParamsMap(request);
System.out.println("return out_trade_no:" + out_trade_no);
System.out.println("return trade_no:" + trade_no);
System.out.println("return total_amount:" + total_amount);
// 验证签名
boolean signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type);
System.out.println("return signVerified:"+signVerified);
if(signVerified) {
} else {
}
return "redirect:/user/subscribe.html";
}
private Map<String, String> getParamsMap(HttpServletRequest request) {
Map<String,String> 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] + ",";
}
//乱码解决,这段代码在参数出现乱码时使用
// try {
// valueStr = new String(valueStr.getBytes("ISO-8859-1"), "UTF-8");
params.put(name, valueStr);
// } catch (UnsupportedEncodingException e) {
// e.printStackTrace();
// }
}
return params;
}
}
注意:
1、异步回调的路径必须暴露在外网下,而同步回调路径可根据环境选择
2、异步回调路径是被支付宝调用的,所以需要注意是否会被自己系统的登录拦截功能所拦截
3、修改订单的状态最好在异步回调里处理,同步回调只负责用户浏览器的跳转
4、AlipayConfig的参数需要确保正确
问题:
1、支付宝回调验签失败解决办法
支付宝有两个重载的方法。如果使用的是RSA加密的话,就调用下面这个方法。
boolean signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.ALIPAY_PUBLIC_KEY, "UTF-8");
如果使用的是RSA2加密的话,就调用下面这个方法。
boolean signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.ALIPAY_PUBLIC_KEY, "UTF-8", "RSA2");
2、支付宝同步和异步验签结果不一致的解决方法
同步验签正确、而异步验签错误,可能是参数中有中文乱码导致的,一般就是subject这个参数,可以在回调函数中打印出该参数,查看是否乱码。
valueStr = new String(valueStr.getBytes("ISO-8859-1"), "UTF-8");