Tech Dive

'사장이지' 인앱 결제 백엔드 개발#1: 결제 서비스 신뢰성 확보를 위한 노력

들어가며

유튜브 프리미엄 구독, 네이버 웹툰 쿠키 구매... 다들 인앱 결제 한 번쯤은 해보셨죠?

사용자 입장에서는 간단합니다.
 
버튼 클릭 → 앱스토어 결제 → "결제 완료!"
그런데 잠깐, 정말 끝일까요? 🤔

올초 신규 프로젝트에 들어가면서 인앱 결제가 필요하다고 했을 때 간단하다고 생각을 했습니다.

게다가 ‘인앱’ 결제이니까 백엔드 개발자 입장에서는 더욱더 그렇게 생각을 했었습니다.

이 글에서는 KT 사장이지 인앱 결제 백엔드를 개발하며 마주친 현실적인 문제들과 해결 과정을 공유합니다. 

같은 고민을 하고 계신 개발자분들에게 조금이라도 도움이 되길 바라며 시작하겠습니다.

 

사장이지 서비스를 소개합니다! 

올해 KT에서는 사장이지 라는 사장님들을 위한 다양한 서비스를 한 곳에서 제공하는 앱을 출시했습니다.

기존에 각각 따로 운영되던 하이오더, 기가아이즈(CCTV), KT 서빙로봇  같은 서비스를 하나의 플랫폼에서 통합해 관리할 수 있도록 하고, 

여기에 AI 이미지 제작, AI 전화, AI 매장음악 서비스 등 사장님들이 실제 매장에서 도움이 되는 다양한 AI 서비스들도 제공합니다. 


1_2.gif

그림. 사장이지 - AI 매장음악



실제 인앱 결제는 여기서 나온 ‘AI 매장음악’ 의 구독형 상품 결제에 들어가 있습니다.

사장이지는 여러 서비스를 연계하는 시스템이다 보니 인앱결 제 백엔드를 구현할 때는 아래와 같은 목표로 설계를 진행하게 되었습니다.
 
개발 목표
단순한 "결제 완료" 기능이 아닌, 여러 제휴사와 연결된 구조에서도 실패에 안전하고
중복 처리 없는 신뢰성있는 시스템 구축


인앱 결제란?

우선 더 이야기 하기전에 인앱 결제 먼저 살펴 보고 넘어가겠습니다.

인앱 결제는 앱 내에서 상품, 서비스 활성화 등을 구글 플레이, 앱스토어와 같은 앱 마켓을 통해 구매할 수 있는 결제 방식입니다. 

01.png

그림. 기본적인 인앱 결제 구조


인앱 결제 백엔드의 역할은?

일반 결제 프로세스를 보면 결제 생성/승인/검증/취소/정산/서비스 제공 등 여러 절차들이 있는데 이걸 인앱 결제와 비교해보면 ‘인앱’ 결제이다보니 기본적인 결제 플로우는 앱과 앱 마켓이 중심이 되어 진행됩니다.

그러면 인앱 결제 과정에서 백엔드의 역할은 뭘까요?
 
버튼 클릭 → 앱스토어 결제 → "결제 완료!"
그런데 잠깐, 정말 끝일까요? 🤔

백엔드 개발자 입장에서는 이제부터가 시작입니다:

  • 앱스토어에서 넘어온 결제 정보가 진짜 맞는지 검증
  • 사장이지 시스템에 구독/구매 정보 저장
  • 실제 서비스 활성화 (프리미엄 기능 열어주기, 쿠키 지급하기 등)

특히 ‘AI 매장음악’의 기능은 사장이지를 통해 제휴사에 서비스 활성화 처리를 시켜야 합니다.

얼핏 보면 별것 없어 보이는데 돈이 관련된 문제라 발생할 수 있는 인앱 결제 실패 시나리오들은 사용성, 유저 경험 측면에서 치명적입니다.

따라서 인앱 결제 백엔드는 결제 승인 후 서비스 활성화까지 포함한 전체 흐름의 신뢰성을 확보하는 것이 핵심 과제입니다.

신뢰성 확보를 위한 고려 사항들

사장이지 내 인앱 결제 흐름을 좀 더 구체화 시켜봤습니다.

결제 승인 후 서비스 활성화까지 포함한 전체 흐름의 신뢰성을 위해 
백엔드 입장에서는 인앱 결제 시 고민 해야 할 사항들이 생겼습니다.

고려 사항 1 : 동일 결제 중복 처리 이슈
고려 사항 2 : 분산 환경에서의 데이터 일관성 관리
02.png

그림 . 사장이지 인앱 결제 구조



동일 결제 중복 처리 이슈

문제 상황

한번 결제했는데 상품이 여러번 제공될 수 있을까요?

처음에는 저런 상황이 발생하겠어? 라고 생각하였으나 찾아 보니 생각보다 발생할 수 있는 상황들이 있었습니다. (결제 승인/갱신/취소 포함)

상황
상세 원인
클라이언트 앱
재시작 시나리오
Google Play 에서 앱 결제 완료 후 호출되는 콜백 처리 중에 앱 크래시 되는 경우 앱 재시작 시 동일한 결제 결과 값으로 중복 발생 가능
네트워크 이슈 등으로 인한
중복 발송
백엔드 일시적인 이슈로 인해 처리 지연 발생 시 [클라이언트 -> 백엔드], [앱마켓 -> 백엔드] 간 재시도 등으로 인해 동일 요청 반복 호출 가능
멀티 디바이스 환경
동일한 앱 마켓 계정으로 여러 단말에 로그인되어 있는 경우 한 단말에서 결제 발생 시 결제 성공 이벤트가 각 단말에 전파되어 백엔드에 동일 결제에 대해 중복 처리 가능
  표. 동일 결제 중복 처리 문제 발생 상황

위와 같은 이슈가 발생하면 단순 서비스 중복 제공 뿐만 아니라 연계된 제휴사까지 영향이 갈수 있는 문제라 심각한 문제였습니다.

중복 결제는 각 앱 마켓에서 방지하고 있지만 그 결과가 사장이지로 넘어올때는 중복 처리에 대한 고려가 필요하여 이 문제는 설계 단계에서 부터 고민을 하게 되었습니다.

중복 결제 → 각 스토어가 방지
중복 서비스 제공 → 사장이지에서 방지 필요
03.png

그림. 동일 결제 중복 처리 이슈


해결 방안 1 : 멱등성

  • 멱등성(Idempotency)이란?

동일한 요청을 여러 번 수행해도 결과가 한 번 수행한 것과 같아야 한다는 원칙입니다.


간단하게 아래 표의 예시를 보면 멱등성의 의미를 바로 파악할 수 있습니다. 인앱결제 백엔드에서는 동일 결제에 대해 위에서 말한 여러 케이스들로 인해 결제 이벤트가 중복 발생 될수 있어 멱등성을 고려하는게 필요합니다.


동일 요청 횟수
멱등성 적용 결과
멱등성 미적용 결과
1회
구독 ID: 12345 생성
구독 ID: 12345 생성
2회
구독 ID: 12345 응답
구독 ID: 12346 생성 (신규 구독 처리)
3회
구독 ID: 12345 응답
구독 ID: 12347 생성 (신규 구독 처리)
4회
✅ 하나의 구독만 존재
❌ 3개의 중복 구독

표. 멱등성 적용/미적용 케이스별 예시

  • 멱등성 키 설계
멱등성이 필요하다면 사장이지 인앱결제 백엔드에서는 어떻게 중복 발생하는 결제 이벤트에서 멱등성을 판단할 수 있을까요?

각 앱 마켓에서는 결제 트랜잭션 별 메시지를 생성 (그림. 동일 결제 중복 처리 이슈 2-1, 2-2)하는데 메시지에 들어있는 필드들을 살펴보면 아래와 같이 멱등성 키를 설계할때 사용할 수 있는 필드들이 있습니다.

