Bir iOS uygulamasına uygulama içi satın alma işlemini nasıl eklersiniz?


257

Bir iOS uygulamasına uygulama içi satın alma işlemini nasıl eklersiniz? Tüm detaylar nelerdir ve herhangi bir örnek kod var mı?

Bu, iOS uygulamalarına uygulama içi satın alımların nasıl ekleneceği ile ilgili her şeyi yakalama amaçlıdır


11
"Uygulama İçi Satın Alma Programlama Kılavuzu" nu okumaya ne dersiniz?
rmaddy

Yanıtlar:


554

Hızlı Kullanıcılar

Hızlı kullanıcılar bu soru için Hızlı Yanıtım'a göz atabilir .
Veya bu Objective-C kodunu Swift'e çeviren Yedidya Reiss'in Cevabına bir göz atın .

Objective-C Kullanıcıları

Bu cevabın geri kalanı Objective-C'de yazılmıştır.

App Store Bağlantısı

  1. Appstoreconnect.apple.com adresine gidin ve giriş yapın
  2. Click My Appssonra sen satın eklemek do istediğiniz uygulamayı tıklayın
  3. FeaturesBaşlığı tıklayın ve ardından In-App Purchasessol taraftan seçin
  4. +Ortadaki simgeyi tıklayın
  5. Bu eğitimde, reklamları kaldırmak için bir uygulama içi satın alma ekleyeceğiz, bu yüzden seçin non-consumable. Kullanıcıya fiziksel bir öğe gönderecek ya da onlara birden fazla satın alabilecekleri bir şey verecek olsaydınız, seçerdiniz consumable.
  6. Referans adı için ne istersen koy (ama ne olduğunu bildiğinden emin ol)
  7. Ürün kimliği koymak için tld.websitename.appname.referencenamebu en iyi şekilde çalışır, bu nedenle örneğincom.jojodmo.blix.removeads
  8. Seç cleared for saleve ardından fiyat katmanını olarak 1 (99 ¢) seçin. Katman 2 1,99 dolar ve katman 3 2,99 dolar olur. Tam liste kullanılabilir seçeneğini tıklarsanız view pricing matrix, 1. katmanı kullanmanızı öneririm, çünkü bu genellikle reklamları kaldırmak için ödeyecek en çok kişi olur.
  9. Mavi add languagedüğmeyi tıklayın ve bilgileri girin. Bu TÜM müşteriye gösterilecek, bu yüzden görmelerini istemediğiniz hiçbir şeyi koymayın
  10. Hayır için hosting content with Appleseçin
  11. ŞİMDİ için inceleme notlarını boş bırakabilirsiniz .
  12. screenshot for review ŞİMDİ İÇİN atla, atladığımız her şey geri gelecektir.
  13. 'Kaydet'i tıklayın

Ürün kimliğinizin kaydolması birkaç saat sürebilir App Store Connect, bu yüzden sabırlı olun.

Projenizi oluşturma

Artık App Store Connect'te uygulama içi satın alma bilgilerinizi ayarladığınıza, Xcode projenize gidin ve uygulama yöneticisine gidin (yöntemlerinizin ve başlık dosyalarınızın bulunduğu yerin üstündeki mavi sayfa benzeri simge) tıklayın uygulamanız hedeflerin altında (birincisi olmalıdır) sonra genel gidin. Alt tarafta, görmelisiniz linked frameworks and librariesaz şey artı simgesini tıklayın ve çerçeve eklemek StoreKit.frameworkBunu yapmazsanız, uygulama içi satın alma olacak DEĞİL çalışır!

