in-app purchase in Swift with a single product

25,014

Solution 1

Step 0: In your iTunes Connect account, create an In-App purchase.

For Single purchases, use this method:

  1. Import
import StoreKit
  1. Conform to StoreKit Delegate
class YOURViewController: UIViewController, SKProductsRequestDelegate, SKPaymentTransactionObserver {
  1. Use a user default to track transactions
let defaults = NSUserDefaults.standardUserDefaults()
  1. Product ID. This one will be same as in your iTunes Connect in app purchase
var product_id: NSString?

override func viewDidLoad() {        
    product_id = "YOUR_PRODUCT_ID"
    super.viewDidLoad()
    SKPaymentQueue.defaultQueue().addTransactionObserver(self)

    //Check if product is purchased

    if (defaults.boolForKey("purchased")){  
       // Hide a view or show content depends on your requirement
       overlayView.hidden = true     
    } else if (!defaults.boolForKey("stonerPurchased")) {
        print("false")            
    }
}
  1. Unlock Content. This is button action which will initialize purchase
@IBAction func unlockAction(sender: AnyObject) {

   print("About to fetch the products")

   // We check that we are allow to make the purchase.
   if (SKPaymentQueue.canMakePayments()) {
        var productID:NSSet = NSSet(object: self.product_id!);
        var productsRequest:SKProductsRequest = SKProductsRequest(productIdentifiers: productID);
        productsRequest.delegate = self;
        productsRequest.start();
        println("Fetching Products");
    } else {
        print("can't make purchases");
    }    
}
  1. Helper Methods
func buyProduct(product: SKProduct) {
    println("Sending the Payment Request to Apple");
    var payment = SKPayment(product: product)
    SKPaymentQueue.defaultQueue().addPayment(payment);
}
  1. Delegate Methods for IAP
func productsRequest (request: SKProductsRequest, didReceiveResponse response: SKProductsResponse) {

    var count : Int = response.products.count
    if (count>0) {
        var validProducts = response.products
        var validProduct: SKProduct = response.products[0] as SKProduct
        if (validProduct.productIdentifier == self.product_id) {
            print(validProduct.localizedTitle)
            print(validProduct.localizedDescription)
            print(validProduct.price)
            buyProduct(validProduct);
        } else {
            print(validProduct.productIdentifier)
        }
    } else {
        print("nothing")
    }
}    

func request(request: SKRequest!, didFailWithError error: NSError!) {
    print("Error Fetching product information");
}

func paymentQueue(queue: SKPaymentQueue!, updatedTransactions transactions: [AnyObject]!)    {
    print("Received Payment Transaction Response from Apple");

    for transaction:AnyObject in transactions {
        if let trans:SKPaymentTransaction = transaction as? SKPaymentTransaction{
            switch trans.transactionState {
            case .Purchased:
                print("Product Purchased");
                SKPaymentQueue.defaultQueue().finishTransaction(transaction as SKPaymentTransaction)
                defaults.setBool(true , forKey: "purchased")
                overlayView.hidden = true
                break;
            case .Failed:
                print("Purchased Failed");
                SKPaymentQueue.defaultQueue().finishTransaction(transaction as SKPaymentTransaction)
                break;
            case .Restored:
                print("Already Purchased");
                SKPaymentQueue.defaultQueue().restoreCompletedTransactions()    
            default:
                break;
            }
        }
    }        
}

Swift > 3.0

import StoreKit
class YOURVIEWController:UIViewController, SKProductsRequestDelegate, SKPaymentTransactionObserver {

let product_id: NSString = "com.some.inappid" // <!-- Change it to your inapp id

In your viewDidLoad add

override func viewDidLoad() {
    super.viewDidLoad()
    SKPaymentQueue.default().add(self)

In your buy button action

@IBAction func buyNowAction(_ sender: UIButton) {


    if (SKPaymentQueue.canMakePayments()) {
        let productID:NSSet = NSSet(array: [self.product_id as NSString]);
        let productsRequest:SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>);
        productsRequest.delegate = self;
        productsRequest.start();
        print("Fetching Products");
    } else {
        print("can't make purchases");
    }
}

In Your Restore button action

// MARK: - Restore In App Purchase
    @IBAction func restoreAction(_ sender: UIButton) {

    if (SKPaymentQueue.canMakePayments()) {
        SKPaymentQueue.default().add(self)
        SKPaymentQueue.default().restoreCompletedTransactions()
    } else {
        // show error
    }

}

Add Delegates:

// SKProductRequest Delegate

func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {

    print(response.products)
    let count : Int = response.products.count
    if (count>0) {

        let validProduct: SKProduct = response.products[0] as SKProduct
        if (validProduct.productIdentifier == self.product_id as String) {
            print(validProduct.localizedTitle)
            print(validProduct.localizedDescription)
            print(validProduct.price)
            self.buyProduct(product: validProduct)
        } else {
            print(validProduct.productIdentifier)
        }
    } else {
        print("nothing")
    }
}

func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
    for transaction:AnyObject in transactions {
        if let trans:SKPaymentTransaction = transaction as? SKPaymentTransaction{

            self.dismissPurchaseBtn.isEnabled = true
            self.restorePurchaseBtn.isEnabled = true
            self.buyNowBtn.isEnabled = true

            switch trans.transactionState {
            case .purchased:
                print("Product Purchased")
                //Do unlocking etc stuff here in case of new purchase
                SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)

                break;
            case .failed:
                print("Purchased Failed");
                SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
                break;
            case .restored:
                print("Already Purchased")
                //Do unlocking etc stuff here in case of restor

                SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
            default:
                break;
            }
        }
    }
}


