Validar compras en aplicación Android usando Python

Tiempo de lectura: 2 minutos

Hoy vamos a aprender a validar compras in app usando Python y una aplicación desarrollada en Android. Servirá para las apps desarrolladas en Android nativo, ionic, kotlin, flutter, react native o la tecnología que quieras.

Flotes - Pexels

Lo primero de todo es crear un refresh token y datos de token oauth 2.0 para ello seguimos este tutorial:

https://devcodelight.com/como-obtener-el-refresh-token-de-google-paso-a-paso-oauth-2-0

Ahora vamos a implementar el código que nos permitirá validar las compras:

Primero debemos obtener el token de acceso usando nuestro refresh token (obtenido en el punto anterior).

Para obtener el token de acceso es:

import requests

def intercambiar_codigo_por_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()

Con esto obtendrás el token de acceso.

Y recibes este:

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

Y ahora para validar hacemos lo siguiente:

Validar compras de productos:

import requests
import json


def validate_android_purchase(
    id_user: str,
    purchase_token: str,
    package_name: str,
    subscription_id: str,
    access_token: str
) -> str:
    """
    Valida una compra de Google Play (producto in-app).

    Retorna:
        - order_id (str) si es válida
        - "-1" si hay un error en la respuesta de Google
        - lanza excepción si falla la conexión o la petición
    """

    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 al parsear la respuesta de error")
        raise
    except requests.RequestException as e:
        print(f"Error de conexión: {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 validando compra:", str(e))

Validar compras de suscripciones:

import requests
import json


def validate_android_subscription(
    id_user: str,
    purchase_token: str,
    package_name: str,
    subscription_id: str,
    access_token: str
) -> dict:
    """
    Valida una suscripción de Google Play.

    Args:
        id_user (str): ID interno del usuario (opcional, útil para trazabilidad)
        purchase_token (str): Token de compra recibido desde la app
        package_name (str): Nombre del paquete de la app (com.ejemplo.app)
        subscription_id (str): ID del producto de suscripción
        access_token (str): Token de acceso OAuth válido

    Returns:
        dict con los datos de la suscripción, o lanza excepción si hay 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 al parsear la respuesta de error")
        raise
    except requests.RequestException as e:
        print(f"Error de conexión: {str(e)}")
        raise

    return response.json()

Ejemplo de uso:

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("Suscripción válida:")
        print(json.dumps(result, indent=2))

    except Exception as e:
        print("Error validando suscripción:", str(e))

Deja un comentario