88 lines
2.8 KiB
Dart
88 lines
2.8 KiB
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import '../../../../core/network/dio_client.dart';
|
|
|
|
class LatestInvoice {
|
|
final String invoiceNumber;
|
|
final String status;
|
|
final String currency;
|
|
final double amount;
|
|
|
|
const LatestInvoice({
|
|
required this.invoiceNumber,
|
|
required this.status,
|
|
required this.currency,
|
|
required this.amount,
|
|
});
|
|
|
|
factory LatestInvoice.fromJson(Map<String, dynamic> json) => LatestInvoice(
|
|
invoiceNumber: json['invoiceNumber'] as String? ?? '',
|
|
status: json['status'] as String? ?? '',
|
|
currency: json['currency'] as String? ?? 'USD',
|
|
amount: (json['totalAmount'] as num?)?.toDouble() ?? 0,
|
|
);
|
|
}
|
|
|
|
class BillingOverview {
|
|
final String planDisplayName;
|
|
final String subscriptionStatus;
|
|
final DateTime? currentPeriodEnd;
|
|
final DateTime? trialEndsAt;
|
|
final int usedTokens;
|
|
final int limitTokens;
|
|
final bool overageAllowed;
|
|
final LatestInvoice? latestInvoice;
|
|
|
|
const BillingOverview({
|
|
required this.planDisplayName,
|
|
required this.subscriptionStatus,
|
|
required this.currentPeriodEnd,
|
|
required this.trialEndsAt,
|
|
required this.usedTokens,
|
|
required this.limitTokens,
|
|
required this.overageAllowed,
|
|
required this.latestInvoice,
|
|
});
|
|
}
|
|
|
|
final billingOverviewProvider = FutureProvider<BillingOverview>((ref) async {
|
|
final dio = ref.read(dioClientProvider);
|
|
|
|
// Fetch subscription, quota, and recent invoice (ignore 404s gracefully)
|
|
Map<String, dynamic>? sub;
|
|
Map<String, dynamic>? quota;
|
|
Map<String, dynamic>? invoicesData;
|
|
|
|
try {
|
|
final r = await dio.get('/api/v1/billing/subscription');
|
|
sub = r.data as Map<String, dynamic>?;
|
|
} catch (_) {}
|
|
|
|
try {
|
|
final r = await dio.get('/api/v1/billing/usage/current');
|
|
quota = r.data as Map<String, dynamic>?;
|
|
} catch (_) {}
|
|
|
|
try {
|
|
final r = await dio.get('/api/v1/billing/invoices?limit=1');
|
|
invoicesData = r.data as Map<String, dynamic>?;
|
|
} catch (_) {}
|
|
|
|
final invoices = (invoicesData?['data'] as List?)?.cast<Map<String, dynamic>>() ?? [];
|
|
final latestInvoice = invoices.isNotEmpty ? LatestInvoice.fromJson(invoices.first) : null;
|
|
|
|
return BillingOverview(
|
|
planDisplayName: ((sub?['plan'] as Map<String, dynamic>?)?['displayName'] as String?) ?? 'Free',
|
|
subscriptionStatus: sub?['status'] as String? ?? 'unknown',
|
|
currentPeriodEnd: sub?['currentPeriodEnd'] != null
|
|
? DateTime.tryParse(sub!['currentPeriodEnd'] as String)
|
|
: null,
|
|
trialEndsAt: sub?['trialEndsAt'] != null
|
|
? DateTime.tryParse(sub!['trialEndsAt'] as String)
|
|
: null,
|
|
usedTokens: quota?['usedTokens'] as int? ?? 0,
|
|
limitTokens: quota?['limitTokens'] as int? ?? 0,
|
|
overageAllowed: quota?['overageAllowed'] as bool? ?? false,
|
|
latestInvoice: latestInvoice,
|
|
);
|
|
});
|