Receipt Data Validation Info Not Coming - ios

I have some issues with receipt validation. I want to purchased subscription expires date on show some places but do not get receipt data info.
do {
guard let receipt = Bundle.main.getReceiptData()?.base64EncodedString() else { return }
let requestDictionary = ["receipt-data": receipt]
guard JSONSerialization.isValidJSONObject(requestDictionary) else {
print("requestDictionary is not valid JSON")
return
}
let requestData = try JSONSerialization.data(withJSONObject: requestDictionary)
let validationURLString = "https://sandbox.itunes.apple.com/verifyReceipt"
guard let validationURL = URL(string: validationURLString) else {
print("the validation url could not be created, unlikely error")
return
}
let session = URLSession(configuration: URLSessionConfiguration.default)
var request = URLRequest(url: validationURL)
request.httpMethod = "POST"
request.cachePolicy = URLRequest.CachePolicy.reloadIgnoringCacheData
let task = session.uploadTask(with: request, from: requestData) { (data, response, error) in
if let data = data , error == nil {
do {
let appReceiptJSON = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
if let dict = appReceiptJSON as? NSDictionary {
print(dict["last_receipt_info"]!) // nil value ????
}
}catch {
print("JSON serialization failed with error: \(error.localizedDescription)")
}
} else {
print("Upload receipt data but something went wrong. Error: \(error?.localizedDescription)")
}
}
task.resume()
} catch let error as NSError {
print("JSON serialization failed. Error: \(error.localizedDescription)")
}
This function result only status code.. Why ??
The resources I have reviewed:
https://developer.apple.com/library/archive/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html#//apple_ref/doc/uid/TP40010573-CH104-SW1
https://medium.flatstack.com/auto-renewable-subscriptions-for-ios-45cb1045f4fa
https://savvyapps.com/blog/how-setup-test-auto-renewable-subscription-ios-app
Thank you for help to me..

Solved problem. Problem is not added shared secret key.
let requestDictionary = ["receipt-data": receipt, "password": "xxxxxxxxxxxxxxx"]
True way 👆🏻

Related

Get temperature of current location API swift

Can anyone help me with this API code. I got everything but one error fixed.
Here is my code:
let APIUrl = NSURL(string:"https://api.openweathermap.org/data/2.5/weather? lat=35&lon=150&appid=e7b2054dc37b1f464d912c00dd309595&units=Metric")
var request = URLRequest(url:APIUrl! as URL)
let task = URLSession.shared.dataTask(with: request as URLRequest)
guard let data = Data else {return}
let decoder = JSONDecoder()
let weatherData = try decoder.decode(MyWeather, from: data)
let ggtemp = weatherData.main?.temp
print(ggtemp, "THIS IS THE TEMP")
DispatchQueue.main.async {
tempDisplay.text = String (ggtemp) + " c"
}
}
Image of error
Once I fix the "let data = data" error, I get an error on the "let task = URLSesss..."
Any help would be appreciated. Thanks in advance.
Try this code
let APIUrl = NSURL(string:"https://api.openweathermap.org/data/2.5/weather?lat=35&lon=150&appid=e7b2054dc37b1f464d912c00dd309595&units=Metric")
var request = URLRequest(url:APIUrl! as URL)
request.httpMethod = "GET"
let dataTask = URLSession.shared.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error ?? "Error is empty.")
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse ?? "HTTP response is empty.")
}
guard let responseData = data else {
print("Error: did not receive data")
return
}
do {
let weatherData = try JSONDecoder().decode(MyWeather.self, from: responseData)
let ggtemp = weatherData.main?.temp
print(ggtemp, "THIS IS THE TEMP")
DispatchQueue.main.async {
tempDisplay.text = String (ggtemp) + " c"
}
} catch {
print("error parsing response from POST on /todos")
return
}
})
dataTask.resume()

How to check whether in subscription or not in iOS

