Compare commits

..

5 Commits

Author SHA1 Message Date
Michael Schwarz 92409e4498 Merge pull request #27 from IdeasOnCanvas/core/enhancement/improveStringRepresentations
Improve string representations
2017-10-12 14:00:01 +02:00
Michael Schwarz b2764203d4 Merge pull request #26 from IdeasOnCanvas/core/enhancement/returnReceiptDataAndDeviceIdData
Return receiptData and device ID data upon validation
2017-10-12 13:59:27 +02:00
Hannes Oud 9817657584 Extend Receipt and InAppPurchaseReceipt for CustomDebugStringConvertible 2017-10-11 16:03:43 +02:00
Hannes Oud a0f87dbc46 Remove the „Parsed“ of remaining occurrences of „ParsedReceipt“ and „ParsedInAppPurchaseReceipt“ 2017-10-11 16:01:43 +02:00
Hannes Oud 20d7a7a2ba Return receiptData and device ID data upon validation 2017-10-11 15:50:48 +02:00
4 changed files with 74 additions and 42 deletions
@@ -33,9 +33,9 @@ struct HekateDemoViewModel {
guard let result = self.lastValidationResult else { return "(No result)" }
switch result {
case .success(let receipt):
case .success(let receipt, _, _):
return "Valid\n" + receipt.description
case .error(let error):
case .error(let error, _, _):
return "Invalid: \(error)"
}
}
+56 -32
View File
@@ -30,9 +30,13 @@ public struct LocalReceiptValidator {
/// Validates a local receipt and returns the result using the passed parameters.
public func validateReceipt(parameters: Parameters = Parameters.allSteps) -> Result {
var data: Data?
var deviceIdData: Data?
do {
deviceIdData = parameters.deviceIdentifier.getData()
guard let receiptData = parameters.receiptOrigin.loadData() else { throw Error.couldNotFindReceipt }
data = receiptData
let receiptContainer = try self.extractPKCS7Container(data: receiptData)
if parameters.shouldValidateSignaturePresence {
@@ -49,14 +53,14 @@ public struct LocalReceiptValidator {
try self.validateProperties(receipt: receipt, validations: parameters.propertyValidations)
if parameters.shouldValidateHash {
guard let deviceIdentifierData = parameters.deviceIdentifier.getData() else { throw Error.deviceIdentifierNotDeterminable }
guard let deviceIdentifierData = deviceIdData else { throw Error.deviceIdentifierNotDeterminable }
try self.validateHash(receipt: receipt, deviceIdentifierData: deviceIdentifierData)
}
return .success(receipt)
return .success(receipt, receiptData: receiptData, deviceIdentifier: deviceIdData)
} catch {
assert(error is LocalReceiptValidator.Error)
return .error(error as? LocalReceiptValidator.Error ?? .unknown)
return .error(error as? LocalReceiptValidator.Error ?? .unknown, receiptData: data, deviceIdentifier: deviceIdData)
}
}
@@ -94,7 +98,7 @@ public struct LocalReceiptValidator {
private extension LocalReceiptValidator {
func validateHash(receipt: Receipt, deviceIdentifierData: Data) throws {
// Make sure that the ParsedReceipt instances has non-nil values needed for hash comparison
// Make sure that the Receipt instances has non-nil values needed for hash comparison
guard let receiptOpaqueValueData = receipt.opaqueValue else { throw Error.incorrectHash }
guard let receiptBundleIdData = receipt.bundleIdData else { throw Error.incorrectHash }
guard let receiptHashData = receipt.sha1Hash else { throw Error.incorrectHash }
@@ -193,65 +197,65 @@ private extension LocalReceiptValidator {
guard let contents = pkcs7.pkcs7.pointee.d.sign.pointee.contents, let octets = contents.pointee.d.data else { throw Error.malformedReceipt }
guard let initialPointer = UnsafePointer(octets.pointee.data) else { throw Error.malformedReceipt }
let length = Int(octets.pointee.length)
var parsedReceipt = Receipt()
var receipt = Receipt()
try self.parseASN1Set(pointer: initialPointer, length: length) { attributeType, value in
guard let attribute = KnownReceiptAttribute(rawValue: attributeType) else { return }
switch attribute {
case .bundleIdentifier:
parsedReceipt.bundleIdData = value.dataValue
parsedReceipt.bundleIdentifier = value.unwrappedStringValue
receipt.bundleIdData = value.dataValue
receipt.bundleIdentifier = value.unwrappedStringValue
case .appVersion:
parsedReceipt.appVersion = value.unwrappedStringValue
receipt.appVersion = value.unwrappedStringValue
case .opaqueValue:
parsedReceipt.opaqueValue = value.dataValue
receipt.opaqueValue = value.dataValue
case .sha1Hash:
parsedReceipt.sha1Hash = value.dataValue
receipt.sha1Hash = value.dataValue
case .inAppPurchaseReceipts:
guard let pointer = value.valuePointer else { break }
let iapReceipt = try parseInAppPurchaseReceipt(pointer: pointer, length: value.length)
parsedReceipt.inAppPurchaseReceipts.append(iapReceipt)
receipt.inAppPurchaseReceipts.append(iapReceipt)
case .receiptCreationDate:
parsedReceipt.receiptCreationDate = value.unwrappedDateValue
receipt.receiptCreationDate = value.unwrappedDateValue
case .originalAppVersion:
parsedReceipt.originalAppVersion = value.unwrappedStringValue
receipt.originalAppVersion = value.unwrappedStringValue
case .expirationDate:
parsedReceipt.expirationDate = value.unwrappedDateValue
receipt.expirationDate = value.unwrappedDateValue
break
}
}
return parsedReceipt
return receipt
}
private func parseInAppPurchaseReceipt(pointer: UnsafePointer<UInt8>, length: Int) throws -> InAppPurchaseReceipt {
var parsedInAppPurchaseReceipt = InAppPurchaseReceipt()
var inAppPurchaseReceipt = InAppPurchaseReceipt()
try self.parseASN1Set(pointer: pointer, length: length) { attributeType, value in
guard let attribute = KnownInAppPurchaseAttribute(rawValue: attributeType) else { return }
switch attribute {
case .quantity:
parsedInAppPurchaseReceipt.quantity = value.intValue
inAppPurchaseReceipt.quantity = value.intValue
case .productIdentifier:
parsedInAppPurchaseReceipt.productIdentifier = value.unwrappedStringValue
inAppPurchaseReceipt.productIdentifier = value.unwrappedStringValue
case .transactionIdentifier:
parsedInAppPurchaseReceipt.transactionIdentifier = value.unwrappedStringValue
inAppPurchaseReceipt.transactionIdentifier = value.unwrappedStringValue
case .originalTransactionIdentifier:
parsedInAppPurchaseReceipt.originalTransactionIdentifier = value.unwrappedStringValue
inAppPurchaseReceipt.originalTransactionIdentifier = value.unwrappedStringValue
case .purchaseDate:
parsedInAppPurchaseReceipt.purchaseDate = value.unwrappedDateValue
inAppPurchaseReceipt.purchaseDate = value.unwrappedDateValue
case .originalPurchaseDate:
parsedInAppPurchaseReceipt.originalPurchaseDate = value.unwrappedDateValue
inAppPurchaseReceipt.originalPurchaseDate = value.unwrappedDateValue
case .subscriptionExpirationDate:
parsedInAppPurchaseReceipt.subscriptionExpirationDate = value.unwrappedDateValue
inAppPurchaseReceipt.subscriptionExpirationDate = value.unwrappedDateValue
case .cancellationDate:
parsedInAppPurchaseReceipt.cancellationDate = value.unwrappedDateValue
inAppPurchaseReceipt.cancellationDate = value.unwrappedDateValue
case .webOrderLineItemId:
parsedInAppPurchaseReceipt.webOrderLineItemId = value.intValue
inAppPurchaseReceipt.webOrderLineItemId = value.intValue
}
}
return parsedInAppPurchaseReceipt
return inAppPurchaseReceipt
}
private func parseASN1Set(pointer initialPointer: UnsafePointer<UInt8>, length: Int, valueAttributeAction: (_ attributeType: Int32, _ value: ASN1Object) throws -> Void) throws {
@@ -284,7 +288,7 @@ private extension LocalReceiptValidator {
private extension LocalReceiptValidator {
/// See ParsedReceipt.swift for details and a link to Apple reference
/// See Receipt.swift for details and a link to Apple reference
enum KnownReceiptAttribute: Int32 {
case bundleIdentifier = 2
case appVersion = 3
@@ -303,7 +307,7 @@ private extension LocalReceiptValidator {
// - and of unknown type 14(L=3), 25(L=3), 11(L=4), 13(L=4), 1(L=6), 9(L=6), 16(L=6), 15(L=8), 7(L=66), 6(L=69 variable)
}
/// See ParsedReceipt.swift for details and a link to Apple reference
/// See Receipt.swift for details and a link to Apple reference
enum KnownInAppPurchaseAttribute: Int32 {
case quantity = 1701
case productIdentifier = 1702
@@ -323,12 +327,12 @@ extension LocalReceiptValidator {
public enum Result {
case success(Receipt)
case error(LocalReceiptValidator.Error)
case success(Receipt, receiptData: Data, deviceIdentifier: Data?)
case error(LocalReceiptValidator.Error, receiptData: Data?, deviceIdentifier: Data?)
public var receipt: Receipt? {
switch self {
case .success(let receipt):
case .success(let receipt, _, _):
return receipt
case .error:
return nil
@@ -339,10 +343,30 @@ extension LocalReceiptValidator {
switch self {
case .success:
return nil
case .error(let error):
case .error(let error, _, _):
return error
}
}
/// The receipt data if it could be loaded
public var receiptData: Data? {
switch self {
case .success(_, let data, _):
return data
case .error(_, let data, _):
return data
}
}
/// The device identifier if it could be determined
public var deviceIdentifier: Data? {
switch self {
case .success(_, _, let data):
return data
case .error(_, _, let data):
return data
}
}
}
}
+14 -6
View File
@@ -76,7 +76,7 @@ extension Receipt: AutoEquatable {}
// MARK: - CustomStringConvertible
extension Receipt: CustomStringConvertible {
extension Receipt: CustomStringConvertible, CustomDebugStringConvertible {
public var description: String {
let formatter = StringFormatter()
@@ -91,11 +91,15 @@ extension Receipt: CustomStringConvertible {
("expirationDate", formatter.format(self.expirationDate)),
("inAppPurchaseReceipts", formatter.format(self.inAppPurchaseReceipts))
]
return "ParsedReceipt(\n" + formatter.format(props) + "\n)"
return "Receipt(\n" + formatter.format(props) + "\n)"
}
public var debugDescription: String {
return description
}
}
// MARK: - ParsedInAppPurchaseReceipt
// MARK: - InAppPurchaseReceipt
/// An In-App-Purchase Receipt as Parsed from a receipt file.
///
@@ -169,7 +173,7 @@ public struct InAppPurchaseReceipt {
/// This value is a unique ID that identifies purchase events across devices, including subscription renewal purchase events.
public internal(set) var webOrderLineItemId: Int?
/// For documentation see ParsedInAppPurchaseReceipt itself.
/// For documentation see InAppPurchaseReceipt itself.
public init(quantity: Int?, productIdentifier: String?, transactionIdentifier: String?, originalTransactionIdentifier: String?, purchaseDate: Date?, originalPurchaseDate: Date?, subscriptionExpirationDate: Date?, cancellationDate: Date?, webOrderLineItemId: Int?) {
self.quantity = quantity
self.productIdentifier = productIdentifier
@@ -191,7 +195,7 @@ extension InAppPurchaseReceipt: AutoEquatable {}
// MARK: - CustomStringConvertible
extension InAppPurchaseReceipt: CustomStringConvertible {
extension InAppPurchaseReceipt: CustomStringConvertible, CustomDebugStringConvertible {
public var description: String {
let formatter = StringFormatter()
@@ -206,7 +210,11 @@ extension InAppPurchaseReceipt: CustomStringConvertible {
("cancellationDate", formatter.format(self.cancellationDate)),
("webOrderLineItemId", formatter.format(self.webOrderLineItemId))
]
return "ParsedInAppPurchaseReceipt(\n" + formatter.format(props) + "\n)"
return "InAppPurchaseReceipt(\n" + formatter.format(props) + "\n)"
}
public var debugDescription: String {
return description
}
}
@@ -23,7 +23,7 @@ private func compareArrays<T>(lhs: [T], rhs: [T], compare: (_ lhs: T, _ rhs: T)
}
// MARK: - AutoEquatable for classes, protocols, structs
// MARK: - ParsedInAppPurchaseReceipt AutoEquatable
// MARK: - InAppPurchaseReceipt AutoEquatable
extension InAppPurchaseReceipt: Equatable {}
public func == (lhs: InAppPurchaseReceipt, rhs: InAppPurchaseReceipt) -> Bool {
guard compareOptionals(lhs: lhs.quantity, rhs: rhs.quantity, compare: ==) else { return false }
@@ -37,7 +37,7 @@ public func == (lhs: InAppPurchaseReceipt, rhs: InAppPurchaseReceipt) -> Bool {
guard compareOptionals(lhs: lhs.webOrderLineItemId, rhs: rhs.webOrderLineItemId, compare: ==) else { return false }
return true
}
// MARK: - ParsedReceipt AutoEquatable
// MARK: - Receipt AutoEquatable
extension Receipt: Equatable {}
public func == (lhs: Receipt, rhs: Receipt) -> Bool {
guard compareOptionals(lhs: lhs.bundleIdentifier, rhs: rhs.bundleIdentifier, compare: ==) else { return false }