Skip to content

Commit bd896ac

Browse files
committed
wip - Android 36, IAP library update
1 parent 745b9e2 commit bd896ac

9 files changed

Lines changed: 211 additions & 145 deletions

File tree

android/app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ kotlin {
8282
}
8383

8484
android {
85-
compileSdk 35
85+
compileSdk 36
8686

8787
compileOptions {
8888
sourceCompatibility = 17

android/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ rootProject.buildDir = '../build'
99
subprojects {
1010
afterEvaluate {
1111
android {
12-
compileSdkVersion 35
12+
compileSdkVersion 36
1313
}
1414
}
1515

lib/cubit/account_cubit.dart

Lines changed: 4 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -95,26 +95,22 @@ class AccountCubit extends Cubit<AccountState> {
9595
}
9696

9797
Future<bool> subscriptionSuccess(
98-
PurchasedItem purchasedItem,
98+
Purchase purchasedItem,
9999
bool isAndroid,
100100
Future<void> Function() ensureRemoteCreated,
101101
Future<void> Function() finishTransaction,
102102
) async {
103103
try {
104-
if (purchasedItem.purchaseToken == null && purchasedItem.transactionReceipt == null) {
104+
if (purchasedItem.purchaseToken == null) {
105105
emit(
106106
AccountSubscribeError(
107107
currentUser,
108-
'Unexpected error associating your subscription - we found no purchaseToken and no transactionReceipt',
108+
'Unexpected error associating your subscription - we found no purchaseToken',
109109
),
110110
);
111111
return false;
112112
}
113-
final success = await _userRepo.associate(
114-
currentUser,
115-
isAndroid ? 2 : 3,
116-
purchasedItem.purchaseToken ?? purchasedItem.transactionReceipt!,
117-
);
113+
final success = await _userRepo.associate(currentUser, isAndroid ? 2 : 3, purchasedItem.purchaseToken!);
118114
if (success) {
119115
// Our association request was successful but we rely on the subscription
120116
// provider to respond too. Ideally we'd set up a websocket to receive an
@@ -301,25 +297,6 @@ class AccountCubit extends Cubit<AccountState> {
301297
await _userRepo.setQuickUnlockUser(user);
302298
emitAuthenticatedOrExpiredOrUnvalidated(user);
303299

304-
final psi = PaymentService.instance;
305-
await psi.ensureReady();
306-
if (KeeVaultPlatform.isIOS &&
307-
user.subscriptionStatus == AccountSubscriptionStatus.current &&
308-
psi.activePurchaseItem != null &&
309-
(user.subscriptionId?.startsWith('ap_') ?? false)) {
310-
// It's impossible to know what the expected subscriptionId is because apple don't
311-
// give us the originaltransactionid unless it is a pointless restoration operation
312-
// to a new phone. So all subscription renewals would sit in the queue forever while
313-
// we have no way to know that we have dealt with them. Thus we just accept that
314-
// everything is probably fine as long as the user has a subscription from the App Store.
315-
// Maybe a problem for subscription restarts after an expiry. User's subscription ID
316-
// is going to stay the same even after expiry but then a new one should come along
317-
// with a whole new original transaction id... or not, if there is some reuse during
318-
// grace periods, etc. But surely in all other cases, any valid app store subscription
319-
// id associated with a user that has a non-expired set of authentication tokens is
320-
// going to be just a renewal operation that we can ignore because we handle it server-side.
321-
await psi.finishTransaction(psi.activePurchaseItem!);
322-
}
323300
l.d('sign in complete');
324301
return user;
325302
}

lib/extension_methods.dart

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,13 +166,57 @@ T? enumFromString<T>(Iterable<T> values, String value) {
166166
return values.firstWhereOrNull((type) => type.toString().split('.').last == value);
167167
}
168168

169-
extension KVPurchasedItem on PurchasedItem {
169+
extension KVPurchasedItem on Purchase {
170170
String get keeVaultSubscriptionId {
171-
final prefix = purchaseStateAndroid != null ? 'gp_' : 'ap_';
171+
final prefix = this is PurchaseAndroid ? 'gp_' : 'ap_';
172172
if (KeeVaultPlatform.isIOS) {
173173
// Apple does not give us the information needed to derive this information
174+
//TODO:f: maybe now available in purchaseToken with StoreKit2 but we already
175+
// have implemented support for the ID to be changed upon receiving the real
176+
// ID from the server so not likely to be worth changing this.
174177
return prefix;
175178
}
176179
return prefix + (purchaseToken ?? '');
177180
}
178181
}
182+
183+
ActiveSubscription purchaseAsActiveSubscription(Purchase purchase) {
184+
// Map platform-specific fields
185+
final bool? autoRenewing = purchase is PurchaseAndroid ? purchase.autoRenewingAndroid : null;
186+
final String? basePlanId = purchase is PurchaseAndroid ? purchase.currentPlanId : null;
187+
final String? environmentIOS = purchase is PurchaseIOS ? purchase.environmentIOS : null;
188+
final double? expirationDateIOS = purchase is PurchaseIOS ? purchase.expirationDateIOS : null;
189+
final RenewalInfoIOS? renewalInfoIOS = purchase is PurchaseIOS ? purchase.renewalInfoIOS : null;
190+
191+
// Calculate daysUntilExpirationIOS and willExpireSoon for iOS
192+
double? daysUntilExpirationIOS;
193+
bool? willExpireSoon;
194+
if (expirationDateIOS != null) {
195+
final expirationDate = DateTime.fromMillisecondsSinceEpoch(expirationDateIOS.toInt());
196+
final now = DateTime.now();
197+
final daysUntilExpiration = expirationDate.difference(now).inDays;
198+
daysUntilExpirationIOS = daysUntilExpiration.toDouble();
199+
// Consider subscription expiring soon if < 7 days remaining
200+
willExpireSoon = daysUntilExpiration > 0 && daysUntilExpiration < 7;
201+
}
202+
203+
// Create ActiveSubscription from Purchase
204+
return ActiveSubscription(
205+
productId: purchase.productId,
206+
transactionId: purchase.transactionIdFor ?? purchase.purchaseToken ?? '',
207+
purchaseToken: purchase.purchaseToken,
208+
transactionDate: purchase.transactionDate is String
209+
? double.tryParse(purchase.transactionDate as String) ?? 0.0
210+
: (purchase.transactionDate as num?)?.toDouble() ?? 0.0,
211+
isActive: purchase.purchaseState == PurchaseState.Purchased,
212+
autoRenewingAndroid: autoRenewing,
213+
basePlanIdAndroid: basePlanId,
214+
currentPlanId: purchase.currentPlanId,
215+
daysUntilExpirationIOS: daysUntilExpirationIOS,
216+
environmentIOS: environmentIOS,
217+
expirationDateIOS: expirationDateIOS,
218+
renewalInfoIOS: renewalInfoIOS,
219+
purchaseTokenAndroid: purchase is PurchaseAndroid ? purchase.purchaseToken : null,
220+
willExpireSoon: willExpireSoon,
221+
);
222+
}

0 commit comments

Comments
 (0)