Wie füge ich einer iOS-Anwendung einen In-App-Kauf hinzu?

257

Wie füge ich einer iOS-App einen In-App-Kauf hinzu? Was sind alle Details und gibt es einen Beispielcode?

Dies ist eine Art Sammelbegriff für das Hinzufügen von In-App-Käufen zu iOS-Apps

Jojodmo
quelle
11
Was ist mit dem Lesen des "In-App Purchase Programming Guide"?
rmaddy
3
Apples In-App-
Hemang

Antworten:

554

Schnelle Benutzer

Swift-Benutzer können My Swift Answer für diese Frage lesen .
Oder lesen Sie die Antwort von Yedidya Reiss , die diesen Objective-C-Code in Swift übersetzt.

Objective-C-Benutzer

Der Rest dieser Antwort ist in Ziel-C geschrieben

App Store Connect

  1. Gehen Sie zu appstoreconnect.apple.com und melden Sie sich an
  2. Klicken Sie My Appsdann auf die App, zu der Sie den Kauf hinzufügen möchten
  3. Klicken Sie auf die FeaturesÜberschrift und wählen Sie dann In-App Purchaseslinks aus
  4. Klicken Sie auf das +Symbol in der Mitte
  5. Für dieses Tutorial fügen wir einen In-App-Kauf hinzu, um Anzeigen zu entfernen. Wählen Sie also non-consumable. Wenn Sie dem Benutzer einen physischen Artikel senden oder ihm etwas geben möchten, das er mehr als einmal kaufen kann, würden Sie wählen consumable.
  6. Geben Sie als Referenznamen ein, was Sie wollen (aber stellen Sie sicher, dass Sie wissen, was es ist).
  7. Für die Produkt-ID tld.websitename.appname.referencenamefunktioniert dies am besten, sodass Sie sie beispielsweise verwenden könnencom.jojodmo.blix.removeads
  8. Wählen Sie cleared for saleund wählen Sie dann Preisstufe als 1 (99 ¢). Tier 2 wäre 1,99 USD und Tier 3 wäre 2,99 USD. Die vollständige Liste ist verfügbar, wenn Sie auf view pricing matrixIch empfehle die Verwendung von Tier 1 klicken , da dies normalerweise das Höchste ist, das jemals jemand für das Entfernen von Anzeigen bezahlen wird.
  9. Klicken Sie auf die blaue add languageSchaltfläche und geben Sie die Informationen ein. Dies wird dem Kunden ALLES angezeigt. Geben Sie also nichts ein, was Sie nicht sehen möchten
  10. Für hosting content with Applewählen Sie nein
  11. Sie können die Überprüfungsnotizen JETZT leer lassen .
  12. Überspringen Sie das screenshot for review FOR NOW , auf alles, was wir überspringen, werden wir zurückkommen.
  13. Klicken Sie auf "Speichern".

Es kann einige Stunden dauern, bis sich Ihre Produkt-ID registriert App Store Connecthat. Seien Sie also geduldig.

Einrichten Ihres Projekts

Nachdem Sie Ihre In-App-Kaufinformationen in App Store Connect eingerichtet haben, rufen Sie Ihr Xcode-Projekt auf und gehen Sie zum Anwendungsmanager (blaues seitenähnliches Symbol oben auf Ihren Methoden und Header-Dateien). Klicken Sie auf Ihre App unter Ziele (sollte die erste sein) gehen Sie dann zu Allgemein. Unten sollten Sie sehen, wie linked frameworks and librariesSie auf das kleine Pluszeichen klicken und das Framework hinzufügen. StoreKit.frameworkWenn Sie dies nicht tun, funktioniert der In-App-Kauf NICHT !