I implemented subscription (auto renewable purchase). I want to check whether the user is still subscribed or not when my app launched ever time. I tried to use the below code but it returns always "success" even if user is not subscribed.
How can I check the user subscription status?
func receiptValidation() {
let SUBSCRIPTION_SECRET = "password"
let receiptPath = Bundle.main.appStoreReceiptURL?.path
if FileManager.default.fileExists(atPath: receiptPath!){
var receiptData:NSData?
do{
receiptData = try NSData(contentsOf: Bundle.main.appStoreReceiptURL!, options: NSData.ReadingOptions.alwaysMapped)
}
catch{
print("ERROR: " + error.localizedDescription)
}
//let receiptString = receiptData?.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))
let base64encodedReceipt = receiptData?.base64EncodedString(options: NSData.Base64EncodingOptions.endLineWithCarriageReturn)
print(base64encodedReceipt!)
let requestDictionary = ["receipt-data":base64encodedReceipt!,"password":SUBSCRIPTION_SECRET]
guard JSONSerialization.isValidJSONObject(requestDictionary) else { print("requestDictionary is not valid JSON"); return }
do {
let requestData = try JSONSerialization.data(withJSONObject: requestDictionary)
let validationURLString = "https://sandbox.itunes.apple.com/verifyReceipt" // this works but as noted above it's best to use your own trusted server
guard let validationURL = URL(string: validationURLString) else { print("the validation url could not be created, unlikely error"); return }
let session = URLSession(configuration: URLSessionConfiguration.default)
var request = URLRequest(url: validationURL)
request.httpMethod = "POST"
request.cachePolicy = URLRequest.CachePolicy.reloadIgnoringCacheData
let task = session.uploadTask(with: request, from: requestData) { (data, response, error) in
if let data = data , error == nil {
do {
let appReceiptJSON = try JSONSerialization.jsonObject(with: data)
print("success. here is the json representation of the app receipt: \(appReceiptJSON)")
// always through here
} catch let error as NSError {
print("json serialization failed with error: \(error)")
}
} else {
print("the upload task returned an error: \(error)")
}
}
task.resume()
} catch let error as NSError {
print("json serialization failed with error: \(error)")
}
}
}

URLSession parameters must be valid

