URL Endpoint
Accepts base64 binary App Store receipt data.
https://receiptverify.ikonetics.com/api
Encrypted URL Endpoint
Alternate endpoint which encrypts successful results before returning to the client application.
This option is only available with higher-tiered paid accounts. See the plan details page for more information.
https://receiptverify.ikonetics.com/apiencrypted
Post Attributes
- receipt
-
base64 string
Required. The base64 encoded iOS in-app purchase receipt.
- apikey
-
string
Required. Your account API access key is available on your account
screen. It is a unique identifier for your account. We use Google
to authenticate you and offer free accounts to all.
- token
-
string
Required. The identifier for one of the Apps configured on your account screen.
The receipt details will be compared to the bundle ID of the App as part of the validation process.
Returned JSON Properties
- receipt
-
Optional. The decoded receipt returned directly from
Apple. This field will not be returned when the result is an error.
Always check the status property.
- status
-
For all successful queries this will be 0.
If there are issues with your request this will contain one of the following status codes.
401 - Authentication is incorrect. Check the API key and App token matches your account.
403 - The data was processed, but failed validation checks and may be from a different app.
This could indicate an invalid receipt is being used to unlock in-app features.
500 - The server failed, which usually indicates an unexpected response from Apple. Perhaps try again later.
If Apple has an issue with the receipt provided you will receive one of Apple's status code. Apple's Receipt
Validation Programming Guide provides a full list of values.
- description
-
Human readable description of the status code. For example,
Apple's code 21002 indicates the binary receipt data you provided was
malformed or missing.
Encrypted Results
If you used the encrypted endpoint, successful results will be encrypted using the encryption key for your specific app.
The encrypted message is base64 encoded into a string before being returned.
Decryption is done by:
- base64 decode the encrypted message into a byte array
- read the first 16 bytes to use as the IV (initialization vector)
- read the remaining bytes to use as the encrypted payload (the receipt result)
- configure the decryption library with the IV and your app specific encryption key
- use the encryption key and IV to decrypt the encrypted payload
Sample JSON receipt result
A valid Apple receipt will be similar to the one below.
Yours will probably be much larger with more complex items.
{
"receipt": {
"receipt_type": "ProductionSandbox",
"bundle_id": "tld.ikonetics.AppName",
"request_date": "2014-04-30 15:05:55 Etc/GMT",
"original_purchase_date": "2014-04-30 15:05:55 Etc/GMT",
"original_purchase_date_pst": "2012-04-30 08:05:55 America/Los_Angeles",
"original_purchase_date_ms": "1335098354868",
"in_app": [
{
"quantity": "1",
"product_id": "tld.ikonetics.AppName.InAppFeature",
"transaction_id": "1000000336206221",
"original_transaction_id": "1000000336206221",
"purchase_date": "2017-09-20 14:43:40 Etc/GMT",
"purchase_date_ms": "1505918620000",
"purchase_date_pst": "2017-09-20 07:43:40 America/Los_Angeles",
"original_purchase_date": "2017-09-20 14:43:40 Etc/GMT",
"original_purchase_date_ms": "1505918620000",
"original_purchase_date_pst": "2017-09-20 07:43:40 America/Los_Angeles",
"is_trial_period": "false"
}
]
},
"status": 0,
"description": "Receipt valid"
}
The following JSON example shows the result returned when
supplying invalid attributes. Notice the error result does not
include the receipt.
{
"status": 401,
"description": "Authentication is incorrect.",
}
Example iOS Code
This code should be used when your App is responding to transaction
statuses, specifically after the SKPaymentTransactionStatePurchased
status. More information is available on Apple's In-App
Purchase Programming Guide .
The lines marked with TODO indicate areas where you
may want to add custom code to handle your specific situation.
// this code example checks a single app-wide receipt for all transactions
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];
if (!receipt) {
// TODO: invalid receipt, ask the app to refresh itself, stop and do error handling
[[[SKReceiptRefreshRequest alloc] initWithReceiptProperties:nil] start];
return;
}
// build the post body with your account api key and app specific token
NSMutableData *postBody = [NSMutableData data];
[postBody appendData: [@"apikey=act_unIquEAccOuntAPIkeySamPle}" dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData: [@"&token=app_SaMpLeAppToKen1" dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData: [@"&receipt=" dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData: [[receipt base64EncodedStringWithOptions:0] dataUsingEncoding:NSUTF8StringEncoding]];
NSString *bodyLength = [NSString stringWithFormat:@"%lu", (unsigned long)[postBody length]];
// setup the a POST request with the post body data
NSURL *apiURL = [NSURL URLWithString:@"https://receiptverify.ikonetics.com/api"];
NSMutableURLRequest *apiRequest = [NSMutableURLRequest requestWithURL:apiURL];
[apiRequest setHTTPMethod: @"POST"];
[apiRequest setValue: @"application/x-www-form-urlencoded" forHTTPHeaderField: @"Content-Type"];
[apiRequest setValue: bodyLength forHTTPHeaderField: @"Content-Length"];
[apiRequest setCachePolicy: NSURLRequestReloadIgnoringLocalCacheData];
[apiRequest setHTTPShouldHandleCookies: NO];
[apiRequest setHTTPBody: postBody];
// create a background session for connecting to the Receipt Verification service.
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *datatask = [session dataTaskWithRequest: apiRequest
completionHandler: ^(NSData *apiData
, NSURLResponse *apiResponse
, NSError *conxErr)
{
// background datatask completion block
NSError *parseErr;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData: apiData
options: 0
error: &parseErr];
// TODO: add error handling for conxErr, json parsing, and invalid http response statuscode
NSDictionary *receipt = [json objectForKey: @"receipt"];
NSArray <NSDictionary*> *lineitems = [receipt objectForKey: @"latest_receipt_info"];
if (nil == lineitems) {
lineitems = [receipt objectForKey: @"in_app"];
}
/* TODO: Unlock the In App purchases ...
At this point the json dictionary will contain the verified receipt from Apple
and each purchased item will be in the array of lineitems.
*/
}];
// start the background session datatask
[datatask resume];
After you receive the JSON receipt your code should perform some
simple checks to verify the JSON receipt from Apple matches your App
and your In-App products. For example you may want to compare the transaction's productIdentifier
to the product_id in the JSON receipt. Read the Apple documentation to learn other fields you
might want to verify before unlocking features.
Assuming you have the productIdentifier from an SKPaymentTransaction and
you have a method named unlockFeature, your code may
look something like this:
NSDictionary *latest = [json objectForKey:@"latest_receipt_info"];
for (NSDictionary *lineitem in latest) {
NSString *receiptItem = [lineitem objectForKey:@"product_id"];
if ([productIdentifier isEqualToString:receiptItem]) {
[self unlockFeature: lineitem ];
}
}