본문 바로가기
개발

Google Play 인앱 구독 상품, 실시간 상태 추적 시스템 구축. RTDN (Real-Time Develop Notification)

by 최승환 2024. 2. 17.

현재 사내 서비스에서는 구독 상품을 판매하고 있다.

현재 개발 된 상황은, 유저의 구독 상품 구매 여부를 DB에 기록하고 있긴 했지만, 상품 구매 당시 최초 한번만 기록하고 있었기에 구독의 갱신이나 취소가 발생 했을 경우엔 Backend 서버에서 이를 추적할 방법이 없었다.

 

기능 상에서 유저의 구독 여부가 필요한 경우엔 Google Play Developer API 으로 체크 하기에 여태 버그 접수는 없었지만, 비즈니스 분석이나, 다른 복합적인 작업을 위해 내부 데이터를 조회할 경우엔 데이터를 신뢰할 수 없어 문제가 되었다.

 

일반적으로 SDK 를 제공하는 곳에서는 백엔드 통합 기능으로 Callback 이나 Push / Notification 같은 기능을 제공한다. Google Play 에서도 이와 비슷한 기능을 제공하는 지 기술 조사를 시작 했고, 구매 수명 주기 관리를 위해 RTDN (Real Time Developer Notifications) 을 제공한 다는 것을 알 수 있었다.

 

서버 백엔드와 Google Play 통합 | Google Play 결제 시스템 | Android Developers

 

서버 백엔드와 Google Play 통합  |  Google Play 결제 시스템  |  Android Developers

알림 1. 2023년 8월 2일부터 모든 신규 앱은 결제 라이브러리 버전 5 이상을 사용해야 합니다. 2023년 11월 1일부터는 기존 앱의 모든 업데이트에도 결제 라이브러리 버전 5 이상이 요구됩니다. 자세

developer.android.com

 

Google Play 앱의 구독 상품에서 결제 정보가 변경 될 경우, Google CloudPub/Sub 서비스에서 백엔드 서버로 Push 메세지를 보낼 수 있는 것이다.

 

간략한 동작 아키텍처는 다음과 같다.

 

동작 아키텍처

 

Google Play 결제 시스템은 일회성 구매, 정기 결제에 따른 구매 수명 주기를 갖고 있다.

 

유저가 일회성 상품을 구매하거나, 정기 결제 상품의 정보가 업데이트 되는 경우, 서비스 백엔드에서 올바른 동작을 할 수 있도록 실시간 개발자 알림(RTDN)을 제공한다.

 

구매 수명 주기와 RTDN  |  Google Play 결제 시스템  |  Android Developers

 

구매 수명 주기와 RTDN  |  Google Play 결제 시스템  |  Android Developers

알림 1. 2023년 8월 2일부터 모든 신규 앱은 결제 라이브러리 버전 5 이상을 사용해야 합니다. 2023년 11월 1일부터는 기존 앱의 모든 업데이트에도 결제 라이브러리 버전 5 이상이 요구됩니다. 자세

developer.android.com

 

구축 작업

 

Google Play Console 과 Google Cloud 연동

 

기술 조사를 하며 다른 부분은 다 이해가 되었지만, 이 부분이 가장 모호했다. 분명 개념상 생각해 보았을 때, Google Play 에 게시된 앱과 RTDN 을 수신하는 Google Cloud 의 Pub/Sub 사이에 연결점이 있어야 할텐데, 관련 내용을 찾기 힘들었기 때문이다.

 

 

공식 가이드에 따르면, API 액세스 라는 메뉴를 통해 Google Play 계정과 Google Cloud를 연동한다.

 

Google Play 에 앱을 게시한 계정이, Google Cloud 의 소유자 권한을 갖고 있으면 설정 - API 엑세스 메뉴에서 Google Cloud 연동을 할 수 있다는 내용은 있었으나, 아무리 이것저것 설정해봐도 API 엑세스 라는 메뉴가 보이지 않는 문제가 있었다.

 

구글링을 해보니 나와 같은 문제를 겪고 있는 사람이 굉~장히 많았었다. 😂

Google Play Developer API - "The current user has insufficient permissions to perform the requested operation."

 

Google Play Developer API - "The current user has insufficient permissions to perform the requested operation."

