Bagaimana Anda menambahkan pembelian dalam aplikasi ke aplikasi iOS?

Jawaban:

554

Pengguna Swift

Pengguna Swift dapat memeriksa My Swift Answer untuk pertanyaan ini .
Atau, periksa Yedidya Reiss's Answer , yang menerjemahkan kode Objective-C ini ke Swift.

Pengguna Objective-C

Sisa dari jawaban ini ditulis dalam Objective-C

App Store Connect

  1. Buka appstoreconnect.apple.com dan masuk
  2. Klik My Appslalu klik aplikasi yang ingin Anda tambahkan pembelian
  3. Klik Featurestajuk, lalu pilih In-App Purchasesdi sebelah kiri
  4. Klik +ikon di tengah
  5. Untuk tutorial ini, kami akan menambahkan pembelian dalam aplikasi untuk menghapus iklan, jadi pilihlah non-consumable. Jika Anda akan mengirim barang fisik kepada pengguna, atau memberi mereka sesuatu yang dapat mereka beli lebih dari sekali, Anda akan memilih consumable.
  6. Untuk nama referensi, masukkan apa pun yang Anda inginkan (tapi pastikan Anda tahu apa itu)
  7. Untuk id produk, tld.websitename.appname.referencenameini akan bekerja dengan baik, jadi misalnya, Anda dapat menggunakannyacom.jojodmo.blix.removeads
  8. Pilih cleared for saledan pilih tier harga sebagai 1 (99 ¢). Tingkat 2 akan menjadi $ 1,99, dan tingkat 3 akan menjadi $ 2,99. Daftar lengkap tersedia jika Anda mengklik view pricing matrixSaya sarankan Anda menggunakan tier 1, karena itu biasanya kebanyakan orang akan pernah membayar untuk menghapus iklan.
  9. Klik add languagetombol biru , dan masukkan informasinya. Ini SEMUA akan ditampilkan kepada pelanggan, jadi jangan menaruh apa pun yang Anda tidak ingin mereka lihat
  10. Untuk hosting content with Applememilih no
  11. Anda dapat membiarkan catatan ulasan kosong UNTUK SEKARANG .
  12. Lewati screenshot for review UNTUK SEKARANG , semua yang kita lewati akan kembali.
  13. Klik 'simpan'

Mungkin perlu beberapa jam untuk ID produk Anda mendaftar App Store Connect, jadi bersabarlah.

Menyiapkan proyek Anda

Sekarang setelah Anda mengatur informasi pembelian dalam aplikasi di App Store Connect, masuk ke proyek Xcode Anda, dan pergi ke manajer aplikasi (ikon seperti halaman biru di bagian atas tempat metode dan file header) klik pada aplikasi Anda di bawah target (harus menjadi yang pertama) kemudian buka general. Di bagian bawah, Anda akan melihat linked frameworks and librariesklik simbol tambah kecil dan tambahkan kerangka kerja StoreKit.frameworkJika Anda tidak melakukan ini, pembelian dalam aplikasi TIDAK akan berfungsi!

Jika Anda menggunakan Objective-C sebagai bahasa untuk aplikasi Anda, Anda harus melewati lima langkah ini . Jika tidak, jika Anda menggunakan Swift, Anda dapat mengikuti My Swift Answer untuk pertanyaan ini, di sini , atau, jika Anda lebih suka menggunakan Objective-C untuk kode Pembelian Dalam Aplikasi tetapi menggunakan Swift di aplikasi Anda, Anda dapat melakukan hal berikut :

  1. Buat baru .h(header) file dengan pergi ke File> New> File...( Command ⌘+ N). File ini akan disebut sebagai " .hFile Anda " di sisa tutorial

  2. Saat diminta, klik Buat Bridging Header . Ini akan menjadi file header bridging kami. Jika Anda tidak diminta, pergi ke langkah 3. Jika Anda sedang diminta, melewatkan langkah 3 dan langsung ke langkah 4.

  3. Buat .hfile lain bernama Bridge.hdalam folder proyek utama, Kemudian pergi ke Manajer Aplikasi (ikon seperti halaman biru), lalu pilih aplikasi Anda di Targetsbagian, dan klik Build Settings. Temukan opsi yang mengatakan Swift Compiler - Code Generation , dan kemudian tetapkan opsi Objective-C Bridging Header keBridge.h

  4. Dalam menjembatani file header Anda, tambahkan baris #import "MyObjectiveCHeaderFile.h", di mana MyObjectiveCHeaderFilenama file header yang Anda buat di langkah pertama. Jadi, misalnya, jika Anda memberi nama file header Anda InAppPurchase.h , Anda akan menambahkan baris #import "InAppPurchase.h"ke file header jembatan Anda.

  5. Buat Objective-C Metode (baru .m) file dengan pergi ke File> New> File...( Command ⌘+ N). Beri nama sama dengan file header yang Anda buat di langkah 1. Misalnya, jika Anda memanggil file di langkah 1 InAppPurchase.h , Anda akan memanggil file baru ini InAppPurchase.m . File ini akan disebut sebagai " .mFile Anda " di sisa tutorial.

