// // EbayWebServiceManager.swift // Vendoo // // Created by Okechi Onyeje on 6/3/16. // Copyright © 2016 Okechi Onyeje. All rights reserved. // import Foundation import Locksmith protocol EbayWebServiceManagerDelegate { func signInUser(string: String!) } class EbayWebServiceManager: NSObject { //API Manager class variables //----------------------------------------------// let baseURL = "https://api.sandbox.ebay.com/ws/api.dll" private var apiKey: String! private var devKey: String! private var certID: String! private var mutableData: NSMutableData = NSMutableData() private var currentElementName:NSString = "" private var catID: NSString! private var catLevel: NSString! private var currentCategory: String! private var catDetailLevel: Int! = 1 private var userEmail:String = (NSUserDefaults.standardUserDefaults().objectForKey("email") as? String)! private var signinURL: String! private var xmlResponseDictionary: Dictionary = Dictionary() var delegate: EbayWebServiceManagerDelegate? var isAuthorized: Bool = NSUserDefaults.standardUserDefaults().boolForKey("ebayAuthorized") static var settingsDictionary = Dictionary() private var subCategories = Dictionary>() private var completion: ServiceResponse! private var isGettingSubCategories: Bool = false private var isRevisingListing: Bool = false private var requestGroup: dispatch_group_t = dispatch_group_create() var isGettingNotification: Bool = false private var qtySold: Int! private var currItemQuantity: Int! //---------------------------------------------// override init(){ super.init() if let path = NSBundle.mainBundle().pathForResource("Services", ofType: "plist"), dict = NSDictionary(contentsOfFile: path) as? [String: AnyObject] { self.apiKey = ((dict["Ebay"] as! Dictionary)["consumerKey"] as! String) self.devKey = ((dict["Ebay"] as! Dictionary)["devID"] as! String) self.certID = ((dict["Ebay"] as! Dictionary)["CertID"] as! String) self.signinURL = ((dict["Ebay"] as! Dictionary)["signin"] as! String) if(self.isAuthorized){ let dictionary = Locksmith.loadDataForUserAccount(self.userEmail, inService: "vendoo_oauth_ebay") self.xmlResponseDictionary["eBayAuthToken"] = dictionary?["ebay_oauth"] as? String } //set default posting settings for ebay if(NSUserDefaults.standardUserDefaults().boolForKey("ebayDefaultsOverriden")) { EbayWebServiceManager.settingsDictionary = (NSUserDefaults.standardUserDefaults().objectForKey("ebaySettings") as? Dictionary)! } else{ EbayWebServiceManager.settingsDictionary = ([ "listingType":"FixedPriceItem", "condition":"1500", "listingDuration":"Days_7", "ebaySite":"US", "currency":"USD", "shipping": [ "shippingType": "Flat", "freeShipping": "true", "shippingServicePriority": "1", "shippingService":"UPSGround", "shippingServiceAdditionalCost":"0.00" ], "payment":"PayPal", "return":"ReturnsNotAccepted", "postalCode":"21216" ] as Dictionary) NSUserDefaults.standardUserDefaults().setObject(EbayWebServiceManager.settingsDictionary, forKey: "ebaySettings") NSUserDefaults.standardUserDefaults().setBool(true, forKey: "ebayDefaultsOverriden") } } } func reinit() { EbayWebServiceManager.settingsDictionary = (NSUserDefaults.standardUserDefaults().objectForKey("ebaySettings") as? Dictionary)! } } //MARK: - authentication methods extension EbayWebServiceManager { private func requestMaker(soapMessage: String!, theRequest: NSMutableURLRequest!) { let msgLength = soapMessage.characters.count theRequest.addValue("text/xml", forHTTPHeaderField: "Content-Type") theRequest.addValue(String(msgLength), forHTTPHeaderField: "Content-Length") theRequest.addValue("967", forHTTPHeaderField: "X-EBAY-API-COMPATIBILITY-LEVEL") theRequest.addValue(self.devKey, forHTTPHeaderField: "X-EBAY-API-DEV-NAME") theRequest.addValue(self.certID, forHTTPHeaderField: "X-EBAY-API-CERT-NAME") theRequest.addValue(self.apiKey, forHTTPHeaderField: "X-EBAY-API-APP-NAME") theRequest.addValue("0", forHTTPHeaderField: "X-EBAY-API-SITEID") theRequest.addValue("", forHTTPHeaderField: "SOAPAction") theRequest.HTTPMethod = "POST" theRequest.HTTPBody = soapMessage.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) // or false let connection = NSURLConnection(request: theRequest, delegate: self, startImmediately: true) connection!.start() if (connection == true) { var mutableData : Void = NSMutableData.initialize() } } func authorizeApp(onComplete: ServiceResponse){ let soapMessage = "" + "" + "Kroleo-Kroleo-Vendoo-S-ktqeobkk" + "" let url = NSURL(string: self.baseURL) let theRequest = NSMutableURLRequest(URL: url!) theRequest.addValue("GetSessionID", forHTTPHeaderField: "X-EBAY-API-CALL-NAME") self.completion = onComplete self.requestMaker(soapMessage, theRequest: theRequest) } func deauthorizeApp(){ //@TODO: Add an alert to let user know if their account has not been deauthorized and keep them logged in. var soapMessage = "" + "" + "" + "\(self.xmlResponseDictionary["eBayAuthToken"]!)" + "" soapMessage = soapMessage + "en_US" + "High​" let url = NSURL(string: self.baseURL) let theRequest = NSMutableURLRequest(URL: url!) theRequest.addValue("RevokeToken", forHTTPHeaderField: "X-EBAY-API-CALL-NAME") self.requestMaker(soapMessage, theRequest: theRequest) //delete token from locksmith keychain do{ try Locksmith.deleteDataForUserAccount(self.userEmail, inService: "vendoo_oauth_ebay") print("account credentials deleted for ebay") NSUserDefaults.standardUserDefaults().setBool(false, forKey: "ebayAuthorized") self.isAuthorized = false } catch{ (error) print(error) //could not save data to keychain print("account credentials could not be deleted") } } private func getPaypalEmail(onCompletion: ServiceResponse){ var soapMessage = "" + "" + "" + "\(self.xmlResponseDictionary["eBayAuthToken"] as! String)" + "" soapMessage = soapMessage + "true" + "true" + "true" + "true" + "true" + "true" + "true" + "true" + "true" + "true" + "true" + "true" + "true" + "true" + "true" + "true" + "true" + "true" + "true" + "true" + "en_US" + " string High" + "" let url = NSURL(string: self.baseURL) let theRequest = NSMutableURLRequest(URL: url!) theRequest.addValue("GetUserPreferences", forHTTPHeaderField: "X-EBAY-API-CALL-NAME") self.completion = onCompletion self.requestMaker(soapMessage, theRequest: theRequest) } func addImagesToListing(listingID: String, imageURLs: [String], listingOnCompletion: ServiceResponse, body: [String:String],onCompletion: ServiceResponse) { listItem(body, imageUrls: imageURLs, completion: listingOnCompletion) /* var soapMessage = "" + "" + "" + "\(self.xmlResponseDictionary["eBayAuthToken"] as! String)" + "" soapMessage = soapMessage + "en_US" + "" + "\(listingID)" for i in 0...(imageURLs.count - 1) { soapMessage = soapMessage + "" + "\(imageURLs[i].stringByReplacingOccurrencesOfString("&", withString: "&"))" + "" } soapMessage = soapMessage + "" let url = NSURL(string: self.baseURL) let theRequest = NSMutableURLRequest(URL: url!) theRequest.addValue("ReviseItem", forHTTPHeaderField: "X-EBAY-API-CALL-NAME") self.completion = onCompletion self.requestMaker(soapMessage, theRequest: theRequest) */ } func listItem(params: [String: String], imageUrls: [String], completion: ServiceResponse?){ var dict = EbayWebServiceManager.settingsDictionary var emailRetrievalGroup = dispatch_group_create() var soapMessage = "" + "" + "" + "\(self.xmlResponseDictionary["eBayAuthToken"] as! String)" + "" if((dict["payment"] as? String) == "PayPal") { dispatch_group_enter(emailRetrievalGroup) if((EbayWebServiceManager.settingsDictionary["paypal_email"]) == nil) { self.getPaypalEmail({ (any, error) -> Void in dispatch_group_leave(emailRetrievalGroup) }) }else { dispatch_group_leave(emailRetrievalGroup) } dispatch_group_notify(emailRetrievalGroup, dispatch_get_main_queue(), { soapMessage = soapMessage + "" + "\(params["title"] as String!)" + "\(params["description"] as String!)" + "" + "\(params["category_id"] as String!)" + "" + "\(params["price"] as String!)" + "\(dict["condition"] as! String!)" + "US" + "USD" + "3" + "\(dict["listingDuration"] as! String!)" + "\(dict["listingType"] as! String!)" + "\(dict["payment"] as! String!)" + "\(dict["paypal_email"] as! String!)" for i in 0...(0/*imageUrls.count - 1*/) { //will replace with actual count once everything is working with images soapMessage = soapMessage + "\(imageUrls[i].stringByReplacingOccurrencesOfString("&", withString: "&"))" } soapMessage = soapMessage + "\(dict["postalCode"] as! String!)" + "\(params["quantity"] as String!)" + "" + "\(dict["return"] as! String!)" + "Vendoo requires seller to set up return policy manually." + "" + "" + "" + "\(dict["shipping"]!["shippingServicePriority"] as! String!)" + "\(dict["shipping"]!["shippingService"] as! String!)" + "0.00" + "\(dict["shipping"]!["shippingServiceAdditionalCost"] as! String!)" + "" + "" + "US" + "" print(soapMessage) let url = NSURL(string: self.baseURL) let theRequest = NSMutableURLRequest(URL: url!) theRequest.addValue("AddItem", forHTTPHeaderField: "X-EBAY-API-CALL-NAME") self.completion = completion! self.requestMaker(soapMessage, theRequest: theRequest) }) } } func fetchToken() { let soapMessage = "" + "\(self.xmlResponseDictionary["SessionID"] as! String!)" let url = NSURL(string: self.baseURL) let theRequest = NSMutableURLRequest(URL: url!) theRequest.addValue("FetchToken", forHTTPHeaderField: "X-EBAY-API-CALL-NAME") //dispatch_group_enter(self.requestGroup) self.requestMaker(soapMessage, theRequest: theRequest) } func getSubCategories(selectedCategory: String, detailLevel: Int, catCode: String, onCompletion: ServiceResponse) { self.isGettingSubCategories = true //var dict = EbayWebServiceManager.settingsDictionary["categories"]! var soapMessage = "" + "" + "" + "\(self.xmlResponseDictionary["eBayAuthToken"] as! String)" soapMessage = soapMessage + "" + "\(catCode)" + "0" + "ReturnAll\(detailLevel)" + "" let url = NSURL(string: self.baseURL) let theRequest = NSMutableURLRequest(URL: url!) theRequest.addValue("GetCategories", forHTTPHeaderField: "X-EBAY-API-CALL-NAME") self.completion = onCompletion self.catDetailLevel = detailLevel self.requestMaker(soapMessage, theRequest: theRequest) } func getListingInfo(listingID: String, onComplete: ServiceResponse?){ var soapMessage = "" + "" + "" + "\(self.xmlResponseDictionary["eBayAuthToken"] as! String)" + "" soapMessage = soapMessage + "\(listingID)" + "" let url = NSURL(string: self.baseURL) let theRequest = NSMutableURLRequest(URL: url!) theRequest.addValue("GetItem", forHTTPHeaderField: "X-EBAY-API-CALL-NAME") self.completion = onComplete self.isGettingNotification = true self.requestMaker(soapMessage, theRequest: theRequest) } } extension EbayWebServiceManager: NSURLConnectionDelegate { func connection(connection: NSURLConnection!, didReceiveResponse response: NSURLResponse!) { mutableData.length = 0; print(response) } func connection(connection: NSURLConnection!, didReceiveData data: NSData!) { mutableData.appendData(data) print(data) } func connectionDidFinishLoading(connection: NSURLConnection!) { let response = NSString(data: mutableData, encoding: NSUTF8StringEncoding) print(response) var response_fixed = response!.stringByReplacingOccurrencesOfString("&", withString: "and") let data_fixed = response_fixed.dataUsingEncoding(NSUTF8StringEncoding) print(response_fixed) let xmlParser = NSXMLParser(data: data_fixed!) xmlParser.delegate = self xmlParser.parse() xmlParser.shouldResolveExternalEntities = true } func connection(connection: NSURLConnection, willSendRequestForAuthenticationChallenge challenge: NSURLAuthenticationChallenge) { challenge.sender!.performDefaultHandlingForAuthenticationChallenge!(challenge) } } extension EbayWebServiceManager: NSXMLParserDelegate { func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) { currentElementName = elementName } func parser(parser: NSXMLParser, foundCharacters string: String) { switch (self.currentElementName) { /* This case will be developed further to allow error handling with ebay and stateful operations */ case "Ack": if(string == "Success") { if(isRevisingListing) { self.completion(nil, nil) self.isRevisingListing = false } } break /* Used to authorize user to allow posting new listings to ebay sites and fetches all necessary information when posting to Ebay */ case "SessionID": self.xmlResponseDictionary["SessionID"] = string var str = "\(signinURL)&SessID=\(string)" self.signinURL = str print(self.signinURL) print((self.currentElementName as String) + " : " + string) self.delegate?.signInUser(self.signinURL) break case "eBayAuthToken": self.xmlResponseDictionary["eBayAuthToken"] = string print(string) //save token to locksmith keychain do{ try Locksmith.saveData(["ebay_oauth" : string], forUserAccount: self.userEmail, inService: "vendoo_oauth_ebay") print("account credentials saved") NSUserDefaults.standardUserDefaults().setBool(true, forKey: "ebayAuthorized") self.isAuthorized = true //self.completion(nil, nil) //Test authentication let soapmessage = "" + "" + "\(string)" + "" /* * boolean ItemIDType (string) string DetailLevelCodeType string string string WarningLevelCodeType */ let url = NSURL(string: self.baseURL) let theRequest = NSMutableURLRequest(URL: url!) theRequest.addValue("GetUser", forHTTPHeaderField: "X-EBAY-API-CALL-NAME") self.requestMaker(soapmessage, theRequest: theRequest) } catch{ (error) print(error) //could not save data to keychain print("account credentials could not be saved") } break case "DefaultPayPalEmailAddress": var dict = (EbayWebServiceManager.settingsDictionary) dict["paypal_email"] = string EbayWebServiceManager.settingsDictionary = dict NSUserDefaults.standardUserDefaults().setObject(EbayWebServiceManager.settingsDictionary, forKey: "ebaySettings") self.completion(nil, nil) break case "UserID" : if(!self.isGettingNotification) { print(string) EbayWebServiceManager.settingsDictionary["userID"] = string NSUserDefaults.standardUserDefaults().setObject(EbayWebServiceManager.settingsDictionary, forKey: "ebaySettings") print(self.xmlResponseDictionary["eBayAuthToken"]) let soapmessage = "" + "" + "\(self.xmlResponseDictionary["eBayAuthToken"] as! String)" + "0ReturnAll1" + "" let url = NSURL(string: self.baseURL) let theRequest = NSMutableURLRequest(URL: url!) theRequest.addValue("GetCategories", forHTTPHeaderField: "X-EBAY-API-CALL-NAME") self.completion(nil, nil) self.requestMaker(soapmessage, theRequest: theRequest) } break /* Used to build both the top level categories, and subcategories on request during item listing workflow */ case "CategoryID" : self.catID = string break case "CategoryLevel": self.catLevel = string break case "CategoryName" : if (!self.isGettingNotification) { if(!self.isGettingSubCategories) { if((EbayWebServiceManager.settingsDictionary["categories"]) == nil) { EbayWebServiceManager.settingsDictionary["categories"] = Dictionary>() } var dict: Dictionary> = (EbayWebServiceManager.settingsDictionary["categories"]!) as! Dictionary> dict[string] = ["cat_id": self.catID, "isLeaf": false, "level": self.catLevel] EbayWebServiceManager.settingsDictionary["categories"] = dict NSUserDefaults.standardUserDefaults().setObject(EbayWebServiceManager.settingsDictionary, forKey: "ebaySettings") } var dict: Dictionary> = (EbayWebServiceManager.settingsDictionary["categories"]!) as! Dictionary> dict[string] = ["cat_id": self.catID, "isLeaf": false, "level": self.catLevel] EbayWebServiceManager.settingsDictionary["categories"] = dict NSUserDefaults.standardUserDefaults().setObject(EbayWebServiceManager.settingsDictionary, forKey: "ebaySettings") } else { if "\(self.catDetailLevel)" == self.catLevel { if((self.subCategories[string] == nil )){ self.subCategories[string] = ["cat_id": self.catID, "isLeaf": false, "level": self.catLevel] } } self.currentCategory = string } break case "LeafCategory": if (!self.isGettingSubCategories) { var dict: Dictionary> = (EbayWebServiceManager.settingsDictionary["categories"]!) as! Dictionary> dict[self.currentCategory]!["isLeaf"] = true } else { if (self.catDetailLevel == Int((self.subCategories[self.currentCategory]!["level"] as! String))) { self.subCategories[self.currentCategory]!["isLeaf"] = true } } break case "CategoryCount": if(self.isGettingSubCategories){ self.completion(self.subCategories, nil) self.isGettingSubCategories = false self.subCategories = Dictionary>() } break /* Used during the listing creation process to save the new listing id, as well as adding pictures to the new listing */ case "ItemID": if (!self.isGettingNotification) { self.completion(string, nil) } break case "ReviseItemResponse": self.isRevisingListing = true break /* Used for retrieving listing info for notifications */ case "Quantity": if self.isGettingNotification { print("Quantity: \(string)") self.currItemQuantity = Int(string) } break case "QuantitySold": if self.isGettingNotification { print("QuantitySold: \(string)") self.qtySold = Int(string) } break case "ListingStatus": if self.isGettingNotification { print("ListingStatus: \(string)") self.completion(["status":string, "quantitySold": self.qtySold, "itemQuantity": self.currItemQuantity], nil) self.qtySold = nil self.currItemQuantity = nil } break default: //print(string) print("\(self.currentElementName):\(string)") } } }