//
|
// InAppReceiptVerificator.swift
|
// SwiftyStoreKit
|
//
|
// Created by Andrea Bizzotto on 16/05/2017.
|
// Copyright (c) 2017 Andrea Bizzotto (bizz84@gmail.com)
|
//
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// of this software and associated documentation files (the "Software"), to deal
|
// in the Software without restriction, including without limitation the rights
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
// copies of the Software, and to permit persons to whom the Software is
|
// furnished to do so, subject to the following conditions:
|
//
|
// The above copyright notice and this permission notice shall be included in
|
// all copies or substantial portions of the Software.
|
//
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
// THE SOFTWARE.
|
|
import Foundation
|
|
class InAppReceiptVerificator: NSObject {
|
|
let appStoreReceiptURL: URL?
|
init(appStoreReceiptURL: URL? = Bundle.main.appStoreReceiptURL) {
|
self.appStoreReceiptURL = appStoreReceiptURL
|
}
|
|
var appStoreReceiptData: Data? {
|
guard let receiptDataURL = appStoreReceiptURL,
|
let data = try? Data(contentsOf: receiptDataURL) else {
|
return nil
|
}
|
return data
|
}
|
|
private var receiptRefreshRequest: InAppReceiptRefreshRequest?
|
|
/**
|
* Verify application receipt.
|
* - Parameter validator: Validator to check the encrypted receipt and return the receipt in readable format
|
* - Parameter forceRefresh: If true, refreshes the receipt even if one already exists.
|
* - Parameter refresh: closure to perform receipt refresh (this is made explicit for testability)
|
* - Parameter completion: handler for result
|
*/
|
@discardableResult
|
public func verifyReceipt(using validator: ReceiptValidator,
|
forceRefresh: Bool,
|
refresh: InAppReceiptRefreshRequest.ReceiptRefresh = InAppReceiptRefreshRequest.refresh,
|
completion: @escaping (VerifyReceiptResult) -> Void) -> InAppRequest? {
|
|
return fetchReceipt(forceRefresh: forceRefresh, refresh: refresh) { result in
|
switch result {
|
case .success(let receiptData):
|
self.verify(receiptData: receiptData, using: validator, completion: completion)
|
case .error(let error):
|
completion(.error(error: error))
|
}
|
}
|
}
|
|
/**
|
* Fetch application receipt. This method does two things:
|
* * If the receipt is missing, refresh it
|
* * If the receipt is available or is refreshed, validate it
|
* - Parameter forceRefresh: If true, refreshes the receipt even if one already exists.
|
* - Parameter refresh: closure to perform receipt refresh (this is made explicit for testability)
|
* - Parameter completion: handler for result
|
*/
|
@discardableResult
|
public func fetchReceipt(forceRefresh: Bool,
|
refresh: InAppReceiptRefreshRequest.ReceiptRefresh = InAppReceiptRefreshRequest.refresh,
|
completion: @escaping (FetchReceiptResult) -> Void) -> InAppRequest? {
|
|
if let receiptData = appStoreReceiptData, forceRefresh == false {
|
completion(.success(receiptData: receiptData))
|
return nil
|
} else {
|
|
receiptRefreshRequest = refresh(nil) { result in
|
|
self.receiptRefreshRequest = nil
|
|
switch result {
|
case .success:
|
if let receiptData = self.appStoreReceiptData {
|
completion(.success(receiptData: receiptData))
|
} else {
|
completion(.error(error: .noReceiptData))
|
}
|
case .error(let e):
|
completion(.error(error: .networkError(error: e)))
|
}
|
}
|
return receiptRefreshRequest
|
}
|
}
|
|
/**
|
* - Parameter receiptData: encrypted receipt data
|
* - Parameter validator: Validator to check the encrypted receipt and return the receipt in readable format
|
* - Parameter completion: handler for result
|
*/
|
private func verify(receiptData: Data, using validator: ReceiptValidator, completion: @escaping (VerifyReceiptResult) -> Void) {
|
|
validator.validate(receiptData: receiptData) { result in
|
|
DispatchQueue.main.async {
|
completion(result)
|
}
|
}
|
}
|
}
|