Coding

Sekarang kita akan masuk ke pengkodean yang sebenarnya. Tambahkan kode berikut ke .hfile Anda :

BOOL areAdsRemoved;

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

Selanjutnya, Anda perlu mengimpor StoreKitkerangka kerja ke .mfile Anda , serta menambah SKProductsRequestDelegatedan SKPaymentTransactionObserversetelah @interfacedeklarasi Anda :

#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

dan sekarang tambahkan berikut ini ke .mfile Anda , bagian ini jadi rumit, jadi saya sarankan Anda membaca komentar dalam kode:

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

Sekarang Anda ingin menambahkan kode Anda untuk apa yang akan terjadi ketika pengguna menyelesaikan transaksi, untuk tutorial ini, kami menggunakan hapus add, Anda harus menambahkan kode Anda sendiri untuk apa yang terjadi ketika tampilan banner dimuat.

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

Jika Anda tidak memiliki iklan di aplikasi Anda, Anda dapat menggunakan hal lain yang Anda inginkan. Misalnya, kita bisa membuat warna latar belakang menjadi biru. Untuk melakukan ini kami ingin menggunakan:

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

Sekarang, di suatu tempat dalam viewDidLoadmetode Anda, Anda akan ingin menambahkan kode berikut:

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
}

Sekarang bahwa Anda telah menambahkan semua kode, pergi ke Anda .xibatau storyboardfile, dan menambahkan dua tombol, satu pembelian katakan, dan pepatah lainnya mengembalikan. Hubungkan tapsRemoveAds IBActionke tombol pembelian yang baru saja Anda buat, dan restore IBActionke tombol restore. The restoretindakan akan memeriksa apakah pengguna sebelumnya telah membeli pembelian di-app, dan memberi mereka di-app pembelian secara gratis jika mereka tidak sudah memilikinya.

Mengirimkan untuk ditinjau

Selanjutnya, masuk ke App Store Connect , dan klik Users and Accesslalu klik Sandbox Testerstajuk, lalu klik +simbol di sebelah kiri tempat dikatakan Testers. Anda dapat memasukkan hal-hal acak untuk nama depan dan belakang, dan email tidak harus nyata - Anda hanya harus dapat mengingatnya. Masukkan kata sandi (yang harus Anda ingat) dan isi informasi selanjutnya. Saya akan merekomendasikan Anda membuat Date of Birthtanggal yang akan membuat pengguna berusia 18 tahun ke atas. App Store Territory HARUS berada di negara yang benar. Selanjutnya, keluar dari akun iTunes Anda yang ada (Anda dapat masuk kembali setelah tutorial ini).

Sekarang, jalankan aplikasi Anda di perangkat iOS Anda, jika Anda mencoba menjalankannya di simulator, pembelian akan selalu salah, Anda HARUS menjalankannya di perangkat iOS Anda. Setelah aplikasi berjalan, ketuk tombol pembelian. Saat Anda diminta untuk masuk ke akun iTunes Anda, masuklah sebagai pengguna uji yang baru saja kami buat. Selanjutnya, ketika meminta Anda untuk mengkonfirmasi pembelian 99 ¢ atau apa pun yang Anda tetapkan juga tingkat harganya, AMBILKAN SNAPSHOT LAYAR, inilah yang akan Anda gunakan untuk screenshot for reviewdi App Store Connect Anda. Sekarang batalkan pembayaran.

