我正在尝试实现ray wenderlich示例:http://www.raywenderlich.com/36270/in-app-purchases-non-renewing-subscription-tutorial
第一次购买产品完全没有任何错误。 当我尝试恢复购买时,我有一个响应,有0个事务要在方法中恢复:
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
奇怪的是,当我试图再购买同样的产品时,我发出警告说我已经购买了该产品,我可以续订或延长。
为什么我不能在尝试再次购买时恢复交易呢?
- (id)initWithProductIdentifiers:(NSSet *)productIdentifiers
{
if ((self = [super init])) {
// Store product identifiers
_productIdentifiers = productIdentifiers;
// Check for previously purchased products
_purchasedProductIdentifiers = [NSMutableSet set];
for (NSString * productIdentifier in _productIdentifiers) {
BOOL productPurchased = [[NSUserDefaults standardUserDefaults] boolForKey:productIdentifier];
if (productPurchased) {
[_purchasedProductIdentifiers addObject:productIdentifier];
NSLog(@"Previously purchased: %@", productIdentifier);
} else {
NSLog(@"Not purchased: %@", productIdentifier);
}
}
// Add self as transaction observer
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}
return self;
}
- (void)requestProductsWithCompletionHandler:(RequestProductsCompletionHandler)completionHandler
{
_completionHandler = [completionHandler copy];
_productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:_productIdentifiers];
_productsRequest.delegate = self;
[_productsRequest start];
}
- (BOOL)productPurchased:(NSString *)productIdentifier
{
NSLog(@"%s, productIdentifier: %@", __PRETTY_FUNCTION__, productIdentifier);
return [_purchasedProductIdentifiers containsObject:productIdentifier];
}
- (void)buyProduct:(SKProduct *)product
{
NSLog(@"Buying %@...", product.productIdentifier);
SKPayment * payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
- (void)validateReceiptForTransaction:(SKPaymentTransaction *)transaction
{
VerificationController * verifier = [VerificationController sharedInstance];
[verifier verifyPurchase:transaction completionHandler:^(BOOL success) {
if (success) {
NSLog(@"Successfully verified receipt!");
[self provideContentForProductIdentifier:transaction.payment.productIdentifier];
}
else {
NSLog(@"Failed to validate receipt.");
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
}];
}
-(int)daysRemainingOnSubscription
{
NSDate * expiryDate = [[NSUserDefaults standardUserDefaults] objectForKey:kSubscriptionExpirationDateKey];
NSDateFormatter *dateformatter = [NSDateFormatter new];
[dateformatter setDateFormat:@"dd MM yyyy"];
NSTimeInterval timeInt = [[dateformatter dateFromString:[dateformatter stringFromDate:expiryDate]] timeIntervalSinceDate: [dateformatter dateFromString:[dateformatter stringFromDate:[NSDate date]]]]; //Is this too complex and messy?
int days = timeInt / 60 / 60 / 24;
if (days >= 0) {
return days;
} else {
return 0;
}
}
-(NSString *)getExpiryDateString
{
if ([self daysRemainingOnSubscription] > 0) {
NSDate *today = [[NSUserDefaults standardUserDefaults] objectForKey:kSubscriptionExpirationDateKey];
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:@"dd/MM/yyyy"];
return [NSString stringWithFormat:@"Subscribed! \nExpires: %@ (%i Days)",[dateFormat stringFromDate:today],[self daysRemainingOnSubscription]];
} else {
return @"Not Subscribed";
}
}
-(NSDate *)getExpiryDateForMonths:(int)months
{
NSDate *originDate;
if ([self daysRemainingOnSubscription] > 0) {
originDate = [[NSUserDefaults standardUserDefaults] objectForKey:kSubscriptionExpirationDateKey];
} else {
originDate = [NSDate date];
}
NSDateComponents *dateComp = [[NSDateComponents alloc] init];
[dateComp setMonth:months];
return [[NSCalendar currentCalendar] dateByAddingComponents:dateComp toDate:originDate options:0];
}
-(void)purchaseSubscriptionWithMonths:(int)months
{
NSDate *expiryDate = [self getExpiryDateForMonths:months];
[[NSUserDefaults standardUserDefaults] setObject:expiryDate forKey:kSubscriptionExpirationDateKey];
[[NSUserDefaults standardUserDefaults] synchronize];
NSLog(@"Subscription Complete!");
}
#pragma mark - SKProductsRequestDelegate
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
NSLog(@"Loaded list of products...");
_productsRequest = nil;
NSArray * skProducts = response.products;
for (SKProduct * skProduct in skProducts) {
NSLog(@"Found product: %@ %@ %0.2f",
skProduct.productIdentifier,
skProduct.localizedTitle,
skProduct.price.floatValue);
}
_completionHandler(YES, skProducts);
_completionHandler = nil;
}
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error
{
NSLog(@"Failed to load list of products.");
_productsRequest = nil;
_completionHandler(NO, nil);
_completionHandler = nil;
}
#pragma mark SKPaymentTransactionOBserver
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
NSLog(@"%s", __PRETTY_FUNCTION__);
for (SKPaymentTransaction * transaction in transactions) {
if (transaction.transactionState == SKPaymentTransactionStatePurchased) {
NSLog(@"transaction.transactionState == SKPaymentTransactionStatePurchased");
[self completeTransaction:transaction];
}
else if (transaction.transactionState == SKPaymentTransactionStateRestored) {
NSLog(@"transaction.transactionState == SKPaymentTransactionStateRestored");
[self restoreTransaction:transaction];
}
else if (transaction.transactionState == SKPaymentTransactionStateFailed) {
NSLog(@"transaction.transactionState == SKPaymentTransactionStateFailed");
[self failedTransaction:transaction];
}
else {
NSLog(@"None of the above.");
}
};
}
- (void)completeTransaction:(SKPaymentTransaction *)transaction
{
NSLog(@"completeTransaction...");
[self validateReceiptForTransaction:transaction];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
- (void)restoreTransaction:(SKPaymentTransaction *)transaction
{
NSLog(@"restoreTransaction...");
[self validateReceiptForTransaction:transaction];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
- (void)failedTransaction:(SKPaymentTransaction *)transaction
{
NSLog(@"failedTransaction...");
if (transaction.error.code != SKErrorPaymentCancelled)
{
NSLog(@"Transaction error: %@", transaction.error.localizedDescription);
}
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (void)provideContentForProductIdentifier:(NSString *)productIdentifier
{
NSLog(@"%s, productIdentifier: %@", __PRETTY_FUNCTION__, productIdentifier);
if ([productIdentifier isEqualToString:@"com.cellularradar.onemonth"]) {
[self purchaseSubscriptionWithMonths:1];
}
else if ([productIdentifier isEqualToString:@"com.cellularradar.threemonths"]) {
[self purchaseSubscriptionWithMonths:3];
}
else if ([productIdentifier isEqualToString:@"com.cellularradar.sixmonths"]) {
[self purchaseSubscriptionWithMonths:6];
}
else if ([productIdentifier isEqualToString:@"com.cellularradar.twelvemonths"]) {
[self purchaseSubscriptionWithMonths:12];
}
[_purchasedProductIdentifiers addObject:productIdentifier];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:productIdentifier];
[[NSUserDefaults standardUserDefaults] synchronize];
[[NSNotificationCenter defaultCenter] postNotificationName:IAPHelperProductPurchasedNotification
object:productIdentifier
userInfo:nil];
}
- (void)restoreCompletedTransactions
{
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
{
NSMutableArray *purchasedItemsIDs = [[NSMutableArray alloc] init];
NSLog(@"Received restored transaction: %lu", (unsigned long)queue.transactions.count);
purchasedItemsIDs = [queue.transactions valueForKeyPath:@"payment.productIdentifier"];
NSLog(@"purchasedItemsIDs: %@", purchasedItemsIDs);
for (SKPaymentTransaction *transaction in queue.transactions) {
NSString *productID = transaction.payment.productIdentifier;
[purchasedItemsIDs addObject:productID];
NSLog(@"productID(transaction.payment.productIdentifier): %@", productID);
}
}
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error
{
NSLog(@"%s, %@", __PRETTY_FUNCTION__, error.localizedDescription);
}
答案 0 :(得分:1)
根据您收到的消息" 点击购买以续订或延长",您的产品似乎是非续订订阅。此产品类型无法通过StoreKit API恢复,您必须自己完成(最有可能通过检查您的服务器)。
来自In-App Purchase Programming Guide (Restoring Purchased Products)的引用:
如果您的应用使用非续订订阅,则您的应用负责恢复过程。
以下是应用程序内购买编程指南中订阅类型的比较:
+------------------------+----------------+----------------+---------------+
| Subscription type | Auto-renewable | Non-renewing | Free |
+------------------------+----------------+----------------+---------------+
| Users can buy | Multiple times | Multiple times | Once |
| Appears in the receipt | Always | Once | Always |
| Synced across devices | By the system | By your app | By the system |
| Restored | By the system | By your app | By the system |
+------------------------+----------------+----------------+---------------+
答案 1 :(得分:1)
在您链接的教程中,最后一节将向您展示如何实施恢复。
以下是该部分的代码:
- (void)restoreTapped:(id)sender {
[[RageIAPHelper sharedInstance] restoreCompletedTransactions];
//1
if ([PFUser currentUser].isAuthenticated) {
PFQuery *query = [PFQuery queryWithClassName:@"_User"];
[query getObjectInBackgroundWithId:[PFUser currentUser].objectId block:^(PFObject *object, NSError *error) {
//2
NSDate *serverDate = [[object objectForKey:kSubscriptionExpirationDateKey] lastObject];
[[NSUserDefaults standardUserDefaults] setObject:serverDate forKey:kSubscriptionExpirationDateKey];
[[NSUserDefaults standardUserDefaults] synchronize];
[self.tableView reloadData];
NSLog(@"Restore Complete!");
}];
}
答案 2 :(得分:0)
另一种方法是通过检查收据数据并相应地计算订阅来恢复购买。
如前所述,无法恢复交易,例如它们是消耗品,但是,您可以获取收据并对其进行解析以管理恢复/计算。