继上一篇实现java对接支付宝,本篇记录一下java实现微信、小程序的支付与退款实现过程,话不多说,直接上代码
pom依赖:
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.0.3</version>
</dependency>
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.11</version>
</dependency>
支付控制层:
/**
* 支付接口
*
*/
@PostMapping("/v1/order/pay")
public Result appPay(@RequestBody @Validated AlipayReq param) {
try {
return alipayService.createAppPay(param);
} catch (Exception e) {
log.error("支付接口请求失败:{}",e);
return Result.error(e.getMessage());
}
}
支付业务层 ,微信支付核心代码在createAppPay方法中
@Service
@Slf4j
@RequiredArgsConstructor
public class PayServiceImpl implements PayService {
private final OrderDataClient orderDataClient;
private final AliPayClientConfig aliPayClientConfig;
private final AlipayClient alipayClient;
private final PayBackRecordService payBackRecordService;
private final PayApplyRecordService payApplyRecordService;
private final PayRefundRecordService payRefundRecordService;
private final RedissonClient redissonClient;
private final WxPayV3Bean wxPayV3Bean;
private final UserInfoClient userInfoClient;
private final RSAAutoCertificateConfig config;
private final PayCommonService payCommonService;
private void checkPay(String orderNo) {
//校验该笔订单是否支付成功过
PayBackRecord build = PayBackRecord.builder().orderNo(orderNo).deleted(Boolean.FALSE).build();
long count = payBackRecordService.count(new QueryWrapper<>(build));
if (count > long0) {
throw new BizException("该订单已支付,请勿重复支付");
}
}
@Override
public Result createAppPay(AlipayReq param) {
log.info("支付请求参数入参:{}", JSONUtil.toJsonStr(param));
//同一时刻用户对同一个订单只能支付一次
String lockKey = RedisKeyContant.LOCK_APP_PAY_ORDER + param.getOrderNo();
RLock rLock = redissonClient.getLock(lockKey);
boolean lockResult = rLock.tryLock();
try {
if (lockResult) {
checkPay(param.getOrderNo());
PayOrderInfo order = this.getOrderInfo(param.getOrderNo());
if(order.getPrice().compareTo(BigDecimal.ZERO)<=0){
throw new BusinessException("支付金额必须大于0");
}
if (Objects.equals(PayWayEnum.PAY_ALIPAY.getCode(), param.getPayWay())) {
//支付宝支付
Result result = this.doAlipayApp(order.getOrderNo(), PayOrderTypeEnum.getByType(order.getPayOrderType()).getMsg(), order.getPrice());
//记录支付申请表
this.toSavePayApplyRecord(order, result, PayWayEnum.PAY_ALIPAY.getPayWay());
log.info("订单编号:{},支付宝响应结果:{}", param.getOrderNo(), JSONUtil.toJsonStr(result));
return result;
}
if (Objects.equals(PayWayEnum.PAY_WECHAT_APPLET.getCode(), param.getPayWay())) {
//微信小程序支付
Result<UserInfoVO> userResult = userInfoClient.getUserById(AuthUserUtil.getCurrentUserId());
if (!userResult.isSuccess() || Objects.isNull(userResult.getObject())) {
throw new BizException("查询用户信息接口异常");
}
UserInfoVO userInfoVo = userResult.getObject();
Result<PrepayWithRequestPaymentResponse> wxPayRespVOResult = this.doWechatAppletPay(order.getOrderNo(), PayOrderTypeEnum.getByType(order.getPayOrderType()).getMsg(), order.getPrice(), userInfoVo.getMiniOpenId());
//记录支付申请表
this.toSavePayApplyRecord(order, wxPayRespVOResult,PayWayEnum.PAY_WECHAT_APPLET.getPayWay());
return wxPayRespVOResult;
}
if (Objects.equals(PayWayEnum.PAY_WECHAT.getCode(), param.getPayWay())) {
//微信支付
Result<WxPayRespVO> payResult = this.doWechatAppPay(order.getOrderNo(), PayOrderTypeEnum.getByType(order.getPayOrderType()).getMsg(), order.getPrice());
//记录支付申请表
this.toSavePayApplyRecord(order, payResult, PayWayEnum.PAY_WECHAT.getPayWay());
return payResult;
}
}
return Result.fail("支付中,请勿重复点击");
} catch (Exception e) {
log.error("支付请求失败:{}",e);
return Result.fail(e.getMessage());
} finally {
if (lockResult) {
rLock.unlock();
}
}
}
/**
* 微信支付
*
* @Param: [java.lang.String, java.lang.String, java.math.BigDecimal]
* @return: cn.aimex.cloud.framework.core.domain.Result<cn.gpets.cloud.payment.server.domain.vo.WxPayRespVO>
* @Author: FJT
* @Date: 2023/11/9
*/
private Result<WxPayRespVO> doWechatAppPay(String orderNo, String msg, BigDecimal price) {
try {
// 构建service
AppService service = new AppService.Builder().config(config).build();
com.wechat.pay.java.service.payments.app.model.PrepayRequest request = new com.wechat.pay.java.service.payments.app.model.PrepayRequest();
com.wechat.pay.java.service.payments.app.model.Amount amount = new com.wechat.pay.java.service.payments.app.model.Amount();
amount.setTotal(NumberUtil.mul(price, 100).intValue());
request.setAmount(amount);
request.setAppid(wxPayV3Bean.getAppId());
request.setMchid(wxPayV3Bean.getMchId());
request.setDescription(msg);
request.setNotifyUrl(wxPayV3Bean.getNotifyUrl());
request.setOutTradeNo(orderNo);
// 调用下单方法,得到应答
// 调用微信sdk接口,生成预支付交易单
com.wechat.pay.java.service.payments.app.model.PrepayResponse response = service.prepay(request);
WxPayRespVO vo = new WxPayRespVO();
Signer signer = config.createSigner();
long timestamp = Instant.now().getEpochSecond();
vo.setTimeStamp(timestamp);
String nonceStr = NonceUtil.createNonce(32);
vo.setNonceStr(nonceStr);
String message =
request.getAppid() + "\n" + timestamp + "\n" + nonceStr + "\n" + response.getPrepayId() + "\n";
String sign = signer.sign(message).getSign();
vo.setPaySign(sign);
vo.setPrepayId(response.getPrepayId());
vo.setAppId(wxPayV3Bean.getAppId());
vo.setPartnerId(wxPayV3Bean.getMchId());
log.info("微信支付请求返回:{}",JSONUtil.toJsonStr(vo));
return Result.success(vo);
} catch (ServiceException e) {
log.error("微信支付请求异常:{}",e);
return Result.fail(JSONUtil.toJsonStr(e.getResponseBody()));
} catch (Exception e) {
log.error("微信支付请求异常:{}",e);
return Result.fail(JSONUtil.toJsonStr(e.getMessage()));
}
}
/**
* 微信小程序支付
*
* @param orderNo
* @param msg
* @param price
* @Param: []
* @return: void
* @Author: FJT
* @Date: 2023/11/8
*/
private Result<PrepayWithRequestPaymentResponse> doWechatAppletPay(String orderNo, String msg, BigDecimal price, String openId) {
try {
// 构建service
JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(config).build();
// request.setXxx(val)设置所需参数,具体参数可见Request定义
PrepayRequest request = new PrepayRequest();
Amount amount = new Amount();
amount.setTotal(NumberUtil.mul(price, 100).intValue());
request.setAmount(amount);
request.setAppid(wxPayV3Bean.getAppletAppId());
request.setMchid(wxPayV3Bean.getMchId());
request.setDescription(msg);
request.setNotifyUrl(wxPayV3Bean.getNotifyUrl());
request.setOutTradeNo(orderNo);
Payer payer = new Payer();
payer.setOpenid(openId);
request.setPayer(payer);
// 调用微信sdk接口,生成预支付交易单
PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(request);
// Signer signer = config.createSigner();
// long timestamp = Instant.now().getEpochSecond();
// vo.setTimeStamp(timestamp);
// String nonceStr = NonceUtil.createNonce(32);
// vo.setNonceStr(nonceStr);
// String message =
// request.getAppid() + "\n" + timestamp + "\n" + nonceStr + "\n" + response.getPrepayId() + "\n";
// String sign = signer.sign(message).getSign();
// vo.setPaySign(sign);
// vo.setPrepayId(response.getPrepayId());
// vo.setAppId(wxPayV3Bean.getAppletAppId());
// vo.setPartnerId(wxPayV3Bean.getMchId());
log.info("微信支付请求返回:{}",JSONUtil.toJsonStr(response));
return Result.success(response);
} catch (ServiceException e) {
log.info("微信支付请求返回异常:{}",e);
return Result.fail(JSONUtil.toJsonStr(e.getResponseBody()));
} catch (Exception e) {
log.info("微信支付请求返回异常:{}",e);
return Result.fail(JSONUtil.toJsonStr(e.getMessage()));
}
}
private void toSavePayApplyRecord(PayOrderInfo order, Result result, String payWay) {
PayApplyRecord payApplyRecord = new PayApplyRecord();
payApplyRecord.setApplyResult(JSONUtil.toJsonStr(result));
payApplyRecord.setBusinessNo(order.getOrderNo());
payApplyRecord.setPayChannel(order.getPayOrderType());
payApplyRecord.setPayPrice(order.getPrice());
payApplyRecord.setPayMode(payWay);
payApplyRecord.setCreateDateTime(LocalDateTime.now());
payApplyRecord.setCreateUser(AuthUserUtil.getCurrentUserId());
payApplyRecordService.save(payApplyRecord);
}
@Override
public PayOrderInfo getOrderInfo(String orderNo) {
Result<PayOrderInfo> orderResult = orderDataClient.getOrderInfoByOrderNo(orderNo);
if (!orderResult.isSuccess()) {
throw new BizException("查询订单信息异常");
}
PayOrderInfo order = orderResult.getObject();
if (Objects.isNull(order)) {
throw new BizException("订单信息不存在");
}
return order;
}
@Override
public Result getOrderPayState(AlipayReq param) {
if (Objects.equals(PayWayEnum.PAY_ALIPAY.getCode(), param.getPayWay())) {
//查询支付宝支付状态
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
AlipayTradeQueryModel model = new AlipayTradeQueryModel();
model.setOutTradeNo(param.getOrderNo());
request.setBizModel(model);
try {
AlipayTradeQueryResponse response = alipayClient.execute(request);
log.info("订单号:{},支付订单状态结果:{}", JSONUtil.toJsonStr(param.getOrderNo()), JSONUtil.toJsonStr(response));
if (response.getTradeStatus().equals(AliPayUtil.TRADE_SUCCESS)) {
return Result.success(JSONUtil.parseObj(response.getBody()));
}
return Result.fail("订单支付失败");
} catch (AlipayApiException e) {
log.error("获取支付订单支付状态失败:{}", e);
return Result.fail(e.getErrMsg());
}
}
if (Objects.equals(PayWayEnum.PAY_WECHAT.getCode(), param.getPayWay())
|| Objects.equals(PayWayEnum.PAY_WECHAT_APPLET.getCode(), param.getPayWay())) {
AppService service = new AppService.Builder().config(config).build();
QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
request.setOutTradeNo(param.getOrderNo());
request.setMchid(wxPayV3Bean.getMchId());
Transaction transaction = null;
try {
transaction = service.queryOrderByOutTradeNo(request);
log.info("微信订单支付状态响应结果:{}", JSONUtil.toJsonStr(transaction));
if (transaction.getTradeState().equals(Transaction.TradeStateEnum.SUCCESS)) {
return Result.success(JSONUtil.parse(transaction));
}
} catch (ServiceException e) {
log.error("获取支付订单支付状态异常:{}", e);
return Result.fail(e.getErrorMessage());
}
return Result.fail("订单支付失败");
}
return Result.fail();
}
/**
* 订单退款
*
* @Param: [cn.gpets.cloud.payment.clent.domain.req.RefundOrderReq]
* @return: cn.aimex.cloud.framework.core.domain.Result
* @Author: FJT
* @Date: 2023/11/7
*/
@Override
public Result refundOrder(RefundOrderReq req) {
log.info("订单退款入参:{}", JSONUtil.toJsonStr(req));
String refundNo = req.getRefundNo();
//查询是否成功退款过
PayRefundRecord payRefundRecordBuild = PayRefundRecord.builder().refundNo(refundNo).deleted(Boolean.FALSE).build();
PayRefundRecord payRefundRecord = payRefundRecordService.getOne(new QueryWrapper<>(payRefundRecordBuild));
if (Objects.isNull(payRefundRecord)) {
log.error("用户没有退款记录,无法退款,退款单号:{}",req.getRefundNo());
throw new BizException("用户没有退款记录,无法退款");
}
if (payRefundRecord.getRefundSuccess()) {
log.error("该商品已退款,请勿重复退款,退款单号:{}",req.getRefundNo());
throw new BizException("该商品已退款,请勿重复退款");
}
PayOrderInfo orderInfo = this.getOrderInfo(payRefundRecord.getRefundBusinessNo());
Result<OrderProductVo> result = orderDataClient.getOrderProductInfo(payRefundRecord.getOrderProductId(), orderInfo.getPayOrderType());
if (!result.isSuccess()) {
throw new BizException("查询商品行信息接口异常");
}
OrderProductVo productVo = result.getObject();
//查询支付申请记录,查看该笔是否成功支付过
PayBackRecord payBackRecord = payBackRecordService.getOne(new QueryWrapper<>(PayBackRecord.builder().orderNo(payRefundRecord.getRefundBusinessNo()).deleted(Boolean.FALSE).build()));
if (Objects.isNull(payBackRecord)) {
log.error("订单未支付过,无法退款,退款单号:{}",req.getRefundNo());
throw new BizException("订单未支付过,无法退款");
}
//同一时刻对同一个商品只能退一次
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append(RedisKeyContant.LOCK_REFUND_ORDER).append(refundNo);
String lockKey = stringBuffer.toString();
RLock rLock = redissonClient.getLock(lockKey);
boolean lockResult = rLock.tryLock();
try {
if (lockResult) {
payRefundRecordBuild.setRefundNo(refundNo);
BigDecimal price = productVo.getPrice();
if (Objects.nonNull(req.getRefundPrice()) && price.compareTo(req.getRefundPrice()) >= 0) {
//如果传过来的退款金额不为空,并且小于原商品行的支付金额,则使用传递的退款金额
price = req.getRefundPrice();
}
if (Objects.equals(PayWayEnum.PAY_ALIPAY.getPayWay(), payBackRecord.getPayWay())) {
//原使用支付宝进行支付的,使用支付宝进行退款
AlipayTradeRefundResponse refundResult = this.doAlipayRefund(payRefundRecord.getRefundBusinessNo(), price, payRefundRecord.getRefundReason(),refundNo);
log.info("退款单号:{},支付宝退款响应结果:{}", refundNo, JSONUtil.toJsonStr(refundResult));
if (refundResult.isSuccess()&&refundResult.getFundChange().equals(AliPayUtil.CHANGE_STATE_YES)) {
//更新退款申请表的退款状态
payRefundRecord.setRefundSuccess(Boolean.TRUE);
payRefundRecord.setUpdateDateTime(LocalDateTime.now());
payRefundRecord.setTradeNo(refundResult.getTradeNo());
payRefundRecord.setRefundDetail(JSONUtil.toJsonStr(refundResult));
payRefundRecordService.updateById(payRefundRecord);
//更新订单退款状态
UpdateOrderRefundReq updateOrderRefundReq = new UpdateOrderRefundReq();
updateOrderRefundReq.setOrderNo(orderInfo.getOrderNo());
updateOrderRefundReq.setOrderProductId(payRefundRecord.getOrderProductId());
updateOrderRefundReq.setOrderProductState(payRefundRecord.getOrderProductState());
updateOrderRefundReq.setPayOrderType(orderInfo.getPayOrderType());
if(Objects.equals(PayOrderTypeEnum.PET_ALL_PAY.getType(),orderInfo.getPayOrderType())){
updateOrderRefundReq.setRefundOrderState(PetOrderStateEnum.STATE_7.getTagId());
}
StringBuffer stringBuffer1 = new StringBuffer();
updateOrderRefundReq.setRefundMsg(stringBuffer1.append(req.getRefundMsg()).append(":").append(OrderRemarkEnum.BUYER_HAD_REFUND.getMsg()).toString());
orderDataClient.updateOrderRefundState(updateOrderRefundReq);
//发送钱包通知消息
payCommonService.doSendRefundMag(MessageContentEnum.REFUND_TO_ACCOUNT,payRefundRecord.getRefundNo(),price,orderInfo.getUserId(),payRefundRecord.getRefundReason());
//给python端发送退款到账消息
payCommonService.doSendRefundResult(refundNo,Boolean.TRUE,null);
return Result.success();
}
return Result.fail(refundResult.getSubMsg());
}
if (Objects.equals(PayWayEnum.PAY_WECHAT.getPayWay(), payBackRecord.getPayWay())
|| Objects.equals(PayWayEnum.PAY_WECHAT_APPLET.getPayWay(), payBackRecord.getPayWay())) {
//使用微信退款
Refund refund = this.doWechatRefund(price, refundNo, payBackRecord.getPayPrice(),payBackRecord.getOrderNo(),req.getRefundMsg());
log.info("退款单号:{},微信退款响应结果:{}", refundNo, JSONUtil.toJsonStr(refund));
if (refund.getStatus().equals(Status.SUCCESS)||refund.getStatus().equals(Status.PROCESSING)) {
//更新订单退款状态
UpdateOrderRefundReq updateOrderRefundReq = new UpdateOrderRefundReq();
updateOrderRefundReq.setOrderNo(orderInfo.getOrderNo());
updateOrderRefundReq.setOrderProductId(payRefundRecord.getOrderProductId());
updateOrderRefundReq.setOrderProductState(payRefundRecord.getOrderProductState());
updateOrderRefundReq.setPayOrderType(orderInfo.getPayOrderType());
if(Objects.equals(PayOrderTypeEnum.PET_ALL_PAY.getType(),orderInfo.getPayOrderType())){
updateOrderRefundReq.setRefundOrderState(PetOrderStateEnum.STATE_6.getTagId());
}
updateOrderRefundReq.setRefundMsg(OrderRemarkEnum.BUYER_APPLY_REFUND.getMsg());
orderDataClient.updateOrderRefundState(updateOrderRefundReq);
//发送钱包通知消息
payCommonService.doSendRefundMag(MessageContentEnum.RETURN_HAD_ACCEPTANCE,payRefundRecord.getRefundNo(),price,orderInfo.getUserId(),payRefundRecord.getRefundReason());
return Result.success();
}
}
} else {
return Result.fail("请勿重复退款");
}
} catch (BizException e) {
log.info("退款失败:{}", e.getMessage());
return Result.fail(e.getMessage());
} finally {
if (lockResult) {
rLock.unlock();
}
}
return Result.fail();
}
/**
* 微信或微信小程序退款
*
* @Param: [java.lang.String, java.math.BigDecimal, java.lang.String]
* @return: void
* @Author: FJT
* @Date: 2023/11/9
*/
private Refund doWechatRefund(BigDecimal price, String refundNo, BigDecimal payPrice, String orderNo, String refundMsg) {
Refund refund = null;
try {
// 构建退款service
RefundService service = new RefundService.Builder().config(config).build();
// request.setXxx(val)设置所需参数,具体参数可见Request定义
//构建退款请求
CreateRequest request = new CreateRequest();
//构建订单金额信息
AmountReq amountReq = new AmountReq();
//退款金额
amountReq.setRefund(NumberUtil.mul(price, 100).longValue());
//原订单金额
amountReq.setTotal(NumberUtil.mul(payPrice, 100).longValue());
//货币类型(默认人民币)
amountReq.setCurrency("CNY");
request.setAmount(amountReq);
request.setOutTradeNo(orderNo);
request.setReason(refundMsg);
//商户退款单号
request.setOutRefundNo(refundNo);
//退款通知回调地址
request.setNotifyUrl(wxPayV3Bean.getRefundNotifyUrl());
// 调用退款方法,得到应答
// 调用微信sdk接口
refund = service.create(request);
//接收退款返回参数
return refund;
} catch (ServiceException e) {
log.error("退款异常:{}", e);
throw new BizException(e.getErrorMessage());
} catch (Exception e) {
log.error("退款异常:{}", e);
throw new BizException(e.getMessage());
}
}
/**
* 支付宝退款
*
* @Param: [java.lang.String, java.math.BigDecimal, java.lang.String]
* @return: void
* @Author: FJT
* @Date: 2023/11/7
*/
private AlipayTradeRefundResponse doAlipayRefund(String refundBusinessNo, BigDecimal sumPrice, String refundReason, String refundNo) {
AlipayTradeRefundResponse response = null;
try {
AlipayTradeRefundRequest alipayTradeCloseRequest = new AlipayTradeRefundRequest();
//请求参数集合对象,除了公共参数之外,所有参数都可通过此对象传递
AlipayTradeRefundModel alipayTradeRefundModel = new AlipayTradeRefundModel();
//退款的订单号,传入生成支付订单时的订单号即可
alipayTradeRefundModel.setOutTradeNo(refundBusinessNo);
//退款金额
alipayTradeRefundModel.setRefundAmount(sumPrice.toString());
//退款的原因
alipayTradeRefundModel.setRefundReason(refundReason);
alipayTradeRefundModel.setOutRequestNo(refundNo);
alipayTradeCloseRequest.setBizModel(alipayTradeRefundModel);
//退款的执行流程与支付不太一样,支付时成功之后,需要通知回调接口,而退款则不需要,只需判断响应 参数 refundResponse.getFundChange().equals("Y") 判断是否发生了资金变化, equals("Y")表示资金发生了变化,退款成功
response = alipayClient.execute(alipayTradeCloseRequest);
return response;
} catch (AlipayApiException e) {
log.error("支付宝退款失败:{}", e);
return response;
}
}
/**
* 支付宝app支付
*
* @Param: [java.lang.String, java.lang.String, java.math.BigDecimal]
* @return: cn.aimex.cloud.framework.core.domain.Result
* @Author: FJT
* @Date: 2023/11/7
*/
private Result doAlipayApp(String orderNumber, String msg, BigDecimal price) {
//实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
//SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setSubject(msg);
model.setOutTradeNo(orderNumber);
model.setTimeoutExpress(AliPayUtil.TIME_OUT_EXPRESS);
model.setTotalAmount(price.toString());
request.setBizModel(model);
//异步通知地址
request.setNotifyUrl(aliPayClientConfig.getNotifyUrl());
try {
//这里和普通的接口调用不同,使用的是sdkExecute
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
if (!response.isSuccess()) {
log.error("接口调用错误:{}", response.getBody());
return Result.fail(response.getBody());
}
//就是orderString 可以直接给客户端请求,无需再做处理。
log.info("支付请求返回:{}",response.getBody());
return Result.success().setData(Collections.singletonList(response.getBody()));
} catch (AlipayApiException e) {
log.error("生成支付订单错误:", e);
return Result.fail(e.getErrMsg());
}
}
}
微信回调类
/**
* 微信支付回调
*
*/
@PostMapping("/v1/pay/callBack/result")
public Result<Boolean> wechatPaycallBack(HttpServletRequest request) {
try {
return payCallBackService.wechatPayCallBack(request);
} catch (Exception e) {
return Result.error(e.getMessage());
}
}
/**
* 微信退款回调
*
*/
@PostMapping("/v1/refund/callBack/result")
public Result<Boolean> wechatRefundCallBack(HttpServletRequest request) {
try {
return payCallBackService.wechatRefundCallBack(request);
} catch (Exception e) {
return Result.error(e.getMessage());
}
}
回调业务处理类
/**
* 微信支付回调
*
* @Param: [java.util.Map<java.lang.String, java.lang.String>]
* @return: cn.aimex.cloud.framework.core.domain.Result<java.lang.Boolean>
* @Author: FJT
* @Date: 2023/11/9
*/
@Override
public Result<Boolean> wechatPayCallBack(HttpServletRequest request) {
try {
//签名校验并封装微信requestParam
RequestParam requestParam = this.getRequestParamAndCheckSign(request);
NotificationParser parser = new NotificationParser(config);
Transaction parse = parser.parse(requestParam, Transaction.class);
String orderNo = parse.getOutTradeNo();
log.info("微信支付回调结果,支付单号:{},:{}",JSONUtil.toJsonStr(parse), orderNo);
PayOrderInfo orderInfo = payService.getOrderInfo(orderNo);
this.checkWxPayParam(orderNo, parse.getMchid(), parse.getAmount(), parse.getTradeState(), orderInfo.getPrice());
String payWay = PayWayEnum.PAY_WECHAT.getPayWay();
if (parse.getTradeType().equals(Transaction.TradeTypeEnum.JSAPI)) {
payWay = PayWayEnum.PAY_WECHAT_APPLET.getPayWay();
}
//保存支付回调记录
String payApplyNo = parse.getTransactionId();
this.toSavePayBackRecord(orderNo, payApplyNo, orderInfo.getPayOrderType(), payWay, orderInfo.getPrice(), JSONUtil.toJsonStr(parse));
//更新支付状态
UpdateOrderPayStateReq req = new UpdateOrderPayStateReq();
req.setOrderNo(orderNo);
req.setPayOrderType(orderInfo.getPayOrderType());
req.setPayApplyNo(payApplyNo);
req.setPayWay(payWay);
orderDataClient.updateOrderPayState(req);
//发送钱包通知消息
payCommonService.doSendPayMag(MessageContentEnum.PAY_SUCCESS,payApplyNo,orderInfo.getPrice(),orderInfo.getUserId());
return Result.success();
} catch (Exception e) {
log.error("处理支付回调失败:{}",e);
throw new BizException("处理支付回调失败");
}
}
/**
* 微信退款回调
*
* @Param: [javax.servlet.http.HttpServletRequest]
* @return: cn.aimex.cloud.framework.core.domain.Result<java.lang.Boolean>
* @Author: FJT
* @Date: 2023/11/9
*/
@Override
public Result<Boolean> wechatRefundCallBack(HttpServletRequest request) {
RequestParam requestParam = this.getRequestParamAndCheckSign(request);
NotificationParser parser = new NotificationParser(config);
RefundNotification parse = parser.parse(requestParam, RefundNotification.class);
PayOrderInfo orderInfo = payService.getOrderInfo(parse.getOutTradeNo());
PayRefundRecord payRefundRecord = null;
try {
payRefundRecord = this.checkWxRefundParam(parse.getOutTradeNo(), parse.getAmount(), parse.getRefundStatus(), orderInfo.getPrice(), parse.getOutRefundNo());
} catch (Exception e) {
//给python端发送退款失败消息
payCommonService.doSendRefundResult(payRefundRecord.getRefundNo(),Boolean.FALSE,e.getMessage());
return Result.fail(e.getMessage());
}
Result<OrderProductVo> result = orderDataClient.getOrderProductInfo(payRefundRecord.getOrderProductId(), orderInfo.getPayOrderType());
if (!result.isSuccess()) {
throw new BizException("查询商品行信息接口异常");
}
OrderProductVo productVo = result.getObject();
//更新退款申请为退款成功
payRefundRecord.setRefundSuccess(Boolean.TRUE);
payRefundRecord.setUpdateDateTime(LocalDateTime.now());
payRefundRecord.setRefundDetail(JSONUtil.toJsonStr(parse));
payRefundRecord.setTradeNo(parse.getRefundId());
payRefundRecordService.updateById(payRefundRecord);
//更新订单退款状态
UpdateOrderRefundReq req=new UpdateOrderRefundReq();
req.setOrderNo(orderInfo.getOrderNo());
req.setOrderProductId(payRefundRecord.getOrderProductId());
req.setOrderProductState(payRefundRecord.getOrderProductState());
req.setPayOrderType(orderInfo.getPayOrderType());
if(Objects.equals(PayOrderTypeEnum.PET_ALL_PAY.getType(),orderInfo.getPayOrderType())){
req.setRefundOrderState(PetOrderStateEnum.STATE_7.getTagId());
}
StringBuffer stringBuffer=new StringBuffer();
req.setRefundMsg(stringBuffer.append(payRefundRecord.getRefundReason()).append(":").append(OrderRemarkEnum.BUYER_HAD_REFUND.getMsg()).toString());
orderDataClient.updateOrderRefundState(req);
//发送钱包通知消息
payCommonService.doSendRefundMag(MessageContentEnum.REFUND_TO_ACCOUNT,payRefundRecord.getRefundNo(),productVo.getPrice(),orderInfo.getUserId(),payRefundRecord.getRefundReason());
//给python端发送退款到账消息
payCommonService.doSendRefundResult(payRefundRecord.getRefundNo(),Boolean.TRUE,null);
return Result.success();
}
/**
* 检查微信退款回调参数
* @Param: [java.lang.String, com.wechat.pay.java.service.refund.model.Amount, com.wechat.pay.java.service.refund.model.Status, java.math.BigDecimal]
* @return: void
* @Author: FJT
* @Date: 2023/11/9
*/
private PayRefundRecord checkWxRefundParam(String orderNo, Amount amount, Status refundStatus, BigDecimal price, String outRefundNo) {
//校验交易状态
if (!Status.SUCCESS.equals(refundStatus)) {
log.error("orderNo:{},微信退款回调未成功", orderNo);
throw new BizException("微信退款回调未成功");
}
Long totalAmount = amount.getTotal();
if (!totalAmount.equals(NumberUtil.mul(price, 100).longValue())) {
log.error("orderNo:{},金额校验失败", orderNo);
throw new BizException("金额校验失败");
}
//校验退款单号
PayRefundRecord refundRecord = payRefundRecordService.getOne(new QueryWrapper<>(PayRefundRecord.builder().refundNo(outRefundNo).deleted(Boolean.FALSE).build()));
if(Objects.isNull(refundRecord)){
throw new BizException("用户退款记录不存在");
}
//校验是否已经退款成功过了
if(refundRecord.getRefundSuccess()){
throw new BizException("用户已成功退款了");
}
return refundRecord;
}
/**
* //签名校验并封装微信requestParam
*
* @Param: [javax.servlet.http.HttpServletRequest]
* @return: com.wechat.pay.java.core.notification.RequestParam
* @Author: FJT
* @Date: 2023/11/9
*/
private RequestParam getRequestParamAndCheckSign(HttpServletRequest request) {
try {
//读取请求体的信息
ServletInputStream inputStream = request.getInputStream();
StringBuffer stringBuffer = new StringBuffer();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String s;
//读取回调请求体
while ((s = bufferedReader.readLine()) != null) {
stringBuffer.append(s);
}
String s1 = stringBuffer.toString();
String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);
String nonce = request.getHeader(WECHAT_PAY_NONCE);
String signType = request.getHeader("Wechatpay-Signature-Type");
String serialNo = request.getHeader(WECHAT_PAY_SERIAL);
String signature = request.getHeader(WECHAT_PAY_SIGNATURE);
RequestParam requestParam = new RequestParam.Builder()
.serialNumber(serialNo)
.nonce(nonce)
.signature(signature)
.timestamp(timestamp)
// 若未设置signType,默认值为 WECHATPAY2-SHA256-RSA2048
.signType(signType)
.body(s1)
.build();
return requestParam;
} catch (Exception e) {
log.error("微信回调数据异常:{}", e);
throw new BizException(e.getMessage());
}
}
private void checkWxPayParam(String orderNo, String mchid, TransactionAmount amount, Transaction.TradeStateEnum tradeState,BigDecimal orderPrice) {
Integer totalAmount = amount.getTotal();
if (!totalAmount.equals(NumberUtil.mul(orderPrice, 100).intValue())) {
log.error("orderNo:{},金额校验失败", orderNo);
throw new BizException("金额校验失败");
}
//3.验证 商户号 是否为该商家本身
if (!wxPayV3Bean.getMchId().equals(mchid)) {
log.error("orderNo:{},应用mchid校验失败", orderNo);
throw new BizException("应用APPID校验失败");
}
//校验交易状态
if (!Transaction.TradeStateEnum.SUCCESS.equals(tradeState)) {
log.error("orderNo:{},微信支付回调未成功", orderNo);
throw new BizException("微信支付回调未成功");
}
}
/**
* 保存支付回调记录
*
* @Param: [java.lang.String, java.lang.String, java.lang.Integer, java.lang.String]
* @return: void
* @Author: FJT
* @Date: 2023/11/7
*/
private void toSavePayBackRecord(String orderNo, String payApplyNo, Integer payOrderType, String payWay, BigDecimal price, String result) {
PayBackRecord build = PayBackRecord.builder().payApplyNo(payApplyNo).orderNo(orderNo).orderPayType(payOrderType)
.payPrice(price).payWay(payWay)
.createDateTime(LocalDateTime.now())
.payResult(result)
.deleted(Boolean.FALSE).build();
payBackRecordService.save(build);
}
工具和对象类
@Component
@Configuration
@ConfigurationProperties(prefix = "gpets-cloud.wechat")
@Data
public class WxPayV3Bean {
/**
* 微信appId
*/
private String appId;
/**
* 微信小程序appId
*/
private String appletAppId;
/**
* 证书序列号
*/
private String mchSerialNo;
/**
* 商户号
*/
private String mchId;
/**
* 商户key
*/
private String apiKey;
/**
* 订单支付回调地址
*/
private String notifyUrl;
/**
* 订单微信退款回调地址
*/
private String refundNotifyUrl;
@Bean
public RSAAutoCertificateConfig getWxConfig() {
// 使用自动更新平台证书的RSA配置
// 一个商户号只能初始化一个配置,否则会因为重复的下载任务报错
return new RSAAutoCertificateConfig.Builder()
.merchantId(mchId)
.privateKey(WXPayUtil.PRIVATE_CERT_KEY)
.merchantSerialNumber(mchSerialNo)
.apiV3Key(apiKey)
.build();
}
ps:这里的微信priavateKey是微信证书的私钥key
WXPayUtil: 证书privateKey替换自己注册的商家证书key
public class WXPayUtil {
//随机字符串设置
private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final Random RANDOM = new SecureRandom();
public static final String TRANSFER_FAIL_STATE="FAIL";
public static final String TRANSFER_SUCCESS_STATE="SUCCESS";
//证书privatekey
public static final String PRIVATE_CERT_KEY="-----BEGIN PRIVATE KEY-----\n"
}
退款请求类:
@Data
public class RefundOrderReq {
/**退款单号**/
@NotNull(message = "退款单号不能为空")
private String refundNo;
/**加密字符串**/
@NotNull(message = "加密字符串不能为空")
private String sign;
/**实际退还金额,如果该字段为空,则使用商品行金额,该金额不能大于商品行金额**/
private BigDecimal refundPrice;
/**退款原因**/
private String refundMsg;
}
好了,里面的业务代码可以替换为自己需要处理的业务,如果对你有帮助可以点个赞