Status code 21002 whenever I try to validate IAP

Hello,
I'm working on non-consumable IAP's and when I try to validate a receipt, I always get this status code 21002, which means my receipt-data property was malformed or missing. But I've been doing exactly how I saw on apple's documentation. What am I missing?

Code Block
guard let receiptFileURL = Bundle.main.appStoreReceiptURL,
       FileManager.default.fileExists(atPath: receiptFileURL.path) else { return }
     
    let receiptData = try? Data(contentsOf: receiptFileURL, options: .alwaysMapped)
       
      guard receiptData != nil else {
        UserDefaults.standard.set(false, forKey: "isSubscribed")
        return
      }
       
      var recieptString = receiptData?.base64EncodedString() ?? ""
       
      let jsonDict : [String : Any] = ["receipt-data" : recieptString,
                       "password" : Constants.ConstantValues.autoRenewableReceitPassword.rawValue,
                       "exclude-old-transactions" : false]
       
      let httpBody = try? JSONSerialization.data(withJSONObject: jsonDict, options: [])
       
      let storeURL = URL(string: Constants.ConstantValues.verifyReceiptSandboxURL.rawValue)!
       
      var storeRequest = URLRequest(url: storeURL)
       
      storeRequest.httpMethod = "POST"
      storeRequest.httpBody = httpBody
       
      storeRequest.setValue("Application/json", forHTTPHeaderField: "Content-Type")
       
      let task = URLSession.shared.dataTask(with: storeRequest) { [weak self] (data, response, error) in
         
        if let data = data, let jsonData = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) {
           
/* Status code is always 21002 */
          
          }
           
        }
      }
       
      task.resume()




There are different ways to handle this. One suggestion is to obtain the complete base64 encoded appStoreReceipt (in your case - recieptString) and manually validate the contents using the Terminal app - curl command line tool. Here's how to do so.

curl -d '{ "exclude-old-transactions": true "password":"yyyy" "receipt-data": "xxxx"}' https://sandbox.itunes.apple.com/verifyReceipt

curl -d '{ "exclude-old-transactions": true "password":"yyyy" "receipt-data": "xxxx"}' https://buy.itunes.apple.com/verifyReceipt

Where exclude-old-transactions is used to limit the contents of the latest_receipt_info to only the most recent entry and

"password" is the request key to indicate the shared-secret that is required when the content is an auto-renewing subscription.

yyyy - is the shared-secret and
xxxx - is the base64 encoded content of the appStoreReceipt.

When manually validating the receipt make sure that the double quote characters are not the curly type. The use of the curly quotes will themselves result in status 21002 for a valid appStoreReceipt.

rich kubota - rkubota@apple.com
developer technical support CoreOS/Hardware/MFI
I have the same issue while implementing IAP in dev environment. I guest it's related to In-app purchases do not work on the simulator things.

Before doing receipt validation and encounter the problem bakedPotato mentioned, I got problem loading products, and have worked around.

Then I tested the receipt and always 21002. I tried sample receipts from internet (and my old pre-iOS 13 receipt data) with same curl script and succeeded. Then I decoded the base64 with online decoder and compared pre-iOS 13 receipts with new one. It seems the new one has different information from old one, for examples, it misses something like "ProductionSandbox0", "#Apple Worldwide Developer Relations"..., but it has "Xcode0", "StoreKit1" that are not in the pre-iOS 13 one.

Sample pre-iOS 13 receipts

New receipt:

