From be4b9792150ddf4424785b6e611006c8546bc07e Mon Sep 17 00:00:00 2001 From: ooinl77 Date: Tue, 16 Dec 2025 21:30:58 +0900 Subject: [PATCH 01/16] =?UTF-8?q?refactor:=20kafka=20inbound=20=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=20=EC=83=9D=EC=84=B1=EC=9E=90,=20setter=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95=20fix:=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=EB=A5=BC=20=EC=9C=84=ED=95=9C=20?= =?UTF-8?q?=EC=A0=95=EC=B1=85=20sql=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kafka/dto/PaymentCreateRequestEvent.java | 24 +++++++------ .../kafka/dto/PaymentRefundRequestEvent.java | 22 ++++++------ .../kafka/dto/SettlementRequestEvent.java | 16 +++++---- payment/src/main/resources/data.sql | 34 ++++++++++++++++--- 4 files changed, 64 insertions(+), 32 deletions(-) diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/dto/PaymentCreateRequestEvent.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/dto/PaymentCreateRequestEvent.java index 347d4b50..845944c1 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/dto/PaymentCreateRequestEvent.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/dto/PaymentCreateRequestEvent.java @@ -1,21 +1,23 @@ package com.smore.payment.payment.infrastructure.kafka.dto; -import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; import java.time.LocalDateTime; import java.util.UUID; @Getter -@AllArgsConstructor +@Setter +@NoArgsConstructor public class PaymentCreateRequestEvent { - private final UUID orderId; - private final Long userId; - private final Integer totalAmount; - private final UUID categoryId; - private final String saleType; - private final Long sellerId; - private final UUID idempotencyKey; - private final LocalDateTime publishedAt; - private final LocalDateTime expiresAt; + private UUID orderId; + private Long userId; + private Integer totalAmount; + private UUID categoryId; + private String saleType; + private Long sellerId; + private UUID idempotencyKey; + private LocalDateTime publishedAt; + private LocalDateTime expiresAt; } diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/dto/PaymentRefundRequestEvent.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/dto/PaymentRefundRequestEvent.java index 2ca5e097..e88051ba 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/dto/PaymentRefundRequestEvent.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/dto/PaymentRefundRequestEvent.java @@ -1,20 +1,22 @@ package com.smore.payment.payment.infrastructure.kafka.dto; -import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; import java.time.LocalDateTime; import java.util.UUID; @Getter -@AllArgsConstructor +@Setter +@NoArgsConstructor public class PaymentRefundRequestEvent { - private final UUID orderId; - private final Long userId; - private final UUID refundId; - private final UUID paymentId; - private final Integer refundAmount; - private final UUID idempotencyKey; - private final String reason; - private final LocalDateTime publishedAt; + private UUID orderId; + private Long userId; + private UUID refundId; + private UUID paymentId; + private Integer refundAmount; + private UUID idempotencyKey; + private String reason; + private LocalDateTime publishedAt; } diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/dto/SettlementRequestEvent.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/dto/SettlementRequestEvent.java index 40afa6c7..593ea3c0 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/dto/SettlementRequestEvent.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/dto/SettlementRequestEvent.java @@ -1,19 +1,21 @@ package com.smore.payment.payment.infrastructure.kafka.dto; -import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.UUID; @Getter -@AllArgsConstructor +@Setter +@NoArgsConstructor public class SettlementRequestEvent { - private final Long userId; - private final BigDecimal amount; - private final String accountNumber; - private final UUID idempotencyKey; - private final LocalDateTime createdAt; + private Long userId; + private BigDecimal amount; + private String accountNumber; + private UUID idempotencyKey; + private LocalDateTime createdAt; } diff --git a/payment/src/main/resources/data.sql b/payment/src/main/resources/data.sql index bd7eec02..0a1a1001 100644 --- a/payment/src/main/resources/data.sql +++ b/payment/src/main/resources/data.sql @@ -14,11 +14,11 @@ INSERT INTO fee_policies ( ) VALUES ( '1b5e6f7a-8b9c-4dad-9e0f-1a2b3c4d5e6f', - 'CATEGORY', - '11111111-1111-1111-1111-111111111111', + 'MERCHANT', + '2002', 'RATE', 0.02, - NULL, + 500.0, true, now(), now() @@ -38,6 +38,19 @@ INSERT INTO cancel_policies ( created_at, updated_at ) VALUES + ( + '5f0a1b2c-3d4e-5f6a-9b7c-8d9e0f1a2b3k', + 'MERCHANT', + '2002', + 1800000000000, -- 3 days = 259,200 seconds = 259,200,000,000,000 ns + 'MIXED', + 0.04, + 300, + false, + true, + now(), + now() + ), ( '3d8e9f0a-1b2c-4d3e-9f4a-5b6c7d8e9f0a', 'AUCTION_TYPE', @@ -79,6 +92,19 @@ INSERT INTO refund_policies ( created_at, updated_at ) VALUES + ( + '5f0a1b2c-3d4e-5f6a-9b7c-8d9e0f1a2b3a', + 'MERCHANT', + '2002', + 259200000000000, -- 3 days = 259,200 seconds = 259,200,000,000,000 ns + 'MIXED', + 0.04, + 300, + false, + true, + now(), + now() + ), ( '5f0a1b2c-3d4e-5f6a-9b7c-8d9e0f1a2b3c', 'AUCTION_TYPE', @@ -87,7 +113,7 @@ INSERT INTO refund_policies ( 'MIXED', 0.04, 300, - false, + true, true, now(), now() From f05fcb640be15439c91e98495c45eea3cc48eb2e Mon Sep 17 00:00:00 2001 From: ooinl77 Date: Tue, 16 Dec 2025 23:37:30 +0900 Subject: [PATCH 02/16] =?UTF-8?q?chore:=20=ED=8C=A8=ED=82=A4=EC=A7=80=20?= =?UTF-8?q?=EA=B5=AC=EC=A1=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/GetCancelPolicyQuery.java | 11 --- .../application/query/GetFeePolicyQuery.java | 11 --- ...ervice.java => ApprovePaymentService.java} | 76 +++++++----------- .../CreateTemporaryPaymentService.java | 7 +- .../application/PaymentRefundService.java | 79 +++++++------------ .../application/PaymentSettlementService.java | 54 +++++-------- .../event/outbound/PaymentApprovedEvent.java | 10 --- .../facade/CancelPolicyFacade.java | 4 +- .../application/facade/FeePolicyFacade.java | 4 +- .../facade/RefundPolicyFacade.java | 6 +- .../in}/ApprovePaymentCommand.java | 4 +- .../port/in/ApprovePaymentResult.java | 14 ++++ .../port/in/ApprovePaymentUseCase.java | 5 ++ .../port/in/RefundPaymentUseCase.java | 7 ++ .../port/in/SettlePaymentUseCase.java | 7 ++ .../application/port/out/OutboxPort.java | 7 ++ .../port/out/TemporaryPaymentPort.java | 16 ++++ .../domain/event/PaymentApprovedEvent.java | 14 ++++ .../event}/SettlementCalculatedEvent.java | 5 +- .../payment/payment/domain/model/Payment.java | 23 +++++- .../domain/repository/OutboxRepository.java | 9 --- .../payment/domain/service/FeeCalculator.java | 23 ++++++ .../domain/service/FeePolicyDecision.java | 10 +++ .../domain/service/RefundCalculator.java | 34 ++++++++ .../domain/service/RefundDecision.java | 14 ++++ .../service/SettlementAmountCalculator.java | 24 ++++++ .../service/SettlementValidationResult.java | 16 ++++ .../service/SettlementValidationService.java | 33 ++++++++ .../kafka/PaymentApprovedOutboxPublisher.java | 2 +- .../kafka/PaymentRefundEventConsumer.java | 6 +- .../kafka/PaymentSettlementEventConsumer.java | 5 +- .../persistence/jpa/mapper/OutboxMapper.java | 2 +- .../jpa/model/outbox/OutboxEntity.java | 2 +- .../outbox/OutboxJpaRepository.java | 2 +- .../OutboxAdapter.java} | 13 +-- .../redis/FailedPaymentHandler.java | 10 +-- ...Impl.java => TemporaryPaymentAdapter.java} | 5 +- .../presentation/PaymentController.java | 15 ++-- .../dto/request/ApprovePaymentRequestDto.java | 5 -- .../response/ApprovePaymentResponseDto.java | 14 ---- .../presentation/mapper/PaymentDtoMapper.java | 31 ++++++++ .../application/CancelPolicyService.java | 10 +-- .../command/CreateCancelPolicyCommand.java | 4 +- .../query/GetCancelPolicyQuery.java | 11 +++ .../cancel}/domain/model/CancelFeeRate.java | 2 +- .../cancel}/domain/model/CancelFeeType.java | 2 +- .../domain/model/CancelFixedAmount.java | 2 +- .../cancel}/domain/model/CancelPolicy.java | 2 +- .../domain/model/CancelTargetType.java | 2 +- .../cancel}/domain/model/TargetKey.java | 2 +- .../cancel}/domain/model/TargetKeyLong.java | 2 +- .../cancel}/domain/model/TargetKeyString.java | 2 +- .../cancel}/domain/model/TargetKeyUUID.java | 2 +- .../repository/CancelPolicyRepository.java | 8 +- .../mapper/CancelPolicyMapper.java | 12 ++- .../persistence/model/CancelFeeRateJpa.java | 2 +- .../model/CancelFixedAmountJpa.java | 2 +- .../persistence/model/CancelPolicyEntity.java | 8 +- .../repository/CancelPolicyJpaRepository.java | 6 +- .../CancelPolicyRepositoryImpl.java | 16 ++-- .../presentation/CancelPolicyController.java | 14 ++-- .../request/CreateCancelPolicyRequestDto.java | 6 +- .../response/GetCancelPolicyResponseDto.java | 4 +- .../fee}/application/FeePolicyService.java | 10 +-- .../command/CreateFeePolicyCommand.java | 4 +- .../application/query/GetFeePolicyQuery.java | 11 +++ .../fee}/domain/model/FeePolicy.java | 2 +- .../fee}/domain/model/FeeRate.java | 2 +- .../fee}/domain/model/FeeType.java | 2 +- .../fee}/domain/model/FixedAmount.java | 2 +- .../fee}/domain/model/TargetKey.java | 2 +- .../fee}/domain/model/TargetKeyLong.java | 2 +- .../fee}/domain/model/TargetKeyUUID.java | 2 +- .../fee}/domain/model/TargetType.java | 2 +- .../repository/FeePolicyRepository.java | 8 +- .../persistence/mapper/FeePolicyMapper.java | 10 +-- .../persistence/model/FeePolicyEntity.java | 8 +- .../persistence/model/FeeRateJpa.java | 2 +- .../persistence/model/FixedAmountJpa.java | 2 +- .../repository/FeePolicyJpaRepository.java | 6 +- .../repository/FeePolicyRepositoryImpl.java | 16 ++-- .../presentation/FeePolicyController.java | 22 +++--- .../request/CreateFeePolicyRequestDto.java | 6 +- .../dto/request/GetFeePolicyRequestDto.java | 12 +-- .../dto/response/GetFeePolicyResponseDto.java | 4 +- .../application/RefundPolicyService.java | 10 +-- .../command/CreateRefundPolicyCommand.java | 6 +- .../query/GetRefundPolicyQuery.java | 9 +++ .../refund}/domain/model/RefundFeeRate.java | 2 +- .../refund}/domain/model/RefundFeeType.java | 4 +- .../domain/model/RefundFixedAmount.java | 2 +- .../refund}/domain/model/RefundPolicy.java | 2 +- .../domain/model/RefundTargetType.java | 2 +- .../refund}/domain/model/TargetKey.java | 2 +- .../refund}/domain/model/TargetKeyLong.java | 2 +- .../refund}/domain/model/TargetKeyString.java | 2 +- .../refund}/domain/model/TargetKeyUUID.java | 2 +- .../repository/RefundPolicyRepository.java | 8 +- .../mapper/RefundPolicyMapper.java | 10 +-- .../persistence/model/RefundFeeRateJpa.java | 2 +- .../model/RefundFixedAmountJpa.java | 2 +- .../persistence/model/RefundPolicyEntity.java | 9 +-- .../repository/RefundPolicyJpaRepository.java | 6 +- .../RefundPolicyRepositoryImpl.java | 17 ++-- .../presentation/RefundPolicyController.java | 14 ++-- .../request/CreateRefundPolicyRequestDto.java | 6 +- .../response/GetRefundPolicyResponseDto.java | 4 +- .../query/GetRefundPolicyQuery.java | 9 --- .../config/AuditorAwareImpl.java | 3 +- .../config/JpaAuditingConfig.java | 2 +- .../config/UserContextHolder.java | 4 +- .../{global => shared}/entity/BaseEntity.java | 3 +- .../outbox/OutboxMessage.java | 6 +- .../outbox/OutboxMessageCreator.java | 14 +++- .../outbox/OutboxStatus.java | 2 +- .../{global => shared}/util/JsonUtil.java | 2 +- 116 files changed, 615 insertions(+), 450 deletions(-) delete mode 100644 payment/src/main/java/com/smore/payment/cancelpolicy/application/query/GetCancelPolicyQuery.java delete mode 100644 payment/src/main/java/com/smore/payment/feepolicy/application/query/GetFeePolicyQuery.java rename payment/src/main/java/com/smore/payment/payment/application/{CreatePaymentService.java => ApprovePaymentService.java} (61%) delete mode 100644 payment/src/main/java/com/smore/payment/payment/application/event/outbound/PaymentApprovedEvent.java rename payment/src/main/java/com/smore/payment/payment/application/{command => port/in}/ApprovePaymentCommand.java (74%) create mode 100644 payment/src/main/java/com/smore/payment/payment/application/port/in/ApprovePaymentResult.java create mode 100644 payment/src/main/java/com/smore/payment/payment/application/port/in/ApprovePaymentUseCase.java create mode 100644 payment/src/main/java/com/smore/payment/payment/application/port/in/RefundPaymentUseCase.java create mode 100644 payment/src/main/java/com/smore/payment/payment/application/port/in/SettlePaymentUseCase.java create mode 100644 payment/src/main/java/com/smore/payment/payment/application/port/out/OutboxPort.java create mode 100644 payment/src/main/java/com/smore/payment/payment/application/port/out/TemporaryPaymentPort.java create mode 100644 payment/src/main/java/com/smore/payment/payment/domain/event/PaymentApprovedEvent.java rename payment/src/main/java/com/smore/payment/payment/{application/event/outbound => domain/event}/SettlementCalculatedEvent.java (73%) delete mode 100644 payment/src/main/java/com/smore/payment/payment/domain/repository/OutboxRepository.java create mode 100644 payment/src/main/java/com/smore/payment/payment/domain/service/FeeCalculator.java create mode 100644 payment/src/main/java/com/smore/payment/payment/domain/service/FeePolicyDecision.java create mode 100644 payment/src/main/java/com/smore/payment/payment/domain/service/RefundCalculator.java create mode 100644 payment/src/main/java/com/smore/payment/payment/domain/service/RefundDecision.java create mode 100644 payment/src/main/java/com/smore/payment/payment/domain/service/SettlementAmountCalculator.java create mode 100644 payment/src/main/java/com/smore/payment/payment/domain/service/SettlementValidationResult.java create mode 100644 payment/src/main/java/com/smore/payment/payment/domain/service/SettlementValidationService.java rename payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/{jpa/repository/outbox/OutboxRepositoryImpl.java => outbox/OutboxAdapter.java} (50%) rename payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/redis/{repository/RedisRepositoryImpl.java => TemporaryPaymentAdapter.java} (91%) create mode 100644 payment/src/main/java/com/smore/payment/payment/presentation/mapper/PaymentDtoMapper.java rename payment/src/main/java/com/smore/payment/{cancelpolicy => policy/cancel}/application/CancelPolicyService.java (84%) rename payment/src/main/java/com/smore/payment/{cancelpolicy => policy/cancel}/application/command/CreateCancelPolicyCommand.java (73%) create mode 100644 payment/src/main/java/com/smore/payment/policy/cancel/application/query/GetCancelPolicyQuery.java rename payment/src/main/java/com/smore/payment/{cancelpolicy => policy/cancel}/domain/model/CancelFeeRate.java (92%) rename payment/src/main/java/com/smore/payment/{cancelpolicy => policy/cancel}/domain/model/CancelFeeType.java (91%) rename payment/src/main/java/com/smore/payment/{cancelpolicy => policy/cancel}/domain/model/CancelFixedAmount.java (90%) rename payment/src/main/java/com/smore/payment/{cancelpolicy => policy/cancel}/domain/model/CancelPolicy.java (98%) rename payment/src/main/java/com/smore/payment/{cancelpolicy => policy/cancel}/domain/model/CancelTargetType.java (86%) rename payment/src/main/java/com/smore/payment/{refundpolicy => policy/cancel}/domain/model/TargetKey.java (62%) rename payment/src/main/java/com/smore/payment/{refundpolicy => policy/cancel}/domain/model/TargetKeyLong.java (81%) rename payment/src/main/java/com/smore/payment/{cancelpolicy => policy/cancel}/domain/model/TargetKeyString.java (80%) rename payment/src/main/java/com/smore/payment/{cancelpolicy => policy/cancel}/domain/model/TargetKeyUUID.java (82%) rename payment/src/main/java/com/smore/payment/{cancelpolicy => policy/cancel}/domain/repository/CancelPolicyRepository.java (66%) rename payment/src/main/java/com/smore/payment/{cancelpolicy => policy/cancel}/infrastructure/persistence/mapper/CancelPolicyMapper.java (81%) rename payment/src/main/java/com/smore/payment/{cancelpolicy => policy/cancel}/infrastructure/persistence/model/CancelFeeRateJpa.java (85%) rename payment/src/main/java/com/smore/payment/{cancelpolicy => policy/cancel}/infrastructure/persistence/model/CancelFixedAmountJpa.java (86%) rename payment/src/main/java/com/smore/payment/{cancelpolicy => policy/cancel}/infrastructure/persistence/model/CancelPolicyEntity.java (88%) rename payment/src/main/java/com/smore/payment/{cancelpolicy => policy/cancel}/infrastructure/persistence/repository/CancelPolicyJpaRepository.java (58%) rename payment/src/main/java/com/smore/payment/{cancelpolicy => policy/cancel}/infrastructure/persistence/repository/CancelPolicyRepositoryImpl.java (79%) rename payment/src/main/java/com/smore/payment/{cancelpolicy => policy/cancel}/presentation/CancelPolicyController.java (80%) rename payment/src/main/java/com/smore/payment/{cancelpolicy => policy/cancel}/presentation/dto/request/CreateCancelPolicyRequestDto.java (87%) rename payment/src/main/java/com/smore/payment/{cancelpolicy => policy/cancel}/presentation/dto/response/GetCancelPolicyResponseDto.java (89%) rename payment/src/main/java/com/smore/payment/{feepolicy => policy/fee}/application/FeePolicyService.java (83%) rename payment/src/main/java/com/smore/payment/{feepolicy => policy/fee}/application/command/CreateFeePolicyCommand.java (62%) create mode 100644 payment/src/main/java/com/smore/payment/policy/fee/application/query/GetFeePolicyQuery.java rename payment/src/main/java/com/smore/payment/{feepolicy => policy/fee}/domain/model/FeePolicy.java (98%) rename payment/src/main/java/com/smore/payment/{feepolicy => policy/fee}/domain/model/FeeRate.java (92%) rename payment/src/main/java/com/smore/payment/{feepolicy => policy/fee}/domain/model/FeeType.java (91%) rename payment/src/main/java/com/smore/payment/{feepolicy => policy/fee}/domain/model/FixedAmount.java (90%) rename payment/src/main/java/com/smore/payment/{feepolicy => policy/fee}/domain/model/TargetKey.java (64%) rename payment/src/main/java/com/smore/payment/{feepolicy => policy/fee}/domain/model/TargetKeyLong.java (82%) rename payment/src/main/java/com/smore/payment/{feepolicy => policy/fee}/domain/model/TargetKeyUUID.java (83%) rename payment/src/main/java/com/smore/payment/{feepolicy => policy/fee}/domain/model/TargetType.java (86%) rename payment/src/main/java/com/smore/payment/{feepolicy => policy/fee}/domain/repository/FeePolicyRepository.java (66%) rename payment/src/main/java/com/smore/payment/{feepolicy => policy/fee}/infrastructure/persistence/mapper/FeePolicyMapper.java (81%) rename payment/src/main/java/com/smore/payment/{feepolicy => policy/fee}/infrastructure/persistence/model/FeePolicyEntity.java (85%) rename payment/src/main/java/com/smore/payment/{feepolicy => policy/fee}/infrastructure/persistence/model/FeeRateJpa.java (85%) rename payment/src/main/java/com/smore/payment/{feepolicy => policy/fee}/infrastructure/persistence/model/FixedAmountJpa.java (86%) rename payment/src/main/java/com/smore/payment/{feepolicy => policy/fee}/infrastructure/persistence/repository/FeePolicyJpaRepository.java (57%) rename payment/src/main/java/com/smore/payment/{feepolicy => policy/fee}/infrastructure/persistence/repository/FeePolicyRepositoryImpl.java (79%) rename payment/src/main/java/com/smore/payment/{feepolicy => policy/fee}/presentation/FeePolicyController.java (69%) rename payment/src/main/java/com/smore/payment/{feepolicy => policy/fee}/presentation/dto/request/CreateFeePolicyRequestDto.java (84%) rename payment/src/main/java/com/smore/payment/{feepolicy => policy/fee}/presentation/dto/request/GetFeePolicyRequestDto.java (67%) rename payment/src/main/java/com/smore/payment/{feepolicy => policy/fee}/presentation/dto/response/GetFeePolicyResponseDto.java (87%) rename payment/src/main/java/com/smore/payment/{refundpolicy => policy/refund}/application/RefundPolicyService.java (84%) rename payment/src/main/java/com/smore/payment/{refundpolicy => policy/refund}/application/command/CreateRefundPolicyCommand.java (58%) create mode 100644 payment/src/main/java/com/smore/payment/policy/refund/application/query/GetRefundPolicyQuery.java rename payment/src/main/java/com/smore/payment/{refundpolicy => policy/refund}/domain/model/RefundFeeRate.java (92%) rename payment/src/main/java/com/smore/payment/{refundpolicy => policy/refund}/domain/model/RefundFeeType.java (89%) rename payment/src/main/java/com/smore/payment/{refundpolicy => policy/refund}/domain/model/RefundFixedAmount.java (90%) rename payment/src/main/java/com/smore/payment/{refundpolicy => policy/refund}/domain/model/RefundPolicy.java (98%) rename payment/src/main/java/com/smore/payment/{refundpolicy => policy/refund}/domain/model/RefundTargetType.java (86%) rename payment/src/main/java/com/smore/payment/{cancelpolicy => policy/refund}/domain/model/TargetKey.java (62%) rename payment/src/main/java/com/smore/payment/{cancelpolicy => policy/refund}/domain/model/TargetKeyLong.java (81%) rename payment/src/main/java/com/smore/payment/{refundpolicy => policy/refund}/domain/model/TargetKeyString.java (80%) rename payment/src/main/java/com/smore/payment/{refundpolicy => policy/refund}/domain/model/TargetKeyUUID.java (82%) rename payment/src/main/java/com/smore/payment/{refundpolicy => policy/refund}/domain/repository/RefundPolicyRepository.java (66%) rename payment/src/main/java/com/smore/payment/{refundpolicy => policy/refund}/infrastructure/persistence/mapper/RefundPolicyMapper.java (84%) rename payment/src/main/java/com/smore/payment/{refundpolicy => policy/refund}/infrastructure/persistence/model/RefundFeeRateJpa.java (85%) rename payment/src/main/java/com/smore/payment/{refundpolicy => policy/refund}/infrastructure/persistence/model/RefundFixedAmountJpa.java (86%) rename payment/src/main/java/com/smore/payment/{refundpolicy => policy/refund}/infrastructure/persistence/model/RefundPolicyEntity.java (85%) rename payment/src/main/java/com/smore/payment/{refundpolicy => policy/refund}/infrastructure/persistence/repository/RefundPolicyJpaRepository.java (57%) rename payment/src/main/java/com/smore/payment/{refundpolicy => policy/refund}/infrastructure/persistence/repository/RefundPolicyRepositoryImpl.java (77%) rename payment/src/main/java/com/smore/payment/{refundpolicy => policy/refund}/presentation/RefundPolicyController.java (80%) rename payment/src/main/java/com/smore/payment/{refundpolicy => policy/refund}/presentation/dto/request/CreateRefundPolicyRequestDto.java (87%) rename payment/src/main/java/com/smore/payment/{refundpolicy => policy/refund}/presentation/dto/response/GetRefundPolicyResponseDto.java (89%) delete mode 100644 payment/src/main/java/com/smore/payment/refundpolicy/application/query/GetRefundPolicyQuery.java rename payment/src/main/java/com/smore/payment/{global => shared}/config/AuditorAwareImpl.java (85%) rename payment/src/main/java/com/smore/payment/{global => shared}/config/JpaAuditingConfig.java (83%) rename payment/src/main/java/com/smore/payment/{global => shared}/config/UserContextHolder.java (81%) rename payment/src/main/java/com/smore/payment/{global => shared}/entity/BaseEntity.java (93%) rename payment/src/main/java/com/smore/payment/{global => shared}/outbox/OutboxMessage.java (78%) rename payment/src/main/java/com/smore/payment/{global => shared}/outbox/OutboxMessageCreator.java (86%) rename payment/src/main/java/com/smore/payment/{global => shared}/outbox/OutboxStatus.java (60%) rename payment/src/main/java/com/smore/payment/{global => shared}/util/JsonUtil.java (95%) diff --git a/payment/src/main/java/com/smore/payment/cancelpolicy/application/query/GetCancelPolicyQuery.java b/payment/src/main/java/com/smore/payment/cancelpolicy/application/query/GetCancelPolicyQuery.java deleted file mode 100644 index 7152a961..00000000 --- a/payment/src/main/java/com/smore/payment/cancelpolicy/application/query/GetCancelPolicyQuery.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.smore.payment.cancelpolicy.application.query; - -import com.smore.payment.cancelpolicy.domain.model.CancelTargetType; -import com.smore.payment.cancelpolicy.domain.model.TargetKey; - -public record GetCancelPolicyQuery( - CancelTargetType cancelTargetType, - TargetKey targetKey -) { - -} diff --git a/payment/src/main/java/com/smore/payment/feepolicy/application/query/GetFeePolicyQuery.java b/payment/src/main/java/com/smore/payment/feepolicy/application/query/GetFeePolicyQuery.java deleted file mode 100644 index a5c6f1c9..00000000 --- a/payment/src/main/java/com/smore/payment/feepolicy/application/query/GetFeePolicyQuery.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.smore.payment.feepolicy.application.query; - -import com.smore.payment.feepolicy.domain.model.TargetKey; -import com.smore.payment.feepolicy.domain.model.TargetType; - -public record GetFeePolicyQuery( - TargetType targetType, - TargetKey targetKey -) { - -} diff --git a/payment/src/main/java/com/smore/payment/payment/application/CreatePaymentService.java b/payment/src/main/java/com/smore/payment/payment/application/ApprovePaymentService.java similarity index 61% rename from payment/src/main/java/com/smore/payment/payment/application/CreatePaymentService.java rename to payment/src/main/java/com/smore/payment/payment/application/ApprovePaymentService.java index 1fa1a512..533f7d23 100644 --- a/payment/src/main/java/com/smore/payment/payment/application/CreatePaymentService.java +++ b/payment/src/main/java/com/smore/payment/payment/application/ApprovePaymentService.java @@ -1,21 +1,24 @@ package com.smore.payment.payment.application; -import com.smore.payment.global.outbox.OutboxMessage; -import com.smore.payment.global.outbox.OutboxMessageCreator; -import com.smore.payment.payment.application.command.ApprovePaymentCommand; -import com.smore.payment.payment.application.event.outbound.SettlementCalculatedEvent; +import com.smore.payment.payment.domain.event.PaymentApprovedEvent; +import com.smore.payment.payment.domain.service.SettlementAmountCalculator; +import com.smore.payment.shared.outbox.OutboxMessage; +import com.smore.payment.shared.outbox.OutboxMessageCreator; +import com.smore.payment.payment.application.port.in.ApprovePaymentCommand; +import com.smore.payment.payment.domain.event.SettlementCalculatedEvent; import com.smore.payment.payment.application.facade.FeePolicyFacade; import com.smore.payment.payment.application.facade.dto.FeePolicyResult; +import com.smore.payment.payment.application.port.in.ApprovePaymentResult; +import com.smore.payment.payment.application.port.in.ApprovePaymentUseCase; +import com.smore.payment.payment.application.port.out.OutboxPort; import com.smore.payment.payment.application.port.out.PgClient; -import com.smore.payment.payment.application.event.outbound.PaymentApprovedEvent; +import com.smore.payment.payment.application.port.out.TemporaryPaymentPort; import com.smore.payment.payment.domain.model.Payment; //import com.smore.payment.payment.domain.document.PgApproveLog; import com.smore.payment.payment.domain.model.PgResponseResult; import com.smore.payment.payment.infrastructure.persistence.redis.model.TemporaryPayment; import com.smore.payment.payment.domain.repository.MongoRepository; -import com.smore.payment.payment.domain.repository.OutboxRepository; import com.smore.payment.payment.domain.repository.PaymentRepository; -import com.smore.payment.payment.domain.repository.RedisRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -27,19 +30,21 @@ @Service @RequiredArgsConstructor @Transactional -public class CreatePaymentService { +public class ApprovePaymentService implements ApprovePaymentUseCase { - private final RedisRepository redisRepository; + private final TemporaryPaymentPort temporaryPaymentPort; private final PaymentRepository paymentRepository; - private final OutboxRepository outboxRepository; + private final OutboxPort outboxPort; private final PgClient pgClient; private final MongoRepository mongoRepository; private final FeePolicyFacade feePolicyFacade; private final OutboxMessageCreator outboxMessageCreator; + private final SettlementAmountCalculator settlementAmountCalculator; - public Payment approve(ApprovePaymentCommand command) { + @Override + public ApprovePaymentResult approve(ApprovePaymentCommand command) { - TemporaryPayment temp = redisRepository.findByOrderId(command.orderId()) + TemporaryPayment temp = temporaryPaymentPort.findByOrderId(command.orderId()) .orElseThrow(() -> new IllegalStateException("임시 결제를 찾을 수 없습니다.")); temp.validateApproval(command.amount()); @@ -85,55 +90,32 @@ public Payment approve(ApprovePaymentCommand command) { paymentRepository.save(payment); - PaymentApprovedEvent event = new PaymentApprovedEvent( - payment.getOrderId(), - payment.getId(), - payment.getAmount(), - payment.getApprovedAt(), - payment.getIdempotencyKey() - ); - FeePolicyResult policy = feePolicyFacade.findApplicablePolicy( payment.getSellerId(), payment.getCategoryId() ); - BigDecimal settlementAmount = calculateSettlementAmount(payment.getAmount(), policy); - - SettlementCalculatedEvent settlementEvent = new SettlementCalculatedEvent( - payment.getSellerId(), - settlementAmount, - payment.getIdempotencyKey() - ); + PaymentApprovedEvent event = payment.createApprovedEvent(); + SettlementCalculatedEvent settlementEvent = payment.createSettlementCalculatedEvent( settlementAmountCalculator.calculate(payment.getAmount(), policy)); OutboxMessage paymentApprovedMsg = outboxMessageCreator.paymentApproved(event); OutboxMessage settlementCalculatedMsg = outboxMessageCreator.settlementCalculated(settlementEvent); - outboxRepository.save(paymentApprovedMsg); + outboxPort.save(paymentApprovedMsg); - outboxRepository.save(settlementCalculatedMsg); + outboxPort.save(settlementCalculatedMsg); - redisRepository.deleteByOrderId(temp.getOrderId()); + temporaryPaymentPort.deleteByOrderId(temp.getOrderId()); - return payment; + return new ApprovePaymentResult( + payment.getId(), + payment.getOrderId(), + payment.getAmount(), + payment.getApprovedAt(), + payment.getStatus().name() + ); } - private BigDecimal calculateSettlementAmount(BigDecimal approvedAmount, FeePolicyResult policy) { - - BigDecimal fee; - - switch (policy.feeType()) { - case "RATE" -> fee = approvedAmount.multiply(policy.rate()); - - case "FIXED" -> fee = policy.fixedAmount(); - - case "MIXED" -> fee = approvedAmount.multiply(policy.rate()) - .add(policy.fixedAmount()); - default -> throw new IllegalArgumentException("Unknown feeType: " + policy.feeType()); - } - - return approvedAmount.subtract(fee); - } } diff --git a/payment/src/main/java/com/smore/payment/payment/application/CreateTemporaryPaymentService.java b/payment/src/main/java/com/smore/payment/payment/application/CreateTemporaryPaymentService.java index b5e83fe0..f070742c 100644 --- a/payment/src/main/java/com/smore/payment/payment/application/CreateTemporaryPaymentService.java +++ b/payment/src/main/java/com/smore/payment/payment/application/CreateTemporaryPaymentService.java @@ -1,6 +1,7 @@ package com.smore.payment.payment.application; import com.smore.payment.payment.application.event.inbound.PaymentRequestedEvent; +import com.smore.payment.payment.application.port.out.TemporaryPaymentPort; import com.smore.payment.payment.infrastructure.persistence.redis.model.TemporaryPayment; import com.smore.payment.payment.domain.repository.RedisRepository; import lombok.RequiredArgsConstructor; @@ -11,12 +12,12 @@ @RequiredArgsConstructor public class CreateTemporaryPaymentService { - private final RedisRepository redisRepository; + private final TemporaryPaymentPort temporaryPaymentPort; @Transactional public void create(PaymentRequestedEvent paymentRequestedEvent) { - if (redisRepository.existsByOrderId(paymentRequestedEvent.orderId())) { + if (temporaryPaymentPort.existsByOrderId(paymentRequestedEvent.orderId())) { return; } @@ -31,6 +32,6 @@ public void create(PaymentRequestedEvent paymentRequestedEvent) { paymentRequestedEvent.expiredAt() ); - redisRepository.save(temp); + temporaryPaymentPort.save(temp); } } diff --git a/payment/src/main/java/com/smore/payment/payment/application/PaymentRefundService.java b/payment/src/main/java/com/smore/payment/payment/application/PaymentRefundService.java index 998455ce..11375ae1 100644 --- a/payment/src/main/java/com/smore/payment/payment/application/PaymentRefundService.java +++ b/payment/src/main/java/com/smore/payment/payment/application/PaymentRefundService.java @@ -1,7 +1,11 @@ package com.smore.payment.payment.application; -import com.smore.payment.global.outbox.OutboxMessage; -import com.smore.payment.global.outbox.OutboxMessageCreator; +import com.smore.payment.payment.application.port.in.RefundPaymentUseCase; +import com.smore.payment.payment.application.port.out.OutboxPort; +import com.smore.payment.payment.domain.service.RefundCalculator; +import com.smore.payment.payment.domain.service.RefundDecision; +import com.smore.payment.shared.outbox.OutboxMessage; +import com.smore.payment.shared.outbox.OutboxMessageCreator; import com.smore.payment.payment.application.event.inbound.PaymentRefundEvent; import com.smore.payment.payment.application.event.outbound.PaymentRefundFailedEvent; import com.smore.payment.payment.application.event.outbound.PaymentRefundSucceededEvent; @@ -12,75 +16,56 @@ import com.smore.payment.payment.application.port.out.PgClient; import com.smore.payment.payment.domain.model.Payment; import com.smore.payment.payment.domain.model.PgResponseResult; -import com.smore.payment.payment.domain.repository.OutboxRepository; import com.smore.payment.payment.domain.repository.PaymentRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; -import java.time.LocalDateTime; @Service @RequiredArgsConstructor @Transactional -public class PaymentRefundService { +public class PaymentRefundService implements RefundPaymentUseCase { private final PaymentRepository paymentRepository; private final CancelPolicyFacade cancelPolicyFacade; private final RefundPolicyFacade refundPolicyFacade; private final PgClient pgClient; private final OutboxMessageCreator outboxCreator; - private final OutboxRepository outboxRepository; + private final OutboxPort outboxPort; + private final RefundCalculator refundCalculator; + @Override public void refund(PaymentRefundEvent event) { - // - // 1) 결제 조회 - // Payment payment = paymentRepository.findById(event.paymentId()) .orElseThrow(() -> new IllegalArgumentException("결제 정보를 찾을 수 없습니다. paymentId=" + event.paymentId())); - // - // 2) 취소 정책 조회 - // CancelPolicyResult cancelResult = cancelPolicyFacade.findApplicablePolicy( payment.getSellerId(), payment.getCategoryId(), payment.getAuctionType() ); - // - // 3) 취소 정책 검증 - // - boolean cancelAvailable = cancelResult.cancellable(payment.getApprovedAt(), event.publishedAt()); - RefundPolicyResult refundResult = null; - if (!cancelAvailable) { - refundResult = refundPolicyFacade.findApplicablePolicy( - payment.getSellerId(), - payment.getCategoryId(), - payment.getAuctionType() - ); + RefundPolicyResult refundResult = refundPolicyFacade.findApplicablePolicy( + payment.getSellerId(), + payment.getCategoryId(), + payment.getAuctionType() + ); + - if (!refundResult.refundable(payment.getApprovedAt(), event.publishedAt())) { - OutboxMessage failedMsg = outboxCreator.paymentRefundFailed( - PaymentRefundFailedEvent.of(event.orderId(), event.refundId(), event.refundAmount(), "환불이 불가능 한 상품입니다.") - ); - outboxRepository.save(failedMsg); - return; - } + RefundDecision refundDecision = refundCalculator.decide(payment, event, cancelResult, refundResult); + if (!refundDecision.refundable()) { + OutboxMessage failedMsg = outboxCreator.paymentRefundFailed( + PaymentRefundFailedEvent.of(event.orderId(), event.refundId(), event.refundAmount(), refundDecision.failureReason()) + ); + outboxRepository.save(failedMsg); + return; } - // - // 5) 최종 환불 금액 계산 - // - final BigDecimal refundAmount = (cancelAvailable) - ? cancelResult.calculateCancelFee(event.refundAmount()) - : refundResult.calculateRefundFee(event.refundAmount()); + final BigDecimal refundAmount = refundDecision.refundAmount(); - // - // 6) PG 환불 호출 - // PgResponseResult pgRefundResult; try { @@ -92,18 +77,14 @@ public void refund(PaymentRefundEvent event) { } catch (Exception e) { - // PG 실패 → 환불 실패 이벤트 Outbox 발행 OutboxMessage failedMsg = outboxCreator.paymentRefundFailed( - PaymentRefundFailedEvent.of(event.orderId(), event.refundId(), refundAmount, e.getMessage()) + PaymentRefundFailedEvent.of(event.orderId(), event.refundId(), refundDecision.refundAmount(), e.getMessage()) ); - outboxRepository.save(failedMsg); + outboxPort.save(failedMsg); throw e; } - // - // 7) Payment 엔티티 상태 업데이트 - // payment.updateRefund( pgRefundResult.cancels().cancelReason(), event.refundAmount(), @@ -114,13 +95,9 @@ public void refund(PaymentRefundEvent event) { paymentRepository.updateRefund(payment.getId(), payment.getRefund()); // Todo: 환불 후 정산금 어떻게 하지.......... - // - // 8) 환불 성공 이벤트 Outbox 저장 - // OutboxMessage successMsg = outboxCreator.paymentRefunded( - PaymentRefundSucceededEvent.of(event.orderId(), event.refundId(), refundAmount) + PaymentRefundSucceededEvent.of(event.orderId(), event.refundId(), refundDecision.refundAmount()) ); - - outboxRepository.save(successMsg); + outboxPort.save(successMsg); } } diff --git a/payment/src/main/java/com/smore/payment/payment/application/PaymentSettlementService.java b/payment/src/main/java/com/smore/payment/payment/application/PaymentSettlementService.java index 4188543c..7ad8afb6 100644 --- a/payment/src/main/java/com/smore/payment/payment/application/PaymentSettlementService.java +++ b/payment/src/main/java/com/smore/payment/payment/application/PaymentSettlementService.java @@ -1,10 +1,14 @@ package com.smore.payment.payment.application; -import com.smore.payment.global.outbox.OutboxMessageCreator; +import com.smore.payment.payment.application.port.in.RefundPaymentUseCase; +import com.smore.payment.payment.application.port.in.SettlePaymentUseCase; +import com.smore.payment.payment.application.port.out.OutboxPort; +import com.smore.payment.payment.domain.service.SettlementValidationResult; +import com.smore.payment.payment.domain.service.SettlementValidationService; +import com.smore.payment.shared.outbox.OutboxMessageCreator; import com.smore.payment.payment.application.event.inbound.PaymentSettlementRequestEvent; import com.smore.payment.payment.application.event.outbound.SettlementFailedEvent; import com.smore.payment.payment.application.event.outbound.SettlementSuccessEvent; -import com.smore.payment.payment.domain.repository.OutboxRepository; import com.smore.payment.payment.domain.repository.SellerSettlementLedgerRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -16,57 +20,37 @@ @Slf4j @Service @RequiredArgsConstructor -public class PaymentSettlementService { +public class PaymentSettlementService implements SettlePaymentUseCase { private final SellerSettlementLedgerRepository sellerSettlementLedgerRepository; - private final OutboxRepository outboxRepository; + private final OutboxPort outboxPort; private final OutboxMessageCreator outboxCreator; + private final SettlementValidationService settlementValidationService; @Transactional - public void settlement(PaymentSettlementRequestEvent event) { + @Override + public void settle(PaymentSettlementRequestEvent event) { - // 1) 멱등성 체크 - if (sellerSettlementLedgerRepository.existsByIdempotencyKey(event.idempotencyKey())) { + SettlementValidationResult validation = settlementValidationService.validate(event); + if (validation.duplicated()) { return; } - // 2) 계좌번호 검증 – 비즈니스 실패 → Retry 불필요 - if (event.accountNumber() == null || event.accountNumber().isBlank()) { - outboxRepository.save( + if (!validation.valid()) { + outboxPort.save( outboxCreator.settlementFailed( SettlementFailedEvent.of( event.userId(), event.amount(), event.accountNumber(), event.idempotencyKey(), - "계좌 번호가 잘못 입력되었습니다." + validation.failureReason() ) ) ); return; } - // 3) 잔액 검증 – 비즈니스 실패 → Retry 불필요 - BigDecimal balance = sellerSettlementLedgerRepository.calculateBalance(event.userId()); - - if (balance.compareTo(event.amount()) < 0 - || balance.compareTo(BigDecimal.valueOf(1_000_000_000L)) > 0) { - - outboxRepository.save( - outboxCreator.settlementFailed( - SettlementFailedEvent.of( - event.userId(), - event.amount(), - event.accountNumber(), - event.idempotencyKey(), - "출금 가능 잔액이 부족합니다." - ) - ) - ); - return; - } - - // 4) 시스템 오류 가능 구간 – Retry + DLT 대상 try { sellerSettlementLedgerRepository.saveLedger( event.userId(), @@ -76,7 +60,7 @@ public void settlement(PaymentSettlementRequestEvent event) { event.idempotencyKey() ); - outboxRepository.save( + outboxPort.save( outboxCreator.settlementCompleted( SettlementSuccessEvent.of( event.userId(), @@ -89,8 +73,7 @@ public void settlement(PaymentSettlementRequestEvent event) { } catch (Exception e) { - // (1) 내부 DLT logging 저장 - outboxRepository.save( + outboxPort.save( outboxCreator.settlementDlt( SettlementFailedEvent.of( event.userId(), @@ -102,7 +85,6 @@ public void settlement(PaymentSettlementRequestEvent event) { ) ); - // (2) Kafka RetryTopic → DLT 로 넘어가도록 강제 예외 전파 throw new RuntimeException("정산 처리 중 시스템 오류 발생", e); } } diff --git a/payment/src/main/java/com/smore/payment/payment/application/event/outbound/PaymentApprovedEvent.java b/payment/src/main/java/com/smore/payment/payment/application/event/outbound/PaymentApprovedEvent.java deleted file mode 100644 index f91db2a7..00000000 --- a/payment/src/main/java/com/smore/payment/payment/application/event/outbound/PaymentApprovedEvent.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.smore.payment.payment.application.event.outbound; - -import java.math.BigDecimal; -import java.time.LocalDateTime; -import java.util.UUID; - -public record PaymentApprovedEvent(UUID orderId, UUID paymentId, BigDecimal amount, LocalDateTime approvedAt, - UUID idempotencyKey) { - -} diff --git a/payment/src/main/java/com/smore/payment/payment/application/facade/CancelPolicyFacade.java b/payment/src/main/java/com/smore/payment/payment/application/facade/CancelPolicyFacade.java index be530720..b725511b 100644 --- a/payment/src/main/java/com/smore/payment/payment/application/facade/CancelPolicyFacade.java +++ b/payment/src/main/java/com/smore/payment/payment/application/facade/CancelPolicyFacade.java @@ -1,7 +1,7 @@ package com.smore.payment.payment.application.facade; -import com.smore.payment.cancelpolicy.domain.model.CancelPolicy; -import com.smore.payment.cancelpolicy.domain.repository.CancelPolicyRepository; +import com.smore.payment.policy.cancel.domain.model.CancelPolicy; +import com.smore.payment.policy.cancel.domain.repository.CancelPolicyRepository; import com.smore.payment.payment.application.facade.dto.CancelPolicyResult; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; diff --git a/payment/src/main/java/com/smore/payment/payment/application/facade/FeePolicyFacade.java b/payment/src/main/java/com/smore/payment/payment/application/facade/FeePolicyFacade.java index 81dc8831..df2327c7 100644 --- a/payment/src/main/java/com/smore/payment/payment/application/facade/FeePolicyFacade.java +++ b/payment/src/main/java/com/smore/payment/payment/application/facade/FeePolicyFacade.java @@ -1,7 +1,7 @@ package com.smore.payment.payment.application.facade; -import com.smore.payment.feepolicy.domain.model.FeePolicy; -import com.smore.payment.feepolicy.domain.repository.FeePolicyRepository; +import com.smore.payment.policy.fee.domain.model.FeePolicy; +import com.smore.payment.policy.fee.domain.repository.FeePolicyRepository; import com.smore.payment.payment.application.facade.dto.FeePolicyResult; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; diff --git a/payment/src/main/java/com/smore/payment/payment/application/facade/RefundPolicyFacade.java b/payment/src/main/java/com/smore/payment/payment/application/facade/RefundPolicyFacade.java index 83ceb84d..660dd35f 100644 --- a/payment/src/main/java/com/smore/payment/payment/application/facade/RefundPolicyFacade.java +++ b/payment/src/main/java/com/smore/payment/payment/application/facade/RefundPolicyFacade.java @@ -1,10 +1,8 @@ package com.smore.payment.payment.application.facade; -import com.smore.payment.cancelpolicy.domain.model.CancelPolicy; -import com.smore.payment.payment.application.facade.dto.CancelPolicyResult; import com.smore.payment.payment.application.facade.dto.RefundPolicyResult; -import com.smore.payment.refundpolicy.domain.model.RefundPolicy; -import com.smore.payment.refundpolicy.domain.repository.RefundPolicyRepository; +import com.smore.payment.policy.refund.domain.model.RefundPolicy; +import com.smore.payment.policy.refund.domain.repository.RefundPolicyRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; diff --git a/payment/src/main/java/com/smore/payment/payment/application/command/ApprovePaymentCommand.java b/payment/src/main/java/com/smore/payment/payment/application/port/in/ApprovePaymentCommand.java similarity index 74% rename from payment/src/main/java/com/smore/payment/payment/application/command/ApprovePaymentCommand.java rename to payment/src/main/java/com/smore/payment/payment/application/port/in/ApprovePaymentCommand.java index d75ff647..93772471 100644 --- a/payment/src/main/java/com/smore/payment/payment/application/command/ApprovePaymentCommand.java +++ b/payment/src/main/java/com/smore/payment/payment/application/port/in/ApprovePaymentCommand.java @@ -1,4 +1,4 @@ -package com.smore.payment.payment.application.command; +package com.smore.payment.payment.application.port.in; import java.math.BigDecimal; import java.util.UUID; @@ -8,5 +8,5 @@ public record ApprovePaymentCommand( BigDecimal amount, String paymentKey, String pgOrderId - ) { +) { } diff --git a/payment/src/main/java/com/smore/payment/payment/application/port/in/ApprovePaymentResult.java b/payment/src/main/java/com/smore/payment/payment/application/port/in/ApprovePaymentResult.java new file mode 100644 index 00000000..75571f20 --- /dev/null +++ b/payment/src/main/java/com/smore/payment/payment/application/port/in/ApprovePaymentResult.java @@ -0,0 +1,14 @@ +package com.smore.payment.payment.application.port.in; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.UUID; + +public record ApprovePaymentResult( + UUID paymentId, + UUID orderId, + BigDecimal approvedAmount, + LocalDateTime approvedAt, + String status +) { +} \ No newline at end of file diff --git a/payment/src/main/java/com/smore/payment/payment/application/port/in/ApprovePaymentUseCase.java b/payment/src/main/java/com/smore/payment/payment/application/port/in/ApprovePaymentUseCase.java new file mode 100644 index 00000000..265c67bf --- /dev/null +++ b/payment/src/main/java/com/smore/payment/payment/application/port/in/ApprovePaymentUseCase.java @@ -0,0 +1,5 @@ +package com.smore.payment.payment.application.port.in; + +public interface ApprovePaymentUseCase { + ApprovePaymentResult approve(ApprovePaymentCommand command); +} diff --git a/payment/src/main/java/com/smore/payment/payment/application/port/in/RefundPaymentUseCase.java b/payment/src/main/java/com/smore/payment/payment/application/port/in/RefundPaymentUseCase.java new file mode 100644 index 00000000..f14cf75d --- /dev/null +++ b/payment/src/main/java/com/smore/payment/payment/application/port/in/RefundPaymentUseCase.java @@ -0,0 +1,7 @@ +package com.smore.payment.payment.application.port.in; + +import com.smore.payment.payment.application.event.inbound.PaymentRefundEvent; + +public interface RefundPaymentUseCase { + void refund(PaymentRefundEvent event); +} diff --git a/payment/src/main/java/com/smore/payment/payment/application/port/in/SettlePaymentUseCase.java b/payment/src/main/java/com/smore/payment/payment/application/port/in/SettlePaymentUseCase.java new file mode 100644 index 00000000..d0b24ade --- /dev/null +++ b/payment/src/main/java/com/smore/payment/payment/application/port/in/SettlePaymentUseCase.java @@ -0,0 +1,7 @@ +package com.smore.payment.payment.application.port.in; + +import com.smore.payment.payment.application.event.inbound.PaymentSettlementRequestEvent; + +public interface SettlePaymentUseCase { + void settle(PaymentSettlementRequestEvent event); +} diff --git a/payment/src/main/java/com/smore/payment/payment/application/port/out/OutboxPort.java b/payment/src/main/java/com/smore/payment/payment/application/port/out/OutboxPort.java new file mode 100644 index 00000000..1a0b0132 --- /dev/null +++ b/payment/src/main/java/com/smore/payment/payment/application/port/out/OutboxPort.java @@ -0,0 +1,7 @@ +package com.smore.payment.payment.application.port.out; + +import com.smore.payment.shared.outbox.OutboxMessage; + +public interface OutboxPort { + void save(OutboxMessage outboxMessage); +} diff --git a/payment/src/main/java/com/smore/payment/payment/application/port/out/TemporaryPaymentPort.java b/payment/src/main/java/com/smore/payment/payment/application/port/out/TemporaryPaymentPort.java new file mode 100644 index 00000000..2a499d27 --- /dev/null +++ b/payment/src/main/java/com/smore/payment/payment/application/port/out/TemporaryPaymentPort.java @@ -0,0 +1,16 @@ +package com.smore.payment.payment.application.port.out; + +import com.smore.payment.payment.infrastructure.persistence.redis.model.TemporaryPayment; + +import java.util.Optional; +import java.util.UUID; + +public interface TemporaryPaymentPort { + Optional findByOrderId(UUID uuid); + + void deleteByOrderId(UUID orderId); + + void save(TemporaryPayment temp); + + boolean existsByOrderId(UUID orderId); +} diff --git a/payment/src/main/java/com/smore/payment/payment/domain/event/PaymentApprovedEvent.java b/payment/src/main/java/com/smore/payment/payment/domain/event/PaymentApprovedEvent.java new file mode 100644 index 00000000..4350780a --- /dev/null +++ b/payment/src/main/java/com/smore/payment/payment/domain/event/PaymentApprovedEvent.java @@ -0,0 +1,14 @@ +package com.smore.payment.payment.domain.event; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.UUID; + +public record PaymentApprovedEvent( + UUID orderId, + UUID paymentId, + BigDecimal amount, + LocalDateTime approvedAt, + UUID idempotencyKey +) { +} \ No newline at end of file diff --git a/payment/src/main/java/com/smore/payment/payment/application/event/outbound/SettlementCalculatedEvent.java b/payment/src/main/java/com/smore/payment/payment/domain/event/SettlementCalculatedEvent.java similarity index 73% rename from payment/src/main/java/com/smore/payment/payment/application/event/outbound/SettlementCalculatedEvent.java rename to payment/src/main/java/com/smore/payment/payment/domain/event/SettlementCalculatedEvent.java index 258fcb37..d090e5ea 100644 --- a/payment/src/main/java/com/smore/payment/payment/application/event/outbound/SettlementCalculatedEvent.java +++ b/payment/src/main/java/com/smore/payment/payment/domain/event/SettlementCalculatedEvent.java @@ -1,4 +1,4 @@ -package com.smore.payment.payment.application.event.outbound; +package com.smore.payment.payment.domain.event; import java.math.BigDecimal; import java.util.UUID; @@ -7,4 +7,5 @@ public record SettlementCalculatedEvent( Long sellerId, BigDecimal settlementAmount, UUID idempotencyKey -) {} +) { +} diff --git a/payment/src/main/java/com/smore/payment/payment/domain/model/Payment.java b/payment/src/main/java/com/smore/payment/payment/domain/model/Payment.java index a6ca61a0..9840ddb4 100644 --- a/payment/src/main/java/com/smore/payment/payment/domain/model/Payment.java +++ b/payment/src/main/java/com/smore/payment/payment/domain/model/Payment.java @@ -1,5 +1,8 @@ package com.smore.payment.payment.domain.model; +import com.smore.payment.payment.domain.event.PaymentApprovedEvent; +import com.smore.payment.payment.domain.event.SettlementCalculatedEvent; + import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.UUID; @@ -254,11 +257,25 @@ public static Payment reconstruct( public UUID getCategoryId() { return categoryId; } public String getAuctionType() { return auctionType; } - public void updateRefund(BigDecimal refundAmount, LocalDateTime refundedAt) { + public void updateRefund(String reason, BigDecimal refundAmount, LocalDateTime refundedAt, String cancelTransactionKey, BigDecimal refundableAmount) { + this.refund = new PaymentRefund(reason, refundAmount, refundedAt, cancelTransactionKey, refundableAmount); + } + public PaymentApprovedEvent createApprovedEvent() { + return new PaymentApprovedEvent( + orderId, + id, + amount, + approvedAt, + idempotencyKey + ); } - public void updateRefund(String reason, BigDecimal refundAmount, LocalDateTime refundedAt, String cancelTransactionKey, BigDecimal refundableAmount) { - this.refund = new PaymentRefund(reason, refundAmount, refundedAt, cancelTransactionKey, refundableAmount); + public SettlementCalculatedEvent createSettlementCalculatedEvent(BigDecimal settlementAmount) { + return new SettlementCalculatedEvent( + sellerId, + settlementAmount, + idempotencyKey + ); } } diff --git a/payment/src/main/java/com/smore/payment/payment/domain/repository/OutboxRepository.java b/payment/src/main/java/com/smore/payment/payment/domain/repository/OutboxRepository.java deleted file mode 100644 index 63dd9ebd..00000000 --- a/payment/src/main/java/com/smore/payment/payment/domain/repository/OutboxRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.smore.payment.payment.domain.repository; - -import com.smore.payment.global.outbox.OutboxMessage; -import org.springframework.stereotype.Repository; - -@Repository -public interface OutboxRepository { - void save(OutboxMessage outboxMessage); -} diff --git a/payment/src/main/java/com/smore/payment/payment/domain/service/FeeCalculator.java b/payment/src/main/java/com/smore/payment/payment/domain/service/FeeCalculator.java new file mode 100644 index 00000000..38aca624 --- /dev/null +++ b/payment/src/main/java/com/smore/payment/payment/domain/service/FeeCalculator.java @@ -0,0 +1,23 @@ +package com.smore.payment.payment.domain.service; + +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; + +@Component +public class FeeCalculator { + + public BigDecimal calculateSettlementAmount(BigDecimal approvedAmount, FeePolicyDecision decision) { + BigDecimal fee; + + switch (decision.feeType()) { + case "RATE" -> fee = approvedAmount.multiply(decision.rate()); + case "FIXED" -> fee = decision.fixedAmount(); + case "MIXED" -> fee = approvedAmount.multiply(decision.rate()) + .add(decision.fixedAmount()); + default -> throw new IllegalArgumentException("Unknown feeType: " + decision.feeType()); + } + + return approvedAmount.subtract(fee); + } +} \ No newline at end of file diff --git a/payment/src/main/java/com/smore/payment/payment/domain/service/FeePolicyDecision.java b/payment/src/main/java/com/smore/payment/payment/domain/service/FeePolicyDecision.java new file mode 100644 index 00000000..553ec08a --- /dev/null +++ b/payment/src/main/java/com/smore/payment/payment/domain/service/FeePolicyDecision.java @@ -0,0 +1,10 @@ +package com.smore.payment.payment.domain.service; + +import java.math.BigDecimal; + +public record FeePolicyDecision( + String feeType, + BigDecimal rate, + BigDecimal fixedAmount +) { +} \ No newline at end of file diff --git a/payment/src/main/java/com/smore/payment/payment/domain/service/RefundCalculator.java b/payment/src/main/java/com/smore/payment/payment/domain/service/RefundCalculator.java new file mode 100644 index 00000000..42831085 --- /dev/null +++ b/payment/src/main/java/com/smore/payment/payment/domain/service/RefundCalculator.java @@ -0,0 +1,34 @@ +package com.smore.payment.payment.domain.service; + +import com.smore.payment.payment.application.event.inbound.PaymentRefundEvent; +import com.smore.payment.payment.application.facade.dto.CancelPolicyResult; +import com.smore.payment.payment.application.facade.dto.RefundPolicyResult; +import com.smore.payment.payment.domain.model.Payment; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; + +@Service +public class RefundCalculator { + + public RefundDecision decide(Payment payment, PaymentRefundEvent event, + CancelPolicyResult cancelResult, RefundPolicyResult refundResult) { + + boolean cancelAvailable = cancelResult.cancellable(payment.getApprovedAt(), event.publishedAt()); + if (cancelAvailable) { + BigDecimal refundAmount = cancelResult.calculateCancelFee(event.refundAmount()); + return RefundDecision.success(refundAmount); + } + + if (refundResult == null) { + return RefundDecision.failure("환불 정책을 찾을 수 없습니다."); + } + + if (!refundResult.refundable(payment.getApprovedAt(), event.publishedAt())) { + return RefundDecision.failure("환불이 불가능 한 상품입니다."); + } + + BigDecimal refundAmount = refundResult.calculateRefundFee(event.refundAmount()); + return RefundDecision.success(refundAmount); + } +} \ No newline at end of file diff --git a/payment/src/main/java/com/smore/payment/payment/domain/service/RefundDecision.java b/payment/src/main/java/com/smore/payment/payment/domain/service/RefundDecision.java new file mode 100644 index 00000000..43b3564c --- /dev/null +++ b/payment/src/main/java/com/smore/payment/payment/domain/service/RefundDecision.java @@ -0,0 +1,14 @@ +package com.smore.payment.payment.domain.service; + +import java.math.BigDecimal; + +public record RefundDecision(boolean refundable, BigDecimal refundAmount, String failureReason) { + + public static RefundDecision success(BigDecimal refundAmount) { + return new RefundDecision(true, refundAmount, null); + } + + public static RefundDecision failure(String failureReason) { + return new RefundDecision(false, null, failureReason); + } +} \ No newline at end of file diff --git a/payment/src/main/java/com/smore/payment/payment/domain/service/SettlementAmountCalculator.java b/payment/src/main/java/com/smore/payment/payment/domain/service/SettlementAmountCalculator.java new file mode 100644 index 00000000..8403ef58 --- /dev/null +++ b/payment/src/main/java/com/smore/payment/payment/domain/service/SettlementAmountCalculator.java @@ -0,0 +1,24 @@ +package com.smore.payment.payment.domain.service; + +import com.smore.payment.payment.application.facade.dto.FeePolicyResult; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; + +@Service +public class SettlementAmountCalculator { + + public BigDecimal calculate(BigDecimal approvedAmount, FeePolicyResult policy) { + BigDecimal fee; + + switch (policy.feeType()) { + case "RATE" -> fee = approvedAmount.multiply(policy.rate()); + case "FIXED" -> fee = policy.fixedAmount(); + case "MIXED" -> fee = approvedAmount.multiply(policy.rate()) + .add(policy.fixedAmount()); + default -> throw new IllegalArgumentException("Unknown feeType: " + policy.feeType()); + } + + return approvedAmount.subtract(fee); + } +} \ No newline at end of file diff --git a/payment/src/main/java/com/smore/payment/payment/domain/service/SettlementValidationResult.java b/payment/src/main/java/com/smore/payment/payment/domain/service/SettlementValidationResult.java new file mode 100644 index 00000000..e4fad494 --- /dev/null +++ b/payment/src/main/java/com/smore/payment/payment/domain/service/SettlementValidationResult.java @@ -0,0 +1,16 @@ +package com.smore.payment.payment.domain.service; + +public record SettlementValidationResult(boolean valid, boolean duplicated, String failureReason) { + + public static SettlementValidationResult duplicated() { + return new SettlementValidationResult(false, true, null); + } + + public static SettlementValidationResult invalid(String failureReason) { + return new SettlementValidationResult(false, false, failureReason); + } + + public static SettlementValidationResult valid() { + return new SettlementValidationResult(true, false, null); + } +} \ No newline at end of file diff --git a/payment/src/main/java/com/smore/payment/payment/domain/service/SettlementValidationService.java b/payment/src/main/java/com/smore/payment/payment/domain/service/SettlementValidationService.java new file mode 100644 index 00000000..c342bc53 --- /dev/null +++ b/payment/src/main/java/com/smore/payment/payment/domain/service/SettlementValidationService.java @@ -0,0 +1,33 @@ +package com.smore.payment.payment.domain.service; + +import com.smore.payment.payment.application.event.inbound.PaymentSettlementRequestEvent; +import com.smore.payment.payment.domain.repository.SellerSettlementLedgerRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; + +@Service +@RequiredArgsConstructor +public class SettlementValidationService { + + private final SellerSettlementLedgerRepository sellerSettlementLedgerRepository; + + public SettlementValidationResult validate(PaymentSettlementRequestEvent event) { + if (sellerSettlementLedgerRepository.existsByIdempotencyKey(event.idempotencyKey())) { + return SettlementValidationResult.duplicated(); + } + + if (event.accountNumber() == null || event.accountNumber().isBlank()) { + return SettlementValidationResult.invalid("계좌 번호가 잘못 입력되었습니다."); + } + + BigDecimal balance = sellerSettlementLedgerRepository.calculateBalance(event.userId()); + if (balance.compareTo(event.amount()) < 0 + || balance.compareTo(BigDecimal.valueOf(1_000_000_000L)) > 0) { + return SettlementValidationResult.invalid("출금 가능 잔액이 부족합니다."); + } + + return SettlementValidationResult.valid(); + } +} \ No newline at end of file diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentApprovedOutboxPublisher.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentApprovedOutboxPublisher.java index 2f618ce4..31a63f3a 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentApprovedOutboxPublisher.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentApprovedOutboxPublisher.java @@ -1,6 +1,6 @@ package com.smore.payment.payment.infrastructure.kafka; -import com.smore.payment.global.outbox.OutboxStatus; +import com.smore.payment.shared.outbox.OutboxStatus; import com.smore.payment.payment.infrastructure.persistence.jpa.model.outbox.OutboxEntity; import com.smore.payment.payment.infrastructure.persistence.jpa.repository.outbox.OutboxJpaRepository; import lombok.RequiredArgsConstructor; diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentRefundEventConsumer.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentRefundEventConsumer.java index 8c71d67b..95169946 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentRefundEventConsumer.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentRefundEventConsumer.java @@ -4,8 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.smore.payment.payment.application.PaymentRefundService; import com.smore.payment.payment.application.event.inbound.PaymentRefundEvent; -import com.smore.payment.payment.application.event.inbound.PaymentRequestedEvent; -import com.smore.payment.payment.infrastructure.kafka.dto.PaymentCreateRequestEvent; +import com.smore.payment.payment.application.port.in.RefundPaymentUseCase; import com.smore.payment.payment.infrastructure.kafka.dto.PaymentRefundRequestEvent; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -14,14 +13,13 @@ import org.springframework.stereotype.Component; import java.math.BigDecimal; -import java.time.LocalDateTime; @Slf4j @Component @RequiredArgsConstructor public class PaymentRefundEventConsumer { - private final PaymentRefundService paymentRefundService; + private final RefundPaymentUseCase paymentRefundService; private final ObjectMapper objectMapper; @KafkaListener(topics = "order.refund.v1") diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentSettlementEventConsumer.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentSettlementEventConsumer.java index 3bb66745..9cb6687a 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentSettlementEventConsumer.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentSettlementEventConsumer.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.smore.payment.payment.application.PaymentSettlementService; import com.smore.payment.payment.application.event.inbound.PaymentSettlementRequestEvent; +import com.smore.payment.payment.application.port.in.SettlePaymentUseCase; import com.smore.payment.payment.infrastructure.kafka.dto.SettlementRequestEvent; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -16,7 +17,7 @@ @RequiredArgsConstructor public class PaymentSettlementEventConsumer { - private final PaymentSettlementService paymentSettlementService; + private final SettlePaymentUseCase paymentSettlementService; private final ObjectMapper objectMapper; @KafkaListener(topics = "seller.settlement.v1") @@ -33,7 +34,7 @@ public void handle(String message, Acknowledgment ack) throws JsonProcessingExce event.getCreatedAt() ); - paymentSettlementService.settlement(paymentSettlementRequestEvent); + paymentSettlementService.settle(paymentSettlementRequestEvent); ack.acknowledge(); } } diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/mapper/OutboxMapper.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/mapper/OutboxMapper.java index 633b5359..399e6183 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/mapper/OutboxMapper.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/mapper/OutboxMapper.java @@ -1,6 +1,6 @@ package com.smore.payment.payment.infrastructure.persistence.jpa.mapper; -import com.smore.payment.global.outbox.OutboxMessage; +import com.smore.payment.shared.outbox.OutboxMessage; import com.smore.payment.payment.infrastructure.persistence.jpa.model.outbox.OutboxEntity; import org.springframework.stereotype.Component; diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/model/outbox/OutboxEntity.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/model/outbox/OutboxEntity.java index 06292ed5..5aea3ca5 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/model/outbox/OutboxEntity.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/model/outbox/OutboxEntity.java @@ -1,6 +1,6 @@ package com.smore.payment.payment.infrastructure.persistence.jpa.model.outbox; -import com.smore.payment.global.outbox.OutboxStatus; +import com.smore.payment.shared.outbox.OutboxStatus; import jakarta.persistence.*; import lombok.Getter; import lombok.NoArgsConstructor; diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/repository/outbox/OutboxJpaRepository.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/repository/outbox/OutboxJpaRepository.java index 6a25bbb8..87c193a4 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/repository/outbox/OutboxJpaRepository.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/repository/outbox/OutboxJpaRepository.java @@ -1,6 +1,6 @@ package com.smore.payment.payment.infrastructure.persistence.jpa.repository.outbox; -import com.smore.payment.global.outbox.OutboxStatus; +import com.smore.payment.shared.outbox.OutboxStatus; import com.smore.payment.payment.infrastructure.persistence.jpa.model.outbox.OutboxEntity; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/repository/outbox/OutboxRepositoryImpl.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/outbox/OutboxAdapter.java similarity index 50% rename from payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/repository/outbox/OutboxRepositoryImpl.java rename to payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/outbox/OutboxAdapter.java index 6dfe8415..9f4a8513 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/repository/outbox/OutboxRepositoryImpl.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/outbox/OutboxAdapter.java @@ -1,14 +1,15 @@ -package com.smore.payment.payment.infrastructure.persistence.jpa.repository.outbox; +package com.smore.payment.payment.infrastructure.persistence.outbox; -import com.smore.payment.global.outbox.OutboxMessage; -import com.smore.payment.payment.domain.repository.OutboxRepository; +import com.smore.payment.shared.outbox.OutboxMessage; +import com.smore.payment.payment.application.port.out.OutboxPort; import com.smore.payment.payment.infrastructure.persistence.jpa.mapper.OutboxMapper; +import com.smore.payment.payment.infrastructure.persistence.jpa.repository.outbox.OutboxJpaRepository; import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; +import org.springframework.stereotype.Component; -@Repository +@Component @RequiredArgsConstructor -public class OutboxRepositoryImpl implements OutboxRepository { +public class OutboxAdapter implements OutboxPort { private final OutboxJpaRepository outboxJpaRepository; private final OutboxMapper outboxMapper; diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/redis/FailedPaymentHandler.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/redis/FailedPaymentHandler.java index c4341f4b..df245521 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/redis/FailedPaymentHandler.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/redis/FailedPaymentHandler.java @@ -1,9 +1,9 @@ package com.smore.payment.payment.infrastructure.persistence.redis; -import com.smore.payment.global.outbox.OutboxMessage; -import com.smore.payment.global.outbox.OutboxMessageCreator; +import com.smore.payment.shared.outbox.OutboxMessage; +import com.smore.payment.shared.outbox.OutboxMessageCreator; import com.smore.payment.payment.application.event.outbound.PaymentFailedEvent; -import com.smore.payment.payment.domain.repository.OutboxRepository; +import com.smore.payment.payment.application.port.out.OutboxPort; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -16,7 +16,7 @@ public class FailedPaymentHandler { private final OutboxMessageCreator outboxMessageCreator; - private final OutboxRepository outboxRepository; + private final OutboxPort outboxPort; public void handleExpiredKey(String key) { @@ -31,6 +31,6 @@ public void handleExpiredKey(String key) { // Outbox 생성 → 저장 OutboxMessage outboxMessage = outboxMessageCreator.paymentFailed(event); - outboxRepository.save(outboxMessage); + outboxPort.save(outboxMessage); } } diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/redis/repository/RedisRepositoryImpl.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/redis/TemporaryPaymentAdapter.java similarity index 91% rename from payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/redis/repository/RedisRepositoryImpl.java rename to payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/redis/TemporaryPaymentAdapter.java index 2999c7de..5aeffb5a 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/redis/repository/RedisRepositoryImpl.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/redis/TemporaryPaymentAdapter.java @@ -1,5 +1,6 @@ -package com.smore.payment.payment.infrastructure.persistence.redis.repository; +package com.smore.payment.payment.infrastructure.persistence.redis; +import com.smore.payment.payment.application.port.out.TemporaryPaymentPort; import com.smore.payment.payment.infrastructure.persistence.redis.model.TemporaryPayment; import com.smore.payment.payment.domain.repository.RedisRepository; import lombok.RequiredArgsConstructor; @@ -15,7 +16,7 @@ @Slf4j @Repository @RequiredArgsConstructor -public class RedisRepositoryImpl implements RedisRepository { +public class TemporaryPaymentAdapter implements TemporaryPaymentPort { private final RedisTemplate temporaryPaymentRedisTemplate; diff --git a/payment/src/main/java/com/smore/payment/payment/presentation/PaymentController.java b/payment/src/main/java/com/smore/payment/payment/presentation/PaymentController.java index 27ec0e98..500d5127 100644 --- a/payment/src/main/java/com/smore/payment/payment/presentation/PaymentController.java +++ b/payment/src/main/java/com/smore/payment/payment/presentation/PaymentController.java @@ -1,10 +1,11 @@ package com.smore.payment.payment.presentation; import com.smore.common.response.ApiResponse; -import com.smore.payment.payment.application.CreatePaymentService; -import com.smore.payment.payment.domain.model.Payment; +import com.smore.payment.payment.application.port.in.ApprovePaymentResult; +import com.smore.payment.payment.application.port.in.ApprovePaymentUseCase; import com.smore.payment.payment.presentation.dto.request.ApprovePaymentRequestDto; import com.smore.payment.payment.presentation.dto.response.ApprovePaymentResponseDto; +import com.smore.payment.payment.presentation.mapper.PaymentDtoMapper; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; @@ -15,16 +16,16 @@ @RequestMapping("/api/v1/payments") public class PaymentController { - private final CreatePaymentService createPaymentService; + private final ApprovePaymentUseCase approvePaymentUseCase; + private final PaymentDtoMapper paymentDtoMapper; @PostMapping("/approve") public ResponseEntity approvePayment( @Valid @RequestBody ApprovePaymentRequestDto request ) { - Payment payment = createPaymentService.approve(request.toCommand()); - return ResponseEntity.ok(ApiResponse.ok(ApprovePaymentResponseDto.from(payment))); + ApprovePaymentResult result = approvePaymentUseCase.approve(paymentDtoMapper.toCommand(request)); + ApprovePaymentResponseDto response = paymentDtoMapper.toResponseDto(result); + return ResponseEntity.ok(ApiResponse.ok(response)); } - - } diff --git a/payment/src/main/java/com/smore/payment/payment/presentation/dto/request/ApprovePaymentRequestDto.java b/payment/src/main/java/com/smore/payment/payment/presentation/dto/request/ApprovePaymentRequestDto.java index 285bfb3d..53a2d75d 100644 --- a/payment/src/main/java/com/smore/payment/payment/presentation/dto/request/ApprovePaymentRequestDto.java +++ b/payment/src/main/java/com/smore/payment/payment/presentation/dto/request/ApprovePaymentRequestDto.java @@ -1,6 +1,5 @@ package com.smore.payment.payment.presentation.dto.request; -import com.smore.payment.payment.application.command.ApprovePaymentCommand; import jakarta.validation.constraints.DecimalMin; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; @@ -28,8 +27,4 @@ public class ApprovePaymentRequestDto { @NotBlank private String pgOrderId; - - public ApprovePaymentCommand toCommand() { - return new ApprovePaymentCommand(orderId,amount,paymentKey,pgOrderId); - } } diff --git a/payment/src/main/java/com/smore/payment/payment/presentation/dto/response/ApprovePaymentResponseDto.java b/payment/src/main/java/com/smore/payment/payment/presentation/dto/response/ApprovePaymentResponseDto.java index 71fb9399..9e15eb89 100644 --- a/payment/src/main/java/com/smore/payment/payment/presentation/dto/response/ApprovePaymentResponseDto.java +++ b/payment/src/main/java/com/smore/payment/payment/presentation/dto/response/ApprovePaymentResponseDto.java @@ -1,6 +1,5 @@ package com.smore.payment.payment.presentation.dto.response; -import com.smore.payment.payment.domain.model.Payment; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; @@ -21,17 +20,4 @@ public class ApprovePaymentResponseDto { private BigDecimal approvedAmount; private LocalDateTime approvedAt; - - public static ApprovePaymentResponseDto from(Payment payment) { - return new ApprovePaymentResponseDto( - true, - payment.getStatus().name(), - - payment.getId().toString(), - payment.getOrderId().toString(), - - payment.getAmount(), - payment.getApprovedAt() - ); - } } diff --git a/payment/src/main/java/com/smore/payment/payment/presentation/mapper/PaymentDtoMapper.java b/payment/src/main/java/com/smore/payment/payment/presentation/mapper/PaymentDtoMapper.java new file mode 100644 index 00000000..4d29823e --- /dev/null +++ b/payment/src/main/java/com/smore/payment/payment/presentation/mapper/PaymentDtoMapper.java @@ -0,0 +1,31 @@ +package com.smore.payment.payment.presentation.mapper; + +import com.smore.payment.payment.application.port.in.ApprovePaymentCommand; +import com.smore.payment.payment.application.port.in.ApprovePaymentResult; +import com.smore.payment.payment.presentation.dto.request.ApprovePaymentRequestDto; +import com.smore.payment.payment.presentation.dto.response.ApprovePaymentResponseDto; +import org.springframework.stereotype.Component; + +@Component +public class PaymentDtoMapper { + + public ApprovePaymentCommand toCommand(ApprovePaymentRequestDto requestDto) { + return new ApprovePaymentCommand( + requestDto.getOrderId(), + requestDto.getAmount(), + requestDto.getPaymentKey(), + requestDto.getPgOrderId() + ); + } + + public ApprovePaymentResponseDto toResponseDto(ApprovePaymentResult result) { + return new ApprovePaymentResponseDto( + true, + result.status(), + result.paymentId().toString(), + result.orderId().toString(), + result.approvedAmount(), + result.approvedAt() + ); + } +} \ No newline at end of file diff --git a/payment/src/main/java/com/smore/payment/cancelpolicy/application/CancelPolicyService.java b/payment/src/main/java/com/smore/payment/policy/cancel/application/CancelPolicyService.java similarity index 84% rename from payment/src/main/java/com/smore/payment/cancelpolicy/application/CancelPolicyService.java rename to payment/src/main/java/com/smore/payment/policy/cancel/application/CancelPolicyService.java index 2b8c55e6..94e280a6 100644 --- a/payment/src/main/java/com/smore/payment/cancelpolicy/application/CancelPolicyService.java +++ b/payment/src/main/java/com/smore/payment/policy/cancel/application/CancelPolicyService.java @@ -1,9 +1,9 @@ -package com.smore.payment.cancelpolicy.application; +package com.smore.payment.policy.cancel.application; -import com.smore.payment.cancelpolicy.application.command.CreateCancelPolicyCommand; -import com.smore.payment.cancelpolicy.application.query.GetCancelPolicyQuery; -import com.smore.payment.cancelpolicy.domain.model.CancelPolicy; -import com.smore.payment.cancelpolicy.domain.repository.CancelPolicyRepository; +import com.smore.payment.policy.cancel.application.command.CreateCancelPolicyCommand; +import com.smore.payment.policy.cancel.application.query.GetCancelPolicyQuery; +import com.smore.payment.policy.cancel.domain.model.CancelPolicy; +import com.smore.payment.policy.cancel.domain.repository.CancelPolicyRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; diff --git a/payment/src/main/java/com/smore/payment/cancelpolicy/application/command/CreateCancelPolicyCommand.java b/payment/src/main/java/com/smore/payment/policy/cancel/application/command/CreateCancelPolicyCommand.java similarity index 73% rename from payment/src/main/java/com/smore/payment/cancelpolicy/application/command/CreateCancelPolicyCommand.java rename to payment/src/main/java/com/smore/payment/policy/cancel/application/command/CreateCancelPolicyCommand.java index cde77251..76b15ae4 100644 --- a/payment/src/main/java/com/smore/payment/cancelpolicy/application/command/CreateCancelPolicyCommand.java +++ b/payment/src/main/java/com/smore/payment/policy/cancel/application/command/CreateCancelPolicyCommand.java @@ -1,6 +1,6 @@ -package com.smore.payment.cancelpolicy.application.command; +package com.smore.payment.policy.cancel.application.command; -import com.smore.payment.cancelpolicy.domain.model.*; +import com.smore.payment.policy.cancel.domain.model.*; import java.time.Duration; diff --git a/payment/src/main/java/com/smore/payment/policy/cancel/application/query/GetCancelPolicyQuery.java b/payment/src/main/java/com/smore/payment/policy/cancel/application/query/GetCancelPolicyQuery.java new file mode 100644 index 00000000..7584a303 --- /dev/null +++ b/payment/src/main/java/com/smore/payment/policy/cancel/application/query/GetCancelPolicyQuery.java @@ -0,0 +1,11 @@ +package com.smore.payment.policy.cancel.application.query; + +import com.smore.payment.policy.cancel.domain.model.CancelTargetType; +import com.smore.payment.policy.cancel.domain.model.TargetKey; + +public record GetCancelPolicyQuery( + CancelTargetType cancelTargetType, + TargetKey targetKey +) { + +} diff --git a/payment/src/main/java/com/smore/payment/cancelpolicy/domain/model/CancelFeeRate.java b/payment/src/main/java/com/smore/payment/policy/cancel/domain/model/CancelFeeRate.java similarity index 92% rename from payment/src/main/java/com/smore/payment/cancelpolicy/domain/model/CancelFeeRate.java rename to payment/src/main/java/com/smore/payment/policy/cancel/domain/model/CancelFeeRate.java index a7074847..1733f47e 100644 --- a/payment/src/main/java/com/smore/payment/cancelpolicy/domain/model/CancelFeeRate.java +++ b/payment/src/main/java/com/smore/payment/policy/cancel/domain/model/CancelFeeRate.java @@ -1,4 +1,4 @@ -package com.smore.payment.cancelpolicy.domain.model; +package com.smore.payment.policy.cancel.domain.model; import java.math.BigDecimal; diff --git a/payment/src/main/java/com/smore/payment/cancelpolicy/domain/model/CancelFeeType.java b/payment/src/main/java/com/smore/payment/policy/cancel/domain/model/CancelFeeType.java similarity index 91% rename from payment/src/main/java/com/smore/payment/cancelpolicy/domain/model/CancelFeeType.java rename to payment/src/main/java/com/smore/payment/policy/cancel/domain/model/CancelFeeType.java index f50bc201..897d462c 100644 --- a/payment/src/main/java/com/smore/payment/cancelpolicy/domain/model/CancelFeeType.java +++ b/payment/src/main/java/com/smore/payment/policy/cancel/domain/model/CancelFeeType.java @@ -1,4 +1,4 @@ -package com.smore.payment.cancelpolicy.domain.model; +package com.smore.payment.policy.cancel.domain.model; import lombok.Getter; diff --git a/payment/src/main/java/com/smore/payment/cancelpolicy/domain/model/CancelFixedAmount.java b/payment/src/main/java/com/smore/payment/policy/cancel/domain/model/CancelFixedAmount.java similarity index 90% rename from payment/src/main/java/com/smore/payment/cancelpolicy/domain/model/CancelFixedAmount.java rename to payment/src/main/java/com/smore/payment/policy/cancel/domain/model/CancelFixedAmount.java index 7656cc08..9d71be87 100644 --- a/payment/src/main/java/com/smore/payment/cancelpolicy/domain/model/CancelFixedAmount.java +++ b/payment/src/main/java/com/smore/payment/policy/cancel/domain/model/CancelFixedAmount.java @@ -1,4 +1,4 @@ -package com.smore.payment.cancelpolicy.domain.model; +package com.smore.payment.policy.cancel.domain.model; import java.math.BigDecimal; diff --git a/payment/src/main/java/com/smore/payment/cancelpolicy/domain/model/CancelPolicy.java b/payment/src/main/java/com/smore/payment/policy/cancel/domain/model/CancelPolicy.java similarity index 98% rename from payment/src/main/java/com/smore/payment/cancelpolicy/domain/model/CancelPolicy.java rename to payment/src/main/java/com/smore/payment/policy/cancel/domain/model/CancelPolicy.java index 059da397..c8b31399 100644 --- a/payment/src/main/java/com/smore/payment/cancelpolicy/domain/model/CancelPolicy.java +++ b/payment/src/main/java/com/smore/payment/policy/cancel/domain/model/CancelPolicy.java @@ -1,4 +1,4 @@ -package com.smore.payment.cancelpolicy.domain.model; +package com.smore.payment.policy.cancel.domain.model; import java.time.Duration; import java.util.UUID; diff --git a/payment/src/main/java/com/smore/payment/cancelpolicy/domain/model/CancelTargetType.java b/payment/src/main/java/com/smore/payment/policy/cancel/domain/model/CancelTargetType.java similarity index 86% rename from payment/src/main/java/com/smore/payment/cancelpolicy/domain/model/CancelTargetType.java rename to payment/src/main/java/com/smore/payment/policy/cancel/domain/model/CancelTargetType.java index e994285f..93bddf71 100644 --- a/payment/src/main/java/com/smore/payment/cancelpolicy/domain/model/CancelTargetType.java +++ b/payment/src/main/java/com/smore/payment/policy/cancel/domain/model/CancelTargetType.java @@ -1,4 +1,4 @@ -package com.smore.payment.cancelpolicy.domain.model; +package com.smore.payment.policy.cancel.domain.model; public enum CancelTargetType { CATEGORY, MERCHANT, AUCTION_TYPE, USER_TYPE; diff --git a/payment/src/main/java/com/smore/payment/refundpolicy/domain/model/TargetKey.java b/payment/src/main/java/com/smore/payment/policy/cancel/domain/model/TargetKey.java similarity index 62% rename from payment/src/main/java/com/smore/payment/refundpolicy/domain/model/TargetKey.java rename to payment/src/main/java/com/smore/payment/policy/cancel/domain/model/TargetKey.java index cb0d6d4c..ecfb28c3 100644 --- a/payment/src/main/java/com/smore/payment/refundpolicy/domain/model/TargetKey.java +++ b/payment/src/main/java/com/smore/payment/policy/cancel/domain/model/TargetKey.java @@ -1,4 +1,4 @@ -package com.smore.payment.refundpolicy.domain.model; +package com.smore.payment.policy.cancel.domain.model; public interface TargetKey { Object getTargetKey(); diff --git a/payment/src/main/java/com/smore/payment/refundpolicy/domain/model/TargetKeyLong.java b/payment/src/main/java/com/smore/payment/policy/cancel/domain/model/TargetKeyLong.java similarity index 81% rename from payment/src/main/java/com/smore/payment/refundpolicy/domain/model/TargetKeyLong.java rename to payment/src/main/java/com/smore/payment/policy/cancel/domain/model/TargetKeyLong.java index c8bf3d29..1df057d1 100644 --- a/payment/src/main/java/com/smore/payment/refundpolicy/domain/model/TargetKeyLong.java +++ b/payment/src/main/java/com/smore/payment/policy/cancel/domain/model/TargetKeyLong.java @@ -1,4 +1,4 @@ -package com.smore.payment.refundpolicy.domain.model; +package com.smore.payment.policy.cancel.domain.model; public record TargetKeyLong(Long value) implements TargetKey { @Override diff --git a/payment/src/main/java/com/smore/payment/cancelpolicy/domain/model/TargetKeyString.java b/payment/src/main/java/com/smore/payment/policy/cancel/domain/model/TargetKeyString.java similarity index 80% rename from payment/src/main/java/com/smore/payment/cancelpolicy/domain/model/TargetKeyString.java rename to payment/src/main/java/com/smore/payment/policy/cancel/domain/model/TargetKeyString.java index 66f70731..b7018233 100644 --- a/payment/src/main/java/com/smore/payment/cancelpolicy/domain/model/TargetKeyString.java +++ b/payment/src/main/java/com/smore/payment/policy/cancel/domain/model/TargetKeyString.java @@ -1,4 +1,4 @@ -package com.smore.payment.cancelpolicy.domain.model; +package com.smore.payment.policy.cancel.domain.model; public record TargetKeyString(String value) implements TargetKey { @Override diff --git a/payment/src/main/java/com/smore/payment/cancelpolicy/domain/model/TargetKeyUUID.java b/payment/src/main/java/com/smore/payment/policy/cancel/domain/model/TargetKeyUUID.java similarity index 82% rename from payment/src/main/java/com/smore/payment/cancelpolicy/domain/model/TargetKeyUUID.java rename to payment/src/main/java/com/smore/payment/policy/cancel/domain/model/TargetKeyUUID.java index 74e704a3..03e56d35 100644 --- a/payment/src/main/java/com/smore/payment/cancelpolicy/domain/model/TargetKeyUUID.java +++ b/payment/src/main/java/com/smore/payment/policy/cancel/domain/model/TargetKeyUUID.java @@ -1,4 +1,4 @@ -package com.smore.payment.cancelpolicy.domain.model; +package com.smore.payment.policy.cancel.domain.model; import java.util.UUID; diff --git a/payment/src/main/java/com/smore/payment/cancelpolicy/domain/repository/CancelPolicyRepository.java b/payment/src/main/java/com/smore/payment/policy/cancel/domain/repository/CancelPolicyRepository.java similarity index 66% rename from payment/src/main/java/com/smore/payment/cancelpolicy/domain/repository/CancelPolicyRepository.java rename to payment/src/main/java/com/smore/payment/policy/cancel/domain/repository/CancelPolicyRepository.java index 96e3a69d..ed562955 100644 --- a/payment/src/main/java/com/smore/payment/cancelpolicy/domain/repository/CancelPolicyRepository.java +++ b/payment/src/main/java/com/smore/payment/policy/cancel/domain/repository/CancelPolicyRepository.java @@ -1,8 +1,8 @@ -package com.smore.payment.cancelpolicy.domain.repository; +package com.smore.payment.policy.cancel.domain.repository; -import com.smore.payment.cancelpolicy.domain.model.CancelPolicy; -import com.smore.payment.cancelpolicy.domain.model.CancelTargetType; -import com.smore.payment.cancelpolicy.domain.model.TargetKey; +import com.smore.payment.policy.cancel.domain.model.CancelPolicy; +import com.smore.payment.policy.cancel.domain.model.CancelTargetType; +import com.smore.payment.policy.cancel.domain.model.TargetKey; import org.springframework.stereotype.Repository; import java.util.Optional; diff --git a/payment/src/main/java/com/smore/payment/cancelpolicy/infrastructure/persistence/mapper/CancelPolicyMapper.java b/payment/src/main/java/com/smore/payment/policy/cancel/infrastructure/persistence/mapper/CancelPolicyMapper.java similarity index 81% rename from payment/src/main/java/com/smore/payment/cancelpolicy/infrastructure/persistence/mapper/CancelPolicyMapper.java rename to payment/src/main/java/com/smore/payment/policy/cancel/infrastructure/persistence/mapper/CancelPolicyMapper.java index ef3629ec..7322a955 100644 --- a/payment/src/main/java/com/smore/payment/cancelpolicy/infrastructure/persistence/mapper/CancelPolicyMapper.java +++ b/payment/src/main/java/com/smore/payment/policy/cancel/infrastructure/persistence/mapper/CancelPolicyMapper.java @@ -1,15 +1,13 @@ -package com.smore.payment.cancelpolicy.infrastructure.persistence.mapper; +package com.smore.payment.policy.cancel.infrastructure.persistence.mapper; -import com.smore.payment.cancelpolicy.domain.model.*; -import com.smore.payment.cancelpolicy.infrastructure.persistence.model.CancelFeeRateJpa; -import com.smore.payment.cancelpolicy.infrastructure.persistence.model.CancelFixedAmountJpa; -import com.smore.payment.cancelpolicy.infrastructure.persistence.model.CancelPolicyEntity; +import com.smore.payment.policy.cancel.infrastructure.persistence.model.CancelFeeRateJpa; +import com.smore.payment.policy.cancel.infrastructure.persistence.model.CancelFixedAmountJpa; +import com.smore.payment.policy.cancel.infrastructure.persistence.model.CancelPolicyEntity; +import com.smore.payment.policy.cancel.domain.model.*; import org.springframework.stereotype.Component; import java.util.UUID; -import static com.smore.payment.cancelpolicy.domain.model.CancelTargetType.*; - @Component public class CancelPolicyMapper { diff --git a/payment/src/main/java/com/smore/payment/cancelpolicy/infrastructure/persistence/model/CancelFeeRateJpa.java b/payment/src/main/java/com/smore/payment/policy/cancel/infrastructure/persistence/model/CancelFeeRateJpa.java similarity index 85% rename from payment/src/main/java/com/smore/payment/cancelpolicy/infrastructure/persistence/model/CancelFeeRateJpa.java rename to payment/src/main/java/com/smore/payment/policy/cancel/infrastructure/persistence/model/CancelFeeRateJpa.java index 006d7b68..471b236d 100644 --- a/payment/src/main/java/com/smore/payment/cancelpolicy/infrastructure/persistence/model/CancelFeeRateJpa.java +++ b/payment/src/main/java/com/smore/payment/policy/cancel/infrastructure/persistence/model/CancelFeeRateJpa.java @@ -1,4 +1,4 @@ -package com.smore.payment.cancelpolicy.infrastructure.persistence.model; +package com.smore.payment.policy.cancel.infrastructure.persistence.model; import jakarta.persistence.Column; import jakarta.persistence.Embeddable; diff --git a/payment/src/main/java/com/smore/payment/cancelpolicy/infrastructure/persistence/model/CancelFixedAmountJpa.java b/payment/src/main/java/com/smore/payment/policy/cancel/infrastructure/persistence/model/CancelFixedAmountJpa.java similarity index 86% rename from payment/src/main/java/com/smore/payment/cancelpolicy/infrastructure/persistence/model/CancelFixedAmountJpa.java rename to payment/src/main/java/com/smore/payment/policy/cancel/infrastructure/persistence/model/CancelFixedAmountJpa.java index 9307a955..5e1bb290 100644 --- a/payment/src/main/java/com/smore/payment/cancelpolicy/infrastructure/persistence/model/CancelFixedAmountJpa.java +++ b/payment/src/main/java/com/smore/payment/policy/cancel/infrastructure/persistence/model/CancelFixedAmountJpa.java @@ -1,4 +1,4 @@ -package com.smore.payment.cancelpolicy.infrastructure.persistence.model; +package com.smore.payment.policy.cancel.infrastructure.persistence.model; import jakarta.persistence.Column; import jakarta.persistence.Embeddable; diff --git a/payment/src/main/java/com/smore/payment/cancelpolicy/infrastructure/persistence/model/CancelPolicyEntity.java b/payment/src/main/java/com/smore/payment/policy/cancel/infrastructure/persistence/model/CancelPolicyEntity.java similarity index 88% rename from payment/src/main/java/com/smore/payment/cancelpolicy/infrastructure/persistence/model/CancelPolicyEntity.java rename to payment/src/main/java/com/smore/payment/policy/cancel/infrastructure/persistence/model/CancelPolicyEntity.java index 0c005706..c200bfb3 100644 --- a/payment/src/main/java/com/smore/payment/cancelpolicy/infrastructure/persistence/model/CancelPolicyEntity.java +++ b/payment/src/main/java/com/smore/payment/policy/cancel/infrastructure/persistence/model/CancelPolicyEntity.java @@ -1,8 +1,8 @@ -package com.smore.payment.cancelpolicy.infrastructure.persistence.model; +package com.smore.payment.policy.cancel.infrastructure.persistence.model; -import com.smore.payment.cancelpolicy.domain.model.CancelFeeType; -import com.smore.payment.cancelpolicy.domain.model.CancelTargetType; -import com.smore.payment.global.entity.BaseEntity; +import com.smore.payment.policy.cancel.domain.model.CancelFeeType; +import com.smore.payment.policy.cancel.domain.model.CancelTargetType; +import com.smore.payment.shared.entity.BaseEntity; import jakarta.persistence.*; import lombok.AccessLevel; import lombok.Getter; diff --git a/payment/src/main/java/com/smore/payment/cancelpolicy/infrastructure/persistence/repository/CancelPolicyJpaRepository.java b/payment/src/main/java/com/smore/payment/policy/cancel/infrastructure/persistence/repository/CancelPolicyJpaRepository.java similarity index 58% rename from payment/src/main/java/com/smore/payment/cancelpolicy/infrastructure/persistence/repository/CancelPolicyJpaRepository.java rename to payment/src/main/java/com/smore/payment/policy/cancel/infrastructure/persistence/repository/CancelPolicyJpaRepository.java index b2ccf7b5..08242734 100644 --- a/payment/src/main/java/com/smore/payment/cancelpolicy/infrastructure/persistence/repository/CancelPolicyJpaRepository.java +++ b/payment/src/main/java/com/smore/payment/policy/cancel/infrastructure/persistence/repository/CancelPolicyJpaRepository.java @@ -1,7 +1,7 @@ -package com.smore.payment.cancelpolicy.infrastructure.persistence.repository; +package com.smore.payment.policy.cancel.infrastructure.persistence.repository; -import com.smore.payment.cancelpolicy.domain.model.CancelTargetType; -import com.smore.payment.cancelpolicy.infrastructure.persistence.model.CancelPolicyEntity; +import com.smore.payment.policy.cancel.domain.model.CancelTargetType; +import com.smore.payment.policy.cancel.infrastructure.persistence.model.CancelPolicyEntity; import org.springframework.data.jpa.repository.JpaRepository; import java.util.Optional; diff --git a/payment/src/main/java/com/smore/payment/cancelpolicy/infrastructure/persistence/repository/CancelPolicyRepositoryImpl.java b/payment/src/main/java/com/smore/payment/policy/cancel/infrastructure/persistence/repository/CancelPolicyRepositoryImpl.java similarity index 79% rename from payment/src/main/java/com/smore/payment/cancelpolicy/infrastructure/persistence/repository/CancelPolicyRepositoryImpl.java rename to payment/src/main/java/com/smore/payment/policy/cancel/infrastructure/persistence/repository/CancelPolicyRepositoryImpl.java index 24d5633a..2980e2dc 100644 --- a/payment/src/main/java/com/smore/payment/cancelpolicy/infrastructure/persistence/repository/CancelPolicyRepositoryImpl.java +++ b/payment/src/main/java/com/smore/payment/policy/cancel/infrastructure/persistence/repository/CancelPolicyRepositoryImpl.java @@ -1,11 +1,11 @@ -package com.smore.payment.cancelpolicy.infrastructure.persistence.repository; - -import com.smore.payment.cancelpolicy.domain.model.CancelPolicy; -import com.smore.payment.cancelpolicy.domain.model.CancelTargetType; -import com.smore.payment.cancelpolicy.domain.model.TargetKey; -import com.smore.payment.cancelpolicy.domain.repository.CancelPolicyRepository; -import com.smore.payment.cancelpolicy.infrastructure.persistence.mapper.CancelPolicyMapper; -import com.smore.payment.cancelpolicy.infrastructure.persistence.model.CancelPolicyEntity; +package com.smore.payment.policy.cancel.infrastructure.persistence.repository; + +import com.smore.payment.policy.cancel.domain.model.CancelPolicy; +import com.smore.payment.policy.cancel.domain.model.CancelTargetType; +import com.smore.payment.policy.cancel.domain.model.TargetKey; +import com.smore.payment.policy.cancel.domain.repository.CancelPolicyRepository; +import com.smore.payment.policy.cancel.infrastructure.persistence.mapper.CancelPolicyMapper; +import com.smore.payment.policy.cancel.infrastructure.persistence.model.CancelPolicyEntity; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; diff --git a/payment/src/main/java/com/smore/payment/cancelpolicy/presentation/CancelPolicyController.java b/payment/src/main/java/com/smore/payment/policy/cancel/presentation/CancelPolicyController.java similarity index 80% rename from payment/src/main/java/com/smore/payment/cancelpolicy/presentation/CancelPolicyController.java rename to payment/src/main/java/com/smore/payment/policy/cancel/presentation/CancelPolicyController.java index 1ea2f344..ae32c99d 100644 --- a/payment/src/main/java/com/smore/payment/cancelpolicy/presentation/CancelPolicyController.java +++ b/payment/src/main/java/com/smore/payment/policy/cancel/presentation/CancelPolicyController.java @@ -1,12 +1,12 @@ -package com.smore.payment.cancelpolicy.presentation; +package com.smore.payment.policy.cancel.presentation; import com.smore.common.response.ApiResponse; -import com.smore.payment.cancelpolicy.application.CancelPolicyService; -import com.smore.payment.cancelpolicy.application.query.GetCancelPolicyQuery; -import com.smore.payment.cancelpolicy.domain.model.*; -import com.smore.payment.cancelpolicy.presentation.dto.request.CreateCancelPolicyRequestDto; -import com.smore.payment.cancelpolicy.presentation.dto.response.GetCancelPolicyResponseDto; -import com.smore.payment.global.config.UserContextHolder; +import com.smore.payment.policy.cancel.application.CancelPolicyService; +import com.smore.payment.policy.cancel.application.query.GetCancelPolicyQuery; +import com.smore.payment.policy.cancel.presentation.dto.request.CreateCancelPolicyRequestDto; +import com.smore.payment.policy.cancel.presentation.dto.response.GetCancelPolicyResponseDto; +import com.smore.payment.policy.cancel.domain.model.*; +import com.smore.payment.shared.config.UserContextHolder; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; diff --git a/payment/src/main/java/com/smore/payment/cancelpolicy/presentation/dto/request/CreateCancelPolicyRequestDto.java b/payment/src/main/java/com/smore/payment/policy/cancel/presentation/dto/request/CreateCancelPolicyRequestDto.java similarity index 87% rename from payment/src/main/java/com/smore/payment/cancelpolicy/presentation/dto/request/CreateCancelPolicyRequestDto.java rename to payment/src/main/java/com/smore/payment/policy/cancel/presentation/dto/request/CreateCancelPolicyRequestDto.java index 9b7fa716..d72e08d1 100644 --- a/payment/src/main/java/com/smore/payment/cancelpolicy/presentation/dto/request/CreateCancelPolicyRequestDto.java +++ b/payment/src/main/java/com/smore/payment/policy/cancel/presentation/dto/request/CreateCancelPolicyRequestDto.java @@ -1,7 +1,7 @@ -package com.smore.payment.cancelpolicy.presentation.dto.request; +package com.smore.payment.policy.cancel.presentation.dto.request; -import com.smore.payment.cancelpolicy.application.command.CreateCancelPolicyCommand; -import com.smore.payment.cancelpolicy.domain.model.*; +import com.smore.payment.policy.cancel.application.command.CreateCancelPolicyCommand; +import com.smore.payment.policy.cancel.domain.model.*; import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/payment/src/main/java/com/smore/payment/cancelpolicy/presentation/dto/response/GetCancelPolicyResponseDto.java b/payment/src/main/java/com/smore/payment/policy/cancel/presentation/dto/response/GetCancelPolicyResponseDto.java similarity index 89% rename from payment/src/main/java/com/smore/payment/cancelpolicy/presentation/dto/response/GetCancelPolicyResponseDto.java rename to payment/src/main/java/com/smore/payment/policy/cancel/presentation/dto/response/GetCancelPolicyResponseDto.java index c66da75d..a49e96f7 100644 --- a/payment/src/main/java/com/smore/payment/cancelpolicy/presentation/dto/response/GetCancelPolicyResponseDto.java +++ b/payment/src/main/java/com/smore/payment/policy/cancel/presentation/dto/response/GetCancelPolicyResponseDto.java @@ -1,6 +1,6 @@ -package com.smore.payment.cancelpolicy.presentation.dto.response; +package com.smore.payment.policy.cancel.presentation.dto.response; -import com.smore.payment.cancelpolicy.domain.model.CancelPolicy; +import com.smore.payment.policy.cancel.domain.model.CancelPolicy; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; diff --git a/payment/src/main/java/com/smore/payment/feepolicy/application/FeePolicyService.java b/payment/src/main/java/com/smore/payment/policy/fee/application/FeePolicyService.java similarity index 83% rename from payment/src/main/java/com/smore/payment/feepolicy/application/FeePolicyService.java rename to payment/src/main/java/com/smore/payment/policy/fee/application/FeePolicyService.java index dce49a4f..3bf1bab2 100644 --- a/payment/src/main/java/com/smore/payment/feepolicy/application/FeePolicyService.java +++ b/payment/src/main/java/com/smore/payment/policy/fee/application/FeePolicyService.java @@ -1,9 +1,9 @@ -package com.smore.payment.feepolicy.application; +package com.smore.payment.policy.fee.application; -import com.smore.payment.feepolicy.application.command.CreateFeePolicyCommand; -import com.smore.payment.feepolicy.application.query.GetFeePolicyQuery; -import com.smore.payment.feepolicy.domain.model.FeePolicy; -import com.smore.payment.feepolicy.domain.repository.FeePolicyRepository; +import com.smore.payment.policy.fee.application.command.CreateFeePolicyCommand; +import com.smore.payment.policy.fee.application.query.GetFeePolicyQuery; +import com.smore.payment.policy.fee.domain.model.FeePolicy; +import com.smore.payment.policy.fee.domain.repository.FeePolicyRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; diff --git a/payment/src/main/java/com/smore/payment/feepolicy/application/command/CreateFeePolicyCommand.java b/payment/src/main/java/com/smore/payment/policy/fee/application/command/CreateFeePolicyCommand.java similarity index 62% rename from payment/src/main/java/com/smore/payment/feepolicy/application/command/CreateFeePolicyCommand.java rename to payment/src/main/java/com/smore/payment/policy/fee/application/command/CreateFeePolicyCommand.java index 6b05b2f9..67413e99 100644 --- a/payment/src/main/java/com/smore/payment/feepolicy/application/command/CreateFeePolicyCommand.java +++ b/payment/src/main/java/com/smore/payment/policy/fee/application/command/CreateFeePolicyCommand.java @@ -1,6 +1,6 @@ -package com.smore.payment.feepolicy.application.command; +package com.smore.payment.policy.fee.application.command; -import com.smore.payment.feepolicy.domain.model.*; +import com.smore.payment.policy.fee.domain.model.*; public record CreateFeePolicyCommand( TargetType targetType, diff --git a/payment/src/main/java/com/smore/payment/policy/fee/application/query/GetFeePolicyQuery.java b/payment/src/main/java/com/smore/payment/policy/fee/application/query/GetFeePolicyQuery.java new file mode 100644 index 00000000..e62355dc --- /dev/null +++ b/payment/src/main/java/com/smore/payment/policy/fee/application/query/GetFeePolicyQuery.java @@ -0,0 +1,11 @@ +package com.smore.payment.policy.fee.application.query; + +import com.smore.payment.policy.fee.domain.model.TargetKey; +import com.smore.payment.policy.fee.domain.model.TargetType; + +public record GetFeePolicyQuery( + TargetType targetType, + TargetKey targetKey +) { + +} diff --git a/payment/src/main/java/com/smore/payment/feepolicy/domain/model/FeePolicy.java b/payment/src/main/java/com/smore/payment/policy/fee/domain/model/FeePolicy.java similarity index 98% rename from payment/src/main/java/com/smore/payment/feepolicy/domain/model/FeePolicy.java rename to payment/src/main/java/com/smore/payment/policy/fee/domain/model/FeePolicy.java index a6091fa3..1e5c6a82 100644 --- a/payment/src/main/java/com/smore/payment/feepolicy/domain/model/FeePolicy.java +++ b/payment/src/main/java/com/smore/payment/policy/fee/domain/model/FeePolicy.java @@ -1,4 +1,4 @@ -package com.smore.payment.feepolicy.domain.model; +package com.smore.payment.policy.fee.domain.model; import java.util.UUID; diff --git a/payment/src/main/java/com/smore/payment/feepolicy/domain/model/FeeRate.java b/payment/src/main/java/com/smore/payment/policy/fee/domain/model/FeeRate.java similarity index 92% rename from payment/src/main/java/com/smore/payment/feepolicy/domain/model/FeeRate.java rename to payment/src/main/java/com/smore/payment/policy/fee/domain/model/FeeRate.java index 7a86edde..0ec35f3e 100644 --- a/payment/src/main/java/com/smore/payment/feepolicy/domain/model/FeeRate.java +++ b/payment/src/main/java/com/smore/payment/policy/fee/domain/model/FeeRate.java @@ -1,4 +1,4 @@ -package com.smore.payment.feepolicy.domain.model; +package com.smore.payment.policy.fee.domain.model; import java.math.BigDecimal; diff --git a/payment/src/main/java/com/smore/payment/feepolicy/domain/model/FeeType.java b/payment/src/main/java/com/smore/payment/policy/fee/domain/model/FeeType.java similarity index 91% rename from payment/src/main/java/com/smore/payment/feepolicy/domain/model/FeeType.java rename to payment/src/main/java/com/smore/payment/policy/fee/domain/model/FeeType.java index 5358a72f..eda9904b 100644 --- a/payment/src/main/java/com/smore/payment/feepolicy/domain/model/FeeType.java +++ b/payment/src/main/java/com/smore/payment/policy/fee/domain/model/FeeType.java @@ -1,4 +1,4 @@ -package com.smore.payment.feepolicy.domain.model; +package com.smore.payment.policy.fee.domain.model; import lombok.Getter; diff --git a/payment/src/main/java/com/smore/payment/feepolicy/domain/model/FixedAmount.java b/payment/src/main/java/com/smore/payment/policy/fee/domain/model/FixedAmount.java similarity index 90% rename from payment/src/main/java/com/smore/payment/feepolicy/domain/model/FixedAmount.java rename to payment/src/main/java/com/smore/payment/policy/fee/domain/model/FixedAmount.java index cf644a5e..d1334f40 100644 --- a/payment/src/main/java/com/smore/payment/feepolicy/domain/model/FixedAmount.java +++ b/payment/src/main/java/com/smore/payment/policy/fee/domain/model/FixedAmount.java @@ -1,4 +1,4 @@ -package com.smore.payment.feepolicy.domain.model; +package com.smore.payment.policy.fee.domain.model; import java.math.BigDecimal; diff --git a/payment/src/main/java/com/smore/payment/feepolicy/domain/model/TargetKey.java b/payment/src/main/java/com/smore/payment/policy/fee/domain/model/TargetKey.java similarity index 64% rename from payment/src/main/java/com/smore/payment/feepolicy/domain/model/TargetKey.java rename to payment/src/main/java/com/smore/payment/policy/fee/domain/model/TargetKey.java index 5a52f832..9ec62aaf 100644 --- a/payment/src/main/java/com/smore/payment/feepolicy/domain/model/TargetKey.java +++ b/payment/src/main/java/com/smore/payment/policy/fee/domain/model/TargetKey.java @@ -1,4 +1,4 @@ -package com.smore.payment.feepolicy.domain.model; +package com.smore.payment.policy.fee.domain.model; public interface TargetKey { Object getTargetKey(); diff --git a/payment/src/main/java/com/smore/payment/feepolicy/domain/model/TargetKeyLong.java b/payment/src/main/java/com/smore/payment/policy/fee/domain/model/TargetKeyLong.java similarity index 82% rename from payment/src/main/java/com/smore/payment/feepolicy/domain/model/TargetKeyLong.java rename to payment/src/main/java/com/smore/payment/policy/fee/domain/model/TargetKeyLong.java index a30580b7..a71f4b4c 100644 --- a/payment/src/main/java/com/smore/payment/feepolicy/domain/model/TargetKeyLong.java +++ b/payment/src/main/java/com/smore/payment/policy/fee/domain/model/TargetKeyLong.java @@ -1,4 +1,4 @@ -package com.smore.payment.feepolicy.domain.model; +package com.smore.payment.policy.fee.domain.model; public record TargetKeyLong(Long value) implements TargetKey { @Override diff --git a/payment/src/main/java/com/smore/payment/feepolicy/domain/model/TargetKeyUUID.java b/payment/src/main/java/com/smore/payment/policy/fee/domain/model/TargetKeyUUID.java similarity index 83% rename from payment/src/main/java/com/smore/payment/feepolicy/domain/model/TargetKeyUUID.java rename to payment/src/main/java/com/smore/payment/policy/fee/domain/model/TargetKeyUUID.java index 3d5cf823..742fcf1c 100644 --- a/payment/src/main/java/com/smore/payment/feepolicy/domain/model/TargetKeyUUID.java +++ b/payment/src/main/java/com/smore/payment/policy/fee/domain/model/TargetKeyUUID.java @@ -1,4 +1,4 @@ -package com.smore.payment.feepolicy.domain.model; +package com.smore.payment.policy.fee.domain.model; import java.util.UUID; diff --git a/payment/src/main/java/com/smore/payment/feepolicy/domain/model/TargetType.java b/payment/src/main/java/com/smore/payment/policy/fee/domain/model/TargetType.java similarity index 86% rename from payment/src/main/java/com/smore/payment/feepolicy/domain/model/TargetType.java rename to payment/src/main/java/com/smore/payment/policy/fee/domain/model/TargetType.java index 3fe7256d..d6d345c1 100644 --- a/payment/src/main/java/com/smore/payment/feepolicy/domain/model/TargetType.java +++ b/payment/src/main/java/com/smore/payment/policy/fee/domain/model/TargetType.java @@ -1,4 +1,4 @@ -package com.smore.payment.feepolicy.domain.model; +package com.smore.payment.policy.fee.domain.model; public enum TargetType { CATEGORY, MERCHANT, USER_TYPE; diff --git a/payment/src/main/java/com/smore/payment/feepolicy/domain/repository/FeePolicyRepository.java b/payment/src/main/java/com/smore/payment/policy/fee/domain/repository/FeePolicyRepository.java similarity index 66% rename from payment/src/main/java/com/smore/payment/feepolicy/domain/repository/FeePolicyRepository.java rename to payment/src/main/java/com/smore/payment/policy/fee/domain/repository/FeePolicyRepository.java index befc6efe..58bb10a1 100644 --- a/payment/src/main/java/com/smore/payment/feepolicy/domain/repository/FeePolicyRepository.java +++ b/payment/src/main/java/com/smore/payment/policy/fee/domain/repository/FeePolicyRepository.java @@ -1,8 +1,8 @@ -package com.smore.payment.feepolicy.domain.repository; +package com.smore.payment.policy.fee.domain.repository; -import com.smore.payment.feepolicy.domain.model.FeePolicy; -import com.smore.payment.feepolicy.domain.model.TargetKey; -import com.smore.payment.feepolicy.domain.model.TargetType; +import com.smore.payment.policy.fee.domain.model.FeePolicy; +import com.smore.payment.policy.fee.domain.model.TargetKey; +import com.smore.payment.policy.fee.domain.model.TargetType; import org.springframework.stereotype.Repository; import java.util.Optional; diff --git a/payment/src/main/java/com/smore/payment/feepolicy/infrastructure/persistence/mapper/FeePolicyMapper.java b/payment/src/main/java/com/smore/payment/policy/fee/infrastructure/persistence/mapper/FeePolicyMapper.java similarity index 81% rename from payment/src/main/java/com/smore/payment/feepolicy/infrastructure/persistence/mapper/FeePolicyMapper.java rename to payment/src/main/java/com/smore/payment/policy/fee/infrastructure/persistence/mapper/FeePolicyMapper.java index 8b85bdce..32b1dd6d 100644 --- a/payment/src/main/java/com/smore/payment/feepolicy/infrastructure/persistence/mapper/FeePolicyMapper.java +++ b/payment/src/main/java/com/smore/payment/policy/fee/infrastructure/persistence/mapper/FeePolicyMapper.java @@ -1,9 +1,9 @@ -package com.smore.payment.feepolicy.infrastructure.persistence.mapper; +package com.smore.payment.policy.fee.infrastructure.persistence.mapper; -import com.smore.payment.feepolicy.domain.model.*; -import com.smore.payment.feepolicy.infrastructure.persistence.model.FeePolicyEntity; -import com.smore.payment.feepolicy.infrastructure.persistence.model.FeeRateJpa; -import com.smore.payment.feepolicy.infrastructure.persistence.model.FixedAmountJpa; +import com.smore.payment.policy.fee.infrastructure.persistence.model.FeePolicyEntity; +import com.smore.payment.policy.fee.infrastructure.persistence.model.FeeRateJpa; +import com.smore.payment.policy.fee.infrastructure.persistence.model.FixedAmountJpa; +import com.smore.payment.policy.fee.domain.model.*; import org.springframework.stereotype.Component; import java.util.UUID; diff --git a/payment/src/main/java/com/smore/payment/feepolicy/infrastructure/persistence/model/FeePolicyEntity.java b/payment/src/main/java/com/smore/payment/policy/fee/infrastructure/persistence/model/FeePolicyEntity.java similarity index 85% rename from payment/src/main/java/com/smore/payment/feepolicy/infrastructure/persistence/model/FeePolicyEntity.java rename to payment/src/main/java/com/smore/payment/policy/fee/infrastructure/persistence/model/FeePolicyEntity.java index 3fc7fc40..de7862b1 100644 --- a/payment/src/main/java/com/smore/payment/feepolicy/infrastructure/persistence/model/FeePolicyEntity.java +++ b/payment/src/main/java/com/smore/payment/policy/fee/infrastructure/persistence/model/FeePolicyEntity.java @@ -1,8 +1,8 @@ -package com.smore.payment.feepolicy.infrastructure.persistence.model; +package com.smore.payment.policy.fee.infrastructure.persistence.model; -import com.smore.payment.feepolicy.domain.model.FeeType; -import com.smore.payment.feepolicy.domain.model.TargetType; -import com.smore.payment.global.entity.BaseEntity; +import com.smore.payment.policy.fee.domain.model.FeeType; +import com.smore.payment.policy.fee.domain.model.TargetType; +import com.smore.payment.shared.entity.BaseEntity; import jakarta.persistence.*; import lombok.AccessLevel; import lombok.Getter; diff --git a/payment/src/main/java/com/smore/payment/feepolicy/infrastructure/persistence/model/FeeRateJpa.java b/payment/src/main/java/com/smore/payment/policy/fee/infrastructure/persistence/model/FeeRateJpa.java similarity index 85% rename from payment/src/main/java/com/smore/payment/feepolicy/infrastructure/persistence/model/FeeRateJpa.java rename to payment/src/main/java/com/smore/payment/policy/fee/infrastructure/persistence/model/FeeRateJpa.java index 2f9e1d8d..93291a0c 100644 --- a/payment/src/main/java/com/smore/payment/feepolicy/infrastructure/persistence/model/FeeRateJpa.java +++ b/payment/src/main/java/com/smore/payment/policy/fee/infrastructure/persistence/model/FeeRateJpa.java @@ -1,4 +1,4 @@ -package com.smore.payment.feepolicy.infrastructure.persistence.model; +package com.smore.payment.policy.fee.infrastructure.persistence.model; import jakarta.persistence.Column; import jakarta.persistence.Embeddable; diff --git a/payment/src/main/java/com/smore/payment/feepolicy/infrastructure/persistence/model/FixedAmountJpa.java b/payment/src/main/java/com/smore/payment/policy/fee/infrastructure/persistence/model/FixedAmountJpa.java similarity index 86% rename from payment/src/main/java/com/smore/payment/feepolicy/infrastructure/persistence/model/FixedAmountJpa.java rename to payment/src/main/java/com/smore/payment/policy/fee/infrastructure/persistence/model/FixedAmountJpa.java index d472c4be..cf63f347 100644 --- a/payment/src/main/java/com/smore/payment/feepolicy/infrastructure/persistence/model/FixedAmountJpa.java +++ b/payment/src/main/java/com/smore/payment/policy/fee/infrastructure/persistence/model/FixedAmountJpa.java @@ -1,4 +1,4 @@ -package com.smore.payment.feepolicy.infrastructure.persistence.model; +package com.smore.payment.policy.fee.infrastructure.persistence.model; import jakarta.persistence.Column; import jakarta.persistence.Embeddable; diff --git a/payment/src/main/java/com/smore/payment/feepolicy/infrastructure/persistence/repository/FeePolicyJpaRepository.java b/payment/src/main/java/com/smore/payment/policy/fee/infrastructure/persistence/repository/FeePolicyJpaRepository.java similarity index 57% rename from payment/src/main/java/com/smore/payment/feepolicy/infrastructure/persistence/repository/FeePolicyJpaRepository.java rename to payment/src/main/java/com/smore/payment/policy/fee/infrastructure/persistence/repository/FeePolicyJpaRepository.java index 47368708..93990791 100644 --- a/payment/src/main/java/com/smore/payment/feepolicy/infrastructure/persistence/repository/FeePolicyJpaRepository.java +++ b/payment/src/main/java/com/smore/payment/policy/fee/infrastructure/persistence/repository/FeePolicyJpaRepository.java @@ -1,7 +1,7 @@ -package com.smore.payment.feepolicy.infrastructure.persistence.repository; +package com.smore.payment.policy.fee.infrastructure.persistence.repository; -import com.smore.payment.feepolicy.domain.model.TargetType; -import com.smore.payment.feepolicy.infrastructure.persistence.model.FeePolicyEntity; +import com.smore.payment.policy.fee.domain.model.TargetType; +import com.smore.payment.policy.fee.infrastructure.persistence.model.FeePolicyEntity; import org.springframework.data.jpa.repository.JpaRepository; import java.util.Optional; diff --git a/payment/src/main/java/com/smore/payment/feepolicy/infrastructure/persistence/repository/FeePolicyRepositoryImpl.java b/payment/src/main/java/com/smore/payment/policy/fee/infrastructure/persistence/repository/FeePolicyRepositoryImpl.java similarity index 79% rename from payment/src/main/java/com/smore/payment/feepolicy/infrastructure/persistence/repository/FeePolicyRepositoryImpl.java rename to payment/src/main/java/com/smore/payment/policy/fee/infrastructure/persistence/repository/FeePolicyRepositoryImpl.java index ec929c5a..40b2ff8e 100644 --- a/payment/src/main/java/com/smore/payment/feepolicy/infrastructure/persistence/repository/FeePolicyRepositoryImpl.java +++ b/payment/src/main/java/com/smore/payment/policy/fee/infrastructure/persistence/repository/FeePolicyRepositoryImpl.java @@ -1,11 +1,11 @@ -package com.smore.payment.feepolicy.infrastructure.persistence.repository; - -import com.smore.payment.feepolicy.domain.model.FeePolicy; -import com.smore.payment.feepolicy.domain.model.TargetKey; -import com.smore.payment.feepolicy.domain.model.TargetType; -import com.smore.payment.feepolicy.domain.repository.FeePolicyRepository; -import com.smore.payment.feepolicy.infrastructure.persistence.mapper.FeePolicyMapper; -import com.smore.payment.feepolicy.infrastructure.persistence.model.FeePolicyEntity; +package com.smore.payment.policy.fee.infrastructure.persistence.repository; + +import com.smore.payment.policy.fee.domain.model.FeePolicy; +import com.smore.payment.policy.fee.domain.model.TargetKey; +import com.smore.payment.policy.fee.domain.model.TargetType; +import com.smore.payment.policy.fee.domain.repository.FeePolicyRepository; +import com.smore.payment.policy.fee.infrastructure.persistence.mapper.FeePolicyMapper; +import com.smore.payment.policy.fee.infrastructure.persistence.model.FeePolicyEntity; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; diff --git a/payment/src/main/java/com/smore/payment/feepolicy/presentation/FeePolicyController.java b/payment/src/main/java/com/smore/payment/policy/fee/presentation/FeePolicyController.java similarity index 69% rename from payment/src/main/java/com/smore/payment/feepolicy/presentation/FeePolicyController.java rename to payment/src/main/java/com/smore/payment/policy/fee/presentation/FeePolicyController.java index 2ff1f0f1..1799467d 100644 --- a/payment/src/main/java/com/smore/payment/feepolicy/presentation/FeePolicyController.java +++ b/payment/src/main/java/com/smore/payment/policy/fee/presentation/FeePolicyController.java @@ -1,17 +1,15 @@ -package com.smore.payment.feepolicy.presentation; +package com.smore.payment.policy.fee.presentation; import com.smore.common.response.ApiResponse; -import com.smore.payment.feepolicy.application.FeePolicyService; -import com.smore.payment.feepolicy.application.command.CreateFeePolicyCommand; -import com.smore.payment.feepolicy.application.query.GetFeePolicyQuery; -import com.smore.payment.feepolicy.domain.model.FeePolicy; -import com.smore.payment.feepolicy.domain.model.TargetKeyLong; -import com.smore.payment.feepolicy.domain.model.TargetKeyUUID; -import com.smore.payment.feepolicy.domain.model.TargetType; -import com.smore.payment.feepolicy.presentation.dto.request.CreateFeePolicyRequestDto; -import com.smore.payment.feepolicy.presentation.dto.request.GetFeePolicyRequestDto; -import com.smore.payment.feepolicy.presentation.dto.response.GetFeePolicyResponseDto; -import com.smore.payment.global.config.UserContextHolder; +import com.smore.payment.policy.fee.application.FeePolicyService; +import com.smore.payment.policy.fee.application.query.GetFeePolicyQuery; +import com.smore.payment.policy.fee.domain.model.FeePolicy; +import com.smore.payment.policy.fee.domain.model.TargetKeyLong; +import com.smore.payment.policy.fee.domain.model.TargetKeyUUID; +import com.smore.payment.policy.fee.domain.model.TargetType; +import com.smore.payment.policy.fee.presentation.dto.request.CreateFeePolicyRequestDto; +import com.smore.payment.policy.fee.presentation.dto.response.GetFeePolicyResponseDto; +import com.smore.payment.shared.config.UserContextHolder; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; diff --git a/payment/src/main/java/com/smore/payment/feepolicy/presentation/dto/request/CreateFeePolicyRequestDto.java b/payment/src/main/java/com/smore/payment/policy/fee/presentation/dto/request/CreateFeePolicyRequestDto.java similarity index 84% rename from payment/src/main/java/com/smore/payment/feepolicy/presentation/dto/request/CreateFeePolicyRequestDto.java rename to payment/src/main/java/com/smore/payment/policy/fee/presentation/dto/request/CreateFeePolicyRequestDto.java index b5f335fe..e9c6b6d0 100644 --- a/payment/src/main/java/com/smore/payment/feepolicy/presentation/dto/request/CreateFeePolicyRequestDto.java +++ b/payment/src/main/java/com/smore/payment/policy/fee/presentation/dto/request/CreateFeePolicyRequestDto.java @@ -1,7 +1,7 @@ -package com.smore.payment.feepolicy.presentation.dto.request; +package com.smore.payment.policy.fee.presentation.dto.request; -import com.smore.payment.feepolicy.application.command.CreateFeePolicyCommand; -import com.smore.payment.feepolicy.domain.model.*; +import com.smore.payment.policy.fee.application.command.CreateFeePolicyCommand; +import com.smore.payment.policy.fee.domain.model.*; import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/payment/src/main/java/com/smore/payment/feepolicy/presentation/dto/request/GetFeePolicyRequestDto.java b/payment/src/main/java/com/smore/payment/policy/fee/presentation/dto/request/GetFeePolicyRequestDto.java similarity index 67% rename from payment/src/main/java/com/smore/payment/feepolicy/presentation/dto/request/GetFeePolicyRequestDto.java rename to payment/src/main/java/com/smore/payment/policy/fee/presentation/dto/request/GetFeePolicyRequestDto.java index 89e35985..4aacb254 100644 --- a/payment/src/main/java/com/smore/payment/feepolicy/presentation/dto/request/GetFeePolicyRequestDto.java +++ b/payment/src/main/java/com/smore/payment/policy/fee/presentation/dto/request/GetFeePolicyRequestDto.java @@ -1,10 +1,10 @@ -package com.smore.payment.feepolicy.presentation.dto.request; +package com.smore.payment.policy.fee.presentation.dto.request; -import com.smore.payment.feepolicy.application.query.GetFeePolicyQuery; -import com.smore.payment.feepolicy.domain.model.TargetKey; -import com.smore.payment.feepolicy.domain.model.TargetKeyLong; -import com.smore.payment.feepolicy.domain.model.TargetKeyUUID; -import com.smore.payment.feepolicy.domain.model.TargetType; +import com.smore.payment.policy.fee.application.query.GetFeePolicyQuery; +import com.smore.payment.policy.fee.domain.model.TargetKey; +import com.smore.payment.policy.fee.domain.model.TargetKeyLong; +import com.smore.payment.policy.fee.domain.model.TargetKeyUUID; +import com.smore.payment.policy.fee.domain.model.TargetType; import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/payment/src/main/java/com/smore/payment/feepolicy/presentation/dto/response/GetFeePolicyResponseDto.java b/payment/src/main/java/com/smore/payment/policy/fee/presentation/dto/response/GetFeePolicyResponseDto.java similarity index 87% rename from payment/src/main/java/com/smore/payment/feepolicy/presentation/dto/response/GetFeePolicyResponseDto.java rename to payment/src/main/java/com/smore/payment/policy/fee/presentation/dto/response/GetFeePolicyResponseDto.java index 4240a827..48796429 100644 --- a/payment/src/main/java/com/smore/payment/feepolicy/presentation/dto/response/GetFeePolicyResponseDto.java +++ b/payment/src/main/java/com/smore/payment/policy/fee/presentation/dto/response/GetFeePolicyResponseDto.java @@ -1,6 +1,6 @@ -package com.smore.payment.feepolicy.presentation.dto.response; +package com.smore.payment.policy.fee.presentation.dto.response; -import com.smore.payment.feepolicy.domain.model.FeePolicy; +import com.smore.payment.policy.fee.domain.model.FeePolicy; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; diff --git a/payment/src/main/java/com/smore/payment/refundpolicy/application/RefundPolicyService.java b/payment/src/main/java/com/smore/payment/policy/refund/application/RefundPolicyService.java similarity index 84% rename from payment/src/main/java/com/smore/payment/refundpolicy/application/RefundPolicyService.java rename to payment/src/main/java/com/smore/payment/policy/refund/application/RefundPolicyService.java index 7ce4a327..5965a244 100644 --- a/payment/src/main/java/com/smore/payment/refundpolicy/application/RefundPolicyService.java +++ b/payment/src/main/java/com/smore/payment/policy/refund/application/RefundPolicyService.java @@ -1,9 +1,9 @@ -package com.smore.payment.refundpolicy.application; +package com.smore.payment.policy.refund.application; -import com.smore.payment.refundpolicy.application.command.CreateRefundPolicyCommand; -import com.smore.payment.refundpolicy.application.query.GetRefundPolicyQuery; -import com.smore.payment.refundpolicy.domain.model.RefundPolicy; -import com.smore.payment.refundpolicy.domain.repository.RefundPolicyRepository; +import com.smore.payment.policy.refund.application.command.CreateRefundPolicyCommand; +import com.smore.payment.policy.refund.application.query.GetRefundPolicyQuery; +import com.smore.payment.policy.refund.domain.model.RefundPolicy; +import com.smore.payment.policy.refund.domain.repository.RefundPolicyRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; diff --git a/payment/src/main/java/com/smore/payment/refundpolicy/application/command/CreateRefundPolicyCommand.java b/payment/src/main/java/com/smore/payment/policy/refund/application/command/CreateRefundPolicyCommand.java similarity index 58% rename from payment/src/main/java/com/smore/payment/refundpolicy/application/command/CreateRefundPolicyCommand.java rename to payment/src/main/java/com/smore/payment/policy/refund/application/command/CreateRefundPolicyCommand.java index cb7e2aad..9a2e5327 100644 --- a/payment/src/main/java/com/smore/payment/refundpolicy/application/command/CreateRefundPolicyCommand.java +++ b/payment/src/main/java/com/smore/payment/policy/refund/application/command/CreateRefundPolicyCommand.java @@ -1,10 +1,8 @@ -package com.smore.payment.refundpolicy.application.command; +package com.smore.payment.policy.refund.application.command; -import com.smore.payment.refundpolicy.domain.model.*; -import com.smore.payment.refundpolicy.presentation.dto.request.CreateRefundPolicyRequestDto; +import com.smore.payment.policy.refund.domain.model.*; import java.time.Duration; -import java.util.UUID; public record CreateRefundPolicyCommand( RefundTargetType refundTargetType, diff --git a/payment/src/main/java/com/smore/payment/policy/refund/application/query/GetRefundPolicyQuery.java b/payment/src/main/java/com/smore/payment/policy/refund/application/query/GetRefundPolicyQuery.java new file mode 100644 index 00000000..1a941296 --- /dev/null +++ b/payment/src/main/java/com/smore/payment/policy/refund/application/query/GetRefundPolicyQuery.java @@ -0,0 +1,9 @@ +package com.smore.payment.policy.refund.application.query; + +import com.smore.payment.policy.refund.domain.model.RefundTargetType; +import com.smore.payment.policy.refund.domain.model.TargetKey; + +public record GetRefundPolicyQuery( + RefundTargetType refundTargetType, + TargetKey targetKey +) {} diff --git a/payment/src/main/java/com/smore/payment/refundpolicy/domain/model/RefundFeeRate.java b/payment/src/main/java/com/smore/payment/policy/refund/domain/model/RefundFeeRate.java similarity index 92% rename from payment/src/main/java/com/smore/payment/refundpolicy/domain/model/RefundFeeRate.java rename to payment/src/main/java/com/smore/payment/policy/refund/domain/model/RefundFeeRate.java index 16f793ee..1d4749a8 100644 --- a/payment/src/main/java/com/smore/payment/refundpolicy/domain/model/RefundFeeRate.java +++ b/payment/src/main/java/com/smore/payment/policy/refund/domain/model/RefundFeeRate.java @@ -1,4 +1,4 @@ -package com.smore.payment.refundpolicy.domain.model; +package com.smore.payment.policy.refund.domain.model; import java.math.BigDecimal; diff --git a/payment/src/main/java/com/smore/payment/refundpolicy/domain/model/RefundFeeType.java b/payment/src/main/java/com/smore/payment/policy/refund/domain/model/RefundFeeType.java similarity index 89% rename from payment/src/main/java/com/smore/payment/refundpolicy/domain/model/RefundFeeType.java rename to payment/src/main/java/com/smore/payment/policy/refund/domain/model/RefundFeeType.java index d57d4d2e..14b0ea71 100644 --- a/payment/src/main/java/com/smore/payment/refundpolicy/domain/model/RefundFeeType.java +++ b/payment/src/main/java/com/smore/payment/policy/refund/domain/model/RefundFeeType.java @@ -1,6 +1,4 @@ -package com.smore.payment.refundpolicy.domain.model; - -import lombok.Getter; +package com.smore.payment.policy.refund.domain.model; public enum RefundFeeType { RATE("비율 수수료 (예: 5%)"), diff --git a/payment/src/main/java/com/smore/payment/refundpolicy/domain/model/RefundFixedAmount.java b/payment/src/main/java/com/smore/payment/policy/refund/domain/model/RefundFixedAmount.java similarity index 90% rename from payment/src/main/java/com/smore/payment/refundpolicy/domain/model/RefundFixedAmount.java rename to payment/src/main/java/com/smore/payment/policy/refund/domain/model/RefundFixedAmount.java index 012c942c..932f4e28 100644 --- a/payment/src/main/java/com/smore/payment/refundpolicy/domain/model/RefundFixedAmount.java +++ b/payment/src/main/java/com/smore/payment/policy/refund/domain/model/RefundFixedAmount.java @@ -1,4 +1,4 @@ -package com.smore.payment.refundpolicy.domain.model; +package com.smore.payment.policy.refund.domain.model; import java.math.BigDecimal; diff --git a/payment/src/main/java/com/smore/payment/refundpolicy/domain/model/RefundPolicy.java b/payment/src/main/java/com/smore/payment/policy/refund/domain/model/RefundPolicy.java similarity index 98% rename from payment/src/main/java/com/smore/payment/refundpolicy/domain/model/RefundPolicy.java rename to payment/src/main/java/com/smore/payment/policy/refund/domain/model/RefundPolicy.java index 12ee73ac..d410b05e 100644 --- a/payment/src/main/java/com/smore/payment/refundpolicy/domain/model/RefundPolicy.java +++ b/payment/src/main/java/com/smore/payment/policy/refund/domain/model/RefundPolicy.java @@ -1,4 +1,4 @@ -package com.smore.payment.refundpolicy.domain.model; +package com.smore.payment.policy.refund.domain.model; import java.time.Duration; import java.util.UUID; diff --git a/payment/src/main/java/com/smore/payment/refundpolicy/domain/model/RefundTargetType.java b/payment/src/main/java/com/smore/payment/policy/refund/domain/model/RefundTargetType.java similarity index 86% rename from payment/src/main/java/com/smore/payment/refundpolicy/domain/model/RefundTargetType.java rename to payment/src/main/java/com/smore/payment/policy/refund/domain/model/RefundTargetType.java index 74d9fe89..ac84371a 100644 --- a/payment/src/main/java/com/smore/payment/refundpolicy/domain/model/RefundTargetType.java +++ b/payment/src/main/java/com/smore/payment/policy/refund/domain/model/RefundTargetType.java @@ -1,4 +1,4 @@ -package com.smore.payment.refundpolicy.domain.model; +package com.smore.payment.policy.refund.domain.model; public enum RefundTargetType { CATEGORY, MERCHANT, AUCTION_TYPE, USER_TYPE; diff --git a/payment/src/main/java/com/smore/payment/cancelpolicy/domain/model/TargetKey.java b/payment/src/main/java/com/smore/payment/policy/refund/domain/model/TargetKey.java similarity index 62% rename from payment/src/main/java/com/smore/payment/cancelpolicy/domain/model/TargetKey.java rename to payment/src/main/java/com/smore/payment/policy/refund/domain/model/TargetKey.java index 34112a14..5473f981 100644 --- a/payment/src/main/java/com/smore/payment/cancelpolicy/domain/model/TargetKey.java +++ b/payment/src/main/java/com/smore/payment/policy/refund/domain/model/TargetKey.java @@ -1,4 +1,4 @@ -package com.smore.payment.cancelpolicy.domain.model; +package com.smore.payment.policy.refund.domain.model; public interface TargetKey { Object getTargetKey(); diff --git a/payment/src/main/java/com/smore/payment/cancelpolicy/domain/model/TargetKeyLong.java b/payment/src/main/java/com/smore/payment/policy/refund/domain/model/TargetKeyLong.java similarity index 81% rename from payment/src/main/java/com/smore/payment/cancelpolicy/domain/model/TargetKeyLong.java rename to payment/src/main/java/com/smore/payment/policy/refund/domain/model/TargetKeyLong.java index 7f1a3427..951bf256 100644 --- a/payment/src/main/java/com/smore/payment/cancelpolicy/domain/model/TargetKeyLong.java +++ b/payment/src/main/java/com/smore/payment/policy/refund/domain/model/TargetKeyLong.java @@ -1,4 +1,4 @@ -package com.smore.payment.cancelpolicy.domain.model; +package com.smore.payment.policy.refund.domain.model; public record TargetKeyLong(Long value) implements TargetKey { @Override diff --git a/payment/src/main/java/com/smore/payment/refundpolicy/domain/model/TargetKeyString.java b/payment/src/main/java/com/smore/payment/policy/refund/domain/model/TargetKeyString.java similarity index 80% rename from payment/src/main/java/com/smore/payment/refundpolicy/domain/model/TargetKeyString.java rename to payment/src/main/java/com/smore/payment/policy/refund/domain/model/TargetKeyString.java index 2faf3e17..e9922833 100644 --- a/payment/src/main/java/com/smore/payment/refundpolicy/domain/model/TargetKeyString.java +++ b/payment/src/main/java/com/smore/payment/policy/refund/domain/model/TargetKeyString.java @@ -1,4 +1,4 @@ -package com.smore.payment.refundpolicy.domain.model; +package com.smore.payment.policy.refund.domain.model; public record TargetKeyString(String value) implements TargetKey { @Override diff --git a/payment/src/main/java/com/smore/payment/refundpolicy/domain/model/TargetKeyUUID.java b/payment/src/main/java/com/smore/payment/policy/refund/domain/model/TargetKeyUUID.java similarity index 82% rename from payment/src/main/java/com/smore/payment/refundpolicy/domain/model/TargetKeyUUID.java rename to payment/src/main/java/com/smore/payment/policy/refund/domain/model/TargetKeyUUID.java index 2f7f81b2..a5c942d1 100644 --- a/payment/src/main/java/com/smore/payment/refundpolicy/domain/model/TargetKeyUUID.java +++ b/payment/src/main/java/com/smore/payment/policy/refund/domain/model/TargetKeyUUID.java @@ -1,4 +1,4 @@ -package com.smore.payment.refundpolicy.domain.model; +package com.smore.payment.policy.refund.domain.model; import java.util.UUID; diff --git a/payment/src/main/java/com/smore/payment/refundpolicy/domain/repository/RefundPolicyRepository.java b/payment/src/main/java/com/smore/payment/policy/refund/domain/repository/RefundPolicyRepository.java similarity index 66% rename from payment/src/main/java/com/smore/payment/refundpolicy/domain/repository/RefundPolicyRepository.java rename to payment/src/main/java/com/smore/payment/policy/refund/domain/repository/RefundPolicyRepository.java index 5a6b4a40..1f0427e0 100644 --- a/payment/src/main/java/com/smore/payment/refundpolicy/domain/repository/RefundPolicyRepository.java +++ b/payment/src/main/java/com/smore/payment/policy/refund/domain/repository/RefundPolicyRepository.java @@ -1,8 +1,8 @@ -package com.smore.payment.refundpolicy.domain.repository; +package com.smore.payment.policy.refund.domain.repository; -import com.smore.payment.refundpolicy.domain.model.RefundPolicy; -import com.smore.payment.refundpolicy.domain.model.RefundTargetType; -import com.smore.payment.refundpolicy.domain.model.TargetKey; +import com.smore.payment.policy.refund.domain.model.RefundPolicy; +import com.smore.payment.policy.refund.domain.model.RefundTargetType; +import com.smore.payment.policy.refund.domain.model.TargetKey; import org.springframework.stereotype.Repository; import java.util.Optional; diff --git a/payment/src/main/java/com/smore/payment/refundpolicy/infrastructure/persistence/mapper/RefundPolicyMapper.java b/payment/src/main/java/com/smore/payment/policy/refund/infrastructure/persistence/mapper/RefundPolicyMapper.java similarity index 84% rename from payment/src/main/java/com/smore/payment/refundpolicy/infrastructure/persistence/mapper/RefundPolicyMapper.java rename to payment/src/main/java/com/smore/payment/policy/refund/infrastructure/persistence/mapper/RefundPolicyMapper.java index 878da9ab..013b0b5e 100644 --- a/payment/src/main/java/com/smore/payment/refundpolicy/infrastructure/persistence/mapper/RefundPolicyMapper.java +++ b/payment/src/main/java/com/smore/payment/policy/refund/infrastructure/persistence/mapper/RefundPolicyMapper.java @@ -1,9 +1,9 @@ -package com.smore.payment.refundpolicy.infrastructure.persistence.mapper; +package com.smore.payment.policy.refund.infrastructure.persistence.mapper; -import com.smore.payment.refundpolicy.domain.model.*; -import com.smore.payment.refundpolicy.infrastructure.persistence.model.RefundFeeRateJpa; -import com.smore.payment.refundpolicy.infrastructure.persistence.model.RefundFixedAmountJpa; -import com.smore.payment.refundpolicy.infrastructure.persistence.model.RefundPolicyEntity; +import com.smore.payment.policy.refund.domain.model.*; +import com.smore.payment.policy.refund.infrastructure.persistence.model.RefundFeeRateJpa; +import com.smore.payment.policy.refund.infrastructure.persistence.model.RefundFixedAmountJpa; +import com.smore.payment.policy.refund.infrastructure.persistence.model.RefundPolicyEntity; import org.springframework.stereotype.Component; import java.util.UUID; diff --git a/payment/src/main/java/com/smore/payment/refundpolicy/infrastructure/persistence/model/RefundFeeRateJpa.java b/payment/src/main/java/com/smore/payment/policy/refund/infrastructure/persistence/model/RefundFeeRateJpa.java similarity index 85% rename from payment/src/main/java/com/smore/payment/refundpolicy/infrastructure/persistence/model/RefundFeeRateJpa.java rename to payment/src/main/java/com/smore/payment/policy/refund/infrastructure/persistence/model/RefundFeeRateJpa.java index 9dd59aab..0600c940 100644 --- a/payment/src/main/java/com/smore/payment/refundpolicy/infrastructure/persistence/model/RefundFeeRateJpa.java +++ b/payment/src/main/java/com/smore/payment/policy/refund/infrastructure/persistence/model/RefundFeeRateJpa.java @@ -1,4 +1,4 @@ -package com.smore.payment.refundpolicy.infrastructure.persistence.model; +package com.smore.payment.policy.refund.infrastructure.persistence.model; import jakarta.persistence.Column; import jakarta.persistence.Embeddable; diff --git a/payment/src/main/java/com/smore/payment/refundpolicy/infrastructure/persistence/model/RefundFixedAmountJpa.java b/payment/src/main/java/com/smore/payment/policy/refund/infrastructure/persistence/model/RefundFixedAmountJpa.java similarity index 86% rename from payment/src/main/java/com/smore/payment/refundpolicy/infrastructure/persistence/model/RefundFixedAmountJpa.java rename to payment/src/main/java/com/smore/payment/policy/refund/infrastructure/persistence/model/RefundFixedAmountJpa.java index 12694667..f00244f1 100644 --- a/payment/src/main/java/com/smore/payment/refundpolicy/infrastructure/persistence/model/RefundFixedAmountJpa.java +++ b/payment/src/main/java/com/smore/payment/policy/refund/infrastructure/persistence/model/RefundFixedAmountJpa.java @@ -1,4 +1,4 @@ -package com.smore.payment.refundpolicy.infrastructure.persistence.model; +package com.smore.payment.policy.refund.infrastructure.persistence.model; import jakarta.persistence.Column; import jakarta.persistence.Embeddable; diff --git a/payment/src/main/java/com/smore/payment/refundpolicy/infrastructure/persistence/model/RefundPolicyEntity.java b/payment/src/main/java/com/smore/payment/policy/refund/infrastructure/persistence/model/RefundPolicyEntity.java similarity index 85% rename from payment/src/main/java/com/smore/payment/refundpolicy/infrastructure/persistence/model/RefundPolicyEntity.java rename to payment/src/main/java/com/smore/payment/policy/refund/infrastructure/persistence/model/RefundPolicyEntity.java index 13e97894..7ca54d82 100644 --- a/payment/src/main/java/com/smore/payment/refundpolicy/infrastructure/persistence/model/RefundPolicyEntity.java +++ b/payment/src/main/java/com/smore/payment/policy/refund/infrastructure/persistence/model/RefundPolicyEntity.java @@ -1,9 +1,8 @@ -package com.smore.payment.refundpolicy.infrastructure.persistence.model; +package com.smore.payment.policy.refund.infrastructure.persistence.model; -import com.smore.payment.global.entity.BaseEntity; -import com.smore.payment.refundpolicy.domain.model.RefundFeeType; -import com.smore.payment.refundpolicy.domain.model.RefundTargetType; -import com.smore.payment.refundpolicy.domain.model.TargetKey; +import com.smore.payment.shared.entity.BaseEntity; +import com.smore.payment.policy.refund.domain.model.RefundFeeType; +import com.smore.payment.policy.refund.domain.model.RefundTargetType; import jakarta.persistence.*; import lombok.AccessLevel; import lombok.Getter; diff --git a/payment/src/main/java/com/smore/payment/refundpolicy/infrastructure/persistence/repository/RefundPolicyJpaRepository.java b/payment/src/main/java/com/smore/payment/policy/refund/infrastructure/persistence/repository/RefundPolicyJpaRepository.java similarity index 57% rename from payment/src/main/java/com/smore/payment/refundpolicy/infrastructure/persistence/repository/RefundPolicyJpaRepository.java rename to payment/src/main/java/com/smore/payment/policy/refund/infrastructure/persistence/repository/RefundPolicyJpaRepository.java index 55cc0fdd..08db21b2 100644 --- a/payment/src/main/java/com/smore/payment/refundpolicy/infrastructure/persistence/repository/RefundPolicyJpaRepository.java +++ b/payment/src/main/java/com/smore/payment/policy/refund/infrastructure/persistence/repository/RefundPolicyJpaRepository.java @@ -1,7 +1,7 @@ -package com.smore.payment.refundpolicy.infrastructure.persistence.repository; +package com.smore.payment.policy.refund.infrastructure.persistence.repository; -import com.smore.payment.refundpolicy.domain.model.RefundTargetType; -import com.smore.payment.refundpolicy.infrastructure.persistence.model.RefundPolicyEntity; +import com.smore.payment.policy.refund.domain.model.RefundTargetType; +import com.smore.payment.policy.refund.infrastructure.persistence.model.RefundPolicyEntity; import org.springframework.data.jpa.repository.JpaRepository; import java.util.Optional; diff --git a/payment/src/main/java/com/smore/payment/refundpolicy/infrastructure/persistence/repository/RefundPolicyRepositoryImpl.java b/payment/src/main/java/com/smore/payment/policy/refund/infrastructure/persistence/repository/RefundPolicyRepositoryImpl.java similarity index 77% rename from payment/src/main/java/com/smore/payment/refundpolicy/infrastructure/persistence/repository/RefundPolicyRepositoryImpl.java rename to payment/src/main/java/com/smore/payment/policy/refund/infrastructure/persistence/repository/RefundPolicyRepositoryImpl.java index d9b075bf..467ea6f3 100644 --- a/payment/src/main/java/com/smore/payment/refundpolicy/infrastructure/persistence/repository/RefundPolicyRepositoryImpl.java +++ b/payment/src/main/java/com/smore/payment/policy/refund/infrastructure/persistence/repository/RefundPolicyRepositoryImpl.java @@ -1,12 +1,11 @@ -package com.smore.payment.refundpolicy.infrastructure.persistence.repository; - -import com.smore.payment.cancelpolicy.domain.model.CancelTargetType; -import com.smore.payment.refundpolicy.domain.model.RefundPolicy; -import com.smore.payment.refundpolicy.domain.model.RefundTargetType; -import com.smore.payment.refundpolicy.domain.model.TargetKey; -import com.smore.payment.refundpolicy.domain.repository.RefundPolicyRepository; -import com.smore.payment.refundpolicy.infrastructure.persistence.mapper.RefundPolicyMapper; -import com.smore.payment.refundpolicy.infrastructure.persistence.model.RefundPolicyEntity; +package com.smore.payment.policy.refund.infrastructure.persistence.repository; + +import com.smore.payment.policy.refund.domain.model.RefundPolicy; +import com.smore.payment.policy.refund.domain.model.RefundTargetType; +import com.smore.payment.policy.refund.domain.model.TargetKey; +import com.smore.payment.policy.refund.domain.repository.RefundPolicyRepository; +import com.smore.payment.policy.refund.infrastructure.persistence.mapper.RefundPolicyMapper; +import com.smore.payment.policy.refund.infrastructure.persistence.model.RefundPolicyEntity; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; diff --git a/payment/src/main/java/com/smore/payment/refundpolicy/presentation/RefundPolicyController.java b/payment/src/main/java/com/smore/payment/policy/refund/presentation/RefundPolicyController.java similarity index 80% rename from payment/src/main/java/com/smore/payment/refundpolicy/presentation/RefundPolicyController.java rename to payment/src/main/java/com/smore/payment/policy/refund/presentation/RefundPolicyController.java index c9037155..b62eecfa 100644 --- a/payment/src/main/java/com/smore/payment/refundpolicy/presentation/RefundPolicyController.java +++ b/payment/src/main/java/com/smore/payment/policy/refund/presentation/RefundPolicyController.java @@ -1,12 +1,12 @@ -package com.smore.payment.refundpolicy.presentation; +package com.smore.payment.policy.refund.presentation; import com.smore.common.response.ApiResponse; -import com.smore.payment.global.config.UserContextHolder; -import com.smore.payment.refundpolicy.application.RefundPolicyService; -import com.smore.payment.refundpolicy.application.query.GetRefundPolicyQuery; -import com.smore.payment.refundpolicy.domain.model.*; -import com.smore.payment.refundpolicy.presentation.dto.request.CreateRefundPolicyRequestDto; -import com.smore.payment.refundpolicy.presentation.dto.response.GetRefundPolicyResponseDto; +import com.smore.payment.policy.refund.domain.model.*; +import com.smore.payment.shared.config.UserContextHolder; +import com.smore.payment.policy.refund.application.RefundPolicyService; +import com.smore.payment.policy.refund.application.query.GetRefundPolicyQuery; +import com.smore.payment.policy.refund.presentation.dto.request.CreateRefundPolicyRequestDto; +import com.smore.payment.policy.refund.presentation.dto.response.GetRefundPolicyResponseDto; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; diff --git a/payment/src/main/java/com/smore/payment/refundpolicy/presentation/dto/request/CreateRefundPolicyRequestDto.java b/payment/src/main/java/com/smore/payment/policy/refund/presentation/dto/request/CreateRefundPolicyRequestDto.java similarity index 87% rename from payment/src/main/java/com/smore/payment/refundpolicy/presentation/dto/request/CreateRefundPolicyRequestDto.java rename to payment/src/main/java/com/smore/payment/policy/refund/presentation/dto/request/CreateRefundPolicyRequestDto.java index ee0a040b..138015a7 100644 --- a/payment/src/main/java/com/smore/payment/refundpolicy/presentation/dto/request/CreateRefundPolicyRequestDto.java +++ b/payment/src/main/java/com/smore/payment/policy/refund/presentation/dto/request/CreateRefundPolicyRequestDto.java @@ -1,7 +1,7 @@ -package com.smore.payment.refundpolicy.presentation.dto.request; +package com.smore.payment.policy.refund.presentation.dto.request; -import com.smore.payment.refundpolicy.application.command.CreateRefundPolicyCommand; -import com.smore.payment.refundpolicy.domain.model.*; +import com.smore.payment.policy.refund.application.command.CreateRefundPolicyCommand; +import com.smore.payment.policy.refund.domain.model.*; import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/payment/src/main/java/com/smore/payment/refundpolicy/presentation/dto/response/GetRefundPolicyResponseDto.java b/payment/src/main/java/com/smore/payment/policy/refund/presentation/dto/response/GetRefundPolicyResponseDto.java similarity index 89% rename from payment/src/main/java/com/smore/payment/refundpolicy/presentation/dto/response/GetRefundPolicyResponseDto.java rename to payment/src/main/java/com/smore/payment/policy/refund/presentation/dto/response/GetRefundPolicyResponseDto.java index 403c0518..f470418b 100644 --- a/payment/src/main/java/com/smore/payment/refundpolicy/presentation/dto/response/GetRefundPolicyResponseDto.java +++ b/payment/src/main/java/com/smore/payment/policy/refund/presentation/dto/response/GetRefundPolicyResponseDto.java @@ -1,6 +1,6 @@ -package com.smore.payment.refundpolicy.presentation.dto.response; +package com.smore.payment.policy.refund.presentation.dto.response; -import com.smore.payment.refundpolicy.domain.model.RefundPolicy; +import com.smore.payment.policy.refund.domain.model.RefundPolicy; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; diff --git a/payment/src/main/java/com/smore/payment/refundpolicy/application/query/GetRefundPolicyQuery.java b/payment/src/main/java/com/smore/payment/refundpolicy/application/query/GetRefundPolicyQuery.java deleted file mode 100644 index 81647010..00000000 --- a/payment/src/main/java/com/smore/payment/refundpolicy/application/query/GetRefundPolicyQuery.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.smore.payment.refundpolicy.application.query; - -import com.smore.payment.refundpolicy.domain.model.RefundTargetType; -import com.smore.payment.refundpolicy.domain.model.TargetKey; - -public record GetRefundPolicyQuery( - RefundTargetType refundTargetType, - TargetKey targetKey -) {} diff --git a/payment/src/main/java/com/smore/payment/global/config/AuditorAwareImpl.java b/payment/src/main/java/com/smore/payment/shared/config/AuditorAwareImpl.java similarity index 85% rename from payment/src/main/java/com/smore/payment/global/config/AuditorAwareImpl.java rename to payment/src/main/java/com/smore/payment/shared/config/AuditorAwareImpl.java index 41dc65b1..837447e8 100644 --- a/payment/src/main/java/com/smore/payment/global/config/AuditorAwareImpl.java +++ b/payment/src/main/java/com/smore/payment/shared/config/AuditorAwareImpl.java @@ -1,11 +1,10 @@ -package com.smore.payment.global.config; +package com.smore.payment.shared.config; import lombok.NonNull; import org.springframework.data.domain.AuditorAware; import org.springframework.stereotype.Component; import java.util.Optional; -import java.util.UUID; @Component public class AuditorAwareImpl implements AuditorAware { diff --git a/payment/src/main/java/com/smore/payment/global/config/JpaAuditingConfig.java b/payment/src/main/java/com/smore/payment/shared/config/JpaAuditingConfig.java similarity index 83% rename from payment/src/main/java/com/smore/payment/global/config/JpaAuditingConfig.java rename to payment/src/main/java/com/smore/payment/shared/config/JpaAuditingConfig.java index 09a9d1df..c41012f1 100644 --- a/payment/src/main/java/com/smore/payment/global/config/JpaAuditingConfig.java +++ b/payment/src/main/java/com/smore/payment/shared/config/JpaAuditingConfig.java @@ -1,4 +1,4 @@ -package com.smore.payment.global.config; +package com.smore.payment.shared.config; import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; diff --git a/payment/src/main/java/com/smore/payment/global/config/UserContextHolder.java b/payment/src/main/java/com/smore/payment/shared/config/UserContextHolder.java similarity index 81% rename from payment/src/main/java/com/smore/payment/global/config/UserContextHolder.java rename to payment/src/main/java/com/smore/payment/shared/config/UserContextHolder.java index 5b19e4ab..c76b7ed5 100644 --- a/payment/src/main/java/com/smore/payment/global/config/UserContextHolder.java +++ b/payment/src/main/java/com/smore/payment/shared/config/UserContextHolder.java @@ -1,6 +1,4 @@ -package com.smore.payment.global.config; - -import java.util.UUID; +package com.smore.payment.shared.config; public class UserContextHolder { private static final ThreadLocal userIdHolder = new ThreadLocal<>(); diff --git a/payment/src/main/java/com/smore/payment/global/entity/BaseEntity.java b/payment/src/main/java/com/smore/payment/shared/entity/BaseEntity.java similarity index 93% rename from payment/src/main/java/com/smore/payment/global/entity/BaseEntity.java rename to payment/src/main/java/com/smore/payment/shared/entity/BaseEntity.java index 05721a4b..3a481248 100644 --- a/payment/src/main/java/com/smore/payment/global/entity/BaseEntity.java +++ b/payment/src/main/java/com/smore/payment/shared/entity/BaseEntity.java @@ -1,10 +1,9 @@ -package com.smore.payment.global.entity; +package com.smore.payment.shared.entity; import jakarta.persistence.EntityListeners; import jakarta.persistence.MappedSuperclass; import java.time.LocalDateTime; -import java.util.UUID; import lombok.Getter; import org.springframework.data.annotation.CreatedBy; diff --git a/payment/src/main/java/com/smore/payment/global/outbox/OutboxMessage.java b/payment/src/main/java/com/smore/payment/shared/outbox/OutboxMessage.java similarity index 78% rename from payment/src/main/java/com/smore/payment/global/outbox/OutboxMessage.java rename to payment/src/main/java/com/smore/payment/shared/outbox/OutboxMessage.java index 7b712b4f..4a4e069f 100644 --- a/payment/src/main/java/com/smore/payment/global/outbox/OutboxMessage.java +++ b/payment/src/main/java/com/smore/payment/shared/outbox/OutboxMessage.java @@ -1,9 +1,5 @@ -package com.smore.payment.global.outbox; +package com.smore.payment.shared.outbox; -import com.smore.payment.payment.application.event.outbound.PaymentApprovedEvent; -import com.smore.payment.payment.application.event.outbound.PaymentFailedEvent; -import com.smore.payment.global.util.JsonUtil; -import com.smore.payment.payment.application.event.outbound.SettlementCalculatedEvent; import lombok.Getter; import java.time.LocalDateTime; diff --git a/payment/src/main/java/com/smore/payment/global/outbox/OutboxMessageCreator.java b/payment/src/main/java/com/smore/payment/shared/outbox/OutboxMessageCreator.java similarity index 86% rename from payment/src/main/java/com/smore/payment/global/outbox/OutboxMessageCreator.java rename to payment/src/main/java/com/smore/payment/shared/outbox/OutboxMessageCreator.java index 056ebe71..22164951 100644 --- a/payment/src/main/java/com/smore/payment/global/outbox/OutboxMessageCreator.java +++ b/payment/src/main/java/com/smore/payment/shared/outbox/OutboxMessageCreator.java @@ -1,7 +1,13 @@ -package com.smore.payment.global.outbox; - -import com.smore.payment.global.util.JsonUtil; -import com.smore.payment.payment.application.event.outbound.*; +package com.smore.payment.shared.outbox; + +import com.smore.payment.shared.util.JsonUtil; +import com.smore.payment.payment.application.event.outbound.PaymentFailedEvent; +import com.smore.payment.payment.application.event.outbound.PaymentRefundFailedEvent; +import com.smore.payment.payment.application.event.outbound.PaymentRefundSucceededEvent; +import com.smore.payment.payment.application.event.outbound.SettlementFailedEvent; +import com.smore.payment.payment.application.event.outbound.SettlementSuccessEvent; +import com.smore.payment.payment.domain.event.PaymentApprovedEvent; +import com.smore.payment.payment.domain.event.SettlementCalculatedEvent; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; diff --git a/payment/src/main/java/com/smore/payment/global/outbox/OutboxStatus.java b/payment/src/main/java/com/smore/payment/shared/outbox/OutboxStatus.java similarity index 60% rename from payment/src/main/java/com/smore/payment/global/outbox/OutboxStatus.java rename to payment/src/main/java/com/smore/payment/shared/outbox/OutboxStatus.java index 950567d1..6475fd56 100644 --- a/payment/src/main/java/com/smore/payment/global/outbox/OutboxStatus.java +++ b/payment/src/main/java/com/smore/payment/shared/outbox/OutboxStatus.java @@ -1,4 +1,4 @@ -package com.smore.payment.global.outbox; +package com.smore.payment.shared.outbox; public enum OutboxStatus { PENDING, diff --git a/payment/src/main/java/com/smore/payment/global/util/JsonUtil.java b/payment/src/main/java/com/smore/payment/shared/util/JsonUtil.java similarity index 95% rename from payment/src/main/java/com/smore/payment/global/util/JsonUtil.java rename to payment/src/main/java/com/smore/payment/shared/util/JsonUtil.java index 92e1cf77..a9c9b8a3 100644 --- a/payment/src/main/java/com/smore/payment/global/util/JsonUtil.java +++ b/payment/src/main/java/com/smore/payment/shared/util/JsonUtil.java @@ -1,4 +1,4 @@ -package com.smore.payment.global.util; +package com.smore.payment.shared.util; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; From 0f6fd19fc1a570d053925c05c19e00d72570f9be Mon Sep 17 00:00:00 2001 From: ooinl77 Date: Tue, 16 Dec 2025 23:49:32 +0900 Subject: [PATCH 03/16] =?UTF-8?q?fix:=20settlement=20domain=20service=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../payment/application/PaymentRefundService.java | 2 +- .../application/PaymentSettlementService.java | 11 ++++------- .../domain/service/SettlementValidationResult.java | 11 ----------- .../domain/service/SettlementValidationService.java | 12 ++++++++---- 4 files changed, 13 insertions(+), 23 deletions(-) diff --git a/payment/src/main/java/com/smore/payment/payment/application/PaymentRefundService.java b/payment/src/main/java/com/smore/payment/payment/application/PaymentRefundService.java index 11375ae1..d1bc3282 100644 --- a/payment/src/main/java/com/smore/payment/payment/application/PaymentRefundService.java +++ b/payment/src/main/java/com/smore/payment/payment/application/PaymentRefundService.java @@ -60,7 +60,7 @@ public void refund(PaymentRefundEvent event) { OutboxMessage failedMsg = outboxCreator.paymentRefundFailed( PaymentRefundFailedEvent.of(event.orderId(), event.refundId(), event.refundAmount(), refundDecision.failureReason()) ); - outboxRepository.save(failedMsg); + outboxPort.save(failedMsg); return; } diff --git a/payment/src/main/java/com/smore/payment/payment/application/PaymentSettlementService.java b/payment/src/main/java/com/smore/payment/payment/application/PaymentSettlementService.java index 7ad8afb6..f18d27a8 100644 --- a/payment/src/main/java/com/smore/payment/payment/application/PaymentSettlementService.java +++ b/payment/src/main/java/com/smore/payment/payment/application/PaymentSettlementService.java @@ -1,22 +1,19 @@ package com.smore.payment.payment.application; -import com.smore.payment.payment.application.port.in.RefundPaymentUseCase; +import com.smore.payment.payment.application.event.inbound.PaymentSettlementRequestEvent; +import com.smore.payment.payment.application.event.outbound.SettlementFailedEvent; +import com.smore.payment.payment.application.event.outbound.SettlementSuccessEvent; import com.smore.payment.payment.application.port.in.SettlePaymentUseCase; import com.smore.payment.payment.application.port.out.OutboxPort; +import com.smore.payment.payment.domain.repository.SellerSettlementLedgerRepository; import com.smore.payment.payment.domain.service.SettlementValidationResult; import com.smore.payment.payment.domain.service.SettlementValidationService; import com.smore.payment.shared.outbox.OutboxMessageCreator; -import com.smore.payment.payment.application.event.inbound.PaymentSettlementRequestEvent; -import com.smore.payment.payment.application.event.outbound.SettlementFailedEvent; -import com.smore.payment.payment.application.event.outbound.SettlementSuccessEvent; -import com.smore.payment.payment.domain.repository.SellerSettlementLedgerRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.math.BigDecimal; - @Slf4j @Service @RequiredArgsConstructor diff --git a/payment/src/main/java/com/smore/payment/payment/domain/service/SettlementValidationResult.java b/payment/src/main/java/com/smore/payment/payment/domain/service/SettlementValidationResult.java index e4fad494..019ec4c6 100644 --- a/payment/src/main/java/com/smore/payment/payment/domain/service/SettlementValidationResult.java +++ b/payment/src/main/java/com/smore/payment/payment/domain/service/SettlementValidationResult.java @@ -2,15 +2,4 @@ public record SettlementValidationResult(boolean valid, boolean duplicated, String failureReason) { - public static SettlementValidationResult duplicated() { - return new SettlementValidationResult(false, true, null); - } - - public static SettlementValidationResult invalid(String failureReason) { - return new SettlementValidationResult(false, false, failureReason); - } - - public static SettlementValidationResult valid() { - return new SettlementValidationResult(true, false, null); - } } \ No newline at end of file diff --git a/payment/src/main/java/com/smore/payment/payment/domain/service/SettlementValidationService.java b/payment/src/main/java/com/smore/payment/payment/domain/service/SettlementValidationService.java index c342bc53..af77cfac 100644 --- a/payment/src/main/java/com/smore/payment/payment/domain/service/SettlementValidationService.java +++ b/payment/src/main/java/com/smore/payment/payment/domain/service/SettlementValidationService.java @@ -15,19 +15,23 @@ public class SettlementValidationService { public SettlementValidationResult validate(PaymentSettlementRequestEvent event) { if (sellerSettlementLedgerRepository.existsByIdempotencyKey(event.idempotencyKey())) { - return SettlementValidationResult.duplicated(); + return new SettlementValidationResult(false, true, null); } if (event.accountNumber() == null || event.accountNumber().isBlank()) { - return SettlementValidationResult.invalid("계좌 번호가 잘못 입력되었습니다."); + return new SettlementValidationResult(false, false, "계좌 번호가 잘못 입력되었습니다."); + } + + if (event.amount() == null || event.amount().compareTo(BigDecimal.ZERO) <= 0) { + return new SettlementValidationResult(false, false, "정산 금액이 올바르지 않습니다."); } BigDecimal balance = sellerSettlementLedgerRepository.calculateBalance(event.userId()); if (balance.compareTo(event.amount()) < 0 || balance.compareTo(BigDecimal.valueOf(1_000_000_000L)) > 0) { - return SettlementValidationResult.invalid("출금 가능 잔액이 부족합니다."); + return new SettlementValidationResult(false, false, "출금 가능 잔액이 부족합니다."); } - return SettlementValidationResult.valid(); + return new SettlementValidationResult(true, false, null); } } \ No newline at end of file From 4b72495cb524cbe6c93766640aa9f2bdf59beba1 Mon Sep 17 00:00:00 2001 From: ooinl77 Date: Wed, 17 Dec 2025 00:26:43 +0900 Subject: [PATCH 04/16] =?UTF-8?q?chore:=20domain=20repository=20=ED=8C=A8?= =?UTF-8?q?=ED=82=A4=EC=A7=80=20=EA=B5=AC=EC=A1=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/ApprovePaymentService.java | 21 +++++++------------ .../CreateTemporaryPaymentService.java | 3 +-- .../application/PaymentRefundService.java | 2 +- .../application/PaymentSettlementService.java | 2 +- .../port/out}/MongoRepository.java | 4 +--- .../port/out}/PaymentRepository.java | 4 +--- .../SellerSettlementLedgerRepository.java | 2 +- .../port/out/TemporaryPaymentPort.java | 3 +-- .../model/TemporaryPayment.java | 3 +-- .../domain/repository/RedisRepository.java | 18 ---------------- .../service/SettlementValidationService.java | 2 +- .../infrastructure/config/RedisConfig.java | 2 +- .../payment/PaymentRepositoryImpl.java | 2 +- .../mongo/repository/MongoRepositoryImpl.java | 2 +- .../SellerSettlementLedgerRepositoryImpl.java | 2 +- .../redis/TemporaryPaymentAdapter.java | 2 +- 16 files changed, 22 insertions(+), 52 deletions(-) rename payment/src/main/java/com/smore/payment/payment/{domain/repository => application/port/out}/MongoRepository.java (58%) rename payment/src/main/java/com/smore/payment/payment/{domain/repository => application/port/out}/PaymentRepository.java (78%) rename payment/src/main/java/com/smore/payment/payment/{domain/repository => application/port/out}/SellerSettlementLedgerRepository.java (87%) rename payment/src/main/java/com/smore/payment/payment/{infrastructure/persistence/redis => domain}/model/TemporaryPayment.java (92%) delete mode 100644 payment/src/main/java/com/smore/payment/payment/domain/repository/RedisRepository.java diff --git a/payment/src/main/java/com/smore/payment/payment/application/ApprovePaymentService.java b/payment/src/main/java/com/smore/payment/payment/application/ApprovePaymentService.java index 533f7d23..21130fbb 100644 --- a/payment/src/main/java/com/smore/payment/payment/application/ApprovePaymentService.java +++ b/payment/src/main/java/com/smore/payment/payment/application/ApprovePaymentService.java @@ -1,31 +1,27 @@ package com.smore.payment.payment.application; -import com.smore.payment.payment.domain.event.PaymentApprovedEvent; -import com.smore.payment.payment.domain.service.SettlementAmountCalculator; -import com.smore.payment.shared.outbox.OutboxMessage; -import com.smore.payment.shared.outbox.OutboxMessageCreator; -import com.smore.payment.payment.application.port.in.ApprovePaymentCommand; -import com.smore.payment.payment.domain.event.SettlementCalculatedEvent; import com.smore.payment.payment.application.facade.FeePolicyFacade; import com.smore.payment.payment.application.facade.dto.FeePolicyResult; +import com.smore.payment.payment.application.port.in.ApprovePaymentCommand; import com.smore.payment.payment.application.port.in.ApprovePaymentResult; import com.smore.payment.payment.application.port.in.ApprovePaymentUseCase; import com.smore.payment.payment.application.port.out.OutboxPort; +import com.smore.payment.payment.application.port.out.PaymentRepository; import com.smore.payment.payment.application.port.out.PgClient; import com.smore.payment.payment.application.port.out.TemporaryPaymentPort; +import com.smore.payment.payment.domain.event.PaymentApprovedEvent; +import com.smore.payment.payment.domain.event.SettlementCalculatedEvent; import com.smore.payment.payment.domain.model.Payment; -//import com.smore.payment.payment.domain.document.PgApproveLog; import com.smore.payment.payment.domain.model.PgResponseResult; -import com.smore.payment.payment.infrastructure.persistence.redis.model.TemporaryPayment; -import com.smore.payment.payment.domain.repository.MongoRepository; -import com.smore.payment.payment.domain.repository.PaymentRepository; +import com.smore.payment.payment.domain.model.TemporaryPayment; +import com.smore.payment.payment.domain.service.SettlementAmountCalculator; +import com.smore.payment.shared.outbox.OutboxMessage; +import com.smore.payment.shared.outbox.OutboxMessageCreator; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.math.BigDecimal; - @Slf4j @Service @RequiredArgsConstructor @@ -36,7 +32,6 @@ public class ApprovePaymentService implements ApprovePaymentUseCase { private final PaymentRepository paymentRepository; private final OutboxPort outboxPort; private final PgClient pgClient; - private final MongoRepository mongoRepository; private final FeePolicyFacade feePolicyFacade; private final OutboxMessageCreator outboxMessageCreator; private final SettlementAmountCalculator settlementAmountCalculator; diff --git a/payment/src/main/java/com/smore/payment/payment/application/CreateTemporaryPaymentService.java b/payment/src/main/java/com/smore/payment/payment/application/CreateTemporaryPaymentService.java index f070742c..d01ea283 100644 --- a/payment/src/main/java/com/smore/payment/payment/application/CreateTemporaryPaymentService.java +++ b/payment/src/main/java/com/smore/payment/payment/application/CreateTemporaryPaymentService.java @@ -2,8 +2,7 @@ import com.smore.payment.payment.application.event.inbound.PaymentRequestedEvent; import com.smore.payment.payment.application.port.out.TemporaryPaymentPort; -import com.smore.payment.payment.infrastructure.persistence.redis.model.TemporaryPayment; -import com.smore.payment.payment.domain.repository.RedisRepository; +import com.smore.payment.payment.domain.model.TemporaryPayment; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; diff --git a/payment/src/main/java/com/smore/payment/payment/application/PaymentRefundService.java b/payment/src/main/java/com/smore/payment/payment/application/PaymentRefundService.java index d1bc3282..61777136 100644 --- a/payment/src/main/java/com/smore/payment/payment/application/PaymentRefundService.java +++ b/payment/src/main/java/com/smore/payment/payment/application/PaymentRefundService.java @@ -16,7 +16,7 @@ import com.smore.payment.payment.application.port.out.PgClient; import com.smore.payment.payment.domain.model.Payment; import com.smore.payment.payment.domain.model.PgResponseResult; -import com.smore.payment.payment.domain.repository.PaymentRepository; +import com.smore.payment.payment.application.port.out.PaymentRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; diff --git a/payment/src/main/java/com/smore/payment/payment/application/PaymentSettlementService.java b/payment/src/main/java/com/smore/payment/payment/application/PaymentSettlementService.java index f18d27a8..5c9404a5 100644 --- a/payment/src/main/java/com/smore/payment/payment/application/PaymentSettlementService.java +++ b/payment/src/main/java/com/smore/payment/payment/application/PaymentSettlementService.java @@ -5,7 +5,7 @@ import com.smore.payment.payment.application.event.outbound.SettlementSuccessEvent; import com.smore.payment.payment.application.port.in.SettlePaymentUseCase; import com.smore.payment.payment.application.port.out.OutboxPort; -import com.smore.payment.payment.domain.repository.SellerSettlementLedgerRepository; +import com.smore.payment.payment.application.port.out.SellerSettlementLedgerRepository; import com.smore.payment.payment.domain.service.SettlementValidationResult; import com.smore.payment.payment.domain.service.SettlementValidationService; import com.smore.payment.shared.outbox.OutboxMessageCreator; diff --git a/payment/src/main/java/com/smore/payment/payment/domain/repository/MongoRepository.java b/payment/src/main/java/com/smore/payment/payment/application/port/out/MongoRepository.java similarity index 58% rename from payment/src/main/java/com/smore/payment/payment/domain/repository/MongoRepository.java rename to payment/src/main/java/com/smore/payment/payment/application/port/out/MongoRepository.java index fa9b967c..9d65c85e 100644 --- a/payment/src/main/java/com/smore/payment/payment/domain/repository/MongoRepository.java +++ b/payment/src/main/java/com/smore/payment/payment/application/port/out/MongoRepository.java @@ -1,9 +1,7 @@ -package com.smore.payment.payment.domain.repository; +package com.smore.payment.payment.application.port.out; //import com.smore.payment.payment.domain.document.PgApproveLog; -import org.springframework.stereotype.Repository; -@Repository public interface MongoRepository { // void savePgApproveLog(PgApproveLog pgApproveLog); diff --git a/payment/src/main/java/com/smore/payment/payment/domain/repository/PaymentRepository.java b/payment/src/main/java/com/smore/payment/payment/application/port/out/PaymentRepository.java similarity index 78% rename from payment/src/main/java/com/smore/payment/payment/domain/repository/PaymentRepository.java rename to payment/src/main/java/com/smore/payment/payment/application/port/out/PaymentRepository.java index 869fc2e1..49dc6b00 100644 --- a/payment/src/main/java/com/smore/payment/payment/domain/repository/PaymentRepository.java +++ b/payment/src/main/java/com/smore/payment/payment/application/port/out/PaymentRepository.java @@ -1,13 +1,11 @@ -package com.smore.payment.payment.domain.repository; +package com.smore.payment.payment.application.port.out; import com.smore.payment.payment.domain.model.Payment; import com.smore.payment.payment.domain.model.PaymentRefund; -import org.springframework.stereotype.Repository; import java.util.Optional; import java.util.UUID; -@Repository public interface PaymentRepository { void save(Payment payment); diff --git a/payment/src/main/java/com/smore/payment/payment/domain/repository/SellerSettlementLedgerRepository.java b/payment/src/main/java/com/smore/payment/payment/application/port/out/SellerSettlementLedgerRepository.java similarity index 87% rename from payment/src/main/java/com/smore/payment/payment/domain/repository/SellerSettlementLedgerRepository.java rename to payment/src/main/java/com/smore/payment/payment/application/port/out/SellerSettlementLedgerRepository.java index d48ce415..abac3df2 100644 --- a/payment/src/main/java/com/smore/payment/payment/domain/repository/SellerSettlementLedgerRepository.java +++ b/payment/src/main/java/com/smore/payment/payment/application/port/out/SellerSettlementLedgerRepository.java @@ -1,4 +1,4 @@ -package com.smore.payment.payment.domain.repository; +package com.smore.payment.payment.application.port.out; import java.math.BigDecimal; import java.util.UUID; diff --git a/payment/src/main/java/com/smore/payment/payment/application/port/out/TemporaryPaymentPort.java b/payment/src/main/java/com/smore/payment/payment/application/port/out/TemporaryPaymentPort.java index 2a499d27..95689995 100644 --- a/payment/src/main/java/com/smore/payment/payment/application/port/out/TemporaryPaymentPort.java +++ b/payment/src/main/java/com/smore/payment/payment/application/port/out/TemporaryPaymentPort.java @@ -1,7 +1,6 @@ package com.smore.payment.payment.application.port.out; -import com.smore.payment.payment.infrastructure.persistence.redis.model.TemporaryPayment; - +import com.smore.payment.payment.domain.model.TemporaryPayment; import java.util.Optional; import java.util.UUID; diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/redis/model/TemporaryPayment.java b/payment/src/main/java/com/smore/payment/payment/domain/model/TemporaryPayment.java similarity index 92% rename from payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/redis/model/TemporaryPayment.java rename to payment/src/main/java/com/smore/payment/payment/domain/model/TemporaryPayment.java index 3f9e9e2a..7366e35e 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/redis/model/TemporaryPayment.java +++ b/payment/src/main/java/com/smore/payment/payment/domain/model/TemporaryPayment.java @@ -1,4 +1,4 @@ -package com.smore.payment.payment.infrastructure.persistence.redis.model; +package com.smore.payment.payment.domain.model; import lombok.AllArgsConstructor; import lombok.Getter; @@ -51,7 +51,6 @@ public void validateApproval(BigDecimal requestAmount) { throw new IllegalArgumentException("요청 금액이 null입니다."); } - // BigDecimal은 equals 대신 compareTo 사용 권장 if (this.amount.compareTo(requestAmount) != 0) { throw new IllegalArgumentException( String.format("결제 금액이 일치하지 않습니다. 예상: %s, 요청: %s", diff --git a/payment/src/main/java/com/smore/payment/payment/domain/repository/RedisRepository.java b/payment/src/main/java/com/smore/payment/payment/domain/repository/RedisRepository.java deleted file mode 100644 index a2d9e814..00000000 --- a/payment/src/main/java/com/smore/payment/payment/domain/repository/RedisRepository.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.smore.payment.payment.domain.repository; - -import com.smore.payment.payment.infrastructure.persistence.redis.model.TemporaryPayment; -import org.springframework.stereotype.Repository; - -import java.util.Optional; -import java.util.UUID; - -@Repository -public interface RedisRepository { - Optional findByOrderId(UUID uuid); - - void deleteByOrderId(UUID orderId); - - void save(TemporaryPayment temp); - - boolean existsByOrderId(UUID orderId); -} diff --git a/payment/src/main/java/com/smore/payment/payment/domain/service/SettlementValidationService.java b/payment/src/main/java/com/smore/payment/payment/domain/service/SettlementValidationService.java index af77cfac..c9b7e8f7 100644 --- a/payment/src/main/java/com/smore/payment/payment/domain/service/SettlementValidationService.java +++ b/payment/src/main/java/com/smore/payment/payment/domain/service/SettlementValidationService.java @@ -1,7 +1,7 @@ package com.smore.payment.payment.domain.service; import com.smore.payment.payment.application.event.inbound.PaymentSettlementRequestEvent; -import com.smore.payment.payment.domain.repository.SellerSettlementLedgerRepository; +import com.smore.payment.payment.application.port.out.SellerSettlementLedgerRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/config/RedisConfig.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/config/RedisConfig.java index 5be4ee60..01563835 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/config/RedisConfig.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/config/RedisConfig.java @@ -3,7 +3,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import com.smore.payment.payment.infrastructure.persistence.redis.model.TemporaryPayment; +import com.smore.payment.payment.domain.model.TemporaryPayment; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/repository/payment/PaymentRepositoryImpl.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/repository/payment/PaymentRepositoryImpl.java index e2dc9a31..66dbfc3b 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/repository/payment/PaymentRepositoryImpl.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/repository/payment/PaymentRepositoryImpl.java @@ -2,7 +2,7 @@ import com.smore.payment.payment.domain.model.Payment; import com.smore.payment.payment.domain.model.PaymentRefund; -import com.smore.payment.payment.domain.repository.PaymentRepository; +import com.smore.payment.payment.application.port.out.PaymentRepository; import com.smore.payment.payment.infrastructure.persistence.jpa.mapper.PaymentMapper; import com.smore.payment.payment.infrastructure.persistence.jpa.model.payment.PaymentEntity; import com.smore.payment.payment.infrastructure.persistence.jpa.model.payment.PaymentRefundJpa; diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/repository/MongoRepositoryImpl.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/repository/MongoRepositoryImpl.java index 50bb4dad..70edd890 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/repository/MongoRepositoryImpl.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/repository/MongoRepositoryImpl.java @@ -1,7 +1,7 @@ package com.smore.payment.payment.infrastructure.persistence.mongo.repository; //import com.smore.payment.payment.domain.document.PgApproveLog; -import com.smore.payment.payment.domain.repository.MongoRepository; +import com.smore.payment.payment.application.port.out.MongoRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/repository/SellerSettlementLedgerRepositoryImpl.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/repository/SellerSettlementLedgerRepositoryImpl.java index 8b09e66e..b3ff287a 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/repository/SellerSettlementLedgerRepositoryImpl.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/repository/SellerSettlementLedgerRepositoryImpl.java @@ -1,6 +1,6 @@ package com.smore.payment.payment.infrastructure.persistence.mongo.repository; -import com.smore.payment.payment.domain.repository.SellerSettlementLedgerRepository; +import com.smore.payment.payment.application.port.out.SellerSettlementLedgerRepository; import com.smore.payment.payment.infrastructure.persistence.mongo.model.SellerSettlementLedger; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/redis/TemporaryPaymentAdapter.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/redis/TemporaryPaymentAdapter.java index 5aeffb5a..9987fe01 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/redis/TemporaryPaymentAdapter.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/redis/TemporaryPaymentAdapter.java @@ -1,7 +1,7 @@ package com.smore.payment.payment.infrastructure.persistence.redis; import com.smore.payment.payment.application.port.out.TemporaryPaymentPort; -import com.smore.payment.payment.infrastructure.persistence.redis.model.TemporaryPayment; +import com.smore.payment.payment.domain.model.TemporaryPayment; import com.smore.payment.payment.domain.repository.RedisRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; From b10a90a12b886639cb0f0838c333479504a9b6d0 Mon Sep 17 00:00:00 2001 From: ooinl77 Date: Wed, 17 Dec 2025 19:06:23 +0900 Subject: [PATCH 05/16] =?UTF-8?q?feat:=20=EB=A9=B1=EB=93=B1=ED=82=A4?= =?UTF-8?q?=EC=97=90=20=EC=9C=A0=EB=8B=88=ED=81=AC=20=EC=86=8D=EC=84=B1=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EB=B0=8F=20flyway=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- payment/build.gradle | 2 + .../jpa/model/payment/PaymentEntity.java | 7 +- .../payment/PaymentRepositoryImpl.java | 11 +- .../mongo/model/SellerSettlementLedger.java | 2 + .../SellerSettlementLedgerRepositoryImpl.java | 7 +- .../persistence/outbox/OutboxAdapter.java | 3 + .../src/main/resources/application-dev.yml | 14 ++- .../db/migration/V1__init_payment_schema.sql | 113 ++++++++++++++++++ 8 files changed, 154 insertions(+), 5 deletions(-) create mode 100644 payment/src/main/resources/db/migration/V1__init_payment_schema.sql diff --git a/payment/build.gradle b/payment/build.gradle index 5f7bc9ea..391bf1cc 100644 --- a/payment/build.gradle +++ b/payment/build.gradle @@ -52,6 +52,8 @@ dependencies { implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.14' implementation 'org.springframework.boot:spring-boot-starter-webflux' + + implementation 'org.flywaydb:flyway-core' } dependencyManagement { diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/model/payment/PaymentEntity.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/model/payment/PaymentEntity.java index 89c1440a..8ecc0b6c 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/model/payment/PaymentEntity.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/model/payment/PaymentEntity.java @@ -13,7 +13,12 @@ import java.util.UUID; @Entity -@Table(name = "payments") +@Table( + name = "payments", + uniqueConstraints = { + @UniqueConstraint(name = "uk_payments_idempotency_key", columnNames = "idempotency_key") + } +) @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter public class PaymentEntity { diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/repository/payment/PaymentRepositoryImpl.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/repository/payment/PaymentRepositoryImpl.java index 66dbfc3b..87906b05 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/repository/payment/PaymentRepositoryImpl.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/repository/payment/PaymentRepositoryImpl.java @@ -7,6 +7,7 @@ import com.smore.payment.payment.infrastructure.persistence.jpa.model.payment.PaymentEntity; import com.smore.payment.payment.infrastructure.persistence.jpa.model.payment.PaymentRefundJpa; import lombok.RequiredArgsConstructor; +import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Repository; import java.util.Optional; @@ -21,8 +22,14 @@ public class PaymentRepositoryImpl implements PaymentRepository { @Override public void save(Payment payment) { - paymentJpaRepository.save(paymentMapper.toEntity(payment)); - } + try { + paymentJpaRepository.save(paymentMapper.toEntity(payment)); + } catch (DataIntegrityViolationException e) { + throw new IllegalStateException( + "이미 처리된 결제 요청입니다. idempotencyKey=" + payment.getIdempotencyKey(), + e + ); + }} @Override public Optional findByIdempotencyKey(UUID idempotencyKey) { diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/model/SellerSettlementLedger.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/model/SellerSettlementLedger.java index 8c4674a4..946030ed 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/model/SellerSettlementLedger.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/model/SellerSettlementLedger.java @@ -3,6 +3,7 @@ import lombok.Builder; import lombok.Getter; import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.index.Indexed; import org.springframework.data.mongodb.core.mapping.Document; import java.math.BigDecimal; @@ -25,6 +26,7 @@ public class SellerSettlementLedger { private UUID paymentId; // 결제 기반 발생이면 기록 (nullable) + @Indexed(unique = true) private UUID idempotencyKey; // 중복 요청 방지용 key private LocalDateTime timestamp; // 기록 시간 diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/repository/SellerSettlementLedgerRepositoryImpl.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/repository/SellerSettlementLedgerRepositoryImpl.java index b3ff287a..a73fdf29 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/repository/SellerSettlementLedgerRepositoryImpl.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/repository/SellerSettlementLedgerRepositoryImpl.java @@ -3,6 +3,7 @@ import com.smore.payment.payment.application.port.out.SellerSettlementLedgerRepository; import com.smore.payment.payment.infrastructure.persistence.mongo.model.SellerSettlementLedger; import lombok.RequiredArgsConstructor; +import org.springframework.dao.DuplicateKeyException; import org.springframework.stereotype.Repository; import java.math.BigDecimal; @@ -43,6 +44,10 @@ public void saveLedger( .timestamp(LocalDateTime.now()) .build(); - sellerSettlementLedgerMongoRepository.save(ledger); + try { + sellerSettlementLedgerMongoRepository.save(ledger); + } catch (DuplicateKeyException e) { + // 동일한 idempotencyKey로 이미 기록된 경우 - 멱등성을 보장하기 위해 무시 + } } } diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/outbox/OutboxAdapter.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/outbox/OutboxAdapter.java index 9f4a8513..4ddcf2a7 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/outbox/OutboxAdapter.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/outbox/OutboxAdapter.java @@ -6,6 +6,8 @@ import com.smore.payment.payment.infrastructure.persistence.jpa.repository.outbox.OutboxJpaRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; @Component @RequiredArgsConstructor @@ -15,6 +17,7 @@ public class OutboxAdapter implements OutboxPort { private final OutboxMapper outboxMapper; @Override + @Transactional(propagation = Propagation.MANDATORY) public void save(OutboxMessage outboxMessage) { outboxJpaRepository.save(outboxMapper.toEntity(outboxMessage)); } diff --git a/payment/src/main/resources/application-dev.yml b/payment/src/main/resources/application-dev.yml index 375a5c38..9042053a 100644 --- a/payment/src/main/resources/application-dev.yml +++ b/payment/src/main/resources/application-dev.yml @@ -6,6 +6,7 @@ spring: activate: on-profile: dev import: "optional:configserver:" + cloud: config: discovery: @@ -16,6 +17,7 @@ spring: max-attempts: 6 initial-interval: 1000 multiplier: 1.5 + datasource: url: jdbc:postgresql://postgresql:5432/payment username: root @@ -26,6 +28,7 @@ spring: minimum-idle: 2 idle-timeout: 30000 pool-name: HikariPool + data: mongodb: uri: mongodb://mongo_user:mongo_pass@mongo:27017/my_mongo_db?authSource=admin @@ -34,15 +37,24 @@ spring: host: redis-stack port: 6379 + flyway: + enabled: true + locations: classpath:db/migration + baseline-on-migrate: true + + sql: + init: + mode: always jpa: database-platform: org.hibernate.dialect.PostgreSQLDialect hibernate: - ddl-auto: create-drop + ddl-auto: validate show-sql: true properties: hibernate: format_sql: true + kafka: # 로컬에서 Docker로 띄운 브로커 외부 포트에 맞춰 설정 (docker-compose의 EXTERNAL 매핑) bootstrap-servers: kafka-1:9092,kafka-2:9092,kafka-3:9092 diff --git a/payment/src/main/resources/db/migration/V1__init_payment_schema.sql b/payment/src/main/resources/db/migration/V1__init_payment_schema.sql new file mode 100644 index 00000000..f603c977 --- /dev/null +++ b/payment/src/main/resources/db/migration/V1__init_payment_schema.sql @@ -0,0 +1,113 @@ +CREATE TABLE fee_policies +( + id UUID PRIMARY KEY, + target_type VARCHAR(50) NOT NULL, + target_key VARCHAR(255) NOT NULL, + fee_type VARCHAR(50) NOT NULL, + rate NUMERIC(10, 4), + fixed_amount NUMERIC(19, 2), + active BOOLEAN NOT NULL, + create_by BIGINT, + created_at TIMESTAMP, + updated_by BIGINT, + updated_at TIMESTAMP, + deleted_by BIGINT, + deleted_at TIMESTAMP +); + +CREATE TABLE cancel_policies +( + id UUID PRIMARY KEY, + cancel_target_type VARCHAR(50) NOT NULL, + target_key VARCHAR(255) NOT NULL, + cancel_limit_minutes BIGINT, + cancel_fee_type VARCHAR(50) NOT NULL, + rate NUMERIC(10, 4), + fixed_amount NUMERIC(19, 2), + cancellable BOOLEAN NOT NULL, + active BOOLEAN NOT NULL, + create_by BIGINT, + created_at TIMESTAMP, + updated_by BIGINT, + updated_at TIMESTAMP, + deleted_by BIGINT, + deleted_at TIMESTAMP +); + +CREATE TABLE refund_policies +( + id UUID PRIMARY KEY, + refund_target_type VARCHAR(50) NOT NULL, + target_key VARCHAR(255) NOT NULL, + refund_period_days BIGINT, + refund_fee_type VARCHAR(50) NOT NULL, + rate NUMERIC(10, 4), + fixed_amount NUMERIC(19, 2), + refundable BOOLEAN NOT NULL, + active BOOLEAN NOT NULL, + create_by BIGINT, + created_at TIMESTAMP, + updated_by BIGINT, + updated_at TIMESTAMP, + deleted_by BIGINT, + deleted_at TIMESTAMP +); + +CREATE TABLE payments +( + id UUID PRIMARY KEY, + idempotency_key UUID NOT NULL, + user_id BIGINT NOT NULL, + order_id UUID NOT NULL, + amount NUMERIC(19, 2) NOT NULL, + payment_method VARCHAR(50), + status VARCHAR(50), + approved_at TIMESTAMP, + card_issuer_code VARCHAR(50), + card_acquirer_code VARCHAR(50), + card_number VARCHAR(50), + installment_months INTEGER, + interest_free BOOLEAN, + approve_no VARCHAR(50), + card_type VARCHAR(50), + card_owner_type VARCHAR(50), + card_acquire_status VARCHAR(50), + card_amount NUMERIC(19, 2), + failure_reason VARCHAR(255), + failed_at TIMESTAMP, + cancel_reason VARCHAR(255), + cancel_amount NUMERIC(19, 2), + cancelled_at TIMESTAMP, + refund_reason VARCHAR(255), + refund_amount NUMERIC(19, 2), + refunded_at TIMESTAMP, + pg_cancel_transaction_key VARCHAR(255), + refundable_amount NUMERIC(19, 2), + pg_provider VARCHAR(100), + payment_key VARCHAR(100), + pg_order_id VARCHAR(100), + pg_order_name VARCHAR(255), + pg_transaction_key VARCHAR(100), + pg_status VARCHAR(100), + currency VARCHAR(10), + pg_total_amount NUMERIC(19, 2), + pg_balance_amount NUMERIC(19, 2), + seller_id BIGINT NOT NULL, + category UUID NOT NULL, + auction_type VARCHAR(50), + CONSTRAINT uk_payments_idempotency_key UNIQUE (idempotency_key) +); + +CREATE TABLE outboxes +( + id BIGSERIAL PRIMARY KEY, + aggregate_type VARCHAR(100), + aggregate_id UUID, + event_type VARCHAR(100), + idempotency_key UUID, + topic_name VARCHAR(255), + payload TEXT, + retry_count INTEGER, + status VARCHAR(50), + created_at TIMESTAMP +); \ No newline at end of file From d22e581f8484d48b25d071abbf6cb02b5cc69943 Mon Sep 17 00:00:00 2001 From: ooinl77 Date: Wed, 17 Dec 2025 21:16:14 +0900 Subject: [PATCH 06/16] =?UTF-8?q?refactor:=20=EA=B2=B0=EC=A0=9C=20?= =?UTF-8?q?=EC=8A=B9=EC=9D=B8=20=ED=8A=B8=EB=9E=9C=EC=9E=AD=EC=85=98=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/ApprovePaymentService.java | 94 +++++-------------- .../application/PaymentFinalizeService.java | 65 +++++++++++++ .../redis/TemporaryPaymentAdapter.java | 1 - 3 files changed, 86 insertions(+), 74 deletions(-) create mode 100644 payment/src/main/java/com/smore/payment/payment/application/PaymentFinalizeService.java diff --git a/payment/src/main/java/com/smore/payment/payment/application/ApprovePaymentService.java b/payment/src/main/java/com/smore/payment/payment/application/ApprovePaymentService.java index 21130fbb..8fa5d621 100644 --- a/payment/src/main/java/com/smore/payment/payment/application/ApprovePaymentService.java +++ b/payment/src/main/java/com/smore/payment/payment/application/ApprovePaymentService.java @@ -1,106 +1,38 @@ package com.smore.payment.payment.application; -import com.smore.payment.payment.application.facade.FeePolicyFacade; -import com.smore.payment.payment.application.facade.dto.FeePolicyResult; import com.smore.payment.payment.application.port.in.ApprovePaymentCommand; import com.smore.payment.payment.application.port.in.ApprovePaymentResult; import com.smore.payment.payment.application.port.in.ApprovePaymentUseCase; -import com.smore.payment.payment.application.port.out.OutboxPort; import com.smore.payment.payment.application.port.out.PaymentRepository; import com.smore.payment.payment.application.port.out.PgClient; import com.smore.payment.payment.application.port.out.TemporaryPaymentPort; -import com.smore.payment.payment.domain.event.PaymentApprovedEvent; -import com.smore.payment.payment.domain.event.SettlementCalculatedEvent; import com.smore.payment.payment.domain.model.Payment; import com.smore.payment.payment.domain.model.PgResponseResult; import com.smore.payment.payment.domain.model.TemporaryPayment; -import com.smore.payment.payment.domain.service.SettlementAmountCalculator; -import com.smore.payment.shared.outbox.OutboxMessage; -import com.smore.payment.shared.outbox.OutboxMessageCreator; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; @Slf4j @Service @RequiredArgsConstructor -@Transactional public class ApprovePaymentService implements ApprovePaymentUseCase { private final TemporaryPaymentPort temporaryPaymentPort; private final PaymentRepository paymentRepository; - private final OutboxPort outboxPort; private final PgClient pgClient; - private final FeePolicyFacade feePolicyFacade; - private final OutboxMessageCreator outboxMessageCreator; - private final SettlementAmountCalculator settlementAmountCalculator; + private final PaymentFinalizeService paymentFinalizeService; + @Override public ApprovePaymentResult approve(ApprovePaymentCommand command) { TemporaryPayment temp = temporaryPaymentPort.findByOrderId(command.orderId()) - .orElseThrow(() -> new IllegalStateException("임시 결제를 찾을 수 없습니다.")); - - temp.validateApproval(command.amount()); - - paymentRepository.findByIdempotencyKey(temp.getIdempotencyKey()) - .ifPresent(existing -> { - throw new IllegalStateException("이미 결제가 승인되었습니다."); - }); - - PgResponseResult result; - - try { - result = pgClient.approve( - command.paymentKey(), - command.pgOrderId(), - command.amount() - ); - - // Todo: 로그 document 추가 -// PgApproveLog log = PgApproveLog.builder().build(); -// mongoRepository.savePgApproveLog(log); - - } catch (Exception e) { - -// PgApproveLog log = PgApproveLog.builder().build(); -// mongoRepository.savePgApproveLog(log); - - // Todo: 예외 반환 - - throw e; - } - - Payment payment = Payment.createFinalPayment( - temp.getIdempotencyKey(), - temp.getUserId(), - temp.getOrderId(), - temp.getAmount(), - temp.getSellerId(), - temp.getCategoryId(), - temp.getAuctionType(), - result - ); - - paymentRepository.save(payment); - - FeePolicyResult policy = feePolicyFacade.findApplicablePolicy( - payment.getSellerId(), - payment.getCategoryId() - ); - - PaymentApprovedEvent event = payment.createApprovedEvent(); - SettlementCalculatedEvent settlementEvent = payment.createSettlementCalculatedEvent( settlementAmountCalculator.calculate(payment.getAmount(), policy)); - - OutboxMessage paymentApprovedMsg = outboxMessageCreator.paymentApproved(event); - OutboxMessage settlementCalculatedMsg = outboxMessageCreator.settlementCalculated(settlementEvent); - - outboxPort.save(paymentApprovedMsg); + .orElseThrow(); - outboxPort.save(settlementCalculatedMsg); + PgResponseResult pgResult = approveWithPg(command, temp); - temporaryPaymentPort.deleteByOrderId(temp.getOrderId()); + Payment payment = paymentFinalizeService.finalizePayment(temp, pgResult); return new ApprovePaymentResult( payment.getId(), @@ -111,6 +43,22 @@ public ApprovePaymentResult approve(ApprovePaymentCommand command) { ); } + public PgResponseResult approveWithPg( + ApprovePaymentCommand command, + TemporaryPayment temp + ) { + temp.validateApproval(command.amount()); + + paymentRepository.findByIdempotencyKey(temp.getIdempotencyKey()) + .ifPresent(p -> { throw new IllegalStateException("이미 승인"); }); + + return pgClient.approve( + command.paymentKey(), + command.pgOrderId(), + command.amount() + ); + } + } diff --git a/payment/src/main/java/com/smore/payment/payment/application/PaymentFinalizeService.java b/payment/src/main/java/com/smore/payment/payment/application/PaymentFinalizeService.java new file mode 100644 index 00000000..975432d6 --- /dev/null +++ b/payment/src/main/java/com/smore/payment/payment/application/PaymentFinalizeService.java @@ -0,0 +1,65 @@ +package com.smore.payment.payment.application; + +import com.smore.payment.payment.application.facade.FeePolicyFacade; +import com.smore.payment.payment.application.facade.dto.FeePolicyResult; +import com.smore.payment.payment.application.port.out.OutboxPort; +import com.smore.payment.payment.application.port.out.PaymentRepository; +import com.smore.payment.payment.application.port.out.TemporaryPaymentPort; +import com.smore.payment.payment.domain.event.SettlementCalculatedEvent; +import com.smore.payment.payment.domain.model.Payment; +import com.smore.payment.payment.domain.model.PgResponseResult; +import com.smore.payment.payment.domain.model.TemporaryPayment; +import com.smore.payment.payment.domain.service.SettlementAmountCalculator; +import com.smore.payment.shared.outbox.OutboxMessageCreator; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class PaymentFinalizeService { + + private final FeePolicyFacade feePolicyFacade; + private final OutboxMessageCreator outboxMessageCreator; + private final SettlementAmountCalculator settlementAmountCalculator; + private final OutboxPort outboxPort; + private final PaymentRepository paymentRepository; + private final TemporaryPaymentPort temporaryPaymentPort; + + @Transactional + public Payment finalizePayment( + TemporaryPayment temp, + PgResponseResult result + ) { + + Payment payment = Payment.createFinalPayment( + temp.getIdempotencyKey(), + temp.getUserId(), + temp.getOrderId(), + temp.getAmount(), + temp.getSellerId(), + temp.getCategoryId(), + temp.getAuctionType(), + result + ); + + paymentRepository.save(payment); + + FeePolicyResult policy = feePolicyFacade.findApplicablePolicy( + payment.getSellerId(), + payment.getCategoryId() + ); + + SettlementCalculatedEvent settlementEvent = + payment.createSettlementCalculatedEvent( + settlementAmountCalculator.calculate(payment.getAmount(), policy) + ); + + outboxPort.save(outboxMessageCreator.paymentApproved(payment.createApprovedEvent())); + outboxPort.save(outboxMessageCreator.settlementCalculated(settlementEvent)); + + temporaryPaymentPort.deleteByOrderId(temp.getOrderId()); + + return payment; + } +} diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/redis/TemporaryPaymentAdapter.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/redis/TemporaryPaymentAdapter.java index 9987fe01..d493e005 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/redis/TemporaryPaymentAdapter.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/redis/TemporaryPaymentAdapter.java @@ -2,7 +2,6 @@ import com.smore.payment.payment.application.port.out.TemporaryPaymentPort; import com.smore.payment.payment.domain.model.TemporaryPayment; -import com.smore.payment.payment.domain.repository.RedisRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.RedisTemplate; From 327e473d6d1c872a3471b837a3f64cc41ad59a07 Mon Sep 17 00:00:00 2001 From: ooinl77 Date: Thu, 18 Dec 2025 13:44:38 +0900 Subject: [PATCH 07/16] =?UTF-8?q?feat:=20mongo=20log=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=20refactor:=20refund=20=ED=8A=B8=EB=9E=9C=EC=9E=AD=EC=85=98=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- payment/build.gradle | 3 +- .../application/ApprovePaymentService.java | 65 +++-- .../CreateTemporaryPaymentService.java | 2 + .../application/PaymentAuditLogService.java | 268 ++++++++++++++++++ .../application/PaymentRefundService.java | 46 +-- .../application/RefundFinalizeService.java | 50 ++++ .../application/port/out/MongoRepository.java | 8 - .../port/out/PaymentAuditLogPort.java | 8 + .../payment/domain/document/PgApproveLog.java | 18 -- .../payment/payment/domain/model/Payment.java | 3 +- .../mongo/model/PaymentAuditEventType.java | 17 ++ .../mongo/model/PaymentAuditLog.java | 55 ++++ .../mongo/repository/MongoRepositoryImpl.java | 17 -- .../repository/PaymentAuditLogRepository.java | 7 + .../PaymentAuditLogRepositoryAdapter.java | 18 ++ .../repository/PaymentMongoRepository.java | 5 - 16 files changed, 495 insertions(+), 95 deletions(-) create mode 100644 payment/src/main/java/com/smore/payment/payment/application/PaymentAuditLogService.java create mode 100644 payment/src/main/java/com/smore/payment/payment/application/RefundFinalizeService.java delete mode 100644 payment/src/main/java/com/smore/payment/payment/application/port/out/MongoRepository.java create mode 100644 payment/src/main/java/com/smore/payment/payment/application/port/out/PaymentAuditLogPort.java delete mode 100644 payment/src/main/java/com/smore/payment/payment/domain/document/PgApproveLog.java create mode 100644 payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/model/PaymentAuditEventType.java create mode 100644 payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/model/PaymentAuditLog.java delete mode 100644 payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/repository/MongoRepositoryImpl.java create mode 100644 payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/repository/PaymentAuditLogRepository.java create mode 100644 payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/repository/PaymentAuditLogRepositoryAdapter.java delete mode 100644 payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/repository/PaymentMongoRepository.java diff --git a/payment/build.gradle b/payment/build.gradle index 391bf1cc..4f23bea6 100644 --- a/payment/build.gradle +++ b/payment/build.gradle @@ -29,7 +29,6 @@ ext { } dependencies { - implementation project(":common") implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-web' @@ -42,6 +41,8 @@ dependencies { compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' + implementation project(":common") + runtimeOnly 'org.postgresql:postgresql' implementation 'org.springframework.boot:spring-boot-starter-data-mongodb' implementation 'org.springframework.boot:spring-boot-starter-data-redis' diff --git a/payment/src/main/java/com/smore/payment/payment/application/ApprovePaymentService.java b/payment/src/main/java/com/smore/payment/payment/application/ApprovePaymentService.java index 8fa5d621..14e6a260 100644 --- a/payment/src/main/java/com/smore/payment/payment/application/ApprovePaymentService.java +++ b/payment/src/main/java/com/smore/payment/payment/application/ApprovePaymentService.java @@ -12,6 +12,8 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; @Slf4j @Service @@ -22,43 +24,58 @@ public class ApprovePaymentService implements ApprovePaymentUseCase { private final PaymentRepository paymentRepository; private final PgClient pgClient; private final PaymentFinalizeService paymentFinalizeService; - + private final PaymentAuditLogService paymentAuditLogService; @Override + @Transactional(propagation = Propagation.NOT_SUPPORTED) public ApprovePaymentResult approve(ApprovePaymentCommand command) { TemporaryPayment temp = temporaryPaymentPort.findByOrderId(command.orderId()) .orElseThrow(); - PgResponseResult pgResult = approveWithPg(command, temp); - - Payment payment = paymentFinalizeService.finalizePayment(temp, pgResult); - - return new ApprovePaymentResult( - payment.getId(), - payment.getOrderId(), - payment.getAmount(), - payment.getApprovedAt(), - payment.getStatus().name() - ); - } - - public PgResponseResult approveWithPg( - ApprovePaymentCommand command, - TemporaryPayment temp - ) { + paymentAuditLogService.logApprovalRequested(command, temp); temp.validateApproval(command.amount()); paymentRepository.findByIdempotencyKey(temp.getIdempotencyKey()) .ifPresent(p -> { throw new IllegalStateException("이미 승인"); }); - return pgClient.approve( - command.paymentKey(), - command.pgOrderId(), - command.amount() - ); + PgResponseResult pgResult; + + try { + + paymentAuditLogService.logPgApprovalRequested(command, temp); + + pgResult = pgClient.approve( + command.paymentKey(), + command.pgOrderId(), + command.amount() + ); + + paymentAuditLogService.logPgApprovalSucceeded(temp, pgResult); + + } catch (RuntimeException e) { + paymentAuditLogService.logPgApprovalFailed(temp, e.getMessage()); + throw e; + } + + try { + Payment payment = paymentFinalizeService.finalizePayment(temp, pgResult); + paymentAuditLogService.logPaymentApprovalSucceeded(payment); + + return new ApprovePaymentResult( + payment.getId(), + payment.getOrderId(), + payment.getAmount(), + payment.getApprovedAt(), + payment.getStatus().name() + ); + } catch (RuntimeException e) { + paymentAuditLogService.logPaymentApprovalFailed(temp, e.getMessage()); + throw e; + } } - + + } diff --git a/payment/src/main/java/com/smore/payment/payment/application/CreateTemporaryPaymentService.java b/payment/src/main/java/com/smore/payment/payment/application/CreateTemporaryPaymentService.java index d01ea283..16eb0d11 100644 --- a/payment/src/main/java/com/smore/payment/payment/application/CreateTemporaryPaymentService.java +++ b/payment/src/main/java/com/smore/payment/payment/application/CreateTemporaryPaymentService.java @@ -12,6 +12,7 @@ public class CreateTemporaryPaymentService { private final TemporaryPaymentPort temporaryPaymentPort; + private final PaymentAuditLogService paymentAuditLogService; @Transactional public void create(PaymentRequestedEvent paymentRequestedEvent) { @@ -32,5 +33,6 @@ public void create(PaymentRequestedEvent paymentRequestedEvent) { ); temporaryPaymentPort.save(temp); + paymentAuditLogService.logTemporaryPaymentCreated(paymentRequestedEvent); } } diff --git a/payment/src/main/java/com/smore/payment/payment/application/PaymentAuditLogService.java b/payment/src/main/java/com/smore/payment/payment/application/PaymentAuditLogService.java new file mode 100644 index 00000000..05a4fbbd --- /dev/null +++ b/payment/src/main/java/com/smore/payment/payment/application/PaymentAuditLogService.java @@ -0,0 +1,268 @@ +package com.smore.payment.payment.application; + +import com.smore.payment.payment.application.event.inbound.PaymentRefundEvent; +import com.smore.payment.payment.application.event.inbound.PaymentRequestedEvent; +import com.smore.payment.payment.application.port.in.ApprovePaymentCommand; +import com.smore.payment.payment.application.port.out.PaymentAuditLogPort; +import com.smore.payment.payment.domain.model.Payment; +import com.smore.payment.payment.domain.model.PgResponseResult; +import com.smore.payment.payment.domain.model.TemporaryPayment; +import com.smore.payment.payment.infrastructure.persistence.mongo.model.PaymentAuditEventType; +import com.smore.payment.payment.infrastructure.persistence.mongo.model.PaymentAuditLog; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Service +@RequiredArgsConstructor +public class PaymentAuditLogService { + + private final PaymentAuditLogPort paymentAuditLogPort; + + public void logTemporaryPaymentCreated(PaymentRequestedEvent event) { + paymentAuditLogPort.save( + PaymentAuditLog.builder() + .eventType(PaymentAuditEventType.TEMPORARY_PAYMENT_CREATED) + .orderId(event.orderId()) + .userId(event.userId()) + .sellerId(event.sellerId()) + .categoryId(event.categoryId()) + .auctionType(event.auctionType()) + .amount(event.amount()) + .idempotencyKey(event.idempotencyKey()) + .occurredAt(LocalDateTime.now()) + .description("임시 결제 생성") + .build() + ); + } + + public void logApprovalRequested(ApprovePaymentCommand command, TemporaryPayment temp) { + paymentAuditLogPort.save( + PaymentAuditLog.builder() + .eventType(PaymentAuditEventType.PAYMENT_APPROVAL_REQUESTED) + .orderId(command.orderId()) + .paymentKey(command.paymentKey()) + .pgOrderId(command.pgOrderId()) + .userId(temp.getUserId()) + .sellerId(temp.getSellerId()) + .categoryId(temp.getCategoryId()) + .auctionType(temp.getAuctionType()) + .amount(command.amount()) + .idempotencyKey(temp.getIdempotencyKey()) + .occurredAt(LocalDateTime.now()) + .description("결제 승인 요청") + .build() + ); + } + + public void logPgApprovalRequested(ApprovePaymentCommand command, TemporaryPayment temp) { + paymentAuditLogPort.save( + PaymentAuditLog.builder() + .eventType(PaymentAuditEventType.PG_APPROVAL_REQUESTED) + .orderId(command.orderId()) + .paymentKey(command.paymentKey()) + .pgOrderId(command.pgOrderId()) + .userId(temp.getUserId()) + .sellerId(temp.getSellerId()) + .categoryId(temp.getCategoryId()) + .auctionType(temp.getAuctionType()) + .amount(command.amount()) + .idempotencyKey(temp.getIdempotencyKey()) + .occurredAt(LocalDateTime.now()) + .description("PG 승인 요청") + .build() + ); + } + + public void logPgApprovalSucceeded(TemporaryPayment temp, PgResponseResult result) { + paymentAuditLogPort.save( + PaymentAuditLog.builder() + .eventType(PaymentAuditEventType.PG_APPROVAL_SUCCEEDED) + .orderId(temp.getOrderId()) + .paymentKey(result.paymentKey()) + .pgOrderId(result.pgOrderId()) + .userId(temp.getUserId()) + .sellerId(temp.getSellerId()) + .categoryId(temp.getCategoryId()) + .auctionType(temp.getAuctionType()) + .amount(result.totalAmount()) + .idempotencyKey(temp.getIdempotencyKey()) + .occurredAt(result.approvedAt()) + .description("PG 승인 완료") + .build() + ); + } + + public void logPgApprovalFailed(TemporaryPayment temp, String reason) { + paymentAuditLogPort.save( + PaymentAuditLog.builder() + .eventType(PaymentAuditEventType.PG_APPROVAL_FAILED) + .orderId(temp.getOrderId()) + .userId(temp.getUserId()) + .sellerId(temp.getSellerId()) + .categoryId(temp.getCategoryId()) + .auctionType(temp.getAuctionType()) + .amount(temp.getAmount()) + .idempotencyKey(temp.getIdempotencyKey()) + .occurredAt(LocalDateTime.now()) + .description(reason) + .build() + ); + } + + public void logPaymentApprovalSucceeded(Payment payment) { + paymentAuditLogPort.save( + PaymentAuditLog.builder() + .eventType(PaymentAuditEventType.PAYMENT_APPROVAL_SUCCEEDED) + .orderId(payment.getOrderId()) + .paymentId(payment.getId()) + .paymentKey(payment.getPaymentKey()) + .pgOrderId(payment.getPgOrderId()) + .userId(payment.getUserId()) + .sellerId(payment.getSellerId()) + .categoryId(payment.getCategoryId()) + .auctionType(payment.getAuctionType()) + .amount(payment.getAmount()) + .idempotencyKey(payment.getIdempotencyKey()) + .occurredAt(payment.getApprovedAt()) + .description("결제 승인 완료") + .build() + ); + } + + public void logPaymentApprovalFailed(TemporaryPayment temp, String reason) { + paymentAuditLogPort.save( + PaymentAuditLog.builder() + .eventType(PaymentAuditEventType.PAYMENT_APPROVAL_FAILED) + .orderId(temp.getOrderId()) + .userId(temp.getUserId()) + .sellerId(temp.getSellerId()) + .categoryId(temp.getCategoryId()) + .auctionType(temp.getAuctionType()) + .amount(temp.getAmount()) + .idempotencyKey(temp.getIdempotencyKey()) + .occurredAt(LocalDateTime.now()) + .description(reason) + .build() + ); + } + + public void logRefundRequested(PaymentRefundEvent event) { + paymentAuditLogPort.save( + PaymentAuditLog.builder() + .eventType(PaymentAuditEventType.PAYMENT_REFUND_REQUESTED) + .orderId(event.orderId()) + .paymentId(event.paymentId()) + .refundId(event.refundId()) + .userId(event.userId()) + .amount(event.refundAmount()) + .idempotencyKey(event.idempotencyKey()) + .occurredAt(event.publishedAt()) + .description(event.refundReason()) + .build() + ); + } + + public void logPgRefundRequested(Payment payment, PaymentRefundEvent event, BigDecimal refundAmount) { + paymentAuditLogPort.save( + PaymentAuditLog.builder() + .eventType(PaymentAuditEventType.PG_REFUND_REQUESTED) + .orderId(payment.getOrderId()) + .paymentId(payment.getId()) + .refundId(event.refundId()) + .userId(payment.getUserId()) + .sellerId(payment.getSellerId()) + .categoryId(payment.getCategoryId()) + .auctionType(payment.getAuctionType()) + .amount(refundAmount) + .paymentKey(payment.getPaymentKey()) + .pgOrderId(payment.getPgOrderId()) + .idempotencyKey(payment.getIdempotencyKey()) + .occurredAt(LocalDateTime.now()) + .description(event.refundReason()) + .build() + ); + } + + public void logPgRefundSucceeded(Payment payment, PaymentRefundEvent event, PgResponseResult result) { + paymentAuditLogPort.save( + PaymentAuditLog.builder() + .eventType(PaymentAuditEventType.PG_REFUND_SUCCEEDED) + .orderId(payment.getOrderId()) + .paymentId(payment.getId()) + .refundId(event.refundId()) + .userId(payment.getUserId()) + .sellerId(payment.getSellerId()) + .categoryId(payment.getCategoryId()) + .auctionType(payment.getAuctionType()) + .amount(result.cancels().cancelAmount()) + .paymentKey(payment.getPaymentKey()) + .pgOrderId(payment.getPgOrderId()) + .idempotencyKey(payment.getIdempotencyKey()) + .occurredAt(result.cancels().canceledAt()) + .description(result.cancels().cancelReason()) + .build() + ); + } + + public void logPgRefundFailed(Payment payment, PaymentRefundEvent event, String reason) { + paymentAuditLogPort.save( + PaymentAuditLog.builder() + .eventType(PaymentAuditEventType.PG_REFUND_FAILED) + .orderId(payment.getOrderId()) + .paymentId(payment.getId()) + .refundId(event.refundId()) + .userId(payment.getUserId()) + .sellerId(payment.getSellerId()) + .categoryId(payment.getCategoryId()) + .auctionType(payment.getAuctionType()) + .amount(event.refundAmount()) + .paymentKey(payment.getPaymentKey()) + .pgOrderId(payment.getPgOrderId()) + .idempotencyKey(payment.getIdempotencyKey()) + .occurredAt(LocalDateTime.now()) + .description(reason) + .build() + ); + } + + public void logRefundSucceeded(Payment payment, PaymentRefundEvent event) { + paymentAuditLogPort.save( + PaymentAuditLog.builder() + .eventType(PaymentAuditEventType.PAYMENT_REFUND_SUCCEEDED) + .orderId(payment.getOrderId()) + .paymentId(payment.getId()) + .refundId(event.refundId()) + .userId(payment.getUserId()) + .sellerId(payment.getSellerId()) + .categoryId(payment.getCategoryId()) + .auctionType(payment.getAuctionType()) + .amount(event.refundAmount()) + .idempotencyKey(payment.getIdempotencyKey()) + .occurredAt(LocalDateTime.now()) + .description("결제 환불 완료") + .build() + ); + } + + public void logRefundFailed(Payment payment, PaymentRefundEvent event, String reason) { + paymentAuditLogPort.save( + PaymentAuditLog.builder() + .eventType(PaymentAuditEventType.PAYMENT_REFUND_FAILED) + .orderId(payment.getOrderId()) + .paymentId(payment.getId()) + .refundId(event.refundId()) + .userId(payment.getUserId()) + .sellerId(payment.getSellerId()) + .categoryId(payment.getCategoryId()) + .auctionType(payment.getAuctionType()) + .amount(event.refundAmount()) + .idempotencyKey(payment.getIdempotencyKey()) + .occurredAt(LocalDateTime.now()) + .description(reason) + .build() + ); + } +} \ No newline at end of file diff --git a/payment/src/main/java/com/smore/payment/payment/application/PaymentRefundService.java b/payment/src/main/java/com/smore/payment/payment/application/PaymentRefundService.java index 61777136..8df0841e 100644 --- a/payment/src/main/java/com/smore/payment/payment/application/PaymentRefundService.java +++ b/payment/src/main/java/com/smore/payment/payment/application/PaymentRefundService.java @@ -1,11 +1,5 @@ package com.smore.payment.payment.application; -import com.smore.payment.payment.application.port.in.RefundPaymentUseCase; -import com.smore.payment.payment.application.port.out.OutboxPort; -import com.smore.payment.payment.domain.service.RefundCalculator; -import com.smore.payment.payment.domain.service.RefundDecision; -import com.smore.payment.shared.outbox.OutboxMessage; -import com.smore.payment.shared.outbox.OutboxMessageCreator; import com.smore.payment.payment.application.event.inbound.PaymentRefundEvent; import com.smore.payment.payment.application.event.outbound.PaymentRefundFailedEvent; import com.smore.payment.payment.application.event.outbound.PaymentRefundSucceededEvent; @@ -13,19 +7,25 @@ import com.smore.payment.payment.application.facade.RefundPolicyFacade; import com.smore.payment.payment.application.facade.dto.CancelPolicyResult; import com.smore.payment.payment.application.facade.dto.RefundPolicyResult; +import com.smore.payment.payment.application.port.in.RefundPaymentUseCase; +import com.smore.payment.payment.application.port.out.OutboxPort; +import com.smore.payment.payment.application.port.out.PaymentRepository; import com.smore.payment.payment.application.port.out.PgClient; import com.smore.payment.payment.domain.model.Payment; import com.smore.payment.payment.domain.model.PgResponseResult; -import com.smore.payment.payment.application.port.out.PaymentRepository; +import com.smore.payment.payment.domain.service.RefundCalculator; +import com.smore.payment.payment.domain.service.RefundDecision; +import com.smore.payment.shared.outbox.OutboxMessage; +import com.smore.payment.shared.outbox.OutboxMessageCreator; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; @Service @RequiredArgsConstructor -@Transactional public class PaymentRefundService implements RefundPaymentUseCase { private final PaymentRepository paymentRepository; @@ -35,10 +35,15 @@ public class PaymentRefundService implements RefundPaymentUseCase { private final OutboxMessageCreator outboxCreator; private final OutboxPort outboxPort; private final RefundCalculator refundCalculator; + private final PaymentAuditLogService paymentAuditLogService; + private final RefundFinalizeService refundFinalizeService; @Override + @Transactional(propagation = Propagation.NOT_SUPPORTED) public void refund(PaymentRefundEvent event) { + paymentAuditLogService.logRefundRequested(event); + Payment payment = paymentRepository.findById(event.paymentId()) .orElseThrow(() -> new IllegalArgumentException("결제 정보를 찾을 수 없습니다. paymentId=" + event.paymentId())); @@ -57,6 +62,8 @@ public void refund(PaymentRefundEvent event) { RefundDecision refundDecision = refundCalculator.decide(payment, event, cancelResult, refundResult); if (!refundDecision.refundable()) { + paymentAuditLogService.logRefundFailed(payment, event, refundDecision.failureReason()); + OutboxMessage failedMsg = outboxCreator.paymentRefundFailed( PaymentRefundFailedEvent.of(event.orderId(), event.refundId(), event.refundAmount(), refundDecision.failureReason()) ); @@ -69,14 +76,21 @@ public void refund(PaymentRefundEvent event) { PgResponseResult pgRefundResult; try { + + paymentAuditLogService.logPgRefundRequested(payment, event, refundAmount); + pgRefundResult = pgClient.refund( payment.getPaymentKey(), refundAmount, event.refundReason() ); + paymentAuditLogService.logPgRefundSucceeded(payment, event, pgRefundResult); + } catch (Exception e) { + paymentAuditLogService.logPgRefundFailed(payment, event, e.getMessage()); + OutboxMessage failedMsg = outboxCreator.paymentRefundFailed( PaymentRefundFailedEvent.of(event.orderId(), event.refundId(), refundDecision.refundAmount(), e.getMessage()) ); @@ -85,19 +99,9 @@ public void refund(PaymentRefundEvent event) { throw e; } - payment.updateRefund( - pgRefundResult.cancels().cancelReason(), - event.refundAmount(), - pgRefundResult.cancels().canceledAt(), - pgRefundResult.cancels().cancelTransactionKey(), - pgRefundResult.cancels().cancelAmount() - ); - paymentRepository.updateRefund(payment.getId(), payment.getRefund()); + refundFinalizeService.finalizeRefund(pgRefundResult, event); - // Todo: 환불 후 정산금 어떻게 하지.......... - OutboxMessage successMsg = outboxCreator.paymentRefunded( - PaymentRefundSucceededEvent.of(event.orderId(), event.refundId(), refundDecision.refundAmount()) - ); - outboxPort.save(successMsg); + paymentAuditLogService.logRefundSucceeded(payment, event); } + } diff --git a/payment/src/main/java/com/smore/payment/payment/application/RefundFinalizeService.java b/payment/src/main/java/com/smore/payment/payment/application/RefundFinalizeService.java new file mode 100644 index 00000000..2ba5df8c --- /dev/null +++ b/payment/src/main/java/com/smore/payment/payment/application/RefundFinalizeService.java @@ -0,0 +1,50 @@ +package com.smore.payment.payment.application; + +import com.smore.payment.payment.application.event.inbound.PaymentRefundEvent; +import com.smore.payment.payment.application.event.outbound.PaymentRefundSucceededEvent; +import com.smore.payment.payment.application.port.out.OutboxPort; +import com.smore.payment.payment.application.port.out.PaymentRepository; +import com.smore.payment.payment.domain.model.Payment; +import com.smore.payment.payment.domain.model.PaymentStatus; +import com.smore.payment.payment.domain.model.PgResponseResult; +import com.smore.payment.shared.outbox.OutboxMessageCreator; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional +public class RefundFinalizeService { + + private final PaymentRepository paymentRepository; + private final OutboxPort outboxPort; + private final OutboxMessageCreator outboxCreator; + + public void finalizeRefund(PgResponseResult pgResponseResult, PaymentRefundEvent paymentRefundEvent) { + + Payment payment = paymentRepository + .findById(paymentRefundEvent.paymentId()) + .orElseThrow(); + + if (payment.getStatus() == PaymentStatus.REFUNDED) { + return; + } + + payment.updateRefund( + pgResponseResult.cancels().cancelReason(), + paymentRefundEvent.refundAmount(), + pgResponseResult.cancels().canceledAt(), + pgResponseResult.cancels().cancelTransactionKey(), + pgResponseResult.cancels().cancelAmount(), + "REFUNDED" + ); + paymentRepository.updateRefund(payment.getId(), payment.getRefund()); + + outboxPort.save( + outboxCreator.paymentRefunded( + PaymentRefundSucceededEvent.of(paymentRefundEvent.orderId(), paymentRefundEvent.refundId(), paymentRefundEvent.refundAmount()) + ) + ); + } +} diff --git a/payment/src/main/java/com/smore/payment/payment/application/port/out/MongoRepository.java b/payment/src/main/java/com/smore/payment/payment/application/port/out/MongoRepository.java deleted file mode 100644 index 9d65c85e..00000000 --- a/payment/src/main/java/com/smore/payment/payment/application/port/out/MongoRepository.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.smore.payment.payment.application.port.out; - -//import com.smore.payment.payment.domain.document.PgApproveLog; - -public interface MongoRepository { - -// void savePgApproveLog(PgApproveLog pgApproveLog); -} diff --git a/payment/src/main/java/com/smore/payment/payment/application/port/out/PaymentAuditLogPort.java b/payment/src/main/java/com/smore/payment/payment/application/port/out/PaymentAuditLogPort.java new file mode 100644 index 00000000..527e65de --- /dev/null +++ b/payment/src/main/java/com/smore/payment/payment/application/port/out/PaymentAuditLogPort.java @@ -0,0 +1,8 @@ +package com.smore.payment.payment.application.port.out; + +import com.smore.payment.payment.infrastructure.persistence.mongo.model.PaymentAuditLog; + +public interface PaymentAuditLogPort { + + void save(PaymentAuditLog auditLog); +} \ No newline at end of file diff --git a/payment/src/main/java/com/smore/payment/payment/domain/document/PgApproveLog.java b/payment/src/main/java/com/smore/payment/payment/domain/document/PgApproveLog.java deleted file mode 100644 index 3df1184b..00000000 --- a/payment/src/main/java/com/smore/payment/payment/domain/document/PgApproveLog.java +++ /dev/null @@ -1,18 +0,0 @@ -//package com.smore.payment.payment.domain.document; -// -//import lombok.AllArgsConstructor; -//import lombok.Builder; -//import lombok.Getter; -//import lombok.NoArgsConstructor; -//import org.springframework.data.mongodb.core.mapping.Document; -// -//@Document("payment_logs") -//@Getter -//@NoArgsConstructor -//@AllArgsConstructor -//@Builder -//public class PgApproveLog { -// -// -//} -// diff --git a/payment/src/main/java/com/smore/payment/payment/domain/model/Payment.java b/payment/src/main/java/com/smore/payment/payment/domain/model/Payment.java index 9840ddb4..1c3e76a7 100644 --- a/payment/src/main/java/com/smore/payment/payment/domain/model/Payment.java +++ b/payment/src/main/java/com/smore/payment/payment/domain/model/Payment.java @@ -257,8 +257,9 @@ public static Payment reconstruct( public UUID getCategoryId() { return categoryId; } public String getAuctionType() { return auctionType; } - public void updateRefund(String reason, BigDecimal refundAmount, LocalDateTime refundedAt, String cancelTransactionKey, BigDecimal refundableAmount) { + public void updateRefund(String reason, BigDecimal refundAmount, LocalDateTime refundedAt, String cancelTransactionKey, BigDecimal refundableAmount, String status) { this.refund = new PaymentRefund(reason, refundAmount, refundedAt, cancelTransactionKey, refundableAmount); + this.status = PaymentStatus.of(status); } public PaymentApprovedEvent createApprovedEvent() { diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/model/PaymentAuditEventType.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/model/PaymentAuditEventType.java new file mode 100644 index 00000000..879dd96d --- /dev/null +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/model/PaymentAuditEventType.java @@ -0,0 +1,17 @@ +package com.smore.payment.payment.infrastructure.persistence.mongo.model; + +public enum PaymentAuditEventType { + TEMPORARY_PAYMENT_CREATED, + PAYMENT_APPROVAL_REQUESTED, + PG_APPROVAL_REQUESTED, + PG_APPROVAL_SUCCEEDED, + PG_APPROVAL_FAILED, + PAYMENT_APPROVAL_SUCCEEDED, + PAYMENT_APPROVAL_FAILED, + PAYMENT_REFUND_REQUESTED, + PG_REFUND_REQUESTED, + PG_REFUND_SUCCEEDED, + PG_REFUND_FAILED, + PAYMENT_REFUND_SUCCEEDED, + PAYMENT_REFUND_FAILED +} \ No newline at end of file diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/model/PaymentAuditLog.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/model/PaymentAuditLog.java new file mode 100644 index 00000000..94fe8dcd --- /dev/null +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/model/PaymentAuditLog.java @@ -0,0 +1,55 @@ +package com.smore.payment.payment.infrastructure.persistence.mongo.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.index.Indexed; +import org.springframework.data.mongodb.core.mapping.Document; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.UUID; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Document(collection = "payment_audit_logs") +public class PaymentAuditLog { + + @Id + private String id; + + @Indexed + private UUID orderId; + + @Indexed + private UUID paymentId; + + private UUID refundId; + + private UUID idempotencyKey; + + private Long userId; + + private Long sellerId; + + private UUID categoryId; + + private String auctionType; + + private BigDecimal amount; + + private String paymentKey; + + private String pgOrderId; + + private PaymentAuditEventType eventType; + + private String description; + + @Builder.Default + private LocalDateTime occurredAt = LocalDateTime.now(); +} \ No newline at end of file diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/repository/MongoRepositoryImpl.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/repository/MongoRepositoryImpl.java deleted file mode 100644 index 70edd890..00000000 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/repository/MongoRepositoryImpl.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.smore.payment.payment.infrastructure.persistence.mongo.repository; - -//import com.smore.payment.payment.domain.document.PgApproveLog; -import com.smore.payment.payment.application.port.out.MongoRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -@Repository -@RequiredArgsConstructor -public class MongoRepositoryImpl implements MongoRepository { - - -// @Override -// public void savePgApproveLog(PgApproveLog pgApproveLog) { -// paymentMongoRepository.save(pgApproveLog); -// } -} diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/repository/PaymentAuditLogRepository.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/repository/PaymentAuditLogRepository.java new file mode 100644 index 00000000..7f4376c6 --- /dev/null +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/repository/PaymentAuditLogRepository.java @@ -0,0 +1,7 @@ +package com.smore.payment.payment.infrastructure.persistence.mongo.repository; + +import com.smore.payment.payment.infrastructure.persistence.mongo.model.PaymentAuditLog; +import org.springframework.data.mongodb.repository.MongoRepository; + +public interface PaymentAuditLogRepository extends MongoRepository { +} \ No newline at end of file diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/repository/PaymentAuditLogRepositoryAdapter.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/repository/PaymentAuditLogRepositoryAdapter.java new file mode 100644 index 00000000..48eb8ff0 --- /dev/null +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/repository/PaymentAuditLogRepositoryAdapter.java @@ -0,0 +1,18 @@ +package com.smore.payment.payment.infrastructure.persistence.mongo.repository; + +import com.smore.payment.payment.application.port.out.PaymentAuditLogPort; +import com.smore.payment.payment.infrastructure.persistence.mongo.model.PaymentAuditLog; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +@Repository +@RequiredArgsConstructor +public class PaymentAuditLogRepositoryAdapter implements PaymentAuditLogPort { + + private final PaymentAuditLogRepository paymentAuditLogRepository; + + @Override + public void save(PaymentAuditLog auditLog) { + paymentAuditLogRepository.save(auditLog); + } +} \ No newline at end of file diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/repository/PaymentMongoRepository.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/repository/PaymentMongoRepository.java deleted file mode 100644 index 95265aac..00000000 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/mongo/repository/PaymentMongoRepository.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.smore.payment.payment.infrastructure.persistence.mongo.repository; - - -public interface PaymentMongoRepository { -} From 8457b5c716d8da7823b6d3063d89ddb127e6e7b7 Mon Sep 17 00:00:00 2001 From: ooinl77 Date: Thu, 18 Dec 2025 17:15:05 +0900 Subject: [PATCH 08/16] =?UTF-8?q?feat:=20refund=20dlt->retry=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- payment/build.gradle | 2 +- .../application/ApprovePaymentService.java | 19 +++++++- .../application/PaymentRefundService.java | 46 +++++++++++++++---- .../port/in/ApprovePaymentResult.java | 41 ++++++++++++++++- .../infrastructure/config/KafkaConfig.java | 11 +++++ .../shared/outbox/OutboxMessageCreator.java | 16 +++++++ .../src/main/resources/application-dev.yml | 14 ++---- 7 files changed, 126 insertions(+), 23 deletions(-) diff --git a/payment/build.gradle b/payment/build.gradle index 4f23bea6..8a64c96b 100644 --- a/payment/build.gradle +++ b/payment/build.gradle @@ -54,7 +54,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-webflux' - implementation 'org.flywaydb:flyway-core' +// implementation 'org.flywaydb:flyway-core' } dependencyManagement { diff --git a/payment/src/main/java/com/smore/payment/payment/application/ApprovePaymentService.java b/payment/src/main/java/com/smore/payment/payment/application/ApprovePaymentService.java index 14e6a260..8efe5e83 100644 --- a/payment/src/main/java/com/smore/payment/payment/application/ApprovePaymentService.java +++ b/payment/src/main/java/com/smore/payment/payment/application/ApprovePaymentService.java @@ -52,6 +52,20 @@ public ApprovePaymentResult approve(ApprovePaymentCommand command) { command.amount() ); + if (!pgResult.pgStatus().equals("DONE")) { + paymentAuditLogService.logPgApprovalFailed( + temp, + pgResult.failureMessage() + ); + + return ApprovePaymentResult.failed( + command.orderId(), + command.amount(), + pgResult.failureCode(), + pgResult.failureMessage() + ); + } + paymentAuditLogService.logPgApprovalSucceeded(temp, pgResult); } catch (RuntimeException e) { @@ -63,13 +77,14 @@ public ApprovePaymentResult approve(ApprovePaymentCommand command) { Payment payment = paymentFinalizeService.finalizePayment(temp, pgResult); paymentAuditLogService.logPaymentApprovalSucceeded(payment); - return new ApprovePaymentResult( + return ApprovePaymentResult.success( payment.getId(), payment.getOrderId(), payment.getAmount(), payment.getApprovedAt(), - payment.getStatus().name() + payment.getStatus().getDesc() ); + } catch (RuntimeException e) { paymentAuditLogService.logPaymentApprovalFailed(temp, e.getMessage()); throw e; diff --git a/payment/src/main/java/com/smore/payment/payment/application/PaymentRefundService.java b/payment/src/main/java/com/smore/payment/payment/application/PaymentRefundService.java index 8df0841e..8d87a95f 100644 --- a/payment/src/main/java/com/smore/payment/payment/application/PaymentRefundService.java +++ b/payment/src/main/java/com/smore/payment/payment/application/PaymentRefundService.java @@ -3,10 +3,12 @@ import com.smore.payment.payment.application.event.inbound.PaymentRefundEvent; import com.smore.payment.payment.application.event.outbound.PaymentRefundFailedEvent; import com.smore.payment.payment.application.event.outbound.PaymentRefundSucceededEvent; +import com.smore.payment.payment.application.event.outbound.SettlementFailedEvent; import com.smore.payment.payment.application.facade.CancelPolicyFacade; import com.smore.payment.payment.application.facade.RefundPolicyFacade; import com.smore.payment.payment.application.facade.dto.CancelPolicyResult; import com.smore.payment.payment.application.facade.dto.RefundPolicyResult; +import com.smore.payment.payment.application.port.in.ApprovePaymentResult; import com.smore.payment.payment.application.port.in.RefundPaymentUseCase; import com.smore.payment.payment.application.port.out.OutboxPort; import com.smore.payment.payment.application.port.out.PaymentRepository; @@ -85,23 +87,51 @@ public void refund(PaymentRefundEvent event) { event.refundReason() ); + if (!pgRefundResult.pgStatus().equals("CANCELED")) { + paymentAuditLogService.logPgRefundFailed(payment, event, pgRefundResult.failureMessage()); + + OutboxMessage failedMsg = outboxCreator.paymentRefundFailed( + PaymentRefundFailedEvent.of(event.orderId(), event.refundId(), refundDecision.refundAmount(), pgRefundResult.failureMessage()) + ); + outboxPort.save(failedMsg); + } + paymentAuditLogService.logPgRefundSucceeded(payment, event, pgRefundResult); - } catch (Exception e) { + } catch (RuntimeException e) { paymentAuditLogService.logPgRefundFailed(payment, event, e.getMessage()); - OutboxMessage failedMsg = outboxCreator.paymentRefundFailed( - PaymentRefundFailedEvent.of(event.orderId(), event.refundId(), refundDecision.refundAmount(), e.getMessage()) + outboxPort.save( + outboxCreator.refundDlt( + PaymentRefundFailedEvent.of( + event.orderId(), + event.refundId(), + event.refundAmount(), + "환불 처리 중 시스템 오류 발생: " + e.getMessage() + ) + ) ); - outboxPort.save(failedMsg); throw e; } - refundFinalizeService.finalizeRefund(pgRefundResult, event); - - paymentAuditLogService.logRefundSucceeded(payment, event); + try { + refundFinalizeService.finalizeRefund(pgRefundResult, event); + paymentAuditLogService.logRefundSucceeded(payment, event); + } catch (RuntimeException e) { + paymentAuditLogService.logRefundFailed(payment, event, e.getMessage()); + outboxPort.save( + outboxCreator.refundDlt( + PaymentRefundFailedEvent.of( + event.orderId(), + event.refundId(), + event.refundAmount(), + "환불 처리 중 시스템 오류 발생: " + e.getMessage() + ) + ) + ); + throw e; + } } - } diff --git a/payment/src/main/java/com/smore/payment/payment/application/port/in/ApprovePaymentResult.java b/payment/src/main/java/com/smore/payment/payment/application/port/in/ApprovePaymentResult.java index 75571f20..e9628267 100644 --- a/payment/src/main/java/com/smore/payment/payment/application/port/in/ApprovePaymentResult.java +++ b/payment/src/main/java/com/smore/payment/payment/application/port/in/ApprovePaymentResult.java @@ -1,5 +1,7 @@ package com.smore.payment.payment.application.port.in; +import org.springframework.cglib.core.Local; + import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.UUID; @@ -9,6 +11,43 @@ public record ApprovePaymentResult( UUID orderId, BigDecimal approvedAmount, LocalDateTime approvedAt, - String status + String status, + String failureCode, + String failureMessage ) { + + public static ApprovePaymentResult success( + UUID paymentId, + UUID orderId, + BigDecimal amount, + LocalDateTime approvedAt, + String status + ) { + return new ApprovePaymentResult( + paymentId, + orderId, + amount, + approvedAt, + status, + null, + null + ); + } + + public static ApprovePaymentResult failed( + UUID orderId, + BigDecimal amount, + String failureCode, + String failureMessage + ) { + return new ApprovePaymentResult( + null, + orderId, + amount, + null, + "FAILED", + failureCode, + failureMessage + ); + } } \ No newline at end of file diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/config/KafkaConfig.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/config/KafkaConfig.java index 10ea965f..c6db3f0f 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/config/KafkaConfig.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/config/KafkaConfig.java @@ -32,6 +32,9 @@ public class KafkaConfig { @Value("${topic.order.refund-fail}") private String orderRefundFailTopic; + @Value("${topic.order.refund-dlt}") + private String orderRefundDltTopic; + @Value("${topic.seller.success}") private String sellerSuccessTopic; @@ -88,6 +91,14 @@ public NewTopic paymentRefundFailTopic() { .build(); } + @Bean + public NewTopic paymentRefundDltTopic() { + return TopicBuilder.name(orderRefundDltTopic) + .partitions(3) + .replicas(3) + .build(); + } + @Bean public NewTopic sellerSuccessTopic() { return TopicBuilder.name(sellerSuccessTopic) diff --git a/payment/src/main/java/com/smore/payment/shared/outbox/OutboxMessageCreator.java b/payment/src/main/java/com/smore/payment/shared/outbox/OutboxMessageCreator.java index 22164951..925bd94e 100644 --- a/payment/src/main/java/com/smore/payment/shared/outbox/OutboxMessageCreator.java +++ b/payment/src/main/java/com/smore/payment/shared/outbox/OutboxMessageCreator.java @@ -36,6 +36,9 @@ public class OutboxMessageCreator { @Value("${topic.order.refund-fail}") private String orderRefundFailTopic; + @Value("${topic.order.refund-dlt}") + private String orderRefundDltTopic; + @Value("${topic.seller.success}") private String sellerSuccessTopic; @@ -148,4 +151,17 @@ public OutboxMessage settlementDlt(SettlementFailedEvent event) { OutboxStatus.FAILED ); } + + public OutboxMessage refundDlt(PaymentRefundFailedEvent event) { + return new OutboxMessage( + "REFUND_FAILED", + event.orderId(), + event.getClass().getSimpleName(), + UUID.randomUUID(), + orderRefundDltTopic, + jsonUtil.jsonToString(event), + 3, + OutboxStatus.FAILED + ); + } } diff --git a/payment/src/main/resources/application-dev.yml b/payment/src/main/resources/application-dev.yml index 9042053a..79ab0baa 100644 --- a/payment/src/main/resources/application-dev.yml +++ b/payment/src/main/resources/application-dev.yml @@ -31,25 +31,16 @@ spring: data: mongodb: - uri: mongodb://mongo_user:mongo_pass@mongo:27017/my_mongo_db?authSource=admin + uri: mongodb://root:examplepassword@mongodb:27017/default?authSource=admin auto-index-creation: true redis: host: redis-stack port: 6379 - flyway: - enabled: true - locations: classpath:db/migration - baseline-on-migrate: true - - sql: - init: - mode: always - jpa: database-platform: org.hibernate.dialect.PostgreSQLDialect hibernate: - ddl-auto: validate + ddl-auto: create show-sql: true properties: hibernate: @@ -95,6 +86,7 @@ topic: failed: payment.failed.v1 refund: payment.refund.v1 refund-fail: payment.refund-fail.v1 + refund-dlt: payment.refund-dlt.v1 seller: approved: payment.settlement.v1 success: payment.settlement-success.v1 From 6dadf6538dc1fb6ab4409942b500414320db40db Mon Sep 17 00:00:00 2001 From: ooinl77 Date: Thu, 18 Dec 2025 20:23:34 +0900 Subject: [PATCH 09/16] =?UTF-8?q?feat:=20approve=20pg=20=EC=8A=B9=EC=9D=B8?= =?UTF-8?q?=20=ED=9B=84=20=EC=9E=AC=EC=8B=9C=EB=8F=84=EB=A5=BC=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/ApprovePaymentService.java | 73 ++++++++++++------- .../port/out/TemporaryPaymentPort.java | 2 + .../domain/model/TemporaryPayment.java | 8 +- .../redis/TemporaryPaymentAdapter.java | 50 +++++++++++-- 4 files changed, 100 insertions(+), 33 deletions(-) diff --git a/payment/src/main/java/com/smore/payment/payment/application/ApprovePaymentService.java b/payment/src/main/java/com/smore/payment/payment/application/ApprovePaymentService.java index 8efe5e83..ada343c0 100644 --- a/payment/src/main/java/com/smore/payment/payment/application/ApprovePaymentService.java +++ b/payment/src/main/java/com/smore/payment/payment/application/ApprovePaymentService.java @@ -38,15 +38,52 @@ public ApprovePaymentResult approve(ApprovePaymentCommand command) { temp.validateApproval(command.amount()); paymentRepository.findByIdempotencyKey(temp.getIdempotencyKey()) - .ifPresent(p -> { throw new IllegalStateException("이미 승인"); }); + .ifPresent(p -> { + throw new IllegalStateException("이미 승인"); + }); - PgResponseResult pgResult; + + PgResponseResult pgResult = findOrRequestPgApproval(command, temp); + + if (!pgResult.pgStatus().equals("DONE")) { + return ApprovePaymentResult.failed( + command.orderId(), + command.amount(), + pgResult.failureCode(), + pgResult.failureMessage() + ); + } + + try { + Payment payment = paymentFinalizeService.finalizePayment(temp, pgResult); + paymentAuditLogService.logPaymentApprovalSucceeded(payment); + + return ApprovePaymentResult.success( + payment.getId(), + payment.getOrderId(), + payment.getAmount(), + payment.getApprovedAt(), + payment.getStatus().getDesc() + ); + + } catch (RuntimeException e) { + paymentAuditLogService.logPaymentApprovalFailed(temp, e.getMessage()); + throw e; + } + } + + + private PgResponseResult findOrRequestPgApproval(ApprovePaymentCommand command, TemporaryPayment temp) { + + if (temp.hasPgApprovalResult()) { + return temp.getPgResponseResult(); + } try { paymentAuditLogService.logPgApprovalRequested(command, temp); - pgResult = pgClient.approve( + PgResponseResult pgResult = pgClient.approve( command.paymentKey(), command.pgOrderId(), command.amount() @@ -58,39 +95,21 @@ public ApprovePaymentResult approve(ApprovePaymentCommand command) { pgResult.failureMessage() ); - return ApprovePaymentResult.failed( - command.orderId(), - command.amount(), - pgResult.failureCode(), - pgResult.failureMessage() - ); + return pgResult; } - paymentAuditLogService.logPgApprovalSucceeded(temp, pgResult); - - } catch (RuntimeException e) { - paymentAuditLogService.logPgApprovalFailed(temp, e.getMessage()); - throw e; - } + temp.setPgResponseResult(pgResult); + temporaryPaymentPort.update(temp); - try { - Payment payment = paymentFinalizeService.finalizePayment(temp, pgResult); - paymentAuditLogService.logPaymentApprovalSucceeded(payment); + paymentAuditLogService.logPgApprovalSucceeded(temp, pgResult); - return ApprovePaymentResult.success( - payment.getId(), - payment.getOrderId(), - payment.getAmount(), - payment.getApprovedAt(), - payment.getStatus().getDesc() - ); + return pgResult; } catch (RuntimeException e) { - paymentAuditLogService.logPaymentApprovalFailed(temp, e.getMessage()); + paymentAuditLogService.logPgApprovalFailed(temp, e.getMessage()); throw e; } } - } diff --git a/payment/src/main/java/com/smore/payment/payment/application/port/out/TemporaryPaymentPort.java b/payment/src/main/java/com/smore/payment/payment/application/port/out/TemporaryPaymentPort.java index 95689995..e9aeda20 100644 --- a/payment/src/main/java/com/smore/payment/payment/application/port/out/TemporaryPaymentPort.java +++ b/payment/src/main/java/com/smore/payment/payment/application/port/out/TemporaryPaymentPort.java @@ -11,5 +11,7 @@ public interface TemporaryPaymentPort { void save(TemporaryPayment temp); + void update(TemporaryPayment temp); + boolean existsByOrderId(UUID orderId); } diff --git a/payment/src/main/java/com/smore/payment/payment/domain/model/TemporaryPayment.java b/payment/src/main/java/com/smore/payment/payment/domain/model/TemporaryPayment.java index 7366e35e..c97875c5 100644 --- a/payment/src/main/java/com/smore/payment/payment/domain/model/TemporaryPayment.java +++ b/payment/src/main/java/com/smore/payment/payment/domain/model/TemporaryPayment.java @@ -23,6 +23,7 @@ public class TemporaryPayment { private UUID categoryId; private String auctionType; private LocalDateTime expiredAt; + private PgResponseResult pgResponseResult; public static TemporaryPayment create( UUID idempotencyKey, @@ -42,7 +43,8 @@ public static TemporaryPayment create( sellerId, categoryId, auctionType, - expiredAt + expiredAt, + null ); } @@ -62,4 +64,8 @@ public void validateApproval(BigDecimal requestAmount) { throw new IllegalStateException("결제 승인 시간이 만료되었습니다."); } } + + public boolean hasPgApprovalResult() { + return pgResponseResult != null; + } } \ No newline at end of file diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/redis/TemporaryPaymentAdapter.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/redis/TemporaryPaymentAdapter.java index d493e005..14d01a3e 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/redis/TemporaryPaymentAdapter.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/redis/TemporaryPaymentAdapter.java @@ -23,8 +23,14 @@ public class TemporaryPaymentAdapter implements TemporaryPaymentPort { public Optional findByOrderId(UUID orderId) { log.info("레디스 찾기 시작: {}", orderId); - TemporaryPayment temp = temporaryPaymentRedisTemplate.opsForValue() - .get(orderId.toString()); + String tempKey = tempKey(orderId); + String approvedKey = approvedKey(orderId); + + TemporaryPayment temp = temporaryPaymentRedisTemplate.opsForValue().get(tempKey); + + if (temp == null) { + temp = temporaryPaymentRedisTemplate.opsForValue().get(approvedKey); + } log.info("레디스 결과: {}", temp); @@ -33,7 +39,8 @@ public Optional findByOrderId(UUID orderId) { @Override public void deleteByOrderId(UUID orderId) { - temporaryPaymentRedisTemplate.delete(orderId.toString()); + temporaryPaymentRedisTemplate.delete(tempKey(orderId)); + temporaryPaymentRedisTemplate.delete(approvedKey(orderId)); } @Override @@ -43,12 +50,45 @@ public void save(TemporaryPayment temp) { if (ttl.isNegative() || ttl.isZero()) { throw new IllegalArgumentException("만료 시간이 현재 시간보다 과거입니다."); } + temporaryPaymentRedisTemplate.opsForValue() + .set(tempKey(temp.getOrderId()), temp, ttl); + } - temporaryPaymentRedisTemplate.opsForValue().set(temp.getOrderId().toString(), temp, ttl); + @Override + public void update(TemporaryPayment temp) { + + String oldKey = tempKey(temp.getOrderId()); + String newKey = approvedKey(temp.getOrderId()); + + Boolean exists = temporaryPaymentRedisTemplate.hasKey(oldKey); + if (!exists) { + throw new IllegalStateException( + "TemporaryPayment가 Redis에 존재하지 않습니다. orderId=" + temp.getOrderId() + ); + } + + temporaryPaymentRedisTemplate.rename(oldKey, newKey); + + temporaryPaymentRedisTemplate.opsForValue().set(newKey, temp); + + log.info( + "TemporaryPayment 승인 상태로 승격. key={}, pgApprovedAt={}", + newKey, + temp.getPgResponseResult() != null ? temp.getPgResponseResult().approvedAt() : null + ); } @Override public boolean existsByOrderId(UUID orderId) { - return temporaryPaymentRedisTemplate.hasKey(orderId.toString()); + return temporaryPaymentRedisTemplate.hasKey(tempKey(orderId)) || + temporaryPaymentRedisTemplate.hasKey(approvedKey(orderId)); + } + + private String tempKey(UUID orderId) { + return "payment:temp:" + orderId; + } + + private String approvedKey(UUID orderId) { + return "payment:pg:approved:" + orderId; } } From 839913b34056c8dd1b1b59f81e282c2d6b1d4f13 Mon Sep 17 00:00:00 2001 From: ooinl77 Date: Fri, 19 Dec 2025 02:37:56 +0900 Subject: [PATCH 10/16] =?UTF-8?q?feat:=20api=20inbox=20=ED=8C=A8=ED=84=B4(?= =?UTF-8?q?redis=20=EC=82=AC=EC=9A=A9)=20=EC=A0=81=EC=9A=A9=20=EB=B0=8F=20?= =?UTF-8?q?=EB=A9=B1=EB=93=B1=20=EA=B2=80=EC=82=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/ApprovePaymentService.java | 70 ++++++++++++++++--- .../CreateTemporaryPaymentService.java | 2 - .../application/PaymentRefundService.java | 4 -- .../port/in/ApprovePaymentCommand.java | 1 + .../application/port/out/ApiInboxPort.java | 12 ++++ .../api_inbox/ApiInboxRepository.java | 30 ++++++++ .../RedisKeyExpirationListenerConfig.java | 2 +- .../kafka/PaymentApprovedOutboxPublisher.java | 4 +- .../persistence/outbox/OutboxAdapter.java | 2 - .../{jpa/model => }/outbox/OutboxEntity.java | 2 +- .../outbox/OutboxJpaRepository.java | 3 +- .../{jpa/mapper => outbox}/OutboxMapper.java | 3 +- .../redis/FailedPaymentHandler.java | 2 +- .../redis/RedisKeyExpirationListener.java | 7 +- .../redis/TemporaryPaymentAdapter.java | 2 +- .../dto/request/ApprovePaymentRequestDto.java | 3 + .../presentation/mapper/PaymentDtoMapper.java | 1 + 17 files changed, 122 insertions(+), 28 deletions(-) create mode 100644 payment/src/main/java/com/smore/payment/payment/application/port/out/ApiInboxPort.java create mode 100644 payment/src/main/java/com/smore/payment/payment/infrastructure/api_inbox/ApiInboxRepository.java rename payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/{jpa/model => }/outbox/OutboxEntity.java (95%) rename payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/{jpa/repository => }/outbox/OutboxJpaRepository.java (63%) rename payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/{jpa/mapper => outbox}/OutboxMapper.java (86%) rename payment/src/main/java/com/smore/payment/payment/infrastructure/{persistence => }/redis/FailedPaymentHandler.java (94%) rename payment/src/main/java/com/smore/payment/payment/infrastructure/{persistence => }/redis/RedisKeyExpirationListener.java (79%) rename payment/src/main/java/com/smore/payment/payment/infrastructure/{persistence => }/redis/TemporaryPaymentAdapter.java (97%) diff --git a/payment/src/main/java/com/smore/payment/payment/application/ApprovePaymentService.java b/payment/src/main/java/com/smore/payment/payment/application/ApprovePaymentService.java index ada343c0..954be20d 100644 --- a/payment/src/main/java/com/smore/payment/payment/application/ApprovePaymentService.java +++ b/payment/src/main/java/com/smore/payment/payment/application/ApprovePaymentService.java @@ -3,6 +3,7 @@ import com.smore.payment.payment.application.port.in.ApprovePaymentCommand; import com.smore.payment.payment.application.port.in.ApprovePaymentResult; import com.smore.payment.payment.application.port.in.ApprovePaymentUseCase; +import com.smore.payment.payment.application.port.out.ApiInboxPort; import com.smore.payment.payment.application.port.out.PaymentRepository; import com.smore.payment.payment.application.port.out.PgClient; import com.smore.payment.payment.application.port.out.TemporaryPaymentPort; @@ -11,10 +12,13 @@ import com.smore.payment.payment.domain.model.TemporaryPayment; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; +import java.util.Optional; + @Slf4j @Service @RequiredArgsConstructor @@ -25,26 +29,53 @@ public class ApprovePaymentService implements ApprovePaymentUseCase { private final PgClient pgClient; private final PaymentFinalizeService paymentFinalizeService; private final PaymentAuditLogService paymentAuditLogService; + private final ApiInboxPort apiInboxPort; @Override @Transactional(propagation = Propagation.NOT_SUPPORTED) public ApprovePaymentResult approve(ApprovePaymentCommand command) { - TemporaryPayment temp = temporaryPaymentPort.findByOrderId(command.orderId()) - .orElseThrow(); + // 단기 멱등(redis) + Optional cached = + apiInboxPort.find(command.idempotencyKey().toString()); - paymentAuditLogService.logApprovalRequested(command, temp); + if (cached.isPresent()) { + return cached.get(); + } - temp.validateApproval(command.amount()); + // 장기 멱등(rdb) 후 redis 캐싱 + Optional existingPayment = + paymentRepository.findByIdempotencyKey(command.idempotencyKey()); - paymentRepository.findByIdempotencyKey(temp.getIdempotencyKey()) - .ifPresent(p -> { - throw new IllegalStateException("이미 승인"); - }); + if (existingPayment.isPresent()) { + Payment payment = existingPayment.get(); + ApprovePaymentResult result = ApprovePaymentResult.success( + payment.getId(), + payment.getOrderId(), + payment.getAmount(), + payment.getApprovedAt(), + payment.getStatus().getDesc() + ); + apiInboxPort.save(command.idempotencyKey().toString(), result); // 캐시 재적재 + return result; + } + + // 결제 승인 시작 - 임시 결제 가져와서 pg에 승인 요청 + TemporaryPayment temp = temporaryPaymentPort.findByOrderId(command.orderId()) + .orElseThrow(() -> new IllegalArgumentException("임시 결제 데이터가 존재하지 않습니다.")); + + // 금액 검증 + temp.validateApproval(command.amount()); + + // 승인 요청 감사 로그 작성(mongodb) + paymentAuditLogService.logApprovalRequested(command, temp); + + // pg 결제 승인 요청 PgResponseResult pgResult = findOrRequestPgApproval(command, temp); + // pg 결제 실패 시 실패 이유를 반환 if (!pgResult.pgStatus().equals("DONE")) { return ApprovePaymentResult.failed( command.orderId(), @@ -54,11 +85,16 @@ public ApprovePaymentResult approve(ApprovePaymentCommand command) { ); } + ApprovePaymentResult result; + try { + // 결제 내역 저장 및 수수료 정책 조회하여 정산금 계산 후 이벤트 발행 Payment payment = paymentFinalizeService.finalizePayment(temp, pgResult); + + // 결제 승인 완료 감사 로그 작성 paymentAuditLogService.logPaymentApprovalSucceeded(payment); - return ApprovePaymentResult.success( + result = ApprovePaymentResult.success( payment.getId(), payment.getOrderId(), payment.getAmount(), @@ -66,10 +102,26 @@ public ApprovePaymentResult approve(ApprovePaymentCommand command) { payment.getStatus().getDesc() ); + } catch (DataIntegrityViolationException e) { + // UNIQUE 제약 충돌 → 이미 생성된 Payment + Payment existing = paymentRepository + .findByIdempotencyKey(command.idempotencyKey()) + .orElseThrow(); + + result = ApprovePaymentResult.success( + existing.getId(), + existing.getOrderId(), + existing.getAmount(), + existing.getApprovedAt(), + existing.getStatus().getDesc() + ); } catch (RuntimeException e) { paymentAuditLogService.logPaymentApprovalFailed(temp, e.getMessage()); throw e; } + + apiInboxPort.save(command.idempotencyKey().toString(), result); + return result; } diff --git a/payment/src/main/java/com/smore/payment/payment/application/CreateTemporaryPaymentService.java b/payment/src/main/java/com/smore/payment/payment/application/CreateTemporaryPaymentService.java index 16eb0d11..f5fde25e 100644 --- a/payment/src/main/java/com/smore/payment/payment/application/CreateTemporaryPaymentService.java +++ b/payment/src/main/java/com/smore/payment/payment/application/CreateTemporaryPaymentService.java @@ -5,7 +5,6 @@ import com.smore.payment.payment.domain.model.TemporaryPayment; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; @Service @RequiredArgsConstructor @@ -14,7 +13,6 @@ public class CreateTemporaryPaymentService { private final TemporaryPaymentPort temporaryPaymentPort; private final PaymentAuditLogService paymentAuditLogService; - @Transactional public void create(PaymentRequestedEvent paymentRequestedEvent) { if (temporaryPaymentPort.existsByOrderId(paymentRequestedEvent.orderId())) { diff --git a/payment/src/main/java/com/smore/payment/payment/application/PaymentRefundService.java b/payment/src/main/java/com/smore/payment/payment/application/PaymentRefundService.java index 8d87a95f..895e0c77 100644 --- a/payment/src/main/java/com/smore/payment/payment/application/PaymentRefundService.java +++ b/payment/src/main/java/com/smore/payment/payment/application/PaymentRefundService.java @@ -2,13 +2,10 @@ import com.smore.payment.payment.application.event.inbound.PaymentRefundEvent; import com.smore.payment.payment.application.event.outbound.PaymentRefundFailedEvent; -import com.smore.payment.payment.application.event.outbound.PaymentRefundSucceededEvent; -import com.smore.payment.payment.application.event.outbound.SettlementFailedEvent; import com.smore.payment.payment.application.facade.CancelPolicyFacade; import com.smore.payment.payment.application.facade.RefundPolicyFacade; import com.smore.payment.payment.application.facade.dto.CancelPolicyResult; import com.smore.payment.payment.application.facade.dto.RefundPolicyResult; -import com.smore.payment.payment.application.port.in.ApprovePaymentResult; import com.smore.payment.payment.application.port.in.RefundPaymentUseCase; import com.smore.payment.payment.application.port.out.OutboxPort; import com.smore.payment.payment.application.port.out.PaymentRepository; @@ -112,7 +109,6 @@ public void refund(PaymentRefundEvent event) { ) ) ); - throw e; } diff --git a/payment/src/main/java/com/smore/payment/payment/application/port/in/ApprovePaymentCommand.java b/payment/src/main/java/com/smore/payment/payment/application/port/in/ApprovePaymentCommand.java index 93772471..72d627f3 100644 --- a/payment/src/main/java/com/smore/payment/payment/application/port/in/ApprovePaymentCommand.java +++ b/payment/src/main/java/com/smore/payment/payment/application/port/in/ApprovePaymentCommand.java @@ -5,6 +5,7 @@ public record ApprovePaymentCommand( UUID orderId, + UUID idempotencyKey, BigDecimal amount, String paymentKey, String pgOrderId diff --git a/payment/src/main/java/com/smore/payment/payment/application/port/out/ApiInboxPort.java b/payment/src/main/java/com/smore/payment/payment/application/port/out/ApiInboxPort.java new file mode 100644 index 00000000..ba3dd7d2 --- /dev/null +++ b/payment/src/main/java/com/smore/payment/payment/application/port/out/ApiInboxPort.java @@ -0,0 +1,12 @@ +package com.smore.payment.payment.application.port.out; + +import com.smore.payment.payment.application.port.in.ApprovePaymentResult; + +import java.util.Optional; + +public interface ApiInboxPort { + + Optional find(String key); + + void save(String key, ApprovePaymentResult result); +} diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/api_inbox/ApiInboxRepository.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/api_inbox/ApiInboxRepository.java new file mode 100644 index 00000000..57915192 --- /dev/null +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/api_inbox/ApiInboxRepository.java @@ -0,0 +1,30 @@ +package com.smore.payment.payment.infrastructure.api_inbox; + +import com.smore.payment.payment.application.port.in.ApprovePaymentResult; +import com.smore.payment.payment.application.port.out.ApiInboxPort; +import lombok.RequiredArgsConstructor; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Repository; + +import java.time.Duration; +import java.util.Optional; + +@Repository +@RequiredArgsConstructor +public class ApiInboxRepository implements ApiInboxPort { + + private final RedisTemplate redisTemplate; + private static final Duration TTL = Duration.ofMinutes(30); + + public Optional find(String key) { + return Optional.ofNullable(redisTemplate.opsForValue().get(keyOf(key))); + } + + public void save(String key, ApprovePaymentResult result) { + redisTemplate.opsForValue().set(keyOf(key), result, TTL); + } + + private String keyOf(String idempotencyKey) { + return "api-inbox:payment:approve:" + idempotencyKey; + } +} diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/config/RedisKeyExpirationListenerConfig.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/config/RedisKeyExpirationListenerConfig.java index 1b87a02b..f2dbe648 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/config/RedisKeyExpirationListenerConfig.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/config/RedisKeyExpirationListenerConfig.java @@ -1,6 +1,6 @@ package com.smore.payment.payment.infrastructure.config; -import com.smore.payment.payment.infrastructure.persistence.redis.RedisKeyExpirationListener; +import com.smore.payment.payment.infrastructure.redis.RedisKeyExpirationListener; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentApprovedOutboxPublisher.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentApprovedOutboxPublisher.java index 31a63f3a..5e6ae78d 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentApprovedOutboxPublisher.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentApprovedOutboxPublisher.java @@ -1,8 +1,8 @@ package com.smore.payment.payment.infrastructure.kafka; import com.smore.payment.shared.outbox.OutboxStatus; -import com.smore.payment.payment.infrastructure.persistence.jpa.model.outbox.OutboxEntity; -import com.smore.payment.payment.infrastructure.persistence.jpa.repository.outbox.OutboxJpaRepository; +import com.smore.payment.payment.infrastructure.persistence.outbox.OutboxEntity; +import com.smore.payment.payment.infrastructure.persistence.outbox.OutboxJpaRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Scheduled; diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/outbox/OutboxAdapter.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/outbox/OutboxAdapter.java index 4ddcf2a7..47fcca4d 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/outbox/OutboxAdapter.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/outbox/OutboxAdapter.java @@ -2,8 +2,6 @@ import com.smore.payment.shared.outbox.OutboxMessage; import com.smore.payment.payment.application.port.out.OutboxPort; -import com.smore.payment.payment.infrastructure.persistence.jpa.mapper.OutboxMapper; -import com.smore.payment.payment.infrastructure.persistence.jpa.repository.outbox.OutboxJpaRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Propagation; diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/model/outbox/OutboxEntity.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/outbox/OutboxEntity.java similarity index 95% rename from payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/model/outbox/OutboxEntity.java rename to payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/outbox/OutboxEntity.java index 5aea3ca5..9e394b57 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/model/outbox/OutboxEntity.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/outbox/OutboxEntity.java @@ -1,4 +1,4 @@ -package com.smore.payment.payment.infrastructure.persistence.jpa.model.outbox; +package com.smore.payment.payment.infrastructure.persistence.outbox; import com.smore.payment.shared.outbox.OutboxStatus; import jakarta.persistence.*; diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/repository/outbox/OutboxJpaRepository.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/outbox/OutboxJpaRepository.java similarity index 63% rename from payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/repository/outbox/OutboxJpaRepository.java rename to payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/outbox/OutboxJpaRepository.java index 87c193a4..57fd974d 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/repository/outbox/OutboxJpaRepository.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/outbox/OutboxJpaRepository.java @@ -1,7 +1,6 @@ -package com.smore.payment.payment.infrastructure.persistence.jpa.repository.outbox; +package com.smore.payment.payment.infrastructure.persistence.outbox; import com.smore.payment.shared.outbox.OutboxStatus; -import com.smore.payment.payment.infrastructure.persistence.jpa.model.outbox.OutboxEntity; import org.springframework.data.jpa.repository.JpaRepository; import java.util.List; diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/mapper/OutboxMapper.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/outbox/OutboxMapper.java similarity index 86% rename from payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/mapper/OutboxMapper.java rename to payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/outbox/OutboxMapper.java index 399e6183..08023332 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/jpa/mapper/OutboxMapper.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/outbox/OutboxMapper.java @@ -1,7 +1,6 @@ -package com.smore.payment.payment.infrastructure.persistence.jpa.mapper; +package com.smore.payment.payment.infrastructure.persistence.outbox; import com.smore.payment.shared.outbox.OutboxMessage; -import com.smore.payment.payment.infrastructure.persistence.jpa.model.outbox.OutboxEntity; import org.springframework.stereotype.Component; @Component diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/redis/FailedPaymentHandler.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/redis/FailedPaymentHandler.java similarity index 94% rename from payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/redis/FailedPaymentHandler.java rename to payment/src/main/java/com/smore/payment/payment/infrastructure/redis/FailedPaymentHandler.java index df245521..239cb03e 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/redis/FailedPaymentHandler.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/redis/FailedPaymentHandler.java @@ -1,4 +1,4 @@ -package com.smore.payment.payment.infrastructure.persistence.redis; +package com.smore.payment.payment.infrastructure.redis; import com.smore.payment.shared.outbox.OutboxMessage; import com.smore.payment.shared.outbox.OutboxMessageCreator; diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/redis/RedisKeyExpirationListener.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/redis/RedisKeyExpirationListener.java similarity index 79% rename from payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/redis/RedisKeyExpirationListener.java rename to payment/src/main/java/com/smore/payment/payment/infrastructure/redis/RedisKeyExpirationListener.java index 7364f053..1b4d5ff2 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/redis/RedisKeyExpirationListener.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/redis/RedisKeyExpirationListener.java @@ -1,4 +1,4 @@ -package com.smore.payment.payment.infrastructure.persistence.redis; +package com.smore.payment.payment.infrastructure.redis; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -19,6 +19,11 @@ public void onMessage(Message message, byte[] pattern) { String expiredKey = message.toString(); // 저장했던 key 문자열 log.info("[REDIS] TTL expired key = {}", expiredKey); + if (!expiredKey.startsWith("api-inbox:payment:approve:")) { + log.debug("TTL expired but ignored key={}", expiredKey); + return; + } + try { failedPaymentHandler.handleExpiredKey(expiredKey); } catch (Exception e) { diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/redis/TemporaryPaymentAdapter.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/redis/TemporaryPaymentAdapter.java similarity index 97% rename from payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/redis/TemporaryPaymentAdapter.java rename to payment/src/main/java/com/smore/payment/payment/infrastructure/redis/TemporaryPaymentAdapter.java index 14d01a3e..50096d6f 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/redis/TemporaryPaymentAdapter.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/redis/TemporaryPaymentAdapter.java @@ -1,4 +1,4 @@ -package com.smore.payment.payment.infrastructure.persistence.redis; +package com.smore.payment.payment.infrastructure.redis; import com.smore.payment.payment.application.port.out.TemporaryPaymentPort; import com.smore.payment.payment.domain.model.TemporaryPayment; diff --git a/payment/src/main/java/com/smore/payment/payment/presentation/dto/request/ApprovePaymentRequestDto.java b/payment/src/main/java/com/smore/payment/payment/presentation/dto/request/ApprovePaymentRequestDto.java index 53a2d75d..e6cde950 100644 --- a/payment/src/main/java/com/smore/payment/payment/presentation/dto/request/ApprovePaymentRequestDto.java +++ b/payment/src/main/java/com/smore/payment/payment/presentation/dto/request/ApprovePaymentRequestDto.java @@ -18,6 +18,9 @@ public class ApprovePaymentRequestDto { @NotNull private UUID orderId; + @NotNull + private UUID idempotencyKey; + @NotNull @DecimalMin(value = "0.0", inclusive = false) private BigDecimal amount; diff --git a/payment/src/main/java/com/smore/payment/payment/presentation/mapper/PaymentDtoMapper.java b/payment/src/main/java/com/smore/payment/payment/presentation/mapper/PaymentDtoMapper.java index 4d29823e..993dce97 100644 --- a/payment/src/main/java/com/smore/payment/payment/presentation/mapper/PaymentDtoMapper.java +++ b/payment/src/main/java/com/smore/payment/payment/presentation/mapper/PaymentDtoMapper.java @@ -12,6 +12,7 @@ public class PaymentDtoMapper { public ApprovePaymentCommand toCommand(ApprovePaymentRequestDto requestDto) { return new ApprovePaymentCommand( requestDto.getOrderId(), + requestDto.getIdempotencyKey(), requestDto.getAmount(), requestDto.getPaymentKey(), requestDto.getPgOrderId() From 63b93d8a4b4b3a26721f31daef84d1037ae45942 Mon Sep 17 00:00:00 2001 From: ooinl77 Date: Tue, 23 Dec 2025 11:13:39 +0900 Subject: [PATCH 11/16] =?UTF-8?q?refactor:=20refund=20inbox,=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20=EC=A0=84=EC=9D=B4,=20=EB=82=99=EA=B4=80=EC=A0=81?= =?UTF-8?q?=20=EB=9D=BD=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 2 +- .../application/ApprovePaymentService.java | 21 ++- ...ervice.java => PaymentFinalizeCreate.java} | 21 ++- .../application/PaymentFinalizeRefund.java | 170 ++++++++++++++++++ .../application/PaymentRefundService.java | 103 ++++------- .../application/RefundFinalizeService.java | 50 ------ .../config/WebClientConfig.java | 4 +- .../kafka/PaymentRefundEventConsumer.java | 55 ++++++ .../kafka/PaymentSettlementEventConsumer.java | 1 - .../persistence/inbox/RefundInbox.java | 124 +++++++++++++ .../inbox/RefundInboxProcessor.java | 46 +++++ .../inbox/RefundInboxRepository.java | 11 ++ .../persistence/inbox/RefundInboxStatus.java | 11 ++ .../shared/outbox/OutboxMessageCreator.java | 8 +- 14 files changed, 486 insertions(+), 141 deletions(-) rename payment/src/main/java/com/smore/payment/payment/application/{PaymentFinalizeService.java => PaymentFinalizeCreate.java} (84%) create mode 100644 payment/src/main/java/com/smore/payment/payment/application/PaymentFinalizeRefund.java delete mode 100644 payment/src/main/java/com/smore/payment/payment/application/RefundFinalizeService.java create mode 100644 payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/inbox/RefundInbox.java create mode 100644 payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/inbox/RefundInboxProcessor.java create mode 100644 payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/inbox/RefundInboxRepository.java create mode 100644 payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/inbox/RefundInboxStatus.java diff --git a/.env b/.env index b5dfcc3e..b5e4551c 100644 --- a/.env +++ b/.env @@ -1 +1 @@ -PG_SECRET_KEY=test_sk_24xLea5zVA9wY1om2pjmVQAMYNwW \ No newline at end of file +PG_SECRET_KEY=test_gsk_docs_OaPz8L5KdmQXkzRz3y47BMw6: \ No newline at end of file diff --git a/payment/src/main/java/com/smore/payment/payment/application/ApprovePaymentService.java b/payment/src/main/java/com/smore/payment/payment/application/ApprovePaymentService.java index 954be20d..a24f73b1 100644 --- a/payment/src/main/java/com/smore/payment/payment/application/ApprovePaymentService.java +++ b/payment/src/main/java/com/smore/payment/payment/application/ApprovePaymentService.java @@ -1,15 +1,17 @@ package com.smore.payment.payment.application; +import com.smore.payment.payment.application.facade.FeePolicyFacade; +import com.smore.payment.payment.application.facade.dto.FeePolicyResult; import com.smore.payment.payment.application.port.in.ApprovePaymentCommand; import com.smore.payment.payment.application.port.in.ApprovePaymentResult; import com.smore.payment.payment.application.port.in.ApprovePaymentUseCase; -import com.smore.payment.payment.application.port.out.ApiInboxPort; -import com.smore.payment.payment.application.port.out.PaymentRepository; -import com.smore.payment.payment.application.port.out.PgClient; -import com.smore.payment.payment.application.port.out.TemporaryPaymentPort; +import com.smore.payment.payment.application.port.out.*; +import com.smore.payment.payment.domain.event.SettlementCalculatedEvent; import com.smore.payment.payment.domain.model.Payment; import com.smore.payment.payment.domain.model.PgResponseResult; import com.smore.payment.payment.domain.model.TemporaryPayment; +import com.smore.payment.payment.domain.service.SettlementAmountCalculator; +import com.smore.payment.shared.outbox.OutboxMessageCreator; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.dao.DataIntegrityViolationException; @@ -24,12 +26,15 @@ @RequiredArgsConstructor public class ApprovePaymentService implements ApprovePaymentUseCase { + private final ApiInboxPort apiInboxPort; private final TemporaryPaymentPort temporaryPaymentPort; private final PaymentRepository paymentRepository; - private final PgClient pgClient; - private final PaymentFinalizeService paymentFinalizeService; + private final PaymentAuditLogService paymentAuditLogService; - private final ApiInboxPort apiInboxPort; + + private final PaymentFinalizeCreate paymentFinalizeCreate; + + private final PgClient pgClient; @Override @Transactional(propagation = Propagation.NOT_SUPPORTED) @@ -89,7 +94,7 @@ public ApprovePaymentResult approve(ApprovePaymentCommand command) { try { // 결제 내역 저장 및 수수료 정책 조회하여 정산금 계산 후 이벤트 발행 - Payment payment = paymentFinalizeService.finalizePayment(temp, pgResult); + Payment payment = paymentFinalizeCreate.finalizePayment(temp, pgResult); // 결제 승인 완료 감사 로그 작성 paymentAuditLogService.logPaymentApprovalSucceeded(payment); diff --git a/payment/src/main/java/com/smore/payment/payment/application/PaymentFinalizeService.java b/payment/src/main/java/com/smore/payment/payment/application/PaymentFinalizeCreate.java similarity index 84% rename from payment/src/main/java/com/smore/payment/payment/application/PaymentFinalizeService.java rename to payment/src/main/java/com/smore/payment/payment/application/PaymentFinalizeCreate.java index 975432d6..e7e0fed2 100644 --- a/payment/src/main/java/com/smore/payment/payment/application/PaymentFinalizeService.java +++ b/payment/src/main/java/com/smore/payment/payment/application/PaymentFinalizeCreate.java @@ -4,6 +4,7 @@ import com.smore.payment.payment.application.facade.dto.FeePolicyResult; import com.smore.payment.payment.application.port.out.OutboxPort; import com.smore.payment.payment.application.port.out.PaymentRepository; +import com.smore.payment.payment.application.port.out.SellerSettlementLedgerRepository; import com.smore.payment.payment.application.port.out.TemporaryPaymentPort; import com.smore.payment.payment.domain.event.SettlementCalculatedEvent; import com.smore.payment.payment.domain.model.Payment; @@ -17,14 +18,17 @@ @Service @RequiredArgsConstructor -public class PaymentFinalizeService { +public class PaymentFinalizeCreate { + private final PaymentRepository paymentRepository; private final FeePolicyFacade feePolicyFacade; - private final OutboxMessageCreator outboxMessageCreator; + private final SellerSettlementLedgerRepository sellerSettlementLedgerRepository; + private final TemporaryPaymentPort temporaryPaymentPort; + private final SettlementAmountCalculator settlementAmountCalculator; + private final OutboxPort outboxPort; - private final PaymentRepository paymentRepository; - private final TemporaryPaymentPort temporaryPaymentPort; + private final OutboxMessageCreator outboxMessageCreator; @Transactional public Payment finalizePayment( @@ -55,6 +59,14 @@ public Payment finalizePayment( settlementAmountCalculator.calculate(payment.getAmount(), policy) ); + sellerSettlementLedgerRepository.saveLedger( + payment.getSellerId(), + "EARN", + settlementEvent.settlementAmount(), + payment.getId(), + payment.getIdempotencyKey() + ); + outboxPort.save(outboxMessageCreator.paymentApproved(payment.createApprovedEvent())); outboxPort.save(outboxMessageCreator.settlementCalculated(settlementEvent)); @@ -62,4 +74,5 @@ public Payment finalizePayment( return payment; } + } diff --git a/payment/src/main/java/com/smore/payment/payment/application/PaymentFinalizeRefund.java b/payment/src/main/java/com/smore/payment/payment/application/PaymentFinalizeRefund.java new file mode 100644 index 00000000..e069a500 --- /dev/null +++ b/payment/src/main/java/com/smore/payment/payment/application/PaymentFinalizeRefund.java @@ -0,0 +1,170 @@ +package com.smore.payment.payment.application; + +import com.smore.payment.payment.application.event.inbound.PaymentRefundEvent; +import com.smore.payment.payment.application.event.outbound.PaymentRefundFailedEvent; +import com.smore.payment.payment.application.event.outbound.PaymentRefundSucceededEvent; +import com.smore.payment.payment.application.facade.CancelPolicyFacade; +import com.smore.payment.payment.application.facade.RefundPolicyFacade; +import com.smore.payment.payment.application.facade.dto.CancelPolicyResult; +import com.smore.payment.payment.application.facade.dto.RefundPolicyResult; +import com.smore.payment.payment.application.port.out.OutboxPort; +import com.smore.payment.payment.application.port.out.PaymentRepository; +import com.smore.payment.payment.application.port.out.PgClient; +import com.smore.payment.payment.domain.model.Payment; +import com.smore.payment.payment.domain.model.PaymentStatus; +import com.smore.payment.payment.domain.model.PgResponseResult; +import com.smore.payment.payment.domain.service.RefundCalculator; +import com.smore.payment.payment.domain.service.RefundDecision; +import com.smore.payment.payment.infrastructure.persistence.inbox.RefundInbox; +import com.smore.payment.payment.infrastructure.persistence.inbox.RefundInboxRepository; +import com.smore.payment.shared.outbox.OutboxMessage; +import com.smore.payment.shared.outbox.OutboxMessageCreator; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; + +@Service +@RequiredArgsConstructor +public class PaymentFinalizeRefund { + + private final RefundInboxRepository refundInboxRepository; + + private final PaymentRepository paymentRepository; + private final CancelPolicyFacade cancelPolicyFacade; + private final RefundPolicyFacade refundPolicyFacade; + private final RefundCalculator refundCalculator; + + private final OutboxMessageCreator outboxCreator; + private final OutboxPort outboxPort; + private final PaymentAuditLogService paymentAuditLogService; + + public record PolicyResult( + boolean refundable, + BigDecimal refundAmount, + String failureReason, + Payment payment + ) {} + + /** + * 1) 정책 판단 + Inbox 상태 갱신 (짧은 TX) + */ + @Transactional + public PolicyResult policyPhaseTx(PaymentRefundEvent event) { + RefundInbox inbox = refundInboxRepository.findByRefundId(event.refundId()) + .orElseThrow(() -> new IllegalStateException("inbox not found. refundId=" + event.refundId())); + + if (inbox.isFinalized()) { + return new PolicyResult(false, BigDecimal.ZERO, "이미 FINALIZED", null); + } + + Payment payment = paymentRepository.findById(event.paymentId()) + .orElseThrow(() -> new IllegalArgumentException("결제 정보를 찾을 수 없습니다. paymentId=" + event.paymentId())); + + CancelPolicyResult cancelResult = cancelPolicyFacade.findApplicablePolicy( + payment.getSellerId(), payment.getCategoryId(), payment.getAuctionType() + ); + + RefundPolicyResult refundResult = refundPolicyFacade.findApplicablePolicy( + payment.getSellerId(), payment.getCategoryId(), payment.getAuctionType() + ); + + RefundDecision decision = refundCalculator.decide(payment, event, cancelResult, refundResult); + + if (!decision.refundable()) { + paymentAuditLogService.logRefundFailed(payment, event, decision.failureReason()); + + inbox.markFailed(decision.failureReason()); + refundInboxRepository.save(inbox); + + // 정책 불가 실패는 “재시도 대상 아님”이 일반적 + OutboxMessage failedMsg = outboxCreator.paymentRefundFailed( + PaymentRefundFailedEvent.of(event.orderId(), event.refundId(), event.refundAmount(), decision.failureReason()) + ); + outboxPort.save(failedMsg); + + return new PolicyResult(false, BigDecimal.ZERO, decision.failureReason(), payment); + } + + inbox.markPolicyPassed(); + refundInboxRepository.save(inbox); + + return new PolicyResult(true, decision.refundAmount(), null, payment); + } + + /** + * 2) PG 요청 전 상태 기록 + */ + @Transactional + public void markPgRequestedTx(PaymentRefundEvent event) { + RefundInbox inbox = refundInboxRepository.findByRefundId(event.refundId()) + .orElseThrow(); + if (inbox.isFinalized()) return; + + inbox.markPgRequested(); + refundInboxRepository.save(inbox); + } + + /** + * PG 실패/시스템 실패 등 재시도 고려 케이스 + */ + @Transactional + public void failSystemTx(PaymentRefundEvent event, String reason, boolean toDlt) { + RefundInbox inbox = refundInboxRepository.findByRefundId(event.refundId()) + .orElseThrow(); + + inbox.increaseRetry(reason); + inbox.markFailed(reason); + refundInboxRepository.save(inbox); + + OutboxMessage msg = toDlt + ? outboxCreator.refundDlt(PaymentRefundFailedEvent.of(event.orderId(), event.refundId(), event.refundAmount(), reason)) + : outboxCreator.paymentRefundFailed(PaymentRefundFailedEvent.of(event.orderId(), event.refundId(), event.refundAmount(), reason)); + + outboxPort.save(msg); + } + + /** + * 3) PG 성공 반영 + Payment 업데이트 + Outbox + Inbox FINALIZED (정합성 핵심) + */ + @Transactional + public void finalizeRefund(PgResponseResult pgResponseResult, PaymentRefundEvent paymentRefundEvent) { + RefundInbox inbox = refundInboxRepository.findByRefundId(paymentRefundEvent.refundId()) + .orElseThrow(); + + if (inbox.isFinalized()) { + return; // 멱등 + } + + inbox.markPgSucceeded(pgResponseResult.transactionKey()); + refundInboxRepository.save(inbox); + + Payment payment = paymentRepository + .findById(paymentRefundEvent.paymentId()) + .orElseThrow(); + + if (payment.getStatus() == PaymentStatus.REFUNDED) { + return; + } + + payment.updateRefund( + pgResponseResult.cancels().cancelReason(), + paymentRefundEvent.refundAmount(), + pgResponseResult.cancels().canceledAt(), + pgResponseResult.cancels().cancelTransactionKey(), + pgResponseResult.cancels().cancelAmount(), + "REFUNDED" + ); + paymentRepository.updateRefund(payment.getId(), payment.getRefund()); + + inbox.markFinalized(); + refundInboxRepository.save(inbox); + + outboxPort.save( + outboxCreator.paymentRefunded( + PaymentRefundSucceededEvent.of(paymentRefundEvent.orderId(), paymentRefundEvent.refundId(), paymentRefundEvent.refundAmount()) + ) + ); + } +} diff --git a/payment/src/main/java/com/smore/payment/payment/application/PaymentRefundService.java b/payment/src/main/java/com/smore/payment/payment/application/PaymentRefundService.java index 895e0c77..d224b431 100644 --- a/payment/src/main/java/com/smore/payment/payment/application/PaymentRefundService.java +++ b/payment/src/main/java/com/smore/payment/payment/application/PaymentRefundService.java @@ -2,6 +2,7 @@ import com.smore.payment.payment.application.event.inbound.PaymentRefundEvent; import com.smore.payment.payment.application.event.outbound.PaymentRefundFailedEvent; +import com.smore.payment.payment.application.event.outbound.PaymentRefundSucceededEvent; import com.smore.payment.payment.application.facade.CancelPolicyFacade; import com.smore.payment.payment.application.facade.RefundPolicyFacade; import com.smore.payment.payment.application.facade.dto.CancelPolicyResult; @@ -11,9 +12,12 @@ import com.smore.payment.payment.application.port.out.PaymentRepository; import com.smore.payment.payment.application.port.out.PgClient; import com.smore.payment.payment.domain.model.Payment; +import com.smore.payment.payment.domain.model.PaymentStatus; import com.smore.payment.payment.domain.model.PgResponseResult; import com.smore.payment.payment.domain.service.RefundCalculator; import com.smore.payment.payment.domain.service.RefundDecision; +import com.smore.payment.payment.infrastructure.persistence.inbox.RefundInbox; +import com.smore.payment.payment.infrastructure.persistence.inbox.RefundInboxRepository; import com.smore.payment.shared.outbox.OutboxMessage; import com.smore.payment.shared.outbox.OutboxMessageCreator; import lombok.RequiredArgsConstructor; @@ -27,15 +31,11 @@ @RequiredArgsConstructor public class PaymentRefundService implements RefundPaymentUseCase { - private final PaymentRepository paymentRepository; - private final CancelPolicyFacade cancelPolicyFacade; - private final RefundPolicyFacade refundPolicyFacade; private final PgClient pgClient; - private final OutboxMessageCreator outboxCreator; - private final OutboxPort outboxPort; - private final RefundCalculator refundCalculator; + private final PaymentAuditLogService paymentAuditLogService; - private final RefundFinalizeService refundFinalizeService; + + private final PaymentFinalizeRefund paymentFinalizeRefund; @Override @Transactional(propagation = Propagation.NOT_SUPPORTED) @@ -43,91 +43,52 @@ public void refund(PaymentRefundEvent event) { paymentAuditLogService.logRefundRequested(event); - Payment payment = paymentRepository.findById(event.paymentId()) - .orElseThrow(() -> new IllegalArgumentException("결제 정보를 찾을 수 없습니다. paymentId=" + event.paymentId())); - - CancelPolicyResult cancelResult = cancelPolicyFacade.findApplicablePolicy( - payment.getSellerId(), - payment.getCategoryId(), - payment.getAuctionType() - ); - - RefundPolicyResult refundResult = refundPolicyFacade.findApplicablePolicy( - payment.getSellerId(), - payment.getCategoryId(), - payment.getAuctionType() - ); - - - RefundDecision refundDecision = refundCalculator.decide(payment, event, cancelResult, refundResult); - if (!refundDecision.refundable()) { - paymentAuditLogService.logRefundFailed(payment, event, refundDecision.failureReason()); - - OutboxMessage failedMsg = outboxCreator.paymentRefundFailed( - PaymentRefundFailedEvent.of(event.orderId(), event.refundId(), event.refundAmount(), refundDecision.failureReason()) - ); - outboxPort.save(failedMsg); + PaymentFinalizeRefund.PolicyResult policy = paymentFinalizeRefund.policyPhaseTx(event); + if (!policy.refundable()) { return; } - final BigDecimal refundAmount = refundDecision.refundAmount(); + paymentFinalizeRefund.markPgRequestedTx(event); PgResponseResult pgRefundResult; try { - - paymentAuditLogService.logPgRefundRequested(payment, event, refundAmount); + paymentAuditLogService.logPgRefundRequested(policy.payment(), event, policy.refundAmount()); pgRefundResult = pgClient.refund( - payment.getPaymentKey(), - refundAmount, + policy.payment().getPaymentKey(), + policy.refundAmount(), event.refundReason() ); - if (!pgRefundResult.pgStatus().equals("CANCELED")) { - paymentAuditLogService.logPgRefundFailed(payment, event, pgRefundResult.failureMessage()); + } catch (RuntimeException e) { + paymentAuditLogService.logPgRefundFailed(policy.payment(), event, e.getMessage()); - OutboxMessage failedMsg = outboxCreator.paymentRefundFailed( - PaymentRefundFailedEvent.of(event.orderId(), event.refundId(), refundDecision.refundAmount(), pgRefundResult.failureMessage()) - ); - outboxPort.save(failedMsg); - } + paymentFinalizeRefund.failSystemTx(event, "PG 환불 요청 실패(시스템 오류): " + e.getMessage(), true); - paymentAuditLogService.logPgRefundSucceeded(payment, event, pgRefundResult); + throw e; + } - } catch (RuntimeException e) { + if (!pgRefundResult.pgStatus().equals("CANCELED")) { + paymentAuditLogService.logPgRefundFailed(policy.payment(), event, pgRefundResult.failureMessage()); - paymentAuditLogService.logPgRefundFailed(payment, event, e.getMessage()); - - outboxPort.save( - outboxCreator.refundDlt( - PaymentRefundFailedEvent.of( - event.orderId(), - event.refundId(), - event.refundAmount(), - "환불 처리 중 시스템 오류 발생: " + e.getMessage() - ) - ) - ); - throw e; + paymentFinalizeRefund.failSystemTx(event, "PG 환불 상태 오류: " + pgRefundResult.failureMessage(), false); + return; } + paymentAuditLogService.logPgRefundSucceeded(policy.payment(), event, pgRefundResult); + try { - refundFinalizeService.finalizeRefund(pgRefundResult, event); - paymentAuditLogService.logRefundSucceeded(payment, event); + paymentFinalizeRefund.finalizeRefund(pgRefundResult, event); + + paymentAuditLogService.logRefundSucceeded(policy.payment(), event); } catch (RuntimeException e) { - paymentAuditLogService.logRefundFailed(payment, event, e.getMessage()); - outboxPort.save( - outboxCreator.refundDlt( - PaymentRefundFailedEvent.of( - event.orderId(), - event.refundId(), - event.refundAmount(), - "환불 처리 중 시스템 오류 발생: " + e.getMessage() - ) - ) - ); + paymentAuditLogService.logRefundFailed(policy.payment(), event, e.getMessage()); + + paymentFinalizeRefund.failSystemTx(event, "finalize 실패: " + e.getMessage(), true); throw e; } } + + } diff --git a/payment/src/main/java/com/smore/payment/payment/application/RefundFinalizeService.java b/payment/src/main/java/com/smore/payment/payment/application/RefundFinalizeService.java deleted file mode 100644 index 2ba5df8c..00000000 --- a/payment/src/main/java/com/smore/payment/payment/application/RefundFinalizeService.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.smore.payment.payment.application; - -import com.smore.payment.payment.application.event.inbound.PaymentRefundEvent; -import com.smore.payment.payment.application.event.outbound.PaymentRefundSucceededEvent; -import com.smore.payment.payment.application.port.out.OutboxPort; -import com.smore.payment.payment.application.port.out.PaymentRepository; -import com.smore.payment.payment.domain.model.Payment; -import com.smore.payment.payment.domain.model.PaymentStatus; -import com.smore.payment.payment.domain.model.PgResponseResult; -import com.smore.payment.shared.outbox.OutboxMessageCreator; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Service -@RequiredArgsConstructor -@Transactional -public class RefundFinalizeService { - - private final PaymentRepository paymentRepository; - private final OutboxPort outboxPort; - private final OutboxMessageCreator outboxCreator; - - public void finalizeRefund(PgResponseResult pgResponseResult, PaymentRefundEvent paymentRefundEvent) { - - Payment payment = paymentRepository - .findById(paymentRefundEvent.paymentId()) - .orElseThrow(); - - if (payment.getStatus() == PaymentStatus.REFUNDED) { - return; - } - - payment.updateRefund( - pgResponseResult.cancels().cancelReason(), - paymentRefundEvent.refundAmount(), - pgResponseResult.cancels().canceledAt(), - pgResponseResult.cancels().cancelTransactionKey(), - pgResponseResult.cancels().cancelAmount(), - "REFUNDED" - ); - paymentRepository.updateRefund(payment.getId(), payment.getRefund()); - - outboxPort.save( - outboxCreator.paymentRefunded( - PaymentRefundSucceededEvent.of(paymentRefundEvent.orderId(), paymentRefundEvent.refundId(), paymentRefundEvent.refundAmount()) - ) - ); - } -} diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/config/WebClientConfig.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/config/WebClientConfig.java index e07b7583..f35cd210 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/config/WebClientConfig.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/config/WebClientConfig.java @@ -21,7 +21,7 @@ public class WebClientConfig { @Bean public WebClient tossApproveWebClient() { String encoded = Base64.getEncoder() - .encodeToString(("test_gsk_docs_OaPz8L5KdmQXkzRz3y47BMw6:") + .encodeToString((pgSecretKey) .getBytes(StandardCharsets.UTF_8)); return WebClient.builder() @@ -35,7 +35,7 @@ public WebClient tossApproveWebClient() { public WebClient tossFailWebClient() { String encoded = Base64.getEncoder() - .encodeToString(("test_gsk_docs_OaPz8L5KdmQXkzRz3y47BMw6:") + .encodeToString((pgSecretKey) .getBytes(StandardCharsets.UTF_8)); return WebClient.builder() diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentRefundEventConsumer.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentRefundEventConsumer.java index 95169946..bc104250 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentRefundEventConsumer.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentRefundEventConsumer.java @@ -4,8 +4,14 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.smore.payment.payment.application.PaymentRefundService; import com.smore.payment.payment.application.event.inbound.PaymentRefundEvent; +import com.smore.payment.payment.application.event.outbound.PaymentRefundFailedEvent; import com.smore.payment.payment.application.port.in.RefundPaymentUseCase; +import com.smore.payment.payment.application.port.out.OutboxPort; import com.smore.payment.payment.infrastructure.kafka.dto.PaymentRefundRequestEvent; +import com.smore.payment.payment.infrastructure.persistence.inbox.RefundInbox; +import com.smore.payment.payment.infrastructure.persistence.inbox.RefundInboxProcessor; +import com.smore.payment.payment.infrastructure.persistence.inbox.RefundInboxRepository; +import com.smore.payment.shared.outbox.OutboxMessageCreator; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.kafka.annotation.KafkaListener; @@ -19,8 +25,14 @@ @RequiredArgsConstructor public class PaymentRefundEventConsumer { + private final RefundInboxProcessor refundInboxProcessor; private final RefundPaymentUseCase paymentRefundService; private final ObjectMapper objectMapper; + private final RefundInboxRepository refundInboxRepository; + private final OutboxPort outboxPort; + private final OutboxMessageCreator outboxMessageCreator; + + private static final Integer MAX_RETRY = 3; @KafkaListener(topics = "order.refund.v1") public void handle(String message, Acknowledgment ack) throws JsonProcessingException { @@ -29,6 +41,25 @@ public void handle(String message, Acknowledgment ack) throws JsonProcessingExce log.info("PaymentRefundRequestEvent 수신: orderId={}, amount={}", event.getOrderId(), event.getRefundAmount()); + RefundInbox inbox = refundInboxRepository + .findByRefundId(event.getRefundId()) + .orElseThrow(); + + if (inbox.getRetryCount() >= MAX_RETRY) { + log.error("재시도 횟수 한계. refundId={}", event.getRefundId()); + + publishRefundDlt(event, inbox); + ack.acknowledge(); + return; + } + + boolean start = refundInboxProcessor.startOrSkip(event); + if (!start) { + // 이미 완료 된 이벤트 또는 처리 중인 이벤트 + ack.acknowledge(); + return; + } + PaymentRefundEvent paymentRequestedEvent = PaymentRefundEvent.of( event.getOrderId(), event.getUserId(), @@ -41,6 +72,30 @@ public void handle(String message, Acknowledgment ack) throws JsonProcessingExce ); paymentRefundService.refund(paymentRequestedEvent); + ack.acknowledge(); } + + private void publishRefundDlt(PaymentRefundRequestEvent event, RefundInbox inbox) { + + String reason = String.format( + "환불 자동 재시도 한계 초과 (retryCount=%d)", + inbox.getRetryCount() + ); + + + outboxPort.save( + outboxMessageCreator.refundDlt( + PaymentRefundFailedEvent.of( + event.getOrderId(), + event.getRefundId(), + BigDecimal.valueOf(event.getRefundAmount()), + reason + ) + ) + ); + + log.error("Refund moved to DLT. refundId={}, reason={}", + event.getRefundId(), reason); + } } diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentSettlementEventConsumer.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentSettlementEventConsumer.java index 9cb6687a..f64fe706 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentSettlementEventConsumer.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentSettlementEventConsumer.java @@ -2,7 +2,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.smore.payment.payment.application.PaymentSettlementService; import com.smore.payment.payment.application.event.inbound.PaymentSettlementRequestEvent; import com.smore.payment.payment.application.port.in.SettlePaymentUseCase; import com.smore.payment.payment.infrastructure.kafka.dto.SettlementRequestEvent; diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/inbox/RefundInbox.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/inbox/RefundInbox.java new file mode 100644 index 00000000..c64c009e --- /dev/null +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/inbox/RefundInbox.java @@ -0,0 +1,124 @@ +package com.smore.payment.payment.infrastructure.persistence.inbox; + +import com.smore.payment.payment.infrastructure.kafka.dto.PaymentRefundRequestEvent; +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; +import java.util.UUID; + +@Entity +@Table( + name = "refund_inbox", + uniqueConstraints = { + @UniqueConstraint( + name = "uk_refund_inbox_refund_id", + columnNames = "refundId" + ) + } +) +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class RefundInbox { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Version + private Long version; + + @Column(nullable = false) + private UUID refundId; + + @Column(nullable = false) + private UUID orderId; + + @Column(nullable = false) + private UUID paymentId; + + @Column + private UUID idempotencyKey; + + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private RefundInboxStatus status; + + @Column(nullable = false) + private int retryCount; + + private String lastError; + + private String pgRefundKey; + + private LocalDateTime createdAt; + private LocalDateTime updatedAt; + + + public static RefundInbox create(PaymentRefundRequestEvent event) { + RefundInbox inbox = new RefundInbox(); + inbox.refundId = event.getRefundId(); + inbox.orderId = event.getOrderId(); + inbox.paymentId = event.getPaymentId(); + inbox.idempotencyKey = event.getIdempotencyKey() == null ? null : event.getIdempotencyKey(); + inbox.status = RefundInboxStatus.RECEIVED; + inbox.retryCount = 0; + inbox.createdAt = LocalDateTime.now(); + inbox.updatedAt = LocalDateTime.now(); + return inbox; + } + + public void markProcessing() { + if (this.status == RefundInboxStatus.FINALIZED) return; + if (this.status == RefundInboxStatus.PROCESSING) return; // 이미 누가 잡음 + this.status = RefundInboxStatus.PROCESSING; + setUpdatedAt(); + } + + public void markPolicyPassed() { + this.status = RefundInboxStatus.POLICY_PASSED; + setUpdatedAt(); + } + + public void markPgRequested() { + this.status = RefundInboxStatus.PG_REQUESTED; + setUpdatedAt(); + } + + public void markPgSucceeded(String pgRefundKey) { + this.status = RefundInboxStatus.PG_SUCCEEDED; + this.pgRefundKey = pgRefundKey; + setUpdatedAt(); + } + + public void markFinalized() { + this.status = RefundInboxStatus.FINALIZED; + setUpdatedAt(); + } + + public void markFailed(String error) { + this.status = RefundInboxStatus.FAILED; + this.lastError = error; + setUpdatedAt(); + } + + public void increaseRetry(String error) { + this.retryCount++; + this.lastError = error; + setUpdatedAt(); + } + + public boolean isFinalized() { + return this.status == RefundInboxStatus.FINALIZED; + } + + public boolean isProcessing() { + return this.status == RefundInboxStatus.PROCESSING; + } + + private void setUpdatedAt() { + this.updatedAt = LocalDateTime.now(); + } +} diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/inbox/RefundInboxProcessor.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/inbox/RefundInboxProcessor.java new file mode 100644 index 00000000..6ee18723 --- /dev/null +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/inbox/RefundInboxProcessor.java @@ -0,0 +1,46 @@ +package com.smore.payment.payment.infrastructure.persistence.inbox; + +import com.smore.payment.payment.infrastructure.kafka.dto.PaymentRefundRequestEvent; +import lombok.RequiredArgsConstructor; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.orm.ObjectOptimisticLockingFailureException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class RefundInboxProcessor { + + private final RefundInboxRepository refundInboxRepository; + + @Transactional + public boolean startOrSkip(PaymentRefundRequestEvent event) { + RefundInbox inbox = refundInboxRepository.findByRefundId(event.getRefundId()) + .orElseGet(() -> { + try { + return refundInboxRepository.save(RefundInbox.create(event)); + } catch (DataIntegrityViolationException e) { + return refundInboxRepository.findByRefundId(event.getRefundId()) + .orElseThrow(() -> e); + } + }); + + // 이미 완료면 처리할 필요 없음 + if (inbox.isFinalized()) { + return false; + } + + // 이미 누가 처리 중이면 skip + if (inbox.isProcessing()) { + return false; + } + + try { + inbox.markProcessing(); + refundInboxRepository.save(inbox); + return true; + } catch (ObjectOptimisticLockingFailureException e) { + return false; + } + } +} diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/inbox/RefundInboxRepository.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/inbox/RefundInboxRepository.java new file mode 100644 index 00000000..766daa87 --- /dev/null +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/inbox/RefundInboxRepository.java @@ -0,0 +1,11 @@ +package com.smore.payment.payment.infrastructure.persistence.inbox; + +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; +import java.util.UUID; + +public interface RefundInboxRepository extends JpaRepository { + + Optional findByRefundId(UUID refundId); +} diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/inbox/RefundInboxStatus.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/inbox/RefundInboxStatus.java new file mode 100644 index 00000000..fabba44a --- /dev/null +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/inbox/RefundInboxStatus.java @@ -0,0 +1,11 @@ +package com.smore.payment.payment.infrastructure.persistence.inbox; + +public enum RefundInboxStatus { + RECEIVED, + PROCESSING, + POLICY_PASSED, + PG_REQUESTED, + PG_SUCCEEDED, + FINALIZED, + FAILED +} \ No newline at end of file diff --git a/payment/src/main/java/com/smore/payment/shared/outbox/OutboxMessageCreator.java b/payment/src/main/java/com/smore/payment/shared/outbox/OutboxMessageCreator.java index 925bd94e..3ef63cbe 100644 --- a/payment/src/main/java/com/smore/payment/shared/outbox/OutboxMessageCreator.java +++ b/payment/src/main/java/com/smore/payment/shared/outbox/OutboxMessageCreator.java @@ -77,7 +77,7 @@ public OutboxMessage paymentFailed(PaymentFailedEvent event) { public OutboxMessage settlementCalculated(SettlementCalculatedEvent event) { return new OutboxMessage( "SETTLEMENT", - UUID.randomUUID(), + event.idempotencyKey(), event.getClass().getSimpleName(), event.idempotencyKey(), sellerApprovedTopic, @@ -116,7 +116,7 @@ public OutboxMessage paymentRefunded(PaymentRefundSucceededEvent event) { public OutboxMessage settlementCompleted(SettlementSuccessEvent event) { return new OutboxMessage( "SETTLEMENT_SUCCESS", - UUID.randomUUID(), + event.idempotencyKey(), event.getClass().getSimpleName(), UUID.randomUUID(), sellerSuccessTopic, @@ -129,7 +129,7 @@ public OutboxMessage settlementCompleted(SettlementSuccessEvent event) { public OutboxMessage settlementFailed(SettlementFailedEvent event) { return new OutboxMessage( "SETTLEMENT_FAILED", - UUID.randomUUID(), + event.idempotencyKey(), event.getClass().getSimpleName(), UUID.randomUUID(), sellerFailedTopic, @@ -142,7 +142,7 @@ public OutboxMessage settlementFailed(SettlementFailedEvent event) { public OutboxMessage settlementDlt(SettlementFailedEvent event) { return new OutboxMessage( "SETTLEMENT_FAILED", - UUID.randomUUID(), + event.idempotencyKey(), event.getClass().getSimpleName(), UUID.randomUUID(), sellerDltTopic, From 1d7c921ce1bddcedf2fd0cf65db38621ac70c6b2 Mon Sep 17 00:00:00 2001 From: ooinl77 Date: Tue, 23 Dec 2025 16:27:41 +0900 Subject: [PATCH 12/16] =?UTF-8?q?refactor:=20kafka=20publish=20=EC=9E=AC?= =?UTF-8?q?=EC=A0=84=EC=86=A1=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kafka/PaymentApprovedOutboxPublisher.java | 8 ++++---- .../infrastructure/persistence/outbox/OutboxEntity.java | 4 ++++ .../persistence/outbox/OutboxJpaRepository.java | 2 ++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentApprovedOutboxPublisher.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentApprovedOutboxPublisher.java index 5e6ae78d..b80e74b4 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentApprovedOutboxPublisher.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentApprovedOutboxPublisher.java @@ -20,7 +20,6 @@ public class PaymentApprovedOutboxPublisher { private final MessageBrokerPublisher messageBrokerPublisher; @Scheduled(fixedDelay = 5000) - @Transactional public void publish() { log.info("이벤트 발행 시작"); List pendingMessages = outboxJpaRepository.findTop50ByStatusOrderByCreatedAtAsc(OutboxStatus.PENDING); @@ -29,10 +28,9 @@ public void publish() { } @Scheduled(fixedDelay = 30000) - @Transactional public void retry() { log.info("이벤트 재전송 시작"); - List pendingMessages = outboxJpaRepository.findTop50ByStatusOrderByCreatedAtAsc(OutboxStatus.FAILED); + List pendingMessages = outboxJpaRepository.findTop50ByStatusAndRetryCountGreaterThanOrderByCreatedAtAsc(OutboxStatus.FAILED, 0); log.info("재전송 아웃박스 찾아오기 {}", pendingMessages.size()); send(pendingMessages); } @@ -51,8 +49,10 @@ private void send(List pendingMessages) { } catch (Exception e) { - if (message.getRetryCount() <= 1) { + if (message.getRetryCount() < 1) { message.markAsFailed(); + message.resetRetryCount(); + log.warn("out box 재전송 실패 : {}", message.getId()); } else { message.decreaseRetryCount(); } diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/outbox/OutboxEntity.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/outbox/OutboxEntity.java index 9e394b57..06d3ca50 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/outbox/OutboxEntity.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/outbox/OutboxEntity.java @@ -69,4 +69,8 @@ public void markAsFailed() { public void decreaseRetryCount() { this.retryCount--; } + + public void resetRetryCount() { + this.retryCount = 0; + } } diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/outbox/OutboxJpaRepository.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/outbox/OutboxJpaRepository.java index 57fd974d..fc05aaa5 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/outbox/OutboxJpaRepository.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/outbox/OutboxJpaRepository.java @@ -7,4 +7,6 @@ public interface OutboxJpaRepository extends JpaRepository { List findTop50ByStatusOrderByCreatedAtAsc(OutboxStatus status); + + List findTop50ByStatusAndRetryCountGreaterThanOrderByCreatedAtAsc(OutboxStatus status, Integer retryCount); } From fae7a0011d97a068af5540315ee37ea96e466189 Mon Sep 17 00:00:00 2001 From: ooinl77 Date: Wed, 24 Dec 2025 11:22:44 +0900 Subject: [PATCH 13/16] =?UTF-8?q?refactor:=20flyway=20=EC=84=A4=EC=A0=95?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- payment/build.gradle | 3 +- .../kafka/PaymentRefundEventConsumer.java | 6 +++- .../kafka/PaymentRequestedEventConsumer.java | 6 +++- .../kafka/PaymentSettlementEventConsumer.java | 6 +++- .../src/main/resources/application-dev.yml | 10 ++++-- payment/src/main/resources/application.yml | 1 - .../migration/V2__insert_initial_data.sql} | 34 +++++++++---------- 7 files changed, 42 insertions(+), 24 deletions(-) rename payment/src/main/resources/{data.sql => db/migration/V2__insert_initial_data.sql} (81%) diff --git a/payment/build.gradle b/payment/build.gradle index 8a64c96b..b0e0ed37 100644 --- a/payment/build.gradle +++ b/payment/build.gradle @@ -54,7 +54,8 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-webflux' -// implementation 'org.flywaydb:flyway-core' + implementation 'org.flywaydb:flyway-core:11.20.0' + implementation 'org.flywaydb:flyway-database-postgresql:11.20.0' } dependencyManagement { diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentRefundEventConsumer.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentRefundEventConsumer.java index bc104250..975fcdac 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentRefundEventConsumer.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentRefundEventConsumer.java @@ -34,7 +34,11 @@ public class PaymentRefundEventConsumer { private static final Integer MAX_RETRY = 3; - @KafkaListener(topics = "order.refund.v1") + @KafkaListener( + topics = "order.refund.v1", + groupId = "consumer.refund.group", + concurrency = "3" + ) public void handle(String message, Acknowledgment ack) throws JsonProcessingException { PaymentRefundRequestEvent event = objectMapper.readValue(message, PaymentRefundRequestEvent.class); diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentRequestedEventConsumer.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentRequestedEventConsumer.java index 9893cd83..b3227ea9 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentRequestedEventConsumer.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentRequestedEventConsumer.java @@ -22,7 +22,11 @@ public class PaymentRequestedEventConsumer { private final CreateTemporaryPaymentService createTemporaryPaymentService; private final ObjectMapper objectMapper; - @KafkaListener(topics = "order.created.v1") + @KafkaListener( + topics = "order.created.v1", + groupId = "consumer.payment.group", + concurrency = "3" + ) public void handle(String message, Acknowledgment ack) throws JsonProcessingException { PaymentCreateRequestEvent event = objectMapper.readValue(message, PaymentCreateRequestEvent.class); diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentSettlementEventConsumer.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentSettlementEventConsumer.java index f64fe706..15293f92 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentSettlementEventConsumer.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentSettlementEventConsumer.java @@ -19,7 +19,11 @@ public class PaymentSettlementEventConsumer { private final SettlePaymentUseCase paymentSettlementService; private final ObjectMapper objectMapper; - @KafkaListener(topics = "seller.settlement.v1") + @KafkaListener( + topics = "seller.settlement.v1", + groupId = "consumer.settlement.group", + concurrency = "3" + ) public void handle(String message, Acknowledgment ack) throws JsonProcessingException { SettlementRequestEvent event = objectMapper.readValue(message, SettlementRequestEvent.class); diff --git a/payment/src/main/resources/application-dev.yml b/payment/src/main/resources/application-dev.yml index a49fcfc0..a60e38ae 100644 --- a/payment/src/main/resources/application-dev.yml +++ b/payment/src/main/resources/application-dev.yml @@ -20,7 +20,7 @@ spring: sql: init: - mode: always + mode: never datasource: url: jdbc:postgresql://postgresql:5432/payment @@ -33,6 +33,12 @@ spring: idle-timeout: 30000 pool-name: HikariPool + flyway: + enabled: true + locations: classpath:db/migration + baseline-on-migrate: true + validate-on-migrate: true + data: mongodb: uri: mongodb://root:examplepassword@mongodb:27017/default?authSource=admin @@ -44,7 +50,7 @@ spring: jpa: database-platform: org.hibernate.dialect.PostgreSQLDialect hibernate: - ddl-auto: create + ddl-auto: validate show-sql: true properties: hibernate: diff --git a/payment/src/main/resources/application.yml b/payment/src/main/resources/application.yml index 14d84f84..d6aa9cdb 100644 --- a/payment/src/main/resources/application.yml +++ b/payment/src/main/resources/application.yml @@ -2,7 +2,6 @@ spring: application: name: payment-service - management: endpoints: web: diff --git a/payment/src/main/resources/data.sql b/payment/src/main/resources/db/migration/V2__insert_initial_data.sql similarity index 81% rename from payment/src/main/resources/data.sql rename to payment/src/main/resources/db/migration/V2__insert_initial_data.sql index 0a1a1001..8cbca74e 100644 --- a/payment/src/main/resources/data.sql +++ b/payment/src/main/resources/db/migration/V2__insert_initial_data.sql @@ -12,17 +12,17 @@ INSERT INTO fee_policies ( created_at, updated_at ) VALUES - ( - '1b5e6f7a-8b9c-4dad-9e0f-1a2b3c4d5e6f', - 'MERCHANT', - '2002', - 'RATE', - 0.02, - 500.0, - true, - now(), - now() - ); + ( + gen_random_uuid(), + 'MERCHANT', + '2002', + 'RATE', + 0.02, + 500.0, + true, + now(), + now() + ); -- Cancel policies INSERT INTO cancel_policies ( @@ -39,7 +39,7 @@ INSERT INTO cancel_policies ( updated_at ) VALUES ( - '5f0a1b2c-3d4e-5f6a-9b7c-8d9e0f1a2b3k', + gen_random_uuid(), 'MERCHANT', '2002', 1800000000000, -- 3 days = 259,200 seconds = 259,200,000,000,000 ns @@ -52,7 +52,7 @@ INSERT INTO cancel_policies ( now() ), ( - '3d8e9f0a-1b2c-4d3e-9f4a-5b6c7d8e9f0a', + gen_random_uuid(), 'AUCTION_TYPE', 'BID', 1800000000000, -- 30 minutes = 1800초 = 1,800,000,000,000 ns @@ -65,7 +65,7 @@ INSERT INTO cancel_policies ( now() ), ( - '3d8e9f0a-1b2c-4d3e-9f4a-5b6c7d8e9f0b', + gen_random_uuid(), 'AUCTION_TYPE', 'AUCTION', 10000000000, -- 1 minutes = 1800초 = 1,800,000,000,000 ns @@ -93,7 +93,7 @@ INSERT INTO refund_policies ( updated_at ) VALUES ( - '5f0a1b2c-3d4e-5f6a-9b7c-8d9e0f1a2b3a', + gen_random_uuid(), 'MERCHANT', '2002', 259200000000000, -- 3 days = 259,200 seconds = 259,200,000,000,000 ns @@ -106,7 +106,7 @@ INSERT INTO refund_policies ( now() ), ( - '5f0a1b2c-3d4e-5f6a-9b7c-8d9e0f1a2b3c', + gen_random_uuid(), 'AUCTION_TYPE', 'BID', 259200000000000, -- 3 days = 259,200 seconds = 259,200,000,000,000 ns @@ -119,7 +119,7 @@ INSERT INTO refund_policies ( now() ), ( - '5f0a1b2c-3d4e-5f6a-9b7c-8d9e0f1a2b3d', + gen_random_uuid(), 'AUCTION_TYPE', 'AUCTION', 259200000000000, -- 3 days = 259,200 seconds = 259,200,000,000,000 ns From 10c637a785e468c176584a05bf34ae62e0c6460e Mon Sep 17 00:00:00 2001 From: ooinl77 Date: Wed, 24 Dec 2025 12:35:26 +0900 Subject: [PATCH 14/16] =?UTF-8?q?refactor:=20init=20ddl=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infrastructure/config/RedisConfig.java | 25 +++++++++++++++++++ .../persistence/model/CancelPolicyEntity.java | 2 +- .../persistence/model/RefundPolicyEntity.java | 2 +- .../src/main/resources/application-dev.yml | 3 +++ payment/src/main/resources/application.yml | 11 +++++--- .../db/migration/V1__init_payment_schema.sql | 21 ++++++++++++++-- .../db/migration/V2__insert_initial_data.sql | 12 ++++----- 7 files changed, 63 insertions(+), 13 deletions(-) diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/config/RedisConfig.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/config/RedisConfig.java index 01563835..59300773 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/config/RedisConfig.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/config/RedisConfig.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.smore.payment.payment.application.port.in.ApprovePaymentResult; import com.smore.payment.payment.domain.model.TemporaryPayment; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -40,4 +41,28 @@ public RedisTemplate temporaryPaymentRedisTemplate( template.afterPropertiesSet(); return template; } + + @Bean + public RedisTemplate approvePaymentResultRedisTemplate( + RedisConnectionFactory connectionFactory) { + + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerModule(new JavaTimeModule()); + objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + + Jackson2JsonRedisSerializer serializer = + new Jackson2JsonRedisSerializer<>(ApprovePaymentResult.class); + + template.setKeySerializer(new StringRedisSerializer()); + template.setHashKeySerializer(new StringRedisSerializer()); + + template.setValueSerializer(serializer); + template.setHashValueSerializer(serializer); + + template.afterPropertiesSet(); + return template; + } } diff --git a/payment/src/main/java/com/smore/payment/policy/cancel/infrastructure/persistence/model/CancelPolicyEntity.java b/payment/src/main/java/com/smore/payment/policy/cancel/infrastructure/persistence/model/CancelPolicyEntity.java index c200bfb3..40b0d017 100644 --- a/payment/src/main/java/com/smore/payment/policy/cancel/infrastructure/persistence/model/CancelPolicyEntity.java +++ b/payment/src/main/java/com/smore/payment/policy/cancel/infrastructure/persistence/model/CancelPolicyEntity.java @@ -29,7 +29,7 @@ public class CancelPolicyEntity extends BaseEntity { @Column(name = "target_key", nullable = false, updatable = false) private String targetKey; - @Column(name = "cancel_limit_minutes") + @Column(name = "cancel_limit_minutes", columnDefinition = "interval") private Duration cancelLimitMinutes; @Enumerated(EnumType.STRING) diff --git a/payment/src/main/java/com/smore/payment/policy/refund/infrastructure/persistence/model/RefundPolicyEntity.java b/payment/src/main/java/com/smore/payment/policy/refund/infrastructure/persistence/model/RefundPolicyEntity.java index 7ca54d82..04ebe739 100644 --- a/payment/src/main/java/com/smore/payment/policy/refund/infrastructure/persistence/model/RefundPolicyEntity.java +++ b/payment/src/main/java/com/smore/payment/policy/refund/infrastructure/persistence/model/RefundPolicyEntity.java @@ -29,7 +29,7 @@ public class RefundPolicyEntity extends BaseEntity { @Column(name = "target_key", nullable = false, updatable = false) private String targetKey; - @Column(name = "refund_period_days") + @Column(name = "refund_period_days", columnDefinition = "interval") private Duration refundPeriodDays; @Enumerated(EnumType.STRING) diff --git a/payment/src/main/resources/application-dev.yml b/payment/src/main/resources/application-dev.yml index a60e38ae..ef814057 100644 --- a/payment/src/main/resources/application-dev.yml +++ b/payment/src/main/resources/application-dev.yml @@ -55,6 +55,8 @@ spring: properties: hibernate: format_sql: true + type: + preferred_duration_jdbc_type: INTERVAL_SECOND kafka: # 로컬에서 Docker로 띄운 브로커 외부 포트에 맞춰 설정 (docker-compose의 EXTERNAL 매핑) @@ -96,6 +98,7 @@ topic: failed: payment.failed.v1 refund: payment.refund.v1 refund-fail: payment.refund-fail.v1 + refund-dlt: payment.refund-dlt.v1 seller: approved: payment.settlement.v1 success: payment.settlement-success.v1 diff --git a/payment/src/main/resources/application.yml b/payment/src/main/resources/application.yml index d6aa9cdb..aaf38ad4 100644 --- a/payment/src/main/resources/application.yml +++ b/payment/src/main/resources/application.yml @@ -5,7 +5,12 @@ spring: management: endpoints: web: - discovery: - enabled: true exposure: - include: "*" \ No newline at end of file + include: prometheus,health,info + metrics: + tags: + application: payment-service + endpoint: + health: + show-details: always + show-components: always \ No newline at end of file diff --git a/payment/src/main/resources/db/migration/V1__init_payment_schema.sql b/payment/src/main/resources/db/migration/V1__init_payment_schema.sql index f603c977..5ffd8d9d 100644 --- a/payment/src/main/resources/db/migration/V1__init_payment_schema.sql +++ b/payment/src/main/resources/db/migration/V1__init_payment_schema.sql @@ -20,7 +20,7 @@ CREATE TABLE cancel_policies id UUID PRIMARY KEY, cancel_target_type VARCHAR(50) NOT NULL, target_key VARCHAR(255) NOT NULL, - cancel_limit_minutes BIGINT, + cancel_limit_minutes INTERVAL, cancel_fee_type VARCHAR(50) NOT NULL, rate NUMERIC(10, 4), fixed_amount NUMERIC(19, 2), @@ -39,7 +39,7 @@ CREATE TABLE refund_policies id UUID PRIMARY KEY, refund_target_type VARCHAR(50) NOT NULL, target_key VARCHAR(255) NOT NULL, - refund_period_days BIGINT, + refund_period_days INTERVAL, refund_fee_type VARCHAR(50) NOT NULL, rate NUMERIC(10, 4), fixed_amount NUMERIC(19, 2), @@ -110,4 +110,21 @@ CREATE TABLE outboxes retry_count INTEGER, status VARCHAR(50), created_at TIMESTAMP +); + +CREATE TABLE refund_inbox +( + id BIGSERIAL PRIMARY KEY, + version BIGINT, + refund_id UUID NOT NULL, + order_id UUID NOT NULL, + payment_id UUID NOT NULL, + idempotency_key UUID, + status VARCHAR(50) NOT NULL, + retry_count INTEGER NOT NULL, + last_error VARCHAR(255), + pg_refund_key VARCHAR(255), + created_at TIMESTAMP, + updated_at TIMESTAMP, + CONSTRAINT uk_refund_inbox_refund_id UNIQUE (refund_id) ); \ No newline at end of file diff --git a/payment/src/main/resources/db/migration/V2__insert_initial_data.sql b/payment/src/main/resources/db/migration/V2__insert_initial_data.sql index 8cbca74e..210b5187 100644 --- a/payment/src/main/resources/db/migration/V2__insert_initial_data.sql +++ b/payment/src/main/resources/db/migration/V2__insert_initial_data.sql @@ -42,7 +42,7 @@ INSERT INTO cancel_policies ( gen_random_uuid(), 'MERCHANT', '2002', - 1800000000000, -- 3 days = 259,200 seconds = 259,200,000,000,000 ns + interval '30 minutes', -- 3 days = 259,200 seconds = 259,200,000,000,000 ns 'MIXED', 0.04, 300, @@ -55,7 +55,7 @@ INSERT INTO cancel_policies ( gen_random_uuid(), 'AUCTION_TYPE', 'BID', - 1800000000000, -- 30 minutes = 1800초 = 1,800,000,000,000 ns + interval '30 minutes', -- 30 minutes = 1800초 = 1,800,000,000,000 ns 'MIXED', 0.03, 250, @@ -68,7 +68,7 @@ INSERT INTO cancel_policies ( gen_random_uuid(), 'AUCTION_TYPE', 'AUCTION', - 10000000000, -- 1 minutes = 1800초 = 1,800,000,000,000 ns + interval '30 minutes', -- 1 minutes = 1800초 = 1,800,000,000,000 ns 'RATE', 100.0, 0.0, @@ -96,7 +96,7 @@ INSERT INTO refund_policies ( gen_random_uuid(), 'MERCHANT', '2002', - 259200000000000, -- 3 days = 259,200 seconds = 259,200,000,000,000 ns + interval '3 days', -- 3 days = 259,200 seconds = 259,200,000,000,000 ns 'MIXED', 0.04, 300, @@ -109,7 +109,7 @@ INSERT INTO refund_policies ( gen_random_uuid(), 'AUCTION_TYPE', 'BID', - 259200000000000, -- 3 days = 259,200 seconds = 259,200,000,000,000 ns + interval '3 days', -- 3 days = 259,200 seconds = 259,200,000,000,000 ns 'MIXED', 0.04, 300, @@ -122,7 +122,7 @@ INSERT INTO refund_policies ( gen_random_uuid(), 'AUCTION_TYPE', 'AUCTION', - 259200000000000, -- 3 days = 259,200 seconds = 259,200,000,000,000 ns + interval '3 days', -- 3 days = 259,200 seconds = 259,200,000,000,000 ns 'MIXED', 0.04, 300, From 2945f899ac83661cac2c14c0f1cf349172926dd2 Mon Sep 17 00:00:00 2001 From: ooinl77 Date: Thu, 25 Dec 2025 15:18:18 +0900 Subject: [PATCH 15/16] =?UTF-8?q?refactor:=20=ED=99=98=EB=B6=88=20?= =?UTF-8?q?=EC=9E=AC=EC=8B=9C=EB=8F=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/PaymentFinalizeRefund.java | 41 ++++++++++++++----- .../infrastructure/config/RedisConfig.java | 2 +- .../shared/outbox/OutboxMessageCreator.java | 13 ++++++ .../src/main/resources/application-dev.yml | 4 ++ .../db/migration/V2__insert_initial_data.sql | 15 ++++++- 5 files changed, 62 insertions(+), 13 deletions(-) diff --git a/payment/src/main/java/com/smore/payment/payment/application/PaymentFinalizeRefund.java b/payment/src/main/java/com/smore/payment/payment/application/PaymentFinalizeRefund.java index e069a500..1e8e245a 100644 --- a/payment/src/main/java/com/smore/payment/payment/application/PaymentFinalizeRefund.java +++ b/payment/src/main/java/com/smore/payment/payment/application/PaymentFinalizeRefund.java @@ -40,12 +40,7 @@ public class PaymentFinalizeRefund { private final OutboxPort outboxPort; private final PaymentAuditLogService paymentAuditLogService; - public record PolicyResult( - boolean refundable, - BigDecimal refundAmount, - String failureReason, - Payment payment - ) {} + private static final Long MAX_RETRY_COUNT = 3L; /** * 1) 정책 판단 + Inbox 상태 갱신 (짧은 TX) @@ -115,12 +110,30 @@ public void failSystemTx(PaymentRefundEvent event, String reason, boolean toDlt) .orElseThrow(); inbox.increaseRetry(reason); - inbox.markFailed(reason); + + boolean retryable = inbox.getRetryCount() <= MAX_RETRY_COUNT; + + if (!retryable) { + inbox.markFailed(reason); + } refundInboxRepository.save(inbox); - OutboxMessage msg = toDlt - ? outboxCreator.refundDlt(PaymentRefundFailedEvent.of(event.orderId(), event.refundId(), event.refundAmount(), reason)) - : outboxCreator.paymentRefundFailed(PaymentRefundFailedEvent.of(event.orderId(), event.refundId(), event.refundAmount(), reason)); + PaymentRefundFailedEvent payload = + PaymentRefundFailedEvent.of( + event.orderId(), + event.refundId(), + event.refundAmount(), + reason + ); + + OutboxMessage msg; + if (retryable) { + msg = outboxCreator.refundRetry(payload); + } else if (toDlt) { + msg = outboxCreator.refundDlt(payload); + } else { + msg = outboxCreator.paymentRefundFailed(payload); + } outboxPort.save(msg); } @@ -167,4 +180,12 @@ public void finalizeRefund(PgResponseResult pgResponseResult, PaymentRefundEvent ) ); } + + public record PolicyResult( + boolean refundable, + BigDecimal refundAmount, + String failureReason, + Payment payment + ) { + } } diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/config/RedisConfig.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/config/RedisConfig.java index 59300773..52b8d2ae 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/config/RedisConfig.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/config/RedisConfig.java @@ -54,7 +54,7 @@ public RedisTemplate approvePaymentResultRedisTemp objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); Jackson2JsonRedisSerializer serializer = - new Jackson2JsonRedisSerializer<>(ApprovePaymentResult.class); + new Jackson2JsonRedisSerializer<>(objectMapper, ApprovePaymentResult.class); template.setKeySerializer(new StringRedisSerializer()); template.setHashKeySerializer(new StringRedisSerializer()); diff --git a/payment/src/main/java/com/smore/payment/shared/outbox/OutboxMessageCreator.java b/payment/src/main/java/com/smore/payment/shared/outbox/OutboxMessageCreator.java index 3ef63cbe..bbb94b45 100644 --- a/payment/src/main/java/com/smore/payment/shared/outbox/OutboxMessageCreator.java +++ b/payment/src/main/java/com/smore/payment/shared/outbox/OutboxMessageCreator.java @@ -164,4 +164,17 @@ public OutboxMessage refundDlt(PaymentRefundFailedEvent event) { OutboxStatus.FAILED ); } + + public OutboxMessage refundRetry(PaymentRefundFailedEvent event) { + return new OutboxMessage( + "REFUND_FAILED", + event.orderId(), + event.getClass().getSimpleName(), + UUID.randomUUID(), + orderRefundDltTopic, + jsonUtil.jsonToString(event), + 3, + OutboxStatus.FAILED + ); + }; } diff --git a/payment/src/main/resources/application-dev.yml b/payment/src/main/resources/application-dev.yml index ef814057..1e6d1172 100644 --- a/payment/src/main/resources/application-dev.yml +++ b/payment/src/main/resources/application-dev.yml @@ -46,6 +46,10 @@ spring: redis: host: redis-stack port: 6379 + password: "" + database: 0 + ssl: + enabled: false jpa: database-platform: org.hibernate.dialect.PostgreSQLDialect diff --git a/payment/src/main/resources/db/migration/V2__insert_initial_data.sql b/payment/src/main/resources/db/migration/V2__insert_initial_data.sql index 210b5187..df02559f 100644 --- a/payment/src/main/resources/db/migration/V2__insert_initial_data.sql +++ b/payment/src/main/resources/db/migration/V2__insert_initial_data.sql @@ -12,6 +12,17 @@ INSERT INTO fee_policies ( created_at, updated_at ) VALUES + ( + gen_random_uuid(), + 'MERCHANT', + '2001', + 'RATE', + 0.02, + 500.0, + true, + now(), + now() + ), ( gen_random_uuid(), 'MERCHANT', @@ -41,7 +52,7 @@ INSERT INTO cancel_policies ( ( gen_random_uuid(), 'MERCHANT', - '2002', + '2001', interval '30 minutes', -- 3 days = 259,200 seconds = 259,200,000,000,000 ns 'MIXED', 0.04, @@ -95,7 +106,7 @@ INSERT INTO refund_policies ( ( gen_random_uuid(), 'MERCHANT', - '2002', + '2001', interval '3 days', -- 3 days = 259,200 seconds = 259,200,000,000,000 ns 'MIXED', 0.04, From a89d297e7056f13521e96df2baa92adabbe256f2 Mon Sep 17 00:00:00 2001 From: ooinl77 Date: Fri, 26 Dec 2025 11:09:07 +0900 Subject: [PATCH 16/16] =?UTF-8?q?fix:=20=ED=94=BC=EB=93=9C=EB=B0=B1=20?= =?UTF-8?q?=E3=85=9C=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kafka/PaymentRefundEventConsumer.java | 43 ------------------- .../inbox/RefundInboxProcessor.java | 42 ++++++++++++++++++ .../shared/outbox/OutboxMessageCreator.java | 6 +-- 3 files changed, 45 insertions(+), 46 deletions(-) diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentRefundEventConsumer.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentRefundEventConsumer.java index 975fcdac..e0f9f3b1 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentRefundEventConsumer.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/kafka/PaymentRefundEventConsumer.java @@ -2,16 +2,10 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.smore.payment.payment.application.PaymentRefundService; import com.smore.payment.payment.application.event.inbound.PaymentRefundEvent; -import com.smore.payment.payment.application.event.outbound.PaymentRefundFailedEvent; import com.smore.payment.payment.application.port.in.RefundPaymentUseCase; -import com.smore.payment.payment.application.port.out.OutboxPort; import com.smore.payment.payment.infrastructure.kafka.dto.PaymentRefundRequestEvent; -import com.smore.payment.payment.infrastructure.persistence.inbox.RefundInbox; import com.smore.payment.payment.infrastructure.persistence.inbox.RefundInboxProcessor; -import com.smore.payment.payment.infrastructure.persistence.inbox.RefundInboxRepository; -import com.smore.payment.shared.outbox.OutboxMessageCreator; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.kafka.annotation.KafkaListener; @@ -28,11 +22,6 @@ public class PaymentRefundEventConsumer { private final RefundInboxProcessor refundInboxProcessor; private final RefundPaymentUseCase paymentRefundService; private final ObjectMapper objectMapper; - private final RefundInboxRepository refundInboxRepository; - private final OutboxPort outboxPort; - private final OutboxMessageCreator outboxMessageCreator; - - private static final Integer MAX_RETRY = 3; @KafkaListener( topics = "order.refund.v1", @@ -45,17 +34,6 @@ public void handle(String message, Acknowledgment ack) throws JsonProcessingExce log.info("PaymentRefundRequestEvent 수신: orderId={}, amount={}", event.getOrderId(), event.getRefundAmount()); - RefundInbox inbox = refundInboxRepository - .findByRefundId(event.getRefundId()) - .orElseThrow(); - - if (inbox.getRetryCount() >= MAX_RETRY) { - log.error("재시도 횟수 한계. refundId={}", event.getRefundId()); - - publishRefundDlt(event, inbox); - ack.acknowledge(); - return; - } boolean start = refundInboxProcessor.startOrSkip(event); if (!start) { @@ -80,26 +58,5 @@ public void handle(String message, Acknowledgment ack) throws JsonProcessingExce ack.acknowledge(); } - private void publishRefundDlt(PaymentRefundRequestEvent event, RefundInbox inbox) { - String reason = String.format( - "환불 자동 재시도 한계 초과 (retryCount=%d)", - inbox.getRetryCount() - ); - - - outboxPort.save( - outboxMessageCreator.refundDlt( - PaymentRefundFailedEvent.of( - event.getOrderId(), - event.getRefundId(), - BigDecimal.valueOf(event.getRefundAmount()), - reason - ) - ) - ); - - log.error("Refund moved to DLT. refundId={}, reason={}", - event.getRefundId(), reason); - } } diff --git a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/inbox/RefundInboxProcessor.java b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/inbox/RefundInboxProcessor.java index 6ee18723..9b816b29 100644 --- a/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/inbox/RefundInboxProcessor.java +++ b/payment/src/main/java/com/smore/payment/payment/infrastructure/persistence/inbox/RefundInboxProcessor.java @@ -1,17 +1,29 @@ package com.smore.payment.payment.infrastructure.persistence.inbox; +import com.smore.payment.payment.application.event.outbound.PaymentRefundFailedEvent; +import com.smore.payment.payment.application.port.out.OutboxPort; import com.smore.payment.payment.infrastructure.kafka.dto.PaymentRefundRequestEvent; +import com.smore.payment.shared.outbox.OutboxMessageCreator; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.orm.ObjectOptimisticLockingFailureException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.math.BigDecimal; + +@Slf4j @Service @RequiredArgsConstructor public class RefundInboxProcessor { private final RefundInboxRepository refundInboxRepository; + private final OutboxPort outboxPort; + private final OutboxMessageCreator outboxMessageCreator; + + private static final Integer MAX_RETRY = 3; + @Transactional public boolean startOrSkip(PaymentRefundRequestEvent event) { @@ -35,6 +47,13 @@ public boolean startOrSkip(PaymentRefundRequestEvent event) { return false; } + if (inbox.getRetryCount() >= MAX_RETRY) { + log.error("재시도 횟수 한계. refundId={}", event.getRefundId()); + + publishRefundDlt(event, inbox); + return false; + } + try { inbox.markProcessing(); refundInboxRepository.save(inbox); @@ -43,4 +62,27 @@ public boolean startOrSkip(PaymentRefundRequestEvent event) { return false; } } + + private void publishRefundDlt(PaymentRefundRequestEvent event, RefundInbox inbox) { + + String reason = String.format( + "환불 자동 재시도 한계 초과 (retryCount=%d)", + inbox.getRetryCount() + ); + + + outboxPort.save( + outboxMessageCreator.refundDlt( + PaymentRefundFailedEvent.of( + event.getOrderId(), + event.getRefundId(), + BigDecimal.valueOf(event.getRefundAmount()), + reason + ) + ) + ); + + log.error("Refund moved to DLT. refundId={}, reason={}", + event.getRefundId(), reason); + } } diff --git a/payment/src/main/java/com/smore/payment/shared/outbox/OutboxMessageCreator.java b/payment/src/main/java/com/smore/payment/shared/outbox/OutboxMessageCreator.java index bbb94b45..a3847ede 100644 --- a/payment/src/main/java/com/smore/payment/shared/outbox/OutboxMessageCreator.java +++ b/payment/src/main/java/com/smore/payment/shared/outbox/OutboxMessageCreator.java @@ -167,11 +167,11 @@ public OutboxMessage refundDlt(PaymentRefundFailedEvent event) { public OutboxMessage refundRetry(PaymentRefundFailedEvent event) { return new OutboxMessage( - "REFUND_FAILED", - event.orderId(), + "REFUND_RETRY", + event.refundId(), event.getClass().getSimpleName(), UUID.randomUUID(), - orderRefundDltTopic, + "order.refund.v1", jsonUtil.jsonToString(event), 3, OutboxStatus.FAILED