Uygulamanızın dili olarak Objective-C kullanıyorsanız, bu beş adımı atlamanız gerekir . Aksi takdirde, Swift kullanıyorsanız, bu soru için Swift Yanıtımı takip edebilirsiniz , burada veya Uygulama İçi Satın Alma kodu için Objective-C kullanmayı tercih ediyorsanız ancak uygulamanızda Swift kullanıyorsanız aşağıdakileri yapabilirsiniz :

  1. Yeni oluştur .hgiderek (başlık) dosyası File> New> File...( Command ⌘+ N). Bu dosyaya .höğreticinin geri kalanında "Dosyanız" adı verilecektir

  2. İstendiğinde, Köprüleme Başlığı Oluştur'u tıklayın . Bu köprüleme başlık dosyamız olacak. Eğer varsa değil istendiyse adım 3'e gidin edilir istendiğinde, 3. adımı atlayın ve 4. adımda doğrudan gitmek.

  3. Ana proje klasöründe başka bir .hdosya oluşturun Bridge.h, ardından Uygulama Yöneticisi'ne (mavi sayfa benzeri simge) gidin, ardından Targetsbölümde uygulamanızı seçin ve simgesini tıklayın Build Settings. Diyor seçeneği bulun Swift Compiler - Kod Üretimi ve ardından set Objective-C Bridging Başlık seçeneği içinBridge.h

  4. Senin köprü başlık dosyasında satır ekleyin #import "MyObjectiveCHeaderFile.h", MyObjectiveCHeaderFileadım birinde oluşturulan bu başlık dosyasının adıdır. Örneğin, başlık dosyanızı InAppPurchase.h olarak adlandırdıysanız , satırı #import "InAppPurchase.h"köprü başlık dosyanıza eklersiniz .

  5. Yeni bir Objective-C Yöntemleri (oluşturun .mgiderek) dosyası File> New> File...( Command ⌘+ N). Bunu 1. adımda oluşturduğunuz başlık dosyasıyla aynı şekilde adlandırın. Örneğin, 1. adımdaki dosyayı InAppPurchase.h'de çağırdıysanız , bu yeni dosyayı InAppPurchase.m olarak adlandırırsınız . Bu dosyaya .möğreticinin geri kalanında "Dosyanız" adı verilecektir .

Kodlama

Şimdi asıl kodlamaya gireceğiz. .hDosyanıza aşağıdaki kodu ekleyin :

BOOL areAdsRemoved;

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

Daha sonra, StoreKitçerçeveyi .mdosyanıza içe aktarmanız ve beyanınızdan sonra SKProductsRequestDelegateve SKPaymentTransactionObserversonra eklemeniz gerekir @interface:

#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

ve şimdi .mdosyanıza aşağıdakileri ekleyin , bu bölüm karmaşıklaşır, bu yüzden koddaki yorumları okumanızı öneririz:

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

Artık kullanıcı işlemi bitirdiğinde ne olacağına ilişkin kodunuzu eklemek istiyorsunuz, bu öğretici için eklemeleri kaldırmayı kullanıyoruz, banner görünümü yüklendiğinde ne olacağı için kendi kodunuzu eklemeniz gerekecek.

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

Uygulamanızda reklam yoksa, istediğiniz başka bir şey kullanabilirsiniz. Örneğin, arka plan rengini mavi yapabiliriz. Bunu yapmak için kullanmak isteriz:

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

Şimdi, viewDidLoadyönteminizin bir yerinde aşağıdaki kodu eklemek isteyeceksiniz:

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
}

Artık tüm kodu eklediğinize, dosyanıza .xibveya storyboarddosyanıza gidin ve biri satın alma ve diğeri geri yükleme diyen iki düğme ekleyin. Bağla tapsRemoveAds IBActionsadece yaptığı satın alma düğmesine ve restore IBActiongeri düğmesine. İşlem restore, kullanıcının uygulama içi satın alma işlemini daha önce satın alıp almadığını kontrol eder ve henüz satın almadıysa onlara uygulama içi satın alma işlemini ücretsiz verir.

İnceleme için gönderiliyor

Ardından, App Store Connect'e gidin ve tıklayın, Users and Accessardından Sandbox Testersbaşlığı tıklayın ve ardından +solda söylediği simgeyi tıklayın Testers. Sadece ad ve soyadı için rastgele şeyler koyabilirsiniz ve e-postanın gerçek olması gerekmez - hatırlamanız gerekir. Bir şifre girin (hatırlamanız gerekecek) ve bilgilerin geri kalanını doldurun. Date of BirthKullanıcıyı 18 veya daha yaşlı kılacak bir tarih yapmanızı tavsiye ederim . App Store Territory HAS doğru ülkede olmak. Ardından, mevcut iTunes hesabınızdan çıkış yapın (bu eğiticiden sonra tekrar giriş yapabilirsiniz).

