diff --git a/src/main/java/com/ycwl/basic/config/WechatConfig.java b/src/main/java/com/ycwl/basic/config/WechatConfig.java index 1adee77..ec096ea 100644 --- a/src/main/java/com/ycwl/basic/config/WechatConfig.java +++ b/src/main/java/com/ycwl/basic/config/WechatConfig.java @@ -57,9 +57,13 @@ public class WechatConfig { private String mchSerialNo; /** - * 回调接口地址 + * 支付回调接口地址 */ - private String notifyUrl; + private String payNotifyUrl; + /** + * 退款回调接口地址 + */ + private String refundNotifyUrl; /** * 商户API私钥路径 diff --git a/src/main/java/com/ycwl/basic/constant/HttpConstant.java b/src/main/java/com/ycwl/basic/constant/HttpConstant.java new file mode 100644 index 0000000..a9b049f --- /dev/null +++ b/src/main/java/com/ycwl/basic/constant/HttpConstant.java @@ -0,0 +1,18 @@ +package com.ycwl.basic.constant; + +/** + *

HTTP常量类

+ * + * @author songmingsong + * @Description HTTP常量类 + */ +public interface HttpConstant { + String Authorization = "Authorization"; + String Accept = "Accept"; + String Content_Type = "Content-Type"; + String Application_Json = "application/json"; + String SUCCESS = "SUCCESS"; + String Message = "message"; + String AES = "AES"; + String AES_GCM_NoPadding = "AES/GCM/NoPadding"; +} diff --git a/src/main/java/com/ycwl/basic/constant/NumberConstant.java b/src/main/java/com/ycwl/basic/constant/NumberConstant.java index a4825d3..56d17ed 100644 --- a/src/main/java/com/ycwl/basic/constant/NumberConstant.java +++ b/src/main/java/com/ycwl/basic/constant/NumberConstant.java @@ -54,6 +54,7 @@ public interface NumberConstant { int SIXTY = 60; int HUNDRED = 100; + int ONE_HUNDRED_TWENTY_EIGHT = 128; int THOUSAND = 1000; diff --git a/src/main/java/com/ycwl/basic/constant/WeiXinConstant.java b/src/main/java/com/ycwl/basic/constant/WeiXinConstant.java index 49a92d4..3748885 100644 --- a/src/main/java/com/ycwl/basic/constant/WeiXinConstant.java +++ b/src/main/java/com/ycwl/basic/constant/WeiXinConstant.java @@ -48,7 +48,7 @@ public class WeiXinConstant { * 公众号模板地址 */ public static final String PUBLIC_ACCOUNT_TEMPLATE = - "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token="; + "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token="; /** * 获取微信用户基本信息地址 */ @@ -78,5 +78,42 @@ public class WeiXinConstant { */ public static final String GENERATE_URL_LINK = "https://api.weixin.qq.com/wxa/generate_urllink?access_token=%s"; + /*-----------------------------*/ + /* */ + /* 支付相关 */ + /* */ + /*-----------------------------*/ + /** + * 退款链接 + */ + public static final String REFUNDS_URL = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds"; + public static final String REFUNDS_URi = "/v3/refund/domestic/refunds/"; + /** + * 其他 + */ + public static final String WECHATPAY_STATUS = "status"; + public static final String WECHATPAY_SUCCESS = "SUCCESS"; + public static final String WECHATPAY_PROCESSING = "PROCESSING"; + public static final String WECHATPAY_OUT_TRADE_NO = "out_trade_no"; + public static final String WECHATPAY_OUT_REFUND_NO = "out_refund_no"; + public static final String WECHATPAY_REFUND = "refund"; + public static final String WECHATPAY_TOTAL = "total"; + public static final String WECHATPAY_CURRENCY = "currency"; + public static final String WECHATPAY_CURRENCY_CNY = "CNY"; + public static final String WECHATPAY_AMOUNT = "amount"; + public static final String WECHATPAY_NOTIFY_URL = "notify_url"; + + public static final String REFUNDS_RESOURCE = "resource"; + public static final String REFUNDS_CIPHERTEXT = "ciphertext"; + public static final String REFUNDS_NONCE = "nonce"; + public static final String REFUNDS_REFUND_STATUS = "refund_status"; + public static final String REFUNDS_ASSOCIATED_DATA = "associated_data"; + /** + * 退款的token的SCHEMA + */ + public static final String REFUNDS_SCHEMA = "Wechatpay-Signature-Type "; // 注意有一个空格 + /** + * 支付请求头 + */ public static final String WECHATPAY_SIGNATURE_TYPE = "Wechatpay-Signature-Type"; } diff --git a/src/main/java/com/ycwl/basic/controller/mobile/AppWxPayController.java b/src/main/java/com/ycwl/basic/controller/mobile/AppWxPayController.java index e9d6d50..0bd7ad0 100644 --- a/src/main/java/com/ycwl/basic/controller/mobile/AppWxPayController.java +++ b/src/main/java/com/ycwl/basic/controller/mobile/AppWxPayController.java @@ -10,13 +10,14 @@ import com.ycwl.basic.utils.ApiResponse; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.security.GeneralSecurityException; /** * @Author: songmingsong @@ -34,7 +35,8 @@ public class AppWxPayController { @ApiOperation(value = "微信预支付", notes = "微信预支付") @PostMapping("/createOrder") - public ApiResponse createOrder(@RequestBody @Validated WXPayOrderReqVO req) throws Exception { + @IgnoreToken + public ApiResponse createOrder(@RequestBody WXPayOrderReqVO req) throws Exception { return ApiResponse.success(wxPayService.createOrder(req)); } @@ -45,4 +47,24 @@ public class AppWxPayController { wxPayService.payNotify(request); return ApiResponse.success(BizCodeEnum.REQUEST_OK); } + + @ApiOperation(value = "微信退款", notes = "微信退款") + @PostMapping("/refundOrder") + @IgnoreToken + public ApiResponse refundOrder(@RequestBody String orderId) throws Exception { + return ApiResponse.buildResult(wxPayService.refundOrder(orderId) ? + BizCodeEnum.SUCCESS : + BizCodeEnum.ADVANCE_PAYMENT_REFUND_FAILED); + } + + @ApiOperation(value = "微信支付回调", notes = "微信支付回调") + @PostMapping("/refundNotify") + @IgnoreToken + public ApiResponse refundNotify(@RequestBody String refundResult) throws GeneralSecurityException, IOException { + return ApiResponse.buildResult(wxPayService.refundNotify(refundResult) ? + BizCodeEnum.SUCCESS : + BizCodeEnum.ADVANCE_PAYMENT_CALLBACK_REFUND_FAILED); + } + + } diff --git a/src/main/java/com/ycwl/basic/enums/BizCodeEnum.java b/src/main/java/com/ycwl/basic/enums/BizCodeEnum.java index 0c303d7..c6401f2 100644 --- a/src/main/java/com/ycwl/basic/enums/BizCodeEnum.java +++ b/src/main/java/com/ycwl/basic/enums/BizCodeEnum.java @@ -423,6 +423,8 @@ public enum BizCodeEnum { /* -------------------------------------*/ ADVANCE_PAYMENT_FAILED(2001, "预支付失败"), ADVANCE_PAYMENT_CALLBACK_FAILED(2002, "预支付回调失败"), + ADVANCE_PAYMENT_REFUND_FAILED(2003, "退款失败"), + ADVANCE_PAYMENT_CALLBACK_REFUND_FAILED(2004, "退款回调失败"), ; diff --git a/src/main/java/com/ycwl/basic/service/HttpService.java b/src/main/java/com/ycwl/basic/service/HttpService.java index 7f5da2d..2598c26 100644 --- a/src/main/java/com/ycwl/basic/service/HttpService.java +++ b/src/main/java/com/ycwl/basic/service/HttpService.java @@ -11,6 +11,7 @@ import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; @@ -99,6 +100,7 @@ public class HttpService { HttpGet get = new HttpGet(requestUrl); // 请求首部--可选的,User-Agent对于一些服务器必选,不加可能不会返回正确结果 get.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64;rv:39.0) Gecko/20100101 Firefox/39.0"); + get.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64;rv:39.0) Gecko/20100101 Firefox/39.0"); // Exec Request CloseableHttpResponse resp = httpClient.execute(get); // 获得起始行 @@ -118,4 +120,49 @@ public class HttpService { } return result; } + + public String doPost(String requestUrl, String jsonData, Map headers, String encoding) throws Exception { + String result = HttpServiceUtil.REQUEST_NO_RESULT; // 默认返回值 + CloseableHttpClient httpClient = null; + try { + // 使用自定义 SSL 工具类创建支持 HTTPS 的 HttpClient + httpClient = SslUtil.sslHttpClientBuild(); + + // 清理 URL 中的空格字符 + requestUrl = requestUrl.replaceAll("\\s*", ""); + HttpPost post = new HttpPost(requestUrl); + + // 设置请求头 + if (headers != null && !headers.isEmpty()) { + for (Map.Entry header : headers.entrySet()) { + post.setHeader(header.getKey(), header.getValue()); + } + } + + // 设置请求体 + if (jsonData != null) { + StringEntity entity = new StringEntity(jsonData, encoding); + entity.setContentType("application/json"); + post.setEntity(entity); + } + + // 执行请求 + CloseableHttpResponse response = httpClient.execute(post); + + // 获取响应状态码及响应数据 + StatusLine status = response.getStatusLine(); + if (HttpServiceUtil.success(status)) { + HttpEntity entity = response.getEntity(); + result = EntityUtils.toString(entity, encoding); + EntityUtils.consume(entity); // 确保释放资源 + } + response.close(); // 关闭响应对象 + } finally { + if (httpClient != null) { + httpClient.close(); // 关闭 HttpClient + } + } + return result; + } + } diff --git a/src/main/java/com/ycwl/basic/service/impl/mobile/WxPayServiceImpl.java b/src/main/java/com/ycwl/basic/service/impl/mobile/WxPayServiceImpl.java index 63db52b..7740fc5 100644 --- a/src/main/java/com/ycwl/basic/service/impl/mobile/WxPayServiceImpl.java +++ b/src/main/java/com/ycwl/basic/service/impl/mobile/WxPayServiceImpl.java @@ -2,43 +2,58 @@ package com.ycwl.basic.service.impl.mobile; import com.alibaba.fastjson.JSONObject; +import com.aliyuncs.utils.StringUtils; import com.wechat.pay.java.core.Config; import com.wechat.pay.java.core.RSAAutoCertificateConfig; import com.wechat.pay.java.core.exception.ServiceException; +import com.wechat.pay.java.core.http.HttpMethod; import com.wechat.pay.java.core.notification.NotificationConfig; import com.wechat.pay.java.core.notification.NotificationParser; import com.wechat.pay.java.core.notification.RequestParam; +import com.wechat.pay.java.core.util.PemUtil; import com.wechat.pay.java.service.partnerpayments.jsapi.model.Transaction; import com.wechat.pay.java.service.payments.jsapi.JsapiService; import com.wechat.pay.java.service.payments.jsapi.model.*; import com.wechat.pay.java.service.payments.model.TransactionAmount; import com.ycwl.basic.config.WechatConfig; +import com.ycwl.basic.constant.HttpConstant; +import com.ycwl.basic.constant.NumberConstant; import com.ycwl.basic.constant.WeiXinConstant; import com.ycwl.basic.enums.BizCodeEnum; import com.ycwl.basic.enums.OrderStateEnum; import com.ycwl.basic.exception.AppException; -import com.ycwl.basic.mapper.pc.OrderMapper; +import com.ycwl.basic.model.pc.order.resp.OrderRespVO; import com.ycwl.basic.model.wxPay.WXPayOrderReqVO; import com.ycwl.basic.model.wxPay.WxPayRespVO; import com.ycwl.basic.model.wxPay.WxchatCallbackSuccessData; +import com.ycwl.basic.service.HttpService; import com.ycwl.basic.service.mobile.WxPayService; import com.ycwl.basic.service.pc.OrderService; +import com.ycwl.basic.utils.SnowFlakeUtil; import com.ycwl.basic.utils.WXPayUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import javax.crypto.Cipher; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.SecretKeySpec; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import java.io.BufferedReader; +import java.io.IOException; import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; import java.math.BigDecimal; -import java.util.Objects; -import java.util.UUID; +import java.nio.charset.StandardCharsets; +import java.security.*; +import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; import static com.wechat.pay.java.core.http.Constant.*; +import static com.ycwl.basic.constant.WeiXinConstant.*; /** * @Author: songmingsong @@ -53,7 +68,7 @@ public class WxPayServiceImpl implements WxPayService { @Autowired private WechatConfig wechatConfig; @Autowired - private OrderMapper orderMapper; + private HttpService httpService; @Autowired private OrderService orderService; @@ -80,7 +95,7 @@ public class WxPayServiceImpl implements WxPayService { request.setAppid(wechatConfig.getAppId()); request.setMchid(wechatConfig.getMchId()); request.setDescription(req.getGoodsName()); - request.setNotifyUrl(wechatConfig.getNotifyUrl()); + request.setNotifyUrl(wechatConfig.getPayNotifyUrl()); request.setOutTradeNo(req.getOrderSn().toString()); Payer payer = new Payer(); @@ -90,7 +105,7 @@ public class WxPayServiceImpl implements WxPayService { // 调用下单方法,得到应答 PrepayResponse response = service.prepay(request); WxPayRespVO vo = new WxPayRespVO(); - Long timeStamp = System.currentTimeMillis() / 1000; + Long timeStamp = System.currentTimeMillis() / NumberConstant.THOUSAND; vo.setTimeStamp(timeStamp); String substring = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32); vo.setNonceStr(substring); @@ -102,7 +117,7 @@ public class WxPayServiceImpl implements WxPayService { return vo; } catch (ServiceException e) { JSONObject parse = JSONObject.parseObject(e.getResponseBody()); - throw new AppException(BizCodeEnum.ADVANCE_PAYMENT_FAILED, parse.getString("message")); + throw new AppException(BizCodeEnum.ADVANCE_PAYMENT_FAILED, parse.getString(HttpConstant.Message)); } catch (Exception e) { throw new AppException(BizCodeEnum.ADVANCE_PAYMENT_FAILED, e.toString()); } @@ -183,7 +198,6 @@ public class WxPayServiceImpl implements WxPayService { com.wechat.pay.java.service.payments.model.Transaction transaction = service.queryOrderByOutTradeNo(queryRequest); String successTime = transaction.getSuccessTime(); - wxchatCallbackSuccessData.setOrderId(orderId.toString()); wxchatCallbackSuccessData.setSuccessTime(successTime); wxchatCallbackSuccessData.setTradetype(WeiXinConstant.getDescriptionType(transaction.getTradeType())); @@ -193,9 +207,165 @@ public class WxPayServiceImpl implements WxPayService { wxchatCallbackSuccessData.setTotalMoney(new BigDecimal(total).movePointLeft(2)); } catch (ServiceException e) { // API返回失败, 例如ORDER_NOT_EXISTS - log.error("code={}, message={}", e.getErrorCode(), e.getErrorMessage()); - log.error("reponse body={}", e.getResponseBody()); + log.error("[微信退款] code={}, message={}", e.getErrorCode(), e.getErrorMessage()); + log.error("[微信退款] Response_body={}", e.getResponseBody()); } return wxchatCallbackSuccessData; } + + @Override + public Boolean refundOrder(String orderId) throws Exception { + OrderRespVO orderDetail = orderService.detail(Long.parseLong(orderId)).getData(); + BigDecimal payPrice = orderDetail.getPayPrice(); + int priceInCents = payPrice.multiply(new BigDecimal(NumberConstant.HUNDRED)).intValue(); // 转换为分(int) + // 构建退款请求参数 + Map data = new HashMap<>(); + data.put(WECHATPAY_OUT_TRADE_NO, orderId);// 你要退款的订单号 + data.put(WECHATPAY_OUT_REFUND_NO, SnowFlakeUtil.getId()); // 退款订单号(随机生成一个就行,保证不重复) + + Map amount = new HashMap<>(); + amount.put(WECHATPAY_REFUND, priceInCents); // 退款金额(以分为单位) + amount.put(WECHATPAY_TOTAL, priceInCents); // 订单总金额(以分为单位) + amount.put(WECHATPAY_CURRENCY, WECHATPAY_CURRENCY_CNY); + data.put(WECHATPAY_AMOUNT, amount); + + // 回调通知 URL + data.put(WECHATPAY_NOTIFY_URL, wechatConfig.getRefundNotifyUrl()); + Map stringObjectMap = this.processRefund(data); + if (stringObjectMap.get(WECHATPAY_STATUS).equals(WECHATPAY_SUCCESS) + || stringObjectMap.get(WECHATPAY_STATUS).equals(WECHATPAY_PROCESSING)) { + orderService.updateOrderState(Long.parseLong(orderId), OrderStateEnum.REFUNDED, null); + return true; + } else { + return false; + } + } + + @Override + public boolean refundNotify(String refundResult) throws IOException, GeneralSecurityException { + // 转为map格式 + Map jsonMap = JSONObject.parseObject(refundResult, Map.class); + + /* + * 退款成功后返回一个加密字段resource,以下为解密 + * 解密需要从resource参数中,获取到ciphertext,nonce,associated_data这三个参数进行解密 + */ + String resource = JSONObject.toJSONString(jsonMap.get(REFUNDS_RESOURCE)); + JSONObject object = JSONObject.parseObject(resource); + + String ciphertext = String.valueOf(object.get(REFUNDS_CIPHERTEXT)); + String nonce = String.valueOf(object.get(REFUNDS_NONCE)); + String associated_data = String.valueOf(object.get(REFUNDS_ASSOCIATED_DATA)); + + String resultStr = decryptToString(associated_data.getBytes(String.valueOf(StandardCharsets.UTF_8)), + nonce.getBytes(String.valueOf(StandardCharsets.UTF_8)), + ciphertext); + Map reqInfo = JSONObject.parseObject(resultStr, Map.class); + + String refund_status = reqInfo.get(REFUNDS_REFUND_STATUS);// 退款状态 + String out_trade_no = reqInfo.get(WECHATPAY_OUT_TRADE_NO); // 订单号 + + if (!StringUtils.isEmpty(refund_status) && WECHATPAY_SUCCESS.equals(refund_status)) { + orderService.updateOrderState(Long.parseLong(out_trade_no), OrderStateEnum.REFUNDED, null); + log.info("[微信退款回调]退款成功"); + return true; + } else { + log.error("[微信退款回调]退款失败"); + return false; + } + } + + + /** + * 退款回调 解密数据 + * + * @param associatedData + * @param nonce + * @param ciphertext + * @return + * @throws GeneralSecurityException + * @throws IOException + */ + public String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext) throws GeneralSecurityException, IOException { + try { + Cipher cipher = Cipher.getInstance(HttpConstant.AES_GCM_NoPadding); + + SecretKeySpec key = new SecretKeySpec(wechatConfig.getKeyPath().getBytes(), HttpConstant.AES); + GCMParameterSpec spec = new GCMParameterSpec(NumberConstant.ONE_HUNDRED_TWENTY_EIGHT, nonce);// 规定为128 + + cipher.init(Cipher.DECRYPT_MODE, key, spec); + cipher.updateAAD(associatedData); + + return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), String.valueOf(StandardCharsets.UTF_8)); + } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { + throw new IllegalStateException(e); + } catch (InvalidKeyException | InvalidAlgorithmParameterException e) { + throw new IllegalArgumentException(e); + } + } + + /** + * 发起退款 + * + * @param data 退款请求参数 + * @return + * @throws Exception + */ + public Map processRefund(Map data) throws Exception { + // 请求头(需要根据业务生成 Authorization Token) + Map headers = new HashMap<>(); + headers.put(HttpConstant.Authorization, getToken(String.valueOf(HttpMethod.POST), REFUNDS_URL, JSONObject.toJSONString(data))); + headers.put(HttpConstant.Accept, HttpConstant.Application_Json); + headers.put(HttpConstant.Content_Type, HttpConstant.Application_Json); + + // 请求体数据 + String jsonData = JSONObject.toJSONString(data); + + // 执行 POST 请求 + String response = httpService.doPost(REFUNDS_URL, jsonData, headers, String.valueOf(StandardCharsets.UTF_8)); + + // 将响应字符串解析为 Map + Map responseMap = JSONObject.parseObject(response, Map.class); + return responseMap; + } + + /** + * 生成退款请求token + * + * @param method + * @param orderId + * @param body + * @return + * @throws InvalidKeyException + * @throws NoSuchAlgorithmException + * @throws UnsupportedEncodingException + * @throws SignatureException + */ + public String getToken(String method, String orderId, String body) throws InvalidKeyException, NoSuchAlgorithmException, UnsupportedEncodingException, SignatureException { + String nonceStr = WXPayUtil.generateNonceStr(); + long timestamp = System.currentTimeMillis() / NumberConstant.THOUSAND; // 生成时间戳 + + String parameter = method + "\n" + + REFUNDS_URi + orderId + "\n" + + timestamp + "\n" + + nonceStr + "\n" + + body + "\n"; + + // 对参数进行加密 + byte[] bytes = parameter.getBytes(String.valueOf(StandardCharsets.UTF_8)); + Signature sign = Signature.getInstance("SHA256withRSA"); + PrivateKey privateKey = PemUtil.loadPrivateKeyFromPath(wechatConfig.getKeyPath()); // privateKeyPath是商户证书密钥的位置apiclient_key.pem + sign.initSign(privateKey); // 商户密钥文件路径 + sign.update(bytes); + String signature = Base64.getEncoder().encodeToString(sign.sign()); + + // 获取token + String token = "mchid=\"" + wechatConfig.getMchId() + "\"," // 商户号 + + "nonce_str=\"" + nonceStr + "\"," + + "timestamp=\"" + timestamp + "\"," + + "serial_no=\"" + wechatConfig.getMchSerialNo() + "\"," // merchantSerialNumber是微信支付中申请的证书序列号 + + "signature=\"" + signature + "\""; + + return REFUNDS_SCHEMA + token; + } } diff --git a/src/main/java/com/ycwl/basic/service/impl/pc/OrderServiceImpl.java b/src/main/java/com/ycwl/basic/service/impl/pc/OrderServiceImpl.java index 438157e..5e1df64 100644 --- a/src/main/java/com/ycwl/basic/service/impl/pc/OrderServiceImpl.java +++ b/src/main/java/com/ycwl/basic/service/impl/pc/OrderServiceImpl.java @@ -24,6 +24,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Objects; /** * @Author:longbinbin @@ -60,9 +61,9 @@ public class OrderServiceImpl implements OrderService { Long orderId = SnowFlakeUtil.getLongId(); order.setId(orderId); int add = orderMapper.add(order); - if (add == 0) { + if (add == NumberConstant.ZERO) { return ApiResponse.fail("订单添加失败"); - }else { + } else { List orderItemList = order.getOrderItemList(); List orderItems = new ArrayList<>(); orderItemList.forEach(orderItemDTO -> { @@ -71,7 +72,7 @@ public class OrderServiceImpl implements OrderService { orderItemEntity.setOrderId(orderId); }); int addOrderItems = orderMapper.addOrderItems(orderItems); - if (addOrderItems == 0) { + if (addOrderItems == NumberConstant.ZERO) { log.error("订单明细添加失败"); return ApiResponse.fail("订单添加失败"); } @@ -82,7 +83,7 @@ public class OrderServiceImpl implements OrderService { @Override public ApiResponse update(OrderAddOrUpdateReq query) { int update = orderMapper.update(query); - if (update == 0) { + if (update == NumberConstant.ZERO) { return ApiResponse.fail("订单更新失败"); } return ApiResponse.success(update); @@ -100,7 +101,9 @@ public class OrderServiceImpl implements OrderService { if (orderStateEnum.getType() == NumberConstant.ONE) { orderAddOrUpdateReq.setRefundStatus(orderStateEnum.getState()); orderAddOrUpdateReq.setRefundAt(new Date()); - orderAddOrUpdateReq.setRefundReason(refundReason); + if (Objects.nonNull(refundReason)) { + orderAddOrUpdateReq.setRefundReason(refundReason); + } } else if (orderStateEnum.getType() == NumberConstant.TWO) { int state = orderStateEnum.getState(); orderAddOrUpdateReq.setPayAt(new Date()); @@ -113,7 +116,7 @@ public class OrderServiceImpl implements OrderService { } @Override - public ApiResponse getOrderCountByUserId(Long userId) { + public ApiResponse getOrderCountByUserId(Long userId) { OrderReqQuery query = new OrderReqQuery(); query.setMemberId(userId); return orderMapper.getOrderCount(query); @@ -125,8 +128,8 @@ public class OrderServiceImpl implements OrderService { List list = orderMapper.appList(orderReqQuery); for (OrderAppRespVO appRespVO : list) { List orderItemList = appRespVO.getOrderItemList(); - if(orderItemList!= null && orderItemList.size() > 0){ - OrderItemVO itemVO = orderItemList.get(0); + if (orderItemList != null && !orderItemList.isEmpty()) { + OrderItemVO itemVO = orderItemList.get(NumberConstant.ZERO); appRespVO.setScenicName(itemVO.getScenicName()); appRespVO.setGoodsName(itemVO.getGoodsName()); } @@ -137,10 +140,10 @@ public class OrderServiceImpl implements OrderService { @Override public ApiResponse appDetail(Long id) { - OrderAppRespVO orderAppRespVO=orderMapper.appDetail(id); + OrderAppRespVO orderAppRespVO = orderMapper.appDetail(id); List orderItemList = orderAppRespVO.getOrderItemList(); - if(orderItemList!= null && orderItemList.size() > 0){ - OrderItemVO itemVO = orderItemList.get(0); + if (orderItemList != null && !orderItemList.isEmpty()) { + OrderItemVO itemVO = orderItemList.get(NumberConstant.ZERO); orderAppRespVO.setScenicName(itemVO.getScenicName()); orderAppRespVO.setGoodsName(itemVO.getGoodsName()); } diff --git a/src/main/java/com/ycwl/basic/service/mobile/WxPayService.java b/src/main/java/com/ycwl/basic/service/mobile/WxPayService.java index 703ea19..c2e3698 100644 --- a/src/main/java/com/ycwl/basic/service/mobile/WxPayService.java +++ b/src/main/java/com/ycwl/basic/service/mobile/WxPayService.java @@ -5,6 +5,8 @@ import com.ycwl.basic.model.wxPay.WxPayRespVO; import com.ycwl.basic.model.wxPay.WxchatCallbackSuccessData; import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.security.GeneralSecurityException; public interface WxPayService { @@ -22,4 +24,18 @@ public interface WxPayService { * 微信支付结果查询 */ WxchatCallbackSuccessData queryPay(Long orderId); + + /** + * 订单退款 + * + * @param orderId 订单id(订单编号) + * @return + * @throws Exception + */ + Boolean refundOrder(String orderId) throws Exception; + + /** + * 微信退款回调 + */ + boolean refundNotify(String refundResult) throws IOException, GeneralSecurityException; } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 1366e7f..aef119f 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -79,8 +79,10 @@ wx: mchId: xxxx # 商户证书序列号 mchSerialNo: xxxxx - # 回调接口地址 - notifyUrl: https://xxxx/a/biz/wxpay/payNotify + # 支付回调接口地址 + payNotifyUrl: https://xxxx/a/biz/wxpay/payNotify + # 退款回调接口地址 + refundNotifyUrl: https://xxxx/a/biz/wxpay/payNotify # 商户API私钥路径 keyPath: module-app/src/main/resources/cert/apiclient_key.pem # 商户APIV3密钥