Sekarang, pergilah ke App Store Connect , kemudian pergi ke My Apps> the app you have the In-app purchase on> In-App Purchases. Kemudian klik pembelian dalam aplikasi Anda dan klik edit di bawah detail pembelian dalam aplikasi. Setelah Anda selesai melakukannya, impor foto yang baru saja Anda ambil di iPhone ke komputer Anda, dan unggah itu sebagai tangkapan layar untuk ditinjau, lalu, dalam catatan ulasan, masukkan e-mail TEST USER dan kata sandi Anda. Ini akan membantu apel dalam proses peninjauan.

Setelah Anda melakukan ini, kembali ke aplikasi di perangkat iOS Anda, masih masuk sebagai akun pengguna uji, dan klik tombol beli. Kali ini, konfirmasi pembayaran. Jangan khawatir, ini TIDAK akan membebankan biaya pada akun Anda APA PUN, akun pengguna uji mendapatkan semua pembelian dalam aplikasi secara gratis Setelah Anda mengkonfirmasi pembayaran, pastikan bahwa apa yang terjadi ketika pengguna membeli produk Anda sebenarnya terjadi Jika tidak, maka itu akan menjadi kesalahan dengan doRemoveAdsmetode Anda . Sekali lagi, saya sarankan menggunakan mengubah latar belakang menjadi biru untuk menguji pembelian dalam aplikasi, ini seharusnya bukan pembelian dalam aplikasi Anda yang sebenarnya. Jika semuanya bekerja dan Anda baik untuk pergi! Pastikan untuk memasukkan pembelian dalam aplikasi dalam biner baru Anda ketika Anda mengunggahnya ke App Store Connect!


Berikut ini beberapa kesalahan umum:

Dicatat: No Products Available

