In-App Purchase – How to implement the payment transaction


In the first part of this tutorial we have seen how to setup products in iTunes Connect and in the second part we have seen how to retrieve them and present them in a table view. In this part we will implement code to allow the user to buy products. You can find the code in Github.

Our application displays a list of products and if you tap on any of them you go to a detail view where you can see the description for that product. We want to add a buy button that also displays the price for the product and implement the payment transaction. The SKProduct objects that we have retrieved from the App Store have lots of useful information like localised description, price and price locale. We’re going to use all this to achieve our goal.

Let’s go to the Main storyboard and add a button to the DetailViewController. Bring up the Assistant editor and add an IBOutlet to your DetailViewController:

buyButtonOutlet

We already have a configureView() method that unwraps the detailItem. At this point we can make some updates to our DetailViewController. We know that the detailItem is actually of SKProduct type so we can change the type of the property:

var detailItem: SKProduct? {
   didSet {
       // Update the view.
       configureView()
   }
}

We need to import StoreKit for everything to compile. We’re going to add code in configureView() to configure our buyButton.  Apple already suggests a way to format a product’s price in their In-App Purchase Programming Guide, so we’re going to use the same:

func configureView() {
    // Update the user interface for the detail item.
    if let product: SKProduct = self.detailItem {
       if let label = self.detailDescriptionLabel {
           label.text = product.localizedDescription
       }
       if let button = self.buyButton {
           configureBuyButton(product)
       }
    }
}
    
func configureBuyButton(product: SKProduct) {
   let numberFormatter = NSNumberFormatter()
        
   numberFormatter.formatterBehavior = .Behavior10_4
   numberFormatter.numberStyle = .CurrencyStyle
   numberFormatter.locale = product.priceLocale
        
   let formattedPrice = numberFormatter.stringFromNumber(product.price)
   let buttonTitle = String(format: " Buy for %@ ", formattedPrice!)
        
   buyButton.setTitle(buttonTitle, forState: .Normal)
}

All we’re doing here is constructing a number formatter, formatting the price and using it as part of the buyButton title. If you want your buy button to look a bit more like the ones in the App Store then let’s use a bit of Quartz magic and draw some borders:

buyButton.layer.borderWidth = 1.0
buyButton.layer.cornerRadius = 4.0
buyButton.layer.borderColor = buyButton.tintColor?.CGColor

The button looks pretty and displays the price, but it doesn’t do anything yet. Let’s add an action for when the button is pressed. Go to the Main.storyboard, bring up the Assistant editor and add an action for the buyButton:

buyButtonAction

When the user selects a product to buy, we need to create a payment request using that product object. We’ll add this product request to the payment queue. It will then be sent to the App Store to be processed and we will receive a status based on which we can decide the next step. If the payment was successful then we need to make available the product (the photos in our case). If something went wrong then we can show the error to the user. First let’s create the payment request and add it to the default payment queue:

@IBAction func buyPressed(sender: UIButton) {
    if let product = self.detailItem {
        let payment = SKPayment(product: product)
        SKPaymentQueue.defaultQueue().addPayment(payment)
    }
}

Adding the payment to the queue means that it is submitted to the App Store for processing. For every payment request that the app submits, it gets back a transaction that needs to be processed. In order to receive the transaction status you need to have a transaction queue observer. You can decide which class plays this role, but make sure that it conforms to the SKPaymentTransactionObserver protocol. We could use IAPHelper for this, however I prefer to create a new class called PaymentTransactionObserver and to have there all the code related to processing the status of the payment request. So go ahead and create this class and make it implement the SKPaymentTransactionObserver protocol.

NSObjectProtocolError

Because SKPaymentTransactionObserver inherits from NSObjectProtocol we either have to implement the methods required in NSObjectProtocol or inherit from NSObject (that way we’ll have the default implementation from the super class). We’re not planning on checking for equality between observers so there’s no point in implementing the required methods ourselves. Instead let’s make NSObject our super class. In Swift, the first class after the : is the super class and the following classes after the comma are protocols (multiple inheritance is not supported).

Before implementing the methods needed to conform to the SKPaymentTransactionObserver protocol let’s tell our app that this will be our observer. Go to the AppDelegate file and add a property for the transaction observer.

var transactionObserver: PaymentTransactionObserver!

In application:didFinishLaunchingWithOptions: initialise the transactionObserver and then add it to the default payment queue.

transactionObserver = PaymentTransactionObserver()
SKPaymentQueue.defaultQueue().addTransactionObserver(transactionObserver)

When the status of the payment transaction changes (e.g. when payment was successful), Store Kit calls the paymentQueue:updatedTransactions: method of our transaction observer. The transaction status tells you what action your app needs to perform. There are 5 possible transaction statuses:

  • SKPaymentTransactionStatePurchasing – transaction in-progress, will be followed by another call
  • SKPaymentTransactionStateDeferred – transaction deferred, will be followed by another call
  • SKPaymentTransactionStateFailed – transaction failed, the error property offers more details on the reason why
  • SKPaymentTransactionStatePurchased – transaction succeeded, you should make the product available to the user at this point
  • SKPaymentTransactionStateRestored – restore previously purchased product

The app should respond appropriately for each of these transaction states, either by updating the UI to tell the user what’s going on, or by providing the product that was purchased. We’re going to implement the first 4 states for now. We’re going to talk about restoring a transaction in a future post, so stay tuned.

Go to the PaymentTransactionObserver file and add the paymentQueue:updatedTransactions: method. We are receiving an array of transactions and we have to process all of them. Let’s make a for loop and inside it a switch statement that checks the transaction status:

func paymentQueue(queue: SKPaymentQueue!, updatedTransactions transactions: [AnyObject]!) {
        
    for transaction in transactions {
            
        if let trans = transaction as? SKPaymentTransaction {
            switch (trans.transactionState) {
            case .Purchasing:
                showTransactionAsInProgress(false)
            case .Deferred:
                showTransactionAsInProgress(true)
            case .Failed:
                failedTransaction(trans)
            case .Purchased:
                completeTransaction(trans)
            case .Restored:
                restoreTransaction(trans)
            }
        }
    }
}

I have added placeholder methods for each situation possible. Let’s start implementing these methods. First we want our app to show that a payment transaction is in progress. We’re going to replace the buy button with an activity indicator. This will prevent the user from tapping on the buy button multiple times (we do not want to have multiple payment transactions added to the queue). In order to let the view controller know that the status for the transaction has changed we will use the notification centre.

One Reply to “In-App Purchase – How to implement the payment transaction”

  1. Pingback: How to download content hosted on Apple's server | Mastering iOS

Leave a Reply

Your email address will not be published. Required fields are marked *

*