Code Block
MIAGCSqGSIb3DQEHAqCAMIACAQExDzANBglghkgBZQMEAgEFADCABgkqhkiG9w0BBwGggCSABIIBOzGCATcwDwIBAAIBAQQHDAVYY29kZTALAgEBAgEBBAMCAQAwFwIBAgIBAQQPDA1jb20uYnVraS5idWtpMAwCAQMCAQEEBAwCNzkwEAIBBAIBAQQIse//TgcAAAAwHAIBBQIBAQQUjIZ+RiRb9aC7bpQ3J2LCtkv3vhMwCgIBCAIBAQQCFgAwIgIBDAIBAQQaFhgyMDIxLTAzLTA4VDE3OjQ4OjM0KzA4MDAwbAIBEQIBAQRkMWIwDAICBqUCAQEEAwIBATAjAgIGpgIBAQQaDBhjb20uYnVraS5idWtpLmNvaW5zLjMwMDAwDAICBqcCAQEEAwwBMDAfAgIGqAIBAQQWFhQyMDIxLTAzLTA4VDE3OjQ4OjM0WjAiAgEVAgEBBBoWGDQwMDEtMDEtMDFUMDg6MDA6MDArMDgwMAAAAAAAAKCCA3gwggN0MIICXKADAgECAgEBMA0GCSqGSIb3DQEBCwUAMF8xETAPBgNVBAMMCFN0b3JlS2l0MREwDwYDVQQKDAhTdG9yZUtpdDERMA8GA1UECwwIU3RvcmVLaXQxCzAJBgNVBAYTAlVTMRcwFQYJKoZIhvcNAQkBFghTdG9yZUtpdDAeFw0yMDA0MDExNzUyMzVaFw00MDAzMjcxNzUyMzVaMF8xETAPBgNVBAMMCFN0b3JlS2l0MREwDwYDVQQKDAhTdG9yZUtpdDERMA8GA1UECwwIU3RvcmVLaXQxCzAJBgNVBAYTAlVTMRcwFQYJKoZIhvcNAQkBFghTdG9yZUtpdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANt/kDwscw/blyZLk7sK+KEhPshh2STIXh91PtqT2zEakYC5v+UMyzy7DkRiJvoEKbZJ52/gG+YNaM0lbsN2CPVKC656dDzEqQuzz2IP+7S899uEXijrRw3x7Yus9Z+wCTijbnvLJlAKGuGJ0XJ2CzlMy09uwLNft5W6uahdSnSr729BpX4Jjbo9Pc1wV9jt79Xad8iTBBzvCYh4Zc6B8o1y5wcabiYS9zKxDbs4kGsGxPwN5ZVQzZHIuiX0WMmM4XHbSkXzLRmWA1aBpkTudXdbU894rF26Pl9QK1wpjN3C1yoX3yK01+R4qK+obafB2mgtZszPKQtQLOPC++ZfEsECAwEAAaM7MDkwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAoQwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwMwDQYJKoZIhvcNAQELBQADggEBALIA4Dzx6OlivcDWHUCeV7k4kHd2UtKoS3BuuGeZ+7OKVZ/vHSi4XyrRc581Rze7RtN2EPLwaezNYplx+WCKhEg4xL2LZyW5q2wzZa3Ywpp4SA/pzMEnDcbPZDxtgFkzjMo2BioRG41Jzgj/ZsBHKEvrWsErCVYspaoJA3syMdzohXhIzsDFEhFqDwyuLwXKb3pkfyAsdeZMsRLT/eMfXg19uFjXoHzkf2Orl5orwyrY0LLh1VoNORtvZyipEoPWh3htmb1eswrgmM726sOObWnrt0CBPYc9cFHRxF2Npdx/alga3mB2N1Ls/6wZXQwVL4oP9YnI1ysdHuwrkQWnPU8xggGPMIIBiwIBATBkMF8xETAPBgNVBAMMCFN0b3JlS2l0MREwDwYDVQQKDAhTdG9yZUtpdDERMA8GA1UECwwIU3RvcmVLaXQxCzAJBgNVBAYTAlVTMRcwFQYJKoZIhvcNAQkBFghTdG9yZUtpdAIBATANBglghkgBZQMEAgEFADANBgkqhkiG9w0BAQsFAASCAQAlfjPYZ/c32kRxPfA05Dx+vfq2Ri6t/FecNic3T135VWnP6ameGee377IW+wzx8vlzBYN3hzDd9Byx+S/UXmayXwApQnm0yLeGnrmHd+wPt+isBY6gFBAoUE3fvhVga7jjxzB7o+2Pm7LI+eLYjTIuODLXmD/54fmF1l31Us/3+vc4scvW0d63vsRbt5bJHIMqOG/JNvtetQmvl8xZa7wehOCPKi1W6aKEMWJalce7fD2Ps2EYqS1OWawj0IseqSfuCTLz5OMq+TFSEYn8bjmTBgygihP6AK2sRBealAxeNBtH+Jy9F8pP1ubbfkKWMSuOz1iAvQCbp0LQPxQdFE9LAAAAAAAA


I will try ad-hoc later and I guess it will get right there.
Status code 21002 whenever I try to validate IAP
 
 
Q