Ini bisa berarti empat hal:

  • Anda tidak memasukkan ID pembelian dalam aplikasi yang benar dalam kode Anda (untuk pengidentifikasi kRemoveAdsProductIdentifier dalam kode di atas
  • Anda tidak menghapus pembelian dalam aplikasi untuk penjualan App Store Connect
  • Anda tidak menunggu ID pembelian dalam aplikasi untuk didaftarkan di App Store Connect . Tunggu beberapa jam dari membuat ID, dan masalah Anda harus diselesaikan.
  • Anda tidak menyelesaikan mengisi Perjanjian, Pajak, dan info Perbankan Anda.

Jika itu tidak berhasil pertama kali, jangan frustrasi! Jangan menyerah! Butuh waktu sekitar 5 jam langsung sebelum saya bisa membuatnya bekerja, dan sekitar 10 jam mencari kode yang tepat! Jika Anda menggunakan kode di atas persis, itu akan berfungsi dengan baik. Jangan ragu untuk berkomentar jika Anda memiliki pertanyaan sama sekali .

Saya harap ini membantu semua yang berharap untuk menambahkan pembelian dalam aplikasi ke aplikasi iOS mereka. Bersulang!

Jojodmo
sumber
1
tetapi jika saya tidak menambahkan baris itu, ketika saya mengklik tombol restore tidak ada yang terjadi .. toh terima kasih banyak untuk tutorial ini;)
Ilario
1
"if ( * transaction * == SKPaymentTransactionStateRestored) {" seharusnya jika ( * transaction.transactionState * == SKPaymentTransactionStateRestored) {
Massmaker
13
Praktik terbaik Apple merekomendasikan Anda menambahkan pengamat transaksi ke AppDelegate, bukan tindakan pengontrol tampilan. developer.apple.com/library/ios/technotes/tn2387/_index.html
Craig Pickering
3
Saya mendapatkan 0 produk, tetapi saya sudah memeriksa 3 kemungkinan alasan yang Anda daftarkan. Satu-satunya hal yang terlintas dalam pikiran jika saya belum mengatur info kontak, info bank dan info pajak pada "kontrak aplikasi berbayar ios" di dalam itunes terhubung, mungkinkah ini alasannya?
Christopher Francisco
4
Anda harus menjelaskan bahwa pada Langkah 9, nama Tampilan adalah yang disajikan kepada pengguna. Dan itu disajikan dengan cara ini: "Apakah Anda ingin membeli satu NAMA TAMPILAN seharga $ 0,99?". Ini penting karena saya membuat nama tampilan saya "Hapus iklan" dan kemudian aplikasi saya ditolak karena saya menggunakan tata bahasa yang tidak benar di pop up! Saya harus mengubah Nama Tampilan menjadi "Paket Penghapusan Iklan".
Alan Scarpa
13

Cukup terjemahkan kode Jojodmo ke 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
sumber
6

Jawaban Cepat

Ini dimaksudkan untuk melengkapi jawaban Objective-C saya untuk pengguna Swift, agar jawaban Objective-C saya tidak terlalu besar.

Mempersiapkan

Pertama, atur pembelian dalam aplikasi di appstoreconnect.apple.com . Ikuti bagian awal jawaban Objective-C saya (langkah 1-13, di bawah App Store Connect tajuk ) untuk instruksi melakukan hal itu.

Diperlukan beberapa jam bagi ID produk Anda untuk mendaftar di App Store Connect, jadi bersabarlah.

Sekarang setelah Anda mengatur informasi pembelian dalam aplikasi di App Store Connect, kami perlu menambahkan kerangka kerja Apple untuk pembelian dalam aplikasi, StoreKit dalam aplikasi,, ke aplikasi.

Pergilah ke proyek Xcode Anda, dan pergi ke manajer aplikasi (ikon seperti halaman biru di bagian atas bilah kiri di mana file aplikasi Anda berada). Klik aplikasi Anda di bawah target di sebelah kiri (itu harus menjadi pilihan pertama), lalu pergi ke "Kemampuan" di atas. Pada daftar, Anda akan melihat opsi "Pembelian Dalam Aplikasi". Nyalakan kemampuan ini, dan Xcode akan menambah StoreKitproyek Anda.

Coding

Sekarang, kita akan mulai coding!

Pertama, buat file cepat baru yang akan mengelola semua pembelian dalam aplikasi Anda. Saya akan menyebutnya IAPManager.swift.

Dalam file ini, kita akan membuat kelas baru, yang disebut IAPManageritu adalah SKProductsRequestDelegatedan SKPaymentTransactionObserver. Di bagian atas, pastikan Anda mengimpor FoundationdanStoreKit

import Foundation
import StoreKit

public class IAPManager: NSObject, SKProductsRequestDelegate,
                         SKPaymentTransactionObserver {
}

Selanjutnya, kita akan menambahkan variabel untuk menentukan pengidentifikasi untuk pembelian dalam aplikasi kami (Anda juga bisa menggunakan enum, yang akan lebih mudah dipertahankan jika Anda memiliki beberapa IAP).

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

Mari kita tambahkan inisialisasi untuk kelas kita selanjutnya:

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

Sekarang, kita akan menambahkan fungsi yang diperlukan untuk SKProductsRequestDelegatedan SKPaymentTransactionObserverberfungsi:

Kami akan menambahkan RemoveAdsManagerkelas nanti

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

Sekarang mari kita tambahkan beberapa fungsi yang dapat digunakan untuk memulai pembelian atau mengembalikan pembelian:

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

Selanjutnya, mari kita tambahkan kelas utilitas baru untuk mengelola IAP kami. Semua kode ini bisa berada dalam satu kelas, tetapi memiliki beberapa kode menjadikannya sedikit lebih bersih. Saya akan membuat kelas baru yang disebut RemoveAdsManager, dan di dalamnya, meletakkan beberapa fungsi

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

Pertama tiga fungsi, removeAds, restoreRemoveAds, dan areAdsRemoved, adalah fungsi yang Anda akan menelepon untuk melakukan tindakan tertentu. Empat yang terakhir adalah yang akan dipanggil olehIAPManager .

Mari kita tambahkan beberapa kode ke dua fungsi pertama, removeAdsdan 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()
}

Dan terakhir, mari kita tambahkan beberapa kode ke lima fungsi terakhir.

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

Menyatukan semuanya, kita mendapatkan sesuatu seperti ini:

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

}

