Last active
April 29, 2021 04:07
-
-
Save speaktoalvin/7c61bab7a401e22ee8c5 to your computer and use it in GitHub Desktop.
Revisions
-
Alvin Varghese revised this gist
Oct 14, 2015 . No changes.There are no files selected for viewing
-
Alvin Varghese revised this gist
Oct 14, 2015 . 1 changed file with 3 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,5 +1,6 @@ About Receipt Validation After you have processed your In-App Purchases you may want to verify the receipt with the App Store directly. You’ll get more details about the purchase so you can store that information in your database. There are two ways: first connecting to the Apple App Store directly within your app, and second send the receipt data to your own server and having your server perform the validation with the App Store server. The second is the easiest and most secure method when verifying receipts since it protects against Man-in-the-middle (MITM) attacks where its possible to get free in-app purchases in any app. Apple recommends you use a trusted server to communicate with the App Store. Using your own server lets you design your app to recognize and trust only your server, and lets you ensure that your server connects with the App Store server. It is not possible to build a trusted connection between a user’s device and the App Store directly because you don’t control either end of that connection. -
Alvin Varghese revised this gist
Oct 14, 2015 . 1 changed file with 5 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,5 @@ About Receipt Validation `After you have processed your In-App Purchases you may want to verify the receipt with the App Store directly. You’ll get more details about the purchase so you can store that information in your database. There are two ways: first connecting to the Apple App Store directly within your app, and second send the receipt data to your own server and having your server perform the validation with the App Store server. The second is the easiest and most secure method when verifying receipts since it protects against Man-in-the-middle (MITM) attacks where its possible to get free in-app purchases in any app. Apple recommends you use a trusted server to communicate with the App Store. Using your own server lets you design your app to recognize and trust only your server, and lets you ensure that your server connects with the App Store server. It is not possible to build a trusted connection between a user’s device and the App Store directly because you don’t control either end of that connection.` -
Alvin Varghese revised this gist
Oct 14, 2015 . No changes.There are no files selected for viewing
-
Alvin Varghese revised this gist
Oct 14, 2015 . No changes.There are no files selected for viewing
-
Alvin Varghese revised this gist
Oct 14, 2015 . 1 changed file with 16 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,16 @@ Status Codes 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. -
Alvin Varghese revised this gist
Oct 14, 2015 . 1 changed file with 40 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,40 @@ PHP Server Side Code <?php function getReceiptData($receipt) { $fh = fopen('showme.txt',w); fwrite($fh,$receipt); fclose($fh); $endpoint = 'https://sandbox.itunes.apple.com/verifyReceipt'; $ch = curl_init($endpoint); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $receipt); $response = curl_exec($ch); $errno = curl_errno($ch); $errmsg = curl_error($ch); curl_close($ch); $msg = $response.' - '.$errno.' - '.$errmsg; echo $response; } foreach ($_POST as $key=>$value){ $newcontent .= $key.' '.$value; } $new = trim($newcontent); $new = trim($newcontent); $new = str_replace('_','+',$new); $new = str_replace(' =','==',$new); if (substr_count($new,'=') == 0){ if (strpos('=',$new) === false){ $new .= '='; } } $new = '{"receipt-data":"'.$new.'","password":"<INSERT YOUR IN-APP PURCHASE SHARED SECRET HERE>"}'; $info = getReceiptData($new); ?> -
Alvin Varghese revised this gist
Oct 14, 2015 . 1 changed file with 18 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,18 @@ Ruby Server Side Code # This code call the apple sandbox in app purchase server to validate a receipt using Ruby Code. # Run this using "ruby verifyReceipt.rb" # By @sauloarruda (http://twitter.com/sauloarruda) require 'net/http' # This core reads an file called receipt (see an example bellow) params_json = "{ \"receipt-data\": \"#{open("./receipt").read}\" }" # Use net/http to post to apple sandbox server uri = URI("https://sandbox.itunes.apple.com") # Use "https://buy.itunes.apple.com" for production Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') do |http| response = http.post('/verifyReceipt', params_json) # Puts the result! (see an example below - result.json) puts response.body end -
Alvin Varghese revised this gist
Oct 14, 2015 . 1 changed file with 20 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,20 @@ // Receipt Example { "receipt": { "original_purchase_date_pst": "2012-04-30 08:05:55 America/Los_Angeles", "original_transaction_id": "1000000046178817", "original_purchase_date_ms": "1335798355868", "transaction_id": "1000000046178817", "quantity": "1", "product_id": "br.com.jera.Example", "bvrs": "20120427", "purchase_date_ms": "1335798355868", "purchase_date": "2012-04-30 15:05:55 Etc/GMT", "original_purchase_date": "2012-04-30 15:05:55 Etc/GMT", "purchase_date_pst": "2012-04-30 08:05:55 America/Los_Angeles", "bid": "br.com.jera.Example", "item_id": "521129812" }, "status": 0 } -
Alvin Varghese revised this gist
Oct 14, 2015 . No changes.There are no files selected for viewing
-
Alvin Varghese renamed this gist
Oct 14, 2015 . 1 changed file with 0 additions and 0 deletions.There are no files selected for viewing
File renamed without changes. -
Alvin Varghese created this gist
Oct 14, 2015 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,316 @@ import UIKit import StoreKit //MARK: SKProductsRequestDelegate extension IAPHelpers : SKProductsRequestDelegate { func productsRequest(request: SKProductsRequest, didReceiveResponse response: SKProductsResponse) { if response.products.count > 0 { let validProduct : SKProduct = response.products[0] switch validProduct.productIdentifier { case PRODUCT_IDENTIFIERS.k_10_WORDS_PRODUCT_IDENTIFIER.rawValue : self.buyProduct(validProduct) case PRODUCT_IDENTIFIERS.k_20_WORDS_PRODUCT_IDENTIFIER.rawValue : self.buyProduct(validProduct) case PRODUCT_IDENTIFIERS.k_ALL_WORDS_PRODUCT_IDENTIFIER.rawValue : self.buyProduct(validProduct) default : self.alertThis("Sorry", message: "No products recieved from server", current: self.currentViewController) } } else { self.alertThis("Sorry", message: "Can't make payments, Please check in settings.", current: self.currentViewController) } } } extension IAPHelpers : SKRequestDelegate { func requestDidFinish(request: SKRequest) { } func request(request: SKRequest, didFailWithError error: NSError) { self.alertThis("Sorry", message: "\(error.localizedDescription)", current: self.currentViewController) } } //MARK: SKPaymentTransactionObserver extension IAPHelpers : SKPaymentTransactionObserver { func paymentQueue(queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { self.handlingTransactions(transactions) } func paymentQueueRestoreCompletedTransactionsFinished(queue: SKPaymentQueue) { if queue.transactions.count == 0 { self.alertThis("Sorry", message: "You didnt make any purchase to restiore, Please do a purchase first.", current: self.currentViewController) } else { self.handlingTransactions(queue.transactions) } } func paymentQueue(queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: NSError) { self.alertThis("Sorry", message: "\(error.localizedDescription)", current: self.currentViewController) } } enum PRODUCT_IDENTIFIERS : String { //MARK: Product ID's case k_10_WORDS_PRODUCT_IDENTIFIER = "YOUR_PRODUCT_IDENTIFIER_10" case k_20_WORDS_PRODUCT_IDENTIFIER = "YOUR_PRODUCT_IDENTIFIER_20" case k_ALL_WORDS_PRODUCT_IDENTIFIER = "YOUR_PRODUCT_IDENTIFIER_ALL" case k_RESTORE_WORDS_PRODUCT_IDENTIFIER = "YOUR_PRODUCT_IDENTIFIER_RESTOREE" } enum USER_DEFAULTS_IDENTIFIERS : String { case TEN_WORDS_PRODUCT_IDENTIFIER = "10_WORDS_IDENTIFIER" case TWENTY_WORDS_PRODUCT_IDENTIFIER = "20_WORDS_IDENTIFIER" case ALL_WORDS_PRODUCT_IDENTIFIER = "ALL_WORDS_IDENTIFIER" } enum ReceiptURL : String { case sandbox = "https://sandbox.itunes.apple.com/verifyReceipt" case production = "https://buy.itunes.apple.com/verifyReceipt" case myServer = "your server" } protocol StoreRequestIAPPorotocol { func transactionCompletedForRequest(PRODUCT_ID : String) } class IAPHelpers: NSObject { //MARK: Variables var delegate : StoreRequestIAPPorotocol! var currentViewController : UIViewController! //MARK: - Creating sharedInstance class var sharedInstance: IAPHelpers { struct Static { static var sharedInstance: IAPHelpers? static var token: dispatch_once_t = 0 } dispatch_once(&Static.token) { Static.sharedInstance = IAPHelpers() } return Static.sharedInstance! } //MARK: Product Request func productRquestStarted(productReferenceName : String) { if (SKPaymentQueue.canMakePayments()) { if productReferenceName == PRODUCT_IDENTIFIERS.k_RESTORE_WORDS_PRODUCT_IDENTIFIER.rawValue { self.restorePurchases() } else { let productID : Set<String> = [productReferenceName] let productsRequest : SKProductsRequest = SKProductsRequest(productIdentifiers: productID) productsRequest.delegate = self productsRequest.start() } } else { // PRESENT A USER FOR UI about not being able to make payments. } } //MARK: Buy Product - Payment Section func buyProduct(product : SKProduct) { let payment = SKPayment(product: product) SKPaymentQueue.defaultQueue().addTransactionObserver(self) SKPaymentQueue.defaultQueue().addPayment(payment) } //MARK: Restore Transaction func restoreTransaction(transaction : SKPaymentTransaction) { self.deliverProduct(transaction.payment.productIdentifier) } //MARK: Restore Purchases func restorePurchases() { SKPaymentQueue.defaultQueue().addTransactionObserver(self) SKPaymentQueue.defaultQueue().restoreCompletedTransactions() } //MARK: - Showing UIAlertView func alertThis(title : String, message : String, current : UIViewController) { let alertView : UIAlertController = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert) alertView.addAction(UIAlertAction(title: "Okay", style: UIAlertActionStyle.Default, handler: { _ in })) current.presentViewController(alertView, animated: true, completion: nil) } //MARK: Transaction Observer Handler func handlingTransactions(transactions : [AnyObject]) { for transaction in transactions { if let paymentTransaction : SKPaymentTransaction = transaction as? SKPaymentTransaction { switch paymentTransaction.transactionState { case .Purchasing : print("Purchasing") case .Purchased : self.deliverProduct(paymentTransaction.payment.productIdentifier) SKPaymentQueue.defaultQueue().finishTransaction(transaction as! SKPaymentTransaction) break case .Failed: SKPaymentQueue.defaultQueue().finishTransaction(transaction as! SKPaymentTransaction) break case .Restored: print("Restored") self.restoreTransaction(paymentTransaction) break default: print("DEFAULT") // PRESENT A USER FOR UI about not being able to make payments. break } } } } //MARK: Deliver Product func deliverProduct(product : String) { self.validateReceipt { status in if status { self.delegate.transactionCompletedForRequest(product) } else { print("Something bad happened") } } } //MARK: Receipt Validation func validateReceipt(completion : (status : Bool) -> ()) { let receiptUrl = NSBundle.mainBundle().appStoreReceiptURL let receipt: NSData = NSData(contentsOfURL: receiptUrl!)! let receiptdata: NSString = receipt.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0)) let dict = ["receipt-data" : receiptdata] let jsonData = try! NSJSONSerialization.dataWithJSONObject(dict, options: NSJSONWritingOptions(rawValue: 0)) let request = NSMutableURLRequest(URL: NSURL(string: ReceiptURL.sandbox.rawValue)!) let session = NSURLSession.sharedSession() request.HTTPMethod = "POST" request.HTTPBody = jsonData let task = session.dataTaskWithRequest(request, completionHandler: { data, response, error in if let dataR = data { self.handleData(dataR, completion: { status in completion(status: status) }) } }) task.resume() } func handleData(responseDatas : NSData, completion : (status : Bool) -> ()) { if let json = try! NSJSONSerialization.JSONObjectWithData(responseDatas, options: NSJSONReadingOptions.MutableLeaves) as? NSDictionary { if let value = json.valueForKeyPath("status") as? Int { if value == 0 { completion(status: true) } else { completion(status: false) } } else { completion(status: false) } } } }