如何在iOS应用程序中添加应用内购买?

Joj*_*dmo 253 cocoa-touch objective-c in-app-purchase ios swift

如何在iOS应用中添加应用内购买?什么是详细信息,是否有示例代码?

对于如何向iOS应用添加应用内购买,这意味着各种各样

Joj*_*dmo 548

获得一个应用程序内购买的最佳方式工作My Apps(和Features在Xcode 5+)是做到以下几点:

  1. 转到appstoreconnect.apple.com并登录
  2. 单击,In-App Purchases然后单击要添加购买的应用程序
  3. 单击+标题,然后non-consumable在左侧选择
  4. 单击consumable中间的图标
  5. 在本教程中,我们将添加应用内购买以移除广告,因此请选择tld.websitename.appname.referencename.如果您要向用户发送物理项目,或者给他们一些他们可以购买多次的东西,您可以选择com.jojodmo.blix.removeads.
  6. 对于参考名称,放置你想要的任何东西(但要确保你知道它是什么)
  7. 对于产品ID put,cleared for sale这将是最好的,所以例如,你可以使用view pricing matrix
  8. 选择add language然后选择价格等级为1(99¢).第2层为1.99美元,第3层为2.99美元.如果您点击hosting content with Apple我推荐您使用第1层,则可以使用完整列表,因为这通常是删除广告所需的最多人.
  9. 单击蓝色screenshot for review按钮,然后输入信息.这将全部显示给客户,所以不要放任何你不希望他们看到的东西
  10. 对于App Store Connect选择
  11. 您可以将审查注意到空白FOR NOW.
  12. 跳过linked frameworks and libraries 现在,我们跳过的所有内容都会回来.
  13. 点击"保存"

您的产品ID可能需要几个小时才能注册StoreKit.framework,请耐心等待.

现在您已经在App Store Connect上设置了应用程序内购买信息,进入Xcode项目,然后转到应用程序管理器(方法和头文件所在位置的蓝色页面图标)点击你的应用程序在目标下(应该是第一个),然后转到一般.在底部,你应该可以看到.h单击小加号,并添加框架File.如果你不这样做,应用程序内购买将工作!

如果您使用Objective-C作为应用程序的语言,则可以跳过这五个步骤.否则,如果您使用的是Swift,请执行以下操作:

  1. 创建一个新New的打算(头)文件File...> .h> .h(Command ⌘+ N).该文件将Bridge.h在本教程的其余部分中称为"您的文件"

  2. 出现提示时,单击" 创建桥接标题".这将是我们的桥接头文件.如果你提示,请转到步骤3.如果您提示,请跳过步骤3,直接转到步骤4.

  3. 创建在主项目文件夹中Targets命名的另一个文件Build Settings,然后转到应用程序管理器(蓝色页面图标),然后在该Bridge.h部分中选择您的应用程序,然后单击#import "MyObjectiveCHeaderFile.h".找到说Swift Compiler - Code Generation的选项,然后将Objective-C Bridging Header选项设置为MyObjectiveCHeaderFile

  4. 在桥接头文件中,添加行#import "InAppPurchase.h",其中.m是您在第一步中创建的头文件的名称.因此,例如,如果您将头文件命名为InAppPurchase.h,则应将该行添加File到桥头文件中.

  5. 创建一个新的Objective-C的方法(New通过转到)文件File...> .m> .h(Command ⌘+ N).将其命名为与在步骤1中创建的头文件相同.例如,如果在步骤1 InAppPurchase.h中调用该文件,则将调用此新文件InAppPurchase.m.该文件将StoreKit在本教程的其余部分中称为"您的文件".

现在我们将进入实际编码.将以下代码添加到您的.m文件中:

BOOL areAdsRemoved;

- (IBAction)restore;
- (IBAction)tapsRemoveAds;
Run Code Online (Sandbox Code Playgroud)

接下来,您需要将SKProductsRequestDelegate框架导入到您的SKPaymentTransactionObserver文件中,以及在声明后添加@interface和:.mviewDidLoad

#import <StoreKit/StoreKit.h>

//put the name of your view controller in place of MyViewController
@interface MyViewController() <SKProductsRequestDelegate, SKPaymentTransactionObserver>

@end

@implementation MyViewController //the name of your view controller (same as above)
  //the code below will be added here
@end
Run Code Online (Sandbox Code Playgroud)

