feat: 添加支付和退款相关功能及接口
- 新增支付回调处理逻辑,支持订单状态更新 - 新增退款接口,支持订单退款操作 - 新增退款回调处理逻辑,确保退款状态同步 - 新增支付和退款相关的DTO、Service及工具类 - 优化订单状态管理,支持支付成功和退款成功状态更新
This commit is contained in:
parent
e01ddf0b9c
commit
abd8f7aa60
|
@ -5,6 +5,7 @@ import com.agileboot.common.exception.ApiException;
|
||||||
import com.agileboot.common.exception.error.ErrorCode;
|
import com.agileboot.common.exception.error.ErrorCode;
|
||||||
import com.agileboot.domain.shop.approval.ReturnApprovalApplicationService;
|
import com.agileboot.domain.shop.approval.ReturnApprovalApplicationService;
|
||||||
import com.agileboot.domain.shop.approval.command.AddReturnApprovalCommand;
|
import com.agileboot.domain.shop.approval.command.AddReturnApprovalCommand;
|
||||||
|
import com.agileboot.domain.shop.approval.db.ReturnApprovalEntity;
|
||||||
import com.agileboot.domain.shop.approval.model.ReturnApprovalModel;
|
import com.agileboot.domain.shop.approval.model.ReturnApprovalModel;
|
||||||
import com.agileboot.domain.shop.order.OrderApplicationService;
|
import com.agileboot.domain.shop.order.OrderApplicationService;
|
||||||
import com.agileboot.domain.shop.order.db.ShopOrderGoodsEntity;
|
import com.agileboot.domain.shop.order.db.ShopOrderGoodsEntity;
|
||||||
|
@ -21,6 +22,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 审批请求控制器
|
* 审批请求控制器
|
||||||
|
@ -51,13 +53,9 @@ public class ApprovalApiController {
|
||||||
*/
|
*/
|
||||||
@PostMapping("/submit")
|
@PostMapping("/submit")
|
||||||
@ApiOperation(value = "提交退货审批")
|
@ApiOperation(value = "提交退货审批")
|
||||||
public ResponseDTO<ReturnApprovalModel> submitApproval(@Valid @RequestBody AddReturnApprovalCommand command) {
|
public ResponseDTO<ReturnApprovalEntity> submitApproval(@Valid @RequestBody AddReturnApprovalCommand command) {
|
||||||
try {
|
if (null == command.getOrderGoodsId()) {
|
||||||
if (null == command.getOrderId()) {
|
return ResponseDTO.fail(new ApiException(ErrorCode.Internal.INTERNAL_ERROR, "订单商品ID不能为空"));
|
||||||
return ResponseDTO.fail(new ApiException(ErrorCode.Internal.INTERNAL_ERROR, "订单ID不能为空"));
|
|
||||||
}
|
|
||||||
if (null == command.getGoodsId()) {
|
|
||||||
return ResponseDTO.fail(new ApiException(ErrorCode.Internal.INTERNAL_ERROR, "商品ID不能为空"));
|
|
||||||
}
|
}
|
||||||
if (null == command.getReturnQuantity()) {
|
if (null == command.getReturnQuantity()) {
|
||||||
return ResponseDTO.fail(new ApiException(ErrorCode.Internal.INTERNAL_ERROR, "归还数量不能为空"));
|
return ResponseDTO.fail(new ApiException(ErrorCode.Internal.INTERNAL_ERROR, "归还数量不能为空"));
|
||||||
|
@ -67,21 +65,13 @@ public class ApprovalApiController {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询订单商品信息
|
// 查询订单商品信息
|
||||||
ShopOrderGoodsEntity orderGoods = orderApplicationService.getOrderGoodsByOrderIdAndGoodsId(command.getOrderId(), command.getGoodsId());
|
ShopOrderGoodsEntity orderGoods = orderApplicationService.getOrderGoodsById(command.getOrderGoodsId());
|
||||||
if (null == orderGoods) {
|
if (null == orderGoods) {
|
||||||
return ResponseDTO.fail(new ApiException(ErrorCode.Internal.INTERNAL_ERROR, "订单商品不存在"));
|
return ResponseDTO.fail(new ApiException(ErrorCode.Internal.INTERNAL_ERROR, "订单商品不存在"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置商品价格并初始化审批状态
|
|
||||||
command.setGoodsPrice(orderGoods.getPrice());
|
|
||||||
command.setStatus(1);
|
|
||||||
|
|
||||||
// 执行业务逻辑
|
// 执行业务逻辑
|
||||||
ReturnApprovalModel returnApprovalModel = approvalApplicationService.addApproval(command);
|
ReturnApprovalEntity returnApproval = approvalApplicationService.submitApproval(command, orderGoods);
|
||||||
return ResponseDTO.ok(returnApprovalModel);
|
return ResponseDTO.ok(returnApproval);
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("提交审批失败", e);
|
|
||||||
return ResponseDTO.fail(new ApiException(ErrorCode.Internal.INTERNAL_ERROR, "提交审批失败"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +1,37 @@
|
||||||
package com.agileboot.api.controller;
|
package com.agileboot.api.controller;
|
||||||
|
|
||||||
|
import com.agileboot.common.constant.PayApiConstants;
|
||||||
import com.agileboot.common.core.base.BaseController;
|
import com.agileboot.common.core.base.BaseController;
|
||||||
import com.agileboot.domain.shop.order.dto.CreateOrderResult;
|
import com.agileboot.domain.shop.order.dto.CreateOrderResult;
|
||||||
import com.agileboot.domain.shop.order.dto.GetOrdersByOpenIdDTO;
|
import com.agileboot.domain.shop.order.dto.GetOrdersByOpenIdDTO;
|
||||||
|
import com.agileboot.domain.shop.order.model.OrderGoodsModelFactory;
|
||||||
|
import com.agileboot.domain.shop.order.model.OrderModel;
|
||||||
|
import com.agileboot.domain.shop.payment.PaymentApplicationService;
|
||||||
|
import com.agileboot.domain.shop.payment.dto.RefundVO;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
import com.agileboot.common.core.dto.ResponseDTO;
|
import com.agileboot.common.core.dto.ResponseDTO;
|
||||||
import com.agileboot.domain.shop.order.OrderApplicationService;
|
import com.agileboot.domain.shop.order.OrderApplicationService;
|
||||||
import com.agileboot.domain.shop.order.command.SubmitOrderCommand;
|
import com.agileboot.domain.shop.order.command.SubmitOrderCommand;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 调度日志操作处理
|
* 调度日志操作处理
|
||||||
*
|
*
|
||||||
* @author valarchie
|
* @author valarchie
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/order")
|
@RequestMapping("/api/order")
|
||||||
public class OrderController extends BaseController {
|
public class OrderController extends BaseController {
|
||||||
|
|
||||||
private final OrderApplicationService orderApplicationService;
|
private final OrderApplicationService orderApplicationService;
|
||||||
|
private final PaymentApplicationService paymentApplicationService;
|
||||||
|
|
||||||
// 新增提交订单接口
|
// 新增提交订单接口
|
||||||
@PostMapping("/submit")
|
@PostMapping("/submit")
|
||||||
|
@ -45,4 +51,30 @@ public class OrderController extends BaseController {
|
||||||
GetOrdersByOpenIdDTO result = orderApplicationService.getOrdersByOpenId(openid);
|
GetOrdersByOpenIdDTO result = orderApplicationService.getOrdersByOpenId(openid);
|
||||||
return ResponseDTO.ok(result);
|
return ResponseDTO.ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/refund/{orderId}")
|
||||||
|
public ResponseDTO<RefundVO> refundOrder(@PathVariable Long orderId, @RequestParam int money) {
|
||||||
|
OrderModel orderModel = orderApplicationService.loadById(orderId);
|
||||||
|
try {
|
||||||
|
// 退款金额对比, 退款金额不能大于订单金额
|
||||||
|
// 金额转换(元转分)并四舍五入
|
||||||
|
BigDecimal amountInFen = orderModel.getTotalAmount()
|
||||||
|
.multiply(new BigDecimal("100"))
|
||||||
|
.setScale(0, RoundingMode.HALF_UP);
|
||||||
|
|
||||||
|
if (money < 0) {
|
||||||
|
throw new IllegalArgumentException("退款金额不能为负数");
|
||||||
|
}
|
||||||
|
BigDecimal moneyDecimal = new BigDecimal(money);
|
||||||
|
if (moneyDecimal.compareTo(amountInFen) > 0) {
|
||||||
|
throw new IllegalArgumentException("退款金额不能超过订单总额");
|
||||||
|
}
|
||||||
|
|
||||||
|
RefundVO refundVO = paymentApplicationService.refund(PayApiConstants.biz_id, PayApiConstants.appkey, orderModel.getBizOrderId(), orderModel.getUcid(), "退还", money);
|
||||||
|
log.info("退款结果:{}", refundVO);
|
||||||
|
return ResponseDTO.ok(refundVO);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import com.agileboot.domain.qywx.user.QyUserApplicationService;
|
||||||
import com.agileboot.domain.qywx.user.db.QyUserEntity;
|
import com.agileboot.domain.qywx.user.db.QyUserEntity;
|
||||||
import com.agileboot.domain.qywx.userQySys.SysUserQyUserApplicationService;
|
import com.agileboot.domain.qywx.userQySys.SysUserQyUserApplicationService;
|
||||||
import com.agileboot.domain.shop.order.OrderApplicationService;
|
import com.agileboot.domain.shop.order.OrderApplicationService;
|
||||||
|
import com.agileboot.domain.shop.payment.PaymentApplicationService;
|
||||||
import com.agileboot.domain.shop.payment.dto.PaymentCallbackRequest;
|
import com.agileboot.domain.shop.payment.dto.PaymentCallbackRequest;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
@ -56,12 +57,12 @@ import org.springframework.web.client.RestTemplate;
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@RequestMapping("/api/payment")
|
@RequestMapping("/api/payment")
|
||||||
public class PaymentController {
|
public class PaymentController {
|
||||||
private final OrderApplicationService orderApplicationService;
|
|
||||||
private final AccessTokenApplicationService accessTokenApplicationService;
|
private final AccessTokenApplicationService accessTokenApplicationService;
|
||||||
private final QyUserApplicationService qyUserApplicationService;
|
private final QyUserApplicationService qyUserApplicationService;
|
||||||
private final AuthCorpInfoApplicationService authCorpInfoApplicationService;
|
private final AuthCorpInfoApplicationService authCorpInfoApplicationService;
|
||||||
private final SysUserQyUserApplicationService sysUserQyUserApplicationService;
|
private final SysUserQyUserApplicationService sysUserQyUserApplicationService;
|
||||||
private final MenuApplicationService menuApplicationService;
|
private final MenuApplicationService menuApplicationService;
|
||||||
|
private final PaymentApplicationService paymentApplicationService;
|
||||||
|
|
||||||
// 新增回调接口
|
// 新增回调接口
|
||||||
/**
|
/**
|
||||||
|
@ -78,32 +79,37 @@ public class PaymentController {
|
||||||
public String paymentCallback(HttpServletRequest request, @RequestBody String requestBody) {
|
public String paymentCallback(HttpServletRequest request, @RequestBody String requestBody) {
|
||||||
log.info("支付回调requestBody:{}", requestBody);
|
log.info("支付回调requestBody:{}", requestBody);
|
||||||
try {
|
try {
|
||||||
// 1. 参数解析
|
paymentApplicationService.paymentCallback(requestBody);
|
||||||
PaymentCallbackRequest callbackReq = parseCallbackRequest(requestBody);
|
return "success";
|
||||||
PaymentCallbackRequest.BizContent bizContent = parseBizContent(callbackReq.getBiz_content());
|
} catch (Exception e) {
|
||||||
|
log.error("支付回调处理失败", e);
|
||||||
// 2. 签名验证(需要根据biz_id获取对应的appKey)
|
|
||||||
String appKey = getAppKeyByBizId(callbackReq.getBiz_id()); // 需要实现根据biz_id获取appKey的逻辑
|
|
||||||
boolean signValid = OpenSignUtil.checkOpenSign(appKey, callbackReq.getSign(), requestBody);
|
|
||||||
|
|
||||||
if (!signValid) {
|
|
||||||
log.error("支付回调签名验证失败:{}", requestBody);
|
|
||||||
return "fail";
|
return "fail";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bizContent.getTrade_status().equals("SUCCESS")) {
|
|
||||||
// 3. 业务处理(需要实现幂等性校验)
|
|
||||||
handlePaymentSuccess(
|
|
||||||
bizContent.getBiz_order_id(),
|
|
||||||
bizContent.getTotal_amount(),
|
|
||||||
bizContent.getTrade_id(),
|
|
||||||
bizContent.getTrade_pay_time()
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
log.error("支付订单失败requestBody:{}", requestBody);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return "success";
|
/**
|
||||||
|
* 微信支付退款回调接口
|
||||||
|
* @param request HTTP请求对象,用于获取请求头信息
|
||||||
|
* @param requestBody 回调请求体(URL编码格式的XML数据)
|
||||||
|
* @return 处理结果:"success"表示成功处理并停止通知,"fail"表示需要微信重新发起通知
|
||||||
|
* @throws ApiException 当出现以下情况时抛出:
|
||||||
|
* <ul>
|
||||||
|
* <li>签名验证失败</li>
|
||||||
|
* <li>数据解密失败</li>
|
||||||
|
* <li>退款状态异常</li>
|
||||||
|
* </ul>
|
||||||
|
* @apiNote 该接口需要处理以下流程:
|
||||||
|
* 1. 签名验证(使用微信支付API密钥)
|
||||||
|
* 2. 解密退款结果(如需加密传输)
|
||||||
|
* 3. 退款状态判断(成功/失败)
|
||||||
|
* 4. 更新订单退款状态(需保证幂等性)
|
||||||
|
* 5. 按微信接口规范返回正确处理结果
|
||||||
|
*/
|
||||||
|
@PostMapping("/refund/callback")
|
||||||
|
public String refundCallback(HttpServletRequest request, @RequestBody String requestBody) {
|
||||||
|
log.info("退款回调requestBody:{}", requestBody);
|
||||||
|
try {
|
||||||
|
return paymentApplicationService.refundCallback(requestBody);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("支付回调处理失败", e);
|
log.error("支付回调处理失败", e);
|
||||||
return "fail";
|
return "fail";
|
||||||
|
@ -255,37 +261,7 @@ public class PaymentController {
|
||||||
return ResponseDTO.ok(response);
|
return ResponseDTO.ok(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
private PaymentCallbackRequest parseCallbackRequest(String requestBody) {
|
|
||||||
// 实现将URL参数解析为PaymentCallbackRequest
|
|
||||||
// 示例实现(需要根据实际参数格式调整):
|
|
||||||
Map<String, String> paramMap = new HashMap<>();
|
|
||||||
for (String param : requestBody.split("&")) {
|
|
||||||
String[] pair = param.split("=");
|
|
||||||
if (pair.length == 2) {
|
|
||||||
paramMap.put(pair[0], URLDecoder.decode(pair[1], StandardCharsets.UTF_8));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return BeanUtil.toBean(paramMap, PaymentCallbackRequest.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
private PaymentCallbackRequest.BizContent parseBizContent(String bizContent) {
|
|
||||||
// 实现biz_content的JSON解析
|
|
||||||
return JSONUtil.toBean(bizContent, PaymentCallbackRequest.BizContent.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 需要实现的方法(根据业务需求补充)
|
|
||||||
private String getAppKeyByBizId(String bizId) {
|
|
||||||
// 根据biz_id从数据库或配置获取对应的appKey
|
|
||||||
return "wxshop202503081132";
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handlePaymentSuccess(String bizOrderId, Integer amount, String tradeId, String tradePayTime) {
|
|
||||||
// 实现订单状态更新和幂等性校验
|
|
||||||
if (StringUtils.isNotBlank(bizOrderId) && bizOrderId.startsWith("wxshop-")) {
|
|
||||||
// 订单号格式为 wxshop-1-time,提取中间的订单号
|
|
||||||
String orderId = bizOrderId.split("-")[1];
|
|
||||||
orderApplicationService.handlePaymentSuccess(Long.valueOf(orderId), amount, tradeId, tradePayTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.agileboot.common.constant;
|
||||||
|
|
||||||
|
public class PayApiConstants {
|
||||||
|
public static final String biz_id = "wxshop";
|
||||||
|
public static final String appkey = "wxshop202503081132";
|
||||||
|
|
||||||
|
}
|
|
@ -10,6 +10,8 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
import com.agileboot.common.constant.PayApiConstants;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.codec.digest.Md5Crypt;
|
import org.apache.commons.codec.digest.Md5Crypt;
|
||||||
import org.apache.commons.collections4.MapUtils;
|
import org.apache.commons.collections4.MapUtils;
|
||||||
|
@ -82,7 +84,7 @@ public class OpenSignUtil {
|
||||||
+ "2448492%22%2C%22trade_id%22%3A%221063669415%22%2C%22total_amount%22%3A1%2C%22extra%22%3A%22%22%2C%22trade_pay_time%22%3A%222025-03-20+17%3A20%3A55%22%2C%22trade_status%22%3A%22SUCCESS%22%2C%22pay_type%22%3A%22116%22%2C%22callback_content%22%3A%22%3Cxml%3E%3Cappid%3E%3C%21%5BCDATA%5Bwx9922dfbb0d4cd7bb%5D%5D%3E%3C%2Fappid%3E%5Cn%3Cattach%3E%3C%21%5BCDATA%5B%257B%2522biz_id%2522%253A%2522wxshop%2522%252C%2522trade_id%2522%253A%25221063669415%2522%257D%5D%5D%3E%3C%2Fattach%3E%5Cn%3Cbank_type%3E%3C%21%5BCDATA%5BOTHERS%5D%5D%3E%3C%2Fbank_type%3E%5Cn%3Ccash_fee%3E%3C%21%5BCDATA%5B1%5D%5D%3E%3C%2Fcash_fee%3E%5Cn%3Cfee_type%3E%3C%21%5BCDATA%5BCNY%5D%5D%3E%3C%2Ffee_type%3E%5Cn%3Cis_subscribe%3E%3C%21%5BCDATA%5BN%5D%5D%3E%3C%2Fis_subscribe%3E%5Cn%3Cmch_id%3E%3C%21%5BCDATA%5B1625101806%5D%5D%3E%3C%2Fmch_id%3E%5Cn%3Cnonce_str%3E%3C%21%5BCDATA%5B24NffiTHxNYm0ppw3QE9WezmzJQDnJQV%5D%5D%3E%3C%2Fnonce_str%3E%5Cn%3Copenid%3E%3C%21%5BCDATA%5BoMRxw6Eum0DB1IjI_pEX_yrawBHw%5D%5D%3E%3C%2Fopenid%3E%5Cn%3Cout_trade_no%3E%3C%21%5BCDATA"
|
+ "2448492%22%2C%22trade_id%22%3A%221063669415%22%2C%22total_amount%22%3A1%2C%22extra%22%3A%22%22%2C%22trade_pay_time%22%3A%222025-03-20+17%3A20%3A55%22%2C%22trade_status%22%3A%22SUCCESS%22%2C%22pay_type%22%3A%22116%22%2C%22callback_content%22%3A%22%3Cxml%3E%3Cappid%3E%3C%21%5BCDATA%5Bwx9922dfbb0d4cd7bb%5D%5D%3E%3C%2Fappid%3E%5Cn%3Cattach%3E%3C%21%5BCDATA%5B%257B%2522biz_id%2522%253A%2522wxshop%2522%252C%2522trade_id%2522%253A%25221063669415%2522%257D%5D%5D%3E%3C%2Fattach%3E%5Cn%3Cbank_type%3E%3C%21%5BCDATA%5BOTHERS%5D%5D%3E%3C%2Fbank_type%3E%5Cn%3Ccash_fee%3E%3C%21%5BCDATA%5B1%5D%5D%3E%3C%2Fcash_fee%3E%5Cn%3Cfee_type%3E%3C%21%5BCDATA%5BCNY%5D%5D%3E%3C%2Ffee_type%3E%5Cn%3Cis_subscribe%3E%3C%21%5BCDATA%5BN%5D%5D%3E%3C%2Fis_subscribe%3E%5Cn%3Cmch_id%3E%3C%21%5BCDATA%5B1625101806%5D%5D%3E%3C%2Fmch_id%3E%5Cn%3Cnonce_str%3E%3C%21%5BCDATA%5B24NffiTHxNYm0ppw3QE9WezmzJQDnJQV%5D%5D%3E%3C%2Fnonce_str%3E%5Cn%3Copenid%3E%3C%21%5BCDATA%5BoMRxw6Eum0DB1IjI_pEX_yrawBHw%5D%5D%3E%3C%2Fopenid%3E%5Cn%3Cout_trade_no%3E%3C%21%5BCDATA"
|
||||||
+ "%5Bwxshop-10-1742462448212%5D%5D%3E%3C%2Fout_trade_no%3E%5Cn%3Cresult_code%3E%3C%21%5BCDATA%5BSUCCESS%5D%5D%3E%3C%2Fresult_code%3E%5Cn%3Creturn_code%3E%3C%21%5BCDATA%5BSUCCESS%5D%5D%3E%3C%2Freturn_code%3E%5Cn%3Csign%3E%3C%21%5BCDATA%5B9CE8A123437E591166DDAF92A750C122%5D%5D%3E%3C%2Fsign%3E%5Cn%3Ctime_end%3E%3C%21%5BCDATA%5B20250320172055%5D%5D%3E%3C%2Ftime_end%3E%5Cn%3Ctotal_fee%3E1%3C%2Ftotal_fee%3E%5Cn%3Ctrade_type%3E%3C%21%5BCDATA%5BJSAPI%5D%5D%3E%3C%2Ftrade_type%3E%5Cn%3Ctransaction_id%3E%3C%21%5BCDATA%5B4200002695202503209706830758%5D%5D%3E%3C%2Ftransaction_id%3E%5Cn%3C%2Fxml%3E%22%2C%22title%22%3A%22%E5%95%86%E5%93%81%E8%AE%A2%E5%8D%95%E6%94%AF%E4%BB%98%22%2C%22biz_order_id%22%3A%22wxshop-10-1742462448212%22%7D&nonce_str=6d80bd6b542a499994adf4ee4c1e89c4&sign=f1ffc9cfc61cf60d8a1acbac0399cd0b&biz_id=wxshop&sign_type=MD5&version=1.0×tamp=1742462460390";
|
+ "%5Bwxshop-10-1742462448212%5D%5D%3E%3C%2Fout_trade_no%3E%5Cn%3Cresult_code%3E%3C%21%5BCDATA%5BSUCCESS%5D%5D%3E%3C%2Fresult_code%3E%5Cn%3Creturn_code%3E%3C%21%5BCDATA%5BSUCCESS%5D%5D%3E%3C%2Freturn_code%3E%5Cn%3Csign%3E%3C%21%5BCDATA%5B9CE8A123437E591166DDAF92A750C122%5D%5D%3E%3C%2Fsign%3E%5Cn%3Ctime_end%3E%3C%21%5BCDATA%5B20250320172055%5D%5D%3E%3C%2Ftime_end%3E%5Cn%3Ctotal_fee%3E1%3C%2Ftotal_fee%3E%5Cn%3Ctrade_type%3E%3C%21%5BCDATA%5BJSAPI%5D%5D%3E%3C%2Ftrade_type%3E%5Cn%3Ctransaction_id%3E%3C%21%5BCDATA%5B4200002695202503209706830758%5D%5D%3E%3C%2Ftransaction_id%3E%5Cn%3C%2Fxml%3E%22%2C%22title%22%3A%22%E5%95%86%E5%93%81%E8%AE%A2%E5%8D%95%E6%94%AF%E4%BB%98%22%2C%22biz_order_id%22%3A%22wxshop-10-1742462448212%22%7D&nonce_str=6d80bd6b542a499994adf4ee4c1e89c4&sign=f1ffc9cfc61cf60d8a1acbac0399cd0b&biz_id=wxshop&sign_type=MD5&version=1.0×tamp=1742462460390";
|
||||||
|
|
||||||
Boolean res = checkOpenSign("wxshop202503081132", "f1ffc9cfc61cf60d8a1acbac0399cd0b", body);
|
Boolean res = checkOpenSign(PayApiConstants.appkey, "f1ffc9cfc61cf60d8a1acbac0399cd0b", body);
|
||||||
log.info("res:{}", res);
|
log.info("res:{}", res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,12 @@ import com.agileboot.domain.shop.approval.dto.ReturnApprovalDTO;
|
||||||
import com.agileboot.domain.shop.approval.model.ReturnApprovalModel;
|
import com.agileboot.domain.shop.approval.model.ReturnApprovalModel;
|
||||||
import com.agileboot.domain.shop.approval.model.ReturnApprovalModelFactory;
|
import com.agileboot.domain.shop.approval.model.ReturnApprovalModelFactory;
|
||||||
import com.agileboot.domain.shop.approval.query.SearchReturnApprovalQuery;
|
import com.agileboot.domain.shop.approval.query.SearchReturnApprovalQuery;
|
||||||
|
import com.agileboot.domain.shop.order.db.ShopOrderGoodsEntity;
|
||||||
|
import com.agileboot.domain.shop.order.model.OrderGoodsModel;
|
||||||
|
import com.agileboot.domain.shop.order.model.OrderGoodsModelFactory;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
@ -24,6 +29,7 @@ public class ReturnApprovalApplicationService {
|
||||||
|
|
||||||
private final ReturnApprovalService approvalService;
|
private final ReturnApprovalService approvalService;
|
||||||
private final ReturnApprovalModelFactory modelFactory;
|
private final ReturnApprovalModelFactory modelFactory;
|
||||||
|
private final OrderGoodsModelFactory orderGoodsModelFactory;
|
||||||
|
|
||||||
public PageDTO<ReturnApprovalDTO> getApprovalList(SearchReturnApprovalQuery<ReturnApprovalEntity> query) {
|
public PageDTO<ReturnApprovalDTO> getApprovalList(SearchReturnApprovalQuery<ReturnApprovalEntity> query) {
|
||||||
Page<ReturnApprovalEntity> page = approvalService.getApprovalList(query);
|
Page<ReturnApprovalEntity> page = approvalService.getApprovalList(query);
|
||||||
|
@ -52,4 +58,29 @@ public class ReturnApprovalApplicationService {
|
||||||
model.deleteById();
|
model.deleteById();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ReturnApprovalEntity submitApproval(AddReturnApprovalCommand command, ShopOrderGoodsEntity orderGoods) {
|
||||||
|
// 设置商品价格并初始化审批状态
|
||||||
|
command.setGoodsId(orderGoods.getGoodsId());
|
||||||
|
command.setOrderId(orderGoods.getOrderId());
|
||||||
|
command.setGoodsPrice(orderGoods.getPrice());
|
||||||
|
command.setReturnImages(command.getReturnImages());
|
||||||
|
command.setReturnRemark(command.getReturnRemark());
|
||||||
|
command.setStatus(1);
|
||||||
|
command.setCreatorId(0L);
|
||||||
|
command.setCreateTime(new Date());
|
||||||
|
command.setUpdaterId(0L);
|
||||||
|
command.setUpdateTime(new Date());
|
||||||
|
command.setDeleted(false);
|
||||||
|
|
||||||
|
// 执行业务逻辑
|
||||||
|
ReturnApprovalModel returnApprovalModel = addApproval(command);
|
||||||
|
|
||||||
|
// 更新订单商品状态
|
||||||
|
OrderGoodsModel orderGoodsModel = orderGoodsModelFactory.create(orderGoods);
|
||||||
|
orderGoodsModel.setStatus(5);
|
||||||
|
orderGoodsModel.updateById();
|
||||||
|
|
||||||
|
return returnApprovalModel.selectById();
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -40,6 +40,10 @@ public class ReturnApprovalEntity extends BaseEntity<ReturnApprovalEntity> {
|
||||||
@TableField("goods_id")
|
@TableField("goods_id")
|
||||||
private Long goodsId;
|
private Long goodsId;
|
||||||
|
|
||||||
|
@ApiModelProperty("关联订单商品ID")
|
||||||
|
@TableField("order_goods_id")
|
||||||
|
private Long orderGoodsId;
|
||||||
|
|
||||||
@ApiModelProperty("归还数量")
|
@ApiModelProperty("归还数量")
|
||||||
@TableField("return_quantity")
|
@TableField("return_quantity")
|
||||||
private Integer returnQuantity;
|
private Integer returnQuantity;
|
||||||
|
|
|
@ -17,7 +17,7 @@ import org.apache.ibatis.annotations.Select;
|
||||||
* @since 2025-04-03
|
* @since 2025-04-03
|
||||||
*/
|
*/
|
||||||
public interface ReturnApprovalMapper extends BaseMapper<ReturnApprovalEntity> {
|
public interface ReturnApprovalMapper extends BaseMapper<ReturnApprovalEntity> {
|
||||||
@Select("SELECT approval_id, order_id, goods_id, return_quantity, goods_price, return_amount, return_images, audit_images, return_remark, audit_remark, status " +
|
@Select("SELECT * " +
|
||||||
"FROM return_approval " +
|
"FROM return_approval " +
|
||||||
"${ew.customSqlSegment}")
|
"${ew.customSqlSegment}")
|
||||||
Page<ReturnApprovalEntity> getApprovalList(
|
Page<ReturnApprovalEntity> getApprovalList(
|
||||||
|
|
|
@ -41,6 +41,9 @@ public class ReturnApprovalDTO {
|
||||||
@ExcelColumn(name = "关联商品ID")
|
@ExcelColumn(name = "关联商品ID")
|
||||||
private Long goodsId;
|
private Long goodsId;
|
||||||
|
|
||||||
|
@ExcelColumn(name = "关联订单商品ID")
|
||||||
|
private Long orderGoodsId;
|
||||||
|
|
||||||
@ExcelColumn(name = "归还数量")
|
@ExcelColumn(name = "归还数量")
|
||||||
private Integer returnQuantity;
|
private Integer returnQuantity;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package com.agileboot.domain.shop.order;
|
package com.agileboot.domain.shop.order;
|
||||||
|
|
||||||
import cn.hutool.core.date.DateUtil;
|
import cn.hutool.core.date.DateUtil;
|
||||||
|
import com.agileboot.common.constant.PayApiConstants;
|
||||||
import com.agileboot.common.exception.ApiException;
|
import com.agileboot.common.exception.ApiException;
|
||||||
import com.agileboot.common.exception.error.ErrorCode;
|
import com.agileboot.common.exception.error.ErrorCode;
|
||||||
import com.agileboot.domain.cabinet.cell.db.CabinetCellEntity;
|
import com.agileboot.domain.cabinet.cell.db.CabinetCellEntity;
|
||||||
|
@ -160,7 +161,7 @@ public class OrderApplicationService {
|
||||||
WxJsApiPreCreateRequest request = new WxJsApiPreCreateRequest();
|
WxJsApiPreCreateRequest request = new WxJsApiPreCreateRequest();
|
||||||
request.setIp("222.218.10.217");
|
request.setIp("222.218.10.217");
|
||||||
request.setOpenid(orderModel.getOpenid()); //
|
request.setOpenid(orderModel.getOpenid()); //
|
||||||
request.setBiz_order_id("wxshop-" + orderModel.getOrderId() + "-" + new Date().getTime()); // 使用订单唯一编号
|
request.setBiz_order_id(orderModel.getBizOrderId()); // 使用订单唯一编号
|
||||||
// 金额转换(元转分)并四舍五入
|
// 金额转换(元转分)并四舍五入
|
||||||
BigDecimal amountInFen = orderModel.getTotalAmount()
|
BigDecimal amountInFen = orderModel.getTotalAmount()
|
||||||
.multiply(new BigDecimal("100"))
|
.multiply(new BigDecimal("100"))
|
||||||
|
@ -169,7 +170,7 @@ public class OrderApplicationService {
|
||||||
|
|
||||||
request.setTitle("商品订单支付");
|
request.setTitle("商品订单支付");
|
||||||
request.setNotify_url("http://wxshop.ab98.cn/shop-api/api/payment/callback");
|
request.setNotify_url("http://wxshop.ab98.cn/shop-api/api/payment/callback");
|
||||||
request.setBiz_id("wxshop");
|
request.setBiz_id(PayApiConstants.biz_id);
|
||||||
request.setUcid(orderModel.getUcid());
|
request.setUcid(orderModel.getUcid());
|
||||||
request.setExtra("");
|
request.setExtra("");
|
||||||
return request;
|
return request;
|
||||||
|
@ -203,6 +204,7 @@ public class OrderApplicationService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新订单总金额
|
// 更新订单总金额
|
||||||
|
orderModel.setBizOrderId("wxshop-" + orderModel.getOrderId() + "-" + new Date().getTime());
|
||||||
orderModel.setTotalAmount(totalAmount);
|
orderModel.setTotalAmount(totalAmount);
|
||||||
orderModel.updateById();
|
orderModel.updateById();
|
||||||
}
|
}
|
||||||
|
@ -252,31 +254,7 @@ public class OrderApplicationService {
|
||||||
|
|
||||||
public void handlePaymentSuccess(Long orderId, Integer amount, String tradeId, String tradePayTime) {
|
public void handlePaymentSuccess(Long orderId, Integer amount, String tradeId, String tradePayTime) {
|
||||||
OrderModel orderModel = orderModelFactory.loadById(orderId);
|
OrderModel orderModel = orderModelFactory.loadById(orderId);
|
||||||
// 状态校验
|
orderModel.handlePaymentSuccess(amount, tradeId, tradePayTime);
|
||||||
orderModel.validateStatusTransition(2); // 2代表已支付
|
|
||||||
// 更新订单状态
|
|
||||||
orderModel.setStatus(2);
|
|
||||||
orderModel.setPayStatus(2);
|
|
||||||
orderModel.setTradeId(tradeId);
|
|
||||||
try {
|
|
||||||
// if (tradePayTime.contains("+")) {
|
|
||||||
// tradePayTime = tradePayTime.replace("+", " ");
|
|
||||||
// }
|
|
||||||
orderModel.setPayTime(DateUtil.parse(tradePayTime));
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("支付时间转换失败", e);
|
|
||||||
}
|
|
||||||
orderModel.updateById();
|
|
||||||
|
|
||||||
|
|
||||||
// 发送指令
|
|
||||||
// 客户端手动开柜
|
|
||||||
// QueryWrapper<ShopOrderGoodsEntity> orderGoodsQueryWrapper = new QueryWrapper<>();
|
|
||||||
// orderGoodsQueryWrapper.eq("order_id", orderId);
|
|
||||||
// List<ShopOrderGoodsEntity> orderGoods = orderGoodsService.list(orderGoodsQueryWrapper);
|
|
||||||
// orderGoods.forEach(g -> {
|
|
||||||
// openOrderGoodsCabinet(orderId, g.getOrderGoodsId());
|
|
||||||
// });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
@ -307,4 +285,12 @@ public class OrderApplicationService {
|
||||||
public ShopOrderGoodsEntity getOrderGoodsByOrderIdAndGoodsId(Long orderId, Long goodsId) {
|
public ShopOrderGoodsEntity getOrderGoodsByOrderIdAndGoodsId(Long orderId, Long goodsId) {
|
||||||
return orderGoodsService.getByOrderIdAndGoodsId(orderId, goodsId);
|
return orderGoodsService.getByOrderIdAndGoodsId(orderId, goodsId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ShopOrderGoodsEntity getOrderGoodsById(Long orderGoodsId) {
|
||||||
|
return orderGoodsService.getById(orderGoodsId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OrderModel loadById(Long orderId) {
|
||||||
|
return orderModelFactory.loadById(orderId);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -45,6 +45,10 @@ public class ShopOrderEntity extends BaseEntity<ShopOrderEntity> {
|
||||||
@TableField("trade_id")
|
@TableField("trade_id")
|
||||||
private String tradeId;
|
private String tradeId;
|
||||||
|
|
||||||
|
@ApiModelProperty("业务系统订单ID(对接外部系统)")
|
||||||
|
@TableField("biz_order_id")
|
||||||
|
private String bizOrderId;
|
||||||
|
|
||||||
@ApiModelProperty("订单总金额")
|
@ApiModelProperty("订单总金额")
|
||||||
@TableField("total_amount")
|
@TableField("total_amount")
|
||||||
private BigDecimal totalAmount;
|
private BigDecimal totalAmount;
|
||||||
|
|
|
@ -52,7 +52,7 @@ public class ShopOrderGoodsEntity extends BaseEntity<ShopOrderGoodsEntity> {
|
||||||
@TableField("total_amount")
|
@TableField("total_amount")
|
||||||
private BigDecimal totalAmount;
|
private BigDecimal totalAmount;
|
||||||
|
|
||||||
@ApiModelProperty("商品状态(1正常 2已退货 3已换货 4已完成)")
|
@ApiModelProperty("商品状态(1正常 2已退货 3已换货 4已完成 5审核中 6退货未通过)")
|
||||||
@TableField("`status`")
|
@TableField("`status`")
|
||||||
private Integer status;
|
private Integer status;
|
||||||
|
|
||||||
|
|
|
@ -11,5 +11,4 @@ import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
* @since 2025-03-10
|
* @since 2025-03-10
|
||||||
*/
|
*/
|
||||||
public interface ShopOrderService extends IService<ShopOrderEntity> {
|
public interface ShopOrderService extends IService<ShopOrderEntity> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package com.agileboot.domain.shop.order.db;
|
package com.agileboot.domain.shop.order.db;
|
||||||
|
|
||||||
|
import cn.hutool.core.date.DateUtil;
|
||||||
|
import com.agileboot.domain.shop.order.model.OrderModel;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@ -13,5 +15,4 @@ import org.springframework.stereotype.Service;
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
public class ShopOrderServiceImpl extends ServiceImpl<ShopOrderMapper, ShopOrderEntity> implements ShopOrderService {
|
public class ShopOrderServiceImpl extends ServiceImpl<ShopOrderMapper, ShopOrderEntity> implements ShopOrderService {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package com.agileboot.domain.shop.order.model;
|
package com.agileboot.domain.shop.order.model;
|
||||||
|
|
||||||
import cn.hutool.core.bean.BeanUtil;
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
|
import cn.hutool.core.date.DateUtil;
|
||||||
import com.agileboot.common.config.AgileBootConfig;
|
import com.agileboot.common.config.AgileBootConfig;
|
||||||
import com.agileboot.common.exception.ApiException;
|
import com.agileboot.common.exception.ApiException;
|
||||||
import com.agileboot.common.exception.error.ErrorCode;
|
import com.agileboot.common.exception.error.ErrorCode;
|
||||||
|
@ -11,7 +12,9 @@ import java.math.BigDecimal;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
@Data
|
@Data
|
||||||
public class OrderModel extends ShopOrderEntity {
|
public class OrderModel extends ShopOrderEntity {
|
||||||
|
@ -68,4 +71,25 @@ public class OrderModel extends ShopOrderEntity {
|
||||||
String orderNo = String.valueOf(System.currentTimeMillis() + (int)(Math.random()*1000));
|
String orderNo = String.valueOf(System.currentTimeMillis() + (int)(Math.random()*1000));
|
||||||
this.setUcid(orderNo);
|
this.setUcid(orderNo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void handlePaymentSuccess(Integer amount, String tradeId, String tradePayTime) {
|
||||||
|
// 状态校验
|
||||||
|
this.validateStatusTransition(2); // 2代表已支付
|
||||||
|
// 更新订单状态
|
||||||
|
this.setStatus(2);
|
||||||
|
this.setPayStatus(2);
|
||||||
|
this.setTradeId(tradeId);
|
||||||
|
try {
|
||||||
|
this.setPayTime(DateUtil.parse(tradePayTime));
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("支付时间转换失败", e);
|
||||||
|
}
|
||||||
|
this.updateById();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleRefundSuccess() {
|
||||||
|
// 更新订单状态
|
||||||
|
this.setPayStatus(4);
|
||||||
|
this.updateById();
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,22 +1,38 @@
|
||||||
package com.agileboot.domain.shop.payment;
|
package com.agileboot.domain.shop.payment;
|
||||||
|
|
||||||
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
|
import cn.hutool.core.lang.TypeReference;
|
||||||
|
import cn.hutool.core.net.URLDecoder;
|
||||||
|
import cn.hutool.core.util.URLUtil;
|
||||||
import cn.hutool.http.ContentType;
|
import cn.hutool.http.ContentType;
|
||||||
import cn.hutool.http.HttpResponse;
|
import cn.hutool.http.HttpResponse;
|
||||||
import cn.hutool.http.HttpStatus;
|
import cn.hutool.http.HttpStatus;
|
||||||
import cn.hutool.http.HttpUtil;
|
import cn.hutool.http.HttpUtil;
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
import cn.hutool.json.JSONUtil;
|
import cn.hutool.json.JSONUtil;
|
||||||
import com.agileboot.domain.shop.payment.dto.WxJsApiPreCreateRequest;
|
import com.agileboot.common.constant.PayApiConstants;
|
||||||
import com.agileboot.domain.shop.payment.dto.WxJsApiPreCreateResponse;
|
import com.agileboot.common.utils.OpenSignUtil;
|
||||||
|
import com.agileboot.domain.shop.order.model.OrderModel;
|
||||||
|
import com.agileboot.domain.shop.order.model.OrderModelFactory;
|
||||||
|
import com.agileboot.domain.shop.payment.dto.*;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import lombok.Data;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class PaymentApplicationService {
|
public class PaymentApplicationService {
|
||||||
|
private final OrderModelFactory orderModelFactory;
|
||||||
|
private static final Object LOCKER = new Object();
|
||||||
|
|
||||||
public WxJsApiPreCreateResponse callJsApiPreCreate(WxJsApiPreCreateRequest request) {
|
public WxJsApiPreCreateResponse callJsApiPreCreate(WxJsApiPreCreateRequest request) {
|
||||||
String gatewayUrl = "http://222.218.10.217:7890/open/trade/wx/jsapi/precreate";
|
String gatewayUrl = "http://222.218.10.217:7890/open/trade/wx/jsapi/precreate";
|
||||||
|
|
||||||
|
@ -61,13 +77,157 @@ public class PaymentApplicationService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加错误响应处理类
|
public void paymentCallback(String requestBody) {
|
||||||
@Data
|
|
||||||
private static class PaymentGatewayError {
|
// 1. 参数解析
|
||||||
private String timestamp;
|
PaymentCallbackRequest callbackReq = parseCallbackRequest(requestBody);
|
||||||
private Integer status;
|
PaymentCallbackRequest.BizContent bizContent = parseBizContent(callbackReq.getBiz_content());
|
||||||
private String error;
|
|
||||||
private String message;
|
// 2. 签名验证(需要根据biz_id获取对应的appKey)
|
||||||
private String path;
|
String appKey = getAppKeyByBizId(callbackReq.getBiz_id()); // 需要实现根据biz_id获取appKey的逻辑
|
||||||
|
boolean signValid = OpenSignUtil.checkOpenSign(appKey, callbackReq.getSign(), requestBody);
|
||||||
|
|
||||||
|
if (!signValid) {
|
||||||
|
log.error("支付回调签名验证失败:{}", requestBody);
|
||||||
|
throw new RuntimeException("支付回调签名验证失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bizContent.getTrade_status().equals("SUCCESS")) {
|
||||||
|
// 3. 业务处理(需要实现幂等性校验)
|
||||||
|
handlePaymentSuccess(
|
||||||
|
bizContent.getBiz_order_id(),
|
||||||
|
bizContent.getTotal_amount(),
|
||||||
|
bizContent.getTrade_id(),
|
||||||
|
bizContent.getTrade_pay_time()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
log.error("支付订单失败requestBody:{}", requestBody);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退款
|
||||||
|
* @param bizId 支付网关分配的业务应用ID
|
||||||
|
* @param appKey 支付网关分配的业务应用秘钥
|
||||||
|
* @param orderId 支付时的订单ID
|
||||||
|
* @param uid 用户ID
|
||||||
|
* @param reason 退款理由
|
||||||
|
* @param money 退款金额,单位:分
|
||||||
|
*/
|
||||||
|
public RefundVO refund(String bizId, String appKey, String orderId, String uid, String reason, int money) throws Exception {
|
||||||
|
String url = "http://222.218.10.217:7890/open/trade/refund";
|
||||||
|
JSONObject bizContent = new JSONObject();
|
||||||
|
bizContent.set("userId", uid);
|
||||||
|
bizContent.set("bizId", bizId);
|
||||||
|
bizContent.set("orderId", orderId);
|
||||||
|
bizContent.set("refundFee", money);
|
||||||
|
bizContent.set("refundReason", reason);
|
||||||
|
Map<String, String> params = new HashMap<>();
|
||||||
|
params.put("client_id", UUID.randomUUID().toString());
|
||||||
|
params.put("biz_id", bizId);
|
||||||
|
params.put("sign_type", "md5");
|
||||||
|
params.put("timestamp", String.valueOf(System.currentTimeMillis()));
|
||||||
|
params.put("version", "1.0");
|
||||||
|
params.put("nonce_str", UUID.randomUUID().toString());
|
||||||
|
params.put("biz_content", URLUtil.encode(JSONUtil.toJsonStr(bizContent)));
|
||||||
|
params.put("sign", SignUtils.openSign(appKey, params));
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (String key : params.keySet()) {
|
||||||
|
if (sb.length() > 0) {
|
||||||
|
sb.append("&");
|
||||||
|
}
|
||||||
|
sb.append(key).append("=").append(params.get(key));
|
||||||
|
}
|
||||||
|
String result = HttpUtil.post(url, sb.toString());
|
||||||
|
CommonResponse<RefundVO> res = JSONUtil.toBean(result, new TypeReference<CommonResponse<RefundVO>>(){}, true);
|
||||||
|
if (res.getCode() != CommonErrorCode.OK.getErrorCode()) {
|
||||||
|
throw new Exception(res.getMsg());
|
||||||
|
}
|
||||||
|
return res.getData();
|
||||||
|
}
|
||||||
|
|
||||||
|
private PaymentCallbackRequest parseCallbackRequest(String requestBody) {
|
||||||
|
// 实现将URL参数解析为PaymentCallbackRequest
|
||||||
|
// 示例实现(需要根据实际参数格式调整):
|
||||||
|
Map<String, String> paramMap = new HashMap<>();
|
||||||
|
for (String param : requestBody.split("&")) {
|
||||||
|
String[] pair = param.split("=");
|
||||||
|
if (pair.length == 2) {
|
||||||
|
paramMap.put(pair[0], URLDecoder.decode(pair[1], StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return BeanUtil.toBean(paramMap, PaymentCallbackRequest.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PaymentCallbackRequest.BizContent parseBizContent(String bizContent) {
|
||||||
|
// 实现biz_content的JSON解析
|
||||||
|
return JSONUtil.toBean(bizContent, PaymentCallbackRequest.BizContent.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 需要实现的方法(根据业务需求补充)
|
||||||
|
private String getAppKeyByBizId(String bizId) {
|
||||||
|
// 根据biz_id从数据库或配置获取对应的appKey
|
||||||
|
return PayApiConstants.appkey;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handlePaymentSuccess(String bizOrderId, Integer amount, String tradeId, String tradePayTime) {
|
||||||
|
// 实现订单状态更新和幂等性校验
|
||||||
|
if (StringUtils.isNotBlank(bizOrderId) && bizOrderId.startsWith("wxshop-")) {
|
||||||
|
// 订单号格式为 wxshop-1-time,提取中间的订单号
|
||||||
|
String orderId = bizOrderId.split("-")[1];
|
||||||
|
OrderModel orderModel = orderModelFactory.loadById(Long.valueOf(orderId));
|
||||||
|
orderModel.handlePaymentSuccess(amount, tradeId, tradePayTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String refundCallback(String reqBody) {
|
||||||
|
JSONObject res = new JSONObject();
|
||||||
|
System.out.println("退款回调:" + reqBody);
|
||||||
|
CommonRequest<RefundVO> notifyRequest = CommonRequest.build(reqBody, RefundVO.class);
|
||||||
|
if (notifyRequest == null || notifyRequest.getBizContent() == null) {
|
||||||
|
res.set("callback_code", 1);
|
||||||
|
res.set("callback_msg", "请求body或bizcontent为空");
|
||||||
|
System.out.println("退款回调处理失败, 原因:请求body或bizcontent为空");
|
||||||
|
return JSONUtil.toJsonStr(res);
|
||||||
|
}
|
||||||
|
MyError error = checkSign(notifyRequest.getBizId(), notifyRequest.getSign(), reqBody);
|
||||||
|
if (error != null) {
|
||||||
|
res.set("callback_code", 1);
|
||||||
|
res.set("callback_msg", error.getCode());
|
||||||
|
System.out.printf("退款回调处理失败, 原因:%s%n", error.getMsg());
|
||||||
|
return JSONUtil.toJsonStr(res);
|
||||||
|
}
|
||||||
|
RefundVO bizContent = notifyRequest.getBizContent();
|
||||||
|
|
||||||
|
String orderNO = bizContent.getBizOrderId();
|
||||||
|
if (StringUtils.isBlank(orderNO)) {
|
||||||
|
res.put("callback_code", 1);
|
||||||
|
res.put("callback_msg", "缺少退款订单ID");
|
||||||
|
return JSONUtil.toJsonStr(res);
|
||||||
|
}
|
||||||
|
synchronized (LOCKER) {
|
||||||
|
//根据退款订单号 orderNo 做你的业务处理。退款回调可能会
|
||||||
|
//有多次,业务应用要做好幂等处理
|
||||||
|
if (orderNO.startsWith("wxshop-")) {
|
||||||
|
// 订单号格式为 wxshop-1-time,提取中间的订单号
|
||||||
|
String orderId = orderNO.split("-")[1];
|
||||||
|
OrderModel orderModel = orderModelFactory.loadById(Long.valueOf(orderId));
|
||||||
|
orderModel.handleRefundSuccess();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.set("callback_code", 0);
|
||||||
|
res.set("callback_msg", "ok");
|
||||||
|
return JSONUtil.toJsonStr(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MyError checkSign(String bizId, String sign, String reqBody) {
|
||||||
|
if (StringUtils.isBlank(bizId)) {
|
||||||
|
return new MyError(CommonErrorCode.BAD_PARAMETER, "bizId is blank");
|
||||||
|
}
|
||||||
|
if (!SignUtils.checkOpenSign(getAppKeyByBizId(bizId), sign, reqBody)) {
|
||||||
|
return new MyError(CommonErrorCode.BAD_PARAMETER, String.format("Invalid sign: %s", sign));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,122 @@
|
||||||
|
package com.agileboot.domain.shop.payment;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.collections4.MapUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public final class SignUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 珊瑚签名计算
|
||||||
|
*/
|
||||||
|
public static String coralSign(String secret, Map<String, Object> params) {
|
||||||
|
return commonSign(secret, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务方签名计算
|
||||||
|
*/
|
||||||
|
public static String bizSign(String secret, Map<String, Object> params) {
|
||||||
|
return commonSign(secret, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认签到计算:params根据key进行字典排序,然后将对应值直接拼成字符串(null跳过),再将业务预生成的"secret"拼在最后,用MD5算法生成签名
|
||||||
|
* @param secret appKey/appSecret
|
||||||
|
* @param params 需要签名的参数
|
||||||
|
* @return 签名
|
||||||
|
*/
|
||||||
|
public static String commonSign(String secret, Map<String, Object> params) {
|
||||||
|
Map<String, Object> sortedParams = new TreeMap<>(params);
|
||||||
|
StringBuilder plainText = new StringBuilder();
|
||||||
|
sortedParams.values().stream().filter(Objects::nonNull).forEach(plainText::append);
|
||||||
|
plainText.append(secret);
|
||||||
|
try {
|
||||||
|
return md5(plainText.toString().getBytes("utf-8"));
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务端接口加签校验,按照Key升序排列,拼接key1=value1&key2=value2(value为null跳过, value需要url encode),在后面拼接上&app_key=xxx,md5生成sign值
|
||||||
|
* @param appKey
|
||||||
|
* @param params
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String openSign(String appKey, Map<String, String> params) {
|
||||||
|
if(MapUtils.isEmpty(params)) {
|
||||||
|
params = new HashMap<String, String>(1);
|
||||||
|
}
|
||||||
|
Map<String, String> sortedParams = new TreeMap<>(params);
|
||||||
|
List<String> kvPairList = getKVList(sortedParams);
|
||||||
|
String sourceText = StringUtils.join(kvPairList, "&");
|
||||||
|
sourceText = sourceText + "&app_key=" + appKey;
|
||||||
|
log.info("sourceText:" + sourceText);
|
||||||
|
try {
|
||||||
|
return md5(sourceText.getBytes("utf-8"));
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean checkOpenSign(String appKey, String sign, String reqBody) {
|
||||||
|
String[] fields = reqBody.split("&");
|
||||||
|
Map<String, String> fieldMap = new HashMap<>();
|
||||||
|
for (String field : fields) {
|
||||||
|
String[] pair = field.split("=");
|
||||||
|
if (pair.length != 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// sign字段不参与校验,空字段不校验
|
||||||
|
if (!"sign".equals(pair[0]) && StringUtils.isNotBlank(pair[1])) {
|
||||||
|
fieldMap.put(pair[0], pair[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String generateSign = openSign(appKey, fieldMap);
|
||||||
|
return Objects.equals(generateSign, sign);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> getKVList(Map<String, String> params) {
|
||||||
|
if(MapUtils.isEmpty(params)) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
List<String> kvPairs = new ArrayList<>();
|
||||||
|
for(String key : params.keySet()) {
|
||||||
|
String value = params.get(key);
|
||||||
|
if(value == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String kv = key + "=" + value;
|
||||||
|
kvPairs.add(kv);
|
||||||
|
}
|
||||||
|
return kvPairs;
|
||||||
|
}
|
||||||
|
private SignUtils() {}
|
||||||
|
|
||||||
|
private static String md5(byte[] textBytes) {
|
||||||
|
try {
|
||||||
|
if (textBytes == null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||||
|
byte[] messageDigest = md.digest(textBytes);
|
||||||
|
BigInteger number = new BigInteger(1, messageDigest);
|
||||||
|
|
||||||
|
String hashtext;
|
||||||
|
for(hashtext = number.toString(16); hashtext.length() < 32; hashtext = "0" + hashtext) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hashtext;
|
||||||
|
}
|
||||||
|
} catch (Exception var5) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
package com.agileboot.domain.shop.payment.dto;
|
||||||
|
|
||||||
|
public class CommonErrorCode {
|
||||||
|
// 成功
|
||||||
|
public static final CommonErrorCode OK = new CommonErrorCode(2000000, "OK");
|
||||||
|
// 400
|
||||||
|
public static final CommonErrorCode BAD_PARAMETER = new CommonErrorCode(4000001, "bad parameter: {0}");
|
||||||
|
public static final CommonErrorCode INVALID_REQUEST_BODY = new CommonErrorCode(4000002, "bad reqeust body");
|
||||||
|
// 403
|
||||||
|
public static final CommonErrorCode USER_NOT_LOGIN = new CommonErrorCode(4030001, "user not login");
|
||||||
|
public static final CommonErrorCode INVALID_REQUEST = new CommonErrorCode(4030003, "invalid request");
|
||||||
|
// 404
|
||||||
|
public static final CommonErrorCode REQUEST_NOT_FOUND = new CommonErrorCode(4040001, "request not found, {0}");
|
||||||
|
// 500
|
||||||
|
public static final CommonErrorCode INTERNAL_SERVER_ERROR = new CommonErrorCode(5000000, "internal server error, {0}");
|
||||||
|
public static final CommonErrorCode TAIR_SERVER_ERROR = new CommonErrorCode(5000003, "internal server error");
|
||||||
|
|
||||||
|
private int errorCode;
|
||||||
|
private String messagePattern;
|
||||||
|
|
||||||
|
protected CommonErrorCode() {}
|
||||||
|
|
||||||
|
protected CommonErrorCode(int errorCode, String messagePattern) {
|
||||||
|
this.errorCode = errorCode;
|
||||||
|
this.messagePattern = messagePattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getErrorCode() {
|
||||||
|
return errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessagePattern() {
|
||||||
|
return messagePattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void throwMyError(Object... msgArgs) {
|
||||||
|
throw new MyError(this, msgArgs);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,155 @@
|
||||||
|
package com.agileboot.domain.shop.payment.dto;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.URLUtil;
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.commons.lang3.math.NumberUtils;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class CommonRequest<T> {
|
||||||
|
private String clientId;
|
||||||
|
|
||||||
|
private String bizId;
|
||||||
|
|
||||||
|
private String sign;
|
||||||
|
|
||||||
|
private String signType;
|
||||||
|
|
||||||
|
private Long timestamp;
|
||||||
|
|
||||||
|
private String version;
|
||||||
|
|
||||||
|
private String nonceStr;
|
||||||
|
|
||||||
|
private T bizContent;
|
||||||
|
|
||||||
|
public CommonRequest() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommonRequest(String clientId, String bizId, String nonceStr, T bizContent) {
|
||||||
|
super();
|
||||||
|
this.clientId = clientId;
|
||||||
|
this.bizId = bizId;
|
||||||
|
this.signType = "MD5";
|
||||||
|
this.timestamp = Instant.now().toEpochMilli();
|
||||||
|
this.version = "1.0";
|
||||||
|
this.nonceStr = nonceStr;
|
||||||
|
this.bizContent = bizContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, String> getSSMapFromKvPairs(String reqBody) {
|
||||||
|
String[] fields = reqBody.split("&");
|
||||||
|
Map<String, String> fieldMap = new HashMap<>();
|
||||||
|
for (String field : fields) {
|
||||||
|
String[] pair = field.split("=");
|
||||||
|
if (pair.length != 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldMap.put(pair[0], pair[1]);
|
||||||
|
}
|
||||||
|
return fieldMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> CommonRequest<T> build(String reqBody, Class<T> clazz) {
|
||||||
|
if (StringUtils.isBlank(reqBody)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: 防止恶意传入&
|
||||||
|
Map<String, String> fieldMap = getSSMapFromKvPairs(reqBody);
|
||||||
|
|
||||||
|
CommonRequest<T> req = new CommonRequest<T>();
|
||||||
|
Optional.ofNullable(fieldMap.get("client_id")).ifPresent(e -> req.setClientId(e));
|
||||||
|
Optional.ofNullable(fieldMap.get("biz_id")).ifPresent(e -> req.setBizId(e));
|
||||||
|
Optional.ofNullable(fieldMap.get("sign")).ifPresent(e -> req.setSign(e));
|
||||||
|
Optional.ofNullable(fieldMap.get("sign_type")).ifPresent(e -> req.setSignType(e));
|
||||||
|
Optional.ofNullable(fieldMap.get("timestamp")).ifPresent(e -> {
|
||||||
|
if (NumberUtils.isNumber(e)) {
|
||||||
|
req.setTimestamp(Long.valueOf(e));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Optional.ofNullable(fieldMap.get("version")).ifPresent(e -> req.setVersion(e));
|
||||||
|
Optional.ofNullable(fieldMap.get("nonce_str")).ifPresent(e -> {
|
||||||
|
req.setNonceStr(URLUtil.decode(e));
|
||||||
|
});
|
||||||
|
Optional.ofNullable(fieldMap.get("biz_content")).ifPresent(e -> {
|
||||||
|
req.setBizContent(JSONUtil.toBean(URLUtil.decode(e), clazz));
|
||||||
|
});
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getClientId() {
|
||||||
|
return clientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClientId(String clientId) {
|
||||||
|
this.clientId = clientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBizId() {
|
||||||
|
return bizId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBizId(String bizId) {
|
||||||
|
this.bizId = bizId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSign() {
|
||||||
|
return sign;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSign(String sign) {
|
||||||
|
this.sign = sign;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSignType() {
|
||||||
|
return signType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSignType(String signType) {
|
||||||
|
this.signType = signType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getTimestamp() {
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimestamp(Long timestamp) {
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVersion(String version) {
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNonceStr() {
|
||||||
|
return nonceStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNonceStr(String nonceStr) {
|
||||||
|
this.nonceStr = nonceStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T getBizContent() {
|
||||||
|
return bizContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBizContent(T bizContent) {
|
||||||
|
this.bizContent = bizContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return JSONUtil.toJsonStr(this);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
package com.agileboot.domain.shop.payment.dto;
|
||||||
|
|
||||||
|
public class CommonResponse<T> {
|
||||||
|
private Integer code;
|
||||||
|
|
||||||
|
private String msg;
|
||||||
|
|
||||||
|
private T data;
|
||||||
|
|
||||||
|
public CommonResponse() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommonResponse(Integer code, String msg, T data) {
|
||||||
|
this.code = code;
|
||||||
|
this.msg = msg;
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommonResponse(Integer code, String msg) {
|
||||||
|
this.code = code;
|
||||||
|
this.msg = msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> CommonResponse<T> resultOk(T data) {
|
||||||
|
CommonResponse response = new CommonResponse(CommonErrorCode.OK.getErrorCode(), CommonErrorCode.OK.getMessagePattern(), data);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> CommonResponse<T> resultError(Integer errorCode,String errorMsg) {
|
||||||
|
CommonResponse response = new CommonResponse(errorCode, errorMsg);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCode(Integer code) {
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMsg() {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMsg(String msg) {
|
||||||
|
this.msg = msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setData(T data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
package com.agileboot.domain.shop.payment.dto;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
|
||||||
|
public class MyError extends RuntimeException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private String msg;
|
||||||
|
|
||||||
|
private int code;
|
||||||
|
|
||||||
|
public MyError() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public MyError(int code, String msg) {
|
||||||
|
super(msg, null, true, true);
|
||||||
|
this.code = code;
|
||||||
|
this.msg = msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MyError(CommonErrorCode errorCode, Object... msgArgs) {
|
||||||
|
this(errorCode.getErrorCode(), MessageFormat.format(errorCode.getMessagePattern(), msgArgs));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void invalidUrl() {
|
||||||
|
throw new MyError(CommonErrorCode.INVALID_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void badParameter(String msg) {
|
||||||
|
throw new MyError(CommonErrorCode.BAD_PARAMETER, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void internalError(String msg) {
|
||||||
|
throw new MyError(CommonErrorCode.INTERNAL_SERVER_ERROR, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void userNotLogin() {
|
||||||
|
throw new MyError(CommonErrorCode.USER_NOT_LOGIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void throwError(CommonErrorCode errorCode, Object ...args) {
|
||||||
|
throw new MyError(errorCode, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMsg() {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMsg(String msg) {
|
||||||
|
this.msg = msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCode(int code) {
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return JSONUtil.toJsonStr(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package com.agileboot.domain.shop.payment.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class PaymentGatewayError {
|
||||||
|
private String timestamp;
|
||||||
|
private Integer status;
|
||||||
|
private String error;
|
||||||
|
private String message;
|
||||||
|
private String path;
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package com.agileboot.domain.shop.payment.dto;
|
||||||
|
|
||||||
|
public class RefundVO {
|
||||||
|
private Boolean success;
|
||||||
|
private String msg;
|
||||||
|
private String refundId;
|
||||||
|
private String bizOrderId;
|
||||||
|
private String wxCallbackJson;
|
||||||
|
|
||||||
|
public Boolean getSuccess() {
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSuccess(Boolean success) {
|
||||||
|
this.success = success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMsg() {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMsg(String msg) {
|
||||||
|
this.msg = msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRefundId() {
|
||||||
|
return refundId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRefundId(String refundId) {
|
||||||
|
this.refundId = refundId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBizOrderId() {
|
||||||
|
return bizOrderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBizOrderId(String bizOrderId) {
|
||||||
|
this.bizOrderId = bizOrderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getWxCallbackJson() {
|
||||||
|
return wxCallbackJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWxCallbackJson(String wxCallbackJson) {
|
||||||
|
this.wxCallbackJson = wxCallbackJson;
|
||||||
|
}
|
||||||
|
}
|
|
@ -41,3 +41,12 @@ CREATE TABLE `wx_user_info` (
|
||||||
PRIMARY KEY (`wx_user_id`),
|
PRIMARY KEY (`wx_user_id`),
|
||||||
UNIQUE KEY `uk_openid` (`openid`)
|
UNIQUE KEY `uk_openid` (`openid`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='微信用户基本信息表';
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='微信用户基本信息表';
|
||||||
|
|
||||||
|
-- 在已有表结构下方添加
|
||||||
|
ALTER TABLE `return_approval`
|
||||||
|
ADD COLUMN `order_goods_id` BIGINT NOT NULL COMMENT '关联订单商品ID' AFTER `goods_id`,
|
||||||
|
ADD KEY `idx_order_goods` (`order_goods_id`),
|
||||||
|
ADD CONSTRAINT `fk_return_order_goods` FOREIGN KEY (`order_goods_id`) REFERENCES `shop_order_goods` (`order_goods_id`);
|
||||||
|
|
||||||
|
ALTER TABLE `shop_order`
|
||||||
|
ADD COLUMN `biz_order_id` VARCHAR(32) NULL COMMENT '业务系统订单ID(对接外部系统)' AFTER `trade_id`;
|
Loading…
Reference in New Issue