|
|
@@ -26,8 +26,11 @@ import com.wechat.pay.java.core.notification.NotificationParser;
|
|
|
import com.wechat.pay.java.core.notification.RequestParam;
|
|
|
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
|
|
|
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
|
|
|
+import com.wechat.pay.java.service.payments.jsapi.model.Amount;
|
|
|
import com.wechat.pay.java.service.payments.jsapi.model.*;
|
|
|
import com.wechat.pay.java.service.payments.model.Transaction;
|
|
|
+import com.wechat.pay.java.service.refund.RefundService;
|
|
|
+import com.wechat.pay.java.service.refund.model.*;
|
|
|
import jakarta.annotation.PostConstruct;
|
|
|
import jakarta.servlet.http.HttpServletRequest;
|
|
|
import lombok.SneakyThrows;
|
|
|
@@ -57,7 +60,9 @@ public class WxPayServiceImpl implements WxPayService {
|
|
|
|
|
|
private static final Logger LOGGER = LoggerFactory.getLogger(WxPayServiceImpl.class);
|
|
|
|
|
|
- public static JsapiService service;
|
|
|
+ public static JsapiService jsapiService;
|
|
|
+
|
|
|
+ public static RefundService refundService;
|
|
|
|
|
|
public static Config config;
|
|
|
|
|
|
@@ -77,17 +82,10 @@ public class WxPayServiceImpl implements WxPayService {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 商户订单号查询订单
|
|
|
+ * 初始化
|
|
|
+ *
|
|
|
+ * @throws IOException
|
|
|
*/
|
|
|
- public static Transaction queryOrderByOutTradeNo() {
|
|
|
-
|
|
|
- QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
|
|
|
- // 调用request.setXxx(val)设置所需参数,具体参数可见Request定义
|
|
|
- // 调用接口
|
|
|
- return service.queryOrderByOutTradeNo(request);
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
@PostConstruct
|
|
|
void init() throws IOException {
|
|
|
String privateKey;
|
|
|
@@ -104,9 +102,62 @@ public class WxPayServiceImpl implements WxPayService {
|
|
|
.merchantSerialNumber(conf.getMchsn()) // 商户证书序列号
|
|
|
.apiV3Key(conf.getV3key()) // 商户APIV3密钥
|
|
|
.build();
|
|
|
- service = new JsapiService.Builder().config(config).build();
|
|
|
+ jsapiService = new JsapiService.Builder().config(config).build();
|
|
|
+ refundService = new RefundService.Builder().config(config).build();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 商户订单号查询订单
|
|
|
+ */
|
|
|
+ public static Transaction queryOrderByOutTradeNo() {
|
|
|
+
|
|
|
+ QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
|
|
|
+ // 调用request.setXxx(val)设置所需参数,具体参数可见Request定义
|
|
|
+ // 调用接口
|
|
|
+ return jsapiService.queryOrderByOutTradeNo(request);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ @SneakyThrows
|
|
|
+ Object[] handleWxNotify(HttpServletRequest request) {
|
|
|
+ var no = RandomUtil.randomInt(1000, 9999);
|
|
|
+ var signature = request.getHeader("Wechatpay-Signature");
|
|
|
+ var serial = request.getHeader("Wechatpay-Serial");
|
|
|
+ var nonce = request.getHeader("Wechatpay-Nonce");
|
|
|
+ var timestamp = request.getHeader("Wechatpay-Timestamp");
|
|
|
+ var signatureType = request.getHeader("Wechatpay-Signature-Type");
|
|
|
+
|
|
|
+ LOGGER.info("微信支付回调{}:\n Request参数:\n signature:{},serial:{},nonce:{},timestamp:{},signatureType:{}", no, signature, serial, nonce, timestamp, signatureType);
|
|
|
+
|
|
|
+ // request中获取body
|
|
|
+ BufferedReader br = request.getReader();
|
|
|
+ String str;
|
|
|
+ var requestBody = new StringBuilder();
|
|
|
+ while ((str = br.readLine()) != null) {
|
|
|
+ requestBody.append(str);
|
|
|
+ }
|
|
|
+
|
|
|
+ LOGGER.info("微信支付回调{}:\nBody数据:\n{}", no, requestBody);
|
|
|
+
|
|
|
+ // 构造 RequestParam
|
|
|
+ RequestParam requestParam = new RequestParam.Builder()
|
|
|
+ .serialNumber(serial)
|
|
|
+ .nonce(nonce) // 随机数
|
|
|
+ .signature(signature)
|
|
|
+ .timestamp(timestamp)
|
|
|
+ .body(requestBody.toString())
|
|
|
+ .build();
|
|
|
+ LOGGER.info("微信支付回调{}:构造 RequestParam完毕", no);
|
|
|
+
|
|
|
+ // 如果已经初始化了 RSAAutoCertificateConfig,可直接使用
|
|
|
+ // 初始化 NotificationParser
|
|
|
+ NotificationParser parser = new NotificationParser((NotificationConfig) config);
|
|
|
+ return new Object[]{requestParam, parser, no};
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
/**
|
|
|
* JSAPI支付下单
|
|
|
*/
|
|
|
@@ -156,7 +207,7 @@ public class WxPayServiceImpl implements WxPayService {
|
|
|
request.setMchid(conf.getMchid());
|
|
|
request.setTransactionId(transactionId);
|
|
|
// 调用接口
|
|
|
- return service.queryOrderById(request);
|
|
|
+ return jsapiService.queryOrderById(request);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -168,51 +219,25 @@ public class WxPayServiceImpl implements WxPayService {
|
|
|
request.setMchid(conf.getMchid());
|
|
|
request.setOutTradeNo(outTradeNo);
|
|
|
// 调用接口
|
|
|
- service.closeOrder(request);
|
|
|
+ jsapiService.closeOrder(request);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 微信支付结果通知
|
|
|
+ *
|
|
|
+ * @param request
|
|
|
+ * @return
|
|
|
+ */
|
|
|
@SneakyThrows
|
|
|
@Override
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
public ResponseEntity.BodyBuilder wxNotify(HttpServletRequest request) {
|
|
|
- var no = RandomUtil.randomInt(1000, 9999);
|
|
|
- var signature = request.getHeader("Wechatpay-Signature");
|
|
|
- var serial = request.getHeader("Wechatpay-Serial");
|
|
|
- var nonce = request.getHeader("Wechatpay-Nonce");
|
|
|
- var timestamp = request.getHeader("Wechatpay-Timestamp");
|
|
|
- var signatureType = request.getHeader("Wechatpay-Signature-Type");
|
|
|
-
|
|
|
- LOGGER.info("微信支付回调{}:\n Request参数:\n signature:{},serial:{},nonce:{},timestamp:{},signatureType:{}", no, signature, serial, nonce, timestamp, signatureType);
|
|
|
-
|
|
|
- // request中获取body
|
|
|
- BufferedReader br = request.getReader();
|
|
|
- String str;
|
|
|
- var requestBody = new StringBuilder();
|
|
|
- while ((str = br.readLine()) != null) {
|
|
|
- requestBody.append(str);
|
|
|
- }
|
|
|
-
|
|
|
- LOGGER.info("微信支付回调{}:\nBody数据:\n{}", no, requestBody);
|
|
|
-
|
|
|
- // 构造 RequestParam
|
|
|
- RequestParam requestParam = new RequestParam.Builder()
|
|
|
- .serialNumber(serial)
|
|
|
- .nonce(nonce) // 随机数
|
|
|
- .signature(signature)
|
|
|
- .timestamp(timestamp)
|
|
|
- .body(requestBody.toString())
|
|
|
- .build();
|
|
|
- LOGGER.info("微信支付回调{}:构造 RequestParam完毕", no);
|
|
|
-
|
|
|
- // 如果已经初始化了 RSAAutoCertificateConfig,可直接使用
|
|
|
- // 初始化 NotificationParser
|
|
|
- NotificationParser parser = new NotificationParser((NotificationConfig) config);
|
|
|
+ var notifyRes = handleWxNotify(request);
|
|
|
|
|
|
- LOGGER.info("微信支付回调{}:初始化NotificationParser完毕", no);
|
|
|
try {
|
|
|
// 以支付通知回调为例,验签、解密并转换成 Transaction
|
|
|
- Transaction transaction = parser.parse(requestParam, Transaction.class);
|
|
|
- LOGGER.info("微信支付回调{}:验签解密完毕,数据:\n{}", no, transaction);
|
|
|
+ Transaction transaction = ((NotificationParser) notifyRes[1]).parse((RequestParam) notifyRes[0], Transaction.class);
|
|
|
+ LOGGER.info("微信支付回调{}:验签解密完毕,数据:\n{}", notifyRes[2], transaction);
|
|
|
// 判断是否已经接收处理过通知
|
|
|
if (payLogService.lambdaQuery().eq(PayLog::getOutTradeNo, transaction.getOutTradeNo()).one() != null) {
|
|
|
return ResponseEntity.status(HttpStatus.OK);
|
|
|
@@ -251,7 +276,7 @@ public class WxPayServiceImpl implements WxPayService {
|
|
|
payLog.setPayerTotal(transaction.getAmount().getPayerTotal());
|
|
|
payLog.setPayerCurrency(transaction.getAmount().getPayerCurrency());
|
|
|
payLogService.save(payLog);
|
|
|
- LOGGER.info("微信支付回调{}:业务处理结束", no);
|
|
|
+ LOGGER.info("微信支付回调{}:业务处理结束", notifyRes[2]);
|
|
|
|
|
|
} else {
|
|
|
LOGGER.error("微信支付通知处理异常,资金流水为空,回调信息:{}", transaction);
|
|
|
@@ -266,4 +291,76 @@ public class WxPayServiceImpl implements WxPayService {
|
|
|
// 处理成功,返回 200 OK 状态码
|
|
|
return ResponseEntity.status(HttpStatus.OK);
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建退款申请
|
|
|
+ *
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public Refund wxRefund(JSONObject params) {
|
|
|
+ CreateRequest request = new CreateRequest();
|
|
|
+ // 调用request.setXxx(val)设置所需参数,具体参数可见Request定义
|
|
|
+ // 原始第三方订单号
|
|
|
+ // TODO: 2023-09-05 填入业务数据
|
|
|
+ request.setOutTradeNo("");
|
|
|
+ // 退款订单号
|
|
|
+ request.setOutRefundNo("");
|
|
|
+ // 退款原因
|
|
|
+ request.setReason("用户申请退款");
|
|
|
+ // 回调通知地址
|
|
|
+ request.setNotifyUrl(conf.getRefundNotifyUrl());
|
|
|
+ var amount = new AmountReq();
|
|
|
+ // 退款金额
|
|
|
+ amount.setRefund(params.getLong("amount"));
|
|
|
+ request.setAmount(amount);
|
|
|
+ return refundService.create(request);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询单笔退款(通过商户退款单号)
|
|
|
+ *
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public Refund queryByOutRefundNo(String outRefundNo) {
|
|
|
+
|
|
|
+ QueryByOutRefundNoRequest request = new QueryByOutRefundNoRequest();
|
|
|
+ // 调用request.setXxx(val)设置所需参数,具体参数可见Request定义
|
|
|
+ request.setOutRefundNo(outRefundNo);
|
|
|
+ // 调用接口
|
|
|
+ return refundService.queryByOutRefundNo(request);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 微信退款结果通知
|
|
|
+ *
|
|
|
+ * @param request
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ @SneakyThrows
|
|
|
+ @Override
|
|
|
+ public ResponseEntity.BodyBuilder wxRefundNotify(HttpServletRequest request) {
|
|
|
+ var notifyRes = handleWxNotify(request);
|
|
|
+ try {
|
|
|
+ // 以支付通知回调为例,验签、解密并转换成 RefundNotification
|
|
|
+ RefundNotification refundNotification = ((NotificationParser) notifyRes[1]).parse((RequestParam) notifyRes[0], RefundNotification.class);
|
|
|
+ // TODO: 2023-09-05 处理业务逻辑
|
|
|
+ } catch (ValidationException e) {
|
|
|
+ // 签名验证失败,返回 401 UNAUTHORIZED 状态码
|
|
|
+ LOGGER.error("sign verification failed", e);
|
|
|
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果处理失败,应返回 4xx/5xx 的状态码,例如 500 INTERNAL_SERVER_ERROR
|
|
|
+ if (false) {
|
|
|
+ return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理成功,返回 200 OK 状态码
|
|
|
+ return ResponseEntity.status(HttpStatus.OK);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
}
|