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密钥