망취소 환경 구축

망취소?

금융/결제 관련 업무를 진행해보셨다면 망취소라는 단어를 접해보셨을분들이 많이 계시겠지만, 그렇지 않은 경우라면 이 단어자체가 생소하신분도 많이 계실것 같습니다.
한글단어로써는 무슨 의미인지 잘 이해가 되지 않을수 있지만, 영단어로 번역해보면 net cancel으로 벌써 어떤 내용인지 벌써 감이 오신분이 있을듯 합니다. 먼저 망취소에 대해서 간략하게 먼저 설명드려보자면 신용카드, 계좌 등의 결제수단으로 거래를 하는경우 일정기간내에 취소할수 있게 하는 기능으로 일반적으로 비정상적으로 거래가 실패한 경우에 자동으로 결제를 취소하고 금액을 복구시키는 과정을 말합니다.

망취소 프로세스가 어떤 상황에서 발생 하게되는지 아래 예시 상황을 보면 조금 더 이해가 잘 되실듯 합니다.

  • 클라이언트에서 카드 결제 요청 보냄
  • 통신 장애로 인해 결제 요청 타임아웃 발생
  • 실제 서버에서는 결제가 정상적으로 처리됨
  • 클라이언트에서는 결제 실패로 판단
  • 망취소 -> 정상적으로 응답받지 않은 거래 취소처리

위와같은 상황외에도 망취소가 발생 할 수 있는 상황은 얼마든지 더 있을 수 있습니다.


망취소 진행시 주의할점

위 예시에서 보여드렸던것 처럼 망취소가 일어나는 상황은 정상적인 결제 성공응답을 받지 못했을때 이뤄지는 경우가 많습니다. 여기서 생각해봐야 할 건, 일반적은 결제취소의 경우 결제 성공 응답에서 결제ID 와 같은 응답값을 파라미터로 취소요청을 보내게 되는데 망취소에서는 결제ID를 받지 못하는 상황에서 취소요청을 보낼수 있어야하는다는 점을 고려해야합니다.

이러한 이유로 결제 요청을 보낼때 요청ID를 보내고, 해당 요청 ID 로 망취소를 할 수 있도록 만들어야 합니다.

일반 결제취소 flow

- 결제 요청 `{"paymentAmount":500}`
- 결제성공 응답 `{"paymentId":"2022-0923-AAAA-0001"}`
- 결제취소 요청 `{"paymentId:"2022-0923-AAAA-0001"}`

망취소 flow

- 결제 요청 `{"paymentAmount":500, "paymentRequestId":"2022-0923-ZZZZ-0001")`
- 결제성공 응답 : `X (응답 못받음)`
- 망취소 요청 `{"paymentRequestId":"2022-0923-ZZZZ-0001")`

한가지 더 고려해야할점은 아무래도 금융권 프로세스의 경우 트랜잭션이 길어 처리에 시간이 오래걸리는 경우가 꽤나 있는데, 이로인해 제공되는 API들도 보장 타임아웃시간이 꽤나 긴 편입니다.
이와같이 프로세스 처리시간이 길어 타임아웃이 발생한 경우에는 바로 취소요청을 보내면 서버상에서는 아직 결제 처리가 완료되지 않았기 때문에 망취소가 실패하는 경우가 발생 할 수도 있습니다. 이러한 상황을 고려하여 망취소를 어느정도 시간이 지난후에 요청을 보내거나, 타임아웃 발생한 케이스라면 망취소가 성공할때까지 재시도를 하는 등의 방식으로 보완을 해줘야합니다.


망취소 프로세스 구축방안 고민

제가 구현하고자 했던 상황은 조금은 더 장애가 많이 발생할수 있는 환경에서 망취소 시스템을 구축하려고 했습니다. 내부 포인트 시스템에서 외부 결제 시스템을 연동하는 프로젝트를 진행하는데 결제시스템에서 제공하는 보장 타임아웃이 1분이상을 요구하는 상황이었습니다. 제 판단으로는 저희 서비스에서 고객에게 제공 할수 있는 보장 타임아웃은 최대 10초 이내로 제공해야한다고 생각했기 때문에 외부결제 연동에서 요구하는 타임아웃을 다 맞춰주다보면 고객들에게 제공되는 타임아웃 또한 늘어나 운영 서비스상 불편함이 증대될것으로 판단했습니다.

이러한 이유로 자체적으로 외부 결제 타임아웃을 5초 으로 제한을 두고 그 이상 걸리는 요청에 대해서는 타임아웃 처리하고, 망취소를 수행한다는 정책을 세우는 방식으로 고객 사용성을 우선하는 방향으로 서비스를 제공하는것으로 결정했습니다.

이와같은 정책을 세웠기때문에, 위에서 설명드렸던것 처럼 타임아웃 상황에서 취소하는 부분에 대해서 더욱이 신경 써야할 부분이 많았습니다. 아래에서 고민했던 구현방향을 공유드리겠습니다.

고민했던 방향1 - Kafka 이벤트를 활용한 지연발행

1

  • 망취소를 담당하는 모듈을 별도로 분리하여 일반 프로세스와는 물리적으로 독립적으로 수행할수 있도록 구분했습니다.
  • 타임아웃 상황에서 바로 취소요청을 보낼시 외부 결제모듈에서는 아직 결제정보가 생성되지 않았기때문에 망취소가 실패할 가능성이 있어 1분정도의 망취소 지연요청을 보내도록 합니다.
  • 지연발행을 위한 별도의 프로세스를 구현해야 합니다. (event delay queue)

고민했던 방향2 - EventSourcing 테이블 + 배치를 이용한 망취소처리

2

  • 망취소를 담당하는 모듈을 별도로 분리하여 일반 프로세스와는 물리적으로 독립적으로 수행할수 있도록 구분했습니다.
  • 결제관련 상태를 기록하는 이벤트 소스를 테이블에 기록합니다.
  • 실패응답건을 배치에서 주기적으로 (1분마다) 조회해 망취소 처리합니다.

선택한 방법

위 두가지 고민했던 방향 모두 망취소를 처리하기에 좋은 방법들이라고 생각이라고 들었지만 선택한건 2번째 방법이었습니다.
해당 방법을 선택한 가장 큰 이유는 타임아웃이 보장되지 않는 환경에서 망취소 처리가 확실히 되는것이 중요했는데, 아무래도 주기적으로 실패거래건을 폴링방식으로 가지고와서 처리하는 배치방식이 망취소에 실패하더라도 재시도 처리하며 망취소를 보장하기 좋다고 판단하였고 아무래도 구현 방식도 비교적 간단하기 때문에 유지보수 측면으로도 유리하다고 생각했습니다.

만약 망취소 처리가 충분히 보장될 수 있는 환경이라면 지연발행 큐 관련 솔루션들 활용해 1번 방향인 이벤트 방식도 좋은 선택지가 될 수 있다고 생각합니다.


마무리

망취소라는 용어가 생소하기도 합니다만 여기까지 읽으신분들은 느끼셨을텐데, 해당 프로세스는 단지 결제상황에서만 사용되는것이 아니라 MSA 환경에서 보상패턴을 처리하는 프로세스에서도 충분히 발생할만한 상황일것입니다. 예를들어 물류 관리 환경에서는 재고차감 API 를 원복해야하는 상황이 발생할수도 있고, 배달 API의 경우 배송이 취소되어야 한다는 등의 MSA으로 구성된 시스템에서도 충분히 활용하고 고려해야하는 프로세스가 될 수 있습니다.