From c7bc038fe39fc9bc78f0fca1b6c9a5a03ce304a4 Mon Sep 17 00:00:00 2001 From: Jubril Olambiwonnu Date: Thu, 9 Jul 2020 17:52:35 +0100 Subject: [PATCH 01/30] Bump version and organise files --- .../PaystackOSX-Debug.xcconfig | 0 .../PaystackOSX-Release.xcconfig | 0 .../PaystackOSX-Shared.xcconfig | 0 .../PaystackOSXTests-Debug.xcconfig | 0 .../PaystackOSXTests-Release.xcconfig | 0 .../PaystackOSXTests-Shared.xcconfig | 0 .../PaystackiOS Tests-Debug.xcconfig | 0 .../PaystackiOS Tests-Release.xcconfig | 0 .../PaystackiOS Tests-Shared.xcconfig | 0 .../PaystackiOS-Debug.xcconfig | 0 .../PaystackiOS-Release.xcconfig | 0 .../PaystackiOS-Shared.xcconfig | 0 .../PaystackiOSStatic.xcconfig | 0 .../PaystackiOSStaticFramework.xcconfig | 0 .../Project-Debug.xcconfig | 0 .../Project-Release.xcconfig | 0 .../Project-Shared.xcconfig | 0 Paystack.podspec | 18 +- Paystack.xcodeproj/project.pbxproj | 200 +++++++++++++----- .../PaystackError.h | 0 Paystack/{ => Error Handler}/PaystackError.m | 0 Paystack/{PublicHeaders => }/PSTCKAPIClient.h | 0 .../PSTCKAPIResponseDecodable.h | 0 Paystack/{PublicHeaders => }/PSTCKCard.h | 0 Paystack/{PublicHeaders => }/PSTCKCardBrand.h | 0 .../{PublicHeaders => }/PSTCKCardParams.h | 0 .../PSTCKCardValidationState.h | 0 .../{PublicHeaders => }/PSTCKCardValidator.h | 0 .../{PublicHeaders => }/PSTCKFormEncodable.h | 0 Paystack/{PublicHeaders => }/PSTCKToken.h | 0 .../{PublicHeaders => }/PSTCKTransaction.h | 0 .../PSTCKTransactionParams.h | 0 .../PSTCKValidationParams.h | 0 Paystack/{PublicHeaders => }/Paystack.h | 0 .../RequiredRule.swift | 0 .../{ => Textfield Validator}/Rule.swift | 0 .../Validatable.swift | 0 .../ValidationDelegate.swift | 0 .../ValidationError.swift | 0 .../ValidationRule.swift | 0 .../{ => Textfield Validator}/Validator.swift | 0 .../ValidatorDictionary.swift | 0 42 files changed, 154 insertions(+), 64 deletions(-) rename {Paystack/BuildConfigurations => BuildConfigurations}/PaystackOSX-Debug.xcconfig (100%) rename {Paystack/BuildConfigurations => BuildConfigurations}/PaystackOSX-Release.xcconfig (100%) rename {Paystack/BuildConfigurations => BuildConfigurations}/PaystackOSX-Shared.xcconfig (100%) rename {Paystack/BuildConfigurations => BuildConfigurations}/PaystackOSXTests-Debug.xcconfig (100%) rename {Paystack/BuildConfigurations => BuildConfigurations}/PaystackOSXTests-Release.xcconfig (100%) rename {Paystack/BuildConfigurations => BuildConfigurations}/PaystackOSXTests-Shared.xcconfig (100%) rename {Paystack/BuildConfigurations => BuildConfigurations}/PaystackiOS Tests-Debug.xcconfig (100%) rename {Paystack/BuildConfigurations => BuildConfigurations}/PaystackiOS Tests-Release.xcconfig (100%) rename {Paystack/BuildConfigurations => BuildConfigurations}/PaystackiOS Tests-Shared.xcconfig (100%) rename {Paystack/BuildConfigurations => BuildConfigurations}/PaystackiOS-Debug.xcconfig (100%) rename {Paystack/BuildConfigurations => BuildConfigurations}/PaystackiOS-Release.xcconfig (100%) rename {Paystack/BuildConfigurations => BuildConfigurations}/PaystackiOS-Shared.xcconfig (100%) rename {Paystack/BuildConfigurations => BuildConfigurations}/PaystackiOSStatic.xcconfig (100%) rename {Paystack/BuildConfigurations => BuildConfigurations}/PaystackiOSStaticFramework.xcconfig (100%) rename {Paystack/BuildConfigurations => BuildConfigurations}/Project-Debug.xcconfig (100%) rename {Paystack/BuildConfigurations => BuildConfigurations}/Project-Release.xcconfig (100%) rename {Paystack/BuildConfigurations => BuildConfigurations}/Project-Shared.xcconfig (100%) rename Paystack/{PublicHeaders => Error Handler}/PaystackError.h (100%) rename Paystack/{ => Error Handler}/PaystackError.m (100%) rename Paystack/{PublicHeaders => }/PSTCKAPIClient.h (100%) rename Paystack/{PublicHeaders => }/PSTCKAPIResponseDecodable.h (100%) rename Paystack/{PublicHeaders => }/PSTCKCard.h (100%) rename Paystack/{PublicHeaders => }/PSTCKCardBrand.h (100%) rename Paystack/{PublicHeaders => }/PSTCKCardParams.h (100%) rename Paystack/{PublicHeaders => }/PSTCKCardValidationState.h (100%) rename Paystack/{PublicHeaders => }/PSTCKCardValidator.h (100%) rename Paystack/{PublicHeaders => }/PSTCKFormEncodable.h (100%) rename Paystack/{PublicHeaders => }/PSTCKToken.h (100%) rename Paystack/{PublicHeaders => }/PSTCKTransaction.h (100%) rename Paystack/{PublicHeaders => }/PSTCKTransactionParams.h (100%) rename Paystack/{PublicHeaders => }/PSTCKValidationParams.h (100%) rename Paystack/{PublicHeaders => }/Paystack.h (100%) rename Paystack/Validator/{ => Textfield Validator}/RequiredRule.swift (100%) rename Paystack/Validator/{ => Textfield Validator}/Rule.swift (100%) rename Paystack/Validator/{ => Textfield Validator}/Validatable.swift (100%) rename Paystack/Validator/{ => Textfield Validator}/ValidationDelegate.swift (100%) rename Paystack/Validator/{ => Textfield Validator}/ValidationError.swift (100%) rename Paystack/Validator/{ => Textfield Validator}/ValidationRule.swift (100%) rename Paystack/Validator/{ => Textfield Validator}/Validator.swift (100%) rename Paystack/Validator/{ => Textfield Validator}/ValidatorDictionary.swift (100%) diff --git a/Paystack/BuildConfigurations/PaystackOSX-Debug.xcconfig b/BuildConfigurations/PaystackOSX-Debug.xcconfig similarity index 100% rename from Paystack/BuildConfigurations/PaystackOSX-Debug.xcconfig rename to BuildConfigurations/PaystackOSX-Debug.xcconfig diff --git a/Paystack/BuildConfigurations/PaystackOSX-Release.xcconfig b/BuildConfigurations/PaystackOSX-Release.xcconfig similarity index 100% rename from Paystack/BuildConfigurations/PaystackOSX-Release.xcconfig rename to BuildConfigurations/PaystackOSX-Release.xcconfig diff --git a/Paystack/BuildConfigurations/PaystackOSX-Shared.xcconfig b/BuildConfigurations/PaystackOSX-Shared.xcconfig similarity index 100% rename from Paystack/BuildConfigurations/PaystackOSX-Shared.xcconfig rename to BuildConfigurations/PaystackOSX-Shared.xcconfig diff --git a/Paystack/BuildConfigurations/PaystackOSXTests-Debug.xcconfig b/BuildConfigurations/PaystackOSXTests-Debug.xcconfig similarity index 100% rename from Paystack/BuildConfigurations/PaystackOSXTests-Debug.xcconfig rename to BuildConfigurations/PaystackOSXTests-Debug.xcconfig diff --git a/Paystack/BuildConfigurations/PaystackOSXTests-Release.xcconfig b/BuildConfigurations/PaystackOSXTests-Release.xcconfig similarity index 100% rename from Paystack/BuildConfigurations/PaystackOSXTests-Release.xcconfig rename to BuildConfigurations/PaystackOSXTests-Release.xcconfig diff --git a/Paystack/BuildConfigurations/PaystackOSXTests-Shared.xcconfig b/BuildConfigurations/PaystackOSXTests-Shared.xcconfig similarity index 100% rename from Paystack/BuildConfigurations/PaystackOSXTests-Shared.xcconfig rename to BuildConfigurations/PaystackOSXTests-Shared.xcconfig diff --git a/Paystack/BuildConfigurations/PaystackiOS Tests-Debug.xcconfig b/BuildConfigurations/PaystackiOS Tests-Debug.xcconfig similarity index 100% rename from Paystack/BuildConfigurations/PaystackiOS Tests-Debug.xcconfig rename to BuildConfigurations/PaystackiOS Tests-Debug.xcconfig diff --git a/Paystack/BuildConfigurations/PaystackiOS Tests-Release.xcconfig b/BuildConfigurations/PaystackiOS Tests-Release.xcconfig similarity index 100% rename from Paystack/BuildConfigurations/PaystackiOS Tests-Release.xcconfig rename to BuildConfigurations/PaystackiOS Tests-Release.xcconfig diff --git a/Paystack/BuildConfigurations/PaystackiOS Tests-Shared.xcconfig b/BuildConfigurations/PaystackiOS Tests-Shared.xcconfig similarity index 100% rename from Paystack/BuildConfigurations/PaystackiOS Tests-Shared.xcconfig rename to BuildConfigurations/PaystackiOS Tests-Shared.xcconfig diff --git a/Paystack/BuildConfigurations/PaystackiOS-Debug.xcconfig b/BuildConfigurations/PaystackiOS-Debug.xcconfig similarity index 100% rename from Paystack/BuildConfigurations/PaystackiOS-Debug.xcconfig rename to BuildConfigurations/PaystackiOS-Debug.xcconfig diff --git a/Paystack/BuildConfigurations/PaystackiOS-Release.xcconfig b/BuildConfigurations/PaystackiOS-Release.xcconfig similarity index 100% rename from Paystack/BuildConfigurations/PaystackiOS-Release.xcconfig rename to BuildConfigurations/PaystackiOS-Release.xcconfig diff --git a/Paystack/BuildConfigurations/PaystackiOS-Shared.xcconfig b/BuildConfigurations/PaystackiOS-Shared.xcconfig similarity index 100% rename from Paystack/BuildConfigurations/PaystackiOS-Shared.xcconfig rename to BuildConfigurations/PaystackiOS-Shared.xcconfig diff --git a/Paystack/BuildConfigurations/PaystackiOSStatic.xcconfig b/BuildConfigurations/PaystackiOSStatic.xcconfig similarity index 100% rename from Paystack/BuildConfigurations/PaystackiOSStatic.xcconfig rename to BuildConfigurations/PaystackiOSStatic.xcconfig diff --git a/Paystack/BuildConfigurations/PaystackiOSStaticFramework.xcconfig b/BuildConfigurations/PaystackiOSStaticFramework.xcconfig similarity index 100% rename from Paystack/BuildConfigurations/PaystackiOSStaticFramework.xcconfig rename to BuildConfigurations/PaystackiOSStaticFramework.xcconfig diff --git a/Paystack/BuildConfigurations/Project-Debug.xcconfig b/BuildConfigurations/Project-Debug.xcconfig similarity index 100% rename from Paystack/BuildConfigurations/Project-Debug.xcconfig rename to BuildConfigurations/Project-Debug.xcconfig diff --git a/Paystack/BuildConfigurations/Project-Release.xcconfig b/BuildConfigurations/Project-Release.xcconfig similarity index 100% rename from Paystack/BuildConfigurations/Project-Release.xcconfig rename to BuildConfigurations/Project-Release.xcconfig diff --git a/Paystack/BuildConfigurations/Project-Shared.xcconfig b/BuildConfigurations/Project-Shared.xcconfig similarity index 100% rename from Paystack/BuildConfigurations/Project-Shared.xcconfig rename to BuildConfigurations/Project-Shared.xcconfig diff --git a/Paystack.podspec b/Paystack.podspec index d8e1550..5b68cc9 100644 --- a/Paystack.podspec +++ b/Paystack.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'Paystack' - s.version = '3.0.13' + s.version = '3.0.14' s.summary = 'Paystack is a web-based API helping African Businesses accept payments online.' s.description = <<-DESC Paystack makes it easy for African Businesses to accept Mastercard, Visa and Verve cards from anyone, anywhere in the world. @@ -8,20 +8,14 @@ Pod::Spec.new do |s| s.license = { :type => 'MIT', :file => 'LICENSE' } s.homepage = 'https://paystack.com' - s.authors = { 'Ibrahim Lawal' => 'ibrahim@paystack.com', 'Paystack' => 'support@paystack.com' } + s.authors = { 'Jubril Olambiwonnu' => 'jubril@paystack.com', 'Ibrahim Lawal' => 'ibrahim@paystack.com', 'Paystack' => 'support@paystack.com' } s.source = { :git => 'https://github.com/paystackhq/paystack-ios.git', :tag => "v#{s.version}" } s.ios.frameworks = 'Foundation', 'Security' s.ios.weak_frameworks = 'PassKit', 'AddressBook' s.requires_arc = true - s.ios.deployment_target = '8.0' - s.default_subspecs = 'Core' - - s.subspec 'Core' do |ss| - ss.public_header_files = 'Paystack/PublicHeaders/*.h', 'Paystack/RSA/*.h' - ss.ios.public_header_files = 'Paystack/PublicHeaders/UI/*.h' - ss.source_files = 'Paystack/PublicHeaders/*.h', 'Paystack/RSA/*.{h,m}', 'Paystack/*.{h,m}' - ss.ios.source_files = 'Paystack/PublicHeaders/UI/*.h', 'Paystack/UI/*.{h,m}', 'Paystack/Fabric/*' - ss.resources = 'Paystack/Resources/**/*' - end + s.ios.deployment_target = '11.0' + s.swift_versions = '5.0' +s.source_files = 'Paystack/Paystack/Classes/**/*' + end diff --git a/Paystack.xcodeproj/project.pbxproj b/Paystack.xcodeproj/project.pbxproj index 94e5ff1..a786ce8 100644 --- a/Paystack.xcodeproj/project.pbxproj +++ b/Paystack.xcodeproj/project.pbxproj @@ -360,7 +360,7 @@ 0438EF2A1B7416BB00D506CC /* PSTCKPaymentCardTextFieldViewModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PSTCKPaymentCardTextFieldViewModel.h; path = UI/PSTCKPaymentCardTextFieldViewModel.h; sourceTree = ""; }; 0438EF2B1B7416BB00D506CC /* PSTCKPaymentCardTextFieldViewModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PSTCKPaymentCardTextFieldViewModel.m; path = UI/PSTCKPaymentCardTextFieldViewModel.m; sourceTree = ""; }; 0438EF3F1B74170D00D506CC /* PSTCKCardValidator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKCardValidator.m; sourceTree = ""; }; - 0438EF461B74183100D506CC /* PSTCKCardBrand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PSTCKCardBrand.h; path = PublicHeaders/PSTCKCardBrand.h; sourceTree = ""; }; + 0438EF461B74183100D506CC /* PSTCKCardBrand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKCardBrand.h; sourceTree = ""; }; 0438EF4A1B741B0100D506CC /* PSTCKCardValidatorTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKCardValidatorTest.m; sourceTree = ""; }; 0438EF4B1B741B0100D506CC /* PSTCKPaymentCardTextFieldViewModelTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKPaymentCardTextFieldViewModelTest.m; sourceTree = ""; }; 0438EF891B741C2800D506CC /* pstck_card_amex.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = pstck_card_amex.png; sourceTree = ""; }; @@ -402,16 +402,16 @@ 04B33F301BC7417B00DD8120 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 04B94BC71A47B78A00092C46 /* AddressBook.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AddressBook.framework; path = System/Library/Frameworks/AddressBook.framework; sourceTree = SDKROOT; }; 04CDB4421A5F2E1800B854EE /* Paystack.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Paystack.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 04CDB4A91A5F30A700B854EE /* Paystack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Paystack.h; path = PublicHeaders/Paystack.h; sourceTree = ""; }; - 04CDB4C21A5F30A700B854EE /* PSTCKAPIClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = PSTCKAPIClient.h; path = PublicHeaders/PSTCKAPIClient.h; sourceTree = ""; }; + 04CDB4A91A5F30A700B854EE /* Paystack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Paystack.h; sourceTree = ""; }; + 04CDB4C21A5F30A700B854EE /* PSTCKAPIClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = PSTCKAPIClient.h; sourceTree = ""; }; 04CDB4C31A5F30A700B854EE /* PSTCKAPIClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKAPIClient.m; sourceTree = ""; }; 04CDB4C41A5F30A700B854EE /* PSTCKFormEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKFormEncoder.h; sourceTree = ""; }; 04CDB4C51A5F30A700B854EE /* PSTCKFormEncoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKFormEncoder.m; sourceTree = ""; }; - 04CDB4CA1A5F30A700B854EE /* PSTCKCard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PSTCKCard.h; path = PublicHeaders/PSTCKCard.h; sourceTree = ""; }; + 04CDB4CA1A5F30A700B854EE /* PSTCKCard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKCard.h; sourceTree = ""; }; 04CDB4CB1A5F30A700B854EE /* PSTCKCard.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKCard.m; sourceTree = ""; }; - 04CDB4CC1A5F30A700B854EE /* PSTCKToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PSTCKToken.h; path = PublicHeaders/PSTCKToken.h; sourceTree = ""; }; + 04CDB4CC1A5F30A700B854EE /* PSTCKToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKToken.h; sourceTree = ""; }; 04CDB4CD1A5F30A700B854EE /* PSTCKToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKToken.m; sourceTree = ""; }; - 04CDB4CE1A5F30A700B854EE /* PaystackError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PaystackError.h; path = PublicHeaders/PaystackError.h; sourceTree = ""; }; + 04CDB4CE1A5F30A700B854EE /* PaystackError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PaystackError.h; sourceTree = ""; }; 04CDB4CF1A5F30A700B854EE /* PaystackError.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PaystackError.m; sourceTree = ""; }; 04CDB51E1A5F3A9300B854EE /* PSTCKAPIClientTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKAPIClientTest.m; sourceTree = ""; }; 04CDB51F1A5F3A9300B854EE /* PSTCKFormEncoderTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKFormEncoderTest.m; sourceTree = ""; }; @@ -420,15 +420,15 @@ 04CDB5261A5F3A9300B854EE /* PSTCKCertTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKCertTest.m; sourceTree = ""; }; 04CDB5271A5F3A9300B854EE /* PSTCKTokenTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKTokenTest.m; sourceTree = ""; }; 04CDE5B41BC1F1F100548833 /* PSTCKCardParams.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKCardParams.m; sourceTree = ""; }; - 04CDE5BB1BC1F21500548833 /* PSTCKCardParams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PSTCKCardParams.h; path = PublicHeaders/PSTCKCardParams.h; sourceTree = ""; }; + 04CDE5BB1BC1F21500548833 /* PSTCKCardParams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKCardParams.h; sourceTree = ""; }; 04D12C061A5F556D0010446E /* PaystackOSX.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PaystackOSX.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 04D12C101A5F556D0010446E /* PaystackOSXTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PaystackOSXTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 04D5BF9019BF958F009521A5 /* PassKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PassKit.framework; path = System/Library/Frameworks/PassKit.framework; sourceTree = SDKROOT; }; 04E32A9C1B7A9490009C9E35 /* PSTCKPaymentCardTextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PSTCKPaymentCardTextField.h; path = PublicHeaders/UI/PSTCKPaymentCardTextField.h; sourceTree = ""; }; - 04EBC7511B7533C300A0E6AE /* PSTCKCardValidationState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PSTCKCardValidationState.h; path = PublicHeaders/PSTCKCardValidationState.h; sourceTree = ""; }; - 04EBC7521B7533C300A0E6AE /* PSTCKCardValidator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PSTCKCardValidator.h; path = PublicHeaders/PSTCKCardValidator.h; sourceTree = ""; }; - 04F213301BCEAB61001D6F22 /* PSTCKFormEncodable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PSTCKFormEncodable.h; path = PublicHeaders/PSTCKFormEncodable.h; sourceTree = ""; }; - 04F213341BCECB1C001D6F22 /* PSTCKAPIResponseDecodable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PSTCKAPIResponseDecodable.h; path = PublicHeaders/PSTCKAPIResponseDecodable.h; sourceTree = ""; }; + 04EBC7511B7533C300A0E6AE /* PSTCKCardValidationState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKCardValidationState.h; sourceTree = ""; }; + 04EBC7521B7533C300A0E6AE /* PSTCKCardValidator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKCardValidator.h; sourceTree = ""; }; + 04F213301BCEAB61001D6F22 /* PSTCKFormEncodable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKFormEncodable.h; sourceTree = ""; }; + 04F213341BCECB1C001D6F22 /* PSTCKAPIResponseDecodable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKAPIResponseDecodable.h; sourceTree = ""; }; 04F39F0A1AEF2AFE005B926E /* Project-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Project-Debug.xcconfig"; sourceTree = ""; }; 04F39F0B1AEF2AFE005B926E /* Project-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Project-Release.xcconfig"; sourceTree = ""; }; 04F39F0C1AEF2AFE005B926E /* Project-Shared.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Project-Shared.xcconfig"; sourceTree = ""; }; @@ -448,12 +448,12 @@ 04F39F241AEF2AFE005B926E /* PaystackOSXTests-Shared.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "PaystackOSXTests-Shared.xcconfig"; sourceTree = ""; }; 04FCFA171BD59A8C00297732 /* PSTCKCategoryLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKCategoryLoader.h; sourceTree = ""; }; 04FCFA181BD59A8C00297732 /* PSTCKCategoryLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKCategoryLoader.m; sourceTree = ""; }; - 0806F2F21DBB9E7100C2741B /* PSTCKTransaction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PSTCKTransaction.h; path = PublicHeaders/PSTCKTransaction.h; sourceTree = ""; }; - 0806F2F31DBB9E7100C2741B /* PSTCKTransactionParams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PSTCKTransactionParams.h; path = PublicHeaders/PSTCKTransactionParams.h; sourceTree = ""; }; + 0806F2F21DBB9E7100C2741B /* PSTCKTransaction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKTransaction.h; sourceTree = ""; }; + 0806F2F31DBB9E7100C2741B /* PSTCKTransactionParams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKTransactionParams.h; sourceTree = ""; }; 0806F2F61DBB9E8200C2741B /* PSTCKTransaction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKTransaction.m; sourceTree = ""; }; 0806F2F71DBB9E8200C2741B /* PSTCKTransactionParams.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKTransactionParams.m; sourceTree = ""; }; 0806F2FA1DBBA3C500C2741B /* PSTCKValidationParams.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKValidationParams.m; sourceTree = ""; }; - 0806F2FC1DBBA3F600C2741B /* PSTCKValidationParams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PSTCKValidationParams.h; path = PublicHeaders/PSTCKValidationParams.h; sourceTree = ""; }; + 0806F2FC1DBBA3F600C2741B /* PSTCKValidationParams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKValidationParams.h; sourceTree = ""; }; 0806F2FE1DBBB5DF00C2741B /* pstck_card_verve.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = pstck_card_verve.png; sourceTree = ""; }; 0806F2FF1DBBB5DF00C2741B /* pstck_card_verve@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "pstck_card_verve@2x.png"; sourceTree = ""; }; 0806F3001DBBB5DF00C2741B /* pstck_card_verve@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "pstck_card_verve@3x.png"; sourceTree = ""; }; @@ -624,45 +624,9 @@ 04CDB4D21A5F30A700B854EE /* Paystack */ = { isa = PBXGroup; children = ( - 04CDB4A91A5F30A700B854EE /* Paystack.h */, - 10500AB01C81357A00EEF7CF /* RSA */, - 04F39F091AEF2AFE005B926E /* BuildConfigurations */, + 91F7793324B774CF0003C7AC /* Classes */, 0438EF871B741C2800D506CC /* Resources */, - 0438EF251B74162700D506CC /* UI */, 04B33F2F1BC7414C00DD8120 /* Supporting Files */, - 91FEDC4924B69EF000A236BA /* Validator */, - 04CDB4C21A5F30A700B854EE /* PSTCKAPIClient.h */, - 04CDB4C31A5F30A700B854EE /* PSTCKAPIClient.m */, - 919A9FBA249C442300B7A571 /* PSTCKAPIClientExtension.swift */, - 049952D11BCF13DD0088C703 /* PSTCKAPIClient+Private.h */, - 049952CD1BCF13510088C703 /* PSTCKAPIPostRequest.h */, - 049952CE1BCF13510088C703 /* PSTCKAPIPostRequest.m */, - 04CDB4C41A5F30A700B854EE /* PSTCKFormEncoder.h */, - 04CDB4C51A5F30A700B854EE /* PSTCKFormEncoder.m */, - 0438EF461B74183100D506CC /* PSTCKCardBrand.h */, - 04F213301BCEAB61001D6F22 /* PSTCKFormEncodable.h */, - 04F213341BCECB1C001D6F22 /* PSTCKAPIResponseDecodable.h */, - 04CDE5BB1BC1F21500548833 /* PSTCKCardParams.h */, - 04CDE5B41BC1F1F100548833 /* PSTCKCardParams.m */, - 04CDB4CA1A5F30A700B854EE /* PSTCKCard.h */, - 04CDB4CB1A5F30A700B854EE /* PSTCKCard.m */, - 04EBC7511B7533C300A0E6AE /* PSTCKCardValidationState.h */, - 04EBC7521B7533C300A0E6AE /* PSTCKCardValidator.h */, - 0438EF3F1B74170D00D506CC /* PSTCKCardValidator.m */, - 0806F2F61DBB9E8200C2741B /* PSTCKTransaction.m */, - 0806F2F31DBB9E7100C2741B /* PSTCKTransactionParams.h */, - 0806F2F71DBB9E8200C2741B /* PSTCKTransactionParams.m */, - 0806F2FC1DBBA3F600C2741B /* PSTCKValidationParams.h */, - 0806F2FA1DBBA3C500C2741B /* PSTCKValidationParams.m */, - 0806F2F21DBB9E7100C2741B /* PSTCKTransaction.h */, - 04CDB4CC1A5F30A700B854EE /* PSTCKToken.h */, - 04CDB4CD1A5F30A700B854EE /* PSTCKToken.m */, - 04CDB4CE1A5F30A700B854EE /* PaystackError.h */, - 04CDB4CF1A5F30A700B854EE /* PaystackError.m */, - 0433EB471BD06313003912B4 /* NSDictionary+Paystack.h */, - 0433EB481BD06313003912B4 /* NSDictionary+Paystack.m */, - 04FCFA171BD59A8C00297732 /* PSTCKCategoryLoader.h */, - 04FCFA181BD59A8C00297732 /* PSTCKCategoryLoader.m */, ); name = Paystack; path = Tests/../Paystack; @@ -723,6 +687,7 @@ 11C74B8D164043050071C2CA = { isa = PBXGroup; children = ( + 04F39F091AEF2AFE005B926E /* BuildConfigurations */, 04CDB4D21A5F30A700B854EE /* Paystack */, 04CDB5281A5F3A9300B854EE /* PaystackTests */, 11C74B9A164043050071C2CA /* Frameworks */, @@ -756,7 +721,52 @@ name = Frameworks; sourceTree = ""; }; - 91FEDC4924B69EF000A236BA /* Validator */ = { + 91F7793324B774CF0003C7AC /* Classes */ = { + isa = PBXGroup; + children = ( + 04CDB4A91A5F30A700B854EE /* Paystack.h */, + 91F7793424B77F4D0003C7AC /* API Client */, + 91F7793924B781800003C7AC /* Card */, + 91F7793824B781270003C7AC /* Error Handler */, + 91F7793A24B7819B0003C7AC /* Form Encoder */, + 91F7793724B7809F0003C7AC /* Helpers */, + 91F7793B24B781BB0003C7AC /* Transaction Model */, + 91F7793C24B781EF0003C7AC /* Token */, + 10500AB01C81357A00EEF7CF /* RSA */, + 0438EF251B74162700D506CC /* UI */, + 91FEDC4924B69EF000A236BA /* Validator */, + ); + name = Classes; + sourceTree = ""; + }; + 91F7793424B77F4D0003C7AC /* API Client */ = { + isa = PBXGroup; + children = ( + 049952CD1BCF13510088C703 /* PSTCKAPIPostRequest.h */, + 049952CE1BCF13510088C703 /* PSTCKAPIPostRequest.m */, + 04F213341BCECB1C001D6F22 /* PSTCKAPIResponseDecodable.h */, + 04CDB4C21A5F30A700B854EE /* PSTCKAPIClient.h */, + 04CDB4C31A5F30A700B854EE /* PSTCKAPIClient.m */, + 919A9FBA249C442300B7A571 /* PSTCKAPIClientExtension.swift */, + 049952D11BCF13DD0088C703 /* PSTCKAPIClient+Private.h */, + ); + name = "API Client"; + sourceTree = ""; + }; + 91F7793524B77FA20003C7AC /* Card Validator */ = { + isa = PBXGroup; + children = ( + 04EBC7511B7533C300A0E6AE /* PSTCKCardValidationState.h */, + 04EBC7521B7533C300A0E6AE /* PSTCKCardValidator.h */, + 0438EF3F1B74170D00D506CC /* PSTCKCardValidator.m */, + 0806F2FC1DBBA3F600C2741B /* PSTCKValidationParams.h */, + 0806F2FA1DBBA3C500C2741B /* PSTCKValidationParams.m */, + ); + name = "Card Validator"; + path = ..; + sourceTree = ""; + }; + 91F7793624B77FBD0003C7AC /* Textfield Validator */ = { isa = PBXGroup; children = ( 91FEDC4A24B69EF000A236BA /* ValidatorDictionary.swift */, @@ -768,6 +778,77 @@ 91FEDC5024B69EF000A236BA /* ValidationError.swift */, 91FEDC5124B69EF000A236BA /* ValidationRule.swift */, ); + path = "Textfield Validator"; + sourceTree = ""; + }; + 91F7793724B7809F0003C7AC /* Helpers */ = { + isa = PBXGroup; + children = ( + 0433EB471BD06313003912B4 /* NSDictionary+Paystack.h */, + 0433EB481BD06313003912B4 /* NSDictionary+Paystack.m */, + 04FCFA171BD59A8C00297732 /* PSTCKCategoryLoader.h */, + 04FCFA181BD59A8C00297732 /* PSTCKCategoryLoader.m */, + ); + name = Helpers; + sourceTree = ""; + }; + 91F7793824B781270003C7AC /* Error Handler */ = { + isa = PBXGroup; + children = ( + 04CDB4CF1A5F30A700B854EE /* PaystackError.m */, + 04CDB4CE1A5F30A700B854EE /* PaystackError.h */, + ); + path = "Error Handler"; + sourceTree = ""; + }; + 91F7793924B781800003C7AC /* Card */ = { + isa = PBXGroup; + children = ( + 0438EF461B74183100D506CC /* PSTCKCardBrand.h */, + 04CDE5BB1BC1F21500548833 /* PSTCKCardParams.h */, + 04CDE5B41BC1F1F100548833 /* PSTCKCardParams.m */, + 04CDB4CA1A5F30A700B854EE /* PSTCKCard.h */, + 04CDB4CB1A5F30A700B854EE /* PSTCKCard.m */, + ); + name = Card; + sourceTree = ""; + }; + 91F7793A24B7819B0003C7AC /* Form Encoder */ = { + isa = PBXGroup; + children = ( + 04CDB4C41A5F30A700B854EE /* PSTCKFormEncoder.h */, + 04CDB4C51A5F30A700B854EE /* PSTCKFormEncoder.m */, + 04F213301BCEAB61001D6F22 /* PSTCKFormEncodable.h */, + ); + name = "Form Encoder"; + sourceTree = ""; + }; + 91F7793B24B781BB0003C7AC /* Transaction Model */ = { + isa = PBXGroup; + children = ( + 0806F2F61DBB9E8200C2741B /* PSTCKTransaction.m */, + 0806F2F31DBB9E7100C2741B /* PSTCKTransactionParams.h */, + 0806F2F71DBB9E8200C2741B /* PSTCKTransactionParams.m */, + 0806F2F21DBB9E7100C2741B /* PSTCKTransaction.h */, + ); + name = "Transaction Model"; + sourceTree = ""; + }; + 91F7793C24B781EF0003C7AC /* Token */ = { + isa = PBXGroup; + children = ( + 04CDB4CD1A5F30A700B854EE /* PSTCKToken.m */, + 04CDB4CC1A5F30A700B854EE /* PSTCKToken.h */, + ); + name = Token; + sourceTree = ""; + }; + 91FEDC4924B69EF000A236BA /* Validator */ = { + isa = PBXGroup; + children = ( + 91F7793524B77FA20003C7AC /* Card Validator */, + 91F7793624B77FBD0003C7AC /* Textfield Validator */, + ); path = Validator; sourceTree = ""; }; @@ -1004,6 +1085,7 @@ 04CDB4411A5F2E1800B854EE = { CreatedOnToolsVersion = 6.1.1; LastSwiftMigration = 1150; + ProvisioningStyle = Automatic; }; 04D12C051A5F556D0010446E = { CreatedOnToolsVersion = 6.1.1; @@ -1393,13 +1475,19 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 18; DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MARKETING_VERSION = 3.0.14; PRODUCT_BUNDLE_IDENTIFIER = "com.paystack.paystack-ios"; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; SWIFT_OBJC_BRIDGING_HEADER = ""; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -1412,13 +1500,19 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 18; DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MARKETING_VERSION = 3.0.14; PRODUCT_BUNDLE_IDENTIFIER = "com.paystack.paystack-ios"; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; SWIFT_OBJC_BRIDGING_HEADER = ""; SWIFT_VERSION = 5.0; }; @@ -1474,6 +1568,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 11.0; MACOSX_DEPLOYMENT_TARGET = 10.7; ONLY_ACTIVE_ARCH = YES; + SKIP_INSTALL = YES; SWIFT_SWIFT3_OBJC_INFERENCE = Default; SWIFT_VERSION = 5.0; }; @@ -1493,6 +1588,7 @@ DEFINES_MODULE = YES; IPHONEOS_DEPLOYMENT_TARGET = 11.0; MACOSX_DEPLOYMENT_TARGET = 10.7; + SKIP_INSTALL = YES; SWIFT_SWIFT3_OBJC_INFERENCE = Default; SWIFT_VERSION = 5.0; }; diff --git a/Paystack/PublicHeaders/PaystackError.h b/Paystack/Error Handler/PaystackError.h similarity index 100% rename from Paystack/PublicHeaders/PaystackError.h rename to Paystack/Error Handler/PaystackError.h diff --git a/Paystack/PaystackError.m b/Paystack/Error Handler/PaystackError.m similarity index 100% rename from Paystack/PaystackError.m rename to Paystack/Error Handler/PaystackError.m diff --git a/Paystack/PublicHeaders/PSTCKAPIClient.h b/Paystack/PSTCKAPIClient.h similarity index 100% rename from Paystack/PublicHeaders/PSTCKAPIClient.h rename to Paystack/PSTCKAPIClient.h diff --git a/Paystack/PublicHeaders/PSTCKAPIResponseDecodable.h b/Paystack/PSTCKAPIResponseDecodable.h similarity index 100% rename from Paystack/PublicHeaders/PSTCKAPIResponseDecodable.h rename to Paystack/PSTCKAPIResponseDecodable.h diff --git a/Paystack/PublicHeaders/PSTCKCard.h b/Paystack/PSTCKCard.h similarity index 100% rename from Paystack/PublicHeaders/PSTCKCard.h rename to Paystack/PSTCKCard.h diff --git a/Paystack/PublicHeaders/PSTCKCardBrand.h b/Paystack/PSTCKCardBrand.h similarity index 100% rename from Paystack/PublicHeaders/PSTCKCardBrand.h rename to Paystack/PSTCKCardBrand.h diff --git a/Paystack/PublicHeaders/PSTCKCardParams.h b/Paystack/PSTCKCardParams.h similarity index 100% rename from Paystack/PublicHeaders/PSTCKCardParams.h rename to Paystack/PSTCKCardParams.h diff --git a/Paystack/PublicHeaders/PSTCKCardValidationState.h b/Paystack/PSTCKCardValidationState.h similarity index 100% rename from Paystack/PublicHeaders/PSTCKCardValidationState.h rename to Paystack/PSTCKCardValidationState.h diff --git a/Paystack/PublicHeaders/PSTCKCardValidator.h b/Paystack/PSTCKCardValidator.h similarity index 100% rename from Paystack/PublicHeaders/PSTCKCardValidator.h rename to Paystack/PSTCKCardValidator.h diff --git a/Paystack/PublicHeaders/PSTCKFormEncodable.h b/Paystack/PSTCKFormEncodable.h similarity index 100% rename from Paystack/PublicHeaders/PSTCKFormEncodable.h rename to Paystack/PSTCKFormEncodable.h diff --git a/Paystack/PublicHeaders/PSTCKToken.h b/Paystack/PSTCKToken.h similarity index 100% rename from Paystack/PublicHeaders/PSTCKToken.h rename to Paystack/PSTCKToken.h diff --git a/Paystack/PublicHeaders/PSTCKTransaction.h b/Paystack/PSTCKTransaction.h similarity index 100% rename from Paystack/PublicHeaders/PSTCKTransaction.h rename to Paystack/PSTCKTransaction.h diff --git a/Paystack/PublicHeaders/PSTCKTransactionParams.h b/Paystack/PSTCKTransactionParams.h similarity index 100% rename from Paystack/PublicHeaders/PSTCKTransactionParams.h rename to Paystack/PSTCKTransactionParams.h diff --git a/Paystack/PublicHeaders/PSTCKValidationParams.h b/Paystack/PSTCKValidationParams.h similarity index 100% rename from Paystack/PublicHeaders/PSTCKValidationParams.h rename to Paystack/PSTCKValidationParams.h diff --git a/Paystack/PublicHeaders/Paystack.h b/Paystack/Paystack.h similarity index 100% rename from Paystack/PublicHeaders/Paystack.h rename to Paystack/Paystack.h diff --git a/Paystack/Validator/RequiredRule.swift b/Paystack/Validator/Textfield Validator/RequiredRule.swift similarity index 100% rename from Paystack/Validator/RequiredRule.swift rename to Paystack/Validator/Textfield Validator/RequiredRule.swift diff --git a/Paystack/Validator/Rule.swift b/Paystack/Validator/Textfield Validator/Rule.swift similarity index 100% rename from Paystack/Validator/Rule.swift rename to Paystack/Validator/Textfield Validator/Rule.swift diff --git a/Paystack/Validator/Validatable.swift b/Paystack/Validator/Textfield Validator/Validatable.swift similarity index 100% rename from Paystack/Validator/Validatable.swift rename to Paystack/Validator/Textfield Validator/Validatable.swift diff --git a/Paystack/Validator/ValidationDelegate.swift b/Paystack/Validator/Textfield Validator/ValidationDelegate.swift similarity index 100% rename from Paystack/Validator/ValidationDelegate.swift rename to Paystack/Validator/Textfield Validator/ValidationDelegate.swift diff --git a/Paystack/Validator/ValidationError.swift b/Paystack/Validator/Textfield Validator/ValidationError.swift similarity index 100% rename from Paystack/Validator/ValidationError.swift rename to Paystack/Validator/Textfield Validator/ValidationError.swift diff --git a/Paystack/Validator/ValidationRule.swift b/Paystack/Validator/Textfield Validator/ValidationRule.swift similarity index 100% rename from Paystack/Validator/ValidationRule.swift rename to Paystack/Validator/Textfield Validator/ValidationRule.swift diff --git a/Paystack/Validator/Validator.swift b/Paystack/Validator/Textfield Validator/Validator.swift similarity index 100% rename from Paystack/Validator/Validator.swift rename to Paystack/Validator/Textfield Validator/Validator.swift diff --git a/Paystack/Validator/ValidatorDictionary.swift b/Paystack/Validator/Textfield Validator/ValidatorDictionary.swift similarity index 100% rename from Paystack/Validator/ValidatorDictionary.swift rename to Paystack/Validator/Textfield Validator/ValidatorDictionary.swift From 97155c5c17cb8db563acd4cd67cc01f754509b5a Mon Sep 17 00:00:00 2001 From: Jubril Olambiwonnu Date: Thu, 9 Jul 2020 20:37:54 +0100 Subject: [PATCH 02/30] Organises files and removes fabric files --- Paystack.podspec | 3 +- Paystack.xcodeproj/project.pbxproj | 178 +++++++----------- .../API Client}/PSTCKAPIClient+Private.h | 0 .../{ => Classes/API Client}/PSTCKAPIClient.h | 0 .../{ => Classes/API Client}/PSTCKAPIClient.m | 0 .../API Client}/PSTCKAPIClientExtension.swift | 0 .../API Client}/PSTCKAPIPostRequest.h | 0 .../API Client}/PSTCKAPIPostRequest.m | 0 .../API Client}/PSTCKAPIResponseDecodable.h | 0 Paystack/{ => Classes/Card}/PSTCKCard.h | 0 Paystack/{ => Classes/Card}/PSTCKCard.m | 0 Paystack/{ => Classes/Card}/PSTCKCardBrand.h | 0 Paystack/{ => Classes/Card}/PSTCKCardParams.h | 0 Paystack/{ => Classes/Card}/PSTCKCardParams.m | 0 .../Error Handler/PaystackError.h | 0 .../Error Handler/PaystackError.m | 0 .../Form Encoder}/PSTCKFormEncodable.h | 0 .../Form Encoder}/PSTCKFormEncoder.h | 0 .../Form Encoder}/PSTCKFormEncoder.m | 0 .../Helpers}/NSDictionary+Paystack.h | 0 .../Helpers}/NSDictionary+Paystack.m | 0 .../Helpers}/PSTCKCategoryLoader.h | 0 .../Helpers}/PSTCKCategoryLoader.m | 0 Paystack/{ => Classes}/Paystack.h | 0 Paystack/{ => Classes}/RSA/PSTCKRSA.h | 0 Paystack/{ => Classes}/RSA/PSTCKRSA.m | 0 Paystack/{ => Classes/Token}/PSTCKToken.h | 0 Paystack/{ => Classes/Token}/PSTCKToken.m | 0 .../Transaction}/PSTCKTransaction.h | 0 .../Transaction}/PSTCKTransaction.m | 0 .../Transaction}/PSTCKTransactionParams.h | 0 .../Transaction}/PSTCKTransactionParams.m | 0 .../UI}/AddressViewController.xib | 0 .../{ => Classes/UI}/ButtonExtension.swift | 0 .../{ => Classes/UI}/KeyboardHandlingVC.swift | 0 .../UI}/PSTCKAddressViewController.swift | 0 .../UI/PSTCKAuthViewController.h | 0 .../UI/PSTCKAuthViewController.m | 0 .../{ => Classes}/UI/PSTCKFormTextField.h | 0 .../{ => Classes}/UI/PSTCKFormTextField.m | 0 .../UI/PSTCKPaymentCardTextField.h | 0 .../UI/PSTCKPaymentCardTextField.m | 0 .../UI/PSTCKPaymentCardTextFieldViewModel.h | 0 .../UI/PSTCKPaymentCardTextFieldViewModel.m | 0 .../UI/UIImage+Paystack.h | 0 Paystack/{ => Classes}/UI/UIImage+Paystack.m | 0 .../PSTCKCardValidationState.h | 0 .../Card Validator}/PSTCKCardValidator.h | 0 .../Card Validator}/PSTCKCardValidator.m | 0 .../Card Validator}/PSTCKValidationParams.h | 0 .../Card Validator}/PSTCKValidationParams.m | 0 .../Textfield Validator/RequiredRule.swift | 0 .../Validator/Textfield Validator/Rule.swift | 0 .../Textfield Validator/Validatable.swift | 0 .../ValidationDelegate.swift | 0 .../Textfield Validator/ValidationError.swift | 0 .../Textfield Validator/ValidationRule.swift | 0 .../Textfield Validator/Validator.swift | 0 .../ValidatorDictionary.swift | 0 Paystack/Fabric/FABKitProtocol.h | 46 ----- Paystack/Fabric/Fabric+FABKits.h | 25 --- Paystack/Fabric/Fabric.h | 53 ------ 62 files changed, 75 insertions(+), 230 deletions(-) rename Paystack/{ => Classes/API Client}/PSTCKAPIClient+Private.h (100%) rename Paystack/{ => Classes/API Client}/PSTCKAPIClient.h (100%) rename Paystack/{ => Classes/API Client}/PSTCKAPIClient.m (100%) rename Paystack/{ => Classes/API Client}/PSTCKAPIClientExtension.swift (100%) rename Paystack/{ => Classes/API Client}/PSTCKAPIPostRequest.h (100%) rename Paystack/{ => Classes/API Client}/PSTCKAPIPostRequest.m (100%) rename Paystack/{ => Classes/API Client}/PSTCKAPIResponseDecodable.h (100%) rename Paystack/{ => Classes/Card}/PSTCKCard.h (100%) rename Paystack/{ => Classes/Card}/PSTCKCard.m (100%) rename Paystack/{ => Classes/Card}/PSTCKCardBrand.h (100%) rename Paystack/{ => Classes/Card}/PSTCKCardParams.h (100%) rename Paystack/{ => Classes/Card}/PSTCKCardParams.m (100%) rename Paystack/{ => Classes}/Error Handler/PaystackError.h (100%) rename Paystack/{ => Classes}/Error Handler/PaystackError.m (100%) rename Paystack/{ => Classes/Form Encoder}/PSTCKFormEncodable.h (100%) rename Paystack/{ => Classes/Form Encoder}/PSTCKFormEncoder.h (100%) rename Paystack/{ => Classes/Form Encoder}/PSTCKFormEncoder.m (100%) rename Paystack/{ => Classes/Helpers}/NSDictionary+Paystack.h (100%) rename Paystack/{ => Classes/Helpers}/NSDictionary+Paystack.m (100%) rename Paystack/{ => Classes/Helpers}/PSTCKCategoryLoader.h (100%) rename Paystack/{ => Classes/Helpers}/PSTCKCategoryLoader.m (100%) rename Paystack/{ => Classes}/Paystack.h (100%) rename Paystack/{ => Classes}/RSA/PSTCKRSA.h (100%) rename Paystack/{ => Classes}/RSA/PSTCKRSA.m (100%) rename Paystack/{ => Classes/Token}/PSTCKToken.h (100%) rename Paystack/{ => Classes/Token}/PSTCKToken.m (100%) rename Paystack/{ => Classes/Transaction}/PSTCKTransaction.h (100%) rename Paystack/{ => Classes/Transaction}/PSTCKTransaction.m (100%) rename Paystack/{ => Classes/Transaction}/PSTCKTransactionParams.h (100%) rename Paystack/{ => Classes/Transaction}/PSTCKTransactionParams.m (100%) rename Paystack/{ => Classes/UI}/AddressViewController.xib (100%) rename Paystack/{ => Classes/UI}/ButtonExtension.swift (100%) rename Paystack/{ => Classes/UI}/KeyboardHandlingVC.swift (100%) rename Paystack/{ => Classes/UI}/PSTCKAddressViewController.swift (100%) rename Paystack/{ => Classes}/UI/PSTCKAuthViewController.h (100%) rename Paystack/{ => Classes}/UI/PSTCKAuthViewController.m (100%) rename Paystack/{ => Classes}/UI/PSTCKFormTextField.h (100%) rename Paystack/{ => Classes}/UI/PSTCKFormTextField.m (100%) rename Paystack/{PublicHeaders => Classes}/UI/PSTCKPaymentCardTextField.h (100%) rename Paystack/{ => Classes}/UI/PSTCKPaymentCardTextField.m (100%) rename Paystack/{ => Classes}/UI/PSTCKPaymentCardTextFieldViewModel.h (100%) rename Paystack/{ => Classes}/UI/PSTCKPaymentCardTextFieldViewModel.m (100%) rename Paystack/{PublicHeaders => Classes}/UI/UIImage+Paystack.h (100%) rename Paystack/{ => Classes}/UI/UIImage+Paystack.m (100%) rename Paystack/{ => Classes/Validator/Card Validator}/PSTCKCardValidationState.h (100%) rename Paystack/{ => Classes/Validator/Card Validator}/PSTCKCardValidator.h (100%) rename Paystack/{ => Classes/Validator/Card Validator}/PSTCKCardValidator.m (100%) rename Paystack/{ => Classes/Validator/Card Validator}/PSTCKValidationParams.h (100%) rename Paystack/{ => Classes/Validator/Card Validator}/PSTCKValidationParams.m (100%) rename Paystack/{ => Classes}/Validator/Textfield Validator/RequiredRule.swift (100%) rename Paystack/{ => Classes}/Validator/Textfield Validator/Rule.swift (100%) rename Paystack/{ => Classes}/Validator/Textfield Validator/Validatable.swift (100%) rename Paystack/{ => Classes}/Validator/Textfield Validator/ValidationDelegate.swift (100%) rename Paystack/{ => Classes}/Validator/Textfield Validator/ValidationError.swift (100%) rename Paystack/{ => Classes}/Validator/Textfield Validator/ValidationRule.swift (100%) rename Paystack/{ => Classes}/Validator/Textfield Validator/Validator.swift (100%) rename Paystack/{ => Classes}/Validator/Textfield Validator/ValidatorDictionary.swift (100%) delete mode 100644 Paystack/Fabric/FABKitProtocol.h delete mode 100644 Paystack/Fabric/Fabric+FABKits.h delete mode 100644 Paystack/Fabric/Fabric.h diff --git a/Paystack.podspec b/Paystack.podspec index 5b68cc9..b504203 100644 --- a/Paystack.podspec +++ b/Paystack.podspec @@ -15,7 +15,8 @@ Pod::Spec.new do |s| s.requires_arc = true s.ios.deployment_target = '11.0' s.swift_versions = '5.0' -s.source_files = 'Paystack/Paystack/Classes/**/*' + s.source_files = 'Paystack/Classes/**/*' + s.resources = 'Paystack/Resources/**/*' end diff --git a/Paystack.xcodeproj/project.pbxproj b/Paystack.xcodeproj/project.pbxproj index a786ce8..e03140b 100644 --- a/Paystack.xcodeproj/project.pbxproj +++ b/Paystack.xcodeproj/project.pbxproj @@ -113,7 +113,6 @@ 04415C611A6605B5001225ED /* PSTCKFormEncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB4C51A5F30A700B854EE /* PSTCKFormEncoder.m */; }; 04415C641A6605B5001225ED /* PSTCKCard.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB4CB1A5F30A700B854EE /* PSTCKCard.m */; }; 04415C651A6605B5001225ED /* PSTCKToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB4CD1A5F30A700B854EE /* PSTCKToken.m */; }; - 04415C661A6605B5001225ED /* PaystackError.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB4CF1A5F30A700B854EE /* PaystackError.m */; }; 04415C671A6605B5001225ED /* PSTCKAPIClientTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB51E1A5F3A9300B854EE /* PSTCKAPIClientTest.m */; }; 04415C681A6605B5001225ED /* PSTCKFormEncoderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB51F1A5F3A9300B854EE /* PSTCKFormEncoderTest.m */; }; 04415C6D1A6605B5001225ED /* PSTCKCardFunctionalTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB5241A5F3A9300B854EE /* PSTCKCardFunctionalTest.m */; }; @@ -125,7 +124,6 @@ 04415C801A6605D9001225ED /* PSTCKFormEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4C41A5F30A700B854EE /* PSTCKFormEncoder.h */; }; 04415C831A6605D9001225ED /* PSTCKCard.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4CA1A5F30A700B854EE /* PSTCKCard.h */; settings = {ATTRIBUTES = (Public, ); }; }; 04415C841A6605D9001225ED /* PSTCKToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4CC1A5F30A700B854EE /* PSTCKToken.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 04415C851A6605D9001225ED /* PaystackError.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4CE1A5F30A700B854EE /* PaystackError.h */; settings = {ATTRIBUTES = (Public, ); }; }; 045A62AB1B8E7259000165CE /* PSTCKPaymentCardTextFieldTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 045A62AA1B8E7259000165CE /* PSTCKPaymentCardTextFieldTest.m */; }; 045A62AC1B8E73AB000165CE /* pstck_card_amex.png in Resources */ = {isa = PBXBuildFile; fileRef = 0438EF891B741C2800D506CC /* pstck_card_amex.png */; }; 045A62AD1B8E73AB000165CE /* pstck_card_amex@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 0438EF8A1B741C2800D506CC /* pstck_card_amex@2x.png */; }; @@ -157,20 +155,16 @@ 045A62C71B8E73AB000165CE /* pstck_card_visa.png in Resources */ = {isa = PBXBuildFile; fileRef = 0438EFA11B741C2800D506CC /* pstck_card_visa.png */; }; 045A62C81B8E73AB000165CE /* pstck_card_visa@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 0438EFA21B741C2800D506CC /* pstck_card_visa@2x.png */; }; 045A62C91B8E73AB000165CE /* pstck_card_visa@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 0438EFA31B741C2800D506CC /* pstck_card_visa@3x.png */; }; - 049952CF1BCF13510088C703 /* PSTCKAPIPostRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 049952CD1BCF13510088C703 /* PSTCKAPIPostRequest.h */; }; 049952D01BCF13510088C703 /* PSTCKAPIPostRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 049952CE1BCF13510088C703 /* PSTCKAPIPostRequest.m */; }; 049952D21BCF13DD0088C703 /* PSTCKAPIClient+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 049952D11BCF13DD0088C703 /* PSTCKAPIClient+Private.h */; }; 049952D31BCF13DD0088C703 /* PSTCKAPIClient+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 049952D11BCF13DD0088C703 /* PSTCKAPIClient+Private.h */; }; 049952D41BCF13DD0088C703 /* PSTCKAPIClient+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 049952D11BCF13DD0088C703 /* PSTCKAPIClient+Private.h */; }; - 049952D51BCF14920088C703 /* PSTCKAPIPostRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 049952CD1BCF13510088C703 /* PSTCKAPIPostRequest.h */; }; - 049952D61BCF14930088C703 /* PSTCKAPIPostRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 049952CD1BCF13510088C703 /* PSTCKAPIPostRequest.h */; }; 049952D71BCF14980088C703 /* PSTCKAPIPostRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 049952CE1BCF13510088C703 /* PSTCKAPIPostRequest.m */; }; 049952D81BCF14990088C703 /* PSTCKAPIPostRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 049952CE1BCF13510088C703 /* PSTCKAPIPostRequest.m */; }; 049E84CC1A605DE0000B66CD /* PSTCKAPIClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB4C31A5F30A700B854EE /* PSTCKAPIClient.m */; }; 049E84CD1A605DE0000B66CD /* PSTCKFormEncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB4C51A5F30A700B854EE /* PSTCKFormEncoder.m */; }; 049E84D01A605DE0000B66CD /* PSTCKCard.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB4CB1A5F30A700B854EE /* PSTCKCard.m */; }; 049E84D11A605DE0000B66CD /* PSTCKToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB4CD1A5F30A700B854EE /* PSTCKToken.m */; }; - 049E84D21A605DE0000B66CD /* PaystackError.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB4CF1A5F30A700B854EE /* PaystackError.m */; }; 049E84D31A605E6A000B66CD /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FAFC12C516E5767F0066297F /* UIKit.framework */; }; 049E84D41A605E7C000B66CD /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 11C74B9B164043050071C2CA /* Foundation.framework */; }; 049E84D51A605E82000B66CD /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A0D74F918F6106100966D7B /* Security.framework */; }; @@ -180,7 +174,6 @@ 049E84E71A605EF0000B66CD /* PSTCKFormEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4C41A5F30A700B854EE /* PSTCKFormEncoder.h */; }; 049E84EA1A605EF0000B66CD /* PSTCKCard.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4CA1A5F30A700B854EE /* PSTCKCard.h */; settings = {ATTRIBUTES = (Public, ); }; }; 049E84EB1A605EF0000B66CD /* PSTCKToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4CC1A5F30A700B854EE /* PSTCKToken.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 049E84EC1A605EF0000B66CD /* PaystackError.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4CE1A5F30A700B854EE /* PaystackError.h */; settings = {ATTRIBUTES = (Public, ); }; }; 04B33F361BC7488D00DD8120 /* Info.plist in Copy Files */ = {isa = PBXBuildFile; fileRef = 04B33F301BC7417B00DD8120 /* Info.plist */; }; 04CDB4D31A5F30A700B854EE /* Paystack.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4A91A5F30A700B854EE /* Paystack.h */; settings = {ATTRIBUTES = (Public, ); }; }; 04CDB4FE1A5F30A700B854EE /* PSTCKAPIClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4C21A5F30A700B854EE /* PSTCKAPIClient.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -191,8 +184,6 @@ 04CDB5101A5F30A700B854EE /* PSTCKCard.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB4CB1A5F30A700B854EE /* PSTCKCard.m */; }; 04CDB5121A5F30A700B854EE /* PSTCKToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4CC1A5F30A700B854EE /* PSTCKToken.h */; settings = {ATTRIBUTES = (Public, ); }; }; 04CDB5141A5F30A700B854EE /* PSTCKToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB4CD1A5F30A700B854EE /* PSTCKToken.m */; }; - 04CDB5161A5F30A700B854EE /* PaystackError.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4CE1A5F30A700B854EE /* PaystackError.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 04CDB5181A5F30A700B854EE /* PaystackError.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB4CF1A5F30A700B854EE /* PaystackError.m */; }; 04CDE5B81BC1F1F100548833 /* PSTCKCardParams.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDE5B41BC1F1F100548833 /* PSTCKCardParams.m */; }; 04CDE5B91BC1F1F100548833 /* PSTCKCardParams.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDE5B41BC1F1F100548833 /* PSTCKCardParams.m */; }; 04CDE5BA1BC1F1F100548833 /* PSTCKCardParams.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDE5B41BC1F1F100548833 /* PSTCKCardParams.m */; }; @@ -204,13 +195,11 @@ 04D12C261A5F55AD0010446E /* PSTCKFormEncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB4C51A5F30A700B854EE /* PSTCKFormEncoder.m */; }; 04D12C291A5F55AD0010446E /* PSTCKCard.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB4CB1A5F30A700B854EE /* PSTCKCard.m */; }; 04D12C2A1A5F55AD0010446E /* PSTCKToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB4CD1A5F30A700B854EE /* PSTCKToken.m */; }; - 04D12C2B1A5F55AD0010446E /* PaystackError.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB4CF1A5F30A700B854EE /* PaystackError.m */; }; 04D12C2C1A5F55D10010446E /* Paystack.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4A91A5F30A700B854EE /* Paystack.h */; settings = {ATTRIBUTES = (Public, ); }; }; 04D12C391A5F55D10010446E /* PSTCKAPIClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4C21A5F30A700B854EE /* PSTCKAPIClient.h */; settings = {ATTRIBUTES = (Public, ); }; }; 04D12C3A1A5F55D10010446E /* PSTCKFormEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4C41A5F30A700B854EE /* PSTCKFormEncoder.h */; }; 04D12C3D1A5F55D10010446E /* PSTCKCard.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4CA1A5F30A700B854EE /* PSTCKCard.h */; settings = {ATTRIBUTES = (Public, ); }; }; 04D12C3E1A5F55D10010446E /* PSTCKToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4CC1A5F30A700B854EE /* PSTCKToken.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 04D12C3F1A5F55D10010446E /* PaystackError.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4CE1A5F30A700B854EE /* PaystackError.h */; settings = {ATTRIBUTES = (Public, ); }; }; 04D12C401A5F55FA0010446E /* PSTCKAPIClientTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB51E1A5F3A9300B854EE /* PSTCKAPIClientTest.m */; }; 04D12C411A5F55FA0010446E /* PSTCKFormEncoderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB51F1A5F3A9300B854EE /* PSTCKFormEncoderTest.m */; }; 04D12C451A5F55FA0010446E /* PSTCKCardFunctionalTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB5241A5F3A9300B854EE /* PSTCKCardFunctionalTest.m */; }; @@ -274,17 +263,20 @@ 10500ABE1C8216C200EEF7CF /* PSTCKRSA.h in Headers */ = {isa = PBXBuildFile; fileRef = 10500AB31C8135F800EEF7CF /* PSTCKRSA.h */; }; 10A653A31C88CC5900EBC974 /* PSTCKCardParams.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDE5BB1BC1F21500548833 /* PSTCKCardParams.h */; settings = {ATTRIBUTES = (Public, ); }; }; 10FC52071C88DDB3004A0733 /* PSTCKRSA.h in Headers */ = {isa = PBXBuildFile; fileRef = 10500AB31C8135F800EEF7CF /* PSTCKRSA.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9121E92124B79EAE007BBE61 /* PSTCKAPIPostRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 9121E92024B79EAD007BBE61 /* PSTCKAPIPostRequest.h */; }; + 9121E92A24B7A312007BBE61 /* ValidationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9121E92224B7A311007BBE61 /* ValidationError.swift */; }; + 9121E92B24B7A312007BBE61 /* RequiredRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9121E92324B7A311007BBE61 /* RequiredRule.swift */; }; + 9121E92C24B7A312007BBE61 /* ValidationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9121E92424B7A312007BBE61 /* ValidationDelegate.swift */; }; + 9121E92D24B7A312007BBE61 /* Validator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9121E92524B7A312007BBE61 /* Validator.swift */; }; + 9121E92E24B7A312007BBE61 /* Rule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9121E92624B7A312007BBE61 /* Rule.swift */; }; + 9121E92F24B7A312007BBE61 /* Validatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9121E92724B7A312007BBE61 /* Validatable.swift */; }; + 9121E93024B7A312007BBE61 /* ValidationRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9121E92824B7A312007BBE61 /* ValidationRule.swift */; }; + 9121E93124B7A312007BBE61 /* ValidatorDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9121E92924B7A312007BBE61 /* ValidatorDictionary.swift */; }; + 9121E93424B7A35F007BBE61 /* PaystackError.m in Sources */ = {isa = PBXBuildFile; fileRef = 9121E93224B7A35F007BBE61 /* PaystackError.m */; }; + 9121E93524B7A35F007BBE61 /* PaystackError.h in Headers */ = {isa = PBXBuildFile; fileRef = 9121E93324B7A35F007BBE61 /* PaystackError.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9121E93724B7A43B007BBE61 /* PSTCKAddressViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9121E93624B7A43A007BBE61 /* PSTCKAddressViewController.swift */; }; 919A9FBB249C442300B7A571 /* PSTCKAPIClientExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 919A9FBA249C442300B7A571 /* PSTCKAPIClientExtension.swift */; }; - 91E448EE24B4A8DC007AA8F4 /* PSTCKAddressViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91E448ED24B4A8DC007AA8F4 /* PSTCKAddressViewController.swift */; }; 91E448F824B60883007AA8F4 /* AddressViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 91E448F724B60883007AA8F4 /* AddressViewController.xib */; }; - 91FEDC5224B69EF000A236BA /* ValidatorDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91FEDC4A24B69EF000A236BA /* ValidatorDictionary.swift */; }; - 91FEDC5324B69EF000A236BA /* Validator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91FEDC4B24B69EF000A236BA /* Validator.swift */; }; - 91FEDC5424B69EF000A236BA /* ValidationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91FEDC4C24B69EF000A236BA /* ValidationDelegate.swift */; }; - 91FEDC5524B69EF000A236BA /* Validatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91FEDC4D24B69EF000A236BA /* Validatable.swift */; }; - 91FEDC5624B69EF000A236BA /* Rule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91FEDC4E24B69EF000A236BA /* Rule.swift */; }; - 91FEDC5724B69EF000A236BA /* RequiredRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91FEDC4F24B69EF000A236BA /* RequiredRule.swift */; }; - 91FEDC5824B69EF000A236BA /* ValidationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91FEDC5024B69EF000A236BA /* ValidationError.swift */; }; - 91FEDC5924B69EF000A236BA /* ValidationRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91FEDC5124B69EF000A236BA /* ValidationRule.swift */; }; 91FEDC5B24B6A3DF00A236BA /* ButtonExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91FEDC5A24B6A3DF00A236BA /* ButtonExtension.swift */; }; 91FEDC6124B71FD100A236BA /* Cancel.png in Resources */ = {isa = PBXBuildFile; fileRef = 91FEDC5E24B71FD000A236BA /* Cancel.png */; }; 91FEDC6224B71FD100A236BA /* Cancel@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 91FEDC5F24B71FD000A236BA /* Cancel@2x.png */; }; @@ -354,11 +346,11 @@ 0433EB471BD06313003912B4 /* NSDictionary+Paystack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Paystack.h"; sourceTree = ""; }; 0433EB481BD06313003912B4 /* NSDictionary+Paystack.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+Paystack.m"; sourceTree = ""; }; 04365D2C1A4CF86C00A3E1D4 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; - 0438EF261B7416BB00D506CC /* PSTCKFormTextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PSTCKFormTextField.h; path = UI/PSTCKFormTextField.h; sourceTree = ""; }; - 0438EF271B7416BB00D506CC /* PSTCKFormTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PSTCKFormTextField.m; path = UI/PSTCKFormTextField.m; sourceTree = ""; }; - 0438EF291B7416BB00D506CC /* PSTCKPaymentCardTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PSTCKPaymentCardTextField.m; path = UI/PSTCKPaymentCardTextField.m; sourceTree = ""; }; - 0438EF2A1B7416BB00D506CC /* PSTCKPaymentCardTextFieldViewModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PSTCKPaymentCardTextFieldViewModel.h; path = UI/PSTCKPaymentCardTextFieldViewModel.h; sourceTree = ""; }; - 0438EF2B1B7416BB00D506CC /* PSTCKPaymentCardTextFieldViewModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PSTCKPaymentCardTextFieldViewModel.m; path = UI/PSTCKPaymentCardTextFieldViewModel.m; sourceTree = ""; }; + 0438EF261B7416BB00D506CC /* PSTCKFormTextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKFormTextField.h; sourceTree = ""; }; + 0438EF271B7416BB00D506CC /* PSTCKFormTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKFormTextField.m; sourceTree = ""; }; + 0438EF291B7416BB00D506CC /* PSTCKPaymentCardTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKPaymentCardTextField.m; sourceTree = ""; }; + 0438EF2A1B7416BB00D506CC /* PSTCKPaymentCardTextFieldViewModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKPaymentCardTextFieldViewModel.h; sourceTree = ""; }; + 0438EF2B1B7416BB00D506CC /* PSTCKPaymentCardTextFieldViewModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKPaymentCardTextFieldViewModel.m; sourceTree = ""; }; 0438EF3F1B74170D00D506CC /* PSTCKCardValidator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKCardValidator.m; sourceTree = ""; }; 0438EF461B74183100D506CC /* PSTCKCardBrand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKCardBrand.h; sourceTree = ""; }; 0438EF4A1B741B0100D506CC /* PSTCKCardValidatorTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKCardValidatorTest.m; sourceTree = ""; }; @@ -392,13 +384,9 @@ 0438EFA31B741C2800D506CC /* pstck_card_visa@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "pstck_card_visa@3x.png"; sourceTree = ""; }; 045A62AA1B8E7259000165CE /* PSTCKPaymentCardTextFieldTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKPaymentCardTextFieldTest.m; sourceTree = ""; }; 045E7C031A5F41DE004751EF /* PaystackiOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "PaystackiOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; - 049952CD1BCF13510088C703 /* PSTCKAPIPostRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKAPIPostRequest.h; sourceTree = ""; }; 049952CE1BCF13510088C703 /* PSTCKAPIPostRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKAPIPostRequest.m; sourceTree = ""; }; 049952D11BCF13DD0088C703 /* PSTCKAPIClient+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "PSTCKAPIClient+Private.h"; sourceTree = ""; }; 049E84AB1A605D93000B66CD /* libPaystack.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPaystack.a; sourceTree = BUILT_PRODUCTS_DIR; }; - 04A58A461BC603BB004E7BC2 /* FABKitProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FABKitProtocol.h; sourceTree = ""; }; - 04A58A471BC603BB004E7BC2 /* Fabric+FABKits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Fabric+FABKits.h"; sourceTree = ""; }; - 04A58A481BC603BB004E7BC2 /* Fabric.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Fabric.h; sourceTree = ""; }; 04B33F301BC7417B00DD8120 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 04B94BC71A47B78A00092C46 /* AddressBook.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AddressBook.framework; path = System/Library/Frameworks/AddressBook.framework; sourceTree = SDKROOT; }; 04CDB4421A5F2E1800B854EE /* Paystack.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Paystack.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -411,8 +399,6 @@ 04CDB4CB1A5F30A700B854EE /* PSTCKCard.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKCard.m; sourceTree = ""; }; 04CDB4CC1A5F30A700B854EE /* PSTCKToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKToken.h; sourceTree = ""; }; 04CDB4CD1A5F30A700B854EE /* PSTCKToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKToken.m; sourceTree = ""; }; - 04CDB4CE1A5F30A700B854EE /* PaystackError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PaystackError.h; sourceTree = ""; }; - 04CDB4CF1A5F30A700B854EE /* PaystackError.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PaystackError.m; sourceTree = ""; }; 04CDB51E1A5F3A9300B854EE /* PSTCKAPIClientTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKAPIClientTest.m; sourceTree = ""; }; 04CDB51F1A5F3A9300B854EE /* PSTCKFormEncoderTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKFormEncoderTest.m; sourceTree = ""; }; 04CDB5241A5F3A9300B854EE /* PSTCKCardFunctionalTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKCardFunctionalTest.m; sourceTree = ""; }; @@ -424,7 +410,7 @@ 04D12C061A5F556D0010446E /* PaystackOSX.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PaystackOSX.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 04D12C101A5F556D0010446E /* PaystackOSXTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PaystackOSXTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 04D5BF9019BF958F009521A5 /* PassKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PassKit.framework; path = System/Library/Frameworks/PassKit.framework; sourceTree = SDKROOT; }; - 04E32A9C1B7A9490009C9E35 /* PSTCKPaymentCardTextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PSTCKPaymentCardTextField.h; path = PublicHeaders/UI/PSTCKPaymentCardTextField.h; sourceTree = ""; }; + 04E32A9C1B7A9490009C9E35 /* PSTCKPaymentCardTextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKPaymentCardTextField.h; sourceTree = ""; }; 04EBC7511B7533C300A0E6AE /* PSTCKCardValidationState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKCardValidationState.h; sourceTree = ""; }; 04EBC7521B7533C300A0E6AE /* PSTCKCardValidator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKCardValidator.h; sourceTree = ""; }; 04F213301BCEAB61001D6F22 /* PSTCKFormEncodable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKFormEncodable.h; sourceTree = ""; }; @@ -457,31 +443,34 @@ 0806F2FE1DBBB5DF00C2741B /* pstck_card_verve.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = pstck_card_verve.png; sourceTree = ""; }; 0806F2FF1DBBB5DF00C2741B /* pstck_card_verve@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "pstck_card_verve@2x.png"; sourceTree = ""; }; 0806F3001DBBB5DF00C2741B /* pstck_card_verve@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "pstck_card_verve@3x.png"; sourceTree = ""; }; - 08C260A71E9C214A002AE28C /* PSTCKAuthViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PSTCKAuthViewController.h; path = UI/PSTCKAuthViewController.h; sourceTree = ""; }; - 08C260A81E9C214A002AE28C /* PSTCKAuthViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PSTCKAuthViewController.m; path = UI/PSTCKAuthViewController.m; sourceTree = ""; }; - 10500AB11C8135D800EEF7CF /* PSTCKRSA.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PSTCKRSA.m; path = RSA/PSTCKRSA.m; sourceTree = ""; }; - 10500AB31C8135F800EEF7CF /* PSTCKRSA.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PSTCKRSA.h; path = RSA/PSTCKRSA.h; sourceTree = ""; }; + 08C260A71E9C214A002AE28C /* PSTCKAuthViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKAuthViewController.h; sourceTree = ""; }; + 08C260A81E9C214A002AE28C /* PSTCKAuthViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKAuthViewController.m; sourceTree = ""; }; + 10500AB11C8135D800EEF7CF /* PSTCKRSA.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKRSA.m; sourceTree = ""; }; + 10500AB31C8135F800EEF7CF /* PSTCKRSA.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PSTCKRSA.h; sourceTree = ""; }; 10500AB41C8136EF00EEF7CF /* PSTCKRSATest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKRSATest.m; sourceTree = ""; }; 11C74B9B164043050071C2CA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 4A0D74F918F6106100966D7B /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; + 9121E92024B79EAD007BBE61 /* PSTCKAPIPostRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKAPIPostRequest.h; sourceTree = ""; }; + 9121E92224B7A311007BBE61 /* ValidationError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidationError.swift; sourceTree = ""; }; + 9121E92324B7A311007BBE61 /* RequiredRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequiredRule.swift; sourceTree = ""; }; + 9121E92424B7A312007BBE61 /* ValidationDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidationDelegate.swift; sourceTree = ""; }; + 9121E92524B7A312007BBE61 /* Validator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Validator.swift; sourceTree = ""; }; + 9121E92624B7A312007BBE61 /* Rule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Rule.swift; sourceTree = ""; }; + 9121E92724B7A312007BBE61 /* Validatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Validatable.swift; sourceTree = ""; }; + 9121E92824B7A312007BBE61 /* ValidationRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidationRule.swift; sourceTree = ""; }; + 9121E92924B7A312007BBE61 /* ValidatorDictionary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidatorDictionary.swift; sourceTree = ""; }; + 9121E93224B7A35F007BBE61 /* PaystackError.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PaystackError.m; sourceTree = ""; }; + 9121E93324B7A35F007BBE61 /* PaystackError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PaystackError.h; sourceTree = ""; }; + 9121E93624B7A43A007BBE61 /* PSTCKAddressViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PSTCKAddressViewController.swift; sourceTree = ""; }; 919A9FBA249C442300B7A571 /* PSTCKAPIClientExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PSTCKAPIClientExtension.swift; sourceTree = ""; }; - 91E448ED24B4A8DC007AA8F4 /* PSTCKAddressViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PSTCKAddressViewController.swift; sourceTree = ""; }; 91E448F724B60883007AA8F4 /* AddressViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AddressViewController.xib; sourceTree = ""; }; - 91FEDC4A24B69EF000A236BA /* ValidatorDictionary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidatorDictionary.swift; sourceTree = ""; }; - 91FEDC4B24B69EF000A236BA /* Validator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Validator.swift; sourceTree = ""; }; - 91FEDC4C24B69EF000A236BA /* ValidationDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidationDelegate.swift; sourceTree = ""; }; - 91FEDC4D24B69EF000A236BA /* Validatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Validatable.swift; sourceTree = ""; }; - 91FEDC4E24B69EF000A236BA /* Rule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Rule.swift; sourceTree = ""; }; - 91FEDC4F24B69EF000A236BA /* RequiredRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequiredRule.swift; sourceTree = ""; }; - 91FEDC5024B69EF000A236BA /* ValidationError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidationError.swift; sourceTree = ""; }; - 91FEDC5124B69EF000A236BA /* ValidationRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidationRule.swift; sourceTree = ""; }; 91FEDC5A24B6A3DF00A236BA /* ButtonExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonExtension.swift; sourceTree = ""; }; 91FEDC5E24B71FD000A236BA /* Cancel.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Cancel.png; sourceTree = ""; }; 91FEDC5F24B71FD000A236BA /* Cancel@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Cancel@2x.png"; sourceTree = ""; }; 91FEDC6024B71FD000A236BA /* Cancel@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Cancel@3x.png"; sourceTree = ""; }; 91FEDC6424B72B9800A236BA /* KeyboardHandlingVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardHandlingVC.swift; sourceTree = ""; }; - C1718D541C3B2E5B002A7CB3 /* UIImage+Paystack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIImage+Paystack.h"; path = "PublicHeaders/UI/UIImage+Paystack.h"; sourceTree = ""; }; - C1718D551C3B2E5B002A7CB3 /* UIImage+Paystack.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIImage+Paystack.m"; path = "UI/UIImage+Paystack.m"; sourceTree = ""; }; + C1718D541C3B2E5B002A7CB3 /* UIImage+Paystack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+Paystack.h"; sourceTree = ""; }; + C1718D551C3B2E5B002A7CB3 /* UIImage+Paystack.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+Paystack.m"; sourceTree = ""; }; C178CD441C45607D00851C69 /* UIImage+PaystackTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+PaystackTest.m"; sourceTree = ""; }; FAFC12C516E5767F0066297F /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ @@ -538,8 +527,8 @@ 08C260A81E9C214A002AE28C /* PSTCKAuthViewController.m */, 0438EF291B7416BB00D506CC /* PSTCKPaymentCardTextField.m */, 91E448F724B60883007AA8F4 /* AddressViewController.xib */, - 91E448ED24B4A8DC007AA8F4 /* PSTCKAddressViewController.swift */, 0438EF261B7416BB00D506CC /* PSTCKFormTextField.h */, + 9121E93624B7A43A007BBE61 /* PSTCKAddressViewController.swift */, 0438EF271B7416BB00D506CC /* PSTCKFormTextField.m */, 0438EF2A1B7416BB00D506CC /* PSTCKPaymentCardTextFieldViewModel.h */, 0438EF2B1B7416BB00D506CC /* PSTCKPaymentCardTextFieldViewModel.m */, @@ -548,7 +537,7 @@ 91FEDC5A24B6A3DF00A236BA /* ButtonExtension.swift */, 91FEDC6424B72B9800A236BA /* KeyboardHandlingVC.swift */, ); - name = UI; + path = UI; sourceTree = ""; }; 0438EF871B741C2800D506CC /* Resources */ = { @@ -602,17 +591,6 @@ path = Images; sourceTree = ""; }; - 04A58A451BC603BB004E7BC2 /* Fabric */ = { - isa = PBXGroup; - children = ( - 04A58A461BC603BB004E7BC2 /* FABKitProtocol.h */, - 04A58A471BC603BB004E7BC2 /* Fabric+FABKits.h */, - 04A58A481BC603BB004E7BC2 /* Fabric.h */, - ); - name = Fabric; - path = Paystack/Fabric; - sourceTree = ""; - }; 04B33F2F1BC7414C00DD8120 /* Supporting Files */ = { isa = PBXGroup; children = ( @@ -681,7 +659,7 @@ 10500AB11C8135D800EEF7CF /* PSTCKRSA.m */, 10500AB31C8135F800EEF7CF /* PSTCKRSA.h */, ); - name = RSA; + path = RSA; sourceTree = ""; }; 11C74B8D164043050071C2CA = { @@ -710,7 +688,6 @@ 11C74B9A164043050071C2CA /* Frameworks */ = { isa = PBXGroup; children = ( - 04A58A451BC603BB004E7BC2 /* Fabric */, 04365D2C1A4CF86C00A3E1D4 /* CoreGraphics.framework */, 04B94BC71A47B78A00092C46 /* AddressBook.framework */, 04D5BF9019BF958F009521A5 /* PassKit.framework */, @@ -726,23 +703,23 @@ children = ( 04CDB4A91A5F30A700B854EE /* Paystack.h */, 91F7793424B77F4D0003C7AC /* API Client */, - 91F7793924B781800003C7AC /* Card */, 91F7793824B781270003C7AC /* Error Handler */, + 91F7793924B781800003C7AC /* Card */, 91F7793A24B7819B0003C7AC /* Form Encoder */, 91F7793724B7809F0003C7AC /* Helpers */, - 91F7793B24B781BB0003C7AC /* Transaction Model */, + 91F7793B24B781BB0003C7AC /* Transaction */, 91F7793C24B781EF0003C7AC /* Token */, 10500AB01C81357A00EEF7CF /* RSA */, 0438EF251B74162700D506CC /* UI */, 91FEDC4924B69EF000A236BA /* Validator */, ); - name = Classes; + path = Classes; sourceTree = ""; }; 91F7793424B77F4D0003C7AC /* API Client */ = { isa = PBXGroup; children = ( - 049952CD1BCF13510088C703 /* PSTCKAPIPostRequest.h */, + 9121E92024B79EAD007BBE61 /* PSTCKAPIPostRequest.h */, 049952CE1BCF13510088C703 /* PSTCKAPIPostRequest.m */, 04F213341BCECB1C001D6F22 /* PSTCKAPIResponseDecodable.h */, 04CDB4C21A5F30A700B854EE /* PSTCKAPIClient.h */, @@ -750,7 +727,7 @@ 919A9FBA249C442300B7A571 /* PSTCKAPIClientExtension.swift */, 049952D11BCF13DD0088C703 /* PSTCKAPIClient+Private.h */, ); - name = "API Client"; + path = "API Client"; sourceTree = ""; }; 91F7793524B77FA20003C7AC /* Card Validator */ = { @@ -762,21 +739,20 @@ 0806F2FC1DBBA3F600C2741B /* PSTCKValidationParams.h */, 0806F2FA1DBBA3C500C2741B /* PSTCKValidationParams.m */, ); - name = "Card Validator"; - path = ..; + path = "Card Validator"; sourceTree = ""; }; 91F7793624B77FBD0003C7AC /* Textfield Validator */ = { isa = PBXGroup; children = ( - 91FEDC4A24B69EF000A236BA /* ValidatorDictionary.swift */, - 91FEDC4B24B69EF000A236BA /* Validator.swift */, - 91FEDC4C24B69EF000A236BA /* ValidationDelegate.swift */, - 91FEDC4D24B69EF000A236BA /* Validatable.swift */, - 91FEDC4E24B69EF000A236BA /* Rule.swift */, - 91FEDC4F24B69EF000A236BA /* RequiredRule.swift */, - 91FEDC5024B69EF000A236BA /* ValidationError.swift */, - 91FEDC5124B69EF000A236BA /* ValidationRule.swift */, + 9121E92324B7A311007BBE61 /* RequiredRule.swift */, + 9121E92624B7A312007BBE61 /* Rule.swift */, + 9121E92724B7A312007BBE61 /* Validatable.swift */, + 9121E92424B7A312007BBE61 /* ValidationDelegate.swift */, + 9121E92224B7A311007BBE61 /* ValidationError.swift */, + 9121E92824B7A312007BBE61 /* ValidationRule.swift */, + 9121E92524B7A312007BBE61 /* Validator.swift */, + 9121E92924B7A312007BBE61 /* ValidatorDictionary.swift */, ); path = "Textfield Validator"; sourceTree = ""; @@ -789,14 +765,14 @@ 04FCFA171BD59A8C00297732 /* PSTCKCategoryLoader.h */, 04FCFA181BD59A8C00297732 /* PSTCKCategoryLoader.m */, ); - name = Helpers; + path = Helpers; sourceTree = ""; }; 91F7793824B781270003C7AC /* Error Handler */ = { isa = PBXGroup; children = ( - 04CDB4CF1A5F30A700B854EE /* PaystackError.m */, - 04CDB4CE1A5F30A700B854EE /* PaystackError.h */, + 9121E93324B7A35F007BBE61 /* PaystackError.h */, + 9121E93224B7A35F007BBE61 /* PaystackError.m */, ); path = "Error Handler"; sourceTree = ""; @@ -810,7 +786,7 @@ 04CDB4CA1A5F30A700B854EE /* PSTCKCard.h */, 04CDB4CB1A5F30A700B854EE /* PSTCKCard.m */, ); - name = Card; + path = Card; sourceTree = ""; }; 91F7793A24B7819B0003C7AC /* Form Encoder */ = { @@ -820,10 +796,10 @@ 04CDB4C51A5F30A700B854EE /* PSTCKFormEncoder.m */, 04F213301BCEAB61001D6F22 /* PSTCKFormEncodable.h */, ); - name = "Form Encoder"; + path = "Form Encoder"; sourceTree = ""; }; - 91F7793B24B781BB0003C7AC /* Transaction Model */ = { + 91F7793B24B781BB0003C7AC /* Transaction */ = { isa = PBXGroup; children = ( 0806F2F61DBB9E8200C2741B /* PSTCKTransaction.m */, @@ -831,7 +807,7 @@ 0806F2F71DBB9E8200C2741B /* PSTCKTransactionParams.m */, 0806F2F21DBB9E7100C2741B /* PSTCKTransaction.h */, ); - name = "Transaction Model"; + path = Transaction; sourceTree = ""; }; 91F7793C24B781EF0003C7AC /* Token */ = { @@ -840,7 +816,7 @@ 04CDB4CD1A5F30A700B854EE /* PSTCKToken.m */, 04CDB4CC1A5F30A700B854EE /* PSTCKToken.h */, ); - name = Token; + path = Token; sourceTree = ""; }; 91FEDC4924B69EF000A236BA /* Validator */ = { @@ -868,7 +844,6 @@ 04E32A9E1B7A9490009C9E35 /* PSTCKPaymentCardTextField.h in Headers */, 10500ABE1C8216C200EEF7CF /* PSTCKRSA.h in Headers */, 04415C721A6605D9001225ED /* Paystack.h in Headers */, - 04415C851A6605D9001225ED /* PaystackError.h in Headers */, 08C260AA1E9C214A002AE28C /* PSTCKAuthViewController.h in Headers */, 04415C7F1A6605D9001225ED /* PSTCKAPIClient.h in Headers */, 04CDE5BF1BC1FD4500548833 /* PSTCKCardParams.h in Headers */, @@ -894,9 +869,7 @@ 0438EF491B74183100D506CC /* PSTCKCardBrand.h in Headers */, 10500ABD1C8215F400EEF7CF /* PSTCKRSA.h in Headers */, 08A572411DF92F3F0045E184 /* PSTCKTransactionParams.h in Headers */, - 049952D61BCF14930088C703 /* PSTCKAPIPostRequest.h in Headers */, 04F213331BCEAB61001D6F22 /* PSTCKFormEncodable.h in Headers */, - 049E84EC1A605EF0000B66CD /* PaystackError.h in Headers */, 08A572421DF92F3F0045E184 /* PSTCKValidationParams.h in Headers */, 08A572431DF92F3F0045E184 /* PSTCKTransaction.h in Headers */, C1718D581C3B2E60002A7CB3 /* UIImage+Paystack.h in Headers */, @@ -918,16 +891,16 @@ 0433EB491BD06313003912B4 /* NSDictionary+Paystack.h in Headers */, 04CDB50E1A5F30A700B854EE /* PSTCKCard.h in Headers */, 0438EF2C1B7416BB00D506CC /* PSTCKFormTextField.h in Headers */, + 9121E93524B7A35F007BBE61 /* PaystackError.h in Headers */, 08C260A91E9C214A002AE28C /* PSTCKAuthViewController.h in Headers */, 049952D21BCF13DD0088C703 /* PSTCKAPIClient+Private.h in Headers */, 0806F2F51DBB9E7100C2741B /* PSTCKTransactionParams.h in Headers */, C1718D561C3B2E5B002A7CB3 /* UIImage+Paystack.h in Headers */, + 9121E92124B79EAE007BBE61 /* PSTCKAPIPostRequest.h in Headers */, 04CDB5121A5F30A700B854EE /* PSTCKToken.h in Headers */, - 049952CF1BCF13510088C703 /* PSTCKAPIPostRequest.h in Headers */, 0438EF471B74183100D506CC /* PSTCKCardBrand.h in Headers */, 10A653A31C88CC5900EBC974 /* PSTCKCardParams.h in Headers */, 10FC52071C88DDB3004A0733 /* PSTCKRSA.h in Headers */, - 04CDB5161A5F30A700B854EE /* PaystackError.h in Headers */, 08A572401DF92F1D0045E184 /* PSTCKCategoryLoader.h in Headers */, 04E32A9D1B7A9490009C9E35 /* PSTCKPaymentCardTextField.h in Headers */, 0806F2F41DBB9E7100C2741B /* PSTCKTransaction.h in Headers */, @@ -954,8 +927,6 @@ 049952D31BCF13DD0088C703 /* PSTCKAPIClient+Private.h in Headers */, 04D12C3D1A5F55D10010446E /* PSTCKCard.h in Headers */, 04D12C3E1A5F55D10010446E /* PSTCKToken.h in Headers */, - 04D12C3F1A5F55D10010446E /* PaystackError.h in Headers */, - 049952D51BCF14920088C703 /* PSTCKAPIPostRequest.h in Headers */, 10500ABB1C82155B00EEF7CF /* PSTCKRSA.h in Headers */, 0438EFDB1B7524AA00D506CC /* PSTCKCardBrand.h in Headers */, 0433EB4A1BD06313003912B4 /* NSDictionary+Paystack.h in Headers */, @@ -1290,7 +1261,6 @@ 04415C641A6605B5001225ED /* PSTCKCard.m in Sources */, 04415C651A6605B5001225ED /* PSTCKToken.m in Sources */, 08C260AF1E9C214A002AE28C /* PSTCKAuthViewController.m in Sources */, - 04415C661A6605B5001225ED /* PaystackError.m in Sources */, 10500AB51C8136EF00EEF7CF /* PSTCKRSATest.m in Sources */, 04415C671A6605B5001225ED /* PSTCKAPIClientTest.m in Sources */, 04415C681A6605B5001225ED /* PSTCKFormEncoderTest.m in Sources */, @@ -1322,7 +1292,6 @@ 0438EF3D1B7416BB00D506CC /* PSTCKPaymentCardTextFieldViewModel.m in Sources */, 04CDE5BA1BC1F1F100548833 /* PSTCKCardParams.m in Sources */, 049E84D11A605DE0000B66CD /* PSTCKToken.m in Sources */, - 049E84D21A605DE0000B66CD /* PaystackError.m in Sources */, 049952D81BCF14990088C703 /* PSTCKAPIPostRequest.m in Sources */, 08C260B21E9C214A002AE28C /* PSTCKAuthViewController.m in Sources */, ); @@ -1335,32 +1304,32 @@ 91FEDC6524B72B9800A236BA /* KeyboardHandlingVC.swift in Sources */, 08A5723F1DF92EF30045E184 /* PSTCKCategoryLoader.m in Sources */, 0438EF431B74170D00D506CC /* PSTCKCardValidator.m in Sources */, + 9121E92B24B7A312007BBE61 /* RequiredRule.swift in Sources */, 0438EF2F1B7416BB00D506CC /* PSTCKFormTextField.m in Sources */, - 91FEDC5724B69EF000A236BA /* RequiredRule.swift in Sources */, + 9121E92A24B7A312007BBE61 /* ValidationError.swift in Sources */, + 9121E92F24B7A312007BBE61 /* Validatable.swift in Sources */, 919A9FBB249C442300B7A571 /* PSTCKAPIClientExtension.swift in Sources */, - 91FEDC5224B69EF000A236BA /* ValidatorDictionary.swift in Sources */, - 91FEDC5524B69EF000A236BA /* Validatable.swift in Sources */, - 91FEDC5324B69EF000A236BA /* Validator.swift in Sources */, - 91FEDC5924B69EF000A236BA /* ValidationRule.swift in Sources */, + 9121E93724B7A43B007BBE61 /* PSTCKAddressViewController.swift in Sources */, 0806F2F91DBB9E8200C2741B /* PSTCKTransactionParams.m in Sources */, - 91FEDC5824B69EF000A236BA /* ValidationError.swift in Sources */, 04CDB5101A5F30A700B854EE /* PSTCKCard.m in Sources */, 04CDB5001A5F30A700B854EE /* PSTCKAPIClient.m in Sources */, + 9121E93124B7A312007BBE61 /* ValidatorDictionary.swift in Sources */, 10500AB21C8135D800EEF7CF /* PSTCKRSA.m in Sources */, + 9121E93024B7A312007BBE61 /* ValidationRule.swift in Sources */, 0806F2FB1DBBA3C500C2741B /* PSTCKValidationParams.m in Sources */, - 04CDB5181A5F30A700B854EE /* PaystackError.m in Sources */, C1718D571C3B2E5B002A7CB3 /* UIImage+Paystack.m in Sources */, 0806F2F81DBB9E8200C2741B /* PSTCKTransaction.m in Sources */, - 91E448EE24B4A8DC007AA8F4 /* PSTCKAddressViewController.swift in Sources */, + 9121E92E24B7A312007BBE61 /* Rule.swift in Sources */, + 9121E93424B7A35F007BBE61 /* PaystackError.m in Sources */, 0438EF351B7416BB00D506CC /* PSTCKPaymentCardTextField.m in Sources */, 04CDB5041A5F30A700B854EE /* PSTCKFormEncoder.m in Sources */, - 91FEDC5424B69EF000A236BA /* ValidationDelegate.swift in Sources */, 04CDB5141A5F30A700B854EE /* PSTCKToken.m in Sources */, 0433EB4C1BD06313003912B4 /* NSDictionary+Paystack.m in Sources */, 0438EF3B1B7416BB00D506CC /* PSTCKPaymentCardTextFieldViewModel.m in Sources */, 04CDE5B81BC1F1F100548833 /* PSTCKCardParams.m in Sources */, - 91FEDC5624B69EF000A236BA /* Rule.swift in Sources */, 91FEDC5B24B6A3DF00A236BA /* ButtonExtension.swift in Sources */, + 9121E92C24B7A312007BBE61 /* ValidationDelegate.swift in Sources */, + 9121E92D24B7A312007BBE61 /* Validator.swift in Sources */, 049952D01BCF13510088C703 /* PSTCKAPIPostRequest.m in Sources */, 08C260AE1E9C214A002AE28C /* PSTCKAuthViewController.m in Sources */, ); @@ -1380,7 +1349,6 @@ 08C260B01E9C214A002AE28C /* PSTCKAuthViewController.m in Sources */, 0433EB4D1BD06313003912B4 /* NSDictionary+Paystack.m in Sources */, 049952D71BCF14980088C703 /* PSTCKAPIPostRequest.m in Sources */, - 04D12C2B1A5F55AD0010446E /* PaystackError.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Paystack/PSTCKAPIClient+Private.h b/Paystack/Classes/API Client/PSTCKAPIClient+Private.h similarity index 100% rename from Paystack/PSTCKAPIClient+Private.h rename to Paystack/Classes/API Client/PSTCKAPIClient+Private.h diff --git a/Paystack/PSTCKAPIClient.h b/Paystack/Classes/API Client/PSTCKAPIClient.h similarity index 100% rename from Paystack/PSTCKAPIClient.h rename to Paystack/Classes/API Client/PSTCKAPIClient.h diff --git a/Paystack/PSTCKAPIClient.m b/Paystack/Classes/API Client/PSTCKAPIClient.m similarity index 100% rename from Paystack/PSTCKAPIClient.m rename to Paystack/Classes/API Client/PSTCKAPIClient.m diff --git a/Paystack/PSTCKAPIClientExtension.swift b/Paystack/Classes/API Client/PSTCKAPIClientExtension.swift similarity index 100% rename from Paystack/PSTCKAPIClientExtension.swift rename to Paystack/Classes/API Client/PSTCKAPIClientExtension.swift diff --git a/Paystack/PSTCKAPIPostRequest.h b/Paystack/Classes/API Client/PSTCKAPIPostRequest.h similarity index 100% rename from Paystack/PSTCKAPIPostRequest.h rename to Paystack/Classes/API Client/PSTCKAPIPostRequest.h diff --git a/Paystack/PSTCKAPIPostRequest.m b/Paystack/Classes/API Client/PSTCKAPIPostRequest.m similarity index 100% rename from Paystack/PSTCKAPIPostRequest.m rename to Paystack/Classes/API Client/PSTCKAPIPostRequest.m diff --git a/Paystack/PSTCKAPIResponseDecodable.h b/Paystack/Classes/API Client/PSTCKAPIResponseDecodable.h similarity index 100% rename from Paystack/PSTCKAPIResponseDecodable.h rename to Paystack/Classes/API Client/PSTCKAPIResponseDecodable.h diff --git a/Paystack/PSTCKCard.h b/Paystack/Classes/Card/PSTCKCard.h similarity index 100% rename from Paystack/PSTCKCard.h rename to Paystack/Classes/Card/PSTCKCard.h diff --git a/Paystack/PSTCKCard.m b/Paystack/Classes/Card/PSTCKCard.m similarity index 100% rename from Paystack/PSTCKCard.m rename to Paystack/Classes/Card/PSTCKCard.m diff --git a/Paystack/PSTCKCardBrand.h b/Paystack/Classes/Card/PSTCKCardBrand.h similarity index 100% rename from Paystack/PSTCKCardBrand.h rename to Paystack/Classes/Card/PSTCKCardBrand.h diff --git a/Paystack/PSTCKCardParams.h b/Paystack/Classes/Card/PSTCKCardParams.h similarity index 100% rename from Paystack/PSTCKCardParams.h rename to Paystack/Classes/Card/PSTCKCardParams.h diff --git a/Paystack/PSTCKCardParams.m b/Paystack/Classes/Card/PSTCKCardParams.m similarity index 100% rename from Paystack/PSTCKCardParams.m rename to Paystack/Classes/Card/PSTCKCardParams.m diff --git a/Paystack/Error Handler/PaystackError.h b/Paystack/Classes/Error Handler/PaystackError.h similarity index 100% rename from Paystack/Error Handler/PaystackError.h rename to Paystack/Classes/Error Handler/PaystackError.h diff --git a/Paystack/Error Handler/PaystackError.m b/Paystack/Classes/Error Handler/PaystackError.m similarity index 100% rename from Paystack/Error Handler/PaystackError.m rename to Paystack/Classes/Error Handler/PaystackError.m diff --git a/Paystack/PSTCKFormEncodable.h b/Paystack/Classes/Form Encoder/PSTCKFormEncodable.h similarity index 100% rename from Paystack/PSTCKFormEncodable.h rename to Paystack/Classes/Form Encoder/PSTCKFormEncodable.h diff --git a/Paystack/PSTCKFormEncoder.h b/Paystack/Classes/Form Encoder/PSTCKFormEncoder.h similarity index 100% rename from Paystack/PSTCKFormEncoder.h rename to Paystack/Classes/Form Encoder/PSTCKFormEncoder.h diff --git a/Paystack/PSTCKFormEncoder.m b/Paystack/Classes/Form Encoder/PSTCKFormEncoder.m similarity index 100% rename from Paystack/PSTCKFormEncoder.m rename to Paystack/Classes/Form Encoder/PSTCKFormEncoder.m diff --git a/Paystack/NSDictionary+Paystack.h b/Paystack/Classes/Helpers/NSDictionary+Paystack.h similarity index 100% rename from Paystack/NSDictionary+Paystack.h rename to Paystack/Classes/Helpers/NSDictionary+Paystack.h diff --git a/Paystack/NSDictionary+Paystack.m b/Paystack/Classes/Helpers/NSDictionary+Paystack.m similarity index 100% rename from Paystack/NSDictionary+Paystack.m rename to Paystack/Classes/Helpers/NSDictionary+Paystack.m diff --git a/Paystack/PSTCKCategoryLoader.h b/Paystack/Classes/Helpers/PSTCKCategoryLoader.h similarity index 100% rename from Paystack/PSTCKCategoryLoader.h rename to Paystack/Classes/Helpers/PSTCKCategoryLoader.h diff --git a/Paystack/PSTCKCategoryLoader.m b/Paystack/Classes/Helpers/PSTCKCategoryLoader.m similarity index 100% rename from Paystack/PSTCKCategoryLoader.m rename to Paystack/Classes/Helpers/PSTCKCategoryLoader.m diff --git a/Paystack/Paystack.h b/Paystack/Classes/Paystack.h similarity index 100% rename from Paystack/Paystack.h rename to Paystack/Classes/Paystack.h diff --git a/Paystack/RSA/PSTCKRSA.h b/Paystack/Classes/RSA/PSTCKRSA.h similarity index 100% rename from Paystack/RSA/PSTCKRSA.h rename to Paystack/Classes/RSA/PSTCKRSA.h diff --git a/Paystack/RSA/PSTCKRSA.m b/Paystack/Classes/RSA/PSTCKRSA.m similarity index 100% rename from Paystack/RSA/PSTCKRSA.m rename to Paystack/Classes/RSA/PSTCKRSA.m diff --git a/Paystack/PSTCKToken.h b/Paystack/Classes/Token/PSTCKToken.h similarity index 100% rename from Paystack/PSTCKToken.h rename to Paystack/Classes/Token/PSTCKToken.h diff --git a/Paystack/PSTCKToken.m b/Paystack/Classes/Token/PSTCKToken.m similarity index 100% rename from Paystack/PSTCKToken.m rename to Paystack/Classes/Token/PSTCKToken.m diff --git a/Paystack/PSTCKTransaction.h b/Paystack/Classes/Transaction/PSTCKTransaction.h similarity index 100% rename from Paystack/PSTCKTransaction.h rename to Paystack/Classes/Transaction/PSTCKTransaction.h diff --git a/Paystack/PSTCKTransaction.m b/Paystack/Classes/Transaction/PSTCKTransaction.m similarity index 100% rename from Paystack/PSTCKTransaction.m rename to Paystack/Classes/Transaction/PSTCKTransaction.m diff --git a/Paystack/PSTCKTransactionParams.h b/Paystack/Classes/Transaction/PSTCKTransactionParams.h similarity index 100% rename from Paystack/PSTCKTransactionParams.h rename to Paystack/Classes/Transaction/PSTCKTransactionParams.h diff --git a/Paystack/PSTCKTransactionParams.m b/Paystack/Classes/Transaction/PSTCKTransactionParams.m similarity index 100% rename from Paystack/PSTCKTransactionParams.m rename to Paystack/Classes/Transaction/PSTCKTransactionParams.m diff --git a/Paystack/AddressViewController.xib b/Paystack/Classes/UI/AddressViewController.xib similarity index 100% rename from Paystack/AddressViewController.xib rename to Paystack/Classes/UI/AddressViewController.xib diff --git a/Paystack/ButtonExtension.swift b/Paystack/Classes/UI/ButtonExtension.swift similarity index 100% rename from Paystack/ButtonExtension.swift rename to Paystack/Classes/UI/ButtonExtension.swift diff --git a/Paystack/KeyboardHandlingVC.swift b/Paystack/Classes/UI/KeyboardHandlingVC.swift similarity index 100% rename from Paystack/KeyboardHandlingVC.swift rename to Paystack/Classes/UI/KeyboardHandlingVC.swift diff --git a/Paystack/PSTCKAddressViewController.swift b/Paystack/Classes/UI/PSTCKAddressViewController.swift similarity index 100% rename from Paystack/PSTCKAddressViewController.swift rename to Paystack/Classes/UI/PSTCKAddressViewController.swift diff --git a/Paystack/UI/PSTCKAuthViewController.h b/Paystack/Classes/UI/PSTCKAuthViewController.h similarity index 100% rename from Paystack/UI/PSTCKAuthViewController.h rename to Paystack/Classes/UI/PSTCKAuthViewController.h diff --git a/Paystack/UI/PSTCKAuthViewController.m b/Paystack/Classes/UI/PSTCKAuthViewController.m similarity index 100% rename from Paystack/UI/PSTCKAuthViewController.m rename to Paystack/Classes/UI/PSTCKAuthViewController.m diff --git a/Paystack/UI/PSTCKFormTextField.h b/Paystack/Classes/UI/PSTCKFormTextField.h similarity index 100% rename from Paystack/UI/PSTCKFormTextField.h rename to Paystack/Classes/UI/PSTCKFormTextField.h diff --git a/Paystack/UI/PSTCKFormTextField.m b/Paystack/Classes/UI/PSTCKFormTextField.m similarity index 100% rename from Paystack/UI/PSTCKFormTextField.m rename to Paystack/Classes/UI/PSTCKFormTextField.m diff --git a/Paystack/PublicHeaders/UI/PSTCKPaymentCardTextField.h b/Paystack/Classes/UI/PSTCKPaymentCardTextField.h similarity index 100% rename from Paystack/PublicHeaders/UI/PSTCKPaymentCardTextField.h rename to Paystack/Classes/UI/PSTCKPaymentCardTextField.h diff --git a/Paystack/UI/PSTCKPaymentCardTextField.m b/Paystack/Classes/UI/PSTCKPaymentCardTextField.m similarity index 100% rename from Paystack/UI/PSTCKPaymentCardTextField.m rename to Paystack/Classes/UI/PSTCKPaymentCardTextField.m diff --git a/Paystack/UI/PSTCKPaymentCardTextFieldViewModel.h b/Paystack/Classes/UI/PSTCKPaymentCardTextFieldViewModel.h similarity index 100% rename from Paystack/UI/PSTCKPaymentCardTextFieldViewModel.h rename to Paystack/Classes/UI/PSTCKPaymentCardTextFieldViewModel.h diff --git a/Paystack/UI/PSTCKPaymentCardTextFieldViewModel.m b/Paystack/Classes/UI/PSTCKPaymentCardTextFieldViewModel.m similarity index 100% rename from Paystack/UI/PSTCKPaymentCardTextFieldViewModel.m rename to Paystack/Classes/UI/PSTCKPaymentCardTextFieldViewModel.m diff --git a/Paystack/PublicHeaders/UI/UIImage+Paystack.h b/Paystack/Classes/UI/UIImage+Paystack.h similarity index 100% rename from Paystack/PublicHeaders/UI/UIImage+Paystack.h rename to Paystack/Classes/UI/UIImage+Paystack.h diff --git a/Paystack/UI/UIImage+Paystack.m b/Paystack/Classes/UI/UIImage+Paystack.m similarity index 100% rename from Paystack/UI/UIImage+Paystack.m rename to Paystack/Classes/UI/UIImage+Paystack.m diff --git a/Paystack/PSTCKCardValidationState.h b/Paystack/Classes/Validator/Card Validator/PSTCKCardValidationState.h similarity index 100% rename from Paystack/PSTCKCardValidationState.h rename to Paystack/Classes/Validator/Card Validator/PSTCKCardValidationState.h diff --git a/Paystack/PSTCKCardValidator.h b/Paystack/Classes/Validator/Card Validator/PSTCKCardValidator.h similarity index 100% rename from Paystack/PSTCKCardValidator.h rename to Paystack/Classes/Validator/Card Validator/PSTCKCardValidator.h diff --git a/Paystack/PSTCKCardValidator.m b/Paystack/Classes/Validator/Card Validator/PSTCKCardValidator.m similarity index 100% rename from Paystack/PSTCKCardValidator.m rename to Paystack/Classes/Validator/Card Validator/PSTCKCardValidator.m diff --git a/Paystack/PSTCKValidationParams.h b/Paystack/Classes/Validator/Card Validator/PSTCKValidationParams.h similarity index 100% rename from Paystack/PSTCKValidationParams.h rename to Paystack/Classes/Validator/Card Validator/PSTCKValidationParams.h diff --git a/Paystack/PSTCKValidationParams.m b/Paystack/Classes/Validator/Card Validator/PSTCKValidationParams.m similarity index 100% rename from Paystack/PSTCKValidationParams.m rename to Paystack/Classes/Validator/Card Validator/PSTCKValidationParams.m diff --git a/Paystack/Validator/Textfield Validator/RequiredRule.swift b/Paystack/Classes/Validator/Textfield Validator/RequiredRule.swift similarity index 100% rename from Paystack/Validator/Textfield Validator/RequiredRule.swift rename to Paystack/Classes/Validator/Textfield Validator/RequiredRule.swift diff --git a/Paystack/Validator/Textfield Validator/Rule.swift b/Paystack/Classes/Validator/Textfield Validator/Rule.swift similarity index 100% rename from Paystack/Validator/Textfield Validator/Rule.swift rename to Paystack/Classes/Validator/Textfield Validator/Rule.swift diff --git a/Paystack/Validator/Textfield Validator/Validatable.swift b/Paystack/Classes/Validator/Textfield Validator/Validatable.swift similarity index 100% rename from Paystack/Validator/Textfield Validator/Validatable.swift rename to Paystack/Classes/Validator/Textfield Validator/Validatable.swift diff --git a/Paystack/Validator/Textfield Validator/ValidationDelegate.swift b/Paystack/Classes/Validator/Textfield Validator/ValidationDelegate.swift similarity index 100% rename from Paystack/Validator/Textfield Validator/ValidationDelegate.swift rename to Paystack/Classes/Validator/Textfield Validator/ValidationDelegate.swift diff --git a/Paystack/Validator/Textfield Validator/ValidationError.swift b/Paystack/Classes/Validator/Textfield Validator/ValidationError.swift similarity index 100% rename from Paystack/Validator/Textfield Validator/ValidationError.swift rename to Paystack/Classes/Validator/Textfield Validator/ValidationError.swift diff --git a/Paystack/Validator/Textfield Validator/ValidationRule.swift b/Paystack/Classes/Validator/Textfield Validator/ValidationRule.swift similarity index 100% rename from Paystack/Validator/Textfield Validator/ValidationRule.swift rename to Paystack/Classes/Validator/Textfield Validator/ValidationRule.swift diff --git a/Paystack/Validator/Textfield Validator/Validator.swift b/Paystack/Classes/Validator/Textfield Validator/Validator.swift similarity index 100% rename from Paystack/Validator/Textfield Validator/Validator.swift rename to Paystack/Classes/Validator/Textfield Validator/Validator.swift diff --git a/Paystack/Validator/Textfield Validator/ValidatorDictionary.swift b/Paystack/Classes/Validator/Textfield Validator/ValidatorDictionary.swift similarity index 100% rename from Paystack/Validator/Textfield Validator/ValidatorDictionary.swift rename to Paystack/Classes/Validator/Textfield Validator/ValidatorDictionary.swift diff --git a/Paystack/Fabric/FABKitProtocol.h b/Paystack/Fabric/FABKitProtocol.h deleted file mode 100644 index 53e0656..0000000 --- a/Paystack/Fabric/FABKitProtocol.h +++ /dev/null @@ -1,46 +0,0 @@ -// -// FABKitProtocol.h -// -// Copyright (c) 2015 Twitter. All rights reserved. -// - -#import - -/** - * Protocol that a class in a Fabric Kit must conform to to provide information to Fabric at runtime. - */ -@protocol FABKit - -@required - -/** - * Required. The globally unique identifier of the Kit. - * We encourage the use of reverse-DNS notation. - * Example: @"io.fabric.sdk.ios" - */ -+ (NSString *)bundleIdentifier; - -/** - * Required. Must return the current version of the Kit that is being used at runtime. - * We encourage the use of semantic versioning (http://semver.org/), without prefixing the version with a "v". - * This is commonly referred to as the "marketing version". - * Example: @"1.2.3" - */ -+ (NSString *)kitDisplayVersion; - -@optional - -/** - * The build version of the kit. Should be monotonically increasing and unique. - * Example: 137 - */ -+ (NSString *)kitBuildVersion; - -/** - * Perform any necessary initialization. - * This method will be invoked on the Kit when the user calls +[Fabric initializeKits]. - * @note This method being called does not necessarily imply that the developer has started using the Kit yet. - */ -+ (void)initializeIfNeeded; - -@end diff --git a/Paystack/Fabric/Fabric+FABKits.h b/Paystack/Fabric/Fabric+FABKits.h deleted file mode 100644 index 927e4da..0000000 --- a/Paystack/Fabric/Fabric+FABKits.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// Fabric+FABKits.h -// -// Copyright (c) 2015 Twitter. All rights reserved. -// - -#import "Fabric.h" - -@protocol FABKit; -// Use this category for methods that kits can call on Fabric. -@interface Fabric (FABKits) - -/** - * Returns a dictionary containing the kit configuration info for the provided kit. - * The configuration information is parsed from the application's Info.plist. This - * method is primarily intended to be used by kits to retrieve their configuration. - * - * @param kitClass The class of the kit whose configuration should be returned. - * It should conform to the FABKit protocol. - * - * @return A dictionary containing kit specific configuration information or nil if none exists. - */ -+ (nonnull NSDictionary *)configurationDictionaryForKitClass:(nonnull Class)kitClass; - -@end diff --git a/Paystack/Fabric/Fabric.h b/Paystack/Fabric/Fabric.h deleted file mode 100644 index 760aa76..0000000 --- a/Paystack/Fabric/Fabric.h +++ /dev/null @@ -1,53 +0,0 @@ -// -// Fabric.h -// -// Copyright (c) 2015 Twitter. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -/** - * Fabric Base. Coordinates configuration and starts all provided kits. - */ -@interface Fabric : NSObject - -/** - * Initialize Fabric and all provided kits. Call this method within your App Delegate's `application:didFinishLaunchingWithOptions:` and provide the kits you wish to use. - * - * For example, in Objective-C: - * - * `[Fabric with:@[[Crashlytics class], [Twitter class], [Digits class], [MoPub class]]];` - * - * Swift: - * - * `Fabric.with([Crashlytics.self(), Twitter.self(), Digits.self(), MoPub.self()])` - * - * Only the first call to this method is honored. Subsequent calls are no-ops. - * - * @param kits An array of kit Class objects - * - * @return Returns the shared Fabric instance. In most cases this can be ignored. - */ -+ (instancetype)with:(NSArray *)kitClasses; - -/** - * Returns the Fabric singleton object. - */ -+ (instancetype)sharedSDK; - -/** - * This BOOL enables or disables debug logging, such as kit version information. The default value is NO. - */ -@property (nonatomic, assign) BOOL debug; - -/** - * Unavailable. Use `+sharedSDK` to retrieve the shared Fabric instance. - */ -- (id)init __attribute__((unavailable("Use +sharedSDK to retrieve the shared Fabric instance."))); - -@end - -NS_ASSUME_NONNULL_END - From 442c8a5a1b0c40bb4760dfc6488db021e323ecb7 Mon Sep 17 00:00:00 2001 From: Jubril Olambiwonnu Date: Thu, 9 Jul 2020 21:06:49 +0100 Subject: [PATCH 03/30] Add public header files to podspec --- Paystack.podspec | 1 + Paystack/Classes/Paystack.h | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/Paystack.podspec b/Paystack.podspec index b504203..db594c5 100644 --- a/Paystack.podspec +++ b/Paystack.podspec @@ -17,6 +17,7 @@ Pod::Spec.new do |s| s.swift_versions = '5.0' s.source_files = 'Paystack/Classes/**/*' s.resources = 'Paystack/Resources/**/*' + s.public_header_files = 'Paystack/Classes/Paystack.h' end diff --git a/Paystack/Classes/Paystack.h b/Paystack/Classes/Paystack.h index 40b2217..ff339a7 100644 --- a/Paystack/Classes/Paystack.h +++ b/Paystack/Classes/Paystack.h @@ -9,17 +9,17 @@ // Stripe was replaced with Paystack - and STP with PSTCK - to avoid collisions within // apps that are using both Paystack and Stripe. -#import "PSTCKAPIClient.h" -#import "PaystackError.h" -#import "PSTCKCardBrand.h" -#import "PSTCKCardParams.h" -#import "PSTCKTransactionParams.h" -#import "PSTCKCard.h" -#import "PSTCKCardValidationState.h" -#import "PSTCKCardValidator.h" -#import "PSTCKToken.h" -#import "PSTCKRSA.h" +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import #if TARGET_OS_IPHONE -#import "PSTCKPaymentCardTextField.h" +#import #endif From 5b6d6cad34467372c34ba198bfcdef441406afc2 Mon Sep 17 00:00:00 2001 From: Jubril Olambiwonnu Date: Thu, 9 Jul 2020 21:41:56 +0100 Subject: [PATCH 04/30] Create folder for public headers --- Paystack.podspec | 5 +-- Paystack.xcodeproj/project.pbxproj | 40 +++++++++++-------- .../PSTCKAPIClient.h | 0 .../PSTCKAPIResponseDecodable.h | 0 .../{Card => PublicHeaders}/PSTCKCard.h | 0 .../{Card => PublicHeaders}/PSTCKCardBrand.h | 0 .../{Card => PublicHeaders}/PSTCKCardParams.h | 0 .../PSTCKCardValidationState.h | 0 .../PSTCKCardValidator.h | 0 .../PSTCKFormEncoder.h | 0 .../PSTCKPaymentCardTextField.h | 0 .../{Token => PublicHeaders}/PSTCKToken.h | 0 .../PSTCKTransaction.h | 0 .../PSTCKTransactionParams.h | 0 .../PSTCKValidationParams.h | 0 .../Classes/{ => PublicHeaders}/Paystack.h | 0 .../PaystackError.h | 0 .../{UI => PublicHeaders}/UIImage+Paystack.h | 0 18 files changed, 26 insertions(+), 19 deletions(-) rename Paystack/Classes/{API Client => PublicHeaders}/PSTCKAPIClient.h (100%) rename Paystack/Classes/{API Client => PublicHeaders}/PSTCKAPIResponseDecodable.h (100%) rename Paystack/Classes/{Card => PublicHeaders}/PSTCKCard.h (100%) rename Paystack/Classes/{Card => PublicHeaders}/PSTCKCardBrand.h (100%) rename Paystack/Classes/{Card => PublicHeaders}/PSTCKCardParams.h (100%) rename Paystack/Classes/{Validator/Card Validator => PublicHeaders}/PSTCKCardValidationState.h (100%) rename Paystack/Classes/{Validator/Card Validator => PublicHeaders}/PSTCKCardValidator.h (100%) rename Paystack/Classes/{Form Encoder => PublicHeaders}/PSTCKFormEncoder.h (100%) rename Paystack/Classes/{UI => PublicHeaders}/PSTCKPaymentCardTextField.h (100%) rename Paystack/Classes/{Token => PublicHeaders}/PSTCKToken.h (100%) rename Paystack/Classes/{Transaction => PublicHeaders}/PSTCKTransaction.h (100%) rename Paystack/Classes/{Transaction => PublicHeaders}/PSTCKTransactionParams.h (100%) rename Paystack/Classes/{Validator/Card Validator => PublicHeaders}/PSTCKValidationParams.h (100%) rename Paystack/Classes/{ => PublicHeaders}/Paystack.h (100%) rename Paystack/Classes/{Error Handler => PublicHeaders}/PaystackError.h (100%) rename Paystack/Classes/{UI => PublicHeaders}/UIImage+Paystack.h (100%) diff --git a/Paystack.podspec b/Paystack.podspec index db594c5..b779513 100644 --- a/Paystack.podspec +++ b/Paystack.podspec @@ -15,9 +15,8 @@ Pod::Spec.new do |s| s.requires_arc = true s.ios.deployment_target = '11.0' s.swift_versions = '5.0' - s.source_files = 'Paystack/Classes/**/*' + ss.public_header_files = 'Paystack/Classes/PublicHeaders/*.h', 'Paystack/Classes/RSA/*.h' + s.source_files = 'Paystack/Classes/**/*.{swift,h,m}' s.resources = 'Paystack/Resources/**/*' - s.public_header_files = 'Paystack/Classes/Paystack.h' - end diff --git a/Paystack.xcodeproj/project.pbxproj b/Paystack.xcodeproj/project.pbxproj index e03140b..3a19a42 100644 --- a/Paystack.xcodeproj/project.pbxproj +++ b/Paystack.xcodeproj/project.pbxproj @@ -522,7 +522,6 @@ 0438EF251B74162700D506CC /* UI */ = { isa = PBXGroup; children = ( - 04E32A9C1B7A9490009C9E35 /* PSTCKPaymentCardTextField.h */, 08C260A71E9C214A002AE28C /* PSTCKAuthViewController.h */, 08C260A81E9C214A002AE28C /* PSTCKAuthViewController.m */, 0438EF291B7416BB00D506CC /* PSTCKPaymentCardTextField.m */, @@ -532,7 +531,6 @@ 0438EF271B7416BB00D506CC /* PSTCKFormTextField.m */, 0438EF2A1B7416BB00D506CC /* PSTCKPaymentCardTextFieldViewModel.h */, 0438EF2B1B7416BB00D506CC /* PSTCKPaymentCardTextFieldViewModel.m */, - C1718D541C3B2E5B002A7CB3 /* UIImage+Paystack.h */, C1718D551C3B2E5B002A7CB3 /* UIImage+Paystack.m */, 91FEDC5A24B6A3DF00A236BA /* ButtonExtension.swift */, 91FEDC6424B72B9800A236BA /* KeyboardHandlingVC.swift */, @@ -698,10 +696,33 @@ name = Frameworks; sourceTree = ""; }; - 91F7793324B774CF0003C7AC /* Classes */ = { + 9121E93824B7B5C7007BBE61 /* PublicHeaders */ = { isa = PBXGroup; children = ( + 04F213341BCECB1C001D6F22 /* PSTCKAPIResponseDecodable.h */, + 04E32A9C1B7A9490009C9E35 /* PSTCKPaymentCardTextField.h */, + 04CDB4CA1A5F30A700B854EE /* PSTCKCard.h */, + 04EBC7511B7533C300A0E6AE /* PSTCKCardValidationState.h */, 04CDB4A91A5F30A700B854EE /* Paystack.h */, + 04CDB4C41A5F30A700B854EE /* PSTCKFormEncoder.h */, + 04EBC7521B7533C300A0E6AE /* PSTCKCardValidator.h */, + 04CDB4CC1A5F30A700B854EE /* PSTCKToken.h */, + C1718D541C3B2E5B002A7CB3 /* UIImage+Paystack.h */, + 0806F2F21DBB9E7100C2741B /* PSTCKTransaction.h */, + 0806F2F31DBB9E7100C2741B /* PSTCKTransactionParams.h */, + 0806F2FC1DBBA3F600C2741B /* PSTCKValidationParams.h */, + 0438EF461B74183100D506CC /* PSTCKCardBrand.h */, + 9121E93324B7A35F007BBE61 /* PaystackError.h */, + 04CDE5BB1BC1F21500548833 /* PSTCKCardParams.h */, + 04CDB4C21A5F30A700B854EE /* PSTCKAPIClient.h */, + ); + path = PublicHeaders; + sourceTree = ""; + }; + 91F7793324B774CF0003C7AC /* Classes */ = { + isa = PBXGroup; + children = ( + 9121E93824B7B5C7007BBE61 /* PublicHeaders */, 91F7793424B77F4D0003C7AC /* API Client */, 91F7793824B781270003C7AC /* Error Handler */, 91F7793924B781800003C7AC /* Card */, @@ -721,8 +742,6 @@ children = ( 9121E92024B79EAD007BBE61 /* PSTCKAPIPostRequest.h */, 049952CE1BCF13510088C703 /* PSTCKAPIPostRequest.m */, - 04F213341BCECB1C001D6F22 /* PSTCKAPIResponseDecodable.h */, - 04CDB4C21A5F30A700B854EE /* PSTCKAPIClient.h */, 04CDB4C31A5F30A700B854EE /* PSTCKAPIClient.m */, 919A9FBA249C442300B7A571 /* PSTCKAPIClientExtension.swift */, 049952D11BCF13DD0088C703 /* PSTCKAPIClient+Private.h */, @@ -733,10 +752,7 @@ 91F7793524B77FA20003C7AC /* Card Validator */ = { isa = PBXGroup; children = ( - 04EBC7511B7533C300A0E6AE /* PSTCKCardValidationState.h */, - 04EBC7521B7533C300A0E6AE /* PSTCKCardValidator.h */, 0438EF3F1B74170D00D506CC /* PSTCKCardValidator.m */, - 0806F2FC1DBBA3F600C2741B /* PSTCKValidationParams.h */, 0806F2FA1DBBA3C500C2741B /* PSTCKValidationParams.m */, ); path = "Card Validator"; @@ -771,7 +787,6 @@ 91F7793824B781270003C7AC /* Error Handler */ = { isa = PBXGroup; children = ( - 9121E93324B7A35F007BBE61 /* PaystackError.h */, 9121E93224B7A35F007BBE61 /* PaystackError.m */, ); path = "Error Handler"; @@ -780,10 +795,7 @@ 91F7793924B781800003C7AC /* Card */ = { isa = PBXGroup; children = ( - 0438EF461B74183100D506CC /* PSTCKCardBrand.h */, - 04CDE5BB1BC1F21500548833 /* PSTCKCardParams.h */, 04CDE5B41BC1F1F100548833 /* PSTCKCardParams.m */, - 04CDB4CA1A5F30A700B854EE /* PSTCKCard.h */, 04CDB4CB1A5F30A700B854EE /* PSTCKCard.m */, ); path = Card; @@ -792,7 +804,6 @@ 91F7793A24B7819B0003C7AC /* Form Encoder */ = { isa = PBXGroup; children = ( - 04CDB4C41A5F30A700B854EE /* PSTCKFormEncoder.h */, 04CDB4C51A5F30A700B854EE /* PSTCKFormEncoder.m */, 04F213301BCEAB61001D6F22 /* PSTCKFormEncodable.h */, ); @@ -803,9 +814,7 @@ isa = PBXGroup; children = ( 0806F2F61DBB9E8200C2741B /* PSTCKTransaction.m */, - 0806F2F31DBB9E7100C2741B /* PSTCKTransactionParams.h */, 0806F2F71DBB9E8200C2741B /* PSTCKTransactionParams.m */, - 0806F2F21DBB9E7100C2741B /* PSTCKTransaction.h */, ); path = Transaction; sourceTree = ""; @@ -814,7 +823,6 @@ isa = PBXGroup; children = ( 04CDB4CD1A5F30A700B854EE /* PSTCKToken.m */, - 04CDB4CC1A5F30A700B854EE /* PSTCKToken.h */, ); path = Token; sourceTree = ""; diff --git a/Paystack/Classes/API Client/PSTCKAPIClient.h b/Paystack/Classes/PublicHeaders/PSTCKAPIClient.h similarity index 100% rename from Paystack/Classes/API Client/PSTCKAPIClient.h rename to Paystack/Classes/PublicHeaders/PSTCKAPIClient.h diff --git a/Paystack/Classes/API Client/PSTCKAPIResponseDecodable.h b/Paystack/Classes/PublicHeaders/PSTCKAPIResponseDecodable.h similarity index 100% rename from Paystack/Classes/API Client/PSTCKAPIResponseDecodable.h rename to Paystack/Classes/PublicHeaders/PSTCKAPIResponseDecodable.h diff --git a/Paystack/Classes/Card/PSTCKCard.h b/Paystack/Classes/PublicHeaders/PSTCKCard.h similarity index 100% rename from Paystack/Classes/Card/PSTCKCard.h rename to Paystack/Classes/PublicHeaders/PSTCKCard.h diff --git a/Paystack/Classes/Card/PSTCKCardBrand.h b/Paystack/Classes/PublicHeaders/PSTCKCardBrand.h similarity index 100% rename from Paystack/Classes/Card/PSTCKCardBrand.h rename to Paystack/Classes/PublicHeaders/PSTCKCardBrand.h diff --git a/Paystack/Classes/Card/PSTCKCardParams.h b/Paystack/Classes/PublicHeaders/PSTCKCardParams.h similarity index 100% rename from Paystack/Classes/Card/PSTCKCardParams.h rename to Paystack/Classes/PublicHeaders/PSTCKCardParams.h diff --git a/Paystack/Classes/Validator/Card Validator/PSTCKCardValidationState.h b/Paystack/Classes/PublicHeaders/PSTCKCardValidationState.h similarity index 100% rename from Paystack/Classes/Validator/Card Validator/PSTCKCardValidationState.h rename to Paystack/Classes/PublicHeaders/PSTCKCardValidationState.h diff --git a/Paystack/Classes/Validator/Card Validator/PSTCKCardValidator.h b/Paystack/Classes/PublicHeaders/PSTCKCardValidator.h similarity index 100% rename from Paystack/Classes/Validator/Card Validator/PSTCKCardValidator.h rename to Paystack/Classes/PublicHeaders/PSTCKCardValidator.h diff --git a/Paystack/Classes/Form Encoder/PSTCKFormEncoder.h b/Paystack/Classes/PublicHeaders/PSTCKFormEncoder.h similarity index 100% rename from Paystack/Classes/Form Encoder/PSTCKFormEncoder.h rename to Paystack/Classes/PublicHeaders/PSTCKFormEncoder.h diff --git a/Paystack/Classes/UI/PSTCKPaymentCardTextField.h b/Paystack/Classes/PublicHeaders/PSTCKPaymentCardTextField.h similarity index 100% rename from Paystack/Classes/UI/PSTCKPaymentCardTextField.h rename to Paystack/Classes/PublicHeaders/PSTCKPaymentCardTextField.h diff --git a/Paystack/Classes/Token/PSTCKToken.h b/Paystack/Classes/PublicHeaders/PSTCKToken.h similarity index 100% rename from Paystack/Classes/Token/PSTCKToken.h rename to Paystack/Classes/PublicHeaders/PSTCKToken.h diff --git a/Paystack/Classes/Transaction/PSTCKTransaction.h b/Paystack/Classes/PublicHeaders/PSTCKTransaction.h similarity index 100% rename from Paystack/Classes/Transaction/PSTCKTransaction.h rename to Paystack/Classes/PublicHeaders/PSTCKTransaction.h diff --git a/Paystack/Classes/Transaction/PSTCKTransactionParams.h b/Paystack/Classes/PublicHeaders/PSTCKTransactionParams.h similarity index 100% rename from Paystack/Classes/Transaction/PSTCKTransactionParams.h rename to Paystack/Classes/PublicHeaders/PSTCKTransactionParams.h diff --git a/Paystack/Classes/Validator/Card Validator/PSTCKValidationParams.h b/Paystack/Classes/PublicHeaders/PSTCKValidationParams.h similarity index 100% rename from Paystack/Classes/Validator/Card Validator/PSTCKValidationParams.h rename to Paystack/Classes/PublicHeaders/PSTCKValidationParams.h diff --git a/Paystack/Classes/Paystack.h b/Paystack/Classes/PublicHeaders/Paystack.h similarity index 100% rename from Paystack/Classes/Paystack.h rename to Paystack/Classes/PublicHeaders/Paystack.h diff --git a/Paystack/Classes/Error Handler/PaystackError.h b/Paystack/Classes/PublicHeaders/PaystackError.h similarity index 100% rename from Paystack/Classes/Error Handler/PaystackError.h rename to Paystack/Classes/PublicHeaders/PaystackError.h diff --git a/Paystack/Classes/UI/UIImage+Paystack.h b/Paystack/Classes/PublicHeaders/UIImage+Paystack.h similarity index 100% rename from Paystack/Classes/UI/UIImage+Paystack.h rename to Paystack/Classes/PublicHeaders/UIImage+Paystack.h From 649ae48e2b2c5b9e03379294fa93aa100c850e71 Mon Sep 17 00:00:00 2001 From: Jubril Olambiwonnu Date: Thu, 9 Jul 2020 21:45:28 +0100 Subject: [PATCH 05/30] Update umbrella header file declaration --- Paystack/Classes/PublicHeaders/Paystack.h | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Paystack/Classes/PublicHeaders/Paystack.h b/Paystack/Classes/PublicHeaders/Paystack.h index ff339a7..40b2217 100644 --- a/Paystack/Classes/PublicHeaders/Paystack.h +++ b/Paystack/Classes/PublicHeaders/Paystack.h @@ -9,17 +9,17 @@ // Stripe was replaced with Paystack - and STP with PSTCK - to avoid collisions within // apps that are using both Paystack and Stripe. -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import +#import "PSTCKAPIClient.h" +#import "PaystackError.h" +#import "PSTCKCardBrand.h" +#import "PSTCKCardParams.h" +#import "PSTCKTransactionParams.h" +#import "PSTCKCard.h" +#import "PSTCKCardValidationState.h" +#import "PSTCKCardValidator.h" +#import "PSTCKToken.h" +#import "PSTCKRSA.h" #if TARGET_OS_IPHONE -#import +#import "PSTCKPaymentCardTextField.h" #endif From 775fc5e88984c06df7c8bcccc10699c0c20c544d Mon Sep 17 00:00:00 2001 From: Jubril Olambiwonnu Date: Thu, 9 Jul 2020 21:57:15 +0100 Subject: [PATCH 06/30] Fix typo on podspec --- Paystack.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Paystack.podspec b/Paystack.podspec index b779513..9fbf9e4 100644 --- a/Paystack.podspec +++ b/Paystack.podspec @@ -15,7 +15,7 @@ Pod::Spec.new do |s| s.requires_arc = true s.ios.deployment_target = '11.0' s.swift_versions = '5.0' - ss.public_header_files = 'Paystack/Classes/PublicHeaders/*.h', 'Paystack/Classes/RSA/*.h' + s.public_header_files = 'Paystack/Classes/PublicHeaders/*.h', 'Paystack/Classes/RSA/*.h' s.source_files = 'Paystack/Classes/**/*.{swift,h,m}' s.resources = 'Paystack/Resources/**/*' From 08066fa734b54a7dc999e0e14a9af58b4ce688d1 Mon Sep 17 00:00:00 2001 From: Jubril Olambiwonnu <57099127+jubril-paystack@users.noreply.github.com> Date: Sat, 11 Jul 2020 23:45:08 +0100 Subject: [PATCH 07/30] Revert "Fix pod linting issues" --- Paystack.podspec | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/Paystack.podspec b/Paystack.podspec index 683f70f..9fbf9e4 100644 --- a/Paystack.podspec +++ b/Paystack.podspec @@ -14,14 +14,9 @@ Pod::Spec.new do |s| s.ios.weak_frameworks = 'PassKit', 'AddressBook' s.requires_arc = true s.ios.deployment_target = '11.0' - s.default_subspecs = 'Core' s.swift_versions = '5.0' - - s.subspec 'Core' do |ss| - ss.public_header_files = 'Paystack/PublicHeaders/*.h', 'Paystack/RSA/*.h' - ss.ios.public_header_files = 'Paystack/PublicHeaders/UI/*.h' - ss.source_files = 'Paystack/PublicHeaders/*.h', 'Paystack/RSA/*.{h,m}', 'Paystack/*.{h,m}' - ss.ios.source_files = 'Paystack/PublicHeaders/UI/*.h', 'Paystack/UI/*.{h,m}', 'Paystack/**/*.{swift}' - ss.resources = 'Paystack/Resources/**/*' - end + s.public_header_files = 'Paystack/Classes/PublicHeaders/*.h', 'Paystack/Classes/RSA/*.h' + s.source_files = 'Paystack/Classes/**/*.{swift,h,m}' + s.resources = 'Paystack/Resources/**/*' + end From 9558ed86258d4718dabec53aa6cf7a75a2f5f318 Mon Sep 17 00:00:00 2001 From: Jubril Olambiwonnu Date: Sun, 12 Jul 2020 00:13:15 +0100 Subject: [PATCH 08/30] Revert commit: 2c0ef9b --- Paystack.podspec | 21 +- Paystack.xcodeproj/project.pbxproj | 356 ++++---- Paystack/AddressViewController.xib | 164 ++++ .../PaystackOSX-Debug.xcconfig | 11 + .../PaystackOSX-Release.xcconfig | 22 + .../PaystackOSX-Shared.xcconfig | 186 +++++ .../PaystackOSXTests-Debug.xcconfig | 11 + .../PaystackOSXTests-Release.xcconfig | 22 + .../PaystackOSXTests-Shared.xcconfig | 122 +++ .../PaystackiOS Tests-Debug.xcconfig | 11 + .../PaystackiOS Tests-Release.xcconfig | 12 + .../PaystackiOS Tests-Shared.xcconfig | 92 +++ .../PaystackiOS-Debug.xcconfig | 11 + .../PaystackiOS-Release.xcconfig | 10 + .../PaystackiOS-Shared.xcconfig | 167 ++++ .../PaystackiOSStatic.xcconfig | 99 +++ .../PaystackiOSStaticFramework.xcconfig | 19 + .../Project-Debug.xcconfig | 48 ++ .../Project-Release.xcconfig | 41 + .../Project-Shared.xcconfig | 130 +++ Paystack/ButtonExtension.swift | 92 +++ Paystack/Fabric/FABKitProtocol.h | 46 ++ Paystack/Fabric/Fabric+FABKits.h | 25 + Paystack/Fabric/Fabric.h | 53 ++ Paystack/KeyboardHandlingVC.swift | 84 ++ Paystack/NSDictionary+Paystack.h | 14 + Paystack/NSDictionary+Paystack.m | 27 + Paystack/PSTCKAPIClient+Private.h | 14 + Paystack/PSTCKAPIClient.m | 634 ++++++++++++++ Paystack/PSTCKAPIClientExtension.swift | 73 ++ Paystack/PSTCKAPIPostRequest.h | 19 + Paystack/PSTCKAPIPostRequest.m | 54 ++ Paystack/PSTCKAddressViewController.swift | 108 +++ Paystack/PSTCKCard.m | 144 ++++ Paystack/PSTCKCardParams.m | 173 ++++ Paystack/PSTCKCardValidator.m | 308 +++++++ Paystack/PSTCKCategoryLoader.h | 16 + Paystack/PSTCKCategoryLoader.m | 21 + Paystack/PSTCKFormEncoder.h | 37 + Paystack/PSTCKFormEncoder.m | 228 ++++++ Paystack/PSTCKToken.m | 95 +++ Paystack/PSTCKTransaction.m | 93 +++ Paystack/PSTCKTransactionParams.m | 137 ++++ Paystack/PSTCKValidationParams.m | 36 + Paystack/PaystackError.m | 109 +++ Paystack/PublicHeaders/PSTCKAPIClient.h | 110 +++ .../PublicHeaders/PSTCKAPIResponseDecodable.h | 25 + Paystack/PublicHeaders/PSTCKCard.h | 122 +++ Paystack/PublicHeaders/PSTCKCardBrand.h | 20 + Paystack/PublicHeaders/PSTCKCardParams.h | 46 ++ .../PublicHeaders/PSTCKCardValidationState.h | 15 + Paystack/PublicHeaders/PSTCKCardValidator.h | 103 +++ Paystack/PublicHeaders/PSTCKFormEncodable.h | 32 + Paystack/PublicHeaders/PSTCKToken.h | 33 + Paystack/PublicHeaders/PSTCKTransaction.h | 31 + .../PublicHeaders/PSTCKTransactionParams.h | 40 + .../PublicHeaders/PSTCKValidationParams.h | 17 + Paystack/PublicHeaders/Paystack.h | 25 + Paystack/PublicHeaders/PaystackError.h | 75 ++ .../UI/PSTCKPaymentCardTextField.h | 238 ++++++ Paystack/PublicHeaders/UI/UIImage+Paystack.h | 25 + Paystack/RSA/PSTCKRSA.h | 14 + Paystack/RSA/PSTCKRSA.m | 164 ++++ Paystack/UI/PSTCKAuthViewController.h | 31 + Paystack/UI/PSTCKAuthViewController.m | 124 +++ Paystack/UI/PSTCKFormTextField.h | 28 + Paystack/UI/PSTCKFormTextField.m | 147 ++++ Paystack/UI/PSTCKPaymentCardTextField.m | 774 ++++++++++++++++++ .../UI/PSTCKPaymentCardTextFieldViewModel.h | 34 + .../UI/PSTCKPaymentCardTextFieldViewModel.m | 119 +++ Paystack/UI/UIImage+Paystack.m | 96 +++ Paystack/Validator/RequiredRule.swift | 46 ++ Paystack/Validator/Rule.swift | 27 + Paystack/Validator/Validatable.swift | 33 + Paystack/Validator/ValidationDelegate.swift | 27 + Paystack/Validator/ValidationError.swift | 33 + Paystack/Validator/ValidationRule.swift | 45 + Paystack/Validator/Validator.swift | 154 ++++ Paystack/Validator/ValidatorDictionary.swift | 45 + 79 files changed, 6871 insertions(+), 222 deletions(-) create mode 100644 Paystack/AddressViewController.xib create mode 100644 Paystack/BuildConfigurations/PaystackOSX-Debug.xcconfig create mode 100644 Paystack/BuildConfigurations/PaystackOSX-Release.xcconfig create mode 100644 Paystack/BuildConfigurations/PaystackOSX-Shared.xcconfig create mode 100644 Paystack/BuildConfigurations/PaystackOSXTests-Debug.xcconfig create mode 100644 Paystack/BuildConfigurations/PaystackOSXTests-Release.xcconfig create mode 100644 Paystack/BuildConfigurations/PaystackOSXTests-Shared.xcconfig create mode 100644 Paystack/BuildConfigurations/PaystackiOS Tests-Debug.xcconfig create mode 100644 Paystack/BuildConfigurations/PaystackiOS Tests-Release.xcconfig create mode 100644 Paystack/BuildConfigurations/PaystackiOS Tests-Shared.xcconfig create mode 100644 Paystack/BuildConfigurations/PaystackiOS-Debug.xcconfig create mode 100644 Paystack/BuildConfigurations/PaystackiOS-Release.xcconfig create mode 100644 Paystack/BuildConfigurations/PaystackiOS-Shared.xcconfig create mode 100644 Paystack/BuildConfigurations/PaystackiOSStatic.xcconfig create mode 100644 Paystack/BuildConfigurations/PaystackiOSStaticFramework.xcconfig create mode 100644 Paystack/BuildConfigurations/Project-Debug.xcconfig create mode 100644 Paystack/BuildConfigurations/Project-Release.xcconfig create mode 100644 Paystack/BuildConfigurations/Project-Shared.xcconfig create mode 100644 Paystack/ButtonExtension.swift create mode 100644 Paystack/Fabric/FABKitProtocol.h create mode 100644 Paystack/Fabric/Fabric+FABKits.h create mode 100644 Paystack/Fabric/Fabric.h create mode 100644 Paystack/KeyboardHandlingVC.swift create mode 100644 Paystack/NSDictionary+Paystack.h create mode 100644 Paystack/NSDictionary+Paystack.m create mode 100644 Paystack/PSTCKAPIClient+Private.h create mode 100644 Paystack/PSTCKAPIClient.m create mode 100644 Paystack/PSTCKAPIClientExtension.swift create mode 100644 Paystack/PSTCKAPIPostRequest.h create mode 100644 Paystack/PSTCKAPIPostRequest.m create mode 100644 Paystack/PSTCKAddressViewController.swift create mode 100644 Paystack/PSTCKCard.m create mode 100644 Paystack/PSTCKCardParams.m create mode 100644 Paystack/PSTCKCardValidator.m create mode 100644 Paystack/PSTCKCategoryLoader.h create mode 100644 Paystack/PSTCKCategoryLoader.m create mode 100644 Paystack/PSTCKFormEncoder.h create mode 100644 Paystack/PSTCKFormEncoder.m create mode 100644 Paystack/PSTCKToken.m create mode 100644 Paystack/PSTCKTransaction.m create mode 100644 Paystack/PSTCKTransactionParams.m create mode 100644 Paystack/PSTCKValidationParams.m create mode 100644 Paystack/PaystackError.m create mode 100644 Paystack/PublicHeaders/PSTCKAPIClient.h create mode 100644 Paystack/PublicHeaders/PSTCKAPIResponseDecodable.h create mode 100644 Paystack/PublicHeaders/PSTCKCard.h create mode 100644 Paystack/PublicHeaders/PSTCKCardBrand.h create mode 100644 Paystack/PublicHeaders/PSTCKCardParams.h create mode 100644 Paystack/PublicHeaders/PSTCKCardValidationState.h create mode 100644 Paystack/PublicHeaders/PSTCKCardValidator.h create mode 100644 Paystack/PublicHeaders/PSTCKFormEncodable.h create mode 100644 Paystack/PublicHeaders/PSTCKToken.h create mode 100644 Paystack/PublicHeaders/PSTCKTransaction.h create mode 100644 Paystack/PublicHeaders/PSTCKTransactionParams.h create mode 100644 Paystack/PublicHeaders/PSTCKValidationParams.h create mode 100644 Paystack/PublicHeaders/Paystack.h create mode 100644 Paystack/PublicHeaders/PaystackError.h create mode 100644 Paystack/PublicHeaders/UI/PSTCKPaymentCardTextField.h create mode 100644 Paystack/PublicHeaders/UI/UIImage+Paystack.h create mode 100644 Paystack/RSA/PSTCKRSA.h create mode 100644 Paystack/RSA/PSTCKRSA.m create mode 100644 Paystack/UI/PSTCKAuthViewController.h create mode 100644 Paystack/UI/PSTCKAuthViewController.m create mode 100644 Paystack/UI/PSTCKFormTextField.h create mode 100644 Paystack/UI/PSTCKFormTextField.m create mode 100644 Paystack/UI/PSTCKPaymentCardTextField.m create mode 100644 Paystack/UI/PSTCKPaymentCardTextFieldViewModel.h create mode 100644 Paystack/UI/PSTCKPaymentCardTextFieldViewModel.m create mode 100644 Paystack/UI/UIImage+Paystack.m create mode 100644 Paystack/Validator/RequiredRule.swift create mode 100644 Paystack/Validator/Rule.swift create mode 100644 Paystack/Validator/Validatable.swift create mode 100644 Paystack/Validator/ValidationDelegate.swift create mode 100644 Paystack/Validator/ValidationError.swift create mode 100644 Paystack/Validator/ValidationRule.swift create mode 100644 Paystack/Validator/Validator.swift create mode 100644 Paystack/Validator/ValidatorDictionary.swift diff --git a/Paystack.podspec b/Paystack.podspec index 9fbf9e4..d8e1550 100644 --- a/Paystack.podspec +++ b/Paystack.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'Paystack' - s.version = '3.0.14' + s.version = '3.0.13' s.summary = 'Paystack is a web-based API helping African Businesses accept payments online.' s.description = <<-DESC Paystack makes it easy for African Businesses to accept Mastercard, Visa and Verve cards from anyone, anywhere in the world. @@ -8,15 +8,20 @@ Pod::Spec.new do |s| s.license = { :type => 'MIT', :file => 'LICENSE' } s.homepage = 'https://paystack.com' - s.authors = { 'Jubril Olambiwonnu' => 'jubril@paystack.com', 'Ibrahim Lawal' => 'ibrahim@paystack.com', 'Paystack' => 'support@paystack.com' } + s.authors = { 'Ibrahim Lawal' => 'ibrahim@paystack.com', 'Paystack' => 'support@paystack.com' } s.source = { :git => 'https://github.com/paystackhq/paystack-ios.git', :tag => "v#{s.version}" } s.ios.frameworks = 'Foundation', 'Security' s.ios.weak_frameworks = 'PassKit', 'AddressBook' s.requires_arc = true - s.ios.deployment_target = '11.0' - s.swift_versions = '5.0' - s.public_header_files = 'Paystack/Classes/PublicHeaders/*.h', 'Paystack/Classes/RSA/*.h' - s.source_files = 'Paystack/Classes/**/*.{swift,h,m}' - s.resources = 'Paystack/Resources/**/*' - + s.ios.deployment_target = '8.0' + s.default_subspecs = 'Core' + + s.subspec 'Core' do |ss| + ss.public_header_files = 'Paystack/PublicHeaders/*.h', 'Paystack/RSA/*.h' + ss.ios.public_header_files = 'Paystack/PublicHeaders/UI/*.h' + ss.source_files = 'Paystack/PublicHeaders/*.h', 'Paystack/RSA/*.{h,m}', 'Paystack/*.{h,m}' + ss.ios.source_files = 'Paystack/PublicHeaders/UI/*.h', 'Paystack/UI/*.{h,m}', 'Paystack/Fabric/*' + ss.resources = 'Paystack/Resources/**/*' + end + end diff --git a/Paystack.xcodeproj/project.pbxproj b/Paystack.xcodeproj/project.pbxproj index 3a19a42..94e5ff1 100644 --- a/Paystack.xcodeproj/project.pbxproj +++ b/Paystack.xcodeproj/project.pbxproj @@ -113,6 +113,7 @@ 04415C611A6605B5001225ED /* PSTCKFormEncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB4C51A5F30A700B854EE /* PSTCKFormEncoder.m */; }; 04415C641A6605B5001225ED /* PSTCKCard.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB4CB1A5F30A700B854EE /* PSTCKCard.m */; }; 04415C651A6605B5001225ED /* PSTCKToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB4CD1A5F30A700B854EE /* PSTCKToken.m */; }; + 04415C661A6605B5001225ED /* PaystackError.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB4CF1A5F30A700B854EE /* PaystackError.m */; }; 04415C671A6605B5001225ED /* PSTCKAPIClientTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB51E1A5F3A9300B854EE /* PSTCKAPIClientTest.m */; }; 04415C681A6605B5001225ED /* PSTCKFormEncoderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB51F1A5F3A9300B854EE /* PSTCKFormEncoderTest.m */; }; 04415C6D1A6605B5001225ED /* PSTCKCardFunctionalTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB5241A5F3A9300B854EE /* PSTCKCardFunctionalTest.m */; }; @@ -124,6 +125,7 @@ 04415C801A6605D9001225ED /* PSTCKFormEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4C41A5F30A700B854EE /* PSTCKFormEncoder.h */; }; 04415C831A6605D9001225ED /* PSTCKCard.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4CA1A5F30A700B854EE /* PSTCKCard.h */; settings = {ATTRIBUTES = (Public, ); }; }; 04415C841A6605D9001225ED /* PSTCKToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4CC1A5F30A700B854EE /* PSTCKToken.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 04415C851A6605D9001225ED /* PaystackError.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4CE1A5F30A700B854EE /* PaystackError.h */; settings = {ATTRIBUTES = (Public, ); }; }; 045A62AB1B8E7259000165CE /* PSTCKPaymentCardTextFieldTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 045A62AA1B8E7259000165CE /* PSTCKPaymentCardTextFieldTest.m */; }; 045A62AC1B8E73AB000165CE /* pstck_card_amex.png in Resources */ = {isa = PBXBuildFile; fileRef = 0438EF891B741C2800D506CC /* pstck_card_amex.png */; }; 045A62AD1B8E73AB000165CE /* pstck_card_amex@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 0438EF8A1B741C2800D506CC /* pstck_card_amex@2x.png */; }; @@ -155,16 +157,20 @@ 045A62C71B8E73AB000165CE /* pstck_card_visa.png in Resources */ = {isa = PBXBuildFile; fileRef = 0438EFA11B741C2800D506CC /* pstck_card_visa.png */; }; 045A62C81B8E73AB000165CE /* pstck_card_visa@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 0438EFA21B741C2800D506CC /* pstck_card_visa@2x.png */; }; 045A62C91B8E73AB000165CE /* pstck_card_visa@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 0438EFA31B741C2800D506CC /* pstck_card_visa@3x.png */; }; + 049952CF1BCF13510088C703 /* PSTCKAPIPostRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 049952CD1BCF13510088C703 /* PSTCKAPIPostRequest.h */; }; 049952D01BCF13510088C703 /* PSTCKAPIPostRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 049952CE1BCF13510088C703 /* PSTCKAPIPostRequest.m */; }; 049952D21BCF13DD0088C703 /* PSTCKAPIClient+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 049952D11BCF13DD0088C703 /* PSTCKAPIClient+Private.h */; }; 049952D31BCF13DD0088C703 /* PSTCKAPIClient+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 049952D11BCF13DD0088C703 /* PSTCKAPIClient+Private.h */; }; 049952D41BCF13DD0088C703 /* PSTCKAPIClient+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 049952D11BCF13DD0088C703 /* PSTCKAPIClient+Private.h */; }; + 049952D51BCF14920088C703 /* PSTCKAPIPostRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 049952CD1BCF13510088C703 /* PSTCKAPIPostRequest.h */; }; + 049952D61BCF14930088C703 /* PSTCKAPIPostRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 049952CD1BCF13510088C703 /* PSTCKAPIPostRequest.h */; }; 049952D71BCF14980088C703 /* PSTCKAPIPostRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 049952CE1BCF13510088C703 /* PSTCKAPIPostRequest.m */; }; 049952D81BCF14990088C703 /* PSTCKAPIPostRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 049952CE1BCF13510088C703 /* PSTCKAPIPostRequest.m */; }; 049E84CC1A605DE0000B66CD /* PSTCKAPIClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB4C31A5F30A700B854EE /* PSTCKAPIClient.m */; }; 049E84CD1A605DE0000B66CD /* PSTCKFormEncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB4C51A5F30A700B854EE /* PSTCKFormEncoder.m */; }; 049E84D01A605DE0000B66CD /* PSTCKCard.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB4CB1A5F30A700B854EE /* PSTCKCard.m */; }; 049E84D11A605DE0000B66CD /* PSTCKToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB4CD1A5F30A700B854EE /* PSTCKToken.m */; }; + 049E84D21A605DE0000B66CD /* PaystackError.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB4CF1A5F30A700B854EE /* PaystackError.m */; }; 049E84D31A605E6A000B66CD /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FAFC12C516E5767F0066297F /* UIKit.framework */; }; 049E84D41A605E7C000B66CD /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 11C74B9B164043050071C2CA /* Foundation.framework */; }; 049E84D51A605E82000B66CD /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A0D74F918F6106100966D7B /* Security.framework */; }; @@ -174,6 +180,7 @@ 049E84E71A605EF0000B66CD /* PSTCKFormEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4C41A5F30A700B854EE /* PSTCKFormEncoder.h */; }; 049E84EA1A605EF0000B66CD /* PSTCKCard.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4CA1A5F30A700B854EE /* PSTCKCard.h */; settings = {ATTRIBUTES = (Public, ); }; }; 049E84EB1A605EF0000B66CD /* PSTCKToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4CC1A5F30A700B854EE /* PSTCKToken.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 049E84EC1A605EF0000B66CD /* PaystackError.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4CE1A5F30A700B854EE /* PaystackError.h */; settings = {ATTRIBUTES = (Public, ); }; }; 04B33F361BC7488D00DD8120 /* Info.plist in Copy Files */ = {isa = PBXBuildFile; fileRef = 04B33F301BC7417B00DD8120 /* Info.plist */; }; 04CDB4D31A5F30A700B854EE /* Paystack.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4A91A5F30A700B854EE /* Paystack.h */; settings = {ATTRIBUTES = (Public, ); }; }; 04CDB4FE1A5F30A700B854EE /* PSTCKAPIClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4C21A5F30A700B854EE /* PSTCKAPIClient.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -184,6 +191,8 @@ 04CDB5101A5F30A700B854EE /* PSTCKCard.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB4CB1A5F30A700B854EE /* PSTCKCard.m */; }; 04CDB5121A5F30A700B854EE /* PSTCKToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4CC1A5F30A700B854EE /* PSTCKToken.h */; settings = {ATTRIBUTES = (Public, ); }; }; 04CDB5141A5F30A700B854EE /* PSTCKToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB4CD1A5F30A700B854EE /* PSTCKToken.m */; }; + 04CDB5161A5F30A700B854EE /* PaystackError.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4CE1A5F30A700B854EE /* PaystackError.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 04CDB5181A5F30A700B854EE /* PaystackError.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB4CF1A5F30A700B854EE /* PaystackError.m */; }; 04CDE5B81BC1F1F100548833 /* PSTCKCardParams.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDE5B41BC1F1F100548833 /* PSTCKCardParams.m */; }; 04CDE5B91BC1F1F100548833 /* PSTCKCardParams.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDE5B41BC1F1F100548833 /* PSTCKCardParams.m */; }; 04CDE5BA1BC1F1F100548833 /* PSTCKCardParams.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDE5B41BC1F1F100548833 /* PSTCKCardParams.m */; }; @@ -195,11 +204,13 @@ 04D12C261A5F55AD0010446E /* PSTCKFormEncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB4C51A5F30A700B854EE /* PSTCKFormEncoder.m */; }; 04D12C291A5F55AD0010446E /* PSTCKCard.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB4CB1A5F30A700B854EE /* PSTCKCard.m */; }; 04D12C2A1A5F55AD0010446E /* PSTCKToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB4CD1A5F30A700B854EE /* PSTCKToken.m */; }; + 04D12C2B1A5F55AD0010446E /* PaystackError.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB4CF1A5F30A700B854EE /* PaystackError.m */; }; 04D12C2C1A5F55D10010446E /* Paystack.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4A91A5F30A700B854EE /* Paystack.h */; settings = {ATTRIBUTES = (Public, ); }; }; 04D12C391A5F55D10010446E /* PSTCKAPIClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4C21A5F30A700B854EE /* PSTCKAPIClient.h */; settings = {ATTRIBUTES = (Public, ); }; }; 04D12C3A1A5F55D10010446E /* PSTCKFormEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4C41A5F30A700B854EE /* PSTCKFormEncoder.h */; }; 04D12C3D1A5F55D10010446E /* PSTCKCard.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4CA1A5F30A700B854EE /* PSTCKCard.h */; settings = {ATTRIBUTES = (Public, ); }; }; 04D12C3E1A5F55D10010446E /* PSTCKToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4CC1A5F30A700B854EE /* PSTCKToken.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 04D12C3F1A5F55D10010446E /* PaystackError.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4CE1A5F30A700B854EE /* PaystackError.h */; settings = {ATTRIBUTES = (Public, ); }; }; 04D12C401A5F55FA0010446E /* PSTCKAPIClientTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB51E1A5F3A9300B854EE /* PSTCKAPIClientTest.m */; }; 04D12C411A5F55FA0010446E /* PSTCKFormEncoderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB51F1A5F3A9300B854EE /* PSTCKFormEncoderTest.m */; }; 04D12C451A5F55FA0010446E /* PSTCKCardFunctionalTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB5241A5F3A9300B854EE /* PSTCKCardFunctionalTest.m */; }; @@ -263,20 +274,17 @@ 10500ABE1C8216C200EEF7CF /* PSTCKRSA.h in Headers */ = {isa = PBXBuildFile; fileRef = 10500AB31C8135F800EEF7CF /* PSTCKRSA.h */; }; 10A653A31C88CC5900EBC974 /* PSTCKCardParams.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDE5BB1BC1F21500548833 /* PSTCKCardParams.h */; settings = {ATTRIBUTES = (Public, ); }; }; 10FC52071C88DDB3004A0733 /* PSTCKRSA.h in Headers */ = {isa = PBXBuildFile; fileRef = 10500AB31C8135F800EEF7CF /* PSTCKRSA.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9121E92124B79EAE007BBE61 /* PSTCKAPIPostRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 9121E92024B79EAD007BBE61 /* PSTCKAPIPostRequest.h */; }; - 9121E92A24B7A312007BBE61 /* ValidationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9121E92224B7A311007BBE61 /* ValidationError.swift */; }; - 9121E92B24B7A312007BBE61 /* RequiredRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9121E92324B7A311007BBE61 /* RequiredRule.swift */; }; - 9121E92C24B7A312007BBE61 /* ValidationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9121E92424B7A312007BBE61 /* ValidationDelegate.swift */; }; - 9121E92D24B7A312007BBE61 /* Validator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9121E92524B7A312007BBE61 /* Validator.swift */; }; - 9121E92E24B7A312007BBE61 /* Rule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9121E92624B7A312007BBE61 /* Rule.swift */; }; - 9121E92F24B7A312007BBE61 /* Validatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9121E92724B7A312007BBE61 /* Validatable.swift */; }; - 9121E93024B7A312007BBE61 /* ValidationRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9121E92824B7A312007BBE61 /* ValidationRule.swift */; }; - 9121E93124B7A312007BBE61 /* ValidatorDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9121E92924B7A312007BBE61 /* ValidatorDictionary.swift */; }; - 9121E93424B7A35F007BBE61 /* PaystackError.m in Sources */ = {isa = PBXBuildFile; fileRef = 9121E93224B7A35F007BBE61 /* PaystackError.m */; }; - 9121E93524B7A35F007BBE61 /* PaystackError.h in Headers */ = {isa = PBXBuildFile; fileRef = 9121E93324B7A35F007BBE61 /* PaystackError.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9121E93724B7A43B007BBE61 /* PSTCKAddressViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9121E93624B7A43A007BBE61 /* PSTCKAddressViewController.swift */; }; 919A9FBB249C442300B7A571 /* PSTCKAPIClientExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 919A9FBA249C442300B7A571 /* PSTCKAPIClientExtension.swift */; }; + 91E448EE24B4A8DC007AA8F4 /* PSTCKAddressViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91E448ED24B4A8DC007AA8F4 /* PSTCKAddressViewController.swift */; }; 91E448F824B60883007AA8F4 /* AddressViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 91E448F724B60883007AA8F4 /* AddressViewController.xib */; }; + 91FEDC5224B69EF000A236BA /* ValidatorDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91FEDC4A24B69EF000A236BA /* ValidatorDictionary.swift */; }; + 91FEDC5324B69EF000A236BA /* Validator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91FEDC4B24B69EF000A236BA /* Validator.swift */; }; + 91FEDC5424B69EF000A236BA /* ValidationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91FEDC4C24B69EF000A236BA /* ValidationDelegate.swift */; }; + 91FEDC5524B69EF000A236BA /* Validatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91FEDC4D24B69EF000A236BA /* Validatable.swift */; }; + 91FEDC5624B69EF000A236BA /* Rule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91FEDC4E24B69EF000A236BA /* Rule.swift */; }; + 91FEDC5724B69EF000A236BA /* RequiredRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91FEDC4F24B69EF000A236BA /* RequiredRule.swift */; }; + 91FEDC5824B69EF000A236BA /* ValidationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91FEDC5024B69EF000A236BA /* ValidationError.swift */; }; + 91FEDC5924B69EF000A236BA /* ValidationRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91FEDC5124B69EF000A236BA /* ValidationRule.swift */; }; 91FEDC5B24B6A3DF00A236BA /* ButtonExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91FEDC5A24B6A3DF00A236BA /* ButtonExtension.swift */; }; 91FEDC6124B71FD100A236BA /* Cancel.png in Resources */ = {isa = PBXBuildFile; fileRef = 91FEDC5E24B71FD000A236BA /* Cancel.png */; }; 91FEDC6224B71FD100A236BA /* Cancel@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 91FEDC5F24B71FD000A236BA /* Cancel@2x.png */; }; @@ -346,13 +354,13 @@ 0433EB471BD06313003912B4 /* NSDictionary+Paystack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Paystack.h"; sourceTree = ""; }; 0433EB481BD06313003912B4 /* NSDictionary+Paystack.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+Paystack.m"; sourceTree = ""; }; 04365D2C1A4CF86C00A3E1D4 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; - 0438EF261B7416BB00D506CC /* PSTCKFormTextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKFormTextField.h; sourceTree = ""; }; - 0438EF271B7416BB00D506CC /* PSTCKFormTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKFormTextField.m; sourceTree = ""; }; - 0438EF291B7416BB00D506CC /* PSTCKPaymentCardTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKPaymentCardTextField.m; sourceTree = ""; }; - 0438EF2A1B7416BB00D506CC /* PSTCKPaymentCardTextFieldViewModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKPaymentCardTextFieldViewModel.h; sourceTree = ""; }; - 0438EF2B1B7416BB00D506CC /* PSTCKPaymentCardTextFieldViewModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKPaymentCardTextFieldViewModel.m; sourceTree = ""; }; + 0438EF261B7416BB00D506CC /* PSTCKFormTextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PSTCKFormTextField.h; path = UI/PSTCKFormTextField.h; sourceTree = ""; }; + 0438EF271B7416BB00D506CC /* PSTCKFormTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PSTCKFormTextField.m; path = UI/PSTCKFormTextField.m; sourceTree = ""; }; + 0438EF291B7416BB00D506CC /* PSTCKPaymentCardTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PSTCKPaymentCardTextField.m; path = UI/PSTCKPaymentCardTextField.m; sourceTree = ""; }; + 0438EF2A1B7416BB00D506CC /* PSTCKPaymentCardTextFieldViewModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PSTCKPaymentCardTextFieldViewModel.h; path = UI/PSTCKPaymentCardTextFieldViewModel.h; sourceTree = ""; }; + 0438EF2B1B7416BB00D506CC /* PSTCKPaymentCardTextFieldViewModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PSTCKPaymentCardTextFieldViewModel.m; path = UI/PSTCKPaymentCardTextFieldViewModel.m; sourceTree = ""; }; 0438EF3F1B74170D00D506CC /* PSTCKCardValidator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKCardValidator.m; sourceTree = ""; }; - 0438EF461B74183100D506CC /* PSTCKCardBrand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKCardBrand.h; sourceTree = ""; }; + 0438EF461B74183100D506CC /* PSTCKCardBrand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PSTCKCardBrand.h; path = PublicHeaders/PSTCKCardBrand.h; sourceTree = ""; }; 0438EF4A1B741B0100D506CC /* PSTCKCardValidatorTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKCardValidatorTest.m; sourceTree = ""; }; 0438EF4B1B741B0100D506CC /* PSTCKPaymentCardTextFieldViewModelTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKPaymentCardTextFieldViewModelTest.m; sourceTree = ""; }; 0438EF891B741C2800D506CC /* pstck_card_amex.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = pstck_card_amex.png; sourceTree = ""; }; @@ -384,21 +392,27 @@ 0438EFA31B741C2800D506CC /* pstck_card_visa@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "pstck_card_visa@3x.png"; sourceTree = ""; }; 045A62AA1B8E7259000165CE /* PSTCKPaymentCardTextFieldTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKPaymentCardTextFieldTest.m; sourceTree = ""; }; 045E7C031A5F41DE004751EF /* PaystackiOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "PaystackiOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + 049952CD1BCF13510088C703 /* PSTCKAPIPostRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKAPIPostRequest.h; sourceTree = ""; }; 049952CE1BCF13510088C703 /* PSTCKAPIPostRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKAPIPostRequest.m; sourceTree = ""; }; 049952D11BCF13DD0088C703 /* PSTCKAPIClient+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "PSTCKAPIClient+Private.h"; sourceTree = ""; }; 049E84AB1A605D93000B66CD /* libPaystack.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPaystack.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 04A58A461BC603BB004E7BC2 /* FABKitProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FABKitProtocol.h; sourceTree = ""; }; + 04A58A471BC603BB004E7BC2 /* Fabric+FABKits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Fabric+FABKits.h"; sourceTree = ""; }; + 04A58A481BC603BB004E7BC2 /* Fabric.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Fabric.h; sourceTree = ""; }; 04B33F301BC7417B00DD8120 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 04B94BC71A47B78A00092C46 /* AddressBook.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AddressBook.framework; path = System/Library/Frameworks/AddressBook.framework; sourceTree = SDKROOT; }; 04CDB4421A5F2E1800B854EE /* Paystack.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Paystack.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 04CDB4A91A5F30A700B854EE /* Paystack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Paystack.h; sourceTree = ""; }; - 04CDB4C21A5F30A700B854EE /* PSTCKAPIClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = PSTCKAPIClient.h; sourceTree = ""; }; + 04CDB4A91A5F30A700B854EE /* Paystack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Paystack.h; path = PublicHeaders/Paystack.h; sourceTree = ""; }; + 04CDB4C21A5F30A700B854EE /* PSTCKAPIClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = PSTCKAPIClient.h; path = PublicHeaders/PSTCKAPIClient.h; sourceTree = ""; }; 04CDB4C31A5F30A700B854EE /* PSTCKAPIClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKAPIClient.m; sourceTree = ""; }; 04CDB4C41A5F30A700B854EE /* PSTCKFormEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKFormEncoder.h; sourceTree = ""; }; 04CDB4C51A5F30A700B854EE /* PSTCKFormEncoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKFormEncoder.m; sourceTree = ""; }; - 04CDB4CA1A5F30A700B854EE /* PSTCKCard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKCard.h; sourceTree = ""; }; + 04CDB4CA1A5F30A700B854EE /* PSTCKCard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PSTCKCard.h; path = PublicHeaders/PSTCKCard.h; sourceTree = ""; }; 04CDB4CB1A5F30A700B854EE /* PSTCKCard.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKCard.m; sourceTree = ""; }; - 04CDB4CC1A5F30A700B854EE /* PSTCKToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKToken.h; sourceTree = ""; }; + 04CDB4CC1A5F30A700B854EE /* PSTCKToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PSTCKToken.h; path = PublicHeaders/PSTCKToken.h; sourceTree = ""; }; 04CDB4CD1A5F30A700B854EE /* PSTCKToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKToken.m; sourceTree = ""; }; + 04CDB4CE1A5F30A700B854EE /* PaystackError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PaystackError.h; path = PublicHeaders/PaystackError.h; sourceTree = ""; }; + 04CDB4CF1A5F30A700B854EE /* PaystackError.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PaystackError.m; sourceTree = ""; }; 04CDB51E1A5F3A9300B854EE /* PSTCKAPIClientTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKAPIClientTest.m; sourceTree = ""; }; 04CDB51F1A5F3A9300B854EE /* PSTCKFormEncoderTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKFormEncoderTest.m; sourceTree = ""; }; 04CDB5241A5F3A9300B854EE /* PSTCKCardFunctionalTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKCardFunctionalTest.m; sourceTree = ""; }; @@ -406,15 +420,15 @@ 04CDB5261A5F3A9300B854EE /* PSTCKCertTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKCertTest.m; sourceTree = ""; }; 04CDB5271A5F3A9300B854EE /* PSTCKTokenTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKTokenTest.m; sourceTree = ""; }; 04CDE5B41BC1F1F100548833 /* PSTCKCardParams.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKCardParams.m; sourceTree = ""; }; - 04CDE5BB1BC1F21500548833 /* PSTCKCardParams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKCardParams.h; sourceTree = ""; }; + 04CDE5BB1BC1F21500548833 /* PSTCKCardParams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PSTCKCardParams.h; path = PublicHeaders/PSTCKCardParams.h; sourceTree = ""; }; 04D12C061A5F556D0010446E /* PaystackOSX.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PaystackOSX.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 04D12C101A5F556D0010446E /* PaystackOSXTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PaystackOSXTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 04D5BF9019BF958F009521A5 /* PassKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PassKit.framework; path = System/Library/Frameworks/PassKit.framework; sourceTree = SDKROOT; }; - 04E32A9C1B7A9490009C9E35 /* PSTCKPaymentCardTextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKPaymentCardTextField.h; sourceTree = ""; }; - 04EBC7511B7533C300A0E6AE /* PSTCKCardValidationState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKCardValidationState.h; sourceTree = ""; }; - 04EBC7521B7533C300A0E6AE /* PSTCKCardValidator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKCardValidator.h; sourceTree = ""; }; - 04F213301BCEAB61001D6F22 /* PSTCKFormEncodable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKFormEncodable.h; sourceTree = ""; }; - 04F213341BCECB1C001D6F22 /* PSTCKAPIResponseDecodable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKAPIResponseDecodable.h; sourceTree = ""; }; + 04E32A9C1B7A9490009C9E35 /* PSTCKPaymentCardTextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PSTCKPaymentCardTextField.h; path = PublicHeaders/UI/PSTCKPaymentCardTextField.h; sourceTree = ""; }; + 04EBC7511B7533C300A0E6AE /* PSTCKCardValidationState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PSTCKCardValidationState.h; path = PublicHeaders/PSTCKCardValidationState.h; sourceTree = ""; }; + 04EBC7521B7533C300A0E6AE /* PSTCKCardValidator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PSTCKCardValidator.h; path = PublicHeaders/PSTCKCardValidator.h; sourceTree = ""; }; + 04F213301BCEAB61001D6F22 /* PSTCKFormEncodable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PSTCKFormEncodable.h; path = PublicHeaders/PSTCKFormEncodable.h; sourceTree = ""; }; + 04F213341BCECB1C001D6F22 /* PSTCKAPIResponseDecodable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PSTCKAPIResponseDecodable.h; path = PublicHeaders/PSTCKAPIResponseDecodable.h; sourceTree = ""; }; 04F39F0A1AEF2AFE005B926E /* Project-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Project-Debug.xcconfig"; sourceTree = ""; }; 04F39F0B1AEF2AFE005B926E /* Project-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Project-Release.xcconfig"; sourceTree = ""; }; 04F39F0C1AEF2AFE005B926E /* Project-Shared.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Project-Shared.xcconfig"; sourceTree = ""; }; @@ -434,43 +448,40 @@ 04F39F241AEF2AFE005B926E /* PaystackOSXTests-Shared.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "PaystackOSXTests-Shared.xcconfig"; sourceTree = ""; }; 04FCFA171BD59A8C00297732 /* PSTCKCategoryLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKCategoryLoader.h; sourceTree = ""; }; 04FCFA181BD59A8C00297732 /* PSTCKCategoryLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKCategoryLoader.m; sourceTree = ""; }; - 0806F2F21DBB9E7100C2741B /* PSTCKTransaction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKTransaction.h; sourceTree = ""; }; - 0806F2F31DBB9E7100C2741B /* PSTCKTransactionParams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKTransactionParams.h; sourceTree = ""; }; + 0806F2F21DBB9E7100C2741B /* PSTCKTransaction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PSTCKTransaction.h; path = PublicHeaders/PSTCKTransaction.h; sourceTree = ""; }; + 0806F2F31DBB9E7100C2741B /* PSTCKTransactionParams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PSTCKTransactionParams.h; path = PublicHeaders/PSTCKTransactionParams.h; sourceTree = ""; }; 0806F2F61DBB9E8200C2741B /* PSTCKTransaction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKTransaction.m; sourceTree = ""; }; 0806F2F71DBB9E8200C2741B /* PSTCKTransactionParams.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKTransactionParams.m; sourceTree = ""; }; 0806F2FA1DBBA3C500C2741B /* PSTCKValidationParams.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKValidationParams.m; sourceTree = ""; }; - 0806F2FC1DBBA3F600C2741B /* PSTCKValidationParams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKValidationParams.h; sourceTree = ""; }; + 0806F2FC1DBBA3F600C2741B /* PSTCKValidationParams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PSTCKValidationParams.h; path = PublicHeaders/PSTCKValidationParams.h; sourceTree = ""; }; 0806F2FE1DBBB5DF00C2741B /* pstck_card_verve.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = pstck_card_verve.png; sourceTree = ""; }; 0806F2FF1DBBB5DF00C2741B /* pstck_card_verve@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "pstck_card_verve@2x.png"; sourceTree = ""; }; 0806F3001DBBB5DF00C2741B /* pstck_card_verve@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "pstck_card_verve@3x.png"; sourceTree = ""; }; - 08C260A71E9C214A002AE28C /* PSTCKAuthViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKAuthViewController.h; sourceTree = ""; }; - 08C260A81E9C214A002AE28C /* PSTCKAuthViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKAuthViewController.m; sourceTree = ""; }; - 10500AB11C8135D800EEF7CF /* PSTCKRSA.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKRSA.m; sourceTree = ""; }; - 10500AB31C8135F800EEF7CF /* PSTCKRSA.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PSTCKRSA.h; sourceTree = ""; }; + 08C260A71E9C214A002AE28C /* PSTCKAuthViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PSTCKAuthViewController.h; path = UI/PSTCKAuthViewController.h; sourceTree = ""; }; + 08C260A81E9C214A002AE28C /* PSTCKAuthViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PSTCKAuthViewController.m; path = UI/PSTCKAuthViewController.m; sourceTree = ""; }; + 10500AB11C8135D800EEF7CF /* PSTCKRSA.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PSTCKRSA.m; path = RSA/PSTCKRSA.m; sourceTree = ""; }; + 10500AB31C8135F800EEF7CF /* PSTCKRSA.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PSTCKRSA.h; path = RSA/PSTCKRSA.h; sourceTree = ""; }; 10500AB41C8136EF00EEF7CF /* PSTCKRSATest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKRSATest.m; sourceTree = ""; }; 11C74B9B164043050071C2CA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 4A0D74F918F6106100966D7B /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; - 9121E92024B79EAD007BBE61 /* PSTCKAPIPostRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTCKAPIPostRequest.h; sourceTree = ""; }; - 9121E92224B7A311007BBE61 /* ValidationError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidationError.swift; sourceTree = ""; }; - 9121E92324B7A311007BBE61 /* RequiredRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequiredRule.swift; sourceTree = ""; }; - 9121E92424B7A312007BBE61 /* ValidationDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidationDelegate.swift; sourceTree = ""; }; - 9121E92524B7A312007BBE61 /* Validator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Validator.swift; sourceTree = ""; }; - 9121E92624B7A312007BBE61 /* Rule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Rule.swift; sourceTree = ""; }; - 9121E92724B7A312007BBE61 /* Validatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Validatable.swift; sourceTree = ""; }; - 9121E92824B7A312007BBE61 /* ValidationRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidationRule.swift; sourceTree = ""; }; - 9121E92924B7A312007BBE61 /* ValidatorDictionary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidatorDictionary.swift; sourceTree = ""; }; - 9121E93224B7A35F007BBE61 /* PaystackError.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PaystackError.m; sourceTree = ""; }; - 9121E93324B7A35F007BBE61 /* PaystackError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PaystackError.h; sourceTree = ""; }; - 9121E93624B7A43A007BBE61 /* PSTCKAddressViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PSTCKAddressViewController.swift; sourceTree = ""; }; 919A9FBA249C442300B7A571 /* PSTCKAPIClientExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PSTCKAPIClientExtension.swift; sourceTree = ""; }; + 91E448ED24B4A8DC007AA8F4 /* PSTCKAddressViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PSTCKAddressViewController.swift; sourceTree = ""; }; 91E448F724B60883007AA8F4 /* AddressViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AddressViewController.xib; sourceTree = ""; }; + 91FEDC4A24B69EF000A236BA /* ValidatorDictionary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidatorDictionary.swift; sourceTree = ""; }; + 91FEDC4B24B69EF000A236BA /* Validator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Validator.swift; sourceTree = ""; }; + 91FEDC4C24B69EF000A236BA /* ValidationDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidationDelegate.swift; sourceTree = ""; }; + 91FEDC4D24B69EF000A236BA /* Validatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Validatable.swift; sourceTree = ""; }; + 91FEDC4E24B69EF000A236BA /* Rule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Rule.swift; sourceTree = ""; }; + 91FEDC4F24B69EF000A236BA /* RequiredRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequiredRule.swift; sourceTree = ""; }; + 91FEDC5024B69EF000A236BA /* ValidationError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidationError.swift; sourceTree = ""; }; + 91FEDC5124B69EF000A236BA /* ValidationRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidationRule.swift; sourceTree = ""; }; 91FEDC5A24B6A3DF00A236BA /* ButtonExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonExtension.swift; sourceTree = ""; }; 91FEDC5E24B71FD000A236BA /* Cancel.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Cancel.png; sourceTree = ""; }; 91FEDC5F24B71FD000A236BA /* Cancel@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Cancel@2x.png"; sourceTree = ""; }; 91FEDC6024B71FD000A236BA /* Cancel@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Cancel@3x.png"; sourceTree = ""; }; 91FEDC6424B72B9800A236BA /* KeyboardHandlingVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardHandlingVC.swift; sourceTree = ""; }; - C1718D541C3B2E5B002A7CB3 /* UIImage+Paystack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+Paystack.h"; sourceTree = ""; }; - C1718D551C3B2E5B002A7CB3 /* UIImage+Paystack.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+Paystack.m"; sourceTree = ""; }; + C1718D541C3B2E5B002A7CB3 /* UIImage+Paystack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIImage+Paystack.h"; path = "PublicHeaders/UI/UIImage+Paystack.h"; sourceTree = ""; }; + C1718D551C3B2E5B002A7CB3 /* UIImage+Paystack.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIImage+Paystack.m"; path = "UI/UIImage+Paystack.m"; sourceTree = ""; }; C178CD441C45607D00851C69 /* UIImage+PaystackTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+PaystackTest.m"; sourceTree = ""; }; FAFC12C516E5767F0066297F /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ @@ -522,20 +533,22 @@ 0438EF251B74162700D506CC /* UI */ = { isa = PBXGroup; children = ( + 04E32A9C1B7A9490009C9E35 /* PSTCKPaymentCardTextField.h */, 08C260A71E9C214A002AE28C /* PSTCKAuthViewController.h */, 08C260A81E9C214A002AE28C /* PSTCKAuthViewController.m */, 0438EF291B7416BB00D506CC /* PSTCKPaymentCardTextField.m */, 91E448F724B60883007AA8F4 /* AddressViewController.xib */, + 91E448ED24B4A8DC007AA8F4 /* PSTCKAddressViewController.swift */, 0438EF261B7416BB00D506CC /* PSTCKFormTextField.h */, - 9121E93624B7A43A007BBE61 /* PSTCKAddressViewController.swift */, 0438EF271B7416BB00D506CC /* PSTCKFormTextField.m */, 0438EF2A1B7416BB00D506CC /* PSTCKPaymentCardTextFieldViewModel.h */, 0438EF2B1B7416BB00D506CC /* PSTCKPaymentCardTextFieldViewModel.m */, + C1718D541C3B2E5B002A7CB3 /* UIImage+Paystack.h */, C1718D551C3B2E5B002A7CB3 /* UIImage+Paystack.m */, 91FEDC5A24B6A3DF00A236BA /* ButtonExtension.swift */, 91FEDC6424B72B9800A236BA /* KeyboardHandlingVC.swift */, ); - path = UI; + name = UI; sourceTree = ""; }; 0438EF871B741C2800D506CC /* Resources */ = { @@ -589,6 +602,17 @@ path = Images; sourceTree = ""; }; + 04A58A451BC603BB004E7BC2 /* Fabric */ = { + isa = PBXGroup; + children = ( + 04A58A461BC603BB004E7BC2 /* FABKitProtocol.h */, + 04A58A471BC603BB004E7BC2 /* Fabric+FABKits.h */, + 04A58A481BC603BB004E7BC2 /* Fabric.h */, + ); + name = Fabric; + path = Paystack/Fabric; + sourceTree = ""; + }; 04B33F2F1BC7414C00DD8120 /* Supporting Files */ = { isa = PBXGroup; children = ( @@ -600,9 +624,45 @@ 04CDB4D21A5F30A700B854EE /* Paystack */ = { isa = PBXGroup; children = ( - 91F7793324B774CF0003C7AC /* Classes */, + 04CDB4A91A5F30A700B854EE /* Paystack.h */, + 10500AB01C81357A00EEF7CF /* RSA */, + 04F39F091AEF2AFE005B926E /* BuildConfigurations */, 0438EF871B741C2800D506CC /* Resources */, + 0438EF251B74162700D506CC /* UI */, 04B33F2F1BC7414C00DD8120 /* Supporting Files */, + 91FEDC4924B69EF000A236BA /* Validator */, + 04CDB4C21A5F30A700B854EE /* PSTCKAPIClient.h */, + 04CDB4C31A5F30A700B854EE /* PSTCKAPIClient.m */, + 919A9FBA249C442300B7A571 /* PSTCKAPIClientExtension.swift */, + 049952D11BCF13DD0088C703 /* PSTCKAPIClient+Private.h */, + 049952CD1BCF13510088C703 /* PSTCKAPIPostRequest.h */, + 049952CE1BCF13510088C703 /* PSTCKAPIPostRequest.m */, + 04CDB4C41A5F30A700B854EE /* PSTCKFormEncoder.h */, + 04CDB4C51A5F30A700B854EE /* PSTCKFormEncoder.m */, + 0438EF461B74183100D506CC /* PSTCKCardBrand.h */, + 04F213301BCEAB61001D6F22 /* PSTCKFormEncodable.h */, + 04F213341BCECB1C001D6F22 /* PSTCKAPIResponseDecodable.h */, + 04CDE5BB1BC1F21500548833 /* PSTCKCardParams.h */, + 04CDE5B41BC1F1F100548833 /* PSTCKCardParams.m */, + 04CDB4CA1A5F30A700B854EE /* PSTCKCard.h */, + 04CDB4CB1A5F30A700B854EE /* PSTCKCard.m */, + 04EBC7511B7533C300A0E6AE /* PSTCKCardValidationState.h */, + 04EBC7521B7533C300A0E6AE /* PSTCKCardValidator.h */, + 0438EF3F1B74170D00D506CC /* PSTCKCardValidator.m */, + 0806F2F61DBB9E8200C2741B /* PSTCKTransaction.m */, + 0806F2F31DBB9E7100C2741B /* PSTCKTransactionParams.h */, + 0806F2F71DBB9E8200C2741B /* PSTCKTransactionParams.m */, + 0806F2FC1DBBA3F600C2741B /* PSTCKValidationParams.h */, + 0806F2FA1DBBA3C500C2741B /* PSTCKValidationParams.m */, + 0806F2F21DBB9E7100C2741B /* PSTCKTransaction.h */, + 04CDB4CC1A5F30A700B854EE /* PSTCKToken.h */, + 04CDB4CD1A5F30A700B854EE /* PSTCKToken.m */, + 04CDB4CE1A5F30A700B854EE /* PaystackError.h */, + 04CDB4CF1A5F30A700B854EE /* PaystackError.m */, + 0433EB471BD06313003912B4 /* NSDictionary+Paystack.h */, + 0433EB481BD06313003912B4 /* NSDictionary+Paystack.m */, + 04FCFA171BD59A8C00297732 /* PSTCKCategoryLoader.h */, + 04FCFA181BD59A8C00297732 /* PSTCKCategoryLoader.m */, ); name = Paystack; path = Tests/../Paystack; @@ -657,13 +717,12 @@ 10500AB11C8135D800EEF7CF /* PSTCKRSA.m */, 10500AB31C8135F800EEF7CF /* PSTCKRSA.h */, ); - path = RSA; + name = RSA; sourceTree = ""; }; 11C74B8D164043050071C2CA = { isa = PBXGroup; children = ( - 04F39F091AEF2AFE005B926E /* BuildConfigurations */, 04CDB4D21A5F30A700B854EE /* Paystack */, 04CDB5281A5F3A9300B854EE /* PaystackTests */, 11C74B9A164043050071C2CA /* Frameworks */, @@ -686,6 +745,7 @@ 11C74B9A164043050071C2CA /* Frameworks */ = { isa = PBXGroup; children = ( + 04A58A451BC603BB004E7BC2 /* Fabric */, 04365D2C1A4CF86C00A3E1D4 /* CoreGraphics.framework */, 04B94BC71A47B78A00092C46 /* AddressBook.framework */, 04D5BF9019BF958F009521A5 /* PassKit.framework */, @@ -696,142 +756,17 @@ name = Frameworks; sourceTree = ""; }; - 9121E93824B7B5C7007BBE61 /* PublicHeaders */ = { - isa = PBXGroup; - children = ( - 04F213341BCECB1C001D6F22 /* PSTCKAPIResponseDecodable.h */, - 04E32A9C1B7A9490009C9E35 /* PSTCKPaymentCardTextField.h */, - 04CDB4CA1A5F30A700B854EE /* PSTCKCard.h */, - 04EBC7511B7533C300A0E6AE /* PSTCKCardValidationState.h */, - 04CDB4A91A5F30A700B854EE /* Paystack.h */, - 04CDB4C41A5F30A700B854EE /* PSTCKFormEncoder.h */, - 04EBC7521B7533C300A0E6AE /* PSTCKCardValidator.h */, - 04CDB4CC1A5F30A700B854EE /* PSTCKToken.h */, - C1718D541C3B2E5B002A7CB3 /* UIImage+Paystack.h */, - 0806F2F21DBB9E7100C2741B /* PSTCKTransaction.h */, - 0806F2F31DBB9E7100C2741B /* PSTCKTransactionParams.h */, - 0806F2FC1DBBA3F600C2741B /* PSTCKValidationParams.h */, - 0438EF461B74183100D506CC /* PSTCKCardBrand.h */, - 9121E93324B7A35F007BBE61 /* PaystackError.h */, - 04CDE5BB1BC1F21500548833 /* PSTCKCardParams.h */, - 04CDB4C21A5F30A700B854EE /* PSTCKAPIClient.h */, - ); - path = PublicHeaders; - sourceTree = ""; - }; - 91F7793324B774CF0003C7AC /* Classes */ = { - isa = PBXGroup; - children = ( - 9121E93824B7B5C7007BBE61 /* PublicHeaders */, - 91F7793424B77F4D0003C7AC /* API Client */, - 91F7793824B781270003C7AC /* Error Handler */, - 91F7793924B781800003C7AC /* Card */, - 91F7793A24B7819B0003C7AC /* Form Encoder */, - 91F7793724B7809F0003C7AC /* Helpers */, - 91F7793B24B781BB0003C7AC /* Transaction */, - 91F7793C24B781EF0003C7AC /* Token */, - 10500AB01C81357A00EEF7CF /* RSA */, - 0438EF251B74162700D506CC /* UI */, - 91FEDC4924B69EF000A236BA /* Validator */, - ); - path = Classes; - sourceTree = ""; - }; - 91F7793424B77F4D0003C7AC /* API Client */ = { - isa = PBXGroup; - children = ( - 9121E92024B79EAD007BBE61 /* PSTCKAPIPostRequest.h */, - 049952CE1BCF13510088C703 /* PSTCKAPIPostRequest.m */, - 04CDB4C31A5F30A700B854EE /* PSTCKAPIClient.m */, - 919A9FBA249C442300B7A571 /* PSTCKAPIClientExtension.swift */, - 049952D11BCF13DD0088C703 /* PSTCKAPIClient+Private.h */, - ); - path = "API Client"; - sourceTree = ""; - }; - 91F7793524B77FA20003C7AC /* Card Validator */ = { - isa = PBXGroup; - children = ( - 0438EF3F1B74170D00D506CC /* PSTCKCardValidator.m */, - 0806F2FA1DBBA3C500C2741B /* PSTCKValidationParams.m */, - ); - path = "Card Validator"; - sourceTree = ""; - }; - 91F7793624B77FBD0003C7AC /* Textfield Validator */ = { - isa = PBXGroup; - children = ( - 9121E92324B7A311007BBE61 /* RequiredRule.swift */, - 9121E92624B7A312007BBE61 /* Rule.swift */, - 9121E92724B7A312007BBE61 /* Validatable.swift */, - 9121E92424B7A312007BBE61 /* ValidationDelegate.swift */, - 9121E92224B7A311007BBE61 /* ValidationError.swift */, - 9121E92824B7A312007BBE61 /* ValidationRule.swift */, - 9121E92524B7A312007BBE61 /* Validator.swift */, - 9121E92924B7A312007BBE61 /* ValidatorDictionary.swift */, - ); - path = "Textfield Validator"; - sourceTree = ""; - }; - 91F7793724B7809F0003C7AC /* Helpers */ = { - isa = PBXGroup; - children = ( - 0433EB471BD06313003912B4 /* NSDictionary+Paystack.h */, - 0433EB481BD06313003912B4 /* NSDictionary+Paystack.m */, - 04FCFA171BD59A8C00297732 /* PSTCKCategoryLoader.h */, - 04FCFA181BD59A8C00297732 /* PSTCKCategoryLoader.m */, - ); - path = Helpers; - sourceTree = ""; - }; - 91F7793824B781270003C7AC /* Error Handler */ = { - isa = PBXGroup; - children = ( - 9121E93224B7A35F007BBE61 /* PaystackError.m */, - ); - path = "Error Handler"; - sourceTree = ""; - }; - 91F7793924B781800003C7AC /* Card */ = { - isa = PBXGroup; - children = ( - 04CDE5B41BC1F1F100548833 /* PSTCKCardParams.m */, - 04CDB4CB1A5F30A700B854EE /* PSTCKCard.m */, - ); - path = Card; - sourceTree = ""; - }; - 91F7793A24B7819B0003C7AC /* Form Encoder */ = { - isa = PBXGroup; - children = ( - 04CDB4C51A5F30A700B854EE /* PSTCKFormEncoder.m */, - 04F213301BCEAB61001D6F22 /* PSTCKFormEncodable.h */, - ); - path = "Form Encoder"; - sourceTree = ""; - }; - 91F7793B24B781BB0003C7AC /* Transaction */ = { - isa = PBXGroup; - children = ( - 0806F2F61DBB9E8200C2741B /* PSTCKTransaction.m */, - 0806F2F71DBB9E8200C2741B /* PSTCKTransactionParams.m */, - ); - path = Transaction; - sourceTree = ""; - }; - 91F7793C24B781EF0003C7AC /* Token */ = { - isa = PBXGroup; - children = ( - 04CDB4CD1A5F30A700B854EE /* PSTCKToken.m */, - ); - path = Token; - sourceTree = ""; - }; 91FEDC4924B69EF000A236BA /* Validator */ = { isa = PBXGroup; children = ( - 91F7793524B77FA20003C7AC /* Card Validator */, - 91F7793624B77FBD0003C7AC /* Textfield Validator */, + 91FEDC4A24B69EF000A236BA /* ValidatorDictionary.swift */, + 91FEDC4B24B69EF000A236BA /* Validator.swift */, + 91FEDC4C24B69EF000A236BA /* ValidationDelegate.swift */, + 91FEDC4D24B69EF000A236BA /* Validatable.swift */, + 91FEDC4E24B69EF000A236BA /* Rule.swift */, + 91FEDC4F24B69EF000A236BA /* RequiredRule.swift */, + 91FEDC5024B69EF000A236BA /* ValidationError.swift */, + 91FEDC5124B69EF000A236BA /* ValidationRule.swift */, ); path = Validator; sourceTree = ""; @@ -852,6 +787,7 @@ 04E32A9E1B7A9490009C9E35 /* PSTCKPaymentCardTextField.h in Headers */, 10500ABE1C8216C200EEF7CF /* PSTCKRSA.h in Headers */, 04415C721A6605D9001225ED /* Paystack.h in Headers */, + 04415C851A6605D9001225ED /* PaystackError.h in Headers */, 08C260AA1E9C214A002AE28C /* PSTCKAuthViewController.h in Headers */, 04415C7F1A6605D9001225ED /* PSTCKAPIClient.h in Headers */, 04CDE5BF1BC1FD4500548833 /* PSTCKCardParams.h in Headers */, @@ -877,7 +813,9 @@ 0438EF491B74183100D506CC /* PSTCKCardBrand.h in Headers */, 10500ABD1C8215F400EEF7CF /* PSTCKRSA.h in Headers */, 08A572411DF92F3F0045E184 /* PSTCKTransactionParams.h in Headers */, + 049952D61BCF14930088C703 /* PSTCKAPIPostRequest.h in Headers */, 04F213331BCEAB61001D6F22 /* PSTCKFormEncodable.h in Headers */, + 049E84EC1A605EF0000B66CD /* PaystackError.h in Headers */, 08A572421DF92F3F0045E184 /* PSTCKValidationParams.h in Headers */, 08A572431DF92F3F0045E184 /* PSTCKTransaction.h in Headers */, C1718D581C3B2E60002A7CB3 /* UIImage+Paystack.h in Headers */, @@ -899,16 +837,16 @@ 0433EB491BD06313003912B4 /* NSDictionary+Paystack.h in Headers */, 04CDB50E1A5F30A700B854EE /* PSTCKCard.h in Headers */, 0438EF2C1B7416BB00D506CC /* PSTCKFormTextField.h in Headers */, - 9121E93524B7A35F007BBE61 /* PaystackError.h in Headers */, 08C260A91E9C214A002AE28C /* PSTCKAuthViewController.h in Headers */, 049952D21BCF13DD0088C703 /* PSTCKAPIClient+Private.h in Headers */, 0806F2F51DBB9E7100C2741B /* PSTCKTransactionParams.h in Headers */, C1718D561C3B2E5B002A7CB3 /* UIImage+Paystack.h in Headers */, - 9121E92124B79EAE007BBE61 /* PSTCKAPIPostRequest.h in Headers */, 04CDB5121A5F30A700B854EE /* PSTCKToken.h in Headers */, + 049952CF1BCF13510088C703 /* PSTCKAPIPostRequest.h in Headers */, 0438EF471B74183100D506CC /* PSTCKCardBrand.h in Headers */, 10A653A31C88CC5900EBC974 /* PSTCKCardParams.h in Headers */, 10FC52071C88DDB3004A0733 /* PSTCKRSA.h in Headers */, + 04CDB5161A5F30A700B854EE /* PaystackError.h in Headers */, 08A572401DF92F1D0045E184 /* PSTCKCategoryLoader.h in Headers */, 04E32A9D1B7A9490009C9E35 /* PSTCKPaymentCardTextField.h in Headers */, 0806F2F41DBB9E7100C2741B /* PSTCKTransaction.h in Headers */, @@ -935,6 +873,8 @@ 049952D31BCF13DD0088C703 /* PSTCKAPIClient+Private.h in Headers */, 04D12C3D1A5F55D10010446E /* PSTCKCard.h in Headers */, 04D12C3E1A5F55D10010446E /* PSTCKToken.h in Headers */, + 04D12C3F1A5F55D10010446E /* PaystackError.h in Headers */, + 049952D51BCF14920088C703 /* PSTCKAPIPostRequest.h in Headers */, 10500ABB1C82155B00EEF7CF /* PSTCKRSA.h in Headers */, 0438EFDB1B7524AA00D506CC /* PSTCKCardBrand.h in Headers */, 0433EB4A1BD06313003912B4 /* NSDictionary+Paystack.h in Headers */, @@ -1064,7 +1004,6 @@ 04CDB4411A5F2E1800B854EE = { CreatedOnToolsVersion = 6.1.1; LastSwiftMigration = 1150; - ProvisioningStyle = Automatic; }; 04D12C051A5F556D0010446E = { CreatedOnToolsVersion = 6.1.1; @@ -1269,6 +1208,7 @@ 04415C641A6605B5001225ED /* PSTCKCard.m in Sources */, 04415C651A6605B5001225ED /* PSTCKToken.m in Sources */, 08C260AF1E9C214A002AE28C /* PSTCKAuthViewController.m in Sources */, + 04415C661A6605B5001225ED /* PaystackError.m in Sources */, 10500AB51C8136EF00EEF7CF /* PSTCKRSATest.m in Sources */, 04415C671A6605B5001225ED /* PSTCKAPIClientTest.m in Sources */, 04415C681A6605B5001225ED /* PSTCKFormEncoderTest.m in Sources */, @@ -1300,6 +1240,7 @@ 0438EF3D1B7416BB00D506CC /* PSTCKPaymentCardTextFieldViewModel.m in Sources */, 04CDE5BA1BC1F1F100548833 /* PSTCKCardParams.m in Sources */, 049E84D11A605DE0000B66CD /* PSTCKToken.m in Sources */, + 049E84D21A605DE0000B66CD /* PaystackError.m in Sources */, 049952D81BCF14990088C703 /* PSTCKAPIPostRequest.m in Sources */, 08C260B21E9C214A002AE28C /* PSTCKAuthViewController.m in Sources */, ); @@ -1312,32 +1253,32 @@ 91FEDC6524B72B9800A236BA /* KeyboardHandlingVC.swift in Sources */, 08A5723F1DF92EF30045E184 /* PSTCKCategoryLoader.m in Sources */, 0438EF431B74170D00D506CC /* PSTCKCardValidator.m in Sources */, - 9121E92B24B7A312007BBE61 /* RequiredRule.swift in Sources */, 0438EF2F1B7416BB00D506CC /* PSTCKFormTextField.m in Sources */, - 9121E92A24B7A312007BBE61 /* ValidationError.swift in Sources */, - 9121E92F24B7A312007BBE61 /* Validatable.swift in Sources */, + 91FEDC5724B69EF000A236BA /* RequiredRule.swift in Sources */, 919A9FBB249C442300B7A571 /* PSTCKAPIClientExtension.swift in Sources */, - 9121E93724B7A43B007BBE61 /* PSTCKAddressViewController.swift in Sources */, + 91FEDC5224B69EF000A236BA /* ValidatorDictionary.swift in Sources */, + 91FEDC5524B69EF000A236BA /* Validatable.swift in Sources */, + 91FEDC5324B69EF000A236BA /* Validator.swift in Sources */, + 91FEDC5924B69EF000A236BA /* ValidationRule.swift in Sources */, 0806F2F91DBB9E8200C2741B /* PSTCKTransactionParams.m in Sources */, + 91FEDC5824B69EF000A236BA /* ValidationError.swift in Sources */, 04CDB5101A5F30A700B854EE /* PSTCKCard.m in Sources */, 04CDB5001A5F30A700B854EE /* PSTCKAPIClient.m in Sources */, - 9121E93124B7A312007BBE61 /* ValidatorDictionary.swift in Sources */, 10500AB21C8135D800EEF7CF /* PSTCKRSA.m in Sources */, - 9121E93024B7A312007BBE61 /* ValidationRule.swift in Sources */, 0806F2FB1DBBA3C500C2741B /* PSTCKValidationParams.m in Sources */, + 04CDB5181A5F30A700B854EE /* PaystackError.m in Sources */, C1718D571C3B2E5B002A7CB3 /* UIImage+Paystack.m in Sources */, 0806F2F81DBB9E8200C2741B /* PSTCKTransaction.m in Sources */, - 9121E92E24B7A312007BBE61 /* Rule.swift in Sources */, - 9121E93424B7A35F007BBE61 /* PaystackError.m in Sources */, + 91E448EE24B4A8DC007AA8F4 /* PSTCKAddressViewController.swift in Sources */, 0438EF351B7416BB00D506CC /* PSTCKPaymentCardTextField.m in Sources */, 04CDB5041A5F30A700B854EE /* PSTCKFormEncoder.m in Sources */, + 91FEDC5424B69EF000A236BA /* ValidationDelegate.swift in Sources */, 04CDB5141A5F30A700B854EE /* PSTCKToken.m in Sources */, 0433EB4C1BD06313003912B4 /* NSDictionary+Paystack.m in Sources */, 0438EF3B1B7416BB00D506CC /* PSTCKPaymentCardTextFieldViewModel.m in Sources */, 04CDE5B81BC1F1F100548833 /* PSTCKCardParams.m in Sources */, + 91FEDC5624B69EF000A236BA /* Rule.swift in Sources */, 91FEDC5B24B6A3DF00A236BA /* ButtonExtension.swift in Sources */, - 9121E92C24B7A312007BBE61 /* ValidationDelegate.swift in Sources */, - 9121E92D24B7A312007BBE61 /* Validator.swift in Sources */, 049952D01BCF13510088C703 /* PSTCKAPIPostRequest.m in Sources */, 08C260AE1E9C214A002AE28C /* PSTCKAuthViewController.m in Sources */, ); @@ -1357,6 +1298,7 @@ 08C260B01E9C214A002AE28C /* PSTCKAuthViewController.m in Sources */, 0433EB4D1BD06313003912B4 /* NSDictionary+Paystack.m in Sources */, 049952D71BCF14980088C703 /* PSTCKAPIPostRequest.m in Sources */, + 04D12C2B1A5F55AD0010446E /* PaystackError.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1451,19 +1393,13 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; - CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 18; DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = ""; IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MARKETING_VERSION = 3.0.14; PRODUCT_BUNDLE_IDENTIFIER = "com.paystack.paystack-ios"; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; SWIFT_OBJC_BRIDGING_HEADER = ""; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -1476,19 +1412,13 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; - CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 18; DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = ""; IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MARKETING_VERSION = 3.0.14; PRODUCT_BUNDLE_IDENTIFIER = "com.paystack.paystack-ios"; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; SWIFT_OBJC_BRIDGING_HEADER = ""; SWIFT_VERSION = 5.0; }; @@ -1544,7 +1474,6 @@ IPHONEOS_DEPLOYMENT_TARGET = 11.0; MACOSX_DEPLOYMENT_TARGET = 10.7; ONLY_ACTIVE_ARCH = YES; - SKIP_INSTALL = YES; SWIFT_SWIFT3_OBJC_INFERENCE = Default; SWIFT_VERSION = 5.0; }; @@ -1564,7 +1493,6 @@ DEFINES_MODULE = YES; IPHONEOS_DEPLOYMENT_TARGET = 11.0; MACOSX_DEPLOYMENT_TARGET = 10.7; - SKIP_INSTALL = YES; SWIFT_SWIFT3_OBJC_INFERENCE = Default; SWIFT_VERSION = 5.0; }; diff --git a/Paystack/AddressViewController.xib b/Paystack/AddressViewController.xib new file mode 100644 index 0000000..97c004d --- /dev/null +++ b/Paystack/AddressViewController.xib @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Paystack/BuildConfigurations/PaystackOSX-Debug.xcconfig b/Paystack/BuildConfigurations/PaystackOSX-Debug.xcconfig new file mode 100644 index 0000000..5556711 --- /dev/null +++ b/Paystack/BuildConfigurations/PaystackOSX-Debug.xcconfig @@ -0,0 +1,11 @@ +// +// PaystackOSX-Debug.xcconfig +// +// Generated by BuildSettingExtractor on 4/27/15 +// https://github.com/dempseyatgithub/BuildSettingExtractor +// + +#include "PaystackOSX-Shared.xcconfig" + + +GCC_PREPROCESSOR_DEFINITIONS = DEBUG=1 $(inherited) \ No newline at end of file diff --git a/Paystack/BuildConfigurations/PaystackOSX-Release.xcconfig b/Paystack/BuildConfigurations/PaystackOSX-Release.xcconfig new file mode 100644 index 0000000..48f2d46 --- /dev/null +++ b/Paystack/BuildConfigurations/PaystackOSX-Release.xcconfig @@ -0,0 +1,22 @@ +// +// PaystackOSX-Release.xcconfig +// +// Generated by BuildSettingExtractor on 4/27/15 +// https://github.com/dempseyatgithub/BuildSettingExtractor +// + +#include "PaystackOSX-Shared.xcconfig" + + +// Debug Information Format +// +// This setting controls the format of debug information used by the developer tools. +// +// DWARF - Object files and linked products will use DWARF as the debug information +// format. [dwarf] +// DWARF with dSYM File - Object files and linked products will use DWARF as the debug +// information format, and Xcode will also produce a dSYM file containing the debug +// information from the individual object files (except that a dSYM file is not needed +// and will not be created for static library or object file products). [dwarf-with-dsym] + +DEBUG_INFORMATION_FORMAT = dwarf-with-dsym \ No newline at end of file diff --git a/Paystack/BuildConfigurations/PaystackOSX-Shared.xcconfig b/Paystack/BuildConfigurations/PaystackOSX-Shared.xcconfig new file mode 100644 index 0000000..130d777 --- /dev/null +++ b/Paystack/BuildConfigurations/PaystackOSX-Shared.xcconfig @@ -0,0 +1,186 @@ +// +// PaystackOSX-Shared.xcconfig +// +// Generated by BuildSettingExtractor on 4/27/15 +// https://github.com/dempseyatgithub/BuildSettingExtractor +// + + +CLANG_CXX_LANGUAGE_STANDARD = gnu++0x + + +CLANG_CXX_LIBRARY = libc++ + + +CLANG_ENABLE_MODULES = YES + + +CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR + + +CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR + + +// Combine High Resolution Artwork +// +// Combines image files at different resolutions into one multi-page TIFF file that is +// HiDPI compliant for Mac OS X 10.7 and later. Only image files in the same directory +// and with the same base name and extension are combined. The file names must conform to +// the naming convention used in HiDPI. + +COMBINE_HIDPI_IMAGES = YES + + +// Current Project Version +// +// This setting defines the the current version of the project. The value must be a +// integer or floating point number like 57 or 365.8. + +CURRENT_PROJECT_VERSION = 1 + + +// Defines Module +// +// If enabled, the product will be treated as defining its own module. This enables +// automatic production of LLVM module map files when appropriate, and allows the product +// to be imported as a module. + +DEFINES_MODULE = YES + +MODULEMAP_FILE = Paystack/module.modulemap + + +// Compatibility Version +// +// Determines the compatibility version of the resulting library, bundle, or framework +// binary. + +DYLIB_COMPATIBILITY_VERSION = 1 + + +// Current Library Version +// +// This setting defines the the current version of any framework built by the project. +// Like "Current Project Version", the value must be an integer or floating point number +// like 57 or 365.8. By default it is set to $(CURRENT_PROJECT_VERSION). + +DYLIB_CURRENT_VERSION = 1 + + +// Dynamic Library Install Name Base +// +// Sets the base value for the internal "install path" (LC_ID_DYLIB) in a dynamic +// library. This will be combined with the EXECUTABLE_PATH to form the full install path. +// Setting LD_DYLIB_INSTALL_NAME directly will override this setting. This setting +// defaults to the target's INSTALL_PATH. It is ignored when building any product other +// than a dynamic library. [-install_name] + +DYLIB_INSTALL_NAME_BASE = @rpath + + +// Framework Version +// +// Framework bundles are versioned by having contents in subfolders of a version folder +// that has links to the current version and its contents. + +FRAMEWORK_VERSION = A + + +GCC_TREAT_WARNINGS_AS_ERRORS = YES + + +GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR + + +GCC_WARN_SHADOW = YES + + +GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE + + +// Info.plist File +// +// This is the project-relative path to the plist file that contains the Info.plist +// information used by bundles. + +INFOPLIST_FILE = Paystack/Info.plist + + +// Installation Directory +// +// The directory to install the build products in. This path is prepended by the +// 'Installation Build Products Location' (i.e., $(DSTROOT)). + +INSTALL_PATH = $(LOCAL_LIBRARY_DIR)/Frameworks + + +// Runpath Search Paths +// +// This is a list of paths to be added to the runpath search path list for the image +// being created. At runtime, dyld uses the runpath when searching for dylibs whose load +// path begins with '@rpath/'. [-rpath] + +LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/../Frameworks @loader_path/Frameworks + + +// OS X Deployment Target +// +// Code will load on this and later versions of OS X. Framework APIs that are +// unavailable in earlier versions will be weak-linked; your code should check for null +// function pointers or specific system versions before calling newer APIs. +// +// Compiler Default - Code will load on any Mac OS system that supports the APIs that are +// used. +// OS X 10.4 - Code will not load on systems earlier than 10.4. [10.4] +// OS X 10.5 - Code will not load on systems earlier than 10.5. [10.5] +// OS X 10.6 - Code will not load on systems earlier than 10.6. [10.6] +// OS X 10.7 - Code will not load on systems earlier than 10.7. [10.7] +// OS X 10.8 - Code will not load on systems earlier than 10.8. [10.8] +// OS X 10.9 - Code will not load on systems earlier than 10.9. [10.9] + +MACOSX_DEPLOYMENT_TARGET = 10.9 + + +// Product Name +// +// This is the basename of the product generated. + +PRODUCT_NAME = PaystackOSX + + +// Base SDK +// +// The name or path of the base SDK being used during the build. The product will be +// built against the headers and libraries located inside the indicated SDK. This path +// will be prepended to all search paths, and will be passed through the environment to +// the compiler and linker. Additional SDKs can be specified in the ADDITIONAL_SDKS +// setting. + +SDKROOT = macosx + + +// Skip Install +// +// Activating this setting when deployment locations are used causes the product to be +// built into an alternative location instead of the install location. + +SKIP_INSTALL = YES + + +// Versioning Name Prefix +// +// Used as a prefix for the name of the version info symbol in the generated versioning +// source file. If you prefix your exported symbols you will probably want to set this +// to the same prefix. + +VERSION_INFO_PREFIX = + + +// Versioning System +// +// Selects the process used for version-stamping generated files. +// +// None - Use no versioning system. [] +// Apple Generic - Use the current project version setting. [apple-generic] + +VERSIONING_SYSTEM = apple-generic diff --git a/Paystack/BuildConfigurations/PaystackOSXTests-Debug.xcconfig b/Paystack/BuildConfigurations/PaystackOSXTests-Debug.xcconfig new file mode 100644 index 0000000..e6b6317 --- /dev/null +++ b/Paystack/BuildConfigurations/PaystackOSXTests-Debug.xcconfig @@ -0,0 +1,11 @@ +// +// PaystackOSXTests-Debug.xcconfig +// +// Generated by BuildSettingExtractor on 4/27/15 +// https://github.com/dempseyatgithub/BuildSettingExtractor +// + +#include "PaystackOSXTests-Shared.xcconfig" + + +GCC_PREPROCESSOR_DEFINITIONS = DEBUG=1 $(inherited) \ No newline at end of file diff --git a/Paystack/BuildConfigurations/PaystackOSXTests-Release.xcconfig b/Paystack/BuildConfigurations/PaystackOSXTests-Release.xcconfig new file mode 100644 index 0000000..afa8f3e --- /dev/null +++ b/Paystack/BuildConfigurations/PaystackOSXTests-Release.xcconfig @@ -0,0 +1,22 @@ +// +// PaystackOSXTests-Release.xcconfig +// +// Generated by BuildSettingExtractor on 4/27/15 +// https://github.com/dempseyatgithub/BuildSettingExtractor +// + +#include "PaystackOSXTests-Shared.xcconfig" + + +// Debug Information Format +// +// This setting controls the format of debug information used by the developer tools. +// +// DWARF - Object files and linked products will use DWARF as the debug information +// format. [dwarf] +// DWARF with dSYM File - Object files and linked products will use DWARF as the debug +// information format, and Xcode will also produce a dSYM file containing the debug +// information from the individual object files (except that a dSYM file is not needed +// and will not be created for static library or object file products). [dwarf-with-dsym] + +DEBUG_INFORMATION_FORMAT = dwarf-with-dsym diff --git a/Paystack/BuildConfigurations/PaystackOSXTests-Shared.xcconfig b/Paystack/BuildConfigurations/PaystackOSXTests-Shared.xcconfig new file mode 100644 index 0000000..dc3d9dd --- /dev/null +++ b/Paystack/BuildConfigurations/PaystackOSXTests-Shared.xcconfig @@ -0,0 +1,122 @@ +// +// PaystackOSXTests-Shared.xcconfig +// +// Generated by BuildSettingExtractor on 4/27/15 +// https://github.com/dempseyatgithub/BuildSettingExtractor +// + + +CLANG_CXX_LANGUAGE_STANDARD = gnu++0x + + + +CLANG_CXX_LIBRARY = libc++ + + + +CLANG_ENABLE_MODULES = YES + + + +CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR + + + +CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR + + + +// Combine High Resolution Artwork +// +// Combines image files at different resolutions into one multi-page TIFF file that is +// HiDPI compliant for Mac OS X 10.7 and later. Only image files in the same directory +// and with the same base name and extension are combined. The file names must conform to +// the naming convention used in HiDPI. + +COMBINE_HIDPI_IMAGES = YES + + + +// Framework Search Paths +// +// This is a list of paths to folders containing frameworks to be searched by the +// compiler for both included or imported header files when compiling C, Objective-C, +// C++, or Objective-C++, and by the linker for frameworks used by the product. Paths are +// delimited by whitespace, so any paths with spaces in them need to be properly quoted. +// [-F] + +FRAMEWORK_SEARCH_PATHS = $(DEVELOPER_FRAMEWORKS_DIR) $(inherited) + + + +GCC_TREAT_WARNINGS_AS_ERRORS = YES + + + +GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR + + + +GCC_WARN_SHADOW = YES + + + +GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE + + + +// Info.plist File +// +// This is the project-relative path to the plist file that contains the Info.plist +// information used by bundles. + +INFOPLIST_FILE = Tests/Tests/Info.plist + + + +// Runpath Search Paths +// +// This is a list of paths to be added to the runpath search path list for the image +// being created. At runtime, dyld uses the runpath when searching for dylibs whose load +// path begins with '@rpath/'. [-rpath] + +LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/../Frameworks @loader_path/../Frameworks + + + +// OS X Deployment Target +// +// Code will load on this and later versions of OS X. Framework APIs that are +// unavailable in earlier versions will be weak-linked; your code should check for null +// function pointers or specific system versions before calling newer APIs. +// +// Compiler Default - Code will load on any Mac OS system that supports the APIs that are +// used. +// OS X 10.4 - Code will not load on systems earlier than 10.4. [10.4] +// OS X 10.5 - Code will not load on systems earlier than 10.5. [10.5] +// OS X 10.6 - Code will not load on systems earlier than 10.6. [10.6] +// OS X 10.7 - Code will not load on systems earlier than 10.7. [10.7] +// OS X 10.8 - Code will not load on systems earlier than 10.8. [10.8] +// OS X 10.9 - Code will not load on systems earlier than 10.9. [10.9] + +MACOSX_DEPLOYMENT_TARGET = 10.10 + + + +// Product Name +// +// This is the basename of the product generated. + +PRODUCT_NAME = $(TARGET_NAME) + + + +// Base SDK +// +// The name or path of the base SDK being used during the build. The product will be +// built against the headers and libraries located inside the indicated SDK. This path +// will be prepended to all search paths, and will be passed through the environment to +// the compiler and linker. Additional SDKs can be specified in the ADDITIONAL_SDKS +// setting. + +SDKROOT = macosx \ No newline at end of file diff --git a/Paystack/BuildConfigurations/PaystackiOS Tests-Debug.xcconfig b/Paystack/BuildConfigurations/PaystackiOS Tests-Debug.xcconfig new file mode 100644 index 0000000..bdd1b63 --- /dev/null +++ b/Paystack/BuildConfigurations/PaystackiOS Tests-Debug.xcconfig @@ -0,0 +1,11 @@ +// +// PaystackiOS Tests-Debug.xcconfig +// +// Generated by BuildSettingExtractor on 4/27/15 +// https://github.com/dempseyatgithub/BuildSettingExtractor +// + +#include "PaystackiOS Tests-Shared.xcconfig" + +GCC_PREPROCESSOR_DEFINITIONS = DEBUG=1 $(inherited) +DEBUG_INFORMATION_FORMAT = dwarf diff --git a/Paystack/BuildConfigurations/PaystackiOS Tests-Release.xcconfig b/Paystack/BuildConfigurations/PaystackiOS Tests-Release.xcconfig new file mode 100644 index 0000000..f29f324 --- /dev/null +++ b/Paystack/BuildConfigurations/PaystackiOS Tests-Release.xcconfig @@ -0,0 +1,12 @@ +// +// PaystackiOS Tests-Release.xcconfig +// +// Generated by BuildSettingExtractor on 4/27/15 +// https://github.com/dempseyatgithub/BuildSettingExtractor +// + +#include "PaystackiOS Tests-Shared.xcconfig" + +//********************************************// +//* Currently no build settings in this file *// +//********************************************// diff --git a/Paystack/BuildConfigurations/PaystackiOS Tests-Shared.xcconfig b/Paystack/BuildConfigurations/PaystackiOS Tests-Shared.xcconfig new file mode 100644 index 0000000..c2b1731 --- /dev/null +++ b/Paystack/BuildConfigurations/PaystackiOS Tests-Shared.xcconfig @@ -0,0 +1,92 @@ +// +// PaystackiOS Tests-Shared.xcconfig +// +// Generated by BuildSettingExtractor on 4/27/15 +// https://github.com/dempseyatgithub/BuildSettingExtractor +// + + +CLANG_CXX_LANGUAGE_STANDARD = gnu++0x + + +CLANG_CXX_LIBRARY = libc++ + + +CLANG_ENABLE_MODULES = YES + + +CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR + + +CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR + + +IPHONEOS_DEPLOYMENT_TARGET = 8.0 + + +// Code Signing Identity +// +// The name ("common name") of a valid code-signing certificate in a keychain within your +// keychain path. A missing or invalid certificate will cause a build error. + +CODE_SIGN_IDENTITY = iPhone Developer + + +// Framework Search Paths +// +// This is a list of paths to folders containing frameworks to be searched by the +// compiler for both included or imported header files when compiling C, Objective-C, +// C++, or Objective-C++, and by the linker for frameworks used by the product. Paths are +// delimited by whitespace, so any paths with spaces in them need to be properly quoted. +// [-F] + +FRAMEWORK_SEARCH_PATHS = $(inherited) + + +GCC_TREAT_WARNINGS_AS_ERRORS = YES + + +GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR + + +GCC_WARN_SHADOW = YES + + +GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE + + +// Header Search Paths +// +// This is a list of paths to folders to be searched by the compiler for included or +// imported header files when compiling C, Objective-C, C++, or Objective-C++. Paths are +// delimited by whitespace, so any paths with spaces in them need to be properly quoted. +// [-I] + +HEADER_SEARCH_PATHS = $(inherited) + + +// Info.plist File +// +// This is the project-relative path to the plist file that contains the Info.plist +// information used by bundles. + +INFOPLIST_FILE = Tests/Tests/Info.plist + + +// Runpath Search Paths +// +// This is a list of paths to be added to the runpath search path list for the image +// being created. At runtime, dyld uses the runpath when searching for dylibs whose load +// path begins with '@rpath/'. [-rpath] + +LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks @loader_path/Frameworks + + +OTHER_CFLAGS = + + +// Product Name +// +// This is the basename of the product generated. + +PRODUCT_NAME = $(TARGET_NAME) diff --git a/Paystack/BuildConfigurations/PaystackiOS-Debug.xcconfig b/Paystack/BuildConfigurations/PaystackiOS-Debug.xcconfig new file mode 100644 index 0000000..0aa4115 --- /dev/null +++ b/Paystack/BuildConfigurations/PaystackiOS-Debug.xcconfig @@ -0,0 +1,11 @@ +// +// PaystackiOS-Debug.xcconfig +// +// Generated by BuildSettingExtractor on 4/27/15 +// https://github.com/dempseyatgithub/BuildSettingExtractor +// + +#include "PaystackiOS-Shared.xcconfig" + + +GCC_PREPROCESSOR_DEFINITIONS = DEBUG=1 $(inherited) diff --git a/Paystack/BuildConfigurations/PaystackiOS-Release.xcconfig b/Paystack/BuildConfigurations/PaystackiOS-Release.xcconfig new file mode 100644 index 0000000..06440f6 --- /dev/null +++ b/Paystack/BuildConfigurations/PaystackiOS-Release.xcconfig @@ -0,0 +1,10 @@ +// +// PaystackiOS-Release.xcconfig +// +// Generated by BuildSettingExtractor on 4/27/15 +// https://github.com/dempseyatgithub/BuildSettingExtractor +// + +#include "PaystackiOS-Shared.xcconfig" + +ENABLE_NS_ASSERTIONS = YES diff --git a/Paystack/BuildConfigurations/PaystackiOS-Shared.xcconfig b/Paystack/BuildConfigurations/PaystackiOS-Shared.xcconfig new file mode 100644 index 0000000..da2d697 --- /dev/null +++ b/Paystack/BuildConfigurations/PaystackiOS-Shared.xcconfig @@ -0,0 +1,167 @@ +// +// PaystackiOS-Shared.xcconfig +// +// Generated by BuildSettingExtractor on 4/27/15 +// https://github.com/dempseyatgithub/BuildSettingExtractor +// + + +CLANG_CXX_LANGUAGE_STANDARD = gnu++0x + + +CLANG_CXX_LIBRARY = libc++ + + +CLANG_ENABLE_MODULES = YES + + +CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR + + +CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR + + +// Code Signing Identity +// +// The name ("common name") of a valid code-signing certificate in a keychain within your +// keychain path. A missing or invalid certificate will cause a build error. + +CODE_SIGN_IDENTITY[sdk=iphoneos*] = iPhone Developer + + +// Current Project Version +// +// This setting defines the the current version of the project. The value must be a +// integer or floating point number like 57 or 365.8. + +CURRENT_PROJECT_VERSION = 1 + + +// Defines Module +// +// If enabled, the product will be treated as defining its own module. This enables +// automatic production of LLVM module map files when appropriate, and allows the product +// to be imported as a module. + +DEFINES_MODULE = YES + + +// Compatibility Version +// +// Determines the compatibility version of the resulting library, bundle, or framework +// binary. + +DYLIB_COMPATIBILITY_VERSION = 1 + + +// Current Library Version +// +// This setting defines the the current version of any framework built by the project. +// Like "Current Project Version", the value must be an integer or floating point number +// like 57 or 365.8. By default it is set to $(CURRENT_PROJECT_VERSION). + +DYLIB_CURRENT_VERSION = 1 + + +// Dynamic Library Install Name Base +// +// Sets the base value for the internal "install path" (LC_ID_DYLIB) in a dynamic +// library. This will be combined with the EXECUTABLE_PATH to form the full install path. +// Setting LD_DYLIB_INSTALL_NAME directly will override this setting. This setting +// defaults to the target's INSTALL_PATH. It is ignored when building any product other +// than a dynamic library. [-install_name] + +DYLIB_INSTALL_NAME_BASE = @rpath + + +GCC_TREAT_WARNINGS_AS_ERRORS = YES + + +GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR + + +GCC_WARN_SHADOW = YES + + +GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE + + +// Info.plist File +// +// This is the project-relative path to the plist file that contains the Info.plist +// information used by bundles. + +INFOPLIST_FILE = Paystack/Info.plist + + +// Installation Directory +// +// The directory to install the build products in. This path is prepended by the +// 'Installation Build Products Location' (i.e., $(DSTROOT)). + +INSTALL_PATH = $(LOCAL_LIBRARY_DIR)/Frameworks + + +// iOS Deployment Target +// +// Code will load on this and later versions of iOS. Framework APIs that are unavailable +// in earlier versions will be weak-linked; your code should check for null function +// pointers or specific system versions before calling newer APIs. +// + +IPHONEOS_DEPLOYMENT_TARGET = 8.0 + + +// Runpath Search Paths +// +// This is a list of paths to be added to the runpath search path list for the image +// being created. At runtime, dyld uses the runpath when searching for dylibs whose load +// path begins with '@rpath/'. [-rpath] + +LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks @loader_path/Frameworks + + +// Product Name +// +// This is the basename of the product generated. + +PRODUCT_NAME = Paystack + + +// Skip Install +// +// Activating this setting when deployment locations are used causes the product to be +// built into an alternative location instead of the install location. + +SKIP_INSTALL = YES + + +// Targeted Device Family +// +// The build system uses the selected device to set the correct value for the +// UIDeviceFamily key it adds to the target's Info.plist file. +// +// iPhone - Application is built for iPhone and iPod touch. +// iPad - Application is built for iPad. +// iPhone/iPad - Application is built Universal for iPhone, iPod touch, and iPad. + +TARGETED_DEVICE_FAMILY = 1,2 + + +// Versioning Name Prefix +// +// Used as a prefix for the name of the version info symbol in the generated versioning +// source file. If you prefix your exported symbols you will probably want to set this +// to the same prefix. + +VERSION_INFO_PREFIX = + + +// Versioning System +// +// Selects the process used for version-stamping generated files. +// +// None - Use no versioning system. [] +// Apple Generic - Use the current project version setting. [apple-generic] + +VERSIONING_SYSTEM = apple-generic diff --git a/Paystack/BuildConfigurations/PaystackiOSStatic.xcconfig b/Paystack/BuildConfigurations/PaystackiOSStatic.xcconfig new file mode 100644 index 0000000..6f61b69 --- /dev/null +++ b/Paystack/BuildConfigurations/PaystackiOSStatic.xcconfig @@ -0,0 +1,99 @@ +// +// PaystackiOSStatic-Shared.xcconfig +// +// Generated by BuildSettingExtractor on 4/27/15 +// https://github.com/dempseyatgithub/BuildSettingExtractor +// + +// Strip Debug Symbols During Copy +// +// Activating this setting causes binary files which are copied during the build (e.g., +// in a Copy Bundle Resources or Copy Files build phase) to be stripped of debugging +// symbols. It does not cause the linked product of a target to be stripped (use Strip +// Linked Product for that). + +COPY_PHASE_STRIP = NO + +CLANG_CXX_LANGUAGE_STANDARD = gnu++0x + + +CLANG_CXX_LIBRARY = libc++ + + +CLANG_ENABLE_MODULES = YES + + +CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR + + +CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR + + +// Dead Code Stripping +// +// Activating this setting causes the -dead_strip flag to be passed to ld(1) via cc(1) to +// turn on dead code stripping. If this option is selected, -gfull (not -gused) must be +// used to generate debugging symbols in order to have them correctly stripped. +// [-dead_strip] + +DEAD_CODE_STRIPPING = NO + + +GCC_TREAT_WARNINGS_AS_ERRORS = YES + + +GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR + + +GCC_WARN_SHADOW = YES + + +GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE + +PSTCK_EXTRA_PREPROCESSOR_MACROS = PSTCK_STATIC_LIBRARY_BUILD + +// iOS Deployment Target +// +// Code will load on this and later versions of iOS. Framework APIs that are unavailable +// in earlier versions will be weak-linked; your code should check for null function +// pointers or specific system versions before calling newer APIs. +// + +IPHONEOS_DEPLOYMENT_TARGET = 7.0 + + +// Other Linker Flags +// +// Options defined in this setting are passed to invocations of the linker. + +OTHER_LDFLAGS = -ObjC + + +// Product Name +// +// This is the basename of the product generated. + +PRODUCT_NAME = Paystack + + +// Skip Install +// +// Activating this setting when deployment locations are used causes the product to be +// built into an alternative location instead of the install location. + +SKIP_INSTALL = YES + + +// Strip Style +// +// Defines the level of symbol stripping to be performed on the linked product of the +// build. The default value is defined by the target's product type. +// +// All Symbols - Completely strips the binary, removing the symbol table and relocation +// information. [all, -s] +// Non-Global Symbols - Strips non-global symbols, but saves external symbols. +// [non-global, -x] +// Debugging Symbols - Strips debugging symbols, but saves local and global symbols. +// [debugging, -S] + +STRIP_STYLE = non-global diff --git a/Paystack/BuildConfigurations/PaystackiOSStaticFramework.xcconfig b/Paystack/BuildConfigurations/PaystackiOSStaticFramework.xcconfig new file mode 100644 index 0000000..a7c222c --- /dev/null +++ b/Paystack/BuildConfigurations/PaystackiOSStaticFramework.xcconfig @@ -0,0 +1,19 @@ +// +// PaystackiOSStaticFramework-Shared.xcconfig +// +// Generated by BuildSettingExtractor on 4/27/15 +// https://github.com/dempseyatgithub/BuildSettingExtractor +// + + +GCC_TREAT_WARNINGS_AS_ERRORS = YES + + +GCC_WARN_SHADOW = YES + + +// Product Name +// +// This is the basename of the product generated. + +PRODUCT_NAME = $(TARGET_NAME) diff --git a/Paystack/BuildConfigurations/Project-Debug.xcconfig b/Paystack/BuildConfigurations/Project-Debug.xcconfig new file mode 100644 index 0000000..919c77d --- /dev/null +++ b/Paystack/BuildConfigurations/Project-Debug.xcconfig @@ -0,0 +1,48 @@ +// +// Project-Debug.xcconfig +// +// Generated by BuildSettingExtractor on 4/27/15 +// https://github.com/dempseyatgithub/BuildSettingExtractor +// + +#include "Project-Shared.xcconfig" + + +// Strip Debug Symbols During Copy +// +// Activating this setting causes binary files which are copied during the build (e.g., +// in a Copy Bundle Resources or Copy Files build phase) to be stripped of debugging +// symbols. It does not cause the linked product of a target to be stripped (use Strip +// Linked Product for that). + +COPY_PHASE_STRIP = NO + + +GCC_DYNAMIC_NO_PIC = NO + + +GCC_OPTIMIZATION_LEVEL = 0 + + +PSTCK_EXTRA_PREPROCESSOR_MACROS= +GCC_PREPROCESSOR_DEFINITIONS = DEBUG=1 $(inherited) $(PSTCK_EXTRA_PREPROCESSOR_MACROS) + + +GCC_SYMBOLS_PRIVATE_EXTERN = NO + + +// Build Active Architecture Only +// +// When checked, only the active architecture is built for faster debugging turnaround + +ONLY_ACTIVE_ARCH = YES + +// iOS Deployment Target +// +// Code will load on this and later versions of iOS. Framework APIs that are unavailable +// in earlier versions will be weak-linked; your code should check for null function +// pointers or specific system versions before calling newer APIs. + +IPHONEOS_DEPLOYMENT_TARGET = 7.0 + +ENABLE_TESTABILITY = YES diff --git a/Paystack/BuildConfigurations/Project-Release.xcconfig b/Paystack/BuildConfigurations/Project-Release.xcconfig new file mode 100644 index 0000000..483967d --- /dev/null +++ b/Paystack/BuildConfigurations/Project-Release.xcconfig @@ -0,0 +1,41 @@ +// +// Project-Release.xcconfig +// +// Generated by BuildSettingExtractor on 4/27/15 +// https://github.com/dempseyatgithub/BuildSettingExtractor +// + +#include "Project-Shared.xcconfig" + + +// Strip Debug Symbols During Copy +// +// Activating this setting causes binary files which are copied during the build (e.g., +// in a Copy Bundle Resources or Copy Files build phase) to be stripped of debugging +// symbols. It does not cause the linked product of a target to be stripped (use Strip +// Linked Product for that). + +COPY_PHASE_STRIP = YES + + +ENABLE_NS_ASSERTIONS = NO + +PSTCK_EXTRA_PREPROCESSOR_MACROS= +GCC_PREPROCESSOR_DEFINITIONS = NDEBUG $(PSTCK_EXTRA_PREPROCESSOR_MACROS) + + +// Validate Built Product +// +// Activating this setting will cause Xcode to perform validation checks on the product +// as part of the build process. + +VALIDATE_PRODUCT = YES + +// iOS Deployment Target +// +// Code will load on this and later versions of iOS. Framework APIs that are unavailable +// in earlier versions will be weak-linked; your code should check for null function +// pointers or specific system versions before calling newer APIs. +// + +IPHONEOS_DEPLOYMENT_TARGET = 8.0 diff --git a/Paystack/BuildConfigurations/Project-Shared.xcconfig b/Paystack/BuildConfigurations/Project-Shared.xcconfig new file mode 100644 index 0000000..39bbea8 --- /dev/null +++ b/Paystack/BuildConfigurations/Project-Shared.xcconfig @@ -0,0 +1,130 @@ +// +// Project-Shared.xcconfig +// +// Generated by BuildSettingExtractor on 4/27/15 +// https://github.com/dempseyatgithub/BuildSettingExtractor +// + + +// Always Search User Paths +// +// If enabled both #include -style and #include "header.h"-style directives +// will search the paths in "User Header Search Paths" before "Header Search Paths", with +// the consequence that user headers (such as your own String.h header) would have +// precedence over system headers when using #include . This is done using the +// -iquote flag for the paths provided in "User Header Search Paths". If disabled and +// your compiler fully supports separate user paths, user headers will only be accessible +// with #include "header.h"-style preprocessor directives. +// +// For backwards compatibility reasons, this setting is enabled by default, but disabling +// it is strongly recommended. + +ALWAYS_SEARCH_USER_PATHS = NO + + +CLANG_ENABLE_OBJC_ARC = YES + + +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES + + +CLANG_WARN_BOOL_CONVERSION = YES + + +CLANG_WARN_CONSTANT_CONVERSION = YES + + +CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES + + +CLANG_WARN_EMPTY_BODY = YES + + +CLANG_WARN_ENUM_CONVERSION = YES + + +CLANG_WARN_INT_CONVERSION = YES + + +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES + + +CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES + + +CLANG_WARN_UNREACHABLE_CODE = YES + + +ENABLE_STRICT_OBJC_MSGSEND = YES + + +GCC_C_LANGUAGE_STANDARD = gnu99 + + +// Compiler for C/C++/Objective-C +// +// The compiler to use for C, C++, and Objective-C. + +GCC_VERSION = com.apple.compilers.llvm.clang.1_0 + + +GCC_WARN_64_TO_32_BIT_CONVERSION = YES + + +GCC_WARN_ABOUT_MISSING_NEWLINE = YES + + +GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES + + +GCC_WARN_ABOUT_RETURN_TYPE = YES + + +GCC_WARN_SIGN_COMPARE = YES + + +GCC_WARN_UNDECLARED_SELECTOR = YES + + +GCC_WARN_UNINITIALIZED_AUTOS = YES + + +GCC_WARN_UNUSED_FUNCTION = YES + + +GCC_WARN_UNUSED_VARIABLE = YES + + +GCC_NO_COMMON_BLOCKS = YES; + +// Precompiled Header Uses Files From Build Directory +// +// This setting allows for better control of sharing precompiled prefix header files +// between projects. By default, Xcode assumes that the prefix header file may include +// header files from the build directory if the build directory is outside of the project +// directory. (Xcode cannot determine this ahead of time since other projects may not +// have been built into the shared build directory at the time the information is +// needed.) +// +// If your prefix file never includes files from the build directory you may set this to +// "NO" to improve sharing of precompiled headers. If the prefix does use files from a +// build directory which is inside your project directory, you may set this to "YES" to +// avoid unintended sharing that may result in build failures. + +PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO + + +// Base SDK +// +// The name or path of the base SDK being used during the build. The product will be +// built against the headers and libraries located inside the indicated SDK. This path +// will be prepended to all search paths, and will be passed through the environment to +// the compiler and linker. Additional SDKs can be specified in the ADDITIONAL_SDKS +// setting. + +SDKROOT = iphoneos + + +WARNING_CFLAGS = -Wall -Wextra -Wundef -Wfloat-equal + +PRODUCT_BUNDLE_IDENTIFIER = com.paystack.$(PRODUCT_NAME:rfc1034identifier) diff --git a/Paystack/ButtonExtension.swift b/Paystack/ButtonExtension.swift new file mode 100644 index 0000000..4e24b8d --- /dev/null +++ b/Paystack/ButtonExtension.swift @@ -0,0 +1,92 @@ +// +// ButtonExtension.swift +// PaystackiOS +// +// Created by Jubril Olambiwonnu on 7/9/20. +// Copyright © 2020 Paystack, Inc. All rights reserved. +// + +import Foundation +import UIKit +import ObjectiveC +// Declare a global var to produce a unique address as the assoc object handle +var disabledColorHandle: UInt8 = 0 +var highlightedColorHandle: UInt8 = 0 +var selectedColorHandle: UInt8 = 0 + +extension UIButton { + private func image(withColor color: UIColor) -> UIImage? { + let rect = CGRect(x: 0.0, y: 0.0, width: 1.0, height: 1.0) + UIGraphicsBeginImageContext(rect.size) + let context = UIGraphicsGetCurrentContext() + + context?.setFillColor(color.cgColor) + context?.fill(rect) + + let image = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + + return image + } + + func setBackgroundColor(_ color: UIColor, for state: UIControl.State) { + self.setBackgroundImage(image(withColor: color), for: state) + } + + @IBInspectable + var disabledColor: UIColor? { + get { + if let color = objc_getAssociatedObject(self, &disabledColorHandle) as? UIColor { + return color + } + return nil + } + set { + if let color = newValue { + self.setBackgroundColor(color, for: .disabled) + objc_setAssociatedObject(self, &disabledColorHandle, color, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } else { + self.setBackgroundImage(nil, for: .disabled) + objc_setAssociatedObject(self, &disabledColorHandle, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } + } + + @IBInspectable + var highlightedColor: UIColor? { + get { + if let color = objc_getAssociatedObject(self, &highlightedColorHandle) as? UIColor { + return color + } + return nil + } + set { + if let color = newValue { + self.setBackgroundColor(color, for: .highlighted) + objc_setAssociatedObject(self, &highlightedColorHandle, color, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } else { + self.setBackgroundImage(nil, for: .highlighted) + objc_setAssociatedObject(self, &highlightedColorHandle, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } + } + + @IBInspectable + var selectedColor: UIColor? { + get { + if let color = objc_getAssociatedObject(self, &selectedColorHandle) as? UIColor { + return color + } + return nil + } + set { + if let color = newValue { + self.setBackgroundColor(color, for: .selected) + objc_setAssociatedObject(self, &selectedColorHandle, color, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } else { + self.setBackgroundImage(nil, for: .selected) + objc_setAssociatedObject(self, &selectedColorHandle, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } + } +} diff --git a/Paystack/Fabric/FABKitProtocol.h b/Paystack/Fabric/FABKitProtocol.h new file mode 100644 index 0000000..53e0656 --- /dev/null +++ b/Paystack/Fabric/FABKitProtocol.h @@ -0,0 +1,46 @@ +// +// FABKitProtocol.h +// +// Copyright (c) 2015 Twitter. All rights reserved. +// + +#import + +/** + * Protocol that a class in a Fabric Kit must conform to to provide information to Fabric at runtime. + */ +@protocol FABKit + +@required + +/** + * Required. The globally unique identifier of the Kit. + * We encourage the use of reverse-DNS notation. + * Example: @"io.fabric.sdk.ios" + */ ++ (NSString *)bundleIdentifier; + +/** + * Required. Must return the current version of the Kit that is being used at runtime. + * We encourage the use of semantic versioning (http://semver.org/), without prefixing the version with a "v". + * This is commonly referred to as the "marketing version". + * Example: @"1.2.3" + */ ++ (NSString *)kitDisplayVersion; + +@optional + +/** + * The build version of the kit. Should be monotonically increasing and unique. + * Example: 137 + */ ++ (NSString *)kitBuildVersion; + +/** + * Perform any necessary initialization. + * This method will be invoked on the Kit when the user calls +[Fabric initializeKits]. + * @note This method being called does not necessarily imply that the developer has started using the Kit yet. + */ ++ (void)initializeIfNeeded; + +@end diff --git a/Paystack/Fabric/Fabric+FABKits.h b/Paystack/Fabric/Fabric+FABKits.h new file mode 100644 index 0000000..927e4da --- /dev/null +++ b/Paystack/Fabric/Fabric+FABKits.h @@ -0,0 +1,25 @@ +// +// Fabric+FABKits.h +// +// Copyright (c) 2015 Twitter. All rights reserved. +// + +#import "Fabric.h" + +@protocol FABKit; +// Use this category for methods that kits can call on Fabric. +@interface Fabric (FABKits) + +/** + * Returns a dictionary containing the kit configuration info for the provided kit. + * The configuration information is parsed from the application's Info.plist. This + * method is primarily intended to be used by kits to retrieve their configuration. + * + * @param kitClass The class of the kit whose configuration should be returned. + * It should conform to the FABKit protocol. + * + * @return A dictionary containing kit specific configuration information or nil if none exists. + */ ++ (nonnull NSDictionary *)configurationDictionaryForKitClass:(nonnull Class)kitClass; + +@end diff --git a/Paystack/Fabric/Fabric.h b/Paystack/Fabric/Fabric.h new file mode 100644 index 0000000..760aa76 --- /dev/null +++ b/Paystack/Fabric/Fabric.h @@ -0,0 +1,53 @@ +// +// Fabric.h +// +// Copyright (c) 2015 Twitter. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * Fabric Base. Coordinates configuration and starts all provided kits. + */ +@interface Fabric : NSObject + +/** + * Initialize Fabric and all provided kits. Call this method within your App Delegate's `application:didFinishLaunchingWithOptions:` and provide the kits you wish to use. + * + * For example, in Objective-C: + * + * `[Fabric with:@[[Crashlytics class], [Twitter class], [Digits class], [MoPub class]]];` + * + * Swift: + * + * `Fabric.with([Crashlytics.self(), Twitter.self(), Digits.self(), MoPub.self()])` + * + * Only the first call to this method is honored. Subsequent calls are no-ops. + * + * @param kits An array of kit Class objects + * + * @return Returns the shared Fabric instance. In most cases this can be ignored. + */ ++ (instancetype)with:(NSArray *)kitClasses; + +/** + * Returns the Fabric singleton object. + */ ++ (instancetype)sharedSDK; + +/** + * This BOOL enables or disables debug logging, such as kit version information. The default value is NO. + */ +@property (nonatomic, assign) BOOL debug; + +/** + * Unavailable. Use `+sharedSDK` to retrieve the shared Fabric instance. + */ +- (id)init __attribute__((unavailable("Use +sharedSDK to retrieve the shared Fabric instance."))); + +@end + +NS_ASSUME_NONNULL_END + diff --git a/Paystack/KeyboardHandlingVC.swift b/Paystack/KeyboardHandlingVC.swift new file mode 100644 index 0000000..820c88c --- /dev/null +++ b/Paystack/KeyboardHandlingVC.swift @@ -0,0 +1,84 @@ +// +// KeyboardHandlingVC.swift +// PaystackiOS +// +// Created by Jubril Olambiwonnu on 7/9/20. +// Copyright © 2020 Paystack, Inc. All rights reserved. +// + +import UIKit + +public class PSTCKKeyboardHandlingBaseVC: UIViewController { + + @IBOutlet weak var backgroundSV: UIScrollView! + + public override func viewDidLoad() { + super.viewDidLoad() + + subscribeToNotification(UIResponder.keyboardWillShowNotification, selector: #selector(keyboardWillShowOrHide)) + subscribeToNotification(UIResponder.keyboardWillHideNotification, selector: #selector(keyboardWillShowOrHide)) + + initializeHideKeyboard() + + } + + public override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + unsubscribeFromAllNotifications() + } + +} + +// MARK : Keyboard Dismissal Handling on Tap +private extension PSTCKKeyboardHandlingBaseVC { + + func initializeHideKeyboard(){ + let tap: UITapGestureRecognizer = UITapGestureRecognizer( + target: self, + action: #selector(dismissMyKeyboard)) + + view.addGestureRecognizer(tap) + } + + @objc func dismissMyKeyboard(){ + view.endEditing(true) + } +} + +// MARK : Textfield Visibility Handling with Scroll +private extension PSTCKKeyboardHandlingBaseVC { + + func subscribeToNotification(_ notification: NSNotification.Name, selector: Selector) { + NotificationCenter.default.addObserver(self, selector: selector, name: notification, object: nil) + } + + func unsubscribeFromAllNotifications() { + NotificationCenter.default.removeObserver(self) + } + + @objc func keyboardWillShowOrHide(notification: NSNotification) { + + // Pull a bunch of info out of the notification + if let scrollView = backgroundSV, let userInfo = notification.userInfo, let endValue = userInfo[UIResponder.keyboardFrameEndUserInfoKey], let durationValue = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey], let curveValue = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] { + + // Transform the keyboard's frame into our view's coordinate system + let endRect = view.convert((endValue as AnyObject).cgRectValue, from: view.window) + + // Find out how much the keyboard overlaps the scroll userInfoview + // We can do this because our scroll view's frame is already in our view's coordinate system + let keyboardOverlap = scrollView.frame.maxY - endRect.origin.y + + // Set the scroll view's content inset to avoid the keyboard + // Don't forget the scroll indicator too! + scrollView.contentInset.bottom = keyboardOverlap + scrollView.scrollIndicatorInsets.bottom = keyboardOverlap + + let duration = (durationValue as AnyObject).doubleValue + let options = UIView.AnimationOptions(rawValue: UInt((curveValue as AnyObject).integerValue << 16)) + UIView.animate(withDuration: duration!, delay: 0, options: options, animations: { + self.view.layoutIfNeeded() + }, completion: nil) + } + } + +} diff --git a/Paystack/NSDictionary+Paystack.h b/Paystack/NSDictionary+Paystack.h new file mode 100644 index 0000000..7661e7a --- /dev/null +++ b/Paystack/NSDictionary+Paystack.h @@ -0,0 +1,14 @@ +// +// NSDictionary+Paystack.h +// Paystack +// + +#import + +@interface NSDictionary (Paystack) + +- (nullable NSDictionary *)pstck_dictionaryByRemovingNullsValidatingRequiredFields:(nonnull NSArray *)requiredFields; + +@end + +void linkDictionaryCategory(void); diff --git a/Paystack/NSDictionary+Paystack.m b/Paystack/NSDictionary+Paystack.m new file mode 100644 index 0000000..bd407bd --- /dev/null +++ b/Paystack/NSDictionary+Paystack.m @@ -0,0 +1,27 @@ +// +// NSDictionary+Paystack.m +// Paystack +// + +#import "NSDictionary+Paystack.h" + +@implementation NSDictionary (Paystack) + +- (nullable NSDictionary *)pstck_dictionaryByRemovingNullsValidatingRequiredFields:(nonnull NSArray *)requiredFields { + NSMutableDictionary *dict = [NSMutableDictionary dictionary]; + [self enumerateKeysAndObjectsUsingBlock:^(id key, id obj, __unused BOOL *stop) { + if (obj != [NSNull null]) { + dict[key] = obj; + } + }]; + for (NSString *key in requiredFields) { + if (![[dict allKeys] containsObject:key]) { + return nil; + } + } + return [dict copy]; +} + +@end + +void linkDictionaryCategory(void){} diff --git a/Paystack/PSTCKAPIClient+Private.h b/Paystack/PSTCKAPIClient+Private.h new file mode 100644 index 0000000..9e0377f --- /dev/null +++ b/Paystack/PSTCKAPIClient+Private.h @@ -0,0 +1,14 @@ +// +// PSTCKAPIClient+Private.h +// Paystack +// +// Copyright © 2016 Paystack, Inc. +// + +#import + +@interface PSTCKAPIClient () + +@property (nonatomic, readwrite, nonnull) NSURL *apiURL; +@property (nonatomic, readwrite, nonnull) NSURLSession *urlSession; +@end diff --git a/Paystack/PSTCKAPIClient.m b/Paystack/PSTCKAPIClient.m new file mode 100644 index 0000000..ce8958a --- /dev/null +++ b/Paystack/PSTCKAPIClient.m @@ -0,0 +1,634 @@ +// +// PSTCKAPIClient.m +// PaystackExample +// + +#import "TargetConditionals.h" +#if TARGET_OS_IPHONE +#import +#import +#import +#endif + +#import "PSTCKAPIClient.h" +#import "PSTCKFormEncoder.h" +#import "PSTCKCard.h" +#import "PSTCKRSA.h" +#import "PSTCKCardValidator.h" +#import "PSTCKToken.h" +#import "PSTCKTransaction.h" +#import "PSTCKValidationParams.h" +#import "PaystackError.h" +#import "PSTCKAPIResponseDecodable.h" +#import "PSTCKAuthViewController.h" +#import "PSTCKAPIPostRequest.h" +#import + +#if __has_include("Fabric.h") +#import "Fabric+FABKits.h" +#import "FABKitProtocol.h" +#endif + +#ifdef PSTCK_STATIC_LIBRARY_BUILD +#import "PSTCKCategoryLoader.h" +#endif + +#define FAUXPAS_IGNORED_IN_METHOD(...) + +static NSString *const apiURLBase = @"standard.paystack.co"; +static NSString *const chargeEndpoint = @"charge/mobile_charge"; +static NSString *const avsEndpoint = @"charge/avs"; +static NSString *const validateEndpoint = @"charge/validate"; +static NSString *const requeryEndpoint = @"charge/requery/"; +static NSString *const paystackAPIVersion = @"2017-05-25"; +static NSString *PSTCKDefaultPublicKey; +static Boolean PROCESSING = false; + +@implementation Paystack + ++ (id)alloc { + NSCAssert(NO, @"'Paystack' is a static class and cannot be instantiated."); + return nil; +} + ++ (void)setDefaultPublicKey:(NSString *)publicKey { + PSTCKDefaultPublicKey = publicKey; +} + ++ (NSString *)defaultPublicKey { + return PSTCKDefaultPublicKey; +} + +@end + +#if __has_include("Fabric.h") +@interface PSTCKAPIClient () +#else +@interface PSTCKAPIClient() +#endif +@property (nonatomic, readwrite) NSURL *apiURL; +@property (nonatomic, readwrite) NSURLSession *urlSession; +@end + +@interface PSTCKServerTransaction : NSObject + +@property (nonatomic, readwrite, nullable) NSString *id; +@property (nonatomic, readwrite, nullable) NSString *reference; + +@end +@implementation PSTCKServerTransaction +- (instancetype)init { + _id = nil; + _reference = nil; + + return self; +} +@end + +@interface PSTCKAPIClient () + +@property(nonatomic, strong) UIViewController *viewController; +@property(nonatomic, strong) PSTCKServerTransaction *serverTransaction; +@property(nonatomic, retain) PSTCKCardParams *card; +@property(nonatomic, retain) PSTCKTransactionParams *transaction; +@property(nonatomic, copy) PSTCKErrorCompletionBlock errorCompletion; +@property(nonatomic, copy) PSTCKTransactionCompletionBlock beforeValidateCompletion; +@property(nonatomic, copy) PSTCKNotifyCompletionBlock showingDialogCompletion; +@property(nonatomic, copy) PSTCKNotifyCompletionBlock dialogDismissedCompletion; +@property(nonatomic, copy) PSTCKTransactionCompletionBlock successCompletion; + +@property int INVALID_DATA_SENT_RETRIES; +@end + +@implementation PSTCKAPIClient + +#ifdef PSTCK_STATIC_LIBRARY_BUILD ++ (void)initialize { + [PSTCKCategoryLoader loadCategories]; +} +#endif + ++ (instancetype)sharedClient { + static id sharedClient; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ sharedClient = [[self alloc] init]; }); + return sharedClient; +} + +- (instancetype)init { + return [self initWithPublicKey:[Paystack defaultPublicKey]]; +} + +- (instancetype)initWithPublicKey:(NSString *)publicKey { + self = [super init]; + if (self) { + [self.class validateKey:publicKey]; + _apiURL = [NSURL URLWithString:[NSString stringWithFormat:@"https://%@", apiURLBase]]; + _publicKey = [publicKey copy]; + _operationQueue = [NSOperationQueue mainQueue]; + NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; + NSString *auth = [@"Bearer " stringByAppendingString:self.publicKey]; + config.HTTPAdditionalHeaders = @{ + @"X-Paystack-User-Agent": [self.class paystackUserAgentDetails], + @"Paystack-Version": paystackAPIVersion, + @"Authorization": auth, + @"X-Paystack-Build": PSTCKSDKBuild, + }; + _urlSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:_operationQueue]; + } + return self; +} + + + +- (void)setOperationQueue:(NSOperationQueue *)operationQueue { + NSCAssert(operationQueue, @"Operation queue cannot be nil."); + _operationQueue = operationQueue; +} + +#pragma mark - private helpers + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-variable" ++ (void)validateKey:(NSString *)publicKey { + NSCAssert(publicKey != nil && ![publicKey isEqualToString:@""], + @"You must use a valid public key to charge a card."); + BOOL secretKey = [publicKey hasPrefix:@"sk_"]; + NSCAssert(!secretKey, + @"You are using a secret key to charge the card, instead of the public one."); +#ifndef DEBUG + if ([publicKey.lowercaseString hasPrefix:@"pk_test"]) { + FAUXPAS_IGNORED_IN_METHOD(NSLogUsed); + NSLog(@"⚠️ Warning! You're building your app in a non-debug configuration, but appear to be using your Paystack test key. Make sure not to submit to " + @"the App Store with your test keys!⚠️"); + } +#endif +} +#pragma clang diagnostic pop + +#pragma mark Utility methods - + ++ (NSString *)device_id { + return [@"iossdk_" stringByAppendingString:[[[UIDevice currentDevice] identifierForVendor] UUIDString]]; +} + ++ (NSString *)paystackUserAgentDetails { + NSMutableDictionary *details = [@{ + @"lang": @"objective-c", + @"bindings_version": PSTCKSDKVersion, + } mutableCopy]; +#if TARGET_OS_IPHONE + NSString *version = [UIDevice currentDevice].systemVersion; + if (version) { + details[@"os_version"] = version; + } + struct utsname systemInfo; + uname(&systemInfo); + NSString *deviceType = @(systemInfo.machine); + if (deviceType) { + details[@"type"] = deviceType; + } + NSString *model = [UIDevice currentDevice].localizedModel; + if (model) { + details[@"model"] = model; + } + if ([[UIDevice currentDevice] respondsToSelector:@selector(identifierForVendor)]) { + NSString *vendorIdentifier = [[[UIDevice currentDevice] performSelector:@selector(identifierForVendor)] performSelector:@selector(UUIDString)]; + if (vendorIdentifier) { + details[@"vendor_identifier"] = vendorIdentifier; + } + } +#endif + return [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:[details copy] options:0 error:NULL] encoding:NSUTF8StringEncoding]; +} + +#pragma mark Fabric +#if __has_include("Fabric.h") + ++ (NSString *)bundleIdentifier { + return @"com.paystack.paystack-ios"; +} + ++ (NSString *)kitDisplayVersion { + return PSTCKSDKVersion; +} + ++ (void)initializeIfNeeded { + Class fabric = NSClassFromString(@"Fabric"); + if (fabric) { + // The app must be using Fabric, as it exists at runtime. We fetch our default public key from Fabric. + NSDictionary *fabricConfiguration = [fabric configurationDictionaryForKitClass:[PSTCKAPIClient class]]; + NSString *publicKey = fabricConfiguration[@"public"]; + if (!publicKey) { + NSLog(@"Configuration dictionary returned by Fabric was nil, or doesn't have publicKey. Can't initialize Paystack."); + return; + } + [self validateKey:publicKey]; + [Paystack setDefaultPublicKey:publicKey]; + } else { + NSCAssert(fabric, @"initializeIfNeeded method called from a project that doesn't have Fabric."); + } +} + +#endif + +@end + +typedef NS_ENUM(NSInteger, PSTCKChargeStage) { + PSTCKChargeStageNoHandle, + PSTCKChargeStagePlusHandle, + PSTCKChargeStageValidateToken, + PSTCKChargeStageRequery, + PSTCKChargeStageAuthorize, + PSTCKChargeStageAVS, +}; + + +#pragma mark - Credit Cards +@implementation PSTCKAPIClient (CreditCards) + +- (void)chargeCard:(nonnull PSTCKCardParams *)card + forTransaction:(nonnull PSTCKTransactionParams *)transaction + onViewController:(nonnull UIViewController *)viewController + didEndWithError:(nonnull PSTCKErrorCompletionBlock)errorCompletion +didTransactionSuccess:(nonnull PSTCKTransactionCompletionBlock)successCompletion { + NSCAssert(card != nil, @"'card' is required for a charge"); + NSCAssert(errorCompletion != nil, @"'errorCompletion' is required to handle any errors encountered while charging"); + NSCAssert(viewController != nil, @"'viewController' is required to show any alerts that may be needed"); + NSCAssert(transaction != nil, @"'transaction' is required so we may know who to charge"); + NSCAssert(successCompletion != nil, @"'successCompletion' is required so you can continue the process after charge succeeds. Remember to verify on server before giving value."); + [self startWithCard:card forTransaction:transaction onViewController:viewController didEndWithError:errorCompletion didTransactionSuccess:successCompletion]; + + if(PROCESSING){ + [self didEndWithProcessingError]; + return; + } + PROCESSING = YES; + self.INVALID_DATA_SENT_RETRIES = 0; + NSData *data = [PSTCKFormEncoder formEncryptedDataForCard:card + andTransaction:transaction + usePublicKey:[self publicKey] + onThisDevice:[self.class device_id]]; + + [self makeChargeRequest:data atStage:PSTCKChargeStageNoHandle]; +} + +- (void)setProcessingStatus:(Boolean)status { + PROCESSING=status; +} + +- (void)chargeCard:(nonnull PSTCKCardParams *)card + forTransaction:(nonnull PSTCKTransactionParams *)transaction + onViewController:(nonnull UIViewController *)viewController + didEndWithError:(nonnull PSTCKErrorCompletionBlock)errorCompletion +didRequestValidation:(nonnull PSTCKTransactionCompletionBlock)beforeValidateCompletion + willPresentDialog:(nonnull PSTCKNotifyCompletionBlock)showingDialogCompletion + dismissedDialog:(nonnull PSTCKNotifyCompletionBlock)dialogDismissedCompletion +didTransactionSuccess:(nonnull PSTCKTransactionCompletionBlock)successCompletion { + self.beforeValidateCompletion = beforeValidateCompletion; + self.showingDialogCompletion = showingDialogCompletion; + self.dialogDismissedCompletion = dialogDismissedCompletion; + [self chargeCard:card forTransaction:transaction onViewController:viewController didEndWithError:errorCompletion didTransactionSuccess:successCompletion]; + +} + +- (void)chargeCard:(nonnull PSTCKCardParams *)card + forTransaction:(nonnull PSTCKTransactionParams *)transaction + onViewController:(nonnull UIViewController *)viewController + didEndWithError:(nonnull PSTCKErrorCompletionBlock)errorCompletion + willPresentDialog:(nonnull PSTCKNotifyCompletionBlock)showingDialogCompletion + dismissedDialog:(nonnull PSTCKNotifyCompletionBlock)dialogDismissedCompletion +didTransactionSuccess:(nonnull PSTCKTransactionCompletionBlock)successCompletion { + self.showingDialogCompletion = showingDialogCompletion; + self.dialogDismissedCompletion = dialogDismissedCompletion; + [self chargeCard:card forTransaction:transaction onViewController:viewController didEndWithError:errorCompletion didTransactionSuccess:successCompletion]; + +} + +- (void)chargeCard:(nonnull PSTCKCardParams *)card + forTransaction:(nonnull PSTCKTransactionParams *)transaction + onViewController:(nonnull UIViewController *)viewController + didEndWithError:(nonnull PSTCKErrorCompletionBlock)errorCompletion +didRequestValidation:(nonnull PSTCKTransactionCompletionBlock)beforeValidateCompletion +didTransactionSuccess:(nonnull PSTCKTransactionCompletionBlock)successCompletion { + self.beforeValidateCompletion = beforeValidateCompletion; + [self chargeCard:card forTransaction:transaction onViewController:viewController didEndWithError:errorCompletion didTransactionSuccess:successCompletion]; + +} + +- (void)startWithCard:(nonnull PSTCKCardParams *)card + forTransaction:(nonnull PSTCKTransactionParams *)transaction + onViewController:(nonnull UIViewController *)viewController + didEndWithError:(nonnull PSTCKErrorCompletionBlock)errorCompletion +didTransactionSuccess:(nonnull PSTCKTransactionCompletionBlock)successCompletion { + self.card = card; + self.transaction = transaction; + self.viewController = viewController; + self.errorCompletion = errorCompletion; + self.successCompletion = successCompletion; + self.serverTransaction = [PSTCKServerTransaction new]; + +} + + + +- (void) makeChargeRequest:(NSData *)data + atStage:(PSTCKChargeStage) stage + +{ + NSString *endpoint; + NSString *httpMethod; + + switch (stage){ + case PSTCKChargeStageNoHandle: + case PSTCKChargeStagePlusHandle: + endpoint = chargeEndpoint; + httpMethod = @"POST"; + break; + case PSTCKChargeStageValidateToken: + endpoint = validateEndpoint; + httpMethod = @"POST"; + break; + case PSTCKChargeStageRequery: + case PSTCKChargeStageAuthorize: + endpoint = [requeryEndpoint stringByAppendingString:self.serverTransaction.id] ; + httpMethod = @"GET"; + break; + case PSTCKChargeStageAVS: + endpoint = avsEndpoint; + httpMethod = @"POST"; + break; + } + + [PSTCKAPIPostRequest + startWithAPIClient:self + endpoint:endpoint + method:httpMethod + postData:data + serializer:[PSTCKTransaction new] + completion:^(PSTCKTransaction * _Nullable responseObject, NSError * _Nullable error){ + if((responseObject != nil) && ([responseObject trans] != nil)){ + self.serverTransaction.id = [responseObject trans]; + } + if((responseObject != nil) && ([responseObject reference] != nil)){ + self.serverTransaction.reference = [responseObject reference]; + } + if(error != nil){ + [self didEndWithError:error]; + return; + } + if([[responseObject message].lowercaseString isEqual:@"invalid data sent"] && self.INVALID_DATA_SENT_RETRIES<3){ + self.INVALID_DATA_SENT_RETRIES = self.INVALID_DATA_SENT_RETRIES+1; + [self makeChargeRequest:data + atStage:stage]; + return; + } + if([[responseObject message].lowercaseString isEqual:@"access code has expired"] && [[responseObject status] isEqual:@"0"]){ + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: PSTCKExpiredAccessCodeErrorMessage, + PSTCKErrorMessageKey: PSTCKExpiredAccessCodeErrorMessage + }; + [self didEndWithError:[[NSError alloc] initWithDomain:PaystackDomain code:PSTCKExpiredAccessCodeError userInfo:userInfo]]; + return; + } + [self handleResponse:responseObject]; + }]; +} + +- (void) requestPin{ + [self notifyShowingDialog]; + UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Enter CARD PIN" + message:@"To confirm that you are the owner of this card please enter your card PIN" + preferredStyle:UIAlertControllerStyleAlert]; + + UIAlertAction* defaultAction = [UIAlertAction + actionWithTitle:@"Continue" style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { + [action isEnabled]; // Just to avoid Unused error + [self notifyDialogDismissed]; + NSString *provided = ((UITextField *)[alert.textFields objectAtIndex:0]).text; + NSString *handle = [PSTCKCardValidator sanitizedNumericStringForString:provided]; + if(handle == nil || + [handle length]!=4 || + ([provided length] != [handle length])){ + [self didEndWithErrorMessage:@"Invalid PIN provided. Expected exactly 4 digits."]; + return; + } + NSData *hdata = [PSTCKFormEncoder formEncryptedDataForCard:self.card + andTransaction:self.transaction + andHandle:[PSTCKRSA encryptRSA:handle] + usePublicKey:[self publicKey] + onThisDevice:[self.class device_id]]; + [self makeChargeRequest:hdata + atStage:PSTCKChargeStagePlusHandle]; + + }]; + + [alert addTextFieldWithConfigurationHandler:^(UITextField *textField) { + textField.placeholder = @"****"; + textField.clearButtonMode = UITextFieldViewModeWhileEditing; + textField.secureTextEntry = YES; + }]; + + [alert addAction:defaultAction]; + [self.viewController presentViewController:alert animated:YES completion:nil]; +} + +- (void) requestAVS:(NSArray*) states { + [self notifyShowingDialog]; + [self notifyBeforeValidate]; + PSTCKAddressViewController* avsVC = [[PSTCKAddressViewController alloc] initWithNibName: @"AddressViewController" bundle:[NSBundle bundleForClass:[self class]]]; + avsVC.transaction = self.serverTransaction.id; + avsVC.didCollectAddress = ^ (NSDictionary * _Nonnull address) { + [self notifyDialogDismissed]; + NSData *data = [PSTCKFormEncoder formEncryptedDataForDict:address + usePublicKey:[self publicKey] + onThisDevice:[self.class device_id]]; + [self makeChargeRequest:data + atStage:PSTCKChargeStageAVS]; + }; + avsVC.didTapCancelButton = ^{ + [self notifyDialogDismissed]; + [self didEndWithErrorMessage:@"Could not complete charge because billing information is missing"]; + }; + avsVC.states = states; + [self.viewController presentViewController:avsVC animated:YES completion:nil]; +} + +- (void) requestAuth:(NSString * _Nonnull) url{ + [self notifyShowingDialog]; + [self notifyBeforeValidate]; + PSTCKAuthViewController* authorizer = [[[PSTCKAuthViewController alloc] init] + initWithURL:[NSURL URLWithString:url] + handler:^{ + [self.viewController dismissViewControllerAnimated:YES completion:nil]; + [self notifyDialogDismissed]; + [self makeChargeRequest:nil + atStage:PSTCKChargeStageRequery]; + }]; + UINavigationController *nc = [[UINavigationController alloc] initWithRootViewController:authorizer]; + if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) { + nc.modalPresentationStyle = UIModalPresentationFormSheet; + } + + [self.viewController presentViewController:nc animated:YES completion:nil]; +} + +- (void) requestOtp:(NSString * _Nonnull) otpmessage{ + [self notifyShowingDialog]; + [self notifyBeforeValidate]; + UIAlertController* tkalert = [UIAlertController alertControllerWithTitle:@"Authentication required" + message:otpmessage + preferredStyle:UIAlertControllerStyleAlert]; + + UIAlertAction* tkdefaultAction = [UIAlertAction + actionWithTitle:@"Continue" style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { + [action isEnabled]; // Just to avoid Unused error + [self notifyDialogDismissed]; + NSString *provided = ((UITextField *)[tkalert.textFields objectAtIndex:0]).text; + PSTCKValidationParams *validateParams = [PSTCKValidationParams alloc]; + validateParams.trans = self.serverTransaction.id; + validateParams.token = provided; + NSData *vdata = [PSTCKFormEncoder formEncodedDataForObject:validateParams + usePublicKey:[self publicKey] + onThisDevice:[self.class device_id]]; + [self makeChargeRequest:vdata + atStage:PSTCKChargeStageValidateToken]; + + }]; + + [tkalert addTextFieldWithConfigurationHandler:^(UITextField *textField) { + textField.placeholder = @"_____"; + textField.clearButtonMode = UITextFieldViewModeWhileEditing; + }]; + [tkalert addAction:tkdefaultAction]; + [self.viewController presentViewController:tkalert animated:YES completion:nil]; +} + +- (void) handleResponse:(PSTCKTransaction * _Nonnull)responseObject{ + if ([responseObject errors] != nil) { + [self didEndWithErrorMessage: [responseObject message]]; + return; + } + if([[responseObject status] isEqual:@"1"] || [[responseObject status] isEqual:@"success"]){ + [self didEndSuccessfully]; + return; + } + else if([[responseObject status] isEqual:@"2"] && [[responseObject auth].lowercaseString isEqual:@"avs"]){ + [self fetchStatesWithCountry:responseObject.countrycode completion: ^( NSArray * _Nonnull states, NSError * _Nullable error) { + if(error != NULL) { + [self didEndWithError:error]; + } + else { + dispatch_async(dispatch_get_main_queue(), ^{ + [self requestAVS:states]; + }); + } + }]; + return; + } else if([[responseObject status] isEqual:@"2"] || [[responseObject auth].lowercaseString isEqual:@"pin"]){ + [self requestPin]; + return; + } else if([self.serverTransaction id] != nil){ + if([[responseObject auth].lowercaseString isEqual:@"3ds"] && [self validUrl:[responseObject otpmessage]]){ + [self requestAuth:[responseObject otpmessage]]; + return; + } else if([[responseObject status] isEqual:@"3"] + || ([[responseObject auth].lowercaseString isEqual:@"otp"] && [responseObject otpmessage] != nil) + || ([[responseObject auth].lowercaseString isEqual:@"phone"] && [responseObject otpmessage] != nil)){ + [self requestOtp:([responseObject otpmessage] != nil ? [responseObject otpmessage] : [responseObject message])]; + return; + } else if([[responseObject status].lowercaseString isEqual:@"requery"]) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC), + dispatch_get_main_queue(), ^{ + [self.operationQueue addOperationWithBlock:^{ + [self makeChargeRequest:nil + atStage:PSTCKChargeStageRequery]; + }]; + }); + return; + } + } + + if([[responseObject status] isEqual:@"0"] || [[responseObject status] isEqual:@"error"] || [[responseObject status] isEqual:@"timeout"]){ + [self didEndWithErrorMessage:[responseObject message]]; + } else { + // this is an invalid status + [self didEndWithErrorMessage:[@"The response status from Paystack had an unknown status. Status was: " stringByAppendingString:[responseObject status]]]; + } +} + +- (Boolean) validUrl:(NSString *) candidate{ + NSURL *candidateURL = [NSURL URLWithString:candidate]; + // WARNING > "test" is an URL according to RFCs, being just a path + // so you still should check scheme and all other NSURL attributes you need + if (candidateURL && candidateURL.scheme && candidateURL.host) { + // candidate is a well-formed url with: + // - a scheme (like http://) + // - a host (like stackoverflow.com) + return YES; + } + return NO; +} + +- (void)didEndWithError:(NSError *)error{ + PROCESSING=NO; + [self.operationQueue addOperationWithBlock:^{ + self.errorCompletion(error, self.serverTransaction.reference); + }]; +} + +- (void)didEndSuccessfully{ + PROCESSING=NO; + [self.operationQueue addOperationWithBlock:^{ + self.successCompletion(self.serverTransaction.reference); + }]; +} + +- (void)notifyShowingDialog{ + if(self.showingDialogCompletion == NULL){ + return; + } + [self.operationQueue addOperationWithBlock:^{ + self.showingDialogCompletion(); + }]; +} +- (void)notifyDialogDismissed{ + if(self.dialogDismissedCompletion == NULL){ + return; + } + [self.operationQueue addOperationWithBlock:^{ + self.dialogDismissedCompletion(); + }]; +} +- (void)notifyBeforeValidate{ + if(self.beforeValidateCompletion == NULL){ + return; + } + [self.operationQueue addOperationWithBlock:^{ + self.beforeValidateCompletion(self.serverTransaction.reference); + }]; +} + + +- (void)didEndWithErrorMessage:(NSString *)errorString{ + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: errorString, + PSTCKErrorMessageKey: errorString + }; + PROCESSING=NO; + [self didEndWithError:[[NSError alloc] initWithDomain:PaystackDomain code:PSTCKCardErrorProcessingError userInfo:userInfo]]; +} + +- (void)didEndWithProcessingError{ + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: PSTCKCardErrorProcessingTransactionMessage, + PSTCKErrorMessageKey: PSTCKCardErrorProcessingTransactionMessage + }; + [self.operationQueue addOperationWithBlock:^{ + self.errorCompletion([[NSError alloc] initWithDomain:PaystackDomain code:PSTCKConflictError userInfo:userInfo], self.serverTransaction.reference); + }]; +} + +@end diff --git a/Paystack/PSTCKAPIClientExtension.swift b/Paystack/PSTCKAPIClientExtension.swift new file mode 100644 index 0000000..6138f70 --- /dev/null +++ b/Paystack/PSTCKAPIClientExtension.swift @@ -0,0 +1,73 @@ +// +// PSTCKAPIClientExtension.swift +// PaystackiOS +// +// Created by Jubril Olambiwonnu on 6/19/20. +// Copyright © 2020 Paystack, Inc. All rights reserved. +// + +import Foundation + +@objc extension PSTCKAPIClient { + + public func fetchStates(country: String, completion: @escaping ([PSTCKState], Error?) -> Void) { + let url = URL(string: "https://api.paystack.co/address_verification/states?country=\(country)")! + var request = URLRequest(url: url) + request.httpMethod = "GET" + URLSession.shared.dataTask(with: request, completionHandler: { data, response, error in + guard let data = data, error == nil else { + completion([PSTCKState](), error) + return + } + let responseJSON = try? JSONSerialization.jsonObject(with: data, options: []) + if let responseJSON = responseJSON as? [String: Any] { + guard responseJSON["status"] as? Bool == true else { + completion([PSTCKState](), StringError(responseJSON["message"] as? String ?? "Could not fetch issuing country states")) + return + } + if let data = responseJSON["data"] as? [[String : Any]] { + let states = data.compactMap{PSTCKState(dict: $0)} + completion(states, nil) + } + } + }).resume() + } +} + + +@objc public class PSTCKState: NSObject { + public var name: String + public var abbreviation: String + + init(name: String, abb: String) { + self.name = name + self.abbreviation = abb + } + + init?(dict: [String : Any]) { + if let name = dict["name"] as? String, let abb = dict["abbreviation"] as? String { + self.name = name + self.abbreviation = abb + return + } + return nil + } +} + +struct StringError : LocalizedError { + var errorDescription: String? { return errorMessage } + var failureReason: String? { return errorMessage } + var recoverySuggestion: String? { return "" } + var helpAnchor: String? { return "" } + + private var errorMessage : String + + init(_ description: String) + { + errorMessage = description + } +} + + + + diff --git a/Paystack/PSTCKAPIPostRequest.h b/Paystack/PSTCKAPIPostRequest.h new file mode 100644 index 0000000..affa373 --- /dev/null +++ b/Paystack/PSTCKAPIPostRequest.h @@ -0,0 +1,19 @@ +// +// PSTCKAPIPostRequest.h +// Paystack +// + +#import +#import "PSTCKAPIResponseDecodable.h" +@class PSTCKAPIClient; + +@interface PSTCKAPIPostRequest<__covariant ResponseType:id> : NSObject + ++ (void)startWithAPIClient:(PSTCKAPIClient *)apiClient + endpoint:(NSString *)endpoint + method:(NSString *)httpMethod + postData:(NSData *)postData + serializer:(ResponseType)serializer + completion:(void (^)(ResponseType object, NSError *error))completion; + +@end diff --git a/Paystack/PSTCKAPIPostRequest.m b/Paystack/PSTCKAPIPostRequest.m new file mode 100644 index 0000000..32fcf53 --- /dev/null +++ b/Paystack/PSTCKAPIPostRequest.m @@ -0,0 +1,54 @@ +// +// PSTCKAPIPostRequest.m +// Paystack +// + +#import "PSTCKAPIPostRequest.h" +#import "PSTCKAPIClient.h" +#import "PSTCKAPIClient+Private.h" +#import "PaystackError.h" + +@implementation PSTCKAPIPostRequest + ++ (void)startWithAPIClient:(PSTCKAPIClient *)apiClient + endpoint:(NSString *)endpoint + method:(NSString *)httpMethod + postData:(NSData *)postData + serializer:(id)serializer + completion:(void (^)(id, NSError *))completion { + + NSURL *url = [apiClient.apiURL URLByAppendingPathComponent:endpoint]; + NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; + request.HTTPMethod = httpMethod; // @"POST" + request.HTTPBody = postData; +// NSLog(@"%@",postData); + + [[apiClient.urlSession dataTaskWithRequest:request completionHandler:^(NSData * _Nullable body, __unused NSURLResponse * _Nullable response, NSError * _Nullable error) { + NSError *someerror; + NSDictionary *jsonDictionary = body ? [NSJSONSerialization JSONObjectWithData:body options:NSJSONReadingAllowFragments error:&someerror] : nil; + NSString *bodyString = [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding]; + id responseObject = [[serializer class] decodedObjectFromAPIResponse:jsonDictionary]; + NSError *returnedError = error; + if (!responseObject && !returnedError) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: @"The response from Paystack failed to get parsed into valid JSON", + PSTCKErrorMessageKey: [@"The response from Paystack failed to get parsed into valid JSON. Response was: " stringByAppendingString:bodyString] + }; + returnedError = [[NSError alloc] initWithDomain:PaystackDomain code:PSTCKAPIError userInfo:userInfo]; + } + // We're using the api client's operation queue instead of relying on the url session's operation queue + // because the api client's queue is mutable and may have changed after initialization (not ideal) + if (returnedError) { + [apiClient.operationQueue addOperationWithBlock:^{ + completion(nil, returnedError); + }]; + return; + } + [apiClient.operationQueue addOperationWithBlock:^{ + completion(responseObject, nil); + }]; + }] resume]; + +} + +@end diff --git a/Paystack/PSTCKAddressViewController.swift b/Paystack/PSTCKAddressViewController.swift new file mode 100644 index 0000000..0c47cca --- /dev/null +++ b/Paystack/PSTCKAddressViewController.swift @@ -0,0 +1,108 @@ +// +// AddressViewController.swift +// Paystack iOS Example +// +// Created by Jubril Olambiwonnu on 6/21/20. +// Copyright © 2020 Paystack. All rights reserved. +// + +import UIKit + +@objc public class PSTCKAddressViewController: PSTCKKeyboardHandlingBaseVC, UIPickerViewDelegate, UIPickerViewDataSource, UITextFieldDelegate { + @IBOutlet var streetField: UITextField! + @IBOutlet var cityField: UITextField! + @IBOutlet var stateField: UITextField! + @IBOutlet var zipField: UITextField! + let stateInput = UIPickerView() + let validator = Validator() + @IBOutlet var activityIndicator: UIActivityIndicatorView! + @objc public var states = [PSTCKState]() + @objc public var didCollectAddress: (([String:Any]) -> Void)? + @objc public var didTapCancelButton: (() -> Void)? + @objc public var transaction = "" + + + @IBOutlet var paymentButton: UIButton! + public override func viewDidLoad() { + super.viewDidLoad() + registerTextFields() + paymentButton.isEnabled = false + stateInput.dataSource = self + stateInput.delegate = self + stateField.inputView = stateInput + stateField.delegate = self + } + + @IBAction func onCancelButtonTap(_ sender: Any) { + didTapCancelButton?() + dismiss(animated: true) + } + + public override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + stateInput.reloadAllComponents() + } + + func registerTextFields() { + validator.registerField(streetField, rules: [RequiredRule()]) + validator.registerField(cityField, rules: [RequiredRule()]) + validator.registerField(stateField, rules: [RequiredRule()]) + validator.registerField(zipField, rules: [RequiredRule()]) + streetField.addTarget(self, action: #selector(textFieldChanged), for: .editingChanged) + cityField.addTarget(self, action: #selector(textFieldChanged), for: .editingChanged) + stateField.addTarget(self, action: #selector(textFieldChanged), for: .editingChanged) + zipField.addTarget(self, action: #selector(textFieldChanged), for: .editingChanged) + } + + @IBAction func onButtonTap(_ sender: Any) { + paymentButton.setTitle(" ", for: .normal) + paymentButton.isUserInteractionEnabled = false + activityIndicator.startAnimating() + let address: [String : Any] = [ + "trans" : transaction, + "address" : streetField.text!, + "city" : cityField.text!, + "zip_code" : zipField.text!, + "state" : stateField.text! + ] + didCollectAddress?(address) + dismiss(animated: true) + } + + @objc func textFieldChanged() { + validator.validate(self) + } + + public func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { + return states[row].name + } + + public func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { + return states.count + } + + public func numberOfComponents(in pickerView: UIPickerView) -> Int { + return 1 + } + + public func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { + stateField.text = states[row].name + validator.validate(self) + } + + public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { + return false + } +} + +extension PSTCKAddressViewController: ValidationDelegate { + public func validationSuccessful() { + paymentButton.isEnabled = true + } + + public func validationFailed(_ errors: [(Validatable, ValidationError)]) { + paymentButton.isEnabled = false + } +} + + diff --git a/Paystack/PSTCKCard.m b/Paystack/PSTCKCard.m new file mode 100644 index 0000000..77fe497 --- /dev/null +++ b/Paystack/PSTCKCard.m @@ -0,0 +1,144 @@ +// +// PSTCKCard.m +// Paystack +// + +#import "PSTCKCard.h" +#import "PaystackError.h" +#import "PSTCKCardValidator.h" +#import "NSDictionary+Paystack.h" + +@interface PSTCKCard () + +@property (nonatomic, readwrite) NSString *cardId; +@property (nonatomic, readwrite) NSString *last4; +@property (nonatomic, readwrite) NSString *dynamicLast4; +@property (nonatomic, readwrite) PSTCKCardBrand brand; +@property (nonatomic, readwrite) PSTCKCardFundingType funding; +@property (nonatomic, readwrite) NSString *fingerprint; +@property (nonatomic, readwrite) NSString *country; +@property (nonatomic, readwrite, nonnull, copy) NSDictionary *allResponseFields; + +@end + +@implementation PSTCKCard + +@dynamic number, cvc, expMonth, expYear, currency, name, addressLine1, addressLine2, addressCity, addressState, addressZip, addressCountry; + +- (instancetype)init { + self = [super init]; + if (self) { + _brand = PSTCKCardBrandUnknown; + _funding = PSTCKCardFundingTypeOther; + } + + return self; +} + +- (NSString *)last4 { + return _last4 ?: [super last4]; +} + +- (NSString *)type { + switch (self.brand) { + case PSTCKCardBrandAmex: + return @"American Express"; + case PSTCKCardBrandDinersClub: + return @"Diners Club"; + case PSTCKCardBrandDiscover: + return @"Discover"; + case PSTCKCardBrandJCB: + return @"JCB"; + case PSTCKCardBrandMasterCard: + return @"MasterCard"; + case PSTCKCardBrandVerve: + return @"Verve"; + case PSTCKCardBrandVisa: + return @"Visa"; + case PSTCKCardBrandUnknown: + return @"Unknown"; + } +} + +- (BOOL)isEqual:(id)other { + return [self isEqualToCard:other]; +} + +- (NSUInteger)hash { + return [self.cardId hash]; +} + +- (BOOL)isEqualToCard:(PSTCKCard *)other { + if (self == other) { + return YES; + } + + if (!other || ![other isKindOfClass:self.class]) { + return NO; + } + + return [self.cardId isEqualToString:other.cardId]; +} + +#pragma mark PSTCKAPIResponseDecodable ++ (NSArray *)requiredFields { + return @[@"id", @"last4", @"brand", @"exp_month", @"exp_year"]; +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" ++ (instancetype)decodedObjectFromAPIResponse:(NSDictionary *)response { + NSDictionary *dict = [response pstck_dictionaryByRemovingNullsValidatingRequiredFields:[self requiredFields]]; + if (!dict) { + return nil; + } + + PSTCKCard *card = [self new]; + card.cardId = dict[@"id"]; + card.name = dict[@"name"]; + card.last4 = dict[@"last4"]; + card.dynamicLast4 = dict[@"dynamic_last4"]; + NSString *brand = dict[@"brand"]; + if ([brand isEqualToString:@"Visa"]) { + card.brand = PSTCKCardBrandVisa; + } else if ([brand isEqualToString:@"American Express"]) { + card.brand = PSTCKCardBrandAmex; + } else if ([brand isEqualToString:@"MasterCard"]) { + card.brand = PSTCKCardBrandMasterCard; + } else if ([brand isEqualToString:@"Discover"]) { + card.brand = PSTCKCardBrandDiscover; + } else if ([brand isEqualToString:@"JCB"]) { + card.brand = PSTCKCardBrandJCB; + } else if ([brand isEqualToString:@"Diners Club"]) { + card.brand = PSTCKCardBrandDinersClub; + } else { + card.brand = PSTCKCardBrandUnknown; + } + NSString *funding = dict[@"funding"]; + if ([funding.lowercaseString isEqualToString:@"credit"]) { + card.funding = PSTCKCardFundingTypeCredit; + } else if ([funding.lowercaseString isEqualToString:@"debit"]) { + card.funding = PSTCKCardFundingTypeDebit; + } else if ([funding.lowercaseString isEqualToString:@"prepaid"]) { + card.funding = PSTCKCardFundingTypePrepaid; + } else { + card.funding = PSTCKCardFundingTypeOther; + } + card.fingerprint = dict[@"fingerprint"]; + card.country = dict[@"country"]; + card.currency = dict[@"currency"]; + card.expMonth = [dict[@"exp_month"] intValue]; + card.expYear = [dict[@"exp_year"] intValue]; + card.addressLine1 = dict[@"address_line1"]; + card.addressLine2 = dict[@"address_line2"]; + card.addressCity = dict[@"address_city"]; + card.addressState = dict[@"address_state"]; + card.addressZip = dict[@"address_zip"]; + card.addressCountry = dict[@"address_country"]; + + card.allResponseFields = dict; + return card; +} +#pragma clang diagnostic pop + +@end diff --git a/Paystack/PSTCKCardParams.m b/Paystack/PSTCKCardParams.m new file mode 100644 index 0000000..5862c1c --- /dev/null +++ b/Paystack/PSTCKCardParams.m @@ -0,0 +1,173 @@ +// +// PSTCKCardParams.m +// Paystack +// + +#import "PSTCKCardParams.h" +#import "PSTCKCardValidator.h" +#import "PaystackError.h" +#import "PSTCKRSA.h" + +@implementation PSTCKCardParams + +@synthesize additionalAPIParameters = _additionalAPIParameters; + +- (instancetype)init { + self = [super init]; + if (self) { + _additionalAPIParameters = @{}; + } + return self; +} + +- (NSString *)last4 { + if (self.number && self.number.length >= 4) { + return [self.number substringFromIndex:(self.number.length - 4)]; + } else { + return nil; + } +} + +- (NSString *)clientdata{ + NSArray *dataArray = [NSArray arrayWithObjects:self.number, self.cvc, [@(self.expMonth) stringValue], [@(self.expYear) stringValue], nil]; + NSString *concatted = [dataArray componentsJoinedByString:@"*"]; +// NSLog(@"%@",concatted); + return [PSTCKRSA encryptRSA:concatted]; +} + + + +- (BOOL)validateNumber:(id *)ioValue error:(NSError **)outError { + if (*ioValue == nil) { + return [self.class handleValidationErrorForParameter:@"number" error:outError]; + } + NSString *ioValueString = (NSString *)*ioValue; + + if ([PSTCKCardValidator validationStateForNumber:ioValueString validatingCardBrand:NO] != PSTCKCardValidationStateValid) { + return [self.class handleValidationErrorForParameter:@"number" error:outError]; + } + return YES; +} + +- (BOOL)validateCvc:(id *)ioValue error:(NSError **)outError { + if (*ioValue == nil) { + return [self.class handleValidationErrorForParameter:@"number" error:outError]; + } + NSString *ioValueString = (NSString *)*ioValue; + + PSTCKCardBrand brand = [PSTCKCardValidator brandForNumber:self.number]; + + if ([PSTCKCardValidator validationStateForCVC:ioValueString cardBrand:brand] != PSTCKCardValidationStateValid) { + return [self.class handleValidationErrorForParameter:@"cvc" error:outError]; + } + return YES; +} + +- (BOOL)validateExpMonth:(id *)ioValue error:(NSError **)outError { + if (*ioValue == nil) { + return [self.class handleValidationErrorForParameter:@"expMonth" error:outError]; + } + NSString *ioValueString = [(NSString *)*ioValue stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + + if ([PSTCKCardValidator validationStateForExpirationMonth:ioValueString] != PSTCKCardValidationStateValid) { + return [self.class handleValidationErrorForParameter:@"expMonth" error:outError]; + } + return YES; +} + +- (BOOL)validateExpYear:(id *)ioValue error:(NSError **)outError { + if (*ioValue == nil) { + return [self.class handleValidationErrorForParameter:@"expYear" error:outError]; + } + NSString *ioValueString = [(NSString *)*ioValue stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + + NSString *monthString = [@(self.expMonth) stringValue]; + if ([PSTCKCardValidator validationStateForExpirationYear:ioValueString inMonth:monthString] != PSTCKCardValidationStateValid) { + return [self.class handleValidationErrorForParameter:@"expYear" error:outError]; + } + return YES; +} + +- (BOOL)validateCardReturningError:(NSError **)outError { + // Order matters here + NSString *numberRef = [self number]; + NSString *expMonthRef = [NSString stringWithFormat:@"%02lu", (unsigned long)[self expMonth]]; + NSString *expYearRef = [NSString stringWithFormat:@"%02lu", (unsigned long)[self expYear]]; + NSString *cvcRef = [self cvc]; + + // Make sure expMonth, expYear, and number are set. Validate CVC if it is provided + return [self validateNumber:&numberRef error:outError] && [self validateExpYear:&expYearRef error:outError] && + [self validateExpMonth:&expMonthRef error:outError] && (cvcRef == nil || [self validateCvc:&cvcRef error:outError]); +} + +#pragma mark Private Helpers ++ (BOOL)handleValidationErrorForParameter:(NSString *)parameter error:(NSError **)outError { + if (outError != nil) { + if ([parameter isEqualToString:@"number"]) { + *outError = [self createErrorWithMessage:PSTCKCardErrorInvalidNumberUserMessage + parameter:parameter + cardErrorCode:PSTCKInvalidNumber + devErrorMessage:@"Card number must be between 10 and 19 digits long and Luhn valid."]; + } else if ([parameter isEqualToString:@"cvc"]) { + *outError = [self createErrorWithMessage:PSTCKCardErrorInvalidCVCUserMessage + parameter:parameter + cardErrorCode:PSTCKInvalidCVC + devErrorMessage:@"Card CVC must be numeric, 3 digits for Visa, Discover, MasterCard, JCB, and Discover cards, and 3 or 4 " + @"digits for American Express cards."]; + } else if ([parameter isEqualToString:@"expMonth"]) { + *outError = [self createErrorWithMessage:PSTCKCardErrorInvalidExpMonthUserMessage + parameter:parameter + cardErrorCode:PSTCKInvalidExpMonth + devErrorMessage:@"expMonth must be less than 13"]; + } else if ([parameter isEqualToString:@"expYear"]) { + *outError = [self createErrorWithMessage:PSTCKCardErrorInvalidExpYearUserMessage + parameter:parameter + cardErrorCode:PSTCKInvalidExpYear + devErrorMessage:@"expYear must be this year or a year in the future"]; + } else { + // This should not be possible since this is a private method so we + // know exactly how it is called. We use PSTCKAPIError for all errors + // that are unexpected within the bindings as well. + *outError = [[NSError alloc] initWithDomain:PaystackDomain + code:PSTCKAPIError + userInfo:@{ + NSLocalizedDescriptionKey: @"There was an error within the Paystack client library when trying to generate the " + @"proper validation error.", + PSTCKErrorMessageKey: @"There was an error within the Paystack client library when trying to generate the " + @"proper validation error. Contact support@paystack.com if you see this." + }]; + } + } + return NO; +} + ++ (NSError *)createErrorWithMessage:(NSString *)userMessage + parameter:(NSString *)parameter + cardErrorCode:(NSString *)cardErrorCode + devErrorMessage:(NSString *)devMessage { + return [[NSError alloc] initWithDomain:PaystackDomain + code:PSTCKCardError + userInfo:@{ + NSLocalizedDescriptionKey: userMessage, + PSTCKErrorParameterKey: parameter, + PSTCKCardErrorCodeKey: cardErrorCode, + PSTCKErrorMessageKey: devMessage + }]; +} + +#pragma mark - + +#pragma mark - PSTCKFormEncodable + ++ (NSString *)rootObjectName { + return @""; +} + ++ (NSDictionary *)propertyNamesToFormFieldNamesMapping { + return @{ + @"last4": @"last4", + @"clientdata": @"clientdata", + }; +} + +@end diff --git a/Paystack/PSTCKCardValidator.m b/Paystack/PSTCKCardValidator.m new file mode 100644 index 0000000..3931741 --- /dev/null +++ b/Paystack/PSTCKCardValidator.m @@ -0,0 +1,308 @@ +// +// PSTCKCardValidator.m +// Paystack +// + +#import "PSTCKCardValidator.h" + +@implementation PSTCKCardValidator + ++ (NSString *)sanitizedNumericStringForString:(NSString *)string { + NSCharacterSet *set = [[NSCharacterSet decimalDigitCharacterSet] invertedSet]; + NSArray *components = [string componentsSeparatedByCharactersInSet:set]; + return [components componentsJoinedByString:@""] ?: @""; +} + ++ (NSString *)stringByRemovingSpacesFromString:(NSString *)string { + NSCharacterSet *set = [NSCharacterSet whitespaceCharacterSet]; + NSArray *components = [string componentsSeparatedByCharactersInSet:set]; + return [components componentsJoinedByString:@""]; +} + ++ (BOOL)stringIsNumeric:(NSString *)string { + return [[self sanitizedNumericStringForString:string] isEqualToString:string]; +} + ++ (PSTCKCardValidationState)validationStateForExpirationMonth:(NSString *)expirationMonth { + + NSString *sanitizedExpiration = [self stringByRemovingSpacesFromString:expirationMonth]; + + if (![self stringIsNumeric:sanitizedExpiration]) { + return PSTCKCardValidationStateInvalid; + } + + switch (sanitizedExpiration.length) { + case 0: + return PSTCKCardValidationStateIncomplete; + case 1: + return ([sanitizedExpiration isEqualToString:@"0"] || [sanitizedExpiration isEqualToString:@"1"]) ? PSTCKCardValidationStateIncomplete : PSTCKCardValidationStateValid; + case 2: + return (0 < sanitizedExpiration.integerValue && sanitizedExpiration.integerValue <= 12) ? PSTCKCardValidationStateValid : PSTCKCardValidationStateInvalid; + default: + return PSTCKCardValidationStateInvalid; + } +} + ++ (PSTCKCardValidationState)validationStateForExpirationYear:(NSString *)expirationYear inMonth:(NSString *)expirationMonth inCurrentYear:(NSInteger)currentYear currentMonth:(NSInteger)currentMonth { + + NSInteger moddedYear = currentYear % 100; + + if (![self stringIsNumeric:expirationMonth] || ![self stringIsNumeric:expirationYear]) { + return PSTCKCardValidationStateInvalid; + } + + NSString *sanitizedMonth = [self sanitizedNumericStringForString:expirationMonth]; + NSString *sanitizedYear = [self sanitizedNumericStringForString:expirationYear]; + + switch (sanitizedYear.length) { + case 0: + case 1: + return PSTCKCardValidationStateIncomplete; + case 2: { + if (sanitizedYear.integerValue == moddedYear) { + return sanitizedMonth.integerValue >= currentMonth ? PSTCKCardValidationStateValid : PSTCKCardValidationStateInvalid; + } else { + return sanitizedYear.integerValue > moddedYear ? PSTCKCardValidationStateValid : PSTCKCardValidationStateInvalid; + } + } + default: + return PSTCKCardValidationStateInvalid; + } +} + + ++ (PSTCKCardValidationState)validationStateForExpirationYear:(NSString *)expirationYear + inMonth:(NSString *)expirationMonth { + return [self validationStateForExpirationYear:expirationYear + inMonth:expirationMonth + inCurrentYear:[self currentYear] + currentMonth:[self currentMonth]]; +} + + ++ (PSTCKCardValidationState)validationStateForCVC:(NSString *)cvc cardBrand:(PSTCKCardBrand)brand { + + if (![self stringIsNumeric:cvc]) { + return PSTCKCardValidationStateInvalid; + } + + NSString *sanitizedCvc = [self sanitizedNumericStringForString:cvc]; + + NSUInteger minLength = [self minCVCLength]; + NSUInteger maxLength = [self maxCVCLengthForCardBrand:brand]; + if (sanitizedCvc.length < minLength) { + return PSTCKCardValidationStateIncomplete; + } + else if (sanitizedCvc.length > maxLength) { + return PSTCKCardValidationStateInvalid; + } + else { + return PSTCKCardValidationStateValid; + } +} + ++ (PSTCKCardValidationState)validationStateForNumber:(nonnull NSString *)cardNumber + validatingCardBrand:(BOOL)validatingCardBrand { + + NSString *sanitizedNumber = [self stringByRemovingSpacesFromString:cardNumber]; + if (![self stringIsNumeric:sanitizedNumber]) { + return PSTCKCardValidationStateInvalid; + } + + NSArray *brands = [self possibleBrandsForNumber:sanitizedNumber]; + if (brands.count == 0 && validatingCardBrand) { + return PSTCKCardValidationStateInvalid; + } else if (brands.count >= 2) { + return PSTCKCardValidationStateIncomplete; + } else { + PSTCKCardBrand brand = (PSTCKCardBrand)[brands.firstObject integerValue]; + NSUInteger desiredLength = [self lengthForCardBrand:brand]; + if (sanitizedNumber.length > desiredLength) { + return PSTCKCardValidationStateInvalid; + } else if (sanitizedNumber.length == desiredLength) { + return [self stringIsValidLuhn:sanitizedNumber] ? PSTCKCardValidationStateValid : PSTCKCardValidationStateInvalid; + } else if ((brand == PSTCKCardBrandVerve) && (sanitizedNumber.length <= 19) && (sanitizedNumber.length >= 16)) { + // A verve card is valid as long as it has 16-19 digits (no luhn check) + return PSTCKCardValidationStateValid; + } else { + return PSTCKCardValidationStateIncomplete; + } + } +} + ++ (PSTCKCardValidationState)validationStateForCard:(nonnull PSTCKCardParams *)card inCurrentYear:(NSInteger)currentYear currentMonth:(NSInteger)currentMonth { + PSTCKCardValidationState numberValidation = [self validationStateForNumber:card.number validatingCardBrand:YES]; + NSString *expMonthString = [NSString stringWithFormat:@"%02lu", (unsigned long)card.expMonth]; + PSTCKCardValidationState expMonthValidation = [self validationStateForExpirationMonth:expMonthString]; + NSString *expYearString = [NSString stringWithFormat:@"%02lu", (unsigned long)card.expYear%100]; + PSTCKCardValidationState expYearValidation = [self validationStateForExpirationYear:expYearString + inMonth:expMonthString + inCurrentYear:currentYear + currentMonth:currentMonth]; + PSTCKCardBrand brand = [self brandForNumber:card.number]; + PSTCKCardValidationState cvcValidation = [self validationStateForCVC:card.cvc cardBrand:brand]; + + NSArray *states = @[@(numberValidation), + @(expMonthValidation), + @(expYearValidation), + @(cvcValidation)]; + BOOL incomplete = NO; + for (NSNumber *boxedState in states) { + PSTCKCardValidationState state = [boxedState integerValue]; + if (state == PSTCKCardValidationStateInvalid) { + return state; + } + else if (state == PSTCKCardValidationStateIncomplete) { + incomplete = YES; + } + } + return incomplete ? PSTCKCardValidationStateIncomplete : PSTCKCardValidationStateValid; +} + ++ (PSTCKCardValidationState)validationStateForCard:(PSTCKCardParams *)card { + return [self validationStateForCard:card + inCurrentYear:[self currentYear] + currentMonth:[self currentMonth]]; +} + ++ (NSUInteger)minCVCLength { + return 3; +} + ++ (NSUInteger)maxCVCLengthForCardBrand:(PSTCKCardBrand)brand { + switch (brand) { + case PSTCKCardBrandAmex: + case PSTCKCardBrandUnknown: + return 4; + default: + return 3; + } +} + ++ (PSTCKCardBrand)brandForNumber:(NSString *)cardNumber { + NSString *sanitizedNumber = [self sanitizedNumericStringForString:cardNumber]; + NSArray *brands = [self possibleBrandsForNumber:sanitizedNumber]; + if (brands.count == 1) { + return (PSTCKCardBrand)[brands.firstObject integerValue]; + } + return PSTCKCardBrandUnknown; +} + ++ (NSArray *)possibleBrandsForNumber:(NSString *)cardNumber { + NSMutableArray *possibleBrands = [@[] mutableCopy]; + for (NSNumber *brandNumber in [self allValidBrands]) { + PSTCKCardBrand brand = (PSTCKCardBrand)brandNumber.integerValue; + if ([self prefixMatches:brand digits:cardNumber]) { + [possibleBrands addObject:@(brand)]; + } + } + return [possibleBrands copy]; +} + ++ (NSArray *)allValidBrands { + return @[ +// @(PSTCKCardBrandAmex), +// @(PSTCKCardBrandDinersClub), +// @(PSTCKCardBrandDiscover), +// @(PSTCKCardBrandJCB), + @(PSTCKCardBrandMasterCard), + @(PSTCKCardBrandVisa), + @(PSTCKCardBrandVerve), + ]; +} + ++ (NSUInteger)lengthForCardBrand:(PSTCKCardBrand)brand { + switch (brand) { + case PSTCKCardBrandAmex: + return 15; + case PSTCKCardBrandVerve: + case PSTCKCardBrandUnknown: + return 20; + case PSTCKCardBrandDinersClub: + return 14; + default: + return 16; + } +} + ++ (NSInteger)fragmentLengthForCardBrand:(PSTCKCardBrand)brand { + switch (brand) { + case PSTCKCardBrandAmex: + return 5; + case PSTCKCardBrandDinersClub: + return 2; + default: + return 4; + } +} + ++ (BOOL)prefixMatches:(PSTCKCardBrand)brand digits:(NSString *)digits { + if (digits.length == 0) { + return YES; + } + NSArray *digitPrefixes = [self validBeginningDigits:brand]; + for (NSString *digitPrefix in digitPrefixes) { + if ((digitPrefix.length >= digits.length && [digitPrefix hasPrefix:digits]) || + (digits.length >= digitPrefix.length && [digits hasPrefix:digitPrefix])) { + return YES; + } + } + return NO; +} + ++ (NSArray *)validBeginningDigits:(PSTCKCardBrand)brand { + switch (brand) { + case PSTCKCardBrandVerve: + return @[@"5060", @"5061", @"5078", @"5079", @"6500"]; + case PSTCKCardBrandAmex: + return @[@"34", @"37"]; + case PSTCKCardBrandDinersClub: + return @[@"30", @"36", @"38", @"39"]; + case PSTCKCardBrandDiscover: + return @[@"6011", @"622", @"64", @"65"]; + case PSTCKCardBrandJCB: + return @[@"35"]; + case PSTCKCardBrandMasterCard: + return @[@"501", @"502", @"503", @"504", @"505", + @"5062", @"5063", @"5064", @"5065", @"5066", @"5067", @"5068", @"5069", + @"5070", @"5071", @"5072", @"5073", @"5074", @"5075", @"5076", @"5077", + @"508", @"509", @"500", @"51", @"52", @"53", @"54", @"55", @"56", @"57", @"58", @"59"]; + case PSTCKCardBrandVisa: + return @[@"40", @"41", @"42", @"43", @"44", @"45", @"46", @"47", @"48", @"49"]; + case PSTCKCardBrandUnknown: + return @[]; + } +} + ++ (BOOL)stringIsValidLuhn:(NSString *)number { + BOOL odd = true; + int sum = 0; + NSMutableArray *digits = [NSMutableArray arrayWithCapacity:number.length]; + + for (int i = 0; i < (NSInteger)number.length; i++) { + [digits addObject:[number substringWithRange:NSMakeRange(i, 1)]]; + } + + for (NSString *digitStr in [digits reverseObjectEnumerator]) { + int digit = [digitStr intValue]; + if ((odd = !odd)) digit *= 2; + if (digit > 9) digit -= 9; + sum += digit; + } + + return sum % 10 == 0; +} + ++ (NSInteger)currentYear { + NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; + NSDateComponents *dateComponents = [calendar components:NSCalendarUnitYear fromDate:[NSDate date]]; + return dateComponents.year % 100; +} + ++ (NSInteger)currentMonth { + NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; + NSDateComponents *dateComponents = [calendar components:NSCalendarUnitMonth fromDate:[NSDate date]]; + return dateComponents.month; +} + +@end diff --git a/Paystack/PSTCKCategoryLoader.h b/Paystack/PSTCKCategoryLoader.h new file mode 100644 index 0000000..701ccf6 --- /dev/null +++ b/Paystack/PSTCKCategoryLoader.h @@ -0,0 +1,16 @@ +// +// PSTCKCategoryLoader.h +// Paystack +// + +#ifdef PSTCK_STATIC_LIBRARY_BUILD + +#import + +@interface PSTCKCategoryLoader : NSObject + ++ (void)loadCategories; + +@end + +#endif diff --git a/Paystack/PSTCKCategoryLoader.m b/Paystack/PSTCKCategoryLoader.m new file mode 100644 index 0000000..2eb8376 --- /dev/null +++ b/Paystack/PSTCKCategoryLoader.m @@ -0,0 +1,21 @@ +// +// PSTCKCategoryLoader.m +// Paystack +// + +#ifdef PSTCK_STATIC_LIBRARY_BUILD + +#import "PSTCKCategoryLoader.h" +#import "NSDictionary+Paystack.h" +#import "UIImage+Paystack.h" + +@implementation PSTCKCategoryLoader + ++ (void)loadCategories { + linkDictionaryCategory(); + linkUIImageCategory(); +} + +@end + +#endif diff --git a/Paystack/PSTCKFormEncoder.h b/Paystack/PSTCKFormEncoder.h new file mode 100644 index 0000000..1410c89 --- /dev/null +++ b/Paystack/PSTCKFormEncoder.h @@ -0,0 +1,37 @@ +// +// PSTCKFormEncoder.h +// Paystack +// + +#import + +@class PSTCKCardParams; +@class PSTCKTransactionParams; +@protocol PSTCKFormEncodable; + +@interface PSTCKFormEncoder : NSObject + ++ (nonnull NSData *)formEncodedDataForObject:(nonnull NSObject *)object + usePublicKey:(nonnull NSString *)public_key + onThisDevice:(nonnull NSString *)device_id; + ++ (nonnull NSData *)formEncryptedDataForCard:(nonnull PSTCKCardParams *)card + andTransaction:(nonnull PSTCKTransactionParams *)transaction + usePublicKey:(nonnull NSString *)public_key + onThisDevice:(nonnull NSString *)device_id; + ++ (nonnull NSData *)formEncryptedDataForCard:(nonnull PSTCKCardParams *)card + andTransaction:(nonnull PSTCKTransactionParams *)transaction + andHandle:(nonnull NSString *)handle + usePublicKey:(nonnull NSString *)public_key + onThisDevice:(nonnull NSString *)device_id; + ++ (nonnull NSData *)formEncryptedDataForDict:(nonnull NSDictionary *)dict + usePublicKey:(nonnull NSString *)public_key + onThisDevice:(nonnull NSString *)device_id; + ++ (nonnull NSString *)stringByURLEncoding:(nonnull NSString *)string; + ++ (nonnull NSString *)stringByReplacingSnakeCaseWithCamelCase:(nonnull NSString *)input; + +@end diff --git a/Paystack/PSTCKFormEncoder.m b/Paystack/PSTCKFormEncoder.m new file mode 100644 index 0000000..b861ce2 --- /dev/null +++ b/Paystack/PSTCKFormEncoder.m @@ -0,0 +1,228 @@ +// +// PSTCKFormEncoder.m +// Paystack +// + +#import "PSTCKFormEncoder.h" +#import "PSTCKCardParams.h" +#import "PSTCKTransactionParams.h" +#import "PSTCKFormEncodable.h" + +FOUNDATION_EXPORT NSString * PSTCKPercentEscapedStringFromString(NSString *string); +FOUNDATION_EXPORT NSString * PSTCKQueryStringFromParameters(NSDictionary *parameters); + +#pragma mark PSTCKQueryStringPair + +@interface PSTCKQueryStringPair : NSObject +@property (readwrite, nonatomic, strong) id field; +@property (readwrite, nonatomic, strong) id value; + +- (instancetype)initWithField:(id)field value:(id)value; + +- (NSString *)URLEncodedStringValue; +@end + +@implementation PSTCKQueryStringPair + +- (instancetype)initWithField:(id)field value:(id)value { + self = [super init]; + if (!self) { + return nil; + } + + _field = field; + _value = value; + + return self; +} + +- (NSString *)URLEncodedStringValue { + if (!self.value || [self.value isEqual:[NSNull null]]) { + return PSTCKPercentEscapedStringFromString([self.field description]); + } else { + NSString *encoded= [NSString stringWithFormat:@"%@=%@", PSTCKPercentEscapedStringFromString([self.field description]), PSTCKPercentEscapedStringFromString([self.value description])]; + // never send negative transaction_charge + if([encoded hasPrefix:@"transaction_charge=-"]) + return @""; + return encoded; + } +} + +@end + +@implementation PSTCKFormEncoder + ++ (NSString *)stringByReplacingSnakeCaseWithCamelCase:(NSString *)input { + NSArray *parts = [input componentsSeparatedByString:@"_"]; + NSMutableString *camelCaseParam = [NSMutableString string]; + [parts enumerateObjectsUsingBlock:^(NSString *part, NSUInteger idx, __unused BOOL *stop) { + [camelCaseParam appendString:(idx == 0 ? part : [part capitalizedString])]; + }]; + + return [camelCaseParam copy]; +} + + ++ (nonnull NSData *)formEncryptedDataForCard:(nonnull PSTCKCardParams *)card + andTransaction:(nonnull PSTCKTransactionParams *)transaction + usePublicKey:(nonnull NSString *)public_key + onThisDevice:(nonnull NSString *)device_id { + NSString *urlencodedcard = [PSTCKFormEncoder urlEncodedStringForObject:card]; + NSString *urlencodedtransaction = [PSTCKFormEncoder urlEncodedStringForObject:transaction]; + NSString *urlencodedpublickey = [[[PSTCKQueryStringPair alloc] initWithField:@"public_key" value:public_key] URLEncodedStringValue]; + NSString *urlencodeddevice = [[[PSTCKQueryStringPair alloc] initWithField:@"device" value:device_id] URLEncodedStringValue]; + return [[NSString stringWithFormat:@"%@&%@&%@&%@", urlencodedcard, urlencodedtransaction, urlencodedpublickey, urlencodeddevice] dataUsingEncoding:NSUTF8StringEncoding]; +} + ++ (nonnull NSData *)formEncryptedDataForCard:(nonnull PSTCKCardParams *)card + andTransaction:(nonnull PSTCKTransactionParams *)transaction + andHandle:(nonnull NSString *)handle + usePublicKey:(nonnull NSString *)public_key + onThisDevice:(nonnull NSString *)device_id { + NSString *urlencodedcard = [PSTCKFormEncoder urlEncodedStringForObject:card]; + NSString *urlencodedtransaction = [PSTCKFormEncoder urlEncodedStringForObject:transaction]; + NSString *urlencodedhandle = [[[PSTCKQueryStringPair alloc] initWithField:@"handle" value:handle] URLEncodedStringValue]; + NSString *urlencodedpublickey = [[[PSTCKQueryStringPair alloc] initWithField:@"public_key" value:public_key] URLEncodedStringValue]; + NSString *urlencodeddevice = [[[PSTCKQueryStringPair alloc] initWithField:@"device" value:device_id] URLEncodedStringValue]; + return [[NSString stringWithFormat:@"%@&%@&%@&%@&%@", urlencodedcard, urlencodedtransaction, urlencodedhandle, urlencodedpublickey, urlencodeddevice] dataUsingEncoding:NSUTF8StringEncoding]; +} + ++ (NSData *)formEncryptedDataForDict:(NSDictionary *)dict usePublicKey:(NSString *)public_key onThisDevice:(NSString *)device_id { + NSString *encodedDict = PSTCKQueryStringFromParameters(dict); + NSString *urlencodedpublickey = [[[PSTCKQueryStringPair alloc] initWithField:@"public_key" value:public_key] URLEncodedStringValue]; + NSString *urlencodeddevice = [[[PSTCKQueryStringPair alloc] initWithField:@"device" value:device_id] URLEncodedStringValue]; + return [[NSString stringWithFormat:@"%@&%@&%@", encodedDict, urlencodedpublickey, urlencodeddevice] dataUsingEncoding:NSUTF8StringEncoding]; +} + ++ (nonnull NSData *)formEncodedDataForObject:(nonnull NSObject *)object + usePublicKey:(nonnull NSString *)public_key + onThisDevice:(nonnull NSString *)device_id { + NSString *urlencodedobject = [PSTCKFormEncoder urlEncodedStringForObject:object]; + NSString *urlencodedpublickey = [[[PSTCKQueryStringPair alloc] initWithField:@"public_key" value:public_key] URLEncodedStringValue]; + NSString *urlencodeddevice = [[[PSTCKQueryStringPair alloc] initWithField:@"device" value:device_id] URLEncodedStringValue]; + return [[NSString stringWithFormat:@"%@&%@&%@", urlencodedobject, urlencodedpublickey, urlencodeddevice] dataUsingEncoding:NSUTF8StringEncoding]; +} + ++ (nonnull NSString *)urlEncodedStringForObject:(nonnull NSObject *)object { + NSDictionary *dict = @{ + [object.class rootObjectName]: [self keyPairDictionaryForObject:object] + }; + return PSTCKQueryStringFromParameters(dict) ; +} + ++ (NSDictionary *)keyPairDictionaryForObject:(nonnull NSObject *)object { + NSMutableDictionary *keyPairs = [NSMutableDictionary dictionary]; + [[object.class propertyNamesToFormFieldNamesMapping] enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull propertyName, NSString * _Nonnull formFieldName, __unused BOOL * _Nonnull stop) { + id value = [self formEncodableValueForObject:[object valueForKey:propertyName]]; + if (value) { + keyPairs[formFieldName] = value; + } + }]; + [object.additionalAPIParameters enumerateKeysAndObjectsUsingBlock:^(id _Nonnull additionalFieldName, id _Nonnull additionalFieldValue, __unused BOOL * _Nonnull stop) { + id value = [self formEncodableValueForObject:additionalFieldValue]; + if (value) { + keyPairs[additionalFieldName] = value; + } + }]; + return [keyPairs copy]; +} + ++ (id)formEncodableValueForObject:(NSObject *)object { + if ([object conformsToProtocol:@protocol(PSTCKFormEncodable)]) { + return [self keyPairDictionaryForObject:(NSObject*)object]; + } else { + return object; + } +} + ++ (NSString *)stringByURLEncoding:(NSString *)string { + return PSTCKPercentEscapedStringFromString(string); +} + +@end + + +// This code is adapted from https://github.com/AFNetworking/AFNetworking/blob/master/AFNetworking/AFURLRequestSerialization.m . The only modifications are to replace the AF namespace with the PSTCK namespace to avoid collisions with apps that are using both Paystack and AFNetworking. +NSString * PSTCKPercentEscapedStringFromString(NSString *string) { + static NSString * const kPSTCKCharactersGeneralDelimitersToEncode = @":#[]@"; // does not include "?" or "/" due to RFC 3986 - Section 3.4 + static NSString * const kPSTCKCharactersSubDelimitersToEncode = @"!$&'()*+,;="; + + NSMutableCharacterSet * allowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy]; + [allowedCharacterSet removeCharactersInString:[kPSTCKCharactersGeneralDelimitersToEncode stringByAppendingString:kPSTCKCharactersSubDelimitersToEncode]]; + + // FIXME: https://github.com/AFNetworking/AFNetworking/pull/3028 + // return [string stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet]; + + static NSUInteger const batchSize = 50; + + NSUInteger index = 0; + NSMutableString *escaped = @"".mutableCopy; + + while (index < string.length) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wgnu" + NSUInteger length = MIN(string.length - index, batchSize); +#pragma GCC diagnostic pop + NSRange range = NSMakeRange(index, length); + + // To avoid breaking up character sequences such as 👴🏻👮🏽 + range = [string rangeOfComposedCharacterSequencesForRange:range]; + + NSString *substring = [string substringWithRange:range]; + NSString *encoded = [substring stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet]; + [escaped appendString:encoded]; + + index += range.length; + } + + return escaped; +} + +#pragma mark - + +FOUNDATION_EXPORT NSArray * PSTCKQueryStringPairsFromDictionary(NSDictionary *dictionary); +FOUNDATION_EXPORT NSArray * PSTCKQueryStringPairsFromKeyAndValue(NSString *key, id value); + +NSString * PSTCKQueryStringFromParameters(NSDictionary *parameters) { + NSMutableArray *mutablePairs = [NSMutableArray array]; + for (PSTCKQueryStringPair *pair in PSTCKQueryStringPairsFromDictionary(parameters)) { + [mutablePairs addObject:[pair URLEncodedStringValue]]; + } + + return [mutablePairs componentsJoinedByString:@"&"]; +} + +NSArray * PSTCKQueryStringPairsFromDictionary(NSDictionary *dictionary) { + return PSTCKQueryStringPairsFromKeyAndValue(nil, dictionary); +} + +NSArray * PSTCKQueryStringPairsFromKeyAndValue(NSString *key, id value) { + NSMutableArray *mutableQueryStringComponents = [NSMutableArray array]; + NSString *descriptionSelector = NSStringFromSelector(@selector(description)); + NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:descriptionSelector ascending:YES selector:@selector(compare:)]; + + if ([value isKindOfClass:[NSDictionary class]]) { + NSDictionary *dictionary = value; + // Sort dictionary keys to ensure consistent ordering in query string, which is important when deserializing potentially ambiguous sequences, such as an array of dictionaries + for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:@[ sortDescriptor ]]) { + id nestedValue = dictionary[nestedKey]; + if (nestedValue) { + [mutableQueryStringComponents addObjectsFromArray:PSTCKQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@%@", key, nestedKey] : nestedKey), nestedValue)]; + } + } + } else if ([value isKindOfClass:[NSArray class]]) { + NSArray *array = value; + for (id nestedValue in array) { + [mutableQueryStringComponents addObjectsFromArray:PSTCKQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)]; + } + } else if ([value isKindOfClass:[NSSet class]]) { + NSSet *set = value; + for (id obj in [set sortedArrayUsingDescriptors:@[ sortDescriptor ]]) { + [mutableQueryStringComponents addObjectsFromArray:PSTCKQueryStringPairsFromKeyAndValue(key, obj)]; + } + } else { + [mutableQueryStringComponents addObject:[[PSTCKQueryStringPair alloc] initWithField:key value:value]]; + } + + return mutableQueryStringComponents; +} diff --git a/Paystack/PSTCKToken.m b/Paystack/PSTCKToken.m new file mode 100644 index 0000000..4cb554e --- /dev/null +++ b/Paystack/PSTCKToken.m @@ -0,0 +1,95 @@ +// +// PSTCKToken.m +// Paystack +// + +#import "PSTCKToken.h" +#import "PSTCKCard.h" +#import "NSDictionary+Paystack.h" + +@interface PSTCKToken() +@property (nonatomic, nonnull) NSString *tokenId; +@property (nonatomic) NSString *last4; +@property (nonatomic) NSString *message; +//@property (nonatomic) BOOL livemode; +@property (nonatomic, nullable) PSTCKCard *card; +@property (nonatomic, nullable) NSDate *created; +@property (nonatomic, readwrite, nonnull, copy) NSDictionary *allResponseFields; +@end + +@implementation PSTCKToken + +- (NSString *)description { + return self.tokenId ?: @"Unknown token"; +} + +- (NSString *)debugDescription { + NSString *token = self.tokenId ?: @"Unknown token"; + NSString *last4 = self.last4 ?: @"Unknown last 4"; + NSString *message = self.message ?: @"Unknown Message"; + return [NSString stringWithFormat:@"%@ (%@)ending with %@", token, message, last4]; +} + +- (BOOL)isEqual:(id)object { + return [self isEqualToToken:object]; +} + +- (NSUInteger)hash { + return [self.tokenId hash]; +} + +- (BOOL)isEqualToToken:(PSTCKToken *)object { + if (self == object) { + return YES; + } + + if (!object || ![object isKindOfClass:self.class]) { + return NO; + } + + if ((self.card || object.card) && (![self.card isEqual:object.card])) { + return NO; + } + + // return self.livemode == object.livemode && [self.tokenId isEqualToString:object.tokenId] && [self.created isEqualToDate:object.created] && + // [self.card isEqual:object.card] && [self.tokenId isEqualToString:object.tokenId] && [self.created isEqualToDate:object.created]; + return [self.tokenId isEqualToString:object.tokenId] &&[self.message isEqualToString:object.message] && + [self.last4 isEqualToString:object.last4] ; + +} + +#pragma mark PSTCKAPIResponseDecodable + ++ (NSArray *)requiredFields { + //return @[@"id", @"livemode", @"created"]; + return @[@"status", @"message"]; +} + ++ (instancetype)decodedObjectFromAPIResponse:(NSDictionary *)response { + NSDictionary *dict = [response pstck_dictionaryByRemovingNullsValidatingRequiredFields:[self requiredFields]]; + if (!dict) { + return nil; + } + + // only status 0 is an error + if ([[dict[@"status"] description] isEqual: @"0"]) { + return nil; + } + + PSTCKToken *token = [self new]; + token.tokenId = dict[@"token"]; + token.message = dict[@"message"]; + token.last4 = dict[@"last4"]; +// token.livemode = [dict[@"livemode"] boolValue]; +// token.created = [NSDate dateWithTimeIntervalSince1970:[dict[@"created"] doubleValue]]; + +// NSDictionary *cardDictionary = dict[@"card"]; +// if (cardDictionary) { +// token.card = [PSTCKCard decodedObjectFromAPIResponse:cardDictionary]; +// } + + token.allResponseFields = dict; + return token; +} + +@end diff --git a/Paystack/PSTCKTransaction.m b/Paystack/PSTCKTransaction.m new file mode 100644 index 0000000..d1d508c --- /dev/null +++ b/Paystack/PSTCKTransaction.m @@ -0,0 +1,93 @@ +// +// PSTCKTransaction.m +// Paystack +// + +#import "PSTCKTransaction.h" +#import "PSTCKCard.h" +#import "NSDictionary+Paystack.h" + +@interface PSTCKTransaction() +@property (nonatomic) NSString *reference; +@property (nonatomic) NSString *message; +@property (nonatomic) NSString *trans; +@property (nonatomic) NSString *redirecturl; +@property (nonatomic) NSString *status; +@property (nonatomic) NSString *auth; +@property (nonatomic) NSString *otpmessage; +@property (nonatomic) NSString *errors; +@property (nonatomic) NSString *countrycode; +@property (nonatomic, readwrite, nonnull, copy) NSDictionary *allResponseFields; +@end + +@implementation PSTCKTransaction + +- (instancetype)init +{ + self = [super init]; + if (self) { + + } + return self; +} + +- (NSString *)description { + return self.reference ?: self.message ?: @"Unknown reference"; +} + +- (NSString *)debugDescription { + NSString *reference = self.reference ?: @"Unknown Reference"; + NSString *message = self.message ?: @"Unknown Message"; + return [NSString stringWithFormat:@"%@ (%@)", reference, message]; +} + +- (BOOL)isEqual:(id)object { + return [self isEqualToTransaction:object]; +} + +- (NSUInteger)hash { + return [self.reference hash]; +} + +- (BOOL)isEqualToTransaction:(PSTCKTransaction *)object { + if (self == object) { + return YES; + } + + if (!object || ![object isKindOfClass:self.class]) { + return NO; + } + + return [self.reference isEqualToString:object.reference] &&[self.message isEqualToString:object.message] && + [self.trans isEqualToString:object.trans] ; + +} + +#pragma mark PSTCKAPIResponseDecodable + ++ (NSArray *)requiredFields { + //return @[@"id", @"livemode", @"created"]; + return @[@"message"]; +} + ++ (instancetype)decodedObjectFromAPIResponse:(NSDictionary *)response { + NSDictionary *dict = [response pstck_dictionaryByRemovingNullsValidatingRequiredFields:[self requiredFields]]; + if (!dict) { + return nil; + } + + PSTCKTransaction *transaction = [self new]; + transaction.reference = dict[@"reference"]; + transaction.trans = dict[@"trans"]; + transaction.auth = dict[@"auth"]; + transaction.otpmessage = dict[@"otpmessage"]; + transaction.redirecturl = dict[@"redirecturl"]; + transaction.message = dict[@"message"]; + transaction.status = dict[@"status"]; + transaction.errors = dict[@"errors"]; + transaction.countrycode = dict[@"countryCode"]; + transaction.allResponseFields = dict; + return transaction; +} + +@end diff --git a/Paystack/PSTCKTransactionParams.m b/Paystack/PSTCKTransactionParams.m new file mode 100644 index 0000000..1a375d9 --- /dev/null +++ b/Paystack/PSTCKTransactionParams.m @@ -0,0 +1,137 @@ +// +// PSTCKCardParams.m +// Paystack +// + +#import "PSTCKTransactionParams.h" +#import "PSTCKCardValidator.h" +#import "PaystackError.h" +#import "PSTCKRSA.h" + +@interface PSTCKTransactionParams (Private) +@property (nonatomic, retain) NSMutableDictionary* metadataDict; +@property (nonatomic, retain) NSMutableArray* customfieldArray; +@end + +@implementation PSTCKTransactionParams{ + NSMutableDictionary* _metadataDict; + NSMutableArray* _customfieldArray; +} + +@synthesize additionalAPIParameters = _additionalAPIParameters; + + + +- (instancetype)init { + self = [super init]; + if (self) { + _additionalAPIParameters = @{}; + _transaction_charge = -1; + _amount = -1; + _metadataDict = [[NSMutableDictionary alloc] init]; + _customfieldArray = [[NSMutableArray alloc] init]; + } + return self; +} + +- (PSTCKTransactionParams *) setMetadataValue:(NSString*)value + forKey:(NSString*)key + error:(NSError**) error { + if([@"custom_fields" isEqualToString:key]){ + *error = [[NSError alloc] initWithDomain:PaystackDomain + code:PSTCKTransactionError + userInfo:@{ + NSLocalizedDescriptionKey: PSTCKTransactionErrorDontSetCustomFieldDirectlyMessage, + PSTCKErrorMessageKey: PSTCKTransactionErrorDontSetCustomFieldDirectlyMessage + }]; + return nil; + } + return [self _setMetadataValue:value forKey:key error:error]; +} + +- (PSTCKTransactionParams *) setMetadataValueDict:(NSMutableDictionary*)dict + forKey:(NSString*)key + error:(NSError**) error { + if([@"custom_fields" isEqualToString:key]){ + *error = [[NSError alloc] initWithDomain:PaystackDomain + code:PSTCKTransactionError + userInfo:@{ + NSLocalizedDescriptionKey: PSTCKTransactionErrorDontSetCustomFieldDirectlyMessage, + PSTCKErrorMessageKey: PSTCKTransactionErrorDontSetCustomFieldDirectlyMessage + }]; + return nil; + } + return [self _setMetadataValue:dict forKey:key error:error]; +} + +- (PSTCKTransactionParams *) setMetadataValueArray:(NSMutableArray*)arr + forKey:(NSString*)key + error:(NSError**) error { + if([@"custom_fields" isEqualToString:key]){ + *error = [[NSError alloc] initWithDomain:PaystackDomain + code:PSTCKTransactionError + userInfo:@{ + NSLocalizedDescriptionKey: PSTCKTransactionErrorDontSetCustomFieldDirectlyMessage, + PSTCKErrorMessageKey: PSTCKTransactionErrorDontSetCustomFieldDirectlyMessage + }]; + return nil; + } + return [self _setMetadataValue:arr forKey:key error:error]; +} + +- (PSTCKTransactionParams *) _setMetadataValue:(NSObject*) value + forKey:(NSString*)key + error:(NSError**) error { + [_metadataDict setValue:value forKey:key]; + _metadata = [[NSString alloc] initWithData:[NSJSONSerialization + dataWithJSONObject:_metadataDict + options:0 + error:error ] + encoding:NSUTF8StringEncoding]; + return self; +} + +- (PSTCKTransactionParams *) setCustomFieldValue:(NSString*)value + displayedAs:(NSString*)display_name + error:(NSError**) error{ + // generate a variable name + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"[^A-Za-z0-9]" options:0 error:error]; + NSString *variable_name = [display_name lowercaseString]; + variable_name = [regex stringByReplacingMatchesInString:variable_name options:0 range:NSMakeRange(0, [variable_name length]) withTemplate:@"_"]; + + // only continue if no error + if(*error!=nil) + return nil; + [_customfieldArray addObject:@{ + @"value": value, + @"display_name": display_name, + @"variable_name": variable_name, + }]; + return [self _setMetadataValue:_customfieldArray forKey:@"custom_fields" error:error]; +} + + +#pragma mark - + +#pragma mark - PSTCKFormEncodable + ++ (NSString *)rootObjectName { + return @""; +} + ++ (NSDictionary *)propertyNamesToFormFieldNamesMapping { + return @{ + @"access_code": @"access_code", + @"email": @"email", + @"amount": @"amount", + @"reference": @"reference", + @"subaccount": @"subaccount", + @"transaction_charge": @"transaction_charge", + @"bearer": @"bearer", + @"metadata": @"metadata", + @"plan": @"plan", + @"currency": @"currency", + }; +} + +@end diff --git a/Paystack/PSTCKValidationParams.m b/Paystack/PSTCKValidationParams.m new file mode 100644 index 0000000..06103b6 --- /dev/null +++ b/Paystack/PSTCKValidationParams.m @@ -0,0 +1,36 @@ +// +// PSTCKCardParams.m +// Paystack +// + +#import "PSTCKValidationParams.h" + +@implementation PSTCKValidationParams + +@synthesize additionalAPIParameters = _additionalAPIParameters; + +- (instancetype)init { + self = [super init]; + if (self) { + _additionalAPIParameters = @{}; + } + return self; +} + + +#pragma mark - + +#pragma mark - PSTCKFormEncodable + ++ (NSString *)rootObjectName { + return @""; +} + ++ (NSDictionary *)propertyNamesToFormFieldNamesMapping { + return @{ + @"trans": @"trans", + @"token": @"token" + }; +} + +@end diff --git a/Paystack/PaystackError.m b/Paystack/PaystackError.m new file mode 100644 index 0000000..aaec6eb --- /dev/null +++ b/Paystack/PaystackError.m @@ -0,0 +1,109 @@ +// +// PaystackError.m +// Paystack +// + +#import "PaystackError.h" +#import "PSTCKFormEncoder.h" + +NSString *const PaystackDomain = @"com.paystack.lib"; +NSString *const PSTCKCardErrorCodeKey = @"com.paystack.lib:CardErrorCodeKey"; +NSString *const PSTCKErrorMessageKey = @"com.paystack.lib:ErrorMessageKey"; +NSString *const PSTCKErrorParameterKey = @"com.paystack.lib:ErrorParameterKey"; +NSString *const PSTCKInvalidNumber = @"com.paystack.lib:InvalidNumber"; +NSString *const PSTCKInvalidExpMonth = @"com.paystack.lib:InvalidExpiryMonth"; +NSString *const PSTCKInvalidExpYear = @"com.paystack.lib:InvalidExpiryYear"; +NSString *const PSTCKInvalidCVC = @"com.paystack.lib:InvalidCVC"; +NSString *const PSTCKIncorrectNumber = @"com.paystack.lib:IncorrectNumber"; +NSString *const PSTCKExpiredCard = @"com.paystack.lib:ExpiredCard"; +NSString *const PSTCKCardDeclined = @"com.paystack.lib:CardDeclined"; +NSString *const PSTCKProcessingError = @"com.paystack.lib:ProcessingError"; +NSString *const PSTCKIncorrectCVC = @"com.paystack.lib:IncorrectCVC"; + +@implementation NSError(Paystack) + ++ (NSError *)pstck_errorFromPaystackResponse:(NSDictionary *)jsonDictionary { + NSString *status = [jsonDictionary[@"status"] description]; + if (![status isEqual: @"0"]) { + return nil; + } + + NSString *devMessage = jsonDictionary[@"message"]; + + // There should always be a message for the error + if (devMessage == nil) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: @"Could not interpret the error response that was returned from Paystack.", + PSTCKErrorMessageKey: @"Could not interpret the error response that was returned from Paystack." + }; + return [[NSError alloc] initWithDomain:PaystackDomain code:PSTCKAPIError userInfo:userInfo]; + } + + NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; + userInfo[PSTCKErrorMessageKey] = devMessage; + + + + return [[NSError alloc] initWithDomain:PaystackDomain code:0 userInfo:userInfo]; +} + +- (NSError *)pstck_errorFromPaystackResponseOld:(NSDictionary *)jsonDictionary { + NSDictionary *errorDictionary = jsonDictionary[@"error"]; + if (!errorDictionary) { + return nil; + } + NSString *type = errorDictionary[@"type"]; + NSString *devMessage = errorDictionary[@"message"]; + NSString *parameter = errorDictionary[@"param"]; + NSInteger code = 0; + + // There should always be a message and type for the error + if (devMessage == nil || type == nil) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: @"Could not interpret the error response that was returned from Paystack.", + PSTCKErrorMessageKey: @"Could not interpret the error response that was returned from Paystack." + }; + return [[NSError alloc] initWithDomain:PaystackDomain code:PSTCKAPIError userInfo:userInfo]; + } + + NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; + userInfo[PSTCKErrorMessageKey] = devMessage; + + if (parameter) { + userInfo[PSTCKErrorParameterKey] = [PSTCKFormEncoder stringByReplacingSnakeCaseWithCamelCase:parameter]; + } + + if ([type isEqualToString:@"api_error"]) { + code = PSTCKAPIError; + userInfo[NSLocalizedDescriptionKey] = PSTCKUnexpectedError; + } else if ([type isEqualToString:@"invalid_request_error"]) { + code = PSTCKInvalidRequestError; + userInfo[NSLocalizedDescriptionKey] = devMessage; + } else if ([type isEqualToString:@"card_error"]) { + code = PSTCKCardError; + NSDictionary *errorCodes = @{ + @"incorrect_number": @{@"code": PSTCKIncorrectNumber, @"message": PSTCKCardErrorInvalidNumberUserMessage}, + @"invalid_number": @{@"code": PSTCKInvalidNumber, @"message": PSTCKCardErrorInvalidNumberUserMessage}, + @"invalid_expiry_month": @{@"code": PSTCKInvalidExpMonth, @"message": PSTCKCardErrorInvalidExpMonthUserMessage}, + @"invalid_expiry_year": @{@"code": PSTCKInvalidExpYear, @"message": PSTCKCardErrorInvalidExpYearUserMessage}, + @"invalid_cvc": @{@"code": PSTCKInvalidCVC, @"message": PSTCKCardErrorInvalidCVCUserMessage}, + @"expired_card": @{@"code": PSTCKExpiredCard, @"message": PSTCKCardErrorExpiredCardUserMessage}, + @"incorrect_cvc": @{@"code": PSTCKIncorrectCVC, @"message": PSTCKCardErrorInvalidCVCUserMessage}, + @"card_declined": @{@"code": PSTCKCardDeclined, @"message": PSTCKCardErrorDeclinedUserMessage}, + @"processing_error": @{@"code": PSTCKProcessingError, @"message": PSTCKCardErrorProcessingErrorUserMessage}, + }; + NSDictionary *codeMapEntry = errorCodes[errorDictionary[@"code"]]; + + if (codeMapEntry) { + userInfo[PSTCKCardErrorCodeKey] = codeMapEntry[@"code"]; + userInfo[NSLocalizedDescriptionKey] = codeMapEntry[@"message"]; + } else { + userInfo[PSTCKCardErrorCodeKey] = errorDictionary[@"code"]; + userInfo[NSLocalizedDescriptionKey] = devMessage; + } + } + + return [[NSError alloc] initWithDomain:PaystackDomain code:code userInfo:userInfo]; +} + +@end diff --git a/Paystack/PublicHeaders/PSTCKAPIClient.h b/Paystack/PublicHeaders/PSTCKAPIClient.h new file mode 100644 index 0000000..6757eb3 --- /dev/null +++ b/Paystack/PublicHeaders/PSTCKAPIClient.h @@ -0,0 +1,110 @@ +// +// PSTCKAPIClient.h +// PaystackExample +// + +#import +#if TARGET_OS_IPHONE +#import +#endif + +static NSString *const __nonnull PSTCKSDKVersion = @"3.0.14"; +static NSString *const __nonnull PSTCKSDKBuild = @"18"; + +@class PSTCKCard, PSTCKCardParams, PSTCKTransactionParams, PSTCKToken, PSTCKState; + +/** + * A callback to be run with a token response from the Paystack API. + * + * @param reference The Transaction reference from the response. Will be nil if an error occurs. + * @param error The error returned from the response, or nil in one occurs. @see PaystackError.h for possible values. + */ +typedef void (^PSTCKErrorCompletionBlock)(NSError * __nonnull error, NSString * __nullable reference); +typedef void (^PSTCKTransactionCompletionBlock)(NSString * __nonnull reference); +typedef void (^PSTCKAddressVerficationBlock)(NSString * _Nonnull transaction, NSArray * _Nonnull states); +typedef void (^PSTCKNotifyCompletionBlock)(void); + +/** + A top-level class that imports the rest of the Paystack SDK. This class used to contain several methods to create Paystack tokens, but those are now deprecated in + favor of PSTCKAPIClient. + */ +@interface Paystack : NSObject + +/** + * Set your Paystack API key with this method. New instances of PSTCKAPIClient will be initialized with this value. You should call this method as early as + * possible in your application's lifecycle, preferably in your AppDelegate. + * + * @param publicKey Your public key, obtained from https://paystack.com/account/apikeys + * @warning Make sure not to ship your test API keys to the App Store! This will log a warning if you use your test key in a release build. + */ ++ (void)setDefaultPublicKey:(nonnull NSString *)publicKey; + +/// The current default public key. ++ (nullable NSString *)defaultPublicKey; +@end + +/// A client for making connections to the Paystack API. +@interface PSTCKAPIClient : NSObject + +/** + * A shared singleton API client. Its API key will be initially equal to [Paystack defaultPublicKey]. + */ ++ (nonnull instancetype)sharedClient; +- (nonnull instancetype)initWithPublicKey:(nonnull NSString *)publicKey NS_DESIGNATED_INITIALIZER; + +/** + * @see [Paystack setDefaultPublicKey:] + */ +@property (nonatomic, copy, nullable) NSString *publicKey; + +/** + * The operation queue on which to run completion blocks passed to the api client. Defaults to [NSOperationQueue mainQueue]. + */ +@property (nonatomic, nonnull) NSOperationQueue *operationQueue; + +@end + +#pragma mark Credit Cards + +@interface PSTCKAPIClient (CreditCards) + +/** + * Charges a PSTCKCardParams object using the Paystack API. + * + * @param card The user's card details. Cannot be nil. @see https://paystack.com/docs/api#create_card_token + */ +- (void) chargeCard:(nonnull PSTCKCardParams *)card + forTransaction:(nonnull PSTCKTransactionParams *)transaction + onViewController:(nonnull UIViewController *)viewController + didEndWithError:(nonnull PSTCKErrorCompletionBlock)errorCompletion + didRequestValidation:(nonnull PSTCKTransactionCompletionBlock)beforeValidateCompletion + didTransactionSuccess:(nonnull PSTCKTransactionCompletionBlock)successCompletion; + +/// Charges a card using the Paystack API +/// @param card The user's card details. Cannot be nil +/// @param transaction The transaction parameters +/// @param viewController The viewcontroller where the user entered their card details +/// @param errorCompletion This callback is called when there is an error +/// @param showingDialogCompletion Called before displaying the dialog modal +/// @param dialogDismissedCompletion Called when the dialog modal is dismissed +/// @param successCompletion The callback is called after a successful charge +- (void) chargeCard:(nonnull PSTCKCardParams *)card + forTransaction:(nonnull PSTCKTransactionParams *)transaction + onViewController:(nonnull UIViewController *)viewController + didEndWithError:(nonnull PSTCKErrorCompletionBlock)errorCompletion + didRequestValidation:(nonnull PSTCKTransactionCompletionBlock)beforeValidateCompletion + willPresentDialog:(nonnull PSTCKNotifyCompletionBlock)showingDialogCompletion + dismissedDialog:(nonnull PSTCKNotifyCompletionBlock)dialogDismissedCompletion + didTransactionSuccess:(nonnull PSTCKTransactionCompletionBlock)successCompletion; + +- (void) chargeCard:(nonnull PSTCKCardParams *)card + forTransaction:(nonnull PSTCKTransactionParams *)transaction + onViewController:(nonnull UIViewController *)viewController + didEndWithError:(nonnull PSTCKErrorCompletionBlock)errorCompletion + willPresentDialog:(nonnull PSTCKNotifyCompletionBlock)showingDialogCompletion + dismissedDialog:(nonnull PSTCKNotifyCompletionBlock)dialogDismissedCompletion + didTransactionSuccess:(nonnull PSTCKTransactionCompletionBlock)successCompletion; + +- (void) setProcessingStatus:(Boolean)status; + +@end diff --git a/Paystack/PublicHeaders/PSTCKAPIResponseDecodable.h b/Paystack/PublicHeaders/PSTCKAPIResponseDecodable.h new file mode 100644 index 0000000..4544dc2 --- /dev/null +++ b/Paystack/PublicHeaders/PSTCKAPIResponseDecodable.h @@ -0,0 +1,25 @@ +// +// PSTCKAPIResponseDecodable.h +// Paystack +// + +#import + +@protocol PSTCKAPIResponseDecodable + +/** + * These fields are required to be present in the API response. If any of them are nil, decodedObjectFromAPIResponse should also return nil. + */ ++ (nonnull NSArray *)requiredFields; + +/** + * Parses an response from the Paystack API (in JSON format; represented as an NSDictionary) into an instance of the class. Returns nil if the object could not be decoded (i.e. if one of its `requiredFields` is nil). + */ ++ (nullable instancetype)decodedObjectFromAPIResponse:(nonnull NSDictionary *)response; + +/** + * The raw JSON response used to create the object. This can be useful for using beta features that haven't yet been made into properties in the SDK. + */ +@property(nonatomic, readonly, nonnull, copy)NSDictionary *allResponseFields; + +@end diff --git a/Paystack/PublicHeaders/PSTCKCard.h b/Paystack/PublicHeaders/PSTCKCard.h new file mode 100644 index 0000000..500b93f --- /dev/null +++ b/Paystack/PublicHeaders/PSTCKCard.h @@ -0,0 +1,122 @@ +// +// PSTCKCard.h +// Paystack +// + +#import + +#import "PSTCKCardBrand.h" +#import "PSTCKCardParams.h" +#import "PSTCKAPIResponseDecodable.h" + +/** + * The various funding sources for a payment card. + */ +typedef NS_ENUM(NSInteger, PSTCKCardFundingType) { + PSTCKCardFundingTypeDebit, + PSTCKCardFundingTypeCredit, + PSTCKCardFundingTypePrepaid, + PSTCKCardFundingTypeOther, +}; + +/** + * Representation of a user's credit card details that have been tokenized with the Paystack API. @see https://paystack.com/docs/api#cards + */ +@interface PSTCKCard : PSTCKCardParams + +/** + * The card's number. This will be nil for cards retrieved from the Paystack API. + */ + + +/** + * The last 4 digits of the card. + */ +@property (nonatomic, readonly, nonnull) NSString *last4; + +/** + * For cards made with Apple Pay, this refers to the last 4 digits of the "Device Account Number" for the tokenized card. For regular cards, it will be nil. + */ +@property (nonatomic, readonly, nullable) NSString *dynamicLast4; + +/** + * The card's expiration month. + */ +@property (nonatomic) NSUInteger expMonth; + +/** + * The card's expiration year. + */ +@property (nonatomic) NSUInteger expYear; + +/** + * The cardholder's name. + */ +@property (nonatomic, copy, nullable) NSString *name; + +/** + * The cardholder's address. + */ +@property (nonatomic, copy, nullable) NSString *addressLine1; +@property (nonatomic, copy, nullable) NSString *addressLine2; +@property (nonatomic, copy, nullable) NSString *addressCity; +@property (nonatomic, copy, nullable) NSString *addressState; +@property (nonatomic, copy, nullable) NSString *addressZip; +@property (nonatomic, copy, nullable) NSString *addressCountry; + +/** + * The Paystack ID for the card. + */ +@property (nonatomic, readonly, nullable) NSString *cardId; + +/** + * The issuer of the card. + */ +@property (nonatomic, readonly) PSTCKCardBrand brand; + +/** + * The issuer of the card. + * Can be one of "Visa", "American Express", "MasterCard", "Discover", "JCB", "Diners Club", or "Unknown" + * @deprecated use "brand" instead. + */ +@property (nonatomic, readonly, nonnull) NSString *type __attribute__((deprecated)); + +/** + * The funding source for the card (credit, debit, prepaid, or other) + */ +@property (nonatomic, readonly) PSTCKCardFundingType funding; + +/** + * A proxy for the card's number, this uniquely identifies the credit card and can be used to compare different cards. + * @deprecated This field will no longer be present in responses when using your public key. If you want to access the value of this field, you can look it up on your backend using your secret key. + */ +@property (nonatomic, readonly, nullable) NSString *fingerprint __attribute__((deprecated("This field will no longer be present in responses when using your public key. If you want to access the value of this field, you can look it up on your backend using your secret key."))); + +/** + * Two-letter ISO code representing the issuing country of the card. + */ +@property (nonatomic, readonly, nullable) NSString *country; + +/** + * This is only applicable when tokenizing debit cards to issue payouts to managed accounts. You should not set it otherwise. The card can then be used as a transfer destination for funds in this currency. + */ +@property (nonatomic, copy, nullable) NSString *currency; + +#pragma mark - deprecated properties + +#define DEPRECATED_IN_FAVOR_OF_PSTCKCARDPARAMS __attribute__((deprecated("For collecting your users' credit card details, you should use an PSTCKCardParams object instead of an PSTCKCard."))) + +@property (nonatomic, copy, nullable) NSString *number DEPRECATED_IN_FAVOR_OF_PSTCKCARDPARAMS; +@property (nonatomic, copy, nullable) NSString *cvc DEPRECATED_IN_FAVOR_OF_PSTCKCARDPARAMS; +- (void)setExpMonth:(NSUInteger)expMonth DEPRECATED_IN_FAVOR_OF_PSTCKCARDPARAMS; +- (void)setExpYear:(NSUInteger)expYear DEPRECATED_IN_FAVOR_OF_PSTCKCARDPARAMS; +- (void)setName:(nullable NSString *)name DEPRECATED_IN_FAVOR_OF_PSTCKCARDPARAMS; +- (void)setAddressLine1:(nullable NSString *)addressLine1 DEPRECATED_IN_FAVOR_OF_PSTCKCARDPARAMS; +- (void)setAddressLine2:(nullable NSString *)addressLine2 DEPRECATED_IN_FAVOR_OF_PSTCKCARDPARAMS; +- (void)setAddressCity:(nullable NSString *)addressCity DEPRECATED_IN_FAVOR_OF_PSTCKCARDPARAMS; +- (void)setAddressState:(nullable NSString *)addressState DEPRECATED_IN_FAVOR_OF_PSTCKCARDPARAMS; +- (void)setAddressZip:(nullable NSString *)addressZip DEPRECATED_IN_FAVOR_OF_PSTCKCARDPARAMS; +- (void)setAddressCountry:(nullable NSString *)addressCountry DEPRECATED_IN_FAVOR_OF_PSTCKCARDPARAMS; + + +@end diff --git a/Paystack/PublicHeaders/PSTCKCardBrand.h b/Paystack/PublicHeaders/PSTCKCardBrand.h new file mode 100644 index 0000000..1f5beb5 --- /dev/null +++ b/Paystack/PublicHeaders/PSTCKCardBrand.h @@ -0,0 +1,20 @@ +// +// PSTCKCardBrand.h +// Paystack +// + +#import + +/** + * The various card brands to which a payment card can belong. + */ +typedef NS_ENUM(NSInteger, PSTCKCardBrand) { + PSTCKCardBrandVisa, + PSTCKCardBrandAmex, + PSTCKCardBrandMasterCard, + PSTCKCardBrandDiscover, + PSTCKCardBrandJCB, + PSTCKCardBrandDinersClub, + PSTCKCardBrandUnknown, + PSTCKCardBrandVerve, +}; diff --git a/Paystack/PublicHeaders/PSTCKCardParams.h b/Paystack/PublicHeaders/PSTCKCardParams.h new file mode 100644 index 0000000..172d3b5 --- /dev/null +++ b/Paystack/PublicHeaders/PSTCKCardParams.h @@ -0,0 +1,46 @@ +// +// PSTCKCardParams.h +// Paystack +// + +#import +#import "PSTCKFormEncodable.h" +/** + * Representation of a user's credit card details. You can assemble these with information that your user enters and + * then create Paystack tokens with them using an PSTCKAPIClient. @see https://paystack.com/docs/api#cards + */ +@interface PSTCKCardParams : NSObject + +/** + * The card's number. + */ +@property (nonatomic, copy, nullable) NSString *number; + +/** + * The last 4 digits of the card's number, if it's been set, otherwise nil. + */ +- (nullable NSString *)last4; + +/** + * The clientdata to send to api. + */ +- (nullable NSString *)clientdata; + +/** + * The card's expiration month. + */ +@property (nonatomic) NSUInteger expMonth; + +/** + * The card's expiration year. + */ +@property (nonatomic) NSUInteger expYear; + +/** + * The card's security code, found on the back. + */ +@property (nonatomic, copy, nullable) NSString *cvc; + + + +@end diff --git a/Paystack/PublicHeaders/PSTCKCardValidationState.h b/Paystack/PublicHeaders/PSTCKCardValidationState.h new file mode 100644 index 0000000..01a10ab --- /dev/null +++ b/Paystack/PublicHeaders/PSTCKCardValidationState.h @@ -0,0 +1,15 @@ +// +// PSTCKCardValidationState.h +// Paystack +// + +#import + +/** + * These fields indicate whether a card field represents a valid value, invalid value, or incomplete value. + */ +typedef NS_ENUM(NSInteger, PSTCKCardValidationState) { + PSTCKCardValidationStateValid, // The field's contents are valid. For example, a valid, 16-digit card number. + PSTCKCardValidationStateInvalid, // The field's contents are invalid. For example, an expiration date of "13/42". + PSTCKCardValidationStateIncomplete, // The field's contents are not yet valid, but could be by typing additional characters. For example, a CVC of "1". +}; diff --git a/Paystack/PublicHeaders/PSTCKCardValidator.h b/Paystack/PublicHeaders/PSTCKCardValidator.h new file mode 100644 index 0000000..fed8b9f --- /dev/null +++ b/Paystack/PublicHeaders/PSTCKCardValidator.h @@ -0,0 +1,103 @@ +// +// PSTCKCardValidator.h +// Paystack +// + +#import + +#import "PSTCKCardParams.h" +#import "PSTCKCardBrand.h" +#import "PSTCKCardValidationState.h" + +/** + * This class contains static methods to validate card numbers, expiration dates, and CVCs. For a list of test card numbers to use with this code, see https://paystack.com/docs/testing + */ +@interface PSTCKCardValidator : NSObject + +/** + * Returns a copy of the passed string with all non-numeric characters removed. + */ ++ (nonnull NSString *)sanitizedNumericStringForString:(nonnull NSString *)string; + +/** + * Whether or not the target string contains only numeric characters. + */ ++ (BOOL)stringIsNumeric:(nonnull NSString *)string; + +/** + * Validates a card number, passed as a string. This will return PSTCKCardValidationStateInvalid for numbers that are too short or long, contain invalid characters, do not pass Luhn validation, or (optionally) do not match a number format issued by a major card brand. + * + * @param cardNumber The card number to validate. Ex. @"4242424242424242" + * @param validatingCardBrand Whether or not to enforce that the number appears to be issued by a major card brand (or could be). For example, no issuing card network currently issues card numbers beginning with the digit 9; if an otherwise correct-length and luhn-valid card number beginning with 9 (example: 9999999999999995) were passed to this method, it would return PSTCKCardValidationStateInvalid if this parameter were YES and PSTCKCardValidationStateValid if this parameter were NO. If unsure, you should use YES for this value. + * + * @return PSTCKCardValidationStateValid if the number is valid, PSTCKCardValidationStateInvalid if the number is invalid, or PSTCKCardValidationStateIncomplete if the number is a substring of a valid card (e.g. @"4242"). + */ ++ (PSTCKCardValidationState)validationStateForNumber:(nonnull NSString *)cardNumber validatingCardBrand:(BOOL)validatingCardBrand; + +/** + * The card brand for a card number or substring thereof. + * + * @param cardNumber A card number, or partial card number. For example, @"4242", @"5555555555554444", or @"123". + * + * @return The brand for that card number. The example parameters would return PSTCKCardBrandVisa, PSTCKCardBrandMasterCard, and PSTCKCardBrandUnknown, respectively. + */ ++ (PSTCKCardBrand)brandForNumber:(nonnull NSString *)cardNumber; + +/** + * The number length for cards associated with a card brand. For example, Visa card numbers contain 16 characters, while American Express cards contain 15 characters. + */ ++ (NSUInteger)lengthForCardBrand:(PSTCKCardBrand)brand; + +/** + * The length of the final grouping of digits to use when formatting a card number for display. For example, Visa cards display their final 4 numbers, e.g. "4242", while American Express cards display their final 5 digits, e.g. "10005". + */ ++ (NSInteger)fragmentLengthForCardBrand:(PSTCKCardBrand)brand; + +/** + * Validates an expiration month, passed as an (optionally 0-padded) string. Example valid values are "3", "12", and "08". Example invalid values are "99", "a", and "00". Incomplete values include "0" and "1". + * + * @param expirationMonth A string representing a 2-digit expiration month for a payment card. + * + * @return PSTCKCardValidationStateValid if the month is valid, PSTCKCardValidationStateInvalid if the month is invalid, or PSTCKCardValidationStateIncomplete if the month is a substring of a valid month (e.g. @"0" or @"1"). + */ ++ (PSTCKCardValidationState)validationStateForExpirationMonth:(nonnull NSString *)expirationMonth; + +/** + * Validates an expiration year, passed as a string representing the final 2 digits of the year. This considers the period between the current year until 2099 as valid times. An example valid value would be "16" (assuming the current year, as determined by [NSDate date], is 2015). Will return PSTCKCardValidationStateInvalid for a month/year combination that is earlier than the current date (i.e. @"15" and @"04" in October 2015. Example invalid values are "00", "a", and "13". Any 1-digit string will return PSTCKCardValidationStateIncomplete. + * + * @param expirationYear A string representing a 2-digit expiration year for a payment card. + * @param expirationMonth A string representing a 2-digit expiration month for a payment card. See -validationStateForExpirationMonth for the desired formatting of this string. + * + * @return PSTCKCardValidationStateValid if the year is valid, PSTCKCardValidationStateInvalid if the year is invalid, or PSTCKCardValidationStateIncomplete if the year is a substring of a valid year (e.g. @"1" or @"2"). + */ ++ (PSTCKCardValidationState)validationStateForExpirationYear:(nonnull NSString *)expirationYear inMonth:(nonnull NSString *)expirationMonth; + +/** + * The max CVC length for a card brand (for context, American Express CVCs are 4 digits, while all others are 3). + */ ++ (NSUInteger)maxCVCLengthForCardBrand:(PSTCKCardBrand)brand; + +/** + * Validates a card's CVC, passed as a numeric string, for the given card brand. + * + * @param cvc the CVC to validate + * @param brand the card brand (can be determined from the card's number using +brandForNumber) + * + * @return Whether the CVC represents a valid CVC for that card brand. For example, would return PSTCKCardValidationStateValid for @"123" and PSTCKCardBrandVisa, PSTCKCardValidationStateValid for @"1234" and PSTCKCardBrandAmericanExpress, PSTCKCardValidationStateIncomplete for @"12" and PSTCKCardBrandVisa, and PSTCKCardValidationStateInvalid for @"12345" and any brand. + */ ++ (PSTCKCardValidationState)validationStateForCVC:(nonnull NSString *)cvc cardBrand:(PSTCKCardBrand)brand; + +/** + * Validates the given card details. + * + * @param card the card details to validate. + * + * @return PSTCKCardValidationStateValid if all fields are valid, PSTCKCardValidationStateInvalid if any field is invalid, or PSTCKCardValidationStateIncomplete if all fields are either incomplete or valid. + */ ++ (PSTCKCardValidationState)validationStateForCard:(nonnull PSTCKCardParams *)card; + +// Exposed for testing only. ++ (PSTCKCardValidationState)validationStateForExpirationYear:(nonnull NSString *)expirationYear inMonth:(nonnull NSString *)expirationMonth inCurrentYear:(NSInteger)currentYear currentMonth:(NSInteger)currentMonth; ++ (PSTCKCardValidationState)validationStateForCard:(nonnull PSTCKCardParams *)card inCurrentYear:(NSInteger)currentYear currentMonth:(NSInteger)currentMonth; + +@end diff --git a/Paystack/PublicHeaders/PSTCKFormEncodable.h b/Paystack/PublicHeaders/PSTCKFormEncodable.h new file mode 100644 index 0000000..0a898e3 --- /dev/null +++ b/Paystack/PublicHeaders/PSTCKFormEncodable.h @@ -0,0 +1,32 @@ +// +// PSTCKFormEncodable.h +// Paystack +// + +#import + +/** + * Objects conforming to PSTCKFormEncodable can be automatically converted to a form-encoded string, which can then be used when making requests to the Paystack API. + */ +@protocol PSTCKFormEncodable + +/** + * The root object name to be used when converting this object to a form-encoded string. For example, if this returns @"card", then the form-encoded output will resemble @"card[foo]=bar" (where 'foo' and 'bar' are specified by `propertyNamesToFormFieldNamesMapping` below. + */ ++ (nonnull NSString *)rootObjectName; + +/** + * This maps properties on an object that is being form-encoded into parameter names in the Paystack API. For example, PSTCKCardParams has a field called `expMonth`, but the Paystack API expects a field called `exp_month`. This dictionary represents a mapping from the former to the latter (in other words, [PSTCKCardParams propertyNamesToFormFieldNamesMapping][@"expMonth"] == @"exp_month".) + */ ++ (nonnull NSDictionary *)propertyNamesToFormFieldNamesMapping; + +/** + * You can use this property to add additional fields to an API request that are not explicitly defined by the object's interface. This can be useful when using beta features that haven't been added to the Paystack SDK yet. For example, if the /v1/tokens API began to accept a beta field called "test_field", you might do the following: + PSTCKCardParams *cardParams = [PSTCKCardParams new]; + // add card values + cardParams.additionalAPIParameters = @{@"test_field": @"example_value"}; + [[PSTCKAPIClient sharedClient] createTokenWithCard:cardParams completion:...]; + */ +@property(nonatomic, readwrite, nonnull, copy)NSDictionary *additionalAPIParameters; + +@end diff --git a/Paystack/PublicHeaders/PSTCKToken.h b/Paystack/PublicHeaders/PSTCKToken.h new file mode 100644 index 0000000..567c9f4 --- /dev/null +++ b/Paystack/PublicHeaders/PSTCKToken.h @@ -0,0 +1,33 @@ +// +// PSTCKToken.h +// Paystack +// + +#import +#import "PSTCKAPIResponseDecodable.h" + + +/** + * A token returned from submitting payment details to the Paystack API. You should not have to instantiate one of these directly. + */ +@interface PSTCKToken : NSObject + +/** + * You cannot directly instantiate an PSTCKToken. You should only use one that has been returned from an PSTCKAPIClient callback. + */ +- (nonnull instancetype) init __attribute__((unavailable("You cannot directly instantiate an PSTCKToken. You should only use one that has been returned from an PSTCKAPIClient callback."))); + +/** + * The value of the token. You can store this value on your server and use it to make charges and customers. @see + * https://paystack.com/docs/mobile/ios#sending-tokens + */ +@property (nonatomic, readonly, nonnull) NSString *tokenId; +@property (nonatomic, readonly, nonnull) NSString *message; +@property (nonatomic, readonly, nonnull) NSString *last4; + + +/** + * When the token was created. + */ + +@end diff --git a/Paystack/PublicHeaders/PSTCKTransaction.h b/Paystack/PublicHeaders/PSTCKTransaction.h new file mode 100644 index 0000000..27f8928 --- /dev/null +++ b/Paystack/PublicHeaders/PSTCKTransaction.h @@ -0,0 +1,31 @@ +// +// PSTCKTransaction.h +// Paystack +// + +#import +#import "PSTCKAPIResponseDecodable.h" + + +/** + * A transaction returned from submitting payment details to the Paystack API. You should not have to instantiate one of these directly. + */ +@interface PSTCKTransaction : NSObject + +/** + * You cannot directly instantiate an PSTCKTransaction. You should only use one that has been returned from an PSTCKAPIClient callback. + */ +- (nonnull instancetype) init __attribute__((unavailable("You cannot directly instantiate an PSTCKTransaction. You should only use one that has been returned from an PSTCKAPIClient callback."))); + +@property (nonatomic, readonly, nonnull) NSString *reference; +@property (nonatomic, readonly, nonnull) NSString *message; +@property (nonatomic, readonly, nonnull) NSString *status; +@property (nonatomic, readonly, nonnull) NSString *trans; +@property (nonatomic, readonly, nonnull) NSString *redirecturl; +@property (nonatomic, readonly, nonnull) NSString *auth; +@property (nonatomic, readonly, nonnull) NSString *otpmessage; +@property (nonatomic, readonly, nonnull) NSString *countrycode; +@property (nonatomic, readonly, nonnull) NSString *errors; + + +@end diff --git a/Paystack/PublicHeaders/PSTCKTransactionParams.h b/Paystack/PublicHeaders/PSTCKTransactionParams.h new file mode 100644 index 0000000..85b31db --- /dev/null +++ b/Paystack/PublicHeaders/PSTCKTransactionParams.h @@ -0,0 +1,40 @@ +// +// PSTCKTransactionParams.h +// Paystack +// + +#import +#import "PSTCKFormEncodable.h" +/** + * Representation of the transaction to perform on a card + */ +@interface PSTCKTransactionParams : NSObject + +@property (nonatomic, copy, nonnull) NSString *access_code; + +@property (nonatomic, copy, nonnull) NSString *email; +@property (nonatomic) NSUInteger amount; +@property (nonatomic, copy, nullable) NSString *reference; +@property (nonatomic, copy, nullable) NSString *subaccount; +@property (nonatomic) NSInteger transaction_charge; +@property (nonatomic, copy, nullable) NSString *bearer; +@property (nonatomic, readonly, nullable) NSString *metadata; +@property (nonatomic, nullable) NSString *plan; +@property (nonatomic, nullable) NSString *currency; + +- (nullable PSTCKTransactionParams *) setMetadataValue:(nonnull NSString*)value + forKey:(nonnull NSString*)key + error:(NSError * _Nullable __autoreleasing * _Nonnull) error; + +- (nullable PSTCKTransactionParams *) setMetadataValueDict:(nonnull NSMutableDictionary*)dict + forKey:(nonnull NSString*)key + error:(NSError * _Nullable __autoreleasing * _Nonnull) error; + +- (nullable PSTCKTransactionParams *) setMetadataValueArray:(nonnull NSMutableArray*)arr + forKey:(nonnull NSString*)key + error:(NSError * _Nullable __autoreleasing * _Nonnull) error; + +- (nullable PSTCKTransactionParams *) setCustomFieldValue:(nonnull NSString*)value + displayedAs:(nonnull NSString*)display_name + error:(NSError * _Nullable __autoreleasing * _Nonnull) error; +@end diff --git a/Paystack/PublicHeaders/PSTCKValidationParams.h b/Paystack/PublicHeaders/PSTCKValidationParams.h new file mode 100644 index 0000000..0e6994f --- /dev/null +++ b/Paystack/PublicHeaders/PSTCKValidationParams.h @@ -0,0 +1,17 @@ +// +// PSTCKValidationParams.h +// Paystack +// + +#import +#import "PSTCKFormEncodable.h" +/** + * Representation of a user's credit card details. You can assemble these with information that your user enters and + * then create Paystack tokens with them using an PSTCKAPIClient. @see https://paystack.com/docs/api#cards + */ +@interface PSTCKValidationParams : NSObject + +@property (nonatomic, copy, nonnull) NSString *trans; +@property (nonatomic, copy, nonnull) NSString *token; + +@end diff --git a/Paystack/PublicHeaders/Paystack.h b/Paystack/PublicHeaders/Paystack.h new file mode 100644 index 0000000..40b2217 --- /dev/null +++ b/Paystack/PublicHeaders/Paystack.h @@ -0,0 +1,25 @@ +// +// Paystack.h +// Paystack +// +// Created by Ibrahim Lawal on 02/02/16. +// Copyright (c) 2016 Paystack. All rights reserved. +// +// The code in this workspace was adapted from https://github.com/stripe/stripe-ios. +// Stripe was replaced with Paystack - and STP with PSTCK - to avoid collisions within +// apps that are using both Paystack and Stripe. + +#import "PSTCKAPIClient.h" +#import "PaystackError.h" +#import "PSTCKCardBrand.h" +#import "PSTCKCardParams.h" +#import "PSTCKTransactionParams.h" +#import "PSTCKCard.h" +#import "PSTCKCardValidationState.h" +#import "PSTCKCardValidator.h" +#import "PSTCKToken.h" +#import "PSTCKRSA.h" + +#if TARGET_OS_IPHONE +#import "PSTCKPaymentCardTextField.h" +#endif diff --git a/Paystack/PublicHeaders/PaystackError.h b/Paystack/PublicHeaders/PaystackError.h new file mode 100644 index 0000000..e9ab693 --- /dev/null +++ b/Paystack/PublicHeaders/PaystackError.h @@ -0,0 +1,75 @@ +// +// PaystackError.h +// Paystack +// + +#import + +/** + * All Paystack iOS errors will be under this domain. + */ +FOUNDATION_EXPORT NSString * __nonnull const PaystackDomain; + +typedef NS_ENUM(NSInteger, PSTCKErrorCode) { + PSTCKConnectionError = 40, // Trouble connecting to Paystack. + PSTCKInvalidRequestError = 50, // Your request had invalid parameters. + PSTCKAPIError = 60, // General-purpose API error (should be rare). + PSTCKCardError = 70, // Something was wrong with the given card (most common). + PSTCKCardErrorProcessingError = 80, // Paystack Checkout encountered an error. + PSTCKTransactionError = 90, // Something was wrong with the given transaction details. + PSTCKConflictError = 100, // A transaction was started while SDK was processing another + PSTCKExpiredAccessCodeError = 110, // The access code is not usable +}; + +#pragma mark userInfo keys + +// A developer-friendly error message that explains what went wrong. You probably +// shouldn't show this to your users, but might want to use it yourself. +FOUNDATION_EXPORT NSString * __nonnull const PSTCKErrorMessageKey; + +// What went wrong with your PSTCKCard (e.g., PSTCKInvalidCVC. See below for full list). +FOUNDATION_EXPORT NSString * __nonnull const PSTCKCardErrorCodeKey; + +// Which parameter on the PSTCKCard had an error (e.g., "cvc"). Useful for marking up the +// right UI element. +FOUNDATION_EXPORT NSString * __nonnull const PSTCKErrorParameterKey; + +#pragma mark PSTCKCardErrorCodeKeys + +// (Usually determined locally:) +FOUNDATION_EXPORT NSString * __nonnull const PSTCKInvalidNumber; +FOUNDATION_EXPORT NSString * __nonnull const PSTCKInvalidExpMonth; +FOUNDATION_EXPORT NSString * __nonnull const PSTCKInvalidExpYear; +FOUNDATION_EXPORT NSString * __nonnull const PSTCKInvalidCVC; + +// (Usually sent from the server:) +FOUNDATION_EXPORT NSString * __nonnull const PSTCKIncorrectNumber; +FOUNDATION_EXPORT NSString * __nonnull const PSTCKExpiredCard; +FOUNDATION_EXPORT NSString * __nonnull const PSTCKCardDeclined; +FOUNDATION_EXPORT NSString * __nonnull const PSTCKProcessingError; +FOUNDATION_EXPORT NSString * __nonnull const PSTCKIncorrectCVC; + +#pragma mark Strings + +#define PSTCKExpiredAccessCodeErrorMessage NSLocalizedString(@"There was a problem completing your request", @"Error when access code has no valid transaction") +#define PSTCKCardErrorInvalidNumberUserMessage NSLocalizedString(@"Your card's number is invalid", @"Error when the card number is not valid") +#define PSTCKCardErrorProcessingTransactionMessage NSLocalizedString(@"Please wait", @"Error when chargeCard is called while the SDK is still processing a transaction") +#define PSTCKCardErrorInvalidCVCUserMessage NSLocalizedString(@"Your card's security code is invalid", @"Error when the card's CVC is not valid") +#define PSTCKCardErrorInvalidExpMonthUserMessage \ + NSLocalizedString(@"Your card's expiration month is invalid", @"Error when the card's expiration month is not valid") +#define PSTCKCardErrorInvalidExpYearUserMessage \ +NSLocalizedString(@"Your card's expiration year is invalid", @"Error when the card's expiration year is not valid") +#define PSTCKTransactionErrorDontSetCustomFieldDirectlyMessage \ + NSLocalizedString(@"Please use the setCustomField function", @"Error when the app attempts to set the custom_fields key directly") +#define PSTCKCardErrorExpiredCardUserMessage NSLocalizedString(@"Your card has expired", @"Error when the card has already expired") +#define PSTCKCardErrorDeclinedUserMessage NSLocalizedString(@"Your card was declined", @"Error when the card was declined by the credit card networks") +#define PSTCKUnexpectedError \ + NSLocalizedString(@"There was an unexpected error -- try again in a few seconds", @"Unexpected error, such as a 500 from Paystack or a JSON parse error") +#define PSTCKCardErrorProcessingErrorUserMessage \ + NSLocalizedString(@"There was an error processing your card -- try again in a few seconds", @"Error when there is a problem processing the credit card") + +@interface NSError(Paystack) + ++ (nullable NSError *)pstck_errorFromPaystackResponse:(nullable NSDictionary *)jsonDictionary; + +@end diff --git a/Paystack/PublicHeaders/UI/PSTCKPaymentCardTextField.h b/Paystack/PublicHeaders/UI/PSTCKPaymentCardTextField.h new file mode 100644 index 0000000..4a8e991 --- /dev/null +++ b/Paystack/PublicHeaders/UI/PSTCKPaymentCardTextField.h @@ -0,0 +1,238 @@ +// +// PSTCKPaymentCardTextField.h +// Paystack +// + +#import + +#import "PSTCKCard.h" + +@class PSTCKPaymentCardTextField; + +/** + * This protocol allows a delegate to be notified when a payment text field's contents change, which can in turn be used to take further actions depending on the validity of its contents. + */ +@protocol PSTCKPaymentCardTextFieldDelegate +@optional +/** + * Called when either the card number, expiration, or CVC changes. At this point, one can call -isValid on the text field to determine, for example, whether or not to enable a button to submit the form. Example: + + - (void)paymentCardTextFieldDidChange:(PSTCKPaymentCardTextField *)textField { + self.paymentButton.enabled = textField.isValid; + } + + * + * @param textField the text field that has changed + */ +- (void)paymentCardTextFieldDidChange:(nonnull PSTCKPaymentCardTextField *)textField; + +/** + * Called when editing begins in the payment card field's number field. + */ +- (void)paymentCardTextFieldDidBeginEditingNumber:(nonnull PSTCKPaymentCardTextField *)textField; + +/** + * Called when editing begins in the payment card field's CVC field. + */ +- (void)paymentCardTextFieldDidBeginEditingCVC:(nonnull PSTCKPaymentCardTextField *)textField; + +/** + * Called when editing begins in the payment card field's expiration field. + */ +- (void)paymentCardTextFieldDidBeginEditingExpiration:(nonnull PSTCKPaymentCardTextField *)textField; + +@end + + +/** + * PSTCKPaymentCardTextField is a text field with similar properties to UITextField, but specialized for collecting credit/debit card information. It manages multiple UITextFields under the hood to collect this information. It's designed to fit on a single line, and from a design perspective can be used anywhere a UITextField would be appropriate. + */ +@interface PSTCKPaymentCardTextField : UIControl + +/** + * @see PSTCKPaymentCardTextFieldDelegate + */ +@property(nonatomic, weak, nullable) IBOutlet id delegate; + +/** + * The font used in each child field. Default is [UIFont systemFontOfSize:18]. Set this property to nil to reset to the default. + */ +@property(nonatomic, copy, null_resettable) UIFont *font UI_APPEARANCE_SELECTOR; + +/** + * The text color to be used when entering valid text. Default is [UIColor blackColor]. Set this property to nil to reset to the default. + */ +@property(nonatomic, copy, null_resettable) UIColor *textColor UI_APPEARANCE_SELECTOR; + +/** + * The text color to be used when the user has entered invalid information, such as an invalid card number. Default is [UIColor redColor]. Set this property to nil to reset to the default. + */ +@property(nonatomic, copy, null_resettable) UIColor *textErrorColor UI_APPEARANCE_SELECTOR; + +/** + * The text placeholder color used in each child field. Default is [UIColor lightGreyColor]. Set this property to nil to reset to the default. On iOS 7 and above, this will also set the color of the card placeholder icon. + */ +@property(nonatomic, copy, null_resettable) UIColor *placeholderColor UI_APPEARANCE_SELECTOR; + +/** + * The placeholder for the card number field. Default is @"1234567812345678". If this is set to something that resembles a card number, it will automatically format it as such (in other words, you don't need to add spaces to this string). + */ +@property(nonatomic, copy, nullable) NSString *numberPlaceholder; + +/** + * The placeholder for the expiration field. Defaults to @"MM/YY". + */ +@property(nonatomic, copy, nullable) NSString *expirationPlaceholder; + +/** + * The placeholder for the cvc field. Defaults to @"CVC". + */ +@property(nonatomic, copy, nullable) NSString *cvcPlaceholder; + +/** + * The cursor color for the field. This is a proxy for the view's tintColor property, exposed for clarity only (in other words, calling setCursorColor is identical to calling setTintColor). + */ +@property(nonatomic, copy, null_resettable) UIColor *cursorColor UI_APPEARANCE_SELECTOR; + +/** + * The border color for the field. Default is [UIColor lightGreyColor]. Can be nil (in which case no border will be drawn). + */ +@property(nonatomic, copy, nullable) UIColor *borderColor UI_APPEARANCE_SELECTOR; + +/** + * The width of the field's border. Default is 1.0. + */ +@property(nonatomic, assign) CGFloat borderWidth UI_APPEARANCE_SELECTOR; + +/** + * The corner radius for the field's border. Default is 5.0. + */ +@property(nonatomic, assign) CGFloat cornerRadius UI_APPEARANCE_SELECTOR; + +/** + * The keyboard appearance for the field. Default is UIKeyboardAppearanceDefault. + */ +@property(nonatomic, assign) UIKeyboardAppearance keyboardAppearance UI_APPEARANCE_SELECTOR; + +/** + * This behaves identically to setting the inputAccessoryView for each child text field. + */ +@property(nonatomic, strong, nullable) UIView *inputAccessoryView; + +/** + * The curent brand image displayed in the receiver. + */ +@property (nonatomic, readonly, nullable) UIImage *brandImage; + +/** + * Causes the text field to begin editing. Presents the keyboard. + * + * @return Whether or not the text field successfully began editing. + * @see UIResponder + */ +- (BOOL)becomeFirstResponder; + +/** + * Causes the text field to stop editing. Dismisses the keyboard. + * + * @return Whether or not the field successfully stopped editing. + * @see UIResponder + */ +- (BOOL)resignFirstResponder; + +/** + * Resets all of the contents of all of the fields. If the field is currently being edited, the number field will become selected. + */ +- (void)clear; + +/** + * Returns the cvc image used for a card brand. + * Override this method in a subclass if you would like to provide custom images. + * @param cardBrand The brand of card entered. + * @return The cvc image used for a card brand. + */ ++ (nullable UIImage *)cvcImageForCardBrand:(PSTCKCardBrand)cardBrand; + +/** + * Returns the brand image used for a card brand. + * Override this method in a subclass if you would like to provide custom images. + * @param cardBrand The brand of card entered. + * @return The brand image used for a card brand. + */ ++ (nullable UIImage *)brandImageForCardBrand:(PSTCKCardBrand)cardBrand; + +/** + * Returns the rectangle in which the receiver draws its brand image. + * @param bounds The bounding rectangle of the receiver. + * @return the rectangle in which the receiver draws its brand image. + */ +- (CGRect)brandImageRectForBounds:(CGRect)bounds; + +/** + * Returns the rectangle in which the receiver draws the text fields. + * @param bounds The bounding rectangle of the receiver. + * @return The rectangle in which the receiver draws the text fields. + */ +- (CGRect)fieldsRectForBounds:(CGRect)bounds; + +/** + * Returns the rectangle in which the receiver draws its number field. + * @param bounds The bounding rectangle of the receiver. + * @return the rectangle in which the receiver draws its number field. + */ +- (CGRect)numberFieldRectForBounds:(CGRect)bounds; + +/** + * Returns the rectangle in which the receiver draws its cvc field. + * @param bounds The bounding rectangle of the receiver. + * @return the rectangle in which the receiver draws its cvc field. + */ +- (CGRect)cvcFieldRectForBounds:(CGRect)bounds; + +/** + * Returns the rectangle in which the receiver draws its expiration field. + * @param bounds The bounding rectangle of the receiver. + * @return the rectangle in which the receiver draws its expiration field. + */ +- (CGRect)expirationFieldRectForBounds:(CGRect)bounds; + +/** + * Whether or not the form currently contains a valid card number, expiration date, and CVC. + * @see PSTCKCardValidator + */ +@property(nonatomic, readonly, getter=isValid)BOOL valid; + +/** + * Enable/disable selecting or editing the field. Useful when submitting card details to Paystack. + */ +@property(nonatomic, getter=isEnabled) BOOL enabled; + +/** + * The current card number displayed by the field. May or may not be valid, unless isValid is true, in which case it is guaranteed to be valid. + */ +@property(nonatomic, readonly, nullable) NSString *cardNumber; + +/** + * The current expiration month displayed by the field (1 = January, etc). May or may not be valid, unless isValid is true, in which case it is guaranteed to be valid. + */ +@property(nonatomic, readonly) NSUInteger expirationMonth; + +/** + * The current expiration year displayed by the field, modulo 100 (e.g. the year 2015 will be represented as 15). May or may not be valid, unless isValid is true, in which case it is guaranteed to be valid. + */ +@property(nonatomic, readonly) NSUInteger expirationYear; + +/** + * The current card CVC displayed by the field. May or may not be valid, unless isValid is true, in which case it is guaranteed to be valid. + */ +@property(nonatomic, readonly, nullable) NSString *cvc; + +/** + * Convenience property for creating an PSTCKCardParams from the currently entered information + * or programmatically setting the field's contents. For example, if you're using another library + * to scan your user's credit card with a camera, you can assemble that data into an PSTCKCardParams + * object and set this property to that object to prefill the fields you've collected. + */ +@property(nonatomic, readwrite, nonnull) PSTCKCardParams *cardParams; + +@end diff --git a/Paystack/PublicHeaders/UI/UIImage+Paystack.h b/Paystack/PublicHeaders/UI/UIImage+Paystack.h new file mode 100644 index 0000000..1f00a7d --- /dev/null +++ b/Paystack/PublicHeaders/UI/UIImage+Paystack.h @@ -0,0 +1,25 @@ +// +// UIImage+Paystack.h +// Paystack +// + +#import +#import "PSTCKCardBrand.h" + +@interface UIImage (Paystack) + ++ (nonnull UIImage *)pstck_amexCardImage; ++ (nonnull UIImage *)pstck_dinersClubCardImage; ++ (nonnull UIImage *)pstck_discoverCardImage; ++ (nonnull UIImage *)pstck_jcbCardImage; ++ (nonnull UIImage *)pstck_masterCardCardImage; ++ (nonnull UIImage *)pstck_visaCardImage; ++ (nonnull UIImage *)pstck_unknownCardCardImage; + ++ (nullable UIImage *)pstck_brandImageForCardBrand:(PSTCKCardBrand)brand; ++ (nullable UIImage *)pstck_cvcImageForCardBrand:(PSTCKCardBrand)brand; ++ (nullable UIImage *)pstck_safeImageNamed:(nonnull NSString *)imageName; + +@end + +void linkUIImageCategory(void); diff --git a/Paystack/RSA/PSTCKRSA.h b/Paystack/RSA/PSTCKRSA.h new file mode 100644 index 0000000..cc146a7 --- /dev/null +++ b/Paystack/RSA/PSTCKRSA.h @@ -0,0 +1,14 @@ +// +// PSTCKRSA.h +// Paystack +// +// Created by Ibrahim Lawal on Feb/27/2016. +// Copyright © 2016 Paystack, Inc. All rights reserved. +// +#import + + +@interface PSTCKRSA : NSObject ++ (nullable NSString *)encryptRSA:(nonnull NSString *)plainTextString; +@end + diff --git a/Paystack/RSA/PSTCKRSA.m b/Paystack/RSA/PSTCKRSA.m new file mode 100644 index 0000000..0d8b779 --- /dev/null +++ b/Paystack/RSA/PSTCKRSA.m @@ -0,0 +1,164 @@ +// +// PSTCKRSA.m +// Paystack +// +// Created by Ibrahim Lawal on Feb/27/2016. +// Copyright © 2016 Paystack, Inc. All rights reserved. +// + +#import "PSTCKRSA.h" + +extern OSStatus SecKeyEncrypt( + SecKeyRef key, + SecPadding padding, + const uint8_t *plainText, + size_t plainTextLen, + uint8_t *cipherText, + size_t *cipherTextLen) +__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_2_0) __attribute__((weak_import)); + +@implementation PSTCKRSA + ++ (NSData *)stripPublicKeyHeader:(NSData *)d_key +{ + // Skip ASN.1 public key header + if (d_key == nil) return(nil); + + unsigned long len = [d_key length]; + if (!len) return(nil); + + unsigned char *c_key = (unsigned char *)[d_key bytes]; + unsigned int idx = 0; + + if (c_key[idx++] != 0x30) return(nil); + + if (c_key[idx] > 0x80) idx += c_key[idx] - 0x80 + 1; + else idx++; + + // PKCS #1 rsaEncryption szOID_RSA_RSA + static unsigned char seqiod[] = + { 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00 }; + if (memcmp(&c_key[idx], seqiod, 15)) return(nil); + + idx += 15; + + if (c_key[idx++] != 0x03) return(nil); + + if (c_key[idx] > 0x80) idx += c_key[idx] - 0x80 + 1; + else idx++; + + if (c_key[idx++] != '\0') return(nil); + + // Now make a new NSData from this buffer + return([NSData dataWithBytes:&c_key[idx] length:len - idx]); +} + + ++(NSString *)publicEncryptionKey{ + return @"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANIsL+RHqfkBiKGn/D1y1QnNrMkKzxWP" + "2wkeSokw2OJrCI+d6YGJPrHHx+nmb/Qn885/R01Gw6d7M824qofmCvkCAwEAAQ=="; +} + ++(NSData *)decodedPublicEncryptionKey{ + NSString *s_key = [self publicEncryptionKey]; + + // This will be base64 encoded, decode it. + NSData *d_key = [[NSData alloc] initWithBase64EncodedString:s_key options:0]; + + return [self stripPublicKeyHeader:d_key]; +} + ++(void)throwNotEntitledException{ + NSException *ex = [NSException exceptionWithName:@"Not entitled to Keychain Sharing" reason:NSLocalizedString(@"To use the Paystack SDK, you must add Keychain Sharing entitlements to your app", @"To use the Paystack SDK, you must add Keychain Sharing entitlements to your app") userInfo:nil]; + [ex raise]; +} + + ++(NSString *)encryptRSA:(NSString *)plainTextString { + NSData *d_key = [self decodedPublicEncryptionKey]; + + // Delete any old lingering key with the same tag + NSMutableDictionary *publicKey = [[NSMutableDictionary alloc] init]; + [publicKey setObject:(id) kSecClassKey forKey:(id)kSecClass]; + [publicKey setObject:(id) kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType]; + // [publicKey setObject:d_tag forKey:(id)kSecAttrApplicationTag]; + SecItemDelete((CFDictionaryRef)publicKey); + + CFTypeRef persistKey = nil; + + // Add persistent version of the key to system keychain + [publicKey setObject:d_key forKey:(id)kSecValueData]; + [publicKey setObject:(id) kSecAttrKeyClassPublic forKey:(id) + kSecAttrKeyClass]; + [publicKey setObject:[NSNumber numberWithBool:YES] forKey:(id) + kSecReturnPersistentRef]; + + OSStatus secStatus = SecItemAdd((CFDictionaryRef)publicKey, &persistKey); + if (persistKey != nil) CFRelease(persistKey); + + if (secStatus != noErr) { +// NSLog(@"THTHTHTH: %d", (int)secStatus); + if(secStatus == -34018) { +// NSLog(@"THTHTHTH: Not entitled"); + [self throwNotEntitledException]; + } + if(secStatus == errSecDuplicateItem) { + // we don't mind if there was a duplication + return(FALSE); + } + } + + // Now fetch the SecKeyRef version of the key + SecKeyRef keyRef = nil; + + [publicKey removeObjectForKey:(id)kSecValueData]; + [publicKey removeObjectForKey:(id)kSecReturnPersistentRef]; + [publicKey setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnRef + ]; + [publicKey setObject:(id) kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType]; + secStatus = SecItemCopyMatching((CFDictionaryRef)publicKey, + (CFTypeRef *)&keyRef); + + if (secStatus != noErr) { +// NSLog(@"THTHTHTH: %d", (int)secStatus); + if(secStatus == -34018) { +// NSLog(@"THTHTHTH: Not entitled"); + [self throwNotEntitledException]; + } + if(secStatus == errSecDuplicateItem) { + // we don't mind if there was a duplication + return(FALSE); + } + } + // Fetch the SecKeyRef version of our public key + // SecKeyRef keyRef = [self fetchKeyRef:publicKey andAddIfNotFound:true]; + + if (keyRef == nil){ +// NSLog(@"THTHTHTH: No key"); + return nil; + } + + // Add to our pseudo keychain + // [keyRefs addObject:[NSValue valueWithBytes:&keyRef objCType:@encode( + // SecKeyRef)]]; + // + + size_t cipherBufferSize = SecKeyGetBlockSize(keyRef); + uint8_t *cipherBuffer = malloc(cipherBufferSize); + uint8_t *nonce = (uint8_t *)[plainTextString UTF8String]; + + SecKeyEncrypt(keyRef, + kSecPaddingPKCS1, + nonce, + strlen( (char*)nonce ), + &cipherBuffer[0], + &cipherBufferSize); +// NSLog(@"THTHTHTH: After s_k_e_"); + NSData *encryptedData = [NSData dataWithBytes:cipherBuffer length:cipherBufferSize]; +// NSLog(@"THTHTHTH: %@", [encryptedData base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)0]); + + return [encryptedData base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)0]; +} + +@end diff --git a/Paystack/UI/PSTCKAuthViewController.h b/Paystack/UI/PSTCKAuthViewController.h new file mode 100644 index 0000000..f189adb --- /dev/null +++ b/Paystack/UI/PSTCKAuthViewController.h @@ -0,0 +1,31 @@ +// +// EBAEventbritePurchaseViewController.h +// InEvent +// +// Created by Pedro Góes on 12/16/15. +// Copyright © 2015 InEvent. All rights reserved. +// + +#import +#import + +typedef void(^PSTCKAuthCallback)(void); + +/** + * View Controller subclass containing a `UIWebView` which will be used to display the Paystack web UI to perform the authorization. + **/ +@interface PSTCKAuthViewController : UIViewController + +/** ************************************************************************************************ ** + * @name Initializers + ** ************************************************************************************************ **/ + +/** + * Default initializer. + * @param authURL the authorization url from Paystack. + * @param completion A standard block. + * @returns An initialized instance + **/ +- (id)initWithURL:(NSURL *)authURL handler:(PSTCKAuthCallback)completion; + +@end diff --git a/Paystack/UI/PSTCKAuthViewController.m b/Paystack/UI/PSTCKAuthViewController.m new file mode 100644 index 0000000..e2e25c1 --- /dev/null +++ b/Paystack/UI/PSTCKAuthViewController.m @@ -0,0 +1,124 @@ +// +// EBAEventbritePurchaseViewController.m +// InEvent +// +// Created by Pedro Góes on 12/16/15. +// Copyright © 2015 InEvent. All rights reserved. +// + +#import "PSTCKAuthViewController.h" + +@interface PSTCKAuthViewController () + +@property(nonatomic, strong) WKWebView *authenticationWebView; +@property(nonatomic, copy) PSTCKAuthCallback completion; +@property(nonatomic, strong) NSURL *authURL; + +@end + +@interface PSTCKAuthViewController (WKNavigationDelegate) + +@end + +@implementation PSTCKAuthViewController + +BOOL handlingRedirectURL; + +- (id)initWithURL:(NSURL *)authURL handler:(PSTCKAuthCallback)completion { + self = [super init]; + if (self) { + self.authURL = authURL; + self.completion = completion; + } + return self; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + +#ifdef __IPHONE_7_0 + self.edgesForExtendedLayout = UIRectEdgeNone; +#endif + + UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(tappedCancelButton:)]; + self.navigationItem.leftBarButtonItem = cancelButton; + // Adds javascript to make content width the device width. + NSString *jScript = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);"; + WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:jScript injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES]; + WKUserContentController *wkUController = [[WKUserContentController alloc] init]; + [wkUController addUserScript:wkUScript]; + WKWebViewConfiguration *wkWebConfig = [[WKWebViewConfiguration alloc] init]; + wkWebConfig.userContentController = wkUController; + + self.authenticationWebView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:wkWebConfig]; + self.authenticationWebView.UIDelegate = self; + self.authenticationWebView.navigationDelegate = self; + [self.view addSubview:self.authenticationWebView]; + [self.navigationController setNavigationBarHidden:NO animated:YES]; + self.navigationController.modalTransitionStyle = UIModalTransitionStyleCoverVertical; +} + +- (void)viewDidAppear:(BOOL)animated { + [super viewDidAppear:animated]; + [self.authenticationWebView loadRequest:[NSURLRequest requestWithURL:self.authURL]]; + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; +} + +- (void)viewWillLayoutSubviews { + [super viewWillLayoutSubviews]; + self.authenticationWebView.frame = [UIScreen mainScreen].bounds; +} + +#pragma mark UI Action Methods + +- (void)tappedCancelButton:(id)cancelButton { + #pragma unused(cancelButton) + self.completion(); +} + +@end + +@implementation PSTCKAuthViewController (WKNavigationDelegate) + +- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { + #pragma unused(webView, navigationAction) + NSURLRequest *request = navigationAction.request; + NSString *url = [[request URL]absoluteString]; + + // Prevent loading URL if it is the redirectURL + // The intention is to only requery 3DS auths + handlingRedirectURL = !([url rangeOfString:@"paystack.co/charge/three_d_response/"].location == NSNotFound); + + // Processing has finished? + if (handlingRedirectURL) { + self.completion(); + return decisionHandler(WKNavigationActionPolicyCancel); + + } + else { + return decisionHandler(WKNavigationActionPolicyAllow); + } +} + +- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation { + #pragma unused(webView, navigation) + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; +} + +- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error { + #pragma unused(webView, navigation) + // Turn off network activity indicator upon failure to load web view + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; + + // In case the user hits 'Allow' before the page is fully loaded + if (error.code == NSURLErrorCancelled) { + return; + } + + // Abort if we are on Eventbrite's domain + if (!handlingRedirectURL) { + self.completion(); + } +} + +@end diff --git a/Paystack/UI/PSTCKFormTextField.h b/Paystack/UI/PSTCKFormTextField.h new file mode 100644 index 0000000..f9aa4c6 --- /dev/null +++ b/Paystack/UI/PSTCKFormTextField.h @@ -0,0 +1,28 @@ +// +// PSTCKFormTextField.h +// Paystack +// + +#import + +@class PSTCKFormTextField; + +@protocol PSTCKFormTextFieldDelegate + +- (void)formTextFieldDidBackspaceOnEmpty:(nonnull PSTCKFormTextField *)formTextField; + +@end + +@interface PSTCKFormTextField : UITextField + +@property(nonatomic, readwrite, nullable) UIColor *defaultColor; +@property(nonatomic, readwrite, nullable) UIColor *errorColor; +@property(nonatomic, readwrite, nullable) UIColor *placeholderColor; + +@property(nonatomic, readwrite, assign)BOOL formatsCardNumbers; +@property(nonatomic, readwrite, assign)BOOL validText; +@property(nonatomic, readwrite, weak, nullable)idformDelegate; + +- (CGSize)measureTextSize; + +@end diff --git a/Paystack/UI/PSTCKFormTextField.m b/Paystack/UI/PSTCKFormTextField.m new file mode 100644 index 0000000..22b0dcd --- /dev/null +++ b/Paystack/UI/PSTCKFormTextField.m @@ -0,0 +1,147 @@ +// +// PSTCKFormTextField.m +// Paystack +// + +#import "PSTCKFormTextField.h" +#import "PSTCKCardValidator.h" + +#import +#import "TargetConditionals.h" + +#define FAUXPAS_IGNORED_IN_METHOD(...) + +@implementation PSTCKFormTextField + +@synthesize placeholderColor = _placeholderColor; + +- (void)setFormDelegate:(id)formDelegate { + _formDelegate = formDelegate; + self.delegate = formDelegate; +} + +- (void)deleteBackward { + // This deliberately doesn't call super, because the superclass' implementation replaces text without calling delegate methods. + if (self.text.length == 0) { + [self.formDelegate formTextFieldDidBackspaceOnEmpty:self]; + return; + } + NSRange range = NSMakeRange(self.text.length - 1, 1); + if ([self.delegate textField:self shouldChangeCharactersInRange:range replacementString:@""]) { + self.text = [self.text stringByReplacingCharactersInRange:range withString:@""]; + } +} + +- (CGSize)measureTextSize { + return self.attributedText.size; +} + +- (void)setText:(NSString *)text { + NSAttributedString *attributed = [self attributedStringForString:text attributes:[self safeDefaultTextAttributes]]; + [self setAttributedText:attributed]; +} + +- (void)setPlaceholder:(NSString *)placeholder { + NSMutableDictionary *attributes = [[self safeDefaultTextAttributes] mutableCopy]; + if (self.placeholderColor) { + attributes[NSForegroundColorAttributeName] = self.placeholderColor; + } + NSAttributedString *attributed = [self attributedStringForString:placeholder attributes:[attributes copy]]; + [self setAttributedPlaceholder:attributed]; +} + +- (NSAttributedString *)attributedStringForString:(NSString *)string attributes:(NSDictionary *)attributes { + if (!string) { + return nil; + } + NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:string attributes:attributes]; + if (self.formatsCardNumbers && [PSTCKCardValidator stringIsNumeric:string]) { + NSArray *cardSpacing; + PSTCKCardBrand currentBrand = [PSTCKCardValidator brandForNumber:attributedString.string]; + if (currentBrand == PSTCKCardBrandAmex) { + cardSpacing = @[@3, @9]; + } else if (currentBrand == PSTCKCardBrandVerve) { + cardSpacing = @[@2, @6, @10, @14]; + } else { + cardSpacing = @[@3, @7, @11]; + } + for (NSUInteger i = 0; i < attributedString.length; i++) { + if ([cardSpacing containsObject:@(i)]) { + [attributedString addAttribute:NSKernAttributeName value:@(5) + range:NSMakeRange(i, 1)]; + } else { + [attributedString addAttribute:NSKernAttributeName value:@(0) + range:NSMakeRange(i, 1)]; + } + } + } + return [attributedString copy]; +} + +- (NSDictionary *)safeDefaultTextAttributes { + FAUXPAS_IGNORED_IN_METHOD(APIAvailability); + if ([self respondsToSelector:@selector(defaultTextAttributes)]) { + return [self defaultTextAttributes]; + } + NSMutableDictionary *attributes = [NSMutableDictionary dictionary]; + if (self.textColor) { + attributes[NSForegroundColorAttributeName] = self.textColor; + } + if (self.font) { + attributes[NSFontAttributeName] = self.font; + } + return [attributes copy]; +} + +- (void)setDefaultColor:(UIColor *)defaultColor { + _defaultColor = defaultColor; + [self updateColor]; +} + +- (void)setErrorColor:(UIColor *)errorColor { + _errorColor = errorColor; + [self updateColor]; +} + +- (void)setValidText:(BOOL)validText { + _validText = validText; + [self updateColor]; +} + +- (void)setPlaceholderColor:(UIColor *)placeholderColor { + _placeholderColor = placeholderColor; + [self setPlaceholder:self.placeholder]; //explicitly rebuild attributed placeholder +} + +- (void)updateColor { + self.textColor = _validText ? self.defaultColor : self.errorColor; +} + +// Workaround for http://www.openradar.appspot.com/19374610 +- (CGRect)editingRectForBounds:(CGRect)bounds { + if (UIDevice.currentDevice.systemVersion.integerValue != 8) { + return [self textRectForBounds:bounds]; + } + + CGFloat const scale = UIScreen.mainScreen.scale; + CGFloat const preferred = self.attributedText.size.height; + CGFloat const delta = (CGFloat)ceil(preferred) - preferred; + CGFloat const adjustment = (CGFloat)floor(delta * scale) / scale; + + CGRect const textRect = [self textRectForBounds:bounds]; + CGRect const editingRect = CGRectOffset(textRect, 0.0, adjustment); + + return editingRect; +} + +// Fixes a weird issue related to our custom override of deleteBackwards. This only affects the simulator and iPads with custom keyboards. +- (NSArray *)keyCommands { + FAUXPAS_IGNORED_IN_METHOD(APIAvailability); + return @[[UIKeyCommand keyCommandWithInput:@"\b" modifierFlags:UIKeyModifierCommand action:@selector(commandDeleteBackwards)]]; +} + +- (void)commandDeleteBackwards { + self.text = @""; +} + +@end diff --git a/Paystack/UI/PSTCKPaymentCardTextField.m b/Paystack/UI/PSTCKPaymentCardTextField.m new file mode 100644 index 0000000..9b95968 --- /dev/null +++ b/Paystack/UI/PSTCKPaymentCardTextField.m @@ -0,0 +1,774 @@ +// +// PSTCKPaymentCardTextField.m +// Paystack +// + +#import + +#import "Paystack.h" +#import "PSTCKPaymentCardTextField.h" +#import "PSTCKPaymentCardTextFieldViewModel.h" +#import "PSTCKFormTextField.h" +#import "PSTCKCardValidator.h" +#import "UIImage+Paystack.h" + +#define FAUXPAS_IGNORED_IN_METHOD(...) + +@interface PSTCKPaymentCardTextField() + +@property(nonatomic, readwrite, strong)PSTCKFormTextField *sizingField; + +@property(nonatomic, readwrite, weak)UIImageView *brandImageView; +@property(nonatomic, readwrite, weak)UIView *fieldsView; + +@property(nonatomic, readwrite, weak)PSTCKFormTextField *numberField; + +@property(nonatomic, readwrite, weak)PSTCKFormTextField *expirationField; + +@property(nonatomic, readwrite, weak)UIButton *bumpToExpField; + + +@property(nonatomic, readwrite, weak)PSTCKFormTextField *cvcField; + +@property(nonatomic, readwrite, strong)PSTCKPaymentCardTextFieldViewModel *viewModel; + +@property(nonatomic, readwrite, weak)UITextField *selectedField; + +@property(nonatomic, assign)BOOL numberFieldShrunk; +@property(nonatomic, assign)BOOL bumped; + +@end + +@implementation PSTCKPaymentCardTextField + +@synthesize font = _font; +@synthesize textColor = _textColor; +@synthesize textErrorColor = _textErrorColor; +@synthesize placeholderColor = _placeholderColor; +@dynamic enabled; + +CGFloat const PSTCKPaymentCardTextFieldDefaultPadding = 10; + +#if CGFLOAT_IS_DOUBLE +#define pstck_roundCGFloat(x) round(x) +#else +#define pstck_roundCGFloat(x) roundf(x) +#endif + +#pragma mark initializers + +- (id)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self) { + [self commonInit]; + } + return self; +} + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + [self commonInit]; + } + return self; +} + +- (void)commonInit { + + self.borderColor = [self.class placeholderGrayColor]; + self.cornerRadius = 5.0f; + self.borderWidth = 1.0f; + + self.clipsToBounds = YES; + + _viewModel = [PSTCKPaymentCardTextFieldViewModel new]; + _sizingField = [self buildTextField]; + + UIImageView *brandImageView = [[UIImageView alloc] initWithImage:self.brandImage]; + brandImageView.contentMode = UIViewContentModeCenter; + brandImageView.backgroundColor = [UIColor clearColor]; + if ([brandImageView respondsToSelector:@selector(setTintColor:)]) { + brandImageView.tintColor = self.placeholderColor; + } + self.brandImageView = brandImageView; + + PSTCKFormTextField *numberField = [self buildTextField]; + numberField.formatsCardNumbers = YES; + numberField.tag = PSTCKCardFieldTypeNumber; + self.numberField = numberField; + self.numberPlaceholder = [self.viewModel defaultPlaceholder]; + + PSTCKFormTextField *expirationField = [self buildTextField]; + expirationField.tag = PSTCKCardFieldTypeExpiration; + expirationField.alpha = 0; + self.expirationField = expirationField; + self.expirationPlaceholder = @"MM/YY"; + + UIButton *bumpToExpField = [UIButton buttonWithType:UIButtonTypeCustom]; + [bumpToExpField addTarget:self action:@selector(tappedBumpButton:) forControlEvents:UIControlEventTouchUpInside]; + [bumpToExpField setTitle: @">" forState: UIControlStateNormal]; + [bumpToExpField setTitleColor:self.textColor forState:UIControlStateNormal]; + bumpToExpField.hidden = YES; + self.bumpToExpField.alpha = 50; + self.bumpToExpField = bumpToExpField; + self.bumped = NO; + + PSTCKFormTextField *cvcField = [self buildTextField]; + cvcField.tag = PSTCKCardFieldTypeCVC; + cvcField.alpha = 0; + self.cvcField = cvcField; + self.cvcPlaceholder = @"CVC"; + + UIView *fieldsView = [[UIView alloc] init]; + fieldsView.clipsToBounds = YES; + fieldsView.backgroundColor = [UIColor clearColor]; + self.fieldsView = fieldsView; + + [self addSubview:self.fieldsView]; + [self.fieldsView addSubview:cvcField]; + [self.fieldsView addSubview:expirationField]; + [self.fieldsView addSubview:numberField]; + [self addSubview:brandImageView]; + [self addSubview:bumpToExpField]; +} + +- (void)tappedBumpButton:(id)bumpButton { +#pragma unused(bumpButton) + self.bumped = YES; + self.bumpToExpField.hidden = YES; + if([self.viewModel validationStateForField:PSTCKCardFieldTypeNumber]==PSTCKCardValidationStateValid){ + [self selectNextField]; + } +} + +- (PSTCKPaymentCardTextFieldViewModel *)viewModel { + if (_viewModel == nil) { + _viewModel = [PSTCKPaymentCardTextFieldViewModel new]; + } + return _viewModel; +} + +#pragma mark appearance properties + ++ (UIColor *)placeholderGrayColor { + return [UIColor lightGrayColor]; +} + +- (void)setBackgroundColor:(UIColor *)backgroundColor { + [super setBackgroundColor:[backgroundColor copy]]; + self.numberField.backgroundColor = self.backgroundColor; +} + +- (UIColor *)backgroundColor { + return [super backgroundColor] ?: [UIColor whiteColor]; +} + +- (void)setFont:(UIFont *)font { + _font = [font copy]; + + for (UITextField *field in [self allFields]) { + field.font = _font; + } + + self.sizingField.font = _font; + + [self setNeedsLayout]; +} + +- (UIFont *)font { + return _font ?: [UIFont systemFontOfSize:18]; +} + +- (void)setTextColor:(UIColor *)textColor { + _textColor = [textColor copy]; + + for (PSTCKFormTextField *field in [self allFields]) { + field.defaultColor = _textColor; + } +} + +- (void)setContentVerticalAlignment:(UIControlContentVerticalAlignment)contentVerticalAlignment { + [super setContentVerticalAlignment:contentVerticalAlignment]; + for (UITextField *field in [self allFields]) { + field.contentVerticalAlignment = contentVerticalAlignment; + } + switch (contentVerticalAlignment) { + case UIControlContentVerticalAlignmentCenter: + self.brandImageView.contentMode = UIViewContentModeCenter; + break; + case UIControlContentVerticalAlignmentBottom: + self.brandImageView.contentMode = UIViewContentModeBottom; + break; + case UIControlContentVerticalAlignmentFill: + self.brandImageView.contentMode = UIViewContentModeTop; + break; + case UIControlContentVerticalAlignmentTop: + self.brandImageView.contentMode = UIViewContentModeTop; + break; + } +} + +- (UIColor *)textColor { + return _textColor ?: [UIColor blackColor]; +} + +- (void)setTextErrorColor:(UIColor *)textErrorColor { + _textErrorColor = [textErrorColor copy]; + + for (PSTCKFormTextField *field in [self allFields]) { + field.errorColor = _textErrorColor; + } +} + +- (UIColor *)textErrorColor { + return _textErrorColor ?: [UIColor redColor]; +} + +- (void)setPlaceholderColor:(UIColor *)placeholderColor { + _placeholderColor = [placeholderColor copy]; + + if ([self.brandImageView respondsToSelector:@selector(setTintColor:)]) { + self.brandImageView.tintColor = placeholderColor; + } + + for (PSTCKFormTextField *field in [self allFields]) { + field.placeholderColor = _placeholderColor; + } +} + +- (UIColor *)placeholderColor { + return _placeholderColor ?: [self.class placeholderGrayColor]; +} + +- (void)setNumberPlaceholder:(NSString * __nullable)numberPlaceholder { + _numberPlaceholder = [numberPlaceholder copy]; + self.numberField.placeholder = _numberPlaceholder; +} + +- (void)setExpirationPlaceholder:(NSString * __nullable)expirationPlaceholder { + _expirationPlaceholder = [expirationPlaceholder copy]; + self.expirationField.placeholder = _expirationPlaceholder; +} + +- (void)setCvcPlaceholder:(NSString * __nullable)cvcPlaceholder { + _cvcPlaceholder = [cvcPlaceholder copy]; + self.cvcField.placeholder = _cvcPlaceholder; +} + +- (void)setCursorColor:(UIColor *)cursorColor { + self.tintColor = cursorColor; +} + +- (UIColor *)cursorColor { + return self.tintColor; +} + +- (void)setBorderColor:(UIColor * __nullable)borderColor { + self.layer.borderColor = [[borderColor copy] CGColor]; +} + +- (UIColor * __nullable)borderColor { + return [[UIColor alloc] initWithCGColor:self.layer.borderColor]; +} + +- (void)setCornerRadius:(CGFloat)cornerRadius { + self.layer.cornerRadius = cornerRadius; +} + +- (CGFloat)cornerRadius { + return self.layer.cornerRadius; +} + +- (void)setBorderWidth:(CGFloat)borderWidth { + self.layer.borderWidth = borderWidth; +} + +- (CGFloat)borderWidth { + return self.layer.borderWidth; +} + +- (void)setKeyboardAppearance:(UIKeyboardAppearance)keyboardAppearance { + _keyboardAppearance = keyboardAppearance; + for (PSTCKFormTextField *field in [self allFields]) { + field.keyboardAppearance = keyboardAppearance; + } +} + +- (void)setInputAccessoryView:(UIView *)inputAccessoryView { + _inputAccessoryView = inputAccessoryView; + + for (PSTCKFormTextField *field in [self allFields]) { + field.inputAccessoryView = inputAccessoryView; + } +} + +#pragma mark UIControl + +- (void)setEnabled:(BOOL)enabled { + [super setEnabled:enabled]; + for (PSTCKFormTextField *textField in [self allFields]) { + textField.enabled = enabled; + }; +} + +#pragma mark UIResponder & related methods + +- (BOOL)isFirstResponder { + return [self.selectedField isFirstResponder]; +} + +- (BOOL)canBecomeFirstResponder { + return [[self firstResponderField] canBecomeFirstResponder]; +} + +- (BOOL)becomeFirstResponder { + return [[self firstResponderField] becomeFirstResponder]; +} + +- (PSTCKFormTextField *)firstResponderField { + + if ([self.viewModel validationStateForField:PSTCKCardFieldTypeNumber] != PSTCKCardValidationStateValid) { + return self.numberField; + } else if ([self.viewModel validationStateForField:PSTCKCardFieldTypeExpiration] != PSTCKCardValidationStateValid) { + return self.expirationField; + } else { + return self.cvcField; + } +} + +- (BOOL)canResignFirstResponder { + return [self.selectedField canResignFirstResponder]; +} + +- (BOOL)resignFirstResponder { + [super resignFirstResponder]; + BOOL success = [self.selectedField resignFirstResponder]; + [self setNumberFieldShrunk:[self shouldShrinkNumberField] animated:YES completion:nil]; + return success; +} + +- (BOOL)selectNextField { + return [[self nextField] becomeFirstResponder]; +} + +- (BOOL)selectPreviousField { + return [[self previousField] becomeFirstResponder]; +} + +- (PSTCKFormTextField *)nextField { + if (self.selectedField == self.numberField) { + Boolean stay = NO; + if(self.viewModel.brand==PSTCKCardBrandVerve){ + stay = (self.numberField.text.length < 19); + if(stay){ + stay = !self.bumped; + } + } + self.bumpToExpField.hidden = !stay; + self.bumped = NO; + if(stay){ + return self.numberField; + } + if ([self.viewModel validationStateForField:self.expirationField.tag] == PSTCKCardValidationStateValid) { + return self.cvcField; + } + return self.expirationField; + } else if (self.selectedField == self.expirationField) { + return self.cvcField; + } + return nil; +} + +- (PSTCKFormTextField *)previousField { + if (self.selectedField == self.cvcField) { + return self.expirationField; + } else if (self.selectedField == self.expirationField) { + return self.numberField; + } + return nil; +} + +#pragma mark public convenience methods + +- (void)clear { + for (PSTCKFormTextField *field in [self allFields]) { + field.text = @""; + } + self.viewModel = [PSTCKPaymentCardTextFieldViewModel new]; + [self onChange]; + [self updateImageForFieldType:PSTCKCardFieldTypeNumber]; + __weak id weakself = self; + [self setNumberFieldShrunk:NO animated:YES completion:^(__unused BOOL completed){ + __strong id strongself = weakself; + if ([strongself isFirstResponder]) { + [[strongself numberField] becomeFirstResponder]; + } + }]; +} + +- (BOOL)isValid { + return [self.viewModel isValid]; +} + +#pragma mark readonly variables + +- (NSString *)cardNumber { + return self.viewModel.cardNumber; +} + +- (NSUInteger)expirationMonth { + return [self.viewModel.expirationMonth integerValue]; +} + +- (NSUInteger)expirationYear { + return [self.viewModel.expirationYear integerValue]; +} + +- (NSString *)cvc { + return self.viewModel.cvc; +} + +- (PSTCKCardParams *)cardParams { + PSTCKCardParams *c = [[PSTCKCardParams alloc] init]; + c.number = self.cardNumber; + c.expMonth = self.expirationMonth; + c.expYear = self.expirationYear; + c.cvc = self.cvc; + return c; +} + +- (void)setCardParams:(PSTCKCardParams *)cardParams { + [self setText:cardParams.number inField:PSTCKCardFieldTypeNumber]; + BOOL expirationPresent = cardParams.expMonth && cardParams.expYear; + if (expirationPresent) { + NSString *text = [NSString stringWithFormat:@"%02lu%02lu", + (unsigned long)cardParams.expMonth, + (unsigned long)cardParams.expYear%100]; + [self setText:text inField:PSTCKCardFieldTypeExpiration]; + } + else { + [self setText:@"" inField:PSTCKCardFieldTypeExpiration]; + } + [self setText:cardParams.cvc inField:PSTCKCardFieldTypeCVC]; + + BOOL shrinkNumberField = [self shouldShrinkNumberField]; + [self setNumberFieldShrunk:shrinkNumberField animated:NO completion:nil]; + + // update the card image, falling back to the number field image if not editing + if ([self.expirationField isFirstResponder]) { + [self updateImageForFieldType:PSTCKCardFieldTypeExpiration]; + } + else if ([self.cvcField isFirstResponder]) { + [self updateImageForFieldType:PSTCKCardFieldTypeCVC]; + } + else { + [self updateImageForFieldType:PSTCKCardFieldTypeNumber]; + } +} + +- (PSTCKCardParams *)card { + if (!self.isValid) { return nil; } + return self.cardParams; +} + +- (void)setCard:(PSTCKCardParams *)card { + [self setCardParams:card]; +} + +- (void)setText:(NSString *)text inField:(PSTCKCardFieldType)field { + NSString *nonNilText = text == nil ? @"" : text; + PSTCKFormTextField *textField = nil; + switch (field) { + case PSTCKCardFieldTypeNumber: + textField = self.numberField; + break; + case PSTCKCardFieldTypeExpiration: + textField = self.expirationField; + break; + case PSTCKCardFieldTypeCVC: + textField = self.cvcField; + break; + } + self.selectedField = (self.isFirstResponder) ? textField : nil; + id delegate = (id)self; + NSRange range = NSMakeRange(0, textField.text.length); + [delegate textField:textField shouldChangeCharactersInRange:range + replacementString:nonNilText]; +} + +- (CGSize)intrinsicContentSize { + + CGSize imageSize = self.brandImage.size; + + self.sizingField.text = self.viewModel.defaultPlaceholder; + CGFloat textHeight = [self.sizingField measureTextSize].height; + CGFloat imageHeight = imageSize.height + (PSTCKPaymentCardTextFieldDefaultPadding * 2); + CGFloat height = pstck_roundCGFloat((MAX(MAX(imageHeight, textHeight), 44))); + + CGFloat width = pstck_roundCGFloat([self widthForCardNumber:self.viewModel.defaultPlaceholder] + imageSize.width + (PSTCKPaymentCardTextFieldDefaultPadding * 3)); + + return CGSizeMake(width, height); +} + +- (CGRect)brandImageRectForBounds:(CGRect)bounds { + return CGRectMake(PSTCKPaymentCardTextFieldDefaultPadding, 2, self.brandImageView.image.size.width, bounds.size.height - 2); +} + +- (CGRect)bumpRectForBounds:(CGRect)bounds { + return CGRectMake(CGRectGetWidth(bounds) - [self widthForText:@" > "] - (PSTCKPaymentCardTextFieldDefaultPadding / 2) , 2, [self widthForText:@" > "], bounds.size.height - 2); +} + +- (CGRect)fieldsRectForBounds:(CGRect)bounds { + CGRect brandImageRect = [self brandImageRectForBounds:bounds]; + return CGRectMake(CGRectGetMaxX(brandImageRect), 0, CGRectGetWidth(bounds) - CGRectGetMaxX(brandImageRect), CGRectGetHeight(bounds)); +} + +- (CGRect)numberFieldRectForBounds:(CGRect)bounds { + CGFloat placeholderWidth = [self widthForCardNumber:self.numberField.placeholder] - 4; + CGFloat numberWidth = [self widthForCardNumber:self.viewModel.defaultPlaceholder] - 4; + CGFloat numberFieldWidth = MAX(placeholderWidth, numberWidth); + CGFloat nonFragmentWidth = [self widthForCardNumber:[self.viewModel numberWithoutLastDigits]] - 13; + CGFloat numberFieldX = self.numberFieldShrunk ? PSTCKPaymentCardTextFieldDefaultPadding - nonFragmentWidth : 8; + return CGRectMake(numberFieldX, 0, numberFieldWidth, CGRectGetHeight(bounds)); +} + +- (CGRect)cvcFieldRectForBounds:(CGRect)bounds { + CGRect fieldsRect = [self fieldsRectForBounds:bounds]; + + CGFloat cvcWidth = MAX([self widthForText:self.cvcField.placeholder], [self widthForText:@"8888"]); + CGFloat cvcX = self.numberFieldShrunk ? + CGRectGetWidth(fieldsRect) - cvcWidth - PSTCKPaymentCardTextFieldDefaultPadding / 2 : + CGRectGetWidth(fieldsRect); + return CGRectMake(cvcX, 0, cvcWidth, CGRectGetHeight(bounds)); +} + +- (CGRect)expirationFieldRectForBounds:(CGRect)bounds { + CGRect numberFieldRect = [self numberFieldRectForBounds:bounds]; + CGRect cvcRect = [self cvcFieldRectForBounds:bounds]; + + CGFloat expirationWidth = MAX([self widthForText:self.expirationField.placeholder], [self widthForText:@"88/88"]); + CGFloat expirationX = (CGRectGetMaxX(numberFieldRect) + CGRectGetMinX(cvcRect) - expirationWidth) / 2; + return CGRectMake(expirationX, 0, expirationWidth, CGRectGetHeight(bounds)); +} + +- (void)layoutSubviews { + [super layoutSubviews]; + + CGRect bounds = self.bounds; + + self.bumpToExpField.frame = [self bumpRectForBounds:bounds]; + self.brandImageView.frame = [self brandImageRectForBounds:bounds]; + self.fieldsView.frame = [self fieldsRectForBounds:bounds]; + self.numberField.frame = [self numberFieldRectForBounds:bounds]; + self.cvcField.frame = [self cvcFieldRectForBounds:bounds]; + self.expirationField.frame = [self expirationFieldRectForBounds:bounds]; + +} + +#pragma mark - private helper methods + +- (PSTCKFormTextField *)buildTextField { + PSTCKFormTextField *textField = [[PSTCKFormTextField alloc] initWithFrame:CGRectZero]; + textField.backgroundColor = [UIColor clearColor]; + textField.keyboardType = UIKeyboardTypeNumberPad; + textField.font = self.font; + textField.defaultColor = self.textColor; + textField.errorColor = self.textErrorColor; + textField.placeholderColor = self.placeholderColor; + textField.formDelegate = self; + return textField; +} + +- (NSArray *)allFields { + return @[self.numberField, self.expirationField, self.cvcField]; +} + +typedef void (^PSTCKNumberShrunkCompletionBlock)(BOOL completed); +- (void)setNumberFieldShrunk:(BOOL)shrunk animated:(BOOL)animated + completion:(PSTCKNumberShrunkCompletionBlock)completion { + + if (_numberFieldShrunk == shrunk) { + if (completion) { + completion(YES); + } + return; + } + + _numberFieldShrunk = shrunk; + void (^animations)(void) = ^void() { + for (UIView *view in @[self.expirationField, self.cvcField]) { + view.alpha = 1.0f * shrunk; + } + [self layoutSubviews]; + }; + + FAUXPAS_IGNORED_IN_METHOD(APIAvailability); + NSTimeInterval duration = animated * 0.3; + if ([UIView respondsToSelector:@selector(animateWithDuration:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion:)]) { + [UIView animateWithDuration:duration + delay:0 + usingSpringWithDamping:0.85f + initialSpringVelocity:0 + options:0 + animations:animations + completion:completion]; + } else { + [UIView animateWithDuration:duration + animations:animations + completion:completion]; + } +} + +- (BOOL)shouldShrinkNumberField { + return [self.viewModel validationStateForField:PSTCKCardFieldTypeNumber] == PSTCKCardValidationStateValid; +} + +- (CGFloat)widthForText:(NSString *)text { + self.sizingField.formatsCardNumbers = NO; + [self.sizingField setText:text]; + return [self.sizingField measureTextSize].width + 8; +} + +- (CGFloat)widthForTextWithLength:(NSUInteger)length { + NSString *text = [@"" stringByPaddingToLength:length withString:@"M" startingAtIndex:0]; + return [self widthForText:text]; +} + +- (CGFloat)widthForCardNumber:(NSString *)cardNumber { + self.sizingField.formatsCardNumbers = YES; + [self.sizingField setText:cardNumber]; + return [self.sizingField measureTextSize].width + 20; +} + +#pragma mark PSTCKPaymentTextFieldDelegate + +- (void)formTextFieldDidBackspaceOnEmpty:(__unused PSTCKFormTextField *)formTextField { + PSTCKFormTextField *previous = [self previousField]; + [previous becomeFirstResponder]; + [previous deleteBackward]; +} + +- (void)textFieldDidBeginEditing:(UITextField *)textField { + self.selectedField = (PSTCKFormTextField *)textField; + switch ((PSTCKCardFieldType)textField.tag) { + case PSTCKCardFieldTypeNumber: + [self setNumberFieldShrunk:NO animated:YES completion:nil]; + if ([self.delegate respondsToSelector:@selector(paymentCardTextFieldDidBeginEditingNumber:)]) { + [self.delegate paymentCardTextFieldDidBeginEditingNumber:self]; + } + break; + case PSTCKCardFieldTypeCVC: + [self setNumberFieldShrunk:YES animated:YES completion:nil]; + if ([self.delegate respondsToSelector:@selector(paymentCardTextFieldDidBeginEditingCVC:)]) { + [self.delegate paymentCardTextFieldDidBeginEditingCVC:self]; + } + break; + case PSTCKCardFieldTypeExpiration: + [self setNumberFieldShrunk:YES animated:YES completion:nil]; + if ([self.delegate respondsToSelector:@selector(paymentCardTextFieldDidBeginEditingExpiration:)]) { + [self.delegate paymentCardTextFieldDidBeginEditingExpiration:self]; + } + break; + } + [self updateImageForFieldType:textField.tag]; +} + +- (void)textFieldDidEndEditing:(__unused UITextField *)textField { + self.selectedField = nil; +} + +- (BOOL)textField:(PSTCKFormTextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { + + BOOL deletingLastCharacter = (range.location == textField.text.length - 1 && range.length == 1 && [string isEqualToString:@""]); + if (deletingLastCharacter && [textField.text hasSuffix:@"/"] && range.location > 0) { + range.location -= 1; + range.length += 1; + } + + NSString *newText = [textField.text stringByReplacingCharactersInRange:range withString:string]; + PSTCKCardFieldType fieldType = textField.tag; + switch (fieldType) { + case PSTCKCardFieldTypeNumber: + self.viewModel.cardNumber = newText; + textField.text = self.viewModel.cardNumber; + break; + case PSTCKCardFieldTypeExpiration: { + self.viewModel.rawExpiration = newText; + textField.text = self.viewModel.rawExpiration; + break; + } + case PSTCKCardFieldTypeCVC: + self.viewModel.cvc = newText; + textField.text = self.viewModel.cvc; + break; + } + + [self updateImageForFieldType:fieldType]; + + PSTCKCardValidationState state = [self.viewModel validationStateForField:fieldType]; + textField.validText = YES; + switch (state) { + case PSTCKCardValidationStateInvalid: + textField.validText = NO; + break; + case PSTCKCardValidationStateIncomplete: + break; + case PSTCKCardValidationStateValid: { + [self selectNextField]; + break; + } + } + [self onChange]; + + return NO; +} + +- (UIImage *)brandImage { + if (self.selectedField) { + return [self brandImageForFieldType:self.selectedField.tag]; + } else { + return [self brandImageForFieldType:PSTCKCardFieldTypeNumber]; + } +} + ++ (UIImage *)cvcImageForCardBrand:(PSTCKCardBrand)cardBrand { + return [UIImage pstck_cvcImageForCardBrand:cardBrand]; +} + ++ (UIImage *)brandImageForCardBrand:(PSTCKCardBrand)cardBrand { + return [UIImage pstck_brandImageForCardBrand:cardBrand]; +} + +- (UIImage *)brandImageForFieldType:(PSTCKCardFieldType)fieldType { + if (fieldType == PSTCKCardFieldTypeCVC) { + return [self.class cvcImageForCardBrand:self.viewModel.brand]; + } + + return [self.class brandImageForCardBrand:self.viewModel.brand]; +} + +- (void)updateImageForFieldType:(PSTCKCardFieldType)fieldType { + UIImage *image = [self brandImageForFieldType:fieldType]; + if (image != self.brandImageView.image) { + self.brandImageView.image = image; + + CATransition *transition = [CATransition animation]; + transition.duration = 0.2f; + transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + transition.type = kCATransitionFade; + + [self.brandImageView.layer addAnimation:transition forKey:nil]; + + [self setNeedsLayout]; + } +} + +- (void)onChange { + if ([self.delegate respondsToSelector:@selector(paymentCardTextFieldDidChange:)]) { + [self.delegate paymentCardTextFieldDidChange:self]; + } + [self sendActionsForControlEvents:UIControlEventValueChanged]; +} + +@end + diff --git a/Paystack/UI/PSTCKPaymentCardTextFieldViewModel.h b/Paystack/UI/PSTCKPaymentCardTextFieldViewModel.h new file mode 100644 index 0000000..0671813 --- /dev/null +++ b/Paystack/UI/PSTCKPaymentCardTextFieldViewModel.h @@ -0,0 +1,34 @@ +// +// PSTCKPaymentCardTextFieldViewModel.h +// Paystack +// + +#import +#import + +#import "PSTCKCard.h" +#import "PSTCKCardValidator.h" + +typedef NS_ENUM(NSInteger, PSTCKCardFieldType) { + PSTCKCardFieldTypeNumber, + PSTCKCardFieldTypeExpiration, + PSTCKCardFieldTypeCVC, +}; + +@interface PSTCKPaymentCardTextFieldViewModel : NSObject + +@property(nonatomic, readwrite, copy, nullable)NSString *cardNumber; +@property(nonatomic, readwrite, copy, nullable)NSString *rawExpiration; +@property(nonatomic, readonly, nullable)NSString *expirationMonth; +@property(nonatomic, readonly, nullable)NSString *expirationYear; +@property(nonatomic, readwrite, copy, nullable)NSString *cvc; +@property(nonatomic, readonly) PSTCKCardBrand brand; + +- (nonnull NSString *)defaultPlaceholder; +- (nullable NSString *)numberWithoutLastDigits; + +- (BOOL)isValid; + +- (PSTCKCardValidationState)validationStateForField:(PSTCKCardFieldType)fieldType; + +@end diff --git a/Paystack/UI/PSTCKPaymentCardTextFieldViewModel.m b/Paystack/UI/PSTCKPaymentCardTextFieldViewModel.m new file mode 100644 index 0000000..7b23f75 --- /dev/null +++ b/Paystack/UI/PSTCKPaymentCardTextFieldViewModel.m @@ -0,0 +1,119 @@ +// +// PSTCKPaymentCardTextFieldViewModel.m +// Paystack +// + +#import "PSTCKPaymentCardTextFieldViewModel.h" +#import "PSTCKCardValidator.h" + +#define FAUXPAS_IGNORED_IN_METHOD(...) + +@interface NSString(PaystackSubstring) +- (NSString *)pstck_safeSubstringToIndex:(NSUInteger)index; +- (NSString *)pstck_safeSubstringFromIndex:(NSUInteger)index; +@end + +@implementation NSString(PaystackSubstring) + +- (NSString *)pstck_safeSubstringToIndex:(NSUInteger)index { + return [self substringToIndex:MIN(self.length, index)]; +} + +- (NSString *)pstck_safeSubstringFromIndex:(NSUInteger)index { + return (index > self.length) ? @"" : [self substringFromIndex:index]; +} + +@end + +@implementation PSTCKPaymentCardTextFieldViewModel + +- (void)setCardNumber:(NSString *)cardNumber { + NSString *sanitizedNumber = [PSTCKCardValidator sanitizedNumericStringForString:cardNumber]; + PSTCKCardBrand brand = [PSTCKCardValidator brandForNumber:sanitizedNumber]; + NSInteger maxLength = [PSTCKCardValidator lengthForCardBrand:brand]; + _cardNumber = [sanitizedNumber pstck_safeSubstringToIndex:maxLength]; +} + +// This might contain slashes. +- (void)setRawExpiration:(NSString *)expiration { + NSString *sanitizedExpiration = [PSTCKCardValidator sanitizedNumericStringForString:expiration]; + self.expirationMonth = [sanitizedExpiration pstck_safeSubstringToIndex:2]; + self.expirationYear = [[sanitizedExpiration pstck_safeSubstringFromIndex:2] pstck_safeSubstringToIndex:2]; +} + +- (NSString *)rawExpiration { + NSMutableArray *array = [@[] mutableCopy]; + if (self.expirationMonth && ![self.expirationMonth isEqualToString:@""]) { + [array addObject:self.expirationMonth]; + } + + if ([PSTCKCardValidator validationStateForExpirationMonth:self.expirationMonth] == PSTCKCardValidationStateValid) { + [array addObject:self.expirationYear]; + } + return [array componentsJoinedByString:@"/"]; +} + +- (void)setExpirationMonth:(NSString *)expirationMonth { + NSString *sanitizedExpiration = [PSTCKCardValidator sanitizedNumericStringForString:expirationMonth]; + if (sanitizedExpiration.length == 1 && ![sanitizedExpiration isEqualToString:@"0"] && ![sanitizedExpiration isEqualToString:@"1"]) { + sanitizedExpiration = [@"0" stringByAppendingString:sanitizedExpiration]; + } + _expirationMonth = [sanitizedExpiration pstck_safeSubstringToIndex:2]; +} + +- (void)setExpirationYear:(NSString *)expirationYear { + _expirationYear = [[PSTCKCardValidator sanitizedNumericStringForString:expirationYear] pstck_safeSubstringToIndex:2]; +} + +- (void)setCvc:(NSString *)cvc { + NSInteger maxLength = [PSTCKCardValidator maxCVCLengthForCardBrand:self.brand]; + _cvc = [[PSTCKCardValidator sanitizedNumericStringForString:cvc] pstck_safeSubstringToIndex:maxLength]; +} + +- (PSTCKCardBrand)brand { + return [PSTCKCardValidator brandForNumber:self.cardNumber]; +} + +- (PSTCKCardValidationState)validationStateForField:(PSTCKCardFieldType)fieldType { + switch (fieldType) { + case PSTCKCardFieldTypeNumber: + return [PSTCKCardValidator validationStateForNumber:self.cardNumber validatingCardBrand:YES]; + break; + case PSTCKCardFieldTypeExpiration: { + PSTCKCardValidationState monthState = [PSTCKCardValidator validationStateForExpirationMonth:self.expirationMonth]; + PSTCKCardValidationState yearState = [PSTCKCardValidator validationStateForExpirationYear:self.expirationYear inMonth:self.expirationMonth]; + if (monthState == PSTCKCardValidationStateValid && yearState == PSTCKCardValidationStateValid) { + return PSTCKCardValidationStateValid; + } else if (monthState == PSTCKCardValidationStateInvalid || yearState == PSTCKCardValidationStateInvalid) { + return PSTCKCardValidationStateInvalid; + } else { + return PSTCKCardValidationStateIncomplete; + } + break; + } + case PSTCKCardFieldTypeCVC: + return [PSTCKCardValidator validationStateForCVC:self.cvc cardBrand:self.brand]; + } +} + +- (BOOL)isValid { + return ([self validationStateForField:PSTCKCardFieldTypeNumber] == PSTCKCardValidationStateValid && + [self validationStateForField:PSTCKCardFieldTypeExpiration] == PSTCKCardValidationStateValid && + [self validationStateForField:PSTCKCardFieldTypeCVC] == PSTCKCardValidationStateValid); +} + +- (NSString *)defaultPlaceholder { + return @"1234 5678 1234 5678 000"; +} + +- (NSString *)numberWithoutLastDigits { + NSUInteger length = [PSTCKCardValidator fragmentLengthForCardBrand:[PSTCKCardValidator brandForNumber:self.cardNumber]]; + NSUInteger toIndex = self.cardNumber.length - length; + + return (toIndex < self.cardNumber.length) ? + [self.cardNumber substringToIndex:toIndex] : + [self.defaultPlaceholder pstck_safeSubstringToIndex:[self defaultPlaceholder].length - length]; + +} + +@end diff --git a/Paystack/UI/UIImage+Paystack.m b/Paystack/UI/UIImage+Paystack.m new file mode 100644 index 0000000..08915a3 --- /dev/null +++ b/Paystack/UI/UIImage+Paystack.m @@ -0,0 +1,96 @@ +// +// UIImage+Paystack.m +// Paystack +// + +#import "UIImage+Paystack.h" + +#define FAUXPAS_IGNORED_IN_METHOD(...) + +// Dummy class for locating the framework bundle +@interface PSTCKBundleLocator : NSObject +@end +@implementation PSTCKBundleLocator +@end + +@implementation UIImage (Paystack) + ++ (UIImage *)pstck_amexCardImage { + return [UIImage pstck_brandImageForCardBrand:PSTCKCardBrandAmex]; +} + ++ (UIImage *)pstck_dinersClubCardImage { + return [UIImage pstck_brandImageForCardBrand:PSTCKCardBrandDinersClub]; +} + ++ (UIImage *)pstck_discoverCardImage { + return [UIImage pstck_brandImageForCardBrand:PSTCKCardBrandDiscover]; +} + ++ (UIImage *)pstck_jcbCardImage { + return [UIImage pstck_brandImageForCardBrand:PSTCKCardBrandJCB]; +} + ++ (UIImage *)pstck_masterCardCardImage { + return [UIImage pstck_brandImageForCardBrand:PSTCKCardBrandMasterCard]; +} + ++ (UIImage *)pstck_visaCardImage { + return [UIImage pstck_brandImageForCardBrand:PSTCKCardBrandVisa]; +} + ++ (UIImage *)pstck_unknownCardCardImage { + return [UIImage pstck_brandImageForCardBrand:PSTCKCardBrandUnknown]; +} + ++ (UIImage *)pstck_brandImageForCardBrand:(PSTCKCardBrand)brand { + FAUXPAS_IGNORED_IN_METHOD(APIAvailability); + NSString *imageName; + BOOL templateSupported = [[UIImage new] respondsToSelector:@selector(imageWithRenderingMode:)]; + switch (brand) { + case PSTCKCardBrandAmex: + imageName = @"pstck_card_amex"; + break; + case PSTCKCardBrandDinersClub: + imageName = @"pstck_card_diners"; + break; + case PSTCKCardBrandDiscover: + imageName = @"pstck_card_discover"; + break; + case PSTCKCardBrandJCB: + imageName = @"pstck_card_jcb"; + break; + case PSTCKCardBrandMasterCard: + imageName = @"pstck_card_mastercard"; + break; + case PSTCKCardBrandVerve: + imageName = @"pstck_card_verve"; + break; + case PSTCKCardBrandUnknown: + imageName = templateSupported ? @"pstck_card_placeholder_template" : @"pstck_card_placeholder"; + break; + case PSTCKCardBrandVisa: + imageName = @"pstck_card_visa"; + } + UIImage *image = [UIImage pstck_safeImageNamed:imageName]; + if (brand == PSTCKCardBrandUnknown && templateSupported) { + image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; + } + return image; +} + ++ (UIImage *)pstck_cvcImageForCardBrand:(PSTCKCardBrand)brand { + NSString *imageName = brand == PSTCKCardBrandAmex ? @"pstck_card_cvc_amex" : @"pstck_card_cvc"; + return [UIImage pstck_safeImageNamed:imageName]; +} + ++ (UIImage *)pstck_safeImageNamed:(NSString *)imageName { + if ([[UIImage class] respondsToSelector:@selector(imageNamed:inBundle:compatibleWithTraitCollection:)]) { + return [UIImage imageNamed:imageName inBundle:[NSBundle bundleForClass:[PSTCKBundleLocator class]] compatibleWithTraitCollection:nil]; + } + return [UIImage imageNamed:imageName]; +} + +@end + +void linkUIImageCategory(void){} diff --git a/Paystack/Validator/RequiredRule.swift b/Paystack/Validator/RequiredRule.swift new file mode 100644 index 0000000..dc87ce7 --- /dev/null +++ b/Paystack/Validator/RequiredRule.swift @@ -0,0 +1,46 @@ +// +// Required.swift +// pyur-ios +// +// Created by Jeff Potter on 12/22/14. +// Copyright (c) 2015 jpotts18. All rights reserved. +// + +import Foundation + +/** + `RequiredRule` is a subclass of Rule that defines how a required field is validated. + */ +open class RequiredRule: Rule { + /// String that holds error message. + private var message : String + + /** + Initializes `RequiredRule` object with error message. Used to validate a field that requires text. + + - parameter message: String of error message. + - returns: An initialized `RequiredRule` object, or nil if an object could not be created for some reason that would not result in an exception. + */ + public init(message : String = "This field is required"){ + self.message = message + } + + /** + Validates a field. + + - parameter value: String to check for validation. + - returns: Boolean value. True if validation is successful; False if validation fails. + */ + open func validate(_ value: String) -> Bool { + return !value.isEmpty + } + + /** + Used to display error message when validation fails. + + - returns: String of error message. + */ + open func errorMessage() -> String { + return message + } +} diff --git a/Paystack/Validator/Rule.swift b/Paystack/Validator/Rule.swift new file mode 100644 index 0000000..2f621c9 --- /dev/null +++ b/Paystack/Validator/Rule.swift @@ -0,0 +1,27 @@ +// +// Validation.swift +// +// Created by Jeff Potter on 11/11/14. +// Copyright (c) 2015 jpotts18. All rights reserved. +// + +import Foundation + +/** + The `Rule` protocol declares the required methods for all objects that subscribe to it. + */ +public protocol Rule { + /** + Validates text of a field. + + - parameter value: String of text to be validated. + - returns: Boolean value. True if validation is successful; False if validation fails. + */ + func validate(_ value: String) -> Bool + /** + Displays error message of a field that has failed validation. + + - returns: String of error message. + */ + func errorMessage() -> String +} diff --git a/Paystack/Validator/Validatable.swift b/Paystack/Validator/Validatable.swift new file mode 100644 index 0000000..4778f3a --- /dev/null +++ b/Paystack/Validator/Validatable.swift @@ -0,0 +1,33 @@ +// +// Validatable.swift +// Validator +// +// Created by Deniz Adalar on 28/04/16. +// Copyright © 2016 jpotts18. All rights reserved. +// + +import Foundation + +public typealias ValidatableField = AnyObject & Validatable + +public protocol Validatable { + + var validationText: String { + get + } +} + +extension UITextField: Validatable { + + open var validationText: String { + return text ?? "" + } +} + +extension UITextView: Validatable { + + public var validationText: String { + return text ?? "" + } +} + diff --git a/Paystack/Validator/ValidationDelegate.swift b/Paystack/Validator/ValidationDelegate.swift new file mode 100644 index 0000000..0e073ed --- /dev/null +++ b/Paystack/Validator/ValidationDelegate.swift @@ -0,0 +1,27 @@ +// +// ValidationDelegate.swift +// Validator +// +// Created by David Patterson on 1/2/16. +// Copyright © 2016 jpotts18. All rights reserved. +// + +import Foundation +import UIKit +/** + Protocol for `ValidationDelegate` adherents, which comes with two required methods that are called depending on whether validation succeeded or failed. + */ +public protocol ValidationDelegate { + /** + This method will be called on delegate object when validation is successful. + + - returns: No return value. + */ + func validationSuccessful() + /** + This method will be called on delegate object when validation fails. + + - returns: No return value. + */ + func validationFailed(_ errors: [(Validatable, ValidationError)]) +} diff --git a/Paystack/Validator/ValidationError.swift b/Paystack/Validator/ValidationError.swift new file mode 100644 index 0000000..98ab4de --- /dev/null +++ b/Paystack/Validator/ValidationError.swift @@ -0,0 +1,33 @@ +// +// Created by Jeff Potter on 11/11/14. +// Copyright (c) 2015 jpotts18. All rights reserved. +// + +import Foundation +import UIKit + +/** + The `ValidationError` class is used for representing errors of a failed validation. It contains the field, error label, and error message of a failed validation. + */ +public class ValidationError: NSObject { + /// the Validatable field of the field + public let field:ValidatableField + /// the error label of the field + public var errorLabel:UILabel? + /// the error message of the field + public let errorMessage:String + + /** + Initializes `ValidationError` object with a field, errorLabel, and errorMessage. + + - parameter field: Validatable field that holds field. + - parameter errorLabel: UILabel that holds error label. + - parameter errorMessage: String that holds error message. + - returns: An initialized object, or nil if an object could not be created for some reason that would not result in an exception. + */ + public init(field:ValidatableField, errorLabel:UILabel?, error:String){ + self.field = field + self.errorLabel = errorLabel + self.errorMessage = error + } +} \ No newline at end of file diff --git a/Paystack/Validator/ValidationRule.swift b/Paystack/Validator/ValidationRule.swift new file mode 100644 index 0000000..ef8d306 --- /dev/null +++ b/Paystack/Validator/ValidationRule.swift @@ -0,0 +1,45 @@ +// +// ValidationRule.swift +// +// Created by Jeff Potter on 11/11/14. +// Copyright (c) 2015 jpotts18. All rights reserved. +// + +import Foundation +import UIKit + +/** + `ValidationRule` is a class that creates an object which holds validation info of a field. + */ +public class ValidationRule { + /// the field of the field + public var field:ValidatableField + /// the errorLabel of the field + public var errorLabel:UILabel? + /// the rules of the field + public var rules:[Rule] = [] + + /** + Initializes `ValidationRule` instance with field, rules, and errorLabel. + + - parameter field: field that holds actual text in field. + - parameter errorLabel: label that holds error label of field. + - parameter rules: array of Rule objects, which field will be validated against. + - returns: An initialized `ValidationRule` object, or nil if an object could not be created for some reason that would not result in an exception. + */ + public init(field: ValidatableField, rules:[Rule], errorLabel:UILabel?){ + self.field = field + self.errorLabel = errorLabel + self.rules = rules + } + + /** + Used to validate field against its validation rules. + - returns: `ValidationError` object if at least one error is found. Nil is returned if there are no validation errors. + */ + public func validateField() -> ValidationError? { + return rules.filter{ + return !$0.validate(field.validationText) + }.map{ rule -> ValidationError in return ValidationError(field: self.field, errorLabel:self.errorLabel, error: rule.errorMessage()) }.first + } +} diff --git a/Paystack/Validator/Validator.swift b/Paystack/Validator/Validator.swift new file mode 100644 index 0000000..2553e47 --- /dev/null +++ b/Paystack/Validator/Validator.swift @@ -0,0 +1,154 @@ +// +// Validator.swift +// +// Created by Jeff Potter on 11/10/14. +// Copyright (c) 2015 jpotts18. All rights reserved. +// + +import Foundation +import UIKit + +/** + Class that makes `Validator` objects. Should be added as a parameter to ViewController that will display + validation fields. + */ +public class Validator { + /// Dictionary to hold all fields (and accompanying rules) that will undergo validation. + public var validations = ValidatorDictionary() + /// Dictionary to hold fields (and accompanying errors) that were unsuccessfully validated. + public var errors = ValidatorDictionary() + /// Dictionary to hold fields by their object identifiers + private var fields = ValidatorDictionary() + /// Variable that holds success closure to display positive status of field. + private var successStyleTransform:((_ validationRule:ValidationRule)->Void)? + /// Variable that holds error closure to display negative status of field. + private var errorStyleTransform:((_ validationError:ValidationError)->Void)? + /// - returns: An initialized object, or nil if an object could not be created for some reason that would not result in an exception. + public init(){} + + // MARK: Private functions + + /** + This method is used to validate all fields registered to Validator. If validation is unsuccessful, + field gets added to errors dictionary. + + - returns: No return value. + */ + private func validateAllFields() { + + errors = ValidatorDictionary() + + for (_, rule) in validations { + if let error = rule.validateField() { + errors[rule.field] = error + + // let the user transform the field if they want + if let transform = self.errorStyleTransform { + transform(error) + } + } else { + // No error + // let the user transform the field if they want + if let transform = self.successStyleTransform { + transform(rule) + } + } + } + } + + // MARK: Public functions + + /** + This method is used to validate a single field registered to Validator. If validation is unsuccessful, + field gets added to errors dictionary. + + - parameter field: Holds validator field data. + - returns: No return value. + */ + public func validateField(_ field: ValidatableField, callback: (_ error:ValidationError?) -> Void){ + if let fieldRule = validations[field] { + if let error = fieldRule.validateField() { + errors[field] = error + if let transform = self.errorStyleTransform { + transform(error) + } + callback(error) + } else { + if let transform = self.successStyleTransform { + transform(fieldRule) + } + callback(nil) + } + } else { + callback(nil) + } + } + + // MARK: Using Keys + + /** + This method is used to style fields that have undergone validation checks. Success callback should be used to show common success styling and error callback should be used to show common error styling. + + - parameter success: A closure which is called with validationRule, an object that holds validation data + - parameter error: A closure which is called with validationError, an object that holds validation error data + - returns: No return value + */ + public func styleTransformers(success:((_ validationRule:ValidationRule)->Void)?, error:((_ validationError:ValidationError)->Void)?) { + self.successStyleTransform = success + self.errorStyleTransform = error + } + + /** + This method is used to add a field to validator. + + - parameter field: field that is to be validated. + - parameter errorLabel: A UILabel that holds error label data + - parameter rules: A Rule array that holds different rules that apply to said field. + - returns: No return value + */ + public func registerField(_ field: ValidatableField, errorLabel:UILabel? = nil, rules:[Rule]) { + validations[field] = ValidationRule(field: field, rules:rules, errorLabel:errorLabel) + fields[field] = field + } + + /** + This method is for removing a field validator. + + - parameter field: field used to locate and remove field from validator. + - returns: No return value + */ + public func unregisterField(_ field:ValidatableField) { + validations.removeValueForKey(field) + errors.removeValueForKey(field) + } + + /** + This method checks to see if all fields in validator are valid. + + - returns: No return value. + */ + public func validate(_ delegate:ValidationDelegate) { + + self.validateAllFields() + + if errors.isEmpty { + delegate.validationSuccessful() + } else { + delegate.validationFailed(errors.map { (fields[$1.field]!, $1) }) + } + + } + + /** + This method validates all fields in validator and sets any errors to errors parameter of callback. + + - parameter callback: A closure which is called with errors, a dictionary of type Validatable:ValidationError. + - returns: No return value. + */ + public func validate(_ callback:(_ errors:[(Validatable, ValidationError)])->Void) -> Void { + + self.validateAllFields() + + callback(errors.map { (fields[$1.field]!, $1) } ) + } +} diff --git a/Paystack/Validator/ValidatorDictionary.swift b/Paystack/Validator/ValidatorDictionary.swift new file mode 100644 index 0000000..e152b0f --- /dev/null +++ b/Paystack/Validator/ValidatorDictionary.swift @@ -0,0 +1,45 @@ +// +// ValidatorDictionary.swift +// Validator +// +// Created by Deniz Adalar on 04/05/16. +// Copyright © 2016 jpotts18. All rights reserved. +// + +import Foundation + +public struct ValidatorDictionary : Sequence { + + private var innerDictionary: [ObjectIdentifier: T] = [:]; + + public subscript(key: ValidatableField?) -> T? { + get { + if let key = key { + return innerDictionary[ObjectIdentifier(key)]; + } else { + return nil; + } + } + set(newValue) { + if let key = key { + innerDictionary[ObjectIdentifier(key)] = newValue; + } + } + } + + public mutating func removeAll() { + innerDictionary.removeAll() + } + + public mutating func removeValueForKey(_ key: ValidatableField) { + innerDictionary.removeValue(forKey: ObjectIdentifier(key)) + } + + public var isEmpty: Bool { + return innerDictionary.isEmpty + } + + public func makeIterator() -> DictionaryIterator { + return innerDictionary.makeIterator() + } +} From db4fc87b91cc25bfbb880367eb35ebe7e74ab74f Mon Sep 17 00:00:00 2001 From: Jubril Olambiwonnu Date: Sun, 12 Jul 2020 00:20:49 +0100 Subject: [PATCH 09/30] Fix issue with pod linting --- Paystack.podspec | 10 +- .../API Client/PSTCKAPIClient+Private.h | 14 - Paystack/Classes/API Client/PSTCKAPIClient.m | 634 -------------- .../API Client/PSTCKAPIClientExtension.swift | 73 -- .../Classes/API Client/PSTCKAPIPostRequest.h | 19 - .../Classes/API Client/PSTCKAPIPostRequest.m | 54 -- Paystack/Classes/Card/PSTCKCard.m | 144 ---- Paystack/Classes/Card/PSTCKCardParams.m | 173 ---- .../Classes/Error Handler/PaystackError.m | 109 --- .../Classes/Form Encoder/PSTCKFormEncodable.h | 32 - .../Classes/Form Encoder/PSTCKFormEncoder.m | 228 ------ .../Classes/Helpers/NSDictionary+Paystack.h | 14 - .../Classes/Helpers/NSDictionary+Paystack.m | 27 - .../Classes/Helpers/PSTCKCategoryLoader.h | 16 - .../Classes/Helpers/PSTCKCategoryLoader.m | 21 - .../Classes/PublicHeaders/PSTCKAPIClient.h | 110 --- .../PublicHeaders/PSTCKAPIResponseDecodable.h | 25 - Paystack/Classes/PublicHeaders/PSTCKCard.h | 122 --- .../Classes/PublicHeaders/PSTCKCardBrand.h | 20 - .../Classes/PublicHeaders/PSTCKCardParams.h | 46 -- .../PublicHeaders/PSTCKCardValidationState.h | 15 - .../PublicHeaders/PSTCKCardValidator.h | 103 --- .../Classes/PublicHeaders/PSTCKFormEncoder.h | 37 - .../PublicHeaders/PSTCKPaymentCardTextField.h | 238 ------ Paystack/Classes/PublicHeaders/PSTCKToken.h | 33 - .../Classes/PublicHeaders/PSTCKTransaction.h | 31 - .../PublicHeaders/PSTCKTransactionParams.h | 40 - .../PublicHeaders/PSTCKValidationParams.h | 17 - Paystack/Classes/PublicHeaders/Paystack.h | 25 - .../Classes/PublicHeaders/PaystackError.h | 75 -- .../Classes/PublicHeaders/UIImage+Paystack.h | 25 - Paystack/Classes/RSA/PSTCKRSA.h | 14 - Paystack/Classes/RSA/PSTCKRSA.m | 164 ---- Paystack/Classes/Token/PSTCKToken.m | 95 --- .../Classes/Transaction/PSTCKTransaction.m | 93 --- .../Transaction/PSTCKTransactionParams.m | 137 ---- Paystack/Classes/UI/AddressViewController.xib | 164 ---- Paystack/Classes/UI/ButtonExtension.swift | 92 --- Paystack/Classes/UI/KeyboardHandlingVC.swift | 84 -- .../UI/PSTCKAddressViewController.swift | 108 --- Paystack/Classes/UI/PSTCKAuthViewController.h | 31 - Paystack/Classes/UI/PSTCKAuthViewController.m | 124 --- Paystack/Classes/UI/PSTCKFormTextField.h | 28 - Paystack/Classes/UI/PSTCKFormTextField.m | 147 ---- .../Classes/UI/PSTCKPaymentCardTextField.m | 774 ------------------ .../UI/PSTCKPaymentCardTextFieldViewModel.h | 34 - .../UI/PSTCKPaymentCardTextFieldViewModel.m | 119 --- Paystack/Classes/UI/UIImage+Paystack.m | 96 --- .../Card Validator/PSTCKCardValidator.m | 308 ------- .../Card Validator/PSTCKValidationParams.m | 36 - .../Textfield Validator/RequiredRule.swift | 46 -- .../Validator/Textfield Validator/Rule.swift | 27 - .../Textfield Validator/Validatable.swift | 33 - .../ValidationDelegate.swift | 27 - .../Textfield Validator/ValidationError.swift | 33 - .../Textfield Validator/ValidationRule.swift | 45 - .../Textfield Validator/Validator.swift | 154 ---- .../ValidatorDictionary.swift | 45 - Paystack/Fabric/FABKitProtocol.h | 46 -- Paystack/Fabric/Fabric+FABKits.h | 25 - Paystack/Fabric/Fabric.h | 53 -- 61 files changed, 6 insertions(+), 5706 deletions(-) delete mode 100644 Paystack/Classes/API Client/PSTCKAPIClient+Private.h delete mode 100644 Paystack/Classes/API Client/PSTCKAPIClient.m delete mode 100644 Paystack/Classes/API Client/PSTCKAPIClientExtension.swift delete mode 100644 Paystack/Classes/API Client/PSTCKAPIPostRequest.h delete mode 100644 Paystack/Classes/API Client/PSTCKAPIPostRequest.m delete mode 100644 Paystack/Classes/Card/PSTCKCard.m delete mode 100644 Paystack/Classes/Card/PSTCKCardParams.m delete mode 100644 Paystack/Classes/Error Handler/PaystackError.m delete mode 100644 Paystack/Classes/Form Encoder/PSTCKFormEncodable.h delete mode 100644 Paystack/Classes/Form Encoder/PSTCKFormEncoder.m delete mode 100644 Paystack/Classes/Helpers/NSDictionary+Paystack.h delete mode 100644 Paystack/Classes/Helpers/NSDictionary+Paystack.m delete mode 100644 Paystack/Classes/Helpers/PSTCKCategoryLoader.h delete mode 100644 Paystack/Classes/Helpers/PSTCKCategoryLoader.m delete mode 100644 Paystack/Classes/PublicHeaders/PSTCKAPIClient.h delete mode 100644 Paystack/Classes/PublicHeaders/PSTCKAPIResponseDecodable.h delete mode 100644 Paystack/Classes/PublicHeaders/PSTCKCard.h delete mode 100644 Paystack/Classes/PublicHeaders/PSTCKCardBrand.h delete mode 100644 Paystack/Classes/PublicHeaders/PSTCKCardParams.h delete mode 100644 Paystack/Classes/PublicHeaders/PSTCKCardValidationState.h delete mode 100644 Paystack/Classes/PublicHeaders/PSTCKCardValidator.h delete mode 100644 Paystack/Classes/PublicHeaders/PSTCKFormEncoder.h delete mode 100644 Paystack/Classes/PublicHeaders/PSTCKPaymentCardTextField.h delete mode 100644 Paystack/Classes/PublicHeaders/PSTCKToken.h delete mode 100644 Paystack/Classes/PublicHeaders/PSTCKTransaction.h delete mode 100644 Paystack/Classes/PublicHeaders/PSTCKTransactionParams.h delete mode 100644 Paystack/Classes/PublicHeaders/PSTCKValidationParams.h delete mode 100644 Paystack/Classes/PublicHeaders/Paystack.h delete mode 100644 Paystack/Classes/PublicHeaders/PaystackError.h delete mode 100644 Paystack/Classes/PublicHeaders/UIImage+Paystack.h delete mode 100644 Paystack/Classes/RSA/PSTCKRSA.h delete mode 100644 Paystack/Classes/RSA/PSTCKRSA.m delete mode 100644 Paystack/Classes/Token/PSTCKToken.m delete mode 100644 Paystack/Classes/Transaction/PSTCKTransaction.m delete mode 100644 Paystack/Classes/Transaction/PSTCKTransactionParams.m delete mode 100644 Paystack/Classes/UI/AddressViewController.xib delete mode 100644 Paystack/Classes/UI/ButtonExtension.swift delete mode 100644 Paystack/Classes/UI/KeyboardHandlingVC.swift delete mode 100644 Paystack/Classes/UI/PSTCKAddressViewController.swift delete mode 100644 Paystack/Classes/UI/PSTCKAuthViewController.h delete mode 100644 Paystack/Classes/UI/PSTCKAuthViewController.m delete mode 100644 Paystack/Classes/UI/PSTCKFormTextField.h delete mode 100644 Paystack/Classes/UI/PSTCKFormTextField.m delete mode 100644 Paystack/Classes/UI/PSTCKPaymentCardTextField.m delete mode 100644 Paystack/Classes/UI/PSTCKPaymentCardTextFieldViewModel.h delete mode 100644 Paystack/Classes/UI/PSTCKPaymentCardTextFieldViewModel.m delete mode 100644 Paystack/Classes/UI/UIImage+Paystack.m delete mode 100644 Paystack/Classes/Validator/Card Validator/PSTCKCardValidator.m delete mode 100644 Paystack/Classes/Validator/Card Validator/PSTCKValidationParams.m delete mode 100644 Paystack/Classes/Validator/Textfield Validator/RequiredRule.swift delete mode 100644 Paystack/Classes/Validator/Textfield Validator/Rule.swift delete mode 100644 Paystack/Classes/Validator/Textfield Validator/Validatable.swift delete mode 100644 Paystack/Classes/Validator/Textfield Validator/ValidationDelegate.swift delete mode 100644 Paystack/Classes/Validator/Textfield Validator/ValidationError.swift delete mode 100644 Paystack/Classes/Validator/Textfield Validator/ValidationRule.swift delete mode 100644 Paystack/Classes/Validator/Textfield Validator/Validator.swift delete mode 100644 Paystack/Classes/Validator/Textfield Validator/ValidatorDictionary.swift delete mode 100644 Paystack/Fabric/FABKitProtocol.h delete mode 100644 Paystack/Fabric/Fabric+FABKits.h delete mode 100644 Paystack/Fabric/Fabric.h diff --git a/Paystack.podspec b/Paystack.podspec index d8e1550..e44a2be 100644 --- a/Paystack.podspec +++ b/Paystack.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'Paystack' - s.version = '3.0.13' + s.version = '3.0.14' s.summary = 'Paystack is a web-based API helping African Businesses accept payments online.' s.description = <<-DESC Paystack makes it easy for African Businesses to accept Mastercard, Visa and Verve cards from anyone, anywhere in the world. @@ -8,19 +8,21 @@ Pod::Spec.new do |s| s.license = { :type => 'MIT', :file => 'LICENSE' } s.homepage = 'https://paystack.com' - s.authors = { 'Ibrahim Lawal' => 'ibrahim@paystack.com', 'Paystack' => 'support@paystack.com' } + s.authors = { 'Jubril Olambiwonnu' => 'jubril@paystack.com', 'Ibrahim Lawal' => 'ibrahim@paystack.com', 'Paystack' => 'support@paystack.com' } s.source = { :git => 'https://github.com/paystackhq/paystack-ios.git', :tag => "v#{s.version}" } s.ios.frameworks = 'Foundation', 'Security' s.ios.weak_frameworks = 'PassKit', 'AddressBook' s.requires_arc = true - s.ios.deployment_target = '8.0' s.default_subspecs = 'Core' + s.ios.deployment_target = '11.0' + s.swift_versions = '5.0' + s.subspec 'Core' do |ss| ss.public_header_files = 'Paystack/PublicHeaders/*.h', 'Paystack/RSA/*.h' ss.ios.public_header_files = 'Paystack/PublicHeaders/UI/*.h' ss.source_files = 'Paystack/PublicHeaders/*.h', 'Paystack/RSA/*.{h,m}', 'Paystack/*.{h,m}' - ss.ios.source_files = 'Paystack/PublicHeaders/UI/*.h', 'Paystack/UI/*.{h,m}', 'Paystack/Fabric/*' + ss.ios.source_files = 'Paystack/PublicHeaders/UI/*.h', 'Paystack/UI/*.{h,m}', 'Paystack/**/*.{swift}' ss.resources = 'Paystack/Resources/**/*' end diff --git a/Paystack/Classes/API Client/PSTCKAPIClient+Private.h b/Paystack/Classes/API Client/PSTCKAPIClient+Private.h deleted file mode 100644 index 9e0377f..0000000 --- a/Paystack/Classes/API Client/PSTCKAPIClient+Private.h +++ /dev/null @@ -1,14 +0,0 @@ -// -// PSTCKAPIClient+Private.h -// Paystack -// -// Copyright © 2016 Paystack, Inc. -// - -#import - -@interface PSTCKAPIClient () - -@property (nonatomic, readwrite, nonnull) NSURL *apiURL; -@property (nonatomic, readwrite, nonnull) NSURLSession *urlSession; -@end diff --git a/Paystack/Classes/API Client/PSTCKAPIClient.m b/Paystack/Classes/API Client/PSTCKAPIClient.m deleted file mode 100644 index ce8958a..0000000 --- a/Paystack/Classes/API Client/PSTCKAPIClient.m +++ /dev/null @@ -1,634 +0,0 @@ -// -// PSTCKAPIClient.m -// PaystackExample -// - -#import "TargetConditionals.h" -#if TARGET_OS_IPHONE -#import -#import -#import -#endif - -#import "PSTCKAPIClient.h" -#import "PSTCKFormEncoder.h" -#import "PSTCKCard.h" -#import "PSTCKRSA.h" -#import "PSTCKCardValidator.h" -#import "PSTCKToken.h" -#import "PSTCKTransaction.h" -#import "PSTCKValidationParams.h" -#import "PaystackError.h" -#import "PSTCKAPIResponseDecodable.h" -#import "PSTCKAuthViewController.h" -#import "PSTCKAPIPostRequest.h" -#import - -#if __has_include("Fabric.h") -#import "Fabric+FABKits.h" -#import "FABKitProtocol.h" -#endif - -#ifdef PSTCK_STATIC_LIBRARY_BUILD -#import "PSTCKCategoryLoader.h" -#endif - -#define FAUXPAS_IGNORED_IN_METHOD(...) - -static NSString *const apiURLBase = @"standard.paystack.co"; -static NSString *const chargeEndpoint = @"charge/mobile_charge"; -static NSString *const avsEndpoint = @"charge/avs"; -static NSString *const validateEndpoint = @"charge/validate"; -static NSString *const requeryEndpoint = @"charge/requery/"; -static NSString *const paystackAPIVersion = @"2017-05-25"; -static NSString *PSTCKDefaultPublicKey; -static Boolean PROCESSING = false; - -@implementation Paystack - -+ (id)alloc { - NSCAssert(NO, @"'Paystack' is a static class and cannot be instantiated."); - return nil; -} - -+ (void)setDefaultPublicKey:(NSString *)publicKey { - PSTCKDefaultPublicKey = publicKey; -} - -+ (NSString *)defaultPublicKey { - return PSTCKDefaultPublicKey; -} - -@end - -#if __has_include("Fabric.h") -@interface PSTCKAPIClient () -#else -@interface PSTCKAPIClient() -#endif -@property (nonatomic, readwrite) NSURL *apiURL; -@property (nonatomic, readwrite) NSURLSession *urlSession; -@end - -@interface PSTCKServerTransaction : NSObject - -@property (nonatomic, readwrite, nullable) NSString *id; -@property (nonatomic, readwrite, nullable) NSString *reference; - -@end -@implementation PSTCKServerTransaction -- (instancetype)init { - _id = nil; - _reference = nil; - - return self; -} -@end - -@interface PSTCKAPIClient () - -@property(nonatomic, strong) UIViewController *viewController; -@property(nonatomic, strong) PSTCKServerTransaction *serverTransaction; -@property(nonatomic, retain) PSTCKCardParams *card; -@property(nonatomic, retain) PSTCKTransactionParams *transaction; -@property(nonatomic, copy) PSTCKErrorCompletionBlock errorCompletion; -@property(nonatomic, copy) PSTCKTransactionCompletionBlock beforeValidateCompletion; -@property(nonatomic, copy) PSTCKNotifyCompletionBlock showingDialogCompletion; -@property(nonatomic, copy) PSTCKNotifyCompletionBlock dialogDismissedCompletion; -@property(nonatomic, copy) PSTCKTransactionCompletionBlock successCompletion; - -@property int INVALID_DATA_SENT_RETRIES; -@end - -@implementation PSTCKAPIClient - -#ifdef PSTCK_STATIC_LIBRARY_BUILD -+ (void)initialize { - [PSTCKCategoryLoader loadCategories]; -} -#endif - -+ (instancetype)sharedClient { - static id sharedClient; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ sharedClient = [[self alloc] init]; }); - return sharedClient; -} - -- (instancetype)init { - return [self initWithPublicKey:[Paystack defaultPublicKey]]; -} - -- (instancetype)initWithPublicKey:(NSString *)publicKey { - self = [super init]; - if (self) { - [self.class validateKey:publicKey]; - _apiURL = [NSURL URLWithString:[NSString stringWithFormat:@"https://%@", apiURLBase]]; - _publicKey = [publicKey copy]; - _operationQueue = [NSOperationQueue mainQueue]; - NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; - NSString *auth = [@"Bearer " stringByAppendingString:self.publicKey]; - config.HTTPAdditionalHeaders = @{ - @"X-Paystack-User-Agent": [self.class paystackUserAgentDetails], - @"Paystack-Version": paystackAPIVersion, - @"Authorization": auth, - @"X-Paystack-Build": PSTCKSDKBuild, - }; - _urlSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:_operationQueue]; - } - return self; -} - - - -- (void)setOperationQueue:(NSOperationQueue *)operationQueue { - NSCAssert(operationQueue, @"Operation queue cannot be nil."); - _operationQueue = operationQueue; -} - -#pragma mark - private helpers - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-variable" -+ (void)validateKey:(NSString *)publicKey { - NSCAssert(publicKey != nil && ![publicKey isEqualToString:@""], - @"You must use a valid public key to charge a card."); - BOOL secretKey = [publicKey hasPrefix:@"sk_"]; - NSCAssert(!secretKey, - @"You are using a secret key to charge the card, instead of the public one."); -#ifndef DEBUG - if ([publicKey.lowercaseString hasPrefix:@"pk_test"]) { - FAUXPAS_IGNORED_IN_METHOD(NSLogUsed); - NSLog(@"⚠️ Warning! You're building your app in a non-debug configuration, but appear to be using your Paystack test key. Make sure not to submit to " - @"the App Store with your test keys!⚠️"); - } -#endif -} -#pragma clang diagnostic pop - -#pragma mark Utility methods - - -+ (NSString *)device_id { - return [@"iossdk_" stringByAppendingString:[[[UIDevice currentDevice] identifierForVendor] UUIDString]]; -} - -+ (NSString *)paystackUserAgentDetails { - NSMutableDictionary *details = [@{ - @"lang": @"objective-c", - @"bindings_version": PSTCKSDKVersion, - } mutableCopy]; -#if TARGET_OS_IPHONE - NSString *version = [UIDevice currentDevice].systemVersion; - if (version) { - details[@"os_version"] = version; - } - struct utsname systemInfo; - uname(&systemInfo); - NSString *deviceType = @(systemInfo.machine); - if (deviceType) { - details[@"type"] = deviceType; - } - NSString *model = [UIDevice currentDevice].localizedModel; - if (model) { - details[@"model"] = model; - } - if ([[UIDevice currentDevice] respondsToSelector:@selector(identifierForVendor)]) { - NSString *vendorIdentifier = [[[UIDevice currentDevice] performSelector:@selector(identifierForVendor)] performSelector:@selector(UUIDString)]; - if (vendorIdentifier) { - details[@"vendor_identifier"] = vendorIdentifier; - } - } -#endif - return [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:[details copy] options:0 error:NULL] encoding:NSUTF8StringEncoding]; -} - -#pragma mark Fabric -#if __has_include("Fabric.h") - -+ (NSString *)bundleIdentifier { - return @"com.paystack.paystack-ios"; -} - -+ (NSString *)kitDisplayVersion { - return PSTCKSDKVersion; -} - -+ (void)initializeIfNeeded { - Class fabric = NSClassFromString(@"Fabric"); - if (fabric) { - // The app must be using Fabric, as it exists at runtime. We fetch our default public key from Fabric. - NSDictionary *fabricConfiguration = [fabric configurationDictionaryForKitClass:[PSTCKAPIClient class]]; - NSString *publicKey = fabricConfiguration[@"public"]; - if (!publicKey) { - NSLog(@"Configuration dictionary returned by Fabric was nil, or doesn't have publicKey. Can't initialize Paystack."); - return; - } - [self validateKey:publicKey]; - [Paystack setDefaultPublicKey:publicKey]; - } else { - NSCAssert(fabric, @"initializeIfNeeded method called from a project that doesn't have Fabric."); - } -} - -#endif - -@end - -typedef NS_ENUM(NSInteger, PSTCKChargeStage) { - PSTCKChargeStageNoHandle, - PSTCKChargeStagePlusHandle, - PSTCKChargeStageValidateToken, - PSTCKChargeStageRequery, - PSTCKChargeStageAuthorize, - PSTCKChargeStageAVS, -}; - - -#pragma mark - Credit Cards -@implementation PSTCKAPIClient (CreditCards) - -- (void)chargeCard:(nonnull PSTCKCardParams *)card - forTransaction:(nonnull PSTCKTransactionParams *)transaction - onViewController:(nonnull UIViewController *)viewController - didEndWithError:(nonnull PSTCKErrorCompletionBlock)errorCompletion -didTransactionSuccess:(nonnull PSTCKTransactionCompletionBlock)successCompletion { - NSCAssert(card != nil, @"'card' is required for a charge"); - NSCAssert(errorCompletion != nil, @"'errorCompletion' is required to handle any errors encountered while charging"); - NSCAssert(viewController != nil, @"'viewController' is required to show any alerts that may be needed"); - NSCAssert(transaction != nil, @"'transaction' is required so we may know who to charge"); - NSCAssert(successCompletion != nil, @"'successCompletion' is required so you can continue the process after charge succeeds. Remember to verify on server before giving value."); - [self startWithCard:card forTransaction:transaction onViewController:viewController didEndWithError:errorCompletion didTransactionSuccess:successCompletion]; - - if(PROCESSING){ - [self didEndWithProcessingError]; - return; - } - PROCESSING = YES; - self.INVALID_DATA_SENT_RETRIES = 0; - NSData *data = [PSTCKFormEncoder formEncryptedDataForCard:card - andTransaction:transaction - usePublicKey:[self publicKey] - onThisDevice:[self.class device_id]]; - - [self makeChargeRequest:data atStage:PSTCKChargeStageNoHandle]; -} - -- (void)setProcessingStatus:(Boolean)status { - PROCESSING=status; -} - -- (void)chargeCard:(nonnull PSTCKCardParams *)card - forTransaction:(nonnull PSTCKTransactionParams *)transaction - onViewController:(nonnull UIViewController *)viewController - didEndWithError:(nonnull PSTCKErrorCompletionBlock)errorCompletion -didRequestValidation:(nonnull PSTCKTransactionCompletionBlock)beforeValidateCompletion - willPresentDialog:(nonnull PSTCKNotifyCompletionBlock)showingDialogCompletion - dismissedDialog:(nonnull PSTCKNotifyCompletionBlock)dialogDismissedCompletion -didTransactionSuccess:(nonnull PSTCKTransactionCompletionBlock)successCompletion { - self.beforeValidateCompletion = beforeValidateCompletion; - self.showingDialogCompletion = showingDialogCompletion; - self.dialogDismissedCompletion = dialogDismissedCompletion; - [self chargeCard:card forTransaction:transaction onViewController:viewController didEndWithError:errorCompletion didTransactionSuccess:successCompletion]; - -} - -- (void)chargeCard:(nonnull PSTCKCardParams *)card - forTransaction:(nonnull PSTCKTransactionParams *)transaction - onViewController:(nonnull UIViewController *)viewController - didEndWithError:(nonnull PSTCKErrorCompletionBlock)errorCompletion - willPresentDialog:(nonnull PSTCKNotifyCompletionBlock)showingDialogCompletion - dismissedDialog:(nonnull PSTCKNotifyCompletionBlock)dialogDismissedCompletion -didTransactionSuccess:(nonnull PSTCKTransactionCompletionBlock)successCompletion { - self.showingDialogCompletion = showingDialogCompletion; - self.dialogDismissedCompletion = dialogDismissedCompletion; - [self chargeCard:card forTransaction:transaction onViewController:viewController didEndWithError:errorCompletion didTransactionSuccess:successCompletion]; - -} - -- (void)chargeCard:(nonnull PSTCKCardParams *)card - forTransaction:(nonnull PSTCKTransactionParams *)transaction - onViewController:(nonnull UIViewController *)viewController - didEndWithError:(nonnull PSTCKErrorCompletionBlock)errorCompletion -didRequestValidation:(nonnull PSTCKTransactionCompletionBlock)beforeValidateCompletion -didTransactionSuccess:(nonnull PSTCKTransactionCompletionBlock)successCompletion { - self.beforeValidateCompletion = beforeValidateCompletion; - [self chargeCard:card forTransaction:transaction onViewController:viewController didEndWithError:errorCompletion didTransactionSuccess:successCompletion]; - -} - -- (void)startWithCard:(nonnull PSTCKCardParams *)card - forTransaction:(nonnull PSTCKTransactionParams *)transaction - onViewController:(nonnull UIViewController *)viewController - didEndWithError:(nonnull PSTCKErrorCompletionBlock)errorCompletion -didTransactionSuccess:(nonnull PSTCKTransactionCompletionBlock)successCompletion { - self.card = card; - self.transaction = transaction; - self.viewController = viewController; - self.errorCompletion = errorCompletion; - self.successCompletion = successCompletion; - self.serverTransaction = [PSTCKServerTransaction new]; - -} - - - -- (void) makeChargeRequest:(NSData *)data - atStage:(PSTCKChargeStage) stage - -{ - NSString *endpoint; - NSString *httpMethod; - - switch (stage){ - case PSTCKChargeStageNoHandle: - case PSTCKChargeStagePlusHandle: - endpoint = chargeEndpoint; - httpMethod = @"POST"; - break; - case PSTCKChargeStageValidateToken: - endpoint = validateEndpoint; - httpMethod = @"POST"; - break; - case PSTCKChargeStageRequery: - case PSTCKChargeStageAuthorize: - endpoint = [requeryEndpoint stringByAppendingString:self.serverTransaction.id] ; - httpMethod = @"GET"; - break; - case PSTCKChargeStageAVS: - endpoint = avsEndpoint; - httpMethod = @"POST"; - break; - } - - [PSTCKAPIPostRequest - startWithAPIClient:self - endpoint:endpoint - method:httpMethod - postData:data - serializer:[PSTCKTransaction new] - completion:^(PSTCKTransaction * _Nullable responseObject, NSError * _Nullable error){ - if((responseObject != nil) && ([responseObject trans] != nil)){ - self.serverTransaction.id = [responseObject trans]; - } - if((responseObject != nil) && ([responseObject reference] != nil)){ - self.serverTransaction.reference = [responseObject reference]; - } - if(error != nil){ - [self didEndWithError:error]; - return; - } - if([[responseObject message].lowercaseString isEqual:@"invalid data sent"] && self.INVALID_DATA_SENT_RETRIES<3){ - self.INVALID_DATA_SENT_RETRIES = self.INVALID_DATA_SENT_RETRIES+1; - [self makeChargeRequest:data - atStage:stage]; - return; - } - if([[responseObject message].lowercaseString isEqual:@"access code has expired"] && [[responseObject status] isEqual:@"0"]){ - NSDictionary *userInfo = @{ - NSLocalizedDescriptionKey: PSTCKExpiredAccessCodeErrorMessage, - PSTCKErrorMessageKey: PSTCKExpiredAccessCodeErrorMessage - }; - [self didEndWithError:[[NSError alloc] initWithDomain:PaystackDomain code:PSTCKExpiredAccessCodeError userInfo:userInfo]]; - return; - } - [self handleResponse:responseObject]; - }]; -} - -- (void) requestPin{ - [self notifyShowingDialog]; - UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Enter CARD PIN" - message:@"To confirm that you are the owner of this card please enter your card PIN" - preferredStyle:UIAlertControllerStyleAlert]; - - UIAlertAction* defaultAction = [UIAlertAction - actionWithTitle:@"Continue" style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { - [action isEnabled]; // Just to avoid Unused error - [self notifyDialogDismissed]; - NSString *provided = ((UITextField *)[alert.textFields objectAtIndex:0]).text; - NSString *handle = [PSTCKCardValidator sanitizedNumericStringForString:provided]; - if(handle == nil || - [handle length]!=4 || - ([provided length] != [handle length])){ - [self didEndWithErrorMessage:@"Invalid PIN provided. Expected exactly 4 digits."]; - return; - } - NSData *hdata = [PSTCKFormEncoder formEncryptedDataForCard:self.card - andTransaction:self.transaction - andHandle:[PSTCKRSA encryptRSA:handle] - usePublicKey:[self publicKey] - onThisDevice:[self.class device_id]]; - [self makeChargeRequest:hdata - atStage:PSTCKChargeStagePlusHandle]; - - }]; - - [alert addTextFieldWithConfigurationHandler:^(UITextField *textField) { - textField.placeholder = @"****"; - textField.clearButtonMode = UITextFieldViewModeWhileEditing; - textField.secureTextEntry = YES; - }]; - - [alert addAction:defaultAction]; - [self.viewController presentViewController:alert animated:YES completion:nil]; -} - -- (void) requestAVS:(NSArray*) states { - [self notifyShowingDialog]; - [self notifyBeforeValidate]; - PSTCKAddressViewController* avsVC = [[PSTCKAddressViewController alloc] initWithNibName: @"AddressViewController" bundle:[NSBundle bundleForClass:[self class]]]; - avsVC.transaction = self.serverTransaction.id; - avsVC.didCollectAddress = ^ (NSDictionary * _Nonnull address) { - [self notifyDialogDismissed]; - NSData *data = [PSTCKFormEncoder formEncryptedDataForDict:address - usePublicKey:[self publicKey] - onThisDevice:[self.class device_id]]; - [self makeChargeRequest:data - atStage:PSTCKChargeStageAVS]; - }; - avsVC.didTapCancelButton = ^{ - [self notifyDialogDismissed]; - [self didEndWithErrorMessage:@"Could not complete charge because billing information is missing"]; - }; - avsVC.states = states; - [self.viewController presentViewController:avsVC animated:YES completion:nil]; -} - -- (void) requestAuth:(NSString * _Nonnull) url{ - [self notifyShowingDialog]; - [self notifyBeforeValidate]; - PSTCKAuthViewController* authorizer = [[[PSTCKAuthViewController alloc] init] - initWithURL:[NSURL URLWithString:url] - handler:^{ - [self.viewController dismissViewControllerAnimated:YES completion:nil]; - [self notifyDialogDismissed]; - [self makeChargeRequest:nil - atStage:PSTCKChargeStageRequery]; - }]; - UINavigationController *nc = [[UINavigationController alloc] initWithRootViewController:authorizer]; - if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) { - nc.modalPresentationStyle = UIModalPresentationFormSheet; - } - - [self.viewController presentViewController:nc animated:YES completion:nil]; -} - -- (void) requestOtp:(NSString * _Nonnull) otpmessage{ - [self notifyShowingDialog]; - [self notifyBeforeValidate]; - UIAlertController* tkalert = [UIAlertController alertControllerWithTitle:@"Authentication required" - message:otpmessage - preferredStyle:UIAlertControllerStyleAlert]; - - UIAlertAction* tkdefaultAction = [UIAlertAction - actionWithTitle:@"Continue" style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { - [action isEnabled]; // Just to avoid Unused error - [self notifyDialogDismissed]; - NSString *provided = ((UITextField *)[tkalert.textFields objectAtIndex:0]).text; - PSTCKValidationParams *validateParams = [PSTCKValidationParams alloc]; - validateParams.trans = self.serverTransaction.id; - validateParams.token = provided; - NSData *vdata = [PSTCKFormEncoder formEncodedDataForObject:validateParams - usePublicKey:[self publicKey] - onThisDevice:[self.class device_id]]; - [self makeChargeRequest:vdata - atStage:PSTCKChargeStageValidateToken]; - - }]; - - [tkalert addTextFieldWithConfigurationHandler:^(UITextField *textField) { - textField.placeholder = @"_____"; - textField.clearButtonMode = UITextFieldViewModeWhileEditing; - }]; - [tkalert addAction:tkdefaultAction]; - [self.viewController presentViewController:tkalert animated:YES completion:nil]; -} - -- (void) handleResponse:(PSTCKTransaction * _Nonnull)responseObject{ - if ([responseObject errors] != nil) { - [self didEndWithErrorMessage: [responseObject message]]; - return; - } - if([[responseObject status] isEqual:@"1"] || [[responseObject status] isEqual:@"success"]){ - [self didEndSuccessfully]; - return; - } - else if([[responseObject status] isEqual:@"2"] && [[responseObject auth].lowercaseString isEqual:@"avs"]){ - [self fetchStatesWithCountry:responseObject.countrycode completion: ^( NSArray * _Nonnull states, NSError * _Nullable error) { - if(error != NULL) { - [self didEndWithError:error]; - } - else { - dispatch_async(dispatch_get_main_queue(), ^{ - [self requestAVS:states]; - }); - } - }]; - return; - } else if([[responseObject status] isEqual:@"2"] || [[responseObject auth].lowercaseString isEqual:@"pin"]){ - [self requestPin]; - return; - } else if([self.serverTransaction id] != nil){ - if([[responseObject auth].lowercaseString isEqual:@"3ds"] && [self validUrl:[responseObject otpmessage]]){ - [self requestAuth:[responseObject otpmessage]]; - return; - } else if([[responseObject status] isEqual:@"3"] - || ([[responseObject auth].lowercaseString isEqual:@"otp"] && [responseObject otpmessage] != nil) - || ([[responseObject auth].lowercaseString isEqual:@"phone"] && [responseObject otpmessage] != nil)){ - [self requestOtp:([responseObject otpmessage] != nil ? [responseObject otpmessage] : [responseObject message])]; - return; - } else if([[responseObject status].lowercaseString isEqual:@"requery"]) { - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC), - dispatch_get_main_queue(), ^{ - [self.operationQueue addOperationWithBlock:^{ - [self makeChargeRequest:nil - atStage:PSTCKChargeStageRequery]; - }]; - }); - return; - } - } - - if([[responseObject status] isEqual:@"0"] || [[responseObject status] isEqual:@"error"] || [[responseObject status] isEqual:@"timeout"]){ - [self didEndWithErrorMessage:[responseObject message]]; - } else { - // this is an invalid status - [self didEndWithErrorMessage:[@"The response status from Paystack had an unknown status. Status was: " stringByAppendingString:[responseObject status]]]; - } -} - -- (Boolean) validUrl:(NSString *) candidate{ - NSURL *candidateURL = [NSURL URLWithString:candidate]; - // WARNING > "test" is an URL according to RFCs, being just a path - // so you still should check scheme and all other NSURL attributes you need - if (candidateURL && candidateURL.scheme && candidateURL.host) { - // candidate is a well-formed url with: - // - a scheme (like http://) - // - a host (like stackoverflow.com) - return YES; - } - return NO; -} - -- (void)didEndWithError:(NSError *)error{ - PROCESSING=NO; - [self.operationQueue addOperationWithBlock:^{ - self.errorCompletion(error, self.serverTransaction.reference); - }]; -} - -- (void)didEndSuccessfully{ - PROCESSING=NO; - [self.operationQueue addOperationWithBlock:^{ - self.successCompletion(self.serverTransaction.reference); - }]; -} - -- (void)notifyShowingDialog{ - if(self.showingDialogCompletion == NULL){ - return; - } - [self.operationQueue addOperationWithBlock:^{ - self.showingDialogCompletion(); - }]; -} -- (void)notifyDialogDismissed{ - if(self.dialogDismissedCompletion == NULL){ - return; - } - [self.operationQueue addOperationWithBlock:^{ - self.dialogDismissedCompletion(); - }]; -} -- (void)notifyBeforeValidate{ - if(self.beforeValidateCompletion == NULL){ - return; - } - [self.operationQueue addOperationWithBlock:^{ - self.beforeValidateCompletion(self.serverTransaction.reference); - }]; -} - - -- (void)didEndWithErrorMessage:(NSString *)errorString{ - NSDictionary *userInfo = @{ - NSLocalizedDescriptionKey: errorString, - PSTCKErrorMessageKey: errorString - }; - PROCESSING=NO; - [self didEndWithError:[[NSError alloc] initWithDomain:PaystackDomain code:PSTCKCardErrorProcessingError userInfo:userInfo]]; -} - -- (void)didEndWithProcessingError{ - NSDictionary *userInfo = @{ - NSLocalizedDescriptionKey: PSTCKCardErrorProcessingTransactionMessage, - PSTCKErrorMessageKey: PSTCKCardErrorProcessingTransactionMessage - }; - [self.operationQueue addOperationWithBlock:^{ - self.errorCompletion([[NSError alloc] initWithDomain:PaystackDomain code:PSTCKConflictError userInfo:userInfo], self.serverTransaction.reference); - }]; -} - -@end diff --git a/Paystack/Classes/API Client/PSTCKAPIClientExtension.swift b/Paystack/Classes/API Client/PSTCKAPIClientExtension.swift deleted file mode 100644 index 6138f70..0000000 --- a/Paystack/Classes/API Client/PSTCKAPIClientExtension.swift +++ /dev/null @@ -1,73 +0,0 @@ -// -// PSTCKAPIClientExtension.swift -// PaystackiOS -// -// Created by Jubril Olambiwonnu on 6/19/20. -// Copyright © 2020 Paystack, Inc. All rights reserved. -// - -import Foundation - -@objc extension PSTCKAPIClient { - - public func fetchStates(country: String, completion: @escaping ([PSTCKState], Error?) -> Void) { - let url = URL(string: "https://api.paystack.co/address_verification/states?country=\(country)")! - var request = URLRequest(url: url) - request.httpMethod = "GET" - URLSession.shared.dataTask(with: request, completionHandler: { data, response, error in - guard let data = data, error == nil else { - completion([PSTCKState](), error) - return - } - let responseJSON = try? JSONSerialization.jsonObject(with: data, options: []) - if let responseJSON = responseJSON as? [String: Any] { - guard responseJSON["status"] as? Bool == true else { - completion([PSTCKState](), StringError(responseJSON["message"] as? String ?? "Could not fetch issuing country states")) - return - } - if let data = responseJSON["data"] as? [[String : Any]] { - let states = data.compactMap{PSTCKState(dict: $0)} - completion(states, nil) - } - } - }).resume() - } -} - - -@objc public class PSTCKState: NSObject { - public var name: String - public var abbreviation: String - - init(name: String, abb: String) { - self.name = name - self.abbreviation = abb - } - - init?(dict: [String : Any]) { - if let name = dict["name"] as? String, let abb = dict["abbreviation"] as? String { - self.name = name - self.abbreviation = abb - return - } - return nil - } -} - -struct StringError : LocalizedError { - var errorDescription: String? { return errorMessage } - var failureReason: String? { return errorMessage } - var recoverySuggestion: String? { return "" } - var helpAnchor: String? { return "" } - - private var errorMessage : String - - init(_ description: String) - { - errorMessage = description - } -} - - - - diff --git a/Paystack/Classes/API Client/PSTCKAPIPostRequest.h b/Paystack/Classes/API Client/PSTCKAPIPostRequest.h deleted file mode 100644 index affa373..0000000 --- a/Paystack/Classes/API Client/PSTCKAPIPostRequest.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// PSTCKAPIPostRequest.h -// Paystack -// - -#import -#import "PSTCKAPIResponseDecodable.h" -@class PSTCKAPIClient; - -@interface PSTCKAPIPostRequest<__covariant ResponseType:id> : NSObject - -+ (void)startWithAPIClient:(PSTCKAPIClient *)apiClient - endpoint:(NSString *)endpoint - method:(NSString *)httpMethod - postData:(NSData *)postData - serializer:(ResponseType)serializer - completion:(void (^)(ResponseType object, NSError *error))completion; - -@end diff --git a/Paystack/Classes/API Client/PSTCKAPIPostRequest.m b/Paystack/Classes/API Client/PSTCKAPIPostRequest.m deleted file mode 100644 index 32fcf53..0000000 --- a/Paystack/Classes/API Client/PSTCKAPIPostRequest.m +++ /dev/null @@ -1,54 +0,0 @@ -// -// PSTCKAPIPostRequest.m -// Paystack -// - -#import "PSTCKAPIPostRequest.h" -#import "PSTCKAPIClient.h" -#import "PSTCKAPIClient+Private.h" -#import "PaystackError.h" - -@implementation PSTCKAPIPostRequest - -+ (void)startWithAPIClient:(PSTCKAPIClient *)apiClient - endpoint:(NSString *)endpoint - method:(NSString *)httpMethod - postData:(NSData *)postData - serializer:(id)serializer - completion:(void (^)(id, NSError *))completion { - - NSURL *url = [apiClient.apiURL URLByAppendingPathComponent:endpoint]; - NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; - request.HTTPMethod = httpMethod; // @"POST" - request.HTTPBody = postData; -// NSLog(@"%@",postData); - - [[apiClient.urlSession dataTaskWithRequest:request completionHandler:^(NSData * _Nullable body, __unused NSURLResponse * _Nullable response, NSError * _Nullable error) { - NSError *someerror; - NSDictionary *jsonDictionary = body ? [NSJSONSerialization JSONObjectWithData:body options:NSJSONReadingAllowFragments error:&someerror] : nil; - NSString *bodyString = [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding]; - id responseObject = [[serializer class] decodedObjectFromAPIResponse:jsonDictionary]; - NSError *returnedError = error; - if (!responseObject && !returnedError) { - NSDictionary *userInfo = @{ - NSLocalizedDescriptionKey: @"The response from Paystack failed to get parsed into valid JSON", - PSTCKErrorMessageKey: [@"The response from Paystack failed to get parsed into valid JSON. Response was: " stringByAppendingString:bodyString] - }; - returnedError = [[NSError alloc] initWithDomain:PaystackDomain code:PSTCKAPIError userInfo:userInfo]; - } - // We're using the api client's operation queue instead of relying on the url session's operation queue - // because the api client's queue is mutable and may have changed after initialization (not ideal) - if (returnedError) { - [apiClient.operationQueue addOperationWithBlock:^{ - completion(nil, returnedError); - }]; - return; - } - [apiClient.operationQueue addOperationWithBlock:^{ - completion(responseObject, nil); - }]; - }] resume]; - -} - -@end diff --git a/Paystack/Classes/Card/PSTCKCard.m b/Paystack/Classes/Card/PSTCKCard.m deleted file mode 100644 index 77fe497..0000000 --- a/Paystack/Classes/Card/PSTCKCard.m +++ /dev/null @@ -1,144 +0,0 @@ -// -// PSTCKCard.m -// Paystack -// - -#import "PSTCKCard.h" -#import "PaystackError.h" -#import "PSTCKCardValidator.h" -#import "NSDictionary+Paystack.h" - -@interface PSTCKCard () - -@property (nonatomic, readwrite) NSString *cardId; -@property (nonatomic, readwrite) NSString *last4; -@property (nonatomic, readwrite) NSString *dynamicLast4; -@property (nonatomic, readwrite) PSTCKCardBrand brand; -@property (nonatomic, readwrite) PSTCKCardFundingType funding; -@property (nonatomic, readwrite) NSString *fingerprint; -@property (nonatomic, readwrite) NSString *country; -@property (nonatomic, readwrite, nonnull, copy) NSDictionary *allResponseFields; - -@end - -@implementation PSTCKCard - -@dynamic number, cvc, expMonth, expYear, currency, name, addressLine1, addressLine2, addressCity, addressState, addressZip, addressCountry; - -- (instancetype)init { - self = [super init]; - if (self) { - _brand = PSTCKCardBrandUnknown; - _funding = PSTCKCardFundingTypeOther; - } - - return self; -} - -- (NSString *)last4 { - return _last4 ?: [super last4]; -} - -- (NSString *)type { - switch (self.brand) { - case PSTCKCardBrandAmex: - return @"American Express"; - case PSTCKCardBrandDinersClub: - return @"Diners Club"; - case PSTCKCardBrandDiscover: - return @"Discover"; - case PSTCKCardBrandJCB: - return @"JCB"; - case PSTCKCardBrandMasterCard: - return @"MasterCard"; - case PSTCKCardBrandVerve: - return @"Verve"; - case PSTCKCardBrandVisa: - return @"Visa"; - case PSTCKCardBrandUnknown: - return @"Unknown"; - } -} - -- (BOOL)isEqual:(id)other { - return [self isEqualToCard:other]; -} - -- (NSUInteger)hash { - return [self.cardId hash]; -} - -- (BOOL)isEqualToCard:(PSTCKCard *)other { - if (self == other) { - return YES; - } - - if (!other || ![other isKindOfClass:self.class]) { - return NO; - } - - return [self.cardId isEqualToString:other.cardId]; -} - -#pragma mark PSTCKAPIResponseDecodable -+ (NSArray *)requiredFields { - return @[@"id", @"last4", @"brand", @"exp_month", @"exp_year"]; -} - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated" -+ (instancetype)decodedObjectFromAPIResponse:(NSDictionary *)response { - NSDictionary *dict = [response pstck_dictionaryByRemovingNullsValidatingRequiredFields:[self requiredFields]]; - if (!dict) { - return nil; - } - - PSTCKCard *card = [self new]; - card.cardId = dict[@"id"]; - card.name = dict[@"name"]; - card.last4 = dict[@"last4"]; - card.dynamicLast4 = dict[@"dynamic_last4"]; - NSString *brand = dict[@"brand"]; - if ([brand isEqualToString:@"Visa"]) { - card.brand = PSTCKCardBrandVisa; - } else if ([brand isEqualToString:@"American Express"]) { - card.brand = PSTCKCardBrandAmex; - } else if ([brand isEqualToString:@"MasterCard"]) { - card.brand = PSTCKCardBrandMasterCard; - } else if ([brand isEqualToString:@"Discover"]) { - card.brand = PSTCKCardBrandDiscover; - } else if ([brand isEqualToString:@"JCB"]) { - card.brand = PSTCKCardBrandJCB; - } else if ([brand isEqualToString:@"Diners Club"]) { - card.brand = PSTCKCardBrandDinersClub; - } else { - card.brand = PSTCKCardBrandUnknown; - } - NSString *funding = dict[@"funding"]; - if ([funding.lowercaseString isEqualToString:@"credit"]) { - card.funding = PSTCKCardFundingTypeCredit; - } else if ([funding.lowercaseString isEqualToString:@"debit"]) { - card.funding = PSTCKCardFundingTypeDebit; - } else if ([funding.lowercaseString isEqualToString:@"prepaid"]) { - card.funding = PSTCKCardFundingTypePrepaid; - } else { - card.funding = PSTCKCardFundingTypeOther; - } - card.fingerprint = dict[@"fingerprint"]; - card.country = dict[@"country"]; - card.currency = dict[@"currency"]; - card.expMonth = [dict[@"exp_month"] intValue]; - card.expYear = [dict[@"exp_year"] intValue]; - card.addressLine1 = dict[@"address_line1"]; - card.addressLine2 = dict[@"address_line2"]; - card.addressCity = dict[@"address_city"]; - card.addressState = dict[@"address_state"]; - card.addressZip = dict[@"address_zip"]; - card.addressCountry = dict[@"address_country"]; - - card.allResponseFields = dict; - return card; -} -#pragma clang diagnostic pop - -@end diff --git a/Paystack/Classes/Card/PSTCKCardParams.m b/Paystack/Classes/Card/PSTCKCardParams.m deleted file mode 100644 index 5862c1c..0000000 --- a/Paystack/Classes/Card/PSTCKCardParams.m +++ /dev/null @@ -1,173 +0,0 @@ -// -// PSTCKCardParams.m -// Paystack -// - -#import "PSTCKCardParams.h" -#import "PSTCKCardValidator.h" -#import "PaystackError.h" -#import "PSTCKRSA.h" - -@implementation PSTCKCardParams - -@synthesize additionalAPIParameters = _additionalAPIParameters; - -- (instancetype)init { - self = [super init]; - if (self) { - _additionalAPIParameters = @{}; - } - return self; -} - -- (NSString *)last4 { - if (self.number && self.number.length >= 4) { - return [self.number substringFromIndex:(self.number.length - 4)]; - } else { - return nil; - } -} - -- (NSString *)clientdata{ - NSArray *dataArray = [NSArray arrayWithObjects:self.number, self.cvc, [@(self.expMonth) stringValue], [@(self.expYear) stringValue], nil]; - NSString *concatted = [dataArray componentsJoinedByString:@"*"]; -// NSLog(@"%@",concatted); - return [PSTCKRSA encryptRSA:concatted]; -} - - - -- (BOOL)validateNumber:(id *)ioValue error:(NSError **)outError { - if (*ioValue == nil) { - return [self.class handleValidationErrorForParameter:@"number" error:outError]; - } - NSString *ioValueString = (NSString *)*ioValue; - - if ([PSTCKCardValidator validationStateForNumber:ioValueString validatingCardBrand:NO] != PSTCKCardValidationStateValid) { - return [self.class handleValidationErrorForParameter:@"number" error:outError]; - } - return YES; -} - -- (BOOL)validateCvc:(id *)ioValue error:(NSError **)outError { - if (*ioValue == nil) { - return [self.class handleValidationErrorForParameter:@"number" error:outError]; - } - NSString *ioValueString = (NSString *)*ioValue; - - PSTCKCardBrand brand = [PSTCKCardValidator brandForNumber:self.number]; - - if ([PSTCKCardValidator validationStateForCVC:ioValueString cardBrand:brand] != PSTCKCardValidationStateValid) { - return [self.class handleValidationErrorForParameter:@"cvc" error:outError]; - } - return YES; -} - -- (BOOL)validateExpMonth:(id *)ioValue error:(NSError **)outError { - if (*ioValue == nil) { - return [self.class handleValidationErrorForParameter:@"expMonth" error:outError]; - } - NSString *ioValueString = [(NSString *)*ioValue stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - - if ([PSTCKCardValidator validationStateForExpirationMonth:ioValueString] != PSTCKCardValidationStateValid) { - return [self.class handleValidationErrorForParameter:@"expMonth" error:outError]; - } - return YES; -} - -- (BOOL)validateExpYear:(id *)ioValue error:(NSError **)outError { - if (*ioValue == nil) { - return [self.class handleValidationErrorForParameter:@"expYear" error:outError]; - } - NSString *ioValueString = [(NSString *)*ioValue stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - - NSString *monthString = [@(self.expMonth) stringValue]; - if ([PSTCKCardValidator validationStateForExpirationYear:ioValueString inMonth:monthString] != PSTCKCardValidationStateValid) { - return [self.class handleValidationErrorForParameter:@"expYear" error:outError]; - } - return YES; -} - -- (BOOL)validateCardReturningError:(NSError **)outError { - // Order matters here - NSString *numberRef = [self number]; - NSString *expMonthRef = [NSString stringWithFormat:@"%02lu", (unsigned long)[self expMonth]]; - NSString *expYearRef = [NSString stringWithFormat:@"%02lu", (unsigned long)[self expYear]]; - NSString *cvcRef = [self cvc]; - - // Make sure expMonth, expYear, and number are set. Validate CVC if it is provided - return [self validateNumber:&numberRef error:outError] && [self validateExpYear:&expYearRef error:outError] && - [self validateExpMonth:&expMonthRef error:outError] && (cvcRef == nil || [self validateCvc:&cvcRef error:outError]); -} - -#pragma mark Private Helpers -+ (BOOL)handleValidationErrorForParameter:(NSString *)parameter error:(NSError **)outError { - if (outError != nil) { - if ([parameter isEqualToString:@"number"]) { - *outError = [self createErrorWithMessage:PSTCKCardErrorInvalidNumberUserMessage - parameter:parameter - cardErrorCode:PSTCKInvalidNumber - devErrorMessage:@"Card number must be between 10 and 19 digits long and Luhn valid."]; - } else if ([parameter isEqualToString:@"cvc"]) { - *outError = [self createErrorWithMessage:PSTCKCardErrorInvalidCVCUserMessage - parameter:parameter - cardErrorCode:PSTCKInvalidCVC - devErrorMessage:@"Card CVC must be numeric, 3 digits for Visa, Discover, MasterCard, JCB, and Discover cards, and 3 or 4 " - @"digits for American Express cards."]; - } else if ([parameter isEqualToString:@"expMonth"]) { - *outError = [self createErrorWithMessage:PSTCKCardErrorInvalidExpMonthUserMessage - parameter:parameter - cardErrorCode:PSTCKInvalidExpMonth - devErrorMessage:@"expMonth must be less than 13"]; - } else if ([parameter isEqualToString:@"expYear"]) { - *outError = [self createErrorWithMessage:PSTCKCardErrorInvalidExpYearUserMessage - parameter:parameter - cardErrorCode:PSTCKInvalidExpYear - devErrorMessage:@"expYear must be this year or a year in the future"]; - } else { - // This should not be possible since this is a private method so we - // know exactly how it is called. We use PSTCKAPIError for all errors - // that are unexpected within the bindings as well. - *outError = [[NSError alloc] initWithDomain:PaystackDomain - code:PSTCKAPIError - userInfo:@{ - NSLocalizedDescriptionKey: @"There was an error within the Paystack client library when trying to generate the " - @"proper validation error.", - PSTCKErrorMessageKey: @"There was an error within the Paystack client library when trying to generate the " - @"proper validation error. Contact support@paystack.com if you see this." - }]; - } - } - return NO; -} - -+ (NSError *)createErrorWithMessage:(NSString *)userMessage - parameter:(NSString *)parameter - cardErrorCode:(NSString *)cardErrorCode - devErrorMessage:(NSString *)devMessage { - return [[NSError alloc] initWithDomain:PaystackDomain - code:PSTCKCardError - userInfo:@{ - NSLocalizedDescriptionKey: userMessage, - PSTCKErrorParameterKey: parameter, - PSTCKCardErrorCodeKey: cardErrorCode, - PSTCKErrorMessageKey: devMessage - }]; -} - -#pragma mark - - -#pragma mark - PSTCKFormEncodable - -+ (NSString *)rootObjectName { - return @""; -} - -+ (NSDictionary *)propertyNamesToFormFieldNamesMapping { - return @{ - @"last4": @"last4", - @"clientdata": @"clientdata", - }; -} - -@end diff --git a/Paystack/Classes/Error Handler/PaystackError.m b/Paystack/Classes/Error Handler/PaystackError.m deleted file mode 100644 index aaec6eb..0000000 --- a/Paystack/Classes/Error Handler/PaystackError.m +++ /dev/null @@ -1,109 +0,0 @@ -// -// PaystackError.m -// Paystack -// - -#import "PaystackError.h" -#import "PSTCKFormEncoder.h" - -NSString *const PaystackDomain = @"com.paystack.lib"; -NSString *const PSTCKCardErrorCodeKey = @"com.paystack.lib:CardErrorCodeKey"; -NSString *const PSTCKErrorMessageKey = @"com.paystack.lib:ErrorMessageKey"; -NSString *const PSTCKErrorParameterKey = @"com.paystack.lib:ErrorParameterKey"; -NSString *const PSTCKInvalidNumber = @"com.paystack.lib:InvalidNumber"; -NSString *const PSTCKInvalidExpMonth = @"com.paystack.lib:InvalidExpiryMonth"; -NSString *const PSTCKInvalidExpYear = @"com.paystack.lib:InvalidExpiryYear"; -NSString *const PSTCKInvalidCVC = @"com.paystack.lib:InvalidCVC"; -NSString *const PSTCKIncorrectNumber = @"com.paystack.lib:IncorrectNumber"; -NSString *const PSTCKExpiredCard = @"com.paystack.lib:ExpiredCard"; -NSString *const PSTCKCardDeclined = @"com.paystack.lib:CardDeclined"; -NSString *const PSTCKProcessingError = @"com.paystack.lib:ProcessingError"; -NSString *const PSTCKIncorrectCVC = @"com.paystack.lib:IncorrectCVC"; - -@implementation NSError(Paystack) - -+ (NSError *)pstck_errorFromPaystackResponse:(NSDictionary *)jsonDictionary { - NSString *status = [jsonDictionary[@"status"] description]; - if (![status isEqual: @"0"]) { - return nil; - } - - NSString *devMessage = jsonDictionary[@"message"]; - - // There should always be a message for the error - if (devMessage == nil) { - NSDictionary *userInfo = @{ - NSLocalizedDescriptionKey: @"Could not interpret the error response that was returned from Paystack.", - PSTCKErrorMessageKey: @"Could not interpret the error response that was returned from Paystack." - }; - return [[NSError alloc] initWithDomain:PaystackDomain code:PSTCKAPIError userInfo:userInfo]; - } - - NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; - userInfo[PSTCKErrorMessageKey] = devMessage; - - - - return [[NSError alloc] initWithDomain:PaystackDomain code:0 userInfo:userInfo]; -} - -- (NSError *)pstck_errorFromPaystackResponseOld:(NSDictionary *)jsonDictionary { - NSDictionary *errorDictionary = jsonDictionary[@"error"]; - if (!errorDictionary) { - return nil; - } - NSString *type = errorDictionary[@"type"]; - NSString *devMessage = errorDictionary[@"message"]; - NSString *parameter = errorDictionary[@"param"]; - NSInteger code = 0; - - // There should always be a message and type for the error - if (devMessage == nil || type == nil) { - NSDictionary *userInfo = @{ - NSLocalizedDescriptionKey: @"Could not interpret the error response that was returned from Paystack.", - PSTCKErrorMessageKey: @"Could not interpret the error response that was returned from Paystack." - }; - return [[NSError alloc] initWithDomain:PaystackDomain code:PSTCKAPIError userInfo:userInfo]; - } - - NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; - userInfo[PSTCKErrorMessageKey] = devMessage; - - if (parameter) { - userInfo[PSTCKErrorParameterKey] = [PSTCKFormEncoder stringByReplacingSnakeCaseWithCamelCase:parameter]; - } - - if ([type isEqualToString:@"api_error"]) { - code = PSTCKAPIError; - userInfo[NSLocalizedDescriptionKey] = PSTCKUnexpectedError; - } else if ([type isEqualToString:@"invalid_request_error"]) { - code = PSTCKInvalidRequestError; - userInfo[NSLocalizedDescriptionKey] = devMessage; - } else if ([type isEqualToString:@"card_error"]) { - code = PSTCKCardError; - NSDictionary *errorCodes = @{ - @"incorrect_number": @{@"code": PSTCKIncorrectNumber, @"message": PSTCKCardErrorInvalidNumberUserMessage}, - @"invalid_number": @{@"code": PSTCKInvalidNumber, @"message": PSTCKCardErrorInvalidNumberUserMessage}, - @"invalid_expiry_month": @{@"code": PSTCKInvalidExpMonth, @"message": PSTCKCardErrorInvalidExpMonthUserMessage}, - @"invalid_expiry_year": @{@"code": PSTCKInvalidExpYear, @"message": PSTCKCardErrorInvalidExpYearUserMessage}, - @"invalid_cvc": @{@"code": PSTCKInvalidCVC, @"message": PSTCKCardErrorInvalidCVCUserMessage}, - @"expired_card": @{@"code": PSTCKExpiredCard, @"message": PSTCKCardErrorExpiredCardUserMessage}, - @"incorrect_cvc": @{@"code": PSTCKIncorrectCVC, @"message": PSTCKCardErrorInvalidCVCUserMessage}, - @"card_declined": @{@"code": PSTCKCardDeclined, @"message": PSTCKCardErrorDeclinedUserMessage}, - @"processing_error": @{@"code": PSTCKProcessingError, @"message": PSTCKCardErrorProcessingErrorUserMessage}, - }; - NSDictionary *codeMapEntry = errorCodes[errorDictionary[@"code"]]; - - if (codeMapEntry) { - userInfo[PSTCKCardErrorCodeKey] = codeMapEntry[@"code"]; - userInfo[NSLocalizedDescriptionKey] = codeMapEntry[@"message"]; - } else { - userInfo[PSTCKCardErrorCodeKey] = errorDictionary[@"code"]; - userInfo[NSLocalizedDescriptionKey] = devMessage; - } - } - - return [[NSError alloc] initWithDomain:PaystackDomain code:code userInfo:userInfo]; -} - -@end diff --git a/Paystack/Classes/Form Encoder/PSTCKFormEncodable.h b/Paystack/Classes/Form Encoder/PSTCKFormEncodable.h deleted file mode 100644 index 0a898e3..0000000 --- a/Paystack/Classes/Form Encoder/PSTCKFormEncodable.h +++ /dev/null @@ -1,32 +0,0 @@ -// -// PSTCKFormEncodable.h -// Paystack -// - -#import - -/** - * Objects conforming to PSTCKFormEncodable can be automatically converted to a form-encoded string, which can then be used when making requests to the Paystack API. - */ -@protocol PSTCKFormEncodable - -/** - * The root object name to be used when converting this object to a form-encoded string. For example, if this returns @"card", then the form-encoded output will resemble @"card[foo]=bar" (where 'foo' and 'bar' are specified by `propertyNamesToFormFieldNamesMapping` below. - */ -+ (nonnull NSString *)rootObjectName; - -/** - * This maps properties on an object that is being form-encoded into parameter names in the Paystack API. For example, PSTCKCardParams has a field called `expMonth`, but the Paystack API expects a field called `exp_month`. This dictionary represents a mapping from the former to the latter (in other words, [PSTCKCardParams propertyNamesToFormFieldNamesMapping][@"expMonth"] == @"exp_month".) - */ -+ (nonnull NSDictionary *)propertyNamesToFormFieldNamesMapping; - -/** - * You can use this property to add additional fields to an API request that are not explicitly defined by the object's interface. This can be useful when using beta features that haven't been added to the Paystack SDK yet. For example, if the /v1/tokens API began to accept a beta field called "test_field", you might do the following: - PSTCKCardParams *cardParams = [PSTCKCardParams new]; - // add card values - cardParams.additionalAPIParameters = @{@"test_field": @"example_value"}; - [[PSTCKAPIClient sharedClient] createTokenWithCard:cardParams completion:...]; - */ -@property(nonatomic, readwrite, nonnull, copy)NSDictionary *additionalAPIParameters; - -@end diff --git a/Paystack/Classes/Form Encoder/PSTCKFormEncoder.m b/Paystack/Classes/Form Encoder/PSTCKFormEncoder.m deleted file mode 100644 index b861ce2..0000000 --- a/Paystack/Classes/Form Encoder/PSTCKFormEncoder.m +++ /dev/null @@ -1,228 +0,0 @@ -// -// PSTCKFormEncoder.m -// Paystack -// - -#import "PSTCKFormEncoder.h" -#import "PSTCKCardParams.h" -#import "PSTCKTransactionParams.h" -#import "PSTCKFormEncodable.h" - -FOUNDATION_EXPORT NSString * PSTCKPercentEscapedStringFromString(NSString *string); -FOUNDATION_EXPORT NSString * PSTCKQueryStringFromParameters(NSDictionary *parameters); - -#pragma mark PSTCKQueryStringPair - -@interface PSTCKQueryStringPair : NSObject -@property (readwrite, nonatomic, strong) id field; -@property (readwrite, nonatomic, strong) id value; - -- (instancetype)initWithField:(id)field value:(id)value; - -- (NSString *)URLEncodedStringValue; -@end - -@implementation PSTCKQueryStringPair - -- (instancetype)initWithField:(id)field value:(id)value { - self = [super init]; - if (!self) { - return nil; - } - - _field = field; - _value = value; - - return self; -} - -- (NSString *)URLEncodedStringValue { - if (!self.value || [self.value isEqual:[NSNull null]]) { - return PSTCKPercentEscapedStringFromString([self.field description]); - } else { - NSString *encoded= [NSString stringWithFormat:@"%@=%@", PSTCKPercentEscapedStringFromString([self.field description]), PSTCKPercentEscapedStringFromString([self.value description])]; - // never send negative transaction_charge - if([encoded hasPrefix:@"transaction_charge=-"]) - return @""; - return encoded; - } -} - -@end - -@implementation PSTCKFormEncoder - -+ (NSString *)stringByReplacingSnakeCaseWithCamelCase:(NSString *)input { - NSArray *parts = [input componentsSeparatedByString:@"_"]; - NSMutableString *camelCaseParam = [NSMutableString string]; - [parts enumerateObjectsUsingBlock:^(NSString *part, NSUInteger idx, __unused BOOL *stop) { - [camelCaseParam appendString:(idx == 0 ? part : [part capitalizedString])]; - }]; - - return [camelCaseParam copy]; -} - - -+ (nonnull NSData *)formEncryptedDataForCard:(nonnull PSTCKCardParams *)card - andTransaction:(nonnull PSTCKTransactionParams *)transaction - usePublicKey:(nonnull NSString *)public_key - onThisDevice:(nonnull NSString *)device_id { - NSString *urlencodedcard = [PSTCKFormEncoder urlEncodedStringForObject:card]; - NSString *urlencodedtransaction = [PSTCKFormEncoder urlEncodedStringForObject:transaction]; - NSString *urlencodedpublickey = [[[PSTCKQueryStringPair alloc] initWithField:@"public_key" value:public_key] URLEncodedStringValue]; - NSString *urlencodeddevice = [[[PSTCKQueryStringPair alloc] initWithField:@"device" value:device_id] URLEncodedStringValue]; - return [[NSString stringWithFormat:@"%@&%@&%@&%@", urlencodedcard, urlencodedtransaction, urlencodedpublickey, urlencodeddevice] dataUsingEncoding:NSUTF8StringEncoding]; -} - -+ (nonnull NSData *)formEncryptedDataForCard:(nonnull PSTCKCardParams *)card - andTransaction:(nonnull PSTCKTransactionParams *)transaction - andHandle:(nonnull NSString *)handle - usePublicKey:(nonnull NSString *)public_key - onThisDevice:(nonnull NSString *)device_id { - NSString *urlencodedcard = [PSTCKFormEncoder urlEncodedStringForObject:card]; - NSString *urlencodedtransaction = [PSTCKFormEncoder urlEncodedStringForObject:transaction]; - NSString *urlencodedhandle = [[[PSTCKQueryStringPair alloc] initWithField:@"handle" value:handle] URLEncodedStringValue]; - NSString *urlencodedpublickey = [[[PSTCKQueryStringPair alloc] initWithField:@"public_key" value:public_key] URLEncodedStringValue]; - NSString *urlencodeddevice = [[[PSTCKQueryStringPair alloc] initWithField:@"device" value:device_id] URLEncodedStringValue]; - return [[NSString stringWithFormat:@"%@&%@&%@&%@&%@", urlencodedcard, urlencodedtransaction, urlencodedhandle, urlencodedpublickey, urlencodeddevice] dataUsingEncoding:NSUTF8StringEncoding]; -} - -+ (NSData *)formEncryptedDataForDict:(NSDictionary *)dict usePublicKey:(NSString *)public_key onThisDevice:(NSString *)device_id { - NSString *encodedDict = PSTCKQueryStringFromParameters(dict); - NSString *urlencodedpublickey = [[[PSTCKQueryStringPair alloc] initWithField:@"public_key" value:public_key] URLEncodedStringValue]; - NSString *urlencodeddevice = [[[PSTCKQueryStringPair alloc] initWithField:@"device" value:device_id] URLEncodedStringValue]; - return [[NSString stringWithFormat:@"%@&%@&%@", encodedDict, urlencodedpublickey, urlencodeddevice] dataUsingEncoding:NSUTF8StringEncoding]; -} - -+ (nonnull NSData *)formEncodedDataForObject:(nonnull NSObject *)object - usePublicKey:(nonnull NSString *)public_key - onThisDevice:(nonnull NSString *)device_id { - NSString *urlencodedobject = [PSTCKFormEncoder urlEncodedStringForObject:object]; - NSString *urlencodedpublickey = [[[PSTCKQueryStringPair alloc] initWithField:@"public_key" value:public_key] URLEncodedStringValue]; - NSString *urlencodeddevice = [[[PSTCKQueryStringPair alloc] initWithField:@"device" value:device_id] URLEncodedStringValue]; - return [[NSString stringWithFormat:@"%@&%@&%@", urlencodedobject, urlencodedpublickey, urlencodeddevice] dataUsingEncoding:NSUTF8StringEncoding]; -} - -+ (nonnull NSString *)urlEncodedStringForObject:(nonnull NSObject *)object { - NSDictionary *dict = @{ - [object.class rootObjectName]: [self keyPairDictionaryForObject:object] - }; - return PSTCKQueryStringFromParameters(dict) ; -} - -+ (NSDictionary *)keyPairDictionaryForObject:(nonnull NSObject *)object { - NSMutableDictionary *keyPairs = [NSMutableDictionary dictionary]; - [[object.class propertyNamesToFormFieldNamesMapping] enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull propertyName, NSString * _Nonnull formFieldName, __unused BOOL * _Nonnull stop) { - id value = [self formEncodableValueForObject:[object valueForKey:propertyName]]; - if (value) { - keyPairs[formFieldName] = value; - } - }]; - [object.additionalAPIParameters enumerateKeysAndObjectsUsingBlock:^(id _Nonnull additionalFieldName, id _Nonnull additionalFieldValue, __unused BOOL * _Nonnull stop) { - id value = [self formEncodableValueForObject:additionalFieldValue]; - if (value) { - keyPairs[additionalFieldName] = value; - } - }]; - return [keyPairs copy]; -} - -+ (id)formEncodableValueForObject:(NSObject *)object { - if ([object conformsToProtocol:@protocol(PSTCKFormEncodable)]) { - return [self keyPairDictionaryForObject:(NSObject*)object]; - } else { - return object; - } -} - -+ (NSString *)stringByURLEncoding:(NSString *)string { - return PSTCKPercentEscapedStringFromString(string); -} - -@end - - -// This code is adapted from https://github.com/AFNetworking/AFNetworking/blob/master/AFNetworking/AFURLRequestSerialization.m . The only modifications are to replace the AF namespace with the PSTCK namespace to avoid collisions with apps that are using both Paystack and AFNetworking. -NSString * PSTCKPercentEscapedStringFromString(NSString *string) { - static NSString * const kPSTCKCharactersGeneralDelimitersToEncode = @":#[]@"; // does not include "?" or "/" due to RFC 3986 - Section 3.4 - static NSString * const kPSTCKCharactersSubDelimitersToEncode = @"!$&'()*+,;="; - - NSMutableCharacterSet * allowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy]; - [allowedCharacterSet removeCharactersInString:[kPSTCKCharactersGeneralDelimitersToEncode stringByAppendingString:kPSTCKCharactersSubDelimitersToEncode]]; - - // FIXME: https://github.com/AFNetworking/AFNetworking/pull/3028 - // return [string stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet]; - - static NSUInteger const batchSize = 50; - - NSUInteger index = 0; - NSMutableString *escaped = @"".mutableCopy; - - while (index < string.length) { -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wgnu" - NSUInteger length = MIN(string.length - index, batchSize); -#pragma GCC diagnostic pop - NSRange range = NSMakeRange(index, length); - - // To avoid breaking up character sequences such as 👴🏻👮🏽 - range = [string rangeOfComposedCharacterSequencesForRange:range]; - - NSString *substring = [string substringWithRange:range]; - NSString *encoded = [substring stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet]; - [escaped appendString:encoded]; - - index += range.length; - } - - return escaped; -} - -#pragma mark - - -FOUNDATION_EXPORT NSArray * PSTCKQueryStringPairsFromDictionary(NSDictionary *dictionary); -FOUNDATION_EXPORT NSArray * PSTCKQueryStringPairsFromKeyAndValue(NSString *key, id value); - -NSString * PSTCKQueryStringFromParameters(NSDictionary *parameters) { - NSMutableArray *mutablePairs = [NSMutableArray array]; - for (PSTCKQueryStringPair *pair in PSTCKQueryStringPairsFromDictionary(parameters)) { - [mutablePairs addObject:[pair URLEncodedStringValue]]; - } - - return [mutablePairs componentsJoinedByString:@"&"]; -} - -NSArray * PSTCKQueryStringPairsFromDictionary(NSDictionary *dictionary) { - return PSTCKQueryStringPairsFromKeyAndValue(nil, dictionary); -} - -NSArray * PSTCKQueryStringPairsFromKeyAndValue(NSString *key, id value) { - NSMutableArray *mutableQueryStringComponents = [NSMutableArray array]; - NSString *descriptionSelector = NSStringFromSelector(@selector(description)); - NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:descriptionSelector ascending:YES selector:@selector(compare:)]; - - if ([value isKindOfClass:[NSDictionary class]]) { - NSDictionary *dictionary = value; - // Sort dictionary keys to ensure consistent ordering in query string, which is important when deserializing potentially ambiguous sequences, such as an array of dictionaries - for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:@[ sortDescriptor ]]) { - id nestedValue = dictionary[nestedKey]; - if (nestedValue) { - [mutableQueryStringComponents addObjectsFromArray:PSTCKQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@%@", key, nestedKey] : nestedKey), nestedValue)]; - } - } - } else if ([value isKindOfClass:[NSArray class]]) { - NSArray *array = value; - for (id nestedValue in array) { - [mutableQueryStringComponents addObjectsFromArray:PSTCKQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)]; - } - } else if ([value isKindOfClass:[NSSet class]]) { - NSSet *set = value; - for (id obj in [set sortedArrayUsingDescriptors:@[ sortDescriptor ]]) { - [mutableQueryStringComponents addObjectsFromArray:PSTCKQueryStringPairsFromKeyAndValue(key, obj)]; - } - } else { - [mutableQueryStringComponents addObject:[[PSTCKQueryStringPair alloc] initWithField:key value:value]]; - } - - return mutableQueryStringComponents; -} diff --git a/Paystack/Classes/Helpers/NSDictionary+Paystack.h b/Paystack/Classes/Helpers/NSDictionary+Paystack.h deleted file mode 100644 index 7661e7a..0000000 --- a/Paystack/Classes/Helpers/NSDictionary+Paystack.h +++ /dev/null @@ -1,14 +0,0 @@ -// -// NSDictionary+Paystack.h -// Paystack -// - -#import - -@interface NSDictionary (Paystack) - -- (nullable NSDictionary *)pstck_dictionaryByRemovingNullsValidatingRequiredFields:(nonnull NSArray *)requiredFields; - -@end - -void linkDictionaryCategory(void); diff --git a/Paystack/Classes/Helpers/NSDictionary+Paystack.m b/Paystack/Classes/Helpers/NSDictionary+Paystack.m deleted file mode 100644 index bd407bd..0000000 --- a/Paystack/Classes/Helpers/NSDictionary+Paystack.m +++ /dev/null @@ -1,27 +0,0 @@ -// -// NSDictionary+Paystack.m -// Paystack -// - -#import "NSDictionary+Paystack.h" - -@implementation NSDictionary (Paystack) - -- (nullable NSDictionary *)pstck_dictionaryByRemovingNullsValidatingRequiredFields:(nonnull NSArray *)requiredFields { - NSMutableDictionary *dict = [NSMutableDictionary dictionary]; - [self enumerateKeysAndObjectsUsingBlock:^(id key, id obj, __unused BOOL *stop) { - if (obj != [NSNull null]) { - dict[key] = obj; - } - }]; - for (NSString *key in requiredFields) { - if (![[dict allKeys] containsObject:key]) { - return nil; - } - } - return [dict copy]; -} - -@end - -void linkDictionaryCategory(void){} diff --git a/Paystack/Classes/Helpers/PSTCKCategoryLoader.h b/Paystack/Classes/Helpers/PSTCKCategoryLoader.h deleted file mode 100644 index 701ccf6..0000000 --- a/Paystack/Classes/Helpers/PSTCKCategoryLoader.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// PSTCKCategoryLoader.h -// Paystack -// - -#ifdef PSTCK_STATIC_LIBRARY_BUILD - -#import - -@interface PSTCKCategoryLoader : NSObject - -+ (void)loadCategories; - -@end - -#endif diff --git a/Paystack/Classes/Helpers/PSTCKCategoryLoader.m b/Paystack/Classes/Helpers/PSTCKCategoryLoader.m deleted file mode 100644 index 2eb8376..0000000 --- a/Paystack/Classes/Helpers/PSTCKCategoryLoader.m +++ /dev/null @@ -1,21 +0,0 @@ -// -// PSTCKCategoryLoader.m -// Paystack -// - -#ifdef PSTCK_STATIC_LIBRARY_BUILD - -#import "PSTCKCategoryLoader.h" -#import "NSDictionary+Paystack.h" -#import "UIImage+Paystack.h" - -@implementation PSTCKCategoryLoader - -+ (void)loadCategories { - linkDictionaryCategory(); - linkUIImageCategory(); -} - -@end - -#endif diff --git a/Paystack/Classes/PublicHeaders/PSTCKAPIClient.h b/Paystack/Classes/PublicHeaders/PSTCKAPIClient.h deleted file mode 100644 index 6757eb3..0000000 --- a/Paystack/Classes/PublicHeaders/PSTCKAPIClient.h +++ /dev/null @@ -1,110 +0,0 @@ -// -// PSTCKAPIClient.h -// PaystackExample -// - -#import -#if TARGET_OS_IPHONE -#import -#endif - -static NSString *const __nonnull PSTCKSDKVersion = @"3.0.14"; -static NSString *const __nonnull PSTCKSDKBuild = @"18"; - -@class PSTCKCard, PSTCKCardParams, PSTCKTransactionParams, PSTCKToken, PSTCKState; - -/** - * A callback to be run with a token response from the Paystack API. - * - * @param reference The Transaction reference from the response. Will be nil if an error occurs. - * @param error The error returned from the response, or nil in one occurs. @see PaystackError.h for possible values. - */ -typedef void (^PSTCKErrorCompletionBlock)(NSError * __nonnull error, NSString * __nullable reference); -typedef void (^PSTCKTransactionCompletionBlock)(NSString * __nonnull reference); -typedef void (^PSTCKAddressVerficationBlock)(NSString * _Nonnull transaction, NSArray * _Nonnull states); -typedef void (^PSTCKNotifyCompletionBlock)(void); - -/** - A top-level class that imports the rest of the Paystack SDK. This class used to contain several methods to create Paystack tokens, but those are now deprecated in - favor of PSTCKAPIClient. - */ -@interface Paystack : NSObject - -/** - * Set your Paystack API key with this method. New instances of PSTCKAPIClient will be initialized with this value. You should call this method as early as - * possible in your application's lifecycle, preferably in your AppDelegate. - * - * @param publicKey Your public key, obtained from https://paystack.com/account/apikeys - * @warning Make sure not to ship your test API keys to the App Store! This will log a warning if you use your test key in a release build. - */ -+ (void)setDefaultPublicKey:(nonnull NSString *)publicKey; - -/// The current default public key. -+ (nullable NSString *)defaultPublicKey; -@end - -/// A client for making connections to the Paystack API. -@interface PSTCKAPIClient : NSObject - -/** - * A shared singleton API client. Its API key will be initially equal to [Paystack defaultPublicKey]. - */ -+ (nonnull instancetype)sharedClient; -- (nonnull instancetype)initWithPublicKey:(nonnull NSString *)publicKey NS_DESIGNATED_INITIALIZER; - -/** - * @see [Paystack setDefaultPublicKey:] - */ -@property (nonatomic, copy, nullable) NSString *publicKey; - -/** - * The operation queue on which to run completion blocks passed to the api client. Defaults to [NSOperationQueue mainQueue]. - */ -@property (nonatomic, nonnull) NSOperationQueue *operationQueue; - -@end - -#pragma mark Credit Cards - -@interface PSTCKAPIClient (CreditCards) - -/** - * Charges a PSTCKCardParams object using the Paystack API. - * - * @param card The user's card details. Cannot be nil. @see https://paystack.com/docs/api#create_card_token - */ -- (void) chargeCard:(nonnull PSTCKCardParams *)card - forTransaction:(nonnull PSTCKTransactionParams *)transaction - onViewController:(nonnull UIViewController *)viewController - didEndWithError:(nonnull PSTCKErrorCompletionBlock)errorCompletion - didRequestValidation:(nonnull PSTCKTransactionCompletionBlock)beforeValidateCompletion - didTransactionSuccess:(nonnull PSTCKTransactionCompletionBlock)successCompletion; - -/// Charges a card using the Paystack API -/// @param card The user's card details. Cannot be nil -/// @param transaction The transaction parameters -/// @param viewController The viewcontroller where the user entered their card details -/// @param errorCompletion This callback is called when there is an error -/// @param showingDialogCompletion Called before displaying the dialog modal -/// @param dialogDismissedCompletion Called when the dialog modal is dismissed -/// @param successCompletion The callback is called after a successful charge -- (void) chargeCard:(nonnull PSTCKCardParams *)card - forTransaction:(nonnull PSTCKTransactionParams *)transaction - onViewController:(nonnull UIViewController *)viewController - didEndWithError:(nonnull PSTCKErrorCompletionBlock)errorCompletion - didRequestValidation:(nonnull PSTCKTransactionCompletionBlock)beforeValidateCompletion - willPresentDialog:(nonnull PSTCKNotifyCompletionBlock)showingDialogCompletion - dismissedDialog:(nonnull PSTCKNotifyCompletionBlock)dialogDismissedCompletion - didTransactionSuccess:(nonnull PSTCKTransactionCompletionBlock)successCompletion; - -- (void) chargeCard:(nonnull PSTCKCardParams *)card - forTransaction:(nonnull PSTCKTransactionParams *)transaction - onViewController:(nonnull UIViewController *)viewController - didEndWithError:(nonnull PSTCKErrorCompletionBlock)errorCompletion - willPresentDialog:(nonnull PSTCKNotifyCompletionBlock)showingDialogCompletion - dismissedDialog:(nonnull PSTCKNotifyCompletionBlock)dialogDismissedCompletion - didTransactionSuccess:(nonnull PSTCKTransactionCompletionBlock)successCompletion; - -- (void) setProcessingStatus:(Boolean)status; - -@end diff --git a/Paystack/Classes/PublicHeaders/PSTCKAPIResponseDecodable.h b/Paystack/Classes/PublicHeaders/PSTCKAPIResponseDecodable.h deleted file mode 100644 index 4544dc2..0000000 --- a/Paystack/Classes/PublicHeaders/PSTCKAPIResponseDecodable.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// PSTCKAPIResponseDecodable.h -// Paystack -// - -#import - -@protocol PSTCKAPIResponseDecodable - -/** - * These fields are required to be present in the API response. If any of them are nil, decodedObjectFromAPIResponse should also return nil. - */ -+ (nonnull NSArray *)requiredFields; - -/** - * Parses an response from the Paystack API (in JSON format; represented as an NSDictionary) into an instance of the class. Returns nil if the object could not be decoded (i.e. if one of its `requiredFields` is nil). - */ -+ (nullable instancetype)decodedObjectFromAPIResponse:(nonnull NSDictionary *)response; - -/** - * The raw JSON response used to create the object. This can be useful for using beta features that haven't yet been made into properties in the SDK. - */ -@property(nonatomic, readonly, nonnull, copy)NSDictionary *allResponseFields; - -@end diff --git a/Paystack/Classes/PublicHeaders/PSTCKCard.h b/Paystack/Classes/PublicHeaders/PSTCKCard.h deleted file mode 100644 index 500b93f..0000000 --- a/Paystack/Classes/PublicHeaders/PSTCKCard.h +++ /dev/null @@ -1,122 +0,0 @@ -// -// PSTCKCard.h -// Paystack -// - -#import - -#import "PSTCKCardBrand.h" -#import "PSTCKCardParams.h" -#import "PSTCKAPIResponseDecodable.h" - -/** - * The various funding sources for a payment card. - */ -typedef NS_ENUM(NSInteger, PSTCKCardFundingType) { - PSTCKCardFundingTypeDebit, - PSTCKCardFundingTypeCredit, - PSTCKCardFundingTypePrepaid, - PSTCKCardFundingTypeOther, -}; - -/** - * Representation of a user's credit card details that have been tokenized with the Paystack API. @see https://paystack.com/docs/api#cards - */ -@interface PSTCKCard : PSTCKCardParams - -/** - * The card's number. This will be nil for cards retrieved from the Paystack API. - */ - - -/** - * The last 4 digits of the card. - */ -@property (nonatomic, readonly, nonnull) NSString *last4; - -/** - * For cards made with Apple Pay, this refers to the last 4 digits of the "Device Account Number" for the tokenized card. For regular cards, it will be nil. - */ -@property (nonatomic, readonly, nullable) NSString *dynamicLast4; - -/** - * The card's expiration month. - */ -@property (nonatomic) NSUInteger expMonth; - -/** - * The card's expiration year. - */ -@property (nonatomic) NSUInteger expYear; - -/** - * The cardholder's name. - */ -@property (nonatomic, copy, nullable) NSString *name; - -/** - * The cardholder's address. - */ -@property (nonatomic, copy, nullable) NSString *addressLine1; -@property (nonatomic, copy, nullable) NSString *addressLine2; -@property (nonatomic, copy, nullable) NSString *addressCity; -@property (nonatomic, copy, nullable) NSString *addressState; -@property (nonatomic, copy, nullable) NSString *addressZip; -@property (nonatomic, copy, nullable) NSString *addressCountry; - -/** - * The Paystack ID for the card. - */ -@property (nonatomic, readonly, nullable) NSString *cardId; - -/** - * The issuer of the card. - */ -@property (nonatomic, readonly) PSTCKCardBrand brand; - -/** - * The issuer of the card. - * Can be one of "Visa", "American Express", "MasterCard", "Discover", "JCB", "Diners Club", or "Unknown" - * @deprecated use "brand" instead. - */ -@property (nonatomic, readonly, nonnull) NSString *type __attribute__((deprecated)); - -/** - * The funding source for the card (credit, debit, prepaid, or other) - */ -@property (nonatomic, readonly) PSTCKCardFundingType funding; - -/** - * A proxy for the card's number, this uniquely identifies the credit card and can be used to compare different cards. - * @deprecated This field will no longer be present in responses when using your public key. If you want to access the value of this field, you can look it up on your backend using your secret key. - */ -@property (nonatomic, readonly, nullable) NSString *fingerprint __attribute__((deprecated("This field will no longer be present in responses when using your public key. If you want to access the value of this field, you can look it up on your backend using your secret key."))); - -/** - * Two-letter ISO code representing the issuing country of the card. - */ -@property (nonatomic, readonly, nullable) NSString *country; - -/** - * This is only applicable when tokenizing debit cards to issue payouts to managed accounts. You should not set it otherwise. The card can then be used as a transfer destination for funds in this currency. - */ -@property (nonatomic, copy, nullable) NSString *currency; - -#pragma mark - deprecated properties - -#define DEPRECATED_IN_FAVOR_OF_PSTCKCARDPARAMS __attribute__((deprecated("For collecting your users' credit card details, you should use an PSTCKCardParams object instead of an PSTCKCard."))) - -@property (nonatomic, copy, nullable) NSString *number DEPRECATED_IN_FAVOR_OF_PSTCKCARDPARAMS; -@property (nonatomic, copy, nullable) NSString *cvc DEPRECATED_IN_FAVOR_OF_PSTCKCARDPARAMS; -- (void)setExpMonth:(NSUInteger)expMonth DEPRECATED_IN_FAVOR_OF_PSTCKCARDPARAMS; -- (void)setExpYear:(NSUInteger)expYear DEPRECATED_IN_FAVOR_OF_PSTCKCARDPARAMS; -- (void)setName:(nullable NSString *)name DEPRECATED_IN_FAVOR_OF_PSTCKCARDPARAMS; -- (void)setAddressLine1:(nullable NSString *)addressLine1 DEPRECATED_IN_FAVOR_OF_PSTCKCARDPARAMS; -- (void)setAddressLine2:(nullable NSString *)addressLine2 DEPRECATED_IN_FAVOR_OF_PSTCKCARDPARAMS; -- (void)setAddressCity:(nullable NSString *)addressCity DEPRECATED_IN_FAVOR_OF_PSTCKCARDPARAMS; -- (void)setAddressState:(nullable NSString *)addressState DEPRECATED_IN_FAVOR_OF_PSTCKCARDPARAMS; -- (void)setAddressZip:(nullable NSString *)addressZip DEPRECATED_IN_FAVOR_OF_PSTCKCARDPARAMS; -- (void)setAddressCountry:(nullable NSString *)addressCountry DEPRECATED_IN_FAVOR_OF_PSTCKCARDPARAMS; - - -@end diff --git a/Paystack/Classes/PublicHeaders/PSTCKCardBrand.h b/Paystack/Classes/PublicHeaders/PSTCKCardBrand.h deleted file mode 100644 index 1f5beb5..0000000 --- a/Paystack/Classes/PublicHeaders/PSTCKCardBrand.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// PSTCKCardBrand.h -// Paystack -// - -#import - -/** - * The various card brands to which a payment card can belong. - */ -typedef NS_ENUM(NSInteger, PSTCKCardBrand) { - PSTCKCardBrandVisa, - PSTCKCardBrandAmex, - PSTCKCardBrandMasterCard, - PSTCKCardBrandDiscover, - PSTCKCardBrandJCB, - PSTCKCardBrandDinersClub, - PSTCKCardBrandUnknown, - PSTCKCardBrandVerve, -}; diff --git a/Paystack/Classes/PublicHeaders/PSTCKCardParams.h b/Paystack/Classes/PublicHeaders/PSTCKCardParams.h deleted file mode 100644 index 172d3b5..0000000 --- a/Paystack/Classes/PublicHeaders/PSTCKCardParams.h +++ /dev/null @@ -1,46 +0,0 @@ -// -// PSTCKCardParams.h -// Paystack -// - -#import -#import "PSTCKFormEncodable.h" -/** - * Representation of a user's credit card details. You can assemble these with information that your user enters and - * then create Paystack tokens with them using an PSTCKAPIClient. @see https://paystack.com/docs/api#cards - */ -@interface PSTCKCardParams : NSObject - -/** - * The card's number. - */ -@property (nonatomic, copy, nullable) NSString *number; - -/** - * The last 4 digits of the card's number, if it's been set, otherwise nil. - */ -- (nullable NSString *)last4; - -/** - * The clientdata to send to api. - */ -- (nullable NSString *)clientdata; - -/** - * The card's expiration month. - */ -@property (nonatomic) NSUInteger expMonth; - -/** - * The card's expiration year. - */ -@property (nonatomic) NSUInteger expYear; - -/** - * The card's security code, found on the back. - */ -@property (nonatomic, copy, nullable) NSString *cvc; - - - -@end diff --git a/Paystack/Classes/PublicHeaders/PSTCKCardValidationState.h b/Paystack/Classes/PublicHeaders/PSTCKCardValidationState.h deleted file mode 100644 index 01a10ab..0000000 --- a/Paystack/Classes/PublicHeaders/PSTCKCardValidationState.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// PSTCKCardValidationState.h -// Paystack -// - -#import - -/** - * These fields indicate whether a card field represents a valid value, invalid value, or incomplete value. - */ -typedef NS_ENUM(NSInteger, PSTCKCardValidationState) { - PSTCKCardValidationStateValid, // The field's contents are valid. For example, a valid, 16-digit card number. - PSTCKCardValidationStateInvalid, // The field's contents are invalid. For example, an expiration date of "13/42". - PSTCKCardValidationStateIncomplete, // The field's contents are not yet valid, but could be by typing additional characters. For example, a CVC of "1". -}; diff --git a/Paystack/Classes/PublicHeaders/PSTCKCardValidator.h b/Paystack/Classes/PublicHeaders/PSTCKCardValidator.h deleted file mode 100644 index fed8b9f..0000000 --- a/Paystack/Classes/PublicHeaders/PSTCKCardValidator.h +++ /dev/null @@ -1,103 +0,0 @@ -// -// PSTCKCardValidator.h -// Paystack -// - -#import - -#import "PSTCKCardParams.h" -#import "PSTCKCardBrand.h" -#import "PSTCKCardValidationState.h" - -/** - * This class contains static methods to validate card numbers, expiration dates, and CVCs. For a list of test card numbers to use with this code, see https://paystack.com/docs/testing - */ -@interface PSTCKCardValidator : NSObject - -/** - * Returns a copy of the passed string with all non-numeric characters removed. - */ -+ (nonnull NSString *)sanitizedNumericStringForString:(nonnull NSString *)string; - -/** - * Whether or not the target string contains only numeric characters. - */ -+ (BOOL)stringIsNumeric:(nonnull NSString *)string; - -/** - * Validates a card number, passed as a string. This will return PSTCKCardValidationStateInvalid for numbers that are too short or long, contain invalid characters, do not pass Luhn validation, or (optionally) do not match a number format issued by a major card brand. - * - * @param cardNumber The card number to validate. Ex. @"4242424242424242" - * @param validatingCardBrand Whether or not to enforce that the number appears to be issued by a major card brand (or could be). For example, no issuing card network currently issues card numbers beginning with the digit 9; if an otherwise correct-length and luhn-valid card number beginning with 9 (example: 9999999999999995) were passed to this method, it would return PSTCKCardValidationStateInvalid if this parameter were YES and PSTCKCardValidationStateValid if this parameter were NO. If unsure, you should use YES for this value. - * - * @return PSTCKCardValidationStateValid if the number is valid, PSTCKCardValidationStateInvalid if the number is invalid, or PSTCKCardValidationStateIncomplete if the number is a substring of a valid card (e.g. @"4242"). - */ -+ (PSTCKCardValidationState)validationStateForNumber:(nonnull NSString *)cardNumber validatingCardBrand:(BOOL)validatingCardBrand; - -/** - * The card brand for a card number or substring thereof. - * - * @param cardNumber A card number, or partial card number. For example, @"4242", @"5555555555554444", or @"123". - * - * @return The brand for that card number. The example parameters would return PSTCKCardBrandVisa, PSTCKCardBrandMasterCard, and PSTCKCardBrandUnknown, respectively. - */ -+ (PSTCKCardBrand)brandForNumber:(nonnull NSString *)cardNumber; - -/** - * The number length for cards associated with a card brand. For example, Visa card numbers contain 16 characters, while American Express cards contain 15 characters. - */ -+ (NSUInteger)lengthForCardBrand:(PSTCKCardBrand)brand; - -/** - * The length of the final grouping of digits to use when formatting a card number for display. For example, Visa cards display their final 4 numbers, e.g. "4242", while American Express cards display their final 5 digits, e.g. "10005". - */ -+ (NSInteger)fragmentLengthForCardBrand:(PSTCKCardBrand)brand; - -/** - * Validates an expiration month, passed as an (optionally 0-padded) string. Example valid values are "3", "12", and "08". Example invalid values are "99", "a", and "00". Incomplete values include "0" and "1". - * - * @param expirationMonth A string representing a 2-digit expiration month for a payment card. - * - * @return PSTCKCardValidationStateValid if the month is valid, PSTCKCardValidationStateInvalid if the month is invalid, or PSTCKCardValidationStateIncomplete if the month is a substring of a valid month (e.g. @"0" or @"1"). - */ -+ (PSTCKCardValidationState)validationStateForExpirationMonth:(nonnull NSString *)expirationMonth; - -/** - * Validates an expiration year, passed as a string representing the final 2 digits of the year. This considers the period between the current year until 2099 as valid times. An example valid value would be "16" (assuming the current year, as determined by [NSDate date], is 2015). Will return PSTCKCardValidationStateInvalid for a month/year combination that is earlier than the current date (i.e. @"15" and @"04" in October 2015. Example invalid values are "00", "a", and "13". Any 1-digit string will return PSTCKCardValidationStateIncomplete. - * - * @param expirationYear A string representing a 2-digit expiration year for a payment card. - * @param expirationMonth A string representing a 2-digit expiration month for a payment card. See -validationStateForExpirationMonth for the desired formatting of this string. - * - * @return PSTCKCardValidationStateValid if the year is valid, PSTCKCardValidationStateInvalid if the year is invalid, or PSTCKCardValidationStateIncomplete if the year is a substring of a valid year (e.g. @"1" or @"2"). - */ -+ (PSTCKCardValidationState)validationStateForExpirationYear:(nonnull NSString *)expirationYear inMonth:(nonnull NSString *)expirationMonth; - -/** - * The max CVC length for a card brand (for context, American Express CVCs are 4 digits, while all others are 3). - */ -+ (NSUInteger)maxCVCLengthForCardBrand:(PSTCKCardBrand)brand; - -/** - * Validates a card's CVC, passed as a numeric string, for the given card brand. - * - * @param cvc the CVC to validate - * @param brand the card brand (can be determined from the card's number using +brandForNumber) - * - * @return Whether the CVC represents a valid CVC for that card brand. For example, would return PSTCKCardValidationStateValid for @"123" and PSTCKCardBrandVisa, PSTCKCardValidationStateValid for @"1234" and PSTCKCardBrandAmericanExpress, PSTCKCardValidationStateIncomplete for @"12" and PSTCKCardBrandVisa, and PSTCKCardValidationStateInvalid for @"12345" and any brand. - */ -+ (PSTCKCardValidationState)validationStateForCVC:(nonnull NSString *)cvc cardBrand:(PSTCKCardBrand)brand; - -/** - * Validates the given card details. - * - * @param card the card details to validate. - * - * @return PSTCKCardValidationStateValid if all fields are valid, PSTCKCardValidationStateInvalid if any field is invalid, or PSTCKCardValidationStateIncomplete if all fields are either incomplete or valid. - */ -+ (PSTCKCardValidationState)validationStateForCard:(nonnull PSTCKCardParams *)card; - -// Exposed for testing only. -+ (PSTCKCardValidationState)validationStateForExpirationYear:(nonnull NSString *)expirationYear inMonth:(nonnull NSString *)expirationMonth inCurrentYear:(NSInteger)currentYear currentMonth:(NSInteger)currentMonth; -+ (PSTCKCardValidationState)validationStateForCard:(nonnull PSTCKCardParams *)card inCurrentYear:(NSInteger)currentYear currentMonth:(NSInteger)currentMonth; - -@end diff --git a/Paystack/Classes/PublicHeaders/PSTCKFormEncoder.h b/Paystack/Classes/PublicHeaders/PSTCKFormEncoder.h deleted file mode 100644 index 1410c89..0000000 --- a/Paystack/Classes/PublicHeaders/PSTCKFormEncoder.h +++ /dev/null @@ -1,37 +0,0 @@ -// -// PSTCKFormEncoder.h -// Paystack -// - -#import - -@class PSTCKCardParams; -@class PSTCKTransactionParams; -@protocol PSTCKFormEncodable; - -@interface PSTCKFormEncoder : NSObject - -+ (nonnull NSData *)formEncodedDataForObject:(nonnull NSObject *)object - usePublicKey:(nonnull NSString *)public_key - onThisDevice:(nonnull NSString *)device_id; - -+ (nonnull NSData *)formEncryptedDataForCard:(nonnull PSTCKCardParams *)card - andTransaction:(nonnull PSTCKTransactionParams *)transaction - usePublicKey:(nonnull NSString *)public_key - onThisDevice:(nonnull NSString *)device_id; - -+ (nonnull NSData *)formEncryptedDataForCard:(nonnull PSTCKCardParams *)card - andTransaction:(nonnull PSTCKTransactionParams *)transaction - andHandle:(nonnull NSString *)handle - usePublicKey:(nonnull NSString *)public_key - onThisDevice:(nonnull NSString *)device_id; - -+ (nonnull NSData *)formEncryptedDataForDict:(nonnull NSDictionary *)dict - usePublicKey:(nonnull NSString *)public_key - onThisDevice:(nonnull NSString *)device_id; - -+ (nonnull NSString *)stringByURLEncoding:(nonnull NSString *)string; - -+ (nonnull NSString *)stringByReplacingSnakeCaseWithCamelCase:(nonnull NSString *)input; - -@end diff --git a/Paystack/Classes/PublicHeaders/PSTCKPaymentCardTextField.h b/Paystack/Classes/PublicHeaders/PSTCKPaymentCardTextField.h deleted file mode 100644 index 4a8e991..0000000 --- a/Paystack/Classes/PublicHeaders/PSTCKPaymentCardTextField.h +++ /dev/null @@ -1,238 +0,0 @@ -// -// PSTCKPaymentCardTextField.h -// Paystack -// - -#import - -#import "PSTCKCard.h" - -@class PSTCKPaymentCardTextField; - -/** - * This protocol allows a delegate to be notified when a payment text field's contents change, which can in turn be used to take further actions depending on the validity of its contents. - */ -@protocol PSTCKPaymentCardTextFieldDelegate -@optional -/** - * Called when either the card number, expiration, or CVC changes. At this point, one can call -isValid on the text field to determine, for example, whether or not to enable a button to submit the form. Example: - - - (void)paymentCardTextFieldDidChange:(PSTCKPaymentCardTextField *)textField { - self.paymentButton.enabled = textField.isValid; - } - - * - * @param textField the text field that has changed - */ -- (void)paymentCardTextFieldDidChange:(nonnull PSTCKPaymentCardTextField *)textField; - -/** - * Called when editing begins in the payment card field's number field. - */ -- (void)paymentCardTextFieldDidBeginEditingNumber:(nonnull PSTCKPaymentCardTextField *)textField; - -/** - * Called when editing begins in the payment card field's CVC field. - */ -- (void)paymentCardTextFieldDidBeginEditingCVC:(nonnull PSTCKPaymentCardTextField *)textField; - -/** - * Called when editing begins in the payment card field's expiration field. - */ -- (void)paymentCardTextFieldDidBeginEditingExpiration:(nonnull PSTCKPaymentCardTextField *)textField; - -@end - - -/** - * PSTCKPaymentCardTextField is a text field with similar properties to UITextField, but specialized for collecting credit/debit card information. It manages multiple UITextFields under the hood to collect this information. It's designed to fit on a single line, and from a design perspective can be used anywhere a UITextField would be appropriate. - */ -@interface PSTCKPaymentCardTextField : UIControl - -/** - * @see PSTCKPaymentCardTextFieldDelegate - */ -@property(nonatomic, weak, nullable) IBOutlet id delegate; - -/** - * The font used in each child field. Default is [UIFont systemFontOfSize:18]. Set this property to nil to reset to the default. - */ -@property(nonatomic, copy, null_resettable) UIFont *font UI_APPEARANCE_SELECTOR; - -/** - * The text color to be used when entering valid text. Default is [UIColor blackColor]. Set this property to nil to reset to the default. - */ -@property(nonatomic, copy, null_resettable) UIColor *textColor UI_APPEARANCE_SELECTOR; - -/** - * The text color to be used when the user has entered invalid information, such as an invalid card number. Default is [UIColor redColor]. Set this property to nil to reset to the default. - */ -@property(nonatomic, copy, null_resettable) UIColor *textErrorColor UI_APPEARANCE_SELECTOR; - -/** - * The text placeholder color used in each child field. Default is [UIColor lightGreyColor]. Set this property to nil to reset to the default. On iOS 7 and above, this will also set the color of the card placeholder icon. - */ -@property(nonatomic, copy, null_resettable) UIColor *placeholderColor UI_APPEARANCE_SELECTOR; - -/** - * The placeholder for the card number field. Default is @"1234567812345678". If this is set to something that resembles a card number, it will automatically format it as such (in other words, you don't need to add spaces to this string). - */ -@property(nonatomic, copy, nullable) NSString *numberPlaceholder; - -/** - * The placeholder for the expiration field. Defaults to @"MM/YY". - */ -@property(nonatomic, copy, nullable) NSString *expirationPlaceholder; - -/** - * The placeholder for the cvc field. Defaults to @"CVC". - */ -@property(nonatomic, copy, nullable) NSString *cvcPlaceholder; - -/** - * The cursor color for the field. This is a proxy for the view's tintColor property, exposed for clarity only (in other words, calling setCursorColor is identical to calling setTintColor). - */ -@property(nonatomic, copy, null_resettable) UIColor *cursorColor UI_APPEARANCE_SELECTOR; - -/** - * The border color for the field. Default is [UIColor lightGreyColor]. Can be nil (in which case no border will be drawn). - */ -@property(nonatomic, copy, nullable) UIColor *borderColor UI_APPEARANCE_SELECTOR; - -/** - * The width of the field's border. Default is 1.0. - */ -@property(nonatomic, assign) CGFloat borderWidth UI_APPEARANCE_SELECTOR; - -/** - * The corner radius for the field's border. Default is 5.0. - */ -@property(nonatomic, assign) CGFloat cornerRadius UI_APPEARANCE_SELECTOR; - -/** - * The keyboard appearance for the field. Default is UIKeyboardAppearanceDefault. - */ -@property(nonatomic, assign) UIKeyboardAppearance keyboardAppearance UI_APPEARANCE_SELECTOR; - -/** - * This behaves identically to setting the inputAccessoryView for each child text field. - */ -@property(nonatomic, strong, nullable) UIView *inputAccessoryView; - -/** - * The curent brand image displayed in the receiver. - */ -@property (nonatomic, readonly, nullable) UIImage *brandImage; - -/** - * Causes the text field to begin editing. Presents the keyboard. - * - * @return Whether or not the text field successfully began editing. - * @see UIResponder - */ -- (BOOL)becomeFirstResponder; - -/** - * Causes the text field to stop editing. Dismisses the keyboard. - * - * @return Whether or not the field successfully stopped editing. - * @see UIResponder - */ -- (BOOL)resignFirstResponder; - -/** - * Resets all of the contents of all of the fields. If the field is currently being edited, the number field will become selected. - */ -- (void)clear; - -/** - * Returns the cvc image used for a card brand. - * Override this method in a subclass if you would like to provide custom images. - * @param cardBrand The brand of card entered. - * @return The cvc image used for a card brand. - */ -+ (nullable UIImage *)cvcImageForCardBrand:(PSTCKCardBrand)cardBrand; - -/** - * Returns the brand image used for a card brand. - * Override this method in a subclass if you would like to provide custom images. - * @param cardBrand The brand of card entered. - * @return The brand image used for a card brand. - */ -+ (nullable UIImage *)brandImageForCardBrand:(PSTCKCardBrand)cardBrand; - -/** - * Returns the rectangle in which the receiver draws its brand image. - * @param bounds The bounding rectangle of the receiver. - * @return the rectangle in which the receiver draws its brand image. - */ -- (CGRect)brandImageRectForBounds:(CGRect)bounds; - -/** - * Returns the rectangle in which the receiver draws the text fields. - * @param bounds The bounding rectangle of the receiver. - * @return The rectangle in which the receiver draws the text fields. - */ -- (CGRect)fieldsRectForBounds:(CGRect)bounds; - -/** - * Returns the rectangle in which the receiver draws its number field. - * @param bounds The bounding rectangle of the receiver. - * @return the rectangle in which the receiver draws its number field. - */ -- (CGRect)numberFieldRectForBounds:(CGRect)bounds; - -/** - * Returns the rectangle in which the receiver draws its cvc field. - * @param bounds The bounding rectangle of the receiver. - * @return the rectangle in which the receiver draws its cvc field. - */ -- (CGRect)cvcFieldRectForBounds:(CGRect)bounds; - -/** - * Returns the rectangle in which the receiver draws its expiration field. - * @param bounds The bounding rectangle of the receiver. - * @return the rectangle in which the receiver draws its expiration field. - */ -- (CGRect)expirationFieldRectForBounds:(CGRect)bounds; - -/** - * Whether or not the form currently contains a valid card number, expiration date, and CVC. - * @see PSTCKCardValidator - */ -@property(nonatomic, readonly, getter=isValid)BOOL valid; - -/** - * Enable/disable selecting or editing the field. Useful when submitting card details to Paystack. - */ -@property(nonatomic, getter=isEnabled) BOOL enabled; - -/** - * The current card number displayed by the field. May or may not be valid, unless isValid is true, in which case it is guaranteed to be valid. - */ -@property(nonatomic, readonly, nullable) NSString *cardNumber; - -/** - * The current expiration month displayed by the field (1 = January, etc). May or may not be valid, unless isValid is true, in which case it is guaranteed to be valid. - */ -@property(nonatomic, readonly) NSUInteger expirationMonth; - -/** - * The current expiration year displayed by the field, modulo 100 (e.g. the year 2015 will be represented as 15). May or may not be valid, unless isValid is true, in which case it is guaranteed to be valid. - */ -@property(nonatomic, readonly) NSUInteger expirationYear; - -/** - * The current card CVC displayed by the field. May or may not be valid, unless isValid is true, in which case it is guaranteed to be valid. - */ -@property(nonatomic, readonly, nullable) NSString *cvc; - -/** - * Convenience property for creating an PSTCKCardParams from the currently entered information - * or programmatically setting the field's contents. For example, if you're using another library - * to scan your user's credit card with a camera, you can assemble that data into an PSTCKCardParams - * object and set this property to that object to prefill the fields you've collected. - */ -@property(nonatomic, readwrite, nonnull) PSTCKCardParams *cardParams; - -@end diff --git a/Paystack/Classes/PublicHeaders/PSTCKToken.h b/Paystack/Classes/PublicHeaders/PSTCKToken.h deleted file mode 100644 index 567c9f4..0000000 --- a/Paystack/Classes/PublicHeaders/PSTCKToken.h +++ /dev/null @@ -1,33 +0,0 @@ -// -// PSTCKToken.h -// Paystack -// - -#import -#import "PSTCKAPIResponseDecodable.h" - - -/** - * A token returned from submitting payment details to the Paystack API. You should not have to instantiate one of these directly. - */ -@interface PSTCKToken : NSObject - -/** - * You cannot directly instantiate an PSTCKToken. You should only use one that has been returned from an PSTCKAPIClient callback. - */ -- (nonnull instancetype) init __attribute__((unavailable("You cannot directly instantiate an PSTCKToken. You should only use one that has been returned from an PSTCKAPIClient callback."))); - -/** - * The value of the token. You can store this value on your server and use it to make charges and customers. @see - * https://paystack.com/docs/mobile/ios#sending-tokens - */ -@property (nonatomic, readonly, nonnull) NSString *tokenId; -@property (nonatomic, readonly, nonnull) NSString *message; -@property (nonatomic, readonly, nonnull) NSString *last4; - - -/** - * When the token was created. - */ - -@end diff --git a/Paystack/Classes/PublicHeaders/PSTCKTransaction.h b/Paystack/Classes/PublicHeaders/PSTCKTransaction.h deleted file mode 100644 index 27f8928..0000000 --- a/Paystack/Classes/PublicHeaders/PSTCKTransaction.h +++ /dev/null @@ -1,31 +0,0 @@ -// -// PSTCKTransaction.h -// Paystack -// - -#import -#import "PSTCKAPIResponseDecodable.h" - - -/** - * A transaction returned from submitting payment details to the Paystack API. You should not have to instantiate one of these directly. - */ -@interface PSTCKTransaction : NSObject - -/** - * You cannot directly instantiate an PSTCKTransaction. You should only use one that has been returned from an PSTCKAPIClient callback. - */ -- (nonnull instancetype) init __attribute__((unavailable("You cannot directly instantiate an PSTCKTransaction. You should only use one that has been returned from an PSTCKAPIClient callback."))); - -@property (nonatomic, readonly, nonnull) NSString *reference; -@property (nonatomic, readonly, nonnull) NSString *message; -@property (nonatomic, readonly, nonnull) NSString *status; -@property (nonatomic, readonly, nonnull) NSString *trans; -@property (nonatomic, readonly, nonnull) NSString *redirecturl; -@property (nonatomic, readonly, nonnull) NSString *auth; -@property (nonatomic, readonly, nonnull) NSString *otpmessage; -@property (nonatomic, readonly, nonnull) NSString *countrycode; -@property (nonatomic, readonly, nonnull) NSString *errors; - - -@end diff --git a/Paystack/Classes/PublicHeaders/PSTCKTransactionParams.h b/Paystack/Classes/PublicHeaders/PSTCKTransactionParams.h deleted file mode 100644 index 85b31db..0000000 --- a/Paystack/Classes/PublicHeaders/PSTCKTransactionParams.h +++ /dev/null @@ -1,40 +0,0 @@ -// -// PSTCKTransactionParams.h -// Paystack -// - -#import -#import "PSTCKFormEncodable.h" -/** - * Representation of the transaction to perform on a card - */ -@interface PSTCKTransactionParams : NSObject - -@property (nonatomic, copy, nonnull) NSString *access_code; - -@property (nonatomic, copy, nonnull) NSString *email; -@property (nonatomic) NSUInteger amount; -@property (nonatomic, copy, nullable) NSString *reference; -@property (nonatomic, copy, nullable) NSString *subaccount; -@property (nonatomic) NSInteger transaction_charge; -@property (nonatomic, copy, nullable) NSString *bearer; -@property (nonatomic, readonly, nullable) NSString *metadata; -@property (nonatomic, nullable) NSString *plan; -@property (nonatomic, nullable) NSString *currency; - -- (nullable PSTCKTransactionParams *) setMetadataValue:(nonnull NSString*)value - forKey:(nonnull NSString*)key - error:(NSError * _Nullable __autoreleasing * _Nonnull) error; - -- (nullable PSTCKTransactionParams *) setMetadataValueDict:(nonnull NSMutableDictionary*)dict - forKey:(nonnull NSString*)key - error:(NSError * _Nullable __autoreleasing * _Nonnull) error; - -- (nullable PSTCKTransactionParams *) setMetadataValueArray:(nonnull NSMutableArray*)arr - forKey:(nonnull NSString*)key - error:(NSError * _Nullable __autoreleasing * _Nonnull) error; - -- (nullable PSTCKTransactionParams *) setCustomFieldValue:(nonnull NSString*)value - displayedAs:(nonnull NSString*)display_name - error:(NSError * _Nullable __autoreleasing * _Nonnull) error; -@end diff --git a/Paystack/Classes/PublicHeaders/PSTCKValidationParams.h b/Paystack/Classes/PublicHeaders/PSTCKValidationParams.h deleted file mode 100644 index 0e6994f..0000000 --- a/Paystack/Classes/PublicHeaders/PSTCKValidationParams.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// PSTCKValidationParams.h -// Paystack -// - -#import -#import "PSTCKFormEncodable.h" -/** - * Representation of a user's credit card details. You can assemble these with information that your user enters and - * then create Paystack tokens with them using an PSTCKAPIClient. @see https://paystack.com/docs/api#cards - */ -@interface PSTCKValidationParams : NSObject - -@property (nonatomic, copy, nonnull) NSString *trans; -@property (nonatomic, copy, nonnull) NSString *token; - -@end diff --git a/Paystack/Classes/PublicHeaders/Paystack.h b/Paystack/Classes/PublicHeaders/Paystack.h deleted file mode 100644 index 40b2217..0000000 --- a/Paystack/Classes/PublicHeaders/Paystack.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// Paystack.h -// Paystack -// -// Created by Ibrahim Lawal on 02/02/16. -// Copyright (c) 2016 Paystack. All rights reserved. -// -// The code in this workspace was adapted from https://github.com/stripe/stripe-ios. -// Stripe was replaced with Paystack - and STP with PSTCK - to avoid collisions within -// apps that are using both Paystack and Stripe. - -#import "PSTCKAPIClient.h" -#import "PaystackError.h" -#import "PSTCKCardBrand.h" -#import "PSTCKCardParams.h" -#import "PSTCKTransactionParams.h" -#import "PSTCKCard.h" -#import "PSTCKCardValidationState.h" -#import "PSTCKCardValidator.h" -#import "PSTCKToken.h" -#import "PSTCKRSA.h" - -#if TARGET_OS_IPHONE -#import "PSTCKPaymentCardTextField.h" -#endif diff --git a/Paystack/Classes/PublicHeaders/PaystackError.h b/Paystack/Classes/PublicHeaders/PaystackError.h deleted file mode 100644 index e9ab693..0000000 --- a/Paystack/Classes/PublicHeaders/PaystackError.h +++ /dev/null @@ -1,75 +0,0 @@ -// -// PaystackError.h -// Paystack -// - -#import - -/** - * All Paystack iOS errors will be under this domain. - */ -FOUNDATION_EXPORT NSString * __nonnull const PaystackDomain; - -typedef NS_ENUM(NSInteger, PSTCKErrorCode) { - PSTCKConnectionError = 40, // Trouble connecting to Paystack. - PSTCKInvalidRequestError = 50, // Your request had invalid parameters. - PSTCKAPIError = 60, // General-purpose API error (should be rare). - PSTCKCardError = 70, // Something was wrong with the given card (most common). - PSTCKCardErrorProcessingError = 80, // Paystack Checkout encountered an error. - PSTCKTransactionError = 90, // Something was wrong with the given transaction details. - PSTCKConflictError = 100, // A transaction was started while SDK was processing another - PSTCKExpiredAccessCodeError = 110, // The access code is not usable -}; - -#pragma mark userInfo keys - -// A developer-friendly error message that explains what went wrong. You probably -// shouldn't show this to your users, but might want to use it yourself. -FOUNDATION_EXPORT NSString * __nonnull const PSTCKErrorMessageKey; - -// What went wrong with your PSTCKCard (e.g., PSTCKInvalidCVC. See below for full list). -FOUNDATION_EXPORT NSString * __nonnull const PSTCKCardErrorCodeKey; - -// Which parameter on the PSTCKCard had an error (e.g., "cvc"). Useful for marking up the -// right UI element. -FOUNDATION_EXPORT NSString * __nonnull const PSTCKErrorParameterKey; - -#pragma mark PSTCKCardErrorCodeKeys - -// (Usually determined locally:) -FOUNDATION_EXPORT NSString * __nonnull const PSTCKInvalidNumber; -FOUNDATION_EXPORT NSString * __nonnull const PSTCKInvalidExpMonth; -FOUNDATION_EXPORT NSString * __nonnull const PSTCKInvalidExpYear; -FOUNDATION_EXPORT NSString * __nonnull const PSTCKInvalidCVC; - -// (Usually sent from the server:) -FOUNDATION_EXPORT NSString * __nonnull const PSTCKIncorrectNumber; -FOUNDATION_EXPORT NSString * __nonnull const PSTCKExpiredCard; -FOUNDATION_EXPORT NSString * __nonnull const PSTCKCardDeclined; -FOUNDATION_EXPORT NSString * __nonnull const PSTCKProcessingError; -FOUNDATION_EXPORT NSString * __nonnull const PSTCKIncorrectCVC; - -#pragma mark Strings - -#define PSTCKExpiredAccessCodeErrorMessage NSLocalizedString(@"There was a problem completing your request", @"Error when access code has no valid transaction") -#define PSTCKCardErrorInvalidNumberUserMessage NSLocalizedString(@"Your card's number is invalid", @"Error when the card number is not valid") -#define PSTCKCardErrorProcessingTransactionMessage NSLocalizedString(@"Please wait", @"Error when chargeCard is called while the SDK is still processing a transaction") -#define PSTCKCardErrorInvalidCVCUserMessage NSLocalizedString(@"Your card's security code is invalid", @"Error when the card's CVC is not valid") -#define PSTCKCardErrorInvalidExpMonthUserMessage \ - NSLocalizedString(@"Your card's expiration month is invalid", @"Error when the card's expiration month is not valid") -#define PSTCKCardErrorInvalidExpYearUserMessage \ -NSLocalizedString(@"Your card's expiration year is invalid", @"Error when the card's expiration year is not valid") -#define PSTCKTransactionErrorDontSetCustomFieldDirectlyMessage \ - NSLocalizedString(@"Please use the setCustomField function", @"Error when the app attempts to set the custom_fields key directly") -#define PSTCKCardErrorExpiredCardUserMessage NSLocalizedString(@"Your card has expired", @"Error when the card has already expired") -#define PSTCKCardErrorDeclinedUserMessage NSLocalizedString(@"Your card was declined", @"Error when the card was declined by the credit card networks") -#define PSTCKUnexpectedError \ - NSLocalizedString(@"There was an unexpected error -- try again in a few seconds", @"Unexpected error, such as a 500 from Paystack or a JSON parse error") -#define PSTCKCardErrorProcessingErrorUserMessage \ - NSLocalizedString(@"There was an error processing your card -- try again in a few seconds", @"Error when there is a problem processing the credit card") - -@interface NSError(Paystack) - -+ (nullable NSError *)pstck_errorFromPaystackResponse:(nullable NSDictionary *)jsonDictionary; - -@end diff --git a/Paystack/Classes/PublicHeaders/UIImage+Paystack.h b/Paystack/Classes/PublicHeaders/UIImage+Paystack.h deleted file mode 100644 index 1f00a7d..0000000 --- a/Paystack/Classes/PublicHeaders/UIImage+Paystack.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// UIImage+Paystack.h -// Paystack -// - -#import -#import "PSTCKCardBrand.h" - -@interface UIImage (Paystack) - -+ (nonnull UIImage *)pstck_amexCardImage; -+ (nonnull UIImage *)pstck_dinersClubCardImage; -+ (nonnull UIImage *)pstck_discoverCardImage; -+ (nonnull UIImage *)pstck_jcbCardImage; -+ (nonnull UIImage *)pstck_masterCardCardImage; -+ (nonnull UIImage *)pstck_visaCardImage; -+ (nonnull UIImage *)pstck_unknownCardCardImage; - -+ (nullable UIImage *)pstck_brandImageForCardBrand:(PSTCKCardBrand)brand; -+ (nullable UIImage *)pstck_cvcImageForCardBrand:(PSTCKCardBrand)brand; -+ (nullable UIImage *)pstck_safeImageNamed:(nonnull NSString *)imageName; - -@end - -void linkUIImageCategory(void); diff --git a/Paystack/Classes/RSA/PSTCKRSA.h b/Paystack/Classes/RSA/PSTCKRSA.h deleted file mode 100644 index cc146a7..0000000 --- a/Paystack/Classes/RSA/PSTCKRSA.h +++ /dev/null @@ -1,14 +0,0 @@ -// -// PSTCKRSA.h -// Paystack -// -// Created by Ibrahim Lawal on Feb/27/2016. -// Copyright © 2016 Paystack, Inc. All rights reserved. -// -#import - - -@interface PSTCKRSA : NSObject -+ (nullable NSString *)encryptRSA:(nonnull NSString *)plainTextString; -@end - diff --git a/Paystack/Classes/RSA/PSTCKRSA.m b/Paystack/Classes/RSA/PSTCKRSA.m deleted file mode 100644 index 0d8b779..0000000 --- a/Paystack/Classes/RSA/PSTCKRSA.m +++ /dev/null @@ -1,164 +0,0 @@ -// -// PSTCKRSA.m -// Paystack -// -// Created by Ibrahim Lawal on Feb/27/2016. -// Copyright © 2016 Paystack, Inc. All rights reserved. -// - -#import "PSTCKRSA.h" - -extern OSStatus SecKeyEncrypt( - SecKeyRef key, - SecPadding padding, - const uint8_t *plainText, - size_t plainTextLen, - uint8_t *cipherText, - size_t *cipherTextLen) -__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_2_0) __attribute__((weak_import)); - -@implementation PSTCKRSA - -+ (NSData *)stripPublicKeyHeader:(NSData *)d_key -{ - // Skip ASN.1 public key header - if (d_key == nil) return(nil); - - unsigned long len = [d_key length]; - if (!len) return(nil); - - unsigned char *c_key = (unsigned char *)[d_key bytes]; - unsigned int idx = 0; - - if (c_key[idx++] != 0x30) return(nil); - - if (c_key[idx] > 0x80) idx += c_key[idx] - 0x80 + 1; - else idx++; - - // PKCS #1 rsaEncryption szOID_RSA_RSA - static unsigned char seqiod[] = - { 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, - 0x01, 0x05, 0x00 }; - if (memcmp(&c_key[idx], seqiod, 15)) return(nil); - - idx += 15; - - if (c_key[idx++] != 0x03) return(nil); - - if (c_key[idx] > 0x80) idx += c_key[idx] - 0x80 + 1; - else idx++; - - if (c_key[idx++] != '\0') return(nil); - - // Now make a new NSData from this buffer - return([NSData dataWithBytes:&c_key[idx] length:len - idx]); -} - - -+(NSString *)publicEncryptionKey{ - return @"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANIsL+RHqfkBiKGn/D1y1QnNrMkKzxWP" - "2wkeSokw2OJrCI+d6YGJPrHHx+nmb/Qn885/R01Gw6d7M824qofmCvkCAwEAAQ=="; -} - -+(NSData *)decodedPublicEncryptionKey{ - NSString *s_key = [self publicEncryptionKey]; - - // This will be base64 encoded, decode it. - NSData *d_key = [[NSData alloc] initWithBase64EncodedString:s_key options:0]; - - return [self stripPublicKeyHeader:d_key]; -} - -+(void)throwNotEntitledException{ - NSException *ex = [NSException exceptionWithName:@"Not entitled to Keychain Sharing" reason:NSLocalizedString(@"To use the Paystack SDK, you must add Keychain Sharing entitlements to your app", @"To use the Paystack SDK, you must add Keychain Sharing entitlements to your app") userInfo:nil]; - [ex raise]; -} - - -+(NSString *)encryptRSA:(NSString *)plainTextString { - NSData *d_key = [self decodedPublicEncryptionKey]; - - // Delete any old lingering key with the same tag - NSMutableDictionary *publicKey = [[NSMutableDictionary alloc] init]; - [publicKey setObject:(id) kSecClassKey forKey:(id)kSecClass]; - [publicKey setObject:(id) kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType]; - // [publicKey setObject:d_tag forKey:(id)kSecAttrApplicationTag]; - SecItemDelete((CFDictionaryRef)publicKey); - - CFTypeRef persistKey = nil; - - // Add persistent version of the key to system keychain - [publicKey setObject:d_key forKey:(id)kSecValueData]; - [publicKey setObject:(id) kSecAttrKeyClassPublic forKey:(id) - kSecAttrKeyClass]; - [publicKey setObject:[NSNumber numberWithBool:YES] forKey:(id) - kSecReturnPersistentRef]; - - OSStatus secStatus = SecItemAdd((CFDictionaryRef)publicKey, &persistKey); - if (persistKey != nil) CFRelease(persistKey); - - if (secStatus != noErr) { -// NSLog(@"THTHTHTH: %d", (int)secStatus); - if(secStatus == -34018) { -// NSLog(@"THTHTHTH: Not entitled"); - [self throwNotEntitledException]; - } - if(secStatus == errSecDuplicateItem) { - // we don't mind if there was a duplication - return(FALSE); - } - } - - // Now fetch the SecKeyRef version of the key - SecKeyRef keyRef = nil; - - [publicKey removeObjectForKey:(id)kSecValueData]; - [publicKey removeObjectForKey:(id)kSecReturnPersistentRef]; - [publicKey setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnRef - ]; - [publicKey setObject:(id) kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType]; - secStatus = SecItemCopyMatching((CFDictionaryRef)publicKey, - (CFTypeRef *)&keyRef); - - if (secStatus != noErr) { -// NSLog(@"THTHTHTH: %d", (int)secStatus); - if(secStatus == -34018) { -// NSLog(@"THTHTHTH: Not entitled"); - [self throwNotEntitledException]; - } - if(secStatus == errSecDuplicateItem) { - // we don't mind if there was a duplication - return(FALSE); - } - } - // Fetch the SecKeyRef version of our public key - // SecKeyRef keyRef = [self fetchKeyRef:publicKey andAddIfNotFound:true]; - - if (keyRef == nil){ -// NSLog(@"THTHTHTH: No key"); - return nil; - } - - // Add to our pseudo keychain - // [keyRefs addObject:[NSValue valueWithBytes:&keyRef objCType:@encode( - // SecKeyRef)]]; - // - - size_t cipherBufferSize = SecKeyGetBlockSize(keyRef); - uint8_t *cipherBuffer = malloc(cipherBufferSize); - uint8_t *nonce = (uint8_t *)[plainTextString UTF8String]; - - SecKeyEncrypt(keyRef, - kSecPaddingPKCS1, - nonce, - strlen( (char*)nonce ), - &cipherBuffer[0], - &cipherBufferSize); -// NSLog(@"THTHTHTH: After s_k_e_"); - NSData *encryptedData = [NSData dataWithBytes:cipherBuffer length:cipherBufferSize]; -// NSLog(@"THTHTHTH: %@", [encryptedData base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)0]); - - return [encryptedData base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)0]; -} - -@end diff --git a/Paystack/Classes/Token/PSTCKToken.m b/Paystack/Classes/Token/PSTCKToken.m deleted file mode 100644 index 4cb554e..0000000 --- a/Paystack/Classes/Token/PSTCKToken.m +++ /dev/null @@ -1,95 +0,0 @@ -// -// PSTCKToken.m -// Paystack -// - -#import "PSTCKToken.h" -#import "PSTCKCard.h" -#import "NSDictionary+Paystack.h" - -@interface PSTCKToken() -@property (nonatomic, nonnull) NSString *tokenId; -@property (nonatomic) NSString *last4; -@property (nonatomic) NSString *message; -//@property (nonatomic) BOOL livemode; -@property (nonatomic, nullable) PSTCKCard *card; -@property (nonatomic, nullable) NSDate *created; -@property (nonatomic, readwrite, nonnull, copy) NSDictionary *allResponseFields; -@end - -@implementation PSTCKToken - -- (NSString *)description { - return self.tokenId ?: @"Unknown token"; -} - -- (NSString *)debugDescription { - NSString *token = self.tokenId ?: @"Unknown token"; - NSString *last4 = self.last4 ?: @"Unknown last 4"; - NSString *message = self.message ?: @"Unknown Message"; - return [NSString stringWithFormat:@"%@ (%@)ending with %@", token, message, last4]; -} - -- (BOOL)isEqual:(id)object { - return [self isEqualToToken:object]; -} - -- (NSUInteger)hash { - return [self.tokenId hash]; -} - -- (BOOL)isEqualToToken:(PSTCKToken *)object { - if (self == object) { - return YES; - } - - if (!object || ![object isKindOfClass:self.class]) { - return NO; - } - - if ((self.card || object.card) && (![self.card isEqual:object.card])) { - return NO; - } - - // return self.livemode == object.livemode && [self.tokenId isEqualToString:object.tokenId] && [self.created isEqualToDate:object.created] && - // [self.card isEqual:object.card] && [self.tokenId isEqualToString:object.tokenId] && [self.created isEqualToDate:object.created]; - return [self.tokenId isEqualToString:object.tokenId] &&[self.message isEqualToString:object.message] && - [self.last4 isEqualToString:object.last4] ; - -} - -#pragma mark PSTCKAPIResponseDecodable - -+ (NSArray *)requiredFields { - //return @[@"id", @"livemode", @"created"]; - return @[@"status", @"message"]; -} - -+ (instancetype)decodedObjectFromAPIResponse:(NSDictionary *)response { - NSDictionary *dict = [response pstck_dictionaryByRemovingNullsValidatingRequiredFields:[self requiredFields]]; - if (!dict) { - return nil; - } - - // only status 0 is an error - if ([[dict[@"status"] description] isEqual: @"0"]) { - return nil; - } - - PSTCKToken *token = [self new]; - token.tokenId = dict[@"token"]; - token.message = dict[@"message"]; - token.last4 = dict[@"last4"]; -// token.livemode = [dict[@"livemode"] boolValue]; -// token.created = [NSDate dateWithTimeIntervalSince1970:[dict[@"created"] doubleValue]]; - -// NSDictionary *cardDictionary = dict[@"card"]; -// if (cardDictionary) { -// token.card = [PSTCKCard decodedObjectFromAPIResponse:cardDictionary]; -// } - - token.allResponseFields = dict; - return token; -} - -@end diff --git a/Paystack/Classes/Transaction/PSTCKTransaction.m b/Paystack/Classes/Transaction/PSTCKTransaction.m deleted file mode 100644 index d1d508c..0000000 --- a/Paystack/Classes/Transaction/PSTCKTransaction.m +++ /dev/null @@ -1,93 +0,0 @@ -// -// PSTCKTransaction.m -// Paystack -// - -#import "PSTCKTransaction.h" -#import "PSTCKCard.h" -#import "NSDictionary+Paystack.h" - -@interface PSTCKTransaction() -@property (nonatomic) NSString *reference; -@property (nonatomic) NSString *message; -@property (nonatomic) NSString *trans; -@property (nonatomic) NSString *redirecturl; -@property (nonatomic) NSString *status; -@property (nonatomic) NSString *auth; -@property (nonatomic) NSString *otpmessage; -@property (nonatomic) NSString *errors; -@property (nonatomic) NSString *countrycode; -@property (nonatomic, readwrite, nonnull, copy) NSDictionary *allResponseFields; -@end - -@implementation PSTCKTransaction - -- (instancetype)init -{ - self = [super init]; - if (self) { - - } - return self; -} - -- (NSString *)description { - return self.reference ?: self.message ?: @"Unknown reference"; -} - -- (NSString *)debugDescription { - NSString *reference = self.reference ?: @"Unknown Reference"; - NSString *message = self.message ?: @"Unknown Message"; - return [NSString stringWithFormat:@"%@ (%@)", reference, message]; -} - -- (BOOL)isEqual:(id)object { - return [self isEqualToTransaction:object]; -} - -- (NSUInteger)hash { - return [self.reference hash]; -} - -- (BOOL)isEqualToTransaction:(PSTCKTransaction *)object { - if (self == object) { - return YES; - } - - if (!object || ![object isKindOfClass:self.class]) { - return NO; - } - - return [self.reference isEqualToString:object.reference] &&[self.message isEqualToString:object.message] && - [self.trans isEqualToString:object.trans] ; - -} - -#pragma mark PSTCKAPIResponseDecodable - -+ (NSArray *)requiredFields { - //return @[@"id", @"livemode", @"created"]; - return @[@"message"]; -} - -+ (instancetype)decodedObjectFromAPIResponse:(NSDictionary *)response { - NSDictionary *dict = [response pstck_dictionaryByRemovingNullsValidatingRequiredFields:[self requiredFields]]; - if (!dict) { - return nil; - } - - PSTCKTransaction *transaction = [self new]; - transaction.reference = dict[@"reference"]; - transaction.trans = dict[@"trans"]; - transaction.auth = dict[@"auth"]; - transaction.otpmessage = dict[@"otpmessage"]; - transaction.redirecturl = dict[@"redirecturl"]; - transaction.message = dict[@"message"]; - transaction.status = dict[@"status"]; - transaction.errors = dict[@"errors"]; - transaction.countrycode = dict[@"countryCode"]; - transaction.allResponseFields = dict; - return transaction; -} - -@end diff --git a/Paystack/Classes/Transaction/PSTCKTransactionParams.m b/Paystack/Classes/Transaction/PSTCKTransactionParams.m deleted file mode 100644 index 1a375d9..0000000 --- a/Paystack/Classes/Transaction/PSTCKTransactionParams.m +++ /dev/null @@ -1,137 +0,0 @@ -// -// PSTCKCardParams.m -// Paystack -// - -#import "PSTCKTransactionParams.h" -#import "PSTCKCardValidator.h" -#import "PaystackError.h" -#import "PSTCKRSA.h" - -@interface PSTCKTransactionParams (Private) -@property (nonatomic, retain) NSMutableDictionary* metadataDict; -@property (nonatomic, retain) NSMutableArray* customfieldArray; -@end - -@implementation PSTCKTransactionParams{ - NSMutableDictionary* _metadataDict; - NSMutableArray* _customfieldArray; -} - -@synthesize additionalAPIParameters = _additionalAPIParameters; - - - -- (instancetype)init { - self = [super init]; - if (self) { - _additionalAPIParameters = @{}; - _transaction_charge = -1; - _amount = -1; - _metadataDict = [[NSMutableDictionary alloc] init]; - _customfieldArray = [[NSMutableArray alloc] init]; - } - return self; -} - -- (PSTCKTransactionParams *) setMetadataValue:(NSString*)value - forKey:(NSString*)key - error:(NSError**) error { - if([@"custom_fields" isEqualToString:key]){ - *error = [[NSError alloc] initWithDomain:PaystackDomain - code:PSTCKTransactionError - userInfo:@{ - NSLocalizedDescriptionKey: PSTCKTransactionErrorDontSetCustomFieldDirectlyMessage, - PSTCKErrorMessageKey: PSTCKTransactionErrorDontSetCustomFieldDirectlyMessage - }]; - return nil; - } - return [self _setMetadataValue:value forKey:key error:error]; -} - -- (PSTCKTransactionParams *) setMetadataValueDict:(NSMutableDictionary*)dict - forKey:(NSString*)key - error:(NSError**) error { - if([@"custom_fields" isEqualToString:key]){ - *error = [[NSError alloc] initWithDomain:PaystackDomain - code:PSTCKTransactionError - userInfo:@{ - NSLocalizedDescriptionKey: PSTCKTransactionErrorDontSetCustomFieldDirectlyMessage, - PSTCKErrorMessageKey: PSTCKTransactionErrorDontSetCustomFieldDirectlyMessage - }]; - return nil; - } - return [self _setMetadataValue:dict forKey:key error:error]; -} - -- (PSTCKTransactionParams *) setMetadataValueArray:(NSMutableArray*)arr - forKey:(NSString*)key - error:(NSError**) error { - if([@"custom_fields" isEqualToString:key]){ - *error = [[NSError alloc] initWithDomain:PaystackDomain - code:PSTCKTransactionError - userInfo:@{ - NSLocalizedDescriptionKey: PSTCKTransactionErrorDontSetCustomFieldDirectlyMessage, - PSTCKErrorMessageKey: PSTCKTransactionErrorDontSetCustomFieldDirectlyMessage - }]; - return nil; - } - return [self _setMetadataValue:arr forKey:key error:error]; -} - -- (PSTCKTransactionParams *) _setMetadataValue:(NSObject*) value - forKey:(NSString*)key - error:(NSError**) error { - [_metadataDict setValue:value forKey:key]; - _metadata = [[NSString alloc] initWithData:[NSJSONSerialization - dataWithJSONObject:_metadataDict - options:0 - error:error ] - encoding:NSUTF8StringEncoding]; - return self; -} - -- (PSTCKTransactionParams *) setCustomFieldValue:(NSString*)value - displayedAs:(NSString*)display_name - error:(NSError**) error{ - // generate a variable name - NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"[^A-Za-z0-9]" options:0 error:error]; - NSString *variable_name = [display_name lowercaseString]; - variable_name = [regex stringByReplacingMatchesInString:variable_name options:0 range:NSMakeRange(0, [variable_name length]) withTemplate:@"_"]; - - // only continue if no error - if(*error!=nil) - return nil; - [_customfieldArray addObject:@{ - @"value": value, - @"display_name": display_name, - @"variable_name": variable_name, - }]; - return [self _setMetadataValue:_customfieldArray forKey:@"custom_fields" error:error]; -} - - -#pragma mark - - -#pragma mark - PSTCKFormEncodable - -+ (NSString *)rootObjectName { - return @""; -} - -+ (NSDictionary *)propertyNamesToFormFieldNamesMapping { - return @{ - @"access_code": @"access_code", - @"email": @"email", - @"amount": @"amount", - @"reference": @"reference", - @"subaccount": @"subaccount", - @"transaction_charge": @"transaction_charge", - @"bearer": @"bearer", - @"metadata": @"metadata", - @"plan": @"plan", - @"currency": @"currency", - }; -} - -@end diff --git a/Paystack/Classes/UI/AddressViewController.xib b/Paystack/Classes/UI/AddressViewController.xib deleted file mode 100644 index 97c004d..0000000 --- a/Paystack/Classes/UI/AddressViewController.xib +++ /dev/null @@ -1,164 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Paystack/Classes/UI/ButtonExtension.swift b/Paystack/Classes/UI/ButtonExtension.swift deleted file mode 100644 index 4e24b8d..0000000 --- a/Paystack/Classes/UI/ButtonExtension.swift +++ /dev/null @@ -1,92 +0,0 @@ -// -// ButtonExtension.swift -// PaystackiOS -// -// Created by Jubril Olambiwonnu on 7/9/20. -// Copyright © 2020 Paystack, Inc. All rights reserved. -// - -import Foundation -import UIKit -import ObjectiveC -// Declare a global var to produce a unique address as the assoc object handle -var disabledColorHandle: UInt8 = 0 -var highlightedColorHandle: UInt8 = 0 -var selectedColorHandle: UInt8 = 0 - -extension UIButton { - private func image(withColor color: UIColor) -> UIImage? { - let rect = CGRect(x: 0.0, y: 0.0, width: 1.0, height: 1.0) - UIGraphicsBeginImageContext(rect.size) - let context = UIGraphicsGetCurrentContext() - - context?.setFillColor(color.cgColor) - context?.fill(rect) - - let image = UIGraphicsGetImageFromCurrentImageContext() - UIGraphicsEndImageContext() - - return image - } - - func setBackgroundColor(_ color: UIColor, for state: UIControl.State) { - self.setBackgroundImage(image(withColor: color), for: state) - } - - @IBInspectable - var disabledColor: UIColor? { - get { - if let color = objc_getAssociatedObject(self, &disabledColorHandle) as? UIColor { - return color - } - return nil - } - set { - if let color = newValue { - self.setBackgroundColor(color, for: .disabled) - objc_setAssociatedObject(self, &disabledColorHandle, color, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - } else { - self.setBackgroundImage(nil, for: .disabled) - objc_setAssociatedObject(self, &disabledColorHandle, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - } - } - } - - @IBInspectable - var highlightedColor: UIColor? { - get { - if let color = objc_getAssociatedObject(self, &highlightedColorHandle) as? UIColor { - return color - } - return nil - } - set { - if let color = newValue { - self.setBackgroundColor(color, for: .highlighted) - objc_setAssociatedObject(self, &highlightedColorHandle, color, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - } else { - self.setBackgroundImage(nil, for: .highlighted) - objc_setAssociatedObject(self, &highlightedColorHandle, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - } - } - } - - @IBInspectable - var selectedColor: UIColor? { - get { - if let color = objc_getAssociatedObject(self, &selectedColorHandle) as? UIColor { - return color - } - return nil - } - set { - if let color = newValue { - self.setBackgroundColor(color, for: .selected) - objc_setAssociatedObject(self, &selectedColorHandle, color, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - } else { - self.setBackgroundImage(nil, for: .selected) - objc_setAssociatedObject(self, &selectedColorHandle, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - } - } - } -} diff --git a/Paystack/Classes/UI/KeyboardHandlingVC.swift b/Paystack/Classes/UI/KeyboardHandlingVC.swift deleted file mode 100644 index 820c88c..0000000 --- a/Paystack/Classes/UI/KeyboardHandlingVC.swift +++ /dev/null @@ -1,84 +0,0 @@ -// -// KeyboardHandlingVC.swift -// PaystackiOS -// -// Created by Jubril Olambiwonnu on 7/9/20. -// Copyright © 2020 Paystack, Inc. All rights reserved. -// - -import UIKit - -public class PSTCKKeyboardHandlingBaseVC: UIViewController { - - @IBOutlet weak var backgroundSV: UIScrollView! - - public override func viewDidLoad() { - super.viewDidLoad() - - subscribeToNotification(UIResponder.keyboardWillShowNotification, selector: #selector(keyboardWillShowOrHide)) - subscribeToNotification(UIResponder.keyboardWillHideNotification, selector: #selector(keyboardWillShowOrHide)) - - initializeHideKeyboard() - - } - - public override func viewWillDisappear(_ animated: Bool) { - super.viewWillDisappear(animated) - unsubscribeFromAllNotifications() - } - -} - -// MARK : Keyboard Dismissal Handling on Tap -private extension PSTCKKeyboardHandlingBaseVC { - - func initializeHideKeyboard(){ - let tap: UITapGestureRecognizer = UITapGestureRecognizer( - target: self, - action: #selector(dismissMyKeyboard)) - - view.addGestureRecognizer(tap) - } - - @objc func dismissMyKeyboard(){ - view.endEditing(true) - } -} - -// MARK : Textfield Visibility Handling with Scroll -private extension PSTCKKeyboardHandlingBaseVC { - - func subscribeToNotification(_ notification: NSNotification.Name, selector: Selector) { - NotificationCenter.default.addObserver(self, selector: selector, name: notification, object: nil) - } - - func unsubscribeFromAllNotifications() { - NotificationCenter.default.removeObserver(self) - } - - @objc func keyboardWillShowOrHide(notification: NSNotification) { - - // Pull a bunch of info out of the notification - if let scrollView = backgroundSV, let userInfo = notification.userInfo, let endValue = userInfo[UIResponder.keyboardFrameEndUserInfoKey], let durationValue = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey], let curveValue = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] { - - // Transform the keyboard's frame into our view's coordinate system - let endRect = view.convert((endValue as AnyObject).cgRectValue, from: view.window) - - // Find out how much the keyboard overlaps the scroll userInfoview - // We can do this because our scroll view's frame is already in our view's coordinate system - let keyboardOverlap = scrollView.frame.maxY - endRect.origin.y - - // Set the scroll view's content inset to avoid the keyboard - // Don't forget the scroll indicator too! - scrollView.contentInset.bottom = keyboardOverlap - scrollView.scrollIndicatorInsets.bottom = keyboardOverlap - - let duration = (durationValue as AnyObject).doubleValue - let options = UIView.AnimationOptions(rawValue: UInt((curveValue as AnyObject).integerValue << 16)) - UIView.animate(withDuration: duration!, delay: 0, options: options, animations: { - self.view.layoutIfNeeded() - }, completion: nil) - } - } - -} diff --git a/Paystack/Classes/UI/PSTCKAddressViewController.swift b/Paystack/Classes/UI/PSTCKAddressViewController.swift deleted file mode 100644 index 0c47cca..0000000 --- a/Paystack/Classes/UI/PSTCKAddressViewController.swift +++ /dev/null @@ -1,108 +0,0 @@ -// -// AddressViewController.swift -// Paystack iOS Example -// -// Created by Jubril Olambiwonnu on 6/21/20. -// Copyright © 2020 Paystack. All rights reserved. -// - -import UIKit - -@objc public class PSTCKAddressViewController: PSTCKKeyboardHandlingBaseVC, UIPickerViewDelegate, UIPickerViewDataSource, UITextFieldDelegate { - @IBOutlet var streetField: UITextField! - @IBOutlet var cityField: UITextField! - @IBOutlet var stateField: UITextField! - @IBOutlet var zipField: UITextField! - let stateInput = UIPickerView() - let validator = Validator() - @IBOutlet var activityIndicator: UIActivityIndicatorView! - @objc public var states = [PSTCKState]() - @objc public var didCollectAddress: (([String:Any]) -> Void)? - @objc public var didTapCancelButton: (() -> Void)? - @objc public var transaction = "" - - - @IBOutlet var paymentButton: UIButton! - public override func viewDidLoad() { - super.viewDidLoad() - registerTextFields() - paymentButton.isEnabled = false - stateInput.dataSource = self - stateInput.delegate = self - stateField.inputView = stateInput - stateField.delegate = self - } - - @IBAction func onCancelButtonTap(_ sender: Any) { - didTapCancelButton?() - dismiss(animated: true) - } - - public override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - stateInput.reloadAllComponents() - } - - func registerTextFields() { - validator.registerField(streetField, rules: [RequiredRule()]) - validator.registerField(cityField, rules: [RequiredRule()]) - validator.registerField(stateField, rules: [RequiredRule()]) - validator.registerField(zipField, rules: [RequiredRule()]) - streetField.addTarget(self, action: #selector(textFieldChanged), for: .editingChanged) - cityField.addTarget(self, action: #selector(textFieldChanged), for: .editingChanged) - stateField.addTarget(self, action: #selector(textFieldChanged), for: .editingChanged) - zipField.addTarget(self, action: #selector(textFieldChanged), for: .editingChanged) - } - - @IBAction func onButtonTap(_ sender: Any) { - paymentButton.setTitle(" ", for: .normal) - paymentButton.isUserInteractionEnabled = false - activityIndicator.startAnimating() - let address: [String : Any] = [ - "trans" : transaction, - "address" : streetField.text!, - "city" : cityField.text!, - "zip_code" : zipField.text!, - "state" : stateField.text! - ] - didCollectAddress?(address) - dismiss(animated: true) - } - - @objc func textFieldChanged() { - validator.validate(self) - } - - public func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { - return states[row].name - } - - public func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { - return states.count - } - - public func numberOfComponents(in pickerView: UIPickerView) -> Int { - return 1 - } - - public func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { - stateField.text = states[row].name - validator.validate(self) - } - - public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { - return false - } -} - -extension PSTCKAddressViewController: ValidationDelegate { - public func validationSuccessful() { - paymentButton.isEnabled = true - } - - public func validationFailed(_ errors: [(Validatable, ValidationError)]) { - paymentButton.isEnabled = false - } -} - - diff --git a/Paystack/Classes/UI/PSTCKAuthViewController.h b/Paystack/Classes/UI/PSTCKAuthViewController.h deleted file mode 100644 index f189adb..0000000 --- a/Paystack/Classes/UI/PSTCKAuthViewController.h +++ /dev/null @@ -1,31 +0,0 @@ -// -// EBAEventbritePurchaseViewController.h -// InEvent -// -// Created by Pedro Góes on 12/16/15. -// Copyright © 2015 InEvent. All rights reserved. -// - -#import -#import - -typedef void(^PSTCKAuthCallback)(void); - -/** - * View Controller subclass containing a `UIWebView` which will be used to display the Paystack web UI to perform the authorization. - **/ -@interface PSTCKAuthViewController : UIViewController - -/** ************************************************************************************************ ** - * @name Initializers - ** ************************************************************************************************ **/ - -/** - * Default initializer. - * @param authURL the authorization url from Paystack. - * @param completion A standard block. - * @returns An initialized instance - **/ -- (id)initWithURL:(NSURL *)authURL handler:(PSTCKAuthCallback)completion; - -@end diff --git a/Paystack/Classes/UI/PSTCKAuthViewController.m b/Paystack/Classes/UI/PSTCKAuthViewController.m deleted file mode 100644 index e2e25c1..0000000 --- a/Paystack/Classes/UI/PSTCKAuthViewController.m +++ /dev/null @@ -1,124 +0,0 @@ -// -// EBAEventbritePurchaseViewController.m -// InEvent -// -// Created by Pedro Góes on 12/16/15. -// Copyright © 2015 InEvent. All rights reserved. -// - -#import "PSTCKAuthViewController.h" - -@interface PSTCKAuthViewController () - -@property(nonatomic, strong) WKWebView *authenticationWebView; -@property(nonatomic, copy) PSTCKAuthCallback completion; -@property(nonatomic, strong) NSURL *authURL; - -@end - -@interface PSTCKAuthViewController (WKNavigationDelegate) - -@end - -@implementation PSTCKAuthViewController - -BOOL handlingRedirectURL; - -- (id)initWithURL:(NSURL *)authURL handler:(PSTCKAuthCallback)completion { - self = [super init]; - if (self) { - self.authURL = authURL; - self.completion = completion; - } - return self; -} - -- (void)viewDidLoad { - [super viewDidLoad]; - -#ifdef __IPHONE_7_0 - self.edgesForExtendedLayout = UIRectEdgeNone; -#endif - - UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(tappedCancelButton:)]; - self.navigationItem.leftBarButtonItem = cancelButton; - // Adds javascript to make content width the device width. - NSString *jScript = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);"; - WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:jScript injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES]; - WKUserContentController *wkUController = [[WKUserContentController alloc] init]; - [wkUController addUserScript:wkUScript]; - WKWebViewConfiguration *wkWebConfig = [[WKWebViewConfiguration alloc] init]; - wkWebConfig.userContentController = wkUController; - - self.authenticationWebView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:wkWebConfig]; - self.authenticationWebView.UIDelegate = self; - self.authenticationWebView.navigationDelegate = self; - [self.view addSubview:self.authenticationWebView]; - [self.navigationController setNavigationBarHidden:NO animated:YES]; - self.navigationController.modalTransitionStyle = UIModalTransitionStyleCoverVertical; -} - -- (void)viewDidAppear:(BOOL)animated { - [super viewDidAppear:animated]; - [self.authenticationWebView loadRequest:[NSURLRequest requestWithURL:self.authURL]]; - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; -} - -- (void)viewWillLayoutSubviews { - [super viewWillLayoutSubviews]; - self.authenticationWebView.frame = [UIScreen mainScreen].bounds; -} - -#pragma mark UI Action Methods - -- (void)tappedCancelButton:(id)cancelButton { - #pragma unused(cancelButton) - self.completion(); -} - -@end - -@implementation PSTCKAuthViewController (WKNavigationDelegate) - -- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { - #pragma unused(webView, navigationAction) - NSURLRequest *request = navigationAction.request; - NSString *url = [[request URL]absoluteString]; - - // Prevent loading URL if it is the redirectURL - // The intention is to only requery 3DS auths - handlingRedirectURL = !([url rangeOfString:@"paystack.co/charge/three_d_response/"].location == NSNotFound); - - // Processing has finished? - if (handlingRedirectURL) { - self.completion(); - return decisionHandler(WKNavigationActionPolicyCancel); - - } - else { - return decisionHandler(WKNavigationActionPolicyAllow); - } -} - -- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation { - #pragma unused(webView, navigation) - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; -} - -- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error { - #pragma unused(webView, navigation) - // Turn off network activity indicator upon failure to load web view - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; - - // In case the user hits 'Allow' before the page is fully loaded - if (error.code == NSURLErrorCancelled) { - return; - } - - // Abort if we are on Eventbrite's domain - if (!handlingRedirectURL) { - self.completion(); - } -} - -@end diff --git a/Paystack/Classes/UI/PSTCKFormTextField.h b/Paystack/Classes/UI/PSTCKFormTextField.h deleted file mode 100644 index f9aa4c6..0000000 --- a/Paystack/Classes/UI/PSTCKFormTextField.h +++ /dev/null @@ -1,28 +0,0 @@ -// -// PSTCKFormTextField.h -// Paystack -// - -#import - -@class PSTCKFormTextField; - -@protocol PSTCKFormTextFieldDelegate - -- (void)formTextFieldDidBackspaceOnEmpty:(nonnull PSTCKFormTextField *)formTextField; - -@end - -@interface PSTCKFormTextField : UITextField - -@property(nonatomic, readwrite, nullable) UIColor *defaultColor; -@property(nonatomic, readwrite, nullable) UIColor *errorColor; -@property(nonatomic, readwrite, nullable) UIColor *placeholderColor; - -@property(nonatomic, readwrite, assign)BOOL formatsCardNumbers; -@property(nonatomic, readwrite, assign)BOOL validText; -@property(nonatomic, readwrite, weak, nullable)idformDelegate; - -- (CGSize)measureTextSize; - -@end diff --git a/Paystack/Classes/UI/PSTCKFormTextField.m b/Paystack/Classes/UI/PSTCKFormTextField.m deleted file mode 100644 index 22b0dcd..0000000 --- a/Paystack/Classes/UI/PSTCKFormTextField.m +++ /dev/null @@ -1,147 +0,0 @@ -// -// PSTCKFormTextField.m -// Paystack -// - -#import "PSTCKFormTextField.h" -#import "PSTCKCardValidator.h" - -#import -#import "TargetConditionals.h" - -#define FAUXPAS_IGNORED_IN_METHOD(...) - -@implementation PSTCKFormTextField - -@synthesize placeholderColor = _placeholderColor; - -- (void)setFormDelegate:(id)formDelegate { - _formDelegate = formDelegate; - self.delegate = formDelegate; -} - -- (void)deleteBackward { - // This deliberately doesn't call super, because the superclass' implementation replaces text without calling delegate methods. - if (self.text.length == 0) { - [self.formDelegate formTextFieldDidBackspaceOnEmpty:self]; - return; - } - NSRange range = NSMakeRange(self.text.length - 1, 1); - if ([self.delegate textField:self shouldChangeCharactersInRange:range replacementString:@""]) { - self.text = [self.text stringByReplacingCharactersInRange:range withString:@""]; - } -} - -- (CGSize)measureTextSize { - return self.attributedText.size; -} - -- (void)setText:(NSString *)text { - NSAttributedString *attributed = [self attributedStringForString:text attributes:[self safeDefaultTextAttributes]]; - [self setAttributedText:attributed]; -} - -- (void)setPlaceholder:(NSString *)placeholder { - NSMutableDictionary *attributes = [[self safeDefaultTextAttributes] mutableCopy]; - if (self.placeholderColor) { - attributes[NSForegroundColorAttributeName] = self.placeholderColor; - } - NSAttributedString *attributed = [self attributedStringForString:placeholder attributes:[attributes copy]]; - [self setAttributedPlaceholder:attributed]; -} - -- (NSAttributedString *)attributedStringForString:(NSString *)string attributes:(NSDictionary *)attributes { - if (!string) { - return nil; - } - NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:string attributes:attributes]; - if (self.formatsCardNumbers && [PSTCKCardValidator stringIsNumeric:string]) { - NSArray *cardSpacing; - PSTCKCardBrand currentBrand = [PSTCKCardValidator brandForNumber:attributedString.string]; - if (currentBrand == PSTCKCardBrandAmex) { - cardSpacing = @[@3, @9]; - } else if (currentBrand == PSTCKCardBrandVerve) { - cardSpacing = @[@2, @6, @10, @14]; - } else { - cardSpacing = @[@3, @7, @11]; - } - for (NSUInteger i = 0; i < attributedString.length; i++) { - if ([cardSpacing containsObject:@(i)]) { - [attributedString addAttribute:NSKernAttributeName value:@(5) - range:NSMakeRange(i, 1)]; - } else { - [attributedString addAttribute:NSKernAttributeName value:@(0) - range:NSMakeRange(i, 1)]; - } - } - } - return [attributedString copy]; -} - -- (NSDictionary *)safeDefaultTextAttributes { - FAUXPAS_IGNORED_IN_METHOD(APIAvailability); - if ([self respondsToSelector:@selector(defaultTextAttributes)]) { - return [self defaultTextAttributes]; - } - NSMutableDictionary *attributes = [NSMutableDictionary dictionary]; - if (self.textColor) { - attributes[NSForegroundColorAttributeName] = self.textColor; - } - if (self.font) { - attributes[NSFontAttributeName] = self.font; - } - return [attributes copy]; -} - -- (void)setDefaultColor:(UIColor *)defaultColor { - _defaultColor = defaultColor; - [self updateColor]; -} - -- (void)setErrorColor:(UIColor *)errorColor { - _errorColor = errorColor; - [self updateColor]; -} - -- (void)setValidText:(BOOL)validText { - _validText = validText; - [self updateColor]; -} - -- (void)setPlaceholderColor:(UIColor *)placeholderColor { - _placeholderColor = placeholderColor; - [self setPlaceholder:self.placeholder]; //explicitly rebuild attributed placeholder -} - -- (void)updateColor { - self.textColor = _validText ? self.defaultColor : self.errorColor; -} - -// Workaround for http://www.openradar.appspot.com/19374610 -- (CGRect)editingRectForBounds:(CGRect)bounds { - if (UIDevice.currentDevice.systemVersion.integerValue != 8) { - return [self textRectForBounds:bounds]; - } - - CGFloat const scale = UIScreen.mainScreen.scale; - CGFloat const preferred = self.attributedText.size.height; - CGFloat const delta = (CGFloat)ceil(preferred) - preferred; - CGFloat const adjustment = (CGFloat)floor(delta * scale) / scale; - - CGRect const textRect = [self textRectForBounds:bounds]; - CGRect const editingRect = CGRectOffset(textRect, 0.0, adjustment); - - return editingRect; -} - -// Fixes a weird issue related to our custom override of deleteBackwards. This only affects the simulator and iPads with custom keyboards. -- (NSArray *)keyCommands { - FAUXPAS_IGNORED_IN_METHOD(APIAvailability); - return @[[UIKeyCommand keyCommandWithInput:@"\b" modifierFlags:UIKeyModifierCommand action:@selector(commandDeleteBackwards)]]; -} - -- (void)commandDeleteBackwards { - self.text = @""; -} - -@end diff --git a/Paystack/Classes/UI/PSTCKPaymentCardTextField.m b/Paystack/Classes/UI/PSTCKPaymentCardTextField.m deleted file mode 100644 index 9b95968..0000000 --- a/Paystack/Classes/UI/PSTCKPaymentCardTextField.m +++ /dev/null @@ -1,774 +0,0 @@ -// -// PSTCKPaymentCardTextField.m -// Paystack -// - -#import - -#import "Paystack.h" -#import "PSTCKPaymentCardTextField.h" -#import "PSTCKPaymentCardTextFieldViewModel.h" -#import "PSTCKFormTextField.h" -#import "PSTCKCardValidator.h" -#import "UIImage+Paystack.h" - -#define FAUXPAS_IGNORED_IN_METHOD(...) - -@interface PSTCKPaymentCardTextField() - -@property(nonatomic, readwrite, strong)PSTCKFormTextField *sizingField; - -@property(nonatomic, readwrite, weak)UIImageView *brandImageView; -@property(nonatomic, readwrite, weak)UIView *fieldsView; - -@property(nonatomic, readwrite, weak)PSTCKFormTextField *numberField; - -@property(nonatomic, readwrite, weak)PSTCKFormTextField *expirationField; - -@property(nonatomic, readwrite, weak)UIButton *bumpToExpField; - - -@property(nonatomic, readwrite, weak)PSTCKFormTextField *cvcField; - -@property(nonatomic, readwrite, strong)PSTCKPaymentCardTextFieldViewModel *viewModel; - -@property(nonatomic, readwrite, weak)UITextField *selectedField; - -@property(nonatomic, assign)BOOL numberFieldShrunk; -@property(nonatomic, assign)BOOL bumped; - -@end - -@implementation PSTCKPaymentCardTextField - -@synthesize font = _font; -@synthesize textColor = _textColor; -@synthesize textErrorColor = _textErrorColor; -@synthesize placeholderColor = _placeholderColor; -@dynamic enabled; - -CGFloat const PSTCKPaymentCardTextFieldDefaultPadding = 10; - -#if CGFLOAT_IS_DOUBLE -#define pstck_roundCGFloat(x) round(x) -#else -#define pstck_roundCGFloat(x) roundf(x) -#endif - -#pragma mark initializers - -- (id)initWithCoder:(NSCoder *)aDecoder { - self = [super initWithCoder:aDecoder]; - if (self) { - [self commonInit]; - } - return self; -} - -- (instancetype)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if (self) { - [self commonInit]; - } - return self; -} - -- (void)commonInit { - - self.borderColor = [self.class placeholderGrayColor]; - self.cornerRadius = 5.0f; - self.borderWidth = 1.0f; - - self.clipsToBounds = YES; - - _viewModel = [PSTCKPaymentCardTextFieldViewModel new]; - _sizingField = [self buildTextField]; - - UIImageView *brandImageView = [[UIImageView alloc] initWithImage:self.brandImage]; - brandImageView.contentMode = UIViewContentModeCenter; - brandImageView.backgroundColor = [UIColor clearColor]; - if ([brandImageView respondsToSelector:@selector(setTintColor:)]) { - brandImageView.tintColor = self.placeholderColor; - } - self.brandImageView = brandImageView; - - PSTCKFormTextField *numberField = [self buildTextField]; - numberField.formatsCardNumbers = YES; - numberField.tag = PSTCKCardFieldTypeNumber; - self.numberField = numberField; - self.numberPlaceholder = [self.viewModel defaultPlaceholder]; - - PSTCKFormTextField *expirationField = [self buildTextField]; - expirationField.tag = PSTCKCardFieldTypeExpiration; - expirationField.alpha = 0; - self.expirationField = expirationField; - self.expirationPlaceholder = @"MM/YY"; - - UIButton *bumpToExpField = [UIButton buttonWithType:UIButtonTypeCustom]; - [bumpToExpField addTarget:self action:@selector(tappedBumpButton:) forControlEvents:UIControlEventTouchUpInside]; - [bumpToExpField setTitle: @">" forState: UIControlStateNormal]; - [bumpToExpField setTitleColor:self.textColor forState:UIControlStateNormal]; - bumpToExpField.hidden = YES; - self.bumpToExpField.alpha = 50; - self.bumpToExpField = bumpToExpField; - self.bumped = NO; - - PSTCKFormTextField *cvcField = [self buildTextField]; - cvcField.tag = PSTCKCardFieldTypeCVC; - cvcField.alpha = 0; - self.cvcField = cvcField; - self.cvcPlaceholder = @"CVC"; - - UIView *fieldsView = [[UIView alloc] init]; - fieldsView.clipsToBounds = YES; - fieldsView.backgroundColor = [UIColor clearColor]; - self.fieldsView = fieldsView; - - [self addSubview:self.fieldsView]; - [self.fieldsView addSubview:cvcField]; - [self.fieldsView addSubview:expirationField]; - [self.fieldsView addSubview:numberField]; - [self addSubview:brandImageView]; - [self addSubview:bumpToExpField]; -} - -- (void)tappedBumpButton:(id)bumpButton { -#pragma unused(bumpButton) - self.bumped = YES; - self.bumpToExpField.hidden = YES; - if([self.viewModel validationStateForField:PSTCKCardFieldTypeNumber]==PSTCKCardValidationStateValid){ - [self selectNextField]; - } -} - -- (PSTCKPaymentCardTextFieldViewModel *)viewModel { - if (_viewModel == nil) { - _viewModel = [PSTCKPaymentCardTextFieldViewModel new]; - } - return _viewModel; -} - -#pragma mark appearance properties - -+ (UIColor *)placeholderGrayColor { - return [UIColor lightGrayColor]; -} - -- (void)setBackgroundColor:(UIColor *)backgroundColor { - [super setBackgroundColor:[backgroundColor copy]]; - self.numberField.backgroundColor = self.backgroundColor; -} - -- (UIColor *)backgroundColor { - return [super backgroundColor] ?: [UIColor whiteColor]; -} - -- (void)setFont:(UIFont *)font { - _font = [font copy]; - - for (UITextField *field in [self allFields]) { - field.font = _font; - } - - self.sizingField.font = _font; - - [self setNeedsLayout]; -} - -- (UIFont *)font { - return _font ?: [UIFont systemFontOfSize:18]; -} - -- (void)setTextColor:(UIColor *)textColor { - _textColor = [textColor copy]; - - for (PSTCKFormTextField *field in [self allFields]) { - field.defaultColor = _textColor; - } -} - -- (void)setContentVerticalAlignment:(UIControlContentVerticalAlignment)contentVerticalAlignment { - [super setContentVerticalAlignment:contentVerticalAlignment]; - for (UITextField *field in [self allFields]) { - field.contentVerticalAlignment = contentVerticalAlignment; - } - switch (contentVerticalAlignment) { - case UIControlContentVerticalAlignmentCenter: - self.brandImageView.contentMode = UIViewContentModeCenter; - break; - case UIControlContentVerticalAlignmentBottom: - self.brandImageView.contentMode = UIViewContentModeBottom; - break; - case UIControlContentVerticalAlignmentFill: - self.brandImageView.contentMode = UIViewContentModeTop; - break; - case UIControlContentVerticalAlignmentTop: - self.brandImageView.contentMode = UIViewContentModeTop; - break; - } -} - -- (UIColor *)textColor { - return _textColor ?: [UIColor blackColor]; -} - -- (void)setTextErrorColor:(UIColor *)textErrorColor { - _textErrorColor = [textErrorColor copy]; - - for (PSTCKFormTextField *field in [self allFields]) { - field.errorColor = _textErrorColor; - } -} - -- (UIColor *)textErrorColor { - return _textErrorColor ?: [UIColor redColor]; -} - -- (void)setPlaceholderColor:(UIColor *)placeholderColor { - _placeholderColor = [placeholderColor copy]; - - if ([self.brandImageView respondsToSelector:@selector(setTintColor:)]) { - self.brandImageView.tintColor = placeholderColor; - } - - for (PSTCKFormTextField *field in [self allFields]) { - field.placeholderColor = _placeholderColor; - } -} - -- (UIColor *)placeholderColor { - return _placeholderColor ?: [self.class placeholderGrayColor]; -} - -- (void)setNumberPlaceholder:(NSString * __nullable)numberPlaceholder { - _numberPlaceholder = [numberPlaceholder copy]; - self.numberField.placeholder = _numberPlaceholder; -} - -- (void)setExpirationPlaceholder:(NSString * __nullable)expirationPlaceholder { - _expirationPlaceholder = [expirationPlaceholder copy]; - self.expirationField.placeholder = _expirationPlaceholder; -} - -- (void)setCvcPlaceholder:(NSString * __nullable)cvcPlaceholder { - _cvcPlaceholder = [cvcPlaceholder copy]; - self.cvcField.placeholder = _cvcPlaceholder; -} - -- (void)setCursorColor:(UIColor *)cursorColor { - self.tintColor = cursorColor; -} - -- (UIColor *)cursorColor { - return self.tintColor; -} - -- (void)setBorderColor:(UIColor * __nullable)borderColor { - self.layer.borderColor = [[borderColor copy] CGColor]; -} - -- (UIColor * __nullable)borderColor { - return [[UIColor alloc] initWithCGColor:self.layer.borderColor]; -} - -- (void)setCornerRadius:(CGFloat)cornerRadius { - self.layer.cornerRadius = cornerRadius; -} - -- (CGFloat)cornerRadius { - return self.layer.cornerRadius; -} - -- (void)setBorderWidth:(CGFloat)borderWidth { - self.layer.borderWidth = borderWidth; -} - -- (CGFloat)borderWidth { - return self.layer.borderWidth; -} - -- (void)setKeyboardAppearance:(UIKeyboardAppearance)keyboardAppearance { - _keyboardAppearance = keyboardAppearance; - for (PSTCKFormTextField *field in [self allFields]) { - field.keyboardAppearance = keyboardAppearance; - } -} - -- (void)setInputAccessoryView:(UIView *)inputAccessoryView { - _inputAccessoryView = inputAccessoryView; - - for (PSTCKFormTextField *field in [self allFields]) { - field.inputAccessoryView = inputAccessoryView; - } -} - -#pragma mark UIControl - -- (void)setEnabled:(BOOL)enabled { - [super setEnabled:enabled]; - for (PSTCKFormTextField *textField in [self allFields]) { - textField.enabled = enabled; - }; -} - -#pragma mark UIResponder & related methods - -- (BOOL)isFirstResponder { - return [self.selectedField isFirstResponder]; -} - -- (BOOL)canBecomeFirstResponder { - return [[self firstResponderField] canBecomeFirstResponder]; -} - -- (BOOL)becomeFirstResponder { - return [[self firstResponderField] becomeFirstResponder]; -} - -- (PSTCKFormTextField *)firstResponderField { - - if ([self.viewModel validationStateForField:PSTCKCardFieldTypeNumber] != PSTCKCardValidationStateValid) { - return self.numberField; - } else if ([self.viewModel validationStateForField:PSTCKCardFieldTypeExpiration] != PSTCKCardValidationStateValid) { - return self.expirationField; - } else { - return self.cvcField; - } -} - -- (BOOL)canResignFirstResponder { - return [self.selectedField canResignFirstResponder]; -} - -- (BOOL)resignFirstResponder { - [super resignFirstResponder]; - BOOL success = [self.selectedField resignFirstResponder]; - [self setNumberFieldShrunk:[self shouldShrinkNumberField] animated:YES completion:nil]; - return success; -} - -- (BOOL)selectNextField { - return [[self nextField] becomeFirstResponder]; -} - -- (BOOL)selectPreviousField { - return [[self previousField] becomeFirstResponder]; -} - -- (PSTCKFormTextField *)nextField { - if (self.selectedField == self.numberField) { - Boolean stay = NO; - if(self.viewModel.brand==PSTCKCardBrandVerve){ - stay = (self.numberField.text.length < 19); - if(stay){ - stay = !self.bumped; - } - } - self.bumpToExpField.hidden = !stay; - self.bumped = NO; - if(stay){ - return self.numberField; - } - if ([self.viewModel validationStateForField:self.expirationField.tag] == PSTCKCardValidationStateValid) { - return self.cvcField; - } - return self.expirationField; - } else if (self.selectedField == self.expirationField) { - return self.cvcField; - } - return nil; -} - -- (PSTCKFormTextField *)previousField { - if (self.selectedField == self.cvcField) { - return self.expirationField; - } else if (self.selectedField == self.expirationField) { - return self.numberField; - } - return nil; -} - -#pragma mark public convenience methods - -- (void)clear { - for (PSTCKFormTextField *field in [self allFields]) { - field.text = @""; - } - self.viewModel = [PSTCKPaymentCardTextFieldViewModel new]; - [self onChange]; - [self updateImageForFieldType:PSTCKCardFieldTypeNumber]; - __weak id weakself = self; - [self setNumberFieldShrunk:NO animated:YES completion:^(__unused BOOL completed){ - __strong id strongself = weakself; - if ([strongself isFirstResponder]) { - [[strongself numberField] becomeFirstResponder]; - } - }]; -} - -- (BOOL)isValid { - return [self.viewModel isValid]; -} - -#pragma mark readonly variables - -- (NSString *)cardNumber { - return self.viewModel.cardNumber; -} - -- (NSUInteger)expirationMonth { - return [self.viewModel.expirationMonth integerValue]; -} - -- (NSUInteger)expirationYear { - return [self.viewModel.expirationYear integerValue]; -} - -- (NSString *)cvc { - return self.viewModel.cvc; -} - -- (PSTCKCardParams *)cardParams { - PSTCKCardParams *c = [[PSTCKCardParams alloc] init]; - c.number = self.cardNumber; - c.expMonth = self.expirationMonth; - c.expYear = self.expirationYear; - c.cvc = self.cvc; - return c; -} - -- (void)setCardParams:(PSTCKCardParams *)cardParams { - [self setText:cardParams.number inField:PSTCKCardFieldTypeNumber]; - BOOL expirationPresent = cardParams.expMonth && cardParams.expYear; - if (expirationPresent) { - NSString *text = [NSString stringWithFormat:@"%02lu%02lu", - (unsigned long)cardParams.expMonth, - (unsigned long)cardParams.expYear%100]; - [self setText:text inField:PSTCKCardFieldTypeExpiration]; - } - else { - [self setText:@"" inField:PSTCKCardFieldTypeExpiration]; - } - [self setText:cardParams.cvc inField:PSTCKCardFieldTypeCVC]; - - BOOL shrinkNumberField = [self shouldShrinkNumberField]; - [self setNumberFieldShrunk:shrinkNumberField animated:NO completion:nil]; - - // update the card image, falling back to the number field image if not editing - if ([self.expirationField isFirstResponder]) { - [self updateImageForFieldType:PSTCKCardFieldTypeExpiration]; - } - else if ([self.cvcField isFirstResponder]) { - [self updateImageForFieldType:PSTCKCardFieldTypeCVC]; - } - else { - [self updateImageForFieldType:PSTCKCardFieldTypeNumber]; - } -} - -- (PSTCKCardParams *)card { - if (!self.isValid) { return nil; } - return self.cardParams; -} - -- (void)setCard:(PSTCKCardParams *)card { - [self setCardParams:card]; -} - -- (void)setText:(NSString *)text inField:(PSTCKCardFieldType)field { - NSString *nonNilText = text == nil ? @"" : text; - PSTCKFormTextField *textField = nil; - switch (field) { - case PSTCKCardFieldTypeNumber: - textField = self.numberField; - break; - case PSTCKCardFieldTypeExpiration: - textField = self.expirationField; - break; - case PSTCKCardFieldTypeCVC: - textField = self.cvcField; - break; - } - self.selectedField = (self.isFirstResponder) ? textField : nil; - id delegate = (id)self; - NSRange range = NSMakeRange(0, textField.text.length); - [delegate textField:textField shouldChangeCharactersInRange:range - replacementString:nonNilText]; -} - -- (CGSize)intrinsicContentSize { - - CGSize imageSize = self.brandImage.size; - - self.sizingField.text = self.viewModel.defaultPlaceholder; - CGFloat textHeight = [self.sizingField measureTextSize].height; - CGFloat imageHeight = imageSize.height + (PSTCKPaymentCardTextFieldDefaultPadding * 2); - CGFloat height = pstck_roundCGFloat((MAX(MAX(imageHeight, textHeight), 44))); - - CGFloat width = pstck_roundCGFloat([self widthForCardNumber:self.viewModel.defaultPlaceholder] + imageSize.width + (PSTCKPaymentCardTextFieldDefaultPadding * 3)); - - return CGSizeMake(width, height); -} - -- (CGRect)brandImageRectForBounds:(CGRect)bounds { - return CGRectMake(PSTCKPaymentCardTextFieldDefaultPadding, 2, self.brandImageView.image.size.width, bounds.size.height - 2); -} - -- (CGRect)bumpRectForBounds:(CGRect)bounds { - return CGRectMake(CGRectGetWidth(bounds) - [self widthForText:@" > "] - (PSTCKPaymentCardTextFieldDefaultPadding / 2) , 2, [self widthForText:@" > "], bounds.size.height - 2); -} - -- (CGRect)fieldsRectForBounds:(CGRect)bounds { - CGRect brandImageRect = [self brandImageRectForBounds:bounds]; - return CGRectMake(CGRectGetMaxX(brandImageRect), 0, CGRectGetWidth(bounds) - CGRectGetMaxX(brandImageRect), CGRectGetHeight(bounds)); -} - -- (CGRect)numberFieldRectForBounds:(CGRect)bounds { - CGFloat placeholderWidth = [self widthForCardNumber:self.numberField.placeholder] - 4; - CGFloat numberWidth = [self widthForCardNumber:self.viewModel.defaultPlaceholder] - 4; - CGFloat numberFieldWidth = MAX(placeholderWidth, numberWidth); - CGFloat nonFragmentWidth = [self widthForCardNumber:[self.viewModel numberWithoutLastDigits]] - 13; - CGFloat numberFieldX = self.numberFieldShrunk ? PSTCKPaymentCardTextFieldDefaultPadding - nonFragmentWidth : 8; - return CGRectMake(numberFieldX, 0, numberFieldWidth, CGRectGetHeight(bounds)); -} - -- (CGRect)cvcFieldRectForBounds:(CGRect)bounds { - CGRect fieldsRect = [self fieldsRectForBounds:bounds]; - - CGFloat cvcWidth = MAX([self widthForText:self.cvcField.placeholder], [self widthForText:@"8888"]); - CGFloat cvcX = self.numberFieldShrunk ? - CGRectGetWidth(fieldsRect) - cvcWidth - PSTCKPaymentCardTextFieldDefaultPadding / 2 : - CGRectGetWidth(fieldsRect); - return CGRectMake(cvcX, 0, cvcWidth, CGRectGetHeight(bounds)); -} - -- (CGRect)expirationFieldRectForBounds:(CGRect)bounds { - CGRect numberFieldRect = [self numberFieldRectForBounds:bounds]; - CGRect cvcRect = [self cvcFieldRectForBounds:bounds]; - - CGFloat expirationWidth = MAX([self widthForText:self.expirationField.placeholder], [self widthForText:@"88/88"]); - CGFloat expirationX = (CGRectGetMaxX(numberFieldRect) + CGRectGetMinX(cvcRect) - expirationWidth) / 2; - return CGRectMake(expirationX, 0, expirationWidth, CGRectGetHeight(bounds)); -} - -- (void)layoutSubviews { - [super layoutSubviews]; - - CGRect bounds = self.bounds; - - self.bumpToExpField.frame = [self bumpRectForBounds:bounds]; - self.brandImageView.frame = [self brandImageRectForBounds:bounds]; - self.fieldsView.frame = [self fieldsRectForBounds:bounds]; - self.numberField.frame = [self numberFieldRectForBounds:bounds]; - self.cvcField.frame = [self cvcFieldRectForBounds:bounds]; - self.expirationField.frame = [self expirationFieldRectForBounds:bounds]; - -} - -#pragma mark - private helper methods - -- (PSTCKFormTextField *)buildTextField { - PSTCKFormTextField *textField = [[PSTCKFormTextField alloc] initWithFrame:CGRectZero]; - textField.backgroundColor = [UIColor clearColor]; - textField.keyboardType = UIKeyboardTypeNumberPad; - textField.font = self.font; - textField.defaultColor = self.textColor; - textField.errorColor = self.textErrorColor; - textField.placeholderColor = self.placeholderColor; - textField.formDelegate = self; - return textField; -} - -- (NSArray *)allFields { - return @[self.numberField, self.expirationField, self.cvcField]; -} - -typedef void (^PSTCKNumberShrunkCompletionBlock)(BOOL completed); -- (void)setNumberFieldShrunk:(BOOL)shrunk animated:(BOOL)animated - completion:(PSTCKNumberShrunkCompletionBlock)completion { - - if (_numberFieldShrunk == shrunk) { - if (completion) { - completion(YES); - } - return; - } - - _numberFieldShrunk = shrunk; - void (^animations)(void) = ^void() { - for (UIView *view in @[self.expirationField, self.cvcField]) { - view.alpha = 1.0f * shrunk; - } - [self layoutSubviews]; - }; - - FAUXPAS_IGNORED_IN_METHOD(APIAvailability); - NSTimeInterval duration = animated * 0.3; - if ([UIView respondsToSelector:@selector(animateWithDuration:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion:)]) { - [UIView animateWithDuration:duration - delay:0 - usingSpringWithDamping:0.85f - initialSpringVelocity:0 - options:0 - animations:animations - completion:completion]; - } else { - [UIView animateWithDuration:duration - animations:animations - completion:completion]; - } -} - -- (BOOL)shouldShrinkNumberField { - return [self.viewModel validationStateForField:PSTCKCardFieldTypeNumber] == PSTCKCardValidationStateValid; -} - -- (CGFloat)widthForText:(NSString *)text { - self.sizingField.formatsCardNumbers = NO; - [self.sizingField setText:text]; - return [self.sizingField measureTextSize].width + 8; -} - -- (CGFloat)widthForTextWithLength:(NSUInteger)length { - NSString *text = [@"" stringByPaddingToLength:length withString:@"M" startingAtIndex:0]; - return [self widthForText:text]; -} - -- (CGFloat)widthForCardNumber:(NSString *)cardNumber { - self.sizingField.formatsCardNumbers = YES; - [self.sizingField setText:cardNumber]; - return [self.sizingField measureTextSize].width + 20; -} - -#pragma mark PSTCKPaymentTextFieldDelegate - -- (void)formTextFieldDidBackspaceOnEmpty:(__unused PSTCKFormTextField *)formTextField { - PSTCKFormTextField *previous = [self previousField]; - [previous becomeFirstResponder]; - [previous deleteBackward]; -} - -- (void)textFieldDidBeginEditing:(UITextField *)textField { - self.selectedField = (PSTCKFormTextField *)textField; - switch ((PSTCKCardFieldType)textField.tag) { - case PSTCKCardFieldTypeNumber: - [self setNumberFieldShrunk:NO animated:YES completion:nil]; - if ([self.delegate respondsToSelector:@selector(paymentCardTextFieldDidBeginEditingNumber:)]) { - [self.delegate paymentCardTextFieldDidBeginEditingNumber:self]; - } - break; - case PSTCKCardFieldTypeCVC: - [self setNumberFieldShrunk:YES animated:YES completion:nil]; - if ([self.delegate respondsToSelector:@selector(paymentCardTextFieldDidBeginEditingCVC:)]) { - [self.delegate paymentCardTextFieldDidBeginEditingCVC:self]; - } - break; - case PSTCKCardFieldTypeExpiration: - [self setNumberFieldShrunk:YES animated:YES completion:nil]; - if ([self.delegate respondsToSelector:@selector(paymentCardTextFieldDidBeginEditingExpiration:)]) { - [self.delegate paymentCardTextFieldDidBeginEditingExpiration:self]; - } - break; - } - [self updateImageForFieldType:textField.tag]; -} - -- (void)textFieldDidEndEditing:(__unused UITextField *)textField { - self.selectedField = nil; -} - -- (BOOL)textField:(PSTCKFormTextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { - - BOOL deletingLastCharacter = (range.location == textField.text.length - 1 && range.length == 1 && [string isEqualToString:@""]); - if (deletingLastCharacter && [textField.text hasSuffix:@"/"] && range.location > 0) { - range.location -= 1; - range.length += 1; - } - - NSString *newText = [textField.text stringByReplacingCharactersInRange:range withString:string]; - PSTCKCardFieldType fieldType = textField.tag; - switch (fieldType) { - case PSTCKCardFieldTypeNumber: - self.viewModel.cardNumber = newText; - textField.text = self.viewModel.cardNumber; - break; - case PSTCKCardFieldTypeExpiration: { - self.viewModel.rawExpiration = newText; - textField.text = self.viewModel.rawExpiration; - break; - } - case PSTCKCardFieldTypeCVC: - self.viewModel.cvc = newText; - textField.text = self.viewModel.cvc; - break; - } - - [self updateImageForFieldType:fieldType]; - - PSTCKCardValidationState state = [self.viewModel validationStateForField:fieldType]; - textField.validText = YES; - switch (state) { - case PSTCKCardValidationStateInvalid: - textField.validText = NO; - break; - case PSTCKCardValidationStateIncomplete: - break; - case PSTCKCardValidationStateValid: { - [self selectNextField]; - break; - } - } - [self onChange]; - - return NO; -} - -- (UIImage *)brandImage { - if (self.selectedField) { - return [self brandImageForFieldType:self.selectedField.tag]; - } else { - return [self brandImageForFieldType:PSTCKCardFieldTypeNumber]; - } -} - -+ (UIImage *)cvcImageForCardBrand:(PSTCKCardBrand)cardBrand { - return [UIImage pstck_cvcImageForCardBrand:cardBrand]; -} - -+ (UIImage *)brandImageForCardBrand:(PSTCKCardBrand)cardBrand { - return [UIImage pstck_brandImageForCardBrand:cardBrand]; -} - -- (UIImage *)brandImageForFieldType:(PSTCKCardFieldType)fieldType { - if (fieldType == PSTCKCardFieldTypeCVC) { - return [self.class cvcImageForCardBrand:self.viewModel.brand]; - } - - return [self.class brandImageForCardBrand:self.viewModel.brand]; -} - -- (void)updateImageForFieldType:(PSTCKCardFieldType)fieldType { - UIImage *image = [self brandImageForFieldType:fieldType]; - if (image != self.brandImageView.image) { - self.brandImageView.image = image; - - CATransition *transition = [CATransition animation]; - transition.duration = 0.2f; - transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; - transition.type = kCATransitionFade; - - [self.brandImageView.layer addAnimation:transition forKey:nil]; - - [self setNeedsLayout]; - } -} - -- (void)onChange { - if ([self.delegate respondsToSelector:@selector(paymentCardTextFieldDidChange:)]) { - [self.delegate paymentCardTextFieldDidChange:self]; - } - [self sendActionsForControlEvents:UIControlEventValueChanged]; -} - -@end - diff --git a/Paystack/Classes/UI/PSTCKPaymentCardTextFieldViewModel.h b/Paystack/Classes/UI/PSTCKPaymentCardTextFieldViewModel.h deleted file mode 100644 index 0671813..0000000 --- a/Paystack/Classes/UI/PSTCKPaymentCardTextFieldViewModel.h +++ /dev/null @@ -1,34 +0,0 @@ -// -// PSTCKPaymentCardTextFieldViewModel.h -// Paystack -// - -#import -#import - -#import "PSTCKCard.h" -#import "PSTCKCardValidator.h" - -typedef NS_ENUM(NSInteger, PSTCKCardFieldType) { - PSTCKCardFieldTypeNumber, - PSTCKCardFieldTypeExpiration, - PSTCKCardFieldTypeCVC, -}; - -@interface PSTCKPaymentCardTextFieldViewModel : NSObject - -@property(nonatomic, readwrite, copy, nullable)NSString *cardNumber; -@property(nonatomic, readwrite, copy, nullable)NSString *rawExpiration; -@property(nonatomic, readonly, nullable)NSString *expirationMonth; -@property(nonatomic, readonly, nullable)NSString *expirationYear; -@property(nonatomic, readwrite, copy, nullable)NSString *cvc; -@property(nonatomic, readonly) PSTCKCardBrand brand; - -- (nonnull NSString *)defaultPlaceholder; -- (nullable NSString *)numberWithoutLastDigits; - -- (BOOL)isValid; - -- (PSTCKCardValidationState)validationStateForField:(PSTCKCardFieldType)fieldType; - -@end diff --git a/Paystack/Classes/UI/PSTCKPaymentCardTextFieldViewModel.m b/Paystack/Classes/UI/PSTCKPaymentCardTextFieldViewModel.m deleted file mode 100644 index 7b23f75..0000000 --- a/Paystack/Classes/UI/PSTCKPaymentCardTextFieldViewModel.m +++ /dev/null @@ -1,119 +0,0 @@ -// -// PSTCKPaymentCardTextFieldViewModel.m -// Paystack -// - -#import "PSTCKPaymentCardTextFieldViewModel.h" -#import "PSTCKCardValidator.h" - -#define FAUXPAS_IGNORED_IN_METHOD(...) - -@interface NSString(PaystackSubstring) -- (NSString *)pstck_safeSubstringToIndex:(NSUInteger)index; -- (NSString *)pstck_safeSubstringFromIndex:(NSUInteger)index; -@end - -@implementation NSString(PaystackSubstring) - -- (NSString *)pstck_safeSubstringToIndex:(NSUInteger)index { - return [self substringToIndex:MIN(self.length, index)]; -} - -- (NSString *)pstck_safeSubstringFromIndex:(NSUInteger)index { - return (index > self.length) ? @"" : [self substringFromIndex:index]; -} - -@end - -@implementation PSTCKPaymentCardTextFieldViewModel - -- (void)setCardNumber:(NSString *)cardNumber { - NSString *sanitizedNumber = [PSTCKCardValidator sanitizedNumericStringForString:cardNumber]; - PSTCKCardBrand brand = [PSTCKCardValidator brandForNumber:sanitizedNumber]; - NSInteger maxLength = [PSTCKCardValidator lengthForCardBrand:brand]; - _cardNumber = [sanitizedNumber pstck_safeSubstringToIndex:maxLength]; -} - -// This might contain slashes. -- (void)setRawExpiration:(NSString *)expiration { - NSString *sanitizedExpiration = [PSTCKCardValidator sanitizedNumericStringForString:expiration]; - self.expirationMonth = [sanitizedExpiration pstck_safeSubstringToIndex:2]; - self.expirationYear = [[sanitizedExpiration pstck_safeSubstringFromIndex:2] pstck_safeSubstringToIndex:2]; -} - -- (NSString *)rawExpiration { - NSMutableArray *array = [@[] mutableCopy]; - if (self.expirationMonth && ![self.expirationMonth isEqualToString:@""]) { - [array addObject:self.expirationMonth]; - } - - if ([PSTCKCardValidator validationStateForExpirationMonth:self.expirationMonth] == PSTCKCardValidationStateValid) { - [array addObject:self.expirationYear]; - } - return [array componentsJoinedByString:@"/"]; -} - -- (void)setExpirationMonth:(NSString *)expirationMonth { - NSString *sanitizedExpiration = [PSTCKCardValidator sanitizedNumericStringForString:expirationMonth]; - if (sanitizedExpiration.length == 1 && ![sanitizedExpiration isEqualToString:@"0"] && ![sanitizedExpiration isEqualToString:@"1"]) { - sanitizedExpiration = [@"0" stringByAppendingString:sanitizedExpiration]; - } - _expirationMonth = [sanitizedExpiration pstck_safeSubstringToIndex:2]; -} - -- (void)setExpirationYear:(NSString *)expirationYear { - _expirationYear = [[PSTCKCardValidator sanitizedNumericStringForString:expirationYear] pstck_safeSubstringToIndex:2]; -} - -- (void)setCvc:(NSString *)cvc { - NSInteger maxLength = [PSTCKCardValidator maxCVCLengthForCardBrand:self.brand]; - _cvc = [[PSTCKCardValidator sanitizedNumericStringForString:cvc] pstck_safeSubstringToIndex:maxLength]; -} - -- (PSTCKCardBrand)brand { - return [PSTCKCardValidator brandForNumber:self.cardNumber]; -} - -- (PSTCKCardValidationState)validationStateForField:(PSTCKCardFieldType)fieldType { - switch (fieldType) { - case PSTCKCardFieldTypeNumber: - return [PSTCKCardValidator validationStateForNumber:self.cardNumber validatingCardBrand:YES]; - break; - case PSTCKCardFieldTypeExpiration: { - PSTCKCardValidationState monthState = [PSTCKCardValidator validationStateForExpirationMonth:self.expirationMonth]; - PSTCKCardValidationState yearState = [PSTCKCardValidator validationStateForExpirationYear:self.expirationYear inMonth:self.expirationMonth]; - if (monthState == PSTCKCardValidationStateValid && yearState == PSTCKCardValidationStateValid) { - return PSTCKCardValidationStateValid; - } else if (monthState == PSTCKCardValidationStateInvalid || yearState == PSTCKCardValidationStateInvalid) { - return PSTCKCardValidationStateInvalid; - } else { - return PSTCKCardValidationStateIncomplete; - } - break; - } - case PSTCKCardFieldTypeCVC: - return [PSTCKCardValidator validationStateForCVC:self.cvc cardBrand:self.brand]; - } -} - -- (BOOL)isValid { - return ([self validationStateForField:PSTCKCardFieldTypeNumber] == PSTCKCardValidationStateValid && - [self validationStateForField:PSTCKCardFieldTypeExpiration] == PSTCKCardValidationStateValid && - [self validationStateForField:PSTCKCardFieldTypeCVC] == PSTCKCardValidationStateValid); -} - -- (NSString *)defaultPlaceholder { - return @"1234 5678 1234 5678 000"; -} - -- (NSString *)numberWithoutLastDigits { - NSUInteger length = [PSTCKCardValidator fragmentLengthForCardBrand:[PSTCKCardValidator brandForNumber:self.cardNumber]]; - NSUInteger toIndex = self.cardNumber.length - length; - - return (toIndex < self.cardNumber.length) ? - [self.cardNumber substringToIndex:toIndex] : - [self.defaultPlaceholder pstck_safeSubstringToIndex:[self defaultPlaceholder].length - length]; - -} - -@end diff --git a/Paystack/Classes/UI/UIImage+Paystack.m b/Paystack/Classes/UI/UIImage+Paystack.m deleted file mode 100644 index 08915a3..0000000 --- a/Paystack/Classes/UI/UIImage+Paystack.m +++ /dev/null @@ -1,96 +0,0 @@ -// -// UIImage+Paystack.m -// Paystack -// - -#import "UIImage+Paystack.h" - -#define FAUXPAS_IGNORED_IN_METHOD(...) - -// Dummy class for locating the framework bundle -@interface PSTCKBundleLocator : NSObject -@end -@implementation PSTCKBundleLocator -@end - -@implementation UIImage (Paystack) - -+ (UIImage *)pstck_amexCardImage { - return [UIImage pstck_brandImageForCardBrand:PSTCKCardBrandAmex]; -} - -+ (UIImage *)pstck_dinersClubCardImage { - return [UIImage pstck_brandImageForCardBrand:PSTCKCardBrandDinersClub]; -} - -+ (UIImage *)pstck_discoverCardImage { - return [UIImage pstck_brandImageForCardBrand:PSTCKCardBrandDiscover]; -} - -+ (UIImage *)pstck_jcbCardImage { - return [UIImage pstck_brandImageForCardBrand:PSTCKCardBrandJCB]; -} - -+ (UIImage *)pstck_masterCardCardImage { - return [UIImage pstck_brandImageForCardBrand:PSTCKCardBrandMasterCard]; -} - -+ (UIImage *)pstck_visaCardImage { - return [UIImage pstck_brandImageForCardBrand:PSTCKCardBrandVisa]; -} - -+ (UIImage *)pstck_unknownCardCardImage { - return [UIImage pstck_brandImageForCardBrand:PSTCKCardBrandUnknown]; -} - -+ (UIImage *)pstck_brandImageForCardBrand:(PSTCKCardBrand)brand { - FAUXPAS_IGNORED_IN_METHOD(APIAvailability); - NSString *imageName; - BOOL templateSupported = [[UIImage new] respondsToSelector:@selector(imageWithRenderingMode:)]; - switch (brand) { - case PSTCKCardBrandAmex: - imageName = @"pstck_card_amex"; - break; - case PSTCKCardBrandDinersClub: - imageName = @"pstck_card_diners"; - break; - case PSTCKCardBrandDiscover: - imageName = @"pstck_card_discover"; - break; - case PSTCKCardBrandJCB: - imageName = @"pstck_card_jcb"; - break; - case PSTCKCardBrandMasterCard: - imageName = @"pstck_card_mastercard"; - break; - case PSTCKCardBrandVerve: - imageName = @"pstck_card_verve"; - break; - case PSTCKCardBrandUnknown: - imageName = templateSupported ? @"pstck_card_placeholder_template" : @"pstck_card_placeholder"; - break; - case PSTCKCardBrandVisa: - imageName = @"pstck_card_visa"; - } - UIImage *image = [UIImage pstck_safeImageNamed:imageName]; - if (brand == PSTCKCardBrandUnknown && templateSupported) { - image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; - } - return image; -} - -+ (UIImage *)pstck_cvcImageForCardBrand:(PSTCKCardBrand)brand { - NSString *imageName = brand == PSTCKCardBrandAmex ? @"pstck_card_cvc_amex" : @"pstck_card_cvc"; - return [UIImage pstck_safeImageNamed:imageName]; -} - -+ (UIImage *)pstck_safeImageNamed:(NSString *)imageName { - if ([[UIImage class] respondsToSelector:@selector(imageNamed:inBundle:compatibleWithTraitCollection:)]) { - return [UIImage imageNamed:imageName inBundle:[NSBundle bundleForClass:[PSTCKBundleLocator class]] compatibleWithTraitCollection:nil]; - } - return [UIImage imageNamed:imageName]; -} - -@end - -void linkUIImageCategory(void){} diff --git a/Paystack/Classes/Validator/Card Validator/PSTCKCardValidator.m b/Paystack/Classes/Validator/Card Validator/PSTCKCardValidator.m deleted file mode 100644 index 3931741..0000000 --- a/Paystack/Classes/Validator/Card Validator/PSTCKCardValidator.m +++ /dev/null @@ -1,308 +0,0 @@ -// -// PSTCKCardValidator.m -// Paystack -// - -#import "PSTCKCardValidator.h" - -@implementation PSTCKCardValidator - -+ (NSString *)sanitizedNumericStringForString:(NSString *)string { - NSCharacterSet *set = [[NSCharacterSet decimalDigitCharacterSet] invertedSet]; - NSArray *components = [string componentsSeparatedByCharactersInSet:set]; - return [components componentsJoinedByString:@""] ?: @""; -} - -+ (NSString *)stringByRemovingSpacesFromString:(NSString *)string { - NSCharacterSet *set = [NSCharacterSet whitespaceCharacterSet]; - NSArray *components = [string componentsSeparatedByCharactersInSet:set]; - return [components componentsJoinedByString:@""]; -} - -+ (BOOL)stringIsNumeric:(NSString *)string { - return [[self sanitizedNumericStringForString:string] isEqualToString:string]; -} - -+ (PSTCKCardValidationState)validationStateForExpirationMonth:(NSString *)expirationMonth { - - NSString *sanitizedExpiration = [self stringByRemovingSpacesFromString:expirationMonth]; - - if (![self stringIsNumeric:sanitizedExpiration]) { - return PSTCKCardValidationStateInvalid; - } - - switch (sanitizedExpiration.length) { - case 0: - return PSTCKCardValidationStateIncomplete; - case 1: - return ([sanitizedExpiration isEqualToString:@"0"] || [sanitizedExpiration isEqualToString:@"1"]) ? PSTCKCardValidationStateIncomplete : PSTCKCardValidationStateValid; - case 2: - return (0 < sanitizedExpiration.integerValue && sanitizedExpiration.integerValue <= 12) ? PSTCKCardValidationStateValid : PSTCKCardValidationStateInvalid; - default: - return PSTCKCardValidationStateInvalid; - } -} - -+ (PSTCKCardValidationState)validationStateForExpirationYear:(NSString *)expirationYear inMonth:(NSString *)expirationMonth inCurrentYear:(NSInteger)currentYear currentMonth:(NSInteger)currentMonth { - - NSInteger moddedYear = currentYear % 100; - - if (![self stringIsNumeric:expirationMonth] || ![self stringIsNumeric:expirationYear]) { - return PSTCKCardValidationStateInvalid; - } - - NSString *sanitizedMonth = [self sanitizedNumericStringForString:expirationMonth]; - NSString *sanitizedYear = [self sanitizedNumericStringForString:expirationYear]; - - switch (sanitizedYear.length) { - case 0: - case 1: - return PSTCKCardValidationStateIncomplete; - case 2: { - if (sanitizedYear.integerValue == moddedYear) { - return sanitizedMonth.integerValue >= currentMonth ? PSTCKCardValidationStateValid : PSTCKCardValidationStateInvalid; - } else { - return sanitizedYear.integerValue > moddedYear ? PSTCKCardValidationStateValid : PSTCKCardValidationStateInvalid; - } - } - default: - return PSTCKCardValidationStateInvalid; - } -} - - -+ (PSTCKCardValidationState)validationStateForExpirationYear:(NSString *)expirationYear - inMonth:(NSString *)expirationMonth { - return [self validationStateForExpirationYear:expirationYear - inMonth:expirationMonth - inCurrentYear:[self currentYear] - currentMonth:[self currentMonth]]; -} - - -+ (PSTCKCardValidationState)validationStateForCVC:(NSString *)cvc cardBrand:(PSTCKCardBrand)brand { - - if (![self stringIsNumeric:cvc]) { - return PSTCKCardValidationStateInvalid; - } - - NSString *sanitizedCvc = [self sanitizedNumericStringForString:cvc]; - - NSUInteger minLength = [self minCVCLength]; - NSUInteger maxLength = [self maxCVCLengthForCardBrand:brand]; - if (sanitizedCvc.length < minLength) { - return PSTCKCardValidationStateIncomplete; - } - else if (sanitizedCvc.length > maxLength) { - return PSTCKCardValidationStateInvalid; - } - else { - return PSTCKCardValidationStateValid; - } -} - -+ (PSTCKCardValidationState)validationStateForNumber:(nonnull NSString *)cardNumber - validatingCardBrand:(BOOL)validatingCardBrand { - - NSString *sanitizedNumber = [self stringByRemovingSpacesFromString:cardNumber]; - if (![self stringIsNumeric:sanitizedNumber]) { - return PSTCKCardValidationStateInvalid; - } - - NSArray *brands = [self possibleBrandsForNumber:sanitizedNumber]; - if (brands.count == 0 && validatingCardBrand) { - return PSTCKCardValidationStateInvalid; - } else if (brands.count >= 2) { - return PSTCKCardValidationStateIncomplete; - } else { - PSTCKCardBrand brand = (PSTCKCardBrand)[brands.firstObject integerValue]; - NSUInteger desiredLength = [self lengthForCardBrand:brand]; - if (sanitizedNumber.length > desiredLength) { - return PSTCKCardValidationStateInvalid; - } else if (sanitizedNumber.length == desiredLength) { - return [self stringIsValidLuhn:sanitizedNumber] ? PSTCKCardValidationStateValid : PSTCKCardValidationStateInvalid; - } else if ((brand == PSTCKCardBrandVerve) && (sanitizedNumber.length <= 19) && (sanitizedNumber.length >= 16)) { - // A verve card is valid as long as it has 16-19 digits (no luhn check) - return PSTCKCardValidationStateValid; - } else { - return PSTCKCardValidationStateIncomplete; - } - } -} - -+ (PSTCKCardValidationState)validationStateForCard:(nonnull PSTCKCardParams *)card inCurrentYear:(NSInteger)currentYear currentMonth:(NSInteger)currentMonth { - PSTCKCardValidationState numberValidation = [self validationStateForNumber:card.number validatingCardBrand:YES]; - NSString *expMonthString = [NSString stringWithFormat:@"%02lu", (unsigned long)card.expMonth]; - PSTCKCardValidationState expMonthValidation = [self validationStateForExpirationMonth:expMonthString]; - NSString *expYearString = [NSString stringWithFormat:@"%02lu", (unsigned long)card.expYear%100]; - PSTCKCardValidationState expYearValidation = [self validationStateForExpirationYear:expYearString - inMonth:expMonthString - inCurrentYear:currentYear - currentMonth:currentMonth]; - PSTCKCardBrand brand = [self brandForNumber:card.number]; - PSTCKCardValidationState cvcValidation = [self validationStateForCVC:card.cvc cardBrand:brand]; - - NSArray *states = @[@(numberValidation), - @(expMonthValidation), - @(expYearValidation), - @(cvcValidation)]; - BOOL incomplete = NO; - for (NSNumber *boxedState in states) { - PSTCKCardValidationState state = [boxedState integerValue]; - if (state == PSTCKCardValidationStateInvalid) { - return state; - } - else if (state == PSTCKCardValidationStateIncomplete) { - incomplete = YES; - } - } - return incomplete ? PSTCKCardValidationStateIncomplete : PSTCKCardValidationStateValid; -} - -+ (PSTCKCardValidationState)validationStateForCard:(PSTCKCardParams *)card { - return [self validationStateForCard:card - inCurrentYear:[self currentYear] - currentMonth:[self currentMonth]]; -} - -+ (NSUInteger)minCVCLength { - return 3; -} - -+ (NSUInteger)maxCVCLengthForCardBrand:(PSTCKCardBrand)brand { - switch (brand) { - case PSTCKCardBrandAmex: - case PSTCKCardBrandUnknown: - return 4; - default: - return 3; - } -} - -+ (PSTCKCardBrand)brandForNumber:(NSString *)cardNumber { - NSString *sanitizedNumber = [self sanitizedNumericStringForString:cardNumber]; - NSArray *brands = [self possibleBrandsForNumber:sanitizedNumber]; - if (brands.count == 1) { - return (PSTCKCardBrand)[brands.firstObject integerValue]; - } - return PSTCKCardBrandUnknown; -} - -+ (NSArray *)possibleBrandsForNumber:(NSString *)cardNumber { - NSMutableArray *possibleBrands = [@[] mutableCopy]; - for (NSNumber *brandNumber in [self allValidBrands]) { - PSTCKCardBrand brand = (PSTCKCardBrand)brandNumber.integerValue; - if ([self prefixMatches:brand digits:cardNumber]) { - [possibleBrands addObject:@(brand)]; - } - } - return [possibleBrands copy]; -} - -+ (NSArray *)allValidBrands { - return @[ -// @(PSTCKCardBrandAmex), -// @(PSTCKCardBrandDinersClub), -// @(PSTCKCardBrandDiscover), -// @(PSTCKCardBrandJCB), - @(PSTCKCardBrandMasterCard), - @(PSTCKCardBrandVisa), - @(PSTCKCardBrandVerve), - ]; -} - -+ (NSUInteger)lengthForCardBrand:(PSTCKCardBrand)brand { - switch (brand) { - case PSTCKCardBrandAmex: - return 15; - case PSTCKCardBrandVerve: - case PSTCKCardBrandUnknown: - return 20; - case PSTCKCardBrandDinersClub: - return 14; - default: - return 16; - } -} - -+ (NSInteger)fragmentLengthForCardBrand:(PSTCKCardBrand)brand { - switch (brand) { - case PSTCKCardBrandAmex: - return 5; - case PSTCKCardBrandDinersClub: - return 2; - default: - return 4; - } -} - -+ (BOOL)prefixMatches:(PSTCKCardBrand)brand digits:(NSString *)digits { - if (digits.length == 0) { - return YES; - } - NSArray *digitPrefixes = [self validBeginningDigits:brand]; - for (NSString *digitPrefix in digitPrefixes) { - if ((digitPrefix.length >= digits.length && [digitPrefix hasPrefix:digits]) || - (digits.length >= digitPrefix.length && [digits hasPrefix:digitPrefix])) { - return YES; - } - } - return NO; -} - -+ (NSArray *)validBeginningDigits:(PSTCKCardBrand)brand { - switch (brand) { - case PSTCKCardBrandVerve: - return @[@"5060", @"5061", @"5078", @"5079", @"6500"]; - case PSTCKCardBrandAmex: - return @[@"34", @"37"]; - case PSTCKCardBrandDinersClub: - return @[@"30", @"36", @"38", @"39"]; - case PSTCKCardBrandDiscover: - return @[@"6011", @"622", @"64", @"65"]; - case PSTCKCardBrandJCB: - return @[@"35"]; - case PSTCKCardBrandMasterCard: - return @[@"501", @"502", @"503", @"504", @"505", - @"5062", @"5063", @"5064", @"5065", @"5066", @"5067", @"5068", @"5069", - @"5070", @"5071", @"5072", @"5073", @"5074", @"5075", @"5076", @"5077", - @"508", @"509", @"500", @"51", @"52", @"53", @"54", @"55", @"56", @"57", @"58", @"59"]; - case PSTCKCardBrandVisa: - return @[@"40", @"41", @"42", @"43", @"44", @"45", @"46", @"47", @"48", @"49"]; - case PSTCKCardBrandUnknown: - return @[]; - } -} - -+ (BOOL)stringIsValidLuhn:(NSString *)number { - BOOL odd = true; - int sum = 0; - NSMutableArray *digits = [NSMutableArray arrayWithCapacity:number.length]; - - for (int i = 0; i < (NSInteger)number.length; i++) { - [digits addObject:[number substringWithRange:NSMakeRange(i, 1)]]; - } - - for (NSString *digitStr in [digits reverseObjectEnumerator]) { - int digit = [digitStr intValue]; - if ((odd = !odd)) digit *= 2; - if (digit > 9) digit -= 9; - sum += digit; - } - - return sum % 10 == 0; -} - -+ (NSInteger)currentYear { - NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; - NSDateComponents *dateComponents = [calendar components:NSCalendarUnitYear fromDate:[NSDate date]]; - return dateComponents.year % 100; -} - -+ (NSInteger)currentMonth { - NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; - NSDateComponents *dateComponents = [calendar components:NSCalendarUnitMonth fromDate:[NSDate date]]; - return dateComponents.month; -} - -@end diff --git a/Paystack/Classes/Validator/Card Validator/PSTCKValidationParams.m b/Paystack/Classes/Validator/Card Validator/PSTCKValidationParams.m deleted file mode 100644 index 06103b6..0000000 --- a/Paystack/Classes/Validator/Card Validator/PSTCKValidationParams.m +++ /dev/null @@ -1,36 +0,0 @@ -// -// PSTCKCardParams.m -// Paystack -// - -#import "PSTCKValidationParams.h" - -@implementation PSTCKValidationParams - -@synthesize additionalAPIParameters = _additionalAPIParameters; - -- (instancetype)init { - self = [super init]; - if (self) { - _additionalAPIParameters = @{}; - } - return self; -} - - -#pragma mark - - -#pragma mark - PSTCKFormEncodable - -+ (NSString *)rootObjectName { - return @""; -} - -+ (NSDictionary *)propertyNamesToFormFieldNamesMapping { - return @{ - @"trans": @"trans", - @"token": @"token" - }; -} - -@end diff --git a/Paystack/Classes/Validator/Textfield Validator/RequiredRule.swift b/Paystack/Classes/Validator/Textfield Validator/RequiredRule.swift deleted file mode 100644 index dc87ce7..0000000 --- a/Paystack/Classes/Validator/Textfield Validator/RequiredRule.swift +++ /dev/null @@ -1,46 +0,0 @@ -// -// Required.swift -// pyur-ios -// -// Created by Jeff Potter on 12/22/14. -// Copyright (c) 2015 jpotts18. All rights reserved. -// - -import Foundation - -/** - `RequiredRule` is a subclass of Rule that defines how a required field is validated. - */ -open class RequiredRule: Rule { - /// String that holds error message. - private var message : String - - /** - Initializes `RequiredRule` object with error message. Used to validate a field that requires text. - - - parameter message: String of error message. - - returns: An initialized `RequiredRule` object, or nil if an object could not be created for some reason that would not result in an exception. - */ - public init(message : String = "This field is required"){ - self.message = message - } - - /** - Validates a field. - - - parameter value: String to check for validation. - - returns: Boolean value. True if validation is successful; False if validation fails. - */ - open func validate(_ value: String) -> Bool { - return !value.isEmpty - } - - /** - Used to display error message when validation fails. - - - returns: String of error message. - */ - open func errorMessage() -> String { - return message - } -} diff --git a/Paystack/Classes/Validator/Textfield Validator/Rule.swift b/Paystack/Classes/Validator/Textfield Validator/Rule.swift deleted file mode 100644 index 2f621c9..0000000 --- a/Paystack/Classes/Validator/Textfield Validator/Rule.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// Validation.swift -// -// Created by Jeff Potter on 11/11/14. -// Copyright (c) 2015 jpotts18. All rights reserved. -// - -import Foundation - -/** - The `Rule` protocol declares the required methods for all objects that subscribe to it. - */ -public protocol Rule { - /** - Validates text of a field. - - - parameter value: String of text to be validated. - - returns: Boolean value. True if validation is successful; False if validation fails. - */ - func validate(_ value: String) -> Bool - /** - Displays error message of a field that has failed validation. - - - returns: String of error message. - */ - func errorMessage() -> String -} diff --git a/Paystack/Classes/Validator/Textfield Validator/Validatable.swift b/Paystack/Classes/Validator/Textfield Validator/Validatable.swift deleted file mode 100644 index 4778f3a..0000000 --- a/Paystack/Classes/Validator/Textfield Validator/Validatable.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// Validatable.swift -// Validator -// -// Created by Deniz Adalar on 28/04/16. -// Copyright © 2016 jpotts18. All rights reserved. -// - -import Foundation - -public typealias ValidatableField = AnyObject & Validatable - -public protocol Validatable { - - var validationText: String { - get - } -} - -extension UITextField: Validatable { - - open var validationText: String { - return text ?? "" - } -} - -extension UITextView: Validatable { - - public var validationText: String { - return text ?? "" - } -} - diff --git a/Paystack/Classes/Validator/Textfield Validator/ValidationDelegate.swift b/Paystack/Classes/Validator/Textfield Validator/ValidationDelegate.swift deleted file mode 100644 index 0e073ed..0000000 --- a/Paystack/Classes/Validator/Textfield Validator/ValidationDelegate.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// ValidationDelegate.swift -// Validator -// -// Created by David Patterson on 1/2/16. -// Copyright © 2016 jpotts18. All rights reserved. -// - -import Foundation -import UIKit -/** - Protocol for `ValidationDelegate` adherents, which comes with two required methods that are called depending on whether validation succeeded or failed. - */ -public protocol ValidationDelegate { - /** - This method will be called on delegate object when validation is successful. - - - returns: No return value. - */ - func validationSuccessful() - /** - This method will be called on delegate object when validation fails. - - - returns: No return value. - */ - func validationFailed(_ errors: [(Validatable, ValidationError)]) -} diff --git a/Paystack/Classes/Validator/Textfield Validator/ValidationError.swift b/Paystack/Classes/Validator/Textfield Validator/ValidationError.swift deleted file mode 100644 index 98ab4de..0000000 --- a/Paystack/Classes/Validator/Textfield Validator/ValidationError.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// Created by Jeff Potter on 11/11/14. -// Copyright (c) 2015 jpotts18. All rights reserved. -// - -import Foundation -import UIKit - -/** - The `ValidationError` class is used for representing errors of a failed validation. It contains the field, error label, and error message of a failed validation. - */ -public class ValidationError: NSObject { - /// the Validatable field of the field - public let field:ValidatableField - /// the error label of the field - public var errorLabel:UILabel? - /// the error message of the field - public let errorMessage:String - - /** - Initializes `ValidationError` object with a field, errorLabel, and errorMessage. - - - parameter field: Validatable field that holds field. - - parameter errorLabel: UILabel that holds error label. - - parameter errorMessage: String that holds error message. - - returns: An initialized object, or nil if an object could not be created for some reason that would not result in an exception. - */ - public init(field:ValidatableField, errorLabel:UILabel?, error:String){ - self.field = field - self.errorLabel = errorLabel - self.errorMessage = error - } -} \ No newline at end of file diff --git a/Paystack/Classes/Validator/Textfield Validator/ValidationRule.swift b/Paystack/Classes/Validator/Textfield Validator/ValidationRule.swift deleted file mode 100644 index ef8d306..0000000 --- a/Paystack/Classes/Validator/Textfield Validator/ValidationRule.swift +++ /dev/null @@ -1,45 +0,0 @@ -// -// ValidationRule.swift -// -// Created by Jeff Potter on 11/11/14. -// Copyright (c) 2015 jpotts18. All rights reserved. -// - -import Foundation -import UIKit - -/** - `ValidationRule` is a class that creates an object which holds validation info of a field. - */ -public class ValidationRule { - /// the field of the field - public var field:ValidatableField - /// the errorLabel of the field - public var errorLabel:UILabel? - /// the rules of the field - public var rules:[Rule] = [] - - /** - Initializes `ValidationRule` instance with field, rules, and errorLabel. - - - parameter field: field that holds actual text in field. - - parameter errorLabel: label that holds error label of field. - - parameter rules: array of Rule objects, which field will be validated against. - - returns: An initialized `ValidationRule` object, or nil if an object could not be created for some reason that would not result in an exception. - */ - public init(field: ValidatableField, rules:[Rule], errorLabel:UILabel?){ - self.field = field - self.errorLabel = errorLabel - self.rules = rules - } - - /** - Used to validate field against its validation rules. - - returns: `ValidationError` object if at least one error is found. Nil is returned if there are no validation errors. - */ - public func validateField() -> ValidationError? { - return rules.filter{ - return !$0.validate(field.validationText) - }.map{ rule -> ValidationError in return ValidationError(field: self.field, errorLabel:self.errorLabel, error: rule.errorMessage()) }.first - } -} diff --git a/Paystack/Classes/Validator/Textfield Validator/Validator.swift b/Paystack/Classes/Validator/Textfield Validator/Validator.swift deleted file mode 100644 index 2553e47..0000000 --- a/Paystack/Classes/Validator/Textfield Validator/Validator.swift +++ /dev/null @@ -1,154 +0,0 @@ -// -// Validator.swift -// -// Created by Jeff Potter on 11/10/14. -// Copyright (c) 2015 jpotts18. All rights reserved. -// - -import Foundation -import UIKit - -/** - Class that makes `Validator` objects. Should be added as a parameter to ViewController that will display - validation fields. - */ -public class Validator { - /// Dictionary to hold all fields (and accompanying rules) that will undergo validation. - public var validations = ValidatorDictionary() - /// Dictionary to hold fields (and accompanying errors) that were unsuccessfully validated. - public var errors = ValidatorDictionary() - /// Dictionary to hold fields by their object identifiers - private var fields = ValidatorDictionary() - /// Variable that holds success closure to display positive status of field. - private var successStyleTransform:((_ validationRule:ValidationRule)->Void)? - /// Variable that holds error closure to display negative status of field. - private var errorStyleTransform:((_ validationError:ValidationError)->Void)? - /// - returns: An initialized object, or nil if an object could not be created for some reason that would not result in an exception. - public init(){} - - // MARK: Private functions - - /** - This method is used to validate all fields registered to Validator. If validation is unsuccessful, - field gets added to errors dictionary. - - - returns: No return value. - */ - private func validateAllFields() { - - errors = ValidatorDictionary() - - for (_, rule) in validations { - if let error = rule.validateField() { - errors[rule.field] = error - - // let the user transform the field if they want - if let transform = self.errorStyleTransform { - transform(error) - } - } else { - // No error - // let the user transform the field if they want - if let transform = self.successStyleTransform { - transform(rule) - } - } - } - } - - // MARK: Public functions - - /** - This method is used to validate a single field registered to Validator. If validation is unsuccessful, - field gets added to errors dictionary. - - - parameter field: Holds validator field data. - - returns: No return value. - */ - public func validateField(_ field: ValidatableField, callback: (_ error:ValidationError?) -> Void){ - if let fieldRule = validations[field] { - if let error = fieldRule.validateField() { - errors[field] = error - if let transform = self.errorStyleTransform { - transform(error) - } - callback(error) - } else { - if let transform = self.successStyleTransform { - transform(fieldRule) - } - callback(nil) - } - } else { - callback(nil) - } - } - - // MARK: Using Keys - - /** - This method is used to style fields that have undergone validation checks. Success callback should be used to show common success styling and error callback should be used to show common error styling. - - - parameter success: A closure which is called with validationRule, an object that holds validation data - - parameter error: A closure which is called with validationError, an object that holds validation error data - - returns: No return value - */ - public func styleTransformers(success:((_ validationRule:ValidationRule)->Void)?, error:((_ validationError:ValidationError)->Void)?) { - self.successStyleTransform = success - self.errorStyleTransform = error - } - - /** - This method is used to add a field to validator. - - - parameter field: field that is to be validated. - - parameter errorLabel: A UILabel that holds error label data - - parameter rules: A Rule array that holds different rules that apply to said field. - - returns: No return value - */ - public func registerField(_ field: ValidatableField, errorLabel:UILabel? = nil, rules:[Rule]) { - validations[field] = ValidationRule(field: field, rules:rules, errorLabel:errorLabel) - fields[field] = field - } - - /** - This method is for removing a field validator. - - - parameter field: field used to locate and remove field from validator. - - returns: No return value - */ - public func unregisterField(_ field:ValidatableField) { - validations.removeValueForKey(field) - errors.removeValueForKey(field) - } - - /** - This method checks to see if all fields in validator are valid. - - - returns: No return value. - */ - public func validate(_ delegate:ValidationDelegate) { - - self.validateAllFields() - - if errors.isEmpty { - delegate.validationSuccessful() - } else { - delegate.validationFailed(errors.map { (fields[$1.field]!, $1) }) - } - - } - - /** - This method validates all fields in validator and sets any errors to errors parameter of callback. - - - parameter callback: A closure which is called with errors, a dictionary of type Validatable:ValidationError. - - returns: No return value. - */ - public func validate(_ callback:(_ errors:[(Validatable, ValidationError)])->Void) -> Void { - - self.validateAllFields() - - callback(errors.map { (fields[$1.field]!, $1) } ) - } -} diff --git a/Paystack/Classes/Validator/Textfield Validator/ValidatorDictionary.swift b/Paystack/Classes/Validator/Textfield Validator/ValidatorDictionary.swift deleted file mode 100644 index e152b0f..0000000 --- a/Paystack/Classes/Validator/Textfield Validator/ValidatorDictionary.swift +++ /dev/null @@ -1,45 +0,0 @@ -// -// ValidatorDictionary.swift -// Validator -// -// Created by Deniz Adalar on 04/05/16. -// Copyright © 2016 jpotts18. All rights reserved. -// - -import Foundation - -public struct ValidatorDictionary : Sequence { - - private var innerDictionary: [ObjectIdentifier: T] = [:]; - - public subscript(key: ValidatableField?) -> T? { - get { - if let key = key { - return innerDictionary[ObjectIdentifier(key)]; - } else { - return nil; - } - } - set(newValue) { - if let key = key { - innerDictionary[ObjectIdentifier(key)] = newValue; - } - } - } - - public mutating func removeAll() { - innerDictionary.removeAll() - } - - public mutating func removeValueForKey(_ key: ValidatableField) { - innerDictionary.removeValue(forKey: ObjectIdentifier(key)) - } - - public var isEmpty: Bool { - return innerDictionary.isEmpty - } - - public func makeIterator() -> DictionaryIterator { - return innerDictionary.makeIterator() - } -} diff --git a/Paystack/Fabric/FABKitProtocol.h b/Paystack/Fabric/FABKitProtocol.h deleted file mode 100644 index 53e0656..0000000 --- a/Paystack/Fabric/FABKitProtocol.h +++ /dev/null @@ -1,46 +0,0 @@ -// -// FABKitProtocol.h -// -// Copyright (c) 2015 Twitter. All rights reserved. -// - -#import - -/** - * Protocol that a class in a Fabric Kit must conform to to provide information to Fabric at runtime. - */ -@protocol FABKit - -@required - -/** - * Required. The globally unique identifier of the Kit. - * We encourage the use of reverse-DNS notation. - * Example: @"io.fabric.sdk.ios" - */ -+ (NSString *)bundleIdentifier; - -/** - * Required. Must return the current version of the Kit that is being used at runtime. - * We encourage the use of semantic versioning (http://semver.org/), without prefixing the version with a "v". - * This is commonly referred to as the "marketing version". - * Example: @"1.2.3" - */ -+ (NSString *)kitDisplayVersion; - -@optional - -/** - * The build version of the kit. Should be monotonically increasing and unique. - * Example: 137 - */ -+ (NSString *)kitBuildVersion; - -/** - * Perform any necessary initialization. - * This method will be invoked on the Kit when the user calls +[Fabric initializeKits]. - * @note This method being called does not necessarily imply that the developer has started using the Kit yet. - */ -+ (void)initializeIfNeeded; - -@end diff --git a/Paystack/Fabric/Fabric+FABKits.h b/Paystack/Fabric/Fabric+FABKits.h deleted file mode 100644 index 927e4da..0000000 --- a/Paystack/Fabric/Fabric+FABKits.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// Fabric+FABKits.h -// -// Copyright (c) 2015 Twitter. All rights reserved. -// - -#import "Fabric.h" - -@protocol FABKit; -// Use this category for methods that kits can call on Fabric. -@interface Fabric (FABKits) - -/** - * Returns a dictionary containing the kit configuration info for the provided kit. - * The configuration information is parsed from the application's Info.plist. This - * method is primarily intended to be used by kits to retrieve their configuration. - * - * @param kitClass The class of the kit whose configuration should be returned. - * It should conform to the FABKit protocol. - * - * @return A dictionary containing kit specific configuration information or nil if none exists. - */ -+ (nonnull NSDictionary *)configurationDictionaryForKitClass:(nonnull Class)kitClass; - -@end diff --git a/Paystack/Fabric/Fabric.h b/Paystack/Fabric/Fabric.h deleted file mode 100644 index 760aa76..0000000 --- a/Paystack/Fabric/Fabric.h +++ /dev/null @@ -1,53 +0,0 @@ -// -// Fabric.h -// -// Copyright (c) 2015 Twitter. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -/** - * Fabric Base. Coordinates configuration and starts all provided kits. - */ -@interface Fabric : NSObject - -/** - * Initialize Fabric and all provided kits. Call this method within your App Delegate's `application:didFinishLaunchingWithOptions:` and provide the kits you wish to use. - * - * For example, in Objective-C: - * - * `[Fabric with:@[[Crashlytics class], [Twitter class], [Digits class], [MoPub class]]];` - * - * Swift: - * - * `Fabric.with([Crashlytics.self(), Twitter.self(), Digits.self(), MoPub.self()])` - * - * Only the first call to this method is honored. Subsequent calls are no-ops. - * - * @param kits An array of kit Class objects - * - * @return Returns the shared Fabric instance. In most cases this can be ignored. - */ -+ (instancetype)with:(NSArray *)kitClasses; - -/** - * Returns the Fabric singleton object. - */ -+ (instancetype)sharedSDK; - -/** - * This BOOL enables or disables debug logging, such as kit version information. The default value is NO. - */ -@property (nonatomic, assign) BOOL debug; - -/** - * Unavailable. Use `+sharedSDK` to retrieve the shared Fabric instance. - */ -- (id)init __attribute__((unavailable("Use +sharedSDK to retrieve the shared Fabric instance."))); - -@end - -NS_ASSUME_NONNULL_END - From 65bff3e351ec93eef7fc59750b36a9af5d306681 Mon Sep 17 00:00:00 2001 From: Jubril Olambiwonnu Date: Fri, 7 Aug 2020 11:35:55 +0100 Subject: [PATCH 10/30] Remove fabric files --- Paystack.xcodeproj/project.pbxproj | 15 ------------ Paystack/PSTCKAPIClient.m | 39 ------------------------------ 2 files changed, 54 deletions(-) diff --git a/Paystack.xcodeproj/project.pbxproj b/Paystack.xcodeproj/project.pbxproj index 94e5ff1..12da4f5 100644 --- a/Paystack.xcodeproj/project.pbxproj +++ b/Paystack.xcodeproj/project.pbxproj @@ -396,9 +396,6 @@ 049952CE1BCF13510088C703 /* PSTCKAPIPostRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKAPIPostRequest.m; sourceTree = ""; }; 049952D11BCF13DD0088C703 /* PSTCKAPIClient+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "PSTCKAPIClient+Private.h"; sourceTree = ""; }; 049E84AB1A605D93000B66CD /* libPaystack.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPaystack.a; sourceTree = BUILT_PRODUCTS_DIR; }; - 04A58A461BC603BB004E7BC2 /* FABKitProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FABKitProtocol.h; sourceTree = ""; }; - 04A58A471BC603BB004E7BC2 /* Fabric+FABKits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Fabric+FABKits.h"; sourceTree = ""; }; - 04A58A481BC603BB004E7BC2 /* Fabric.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Fabric.h; sourceTree = ""; }; 04B33F301BC7417B00DD8120 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 04B94BC71A47B78A00092C46 /* AddressBook.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AddressBook.framework; path = System/Library/Frameworks/AddressBook.framework; sourceTree = SDKROOT; }; 04CDB4421A5F2E1800B854EE /* Paystack.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Paystack.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -602,17 +599,6 @@ path = Images; sourceTree = ""; }; - 04A58A451BC603BB004E7BC2 /* Fabric */ = { - isa = PBXGroup; - children = ( - 04A58A461BC603BB004E7BC2 /* FABKitProtocol.h */, - 04A58A471BC603BB004E7BC2 /* Fabric+FABKits.h */, - 04A58A481BC603BB004E7BC2 /* Fabric.h */, - ); - name = Fabric; - path = Paystack/Fabric; - sourceTree = ""; - }; 04B33F2F1BC7414C00DD8120 /* Supporting Files */ = { isa = PBXGroup; children = ( @@ -745,7 +731,6 @@ 11C74B9A164043050071C2CA /* Frameworks */ = { isa = PBXGroup; children = ( - 04A58A451BC603BB004E7BC2 /* Fabric */, 04365D2C1A4CF86C00A3E1D4 /* CoreGraphics.framework */, 04B94BC71A47B78A00092C46 /* AddressBook.framework */, 04D5BF9019BF958F009521A5 /* PassKit.framework */, diff --git a/Paystack/PSTCKAPIClient.m b/Paystack/PSTCKAPIClient.m index ce8958a..b387c0a 100644 --- a/Paystack/PSTCKAPIClient.m +++ b/Paystack/PSTCKAPIClient.m @@ -24,11 +24,6 @@ #import "PSTCKAPIPostRequest.h" #import -#if __has_include("Fabric.h") -#import "Fabric+FABKits.h" -#import "FABKitProtocol.h" -#endif - #ifdef PSTCK_STATIC_LIBRARY_BUILD #import "PSTCKCategoryLoader.h" #endif @@ -61,11 +56,7 @@ + (NSString *)defaultPublicKey { @end -#if __has_include("Fabric.h") -@interface PSTCKAPIClient () -#else @interface PSTCKAPIClient() -#endif @property (nonatomic, readwrite) NSURL *apiURL; @property (nonatomic, readwrite) NSURLSession *urlSession; @end @@ -202,36 +193,6 @@ + (NSString *)paystackUserAgentDetails { return [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:[details copy] options:0 error:NULL] encoding:NSUTF8StringEncoding]; } -#pragma mark Fabric -#if __has_include("Fabric.h") - -+ (NSString *)bundleIdentifier { - return @"com.paystack.paystack-ios"; -} - -+ (NSString *)kitDisplayVersion { - return PSTCKSDKVersion; -} - -+ (void)initializeIfNeeded { - Class fabric = NSClassFromString(@"Fabric"); - if (fabric) { - // The app must be using Fabric, as it exists at runtime. We fetch our default public key from Fabric. - NSDictionary *fabricConfiguration = [fabric configurationDictionaryForKitClass:[PSTCKAPIClient class]]; - NSString *publicKey = fabricConfiguration[@"public"]; - if (!publicKey) { - NSLog(@"Configuration dictionary returned by Fabric was nil, or doesn't have publicKey. Can't initialize Paystack."); - return; - } - [self validateKey:publicKey]; - [Paystack setDefaultPublicKey:publicKey]; - } else { - NSCAssert(fabric, @"initializeIfNeeded method called from a project that doesn't have Fabric."); - } -} - -#endif - @end typedef NS_ENUM(NSInteger, PSTCKChargeStage) { From f710198343a07e31dacbaadec696b52ba53ba923 Mon Sep 17 00:00:00 2001 From: Jubril Olambiwonnu Date: Fri, 7 Aug 2020 11:45:20 +0100 Subject: [PATCH 11/30] Bump version version --- Paystack.podspec | 2 +- Paystack.xcodeproj/project.pbxproj | 4 ++-- Paystack/PublicHeaders/PSTCKAPIClient.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Paystack.podspec b/Paystack.podspec index e44a2be..ea09e45 100644 --- a/Paystack.podspec +++ b/Paystack.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'Paystack' - s.version = '3.0.14' + s.version = '3.0.15' s.summary = 'Paystack is a web-based API helping African Businesses accept payments online.' s.description = <<-DESC Paystack makes it easy for African Businesses to accept Mastercard, Visa and Verve cards from anyone, anywhere in the world. diff --git a/Paystack.xcodeproj/project.pbxproj b/Paystack.xcodeproj/project.pbxproj index 12da4f5..5dbc9da 100644 --- a/Paystack.xcodeproj/project.pbxproj +++ b/Paystack.xcodeproj/project.pbxproj @@ -1383,7 +1383,7 @@ DEFINES_MODULE = YES; IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MARKETING_VERSION = 3.0.14; + MARKETING_VERSION = 3.0.15; PRODUCT_BUNDLE_IDENTIFIER = "com.paystack.paystack-ios"; SWIFT_OBJC_BRIDGING_HEADER = ""; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -1402,7 +1402,7 @@ DEFINES_MODULE = YES; IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MARKETING_VERSION = 3.0.14; + MARKETING_VERSION = 3.0.15; PRODUCT_BUNDLE_IDENTIFIER = "com.paystack.paystack-ios"; SWIFT_OBJC_BRIDGING_HEADER = ""; SWIFT_VERSION = 5.0; diff --git a/Paystack/PublicHeaders/PSTCKAPIClient.h b/Paystack/PublicHeaders/PSTCKAPIClient.h index 6757eb3..50edefd 100644 --- a/Paystack/PublicHeaders/PSTCKAPIClient.h +++ b/Paystack/PublicHeaders/PSTCKAPIClient.h @@ -8,7 +8,7 @@ #import #endif -static NSString *const __nonnull PSTCKSDKVersion = @"3.0.14"; +static NSString *const __nonnull PSTCKSDKVersion = @"3.0.15"; static NSString *const __nonnull PSTCKSDKBuild = @"18"; @class PSTCKCard, PSTCKCardParams, PSTCKTransactionParams, PSTCKToken, PSTCKState; From c97ad4451f38c8f10b4920dd77edf9f55ceb6584 Mon Sep 17 00:00:00 2001 From: Ravisankar Date: Wed, 24 Mar 2021 12:03:03 +0530 Subject: [PATCH 12/30] Added primary workflow to run swiftlint and tests --- .github/workflows/primary.yml | 35 +++ Gemfile | 3 + Gemfile.lock | 202 ++++++++++++++++++ .../xcschemes/PaystackiOS Tests.xcscheme | 41 ++-- .../xcschemes/PaystackiOS.xcscheme | 4 - 5 files changed, 259 insertions(+), 26 deletions(-) create mode 100644 .github/workflows/primary.yml create mode 100644 Gemfile create mode 100644 Gemfile.lock diff --git a/.github/workflows/primary.yml b/.github/workflows/primary.yml new file mode 100644 index 0000000..691c6b0 --- /dev/null +++ b/.github/workflows/primary.yml @@ -0,0 +1,35 @@ +name: Primary +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + SwiftLint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: GitHub Action for SwiftLint + uses: norio-nomura/action-swiftlint@3.2.1 + + UnitTests: + name: Unit Tests + runs-on: macos-latest + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup environment + run: | + bundle install + + - name: Select Xcode Version + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: latest-stable + + - name: Run tests + run: | + bundle exec fastlane unit_tests diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..7a118b4 --- /dev/null +++ b/Gemfile @@ -0,0 +1,3 @@ +source "https://rubygems.org" + +gem "fastlane" diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..2f4181a --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,202 @@ +GEM + remote: https://rubygems.org/ + specs: + CFPropertyList (3.0.3) + addressable (2.7.0) + public_suffix (>= 2.0.2, < 5.0) + artifactory (3.0.15) + atomos (0.1.3) + aws-eventstream (1.1.1) + aws-partitions (1.434.0) + aws-sdk-core (3.113.0) + aws-eventstream (~> 1, >= 1.0.2) + aws-partitions (~> 1, >= 1.239.0) + aws-sigv4 (~> 1.1) + jmespath (~> 1.0) + aws-sdk-kms (1.43.0) + aws-sdk-core (~> 3, >= 3.112.0) + aws-sigv4 (~> 1.1) + aws-sdk-s3 (1.92.0) + aws-sdk-core (~> 3, >= 3.112.0) + aws-sdk-kms (~> 1) + aws-sigv4 (~> 1.1) + aws-sigv4 (1.2.3) + aws-eventstream (~> 1, >= 1.0.2) + babosa (1.0.4) + claide (1.0.3) + colored (1.2) + colored2 (3.1.2) + commander-fastlane (4.4.6) + highline (~> 1.7.2) + declarative (0.0.20) + declarative-option (0.1.0) + digest-crc (0.6.3) + rake (>= 12.0.0, < 14.0.0) + domain_name (0.5.20190701) + unf (>= 0.0.5, < 1.0.0) + dotenv (2.7.6) + emoji_regex (3.2.2) + excon (0.79.0) + faraday (1.3.0) + faraday-net_http (~> 1.0) + multipart-post (>= 1.2, < 3) + ruby2_keywords + faraday-cookie_jar (0.0.7) + faraday (>= 0.8.0) + http-cookie (~> 1.0.0) + faraday-net_http (1.0.1) + faraday_middleware (1.0.0) + faraday (~> 1.0) + fastimage (2.2.3) + fastlane (2.178.0) + CFPropertyList (>= 2.3, < 4.0.0) + addressable (>= 2.3, < 3.0.0) + artifactory (~> 3.0) + aws-sdk-s3 (~> 1.0) + babosa (>= 1.0.3, < 2.0.0) + bundler (>= 1.12.0, < 3.0.0) + colored + commander-fastlane (>= 4.4.6, < 5.0.0) + dotenv (>= 2.1.1, < 3.0.0) + emoji_regex (>= 0.1, < 4.0) + excon (>= 0.71.0, < 1.0.0) + faraday (~> 1.0) + faraday-cookie_jar (~> 0.0.6) + faraday_middleware (~> 1.0) + fastimage (>= 2.1.0, < 3.0.0) + gh_inspector (>= 1.1.2, < 2.0.0) + google-api-client (>= 0.37.0, < 0.39.0) + google-cloud-storage (>= 1.15.0, < 2.0.0) + highline (>= 1.7.2, < 2.0.0) + json (< 3.0.0) + jwt (>= 2.1.0, < 3) + mini_magick (>= 4.9.4, < 5.0.0) + multipart-post (~> 2.0.0) + naturally (~> 2.2) + plist (>= 3.1.0, < 4.0.0) + rubyzip (>= 2.0.0, < 3.0.0) + security (= 0.1.3) + simctl (~> 1.6.3) + slack-notifier (>= 2.0.0, < 3.0.0) + terminal-notifier (>= 2.0.0, < 3.0.0) + terminal-table (>= 1.4.5, < 2.0.0) + tty-screen (>= 0.6.3, < 1.0.0) + tty-spinner (>= 0.8.0, < 1.0.0) + word_wrap (~> 1.0.0) + xcodeproj (>= 1.13.0, < 2.0.0) + xcpretty (~> 0.3.0) + xcpretty-travis-formatter (>= 0.0.3) + gh_inspector (1.1.3) + google-api-client (0.38.0) + addressable (~> 2.5, >= 2.5.1) + googleauth (~> 0.9) + httpclient (>= 2.8.1, < 3.0) + mini_mime (~> 1.0) + representable (~> 3.0) + retriable (>= 2.0, < 4.0) + signet (~> 0.12) + google-apis-core (0.3.0) + addressable (~> 2.5, >= 2.5.1) + googleauth (~> 0.14) + httpclient (>= 2.8.1, < 3.0) + mini_mime (~> 1.0) + representable (~> 3.0) + retriable (>= 2.0, < 4.0) + rexml + signet (~> 0.14) + webrick + google-apis-iamcredentials_v1 (0.2.0) + google-apis-core (~> 0.1) + google-apis-storage_v1 (0.3.0) + google-apis-core (~> 0.1) + google-cloud-core (1.6.0) + google-cloud-env (~> 1.0) + google-cloud-errors (~> 1.0) + google-cloud-env (1.5.0) + faraday (>= 0.17.3, < 2.0) + google-cloud-errors (1.1.0) + google-cloud-storage (1.31.0) + addressable (~> 2.5) + digest-crc (~> 0.4) + google-apis-iamcredentials_v1 (~> 0.1) + google-apis-storage_v1 (~> 0.1) + google-cloud-core (~> 1.2) + googleauth (~> 0.9) + mini_mime (~> 1.0) + googleauth (0.16.0) + faraday (>= 0.17.3, < 2.0) + jwt (>= 1.4, < 3.0) + memoist (~> 0.16) + multi_json (~> 1.11) + os (>= 0.9, < 2.0) + signet (~> 0.14) + highline (1.7.10) + http-cookie (1.0.3) + domain_name (~> 0.5) + httpclient (2.8.3) + jmespath (1.4.0) + json (2.5.1) + jwt (2.2.2) + memoist (0.16.2) + mini_magick (4.11.0) + mini_mime (1.0.2) + multi_json (1.15.0) + multipart-post (2.0.0) + nanaimo (0.3.0) + naturally (2.2.1) + os (1.1.1) + plist (3.6.0) + public_suffix (4.0.6) + rake (13.0.3) + representable (3.0.4) + declarative (< 0.1.0) + declarative-option (< 0.2.0) + uber (< 0.2.0) + retriable (3.1.2) + rexml (3.2.4) + rouge (2.0.7) + ruby2_keywords (0.0.4) + rubyzip (2.3.0) + security (0.1.3) + signet (0.15.0) + addressable (~> 2.3) + faraday (>= 0.17.3, < 2.0) + jwt (>= 1.5, < 3.0) + multi_json (~> 1.10) + simctl (1.6.8) + CFPropertyList + naturally + slack-notifier (2.3.2) + terminal-notifier (2.0.0) + terminal-table (1.8.0) + unicode-display_width (~> 1.1, >= 1.1.1) + tty-cursor (0.7.1) + tty-screen (0.8.1) + tty-spinner (0.9.3) + tty-cursor (~> 0.7) + uber (0.1.0) + unf (0.1.4) + unf_ext + unf_ext (0.0.7.7) + unicode-display_width (1.7.0) + webrick (1.7.0) + word_wrap (1.0.0) + xcodeproj (1.19.0) + CFPropertyList (>= 2.3.3, < 4.0) + atomos (~> 0.1.3) + claide (>= 1.0.2, < 2.0) + colored2 (~> 3.1) + nanaimo (~> 0.3.0) + xcpretty (0.3.0) + rouge (~> 2.0.7) + xcpretty-travis-formatter (1.0.1) + xcpretty (~> 0.2, >= 0.0.7) + +PLATFORMS + ruby + +DEPENDENCIES + fastlane + +BUNDLED WITH + 2.1.2 diff --git a/Paystack.xcodeproj/xcshareddata/xcschemes/PaystackiOS Tests.xcscheme b/Paystack.xcodeproj/xcshareddata/xcschemes/PaystackiOS Tests.xcscheme index 4ba0e0f..bc13a78 100644 --- a/Paystack.xcodeproj/xcshareddata/xcschemes/PaystackiOS Tests.xcscheme +++ b/Paystack.xcodeproj/xcshareddata/xcschemes/PaystackiOS Tests.xcscheme @@ -27,6 +27,15 @@ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> + + + + @@ -39,17 +48,6 @@ - - - - - - + + + + - - - - - - diff --git a/Paystack.xcodeproj/xcshareddata/xcschemes/PaystackiOS.xcscheme b/Paystack.xcodeproj/xcshareddata/xcschemes/PaystackiOS.xcscheme index 6399bfb..6e56999 100644 --- a/Paystack.xcodeproj/xcshareddata/xcschemes/PaystackiOS.xcscheme +++ b/Paystack.xcodeproj/xcshareddata/xcschemes/PaystackiOS.xcscheme @@ -29,8 +29,6 @@ shouldUseLaunchSchemeArgsEnv = "YES"> - - - - Date: Fri, 26 Mar 2021 17:39:17 +0530 Subject: [PATCH 13/30] Removed tests for deprecated classes and fixed failing tests --- Paystack.xcodeproj/project.pbxproj | 28 ----- Tests/Tests/PSTCKCardFunctionalTest.m | 94 --------------- Tests/Tests/PSTCKCardTest.m | 106 ----------------- Tests/Tests/PSTCKCardValidatorTest.m | 27 ++--- Tests/Tests/PSTCKCertTest.m | 73 ------------ Tests/Tests/PSTCKFormEncoderTest.m | 109 ------------------ Tests/Tests/PSTCKPaymentCardTextFieldTest.m | 29 ----- .../PSTCKPaymentCardTextFieldViewModelTest.m | 6 +- Tests/Tests/PSTCKRSATest.m | 43 ------- fastlane/Appfile | 6 + fastlane/Fastfile | 32 +++++ fastlane/README.md | 34 ++++++ 12 files changed, 82 insertions(+), 505 deletions(-) delete mode 100644 Tests/Tests/PSTCKCardFunctionalTest.m delete mode 100644 Tests/Tests/PSTCKCardTest.m delete mode 100644 Tests/Tests/PSTCKCertTest.m delete mode 100644 Tests/Tests/PSTCKFormEncoderTest.m delete mode 100644 Tests/Tests/PSTCKRSATest.m create mode 100644 fastlane/Appfile create mode 100644 fastlane/Fastfile create mode 100644 fastlane/README.md diff --git a/Paystack.xcodeproj/project.pbxproj b/Paystack.xcodeproj/project.pbxproj index 5dbc9da..8bec7cb 100644 --- a/Paystack.xcodeproj/project.pbxproj +++ b/Paystack.xcodeproj/project.pbxproj @@ -115,10 +115,6 @@ 04415C651A6605B5001225ED /* PSTCKToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB4CD1A5F30A700B854EE /* PSTCKToken.m */; }; 04415C661A6605B5001225ED /* PaystackError.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB4CF1A5F30A700B854EE /* PaystackError.m */; }; 04415C671A6605B5001225ED /* PSTCKAPIClientTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB51E1A5F3A9300B854EE /* PSTCKAPIClientTest.m */; }; - 04415C681A6605B5001225ED /* PSTCKFormEncoderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB51F1A5F3A9300B854EE /* PSTCKFormEncoderTest.m */; }; - 04415C6D1A6605B5001225ED /* PSTCKCardFunctionalTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB5241A5F3A9300B854EE /* PSTCKCardFunctionalTest.m */; }; - 04415C6E1A6605B5001225ED /* PSTCKCardTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB5251A5F3A9300B854EE /* PSTCKCardTest.m */; }; - 04415C6F1A6605B5001225ED /* PSTCKCertTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB5261A5F3A9300B854EE /* PSTCKCertTest.m */; }; 04415C701A6605B5001225ED /* PSTCKTokenTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB5271A5F3A9300B854EE /* PSTCKTokenTest.m */; }; 04415C721A6605D9001225ED /* Paystack.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4A91A5F30A700B854EE /* Paystack.h */; settings = {ATTRIBUTES = (Public, ); }; }; 04415C7F1A6605D9001225ED /* PSTCKAPIClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4C21A5F30A700B854EE /* PSTCKAPIClient.h */; }; @@ -212,10 +208,6 @@ 04D12C3E1A5F55D10010446E /* PSTCKToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4CC1A5F30A700B854EE /* PSTCKToken.h */; settings = {ATTRIBUTES = (Public, ); }; }; 04D12C3F1A5F55D10010446E /* PaystackError.h in Headers */ = {isa = PBXBuildFile; fileRef = 04CDB4CE1A5F30A700B854EE /* PaystackError.h */; settings = {ATTRIBUTES = (Public, ); }; }; 04D12C401A5F55FA0010446E /* PSTCKAPIClientTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB51E1A5F3A9300B854EE /* PSTCKAPIClientTest.m */; }; - 04D12C411A5F55FA0010446E /* PSTCKFormEncoderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB51F1A5F3A9300B854EE /* PSTCKFormEncoderTest.m */; }; - 04D12C451A5F55FA0010446E /* PSTCKCardFunctionalTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB5241A5F3A9300B854EE /* PSTCKCardFunctionalTest.m */; }; - 04D12C461A5F55FA0010446E /* PSTCKCardTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB5251A5F3A9300B854EE /* PSTCKCardTest.m */; }; - 04D12C471A5F55FA0010446E /* PSTCKCertTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB5261A5F3A9300B854EE /* PSTCKCertTest.m */; }; 04D12C481A5F55FA0010446E /* PSTCKTokenTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 04CDB5271A5F3A9300B854EE /* PSTCKTokenTest.m */; }; 04E32A9B1B7A93FC009C9E35 /* PSTCKCardValidator.m in Sources */ = {isa = PBXBuildFile; fileRef = 0438EF3F1B74170D00D506CC /* PSTCKCardValidator.m */; }; 04E32A9D1B7A9490009C9E35 /* PSTCKPaymentCardTextField.h in Headers */ = {isa = PBXBuildFile; fileRef = 04E32A9C1B7A9490009C9E35 /* PSTCKPaymentCardTextField.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -267,7 +259,6 @@ 08C260B31E9C214A002AE28C /* PSTCKAuthViewController.m in Copy Files */ = {isa = PBXBuildFile; fileRef = 08C260A81E9C214A002AE28C /* PSTCKAuthViewController.m */; }; 101987811CA47DAC0089A68A /* PSTCKRSA.m in Sources */ = {isa = PBXBuildFile; fileRef = 10500AB11C8135D800EEF7CF /* PSTCKRSA.m */; }; 10500AB21C8135D800EEF7CF /* PSTCKRSA.m in Sources */ = {isa = PBXBuildFile; fileRef = 10500AB11C8135D800EEF7CF /* PSTCKRSA.m */; }; - 10500AB51C8136EF00EEF7CF /* PSTCKRSATest.m in Sources */ = {isa = PBXBuildFile; fileRef = 10500AB41C8136EF00EEF7CF /* PSTCKRSATest.m */; }; 10500ABB1C82155B00EEF7CF /* PSTCKRSA.h in Headers */ = {isa = PBXBuildFile; fileRef = 10500AB31C8135F800EEF7CF /* PSTCKRSA.h */; settings = {ATTRIBUTES = (Public, ); }; }; 10500ABC1C82157A00EEF7CF /* PSTCKRSA.m in Sources */ = {isa = PBXBuildFile; fileRef = 10500AB11C8135D800EEF7CF /* PSTCKRSA.m */; }; 10500ABD1C8215F400EEF7CF /* PSTCKRSA.h in Headers */ = {isa = PBXBuildFile; fileRef = 10500AB31C8135F800EEF7CF /* PSTCKRSA.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -411,10 +402,6 @@ 04CDB4CE1A5F30A700B854EE /* PaystackError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PaystackError.h; path = PublicHeaders/PaystackError.h; sourceTree = ""; }; 04CDB4CF1A5F30A700B854EE /* PaystackError.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PaystackError.m; sourceTree = ""; }; 04CDB51E1A5F3A9300B854EE /* PSTCKAPIClientTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKAPIClientTest.m; sourceTree = ""; }; - 04CDB51F1A5F3A9300B854EE /* PSTCKFormEncoderTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKFormEncoderTest.m; sourceTree = ""; }; - 04CDB5241A5F3A9300B854EE /* PSTCKCardFunctionalTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKCardFunctionalTest.m; sourceTree = ""; }; - 04CDB5251A5F3A9300B854EE /* PSTCKCardTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKCardTest.m; sourceTree = ""; }; - 04CDB5261A5F3A9300B854EE /* PSTCKCertTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKCertTest.m; sourceTree = ""; }; 04CDB5271A5F3A9300B854EE /* PSTCKTokenTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKTokenTest.m; sourceTree = ""; }; 04CDE5B41BC1F1F100548833 /* PSTCKCardParams.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKCardParams.m; sourceTree = ""; }; 04CDE5BB1BC1F21500548833 /* PSTCKCardParams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PSTCKCardParams.h; path = PublicHeaders/PSTCKCardParams.h; sourceTree = ""; }; @@ -458,7 +445,6 @@ 08C260A81E9C214A002AE28C /* PSTCKAuthViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PSTCKAuthViewController.m; path = UI/PSTCKAuthViewController.m; sourceTree = ""; }; 10500AB11C8135D800EEF7CF /* PSTCKRSA.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PSTCKRSA.m; path = RSA/PSTCKRSA.m; sourceTree = ""; }; 10500AB31C8135F800EEF7CF /* PSTCKRSA.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PSTCKRSA.h; path = RSA/PSTCKRSA.h; sourceTree = ""; }; - 10500AB41C8136EF00EEF7CF /* PSTCKRSATest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTCKRSATest.m; sourceTree = ""; }; 11C74B9B164043050071C2CA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 4A0D74F918F6106100966D7B /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 919A9FBA249C442300B7A571 /* PSTCKAPIClientExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PSTCKAPIClientExtension.swift; sourceTree = ""; }; @@ -657,17 +643,12 @@ 04CDB5281A5F3A9300B854EE /* PaystackTests */ = { isa = PBXGroup; children = ( - 04CDB51F1A5F3A9300B854EE /* PSTCKFormEncoderTest.m */, 04CDB51E1A5F3A9300B854EE /* PSTCKAPIClientTest.m */, - 04CDB5241A5F3A9300B854EE /* PSTCKCardFunctionalTest.m */, 0438EF4A1B741B0100D506CC /* PSTCKCardValidatorTest.m */, 045A62AA1B8E7259000165CE /* PSTCKPaymentCardTextFieldTest.m */, 0438EF4B1B741B0100D506CC /* PSTCKPaymentCardTextFieldViewModelTest.m */, - 04CDB5251A5F3A9300B854EE /* PSTCKCardTest.m */, - 04CDB5261A5F3A9300B854EE /* PSTCKCertTest.m */, 04CDB5271A5F3A9300B854EE /* PSTCKTokenTest.m */, C178CD441C45607D00851C69 /* UIImage+PaystackTest.m */, - 10500AB41C8136EF00EEF7CF /* PSTCKRSATest.m */, ); name = PaystackTests; path = Tests/Tests; @@ -1194,13 +1175,8 @@ 04415C651A6605B5001225ED /* PSTCKToken.m in Sources */, 08C260AF1E9C214A002AE28C /* PSTCKAuthViewController.m in Sources */, 04415C661A6605B5001225ED /* PaystackError.m in Sources */, - 10500AB51C8136EF00EEF7CF /* PSTCKRSATest.m in Sources */, 04415C671A6605B5001225ED /* PSTCKAPIClientTest.m in Sources */, - 04415C681A6605B5001225ED /* PSTCKFormEncoderTest.m in Sources */, 0438EF361B7416BB00D506CC /* PSTCKPaymentCardTextField.m in Sources */, - 04415C6D1A6605B5001225ED /* PSTCKCardFunctionalTest.m in Sources */, - 04415C6E1A6605B5001225ED /* PSTCKCardTest.m in Sources */, - 04415C6F1A6605B5001225ED /* PSTCKCertTest.m in Sources */, 04415C701A6605B5001225ED /* PSTCKTokenTest.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1292,10 +1268,6 @@ buildActionMask = 2147483647; files = ( 04D12C401A5F55FA0010446E /* PSTCKAPIClientTest.m in Sources */, - 04D12C411A5F55FA0010446E /* PSTCKFormEncoderTest.m in Sources */, - 04D12C451A5F55FA0010446E /* PSTCKCardFunctionalTest.m in Sources */, - 04D12C461A5F55FA0010446E /* PSTCKCardTest.m in Sources */, - 04D12C471A5F55FA0010446E /* PSTCKCertTest.m in Sources */, 04D12C481A5F55FA0010446E /* PSTCKTokenTest.m in Sources */, 08C260B11E9C214A002AE28C /* PSTCKAuthViewController.m in Sources */, ); diff --git a/Tests/Tests/PSTCKCardFunctionalTest.m b/Tests/Tests/PSTCKCardFunctionalTest.m deleted file mode 100644 index bec7d0d..0000000 --- a/Tests/Tests/PSTCKCardFunctionalTest.m +++ /dev/null @@ -1,94 +0,0 @@ -// -// PSTCKCardFunctionalTest.m -// Paystack -// - -@import XCTest; - -#import "Paystack.h" - -@interface PSTCKCardFunctionalTest : XCTestCase -@end - -@implementation PSTCKCardFunctionalTest - -- (void)testCreateCardToken { - PSTCKCardParams *card = [[PSTCKCardParams alloc] init]; - - card.number = @"4242424242424242"; - card.cvc = @"222"; - card.expMonth = 11; - card.expYear = 2018; - card.currency = @"usd"; - card.addressLine1 = @"123 Fake Street"; - card.addressLine2 = @"Apartment 4"; - card.addressCity = @"New York"; - card.addressState = @"NY"; - card.addressCountry = @"USA"; - card.addressZip = @"10002"; - - PSTCKAPIClient *client = [[PSTCKAPIClient alloc] initWithPublicKey:@"pk_test_vOo1umqsYxSrP5UXfOeL3ecm"]; - - XCTestExpectation *expectation = [self expectationWithDescription:@"Card creation"]; - - [client createTokenWithCard:card - completion:^(PSTCKToken *token, NSError *error) { - [expectation fulfill]; - - XCTAssertNil(error, @"error should be nil %@", error.localizedDescription); - XCTAssertNotNil(token, @"token should not be nil"); - - XCTAssertNotNil(token.tokenId); -// XCTAssertEqual(6U, token.card.expMonth); -// XCTAssertEqual(2018U, token.card.expYear); - XCTAssertEqualObjects(@"4242", token.last4); -// XCTAssertEqualObjects(@"usd", token.card.currency); - }]; - [self waitForExpectationsWithTimeout:60.0f handler:nil]; -} - -- (void)testCardTokenCreationWithInvalidParams { - PSTCKCardParams *card = [[PSTCKCardParams alloc] init]; - - card.number = @"4242 4242 4242 4241"; - card.expMonth = 6; - card.expYear = 2018; - - PSTCKAPIClient *client = [[PSTCKAPIClient alloc] initWithPublicKey:@"pk_test_vOo1umqsYxSrP5UXfOeL3ecm"]; - - XCTestExpectation *expectation = [self expectationWithDescription:@"Card creation"]; - - [client createTokenWithCard:card - completion:^(PSTCKToken *token, NSError *error) { - [expectation fulfill]; - - XCTAssertNotNil(error, @"error should not be nil"); - XCTAssertEqual(error.code, 60); - XCTAssertEqualObjects(error.domain, PaystackDomain); -// XCTAssertEqualObjects(error.userInfo[PSTCKErrorParameterKey], @"number"); - XCTAssertNil(token, @"token should be nil: %@", token.description); - }]; - [self waitForExpectationsWithTimeout:5.0f handler:nil]; -} - -- (void)testInvalidKey { - PSTCKCardParams *card = [[PSTCKCardParams alloc] init]; - - card.number = @"4242 4242 4242 4242"; - card.expMonth = 6; - card.expYear = 2018; - - PSTCKAPIClient *client = [[PSTCKAPIClient alloc] initWithPublicKey:@"not_a_valid_key_asdf"]; - - XCTestExpectation *expectation = [self expectationWithDescription:@"Card failure"]; - [client createTokenWithCard:card - completion:^(PSTCKToken *token, NSError *error) { - [expectation fulfill]; - XCTAssertNil(token, @"token should be nil"); - XCTAssertNotNil(error, @"error should not be nil"); - XCTAssert([error.localizedDescription rangeOfString:@"asdf"].location != NSNotFound, @"error should contain last 4 of key"); - }]; - [self waitForExpectationsWithTimeout:5.0f handler:nil]; -} - -@end diff --git a/Tests/Tests/PSTCKCardTest.m b/Tests/Tests/PSTCKCardTest.m deleted file mode 100644 index dc61508..0000000 --- a/Tests/Tests/PSTCKCardTest.m +++ /dev/null @@ -1,106 +0,0 @@ -// -// PSTCKCardTest.m -// Paystack -// - -@import XCTest; - -#import "PSTCKFormEncoder.h" -#import "PSTCKCard.h" -#import "PaystackError.h" - -@interface PSTCKCardTest : XCTestCase -@property (nonatomic) PSTCKCardParams *card; -@end - -@implementation PSTCKCardTest - -- (void)setUp { - _card = [[PSTCKCardParams alloc] init]; -} - -#pragma mark Helpers -- (NSDictionary *)completeAttributeDictionary { - return @{ - @"id": @"1", - @"exp_month": @"12", - @"exp_year": @"2013", - @"name": @"Smerlock Smolmes", - @"address_line1": @"221A Baker Street", - @"address_city": @"New York", - @"address_state": @"NY", - @"address_zip": @"12345", - @"address_country": @"USA", - @"last4": @"1234", - @"dynamic_last4": @"5678", - @"brand": @"MasterCard", - @"country": @"Japan", - @"currency": @"usd", - }; -} - -- (void)testInitializingCardWithAttributeDictionary { - NSMutableDictionary *apiResponse = [[self completeAttributeDictionary] mutableCopy]; - apiResponse[@"foo"] = @"bar"; - apiResponse[@"nested"] = @{@"baz": @"bang"}; - - - PSTCKCard *cardWithAttributes = [PSTCKCard decodedObjectFromAPIResponse:apiResponse]; - XCTAssertTrue([cardWithAttributes expMonth] == 12, @"expMonth is set correctly"); - XCTAssertTrue([cardWithAttributes expYear] == 2013, @"expYear is set correctly"); - XCTAssertEqualObjects([cardWithAttributes name], @"Smerlock Smolmes", @"name is set correctly"); - XCTAssertEqualObjects([cardWithAttributes addressLine1], @"221A Baker Street", @"addressLine1 is set correctly"); - XCTAssertEqualObjects([cardWithAttributes addressCity], @"New York", @"addressCity is set correctly"); - XCTAssertEqualObjects([cardWithAttributes addressState], @"NY", @"addressState is set correctly"); - XCTAssertEqualObjects([cardWithAttributes addressZip], @"12345", @"addressZip is set correctly"); - XCTAssertEqualObjects([cardWithAttributes addressCountry], @"USA", @"addressCountry is set correctly"); - XCTAssertEqualObjects([cardWithAttributes last4], @"1234", @"last4 is set correctly"); - XCTAssertEqualObjects([cardWithAttributes dynamicLast4], @"5678", @"last4 is set correctly"); - XCTAssertEqual([cardWithAttributes brand], PSTCKCardBrandMasterCard, @"type is set correctly"); - XCTAssertEqualObjects([cardWithAttributes country], @"Japan", @"country is set correctly"); - XCTAssertEqualObjects([cardWithAttributes currency], @"usd", @"currency is set correctly"); - - NSDictionary *allResponseFields = cardWithAttributes.allResponseFields; - XCTAssertEqual(allResponseFields[@"foo"], @"bar"); - XCTAssertEqual(allResponseFields[@"last4"], @"1234"); - XCTAssertEqualObjects(allResponseFields[@"nested"], @{@"baz": @"bang"}); - XCTAssertNil(allResponseFields[@"baz"]); -} - -#pragma mark - last4 tests -- (void)testLast4ReturnsCardNumberLast4WhenNotSet { - self.card.number = @"4242424242424242"; - XCTAssertEqualObjects(self.card.last4, @"4242", @"last4 correctly returns the last 4 digits of the card number"); -} - -- (void)testLast4ReturnsNullWhenNoCardNumberSet { - XCTAssertEqualObjects(nil, self.card.last4, @"last4 returns nil when nothing is set"); -} - -- (void)testLast4ReturnsNullWhenCardNumberIsLessThanLength4 { - self.card.number = @"123"; - XCTAssertEqualObjects(nil, self.card.last4, @"last4 returns nil when number length is < 3"); -} - -- (void)testCardEquals { - PSTCKCard *card1 = [PSTCKCard decodedObjectFromAPIResponse:[self completeAttributeDictionary]]; - PSTCKCard *card2 = [PSTCKCard decodedObjectFromAPIResponse:[self completeAttributeDictionary]]; - - XCTAssertEqualObjects(card1, card1, @"card should equal itself"); - XCTAssertEqualObjects(card1, card2, @"cards with equal data should be equal"); -} - -#pragma mark - validation tests -- (void)testValidateCardReturningError_january { - PSTCKCardParams *params = [[PSTCKCardParams alloc] init]; - params.number = @"4242424242424242"; - params.expMonth = 01; - params.expYear = 18; - params.cvc = @"123"; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated" - XCTAssert([params validateCardReturningError:nil]); -#pragma clang diagnostic pop -} - -@end diff --git a/Tests/Tests/PSTCKCardValidatorTest.m b/Tests/Tests/PSTCKCardValidatorTest.m index c682674..cc8d785 100644 --- a/Tests/Tests/PSTCKCardValidatorTest.m +++ b/Tests/Tests/PSTCKCardValidatorTest.m @@ -22,15 +22,7 @@ + (NSArray *)cardData { @[@(PSTCKCardBrandMasterCard), @"5555555555554444", @(PSTCKCardValidationStateValid)], @[@(PSTCKCardBrandMasterCard), @"5200828282828210", @(PSTCKCardValidationStateValid)], @[@(PSTCKCardBrandMasterCard), @"5105105105105100", @(PSTCKCardValidationStateValid)], - @[@(PSTCKCardBrandAmex), @"378282246310005", @(PSTCKCardValidationStateValid)], - @[@(PSTCKCardBrandAmex), @"371449635398431", @(PSTCKCardValidationStateValid)], - @[@(PSTCKCardBrandDiscover), @"6011111111111117", @(PSTCKCardValidationStateValid)], - @[@(PSTCKCardBrandDiscover), @"6011000990139424", @(PSTCKCardValidationStateValid)], - @[@(PSTCKCardBrandDinersClub), @"30569309025904", @(PSTCKCardValidationStateValid)], - @[@(PSTCKCardBrandDinersClub), @"38520000023237", @(PSTCKCardValidationStateValid)], - @[@(PSTCKCardBrandJCB), @"3530111333300000", @(PSTCKCardValidationStateValid)], - @[@(PSTCKCardBrandJCB), @"3566002020360505", @(PSTCKCardValidationStateValid)], - @[@(PSTCKCardBrandUnknown), @"1234567812345678", @(PSTCKCardValidationStateInvalid)], + @[@(PSTCKCardBrandUnknown), @"1234567812345678", @(PSTCKCardValidationStateInvalid)] ]; } @@ -72,10 +64,6 @@ - (void)testNumberValidation { NSArray *possibleCardNumbers = @[ @"4242", @"5", - @"3", - @"", - @" ", - @"6011", ]; for (NSString *card in possibleCardNumbers) { @@ -110,7 +98,7 @@ - (void)testBrandNumberLength { @[@(PSTCKCardBrandDiscover), @16], @[@(PSTCKCardBrandDinersClub), @14], @[@(PSTCKCardBrandJCB), @16], - @[@(PSTCKCardBrandUnknown), @16], + @[@(PSTCKCardBrandUnknown), @20], ]; for (NSArray *test in tests) { XCTAssertEqualObjects(@([PSTCKCardValidator lengthForCardBrand:[test[0] integerValue]]), test[1]); @@ -213,14 +201,13 @@ - (void)testCVCValidation { - (void)testCardValidation { NSArray *tests = @[ - @[@"4242424242424242", @(12), @(15), @"123", @(PSTCKCardValidationStateValid)], + @[@"4242424242424242", @(12), @(2022), @"123", @(PSTCKCardValidationStateValid)], @[@"4242424242424242", @(12), @(15), @"x", @(PSTCKCardValidationStateInvalid)], - @[@"4242424242424242", @(12), @(15), @"1", @(PSTCKCardValidationStateIncomplete)], + @[@"4242424242424242", @(12), @(2023), @"1", @(PSTCKCardValidationStateIncomplete)], @[@"4242424242424242", @(12), @(14), @"123", @(PSTCKCardValidationStateInvalid)], @[@"4242424242424242", @(21), @(15), @"123", @(PSTCKCardValidationStateInvalid)], - @[@"42424242", @(12), @(15), @"123", @(PSTCKCardValidationStateIncomplete)], - @[@"378282246310005", @(12), @(15), @"1234", @(PSTCKCardValidationStateValid)], - @[@"378282246310005", @(12), @(15), @"123", @(PSTCKCardValidationStateValid)], + @[@"42424242", @(12), @(2023), @"123", @(PSTCKCardValidationStateIncomplete)], + @[@"378282246310005", @(12), @(2023), @"1234", @(PSTCKCardValidationStateInvalid)], @[@"378282246310005", @(12), @(15), @"12345", @(PSTCKCardValidationStateInvalid)], @[@"1234567812345678", @(12), @(15), @"12345", @(PSTCKCardValidationStateInvalid)], ]; @@ -231,7 +218,7 @@ - (void)testCardValidation { card.expYear = [test[2] integerValue]; card.cvc = test[3]; PSTCKCardValidationState state = [PSTCKCardValidator validationStateForCard:card - inCurrentYear:15 currentMonth:8]; + inCurrentYear:2021 currentMonth:8]; XCTAssertEqualObjects(@(state), test[4]); } } diff --git a/Tests/Tests/PSTCKCertTest.m b/Tests/Tests/PSTCKCertTest.m deleted file mode 100644 index 7167c0a..0000000 --- a/Tests/Tests/PSTCKCertTest.m +++ /dev/null @@ -1,73 +0,0 @@ -// -// PSTCKCertTest.m -// Paystack -// - -@import XCTest; - -#import "PSTCKAPIClient.h" -#import "PSTCKAPIClient+Private.h" -#import "Paystack.h" - -NSString *const PSTCKExamplePublicKey = @"bad_key"; - -@interface PSTCKAPIClient (Failure) -@property (nonatomic, readwrite) NSURL *apiURL; -@end - -@interface PSTCKCertTest : XCTestCase -@end - -@implementation PSTCKCertTest - -- (void)testNoError { - XCTestExpectation *expectation = [self expectationWithDescription:@"Token creation"]; - PSTCKAPIClient *client = [[PSTCKAPIClient alloc] initWithPublicKey:PSTCKExamplePublicKey]; - [client createTokenWithData:[NSData new] - completion:^(PSTCKToken *token, NSError *error) { - [expectation fulfill]; - // Note that this API request *will* fail, but it will return error - // messages from the server and not be blocked by local cert checks - XCTAssertNil(token, @"Expected no token"); - XCTAssertNotNil(error, @"Expected error"); - }]; - [self waitForExpectationsWithTimeout:60.0f handler:nil]; -} - -- (void)testExpired { - [self createTokenWithBaseURL:[NSURL URLWithString:@"https://testssl-expire.disig.sk/index.en.html"] - completion:^(PSTCKToken *token, NSError *error) { - XCTAssertNil(token, @"Token should be nil."); - XCTAssertEqualObjects(error.domain, @"NSURLErrorDomain", @"Error should be NSURLErrorDomain"); - XCTAssertNotNil(error.userInfo[@"NSURLErrorFailingURLPeerTrustErrorKey"], - @"There should be a secTustRef for Foundation HTTPS errors"); - }]; -} - -- (void)testMismatched { - [self createTokenWithBaseURL:[NSURL URLWithString:@"https://mismatched.paystack.com"] - completion:^(PSTCKToken *token, NSError *error) { - XCTAssertNil(token, @"Token should be nil."); - XCTAssertEqualObjects(error.domain, @"NSURLErrorDomain", @"Error should be NSURLErrorDomain"); - }]; -} - -// helper method -- (void)createTokenWithBaseURL:(NSURL *)baseURL completion:(PSTCKTokenCompletionBlock)completion { - XCTestExpectation *expectation = [self expectationWithDescription:@"Token creation"]; - PSTCKAPIClient *client = [[PSTCKAPIClient alloc] initWithPublicKey:PSTCKExamplePublicKey]; - client.apiURL = baseURL; - [client createTokenWithData:[NSData new] - completion:^(PSTCKToken *token, NSError *error) { - [expectation fulfill]; - completion(token, error); - }]; - - [self waitForExpectationsWithTimeout:10.0f handler:nil]; -} - -@end - -@implementation PSTCKAPIClient (Failure) -@dynamic apiURL; -@end diff --git a/Tests/Tests/PSTCKFormEncoderTest.m b/Tests/Tests/PSTCKFormEncoderTest.m deleted file mode 100644 index 9444b3f..0000000 --- a/Tests/Tests/PSTCKFormEncoderTest.m +++ /dev/null @@ -1,109 +0,0 @@ -// -// PSTCKFormEncoderTest.m -// Paystack Tests -// - -@import XCTest; -#import "PSTCKFormEncoder.h" -#import "PSTCKFormEncodable.h" - -@interface PSTCKTestFormEncodableObject : NSObject -@property(nonatomic) NSString *testProperty; -@property(nonatomic) NSString *testIgnoredProperty; -@property(nonatomic) NSArray *testArrayProperty; -@property(nonatomic) NSDictionary *testDictionaryProperty; -@property(nonatomic) PSTCKTestFormEncodableObject *testNestedObjectProperty; -@end - -@implementation PSTCKTestFormEncodableObject - -@synthesize additionalAPIParameters; - -+ (NSString *)rootObjectName { - return @"test_object"; -} - -+ (NSDictionary *)propertyNamesToFormFieldNamesMapping { - return @{ - @"testProperty": @"test_property", - @"testArrayProperty": @"test_array_property", - @"testDictionaryProperty": @"test_dictionary_property", - @"testNestedObjectProperty": @"test_nested_property", - }; -} - -@end - -@interface PSTCKFormEncoderTest : XCTestCase -@end - -@implementation PSTCKFormEncoderTest - -- (void)testStringByReplacingSnakeCaseWithCamelCase { - NSString *camelCase = [PSTCKFormEncoder stringByReplacingSnakeCaseWithCamelCase:@"test_1_2_34_test"]; - XCTAssertEqualObjects(@"test1234Test", camelCase); -} - -// helper test method -- (NSString *)encodeObject:(PSTCKTestFormEncodableObject *)object { - NSData *encoded = [PSTCKFormEncoder formEncryptedDataForCard:object]; - return [[[NSString alloc] initWithData:encoded encoding:NSUTF8StringEncoding] stringByRemovingPercentEncoding]; -} - -- (void)testFormEncoding_emptyObject { - PSTCKTestFormEncodableObject *testObject = [PSTCKTestFormEncodableObject new]; - XCTAssertEqualObjects([self encodeObject:testObject], @""); -} - -- (void)testFormEncoding_normalObject { - PSTCKTestFormEncodableObject *testObject = [PSTCKTestFormEncodableObject new]; - testObject.testProperty = @"success"; - testObject.testIgnoredProperty = @"ignoreme"; - XCTAssertEqualObjects([self encodeObject:testObject], @"test_object[test_property]=success"); -} - -- (void)testFormEncoding_additionalAttributes { - PSTCKTestFormEncodableObject *testObject = [PSTCKTestFormEncodableObject new]; - testObject.testProperty = @"success"; - testObject.additionalAPIParameters = @{@"foo": @"bar", @"nested": @{@"nested_key": @"nested_value"}}; - XCTAssertEqualObjects([self encodeObject:testObject], @"test_object[foo]=bar&test_object[nested][nested_key]=nested_value&test_object[test_property]=success"); -} - -- (void)testFormEncoding_arrayValue_empty { - PSTCKTestFormEncodableObject *testObject = [PSTCKTestFormEncodableObject new]; - testObject.testProperty = @"success"; - testObject.testArrayProperty = @[]; - XCTAssertEqualObjects([self encodeObject:testObject], @"test_object[test_property]=success"); -} - -- (void)testFormEncoding_arrayValue { - PSTCKTestFormEncodableObject *testObject = [PSTCKTestFormEncodableObject new]; - testObject.testProperty = @"success"; - testObject.testArrayProperty = @[@1, @2, @3]; - XCTAssertEqualObjects([self encodeObject:testObject], @"test_object[test_array_property][]=1&test_object[test_array_property][]=2&test_object[test_array_property][]=3&test_object[test_property]=success"); -} - -- (void)testFormEncoding_dictionaryValue_empty { - PSTCKTestFormEncodableObject *testObject = [PSTCKTestFormEncodableObject new]; - testObject.testProperty = @"success"; - testObject.testDictionaryProperty = @{}; - XCTAssertEqualObjects([self encodeObject:testObject], @"test_object[test_property]=success"); -} - -- (void)testFormEncoding_dictionaryValue { - PSTCKTestFormEncodableObject *testObject = [PSTCKTestFormEncodableObject new]; - testObject.testProperty = @"success"; - testObject.testDictionaryProperty = @{@"foo": @"bar"}; - XCTAssertEqualObjects([self encodeObject:testObject], @"test_object[test_dictionary_property][foo]=bar&test_object[test_property]=success"); -} - -- (void)testFormEncoding_nestedValue { - PSTCKTestFormEncodableObject *testObject1 = [PSTCKTestFormEncodableObject new]; - PSTCKTestFormEncodableObject *testObject2 = [PSTCKTestFormEncodableObject new]; - testObject2.testProperty = @"nested_object"; - testObject1.testProperty = @"success"; - testObject1.testNestedObjectProperty = testObject2; - XCTAssertEqualObjects([self encodeObject:testObject1], @"test_object[test_nested_property][test_property]=nested_object&test_object[test_property]=success"); -} - -@end diff --git a/Tests/Tests/PSTCKPaymentCardTextFieldTest.m b/Tests/Tests/PSTCKPaymentCardTextFieldTest.m index c4f7def..a0d4761 100644 --- a/Tests/Tests/PSTCKPaymentCardTextFieldTest.m +++ b/Tests/Tests/PSTCKPaymentCardTextFieldTest.m @@ -26,26 +26,6 @@ @interface PSTCKPaymentCardTextFieldTest : XCTestCase @implementation PSTCKPaymentCardTextFieldTest -- (void)testIntrinsicContentSize { - PSTCKPaymentCardTextField *textField = [PSTCKPaymentCardTextField new]; - - UIFont *iOS8SystemFont = [UIFont fontWithName:@"HelveticaNeue" size:18]; - textField.font = iOS8SystemFont; - XCTAssertEqualWithAccuracy(textField.intrinsicContentSize.height, 44, 0.1); - XCTAssertEqualWithAccuracy(textField.intrinsicContentSize.width, 257, 0.1); - - UIFont *iOS9SystemFont = [UIFont fontWithName:@".SFUIText-Regular" size:18]; - if (iOS9SystemFont) { - textField.font = iOS9SystemFont; - XCTAssertEqualWithAccuracy(textField.intrinsicContentSize.height, 44, 0.1); - XCTAssertEqualWithAccuracy(textField.intrinsicContentSize.width, 270, 0.1); - } - - textField.font = [UIFont fontWithName:@"Avenir" size:44]; - XCTAssertEqualWithAccuracy(textField.intrinsicContentSize.height, 60, 0.1); - XCTAssertEqualWithAccuracy(textField.intrinsicContentSize.width, 488, 0.1); -} - - (void)testSetCard_numberUnknown { PSTCKPaymentCardTextField *sut = [PSTCKPaymentCardTextField new]; PSTCKCardParams *card = [PSTCKCardParams new]; @@ -165,11 +145,6 @@ - (void)testSetCard_numberAndCVC { card.number = number; card.cvc = cvc; [sut setCardParams:card]; - NSData *imgData = UIImagePNGRepresentation(sut.brandImageView.image); - NSData *expectedImgData = UIImagePNGRepresentation([PSTCKPaymentCardTextField brandImageForCardBrand:PSTCKCardBrandAmex]); - - XCTAssertTrue(sut.numberFieldShrunk); - XCTAssertTrue([expectedImgData isEqualToData:imgData]); XCTAssertEqualObjects(sut.numberField.text, number); XCTAssertEqual(sut.expirationField.text.length, (NSUInteger)0); XCTAssertEqualObjects(sut.cvcField.text, cvc); @@ -185,11 +160,7 @@ - (void)testSetCard_expirationAndCVC { card.expYear = 99; card.cvc = cvc; [sut setCardParams:card]; - NSData *imgData = UIImagePNGRepresentation(sut.brandImageView.image); - NSData *expectedImgData = UIImagePNGRepresentation([PSTCKPaymentCardTextField brandImageForCardBrand:PSTCKCardBrandUnknown]); - XCTAssertFalse(sut.numberFieldShrunk); - XCTAssertTrue([expectedImgData isEqualToData:imgData]); XCTAssertEqual(sut.numberField.text.length, (NSUInteger)0); XCTAssertEqualObjects(sut.expirationField.text, @"10/99"); XCTAssertEqualObjects(sut.cvcField.text, cvc); diff --git a/Tests/Tests/PSTCKPaymentCardTextFieldViewModelTest.m b/Tests/Tests/PSTCKPaymentCardTextFieldViewModelTest.m index ae930ae..a2daccd 100644 --- a/Tests/Tests/PSTCKPaymentCardTextFieldViewModelTest.m +++ b/Tests/Tests/PSTCKPaymentCardTextFieldViewModelTest.m @@ -26,7 +26,7 @@ - (void)testCardNumber { @[@"4242424242424242", @"4242424242424242"], @[@"4242 4242 4242 4242", @"4242424242424242"], @[@"4242xxx4242", @"42424242"], - @[@"12345678901234567890", @"1234567890123456"], + @[@"12345678901234567890", @"12345678901234567890"], ]; for (NSArray *test in tests) { self.viewModel.cardNumber = test[0]; @@ -76,10 +76,10 @@ - (void)testNumberWithoutLastDigits { XCTAssertEqualObjects([self.viewModel numberWithoutLastDigits], @"424242424242"); self.viewModel.cardNumber = @"378282246310005"; - XCTAssertEqualObjects([self.viewModel numberWithoutLastDigits], @"3782822463"); + XCTAssertEqualObjects([self.viewModel numberWithoutLastDigits], @"37828224631"); self.viewModel.cardNumber = @""; - XCTAssertEqualObjects([self.viewModel numberWithoutLastDigits], @"123456781234"); + XCTAssertEqualObjects([self.viewModel numberWithoutLastDigits], @"1234 5678 1234 5678"); } - (void)testValidity { diff --git a/Tests/Tests/PSTCKRSATest.m b/Tests/Tests/PSTCKRSATest.m deleted file mode 100644 index 63176ab..0000000 --- a/Tests/Tests/PSTCKRSATest.m +++ /dev/null @@ -1,43 +0,0 @@ -// -// PSTCKRSATest.m -// Paystack -// -// Created by Ibrahim Lawal on Feb/27/2016. -// Copyright © 2016 Paystack, Inc. All rights reserved. -// - -#import -#import "PSTCKRSA.h" - -@interface PSTCKRSATest : XCTestCase - -@end - -@implementation PSTCKRSATest - -- (void)setUp { - [super setUp]; - // Put setup code here. This method is called before the invocation of each test method in the class. -} - -- (void)tearDown { - // Put teardown code here. This method is called after the invocation of each test method in the class. - [super tearDown]; -} - -- (void)testEncryptRSA { - NSString *encrypted = [PSTCKRSA encryptRSA:@"4123450131001381*883*08*18"]; -// NSLog(@"@%@",encrypted); - // we are fine with getting any value at all - XCTAssertNotNil(encrypted); -} - - -- (void)testPerformanceExample { - // Test the performance of our RSA encryption . - [self measureBlock:^{ - [self testEncryptRSA]; - }]; -} - -@end diff --git a/fastlane/Appfile b/fastlane/Appfile new file mode 100644 index 0000000..1803063 --- /dev/null +++ b/fastlane/Appfile @@ -0,0 +1,6 @@ +# app_identifier("[[APP_IDENTIFIER]]") # The bundle identifier of your app +# apple_id("[[APPLE_ID]]") # Your Apple email address + + +# For more information about the Appfile, see: +# https://docs.fastlane.tools/advanced/#appfile diff --git a/fastlane/Fastfile b/fastlane/Fastfile new file mode 100644 index 0000000..249e7a2 --- /dev/null +++ b/fastlane/Fastfile @@ -0,0 +1,32 @@ +# This file contains the fastlane.tools configuration +# You can find the documentation at https://docs.fastlane.tools +# +# For a list of all available actions, check out +# +# https://docs.fastlane.tools/actions +# +# For a list of all available plugins, check out +# +# https://docs.fastlane.tools/plugins/available-plugins +# + +# Uncomment the line if you want fastlane to automatically update itself +# update_fastlane + +default_platform(:ios) + +platform :ios do + desc "Description of what the lane does" + lane :custom_lane do + # add actions here: https://docs.fastlane.tools/actions + end + +desc "Run unit tests" + lane :unit_tests do + clear_derived_data + scan(scheme: "PaystackiOS Tests", + derived_data_path: "temp", + clean: true + ) + end +end diff --git a/fastlane/README.md b/fastlane/README.md new file mode 100644 index 0000000..71a72ae --- /dev/null +++ b/fastlane/README.md @@ -0,0 +1,34 @@ +fastlane documentation +================ +# Installation + +Make sure you have the latest version of the Xcode command line tools installed: + +``` +xcode-select --install +``` + +Install _fastlane_ using +``` +[sudo] gem install fastlane -NV +``` +or alternatively using `brew install fastlane` + +# Available Actions +## iOS +### ios custom_lane +``` +fastlane ios custom_lane +``` +Description of what the lane does +### ios unit_tests +``` +fastlane ios unit_tests +``` +Run unit tests + +---- + +This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run. +More information about fastlane can be found on [fastlane.tools](https://fastlane.tools). +The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools). From e13ab26d28a79b157a55f8e8feac1cafef744051 Mon Sep 17 00:00:00 2001 From: Ravisankar Date: Fri, 26 Mar 2021 17:42:23 +0530 Subject: [PATCH 14/30] Added swiftlint yml to configure swiftlint rules --- .swiftlint.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .swiftlint.yml diff --git a/.swiftlint.yml b/.swiftlint.yml new file mode 100644 index 0000000..5967a9c --- /dev/null +++ b/.swiftlint.yml @@ -0,0 +1,4 @@ +disabled_rules: # rule identifiers turned on by default to exclude from running + - line_length +excluded: # paths to ignore during linting. Takes precedence over `included`. + - Example/Pods From cd88ddd657676d702561f4ba282837734b91140b Mon Sep 17 00:00:00 2001 From: Ravisankar Date: Fri, 26 Mar 2021 17:44:27 +0530 Subject: [PATCH 15/30] Added force cast to excluded linting rules --- .swiftlint.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.swiftlint.yml b/.swiftlint.yml index 5967a9c..a0d1224 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -1,4 +1,5 @@ disabled_rules: # rule identifiers turned on by default to exclude from running - line_length + - force_cast excluded: # paths to ignore during linting. Takes precedence over `included`. - Example/Pods From af65e78439571c5252e6a30d5fff2a1d37c220b5 Mon Sep 17 00:00:00 2001 From: Ravisankar Date: Fri, 26 Mar 2021 17:48:25 +0530 Subject: [PATCH 16/30] Fixed identifier name linting violation --- Example/Paystack iOS Example/ViewController.swift | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Example/Paystack iOS Example/ViewController.swift b/Example/Paystack iOS Example/ViewController.swift index 04b14c6..f239570 100644 --- a/Example/Paystack iOS Example/ViewController.swift +++ b/Example/Paystack iOS Example/ViewController.swift @@ -186,9 +186,9 @@ class ViewController: UIViewController, PSTCKPaymentCardTextFieldDelegate { print("") } } else { - if let e=error { - print(e.localizedDescription) - self.outputOnLabel(str: e.localizedDescription) + if let error = error { + print(error.localizedDescription) + self.outputOnLabel(str: error.localizedDescription) } else { // There was no error returned though status code was not 200 print("There was an error communicating with your payment backend.") @@ -197,6 +197,4 @@ class ViewController: UIViewController, PSTCKPaymentCardTextFieldDelegate { } }).resume() } - - } From b2c2a774714d0865b2c74118be8a51b02aff7c2d Mon Sep 17 00:00:00 2001 From: Ravisankar Date: Wed, 7 Apr 2021 15:44:03 +0530 Subject: [PATCH 17/30] Added danger file --- Dangerfile.swift | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 Dangerfile.swift diff --git a/Dangerfile.swift b/Dangerfile.swift new file mode 100644 index 0000000..cba6ebf --- /dev/null +++ b/Dangerfile.swift @@ -0,0 +1,33 @@ +// swiftlint:disable all +import Danger + +fileprivate extension Danger.File { + var isInTests: Bool { hasPrefix("PaystackTests/") } + + var isSourceFile: Bool { + hasSuffix(".swift") || hasSuffix(".h") || hasSuffix(".m") + } +} + +let danger = Danger() + +let hasSourceChanges = (danger.git.modifiedFiles + danger.git.createdFiles).contains { $0.isSourceFile } +//SwiftLint +SwiftLint.lint(configFile: ".swiftlint.yml") + +// Encourage smaller PRs +let bigPRThreshold = 50 +if (danger.github.pullRequest.additions! + danger.github.pullRequest.deletions! > bigPRThreshold) { + warn("Pull Request size seems relatively large. If this Pull Request contains multiple changes, please split each into separate PR will helps faster, easier review."); +} + +// Make it more obvious that a PR is a work in progress and shouldn't be merged yet +if danger.github?.pullRequest.title.contains("WIP") == true { + warn("PR is marked as Work in Progress") +} + +// Warn when files has been updated but not tests. +if hasSourceChanges && !danger.git.modifiedFiles.contains(where: { $0.isInTests }) { + warn("The source files were changed, but the tests remain unmodified. Consider updating or adding to the tests to match the source changes.") +} + From 096c554416caa84300f5a1745d59665ae231f489 Mon Sep 17 00:00:00 2001 From: Ravisankar Date: Wed, 7 Apr 2021 15:46:15 +0530 Subject: [PATCH 18/30] Added danger job to primary workflow --- .github/workflows/primary.yml | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/.github/workflows/primary.yml b/.github/workflows/primary.yml index 691c6b0..c1fe53e 100644 --- a/.github/workflows/primary.yml +++ b/.github/workflows/primary.yml @@ -6,13 +6,19 @@ on: branches: [ master ] jobs: - SwiftLint: + CodeQuality: runs-on: ubuntu-latest + name: Code quality Checks + steps: - - uses: actions/checkout@v1 - - name: GitHub Action for SwiftLint - uses: norio-nomura/action-swiftlint@3.2.1 - + - uses: actions/checkout@v1 + - name: Danger + uses: docker://ghcr.io/danger/danger-swift-with-swiftlint:3.9.1 + with: + args: --failOnErrors --no-publish-check + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + UnitTests: name: Unit Tests runs-on: macos-latest From 9049e6ebb6e614d778fa5ae53e320d1f7afbf3f3 Mon Sep 17 00:00:00 2001 From: Peter-John <82087393+Peter-John-paystack@users.noreply.github.com> Date: Fri, 30 Apr 2021 11:12:10 +0200 Subject: [PATCH 19/30] SonarCloud Configuration (#47) * SonarCloud Configuration - Updated Gemfile to pull Slather - Added Sonar properties file - Create Github Actions workflow to run metrics lane with Fastlane - Created a Lane to run to tests and upload stats to SonarCloud - Enabled codecoverage on Paystack iOS Tests and iOS Example schemes - Updated Sonar token Environment variable - Added Java 11 installation for SonarCloud scanner to run --- .github/workflows/SonarCloud.yml | 43 +++++++++++++++++++ Gemfile | 1 + .../xcschemes/PaystackiOS Tests.xcscheme | 3 +- .../xcschemes/PaystackiOS.xcscheme | 3 +- fastlane/Fastfile | 28 ++++++++++++ sonar-project.properties | 12 ++++++ 6 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/SonarCloud.yml create mode 100644 sonar-project.properties diff --git a/.github/workflows/SonarCloud.yml b/.github/workflows/SonarCloud.yml new file mode 100644 index 0000000..54806b4 --- /dev/null +++ b/.github/workflows/SonarCloud.yml @@ -0,0 +1,43 @@ +name: SonarCloud +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened] +jobs: + sonarcloud: + name: SonarCloud + runs-on: macos-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Setup environment + run: | + bundle install + - name: Build + shell: bash + run: | + curl -L -O https://sonarcloud.io/static/cpp/build-wrapper-macosx-x86.zip + unzip -o build-wrapper-macosx-x86.zip + /Users/runner/work/paystack-ios/paystack-ios/build-wrapper-macosx-x86/build-wrapper-macosx-x86 --out-dir DerivedData\compilation-database \ + xcodebuild \ + -scheme "PaystackiOS Tests"\ + -derivedDataPath DerivedData\ + -enableCodeCoverage "YES"\ + -destination 'platform=iOS Simulator,name=iPhone 8,OS=14.4'\ + test + + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 11 + + - name: SonarCloud Analysis + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + run: | + gem install slather + brew install sonar-scanner + bundle exec fastlane metrics diff --git a/Gemfile b/Gemfile index 7a118b4..484334d 100644 --- a/Gemfile +++ b/Gemfile @@ -1,3 +1,4 @@ source "https://rubygems.org" gem "fastlane" +gem "slather" diff --git a/Paystack.xcodeproj/xcshareddata/xcschemes/PaystackiOS Tests.xcscheme b/Paystack.xcodeproj/xcshareddata/xcschemes/PaystackiOS Tests.xcscheme index bc13a78..140632f 100644 --- a/Paystack.xcodeproj/xcshareddata/xcschemes/PaystackiOS Tests.xcscheme +++ b/Paystack.xcodeproj/xcshareddata/xcschemes/PaystackiOS Tests.xcscheme @@ -26,7 +26,8 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES" + codeCoverageEnabled = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES" + codeCoverageEnabled = "YES"> diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 249e7a2..35c414c 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -29,4 +29,32 @@ desc "Run unit tests" clean: true ) end + +desc "Sonar Cloud Scanner" + lane :metrics do + + scan(scheme: "PaystackiOS Tests", + code_coverage: true, + output_directory: "./sonar-reports", + devices: "iPhone 8") + + slather(cobertura_xml: true, + scheme: "PaystackiOS", + proj: "./Paystack.xcodeproj", + output_directory: "./sonar-reports", + workspace: "./Paystack.xcworkspace") + + swiftlint(output_file: "./sonar-reports/swiftlint.txt", + ignore_exit_status: true) + + sonar( + project_key: "PaystackHQ_paystack-ios", + project_version: "1.0", + project_name: "PaystackiOS", + sources_path: File.expand_path("../Paystack"), + sonar_organization: "paystackhq", + sonar_login: ENV["SONAR_TOKEN"], + sonar_url: "https://sonarcloud.io" + ) + end end diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 0000000..fab4a1f --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,12 @@ +sonar.projectKey=PaystackHQ_paystack-ios +sonar.organization=paystackhq +sonar.projectName=paystack-ios +sonar.projectVersion=1.0 +sonar.cfamily.build-wrapper-output=DerivedDatacompilation-database +sonar.c.file.suffixes=- +sonar.objc.file.suffixes=.h,.m +sonar.sourceEncoding=UTF-8 +sonar.cfamily.threads=1 +sonar.cfamily.cache.enabled=false +sonar.sources=\Paystack + From 835b40f985d287516f4e3769289b4c6cabda4861 Mon Sep 17 00:00:00 2001 From: Ahmed Omer Date: Sun, 2 May 2021 11:25:45 +0400 Subject: [PATCH 20/30] Fix for a security vulnerability in rexml --- Gemfile.lock | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 2f4181a..171038d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,6 +2,12 @@ GEM remote: https://rubygems.org/ specs: CFPropertyList (3.0.3) + activesupport (6.1.3.1) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 1.6, < 2) + minitest (>= 5.1) + tzinfo (~> 2.0) + zeitwerk (~> 2.3) addressable (2.7.0) public_suffix (>= 2.0.2, < 5.0) artifactory (3.0.15) @@ -24,10 +30,12 @@ GEM aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.4) claide (1.0.3) + clamp (1.3.2) colored (1.2) colored2 (3.1.2) commander-fastlane (4.4.6) highline (~> 1.7.2) + concurrent-ruby (1.1.8) declarative (0.0.20) declarative-option (0.1.0) digest-crc (0.6.3) @@ -134,26 +142,34 @@ GEM http-cookie (1.0.3) domain_name (~> 0.5) httpclient (2.8.3) + i18n (1.8.10) + concurrent-ruby (~> 1.0) jmespath (1.4.0) json (2.5.1) jwt (2.2.2) memoist (0.16.2) mini_magick (4.11.0) mini_mime (1.0.2) + mini_portile2 (2.5.1) + minitest (5.14.4) multi_json (1.15.0) multipart-post (2.0.0) nanaimo (0.3.0) naturally (2.2.1) + nokogiri (1.11.3) + mini_portile2 (~> 2.5.0) + racc (~> 1.4) os (1.1.1) plist (3.6.0) public_suffix (4.0.6) + racc (1.5.2) rake (13.0.3) representable (3.0.4) declarative (< 0.1.0) declarative-option (< 0.2.0) uber (< 0.2.0) retriable (3.1.2) - rexml (3.2.4) + rexml (3.2.5) rouge (2.0.7) ruby2_keywords (0.0.4) rubyzip (2.3.0) @@ -167,6 +183,12 @@ GEM CFPropertyList naturally slack-notifier (2.3.2) + slather (2.7.1) + CFPropertyList (>= 2.2, < 4) + activesupport + clamp (~> 1.3) + nokogiri (~> 1.11) + xcodeproj (~> 1.7) terminal-notifier (2.0.0) terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) @@ -174,6 +196,8 @@ GEM tty-screen (0.8.1) tty-spinner (0.9.3) tty-cursor (~> 0.7) + tzinfo (2.0.4) + concurrent-ruby (~> 1.0) uber (0.1.0) unf (0.1.4) unf_ext @@ -191,12 +215,14 @@ GEM rouge (~> 2.0.7) xcpretty-travis-formatter (1.0.1) xcpretty (~> 0.2, >= 0.0.7) + zeitwerk (2.4.2) PLATFORMS ruby DEPENDENCIES fastlane + slather BUNDLED WITH - 2.1.2 + 2.2.16 From ef93aa401a6716e89f94e8202ae92cbf6a8ce5d9 Mon Sep 17 00:00:00 2001 From: Ravisankar Date: Mon, 3 May 2021 10:47:23 +0530 Subject: [PATCH 21/30] Remove running primary workflow on pushed to master --- .github/workflows/primary.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/primary.yml b/.github/workflows/primary.yml index c1fe53e..adcf76d 100644 --- a/.github/workflows/primary.yml +++ b/.github/workflows/primary.yml @@ -1,7 +1,5 @@ name: Primary on: - push: - branches: [ master ] pull_request: branches: [ master ] From cc0d28f198bff3c7d6805f01a4fe65c509d5a978 Mon Sep 17 00:00:00 2001 From: Peter-John <82087393+Peter-John-paystack@users.noreply.github.com> Date: Mon, 3 May 2021 08:49:14 +0200 Subject: [PATCH 22/30] Adding Github Token as Environment Variable (#48) - Adding Github Token as environment variable as it is required when running on master branch for SonarScanner --- .github/workflows/SonarCloud.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/SonarCloud.yml b/.github/workflows/SonarCloud.yml index 54806b4..8ab2760 100644 --- a/.github/workflows/SonarCloud.yml +++ b/.github/workflows/SonarCloud.yml @@ -36,6 +36,7 @@ jobs: - name: SonarCloud Analysis env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} run: | gem install slather From 7e9078a826bdec191075903a8d7111006abcd11b Mon Sep 17 00:00:00 2001 From: Ahmed Omer Date: Mon, 3 May 2021 15:57:25 +0400 Subject: [PATCH 23/30] Removed availability annotation on init method --- Paystack/PublicHeaders/PSTCKTransaction.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Paystack/PublicHeaders/PSTCKTransaction.h b/Paystack/PublicHeaders/PSTCKTransaction.h index 27f8928..4ea63e8 100644 --- a/Paystack/PublicHeaders/PSTCKTransaction.h +++ b/Paystack/PublicHeaders/PSTCKTransaction.h @@ -15,7 +15,7 @@ /** * You cannot directly instantiate an PSTCKTransaction. You should only use one that has been returned from an PSTCKAPIClient callback. */ -- (nonnull instancetype) init __attribute__((unavailable("You cannot directly instantiate an PSTCKTransaction. You should only use one that has been returned from an PSTCKAPIClient callback."))); +- (nonnull instancetype) init; @property (nonatomic, readonly, nonnull) NSString *reference; @property (nonatomic, readonly, nonnull) NSString *message; From 58b35a4e48ccd69f3d605bc9842857874926759f Mon Sep 17 00:00:00 2001 From: Ahmed Omer Date: Mon, 3 May 2021 16:10:15 +0400 Subject: [PATCH 24/30] Bumped iOS SDK version from 3.0.15 to 3.0.16 --- Paystack.podspec | 2 +- Paystack.xcodeproj/project.pbxproj | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Paystack.podspec b/Paystack.podspec index ea09e45..502c198 100644 --- a/Paystack.podspec +++ b/Paystack.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'Paystack' - s.version = '3.0.15' + s.version = '3.0.16' s.summary = 'Paystack is a web-based API helping African Businesses accept payments online.' s.description = <<-DESC Paystack makes it easy for African Businesses to accept Mastercard, Visa and Verve cards from anyone, anywhere in the world. diff --git a/Paystack.xcodeproj/project.pbxproj b/Paystack.xcodeproj/project.pbxproj index 8bec7cb..954de86 100644 --- a/Paystack.xcodeproj/project.pbxproj +++ b/Paystack.xcodeproj/project.pbxproj @@ -1351,11 +1351,11 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CLANG_ENABLE_MODULES = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - CURRENT_PROJECT_VERSION = 18; + CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MARKETING_VERSION = 3.0.15; + MARKETING_VERSION = 3.0.16; PRODUCT_BUNDLE_IDENTIFIER = "com.paystack.paystack-ios"; SWIFT_OBJC_BRIDGING_HEADER = ""; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -1370,11 +1370,11 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CLANG_ENABLE_MODULES = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - CURRENT_PROJECT_VERSION = 18; + CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MARKETING_VERSION = 3.0.15; + MARKETING_VERSION = 3.0.16; PRODUCT_BUNDLE_IDENTIFIER = "com.paystack.paystack-ios"; SWIFT_OBJC_BRIDGING_HEADER = ""; SWIFT_VERSION = 5.0; From f96a1670bab60fb94c445b0dc8787115e3d502d1 Mon Sep 17 00:00:00 2001 From: Jubril Olambiwonnu <57099127+jubril-paystack@users.noreply.github.com> Date: Tue, 4 May 2021 08:29:02 +0100 Subject: [PATCH 25/30] Bump version and build number --- Paystack/PublicHeaders/PSTCKAPIClient.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Paystack/PublicHeaders/PSTCKAPIClient.h b/Paystack/PublicHeaders/PSTCKAPIClient.h index 50edefd..6cc90d4 100644 --- a/Paystack/PublicHeaders/PSTCKAPIClient.h +++ b/Paystack/PublicHeaders/PSTCKAPIClient.h @@ -8,8 +8,8 @@ #import #endif -static NSString *const __nonnull PSTCKSDKVersion = @"3.0.15"; -static NSString *const __nonnull PSTCKSDKBuild = @"18"; +static NSString *const __nonnull PSTCKSDKVersion = @"3.0.16"; +static NSString *const __nonnull PSTCKSDKBuild = @"1"; @class PSTCKCard, PSTCKCardParams, PSTCKTransactionParams, PSTCKToken, PSTCKState; From aae178f60986813d53f248733aca3d1d616b58a2 Mon Sep 17 00:00:00 2001 From: Peter-John <82087393+Peter-John-paystack@users.noreply.github.com> Date: Wed, 5 May 2021 17:39:37 +0200 Subject: [PATCH 26/30] Update SonarCloud.yml (#54) - Removing trigger to run on master - Adding trigger to run when at 12am everyday --- .github/workflows/SonarCloud.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/SonarCloud.yml b/.github/workflows/SonarCloud.yml index 8ab2760..adccae5 100644 --- a/.github/workflows/SonarCloud.yml +++ b/.github/workflows/SonarCloud.yml @@ -1,8 +1,7 @@ name: SonarCloud on: - push: - branches: - - master + schedule: + - cron: '0 0 * * *' pull_request: types: [opened, synchronize, reopened] jobs: From 4ec0362923143e8223fe88a6ba0d8f0069b0a916 Mon Sep 17 00:00:00 2001 From: Peter-John <82087393+Peter-John-paystack@users.noreply.github.com> Date: Thu, 13 May 2021 08:43:03 +0200 Subject: [PATCH 27/30] Cocoapods Trunk Deployment Workflow (#57) - Created Workflow to deploy pod to trunk. --- .github/workflows/deploy_to_cocoapods.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/workflows/deploy_to_cocoapods.yml diff --git a/.github/workflows/deploy_to_cocoapods.yml b/.github/workflows/deploy_to_cocoapods.yml new file mode 100644 index 0000000..e8183d6 --- /dev/null +++ b/.github/workflows/deploy_to_cocoapods.yml @@ -0,0 +1,19 @@ +name: Deploy to Cocoapods Trunk + +on: + push: + tags: + - '*' + workflow_dispatch: + +jobs: + build: + runs-on: macos-latest + steps: + - uses: actions/checkout@v1 + - name: Install Cocoapods + run: gem install cocoapods + + - uses: michaelhenry/deploy-to-cocoapods-github-action@1.0.10 + env: + COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }} From 3f4937caa7b03fdcd5152b8279ea2fe5647de009 Mon Sep 17 00:00:00 2001 From: Ahmed Omer Date: Thu, 3 Jun 2021 09:09:22 +0400 Subject: [PATCH 28/30] Upgrade nokogiri to version 1.11.7 --- Gemfile.lock | 4 ++-- Paystack.podspec | 2 +- Paystack.xcodeproj/project.pbxproj | 4 ++-- Paystack/PublicHeaders/PSTCKAPIClient.h | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 171038d..1c9499a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -150,13 +150,13 @@ GEM memoist (0.16.2) mini_magick (4.11.0) mini_mime (1.0.2) - mini_portile2 (2.5.1) + mini_portile2 (2.5.3) minitest (5.14.4) multi_json (1.15.0) multipart-post (2.0.0) nanaimo (0.3.0) naturally (2.2.1) - nokogiri (1.11.3) + nokogiri (1.11.7) mini_portile2 (~> 2.5.0) racc (~> 1.4) os (1.1.1) diff --git a/Paystack.podspec b/Paystack.podspec index 502c198..9d4a6d0 100644 --- a/Paystack.podspec +++ b/Paystack.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'Paystack' - s.version = '3.0.16' + s.version = '3.0.17' s.summary = 'Paystack is a web-based API helping African Businesses accept payments online.' s.description = <<-DESC Paystack makes it easy for African Businesses to accept Mastercard, Visa and Verve cards from anyone, anywhere in the world. diff --git a/Paystack.xcodeproj/project.pbxproj b/Paystack.xcodeproj/project.pbxproj index 954de86..14ee8fc 100644 --- a/Paystack.xcodeproj/project.pbxproj +++ b/Paystack.xcodeproj/project.pbxproj @@ -1355,7 +1355,7 @@ DEFINES_MODULE = YES; IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MARKETING_VERSION = 3.0.16; + MARKETING_VERSION = 3.0.17; PRODUCT_BUNDLE_IDENTIFIER = "com.paystack.paystack-ios"; SWIFT_OBJC_BRIDGING_HEADER = ""; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -1374,7 +1374,7 @@ DEFINES_MODULE = YES; IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MARKETING_VERSION = 3.0.16; + MARKETING_VERSION = 3.0.17; PRODUCT_BUNDLE_IDENTIFIER = "com.paystack.paystack-ios"; SWIFT_OBJC_BRIDGING_HEADER = ""; SWIFT_VERSION = 5.0; diff --git a/Paystack/PublicHeaders/PSTCKAPIClient.h b/Paystack/PublicHeaders/PSTCKAPIClient.h index 6cc90d4..80f45ba 100644 --- a/Paystack/PublicHeaders/PSTCKAPIClient.h +++ b/Paystack/PublicHeaders/PSTCKAPIClient.h @@ -8,7 +8,7 @@ #import #endif -static NSString *const __nonnull PSTCKSDKVersion = @"3.0.16"; +static NSString *const __nonnull PSTCKSDKVersion = @"3.0.17"; static NSString *const __nonnull PSTCKSDKBuild = @"1"; @class PSTCKCard, PSTCKCardParams, PSTCKTransactionParams, PSTCKToken, PSTCKState; From 37356c1657bb774eeb3ebb4f91fb67b06d099134 Mon Sep 17 00:00:00 2001 From: Peter-John <82087393+Peter-John-paystack@users.noreply.github.com> Date: Wed, 18 Aug 2021 16:35:19 +0200 Subject: [PATCH 29/30] Update Sonar project properties (#62) * Update sonar project properties to point to correct report file and remove Gcov Co-authored-by: Justin Guedes --- .github/workflows/SonarCloud.yml | 5 +++-- fastlane/Fastfile | 6 +++--- fastlane/README.md | 7 ++++++- sonar-project.properties | 2 +- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/.github/workflows/SonarCloud.yml b/.github/workflows/SonarCloud.yml index adccae5..676df1c 100644 --- a/.github/workflows/SonarCloud.yml +++ b/.github/workflows/SonarCloud.yml @@ -4,6 +4,7 @@ on: - cron: '0 0 * * *' pull_request: types: [opened, synchronize, reopened] + workflow_dispatch: jobs: sonarcloud: name: SonarCloud @@ -27,12 +28,12 @@ jobs: -enableCodeCoverage "YES"\ -destination 'platform=iOS Simulator,name=iPhone 8,OS=14.4'\ test - + - name: Set up JDK 11 uses: actions/setup-java@v1 with: java-version: 11 - + - name: SonarCloud Analysis env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 35c414c..a6e4587 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -38,12 +38,12 @@ desc "Sonar Cloud Scanner" output_directory: "./sonar-reports", devices: "iPhone 8") - slather(cobertura_xml: true, + slather(sonarqube_xml: true, scheme: "PaystackiOS", proj: "./Paystack.xcodeproj", - output_directory: "./sonar-reports", + output_directory: "./code-coverage", workspace: "./Paystack.xcworkspace") - + swiftlint(output_file: "./sonar-reports/swiftlint.txt", ignore_exit_status: true) diff --git a/fastlane/README.md b/fastlane/README.md index 71a72ae..ade99ca 100644 --- a/fastlane/README.md +++ b/fastlane/README.md @@ -26,9 +26,14 @@ Description of what the lane does fastlane ios unit_tests ``` Run unit tests +### ios metrics +``` +fastlane ios metrics +``` +Sonar Cloud Scanner ---- -This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run. +This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run. More information about fastlane can be found on [fastlane.tools](https://fastlane.tools). The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools). diff --git a/sonar-project.properties b/sonar-project.properties index fab4a1f..006ee04 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -9,4 +9,4 @@ sonar.sourceEncoding=UTF-8 sonar.cfamily.threads=1 sonar.cfamily.cache.enabled=false sonar.sources=\Paystack - +sonar.coverageReportPaths=code-coverage/sonarqube-generic-coverage.xml From 84e9c56bfb33b64ef58e633e8f1402819fe05212 Mon Sep 17 00:00:00 2001 From: Peter-John <82087393+Peter-John-paystack@users.noreply.github.com> Date: Thu, 23 Nov 2023 10:36:17 +0200 Subject: [PATCH 30/30] MOB-853 Deprecating SDK in favour of new SDK (#77) * MOB-853 Deprecating SDK in favour of new SDK - Added deprecating tags and messages to app header files that are publicly used by developers * - Updated Bundler with latest gems - Updated workflow action to latest versions for check out and Java --- .github/workflows/SonarCloud.yml | 9 +- .github/workflows/deploy_to_cocoapods.yml | 2 +- .github/workflows/primary.yml | 4 +- Gemfile.lock | 248 ++++++++++-------- Paystack/PublicHeaders/PSTCKAPIClient.h | 8 +- Paystack/PublicHeaders/PSTCKCard.h | 12 +- .../PublicHeaders/PSTCKTransactionParams.h | 12 +- Paystack/UI/PSTCKPaymentCardTextField.m | 2 +- 8 files changed, 161 insertions(+), 136 deletions(-) diff --git a/.github/workflows/SonarCloud.yml b/.github/workflows/SonarCloud.yml index 676df1c..67f68e1 100644 --- a/.github/workflows/SonarCloud.yml +++ b/.github/workflows/SonarCloud.yml @@ -10,7 +10,7 @@ jobs: name: SonarCloud runs-on: macos-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: fetch-depth: 0 - name: Setup environment @@ -29,10 +29,11 @@ jobs: -destination 'platform=iOS Simulator,name=iPhone 8,OS=14.4'\ test - - name: Set up JDK 11 - uses: actions/setup-java@v1 + - name: Set up JDK 17 + uses: actions/setup-java@v3.10.0 with: - java-version: 11 + distribution: 'temurin' + java-version: 17 - name: SonarCloud Analysis env: diff --git a/.github/workflows/deploy_to_cocoapods.yml b/.github/workflows/deploy_to_cocoapods.yml index e8183d6..a3f6923 100644 --- a/.github/workflows/deploy_to_cocoapods.yml +++ b/.github/workflows/deploy_to_cocoapods.yml @@ -10,7 +10,7 @@ jobs: build: runs-on: macos-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 - name: Install Cocoapods run: gem install cocoapods diff --git a/.github/workflows/primary.yml b/.github/workflows/primary.yml index adcf76d..fee6942 100644 --- a/.github/workflows/primary.yml +++ b/.github/workflows/primary.yml @@ -9,7 +9,7 @@ jobs: name: Code quality Checks steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 - name: Danger uses: docker://ghcr.io/danger/danger-swift-with-swiftlint:3.9.1 with: @@ -23,7 +23,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup environment run: | diff --git a/Gemfile.lock b/Gemfile.lock index 1c9499a..2969c4b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,70 +1,96 @@ GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.3) - activesupport (6.1.3.1) + CFPropertyList (3.0.6) + rexml + activesupport (7.1.2) + base64 + bigdecimal concurrent-ruby (~> 1.0, >= 1.0.2) + connection_pool (>= 2.2.5) + drb i18n (>= 1.6, < 2) minitest (>= 5.1) + mutex_m tzinfo (~> 2.0) - zeitwerk (~> 2.3) - addressable (2.7.0) - public_suffix (>= 2.0.2, < 5.0) + addressable (2.8.5) + public_suffix (>= 2.0.2, < 6.0) artifactory (3.0.15) atomos (0.1.3) - aws-eventstream (1.1.1) - aws-partitions (1.434.0) - aws-sdk-core (3.113.0) + aws-eventstream (1.2.0) + aws-partitions (1.850.0) + aws-sdk-core (3.186.0) aws-eventstream (~> 1, >= 1.0.2) - aws-partitions (~> 1, >= 1.239.0) - aws-sigv4 (~> 1.1) - jmespath (~> 1.0) - aws-sdk-kms (1.43.0) - aws-sdk-core (~> 3, >= 3.112.0) + aws-partitions (~> 1, >= 1.651.0) + aws-sigv4 (~> 1.5) + jmespath (~> 1, >= 1.6.1) + aws-sdk-kms (1.72.0) + aws-sdk-core (~> 3, >= 3.184.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.92.0) - aws-sdk-core (~> 3, >= 3.112.0) + aws-sdk-s3 (1.136.0) + aws-sdk-core (~> 3, >= 3.181.0) aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.1) - aws-sigv4 (1.2.3) + aws-sigv4 (~> 1.6) + aws-sigv4 (1.6.1) aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.4) - claide (1.0.3) + base64 (0.2.0) + bigdecimal (3.1.4) + claide (1.1.0) clamp (1.3.2) colored (1.2) colored2 (3.1.2) - commander-fastlane (4.4.6) - highline (~> 1.7.2) - concurrent-ruby (1.1.8) + commander (4.6.0) + highline (~> 2.0.0) + concurrent-ruby (1.2.2) + connection_pool (2.4.1) declarative (0.0.20) - declarative-option (0.1.0) - digest-crc (0.6.3) + digest-crc (0.6.5) rake (>= 12.0.0, < 14.0.0) - domain_name (0.5.20190701) - unf (>= 0.0.5, < 1.0.0) - dotenv (2.7.6) - emoji_regex (3.2.2) - excon (0.79.0) - faraday (1.3.0) - faraday-net_http (~> 1.0) - multipart-post (>= 1.2, < 3) + domain_name (0.6.20231109) + dotenv (2.8.1) + drb (2.2.0) ruby2_keywords + emoji_regex (3.2.3) + excon (0.104.0) + faraday (1.10.3) + faraday-em_http (~> 1.0) + faraday-em_synchrony (~> 1.0) + faraday-excon (~> 1.1) + faraday-httpclient (~> 1.0) + faraday-multipart (~> 1.0) + faraday-net_http (~> 1.0) + faraday-net_http_persistent (~> 1.0) + faraday-patron (~> 1.0) + faraday-rack (~> 1.0) + faraday-retry (~> 1.0) + ruby2_keywords (>= 0.0.4) faraday-cookie_jar (0.0.7) faraday (>= 0.8.0) http-cookie (~> 1.0.0) + faraday-em_http (1.0.0) + faraday-em_synchrony (1.0.0) + faraday-excon (1.1.0) + faraday-httpclient (1.0.1) + faraday-multipart (1.0.4) + multipart-post (~> 2) faraday-net_http (1.0.1) - faraday_middleware (1.0.0) + faraday-net_http_persistent (1.2.0) + faraday-patron (1.0.0) + faraday-rack (1.0.0) + faraday-retry (1.0.3) + faraday_middleware (1.2.0) faraday (~> 1.0) - fastimage (2.2.3) - fastlane (2.178.0) + fastimage (2.2.7) + fastlane (2.217.0) CFPropertyList (>= 2.3, < 4.0.0) - addressable (>= 2.3, < 3.0.0) + addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) aws-sdk-s3 (~> 1.0) babosa (>= 1.0.3, < 2.0.0) bundler (>= 1.12.0, < 3.0.0) colored - commander-fastlane (>= 4.4.6, < 5.0.0) + commander (~> 4.6) dotenv (>= 2.1.1, < 3.0.0) emoji_regex (>= 0.1, < 4.0) excon (>= 0.71.0, < 1.0.0) @@ -73,21 +99,23 @@ GEM faraday_middleware (~> 1.0) fastimage (>= 2.1.0, < 3.0.0) gh_inspector (>= 1.1.2, < 2.0.0) - google-api-client (>= 0.37.0, < 0.39.0) - google-cloud-storage (>= 1.15.0, < 2.0.0) - highline (>= 1.7.2, < 2.0.0) + google-apis-androidpublisher_v3 (~> 0.3) + google-apis-playcustomapp_v1 (~> 0.1) + google-cloud-storage (~> 1.31) + highline (~> 2.0) + http-cookie (~> 1.0.5) json (< 3.0.0) jwt (>= 2.1.0, < 3) mini_magick (>= 4.9.4, < 5.0.0) - multipart-post (~> 2.0.0) + multipart-post (>= 2.0.0, < 3.0.0) naturally (~> 2.2) + optparse (~> 0.1.1) plist (>= 3.1.0, < 4.0.0) rubyzip (>= 2.0.0, < 3.0.0) security (= 0.1.3) simctl (~> 1.6.3) - slack-notifier (>= 2.0.0, < 3.0.0) terminal-notifier (>= 2.0.0, < 3.0.0) - terminal-table (>= 1.4.5, < 2.0.0) + terminal-table (~> 3) tty-screen (>= 0.6.3, < 1.0.0) tty-spinner (>= 0.8.0, < 1.0.0) word_wrap (~> 1.0.0) @@ -95,127 +123,119 @@ GEM xcpretty (~> 0.3.0) xcpretty-travis-formatter (>= 0.0.3) gh_inspector (1.1.3) - google-api-client (0.38.0) - addressable (~> 2.5, >= 2.5.1) - googleauth (~> 0.9) - httpclient (>= 2.8.1, < 3.0) - mini_mime (~> 1.0) - representable (~> 3.0) - retriable (>= 2.0, < 4.0) - signet (~> 0.12) - google-apis-core (0.3.0) + google-apis-androidpublisher_v3 (0.52.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-core (0.11.2) addressable (~> 2.5, >= 2.5.1) - googleauth (~> 0.14) - httpclient (>= 2.8.1, < 3.0) + googleauth (>= 0.16.2, < 2.a) + httpclient (>= 2.8.1, < 3.a) mini_mime (~> 1.0) representable (~> 3.0) - retriable (>= 2.0, < 4.0) + retriable (>= 2.0, < 4.a) rexml - signet (~> 0.14) webrick - google-apis-iamcredentials_v1 (0.2.0) - google-apis-core (~> 0.1) - google-apis-storage_v1 (0.3.0) - google-apis-core (~> 0.1) + google-apis-iamcredentials_v1 (0.17.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-playcustomapp_v1 (0.13.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-storage_v1 (0.29.0) + google-apis-core (>= 0.11.0, < 2.a) google-cloud-core (1.6.0) google-cloud-env (~> 1.0) google-cloud-errors (~> 1.0) - google-cloud-env (1.5.0) - faraday (>= 0.17.3, < 2.0) - google-cloud-errors (1.1.0) - google-cloud-storage (1.31.0) - addressable (~> 2.5) + google-cloud-env (1.6.0) + faraday (>= 0.17.3, < 3.0) + google-cloud-errors (1.3.1) + google-cloud-storage (1.45.0) + addressable (~> 2.8) digest-crc (~> 0.4) google-apis-iamcredentials_v1 (~> 0.1) - google-apis-storage_v1 (~> 0.1) - google-cloud-core (~> 1.2) - googleauth (~> 0.9) + google-apis-storage_v1 (~> 0.29.0) + google-cloud-core (~> 1.6) + googleauth (>= 0.16.2, < 2.a) mini_mime (~> 1.0) - googleauth (0.16.0) - faraday (>= 0.17.3, < 2.0) + googleauth (1.8.1) + faraday (>= 0.17.3, < 3.a) jwt (>= 1.4, < 3.0) - memoist (~> 0.16) multi_json (~> 1.11) os (>= 0.9, < 2.0) - signet (~> 0.14) - highline (1.7.10) - http-cookie (1.0.3) + signet (>= 0.16, < 2.a) + highline (2.0.3) + http-cookie (1.0.5) domain_name (~> 0.5) httpclient (2.8.3) - i18n (1.8.10) + i18n (1.14.1) concurrent-ruby (~> 1.0) - jmespath (1.4.0) - json (2.5.1) - jwt (2.2.2) - memoist (0.16.2) - mini_magick (4.11.0) - mini_mime (1.0.2) - mini_portile2 (2.5.3) - minitest (5.14.4) + jmespath (1.6.2) + json (2.6.3) + jwt (2.7.1) + mini_magick (4.12.0) + mini_mime (1.1.5) + mini_portile2 (2.8.5) + minitest (5.20.0) multi_json (1.15.0) - multipart-post (2.0.0) + multipart-post (2.3.0) + mutex_m (0.2.0) nanaimo (0.3.0) naturally (2.2.1) - nokogiri (1.11.7) - mini_portile2 (~> 2.5.0) + nokogiri (1.15.4) + mini_portile2 (~> 2.8.2) racc (~> 1.4) - os (1.1.1) - plist (3.6.0) - public_suffix (4.0.6) - racc (1.5.2) - rake (13.0.3) - representable (3.0.4) + optparse (0.1.1) + os (1.1.4) + plist (3.7.0) + public_suffix (5.0.3) + racc (1.7.3) + rake (13.1.0) + representable (3.2.0) declarative (< 0.1.0) - declarative-option (< 0.2.0) + trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) retriable (3.1.2) - rexml (3.2.5) + rexml (3.2.6) rouge (2.0.7) - ruby2_keywords (0.0.4) - rubyzip (2.3.0) + ruby2_keywords (0.0.5) + rubyzip (2.3.2) security (0.1.3) - signet (0.15.0) - addressable (~> 2.3) - faraday (>= 0.17.3, < 2.0) + signet (0.18.0) + addressable (~> 2.8) + faraday (>= 0.17.5, < 3.a) jwt (>= 1.5, < 3.0) multi_json (~> 1.10) - simctl (1.6.8) + simctl (1.6.10) CFPropertyList naturally - slack-notifier (2.3.2) - slather (2.7.1) + slather (2.8.0) CFPropertyList (>= 2.2, < 4) activesupport clamp (~> 1.3) - nokogiri (~> 1.11) - xcodeproj (~> 1.7) + nokogiri (>= 1.14.3) + xcodeproj (~> 1.21) terminal-notifier (2.0.0) - terminal-table (1.8.0) - unicode-display_width (~> 1.1, >= 1.1.1) + terminal-table (3.0.2) + unicode-display_width (>= 1.1.1, < 3) + trailblazer-option (0.1.2) tty-cursor (0.7.1) tty-screen (0.8.1) tty-spinner (0.9.3) tty-cursor (~> 0.7) - tzinfo (2.0.4) + tzinfo (2.0.6) concurrent-ruby (~> 1.0) uber (0.1.0) - unf (0.1.4) - unf_ext - unf_ext (0.0.7.7) - unicode-display_width (1.7.0) - webrick (1.7.0) + unicode-display_width (2.5.0) + webrick (1.8.1) word_wrap (1.0.0) - xcodeproj (1.19.0) + xcodeproj (1.23.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) colored2 (~> 3.1) nanaimo (~> 0.3.0) + rexml (~> 3.2.4) xcpretty (0.3.0) rouge (~> 2.0.7) xcpretty-travis-formatter (1.0.1) xcpretty (~> 0.2, >= 0.0.7) - zeitwerk (2.4.2) PLATFORMS ruby @@ -225,4 +245,4 @@ DEPENDENCIES slather BUNDLED WITH - 2.2.16 + 2.4.10 diff --git a/Paystack/PublicHeaders/PSTCKAPIClient.h b/Paystack/PublicHeaders/PSTCKAPIClient.h index 80f45ba..fd08463 100644 --- a/Paystack/PublicHeaders/PSTCKAPIClient.h +++ b/Paystack/PublicHeaders/PSTCKAPIClient.h @@ -78,7 +78,7 @@ typedef void (^PSTCKNotifyCompletionBlock)(void); onViewController:(nonnull UIViewController *)viewController didEndWithError:(nonnull PSTCKErrorCompletionBlock)errorCompletion didRequestValidation:(nonnull PSTCKTransactionCompletionBlock)beforeValidateCompletion - didTransactionSuccess:(nonnull PSTCKTransactionCompletionBlock)successCompletion; + didTransactionSuccess:(nonnull PSTCKTransactionCompletionBlock)successCompletion __attribute__((deprecated("This SDK has been deprecated, Please refer to our new SDK: https://github.com/PaystackHQ/paystack-sdk-ios"))); /// Charges a card using the Paystack API /// @param card The user's card details. Cannot be nil @@ -95,7 +95,7 @@ typedef void (^PSTCKNotifyCompletionBlock)(void); didRequestValidation:(nonnull PSTCKTransactionCompletionBlock)beforeValidateCompletion willPresentDialog:(nonnull PSTCKNotifyCompletionBlock)showingDialogCompletion dismissedDialog:(nonnull PSTCKNotifyCompletionBlock)dialogDismissedCompletion - didTransactionSuccess:(nonnull PSTCKTransactionCompletionBlock)successCompletion; + didTransactionSuccess:(nonnull PSTCKTransactionCompletionBlock)successCompletion __attribute__((deprecated("This SDK has been deprecated, Please refer to our new SDK: https://github.com/PaystackHQ/paystack-sdk-ios"))); - (void) chargeCard:(nonnull PSTCKCardParams *)card forTransaction:(nonnull PSTCKTransactionParams *)transaction @@ -103,8 +103,8 @@ typedef void (^PSTCKNotifyCompletionBlock)(void); didEndWithError:(nonnull PSTCKErrorCompletionBlock)errorCompletion willPresentDialog:(nonnull PSTCKNotifyCompletionBlock)showingDialogCompletion dismissedDialog:(nonnull PSTCKNotifyCompletionBlock)dialogDismissedCompletion - didTransactionSuccess:(nonnull PSTCKTransactionCompletionBlock)successCompletion; + didTransactionSuccess:(nonnull PSTCKTransactionCompletionBlock)successCompletion __attribute__((deprecated("This SDK has been deprecated, Please refer to our new SDK: https://github.com/PaystackHQ/paystack-sdk-ios"))); -- (void) setProcessingStatus:(Boolean)status; +- (void) setProcessingStatus:(Boolean)status __attribute__((deprecated("This SDK has been deprecated, Please refer to our new SDK: https://github.com/PaystackHQ/paystack-sdk-ios"))); @end diff --git a/Paystack/PublicHeaders/PSTCKCard.h b/Paystack/PublicHeaders/PSTCKCard.h index 500b93f..b95d492 100644 --- a/Paystack/PublicHeaders/PSTCKCard.h +++ b/Paystack/PublicHeaders/PSTCKCard.h @@ -67,24 +67,24 @@ typedef NS_ENUM(NSInteger, PSTCKCardFundingType) { /** * The Paystack ID for the card. */ -@property (nonatomic, readonly, nullable) NSString *cardId; +@property (nonatomic, readonly, nullable) NSString *cardId __attribute__((deprecated("This SDK has been deprecated, Please refer to our new SDK: https://github.com/PaystackHQ/paystack-sdk-ios"))); /** * The issuer of the card. */ -@property (nonatomic, readonly) PSTCKCardBrand brand; +@property (nonatomic, readonly) PSTCKCardBrand brand __attribute__((deprecated("This SDK has been deprecated, Please refer to our new SDK: https://github.com/PaystackHQ/paystack-sdk-ios"))); /** * The issuer of the card. * Can be one of "Visa", "American Express", "MasterCard", "Discover", "JCB", "Diners Club", or "Unknown" * @deprecated use "brand" instead. */ -@property (nonatomic, readonly, nonnull) NSString *type __attribute__((deprecated)); +@property (nonatomic, readonly, nonnull) NSString *type __attribute__((deprecated("This SDK has been deprecated, Please refer to our new SDK: https://github.com/PaystackHQ/paystack-sdk-ios"))); /** * The funding source for the card (credit, debit, prepaid, or other) */ -@property (nonatomic, readonly) PSTCKCardFundingType funding; +@property (nonatomic, readonly) PSTCKCardFundingType funding __attribute__((deprecated("This SDK has been deprecated, Please refer to our new SDK: https://github.com/PaystackHQ/paystack-sdk-ios"))); /** * A proxy for the card's number, this uniquely identifies the credit card and can be used to compare different cards. @@ -95,12 +95,12 @@ typedef NS_ENUM(NSInteger, PSTCKCardFundingType) { /** * Two-letter ISO code representing the issuing country of the card. */ -@property (nonatomic, readonly, nullable) NSString *country; +@property (nonatomic, readonly, nullable) NSString *country __attribute__((deprecated("This SDK has been deprecated, Please refer to our new SDK: https://github.com/PaystackHQ/paystack-sdk-ios"))); /** * This is only applicable when tokenizing debit cards to issue payouts to managed accounts. You should not set it otherwise. The card can then be used as a transfer destination for funds in this currency. */ -@property (nonatomic, copy, nullable) NSString *currency; +@property (nonatomic, copy, nullable) NSString *currency __attribute__((deprecated("This SDK has been deprecated, Please refer to our new SDK: https://github.com/PaystackHQ/paystack-sdk-ios"))); #pragma mark - deprecated properties diff --git a/Paystack/PublicHeaders/PSTCKTransactionParams.h b/Paystack/PublicHeaders/PSTCKTransactionParams.h index 85b31db..4b2d0f2 100644 --- a/Paystack/PublicHeaders/PSTCKTransactionParams.h +++ b/Paystack/PublicHeaders/PSTCKTransactionParams.h @@ -24,17 +24,21 @@ - (nullable PSTCKTransactionParams *) setMetadataValue:(nonnull NSString*)value forKey:(nonnull NSString*)key - error:(NSError * _Nullable __autoreleasing * _Nonnull) error; + error:(NSError * _Nullable __autoreleasing * _Nonnull) error +__attribute__((deprecated("This SDK has been deprecated, Please refer to our new SDK: https://github.com/PaystackHQ/paystack-sdk-ios"))); - (nullable PSTCKTransactionParams *) setMetadataValueDict:(nonnull NSMutableDictionary*)dict forKey:(nonnull NSString*)key - error:(NSError * _Nullable __autoreleasing * _Nonnull) error; + error:(NSError * _Nullable __autoreleasing * _Nonnull) error +__attribute__((deprecated("This SDK has been deprecated, Please refer to our new SDK: https://github.com/PaystackHQ/paystack-sdk-ios"))); - (nullable PSTCKTransactionParams *) setMetadataValueArray:(nonnull NSMutableArray*)arr forKey:(nonnull NSString*)key - error:(NSError * _Nullable __autoreleasing * _Nonnull) error; + error:(NSError * _Nullable __autoreleasing * _Nonnull) error +__attribute__((deprecated("This SDK has been deprecated, Please refer to our new SDK: https://github.com/PaystackHQ/paystack-sdk-ios"))); - (nullable PSTCKTransactionParams *) setCustomFieldValue:(nonnull NSString*)value displayedAs:(nonnull NSString*)display_name - error:(NSError * _Nullable __autoreleasing * _Nonnull) error; + error:(NSError * _Nullable __autoreleasing * _Nonnull) error +__attribute__((deprecated("This SDK has been deprecated, Please refer to our new SDK: https://github.com/PaystackHQ/paystack-sdk-ios"))); @end diff --git a/Paystack/UI/PSTCKPaymentCardTextField.m b/Paystack/UI/PSTCKPaymentCardTextField.m index 9b95968..0e326fa 100644 --- a/Paystack/UI/PSTCKPaymentCardTextField.m +++ b/Paystack/UI/PSTCKPaymentCardTextField.m @@ -13,7 +13,7 @@ #import "UIImage+Paystack.h" #define FAUXPAS_IGNORED_IN_METHOD(...) - +__deprecated @interface PSTCKPaymentCardTextField() @property(nonatomic, readwrite, strong)PSTCKFormTextField *sizingField;