In App Purchase Verify certificate problem - iphone

I am getting the response as below.
{"status":21002, "exception":"java.lang.NullPointerException"}
I am encoding the receipt using Base64 only.
I am testing it in sandbox.
What will be the problem? Can anyone help me?

hi all i got the solution
just send the receipt data after encoding into base 64 as json with key "receipt-data"

VIP Worth noting that 21002 is also the message you get back when trying to verify a transaction that was initiated via rooted iTunes hack software like the Urus app.
We verify all transactions server side, so are not device dependant, the only transactions that get the 21002 response are ALL not real payments.
an easy way to spot it to look at the transaction id returned Urus gives com.urus.iap.XXXXXXX (x being random numbers), other ones include returning the pack name as the completed transaction id, all very different to the id's you get from the various iTunes servers.
I can't recommend more that everyone verifies server-side in an environment you can control, test and prove, that cant be manipulated (unless your hacked). If this is not an option, then do not pay out on a 21002 response.
verifying in a java environment the json response for the 21002 will be:
json: {"status":21002,"exception":"java.lang.ClassCastException"}

We had the same problem - until we discovered that we didn't include the post body in the request and only sent an empty request.

The only option that work for me after 2 days of hitting my head against the wall:
Testing in the sandbox
Pay attention to this advice by apple
"Important Do not sign in with your test account in the Settings application."
Make sure on the test device you are signed out of the "test" apple store account before encoding to base 64.

Related

iTunes in-app purchase receipt verification - obscure-JSON

This question related to AppStore in-App Purchase Receipt Verification Issues
In short - receipt data from iTunes is encoded in strange kinda-JSON format without any specifications for it. The accepted answer states that there is no need to decode it, it must be just sent to iTunes.
In my case I want decode that data to ensure that bundle_id parameter is equal to my apps bundle_id.
https://buy.itunes.apple.com/verifyReceipt method treats receipt as valid even if it was not generated for my application - so if hacker will send valid receipt from other app to my server - he well succeed.
The question is in how to do such verification properly, that additional step was certainly not intended by iTunes developers (otherwise they would not used pseudo-JSON), but nevertheless I think that it is necessary for protection from such attacks.
Any thoughts on this?
Gill, you should consider the receipt data that you are validating opaque and not try to interpret it. Send it to Apple's verifyReceipt service and examine the response. It will be a non-strange JSON object. If the "status" field is 0 then Apple considers it to be a valid receipt but it is up to your server app to determine if it is being used in a valid context. To do that you can examine the "receipt" object where you'll find the bundle and product IDs among a lot of other things.
If you're determined to some pre-validation of the receipt data before sending to Apple you can do that as well with some extra effort. It is a base64 encoded JSON object that contains two other base64 encoded objects one of which is receipt data as described above. The problem with this approach is the data is not intended to be examined by Apple service users so would be subject to change without notice. It can be a useful technique for debugging however.
If you are using iOS7, you can do receipt validation including bundle_id verification using on-device methods. Take a look at-- iOS InApp Purchase Receipt Validation iOS 7 and A complete solution to LOCALLY validate an in-app receipts and bundle receipts on iOS 7. I'm assuming you are talking about on-device validation and not using your own server to handle validation.

Debugging a zero-transaction result from the transactions endpoint in customer data api

We use the https://financialdatafeed.platform.intuit.com/v1/accounts/account_id_goes_here/transactions endpoint on a recurring basis to fetch transactions for all of the accounts we sync. We've been using this stably for quite awhile now, across a wide variety of accounts spanning 100s of financial institutions. This works great.
However, occasionally we get a report from a user who claims that we're not receiving transactions that they know to exist. Our investigation protocol is as follows:
To ask the user if they see the transactions when they sign into their bank's web site directly
To ask them to confirm that the credentials they used on their bank's web site are precisely the ones that they entered when setting up credit card sync on our site
We then manually inspect the response body from the above mentioned URL, to make sure that the HTTPS response indicates HTTP 200 and has a non-error response body (our app catches these errors correctly, but if debugging mysteriously missing transactions, we inspect the response body visually).
We look to see whether we're successfully syncing transactions for any other user that relies on the same FI. If we are, we become confident that both the bank and Intuit APIs are well-behaved, and that the problem is on our end somehow.
We sometimes ask users to try the same FI in Mint, guessing that if it fails in Mint, that it might be a bank or FI issue.
Investigation steps 1-2-3-4-5 tease out the root cause of at least 99% of the times when a user emails us to say that we're not successfully receiving their transactions. However, the remaining 1% are the tricky ones.
Today I'm faced with a situation where a user sees the txns on their bank website, swears that they are using the same creds when adding the card to our site, the HTTP response from the endpoint is HTTP 200 but contains zero transactions, but yet when the user tries via Mint they successfully see transactions.
However, the particular FI (OnPoint Community Credit Union) is not one where I can do investigation step 4, because we have no other users that currently rely on that FI. Is it possible for someone at Intuit to check to see whether there is evidence that users relying on OnPoint Community Credit Union are currently, successfully, retrieving transactions from that particular FI?
Any other suggestions for how to further deduce whether the zero-transaction response is due to: (a) user error, (b) bank server responding incorrectly, (c) Intuit server responding incorrectly, vs (d) our app behaving incorrectly?
Can you please submit a support ticket to Intuit with the Account_ID that is missing the transactions so that we can diagnose the issue? The first place to start when diagnosing the issue is to look at the Agg_status_code to make sure that reflects a '0'. If we are unable to login due to invalid credentials or MFA might be a cause of the missing transactions. I can help diagnose though once a ticket is submitted.

