Last active
December 6, 2016 12:58
-
-
Save elmodos/d7332149875f890c3e1b79a5f32e69a4 to your computer and use it in GitHub Desktop.
Check iOS auto-renewable subscriptions if they are still valid
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
- (void)validateRenewablePurchases:(NSArray<NSString *> *)products callback:(void (^)(NSDictionary *values, NSString *errorMessage))callback | |
{ | |
// Sending into background | |
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ @autoreleasepool{ | |
void (^callbackOnMainThread)(NSDictionary *values, NSString *errorMessage) = ^(NSDictionary *values, NSString *errorMessage) { | |
dispatch_async(dispatch_get_main_queue(), ^{ | |
callback(values, errorMessage); | |
}); | |
}; | |
// Load the receipt from the app bundle. | |
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL]; | |
NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL]; | |
// | |
if (receiptData) | |
{ | |
BOOL isSandbox = [[receiptURL lastPathComponent] isEqualToString:@"sandboxReceipt"]; | |
// Make JSON object that describes the request | |
NSDictionary *requestContents = @{ | |
@"receipt-data" : [receiptData base64EncodedStringWithOptions:0], | |
@"password" : kStoreKitReceiptValidationSharedSecred | |
}; | |
// | |
NSError *error = nil; | |
NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents options:0 error:nil]; | |
if (requestData) | |
{ | |
// Create a POST request with the receipt data. | |
NSURL *storeURL = isSandbox ? | |
[NSURL URLWithString:@"https://sandbox.itunes.apple.com/verifyReceipt"] : | |
[NSURL URLWithString:@"https://buy.itunes.apple.com/verifyReceipt"]; | |
// | |
NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:storeURL]; | |
[storeRequest setHTTPMethod:@"POST"]; | |
[storeRequest setHTTPBody:requestData]; | |
// Sending synchronous request in place | |
error = nil; | |
NSURLResponse *response = nil; | |
NSData *resData = [NSURLConnection sendSynchronousRequest:storeRequest returningResponse:&response error:&error]; | |
if (error) | |
{ | |
callbackOnMainThread(nil, error.localizedDescription); | |
} | |
else | |
{ | |
NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:resData options:0 error:&error]; | |
if (jsonResponse == nil) | |
{ | |
callbackOnMainThread(nil, error.localizedDescription); | |
} | |
else | |
{ | |
NSLog(@"jsonResponse:%@", jsonResponse); | |
// | |
NSArray *latestReceipts = jsonResponse[@"latest_receipt_info"]; | |
if ([latestReceipts isKindOfClass:[NSArray class]]) | |
{ | |
// | |
NSMutableDictionary *result = [NSMutableDictionary dictionary]; | |
// get every requested product | |
for (NSString *product in products) | |
{ | |
// Filter out items with requested product | |
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) { | |
return [evaluatedObject[@"product_id"] isEqual:product]; | |
}]; | |
NSArray *filteredReceipts = [latestReceipts filteredArrayUsingPredicate:predicate]; | |
NSArray *sortedFiltered = [filteredReceipts sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { | |
return [obj1[@"expires_date_ms"] compare:obj2[@"expires_date_ms"]]; | |
}]; | |
// Get maximum value of expiration date | |
NSString *maxTimeStringMs = sortedFiltered.lastObject[@"expires_date_ms"]; | |
if ([maxTimeStringMs isKindOfClass:[NSString class]]) | |
{ | |
// crap starts here | |
// Was "expires_date_ms": "1479951889000" | |
NSString *maxTimeStringSeconds = [maxTimeStringMs substringToIndex:maxTimeStringMs.length - 3]; | |
// Now "expires_date_ms": "1479951889" | |
// Now we can fit double | |
NSTimeInterval doubleValue = [maxTimeStringSeconds doubleValue]; | |
NSDate *expirationDate = [NSDate dateWithTimeIntervalSince1970:doubleValue]; | |
NSDate *now = [NSDate date]; | |
BOOL isValid = [expirationDate compare:now] == NSOrderedDescending; | |
result[product] = [NSNumber numberWithBool:isValid]; | |
} | |
} | |
callbackOnMainThread(result, nil); | |
} | |
else | |
{ | |
callbackOnMainThread(nil, nil); | |
} | |
} | |
} | |
} | |
else | |
{ | |
// smth went wrong with JSON encoding | |
callbackOnMainThread(nil, error.localizedDescription); | |
} | |
} | |
else | |
{ | |
// no receipt, no message, be silent | |
callbackOnMainThread(nil, nil); | |
} | |
}}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment