Validating Purchases on Android App using Python

Tiempo de lectura: 2 minutos

We will today learn how to validate in-app purchases using Python and an Android app. This will serve for native Android apps, ionic, kotlin, flutter, react native or any other technology you choose.

The first thing to do is create a refresh token and OAuth 2.0 token data. To follow this tutorial, we will continue with the following steps:

https://devcodelight.com/how-to-get-the-refresh-token-of-google-step-by-step-oauth-2-0

NOW we are going to implement the code that will allow us to validate purchases:

First, we need to get the access token using our refresh token (obtained in the previous step).

To get the access token is:

import requestsdef exchange_code_for_token(code: str) -> dict:    url = "https://oauth2.googleapis.com/token"    data = {        "code": code,        "client_id": "TU_CLIENT_ID",        "client_secret": "TU_CLIENT_SECRET",        "redirect_uri": "http://localhost:8000/auth/callback",        "grant_type": "authorization_code"    }    response = requests.post(url, data=data)    response.raise_for_status()    return response.json()

You will get the access token with this.

You receive this:

{ "access_token": "...", "expires_in": 3599, "refresh_token": "...", "scope": "https://www.googleapis.com/auth/androidpublisher", "token_type": "Bearer" } 

You now need to validate the following:

Validating product purchases:

Validación de compras y suscripciones Android

Validar compra de Google Play (producto in-app)

        import requests        import json        def validate_android_purchase(            id_user: str,            purchase_token: str,            package_name: str,            subscription_id: str,            access_token: str,        ) -> str:            """ Returns order ID if valid, -1 if there's an error in the response, or raises exceptions if connection or request fails """            url = (                f"https://www.googleapis.com/androidpublisher/v3/applications/"                f"{package_name}/purchases/products/{subscription_id}/tokens/{purchase_token}"            )            headers = {                "Authorization": f"Bearer {access_token}",                "Accept": "application/json",                "User-Agent": "SecurePythonClient/1.0"            }            try:                response = requests.get(url, headers=headers)                response.raise_for_status()            except requests.HTTPError as e:                print(f"HTTP Error: {response.status_code} - {response.reason}")                try:                    print(json.dumps(response.json(), indent=2))                except Exception:                    print("Error parsing error response")                raise            except requests.RequestException as e:                print(f"Connection error: {str(e)}")                raise            data = response.json()            if 'error' in data:                return "-1"            return data.get("orderId", "NO_ORDER_ID")    

Ejemplo de uso:

        if name == "main":            try:                order_id = validate_android_purchase(                    id_user="user_123",                    purchase_token="PURCHASE_TOKEN_AQUÍ",                    package_name="com.tu.app",                    subscription_id="producto_id",                    access_token="ya29.a0AfH6SMA..."                )                print("Order ID:", order_id)            except Exception as e:                print("Error validating purchase:", str(e))    

Validating subscriptions:

        import requests        import json        def validate_android_subscription(            id_user: str,            purchase_token: str,            package_name: str,            subscription_id: str,            access_token: str,        ) -> dict:            """ Validates a Google Play subscription. Args: id_user (str): ID internal of the user (optional, useful for tracking) purchase_token (str): Purchase token received from the app package_name (str): Name of the app's package (com.example.app) subscription_id (str): ID of the subscription access_token (str): OAuth access token Returns: Dictionary with subscription data or raises exceptions if there's an error """            url = (                f"https://androidpublisher.googleapis.com/androidpublisher/v3/applications/"                f"{package_name}/purchases/subscriptions/{subscription_id}/tokens/{purchase_token}"            )            headers = {                "Authorization": f"Bearer {access_token}",                "Accept": "application/json",                "User-Agent": "SecurePythonClient/1.0"            }            try:                response = requests.get(url, headers=headers)                response.raise_for_status()            except requests.HTTPError as e:                print(f"HTTP Error: {response.status_code} - {response.reason}")                try:                    print(json.dumps(response.json(), indent=2))                except Exception:                    print("Error parsing error response")                raise            except requests.RequestException as e:                print(f"Connection error: {str(e)}")                raise            return response.json()    

Ejemplo of use:

if name == "main": try: result = validate_android_subscription( id_user="user_456", purchase_token="TOKEN_DE_COMPRA", package_name="com.ejemplo.app", subscription_id="subs_mensual", access_token="ya29.a0AfH6SMA..." ) print("Valid Subscription:") print(json.dumps(result, indent=2)) except Exception as e: print("Error validating subscription:", str(e))

Leave a Comment