Files
observer/flutter_app/lib/payment/license_service.dart
T

80 lines
2.8 KiB
Dart

import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import '../auth/session_store.dart';
import 'models.dart';
import 'order_api.dart';
/// 授权管理:本地缓存用于主界面展示到期时间;识别入口强制服务端校验。
/// 缓存按手机号隔离(键含账号),切换账号不会读到上一账号的到期时间。
class LicenseService {
static const _storage = FlutterSecureStorage();
final OrderApi orderApi;
final SessionStore sessionStore;
LicenseService({required this.orderApi, required this.sessionStore});
/// 主界面展示/启动加载:先读本账号本地缓存,有效则直接用;
/// 无效或缺失时询问服务端(网络失败按缓存兜底,未登录按过期处理)。
Future<LicenseStatus> check() async {
final phone = await sessionStore.readPhone();
if (phone == null) return const LicenseStatus(active: false);
final cached = await _readCache(phone);
final now = DateTime.now();
if (cached != null && cached.expiresAt!.isAfter(now)) return cached;
if (await sessionStore.readToken() == null) {
return const LicenseStatus(active: false);
}
try {
final remote = await orderApi.fetchLicense();
try {
if (remote.active && remote.expiresAt != null) {
await _writeCache(phone, remote.expiresAt!);
} else if (!remote.active) {
await _clearCache(phone);
}
} catch (_) {
// 本地缓存不可用不影响授权状态展示
}
return remote;
} on OrderApiException {
return cached ?? const LicenseStatus(active: false);
}
}
/// 识别入口强制校验:必须走服务端且 active 才放行,失败即抛错(不进相机)
Future<LicenseStatus> verifyServer() async {
if (await sessionStore.readToken() == null) {
throw const SessionExpiredException();
}
return orderApi.fetchLicense();
}
/// 支付成功后立即刷新(清缓存强制走服务端,避免旧授权干扰)
Future<LicenseStatus> refresh() async {
final phone = await sessionStore.readPhone();
if (phone != null) await _clearCache(phone);
return orderApi.fetchLicense();
}
Future<LicenseStatus?> _readCache(String phone) async {
try {
final raw = await _storage.read(key: _cacheKey(phone));
final t = raw == null ? null : DateTime.tryParse(raw);
if (t == null || !t.isAfter(DateTime.now())) return null;
return LicenseStatus(active: true, expiresAt: t);
} catch (_) {
return null;
}
}
Future<void> _writeCache(String phone, DateTime expiresAt) =>
_storage.write(key: _cacheKey(phone), value: expiresAt.toIso8601String());
Future<void> _clearCache(String phone) => _storage.delete(key: _cacheKey(phone));
static String _cacheKey(String phone) => 'license_expires_at:$phone';
}