Understand the serverVerificationData in_app_purchase Flutter
It looks like either your receipt is not correct ( sandbox has issues sometimes ) or your server-side setup is wrong
For the first point, you can try creating a receipt by generating a storeKit config file. This can't be done in flutter, you have to open iOS module with code and setup storekit config file by going here.
After setting up the storekit file, you can either run the app from xCode directly or just close xCode and run from your preferred flutter IDE
Now, iOS will never hit the production purchase identifiers when you try to fetch/buy products from the app, and instead fetch the products from your storekit config and generate a receipt from those. This receipt is accepted by apple sandbox verification endpoint, you can also test refunds and subscription cancellations from xCode using a storekit config.
For the second point, you have to enable the app specific shared secret in iTunes connect and then use that in the 'password' key in the receipt validation API. Here is where you find it AppStoreConnect > Your app > Subscriptions
If it still doesn't solve the issue, I'd be happy to assist further.
EDIT: I just tested purchasing an auto renewable subscription purchased in sandbox (not storeki t) and then validating it using the sandbox URL and it returned the correct receipt data. In your post above, you don't need to base64 encode the purchaseDetails.verificationData.serverVerificationData since its already encoded. Have you tested this on postman? It Works there
EDIT: so the request is malformed because you are not sending data as String so you need to dump the dict
:
request_body = json.dumps({"receipt-data": token})
Tom3652
Updated on January 04, 2023Comments
-
Tom3652 over 1 year
I have done all the setup to verify my receipts server side (in python with the
requests
package).Here is my code :
url = "https://buy.itunes.apple.com/verifyReceipt" request_body = {"receipt-data": token} headers = {'Content-Type': 'application/json'} response = requests.post(url=url, headers=headers, data=request_body)
The only variable here is
token
that is sent from the client (flutter
) and that is :purchaseDetails.verificationData.serverVerificationData
.I can't verify the receipt because if i pass
token
asstring
, i receive always a 21002 error (malformed).
If i try something like this in python :token = base64.b64encode(token)
It throws this error :
a bytes-like object is required, not 'str'
which i don't understand because i am actually passing abytes
object.What is the correct format to pass to the POST request to verify the iOS receipt ? Is the flutter one correct or should we encode something ?
Any concrete example will be accepted because i could not find any.
PS : i am redirected to the sandbox url
"https://sandbox.itunes.apple.com/verifyReceipt"
if the production one fails. The sandboxresponse
is the same as the production one (statusCode: 21002
)-
Akshay almost 2 yearsHave you added the password key in the verifyReceipt endpoint ?
-
Tom3652 almost 2 yearsNo i haven't because i have no subscription and it seemed to be used for subscription right ?
-
-
Tom3652 almost 2 yearsHi thanks for the detailed answer, so basically you are telling me that i have a malformed receipt because i don't use the storekit and flutter IAP generates a "Test" receipt when purchasing from an app in
debug
mode ? -
Tom3652 almost 2 yearsAlso, when you say "iOS will never hit the production purchase IDs", flutter IAP seems to be retrieving those because i can fake the purchase till the end (it shows the App store sandbox dialog with my product from App Store connect). Or do you mean that i should actually not hit production but the storekit during tests ? And if so, how can i be sure that my app will be validating receipts once on the store ?
-
Tom3652 almost 2 yearsCould you also clarify this part please : ` #if DEBUG let certificate = "StoreKitTestCertificate" #else let certificate = "AppleIncRootCertificate" #endif ` Does it mean that Flutter IAP will automatically "sign" receipt with this certificate ? And this way Apple will validate my receipts ? - How / where should i add this please ? In the AppDelegate.swift ? Thanks !
-
Akshay almost 2 years@Tom3652 Storekit is used to test your purchases locally and doesn't even require internet. Have you tried purchasing with the sandbox tester account on a real device? I don't think the simulator will work in that case. As you test in-app purchases, StoreKit in Xcode generates receipts that are valid only in the test environment. Your app can validate the receipts locally by using a certificate that Xcode provides. Important You can’t validate receipts from the test environment with verifyReceipt because App Store doesn’t sign these receipts, and the verification fails.
-
Akshay almost 2 years@Tom3652 I just tested purchasing an auto renewable subscription purchased in sandbox (not storekit ) and then validating it using the sandbox URL and it returned the correct receipt data. In your post above, you don't need to base64 encode the
purchaseDetails.verificationData.serverVerificationData
since its already encoded. Have you tested this on postman? -
Akshay almost 2 years@Tom3652 I was able to replicate the status code 21002 by removing the "password" field from the request on postman. Have you tried fetching the base64 and calling the sandbox API on postman instead? the issue is definitely server side because I used the same code to fetch my receipts and send to my server.
-
Tom3652 almost 2 yearsAlright @Akshay, thank you very much for taking time on this. You were right, with Postman i am getting a 0 status code everything is working. When i have tried running a simple POST request in python, this didn't work.
-
Tom3652 almost 2 yearsOh i have found the issue, i should do request_body = json.dumps({"receipt-data": token}) before passing it to the request.... It was as simple as that ! I am granted you the bounty for trying to help me. Update your answer with this and i will accept as well :)