Wenn Sie Objective-C als Sprache für Ihre App verwenden, sollten Sie diese fünf Schritte überspringen . Wenn Sie Swift verwenden, können Sie My Swift Answer für diese Frage hier folgen. Wenn Sie Objective-C für den In-App-Kaufcode verwenden möchten, Swift jedoch in Ihrer App verwenden, können Sie Folgendes tun ::

  1. Erstellen Sie eine neue .h(Header - Datei) , indem Sie auf File> New> File...( Command ⌘+ N). Diese Datei wird .him Rest des Tutorials als "Ihre Datei" bezeichnet

  2. Wenn Sie dazu aufgefordert werden, klicken Sie auf Bridging-Header erstellen . Dies wird unsere Bridging-Header-Datei sein. Wenn Sie nicht dazu aufgefordert werden, fahren Sie mit Schritt 3 fort. Wenn Sie dazu aufgefordert werden, überspringen Sie Schritt 3 und fahren Sie direkt mit Schritt 4 fort.

  3. Erstellen Sie eine weitere .hDatei mit dem Namen Bridge.him Hauptprojektordner. Wechseln Sie dann zum Anwendungsmanager (das blaue seitenähnliche Symbol), wählen Sie Ihre App im TargetsAbschnitt aus und klicken Sie auf Build Settings. Suchen Sie die Option Swift Compiler - Codegenerierung und setzen Sie die Option Objective-C Bridging Header aufBridge.h

  4. Fügen Sie in Ihrer Bridging-Header-Datei die Zeile hinzu #import "MyObjectiveCHeaderFile.h", in der MyObjectiveCHeaderFilesich der Name der Header-Datei befindet, die Sie in Schritt 1 erstellt haben. Wenn Sie beispielsweise Ihre Header-Datei InAppPurchase.h benannt haben , würden Sie die Zeile #import "InAppPurchase.h"zu Ihrer Bridge-Header-Datei hinzufügen .

  5. Erstellen Sie eine neue Objective-C - Methoden ( .mDatei) , indem Sie auf File> New> File...( Command ⌘+ N). Nennen Sie es genauso wie die Header-Datei, die Sie in Schritt 1 erstellt haben. Wenn Sie beispielsweise die Datei in Schritt 1 InAppPurchase.h aufgerufen haben , würden Sie diese neue Datei InAppPurchase.m aufrufen . Diese Datei wird .mim Rest des Tutorials als "Ihre Datei" bezeichnet.

Codierung

Jetzt werden wir uns mit der eigentlichen Codierung befassen. Fügen Sie Ihrer .hDatei den folgenden Code hinzu :

BOOL areAdsRemoved;

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

Als nächstes müssen Sie das StoreKitFramework in Ihre .mDatei importieren sowie hinzufügen SKProductsRequestDelegateund SKPaymentTransactionObservernach Ihrer @interfaceDeklaration:

#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

Wenn Sie nun Folgendes in Ihre .mDatei einfügen, wird dieser Teil kompliziert. Ich empfehle daher, die Kommentare im Code zu lesen:

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

Jetzt möchten Sie Ihren Code für das hinzufügen, was passieren wird, wenn der Benutzer die Transaktion beendet. In diesem Lernprogramm verwenden wir das Entfernen von Adds. Sie müssen Ihren eigenen Code für das hinzufügen, was passiert, wenn die Banneransicht geladen wird.

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

Wenn Ihre Anwendung keine Anzeigen enthält, können Sie eine beliebige andere Verwendung verwenden. Zum Beispiel könnten wir die Farbe des Hintergrunds blau machen. Dazu möchten wir Folgendes verwenden:

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

Jetzt viewDidLoadmöchten Sie irgendwo in Ihrer Methode den folgenden Code hinzufügen:

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
}

Nachdem Sie den gesamten Code hinzugefügt haben, gehen Sie in Ihre .xiboder storyboard-Datei und fügen Sie zwei Schaltflächen hinzu, eine mit der Aufschrift "Kaufen" und die andere mit der Aufschrift "Wiederherstellen". Schließen Sie die tapsRemoveAds IBActionSchaltfläche "Kaufen", die Sie gerade erstellt haben, und die restore IBActionSchaltfläche "Wiederherstellen" an. Die restoreAktion prüft, ob der Benutzer den In-App-Kauf zuvor gekauft hat, und gibt ihm den In-App-Kauf kostenlos, falls er ihn noch nicht hat.

Zur Überprüfung einreichen

Gehen Sie als Nächstes in den App Store Connect und klicken Users and AccessSie auf die Sandbox TestersÜberschrift und dann auf das +Symbol links, wo es steht Testers. Sie können nur zufällige Dinge für den Vor- und Nachnamen eingeben, und die E-Mail muss nicht echt sein - Sie müssen sich nur daran erinnern können. Geben Sie ein Passwort ein (an das Sie sich erinnern müssen) und geben Sie den Rest der Informationen ein. Ich würde empfehlen, dass Sie Date of Birthein Datum festlegen, an dem der Benutzer mindestens 18 Jahre alt ist. App Store Territory MUSS im richtigen Land sein. Melden Sie sich anschließend von Ihrem vorhandenen iTunes-Konto ab (Sie können sich nach diesem Lernprogramm wieder anmelden).

