iosin-app-purchaseapple-push-notificationsreceipt-validation

Inconsistencies with documentation in response from Apple's verifyReceipt endpoint


I'm in the process of setting up receipt validation for Apple's auto-renewable subscriptions on our server and noticed some inconsistencies with the official documentation. When verifying a sandbox receipt with the sandbox verifyReceipt endpoint, the response looks as follows:

{  
  "auto_renew_status": 1,  
  "status": 0,  
  "auto_renew_product_id": "app.xxx",  
  "receipt": {  
    "original_purchase_date_pst": "2020-03-18 01:11:45 America/Los_Angeles",  
    "quantity": "1",  
    "unique_vendor_identifier": "6D2xxx194",  
    "bvrs": "2",  
    "expires_date_formatted": "2020-03-20 12:27:07 Etc/GMT",  
    "is_in_intro_offer_period": "false",  
    "purchase_date_ms": "1584703627636",  
    "expires_date_formatted_pst": "2020-03-20 05:27:07 America/Los_Angeles",  
    "is_trial_period": "false",  
    "item_id": "15xxx27",  
    "unique_identifier": "cd5xxx424",  
    "original_transaction_id": "100xxx735",  
    "subscription_group_identifier": "20xxx02",  
    "transaction_id": "100xxx439",  
    "web_order_line_item_id": "100xxx419",  
    "version_external_identifier": "0",  
    "purchase_date": "2020-03-20 11:27:07 Etc/GMT",  
    "product_id": "app.xxx",  
    "expires_date": "1584707227636",  
    "original_purchase_date": "2020-03-18 08:11:45 Etc/GMT",  
    "purchase_date_pst": "2020-03-20 04:27:07 America/Los_Angeles",  
    "bid": "app.xxx",  
    "original_purchase_date_ms": "1584519105000"  
  },  
  "latest_receipt_info": {  
    "original_purchase_date_pst": "2020-03-18 01:11:45 America/Los_Angeles",  
    "quantity": "1",  
    "unique_vendor_identifier": "6D2xxx194",  
    "bvrs": "2",  
    "expires_date_formatted": "2020-03-20 12:27:07 Etc/GMT",  
    "is_in_intro_offer_period": "false",  
    "purchase_date_ms": "1584703627000",  
    "expires_date_formatted_pst": "2020-03-20 05:27:07 America/Los_Angeles",  
    "is_trial_period": "false",  
    "item_id": "15xxx27",  
    "unique_identifier": "cd5xxx424",  
    "original_transaction_id": "100xxx735",  
    "subscription_group_identifier": "20xxx02",  
    "transaction_id": "100xxx439",  
    "bid": "app.xxx",  
    "web_order_line_item_id": "100xxx419",  
    "purchase_date": "2020-03-20 11:27:07 Etc/GMT",  
    "product_id": "app.xxx",  
    "expires_date": "1584707227000",  
    "original_purchase_date": "2020-03-18 08:11:45 Etc/GMT",  
    "purchase_date_pst": "2020-03-20 04:27:07 America/Los_Angeles",  
    "original_purchase_date_ms": "1584519105000"  
  },  
  "latest_receipt": "xxx"  
} 

I especially want to point out the following fields of that response:

{  
    ...  
  "latest_receipt_info": {  
      ...  
    "expires_date": "1584707227000",  
    "expires_date_formatted": "2020-03-20 12:27:07 Etc/GMT",  
    "expires_date_formatted_pst": "2020-03-20 05:27:07 America/Los_Angeles",  
    "subscription_group_identifier": "20xxx02",  
    "bid": "app.xxx",  
      ...  
  },  
  "receipt": {  
      ...  
    "expires_date": "1584707227636",  
    "expires_date_formatted": "2020-03-20 12:27:07 Etc/GMT",  
    "expires_date_formatted_pst": "2020-03-20 05:27:07 America/Los_Angeles",  
    "subscription_group_identifier": "20xxx02",  
    "bid": "app.xxx",  
      ...  
  },  
    ...  
} 

The inconsistencies with the official documentation are:

  1. latest_receipt_info is documented to be an array, however, it is a single json object.
  2. In the latest_receipt_info, the expires_date is not in "date-time format similar to the ISO 8601" as the documentation says, but looks like it is in milliseconds since epoch (what should be the expires_date_ms). However, we can find the key expires_date_formatted that is in date-time format.
  3. The same fields as in (2) can be also found in the receipt, however, the documentation states only a key expiration_date (analogue to the expires_date in the latest_receipt_info in date-time format) and expiration_date_ms (in milliseconds since epoch).
  4. The documented bundle_id key (here and here) is not present, but a key bid is, that contains the bundle id.
  5. The key subscription_group_identifer does not contain the exact string entered in AppStoreConnect as subscription group identifer, as documented (here and here), but contains some integer value.

So according to the documentation, the response should look like this, for me:

{  
    ...  
  "latest_receipt_info": [  
    {  
        ...  
      "expires_date": "2020-03-20 12:27:07 Etc/GMT",  
      "expires_date_ms": "1584707227000",  
      "expires_date_pst": "2020-03-20 05:27:07 America/Los_Angeles",  
      "subscription_group_identifier": "MY_SUBSCRIPTION_GROUP_ID",  
      "bundle_id": "app.xxx",  
        ...  
    }  
  ],  
  "receipt": {  
      ...  
    "expiration_date": "2020-03-20 12:27:07 Etc/GMT",  
    "expiration_date_ms": "1584707227636",  
    "expiration_date_pst": "2020-03-20 05:27:07 America/Los_Angeles",  
    "subscription_group_identifier": "MY_SUBSCRIPTION_GROUP_ID",  
    "bundle_id": "app.xxx",  
      ...  
  },  
    ...  
}  

Thanks in advance!


Solution

  • For everyone that faces the same problem: We sent the wrong receipt data to our backend since we requested the receipt via the deprecated transactionReceipt and not via appStoreReceiptURL.