//If an error occurs, the code will go to this function
    func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: Error) {
        // Show some alert
    }

Solution 2

In app Purchas delegate Method :

func buyInApp()
{
    if (SKPaymentQueue.canMakePayments())
    {
        let productID:NSSet = NSSet(object: "com.valueedge.plane_post");
        let productsRequest:SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>);
        productsRequest.delegate = self;
        productsRequest.start();
    }
    else
    {
        MBProgressHUD.hideHUDForView(self.view, animated: true)
    }
}

func productsRequest (request: SKProductsRequest, didReceiveResponse response: SKProductsResponse)
{
    let count : Int = response.products.count
    if (count>0)
    {
        let validProduct: SKProduct = response.products[0] as SKProduct

        if (validProduct.productIdentifier == "com.valueedge.plane_post")
        {
            buyProduct(validProduct);
        }
        else
        {
            MBProgressHUD.hideHUDForView(self.view, animated: true)
        }
    }
    else
    {
        MBProgressHUD.hideHUDForView(self.view, animated: true)
    }
}

func buyProduct(product: SKProduct)
{
    let payment = SKPayment(product: product)
    SKPaymentQueue.defaultQueue().addPayment(payment)
    SKPaymentQueue.defaultQueue().addTransactionObserver(self)
}

func paymentQueue(queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction])
{
    for transaction:AnyObject in transactions
    {
        if let trans:SKPaymentTransaction = transaction as? SKPaymentTransaction
        {
            switch trans.transactionState
            {
            case .Purchased:
                self.PaymentSuccess("Apple", details: "Apple(In-App)")
                print("In App Payment Success")
                SKPaymentQueue.defaultQueue().finishTransaction(transaction as! SKPaymentTransaction)
                MBProgressHUD.hideHUDForView(self.view, animated: true)
                break
            case .Failed:
                MBProgressHUD.hideHUDForView(self.view, animated: true)
                SKPaymentQueue.defaultQueue().finishTransaction(transaction as! SKPaymentTransaction)

                let alert = UIAlertController(title: "Planes Only", message: "Payment failed", preferredStyle: .Alert)
                alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: {(action:UIAlertAction!) in
                }))
                self.presentViewController(alert, animated: true, completion: nil)

                break
            case .Restored :
                SKPaymentQueue.defaultQueue().finishTransaction(transaction as! SKPaymentTransaction)
                MBProgressHUD.hideHUDForView(self.view, animated: true)
                break
            default:
                MBProgressHUD.hideHUDForView(self.view, animated: true)
                break
            }
        }
    }
}