现在将以下内容添加到您的.xib文件中,这部分变得复杂,所以我建议您阅读代码中的注释:

//If you have more than one in-app purchase, you can define both of
//of them here. So, for example, you could define both kRemoveAdsProductIdentifier
//and kBuyCurrencyProductIdentifier with their respective product ids
//
//for this example, we will only use one product

#define kRemoveAdsProductIdentifier @"put your product id (the one that we just made in App Store Connect) in here"

- (IBAction)tapsRemoveAds{
    NSLog(@"User requests to remove ads");

    if([SKPaymentQueue canMakePayments]){
        NSLog(@"User can make payments");

        //If you have more than one in-app purchase, and would like
        //to have the user purchase a different product, simply define 
        //another function and replace kRemoveAdsProductIdentifier with 
        //the identifier for the other product

        SKProductsRequest *productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:kRemoveAdsProductIdentifier]];
        productsRequest.delegate = self;
        [productsRequest start];

    }
    else{
        NSLog(@"User cannot make payments due to parental controls");
        //this is called the user cannot make payments, most likely due to parental controls
    }
}

- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
    SKProduct *validProduct = nil;
    int count = [response.products count];
    if(count > 0){
        validProduct = [response.products objectAtIndex:0];
        NSLog(@"Products Available!");
        [self purchase:validProduct];
    }
    else if(!validProduct){
        NSLog(@"No products available");
        //this is called if your product id is not valid, this shouldn't be called unless that happens.
    }
}

- (void)purchase:(SKProduct *)product{
    SKPayment *payment = [SKPayment paymentWithProduct:product];

    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
    [[SKPaymentQueue defaultQueue] addPayment:payment];
}

