尝试所有这些步骤后,“应用内购买”仍然无法正常工作

Posted

技术标签:

【中文标题】尝试所有这些步骤后,“应用内购买”仍然无法正常工作【英文标题】:"In App Purchasing" still wont work after trying all these steps 【发布时间】:2013-08-15 19:31:36 【问题描述】:

我已经执行了这几个步骤,以便在我的应用程序中使用五个非消耗性应用程序购买,但是当我转到将 IAP 代码链接到的 TableViewController 时仍然没有显示任何内容...(我也想非常感谢我遵循的教程,raywenderlich 让我走到了这一步)

-创建了一个新的应用ID -已成功完成链接和下载证书的所有步骤 将我项目的 xcode 上的捆绑 ID 更改为我为 我在 ios 开发者门户上的应用 -链接所有设备并创建一个测试用户帐户 -使iTunes上的非消耗性IAP连接并使用IAP编码中的标识符 -从我的 iTunes 帐户中注销我的手机,以便它可以与测试帐户一起使用 -IAP 的编码看起来是正确的,导入了商店套件并在 .m 文件中实现了产品标识符 -等待 24 小时让新创建的 IAP 与 iTunes Connect 同步 -我没有上传二进制文件! 我打开了苹果主机,但没有上传任何东西 - 可以 这是问题所在? 我删除了手机上的应用程序并重新安装以进行测试,但仍然 什么都没有

当我成功运行构建时,我得到的只是一个页面,它看起来像是在加载一些东西,但没有一个空白页面,我可以下拉刷新,但仍然没有我创建的 IAP。

关于我还能做什么或添加什么的任何建议,如果您需要我上传所有我能做的文件的所有代码...

ViewController.h 文件中的 IAP 编码

#import "Accounts/Accounts.h"
#import <Foundation/Foundation.h>
#import <StoreKit/StoreKit.h>

@interface ViewController19 : UITableViewController

@end

ViewController.m 文件中的 IAP 编码

#import "DetailViewController.h"
#import "SecretsIAPHelper.h"
#import <StoreKit/StoreKit.h>

@interface ViewController19 () 
    NSArray *_products;
    NSNumberFormatter * _priceFormatter;

@end

@implementation ViewController19

- (void)viewDidLoad

    [super viewDidLoad];

    self.title = @"XXXXXXXXXXXXX";

    self.refreshControl = [[UIRefreshControl alloc] init];
    [self.refreshControl addTarget:self action:@selector(reload) forControlEvents:UIControlEventValueChanged];
    [self reload];
    [self.refreshControl beginRefreshing];

    _priceFormatter = [[NSNumberFormatter alloc] init];
    [_priceFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
    [_priceFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];

    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Restore" style:UIBarButtonItemStyleBordered target:self action:@selector(restoreTapped:)];



- (void)restoreTapped:(id)sender 
    [[SecretsIAPHelper sharedInstance] restoreCompletedTransactions];


- (void)viewWillAppear:(BOOL)animated 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(productPurchased:) name:IAPHelperProductPurchasedNotification object:nil];


- (void)viewWillDisappear:(BOOL)animated 
    [[NSNotificationCenter defaultCenter] removeObserver:self];


- (void)productPurchased:(NSNotification *)notification 

    NSString * productIdentifier = notification.object;
    [_products enumerateObjectsUsingBlock:^(SKProduct * product, NSUInteger idx, BOOL *stop) 
        if ([product.productIdentifier isEqualToString:productIdentifier]) 
            [self.tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:idx inSection:0]] withRowAnimation:UITableViewRowAnimationFade];
            *stop = YES;
        
    ];



- (void)reload 
    _products = nil;
    [self.tableView reloadData];
    [[SecretsIAPHelper sharedInstance] requestProductsWithCompletionHandler:^(BOOL success, NSArray *products) 
        if (success) 
            _products = products;
            [self.tableView reloadData];
        
        [self.refreshControl endRefreshing];
    ];