Führen Sie jetzt Ihre Anwendung auf Ihrem iOS-Gerät aus. Wenn Sie versuchen, sie auf dem Simulator auszuführen , wird der Kauf immer fehlschlagen. Sie MÜSSEN sie auf Ihrem iOS-Gerät ausführen. Sobald die App ausgeführt wird, tippen Sie auf die Schaltfläche Kaufen. Wenn Sie aufgefordert werden, sich bei Ihrem iTunes-Konto anzumelden, melden Sie sich als Testbenutzer an, den wir gerade erstellt haben. Wenn Sie als Nächstes aufgefordert werden, den Kauf von 99 ¢ zu bestätigen, oder was auch immer Sie für die Preisstufe festlegen, machen Sie einen BILDSCHIRM-SCHNAPPSCHUSS , den Sie für Ihren screenshot for reviewApp Store Connect verwenden werden. Stornieren Sie nun die Zahlung.

Jetzt gehen, um App Store Connect , dann gehen My Apps> the app you have the In-app purchase on> In-App Purchases. Klicken Sie dann auf Ihren In-App-Kauf und dann unter den In-App-Kaufdetails auf Bearbeiten. Wenn Sie dies getan haben, importieren Sie das Foto, das Sie gerade auf Ihrem iPhone aufgenommen haben, in Ihren Computer und laden Sie es als Screenshot zur Überprüfung hoch. Geben Sie dann in den Überprüfungsnotizen Ihre TEST-BENUTZER- E-Mail-Adresse und Ihr Kennwort ein. Dies hilft Apple bei der Überprüfung.

Nachdem Sie dies getan haben, kehren Sie zu der Anwendung auf Ihrem iOS-Gerät zurück, die weiterhin als Testbenutzerkonto angemeldet ist, und klicken Sie auf die Schaltfläche "Kaufen". Bestätigen Sie diesmal die Zahlung. Keine Sorge, dies belastet Ihr Konto NICHT mit Geld. Testbenutzerkonten erhalten alle In-App-Käufe kostenlos. Nachdem Sie die Zahlung bestätigt haben, stellen Sie sicher, was passiert, wenn der Benutzer Ihr Produkt tatsächlich kauft das passiert. Wenn dies nicht der Fall ist, ist dies ein Fehler bei Ihrer doRemoveAdsMethode. Auch hier empfehle ich, den Hintergrund zum Testen des In-App-Kaufs auf Blau zu ändern. Dies sollte jedoch nicht Ihr tatsächlicher In-App-Kauf sein. Wenn alles funktioniert und Sie bereit sind zu gehen! Stellen Sie einfach sicher, dass Sie den In-App-Kauf in Ihre neue Binärdatei aufnehmen, wenn Sie ihn in den App Store Connect hochladen!


Hier sind einige häufige Fehler:

Protokolliert: No Products Available

Dies könnte vier Dinge bedeuten:

  • Sie haben nicht die richtige In-App-Kauf-ID in Ihren Code eingegeben (für die Kennung kRemoveAdsProductIdentifierim obigen Code
  • Sie haben Ihren In-App-Kauf für den Verkauf im App Store Connect nicht freigegeben
  • Sie haben nicht darauf gewartet, dass die In-App-Kauf-ID im App Store Connect registriert wird . Warten Sie einige Stunden nach dem Erstellen der ID, und Ihr Problem sollte behoben sein.
  • Sie haben Ihre Vertrags-, Steuer- und Bankdaten nicht vollständig ausgefüllt.

Wenn es beim ersten Mal nicht funktioniert, seien Sie nicht frustriert! Gib nicht auf! Es dauerte ungefähr 5 Stunden, bis ich das zum Laufen bringen konnte, und ungefähr 10 Stunden, um nach dem richtigen Code zu suchen! Wenn Sie den obigen Code genau verwenden, sollte er einwandfrei funktionieren. Fühlen Sie sich frei zu kommentieren , wenn Sie Fragen haben , überhaupt .

Ich hoffe, dies hilft all jenen, die hoffen, ihrer iOS-Anwendung einen In-App-Kauf hinzuzufügen. Prost!

Jojodmo
quelle
1
aber wenn ich diese Zeile nicht hinzufüge, wenn ich auf die Schaltfläche "Wiederherstellen" klicke, passiert nichts. Trotzdem vielen Dank für dieses Tutorial;)
Ilario
1
"if ( * transaction * == SKPaymentTransactionStateRestored) {" sollte if ( * transaction.transactionState * == SKPaymentTransactionStateRestored) {
Massmaker
13
In den Best Practices von Apple wird empfohlen, den Transaktionsbeobachter zum AppDelegate hinzuzufügen, nicht die View Controller-Aktionen. developer.apple.com/library/ios/technotes/tn2387/_index.html
Craig Pickering
3
Ich erhalte eine Anzahl von 0 Produkten, habe aber bereits die 3 möglichen Gründe überprüft, die Sie aufgelistet haben. Das einzige, was mir in den Sinn kommt, wenn ich die Kontaktinformationen, Bankinformationen und Steuerinformationen für den "ios Paid App-Vertrag" in iTunes Connect nicht eingerichtet habe, könnte dies der Grund sein?
Christopher Francisco
4
Sie sollten erklären, dass in Schritt 9 der Anzeigename dem Benutzer angezeigt wird. Und es wird folgendermaßen dargestellt: "Möchten Sie einen DISPLAY NAME für 0,99 USD kaufen?". Dies ist wichtig, da ich meinen Anzeigenamen "Anzeigen entfernen" eingegeben habe und meine App dann abgelehnt wurde, weil ich im Popup eine falsche Grammatik verwendet habe! Ich musste meinen Anzeigenamen in "Ad Removal Package" ändern.
Alan Scarpa
13

Übersetzen Sie einfach Jojodmo-Code in 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")

        }


    }
}
} 
Yedidya Reiss
quelle
6

