更新了商品管理模块
This commit is contained in:
parent
42a5e98f7a
commit
37c501ebfd
12
pom.xml
12
pom.xml
|
@ -178,12 +178,20 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- 微信支付-->
|
<!-- 微信支付-->
|
||||||
|
<!-- <dependency>-->
|
||||||
|
<!-- <groupId>com.github.wechatpay-apiv3</groupId>-->
|
||||||
|
<!-- <artifactId>wechatpay-java</artifactId>-->
|
||||||
|
<!-- <version>0.2.10</version>-->
|
||||||
|
<!-- </dependency>-->
|
||||||
|
|
||||||
|
<!--微信支付SDK-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.wechatpay-apiv3</groupId>
|
<groupId>com.github.wechatpay-apiv3</groupId>
|
||||||
<artifactId>wechatpay-java</artifactId>
|
<artifactId>wechatpay-apache-httpclient</artifactId>
|
||||||
<version>0.2.10</version>
|
<version>0.3.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,78 +1,78 @@
|
||||||
package com.cultural.heritage.config;
|
//package com.cultural.heritage.config;
|
||||||
|
//
|
||||||
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
|
//import com.wechat.pay.java.core.RSAAutoCertificateConfig;
|
||||||
import com.wechat.pay.java.core.util.IOUtil;
|
//import com.wechat.pay.java.core.util.IOUtil;
|
||||||
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
|
//import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
|
||||||
import com.wechat.pay.java.service.refund.RefundService;
|
//import com.wechat.pay.java.service.refund.RefundService;
|
||||||
import lombok.Data;
|
//import lombok.Data;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
//import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
//import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
import org.springframework.context.annotation.Bean;
|
//import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
//import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.core.io.ClassPathResource;
|
//import org.springframework.core.io.ClassPathResource;
|
||||||
import org.springframework.stereotype.Component;
|
//import org.springframework.stereotype.Component;
|
||||||
|
//
|
||||||
import java.io.IOException;
|
//import java.io.IOException;
|
||||||
|
//
|
||||||
@Data
|
//@Data
|
||||||
@Slf4j
|
//@Slf4j
|
||||||
@Configuration
|
//@Configuration
|
||||||
@Component("WxPayConfig")
|
//@Component("WxPayConfig")
|
||||||
@ConfigurationProperties(prefix = "wx.pay")
|
//@ConfigurationProperties(prefix = "wx.pay")
|
||||||
public class WxPayConfig {
|
//public class WxPayConfig {
|
||||||
|
//
|
||||||
private String appId;
|
// private String appId;
|
||||||
|
//
|
||||||
private String apiV3Key;
|
// private String apiV3Key;
|
||||||
|
//
|
||||||
private String notifyUrl;
|
// private String notifyUrl;
|
||||||
|
//
|
||||||
private String merchantId;
|
// private String merchantId;
|
||||||
|
//
|
||||||
private String privateKeyPath;
|
// private String privateKeyPath;
|
||||||
|
//
|
||||||
private String merchantSerialNumber;
|
// private String merchantSerialNumber;
|
||||||
|
//
|
||||||
// RSA配置
|
// // RSA配置
|
||||||
private RSAAutoCertificateConfig RSAConfig;
|
// private RSAAutoCertificateConfig RSAConfig;
|
||||||
|
//
|
||||||
// JSAPI支付
|
// // JSAPI支付
|
||||||
private JsapiServiceExtension jsapiServiceExtension;
|
// private JsapiServiceExtension jsapiServiceExtension;
|
||||||
|
//
|
||||||
// 退款
|
// // 退款
|
||||||
private RefundService refundService;
|
// private RefundService refundService;
|
||||||
|
//
|
||||||
/**
|
// /**
|
||||||
* 初始化配置
|
// * 初始化配置
|
||||||
*/
|
// */
|
||||||
@Bean
|
// @Bean
|
||||||
public boolean initWxPayConfig() throws IOException {
|
// public boolean initWxPayConfig() throws IOException {
|
||||||
this.RSAConfig = buildRSAAutoCertificateConfig();
|
// this.RSAConfig = buildRSAAutoCertificateConfig();
|
||||||
this.jsapiServiceExtension = buildJsapiServiceExtension(RSAConfig);
|
// this.jsapiServiceExtension = buildJsapiServiceExtension(RSAConfig);
|
||||||
this.refundService = buildRefundService(RSAConfig);
|
// this.refundService = buildRefundService(RSAConfig);
|
||||||
return true;
|
// return true;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
// 构建并使用自动更新平台证书的RSA配置,一个商户号只能初始化一个配置,否则会因为重复的下载任务报错
|
// // 构建并使用自动更新平台证书的RSA配置,一个商户号只能初始化一个配置,否则会因为重复的下载任务报错
|
||||||
private RSAAutoCertificateConfig buildRSAAutoCertificateConfig() throws IOException {
|
// private RSAAutoCertificateConfig buildRSAAutoCertificateConfig() throws IOException {
|
||||||
// 将 resource 目录下的文件转为 InputStream,然后利用 IOUtil.toString(inputStream) 转化为密钥
|
// // 将 resource 目录下的文件转为 InputStream,然后利用 IOUtil.toString(inputStream) 转化为密钥
|
||||||
String privateKey = IOUtil.toString(new ClassPathResource(privateKeyPath).getInputStream());
|
// String privateKey = IOUtil.toString(new ClassPathResource(privateKeyPath).getInputStream());
|
||||||
return new RSAAutoCertificateConfig.Builder()
|
// return new RSAAutoCertificateConfig.Builder()
|
||||||
.merchantId(merchantId)
|
// .merchantId(merchantId)
|
||||||
.privateKey(privateKey)
|
// .privateKey(privateKey)
|
||||||
.merchantSerialNumber(merchantSerialNumber)
|
// .merchantSerialNumber(merchantSerialNumber)
|
||||||
.apiV3Key(apiV3Key)
|
// .apiV3Key(apiV3Key)
|
||||||
.build();
|
// .build();
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
// 构建JSAPI支付
|
// // 构建JSAPI支付
|
||||||
private JsapiServiceExtension buildJsapiServiceExtension(RSAAutoCertificateConfig config) {
|
// private JsapiServiceExtension buildJsapiServiceExtension(RSAAutoCertificateConfig config) {
|
||||||
return new JsapiServiceExtension.Builder().config(config).build();
|
// return new JsapiServiceExtension.Builder().config(config).build();
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
// 构建退款
|
// // 构建退款
|
||||||
private RefundService buildRefundService(RSAAutoCertificateConfig config) {
|
// private RefundService buildRefundService(RSAAutoCertificateConfig config) {
|
||||||
return new RefundService.Builder().config(config).build();
|
// return new RefundService.Builder().config(config).build();
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
}
|
//}
|
||||||
|
|
|
@ -160,7 +160,7 @@ public class AppointmentDateController {
|
||||||
@Operation(summary = "Web端管理员添加预约时间段", description = "参数:预约时间段添加请求体,权限:管理员(admin, boss),方法名:addAppointmentDate")
|
@Operation(summary = "Web端管理员添加预约时间段", description = "参数:预约时间段添加请求体,权限:管理员(admin, boss),方法名:addAppointmentDate")
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
|
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
|
||||||
public BaseResponse<Boolean> addTimePeriod(@RequestBody TimePeriodSingleAddRequest timePeriodSingleAddRequest) {
|
public BaseResponse<Long> addTimePeriod(@RequestBody TimePeriodSingleAddRequest timePeriodSingleAddRequest) {
|
||||||
if (timePeriodSingleAddRequest == null) {
|
if (timePeriodSingleAddRequest == null) {
|
||||||
throw new BusinessException(ErrorCode.PARAMS_ERROR);
|
throw new BusinessException(ErrorCode.PARAMS_ERROR);
|
||||||
}
|
}
|
||||||
|
@ -168,7 +168,7 @@ public class AppointmentDateController {
|
||||||
BeanUtils.copyProperties(timePeriodSingleAddRequest, timePeriod);
|
BeanUtils.copyProperties(timePeriodSingleAddRequest, timePeriod);
|
||||||
boolean save = timePeriodService.save(timePeriod);
|
boolean save = timePeriodService.save(timePeriod);
|
||||||
ThrowUtils.throwIf(!save, ErrorCode.OPERATION_ERROR, "预约时间段添加失败");
|
ThrowUtils.throwIf(!save, ErrorCode.OPERATION_ERROR, "预约时间段添加失败");
|
||||||
return ResultUtils.success(true);
|
return ResultUtils.success(timePeriod.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,120 +1,120 @@
|
||||||
package com.cultural.heritage.controller.wx;
|
//package com.cultural.heritage.controller.wx;
|
||||||
|
//
|
||||||
|
//
|
||||||
import com.cultural.heritage.annotation.AuthCheck;
|
//import com.cultural.heritage.annotation.AuthCheck;
|
||||||
import com.cultural.heritage.common.BaseResponse;
|
//import com.cultural.heritage.common.BaseResponse;
|
||||||
import com.cultural.heritage.common.ErrorCode;
|
//import com.cultural.heritage.common.ErrorCode;
|
||||||
import com.cultural.heritage.common.ResultUtils;
|
//import com.cultural.heritage.common.ResultUtils;
|
||||||
import com.cultural.heritage.constant.UserConstant;
|
//import com.cultural.heritage.constant.UserConstant;
|
||||||
import com.cultural.heritage.exception.BusinessException;
|
//import com.cultural.heritage.exception.BusinessException;
|
||||||
import com.cultural.heritage.exception.ThrowUtils;
|
//import com.cultural.heritage.exception.ThrowUtils;
|
||||||
import com.cultural.heritage.model.dto.CommonRequest;
|
//import com.cultural.heritage.model.dto.CommonRequest;
|
||||||
import com.cultural.heritage.model.entity.Order;
|
//import com.cultural.heritage.model.entity.Order;
|
||||||
import com.cultural.heritage.model.entity.User;
|
//import com.cultural.heritage.model.entity.User;
|
||||||
import com.cultural.heritage.service.order.OrderService;
|
//import com.cultural.heritage.service.order.OrderService;
|
||||||
import com.cultural.heritage.service.user.UserService;
|
//import com.cultural.heritage.service.user.UserService;
|
||||||
import com.cultural.heritage.service.wxpay.WeChatService;
|
//import com.cultural.heritage.service.wxpay.WeChatService;
|
||||||
import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse;
|
//import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse;
|
||||||
import com.wechat.pay.java.service.payments.model.Transaction;
|
//import com.wechat.pay.java.service.payments.model.Transaction;
|
||||||
import com.wechat.pay.java.service.refund.model.Refund;
|
//import com.wechat.pay.java.service.refund.model.Refund;
|
||||||
import com.wechat.pay.java.service.refund.model.RefundNotification;
|
//import com.wechat.pay.java.service.refund.model.RefundNotification;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
//import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.annotation.Resource;
|
//import jakarta.annotation.Resource;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
//import jakarta.servlet.http.HttpServletRequest;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
//import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
//import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.web.bind.annotation.*;
|
//import org.springframework.web.bind.annotation.*;
|
||||||
|
//
|
||||||
import java.io.IOException;
|
//import java.io.IOException;
|
||||||
|
//
|
||||||
/**
|
///**
|
||||||
* 微信小程序相关接口
|
// * 微信小程序相关接口
|
||||||
**/
|
// **/
|
||||||
@Slf4j
|
//@Slf4j
|
||||||
@RestController
|
//@RestController
|
||||||
@Tag(name = "微信支付接口")
|
//@Tag(name = "微信支付接口")
|
||||||
@RequestMapping("/wechat")
|
//@RequestMapping("/wechat")
|
||||||
public class WeChatPayController {
|
//public class WeChatPayController {
|
||||||
|
//
|
||||||
@Resource
|
// @Resource
|
||||||
private UserService userService;
|
// private UserService userService;
|
||||||
|
//
|
||||||
@Resource
|
// @Resource
|
||||||
private OrderService ordersService;
|
// private OrderService ordersService;
|
||||||
|
//
|
||||||
@Resource
|
// @Resource
|
||||||
private WeChatService weChatService;
|
// private WeChatService weChatService;
|
||||||
|
//
|
||||||
|
//
|
||||||
/**
|
// /**
|
||||||
* JSAPI 下单
|
// * JSAPI 下单
|
||||||
*/
|
// */
|
||||||
@PostMapping("/payment/create")
|
// @PostMapping("/payment/create")
|
||||||
public BaseResponse<PrepayWithRequestPaymentResponse> createPayment(@RequestBody CommonRequest commonRequest, HttpServletRequest request) {
|
// public BaseResponse<PrepayWithRequestPaymentResponse> createPayment(@RequestBody CommonRequest commonRequest, HttpServletRequest request) {
|
||||||
User loginUser = userService.getLoginUser(request);
|
// User loginUser = userService.getLoginUser(request);
|
||||||
String miniOpenId = loginUser.getMiniOpenId();
|
// String miniOpenId = loginUser.getMiniOpenId();
|
||||||
ThrowUtils.throwIf(miniOpenId == null, ErrorCode.NOT_FOUND_ERROR, "不是小程序用户");
|
// ThrowUtils.throwIf(miniOpenId == null, ErrorCode.NOT_FOUND_ERROR, "不是小程序用户");
|
||||||
Long orderId = commonRequest.getId();
|
// Long orderId = commonRequest.getId();
|
||||||
Order order = ordersService.getById(orderId);
|
// Order order = ordersService.getById(orderId);
|
||||||
ThrowUtils.throwIf(order == null, ErrorCode.NOT_FOUND_ERROR, "订单不存在");
|
// ThrowUtils.throwIf(order == null, ErrorCode.NOT_FOUND_ERROR, "订单不存在");
|
||||||
// ThrowUtils.throwIf(order.getState() != 0, ErrorCode.OPERATION_ERROR, "订单状态错误");
|
//// ThrowUtils.throwIf(order.getState() != 0, ErrorCode.OPERATION_ERROR, "订单状态错误");
|
||||||
if (!loginUser.getId().equals(order.getUserId())) {
|
// if (!loginUser.getId().equals(order.getUserId())) {
|
||||||
throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "你不是该订单用户!");
|
// throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "你不是该订单用户!");
|
||||||
}
|
// }
|
||||||
PrepayWithRequestPaymentResponse response = weChatService.createPayment(String.valueOf(orderId), miniOpenId, order.getTotalAmount());
|
// PrepayWithRequestPaymentResponse response = weChatService.createPayment(String.valueOf(orderId), miniOpenId, order.getTotalAmount());
|
||||||
return ResultUtils.success(response);
|
// return ResultUtils.success(response);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
/**
|
// /**
|
||||||
* JSAPI 下单回调
|
// * JSAPI 下单回调
|
||||||
*/
|
// */
|
||||||
@PostMapping("/payment/callback")
|
// @PostMapping("/payment/callback")
|
||||||
@Transactional(rollbackFor = Exception.class)
|
// @Transactional(rollbackFor = Exception.class)
|
||||||
public synchronized BaseResponse<Boolean> callbackPayment(HttpServletRequest request) throws IOException {
|
// public synchronized BaseResponse<Boolean> callbackPayment(HttpServletRequest request) throws IOException {
|
||||||
// 获取下单信息
|
// // 获取下单信息
|
||||||
Transaction transaction = weChatService.getTransactionInfo(request);
|
// Transaction transaction = weChatService.getTransactionInfo(request);
|
||||||
System.out.println("下单信息:" + transaction);
|
// System.out.println("下单信息:" + transaction);
|
||||||
// 支付回调
|
// // 支付回调
|
||||||
boolean result = weChatService.paymentCallback(transaction);
|
// boolean result = weChatService.paymentCallback(transaction);
|
||||||
ThrowUtils.throwIf(!result, ErrorCode.SYSTEM_ERROR, "微信支付回调失败");
|
// ThrowUtils.throwIf(!result, ErrorCode.SYSTEM_ERROR, "微信支付回调失败");
|
||||||
return ResultUtils.success(true);
|
// return ResultUtils.success(true);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
/**
|
// /**
|
||||||
* 退款(仅管理员和商家)
|
// * 退款(仅管理员和商家)
|
||||||
*/
|
// */
|
||||||
@PostMapping("/refund/create")
|
// @PostMapping("/refund/create")
|
||||||
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
|
// @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
|
||||||
public BaseResponse<Refund> createRefund(@RequestBody CommonRequest commonRequest) {
|
// public BaseResponse<Refund> createRefund(@RequestBody CommonRequest commonRequest) {
|
||||||
Long orderId = commonRequest.getId();
|
// Long orderId = commonRequest.getId();
|
||||||
Order order = ordersService.getById(orderId);
|
// Order order = ordersService.getById(orderId);
|
||||||
ThrowUtils.throwIf(order == null, ErrorCode.NOT_FOUND_ERROR, "订单不存在");
|
// ThrowUtils.throwIf(order == null, ErrorCode.NOT_FOUND_ERROR, "订单不存在");
|
||||||
Refund refund = weChatService.refundPayment(String.valueOf(orderId), order.getTotalAmount());
|
// Refund refund = weChatService.refundPayment(String.valueOf(orderId), order.getTotalAmount());
|
||||||
return ResultUtils.success(refund);
|
// return ResultUtils.success(refund);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
/**
|
// /**
|
||||||
* 退款回调
|
// * 退款回调
|
||||||
*/
|
// */
|
||||||
@PostMapping("/refund/callback")
|
// @PostMapping("/refund/callback")
|
||||||
public BaseResponse<Boolean> callbackRefund(HttpServletRequest request) {
|
// public BaseResponse<Boolean> callbackRefund(HttpServletRequest request) {
|
||||||
// 获取退款信息
|
// // 获取退款信息
|
||||||
RefundNotification refundNotification = weChatService.getRefundInfo(request);
|
// RefundNotification refundNotification = weChatService.getRefundInfo(request);
|
||||||
// 退款回调
|
// // 退款回调
|
||||||
boolean result = weChatService.refundCallback(refundNotification);
|
// boolean result = weChatService.refundCallback(refundNotification);
|
||||||
ThrowUtils.throwIf(!result, ErrorCode.SYSTEM_ERROR, "退款回调失败");
|
// ThrowUtils.throwIf(!result, ErrorCode.SYSTEM_ERROR, "退款回调失败");
|
||||||
return ResultUtils.success(true);
|
// return ResultUtils.success(true);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
/**
|
// /**
|
||||||
* 发送订阅消息
|
// * 发送订阅消息
|
||||||
*/
|
// */
|
||||||
@GetMapping("/refund/callback")
|
// @GetMapping("/refund/callback")
|
||||||
public BaseResponse<Boolean> testSendMessage() {
|
// public BaseResponse<Boolean> testSendMessage() {
|
||||||
String miniOpenId = "o0o_B5CMLFiOs96dJZwtkyHcJzcM";
|
// String miniOpenId = "o0o_B5CMLFiOs96dJZwtkyHcJzcM";
|
||||||
String templateId = "MK13FfX0XxsPV6m1vi6J8_8Bf7JT8rsayFs9q3f4FW4";
|
// String templateId = "MK13FfX0XxsPV6m1vi6J8_8Bf7JT8rsayFs9q3f4FW4";
|
||||||
boolean subscribeMessage = weChatService.sendSubscribeMessage(miniOpenId, templateId);
|
// boolean subscribeMessage = weChatService.sendSubscribeMessage(miniOpenId, templateId);
|
||||||
return ResultUtils.success(subscribeMessage);
|
// return ResultUtils.success(subscribeMessage);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
}
|
//}
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
package com.cultural.heritage.controller.wxPayDemo;
|
||||||
|
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author weikai
|
||||||
|
*/
|
||||||
|
public class HttpUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将通知参数转化为字符串
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String readData(HttpServletRequest request) {
|
||||||
|
BufferedReader br = null;
|
||||||
|
try {
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
br = request.getReader();
|
||||||
|
for (String line; (line = br.readLine()) != null; ) {
|
||||||
|
if (result.length() > 0) {
|
||||||
|
result.append("\n");
|
||||||
|
}
|
||||||
|
result.append(line);
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} finally {
|
||||||
|
if (br != null) {
|
||||||
|
try {
|
||||||
|
br.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,110 @@
|
||||||
|
package com.cultural.heritage.controller.wxPayDemo;
|
||||||
|
|
||||||
|
|
||||||
|
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import org.apache.http.HttpEntity;
|
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
|
import org.apache.http.util.EntityUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.time.DateTimeException;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
import static com.wechat.pay.contrib.apache.httpclient.constant.WechatPayHttpHeaders.*;
|
||||||
|
|
||||||
|
public class WechatPay2ValidatorForRequest {
|
||||||
|
|
||||||
|
protected static final Logger log = LoggerFactory.getLogger(WechatPay2ValidatorForRequest.class);
|
||||||
|
/**
|
||||||
|
* 应答超时时间,单位为分钟
|
||||||
|
*/
|
||||||
|
protected static final long RESPONSE_EXPIRED_MINUTES = 5;
|
||||||
|
protected final Verifier verifier;
|
||||||
|
protected final String requestId;
|
||||||
|
protected final String body;
|
||||||
|
|
||||||
|
|
||||||
|
public WechatPay2ValidatorForRequest(Verifier verifier, String requestId, String body) {
|
||||||
|
this.verifier = verifier;
|
||||||
|
this.requestId = requestId;
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static IllegalArgumentException parameterError(String message, Object... args) {
|
||||||
|
message = String.format(message, args);
|
||||||
|
return new IllegalArgumentException("parameter error: " + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static IllegalArgumentException verifyFail(String message, Object... args) {
|
||||||
|
message = String.format(message, args);
|
||||||
|
return new IllegalArgumentException("signature verify fail: " + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final boolean validate(HttpServletRequest request) throws IOException {
|
||||||
|
try {
|
||||||
|
//处理请求参数
|
||||||
|
validateParameters(request);
|
||||||
|
|
||||||
|
//构造验签名串
|
||||||
|
String message = buildMessage(request);
|
||||||
|
|
||||||
|
String serial = request.getHeader(WECHAT_PAY_SERIAL);
|
||||||
|
String signature = request.getHeader(WECHAT_PAY_SIGNATURE);
|
||||||
|
|
||||||
|
//验签
|
||||||
|
if (!verifier.verify(serial, message.getBytes(StandardCharsets.UTF_8), signature)) {
|
||||||
|
throw verifyFail("serial=[%s] message=[%s] sign=[%s], request-id=[%s]",
|
||||||
|
serial, message, signature, requestId);
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
log.warn(e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final void validateParameters(HttpServletRequest request) {
|
||||||
|
|
||||||
|
// NOTE: ensure HEADER_WECHAT_PAY_TIMESTAMP at last
|
||||||
|
String[] headers = {WECHAT_PAY_SERIAL, WECHAT_PAY_SIGNATURE, WECHAT_PAY_NONCE, WECHAT_PAY_TIMESTAMP};
|
||||||
|
|
||||||
|
String header = null;
|
||||||
|
for (String headerName : headers) {
|
||||||
|
header = request.getHeader(headerName);
|
||||||
|
if (header == null) {
|
||||||
|
throw parameterError("empty [%s], request-id=[%s]", headerName, requestId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//判断请求是否过期
|
||||||
|
String timestampStr = header;
|
||||||
|
try {
|
||||||
|
Instant responseTime = Instant.ofEpochSecond(Long.parseLong(timestampStr));
|
||||||
|
// 拒绝过期请求
|
||||||
|
if (Duration.between(responseTime, Instant.now()).abs().toMinutes() >= RESPONSE_EXPIRED_MINUTES) {
|
||||||
|
throw parameterError("timestamp=[%s] expires, request-id=[%s]", timestampStr, requestId);
|
||||||
|
}
|
||||||
|
} catch (DateTimeException | NumberFormatException e) {
|
||||||
|
throw parameterError("invalid timestamp=[%s], request-id=[%s]", timestampStr, requestId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final String buildMessage(HttpServletRequest request) throws IOException {
|
||||||
|
String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);
|
||||||
|
String nonce = request.getHeader(WECHAT_PAY_NONCE);
|
||||||
|
return timestamp + "\n"
|
||||||
|
+ nonce + "\n"
|
||||||
|
+ body + "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final String getResponseBody(CloseableHttpResponse response) throws IOException {
|
||||||
|
HttpEntity entity = response.getEntity();
|
||||||
|
return (entity != null && entity.isRepeatable()) ? EntityUtils.toString(entity) : "";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
package com.cultural.heritage.controller.wxPayDemo;
|
||||||
|
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public enum WxApiType {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Native下单
|
||||||
|
*/
|
||||||
|
NATIVE_PAY("/v3/pay/transactions/native"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询订单
|
||||||
|
*/
|
||||||
|
ORDER_QUERY_BY_NO("/v3/pay/transactions/out-trade-no/%s"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭订单
|
||||||
|
*/
|
||||||
|
CLOSE_ORDER_BY_NO("/v3/pay/transactions/out-trade-no/%s/close"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 申请退款
|
||||||
|
*/
|
||||||
|
DOMESTIC_REFUNDS("/v3/refund/domestic/refunds"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询单笔退款
|
||||||
|
*/
|
||||||
|
DOMESTIC_REFUNDS_QUERY("/v3/refund/domestic/refunds/%s"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 申请交易账单
|
||||||
|
*/
|
||||||
|
TRADE_BILLS("/v3/bill/tradebill"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 申请资金账单
|
||||||
|
*/
|
||||||
|
FUND_FLOW_BILLS("/v3/bill/fundflowbill");
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类型
|
||||||
|
*/
|
||||||
|
private final String type;
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package com.cultural.heritage.controller.wxPayDemo;
|
||||||
|
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public enum WxNotifyType {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付通知
|
||||||
|
*/
|
||||||
|
NATIVE_NOTIFY("/api/wx-pay/notify/native"),
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退款结果通知
|
||||||
|
*/
|
||||||
|
REFUND_NOTIFY("/api/wx-pay/notify/refunds");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类型
|
||||||
|
*/
|
||||||
|
private final String type;
|
||||||
|
}
|
|
@ -0,0 +1,142 @@
|
||||||
|
package com.cultural.heritage.controller.wxPayDemo;
|
||||||
|
|
||||||
|
|
||||||
|
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
|
||||||
|
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
|
||||||
|
import com.wechat.pay.contrib.apache.httpclient.auth.ScheduledUpdateCertificatesVerifier;
|
||||||
|
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
|
||||||
|
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
|
||||||
|
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties(prefix = "wx.pay") //读取wxpay节点
|
||||||
|
@Data //使用set方法将wxpay节点中的值填充到当前类的属性中
|
||||||
|
@Slf4j
|
||||||
|
public class WxPayConfig {
|
||||||
|
|
||||||
|
// 商户号
|
||||||
|
private String mchId;
|
||||||
|
|
||||||
|
// 商户API证书序列号
|
||||||
|
private String mchSerialNo;
|
||||||
|
|
||||||
|
// 商户私钥文件
|
||||||
|
private String privateKeyPath;
|
||||||
|
|
||||||
|
// APIv3密钥
|
||||||
|
private String apiV3Key;
|
||||||
|
|
||||||
|
// APPID
|
||||||
|
private String appid;
|
||||||
|
|
||||||
|
// 微信服务器地址
|
||||||
|
private String domain;
|
||||||
|
|
||||||
|
// 接收结果通知地址
|
||||||
|
private String notifyDomain;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取商户的私钥文件
|
||||||
|
*
|
||||||
|
* @param filename
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private PrivateKey getPrivateKey(String filename) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
return PemUtil.loadPrivateKey(new FileInputStream(filename));
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
throw new RuntimeException("私钥文件不存在", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取签名验证器
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public ScheduledUpdateCertificatesVerifier getVerifier() {
|
||||||
|
|
||||||
|
log.info("获取签名验证器");
|
||||||
|
|
||||||
|
//获取商户私钥
|
||||||
|
PrivateKey privateKey = getPrivateKey(privateKeyPath);
|
||||||
|
|
||||||
|
//私钥签名对象
|
||||||
|
PrivateKeySigner privateKeySigner = new PrivateKeySigner(mchSerialNo, privateKey);
|
||||||
|
|
||||||
|
//身份认证对象
|
||||||
|
WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(mchId, privateKeySigner);
|
||||||
|
|
||||||
|
// 使用定时更新的签名验证器,不需要传入证书
|
||||||
|
ScheduledUpdateCertificatesVerifier verifier = new ScheduledUpdateCertificatesVerifier(
|
||||||
|
wechatPay2Credentials,
|
||||||
|
apiV3Key.getBytes(StandardCharsets.UTF_8));
|
||||||
|
|
||||||
|
return verifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取http请求对象
|
||||||
|
*
|
||||||
|
* @param verifier
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Bean(name = "wxPayClient")
|
||||||
|
public CloseableHttpClient getWxPayClient(ScheduledUpdateCertificatesVerifier verifier) {
|
||||||
|
|
||||||
|
log.info("获取httpClient");
|
||||||
|
|
||||||
|
//获取商户私钥
|
||||||
|
PrivateKey privateKey = getPrivateKey(privateKeyPath);
|
||||||
|
|
||||||
|
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
|
||||||
|
.withMerchant(mchId, mchSerialNo, privateKey)
|
||||||
|
.withValidator(new WechatPay2Validator(verifier));
|
||||||
|
// ... 接下来,你仍然可以通过builder设置各种参数,来配置你的HttpClient
|
||||||
|
|
||||||
|
// 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
|
||||||
|
CloseableHttpClient httpClient = builder.build();
|
||||||
|
|
||||||
|
return httpClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取HttpClient,无需进行应答签名验证,跳过验签的流程
|
||||||
|
*/
|
||||||
|
@Bean(name = "wxPayNoSignClient")
|
||||||
|
public CloseableHttpClient getWxPayNoSignClient() {
|
||||||
|
|
||||||
|
log.info("无需进行应答签名验证,获取httpClient");
|
||||||
|
|
||||||
|
|
||||||
|
//获取商户私钥
|
||||||
|
PrivateKey privateKey = getPrivateKey(privateKeyPath);
|
||||||
|
|
||||||
|
//用于构造HttpClient
|
||||||
|
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
|
||||||
|
//设置商户信息
|
||||||
|
.withMerchant(mchId, mchSerialNo, privateKey)
|
||||||
|
//无需进行签名验证、通过withValidator((response) -> true)实现
|
||||||
|
.withValidator((response) -> true);
|
||||||
|
|
||||||
|
// 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
|
||||||
|
CloseableHttpClient httpClient = builder.build();
|
||||||
|
|
||||||
|
return httpClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,361 @@
|
||||||
|
package com.cultural.heritage.controller.wxPayDemo;
|
||||||
|
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
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.util.EntityUtils;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/wx-pay")
|
||||||
|
@Slf4j
|
||||||
|
public class WxPayController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private CloseableHttpClient wxPayClient;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
//无需应答签名
|
||||||
|
private CloseableHttpClient wxPayNoSignClient;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private WxPayConfig wxPayConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Native下单
|
||||||
|
*/
|
||||||
|
@PostMapping("/native")
|
||||||
|
public void nativePay() throws Exception {
|
||||||
|
|
||||||
|
log.info("发起支付请求 v3");
|
||||||
|
|
||||||
|
log.info("调用统一下单API");
|
||||||
|
|
||||||
|
//调用统一下单API
|
||||||
|
HttpPost httpPost = new HttpPost(wxPayConfig.getDomain().concat(WxApiType.NATIVE_PAY.getType()));
|
||||||
|
|
||||||
|
// 请求body参数
|
||||||
|
Gson gson = new Gson();
|
||||||
|
HashMap<String, Object> paramsMap = new HashMap<>();
|
||||||
|
paramsMap.put("appid", wxPayConfig.getAppid());// APPID
|
||||||
|
paramsMap.put("mchid", wxPayConfig.getMchId());// 商户id
|
||||||
|
paramsMap.put("description", "魏凯的小商铺"); // 订单描述
|
||||||
|
paramsMap.put("out_trade_no", "123456789987"); // 订单号
|
||||||
|
paramsMap.put("notify_url", wxPayConfig.getNotifyDomain().concat(WxNotifyType.NATIVE_NOTIFY.getType()));// 二维码扫描支付成功后进行回调
|
||||||
|
|
||||||
|
Map<String, Object> amountMap = new HashMap<>();
|
||||||
|
amountMap.put("total", 1); // 金额 以分为单位 这里写1分钱 订单号123456789987对应的金额为1分
|
||||||
|
amountMap.put("currency", "CNY");
|
||||||
|
|
||||||
|
paramsMap.put("amount", amountMap);
|
||||||
|
|
||||||
|
//将参数转换成json字符串
|
||||||
|
String jsonParams = gson.toJson(paramsMap);
|
||||||
|
log.info("请求参数 ===> {}" + jsonParams);
|
||||||
|
|
||||||
|
StringEntity entity = new StringEntity(jsonParams, "utf-8");
|
||||||
|
entity.setContentType("application/json");
|
||||||
|
httpPost.setEntity(entity);
|
||||||
|
httpPost.setHeader("Accept", "application/json");
|
||||||
|
|
||||||
|
//完成签名并执行请求(这个地方就是mchId与商户证书密钥进行校验)
|
||||||
|
try (CloseableHttpResponse response = wxPayClient.execute(httpPost)) {
|
||||||
|
String bodyAsString = EntityUtils.toString(response.getEntity());//响应体
|
||||||
|
int statusCode = response.getStatusLine().getStatusCode();//响应状态码
|
||||||
|
if (statusCode == 200) { //处理成功
|
||||||
|
log.info("成功, 返回结果 = " + bodyAsString);
|
||||||
|
} else if (statusCode == 204) { //处理成功,无返回Body
|
||||||
|
log.info("成功");
|
||||||
|
} else {
|
||||||
|
log.info("Native下单失败,响应码 = " + statusCode + ",返回结果 = " + bodyAsString);
|
||||||
|
throw new IOException("request failed");
|
||||||
|
}
|
||||||
|
//响应结果
|
||||||
|
HashMap resultMap = gson.fromJson(bodyAsString, HashMap.class);
|
||||||
|
//二维码
|
||||||
|
String codeUrl = (String) resultMap.get("code_url");
|
||||||
|
|
||||||
|
log.info("二维码为: " + codeUrl);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询订单
|
||||||
|
*/
|
||||||
|
@GetMapping("/query/{orderNo}")
|
||||||
|
public void queryOrder(@PathVariable String orderNo) throws Exception {
|
||||||
|
|
||||||
|
log.info("查询订单");
|
||||||
|
|
||||||
|
log.info("查单接口调用 ===> {}", orderNo);
|
||||||
|
|
||||||
|
// 拼接url
|
||||||
|
String url = String.format(WxApiType.ORDER_QUERY_BY_NO.getType(), orderNo);
|
||||||
|
url = wxPayConfig.getDomain().concat(url).concat("?mchid=").concat(wxPayConfig.getMchId());
|
||||||
|
|
||||||
|
HttpGet httpGet = new HttpGet(url);
|
||||||
|
httpGet.setHeader("Accept", "application/json");
|
||||||
|
|
||||||
|
//完成签名并执行请求
|
||||||
|
try (CloseableHttpResponse response = wxPayClient.execute(httpGet)) {
|
||||||
|
String bodyAsString = EntityUtils.toString(response.getEntity());//响应体
|
||||||
|
int statusCode = response.getStatusLine().getStatusCode();//响应状态码
|
||||||
|
if (statusCode == 200) { //处理成功
|
||||||
|
log.info("成功, 返回结果 = " + bodyAsString);
|
||||||
|
} else if (statusCode == 204) { //处理成功,无返回Body
|
||||||
|
log.info("成功");
|
||||||
|
} else {
|
||||||
|
log.info("查单接口调用,响应码 = " + statusCode + ",返回结果 = " + bodyAsString);
|
||||||
|
throw new IOException("request failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户取消订单
|
||||||
|
*/
|
||||||
|
@PostMapping("/cancel/{orderNo}")
|
||||||
|
public void cancel(@PathVariable String orderNo) throws Exception {
|
||||||
|
|
||||||
|
log.info("关单接口的调用,订单号 ===> {}", orderNo);
|
||||||
|
|
||||||
|
//创建远程请求对象
|
||||||
|
String url = String.format(WxApiType.CLOSE_ORDER_BY_NO.getType(), orderNo);
|
||||||
|
url = wxPayConfig.getDomain().concat(url);
|
||||||
|
HttpPost httpPost = new HttpPost(url);
|
||||||
|
|
||||||
|
//组装json请求体
|
||||||
|
Gson gson = new Gson();
|
||||||
|
Map<String, String> paramsMap = new HashMap<>();
|
||||||
|
paramsMap.put("mchid", wxPayConfig.getMchId());
|
||||||
|
String jsonParams = gson.toJson(paramsMap);
|
||||||
|
log.info("请求参数 ===> {}", jsonParams);
|
||||||
|
|
||||||
|
//将请求参数设置到请求对象中
|
||||||
|
StringEntity entity = new StringEntity(jsonParams, "utf-8");
|
||||||
|
entity.setContentType("application/json");
|
||||||
|
httpPost.setEntity(entity);
|
||||||
|
httpPost.setHeader("Accept", "application/json");
|
||||||
|
|
||||||
|
//完成签名并执行请求
|
||||||
|
try (CloseableHttpResponse response = wxPayClient.execute(httpPost)) {
|
||||||
|
int statusCode = response.getStatusLine().getStatusCode();//响应状态码
|
||||||
|
if (statusCode == 200) { //处理成功
|
||||||
|
log.info("成功200");
|
||||||
|
} else if (statusCode == 204) { //处理成功,无返回Body
|
||||||
|
log.info("成功204");
|
||||||
|
} else {
|
||||||
|
log.info("Native下单失败,响应码 = " + statusCode);
|
||||||
|
throw new IOException("request failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户申请退款
|
||||||
|
*/
|
||||||
|
@PostMapping("/refunds/{orderNo}")
|
||||||
|
public void refunds(@PathVariable String orderNo) throws Exception {
|
||||||
|
|
||||||
|
log.info("申请退款");
|
||||||
|
log.info("调用退款API");
|
||||||
|
//调用统一下单API
|
||||||
|
String url = wxPayConfig.getDomain().concat(WxApiType.DOMESTIC_REFUNDS.getType());
|
||||||
|
HttpPost httpPost = new HttpPost(url);
|
||||||
|
|
||||||
|
// 请求body参数
|
||||||
|
Gson gson = new Gson();
|
||||||
|
Map<String, Object> paramsMap = new HashMap<>();
|
||||||
|
paramsMap.put("out_trade_no", orderNo);//订单编号
|
||||||
|
paramsMap.put("out_refund_no", "123456");//退款单编号 随便填
|
||||||
|
paramsMap.put("reason", "随便填一个");//退款原因
|
||||||
|
paramsMap.put("notify_url", wxPayConfig.getNotifyDomain().concat(WxNotifyType.REFUND_NOTIFY.getType()));//退款成功通知地址
|
||||||
|
|
||||||
|
Map<String, java.io.Serializable> amountMap = new HashMap<String, java.io.Serializable>();
|
||||||
|
amountMap.put("refund", 1);//退款金额 这里的金额应该根据订单id查询出来
|
||||||
|
amountMap.put("total", 1);//原订单金额 这里的金额应该根据订单id查询出来
|
||||||
|
amountMap.put("currency", "CNY");//退款币种
|
||||||
|
paramsMap.put("amount", amountMap);
|
||||||
|
|
||||||
|
//将参数转换成json字符串
|
||||||
|
String jsonParams = gson.toJson(paramsMap);
|
||||||
|
log.info("请求参数 ===> {}" + jsonParams);
|
||||||
|
|
||||||
|
StringEntity entity = new StringEntity(jsonParams, "utf-8");
|
||||||
|
entity.setContentType("application/json");//设置请求报文格式
|
||||||
|
httpPost.setEntity(entity);//将请求报文放入请求对象
|
||||||
|
httpPost.setHeader("Accept", "application/json");//设置响应报文格式
|
||||||
|
|
||||||
|
//完成签名并执行请求,并完成验签
|
||||||
|
try (CloseableHttpResponse response = wxPayClient.execute(httpPost)) {
|
||||||
|
//解析响应结果
|
||||||
|
String bodyAsString = EntityUtils.toString(response.getEntity());
|
||||||
|
int statusCode = response.getStatusLine().getStatusCode();
|
||||||
|
if (statusCode == 200) {
|
||||||
|
log.info("成功, 退款返回结果 = " + bodyAsString);
|
||||||
|
} else if (statusCode == 204) {
|
||||||
|
log.info("成功");
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("退款异常, 响应码 = " + statusCode + ", 退款返回结果 = " + bodyAsString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询退款
|
||||||
|
*/
|
||||||
|
@GetMapping("/query-refund/{refundNo}")
|
||||||
|
public void queryRefund(@PathVariable String refundNo) throws Exception {
|
||||||
|
|
||||||
|
log.info("查询退款");
|
||||||
|
log.info("查询退款接口调用 ===> {}", refundNo);
|
||||||
|
|
||||||
|
String url = String.format(WxApiType.DOMESTIC_REFUNDS_QUERY.getType(), refundNo);
|
||||||
|
url = wxPayConfig.getDomain().concat(url);
|
||||||
|
|
||||||
|
//创建远程Get 请求对象
|
||||||
|
HttpGet httpGet = new HttpGet(url);
|
||||||
|
httpGet.setHeader("Accept", "application/json");
|
||||||
|
|
||||||
|
//完成签名并执行请求
|
||||||
|
try (CloseableHttpResponse response = wxPayClient.execute(httpGet)) {
|
||||||
|
String bodyAsString = EntityUtils.toString(response.getEntity());
|
||||||
|
int statusCode = response.getStatusLine().getStatusCode();
|
||||||
|
if (statusCode == 200) {
|
||||||
|
log.info("成功, 查询退款返回结果 = " + bodyAsString);
|
||||||
|
} else if (statusCode == 204) {
|
||||||
|
log.info("成功");
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("查询退款异常, 响应码 = " + statusCode + ", 查询退款返回结果 = " + bodyAsString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取账单url 这个url无法在浏览器打开
|
||||||
|
*/
|
||||||
|
@GetMapping("/querybill/{billDate}/{type}")
|
||||||
|
public void queryTradeBill(@PathVariable String billDate, @PathVariable String type) throws Exception {
|
||||||
|
|
||||||
|
log.info("获取账单url");
|
||||||
|
|
||||||
|
log.warn("申请账单接口调用 {}", billDate);
|
||||||
|
|
||||||
|
String url = "";
|
||||||
|
if ("tradebill".equals(type)) {
|
||||||
|
url = WxApiType.TRADE_BILLS.getType();
|
||||||
|
} else if ("fundflowbill".equals(type)) {
|
||||||
|
url = WxApiType.FUND_FLOW_BILLS.getType();
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("不支持的账单类型");
|
||||||
|
}
|
||||||
|
|
||||||
|
url = wxPayConfig.getDomain().concat(url).concat("?bill_date=").concat(billDate);
|
||||||
|
|
||||||
|
//创建远程Get 请求对象
|
||||||
|
HttpGet httpGet = new HttpGet(url);
|
||||||
|
httpGet.addHeader("Accept", "application/json");
|
||||||
|
|
||||||
|
//使用wxPayClient发送请求得到响应
|
||||||
|
try (CloseableHttpResponse response = wxPayClient.execute(httpGet)) {
|
||||||
|
String bodyAsString = EntityUtils.toString(response.getEntity());
|
||||||
|
|
||||||
|
int statusCode = response.getStatusLine().getStatusCode();
|
||||||
|
if (statusCode == 200) {
|
||||||
|
log.info("成功, 申请账单返回结果 = " + bodyAsString);
|
||||||
|
} else if (statusCode == 204) {
|
||||||
|
log.info("成功");
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("申请账单异常, 响应码 = " + statusCode + ", 申请账单返回结果 = " + bodyAsString);
|
||||||
|
}
|
||||||
|
//获取账单下载地址
|
||||||
|
Gson gson = new Gson();
|
||||||
|
Map<String, String> resultMap = gson.fromJson(bodyAsString, HashMap.class);
|
||||||
|
log.info("账单链接;" + resultMap.get("download_url"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载账单
|
||||||
|
*/
|
||||||
|
@GetMapping("/downloadbill/{billDate}/{type}")
|
||||||
|
public void downloadBill(@PathVariable String billDate, @PathVariable String type) throws Exception {
|
||||||
|
|
||||||
|
log.info("下载账单");
|
||||||
|
log.warn("下载账单接口调用 {}, {}", billDate, type);
|
||||||
|
|
||||||
|
//获取账单url地址
|
||||||
|
String downloadUrl = this.queryBill(billDate, type);
|
||||||
|
//创建远程Get 请求对象
|
||||||
|
HttpGet httpGet = new HttpGet(downloadUrl);
|
||||||
|
httpGet.addHeader("Accept", "application/json");
|
||||||
|
|
||||||
|
//使用wxPayClient发送请求得到响应
|
||||||
|
try (CloseableHttpResponse response = wxPayNoSignClient.execute(httpGet)) {
|
||||||
|
String bodyAsString = EntityUtils.toString(response.getEntity());
|
||||||
|
int statusCode = response.getStatusLine().getStatusCode();
|
||||||
|
if (statusCode == 200) {
|
||||||
|
log.info("成功, 下载账单返回结果 = " + bodyAsString);
|
||||||
|
} else if (statusCode == 204) {
|
||||||
|
log.info("成功");
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("下载账单异常, 响应码 = " + statusCode + ", 下载账单返回结果 = " + bodyAsString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 申请账单
|
||||||
|
*/
|
||||||
|
public String queryBill(String billDate, String type) throws Exception {
|
||||||
|
log.warn("申请账单接口调用 {}", billDate);
|
||||||
|
|
||||||
|
String url = "";
|
||||||
|
if ("tradebill".equals(type)) {
|
||||||
|
url = WxApiType.TRADE_BILLS.getType();
|
||||||
|
} else if ("fundflowbill".equals(type)) {
|
||||||
|
url = WxApiType.FUND_FLOW_BILLS.getType();
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("不支持的账单类型");
|
||||||
|
}
|
||||||
|
|
||||||
|
url = wxPayConfig.getDomain().concat(url).concat("?bill_date=").concat(billDate);
|
||||||
|
|
||||||
|
//创建远程Get 请求对象
|
||||||
|
HttpGet httpGet = new HttpGet(url);
|
||||||
|
httpGet.addHeader("Accept", "application/json");
|
||||||
|
|
||||||
|
//使用wxPayClient发送请求得到响应
|
||||||
|
try (CloseableHttpResponse response = wxPayClient.execute(httpGet)) {
|
||||||
|
String bodyAsString = EntityUtils.toString(response.getEntity());
|
||||||
|
|
||||||
|
int statusCode = response.getStatusLine().getStatusCode();
|
||||||
|
if (statusCode == 200) {
|
||||||
|
log.info("成功, 申请账单返回结果 = " + bodyAsString);
|
||||||
|
} else if (statusCode == 204) {
|
||||||
|
log.info("成功");
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("申请账单异常, 响应码 = " + statusCode + ", 申请账单返回结果 = " + bodyAsString);
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取账单下载地址
|
||||||
|
Gson gson = new Gson();
|
||||||
|
Map<String, String> resultMap = gson.fromJson(bodyAsString, HashMap.class);
|
||||||
|
return resultMap.get("download_url");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,124 @@
|
||||||
|
package com.cultural.heritage.controller.wxPayDemo;
|
||||||
|
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/wx-pay/notify")
|
||||||
|
@Slf4j
|
||||||
|
public class WxPayNotifyController {
|
||||||
|
@Resource
|
||||||
|
private Verifier verifier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单支付成功回调
|
||||||
|
*/
|
||||||
|
@PostMapping("/native")
|
||||||
|
public String nativeNotify(HttpServletRequest request, HttpServletResponse response) {
|
||||||
|
|
||||||
|
Gson gson = new Gson();
|
||||||
|
Map<String, String> map = new HashMap<>();//应答对象
|
||||||
|
|
||||||
|
try {
|
||||||
|
//处理通知参数
|
||||||
|
String body = HttpUtils.readData(request);
|
||||||
|
Map<String, Object> bodyMap = gson.fromJson(body, HashMap.class);
|
||||||
|
String requestId = (String) bodyMap.get("id");
|
||||||
|
log.info("支付通知的id ===> {}", requestId);
|
||||||
|
|
||||||
|
//签名的验证
|
||||||
|
WechatPay2ValidatorForRequest wechatPay2ValidatorForRequest
|
||||||
|
= new WechatPay2ValidatorForRequest(verifier, requestId, body);
|
||||||
|
if (!wechatPay2ValidatorForRequest.validate(request)) {
|
||||||
|
|
||||||
|
log.error("通知验签失败");
|
||||||
|
//失败应答
|
||||||
|
response.setStatus(500);
|
||||||
|
map.put("code", "ERROR");
|
||||||
|
map.put("message", "通知验签失败");
|
||||||
|
return gson.toJson(map);
|
||||||
|
}
|
||||||
|
log.info("通知验签成功");
|
||||||
|
|
||||||
|
//处理订单 这里可以对订单进行处理 比如订单支付成功 修改数据库订单表订单状态为已支付
|
||||||
|
// processOrder(bodyMap);
|
||||||
|
|
||||||
|
//应答超时
|
||||||
|
//模拟接收微信端的重复通知
|
||||||
|
TimeUnit.SECONDS.sleep(5);
|
||||||
|
|
||||||
|
//成功应答
|
||||||
|
response.setStatus(200);
|
||||||
|
map.put("code", "SUCCESS");
|
||||||
|
map.put("message", "成功");
|
||||||
|
return gson.toJson(map);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
//失败应答
|
||||||
|
response.setStatus(500);
|
||||||
|
map.put("code", "ERROR");
|
||||||
|
map.put("message", "失败");
|
||||||
|
return gson.toJson(map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/refunds")
|
||||||
|
public String refundsNotify(HttpServletRequest request, HttpServletResponse response){
|
||||||
|
|
||||||
|
log.info("退款通知执行");
|
||||||
|
Gson gson = new Gson();
|
||||||
|
Map<String, String> map = new HashMap<>();//应答对象
|
||||||
|
|
||||||
|
try {
|
||||||
|
//处理通知参数
|
||||||
|
String body = HttpUtils.readData(request);
|
||||||
|
Map<String, Object> bodyMap = gson.fromJson(body, HashMap.class);
|
||||||
|
String requestId = (String)bodyMap.get("id");
|
||||||
|
log.info("支付通知的id ===> {}", requestId);
|
||||||
|
|
||||||
|
//签名的验证
|
||||||
|
WechatPay2ValidatorForRequest wechatPay2ValidatorForRequest
|
||||||
|
= new WechatPay2ValidatorForRequest(verifier, requestId, body);
|
||||||
|
if(!wechatPay2ValidatorForRequest.validate(request)){
|
||||||
|
|
||||||
|
log.error("通知验签失败");
|
||||||
|
//失败应答
|
||||||
|
response.setStatus(500);
|
||||||
|
map.put("code", "ERROR");
|
||||||
|
map.put("message", "通知验签失败");
|
||||||
|
return gson.toJson(map);
|
||||||
|
}
|
||||||
|
log.info("通知验签成功");
|
||||||
|
|
||||||
|
//处理退款单 订单退款成功 修改订单表中订单状态为已退款
|
||||||
|
// processRefund(bodyMap);
|
||||||
|
|
||||||
|
//成功应答
|
||||||
|
response.setStatus(200);
|
||||||
|
map.put("code", "SUCCESS");
|
||||||
|
map.put("message", "成功");
|
||||||
|
return gson.toJson(map);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
//失败应答
|
||||||
|
response.setStatus(500);
|
||||||
|
map.put("code", "ERROR");
|
||||||
|
map.put("message", "失败");
|
||||||
|
return gson.toJson(map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,53 +1,53 @@
|
||||||
package com.cultural.heritage.service.wxpay;
|
//package com.cultural.heritage.service.wxpay;
|
||||||
|
//
|
||||||
|
//
|
||||||
import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse;
|
//import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse;
|
||||||
import com.wechat.pay.java.service.payments.model.Transaction;
|
//import com.wechat.pay.java.service.payments.model.Transaction;
|
||||||
import com.wechat.pay.java.service.refund.model.Refund;
|
//import com.wechat.pay.java.service.refund.model.Refund;
|
||||||
import com.wechat.pay.java.service.refund.model.RefundNotification;
|
//import com.wechat.pay.java.service.refund.model.RefundNotification;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
//import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
//
|
||||||
import java.io.IOException;
|
//import java.io.IOException;
|
||||||
import java.math.BigDecimal;
|
//import java.math.BigDecimal;
|
||||||
|
//
|
||||||
/**
|
///**
|
||||||
* @author 玄德
|
// * @author 玄德
|
||||||
*/
|
// */
|
||||||
public interface WeChatService {
|
//public interface WeChatService {
|
||||||
|
//
|
||||||
/**
|
// /**
|
||||||
* 微信支付
|
// * 微信支付
|
||||||
*/
|
// */
|
||||||
PrepayWithRequestPaymentResponse createPayment(String orderId, String miniOpenId, BigDecimal amount);
|
// PrepayWithRequestPaymentResponse createPayment(String orderId, String miniOpenId, BigDecimal amount);
|
||||||
|
//
|
||||||
/**
|
// /**
|
||||||
* 获取支付回调信息
|
// * 获取支付回调信息
|
||||||
*/
|
// */
|
||||||
Transaction getTransactionInfo(HttpServletRequest request);
|
// Transaction getTransactionInfo(HttpServletRequest request);
|
||||||
|
//
|
||||||
/**
|
// /**
|
||||||
* 支付回调
|
// * 支付回调
|
||||||
*/
|
// */
|
||||||
boolean paymentCallback(Transaction transaction) throws IOException;
|
// boolean paymentCallback(Transaction transaction) throws IOException;
|
||||||
|
//
|
||||||
/**
|
// /**
|
||||||
* 退款申请
|
// * 退款申请
|
||||||
*/
|
// */
|
||||||
Refund refundPayment(String orderId, BigDecimal amount);
|
// Refund refundPayment(String orderId, BigDecimal amount);
|
||||||
|
//
|
||||||
/**
|
// /**
|
||||||
* 获取退款回调信息
|
// * 获取退款回调信息
|
||||||
*/
|
// */
|
||||||
RefundNotification getRefundInfo(HttpServletRequest request);
|
// RefundNotification getRefundInfo(HttpServletRequest request);
|
||||||
|
//
|
||||||
/**
|
// /**
|
||||||
* 退款回调
|
// * 退款回调
|
||||||
*/
|
// */
|
||||||
boolean refundCallback(RefundNotification refundNotification);
|
// boolean refundCallback(RefundNotification refundNotification);
|
||||||
|
//
|
||||||
/**
|
// /**
|
||||||
* 发送订阅模板消息
|
// * 发送订阅模板消息
|
||||||
*/
|
// */
|
||||||
boolean sendSubscribeMessage(String openid, String templateId);
|
// boolean sendSubscribeMessage(String openid, String templateId);
|
||||||
|
//
|
||||||
}
|
//}
|
||||||
|
|
|
@ -1,265 +1,265 @@
|
||||||
package com.cultural.heritage.service.wxpay.impl;
|
//package com.cultural.heritage.service.wxpay.impl;
|
||||||
|
//
|
||||||
|
//
|
||||||
import cn.binarywang.wx.miniapp.api.WxMaMsgService;
|
//import cn.binarywang.wx.miniapp.api.WxMaMsgService;
|
||||||
import cn.binarywang.wx.miniapp.api.WxMaService;
|
//import cn.binarywang.wx.miniapp.api.WxMaService;
|
||||||
import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMessage;
|
//import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMessage;
|
||||||
import cn.hutool.core.util.RandomUtil;
|
//import cn.hutool.core.util.RandomUtil;
|
||||||
import com.cultural.heritage.common.ErrorCode;
|
//import com.cultural.heritage.common.ErrorCode;
|
||||||
import com.cultural.heritage.config.WxOpenConfig;
|
//import com.cultural.heritage.config.WxOpenConfig;
|
||||||
import com.cultural.heritage.config.WxPayConfig;
|
//import com.cultural.heritage.config.WxPayConfig;
|
||||||
import com.cultural.heritage.exception.BusinessException;
|
//import com.cultural.heritage.exception.BusinessException;
|
||||||
import com.cultural.heritage.model.entity.Order;
|
//import com.cultural.heritage.model.entity.Order;
|
||||||
import com.cultural.heritage.service.order.OrderService;
|
//import com.cultural.heritage.service.order.OrderService;
|
||||||
import com.cultural.heritage.service.wxpay.WeChatService;
|
//import com.cultural.heritage.service.wxpay.WeChatService;
|
||||||
import com.wechat.pay.java.core.notification.NotificationParser;
|
//import com.wechat.pay.java.core.notification.NotificationParser;
|
||||||
import com.wechat.pay.java.core.notification.RequestParam;
|
//import com.wechat.pay.java.core.notification.RequestParam;
|
||||||
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
|
//import com.wechat.pay.java.service.payments.jsapi.model.Amount;
|
||||||
import com.wechat.pay.java.service.payments.jsapi.model.Payer;
|
//import com.wechat.pay.java.service.payments.jsapi.model.Payer;
|
||||||
import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;
|
//import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;
|
||||||
import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse;
|
//import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse;
|
||||||
import com.wechat.pay.java.service.payments.model.Transaction;
|
//import com.wechat.pay.java.service.payments.model.Transaction;
|
||||||
import com.wechat.pay.java.service.refund.model.AmountReq;
|
//import com.wechat.pay.java.service.refund.model.AmountReq;
|
||||||
import com.wechat.pay.java.service.refund.model.CreateRequest;
|
//import com.wechat.pay.java.service.refund.model.CreateRequest;
|
||||||
import com.wechat.pay.java.service.refund.model.Refund;
|
//import com.wechat.pay.java.service.refund.model.Refund;
|
||||||
import com.wechat.pay.java.service.refund.model.RefundNotification;
|
//import com.wechat.pay.java.service.refund.model.RefundNotification;
|
||||||
import jakarta.annotation.Resource;
|
//import jakarta.annotation.Resource;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
//import jakarta.servlet.http.HttpServletRequest;
|
||||||
import lombok.SneakyThrows;
|
//import lombok.SneakyThrows;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
//import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
//import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
//import org.springframework.transaction.annotation.Transactional;
|
||||||
|
//
|
||||||
import java.io.BufferedReader;
|
//import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
//import java.io.IOException;
|
||||||
import java.math.BigDecimal;
|
//import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
//import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
//import java.time.LocalDateTime;
|
||||||
import java.time.LocalTime;
|
//import java.time.LocalTime;
|
||||||
import java.util.ArrayList;
|
//import java.util.ArrayList;
|
||||||
import java.util.List;
|
//import java.util.List;
|
||||||
|
//
|
||||||
/**
|
///**
|
||||||
* @author 玄德
|
// * @author 玄德
|
||||||
*/
|
// */
|
||||||
@Slf4j
|
//@Slf4j
|
||||||
@Service
|
//@Service
|
||||||
public class WeChatServiceImpl implements WeChatService {
|
//public class WeChatServiceImpl implements WeChatService {
|
||||||
|
//
|
||||||
@Resource
|
// @Resource
|
||||||
private WxPayConfig wxPayConfig;
|
// private WxPayConfig wxPayConfig;
|
||||||
|
//
|
||||||
@Resource
|
// @Resource
|
||||||
private WxOpenConfig wxOpenConfig;
|
// private WxOpenConfig wxOpenConfig;
|
||||||
|
//
|
||||||
@Resource
|
// @Resource
|
||||||
private OrderService ordersService;
|
// private OrderService ordersService;
|
||||||
|
//
|
||||||
/**
|
// /**
|
||||||
* 请求参数
|
// * 请求参数
|
||||||
*/
|
// */
|
||||||
public static RequestParam requestParam = null;
|
// public static RequestParam requestParam = null;
|
||||||
|
//
|
||||||
@Override
|
// @Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
// @Transactional(rollbackFor = Exception.class)
|
||||||
public synchronized PrepayWithRequestPaymentResponse createPayment(String orderId, String miniOpenId, BigDecimal amount) {
|
// public synchronized PrepayWithRequestPaymentResponse createPayment(String orderId, String miniOpenId, BigDecimal amount) {
|
||||||
// request.setXxx(val)设置所需参数,具体参数可见Request定义
|
// // request.setXxx(val)设置所需参数,具体参数可见Request定义
|
||||||
PrepayRequest request = new PrepayRequest();
|
// PrepayRequest request = new PrepayRequest();
|
||||||
// 金额
|
// // 金额
|
||||||
Amount WxAmount = new Amount();
|
// Amount WxAmount = new Amount();
|
||||||
WxAmount.setTotal(amount.movePointRight(2).intValue());
|
// WxAmount.setTotal(amount.movePointRight(2).intValue());
|
||||||
WxAmount.setCurrency("CNY");
|
// WxAmount.setCurrency("CNY");
|
||||||
request.setAmount(WxAmount);
|
// request.setAmount(WxAmount);
|
||||||
// 公众号id
|
// // 公众号id
|
||||||
request.setAppid(wxPayConfig.getAppId());
|
// request.setAppid(wxPayConfig.getAppId());
|
||||||
// 商户号
|
// // 商户号
|
||||||
request.setMchid(wxPayConfig.getMerchantId());
|
// request.setMchid(wxPayConfig.getMerchantId());
|
||||||
// 支付者信息
|
// // 支付者信息
|
||||||
Payer payer = new Payer();
|
// Payer payer = new Payer();
|
||||||
payer.setOpenid(miniOpenId);
|
// payer.setOpenid(miniOpenId);
|
||||||
request.setPayer(payer);
|
// request.setPayer(payer);
|
||||||
// 描述
|
// // 描述
|
||||||
request.setDescription("订单号:" + orderId);
|
// request.setDescription("订单号:" + orderId);
|
||||||
// 微信回调地址
|
// // 微信回调地址
|
||||||
request.setNotifyUrl(wxPayConfig.getNotifyUrl() + "/api/wechat/payment/callback");
|
// request.setNotifyUrl(wxPayConfig.getNotifyUrl() + "/api/wechat/payment/callback");
|
||||||
// 商户订单号
|
// // 商户订单号
|
||||||
request.setOutTradeNo(RandomUtil.randomNumbers(12));
|
// request.setOutTradeNo(RandomUtil.randomNumbers(12));
|
||||||
//返回数据,前端调起支付
|
// //返回数据,前端调起支付
|
||||||
return wxPayConfig.getJsapiServiceExtension().prepayWithRequestPayment(request);
|
// return wxPayConfig.getJsapiServiceExtension().prepayWithRequestPayment(request);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
@Override
|
// @Override
|
||||||
public Transaction getTransactionInfo(HttpServletRequest request) {
|
// public Transaction getTransactionInfo(HttpServletRequest request) {
|
||||||
NotificationParser notificationParser = getNotificationParser(request);
|
// NotificationParser notificationParser = getNotificationParser(request);
|
||||||
return notificationParser.parse(requestParam, Transaction.class);
|
// return notificationParser.parse(requestParam, Transaction.class);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
@Override
|
// @Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
// @Transactional(rollbackFor = Exception.class)
|
||||||
public synchronized boolean paymentCallback(Transaction transaction) throws IOException {
|
// public synchronized boolean paymentCallback(Transaction transaction) throws IOException {
|
||||||
System.out.println("---------------------------微信支付回调(开始)-------------------------------");
|
// System.out.println("---------------------------微信支付回调(开始)-------------------------------");
|
||||||
// 获取订单信息
|
// // 获取订单信息
|
||||||
String orderIdByString = transaction.getOutTradeNo();
|
// String orderIdByString = transaction.getOutTradeNo();
|
||||||
Order order = ordersService.getById(orderIdByString);
|
// Order order = ordersService.getById(orderIdByString);
|
||||||
if (order == null) {
|
// if (order == null) {
|
||||||
log.error("订单不存在");
|
// log.error("订单不存在");
|
||||||
throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "订单不存在,订单号:" + transaction.getOutTradeNo());
|
// throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "订单不存在,订单号:" + transaction.getOutTradeNo());
|
||||||
}
|
// }
|
||||||
// 生成取餐码
|
|
||||||
// 获取当日的日期
|
|
||||||
LocalDate today = LocalDate.now();
|
|
||||||
// 获取当日的 00:00 时间
|
|
||||||
LocalDateTime startTime = today.atStartOfDay();
|
|
||||||
// 获取当日的 23:59 时间
|
|
||||||
LocalDateTime endTime = today.atTime(LocalTime.MAX);
|
|
||||||
// // 生成取餐码
|
// // 生成取餐码
|
||||||
// QueryWrapper<Orders> queryWrapper = new QueryWrapper<>();
|
// // 获取当日的日期
|
||||||
// queryWrapper.eq("businessId", order.getBusinessId());
|
// LocalDate today = LocalDate.now();
|
||||||
// queryWrapper.between("createTime", startTime, endTime);
|
// // 获取当日的 00:00 时间
|
||||||
// queryWrapper.in("state", 1, 2);
|
// LocalDateTime startTime = today.atStartOfDay();
|
||||||
// long count = ordersService.count(queryWrapper);
|
// // 获取当日的 23:59 时间
|
||||||
// String pickupCode = String.format("%04d", count + 1);
|
// LocalDateTime endTime = today.atTime(LocalTime.MAX);
|
||||||
|
//// // 生成取餐码
|
||||||
|
//// QueryWrapper<Orders> queryWrapper = new QueryWrapper<>();
|
||||||
|
//// queryWrapper.eq("businessId", order.getBusinessId());
|
||||||
|
//// queryWrapper.between("createTime", startTime, endTime);
|
||||||
|
//// queryWrapper.in("state", 1, 2);
|
||||||
|
//// long count = ordersService.count(queryWrapper);
|
||||||
|
//// String pickupCode = String.format("%04d", count + 1);
|
||||||
|
//// // 修改订单信息
|
||||||
|
//// order.setState(1);
|
||||||
|
//// order.setPickupCode(pickupCode);
|
||||||
|
//// Date date = new Date();
|
||||||
|
//// order.setUpdateTime(date);
|
||||||
|
//// boolean update = ordersService.updateById(order);
|
||||||
|
//// ThrowUtils.throwIf(!update, ErrorCode.OPERATION_ERROR, "修改订单状态失败");
|
||||||
|
//// // 通知商家新订单
|
||||||
|
//// Business business = businessService.getById(order.getBusinessId());
|
||||||
|
//// Long userId = order.getUserId();
|
||||||
|
//// Long businessId = business.getUserId();
|
||||||
|
//// MessageVO messageVO = new MessageVO();
|
||||||
|
//// messageVO.setType(1);
|
||||||
|
//// messageVO.setUserId(userId);
|
||||||
|
//// messageVO.setToUserId(businessId);
|
||||||
|
//// messageVO.setContent(orderIdByString);
|
||||||
|
//// String message = JSONUtil.toJsonStr(messageVO);
|
||||||
|
//// // 通知商家新订单
|
||||||
|
//// webSocketServer.onMessage(message);
|
||||||
|
// System.out.println("---------------------------微信支付回调(结束)-------------------------------");
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public Refund refundPayment(String orderId, BigDecimal amount) {
|
||||||
|
// // 退款请求
|
||||||
|
// CreateRequest createRequest = new CreateRequest();
|
||||||
|
// // 商户订单号
|
||||||
|
// createRequest.setOutTradeNo(orderId);
|
||||||
|
// // 商户退款单号
|
||||||
|
// createRequest.setOutRefundNo(orderId);
|
||||||
|
// // 退款结果回调
|
||||||
|
// createRequest.setNotifyUrl(wxPayConfig.getNotifyUrl() + "/api/wechat/refund/callback");
|
||||||
|
// // 退款金额
|
||||||
|
// AmountReq amountReq = new AmountReq();
|
||||||
|
// long refundAmount = amount.movePointRight(2).intValue();
|
||||||
|
// amountReq.setRefund(refundAmount);
|
||||||
|
// amountReq.setTotal(refundAmount);
|
||||||
|
// amountReq.setCurrency("CNY");
|
||||||
|
// createRequest.setAmount(amountReq);
|
||||||
|
// // 申请退款
|
||||||
|
// System.out.println("退款请求:" + createRequest);
|
||||||
|
// Refund refund = wxPayConfig.getRefundService().create(createRequest);
|
||||||
|
// System.out.println("退款申请结果:" + refund);
|
||||||
|
// return refund;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public RefundNotification getRefundInfo(HttpServletRequest request) {
|
||||||
|
// NotificationParser notificationParser = getNotificationParser(request);
|
||||||
|
// return notificationParser.parse(requestParam, RefundNotification.class);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// @Transactional(rollbackFor = Exception.class)
|
||||||
|
// public synchronized boolean refundCallback(RefundNotification refundNotification) {
|
||||||
|
// System.out.println("---------------------------微信退款回调(开始)-------------------------------");
|
||||||
|
// // 获取订单信息
|
||||||
|
// String orderIdByString = refundNotification.getOutTradeNo();
|
||||||
|
// Order order = ordersService.getById(orderIdByString);
|
||||||
// // 修改订单信息
|
// // 修改订单信息
|
||||||
// order.setState(1);
|
// order.setOrderStatus("已退款");
|
||||||
// order.setPickupCode(pickupCode);
|
// ordersService.updateById(order);
|
||||||
// Date date = new Date();
|
// System.out.println("---------------------------微信退款回调(结束)-------------------------------");
|
||||||
// order.setUpdateTime(date);
|
// return true;
|
||||||
// boolean update = ordersService.updateById(order);
|
// }
|
||||||
// ThrowUtils.throwIf(!update, ErrorCode.OPERATION_ERROR, "修改订单状态失败");
|
//
|
||||||
// // 通知商家新订单
|
// @Override
|
||||||
// Business business = businessService.getById(order.getBusinessId());
|
// public boolean sendSubscribeMessage(String openid, String templateId) {
|
||||||
// Long userId = order.getUserId();
|
// try {
|
||||||
// Long businessId = business.getUserId();
|
// // 获取小程序对象
|
||||||
// MessageVO messageVO = new MessageVO();
|
// WxMaService wxMaService = wxOpenConfig.getWxMaService();
|
||||||
// messageVO.setType(1);
|
// // 获取消息接口
|
||||||
// messageVO.setUserId(userId);
|
// WxMaMsgService msgService = wxMaService.getMsgService();
|
||||||
// messageVO.setToUserId(businessId);
|
// // 构建订阅消息体
|
||||||
// messageVO.setContent(orderIdByString);
|
// WxMaSubscribeMessage wxMaSubscribeMessage = new WxMaSubscribeMessage();
|
||||||
// String message = JSONUtil.toJsonStr(messageVO);
|
// wxMaSubscribeMessage.setToUser(openid);
|
||||||
// // 通知商家新订单
|
// wxMaSubscribeMessage.setTemplateId(templateId);
|
||||||
// webSocketServer.onMessage(message);
|
// List<WxMaSubscribeMessage.MsgData> msgDataList = getMsgData();
|
||||||
System.out.println("---------------------------微信支付回调(结束)-------------------------------");
|
// wxMaSubscribeMessage.setData(msgDataList);
|
||||||
return true;
|
// // 发送消息
|
||||||
}
|
// msgService.sendSubscribeMsg(wxMaSubscribeMessage);
|
||||||
|
// return true;
|
||||||
@Override
|
// } catch (Exception e) {
|
||||||
public Refund refundPayment(String orderId, BigDecimal amount) {
|
// log.error("发送订阅消息失败:", e);
|
||||||
// 退款请求
|
// throw new BusinessException(ErrorCode.SYSTEM_ERROR, "发送订阅消息失败");
|
||||||
CreateRequest createRequest = new CreateRequest();
|
// }
|
||||||
// 商户订单号
|
// }
|
||||||
createRequest.setOutTradeNo(orderId);
|
//
|
||||||
// 商户退款单号
|
// /**
|
||||||
createRequest.setOutRefundNo(orderId);
|
// * 提取消息数据
|
||||||
// 退款结果回调
|
// */
|
||||||
createRequest.setNotifyUrl(wxPayConfig.getNotifyUrl() + "/api/wechat/refund/callback");
|
// private static List<WxMaSubscribeMessage.MsgData> getMsgData() {
|
||||||
// 退款金额
|
// List<WxMaSubscribeMessage.MsgData> msgDataList = new ArrayList<>();
|
||||||
AmountReq amountReq = new AmountReq();
|
// String[][] dataItems = {
|
||||||
long refundAmount = amount.movePointRight(2).intValue();
|
// {"thing1", "新订单"},
|
||||||
amountReq.setRefund(refundAmount);
|
// {"phrase2", "已支付"},
|
||||||
amountReq.setTotal(refundAmount);
|
// {"amount3", "666"},
|
||||||
amountReq.setCurrency("CNY");
|
// {"thing10", "666666"},
|
||||||
createRequest.setAmount(amountReq);
|
// {"time8", "2019-12-18 12:12:12"}
|
||||||
// 申请退款
|
// };
|
||||||
System.out.println("退款请求:" + createRequest);
|
// for (String[] item : dataItems) {
|
||||||
Refund refund = wxPayConfig.getRefundService().create(createRequest);
|
// WxMaSubscribeMessage.MsgData msgData = new WxMaSubscribeMessage.MsgData();
|
||||||
System.out.println("退款申请结果:" + refund);
|
// msgData.setName(item[0]);
|
||||||
return refund;
|
// msgData.setValue(item[1]);
|
||||||
}
|
// msgDataList.add(msgData);
|
||||||
|
// }
|
||||||
@Override
|
// return msgDataList;
|
||||||
public RefundNotification getRefundInfo(HttpServletRequest request) {
|
// }
|
||||||
NotificationParser notificationParser = getNotificationParser(request);
|
//
|
||||||
return notificationParser.parse(requestParam, RefundNotification.class);
|
// /**
|
||||||
}
|
// * 根据微信官方发送的请求获取信息
|
||||||
|
// */
|
||||||
@Override
|
// @SneakyThrows
|
||||||
@Transactional(rollbackFor = Exception.class)
|
// public NotificationParser getNotificationParser(HttpServletRequest request) {
|
||||||
public synchronized boolean refundCallback(RefundNotification refundNotification) {
|
// System.out.println("---------------------------获取信息-------------------------------");
|
||||||
System.out.println("---------------------------微信退款回调(开始)-------------------------------");
|
// // 获取RSA配置
|
||||||
// 获取订单信息
|
// NotificationParser notificationParser = new NotificationParser(wxPayConfig.getRSAConfig());
|
||||||
String orderIdByString = refundNotification.getOutTradeNo();
|
// // 构建请求
|
||||||
Order order = ordersService.getById(orderIdByString);
|
// StringBuilder bodyBuilder = new StringBuilder();
|
||||||
// 修改订单信息
|
// BufferedReader reader = request.getReader();
|
||||||
order.setOrderStatus("已退款");
|
// String line;
|
||||||
ordersService.updateById(order);
|
// while ((line = reader.readLine()) != null) {
|
||||||
System.out.println("---------------------------微信退款回调(结束)-------------------------------");
|
// bodyBuilder.append(line);
|
||||||
return true;
|
// }
|
||||||
}
|
// String body = bodyBuilder.toString();
|
||||||
|
// String timestamp = request.getHeader("Wechatpay-Timestamp");
|
||||||
@Override
|
// String nonce = request.getHeader("Wechatpay-Nonce");
|
||||||
public boolean sendSubscribeMessage(String openid, String templateId) {
|
// String signature = request.getHeader("Wechatpay-Signature");
|
||||||
try {
|
// String singType = request.getHeader("Wechatpay-Signature-Type");
|
||||||
// 获取小程序对象
|
// String wechatPayCertificateSerialNumber = request.getHeader("Wechatpay-Serial");
|
||||||
WxMaService wxMaService = wxOpenConfig.getWxMaService();
|
// requestParam = new RequestParam.Builder()
|
||||||
// 获取消息接口
|
// .serialNumber(wechatPayCertificateSerialNumber)
|
||||||
WxMaMsgService msgService = wxMaService.getMsgService();
|
// .nonce(nonce)
|
||||||
// 构建订阅消息体
|
// .signature(signature)
|
||||||
WxMaSubscribeMessage wxMaSubscribeMessage = new WxMaSubscribeMessage();
|
// .timestamp(timestamp)
|
||||||
wxMaSubscribeMessage.setToUser(openid);
|
// .signType(singType)
|
||||||
wxMaSubscribeMessage.setTemplateId(templateId);
|
// .body(body)
|
||||||
List<WxMaSubscribeMessage.MsgData> msgDataList = getMsgData();
|
// .build();
|
||||||
wxMaSubscribeMessage.setData(msgDataList);
|
// System.out.println(requestParam.toString());
|
||||||
// 发送消息
|
// System.out.println("---------------------------信息获取完毕-------------------------------");
|
||||||
msgService.sendSubscribeMsg(wxMaSubscribeMessage);
|
// return notificationParser;
|
||||||
return true;
|
// }
|
||||||
} catch (Exception e) {
|
//}
|
||||||
log.error("发送订阅消息失败:", e);
|
|
||||||
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "发送订阅消息失败");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 提取消息数据
|
|
||||||
*/
|
|
||||||
private static List<WxMaSubscribeMessage.MsgData> getMsgData() {
|
|
||||||
List<WxMaSubscribeMessage.MsgData> msgDataList = new ArrayList<>();
|
|
||||||
String[][] dataItems = {
|
|
||||||
{"thing1", "新订单"},
|
|
||||||
{"phrase2", "已支付"},
|
|
||||||
{"amount3", "666"},
|
|
||||||
{"thing10", "666666"},
|
|
||||||
{"time8", "2019-12-18 12:12:12"}
|
|
||||||
};
|
|
||||||
for (String[] item : dataItems) {
|
|
||||||
WxMaSubscribeMessage.MsgData msgData = new WxMaSubscribeMessage.MsgData();
|
|
||||||
msgData.setName(item[0]);
|
|
||||||
msgData.setValue(item[1]);
|
|
||||||
msgDataList.add(msgData);
|
|
||||||
}
|
|
||||||
return msgDataList;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据微信官方发送的请求获取信息
|
|
||||||
*/
|
|
||||||
@SneakyThrows
|
|
||||||
public NotificationParser getNotificationParser(HttpServletRequest request) {
|
|
||||||
System.out.println("---------------------------获取信息-------------------------------");
|
|
||||||
// 获取RSA配置
|
|
||||||
NotificationParser notificationParser = new NotificationParser(wxPayConfig.getRSAConfig());
|
|
||||||
// 构建请求
|
|
||||||
StringBuilder bodyBuilder = new StringBuilder();
|
|
||||||
BufferedReader reader = request.getReader();
|
|
||||||
String line;
|
|
||||||
while ((line = reader.readLine()) != null) {
|
|
||||||
bodyBuilder.append(line);
|
|
||||||
}
|
|
||||||
String body = bodyBuilder.toString();
|
|
||||||
String timestamp = request.getHeader("Wechatpay-Timestamp");
|
|
||||||
String nonce = request.getHeader("Wechatpay-Nonce");
|
|
||||||
String signature = request.getHeader("Wechatpay-Signature");
|
|
||||||
String singType = request.getHeader("Wechatpay-Signature-Type");
|
|
||||||
String wechatPayCertificateSerialNumber = request.getHeader("Wechatpay-Serial");
|
|
||||||
requestParam = new RequestParam.Builder()
|
|
||||||
.serialNumber(wechatPayCertificateSerialNumber)
|
|
||||||
.nonce(nonce)
|
|
||||||
.signature(signature)
|
|
||||||
.timestamp(timestamp)
|
|
||||||
.signType(singType)
|
|
||||||
.body(body)
|
|
||||||
.build();
|
|
||||||
System.out.println(requestParam.toString());
|
|
||||||
System.out.println("---------------------------信息获取完毕-------------------------------");
|
|
||||||
return notificationParser;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -70,17 +70,19 @@ wx:
|
||||||
#应用id(小程序id)
|
#应用id(小程序id)
|
||||||
appId: wx61b63e27bddf4ea2
|
appId: wx61b63e27bddf4ea2
|
||||||
#商户号
|
#商户号
|
||||||
merchantId: 1700326544
|
#merchantId: 1700326544
|
||||||
|
mchSerialNo: 1700326544
|
||||||
#商户API私钥
|
#商户API私钥
|
||||||
privateKeyPath: apiclient_key.pem
|
privateKeyPath: apiclient_key.pem
|
||||||
#商户证书序列号
|
#商户证书序列号
|
||||||
merchantSerialNumber: 6DC8953AB741D309920DA650B92F837BE38A2757
|
merchantSerialNumber: 6DC8953AB741D309920DA650B92F837BE38A2757
|
||||||
#商户APIv3密钥
|
#商户APIv3密钥
|
||||||
apiV3Key: fbemuj4Xql7CYlQJAoTEPYxvPSNgYT2t
|
apiV3Key: fbemuj4Xql7CYlQJAoTEPYxvPSNgYT2t
|
||||||
# 通知地址
|
#通知地址
|
||||||
notifyUrl: https://winning-mouse-internally.ngrok-free.app
|
notifyUrl: https://winning-mouse-internally.ngrok-free.app
|
||||||
# notifyUrl: http://localhost:9092
|
#notifyUrl: http://localhost:9092
|
||||||
|
#微信服务器地址
|
||||||
|
domain: https://api.mch.weixin.qq.com
|
||||||
|
|
||||||
|
|
||||||
knife4j:
|
knife4j:
|
||||||
|
|
Loading…
Reference in New Issue
Block a user