From 4e124926ee6ee682084466067ae37550fca475fa Mon Sep 17 00:00:00 2001
From: 无关风月 <443237572@qq.com>
Date: 星期二, 05 八月 2025 11:39:48 +0800
Subject: [PATCH] 代码提交 世界杯修改支付宝资金结算功能
---
cloud-server-competition/src/main/java/com/dsh/competition/util/wx/Create.java | 292 +++++++++++
cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/service/impl/WorldCupServiceImpl.java | 32
cloud-server-other/src/main/java/com/dsh/other/util/PayMoneyUtil.java | 43 +
cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/service/impl/WorldCupPaymentServiceImpl.java | 48 +
cloud-server-other/src/main/java/com/dsh/other/util/wx/Create.java | 300 ++++++++++++
cloud-server-activity/src/main/resources/logback-spring.xml | 2
cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/service/IWorldCupPaymentService.java | 2
cloud-server-other/src/main/java/com/dsh/other/controller/SiteController.java | 53 ++
cloud-server-communityWorldCup/src/main/resources/sharding-jdbc.properties | 23
cloud-server-account/src/main/java/com/dsh/account/util/PayMoneyUtil.java | 26
cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/service/IWorldCupService.java | 2
cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/controller/WorldCupController.java | 53 ++
cloud-server-activity/src/main/resources/sharding-jdbc.properties | 2
cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/util/wx/Create.java | 292 +++++++++++
cloud-server-other/src/main/java/com/dsh/other/service/impl/SiteServiceImpl.java | 65 +-
cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/util/PayMoneyUtil.java | 38 +
cloud-server-competition/src/main/java/com/dsh/competition/service/impl/PaymentCompetitionServiceImpl.java | 52 -
cloud-server-competition/src/main/java/com/dsh/competition/util/PayMoneyUtil.java | 38 +
cloud-server-competition/src/main/java/com/dsh/competition/controller/CompetitionController.java | 115 +++-
19 files changed, 1,310 insertions(+), 168 deletions(-)
diff --git a/cloud-server-account/src/main/java/com/dsh/account/util/PayMoneyUtil.java b/cloud-server-account/src/main/java/com/dsh/account/util/PayMoneyUtil.java
index 9397dea..2fb7d51 100644
--- a/cloud-server-account/src/main/java/com/dsh/account/util/PayMoneyUtil.java
+++ b/cloud-server-account/src/main/java/com/dsh/account/util/PayMoneyUtil.java
@@ -377,19 +377,19 @@
}
return ResultUtil.success(response.getBody());
}
- public static void main(String[] args) throws AlipayApiException {
- confirm9("2088670241691219", "20250706132544057730", "2025070622001467281419691745", "0.1");
- confirm9("2088670241691219", "20250706132448647822", "2025070622001467281421616221", "0.1");
- confirm9("2088670241691219", "20250706132703365228", "2025070622001467281418986854", "0.1");
- confirm9("2088670241691219", "20250706132812692528", "2025070622001467281421628990", "0.1");
- confirm9("2088670241691219", "20250706133122769531", "2025070622001467281421682297", "0.1");
- confirm9("2088670241691219", "20250706155632714326", "2025070622001423881412880254", "0.01");
- confirm9("2088670241691219", "20250707124157706846", "2025070722001479841407246616", "0.01");
- confirm9("2088670241691219", "20250709182513843937", "2025070922001467281434141170", "0.01");
- confirm9("2088670241691219", "20250709183148107861", "2025070922001467281433375653", "0.01");
- confirm9("2088670241691219", "20250715203444033496", "2025071522001457851416341513", "5");
-
- }
+// public static void main(String[] args) throws AlipayApiException {
+// confirm9("2088670241691219", "20250706132544057730", "2025070622001467281419691745", "0.1");
+// confirm9("2088670241691219", "20250706132448647822", "2025070622001467281421616221", "0.1");
+// confirm9("2088670241691219", "20250706132703365228", "2025070622001467281418986854", "0.1");
+// confirm9("2088670241691219", "20250706132812692528", "2025070622001467281421628990", "0.1");
+// confirm9("2088670241691219", "20250706133122769531", "2025070622001467281421682297", "0.1");
+// confirm9("2088670241691219", "20250706155632714326", "2025070622001423881412880254", "0.01");
+// confirm9("2088670241691219", "20250707124157706846", "2025070722001479841407246616", "0.01");
+// confirm9("2088670241691219", "20250709182513843937", "2025070922001467281434141170", "0.01");
+// confirm9("2088670241691219", "20250709183148107861", "2025070922001467281433375653", "0.01");
+// confirm9("2088670241691219", "20250715203444033496", "2025071522001457851416341513", "5");
+//
+// }
private static AlipayConfig getAlipayConfig() {
String privateKey = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCi5i9nW/hGLJ3A06cZxTQdviFC7THpdSihoTYGLr9q006hu0V26ecBMY/o4w5bvIX0Ok/yofmZsVcCJpAPvbXL/uqVrIjnRRxXiaeBFThlxoBUTdunvbUSDYfzlEhJr5NvUKI6H6lz2niXlQGx4qy8Hau4ccWit9kM8jwUvsBVQoFgJA+xrjMvooA7YLopQtpOD+UJr5thApTSf1xrnr1W12yolTLEH15JmNV372cqXrYUuqnY0QsaPtxeqJUGAOcGdVLllQ7easEznP8DFBvDdHATcmp2SHNQDUEWN6MCVPbMgY06NQVqAXxqjTAYSVh+6TRu6bofPmpYC3TZB003AgMBAAECggEBAJAcR2+PA3NBYUYHeFrqBRMS8uX8ZR19kjZ7IgoSLTFaQsP9opRylPSPXhrPVBKAE5leRQAHn4MCSlESwHvMfxo7KFjFTFAc6dffZZpipYQUOc9bGampwJh58/3e/pyBgVMG6J23CPf/HJQtNFSkjd/V9+ayb/9l2dUEL3bC0fAZ/dbx8HsxdLw8wn3fLlWLj68hOMqa2deCZe3JdSVsPbeWqkh56FFsMLug0Nd+Ar4TgRl9/jnhXF0JWiD0LmPUYLhboY7EfUBzN4w1iYbDi1P+3zvoOYsiVKAXox9GMhQ2VzOO2UcSTuizSza2e98mGpabl/GpKmCz+RDFjtkX6eECgYEA2MyCij65eO3aGIm3FUe93DULRBYTfX8qJQSJq2WOWA3mmQlEW6L3O2B5/lG2h+8WmN6iLEs9eHpgycGYp7vAqgrANEn16ACVcuyx0scFtrZfZ+kmHMzFfiUWxJjVYk/6YngsGVBLdw6ueM42C8TTP67X9tU5TdVGoGWuqEj4W98CgYEAwFqwprXOch5Pqk/RPbb49r0Ou03K/UbciWnWWKzUhFFNS8MdlQPoDvQZbMwHLeWsa2VhaKITK3x5biLQb3U+0GLOn6lTvEyrEUH+ucREyLgVYTRAvwBPtnvlrzpyxPk2HnslQjju8WrvvLLBMKWUjlTrTOzhaHT21gz3pHMiOakCgYEAhLmfaXdBITGshb054sNLDtdCkGpbgEcrzAHdLps769iGxkYQHXHFngpQZUwtTUcoNGqIKknd1jZFrv7gsD+XkgKG7PwimehRlkwmCX5ilxtLiVgJRzRt6+5U5AMVD90a0tHzXYP0z2yjj73fBJF5KtGl0a10KZxaYrQdm1UhB00CgYBZZgzx/k9rtHC8LAqIj1CYhHejT92G53c6Gkl3vyOqN4sgKhfGmSEySfrDGPRBPZxr8ZtbIPCd5mUdberH0osWGMYFaJI1UsCy7aQwvGpniz7MhZeN7dweaOjwDs8mgtjHQ96mL4XGCDhR0BZ/wIURvZ/6iaGdhbbu9unlsWj3uQKBgQCmZYdsbbZkd3ev6f8rwyvMz+DrCQyYpY44cegBYuJgrZiQnL2fJioeN7ixX0UM48SfwsZEIrzshP/LGAwnc2MdjxKUl4jLN8SEe0NAjXOnz9Zaw740+aOmLpXcLWdP4uM2gIhWsvW1tEkQZCXmm7c9s/RsU8Pmzv+YL3+fSijOzA==";
String alipayPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmu8n/4yTHWbn7VOrNc9OsLtDL1bEQ8gC1dHkj8Wy5z0mkaOsjJRIG/28ze12M0V8jdCKuuDr5Z1OPKiqf+XO3ypguEh+mYUVMBM/cZodDFQfTY1TKLWjvQCuaqlA+QUTCK6f7T7stsgyQ1o9Jj0rXZDz6PM4QHSTzjrLIBaeqM5WIBvH+fy/X+QG5Utd+/UT0kc0JyvuKhZ65yVUd/C9VcwJJAPliRsAQNrqYterwAJ9zvw9tF11wj9W0XgJ8Ccu4x3gR1vrlLRJJo/OA97RmxPQ+5hSacWQZCUd1dwiBq+YCrKVHGTj14izRHXrLc0yBlRXo7tBOIqcy3IsvKVthQIDAQAB";
diff --git a/cloud-server-activity/src/main/resources/logback-spring.xml b/cloud-server-activity/src/main/resources/logback-spring.xml
index 37d854d..4a781fa 100644
--- a/cloud-server-activity/src/main/resources/logback-spring.xml
+++ b/cloud-server-activity/src/main/resources/logback-spring.xml
@@ -24,7 +24,7 @@
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
- <level>debug</level>
+ <level>info</level>
</filter>
<encoder>
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
diff --git a/cloud-server-activity/src/main/resources/sharding-jdbc.properties b/cloud-server-activity/src/main/resources/sharding-jdbc.properties
index 7f8645e..13de04c 100644
--- a/cloud-server-activity/src/main/resources/sharding-jdbc.properties
+++ b/cloud-server-activity/src/main/resources/sharding-jdbc.properties
@@ -14,7 +14,7 @@
datasource.names=master0
datasource.master0.type=com.alibaba.druid.pool.DruidDataSource
datasource.master0.driverClassName=com.mysql.cj.jdbc.Driver
-datasource.master0.url=jdbc:mysql://127.0.0.1:3306/playpai_activity?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai
+datasource.master0.url=jdbc:mysql://127.0.0.1/playpai_activity?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai
datasource.master0.username=root
datasource.master0.password=playpai2023!
datasource.master0.maxActive=20
diff --git a/cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/controller/WorldCupController.java b/cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/controller/WorldCupController.java
index 0ade7ca..cae4036 100644
--- a/cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/controller/WorldCupController.java
+++ b/cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/controller/WorldCupController.java
@@ -106,7 +106,6 @@
/**
* 查询社区世界杯收入--管理后台
- * @param storeId
* @return
*/
@ResponseBody
@@ -654,7 +653,7 @@
@ApiImplicitParam(name = "id", value = "列表中的id", required = true, dataType = "Long"),
@ApiImplicitParam(name = "Authorization", value = "用户token(Bearer +token)", required = true, dataType = "String", paramType = "header", defaultValue = "Bearer eyJhbGciOiJIUzUxMiJ9.....")
})
- public ResultUtil cancelMyWorldCup(Long id){
+ public ResultUtil cancelMyWorldCup(Long id) throws Exception {
return worldCupPaymentService.cancelMyWorldCup(id);
}
@@ -942,7 +941,7 @@
*/
@ResponseBody
@PostMapping("/worldCup/cancelWorldCupRefund")
- public void cancelWorldCupRefund(@RequestBody Integer id){
+ public void cancelWorldCupRefund(@RequestBody Integer id) throws Exception {
worldCupService.cancelWorldCupRefund(id);
}
@@ -988,6 +987,54 @@
out.close();
}
}
+ /**
+ * 取消赛事后微信退款回调微信V3回调
+ * @param request
+ * @param response
+ */
+ @ResponseBody
+ @PostMapping("/base/worldCup/wxRefundWorldCupCallback1")
+ public void wxRefundWorldCupCallback1(HttpServletRequest request, HttpServletResponse response){
+ try {
+ System.err.println("微信回调");
+ System.err.println("请求" + request);
+ BufferedReader reader = request.getReader();
+ String string1 = reader.toString();
+ System.err.println("请求reader" + string1);
+ StringBuilder requestBody = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ requestBody.append(line);
+ }
+ System.err.println("全部请求体" + requestBody);
+ JSONObject jsonObject = JSONObject.parseObject(requestBody.toString());
+ JSONObject resource = jsonObject.getJSONObject("resource");
+
+ AesUtil aesUtil = new AesUtil(WxV3PayConfig.apiV3Key.getBytes(StandardCharsets.UTF_8));
+ String decryptedData = aesUtil.decryptToString(resource.getString("associated_data").getBytes(StandardCharsets.UTF_8), resource.getString("nonce").getBytes(StandardCharsets.UTF_8),
+ resource.getString("ciphertext"));
+ System.err.println("微信解密的字符串信息" + decryptedData);
+ JSONObject jsonInfo = (JSONObject) JSONObject.parse(decryptedData);
+ String code = jsonInfo.getString("out_trade_no");
+ String transaction_id = jsonInfo.getString("transaction_id");
+ String refund_status = jsonInfo.getString("refund_status");
+ String out_refund_no = jsonInfo.getString("out_refund_no");
+ if (refund_status.equals("SUCCESS")) {
+ WorldCupPayment worldCupPayment = worldCupPaymentService.getOne(new QueryWrapper<WorldCupPayment>().eq("code", out_refund_no));
+ worldCupPayment.setRefundOrderNo(code);
+ worldCupPayment.setRefundTime(new Date());
+ worldCupPayment.setPayStatus(3);
+ worldCupPayment.setWorldCupId(null);
+ worldCupPaymentService.updateById(worldCupPayment);
+ PrintWriter out = response.getWriter();
+ out.write("SUCCESS");
+ out.flush();
+ out.close();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
/**
diff --git a/cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/service/IWorldCupPaymentService.java b/cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/service/IWorldCupPaymentService.java
index 2bd7b9a..36ab6f5 100644
--- a/cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/service/IWorldCupPaymentService.java
+++ b/cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/service/IWorldCupPaymentService.java
@@ -16,5 +16,5 @@
* @param id
* @return
*/
- ResultUtil cancelMyWorldCup(Long id);
+ ResultUtil cancelMyWorldCup(Long id) throws Exception;
}
diff --git a/cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/service/IWorldCupService.java b/cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/service/IWorldCupService.java
index 83f799a..4bcb1e9 100644
--- a/cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/service/IWorldCupService.java
+++ b/cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/service/IWorldCupService.java
@@ -85,7 +85,7 @@
* 取消赛事后退还费用
* @param id
*/
- void cancelWorldCupRefund(Integer id);
+ void cancelWorldCupRefund(Integer id) throws Exception;
/**
diff --git a/cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/service/impl/WorldCupPaymentServiceImpl.java b/cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/service/impl/WorldCupPaymentServiceImpl.java
index 183f065..4f3d5d3 100644
--- a/cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/service/impl/WorldCupPaymentServiceImpl.java
+++ b/cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/service/impl/WorldCupPaymentServiceImpl.java
@@ -7,23 +7,30 @@
import com.dsh.communityWorldCup.entity.WorldCup;
import com.dsh.communityWorldCup.entity.WorldCupPayment;
import com.dsh.communityWorldCup.entity.WorldCupPaymentParticipant;
+import com.dsh.communityWorldCup.entity.WorldCupStore;
import com.dsh.communityWorldCup.feignclient.account.AppUserClient;
import com.dsh.communityWorldCup.feignclient.account.model.AppUser;
import com.dsh.communityWorldCup.feignclient.course.CoursePackageOrderStudentClient;
import com.dsh.communityWorldCup.feignclient.course.model.CoursePackageOrderStudent;
+import com.dsh.communityWorldCup.feignclient.other.StoreClient;
+import com.dsh.communityWorldCup.feignclient.other.model.Store;
import com.dsh.communityWorldCup.mapper.WorldCupPaymentMapper;
import com.dsh.communityWorldCup.model.DeductionClassHourList;
import com.dsh.communityWorldCup.service.IWorldCupPaymentParticipantService;
import com.dsh.communityWorldCup.service.IWorldCupPaymentService;
import com.dsh.communityWorldCup.service.IWorldCupService;
+import com.dsh.communityWorldCup.service.IWorldCupStoreService;
import com.dsh.communityWorldCup.util.PayMoneyUtil;
import com.dsh.communityWorldCup.util.ResultUtil;
+import com.dsh.communityWorldCup.util.UUIDUtil;
import net.bytebuddy.asm.Advice;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.math.BigDecimal;
+import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
@@ -37,6 +44,8 @@
@Autowired
private IWorldCupPaymentParticipantService worldCupPaymentParticipantService;
+ @Autowired
+ private IWorldCupStoreService worldCupStoreService;
@Autowired
private IWorldCupService worldCupService;
@@ -46,6 +55,8 @@
@Resource
private AppUserClient appUserClient;
+ @Resource
+ private StoreClient storeClient;
@Resource
private CoursePackageOrderStudentClient coursePackageOrderStudentClient;
@@ -59,7 +70,7 @@
* @return
*/
@Override
- public ResultUtil cancelMyWorldCup(Long id) {
+ public ResultUtil cancelMyWorldCup(Long id) throws Exception {
WorldCupPaymentParticipant worldCupPaymentParticipant = worldCupPaymentParticipantService.getById(id);
WorldCupPayment worldCupPayment = this.getById(worldCupPaymentParticipant.getWorldCupPaymentId());
WorldCup worldCup = worldCupService.getById(worldCupPayment.getWorldCupId());
@@ -82,17 +93,32 @@
//总排款金额(排除已经参加比赛的数据)
BigDecimal multiply = worldCupPayment.getUnitPrice().multiply(new BigDecimal(list1.size()));
//微信支付
+
+ List<WorldCupStore> list = worldCupStoreService.lambdaQuery().eq(WorldCupStore::getWorldCupId, worldCup.getId()).list();
+ Integer storeId = list.get(0).getStoreId();
+ Store store = storeClient.queryStoreById(storeId);
if(worldCupPayment.getPayType() == 1){
- Map<String, String> map = payMoneyUtil.wxRefund(worldCupPayment.getPayOrderNo(), worldCupPayment.getCode(),
- worldCupPayment.getAmount().toString(), multiply.toString(), "/base/worldCup/wxRefundWorldCupCallback");
- if(!"SUCCESS".equals(map.get("return_code"))){
- System.err.println("-------------微信退款失败---------");
- System.err.println(map.get("return_msg"));
- return ResultUtil.error("微信退款失败");
- }else{
- worldCupPayment.setRefundAmount(multiply);
- worldCupPayment.setWorldCupId(null);
- this.updateById(worldCupPayment);
+ if (store.getOperatorId()!=null && store.getOperatorId()!=0){
+ String smidVx= storeClient.getmerchantNumberByOperatorId(store.getOperatorId());
+ if (!StringUtils.hasLength(smidVx)){
+ System.err.println("运营商未配置微信商户号,获取支付失败!");
+ }
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
+ String codeRefund = sdf.format(new Date()) + UUIDUtil.getNumberRandom(5);// 退款单号
+ // 运营商退款
+ payMoneyUtil.weixinRefundV3(smidVx, codeRefund, worldCupPayment.getPayOrderNo(), worldCupPayment.getAmount().toString(), "/base/worldCup/wxRefundWorldCupCallback1");
+ }else {
+ Map<String, String> map = payMoneyUtil.wxRefund(worldCupPayment.getPayOrderNo(), worldCupPayment.getCode(),
+ worldCupPayment.getAmount().toString(), multiply.toString(), "/base/worldCup/wxRefundWorldCupCallback");
+ if (!"SUCCESS".equals(map.get("return_code"))) {
+ System.err.println("-------------微信退款失败---------");
+ System.err.println(map.get("return_msg"));
+ return ResultUtil.error("微信退款失败");
+ } else {
+ worldCupPayment.setRefundAmount(multiply);
+ worldCupPayment.setWorldCupId(null);
+ this.updateById(worldCupPayment);
+ }
}
}
//支付宝支付
diff --git a/cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/service/impl/WorldCupServiceImpl.java b/cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/service/impl/WorldCupServiceImpl.java
index dc89dab..635b821 100644
--- a/cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/service/impl/WorldCupServiceImpl.java
+++ b/cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/service/impl/WorldCupServiceImpl.java
@@ -849,10 +849,13 @@
* @param id
*/
@Override
- public void cancelWorldCupRefund(Integer id) {
+ public void cancelWorldCupRefund(Integer id) throws Exception {
//免费除外
List<WorldCupPayment> list2 = worldCupPaymentService.list(new QueryWrapper<WorldCupPayment>().eq("worldCupId", id)
.ne("payType", 0).eq("payStatus", 2).eq("state", 1));
+ List<WorldCupStore> list = worldCupStoreService.lambdaQuery().eq(WorldCupStore::getWorldCupId, id).list();
+ Integer storeId = list.get(0).getStoreId();
+ Store store = storeClient.queryStoreById(storeId);
for (WorldCupPayment worldCupPayment : list2) {
List<WorldCupPaymentParticipant> list1 = worldCupPaymentParticipantService.list(new QueryWrapper<WorldCupPaymentParticipant>()
.eq("worldCupId", id).eq("worldCupPaymentId", worldCupPayment.getId()).eq("alreadyEntered", 0));
@@ -860,14 +863,25 @@
BigDecimal multiply = worldCupPayment.getUnitPrice().multiply(new BigDecimal(list1.size()));
//微信支付
if(worldCupPayment.getPayType() == 1){
- Map<String, String> map = payMoneyUtil.wxRefund(worldCupPayment.getPayOrderNo(), worldCupPayment.getCode(),
- worldCupPayment.getAmount().toString(), multiply.toString(), "/base/worldCup/wxRefundWorldCupCallback");
- if(!"SUCCESS".equals(map.get("return_code"))){
- System.err.println("-------------微信退款失败---------");
- System.err.println(map.get("return_msg"));
- }else{
- worldCupPayment.setRefundAmount(multiply);
- worldCupPaymentService.updateById(worldCupPayment);
+ if (store.getOperatorId()!=null && store.getOperatorId()!=0){
+ String smidVx= storeClient.getmerchantNumberByOperatorId(store.getOperatorId());
+ if (!StringUtils.hasLength(smidVx)){
+ System.err.println("运营商未配置微信商户号,获取支付失败!");
+ }
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
+ String codeRefund = sdf.format(new Date()) + UUIDUtil.getNumberRandom(5);// 退款单号
+ // 运营商退款
+ payMoneyUtil.weixinRefundV3(smidVx, codeRefund, worldCupPayment.getPayOrderNo(), worldCupPayment.getAmount().toString(), "/base/worldCup/wxRefundWorldCupCallback1");
+ }else {
+ Map<String, String> map = payMoneyUtil.wxRefund(worldCupPayment.getPayOrderNo(), worldCupPayment.getCode(),
+ worldCupPayment.getAmount().toString(), multiply.toString(), "/base/worldCup/wxRefundWorldCupCallback");
+ if (!"SUCCESS".equals(map.get("return_code"))) {
+ System.err.println("-------------微信退款失败---------");
+ System.err.println(map.get("return_msg"));
+ } else {
+ worldCupPayment.setRefundAmount(multiply);
+ worldCupPaymentService.updateById(worldCupPayment);
+ }
}
}
//支付宝支付
diff --git a/cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/util/PayMoneyUtil.java b/cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/util/PayMoneyUtil.java
index 6c342bc..d2fbb60 100644
--- a/cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/util/PayMoneyUtil.java
+++ b/cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/util/PayMoneyUtil.java
@@ -7,10 +7,7 @@
import com.alipay.api.request.*;
import com.alipay.api.response.*;
import com.dsh.communityWorldCup.util.httpClinet.HttpClientUtil;
-import com.dsh.communityWorldCup.util.wx.PartnerAppPrepay;
-import com.dsh.communityWorldCup.util.wx.WXPayUtility;
-import com.dsh.communityWorldCup.util.wx.WeChatV3SignUtil;
-import com.dsh.communityWorldCup.util.wx.WxV3PayConfig;
+import com.dsh.communityWorldCup.util.wx.*;
import org.apache.commons.collections.map.HashedMap;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.dom4j.Document;
@@ -729,6 +726,39 @@
}
}
+ public ResultUtil weixinRefundV3(String subMchid,String outRefundNo, String transactionId, String totalFee, String notifyUrl) throws Exception {
+ BigDecimal bigDecimal = new BigDecimal(totalFee);
+ int i = bigDecimal.multiply(new BigDecimal("100")).intValue();
+ // TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/partner/4013080340
+ Create client = new Create(
+ "1681873607", // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756
+ "55714944F7A7E52526F708280B176DCC838F371A", // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
+ "/usr/playpai/server/wxV3/1681873607_20250424_cert/apiclient_key.pem", // 商户API证书私钥文件路径,本地文件路径
+ "PUB_KEY_ID_0116818736072025042400351694002605", // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
+ "/usr/playpai/server/wxV3/pub_key.pem" // 微信支付公钥文件路径,本地文件路径
+ );
+
+ Create.CreateRequest request = new Create.CreateRequest();
+ request.subMchid = subMchid;
+ request.transactionId = transactionId;
+ request.outRefundNo = outRefundNo;
+ request.reason = "退款";
+ request.notifyUrl = callbackPath+notifyUrl;
+ request.amount = new Create.AmountReq();
+ request.amount.refund = (long) i;
+ request.amount.total = (long) i;
+ request.amount.currency = "CNY";
+ try {
+ Create.Refund response = client.run(request);
+
+ // TODO: 请求成功,继续业务逻辑
+ System.out.println(response);
+ } catch (WXPayUtility.ApiException e) {
+ // TODO: 请求失败,根据状态码执行不同的逻辑
+ e.printStackTrace();
+ }
+ return ResultUtil.success();
+ }
/**
* 微信退款成功后的回调处理
diff --git a/cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/util/wx/Create.java b/cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/util/wx/Create.java
new file mode 100644
index 0000000..e1f6045
--- /dev/null
+++ b/cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/util/wx/Create.java
@@ -0,0 +1,292 @@
+package com.dsh.communityWorldCup.util.wx;
+
+import com.google.gson.annotations.SerializedName;
+import okhttp3.*;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.List;
+
+/**
+ * 退款申请
+ */
+public class Create {
+ private static String HOST = "https://api.mch.weixin.qq.com";
+ private static String METHOD = "POST";
+ private static String PATH = "/v3/refund/domestic/refunds";
+
+
+ public Refund run(CreateRequest request) {
+ String uri = PATH;
+ String reqBody = WXPayUtility.toJson(request);
+
+ Request.Builder reqBuilder = new Request.Builder().url(HOST + uri);
+ reqBuilder.addHeader("Accept", "application/json");
+ reqBuilder.addHeader("Wechatpay-Serial", wechatPayPublicKeyId);
+ reqBuilder.addHeader("Authorization", WXPayUtility.buildAuthorization(mchid, certificateSerialNo, privateKey, METHOD, uri, reqBody));
+ reqBuilder.addHeader("Content-Type", "application/json");
+ RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), reqBody);
+ reqBuilder.method(METHOD, requestBody);
+ Request httpRequest = reqBuilder.build();
+
+ // 发送HTTP请求
+ OkHttpClient client = new OkHttpClient.Builder().build();
+ try (Response httpResponse = client.newCall(httpRequest).execute()) {
+ String respBody = WXPayUtility.extractBody(httpResponse);
+ if (httpResponse.code() >= 200 && httpResponse.code() < 300) {
+ // 2XX 成功,验证应答签名
+ WXPayUtility.validateResponse(this.wechatPayPublicKeyId, this.wechatPayPublicKey,
+ httpResponse.headers(), respBody);
+
+ // 从HTTP应答报文构建返回数据
+ return WXPayUtility.fromJson(respBody, Refund.class);
+ } else {
+ throw new WXPayUtility.ApiException(httpResponse.code(), respBody, httpResponse.headers());
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException("Sending request to " + uri + " failed.", e);
+ }
+ }
+
+ private final String mchid;
+ private final String certificateSerialNo;
+ private final PrivateKey privateKey;
+ private final String wechatPayPublicKeyId;
+ private final PublicKey wechatPayPublicKey;
+
+ public Create(String mchid, String certificateSerialNo, String privateKeyFilePath, String wechatPayPublicKeyId, String wechatPayPublicKeyFilePath) {
+ this.mchid = mchid;
+ this.certificateSerialNo = certificateSerialNo;
+ this.privateKey = WXPayUtility.loadPrivateKeyFromPath(privateKeyFilePath);
+ this.wechatPayPublicKeyId = wechatPayPublicKeyId;
+ this.wechatPayPublicKey = WXPayUtility.loadPublicKeyFromPath(wechatPayPublicKeyFilePath);
+ }
+
+ public enum Status {
+ @SerializedName("SUCCESS")
+ SUCCESS,
+ @SerializedName("CLOSED")
+ CLOSED,
+ @SerializedName("PROCESSING")
+ PROCESSING,
+ @SerializedName("ABNORMAL")
+ ABNORMAL
+ }
+
+ public enum Account {
+ @SerializedName("AVAILABLE")
+ AVAILABLE,
+ @SerializedName("UNAVAILABLE")
+ UNAVAILABLE
+ }
+
+ public enum PromotionType {
+ @SerializedName("COUPON")
+ COUPON,
+ @SerializedName("DISCOUNT")
+ DISCOUNT
+ }
+
+ public static class GoodsDetail {
+ @SerializedName("merchant_goods_id")
+ public String merchantGoodsId;
+
+ @SerializedName("wechatpay_goods_id")
+ public String wechatpayGoodsId;
+
+ @SerializedName("goods_name")
+ public String goodsName;
+
+ @SerializedName("unit_price")
+ public Long unitPrice;
+
+ @SerializedName("refund_amount")
+ public Long refundAmount;
+
+ @SerializedName("refund_quantity")
+ public Integer refundQuantity;
+ }
+
+ public static class CreateRequest {
+ @SerializedName("sub_mchid")
+ public String subMchid;
+
+ @SerializedName("transaction_id")
+ public String transactionId;
+
+ @SerializedName("out_trade_no")
+ public String outTradeNo;
+
+ @SerializedName("out_refund_no")
+ public String outRefundNo;
+
+ @SerializedName("reason")
+ public String reason;
+
+ @SerializedName("notify_url")
+ public String notifyUrl;
+
+ @SerializedName("funds_account")
+ public ReqFundsAccount fundsAccount;
+
+ @SerializedName("amount")
+ public AmountReq amount;
+
+ @SerializedName("goods_detail")
+ public List<GoodsDetail> goodsDetail;
+ }
+
+ public enum Channel {
+ @SerializedName("ORIGINAL")
+ ORIGINAL,
+ @SerializedName("BALANCE")
+ BALANCE,
+ @SerializedName("OTHER_BALANCE")
+ OTHER_BALANCE,
+ @SerializedName("OTHER_BANKCARD")
+ OTHER_BANKCARD
+ }
+
+ public static class Amount {
+ @SerializedName("total")
+ public Long total;
+
+ @SerializedName("refund")
+ public Long refund;
+
+ @SerializedName("from")
+ public List<FundsFromItem> from;
+
+ @SerializedName("payer_total")
+ public Long payerTotal;
+
+ @SerializedName("payer_refund")
+ public Long payerRefund;
+
+ @SerializedName("settlement_refund")
+ public Long settlementRefund;
+
+ @SerializedName("settlement_total")
+ public Long settlementTotal;
+
+ @SerializedName("discount_refund")
+ public Long discountRefund;
+
+ @SerializedName("currency")
+ public String currency;
+
+ @SerializedName("refund_fee")
+ public Long refundFee;
+ }
+
+ public enum ReqFundsAccount {
+ @SerializedName("AVAILABLE")
+ AVAILABLE,
+ @SerializedName("UNSETTLED")
+ UNSETTLED
+ }
+
+ public enum FundsAccount {
+ @SerializedName("UNSETTLED")
+ UNSETTLED,
+ @SerializedName("AVAILABLE")
+ AVAILABLE,
+ @SerializedName("UNAVAILABLE")
+ UNAVAILABLE,
+ @SerializedName("OPERATION")
+ OPERATION,
+ @SerializedName("BASIC")
+ BASIC,
+ @SerializedName("ECNY_BASIC")
+ ECNY_BASIC
+ }
+
+ public static class Promotion {
+ @SerializedName("promotion_id")
+ public String promotionId;
+
+ @SerializedName("scope")
+ public PromotionScope scope;
+
+ @SerializedName("type")
+ public PromotionType type;
+
+ @SerializedName("amount")
+ public Long amount;
+
+ @SerializedName("refund_amount")
+ public Long refundAmount;
+
+ @SerializedName("goods_detail")
+ public List<GoodsDetail> goodsDetail;
+ }
+
+ public static class Refund {
+ @SerializedName("refund_id")
+ public String refundId;
+
+ @SerializedName("out_refund_no")
+ public String outRefundNo;
+
+ @SerializedName("transaction_id")
+ public String transactionId;
+
+ @SerializedName("out_trade_no")
+ public String outTradeNo;
+
+ @SerializedName("channel")
+ public Channel channel;
+
+ @SerializedName("user_received_account")
+ public String userReceivedAccount;
+
+ @SerializedName("success_time")
+ public String successTime;
+
+ @SerializedName("create_time")
+ public String createTime;
+
+ @SerializedName("status")
+ public Status status;
+
+ @SerializedName("funds_account")
+ public FundsAccount fundsAccount;
+
+ @SerializedName("amount")
+ public Amount amount;
+
+ @SerializedName("promotion_detail")
+ public List<Promotion> promotionDetail;
+ }
+
+ public static class FundsFromItem {
+ @SerializedName("account")
+ public Account account;
+
+ @SerializedName("amount")
+ public Long amount;
+ }
+
+ public static class AmountReq {
+ @SerializedName("refund")
+ public Long refund;
+
+ @SerializedName("from")
+ public List<FundsFromItem> from;
+
+ @SerializedName("total")
+ public Long total;
+
+ @SerializedName("currency")
+ public String currency;
+ }
+
+ public enum PromotionScope {
+ @SerializedName("GLOBAL")
+ GLOBAL,
+ @SerializedName("SINGLE")
+ SINGLE
+ }
+}
\ No newline at end of file
diff --git a/cloud-server-communityWorldCup/src/main/resources/sharding-jdbc.properties b/cloud-server-communityWorldCup/src/main/resources/sharding-jdbc.properties
index 9156bff..1007165 100644
--- a/cloud-server-communityWorldCup/src/main/resources/sharding-jdbc.properties
+++ b/cloud-server-communityWorldCup/src/main/resources/sharding-jdbc.properties
@@ -1,15 +1,3 @@
-datasource.names=master0
-datasource.master0.type=com.alibaba.druid.pool.DruidDataSource
-datasource.master0.driverClassName=com.mysql.cj.jdbc.Driver
-datasource.master0.url=jdbc:mysql://8.137.22.229:3306/playpai_community_world_cup?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai
-datasource.master0.username=root
-datasource.master0.password=playpai2023!
-datasource.master0.maxActive=20
-datasource.master0.maxWait=60000
-datasource.master0.minIdle=5
-datasource.master0.initialSize=2
-
-
#datasource.names=master0
#datasource.master0.type=com.alibaba.druid.pool.DruidDataSource
@@ -23,4 +11,13 @@
#datasource.master0.initialSize=2
-
+datasource.names=master0
+datasource.master0.type=com.alibaba.druid.pool.DruidDataSource
+datasource.master0.driverClassName=com.mysql.cj.jdbc.Driver
+datasource.master0.url=jdbc:mysql://127.0.0.1:3306/playpai_community_world_cup?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai
+datasource.master0.username=root
+datasource.master0.password=playpai2023!
+datasource.master0.maxActive=20
+datasource.master0.maxWait=60000
+datasource.master0.minIdle=5
+datasource.master0.initialSize=2
diff --git a/cloud-server-competition/src/main/java/com/dsh/competition/controller/CompetitionController.java b/cloud-server-competition/src/main/java/com/dsh/competition/controller/CompetitionController.java
index b52f889..208c8bc 100644
--- a/cloud-server-competition/src/main/java/com/dsh/competition/controller/CompetitionController.java
+++ b/cloud-server-competition/src/main/java/com/dsh/competition/controller/CompetitionController.java
@@ -21,6 +21,7 @@
import com.dsh.competition.feignclient.course.model.PaymentDeductionClassHour;
import com.dsh.competition.feignclient.model.*;
import com.dsh.competition.feignclient.other.StoreClient;
+import com.dsh.competition.feignclient.other.model.Store;
import com.dsh.competition.model.*;
import com.dsh.competition.service.CompetitionService;
import com.dsh.competition.service.IParticipantService;
@@ -667,6 +668,64 @@
e.printStackTrace();
}
}
+ /**
+ * 取消已报名赛事后微信回退金额回调微信V3服务商版本
+ *
+ * @param request
+ * @param response
+ */
+ @ResponseBody
+ @PostMapping("/base/competition/weChatCancelPaymentCompetitionCallback1")
+ public void weChatCancelPaymentCompetitionCallback1(HttpServletRequest request, HttpServletResponse response) {
+ try {
+ System.err.println("微信回调");
+ System.err.println("请求" + request);
+ BufferedReader reader = request.getReader();
+ String string1 = reader.toString();
+ System.err.println("请求reader" + string1);
+ StringBuilder requestBody = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ requestBody.append(line);
+ }
+ System.err.println("全部请求体" + requestBody);
+ JSONObject jsonObject = JSONObject.parseObject(requestBody.toString());
+ JSONObject resource = jsonObject.getJSONObject("resource");
+
+ AesUtil aesUtil = new AesUtil(WxV3PayConfig.apiV3Key.getBytes(StandardCharsets.UTF_8));
+ String decryptedData = aesUtil.decryptToString(resource.getString("associated_data").getBytes(StandardCharsets.UTF_8), resource.getString("nonce").getBytes(StandardCharsets.UTF_8),
+ resource.getString("ciphertext"));
+ System.err.println("微信解密的字符串信息" + decryptedData);
+ JSONObject jsonInfo = (JSONObject) JSONObject.parse(decryptedData);
+ String code = jsonInfo.getString("out_trade_no");
+ String transaction_id = jsonInfo.getString("transaction_id");
+ String refund_status = jsonInfo.getString("refund_status");
+ String out_refund_no = jsonInfo.getString("out_refund_no");
+ if (refund_status.equals("SUCCESS")) {
+ PaymentCompetition paymentCompetition = paymentCompetitionService.getOne(new QueryWrapper<PaymentCompetition>()
+ .eq("code", code).eq("payType", 1));
+ if (paymentCompetition.getPayStatus() == 2) {
+ paymentCompetition.setPayStatus(3);
+ paymentCompetition.setRefundTime(new Date());
+ paymentCompetition.setRefundOrderNo(out_refund_no);
+ paymentCompetition.setAppUserId(null);
+ paymentCompetitionService.updateById(paymentCompetition);
+
+ Competition competition = cttService.getById(paymentCompetition.getCompetitionId());
+ competition.setApplicantsNumber(competition.getApplicantsNumber() - 1);
+ cttService.updateById(competition);
+ storeClient.addBackRecord(paymentCompetition.getAmount() + "_" + paymentCompetition.getAppUserId());
+
+ }
+ PrintWriter out = response.getWriter();
+ out.write("SUCCESS");
+ out.flush();
+ out.close();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
@PostMapping("/base/competition/queryById")
@@ -859,46 +918,32 @@
String code = paymentCompetition.getCode();
Double amount = paymentCompetition.getAmount();
Competition competition = competitionService.getById(paymentCompetition.getCompetitionId());
-
-
+ Store store = storeClient.queryStoreById(Integer.valueOf(competition.getStoreId().split(",")[0]));
String payOrderNo = paymentCompetition.getPayOrderNo();
if (paymentCompetition.getPayType() == 1) {//微信支付
- if (StringUtils.hasLength(paymentCompetition.getFenzhangNo())){
- // 是分账订单 如果分账金额不为0 那么回退分账金额
- if (paymentCompetition.getFenzhangAmount()!=null && paymentCompetition.getFenzhangAmount().compareTo(BigDecimal.ZERO)>0){
- String randomCode = UUIDUtil.getRandomCode(16);
- String randomCode1 = UUIDUtil.getRandomCode(16);
- if (competition.getOperatorId()!=null && competition.getOperatorId()!=0){
- // 微信商户号
- String s2 =storeClient.getmerchantNumberByOperatorId(competition.getOperatorId());
- ResultUtil resultUtil = payMoneyUtil.fenzhangRefund(paymentCompetition.getFenzhangNo(), paymentCompetition.getFenzhangAmount(), s2, randomCode, randomCode1);
- if (!resultUtil.getCode().equals(200)){
- System.err.println("分账回退失败 原因是:"+resultUtil.getMsg());
- }else{
- paymentCompetition.setFenzhangRefundNo(resultUtil.getData().toString());
- paymentCompetitionService.updateById(paymentCompetition);
- }
-
- }
+ if (store.getOperatorId()!=null && store.getOperatorId()!=0){
+ String smidVx= storeClient.getmerchantNumberByOperatorId(store.getOperatorId());
+ if (!StringUtils.hasLength(smidVx)){
+ System.err.println("运营商未配置微信商户号,获取支付失败!");
}
- }
-
-
-
-
- Map<String, String> map = payMoneyUtil.wxRefund(payOrderNo, code, amount.toString(), amount.toString(),
- "/base/competition/weChatCancelPaymentCompetitionCallback");
- String return_code = map.get("return_code");
- if (!"SUCCESS".equals(return_code)) {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
+ String codeRefund = sdf.format(new Date()) + UUIDUtil.getNumberRandom(5);// 退款单号
+ // 运营商退款
+ payMoneyUtil.weixinRefundV3(smidVx, codeRefund, pay.getPayOrderNo(), pay.getAmount().toString(), "/base/competition/weChatCancelPaymentCompetitionCallback1");
+ }else {
+ Map<String, String> map = payMoneyUtil.wxRefund(payOrderNo, code, amount.toString(), amount.toString(),
+ "/base/competition/weChatCancelPaymentCompetitionCallback");
+ String return_code = map.get("return_code");
+ if (!"SUCCESS".equals(return_code)) {
// return ResultUtil.error(map.get("return_msg"));
- continue;
+ continue;
+ }
+ String refund_id = map.get("refund_id");
+ paymentCompetition.setRefundOrderNo(refund_id);
+ paymentCompetition.setAppUserId(null);
+ paymentCompetitionService.updateById(paymentCompetition);
+ storeClient.addBackRecord(paymentCompetition.getAmount() + "_" + paymentCompetition.getAppUserId());
}
- String refund_id = map.get("refund_id");
- paymentCompetition.setRefundOrderNo(refund_id);
- paymentCompetition.setAppUserId(null);
- paymentCompetitionService.updateById(paymentCompetition);
- storeClient.addBackRecord(paymentCompetition.getAmount() + "_" + paymentCompetition.getAppUserId());
-
}
if (paymentCompetition.getPayType() == 2) {//支付宝支付
Map<String, String> map = payMoneyUtil.aliRefund(payOrderNo, amount.toString());
diff --git a/cloud-server-competition/src/main/java/com/dsh/competition/service/impl/PaymentCompetitionServiceImpl.java b/cloud-server-competition/src/main/java/com/dsh/competition/service/impl/PaymentCompetitionServiceImpl.java
index 81b6967..f7914ad 100644
--- a/cloud-server-competition/src/main/java/com/dsh/competition/service/impl/PaymentCompetitionServiceImpl.java
+++ b/cloud-server-competition/src/main/java/com/dsh/competition/service/impl/PaymentCompetitionServiceImpl.java
@@ -248,38 +248,32 @@
}
String payOrderNo = paymentCompetition.getPayOrderNo();
+ Store store = storeClient.queryStoreById(Integer.valueOf(competition.getStoreId().split(",")[0]));
+
if (paymentCompetition.getPayType() == 1) {//微信支付
- if (StringUtils.hasLength(paymentCompetition.getFenzhangNo())){
- // 是分账订单 如果分账金额不为0 那么回退分账金额
- if (paymentCompetition.getFenzhangAmount()!=null && paymentCompetition.getFenzhangAmount().compareTo(BigDecimal.ZERO)>0){
- String randomCode = UUIDUtil.getRandomCode(16);
- String randomCode1 = UUIDUtil.getRandomCode(16);
- if (competition.getOperatorId()!=null && competition.getOperatorId()!=0){
- // 微信商户号
- String s2 =storeClient.getmerchantNumberByOperatorId(competition.getOperatorId());
- ResultUtil resultUtil = payMoneyUtil.fenzhangRefund(paymentCompetition.getFenzhangNo(), paymentCompetition.getFenzhangAmount(), s2, randomCode, randomCode1);
- if (!resultUtil.getCode().equals(200)){
- System.err.println("分账回退失败 原因是:"+resultUtil.getData().toString());
- }else{
- paymentCompetition.setFenzhangRefundNo(resultUtil.getData().toString());
- paymentCompetitionMapper.updateById(paymentCompetition);
- }
-
- }
+ if (store.getOperatorId()!=null && store.getOperatorId()!=0){
+ String smidVx= storeClient.getmerchantNumberByOperatorId(store.getOperatorId());
+ if (!StringUtils.hasLength(smidVx)){
+ System.err.println("运营商未配置微信商户号,获取支付失败!");
}
- }
- Map<String, String> map = payMoneyUtil.wxRefund(payOrderNo, code, amount.toString(), amount.toString(),
- "/base/competition/weChatCancelPaymentCompetitionCallback");
- String return_code = map.get("return_code");
- if (!"SUCCESS".equals(return_code)) {
- return ResultUtil.error(map.get("return_msg"));
- }
- String refund_id = map.get("refund_id");
- paymentCompetition.setRefundOrderNo(refund_id);
- paymentCompetition.setAppUserId(null);
- this.updateById(paymentCompetition);
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
+ String codeRefund = sdf.format(new Date()) + UUIDUtil.getNumberRandom(5);// 退款单号
+ // 运营商退款
+ payMoneyUtil.weixinRefundV3(smidVx, codeRefund, paymentCompetition.getPayOrderNo(), paymentCompetition.getAmount().toString(), "/base/competition/weChatCancelPaymentCompetitionCallback1");
+ }else {
+ Map<String, String> map = payMoneyUtil.wxRefund(payOrderNo, code, amount.toString(), amount.toString(),
+ "/base/competition/weChatCancelPaymentCompetitionCallback");
+ String return_code = map.get("return_code");
+ if (!"SUCCESS".equals(return_code)) {
+ return ResultUtil.error(map.get("return_msg"));
+ }
+ String refund_id = map.get("refund_id");
+ paymentCompetition.setRefundOrderNo(refund_id);
+ paymentCompetition.setAppUserId(null);
+ this.updateById(paymentCompetition);
- storeClient.addBackRecord(paymentCompetition.getAmount() + "_" + paymentCompetition.getAppUserId());
+ storeClient.addBackRecord(paymentCompetition.getAmount() + "_" + paymentCompetition.getAppUserId());
+ }
}
if (paymentCompetition.getPayType() == 2) {//支付宝支付
Map<String, String> map = payMoneyUtil.aliRefund(payOrderNo, amount.toString());
diff --git a/cloud-server-competition/src/main/java/com/dsh/competition/util/PayMoneyUtil.java b/cloud-server-competition/src/main/java/com/dsh/competition/util/PayMoneyUtil.java
index 7f95b70..fa7103f 100644
--- a/cloud-server-competition/src/main/java/com/dsh/competition/util/PayMoneyUtil.java
+++ b/cloud-server-competition/src/main/java/com/dsh/competition/util/PayMoneyUtil.java
@@ -8,10 +8,7 @@
import com.alipay.api.request.*;
import com.alipay.api.response.*;
import com.dsh.competition.util.httpClinet.HttpClientUtil;
-import com.dsh.competition.util.wx.PartnerAppPrepay;
-import com.dsh.competition.util.wx.WXPayUtility;
-import com.dsh.competition.util.wx.WeChatV3SignUtil;
-import com.dsh.competition.util.wx.WxV3PayConfig;
+import com.dsh.competition.util.wx.*;
import org.apache.commons.collections.map.HashedMap;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.dom4j.Document;
@@ -848,6 +845,39 @@
return map2;
}
}
+ public ResultUtil weixinRefundV3(String subMchid,String outRefundNo, String transactionId, String totalFee, String notifyUrl) throws Exception {
+ BigDecimal bigDecimal = new BigDecimal(totalFee);
+ int i = bigDecimal.multiply(new BigDecimal("100")).intValue();
+ // TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/partner/4013080340
+ Create client = new Create(
+ "1681873607", // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756
+ "55714944F7A7E52526F708280B176DCC838F371A", // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
+ "/usr/playpai/server/wxV3/1681873607_20250424_cert/apiclient_key.pem", // 商户API证书私钥文件路径,本地文件路径
+ "PUB_KEY_ID_0116818736072025042400351694002605", // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
+ "/usr/playpai/server/wxV3/pub_key.pem" // 微信支付公钥文件路径,本地文件路径
+ );
+
+ Create.CreateRequest request = new Create.CreateRequest();
+ request.subMchid = subMchid;
+ request.transactionId = transactionId;
+ request.outRefundNo = outRefundNo;
+ request.reason = "退款";
+ request.notifyUrl = callbackPath+notifyUrl;
+ request.amount = new Create.AmountReq();
+ request.amount.refund = (long) i;
+ request.amount.total = (long) i;
+ request.amount.currency = "CNY";
+ try {
+ Create.Refund response = client.run(request);
+
+ // TODO: 请求成功,继续业务逻辑
+ System.out.println(response);
+ } catch (WXPayUtility.ApiException e) {
+ // TODO: 请求失败,根据状态码执行不同的逻辑
+ e.printStackTrace();
+ }
+ return ResultUtil.success();
+ }
/**
* 发起分账回退
* @param order 微信订单号
diff --git a/cloud-server-competition/src/main/java/com/dsh/competition/util/wx/Create.java b/cloud-server-competition/src/main/java/com/dsh/competition/util/wx/Create.java
new file mode 100644
index 0000000..7f6af48
--- /dev/null
+++ b/cloud-server-competition/src/main/java/com/dsh/competition/util/wx/Create.java
@@ -0,0 +1,292 @@
+package com.dsh.competition.util.wx;
+
+import com.google.gson.annotations.SerializedName;
+import okhttp3.*;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.List;
+
+/**
+ * 退款申请
+ */
+public class Create {
+ private static String HOST = "https://api.mch.weixin.qq.com";
+ private static String METHOD = "POST";
+ private static String PATH = "/v3/refund/domestic/refunds";
+
+
+ public Refund run(CreateRequest request) {
+ String uri = PATH;
+ String reqBody = WXPayUtility.toJson(request);
+
+ Request.Builder reqBuilder = new Request.Builder().url(HOST + uri);
+ reqBuilder.addHeader("Accept", "application/json");
+ reqBuilder.addHeader("Wechatpay-Serial", wechatPayPublicKeyId);
+ reqBuilder.addHeader("Authorization", WXPayUtility.buildAuthorization(mchid, certificateSerialNo, privateKey, METHOD, uri, reqBody));
+ reqBuilder.addHeader("Content-Type", "application/json");
+ RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), reqBody);
+ reqBuilder.method(METHOD, requestBody);
+ Request httpRequest = reqBuilder.build();
+
+ // 发送HTTP请求
+ OkHttpClient client = new OkHttpClient.Builder().build();
+ try (Response httpResponse = client.newCall(httpRequest).execute()) {
+ String respBody = WXPayUtility.extractBody(httpResponse);
+ if (httpResponse.code() >= 200 && httpResponse.code() < 300) {
+ // 2XX 成功,验证应答签名
+ WXPayUtility.validateResponse(this.wechatPayPublicKeyId, this.wechatPayPublicKey,
+ httpResponse.headers(), respBody);
+
+ // 从HTTP应答报文构建返回数据
+ return WXPayUtility.fromJson(respBody, Refund.class);
+ } else {
+ throw new WXPayUtility.ApiException(httpResponse.code(), respBody, httpResponse.headers());
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException("Sending request to " + uri + " failed.", e);
+ }
+ }
+
+ private final String mchid;
+ private final String certificateSerialNo;
+ private final PrivateKey privateKey;
+ private final String wechatPayPublicKeyId;
+ private final PublicKey wechatPayPublicKey;
+
+ public Create(String mchid, String certificateSerialNo, String privateKeyFilePath, String wechatPayPublicKeyId, String wechatPayPublicKeyFilePath) {
+ this.mchid = mchid;
+ this.certificateSerialNo = certificateSerialNo;
+ this.privateKey = WXPayUtility.loadPrivateKeyFromPath(privateKeyFilePath);
+ this.wechatPayPublicKeyId = wechatPayPublicKeyId;
+ this.wechatPayPublicKey = WXPayUtility.loadPublicKeyFromPath(wechatPayPublicKeyFilePath);
+ }
+
+ public enum Status {
+ @SerializedName("SUCCESS")
+ SUCCESS,
+ @SerializedName("CLOSED")
+ CLOSED,
+ @SerializedName("PROCESSING")
+ PROCESSING,
+ @SerializedName("ABNORMAL")
+ ABNORMAL
+ }
+
+ public enum Account {
+ @SerializedName("AVAILABLE")
+ AVAILABLE,
+ @SerializedName("UNAVAILABLE")
+ UNAVAILABLE
+ }
+
+ public enum PromotionType {
+ @SerializedName("COUPON")
+ COUPON,
+ @SerializedName("DISCOUNT")
+ DISCOUNT
+ }
+
+ public static class GoodsDetail {
+ @SerializedName("merchant_goods_id")
+ public String merchantGoodsId;
+
+ @SerializedName("wechatpay_goods_id")
+ public String wechatpayGoodsId;
+
+ @SerializedName("goods_name")
+ public String goodsName;
+
+ @SerializedName("unit_price")
+ public Long unitPrice;
+
+ @SerializedName("refund_amount")
+ public Long refundAmount;
+
+ @SerializedName("refund_quantity")
+ public Integer refundQuantity;
+ }
+
+ public static class CreateRequest {
+ @SerializedName("sub_mchid")
+ public String subMchid;
+
+ @SerializedName("transaction_id")
+ public String transactionId;
+
+ @SerializedName("out_trade_no")
+ public String outTradeNo;
+
+ @SerializedName("out_refund_no")
+ public String outRefundNo;
+
+ @SerializedName("reason")
+ public String reason;
+
+ @SerializedName("notify_url")
+ public String notifyUrl;
+
+ @SerializedName("funds_account")
+ public ReqFundsAccount fundsAccount;
+
+ @SerializedName("amount")
+ public AmountReq amount;
+
+ @SerializedName("goods_detail")
+ public List<GoodsDetail> goodsDetail;
+ }
+
+ public enum Channel {
+ @SerializedName("ORIGINAL")
+ ORIGINAL,
+ @SerializedName("BALANCE")
+ BALANCE,
+ @SerializedName("OTHER_BALANCE")
+ OTHER_BALANCE,
+ @SerializedName("OTHER_BANKCARD")
+ OTHER_BANKCARD
+ }
+
+ public static class Amount {
+ @SerializedName("total")
+ public Long total;
+
+ @SerializedName("refund")
+ public Long refund;
+
+ @SerializedName("from")
+ public List<FundsFromItem> from;
+
+ @SerializedName("payer_total")
+ public Long payerTotal;
+
+ @SerializedName("payer_refund")
+ public Long payerRefund;
+
+ @SerializedName("settlement_refund")
+ public Long settlementRefund;
+
+ @SerializedName("settlement_total")
+ public Long settlementTotal;
+
+ @SerializedName("discount_refund")
+ public Long discountRefund;
+
+ @SerializedName("currency")
+ public String currency;
+
+ @SerializedName("refund_fee")
+ public Long refundFee;
+ }
+
+ public enum ReqFundsAccount {
+ @SerializedName("AVAILABLE")
+ AVAILABLE,
+ @SerializedName("UNSETTLED")
+ UNSETTLED
+ }
+
+ public enum FundsAccount {
+ @SerializedName("UNSETTLED")
+ UNSETTLED,
+ @SerializedName("AVAILABLE")
+ AVAILABLE,
+ @SerializedName("UNAVAILABLE")
+ UNAVAILABLE,
+ @SerializedName("OPERATION")
+ OPERATION,
+ @SerializedName("BASIC")
+ BASIC,
+ @SerializedName("ECNY_BASIC")
+ ECNY_BASIC
+ }
+
+ public static class Promotion {
+ @SerializedName("promotion_id")
+ public String promotionId;
+
+ @SerializedName("scope")
+ public PromotionScope scope;
+
+ @SerializedName("type")
+ public PromotionType type;
+
+ @SerializedName("amount")
+ public Long amount;
+
+ @SerializedName("refund_amount")
+ public Long refundAmount;
+
+ @SerializedName("goods_detail")
+ public List<GoodsDetail> goodsDetail;
+ }
+
+ public static class Refund {
+ @SerializedName("refund_id")
+ public String refundId;
+
+ @SerializedName("out_refund_no")
+ public String outRefundNo;
+
+ @SerializedName("transaction_id")
+ public String transactionId;
+
+ @SerializedName("out_trade_no")
+ public String outTradeNo;
+
+ @SerializedName("channel")
+ public Channel channel;
+
+ @SerializedName("user_received_account")
+ public String userReceivedAccount;
+
+ @SerializedName("success_time")
+ public String successTime;
+
+ @SerializedName("create_time")
+ public String createTime;
+
+ @SerializedName("status")
+ public Status status;
+
+ @SerializedName("funds_account")
+ public FundsAccount fundsAccount;
+
+ @SerializedName("amount")
+ public Amount amount;
+
+ @SerializedName("promotion_detail")
+ public List<Promotion> promotionDetail;
+ }
+
+ public static class FundsFromItem {
+ @SerializedName("account")
+ public Account account;
+
+ @SerializedName("amount")
+ public Long amount;
+ }
+
+ public static class AmountReq {
+ @SerializedName("refund")
+ public Long refund;
+
+ @SerializedName("from")
+ public List<FundsFromItem> from;
+
+ @SerializedName("total")
+ public Long total;
+
+ @SerializedName("currency")
+ public String currency;
+ }
+
+ public enum PromotionScope {
+ @SerializedName("GLOBAL")
+ GLOBAL,
+ @SerializedName("SINGLE")
+ SINGLE
+ }
+}
\ No newline at end of file
diff --git a/cloud-server-other/src/main/java/com/dsh/other/controller/SiteController.java b/cloud-server-other/src/main/java/com/dsh/other/controller/SiteController.java
index ca5439a..3a637db 100644
--- a/cloud-server-other/src/main/java/com/dsh/other/controller/SiteController.java
+++ b/cloud-server-other/src/main/java/com/dsh/other/controller/SiteController.java
@@ -709,6 +709,7 @@
})
public ResultUtil cancelMySite(Integer id) {
try {
+ System.err.println("预约数据id");
Integer uid = tokenUtil.getUserIdFormRedis();
if (null == uid) {
return ResultUtil.tokenErr();
@@ -756,7 +757,59 @@
e.printStackTrace();
}
}
+ /**
+ * 微信退款回调V3服务商版本
+ *
+ * @param request
+ * @param response
+ */
+ @ResponseBody
+ @PostMapping("/base/site/cancelMySiteCallback1")
+ public void cancelMySiteCallback1(HttpServletRequest request, HttpServletResponse response) {
+ try {
+ System.err.println("微信回调");
+ System.err.println("请求" + request);
+ BufferedReader reader = request.getReader();
+ String string1 = reader.toString();
+ System.err.println("请求reader" + string1);
+ StringBuilder requestBody = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ requestBody.append(line);
+ }
+ System.err.println("全部请求体" + requestBody);
+ JSONObject jsonObject = JSONObject.parseObject(requestBody.toString());
+ JSONObject resource = jsonObject.getJSONObject("resource");
+ AesUtil aesUtil = new AesUtil(WxV3PayConfig.apiV3Key.getBytes(StandardCharsets.UTF_8));
+ String decryptedData = aesUtil.decryptToString(resource.getString("associated_data").getBytes(StandardCharsets.UTF_8), resource.getString("nonce").getBytes(StandardCharsets.UTF_8),
+ resource.getString("ciphertext"));
+ System.err.println("微信解密的字符串信息" + decryptedData);
+ JSONObject jsonInfo = (JSONObject) JSONObject.parse(decryptedData);
+ String code = jsonInfo.getString("out_trade_no");
+ String transaction_id = jsonInfo.getString("transaction_id");
+ String refund_status = jsonInfo.getString("refund_status");
+ String out_refund_no = jsonInfo.getString("out_refund_no");
+ if (refund_status.equals("SUCCESS")) {
+ SiteBooking siteBooking = siteBookingService.getOne(new QueryWrapper<SiteBooking>().eq("orderNo", code).eq("state", 1));
+ siteBooking.setStatus(5);
+ siteBooking.setCancelTime(new Date());
+ siteBooking.setRefundOrderNo(out_refund_no);
+ siteBookingService.updateById(siteBooking);
+ if (null != siteBooking.getUserCouponId()) {
+ UserCoupon userCoupon = userCouponClient.queryUserCouponById(new QueryUserCouponByIdAndUserId(siteBooking.getUserCouponId(), siteBooking.getAppUserId()));
+ userCoupon.setStatus(1);
+ userCouponClient.updateUserCoupon(userCoupon);
+ }
+ PrintWriter out = response.getWriter();
+ out.write("SUCCESS");
+ out.flush();
+ out.close();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
@ResponseBody
@PostMapping("/api/site/continuePaymentMySite")
diff --git a/cloud-server-other/src/main/java/com/dsh/other/service/impl/SiteServiceImpl.java b/cloud-server-other/src/main/java/com/dsh/other/service/impl/SiteServiceImpl.java
index 95a170c..f0ca39a 100644
--- a/cloud-server-other/src/main/java/com/dsh/other/service/impl/SiteServiceImpl.java
+++ b/cloud-server-other/src/main/java/com/dsh/other/service/impl/SiteServiceImpl.java
@@ -65,6 +65,7 @@
@Resource
private UserCouponClient userCouponClient;
+
@Resource
private CouponClient couponClient;
@@ -708,43 +709,39 @@
if (siteBooking.getStatus() != 1 && siteBooking.getStatus() != 2) {
return ResultUtil.error("不能进行取消操作");
}
+ Integer storeId = siteBooking.getStoreId();
+ Store store = storeService.getById(storeId);
+
if (siteBooking.getPayType() == 1) {//微信支付
- if (StringUtils.hasLength(siteBooking.getFenzhangNo())){
- // 是分账订单 如果分账金额不为0 那么回退分账金额
- if (siteBooking.getFenzhangAmount()!=null && siteBooking.getFenzhangAmount().compareTo(BigDecimal.ZERO)>0){
- String randomCode = UUIDUtil.getRandomCode(16);
- String randomCode1 = UUIDUtil.getRandomCode(16);
- if (byId.getOperatorId()!=null && byId.getOperatorId()!=0){
- // 微信商户号
- String s2 =siteService.getmerchantNumberByOperatorId(byId.getOperatorId());
- ResultUtil resultUtil = payMoneyUtil.fenzhangRefund(siteBooking.getFenzhangNo(), siteBooking.getFenzhangAmount().multiply(new BigDecimal("100")), s2, randomCode, randomCode1);
- if (!resultUtil.getCode().equals(200)){
- System.err.println("分账回退失败 原因是:"+resultUtil.getMsg()+resultUtil.getData());
- }else{
- siteBooking.setFenzhangRefundNo(resultUtil.getMsg());
- siteBookingService.updateById(siteBooking);
- }
-
- }
+ if (store.getOperatorId()!=null && store.getOperatorId()!=0){
+ String smidVx= operatorUserService.getmerchantNumberByOperatorId(store.getOperatorId());
+ if (!StringUtils.hasLength(smidVx)){
+ return ResultUtil.error("运营商未配置微信商户号,获取支付失败!");
}
- }
- Map<String, String> map = payMoneyUtil.wxRefund(siteBooking.getPayOrderNo(), siteBooking.getOrderNo(), siteBooking.getPayMoney().toString(),
- siteBooking.getPayMoney().toString(), "/base/site/cancelMySiteCallback");
- if (null == map) {
- return ResultUtil.error("取消退款异常");
- }
- String return_code = map.get("return_code");
- if (!"SUCCESS".equals(return_code)) {
- return ResultUtil.error(map.get("return_msg"));
- }
- siteBooking.setCancelUserId(uid);
- siteBookingService.updateById(siteBooking);
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
+ String code = sdf.format(new Date()) + UUIDUtil.getNumberRandom(5);// 退款单号
+ // 运营商退款
+ payMoneyUtil.weixinRefundV3(smidVx, code, siteBooking.getPayOrderNo(), siteBooking.getPayMoney().toString(), "/base/site/cancelMySiteCallback1");
+ }else{
+ Map<String, String> map = payMoneyUtil.wxRefund(siteBooking.getPayOrderNo(), siteBooking.getOrderNo(), siteBooking.getPayMoney().toString(),
+ siteBooking.getPayMoney().toString(), "/base/site/cancelMySiteCallback");
+ if (null == map) {
+ return ResultUtil.error("取消退款异常");
+ }
+ String return_code = map.get("return_code");
+ if (!"SUCCESS".equals(return_code)) {
+ return ResultUtil.error(map.get("return_msg"));
+ }
+ siteBooking.setCancelUserId(uid);
+ siteBookingService.updateById(siteBooking);
- TBackRecord tBackRecord = new TBackRecord();
- tBackRecord.setMoney(siteBooking.getPayMoney());
- tBackRecord.setUserId(siteBooking.getAppUserId());
- tBackRecord.setTime(new Date());
- backRecordService.save(tBackRecord);
+ TBackRecord tBackRecord = new TBackRecord();
+ tBackRecord.setMoney(siteBooking.getPayMoney());
+ tBackRecord.setUserId(siteBooking.getAppUserId());
+ tBackRecord.setTime(new Date());
+ backRecordService.save(tBackRecord);
+ }
+
}
if (siteBooking.getPayType() == 2) {//支付宝
diff --git a/cloud-server-other/src/main/java/com/dsh/other/util/PayMoneyUtil.java b/cloud-server-other/src/main/java/com/dsh/other/util/PayMoneyUtil.java
index 1d7e21d..f36d263 100644
--- a/cloud-server-other/src/main/java/com/dsh/other/util/PayMoneyUtil.java
+++ b/cloud-server-other/src/main/java/com/dsh/other/util/PayMoneyUtil.java
@@ -5,15 +5,9 @@
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.*;
import com.alipay.api.domain.*;
-import com.alipay.api.msg.AlipayMsgClient;
-import com.alipay.api.msg.MsgHandler;
import com.alipay.api.request.*;
import com.alipay.api.response.*;
-import com.dsh.other.util.wx.PartnerAppPrepay;
-import com.dsh.other.util.wx.WXPayUtility;
-import com.dsh.other.util.wx.WeChatV3SignUtil;
-import com.dsh.other.util.wx.WxV3PayConfig;
-import lombok.Synchronized;
+import com.dsh.other.util.wx.*;
import org.apache.commons.collections.map.HashedMap;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.dom4j.Document;
@@ -23,9 +17,7 @@
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
-import org.springframework.util.StringUtils;
-import javax.annotation.Resource;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
@@ -575,6 +567,39 @@
System.err.println(map3);
return ResultUtil.success(map3);
}
+ public ResultUtil weixinRefundV3(String subMchid,String outRefundNo, String transactionId, String totalFee, String notifyUrl) throws Exception {
+ BigDecimal bigDecimal = new BigDecimal(totalFee);
+ int i = bigDecimal.multiply(new BigDecimal("100")).intValue();
+ // TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/partner/4013080340
+ Create client = new Create(
+ "1681873607", // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756
+ "55714944F7A7E52526F708280B176DCC838F371A", // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
+ "/usr/playpai/server/wxV3/1681873607_20250424_cert/apiclient_key.pem", // 商户API证书私钥文件路径,本地文件路径
+ "PUB_KEY_ID_0116818736072025042400351694002605", // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
+ "/usr/playpai/server/wxV3/pub_key.pem" // 微信支付公钥文件路径,本地文件路径
+ );
+
+ Create.CreateRequest request = new Create.CreateRequest();
+ request.subMchid = subMchid;
+ request.transactionId = transactionId;
+ request.outRefundNo = outRefundNo;
+ request.reason = "退款";
+ request.notifyUrl = callbackPath+notifyUrl;
+ request.amount = new Create.AmountReq();
+ request.amount.refund = (long) i;
+ request.amount.total = (long) i;
+ request.amount.currency = "CNY";
+ try {
+ Create.Refund response = client.run(request);
+
+ // TODO: 请求成功,继续业务逻辑
+ System.out.println(response);
+ } catch (WXPayUtility.ApiException e) {
+ // TODO: 请求失败,根据状态码执行不同的逻辑
+ e.printStackTrace();
+ }
+ return ResultUtil.success();
+ }
/**
* 发起分账
diff --git a/cloud-server-other/src/main/java/com/dsh/other/util/wx/Create.java b/cloud-server-other/src/main/java/com/dsh/other/util/wx/Create.java
new file mode 100644
index 0000000..f693fc7
--- /dev/null
+++ b/cloud-server-other/src/main/java/com/dsh/other/util/wx/Create.java
@@ -0,0 +1,300 @@
+package com.dsh.other.util.wx;
+
+import com.dsh.other.util.wx.WXPayUtility;
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import okhttp3.MediaType;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+
+/**
+ * 退款申请
+ */
+public class Create {
+ private static String HOST = "https://api.mch.weixin.qq.com";
+ private static String METHOD = "POST";
+ private static String PATH = "/v3/refund/domestic/refunds";
+
+
+ public Refund run(CreateRequest request) {
+ String uri = PATH;
+ String reqBody = WXPayUtility.toJson(request);
+
+ Request.Builder reqBuilder = new Request.Builder().url(HOST + uri);
+ reqBuilder.addHeader("Accept", "application/json");
+ reqBuilder.addHeader("Wechatpay-Serial", wechatPayPublicKeyId);
+ reqBuilder.addHeader("Authorization", WXPayUtility.buildAuthorization(mchid, certificateSerialNo, privateKey, METHOD, uri, reqBody));
+ reqBuilder.addHeader("Content-Type", "application/json");
+ RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), reqBody);
+ reqBuilder.method(METHOD, requestBody);
+ Request httpRequest = reqBuilder.build();
+
+ // 发送HTTP请求
+ OkHttpClient client = new OkHttpClient.Builder().build();
+ try (Response httpResponse = client.newCall(httpRequest).execute()) {
+ String respBody = WXPayUtility.extractBody(httpResponse);
+ if (httpResponse.code() >= 200 && httpResponse.code() < 300) {
+ // 2XX 成功,验证应答签名
+ WXPayUtility.validateResponse(this.wechatPayPublicKeyId, this.wechatPayPublicKey,
+ httpResponse.headers(), respBody);
+
+ // 从HTTP应答报文构建返回数据
+ return WXPayUtility.fromJson(respBody, Refund.class);
+ } else {
+ throw new WXPayUtility.ApiException(httpResponse.code(), respBody, httpResponse.headers());
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException("Sending request to " + uri + " failed.", e);
+ }
+ }
+
+ private final String mchid;
+ private final String certificateSerialNo;
+ private final PrivateKey privateKey;
+ private final String wechatPayPublicKeyId;
+ private final PublicKey wechatPayPublicKey;
+
+ public Create(String mchid, String certificateSerialNo, String privateKeyFilePath, String wechatPayPublicKeyId, String wechatPayPublicKeyFilePath) {
+ this.mchid = mchid;
+ this.certificateSerialNo = certificateSerialNo;
+ this.privateKey = WXPayUtility.loadPrivateKeyFromPath(privateKeyFilePath);
+ this.wechatPayPublicKeyId = wechatPayPublicKeyId;
+ this.wechatPayPublicKey = WXPayUtility.loadPublicKeyFromPath(wechatPayPublicKeyFilePath);
+ }
+
+ public enum Status {
+ @SerializedName("SUCCESS")
+ SUCCESS,
+ @SerializedName("CLOSED")
+ CLOSED,
+ @SerializedName("PROCESSING")
+ PROCESSING,
+ @SerializedName("ABNORMAL")
+ ABNORMAL
+ }
+
+ public enum Account {
+ @SerializedName("AVAILABLE")
+ AVAILABLE,
+ @SerializedName("UNAVAILABLE")
+ UNAVAILABLE
+ }
+
+ public enum PromotionType {
+ @SerializedName("COUPON")
+ COUPON,
+ @SerializedName("DISCOUNT")
+ DISCOUNT
+ }
+
+ public static class GoodsDetail {
+ @SerializedName("merchant_goods_id")
+ public String merchantGoodsId;
+
+ @SerializedName("wechatpay_goods_id")
+ public String wechatpayGoodsId;
+
+ @SerializedName("goods_name")
+ public String goodsName;
+
+ @SerializedName("unit_price")
+ public Long unitPrice;
+
+ @SerializedName("refund_amount")
+ public Long refundAmount;
+
+ @SerializedName("refund_quantity")
+ public Integer refundQuantity;
+ }
+
+ public static class CreateRequest {
+ @SerializedName("sub_mchid")
+ public String subMchid;
+
+ @SerializedName("transaction_id")
+ public String transactionId;
+
+ @SerializedName("out_trade_no")
+ public String outTradeNo;
+
+ @SerializedName("out_refund_no")
+ public String outRefundNo;
+
+ @SerializedName("reason")
+ public String reason;
+
+ @SerializedName("notify_url")
+ public String notifyUrl;
+
+ @SerializedName("funds_account")
+ public ReqFundsAccount fundsAccount;
+
+ @SerializedName("amount")
+ public AmountReq amount;
+
+ @SerializedName("goods_detail")
+ public List<GoodsDetail> goodsDetail;
+ }
+
+ public enum Channel {
+ @SerializedName("ORIGINAL")
+ ORIGINAL,
+ @SerializedName("BALANCE")
+ BALANCE,
+ @SerializedName("OTHER_BALANCE")
+ OTHER_BALANCE,
+ @SerializedName("OTHER_BANKCARD")
+ OTHER_BANKCARD
+ }
+
+ public static class Amount {
+ @SerializedName("total")
+ public Long total;
+
+ @SerializedName("refund")
+ public Long refund;
+
+ @SerializedName("from")
+ public List<FundsFromItem> from;
+
+ @SerializedName("payer_total")
+ public Long payerTotal;
+
+ @SerializedName("payer_refund")
+ public Long payerRefund;
+
+ @SerializedName("settlement_refund")
+ public Long settlementRefund;
+
+ @SerializedName("settlement_total")
+ public Long settlementTotal;
+
+ @SerializedName("discount_refund")
+ public Long discountRefund;
+
+ @SerializedName("currency")
+ public String currency;
+
+ @SerializedName("refund_fee")
+ public Long refundFee;
+ }
+
+ public enum ReqFundsAccount {
+ @SerializedName("AVAILABLE")
+ AVAILABLE,
+ @SerializedName("UNSETTLED")
+ UNSETTLED
+ }
+
+ public enum FundsAccount {
+ @SerializedName("UNSETTLED")
+ UNSETTLED,
+ @SerializedName("AVAILABLE")
+ AVAILABLE,
+ @SerializedName("UNAVAILABLE")
+ UNAVAILABLE,
+ @SerializedName("OPERATION")
+ OPERATION,
+ @SerializedName("BASIC")
+ BASIC,
+ @SerializedName("ECNY_BASIC")
+ ECNY_BASIC
+ }
+
+ public static class Promotion {
+ @SerializedName("promotion_id")
+ public String promotionId;
+
+ @SerializedName("scope")
+ public PromotionScope scope;
+
+ @SerializedName("type")
+ public PromotionType type;
+
+ @SerializedName("amount")
+ public Long amount;
+
+ @SerializedName("refund_amount")
+ public Long refundAmount;
+
+ @SerializedName("goods_detail")
+ public List<GoodsDetail> goodsDetail;
+ }
+
+ public static class Refund {
+ @SerializedName("refund_id")
+ public String refundId;
+
+ @SerializedName("out_refund_no")
+ public String outRefundNo;
+
+ @SerializedName("transaction_id")
+ public String transactionId;
+
+ @SerializedName("out_trade_no")
+ public String outTradeNo;
+
+ @SerializedName("channel")
+ public Channel channel;
+
+ @SerializedName("user_received_account")
+ public String userReceivedAccount;
+
+ @SerializedName("success_time")
+ public String successTime;
+
+ @SerializedName("create_time")
+ public String createTime;
+
+ @SerializedName("status")
+ public Status status;
+
+ @SerializedName("funds_account")
+ public FundsAccount fundsAccount;
+
+ @SerializedName("amount")
+ public Amount amount;
+
+ @SerializedName("promotion_detail")
+ public List<Promotion> promotionDetail;
+ }
+
+ public static class FundsFromItem {
+ @SerializedName("account")
+ public Account account;
+
+ @SerializedName("amount")
+ public Long amount;
+ }
+
+ public static class AmountReq {
+ @SerializedName("refund")
+ public Long refund;
+
+ @SerializedName("from")
+ public List<FundsFromItem> from;
+
+ @SerializedName("total")
+ public Long total;
+
+ @SerializedName("currency")
+ public String currency;
+ }
+
+ public enum PromotionScope {
+ @SerializedName("GLOBAL")
+ GLOBAL,
+ @SerializedName("SINGLE")
+ SINGLE
+ }
+}
\ No newline at end of file
--
Gitblit v1.7.1