fix(compras): revocar el PRO tras un reembolso sin penalizar a quien pago
Build & Deploy PluriWave / Análisis de código (push) Successful in 41s
Build & Deploy PluriWave / Build APK + AAB release (push) Successful in 4m8s

Verificacion silenciosa en segundo plano con queryPastPurchases, al abrir o
volver a la app y al cargar Android Auto. Solo revoca tras dos respuestas
validas de Play sin la compra separadas 12h; sin red o con error no toca nada.
Reactiva el PRO automaticamente si Play confirma la compra.
This commit is contained in:
Javier Bautista Fernández
2026-09-18 11:45:43 +02:00
parent d7366bbf99
commit 0b7919e72e
14 changed files with 1163 additions and 17 deletions
+38
View File
@@ -20,6 +20,7 @@ import 'emisoras_destacadas.dart';
import 'musica_local_auto.dart';
import 'navegacion_auto.dart';
import 'servicio_audio_session.dart';
import 'verificacion_licencia.dart' show CambioLicencia;
/// Estado de reproducción expuesto al UI.
enum EstadoReproduccion {
@@ -383,6 +384,36 @@ void invalidarArbolAuto() {
_invalidarArbolAutoGlobal?.call();
}
/// Silent license re-verification for the headless Android Auto engine,
/// registered from `main.dart` (it owns the purchase port and the prefs).
/// `null` until registered — [dispararVerificacionLicenciaAuto] is then a
/// no-op.
Future<CambioLicencia> Function()? _verificarLicenciaAutoGlobal;
/// Registers (or, with `null`, clears) the verification
/// [dispararVerificacionLicenciaAuto] runs. Module-level like every other
/// `registrar*` seam here, so tests need no real handler.
void registrarVerificacionLicenciaAuto(
Future<CambioLicencia> Function()? verificar,
) {
_verificarLicenciaAutoGlobal = verificar;
}
/// Runs the registered license check and, if it changed the persisted flag
/// in either direction, invalidates the cached car tree (local music is
/// premium-gated). Never throws. The browse path calls it UNAWAITED so a
/// head unit's browse answer is never delayed by a Play query.
Future<void> dispararVerificacionLicenciaAuto() async {
final verificar = _verificarLicenciaAutoGlobal;
if (verificar == null) return;
try {
final cambio = await verificar();
if (cambio != CambioLicencia.sinCambios) invalidarArbolAuto();
} catch (e) {
debugPrint('[PluriWave][licencia] verificacion Auto fallida $e');
}
}
/// Whether a head unit has actually SUBSCRIBED to at least one browse id on
/// the live handler (fix/android-auto-musica-local, item 4 — corrected).
///
@@ -3472,6 +3503,13 @@ class PluriWaveAudioHandler extends BaseAudioHandler
// [resolverLocalizacionesRespaldo], so this works on the engine
// Android Auto starts without an Activity -- which is the only engine
// a Play reviewer ever gets.
// Silent license re-verification (refund revocation), on the root
// only — every car connection asks for it — and fire-and-forget: the
// browse answer below is served from the persisted flag right away,
// and the check is throttled to at most once a day.
if (parentMediaId == AudioService.browsableRootId) {
unawaited(dispararVerificacionLicenciaAuto());
}
final etiquetas = etiquetasArbolAutoDesde(_textos);
final constructor = ConstructorArbolAuto(etiquetas: etiquetas);
// The "recent" root, resolved BEFORE the entitlement gate.