I've tried to submit to the sandbox (and live) verifyReceipt endpoints, but I'm always getting the following JSON response from Apple...
{"status":21002, "exception":"java.lang.IllegalArgumentException"}
Here's a postman screenshot
I'm POSTing the data and setting the headers (application/json)
{"receipt-data":"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"}
This is being executed from an application running on the iOS device through a TestFlight internal test deployment.
I'm a little unsure whether the Base64 string isn't well formed or something - it did have some spaces in, but I now remove those (because I've seen reports saying that it helps).
Here's what I do in the mobile app to capture the base 64 encoding, please note this is Xamarin code (C#), but it maps to what you do in objective-c / swift very closely...
String purchaseToken = null;
NSUrl receiptUrl = NSBundle.MainBundle.AppStoreReceiptUrl;
NSData receipt = null;
if (receiptUrl != null) {
receipt = NSData.FromUrl (receiptUrl);
}
if (receipt == null) {
// No local receipt -- handle the error
// https://forums.xamarin.com/discussion/25304/how-do-i-exit-with-reason
Environment.Exit (173);
} else {
// http://stackoverflow.com/questions/37505020/example-of-parsing-a-receipt-for-an-in-app-purchase-using-ios-xamarin
NSDictionary requestContents = NSDictionary.FromObjectAndKey ((NSString)receipt.GetBase64EncodedString (
NSDataBase64EncodingOptions.None),
(NSString)"receipt-data");
string receiptData = (requestContents ["receipt-data"] as NSString).ToString ();
purchaseToken = receiptData;
}
this.iapConsumableEvent.PurchaseToken = purchaseToken;
The server code (Java) transmits this to Apple using HttpClient...
HttpClient client = HttpClientHelper.createHttpClient(new DefaultRedirectStrategy());
final HttpPost postRequest = new HttpPost(appleEnvironmentUrl);
StringEntity input = new StringEntity(receiptRequestJson);
input.setContentType("application/json");
postRequest.setEntity(input);
HttpResponse response = client.execute(postRequest);
String rawResponse = EntityUtils.toString(response.getEntity(), "UTF-8");
BasicDBObject appleJson = BasicDBObject.parse(rawResponse);
int statusCode = appleJson.getInt("status", 99999);
//ALWAYS RETURNING 21002 The data in the receipt-data property was malformed or missing.
I appreciate any thoughts / answers!
I had issues trying to get it to work through postman.
This works for me in node.js. I had to JSON.stringify before it did work :
function createReceiptRequestJSON(base64String, secret) {
return JSON.stringify({
"receipt-data":base64String,
password:secret
});
}
You can find your secret through itunes connect but I'm fairly sure you need to include in the request.
Edit:
Had another look with postman. Got the receipt from this answer Apple receipt_data sample.
Seems to work...
There is simple code that can help you to check your current purchased products:
It works on iOS 13.1
func verifyPurchaseWithPayment() {
let appsToreUrlString = "https://sandbox.itunes.apple.com/verifyReceipt"
let receiptUrl = Bundle.main.appStoreReceiptURL
do {
let receipt = try Data(contentsOf: receiptUrl!)
let encodedString = receipt.base64EncodedString()
let requestContents = ["receipt-data" : encodedString] as [String : Any]
do {
let requestJsonData = try JSONSerialization.data(withJSONObject: requestContents, options: [])
let session = URLSession.shared
let request = NSMutableURLRequest(url: URL(string: appsToreUrlString)!)
request.httpMethod = "POST"
request.httpBody = requestJsonData
let dataTask = session.dataTask(with: request as URLRequest) { (data, response, err) in
let json = try? JSONSerialization.jsonObject(with: data!, options: [])
print(json ?? "")
}
dataTask.resume()
} catch { return }
} catch { return }
}
I had a problem with Alamofire lib that changed some symbols, so i shielded base64 string before sending to server for validation
let receiptString = receiptData.base64EncodedString()
let allowedCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~"
let allowedCharSet = CharacterSet(charactersIn: allowedCharacters)
let encodedString = receiptString.addingPercentEncoding(withAllowedCharacters: allowedCharSet)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With