Skip to content

Commit

Permalink
Handle Google's VoidedPurchaseNotification
Browse files Browse the repository at this point in the history
  • Loading branch information
agoncharov-reef committed Dec 20, 2023
1 parent 48b84af commit 6592c37
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 0 deletions.
21 changes: 21 additions & 0 deletions demo/demo/tests/fixtures/google_in_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,27 @@ def build_google_rtdn_notification(type_: GoogleSubscriptionNotificationType):
return build_google_rtdn_notification


@pytest.fixture
def google_rtdn_voided_purchase_notification(settings, purchase_token):
return {
"message": {
"messageId": "136969346945",
"publishTime": "2022-10-25T13:15:00.858Z",
"data": b64encode(json.dumps({
"version": '1.0',
"packageName": settings.GOOGLE_PLAY_PACKAGE_NAME,
"eventTimeMillis": 100,
"voidedPurchaseNotification": {
'purchaseToken': purchase_token,
'orderId': 'GS.0000-0000-0000',
'productType': 1,
},
}).encode('utf8')).decode('utf8'),
},
"subscription": "projects/myproject/subscriptions/mysubscription",
}


@pytest.fixture
def google_rtdn_notification(google_rtdn_notification_factory) -> dict:
return google_rtdn_notification_factory(GoogleSubscriptionNotificationType.PURCHASED)
Expand Down
34 changes: 34 additions & 0 deletions demo/demo/tests/test_google_in_app.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

from dataclasses import dataclass
from datetime import timedelta
from typing import Any
from unittest import mock

Expand Down Expand Up @@ -356,3 +357,36 @@ def test__google__subscriptions__cancel__google(

assert user.subscriptions.active().count() == 1
assert user.subscriptions.active().latest().auto_prolong is False


@pytest.mark.django_db(databases=['actual_db'])
def test__google__subscriptions__voided_purchase(
user,
client,
subscription,
google_in_app,
google_rtdn_voided_purchase_notification,
):
subscription.end = now() + days(10)
subscription.save()

SubscriptionPayment.objects.create(
user=user,
plan=subscription.plan,
subscription=subscription,
provider_codename=google_in_app.codename,
provider_transaction_id='12345',
status=SubscriptionPayment.Status.PENDING,
subscription_start=now() + days(10),
subscription_end=now() + days(40),
)

assert user.subscriptions.active().count() == 1

response = client.post('/api/webhook/google_in_app/', google_rtdn_voided_purchase_notification, content_type="application/json")
assert response.status_code == 200, response.content

assert user.subscriptions.active().count() == 0
subscription = user.subscriptions.latest()
assert now() - timedelta(seconds=5) <= subscription.end <= now()
# assert subscription.auto_prolong is False
8 changes: 8 additions & 0 deletions subscriptions/providers/google_in_app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,14 @@ def handle_google_notification(self, notification: GoogleDeveloperNotification)
When a subscription is purchased, a SubscriptionNotification with type SUBSCRIPTION_PURCHASED notification is sent. When you receive this notification, you should query the Google Play Developer API to get the latest subscription state.
"""
log.debug('Received RTDN notification %s', notification)

if notification.voidedPurchaseNotification:
log.debug('Received voided purchase notification -> cancelling subscription')
self.dismiss_token(notification.voidedPurchaseNotification.purchaseToken)
return Response({}, status=HTTP_200_OK)

assert notification.subscriptionNotification, \
'subscriptionNotification field not found in notification'
payment = self.update_or_create_subscription(
purchase_token=notification.subscriptionNotification.purchaseToken,
event=notification.subscriptionNotification.notificationType,
Expand Down
10 changes: 10 additions & 0 deletions subscriptions/providers/google_in_app/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,23 @@ class Config:
extra = Extra.forbid


class VoidedPurchaseNotification(BaseModel):
purchaseToken: str
orderId: str
productType: int

class Config:
extra = Extra.forbid


class GoogleDeveloperNotification(BaseModel):
# https://developer.android.com/google/play/billing/rtdn-reference#sub
version: str
packageName: str
eventTimeMillis: str
oneTimeProductNotification: Optional[Any] = None
subscriptionNotification: Optional[GoogleSubscriptionNotification] = None
voidedPurchaseNotification: Optional[VoidedPurchaseNotification] = None
testNotification: Optional[GoogleTestNotification] = None

class Config:
Expand Down

0 comments on commit 6592c37

Please sign in to comment.