Sandbox environment returns 21003 status code for receipt validation
Asked Answered
B

5

8

When I test an in-app purchase with the sandbox the post request to the sandbox url https://sandbox.itunes.apple.com/verifyReceipt returns

 data: { environment: 'Sandbox', status: 21003 }

The 21003 status code means that the receipt could not be authenticated. https://developer.apple.com/documentation/appstorereceipts/status?language=objc

Is this expected? I'd assumed my test receipt would be considered valid for the sandbox environment and return a status of 0.

Booker answered 19/6, 2021 at 19:35 Comment(3)
Does this answer your question? "The receipt could not be authenticated" - should it be checked again?Fastening
We’ve been receiving the same error with sandbox receipts from App Review for our macOS app for the last week. When verifying the receipts without a password they return a valid receipt JSON, so the receipt data seems to be ok. The issue in this case seems to be on Apple’s side and we’ve opened a DTS incident to try to get this resolved.Disc
It returned 21003 for me for about an hour after the shared secret was created. Then it disappeared. I assume it takes some time for the shared secret to take effect after it's created?Southeasterly
B
5

No its not expected. I needed to provide a valid code in the password field even though the in-app purchase was not for an auto-renewable subscription.

Booker answered 23/6, 2021 at 0:52 Comment(0)
P
6

You report that when you send the appStoreReceipt to the verifyReceipt endpoint that you are seeing the status result 21003. This status indicates that the appStoreReceipt was malformed, incomplete, or incorrectly encoded. Can you capture the base64 encoded appStoreReceipt and send me the contents as a text file for me to manually validate the contents. If you app process sells an auto-renewing subscription item, please include the app's shared secret. I use the following curl command line tool to validate appStoreReceipts.

For sandbox receipts:

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

For production receipts:

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.

Purveyance answered 10/8, 2021 at 5:38 Comment(0)
B
5

No its not expected. I needed to provide a valid code in the password field even though the in-app purchase was not for an auto-renewable subscription.

Booker answered 23/6, 2021 at 0:52 Comment(0)
A
4

I also experienced this error, in my case the shared secret that I was using was wrong.

Acronym answered 21/9, 2023 at 9:23 Comment(0)
S
0

Maybe someone need a bash script I have wrote for this.

#!/bin/bash
clear

green='\033[0;32m'
cyan='\033[0;36m'
noColor='\033[0m' # No Color

sig=$1
mode=$2

if [ -z "$mode" ];
  then
    PS3="Please select a mode: "
    options=("Sandbox" "Production")
    select opt in "${options[@]}"
    do
        case $opt in
            "Sandbox") break;;
            "Production") break;;
            *) echo -e ${red}"\ninvalid option" \"$REPLY\"${noColor};;
          esac
        done
    else
      opt=$mode
fi

if [[ "$opt" == "Production" ]]
then
  echo -e ${green}"Production selected"${noColor}
  commandToExecute="curl -d '{\"receipt-data\":\"$sig\"}' https://buy.itunes.apple.com/verifyReceipt"
else
  echo -e ${cyan}"Sandbox selected"${noColor}
  commandToExecute="curl -d '{\"receipt-data\":\"$sig\"}' https://sandbox.itunes.apple.com/verifyReceipt"
fi

eval $commandToExecute

Call it like ./scriptName signatureToValidate

Stannite answered 26/9, 2022 at 10:20 Comment(0)
I
0

In my case the password was incorrect of the API call was incorrect.

You should go to Apple Store Connect > Users and Access > Shared Secret :

Then Create (or copy if you already have one) the shared secret.

Then in the API call you should include this code in the password field of the body:

exports.postDataApple = async (receiptData) => {
    const res = await fetch(process.env.APPLE_VERIFY_RECEIPT_URL, {
        method: "POST",
        headers: {
             "Content-type": "application/json",
             "Accept": "application/json",
        },
        body: JSON.stringify({
            "password": process.env.APPLE_SECRET_STRING,
            "receipt-data": receiptData,
        }),
    });
    return res !== undefined && res.status !== undefined && res.status === 200
        ? await res.json()
        : undefined;
}
Ias answered 8/12, 2023 at 9:20 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.