Eğer simülatörü üzerinde denemenizi Şimdi, eğer iOS cihazınızda uygulamayı çalıştırmak, satın alınacak her zaman hata, sen ZORUNDA iOS cihazınızda çalıştırın. Uygulama çalıştıktan sonra satın al düğmesine dokunun. İTunes hesabınıza giriş yapmanız istendiğinde, az önce oluşturduğumuz test kullanıcısı olarak giriş yapın. Daha sonra, sizden 99 ¢ satın alma işlemini veya fiyat katmanını ne ayarladığınızı onaylamanızı istediğinde, EKRAN BİR SNAPSHOT ALINscreenshot for review App Store Connect'iniz için kullanacağınız şey budur . Şimdi ödemeyi iptal edin.

Şimdi, gidin App Store Connect'e sonra gidin My Apps> the app you have the In-app purchase on> In-App Purchases. Ardından, uygulama içi satın alma işleminizi ve uygulama içi satın alma ayrıntılarının altındaki düzenle'yi tıklayın. Bunu yaptıktan sonra, iPhone'unuzda çektiğiniz fotoğrafı bilgisayarınıza alın ve incelenmek üzere ekran görüntüsü olarak yükleyin, ardından inceleme notlarına TEST KULLANICI e-postanızı ve şifrenizi girin. Bu, elmanın inceleme sürecinde yardımcı olacaktır.

Bunu yaptıktan sonra, iOS cihazınızdaki uygulamaya tekrar gidin, yine de test kullanıcı hesabı olarak oturum açın ve satın al düğmesini tıklayın. Bu kez, ödemeyi onaylayın Endişelenmeyin, bu hesabınızdan HERHANGİ bir ücret talep etmeyecektir, test kullanıcı hesapları tüm uygulama içi satın alımları ücretsiz olarak alır Ödemeyi onayladıktan sonra, kullanıcı ürününüzü gerçekten satın aldığında ne olduğundan emin olun olur. Değilse, doRemoveAdsyönteminizle ilgili bir hata olacaktır . Yine, uygulama içi satın almayı test etmek için arka planı mavi olarak değiştirmenizi öneririm, ancak gerçek uygulama içi satın alımınız olmamalıdır. Her şey yolunda giderse ve gitmeye hazırsanız! App Store Connect'e yüklediğinizde yeni ikili dosyaya uygulama içi satın alma işlemini eklemeyi unutmayın!


Bazı yaygın hatalar şunlardır:

Giriş yapan kullanıcı: No Products Available

Bu dört anlama gelebilir:

  • Kodunuza doğru uygulama içi satın alma kimliğini koymadınız ( kRemoveAdsProductIdentifieryukarıdaki koddaki tanımlayıcı için)
  • App Store Connect'te satışa sunulan uygulama içi satın alma işleminizi temizlemediniz
  • Uygulama içi satın alma kimliğinin App Store Connect'e kaydedilmesini beklemediniz . Kimliği oluşturmak için birkaç saat bekleyin; sorununuz çözülmelidir.
  • Anlaşmalar, Vergi ve Bankacılık bilgilerinizi doldurmayı tamamlamadınız.

İlk kez çalışmazsa, sinirlenmeyin! Vazgeçme! Bu işe başlamadan önce yaklaşık 5 saat sürdü ve doğru kodu aramak yaklaşık 10 saat sürdü! Yukarıdaki kodu tam olarak kullanırsanız, düzgün çalışmalıdır. Herhangi bir sorunuz varsa yorum yapmaktan çekinmeyin hiç .

Umarım bu, iOS uygulamalarına bir uygulama içi satın alma eklemeyi umut edenlerin tümüne yardımcı olur. Şerefe!


1
ama bu satırı eklemezseniz, geri yükleme düğmesine tıkladığımda hiçbir şey olmuyor .. yine de bu eğitim için çok teşekkür ederim;)
Ilario

