Everyone wants their app to make money, but what options are available if you don’t want to take the paid app path? Lets start with the different types of in app purchases you can create.

  • Consumable products. Items that get used up over the course of running your app. Examples include minutes for a Voice over IP app and one-time services such as voice transcription.
  • Non-consumable products. Items that remain available to the user indefinitely on all of the user’s devices. They’re made available to all of the user’s devices. Examples include content, such as books and game levels, and additional app functionality.
  • Auto-renewable subscriptions. Episodic content. Like non-consumable products, auto-renewable subscriptions remain available to the user indefinitely on all of the user’s devices. Unlike non-consumable products, auto-renewable subscriptions have an expiration date. You deliver new content regularly, and users get access to content published during the time period their subscription is active. When an auto-renewable subscription is about to expire, the system automatically renews it on the user’s behalf.
  • Non-renewable subscriptions. Subscriptions that don’t involve delivering episodic content. Examples include access to a database of historic photos or a collection of flight maps. It’s your app’s responsibility to make the subscription available on all of the user’s devices and to let users restore the purchase. This product type is often used when your users already have an account on your server that you can use to identify them when restoring content. Expiration and the duration of the subscription are also left to your app (or your server) to implement and enforce.
  • Free subscriptions. A way to put free subscription content in Newsstand. After a user signs up for a free subscription, the content is available on all devices associated with the user’s Apple ID. Free subscriptions don’t expire and can be offered only in Newsstand-enabled apps.

From: https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Chapters/Products.html#//apple_ref/doc/uid/TP40008267-CH2-SW2

Where are my Products?

Start by adding the StoreKit framework to your app and import StoreKit. Then make sure the class your working in conforms to the SKProductsRequestDelegate. Then add the delegate method which gives you an array of the products you created in ItunesConnect.

OMGZ, I’ve lost them all!

You also need to allow users to restore their purchases incase they are on a different device or delete the app. Restoring Purchases

I get a receipt right?

When you make a purchase, a receipt will show up inside your app’s container (along side the Documents and Library folders) inside a newly created folder called StoreKit.

So how do I get it?

So whats this so-called receipt look like?

Here is the parsed JSON of a sandbox receipt

It gives you a lot of information about the purchase made and which purchases have been made.

After getting the receipt you want to make sure that the bundle Id in the receipt matches that of your app Id, and confirm the product Ids in the receipt match the actual products you’ve created.

Its all secure right?

Well sorta. There are various different attacks against in app purchases. Apple suggests that you send the receipt to your own server (with your own encryption to protect the receipt) and then send the receipt to Apple from your server to verify it. Else, someone could smudge their device’s DNS settings or create their own man in the middle attack to capture the receipt and return their own success message to the app.

But with so many different types of attacks and the ability to jailbreak your device, its not easy to stop every single instance.

More can be read here

https://developer.apple.com/library/ios/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html#//apple_ref/doc/uid/TP40010573-CH104-SW1

4 thoughts on “iOS In-App Purchases

  1. Hi Scott. Thank you for a tutorial well made. I’ve run into a problem running your code in Swift 3 and Xcode 8.3.1. The code executes a warning in this function below and I can’t decipher it. Can you help?

    func verifyReceipt() {

    var response: URLResponse?
    var error: NSError?

    let receiptUrl = Bundle.main.appStoreReceiptURL
    let receipt = NSData(contentsOf: receiptUrl!)

    let receiptdata: NSString = receipt!.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0)) as NSString
    print(receiptdata)

    let request = NSMutableURLRequest(url: NSURL(string: “http://myprivatesecureURL/verifyReceipt.php”)! as URL)

    let session = URLSession.shared
    request.httpMethod = “POST”

    var err: NSError?

    request.httpBody = receiptdata.data(using: String.Encoding.ascii.rawValue)

    // ERROR HERE:
    // Invalid conversion from throwing function of type ‘(_, _, _) throws -> Void’ to non-throwing function type ‘(Data?, URLResponse?, Error?) -> Void’
    let task = session.dataTask(with: request as URLRequest, completionHandler: {data, response, error -> Void in
    let err: NSError?
    var json = try JSONSerialization.jsonObject(with: data!, options: .mutableLeaves) as? NSDictionary

    if(err != nil)
    {
    print(err!.localizedDescription)
    let jsonStr = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
    print(“Error could not parse JSON: ‘\(String(describing: jsonStr))'”)
    }
    else {
    if let parseJSON = json {
    print(“Receipt \(parseJSON)”)
    }
    else {
    let jsonStr = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
    print(“Receipt Error: \(String(describing: jsonStr))”)
    }
    }
    })

    task.resume()

    }

    1. Give this a shot,

      let task = session.dataTask(with: request as URLRequest, completionHandler: {(data:Data?, urlResponse: URLResponse?, error: Error?) -> Void in
      var json = try JSONSerialization.jsonObject(with: data!, options: .mutableLeaves) as? NSDictionary
      } as! (Data?, URLResponse?, Error?) -> Void

      1. Thanks Scott. But this throws another error:

        let task = session.dataTask(with: request as URLRequest, completionHandler: {(data:Data?, urlResponse: URLResponse?, error: Error?) -> Void in
        var json = try JSONSerialization.jsonObject(with: data!, options: .mutableLeaves) as? NSDictionary
        } as! (Data?, URLResponse?, Error?) -> Void

        if (err != nil) // ERROR: Expected ‘)’ in expression list
        {
        print(err!.localizedDescription)
        let jsonStr = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
        print(“Error could not parse JSON: ‘\(String(describing: jsonStr))'”)
        }
        else {
        if let parseJSON = json {
        print(“Receipt \(parseJSON)”)
        }
        else {
        let jsonStr = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
        print(“Receipt Error: \(String(describing: jsonStr))”)
        }
        }
        })

  2. Your almost there, you just need to move your code into the completion handler.

    let task = session.dataTask(with: request as URLRequest, completionHandler: {(data:Data?, urlResponse: URLResponse?, error: Error?) -> Void in
    //Parse Json
    var json = try JSONSerialization.jsonObject(with: data!, options: .mutableLeaves) as? NSDictionary
    if (err != nil)
    {
    print(err!.localizedDescription)
    let jsonStr = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
    print(“Error could not parse JSON: ‘\(String(describing: jsonStr))'”)
    }
    else
    {
    if let parseJSON = json {
    print(“Receipt \(parseJSON)”)
    }
    else {
    let jsonStr = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
    print(“Receipt Error: \(String(describing: jsonStr))”)
    }
    }
    //End Completion handler
    } as! (Data?, URLResponse?, Error?) -> Void)

Leave a Reply