Skip to content

Commit ddba303

Browse files
author
Christian Bartsch
committed
Subscription receipt validation: prevent problems if the receipts are not in date-order (find the latest expiry date for each subscription ID)
1 parent c3ccfde commit ddba303

File tree

1 file changed

+36
-11
lines changed

1 file changed

+36
-11
lines changed

SoomlaiOSStore/SoomlaStore.m

+36-11
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ - (void)verifySubscriptions {
113113
isoDate.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ";
114114
isoDate.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
115115

116+
NSMutableDictionary *latestExpiryDatePerProduct = [NSMutableDictionary new];
116117
NSArray *iapDataArray = [VerifyStoreReceipt obtainInAppPurchases:path];
117118
for(NSDictionary *iapData in iapDataArray) {
118119
NSString *itemId = iapData[@"ProductIdentifier"];
@@ -134,26 +135,50 @@ - (void)verifySubscriptions {
134135
continue;
135136
}
136137

137-
BOOL isPurchased = NO;
138138
if(!((PurchaseWithMarket *)pvi.purchaseType).isSubscription) {
139-
LogDebug(TAG, ([NSString stringWithFormat:@"Receipt for non-consumable item \"%@\" found, setting purchased to true.", pvi.itemId]))
140-
isPurchased = YES;
139+
LogDebug(TAG, ([NSString stringWithFormat:@"Receipt for non-consumable item \"%@\" found, setting purchased to true.", pvi.itemId]));
140+
141+
[pvi resetBalance:1];
141142
} else if(iapData[@"SubExpDate"]) {
142143
// found receipt for a subscription
143144
LogDebug(TAG, ([NSString stringWithFormat:@"Receipt for subscription item \"%@\" found: %@", pvi.itemId, iapData]));
144145

145146
NSDate *expireDate = [isoDate dateFromString:iapData[@"SubExpDate"]];
146-
// sub expire date is after current date
147-
// NOTE: checking local date is not optimal as users can just change the date in settings
148-
NSDate *now = [NSDate date];
149-
LogDebug(TAG, ([NSString stringWithFormat:@"Subscription expire date: %@, current date: %@", expireDate, now]));
150-
if([expireDate compare:now] == NSOrderedDescending) {
151-
LogDebug(TAG, @"Subscription still active, setting purchased to true.");
152-
isPurchased = YES;
147+
NSDate *latest = [latestExpiryDatePerProduct objectForKey:itemId];
148+
149+
if(latest == nil || [expireDate compare:latest] == NSOrderedDescending) {
150+
[latestExpiryDatePerProduct setValue:expireDate forKey:itemId];
153151
}
154152
}
153+
}
155154

156-
if(isPurchased) {
155+
for(NSString *itemId in latestExpiryDatePerProduct) {
156+
PurchasableVirtualItem *pvi;
157+
@try {
158+
// purchasableItemWithProductId: throws exception if the item does not exist
159+
// this happens if the user has previously bought an IAP (non-consumable or subscription)
160+
// and the developer removed/changed product IDs afterwards
161+
pvi = [[StoreInfo getInstance] purchasableItemWithProductId:itemId];
162+
} @catch(VirtualItemNotFoundException *ex) {
163+
LogError(TAG, ([NSString stringWithFormat:@"Couldn't find the VirtualCurrencyPack OR MarketItem with productId: %@ from the IAP receipt"
164+
@". It's unexpected so an unexpected error is being emitted.", itemId]));
165+
166+
[StoreEventHandling postUnexpectedError:ERR_GENERAL forObject:self];
167+
continue;
168+
}
169+
170+
if(pvi == nil || ![pvi.purchaseType isKindOfClass:[PurchaseWithMarket class]]) {
171+
continue;
172+
}
173+
174+
// sub expire date is after current date
175+
// NOTE: checking local date is not optimal as users can just change the date in settings
176+
NSDate *expireDate = [latestExpiryDatePerProduct objectForKey:itemId];
177+
NSDate *now = [NSDate date];
178+
179+
LogDebug(TAG, ([NSString stringWithFormat:@"Subscription expire date for item %@: %@, current date: %@", itemId, expireDate, now]));
180+
if([expireDate compare:now] == NSOrderedDescending) {
181+
LogDebug(TAG, @"Subscription still active, setting purchased to true.");
157182
[pvi resetBalance:1];
158183
} else {
159184
LogDebug(TAG, @"Subscription not active, setting purchased to false.");

0 commit comments

Comments
 (0)