簡體   English   中英

如何將應用內購買添加到 iOS 應用程序?

[英]How do you add an in-app purchase to an iOS application?

如何將應用內購買添加到 iOS 應用? 所有細節是什么,是否有任何示例代碼?

這是關於如何向 iOS 應用程序添加應用程序內購買的各種信息

Swift 用戶

Swift 用戶可以查看我的 Swift Answer 來了解這個問題
或者,查看Yedidya Reiss's Answer ,它將此 Objective-C 代碼轉換為 Swift。

Objective-C 用戶

這個答案的其余部分是用 Objective-C 編寫的

應用商店連接

  1. 前往appstoreconnect.apple.com並登錄
  2. 單擊My Apps然后單擊您想要添加購買的應用程序
  3. 單擊Features標題,然后選擇左側In-App Purchases
  4. 單擊中間的+圖標
  5. 在本教程中,我們將添加一個應用內購買來移除廣告,所以選擇non-consumable 如果您要向用戶發送實物,或者給他們一些他們可以多次購買的東西,您會選擇consumable
  6. 對於參考名稱,輸入您想要的任何內容(但請確保您知道它是什么)
  7. 對於 product id put tld.websitename.appname.referencename這將是最好的,例如,你可以使用com.jojodmo.blix.removeads
  8. 選擇cleared for sale ,然后選擇價格等級為 1 (99¢)。 第 2 層為 1.99 美元,第 3 層為 2.99 美元。 如果您單擊view pricing matrix則可以使用完整列表。我建議您使用第 1 層,因為這通常是任何人為刪除廣告而支付的最高費用。
  9. 點擊藍色的add language按鈕,輸入信息。 這將全部顯示給客戶,所以不要放任何你不想讓他們看到的東西
  10. hosting content with Apple選擇“否”
  11. 您可以將復習筆記空白現在
  12. 跳過screenshot for review現在,一切我們跳過我們會回來。
  13. 點擊“保存”

您的產品 ID 在App Store Connect注冊可能需要幾個小時,所以請耐心等待。

設置您的項目

現在你已經在 App Store Connect 上設置了你的應用內購買信息,進入你的 Xcode 項目,然后進入應用程序管理器(方法和頭文件所在的頂部的藍色頁面圖標)點擊您的應用程序在目標下(應該是第一個)然后轉到一般。 在底部,你會看到linked frameworks and libraries點擊小加號,並添加框架StoreKit.framework如果你不這樣做,應用程序內購買將無法正常工作!

如果你使用 Objective-C 作為你的應用程序語言,你應該跳過這五個步驟 否則,如果您使用的是 Swift,則可以在此處按照我的 Swift 回答對此問題進行操作,或者,如果您更喜歡將 Objective-C 用於應用內購買代碼但在您的應用中使用 Swift,則可以執行以下操作:

  1. 通過轉到File > New > File...命令 ⌘ + N )創建一個新的.h (頭文件)文件。 此文件將在本教程的其余部分稱為“您的.h文件”

  2. 出現提示時,單擊創建橋接頭 這將是我們的橋接頭文件。 如果沒有提示您,請轉到步驟3.如果系統提示您,請跳過步驟3,直接進入第4步。

  3. 在主項目文件夾中創建另一個名為Bridge.h .h文件,然后轉到 Application Manager(藍色頁面狀圖標),然后在Targets部分中選擇您的應用程序,然后單擊Build Settings 找到Swift Compiler - Code Generation選項,然后將Objective-C Bridging Header選項設置為Bridge.h

  4. 在您的橋接頭文件中,添加行#import "MyObjectiveCHeaderFile.h" ,其中MyObjectiveCHeaderFile是您在第一步中創建的頭文件的名稱。 因此,例如,如果您將頭文件命名為InAppPurchase.h ,則應將#import "InAppPurchase.h"行添加到橋頭文件中。

  5. 通過轉到File > New > File...命令 ⌘ + N )創建一個新的 Objective-C 方法 ( .m ) 文件。 將其命名為您在第 1 步中創建的頭文件。例如,如果您在第 1 步中調用該文件InAppPurchase.h ,您將調用這個新文件InAppPurchase.m 在本教程的其余部分,此文件將被稱為“您的.m文件”。

