支付宝的签名方式有两种(普通公钥方式、公钥证书方式),一般最常用的就是普通公钥方式,也相对比较简单,但是公钥证书方式是现在支付宝支付官方文档上面推荐的签名方式.
申请步骤可以参照官方文档:
参考链接:https://docs.open.alipay.com/291/105971/
pom依赖,不要乱用,网上有很多不同的依赖,我都试过,不好用,这个依赖是我试过,好用的
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.16.50.ALL</version>
</dependency>
yml的相关配置
公钥证书模式
实现类里(支付宝也是先请求预下单,返回一个body的数据,安卓拿着body去拉起支付宝支付,最后支付成功,会调用自己写的回调接口)
/**
* 把预下单封装成一个对象,共同调用这个
* @param totalAmount 金额,以元为单位
* @param orderNo 商户自定义编号
* @return
*/
public R<String> aliPrePay( BigDecimal totalAmount,String orderNo,String notifyUrl){
CertAlipayRequest certAlipayRequest = new CertAlipayRequest();
certAlipayRequest.setServerUrl(getwayUrl);//设置网关地址
certAlipayRequest.setAppId(appId);//设置应用Id
certAlipayRequest.setPrivateKey(merchantPrivateKey); //设置应用私钥
certAlipayRequest.setFormat("json"); //设置请求格式,固定值json
certAlipayRequest.setCharset(charset);//设置字符集
certAlipayRequest.setSignType(signType); //设置签名类型
certAlipayRequest.setCertPath(certPath);//设置应用公钥证书路径 ,一定是本地的绝对路径
certAlipayRequest.setAlipayPublicCertPath(alipayPublicCertPath); //设置支付宝公钥证书路径,一定是本地的绝对路径
certAlipayRequest.setRootCertPath(rootCertPath);//设置支付宝根证书路径,一定是本地的绝对路径
//获得初始化的alipayClient,构造client
AlipayClient alipayClient = null;
String result = "";
try {
//公钥证书,一定要使用这个构造方法
alipayClient = new DefaultAlipayClient(certAlipayRequest);
//实例化具体API对应的request类,
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
//按照需要传入参数,以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setSubject(bodyMall);//订单标题
model.setOutTradeNo(orderNo);//商户订单号,商家自定义
model.setTotalAmount(totalAmount+"");//金额,string类型
request.setBizModel(model);
request.setNotifyUrl(notifyUrl);//异步回调地址
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
result = response.getBody();
return R.success(result);
} catch (AlipayApiException e) {
e.printStackTrace();
return R.fail("预下单失败!");
}
}
/**
* 商城订单的支付宝预下单
*
* @param memberId 用户id
* @param totalAmount 金额
* @return
*/
@Override
public R<String> aliPayMallOrder(Integer memberId, BigDecimal totalAmount,Integer orderId) {
String orderNo = OrderNoUtil.getOrderId();
R<String> pay = aliPrePay(totalAmount, orderNo,notifyUrlMallOrder);
if(!pay.getIsSuccess()){ //失败直接返回上一级返回的内容
return pay;
}
//生成支付宝的预下单对象
ALPrePay prePay = new ALPrePay();
prePay.setMemberId(memberId);//用户id
prePay.setOrderNo(orderNo);//订单编号
prePay.setOrderId(orderId);//订单id
prePay.setOrderType(MallEmums.PRE_PAY_TYPE_MALL.getCode());//订单支付类别
prePay.setTotalAmount(totalAmount.multiply(new BigDecimal(100)).intValue());//金额已分展示
baseMapper.insert(prePay);
return pay;
}
拉起支付宝的回调以后,就要用写回调方法
/**
* 支付宝回调
* 接口封装好, 统一验证签名,并返回自定义的商户编号 outTradeNo
* @param request
* @return outTradeNo
*/
public R<String> verification(HttpServletRequest request){
//回调的待验签字符串
String resultInfo = convertRequestParamsToString(request).toString();
//对待签名字符串数据通过&进行拆分
String [] temp = resultInfo.split("&");
LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
//把拆分数据放在map集合内
for (int i = 0; i < temp.length; i++) {
String[] arr = temp[i].split("=", 2); //通过"="号分割成2个数据
String[] tempAagin = new String[arr.length]; //再开辟一个数组用来接收分割后的数据
for (int j = 0; j < arr.length; j++) {
tempAagin[j] = arr[j];
}
map.put(tempAagin[0], tempAagin[1]);
}
System.out.println(map);
//验签方法
boolean signVerified= false;
try {
signVerified = AlipaySignature.rsaCertCheckV1(map, alipayPublicCertPath, charset,signType);
if(signVerified){
System.out.println("支付宝回调签名认证成功");
String outTradeNo = map.get("out_trade_no");//商户自定义的订单编号
String tradeNo = map.get("trade_no");//支付宝交易凭证号
String buyerLogonId = map.get("buyer_logon_id");//支付宝交易凭证号
System.out.println("买家支付宝账号:"+buyerLogonId);
ALPrePay prePay = prePayService.getALPrePayByOutTradeNo(outTradeNo);
prePay.setTradeNo(tradeNo);
prePayMapper.updateById(prePay);
return R.success(outTradeNo).setMsg("支付成功");
}else{
System.out.println("支付宝回调签名认证失败");
return R.fail("支付失败");
}
} catch (AlipayApiException e) {
e.printStackTrace();
return R.fail("支付失败");
}
}
// 将request中的参数转换成Map
private StringBuffer convertRequestParamsToString(HttpServletRequest request) {
StringBuffer info = new StringBuffer();
Set<Map.Entry<String, String[]>> entrySet = request.getParameterMap().entrySet();
for (Map.Entry<String, String[]> entry : entrySet) {
String name = entry.getKey();
String[] values = entry.getValue();
int valLen = values.length;
if (valLen == 1) {
info.append(name).append("=").append(values[0]).append("&");
} else if (valLen > 1) {
StringBuilder sb = new StringBuilder();
for (String val : values) {
sb.append(",").append(val);
}
info.append(name).append("=").append(sb.toString().substring(1)).append("&");
} else {
info.append(name).append("=").append("").append("&");
}
}
return info;
}
/**
* 商城的支付宝回调
* 1.从请求中找到商户自定义的outTradeNo,
* 2.根据商户自定义的outTradeNo去找预下单表中的orderId
* 3.找到对应的订单数据,然后在调用对应的修改订单接口
* @param request
* @return
*/
@Override
public R aliPayMallOrderCallBack(HttpServletRequest request) {
R verification = verification(request);
Boolean isSuccess = verification.getIsSuccess();
if(isSuccess){
//验证签名正确,开始处理订单的业务逻辑
//1.取出outTradeNo
String outTradeNo= verification.getData().toString();
//2.根据outTradeNo从预下单中取出orderId
ALPrePay prePay = prePayService.getALPrePayByOutTradeNo(outTradeNo);
//订单id
Integer orderId = prePay.getOrderId();
//订单no
String orderNo = prePay.getOrderNo();
//因为回调会依次调用很多次,所有每一次更改订单状态要判断他是否支付过,如果支付过了,就不可以在支付了
Boolean b = orderFeignService.hasPayByOrderId(orderId).getData();
if (!b) { //false,已支付
return R.success().setMsg("支付成功");
}
//支付成功,调用更改订单的状态等数据
AyjMallOrder order = orderFeignService.getOrder(orderId).getData();
if (order.getOrderStatus() == MallEmums.ORDER_STATUS_NOT.getCode()) { //订单未支付
orderFeignService.updatePaymentOrder(orderId, orderNo, MallEmums.ORDER_PAY_TYPE_ALIPAY.getCode()).getData();
} else { //优惠买单未支付
orderFeignService.updatePaymentDiscounts(orderId, orderNo, MallEmums.ORDER_PAY_TYPE_ALIPAY.getCode());
}
return R.success().setMsg("支付成功");
}else{
return verification;
}
}
退款
/**
* 支付宝商城退款
* @param orderNo
* @return
*/
@Override
public R aliPayRefund(String orderNo) {
ALPrePay prePay = prePayService.getALPrePayByOutTradeNo(orderNo);
AlipayTradeRefundRequest alipay_request = new AlipayTradeRefundRequest();
AlipayTradeRefundModel model=new AlipayTradeRefundModel();
model.setOutTradeNo(orderNo);//商户自定义的单号
model.setTradeNo(prePay.getTradeNo());//支付宝交易凭证号
model.setRefundAmount(new BigDecimal(prePay.getTotalAmount()).divide(new BigDecimal(100))+"");//金额
model.setRefundReason(refundBodyMall);//退款原因
alipay_request.setBizModel(model);
CertAlipayRequest certAlipayRequest = new CertAlipayRequest();
certAlipayRequest.setServerUrl(getwayUrl);//设置网关地址
certAlipayRequest.setAppId(appId);//设置应用Id
certAlipayRequest.setPrivateKey(merchantPrivateKey); //设置应用私钥
certAlipayRequest.setFormat("json"); //设置请求格式,固定值json
certAlipayRequest.setCharset(charset);//设置字符集
certAlipayRequest.setSignType(signType); //设置签名类型
certAlipayRequest.setCertPath(certPath);//设置应用公钥证书路径 ,一定是本地的绝对路径
certAlipayRequest.setAlipayPublicCertPath(alipayPublicCertPath); //设置支付宝公钥证书路径,一定是本地的绝对路径
certAlipayRequest.setRootCertPath(rootCertPath);//设置支付宝根证书路径,一定是本地的绝对路径
//获得初始化的alipayClient,构造client
AlipayClient alipayClient = null;
try {
//公钥证书,一定要使用这个构造方法
alipayClient = new DefaultAlipayClient(certAlipayRequest);
AlipayTradeRefundResponse alipay_response = alipayClient.certificateExecute(alipay_request);
String alipayRefundStr = alipay_response.getBody();
System.out.println(alipayRefundStr);
prePay.setHasRefund(MallEmums.YES.getCode());//已退款
prePayMapper.updateById(prePay);
return R.success().setMsg("退款成功");
} catch (AlipayApiException e) {
e.printStackTrace();
return R.fail("服务器异常,退款失败");
}
}
普通公钥模式
@Value("${ayj.alipay.appId}")
private String appId;
@Value("${ayj.alipay.merchantPrivateKey}")
private String merchantPrivateKey;
@Value("${ayj.alipay.merchantPublicKey}")
private String merchantPublicKey;
@Value("${ayj.alipay.alipayPublicKey}")
private String alipayPublicKey;
@Value("${ayj.alipay.signType}")
private String signType;
@Value("${ayj.alipay.charset}")
private String charset;
@Value("${ayj.alipay.getwayUrl}")
private String getwayUrl;
@Value("${ayj.alipay.certPath}")
private String certPath;
@Value("${ayj.alipay.alipayPublicCertPath}")
private String alipayPublicCertPath;
@Value("${ayj.alipay.rootCertPath}")
private String rootCertPath;
@Value("${ayj.alipay.bodyMall}")
private String bodyMall;
@Value("${ayj.alipay.notifyUrlMallOrder}")
private String notifyUrlMallOrder;
@Value("${ayj.alipay.notifyUrlMallOrderQqh}")
private String notifyUrlMallOrderQqh;
@Value("${ayj.alipay.notifyUrlMallOrderZwy}")
private String notifyUrlMallOrderZwy;
@Value("${ayj.alipay.notifyUrlCharity}")
private String notifyUrlCharity;
@Value("${ayj.alipay.notifyUrlChatGift}")
private String notifyUrlChatGift;
/**
* 把预下单封装成一个对象,共同调用这个
* @param totalAmount 金额,以元为单位
* @param orderNo 商户自定义编号
* @return
*/
public R<String> aliPrePay( BigDecimal totalAmount,String orderNo,String notifyUrl){
//实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
AlipayClient alipayClient = new DefaultAlipayClient(getwayUrl, appId,merchantPrivateKey , "json", charset,
alipayPublicKey, signType);
String result = "";
try {
//实例化具体API对应的request类,
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
//按照需要传入参数,以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setSubject(bodyMall);//订单标题
model.setOutTradeNo(orderNo);//商户订单号,商家自定义
model.setTotalAmount(totalAmount+"");//金额,string类型
request.setBizModel(model);
request.setNotifyUrl(notifyUrl);//异步回调地址
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
result = response.getBody();
return R.success(result);
} catch (AlipayApiException e) {
e.printStackTrace();
return R.fail("预下单失败!");
}
}
/**
* 商城订单的支付宝预下单
*
* @param memberId 用户id
* @param totalAmount 金额
* @return
*/
@Override
public R<String> aliPayMallOrder(Integer memberId, BigDecimal totalAmount,Integer orderId) {
String orderNo = OrderNoUtil.getOrderId();
R<String> pay = aliPrePay(totalAmount, orderNo,notifyUrlMallOrder);
if(!pay.getIsSuccess()){ //失败直接返回上一级返回的内容
return pay;
}
//生成支付宝的预下单对象
ALPrePay prePay = new ALPrePay();
prePay.setMemberId(memberId);//用户id
prePay.setOrderNo(orderNo);//订单编号
prePay.setOrderId(orderId);//订单id
prePay.setOrderType(MallEmums.PRE_PAY_TYPE_MALL.getCode());//订单支付类别
prePay.setTotalAmount(totalAmount.multiply(new BigDecimal(100)).intValue());//金额已分展示
baseMapper.insert(prePay);
return pay;
}
回调方法
/**
* 支付宝回调
* 接口封装好, 统一验证签名,并返回自定义的商户编号 outTradeNo
* @param request
* @return outTradeNo
*/
public R<String> verification(HttpServletRequest request){
//回调的待验签字符串
String resultInfo = convertRequestParamsToString(request).toString();
//对待签名字符串数据通过&进行拆分
String [] temp = resultInfo.split("&");
LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
//把拆分数据放在map集合内
for (int i = 0; i < temp.length; i++) {
String[] arr = temp[i].split("=", 2); //通过"="号分割成2个数据
String[] tempAagin = new String[arr.length]; //再开辟一个数组用来接收分割后的数据
for (int j = 0; j < arr.length; j++) {
tempAagin[j] = arr[j];
}
map.put(tempAagin[0], tempAagin[1]);
}
System.out.println(map);
//验签方法
boolean signVerified= false;
try {
signVerified = AlipaySignature.rsaCheckV1 (map,alipayPublicKey, charset,signType); //这里采用的普通公钥方式
if(signVerified){
System.out.println("支付宝回调签名认证成功");
String outTradeNo = map.get("out_trade_no");//商户自定义的订单编号
String tradeNo = map.get("trade_no");//支付宝交易凭证号
String buyerLogonId = map.get("buyer_logon_id");//支付宝交易凭证号
System.out.println("买家支付宝账号:"+buyerLogonId);
ALPrePay prePay = prePayService.getALPrePayByOutTradeNo(outTradeNo);
prePay.setTradeNo(tradeNo);
prePayMapper.updateById(prePay);
return R.success(outTradeNo).setMsg("支付成功");
}else{
System.out.println("支付宝回调签名认证失败");
return R.fail("支付失败");
}
} catch (AlipayApiException e) {
e.printStackTrace();
return R.fail("支付失败");
}
}
// 将request中的参数转换成Map
private StringBuffer convertRequestParamsToString(HttpServletRequest request) {
StringBuffer info = new StringBuffer();
Set<Map.Entry<String, String[]>> entrySet = request.getParameterMap().entrySet();
for (Map.Entry<String, String[]> entry : entrySet) {
String name = entry.getKey();
String[] values = entry.getValue();
int valLen = values.length;
if (valLen == 1) {
info.append(name).append("=").append(values[0]).append("&");
} else if (valLen > 1) {
StringBuilder sb = new StringBuilder();
for (String val : values) {
sb.append(",").append(val);
}
info.append(name).append("=").append(sb.toString().substring(1)).append("&");
} else {
info.append(name).append("=").append("").append("&");
}
}
return info;
}
/**
* 商城的支付宝回调
* 1.从请求中找到商户自定义的outTradeNo,
* 2.根据商户自定义的outTradeNo去找预下单表中的orderId
* 3.找到对应的订单数据,然后在调用对应的修改订单接口
* @param request
* @return
*/
@Override
public R aliPayMallOrderCallBack(HttpServletRequest request) {
R verification = verification(request);
Boolean isSuccess = verification.getIsSuccess();
if(isSuccess){
//验证签名正确,开始处理订单的业务逻辑
//1.取出outTradeNo
String outTradeNo= verification.getData().toString();
//2.根据outTradeNo从预下单中取出orderId
ALPrePay prePay = prePayService.getALPrePayByOutTradeNo(outTradeNo);
//订单id
Integer orderId = prePay.getOrderId();
//订单no
String orderNo = prePay.getOrderNo();
//因为回调会依次调用很多次,所有每一次更改订单状态要判断他是否支付过,如果支付过了,就不可以在支付了
Boolean b = orderFeignService.hasPayByOrderId(orderId).getData();
if (!b) { //false,已支付
return R.success().setMsg("支付成功");
}
//支付成功,调用更改订单的状态等数据
//.....业务逻辑
}
return R.success().setMsg("支付成功");
}else{
return verification;
}
}
退款(这个普通公钥方式的退款,字符集一定要用GBK,不能用utf8)
/**
* 支付宝商城退款
* @param orderNo
* @return
*/
@Override
public R aliPayRefund(String orderNo) {
//普通公钥方式构造
AlipayClient alipayClient = new DefaultAlipayClient(getwayUrl,appId,merchantPrivateKey,
"json","GBK",alipayPublicKey,signType);//这的字符集,一定要是GBK,不能是utf-8
AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
ALPrePay prePay = prePayService.getALPrePayByOutTradeNo(orderNo);
JSONObject bizContent = new JSONObject();
// bizContent.put("out_trade_no", orderNo); //商户自定义的单号
bizContent.put("trade_no", prePay.getTradeNo()); //支付宝交易凭证号
bizContent.put("refund_amount",new BigDecimal(prePay.getTotalAmount()).divide(new BigDecimal(100))+"");//金额
bizContent.put("refund_reason", "refundBodyMall"); //退款原因
request.setBizContent(bizContent.toString());
try {
AlipayTradeRefundResponse alipay_response = alipayClient.execute(request);
String alipayRefundStr = alipay_response.getBody();
System.out.println(alipayRefundStr);
prePay.setHasRefund(MallEmums.YES.getCode());//已退款
prePayMapper.updateById(prePay);
return R.success().setMsg("退款成功");
} catch (AlipayApiException e) {
e.printStackTrace();
return R.fail("服务器异常,退款失败");
}
}