99import com .roome .domain .payment .entity .Payment ;
1010import com .roome .domain .payment .entity .PaymentLog ;
1111import com .roome .domain .payment .entity .PaymentStatus ;
12+ import com .roome .domain .payment .entity .PointProduct ;
1213import com .roome .domain .payment .repository .PaymentLogRepository ;
1314import com .roome .domain .payment .repository .PaymentRepository ;
1415import com .roome .domain .point .entity .Point ;
@@ -60,11 +61,20 @@ public PaymentResponseDto requestPayment(Long userId, PaymentRequestDto requestD
6061 User user = userRepository .findById (userId )
6162 .orElseThrow (() -> new BusinessException (ErrorCode .USER_NOT_FOUND ));
6263
64+ // 금액이 판매 중인 상품 가격인지 검증하고, 지급 포인트는 클라이언트 값이 아닌 카탈로그에서 파생한다.
65+ PointProduct product = PointProduct .findByPrice (requestDto .getAmount ())
66+ .orElseThrow (() -> new BusinessException (ErrorCode .INVALID_PAYMENT_AMOUNT ));
67+
68+ if (product .getPoints () != requestDto .getPurchasedPoints ()) {
69+ log .warn ("포인트 수량 위변조 의심: userId={}, orderId={}, 요청 포인트={}, 카탈로그 포인트={}" ,
70+ userId , requestDto .getOrderId (), requestDto .getPurchasedPoints (), product .getPoints ());
71+ }
72+
6373 Payment payment = Payment .builder ()
6474 .user (user )
6575 .orderId (requestDto .getOrderId ())
66- .amount (requestDto . getAmount ())
67- .purchasedPoints (requestDto . getPurchasedPoints ())
76+ .amount (product . getPrice ())
77+ .purchasedPoints (product . getPoints ())
6878 .status (PaymentStatus .PENDING )
6979 .paymentKey (null ) // 결제 성공 후 업데이트 예정
7080 .build ();
@@ -151,8 +161,10 @@ public PaymentResponseDto verifyPayment(Long userId, PaymentVerifyDto verifyDto)
151161 paymentRepository .save (payment );
152162
153163 // 사용자 포인트 지급
154- PointReason pointReason = getPointReasonForAmount (payment .getPurchasedPoints ());
155- pointService .earnPoints (payment .getUser (), pointReason );
164+ // Toss가 승인한 결제 금액(payment.amount)을 기준으로 카탈로그에서 지급 사유를 파생
165+ PointProduct product = PointProduct .findByPrice (payment .getAmount ())
166+ .orElseThrow (() -> new BusinessException (ErrorCode .INVALID_PAYMENT_AMOUNT ));
167+ pointService .earnPoints (payment .getUser (), product .getEarnReason ());
156168
157169 // 결제 내역 로그 저장
158170 savePaymentLog (payment , verifyDto .getPaymentKey ());
@@ -192,12 +204,7 @@ public PaymentResponseDto cancelPayment(Long userId, String paymentKey, String c
192204 throw new BusinessException (ErrorCode .PAYMENT_ACCESS_DENIED );
193205 }
194206
195- List <PointReason > purchaseReasons = List .of (
196- PointReason .POINT_PURCHASE_100 ,
197- PointReason .POINT_PURCHASE_550 ,
198- PointReason .POINT_PURCHASE_1200 ,
199- PointReason .POINT_PURCHASE_4000
200- );
207+ List <PointReason > purchaseReasons = PointProduct .purchaseReasons ();
201208 PageRequest pageRequest = PageRequest .of (0 , 1 ); // 최신 1개만 조회
202209
203210 List <PointHistory > latestPurchases = pointHistoryRepository .findLatestPurchase (userId , purchaseReasons , pageRequest );
@@ -226,7 +233,13 @@ public PaymentResponseDto cancelPayment(Long userId, String paymentKey, String c
226233 throw new BusinessException (ErrorCode .PAYMENT_NOT_CANCELABLE );
227234 }
228235
229- int refundPoints = getRefundPointsForAmount (cancelAmount );
236+ // 환불 금액이 판매 중인 상품 가격 단위인지 검증하고, 차감 포인트를 카탈로그에서 파생한다.
237+ if (cancelAmount == null ) {
238+ throw new BusinessException (ErrorCode .INVALID_REFUND_AMOUNT );
239+ }
240+ PointProduct refundProduct = PointProduct .findByPrice (cancelAmount )
241+ .orElseThrow (() -> new BusinessException (ErrorCode .INVALID_REFUND_AMOUNT ));
242+ int refundPoints = refundProduct .getPoints ();
230243
231244 // Toss API에 결제 취소 요청
232245 boolean isCanceled = tossPaymentClient .cancelPayment (payment .getPaymentKey (), cancelReason ,
@@ -240,7 +253,7 @@ public PaymentResponseDto cancelPayment(Long userId, String paymentKey, String c
240253 paymentRepository .save (payment );
241254
242255 // 사용자 포인트 차감
243- pointService .usePoints (payment .getUser (), getRefundReasonForAmount ( refundPoints ));
256+ pointService .usePoints (payment .getUser (), refundProduct . getRefundReason ( ));
244257
245258 saveRefundLog (payment , cancelAmount , paymentKey );
246259
@@ -297,33 +310,4 @@ private void saveRefundLog(Payment payment, int refundAmount, String paymentKey)
297310 }
298311
299312
300- private PointReason getPointReasonForAmount (int purchasedPoints ) {
301- return switch (purchasedPoints ) {
302- case 100 -> PointReason .POINT_PURCHASE_100 ;
303- case 550 -> PointReason .POINT_PURCHASE_550 ;
304- case 1200 -> PointReason .POINT_PURCHASE_1200 ;
305- case 4000 -> PointReason .POINT_PURCHASE_4000 ;
306- default -> throw new BusinessException (ErrorCode .INVALID_PAYMENT_AMOUNT );
307- };
308- }
309-
310- private PointReason getRefundReasonForAmount (int refundPoints ) {
311- return switch (refundPoints ) {
312- case 100 -> PointReason .POINT_REFUND_100 ;
313- case 550 -> PointReason .POINT_REFUND_550 ;
314- case 1200 -> PointReason .POINT_REFUND_1200 ;
315- case 4000 -> PointReason .POINT_REFUND_4000 ;
316- default -> throw new BusinessException (ErrorCode .INVALID_REFUND_POINT_AMOUNT );
317- };
318- }
319-
320- private int getRefundPointsForAmount (int cancelAmount ) {
321- return switch (cancelAmount ) {
322- case 1000 -> 100 ;
323- case 5000 -> 550 ;
324- case 10000 -> 1200 ;
325- case 30000 -> 4000 ;
326- default -> throw new BusinessException (ErrorCode .INVALID_REFUND_AMOUNT );
327- };
328- }
329313}
0 commit comments