지난 시간을 통해 만든 서버에서 구글 플레이(Google Play) 인앱 결제 영수증 검증을 위해 백엔드 서버에서 API를 호출하는 과정 중 참 황당한 에러를 마주했다.
서버 로그에 남은 에러 메시지는 다음과 같았다.
error_code: "internal"
error_message: "The current user has insufficient permissions to perform the requested operation."
event_name: "billing.request.failed"
에러 메시지 자체는 명확했다. API를 호출하는 주체에게 권한이 부족하다.
하지만 이를 해결하기 위해 파고든 원인은 겉보기처럼 단순하지 않았다. 이 글에서 구글 클라우드(GCP)와 플레이 콘솔 간의 권한이 어떻게 얽혀 있는지, 그리고 어떻게 해결했는지 정리해봤다.
1. 첫 번째 시도, 사용자 추가 실패
가장 먼저 'GCP 서비스 계정에 Play Console 접근 권한이 없다'는 것에 집중했다. 백엔드가 배포된 Cloud Run 환경에서 구글 API를 호출하고 있었으므로, 해당 Cloud Run Admin 권한을 가진 IAM 서비스 계정을 Google Play Console에 관리자 권한으로 추가했다.
(사실 GCP에 이미 Play Integrity API를 연결해뒀었어서 될거라고 생각했는데, 이거랑 결제를 검증하는 Android Developer API랑은 또 다른거였다;;)
대부분의 자료에서는 이렇게 하면 된다고 했다.
이제는 당연히 해결될 것이라 믿고 다시 테스트를 진행했다. 하지만 결과는 동일했다. 여전히 insufficient permissions 오류가 발생했다.
2. 문제의 근본 원인: 계정 이전
그러다 찾게 된 한 블로그...
그리고 시스템 로그와 과거 작업 히스토리를 천천히 되짚어 보았다. 생각해보니 구글 결제 프로필 설정 과정에서 있었던 하나의 이벤트를 발견했다.
이번에 사업자 등록을 진행하며 계정 이전을 진행하였다. 이 과정에서 기존에 등록해 두었던 '인앱 상품' 데이터는 새로운 계정으로 무사히 마이그레이션 되었다. 표면적으로는 아무런 문제가 없어 보였다.
하지만 문제는 보이지 않는 '권한'에 있었다. 구글 플레이 콘솔에 새로 추가한 IAM 계정은 콘솔 자체에 대한 권한은 얻었지만, 계정 이전 전에 생성되었던 기존 Legacy Products에 대한 API 접근 권한까지 온전히 상속받지 못한 상태였다.
즉, 데이터(상품)는 이전되었지만, 결제 검증에 필요한 내부적인 리소스 권한 맵핑은 과거의 결제 프로필에 머물러 있거나 단절된 것이다. 새로운 권한을 아무리 부여해도 시스템은 과거의 식별자와 충돌하며 '권한 없음'을 반환하고 있었던 셈이다.
3. 해결: 초기화와 재등록
해결 방법은 위 글에 나온 대로 초기화에 가까운 접근이었다. (앱 이전 정상적으로 해준다며...구글...)
기존에 등록되어 있던 인앱 상품들을 모두 비활성화(또는 삭제)하고, 현재의 올바른 권한이 세팅된 상태에서 상품을 처음부터 새로 등록했다. 새로 등록된 상품은 현재의 결제 프로필과 새롭게 추가된 IAM 계정의 권한 하에 온전하게 생성된다고 한다.
상품을 재등록한 후, 다시 영수증 검증 API를 호출했다.
결과는 성공이었다. internal 에러가 사라지고 정상적인 검증 응답 데이터가 반환되었다.
아, 그리고 '라이센스 계정' 등록을 해줘야 실구매 없이 테스트 결제가 가능하다!
마치며
이번 이슈를 통해 클라우드 인프라와 플랫폼 서비스(BaaS)를 다룰 때 명심해야 할 중요한 교훈을 얻었다.
첫째, 플랫폼 간의 연동(GCP - Play Console)에서 발생하는 권한 문제는 단순히 UI 상의 체크박스를 켜고 끄는 것으로 끝나지 않을 때가 많다. 특히 '계정 이전'이나 '결제 프로필 변경'과 같은 굵직한 메타 데이터의 변화가 있을 때는, 리소스 간의 내부 참조(Reference)가 깨질 수 있음을 항상 염두에 두어야 한다.
둘째, 원인을 알 수 없는 권한 오류가 발생했을 때, 권한을 계속해서 높이는 것(Escalation)은 정답이 아니다. 리소스 자체가 가진 상태(State)와 권한이 부여된 시점(Lifecycle)을 의심해 보아야 한다.
단순한 상품 재등록으로 끝난 문제였지만, 그 이면에 숨겨진 분산 시스템의 권한 동기화 문제를 엿볼 수 있었던 의미 있는 시간이었다.
추천글
[스타트업/기술] Node.js & Google Play 인앱결제 | 결제 시스템 아키텍처 설계 (Part 1)
[스타트업/기술] Node.js & App Store 인앱결제 | 결제 시스템 아키텍처 (Part 2)