Как восстановить правильную транзакцию при использовании автообновляемых покупок в приложении?
Этот вопрос касается Auto-Renewable IAP и того, как они должны быть восстановлены.
Эти ссылки: this и это не помогли мне, к сожалению.
В моем приложении у меня есть пользователи, подписавшиеся на Автоматически обновляемые покупки в приложениях.
Они могут подписаться на 1, 6 или 12 месяцев.
Когда они подписываются, квитанция транзакции отправляется на мой сервер для проверки позже.
Я не проверять получение сразу же, так как это замедлит работу пользователя (для проверки запросов на проверку ячеек для яблок мне требуется 1 - 2 секунды). Вместо этого я использую наивный подход и предоставляю контент, на который подписаны пользователи, без прямой проверки квитанции. Я планирую задание cron для проверки каждой пользовательской квитанции один раз в день и отмены привилегий при устаревших квитанциях.
Теперь, поскольку в правилах яблок четко указано, что для приложений с автоматически возобновляемыми подписками требуется функция восстановления, я решил реализовать это.
Когда я пытаюсь восстановить покупки в режиме песочницы, используя:
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
Я получаю не только текущие подписки, но и все предыдущие подписки (включая устаревшие) в обратном вызове:
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
В настоящее время я пробовал свои IAP примерно 30 раз, а это означает, что указанному выше методу отправлено 30 различных транзакций (устаревших и активных). Для каждой из этих транзакций я загружаю квитанцию о транзакциях на свой веб-сервис для последующей проверки.
Теперь. В случае, если последняя транзакция имеет устаревшую квитанцию (но вторая - последняя транзакция была действительно действительна), она перезапишет текущую (действительную) квитанцию для текущего пользователя и, таким образом, отменит права пользователя для пользователя.
В основном, моя проблема заключается в том, что при вызове restoreCompletedTransactions
я получаю список как устаревших, так и активных транзакций. И на стороне сервера они могут аннулировать друг друга. Оптимально, я хотел бы получить только одну транзакцию (наиболее актуальную) и отправить ее на мой сервер для последующей проверки.
В целом, я думаю, мой главный вопрос:
Как я могу убедиться, что восстановлена только активная (то есть самая последняя) транзакция?
Ответы
Ответ 1
Мое решение: получить квитанцию и подтвердить ее против ваших идентификаторов продукта. Использование SKPaymentQueue.defaultQueue().restoreCompletedTransactions()
для автоматически возобновляемых подписки не имеет смысла, потому что:
- занимает слишком много времени, потому что он требует, чтобы проверка валидации приема вызывалась слишком много раз (один раз для каждой транзакции в прошлом).
- может привести к тому, что действительная проверка транзакции будет перезаписана последующей неудачной транзакцией.
Например, если у вас есть три продолжительности для вашей автоматической возобновляемой подписки, просто подтвердите получение один раз по отношению к трем идентификаторам продукта, связанным с тремя продолжительностью подписки.
Ответ 2
Я считаю, что вам придется обработать квитанцию и посмотреть "Оригинальную дату покупки и дату истечения срока подписки" (https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Chapters/Subscriptions.html) каждой покупки в квитанции, чтобы узнать, активна ли какая-либо покупка. Вы можете обработать квитанцию либо с помощью сервера, либо с помощью Apple, чтобы получить JSON для получения. Или, если вы работаете с iOS7, вы можете проверить квитанцию на устройстве, а также получить JSON (например, см. Полное решение для ЛОКАЛЬНО проверять поступления и пакеты в приложении квитанции на iOS 7). Если вы работаете с iOS7, у вас будет одна квитанция со всеми покупками, содержащимися в ней, с помощью:
[[NSBundle mainBundle] appStoreReceiptURL]
Ответ 3
Используя uniqueID
, используя KeyChains
, мы можем сохранить receipt
.
-(NSString*)checkUniqueIDInKeyChains {
NSString *uniqueID = [apDelegate.keyChain objectForKey:(__bridge id)kSecValueData];
return uniqueID;
}
-(void)saveUniqIDinKeyChain:(NSString*)uniqueID {
[apDelegate.keyChain setObject:uniqueID forKey:(__bridge id)kSecValueData];
}
-(NSString *)generateUUID {
//Check for the UDID in the keychain , if not present create else take it from keychain.
CFUUIDRef theUUID = CFUUIDCreate(NULL);
CFStringRef string = CFUUIDCreateString(NULL, theUUID);
CFRelease(theUUID);
return (__bridge NSString *)string;
}
-(NSDateFormatter*)getDateFormatter {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:@"GMT"]];
[dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]];
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
return dateFormatter;
}
Приобретите product
, проверив receipt
.
-(BOOL)isPurchaseExpired:(NSDate*)expireDate {
NSDateFormatter *dateFormatter = [self getDateFormatter];
NSString *expireDateString = [[NSUserDefaults standardUserDefaults] objectForKey:@"purchaseExpireDate"];
expireDate = [dateFormatter dateFromString:[expireDateString substringToIndex:18]];
NSComparisonResult result = [expireDate compare:[dateFormatter dateFromString: [dateFormatter stringFromDate:[NSDate date]]]];
NSLog(@"\n %@ \n %@ ", expireDate, [dateFormatter dateFromString:[dateFormatter stringFromDate:[NSDate date]]]);
if (result == NSOrderedAscending) {
NSLog(@"Current Date is Greater than the Purchased, allowing user to access the content");
return YES;
}
else if (result == NSOrderedDescending) {
NSLog(@"Current date is Smaller than the Purchase Date");
return NO;
}
else {
NSLog(@"Current and Purchase Dates are Equal , allowing user to access the content");
return YES;
}
}