구글플레이 인앱결제 영수증 서버 검증으로 안전한 코인 적립 구현
목차
구글플레이 인앱결제로 사용자가 구매한 코인을 신뢰할 수 있게 적립하는 백엔드 다리를 만들었다. 클라이언트에서 받은 영수증을 검증하고, 검증 결과에 따라 안전하게 코인을 지급하는 흐름이다.
왜 서버 검증인가
모바일 클라이언트에서 직접 구글플레이 API를 호출해서 인앱결제를 완료할 수 있지만, 서버에서 영수증을 재검증하는 과정은 선택이 아니라 필수다. 클라이언트는 사용자가 조작할 수 있는 환경이고, 영수증 데이터도 로컬에 남아있다. 만약 검증 없이 그냥 코인을 지급한다면, 한 번의 구매로 여러 계정에 코인을 뿌리거나, 과거 영수증을 재활용하는 악의적 요청에 무방비가 된다.
특히 금전이 실제로 오가는 결제 시스템에서는 "신청했다 == 검증됐다"가 절대 같지 않다. 서버는 반드시 제삼의 신뢰할 수 있는 출처(여기서는 구글플레이 스토어)에서 영수증의 정합성을 다시 확인해야 한다. 이를 통해서만 실제 결제가 이뤄졌고, 거래액이 정확하고, 해당 사용자가 정당한 소유자임을 보증할 수 있다.
구글플레이 영수증 검증 구현
새로운 API 엔드포인트를 만들고, 별도의 라이브러리 레이어를 두어 책임을 분리했다.
| 파일 | 역할 |
|---|---|
src/app/api/coins/grant/route.ts |
클라이언트로부터 영수증과 코인 적립 요청을 받는 진입점 |
src/lib/gplay.ts |
구글플레이 API와의 통신, 영수증 검증 로직 |
src/lib/payments.ts |
결제 관련 유틸리티, 영수증 데이터 파싱, 중복 검사 등 |
API 엔드포인트는 클라이언트의 요청을 받아서 기본 검증(null 체크, 형식 확인)을 하고, 즉시 gplay.ts로 넘겨 구글플레이에 영수증을 검증하도록 한다. 구글 쪽에서 "유효한 영수증"이라고 응답해야만, payments.ts의 로직을 통해 추가 검증(중복 적립 여부, 이미 처리된 거래는 아닌지)을 거친 후 최종적으로 코인을 지급한다.
라이브러리를 나누는 이유는 단순히 파일을 쪼개는 것이 아니라, 각 계층이 할 일을 명확히 하기 위함이다. 구글 API 통신 로직이 변해도 결제 핵심 로직(중복 처리, 감사 기록)은 영향받지 않고, 나중에 다른 결제 채널(Apple IAP, 삼성 결제 등)을 추가할 때도 payments.ts 같은 공통 로직은 재사용할 수 있다.
결제 시스템에서의 주의점
결제를 다룰 때는 몇 가지 원칙을 지킨다:
-
멱등성(Idempotency): 같은 영수증으로 중복 요청이 들어와도 한 번만 코인이 적립되어야 한다. 네트워크 타임아웃이나 클라이언트 재시도 시나리오를 고려해서, 같은 거래 ID는 이미 처리됐는지 확인하는 단계가 필수다.
-
로깅과 감사: 실제 금전 거래이므로 모든 적립 시도, 검증 결과, 성공/실패를 기록한다. 나중에 분쟁이나 감시 필요시 근거가 된다.
-
구글 API 신뢰성: 구글 서버가 일시적으로 느리거나 응답이 없을 수 있다. 그래서 타임아웃 설정, 재시도 로직, 폴백 처리를 고민해야 한다. 실패 응답에 대해 클라이언트를 일단
나중에 다시 시도하세요로 안내하고, 백그라운드에서 나중에 재처리하는 방식도 있다. -
버전 관리: 구글플레이 API의 엔드포인트나 응답 형식이 바뀔 가능성이 있다.
gplay.ts에 API 버전을 명시하고, 응답 파싱 로직이 견고한지 미리 테스트해둔다.
팀 관점의 설계
이 작업을 하면서 느낀 점은, 결제 같은 critical 시스템은 단순한 기술 구현이 아니라 운영 가능성까지 생각해야 한다는 것. API 엔드포인트는 제한된 QPS로 설정하고(DDoS 방어), 영수증 검증은 캐시할 수 있는지 검토하고, 예상 트래픽에 따라 구글 API 쿼터를 미리 신청해두는 식이다. 그리고 온콜 담당자가 밤 3시에 깨지 않으려면, 에러 알림도 제대로 설정해야 한다.
또한 다른 팀(예: 고객 지원팀)이 사용자 코인 적립 히스토리를 조회하거나, 실수로 중복 적립된 경우 수정할 수 있는 관리 도구도 함께 고민해야 한다. 백엔드 구현만 했다고 끝이 아니다.
🛒 이 글과 어울리는 추천 상품
*위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.
댓글 0
첫 댓글 달아줘.