Join Ron Buencamino for an in-depth discussion in this video Receipt validation, part of Implementing In-App Purchases in iOS 11 with StoreKit.
- [Instructor] When it comes to validating our receipt, I'm going to show you validation via the app store API that is meant for server to server verification. It's a good way to test and make sure our logic is sufficient. However, as a reminder, although we're showing this methodology here, it's not recommended that you upload your receipt to the app store API directly from your device. You should always use an intermediary server that you can verify for authenticity or use on device validation methods.
This is meant to be an example of how to use receipt data with an implementation that's easier to follow. You can apply the logic learned here towards your own server or implement an on device solution. So I'm going to start here in IAPManager.swift. I'm going to drop down to the bottom and we're going to mark off a section for a receipt validation. The first thing that I want to do is create a function called validateReceipt.
Inside of this function, I'm going to create a completion handler that we're going to call once all of our background processes are complete. So inside a validateReceipt, there's two things that I want to do. First, I want to find our app receipt, which is located in our main bundle. Once we find our receipt, then I want to call receipt validator to perform an action that's going to take the receipt and send it up to the app store API for a validation.
So let's go ahead and start by grabbing our receipt data. So for line 109, I'm guarding to see if I can get a URL for our app store receipt URL which is located in Bundle.main. Then, I try to create a receipt data object via the contents of our receiptURL.
And if I can't do both, we're going to break out of this and return early in our function and not proceed any further. But if we can create both of these, then it's time for us to call our receipt validator.
So what I'm doing here on line number 111 is I'm calling the receipt validator object and in particular, it has a function called validate receipt which takes in an argument of a receipt NSData object and it has a handler of its own. And when it's completed, we can expect to see bullion value to represent a success, an array for purchases if they exist, and any errors again if they do exist. Immediately though, I'm calling our completion handler and passing the success value onto it as to determine whether or not we had a successful receipt validation.
In here though, in handling this, what I want to do first is check against success. So we'll say if success. So if we have a successful receipt validation, I simply want to print out a statement right now that says validation was successful. And if we fail for whatever reason, we're going to go ahead and state that as well. Great. So, we're almost done here.
What we need to do now is flesh out what the validate receipt function does. So let's go ahead and jump to that. And I'm going to hold the command button, highlight validate receipt, and select jump to definition. As we can see here, we have an empty function that we're going to have to create. So let's talk about what we need to do. When we're sending up a request to the app store API, there are three things that needs to happen. First, we have to format our data into a valid JSON object.
After we have that JSON object, then we're going to create the request. And once we figure out that request, then we're going to create the data task that's going to execute our request for us. Inside of that data task is going to be where we handle what happens when we receive a response from the API. So let's go ahead and break this down step by step starting first with the formatting of our JSON data. So this is the first part and this is our formatting of our JSON data.
In line 29, we're using an NSString object to create a base64EncodedString of our receipt data. In line 31, I'm creating a dictionary that has a key for receipt data and for its value is our base64EncodedString. Something we want to make sure here is that your key is correct. The API is expecting to read something called receipt-data, so you need to make sure that this is there. Then on line 32, we're calling JSONSerialization to try to serialize our objects so that we can use it in a request.
Next, it's time for us to build our request. So let's go ahead and take care of that. Alright. Let's take a look at what I quickly did over here. Starting on line 34, we're going to create our Mutable URL Request. And for the URL string, I'm passing in the URL for the sandbox receipt verification endpoint. On line 35, we're setting our http method of the request to be post. And on line 36, we're setting the body to be the serialized JSON data that we created.
Line 38, I grab a pointer to our shared URLSession and in line number 40, I'm creating a data task from our session passing in our request as its parameter. And we're given a callback block that we're going to fill in shortly. On line 44, I'm calling task.resume so that we can execute this command. So the last thing we need to do here is handle the response that is given to us from the app store API. So what I want to do first is guard to make sure that we don't receive any errors.
So I'm going to say guard error = nil. And then, we're going to handle something else if in fact there is an error instead. So, we're going to go ahead and put in return, but we need to do something more than that. We're going to call our handler and we're going to state that it didn't occur. So we're going to say false, nil, and we're going to pass in our error.
Now that we've guarded against our error case, what we want to do is take the data that was given to us in a response and we want to deserialize that so we can work with the underlying objects. So, I'm going to write out some deserialization for us and we're going to go over it. Okay, taking a look at what I did over here for the deserialization. In line 47, I open up my do clause. In line 48, I try to serialize the JSON object that was returned to us from the API.
In line 49, I'm looking for a key in the object called status. And this is the status that's given to us from the API that determines whether or not the receipt that we sent them was in a good format or if there's some information that was missing. So if you get a status of zero, that means what you sent them was a valid receipt and you're going to get back a good response. But if you don't get a zero and you get some other code, then you need to handle that. So for now, in the else clause I put invalid receipt code and I called the handler and returned the false status.
So, given that I have both of these items already in here, we see that we are handling the receipt validation to the API as expected. We can now go ahead and test this out. But before we do, let's go ahead and jump back over to IAPManager and verify that we're doing what we need to. So first, I have a function here for validateReceipt and when that's called, we try to get the receipt data from the bundle. If we get that, then we call our receipt validator class so that we can send it up to the iTunes store, so that we can verify it with them.
And if everything works out, we're going to get a successful callback and we're going to be able to get a printout that says validation was successful. But if not, we're going to get an error that's going to say validation was not successful. You want to make sure that you're calling your completion handler over here because the originating function is going to be expecting that. And that's going to happen over here in payment queue updated transactions. So right now for the case of purchased, we're just immediately finishing the transaction.
And I don't want to do that. What I want to do instead is I want to call self.validateReceipt and if it's successful, we're going to perform an action and if it isn't, we're going to perform another action. So now that I have this in place, let's go ahead and run this on our device. Whoops, typo. Okay. Let's go ahead now and run it on our device and make sure that it all works.
Alright, so we're in our app. We're going to go to the store. And we're going to try to purchase with a single credit. Okay, so it's going to confirm for us that we want to make this purchase. So I'm going to go ahead and say buy. And we see here that we're all set. I can press OK in the app and take a look at Xcode. And what happens is that we see that we successfully went into purchase status. We have a valid receipt because it printed it out for us. And we saw that the validation was successful, which is what we were expecting to see from our validateReceipt function.
So now that we have that, we think we're in a good place. However, that's not all. Let's move over to our next lesson where we're going to talk about parsing the receipt and making use of the different pieces of data that come along with it.
First, get an overview of StoreKit and the types of purchases you can offer, including consumable and non-consumable products and non-renewing and auto-renewable subscriptions. Next, learn how to configure new products for sale with iTunes Connect and display a storefront right within your app. Ron then shows how to request and process payments, and deliver users' purchases to their iOS devices. Plus, learn about restoring purchased content—a smart strategy for keeping your app rating high—and requesting reviews from users using the new SKStoreReviewController API, which doesn't require leaving the app.
- What is StoreKit?
- Creating products in iTunes Connect
- Displaying products in your storefront
- Requesting payments
- Receipt validation
- Delivering products
- Restoring purchased content
- Asking for app reviews