From e81ec8cca7c6a97c1f9fe767790ac1a27b47556a Mon Sep 17 00:00:00 2001 From: future <1360317836@qq.com> Date: Wed, 24 Jun 2026 23:23:55 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=A0=E9=99=A4gym-payment=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=EF=BC=8C=E4=B8=8Edev=E5=88=86=E6=94=AF=E4=BF=9D=E6=8C=81?= =?UTF-8?q?=E4=B8=80=E8=87=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gym-manage-api/gym-payment/pom.xml | 62 --- .../payment/config/AlipayClientConfig.java | 32 -- .../payment/config/AlipayProperties.java | 57 --- .../gym/manage/payment/dto/ApiResponse.java | 52 -- .../payment/dto/CreatePaymentRequest.java | 54 --- .../manage/payment/dto/PaymentResponse.java | 54 --- .../payment/handler/PaymentHandler.java | 140 ------ .../payment/service/PaymentService.java | 59 --- .../service/impl/PaymentServiceImpl.java | 445 ------------------ gym-manage-api/manage-app/pom.xml | 5 - gym-manage-api/pom.xml | 1 - 11 files changed, 961 deletions(-) delete mode 100644 gym-manage-api/gym-payment/pom.xml delete mode 100644 gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/config/AlipayClientConfig.java delete mode 100644 gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/config/AlipayProperties.java delete mode 100644 gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/dto/ApiResponse.java delete mode 100644 gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/dto/CreatePaymentRequest.java delete mode 100644 gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/dto/PaymentResponse.java delete mode 100644 gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/handler/PaymentHandler.java delete mode 100644 gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/service/PaymentService.java delete mode 100644 gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/service/impl/PaymentServiceImpl.java diff --git a/gym-manage-api/gym-payment/pom.xml b/gym-manage-api/gym-payment/pom.xml deleted file mode 100644 index fc273a9..0000000 --- a/gym-manage-api/gym-payment/pom.xml +++ /dev/null @@ -1,62 +0,0 @@ - - - 4.0.0 - - - cn.novalon.gym.manage - gym-manage-api - 1.0.0 - - - cn.novalon.gym.manage - gym-payment - jar - - Gym Payment - 支付模块 - 支付宝App支付 - - - - - org.springframework.boot - spring-boot-starter-webflux - - - - - cn.novalon.gym.manage - manage-common - ${project.version} - - - - - com.alipay.sdk - alipay-sdk-java - 4.35.120.ALL - - - - - com.google.code.gson - gson - 2.10.1 - - - - - org.projectlombok - lombok - true - - - - - io.swagger.core.v3 - swagger-annotations - 2.2.19 - - - diff --git a/gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/config/AlipayClientConfig.java b/gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/config/AlipayClientConfig.java deleted file mode 100644 index e0b747e..0000000 --- a/gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/config/AlipayClientConfig.java +++ /dev/null @@ -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() - ); - } -} diff --git a/gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/config/AlipayProperties.java b/gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/config/AlipayProperties.java deleted file mode 100644 index f78fd71..0000000 --- a/gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/config/AlipayProperties.java +++ /dev/null @@ -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; -} diff --git a/gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/dto/ApiResponse.java b/gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/dto/ApiResponse.java deleted file mode 100644 index b01b20a..0000000 --- a/gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/dto/ApiResponse.java +++ /dev/null @@ -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 { - - private int code; - private String message; - private T data; - - public static ApiResponse success(T data) { - return ApiResponse.builder() - .code(200) - .message("success") - .data(data) - .build(); - } - - public static ApiResponse success(String message, T data) { - return ApiResponse.builder() - .code(200) - .message(message) - .data(data) - .build(); - } - - public static ApiResponse error(int code, String message) { - return ApiResponse.builder() - .code(code) - .message(message) - .data(null) - .build(); - } - - public static ApiResponse error(String message) { - return ApiResponse.builder() - .code(500) - .message(message) - .data(null) - .build(); - } -} diff --git a/gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/dto/CreatePaymentRequest.java b/gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/dto/CreatePaymentRequest.java deleted file mode 100644 index 5b1347e..0000000 --- a/gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/dto/CreatePaymentRequest.java +++ /dev/null @@ -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; -} diff --git a/gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/dto/PaymentResponse.java b/gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/dto/PaymentResponse.java deleted file mode 100644 index 50fd4cb..0000000 --- a/gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/dto/PaymentResponse.java +++ /dev/null @@ -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; -} diff --git a/gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/handler/PaymentHandler.java b/gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/handler/PaymentHandler.java deleted file mode 100644 index e402f0c..0000000 --- a/gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/handler/PaymentHandler.java +++ /dev/null @@ -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 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 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 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 alipayNotify(ServerRequest request) { - return request.bodyToMono(Map.class) - .flatMap(params -> { - log.info("[Payment] 收到支付宝异步通知: params={}", params); - - // 转换Map类型 - Map 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 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())); - }); - } -} diff --git a/gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/service/PaymentService.java b/gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/service/PaymentService.java deleted file mode 100644 index 7170ce5..0000000 --- a/gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/service/PaymentService.java +++ /dev/null @@ -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 params); - - /** - * 申请退款 - * - * @param orderId 订单ID - * @param refundAmt 退款金额(分) - * @return 退款结果 - */ - boolean refund(String orderId, String refundAmt); -} diff --git a/gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/service/impl/PaymentServiceImpl.java b/gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/service/impl/PaymentServiceImpl.java deleted file mode 100644 index 2731d27..0000000 --- a/gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/service/impl/PaymentServiceImpl.java +++ /dev/null @@ -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 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 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 "支付宝接口调用失败,请稍后重试"; - } -} diff --git a/gym-manage-api/manage-app/pom.xml b/gym-manage-api/manage-app/pom.xml index ed55a4e..7a15e65 100644 --- a/gym-manage-api/manage-app/pom.xml +++ b/gym-manage-api/manage-app/pom.xml @@ -48,11 +48,6 @@ gym-checkIn ${project.version} - - cn.novalon.gym.manage - gym-payment - ${project.version} - cn.novalon.gym.manage gym-dataCount diff --git a/gym-manage-api/pom.xml b/gym-manage-api/pom.xml index 69441c7..8746a4a 100644 --- a/gym-manage-api/pom.xml +++ b/gym-manage-api/pom.xml @@ -47,7 +47,6 @@ gym-checkIn gym-dataCount gym-auth - gym-payment