目录
前言
代码实现
1、yaml配置
#支付宝配置
alipay:
protocol: https
gatewayHost: openapi.alipay.com
signType: RSA2
app-id: xxxxxxxxxxxx
public-key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
private-key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
#订单支付回调地址
orderPayNotifyUrl: https://xx.xxxxxxx.com/alipay/callback
2、前端唤醒支付宝支付controller
@PostMapping(value = "/alipay")
@ApiOperation("支付宝支付")
@ApiImplicitParam(paramType = Constants.HEADER, dataType = Constants.STRING, name = Constants.AUTHORIZATION, value = "授权token", required = true)
public Result<OrderAlipayPayVO> alipay(@Valid @RequestBody OrderPayDTO params) throws Exception {
OrderAlipayPayVO orderAlipayPayVO = orderPayService.alipayPay(params);
return Result.ok(orderAlipayPayVO);
}
3、前端唤醒支付宝支付controller传参
@Data
@Accessors(chain = true)
@ApiModel("订单支付")
public class OrderPayDTO {
@ApiModelProperty(value = "订单id", required = true)
@NotNull(message = "订单id不能为空")
private Long orderId;
@ApiModelProperty(value = "下单用户终端 1.app 2.微信小程序", required = true)
@NotNull(message = "下单用户终端不能为空")
private Integer terminal;
@ApiModelProperty(value = "是否部分支付", required = true)
@NotNull(message = "是否部分支付不能为空")
private Boolean partPay;
@ApiModelProperty(value = "部分支付金额,部分支付时必传", required = false)
private BigDecimal payPrice;
}
4、前端唤醒支付宝支付controller返回参数
@Data
@ApiModel(value = "订单支付宝支付返回")
public class OrderAlipayPayVO {
@ApiModelProperty(value = "支付流水id")
private Long id;
@ApiModelProperty(value = "支付流水号")
private String payNo;
@ApiModelProperty(value = "支付金额")
private BigDecimal payPrice;
@ApiModelProperty(value = "支付唤醒参数")
private String payData;
}
5、前端唤醒微信支付service具体实现
/**
* 支付宝支付
*
* @param params
*/
@Transactional(rollbackFor = Exception.class)
public OrderAlipayPayVO alipayPay(OrderPayDTO params) throws Exception{
//自己的业务逻辑:创建支付流水
OrderPay orderPay = createOrderPay(params, PayMethodCode.ALIPAY_PAY.getCode());
String payResult = AlipayUtil.createAppOrder("购买", orderPay.getPayNo(), orderPay.getPayPrice(), alipayProperties.getCourseOrderPayNotifyUrl());
OrderAlipayPayVO alipayPayVO = new OrderAlipayPayVO();
alipayPayVO.setId(orderPay.getId());
alipayPayVO.setPayNo(orderPay.getPayNo());
alipayPayVO.setPayPrice(orderPay.getPayPrice());
alipayPayVO.setPayData(payResult);
return alipayPayVO;
}
6、常量配置
/**
* 支付宝配置
*
* @author: cyl
* @date: 2021-04-19 15:47
*/
@Getter
@Setter
@Component
@ConfigurationProperties(prefix = "alipay")
public class AlipayProperties {
private String protocol;
private String gatewayHost;
private String signType;
private String appId;
private String privateKey;
private String publicKey;
private String courseOrderPayNotifyUrl;
}
7、支付宝支付工具类
package com.lw.order.util;
import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.payment.app.Client;
import com.alipay.easysdk.payment.common.models.AlipayTradeRefundResponse;
import com.lw.order.constant.AlipayPayConstants;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.Map;
/**
* 支付宝支付,需初始化AlipayConfig
*
* @author: cyl
* @date: 2021-04-19 21:03
*/
@Component
@RequiredArgsConstructor
@Slf4j
public class AlipayUtil {
/**
* 创建app支付订单
*
* @param subject 订单描述
* @param outTradeNo 订单编号
* @param totalAmount 订单金额,字符串,单位元,精确到小数2位
* @param notifyUrl 回调地址
* @return 支付字符串
* @throws Exception 创建订单异常
*/
public static String createAppOrder(String subject, String outTradeNo, BigDecimal totalAmount, String notifyUrl)
throws Exception {
Client client = Factory.Payment.App().asyncNotify(notifyUrl);
return client.pay(subject, outTradeNo, String.valueOf(totalAmount)).getBody();
}
/**
* 创建PC支付订单
*
* @param subject 订单描述
* @param outTradeNo 订单编号
* @param totalAmount 订单金额,字符串,单位元,精确到小数2位
* @param notifyUrl 回调地址
* @return 支付字符串
* @throws Exception 创建订单异常
*/
public static String createPageOrder(String subject, String outTradeNo, double totalAmount, String notifyUrl)
throws Exception {
return Factory.Payment.Page().asyncNotify(notifyUrl).pay(subject, outTradeNo, String.valueOf(totalAmount), "").getBody();
}
/**
* 创建PC二维码支付订单
*
* @param subject 订单描述
* @param outTradeNo 订单编号
* @param totalAmount 订单金额,字符串,单位元,精确到小数2位
* @param notifyUrl 回调地址
* @return 支付二维码链接
* @throws Exception 创建订单异常
*/
public static String createPageCodeOrder(String subject, String outTradeNo, double totalAmount, String notifyUrl)
throws Exception {
return Factory.Payment.FaceToFace().asyncNotify(notifyUrl).preCreate(subject, outTradeNo, String.valueOf(totalAmount)).getQrCode();
}
/**
* 创建手机浏览器支付订单
*
* @param subject 订单描述
* @param outTradeNo 订单编号
* @param totalAmount 订单金额,字符串,单位元,精确到小数2位
* @param notifyUrl 回调地址
* @return 支付字符串
* @throws Exception 创建订单异常
*/
public static String createWapOrder(String subject, String outTradeNo, double totalAmount, String notifyUrl)
throws Exception {
return Factory.Payment.Wap().asyncNotify(notifyUrl).pay(subject, outTradeNo, String.valueOf(totalAmount), "", "").getBody();
}
/**
* 订单申请退款
*
* @param outTradeNo 订单编号
* @param refundAmount 退款金额,字符串,单位元,精确到小数2位
* @throws Exception 退款异常
*/
public static void refund(String outTradeNo, double refundAmount, String appId, String privateKey, String publicKey) throws Exception {
try {
AlipayTradeRefundResponse refund = Factory.Payment.Common().optional("appId", appId).optional("merchantPrivateKey", privateKey).optional("alipayPublicKey", publicKey)
.refund(outTradeNo, String.valueOf(refundAmount));
if (!AlipayPayConstants.REFUND_CODE.equals(refund.getCode())) {
throw new IllegalArgumentException(refund.getSubMsg());
}
} catch (IllegalArgumentException e) {
throw e;
} catch (Exception e) {
log.error("支付宝退款异常", e);
throw e;
}
}
/**
* 查询订单支付状态
*
* @param outTradeNo 订单号
* @throws Exception 查询异常
*/
public static boolean tradeQuery(String outTradeNo) throws Exception {
String status = Factory.Payment.Common().query(outTradeNo).getTradeStatus();
return AlipayPayConstants.TRADE_SUCCESS.equals(status) || AlipayPayConstants.TRADE_FINISHED.equals(status);
}
/**
* 异步通知校验
*
* @param parameters 响应参数
* @throws Exception 校验异常
*/
public static boolean verifyNotify(Map<String, String> parameters) throws Exception {
return Factory.Payment.Common().verifyNotify(parameters);
}
}
8、支付宝支付回调
@PostMapping(value = "/alipay/callback")
@ApiOperation(value = "支付宝支付回调", hidden = true)
public String alipayCallback(HttpServletRequest request) {
RLock lock = null;
try {
Map<String, String> params = requestParam(request);
boolean signVerified = AlipayUtil.verifyNotify(params);
if (!signVerified) {
log.error("支付宝签名验证不通过");
return AlipayPayConstants.FAILURE;
}
log.info("支付宝异步通知参数:{}", params.toString());
String tradeNo = params.get("out_trade_no");
//使用redisson获取分布式锁
lock = redissonClient.getLock(DLockConstants.ALIPAY_OUT_TRADE_NO + tradeNo);
//尝试加锁,设置等待时间10秒,锁超时时间10秒
boolean locked = lock.tryLock(10, 10, TimeUnit.SECONDS);
if (locked) {
OrderPay orderPay = orderPayService.findByPayNo(tradeNo);
if (null == orderPay || orderPay.getStatus() != PayStatusCode.WAIT_PAY.getCode()) {
log.error("订单不存在或已支付:{}", tradeNo);
return AlipayPayConstants.FAILURE;
}
String tradeStatus = params.get("trade_status");
if (!AlipayPayConstants.TRADE_SUCCESS.equals(tradeStatus) && !AlipayPayConstants.TRADE_FINISHED.equals(tradeStatus)) {
log.error("订单支付回调状态不通过:{}", tradeNo);
return AlipayPayConstants.FAILURE;
}
//重新调用支付宝接口查询订单状态
if (AlipayUtil.tradeQuery(tradeNo)) {
orderPayService.paySuccess(orderPay, PayMethodCode.ALIPAY_PAY.getCode(), params.get("trade_no"));
return AlipayPayConstants.SUCCESS;
}
log.error("支付宝支付回调通知二次校验不通过:{}", tradeNo);
return AlipayPayConstants.FAILURE;
}
log.error("支付宝支付回调通知排队中:{}", tradeNo);
return AlipayPayConstants.FAILURE;
} catch (Exception e) {
log.error("支付宝支付回调处理异常", e);
return AlipayPayConstants.FAILURE;
} finally {
if (null != lock && lock.isLocked() && lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
9、解析回调参数
/**
* 解析回调参数
*
* @param request
* @return
*/
private static Map<String, String> requestParam(HttpServletRequest request) {
Map<String, String> params = Maps.newHashMap();
Map<String, String[]> requestParams = request.getParameterMap();
for (Map.Entry<String, String[]> entry : requestParams.entrySet()) {
String valueStr = String.join(",", entry.getValue());
params.put(entry.getKey(), valueStr);
}
return params;
}