这个问题涉及自动可再生IAP以及如何恢复它们。 这些链接:this和this不幸地帮助了我。
在我的应用中,我有用户订阅自动续订应用内购买。 他们可以订阅1个月,6个月或12个月。
订阅时,交易收据会发送到我的服务器,以便以后验证。 我不立即验证收据,因为它会降低用户体验的速度(对苹果服务器的收据验证查询对我来说需要大约1-2秒)。相反,我使用天真的方法并提供用户订阅的内容,而不进行任何直接收据验证。我安排了一个cron作业,每天验证每个用户的收据,并撤销过期收据的权限。
既然苹果指南明确指出具有自动续订订阅的应用程序需要恢复功能,我选择实现它。
当我尝试在沙盒模式下恢复购买时,请使用:
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
我不仅获得当前订阅,还获得回调中所有先前订阅(包括过时订阅):
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
目前我已尝试了大约30次IAP,这意味着上述方法发送了30个不同的事务(过时和活动)。对于这些交易中的每一个,我都将交易收据上传到我的Web服务以供以后验证。
现在。如果最后一个事务有一个过时的收据(但倒数第二个事务实际上是有效的),它将覆盖当前用户的当前(有效)收据,从而错误地撤销用户的权限。
基本上我的问题是,在调用restoreCompletedTransactions
时,我获得了过时和活动交易的列表。在服务器端,它们可能会互相失效。最理想的情况是,我只想检索一个事务(最相关的)并将该收据发送到我的服务器以供以后验证。
总而言之,我想我的主要问题是:
如何确保只恢复活动(即最新)的事务?
答案 0 :(得分:2)
我的解决方案:检索收据并根据您的productIdentifiers对其进行验证。使用SKPaymentQueue.defaultQueue().restoreCompletedTransactions()
进行自动续订订阅没有意义,因为:
例如,如果您的自动续订订阅有三个持续时间,则只需针对与三个订阅持续时间关联的三个productIdentifier验证收据。
答案 1 :(得分:0)
我相信您必须处理收据并查看收据中每次购买的“原始购买日期和订阅到期日期”(https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Chapters/Subscriptions.html),以查看特定购买是否仍然有效。您可以使用服务器处理收据,并使用Apple进行验证以获取收据的JSON。或者,如果您使用的是iOS7,则可以验证设备上的收据,并获取JSON(例如,请参阅A complete solution to LOCALLY validate an in-app receipts and bundle receipts on iOS 7)。如果您使用的是iOS7,您将获得一张单收据,其中包含所有购买内容:
[[NSBundle mainBundle] appStoreReceiptURL]
答案 2 :(得分:0)
使用 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;
}
}