각 앱 마켓을 식별하는 코드값과 아래 필드들을 조합하면 동일한 결제 이벤트가 중복 발생해도 사장이지 백엔드에서는 중복을 인지하여 멱등성을 보장할 수 있습니다.

앱 마켓
필드
설명
Google Play
purchase token
단건 결제, 정기 구독 사이클 별 고유한 값을 가집니다.
구글 플레이-클라이언트-백엔드가 공유하는 값입니다.
Google Play
orderId
단건 결제 및 정기 구독 각 단계의 이벤트별 발급되는 값으로
실제 고객에게도 영수증 등으로 전달되는 값입니다. (GPA.XXXXXXX)
App Store
originalTransactionId
 단건 결제 및 정기 구독의 주기를 식별하는 값입니다.
App Store
transactionId
단건 결제 및 정기 구독 각 단계의 이벤트별 발급되는 값입니다.
(단건 결제 시 originalTransactionId와 transactionId 는 동일)
공통
productId
각 앱 마켓에서 관리하는 구매한 상품의 식별자입니다.

표. 인앱 결제 메시지 내 필드들

간단히 정리해보면 아래와 같습니다.

단건 결제 발생 시 크게 결제/결제 환불 이벤트가 발생 가능한데 아래와 같이 필드의 값들이 채워집니다.

 
04.jpg

그림. 단건 결제 시 앱 마켓별 필드 예시


필드 내 있는결제 사이클별 고유한 값과 결제 이벤트별 고유한 값을 활용하면 단건 결제뿐만 아니라 구독 결제 또한 멱등성을 보장할 수 있습니다.(앱스토어의 경우 originalTransactionId 만으로 유일성은 보장 되나 동일 구독 그룹 내 다른 상품을 구독하는 경우 originalTransactionId 가 동일하여 멱등성 키에 productId 를 포함하여 확인 할 수도 있습니다.)
 

해결 방안 2 : 멱등성 + 발생 시간 검증

멱등성만으로 처리할 때 문제가 없어 보입니다. 이런 상황은 어떨까요? 단건 결제가 아닌 구독 결제 케이스입니다.
05.jpg

그림. 멱등성만 고려하는 경우 최종 데이터 불일치 발생


다소 극단적인 예시이지만 충분이 발생할 수 있습니다. 단건 결제와 달리 구독의 경우 상태가 있어 멱등성 만으로는 이슈가 있어보입니다.


다행히 각 앱 마켓에서 생성하는 메시지에는 이 문제를 해결할 필드가 있습니다.

 
발생 시간 :각 앱마켓에서 제공하는 메시지에 포함된 필드 중 실제 결제관련 이벤트가 발생한 시간으로 하나의 결제 이벤트별 고정된 발생 시간을 가집니다. (메시지 발생 시간과 이벤트 발생 시간은 구분 필요)

결제 메시지를 처리 시 멱등성과 더불어 발생 시간을 같이 검증하면 위 문제가 해결될 수 있습니다. 

 
06.jpg

그림. 멱등성과 발생 시간을 고려한 경우



인앱결제 백엔드에서는 모든 결제 이벤트 처리 시 서버내 관리하고 있는 구독의 최신 상태와결제 이벤트 내 발생 시간을 비교하여 서버내 관리 되고 있는 메시지 보다 최신 메시지만 반영을 해야 합니다.

다음 편에서는, 사장이지 인앱 결제 백엔드 개발에서 결제 승인부터 서비스 활성화까지 전체 흐름의 신뢰성 확보를 위한 두번째 고려 사항으로 '분산 환경에서의 데이터 일관성 관리' 에 대해 자세히 설명드리겠습니다.  (#2편 보러가기 )

이상현

KT에서 백엔드 개발자로 근무하고 있으며 좋은 동료가 되기 위해 노력하고 있습니다.

이전 글
'사장이지' 인앱 결제 백엔드 개발#1: 결제 서비스 신뢰성 확보를 위한 노력 - kode