23

アプリ内購入を試みた後、起動時にアプリがクラッシュするようになったと報告したユーザーが何人かいます。機能していないアプリを削除して再インストールするように依頼し、機能していないネットワーク通信を停止するために機内モードにするよう依頼しようとしました。

とにかくデバイスでエラーを再現できず、アプリ内購入はサンドボックス モードとプロダクション モードで問題なく実行されます。私の考えでは、どういうわけか彼らのトランザクションが起動時のクラッシュを引き起こしている nil productIdentifier を受け取ったのですが、アプリの起動時にどのトランザクション オブザーバー メソッドが呼び出され、問題を解決できるかわかりません。

トランザクションキューを「クリア」するか、起動時に製品識別子がゼロであることをテストして、これらのユーザーが少なくともアプリを再度実行できるようにする方法はありますか? 私は以下のコードを使用してアプリの購入を数百回行いましたが、これは最近発生し始めました. アプリの起動時に呼び出されるヘルパー メソッドはどれですか?

AppDelegate.m 内

[[SKPaymentQueue defaultQueue] addTransactionObserver:[MovieClockIAPHelper sharedHelper]];

アプリ ヘルパー コード内:

- (id)initWithProductIdentifiers:(NSSet *)productIdentifiers {
    if ((self = [super init])) {

        // Store product identifiers
        _productIdentifiers = [productIdentifiers retain];

        // Check for previously purchased products

        NSMutableSet * purchasedProducts = [NSMutableSet set];
        for (NSString * productIdentifier in _productIdentifiers) {

            BOOL productPurchased = [[NSUserDefaults standardUserDefaults] boolForKey:productIdentifier];

            if (productPurchased) {
                [purchasedProducts addObject:productIdentifier];
                NSLog(@"Previously purchased: %@", productIdentifier);
            }
            else{
            NSLog(@"Not purchased: %@", productIdentifier);
            }
        }
        self.purchasedProducts = purchasedProducts;

    }
    return self;
}

- (void)requestProducts {

    self.request = [[[SKProductsRequest alloc] initWithProductIdentifiers:_productIdentifiers] autorelease];
    _request.delegate = self;
    [_request start];

}

- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {

    NSLog(@"Received products results...");   
    self.products = response.products;
    self.request = nil;    

    [[NSNotificationCenter defaultCenter] postNotificationName:kProductsLoadedNotification object:_products];    
}


- (void)restoreCompletedTransactions {
    [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}

- (void)provideContent:(NSString *)productIdentifier {

    NSLog(@"Toggling flag for: %@", productIdentifier);
    [[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:productIdentifier];
    [[NSUserDefaults standardUserDefaults] synchronize];
    [_purchasedProducts addObject:productIdentifier];

    [[NSNotificationCenter defaultCenter] postNotificationName:kProductPurchasedNotification object:productIdentifier];

}

- (void)completeTransaction:(SKPaymentTransaction *)transaction {

    NSLog(@"completeTransaction...");

    [self recordTransaction: transaction];
    [self provideContent: transaction.payment.productIdentifier];
    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];

}

- (void)restoreTransaction:(SKPaymentTransaction *)transaction {

    NSLog(@"restoreTransaction...");


    [self recordTransaction: transaction];
    [self provideContent: transaction.originalTransaction.payment.productIdentifier];
    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];

}

- (void)failedTransaction:(SKPaymentTransaction *)transaction {

    if (transaction.error.code != SKErrorPaymentCancelled)
    {
        NSLog(@"Transaction error: %@", transaction.error.localizedDescription);
    }

    [[NSNotificationCenter defaultCenter] postNotificationName:kProductPurchaseFailedNotification object:transaction];

    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];

}

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
    NSLog(@"in the payment queue");

    for (SKPaymentTransaction *transaction in transactions)
    {

        switch (transaction.transactionState)
        {
            case SKPaymentTransactionStatePurchased:
                [self completeTransaction:transaction];
                break;
            case SKPaymentTransactionStateFailed:
                [self failedTransaction:transaction];
                break;
            case SKPaymentTransactionStateRestored:
                [self restoreTransaction:transaction];
            default:
                break;
        }
        }

}