Terakhir, Anda perlu menambahkan beberapa cara bagi pengguna untuk memulai pembelian dan menelepon RemoveAdsManager.removeAds()dan memulai pengembalian dan panggilanRemoveAdsManager.restoreRemoveAds() , seperti tombol di suatu tempat! Perlu diingat bahwa, sesuai pedoman App Store, Anda perlu memberikan tombol untuk mengembalikan pembelian di suatu tempat.

Mengirimkan untuk ditinjau

Hal terakhir yang harus dilakukan adalah mengirimkan IAP Anda untuk ditinjau di App Store Connect! Untuk petunjuk terperinci tentang melakukan hal itu, Anda dapat mengikuti bagian terakhir dari jawaban Objective-C saya , di bawah judul Pengiriman untuk ulasan .

Jojodmo
sumber
4

RMStore adalah perpustakaan iOS ringan untuk Pembelian Dalam Aplikasi. Itu membungkus API StoreKit dan memberi Anda blok praktis untuk permintaan asinkron. Membeli produk semudah memanggil satu metode.

Untuk pengguna tingkat lanjut, perpustakaan ini juga menyediakan verifikasi tanda terima, unduhan konten, dan kegigihan transaksi.

Vladimir Grigorov
sumber
-1

Saya tahu saya cukup terlambat untuk memposting ini, tetapi saya berbagi pengalaman yang sama ketika saya mempelajari tali model IAP.

Pembelian dalam aplikasi adalah salah satu alur kerja paling komprehensif di iOS yang diterapkan oleh kerangka kerja Storekit. The seluruh dokumentasi cukup jelas jika Anda kesabaran untuk membacanya, tapi agak maju dalam sifat teknis.

Untuk meringkas:

1 - Minta produk - gunakan kelas SKProductRequest & SKProductRequestDelegate untuk mengeluarkan permintaan ID Produk dan menerimanya kembali dari toko itunesconnect Anda sendiri.

Produk-produk SKP ini harus digunakan untuk mengisi UI toko Anda yang dapat digunakan pengguna untuk membeli produk tertentu.

2 - Masalah permintaan pembayaran - gunakan SKPayment & SKPaymentQueue untuk menambahkan pembayaran ke antrian transaksi.

3 - Memantau antrian transaksi untuk pembaruan status - gunakan metode updatedTransactions Protocol SKPaymentTransactionObserver untuk memantau status:

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

4 - Kembalikan aliran tombol - gunakan restore SKPaymentQueue'sTransletedTransactions untuk mencapai ini - langkah 3 akan mengurus sisanya, bersama dengan metode SKPaymentTransactionObserver berikut:

paymentQueueRestoreCompletedTransactionsFinished
restoreCompletedTransactionsFailedWithError

Berikut ini adalah tutorial langkah demi langkah (yang ditulis oleh saya sebagai hasil dari upaya saya sendiri untuk memahaminya) yang menjelaskannya. Pada akhirnya ini juga menyediakan contoh kode yang dapat Anda gunakan langsung.

Ini adalah satu lagi yang saya buat untuk menjelaskan hal-hal tertentu yang hanya bisa dijelaskan oleh teks dengan cara yang lebih baik.

Nirav Bhatt
sumber
21
StackOverflow adalah situs web untuk membantu orang lain, dan bukan untuk mencoba menghasilkan uang dari mereka. Anda harus menghapus tautan kedua ke terakhir, atau cukup memposting apa yang dilakukan dalam tutorial itu di sini, gratis.
Jojodmo
@Jojodmo dapatkah Anda membuktikan klaim Anda dengan pedoman apa pun oleh SO? Saya melihat banyak orang memasarkan SDK mereka sendiri (bahkan membayar satu) dengan disclaimer, yang menurut saya juga banyak hadir di sini.
Nirav Bhatt
12
Tidak ada pedoman untuk menentangnya, tetapi jika Anda di sini untuk menghasilkan uang, Anda mungkin di sini karena alasan yang salah. IMO, jawaban Anda tampaknya terfokus untuk membuat orang mendaftar dalam tutorial video Anda, dan tidak membantu orang lain
Jojodmo
3
Ini hanyalah gangguan.
durazno