diff --git a/RevenueCat.xcodeproj/project.pbxproj b/RevenueCat.xcodeproj/project.pbxproj index 9c029db2ca..742fd2406c 100644 --- a/RevenueCat.xcodeproj/project.pbxproj +++ b/RevenueCat.xcodeproj/project.pbxproj @@ -930,6 +930,15 @@ B3F8418F26F3A93400E560FB /* ErrorCodeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3F8418E26F3A93400E560FB /* ErrorCodeTests.swift */; }; C3AD12BA2C6EA61F00A4F86F /* CompatibilityNavigationStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3AD12B92C6EA61F00A4F86F /* CompatibilityNavigationStack.swift */; }; C3AD12BC2C6EA69D00A4F86F /* SubscriptionDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3AD12BB2C6EA69D00A4F86F /* SubscriptionDetailsView.swift */; }; + C5434EBE2CA3462900FE8F15 /* base64encoded_sandboxReceipt2.txt in Resources */ = {isa = PBXBuildFile; fileRef = C5434EBA2CA3462900FE8F15 /* base64encoded_sandboxReceipt2.txt */; }; + C5434EBF2CA3462900FE8F15 /* base64encoded_sandboxReceipt3.txt in Resources */ = {isa = PBXBuildFile; fileRef = C5434EBB2CA3462900FE8F15 /* base64encoded_sandboxReceipt3.txt */; }; + C5434EC02CA3462900FE8F15 /* base64encoded_unsupportedReceipt1.txt in Resources */ = {isa = PBXBuildFile; fileRef = C5434EBC2CA3462900FE8F15 /* base64encoded_unsupportedReceipt1.txt */; }; + C5434EC12CA3462900FE8F15 /* base64encoded_unsupportedReceipt2.txt in Resources */ = {isa = PBXBuildFile; fileRef = C5434EBD2CA3462900FE8F15 /* base64encoded_unsupportedReceipt2.txt */; }; + C5434EC62CA3475800FE8F15 /* base64encoded_sandboxReceipt2.txt in CopyFiles */ = {isa = PBXBuildFile; fileRef = C5434EBA2CA3462900FE8F15 /* base64encoded_sandboxReceipt2.txt */; }; + C5434EC72CA3475800FE8F15 /* base64encoded_sandboxReceipt3.txt in CopyFiles */ = {isa = PBXBuildFile; fileRef = C5434EBB2CA3462900FE8F15 /* base64encoded_sandboxReceipt3.txt */; }; + C5434EC82CA3475800FE8F15 /* base64encoded_unsupportedReceipt1.txt in CopyFiles */ = {isa = PBXBuildFile; fileRef = C5434EBC2CA3462900FE8F15 /* base64encoded_unsupportedReceipt1.txt */; }; + C5434EC92CA3475800FE8F15 /* base64encoded_unsupportedReceipt2.txt in CopyFiles */ = {isa = PBXBuildFile; fileRef = C5434EBD2CA3462900FE8F15 /* base64encoded_unsupportedReceipt2.txt */; }; + C5434ECB2CA347BB00FE8F15 /* ISO8601DateFormatterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5434ECA2CA347B800FE8F15 /* ISO8601DateFormatterTests.swift */; }; F516BD29282434070083480B /* StoreKit2StorefrontListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = F516BD28282434070083480B /* StoreKit2StorefrontListener.swift */; }; F52CFAFC28290AE500E8ABC5 /* StoreKit2StorefrontListenerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F516BD322828FDD90083480B /* StoreKit2StorefrontListenerTests.swift */; }; F530E4FF275646EF001AF6BD /* MacDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = F530E4FE275646EF001AF6BD /* MacDevice.swift */; }; @@ -1123,6 +1132,10 @@ dstPath = ""; dstSubfolderSpec = 7; files = ( + C5434EC62CA3475800FE8F15 /* base64encoded_sandboxReceipt2.txt in CopyFiles */, + C5434EC72CA3475800FE8F15 /* base64encoded_sandboxReceipt3.txt in CopyFiles */, + C5434EC82CA3475800FE8F15 /* base64encoded_unsupportedReceipt1.txt in CopyFiles */, + C5434EC92CA3475800FE8F15 /* base64encoded_unsupportedReceipt2.txt in CopyFiles */, 57E6C2C92975C777001AFE98 /* verifyReceiptSample1.txt in CopyFiles */, 57E6C2CA2975C777001AFE98 /* base64encodedreceiptsample1.txt in CopyFiles */, 57E6C2CB2975C777001AFE98 /* base64encoded_sandboxReceipt.txt in CopyFiles */, @@ -2082,6 +2095,11 @@ B3F8418E26F3A93400E560FB /* ErrorCodeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorCodeTests.swift; sourceTree = ""; }; C3AD12B92C6EA61F00A4F86F /* CompatibilityNavigationStack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompatibilityNavigationStack.swift; sourceTree = ""; }; C3AD12BB2C6EA69D00A4F86F /* SubscriptionDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionDetailsView.swift; sourceTree = ""; }; + C5434EBA2CA3462900FE8F15 /* base64encoded_sandboxReceipt2.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = base64encoded_sandboxReceipt2.txt; sourceTree = ""; }; + C5434EBB2CA3462900FE8F15 /* base64encoded_sandboxReceipt3.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = base64encoded_sandboxReceipt3.txt; sourceTree = ""; }; + C5434EBC2CA3462900FE8F15 /* base64encoded_unsupportedReceipt1.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = base64encoded_unsupportedReceipt1.txt; sourceTree = ""; }; + C5434EBD2CA3462900FE8F15 /* base64encoded_unsupportedReceipt2.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = base64encoded_unsupportedReceipt2.txt; sourceTree = ""; }; + C5434ECA2CA347B800FE8F15 /* ISO8601DateFormatterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ISO8601DateFormatterTests.swift; sourceTree = ""; }; EFB3CBAA73855779FE828CE2 /* ProductsFetcherSK1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductsFetcherSK1.swift; sourceTree = ""; }; F516BD28282434070083480B /* StoreKit2StorefrontListener.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreKit2StorefrontListener.swift; sourceTree = ""; }; F516BD322828FDD90083480B /* StoreKit2StorefrontListenerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreKit2StorefrontListenerTests.swift; sourceTree = ""; }; @@ -2886,6 +2904,10 @@ children = ( 2DDE559A24C8B5E300DCB087 /* verifyReceiptSample1.txt */, 2DDE559B24C8B5E300DCB087 /* base64encodedreceiptsample1.txt */, + C5434EBA2CA3462900FE8F15 /* base64encoded_sandboxReceipt2.txt */, + C5434EBB2CA3462900FE8F15 /* base64encoded_sandboxReceipt3.txt */, + C5434EBC2CA3462900FE8F15 /* base64encoded_unsupportedReceipt1.txt */, + C5434EBD2CA3462900FE8F15 /* base64encoded_unsupportedReceipt2.txt */, 5791CE7F273F26A000E50C4B /* base64encoded_sandboxReceipt.txt */, B319514A26C1991E002CA9AC /* base64EncodedReceiptSampleForDataExtension.txt */, ); @@ -3716,6 +3738,7 @@ 5759B462296E1A3E002472D5 /* Helpers */, 4FA696A329FC43C600D228B1 /* ReceiptParserTests-Info.plist */, 5759B404296DF6C4002472D5 /* ReceiptParserFetchingTests.swift */, + C5434ECA2CA347B800FE8F15 /* ISO8601DateFormatterTests.swift */, ); name = ReceiptParserTests; path = Tests/ReceiptParserTests; @@ -5006,6 +5029,10 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + C5434EBE2CA3462900FE8F15 /* base64encoded_sandboxReceipt2.txt in Resources */, + C5434EBF2CA3462900FE8F15 /* base64encoded_sandboxReceipt3.txt in Resources */, + C5434EC02CA3462900FE8F15 /* base64encoded_unsupportedReceipt1.txt in Resources */, + C5434EC12CA3462900FE8F15 /* base64encoded_unsupportedReceipt2.txt in Resources */, 2D4D6AF624F7193700B656BE /* verifyReceiptSample1.txt in Resources */, 2D4D6AF724F7193700B656BE /* base64encodedreceiptsample1.txt in Resources */, 5774F9BE2805E71100997128 /* Fixtures in Resources */, @@ -5889,6 +5916,7 @@ buildActionMask = 2147483647; files = ( 5759B406296DF8EE002472D5 /* ReceiptParserFetchingTests.swift in Sources */, + C5434ECB2CA347BB00FE8F15 /* ISO8601DateFormatterTests.swift in Sources */, 5759B465296E1A4B002472D5 /* MockBundle.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Sources/LocalReceiptParsing/DataConverters/ArraySlice_UInt8+Extensions.swift b/Sources/LocalReceiptParsing/DataConverters/ArraySlice_UInt8+Extensions.swift index 237b30ee62..a455e29e15 100644 --- a/Sources/LocalReceiptParsing/DataConverters/ArraySlice_UInt8+Extensions.swift +++ b/Sources/LocalReceiptParsing/DataConverters/ArraySlice_UInt8+Extensions.swift @@ -43,6 +43,10 @@ extension ArraySlice where Element == UInt8 { } func toDate() -> Date? { + if let fastParsed = toDateFastParse() { + // This approach is around ~60% faster than `ISO8601DateFormatter.default` + return fastParsed + } guard let dateString = String(bytes: Array(self), encoding: .ascii) else { return nil } return ISO8601DateFormatter.default.date(from: dateString) @@ -53,3 +57,77 @@ extension ArraySlice where Element == UInt8 { } } + +private extension ArraySlice where Element == UInt8 { + + static let toDateCalendar: Calendar = { + var calendar = Calendar(identifier: .gregorian) + calendar.timeZone = TimeZone(secondsFromGMT: 0)! + return calendar + }() + + func toDateFastParse() -> Date? { + // expected format 2015-08-10T07:19:32Z + guard count == 20 else { return nil } + let asciiZero: UInt8 = 48 + let asciiNine: UInt8 = 57 + let asciiDash: UInt8 = 45 + let asciiColon: UInt8 = 58 + let asciiT: UInt8 = 84 + let asciiZ: UInt8 = 90 + let limits: [(min: UInt8, max: UInt8)] = [ + (asciiZero, asciiNine), (asciiZero, asciiNine), (asciiZero, asciiNine), (asciiZero, asciiNine), // year + (asciiDash, asciiDash), + (asciiZero, asciiNine), (asciiZero, asciiNine), // month + (asciiDash, asciiDash), + (asciiZero, asciiNine), (asciiZero, asciiNine), // day + (asciiT, asciiT), + (asciiZero, asciiNine), (asciiZero, asciiNine), // hour + (asciiColon, asciiColon), + (asciiZero, asciiNine), (asciiZero, asciiNine), // minute + (asciiColon, asciiColon), + (asciiZero, asciiNine), (asciiZero, asciiNine), // second + (asciiZ, asciiZ) + ] + for (character, limit) in zip(self, limits) { + guard limit.min <= character, + character <= limit.max else { return nil } + } + + let year = toDateParseAsciiNumber(from: 0, to: 4) + let month = toDateParseAsciiNumber(from: 5, to: 7) + guard 1 <= month, + month <= 12 else { return nil } + let day = toDateParseAsciiNumber(from: 8, to: 10) + guard 1 <= day, + day <= 31 else { return nil } + let hour = toDateParseAsciiNumber(from: 11, to: 13) + guard 0 <= hour, + hour <= 23 else { return nil } + let minute = toDateParseAsciiNumber(from: 14, to: 16) + guard 0 <= minute, + minute <= 59 else { return nil } + let second = toDateParseAsciiNumber(from: 17, to: 19) + guard 0 <= second, + second <= 59 else { return nil } + + let components = DateComponents( + year: year, month: month, day: day, hour: hour, minute: minute, second: second + ) + return Self.toDateCalendar.date(from: components) + } + + func toDateParseAsciiNumber(from: Int, to: Int) -> Int { // swiftlint:disable:this identifier_name + let asciiZero: UInt8 = 48 + var index = from + startIndex + let end = to + startIndex + var result = 0 + while index < end { + let digit = self[index] - asciiZero + result = result * 10 + Int(digit) + index += 1 + } + return result + } + +} diff --git a/Tests/ReceiptParserTests/Helpers/MockBundle.swift b/Tests/ReceiptParserTests/Helpers/MockBundle.swift index 302f69a40a..71d388dd39 100644 --- a/Tests/ReceiptParserTests/Helpers/MockBundle.swift +++ b/Tests/ReceiptParserTests/Helpers/MockBundle.swift @@ -20,6 +20,10 @@ final class MockBundle: Bundle { case appStoreReceipt case emptyReceipt case sandboxReceipt + case sandboxReceipt2 + case sandboxReceipt3 + case unsupportedReceipt1 + case unsupportedReceipt2 case nilURL } @@ -38,6 +42,18 @@ final class MockBundle: Bundle { case .sandboxReceipt: return testBundle .url(forResource: Self.mockSandboxReceiptFileName, withExtension: "txt") + case .sandboxReceipt2: + return testBundle + .url(forResource: Self.mockSandboxReceiptFileName2, withExtension: "txt") + case .sandboxReceipt3: + return testBundle + .url(forResource: Self.mockSandboxReceiptFileName3, withExtension: "txt") + case .unsupportedReceipt1: + return testBundle + .url(forResource: Self.mockUnsupportedReceiptFileName1, withExtension: "txt") + case .unsupportedReceipt2: + return testBundle + .url(forResource: Self.mockUnsupportedReceiptFileName2, withExtension: "txt") case .nilURL: return nil } @@ -47,5 +63,9 @@ final class MockBundle: Bundle { private static let mockAppStoreReceiptFileName = "base64encodedreceiptsample1" private static let mockSandboxReceiptFileName = "base64encoded_sandboxReceipt" + private static let mockSandboxReceiptFileName2 = "base64encoded_sandboxReceipt2" + private static let mockSandboxReceiptFileName3 = "base64encoded_sandboxReceipt3" + private static let mockUnsupportedReceiptFileName1 = "base64encoded_unsupportedReceipt1" + private static let mockUnsupportedReceiptFileName2 = "base64encoded_unsupportedReceipt2" } diff --git a/Tests/ReceiptParserTests/ISO8601DateFormatterTests.swift b/Tests/ReceiptParserTests/ISO8601DateFormatterTests.swift new file mode 100644 index 0000000000..6432f7aa08 --- /dev/null +++ b/Tests/ReceiptParserTests/ISO8601DateFormatterTests.swift @@ -0,0 +1,83 @@ +// +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// ISO8601DateFormatterTests.swift +// +// Created by Paweł Czerwiński on 6/28/24. + +import Nimble +@testable import ReceiptParser +import XCTest + +class ISO8601DateFormatterTests: XCTestCase { + + private let someValidDates: [(text: String, date: Date)] = [ + ("2018-11-13T16:46:31Z", Date(timeIntervalSince1970: 1542127591.0)), + ("2020-07-22T17:39:08Z", Date(timeIntervalSince1970: 1595439548.0)), + ("2020-07-14T21:47:57Z", Date(timeIntervalSince1970: 1594763277.0)), + ("2020-07-22T17:39:06Z", Date(timeIntervalSince1970: 1595439546.0)), + ("2022-09-14T01:47:57Z", Date(timeIntervalSince1970: 1663120077.0)), + ("2022-09-14T01:47:07Z", Date(timeIntervalSince1970: 1663120027.0)) + ] + + private let someInvalidlyParsedDates: [(text: String, date: Date)] = [ + ("2022-09-31T01:47:57Z", Date(timeIntervalSince1970: 1664588877.0)), // September has only 30 days + ("2022-02-29T12:47:57Z", Date(timeIntervalSince1970: 1646138877.0)), // In 2022 February had 28 days not 29 + ("2022-02-30T12:47:57Z", Date(timeIntervalSince1970: 1646225277.0)), // In 2022 February had 28 days not 30 + ("2022-02-31T01:47:57Z", Date(timeIntervalSince1970: 1646272077.0)), // In 2022 February had 28 days not 31 + ("2016-02-30T01:47:57Z", Date(timeIntervalSince1970: 1456796877.0)), // In 2016 February had 29 days not 30 + ("2016-02-31T01:47:57Z", Date(timeIntervalSince1970: 1456883277.0)), // In 2016 February had 29 days not 31 + ("2022-09-14T24:47:07Z", Date(timeIntervalSince1970: 1663202827.0)) // Too high hour + ] + + private let someInvalidDates: [String] = [ + "2022-13-14T24:47:07Z", // invalid month + "2022-12-32T24:47:07Z", // invalid day + "2022-09-14T25:47:07Z", // invalid hour + "2022-09-14T23:61:07Z", // invalid minutes + "2022-09-14T23:60:07Z", // invalid minutes + "2022-09-14T12:47:60Z", // invalid seconds + "2022-09-14T12:47:72Z" // invalid seconds + ] + + func testParseStandardDates() { + for (dateString, expectedResult) in someValidDates { + expect(ISO8601DateFormatter.default.date(from: dateString)) == expectedResult + } + } + + func testParseInvalidDatesThatForSomeReasonWorks() { + for (dateString, observedResult) in someInvalidlyParsedDates { + expect(ISO8601DateFormatter.default.date(from: dateString)) == observedResult + } + } + + func testParseInvalidDates() { + for dateString in someInvalidDates { + expect(ISO8601DateFormatter.default.date(from: dateString)).to(beNil()) + } + } + + func testConsistencyWithRawBitsParser() { + let allDates = someValidDates.map { $0.text } + + someInvalidlyParsedDates.map { $0.text } + + someInvalidDates + + for dateString in allDates { + do { + let data = try XCTUnwrap(dateString.data(using: .ascii)) + let rawBits = ArraySlice(data) + XCTAssertEqual(ISO8601DateFormatter.default.date(from: dateString), rawBits.toDate()) + } catch { + fail("Unexpected error for \(dateString), error: \(error)") + } + } + } + +} diff --git a/Tests/ReceiptParserTests/ReceiptParserFetchingTests.swift b/Tests/ReceiptParserTests/ReceiptParserFetchingTests.swift index 9134d0f8fa..e6de4bb20d 100644 --- a/Tests/ReceiptParserTests/ReceiptParserFetchingTests.swift +++ b/Tests/ReceiptParserTests/ReceiptParserFetchingTests.swift @@ -15,7 +15,7 @@ import Nimble @testable import ReceiptParser import XCTest -class ReceiptParserFetchingTests: XCTestCase { +class ReceiptParserFetchingTests: XCTestCase { // swiftlint:disable:this type_body_length private let parser: LocalReceiptFetcher = .init() private var mockFileReader: MockFileReader! @@ -69,7 +69,7 @@ class ReceiptParserFetchingTests: XCTestCase { } } - func testParseReceipt() throws { + func testParseReceipt() throws { // swiftlint:disable:this function_body_length self.mockBundle.receiptURLResult = .appStoreReceipt let receiptURL = try XCTUnwrap(self.mockBundle.appStoreReceiptURL) @@ -81,13 +81,467 @@ class ReceiptParserFetchingTests: XCTestCase { self.mockFileReader.mock(url: receiptURL, with: decodedData) let receipt = try self.fetchAndParse() - expect(receipt.bundleId) == "com.revenuecat.sampleapp" + expect(receipt.environment) == .sandbox + expect(receipt.bundleId) == "com.revenuecat.sampleapp" expect(receipt.applicationVersion) == "4" expect(receipt.originalApplicationVersion) == "1.0" - expect(receipt.opaqueValue).toNot(beNil()) - expect(receipt.sha1Hash).toNot(beNil()) + expect(receipt.opaqueValue) == (try XCTUnwrap(Data(base64Encoded: "S5Yfx+yvdaa9O5w6EvDuZA=="))) + expect(receipt.sha1Hash) == (try XCTUnwrap(Data(base64Encoded: "deUV5jHlBbtD8cm+XBgY/95o7zw="))) + expect(receipt.creationDate) == Date(timeIntervalSince1970: 1595439548.0) + expect(receipt.expirationDate).to(beNil()) + expect(receipt.inAppPurchases) == [ + AppleReceipt.InAppPurchase(quantity: 1, + productId: "com.revenuecat.monthly_4.99.1_week_intro", + transactionId: "1000000692879214", + originalTransactionId: "1000000692878476", + productType: .autoRenewableSubscription, + purchaseDate: Date(timeIntervalSince1970: 1594755400.0), + originalPurchaseDate: Date(timeIntervalSince1970: 1594755207.0), + expiresDate: Date(timeIntervalSince1970: 1594755700.0), + cancellationDate: nil, + isInTrialPeriod: false, + isInIntroOfferPeriod: false, + webOrderLineItemId: 1000000054042695, + promotionalOfferIdentifier: nil), + AppleReceipt.InAppPurchase(quantity: 1, + productId: "com.revenuecat.monthly_4.99.1_week_intro", + transactionId: "1000000692901513", + originalTransactionId: "1000000692878476", + productType: .autoRenewableSubscription, + purchaseDate: Date(timeIntervalSince1970: 1594762977.0), + originalPurchaseDate: Date(timeIntervalSince1970: 1594755207.0), + expiresDate: Date(timeIntervalSince1970: 1594763277.0), + cancellationDate: nil, + isInTrialPeriod: false, + isInIntroOfferPeriod: false, + webOrderLineItemId: 1000000054042739, + promotionalOfferIdentifier: nil), + AppleReceipt.InAppPurchase(quantity: 1, + productId: "com.revenuecat.monthly_4.99.1_week_intro", + transactionId: "1000000692902182", + originalTransactionId: "1000000692878476", + productType: .autoRenewableSubscription, + purchaseDate: Date(timeIntervalSince1970: 1594763277.0), + originalPurchaseDate: Date(timeIntervalSince1970: 1594755207.0), + expiresDate: Date(timeIntervalSince1970: 1594763577.0), + cancellationDate: nil, + isInTrialPeriod: false, + isInIntroOfferPeriod: false, + webOrderLineItemId: 1000000054044460, + promotionalOfferIdentifier: nil), + AppleReceipt.InAppPurchase(quantity: 1, + productId: "com.revenuecat.monthly_4.99.1_week_intro", + transactionId: "1000000692902990", + originalTransactionId: "1000000692878476", + productType: .autoRenewableSubscription, + purchaseDate: Date(timeIntervalSince1970: 1594763577.0), + originalPurchaseDate: Date(timeIntervalSince1970: 1594755207.0), + expiresDate: Date(timeIntervalSince1970: 1594763877.0), + cancellationDate: nil, + isInTrialPeriod: false, + isInIntroOfferPeriod: false, + webOrderLineItemId: 1000000054044520, + promotionalOfferIdentifier: nil), + AppleReceipt.InAppPurchase(quantity: 1, + productId: "com.revenuecat.monthly_4.99.1_week_intro", + transactionId: "1000000692905419", + originalTransactionId: "1000000692878476", + productType: .autoRenewableSubscription, + purchaseDate: Date(timeIntervalSince1970: 1594763877.0), + originalPurchaseDate: Date(timeIntervalSince1970: 1594755207.0), + expiresDate: Date(timeIntervalSince1970: 1594764177.0), + cancellationDate: nil, + isInTrialPeriod: false, + isInIntroOfferPeriod: false, + webOrderLineItemId: 1000000054044587, + promotionalOfferIdentifier: nil), + AppleReceipt.InAppPurchase(quantity: 1, + productId: "com.revenuecat.monthly_4.99.1_week_intro", + transactionId: "1000000692905971", + originalTransactionId: "1000000692878476", + productType: .autoRenewableSubscription, + purchaseDate: Date(timeIntervalSince1970: 1594764177.0), + originalPurchaseDate: Date(timeIntervalSince1970: 1594755207.0), + expiresDate: Date(timeIntervalSince1970: 1594764477.0), + cancellationDate: nil, + isInTrialPeriod: false, + isInIntroOfferPeriod: false, + webOrderLineItemId: 1000000054044637, + promotionalOfferIdentifier: nil), + AppleReceipt.InAppPurchase(quantity: 1, + productId: "com.revenuecat.monthly_4.99.1_week_intro", + transactionId: "1000000692906727", + originalTransactionId: "1000000692878476", + productType: .autoRenewableSubscription, + purchaseDate: Date(timeIntervalSince1970: 1594764500.0), + originalPurchaseDate: Date(timeIntervalSince1970: 1594755207.0), + expiresDate: Date(timeIntervalSince1970: 1594764800.0), + cancellationDate: nil, + isInTrialPeriod: false, + isInIntroOfferPeriod: false, + webOrderLineItemId: 1000000054044710, + promotionalOfferIdentifier: nil), + AppleReceipt.InAppPurchase(quantity: 1, + productId: "com.revenuecat.annual_39.99.2_week_intro", + transactionId: "1000000696553650", + originalTransactionId: "1000000692878476", + productType: .autoRenewableSubscription, + purchaseDate: Date(timeIntervalSince1970: 1595439546.0), + originalPurchaseDate: Date(timeIntervalSince1970: 1594755207.0), + expiresDate: Date(timeIntervalSince1970: 1595443146.0), + cancellationDate: nil, + isInTrialPeriod: false, + isInIntroOfferPeriod: false, + webOrderLineItemId: 1000000054044800, + promotionalOfferIdentifier: nil), + AppleReceipt.InAppPurchase(quantity: 1, + productId: "com.revenuecat.monthly_4.99.1_week_intro", + transactionId: "1000000692878476", + originalTransactionId: "1000000692878476", + productType: .autoRenewableSubscription, + purchaseDate: Date(timeIntervalSince1970: 1594755206.0), + originalPurchaseDate: Date(timeIntervalSince1970: 1594755207.0), + expiresDate: Date(timeIntervalSince1970: 1594755386.0), + cancellationDate: nil, + isInTrialPeriod: true, + isInIntroOfferPeriod: false, + webOrderLineItemId: 1000000054042694, + promotionalOfferIdentifier: nil) + ] } + func testParseSandboxReceipt() throws { // swiftlint:disable:this function_body_length + self.mockBundle.receiptURLResult = .sandboxReceipt + + let receiptURL = try XCTUnwrap(self.mockBundle.appStoreReceiptURL) + let data = try DefaultFileReader().contents(of: receiptURL) + let decodedData = try XCTUnwrap( + Data(base64Encoded: XCTUnwrap(String(data: data, encoding: .utf8))) + ) + + self.mockFileReader.mock(url: receiptURL, with: decodedData) + + let receipt = try self.fetchAndParse() + expect(receipt.environment) == .sandbox + expect(receipt.bundleId) == "com.revenuecat.sampleapp" + expect(receipt.applicationVersion) == "4" + expect(receipt.originalApplicationVersion) == "1.0" + expect(receipt.opaqueValue) == (try XCTUnwrap(Data(base64Encoded: "S5Yfx+yvdaa9O5w6EvDuZA=="))) + expect(receipt.sha1Hash) == (try XCTUnwrap(Data(base64Encoded: "deUV5jHlBbtD8cm+XBgY/95o7zw="))) + expect(receipt.creationDate) == Date(timeIntervalSince1970: 1595439548.0) + expect(receipt.expirationDate).to(beNil()) + expect(receipt.inAppPurchases) == [ + AppleReceipt.InAppPurchase(quantity: 1, + productId: "com.revenuecat.monthly_4.99.1_week_intro", + transactionId: "1000000692879214", + originalTransactionId: "1000000692878476", + productType: .autoRenewableSubscription, + purchaseDate: Date(timeIntervalSince1970: 1594755400.0), + originalPurchaseDate: Date(timeIntervalSince1970: 1594755207.0), + expiresDate: Date(timeIntervalSince1970: 1594755700.0), + cancellationDate: nil, + isInTrialPeriod: false, + isInIntroOfferPeriod: false, + webOrderLineItemId: 1000000054042695, + promotionalOfferIdentifier: nil), + AppleReceipt.InAppPurchase(quantity: 1, + productId: "com.revenuecat.monthly_4.99.1_week_intro", + transactionId: "1000000692901513", + originalTransactionId: "1000000692878476", + productType: .autoRenewableSubscription, + purchaseDate: Date(timeIntervalSince1970: 1594762977.0), + originalPurchaseDate: Date(timeIntervalSince1970: 1594755207.0), + expiresDate: Date(timeIntervalSince1970: 1594763277.0), + cancellationDate: nil, + isInTrialPeriod: false, + isInIntroOfferPeriod: false, + webOrderLineItemId: 1000000054042739, + promotionalOfferIdentifier: nil), + AppleReceipt.InAppPurchase(quantity: 1, + productId: "com.revenuecat.monthly_4.99.1_week_intro", + transactionId: "1000000692902182", + originalTransactionId: "1000000692878476", + productType: .autoRenewableSubscription, + purchaseDate: Date(timeIntervalSince1970: 1594763277.0), + originalPurchaseDate: Date(timeIntervalSince1970: 1594755207.0), + expiresDate: Date(timeIntervalSince1970: 1594763577.0), + cancellationDate: nil, + isInTrialPeriod: false, + isInIntroOfferPeriod: false, + webOrderLineItemId: 1000000054044460, + promotionalOfferIdentifier: nil), + AppleReceipt.InAppPurchase(quantity: 1, + productId: "com.revenuecat.monthly_4.99.1_week_intro", + transactionId: "1000000692902990", + originalTransactionId: "1000000692878476", + productType: .autoRenewableSubscription, + purchaseDate: Date(timeIntervalSince1970: 1594763577.0), + originalPurchaseDate: Date(timeIntervalSince1970: 1594755207.0), + expiresDate: Date(timeIntervalSince1970: 1594763877.0), + cancellationDate: nil, + isInTrialPeriod: false, + isInIntroOfferPeriod: false, + webOrderLineItemId: 1000000054044520, + promotionalOfferIdentifier: nil), + AppleReceipt.InAppPurchase(quantity: 1, + productId: "com.revenuecat.monthly_4.99.1_week_intro", + transactionId: "1000000692905419", + originalTransactionId: "1000000692878476", + productType: .autoRenewableSubscription, + purchaseDate: Date(timeIntervalSince1970: 1594763877.0), + originalPurchaseDate: Date(timeIntervalSince1970: 1594755207.0), + expiresDate: Date(timeIntervalSince1970: 1594764177.0), + cancellationDate: nil, + isInTrialPeriod: false, + isInIntroOfferPeriod: false, + webOrderLineItemId: 1000000054044587, + promotionalOfferIdentifier: nil), + AppleReceipt.InAppPurchase(quantity: 1, + productId: "com.revenuecat.monthly_4.99.1_week_intro", + transactionId: "1000000692905971", + originalTransactionId: "1000000692878476", + productType: .autoRenewableSubscription, + purchaseDate: Date(timeIntervalSince1970: 1594764177.0), + originalPurchaseDate: Date(timeIntervalSince1970: 1594755207.0), + expiresDate: Date(timeIntervalSince1970: 1594764477.0), + cancellationDate: nil, + isInTrialPeriod: false, + isInIntroOfferPeriod: false, + webOrderLineItemId: 1000000054044637, + promotionalOfferIdentifier: nil), + AppleReceipt.InAppPurchase(quantity: 1, + productId: "com.revenuecat.monthly_4.99.1_week_intro", + transactionId: "1000000692906727", + originalTransactionId: "1000000692878476", + productType: .autoRenewableSubscription, + purchaseDate: Date(timeIntervalSince1970: 1594764500.0), + originalPurchaseDate: Date(timeIntervalSince1970: 1594755207.0), + expiresDate: Date(timeIntervalSince1970: 1594764800.0), + cancellationDate: nil, + isInTrialPeriod: false, + isInIntroOfferPeriod: false, + webOrderLineItemId: 1000000054044710, + promotionalOfferIdentifier: nil), + AppleReceipt.InAppPurchase(quantity: 1, + productId: "com.revenuecat.annual_39.99.2_week_intro", + transactionId: "1000000696553650", + originalTransactionId: "1000000692878476", + productType: .autoRenewableSubscription, + purchaseDate: Date(timeIntervalSince1970: 1595439546.0), + originalPurchaseDate: Date(timeIntervalSince1970: 1594755207.0), + expiresDate: Date(timeIntervalSince1970: 1595443146.0), + cancellationDate: nil, + isInTrialPeriod: false, + isInIntroOfferPeriod: false, + webOrderLineItemId: 1000000054044800, + promotionalOfferIdentifier: nil), + AppleReceipt.InAppPurchase(quantity: 1, + productId: "com.revenuecat.monthly_4.99.1_week_intro", + transactionId: "1000000692878476", + originalTransactionId: "1000000692878476", + productType: .autoRenewableSubscription, + purchaseDate: Date(timeIntervalSince1970: 1594755206.0), + originalPurchaseDate: Date(timeIntervalSince1970: 1594755207.0), + expiresDate: Date(timeIntervalSince1970: 1594755386.0), + cancellationDate: nil, + isInTrialPeriod: true, + isInIntroOfferPeriod: false, + webOrderLineItemId: 1000000054042694, + promotionalOfferIdentifier: nil) + ] + } + + func testParseSandboxReceipt2() throws { // swiftlint:disable:this function_body_length + self.mockBundle.receiptURLResult = .sandboxReceipt2 + + let receiptURL = try XCTUnwrap(self.mockBundle.appStoreReceiptURL) + let data = try DefaultFileReader().contents(of: receiptURL) + let decodedData = try XCTUnwrap( + Data(base64Encoded: XCTUnwrap(String(data: data, encoding: .utf8))) + ) + + self.mockFileReader.mock(url: receiptURL, with: decodedData) + + let receipt = try self.fetchAndParse() + expect(receipt.environment) == .sandbox + expect(receipt.bundleId) == "com.mbaasy.ios.demo" + expect(receipt.applicationVersion) == "1" + expect(receipt.originalApplicationVersion) == "1.0" + expect(receipt.opaqueValue) == (try XCTUnwrap(Data(base64Encoded: "xN1AVLC2Gge+tYX2qELgSA=="))) + expect(receipt.sha1Hash) == (try XCTUnwrap(Data(base64Encoded: "LgoRW+rBxXAjpb03NJlVqa2Z200="))) + expect(receipt.creationDate) == Date(timeIntervalSince1970: 1439452246.0) + expect(receipt.expirationDate).to(beNil()) + expect(receipt.inAppPurchases) == [ + AppleReceipt.InAppPurchase(quantity: 1, + productId: "consumable", + transactionId: "1000000166865231", + originalTransactionId: "1000000166865231", + productType: .consumable, + purchaseDate: Date(timeIntervalSince1970: 1438979875.0), + originalPurchaseDate: Date(timeIntervalSince1970: 1438979875.0), + expiresDate: nil, + cancellationDate: nil, + isInTrialPeriod: false, + isInIntroOfferPeriod: nil, + webOrderLineItemId: 0, + promotionalOfferIdentifier: nil), + AppleReceipt.InAppPurchase(quantity: 1, + productId: "monthly", + transactionId: "1000000166965150", + originalTransactionId: "1000000166965150", + productType: .autoRenewableSubscription, + purchaseDate: Date(timeIntervalSince1970: 1439189372.0), + originalPurchaseDate: Date(timeIntervalSince1970: 1439189373.0), + expiresDate: Date(timeIntervalSince1970: 1439189672.0), + cancellationDate: nil, + isInTrialPeriod: false, + isInIntroOfferPeriod: nil, + webOrderLineItemId: 1000000030274153, + promotionalOfferIdentifier: nil), + AppleReceipt.InAppPurchase(quantity: 1, + productId: "monthly", + transactionId: "1000000166965327", + originalTransactionId: "1000000166965150", + productType: .autoRenewableSubscription, + purchaseDate: Date(timeIntervalSince1970: 1439189672.0), + originalPurchaseDate: Date(timeIntervalSince1970: 1439189598.0), + expiresDate: Date(timeIntervalSince1970: 1439189972.0), + cancellationDate: nil, + isInTrialPeriod: false, + isInIntroOfferPeriod: nil, + webOrderLineItemId: 1000000030274154, + promotionalOfferIdentifier: nil), + AppleReceipt.InAppPurchase(quantity: 1, + productId: "monthly", + transactionId: "1000000166965895", + originalTransactionId: "1000000166965150", + productType: .autoRenewableSubscription, + purchaseDate: Date(timeIntervalSince1970: 1439189972.0), + originalPurchaseDate: Date(timeIntervalSince1970: 1439189854.0), + expiresDate: Date(timeIntervalSince1970: 1439190272.0), + cancellationDate: nil, + isInTrialPeriod: false, + isInIntroOfferPeriod: nil, + webOrderLineItemId: 1000000030274165, + promotionalOfferIdentifier: nil), + AppleReceipt.InAppPurchase(quantity: 1, + productId: "monthly", + transactionId: "1000000166967152", + originalTransactionId: "1000000166965150", + productType: .autoRenewableSubscription, + purchaseDate: Date(timeIntervalSince1970: 1439190272.0), + originalPurchaseDate: Date(timeIntervalSince1970: 1439190153.0), + expiresDate: Date(timeIntervalSince1970: 1439190572.0), + cancellationDate: nil, + isInTrialPeriod: false, + isInIntroOfferPeriod: nil, + webOrderLineItemId: 1000000030274192, + promotionalOfferIdentifier: nil), + AppleReceipt.InAppPurchase(quantity: 1, + productId: "monthly", + transactionId: "1000000166967484", + originalTransactionId: "1000000166965150", + productType: .autoRenewableSubscription, + purchaseDate: Date(timeIntervalSince1970: 1439190572.0), + originalPurchaseDate: Date(timeIntervalSince1970: 1439190510.0), + expiresDate: Date(timeIntervalSince1970: 1439190872.0), + cancellationDate: nil, + isInTrialPeriod: false, + isInIntroOfferPeriod: nil, + webOrderLineItemId: 1000000030274219, + promotionalOfferIdentifier: nil), + AppleReceipt.InAppPurchase(quantity: 1, + productId: "monthly", + transactionId: "1000000166967782", + originalTransactionId: "1000000166965150", + productType: .autoRenewableSubscription, + purchaseDate: Date(timeIntervalSince1970: 1439190872.0), + originalPurchaseDate: Date(timeIntervalSince1970: 1439190754.0), + expiresDate: Date(timeIntervalSince1970: 1439191172.0), + cancellationDate: nil, + isInTrialPeriod: false, + isInIntroOfferPeriod: nil, + webOrderLineItemId: 1000000030274249, + promotionalOfferIdentifier: nil) + ] + } + + func testParseSandboxReceipt3() throws { + self.mockBundle.receiptURLResult = .sandboxReceipt3 + + let receiptURL = try XCTUnwrap(self.mockBundle.appStoreReceiptURL) + let data = try DefaultFileReader().contents(of: receiptURL) + let decodedData = try XCTUnwrap( + Data(base64Encoded: XCTUnwrap(String(data: data, encoding: .utf8))) + ) + + self.mockFileReader.mock(url: receiptURL, with: decodedData) + + let receipt = try self.fetchAndParse() + expect(receipt.environment) == .sandbox + expect(receipt.bundleId) == "com.belive.app.ios" + expect(receipt.applicationVersion) == "3" + expect(receipt.originalApplicationVersion) == "1.0" + expect(receipt.opaqueValue) == (try XCTUnwrap(Data(base64Encoded: "NOI0mwvWYuTsEnpr/RCvJA=="))) + expect(receipt.sha1Hash) == (try XCTUnwrap(Data(base64Encoded: "JzhO1BR1kxOVGrCEqQLkwvUuZP8="))) + expect(receipt.creationDate) == Date(timeIntervalSince1970: 1542127591.0) + expect(receipt.expirationDate).to(beNil()) + expect(receipt.inAppPurchases) == [ + AppleReceipt.InAppPurchase(quantity: 1, + productId: "test2", + transactionId: "1000000472106082", + originalTransactionId: "1000000472106082", + productType: .consumable, + purchaseDate: Date(timeIntervalSince1970: 1542127591.0), + originalPurchaseDate: Date(timeIntervalSince1970: 1542127591.0), + expiresDate: nil, + cancellationDate: nil, + isInTrialPeriod: false, + isInIntroOfferPeriod: nil, + webOrderLineItemId: 0, + promotionalOfferIdentifier: nil) + ] + } + + func testParseUnsupportedReceipt1() throws { + self.mockBundle.receiptURLResult = .unsupportedReceipt1 + + let receiptURL = try XCTUnwrap(self.mockBundle.appStoreReceiptURL) + let data = try DefaultFileReader().contents(of: receiptURL) + let decodedData = try XCTUnwrap( + Data(base64Encoded: XCTUnwrap(String(data: data, encoding: .utf8))) + ) + + self.mockFileReader.mock(url: receiptURL, with: decodedData) + do { + _ = try self.fetchAndParse() + fail("Expected error") + } catch { + expect(error).to(matchError( + PurchasesReceiptParser.Error.asn1ParsingError(description: "payload is shorter than length value")) + ) + } + } + + func testParseUnsupportedReceipt2() throws { + self.mockBundle.receiptURLResult = .unsupportedReceipt2 + + let receiptURL = try XCTUnwrap(self.mockBundle.appStoreReceiptURL) + let data = try DefaultFileReader().contents(of: receiptURL) + let decodedData = try XCTUnwrap( + Data(base64Encoded: XCTUnwrap(String(data: data, encoding: .utf8))) + ) + + self.mockFileReader.mock(url: receiptURL, with: decodedData) + do { + _ = try self.fetchAndParse() + fail("Expected error") + } catch { + expect(error).to(matchError( + PurchasesReceiptParser.Error.asn1ParsingError(description: "payload is shorter than length value")) + ) + } + } } // MARK: - Private @@ -132,3 +586,5 @@ private final class MockFileReader: FileReader { } } + +// swiftlint:disable:this file_length diff --git a/Tests/UnitTests/Resources/receipts/base64encoded_sandboxReceipt2.txt b/Tests/UnitTests/Resources/receipts/base64encoded_sandboxReceipt2.txt new file mode 100644 index 0000000000..25d2597725 --- /dev/null +++ b/Tests/UnitTests/Resources/receipts/base64encoded_sandboxReceipt2.txt @@ -0,0 +1 @@ +MIIcSAYJKoZIhvcNAQcCoIIcOTCCHDUCAQExCzAJBgUrDgMCGgUAMIIL+QYJKoZIhvcNAQcBoIIL6gSCC+YxggviMAoCAQgCAQEEAhYAMAoCARQCAQEEAgwAMAsCAQECAQEEAwIBADALAgEDAgEBBAMMATEwCwIBCwIBAQQDAgEAMAsCAQ4CAQEEAwIBWjALAgEPAgEBBAMCAQAwCwIBEAIBAQQDAgEAMAsCARkCAQEEAwIBAzAMAgEKAgEBBAQWAjQrMA0CAQ0CAQEEBQIDAV+QMA0CARMCAQEEBQwDMS4wMA4CAQkCAQEEBgIEUDIzNDAYAgEEAgECBBDE3UBUsLYaB761hfaoQuBIMBsCAQACAQEEEwwRUHJvZHVjdGlvblNhbmRib3gwHAIBBQIBAQQULgoRW+rBxXAjpb03NJlVqa2Z200wHQIBAgIBAQQVDBNjb20ubWJhYXN5Lmlvcy5kZW1vMB4CAQwCAQEEFhYUMjAxNS0wOC0xM1QwNzo1MDo0NlowHgIBEgIBAQQWFhQyMDEzLTA4LTAxVDA3OjAwOjAwWjBLAgEHAgEBBEPixSwGknvYHA7GNo11ue/NJtHjgD6PTlcYOBGS4D+bAG3hIUBsvRx0RDoaF7CUW1XeUoKFE/jqIHH5AzsHl3pS/WYgMGECAQYCAQEEWRhf33g2yaZFPTmP34+a61oc/n3P7iVoZOuazq1x1u1JXgIDY2hJpxZU4y6o5FBZ8JZC+6uvjOlYYiOd9QfeqHBV4YwCU5Mbd5L2aJji1yJYTDmqHEroWyJ4MIIBTwIBEQIBAQSCAUUxggFBMAsCAgasAgEBBAIWADALAgIGrQIBAQQCDAAwCwICBrACAQEEAhYAMAsCAgayAgEBBAIMADALAgIGswIBAQQCDAAwCwICBrQCAQEEAgwAMAsCAga1AgEBBAIMADALAgIGtgIBAQQCDAAwDAICBqUCAQEEAwIBATAMAgIGqwIBAQQDAgEBMAwCAgauAgEBBAMCAQAwDAICBq8CAQEEAwIBADAMAgIGsQIBAQQDAgEAMBUCAgamAgEBBAwMCmNvbnN1bWFibGUwGwICBqcCAQEEEgwQMTAwMDAwMDE2Njg2NTIzMTAbAgIGqQIBAQQSDBAxMDAwMDAwMTY2ODY1MjMxMB8CAgaoAgEBBBYWFDIwMTUtMDgtMDdUMjA6Mzc6NTVaMB8CAgaqAgEBBBYWFDIwMTUtMDgtMDdUMjA6Mzc6NTVaMIIBZgIBEQIBAQSCAVwxggFYMAsCAgatAgEBBAIMADALAgIGsAIBAQQCFgAwCwICBrICAQEEAgwAMAsCAgazAgEBBAIMADALAgIGtAIBAQQCDAAwCwICBrUCAQEEAgwAMAsCAga2AgEBBAIMADAMAgIGpQIBAQQDAgEBMAwCAgarAgEBBAMCAQMwDAICBq4CAQEEAwIBADAMAgIGsQIBAQQDAgEAMBICAgamAgEBBAkMB21vbnRobHkwEgICBq8CAQEECQIHA41+ppRyaTAbAgIGpwIBAQQSDBAxMDAwMDAwMTY2OTY1MTUwMBsCAgapAgEBBBIMEDEwMDAwMDAxNjY5NjUxNTAwHwICBqgCAQEEFhYUMjAxNS0wOC0xMFQwNjo0OTozMlowHwICBqoCAQEEFhYUMjAxNS0wOC0xMFQwNjo0OTozM1owHwICBqwCAQEEFhYUMjAxNS0wOC0xMFQwNjo1NDozMlowggFmAgERAgEBBIIBXDGCAVgwCwICBq0CAQEEAgwAMAsCAgawAgEBBAIWADALAgIGsgIBAQQCDAAwCwICBrMCAQEEAgwAMAsCAga0AgEBBAIMADALAgIGtQIBAQQCDAAwCwICBrYCAQEEAgwAMAwCAgalAgEBBAMCAQEwDAICBqsCAQEEAwIBAzAMAgIGrgIBAQQDAgEAMAwCAgaxAgEBBAMCAQAwEgICBqYCAQEECQwHbW9udGhseTASAgIGrwIBAQQJAgcDjX6mlHJqMBsCAganAgEBBBIMEDEwMDAwMDAxNjY5NjUzMjcwGwICBqkCAQEEEgwQMTAwMDAwMDE2Njk2NTE1MDAfAgIGqAIBAQQWFhQyMDE1LTA4LTEwVDA2OjU0OjMyWjAfAgIGqgIBAQQWFhQyMDE1LTA4LTEwVDA2OjUzOjE4WjAfAgIGrAIBAQQWFhQyMDE1LTA4LTEwVDA2OjU5OjMyWjCCAWYCARECAQEEggFcMYIBWDALAgIGrQIBAQQCDAAwCwICBrACAQEEAhYAMAsCAgayAgEBBAIMADALAgIGswIBAQQCDAAwCwICBrQCAQEEAgwAMAsCAga1AgEBBAIMADALAgIGtgIBAQQCDAAwDAICBqUCAQEEAwIBATAMAgIGqwIBAQQDAgEDMAwCAgauAgEBBAMCAQAwDAICBrECAQEEAwIBADASAgIGpgIBAQQJDAdtb250aGx5MBICAgavAgEBBAkCBwONfqaUcnUwGwICBqcCAQEEEgwQMTAwMDAwMDE2Njk2NTg5NTAbAgIGqQIBAQQSDBAxMDAwMDAwMTY2OTY1MTUwMB8CAgaoAgEBBBYWFDIwMTUtMDgtMTBUMDY6NTk6MzJaMB8CAgaqAgEBBBYWFDIwMTUtMDgtMTBUMDY6NTc6MzRaMB8CAgasAgEBBBYWFDIwMTUtMDgtMTBUMDc6MDQ6MzJaMIIBZgIBEQIBAQSCAVwxggFYMAsCAgatAgEBBAIMADALAgIGsAIBAQQCFgAwCwICBrICAQEEAgwAMAsCAgazAgEBBAIMADALAgIGtAIBAQQCDAAwCwICBrUCAQEEAgwAMAsCAga2AgEBBAIMADAMAgIGpQIBAQQDAgEBMAwCAgarAgEBBAMCAQMwDAICBq4CAQEEAwIBADAMAgIGsQIBAQQDAgEAMBICAgamAgEBBAkMB21vbnRobHkwEgICBq8CAQEECQIHA41+ppRykDAbAgIGpwIBAQQSDBAxMDAwMDAwMTY2OTY3MTUyMBsCAgapAgEBBBIMEDEwMDAwMDAxNjY5NjUxNTAwHwICBqgCAQEEFhYUMjAxNS0wOC0xMFQwNzowNDozMlowHwICBqoCAQEEFhYUMjAxNS0wOC0xMFQwNzowMjozM1owHwICBqwCAQEEFhYUMjAxNS0wOC0xMFQwNzowOTozMlowggFmAgERAgEBBIIBXDGCAVgwCwICBq0CAQEEAgwAMAsCAgawAgEBBAIWADALAgIGsgIBAQQCDAAwCwICBrMCAQEEAgwAMAsCAga0AgEBBAIMADALAgIGtQIBAQQCDAAwCwICBrYCAQEEAgwAMAwCAgalAgEBBAMCAQEwDAICBqsCAQEEAwIBAzAMAgIGrgIBAQQDAgEAMAwCAgaxAgEBBAMCAQAwEgICBqYCAQEECQwHbW9udGhseTASAgIGrwIBAQQJAgcDjX6mlHKrMBsCAganAgEBBBIMEDEwMDAwMDAxNjY5Njc0ODQwGwICBqkCAQEEEgwQMTAwMDAwMDE2Njk2NTE1MDAfAgIGqAIBAQQWFhQyMDE1LTA4LTEwVDA3OjA5OjMyWjAfAgIGqgIBAQQWFhQyMDE1LTA4LTEwVDA3OjA4OjMwWjAfAgIGrAIBAQQWFhQyMDE1LTA4LTEwVDA3OjE0OjMyWjCCAWYCARECAQEEggFcMYIBWDALAgIGrQIBAQQCDAAwCwICBrACAQEEAhYAMAsCAgayAgEBBAIMADALAgIGswIBAQQCDAAwCwICBrQCAQEEAgwAMAsCAga1AgEBBAIMADALAgIGtgIBAQQCDAAwDAICBqUCAQEEAwIBATAMAgIGqwIBAQQDAgEDMAwCAgauAgEBBAMCAQAwDAICBrECAQEEAwIBADASAgIGpgIBAQQJDAdtb250aGx5MBICAgavAgEBBAkCBwONfqaUcskwGwICBqcCAQEEEgwQMTAwMDAwMDE2Njk2Nzc4MjAbAgIGqQIBAQQSDBAxMDAwMDAwMTY2OTY1MTUwMB8CAgaoAgEBBBYWFDIwMTUtMDgtMTBUMDc6MTQ6MzJaMB8CAgaqAgEBBBYWFDIwMTUtMDgtMTBUMDc6MTI6MzRaMB8CAgasAgEBBBYWFDIwMTUtMDgtMTBUMDc6MTk6MzJaoIIOVTCCBWswggRToAMCAQICCBhZQyFydJz8MA0GCSqGSIb3DQEBBQUAMIGWMQswCQYDVQQGEwJVUzETMBEGA1UECgwKQXBwbGUgSW5jLjEsMCoGA1UECwwjQXBwbGUgV29ybGR3aWRlIERldmVsb3BlciBSZWxhdGlvbnMxRDBCBgNVBAMMO0FwcGxlIFdvcmxkd2lkZSBEZXZlbG9wZXIgUmVsYXRpb25zIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEwMTExMTIxNTgwMVoXDTE1MTExMTIxNTgwMVoweDEmMCQGA1UEAwwdTWFjIEFwcCBTdG9yZSBSZWNlaXB0IFNpZ25pbmcxLDAqBgNVBAsMI0FwcGxlIFdvcmxkd2lkZSBEZXZlbG9wZXIgUmVsYXRpb25zMRMwEQYDVQQKDApBcHBsZSBJbmMuMQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALaTwrcPJF7t0jRI6IUF4zOUZlvoJze/e0NJ6/nJF5czczJJSshvaCkUuJSm9GVLO0fX0SxmS7iY2bz1ElHL5i+p9LOfHOgo/FLAgaLLVmKAWqKRrk5Aw30oLtfT7U3ZrYr78mdI7Ot5vQJtBFkY/4w3n4o38WL/u6IDUIcK1ZLghhFeI0b14SVjK6JqjLIQt5EjTZo/g0DyZAla942uVlzU9bRuAxsEXSwbrwCZF9el+0mRzuKhETFeGQHA2s5Qg17I60k7SRoq6uCfv9JGSZzYq6GDYWwPwfyzrZl1Kvwjm+8iCOt7WRQRn3M0Lea5OaY79+Y+7Mqm+6uvJt+PiIECAwEAAaOCAdgwggHUMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUiCcXCam2GGCL7Ou69kdZxVJUo7cwTQYDVR0fBEYwRDBCoECgPoY8aHR0cDovL2RldmVsb3Blci5hcHBsZS5jb20vY2VydGlmaWNhdGlvbmF1dGhvcml0eS93d2RyY2EuY3JsMA4GA1UdDwEB/wQEAwIHgDAdBgNVHQ4EFgQUdXYkomtiDJc0ofpOXggMIr9z774wggERBgNVHSAEggEIMIIBBDCCAQAGCiqGSIb3Y2QFBgEwgfEwgcMGCCsGAQUFBwICMIG2DIGzUmVsaWFuY2Ugb24gdGhpcyBjZXJ0aWZpY2F0ZSBieSBhbnkgcGFydHkgYXNzdW1lcyBhY2NlcHRhbmNlIG9mIHRoZSB0aGVuIGFwcGxpY2FibGUgc3RhbmRhcmQgdGVybXMgYW5kIGNvbmRpdGlvbnMgb2YgdXNlLCBjZXJ0aWZpY2F0ZSBwb2xpY3kgYW5kIGNlcnRpZmljYXRpb24gcHJhY3RpY2Ugc3RhdGVtZW50cy4wKQYIKwYBBQUHAgEWHWh0dHA6Ly93d3cuYXBwbGUuY29tL2FwcGxlY2EvMBAGCiqGSIb3Y2QGCwEEAgUAMA0GCSqGSIb3DQEBBQUAA4IBAQCgO/GHvGm0t4N8GfSfxAJk3wLJjjFzyxw+3CYHi/2e8+2+Q9aNYS3k8NwWcwHWNKNpGXcUv7lYx1LJhgB/bGyAl6mZheh485oSp344OGTzBMtf8vZB+wclywIhcfNEP9Die2H3QuOrv3ds3SxQnICExaVvWFl6RjFBaLsTNUVCpIz6EdVLFvIyNd4fvNKZXcjmAjJZkOiNyznfIdrDdvt6NhoWGphMhRvmK0UtL1kaLcaa1maSo9I2UlCAIE0zyLKa1lNisWBS8PX3fRBQ5BK/vXG+tIDHbcRvWzk10ee33oEgJ444XIKHOnNgxNbxHKCpZkR+zgwomyN/rOzmoDvdMIIEIzCCAwugAwIBAgIBGTANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQXBwbGUgSW5jLjEmMCQGA1UECxMdQXBwbGUgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0EwHhcNMDgwMjE0MTg1NjM1WhcNMTYwMjE0MTg1NjM1WjCBljELMAkGA1UEBhMCVVMxEzARBgNVBAoMCkFwcGxlIEluYy4xLDAqBgNVBAsMI0FwcGxlIFdvcmxkd2lkZSBEZXZlbG9wZXIgUmVsYXRpb25zMUQwQgYDVQQDDDtBcHBsZSBXb3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9ucyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMo4VKbLVqrIJDlI6Yzu7F+4fyaRvDRTes58Y4Bhd2RepQcjtjn+UC0VVlhwLX7EbsFKhT4v8N6EGqFXya97GP9q+hUSSRUIGayq2yoy7ZZjaFIVPYyK7L9rGJXgA6wBfZcFZ84OhZU3au0Jtq5nzVFkn8Zc0bxXbmc1gHY2pIeBbjiP2CsVTnsl2Fq/ToPBjdKT1RpxtWCcnTNOVfkSWAyGuBYNweV3RY1QSLorLeSUheHoxJ3GaKWwo/xnfnC6AllLd0KRObn1zeFM78A7SIym5SFd/Wpqu6cWNWDS5q3zRinJ6MOL6XnAamFnFbLw/eVovGJfbs+Z3e8bY/6SZasCAwEAAaOBrjCBqzAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUiCcXCam2GGCL7Ou69kdZxVJUo7cwHwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01/CF4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL3d3dy5hcHBsZS5jb20vYXBwbGVjYS9yb290LmNybDAQBgoqhkiG92NkBgIBBAIFADANBgkqhkiG9w0BAQUFAAOCAQEA2jIAlsVUlNM7gjdmfS5o1cPGuMsmjEiQzxMkakaOY9Tw0BMG3djEwTcV8jMTOSYtzi5VQOMLA6/6EsLnDSG41YDPrCgvzi2zTq+GGQTG6VDdTClHECP8bLsbmGtIieFbnd5G2zWFNe8+0OJYSzj07XVaH1xwHVY5EuXhDRHkiSUGvdW0FY5e0FmXkOlLgeLfGK9EdB4ZoDpHzJEdOusjWv6lLZf3e7vWh0ZChetSPSayY6i0scqP9Mzis8hH4L+aWYP62phTKoL1fGUuldkzXfXtZcwxN8VaBOhr4eeIA0p1npsoy0pAiGVDdd3LOiUjxZ5X+C7O0qmSXnMuLyV1FTCCBLswggOjoAMCAQICAQIwDQYJKoZIhvcNAQEFBQAwYjELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkFwcGxlIEluYy4xJjAkBgNVBAsTHUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRYwFAYDVQQDEw1BcHBsZSBSb290IENBMB4XDTA2MDQyNTIxNDAzNloXDTM1MDIwOTIxNDAzNlowYjELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkFwcGxlIEluYy4xJjAkBgNVBAsTHUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRYwFAYDVQQDEw1BcHBsZSBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5JGpCR+R2x5HUOsF7V55hC3rNqJXTFXsixmJ3vlLbPUHqyIwAugYPvhQCdN/QaiY+dHKZpwkaxHQo7vkGyrDH5WeegykR4tb1BY3M8vED03OFGnRyRly9V0O1X9fm/IlA7pVj01dDfFkNSMVSxVZHbOU9/acns9QusFYUGePCLQg98usLCBvcLY/ATCMt0PPD5098ytJKBrI/s61uQ7ZXhzWyz21Oq30Dw4AkguxIRYudNU8DdtiFqujcZJHU1XBry9Bs/j743DN5qNMRX4fTGtQlkGJxHRiCxCDQYczioGxMFjsWgQyjGizjx3eZXP/Z15lvEnYdp8zFGWhd5TJLQIDAQABo4IBejCCAXYwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFCvQaUeUdgn+9GuNLkCm90dNfwheMB8GA1UdIwQYMBaAFCvQaUeUdgn+9GuNLkCm90dNfwheMIIBEQYDVR0gBIIBCDCCAQQwggEABgkqhkiG92NkBQEwgfIwKgYIKwYBBQUHAgEWHmh0dHBzOi8vd3d3LmFwcGxlLmNvbS9hcHBsZWNhLzCBwwYIKwYBBQUHAgIwgbYagbNSZWxpYW5jZSBvbiB0aGlzIGNlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRlIHBvbGljeSBhbmQgY2VydGlmaWNhdGlvbiBwcmFjdGljZSBzdGF0ZW1lbnRzLjANBgkqhkiG9w0BAQUFAAOCAQEAXDaZTC14t+2Mm9zzd5vydtJ3ME/BH4WDhRuZPUc38qmbQI4s1LGQEti+9HOb7tJkD8t5TzTYoj75eP9ryAfsfTmDi1Mg0zjEsb+aTwpr/yv8WacFCXwXQFYRHnTTt4sjO0ej1W8k4uvRt3DfD0XhJ8rxbXjt57UXF6jcfiI1yiXV2Q/Wa9SiJCMR96Gsj3OBYMYbWwkvkrL4REjwYDieFfU9JmcgijNq9w2Cz97roy/5U2pbZMBjM3f3OgcsVuvaDyEO2rpzGU+12TZ/wYdV2aeZuTJC+9jVcZ5+oVK3G72TQiQSKscPHbZNnF5jyEuAF1CqitXa5PzQCQc3sHV1ITGCAcswggHHAgEBMIGjMIGWMQswCQYDVQQGEwJVUzETMBEGA1UECgwKQXBwbGUgSW5jLjEsMCoGA1UECwwjQXBwbGUgV29ybGR3aWRlIERldmVsb3BlciBSZWxhdGlvbnMxRDBCBgNVBAMMO0FwcGxlIFdvcmxkd2lkZSBEZXZlbG9wZXIgUmVsYXRpb25zIENlcnRpZmljYXRpb24gQXV0aG9yaXR5AggYWUMhcnSc/DAJBgUrDgMCGgUAMA0GCSqGSIb3DQEBAQUABIIBAIa0+qXTtrEAb7a6j4vNufx2K9veaN6S7YJeS30EIaW6xpiwpz7gFz//uLk8NZkDN6fiFb+rAB00mI2CTX6lW3kGtUAAefg2tRFrJ0tpCdt5+03popovMb8KzDRqlJuKs6D6xzx+MSxKV6i7iPHHaxoq3eAFS2i9N6BwuH52vwSJ6+vUJsH+klw2sSCL/XAaLCpzQDS66V6CqbUo6peo/hqtHsOAjDmFgTNPXXVAPXMk+q3loe+oveSC4n362W7f+N6R9CB6lJ8rloluUN2JvNwiFzPY0eAtCToLpBfGdL2I3ymDFJWiQbCuuNRa/PfGBFpMc0PGSqyQ0C5GFULTDVU= \ No newline at end of file diff --git a/Tests/UnitTests/Resources/receipts/base64encoded_sandboxReceipt3.txt b/Tests/UnitTests/Resources/receipts/base64encoded_sandboxReceipt3.txt new file mode 100644 index 0000000000..f327e808c4 --- /dev/null +++ b/Tests/UnitTests/Resources/receipts/base64encoded_sandboxReceipt3.txt @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/Tests/UnitTests/Resources/receipts/base64encoded_unsupportedReceipt1.txt b/Tests/UnitTests/Resources/receipts/base64encoded_unsupportedReceipt1.txt new file mode 100644 index 0000000000..0743352e43 --- /dev/null +++ b/Tests/UnitTests/Resources/receipts/base64encoded_unsupportedReceipt1.txt @@ -0,0 +1 @@ +ewoJInNpZ25hdHVyZSIgPSAiQW5USlN6UUFqZWhXWW1ucWxvZk9ZVnFyWEo1MVVOWnI5Ly8ySFhxM01COWkyYVBqVmlsdjM4aXhtWm9PLzlZZlBsUkhZRHVzWFQySXBZYkRzNHBGWk53L21RTDFUemtJSWV0WWVhNE95anVWNUtsdUVCNExLVm9sN25tSGZkMjdISTZQTTZqQkRaS0xtcGt0bU5WQ21mbmhlVCtqbE1qTHg3ZVpLakhTRmhsUkFBQURWekNDQTFNd2dnSTdvQU1DQVFJQ0NCdXA0K1BBaG0vTE1BMEdDU3FHU0liM0RRRUJCUVVBTUg4eEN6QUpCZ05WQkFZVEFsVlRNUk13RVFZRFZRUUtEQXBCY0hCc1pTQkpibU11TVNZd0pBWURWUVFMREIxQmNIQnNaU0JEWlhKMGFXWnBZMkYwYVc5dUlFRjFkR2h2Y21sMGVURXpNREVHQTFVRUF3d3FRWEJ3YkdVZ2FWUjFibVZ6SUZOMGIzSmxJRU5sY25ScFptbGpZWFJwYjI0Z1FYVjBhRzl5YVhSNU1CNFhEVEUwTURZd056QXdNREl5TVZvWERURTJNRFV4T0RFNE16RXpNRm93WkRFak1DRUdBMVVFQXd3YVVIVnlZMmhoYzJWU1pXTmxhWEIwUTJWeWRHbG1hV05oZEdVeEd6QVpCZ05WQkFzTUVrRndjR3hsSUdsVWRXNWxjeUJUZEc5eVpURVRNQkVHQTFVRUNnd0tRWEJ3YkdVZ1NXNWpMakVMTUFrR0ExVUVCaE1DVlZNd2daOHdEUVlKS29aSWh2Y05BUUVCQlFBRGdZMEFNSUdKQW9HQkFNbVRFdUxnamltTHdSSnh5MW9FZjBlc1VORFZFSWU2d0Rzbm5hbDE0aE5CdDF2MTk1WDZuOTNZTzdnaTNvclBTdXg5RDU1NFNrTXArU2F5Zzg0bFRjMzYyVXRtWUxwV25iMzRucXlHeDlLQlZUeTVPR1Y0bGpFMU93QytvVG5STStRTFJDbWVOeE1iUFpoUzQ3VCtlWnRERWhWQjl1c2szK0pNMkNvZ2Z3bzdBZ01CQUFHamNqQndNQjBHQTFVZERnUVdCQlNKYUVlTnVxOURmNlpmTjY4RmUrSTJ1MjJzc0RBTUJnTlZIUk1CQWY4RUFqQUFNQjhHQTFVZEl3UVlNQmFBRkRZZDZPS2RndElCR0xVeWF3N1hRd3VSV0VNNk1BNEdBMVVkRHdFQi93UUVBd0lIZ0RBUUJnb3Foa2lHOTJOa0JnVUJCQUlGQURBTkJna3Foa2lHOXcwQkFRVUZBQU9DQVFFQWVhSlYyVTUxcnhmY3FBQWU1QzIvZkVXOEtVbDRpTzRsTXV0YTdONlh6UDFwWkl6MU5ra0N0SUl3ZXlOajVVUllISytIalJLU1U5UkxndU5sMG5rZnhxT2JpTWNrd1J1ZEtTcTY5Tkluclp5Q0Q2NlI0Szc3bmI5bE1UQUJTU1lsc0t0OG9OdGxoZ1IvMWtqU1NSUWNIa3RzRGNTaVFHS01ka1NscDRBeVhmN3ZuSFBCZTR5Q3dZVjJQcFNOMDRrYm9pSjNwQmx4c0d3Vi9abEwyNk0ydWVZSEtZQ3VYaGRxRnd4VmdtNTJoM29lSk9PdC92WTRFY1FxN2VxSG02bTAzWjliN1BSellNMktHWEhEbU9Nazd2RHBlTVZsTERQU0dZejErVTNzRHhKemViU3BiYUptVDdpbXpVS2ZnZ0VZN3h4ZjRjemZIMHlqNXdOelNHVE92UT09IjsKCSJwdXJjaGFzZS1pbmZvIiA9ICJld29KSW05eWFXZHBibUZzTFhCMWNtTm9ZWE5sTFdSaGRHVXRjSE4wSWlBOUlDSXlNREUxTFRFeExURTNJREE0T2pNME9qUXhJRUZ0WlhKcFkyRXZURzl6WDBGdVoyVnNaWE1pT3dvSkluQjFjbU5vWVhObExXUmhkR1V0YlhNaUlEMGdJakUwTkRjNE1qRXlPREV3TURBaU93b0pJblZ1YVhGMVpTMXBaR1Z1ZEdsbWFXVnlJaUE5SUNKa1pEQmxNek5sWW1ZMU5tSmlNV1l6WVdRMFl6SmtZbUZoTjJFd1lqTXpZV00yT0dJd1pUZzFJanNLQ1NKdmNtbG5hVzVoYkMxMGNtRnVjMkZqZEdsdmJpMXBaQ0lnUFNBaU1UQXdNREF3TURFNE1EWXpOakl5TmlJN0Nna2laWGh3YVhKbGN5MWtZWFJsSWlBOUlDSXhORFE0TXpRMk9EZ3hNREF3SWpzS0NTSjBjbUZ1YzJGamRHbHZiaTFwWkNJZ1BTQWlNVEF3TURBd01ERTRNVEk1T0Rjek5DSTdDZ2tpYjNKcFoybHVZV3d0Y0hWeVkyaGhjMlV0WkdGMFpTMXRjeUlnUFNBaU1UUTBOemMzT0RBNE1UQXdNQ0k3Q2draWQyVmlMVzl5WkdWeUxXeHBibVV0YVhSbGJTMXBaQ0lnUFNBaU1UQXdNREF3TURBek1Ea3pPRGN4TXlJN0Nna2lZblp5Y3lJZ1BTQWlNU0k3Q2draWRXNXBjWFZsTFhabGJtUnZjaTFwWkdWdWRHbG1hV1Z5SWlBOUlDSTBSVFZGTVVFelFpMUdNREZCTFRRNE5UVXRPREl3UmkxR016UTVSakV5TkRJeE5EZ2lPd29KSW1WNGNHbHlaWE10WkdGMFpTMW1iM0p0WVhSMFpXUXRjSE4wSWlBOUlDSXlNREUxTFRFeExUSXpJREl5T2pNME9qUXhJRUZ0WlhKcFkyRXZURzl6WDBGdVoyVnNaWE1pT3dvSkltbDBaVzB0YVdRaUlEMGdJakV3TWpnNU5UQTNPVGNpT3dvSkltVjRjR2x5WlhNdFpHRjBaUzFtYjNKdFlYUjBaV1FpSUQwZ0lqSXdNVFV0TVRFdE1qUWdNRFk2TXpRNk5ERWdSWFJqTDBkTlZDSTdDZ2tpY0hKdlpIVmpkQzFwWkNJZ1BTQWllV1ZoY214NUlqc0tDU0p3ZFhKamFHRnpaUzFrWVhSbElpQTlJQ0l5TURFMUxURXhMVEU0SURBME9qTTBPalF4SUVWMFl5OUhUVlFpT3dvSkltOXlhV2RwYm1Gc0xYQjFjbU5vWVhObExXUmhkR1VpSUQwZ0lqSXdNVFV0TVRFdE1UY2dNVFk2TXpRNk5ERWdSWFJqTDBkTlZDSTdDZ2tpWW1sa0lpQTlJQ0pqYjIwdWJXSmhZWE41TG1sdmN5NWtaVzF2SWpzS0NTSndkWEpqYUdGelpTMWtZWFJsTFhCemRDSWdQU0FpTWpBeE5TMHhNUzB4TnlBeU1Eb3pORG8wTVNCQmJXVnlhV05oTDB4dmMxOUJibWRsYkdWeklqc0tDU0p4ZFdGdWRHbDBlU0lnUFNBaU1TSTdDbjA9IjsKCSJlbnZpcm9ubWVudCIgPSAiU2FuZGJveCI7CgkicG9kIiA9ICIxMDAiOwoJInNpZ25pbmctc3RhdHVzIiA9ICIwIjsKfQ== \ No newline at end of file diff --git a/Tests/UnitTests/Resources/receipts/base64encoded_unsupportedReceipt2.txt b/Tests/UnitTests/Resources/receipts/base64encoded_unsupportedReceipt2.txt new file mode 100644 index 0000000000..eb51631364 --- /dev/null +++ b/Tests/UnitTests/Resources/receipts/base64encoded_unsupportedReceipt2.txt @@ -0,0 +1 @@ +eyJzaWduYXR1cmUiID0gIkFoaHEwbmFxaG01ci8wSUNuYk9hVThCeVBLNkRja2ZsanRCMDNnZUh4dk0ybEVjVkdqK2NVM1lnWGZ0RkZCZ2lFYkR1NGdoYXFVWFRqRzlpc25Zeit6VWFhTXRUZDJ6YnNLbFhIMitzYm1ZaC9tY2M2eWt3dVFkaFZsOWZKYkxNaFVXWHNTUlIxVlVjQlE4TkxjVGg5ZHNXOTVTREcxdG9DQk45cWM0L01CZlVBQUFEVnpDQ0ExTXdnZ0k3b0FNQ0FRSUNDQnVwNCtQQWhtL0xNQTBHQ1NxR1NJYjNEUUVCQlFVQU1IOHhDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFLREFwQmNIQnNaU0JKYm1NdU1TWXdKQVlEVlFRTERCMUJjSEJzWlNCRFpYSjBhV1pwWTJGMGFXOXVJRUYxZEdodmNtbDBlVEV6TURFR0ExVUVBd3dxUVhCd2JHVWdhVlIxYm1WeklGTjBiM0psSUVObGNuUnBabWxqWVhScGIyNGdRWFYwYUc5eWFYUjVNQjRYRFRFME1EWXdOekF3TURJeU1Wb1hEVEUyTURVeE9ERTRNekV6TUZvd1pERWpNQ0VHQTFVRUF3d2FVSFZ5WTJoaGMyVlNaV05sYVhCMFEyVnlkR2xtYVdOaGRHVXhHekFaQmdOVkJBc01Fa0Z3Y0d4bElHbFVkVzVsY3lCVGRHOXlaVEVUTUJFR0ExVUVDZ3dLUVhCd2JHVWdTVzVqTGpFTE1Ba0dBMVVFQmhNQ1ZWTXdnWjh3RFFZSktvWklodmNOQVFFQkJRQURnWTBBTUlHSkFvR0JBTW1URXVMZ2ppbUx3Ukp4eTFvRWYwZXNVTkRWRUllNndEc25uYWwxNGhOQnQxdjE5NVg2bjkzWU83Z2kzb3JQU3V4OUQ1NTRTa01wK1NheWc4NGxUYzM2MlV0bVlMcFduYjM0bnF5R3g5S0JWVHk1T0dWNGxqRTFPd0Mrb1RuUk0rUUxSQ21lTnhNYlBaaFM0N1QrZVp0REVoVkI5dXNrMytKTTJDb2dmd283QWdNQkFBR2pjakJ3TUIwR0ExVWREZ1FXQkJTSmFFZU51cTlEZjZaZk42OEZlK0kydTIyc3NEQU1CZ05WSFJNQkFmOEVBakFBTUI4R0ExVWRJd1FZTUJhQUZEWWQ2T0tkZ3RJQkdMVXlhdzdYUXd1UldFTTZNQTRHQTFVZER3RUIvd1FFQXdJSGdEQVFCZ29xaGtpRzkyTmtCZ1VCQkFJRkFEQU5CZ2txaGtpRzl3MEJBUVVGQUFPQ0FRRUFlYUpWMlU1MXJ4ZmNxQUFlNUMyL2ZFVzhLVWw0aU80bE11dGE3TjZYelAxcFpJejFOa2tDdElJd2V5Tmo1VVJZSEsrSGpSS1NVOVJMZ3VObDBua2Z4cU9iaU1ja3dSdWRLU3E2OU5JbnJaeUNENjZSNEs3N25iOWxNVEFCU1NZbHNLdDhvTnRsaGdSLzFralNTUlFjSGt0c0RjU2lRR0tNZGtTbHA0QXlYZjd2bkhQQmU0eUN3WVYyUHBTTjA0a2JvaUozcEJseHNHd1YvWmxMMjZNMnVlWUhLWUN1WGhkcUZ3eFZnbTUyaDNvZUpPT3Qvdlk0RWNRcTdlcUhtNm0wM1o5YjdQUnpZTTJLR1hIRG1PTWs3dkRwZU1WbExEUFNHWXoxK1Uzc0R4SnplYlNwYmFKbVQ3aW16VUtmZ2dFWTd4eGY0Y3pmSDB5ajV3TnpTR1RPdlE9PSI7ICJwdXJjaGFzZS1pbmZvIiA9ICJld29KSW05eWFXZHBibUZzTFhCMWNtTm9ZWE5sTFdSaGRHVXRjSE4wSWlBOUlDSXlNREUxTFRFeExUSXpJREE0T2pNeU9qSTNJRUZ0WlhKcFkyRXZURzl6WDBGdVoyVnNaWE1pT3dvSkluVnVhWEYxWlMxcFpHVnVkR2xtYVdWeUlpQTlJQ0psTkRjME1qVmtaamRtWWpaaFlqaGpNMk0zWlRVM016QmpNMkUzWldJMVlqWTFOak00TWpOa0lqc0tDU0p2Y21sbmFXNWhiQzEwY21GdWMyRmpkR2x2YmkxcFpDSWdQU0FpTVRBd01EQXdNREU0TVRRNU1qVTFOeUk3Q2draVluWnljeUlnUFNBaU15STdDZ2tpZEhKaGJuTmhZM1JwYjI0dGFXUWlJRDBnSWpFd01EQXdNREF4T0RFME9USTFOVGNpT3dvSkluRjFZVzUwYVhSNUlpQTlJQ0l4SWpzS0NTSnZjbWxuYVc1aGJDMXdkWEpqYUdGelpTMWtZWFJsTFcxeklpQTlJQ0l4TkRRNE1qazJNelEzTVRNNElqc0tDU0oxYm1seGRXVXRkbVZ1Wkc5eUxXbGtaVzUwYVdacFpYSWlJRDBnSWpWRE1qYzFRalZCTFRORk5qWXROREF5TmkwNU5USXpMVGcwTlVJeU1qVTNOVFZHUXlJN0Nna2ljSEp2WkhWamRDMXBaQ0lnUFNBaWNIVnlZMmhoYzJWeUxtTnZibk4xYldGaWJHVkdaV0YwZFhKbElqc0tDU0pwZEdWdExXbGtJaUE5SUNJeE1EWXhOVFUzTkRnMElqc0tDU0ppYVdRaUlEMGdJbU52YlM1bGN5NVFkWEpqYUdGelpYSWlPd29KSW5CMWNtTm9ZWE5sTFdSaGRHVXRiWE1pSUQwZ0lqRTBORGd5T1RZek5EY3hNemdpT3dvSkluQjFjbU5vWVhObExXUmhkR1VpSUQwZ0lqSXdNVFV0TVRFdE1qTWdNVFk2TXpJNk1qY2dSWFJqTDBkTlZDSTdDZ2tpY0hWeVkyaGhjMlV0WkdGMFpTMXdjM1FpSUQwZ0lqSXdNVFV0TVRFdE1qTWdNRGc2TXpJNk1qY2dRVzFsY21sallTOU1iM05mUVc1blpXeGxjeUk3Q2draWIzSnBaMmx1WVd3dGNIVnlZMmhoYzJVdFpHRjBaU0lnUFNBaU1qQXhOUzB4TVMweU15QXhOam96TWpveU55QkZkR012UjAxVUlqc0tmUT09IjsiZW52aXJvbm1lbnQiID0gIlNhbmRib3giOyJwb2QiID0gIjEwMCI7InNpZ25pbmctc3RhdHVzIiA9ICIwIjt9 \ No newline at end of file