feat(transaction): 实现易生支付实时退款功能并优化微信支付流程

- 新增易生支付实时退款功能,实现订单退款操作- 优化微信支付流程,使用微信登录授权码替代原有的openid- 调整订单标题和描述的处理逻辑,提高支付请求的灵活性
- 移除不必要的接口,简化系统架构
This commit is contained in:
huk 2025-09-01 10:53:40 +08:00
parent 4e543a22d2
commit 4ded3b177b
5 changed files with 85 additions and 56 deletions

View File

@ -3,16 +3,22 @@ package com.wzj.soopin.transaction.controller;
import cn.dev33.satoken.annotation.SaIgnore; import cn.dev33.satoken.annotation.SaIgnore;
import com.wzj.soopin.transaction.domain.bo.PaymentBO; import com.wzj.soopin.transaction.domain.bo.PaymentBO;
import com.wzj.soopin.transaction.domain.bo.easypay.EasyPayRequest; import com.wzj.soopin.transaction.domain.bo.easypay.EasyPayRequest;
import com.wzj.soopin.transaction.domain.entity.WxAuthResponse;
import com.wzj.soopin.transaction.domain.vo.EasypayPaymentResultVO; import com.wzj.soopin.transaction.domain.vo.EasypayPaymentResultVO;
import com.wzj.soopin.transaction.domain.vo.EasypayPrePayVO; import com.wzj.soopin.transaction.domain.vo.EasypayPrePayVO;
import com.wzj.soopin.transaction.service.IEasypayService; import com.wzj.soopin.transaction.service.IEasypayService;
import com.wzj.soopin.transaction.service.impl.WxAuthService;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.R; import org.dromara.common.core.domain.R;
import org.dromara.common.log.annotation.Log; import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType; import org.dromara.common.log.enums.BusinessType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.rmi.ServerException; import java.rmi.ServerException;
@ -37,7 +43,7 @@ public class TransEasypayController {
* @return * @return
*/ */
@SaIgnore @SaIgnore
@Log(title = "易生支付结果通知回调", businessType = BusinessType.UPDATE) @Log(title = "易生支付-支付结果通知回调", businessType = BusinessType.UPDATE)
@PostMapping("/trade/callback") @PostMapping("/trade/callback")
public Map tradeCallback(@RequestBody EasyPayRequest easyPayRequest) { public Map tradeCallback(@RequestBody EasyPayRequest easyPayRequest) {
easypayService.handleTradeCallback(easyPayRequest); easypayService.handleTradeCallback(easyPayRequest);
@ -53,7 +59,7 @@ public class TransEasypayController {
* @param paymentBO * @param paymentBO
* @return * @return
*/ */
@Log(title = "发起支付", businessType = BusinessType.OTHER) @Log(title = "易生支付-发起支付", businessType = BusinessType.OTHER)
@PostMapping("/trade") @PostMapping("/trade")
public R<EasypayPrePayVO> trade(@RequestBody PaymentBO paymentBO) throws ServerException { public R<EasypayPrePayVO> trade(@RequestBody PaymentBO paymentBO) throws ServerException {
EasypayPrePayVO easypayPrePayVO = easypayService.payment(paymentBO); EasypayPrePayVO easypayPrePayVO = easypayService.payment(paymentBO);
@ -67,8 +73,21 @@ public class TransEasypayController {
* @return * @return
*/ */
@GetMapping("/paymentQuery/{orderId}") @GetMapping("/paymentQuery/{orderId}")
public R<EasypayPaymentResultVO> paymentQuery(@PathVariable("orderId") String orderId) throws ServerException { public R<EasypayPaymentResultVO> paymentQuery(@PathVariable("orderId") Long orderId) throws ServerException {
EasypayPaymentResultVO easypayPaymentResultVO = easypayService.paymentQuery(orderId); EasypayPaymentResultVO easypayPaymentResultVO = easypayService.paymentQuery(orderId);
return R.ok(easypayPaymentResultVO); return R.ok(easypayPaymentResultVO);
} }
/**
* 实时退款
*
* @param orderId 订单id
* @return
*/
@Log(title = "易生支付-实时退款", businessType = BusinessType.OTHER)
@PostMapping("/refund/{orderId}")
public R<EasypayPrePayVO> refund(@PathVariable("orderId") Long orderId) throws ServerException {
easypayService.refund(orderId);
return R.ok();
}
} }

View File

@ -179,21 +179,7 @@ public class WxPayController {
} }
} }
/**
* 通过小程序的授权码获取用户的openid
*
* @param code 授权码
* @return 包含openid的响应对象
*/
@Tag(name = "获取用户openid")
@GetMapping("/openid2")
@Parameters({
@Parameter(name = "code", description = "授权码", required = true, in = ParameterIn.QUERY)
})
public R<WxAuthResponse> getOpenIdByMiniProgramCode(@RequestParam String code) {
WxAuthResponse response = wxAuthService.getOpenIdByMiniProgramCode(code);
return R.ok(response);
}

View File

@ -23,17 +23,6 @@ public class PaymentBO {
@NotNull(message = "订单Id不能为空") @NotNull(message = "订单Id不能为空")
private Long orderId; private Long orderId;
/**
* 订单标题对应支付宝订单里的 商品说明微信订单里的商品
*/
@NotBlank(message = "订单标题不能为空")
private String orderSub;
/**
* 订单描述
*/
@NotBlank(message = "订单描述不能为空")
private String orderDes;
/** /**
* 支付方式 * 支付方式
*/ */
@ -46,9 +35,9 @@ public class PaymentBO {
private String buyerId; private String buyerId;
/** /**
* 微信业务参数:微信用户子标识 * 微信登录授权码
*/ */
private String subOpenId; private String wxLoginCode;
// ============银联业务参数 start ============== // ============银联业务参数 start ==============
/** /**

View File

@ -24,7 +24,7 @@ public interface IEasypayService {
* @param orderId * @param orderId
* @return * @return
*/ */
EasypayPaymentResultVO paymentQuery(String orderId) throws ServerException; EasypayPaymentResultVO paymentQuery(Long orderId) throws ServerException;
/** /**
* 处理易生支付结果通知回调 * 处理易生支付结果通知回调
@ -32,6 +32,12 @@ public interface IEasypayService {
*/ */
void handleTradeCallback(EasyPayRequest easyPayRequest); void handleTradeCallback(EasyPayRequest easyPayRequest);
/**
* 易生退款
* @param orderId
*/
void refund(Long orderId);
/** /**
* 获取易生账户 * 获取易生账户
* @param memberId * @param memberId

View File

@ -26,6 +26,9 @@ import com.wzj.soopin.transaction.config.EasypayConfig;
import com.wzj.soopin.transaction.config.WechatMiniProgramConfig; import com.wzj.soopin.transaction.config.WechatMiniProgramConfig;
import com.wzj.soopin.transaction.domain.bo.PaymentBO; import com.wzj.soopin.transaction.domain.bo.PaymentBO;
import com.wzj.soopin.transaction.domain.bo.easypay.*; import com.wzj.soopin.transaction.domain.bo.easypay.*;
import com.wzj.soopin.transaction.domain.bo.easypay.refund.apply.req.RefundApplyReqBody;
import com.wzj.soopin.transaction.domain.bo.easypay.refund.apply.req.RefundReqOrderInfo;
import com.wzj.soopin.transaction.domain.bo.easypay.refund.apply.resp.RefundApplyRespBody;
import com.wzj.soopin.transaction.domain.bo.easypay.trade.jsapi.req.*; import com.wzj.soopin.transaction.domain.bo.easypay.trade.jsapi.req.*;
import com.wzj.soopin.transaction.domain.bo.easypay.trade.jsapi.resp.JsApiRespBody; import com.wzj.soopin.transaction.domain.bo.easypay.trade.jsapi.resp.JsApiRespBody;
import com.wzj.soopin.transaction.domain.bo.easypay.trade.jsapi.resp.JsapiRespOrderInfo; import com.wzj.soopin.transaction.domain.bo.easypay.trade.jsapi.resp.JsapiRespOrderInfo;
@ -33,6 +36,7 @@ import com.wzj.soopin.transaction.domain.bo.easypay.trade.jsapi.resp.WxJsApiResp
import com.wzj.soopin.transaction.domain.bo.easypay.trade.query.req.TradeQueryReqBody; import com.wzj.soopin.transaction.domain.bo.easypay.trade.query.req.TradeQueryReqBody;
import com.wzj.soopin.transaction.domain.bo.easypay.trade.query.resp.TradeQueryRespBody; import com.wzj.soopin.transaction.domain.bo.easypay.trade.query.resp.TradeQueryRespBody;
import com.wzj.soopin.transaction.domain.bo.easypay.trade.query.resp.TradeQueryRespOrderInfo; import com.wzj.soopin.transaction.domain.bo.easypay.trade.query.resp.TradeQueryRespOrderInfo;
import com.wzj.soopin.transaction.domain.entity.WxAuthResponse;
import com.wzj.soopin.transaction.domain.po.PayOrder; import com.wzj.soopin.transaction.domain.po.PayOrder;
import com.wzj.soopin.transaction.domain.vo.EasypayAccountVO; import com.wzj.soopin.transaction.domain.vo.EasypayAccountVO;
import com.wzj.soopin.transaction.domain.vo.EasypayPaymentResultVO; import com.wzj.soopin.transaction.domain.vo.EasypayPaymentResultVO;
@ -76,12 +80,12 @@ import static com.wzj.soopin.transaction.constans.EasypayConstants.*;
@Slf4j @Slf4j
public class EasypayServiceImpl implements IEasypayService { public class EasypayServiceImpl implements IEasypayService {
@Autowired
private EasypayConfig easypayConfig; private EasypayConfig easypayConfig;
@Autowired
private WechatMiniProgramConfig wechatMiniProgramConfig; private WechatMiniProgramConfig wechatMiniProgramConfig;
private WxAuthService wxAuthService;
private final OrderMapper orderMapper; private final OrderMapper orderMapper;
private final DivideMapper divideMapper; private final DivideMapper divideMapper;
@ -252,7 +256,7 @@ public class EasypayServiceImpl implements IEasypayService {
private void checkPaymentParamByPayType(PaymentBO paymentBO) throws ServerException { private void checkPaymentParamByPayType(PaymentBO paymentBO) throws ServerException {
switch (paymentBO.getPayType()) { switch (paymentBO.getPayType()) {
case ALI_PAY_JSAPI, ALI_PAY_MINI_APP -> Assert.notBlank(paymentBO.getBuyerId(), () -> new ServiceException("支付宝支付缺少必要参数:buyerId")); case ALI_PAY_JSAPI, ALI_PAY_MINI_APP -> Assert.notBlank(paymentBO.getBuyerId(), () -> new ServiceException("支付宝支付缺少必要参数:buyerId"));
case WE_CHAT_JSAPI, WE_CHAT_MINI_APP -> Assert.notBlank(paymentBO.getSubOpenId(), () -> new ServiceException("微信支付缺少必要参数:subOpenId")); case WE_CHAT_JSAPI, WE_CHAT_MINI_APP -> Assert.notBlank(paymentBO.getWxLoginCode(), () -> new ServiceException("微信支付缺少必要参数:微信登录授权code"));
case UNION_PAY_JSAPI, UNION_PAY_Js_MINI -> Assert.isTrue(StrUtil.isAllNotBlank(paymentBO.getTransType(), paymentBO.getUserAuthCode(), case UNION_PAY_JSAPI, UNION_PAY_Js_MINI -> Assert.isTrue(StrUtil.isAllNotBlank(paymentBO.getTransType(), paymentBO.getUserAuthCode(),
paymentBO.getUserId(), paymentBO.getAreaInfo(), paymentBO.getPaymentValidTime(), paymentBO.getUserId(), paymentBO.getAreaInfo(), paymentBO.getPaymentValidTime(),
paymentBO.getQrCode(), paymentBO.getQrCodeType()), () -> new ServiceException("银联支付缺少必要参数")); paymentBO.getQrCode(), paymentBO.getQrCodeType()), () -> new ServiceException("银联支付缺少必要参数"));
@ -267,29 +271,21 @@ public class EasypayServiceImpl implements IEasypayService {
* @param paymentBO * @param paymentBO
*/ */
private void setPayInfo(JsApiReqBody apiReqBody, PaymentBO paymentBO) throws ServerException { private void setPayInfo(JsApiReqBody apiReqBody, PaymentBO paymentBO) throws ServerException {
switch (paymentBO.getPayType()) {
case ALI_PAY_JSAPI, ALI_PAY_MINI_APP -> apiReqBody.setAliBizParam(AliBizParam.builder().buyerId(paymentBO.getBuyerId()).build());
case WE_CHAT_JSAPI, WE_CHAT_MINI_APP -> {
WxAuthResponse wxAuthResponse = wxAuthService.getOpenIdByMiniProgramCode(paymentBO.getWxLoginCode());
Assert.isTrue(StrUtil.equals(wxAuthResponse.getErrcode(), "0"), () -> new ServiceException("微信小程序登录异常"));
apiReqBody.setWxBizParam(WxBizParam.builder().subAppid(wechatMiniProgramConfig.getAppId()).subOpenId(wxAuthResponse.getOpenid()).build());
}
case UNION_PAY_JSAPI, UNION_PAY_Js_MINI -> apiReqBody.setQrBizParam(BeanUtil.copyProperties(paymentBO, QrBizParam.class));
default -> throw new ServerException("不支持的支付方式");
}
PayInfo payInfo = PayInfo.builder() PayInfo payInfo = PayInfo.builder()
.payType(paymentBO.getPayType().getValue()) .payType(paymentBO.getPayType().getValue())
.transDate(DateUtils.parseDateToStr(FormatsType.YYYYMMDD, new Date())) .transDate(DateUtils.parseDateToStr(FormatsType.YYYYMMDD, new Date()))
.build(); .build();
apiReqBody.setPayInfo(payInfo); apiReqBody.setPayInfo(payInfo);
switch (paymentBO.getPayType()) {
case ALI_PAY_JSAPI, ALI_PAY_MINI_APP -> {
Assert.isTrue(StrUtil.isNotBlank(paymentBO.getBuyerId()), () -> new ServiceException("支付宝支付需传入买家ID"));
apiReqBody.setAliBizParam(AliBizParam.builder().buyerId(paymentBO.getBuyerId()).build());
}
case WE_CHAT_JSAPI, WE_CHAT_MINI_APP -> {
Assert.isTrue(StrUtil.isAllNotBlank(paymentBO.getSubOpenId()), () -> new ServiceException("微信支付需传入appId和openId"));
apiReqBody.setWxBizParam(WxBizParam.builder().subAppid(wechatMiniProgramConfig.getAppId()).subOpenId(paymentBO.getSubOpenId()).build());
}
case UNION_PAY_JSAPI, UNION_PAY_Js_MINI -> {
Assert.isTrue(StrUtil.isAllNotBlank(paymentBO.getTransType(), paymentBO.getUserAuthCode(),
paymentBO.getUserId(), paymentBO.getAreaInfo(), paymentBO.getPaymentValidTime(),
paymentBO.getQrCode(), paymentBO.getQrCodeType()), () -> new ServiceException("银联支付传入参数不完整"));
QrBizParam qrBizParam = BeanUtil.copyProperties(paymentBO, QrBizParam.class);
apiReqBody.setQrBizParam(qrBizParam);
}
default -> throw new ServerException("不支持的支付方式");
}
} }
/** /**
@ -323,8 +319,8 @@ public class EasypayServiceImpl implements IEasypayService {
.transAmount(transAmount) .transAmount(transAmount)
.backUrl(easypayConfig.getBackUrl()) .backUrl(easypayConfig.getBackUrl())
.timeout(String.valueOf(TRACE_TIMEOUT)) .timeout(String.valueOf(TRACE_TIMEOUT))
.orderSub(paymentBO.getOrderSub()) .orderSub("无终街支付订单")
.orderDes(paymentBO.getOrderDes()) .orderDes("无终街支付订单:" + order.getMerchantNote())
.build(); .build();
apiReqBody.setReqOrderInfo(jsapiReqOrderInfo); apiReqBody.setReqOrderInfo(jsapiReqOrderInfo);
return payOrder; return payOrder;
@ -366,7 +362,7 @@ public class EasypayServiceImpl implements IEasypayService {
* @throws ServerException * @throws ServerException
*/ */
@Override @Override
public EasypayPaymentResultVO paymentQuery(String orderId) throws ServerException { public EasypayPaymentResultVO paymentQuery(Long orderId) throws ServerException {
Order order = orderMapper.selectById(orderId); Order order = orderMapper.selectById(orderId);
Assert.notNull(order, () -> new ServiceException("订单不存在")); Assert.notNull(order, () -> new ServiceException("订单不存在"));
PayOrder payOrder = payOrderMapper.selectOne(Wrappers.lambdaQuery(PayOrder.class) PayOrder payOrder = payOrderMapper.selectOne(Wrappers.lambdaQuery(PayOrder.class)
@ -477,6 +473,39 @@ public class EasypayServiceImpl implements IEasypayService {
} }
} }
@Override
public void refund(Long orderId) {
Order order = orderMapper.selectById(orderId);
Assert.notNull(order, () -> new ServiceException("订单不存在"));
PayOrder payOrder = payOrderMapper.selectOne(Wrappers.lambdaQuery(PayOrder.class).eq(PayOrder::getOrderId, orderId).last("limit 1"));
Assert.notNull(payOrder, () -> new ServiceException("订单不存在"));
EasyPayRequestHeader reqHeader = generateEasyPayRequestHeader();
RefundApplyReqBody refundApplyReqBody = RefundApplyReqBody.builder()
.reqInfo(ReqInfo.builder().mchtCode(easypayConfig.getMchtCode()).build())
.payInfo(PayInfo.builder().transDate(DateUtils.parseDateToStr(FormatsType.YYYYMMDD, new Date())).build())
.reqOrderInfo(RefundReqOrderInfo.builder()
.orgTrace(StrBuilder.create(TRACE_PREFIX).append(System.currentTimeMillis()).append(RandomUtil.randomString(4)).toString())
.oriOrgTrace(String.valueOf(order.getPayId()))
.oriTransDate(DateUtils.parseDateToStr(FormatsType.YYYYMMDD,payOrder.getStartTransDate()))
.refundAmount(payOrder.getTransAmount())
.build())
.build();
String reqSign = getSignStr(reqHeader, refundApplyReqBody);
EasyPayRequest easyRequest = EasyPayRequest.builder()
.reqHeader(reqHeader)
.reqBody(refundApplyReqBody)
.reqSign(reqSign)
.build();
log.debug("调用易生实时退款接口请求:{}",JSONObject.toJSONString(easyRequest));
String url = StrBuilder.create(easypayConfig.getApiPathPrefix()).append("/trade/refund/apply").toString();
String body = HttpRequest.post(url)
.timeout(3000)
.body(JSON.toJSONString(easyRequest))
.execute()
.body();
log.debug("调用易实时退款接口响应:{}",body);
RefundApplyRespBody refundApplyRespBody = JSONObject.parseObject(body, RefundApplyRespBody.class);
}
@Override @Override