#pragma mark - Table View

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

    return 1;


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

    return _products.count;


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];

    SKProduct * product = (SKProduct *) _products[indexPath.row];
    cell.textLabel.text = product.localizedTitle;
    [_priceFormatter setLocale:product.priceLocale];
    cell.detailTextLabel.text = [_priceFormatter stringFromNumber:product.price];

    if ([[SecretsIAPHelper sharedInstance] productPurchased:product.productIdentifier]) 
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
        cell.accessoryView = nil;
     else 
        UIButton *buyButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
        buyButton.frame = CGRectMake(0, 0, 72, 37);
        [buyButton setTitle:@"Buy" forState:UIControlStateNormal];
        buyButton.tag = indexPath.row;
        [buyButton addTarget:self action:@selector(buyButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
        cell.accessoryType = UITableViewCellAccessoryNone;
        cell.accessoryView = buyButton;
    

    return cell;


- (void)buyButtonTapped:(id)sender 

    UIButton *buyButton = (UIButton *)sender;
    SKProduct *product = _products[buyButton.tag];

    NSLog(@"Buying %@...", product.productIdentifier);
    [[SecretsIAPHelper sharedInstance] buyProduct:product];



@end

在 IAPHelper.h 文件中为 IAP 编码

#import <Foundation/Foundation.h>
#import <StoreKit/StoreKit.h>

UIKIT_EXTERN NSString *const IAPHelperProductPurchasedNotification;

typedef void (^RequestProductsCompletionHandler)(BOOL success, NSArray * products);

@interface IAPHelper : NSObject

- (id)initWithProductIdentifiers:(NSSet *)productIdentifiers;
- (void)requestProductsWithCompletionHandler:(RequestProductsCompletionHandler)completionHandler;
- (void)buyProduct:(SKProduct *)product;
- (BOOL)productPurchased:(NSString *)productIdentifier;
- (void)restoreCompletedTransactions;


@end

在 IAPHelper.m 文件中为 IAP 编码

#import "IAPHelper.h"
#import <StoreKit/StoreKit.h>

NSString *const IAPHelperProductPurchasedNotification = @"IAPHelperProductPurchasedNotification";

// 2
@interface IAPHelper () <SKProductsRequestDelegate, SKPaymentTransactionObserver>
@end

// 3
@implementation IAPHelper 
    SKProductsRequest * _productsRequest;
    RequestProductsCompletionHandler _completionHandler;

    NSSet * _productIdentifiers;
    NSMutableSet * _purchasedProductIdentifiers;


- (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 


    // 1
    _completionHandler = [completionHandler copy];

    // 2
    _productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:_productIdentifiers];
    _productsRequest.delegate = self;
    [_productsRequest start];



- (BOOL)productPurchased:(NSString *)productIdentifier 
    return [_purchasedProductIdentifiers containsObject:productIdentifier];


- (void)buyProduct:(SKProduct *)product 

    NSLog(@"Buying %@...", product.productIdentifier);

    SKPayment * payment = [SKPayment paymentWithProduct:product];
    [[SKPaymentQueue defaultQueue] addPayment:payment];



#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

    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)completeTransaction:(SKPaymentTransaction *)transaction 
    NSLog(@"completeTransaction...");

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


- (void)restoreTransaction:(SKPaymentTransaction *)transaction 
    NSLog(@"restoreTransaction...");

    [self provideContentForProductIdentifier:transaction.originalTransaction.payment.productIdentifier];
    [[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 

    [_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];

@end

SecretsIAPHelper.h 文件中的 IAP 编码

#import "IAPHelper.h"

@interface SecretsIAPHelper : IAPHelper

+ (SecretsIAPHelper *)sharedInstance;

@end

SecretsIAPHelper.m 文件中的 IAP 编码

#import "SecretsIAPHelper.h"

@implementation SecretsIAPHelper

+ (SecretsIAPHelper *)sharedInstance 
    static dispatch_once_t once;
    static SecretsIAPHelper * sharedInstance;
    dispatch_once(&once, ^
        NSSet * productIdentifiers = [NSSet setWithObjects:
                                      @"com.designsbydeondrae.XXXXXXX.remove_ads",
                                      @"com.designsbydeondrae.XXXXXXX.FoundationSkills",
                                      @"com.designsbydeondrae.XXXXXXX.IntermediateSkills",
                                      @"com.designsbydeondrae.XXXXXXX.AllSkills",
                                      @"com.designsbydeondrae.XXXXXXX.AdvancedSkills",
                                      nil];
        sharedInstance = [[self alloc] initWithProductIdentifiers:productIdentifiers];
    );
    return sharedInstance;



@end

DetailViewController.h 文件中的 IAP 编码

#import <UIKit/UIKit.h>

@interface DetailViewController : UIViewController

@property (strong, nonatomic) id detailItem;

@property (weak, nonatomic) IBOutlet UILabel *detailDescriptionLabel;

@end

DetailViewController.h 文件中的 IAP 编码

#import "DetailViewController.h"

@interface DetailViewController ()
- (void)configureView;
@end

@implementation DetailViewController

#pragma mark - Managing the detail item

- (void)setDetailItem:(id)newDetailItem

    if (_detailItem != newDetailItem) 
        _detailItem = newDetailItem;

        // Update the view.
        [self configureView];
    


- (void)configureView

    // Update the user interface for the detail item.

    if (self.detailItem) 
        self.detailDescriptionLabel.text = [self.detailItem description];
    


- (void)viewDidLoad

    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    [self configureView];


- (void)didReceiveMemoryWarning

    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.


@end

总结一下……

使用 StoreKit 访问应用内购买 API 并检索 运行构建时什么都不显示的列表 为我的应用指定产品标识符 显示产品,这似乎是一个问题,虽然它确实 显示一个屏幕,就像它即将加载一些东西然后什么都没有 页面上拉刷新

【问题讨论】:

您通过调试了解到发生了什么?您的表格视图没有加载任何内容 - 好吧,这是为什么呢?它等待加载什么响应,得到什么响应? 删除并重新安装应用程序,您可能仍然拥有旧捆绑包。 requestProductsWithCompletionHandler:^(BOOL success, NSArray *products) 返回一个空数组吗? 同名 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath]; @"Cell" 这绝对是荒谬的代码量,期望其他人阅读和调试。请将其减少到证明问题所需的最低限度。 【参考方案1】:

对于以后发现此帖子的任何人来说,在请求 IAP 时没有显示任何项目的原因是由于未为托管内容上传内容。

【讨论】:

以上是关于尝试所有这些步骤后,“应用内购买”仍然无法正常工作的主要内容,如果未能解决你的问题,请参考以下文章

使用 Flutters 应用内购买插件检查订阅有效性?

应用内购买后无法在设备上运行应用程序

应用内购买产品请求不起作用

应用内购买被“无法连接到商店”阻止

即使 Flutter/bin 在我的 $PATH 中,Flutter 命令仍然无法正常工作

Google Play 应用内购买问题 - 错误您已经拥有此项目