This is the Url I am requesting for :
https://api.rss2json.com/v1/api.json?rss_url=https%3A%2F%2Fwww.theverge.com%2Frss%2Findex.xml&api_key=u3vl3sxdva2cch7jjyrnynvwlhjycjrizhcsgmmq
If you run this on browser, it works fine and gives a JSON response. However I get this error in the catch block on running the following code : URLSession parameters must be valid :
let callURL = URL.init(string: urlString) // urlString is the above url
var request = URLRequest.init(url: callURL!)
request.addValue(ContentType_ApplicationJson, forHTTPHeaderField: HTTPHeaderField_ContentType)
request.httpMethod = HTTPMethod_Get
let dataTask = urlSession.dataTask(with: request) { (data,response,error) in
if error != nil{
completion(nil)
return
}
do {
if let resultJson = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary{
print("Result",resultJson)
if let news = resultJson.value(forKeyPath: "articles") as? [NSDictionary]{
print("News",news)
completion(news)
return
}
}
} catch {
print("Error -> \(error)")
}
}
dataTask.resume()
I am not sure what is wrong in your code but I have tested your above code in playground I am getting news below is the code
import Foundation
import UIKit
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
let urlString = "https://api.rss2json.com/v1/api.json?rss_url=https%3A%2F%2Fwww.theverge.com%2Frss%2Findex.xml&api_key=u3vl3sxdva2cch7jjyrnynvwlhjycjrizhcsgmmq"
let callURL = URL.init(string: urlString) // urlString is the above url
var request = URLRequest.init(url: callURL!)
request.addValue("Content-Type", forHTTPHeaderField: "application/json")
request.httpMethod = "GET"
let dataTask = URLSession.shared.dataTask(with: request) { (data,response,error) in
let res = response as! HTTPURLResponse
print(res)
if error != nil{
print(error!)
return
}
do {
if let resultJson = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary{
print("Result",resultJson)
if let news = resultJson.value(forKeyPath: "articles") as? [NSDictionary]{
print("News",news)
return
}
}
} catch {
print("Error -> \(error)")
}
}
dataTask.resume()
This worked with me
let callURL = URL.init(string: "https://api.rss2json.com/v1/api.json?rss_url=https%3A%2F%2Fwww.theverge.com%2Frss%2Findex.xml&api_key=u3vl3sxdva2cch7jjyrnynvwlhjycjrizhcsgmmq") // urlString is the above url
var request = URLRequest.init(url: callURL!)
request.httpMethod = HTTPMethod.get.rawValue
let dataTask = URLSession.shared.dataTask(with: request) { (data,response,error) in
if error != nil{
print(error)
}
do {
if let resultJson = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary{
print("Result",resultJson)
if let news = resultJson.value(forKeyPath: "articles") as? [NSDictionary]{
print("News",news)
return
}
}
} catch {
print("Error -> \(error)")
}
}
dataTask.resume()
You don't need an URLRequest for a GET request, it's the default, just pass the URL
This works, however there is no value for key articles
let urlString = "https://api.rss2json.com/v1/api.json?rss_url=https%3A%2F%2Fwww.theverge.com%2Frss%2Findex.xml&api_key=u3vl3sxdva2cch7jjyrnynvwlhjycjrizhcsgmmq"
let callURL = URL(string: urlString)!
let dataTask = URLSession.shared.dataTask(with: callURL) { (data,response,error) in
if error != nil { return }
do {
if let resultJson = try JSONSerialization.jsonObject(with: data!) as? [String:Any] {
print("Result",resultJson)
// if let news = resultJson["articles"] as? [[String:Any]] {
// print("News",news.count)
// return
// }
}
} catch {
print("Error -> \(error)")
}
}
dataTask.resume()
And – as always – do not use NSDictionary and valueForKey in Swift and .allowFragments is pointless if the root object is a collection type.

Downloading web content with Swift 3

I am trying to download webcontent for a weather app that I am making. When I run the app the source code on the website does not appear on my Xcode. I also updated my info.plist to accept web content.
Do you have an idea on what the problem is and how I can solve it?
I have a copied my code below:
override func viewDidLoad() {
super.viewDidLoad()
let url = NSURL(string: "http://weather.weatherbug.com/weather-forecast/now/abuja")!
let request = NSMutableURLRequest(url:url as URL)
let task = URLSession.shared.dataTask(with: request as URLRequest) {
data, response, error in
if error != nil{
print(error.debugDescription)
}
else {
if let unwrappedData = data{
let dataString = NSString(data: unwrappedData, encoding: String.Encoding.utf8.rawValue)
print(dataString as Any)
}
}
}
task.resume()
}
Change your url to use https and it should work.
let url = NSURL(string: "https://weather.weatherbug.com/weather-forecast/now/abuja")!
Here's an example in Swift 4 for downloading a document and parsing as JSON:
// If you're doing this in an Xcode Playground, uncomment these lines:
// import XCPlayground
// XCPSetExecutionShouldContinueIndefinitely()
let url = URL(string: "http://json-schema.org/example/geo.json")!
let task = URLSession.shared.dataTask(with: url) {
data, response, error in
guard error == nil else { return }
guard data != nil else { return }
guard (response as? HTTPURLResponse)?.statusCode == 200 else { return }
do {
if let json = try JSONSerialization.jsonObject(with: data!, options: []) as? [String: Any] {
print(json)
}
} catch { return }
}
task.resume()
Use "if let" instead of only "let" and it should work.
if let url = URL(string:"http://weather.weatherbug.com/weather-forecast/now/abuja"){
let request = NSMutableURLRequest(url: url)
let task = URLSession.shared.dataTask(with: request as URLRequest){
data, responds, error in
if error != nil{
print(error!)
} else {
if let unwrappedData = data {
let dataString = NSString(data: unwrappedData, encoding: String.Encoding.utf8.rawValue)
print(dataString!)
DispatchQueue.main.sync(execute: {
})
}
}
}
task.resume()
}
Use
let myURLString = "http://weather.weatherbug.com/weather-forecast/now/abuja"
guard let myURL = URL(string: myURLString) else {
print("Error: \(myURLString) doesn't seem to be a valid URL")
return
}
do {
let myHTMLString = try String(contentsOf: myURL, encoding: .ascii)
print("HTML : \(myHTMLString)")
} catch let error {
print("Error: \(error)")
}
From Link