func restore()
{
    SKPaymentQueue.defaultQueue().addTransactionObserver(self)
    SKPaymentQueue.defaultQueue().restoreCompletedTransactions()
    MBProgressHUD.showHUDAddedTo(self.view, animated: true)
}

func paymentQueueRestoreCompletedTransactionsFinished(queue: SKPaymentQueue)
{
    for transact:SKPaymentTransaction in queue.transactions
    {
        if transact.transactionState == SKPaymentTransactionState.Restored
        {
            //let t: SKPaymentTransaction = transact as SKPaymentTransaction
            //let prodID = t.payment.productIdentifier as String
            //restore prodID
            SKPaymentQueue .defaultQueue().finishTransaction(transact)
            MBProgressHUD.hideHUDForView(self.view, animated: true)
        }
    }
}

func paymentQueue(queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: NSError)
{
    for transaction:SKPaymentTransaction  in queue.transactions
    {
        if transaction.transactionState == SKPaymentTransactionState.Restored
        {
            SKPaymentQueue.defaultQueue().finishTransaction(transaction)
            MBProgressHUD.hideHUDForView(self.view, animated: true)
            break
        }
    }
    MBProgressHUD.hideHUDForView(self.view, animated: true)
}

func request(request: SKRequest, didFailWithError error: NSError)
{
    MBProgressHUD.hideHUDForView(self.view, animated: true)
}

In app button Clicked event :

@IBAction func In_App_Payment_Clicked(sender: AnyObject)
{
    MBProgressHUD.showHUDAddedTo(self.view, animated: true)
    self.buyInApp()
}

Solution 3

For development in app purchase app even in simple way you need to resolve next subtasks which not relate to coding:

    • Register as developer and creating an App ID under https://developer.apple.com/account/
    • select Certificates, IDs & Profiles.
    • select Identifiers > App IDs, and click + to create a new App ID.
    • make sure that In-App Purchase is enabled for your App ID.
    • Fill out the agreement and bank info. For that login to https://itunesconnect.apple.com/
    • Important step - double check the Agreements, Tax, and Banking section. All of them should be filled out. In other case in app purchase will not work. You will get failed response from the server.
    • Creating an App in iTunes Connect. For that just click to My Apps button under https://itunesconnect.apple.com/
    • Creating In-App Purchase Products. In your case Consumable or non Consumable. For managing Consumable or non Consumable code will be similar.
  1. Create sandbox user or users. Creating an App in iTunes Connect. For that just go to https://itunesconnect.apple.com/ click to select Users and Roles button, then click the Sandbox Testers tab. Create the user-> and go to the test email and confirm user from email!
  2. For starting the development and checking the connection between itunesconnect and your app you can use simulator. But when you will try to buy the product you need to use real device.
  3. Mandatory for ios app with in app purchase to have not only buy button the "restore purchases" button should be developed too. (for example in Android app it is not mandatory, Android app check it in different way). In case if user remove his app and install it again he want to restore already purchases items. For that "restore purchases" should be developed.
Share:
25,014
angeant
Author by

angeant

Updated on February 04, 2020

Comments

  • angeant
    angeant over 4 years

    Is there a simple way to implement a in-app purchase in swift for a single product?

    I want a button that triggers the in-app purchase like a [ad-removal] or [unlock-premium-content]

    I can't understand the full logic of it.

    I'm trying to follow and translate this tutorial from [Techotopia] http://www.techotopia.com/index.php/An_iOS_7_In-App_Purchase_Tutorial

    But It's my first time with the StoreKit Framework, and also with Swift.

    I just want to know the logic of the in-app purchase transaction with the Swift StoreKit Framework.

    Thanks!