Schnelle Antwort

Dies soll meine Objective-C-Antwort für Swift-Benutzer ergänzen , um zu verhindern, dass die Objective-C-Antwort zu groß wird.

Konfiguration

Richten Sie zunächst den In-App-Kauf auf appstoreconnect.apple.com ein . Befolgen Sie dazu den Anfang meiner Objective-C-Antwort (Schritte 1-13 unter dem App Store Connect- Header).

Es kann einige Stunden dauern, bis sich Ihre Produkt-ID im App Store Connect registriert hat. Seien Sie also geduldig.

Nachdem Sie Ihre In-App-Kaufinformationen in App Store Connect eingerichtet haben, müssen wir der App das Apple-Framework für In-App-Käufe hinzufügen StoreKit.

Gehen Sie in Ihr Xcode-Projekt und rufen Sie den Anwendungsmanager auf (blaues seitenähnliches Symbol oben in der linken Leiste, in dem sich die Dateien Ihrer App befinden). Klicken Sie links unter den Zielen auf Ihre App (dies sollte die erste Option sein) und gehen Sie dann oben zu "Funktionen". In der Liste sollte die Option "In-App-Kauf" angezeigt werden. Wenn Sie diese Funktion aktivieren, wird Xcode StoreKitzu Ihrem Projekt hinzugefügt .

Codierung

Jetzt fangen wir an zu programmieren!

Erstellen Sie zunächst eine neue schnelle Datei, die alle Ihre In-App-Käufe verwaltet. Ich werde es nennen IAPManager.swift.

In dieser Datei erstellen wir eine neue Klasse mit dem Namen IAPManagera SKProductsRequestDelegateund SKPaymentTransactionObserver. Stellen Sie oben sicher, dass Sie Foundationund importierenStoreKit

import Foundation
import StoreKit

public class IAPManager: NSObject, SKProductsRequestDelegate,
                         SKPaymentTransactionObserver {
}

Als Nächstes fügen wir eine Variable hinzu, um die Kennung für unseren In-App-Kauf zu definieren (Sie können auch eine verwenden enum, die bei mehreren IAPs einfacher zu warten wäre).

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

Fügen wir als nächstes einen Initialisierer für unsere Klasse hinzu:

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

Jetzt werden wir die erforderlichen Funktionen hinzufügen SKProductsRequestDelegateund SKPaymentTransactionObserverfunktionieren:

Wir werden die RemoveAdsManagerKlasse später hinzufügen

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

Fügen wir nun einige Funktionen hinzu, mit denen Sie einen Kauf starten oder einen Kauf wiederherstellen können:

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

Als Nächstes fügen wir eine neue Dienstprogrammklasse hinzu, um unsere IAPs zu verwalten. Der gesamte Code könnte in einer Klasse sein, aber wenn er mehrfach vorhanden ist, ist er etwas sauberer. Ich werde eine neue Klasse namens RemoveAdsManagererstellen und darin einige Funktionen einfügen

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

Die ersten drei Funktionen removeAds, restoreRemoveAdsund areAdsRemovedsind Funktionen , dass Sie bestimmte Aktionen zu tun, nennen. Die letzten vier werden von aufgerufen IAPManager.

Fügen wir den ersten beiden Funktionen Code hinzu removeAdsund 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()
}

Zuletzt fügen wir den letzten fünf Funktionen Code hinzu.

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

