This file contains 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 characters
{ | |
"latest_receipt": "long_base64_receipt_here...", | |
"latest_receipt_info": { | |
"original_purchase_date_pst": "2020-04-01 01:12:42 America/Los_Angeles", | |
"quantity": "1", | |
"subscription_group_identifier": "20537620", | |
"unique_vendor_identifier": "A779C77C-571E-477D-B184-474DBD2F3F5C", | |
"original_purchase_date_ms": "1585728762000", | |
"expires_date_formatted": "2020-04-01 08:17:41 Etc/GMT", | |
"is_in_intro_offer_period": "true", |
This file contains 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 characters
{ | |
"status": 0, | |
"environment": "Sandbox", | |
"receipt": { | |
"receipt_type": "ProductionSandbox", | |
"adam_id": 0, | |
"app_item_id": 0, | |
"bundle_id": "com.apphud.subscriptionstest", | |
"application_version": "1", | |
"download_id": 0, |
This file contains 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 characters
func isEligibleForIntroductory(callback: @escaping (Bool) -> Void){ | |
guard let receiptUrl = Bundle.main.appStoreReceiptURL else { | |
callback(true) | |
return | |
} | |
#if DEBUG | |
let urlString = "https://sandbox.itunes.apple.com/verifyReceipt" | |
#else |
This file contains 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 characters
// | |
// IAPManager.swift | |
// http://apphud.com | |
// | |
// Created by Apphud on 04/01/2019. | |
// Copyright © 2019 Apphud. All rights reserved. | |
// | |
import UIKit | |
import StoreKit |
This file contains 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 characters
func expirationDateFor(_ identifier : String) -> Date?{ | |
return UserDefaults.standard.object(forKey: identifier) as? Date | |
} | |
let subscriptionDate = IAPManager.shared.expirationDateFor("YOUR_PRODUCT_ID") ?? Date() | |
let isActive = subscriptionDate > Date() |
This file contains 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 characters
func purchaseProduct(product : SKProduct, success: @escaping SuccessBlock, failure: @escaping FailureBlock){ | |
guard SKPaymentQueue.canMakePayments() else { | |
return | |
} | |
guard SKPaymentQueue.default().transactions.last?.transactionState != .purchasing else { | |
return | |
} | |
self.successBlock = success | |
self.failureBlock = failure |
This file contains 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 characters
// Starts products loading and sets transaction observer delegate | |
@objc func startWith(arrayOfIds : Set<String>!, sharedSecret : String){ | |
SKPaymentQueue.default().add(self) | |
self.sharedSecret = sharedSecret | |
self.productIds = arrayOfIds | |
loadProducts() | |
} | |
private func loadProducts(){ | |
let request = SKProductsRequest.init(productIdentifiers: productIds) |
This file contains 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 characters
private func parseReceipt(_ json : Dictionary<String, Any>) { | |
// It's the most simple way to get latest expiration date. Consider this code as for learning purposes. Do not use current code in production apps. | |
guard let receipts_array = json["latest_receipt_info"] as? [Dictionary<String, Any>] else { | |
self.refreshSubscriptionFailureBlock?(nil) | |
self.cleanUpRefeshReceiptBlocks() | |
return | |
} | |
for receipt in receipts_array { | |
let productID = receipt["product_id"] as! String | |
let formatter = DateFormatter() |
This file contains 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 characters
private func refreshReceipt(){ | |
let request = SKReceiptRefreshRequest(receiptProperties: nil) | |
request.delegate = self | |
request.start() | |
} | |
func requestDidFinish(_ request: SKRequest) { | |
// call refresh subscriptions method again with same blocks | |
if request is SKReceiptRefreshRequest { | |
refreshSubscriptionsStatus(callback: self.successBlock ?? {}, failure: self.failureBlock ?? {_ in}) |
This file contains 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 characters
func refreshSubscriptionsStatus(callback : @escaping SuccessBlock, failure : @escaping FailureBlock){ | |
// save blocks for further use | |
self.refreshSubscriptionSuccessBlock = callback | |
self.refreshSubscriptionFailureBlock = failure | |
guard let receiptUrl = Bundle.main.appStoreReceiptURL else { | |
refreshReceipt() | |
// do not call block yet | |
return | |
} | |
#if DEBUG |
NewerOlder