編碼

現在我們將進入實際的編碼。 將以下代碼添加到您的.h文件中:

BOOL areAdsRemoved;

- (IBAction)restore;
- (IBAction)tapsRemoveAds;

接下來,您需要將StoreKit框架導入到您的.m文件中,並在您的@interface聲明之后添加SKProductsRequestDelegateSKPaymentTransactionObserver

#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

現在將以下內容添加到您的.m文件中,這部分變得復雜,所以我建議您閱讀代碼中的注釋:

//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;
        }
    }
}

現在你想添加你的代碼來處理當用戶完成交易時會發生什么,在本教程中,我們使用刪除添加,你必須為橫幅視圖加載時發生的情況添加自己的代碼。

- (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];
}

如果您的應用程序中沒有廣告,您可以使用任何其他您想要的東西。 例如,我們可以將背景的顏色設為藍色。 為此,我們需要使用:

- (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];
}

現在,在viewDidLoad方法中的某處,您將要添加以下代碼:

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
}

現在您已經添加了所有代碼,進入您的.xibstoryboard文件,並添加兩個按鈕,一個表示購買,另一個表示恢復。 tapsRemoveAds IBAction到您剛剛創建的購買按鈕,並將restore IBAction連接到恢復按鈕。 restore操作將檢查用戶之前是否購買了應用內購買,如果他們還沒有應用內購買,則免費為他們提供應用內購買。

提交審核

接下來,進入App Store Connect ,然后單擊Users and Access然后單擊Sandbox Testers標題,然后單擊左側的+符號Testers 您可以隨意輸入名字和姓氏的內容,並且電子郵件不必是真實的——您只需要能夠記住它。 輸入密碼(您必須記住)並填寫其余信息。 我建議您將Date of Birth日期設為使用戶年滿 18 歲的日期。 App Store Territory必須在正確的國家。 接下來,注銷您現有的 iTunes 帳戶(您可以在本教程之后重新登錄)。

現在,你的iOS設備上運行應用程序,如果你試圖在模擬器上運行它,購買總會錯誤,你有你的iOS設備上運行。 應用程序運行后,點擊購買按鈕。 當系統提示您登錄 iTunes 帳戶時,請以我們剛剛創建的測試用戶身份登錄。 接下來,當它要求您確認購買 99 美分或任何您設置的價格等級時,請screenshot for review它的屏幕快照,這就是您將screenshot for review在 App Store Connect 上查看screenshot for review 現在取消付款。

現在,轉到App Store Connect ,然后轉到My Apps > the app you have the In-app purchase on > In-App Purchases 然后點擊您的應用內購買,然后點擊應用內購買詳情下的編輯。 完成后,將您剛在 iPhone 上拍攝的照片導入計算機,並將其作為屏幕截圖上傳以供審核,然后在審核筆記中輸入您的測試用戶電子郵件和密碼。 這將有助於蘋果在審查過程中。

完成此操作后,返回 iOS 設備上的應用程序,仍以測試用戶帳戶登錄,然后單擊購買按鈕。 這一次,確認付款別擔心,這不會向您的帳戶收取任何費用,測試用戶帳戶免費獲得所有應用內購買確認付款后,請確保用戶實際購買您的產品時會發生什么發生。 如果沒有,那么您的doRemoveAds方法就會出錯。 同樣,我建議使用將背景更改為藍色來測試應用內購買,但這不應該是您實際的應用內購買。 如果一切正常,你就可以開始了! 當您將其上傳到 App Store Connect 時,請確保將應用內購買包含在您的新二進制文件中!


以下是一些常見錯誤:

已記錄: No Products Available

