精简版
Apple 拒绝了我的应用程序,说我正在使用SKReceiptRefreshRequest
,但我在代码中找不到它(如下)。
长版
我目前正在尝试让我的应用在应用商店获得批准并遇到一些问题。目前的主要问题是他们说我错误地实施了收据验证,据我所知,根据他们的文件,我已经按照他们的规定完成了。然后,他们将我引导至开发人员技术支持代表。然后他继续进行淘汰过程——基于推测,这是由于无法看到我的代码并且没有与应用程序审查部门有任何联系。
首先,他提到SKReceiptRefreshRequest
了一张盛大的统一收据。我告诉他我没有使用SKReceiptRefreshRequest
;我搜索了整个项目。然后我们又进行了一些消除,最终我最终发送了一个 .IPA 以及配置文件供 DTS 代表亲自测试。他报告说他没有发现任何问题,并会联系应用审查部门以找出拒绝的原因。终于有了一些进展。
他回来说
"这里的问题是 App Review 拒绝您的应用程序使用 SKReceiptRefreshRequest。App Reviewer 正在使用一些步骤序列,导致应用程序进行此调用。应用程序只需要不重新请求此方法即可按顺序完成供审阅者访问应用内购买项目 - 购买按钮。 ”
...
所以我唯一的结论是我在我的代码中以某种方式使用了这个方法的委托。谁能告诉我我在哪里,或者可能在哪里,使用SKReceiptRefreshRequest
,因为我很困惑(下面的代码)。
更多信息
代码
(为安全起见,部分遮盖了)
#import "IAPHelper.h"
#import "SKProduct+LocalizedPrice.h"
#import ***
#import "NSString+NSStringAdditions.h"
#import ***
#import ***
#import ***
#import ***
@implementation IAPHelper
@synthesize productIdentifiers = _productIdentifiers;
@synthesize products = _products;
@synthesize purchasedProducts = _purchasedProducts;
@synthesize request = _request;
@synthesize localProducts = _localProducts;
@synthesize productsCategory = _productsCategory;
- (id)initWithProductIdentifiers:(NSSet *)productIdentifiers
{
if ((self = [super init]))
{
// Store product identifiers
_productIdentifiers = productIdentifiers;
// Check for previously purchased products
NSMutableSet * purchasedProducts = [NSMutableSet set];
_localProducts = [NSMutableArray new];
[_localProducts addObjectsFromArray:***];
[_localProducts addObjectsFromArray:***];
[_localProducts addObjectsFromArray:***];
_productsCategory = [NSMutableDictionary new];
[_productsCategory setObject:[self sortAndTrimProducts:***] forKey:***];
[_productsCategory setObject:[self sortAndTrimProducts:***] forKey:***];
[_productsCategory setObject:[self sortAndTrimProducts:***] forKey:***];
self.purchasedProducts = purchasedProducts;
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}
return self;
}
- (void)getProducts:(NSMutableArray*)array forId:(int)Id
{
for (NSString * productIdentifier in _productIdentifiers)
{
NSArray *stringByComponent = [productIdentifier componentsSeparatedByString:@"*"];
int identifier = [[stringByComponent objectAtIndex:*] integerValue];
BOOL productPurchased = [[NSUserDefaults standardUserDefaults] boolForKey:productIdentifier];
if (productPurchased)
{
[_purchasedProducts addObject:productIdentifier];
DDLogInfo(@"Previously purchased: %@", productIdentifier);
}
else
{
DDLogInfo(@"Not purchased: %@", productIdentifier);
}
if (identifier == Id)
{
[array addObject:productIdentifier];
}
}
}
-(NSMutableArray*)sortAndTrimProducts:(NSMutableArray*)array
{
NSArray *sortedProducts = [self sortProducts:array];
NSMutableArray *returnedArray = [NSMutableArray new];
for (NSString *string in sortedProducts)
{
NSArray *components = [string componentsSeparatedByString:@"*"];
int type = [[components objectAtIndex:*] integerValue];
switch (type)
{
case ***:
{
NSString *productName = [components objectAtIndex:*];
[returnedArray addObject:productName];
}
break;
case ***:
{
NSString *productName = [components objectAtIndex:*];
[returnedArray addObject:productName];
}
break;
case ***:
{
NSString *productName = [components objectAtIndex:*];
[returnedArray addObject:productName];
}
break;
default:
break;
}
}
return returnedArray;
}
- (NSArray*)sortProducts:(NSMutableArray*)array
{
NSArray *sortedArray;
sortedArray = [array sortedArrayUsingComparator:^NSComparisonResult(id a, id b)
{
***
}];
return sortedArray;
}
- (void)requestProducts
{
self.request = [[SKProductsRequest alloc] initWithProductIdentifiers:_productIdentifiers];
_request.delegate = self;
[_request start];
}
- (void)requestProductsWithCompletionHandler:(RequestProductsCompletionHandler)completionHandler
{
// 1
_completionHandler = [completionHandler copy];
// 2
_request = [[SKProductsRequest alloc] initWithProductIdentifiers:_productIdentifiers];
_request.delegate = self;
[_request start];
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
DDLogInfo(@"Received products results...%@",response.products);
self.products = response.products;
self.request = nil;
for( int i = 0; i < [_products count]; ++i )
{
if ( [_products objectAtIndex:i] )
{
DDLogInfo(@"Product title: %@" , [[_products objectAtIndex:i] localizedTitle]);
DDLogInfo(@"Product description: %@" , [[_products objectAtIndex:i] localizedDescription]);
DDLogInfo(@"Product price: %@" , [[_products objectAtIndex:i] localizedPrice]);
DDLogInfo(@"Product id: %@" , [[_products objectAtIndex:i] productIdentifier]);
}
for (NSString *invalidProductId in response.invalidProductIdentifiers)
{
DDLogError(@"Invalid product id: %@" , invalidProductId);
}
}
[[NSNotificationCenter defaultCenter] postNotificationName:kProductsLoadedNotification object:_products];
}
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error
{
DDLogError(@"Failed to load list of products. %@",error.localizedDescription);
_request = nil;
}
- (void)recordTransaction:(SKPaymentTransaction *)transaction
{
}
- (void)provideContent:(NSString *)productIdentifier
{
NSArray *formattedarrayFromString = [productIdentifier componentsSeparatedByString:@"*"];
NSString *type = [formattedarrayFromString objectAtIndex:*];
NSDictionary *dictionary = @{
@"data" : formattedarrayFromString,
@"ID" : productIdentifier,
};
if ( type.integerValue == 1 )
{
[[NSNotificationCenter defaultCenter] postNotificationName:*** object:nil userInfo:dictionary];
}
else
{
DDLogInfo(@"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
{
DDLogInfo(@"completeTransaction...");
[self validateReceiptForTransaction:transaction];
[self recordTransaction: transaction];
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
DDLogInfo(@"%@",response);
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
DDLogInfo(@"Succeeded! Received bytes of data"); // Deal with an error
}
- (void)restoreTransaction:(SKPaymentTransaction *)transaction
{
DDLogInfo(@"restoreTransaction...");
[self recordTransaction: transaction];
[self validateReceiptForTransaction:transaction];
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (void)failedTransaction:(SKPaymentTransaction *)transaction
{
if (transaction.error.code != SKErrorPaymentCancelled)
{
DDLogError(@"Transaction error: %@", transaction.error.localizedDescription);
}
[[NSNotificationCenter defaultCenter] postNotificationName:kProductPurchaseFailedNotification object:transaction];
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (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;
}
}
}
- (BOOL)productPurchased:(NSString *)productIdentifier
{
DDLogInfo(@"%@",productIdentifier);
DDLogInfo(@"%@",_purchasedProducts);
return [_purchasedProducts containsObject:productIdentifier];
}
- (void)buyProduct:(SKProduct *)product
{
DDLogInfo(@"Buying %@...", product.productIdentifier);
SKPayment * payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
- (void)restoreCompletedTransactions
{
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
+ (NSString*) encode:(const uint8_t*) input length:(NSInteger) length
{
***
}
- (void)validateReceiptForTransaction:(SKPaymentTransaction *)transaction
{
MyVerificationController * verifier = [MyVerificationController sharedInstance];
[verifier verifyPurchaseForProduction:transaction completionHandler:^(BOOL success, NSError *error)
{
if(success)
{
DDLogInfo(@"successfully verified receipt!");
[self provideContent:transaction.payment.productIdentifier];
}
else
{
if (error.code == 21007)
{
[verifier verifyPurchaseForSandbox:transaction completionHandler:^(BOOL success, NSError *error)
{
if(success)
{
DDLogInfo(@"successfully verified receipt!");
[self provideContent:transaction.payment.productIdentifier];
}
else
{
DDLogError(@"Failed to validate receipt.");
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
[[NSNotificationCenter defaultCenter] postNotificationName:*** object:nil userInfo:nil];
}
}];
}
else
{
DDLogError(@"Failed to validate receipt.");
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
[[NSNotificationCenter defaultCenter] postNotificationName:*** object:nil userInfo:nil];
}
}
}];
}
@end
我们只在苹果服务器上托管消耗品供他们购买,所有可能导致问题的非消耗品都由我们直接处理,并与他们在我们服务器上的帐户绑定。
我在上面的课程中使用的代表
<SKProductsRequestDelegate, SKPaymentTransactionObserver,NSURLConnectionDelegate>