본문 바로가기

django

구글 인앱 결제 웹훅 pub sub

1. Pub/Sub 개념 설명

1.1 Pub/Sub이란 무엇인가?

Pub/Sub(Publish-Subscribe)은 비동기 메시징 서비스로, 발행자(Publisher)와 구독자(Subscriber)가 메시지를 주고받을 수 있도록 돕는 Google Cloud의 서비스다.

발행자(Publisher): 메시지를 특정 주제(Topic)에 게시

구독자(Subscriber): 해당 주제의 메시지를 구독하고 처리

 

이를 활용하면 시스템 간의 결합도를 낮추고, 실시간 데이터 처리를 간편하게 할 수 있다. 즉 웹훅으로 사용할 수 있다.

 

1.2 왜 구글 인앱 결제에서 Pub/Sub을 사용해야 할까?

구글 인앱 결제에서는 사용자가 구매, 구독 취소, 결제 실패 등의 이벤트를 발생시킨다.

하지만 클라이언트에서 직접 구매 상태를 확인하는 것은 보안상 위험할 수 있다.

Pub/Sub을 활용하면 결제 이벤트를 서버에서 안전하게 감지하고 처리할 수 있다.

 

1.3 구글 Play Billing 시스템과의 관계

Google Play Billing 시스템은 결제 이벤트가 발생하면 이를 Google Cloud Pub/Sub으로 전송할 수 있다.

Play Console에서 Pub/Sub 주제를 설정하면 Google Play Billing이 자동으로 관련 메시지를 발행

구독된 서버에서 이를 감지하고 결제 처리 로직을 수행

 

2. Google Cloud Pub/Sub 설정하기

2.1 GCP에서 Pub/Sub 주제(Topic) 만들기

1. Google Cloud Console 접속

2. Pub/Sub → 주제(Topic) 만들기 클릭

3. 적절한 이름을 입력 (예: play-billing-topic)

4. 메시지 보안 강화를 위해 메시지 데이터 암호화(KMS) 옵션 선택 가능

 

 

2.2 구독(Subscription) 생성

1. 생성한 주제에서 “구독 만들기” 선택

2. 구독 방식 선택

푸시(Push) 방식: 메시지를 특정 엔드포인트(URL)로 전달

풀(Pull) 방식: 서버가 직접 메시지를 가져옴

3. Cloud Functions, Cloud Run 또는 서버 API와 연동할 경우 푸시 방식 권장

 

2.3 IAM 권한 설정

Pub/Sub을 활용하려면 Google Play Billing이 메시지를 발행할 수 있도록 권한을 부여해야 한다.

1. IAM & 관리자 → 역할 추가

2. service-<project-number>@gcp-sa-playbilling.iam.gserviceaccount.com 사용자 추가

3. Pub/Sub 게시자(Pub/Sub Publisher) 역할 할당

 

3. Google Play Console에서 Pub/Sub 연동하기

 

3.1 Google Play 결제 이벤트를 Pub/Sub에 연결하는 방법

1. Play Console 접속

2. “설정 → 개발자 계정 → API 접근 → Pub/Sub 설정” 이동

3. 방금 생성한 Pub/Sub 주제를 입력하고 저장

 

3.2 JSON 메시지 형식과 주요 필드

 

Cloud Pub/Sub 주제에 관한 각 게시에는 하나의 base64로 인코딩된 데이터 필드가 포함되어 있다.

{
  "message": {
    "attributes": {
      "key": "value"
    },
    "data": "eyAidmVyc2lvbiI6IHN0cmluZywgInBhY2thZ2VOYW1lIjogc3RyaW5nLCAiZXZlbnRUaW1lTWlsbGlzIjogbG9uZywgIm9uZVRpbWVQcm9kdWN0Tm90aWZpY2F0aW9uIjogT25lVGltZVByb2R1Y3ROb3RpZmljYXRpb24sICJzdWJzY3JpcHRpb25Ob3RpZmljYXRpb24iOiBTdWJzY3JpcHRpb25Ob3RpZmljYXRpb24sICJ0ZXN0Tm90aWZpY2F0aW9uIjogVGVzdE5vdGlmaWNhdGlvbiB9",
    "messageId": "136969346945"
  },
  "subscription": "projects/myproject/subscriptions/mysubscription"
}

 

base64로 인코딩된 데이터 필드를 디코딩하면 DeveloperNotification에 다음 필드가 포함된다.

{
  "version": string,
  "packageName": string,
  "eventTimeMillis": long,
  "oneTimeProductNotification": OneTimeProductNotification,
  "subscriptionNotification": SubscriptionNotification,
  "voidedPurchaseNotification": VoidedPurchaseNotification,
  "testNotification": TestNotification
}

 

 

4. 서버에서 Pub/Sub 메시지 처리하기

 

4.1 서버에서 Pub/Sub 메시지 받는 방법 

# views.py
@action(methods=["POST"], detail=False, serializer_class=GoogleWebhookHistorySerializer, permission_classes=[])
    def webhook(self, request, *args, **kwargs):
        return self._create(request, *args, **kwargs)


# serializer.py
class GoogleWebhookHistorySerializer(serializers.ModelSerializer):
    message = serializers.DictField(label="메세지", write_only=True)

    class Meta:
        model = GoogleReceipt
        fields = ["message"]

    def validate(self, attrs):

        str_bytes = base64.b64decode(attrs["message"]["data"])
        base64_decode_dict = eval(str_bytes.decode("utf-8"))

        if "voidedPurchaseNotification" not in base64_decode_dict.keys():
            raise ValidationError({"nonField": ["취소 요청이 아닙니다."]})

        attrs["notification_type"] = base64_decode_dict["voidedPurchaseNotification"]["productType"]
        attrs["purchase_token"] = base64_decode_dict["voidedPurchaseNotification"]["purchaseToken"]
        attrs["order_id"] = base64_decode_dict["voidedPurchaseNotification"]["orderId"]

 

 

Pub/Sub을 활용할 때 주의할 점

중복 메시지 처리: 메시지가 중복 전달될 수 있으므로 messageId를 기반으로 중복 제거

보안 강화: 푸시 방식 사용 시 검증된 IP에서만 요청을 허용

 

메세지가 수신되지 않을 때 IAM 권환 확인이 필요하다. (Pub/Sub Subscriber 역할 필요)