Commit 6856d847 by 郑云飞

支付回调,退款,退款回调功能完成

parent c47aeba4
...@@ -176,7 +176,8 @@ wx: ...@@ -176,7 +176,8 @@ wx:
subAppId: #服务商模式下的子商户公众账号ID subAppId: #服务商模式下的子商户公众账号ID
subMchId: #服务商模式下的子商户号 subMchId: #服务商模式下的子商户号
keyPath: C:\\ProgramData\\certKey\xinrenli\\apiclient_cert.p12 # p12证书的位置,可以指定绝对路径,也可以指定类路径(以classpath:开头) keyPath: C:\\ProgramData\\certKey\xinrenli\\apiclient_cert.p12 # p12证书的位置,可以指定绝对路径,也可以指定类路径(以classpath:开头)
notifyUrl: http://xinrenli.nyinhong.com/api//xinrenli/order/notify # 微信支付回调接口 notifyUrl: http://xinrenli.nyinhong.com/api/xinrenli/order/notify # 微信支付回调接口
refundNotifyUrl: http://xinrenli.nyinhong.com/api/xinrenli/order/refundNotify # 微信t退款回调接口
# mp: # mp:
# useRedis: false # useRedis: false
# redisConfig: # redisConfig:
......
...@@ -31,7 +31,6 @@ public class WxPayConfiguration { ...@@ -31,7 +31,6 @@ public class WxPayConfiguration {
payConfig.setSubAppId(StringUtils.trimToNull(this.properties.getSubAppId())); payConfig.setSubAppId(StringUtils.trimToNull(this.properties.getSubAppId()));
payConfig.setSubMchId(StringUtils.trimToNull(this.properties.getSubMchId())); payConfig.setSubMchId(StringUtils.trimToNull(this.properties.getSubMchId()));
payConfig.setKeyPath(StringUtils.trimToNull(this.properties.getKeyPath())); payConfig.setKeyPath(StringUtils.trimToNull(this.properties.getKeyPath()));
payConfig.setNotifyUrl(StringUtils.trimToNull(this.properties.getNotifyUrl()));
// 可以指定是否使用沙箱环境 // 可以指定是否使用沙箱环境
payConfig.setUseSandboxEnv(false); payConfig.setUseSandboxEnv(false);
......
...@@ -41,9 +41,4 @@ public class WxPayProperties { ...@@ -41,9 +41,4 @@ public class WxPayProperties {
*/ */
private String keyPath; private String keyPath;
/**
* 支付回调接口
*/
private String notifyUrl;
} }
package com.yongqi.xinrenli.controller; package com.yongqi.xinrenli.controller;
import java.math.BigDecimal;
import java.util.List; import java.util.List;
import java.util.Arrays; import java.util.Arrays;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.yongqi.common.core.domain.model.LoginUser; import com.yongqi.common.core.domain.model.LoginUser;
import com.yongqi.common.helper.LoginHelper; import com.yongqi.common.helper.LoginHelper;
import com.yongqi.common.utils.ip.AddressUtils; import com.yongqi.common.utils.ip.AddressUtils;
...@@ -144,10 +146,14 @@ public class DbOrderController extends BaseController { ...@@ -144,10 +146,14 @@ public class DbOrderController extends BaseController {
/** /**
* 下单 * 下单
* @param orderId 订单id
* @param couponId 优惠券id
* @param request
* @return
*/ */
@SaCheckPermission("xinrenli:order:pay") @SaCheckPermission("xinrenli:order:pay")
@Log(title = "下单", businessType = BusinessType.DELETE) @Log(title = "下单", businessType = BusinessType.OTHER)
@RequestMapping("/orderPay") @GetMapping("/orderPay")
public R orderPay(@NotNull(message = "主键不能为空") @RequestParam("id") Long orderId public R orderPay(@NotNull(message = "主键不能为空") @RequestParam("id") Long orderId
, @RequestParam(value = "id", required = false) Long couponId , @RequestParam(value = "id", required = false) Long couponId
, HttpServletRequest request) { , HttpServletRequest request) {
...@@ -157,16 +163,41 @@ public class DbOrderController extends BaseController { ...@@ -157,16 +163,41 @@ public class DbOrderController extends BaseController {
} }
/** /**
* 下单 * 下单回调
*/ */
@SaCheckPermission("xinrenli:order:notify") @SaCheckPermission("xinrenli:order:notify")
@Log(title = "下单", businessType = BusinessType.DELETE) @Log(title = "下单回调", businessType = BusinessType.OTHER)
@RequestMapping("/notify") @PostMapping("/notify")
public R payCallback(@RequestBody String xmlData) { public String payCallback(@RequestBody String xmlData) throws WxPayException {
return iDbOrderService.payCallback(xmlData); return iDbOrderService.payCallback(xmlData);
} }
/** /**
* 退款
* @param orderId 订单id
* @param refundAmount 退款金额
* @return
*/
@SaCheckPermission("xinrenli:order:refund")
@Log(title = "退款", businessType = BusinessType.OTHER)
@GetMapping("/refund")
public R refund(@NotNull(message = "订单id不能为空") @RequestParam("orderId") Long orderId,
@NotNull(message = "退款金额不能为空")@RequestParam("refundeAmount")BigDecimal refundAmount) {
LoginUser loginUser = LoginHelper.getLoginUser();
return iDbOrderService.refund(orderId, refundAmount, loginUser);
}
/**
* 退款回调
*/
@SaCheckPermission("xinrenli:order:refundNotify")
@Log(title = "退款回调", businessType = BusinessType.OTHER)
@PostMapping("/refundNotify")
public String refundNotify(@RequestBody String xmlData) throws WxPayException {
return iDbOrderService.refundNotify(xmlData);
}
/**
* 报表 * 报表
*/ */
@SaCheckPermission("xinrenli:order:reportForms") @SaCheckPermission("xinrenli:order:reportForms")
......
...@@ -33,6 +33,10 @@ public class DbOrder { ...@@ -33,6 +33,10 @@ public class DbOrder {
*/ */
private String wxOrderNo; private String wxOrderNo;
/** /**
* 退款单号
*/
private String refundNo;
/**
* 用户id * 用户id
*/ */
private Long userId; private Long userId;
...@@ -69,6 +73,10 @@ public class DbOrder { ...@@ -69,6 +73,10 @@ public class DbOrder {
*/ */
private BigDecimal payAmount; private BigDecimal payAmount;
/** /**
* 退款金额
*/
private BigDecimal refundAmount;
/**
* 使用优惠券id * 使用优惠券id
*/ */
private Long useCouponId; private Long useCouponId;
......
...@@ -5,6 +5,7 @@ import lombok.Data; ...@@ -5,6 +5,7 @@ import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.Date;
/** /**
...@@ -39,6 +40,6 @@ public class WxPayOrderResult { ...@@ -39,6 +40,6 @@ public class WxPayOrderResult {
*/ */
@DateTimeFormat(pattern = "yyyy/MM/dd HH:mm:ss") @DateTimeFormat(pattern = "yyyy/MM/dd HH:mm:ss")
@JsonFormat(pattern = "yyyy/MM/dd HH:mm:ss") @JsonFormat(pattern = "yyyy/MM/dd HH:mm:ss")
private LocalDateTime createTime; private Date createTime;
} }
...@@ -10,6 +10,7 @@ import com.yongqi.common.core.page.TableDataInfo; ...@@ -10,6 +10,7 @@ import com.yongqi.common.core.page.TableDataInfo;
import com.yongqi.common.core.domain.PageQuery; import com.yongqi.common.core.domain.PageQuery;
import com.yongqi.xinrenli.domain.vo.ReportFormsVo; import com.yongqi.xinrenli.domain.vo.ReportFormsVo;
import java.math.BigDecimal;
import java.util.List; import java.util.List;
/** /**
...@@ -85,5 +86,21 @@ public interface IDbOrderService { ...@@ -85,5 +86,21 @@ public interface IDbOrderService {
* @param xmlData * @param xmlData
* @return * @return
*/ */
R payCallback(String xmlData) throws WxPayException; String payCallback(String xmlData) throws WxPayException;
/**
* 退款
* @param orderId
* @param refundAmount
* @param loginUser
* @return
*/
R refund(Long orderId, BigDecimal refundAmount, LoginUser loginUser);
/**
* 退款回调
* @param xmlData
* @return
*/
String refundNotify(String xmlData) throws WxPayException;
} }
...@@ -3,8 +3,12 @@ package com.yongqi.xinrenli.service.impl; ...@@ -3,8 +3,12 @@ package com.yongqi.xinrenli.service.impl;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils; import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest; import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
import com.github.binarywang.wxpay.constant.WxPayConstants; import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException; import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService; import com.github.binarywang.wxpay.service.WxPayService;
...@@ -19,6 +23,7 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers; ...@@ -19,6 +23,7 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.yongqi.common.utils.redis.CodeUtils; import com.yongqi.common.utils.redis.CodeUtils;
import com.yongqi.xinrenli.domain.DbCoupon; import com.yongqi.xinrenli.domain.DbCoupon;
import com.yongqi.xinrenli.domain.DbFile; import com.yongqi.xinrenli.domain.DbFile;
import com.yongqi.xinrenli.domain.DbUserCoupon;
import com.yongqi.xinrenli.domain.bo.DbOrderEditBo; import com.yongqi.xinrenli.domain.bo.DbOrderEditBo;
import com.yongqi.xinrenli.domain.vo.DbFileVo; import com.yongqi.xinrenli.domain.vo.DbFileVo;
import com.yongqi.xinrenli.domain.vo.ReportFormsVo; import com.yongqi.xinrenli.domain.vo.ReportFormsVo;
...@@ -28,9 +33,11 @@ import com.yongqi.xinrenli.enums.FileEnumServiceType; ...@@ -28,9 +33,11 @@ import com.yongqi.xinrenli.enums.FileEnumServiceType;
import com.yongqi.xinrenli.enums.OrderStatusEnum; import com.yongqi.xinrenli.enums.OrderStatusEnum;
import com.yongqi.xinrenli.mapper.DbCouponMapper; import com.yongqi.xinrenli.mapper.DbCouponMapper;
import com.yongqi.xinrenli.mapper.DbFileMapper; import com.yongqi.xinrenli.mapper.DbFileMapper;
import com.yongqi.xinrenli.mapper.DbUserCouponMapper;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import com.yongqi.xinrenli.domain.bo.DbOrderBo; import com.yongqi.xinrenli.domain.bo.DbOrderBo;
import com.yongqi.xinrenli.domain.vo.DbOrderVo; import com.yongqi.xinrenli.domain.vo.DbOrderVo;
...@@ -59,6 +66,12 @@ public class DbOrderServiceImpl implements IDbOrderService { ...@@ -59,6 +66,12 @@ public class DbOrderServiceImpl implements IDbOrderService {
private DbCouponMapper couponMapper; private DbCouponMapper couponMapper;
@Autowired @Autowired
private WxPayService wxPayService; private WxPayService wxPayService;
@Autowired
private DbUserCouponMapper userCouponMapper;
@Value("${wx.pay.notifyUrl}")
private String payNotifyUrl;
@Value("${wx.pay.refundNotifyUrl}")
private String refundNotifyUrl;
/** /**
* 查询订单 * 查询订单
...@@ -288,6 +301,7 @@ public class DbOrderServiceImpl implements IDbOrderService { ...@@ -288,6 +301,7 @@ public class DbOrderServiceImpl implements IDbOrderService {
* @return * @return
*/ */
@Override @Override
@Transactional
public R orderPay(Long orderId, Long couponId, LoginUser loginUser, String ipAddr) { public R orderPay(Long orderId, Long couponId, LoginUser loginUser, String ipAddr) {
// 1.根据订单id获取订单信息 // 1.根据订单id获取订单信息
DbOrder dbOrder = this.baseMapper.selectById(orderId); DbOrder dbOrder = this.baseMapper.selectById(orderId);
...@@ -297,11 +311,26 @@ public class DbOrderServiceImpl implements IDbOrderService { ...@@ -297,11 +311,26 @@ public class DbOrderServiceImpl implements IDbOrderService {
if (dbCoupon.getMinUsed().compareTo(dbOrder.getActualAmount()) > 0){ if (dbCoupon.getMinUsed().compareTo(dbOrder.getActualAmount()) > 0){
throw new RuntimeException("未达到此优惠券的最低消费金额"); throw new RuntimeException("未达到此优惠券的最低消费金额");
} }
// 查询用户优惠券关联信息
DbUserCoupon userCoupon = userCouponMapper.selectOne(Wrappers.<DbUserCoupon>lambdaQuery()
.eq(DbUserCoupon::getCouponId, couponId)
.eq(DbUserCoupon::getUserId, loginUser.getUserId()));
// 判断用户是否有此优惠券
if (ObjectUtils.isEmpty(userCoupon)) {
log.error("该用户没有此优惠券:userId:{},couponId:{}",loginUser.getUserId(), couponId);
return R.fail("没有此优惠券");
}
if (Objects.equals(userCoupon.getUseStatus(), DeleteStatusEnum.DELETED.getCode())){
log.error("此优惠券已经使用过了:userId:{},couponId:{}",loginUser.getUserId(), couponId);
return R.fail("此优惠券已经使用过了");
}
BigDecimal payAmount = dbOrder.getActualAmount().subtract(dbCoupon.getFullSubtraction()); BigDecimal payAmount = dbOrder.getActualAmount().subtract(dbCoupon.getFullSubtraction());
dbOrder.setPayAmount(payAmount); dbOrder.setPayAmount(payAmount);
dbOrder.setUseCouponId(couponId); dbOrder.setUseCouponId(couponId);
String orderNo = CodeUtils.createSerialNumber("XQ"); String orderNo = CodeUtils.createSerialNumber("XQ");
dbOrder.setOrderNo(orderNo); dbOrder.setOrderNo(orderNo);
dbOrder.setPayTime(new Date());
baseMapper.updateById(dbOrder);
// 4.组装支付数据 // 4.组装支付数据
WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest(); WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
// 需求标题 // 需求标题
...@@ -324,17 +353,121 @@ public class DbOrderServiceImpl implements IDbOrderService { ...@@ -324,17 +353,121 @@ public class DbOrderServiceImpl implements IDbOrderService {
request.setSignType(WxPayConstants.SignType.MD5); request.setSignType(WxPayConstants.SignType.MD5);
// 用户标识 // 用户标识
request.setOpenid(loginUser.getOpenId()); request.setOpenid(loginUser.getOpenId());
request.setNotifyUrl(payNotifyUrl);
// 5.调用微信统一下单接口 // 5.调用微信统一下单接口
try { try {
Object order = wxPayService.createOrder(request); Object order = wxPayService.createOrder(request);
// 6.返回支付所需数据
String json = JSON.toJSONString(order); String json = JSON.toJSONString(order);
WxPayOrderResult result = JSON.parseObject(json, WxPayOrderResult.class); WxPayOrderResult result = JSON.parseObject(json, WxPayOrderResult.class);
// 6.返回支付所需数据
return R.ok(result); return R.ok(result);
} catch (WxPayException e) { } catch (WxPayException e) {
throw new RuntimeException(e); throw new RuntimeException(e.getMessage());
}
}
/**
* 支付回调
* @param xmlData
* @return
*/
@Override
@Transactional
public String payCallback(String xmlData) throws WxPayException {
final WxPayOrderNotifyResult notifyResult = wxPayService.parseOrderNotifyResult(xmlData);
log.info("支付回调参数解析:{}", notifyResult);
String resultCode = notifyResult.getResultCode();
if (!"SUCCESS".equals(resultCode)) {
return WxPayNotifyResponse.fail("订单未支付,无需处理");
}
// 1.查询订单信息 修改订单状态为已接单
DbOrder dbOrder = baseMapper.selectOne(Wrappers.<DbOrder>lambdaQuery().eq(DbOrder::getOrderNo, notifyResult.getOutTradeNo()));
if (ObjectUtils.isEmpty(dbOrder)){
return WxPayNotifyResponse.fail("订单不存在");
}
dbOrder.setStatus(OrderStatusEnum.RECEIVED_ORDER.getCode());
dbOrder.setWxOrderNo(notifyResult.getTransactionId());
baseMapper.updateById(dbOrder);
// 2.修改用户优惠券使用状态
DbUserCoupon userCoupon = userCouponMapper.selectOne(Wrappers.<DbUserCoupon>lambdaQuery()
.eq(DbUserCoupon::getCouponId, dbOrder.getUseCouponId())
.eq(DbUserCoupon::getUserId, dbOrder.getUserId()));
if (ObjectUtils.isEmpty(userCoupon)) {
log.error("该用户没有此优惠券:userId:{},couponId:{}",dbOrder.getUserId(), dbOrder.getUseCouponId());
return WxPayNotifyResponse.fail("失败");
}
if (Objects.equals(userCoupon.getUseStatus(), DeleteStatusEnum.DELETED.getCode())){
log.error("此优惠券已经使用过了:userId:{},couponId:{}",dbOrder.getUserId(), dbOrder.getUseCouponId());
return WxPayNotifyResponse.fail("失败");
}
userCoupon.setUseStatus(DeleteStatusEnum.DELETED.getCode());
userCouponMapper.updateById(userCoupon);
return WxPayNotifyResponse.success("成功");
}
/**
* 退款
* @param orderId
* @param refundAmount
* @param loginUser
* @return
*/
@Override
@Transactional
public R refund(Long orderId, BigDecimal refundAmount, LoginUser loginUser) {
// 查询订单
DbOrder order = baseMapper.selectById(orderId);
if (ObjectUtils.isEmpty(order)) {
return R.fail("订单不存在");
}
String refundNo = CodeUtils.createSerialNumber("TK");
// 修改订单
order.setRefundNo(refundNo);
order.setRefundAmount(refundAmount);
baseMapper.updateById(order);
// 组装退款参数
WxPayRefundRequest request = new WxPayRefundRequest();
request.setTransactionId(order.getWxOrderNo());
request.setTotalFee(order.getPayAmount().multiply(BigDecimal.valueOf(100L)).intValue());
request.setRefundFee(refundAmount.multiply(BigDecimal.valueOf(100L)).intValue());
request.setOutRefundNo(refundNo);
request.setNotifyUrl(refundNotifyUrl);
// 发起退款
try {
WxPayRefundResult refund = wxPayService.refund(request);
if ("SUCCESS".equals(refund.getReturnCode())) {
return R.ok("退款申请成功");
}
} catch (WxPayException e) {
log.error("退款报错:{}", e.getMessage());
return R.fail(e.getMessage());
} }
return R.fail("退款申请失败,请稍后重试");
}
/**
* 退款回调
* @param xmlData
* @return
*/
@Override
public String refundNotify(String xmlData) throws WxPayException {
final WxPayRefundNotifyResult result = wxPayService.parseRefundNotifyResult(xmlData);
log.info("退款回调解析结果:{}", result);
// 查询订单
DbOrder order = baseMapper.selectOne(Wrappers.<DbOrder>lambdaQuery().eq(DbOrder::getRefundNo, result.getReqInfo().getOutRefundNo()));
if (ObjectUtils.isEmpty(order)) {
log.error("订单不存在:{}",result.getReqInfo().getOutRefundNo());
return WxPayNotifyResponse.fail("订单不存在");
}
if (Objects.equals(order.getStatus(), OrderStatusEnum.REFUNDED.getCode())){
log.error("订单已退款:{}", result.getReqInfo().getOutRefundNo());
return WxPayNotifyResponse.fail("订单已退款");
}
// 修改订单
order.setStatus(OrderStatusEnum.REFUNDED.getCode());
baseMapper.updateById(order);
return WxPayNotifyResponse.success("成功");
} }
/** /**
...@@ -359,16 +492,5 @@ public class DbOrderServiceImpl implements IDbOrderService { ...@@ -359,16 +492,5 @@ public class DbOrderServiceImpl implements IDbOrderService {
return baseMapper.unpaidJob(); return baseMapper.unpaidJob();
} }
/**
* 支付回调
* @param xmlData
* @return
*/
@Override
public R payCallback(String xmlData) throws WxPayException {
final WxPayOrderNotifyResult notifyResult = wxPayService.parseOrderNotifyResult(xmlData);
log.info("支付回调参数解析:{}", notifyResult);
// 1.查询订单信息
return null;
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment