删除gym-payment模块,与dev分支保持一致
This commit is contained in:
@@ -1,62 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
|
|
||||||
<parent>
|
|
||||||
<groupId>cn.novalon.gym.manage</groupId>
|
|
||||||
<artifactId>gym-manage-api</artifactId>
|
|
||||||
<version>1.0.0</version>
|
|
||||||
</parent>
|
|
||||||
|
|
||||||
<groupId>cn.novalon.gym.manage</groupId>
|
|
||||||
<artifactId>gym-payment</artifactId>
|
|
||||||
<packaging>jar</packaging>
|
|
||||||
|
|
||||||
<name>Gym Payment</name>
|
|
||||||
<description>支付模块 - 支付宝App支付</description>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<!-- Spring Boot WebFlux -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- 通用模块 (含Redis工具类) -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>cn.novalon.gym.manage</groupId>
|
|
||||||
<artifactId>manage-common</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- 支付宝SDK -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.alipay.sdk</groupId>
|
|
||||||
<artifactId>alipay-sdk-java</artifactId>
|
|
||||||
<version>4.35.120.ALL</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- JSON处理 -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.google.code.gson</groupId>
|
|
||||||
<artifactId>gson</artifactId>
|
|
||||||
<version>2.10.1</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- Lombok -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.projectlombok</groupId>
|
|
||||||
<artifactId>lombok</artifactId>
|
|
||||||
<optional>true</optional>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- Swagger -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.swagger.core.v3</groupId>
|
|
||||||
<artifactId>swagger-annotations</artifactId>
|
|
||||||
<version>2.2.19</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
</project>
|
|
||||||
-32
@@ -1,32 +0,0 @@
|
|||||||
package cn.novalon.gym.manage.payment.config;
|
|
||||||
|
|
||||||
import com.alipay.api.AlipayClient;
|
|
||||||
import com.alipay.api.DefaultAlipayClient;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 支付宝客户端配置
|
|
||||||
*/
|
|
||||||
@Configuration
|
|
||||||
public class AlipayClientConfig {
|
|
||||||
|
|
||||||
private final AlipayProperties alipayProperties;
|
|
||||||
|
|
||||||
public AlipayClientConfig(AlipayProperties alipayProperties) {
|
|
||||||
this.alipayProperties = alipayProperties;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public AlipayClient alipayClient() {
|
|
||||||
return new DefaultAlipayClient(
|
|
||||||
alipayProperties.getGateway(),
|
|
||||||
alipayProperties.getAppId(),
|
|
||||||
alipayProperties.getAppPrivateKey(),
|
|
||||||
"json",
|
|
||||||
alipayProperties.getCharset(),
|
|
||||||
alipayProperties.getAlipayPublicKey(),
|
|
||||||
alipayProperties.getSignType()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-57
@@ -1,57 +0,0 @@
|
|||||||
package cn.novalon.gym.manage.payment.config;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 支付宝配置属性
|
|
||||||
* 沙箱环境配置
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@Component
|
|
||||||
@ConfigurationProperties(prefix = "alipay")
|
|
||||||
public class AlipayProperties {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 应用ID
|
|
||||||
*/
|
|
||||||
private String appId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 应用私钥 (PKCS#8格式)
|
|
||||||
*/
|
|
||||||
private String appPrivateKey;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 支付宝公钥
|
|
||||||
*/
|
|
||||||
private String alipayPublicKey;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 签名方式
|
|
||||||
*/
|
|
||||||
private String signType = "RSA2";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 编码格式
|
|
||||||
*/
|
|
||||||
private String charset = "UTF-8";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 网关地址
|
|
||||||
* 沙箱环境: https://openapi-sandbox.dl.alipaydev.com/gateway.do
|
|
||||||
* 正式环境: https://openapi.alipay.com/gateway.do
|
|
||||||
*/
|
|
||||||
private String gateway;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 异步通知地址
|
|
||||||
*/
|
|
||||||
private String notifyUrl;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 是否沙箱环境
|
|
||||||
*/
|
|
||||||
private boolean sandbox = true;
|
|
||||||
}
|
|
||||||
-52
@@ -1,52 +0,0 @@
|
|||||||
package cn.novalon.gym.manage.payment.dto;
|
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Builder;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 统一API响应
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@Builder
|
|
||||||
@NoArgsConstructor
|
|
||||||
@AllArgsConstructor
|
|
||||||
public class ApiResponse<T> {
|
|
||||||
|
|
||||||
private int code;
|
|
||||||
private String message;
|
|
||||||
private T data;
|
|
||||||
|
|
||||||
public static <T> ApiResponse<T> success(T data) {
|
|
||||||
return ApiResponse.<T>builder()
|
|
||||||
.code(200)
|
|
||||||
.message("success")
|
|
||||||
.data(data)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> ApiResponse<T> success(String message, T data) {
|
|
||||||
return ApiResponse.<T>builder()
|
|
||||||
.code(200)
|
|
||||||
.message(message)
|
|
||||||
.data(data)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> ApiResponse<T> error(int code, String message) {
|
|
||||||
return ApiResponse.<T>builder()
|
|
||||||
.code(code)
|
|
||||||
.message(message)
|
|
||||||
.data(null)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> ApiResponse<T> error(String message) {
|
|
||||||
return ApiResponse.<T>builder()
|
|
||||||
.code(500)
|
|
||||||
.message(message)
|
|
||||||
.data(null)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-54
@@ -1,54 +0,0 @@
|
|||||||
package cn.novalon.gym.manage.payment.dto;
|
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建支付请求DTO
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@Schema(description = "创建支付请求")
|
|
||||||
public class CreatePaymentRequest {
|
|
||||||
|
|
||||||
@Schema(description = "会员ID")
|
|
||||||
private Long memberId;
|
|
||||||
|
|
||||||
@Schema(description = "订单类型: MEMBER_CARD-会员卡, GROUP_COURSE-团课, GOODS-商品")
|
|
||||||
private String orderType;
|
|
||||||
|
|
||||||
@Schema(description = "商品描述")
|
|
||||||
private String goodsDesc;
|
|
||||||
|
|
||||||
@Schema(description = "交易金额(单位:分)")
|
|
||||||
private String transAmt;
|
|
||||||
|
|
||||||
@Schema(description = "交易类型: ALIPAY-支付宝, WECHAT-微信")
|
|
||||||
private String tradeType;
|
|
||||||
|
|
||||||
@Schema(description = "备注")
|
|
||||||
private String remark;
|
|
||||||
|
|
||||||
@Schema(description = "账户号")
|
|
||||||
private String acctId;
|
|
||||||
|
|
||||||
@Schema(description = "过期时间(yyyyMMddHHmmss)")
|
|
||||||
private String timeExpire;
|
|
||||||
|
|
||||||
@Schema(description = "延迟入账标识")
|
|
||||||
private String delayAcctFlag;
|
|
||||||
|
|
||||||
@Schema(description = "手续费标识")
|
|
||||||
private Integer feeFlag;
|
|
||||||
|
|
||||||
@Schema(description = "禁用支付方式")
|
|
||||||
private String limitPayType;
|
|
||||||
|
|
||||||
@Schema(description = "渠道号")
|
|
||||||
private String channelNo;
|
|
||||||
|
|
||||||
@Schema(description = "支付场景")
|
|
||||||
private String payScene;
|
|
||||||
|
|
||||||
@Schema(description = "异步通知URL")
|
|
||||||
private String notifyUrl;
|
|
||||||
}
|
|
||||||
-54
@@ -1,54 +0,0 @@
|
|||||||
package cn.novalon.gym.manage.payment.dto;
|
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Builder;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 支付响应DTO
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@Builder
|
|
||||||
@NoArgsConstructor
|
|
||||||
@AllArgsConstructor
|
|
||||||
@Schema(description = "支付响应")
|
|
||||||
public class PaymentResponse {
|
|
||||||
|
|
||||||
@Schema(description = "订单ID")
|
|
||||||
private String orderId;
|
|
||||||
|
|
||||||
@Schema(description = "支付状态: PENDING-待支付, SUCCESS-成功, FAIL-失败, CLOSED-关闭")
|
|
||||||
private String status;
|
|
||||||
|
|
||||||
@Schema(description = "支付链接(App支付返回 scheme)")
|
|
||||||
private String payUrl;
|
|
||||||
|
|
||||||
@Schema(description = "二维码链接 (扫码支付)")
|
|
||||||
private String qrCode;
|
|
||||||
|
|
||||||
@Schema(description = "完整支付信息")
|
|
||||||
private String payInfo;
|
|
||||||
|
|
||||||
@Schema(description = "H5支付链接")
|
|
||||||
private String h5PayUrl;
|
|
||||||
|
|
||||||
@Schema(description = "错误码")
|
|
||||||
private String errorCode;
|
|
||||||
|
|
||||||
@Schema(description = "错误消息")
|
|
||||||
private String errorMsg;
|
|
||||||
|
|
||||||
@Schema(description = "交易金额(分)")
|
|
||||||
private String transAmt;
|
|
||||||
|
|
||||||
@Schema(description = "商品描述")
|
|
||||||
private String goodsDesc;
|
|
||||||
|
|
||||||
@Schema(description = "交易类型")
|
|
||||||
private String tradeType;
|
|
||||||
|
|
||||||
@Schema(description = "支付成功时间")
|
|
||||||
private String payTime;
|
|
||||||
}
|
|
||||||
-140
@@ -1,140 +0,0 @@
|
|||||||
package cn.novalon.gym.manage.payment.handler;
|
|
||||||
|
|
||||||
import cn.novalon.gym.manage.payment.dto.ApiResponse;
|
|
||||||
import cn.novalon.gym.manage.payment.dto.CreatePaymentRequest;
|
|
||||||
import cn.novalon.gym.manage.payment.dto.PaymentResponse;
|
|
||||||
import cn.novalon.gym.manage.payment.service.PaymentService;
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
|
||||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
|
||||||
import reactor.core.publisher.Mono;
|
|
||||||
import reactor.core.publisher.MonoSink;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 支付处理器
|
|
||||||
* 提供支付宝App支付相关接口
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
@Component
|
|
||||||
@Tag(name = "支付管理", description = "支付宝App支付接口")
|
|
||||||
public class PaymentHandler {
|
|
||||||
|
|
||||||
private final PaymentService paymentService;
|
|
||||||
|
|
||||||
public PaymentHandler(PaymentService paymentService) {
|
|
||||||
this.paymentService = paymentService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Operation(summary = "创建支付订单", description = "创建支付宝App支付订单")
|
|
||||||
public Mono<ServerResponse> createPayment(ServerRequest request) {
|
|
||||||
return request.bodyToMono(CreatePaymentRequest.class)
|
|
||||||
.flatMap(createReq -> {
|
|
||||||
log.info("[Payment] 创建支付订单: memberId={}, orderType={}, goodsDesc={}, transAmt={}",
|
|
||||||
createReq.getMemberId(), createReq.getOrderType(), createReq.getGoodsDesc(), createReq.getTransAmt());
|
|
||||||
|
|
||||||
PaymentResponse result = paymentService.alipayAppPay(createReq);
|
|
||||||
|
|
||||||
if ("FAIL".equals(result.getStatus())) {
|
|
||||||
return ServerResponse.ok()
|
|
||||||
.bodyValue(ApiResponse.error(result.getErrorMsg()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return ServerResponse.ok()
|
|
||||||
.bodyValue(ApiResponse.success(result));
|
|
||||||
})
|
|
||||||
.onErrorResume(e -> {
|
|
||||||
log.error("[Payment] 创建支付订单异常", e);
|
|
||||||
return ServerResponse.ok()
|
|
||||||
.bodyValue(ApiResponse.error("创建支付订单失败: " + e.getMessage()));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Operation(summary = "创建扫码支付订单", description = "创建支付宝扫码支付订单,返回二维码链接")
|
|
||||||
public Mono<ServerResponse> createQrCodePayment(ServerRequest request) {
|
|
||||||
return request.bodyToMono(CreatePaymentRequest.class)
|
|
||||||
.flatMap(createReq -> {
|
|
||||||
log.info("[Payment] 创建扫码支付订单: memberId={}, orderType={}, goodsDesc={}, transAmt={}",
|
|
||||||
createReq.getMemberId(), createReq.getOrderType(), createReq.getGoodsDesc(), createReq.getTransAmt());
|
|
||||||
|
|
||||||
PaymentResponse result = paymentService.alipayQrCodePay(createReq);
|
|
||||||
|
|
||||||
if ("FAIL".equals(result.getStatus())) {
|
|
||||||
return ServerResponse.ok()
|
|
||||||
.bodyValue(ApiResponse.error(result.getErrorMsg()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return ServerResponse.ok()
|
|
||||||
.bodyValue(ApiResponse.success(result));
|
|
||||||
})
|
|
||||||
.onErrorResume(e -> {
|
|
||||||
log.error("[Payment] 创建扫码支付订单异常", e);
|
|
||||||
return ServerResponse.ok()
|
|
||||||
.bodyValue(ApiResponse.error("创建扫码支付订单失败: " + e.getMessage()));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Operation(summary = "查询支付状态", description = "查询支付订单状态")
|
|
||||||
public Mono<ServerResponse> getPaymentStatus(ServerRequest request) {
|
|
||||||
String orderId = request.pathVariable("orderId");
|
|
||||||
log.info("[Payment] 查询支付状态: orderId={}", orderId);
|
|
||||||
|
|
||||||
PaymentResponse result = paymentService.getPaymentStatus(orderId);
|
|
||||||
return ServerResponse.ok()
|
|
||||||
.bodyValue(ApiResponse.success(result));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Operation(summary = "支付宝异步通知", description = "接收支付宝异步回调通知")
|
|
||||||
public Mono<ServerResponse> alipayNotify(ServerRequest request) {
|
|
||||||
return request.bodyToMono(Map.class)
|
|
||||||
.flatMap(params -> {
|
|
||||||
log.info("[Payment] 收到支付宝异步通知: params={}", params);
|
|
||||||
|
|
||||||
// 转换Map类型
|
|
||||||
Map<String, String> notifyParams = new HashMap<>();
|
|
||||||
if (params instanceof Map) {
|
|
||||||
((Map<?, ?>) params).forEach((key, value) ->
|
|
||||||
notifyParams.put(String.valueOf(key), String.valueOf(value)));
|
|
||||||
}
|
|
||||||
|
|
||||||
String result = paymentService.handleAlipayNotify(notifyParams);
|
|
||||||
return ServerResponse.ok()
|
|
||||||
.bodyValue(result);
|
|
||||||
})
|
|
||||||
.onErrorResume(e -> {
|
|
||||||
log.error("[Payment] 异步通知处理异常", e);
|
|
||||||
return ServerResponse.ok()
|
|
||||||
.bodyValue("fail");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Operation(summary = "申请退款", description = "申请支付退款")
|
|
||||||
public Mono<ServerResponse> refundPayment(ServerRequest request) {
|
|
||||||
String orderId = request.pathVariable("orderId");
|
|
||||||
|
|
||||||
return request.bodyToMono(Map.class)
|
|
||||||
.flatMap(body -> {
|
|
||||||
String refundAmt = String.valueOf(body.get("refundAmt"));
|
|
||||||
log.info("[Payment] 申请退款: orderId={}, refundAmt={}", orderId, refundAmt);
|
|
||||||
|
|
||||||
boolean success = paymentService.refund(orderId, refundAmt);
|
|
||||||
if (success) {
|
|
||||||
return ServerResponse.ok()
|
|
||||||
.bodyValue(ApiResponse.success("退款申请成功"));
|
|
||||||
} else {
|
|
||||||
return ServerResponse.ok()
|
|
||||||
.bodyValue(ApiResponse.error("退款申请失败"));
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.onErrorResume(e -> {
|
|
||||||
log.error("[Payment] 退款异常: orderId={}", orderId, e);
|
|
||||||
return ServerResponse.ok()
|
|
||||||
.bodyValue(ApiResponse.error("退款失败: " + e.getMessage()));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-59
@@ -1,59 +0,0 @@
|
|||||||
package cn.novalon.gym.manage.payment.service;
|
|
||||||
|
|
||||||
import cn.novalon.gym.manage.payment.dto.CreatePaymentRequest;
|
|
||||||
import cn.novalon.gym.manage.payment.dto.PaymentResponse;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 支付服务接口
|
|
||||||
*/
|
|
||||||
public interface PaymentService {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建支付订单
|
|
||||||
*
|
|
||||||
* @param request 创建支付请求
|
|
||||||
* @return 支付响应
|
|
||||||
*/
|
|
||||||
PaymentResponse createPayment(CreatePaymentRequest request);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询支付状态
|
|
||||||
*
|
|
||||||
* @param orderId 订单ID
|
|
||||||
* @return 支付响应
|
|
||||||
*/
|
|
||||||
PaymentResponse getPaymentStatus(String orderId);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 支付宝App支付
|
|
||||||
*
|
|
||||||
* @param request 创建支付请求
|
|
||||||
* @return 支付响应
|
|
||||||
*/
|
|
||||||
PaymentResponse alipayAppPay(CreatePaymentRequest request);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 支付宝扫码支付(二维码支付)
|
|
||||||
*
|
|
||||||
* @param request 创建支付请求
|
|
||||||
* @return 支付响应(包含二维码链接)
|
|
||||||
*/
|
|
||||||
PaymentResponse alipayQrCodePay(CreatePaymentRequest request);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理支付宝异步通知
|
|
||||||
*
|
|
||||||
* @param params 通知参数
|
|
||||||
* @return 处理结果
|
|
||||||
*/
|
|
||||||
String handleAlipayNotify(java.util.Map<String, String> params);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 申请退款
|
|
||||||
*
|
|
||||||
* @param orderId 订单ID
|
|
||||||
* @param refundAmt 退款金额(分)
|
|
||||||
* @return 退款结果
|
|
||||||
*/
|
|
||||||
boolean refund(String orderId, String refundAmt);
|
|
||||||
}
|
|
||||||
-445
@@ -1,445 +0,0 @@
|
|||||||
package cn.novalon.gym.manage.payment.service.impl;
|
|
||||||
|
|
||||||
import cn.novalon.gym.manage.common.util.RedisUtil;
|
|
||||||
import cn.novalon.gym.manage.payment.config.AlipayProperties;
|
|
||||||
import cn.novalon.gym.manage.payment.dto.CreatePaymentRequest;
|
|
||||||
import cn.novalon.gym.manage.payment.dto.PaymentResponse;
|
|
||||||
import cn.novalon.gym.manage.payment.service.PaymentService;
|
|
||||||
import com.alipay.api.AlipayApiException;
|
|
||||||
import com.alipay.api.AlipayClient;
|
|
||||||
import com.alipay.api.internal.util.AlipaySignature;
|
|
||||||
import com.alipay.api.request.AlipayTradeAppPayRequest;
|
|
||||||
import com.alipay.api.request.AlipayTradePrecreateRequest;
|
|
||||||
import com.alipay.api.request.AlipayTradeQueryRequest;
|
|
||||||
import com.alipay.api.request.AlipayTradeRefundRequest;
|
|
||||||
import com.alipay.api.response.AlipayTradeAppPayResponse;
|
|
||||||
import com.alipay.api.response.AlipayTradePrecreateResponse;
|
|
||||||
import com.alipay.api.response.AlipayTradeQueryResponse;
|
|
||||||
import com.alipay.api.response.AlipayTradeRefundResponse;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 支付服务实现
|
|
||||||
* App支付 (alipay.trade.app.pay)
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
@Service
|
|
||||||
public class PaymentServiceImpl implements PaymentService {
|
|
||||||
|
|
||||||
private static final String PROCESSED_TRADE_PREFIX = "alipay:processed:trade:";
|
|
||||||
private static final String REFUND_ORDER_PREFIX = "alipay:refund:order:";
|
|
||||||
private static final String ORDER_CACHE_PREFIX = "alipay:order:";
|
|
||||||
private static final long IDEMPOTENT_EXPIRE_SECONDS = 86400; // 24小时过期
|
|
||||||
|
|
||||||
private final AlipayClient alipayClient;
|
|
||||||
private final AlipayProperties alipayProperties;
|
|
||||||
private final RedisUtil redisUtil;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 订单本地缓存 (作为Redis的二级缓存)
|
|
||||||
*/
|
|
||||||
private final Map<String, PaymentResponse> localOrderCache = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
public PaymentServiceImpl(AlipayClient alipayClient, AlipayProperties alipayProperties, RedisUtil redisUtil) {
|
|
||||||
this.alipayClient = alipayClient;
|
|
||||||
this.alipayProperties = alipayProperties;
|
|
||||||
this.redisUtil = redisUtil;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PaymentResponse createPayment(CreatePaymentRequest request) {
|
|
||||||
// App支付使用 alipay.trade.app.pay
|
|
||||||
return alipayAppPay(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PaymentResponse getPaymentStatus(String orderId) {
|
|
||||||
// 先查本地缓存
|
|
||||||
PaymentResponse cached = localOrderCache.get(orderId);
|
|
||||||
if (cached != null && "SUCCESS".equals(cached.getStatus())) {
|
|
||||||
return cached;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查Redis缓存
|
|
||||||
PaymentResponse redisCached = getOrderFromRedis(orderId);
|
|
||||||
if (redisCached != null && "SUCCESS".equals(redisCached.getStatus())) {
|
|
||||||
localOrderCache.put(orderId, redisCached);
|
|
||||||
return redisCached;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
AlipayTradeQueryRequest queryRequest = new AlipayTradeQueryRequest();
|
|
||||||
queryRequest.setBizContent("{\"out_trade_no\":\"" + orderId + "\"}");
|
|
||||||
|
|
||||||
AlipayTradeQueryResponse response = alipayClient.execute(queryRequest);
|
|
||||||
if (response.isSuccess()) {
|
|
||||||
Date sendPayDate = response.getSendPayDate();
|
|
||||||
String payTimeStr = sendPayDate != null ? new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(sendPayDate) : null;
|
|
||||||
|
|
||||||
PaymentResponse result = PaymentResponse.builder()
|
|
||||||
.orderId(orderId)
|
|
||||||
.status(response.getTradeStatus())
|
|
||||||
.payTime(payTimeStr)
|
|
||||||
.transAmt(response.getTotalAmount())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
if ("TRADE_SUCCESS".equals(response.getTradeStatus())) {
|
|
||||||
result.setStatus("SUCCESS");
|
|
||||||
// 更新缓存
|
|
||||||
saveOrderToRedis(orderId, result);
|
|
||||||
localOrderCache.put(orderId, result);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
} catch (AlipayApiException e) {
|
|
||||||
log.error("查询支付状态失败: orderId={}", orderId, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return PaymentResponse.builder()
|
|
||||||
.orderId(orderId)
|
|
||||||
.status(cached != null ? cached.getStatus() : (redisCached != null ? redisCached.getStatus() : "PENDING"))
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PaymentResponse alipayAppPay(CreatePaymentRequest request) {
|
|
||||||
// 生成订单号
|
|
||||||
String orderId = generateOrderId();
|
|
||||||
|
|
||||||
try {
|
|
||||||
AlipayTradeAppPayRequest payRequest = new AlipayTradeAppPayRequest();
|
|
||||||
payRequest.setNotifyUrl(alipayProperties.getNotifyUrl());
|
|
||||||
|
|
||||||
// 构建业务参数 (测试金额:固定1分钱)
|
|
||||||
String bizContent = "{\"out_trade_no\":\"" + orderId + "\","
|
|
||||||
+ "\"total_amount\":\"0.01\","
|
|
||||||
+ "\"subject\":\"" + request.getGoodsDesc() + "\","
|
|
||||||
+ "\"product_code\":\"QUICK_MSECURITY_PAY\","
|
|
||||||
+ "\"timeout_express\":\"30m\"}";
|
|
||||||
|
|
||||||
log.info("[Alipay] App支付请求: orderId={}, bizContent={}", orderId, bizContent);
|
|
||||||
|
|
||||||
payRequest.setBizContent(bizContent);
|
|
||||||
|
|
||||||
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(payRequest);
|
|
||||||
|
|
||||||
if (response.isSuccess()) {
|
|
||||||
String payUrl = response.getBody();
|
|
||||||
|
|
||||||
// App唤起支付宝的scheme格式: alipay://...
|
|
||||||
// 实际使用时,前端使用 plus.runtime.openURL(payUrl) 唤起支付宝
|
|
||||||
log.info("[Alipay] App支付创建成功: orderId={}, payUrl={}", orderId, payUrl);
|
|
||||||
|
|
||||||
PaymentResponse result = PaymentResponse.builder()
|
|
||||||
.orderId(orderId)
|
|
||||||
.status("PENDING")
|
|
||||||
.payUrl(payUrl)
|
|
||||||
.payInfo(response.getBody())
|
|
||||||
.goodsDesc(request.getGoodsDesc())
|
|
||||||
.transAmt(request.getTransAmt())
|
|
||||||
.tradeType(request.getTradeType())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
// 缓存订单到Redis和本地
|
|
||||||
saveOrderToRedis(orderId, result);
|
|
||||||
localOrderCache.put(orderId, result);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
} else {
|
|
||||||
log.error("[Alipay] App支付创建失败: orderId={}, error={}", orderId, response.getMsg());
|
|
||||||
return PaymentResponse.builder()
|
|
||||||
.orderId(orderId)
|
|
||||||
.status("FAIL")
|
|
||||||
.errorCode(response.getCode())
|
|
||||||
.errorMsg(response.getMsg())
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
} catch (AlipayApiException e) {
|
|
||||||
log.error("[Alipay] App支付异常: orderId={}", orderId, e);
|
|
||||||
String errCode = e.getErrCode() != null ? e.getErrCode() : "NETWORK_ERROR";
|
|
||||||
String errMsg = e.getErrMsg() != null ? e.getErrMsg() : extractErrorMessage(e);
|
|
||||||
return PaymentResponse.builder()
|
|
||||||
.orderId(orderId)
|
|
||||||
.status("FAIL")
|
|
||||||
.errorCode(errCode)
|
|
||||||
.errorMsg(errMsg)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 支付宝扫码支付 (alipay.trade.precreate)
|
|
||||||
* 生成付款二维码,用户使用支付宝扫码完成支付
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public PaymentResponse alipayQrCodePay(CreatePaymentRequest request) {
|
|
||||||
String orderId = generateOrderId();
|
|
||||||
|
|
||||||
try {
|
|
||||||
AlipayTradePrecreateRequest precreateRequest = new AlipayTradePrecreateRequest();
|
|
||||||
precreateRequest.setNotifyUrl(alipayProperties.getNotifyUrl());
|
|
||||||
|
|
||||||
// 构建业务参数
|
|
||||||
String bizContent = "{\"out_trade_no\":\"" + orderId + "\","
|
|
||||||
+ "\"total_amount\":\"0.01\","
|
|
||||||
+ "\"subject\":\"" + request.getGoodsDesc() + "\","
|
|
||||||
+ "\"timeout_express\":\"30m\"}";
|
|
||||||
|
|
||||||
log.info("[Alipay] 扫码支付请求: orderId={}, bizContent={}", orderId, bizContent);
|
|
||||||
|
|
||||||
precreateRequest.setBizContent(bizContent);
|
|
||||||
AlipayTradePrecreateResponse response = alipayClient.execute(precreateRequest);
|
|
||||||
|
|
||||||
if (response.isSuccess()) {
|
|
||||||
String qrCode = response.getQrCode(); // 二维码链接
|
|
||||||
|
|
||||||
log.info("[Alipay] 扫码支付创建成功: orderId={}, qrCode={}", orderId, qrCode);
|
|
||||||
|
|
||||||
PaymentResponse result = PaymentResponse.builder()
|
|
||||||
.orderId(orderId)
|
|
||||||
.status("PENDING")
|
|
||||||
.qrCode(qrCode)
|
|
||||||
.payInfo(response.getBody())
|
|
||||||
.goodsDesc(request.getGoodsDesc())
|
|
||||||
.transAmt(request.getTransAmt())
|
|
||||||
.tradeType("QRCODE")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
// 缓存订单到Redis和本地
|
|
||||||
saveOrderToRedis(orderId, result);
|
|
||||||
localOrderCache.put(orderId, result);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
} else {
|
|
||||||
log.error("[Alipay] 扫码支付创建失败: orderId={}, error={}", orderId, response.getMsg());
|
|
||||||
return PaymentResponse.builder()
|
|
||||||
.orderId(orderId)
|
|
||||||
.status("FAIL")
|
|
||||||
.errorCode(response.getCode())
|
|
||||||
.errorMsg(response.getMsg())
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
} catch (AlipayApiException e) {
|
|
||||||
log.error("[Alipay] 扫码支付异常: orderId={}", orderId, e);
|
|
||||||
String errCode = e.getErrCode() != null ? e.getErrCode() : "NETWORK_ERROR";
|
|
||||||
String errMsg = e.getErrMsg() != null ? e.getErrMsg() : extractErrorMessage(e);
|
|
||||||
return PaymentResponse.builder()
|
|
||||||
.orderId(orderId)
|
|
||||||
.status("FAIL")
|
|
||||||
.errorCode(errCode)
|
|
||||||
.errorMsg(errMsg)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String handleAlipayNotify(Map<String, String> params) {
|
|
||||||
String outTradeNo = params.get("out_trade_no");
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 1. 幂等处理:检查是否已处理过该通知(使用Redis)
|
|
||||||
if (isTradeProcessed(outTradeNo)) {
|
|
||||||
log.info("[Alipay] 幂等处理:通知已处理,跳过, outTradeNo={}", outTradeNo);
|
|
||||||
return "success";
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 验签
|
|
||||||
boolean signVerified = AlipaySignature.rsaCheckV1(
|
|
||||||
params,
|
|
||||||
alipayProperties.getAlipayPublicKey(),
|
|
||||||
alipayProperties.getCharset(),
|
|
||||||
alipayProperties.getSignType()
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!signVerified) {
|
|
||||||
log.warn("[Alipay] 异步通知验签失败: outTradeNo={}", outTradeNo);
|
|
||||||
return "fail";
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. 关键信息校验
|
|
||||||
// 校验 app_id 是否匹配
|
|
||||||
String notifyAppId = params.get("app_id");
|
|
||||||
if (!alipayProperties.getAppId().equals(notifyAppId)) {
|
|
||||||
log.warn("[Alipay] app_id 不匹配: expected={}, actual={}", alipayProperties.getAppId(), notifyAppId);
|
|
||||||
return "fail";
|
|
||||||
}
|
|
||||||
|
|
||||||
// 校验卖家ID (seller_id)
|
|
||||||
String sellerId = params.get("seller_id");
|
|
||||||
if (sellerId != null && !sellerId.isEmpty()) {
|
|
||||||
// 商家ID校验逻辑根据实际情况添加
|
|
||||||
log.debug("[Alipay] 卖家ID校验: seller_id={}", sellerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 校验交易金额
|
|
||||||
String notifyTotalAmount = params.get("total_amount");
|
|
||||||
PaymentResponse cachedOrder = getOrderFromRedis(outTradeNo);
|
|
||||||
if (cachedOrder != null && notifyTotalAmount != null) {
|
|
||||||
// 金额校验:确保通知金额与订单金额一致(防止金额篡改)
|
|
||||||
log.debug("[Alipay] 交易金额校验: orderId={}, amount={}", outTradeNo, notifyTotalAmount);
|
|
||||||
}
|
|
||||||
|
|
||||||
String tradeStatus = params.get("trade_status");
|
|
||||||
log.info("[Alipay] 异步通知验签成功: outTradeNo={}, tradeStatus={}", outTradeNo, tradeStatus);
|
|
||||||
|
|
||||||
// 4. 判断交易状态
|
|
||||||
if ("TRADE_SUCCESS".equals(tradeStatus) || "TRADE_FINISHED".equals(tradeStatus)) {
|
|
||||||
// 更新订单状态
|
|
||||||
if (cachedOrder != null) {
|
|
||||||
cachedOrder.setStatus("SUCCESS");
|
|
||||||
cachedOrder.setPayTime(params.get("gmt_payment"));
|
|
||||||
saveOrderToRedis(outTradeNo, cachedOrder);
|
|
||||||
localOrderCache.put(outTradeNo, cachedOrder);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. 标记为已处理(幂等,使用Redis)
|
|
||||||
markTradeProcessed(outTradeNo);
|
|
||||||
|
|
||||||
return "success";
|
|
||||||
}
|
|
||||||
|
|
||||||
return "fail";
|
|
||||||
} catch (AlipayApiException e) {
|
|
||||||
log.error("[Alipay] 异步通知处理异常: outTradeNo={}", outTradeNo, e);
|
|
||||||
return "fail";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean refund(String orderId, String refundAmt) {
|
|
||||||
// 1. 退款幂等检查:防止重复退款(使用Redis)
|
|
||||||
if (isOrderRefunded(orderId)) {
|
|
||||||
log.info("[Alipay] 退款幂等:订单已退款过,跳过, orderId={}", orderId);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
AlipayTradeRefundRequest refundRequest = new AlipayTradeRefundRequest();
|
|
||||||
refundRequest.setBizContent("{\"out_trade_no\":\"" + orderId + "\",\"refund_amount\":\"" + refundAmt + "\"}");
|
|
||||||
|
|
||||||
AlipayTradeRefundResponse response = alipayClient.execute(refundRequest);
|
|
||||||
if (response.isSuccess()) {
|
|
||||||
// 2. 校验退款是否真正成功:必须判断 fund_change=Y
|
|
||||||
String fundChange = response.getFundChange();
|
|
||||||
if ("Y".equals(fundChange)) {
|
|
||||||
log.info("[Alipay] 退款成功: orderId={}, refundAmt={}, fundChange={}", orderId, refundAmt, fundChange);
|
|
||||||
// 标记为已退款(幂等,使用Redis)
|
|
||||||
markOrderRefunded(orderId);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
log.warn("[Alipay] 退款返回Y但fund_change!=Y: orderId={}, fundChange={}", orderId, fundChange);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.error("[Alipay] 退款失败: orderId={}, error={}", orderId, response.getMsg());
|
|
||||||
}
|
|
||||||
} catch (AlipayApiException e) {
|
|
||||||
log.error("[Alipay] 退款异常: orderId={}", orderId, e);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================== Redis操作方法 ====================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查交易是否已处理(幂等)
|
|
||||||
*/
|
|
||||||
private boolean isTradeProcessed(String outTradeNo) {
|
|
||||||
try {
|
|
||||||
return Boolean.TRUE.equals(redisUtil.hasKey(PROCESSED_TRADE_PREFIX + outTradeNo).block());
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("[Alipay] 检查交易处理状态异常: outTradeNo={}", outTradeNo, e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 标记交易已处理(幂等)
|
|
||||||
*/
|
|
||||||
private void markTradeProcessed(String outTradeNo) {
|
|
||||||
try {
|
|
||||||
redisUtil.setWithExpire(PROCESSED_TRADE_PREFIX + outTradeNo, "1", IDEMPOTENT_EXPIRE_SECONDS).block();
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("[Alipay] 标记交易处理状态异常: outTradeNo={}", outTradeNo, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查订单是否已退款(幂等)
|
|
||||||
*/
|
|
||||||
private boolean isOrderRefunded(String orderId) {
|
|
||||||
try {
|
|
||||||
return Boolean.TRUE.equals(redisUtil.hasKey(REFUND_ORDER_PREFIX + orderId).block());
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("[Alipay] 检查订单退款状态异常: orderId={}", orderId, e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 标记订单已退款(幂等)
|
|
||||||
*/
|
|
||||||
private void markOrderRefunded(String orderId) {
|
|
||||||
try {
|
|
||||||
redisUtil.setWithExpire(REFUND_ORDER_PREFIX + orderId, "1", IDEMPOTENT_EXPIRE_SECONDS).block();
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("[Alipay] 标记订单退款状态异常: orderId={}", orderId, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 从Redis获取订单
|
|
||||||
*/
|
|
||||||
private PaymentResponse getOrderFromRedis(String orderId) {
|
|
||||||
try {
|
|
||||||
return redisUtil.get(ORDER_CACHE_PREFIX + orderId, PaymentResponse.class).block();
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("[Alipay] 获取Redis订单异常: orderId={}", orderId, e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 保存订单到Redis
|
|
||||||
*/
|
|
||||||
private void saveOrderToRedis(String orderId, PaymentResponse response) {
|
|
||||||
try {
|
|
||||||
redisUtil.setWithExpire(ORDER_CACHE_PREFIX + orderId, response, IDEMPOTENT_EXPIRE_SECONDS).block();
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("[Alipay] 保存订单到Redis异常: orderId={}", orderId, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 生成订单号
|
|
||||||
*/
|
|
||||||
private String generateOrderId() {
|
|
||||||
String timestamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
|
|
||||||
String uuid = UUID.randomUUID().toString().replace("-", "").substring(0, 8);
|
|
||||||
return timestamp + uuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 提取异常中的错误信息(兜底)
|
|
||||||
*/
|
|
||||||
private String extractErrorMessage(AlipayApiException e) {
|
|
||||||
Throwable cause = e.getCause();
|
|
||||||
if (cause != null && cause.getMessage() != null && cause.getMessage().contains("504")) {
|
|
||||||
return "支付宝沙箱网关连接超时(504),请检查服务器网络能否访问 openapi-sandbox.dl.alipaydev.com";
|
|
||||||
}
|
|
||||||
if (cause != null) {
|
|
||||||
return "网络异常: " + cause.getClass().getSimpleName() + " - " + (cause.getMessage() != null ? cause.getMessage().substring(0, Math.min(cause.getMessage().length(), 200)) : "");
|
|
||||||
}
|
|
||||||
if (e.getMessage() != null) {
|
|
||||||
return "支付宝接口异常: " + (e.getMessage().length() > 200 ? e.getMessage().substring(0, 200) : e.getMessage());
|
|
||||||
}
|
|
||||||
return "支付宝接口调用失败,请稍后重试";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -48,11 +48,6 @@
|
|||||||
<artifactId>gym-checkIn</artifactId>
|
<artifactId>gym-checkIn</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>cn.novalon.gym.manage</groupId>
|
|
||||||
<artifactId>gym-payment</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cn.novalon.gym.manage</groupId>
|
<groupId>cn.novalon.gym.manage</groupId>
|
||||||
<artifactId>gym-dataCount</artifactId>
|
<artifactId>gym-dataCount</artifactId>
|
||||||
|
|||||||
@@ -47,7 +47,6 @@
|
|||||||
<module>gym-checkIn</module>
|
<module>gym-checkIn</module>
|
||||||
<module>gym-dataCount</module>
|
<module>gym-dataCount</module>
|
||||||
<module>gym-auth</module>
|
<module>gym-auth</module>
|
||||||
<module>gym-payment</module>
|
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
|
|||||||
Reference in New Issue
Block a user