Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,14 @@
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.Base64;
import java.util.List;

import static com.permitseoul.permitserver.global.util.LogFormUtil.maskPaymentKey;
import static net.logstash.logback.argument.StructuredArguments.keyValue;

@Slf4j
Expand Down Expand Up @@ -130,14 +134,11 @@ public PaymentConfirmResponse getPaymentConfirm(final long userId,

final TossPaymentResponse tossPaymentResponse = getTossPaymentConfirm(authorizationHeader, paymentKey, reservation.getOrderId(), reservation.getTotalAmount());

log.info("토스 결제 승인 완료",
(Object[]) LogFormUtil.paymentLog(
userId,
tossPaymentResponse.orderId(),
tossPaymentResponse.paymentKey(),
reservation.getReservationId(),
tossPaymentResponse.totalAmount()
)
log.info("[Payment] 토스 결제 승인 완료 - orderId={}, paymentKey={}, reservationId={}, amount={}",
tossPaymentResponse.orderId(),
LogFormUtil.maskPaymentKey(tossPaymentResponse.paymentKey()),
reservation.getReservationId(),
tossPaymentResponse.totalAmount()
);

updateReservationStatusAndTossPaymentResponseTime(reservation.getReservationId(), ReservationStatus.PAYMENT_SUCCESS);
Expand Down Expand Up @@ -202,23 +203,25 @@ public PaymentConfirmResponse getPaymentConfirm(final long userId,
deleteReservationSessionByOrderId(orderId);
throw handleFeignException(e, orderId, userId);

} catch (AlgorithmException e) { //todo: 결제는 됐는데, 티켓 발급 과정에서 실패했으므로, 따로 알림 구축해놔야될듯
logPaymentSuccessButTicketIssueFailed(userId, reservationSessionKey, orderId, totalAmount, paymentKey, reservation.getReservationId());
} catch (AlgorithmException e) {
logPaymentSuccessButTicketIssueFailed(orderId, totalAmount, paymentKey, reservation.getReservationId());
throw new TicketAlgorithmException(ErrorCode.INTERNAL_TICKET_ALGORITHM_ERROR);

} catch (IllegalEnumTransitionException e) { //todo: 결제는 됐는데, 티켓 발급 과정에서 실패했으므로, 따로 알림 구축해놔야될듯
logPaymentSuccessButTicketIssueFailed(userId, reservationSessionKey, orderId, totalAmount, paymentKey, reservation.getReservationId());
} catch (IllegalEnumTransitionException e) {
logPaymentSuccessButTicketIssueFailed(orderId, totalAmount, paymentKey, reservation.getReservationId());
throw new ReservationIllegalException(ErrorCode.INTERNAL_TRANSITION_ENUM_ERROR);

} catch (ReservationSessionNotFoundAfterPaymentSuccessException e) { //결제는 됐는데, 티켓 발급 과정에서 실패했으므로, 따로 알림 구축해놔야될듯
logPaymentSuccessButTicketIssueFailed(userId, reservationSessionKey, orderId, totalAmount, paymentKey, reservation.getReservationId());
} catch (ReservationSessionNotFoundAfterPaymentSuccessException e) {
logPaymentSuccessButTicketIssueFailed(orderId, totalAmount, paymentKey, reservation.getReservationId());
throw new NotFoundPaymentException(ErrorCode.NOT_FOUND_RESERVATION_SESSION_AFTER_PAYMENT_SUCCESS);
}
}

public void cancelPayment(final long userId, final String orderId) {
try {
final Payment payment = paymentRetriever.findPaymentByOrderId(orderId);
validateCancelAvailablePeriod(payment.getEventId());

final List<Ticket> ticketList = ticketRetriever.findAllTicketsByOrderIdAndUserId(payment.getOrderId(), userId);
validateTicketStatusForCancel(ticketList);

Expand All @@ -245,6 +248,8 @@ public void cancelPayment(final long userId, final String orderId) {

} catch (PaymentNotFoundException e) {
throw new NotFoundPaymentException(ErrorCode.NOT_FOUND_PAYMENT);
} catch (EventNotfoundException e) {
throw new NotFoundPaymentException(ErrorCode.NOT_FOUND_EVENT);
} catch(FeignException e) {
throw handleFeignException(e, orderId, userId);
} catch (TicketNotFoundException e) {
Expand All @@ -263,6 +268,23 @@ public void cancelPayment(final long userId, final String orderId) {
}
}

private void validateCancelAvailablePeriod(final long eventId) {
final Event event = eventRetriever.findEventById(eventId);

final LocalDate eventDate = event.getStartAt().toLocalDate();
final LocalDate today = LocalDate.now(ZoneId.of("Asia/Seoul"));

// 오늘과 행사일 사이의 일수 계산
long daysUntilEvent = ChronoUnit.DAYS.between(today, eventDate);

// 3일 전까지만 환불 가능
if (daysUntilEvent < 3) {
log.warn("[Payment Cancel] 취소 기한 초과 - eventId={}, eventDate={}, today={}, daysUntilEvent={}",
eventId, eventDate, today, daysUntilEvent);
throw new PaymentBadRequestException(ErrorCode.BAD_REQUEST_CANCEL_PERIOD_EXPIRED);
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

private void deleteReservationSessionByOrderId(final String orderId) {
try {
reservationSessionRemover.deleteByOrderId(orderId);
Expand Down Expand Up @@ -302,18 +324,15 @@ private void handleFailedTossPayment(final Reservation reservation,
}
}

private void logPaymentSuccessButTicketIssueFailed( final long userId,
final String sessionKey,
final String orderId,
final BigDecimal totalAmount,
final String paymentKey,
final long reservationId) {
log.error("토스 결제 승인 완료 -> 티켓 발급 실패",
keyValue(Constants.USER_ID, userId),
keyValue(Constants.ORDER_ID, orderId),
keyValue(Constants.PAYMENT_KEY, LogFormUtil.maskPaymentKey(paymentKey)),
keyValue(Constants.RESERVATION_ID, reservationId),
keyValue(Constants.TOTAL_AMOUNT, totalAmount)
private void logPaymentSuccessButTicketIssueFailed(final String orderId,
final BigDecimal totalAmount,
final String paymentKey,
final long reservationId) {
log.error("[Payment] 토스 결제 승인 완료 -> 티켓 발급 실패 - orderId={}, paymentKey={}, reservationId={}, amount={}",
orderId,
LogFormUtil.maskPaymentKey(paymentKey),
reservationId,
totalAmount
);
}

Expand Down Expand Up @@ -341,7 +360,7 @@ private void logRollbackFailed(final long userId,
final BigDecimal totalAmount,
final String paymentKey) {
log.error("[결제 승인 API - redis Rollback Failed] userId: {}, sessionKey: {}, orderId: {}, totalAmount: {}, paymentKey: {}",
userId, sessionKey, orderId, totalAmount, LogFormUtil.maskPaymentKey(paymentKey));
userId, sessionKey, orderId, totalAmount, maskPaymentKey(paymentKey));
}

private void updateReservationStatusAndTossPaymentResponseTime(final long reservationId, final ReservationStatus status) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public enum ErrorCode implements ApiCode {
BAD_REQUEST_MISMATCH_TICKET_TYPE_ROUND(HttpStatus.BAD_REQUEST, 40016, "ticketType의 roundId와 다른 ticketRoundId 입니다."),
BAD_REQUEST_MISMATCH_LIST_SIZE(HttpStatus.BAD_REQUEST, 40017, "list의 길이가 다릅니다."),
BAD_REQUEST_REDIS_TICKET_TYPE_MISMATCH(HttpStatus.BAD_REQUEST, 40018, "redis ticket tpye mismatch 에러입니다. "),
BAD_REQUEST_CANCEL_PERIOD_EXPIRED(HttpStatus.BAD_REQUEST, 40019, "환불 가능 기간이 아닙니다. 환불은 행사 시작일 기준 3일전까지만 가능합니다.(환불 정책 참고)."),



Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
package com.permitseoul.permitserver.global.util;

import com.permitseoul.permitserver.global.Constants;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.experimental.UtilityClass;
import net.logstash.logback.argument.StructuredArgument;

import java.math.BigDecimal;

import static net.logstash.logback.argument.StructuredArguments.keyValue;

@UtilityClass
public final class LogFormUtil {
Expand All @@ -22,22 +14,6 @@ public final class LogFormUtil {
private static final int PAYMENT_KEY_LONG_PREFIX = 4;
private static final int PAYMENT_KEY_LONG_SUFFIX = 5;

public static StructuredArgument[] paymentLog(
final long userId,
final String orderId,
final String paymentKey,
final long reservationId,
final BigDecimal totalAmount
) {
return new StructuredArgument[] {
keyValue(Constants.USER_ID, userId),
keyValue(Constants.ORDER_ID, orderId),
keyValue(Constants.PAYMENT_KEY, maskPaymentKey(paymentKey)),
keyValue(Constants.RESERVATION_ID, reservationId),
keyValue(Constants.TOTAL_AMOUNT, totalAmount)
};
}

public static String maskPaymentKey(final String paymentKey) {
if (paymentKey == null || paymentKey.isBlank()) {
return paymentKey;
Expand Down