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.

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
O utilizamos el nuevo método para obtener el access token:
https://devcodelight.com/crear-service-account-para-generar-acess-tokens-de-google-api-para-validar-compras-en-aplicacion-compras-in-app-en-android/
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"
}
Para refrescar token de acceso:
def obtener_token_acceso(refresh_token: str) -> dict:
url = "https://oauth2.googleapis.com/token"
data = {
"refresh_token": refresh_token,
"client_id": CLIENT_ID_GOOGLE_API,
"client_secret": CLIENT_SECRET_GOOGLE_API,
"grant_type": "refresh_token"
}
headers = {
"Content-Type": "application/x-www-form-urlencoded",
"Accept": "application/json"
}
try:
response = requests.post(url, data=data, headers=headers)
response.raise_for_status()
token_data = response.json()
return token_data["access_token"]
except requests.HTTPError as e:
print(f"HTTP Error: {e.response.status_code} - {e.response.text}")
raise
except Exception as e:
print(f"Error inesperado: {str(e)}")
raise
Y ahora, para validar hacemos lo siguiente:
Validar compras de productos:
import requests
import json
def validate_android_purchase(
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:
access_token = obtener_token_acceso(refresh_token_google_IAP)
order_id = validate_android_purchase(
purchase_token="PURCHASE_TOKEN_AQUÍ",
package_name="com.tu.app",
subscription_id="producto_id",
access_token=access_token
)
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(
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:
access_token = obtener_token_acceso(refresh_token_google_IAP)
result = validate_android_subscription(
purchase_token="TOKEN_DE_COMPRA",
package_name="com.ejemplo.app",
subscription_id="subs_mensual",
access_token=access_token
)
print("Suscripción válida:")
print(json.dumps(result, indent=2))
except Exception as e:
print("Error validando suscripción:", str(e))
NOTA: Al verificar puede devolver un error que indica que debemos completar los permisos https://console.developers.google.com/apis/api/androidpublisher.googleapis.com/overview?project=ID_PROYECTO
Tendrás que habilitar Google Play Android Developer API desde google console.

Ingeniero en Informática, Investigador, me encanta crear cosas o arreglarlas y darles una nueva vida. Escritor y poeta. Más de 20 APPs publicadas y un libro en Amazon.
2 comentarios en «Validar compras en aplicación Android usando Python»