冲突恢复交易

时间:2014-06-05 11:13:53

标签: ios objective-c in-app-purchase

我正在尝试实现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);
}

3 个答案:

答案 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)

另一种方法是通过检查收据数据并相应地计算订阅来恢复购买。

如前所述,无法恢复交易,例如它们是消耗品,但是,您可以获取收据并对其进行解析以管理恢复/计算。