Spring

Spring Boot로 결제되지 않은 주문을 30분 후 자동으로 취소하는 방법

Written by 개발자서동우 · 40 sec read >
Spring Boot 자동 주문 취소

결제와 관련된 비즈니스 상황에서는, 주문이 생성된 후 일정 시간 내에 결제가 완료되지 않으면 시스템이 자동으로 해당 주문을 취소하는 기능이 종종 필요합니다.

이번 글에서는 Spring Boot를 사용해 주문이 생성된 후 30분 이내에 결제가 완료되지 않았을 때 자동으로 주문이 취소되도록 하는 여러 가지 방법을 예제 코드와 함께 설명해 보겠습니다.

방법 1: 스케줄 작업 (Scheduled Tasks) 사용하기

@Scheduled 어노테이션을 이용하면, 주기적으로 주문 내역을 스캔하는 스케줄 작업을 손쉽게 구현할 수 있습니다.

30분이 지나고 결제가 완료되지 않은 주문이 있으면 해당 주문은 자동으로 취소됩니다.

@Component
public class OrderSchedule {
    private final OrderService orderService;

    public OrderSchedule(OrderService orderService) {
        this.orderService = orderService;
    }

    @Scheduled(cron = "0 0/1 * * * ?")
    public void cancelUnpaidOrders() {
        LocalDateTime now = LocalDateTime.now();
        List<Integer> idList = orderService.getUnpaidOrders(now.minusMinutes(30));
        orderService.cancelOrderList(idList);
    }
}

방법 2: 지연 큐 (Delayed Queue) 사용하기

메시지 큐와 지연 큐를 활용하면, 주문이 생성될 때 해당 주문의 ID를 30분 후에 만료되도록 지연 큐에 푸시할 수 있습니다.

지연 시간이 지나면 메시지가 처리되고, 결제가 완료되지 않은 주문은 자동으로 취소됩니다.

@Service
public class OrderService {
    private final RabbitTemplate rabbitTemplate;

    public OrderService(RabbitTemplate rabbitTemplate) {
        this.rabbitTemplate = rabbitTemplate;
    }

    public void createOrder(Order order) {
        saveOrder(order);
        sendOrderToDelayQueue(order.getId(), 30 * 60 * 1000);
    }

    private void sendOrderToDelayQueue(Integer orderId, int delayInMillis) {
        rabbitTemplate.convertAndSend("orderDelayExchange", "orderDelayKey", orderId, message -> {
            message.getMessageProperties().setDelay(delayInMillis);
            return message;
        });
    }
}

@Component
public class OrderDelayConsumer {

    private final OrderService orderService;

    @Autowired
    public OrderDelayConsumer(OrderService orderService) {
        this.orderService = orderService;
    }

    @RabbitHandler
    @RabbitListener(queues = "orderDelayQueue")
    public void cancelOrder(String orderId) {
        orderService.cancelOrder(orderId);
    }
}

방법 3: Redis 만료 이벤트 사용하기

Redis의 키 만료 이벤트를 활용하면, 주문이 생성될 때 해당 주문의 키를 30분 뒤에 만료되도록 Redis에 저장할 수 있습니다.

키가 만료되면 Redis가 이벤트를 트리거하여 해당 주문을 취소하도록 합니다.

@Service
public class OrderService {

    private final StringRedisTemplate redisTemplate;

    @Autowired
    public OrderService(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public void createOrder(Order order) {
        saveOrder(order);
        storeOrderInRedis(order.getId(), 30, TimeUnit.MINUTES);
    }

    private void storeOrderInRedis(String orderId, long timeout, TimeUnit unit) {
        redisTemplate.opsForValue().set("order:" + orderId, orderId, timeout, unit);
    }

    public void onOrderKeyExpired(String orderId) {
        cancelOrder(orderId);
    }
}

참고: Redis의 키 만료 알림 기능은 일반적인 퍼블리시-구독 패턴을 따릅니다.

Redis에서는 키 만료와 같은 특정 이벤트를 구독할 수 있습니다. 이 기능을 사용하려면 Redis 서버를 적절히 설정해야 하며, 구체적인 설정 방법은 다음 글에서 다룰 예정입니다.

마무리

위에서 설명한 세 가지 방법 모두 주문 생성 후 30분 이내에 결제가 이루어지지 않으면 자동으로 주문을 취소하는 요구사항을 충족할 수 있습니다.

비즈니스 요구사항, 시스템 부하, 기타 요인을 고려해 본인 시스템에 가장 적합한 방법을 선택할 수 있습니다.

각 방법마다 장단점이 있으므로 신중하게 고려해 적절한 방법을 선택해야 합니다.

Written by 개발자서동우
안녕하세요! 저는 기술 분야에서 활동 중인 개발자 서동우입니다. 명품 플랫폼 (주)트렌비의 창업 멤버이자 CTO로 활동했으며, AI 기술회사 (주)헤드리스의 공동 창업자이자 CTO로서 역할을 수행했습니다. 다양한 스타트업에서 일하며 회사의 성장과 더불어 비즈니스 상황에 맞는 기술 선택, 개발팀 구성 및 문화 정착에 깊은 경험을 쌓았습니다. 개발 관련 고민은 언제든지 편하게 연락주세요 :) https://linktr.ee/dannyseo Profile

Leave a Reply

Your email address will not be published. Required fields are marked *