- (IBAction) restore{
    //this is called when the user restores purchases, you should hook this up to a button
    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
    [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}

- (void) paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
{
    NSLog(@"received restored transactions: %i", queue.transactions.count);
    for(SKPaymentTransaction *transaction in queue.transactions){
        if(transaction.transactionState == SKPaymentTransactionStateRestored){
            //called when the user successfully restores a purchase
            NSLog(@"Transaction state -> Restored");

            //if you have more than one in-app purchase product,
            //you restore the correct product for the identifier.
            //For example, you could use
            //if(productID == kRemoveAdsProductIdentifier)
            //to get the product identifier for the
            //restored purchases, you can use
            //
            //NSString *productID = transaction.payment.productIdentifier;
            [self doRemoveAds];
            [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
            break;
        }
    }   
}

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{
    for(SKPaymentTransaction *transaction in transactions){
        //if you have multiple in app purchases in your app,
        //you can get the product identifier of this transaction
        //by using transaction.payment.productIdentifier
        //
        //then, check the identifier against the product IDs
        //that you have defined to check which product the user
        //just purchased            

        switch(transaction.transactionState){
            case SKPaymentTransactionStatePurchasing: NSLog(@"Transaction state -> Purchasing");
                //called when the user is in the process of purchasing, do not add any of your own code here.
                break;
            case SKPaymentTransactionStatePurchased:
            //this is called when the user has successfully purchased the package (Cha-Ching!)
                [self doRemoveAds]; //you can add your code for what you want to happen when the user buys the purchase here, for this tutorial we use removing ads
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                NSLog(@"Transaction state -> Purchased");
                break;
            case SKPaymentTransactionStateRestored:
                NSLog(@"Transaction state -> Restored");
                //add the same code as you did from SKPaymentTransactionStatePurchased here
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                break;
            case SKPaymentTransactionStateFailed:
                //called when the transaction does not finish
                if(transaction.error.code == SKErrorPaymentCancelled){
                    NSLog(@"Transaction state -> Cancelled");
                    //the user cancelled the payment ;(
                }
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                break;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,您希望为用户完成事务时添加代码,在本教程中,我们使用删除添加,您必须为横幅视图加载时发生的情况添加自己的代码.

- (void)doRemoveAds{
    ADBannerView *banner;
    [banner setAlpha:0];
    areAdsRemoved = YES;
    removeAdsButton.hidden = YES;
    removeAdsButton.enabled = NO;
    [[NSUserDefaults standardUserDefaults] setBool:areAdsRemoved forKey:@"areAdsRemoved"];
    //use NSUserDefaults so that you can load whether or not they bought it
    //it would be better to use KeyChain access, or something more secure
    //to store the user data, because NSUserDefaults can be changed.
    //You're average downloader won't be able to change it very easily, but
    //it's still best to use something more secure than NSUserDefaults.
    //For the purpose of this tutorial, though, we're going to use NSUserDefaults
    [[NSUserDefaults standardUserDefaults] synchronize];
}
Run Code Online (Sandbox Code Playgroud)

如果您的应用程序中没有广告,则可以使用您想要的任何其他内容.例如,我们可以将背景颜色设为蓝色.要做到这一点,我们想要使用:

- (void)doRemoveAds{
    [self.view setBackgroundColor:[UIColor blueColor]];
    areAdsRemoved = YES
    //set the bool for whether or not they purchased it to YES, you could use your own boolean here, but you would have to declare it in your .h file

    [[NSUserDefaults standardUserDefaults] setBool:areAdsRemoved forKey:@"areAdsRemoved"];
    //use NSUserDefaults so that you can load wether or not they bought it
    [[NSUserDefaults standardUserDefaults] synchronize];
}
Run Code Online (Sandbox Code Playgroud)

现在,在您的storyboard方法的某个地方,您将要添加以下代码:

areAdsRemoved = [[NSUserDefaults standardUserDefaults] boolForKey:@"areAdsRemoved"];
[[NSUserDefaults standardUserDefaults] synchronize];
//this will load wether or not they bought the in-app purchase

if(areAdsRemoved){
    [self.view setBackgroundColor:[UIColor blueColor]];
    //if they did buy it, set the background to blue, if your using the code above to set the background to blue, if your removing ads, your going to have to make your own code here
}
Run Code Online (Sandbox Code Playgroud)

现在,您已经添加了所有的代码,去到你tapsRemoveAdsIBAction文件,并添加两个按钮,一个说购买,另一个说恢复.连接restore IBAction到您刚刚制作的购买按钮,然后连接restore Users and Access到恢复按钮.该Sandbox Testers操作将检查用户之前是否购买了应用内购买,并且如果他们还没有购买,则免费为他们提供应用内购买.

接下来,进入App Store Connect,单击+然后单击Testers标题,然后单击Date of Birth左侧的符号App Store Territory.您可以随意添加名字和姓氏,电子邮件不必是真实的 - 您只需要能够记住它.输入密码(您必须记住)并填写其余信息.我建议您制作screenshot for review一个使用户年满18岁的日期.My Apps HAS是在正确的国家.接下来,注销现有的iTunes帐户(您可以在本教程之后重新登录).

现在,你的iOS设备上运行应用程序,如果你试图在模拟器上运行它,这次收购将永远错误,你你的iOS设备上运行.应用程序运行后,点按"购买"按钮.当系统提示您登录iTunes帐户时,请以我们刚创建的测试用户身份登录.接下来,当它要求您确认购买99美分或任何您设置价格等级时,请选择屏幕快照,这将是您the app you have the In-app purchase on在App Store Connect上使用的内容.现在取消付款.

现在,进入App Store的连接,然后去In-App Purchases> doRemoveAds> No Products Available.然后点击应用内购买,点击应用内购买详情下的修改.完成后,将刚刚拍摄的iPhone照片导入计算机,然后将其上传为截图供审阅,然后在评论说明中输入您的TEST USER电子邮件和密码.这将有助于苹果在审核过程中.

完成此操作后,返回iOS设备上的应用程序,仍以测试用户帐户登录,然后单击购买按钮.这一次,确认付款不要担心,这不会向您的帐户收取任何费用,测试用户帐户免费获得所有应用程序内购买确认付款后,请确保用户实际购买产品时会发生什么发生.如果没有,那么那将是你的kRemoveAdsProductIdentifier方法的错误.同样,我建议使用将背景更改为蓝色以测试应用内购买,但这不应该是您实际的应用内购买.如果一切正常,你就会好起来!当您将其上传到App Store Connect时,请确保在新二进制文件中包含应用内购买!


以下是一些常见错误:

记录: My Apps

这可能意味着三件事:

  • 您没有在代码中添加正确的应用内购买ID(对于Features上面代码中的标识符)
  • 您没有在App Store Connect上清除您的应用内购买
  • 您没有等待应用内购买ID在App Store Connect中注册.等待几个小时创建ID,您的问题应该得到解决.
  • 您未完成填写协议,税务和银行信息.

如果它第一次不起作用,请不要感到沮丧!不要放弃!我需要大约5个小时才能完成这项工作,大约需要10个小时才能找到合适的代码!如果你完全使用上面的代码,它应该工作正常.如果您有任何问题,请随时发表评论.

我希望这有助于所有希望在他们的iOS应用程序中添加应用程序内购买的人.干杯!

  • Apple的最佳实践建议您将事务观察器添加到AppDelegate,而不是视图控制器操作.https://developer.apple.com/library/ios/technotes/tn2387/_index.html (13认同)
  • 您应该解释,在步骤9中,显示名称是呈现给用户的内容.它以这种方式呈现:"你想以0.99美元购买一个显示名称吗?".这很重要,因为我的显示名称为"删除广告",然后我的应用被拒绝,因为我在弹出窗口中使用了不正确的语法!我不得不将我的显示名称更改为"广告删除包". (4认同)
  • 我得到了0个产品,但我已经检查了你列出的3个可能的原因.如果我没有在itunes connect中的"ios付费应用合同"上设置联系信息,银行信息和税务信息,那么唯一可以想到的是,这可能是什么原因? (3认同)

Yed*_*iss 12

只需将Jojodmo代码翻译为Swift:

class InAppPurchaseManager: NSObject , SKProductsRequestDelegate, SKPaymentTransactionObserver{





//If you have more than one in-app purchase, you can define both of
//of them here. So, for example, you could define both kRemoveAdsProductIdentifier
//and kBuyCurrencyProductIdentifier with their respective product ids
//
//for this example, we will only use one product

let kRemoveAdsProductIdentifier = "put your product id (the one that we just made in iTunesConnect) in here"

@IBAction func tapsRemoveAds() {

    NSLog("User requests to remove ads")

    if SKPaymentQueue.canMakePayments() {
        NSLog("User can make payments")

        //If you have more than one in-app purchase, and would like
        //to have the user purchase a different product, simply define
        //another function and replace kRemoveAdsProductIdentifier with
        //the identifier for the other product
        let set : Set<String> = [kRemoveAdsProductIdentifier]
        let productsRequest = SKProductsRequest(productIdentifiers: set)
        productsRequest.delegate = self
        productsRequest.start()

    }
    else {
        NSLog("User cannot make payments due to parental controls")
        //this is called the user cannot make payments, most likely due to parental controls
    }
}


func purchase(product : SKProduct) {

    let payment = SKPayment(product: product)
    SKPaymentQueue.defaultQueue().addTransactionObserver(self)
    SKPaymentQueue.defaultQueue().addPayment(payment)
}

func restore() {
    //this is called when the user restores purchases, you should hook this up to a button
    SKPaymentQueue.defaultQueue().addTransactionObserver(self)
    SKPaymentQueue.defaultQueue().restoreCompletedTransactions()
}


func doRemoveAds() {
    //TODO: implement
}

/////////////////////////////////////////////////
//////////////// store delegate /////////////////
/////////////////////////////////////////////////
// MARK: - store delegate -


func productsRequest(request: SKProductsRequest, didReceiveResponse response: SKProductsResponse) {

    if let validProduct = response.products.first {
        NSLog("Products Available!")
        self.purchase(validProduct)
    }
    else {
        NSLog("No products available")
        //this is called if your product id is not valid, this shouldn't be called unless that happens.
    }
}

func paymentQueueRestoreCompletedTransactionsFinished(queue: SKPaymentQueue) {


    NSLog("received restored transactions: \(queue.transactions.count)")
    for transaction in queue.transactions {
        if transaction.transactionState == .Restored {
            //called when the user successfully restores a purchase
            NSLog("Transaction state -> Restored")

            //if you have more than one in-app purchase product,
            //you restore the correct product for the identifier.
            //For example, you could use
            //if(productID == kRemoveAdsProductIdentifier)
            //to get the product identifier for the
            //restored purchases, you can use
            //
            //NSString *productID = transaction.payment.productIdentifier;
            self.doRemoveAds()
            SKPaymentQueue.defaultQueue().finishTransaction(transaction)
            break;
        }
    }
}


func paymentQueue(queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {

    for transaction in transactions {
        switch transaction.transactionState {
        case .Purchasing: NSLog("Transaction state -> Purchasing")
            //called when the user is in the process of purchasing, do not add any of your own code here.
        case .Purchased:
            //this is called when the user has successfully purchased the package (Cha-Ching!)
            self.doRemoveAds() //you can add your code for what you want to happen when the user buys the purchase here, for this tutorial we use removing ads
            SKPaymentQueue.defaultQueue().finishTransaction(transaction)
            NSLog("Transaction state -> Purchased")
        case .Restored:
            NSLog("Transaction state -> Restored")
            //add the same code as you did from SKPaymentTransactionStatePurchased here
            SKPaymentQueue.defaultQueue().finishTransaction(transaction)
        case .Failed:
            //called when the transaction does not finish
            if transaction.error?.code == SKErrorPaymentCancelled {
                NSLog("Transaction state -> Cancelled")
                //the user cancelled the payment ;(
            }
            SKPaymentQueue.defaultQueue().finishTransaction(transaction)
        case .Deferred:
            // The transaction is in the queue, but its final status is pending external action.
            NSLog("Transaction state -> Deferred")

        }


    }
}
} 
Run Code Online (Sandbox Code Playgroud)


Joj*_*dmo 9

快速回答

这是为了补充为 Swift 用户提供的 Objective-C 答案,以防止 Objective-C 答案变得太大。

设置

首先,在appstoreconnect.apple.com上设置应用内购买。按照我的 Objective-C 答案的开头部分(步骤 1-13,在App Store Connect标题下)获取有关执行此操作的说明。

您的产品 ID 可能需要几个小时才能在 App Store Connect 中注册,因此请耐心等待。

现在您已经在 App Store Connect 上设置了应用内购买信息,我们需要将 Apple 的应用内购买框架添加StoreKit到应用中。

进入您的 Xcode 项目,然后转到应用程序管理器(应用程序文件所在的左侧栏顶部的蓝色页面状图标)。单击左侧目标下的应用程序(它应该是第一个选项),然后转到顶部的“功能”。在列表中,您应该会看到“应用内购买”选项。打开此功能,Xcode 将添加StoreKit到您的项目中。

编码

现在,我们要开始编码了!

首先,创建一个新的 swift 文件来管理您所有的应用内购买。我要打电话给它IAPManager.swift

在这个文件中,我们将创建一个新类,称为IAPManagera SKProductsRequestDelegateand SKPaymentTransactionObserver。在顶部,确保您导入FoundationStoreKit

import Foundation
import StoreKit

public class IAPManager: NSObject, SKProductsRequestDelegate,
                         SKPaymentTransactionObserver {
}
Run Code Online (Sandbox Code Playgroud)

接下来,我们将添加一个变量来定义我们的应用内购买的标识符(您也可以使用enum,如果您有多个 IAP,这将更容易维护)。

// This should the ID of the in-app-purchase you made on AppStore Connect.
// if you have multiple IAPs, you'll need to store their identifiers in
// other variables, too (or, preferably in an enum).
let removeAdsID = "com.skiplit.removeAds"
Run Code Online (Sandbox Code Playgroud)

接下来让我们为我们的类添加一个初始化程序:

// This is the initializer for your IAPManager class
//
// A better, and more scaleable way of doing this
// is to also accept a callback in the initializer, and call
// that callback in places like the paymentQueue function, and
// in all functions in this class, in place of calls to functions
// in RemoveAdsManager (you'll see those calls in the code below).

let productID: String
init(productID: String){
    self.productID = productID
}
Run Code Online (Sandbox Code Playgroud)

现在,我们将添加所需的功能SKProductsRequestDelegateSKPaymentTransactionObserver使其工作:

我们稍后会添加RemoveAdsManager

// This is called when a SKProductsRequest receives a response
public func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse){
    // Let's try to get the first product from the response
    // to the request
    if let product = response.products.first{
        // We were able to get the product! Make a new payment
        // using this product
        let payment = SKPayment(product: product)

        // add the new payment to the queue
        SKPaymentQueue.default().add(self)
        SKPaymentQueue.default().add(payment)
    }
    else{
        // Something went wrong! It is likely that either
        // the user doesn't have internet connection, or
        // your product ID is wrong!
        //
        // Tell the user in requestFailed() by sending an alert,
        // or something of the sort

        RemoveAdsManager.removeAdsFailure()
    }
}

// This is called when the user restores their IAP sucessfully
private func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue){
    // For every transaction in the transaction queue...
    for transaction in queue.transactions{
        // If that transaction was restored
        if transaction.transactionState == .restored{
            // get the producted ID from the transaction
            let productID = transaction.payment.productIdentifier

            // In this case, we have only one IAP, so we don't need to check
            // what IAP it is. However, this is useful if you have multiple IAPs!
            // You'll need to figure out which one was restored
            if(productID.lowercased() == IAPManager.removeAdsID.lowercased()){
                // Restore the user's purchases
                RemoveAdsManager.restoreRemoveAdsSuccess()
            }

            // finish the payment
            SKPaymentQueue.default().finishTransaction(transaction)
        }
    }
}

// This is called when the state of the IAP changes -- from purchasing to purchased, for example.
// This is where the magic happens :)
public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]){
    for transaction in transactions{
        // get the producted ID from the transaction
        let productID = transaction.payment.productIdentifier

        // In this case, we have only one IAP, so we don't need to check
        // what IAP it is.
        // However, if you have multiple IAPs, you'll need to use productID
        // to check what functions you should run here!

        switch transaction.transactionState{
        case .purchasing:
            // if the user is currently purchasing the IAP,
            // we don't need to do anything.
            //
            // You could use this to show the user
            // an activity indicator, or something like that
            break
        case .purchased:
            // the user successfully purchased the IAP!
            RemoveAdsManager.removeAdsSuccess()
            SKPaymentQueue.default().finishTransaction(transaction)
        case .restored:
                // the user restored their IAP!
                IAPTestingHandler.restoreRemoveAdsSuccess()
                SKPaymentQueue.default().finishTransaction(transaction)
        case .failed:
                // The transaction failed!
                RemoveAdsManager.removeAdsFailure()
                // finish the transaction
                SKPaymentQueue.default().finishTransaction(transaction)
        case .deferred:
                // This happens when the IAP needs an external action
                // in order to proceeded, like Ask to Buy
                RemoveAdsManager.removeAdsDeferred()
                break
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在让我们添加一些可用于开始购买或恢复购买的功能:

// Call this when you want to begin a purchase
// for the productID you gave to the initializer
public func beginPurchase(){
    // If the user can make payments
    if SKPaymentQueue.canMakePayments(){
        // Create a new request
        let request = SKProductsRequest(productIdentifiers: [productID])
        // Set the request delegate to self, so we receive a response
        request.delegate = self
        // start the request
        request.start()
    }
    else{
        // Otherwise, tell the user that
        // they are not authorized to make payments,
        // due to parental controls, etc
    }
}

// Call this when you want to restore all purchases
// regardless of the productID you gave to the initializer
public func beginRestorePurchases(){
    // restore purchases, and give responses to self
    SKPaymentQueue.default().add(self)
    SKPaymentQueue.default().restoreCompletedTransactions()
}
Run Code Online (Sandbox Code Playgroud)

接下来,让我们添加一个新的实用程序类来管理我们的 IAP。所有这些代码都可以在一个类中,但是如果有多个代码,它会更简洁一些。我将创建一个名为 的新类RemoveAdsManager,并在其中放入一些函数

public class RemoveAdsManager{

    class func removeAds()
    class func restoreRemoveAds()

    class func areAdsRemoved() -> Bool

    class func removeAdsSuccess()
    class func restoreRemoveAdsSuccess()
    class func removeAdsDeferred()
    class func removeAdsFailure()
}
Run Code Online (Sandbox Code Playgroud)

前三项功能,removeAdsrestoreRemoveAds,和areAdsRemoved,是函数,你会打电话做某些动作。最后四个是将被调用的IAPManager

让我们为前两个函数添加一些代码,removeAds然后restoreRemoveAds

// Call this when the user wants
// to remove ads, like when they
// press a "remove ads" button
class func removeAds(){
    // Before starting the purchase, you could tell the
    // user that their purchase is happening, maybe with
    // an activity indicator

    let iap = IAPManager(productID: IAPManager.removeAdsID)
    iap.beginPurchase()
}

// Call this when the user wants
// to restore their IAP purchases,
// like when they press a "restore
// purchases" button.
class func restoreRemoveAds(){
    // Before starting the purchase, you could tell the
    // user that the restore action is happening, maybe with
    // an activity indicator

    let iap = IAPManager(productID: IAPManager.removeAdsID)
    iap.beginRestorePurchases()
}
Run Code Online (Sandbox Code Playgroud)

最后,让我们为最后五个函数添加一些代码。

// Call this to check whether or not
// ads are removed. You can use the
// result of this to hide or show
// ads
class func areAdsRemoved() -> Bool{
    // This is the code that is run to check
    // if the user has the IAP.

    return UserDefaults.standard.bool(forKey: "RemoveAdsPurchased")
}

// This will be called by IAPManager
// when the user sucessfully purchases
// the IAP
class func removeAdsSuccess(){
    // This is the code that is run to actually
    // give the IAP to the user!
    //
    // I'm using UserDefaults in this example,
    // but you may want to use Keychain,
    // or some other method, as UserDefaults
    // can be modified by users using their
    // computer, if they know how to, more
    // easily than Keychain

    UserDefaults.standard.set(true, forKey: "RemoveAdsPurchased")
    UserDefaults.standard.synchronize()
}

// This will be called by IAPManager
// when the user sucessfully restores
//  their purchases
class func restoreRemoveAdsSuccess(){
    // Give the user their IAP back! Likely all you'll need to
    // do is call the same function you call when a user
    // sucessfully completes their purchase. In this case, removeAdsSuccess()

    removeAdsSuccess()
}

// This will be called by IAPManager
// when the IAP failed
class func removeAdsFailure(){
    // Send the user a message explaining that the IAP
    // failed for some reason, and to try again later
}

// This will be called by IAPManager
// when the IAP gets deferred.
class func removeAdsDeferred(){
    // Send the user a message explaining that the IAP
    // was deferred, and pending an external action, like
    // Ask to Buy.
}
Run Code Online (Sandbox Code Playgroud)

把它们放在一起,我们得到这样的东西:

import Foundation
import StoreKit

public class RemoveAdsManager{

    // Call this when the user wants
    // to remove ads, like when they
    // press a "remove ads" button
    class func removeAds(){
        // Before starting the purchase, you could tell the
        // user that their purchase is happening, maybe with
        // an activity indicator

        let iap = IAPManager(productID: IAPManager.removeAdsID)
        iap.beginPurchase()
    }

    // Call this when the user wants
    // to restore their IAP purchases,
    // like when they press a "restore
    // purchases" button.
    class func restoreRemoveAds(){
        // Before starting the purchase, you could tell the
        // user that the restore action is happening, maybe with
        // an activity indicator

        let iap = IAPManager(productID: IAPManager.removeAdsID)
        iap.beginRestorePurchases()
    }

    // Call this to check whether or not
    // ads are removed. You can use the
    // result of this to hide or show
    // ads
    class func areAdsRemoved() -> Bool{
        // This is the code that is run to check
        // if the user has the IAP.

        return UserDefaults.standard.bool(forKey: "RemoveAdsPurchased")
    }

    // This will be called by IAPManager
    // when the user sucessfully purchases
    // the IAP
    class func removeAdsSuccess(){
        // This is the code that is run to actually
        // give the IAP to the user!
        //
        // I'm using UserDefaults in this example,
        // but you may want to use Keychain,
        // or some other method, as UserDefaults
        // can be modified by users using their
        // computer, if they know how to, more
        // easily than Keychain

        UserDefaults.standard.set(true, forKey: "RemoveAdsPurchased")
        UserDefaults.standard.synchronize()
    }

    // This will be called by IAPManager
    // when the user sucessfully restores
    //  their purchases
    class func restoreRemoveAdsSuccess(){
        // Give the user their IAP back! Likely all you'll need to
        // do is call the same function you call when a user
        // sucessfully completes their purchase. In this case, removeAdsSuccess()
        removeAdsSuccess()
    }

    // This will be called by IAPManager
    // when the IAP failed
    class func removeAdsFailure(){
        // Send the user a message explaining that the IAP
        // failed for some reason, and to try again later
    }

    // This will be called by IAPManager
    // when the IAP gets deferred.
    class func removeAdsDeferred(){
        // Send the user a message explaining that the IAP
        // was deferred, and pending an external action, like
        // Ask to Buy.
    }

}

public class IAPManager: NSObject, SKProductsRequestDelegate, SKPaymentTransactionObserver{

    // This should the ID of the in-app-purchase you made on AppStore Connect.
    // if you have multiple IAPs, you'll need to store their identifiers in
    // other variables, too (or, preferably in an enum).
    static let removeAdsID = "com.skiplit.removeAds"

    // This is the initializer for your IAPManager class
    //
    // An alternative, and more scaleable way of doing this
    // is to also accept a callback in the initializer, and call
    // that callback in places like the paymentQueue function, and
    // in all functions in this class, in place of calls to functions
    // in RemoveAdsManager.
    let productID: String
    init(productID: String){
        self.productID = productID
    }

    // Call this when you want to begin a purchase
    // for the productID you gave to the initializer
    public func beginPurchase(){
        // If the user can make payments
        if SKPaymentQueue.canMakePayments(){
            // Create a new request
            let request = SKProductsRequest(productIdentifiers: [productID])
            request.delegate = self
            request.start()
        }
        else{
            // Otherwise, tell the user that
            // they are not authorized to make payments,
            // due to parental controls, etc
        }
    }

    // Call this when you want to restore all purchases
    // regardless of the productID you gave to the initializer
    public func beginRestorePurchases(){
        SKPaymentQueue.default().add(self)
        SKPaymentQueue.default().restoreCompletedTransactions()
    }

    // This is called when a SKProductsRequest receives a response
    public func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse){
        // Let's try to get the first product from the response
        // to the request
        if let product = response.products.first{
            // We were able to get the product! Make a new payment
            // using this product
            let payment = SKPayment(product: product)

            // add the new payment to the queue
            SKPaymentQueue.default().add(self)
            SKPaymentQueue.default().add(payment)
        }
        else{
            // Something went wrong! It is likely that either
            // the user doesn't have internet connection, or
            // your product ID is wrong!
            //
            // Tell the user in requestFailed() by sending an alert,
            // or something of the sort

            RemoveAdsManager.removeAdsFailure()
        }
    }

    // This is called when the user restores their IAP sucessfully
    private func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue){
        // For every transaction in the transaction queue...
        for transaction in queue.transactions{
            // If that transaction was restored
            if transaction.transactionState == .restored{
                // get the producted ID from the transaction
                let productID = transaction.payment.productIdentifier

                // In this case, we have only one IAP, so we don't need to check
                // what IAP it is. However, this is useful if you have multiple IAPs!
                // You'll need to figure out which one was restored
                if(productID.lowercased() == IAPManager.removeAdsID.lowercased()){
                    // Restore the user's purchases
                    RemoveAdsManager.restoreRemoveAdsSuccess()
                }

                // finish the payment
                SKPaymentQueue.default().finishTransaction(transaction)
            }
        }
    }

    // This is called when the state of the IAP changes -- from purchasing to purchased, for example.
    // This is where the magic happens :)
    public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]){
        for transaction in transactions{
            // get the producted ID from the transaction
            let productID = transaction.payment.productIdentifier

            // In this case, we have only one IAP, so we don't need to check
            // what IAP it is.
            // However, if you have multiple IAPs, you'll need to use productID
            // to check what functions you should run here!

            switch transaction.transactionState{
            case .purchasing:
                // if the user is currently purchasing the IAP,
                // we don't need to do anything.
                //
                // You could use this to show the user
                // an activity indicator, or something like that
                break
            case .purchased:
                // the user sucessfully purchased the IAP!
                RemoveAdsManager.removeAdsSuccess()
                SKPaymentQueue.default().finishTransaction(transaction)
            case .restored:
                // the user restored their IAP!
                RemoveAdsManager.restoreRemoveAdsSuccess()
                SKPaymentQueue.default().finishTransaction(transaction)
            case .failed:
                // The transaction failed!
                RemoveAdsManager.removeAdsFailure()
                // finish the transaction
                SKPaymentQueue.default().finishTransaction(transaction)
            case .deferred:
                // This happens when the IAP needs an external action
                // in order to proceeded, like Ask to Buy
                RemoveAdsManager.removeAdsDeferred()
                break
            }
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

最后,您需要为用户添加一些方式来开始购买和通话,RemoveAdsManager.removeAds()并开始恢复和通话RemoveAdsManager.restoreRemoveAds(),就像某处的按钮一样!请记住,根据 App Store 指南,您确实需要提供一个按钮以在某处恢复购买。

提交审核

最后要做的就是在 App Store Connect 上提交您的 IAP 以供审核!有关这样做的详细说明,您可以按照我的 Objective-C 答案的最后一部分,在提交以供审核标题下。