Verify in app purchase receipt without external server?

I would like to verify an in-app purchase receipt by sending a JSON to buy.itunes.apple.com/verifyReceipt, but im not sure how. I can find lots of tutorials on how to do it with a php server, but I dont use an external server for my purchase. Help?
There are cases where validating receipts is an essential thing, but your case does not sound like one of those cases. In your case, you are getting receipts directly from StoreKit and using them within the app.
The main reason someone would have to have validation of receipts is if they are 'delivering' content to an application from an external source. In those cases, validating a receipt is essential because the receipt is coming from an untrusted source (the application could send any receipt it wanted to).
That being said, there is no reason you can't follow the exact process to validate local receipts as well (if desired).

Which URL should be used to verify in-App purchase receipts

I have completed an iPhone application which sells its subscription using IAP and, for that, I have took all the necessary steps and it's working fine.
I just need to know which URL I should used to verify receipts on the server side?
When I was developing the application and testing IAP I was using:
"https://sandbox.itunes.apple.com/verifyReceipt"
Now I have submitted my application and IAP for review, on which environment will Apple test my IAP? Do I need to change the URL to:
"https://buy.itunes.apple.com/verifyReceipt"
Look at Apple's technotes, go to "FAQ" and scroll down to number 15.
it says:
Use the sandbox url
https://sandbox.itunes.apple.com/verifyReceipt
while testing your application in the
sandbox and while your application is
in review.
Use the production url
https://buy.itunes.apple.com/verifyReceipt
once your application is live in the
App Store.
The way Mugunth Kumar did it in his MKStoreKit was:
#ifndef NDEBUG
#define kReceiptValidationURL #"https://sandbox.itunes.apple.com/verifyReceipt"
#else
#define kReceiptValidationURL #"https://buy.itunes.apple.com/verifyReceipt"
#endif
and that's always worked for me. I do that on my app side.
UPDATE:
But here is the actual correct answer from that same Tech Note TN2259 found here:
How do I verify my receipt (iOS)?
Always verify your receipt first with the production URL; proceed to verify
with the sandbox URL if you receive a 21007 status code. Following this
approach ensures that you do not have to switch between URLs while your
application is being tested or reviewed in the sandbox or is live in the
App Store.
Note: The 21007 status code indicates that this receipt is a sandbox receipt,
but it was sent to the production service for verification.
So the right way to do it is to always check the receipt at the live URL first, if you get back a status code of 21007, check it again against the sandbox url. Then you'll know if it was verified or not and if it was verified in the live server or in the sandbox. We send the receipt to our server and this check is done from there, which keeps the user from using a redirection exploit. Then our server sends a secret back to the app if it checks out and records the transaction in a database. Also from here we can track how many hack attempts are happening each day.
Please note that, as #Mark mentioned above, the documentation is incorrect or outdated - I filed a bug report to Apple.
The production URL is with https.
https://buy.itunes.apple.com/verifyReceipt.
You may verify the URL by just requesting it, it should return a JSON string like
{"status":21000}
Cost me two expedited reviews ;)

How can my server securely authenticate iPhone in-app purchase?

