`Programming Guide
`
`
`
`Contents
`
`About Receipt Validation 5
`At a Glance 5
`Validating Receipts Locally 5
`Validating Receipts With the App Store 5
`
`Validating Receipts Locally 6
`Locate and Parse the Receipt 6
`Compute the Hash of the GUID 8
`Validate the Receipt 9
`Respond to Receipt Validation Failure 9
`Exit If Validation Fails in OS X 10
`Refresh the Receipt If Validation Fails in iOS 10
`Set a Minimum System Version for Mac Apps 10
`Don’t Localize Your Version Number 10
`Protect Your Validation Check 10
`Test During the Development Process 11
`Validate In-App Purchases 11
`Implementation Tips 13
`Get the GUID in OS X 13
`Parse the Receipt and Verify Its Signature 15
`
`Validating Receipts With the App Store 19
`Verifying an App Receipt 19
`Verifying an Auto-Renewable Subscription Receipt 20
`
`Receipt Fields 22
`App Receipt Fields 22
`Bundle Identifier 22
`App Version 22
`Opaque Value 23
`SHA-1 Hash 23
`In-App Purchase Receipt 23
`Original Application Version 23
`Receipt Expiration Date 24
`
`2013-09-18 | Copyright © 2013 Apple Inc. All Rights Reserved.
`
`2
`
`
`
`Contents
`
`In-App Purchase Receipt Fields 24
`Quantity 24
`Product Identifier 25
`Transaction Identifier 25
`Original Transaction Identifier 25
`Purchase Date 26
`Original Purchase Date 26
`Subscription Expiration Date 26
`Cancellation Date 27
`App Item ID 27
`External Version Identifier 27
`Web Order Line Item ID 28
`
`Document Revision History 29
`
`2013-09-18 | Copyright © 2013 Apple Inc. All Rights Reserved.
`
`3
`
`
`
`Figures, Tables, and Listings
`
`Validating Receipts Locally 6
`Figure 1-1
`Structure of a receipt 7
`Listing 1-1
`ASN.1 definition of the payload format 8
`Listing 1-2
`ASN.1 definition of the in-app purchase receipt format 12
`Listing 1-3
`Get the computer’s GUID 13
`Listing 1-4
`Verify the signature using OpenSSL 15
`Listing 1-5
`Parse the payload using asn1c 16
`Listing 1-6
`Extract the receipt attributes 17
`Listing 1-7
`Compute the hash of the GUID 17
`
`Validating Receipts With the App Store 19
`Table 2-1
`Status codes for auto-renewable subscriptions 21
`
`2013-09-18 | Copyright © 2013 Apple Inc. All Rights Reserved.
`
`4
`
`
`
`About Receipt Validation
`
`Note: This book was previously titled Validating Mac App Store Receipts .
`
`The receipt for an application or in-app purchase is a record of the sale of the application and of any in-app
`purchases made from within the application. You can add receipt validation code to your application to prevent
`unauthorized copies of your application from running. Refer to the license agreement and the review guidelines
`for specific information about what your application may and may not do to implement copy protection.
`
`Receipt validation requires an understanding of cryptography and a variety of secure coding techniques. It's
`important that you employ a solution that is unique to your application.
`
`At a Glance
`There are two ways to validate receipts: locally and with the App Store. Compare both approaches and determine
`which is a better fit for your app and your infrastructure. You can also choose to implement both approaches.
`
`Validating Receipts Locally
`Validating locally requires code to read and validate a PKCS #7 signature, and code to parse and validate the
`signed payload.
`
`Relevant Chapters: “Validating Receipts Locally” (page 6), “Receipt Fields” (page 22)
`
`Validating Receipts With the App Store
`Validating with the App Store requires a secure connection between your app and your server, and code on
`your server to to validate the receipt with the App Store.
`
`Relevant Chapters: “Validating Receipts With the App Store” (page 19), “Receipt Fields” (page 22)
`
`2013-09-18 | Copyright © 2013 Apple Inc. All Rights Reserved.
`
`5
`
`
`
`Validating Receipts Locally
`
`Perform receipt validation immediately after your app is launched, before displaying any user interface or
`spawning any child processes. Implement this check in the main function, before the NSApplicationMain
`function is called. For additional security, you may repeat this check periodically while your application is
`running.
`
`Locate and Parse the Receipt
`When an application is installed from the App Store, it contains an application receipt that is cryptographically
`signed, ensuring that only Apple can create valid receipts. The receipt is stored inside the application bundle.
`Call the appStoreReceiptURL method of the NSBundle class to locate the receipt.
`
`2013-09-18 | Copyright © 2013 Apple Inc. All Rights Reserved.
`
`6
`
`
`
`Validating Receipts Locally
`Locate and Parse the Receipt
`
`Note: In OS X, if the appStoreReceiptURL method is not available (on older systems), you can
`fall back to a hardcoded path. The receipt’s path is /Contents/_MASReceipt/receipt inside the
`app bundle.
`In iOS, if the appStoreReceiptURL method is not available (on older systems), you can fall back
`to validating the transactionReceipt property of an SKPaymentTransaction object with the
`App Store. For details, see “Validating Receipts With the App Store” (page 19).
`
`The receipt is a binary file with the structure shown in Figure 1-1.
`
`Figure 1-1
`
`Structure of a receipt
`
`The outermost portion (labeled Receipt in the figure) is a PKCS #7 container, as defined by RFC 2315, with its
`payload encoded using ASN.1 (Abstract Syntax Notation One), as defined by ITU-T X.690. The payload is
`composed of a set of receipt attributes. Each receipt attribute contains a type, a version, and a value.
`
`2013-09-18 | Copyright © 2013 Apple Inc. All Rights Reserved.
`
`7
`
`Receipt
`Payload
`Attribute
`
`Attribute
`
`Certificate chain
`
`Signature
`
`...
`
`In-App Purchase Receipt
`
`...
`
`Attribute
`
`...
`
`
`
`Validating Receipts Locally
`Compute the Hash of the GUID
`
`The structure of the payload is defined using ASN.1 notation in Listing 1-1. You can use this definition with
`the asn1c tool to generate data type declarations and functions for decoding the payload, rather than writing
`that part of your code by hand. You may need to install asn1c first; it is available through MacPorts and
`SourceForge.
`
`For information about keys found in a receipt, see “Receipt Fields” (page 22).
`
`To generate the code, save the payload description shown in Listing 1-1 to a file and, in Terminal, run the
`following command:
`
`asn1c -fnative-types filename
`
`After the asn1c tool finishes generating files in the current directory, add the files it generated to your Xcode
`project.
`
`Listing 1-1
`
`ASN.1 definition of the payload format
`
`ReceiptModule DEFINITIONS ::=
`BEGIN
`
`ReceiptAttribute ::= SEQUENCE {
`type
`INTEGER,
`version INTEGER,
`value
`OCTET STRING
`
`} P
`
`ayload ::= SET OF ReceiptAttribute
`
`END
`
`Compute the Hash of the GUID
`In OS X, use the method described in “Get the GUID on OS X” (page 13) to fetch the computer’s GUID.
`
`In iOS, use the value returned by the identifierForVendor property of UIDevice as the computer’s GUID.
`
`2013-09-18 | Copyright © 2013 Apple Inc. All Rights Reserved.
`
`8
`
`
`
`Validating Receipts Locally
`Validate the Receipt
`
`To compute the hash, first concatenate the GUID value with the opaque value (the attribute of type 4) and the
`bundle identifier. Use the raw bytes from the receipt without performing any UTF-8 string interpretation or
`normalization. Then compute the SHA-1 hash of this concatenated series of bytes.
`
`Validate the Receipt
`To validate the receipt, perform the following tests, in order:
`
`1.
`
`Locate the receipt.
`
`If no receipt is present, validation fails.
`
`2.
`
`Verify that the receipt is properly signed by Apple.
`
`If it is not signed by Apple, validation fails.
`
`3.
`
`4.
`
`Verify that the bundle identifier in the receipt matches a hard-coded constant containing the
`CFBundleIdentifier value you expect in the Info.plist file.
`
`If they do not match, validation fails.
`
`Verify that the version identifier string in the receipt matches a hard-coded constant containing the
`CFBundleShortVersionString value you expect in the Info.plist file.
`
`If they do not match, validation fails.
`
`5.
`
`Compute the hash of the GUID as described in “Compute the Hash of the GUID” (page 8).
`
`If the result does not match the hash in the receipt, validation fails.
`
`If all of the tests pass, validation passes.
`
`Note: Bundle identifiers and version identifier strings are UTF-8 strings, not just a series of bytes.
`Make sure you code your comparison logic accordingly.
`
`If your app supports the Volume Purchase Program, check the receipt’s expiration date.
`
`Respond to Receipt Validation Failure
`Validation can fail for a variety of reasons. For example, when users copy your application from one Mac to
`another, the GUID no longer matches, causing receipt validation to fail.
`
`2013-09-18 | Copyright © 2013 Apple Inc. All Rights Reserved.
`
`9
`
`
`
`Validating Receipts Locally
`Set a Minimum System Version for Mac Apps
`
`Exit If Validation Fails in OS X
`If validation fails in OS X, call exit with a status of 173. This exit status notifies the system that your application
`has determined that its receipt is invalid. At this point, the system attempts to obtain a valid receipt and may
`prompt for the user’s iTunes credentials.
`
`If the system successfully obtains a valid receipt, it relaunches the application. Otherwise, it displays an error
`message to the user, explaining the problem.
`
`Do not display any error message to the user if validation fails. The system is responsible for trying to obtain
`a valid receipt or informing the user that the receipt is not valid.
`
`Refresh the Receipt If Validation Fails in iOS
`If validation fails in iOS, use the SKReceiptRefreshRequest class to refresh the receipt.
`
`Do not try to terminate the app. At your option, you may give the user a grace period or restrict functionality
`inside your app.
`
`Set a Minimum System Version for Mac Apps
`Include the LSMinimumSystemVersion key with a value of 10.6.6 or greater in your application’s Info.plist
`file. If receipt validation fails on versions of OS X earlier than 10.6.6, your application quits immediately after
`launch with no explanation to the user. Earlier versions of OS X do not interpret the exit status of 173, so they
`do not try to obtain a valid receipt or display any error message.
`
`Don’t Localize Your Version Number
`If your application is localized, the CFBundleShortVersionString key should not appear in any of your
`application’s InfoPlist.strings files. The unlocalized value from your Info.plist file is stored in the
`receipt—attempting to localize the value for this key can cause receipt validation to fail.
`
`Protect Your Validation Check
`An attacker may try to circumvent the validation code by patching your application binary or altering the basic
`operating system routines that the validation code depends upon. Resilience against these types of attacks
`requires a variety of coding techniques, including the following:
`
` ●
`
`Inline the code for cryptographic checks instead of using the APIs provided by the system.
`
`2013-09-18 | Copyright © 2013 Apple Inc. All Rights Reserved.
`
`10
`
`
`
`Validating Receipts Locally
`Test During the Development Process
`
` ● Avoid simple code constructions that provide a trivial target for patching the application binary.
`
`For example, avoid writing code like the following:
`
`if (failedValidation) {
`exit(173);
`
`}
`
` ●
`
`Implement code robustness techniques, such as obfuscation.
`
`If multiple applications use the same code for performing validation, this common code signature can be
`targeted by tools that patch application binaries.
`Ensure that, even if the exit function fails to terminate your application, your application stops running.
`
` ●
`
`Test During the Development Process
`In order to test your main application during the development process, you need a valid receipt so that your
`application launches. To set this up, do the following:
`
`1. Make sure you have Internet access so you can connect to Apple’s servers.
`
`2.
`
`Launch your application by double-clicking on it (or in some way cause Launch Services to launch it).
`
`After you launch your application, the following occurs:
`
` ●
`
` ●
`
` ●
`
`Your application fails to validate its receipt because there is no receipt present, and it exits with a status
`of 173.
`
`The system interprets the exit status and attempts to obtain a valid receipt. Assuming your application
`signing certificate is valid, the system installs a valid receipt for the application. The system may prompt
`you for your iTunes credentials.
`
`The system relaunches your application, and your application successfully validates the receipt.
`
`With this development receipt installed, you can launch your application by any method—for example, with
`gdb or the Xcode debugger.
`
`Validate In-App Purchases
`To validate an in-app purchase, your application performs the following tests, in order:
`
`1.
`
`Parse and validate the application’s receipt, as described in the previous sections.
`
`2013-09-18 | Copyright © 2013 Apple Inc. All Rights Reserved.
`
`11
`
`
`
`Validating Receipts Locally
`Validate In-App Purchases
`
`If the receipt is not valid, none of the in-app purchases are valid.
`
`2.
`
`Parse the in-app purchase receipts (the values for the attributes of type 17).
`
`Each in-app purchase receipt consists of a set of attributes, like the application’s receipt does. The structure
`for these receipts is defined in Listing 1-2 (page 12). As when parsing the receipt, you can generate some
`of your code from the ASN.1 description using the asn1c tool. Ignore all attributes with types that do not
`appear in the table—they are reserved for use by the system and their contents may change at any time.
`
`For information about the fields in a receipt, see “Receipt Fields” (page 22).
`
`3.
`
`Compare the product identifier in question to the product identifier of each in-app purchase receipt.
`
`If there is a receipt that matches, validation succeeds. Otherwise, validation fails.
`
`When validation succeeds, your application enables the purchased functionality—for example, by downloading
`content or adding features. When validation fails, your application simply does not enable the functionality.
`
`Listing 1-2
`
`ASN.1 definition of the in-app purchase receipt format
`
`InAppAttribute ::= SEQUENCE {
`type
`INTEGER,
`version
`INTEGER,
`value
`OCTET STRING
`
`} I
`
`nAppReceipt ::= SET OF InAppAttribute
`
`The attributes for the original transaction identifier and original transaction date are used when a purchase is
`redownloaded. The redownloaded purchase is given a new transaction identifier, but it contains the identifier
`and date of the original purchase.
`
`2013-09-18 | Copyright © 2013 Apple Inc. All Rights Reserved.
`
`12
`
`
`
`Validating Receipts Locally
`Implementation Tips
`
`Consumable Products and Non-Renewing Subscriptions: The in-app purchare receipt for a
`consumable product or a non-renewing subscription is added to the receipt when the purchase is
`made. It is kept in the receipt until your app finishes that transaction. After that point, it is removed
`from the receipt the next time the receipt is updated—for example, when the user makes another
`purchase or if your app explicitly refreshes the receipt.
`
`Implementation Tips
`This section contains several code listings for your reference as you implement receipt validation.
`
`Get the GUID in OS X
`In OS X, follow the model in Listing 1-3 (or even use this exact same code), so that the method you use to get
`the GUID in your validation code is exactly the same as the method used when the application’s receipt was
`created.
`
`Listing 1-3
`
`Get the computer’s GUID
`
`#import <IOKit/IOKitLib.h>
`#import <Foundation/Foundation.h>
`
`// Returns a CFData object, containing the computer's GUID.
`CFDataRef copy_mac_address(void)
`{
`
`kern_return_t
`mach_port_t
`CFMutableDictionaryRef
`io_iterator_t
`io_object_t
`CFDataRef
`
`kernResult;
`master_port;
`matchingDict;
`iterator;
`service;
`macAddress = nil;
`
`kernResult = IOMasterPort(MACH_PORT_NULL, &master_port);
`if (kernResult != KERN_SUCCESS) {
`printf("IOMasterPort returned %d\n", kernResult);
`return nil;
`
`}
`
`2013-09-18 | Copyright © 2013 Apple Inc. All Rights Reserved.
`
`13
`
`
`
`Validating Receipts Locally
`Implementation Tips
`
`matchingDict = IOBSDNameMatching(master_port, 0, "en0");
`if (!matchingDict) {
`printf("IOBSDNameMatching returned empty dictionary\n");
`return nil;
`
`}
`
`kernResult = IOServiceGetMatchingServices(master_port, matchingDict, &iterator);
`if (kernResult != KERN_SUCCESS) {
`printf("IOServiceGetMatchingServices returned %d\n", kernResult);
`return nil;
`
`} w
`
`} I
`
`hile((service = IOIteratorNext(iterator)) != 0) {
`io_object_t parentService;
`
`kernResult = IORegistryEntryGetParentEntry(service, kIOServicePlane,
`&parentService);
`if (kernResult == KERN_SUCCESS) {
`if (macAddress) CFRelease(macAddress);
`
`macAddress = (CFDataRef) IORegistryEntryCreateCFProperty(parentService,
`CFSTR("IOMACAddress"), kCFAllocatorDefault, 0);
`IOObjectRelease(parentService);
`} else {
`printf("IORegistryEntryGetParentEntry returned %d\n", kernResult);
`
`} I
`
`OObjectRelease(service);
`
`OObjectRelease(iterator);
`
`return macAddress;
`
`}
`
`2013-09-18 | Copyright © 2013 Apple Inc. All Rights Reserved.
`
`14
`
`
`
`Validating Receipts Locally
`Implementation Tips
`
`Parse the Receipt and Verify Its Signature
`Use the following code listings as an outline of one possible implementation of receipt validation using OpenSSL
`and asn1c. These listings are provided to guide you as you write your own code, by highlighting relevant APIs
`and data structures, not to use as a copy-and-paste solution.
`
`If you use OpenSSL, statically link your binary against it. Dynamic linking against OpenSSL is deprecated and
`results in build warnings.
`
`Make sure your code does the following as outlined in the listings:
`
`1.
`
`2.
`
`3.
`
`Verify the signature (Listing 1-4 (page 15)).
`
`Parse the payload (Listing 1-5 (page 16)).
`
`Extract the receipt attributes (Listing 1-6 (page 17)).
`
`4. Compute the hash of the GUID (Listing 1-7 (page 17)).
`
`Listing 1-4
`
`Verify the signature using OpenSSL
`
`/* The PKCS #7 container (the receipt) and the output of the verification. */
`BIO *b_p7;
`PKCS7 *p7;
`
`/* The Apple root certificate, as raw data and in its OpenSSL representation. */
`BIO *b_x509;
`X509 *Apple;
`
`/* The root certificate for chain-of-trust verification. */
`X509_STORE *store = X509_STORE_new();
`
`/* ... Initialize both BIO variables using BIO_new_mem_buf() with a buffer and its
`size ... */
`
`/* Initialize b_out as an output BIO to hold the receipt payload extracted during
`signature verification. */
`BIO *b_out = BIO_new(BIO_s_mem());
`
`/* Capture the content of the receipt file and populate the p7 variable with the
`PKCS #7 container. */
`p7 = d2i_PKCS7_bio(b_p7, NULL);
`
`2013-09-18 | Copyright © 2013 Apple Inc. All Rights Reserved.
`
`15
`
`
`
`Validating Receipts Locally
`Implementation Tips
`
`/* ... Load the Apple root certificate into b_X509 ... */
`
`/* Initialize b_x509 as an input BIO with a value of the Apple root certificate and
`load it into X509 data structure. Then add the Apple root certificate to the
`structure. */
`Apple = d2i_X509_bio(b_x509, NULL);
`X509_STORE_add_cert(store, Apple);
`
`/* Verify the signature. If the verification is correct, b_out will contain the
`PKCS #7 payload and rc will be 1. */
`int rc = PKCS7_verify(p7, NULL, store, NULL, b_out, 0);
`
`/* For additional security, you may verify the fingerprint of the root certificate
`and verify the OIDs of the intermediate certificate and signing certificate. The
`OID in the certificate policies extension of the intermediate certificate is (1
`2 840 113635 100 5 6 1), and the marker OID of the signing certificate is (1 2 840
`113635 100 6 11 1). */
`
`Listing 1-5
`
`Parse the payload using asn1c
`
`#include "Payload.h" /* This header file is generated by asn1c. */
`
`/* The receipt payload and its size. */
`void *pld = NULL;
`size_t pld_sz;
`
`/* Variables used to parse the payload. Both data types are declared in Payload.h.
`*/
`Payload_t *payload = NULL;
`asn_dec_rval_t rval;
`
`/* ... Load the payload from the receipt file into pld and set pld_sz to the payload
`size ... */
`
`/* Parse the buffer using the decoder function generated by asn1c. The payload
`variable will contain the receipt attributes. */
`rval = asn_DEF_Payload.ber_decoder(NULL, &asn_DEF_Payload, (void **)&payload, pld,
`pld_sz, 0);
`
`2013-09-18 | Copyright © 2013 Apple Inc. All Rights Reserved.
`
`16
`
`
`
`Validating Receipts Locally
`Implementation Tips
`
`Listing 1-6
`
`Extract the receipt attributes
`
`/* Variables used to store the receipt attributes. */
`OCTET_STRING_t *bundle_id = NULL;
`OCTET_STRING_t *bundle_version = NULL;
`OCTET_STRING_t *opaque = NULL;
`OCTET_STRING_t *hash = NULL;
`
`/* Iterate over the receipt attributes, saving the values needed to compute the
`GUID hash. */
`size_t i;
`for (i = 0; i < payload->list.count; i++) {
`ReceiptAttribute_t *entry;
`
`entry = payload->list.array[i];
`
`switch (entry->type) {
`case 2:
`bundle_id = &entry->value;
`break;
`case 3:
`bundle_version = &entry->value;
`break;
`case 4:
`opaque = &entry->value;
`break;
`case 5:
`hash = &entry->value;
`break;
`
`}
`
`}
`
`Listing 1-7
`
`Compute the hash of the GUID
`
`/* The GUID returned by copy_mac_address() is a CFDataRef. Use CFDataGetBytePtr()
`and CFDataGetLength() to get a pointer to the bytes that make up the GUID and to
`get its length. */
`UInt8 *guid = NULL;
`
`2013-09-18 | Copyright © 2013 Apple Inc. All Rights Reserved.
`
`17
`
`
`
`Validating Receipts Locally
`Implementation Tips
`
`size_t guid_sz;
`
`/* Declare and initialize an EVP context for OpenSSL. */
`EVP_MD_CTX evp_ctx;
`EVP_MD_CTX_init(&evp_ctx);
`
`/* A buffer for result of the hash computation. */
`UInt8 digest[20];
`
`/* Set up the EVP context to compute a SHA-1 digest. */
`EVP_DigestInit_ex(&evp_ctx, EVP_sha1(), NULL);
`
`/* Concatenate the pieces to be hashed. They must be concatenated in this order.
`*/
`EVP_DigestUpdate(&evp_ctx, guid, guid_sz);
`EVP_DigestUpdate(&evp_ctx, opaque->buf, opaque->size);
`EVP_DigestUpdate(&evp_ctx, bundle_id->buf, bundle_id->size);
`
`/* Compute the hash, saving the result into the digest variable. */
`EVP_DigestFinal_ex(&evp_ctx, digest, NULL);
`
`2013-09-18 | Copyright © 2013 Apple Inc. All Rights Reserved.
`
`18
`
`
`
`Validating Receipts With the App Store
`
`Note: There is a vulnerability in iOS 5.1 and earlier related to receipt validation with the app store
`directly from a device, without using a server. For more details and a mitigation strategy, see In-App
`Purchase Receipt Validation for iOS 5.1 and Earlier .
`
`Use a trusted server to communicate with the App Store. Using your own server lets you design your app to
`recognize and trust only your server, and lets you ensure that your server connects with the App Store server.
`It is not possible to build a trusted connection between a users device and the App store directly because you
`don’t control either end of that connection.
`
`Verifying an App Receipt
`Communication with the App Store is structured as JSON dictionaries, as defined in RFC 4627. To verify the
`receipt, perform the following steps:
`
`1.
`
`Retrieve the receipt data and base64 encode it (using the RFC 4648 form of base64). Use the
`appStoreReceiptURL method of NSBundle to locate the app’s receipt, and then read the entire file.
`If the appStoreReceiptURL method is not available, you can fall back to the value of a transaction's
`transactionReceipt property for backward compatibility.
`2. Create a JSON object with a single key named receipt-data whose value is the base64-encoded receipt
`data. Your JSON object should look like this:
`
`{
`
`}
`
`"receipt-data" : "(receipt bytes here)"
`
`3.
`
`4.
`
`Send the JSON object to the App Store using an HTTP POST request. The URL for the store is
`https://buy.itunes.apple.com/verifyReceipt.
`The response received from the App Store is a JSON object with the keys status and receipt. (When
`validating an auto-renewable subscription, the response contains additional keys, as described in “Verifying
`an Auto-Renewable Subscription Receipt” (page 20)). It should look something like this:
`
`2013-09-18 | Copyright © 2013 Apple Inc. All Rights Reserved.
`
`19
`
`
`
`Validating Receipts With the App Store
`Verifying an Auto-Renewable Subscription Receipt
`
`{
`
`}
`
`"status" : 0,
`
`"receipt" : { (receipt here) }
`
`If the value of the status key is 0, this is a valid receipt. If the value is anything other than 0, this receipt
`is invalid.
`
`The value of the receipt key is a JSON object that contains the receipt’s fields. For information about the
`fields in a receipt, see “Receipt Fields” (page 22).
`
`Verifying an Auto-Renewable Subscription Receipt
`Verifying a receipt for an auto-renewable subscription is almost identical to the process described in “Validating
`Receipts With the App Store” (page 19). Your application creates a JSON object and posts it to the App Store.
`The JSON object for an auto-renewable subscription receipt includes a second parameter: the shared secret
`you created in iTunes Connect:
`
`{
`
`}
`
`"receipt-data" : "(receipt bytes here)",
`
`"password"
`
`: "(shared secret bytes here)"
`
`The response includes a status field that indicates whether the receipt was successfully validated.
`
`{
`
`}
`
`"status" : 0,
`
`"receipt" : { (receipt here) },
`
`"latest_receipt" : "(base-64 encoded receipt here)",
`
`"latest_receipt_info" : { (latest receipt info here) }
`
`2013-09-18 | Copyright © 2013 Apple Inc. All Rights Reserved.
`
`20
`
`
`
`Validating Receipts With the App Store
`Verifying an Auto-Renewable Subscription Receipt
`
`If the user’s receipt was valid and the subscription is active, the status field holds 0, and the receipt field is
`populated with the decoded receipt data. If your server receives a non-zero status value, use “Status codes for
`auto-renewable subscriptions” to interpret non-zero status codes.
`
`Table 2-1
`
`Status codes for auto-renewable subscriptions
`
`Status Code
`
`Description
`
`21000
`
`21002
`
`21003
`
`21004
`
`21005
`
`21006
`
`21007
`
`21008
`
`The App Store could not read the JSON object you provided.
`
`The data in the receipt-data property was malformed.
`
`The receipt could not be authenticated.
`
`The shared secret you provided does not match the shared secret on file for your
`account.
`
`The receipt server is not currently available.
`
`This receipt is valid but the subscription has expired. When this status code is returned
`to your server, the receipt data is also decoded and returned as part of the response.
`
`This receipt is a sandbox receipt, but it was sent to the production service for verification.
`
`This receipt is a production receipt, but it was sent to the sandbox service for verification.
`
`Important: The non-zero status codes here apply only when validating auto-renewable subscriptions. Do
`not use these status codes when testing responses for other kinds of products.
`
`The receipt field on the JSON object holds the parsed information from the receipt. The receipt data for an
`auto-renewable subscription includes some additional keys, and some other keys are used differently for
`subscriptions. For information about keys found in a receipt, see “Receipt Fields” (page 22).
`
`In addition to the receipt_data field, the response may also include two other fields. If the user’s subscription
`is active and was renewed by a transaction that took place after the receipt your server sent to the App Store,
`the latest_receipt field includes a base-64 encoded receipt for the last renewal for this subscription. The
`decoded data for this new receipt is also provided in the latest_expired_receipt_info field. Your server
`can use this new receipt to maintain a record of the most recent renewal.
`
`2013-09-18 | Copyright © 2013 Apple Inc. All Rights Reserved.
`
`21
`
`
`
`Receipt Fields
`
`Receipts are made up of a number of fields. Some fields are only available locally, in the ANS.1 form of the
`receipt, or only when validating with the App Store, in the JSON form of the receipt. Keys not documented
`below are reserved for use by Apple and must be ignored by your app.
`
`App Receipt Fields
`
`Bundle Identifier
`The app’s bundle identifier.
`
`ASN.1 Field Type 2
`
`ASN.1 Field Value UTF8STRING
`
`JSON Field Name bundle_id
`
`JSON Field Value string
`
`This corresponds to the value of CFBundleIdentifier in the Info.plist file.
`
`App Version
`The app’s version number.
`
`ASN.1 Field Type 3
`
`ASN.1 Field Value UTF8STRING
`
`JSON Field Name application_version
`
`JSON Field Value string
`
`This corresponds to the value of CFBundleVersion (in iOS) or CFBundleShortVersionString (in OS X)
`in the Info.plist.
`
`2013-09-18 | Copyright © 2013 Apple Inc. All Rights Reserved.
`
`22
`
`
`
`Receipt Fields
`App Receipt Fields
`
`Opaque Value
`An opaque value used as part of the SHA-1 hash.
`
`ASN.1 Field Type 4
`
`ASN.1 Field Value A series of bytes
`
`JSON Field Name (none)
`
`JSON Field Value (none)
`
`SHA-1 Hash
`A SHA-1 hash, used to validate the receipt.
`
`ASN.1 Field Type 5
`
`ASN.1 Field Value 20-byte SHA-1 digest
`
`JSON Field Name (none)
`
`JSON Field Value (none)
`
`In-App Purchase Receipt
`The receipt for an in-app purchase.
`
`ASN.1 Field Type 17
`
`ASN.1 Field Value SET of in-app purchase receipt attributes
`
`JSON Field Name in_app
`
`JSON Field Value array of in-app purchase receipts
`
`In the JSON file, the value of this key is an array containing all in-app purchase receipts. In the ASN.1 file, there
`are multiple fields that all have type 17, each of which contains a single in-app purchase receipt.
`
`Original Application Version
`The version of the app that was originally purchased.
`
`ASN.1 Field Type 19
`
`ASN.1 Field Value UTF8STRING
`
`2013-09-18 | Copyright © 2013 Apple Inc. All Rights Reserved.
`
`23
`
`
`
`Receipt Fields
`In-App Purchase Receipt Fields
`
`JSON Field Name original_application_version
`
`JSON Field Value string
`
`This corresponds to the value of CFBundleVersion (in iOS) or CFBundleShortVersionString (in OS X)
`in the Info.plist file when the purchase was originally made.
`
`In the sandbox environment, the value of this field is always “1.0”.
`
`Receipt Expiration Date
`The date that the app receipt expires.
`
`ASN.1 Field Type 21
`
`ASN.1 Field Value IA5STRING, interpreted as an RFC 3339 date
`
`JSON Field Name expiration_date
`
`JSON Field Value IA5STRING, interpreted as an RFC 3339 date
`
`This key is present only for apps purchased through the Volume Purchase Program. If this key is not present,
`the receipt does not expire.
`
`When validating a receipt, compare this date to the current date to determine whether the receipt is expired.
`Do not try to use this date to calculate any other information, such as the time remaining before expiration.
`
`In-App Purchase Receipt Fields
`
`Quantity
`The number of items purchased.
`
`ASN.1 Field Type 1701
`
`ASN.1 Field Value INTEGER
`
`JSON Field Name quantity
`
`JSON Field Value string, interpreted as an integer
`
`This value corresponds to the quantity property of the SKPayment object stored in the transaction’s payment
`property.
`
`2013-09-18 | Copyright © 2013 Apple Inc. All Rights Reserved.
`
`24
`
`
`
`Receipt Fields
`In-App Purchase Receipt Fields
`
`Product Identifier
`The product identifier of the item that was purchased.
`
`ASN.1 Field Type 1702
`
`ASN.1 Field Value UTF8STRING
`
`JSON Field Name product_id
`
`JSON Field Value string
`
`This value corresponds to the productIdentifier property of the SKPayment object stored in the
`transaction’s payment property.
`
`Transaction Identifier
`The transaction identifier of the item that was purchased.
`
`ASN.1 Field Type 1703
`
`ASN.1 Field Value UTF8STRING
`
`JSON Field Name transaction_id
`
`JSON Field Value string
`
`This value corresponds to the transaction’s transactionIdentifier property.
`
`Original Transaction Identifier
`For a transaction that restores a previous transaction, the transaction identifier of the original transaction.
`Otherwise, identical to the transaction identifier.
`
`ASN.1 Field Type 1705
`
`ASN.1 Field Value UTF8STRING
`
`JSON Field Name original_transaction_id
`
`JSON Field Value string
`
`This value corresponds to the original transaction’s transactionIdentifier property.
`
`All receipts in a chain of renewals for an auto-renewable subscription have the same value for this field.
`
`2013-09-18 | Copyright © 2013 Apple Inc. All Rights Reserved.
`
`25
`
`
`
`Receipt Fields
`In-App Purchase Receipt Fields
`
`Purchase Date
`The date and time that the item was purchased.
`
`ASN.1 Field Type 1704
`
`ASN.1 Field Value IA5STRING, interpreted as an RFC 3339 date
`
`JSON Field Name purchase_date
`
`JSON Field Value string, interpreted as an RFC 3339 date
`
`This value corresponds to the transaction’s transactionDate property.
`
`For a transaction that restores a previous transaction, the purchase date is the date of the restoration. Use
`“Original Purchase Date” (page 26) to get the date of the original transaction.
`
`In an auto-renewable subscription receipt, this is always the date when the subscription was purchased or
`renewed, regardles of whether the transaction has been restored.
`
`Original Purchase Date
`For a transaction that restores a previous transaction, the date of the original transaction.
`
`ASN.1 Field Type 1706
`
`ASN.1 Field Va