-(void)buyProduct:(SKProduct *)product
{
    NSLog(@"In buyproduct Buying %@...", product.productIdentifier);

    SKPayment *payment = [SKPayment paymentWithProduct:product];

    [[SKPaymentQueue defaultQueue] addPayment:payment];
}

- (void)dealloc
{
    [_productIdentifiers release];
    _productIdentifiers = nil;
    [_products release];
    _products = nil;
    [_purchasedProducts release];
    _purchasedProducts = nil;
    [_request release];
    _request = nil;
    [super dealloc];
}

@end
4

5 に答える 5

8

トランザクションが SKPaymentTransactionStateRestored にあるときに、同様の問題に遭遇しました。私のテストでは、これは IOS 7.0.3 の問題である可能性があることが示されましたが、これを確認することはできませんでした。

StoreKit は、アプリケーションが完了する必要があるトランザクションの永続的なリストを保持します。お気づきのように、トランザクションは完了するまですべての起動時に報告されます。

実装した解決策は、エントリ ポイントから使用する前に製品識別子が nil かどうかを確認することです。

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions

商品識別子が nil のトランザクションを受け取った場合でも、 を正常に呼び出すことができましたfinishTransaction

これが役立つことを願っています。

于 2013-11-07T21:02:25.910 に答える
3

ショーンの答えを詳しく説明すると、- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactionsおそらく次のようなコードがあります。

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
    for (SKPaymentTransaction *transaction in transactions)
    {
        switch (transaction.transactionState)
        {
            case SKPaymentTransactionStatePurchased:

                                [self completeTransaction:transaction];

                                break;

                        case SKPaymentTransactionStateFailed:

                                [self failedTransaction:transaction];

                                break;

                        case SKPaymentTransactionStateRestored:

                                [self restoreTransaction:transaction];

                        default:

                                break;
        }     
    }
}

- (void) restoreTransaction: (SKPaymentTransaction *)transaction
{   
    /* Handle restore here */
    [[SKPaymentQueue defaultQueue] finishTransaction: transaction]; 
}

restoreTransaction:次のように、productIdentifier が nil かどうかを確認するチェックを追加することで、 nil の productIdentifier を処理できます。

- (void) restoreTransaction: (SKPaymentTransaction *)transaction
{   
    if (!transaction.originalTransaction.payment.productIdentifier) {
        NSLog(@"productIdentifier is nil; Apple bug?");
        [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
        return;
    }

    /* Handle restore here */
    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}

この手法により、アプリの問題が修正されました。アプリが起動し、「productIdentifier is nil; Apple bug?」がログに記録されました。そしてクラッシュしませんでした。その後、トランザクションを手動で復元したところ、Apple は有効なトランザクションを送信し、アプリは設計どおりに機能しました。

于 2013-11-17T05:14:47.870 に答える
3

マリンバが上記に投稿したようにこの問題を修正し、お客様に役立ちました。

case SKPaymentTransactionStateRestored:
{
  NSString *productID = transaction.originalTransaction.payment.productIdentifier;
  if (productID == nil) {
    productID = transaction.payment.productIdentifier;
  }
  [self handlePurchaseSuccessful:transaction.originalTransaction productIdentifier:productID];
}
于 2013-12-02T10:55:32.810 に答える
0

の状態のインスタンスでpaymentQueue:updatedTransactions:呼び出されていたのと同じ問題がありました。SKPaymentTransactionSKPaymentTransactionStateRestorednil productIdentifieroriginalTransaction

これは非常に奇妙に聞こえますが、7.0.3 SDK のバグである可能性があります。

まだ SDK 6 でコンパイルしていますか?

問題が修正されるかどうかを確認するためにアプリをストアに再送信しようとしていますが、そのようなトランザクションを終了するだけですが、アプリがクラッシュしないように、有効な productIdentifier を持つ他のトランザクションが処理されることを願っています。復元します。

于 2013-11-13T18:07:41.377 に答える