1
"(eğer * işlem * == SKPaymentTransactionStateRestored) {" (eğer olmalıdır * transaction.transactionState * == SKPaymentTransactionStateRestored) {
Massmaker

13
Apple'ın en iyi uygulamaları, işlem denetleyicisini görünüm denetleyicisi eylemlerine değil, AppDelegate'e eklemenizi önerir. developer.apple.com/library/ios/technotes/tn2387/_index.html
Craig Pickering

3
Toplam 0 ürün alıyorum, ancak listelediğiniz olası 3 nedeni zaten kontrol ettim. Eğer itunes connect içinde "ios ücretli uygulama sözleşmesi" iletişim bilgileri, banka bilgileri ve vergi bilgileri kurmak değil akla gelen tek şey, bu olabilir mi?
Christopher Francisco

4
Adım 9'da Görünen adın kullanıcıya sunulan şey olduğunu açıklamalısınız. Ve bu şekilde sunulur: "0,99 $ karşılığında bir EKRAN ADI satın almak ister misiniz?". Görünen adımı "Reklamları kaldır" ı yaptığım ve pop-up'ta uygunsuz dilbilgisi kullandığım için uygulamam reddedildiğinden bu önemlidir! Görünen Adımı "Reklam Kaldırma Paketi" olarak değiştirmek zorunda kaldım.
Alan Scarpa

13

Jojodmo kodunu Swift'e çevirin:

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

        }


    }
}
} 

6

Hızlı Cevap

Bu desteklemek için tasarlanmıştır benim Objective-C cevabı çok büyük girmesini Objective-C cevabı tutmak için, Swift kullanıcıları için.

Kurmak

İlk olarak, appstoreconnect.apple.com adresinden uygulama içi satın alma işlemini ayarlayın . Bunu yapmakla ilgili talimatlar için Objective-C cevabımın ( App Store Connect başlığının altındaki 1-13. Adımlar) başlangıç ​​kısmını takip edin .

Ürün kimliğinizin App Store Connect'e kaydolması birkaç saat sürebilir, bu yüzden sabırlı olun.

Uygulama içi satın alma bilgilerinizi App Store Connect'te ayarladığınıza göre, Apple'ın uygulama içi satın almalarla ilgili çerçevesini uygulamaya eklememiz gerekiyor StoreKit.

Xcode projenize gidin ve uygulama yöneticisine gidin (soldaki çubuğun üst kısmında, uygulamanızın dosyalarının bulunduğu mavi sayfa benzeri simge). Soldaki hedeflerin altındaki uygulamanızı tıklayın (ilk seçenek olmalıdır), ardından üstteki "Yetenekler" e gidin. Listede bir "Uygulama İçi Satın Alma" seçeneği görmeniz gerekir. Bu özelliği AÇIN, Xcode StoreKitprojenize eklenecektir .

Kodlama

Şimdi kodlamaya başlayacağız!

İlk olarak, tüm uygulama içi satın alma işlemlerinizi yönetecek yeni bir hızlı dosya oluşturun. Ben arayacağım IAPManager.swift.

Bu dosyada, biz denilen yeni bir sınıf oluşturmak için gidiyoruz IAPManagerbir olduğunu SKProductsRequestDelegateve SKPaymentTransactionObserver. Üstte, içe aktardığınızdan FoundationveStoreKit

import Foundation
import StoreKit

public class IAPManager: NSObject, SKProductsRequestDelegate,
                         SKPaymentTransactionObserver {
}

Daha sonra, uygulama içi satın alma işlemimizin tanımlayıcısını tanımlamak için bir değişken ekleyeceğiz ( enumbirden fazla IAP'niz varsa bakımı daha kolay olacak bir an da kullanabilirsiniz ).

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

Şimdi sınıfımız için bir başlatıcı ekleyelim:

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

Şimdi, çalışmak SKProductsRequestDelegateve SKPaymentTransactionObserverçalışmak için gerekli işlevleri ekleyeceğiz :

RemoveAdsManagerSınıfı daha sonra ekleyeceğiz

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

Şimdi bir satın alma işlemini başlatmak veya satın alma işlemlerini geri yüklemek için kullanılabilecek bazı işlevler ekleyelim:

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

Ardından, IAP'larımızı yönetmek için yeni bir yardımcı program sınıfı ekleyelim. Bu kodun hepsi bir sınıfta olabilir, ancak birden fazla olması onu biraz daha temiz hale getirir. Yeni bir sınıf yapacağım RemoveAdsManagerve içine birkaç işlev koyacağım

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

İlk üç fonksiyonlar, removeAds, restoreRemoveAds, ve areAdsRemoved, belirli eylemleri yapmak diyeceğiz o fonksiyonlardır. Son dördü çağrılacak olanlardır IAPManager.

İlk iki işleve bir kod ekleyelim removeAdsve 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()
}

Son olarak, son beş işleve biraz kod ekleyelim.

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