Look at Apple's diagram for the server purchase model.
In step #9, how can the server know that it is really talking with an iPhone that is entitled to the purchase, and that Eve is not performing a replay with a dishonestly obtained receipt?
The receipt may be valid, but that doesn't prove that the sender is the entitled party.
Is there any notion of a device certificate on the iPhone that can be used to sign the receipt?
Is there any way to bind the receipt to the device, or bind the receipt to both the iTunes account and to the device, so the server can validate?
Apple-Provided Vulnerable Approach
The server can authenticate a purchase by doing the following:
The iPhone application receives a transactionReceipt after the purchase. Have the iPhone base64 encode it (You can use this open-source addition to NSData) and send it to your server. (You could even send it as-is and have the server base64 encode it before validation.)
Have your server send a JSON request with the single key receipt-data with the base64 encoded transactionReceipt to https://buy.itunes.apple.com/verifyReceipt using an HTTP POST. (For directions on how to do this in various server-side languages see this site)
The server will respond with a JSON object with two keys: status which is an integer and receipt which is the receipt repeated.
If status is zero, the receipt is valid should be accepted, a non-zero value means the receipt isn't valid.
Secure Additions to Apple's Approach
However, there are a few security implications. A user could use another user's receipt since devices aren't tied to receipts, or a user could use another product's receipt since the server doesn't verify the product id of the receipt. To ensure this doesn't happen you should also do the following:
When you first get the receipt in the application, immediately send it to your server along with the device's UUID over a secure channel such as HTTPS or an SSL socket. Do not store it anywhere, leave it in memory.
On your server, store the UUID and receipt pair in a database.
When a device sends a UUID and receipt pair, verify with your database that the receipt has not already been used, and make sure the receipt is actually for your product by checking the receipt's product id. The receipt is just a JSON object, so your server can read the contents by decoding the receipt from base64.
Return a response to the device over the secure channel telling it whether the purchase is:
Authenticated as new (wasn't in DB and was valid)
Authenticated in the past (Same UUID and receipt pair was already in DB)
Denied due to wrong product id
Denied due to having already used the receipt with another UUID.
Since the receipt is only ever in memory on the device, and your application uses the device's UUID (can be spoofed by jailbroken devices, see comments), and all purchases of your product are logged with the device's UUID on your server in a secure manner; a user could not use another user's receipt to verify the purchase, nor could they use a receipt from another product, since you check for that.
You can also validate other fields from the receipt if you want to verify other details of the transaction. For example, if your product is a subscription, you'll like want to look at the transaction date as well.
Also, users cannot pretend to be your server by having the device on a private network with a host of the same name as yours, since they won't have your SSL certificate.
Failure Considerations
Since failure might occur between when the user's device gets the receipt and verifying it with your server (for example if the user looses connectivity, or your server is down for maintenance), you should also let the user "re-authorize". Re-authorizing should get the receipt from the store (using a Restored Transaction) and re-send it to the server just as though this was a new purchase. This should rarely need to be used, but should be available to save the user having to re-buy the product in the case of network failure.
Multiple Devices Consideration
This means that if a user wants to use an application on more than one device, they will have to purchase the product multiple times. This might be the desired effect, but you should likely inform your users before they purchase since they might expect to be able to use the content across devices they have associated with their account.
If the receipt also contains the iTunes account information, authentication could use that to allow users to share content between all their devices (but not their friends').
I do not think that can bind the receipt to the device.
My understanding is that you are allowed to install an application on multiple devices without extra cost. Binding it to the device would mean that if you for example upgrade/change your phone you would need to purchase all the apps again.
I believe if you can't read the user's apple ID, your only protection against piracy would be keeping track(server-side of course) of the number of download requests per transaction_id and limit those if they go over a certain value.
So if you limit it to say 50, that gives a reasonable margin for the user to deploy the app and it's contents on multiple devices and restore several times, but makes it hard for whoever wants to distribute a pirated version with a valid receipt for unlimited restores. Of course they can just distribute a version with all your content, but that's nothing you can do about that and at least they're not taxing your servers.
UDID Does Not Work Anymore
Beniot answer is great, however, these days, as mentioned by Joe D'Andrea, UDID is deprecated and the last time I tried, an App that used the call to get the UDID failed to pass validation during upload to iTunes.
Time-limited Receipts as Alternative to Receipt Counters
To add on to hloupyhonza's answer, besides having a "download request" counter for a particular receipt, you can just limit the receipt validity by time. I found anything between 12 to 24 hours reasonable.
This method also allows the purchaser to use the purchase on any other device he owns as long as he logs into the App Store with the same Apple ID. Note: Each time Restore Purchases is done, Apple returns a completely new receipt (with details of the original receipt contained) - this allows purchases to be restored past the time limit we set for a particular receipt.
Preventing "Off-The-Shelf" Hacks
To prevent typical "Googled" hacking solutions (my data shows this constitutes almost all of IAP hacking attempts), I use a checksum (pick your favorite algorithm, doesn't matter unless you want to make it watertight) of the following concatenation:
receipt-data json string
A shared secret key
Validation success status code.
The App will verify the checksum returned by our validation server. This is not watertight though, as the hacker may retrieve the shared key from your App's binary. But it has prevented all "off-the-shelf" hacks thus far and that's good enough for my use.