Closed liyamahendra closed 1 year ago
@liyamahendra : have you tried it without reencoding with btoa
? the receipt is already returned in base64
Closing in favor of: https://github.com/dooboolab/react-native-iap/issues/1967
@andresesfm my initial attempt was without reencoding with btoa
and it didn't work. Out of curiosity, I tried to encode it using base64 but no luck with that either. Sorry, I forgot to remove that from the issue details I posted.
I upgraded to 10.1.1 and still facing the same issue.
Any advise please? Do you need any specific details?
@liyamahendra in that case, I think it's related to this: https://stackoverflow.com/questions/32836058/ios-receipt-validation-error-21002.
@andresesfm thank you for reopening the issue! I'm not doing server-side validation of the Receipt. I think the RNIap.validateReceiptIos
call does validation against AppStore, right?
One of the Stackoverflow answer recommends replacing \r
and \n
characters. Do you think that's required? I've done Receipt validation in native iOS code - hadn't faced such issue there.
Fixed here following the suggestion from SO: https://github.com/dooboolab/react-native-iap/pull/1977
Released as part of 10.1.2
. Please consider making a contribution to the project
Hi @andresesfm thank you for looking into this again. I installed the module from cloning your fork and checking the base_64_encoding_options branch.
Test the app but it still returns {"status": 21002}
.
Have you tried looking at the receipt and see if there's something strange we are not seeing?
I noticed that you merged the base_64_encoding_options. I installed 10.1.2
and made sure to test the app using TestFlight build.
This seeing the same issue.
I'll comment about the receipt shortly.
I used the tool available here to decode the receipt using my shared secret. Following is the decoded receipt content:
{
"environment": "Sandbox",
"receipt": {
"receipt_type": "ProductionSandbox",
"adam_id": 0,
"app_item_id": 0,
"bundle_id": "my-bundle-id",
"application_version": "13",
"download_id": 0,
"version_external_identifier": 0,
"receipt_creation_date": "2022-09-20 17:42:53 Etc/GMT",
"receipt_creation_date_ms": "1663695773000",
"receipt_creation_date_pst": "2022-09-20 10:42:53 America/Los_Angeles",
"request_date": "2022-09-20 17:45:44 Etc/GMT",
"request_date_ms": "1663695944519",
"request_date_pst": "2022-09-20 10:45:44 America/Los_Angeles",
"original_purchase_date": "2013-08-01 07:00:00 Etc/GMT",
"original_purchase_date_ms": "1375340400000",
"original_purchase_date_pst": "2013-08-01 00:00:00 America/Los_Angeles",
"original_application_version": "1.0",
"in_app": [
{
"quantity": "1",
"product_id": "subscription.monthly",
"transaction_id": "2000000157180412",
"original_transaction_id": "2000000156809195",
"purchase_date": "2022-09-16 18:26:40 Etc/GMT",
"purchase_date_ms": "1663352800000",
"purchase_date_pst": "2022-09-16 11:26:40 America/Los_Angeles",
"original_purchase_date": "2022-09-16 18:21:41 Etc/GMT",
"original_purchase_date_ms": "1663352501000",
"original_purchase_date_pst": "2022-09-16 11:21:41 America/Los_Angeles",
"expires_date": "2022-09-16 18:31:40 Etc/GMT",
"expires_date_ms": "1663353100000",
"expires_date_pst": "2022-09-16 11:31:40 America/Los_Angeles",
"web_order_line_item_id": "2000000011165376",
"is_trial_period": "false",
"is_in_intro_offer_period": "false",
"in_app_ownership_type": "PURCHASED"
},
// many such objects
]
},
"latest_receipt_info": [
{
"quantity": "1",
"product_id": "subscription.monthly",
"transaction_id": "2000000159150610",
"original_transaction_id": "2000000156809195",
"purchase_date": "2022-09-20 16:27:15 Etc/GMT",
"purchase_date_ms": "1663691235000",
"purchase_date_pst": "2022-09-20 09:27:15 America/Los_Angeles",
"original_purchase_date": "2022-09-16 18:21:41 Etc/GMT",
"original_purchase_date_ms": "1663352501000",
"original_purchase_date_pst": "2022-09-16 11:21:41 America/Los_Angeles",
"expires_date": "2022-09-20 16:32:15 Etc/GMT",
"expires_date_ms": "1663691535000",
"expires_date_pst": "2022-09-20 09:32:15 America/Los_Angeles",
"web_order_line_item_id": "2000000011359346",
"is_trial_period": "false",
"is_in_intro_offer_period": "false",
"in_app_ownership_type": "PURCHASED",
"subscription_group_identifier": "21017206"
},
// many such objects
],
"latest_receipt": "MIKtXAY...=",
"pending_renewal_info": [
{
"expiration_intent": "1",
"auto_renew_product_id": "subscription.monthly",
"is_in_billing_retry_period": "0",
"product_id": "subscription.monthly",
"original_transaction_id": "2000000156809195",
"auto_renew_status": "0"
}
],
"status": 0
}
It looks like a valid JSON to me. So what's left is your SECRET, could it be that you are using the wrong one?
Well, I used the same secret to decode the receipt in the tool above - so I don't believe its the wrong one.
In that case, try sending it manually with curl or fetch. If you notice, all that method does is post to an end point
The request succeeds using CURL.
Can you please post here an example of the curl? Thank you for all your help
Here is the complete curl command:
curl -H "Accept: application/json" -H "Content-Type: application/json" -X POST -d '{"receipt-data":"MIKtYAYJKoZIhvcNAQcCoIKt.....", "password": "67a46f..."}' https://sandbox.itunes.apple.com/verifyReceipt
2 things come to mind: 1) You are using the URL that would be used if isTestEnvironment
is passed as true. Can you verify that's the case? and 2) If that's still the case, you'd need to capture the request using your dev tools or proxy.
Please let me know how that goes
I verified the URL using Charles - the sandbox url is used. Please see the screencast below:
https://user-images.githubusercontent.com/911879/192083142-658459b4-1032-4ad1-8523-1ec3c6c8f036.mp4
@andresesfm is there anything I can do to help you debug this issue and hopefully fix this?
How do you test & verify it on your end?
@liyamahendra can you please confirm that the request to sandbox captured on Charles is the same as the one you used in the cUrl?
@andresesfm yes, I can confirm the requests were the same.
Check the headers and the encoding please
@andresesfm below is the screenshot showing the headers ( and the URL as well)
@liyamahendra those are the response headers, I meant the request headers
@andresesfm check this link Apple Developer Documentation that is not even an issue of the plugin developer have to apply simple checks and he is good to go just read this documentation learn about different status code based on receipt i think this issue need to be closed
@alihassan143 sorry, not sure what checks you're referring to!
@liyamahendra you can check apple documentation and based on different status code you can perform actions accordingly you have to do it in app
I tried this with 12.0.3
as well. Same 21002 error.
currentPurchase return transactionReceipt
and purchaseToken
empty after subscription is successful.
How can I validate receipt without transactionReceipt
?
Does anybody encounter same issue. If yes then how it is fixed?
{"originalTransactionDateIOS": "1666159614000", "originalTransactionIdentifierIOS": "2000000180630438", "productId": "com.quiptgsm.pkg3", "purchaseToken": "", "quantityIOS": 1, "transactionDate": 1666162562000, "transactionId": "2000000180630438", "transactionReceipt": ""}
@iambinodstha are you using configuration.storekit
@iambinodstha configuration.storekit always return empty you have to test it on real device
@alihassan143 Yes I do have setup({storekitMode: 'STOREKIT2_MODE'});
configuration in my index file and also I am using real device but still getting empty receipt.
@iambinodstha when testing on real remove storekit configuration file from your build and then test that if you use storekit configurations file its always return empty mostly the reciept validation should be managed on server side and apple also recommend that never validate that kind of information inn mobile
Storekit 2 does not have receipt validation, It's done via the JWT Token
@andresesfm yes receipt validation should be manage on backend if developer don't have backend than he can validate receipt using apple rest api and can perform certain operations that apple return and clearly mentioned in the rest api documentation
@alihassan143 @andresesfm thanks for the info. I have handled receipt validation in my backend. but the problem now is receipt expires within few hours after I subscribe to the 1 month package.
Here is the JavaScript Code to check Receipt is Expired or not:
const data = {
"receipt-data": transactionReceipt,
"password": password,
"exclude-old-transaction": true
}
let url = isTest ? "https://sandbox.itunes.apple.com/verifyReceipt" : "https://buy.itunes.apple.com/verifyReceipt"
const result = await axios.post(
url,
data
)
const receiptData = result["data"]["latest_receipt_info"][0];
const expiry = receiptData.expires_date_ms;
const expired = Date.now() > expiry;
return {
isExpired: expired
}
If I used enabled storekit 2, how can I validate subscription without purchase token and receipt?
@iambinodstha you cannot validate receipt without token actually token is base64 string that contains all information related to user package in test the package is only for 5 minutes after that it cycle restarts again
Just leaving a small update - this wasn't resolved for me. I ended by writing a react bridge for iOS, specifically to check the subscription status, and solved the issue.
@alihassan143 sorry, not sure what checks you're referring to!
Hello @liyamahendra I got the same issue. Have you been able to sort it out since ?
@AF-Hub please see the update I left above.
@liyamahendra would you care to share that code/ create a PR with your change? it could benefit the project a lot since you mention it is still an issue
@andresesfm I don't think creating a PR would be a good choice because I used RMStore for this purpose.
@andresesfm since I've native iOS experience, would you like me to debug & evaluate why this bridge is failing?
@andresesfm can you provide more details did you are successfully be able to subscribe or purchase item through in app purchase if yes than validation thing is not package things actually you have to accept that validation thing is not package does the only just call the apple api and apple servers responed according to your data validations have to be manage by the developers
@andresesfm since I've native iOS experience, would you like me to debug & evaluation why this bridge is failing?
I just pushed a fix, please let us know if any issues
In order to validate a receipt, it must be purchased through a build in TestFlight.
Any receipts purchased with a build outside TestFlight return a 21002 (either real device or simulator)
@atkristin thanks for the information. I can confirm that I was testing the InApp Purchase through TestFlight build.
If you using local StoreKit config that the receipt is not valid because the certificate is created on your computer so the both sandbox and prod doesn't accept the receipt. It is recommended to verify through the server, refer to https://tkzo.jp/blog/flutter-iap-implementation/
Description
On app startup, I want to check if the auto-renewing subscription is still active or not. I'm using the below code:
However, the
decodedReceipt
returns{"status": 21002}
Expected Behavior
Should return the Receipt Info
Screenshots
Environment:
To Reproduce Steps to reproduce the behavior:
RNIap.initConnection()
RNIap.getAvailablePurchases()
as shown inisSubscriptionActive
method[Optional] Additional Context
I'm using TypeScript.