I have a Google dev console process with Google Play Developer API is enabled and the project is linked to Google Play project. In Google Dev console project, created OAuth Client ID (web applicati...

stackoverflow.com

 

2023년 12월에 작성된 꽤나 최근 (현재는 2024년 1월이다) 블로그 문서를 살펴보면 그 이유를 알 수 있다.

You no longer need to have a GCP project associated with your Google Play Developer Account to…

 

더 이상 위와 같이 API 액세스 메뉴에서 Google Cloud 와 연결할 필요가 없었으며,

인앱 아이템의 수익 창출 설정 - 실시간 개발자 알림 메뉴에서 Pub/Sub 에서 설정한 주제와 연결 시켜주면 되었다.

 

 

Google Cloud Pub/Sub 설정

위 설정을 하기 위해선, Google Cloud 에서 메세지를 보내기 위한 Pub/Sub 서비스의 관한 설정을 먼저 해야 한다.

 

 

주제 항목을 생성한 후, 주제의 구독 항목에서 전송 유형 을 푸시 로 하게 되면, 설정한 End-Point 로 메세지를 전송해 준다.

 

 

 

또한 주제의 권한에서, 게시/구독 게시자로 Google Play Develop Notification 을 추가해 줘야 한다. 그래야 Google Play 에서 해당 주제에 대해 게시 권한이 생긴다. 해당 내용은 바로 아래 문서에서 확인할 수 있다.

 

준비하기  |  Google Play 결제 시스템  |  Android Developers

 

준비하기  |  Google Play 결제 시스템  |  Android Developers

알림 1. 2023년 8월 2일부터 모든 신규 앱은 결제 라이브러리 버전 5 이상을 사용해야 합니다. 2023년 11월 1일부터는 기존 앱의 모든 업데이트에도 결제 라이브러리 버전 5 이상이 요구됩니다. 자세

developer.android.com

 

백엔드 작업

 

Google Cloud 에서 지정된 End-Point 로 메세지를 전달하도록 설정 하였다면, 이제 메세지를 받기 위한 컨트롤러를 작업해 줘야 한다.

NestJs 예시 코드는 다음과 같다.

외부 서비스, 서드파티에서 전달 받는 메세지를 처리하기 위한 MessageBroker 모듈을 생성 한 후, Controller 를 작업 하였다.

import { Body, Controller, Logger, Post } from '@nestjs/common';
import { GooglePubSubMessage } from './message-broker.type';
import { MessageBrokerService } from './message-broker.service';

@Controller('message-broker')
export class MessageBrokerController {
  logger = new Logger(MessageBrokerController.name);

  constructor(private readonly messageBroker: MessageBrokerService) {}

  @Post('/google-pub-sub-message')
  async googleMessageHandler(@Body() body) {
    try {
      this.logger.debug(body);
      const message: GooglePubSubMessage = body;
      const result = await this.messageBroker.processGooglePlayMessage(message);

      return result;
    } catch (e) {
      this.logger.error(e, { body });
      throw e;
    }
  }
}

 

Service 예시 코드는 다음과 같다.

import { Injectable, Logger } from '@nestjs/common';
import { GooglePubSubMessage, GoogleRtdnData } from './message-broker.type';

@Injectable()
export class MessageBrokerService {
  logger = new Logger(MessageBrokerService.name);

  constructor() {}

  processGooglePlayMessage(message: GooglePubSubMessage) {
    try {
      const encodedData = message?.message?.data;
      const decodedString: string = Buffer.from(encodedData, 'base64').toString('utf-8');
      const data: GoogleRtdnData = JSON.parse(decodedString);

      return true;
    } catch (e) {
      throw e;
    }
  }
}

 

Pub/Sub Message 형식은 아래 문서에 정의되어 있다. message.data 필드에 관련 정보가 base64로 인코딩 되어 들어온다.

PubsubMessage  |  Cloud Pub/Sub Documentation  |  Google Cloud

 

PubsubMessage  |  Cloud Pub/Sub Documentation  |  Google Cloud

Send feedback PubsubMessage Stay organized with collections Save and categorize content based on your preferences. A message that is published by publishers and consumed by subscribers. The message must contain either a non-empty data field or at least one

cloud.google.com

 

아래 문서에서 각 필드의 상세 설명을 볼 수 있다.

실시간 개발자 알림 참조 가이드  |  Google Play 결제 시스템  |  Android Developers

 

실시간 개발자 알림 참조 가이드  |  Google Play 결제 시스템  |  Android Developers

알림 1. 2023년 8월 2일부터 모든 신규 앱은 결제 라이브러리 버전 5 이상을 사용해야 합니다. 2023년 11월 1일부터는 기존 앱의 모든 업데이트에도 결제 라이브러리 버전 5 이상이 요구됩니다. 자세

developer.android.com

 

위 가이드를 참조하여 Type 정의를 하였다.

export interface GooglePubSubMessage {
  message: {
    data: string;
    messageId: string;
    message_id: string;
    publishTime: string; // zulu format
    publish_time: string; // zulu format
  };
  subscription: string;
}

export interface GoogleRtdnData {
  version: string;
  packageName: string;
  eventTimeMillis: string;
  oneTimeProductNotification?: {
    version: string;
    notificationType: number;
    purchaseToken: string;
    sku: string;
  };
  subscriptionNotification?: {
    version: string;
    notificationType: number;
    purchaseToken: string;
    subscriptionId: string;
  };
}

테스트

각 개발팀마다 개발 환경은 다르겠지만, 우리 팀은 따로 개인 개발 용도로 사용하기 위한 서버는 없으므로, 이러한 외부 Http 통신을 받아야 하는 상황에선 ngrok 을 주로 사용한다.

ngrok 이나 localTunnel 은 로컬 개발 환경에서 실행 중인 서버를 외부 인터넷에 노출 시키는 도구로,

외부에서 접근 가능한 URL을 로컬 환경의 포트와 매핑해 주는 기능을 한다.

ngrok | Unified Application Delivery Platform for Developers

 

ngrok | Unified Application Delivery Platform for Developers

ngrok is a secure unified ingress platform that combines your global server load balancing, reverse proxy, firewall, API gateway and Kubernetes Ingress Controller to deliver applications and APIs.

ngrok.com

ngrok, 백엔드 서버의 테스트 준비를 마친 후, Google Play Console 의 실시간 개발자 알림 탭에서, 테스트 알림 을 보내보면, 메세지가 정상적으로 수신 됨을 확인할 수 있다.

이제 수신된 메세지를 활용하여 서비스에 필요한 비즈니스 로직들을 작업하면 된다.