Implementing Receipt Validation in Swift 3

I am developing an iOS app in Swift 3 and trying to implement receipt validation following this tutorial: http://savvyapps.com/blog/how-setup-test-auto-renewable-subscription-ios-app. However, the tutorial seems to have been written using an earlier version of Swift, so I had to make several changes. Here is my receiptValidation() function:
func receiptValidation() {
let receiptPath = Bundle.main.appStoreReceiptURL?.path
if FileManager.default.fileExists(atPath: receiptPath!){
var receiptData:NSData?
do{
receiptData = try NSData(contentsOf: Bundle.main.appStoreReceiptURL!, options: NSData.ReadingOptions.alwaysMapped)
}
catch{
print("ERROR: " + error.localizedDescription)
}
let receiptString = receiptData?.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))
let postString = "receipt-data=" + receiptString! + "&password=" + SUBSCRIPTION_SECRET
let storeURL = NSURL(string:"https://sandbox.itunes.apple.com/verifyReceipt")!
let storeRequest = NSMutableURLRequest(url: storeURL as URL)
storeRequest.httpMethod = "POST"
storeRequest.httpBody = postString.data(using: .utf8)
let session = URLSession(configuration:URLSessionConfiguration.default)
let task = session.dataTask(with: storeRequest as URLRequest) { data, response, error in
do{
let jsonResponse:NSDictionary = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSDictionary
let expirationDate:NSDate = self.expirationDateFromResponse(jsonResponse: jsonResponse)!
self.updateIAPExpirationDate(date: expirationDate)
}
catch{
print("ERROR: " + error.localizedDescription)
}
}
task.resume()
}
}
The problem shows up when I try to call the expirationDateFromResponse() method. It turns out that the jsonResponse that gets passed to this method only contains: status = 21002;. I looked this up and it means "The data in the receipt-data property was malformed or missing." However, the device I'm testing on has an active sandbox subscription for the product, and the subscription seems to work correctly aside from this issue. Is there something else I still need to do to make sure the receiptData value will be read and encoded correctly, or some other issue that might be causing this problem?
EDIT:
I tried an alternate way of setting storeRequest.httpBody:
func receiptValidation() {
let receiptPath = Bundle.main.appStoreReceiptURL?.path
if FileManager.default.fileExists(atPath: receiptPath!){
var receiptData:NSData?
do{
receiptData = try NSData(contentsOf: Bundle.main.appStoreReceiptURL!, options: NSData.ReadingOptions.alwaysMapped)
}
catch{
print("ERROR: " + error.localizedDescription)
}
let receiptString = receiptData?.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0)) //.URLEncoded
let dict = ["receipt-data":receiptString, "password":SUBSCRIPTION_SECRET] as [String : Any]
var jsonData:Data?
do{
jsonData = try JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted)
}
catch{
print("ERROR: " + error.localizedDescription)
}
let storeURL = NSURL(string:"https://sandbox.itunes.apple.com/verifyReceipt")!
let storeRequest = NSMutableURLRequest(url: storeURL as URL)
storeRequest.httpMethod = "POST"
storeRequest.httpBody = jsonData!
let session = URLSession(configuration:URLSessionConfiguration.default)
let task = session.dataTask(with: storeRequest as URLRequest) { data, response, error in
do{
let jsonResponse:NSDictionary = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSDictionary
let expirationDate:NSDate = self.expirationDateFromResponse(jsonResponse: jsonResponse)!
self.updateIAPExpirationDate(date: expirationDate)
}
catch{
print("ERROR: " + error.localizedDescription)
}
}
task.resume()
}
}
However, when I run the app with this code, it hangs upon reaching the line jsonData = try JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted). It doesn't even make it to the catch block, it just stops doing anything. From what I've seen online, other people seem to have trouble using JSONSerialization.data to set the request httpBody in Swift 3.
Its working correctly with Swift 4
func receiptValidation() {
let SUBSCRIPTION_SECRET = "yourpasswordift"
let receiptPath = Bundle.main.appStoreReceiptURL?.path
if FileManager.default.fileExists(atPath: receiptPath!){
var receiptData:NSData?
do{
receiptData = try NSData(contentsOf: Bundle.main.appStoreReceiptURL!, options: NSData.ReadingOptions.alwaysMapped)
}
catch{
print("ERROR: " + error.localizedDescription)
}
//let receiptString = receiptData?.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))
let base64encodedReceipt = receiptData?.base64EncodedString(options: NSData.Base64EncodingOptions.endLineWithCarriageReturn)
print(base64encodedReceipt!)
let requestDictionary = ["receipt-data":base64encodedReceipt!,"password":SUBSCRIPTION_SECRET]
guard JSONSerialization.isValidJSONObject(requestDictionary) else { print("requestDictionary is not valid JSON"); return }
do {
let requestData = try JSONSerialization.data(withJSONObject: requestDictionary)
let validationURLString = "https://sandbox.itunes.apple.com/verifyReceipt" // this works but as noted above it's best to use your own trusted server
guard let validationURL = URL(string: validationURLString) else { print("the validation url could not be created, unlikely error"); return }
let session = URLSession(configuration: URLSessionConfiguration.default)
var request = URLRequest(url: validationURL)
request.httpMethod = "POST"
request.cachePolicy = URLRequest.CachePolicy.reloadIgnoringCacheData
let task = session.uploadTask(with: request, from: requestData) { (data, response, error) in
if let data = data , error == nil {
do {
let appReceiptJSON = try JSONSerialization.jsonObject(with: data)
print("success. here is the json representation of the app receipt: \(appReceiptJSON)")
// if you are using your server this will be a json representation of whatever your server provided
} catch let error as NSError {
print("json serialization failed with error: \(error)")
}
} else {
print("the upload task returned an error: \(error)")
}
}
task.resume()
} catch let error as NSError {
print("json serialization failed with error: \(error)")
}
}
}
I have updated the #user3726962's code, removing unnecessary NS'es and "crash operators". It should look more like Swift 3 now.
Before using this code be warned that Apple doesn't recommend doing direct [device] <-> [Apple server] validation and asks to do it [device] <-> [your server] <-> [Apple server]. Use only if you are not afraid to have your In-App Purchases hacked.
UPDATE: Made the function universal: it will attempt to validate receipt with Production first, if fails - it will repeat with Sandbox. It's a bit bulky, but should be quite self-contained and independent from 3rd-parties.
func tryCheckValidateReceiptAndUpdateExpirationDate() {
if let appStoreReceiptURL = Bundle.main.appStoreReceiptURL,
FileManager.default.fileExists(atPath: appStoreReceiptURL.path) {
NSLog("^A receipt found. Validating it...")
GlobalVariables.isPremiumInAmbiquousState = true // We will allow user to use all premium features until receipt is validated
// If we have problems validating the purchase - this is not user's fault
do {
let receiptData = try Data(contentsOf: appStoreReceiptURL, options: .alwaysMapped)
let receiptString = receiptData.base64EncodedString(options: [])
let dict = ["receipt-data" : receiptString, "password" : "your_shared_secret"] as [String : Any]
do {
let jsonData = try JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted)
if let storeURL = Foundation.URL(string:"https://buy.itunes.apple.com/verifyReceipt"),
let sandboxURL = Foundation.URL(string: "https://sandbox.itunes.apple.com/verifyReceipt") {
var request = URLRequest(url: storeURL)
request.httpMethod = "POST"
request.httpBody = jsonData
let session = URLSession(configuration: URLSessionConfiguration.default)
NSLog("^Connecting to production...")
let task = session.dataTask(with: request) { data, response, error in
// BEGIN of closure #1 - verification with Production
if let receivedData = data, let httpResponse = response as? HTTPURLResponse,
error == nil, httpResponse.statusCode == 200 {
NSLog("^Received 200, verifying data...")
do {
if let jsonResponse = try JSONSerialization.jsonObject(with: receivedData, options: JSONSerialization.ReadingOptions.mutableContainers) as? Dictionary<String, AnyObject>,
let status = jsonResponse["status"] as? Int64 {
switch status {
case 0: // receipt verified in Production
NSLog("^Verification with Production succesful, updating expiration date...")
self.updateExpirationDate(jsonResponse: jsonResponse) // Leaves isPremiumInAmbiquousState=true if fails
case 21007: // Means that our receipt is from sandbox environment, need to validate it there instead
NSLog("^need to repeat evrything with Sandbox")
var request = URLRequest(url: sandboxURL)
request.httpMethod = "POST"
request.httpBody = jsonData
let session = URLSession(configuration: URLSessionConfiguration.default)
NSLog("^Connecting to Sandbox...")
let task = session.dataTask(with: request) { data, response, error in
// BEGIN of closure #2 - verification with Sandbox
if let receivedData = data, let httpResponse = response as? HTTPURLResponse,
error == nil, httpResponse.statusCode == 200 {
NSLog("^Received 200, verifying data...")
do {
if let jsonResponse = try JSONSerialization.jsonObject(with: receivedData, options: JSONSerialization.ReadingOptions.mutableContainers) as? Dictionary<String, AnyObject>,
let status = jsonResponse["status"] as? Int64 {
switch status {
case 0: // receipt verified in Sandbox
NSLog("^Verification succesfull, updating expiration date...")
self.updateExpirationDate(jsonResponse: jsonResponse) // Leaves isPremiumInAmbiquousState=true if fails
default: self.showAlertWithErrorCode(errorCode: status)
}
} else { DebugLog("Failed to cast serialized JSON to Dictionary<String, AnyObject>") }
}
catch { DebugLog("Couldn't serialize JSON with error: " + error.localizedDescription) }
} else { self.handleNetworkError(data: data, response: response, error: error) }
}
// END of closure #2 = verification with Sandbox
task.resume()
default: self.showAlertWithErrorCode(errorCode: status)
}
} else { DebugLog("Failed to cast serialized JSON to Dictionary<String, AnyObject>") }
}
catch { DebugLog("Couldn't serialize JSON with error: " + error.localizedDescription) }
} else { self.handleNetworkError(data: data, response: response, error: error) }
}
// END of closure #1 - verification with Production
task.resume()
} else { DebugLog("Couldn't convert string into URL. Check for special characters.") }
}
catch { DebugLog("Couldn't create JSON with error: " + error.localizedDescription) }
}
catch { DebugLog("Couldn't read receipt data with error: " + error.localizedDescription) }
} else {
DebugLog("No receipt found even though there is an indication something has been purchased before")
NSLog("^No receipt found. Need to refresh receipt.")
self.refreshReceipt()
}
}
func refreshReceipt() {
let request = SKReceiptRefreshRequest()
request.delegate = self // to be able to receive the results of this request, check the SKRequestDelegate protocol
request.start()
}
This works for auto-renewable subscriptions. Haven't tested it with other kinds of subscriptions yet. Leave a comment if it works for you with some other subscription type.
//too low rep to comment
Yasin Aktimur, thanks for your answer, it's awesome. However, looking at Apple documentation on this, they say to connect to iTunes on a separate Queue. So it should look like this:
func receiptValidation() {
let SUBSCRIPTION_SECRET = "secret"
let receiptPath = Bundle.main.appStoreReceiptURL?.path
if FileManager.default.fileExists(atPath: receiptPath!){
var receiptData:NSData?
do{
receiptData = try NSData(contentsOf: Bundle.main.appStoreReceiptURL!, options: NSData.ReadingOptions.alwaysMapped)
}
catch{
print("ERROR: " + error.localizedDescription)
}
let base64encodedReceipt = receiptData?.base64EncodedString(options: NSData.Base64EncodingOptions.endLineWithCarriageReturn)
let requestDictionary = ["receipt-data":base64encodedReceipt!,"password":SUBSCRIPTION_SECRET]
guard JSONSerialization.isValidJSONObject(requestDictionary) else { print("requestDictionary is not valid JSON"); return }
do {
let requestData = try JSONSerialization.data(withJSONObject: requestDictionary)
let validationURLString = "https://sandbox.itunes.apple.com/verifyReceipt" // this works but as noted above it's best to use your own trusted server
guard let validationURL = URL(string: validationURLString) else { print("the validation url could not be created, unlikely error"); return }
let session = URLSession(configuration: URLSessionConfiguration.default)
var request = URLRequest(url: validationURL)
request.httpMethod = "POST"
request.cachePolicy = URLRequest.CachePolicy.reloadIgnoringCacheData
let queue = DispatchQueue(label: "itunesConnect")
queue.async {
let task = session.uploadTask(with: request, from: requestData) { (data, response, error) in
if let data = data , error == nil {
do {
let appReceiptJSON = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? NSDictionary
print("success. here is the json representation of the app receipt: \(appReceiptJSON)")
} catch let error as NSError {
print("json serialization failed with error: \(error)")
}
} else {
print("the upload task returned an error: \(error ?? "couldn't upload" as! Error)")
}
}
task.resume()
}
} catch let error as NSError {
print("json serialization failed with error: \(error)")
}
}
}
I struggled my head with the same problem. The issue is that this line:
let receiptString = receiptData?.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))
Returns an OPTIONAL and
jsonData = try JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted)
cannot handle optionals. So to fix it, simply substitute the first line of code with this:
let receiptString:String = receiptData?.base64EncodedString(options: NSData.Base64EncodingOptions.lineLength64Characters) as String!
And everything will work like charm!
I liked your answer and I just rewrote it in C# for those who are using it like me as I did not find a good source for the solution.
Thanks Again
For Consumable IAP
void ReceiptValidation()
{
var recPath = NSBundle.MainBundle.AppStoreReceiptUrl.Path;
if (File.Exists(recPath))
{
NSData recData;
NSError error;
recData = NSData.FromUrl(NSBundle.MainBundle.AppStoreReceiptUrl, NSDataReadingOptions.MappedAlways, out error);
var recString = recData.GetBase64EncodedString(NSDataBase64EncodingOptions.None);
var dict = new Dictionary<String,String>();
dict.TryAdd("receipt-data", recString);
var dict1 = NSDictionary.FromObjectsAndKeys(dict.Values.ToArray(), dict.Keys.ToArray());
var storeURL = new NSUrl("https://sandbox.itunes.apple.com/verifyReceipt");
var storeRequest = new NSMutableUrlRequest(storeURL);
storeRequest.HttpMethod = "POST";
var jsonData = NSJsonSerialization.Serialize(dict1, NSJsonWritingOptions.PrettyPrinted, out error);
if (error == null)
{
storeRequest.Body = jsonData;
var session = NSUrlSession.FromConfiguration(NSUrlSessionConfiguration.DefaultSessionConfiguration);
var tsk = session.CreateDataTask(storeRequest, (data, response, err) =>
{
if (err == null)
{
var rstr = NSJsonSerialization.FromObject(data);
}
else
{
// Check Error
}
});
tsk.Resume();
}else
{
// JSON Error Handling
}
}
}
Eventually I was able to solve the problem by having my app call a Lambda function written in Python, as shown in this answer. I'm still not sure what was wrong with my Swift code or how to do this entirely in Swift 3, but the Lambda function got the desired result in any case.

Resources