Hepsini bir araya getirirsek, şöyle bir şey elde ederiz:

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

}

Son olarak, kullanıcının satın alma işlemini başlatması ve bir yerde bir düğme gibi bir RemoveAdsManager.removeAds()geri yükleme ve arama başlatması için bir yol eklemeniz gerekir RemoveAdsManager.restoreRemoveAds()! App Store yönergelerine göre, satın alma işlemlerini bir yere geri yüklemek için bir düğme sağlamanız gerektiğini unutmayın.

İnceleme için gönderiliyor

Yapılacak son şey, IAP'nizi App Store Connect'te incelenmek üzere göndermektir! Bunu yapmakla ilgili ayrıntılı talimatlar için, İnceleme için gönder başlığı altında Objective-C cevabımın son bölümünü takip edebilirsiniz .


4

RMStore , Uygulama İçi Satın Alımlar için hafif bir iOS kütüphanesidir. StoreKit API'sini sarar ve size eşzamansız istekler için kullanışlı bloklar sağlar. Bir ürünü satın almak, tek bir yöntemi çağırmak kadar kolaydır.

İleri düzey kullanıcılar için bu kütüphane ayrıca makbuz doğrulaması, içerik indirmeleri ve işlemin kalıcılığı da sağlar.


-1

Bunu yayınlamak için çok geç kaldığımı biliyorum, ancak IAP modelinin iplerini öğrendiğimde benzer deneyimleri paylaşıyorum.

Uygulama içi satın alma, iOS'ta Storekit çerçevesi tarafından uygulanan en kapsamlı iş akışlarından biridir. Tüm belgeler size sabır okumak için eğer çok açık olduğunu, ancak biraz teknik bir doğası içinde ilerletilir.

Özetlemek:

1 - Ürünleri talep edin - Ürün Kimlikleri için istek göndermek ve bunları kendi itunesconnect mağazanızdan geri almak için SKProductRequest & SKProductRequestDelegate sınıflarını kullanın.

Bu SKP ürünleri, kullanıcının belirli bir ürünü satın almak için kullanabileceği mağaza kullanıcı arayüzünü doldurmak için kullanılmalıdır.

2 - Ödeme isteği yayınla - işlem kuyruğuna ödeme eklemek için SKPayment & SKPaymentQueue kullanın.

3 - Durum güncellemesi için işlem kuyruğunu izleyin - durumu izlemek için SKPaymentTransactionObserver Protocol'un updatedTransactions yöntemini kullanın:

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

4 - Düğme akışını geri yükle - bunu gerçekleştirmek için SKPaymentQueue'nun restoreCompletedTransactions komutunu kullanın - 3. adım, SKPaymentTransactionObserver'ın aşağıdaki yöntemleriyle birlikte geri kalanı ile ilgilenir:

paymentQueueRestoreCompletedTransactionsFinished
restoreCompletedTransactionsFailedWithError

İşte bunu (kendi anlama girişimlerimin bir sonucu olarak benim tarafımdan yazılan) açıklayan bir adım adım öğretici. Sonunda, doğrudan kullanabileceğiniz kod örneği de sağlar.

İşte sadece metnin daha iyi tanımlayabileceği bazı şeyleri açıklamak için oluşturduğum başka bir örnek.


21
StackOverflow başkalarına yardım eden bir web sitesidir ve onlardan para kazanmaya çalışmak için değildir. Ya sondan ikinci bağlantıyı kaldırmalı ya da bu öğreticide yapılanları ücretsiz olarak göndermelisiniz.
Jojodmo

@Jojodmo, talebinizi SO tarafından herhangi bir yönerge ile kanıtlayabilir misiniz? Bir çok insanın kendi SDK'sını (hatta ücretli) bile bir yasal uyarı ile pazarladığını görüyorum, ki burada da çok fazla mevcut olduğunu düşünüyorum.
Nirav Bhatt

12
Buna karşı yönergeler yok, ancak para kazanmak için buradaysanız, muhtemelen yanlış nedenlerden dolayı buradasınız. IMO, cevabınız, başkalarına yardım etmemek için insanları video eğitimlerinize kaydettirmeye odaklanmış gibi görünüyor
Jojodmo

3
Bu sadece rahatsızlıktan başka bir şey değildir.
durazno
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.