본문 바로가기

자바 심화/TIL

결제 기능 구현

개요

주문 배달 관리 시스템에서 주문/결제 부분을 개발하게 되었고 주문에 대한 결제를 어떤 식으로 구현할 지에 대해 고민해 보았다.

 

1. 주문이 생성되면 해당 주문의 정보를 토대로 결제 정보를 만들고 저장한다.

  • 고객의 주문이 들어오면 해당 주문을 가지고 Payment(결제 정보)를 구현하고 DB에 저장했다.
  • Payment에는 주문 식별값(order_id), UUID(외부로 노출할 Unique 컬럼), 결제 비용(amount), 결제 상태(대기, 완료, 취소, 실패), 결제 종류(카드, 외부 결제 등)이 저장된다.

2. 결제 상태를 활용하기.

  • 초기 API 명세서를 만들 때 결제 관련 API는 하나로 만들고 주문 부분부터 개발을 시작했다.
  • 결제 상태(대기, 완료, 취소, 실패)에는 여러 가지가 있는데 주문 API가 하나라면 결제를 진행만 하는 API 하나만 있을 때 상태가 있을 필요성이 없다고 생각했다.
  • 추가적으로 주문을 만들 때 결제 정보를 가지고 결제 대기 상태로 DB에 저장한 후 결제 진행 시 정보를 가져와 상태를 변경하는 것이 로직 상으로 더 맞을 것이라고 판단했기 때문에 팀원에게 의견을 전달해 결제 등록(주문 등록과 같이 되도록)과 결제 진행을 나누기로 했다.

3. 시간 문제

  • 프로젝트 진행 중 시간 상의 문제로 결제 취소 처리와 결제 실패 시 재 결제에 대한 부분을 구현하지 못한 점이 아쉽다.

 

결제 관련 코드

결제를 진행하는데 필요한 클래스 코드들 중 일부

결제 등록 - 주문 등록과 같이 진행

// OrderService 코드 일부
public OrderPayment createOrder(OrderForCreate orderForCreate) {
    User user = validateUserIdAndGet(orderForCreate.userId());
    checkUserRoleForOrderType(user.getRole(), orderForCreate.orderType());

    productService.validateProductsAndGetProductList(orderForCreate.productList());
    shopService.validateShopIdAndGetShop(orderForCreate.shopId());

    String orderId = orderOutPutPort.saveOrder(orderForCreate);
    String paymentId = paymentService.createPayment(orderId);

    return new OrderPayment(orderId, paymentId);
}
  • 주문이 성공적으로 생성되면 paymentService의 createPayment(orderId)를 호출한다.
//paymentService 코드 일부
public String createPayment(String orderId) {

    return paymentOutputPort.savePayment(orderId);
}
  • 주문 ID와 함께 결제 정보를 저장하는 savePayment(orderId)를 호출한다.
//Payment Adapter 코드 일부
@Transactional
@Override
public String savePayment(String orderId) {

    OrderEntity orderEntity = orderRepository.findByOrderUuid(orderId).get();
    PaymentEntity paymentEntity = paymentRepository.save(PaymentEntity.from(orderEntity));
    orderEntity.updatePayment(paymentEntity);

    return paymentEntity.getPaymentUuid();
}
  • OrderId를 기준으로 Order 정보를 가져와 Payment를 저장하고 Order에 Payment 정보를 업데이트 한 후 필요한 값을 반환하도록 했다.

결제 진행(실패 처리 포함)

결제 등록과 다르게 주문과 별개의 상황에서 진행되도록 코드를 설계했다.

// paymentService 코드 일부
public Payment processPayment(PaymentForUpdate paymentForUpdate) {

    Order order = OrderUserIdInvalidAndGetOrder(paymentForUpdate.orderUuid(), paymentForUpdate.updatedUserId());
    validateOrderCheckCanceled(order);
    List<OrderProduct> orderProducts = orderProductService.findOrderProductsByOrderId(paymentForUpdate.orderUuid());

    int amount = calculateTotalOrderPrice(orderProducts);
    return paymentOutputPort.processPayment(paymentForUpdate, amount);
}
  • 결제 진행 시 결제에 필요한 정보를 받아와 결제 금액을 계산하고 주문 상태를 변경한 후 결제 진행에 필요한 정보를 반환한다.
    • 실제 결제 과정과 차이가 있는 것 같고, 수정이 필요하다, 추후 결제 시스템에 대해 더 공부하고 리팩토링을 진행해야 겠다.
// payment Adapter 코드 일부
@Transactional
@Override
public Payment processPayment(PaymentForUpdate paymentForUpdate, int amount) {
    PaymentEntity paymentEntity = paymentRepository.findByPaymentUuid(paymentForUpdate.paymentUuid()).get();

    try {
        paymentEntity.processPayment(paymentForUpdate, amount);
    } catch (PaymentProcessingException e) {
        paymentEntity.failedState();

        throw new PaymentFailedException(e);
    }

    return  paymentEntity.toDomain();
}
  • 결제 진행 중 문제가 발생하면 실패 처리를 할 수 있도록 try-catch문을 사용해 exception이 발생하면 결제 상태를 실패로 변경하고 예외와 메시지를 클라이언트로 반환하도록 설정했다.

 

마무리

  • 짧은 시간동안 주문 등록, 주문 변경 및 취소, 조회, 결제 등의 기능을 만들어야 되서 많이 미흡한 점도 있었고 충분히 공부할 시간도 부족했다.
  • 추후 결제 시스템에 대한 공부를 통해 현재 결제 시스템을 개선하고 싶다.

'자바 심화 > TIL' 카테고리의 다른 글

클린 코드 1  (2) 2024.11.19
페이지네이션 Offset vs Cursor  (2) 2024.11.18
PostgreSQL 기초  (1) 2024.11.14
아키텍처(Architecture)  (0) 2024.11.12
데이터베이스 PK 타입 및 페이지네이션 관련 의사 결정  (1) 2024.11.11