iap-receipt-validator
iap-receipt-validator copied to clipboard
Failed to execute 'readAsText' on 'FileReader': parameter 1 is not of type 'Blob'.
Using https://github.com/chirag04/react-native-in-app-utils for purchasing, and:
InAppUtils.receiptData((error, receiptData)=> { if(error) { } else { IOSReceiptValidator.validate(receiptData).then(function (result) { }).catch(function (error) { }); }
For getting last receipt, when trying to validate, getting this error (While debugging on real device in Chrome)
Failed to execute 'readAsText' on 'FileReader': parameter 1 is not of type 'Blob'.
Any solution to this?
@mcmatan could you provide full stack trace? and also the receiptData
?
Hi @sibelius Loving this library so far!
So this is what I am doing. After a purchase is successful, I am calling this function for validating the receipt
const validateRecipt = (response) => {
InAppUtils.receiptData((error, receiptData) => {
try {
generalHelper.validate(receiptData);
} catch (err) {
console.log(err.valid, err.error, err.message);
}
if (error) {
console.log(error);
// AlertIOS.alert('itunes Error', 'Receipt not found.');
} else {
//send to validation server
}
});
const password = 'hidden'; // Shared Secret from iTunes connect
const production = false; // use sandbox or production url for validation
class GeneralHelper {
async validate(receiptData) {
const validateReceipt = iapReceiptValidator(password, production);
try {
const validationData = await validateReceipt(receiptData);
console.log(validationData);
// check if Auto-Renewable Subscription is still valid
// validationData['latest_receipt_info'][0].expires_date > today
} catch (err) {
console.log(err.valid, err.error, err.message);
}
}
}
export default GeneralHelper;
Its throwing error on
const validateReceipt = iapReceiptValidator(password, production);
Here is the error
undefined undefined "Failed to execute 'readAsText' on 'FileReader': parameter 1 is not of type 'Blob'."
I get the following receiptData
Hi, yes I've solved this, the error seems to be because of the fetch req, not the URL this library is using. I've fixed this by using 'fetchBlob' library for requesting, and used this url's:
const url = Env.isDebug() ? "https://sandbox.itunes.apple.com/verifyReceipt" : "https://buy.itunes.apple.com/verifyReceipt";
Body:
"receipt-data": receiptData, password: password
And this are the responses you can get:
0 : Valid 21000 :The App Store could not read the JSON object you provided. 21002 : The data in the receipt-data property was malformed or missing. 21003 : The receipt could not be authenticated. 21004 : The shared secret you provided does not match the shared secret on file for your account. Only returned for iOS 6 style transaction receipts for auto-renewable subscriptions. 21005: The receipt server is not currently available. 21006: This receipt is valid but the subscription has expired. When this status code is returned to your server, the receipt data is also decoded and returned as part of the response. Only returned for iOS 6 style transaction receipts for auto-renewable subscriptions. 21007 : This receipt is from the test environment, but it was sent to the production environment for verification. Send it to the test environment instead. 21008 : This receipt is from the production environment, but it was sent to the test environment for verification. Send it to the production environment instead.
@mcmatan do you mind sending a PR fixing this?
@sibelius I didn't use the library for it at all... I've just made a vanilla request with fetchBlob to the end point I've mentioned, I can if you want, but this means using the fetchBlob library, are you ok with it?
I'm ok with it
K I'll try to do it soon
here's example using RNFetchBlob:
let receipts = <..go & get the receipt data...>
const url = 'https://sandbox.itunes.apple.com/verifyReceipt'
//for prod:- 'https://buy.itunes.apple.com/verifyReceipt'
let body = {
'receipt-data': receipts,
password: '<your iTunes Connect shared secret here>'
}
RNFetchBlob.fetch(
'POST',
url,
{
'Content-Type': 'application/x-www-form-urlencoded'
},
JSON.stringify(body)
)
.then(res => {
console.log('response', res.text())
})
.catch(err => {
console.log(err.text())
})
I solved it by resetting the window.XMLHttpRequest to it's original state after finished using the fetch blob for uploading my image to cloud.
const Blob = FetchBlob.polyfill.Blob; // Keep an original copy of window.XMLHttpRequest before set it to FetchBlob.polyfill.XMLHttpRequest const oriWindowXMLHttpRequest = window.XMLHttpRequest; window.XMLHttpRequest = FetchBlob.polyfill.XMLHttpRequest; window.Blob = Blob;
return new Promise((resolve, reject) => { Blob.build(imageFile, { type: mime }) .then(blob => { uploadBlob = blob; return imageRef.put(blob, { contentType: mime }); }) .then(() => { uploadBlob.close(); return imageRef.getDownloadURL(); }) .then(url => { // Reset it to the original WindowXMLHttpRequest after the task has been done completely window.XMLHttpRequest = oriWindowXMLHttpRequest; resolve(getSuccessResponse(url)); }) .catch(error => { // Reset it to the original WindowXMLHttpRequest after the task has been done completely window.XMLHttpRequest = oriWindowXMLHttpRequest; showToast(getErrorMessage(error)); reject(getFailResponse(error)); }); });
Hope it helps :)