web-dev-qa-db-ja.com

iOSアプリケーションにアプリ内購入をどのように追加しますか?

IOSアプリにアプリ内購入をどのように追加しますか?すべての詳細とサンプルコードはありますか?

これは、iOSアプリにアプリ内購入を追加する方法の包括的な種類のものを意味します

251
Jojodmo

IOS 7-Xcode 5+のiOS 12でアプリ内購入を行うための最良の方法は、以下を実行することです。

スイフトユーザー

Swiftユーザーは、チェックアウトできます My Swiftこの質問に対する回答
または、 Yedidya Reiss's Answer をチェックしてください。これは、このObjective-CコードをSwiftに変換します。

App Store Connect

  1. appstoreconnect.Apple.com に移動してログインします
  2. My Appsをクリックしてから、購入を追加するアプリをクリックします
  3. Featuresname__ヘッダーをクリックし、左側のIn-App Purchasesを選択します
  4. 中央の+アイコンをクリックします
  5. このチュートリアルでは、アプリ内購入を追加して広告を削除するため、non-consumableを選択します。物理的なアイテムをユーザーに送信する場合、または複数回購入できるものをユーザーに提供する場合は、consumablename__を選択します。
  6. 参照名には、必要なものを何でも入れます(ただし、それが何であるかを確認してください)
  7. 製品IDにtld.websitename.appname.referencenameを追加すると、これが最適に機能します。たとえば、com.jojodmo.blix.removeadsを使用できます。
  8. cleared for saleを選択してから、価格ティアを1(99¢)に選択します。ティア2は1.99ドル、ティア3は2.99ドルです。 view pricing matrixをクリックすると、完全なリストが利用可能になります。通常、Tier 1を使用することをお勧めします。Tier1は通常、広告を削除するために支払う金額が最も多いためです。
  9. 青いadd languageボタンをクリックして、情報を入力します。これはすべて顧客に表示されるので、見たくないものは置かないでください
  10. hosting content with Appleにはnoを選択します
  11. レビューノートを空白のままにすることができます FOR NOW
  12. screenshot for review FOR NOW をスキップします。スキップしたものはすべて戻ってきます。
  13. 「保存」をクリックします

製品IDがApp Store Connectに登録されるまでに数時間かかる場合があるため、しばらくお待ちください。

プロジェクトを設定する

App Store Connectでアプリ内購入情報を設定し、Xcodeプロジェクトに移動して、アプリケーションマネージャー(メソッドとヘッダーファイルがある場所の上部にある青いページのようなアイコン)に移動します。アプリをターゲット(最初のもの)の下に置いてから、一般に進みます。一番下に、linked frameworks and librariesが小さなプラス記号をクリックしてフレームワークを追加するのが見えるはずですStoreKit.frameworkこれを行わないと、アプリ内購入はではありません!

アプリの言語としてObjective-Cを使用している場合、これら5つのステップをスキップする必要があります。それ以外の場合、Swiftを使用している場合は、 My Swiftこの質問に対する回答はこちら に従うか、In-App PurchaseコードにObjective-Cを使用する場合アプリでSwiftを使用している場合は、次のことができます。

  1. Filename__> Newname__> .h(に移動して、新しいFile...(ヘッダー)ファイルを作成します。Command ⌘ + N)。このファイルは、チュートリアルの残りの部分で「Your .h file」と呼ばれます

  2. プロンプトが表示されたら、[ブリッジングヘッダーの作成]をクリックします。これがブリッジングヘッダーファイルになります。 notプロンプトが表示されたら、ステップ3に進みます。 are プロンプトが表示されたら、ステップ3をスキップしてステップ4に直接進みます。

  3. メインプロジェクトフォルダーに.hという名前の別のBridge.hファイルを作成し、アプリケーションマネージャー(青いページのようなアイコン)に移動して、Targetsname__セクションでアプリを選択し、Build Settingsをクリックします。 Swift Compiler-Code Generationと書かれているオプションを見つけて、Objective-C Bridging HeaderオプションをBridge.hに設定します

  4. ブリッジングヘッダーファイルに、行#import "MyObjectiveCHeaderFile.h"を追加します。ここで、MyObjectiveCHeaderFilename__は、手順1で作成したヘッダーファイルの名前です。したがって、たとえば、ヘッダーファイルにInAppPurchase.hという名前を付けた場合、#import "InAppPurchase.h"行をブリッジヘッダーファイルに追加します。

  5. Filename__> Newname__> .m(に移動して、新しいObjective-Cメソッド(File...)ファイルを作成します。Command ⌘ + N)。手順1で作成したヘッダーファイルと同じ名前を付けます。たとえば、手順1でファイルを呼び出した場合InAppPurchase.h、この新しいファイルを呼び出しますInAppPurchase.m。このファイルは、チュートリアルの残りの部分で「Your .m file」と呼ばれます。

コーディング

次に、実際のコーディングについて説明します。次のコードを.hファイルに追加します。

BOOL areAdsRemoved;

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

次に、StoreKitname__フレームワークを.mファイルにインポートし、@interface宣言の後にSKProductsRequestDelegatename__およびSKPaymentTransactionObservername__を追加する必要があります。

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

ここで、ユーザーがトランザクションを終了したときに何が起こるかについてコードを追加します。このチュートリアルでは、addsを削除します。バナービューが読み込まれたときに何が起こるかについて、独自のコードを追加する必要があります。

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

ここで、viewDidLoadname__メソッドのどこかに、次のコードを追加する必要があります。

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
}

すべてのコードを追加したので、.xibまたはstoryboardname__ファイルに移動し、2つのボタンを追加します。1つは購入、もう1つは復元です。 tapsRemoveAdsIBActionname__を作成したばかりの購入ボタンに、restoreIBActionname__を復元ボタンに接続します。 restorename__アクションは、ユーザーが以前にアプリ内購入を購入したかどうかを確認し、まだ持っていない場合は無料でアプリ内購入を提供します。

レビューのために提出

次に、 App Store Connect に移動し、Users and AccessをクリックしてからSandbox Testersヘッダーをクリックし、左側のTestersname__と書かれている+記号をクリックします。姓と名にランダムなものを入れるだけで、電子メールは本物である必要はありません-覚えておく必要があります。パスワード(覚えておく必要があります)を入力し、残りの情報を入力します。 Date of Birthを、ユーザーが18歳以上になる日付にすることをお勧めします。 App Store Territoryが正しい国にあるようにします。次に、既存のiTunesアカウントからログアウトします(このチュートリアルの後、再度ログインできます)。

IOSデバイスでアプリケーションを実行します。シミュレータで実行しようとすると、購入は always エラーになります。HAVE TOで実行しますiOSデバイス。アプリが実行されたら、購入ボタンをタップします。 iTunesアカウントにログインするように求められたら、作成したテストユーザーとしてログインします。次に、99¢の購入を確認するか、価格ティアを設定したものを確認するよう求められたら、画面の概要を確認これがApp Store Connectのscreenshot for reviewに使用するものです。支払いをキャンセルします。

次に、 App Store Connect に移動してから、My Apps> the app you have the In-app purchase on> In-App Purchasesに移動します。次に、アプリ内購入をクリックし、アプリ内購入の詳細の下にある[編集]をクリックします。それが終わったら、iPhoneで撮った写真をコンピューターにインポートし、それをレビュー用のスクリーンショットとしてアップロードし、レビューノートにTEST USER電子メールを入れますとパスワード。これは、レビュープロセスでAppleに役立ちます。

これを行った後、iOSデバイスのアプリケーションに戻り、テストユーザーアカウントとしてログインし、購入ボタンをクリックします。今回は、支払いを確認しますご心配なく、これはアカウントにお金を請求しません。テストユーザーアカウントはすべてのアプリ内購入を無料で取得します支払いを確認したら、何が起こるかを確認しますユーザーが実際に製品を購入するときが発生します。そうでない場合は、doRemoveAdsname__メソッドでエラーになります。繰り返しますが、アプリ内購入のテストには背景を青に変更することをお勧めしますが、これは実際のアプリ内購入ではありません。すべてがうまくいき、あなたが行ってもいいなら! App Store Connectにアップロードするときに、新しいバイナリにアプリ内購入を含めるようにしてください!


一般的なエラーは次のとおりです。

Logged:No Products Available

これは次の3つのことを意味します。

  • コードに正しいアプリ内購入IDを入れていません(上記のコードの識別子kRemoveAdsProductIdentifiername__に対して)
  • App Store Connect で販売するアプリ内購入をクリアしませんでした
  • アプリ内購入IDが App Store Connect に登録されるのを待ちませんでした。 IDの作成から数時間待つと、問題が解決するはずです。
  • 契約、税金、銀行情報の入力が完了していません。

初めて動作しない場合でも、イライラしないでください!あきらめないで!これが機能するまでに約5時間かかり、適切なコードを検索するのに約10時間かかりました。上記のコードを正確に使用すると、正常に機能するはずです。質問があればまったく/とコメントしてください。

これが、iOSアプリにアプリ内購入を追加したいすべての人に役立つことを願っています。乾杯!

543
Jojodmo

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")

        }


    }
}
} 
12
Yedidya Reiss

RMStore はアプリ内購入向けの軽量iOSライブラリです。 StoreKit APIをラップし、非同期リクエスト用の便利なブロックを提供します。商品の購入は、単一の方法を呼び出すのと同じくらい簡単です。

上級ユーザー向けに、このライブラリはレシートの確認、コンテンツのダウンロード、そしてトランザクションの持続性も提供します。

4

迅速な回答

これは、Objective-Cの回答が大きくなりすぎないようにするために、Swiftユーザーに対して 私のObjective-Cの回答 を補足することを目的としています。

セットアップ

まず、 appstoreconnect.Apple.com でアプリ内購入を設定します。手順については my Objective-C answer (ステップ1-13、App Store Connectヘッダーの下)の冒頭部分に従ってくださいそうすることで。

製品IDがApp Store Connectに登録されるまでに数時間かかることがありますので、しばらくお待ちください。

App Store Connectでアプリ内購入情報を設定したので、アプリ内購入用のAppleのフレームワークStoreKitをアプリに追加する必要があります。

Xcodeプロジェクトに移動し、アプリケーションマネージャーに移動します(アプリのファイルがある左側のバーの上部にある青いページのようなアイコン)。左側のターゲットの下にあるアプリをクリックし(最初のオプションである必要があります)、上部の[機能]に移動します。リストに「In-App Purchase」オプションが表示されます。この機能をオンにすると、XcodeはプロジェクトにStoreKitを追加します。

コーディング

さあ、コーディングを始めましょう!

最初に、すべてのアプリ内購入を管理する新しいSwiftファイルを作成します。 IAPManager.Swiftと呼びます。

このファイルでは、IAPManagerおよびSKProductsRequestDelegateであるSKPaymentTransactionObserverという新しいクラスを作成します。上部で、必ずFoundationStoreKitをインポートしてください

import Foundation
import StoreKit

public class IAPManager: NSObject, SKProductsRequestDelegate,
                         SKPaymentTransactionObserver {
}

次に、アプリ内購入の識別子を定義する変数を追加します(複数のIAPがある場合に維持しやすいenumを使用することもできます)。

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

次に、SKProductsRequestDelegateおよびSKPaymentTransactionObserverが機能するために必要な関数を追加します。

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を管理する新しいユーティリティクラスを追加しましょう。このコードはすべて1つのクラスに含めることができますが、複数のコードを使用することで少し簡潔になります。 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()
}

最初の3つの関数、removeAdsrestoreRemoveAds、およびareAdsRemovedは、特定のアクションを実行するために呼び出す関数です。最後の4つは、IAPManagerによって呼び出されるものです。

最初の2つの関数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()
}

最後に、最後の5つの関数にコードを追加しましょう。

// 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の答え の最後の部分をたどって、レビューのために送信するヘッダ。

0
Jojodmo

私はこれを投稿するのがかなり遅れていることを知っています、しかし私がIAPモデルのロープを学んだとき私は同様の経験を共有します。

アプリ内購入は、Storekitフレームワークによって実装されるiOSの最も包括的なワークフローの1つです。 全体のドキュメント あなたがそれを読むことに忍耐があるかどうかはかなり明確ですが、専門性の点でやや高度です。

要約する:

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

ここ はそれを説明するステップバイステップのチュートリアル(私自身がそれを理解しようとした結果として作成されたもの)です。最後に、直接使用できるコードサンプルも提供されています。

ここ は、テキストだけがより良い方法で記述できることを説明するために作成したものです。

0
Nirav Bhatt