Alles zusammen ergibt so etwas:

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

}

Zuletzt müssen Sie dem Benutzer eine Möglichkeit hinzufügen, den Kauf zu starten und anzurufen RemoveAdsManager.removeAds()und eine Wiederherstellung und einen Anruf zu starten RemoveAdsManager.restoreRemoveAds(), wie irgendwo eine Schaltfläche! Beachten Sie, dass Sie gemäß den App Store-Richtlinien eine Schaltfläche bereitstellen müssen, um Einkäufe irgendwo wiederherzustellen.

Zur Überprüfung einreichen

Als letztes müssen Sie Ihren IAP zur Überprüfung im App Store Connect einreichen! Ausführliche Anweisungen hierzu finden Sie im letzten Teil meiner Objective-C-Antwort unter der Überschrift "Zur Überprüfung einreichen" .

Jojodmo
quelle
4

RMStore ist eine kompakte iOS-Bibliothek für In-App-Käufe. Es umschließt die StoreKit-API und bietet Ihnen praktische Blöcke für asynchrone Anforderungen. Der Kauf eines Produkts ist so einfach wie das Aufrufen einer einzelnen Methode.

Für fortgeschrittene Benutzer bietet diese Bibliothek auch eine Empfangsüberprüfung, das Herunterladen von Inhalten und die Persistenz von Transaktionen.

Vladimir Grigorov
quelle
-1

Ich weiß, dass ich ziemlich spät dran bin, um dies zu veröffentlichen, aber ich teile ähnliche Erfahrungen, als ich die Seile des IAP-Modells lernte.

Der In-App-Kauf ist einer der umfassendsten Workflows in iOS, die vom Storekit-Framework implementiert werden. Die gesamte Dokumentation ist ziemlich klar, wenn Sie Geduld haben, sie zu lesen, ist jedoch in technischer Hinsicht etwas fortgeschritten.

Zusammenfassen:

1 - Produkte anfordern - Verwenden Sie die Klassen SKProductRequest und SKProductRequestDelegate, um eine Anforderung für Produkt-IDs auszugeben und diese von Ihrem eigenen itunesconnect-Shop zurück zu erhalten.

Diese SKProducts sollten verwendet werden, um die Benutzeroberfläche Ihres Geschäfts zu füllen, mit der der Benutzer ein bestimmtes Produkt kaufen kann.

2 - Zahlungsanforderung ausgeben - Verwenden Sie SKPayment & SKPaymentQueue, um der Transaktionswarteschlange eine Zahlung hinzuzufügen.

3 - Überwachen der Transaktionswarteschlange auf Statusaktualisierung - Verwenden Sie die aktualisierte Transaktionsmethode des SKPaymentTransactionObserver-Protokolls, um den Status zu überwachen:

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

4 - Schaltflächenfluss wiederherstellen - Verwenden Sie dazu die restoreCompletedTransactions von SKPaymentQueue. Schritt 3 erledigt den Rest zusammen mit den folgenden Methoden von SKPaymentTransactionObserver:

paymentQueueRestoreCompletedTransactionsFinished
restoreCompletedTransactionsFailedWithError

Hier ist ein Schritt-für-Schritt-Tutorial (das ich aufgrund meiner eigenen Versuche, es zu verstehen, verfasst habe), das es erklärt. Am Ende finden Sie auch ein Codebeispiel, das Sie direkt verwenden können.

Hier ist eine andere, die ich erstellt habe, um bestimmte Dinge zu erklären, die nur Text besser beschreiben kann.

Nirav Bhatt
quelle
21
StackOverflow ist eine Website, um anderen zu helfen und nicht um Geld mit ihnen zu verdienen. Sie sollten entweder den vorletzten Link entfernen oder einfach kostenlos hier posten, was in diesem Tutorial gemacht wird.
Jojodmo
@Jojodmo können Sie Ihren Anspruch mit Richtlinien von SO begründen? Ich sehe viele Leute, die ihr eigenes SDK (sogar eines bezahlten) mit einem Haftungsausschluss vermarkten, was meiner Meinung nach auch hier sehr präsent ist.
Nirav Bhatt
12
Es gibt keine Richtlinien dagegen, aber wenn Sie hier sind, um Geld zu verdienen, sind Sie wahrscheinlich aus den falschen Gründen hier. IMO, Ihre Antwort scheint sich darauf zu konzentrieren, Leute dazu zu bringen, sich für Ihre Video-Tutorials
anzumelden
3
Das ist nur nichts als Ärger.
Durazno