diff --git a/Example-app.xcodeproj/project.pbxproj b/Example-app.xcodeproj/project.pbxproj index ae9a5e1..c430d81 100644 --- a/Example-app.xcodeproj/project.pbxproj +++ b/Example-app.xcodeproj/project.pbxproj @@ -7,7 +7,17 @@ objects = { /* Begin PBXBuildFile section */ + 04EE79822AC1B657002ACDAA /* CodeScanner in Frameworks */ = {isa = PBXBuildFile; productRef = 04EE79812AC1B657002ACDAA /* CodeScanner */; }; + 04EE79842AC1D07E002ACDAA /* ScanUrl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04EE79832AC1D07E002ACDAA /* ScanUrl.swift */; }; + 04EE79872AC2A938002ACDAA /* IconButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04EE79862AC2A938002ACDAA /* IconButton.swift */; }; 63E65DE526EB562C002D116E /* Example_app_UITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63E65DE426EB562C002D116E /* Example_app_UITests.swift */; }; + 890702972A053FDE0027D7A8 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 890702952A053FDE0027D7A8 /* Localizable.strings */; }; + 8907029A2A0541E60027D7A8 /* String+Localize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 890702992A0541E60027D7A8 /* String+Localize.swift */; }; + 8907029C2A0A20030027D7A8 /* StorageHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8907029B2A0A20020027D7A8 /* StorageHelper.swift */; }; + 890702A02A0A23ED0027D7A8 /* StandaloneUrlView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8907029F2A0A23ED0027D7A8 /* StandaloneUrlView.swift */; }; + 890702A22A0A24820027D7A8 /* PaymentAlternativesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 890702A12A0A24820027D7A8 /* PaymentAlternativesViewController.swift */; }; + 890702A42A0A2CC50027D7A8 /* StandaloneUrlViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 890702A32A0A2CC50027D7A8 /* StandaloneUrlViewModel.swift */; }; + 89919A382A0140C60011ADF0 /* SwedbankPayConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89919A372A0140C60011ADF0 /* SwedbankPayConfiguration.swift */; }; A548FFB3241BCC76007C7B17 /* EnvironmentSettingsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A548FFB2241BCC76007C7B17 /* EnvironmentSettingsCell.swift */; }; A569986524113FF100162D69 /* ConsumerSettingsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A569986424113FF100162D69 /* ConsumerSettingsCell.swift */; }; A56998672417C3B000162D69 /* SettingsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A56998662417C3B000162D69 /* SettingsCell.swift */; }; @@ -17,7 +27,6 @@ A58DAF0724053A5A004F9AF2 /* ShoppingCartHeaderCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A58DAF0624053A5A004F9AF2 /* ShoppingCartHeaderCell.swift */; }; A5AD795B26664B7800DE62B1 /* StyleSettingsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5AD795A26664B7800DE62B1 /* StyleSettingsCell.swift */; }; A5AD795F2666744700DE62B1 /* StyleParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5AD795E2666744700DE62B1 /* StyleParser.swift */; }; - A5BE1E6F25791BAB00336A79 /* EnvironmentOptionView.xib in Resources */ = {isa = PBXBuildFile; fileRef = A5BE1E6D25791BAB00336A79 /* EnvironmentOptionView.xib */; }; A5BE1E7225791BC400336A79 /* EnvironmentOptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5BE1E7125791BC400336A79 /* EnvironmentOptionView.swift */; }; A5C24B5A257E5D8B00D832D9 /* PayerOwnedPaymentTokensViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5C24B59257E5D8B00D832D9 /* PayerOwnedPaymentTokensViewController.swift */; }; A5C24B5D257E6A3300D832D9 /* PaymentTokenCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5C24B5C257E6A3300D832D9 /* PaymentTokenCell.swift */; }; @@ -97,6 +106,8 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 04EE79832AC1D07E002ACDAA /* ScanUrl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanUrl.swift; sourceTree = ""; }; + 04EE79862AC2A938002ACDAA /* IconButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconButton.swift; sourceTree = ""; }; 6321C1A9274F8279009FC005 /* deploy_new_version.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = deploy_new_version.yml; sourceTree = ""; }; 639094BB27550F8F00B8A6CB /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; 639094BE2755189100B8A6CB /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; @@ -118,6 +129,14 @@ 63E65DE226EB562C002D116E /* Example-app-UITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Example-app-UITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 63E65DE426EB562C002D116E /* Example_app_UITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Example_app_UITests.swift; sourceTree = ""; }; 63E65DE626EB562C002D116E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 890702962A053FDE0027D7A8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Localizable.strings; sourceTree = ""; }; + 890702982A053FE50027D7A8 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = ../en.lproj/Localizable.strings; sourceTree = ""; }; + 890702992A0541E60027D7A8 /* String+Localize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Localize.swift"; sourceTree = ""; }; + 8907029B2A0A20020027D7A8 /* StorageHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageHelper.swift; sourceTree = ""; }; + 8907029F2A0A23ED0027D7A8 /* StandaloneUrlView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StandaloneUrlView.swift; sourceTree = ""; }; + 890702A12A0A24820027D7A8 /* PaymentAlternativesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentAlternativesViewController.swift; sourceTree = ""; }; + 890702A32A0A2CC50027D7A8 /* StandaloneUrlViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StandaloneUrlViewModel.swift; sourceTree = ""; }; + 89919A372A0140C60011ADF0 /* SwedbankPayConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwedbankPayConfiguration.swift; sourceTree = ""; }; A548FFB2241BCC76007C7B17 /* EnvironmentSettingsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnvironmentSettingsCell.swift; sourceTree = ""; }; A569986424113FF100162D69 /* ConsumerSettingsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsumerSettingsCell.swift; sourceTree = ""; }; A56998662417C3B000162D69 /* SettingsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsCell.swift; sourceTree = ""; }; @@ -209,6 +228,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 04EE79822AC1B657002ACDAA /* CodeScanner in Frameworks */, A569C75326D4CB2400B2237C /* SwedbankPaySDKMerchantBackend in Frameworks */, A569C75126D4CB2400B2237C /* SwedbankPaySDK in Frameworks */, ); @@ -217,6 +237,14 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 04EE79852AC2A8DD002ACDAA /* ccomponents */ = { + isa = PBXGroup; + children = ( + 04EE79862AC2A938002ACDAA /* IconButton.swift */, + ); + path = ccomponents; + sourceTree = ""; + }; 6332462C26EF820C00FB705A /* .github */ = { isa = PBXGroup; children = ( @@ -282,6 +310,15 @@ path = "Example-app-UITests"; sourceTree = ""; }; + 89919A2E29FC0D3F0011ADF0 /* Utilities */ = { + isa = PBXGroup; + children = ( + 89919A372A0140C60011ADF0 /* SwedbankPayConfiguration.swift */, + 8907029B2A0A20020027D7A8 /* StorageHelper.swift */, + ); + path = Utilities; + sourceTree = ""; + }; A5AD795D2666743400DE62B1 /* Utils */ = { isa = PBXGroup; children = ( @@ -324,7 +361,8 @@ A5AA1D6D23967AE4008A62CC /* Example-app.entitlements */, C54D556923701F9D00E3B301 /* Info.plist */, C54D556A23701F9D00E3B301 /* AppDelegate.swift */, - C54D557023701F9D00E3B301 /* en.lproj */, + C54D557023701F9D00E3B301 /* Base.lproj */, + 89919A2E29FC0D3F0011ADF0 /* Utilities */, C54D556423701F9D00E3B301 /* Extensions */, C54D557623701F9D00E3B301 /* Models */, C54D558123701F9D00E3B301 /* Resources */, @@ -350,6 +388,7 @@ D50E22C928995E0F007D8E83 /* ColorExtensions.swift */, D50E22CB28996D0F007D8E83 /* TransitionExtensions.swift */, D50E22D5289AF699007D8E83 /* ToggleStyle.swift */, + 890702992A0541E60027D7A8 /* String+Localize.swift */, ); path = Extensions; sourceTree = ""; @@ -362,19 +401,21 @@ C54D556D23701F9D00E3B301 /* PaymentViewModel.swift */, C54D556E23701F9D00E3B301 /* StoreViewModel.swift */, D50E22CD289AAD62007D8E83 /* StoreViewModelObserved.swift */, + 890702A32A0A2CC50027D7A8 /* StandaloneUrlViewModel.swift */, ); path = ViewModels; sourceTree = ""; }; - C54D557023701F9D00E3B301 /* en.lproj */ = { + C54D557023701F9D00E3B301 /* Base.lproj */ = { isa = PBXGroup; children = ( C54D557123701F9D00E3B301 /* LaunchScreen.xib */, A5BE1E6D25791BAB00336A79 /* EnvironmentOptionView.xib */, C54D557323701F9D00E3B301 /* Main.storyboard */, C54D55B92370204000E3B301 /* ProductTableViewCell.xib */, + 890702952A053FDE0027D7A8 /* Localizable.strings */, ); - path = en.lproj; + path = Base.lproj; sourceTree = ""; }; C54D557623701F9D00E3B301 /* Models */ = { @@ -387,6 +428,7 @@ C50CF6B6237AB15C003F79DF /* Product.swift */, C50CF6AA237AB10A003F79DF /* PurchaseData.swift */, C50CF6AC237AB119003F79DF /* PurchaseItem.swift */, + 04EE79832AC1D07E002ACDAA /* ScanUrl.swift */, ); path = Models; sourceTree = ""; @@ -410,6 +452,7 @@ A5BE1E7125791BC400336A79 /* EnvironmentOptionView.swift */, A5C24B59257E5D8B00D832D9 /* PayerOwnedPaymentTokensViewController.swift */, A5C24B5C257E6A3300D832D9 /* PaymentTokenCell.swift */, + 890702A12A0A24820027D7A8 /* PaymentAlternativesViewController.swift */, ); path = ViewControllers; sourceTree = ""; @@ -448,6 +491,7 @@ D5D6E2A328742B14008E75D7 /* Views */ = { isa = PBXGroup; children = ( + 04EE79852AC2A8DD002ACDAA /* ccomponents */, D5D6E2A428742BB7008E75D7 /* LogView.swift */, D5D6E2D5287EBD52008E75D7 /* GeneralSettings.swift */, D5D6E2DF287F4DF0008E75D7 /* PayerReferenceSettings.swift */, @@ -461,6 +505,7 @@ D50E22D7289BB013007D8E83 /* EnvironmentSettingsView.swift */, D50E22D9289BEE38007D8E83 /* ConsumerSettingsView.swift */, D554F1AF28AA5E8B007867A4 /* StyleSettings.swift */, + 8907029F2A0A23ED0027D7A8 /* StandaloneUrlView.swift */, ); path = Views; sourceTree = ""; @@ -502,6 +547,7 @@ packageProductDependencies = ( A569C75026D4CB2400B2237C /* SwedbankPaySDK */, A569C75226D4CB2400B2237C /* SwedbankPaySDKMerchantBackend */, + 04EE79812AC1B657002ACDAA /* CodeScanner */, ); productName = "Swedbank Pay SDK Example"; productReference = C54D554C23701E6B00E3B301 /* Example-app.app */; @@ -538,6 +584,7 @@ mainGroup = C54D554323701E6B00E3B301; packageReferences = ( A569C74F26D4CB2400B2237C /* XCRemoteSwiftPackageReference "swedbank-pay-sdk-ios" */, + 04EE79802AC1B657002ACDAA /* XCRemoteSwiftPackageReference "CodeScanner" */, ); productRefGroup = C54D554D23701E6B00E3B301 /* Products */; projectDirPath = ""; @@ -571,7 +618,7 @@ C54D55AF23701F9D00E3B301 /* IBMPlexMono-ExtraLightItalic.ttf in Resources */, C54D559D23701F9D00E3B301 /* LaunchScreen.xib in Resources */, C54D55B123701F9D00E3B301 /* IBMPlexMono-Regular.ttf in Resources */, - A5BE1E6F25791BAB00336A79 /* EnvironmentOptionView.xib in Resources */, + 890702972A053FDE0027D7A8 /* Localizable.strings in Resources */, C54D55AB23701F9D00E3B301 /* IBMPlexMono-MediumItalic.ttf in Resources */, C54D55B723701F9D00E3B301 /* IBMPlexMono-LightItalic.ttf in Resources */, C54D55A823701F9D00E3B301 /* Images.xcassets in Resources */, @@ -611,6 +658,7 @@ D50E22CE289AAD62007D8E83 /* StoreViewModelObserved.swift in Sources */, D5D6E2DC287EFF44008E75D7 /* ViewExtensions.swift in Sources */, A56998672417C3B000162D69 /* SettingsCell.swift in Sources */, + 890702A42A0A2CC50027D7A8 /* StandaloneUrlViewModel.swift in Sources */, C50CF6AB237AB10A003F79DF /* PurchaseData.swift in Sources */, C54D559523701F9D00E3B301 /* UIView+Extension.swift in Sources */, D50E22CA28995E0F007D8E83 /* ColorExtensions.swift in Sources */, @@ -625,6 +673,7 @@ D554F1B028AA5E8B007867A4 /* StyleSettings.swift in Sources */, C54D559323701F9D00E3B301 /* UIFont+Extension.swift in Sources */, C50CF6B3237AB149003F79DF /* Country.swift in Sources */, + 890702A22A0A24820027D7A8 /* PaymentAlternativesViewController.swift in Sources */, D50E22D2289AAE58007D8E83 /* ProductListView.swift in Sources */, C50CF6AF237AB126003F79DF /* PaymentResult.swift in Sources */, D5D6E2E2288191DA008E75D7 /* PaymentTokenSettings.swift in Sources */, @@ -633,6 +682,7 @@ C50CF6B7237AB15C003F79DF /* Product.swift in Sources */, C54D55A723701F9D00E3B301 /* ShoppingCartProductTableViewCell.swift in Sources */, D50E22D8289BB013007D8E83 /* EnvironmentSettingsView.swift in Sources */, + 04EE79872AC2A938002ACDAA /* IconButton.swift in Sources */, D50E22D6289AF699007D8E83 /* ToggleStyle.swift in Sources */, D5083C3D2897FAEE00F4A307 /* ShoppingBasketView.swift in Sources */, C54D55A623701F9D00E3B301 /* ShoppingCartSummaryFooterView.swift in Sources */, @@ -644,12 +694,17 @@ D5D6E2DE287F4DAA008E75D7 /* ImageExtensions.swift in Sources */, D50E22DA289BEE38007D8E83 /* ConsumerSettingsView.swift in Sources */, D5D6E2DA287EFF23008E75D7 /* TextExtensions.swift in Sources */, + 8907029A2A0541E60027D7A8 /* String+Localize.swift in Sources */, D5D6E2D6287EBD52008E75D7 /* GeneralSettings.swift in Sources */, C54D55A323701F9D00E3B301 /* ShoppingCartViewController.swift in Sources */, D5D6E2E6288195F3008E75D7 /* InstrumentModeSelector.swift in Sources */, C54D55A123701F9D00E3B301 /* StoreViewController.swift in Sources */, + 8907029C2A0A20030027D7A8 /* StorageHelper.swift in Sources */, + 04EE79842AC1D07E002ACDAA /* ScanUrl.swift in Sources */, + 890702A02A0A23ED0027D7A8 /* StandaloneUrlView.swift in Sources */, C54D559623701F9D00E3B301 /* UINavigationController+Extension.swift in Sources */, D50E22D4289ABA97007D8E83 /* ShoppingCartTable.swift in Sources */, + 89919A382A0140C60011ADF0 /* SwedbankPayConfiguration.swift in Sources */, C54D55A223701F9D00E3B301 /* ResultViewController.swift in Sources */, D5D6E2A528742BB7008E75D7 /* LogView.swift in Sources */, C50CF6AD237AB119003F79DF /* PurchaseItem.swift in Sources */, @@ -667,6 +722,15 @@ /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ + 890702952A053FDE0027D7A8 /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + 890702962A053FDE0027D7A8 /* Base */, + 890702982A053FE50027D7A8 /* en */, + ); + name = Localizable.strings; + sourceTree = ""; + }; A5BE1E6D25791BAB00336A79 /* EnvironmentOptionView.xib */ = { isa = PBXVariantGroup; children = ( @@ -873,7 +937,7 @@ DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = MUD77FXNE9; INFOPLIST_FILE = "Example-app/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -901,7 +965,7 @@ DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = MUD77FXNE9; INFOPLIST_FILE = "Example-app/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -949,6 +1013,14 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ + 04EE79802AC1B657002ACDAA /* XCRemoteSwiftPackageReference "CodeScanner" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/twostraws/CodeScanner"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.0.0; + }; + }; A569C74F26D4CB2400B2237C /* XCRemoteSwiftPackageReference "swedbank-pay-sdk-ios" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/SwedbankPay/swedbank-pay-sdk-ios.git"; @@ -960,6 +1032,11 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 04EE79812AC1B657002ACDAA /* CodeScanner */ = { + isa = XCSwiftPackageProductDependency; + package = 04EE79802AC1B657002ACDAA /* XCRemoteSwiftPackageReference "CodeScanner" */; + productName = CodeScanner; + }; A569C75026D4CB2400B2237C /* SwedbankPaySDK */ = { isa = XCSwiftPackageProductDependency; package = A569C74F26D4CB2400B2237C /* XCRemoteSwiftPackageReference "swedbank-pay-sdk-ios" */; diff --git a/Example-app/Base.lproj/Localizable.strings b/Example-app/Base.lproj/Localizable.strings new file mode 100644 index 0000000..31765cc --- /dev/null +++ b/Example-app/Base.lproj/Localizable.strings @@ -0,0 +1,14 @@ +//General +"general_checkout" = "Checkout"; + +// Stand-alone URL View Controller +"stand_alone_url_payment_successful" = "Payment completed"; +"stand_alone_url_payment_cancelled" = "Payment cancelled"; + +"stand_alone_url_payment_view_checkout_url" = "View checkout URL"; +"stand_alone_url_payment_base_url" = "Base URL"; +"stand_alone_url_payment_complete_url" = "Complete URL"; +"stand_alone_url_payment_cancel_url" = "Cancel URL"; +"stand_alone_url_payment_checkout_v3" = "Use Checkout V3"; +"stand_alone_url_payment_payment_url" = "Payment URL"; +"stand_alone_url_payment_payment_url_scheme" = "swedbankexample://"; diff --git a/Example-app/Base.lproj/Main.storyboard b/Example-app/Base.lproj/Main.storyboard index 862ab83..45c6648 100644 --- a/Example-app/Base.lproj/Main.storyboard +++ b/Example-app/Base.lproj/Main.storyboard @@ -1,9 +1,9 @@ - + - + @@ -29,7 +29,7 @@ - + @@ -45,12 +45,75 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -61,7 +124,7 @@ - + @@ -130,7 +193,7 @@ - + @@ -166,7 +229,7 @@ - + @@ -178,7 +241,7 @@ - + @@ -226,7 +289,7 @@ - + @@ -297,7 +360,7 @@ - + @@ -383,7 +446,7 @@ - + @@ -449,7 +512,7 @@ - + @@ -634,7 +697,7 @@ - + @@ -880,7 +943,7 @@ - + @@ -950,20 +1013,20 @@ - + - + @@ -1617,18 +1680,18 @@ - + - + @@ -1692,7 +1755,7 @@ @@ -1738,7 +1801,7 @@ - + @@ -1780,7 +1843,7 @@ - + @@ -1826,7 +1889,7 @@ - + @@ -1875,7 +1938,7 @@ - + @@ -1923,7 +1986,7 @@ - + @@ -1965,7 +2028,17 @@ - + + + + + + + + + + + @@ -1976,7 +2049,7 @@ - + diff --git a/Example-app/Extensions/String+Localize.swift b/Example-app/Extensions/String+Localize.swift new file mode 100644 index 0000000..720cecc --- /dev/null +++ b/Example-app/Extensions/String+Localize.swift @@ -0,0 +1,11 @@ +import Foundation + +extension String { + var localize: String { + return NSLocalizedString(self, comment: "") + } + + func localize(_ arguments: CVarArg...) -> String { + return String(format: self.localize, arguments: arguments) + } +} diff --git a/Example-app/Images.xcassets/Contents.json b/Example-app/Images.xcassets/Contents.json index da4a164..73c0059 100644 --- a/Example-app/Images.xcassets/Contents.json +++ b/Example-app/Images.xcassets/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Example-app/Images.xcassets/payment_failed_icon.imageset/Contents.json b/Example-app/Images.xcassets/payment_failed_icon.imageset/Contents.json new file mode 100644 index 0000000..fd0e53a --- /dev/null +++ b/Example-app/Images.xcassets/payment_failed_icon.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "download-2.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "original" + } +} diff --git a/Example-app/Images.xcassets/payment_failed_icon.imageset/download-2.png b/Example-app/Images.xcassets/payment_failed_icon.imageset/download-2.png new file mode 100644 index 0000000..7f640f1 Binary files /dev/null and b/Example-app/Images.xcassets/payment_failed_icon.imageset/download-2.png differ diff --git a/Example-app/Images.xcassets/payment_success_icon.imageset/Contents.json b/Example-app/Images.xcassets/payment_success_icon.imageset/Contents.json new file mode 100644 index 0000000..952a18e --- /dev/null +++ b/Example-app/Images.xcassets/payment_success_icon.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "download.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "original" + } +} diff --git a/Example-app/Images.xcassets/payment_success_icon.imageset/download.png b/Example-app/Images.xcassets/payment_success_icon.imageset/download.png new file mode 100644 index 0000000..9eb97a8 Binary files /dev/null and b/Example-app/Images.xcassets/payment_success_icon.imageset/download.png differ diff --git a/Example-app/Info.plist b/Example-app/Info.plist index d6bb7b7..f303f26 100644 --- a/Example-app/Info.plist +++ b/Example-app/Info.plist @@ -39,6 +39,8 @@ LSRequiresIPhoneOS + NSCameraUsageDescription + Scanning QR codes UIAppFonts IBMPlexMono-Medium.ttf diff --git a/Example-app/Models/ScanUrl.swift b/Example-app/Models/ScanUrl.swift new file mode 100644 index 0000000..6260648 --- /dev/null +++ b/Example-app/Models/ScanUrl.swift @@ -0,0 +1,23 @@ +enum ScanUrl: String { + case checkout + case base + case complete + case cancel + case payment + case unknown + + func toKey() -> StorageHelper.Key? { + switch self { + case .base: + return .baseUrl + case .complete: + return .completeUrl + case .cancel: + return .cancelUrl + case .payment: + return .paymentUrl + default: + return nil + } + } +} diff --git a/Example-app/Utilities/StorageHelper.swift b/Example-app/Utilities/StorageHelper.swift new file mode 100644 index 0000000..a430f75 --- /dev/null +++ b/Example-app/Utilities/StorageHelper.swift @@ -0,0 +1,29 @@ +import Foundation + +struct StorageHelper { + + enum Key: String, CaseIterable { + case baseUrl + case completeUrl + case cancelUrl + case useCheckoutV3 + case paymentUrl + + var storageString: String { + return "Storage\(self.rawValue)" + } + } + + static let shared = StorageHelper() + + private let store = UserDefaults.standard + + // MARK: Generic methods + func save(value: T, forKey key: Key) { + store.set(value, forKey: key.storageString) + } + + func value(for key: Key) -> T? { + return store.value(forKey: key.storageString) as? T + } +} diff --git a/Example-app/Utilities/SwedbankPayConfiguration.swift b/Example-app/Utilities/SwedbankPayConfiguration.swift new file mode 100644 index 0000000..59dbaf1 --- /dev/null +++ b/Example-app/Utilities/SwedbankPayConfiguration.swift @@ -0,0 +1,36 @@ +import Foundation +import SwedbankPaySDK + +enum SwedbankPayConfigurationError: Error { + case notImplemented +} + +class SwedbankPayConfiguration { + let orderInfo: SwedbankPaySDK.ViewPaymentOrderInfo + + init(isV3: Bool = true, webViewBaseURL: URL?, + viewPaymentLink: URL, completeUrl: URL, cancelUrl: URL?, + paymentUrl: URL? = nil, termsOfServiceUrl: URL? = nil) { + self.orderInfo = SwedbankPaySDK.ViewPaymentOrderInfo( + isV3: isV3, + webViewBaseURL: webViewBaseURL, + viewPaymentLink: viewPaymentLink, + completeUrl: completeUrl, + cancelUrl: cancelUrl, + paymentUrl: paymentUrl, + termsOfServiceUrl: termsOfServiceUrl + ) + } +} + +extension SwedbankPayConfiguration: SwedbankPaySDKConfiguration { + + // This delegate method is not used but required + func postConsumers(consumer: SwedbankPaySDK.Consumer?, userData: Any?, completion: @escaping (Result) -> Void) { + completion(.failure(SwedbankPayConfigurationError.notImplemented)) + } + + func postPaymentorders(paymentOrder: SwedbankPaySDK.PaymentOrder?, userData: Any?, consumerProfileRef: String?, options: SwedbankPaySDK.VersionOptions, completion: @escaping (Result) -> Void) { + completion(.success(orderInfo)) + } +} diff --git a/Example-app/ViewControllers/PaymentAlternativesViewController.swift b/Example-app/ViewControllers/PaymentAlternativesViewController.swift new file mode 100644 index 0000000..1823882 --- /dev/null +++ b/Example-app/ViewControllers/PaymentAlternativesViewController.swift @@ -0,0 +1,10 @@ +import Foundation +import SwiftUI +import UIKit + +class PaymentAlternativesViewController: UIViewController { + + @IBSegueAction func showStandaloneUrlView(_ coder: NSCoder) -> UIViewController? { + return UIHostingController(coder: coder, rootView: StandaloneUrlView()) + } +} diff --git a/Example-app/ViewModels/StandaloneUrlViewModel.swift b/Example-app/ViewModels/StandaloneUrlViewModel.swift new file mode 100644 index 0000000..d1887a1 --- /dev/null +++ b/Example-app/ViewModels/StandaloneUrlViewModel.swift @@ -0,0 +1,85 @@ +import Foundation +import SwedbankPaySDK + +extension StandaloneUrlView { + class StandaloneUrlViewModel: ObservableObject, SwedbankPaySDKDelegate { + @Published var viewCheckoutUrl: String = "" + @Published var baseUrl: String + @Published var completeUrl: String + @Published var cancelUrl: String + @Published var useCheckoutV3: Bool + + @Published var paymentUrlAuthorityAndPath: String + @Published var paymentUrlScheme: String + + @Published var displaySwedbankPayController: Bool = false + @Published var displayScannerSheet: Bool = false + + @Published var paymentResultIcon: String? + @Published var paymentResultMessage: String? + + init() { + baseUrl = String(StorageHelper.shared.value(for: .baseUrl) ?? "") + completeUrl = String(StorageHelper.shared.value(for: .completeUrl) ?? "") + cancelUrl = String(StorageHelper.shared.value(for: .cancelUrl) ?? "") + useCheckoutV3 = Bool(StorageHelper.shared.value(for: .useCheckoutV3) ?? true) + paymentUrlAuthorityAndPath = String(StorageHelper.shared.value(for: .paymentUrl) ?? "") + paymentUrlScheme = "stand_alone_url_payment_payment_url_scheme".localize + } + + var isCheckoutEnabled: Bool { + return !viewCheckoutUrl.isEmpty && !completeUrl.isEmpty + } + + func configurePayment() -> SwedbankPayConfiguration? { + guard let viewCheckoutUrl = URL(string: viewCheckoutUrl), let completeUrl = URL(string: completeUrl) else { + return nil + } + + StorageHelper.shared.save(value: baseUrl, forKey: .baseUrl) + StorageHelper.shared.save(value: self.completeUrl, forKey: .completeUrl) + StorageHelper.shared.save(value: cancelUrl, forKey: .cancelUrl) + StorageHelper.shared.save(value: useCheckoutV3, forKey: .useCheckoutV3) + StorageHelper.shared.save(value: paymentUrlAuthorityAndPath, forKey: .paymentUrl) + + let paymentUrl = paymentUrlScheme+paymentUrlAuthorityAndPath + + let configuration = SwedbankPayConfiguration( + isV3: useCheckoutV3, + webViewBaseURL: URL(string: baseUrl), + viewPaymentLink: viewCheckoutUrl, + completeUrl: completeUrl, + cancelUrl: URL(string: cancelUrl), + paymentUrl: URL(string: paymentUrl) + ) + + return configuration + } + + func saveUrl(urlType: ScanUrl, url: String) { + if let key = urlType.toKey() { + StorageHelper.shared.save(value: url, forKey: key) + } + } + + private func setPaymentResult(success: Bool, resultText: String) { + paymentResultIcon = success ? "payment_success_icon" : "payment_failed_icon" + + paymentResultMessage = resultText + + displaySwedbankPayController = false + } + + func paymentComplete() { + setPaymentResult(success: true, resultText: "stand_alone_url_payment_successful".localize) + } + + func paymentFailed(error: Error) { + setPaymentResult(success: false, resultText: error.localizedDescription) + } + + func paymentCanceled() { + setPaymentResult(success: false, resultText: "stand_alone_url_payment_cancelled".localize) + } + } +} diff --git a/Example-app/Views/StandaloneUrlView.swift b/Example-app/Views/StandaloneUrlView.swift new file mode 100644 index 0000000..1d5e35e --- /dev/null +++ b/Example-app/Views/StandaloneUrlView.swift @@ -0,0 +1,249 @@ +// +// StandaloneUrlView.swift +// Example-app +// +// Created by Andreas Petersson on 2023-05-09. +// + +import SwiftUI +import SwedbankPaySDK +import CodeScanner + +struct StandaloneUrlView: View { + @StateObject private var viewModel = StandaloneUrlViewModel() + @FocusState private var isFocused: Bool + + @State var latestClickedUrl: ScanUrl = .unknown + + var scannerSheet : some View { + CodeScannerView( + codeTypes: [.qr], + completion: { result in + if case let .success(code) = result { + switch self.latestClickedUrl { + case .checkout: + viewModel.viewCheckoutUrl = code.string + break + case .base: + viewModel.baseUrl = code.string + saveEnteredUrl(scanUrl: .base) + break + case .complete: + viewModel.completeUrl = code.string + saveEnteredUrl(scanUrl: .complete) + break + case .cancel: + viewModel.cancelUrl = code.string + saveEnteredUrl(scanUrl: .cancel) + break + case .payment: + viewModel.paymentUrlAuthorityAndPath = code.string + case .unknown: + break + } + viewModel.displayScannerSheet = false + } + }) + } + + var body: some View { + ScrollView { + VStack { + // Display result information from payment + if let icon = viewModel.paymentResultIcon, let text = viewModel.paymentResultMessage { + Image(icon) + Text(text) + } + } + + VStack(alignment: .leading, spacing: 16) { + HStack { + TextField( + "stand_alone_url_payment_view_checkout_url", + text: $viewModel.viewCheckoutUrl + ) + .disableAutocorrection(true) + .autocapitalization(.none) + .lightTextField() + .keyboardType(.URL) + .focused($isFocused) + + IconButton(systemName: "qrcode.viewfinder") { + viewModel.displayScannerSheet = true + self.isFocused = false + self.latestClickedUrl = .checkout + } + } + + HStack { + TextField( + "stand_alone_url_payment_base_url", + text: $viewModel.baseUrl, + onEditingChanged: { focused in + if (!focused) { + saveEnteredUrl(scanUrl: .base) + } + } + ) + .disableAutocorrection(true) + .autocapitalization(.none) + .lightTextField() + .keyboardType(.URL) + .focused($isFocused) + + IconButton(systemName: "qrcode.viewfinder") { + viewModel.displayScannerSheet = true + self.isFocused = false + self.latestClickedUrl = .base + } + } + + HStack { + TextField( + "stand_alone_url_payment_complete_url", + text: $viewModel.completeUrl, + onEditingChanged: { focused in + if (!focused) { + saveEnteredUrl(scanUrl: .complete) + } + } + ) + .disableAutocorrection(true) + .autocapitalization(.none) + .lightTextField() + .keyboardType(.URL) + .focused($isFocused) + + IconButton(systemName: "qrcode.viewfinder") { + viewModel.displayScannerSheet = true + self.isFocused = false + self.latestClickedUrl = .complete + } + } + + HStack { + TextField( + "stand_alone_url_payment_cancel_url", + text: $viewModel.cancelUrl, + onEditingChanged: { focused in + if (!focused) { + saveEnteredUrl(scanUrl: .cancel) + } + } + ) + .disableAutocorrection(true) + .autocapitalization(.none) + .lightTextField() + .keyboardType(.URL) + .focused($isFocused) + + IconButton(systemName: "qrcode.viewfinder") { + viewModel.displayScannerSheet = true + self.isFocused = false + self.latestClickedUrl = .cancel + } + } + + Toggle("stand_alone_url_payment_checkout_v3", isOn: $viewModel.useCheckoutV3) + + Button { + isFocused = false + viewModel.displaySwedbankPayController = true + } label: { + Text("general_checkout") + .smallFont() + .frame(maxWidth: .infinity) + .frame(height: 48) + .accessibilityIdentifier("checkoutButton") + } + .disabled(!viewModel.isCheckoutEnabled) + .foregroundColor(viewModel.isCheckoutEnabled ? .white : .gray) + .background(viewModel.isCheckoutEnabled ? .black : .backgroundGray) + .cornerRadius(30) + .padding(.top, 10) + + HStack { + Text("stand_alone_url_payment_payment_url_scheme") + TextField( + "stand_alone_url_payment_payment_url", + text: $viewModel.paymentUrlAuthorityAndPath, + onEditingChanged: { focused in + if(!focused) { + saveEnteredUrl(scanUrl: .payment) + } + } + ) + .disableAutocorrection(true) + .autocapitalization(.none) + .lightTextField() + .keyboardType(.URL) + .focused($isFocused) + } + } + .padding() + .sheet(isPresented: $viewModel.displaySwedbankPayController) { + if let configuration = viewModel.configurePayment() { + SwedbankPayView(swedbankPayConfiguration: configuration, delegate: viewModel) + } + } + .sheet(isPresented: $viewModel.displayScannerSheet) { + self.scannerSheet + } + } + } + + func saveEnteredUrl(scanUrl: ScanUrl) { + switch scanUrl { + case .checkout: + break + case .base: + viewModel.saveUrl(urlType: scanUrl, url: viewModel.baseUrl) + break + case .complete: + viewModel.saveUrl(urlType: scanUrl, url: viewModel.completeUrl) + break + case .cancel: + viewModel.saveUrl(urlType: scanUrl, url: viewModel.cancelUrl) + break + case .payment: + viewModel.saveUrl(urlType: scanUrl, url: viewModel.paymentUrlAuthorityAndPath) + break + case .unknown: + break + } + } +} + +struct SwedbankPayView: UIViewControllerRepresentable { + typealias UIViewControllerType = SwedbankPaySDKController + + private let swedbankPayConfiguration: SwedbankPayConfiguration + private let delegate: SwedbankPaySDKDelegate + + init(swedbankPayConfiguration: SwedbankPayConfiguration, delegate: SwedbankPaySDKDelegate) { + self.swedbankPayConfiguration = swedbankPayConfiguration + self.delegate = delegate + } + + func makeUIViewController(context: Context) -> SwedbankPaySDKController { + let vc = SwedbankPaySDKController( + configuration: swedbankPayConfiguration, + withCheckin: false, + consumer: nil, + paymentOrder: nil, + userData: nil) + + vc.delegate = delegate + + return vc + } + + func updateUIViewController(_ uiViewController: SwedbankPaySDKController, context: Context) { + } +} + +struct StandaloneUrlView_Previews: PreviewProvider { + static var previews: some View { + StandaloneUrlView() + } +} diff --git a/Example-app/Views/ccomponents/IconButton.swift b/Example-app/Views/ccomponents/IconButton.swift new file mode 100644 index 0000000..bdc274c --- /dev/null +++ b/Example-app/Views/ccomponents/IconButton.swift @@ -0,0 +1,14 @@ +import SwiftUI + +struct IconButton: View { + let systemName: String + let action: () -> Void + + var body: some View { + Button(action: action) { + Image(systemName: systemName) + .font(.title) + .foregroundColor(.black) + } + } +} diff --git a/Example-app/en.lproj/Localizable.strings b/Example-app/en.lproj/Localizable.strings new file mode 100644 index 0000000..31765cc --- /dev/null +++ b/Example-app/en.lproj/Localizable.strings @@ -0,0 +1,14 @@ +//General +"general_checkout" = "Checkout"; + +// Stand-alone URL View Controller +"stand_alone_url_payment_successful" = "Payment completed"; +"stand_alone_url_payment_cancelled" = "Payment cancelled"; + +"stand_alone_url_payment_view_checkout_url" = "View checkout URL"; +"stand_alone_url_payment_base_url" = "Base URL"; +"stand_alone_url_payment_complete_url" = "Complete URL"; +"stand_alone_url_payment_cancel_url" = "Cancel URL"; +"stand_alone_url_payment_checkout_v3" = "Use Checkout V3"; +"stand_alone_url_payment_payment_url" = "Payment URL"; +"stand_alone_url_payment_payment_url_scheme" = "swedbankexample://";