這可能意味着四件事:

  • 您沒有在代碼中輸入正確的應用內購買 ID(對於上述代碼中的標識符kRemoveAdsProductIdentifier
  • 您未在App Store Connect上清除要出售的應用內購買
  • 您沒有等待應用內購買 ID 在App Store Connect 中注冊。 創建 ID 后等待幾個小時,您的問題應該會得到解決。
  • 您沒有完成協議、稅務和銀行信息的填寫。

如果它第一次不起作用,請不要沮喪! 不要放棄! 我花了大約 5 個小時才開始工作,大約 10 個小時才能找到正確的代碼! 如果您完全使用上面的代碼,它應該可以正常工作。 如果您有任何問題請隨時發表評論。

我希望這對所有希望在他們的 iOS 應用程序中添加應用程序內購買的人有所幫助。 干杯!

只需將 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")

        }


    }
}
} 

快速回答

這是為了補充為 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

在這個文件中,我們將創建一個名為IAPManager的新類,它是一個SKProductsRequestDelegateSKPaymentTransactionObserver 在頂部,確保導入FoundationStoreKit

import Foundation
import StoreKit

public class IAPManager: NSObject, SKProductsRequestDelegate,
                         SKPaymentTransactionObserver {
}

接下來,我們將添加一個變量來定義我們的應用內購買的標識符(您也可以使用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"

接下來讓我們為我們的類添加一個初始化程序:

// 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
}

現在,我們將添加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
        }
    }
}

現在讓我們添加一些可用於開始購買或恢復購買的功能:

// 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()
}

接下來,讓我們添加一個新的實用程序類來管理我們的 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()
}

前三個函數removeAdsrestoreRemoveAdsareAdsRemoved是您將調用以執行某些操作的函數。 最后四個是將被IAPManager調用的IAPManager

讓我們為前兩個函數removeAdsrestoreRemoveAds添加一些代碼:

// 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.
}

把它們放在一起,我們得到這樣的東西:

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

}

最后,您需要為用戶添加一些方法來開始購買並調用RemoveAdsManager.removeAds()並開始恢復並調用RemoveAdsManager.restoreRemoveAds() ,就像某個按鈕一樣! 請記住,根據 App Store 指南,您確實需要提供一個按鈕以在某處恢復購買。

提交審核

最后要做的就是在 App Store Connect 上提交您的 IAP 以供審核! 有關這樣做的詳細說明,您可以按照我的 Objective-C 答案的最后一部分,在提交以供審核標題下。

RMStore是一個用於應用內購買的輕量級 iOS 庫。 它包裝了 StoreKit API 並為您提供了方便的異步請求塊。 購買產品就像調用一個方法一樣簡單。

對於高級用戶,該庫還提供收據驗證、內容下載和交易持久化。

我知道我發布這個已經很晚了,但是當我學習 IAP 模型的繩索時,我分享了類似的經驗。

應用內購買是由 Storekit 框架實現的 iOS 中最全面的工作流程之一。 如果您耐心閱讀, 整個文檔會非常清晰,但在技術性方面有些先進。

總結一下:

1 - 請求產品 - 使用 SKProductRequest 和 SKProductRequestDelegate 類發出對產品 ID 的請求,並從您自己的 itunesconnect 商店接收它們。

這些 SKProducts 應該用於填充用戶可以用來購買特定產品的商店 UI。

2 - 發出付款請求 - 使用 SKPayment & SKPaymentQueue 將付款添加到交易隊列中。

3 - 監控交易隊列的狀態更新 - 使用 SKPaymentTransactionObserver 協議的 updatedTransactions 方法來監控狀態:

SKPaymentTransactionStatePurchasing - don't do anything
SKPaymentTransactionStatePurchased - unlock product, finish the transaction
SKPaymentTransactionStateFailed - show error, finish the transaction
SKPaymentTransactionStateRestored - unlock product, finish the transaction

4 - 恢復按鈕流程 - 使用 SKPaymentQueue 的 restoreCompletedTransactions 來完成此操作 - 步驟 3 將處理其余部分,以及 SKPaymentTransactionObserver 的以下方法:

paymentQueueRestoreCompletedTransactionsFinished
restoreCompletedTransactionsFailedWithError

是一個解釋它的分步教程(由於我自己嘗試理解它而由我編寫)。 最后還提供了可以直接使用的代碼示例。

是我創建的另一個內容,用於解釋某些只有文字才能更好地描述的事物。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM