From 4cc1b4cca5659c76c80edb5e10b035b2f9fcecf0 Mon Sep 17 00:00:00 2001 From: pscb-dev <> Date: Mon, 8 Jul 2024 15:20:00 +0300 Subject: [PATCH] init --- LICENSE.txt | 1 + PSCBOnline.podspec | 138 +++ PSCBOnline.xcodeproj/project.pbxproj | 652 +++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../UserInterfaceState.xcuserstate | Bin 0 -> 122230 bytes .../xcschemes/xcschememanagement.plist | 27 + PSCBOnline/Info.plist | 22 + PSCBOnline/PSCBOnline.h | 18 + PSCBOnline/Sources/Extension/String.swift | 26 + PSCBOnline/Sources/Helpers/DigestHelper.swift | 29 + PSCBOnline/Sources/Helpers/JSONDecoders.swift | 33 + PSCBOnline/Sources/Helpers/RSAHelper.swift | 86 ++ PSCBOnline/Sources/Models/CardData.swift | 92 ++ PSCBOnline/Sources/Models/CustomerData.swift | 29 + PSCBOnline/Sources/Models/Payment.swift | 90 ++ .../Sources/Models/RequestWrapper.swift | 44 + PSCBOnline/Sources/Models/Response.swift | 100 ++ PSCBOnline/Sources/PSCBAPI.swift | 55 + PSCBOnline/Sources/PSCBOnlineClient.swift | 306 +++++ PSCBOnline/Sources/PaymentHandler.swift | 113 ++ .../PKPaymentToken+Serializable.swift | 79 ++ .../Serializable/Payment+Serializable.swift | 27 + .../RequestWrapper+Serializable.swift | 21 + .../Sources/Serializable/Serializable.swift | 61 + .../project.pbxproj | 762 ++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../UserInterfaceState.xcuserstate | Bin 0 -> 23128 bytes .../xcschemes/PSCBOnlineSample.xcscheme | 98 ++ .../xcschemes/xcschememanagement.plist | 37 + .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../UserInterfaceState.xcuserstate | Bin 0 -> 25954 bytes .../PSCBOnlineSample/AppDelegate.swift | 36 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 98 ++ .../Assets.xcassets/Contents.json | 6 + .../Base.lproj/LaunchScreen.storyboard | 25 + .../PSCBOnlineSample/ContentView.swift | 124 ++ PSCBOnlineSample/PSCBOnlineSample/Info.plist | 62 + .../PSCBOnlineSample/PaymentHandler.swift | 129 +++ .../Preview Assets.xcassets/Contents.json | 6 + .../PSCBOnlineSample/SceneDelegate.swift | 63 + .../PSCBOnlineSample/sdk.sample.entitlements | 12 + .../PSCBOnlineSampleTests/Info.plist | 22 + .../sdk_sampleTests.swift | 33 + .../PSCBOnlineSampleUITests/Info.plist | 22 + .../sdk_sampleUITests.swift | 42 + PSCBOnlineSample/Podfile.lock | 22 + .../Local Podspecs/PSCBOnline.podspec.json | 25 + PSCBOnlineSample/Pods/Manifest.lock | 22 + PSCBOnlineSample/Pods/PSCBOnline/LICENSE.txt | 1 + .../Pods/PSCBOnline/PSCBOnline/PSCBOnline.h | 18 + .../PSCBOnline/Sources/Extension/String.swift | 26 + .../Sources/Helpers/DigestHelper.swift | 29 + .../Sources/Helpers/JSONDecoders.swift | 33 + .../Sources/Helpers/RSAHelper.swift | 86 ++ .../PSCBOnline/Sources/Models/CardData.swift | 92 ++ .../Sources/Models/CustomerData.swift | 29 + .../PSCBOnline/Sources/Models/Payment.swift | 90 ++ .../Sources/Models/RequestWrapper.swift | 44 + .../PSCBOnline/Sources/Models/Response.swift | 100 ++ .../PSCBOnline/Sources/PSCBAPI.swift | 55 + .../PSCBOnline/Sources/PSCBOnlineClient.swift | 306 +++++ .../PSCBOnline/Sources/PaymentHandler.swift | 113 ++ .../PKPaymentToken+Serializable.swift | 79 ++ .../Serializable/Payment+Serializable.swift | 27 + .../RequestWrapper+Serializable.swift | 21 + .../Sources/Serializable/Serializable.swift | 61 + PSCBOnlineSample/Pods/PSCBOnline/README.md | 217 ++++ .../Pods/Pods.xcodeproj/project.pbxproj | 1030 +++++++++++++++++ .../xcschemes/PSCBOnline.xcscheme | 60 + ...ineSample-PSCBOnlineSampleUITests.xcscheme | 58 + .../xcschemes/Pods-PSCBOnlineSample.xcscheme | 58 + .../Pods-PSCBOnlineSampleTests.xcscheme | 58 + .../xcschemes/xcschememanagement.plist | 31 + .../PSCBOnline/PSCBOnline-Info.plist | 26 + .../PSCBOnline/PSCBOnline-dummy.m | 5 + .../PSCBOnline/PSCBOnline-prefix.pch | 12 + .../PSCBOnline/PSCBOnline-umbrella.h | 17 + .../PSCBOnline/PSCBOnline.debug.xcconfig | 12 + .../PSCBOnline/PSCBOnline.modulemap | 6 + .../PSCBOnline/PSCBOnline.release.xcconfig | 12 + ...eSample-PSCBOnlineSampleUITests-Info.plist | 26 + ...ineSampleUITests-acknowledgements.markdown | 8 + ...OnlineSampleUITests-acknowledgements.plist | 40 + ...lineSample-PSCBOnlineSampleUITests-dummy.m | 5 + ...ts-frameworks-Debug-input-files.xcfilelist | 2 + ...s-frameworks-Debug-output-files.xcfilelist | 1 + ...-frameworks-Release-input-files.xcfilelist | 2 + ...frameworks-Release-output-files.xcfilelist | 1 + ...mple-PSCBOnlineSampleUITests-frameworks.sh | 185 +++ ...eSample-PSCBOnlineSampleUITests-umbrella.h | 16 + ...ple-PSCBOnlineSampleUITests.debug.xcconfig | 14 + ...neSample-PSCBOnlineSampleUITests.modulemap | 6 + ...e-PSCBOnlineSampleUITests.release.xcconfig | 14 + .../Pods-PSCBOnlineSample-Info.plist | 26 + ...PSCBOnlineSample-acknowledgements.markdown | 8 + ...ds-PSCBOnlineSample-acknowledgements.plist | 40 + .../Pods-PSCBOnlineSample-dummy.m | 5 + ...le-frameworks-Debug-input-files.xcfilelist | 2 + ...e-frameworks-Debug-output-files.xcfilelist | 1 + ...-frameworks-Release-input-files.xcfilelist | 2 + ...frameworks-Release-output-files.xcfilelist | 1 + .../Pods-PSCBOnlineSample-frameworks.sh | 185 +++ .../Pods-PSCBOnlineSample-umbrella.h | 16 + .../Pods-PSCBOnlineSample.debug.xcconfig | 14 + .../Pods-PSCBOnlineSample.modulemap | 6 + .../Pods-PSCBOnlineSample.release.xcconfig | 14 + .../Pods-PSCBOnlineSampleTests-Info.plist | 26 + ...nlineSampleTests-acknowledgements.markdown | 3 + ...CBOnlineSampleTests-acknowledgements.plist | 29 + .../Pods-PSCBOnlineSampleTests-dummy.m | 5 + .../Pods-PSCBOnlineSampleTests-umbrella.h | 16 + .../Pods-PSCBOnlineSampleTests.debug.xcconfig | 11 + .../Pods-PSCBOnlineSampleTests.modulemap | 6 + ...ods-PSCBOnlineSampleTests.release.xcconfig | 11 + .../Helpers/DigestHelperTests.swift | 57 + PSCBOnlineTests/Info.plist | 22 + PSCBOnlineTests/Mocks/PassKitMocks.swift | 118 ++ PSCBOnlineTests/PSCBOnlineTests.swift | 33 + .../PKPaymentToken+SerializableTests.swift | 109 ++ .../Serializable/PKPaymentTokenTests.swift | 35 + .../Sources/Models/CardDataTests.swift | 47 + .../Sources/Models/PaymentTests.swift | 36 + .../Sources/Models/ResponseTests.swift | 117 ++ .../Sources/OOSAPIClientTests.swift | 114 ++ README.md | 217 ++++ Untitled.xcworkspace/contents.xcworkspacedata | 16 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../UserInterfaceState.xcuserstate | Bin 0 -> 57657 bytes 132 files changed, 8242 insertions(+) create mode 100644 LICENSE.txt create mode 100644 PSCBOnline.podspec create mode 100644 PSCBOnline.xcodeproj/project.pbxproj create mode 100644 PSCBOnline.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 PSCBOnline.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 PSCBOnline.xcodeproj/project.xcworkspace/xcuserdata/oa.xcuserdatad/UserInterfaceState.xcuserstate create mode 100644 PSCBOnline.xcodeproj/xcuserdata/oa.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 PSCBOnline/Info.plist create mode 100644 PSCBOnline/PSCBOnline.h create mode 100644 PSCBOnline/Sources/Extension/String.swift create mode 100644 PSCBOnline/Sources/Helpers/DigestHelper.swift create mode 100644 PSCBOnline/Sources/Helpers/JSONDecoders.swift create mode 100644 PSCBOnline/Sources/Helpers/RSAHelper.swift create mode 100644 PSCBOnline/Sources/Models/CardData.swift create mode 100644 PSCBOnline/Sources/Models/CustomerData.swift create mode 100644 PSCBOnline/Sources/Models/Payment.swift create mode 100644 PSCBOnline/Sources/Models/RequestWrapper.swift create mode 100644 PSCBOnline/Sources/Models/Response.swift create mode 100644 PSCBOnline/Sources/PSCBAPI.swift create mode 100644 PSCBOnline/Sources/PSCBOnlineClient.swift create mode 100644 PSCBOnline/Sources/PaymentHandler.swift create mode 100644 PSCBOnline/Sources/Serializable/PKPaymentToken+Serializable.swift create mode 100644 PSCBOnline/Sources/Serializable/Payment+Serializable.swift create mode 100644 PSCBOnline/Sources/Serializable/RequestWrapper+Serializable.swift create mode 100644 PSCBOnline/Sources/Serializable/Serializable.swift create mode 100644 PSCBOnlineSample/PSCBOnlineSample.xcodeproj/project.pbxproj create mode 100644 PSCBOnlineSample/PSCBOnlineSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 PSCBOnlineSample/PSCBOnlineSample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 PSCBOnlineSample/PSCBOnlineSample.xcodeproj/project.xcworkspace/xcuserdata/oa.xcuserdatad/UserInterfaceState.xcuserstate create mode 100644 PSCBOnlineSample/PSCBOnlineSample.xcodeproj/xcshareddata/xcschemes/PSCBOnlineSample.xcscheme create mode 100644 PSCBOnlineSample/PSCBOnlineSample.xcodeproj/xcuserdata/oa.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 PSCBOnlineSample/PSCBOnlineSample.xcworkspace/contents.xcworkspacedata create mode 100644 PSCBOnlineSample/PSCBOnlineSample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 PSCBOnlineSample/PSCBOnlineSample.xcworkspace/xcuserdata/oa.xcuserdatad/UserInterfaceState.xcuserstate create mode 100644 PSCBOnlineSample/PSCBOnlineSample/AppDelegate.swift create mode 100644 PSCBOnlineSample/PSCBOnlineSample/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 PSCBOnlineSample/PSCBOnlineSample/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 PSCBOnlineSample/PSCBOnlineSample/Assets.xcassets/Contents.json create mode 100644 PSCBOnlineSample/PSCBOnlineSample/Base.lproj/LaunchScreen.storyboard create mode 100644 PSCBOnlineSample/PSCBOnlineSample/ContentView.swift create mode 100644 PSCBOnlineSample/PSCBOnlineSample/Info.plist create mode 100644 PSCBOnlineSample/PSCBOnlineSample/PaymentHandler.swift create mode 100644 PSCBOnlineSample/PSCBOnlineSample/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 PSCBOnlineSample/PSCBOnlineSample/SceneDelegate.swift create mode 100644 PSCBOnlineSample/PSCBOnlineSample/sdk.sample.entitlements create mode 100644 PSCBOnlineSample/PSCBOnlineSampleTests/Info.plist create mode 100644 PSCBOnlineSample/PSCBOnlineSampleTests/sdk_sampleTests.swift create mode 100644 PSCBOnlineSample/PSCBOnlineSampleUITests/Info.plist create mode 100644 PSCBOnlineSample/PSCBOnlineSampleUITests/sdk_sampleUITests.swift create mode 100644 PSCBOnlineSample/Podfile.lock create mode 100644 PSCBOnlineSample/Pods/Local Podspecs/PSCBOnline.podspec.json create mode 100644 PSCBOnlineSample/Pods/Manifest.lock create mode 100644 PSCBOnlineSample/Pods/PSCBOnline/LICENSE.txt create mode 100644 PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/PSCBOnline.h create mode 100644 PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Extension/String.swift create mode 100644 PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Helpers/DigestHelper.swift create mode 100644 PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Helpers/JSONDecoders.swift create mode 100644 PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Helpers/RSAHelper.swift create mode 100644 PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Models/CardData.swift create mode 100644 PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Models/CustomerData.swift create mode 100644 PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Models/Payment.swift create mode 100644 PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Models/RequestWrapper.swift create mode 100644 PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Models/Response.swift create mode 100644 PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/PSCBAPI.swift create mode 100644 PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/PSCBOnlineClient.swift create mode 100644 PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/PaymentHandler.swift create mode 100644 PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Serializable/PKPaymentToken+Serializable.swift create mode 100644 PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Serializable/Payment+Serializable.swift create mode 100644 PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Serializable/RequestWrapper+Serializable.swift create mode 100644 PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Serializable/Serializable.swift create mode 100644 PSCBOnlineSample/Pods/PSCBOnline/README.md create mode 100644 PSCBOnlineSample/Pods/Pods.xcodeproj/project.pbxproj create mode 100644 PSCBOnlineSample/Pods/Pods.xcodeproj/xcuserdata/oa.xcuserdatad/xcschemes/PSCBOnline.xcscheme create mode 100644 PSCBOnlineSample/Pods/Pods.xcodeproj/xcuserdata/oa.xcuserdatad/xcschemes/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.xcscheme create mode 100644 PSCBOnlineSample/Pods/Pods.xcodeproj/xcuserdata/oa.xcuserdatad/xcschemes/Pods-PSCBOnlineSample.xcscheme create mode 100644 PSCBOnlineSample/Pods/Pods.xcodeproj/xcuserdata/oa.xcuserdatad/xcschemes/Pods-PSCBOnlineSampleTests.xcscheme create mode 100644 PSCBOnlineSample/Pods/Pods.xcodeproj/xcuserdata/oa.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 PSCBOnlineSample/Pods/Target Support Files/PSCBOnline/PSCBOnline-Info.plist create mode 100644 PSCBOnlineSample/Pods/Target Support Files/PSCBOnline/PSCBOnline-dummy.m create mode 100644 PSCBOnlineSample/Pods/Target Support Files/PSCBOnline/PSCBOnline-prefix.pch create mode 100644 PSCBOnlineSample/Pods/Target Support Files/PSCBOnline/PSCBOnline-umbrella.h create mode 100644 PSCBOnlineSample/Pods/Target Support Files/PSCBOnline/PSCBOnline.debug.xcconfig create mode 100644 PSCBOnlineSample/Pods/Target Support Files/PSCBOnline/PSCBOnline.modulemap create mode 100644 PSCBOnlineSample/Pods/Target Support Files/PSCBOnline/PSCBOnline.release.xcconfig create mode 100644 PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-Info.plist create mode 100644 PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-acknowledgements.markdown create mode 100644 PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-acknowledgements.plist create mode 100644 PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-dummy.m create mode 100644 PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-frameworks-Debug-input-files.xcfilelist create mode 100644 PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-frameworks-Debug-output-files.xcfilelist create mode 100644 PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-frameworks-Release-input-files.xcfilelist create mode 100644 PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-frameworks-Release-output-files.xcfilelist create mode 100644 PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-frameworks.sh create mode 100644 PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-umbrella.h create mode 100644 PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.debug.xcconfig create mode 100644 PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.modulemap create mode 100644 PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.release.xcconfig create mode 100644 PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-Info.plist create mode 100644 PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-acknowledgements.markdown create mode 100644 PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-acknowledgements.plist create mode 100644 PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-dummy.m create mode 100644 PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-frameworks-Debug-input-files.xcfilelist create mode 100644 PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-frameworks-Debug-output-files.xcfilelist create mode 100644 PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-frameworks-Release-input-files.xcfilelist create mode 100644 PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-frameworks-Release-output-files.xcfilelist create mode 100644 PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-frameworks.sh create mode 100644 PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-umbrella.h create mode 100644 PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample.debug.xcconfig create mode 100644 PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample.modulemap create mode 100644 PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample.release.xcconfig create mode 100644 PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests-Info.plist create mode 100644 PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests-acknowledgements.markdown create mode 100644 PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests-acknowledgements.plist create mode 100644 PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests-dummy.m create mode 100644 PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests-umbrella.h create mode 100644 PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests.debug.xcconfig create mode 100644 PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests.modulemap create mode 100644 PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests.release.xcconfig create mode 100644 PSCBOnlineTests/Helpers/DigestHelperTests.swift create mode 100644 PSCBOnlineTests/Info.plist create mode 100644 PSCBOnlineTests/Mocks/PassKitMocks.swift create mode 100644 PSCBOnlineTests/PSCBOnlineTests.swift create mode 100644 PSCBOnlineTests/Serializable/PKPaymentToken+SerializableTests.swift create mode 100644 PSCBOnlineTests/Serializable/PKPaymentTokenTests.swift create mode 100644 PSCBOnlineTests/Sources/Models/CardDataTests.swift create mode 100644 PSCBOnlineTests/Sources/Models/PaymentTests.swift create mode 100644 PSCBOnlineTests/Sources/Models/ResponseTests.swift create mode 100644 PSCBOnlineTests/Sources/OOSAPIClientTests.swift create mode 100644 README.md create mode 100644 Untitled.xcworkspace/contents.xcworkspacedata create mode 100644 Untitled.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 Untitled.xcworkspace/xcuserdata/oa.xcuserdatad/UserInterfaceState.xcuserstate diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..139d470 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1 @@ +LICENSE BE HERE diff --git a/PSCBOnline.podspec b/PSCBOnline.podspec new file mode 100644 index 0000000..ea1b41b --- /dev/null +++ b/PSCBOnline.podspec @@ -0,0 +1,138 @@ +# +# Be sure to run `pod spec lint PSCBOnline.podspec' to ensure this is a +# valid spec and to remove all comments including this before submitting the spec. +# +# To learn more about Podspec attributes see https://guides.cocoapods.org/syntax/podspec.html +# To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ +# + +Pod::Spec.new do |spec| + + # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # These will help people to find your library, and whilst it + # can feel like a chore to fill in it's definitely to your advantage. The + # summary should be tweet-length, and the description more in depth. + # + + spec.name = "PSCBOnline" + spec.version = "1.0.1" + spec.summary = "An iOS SDK for PSCB Online (OOS) acquiring protocol." + + # This description is used to generate tags and improve search results. + # * Think: What does it do? Why did you write it? What is the focus? + # * Try to keep it short, snappy and to the point. + # * Write the description between the DESC delimiters below. + # * Finally, don't worry about the indent, CocoaPods strips it! + spec.description = "An implementation of PSCB-Online (oos.pscb.ru) acquiring protocol for iOS platforms." + + spec.homepage = "https://oos.pscb.ru/" + # spec.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif" + + + # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # Licensing your code is important. See https://choosealicense.com for more info. + # CocoaPods will detect a license file if there is a named LICENSE* + # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. + # + + spec.license = { :type => "PSCB", :file => "LICENSE.txt" } + # spec.license = { :type => "MIT", :file => "FILE_LICENSE" } + + + # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # Specify the authors of the library, with email addresses. Email addresses + # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also + # accepts just a name if you'd rather not provide an email address. + # + # Specify a social_media_url where others can refer to, for example a twitter + # profile URL. + # + + spec.authors = { "Antonov Ilia" => "antilya@gmail.com" } + + # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # If this Pod runs only on iOS or OS X, then specify the platform and + # the deployment target. You can optionally include the target after the platform. + # + + # spec.platform = :ios + spec.platform = :ios, "10.0" + + # When using multiple platforms + spec.ios.deployment_target = "10.0" + # spec.osx.deployment_target = "10.7" + # spec.watchos.deployment_target = "2.0" + # spec.tvos.deployment_target = "9.0" + + + # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # Specify the location from where the source should be retrieved. + # Supports git, hg, bzr, svn and HTTP. + # + + #spec.source = { :git => "http://EXAMPLE/PSCB_OOS.git", :tag => "#{spec.version}" } + spec.source = { :git => "https://bitbucket.org/dev_ai/oos-ios.git", :tag => "#{spec.version}" } + + + # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # CocoaPods is smart about how it includes source code. For source files + # giving a folder will include any swift, h, m, mm, c & cpp files. + # For header files it will include any header in the folder. + # Not including the public_header_files will make all headers public. + # + + #spec.source_files = "PSCBOnline", "PSCBOnline/**/*.{h,m,swift}", "PSCBOnline/Sources/**/*.{h,m,swift}" + spec.source_files = "PSCBOnline/**/*.{h,m,swift}" + # spec.source_files = "PSCB-OOS-iOS", "PSCB-OOS-iOS/**/*.{h,m}" + spec.exclude_files = "" + + # spec.public_header_files = "Classes/**/*.h" + + + # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # A list of resources included with the Pod. These are copied into the + # target bundle with a build phase script. Anything else will be cleaned. + # You can preserve files from being cleaned, please don't preserve + # non-essential files like tests, examples and documentation. + # + + # spec.resource = "icon.png" + # spec.resources = "Resources/*.png" + + # spec.preserve_paths = "FilesToSave", "MoreFilesToSave" + + + # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # Link your library with frameworks, or libraries. Libraries do not include + # the lib prefix of their name. + # + + # spec.framework = "SomeFramework" + # spec.frameworks = "SomeFramework", "AnotherFramework" + + # spec.library = "iconv" + # spec.libraries = "iconv", "xml2" + + + # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # If your library depends on compiler flags you can set them in the xcconfig hash + # where they will only apply to your library. If you depend on other Podspecs + # you can include multiple dependencies to ensure it works. + + # spec.requires_arc = true + + # spec.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } + # spec.dependency "JSONKit", "~> 1.4" + + spec.swift_version = "5.3" + +end diff --git a/PSCBOnline.xcodeproj/project.pbxproj b/PSCBOnline.xcodeproj/project.pbxproj new file mode 100644 index 0000000..84f6dfb --- /dev/null +++ b/PSCBOnline.xcodeproj/project.pbxproj @@ -0,0 +1,652 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 0E01EAB425399CB500B0759B /* PSCBOnline.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E01EAAA25399CB500B0759B /* PSCBOnline.framework */; }; + 0E01EAB925399CB500B0759B /* PSCBOnlineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E01EAB825399CB500B0759B /* PSCBOnlineTests.swift */; }; + 0E01EABB25399CB500B0759B /* PSCBOnline.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E01EAAD25399CB500B0759B /* PSCBOnline.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0E01EACD25399DB000B0759B /* Payment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E01EACC25399DB000B0759B /* Payment.swift */; }; + 0E01EAD325399DF600B0759B /* CustomerData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E01EAD225399DF500B0759B /* CustomerData.swift */; }; + 0E01EAD825399E3D00B0759B /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E01EAD725399E3D00B0759B /* String.swift */; }; + 0E01EADE25399E8800B0759B /* RequestWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E01EADD25399E8800B0759B /* RequestWrapper.swift */; }; + 0E01EAE425399EE800B0759B /* PaymentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E01EAE325399EE800B0759B /* PaymentTests.swift */; }; + 0E0A2C1F25408AA1006BBBA1 /* CardData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0A2C1E25408AA1006BBBA1 /* CardData.swift */; }; + 0E0A2C23254093D0006BBBA1 /* Payment+Serializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0A2C22254093D0006BBBA1 /* Payment+Serializable.swift */; }; + 0E26F5862542FB5800F80DAC /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E26F5852542FB5800F80DAC /* Response.swift */; }; + 0E26F58A2542FE2000F80DAC /* ResponseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E26F5892542FE2000F80DAC /* ResponseTests.swift */; }; + 0E26F58F2545AE5800F80DAC /* JSONDecoders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E26F58E2545AE5800F80DAC /* JSONDecoders.swift */; }; + 0E26F59F2547097300F80DAC /* RSAHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E26F59E2547097300F80DAC /* RSAHelper.swift */; }; + 0E26F5A3254714B600F80DAC /* CardDataTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E26F5A2254714B600F80DAC /* CardDataTests.swift */; }; + 0E26F5E82548717B00F80DAC /* PSCBAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E26F5E72548717B00F80DAC /* PSCBAPI.swift */; }; + 0E26F62E2549591E00F80DAC /* DigestHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E26F62D2549591D00F80DAC /* DigestHelper.swift */; }; + 0E26F63325495A5B00F80DAC /* DigestHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E26F63225495A5B00F80DAC /* DigestHelperTests.swift */; }; + 0E790FA3253DEA930086AD71 /* PSCBOnline.podspec in Resources */ = {isa = PBXBuildFile; fileRef = 0E790FA2253DEA930086AD71 /* PSCBOnline.podspec */; }; + 0E790FAB253E017E0086AD71 /* LICENSE.txt in Resources */ = {isa = PBXBuildFile; fileRef = 0E790FAA253E017E0086AD71 /* LICENSE.txt */; }; + 0EA2C84E2539A5BA00C5DD14 /* Serializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA2C84D2539A5BA00C5DD14 /* Serializable.swift */; }; + 0EA2C8582539F4D500C5DD14 /* PSCBOnlineClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA2C8572539F4D500C5DD14 /* PSCBOnlineClient.swift */; }; + 0EA2C8622539FAC200C5DD14 /* PaymentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA2C8612539FAC200C5DD14 /* PaymentHandler.swift */; }; + 0EA2C86A253B29A900C5DD14 /* PKPaymentToken+Serializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA2C869253B29A900C5DD14 /* PKPaymentToken+Serializable.swift */; }; + 0EA2C872253CC44A00C5DD14 /* RequestWrapper+Serializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA2C871253CC44A00C5DD14 /* RequestWrapper+Serializable.swift */; }; + 0EC9DB072540A1C8005392A0 /* OOSAPIClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC9DB062540A1C8005392A0 /* OOSAPIClientTests.swift */; }; + 0EC9DB0C2541B699005392A0 /* PassKitMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC9DB0B2541B699005392A0 /* PassKitMocks.swift */; }; + 0EC9DB132541BF45005392A0 /* PKPaymentToken+SerializableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC9DB122541BF45005392A0 /* PKPaymentToken+SerializableTests.swift */; }; + 0EC9DB192541D9F9005392A0 /* PKPaymentTokenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC9DB182541D9F9005392A0 /* PKPaymentTokenTests.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 0E01EAB525399CB500B0759B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0E01EAA125399CB500B0759B /* Project object */; + proxyType = 1; + remoteGlobalIDString = 0E01EAA925399CB500B0759B; + remoteInfo = "PSCB-OOS-iOS"; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 0E01EAAA25399CB500B0759B /* PSCBOnline.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PSCBOnline.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 0E01EAAD25399CB500B0759B /* PSCBOnline.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PSCBOnline.h; sourceTree = ""; }; + 0E01EAAE25399CB500B0759B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 0E01EAB325399CB500B0759B /* PSCBOnlineTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PSCBOnlineTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 0E01EAB825399CB500B0759B /* PSCBOnlineTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PSCBOnlineTests.swift; sourceTree = ""; }; + 0E01EABA25399CB500B0759B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 0E01EACC25399DB000B0759B /* Payment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Payment.swift; sourceTree = ""; }; + 0E01EAD225399DF500B0759B /* CustomerData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomerData.swift; sourceTree = ""; }; + 0E01EAD725399E3D00B0759B /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = ""; }; + 0E01EADD25399E8800B0759B /* RequestWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestWrapper.swift; sourceTree = ""; }; + 0E01EAE325399EE800B0759B /* PaymentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentTests.swift; sourceTree = ""; }; + 0E0A2C1E25408AA1006BBBA1 /* CardData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardData.swift; sourceTree = ""; }; + 0E0A2C22254093D0006BBBA1 /* Payment+Serializable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Payment+Serializable.swift"; sourceTree = ""; }; + 0E26F5852542FB5800F80DAC /* Response.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Response.swift; sourceTree = ""; }; + 0E26F5892542FE2000F80DAC /* ResponseTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResponseTests.swift; sourceTree = ""; }; + 0E26F58E2545AE5800F80DAC /* JSONDecoders.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONDecoders.swift; sourceTree = ""; }; + 0E26F59E2547097300F80DAC /* RSAHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSAHelper.swift; sourceTree = ""; }; + 0E26F5A2254714B600F80DAC /* CardDataTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardDataTests.swift; sourceTree = ""; }; + 0E26F5E72548717B00F80DAC /* PSCBAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PSCBAPI.swift; sourceTree = ""; }; + 0E26F62D2549591D00F80DAC /* DigestHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DigestHelper.swift; sourceTree = ""; }; + 0E26F63225495A5B00F80DAC /* DigestHelperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DigestHelperTests.swift; sourceTree = ""; }; + 0E547EB32552BEA500E0F8C0 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + 0E790FA2253DEA930086AD71 /* PSCBOnline.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = PSCBOnline.podspec; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; + 0E790FAA253E017E0086AD71 /* LICENSE.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE.txt; sourceTree = ""; }; + 0EA2C84D2539A5BA00C5DD14 /* Serializable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Serializable.swift; sourceTree = ""; }; + 0EA2C8572539F4D500C5DD14 /* PSCBOnlineClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PSCBOnlineClient.swift; sourceTree = ""; }; + 0EA2C8612539FAC200C5DD14 /* PaymentHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentHandler.swift; sourceTree = ""; }; + 0EA2C869253B29A900C5DD14 /* PKPaymentToken+Serializable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PKPaymentToken+Serializable.swift"; sourceTree = ""; }; + 0EA2C871253CC44A00C5DD14 /* RequestWrapper+Serializable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RequestWrapper+Serializable.swift"; sourceTree = ""; }; + 0EC9DB062540A1C8005392A0 /* OOSAPIClientTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OOSAPIClientTests.swift; sourceTree = ""; }; + 0EC9DB0B2541B699005392A0 /* PassKitMocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PassKitMocks.swift; sourceTree = ""; }; + 0EC9DB122541BF45005392A0 /* PKPaymentToken+SerializableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PKPaymentToken+SerializableTests.swift"; sourceTree = ""; }; + 0EC9DB182541D9F9005392A0 /* PKPaymentTokenTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PKPaymentTokenTests.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 0E01EAA725399CB500B0759B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0E01EAB025399CB500B0759B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 0E01EAB425399CB500B0759B /* PSCBOnline.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0E01EAA025399CB500B0759B = { + isa = PBXGroup; + children = ( + 0E547EB32552BEA500E0F8C0 /* README.md */, + 0E790FAA253E017E0086AD71 /* LICENSE.txt */, + 0E790FA2253DEA930086AD71 /* PSCBOnline.podspec */, + 0E01EAAC25399CB500B0759B /* PSCBOnline */, + 0E01EAB725399CB500B0759B /* PSCBOnlineTests */, + 0E01EAAB25399CB500B0759B /* Products */, + ); + sourceTree = ""; + }; + 0E01EAAB25399CB500B0759B /* Products */ = { + isa = PBXGroup; + children = ( + 0E01EAAA25399CB500B0759B /* PSCBOnline.framework */, + 0E01EAB325399CB500B0759B /* PSCBOnlineTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 0E01EAAC25399CB500B0759B /* PSCBOnline */ = { + isa = PBXGroup; + children = ( + 0E01EAC625399CEF00B0759B /* Sources */, + 0E01EAAD25399CB500B0759B /* PSCBOnline.h */, + 0E01EAAE25399CB500B0759B /* Info.plist */, + ); + path = PSCBOnline; + sourceTree = ""; + }; + 0E01EAB725399CB500B0759B /* PSCBOnlineTests */ = { + isa = PBXGroup; + children = ( + 0E26F63125495A2F00F80DAC /* Helpers */, + 0EC9DB112541BF2D005392A0 /* Serializable */, + 0EC9DB0A2541B686005392A0 /* Mocks */, + 0E01EAE125399ECE00B0759B /* Sources */, + 0E01EAB825399CB500B0759B /* PSCBOnlineTests.swift */, + 0E01EABA25399CB500B0759B /* Info.plist */, + ); + path = PSCBOnlineTests; + sourceTree = ""; + }; + 0E01EAC625399CEF00B0759B /* Sources */ = { + isa = PBXGroup; + children = ( + 0E26F58D2545AE4200F80DAC /* Helpers */, + 0EA2C84C2539A5A300C5DD14 /* Serializable */, + 0E01EAD625399E0F00B0759B /* Extension */, + 0E01EACB25399D8F00B0759B /* Models */, + 0EA2C8572539F4D500C5DD14 /* PSCBOnlineClient.swift */, + 0EA2C8612539FAC200C5DD14 /* PaymentHandler.swift */, + 0E26F5E72548717B00F80DAC /* PSCBAPI.swift */, + ); + path = Sources; + sourceTree = ""; + }; + 0E01EACB25399D8F00B0759B /* Models */ = { + isa = PBXGroup; + children = ( + 0E01EACC25399DB000B0759B /* Payment.swift */, + 0E01EAD225399DF500B0759B /* CustomerData.swift */, + 0E01EADD25399E8800B0759B /* RequestWrapper.swift */, + 0E0A2C1E25408AA1006BBBA1 /* CardData.swift */, + 0E26F5852542FB5800F80DAC /* Response.swift */, + ); + path = Models; + sourceTree = ""; + }; + 0E01EAD625399E0F00B0759B /* Extension */ = { + isa = PBXGroup; + children = ( + 0E01EAD725399E3D00B0759B /* String.swift */, + ); + path = Extension; + sourceTree = ""; + }; + 0E01EAE125399ECE00B0759B /* Sources */ = { + isa = PBXGroup; + children = ( + 0E01EAE225399ED400B0759B /* Models */, + 0EC9DB062540A1C8005392A0 /* OOSAPIClientTests.swift */, + ); + path = Sources; + sourceTree = ""; + }; + 0E01EAE225399ED400B0759B /* Models */ = { + isa = PBXGroup; + children = ( + 0E01EAE325399EE800B0759B /* PaymentTests.swift */, + 0E26F5892542FE2000F80DAC /* ResponseTests.swift */, + 0E26F5A2254714B600F80DAC /* CardDataTests.swift */, + ); + path = Models; + sourceTree = ""; + }; + 0E26F58D2545AE4200F80DAC /* Helpers */ = { + isa = PBXGroup; + children = ( + 0E26F58E2545AE5800F80DAC /* JSONDecoders.swift */, + 0E26F59E2547097300F80DAC /* RSAHelper.swift */, + 0E26F62D2549591D00F80DAC /* DigestHelper.swift */, + ); + path = Helpers; + sourceTree = ""; + }; + 0E26F63125495A2F00F80DAC /* Helpers */ = { + isa = PBXGroup; + children = ( + 0E26F63225495A5B00F80DAC /* DigestHelperTests.swift */, + ); + path = Helpers; + sourceTree = ""; + }; + 0EA2C84C2539A5A300C5DD14 /* Serializable */ = { + isa = PBXGroup; + children = ( + 0EA2C84D2539A5BA00C5DD14 /* Serializable.swift */, + 0EA2C869253B29A900C5DD14 /* PKPaymentToken+Serializable.swift */, + 0EA2C871253CC44A00C5DD14 /* RequestWrapper+Serializable.swift */, + 0E0A2C22254093D0006BBBA1 /* Payment+Serializable.swift */, + ); + path = Serializable; + sourceTree = ""; + }; + 0EC9DB0A2541B686005392A0 /* Mocks */ = { + isa = PBXGroup; + children = ( + 0EC9DB0B2541B699005392A0 /* PassKitMocks.swift */, + ); + path = Mocks; + sourceTree = ""; + }; + 0EC9DB112541BF2D005392A0 /* Serializable */ = { + isa = PBXGroup; + children = ( + 0EC9DB122541BF45005392A0 /* PKPaymentToken+SerializableTests.swift */, + 0EC9DB182541D9F9005392A0 /* PKPaymentTokenTests.swift */, + ); + path = Serializable; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 0E01EAA525399CB500B0759B /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 0E01EABB25399CB500B0759B /* PSCBOnline.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 0E01EAA925399CB500B0759B /* PSCBOnline */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0E01EABE25399CB500B0759B /* Build configuration list for PBXNativeTarget "PSCBOnline" */; + buildPhases = ( + 0E01EAA525399CB500B0759B /* Headers */, + 0E01EAA625399CB500B0759B /* Sources */, + 0E01EAA725399CB500B0759B /* Frameworks */, + 0E01EAA825399CB500B0759B /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = PSCBOnline; + productName = "PSCB-OOS-iOS"; + productReference = 0E01EAAA25399CB500B0759B /* PSCBOnline.framework */; + productType = "com.apple.product-type.framework"; + }; + 0E01EAB225399CB500B0759B /* PSCBOnlineTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0E01EAC125399CB500B0759B /* Build configuration list for PBXNativeTarget "PSCBOnlineTests" */; + buildPhases = ( + 0E01EAAF25399CB500B0759B /* Sources */, + 0E01EAB025399CB500B0759B /* Frameworks */, + 0E01EAB125399CB500B0759B /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 0E01EAB625399CB500B0759B /* PBXTargetDependency */, + ); + name = PSCBOnlineTests; + productName = "PSCB-OOS-iOSTests"; + productReference = 0E01EAB325399CB500B0759B /* PSCBOnlineTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 0E01EAA125399CB500B0759B /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1200; + LastUpgradeCheck = 1200; + TargetAttributes = { + 0E01EAA925399CB500B0759B = { + CreatedOnToolsVersion = 12.0.1; + LastSwiftMigration = 1200; + }; + 0E01EAB225399CB500B0759B = { + CreatedOnToolsVersion = 12.0.1; + }; + }; + }; + buildConfigurationList = 0E01EAA425399CB500B0759B /* Build configuration list for PBXProject "PSCBOnline" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 0E01EAA025399CB500B0759B; + productRefGroup = 0E01EAAB25399CB500B0759B /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 0E01EAA925399CB500B0759B /* PSCBOnline */, + 0E01EAB225399CB500B0759B /* PSCBOnlineTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 0E01EAA825399CB500B0759B /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0E790FAB253E017E0086AD71 /* LICENSE.txt in Resources */, + 0E790FA3253DEA930086AD71 /* PSCBOnline.podspec in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0E01EAB125399CB500B0759B /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 0E01EAA625399CB500B0759B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0E26F59F2547097300F80DAC /* RSAHelper.swift in Sources */, + 0EA2C872253CC44A00C5DD14 /* RequestWrapper+Serializable.swift in Sources */, + 0EA2C86A253B29A900C5DD14 /* PKPaymentToken+Serializable.swift in Sources */, + 0E0A2C1F25408AA1006BBBA1 /* CardData.swift in Sources */, + 0EA2C84E2539A5BA00C5DD14 /* Serializable.swift in Sources */, + 0E0A2C23254093D0006BBBA1 /* Payment+Serializable.swift in Sources */, + 0E26F5E82548717B00F80DAC /* PSCBAPI.swift in Sources */, + 0E26F5862542FB5800F80DAC /* Response.swift in Sources */, + 0E26F58F2545AE5800F80DAC /* JSONDecoders.swift in Sources */, + 0E01EAD325399DF600B0759B /* CustomerData.swift in Sources */, + 0E01EACD25399DB000B0759B /* Payment.swift in Sources */, + 0EA2C8582539F4D500C5DD14 /* PSCBOnlineClient.swift in Sources */, + 0EA2C8622539FAC200C5DD14 /* PaymentHandler.swift in Sources */, + 0E01EADE25399E8800B0759B /* RequestWrapper.swift in Sources */, + 0E26F62E2549591E00F80DAC /* DigestHelper.swift in Sources */, + 0E01EAD825399E3D00B0759B /* String.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0E01EAAF25399CB500B0759B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0EC9DB072540A1C8005392A0 /* OOSAPIClientTests.swift in Sources */, + 0EC9DB192541D9F9005392A0 /* PKPaymentTokenTests.swift in Sources */, + 0EC9DB0C2541B699005392A0 /* PassKitMocks.swift in Sources */, + 0E26F58A2542FE2000F80DAC /* ResponseTests.swift in Sources */, + 0EC9DB132541BF45005392A0 /* PKPaymentToken+SerializableTests.swift in Sources */, + 0E26F5A3254714B600F80DAC /* CardDataTests.swift in Sources */, + 0E26F63325495A5B00F80DAC /* DigestHelperTests.swift in Sources */, + 0E01EAE425399EE800B0759B /* PaymentTests.swift in Sources */, + 0E01EAB925399CB500B0759B /* PSCBOnlineTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 0E01EAB625399CB500B0759B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 0E01EAA925399CB500B0759B /* PSCBOnline */; + targetProxy = 0E01EAB525399CB500B0759B /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 0E01EABC25399CB500B0759B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 0E01EABD25399CB500B0759B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 0E01EABF25399CB500B0759B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = 46N426YCGP; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = PSCBOnline/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "ru.pscb.oos-ios-sdk"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 0E01EAC025399CB500B0759B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = 46N426YCGP; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = PSCBOnline/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "ru.pscb.oos-ios-sdk"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 0E01EAC225399CB500B0759B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 46N426YCGP; + INFOPLIST_FILE = PSCBOnlineTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = ru.pscb.PSCBOnlineTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 0E01EAC325399CB500B0759B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 46N426YCGP; + INFOPLIST_FILE = PSCBOnlineTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = ru.pscb.PSCBOnlineTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 0E01EAA425399CB500B0759B /* Build configuration list for PBXProject "PSCBOnline" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0E01EABC25399CB500B0759B /* Debug */, + 0E01EABD25399CB500B0759B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 0E01EABE25399CB500B0759B /* Build configuration list for PBXNativeTarget "PSCBOnline" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0E01EABF25399CB500B0759B /* Debug */, + 0E01EAC025399CB500B0759B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 0E01EAC125399CB500B0759B /* Build configuration list for PBXNativeTarget "PSCBOnlineTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0E01EAC225399CB500B0759B /* Debug */, + 0E01EAC325399CB500B0759B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 0E01EAA125399CB500B0759B /* Project object */; +} diff --git a/PSCBOnline.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/PSCBOnline.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/PSCBOnline.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/PSCBOnline.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/PSCBOnline.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/PSCBOnline.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/PSCBOnline.xcodeproj/project.xcworkspace/xcuserdata/oa.xcuserdatad/UserInterfaceState.xcuserstate b/PSCBOnline.xcodeproj/project.xcworkspace/xcuserdata/oa.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..c937c3659f71aaed1777469b78033fb06d0159f4 GIT binary patch literal 122230 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

B>6#Wu8k~@B=kzF-(&dv(HmnL`|d^e`uE` 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 literal 0 HcmV?d00001 diff --git a/PSCBOnline.xcodeproj/xcuserdata/oa.xcuserdatad/xcschemes/xcschememanagement.plist b/PSCBOnline.xcodeproj/xcuserdata/oa.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..0bcb71f --- /dev/null +++ b/PSCBOnline.xcodeproj/xcuserdata/oa.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,27 @@ + + + + + SchemeUserState + + PSCBOnline.xcscheme_^#shared#^_ + + orderHint + 0 + + + SuppressBuildableAutocreation + + 0E01EAA925399CB500B0759B + + primary + + + 0E01EAB225399CB500B0759B + + primary + + + + + diff --git a/PSCBOnline/Info.plist b/PSCBOnline/Info.plist new file mode 100644 index 0000000..9bcb244 --- /dev/null +++ b/PSCBOnline/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + + diff --git a/PSCBOnline/PSCBOnline.h b/PSCBOnline/PSCBOnline.h new file mode 100644 index 0000000..58e4a8b --- /dev/null +++ b/PSCBOnline/PSCBOnline.h @@ -0,0 +1,18 @@ +// +// PSCBOnline.h +// PSCBOnline +// +// Created by Antonov Ilia on 16.10.2020. +// + +#import + +//! Project version number for PSCBOnline. +FOUNDATION_EXPORT double PSCBOnlineVersionNumber; + +//! Project version string for PSCBOnline. +FOUNDATION_EXPORT const unsigned char PSCBOnlineVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/PSCBOnline/Sources/Extension/String.swift b/PSCBOnline/Sources/Extension/String.swift new file mode 100644 index 0000000..b9ff5b9 --- /dev/null +++ b/PSCBOnline/Sources/Extension/String.swift @@ -0,0 +1,26 @@ +// +// String.swift +// +// +// Created by AntonovIA on 12.10.2020. +// + +import Foundation + +extension String { + + private static let alphanumeric = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + + static func random(length: Int, allowedChars: String? = nil) -> String { + let seq: String = ((allowedChars == nil) ? alphanumeric : allowedChars!) as String + var rnd: String = "" + + for _ in 0.. String { + let data = string.data(using: .utf8)! + var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH)) + var sign = "" + + data.withUnsafeBytes { + _ = CC_SHA256($0.baseAddress, CC_LONG(data.count), &hash) + } + + for byte in hash { + sign += String(format: "%02x", UInt8(byte)) + } + + return sign.lowercased() + } + +} diff --git a/PSCBOnline/Sources/Helpers/JSONDecoders.swift b/PSCBOnline/Sources/Helpers/JSONDecoders.swift new file mode 100644 index 0000000..eb537e2 --- /dev/null +++ b/PSCBOnline/Sources/Helpers/JSONDecoders.swift @@ -0,0 +1,33 @@ +// +// JSONDecoders.swift +// PSCB-OOS-iOS +// +// Created by Antonov Ilia on 25.10.2020. +// + +import Foundation + +final public class JSONDecoders { + + public static func iso8601DateAwareDecoder() -> JSONDecoder { + return ISO8601DateAwareJSONDecoder() + } + +} + +// MARK: - ISO8601 date aware JSON Decoder +class ISO8601DateAwareJSONDecoder: JSONDecoder { + + static internal let dateTimeFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ" + + let dateFormatter: DateFormatter = DateFormatter() + let calendar = Calendar.current + + override init() { + super.init() + + dateFormatter.dateFormat = Self.dateTimeFormat + dateDecodingStrategy = .formatted(dateFormatter) + } + +} diff --git a/PSCBOnline/Sources/Helpers/RSAHelper.swift b/PSCBOnline/Sources/Helpers/RSAHelper.swift new file mode 100644 index 0000000..9b806f3 --- /dev/null +++ b/PSCBOnline/Sources/Helpers/RSAHelper.swift @@ -0,0 +1,86 @@ +// +// RSAHelper.swift +// PSCB-OOS-iOS +// +// Created by Antonov Ilia on 26.10.2020. +// + +import Foundation + +enum RSAKeyError: Error { + + /// Thrown when provided public key is invalid + case invalidKey + + /// Thrown when encryption of message failed + case encryption(OSStatus) + + /// Thrown when unable to initialize `SecKey` with given key string + case unmanaged(Unmanaged) +} + +public final class RSAHelper { + + internal static let key = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAPQsWyHaynwoc2tuMbengf1SFase9tPnwtPh4o1tR+94xsWztADdhhUaUBk/68ipaoZE8uSnM9UgdEPmOotFXyUCAwEAAQ==" + + internal static var secKey: SecKey? = nil + + static func initPSCBOnlinePublicKey() throws { + // Treat as assert + guard let keyData = Data(base64Encoded: key) else { + print("Invalid key string: \(key)") + throw RSAKeyError.invalidKey + } + + var attributes: CFDictionary { + return [ + kSecAttrKeyType: kSecAttrKeyTypeRSA, + kSecAttrKeyClass: kSecAttrKeyClassPublic, + kSecAttrKeySizeInBits: 2048, + kSecReturnPersistentRef: true + ] as CFDictionary + } + + var error: Unmanaged? = nil + guard let secKey = SecKeyCreateWithData(keyData as CFData, attributes, &error) else { + print("Error creating a security key with \(String(describing: keyData))") + print(error.debugDescription) + throw RSAKeyError.unmanaged(error!) + } + + Self.secKey = secKey + } + + /// Encrypts given message with OOS public key. + /// + /// - Parameters: + /// - message: A message to encrypt. + /// + /// - Returns: A base64 encoded string + /// + /// - Throws: `RSAKeyError` type error + public static func encryptEncodeBase64(message: String) throws -> String { + // Initialize OOS public key + if Self.secKey == nil { + try Self.initPSCBOnlinePublicKey() + } + + // Initialize buffers + let sKey = Self.secKey! + let buff = [UInt8](message.utf8) + + // Mutable encryption refs + var size = SecKeyGetBlockSize(sKey) + var kBuf = [UInt8](repeating: 0, count: size) + + let status = SecKeyEncrypt(sKey, SecPadding.PKCS1, buff, buff.count, &kBuf, &size) + + // Sanity check + guard status == errSecSuccess else { + throw RSAKeyError.encryption(status) + } + + return Data(bytes: kBuf, count: size).base64EncodedString() + } + +} diff --git a/PSCBOnline/Sources/Models/CardData.swift b/PSCBOnline/Sources/Models/CardData.swift new file mode 100644 index 0000000..bd8ed9a --- /dev/null +++ b/PSCBOnline/Sources/Models/CardData.swift @@ -0,0 +1,92 @@ +// +// CardData.swift +// PSCB-OOS-iOS +// +// Created by Antonov Ilia on 21.10.2020. +// + +import Foundation +import PassKit + +public enum CardDataError: Error { + case invalidExiryDate(String) + case invalidCvCode + case invalidPan +} + +/// Billing info. +/// Detailed card information +public struct CardData { + let pan: String + let expiryYear: Int + let expiryMonth: Int + let cardholder: String? + let cvCode: String + + /// Constructs card data from all necessary for billing fields + /// + /// - Parameters: + /// - pan: Card number + /// - expiryYear: A full year number (e.g: `2020`). For production cannot be in the past + /// - expiryMonth: A month number from 1 to 12. + /// - cvCode: CVC/CVV number + /// - cardholder: (Optional) Cardholder name in latin. + /// + /// - Returns: optional instance of a new card. If validation fails returns nil + public init?(pan: String, expiryYear: Int, expiryMonth: Int, cvCode: String, cardholder: String? = nil) { + guard expiryMonth >= 1 && expiryMonth <= 12 else { + print("Month can be between 1 and 12") + return nil + } + + #if !DEBUG + // todo check year + month in the past + let currentYear = Calendar.current.component(.year, from: Date()) + guard expiryYear >= currentYear else { + print("Year must be current or in the future") + return nil + } + #endif + + self.pan = pan + self.expiryMonth = expiryMonth + self.expiryYear = expiryYear + self.cvCode = cvCode + self.cardholder = cardholder + } + + /// - Returns: A last 2 digits of the expiry year + public func getExpYearString() -> String { + return String(String(expiryYear).dropFirst(2)) + } + + /// - Returns: A zero padded expiry month string + public func getExpMonthString() -> String { + return expiryMonth < 10 ? "0\(self.expiryMonth)" : String(self.expiryMonth) + } + +} + +extension CardData { + + internal static let pubKey: Data = { + let key = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAPQsWyHaynwoc2tuMbengf1SFase9tPnwtPh4o1tR+94xsWztADdhhUaUBk/68ipaoZE8uSnM9UgdEPmOotFXyUCAwEAAQ==" + + return Data(base64Encoded: key)! + }() + + + /// Produces a cryptogram string from given `CardData` + /// See: https://docs.pscb.ru/oos/shpa.html#shlyuz-prozrachnoj-avtorizacii-sozdanie-platezha-otvet + /// + /// - Returns: a Base64 encoded cryptogram string + public func toCryptgramString() throws -> String { + let month = self.getExpMonthString() + let year = self.getExpYearString() + let joinedString = "\(self.pan)|\(month)|\(year)|\(cvCode)\(self.cardholder ?? "")" + let base64Encode = try RSAHelper.encryptEncodeBase64(message: joinedString) + + return base64Encode + } + +} diff --git a/PSCBOnline/Sources/Models/CustomerData.swift b/PSCBOnline/Sources/Models/CustomerData.swift new file mode 100644 index 0000000..710ea0b --- /dev/null +++ b/PSCBOnline/Sources/Models/CustomerData.swift @@ -0,0 +1,29 @@ +// +// CustomerData.swift +// +// +// Created by Antonov Ilia on 12.10.2020. +// + +import Foundation + +/// A costumer data to present in PaymentData +public struct CustomerData: Codable { + + /// Unique costumer ID + let account: String + + let comment: String? + + let email: String? + + let phone: String? + + enum CodingKeys: String, CodingKey { + case account = "customerAccount" + case comment = "customerComment" + case email = "customerEmail" + case phone = "customerPhone" + } + +} diff --git a/PSCBOnline/Sources/Models/Payment.swift b/PSCBOnline/Sources/Models/Payment.swift new file mode 100644 index 0000000..74b0b37 --- /dev/null +++ b/PSCBOnline/Sources/Models/Payment.swift @@ -0,0 +1,90 @@ +// +// Payment.swift +// +// +// Created by AntonovIA on 12.10.2020. +// + +import Foundation + +// MARK: - Payment methods + +internal enum PaymentMethods: String { + case shpa = "ac-shpa" +} + +// MARK: - Request Message + +public struct Payment: Encodable { + + // Required: + + /// Payment amount in rub + /// min: 1.00 + public let amount: Decimal + + /// Your market order ID + /// maxlength: 20 + public let orderId: String + + // Payment method + internal let paymentMethod: PaymentMethods = .shpa + + // Optional: + + /// Human readable `orderId` + /// Defaults to `orderId` + var showOrderId: String? + + /// Detailed text describing this order payment. + var details: String? + + /// Data about paying customer + var customer: CustomerData? + + // + var recurrentable: Bool = false + + public init(amount: Decimal, orderId: String, + showOrderId: String? = nil, details: String? = nil, + customer: CustomerData? = nil, recurrentable: Bool = false) { + self.amount = amount + self.orderId = orderId + self.showOrderId = (showOrderId == nil) ? orderId : showOrderId + self.details = details + self.customer = customer + self.recurrentable = recurrentable + } + + // MARK: - Encoder + + private enum CodingKeys: String, CodingKey { + case amount, orderId, showOrderId, details, recurrentable, customer + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(amount, forKey: .amount) + try container.encode(orderId, forKey: .orderId) + try container.encode(showOrderId, forKey: .showOrderId) + try container.encode(details, forKey: .details) + try container.encode(recurrentable, forKey: .recurrentable) + + // Customer + try customer?.encode(to: encoder) + } + + #if DEBUG + + public static let example = Payment( + amount: 150.00, orderId: String.random(length: 6), + details: "Wonderful warm socks", + customer: CustomerData( + account: "ID-12345", comment: "By tomorrow please", + email: "foo@bar.com", phone: "+7 900 000 00 00" + ) + ) + + #endif + +} diff --git a/PSCBOnline/Sources/Models/RequestWrapper.swift b/PSCBOnline/Sources/Models/RequestWrapper.swift new file mode 100644 index 0000000..ad4aebc --- /dev/null +++ b/PSCBOnline/Sources/Models/RequestWrapper.swift @@ -0,0 +1,44 @@ +// +// RequestWrapper.swift +// +// +// Created by Antonov Ilia on 12.10.2020. +// + +import Foundation + +// MARK: - Top level request + +/// Top level request wrapper for OOS requests +public struct RequestWrapper: Encodable { + + /// Merchant ID + public let marketPlaceId: String + + /// Payment info + public let payment: Payment + + /// Encoded card data + public let cardData: String + + /// Creates instance of RequestWrapper + /// + /// - Parameters: + /// - marketPlaceId: Your OOS market place ID + /// - payment: Payment details object + /// - cardData: Encoded card data + /// + /// - Returns: Prepared request ready to fire + public init(marketPlaceId: String, payment: Payment, cardData: String) { + self.marketPlaceId = marketPlaceId + self.payment = payment + self.cardData = cardData + } + + private enum CodingKeys: String, CodingKey { + case marketPlaceId = "marketPlace" + case payment + case cardData + } + +} diff --git a/PSCBOnline/Sources/Models/Response.swift b/PSCBOnline/Sources/Models/Response.swift new file mode 100644 index 0000000..d23f383 --- /dev/null +++ b/PSCBOnline/Sources/Models/Response.swift @@ -0,0 +1,100 @@ +// +// Response.swift +// PSCB-OOS-iOS +// +// Created by Antonov Ilia on 23.10.2020. +// + +import Foundation + +// MARK: - PSCB OOS Response status +public enum ResponseStatus: String, Decodable { + case success = "STATUS_SUCCESS" + case failure = "STATUS_FAILURE" +} + +// MARK: - Acquiring data +public struct AcquiringResponseData: Decodable { + public let paReq: String? + public let order: String? + public let termUrl: String? + public let url: String? + public let md: String? + + private enum CodingKeys: String, CodingKey { + case order, termUrl = "TermUrl", url = "URL", md = "MD", paReq = "PaReq" + } +} + +// MARK: - OOS Error + +/// An error structure for OOS errors returned in flat JSON +/// `errorCode` and `errorDescription` +public struct ResponseError: Decodable { + + public let code: String + public let description: String? + + private enum CodingKeys: String, CodingKey { + case code = "errorCode" + case description = "errorDescription" + } + +} + +// MARK: - OOS Response + +/// JSON Response from OOS API +public struct Response: Decodable { + + public let status: ResponseStatus + public let requestId: String + public let error: ResponseError? + public let payment: ResponsePayment? + public let acquiringData: AcquiringResponseData? + public let description: String? + + private enum CodingKeys: String, CodingKey { + case status, requestId, payment, acquiringData, description + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.status = try container.decode(ResponseStatus.self, forKey: .status) + self.requestId = try container.decode(String.self, forKey: .requestId) + self.description = try? container.decode(String.self, forKey: .description) + + // Decode flat and nested data into nested structs + self.payment = try? container.decode(ResponsePayment.self, forKey: .payment) + self.acquiringData = try? AcquiringResponseData(from: decoder) + self.error = try? ResponseError(from: decoder) + } + +} + +// MARK: - Payment status +/// Payment stats +/// see: https://docs.pscb.ru/oos/index.html#obshaya-informaciya-spravochniki-statusy-platezhej +public enum PaymentState: String, Decodable { + case sent = "sent" + case new = "new" + case end = "end" + case refunded = "ref" + case hold = "hold" + case expired = "exp" + case canceled = "canceled" + case error = "error" + case rejected = "rej" + case undefined = "undef" +} + +// MARK: - Payment response struct +public struct ResponsePayment: Decodable { + public let orderId: String + public let showOrderId: String + public let paymentId: String + public let amount: Decimal + public let state: PaymentState + public let marketPlace: UInt16 + public let stateDate: Date +} diff --git a/PSCBOnline/Sources/PSCBAPI.swift b/PSCBOnline/Sources/PSCBAPI.swift new file mode 100644 index 0000000..83e04d6 --- /dev/null +++ b/PSCBOnline/Sources/PSCBAPI.swift @@ -0,0 +1,55 @@ +// +// OOSAPI.swift +// PSCB-OOS-iOS +// +// Created by Antonov Ilia on 27.10.2020. +// + +import Foundation +import PassKit + +/// Utility fabric methods required for PSCB API. +public class PSCBAPI { + + public static let supportedNetworks: [PKPaymentNetwork] = [.visa, .masterCard] + + /// - Returns: a tuple describing if application `canMakePayments` and `canSetupCards` using supported networks. + public class func canMakePayments() -> (canMakePayments: Bool, canSetupCards: Bool) { + return ( + PKPaymentAuthorizationController.canMakePayments(), + PKPaymentAuthorizationController.canMakePayments(usingNetworks: supportedNetworks) + ) + } + + /// Creates a default instance of `PKPaymentRequest` with pre-default parameters. + /// + /// - Returns: `PKPaymentRequest` + public class func makePaymentRequest(items: [PKPaymentSummaryItem]) -> PKPaymentRequest { + return makePaymentRequest(merchantId: "merchant.pscb.pay", items: items) + } + + /// Creates a default instance of `PKPaymentRequest` with pre-default parameters. + /// + /// - Parameters: + /// - merchantId: Your Apple Pay merchant ID + /// - items: Summary items + /// + /// - Returns: `PKPaymentRequest` + public class func makePaymentRequest(merchantId: String, items: [PKPaymentSummaryItem]) -> PKPaymentRequest { + let request = PKPaymentRequest() + request.merchantIdentifier = merchantId + request.supportedNetworks = Self.supportedNetworks + + if #available(iOS 11.0, *) { + //request.supportedCountries = ["RU", "UA", "BY", "US"] + } + + request.merchantCapabilities = .capability3DS + request.countryCode = "RU" + request.currencyCode = "RUB" + request.paymentSummaryItems = items + + return request + } + +} diff --git a/PSCBOnline/Sources/PSCBOnlineClient.swift b/PSCBOnline/Sources/PSCBOnlineClient.swift new file mode 100644 index 0000000..95538fb --- /dev/null +++ b/PSCBOnline/Sources/PSCBOnlineClient.swift @@ -0,0 +1,306 @@ +// +// OOSAPIClient.swift +// PSCB-OOS-iOS +// +// Created by Antonov Ilia on 12.10.2020. +// + +import Foundation +import PassKit + +// MARK: - Environments + +public enum BackendEnvironment: String { + + case production = "oos.pscb.ru" + case sandbox = "oosdemo.pscb.ru" + +} + +// MARK: - Request status + +/// Backend request status +/// Consists of two possible states: `.success` and `.failure`. +/// +/// This one represents if request to the backend server was successful +public enum RequestStatus { + case success, failure +} + +// MARK: - Response wrapper + +/// Backend response +public struct BackendResponse { + + /// Represents request status + public let status: RequestStatus + + /// Hold error information if request is a `.failure` + public let error: Error? + + /// Holds response information if request is a `.success` + public let response: Response? +} + +// MARK: - Alias for completion handler + +/// Callback for `OOSAPIClient.send` method +public typealias APICompletionHandler = (BackendResponse) -> Void + +/// Callback for another `OOSAPIClient.send` method which fires after certain guards ensure success or failure +/// to simplify API usage +public typealias PostResponseHandler = (Error?, Response?) -> Void + +// MARK: - API Integration errors + +/// Possible erros when dealing with OOS backend +public enum OOSErrors: Error { + case noData + case parse(Error) + case cause(Error) + + /// Consists of error code and error description + case backend(String, String) +} + +// MARK: - API Client + +/// Implementation of OOS Merchant API HTTP protocol. +final public class PSCBOnlineClient { + + private lazy var urlSession: URLSession = { + URLSession(configuration: .default, delegate: nil, delegateQueue: .main) + }() + + private lazy var url: URL = { + var components = URLComponents() + components.scheme = "https" + components.host = self.environment.rawValue + components.path = "/merchantApi/payShpa" + + return components.url! + }() + + private let decoder = JSONDecoders.iso8601DateAwareDecoder() + + // init: + + let environment: BackendEnvironment + let marketPlaceId: String + let signingKey: String + + /// Initializes `OOSAPIClient` + /// + /// - Parameters: + /// - environment: Backend environment. Which one to use. For testing environment use `.sandbox` + /// - marketPlaceId: Your OOS market-place ID + /// - siginingKey: Your OOS signing key + public init(environment: BackendEnvironment, marketPlaceId: String, signingKey: String) { + self.environment = environment + self.marketPlaceId = marketPlaceId + self.signingKey = signingKey + } + + // impl: + + /// Creates instance of `RequestWrapper` for a backend to process from given PKPayment instance and other details + /// + /// - Parameters: + /// - payment: authorized PKPayment instance + /// - amount: total order amount + /// - orderId: Unique merchant order ID + /// + /// - Returns: RequestWrapper instance for backend + public func makeRequestWithPayment(payment: PKPayment, amount: Decimal, orderId: String) throws -> RequestWrapper { + return try makeRequestWithPayment(pkPayment: payment, payment: Payment(amount: amount, orderId: orderId)) + } + + /// Creates instance of `RequestWrapper` for a backend to process from given `PKPayment` instance and backend `Payment` instance + /// + /// - Parameters: + /// - pkPayment: authorized `PKPayment` instance + /// - payment: backend `Payment` details + /// + /// - Returns: RequestWrapper instance for backend + public func makeRequestWithPayment(pkPayment: PKPayment, payment: Payment) throws -> RequestWrapper { + // Get card data cryptogram from payment token + let cardDataCrypto = try pkPayment.token.toCryptogramString() + + // Backend request wrapper + let requestWrapper = RequestWrapper( + marketPlaceId: self.marketPlaceId, + payment: payment, + cardData: cardDataCrypto + ) + + // Fail early + try requestWrapper.assumeSerializes() + + return requestWrapper + } + + /// Creates instance of `RequestWrapper` for a backend to process from raw card data instead of ApplePay token + /// + /// Example: + /// ``` + /// let card = CardData( + /// pan: "409444400001234", + /// expiryYear: 2025, + /// expiryMonth: 12, + /// cvCode: "000", + /// cardholder: "JOHN DOE" + /// ) + /// + /// let payment = Payment(amount: Decimal(1500), orderId: "XC-12345") + /// let request = try apiClient.createRequestWithCardData(card: card, payment: payment) + /// + /// // Send to backend: + /// apiClient.send(request) { (response) in /code/ } + /// ``` + /// + /// - Parameters: + /// - cardData: an instance of card data. + public func makeRequestWithCardData(card: CardData, payment: Payment) throws -> RequestWrapper { + let cryptogram = try card.toCryptgramString() + let request = RequestWrapper( + marketPlaceId: self.marketPlaceId, + payment: payment, + cardData: cryptogram + ) + + // Fail-early compile + try request.assumeSerializes() + + return request + } + + /// Signs and sends compiled `RequestWrapper` to the backend server + /// Fires `APICompletionHandler` once requests succeeds or fails + /// + /// - Parameters: + /// - request: `RequestWrapper` created from `createRequestWithPayment(...)` + /// - completionHander: `APICompletionHandler` a callback for when requests succeeds or fails + /// + public func send(_ request: RequestWrapper, completionHandler: @escaping APICompletionHandler) { + // Calculate signature and get HTTP body at the same time + let httpBody = try! request.serializeToString() + let signature = calculateSignature(httpBody) + + // Request params + let url = self.url + var req = URLRequest(url: url) + + // Set request parameters + req.httpMethod = "POST" + req.httpBody = httpBody.data(using: .utf8) + req.setValue(signature, forHTTPHeaderField: "Signature") + + print(">> Request JSON: \(String(describing: String(data: req.httpBody!, encoding: .utf8)))") + + let task = urlSession.dataTask(with: req) { (data, res, err) in + guard err == nil else { + let error = self.err_requestError(req, error: err) + completionHandler(error) + return + } + + guard let data = data else { + let error = self.failure(req, error: OOSErrors.noData) + completionHandler(error) + return + } + + print("<< Response data: \(String(data: data, encoding: .utf8) ?? "N/A")") + + do { + let response = try self.decoder.decode(Response.self, from: data) + + // Finaly success + let success = BackendResponse(status: .success, error: nil, response: response) + + completionHandler(success) + } catch let error { + print("Failed to do task on request \(req)") + print(error.localizedDescription) + + // Reading and parsing errors + let failure = self.failure(req, error: .parse(error)) + completionHandler(failure) + } + + } + + // Fire task + task.resume() + } + + /// Signs and sends compiled `RequestWrapper` to the backend server + /// Fires `PostResponseHandler` once requests succeeds or fails. + /// `PostResponseHandler` accepts two arguments: `Error?` and `Response?`. + /// + /// If requests succeeds and payment in desired state `Error?` will always be `nil`. + /// `Response?` presents on some errors and on all successes. + /// + /// This is an utility method to reduce boilerplate for necessary checks. + /// + /// - Parameters: + /// - request: `RequestWrapper` created from `createRequestWithPayment(...)` + /// - completionHander: `APICompletionHandler` a callback for when requests succeeds or fails + /// + public func send(_ request: RequestWrapper, responseHandler: @escaping PostResponseHandler) { + // Construct a closure wrapper with predefined guards and checks + let wrapper: APICompletionHandler = { (backendResponse) in + // HTTP request succeeded? + guard backendResponse.status == .success else { + print("Failed to execute request due to error: \(String(describing: backendResponse.error))") + + responseHandler(OOSErrors.cause(backendResponse.error!), nil) + return + } + + // Backend returned valid response? + guard let response = backendResponse.response else { + print("Backend returned empty body") + + responseHandler(OOSErrors.noData, nil) + return + } + + // Backend payment succeeded? + guard response.status == .success else { + print("Unable to process payment") + + let responseError = response.error + let backendError = OOSErrors.backend(responseError?.code ?? "FAILED", responseError?.description ?? "") + + // At this point we can pass response too + responseHandler(backendError, response) + return + } + + // Finally succeeds + responseHandler(nil, response) + } + + // Send using more lower-level method above + send(request, completionHandler: wrapper) + } + + // MARK: - Private + + private func err_requestError(_ req: URLRequest, error: Error?) -> BackendResponse { + print("Failed to do task on request \(req) with error: \(String(describing: error))") + return BackendResponse(status: .failure, error: OOSErrors.cause(error!), response: nil) + } + + private func failure(_ req: URLRequest, error: OOSErrors) -> BackendResponse { + print("Request \(req) failed with error: \(String(describing: error))") + return BackendResponse(status: .failure, error: error, response: nil) + } + + private func calculateSignature(_ jsonString: String) -> String { + let composite = (jsonString + self.signingKey) + return DigestHelper.sha256String(composite) + } + +} diff --git a/PSCBOnline/Sources/PaymentHandler.swift b/PSCBOnline/Sources/PaymentHandler.swift new file mode 100644 index 0000000..a6bad97 --- /dev/null +++ b/PSCBOnline/Sources/PaymentHandler.swift @@ -0,0 +1,113 @@ +// +// PaymentHandler.swift +// PSCB-OOS-iOS +// +// Created by Antonov Ilia on 16.10.2020. +// + +import Foundation +import PassKit + +public typealias PaymentCompletionHandler = (Bool) -> Void + +public class ApplePayHandler: NSObject { + + static let supportedNetworks: [PKPaymentNetwork] = [.visa, .masterCard] + + // MARK: - Fields + + public let merchantId: String + public let merchantKey: String + public let summaryItems: [PKPaymentSummaryItem] + + // MARK: - Properties + + var paymentController: PKPaymentAuthorizationController? + var paymentStatus: PKPaymentAuthorizationStatus = .failure + var completionHandler: PaymentCompletionHandler? + var payment: Payment? + + public init?(merchantId: String, merchantKey: String, summaryItems: [PKPaymentSummaryItem]) { + guard PKPaymentAuthorizationController.canMakePayments(usingNetworks: Self.supportedNetworks) else { + print("Could not instantiate ApplePayHandler. Cannot make payments with provided networks") + return nil + } + + self.merchantId = merchantId + self.merchantKey = merchantKey + self.summaryItems = summaryItems + } + + public func handle(payment: Payment, completion: @escaping PaymentCompletionHandler) { + self.completionHandler = completion + self.payment = payment + + NSLog("About to handle payment: \(payment)") + + paymentController = PKPaymentAuthorizationController(paymentRequest: paymentRequest) + paymentController?.delegate = self + paymentController?.present { (presented) in + if !presented { + NSLog("Could not present PKPaymentAuthorizationController") + self.completionHandler!(false) + } else { + NSLog("PKPaymentAuthorizationController presented") + } + } + + } + + // MARK: - Private details + + private var paymentRequest: PKPaymentRequest { + let request = PKPaymentRequest() + + request.countryCode = "RU" + request.merchantIdentifier = "merchant.pscb.pay" + request.currencyCode = "RUB" + request.supportedNetworks = Self.supportedNetworks + request.merchantCapabilities = .capability3DS + request.paymentSummaryItems = summaryItems + + return request + } + +} + + +// MARK: - Extension for PKPaymentAuthorizationControllerDelegate + +extension ApplePayHandler: PKPaymentAuthorizationControllerDelegate { + + public func paymentAuthorizationController(_ controller: PKPaymentAuthorizationController, + didAuthorizePayment payment: PKPayment, + completion: @escaping (PKPaymentAuthorizationStatus) -> Void) { + +// print("Payment string") +// let paymentString = try! payment.serializeToString() +// print(paymentString) +// +// // +// +// self.paymentStatus = .success + completion(PKPaymentAuthorizationStatus.success) + } + + @available(iOS 11.0, *) + public func paymentAuthorizationController(_ controller: PKPaymentAuthorizationController, didAuthorizePayment payment: PKPayment, handler completion: @escaping (PKPaymentAuthorizationResult) -> Void) { +// +// NSLog("About to send request to backend") +// let paymentString = try! payment.serializeToString() +// +// NSLog("Payment JSON: \(paymentString)") +// + let result = PKPaymentAuthorizationResult(status: .success, errors: nil) + completion(result) + self.completionHandler?(true) + } + + public func paymentAuthorizationControllerDidFinish(_ controller: PKPaymentAuthorizationController) { + controller.dismiss(completion: nil) + } + +} diff --git a/PSCBOnline/Sources/Serializable/PKPaymentToken+Serializable.swift b/PSCBOnline/Sources/Serializable/PKPaymentToken+Serializable.swift new file mode 100644 index 0000000..a64bacd --- /dev/null +++ b/PSCBOnline/Sources/Serializable/PKPaymentToken+Serializable.swift @@ -0,0 +1,79 @@ +// +// PKPaymentToken+Serializable.swift +// PSCB-OOS-iOS +// +// Created by OA on 17.10.2020. +// + +import Foundation +import PassKit + +// MARK: - Comfront to serializable +extension PKPaymentToken: Serializable { + + // Serializes token to a JSONLike dictionary + public func serializeToJSON() -> JSONDict { + let paymentJson: JSONDict? = try? JSONSerialization.jsonObject( + with: self.paymentData, options: .mutableContainers + ) as? JSONDict + + var paymentType: String = "debit" + var methodAndNetwork: JSONDict = [ + "network": "", + "type": paymentType, + "displayName": "" + ] + + if #available(iOS 9.0, *) { + methodAndNetwork = [ + "network": self.paymentMethod.network?.rawValue ?? "", + "type": paymentType, + "displayName": self.paymentMethod.displayName ?? "" + ] + + switch self.paymentMethod.type { + case .debit: + paymentType = "debit" + case .credit: + paymentType = "credit" + case .store: + paymentType = "store" + case .prepaid: + paymentType = "prepaid" + default: + paymentType = "unknown" + } + } + + return [ + "paymentData": paymentJson, + "transactionIdentifier": self.transactionIdentifier, + "paymentMethod": methodAndNetwork + ] as JSONDict + } + +} + +// MARK: - To string cryptogram +extension PKPaymentToken { + + private func paymentJSONData() -> JSONDict? { + let json = self.serializeToJSON() + let data = json["paymentData"] as! JSONDict? + + return data + } + + /// Creates a Base64 encoded cryptogram string accepted by PSCB OOS protocol + /// May throw JSON serialization errors if token could not be converted to JSON string before writing a cryptogram. + /// + /// - Returns: Base64 encoded string + public func toCryptogramString() throws -> String { + let json = self.paymentJSONData() + let data = try JSONSerialization.data(withJSONObject: json!, options: []) + // let string = String(data: data, encoding: .utf8)! + + return data.base64EncodedString(options: Data.Base64EncodingOptions(rawValue: 0)) + } + +} diff --git a/PSCBOnline/Sources/Serializable/Payment+Serializable.swift b/PSCBOnline/Sources/Serializable/Payment+Serializable.swift new file mode 100644 index 0000000..74767bd --- /dev/null +++ b/PSCBOnline/Sources/Serializable/Payment+Serializable.swift @@ -0,0 +1,27 @@ +// +// Payment+Serializable.swift +// PSCB-OOS-iOS +// +// Created by Antonov Ilia on 21.10.2020. +// + +import Foundation + +extension Payment: Serializable { + + public func serializeToJSON() -> JSONDict { + var json = JSONDict() + json["orderId"] = self.orderId + json["amount"] = self.amount + json["showOrderId"] = self.showOrderId + json["details"] = self.details + json["recurrentable"] = self.recurrentable + json["customerAccount"] = self.customer?.account + json["customerComment"] = self.customer?.comment + json["customerEmail"] = self.customer?.email + json["customerPhone"] = self.customer?.phone + + return json + } + +} diff --git a/PSCBOnline/Sources/Serializable/RequestWrapper+Serializable.swift b/PSCBOnline/Sources/Serializable/RequestWrapper+Serializable.swift new file mode 100644 index 0000000..25a1bdf --- /dev/null +++ b/PSCBOnline/Sources/Serializable/RequestWrapper+Serializable.swift @@ -0,0 +1,21 @@ +// +// RequestWrapper+Serializable.swift +// PSCB-OOS-iOS +// +// Created by Antonov Ilia on 18.10.2020. +// + +import Foundation + +extension RequestWrapper: Serializable { + + public func serializeToJSON() -> JSONDict { + var json = JSONDict() + json["marketPlace"] = self.marketPlaceId + json["payment"] = self.payment.serializeToJSON() + json["cardData"] = self.cardData + + return json + } + +} diff --git a/PSCBOnline/Sources/Serializable/Serializable.swift b/PSCBOnline/Sources/Serializable/Serializable.swift new file mode 100644 index 0000000..520de98 --- /dev/null +++ b/PSCBOnline/Sources/Serializable/Serializable.swift @@ -0,0 +1,61 @@ +// +// Serializable.swift +// PSCB-OOS-iOS +// +// Created by OA on 16.10.2020. +// + +import Foundation + +// MARK: - Serializable + +public protocol Serializable { + + /// Serializes current object to generic JSON-Like dictionary + func serializeToJSON() -> JSONDict + +} + +// MARK: - Extension + +public extension Serializable { + + /// Serializes current object to data + func serializeToData() throws -> Data { +// let encoder = JSONEncoder() +// let data = try encoder.encode(self.serializeToJSON()) +// +// return data + return try JSONSerialization.data( + withJSONObject: self.serializeToJSON(), + options: [] + ) + } + + /// Serialzes to JSON-String + func serializeToString() throws -> String { + let data = try self.serializeToData() + return String(data: data, encoding: .utf8)! + } + + /// Used for checking purposes if this object can be serialized without exception + func assumeSerializes() throws { + try serializeToString() + } + +} + +// MARK: - JSONDict + +public typealias JSONDict = [AnyHashable: AnyHashable] + +//extension JSONDict { +// +// public mutating func add(key: T, nullable value: JSONDict.Value?) where T.RawValue == JSONDict.Key { +// if let value = value { +// self.updateValue(value, forKey: key.rawValue) +// } +// } +// +//} + diff --git a/PSCBOnlineSample/PSCBOnlineSample.xcodeproj/project.pbxproj b/PSCBOnlineSample/PSCBOnlineSample.xcodeproj/project.pbxproj new file mode 100644 index 0000000..bb8c6f7 --- /dev/null +++ b/PSCBOnlineSample/PSCBOnlineSample.xcodeproj/project.pbxproj @@ -0,0 +1,762 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXBuildFile section */ + 001B6B6B8EAA1CC9E76C93F2 /* Pods_PSCBOnlineSample_PSCBOnlineSampleUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 215AD129A382ED26BE4A9ADC /* Pods_PSCBOnlineSample_PSCBOnlineSampleUITests.framework */; }; + 0ECA01D9254C88BF003DBD74 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECA01D8254C88BF003DBD74 /* AppDelegate.swift */; }; + 0ECA01DB254C88BF003DBD74 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECA01DA254C88BF003DBD74 /* SceneDelegate.swift */; }; + 0ECA01DD254C88BF003DBD74 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECA01DC254C88BF003DBD74 /* ContentView.swift */; }; + 0ECA01DF254C88C4003DBD74 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0ECA01DE254C88C4003DBD74 /* Assets.xcassets */; }; + 0ECA01E2254C88C4003DBD74 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0ECA01E1254C88C4003DBD74 /* Preview Assets.xcassets */; }; + 0ECA01E5254C88C4003DBD74 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0ECA01E3254C88C4003DBD74 /* LaunchScreen.storyboard */; }; + 0ECA01F0254C88C5003DBD74 /* sdk_sampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECA01EF254C88C5003DBD74 /* sdk_sampleTests.swift */; }; + 0ECA01FB254C88C5003DBD74 /* sdk_sampleUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECA01FA254C88C5003DBD74 /* sdk_sampleUITests.swift */; }; + 0ECA0218254C8C12003DBD74 /* Podfile in Resources */ = {isa = PBXBuildFile; fileRef = 0ECA0217254C8C12003DBD74 /* Podfile */; }; + 0ECA0237254C8DA0003DBD74 /* PaymentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECA0236254C8DA0003DBD74 /* PaymentHandler.swift */; }; + 37FA89195E22BD0C552D887A /* Pods_PSCBOnlineSampleTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 75E713AE69B50BE64C2D03E8 /* Pods_PSCBOnlineSampleTests.framework */; }; + 69E05BEE77EA2E75B91678EE /* Pods_PSCBOnlineSample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ADAE0E73DFA819AE992A9C59 /* Pods_PSCBOnlineSample.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 0ECA01EC254C88C5003DBD74 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0ECA01CD254C88BF003DBD74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 0ECA01D4254C88BF003DBD74; + remoteInfo = sdk.sample; + }; + 0ECA01F7254C88C5003DBD74 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0ECA01CD254C88BF003DBD74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 0ECA01D4254C88BF003DBD74; + remoteInfo = sdk.sample; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 0ECA01D5254C88BF003DBD74 /* PSCBOnlineSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PSCBOnlineSample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 0ECA01D8254C88BF003DBD74 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 0ECA01DA254C88BF003DBD74 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 0ECA01DC254C88BF003DBD74 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 0ECA01DE254C88C4003DBD74 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 0ECA01E1254C88C4003DBD74 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 0ECA01E4254C88C4003DBD74 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 0ECA01E6254C88C4003DBD74 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 0ECA01EB254C88C5003DBD74 /* PSCBOnlineSampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PSCBOnlineSampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 0ECA01EF254C88C5003DBD74 /* sdk_sampleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = sdk_sampleTests.swift; sourceTree = ""; }; + 0ECA01F1254C88C5003DBD74 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 0ECA01F6254C88C5003DBD74 /* PSCBOnlineSampleUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PSCBOnlineSampleUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 0ECA01FA254C88C5003DBD74 /* sdk_sampleUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = sdk_sampleUITests.swift; sourceTree = ""; }; + 0ECA01FC254C88C5003DBD74 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 0ECA020B254C8902003DBD74 /* sdk.sample.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = sdk.sample.entitlements; sourceTree = ""; }; + 0ECA0217254C8C12003DBD74 /* Podfile */ = {isa = PBXFileReference; lastKnownFileType = text; path = Podfile; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; + 0ECA0236254C8DA0003DBD74 /* PaymentHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentHandler.swift; sourceTree = ""; }; + 215AD129A382ED26BE4A9ADC /* Pods_PSCBOnlineSample_PSCBOnlineSampleUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PSCBOnlineSample_PSCBOnlineSampleUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 2EAAA258959A1F5D92CFD851 /* Pods-PSCBOnlineSample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PSCBOnlineSample.release.xcconfig"; path = "Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample.release.xcconfig"; sourceTree = ""; }; + 3E1FFD5E11C6E53BAB8BDD2B /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.release.xcconfig"; path = "Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.release.xcconfig"; sourceTree = ""; }; + 597562929002737E312BE5A9 /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.debug.xcconfig"; path = "Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.debug.xcconfig"; sourceTree = ""; }; + 5D25A9250438F9C8AA6153F8 /* Pods-PSCBOnlineSample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PSCBOnlineSample.debug.xcconfig"; path = "Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample.debug.xcconfig"; sourceTree = ""; }; + 75E713AE69B50BE64C2D03E8 /* Pods_PSCBOnlineSampleTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PSCBOnlineSampleTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + ADAE0E73DFA819AE992A9C59 /* Pods_PSCBOnlineSample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PSCBOnlineSample.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + AE94091A3CE7B540776F6146 /* Pods-PSCBOnlineSampleTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PSCBOnlineSampleTests.release.xcconfig"; path = "Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests.release.xcconfig"; sourceTree = ""; }; + EE4EA7D50047330E553B55B6 /* Pods-PSCBOnlineSampleTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PSCBOnlineSampleTests.debug.xcconfig"; path = "Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests.debug.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 0ECA01D2254C88BF003DBD74 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 69E05BEE77EA2E75B91678EE /* Pods_PSCBOnlineSample.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0ECA01E8254C88C5003DBD74 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 37FA89195E22BD0C552D887A /* Pods_PSCBOnlineSampleTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0ECA01F3254C88C5003DBD74 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 001B6B6B8EAA1CC9E76C93F2 /* Pods_PSCBOnlineSample_PSCBOnlineSampleUITests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0ECA01CC254C88BF003DBD74 = { + isa = PBXGroup; + children = ( + 0ECA0217254C8C12003DBD74 /* Podfile */, + 0ECA01D7254C88BF003DBD74 /* PSCBOnlineSample */, + 0ECA01EE254C88C5003DBD74 /* PSCBOnlineSampleTests */, + 0ECA01F9254C88C5003DBD74 /* PSCBOnlineSampleUITests */, + 0ECA01D6254C88BF003DBD74 /* Products */, + A6BE523F775C844325774B50 /* Pods */, + CC92252B44ABB68C5546B8DC /* Frameworks */, + ); + sourceTree = ""; + }; + 0ECA01D6254C88BF003DBD74 /* Products */ = { + isa = PBXGroup; + children = ( + 0ECA01D5254C88BF003DBD74 /* PSCBOnlineSample.app */, + 0ECA01EB254C88C5003DBD74 /* PSCBOnlineSampleTests.xctest */, + 0ECA01F6254C88C5003DBD74 /* PSCBOnlineSampleUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 0ECA01D7254C88BF003DBD74 /* PSCBOnlineSample */ = { + isa = PBXGroup; + children = ( + 0ECA020B254C8902003DBD74 /* sdk.sample.entitlements */, + 0ECA01D8254C88BF003DBD74 /* AppDelegate.swift */, + 0ECA01DA254C88BF003DBD74 /* SceneDelegate.swift */, + 0ECA01DC254C88BF003DBD74 /* ContentView.swift */, + 0ECA01DE254C88C4003DBD74 /* Assets.xcassets */, + 0ECA01E3254C88C4003DBD74 /* LaunchScreen.storyboard */, + 0ECA01E6254C88C4003DBD74 /* Info.plist */, + 0ECA01E0254C88C4003DBD74 /* Preview Content */, + 0ECA0236254C8DA0003DBD74 /* PaymentHandler.swift */, + ); + path = PSCBOnlineSample; + sourceTree = ""; + }; + 0ECA01E0254C88C4003DBD74 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 0ECA01E1254C88C4003DBD74 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 0ECA01EE254C88C5003DBD74 /* PSCBOnlineSampleTests */ = { + isa = PBXGroup; + children = ( + 0ECA01EF254C88C5003DBD74 /* sdk_sampleTests.swift */, + 0ECA01F1254C88C5003DBD74 /* Info.plist */, + ); + path = PSCBOnlineSampleTests; + sourceTree = ""; + }; + 0ECA01F9254C88C5003DBD74 /* PSCBOnlineSampleUITests */ = { + isa = PBXGroup; + children = ( + 0ECA01FA254C88C5003DBD74 /* sdk_sampleUITests.swift */, + 0ECA01FC254C88C5003DBD74 /* Info.plist */, + ); + path = PSCBOnlineSampleUITests; + sourceTree = ""; + }; + A6BE523F775C844325774B50 /* Pods */ = { + isa = PBXGroup; + children = ( + 5D25A9250438F9C8AA6153F8 /* Pods-PSCBOnlineSample.debug.xcconfig */, + 2EAAA258959A1F5D92CFD851 /* Pods-PSCBOnlineSample.release.xcconfig */, + 597562929002737E312BE5A9 /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.debug.xcconfig */, + 3E1FFD5E11C6E53BAB8BDD2B /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.release.xcconfig */, + EE4EA7D50047330E553B55B6 /* Pods-PSCBOnlineSampleTests.debug.xcconfig */, + AE94091A3CE7B540776F6146 /* Pods-PSCBOnlineSampleTests.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + CC92252B44ABB68C5546B8DC /* Frameworks */ = { + isa = PBXGroup; + children = ( + ADAE0E73DFA819AE992A9C59 /* Pods_PSCBOnlineSample.framework */, + 215AD129A382ED26BE4A9ADC /* Pods_PSCBOnlineSample_PSCBOnlineSampleUITests.framework */, + 75E713AE69B50BE64C2D03E8 /* Pods_PSCBOnlineSampleTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 0ECA01D4254C88BF003DBD74 /* PSCBOnlineSample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0ECA01FF254C88C5003DBD74 /* Build configuration list for PBXNativeTarget "PSCBOnlineSample" */; + buildPhases = ( + CD6C55176C28DD5EC847748D /* [CP] Check Pods Manifest.lock */, + 0ECA01D1254C88BF003DBD74 /* Sources */, + 0ECA01D2254C88BF003DBD74 /* Frameworks */, + 0ECA01D3254C88BF003DBD74 /* Resources */, + 1CCC3026FF4B068D24933916 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = PSCBOnlineSample; + productName = sdk.sample; + productReference = 0ECA01D5254C88BF003DBD74 /* PSCBOnlineSample.app */; + productType = "com.apple.product-type.application"; + }; + 0ECA01EA254C88C5003DBD74 /* PSCBOnlineSampleTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0ECA0202254C88C5003DBD74 /* Build configuration list for PBXNativeTarget "PSCBOnlineSampleTests" */; + buildPhases = ( + 143B1BD2DA6C604E39332B78 /* [CP] Check Pods Manifest.lock */, + 0ECA01E7254C88C5003DBD74 /* Sources */, + 0ECA01E8254C88C5003DBD74 /* Frameworks */, + 0ECA01E9254C88C5003DBD74 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 0ECA01ED254C88C5003DBD74 /* PBXTargetDependency */, + ); + name = PSCBOnlineSampleTests; + productName = sdk.sampleTests; + productReference = 0ECA01EB254C88C5003DBD74 /* PSCBOnlineSampleTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 0ECA01F5254C88C5003DBD74 /* PSCBOnlineSampleUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0ECA0205254C88C5003DBD74 /* Build configuration list for PBXNativeTarget "PSCBOnlineSampleUITests" */; + buildPhases = ( + A1AABD89A7AA5EFA1FDE3606 /* [CP] Check Pods Manifest.lock */, + 0ECA01F2254C88C5003DBD74 /* Sources */, + 0ECA01F3254C88C5003DBD74 /* Frameworks */, + 0ECA01F4254C88C5003DBD74 /* Resources */, + 6BAD522C988953C7BFCC5A17 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 0ECA01F8254C88C5003DBD74 /* PBXTargetDependency */, + ); + name = PSCBOnlineSampleUITests; + productName = sdk.sampleUITests; + productReference = 0ECA01F6254C88C5003DBD74 /* PSCBOnlineSampleUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 0ECA01CD254C88BF003DBD74 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1210; + LastUpgradeCheck = 1210; + TargetAttributes = { + 0ECA01D4254C88BF003DBD74 = { + CreatedOnToolsVersion = 12.1; + }; + 0ECA01EA254C88C5003DBD74 = { + CreatedOnToolsVersion = 12.1; + TestTargetID = 0ECA01D4254C88BF003DBD74; + }; + 0ECA01F5254C88C5003DBD74 = { + CreatedOnToolsVersion = 12.1; + TestTargetID = 0ECA01D4254C88BF003DBD74; + }; + }; + }; + buildConfigurationList = 0ECA01D0254C88BF003DBD74 /* Build configuration list for PBXProject "PSCBOnlineSample" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 0ECA01CC254C88BF003DBD74; + productRefGroup = 0ECA01D6254C88BF003DBD74 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 0ECA01D4254C88BF003DBD74 /* PSCBOnlineSample */, + 0ECA01EA254C88C5003DBD74 /* PSCBOnlineSampleTests */, + 0ECA01F5254C88C5003DBD74 /* PSCBOnlineSampleUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 0ECA01D3254C88BF003DBD74 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0ECA01E5254C88C4003DBD74 /* LaunchScreen.storyboard in Resources */, + 0ECA01E2254C88C4003DBD74 /* Preview Assets.xcassets in Resources */, + 0ECA0218254C8C12003DBD74 /* Podfile in Resources */, + 0ECA01DF254C88C4003DBD74 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0ECA01E9254C88C5003DBD74 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0ECA01F4254C88C5003DBD74 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 143B1BD2DA6C604E39332B78 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-PSCBOnlineSampleTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 1CCC3026FF4B068D24933916 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 6BAD522C988953C7BFCC5A17 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + A1AABD89A7AA5EFA1FDE3606 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + CD6C55176C28DD5EC847748D /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-PSCBOnlineSample-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 0ECA01D1254C88BF003DBD74 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0ECA01D9254C88BF003DBD74 /* AppDelegate.swift in Sources */, + 0ECA01DB254C88BF003DBD74 /* SceneDelegate.swift in Sources */, + 0ECA01DD254C88BF003DBD74 /* ContentView.swift in Sources */, + 0ECA0237254C8DA0003DBD74 /* PaymentHandler.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0ECA01E7254C88C5003DBD74 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0ECA01F0254C88C5003DBD74 /* sdk_sampleTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0ECA01F2254C88C5003DBD74 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0ECA01FB254C88C5003DBD74 /* sdk_sampleUITests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 0ECA01ED254C88C5003DBD74 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 0ECA01D4254C88BF003DBD74 /* PSCBOnlineSample */; + targetProxy = 0ECA01EC254C88C5003DBD74 /* PBXContainerItemProxy */; + }; + 0ECA01F8254C88C5003DBD74 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 0ECA01D4254C88BF003DBD74 /* PSCBOnlineSample */; + targetProxy = 0ECA01F7254C88C5003DBD74 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 0ECA01E3254C88C4003DBD74 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 0ECA01E4254C88C4003DBD74 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 0ECA01FD254C88C5003DBD74 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.1; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 0ECA01FE254C88C5003DBD74 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.1; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 0ECA0200254C88C5003DBD74 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5D25A9250438F9C8AA6153F8 /* Pods-PSCBOnlineSample.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"PSCBOnlineSample/Preview Content\""; + DEVELOPMENT_TEAM = 46N426YCGP; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = PSCBOnlineSample/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "ru.pscb.ios-lib-sample"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 0ECA0201254C88C5003DBD74 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 2EAAA258959A1F5D92CFD851 /* Pods-PSCBOnlineSample.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"PSCBOnlineSample/Preview Content\""; + DEVELOPMENT_TEAM = 46N426YCGP; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = PSCBOnlineSample/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "ru.pscb.ios-lib-sample"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 0ECA0203254C88C5003DBD74 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = EE4EA7D50047330E553B55B6 /* Pods-PSCBOnlineSampleTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 46N426YCGP; + INFOPLIST_FILE = PSCBOnlineSampleTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "ru.pscb.sdk-sampleTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PSCBOnlineSample.app/PSCBOnlineSample"; + }; + name = Debug; + }; + 0ECA0204254C88C5003DBD74 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AE94091A3CE7B540776F6146 /* Pods-PSCBOnlineSampleTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 46N426YCGP; + INFOPLIST_FILE = PSCBOnlineSampleTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "ru.pscb.sdk-sampleTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PSCBOnlineSample.app/PSCBOnlineSample"; + }; + name = Release; + }; + 0ECA0206254C88C5003DBD74 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 597562929002737E312BE5A9 /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.debug.xcconfig */; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 46N426YCGP; + INFOPLIST_FILE = PSCBOnlineSampleUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "ru.pscb.sdk-sampleUITests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = sdk.sample; + }; + name = Debug; + }; + 0ECA0207254C88C5003DBD74 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3E1FFD5E11C6E53BAB8BDD2B /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.release.xcconfig */; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 46N426YCGP; + INFOPLIST_FILE = PSCBOnlineSampleUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "ru.pscb.sdk-sampleUITests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = sdk.sample; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 0ECA01D0254C88BF003DBD74 /* Build configuration list for PBXProject "PSCBOnlineSample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0ECA01FD254C88C5003DBD74 /* Debug */, + 0ECA01FE254C88C5003DBD74 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 0ECA01FF254C88C5003DBD74 /* Build configuration list for PBXNativeTarget "PSCBOnlineSample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0ECA0200254C88C5003DBD74 /* Debug */, + 0ECA0201254C88C5003DBD74 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 0ECA0202254C88C5003DBD74 /* Build configuration list for PBXNativeTarget "PSCBOnlineSampleTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0ECA0203254C88C5003DBD74 /* Debug */, + 0ECA0204254C88C5003DBD74 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 0ECA0205254C88C5003DBD74 /* Build configuration list for PBXNativeTarget "PSCBOnlineSampleUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0ECA0206254C88C5003DBD74 /* Debug */, + 0ECA0207254C88C5003DBD74 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 0ECA01CD254C88BF003DBD74 /* Project object */; +} diff --git a/PSCBOnlineSample/PSCBOnlineSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/PSCBOnlineSample/PSCBOnlineSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/PSCBOnlineSample/PSCBOnlineSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/PSCBOnlineSample/PSCBOnlineSample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/PSCBOnlineSample/PSCBOnlineSample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/PSCBOnlineSample/PSCBOnlineSample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/PSCBOnlineSample/PSCBOnlineSample.xcodeproj/project.xcworkspace/xcuserdata/oa.xcuserdatad/UserInterfaceState.xcuserstate b/PSCBOnlineSample/PSCBOnlineSample.xcodeproj/project.xcworkspace/xcuserdata/oa.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..05f84ea29f33c16951758afe059591b8dd1e1ccc GIT binary patch literal 23128 zcmeHvd3+Pq*Y~}%bfayOrfX=^w4roO+O%oXv@G2y6xvd{u~R~umO$H-BxM!6BM7pH zh=6ROEt`M>q9P))BQA*SpvWd7BB-d~f(Y-KOw&cG56|!Oyzd|H=Ydb#J2Q8iGv}Op z&i9;qN7dJuZT5tOQwSpp(TG7TazU=hV^EjzCacYCsTS-Xg(op-#vX1)x9_gkn(~QXw^p zM+r!S5>XQBg>q1D)Cc9FzNjC{L-}YR8iWR;A*c#fqtVEW#-KW6M-6BSdIAx|p_ynl znu8Xg=g~s66fHw5(JQDC?L+&~0dx=@LLZ>R=tJ}o`WStJj-XG`QS=!)hK{4N=p4F$ zuA^_!4fGwliEg1^(QoK?^ar|!9%33Z*bVz&DGtJ+Sb@9XDD)SO#wx7F30Q~qI1Ts2 zS-3wg!~<{lxIv#8Y9=*{noB)LEua=rOQ~hlOH?DZk=jaar*=}isr}Re>M(VIx=3B3E>l;i zuc)ik*VHxY8|pjiC+cVF7wQgmkNT6QX@+KL7uuT^(Nfx%_M^k-a9T-sp(AJwok%Cq zS~{6dqr1@=bU!+e&Zh^`gXqC@IX#lDq({@!=qG7HbM#a6bb1E;G(D4^ML$n3q+g(y z(J#@f=#BIy`ZfA>dNaL+-b%kk@1)6xypRU+-81a{$TF06ic)2tOwhPRkB^!2(~L5 z$wsl!Yz!OA#<40^&Bn6{tcFcwli2QT54II_Md&8vi~I<~VVWq)#a$9I^jQhL^eL*8 zENzBLt4&H)8Bz>7RhBL%$&jN<&}*_Z|O&c*0HvFV}&W(QqfRrsJO;(RREN|>mYPvUYp!$Da&chmGsleDJo2A1 zB_U3s(@c$1OwlB!Kv}QRO`YnbuNHOV>6?snNRLvG0i~ie;!Q+EOeDmINQv)yplT1) z6Q!dJC}jar{Yfl|BMDH_kjQee7zQsMZJAJD9B-~R!mA0lS}rrJCU`*!)GD=`Ys_}D z$u`hjX&)_A%dNB3!vbKjwpMFt*`^vlKuh!C&{wZYb8{ni=erc3A{4V4^+$zh00|(0 zBxp4%MkT0}$Ve24=I5#yRAH$!siA#8vt8XXkh+EFFwtUD4NS;X+42;YrQTLcL%r|- z7BQQ1#Gz;;idln(q2Xu*Dkq&uFbN^@HOPoYp$cRqp(Koi!*3YbH^*FKH(4E@JabY$ z%mNlV7%k6ev$u@I-!2#Xm)2EvSUFJW!&F(C+rA&?e8!?0~!qftx~ z>y351<>Ht}QpWy`;lFRnP8MV6)u&UV#|d9Q|4xtBP!?F9DTc z6H_OGL*$P+3FVcqKK~5*QiL~ogbgaRS}GgBiV`y?xKp- zE|lrhM>|T5-C?vIomP8z^eh+qWz@q5D>P2(W2~zLovpS_Fjv{ZOq9SPp+?=o@EZi; zI0_`ef+mBR$Du`NHF^zgMsK3G(M~YpzC_o+X!{-vHH=-rM2o^2Fv<)#6KCUGFvP}U zJD6clU=A6?cuFDouj(zModaCVRcb7Uo_O%%^*p|pmgnpR&y<=_&nnzOM@L2rEJrvhU#iq zt@wvLdk$&WgL8#!U4tDJb9v=j2x^vJpZNt_cn->uzi{>`tn;Gg^}?Z8C)JonIA5Z& zS>G$NO;tviVqrADfJhSvvZM)>4>c^B9h) z3G<5jSCX1-Ez=aEa9ZPL1s1tRa)BB862)-nu2nRL z@mH+|p%^at@0M?@t~1*kDoumoZ3RLo)6F9tddWG`_voibM*108@eauYHY?y4c#(rv z0#ERCa#N-c+w15=j839I(S2Cw{{q+e0lZ~FaSI>hnd+(?c3zu%WyYEYlNgf$4tc`} zsoJ@gLTa_Z~rQ1+tz1fBq<{mq?DwWi{o2#lfb^9{rC@= zZQ;9XH6mNIl;Z25iDkGm%sd!v!}1}mW0g8)K?Z@K5B{g6 zPM}*D4lfr6!)Iw-IABy>TMI&Hwl-lUuZsV*j&mLnxGR>Ep=1~t+{P+$K8@jLNk;IK zYV%akmoMY^CSE?M4W`RIC*GgvlmxdlsDLJt_AA`%q(IgqXqoZ!|xE9x83zWuT@B@RKjL?o|1r9enjAsMh`Q_~pwavl2?P<=N z$UO4bYG9O$JJ&h9%x1y+dwof}r~Ei=SrJ-0=hJEgv=}$UN+y#j#6ng=X(CzCR=e17 zgP>i=cv3GY1A!`&d8$kSTX8BGN9?4bRkX+A!5}nNAT&s_gvS_Q3FB3=V6ciQr*uU) zq-!WIT^-&MRL*UV`WI$j&IE%>;^9Z8g<^(j0Fh zh)g0A1W{RrmzNe4gD^MYWe%aLZx~f$t`OctWwSI`D@;yydkMdaVm9Jc_+|VGZp2Ms zL9fAU@jASoJVBzN;(?fYHWk@Vi4h ztdmXi&lP6*}Y{h<|$fj0XE?ixuYdC~795is%r z^1}yk;2L}oAL4(=Gi0{or(EnN_I7^hBlsu)0r*ogw-J9v=J9rdEZbCLHwv>CY@-&+ zI9r{>XOMprK7~({XUY5~d={T0&yfX0F_eE3hr%)0MmXhEupopPUBH2_ka7tgq1DfKrR~Gp%jvAr?z5kfr@i z-b&>qG}F>hR|!<>-2QQ)xvtJs+1!P)XY(8ios;+p-{D{EXZ#CUMwYjEvC=A_^WR^o zT&!~R?u4QQnmHFSb)l)+Xlt;wvXUT(_wio>UMf^?qqv*!170$DEcKCRk0@#iQJA7A z8e5>`Lb;MgvYxz3R)OkkB?fm~uGd-%~{x7H-syC<`stCWCwY+i5h{=l6OJfDE_8kB-Bag zK$TRrLyV~^5aahijHwaO;N3@>{C5TTQ6a1YYblu;2SRA0>>z~u!2UpFHwfWj@rp)V>=%KmJ$l^l}2g>`3Nu;Y870+OuYh_aYX~5GIjPxv=#Z7+<1go zI2hMZYpAtgS_V6aH3*1xf_wtR8uWK!ZK7UlCf0Fsq%E=D;EA=3dXsvKd`gaz&svBj z_+f(E^)E!(;~>hh#~_NZE>Ry+CwNWs5%n?k33Y_}lsZa%MjfM$lau5WIZe)xv*aB4 zoP0sfZ=g;#YnrpvIfte>Prhu{G*`$s4o!3Ye>Dv_J!%`Q(*F&^hPuvcns3Pkr>40H znufYXeGflBl8fXL{9GpGVt28tSmaa!cd6faZSyPns*(DgTzyP!bDw(1tBzCD12ApA z2Gv1hP#xDEQ5_QZWi5br^N6%7?cq>6v^%JsZ~wWi!w#Uuv;>6u?`u66gcW0(Xdm7X z==dRzCWJp(gAcJ9Yo8>_v8oiBe}hX4yHqJS2~pZM1BQ);30_| z#KT>@og|FX0%^6NXhQ_kkGE(CJ3ukMvU8ZObSytiBppRZ(=p^{@(Z~`?ydp6X&XL^ zqF_Z$2cr4$yxzGC&`cg-23*TDfw#_VMaBsZ014hu*8XX;(^_0%sRw&l(e9z6xy5SF zHo;P6t_LoH_WtdvIC|$jQRh5$bP9@Tr1j*tM%qAr=jZbe03O{P06e+}-IM%5?zedQ zbS9nicUX;k&zk*3Aaw3Pe>O&<_ND_NoKf_80( z(8Kt759Kf{C7D)84Y#chJ|FnWpEUIQOY0)cVife;=ySs~4U zhdy`+y9}Jq+}v!yCJKsk%vL^rXsfpX&}9XWHH*(c5FiC{OD6&c@~wXN-&{`iyP!OAi@w=2?WPkA`v5r zJH18hqlAE4WK?uak|8xMqj#U&y#7M!1h8)cgxwW<%$*OpD-z(paG}>L%3!9cNlE{- zG6OUN!rd}WVK#+=yg7Y@*0hC_v2iMOLL&buJI>E&j3;i58pw6+>h}$h3fr4sAwKq9G+EL8VPdN>u4J2??s~UU~zRG7QOC z8M;KRE@NnOoiY*LC4NG z1j`{eEF7}KxQ4o#7iL$*EyAxnYMv(Lyw`y(lvA~T}NB!dU_mf zm1(9;jJ8ggI8R>nGkFkTW^7Yz0Hvw1888S17>iUMSDwc9}s zxd2I8t03npv%y>gy7ZC8xtY$XEuxq3i^^gSOB(4HIV>fSZ47^UC2#n1*r!~a`go3e zE6lH+$Rknp@D|RoUZ&UZ2Er?JBi%%==CCh^fk*r~9I%F7ORuBX)30(kki$V7mT@?o zr-%ThIB6l!z=6|P8f?u35^g%yigIyWJG!*5Ut+XYo0`3nf2r>LyiQU(!w$d~ksb72 zF5D|NLpZLkS{Sih9MG;N&p6H}(hll7P%s*tm_~XVhdYzx6|*?E9rL! zx8R{14&iVxd=d_aDfwKBz_l4yoD|+g?}3N_{XV^$!*ULXHqm==1cwz60E#RVW>6-O z0oKe8C&H_V!}N#c;*fT4@ONUYraz)T=B?3xuhwyt#yQ&~^ru3&&w1k*4PI3veVoHx z8tIc9?%JLi&k4*JQ7+az9y2zhNa~5^QO+UG)0cT3{F1&vU!*T_IFiFr9FFENgl^8# zU(uK7uL%fcEZpF50?&d$PKL-g@is$iNDtmVG&i%)q)}EggtnWr7kGoTb^QkGM`#T| zHn8h>GYBKBT)CN9#=7x*ZeiQX&MDrcf8amjE&6*7$8lKIME^+N=CGQ>@%(%Oax>ec zdbAzOH#ZZ$)7XxCy-WYvp4flT_uAk1i(ydAI{E?qkU+hQo;*PU5hZ!^s@h zt;5$D7krj+W85K$VF`!zJhf6dYyk5^yhL89H$PSv$ z=*);1I>n$ttu zXP*BJvVdZC=@HxOf7j%GqItMZ_W}e5Zm}+J;hx>E5ki!H1ug6iGqg$B< z2mZm>$ubTXw+Wy84XSlEn8G{_<|p$6GnJXfJjp=PIr9`VoteSmQVs*P267mvHJHOg zI1JA4Fb)sjz|3rxgE<16&&=oW2&WW`6aak=oBn?jf&V9nKC_(X>lGX>ck=bB4k3N# z_gl-X=lOaahmDO4SahQvldoT6w(yK{irGw_<#0t4vy~db;mQsWdWXHlyzPh@I!E2X zyeF{PPM*!G{-w3E#XjZ}p3n9(2bhD*A?5?-F!Lev5%V#JK}yUV9>d|W9IoMTEr;tk zY~gVI2IfdJpB)pxTn1!toRiON0-Vd?3IEGy{{f!MT;}=g3Wu#uKKr^uK-c;GZZhBV ze0Gb&_D1Fh4mUg|pZ(0-<@M(;y#5>y`V;naZ6()uPWyZC$T`xV%mab9{^EISBJdUs zDa4f>0Km;HScY|hxv}6ZPi|zvS)Kw}eSrJ$V3q*Jv;6;%isB9M1lJH%}3Rb#H^H@<+m z+O|e6?C^j@63EpD)2pQ$2jI#ZdhL31dQJd52{j!lSaxt~A~f*`TjV(WPj#iCRrXRTXRj(i%&R)>@sm#9U~s9LWDlj!SfYAT0~P z@oF}i)vh>r@Ga|v~#5UCO3Aq9n6q~Af zbOC6|Z?tINq<=lX`2imJKk~?dtU8n+n}JrWWi#0)F-D7B+m*w_gj^n$Omli10; zXVk>u)g-d{EgYykJB_aBFOe+$I~xC=@1b>bFR@xIcGy5G4)5eC^#vS0CN@3|pxKC? zS^e_z3;LH19x`-9MOFRy2~(yu@B9muVF=sQIT*I&1rtBO5py6N4r*puU?arouyAD;ZwH7DPk6K$ z2Cd4eF;+V^Dz$`c;32OVmFZmtdP|-W!xlhS(s_uwIH#akl?hokC%gd(2mhrsmmi@u z+|y>xGUCopH6b~5jRrQY@H+?r824HOsJOwg@y)Su%n>Z$`{3u((i-AQFzFKhevZk= z>!)Iq9Z&h2rEES=ivNCHA@??0bJmgr+hH06^0NavCE0V0Y$|ff${B;?V^$v zC>~mr4gC9lh_XQ?@a-I%ApqwTUW6yU<847V3wL_e){i#YfO^7x=xNw+2UVTGa?1|1 z)x(NP;Bom#SS$Yi;Z_?*m2_|$ZV61(`iY^3N>ejyHC2|Jt{!b3<2|;fw$1`?$17G? z06>RA9%E7OOvf$Yy<&dLsUzZ%)c_f_wGM`K-0ZCp0yh1{Aqi?jO0q$ll9Z@TQAw+Rg>&5zeYJLfJP1y9rpQ0EO+g<~JLLiKkh{*N2B znO2*5#1--Tx=eKyCIt^=f)a+$T?YUnm_rJ4-9LTd|FBt`Iq*e+uc07a^8opltC9FK zUqpX@1ToT1h#s~Ip5T`6mgipPh_($3y)N)Q--95{j#A4n>NzMtY0KJRXf|A@(@IvH5W( znhWP!EJn*=m&O{{na1ZazJqq5eQ>tLCvcX=a87+r%iEN;UY7Jp)lU12kgaB@Wi zL?sh(3hse>!RCY_$Y>vlt0BL?0TSM)vD{z9s18|dq;hcn2uxtmh)$9azCcA`P$G*)TVo$Tz*k4^-T>M=k zUGy$}Tn4+0ahc*W&*deTEiQXqj=NlSx$Elc8sr-5+TFFktI^f&I@5KT>uauiTu-=O zbN$_|lUt};qFavJ5VtzFr`(pfZFJl1cEatt+n?@Y_Xzh?_x|pc?vvb~b6?}W)BQ8| zYwmw~NIW7vx_gv(jPc++mU?XVIOOrA$1j~abqeof=ro{{xfAKMwA0p3A9lLZ=?_mI z<}v&taYop7TA|d+zo8-18SNFR!j%>0U#;>|XP|Hh3NIy6E-0x71tho$Foc{iOE_ z@9o|vynhh6iy}lBq7kCWq9vj?MW2aoL1t4|ahBL9o+e%)eph@}d{-isXe9k5wUT*~ zjgpTf-}<=vboJ@wQ|U9qXSL4(pQ}<@s+49)E2Pt3`se!B_&?{r&Hs%5y?~H_i~v)>?10SyCjx#8>>QXL zXbPMYxFzs(;JqM4P_Lk|LC*)h6Ldkw$YNvzWD{hoWQSz8I{S7`>s--!PUmf%zX+y+ zV}gr|&Rq&{R>$bpbsa({Wce2jdNe7F2Ys5G=ks5x{|=$_E;6n=^f zMU7&q;-KPoSa4XMu!gY4u%lsrhDU~%hCda)CH#WYLz$wiQZ7>NSKjUt+9kis6J0iS z`69wSA|+yU#ETJ!BYx`|*>zyoSzX`h`fX%jWNzf-$W4)7MtMc`j2ai!6m=@vB|0T~ zZ1hXf$6~0M~)UVW^P4P`BOqrMRp@B7I8lE)lGW?OM zOSPwNOZ_oTl{PkQW7@afBD0U zUYWiw{dz`p#+Z!PGJeQR$h2m@o%vf->H2M_Vesl+;4fmukvE@ta&^0sr-`&(TXp-!f;Jmz#eY(`U@fV}2Xkf9(3P z4{HY2Y^imt9a;Nsolo7^x`P(EWuoPHeQf>A`pe@|$1NUr+uF~%&W3HnZ9D9~U`!op zh-!Gc;mY_P<5!ITeL~rUHz!Ia)=fMzDQ42_N!KUmOkO*MnNl%j{}W+Pa8F#BnlZI; z8lE<4+Wse%PtJJq8f@}>mGj`na-Tkx@YKSm?o1yzedmmj8Dz%Qr+YuWX{Pr~>&&yW z(q_H<4D*cnnWM9lW-pulU{2+nkLM=LT{8Fnyoz}rKdX6m>9Y^#SIs~AobI_*3tSe| zEjaUh&*xuVC|Njp;nf%NU)a7#v1rbsJB!N~f3!rqr^3uGeJC=1>wqV)) z<)fFMUXi(C%gW%DvseE1Qsql0R;91n{Bp?4b6>vqiusjujlCPUH+5}Vvf5?!_|@O6 zDO+=RZOYmW>tyTZuKR0!-TKR~7QK3KgMPz?jlmnA+eB?@*mUEyVXqx~J^S??n^l_| zxA3o&Z>GGt`K^ezR=zELd(L*eebV;Z?~H!u@{Ykfj=$UY z-Tgb$cE0_d`n~nL!gj5A-|zkBc6;oevHRhk$$ReZt>1fVU-iDL`^)#AKQQRPse=O! z9zE3W(1#!7d~o1!#^F65cK>kKN2wpZ`*F(0?|h>BWc!iiBX572{OQ|Abw{^Lx z+g&$uZXEk==y%s{*5CZ|*39oE->>{3>W8;}O#ku7?LoJ%{bc>=;m`Aa3HoLI9qpa{ zcMI=c{I&MiKYyF^d(iK%{*m&>;d=w`UH@~!eb@U-|BC)==YxI^zI<5w@F6%j6DPHE zT$(1Aw7Z5HPfT^X)lEb<7e6S*#z=l`{JKVRxcJA;(F9l{f-N zLT-~54p%6HjfX?95%vp?hMj^nU`5!#j+g)kElj~v@e6Ra)#q@q!XI!>)n9N@l?&`U z=|p+K{t_RsBK#>WRX`QOiBtpOG^(-GM96lULCppmVkIPnZ-;!Rlhh5!==~W^lyZlJ z)p$5NDh)C+bLldA7;Od%VkRVxyb5PRy+*%D@1S?nd+Gi3L9tluCk_`!h!e$naZhok zxVLzixSTh%zzlPETTpn)aoO@qY==B4Cq%@t&p<33@UrRb3^osp3qVr=HCxMJ2x~yH z%z6&Lx{fWtUD>(pJjn9u3LqMRNYn-nZ-QLfVLS>L{_mI<@5TR@l^`t(Dn@s#aINf~TO)TsU98m(N zyi8IU?Fx-PRhuOKPQV{m!43c<0ldh$y`61hA3P*9^m8Zg? zM*~n9obEG%{+K=nxggi*+w^brefj~UKCp}{;{j)dB{6-Ofe&_s z5cxDSi3lc`Y$2Qhwwn1GtkD#92s<9m-P*{$!+yYi$bQToVUM!M*c0q2_9|GU z*V!BFP4;{CNA@T77qCu)TvRUUE+sD2E)6c@U8cLt0Sk4$%L11dU6#4509*BCm;Ekh zT)uU=>+--Axl*o-tBb41RpKgj^>YmX>ovhO+jY3>MAxUmdY$Du+jXJqORno&-*nyK zy3h4X*Bh?4Tz_!=$@Lf4yKZ8)AUB;`id$-@Ah1aLbt>vKrPJk3H#$A^bn%S#)Oi{_ z(>!~4rh8_3j`6g463^+LPkTP&ImdIJ=T^^mJ$HKU@;u~u%JaPERnJ?Ve|Y(O1$xQ6 zg1zKk3a@alE?~JvdBu3ed8xe;yb`_odkyd^_A2!n=r!1DsMm0>axbG-g_p^z+RN-U z)~nX5$?K5U=U!iXyL$V1M|r1$gP<)v}lZ|2JGNjqUEABqSr+`Mf*etM2AF&MMp))L?=Y2 zL}x_bif)L07TpnJ&;(K97;&69S)3ux5f_Pvi^qt^ifhDm;_2cg;Ni9!-C=_2VWiIOBs(j_^PK9atYJV~X)u6|y!Rr0=Mk7S?ZfaIv;yySx9 zlH`iymgKJF9$4E_pAer=pD-V#PmNE5&s?7cKI?q8_+KukTi`p=cbe})-&MZ5e0Tfq_1*7#(Dwu14}CxOJ>q-C_p0wT-|N0Nd~f>R z@%_{HFW-lL*pK!TgLMxn+kP>AaefBBG{5eCJ^k|h3j7NFiu}s`jD8h(d`X#ZG$m4Cdy#y`iukAGkPJpTg! zLjNNF68|#)LHKi~oQ6j6by!1zE-pe`^aFg37SV4uK&fg=Nrffa#O zftJ800;dN)9XKm+cHn}*g@KC#mjo^id_C|;;CF$)1l|e4K`udVK^{S(AW4uk$Su^{vRSg(vX^8VW$(-O$o9z& zfR}Sv_L1xp*{8D4WM9d?mVG1pR`#9jmh1=FZQ0MVJF;IpOFJiaF6%t8^UBWqJAV_* z26qWo1t$b225W=62j>L$3GN%57hDir2)+-# z$QOd&wnV;E-Y9=nzES>~e6xJ3{C)Wz`9App`7!wk`6>As`BnKf`E~gX`JGT4N{6zc zuA#o6k)hF{v7xHa_)tw~QfP9hKGYDJ7g`Wn7+Mrs5?U5ID0E2Zu+R~qBSWW#t_{RSh>{jeme5yF6IH5SLIIH+v z@oN|z<`(7=<{2gllZN?)1%!1Ciw;W+>lv0EmIMA|epvso0b#{qqr%39O%Hn}?3J)> zVQ+=)3;Q_i2>6-D!cK&p54#X{DeOwv)v#+}KZLu4OTs&cM}=#`lfsk3_2Gu_%<$~+ zobW#3eZ%v@$Ar%c-xz)%{8;$O@YCVv!oLXrGW>SI1lqrLia%F@v zQW>p`RjQOaWlv?EvRGNF9H<HZInJLHL815dQ?_aPE>AG zUQ|JpDe9@H=c3j{eI9i)+AUfZogAGRog1AO-9LIj^q}aW(IcXb(G|QG9DO1Ba`e^c zZ=!ER-;Dk}`d;+?=!Y@gVsc}OVoGBM#SD!Z5mOyACZ;x~KE@W)5VJPsR4g5I8K+b(wmq`bG6d^>+0E^+EL^ z^g(zo>YM7H)pymutN&F074H%68Sfo0i7$(C-E-%8w`xFd0A;`@nv689w@NcH>54bg^b!?a3mf;LI3(;Bqhv^}&#wWGDQT8q}IouHkp zeL_1;`>b|}_C@V7?KV$O(Eg;oqy0^LPy1IgN~V)t zl7o_iljX^Z;G_ zu6tTHOE+6LSGQERLbpoSs9U33r#qlKraP@Wr~5*8Nq0r}weB0;T|K3D)w}CG^%A|W z-d`W6kJM}RdVQ+CyFOi?sV~$I)sNOs)f4@6{Y?FA{XG41`i1(%`lb36`mOpm_1pD3 z^gH$M>-Xvp=s(bZr2j8%IhgNQhrRilXBnS zZV(%!27g14L19oDx*DPlv4#hq~fQ&*&}N^MMiHFZhaWb zspnHKrd~^x_O_!FE)-$b7-q(58 z^X}y1d?w#D-y`2EUz{(^SL7@6Bl4s2WAjz{@%ftkvivFeYx6(LKbL + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PSCBOnlineSample/PSCBOnlineSample.xcodeproj/xcuserdata/oa.xcuserdatad/xcschemes/xcschememanagement.plist b/PSCBOnlineSample/PSCBOnlineSample.xcodeproj/xcuserdata/oa.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..fb70a58 --- /dev/null +++ b/PSCBOnlineSample/PSCBOnlineSample.xcodeproj/xcuserdata/oa.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,37 @@ + + + + + SchemeUserState + + PSCBOnlineSample.xcscheme_^#shared#^_ + + orderHint + 0 + + sdk.sample.xcscheme_^#shared#^_ + + orderHint + 4 + + + SuppressBuildableAutocreation + + 0ECA01D4254C88BF003DBD74 + + primary + + + 0ECA01EA254C88C5003DBD74 + + primary + + + 0ECA01F5254C88C5003DBD74 + + primary + + + + + diff --git a/PSCBOnlineSample/PSCBOnlineSample.xcworkspace/contents.xcworkspacedata b/PSCBOnlineSample/PSCBOnlineSample.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..d258a81 --- /dev/null +++ b/PSCBOnlineSample/PSCBOnlineSample.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/PSCBOnlineSample/PSCBOnlineSample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/PSCBOnlineSample/PSCBOnlineSample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/PSCBOnlineSample/PSCBOnlineSample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/PSCBOnlineSample/PSCBOnlineSample.xcworkspace/xcuserdata/oa.xcuserdatad/UserInterfaceState.xcuserstate b/PSCBOnlineSample/PSCBOnlineSample.xcworkspace/xcuserdata/oa.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..27c11fdf664bee0f53a33596be8500316bce14d4 GIT binary patch literal 25954 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 XT37a{v=cQviV}Y%9$hp(R4M&Gb?{XZ literal 0 HcmV?d00001 diff --git a/PSCBOnlineSample/PSCBOnlineSample/AppDelegate.swift b/PSCBOnlineSample/PSCBOnlineSample/AppDelegate.swift new file mode 100644 index 0000000..e0d05f7 --- /dev/null +++ b/PSCBOnlineSample/PSCBOnlineSample/AppDelegate.swift @@ -0,0 +1,36 @@ +// +// AppDelegate.swift +// sdk.sample +// +// Created by OA on 30.10.2020. +// + +import UIKit + +@main +class AppDelegate: UIResponder, UIApplicationDelegate { + + + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + // MARK: UISceneSession Lifecycle + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + } + + +} + diff --git a/PSCBOnlineSample/PSCBOnlineSample/Assets.xcassets/AccentColor.colorset/Contents.json b/PSCBOnlineSample/PSCBOnlineSample/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/PSCBOnlineSample/PSCBOnlineSample/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/PSCBOnlineSample/PSCBOnlineSample/Assets.xcassets/AppIcon.appiconset/Contents.json b/PSCBOnlineSample/PSCBOnlineSample/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..9221b9b --- /dev/null +++ b/PSCBOnlineSample/PSCBOnlineSample/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/PSCBOnlineSample/PSCBOnlineSample/Assets.xcassets/Contents.json b/PSCBOnlineSample/PSCBOnlineSample/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/PSCBOnlineSample/PSCBOnlineSample/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/PSCBOnlineSample/PSCBOnlineSample/Base.lproj/LaunchScreen.storyboard b/PSCBOnlineSample/PSCBOnlineSample/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..865e932 --- /dev/null +++ b/PSCBOnlineSample/PSCBOnlineSample/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PSCBOnlineSample/PSCBOnlineSample/ContentView.swift b/PSCBOnlineSample/PSCBOnlineSample/ContentView.swift new file mode 100644 index 0000000..3c1c9f7 --- /dev/null +++ b/PSCBOnlineSample/PSCBOnlineSample/ContentView.swift @@ -0,0 +1,124 @@ +// +// ContentView.swift +// sdk.sample +// +// Created by Antonov Ilia on 30.10.2020. +// + +import SwiftUI +import PassKit +import PSCBOnline + +struct ContentView: View { + + static let dateFormatter: DateFormatter = { + let df = DateFormatter() + df.dateFormat = "yyMMddhhmmss" + + return df + }() + + let paymentMethods = ["Apple pay", "Credit Card"] + let amount = Decimal(155.35) + let handler = PaymentHandler() + + @State private var paymentMethod = 0 + @State private var showAlert = false + @State private var paymentState: PaymentStatus = .unknown + @State private var lastOrderId: String? + + var body: some View { + VStack { + if (self.lastOrderId != nil) { + Text("Last order ID = \(self.lastOrderId!)") + } else { + Text("No payments has been made yet") + } + + Picker(selection: $paymentMethod, label: Text("Which way would you like to pay?")) { + ForEach(0 ..< paymentMethods.count) { + Text(self.paymentMethods[$0]) + } + } + + Button(action: { + self.handlePayment() + }) { + HStack { + Text("Pay!") + .fontWeight(.semibold) + .font(.title) + } + .frame(minWidth: 0, maxWidth: .infinity) + .padding() + .foregroundColor(.white) + .background(Color(.black)) + .cornerRadius(40) + .alert(isPresented: self.$showAlert, content: { + Alert( + title: Text("Payment status"), + message: Text("\(String(describing: self.paymentState))"), + dismissButton: .default(Text("OK")) + ) + }) + } + } + } + + func handlePayment() { + let now = Self.dateFormatter.string(from: Date()) + + // Payment info + let payment = Payment(amount: self.amount, orderId: "XC-\(now)") + + if (self.paymentMethod == 0) { + payByApplePay(payment) + } + + if (self.paymentMethod == 1) { + payByCreditCard(payment) + } + } + + private func payByApplePay(_ payment: Payment) { + let item = PKPaymentSummaryItem( + label: "Sample", + amount: NSDecimalNumber(decimal: self.amount) + ) + + self.handler.applePay(payment, items: [item]) { (status) in + if status == .success { + self.lastOrderId = payment.orderId + print("Meow") + } else { + print("ArghhH!") + } + } + } + + private func payByCreditCard(_ payment: Payment) { + // Card info + let card = CardData( + pan: "4761 1200 10000492", + expiryYear: 2022, + expiryMonth: 11, + cvCode: "533" + ) + + self.handler.creditCard( + payment, + card: card!, + completionHandler: { (status) in + self.showAlert = true + self.paymentState = status + print("Success?: \(status)") + }) + } + +} + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView() + } +} diff --git a/PSCBOnlineSample/PSCBOnlineSample/Info.plist b/PSCBOnlineSample/PSCBOnlineSample/Info.plist new file mode 100644 index 0000000..2688b32 --- /dev/null +++ b/PSCBOnlineSample/PSCBOnlineSample/Info.plist @@ -0,0 +1,62 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + + + + + UIApplicationSupportsIndirectInputEvents + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/PSCBOnlineSample/PSCBOnlineSample/PaymentHandler.swift b/PSCBOnlineSample/PSCBOnlineSample/PaymentHandler.swift new file mode 100644 index 0000000..9ef5846 --- /dev/null +++ b/PSCBOnlineSample/PSCBOnlineSample/PaymentHandler.swift @@ -0,0 +1,129 @@ +// +// PaymentHandler.swift +// sdk.sample +// +// Created by Antonov Ilia on 30.10.2020. +// + +import Foundation +import PassKit +import PSCBOnline + +// MARK: - Payment status +public enum PaymentStatus { + case success + case failure + case unknown +} + +public typealias PaymentCompletionHandler = (PaymentStatus) -> Void + +// MARK: - Payment hander +public class PaymentHandler: NSObject { + + // Initialize OOS API Client + static let client = PSCBOnlineClient(environment: .sandbox, marketPlaceId: "288747332", signingKey: "111111") + + // Current payment status + private var paymentStatus: PaymentStatus = .unknown + private var completionHandler: PaymentCompletionHandler? + + private var authorizationController: PKPaymentAuthorizationController? + private var payment: Payment? + + // MARK: - Payment handlers + func applePay(_ payment: Payment, items: [PKPaymentSummaryItem], + completionHandler: @escaping PaymentCompletionHandler) { + let status = PSCBAPI.canMakePayments() + print("status: \(String(describing: status))") + + // Create PKPayment instance + let pk = PSCBAPI.makePaymentRequest(items: items) + + self.completionHandler = completionHandler + self.payment = payment + + authorizationController = PKPaymentAuthorizationController(paymentRequest: pk) + authorizationController?.delegate = self + authorizationController?.present(completion: { presented in + if presented { + print("Controller presented") + } else { + print("Controller could not be presented") + completionHandler(.unknown) + } + }) + } + + func creditCard(_ payment: Payment, card: CardData, + completionHandler: @escaping PaymentCompletionHandler) { + let client = Self.client + + do { + // Create request token + let request = try client.makeRequestWithCardData(card: card, payment: payment) + + // Send request to backend + client.send(request, responseHandler: { (error, response) in + guard nil == error else { + print("Error sending request: \(String(describing: error))") + + completionHandler(.failure) + return + } + + print("Successful request:") + print(response!) + + completionHandler(.success) + }) + } catch { + print("Unable to create request: \(String(describing: error))") + completionHandler(.failure) + } + } + +} + +// MARK: - Conforming to PKPaymentAuthorizationControllerDelegate +extension PaymentHandler: PKPaymentAuthorizationControllerDelegate { + + public func paymentAuthorizationController(_ controller: PKPaymentAuthorizationController, + didAuthorizePayment payment: PKPayment, + handler completion: @escaping (PKPaymentAuthorizationResult) -> Void) { + // Payment authorized. Make request to backend + // Create request token + let request = try! Self.client.makeRequestWithPayment(pkPayment: payment, payment: self.payment!) + + Self.client.send(request, responseHandler: { (error, response) in + if (error == nil && response != nil) { + self.paymentStatus = .success + + completion(PKPaymentAuthorizationResult(status: .success, errors: nil)) + } else { + print("Error sending payment: \(String(describing: error))") + self.paymentStatus = .failure + + completion(PKPaymentAuthorizationResult(status: .failure, errors: nil)) + } + + self.completionHandler!(self.paymentStatus) + }) + } + + public func paymentAuthorizationControllerDidFinish(_ controller: PKPaymentAuthorizationController) { + controller.dismiss { + DispatchQueue.main.async { + print("Did finish with status: \(self.paymentStatus)") + if self.paymentStatus == .success { + self.completionHandler!(.success) + } else { + self.completionHandler!(.failure) + } + + self.paymentStatus = .unknown + } + } + } + +} diff --git a/PSCBOnlineSample/PSCBOnlineSample/Preview Content/Preview Assets.xcassets/Contents.json b/PSCBOnlineSample/PSCBOnlineSample/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/PSCBOnlineSample/PSCBOnlineSample/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/PSCBOnlineSample/PSCBOnlineSample/SceneDelegate.swift b/PSCBOnlineSample/PSCBOnlineSample/SceneDelegate.swift new file mode 100644 index 0000000..b759199 --- /dev/null +++ b/PSCBOnlineSample/PSCBOnlineSample/SceneDelegate.swift @@ -0,0 +1,63 @@ +// +// SceneDelegate.swift +// sdk.sample +// +// Created by OA on 30.10.2020. +// + +import UIKit +import SwiftUI + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + + var window: UIWindow? + + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. + // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. + // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). + + // Create the SwiftUI view that provides the window contents. + let contentView = ContentView() + + // Use a UIHostingController as window root view controller. + if let windowScene = scene as? UIWindowScene { + let window = UIWindow(windowScene: windowScene) + window.rootViewController = UIHostingController(rootView: contentView) + self.window = window + window.makeKeyAndVisible() + } + } + + func sceneDidDisconnect(_ scene: UIScene) { + // Called as the scene is being released by the system. + // This occurs shortly after the scene enters the background, or when its session is discarded. + // Release any resources associated with this scene that can be re-created the next time the scene connects. + // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). + } + + func sceneDidBecomeActive(_ scene: UIScene) { + // Called when the scene has moved from an inactive state to an active state. + // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. + } + + func sceneWillResignActive(_ scene: UIScene) { + // Called when the scene will move from an active state to an inactive state. + // This may occur due to temporary interruptions (ex. an incoming phone call). + } + + func sceneWillEnterForeground(_ scene: UIScene) { + // Called as the scene transitions from the background to the foreground. + // Use this method to undo the changes made on entering the background. + } + + func sceneDidEnterBackground(_ scene: UIScene) { + // Called as the scene transitions from the foreground to the background. + // Use this method to save data, release shared resources, and store enough scene-specific state information + // to restore the scene back to its current state. + } + + +} + diff --git a/PSCBOnlineSample/PSCBOnlineSample/sdk.sample.entitlements b/PSCBOnlineSample/PSCBOnlineSample/sdk.sample.entitlements new file mode 100644 index 0000000..5ed1a01 --- /dev/null +++ b/PSCBOnlineSample/PSCBOnlineSample/sdk.sample.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.developer.associated-domains + + com.apple.developer.in-app-payments + + merchant.pscb.pay + + + diff --git a/PSCBOnlineSample/PSCBOnlineSampleTests/Info.plist b/PSCBOnlineSample/PSCBOnlineSampleTests/Info.plist new file mode 100644 index 0000000..64d65ca --- /dev/null +++ b/PSCBOnlineSample/PSCBOnlineSampleTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/PSCBOnlineSample/PSCBOnlineSampleTests/sdk_sampleTests.swift b/PSCBOnlineSample/PSCBOnlineSampleTests/sdk_sampleTests.swift new file mode 100644 index 0000000..ee3d01b --- /dev/null +++ b/PSCBOnlineSample/PSCBOnlineSampleTests/sdk_sampleTests.swift @@ -0,0 +1,33 @@ +// +// sdk_sampleTests.swift +// sdk.sampleTests +// +// Created by OA on 30.10.2020. +// + +import XCTest +@testable import sdk_sample + +class sdk_sampleTests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testPerformanceExample() throws { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/PSCBOnlineSample/PSCBOnlineSampleUITests/Info.plist b/PSCBOnlineSample/PSCBOnlineSampleUITests/Info.plist new file mode 100644 index 0000000..64d65ca --- /dev/null +++ b/PSCBOnlineSample/PSCBOnlineSampleUITests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/PSCBOnlineSample/PSCBOnlineSampleUITests/sdk_sampleUITests.swift b/PSCBOnlineSample/PSCBOnlineSampleUITests/sdk_sampleUITests.swift new file mode 100644 index 0000000..0b33773 --- /dev/null +++ b/PSCBOnlineSample/PSCBOnlineSampleUITests/sdk_sampleUITests.swift @@ -0,0 +1,42 @@ +// +// sdk_sampleUITests.swift +// sdk.sampleUITests +// +// Created by OA on 30.10.2020. +// + +import XCTest + +class sdk_sampleUITests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + continueAfterFailure = false + + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // UI tests must launch the application that they test. + let app = XCUIApplication() + app.launch() + + // Use recording to get started writing UI tests. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testLaunchPerformance() throws { + if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) { + // This measures how long it takes to launch your application. + measure(metrics: [XCTApplicationLaunchMetric()]) { + XCUIApplication().launch() + } + } + } +} diff --git a/PSCBOnlineSample/Podfile.lock b/PSCBOnlineSample/Podfile.lock new file mode 100644 index 0000000..9c20897 --- /dev/null +++ b/PSCBOnlineSample/Podfile.lock @@ -0,0 +1,22 @@ +PODS: + - PSCBOnline (1.0.0) + +DEPENDENCIES: + - PSCBOnline (from `https://bitbucket.org/dev_ai/pscbonline.git`, tag `1.0.1`) + +EXTERNAL SOURCES: + PSCBOnline: + :git: https://bitbucket.org/dev_ai/pscbonline.git + :tag: 1.0.1 + +CHECKOUT OPTIONS: + PSCBOnline: + :git: https://bitbucket.org/dev_ai/pscbonline.git + :tag: 1.0.1 + +SPEC CHECKSUMS: + PSCBOnline: 0fe49a5f94b13c88b422774d9225449810f6a437 + +PODFILE CHECKSUM: 56239c9e9e41b37a479ce9620a24a65d5832a2a1 + +COCOAPODS: 1.10.0 diff --git a/PSCBOnlineSample/Pods/Local Podspecs/PSCBOnline.podspec.json b/PSCBOnlineSample/Pods/Local Podspecs/PSCBOnline.podspec.json new file mode 100644 index 0000000..a230daf --- /dev/null +++ b/PSCBOnlineSample/Pods/Local Podspecs/PSCBOnline.podspec.json @@ -0,0 +1,25 @@ +{ + "name": "PSCBOnline", + "version": "1.0.0", + "summary": "An iOS SDK for PSCB Online (OOS) acquiring protocol.", + "description": "An implementation of PSCB-Online (oos.pscb.ru) acquiring protocol for iOS platforms.", + "homepage": "https://oos.pscb.ru/", + "license": { + "type": "PSCB", + "file": "LICENSE.txt" + }, + "authors": { + "Antonov Ilia": "antilya@gmail.com" + }, + "platforms": { + "ios": "10.0" + }, + "source": { + "git": "https://bitbucket.org/dev_ai/oos-ios.git", + "tag": "1.0.0" + }, + "source_files": "PSCBOnline/**/*.{h,m,swift}", + "exclude_files": "", + "swift_versions": "5.3", + "swift_version": "5.3" +} diff --git a/PSCBOnlineSample/Pods/Manifest.lock b/PSCBOnlineSample/Pods/Manifest.lock new file mode 100644 index 0000000..9c20897 --- /dev/null +++ b/PSCBOnlineSample/Pods/Manifest.lock @@ -0,0 +1,22 @@ +PODS: + - PSCBOnline (1.0.0) + +DEPENDENCIES: + - PSCBOnline (from `https://bitbucket.org/dev_ai/pscbonline.git`, tag `1.0.1`) + +EXTERNAL SOURCES: + PSCBOnline: + :git: https://bitbucket.org/dev_ai/pscbonline.git + :tag: 1.0.1 + +CHECKOUT OPTIONS: + PSCBOnline: + :git: https://bitbucket.org/dev_ai/pscbonline.git + :tag: 1.0.1 + +SPEC CHECKSUMS: + PSCBOnline: 0fe49a5f94b13c88b422774d9225449810f6a437 + +PODFILE CHECKSUM: 56239c9e9e41b37a479ce9620a24a65d5832a2a1 + +COCOAPODS: 1.10.0 diff --git a/PSCBOnlineSample/Pods/PSCBOnline/LICENSE.txt b/PSCBOnlineSample/Pods/PSCBOnline/LICENSE.txt new file mode 100644 index 0000000..139d470 --- /dev/null +++ b/PSCBOnlineSample/Pods/PSCBOnline/LICENSE.txt @@ -0,0 +1 @@ +LICENSE BE HERE diff --git a/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/PSCBOnline.h b/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/PSCBOnline.h new file mode 100644 index 0000000..58e4a8b --- /dev/null +++ b/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/PSCBOnline.h @@ -0,0 +1,18 @@ +// +// PSCBOnline.h +// PSCBOnline +// +// Created by Antonov Ilia on 16.10.2020. +// + +#import + +//! Project version number for PSCBOnline. +FOUNDATION_EXPORT double PSCBOnlineVersionNumber; + +//! Project version string for PSCBOnline. +FOUNDATION_EXPORT const unsigned char PSCBOnlineVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Extension/String.swift b/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Extension/String.swift new file mode 100644 index 0000000..b9ff5b9 --- /dev/null +++ b/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Extension/String.swift @@ -0,0 +1,26 @@ +// +// String.swift +// +// +// Created by AntonovIA on 12.10.2020. +// + +import Foundation + +extension String { + + private static let alphanumeric = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + + static func random(length: Int, allowedChars: String? = nil) -> String { + let seq: String = ((allowedChars == nil) ? alphanumeric : allowedChars!) as String + var rnd: String = "" + + for _ in 0.. String { + let data = string.data(using: .utf8)! + var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH)) + var sign = "" + + data.withUnsafeBytes { + _ = CC_SHA256($0.baseAddress, CC_LONG(data.count), &hash) + } + + for byte in hash { + sign += String(format: "%02x", UInt8(byte)) + } + + return sign.lowercased() + } + +} diff --git a/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Helpers/JSONDecoders.swift b/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Helpers/JSONDecoders.swift new file mode 100644 index 0000000..eb537e2 --- /dev/null +++ b/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Helpers/JSONDecoders.swift @@ -0,0 +1,33 @@ +// +// JSONDecoders.swift +// PSCB-OOS-iOS +// +// Created by Antonov Ilia on 25.10.2020. +// + +import Foundation + +final public class JSONDecoders { + + public static func iso8601DateAwareDecoder() -> JSONDecoder { + return ISO8601DateAwareJSONDecoder() + } + +} + +// MARK: - ISO8601 date aware JSON Decoder +class ISO8601DateAwareJSONDecoder: JSONDecoder { + + static internal let dateTimeFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ" + + let dateFormatter: DateFormatter = DateFormatter() + let calendar = Calendar.current + + override init() { + super.init() + + dateFormatter.dateFormat = Self.dateTimeFormat + dateDecodingStrategy = .formatted(dateFormatter) + } + +} diff --git a/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Helpers/RSAHelper.swift b/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Helpers/RSAHelper.swift new file mode 100644 index 0000000..9b806f3 --- /dev/null +++ b/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Helpers/RSAHelper.swift @@ -0,0 +1,86 @@ +// +// RSAHelper.swift +// PSCB-OOS-iOS +// +// Created by Antonov Ilia on 26.10.2020. +// + +import Foundation + +enum RSAKeyError: Error { + + /// Thrown when provided public key is invalid + case invalidKey + + /// Thrown when encryption of message failed + case encryption(OSStatus) + + /// Thrown when unable to initialize `SecKey` with given key string + case unmanaged(Unmanaged) +} + +public final class RSAHelper { + + internal static let key = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAPQsWyHaynwoc2tuMbengf1SFase9tPnwtPh4o1tR+94xsWztADdhhUaUBk/68ipaoZE8uSnM9UgdEPmOotFXyUCAwEAAQ==" + + internal static var secKey: SecKey? = nil + + static func initPSCBOnlinePublicKey() throws { + // Treat as assert + guard let keyData = Data(base64Encoded: key) else { + print("Invalid key string: \(key)") + throw RSAKeyError.invalidKey + } + + var attributes: CFDictionary { + return [ + kSecAttrKeyType: kSecAttrKeyTypeRSA, + kSecAttrKeyClass: kSecAttrKeyClassPublic, + kSecAttrKeySizeInBits: 2048, + kSecReturnPersistentRef: true + ] as CFDictionary + } + + var error: Unmanaged? = nil + guard let secKey = SecKeyCreateWithData(keyData as CFData, attributes, &error) else { + print("Error creating a security key with \(String(describing: keyData))") + print(error.debugDescription) + throw RSAKeyError.unmanaged(error!) + } + + Self.secKey = secKey + } + + /// Encrypts given message with OOS public key. + /// + /// - Parameters: + /// - message: A message to encrypt. + /// + /// - Returns: A base64 encoded string + /// + /// - Throws: `RSAKeyError` type error + public static func encryptEncodeBase64(message: String) throws -> String { + // Initialize OOS public key + if Self.secKey == nil { + try Self.initPSCBOnlinePublicKey() + } + + // Initialize buffers + let sKey = Self.secKey! + let buff = [UInt8](message.utf8) + + // Mutable encryption refs + var size = SecKeyGetBlockSize(sKey) + var kBuf = [UInt8](repeating: 0, count: size) + + let status = SecKeyEncrypt(sKey, SecPadding.PKCS1, buff, buff.count, &kBuf, &size) + + // Sanity check + guard status == errSecSuccess else { + throw RSAKeyError.encryption(status) + } + + return Data(bytes: kBuf, count: size).base64EncodedString() + } + +} diff --git a/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Models/CardData.swift b/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Models/CardData.swift new file mode 100644 index 0000000..bd8ed9a --- /dev/null +++ b/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Models/CardData.swift @@ -0,0 +1,92 @@ +// +// CardData.swift +// PSCB-OOS-iOS +// +// Created by Antonov Ilia on 21.10.2020. +// + +import Foundation +import PassKit + +public enum CardDataError: Error { + case invalidExiryDate(String) + case invalidCvCode + case invalidPan +} + +/// Billing info. +/// Detailed card information +public struct CardData { + let pan: String + let expiryYear: Int + let expiryMonth: Int + let cardholder: String? + let cvCode: String + + /// Constructs card data from all necessary for billing fields + /// + /// - Parameters: + /// - pan: Card number + /// - expiryYear: A full year number (e.g: `2020`). For production cannot be in the past + /// - expiryMonth: A month number from 1 to 12. + /// - cvCode: CVC/CVV number + /// - cardholder: (Optional) Cardholder name in latin. + /// + /// - Returns: optional instance of a new card. If validation fails returns nil + public init?(pan: String, expiryYear: Int, expiryMonth: Int, cvCode: String, cardholder: String? = nil) { + guard expiryMonth >= 1 && expiryMonth <= 12 else { + print("Month can be between 1 and 12") + return nil + } + + #if !DEBUG + // todo check year + month in the past + let currentYear = Calendar.current.component(.year, from: Date()) + guard expiryYear >= currentYear else { + print("Year must be current or in the future") + return nil + } + #endif + + self.pan = pan + self.expiryMonth = expiryMonth + self.expiryYear = expiryYear + self.cvCode = cvCode + self.cardholder = cardholder + } + + /// - Returns: A last 2 digits of the expiry year + public func getExpYearString() -> String { + return String(String(expiryYear).dropFirst(2)) + } + + /// - Returns: A zero padded expiry month string + public func getExpMonthString() -> String { + return expiryMonth < 10 ? "0\(self.expiryMonth)" : String(self.expiryMonth) + } + +} + +extension CardData { + + internal static let pubKey: Data = { + let key = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAPQsWyHaynwoc2tuMbengf1SFase9tPnwtPh4o1tR+94xsWztADdhhUaUBk/68ipaoZE8uSnM9UgdEPmOotFXyUCAwEAAQ==" + + return Data(base64Encoded: key)! + }() + + + /// Produces a cryptogram string from given `CardData` + /// See: https://docs.pscb.ru/oos/shpa.html#shlyuz-prozrachnoj-avtorizacii-sozdanie-platezha-otvet + /// + /// - Returns: a Base64 encoded cryptogram string + public func toCryptgramString() throws -> String { + let month = self.getExpMonthString() + let year = self.getExpYearString() + let joinedString = "\(self.pan)|\(month)|\(year)|\(cvCode)\(self.cardholder ?? "")" + let base64Encode = try RSAHelper.encryptEncodeBase64(message: joinedString) + + return base64Encode + } + +} diff --git a/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Models/CustomerData.swift b/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Models/CustomerData.swift new file mode 100644 index 0000000..710ea0b --- /dev/null +++ b/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Models/CustomerData.swift @@ -0,0 +1,29 @@ +// +// CustomerData.swift +// +// +// Created by Antonov Ilia on 12.10.2020. +// + +import Foundation + +/// A costumer data to present in PaymentData +public struct CustomerData: Codable { + + /// Unique costumer ID + let account: String + + let comment: String? + + let email: String? + + let phone: String? + + enum CodingKeys: String, CodingKey { + case account = "customerAccount" + case comment = "customerComment" + case email = "customerEmail" + case phone = "customerPhone" + } + +} diff --git a/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Models/Payment.swift b/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Models/Payment.swift new file mode 100644 index 0000000..74b0b37 --- /dev/null +++ b/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Models/Payment.swift @@ -0,0 +1,90 @@ +// +// Payment.swift +// +// +// Created by AntonovIA on 12.10.2020. +// + +import Foundation + +// MARK: - Payment methods + +internal enum PaymentMethods: String { + case shpa = "ac-shpa" +} + +// MARK: - Request Message + +public struct Payment: Encodable { + + // Required: + + /// Payment amount in rub + /// min: 1.00 + public let amount: Decimal + + /// Your market order ID + /// maxlength: 20 + public let orderId: String + + // Payment method + internal let paymentMethod: PaymentMethods = .shpa + + // Optional: + + /// Human readable `orderId` + /// Defaults to `orderId` + var showOrderId: String? + + /// Detailed text describing this order payment. + var details: String? + + /// Data about paying customer + var customer: CustomerData? + + // + var recurrentable: Bool = false + + public init(amount: Decimal, orderId: String, + showOrderId: String? = nil, details: String? = nil, + customer: CustomerData? = nil, recurrentable: Bool = false) { + self.amount = amount + self.orderId = orderId + self.showOrderId = (showOrderId == nil) ? orderId : showOrderId + self.details = details + self.customer = customer + self.recurrentable = recurrentable + } + + // MARK: - Encoder + + private enum CodingKeys: String, CodingKey { + case amount, orderId, showOrderId, details, recurrentable, customer + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(amount, forKey: .amount) + try container.encode(orderId, forKey: .orderId) + try container.encode(showOrderId, forKey: .showOrderId) + try container.encode(details, forKey: .details) + try container.encode(recurrentable, forKey: .recurrentable) + + // Customer + try customer?.encode(to: encoder) + } + + #if DEBUG + + public static let example = Payment( + amount: 150.00, orderId: String.random(length: 6), + details: "Wonderful warm socks", + customer: CustomerData( + account: "ID-12345", comment: "By tomorrow please", + email: "foo@bar.com", phone: "+7 900 000 00 00" + ) + ) + + #endif + +} diff --git a/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Models/RequestWrapper.swift b/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Models/RequestWrapper.swift new file mode 100644 index 0000000..ad4aebc --- /dev/null +++ b/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Models/RequestWrapper.swift @@ -0,0 +1,44 @@ +// +// RequestWrapper.swift +// +// +// Created by Antonov Ilia on 12.10.2020. +// + +import Foundation + +// MARK: - Top level request + +/// Top level request wrapper for OOS requests +public struct RequestWrapper: Encodable { + + /// Merchant ID + public let marketPlaceId: String + + /// Payment info + public let payment: Payment + + /// Encoded card data + public let cardData: String + + /// Creates instance of RequestWrapper + /// + /// - Parameters: + /// - marketPlaceId: Your OOS market place ID + /// - payment: Payment details object + /// - cardData: Encoded card data + /// + /// - Returns: Prepared request ready to fire + public init(marketPlaceId: String, payment: Payment, cardData: String) { + self.marketPlaceId = marketPlaceId + self.payment = payment + self.cardData = cardData + } + + private enum CodingKeys: String, CodingKey { + case marketPlaceId = "marketPlace" + case payment + case cardData + } + +} diff --git a/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Models/Response.swift b/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Models/Response.swift new file mode 100644 index 0000000..d23f383 --- /dev/null +++ b/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Models/Response.swift @@ -0,0 +1,100 @@ +// +// Response.swift +// PSCB-OOS-iOS +// +// Created by Antonov Ilia on 23.10.2020. +// + +import Foundation + +// MARK: - PSCB OOS Response status +public enum ResponseStatus: String, Decodable { + case success = "STATUS_SUCCESS" + case failure = "STATUS_FAILURE" +} + +// MARK: - Acquiring data +public struct AcquiringResponseData: Decodable { + public let paReq: String? + public let order: String? + public let termUrl: String? + public let url: String? + public let md: String? + + private enum CodingKeys: String, CodingKey { + case order, termUrl = "TermUrl", url = "URL", md = "MD", paReq = "PaReq" + } +} + +// MARK: - OOS Error + +/// An error structure for OOS errors returned in flat JSON +/// `errorCode` and `errorDescription` +public struct ResponseError: Decodable { + + public let code: String + public let description: String? + + private enum CodingKeys: String, CodingKey { + case code = "errorCode" + case description = "errorDescription" + } + +} + +// MARK: - OOS Response + +/// JSON Response from OOS API +public struct Response: Decodable { + + public let status: ResponseStatus + public let requestId: String + public let error: ResponseError? + public let payment: ResponsePayment? + public let acquiringData: AcquiringResponseData? + public let description: String? + + private enum CodingKeys: String, CodingKey { + case status, requestId, payment, acquiringData, description + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.status = try container.decode(ResponseStatus.self, forKey: .status) + self.requestId = try container.decode(String.self, forKey: .requestId) + self.description = try? container.decode(String.self, forKey: .description) + + // Decode flat and nested data into nested structs + self.payment = try? container.decode(ResponsePayment.self, forKey: .payment) + self.acquiringData = try? AcquiringResponseData(from: decoder) + self.error = try? ResponseError(from: decoder) + } + +} + +// MARK: - Payment status +/// Payment stats +/// see: https://docs.pscb.ru/oos/index.html#obshaya-informaciya-spravochniki-statusy-platezhej +public enum PaymentState: String, Decodable { + case sent = "sent" + case new = "new" + case end = "end" + case refunded = "ref" + case hold = "hold" + case expired = "exp" + case canceled = "canceled" + case error = "error" + case rejected = "rej" + case undefined = "undef" +} + +// MARK: - Payment response struct +public struct ResponsePayment: Decodable { + public let orderId: String + public let showOrderId: String + public let paymentId: String + public let amount: Decimal + public let state: PaymentState + public let marketPlace: UInt16 + public let stateDate: Date +} diff --git a/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/PSCBAPI.swift b/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/PSCBAPI.swift new file mode 100644 index 0000000..83e04d6 --- /dev/null +++ b/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/PSCBAPI.swift @@ -0,0 +1,55 @@ +// +// OOSAPI.swift +// PSCB-OOS-iOS +// +// Created by Antonov Ilia on 27.10.2020. +// + +import Foundation +import PassKit + +/// Utility fabric methods required for PSCB API. +public class PSCBAPI { + + public static let supportedNetworks: [PKPaymentNetwork] = [.visa, .masterCard] + + /// - Returns: a tuple describing if application `canMakePayments` and `canSetupCards` using supported networks. + public class func canMakePayments() -> (canMakePayments: Bool, canSetupCards: Bool) { + return ( + PKPaymentAuthorizationController.canMakePayments(), + PKPaymentAuthorizationController.canMakePayments(usingNetworks: supportedNetworks) + ) + } + + /// Creates a default instance of `PKPaymentRequest` with pre-default parameters. + /// + /// - Returns: `PKPaymentRequest` + public class func makePaymentRequest(items: [PKPaymentSummaryItem]) -> PKPaymentRequest { + return makePaymentRequest(merchantId: "merchant.pscb.pay", items: items) + } + + /// Creates a default instance of `PKPaymentRequest` with pre-default parameters. + /// + /// - Parameters: + /// - merchantId: Your Apple Pay merchant ID + /// - items: Summary items + /// + /// - Returns: `PKPaymentRequest` + public class func makePaymentRequest(merchantId: String, items: [PKPaymentSummaryItem]) -> PKPaymentRequest { + let request = PKPaymentRequest() + request.merchantIdentifier = merchantId + request.supportedNetworks = Self.supportedNetworks + + if #available(iOS 11.0, *) { + //request.supportedCountries = ["RU", "UA", "BY", "US"] + } + + request.merchantCapabilities = .capability3DS + request.countryCode = "RU" + request.currencyCode = "RUB" + request.paymentSummaryItems = items + + return request + } + +} diff --git a/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/PSCBOnlineClient.swift b/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/PSCBOnlineClient.swift new file mode 100644 index 0000000..95538fb --- /dev/null +++ b/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/PSCBOnlineClient.swift @@ -0,0 +1,306 @@ +// +// OOSAPIClient.swift +// PSCB-OOS-iOS +// +// Created by Antonov Ilia on 12.10.2020. +// + +import Foundation +import PassKit + +// MARK: - Environments + +public enum BackendEnvironment: String { + + case production = "oos.pscb.ru" + case sandbox = "oosdemo.pscb.ru" + +} + +// MARK: - Request status + +/// Backend request status +/// Consists of two possible states: `.success` and `.failure`. +/// +/// This one represents if request to the backend server was successful +public enum RequestStatus { + case success, failure +} + +// MARK: - Response wrapper + +/// Backend response +public struct BackendResponse { + + /// Represents request status + public let status: RequestStatus + + /// Hold error information if request is a `.failure` + public let error: Error? + + /// Holds response information if request is a `.success` + public let response: Response? +} + +// MARK: - Alias for completion handler + +/// Callback for `OOSAPIClient.send` method +public typealias APICompletionHandler = (BackendResponse) -> Void + +/// Callback for another `OOSAPIClient.send` method which fires after certain guards ensure success or failure +/// to simplify API usage +public typealias PostResponseHandler = (Error?, Response?) -> Void + +// MARK: - API Integration errors + +/// Possible erros when dealing with OOS backend +public enum OOSErrors: Error { + case noData + case parse(Error) + case cause(Error) + + /// Consists of error code and error description + case backend(String, String) +} + +// MARK: - API Client + +/// Implementation of OOS Merchant API HTTP protocol. +final public class PSCBOnlineClient { + + private lazy var urlSession: URLSession = { + URLSession(configuration: .default, delegate: nil, delegateQueue: .main) + }() + + private lazy var url: URL = { + var components = URLComponents() + components.scheme = "https" + components.host = self.environment.rawValue + components.path = "/merchantApi/payShpa" + + return components.url! + }() + + private let decoder = JSONDecoders.iso8601DateAwareDecoder() + + // init: + + let environment: BackendEnvironment + let marketPlaceId: String + let signingKey: String + + /// Initializes `OOSAPIClient` + /// + /// - Parameters: + /// - environment: Backend environment. Which one to use. For testing environment use `.sandbox` + /// - marketPlaceId: Your OOS market-place ID + /// - siginingKey: Your OOS signing key + public init(environment: BackendEnvironment, marketPlaceId: String, signingKey: String) { + self.environment = environment + self.marketPlaceId = marketPlaceId + self.signingKey = signingKey + } + + // impl: + + /// Creates instance of `RequestWrapper` for a backend to process from given PKPayment instance and other details + /// + /// - Parameters: + /// - payment: authorized PKPayment instance + /// - amount: total order amount + /// - orderId: Unique merchant order ID + /// + /// - Returns: RequestWrapper instance for backend + public func makeRequestWithPayment(payment: PKPayment, amount: Decimal, orderId: String) throws -> RequestWrapper { + return try makeRequestWithPayment(pkPayment: payment, payment: Payment(amount: amount, orderId: orderId)) + } + + /// Creates instance of `RequestWrapper` for a backend to process from given `PKPayment` instance and backend `Payment` instance + /// + /// - Parameters: + /// - pkPayment: authorized `PKPayment` instance + /// - payment: backend `Payment` details + /// + /// - Returns: RequestWrapper instance for backend + public func makeRequestWithPayment(pkPayment: PKPayment, payment: Payment) throws -> RequestWrapper { + // Get card data cryptogram from payment token + let cardDataCrypto = try pkPayment.token.toCryptogramString() + + // Backend request wrapper + let requestWrapper = RequestWrapper( + marketPlaceId: self.marketPlaceId, + payment: payment, + cardData: cardDataCrypto + ) + + // Fail early + try requestWrapper.assumeSerializes() + + return requestWrapper + } + + /// Creates instance of `RequestWrapper` for a backend to process from raw card data instead of ApplePay token + /// + /// Example: + /// ``` + /// let card = CardData( + /// pan: "409444400001234", + /// expiryYear: 2025, + /// expiryMonth: 12, + /// cvCode: "000", + /// cardholder: "JOHN DOE" + /// ) + /// + /// let payment = Payment(amount: Decimal(1500), orderId: "XC-12345") + /// let request = try apiClient.createRequestWithCardData(card: card, payment: payment) + /// + /// // Send to backend: + /// apiClient.send(request) { (response) in /code/ } + /// ``` + /// + /// - Parameters: + /// - cardData: an instance of card data. + public func makeRequestWithCardData(card: CardData, payment: Payment) throws -> RequestWrapper { + let cryptogram = try card.toCryptgramString() + let request = RequestWrapper( + marketPlaceId: self.marketPlaceId, + payment: payment, + cardData: cryptogram + ) + + // Fail-early compile + try request.assumeSerializes() + + return request + } + + /// Signs and sends compiled `RequestWrapper` to the backend server + /// Fires `APICompletionHandler` once requests succeeds or fails + /// + /// - Parameters: + /// - request: `RequestWrapper` created from `createRequestWithPayment(...)` + /// - completionHander: `APICompletionHandler` a callback for when requests succeeds or fails + /// + public func send(_ request: RequestWrapper, completionHandler: @escaping APICompletionHandler) { + // Calculate signature and get HTTP body at the same time + let httpBody = try! request.serializeToString() + let signature = calculateSignature(httpBody) + + // Request params + let url = self.url + var req = URLRequest(url: url) + + // Set request parameters + req.httpMethod = "POST" + req.httpBody = httpBody.data(using: .utf8) + req.setValue(signature, forHTTPHeaderField: "Signature") + + print(">> Request JSON: \(String(describing: String(data: req.httpBody!, encoding: .utf8)))") + + let task = urlSession.dataTask(with: req) { (data, res, err) in + guard err == nil else { + let error = self.err_requestError(req, error: err) + completionHandler(error) + return + } + + guard let data = data else { + let error = self.failure(req, error: OOSErrors.noData) + completionHandler(error) + return + } + + print("<< Response data: \(String(data: data, encoding: .utf8) ?? "N/A")") + + do { + let response = try self.decoder.decode(Response.self, from: data) + + // Finaly success + let success = BackendResponse(status: .success, error: nil, response: response) + + completionHandler(success) + } catch let error { + print("Failed to do task on request \(req)") + print(error.localizedDescription) + + // Reading and parsing errors + let failure = self.failure(req, error: .parse(error)) + completionHandler(failure) + } + + } + + // Fire task + task.resume() + } + + /// Signs and sends compiled `RequestWrapper` to the backend server + /// Fires `PostResponseHandler` once requests succeeds or fails. + /// `PostResponseHandler` accepts two arguments: `Error?` and `Response?`. + /// + /// If requests succeeds and payment in desired state `Error?` will always be `nil`. + /// `Response?` presents on some errors and on all successes. + /// + /// This is an utility method to reduce boilerplate for necessary checks. + /// + /// - Parameters: + /// - request: `RequestWrapper` created from `createRequestWithPayment(...)` + /// - completionHander: `APICompletionHandler` a callback for when requests succeeds or fails + /// + public func send(_ request: RequestWrapper, responseHandler: @escaping PostResponseHandler) { + // Construct a closure wrapper with predefined guards and checks + let wrapper: APICompletionHandler = { (backendResponse) in + // HTTP request succeeded? + guard backendResponse.status == .success else { + print("Failed to execute request due to error: \(String(describing: backendResponse.error))") + + responseHandler(OOSErrors.cause(backendResponse.error!), nil) + return + } + + // Backend returned valid response? + guard let response = backendResponse.response else { + print("Backend returned empty body") + + responseHandler(OOSErrors.noData, nil) + return + } + + // Backend payment succeeded? + guard response.status == .success else { + print("Unable to process payment") + + let responseError = response.error + let backendError = OOSErrors.backend(responseError?.code ?? "FAILED", responseError?.description ?? "") + + // At this point we can pass response too + responseHandler(backendError, response) + return + } + + // Finally succeeds + responseHandler(nil, response) + } + + // Send using more lower-level method above + send(request, completionHandler: wrapper) + } + + // MARK: - Private + + private func err_requestError(_ req: URLRequest, error: Error?) -> BackendResponse { + print("Failed to do task on request \(req) with error: \(String(describing: error))") + return BackendResponse(status: .failure, error: OOSErrors.cause(error!), response: nil) + } + + private func failure(_ req: URLRequest, error: OOSErrors) -> BackendResponse { + print("Request \(req) failed with error: \(String(describing: error))") + return BackendResponse(status: .failure, error: error, response: nil) + } + + private func calculateSignature(_ jsonString: String) -> String { + let composite = (jsonString + self.signingKey) + return DigestHelper.sha256String(composite) + } + +} diff --git a/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/PaymentHandler.swift b/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/PaymentHandler.swift new file mode 100644 index 0000000..a6bad97 --- /dev/null +++ b/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/PaymentHandler.swift @@ -0,0 +1,113 @@ +// +// PaymentHandler.swift +// PSCB-OOS-iOS +// +// Created by Antonov Ilia on 16.10.2020. +// + +import Foundation +import PassKit + +public typealias PaymentCompletionHandler = (Bool) -> Void + +public class ApplePayHandler: NSObject { + + static let supportedNetworks: [PKPaymentNetwork] = [.visa, .masterCard] + + // MARK: - Fields + + public let merchantId: String + public let merchantKey: String + public let summaryItems: [PKPaymentSummaryItem] + + // MARK: - Properties + + var paymentController: PKPaymentAuthorizationController? + var paymentStatus: PKPaymentAuthorizationStatus = .failure + var completionHandler: PaymentCompletionHandler? + var payment: Payment? + + public init?(merchantId: String, merchantKey: String, summaryItems: [PKPaymentSummaryItem]) { + guard PKPaymentAuthorizationController.canMakePayments(usingNetworks: Self.supportedNetworks) else { + print("Could not instantiate ApplePayHandler. Cannot make payments with provided networks") + return nil + } + + self.merchantId = merchantId + self.merchantKey = merchantKey + self.summaryItems = summaryItems + } + + public func handle(payment: Payment, completion: @escaping PaymentCompletionHandler) { + self.completionHandler = completion + self.payment = payment + + NSLog("About to handle payment: \(payment)") + + paymentController = PKPaymentAuthorizationController(paymentRequest: paymentRequest) + paymentController?.delegate = self + paymentController?.present { (presented) in + if !presented { + NSLog("Could not present PKPaymentAuthorizationController") + self.completionHandler!(false) + } else { + NSLog("PKPaymentAuthorizationController presented") + } + } + + } + + // MARK: - Private details + + private var paymentRequest: PKPaymentRequest { + let request = PKPaymentRequest() + + request.countryCode = "RU" + request.merchantIdentifier = "merchant.pscb.pay" + request.currencyCode = "RUB" + request.supportedNetworks = Self.supportedNetworks + request.merchantCapabilities = .capability3DS + request.paymentSummaryItems = summaryItems + + return request + } + +} + + +// MARK: - Extension for PKPaymentAuthorizationControllerDelegate + +extension ApplePayHandler: PKPaymentAuthorizationControllerDelegate { + + public func paymentAuthorizationController(_ controller: PKPaymentAuthorizationController, + didAuthorizePayment payment: PKPayment, + completion: @escaping (PKPaymentAuthorizationStatus) -> Void) { + +// print("Payment string") +// let paymentString = try! payment.serializeToString() +// print(paymentString) +// +// // +// +// self.paymentStatus = .success + completion(PKPaymentAuthorizationStatus.success) + } + + @available(iOS 11.0, *) + public func paymentAuthorizationController(_ controller: PKPaymentAuthorizationController, didAuthorizePayment payment: PKPayment, handler completion: @escaping (PKPaymentAuthorizationResult) -> Void) { +// +// NSLog("About to send request to backend") +// let paymentString = try! payment.serializeToString() +// +// NSLog("Payment JSON: \(paymentString)") +// + let result = PKPaymentAuthorizationResult(status: .success, errors: nil) + completion(result) + self.completionHandler?(true) + } + + public func paymentAuthorizationControllerDidFinish(_ controller: PKPaymentAuthorizationController) { + controller.dismiss(completion: nil) + } + +} diff --git a/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Serializable/PKPaymentToken+Serializable.swift b/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Serializable/PKPaymentToken+Serializable.swift new file mode 100644 index 0000000..a64bacd --- /dev/null +++ b/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Serializable/PKPaymentToken+Serializable.swift @@ -0,0 +1,79 @@ +// +// PKPaymentToken+Serializable.swift +// PSCB-OOS-iOS +// +// Created by OA on 17.10.2020. +// + +import Foundation +import PassKit + +// MARK: - Comfront to serializable +extension PKPaymentToken: Serializable { + + // Serializes token to a JSONLike dictionary + public func serializeToJSON() -> JSONDict { + let paymentJson: JSONDict? = try? JSONSerialization.jsonObject( + with: self.paymentData, options: .mutableContainers + ) as? JSONDict + + var paymentType: String = "debit" + var methodAndNetwork: JSONDict = [ + "network": "", + "type": paymentType, + "displayName": "" + ] + + if #available(iOS 9.0, *) { + methodAndNetwork = [ + "network": self.paymentMethod.network?.rawValue ?? "", + "type": paymentType, + "displayName": self.paymentMethod.displayName ?? "" + ] + + switch self.paymentMethod.type { + case .debit: + paymentType = "debit" + case .credit: + paymentType = "credit" + case .store: + paymentType = "store" + case .prepaid: + paymentType = "prepaid" + default: + paymentType = "unknown" + } + } + + return [ + "paymentData": paymentJson, + "transactionIdentifier": self.transactionIdentifier, + "paymentMethod": methodAndNetwork + ] as JSONDict + } + +} + +// MARK: - To string cryptogram +extension PKPaymentToken { + + private func paymentJSONData() -> JSONDict? { + let json = self.serializeToJSON() + let data = json["paymentData"] as! JSONDict? + + return data + } + + /// Creates a Base64 encoded cryptogram string accepted by PSCB OOS protocol + /// May throw JSON serialization errors if token could not be converted to JSON string before writing a cryptogram. + /// + /// - Returns: Base64 encoded string + public func toCryptogramString() throws -> String { + let json = self.paymentJSONData() + let data = try JSONSerialization.data(withJSONObject: json!, options: []) + // let string = String(data: data, encoding: .utf8)! + + return data.base64EncodedString(options: Data.Base64EncodingOptions(rawValue: 0)) + } + +} diff --git a/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Serializable/Payment+Serializable.swift b/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Serializable/Payment+Serializable.swift new file mode 100644 index 0000000..74767bd --- /dev/null +++ b/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Serializable/Payment+Serializable.swift @@ -0,0 +1,27 @@ +// +// Payment+Serializable.swift +// PSCB-OOS-iOS +// +// Created by Antonov Ilia on 21.10.2020. +// + +import Foundation + +extension Payment: Serializable { + + public func serializeToJSON() -> JSONDict { + var json = JSONDict() + json["orderId"] = self.orderId + json["amount"] = self.amount + json["showOrderId"] = self.showOrderId + json["details"] = self.details + json["recurrentable"] = self.recurrentable + json["customerAccount"] = self.customer?.account + json["customerComment"] = self.customer?.comment + json["customerEmail"] = self.customer?.email + json["customerPhone"] = self.customer?.phone + + return json + } + +} diff --git a/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Serializable/RequestWrapper+Serializable.swift b/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Serializable/RequestWrapper+Serializable.swift new file mode 100644 index 0000000..25a1bdf --- /dev/null +++ b/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Serializable/RequestWrapper+Serializable.swift @@ -0,0 +1,21 @@ +// +// RequestWrapper+Serializable.swift +// PSCB-OOS-iOS +// +// Created by Antonov Ilia on 18.10.2020. +// + +import Foundation + +extension RequestWrapper: Serializable { + + public func serializeToJSON() -> JSONDict { + var json = JSONDict() + json["marketPlace"] = self.marketPlaceId + json["payment"] = self.payment.serializeToJSON() + json["cardData"] = self.cardData + + return json + } + +} diff --git a/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Serializable/Serializable.swift b/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Serializable/Serializable.swift new file mode 100644 index 0000000..520de98 --- /dev/null +++ b/PSCBOnlineSample/Pods/PSCBOnline/PSCBOnline/Sources/Serializable/Serializable.swift @@ -0,0 +1,61 @@ +// +// Serializable.swift +// PSCB-OOS-iOS +// +// Created by OA on 16.10.2020. +// + +import Foundation + +// MARK: - Serializable + +public protocol Serializable { + + /// Serializes current object to generic JSON-Like dictionary + func serializeToJSON() -> JSONDict + +} + +// MARK: - Extension + +public extension Serializable { + + /// Serializes current object to data + func serializeToData() throws -> Data { +// let encoder = JSONEncoder() +// let data = try encoder.encode(self.serializeToJSON()) +// +// return data + return try JSONSerialization.data( + withJSONObject: self.serializeToJSON(), + options: [] + ) + } + + /// Serialzes to JSON-String + func serializeToString() throws -> String { + let data = try self.serializeToData() + return String(data: data, encoding: .utf8)! + } + + /// Used for checking purposes if this object can be serialized without exception + func assumeSerializes() throws { + try serializeToString() + } + +} + +// MARK: - JSONDict + +public typealias JSONDict = [AnyHashable: AnyHashable] + +//extension JSONDict { +// +// public mutating func add(key: T, nullable value: JSONDict.Value?) where T.RawValue == JSONDict.Key { +// if let value = value { +// self.updateValue(value, forKey: key.rawValue) +// } +// } +// +//} + diff --git a/PSCBOnlineSample/Pods/PSCBOnline/README.md b/PSCBOnlineSample/Pods/PSCBOnline/README.md new file mode 100644 index 0000000..c215649 --- /dev/null +++ b/PSCBOnlineSample/Pods/PSCBOnline/README.md @@ -0,0 +1,217 @@ +# ПСКБ Платежи iOS SDK + +[![Platform](https://img.shields.io/badge/Support-iOS%2010.0+-brightgreen.svg)](https://img.shields.io/badge/Support-iOS%2010.3+-brightgreen.svg) + +Библиотека является дополнением к API системы интернет-эквайринга [ПСКБ "Платежи"](https://online.pscb.ru) +и позволяет подключить приём платежей по картам в мобильных приложениях iOS с минимальными усилиями. + +## Возможности + +На текущий момент библиотека поддерживает: + + - Apple Pay + - Оплата картами + +## Подключение зависимостей + +1. Установите CocoaPods 1.10.0 или выше + +```zsh +gem install cocoapods +``` +[Официальная документация по установке CocoaPods](https://guides.cocoapods.org/using/getting-started.html) + +2.  Создайте в своём приложении `Podfile` + +Это также можно сделать при помощи команды `pod init` , находясь в директории своего проекта. (В таком случае будет создан `Podfile` с настройками по умолчанию) + +3. Добавьте зависимости в `Podfile` + +```ruby +platform :ios, '10.0' + +target '' do + use_frameworks! + + pod 'PSCBOnline', :git => "https://bitbucket.org/dev_ai/pscbonline.git", :tag => "1.0.0" + +end +``` + +> `` - Название проекта вашего приложения в XCode + +4. Выполните команду `pod install` + +## Интеграция + +1. Для работы с библиотекой импортируйте зависимости в нужный файл проекта: + +```swift +import PSCBOnline +``` + +2. Создайте экземпляр `PSCBOnlineClient` с вашими настройками: + +```swift +let apiClient = PSCBOnlineClient( + environment: .sandbox, + marketPlaceId: "", + signingKey: "" +) +``` + +> `environment` -  окружение, в рамках которого библиотека взаимодействует с сервисом. `.sandbox` - тестовое окружение; `.production` - продуктовое. +> `Your MarketPlace ID` - ваш идентификатор в системе ПСКБ-Онлайн. +> `Your Signing Key` - ваш ключ подписи запросов к системе. + +3. Создайте экземпляр `Payment` + +```swift +let payment = Payment(amount: Decimal(1000.00), orderId: "Order-ID") +``` +> `amount` - сумма в рублях. (_На текущий момент другая валюта не поддерживается._) +> `orderId` - уникальный идентификатор заказа в рамках магазина. + +_Для детальной информации, какие параметры принимает смотрите документацию `Payment`._ + +---- + +Дальнейшая интеграция отличается от способа оплаты. + +## Доступные способы оплаты + +Сейчас в SDK доступна оплата: + + - Банковкой кратой + - Apple Pay + + Для их настройки реализации смотрите дальше. + +### Приём оплаты банковскимикартами + +1. Для приёма оплаты картами нужно создать экземпляр класса `CardData`. + +```swift +let card = CardData( + pan: "4761349750010326", + expiryYear: 2022, + expiryMonth: 12, + cvCode: "851" +) +``` + +2. Создать токен запроса, используя экземпляр `PSCBOnlineClient`, созданный ранее. + +```swift +let request = try! client.makeRequestWithCardData(card: card, payment: payment) +``` + +3. Отправить токен запроса на сервер ПСКБ-Онлайн. + +```swift +// Send request to backend +client.send(request, responseHandler: { (error, response) in + guard nil == error && nil != response else { + print("Error sending request: \(String(describing: error))") + return + } + + print("Successful request:") + print(response!) +}) +``` + +> В случае успешного выполнения запроса, `response` будет содержать информацию о принятом платеже, его ID, состояние и прочие данные. + +### Приём оплаты Apple Pay + +Для приёма платежей через Apple Pay вы должны зарегестрировать Merchant ID в Apple. + +Помимо `merchant ID` необходимо настроить сертификат обработки запросов (`Payment Processing Certificate`) и передать его ПСКБ-Онлайн. Этим сертификатом Apple будет шифровать данные банковских карт перед отправкой на сервер ПСКБ-Онлайн. + +Все пререквизиты описаны на сайте [официальной документации Apple](https://developer.apple.com/documentation/passkit/apple_pay/setting_up_apple_pay_requirements). + +Также вы можете ознакомиться сподробной инструкцией на сайте документации [ПСКБ Онлайн](https://docs.pscb.ru/oos/advanced.html#dopolnitelnye-opcii-apple-pay). + +--- + +После выполнения пререквизитов, в коде необходимо: + +1. Импортировать `PassKit`: + +```swift +import PassKit +``` + +2. Создать экземпляр класса `PKPaymentAuthorizationController` и настроить делегирующий класс `PKPaymentAuthorizationControllerDelegate`: + +Пример: + +```swift +import PassKit +import PSCBOnline + +class ApplePayHandler: NSObject { + + public func handleApplePay(payment: Payment) { + // Позиции оплаты для представления пользователя + let items = [PKPaymentSummaryItem(label: "Shoes", amount: NSDecimalNumber(intergerLiteral: 1000))] + + // Запрос на оплату для PKPaymentAuthorizationController + let paymentRequest = PSCBAPI.makePaymentRequest(merchantId: "", items: []) + + // Вызов модуля оплаты + let authorizationController = PKPaymentAuthorizationController(paymentRequest: paymentRequest) + + authorizationController?.delegate = self + authorizationController?.present(completion: { presented in + if presented { + print("Controller presented") + } else { + print("Controller could not be presented") + } + }) + } +} + +extension ApplePayHandler: PKPaymentAuthorizationControllerDelegate { + + public func paymentAuthorizationController(_ controller: PKPaymentAuthorizationController, + didAuthorizePayment payment: PKPayment, + handler completion: @escaping (PKPaymentAuthorizationResult) -> Void) { + + // 1. + // Вызывается после авторизации платежа плательщиком биометрическими данными или паролем + } + + public func paymentAuthorizationControllerDidFinish(_ controller: PKPaymentAuthorizationController) { + // 2. + // Вызывается после того, как платёж совершён или пользователь закрыл модуль оплаты. + } + +} +``` + +> `` - Ваш идентификатор мерчанта. + +3. Далее в примере выше в п.1 и п.2 реализовать необходимую логику отправки запроса на сервер ПСКБ-Онлайн. + Для пункта 1 - это создание токена запроса (используя `client.makeRequestWithPayment(pkPayment, pscbPayment)` и отправка его на сервер: + +```swift +// Request token +let request = try! client.makeRequestWithPayment(pkPayment: pkPayment, pscbPayment: payment) + +// Sending request to backend +client.send(request, responseHandler: { (error, response) in + if (error == nil && response != nil) { + completion(PKPaymentAuthorizationResult(status: .success, errors: nil)) + } else { + print("Error sending payment: \(String(describing: error))") + completion(PKPaymentAuthorizationResult(status: .failure, errors: nil)) + } +}) +``` + +4. А для пункта 2 - обработка закрытия модуля оплаты. + +## Описание классов и параметров diff --git a/PSCBOnlineSample/Pods/Pods.xcodeproj/project.pbxproj b/PSCBOnlineSample/Pods/Pods.xcodeproj/project.pbxproj new file mode 100644 index 0000000..521e484 --- /dev/null +++ b/PSCBOnlineSample/Pods/Pods.xcodeproj/project.pbxproj @@ -0,0 +1,1030 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXBuildFile section */ + 08153B37829081830118DC3FDA1C8891 /* RequestWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D302DD80F732D394A15C10B518CA9793 /* RequestWrapper.swift */; }; + 13C0ACA010CFDF4876F85B5308F50E1B /* Pods-PSCBOnlineSample-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 07A5FA728F8AB4366597F4A40E03957E /* Pods-PSCBOnlineSample-dummy.m */; }; + 1639B897ACE3EDFA100BC2BA2CDB90F8 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 73010CC983E3809BECEE5348DA1BB8C6 /* Foundation.framework */; }; + 1D41A2AE55314D31499ECB25848838D4 /* PSCBOnlineClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = E203FA4820121CCEA995CEE6472ADCC6 /* PSCBOnlineClient.swift */; }; + 22C319C0A78167ACBCC7E91EE27598C4 /* PKPaymentToken+Serializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 002D20147E567CDBEB2824598F4716CB /* PKPaymentToken+Serializable.swift */; }; + 232BCBC67C898EC14B4DE9B1B75991B0 /* CardData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B5183B110BD9174B18FFF38079FEB02 /* CardData.swift */; }; + 238AC86B87F44EB8205349B411A8003C /* Serializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60CDD28AD2DDBACF450F4152A9284250 /* Serializable.swift */; }; + 269AAEA2EB76940DD8E64F0855F4CFFE /* JSONDecoders.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE139BB75A732730123A0FF4C0CB9DDB /* JSONDecoders.swift */; }; + 283419E95C403C3E04992A6FA48BB861 /* Payment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2E4E6B8D912C02E7B4AB7E039583EE /* Payment.swift */; }; + 4C712069DB64560C09EB60BC1CF859A0 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 73010CC983E3809BECEE5348DA1BB8C6 /* Foundation.framework */; }; + 4E44BDDA6DD7F1183146048FD6C95764 /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F4E90E25A6D567DEEEBFD1C4F45D15D /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-dummy.m */; }; + 4E473622002751DBAC7B0C7E9659939A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 73010CC983E3809BECEE5348DA1BB8C6 /* Foundation.framework */; }; + 5F5D4DFE18ED0AAD59C04E47975E20F7 /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = A9AC4D172CDD3DDC2E65569FCA727D3A /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 5F5F26822516C9474E7AD01114879F78 /* CustomerData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 270D42B4EB19C66D7A39FF5F2E0C2B43 /* CustomerData.swift */; }; + 62268E998A000DAADD5E9BA5D0F2EE6F /* PaymentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA42370209FF35773B6B0C7281AF1795 /* PaymentHandler.swift */; }; + 71803D0B8BC9EC5BA1199DC05D6554D4 /* PSCBOnline.h in Headers */ = {isa = PBXBuildFile; fileRef = E7C8744A223F4FAA81D82BFA5F315A8B /* PSCBOnline.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 725B068C5E0B4AD9FF917A56C6C53049 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B60541BCD4B1ADE8420D889E1D5CBBC /* String.swift */; }; + 873C6813FE5DB5C3913FFD8FFFEBCAD9 /* PSCBOnline-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 40B63E76D69519F459D9BB11D8F07272 /* PSCBOnline-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95FFF40AE0BB33405D8CC97AB08E4A38 /* Pods-PSCBOnlineSampleTests-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D5C736A601CF7F3441804459B3AAF43 /* Pods-PSCBOnlineSampleTests-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B0C19FDF374038C5043A35C7DBE8946B /* RSAHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F44ECCC00616D9D42F57713A86E39D0 /* RSAHelper.swift */; }; + BC27C4D84BBCE78CA390FAC2DBD57B26 /* Payment+Serializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9531D3ED1E4E9C21F2AE0C026D64EC /* Payment+Serializable.swift */; }; + D18BBEC284892F81901DFD0BD22B07DC /* DigestHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A13187A86E701DCD971C0A528A814C /* DigestHelper.swift */; }; + D319206921562386DADE2F3A04553414 /* RequestWrapper+Serializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF470A0A6171E66AF57ACC8C5BDE637B /* RequestWrapper+Serializable.swift */; }; + D80F58C566E10DF3FAA7AFAAF3BCFD7B /* PSCBAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 766D7A8DB0E03B619C43BBB03FE105E2 /* PSCBAPI.swift */; }; + DAA505D95F5A8AB9E1A058A0AAB37D62 /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6DD36A515037D0DF509ED0DAD6DCABA /* Response.swift */; }; + DF10B5E93A702195E92624CA2ED6240C /* Pods-PSCBOnlineSampleTests-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E8C86CC7D69E4B87AC8AF542842047F /* Pods-PSCBOnlineSampleTests-dummy.m */; }; + E481B26E484F433D679AE3C26A849E50 /* Pods-PSCBOnlineSample-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 7E7C2E55F1D481F4F7D9CCBF9B5CF1F5 /* Pods-PSCBOnlineSample-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E63D1F5BFB39423C3BB893984033AFE6 /* PSCBOnline-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 1BC99FAA9B0A90B70F4E6F59DF91FE33 /* PSCBOnline-dummy.m */; }; + F52AE2CB1161C30AE4C80DE4D4072F3F /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 73010CC983E3809BECEE5348DA1BB8C6 /* Foundation.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 37A4D1638B2507DEFB76997F27A0726A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DEF6F46953D81544729456672DEB8C3B; + remoteInfo = PSCBOnline; + }; + 8CFFCA919167B6A5449BB2BAE5D0AB0F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = E2DB125EC4D0570EFCBF775B921460CD; + remoteInfo = "Pods-PSCBOnlineSample"; + }; + C1D757A49E98C9BC7C322C984D7C7882 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DEF6F46953D81544729456672DEB8C3B; + remoteInfo = PSCBOnline; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 002D20147E567CDBEB2824598F4716CB /* PKPaymentToken+Serializable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "PKPaymentToken+Serializable.swift"; path = "PSCBOnline/Sources/Serializable/PKPaymentToken+Serializable.swift"; sourceTree = ""; }; + 07A5FA728F8AB4366597F4A40E03957E /* Pods-PSCBOnlineSample-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-PSCBOnlineSample-dummy.m"; sourceTree = ""; }; + 0B60541BCD4B1ADE8420D889E1D5CBBC /* String.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = String.swift; path = PSCBOnline/Sources/Extension/String.swift; sourceTree = ""; }; + 1281087CA1E66877C9B17C2545008FB7 /* Pods-PSCBOnlineSampleTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-PSCBOnlineSampleTests.release.xcconfig"; sourceTree = ""; }; + 189FD1CBEB8769A50F58E768DD38964F /* Pods_PSCBOnlineSampleTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_PSCBOnlineSampleTests.framework; path = "Pods-PSCBOnlineSampleTests.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; + 1BC99FAA9B0A90B70F4E6F59DF91FE33 /* PSCBOnline-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "PSCBOnline-dummy.m"; sourceTree = ""; }; + 1D5F78C92E6EE2C0F7BDEFCCE8E31ECA /* Pods-PSCBOnlineSample-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-PSCBOnlineSample-acknowledgements.plist"; sourceTree = ""; }; + 202B688F92A9482C65456D1A8A2D79EB /* Pods-PSCBOnlineSample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-PSCBOnlineSample.release.xcconfig"; sourceTree = ""; }; + 20E243A5EB4698B0A8D41FF295F7DE3B /* Pods_PSCBOnlineSample_PSCBOnlineSampleUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_PSCBOnlineSample_PSCBOnlineSampleUITests.framework; path = "Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; + 21D73543BEAE0D2DDAFE272B955D6ACD /* PSCBOnline.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = PSCBOnline.debug.xcconfig; sourceTree = ""; }; + 25092117CDF47E1018B6D93F4BABB2FA /* PSCBOnline-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "PSCBOnline-Info.plist"; sourceTree = ""; }; + 25654A44F1162BD0643FDB9B306FBA8E /* Pods-PSCBOnlineSampleTests-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-PSCBOnlineSampleTests-acknowledgements.markdown"; sourceTree = ""; }; + 270D42B4EB19C66D7A39FF5F2E0C2B43 /* CustomerData.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CustomerData.swift; path = PSCBOnline/Sources/Models/CustomerData.swift; sourceTree = ""; }; + 2E72013D1FDD07529BFDA1DD586E4A1B /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.modulemap"; sourceTree = ""; }; + 3D5C736A601CF7F3441804459B3AAF43 /* Pods-PSCBOnlineSampleTests-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-PSCBOnlineSampleTests-umbrella.h"; sourceTree = ""; }; + 40B63E76D69519F459D9BB11D8F07272 /* PSCBOnline-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "PSCBOnline-umbrella.h"; sourceTree = ""; }; + 4B5183B110BD9174B18FFF38079FEB02 /* CardData.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CardData.swift; path = PSCBOnline/Sources/Models/CardData.swift; sourceTree = ""; }; + 4C5D750DE4FB9525426662E218C20796 /* PSCBOnline.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = PSCBOnline.modulemap; sourceTree = ""; }; + 55A13187A86E701DCD971C0A528A814C /* DigestHelper.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DigestHelper.swift; path = PSCBOnline/Sources/Helpers/DigestHelper.swift; sourceTree = ""; }; + 5E8C86CC7D69E4B87AC8AF542842047F /* Pods-PSCBOnlineSampleTests-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-PSCBOnlineSampleTests-dummy.m"; sourceTree = ""; }; + 5F44ECCC00616D9D42F57713A86E39D0 /* RSAHelper.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RSAHelper.swift; path = PSCBOnline/Sources/Helpers/RSAHelper.swift; sourceTree = ""; }; + 60CDD28AD2DDBACF450F4152A9284250 /* Serializable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Serializable.swift; path = PSCBOnline/Sources/Serializable/Serializable.swift; sourceTree = ""; }; + 6F4E90E25A6D567DEEEBFD1C4F45D15D /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-dummy.m"; sourceTree = ""; }; + 73010CC983E3809BECEE5348DA1BB8C6 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; + 766D7A8DB0E03B619C43BBB03FE105E2 /* PSCBAPI.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PSCBAPI.swift; path = PSCBOnline/Sources/PSCBAPI.swift; sourceTree = ""; }; + 7A2E4E6B8D912C02E7B4AB7E039583EE /* Payment.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Payment.swift; path = PSCBOnline/Sources/Models/Payment.swift; sourceTree = ""; }; + 7E7C2E55F1D481F4F7D9CCBF9B5CF1F5 /* Pods-PSCBOnlineSample-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-PSCBOnlineSample-umbrella.h"; sourceTree = ""; }; + 862A89FB15BA9064C1382A158D5A7980 /* PSCBOnline.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = PSCBOnline.framework; path = PSCBOnline.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 8A3600CD1CB2AC17C9CED8C3D43A8C5F /* Pods-PSCBOnlineSampleTests.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-PSCBOnlineSampleTests.modulemap"; sourceTree = ""; }; + 9524CC825572DBE819487CC420093666 /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.release.xcconfig"; sourceTree = ""; }; + 964F78D65B05B3AD1ED2298EC279C414 /* PSCBOnline.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = PSCBOnline.release.xcconfig; sourceTree = ""; }; + 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; + 9ECAF26B7470641EFC17878F4A0DDE76 /* Pods-PSCBOnlineSampleTests-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-PSCBOnlineSampleTests-acknowledgements.plist"; sourceTree = ""; }; + A140FBF925A6DA8D8E1AA5E483315ADC /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.debug.xcconfig"; sourceTree = ""; }; + A9AC4D172CDD3DDC2E65569FCA727D3A /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-umbrella.h"; sourceTree = ""; }; + AA42370209FF35773B6B0C7281AF1795 /* PaymentHandler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PaymentHandler.swift; path = PSCBOnline/Sources/PaymentHandler.swift; sourceTree = ""; }; + AB380BCCFA6B59E92BDD788B999AD5CC /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-acknowledgements.markdown"; sourceTree = ""; }; + ACA7FF1BC2F9206FC894DFD51E9D99C4 /* Pods-PSCBOnlineSample-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-PSCBOnlineSample-Info.plist"; sourceTree = ""; }; + B383E2CC977BF6834F6975695A5662AA /* Pods-PSCBOnlineSampleTests-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-PSCBOnlineSampleTests-Info.plist"; sourceTree = ""; }; + BE932C7A83C19DB4677F1EF18894DE69 /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-Info.plist"; sourceTree = ""; }; + BF9531D3ED1E4E9C21F2AE0C026D64EC /* Payment+Serializable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Payment+Serializable.swift"; path = "PSCBOnline/Sources/Serializable/Payment+Serializable.swift"; sourceTree = ""; }; + C07A1F703F1B2073CAF8027CF10AA4B2 /* Pods-PSCBOnlineSample-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-PSCBOnlineSample-acknowledgements.markdown"; sourceTree = ""; }; + C2B81AE6D89092D2AE03A8B4A218EF65 /* Pods-PSCBOnlineSample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-PSCBOnlineSample.debug.xcconfig"; sourceTree = ""; }; + CE1F465A2AAE1FD7D58D02B0C9FCEFD6 /* Pods-PSCBOnlineSampleTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-PSCBOnlineSampleTests.debug.xcconfig"; sourceTree = ""; }; + D302DD80F732D394A15C10B518CA9793 /* RequestWrapper.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RequestWrapper.swift; path = PSCBOnline/Sources/Models/RequestWrapper.swift; sourceTree = ""; }; + DE139BB75A732730123A0FF4C0CB9DDB /* JSONDecoders.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = JSONDecoders.swift; path = PSCBOnline/Sources/Helpers/JSONDecoders.swift; sourceTree = ""; }; + DF17C1ED918A56031196F0EEB88EBAF8 /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-acknowledgements.plist"; sourceTree = ""; }; + DFAF349D467D69A30791324875D7FA6A /* Pods-PSCBOnlineSample.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-PSCBOnlineSample.modulemap"; sourceTree = ""; }; + E203FA4820121CCEA995CEE6472ADCC6 /* PSCBOnlineClient.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PSCBOnlineClient.swift; path = PSCBOnline/Sources/PSCBOnlineClient.swift; sourceTree = ""; }; + E79DF10C490F897BAE9C93E7F98A6542 /* Pods-PSCBOnlineSample-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-PSCBOnlineSample-frameworks.sh"; sourceTree = ""; }; + E7C8744A223F4FAA81D82BFA5F315A8B /* PSCBOnline.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PSCBOnline.h; path = PSCBOnline/PSCBOnline.h; sourceTree = ""; }; + E86BD61CBF59BE5420688D690F7110A8 /* PSCBOnline-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "PSCBOnline-prefix.pch"; sourceTree = ""; }; + F1543AA141881D8151741C70AEAD76AB /* Pods_PSCBOnlineSample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_PSCBOnlineSample.framework; path = "Pods-PSCBOnlineSample.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; + F39478722626866FBDA804BD452BFB3B /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-frameworks.sh"; sourceTree = ""; }; + F6DD36A515037D0DF509ED0DAD6DCABA /* Response.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Response.swift; path = PSCBOnline/Sources/Models/Response.swift; sourceTree = ""; }; + FF470A0A6171E66AF57ACC8C5BDE637B /* RequestWrapper+Serializable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "RequestWrapper+Serializable.swift"; path = "PSCBOnline/Sources/Serializable/RequestWrapper+Serializable.swift"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 763D1C06205F42BEA1A1CFE0D8C8BB71 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F52AE2CB1161C30AE4C80DE4D4072F3F /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + AFDF9A1AB856CC708266174710086055 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 1639B897ACE3EDFA100BC2BA2CDB90F8 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C9FEA5C3739CD0C95377CAD2BF939599 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4E473622002751DBAC7B0C7E9659939A /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + EB93FFB23CC96F7B4EA1D01DD87A4594 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C712069DB64560C09EB60BC1CF859A0 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 139198AD913E78566F9BFFCD6A069456 /* Targets Support Files */ = { + isa = PBXGroup; + children = ( + 7551CD2289AC9C39640091C3502B4225 /* Pods-PSCBOnlineSample */, + 6EC45F85124C022792FF8380CF79D6D1 /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests */, + 61C5E1BFC6D540A650D2AF60840B7ABC /* Pods-PSCBOnlineSampleTests */, + ); + name = "Targets Support Files"; + sourceTree = ""; + }; + 578452D2E740E91742655AC8F1636D1F /* iOS */ = { + isa = PBXGroup; + children = ( + 73010CC983E3809BECEE5348DA1BB8C6 /* Foundation.framework */, + ); + name = iOS; + sourceTree = ""; + }; + 61C5E1BFC6D540A650D2AF60840B7ABC /* Pods-PSCBOnlineSampleTests */ = { + isa = PBXGroup; + children = ( + 8A3600CD1CB2AC17C9CED8C3D43A8C5F /* Pods-PSCBOnlineSampleTests.modulemap */, + 25654A44F1162BD0643FDB9B306FBA8E /* Pods-PSCBOnlineSampleTests-acknowledgements.markdown */, + 9ECAF26B7470641EFC17878F4A0DDE76 /* Pods-PSCBOnlineSampleTests-acknowledgements.plist */, + 5E8C86CC7D69E4B87AC8AF542842047F /* Pods-PSCBOnlineSampleTests-dummy.m */, + B383E2CC977BF6834F6975695A5662AA /* Pods-PSCBOnlineSampleTests-Info.plist */, + 3D5C736A601CF7F3441804459B3AAF43 /* Pods-PSCBOnlineSampleTests-umbrella.h */, + CE1F465A2AAE1FD7D58D02B0C9FCEFD6 /* Pods-PSCBOnlineSampleTests.debug.xcconfig */, + 1281087CA1E66877C9B17C2545008FB7 /* Pods-PSCBOnlineSampleTests.release.xcconfig */, + ); + name = "Pods-PSCBOnlineSampleTests"; + path = "Target Support Files/Pods-PSCBOnlineSampleTests"; + sourceTree = ""; + }; + 6EC45F85124C022792FF8380CF79D6D1 /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests */ = { + isa = PBXGroup; + children = ( + 2E72013D1FDD07529BFDA1DD586E4A1B /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.modulemap */, + AB380BCCFA6B59E92BDD788B999AD5CC /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-acknowledgements.markdown */, + DF17C1ED918A56031196F0EEB88EBAF8 /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-acknowledgements.plist */, + 6F4E90E25A6D567DEEEBFD1C4F45D15D /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-dummy.m */, + F39478722626866FBDA804BD452BFB3B /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-frameworks.sh */, + BE932C7A83C19DB4677F1EF18894DE69 /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-Info.plist */, + A9AC4D172CDD3DDC2E65569FCA727D3A /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-umbrella.h */, + A140FBF925A6DA8D8E1AA5E483315ADC /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.debug.xcconfig */, + 9524CC825572DBE819487CC420093666 /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.release.xcconfig */, + ); + name = "Pods-PSCBOnlineSample-PSCBOnlineSampleUITests"; + path = "Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests"; + sourceTree = ""; + }; + 7551CD2289AC9C39640091C3502B4225 /* Pods-PSCBOnlineSample */ = { + isa = PBXGroup; + children = ( + DFAF349D467D69A30791324875D7FA6A /* Pods-PSCBOnlineSample.modulemap */, + C07A1F703F1B2073CAF8027CF10AA4B2 /* Pods-PSCBOnlineSample-acknowledgements.markdown */, + 1D5F78C92E6EE2C0F7BDEFCCE8E31ECA /* Pods-PSCBOnlineSample-acknowledgements.plist */, + 07A5FA728F8AB4366597F4A40E03957E /* Pods-PSCBOnlineSample-dummy.m */, + E79DF10C490F897BAE9C93E7F98A6542 /* Pods-PSCBOnlineSample-frameworks.sh */, + ACA7FF1BC2F9206FC894DFD51E9D99C4 /* Pods-PSCBOnlineSample-Info.plist */, + 7E7C2E55F1D481F4F7D9CCBF9B5CF1F5 /* Pods-PSCBOnlineSample-umbrella.h */, + C2B81AE6D89092D2AE03A8B4A218EF65 /* Pods-PSCBOnlineSample.debug.xcconfig */, + 202B688F92A9482C65456D1A8A2D79EB /* Pods-PSCBOnlineSample.release.xcconfig */, + ); + name = "Pods-PSCBOnlineSample"; + path = "Target Support Files/Pods-PSCBOnlineSample"; + sourceTree = ""; + }; + AA093FAB843080CA6FDCF0C797075EA6 /* Products */ = { + isa = PBXGroup; + children = ( + F1543AA141881D8151741C70AEAD76AB /* Pods_PSCBOnlineSample.framework */, + 20E243A5EB4698B0A8D41FF295F7DE3B /* Pods_PSCBOnlineSample_PSCBOnlineSampleUITests.framework */, + 189FD1CBEB8769A50F58E768DD38964F /* Pods_PSCBOnlineSampleTests.framework */, + 862A89FB15BA9064C1382A158D5A7980 /* PSCBOnline.framework */, + ); + name = Products; + sourceTree = ""; + }; + B81AC2CB92F684C1CD91F72B61E36A57 /* Pods */ = { + isa = PBXGroup; + children = ( + EA0C1886EE21C2D1AAFE0D4FD45A9633 /* PSCBOnline */, + ); + name = Pods; + sourceTree = ""; + }; + CF1408CF629C7361332E53B88F7BD30C = { + isa = PBXGroup; + children = ( + 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */, + D210D550F4EA176C3123ED886F8F87F5 /* Frameworks */, + B81AC2CB92F684C1CD91F72B61E36A57 /* Pods */, + AA093FAB843080CA6FDCF0C797075EA6 /* Products */, + 139198AD913E78566F9BFFCD6A069456 /* Targets Support Files */, + ); + sourceTree = ""; + }; + D210D550F4EA176C3123ED886F8F87F5 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 578452D2E740E91742655AC8F1636D1F /* iOS */, + ); + name = Frameworks; + sourceTree = ""; + }; + EA0C1886EE21C2D1AAFE0D4FD45A9633 /* PSCBOnline */ = { + isa = PBXGroup; + children = ( + 4B5183B110BD9174B18FFF38079FEB02 /* CardData.swift */, + 270D42B4EB19C66D7A39FF5F2E0C2B43 /* CustomerData.swift */, + 55A13187A86E701DCD971C0A528A814C /* DigestHelper.swift */, + DE139BB75A732730123A0FF4C0CB9DDB /* JSONDecoders.swift */, + 7A2E4E6B8D912C02E7B4AB7E039583EE /* Payment.swift */, + BF9531D3ED1E4E9C21F2AE0C026D64EC /* Payment+Serializable.swift */, + AA42370209FF35773B6B0C7281AF1795 /* PaymentHandler.swift */, + 002D20147E567CDBEB2824598F4716CB /* PKPaymentToken+Serializable.swift */, + 766D7A8DB0E03B619C43BBB03FE105E2 /* PSCBAPI.swift */, + E7C8744A223F4FAA81D82BFA5F315A8B /* PSCBOnline.h */, + E203FA4820121CCEA995CEE6472ADCC6 /* PSCBOnlineClient.swift */, + D302DD80F732D394A15C10B518CA9793 /* RequestWrapper.swift */, + FF470A0A6171E66AF57ACC8C5BDE637B /* RequestWrapper+Serializable.swift */, + F6DD36A515037D0DF509ED0DAD6DCABA /* Response.swift */, + 5F44ECCC00616D9D42F57713A86E39D0 /* RSAHelper.swift */, + 60CDD28AD2DDBACF450F4152A9284250 /* Serializable.swift */, + 0B60541BCD4B1ADE8420D889E1D5CBBC /* String.swift */, + EB6937ADE9E65E7EE5DA76FAA9A9883D /* Support Files */, + ); + name = PSCBOnline; + path = PSCBOnline; + sourceTree = ""; + }; + EB6937ADE9E65E7EE5DA76FAA9A9883D /* Support Files */ = { + isa = PBXGroup; + children = ( + 4C5D750DE4FB9525426662E218C20796 /* PSCBOnline.modulemap */, + 1BC99FAA9B0A90B70F4E6F59DF91FE33 /* PSCBOnline-dummy.m */, + 25092117CDF47E1018B6D93F4BABB2FA /* PSCBOnline-Info.plist */, + E86BD61CBF59BE5420688D690F7110A8 /* PSCBOnline-prefix.pch */, + 40B63E76D69519F459D9BB11D8F07272 /* PSCBOnline-umbrella.h */, + 21D73543BEAE0D2DDAFE272B955D6ACD /* PSCBOnline.debug.xcconfig */, + 964F78D65B05B3AD1ED2298EC279C414 /* PSCBOnline.release.xcconfig */, + ); + name = "Support Files"; + path = "../Target Support Files/PSCBOnline"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 10A0B3BE810B90B3331E0A1B1160F5F5 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + E481B26E484F433D679AE3C26A849E50 /* Pods-PSCBOnlineSample-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 15ACE53FB1DF78A1FBC7224A2605EAB9 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 873C6813FE5DB5C3913FFD8FFFEBCAD9 /* PSCBOnline-umbrella.h in Headers */, + 71803D0B8BC9EC5BA1199DC05D6554D4 /* PSCBOnline.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 1FECB87A87535F89CB0089E1E014D464 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 95FFF40AE0BB33405D8CC97AB08E4A38 /* Pods-PSCBOnlineSampleTests-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F7E458D7301AD39050296493E2D0348A /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 5F5D4DFE18ED0AAD59C04E47975E20F7 /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 3E950DEFA4527CA727310C9636D007AA /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 821254A2699ECCF96583837AE25708A8 /* Build configuration list for PBXNativeTarget "Pods-PSCBOnlineSample-PSCBOnlineSampleUITests" */; + buildPhases = ( + F7E458D7301AD39050296493E2D0348A /* Headers */, + 361683349FA0428DD7AB855D1D99FD9E /* Sources */, + 763D1C06205F42BEA1A1CFE0D8C8BB71 /* Frameworks */, + B61F62746243F8208A240183C99ECEF8 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 1735BEB63CF0373C1F13373BB0453A55 /* PBXTargetDependency */, + ); + name = "Pods-PSCBOnlineSample-PSCBOnlineSampleUITests"; + productName = "Pods-PSCBOnlineSample-PSCBOnlineSampleUITests"; + productReference = 20E243A5EB4698B0A8D41FF295F7DE3B /* Pods_PSCBOnlineSample_PSCBOnlineSampleUITests.framework */; + productType = "com.apple.product-type.framework"; + }; + BFE11DEFCEE6C73419C9155DCB4FE27B /* Pods-PSCBOnlineSampleTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1AA7291B35DB9D48466B0B89E8BE9D3E /* Build configuration list for PBXNativeTarget "Pods-PSCBOnlineSampleTests" */; + buildPhases = ( + 1FECB87A87535F89CB0089E1E014D464 /* Headers */, + DCD0EBE8BE1C4029856C7CEB0E24F562 /* Sources */, + C9FEA5C3739CD0C95377CAD2BF939599 /* Frameworks */, + BE63E0D73A70687DD15F51B07CF5FFE4 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + DF05A256E61F4E23E3D0A8CABBB51337 /* PBXTargetDependency */, + ); + name = "Pods-PSCBOnlineSampleTests"; + productName = "Pods-PSCBOnlineSampleTests"; + productReference = 189FD1CBEB8769A50F58E768DD38964F /* Pods_PSCBOnlineSampleTests.framework */; + productType = "com.apple.product-type.framework"; + }; + DEF6F46953D81544729456672DEB8C3B /* PSCBOnline */ = { + isa = PBXNativeTarget; + buildConfigurationList = CB032F027FD86C5278ED4A043DF09506 /* Build configuration list for PBXNativeTarget "PSCBOnline" */; + buildPhases = ( + 15ACE53FB1DF78A1FBC7224A2605EAB9 /* Headers */, + F134B92D447DB0D2DD3B409493035DA7 /* Sources */, + AFDF9A1AB856CC708266174710086055 /* Frameworks */, + 3F62045B558EED6E6A05A6010BD64C44 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = PSCBOnline; + productName = PSCBOnline; + productReference = 862A89FB15BA9064C1382A158D5A7980 /* PSCBOnline.framework */; + productType = "com.apple.product-type.framework"; + }; + E2DB125EC4D0570EFCBF775B921460CD /* Pods-PSCBOnlineSample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 781F95C22E1C3CABFD280C33D1713725 /* Build configuration list for PBXNativeTarget "Pods-PSCBOnlineSample" */; + buildPhases = ( + 10A0B3BE810B90B3331E0A1B1160F5F5 /* Headers */, + 43AC7C687F5F0452B8784C3FC1A4B1B8 /* Sources */, + EB93FFB23CC96F7B4EA1D01DD87A4594 /* Frameworks */, + 0D002536F6621F125AC3B90A552B8985 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + AFDD0DD939AAA763CD883E9CB481E8B7 /* PBXTargetDependency */, + ); + name = "Pods-PSCBOnlineSample"; + productName = "Pods-PSCBOnlineSample"; + productReference = F1543AA141881D8151741C70AEAD76AB /* Pods_PSCBOnlineSample.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + BFDFE7DC352907FC980B868725387E98 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1100; + LastUpgradeCheck = 1100; + }; + buildConfigurationList = 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */; + compatibilityVersion = "Xcode 10.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = CF1408CF629C7361332E53B88F7BD30C; + productRefGroup = AA093FAB843080CA6FDCF0C797075EA6 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + E2DB125EC4D0570EFCBF775B921460CD /* Pods-PSCBOnlineSample */, + 3E950DEFA4527CA727310C9636D007AA /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests */, + BFE11DEFCEE6C73419C9155DCB4FE27B /* Pods-PSCBOnlineSampleTests */, + DEF6F46953D81544729456672DEB8C3B /* PSCBOnline */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 0D002536F6621F125AC3B90A552B8985 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3F62045B558EED6E6A05A6010BD64C44 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B61F62746243F8208A240183C99ECEF8 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BE63E0D73A70687DD15F51B07CF5FFE4 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 361683349FA0428DD7AB855D1D99FD9E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4E44BDDA6DD7F1183146048FD6C95764 /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 43AC7C687F5F0452B8784C3FC1A4B1B8 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 13C0ACA010CFDF4876F85B5308F50E1B /* Pods-PSCBOnlineSample-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCD0EBE8BE1C4029856C7CEB0E24F562 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DF10B5E93A702195E92624CA2ED6240C /* Pods-PSCBOnlineSampleTests-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F134B92D447DB0D2DD3B409493035DA7 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 232BCBC67C898EC14B4DE9B1B75991B0 /* CardData.swift in Sources */, + 5F5F26822516C9474E7AD01114879F78 /* CustomerData.swift in Sources */, + D18BBEC284892F81901DFD0BD22B07DC /* DigestHelper.swift in Sources */, + 269AAEA2EB76940DD8E64F0855F4CFFE /* JSONDecoders.swift in Sources */, + BC27C4D84BBCE78CA390FAC2DBD57B26 /* Payment+Serializable.swift in Sources */, + 283419E95C403C3E04992A6FA48BB861 /* Payment.swift in Sources */, + 62268E998A000DAADD5E9BA5D0F2EE6F /* PaymentHandler.swift in Sources */, + 22C319C0A78167ACBCC7E91EE27598C4 /* PKPaymentToken+Serializable.swift in Sources */, + D80F58C566E10DF3FAA7AFAAF3BCFD7B /* PSCBAPI.swift in Sources */, + E63D1F5BFB39423C3BB893984033AFE6 /* PSCBOnline-dummy.m in Sources */, + 1D41A2AE55314D31499ECB25848838D4 /* PSCBOnlineClient.swift in Sources */, + D319206921562386DADE2F3A04553414 /* RequestWrapper+Serializable.swift in Sources */, + 08153B37829081830118DC3FDA1C8891 /* RequestWrapper.swift in Sources */, + DAA505D95F5A8AB9E1A058A0AAB37D62 /* Response.swift in Sources */, + B0C19FDF374038C5043A35C7DBE8946B /* RSAHelper.swift in Sources */, + 238AC86B87F44EB8205349B411A8003C /* Serializable.swift in Sources */, + 725B068C5E0B4AD9FF917A56C6C53049 /* String.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 1735BEB63CF0373C1F13373BB0453A55 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = PSCBOnline; + target = DEF6F46953D81544729456672DEB8C3B /* PSCBOnline */; + targetProxy = C1D757A49E98C9BC7C322C984D7C7882 /* PBXContainerItemProxy */; + }; + AFDD0DD939AAA763CD883E9CB481E8B7 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = PSCBOnline; + target = DEF6F46953D81544729456672DEB8C3B /* PSCBOnline */; + targetProxy = 37A4D1638B2507DEFB76997F27A0726A /* PBXContainerItemProxy */; + }; + DF05A256E61F4E23E3D0A8CABBB51337 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "Pods-PSCBOnlineSample"; + target = E2DB125EC4D0570EFCBF775B921460CD /* Pods-PSCBOnlineSample */; + targetProxy = 8CFFCA919167B6A5449BB2BAE5D0AB0F /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 0464917AC5F154BDCBB4474BE7A23D58 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 21D73543BEAE0D2DDAFE272B955D6ACD /* PSCBOnline.debug.xcconfig */; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/PSCBOnline/PSCBOnline-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/PSCBOnline/PSCBOnline-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/PSCBOnline/PSCBOnline.modulemap"; + PRODUCT_MODULE_NAME = PSCBOnline; + PRODUCT_NAME = PSCBOnline; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.3; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 55646993F4CC60DF2F507B91A4388A08 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 202B688F92A9482C65456D1A8A2D79EB /* Pods-PSCBOnlineSample.release.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 5FFC240D4EA061896A728F02F65A45CF /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 964F78D65B05B3AD1ED2298EC279C414 /* PSCBOnline.release.xcconfig */; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/PSCBOnline/PSCBOnline-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/PSCBOnline/PSCBOnline-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/PSCBOnline/PSCBOnline.modulemap"; + PRODUCT_MODULE_NAME = PSCBOnline; + PRODUCT_NAME = PSCBOnline; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.3; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 7EE7A78859F657F6BEFC651185B43192 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "POD_CONFIGURATION_RELEASE=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRIP_INSTALLED_PRODUCT = NO; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + SYMROOT = "${SRCROOT}/../build"; + }; + name = Release; + }; + 99466761BC3F29A556F346FFC2D338E5 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = CE1F465A2AAE1FD7D58D02B0C9FCEFD6 /* Pods-PSCBOnlineSampleTests.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 9D5356CC1E18A79301D31E9692253E7B /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A140FBF925A6DA8D8E1AA5E483315ADC /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + CACF7A9D9D4EDE8DE08794FE6530848A /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9524CC825572DBE819487CC420093666 /* Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.release.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + D299434AB35E7FD6F7921C8EF24742FF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "POD_CONFIGURATION_DEBUG=1", + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRIP_INSTALLED_PRODUCT = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + SYMROOT = "${SRCROOT}/../build"; + }; + name = Debug; + }; + E1B5448BB6A8709785D27C54A790D3CC /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 1281087CA1E66877C9B17C2545008FB7 /* Pods-PSCBOnlineSampleTests.release.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + FB90144B7CF6629C1911ECE17FA90C3B /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = C2B81AE6D89092D2AE03A8B4A218EF65 /* Pods-PSCBOnlineSample.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1AA7291B35DB9D48466B0B89E8BE9D3E /* Build configuration list for PBXNativeTarget "Pods-PSCBOnlineSampleTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 99466761BC3F29A556F346FFC2D338E5 /* Debug */, + E1B5448BB6A8709785D27C54A790D3CC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D299434AB35E7FD6F7921C8EF24742FF /* Debug */, + 7EE7A78859F657F6BEFC651185B43192 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 781F95C22E1C3CABFD280C33D1713725 /* Build configuration list for PBXNativeTarget "Pods-PSCBOnlineSample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FB90144B7CF6629C1911ECE17FA90C3B /* Debug */, + 55646993F4CC60DF2F507B91A4388A08 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 821254A2699ECCF96583837AE25708A8 /* Build configuration list for PBXNativeTarget "Pods-PSCBOnlineSample-PSCBOnlineSampleUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 9D5356CC1E18A79301D31E9692253E7B /* Debug */, + CACF7A9D9D4EDE8DE08794FE6530848A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + CB032F027FD86C5278ED4A043DF09506 /* Build configuration list for PBXNativeTarget "PSCBOnline" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0464917AC5F154BDCBB4474BE7A23D58 /* Debug */, + 5FFC240D4EA061896A728F02F65A45CF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = BFDFE7DC352907FC980B868725387E98 /* Project object */; +} diff --git a/PSCBOnlineSample/Pods/Pods.xcodeproj/xcuserdata/oa.xcuserdatad/xcschemes/PSCBOnline.xcscheme b/PSCBOnlineSample/Pods/Pods.xcodeproj/xcuserdata/oa.xcuserdatad/xcschemes/PSCBOnline.xcscheme new file mode 100644 index 0000000..67deaa5 --- /dev/null +++ b/PSCBOnlineSample/Pods/Pods.xcodeproj/xcuserdata/oa.xcuserdatad/xcschemes/PSCBOnline.xcscheme @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PSCBOnlineSample/Pods/Pods.xcodeproj/xcuserdata/oa.xcuserdatad/xcschemes/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.xcscheme b/PSCBOnlineSample/Pods/Pods.xcodeproj/xcuserdata/oa.xcuserdatad/xcschemes/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.xcscheme new file mode 100644 index 0000000..cb7281d --- /dev/null +++ b/PSCBOnlineSample/Pods/Pods.xcodeproj/xcuserdata/oa.xcuserdatad/xcschemes/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PSCBOnlineSample/Pods/Pods.xcodeproj/xcuserdata/oa.xcuserdatad/xcschemes/Pods-PSCBOnlineSample.xcscheme b/PSCBOnlineSample/Pods/Pods.xcodeproj/xcuserdata/oa.xcuserdatad/xcschemes/Pods-PSCBOnlineSample.xcscheme new file mode 100644 index 0000000..d71c670 --- /dev/null +++ b/PSCBOnlineSample/Pods/Pods.xcodeproj/xcuserdata/oa.xcuserdatad/xcschemes/Pods-PSCBOnlineSample.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PSCBOnlineSample/Pods/Pods.xcodeproj/xcuserdata/oa.xcuserdatad/xcschemes/Pods-PSCBOnlineSampleTests.xcscheme b/PSCBOnlineSample/Pods/Pods.xcodeproj/xcuserdata/oa.xcuserdatad/xcschemes/Pods-PSCBOnlineSampleTests.xcscheme new file mode 100644 index 0000000..2ccf1d7 --- /dev/null +++ b/PSCBOnlineSample/Pods/Pods.xcodeproj/xcuserdata/oa.xcuserdatad/xcschemes/Pods-PSCBOnlineSampleTests.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PSCBOnlineSample/Pods/Pods.xcodeproj/xcuserdata/oa.xcuserdatad/xcschemes/xcschememanagement.plist b/PSCBOnlineSample/Pods/Pods.xcodeproj/xcuserdata/oa.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..f0d1868 --- /dev/null +++ b/PSCBOnlineSample/Pods/Pods.xcodeproj/xcuserdata/oa.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,31 @@ + + + + + SchemeUserState + + PSCBOnline.xcscheme + + isShown + + + Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.xcscheme + + isShown + + + Pods-PSCBOnlineSample.xcscheme + + isShown + + + Pods-PSCBOnlineSampleTests.xcscheme + + isShown + + + + SuppressBuildableAutocreation + + + diff --git a/PSCBOnlineSample/Pods/Target Support Files/PSCBOnline/PSCBOnline-Info.plist b/PSCBOnlineSample/Pods/Target Support Files/PSCBOnline/PSCBOnline-Info.plist new file mode 100644 index 0000000..2243fe6 --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/PSCBOnline/PSCBOnline-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/PSCBOnlineSample/Pods/Target Support Files/PSCBOnline/PSCBOnline-dummy.m b/PSCBOnlineSample/Pods/Target Support Files/PSCBOnline/PSCBOnline-dummy.m new file mode 100644 index 0000000..7158a46 --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/PSCBOnline/PSCBOnline-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_PSCBOnline : NSObject +@end +@implementation PodsDummy_PSCBOnline +@end diff --git a/PSCBOnlineSample/Pods/Target Support Files/PSCBOnline/PSCBOnline-prefix.pch b/PSCBOnlineSample/Pods/Target Support Files/PSCBOnline/PSCBOnline-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/PSCBOnline/PSCBOnline-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/PSCBOnlineSample/Pods/Target Support Files/PSCBOnline/PSCBOnline-umbrella.h b/PSCBOnlineSample/Pods/Target Support Files/PSCBOnline/PSCBOnline-umbrella.h new file mode 100644 index 0000000..fa1c265 --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/PSCBOnline/PSCBOnline-umbrella.h @@ -0,0 +1,17 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + +#import "PSCBOnline.h" + +FOUNDATION_EXPORT double PSCBOnlineVersionNumber; +FOUNDATION_EXPORT const unsigned char PSCBOnlineVersionString[]; + diff --git a/PSCBOnlineSample/Pods/Target Support Files/PSCBOnline/PSCBOnline.debug.xcconfig b/PSCBOnlineSample/Pods/Target Support Files/PSCBOnline/PSCBOnline.debug.xcconfig new file mode 100644 index 0000000..db7e6c1 --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/PSCBOnline/PSCBOnline.debug.xcconfig @@ -0,0 +1,12 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/PSCBOnline +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/PSCBOnline +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/PSCBOnlineSample/Pods/Target Support Files/PSCBOnline/PSCBOnline.modulemap b/PSCBOnlineSample/Pods/Target Support Files/PSCBOnline/PSCBOnline.modulemap new file mode 100644 index 0000000..0ce6c09 --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/PSCBOnline/PSCBOnline.modulemap @@ -0,0 +1,6 @@ +framework module PSCBOnline { + umbrella header "PSCBOnline-umbrella.h" + + export * + module * { export * } +} diff --git a/PSCBOnlineSample/Pods/Target Support Files/PSCBOnline/PSCBOnline.release.xcconfig b/PSCBOnlineSample/Pods/Target Support Files/PSCBOnline/PSCBOnline.release.xcconfig new file mode 100644 index 0000000..db7e6c1 --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/PSCBOnline/PSCBOnline.release.xcconfig @@ -0,0 +1,12 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/PSCBOnline +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/PSCBOnline +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-Info.plist b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-Info.plist new file mode 100644 index 0000000..2243fe6 --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-acknowledgements.markdown b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-acknowledgements.markdown new file mode 100644 index 0000000..87be8da --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-acknowledgements.markdown @@ -0,0 +1,8 @@ +# Acknowledgements +This application makes use of the following third party libraries: + +## PSCBOnline + +LICENSE BE HERE + +Generated by CocoaPods - https://cocoapods.org diff --git a/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-acknowledgements.plist b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-acknowledgements.plist new file mode 100644 index 0000000..9f7ea6c --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-acknowledgements.plist @@ -0,0 +1,40 @@ + + + + + PreferenceSpecifiers + + + FooterText + This application makes use of the following third party libraries: + Title + Acknowledgements + Type + PSGroupSpecifier + + + FooterText + LICENSE BE HERE + + License + PSCB + Title + PSCBOnline + Type + PSGroupSpecifier + + + FooterText + Generated by CocoaPods - https://cocoapods.org + Title + + Type + PSGroupSpecifier + + + StringsTable + Acknowledgements + Title + Acknowledgements + + diff --git a/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-dummy.m b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-dummy.m new file mode 100644 index 0000000..587e461 --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Pods_PSCBOnlineSample_PSCBOnlineSampleUITests : NSObject +@end +@implementation PodsDummy_Pods_PSCBOnlineSample_PSCBOnlineSampleUITests +@end diff --git a/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-frameworks-Debug-input-files.xcfilelist b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-frameworks-Debug-input-files.xcfilelist new file mode 100644 index 0000000..c83c9e2 --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-frameworks-Debug-input-files.xcfilelist @@ -0,0 +1,2 @@ +${PODS_ROOT}/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-frameworks.sh +${BUILT_PRODUCTS_DIR}/PSCBOnline/PSCBOnline.framework \ No newline at end of file diff --git a/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-frameworks-Debug-output-files.xcfilelist b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-frameworks-Debug-output-files.xcfilelist new file mode 100644 index 0000000..c0dee12 --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-frameworks-Debug-output-files.xcfilelist @@ -0,0 +1 @@ +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PSCBOnline.framework \ No newline at end of file diff --git a/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-frameworks-Release-input-files.xcfilelist b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-frameworks-Release-input-files.xcfilelist new file mode 100644 index 0000000..c83c9e2 --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-frameworks-Release-input-files.xcfilelist @@ -0,0 +1,2 @@ +${PODS_ROOT}/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-frameworks.sh +${BUILT_PRODUCTS_DIR}/PSCBOnline/PSCBOnline.framework \ No newline at end of file diff --git a/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-frameworks-Release-output-files.xcfilelist b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-frameworks-Release-output-files.xcfilelist new file mode 100644 index 0000000..c0dee12 --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-frameworks-Release-output-files.xcfilelist @@ -0,0 +1 @@ +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PSCBOnline.framework \ No newline at end of file diff --git a/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-frameworks.sh b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-frameworks.sh new file mode 100644 index 0000000..356ff54 --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-frameworks.sh @@ -0,0 +1,185 @@ +#!/bin/sh +set -e +set -u +set -o pipefail + +function on_error { + echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" +} +trap 'on_error $LINENO' ERR + +if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then + # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy + # frameworks to, so exit 0 (signalling the script phase was successful). + exit 0 +fi + +echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" +mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + +COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" +SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" +BCSYMBOLMAP_DIR="BCSymbolMaps" + + +# This protects against multiple targets copying the same framework dependency at the same time. The solution +# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html +RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") + +# Copies and strips a vendored framework +install_framework() +{ + if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then + local source="${BUILT_PRODUCTS_DIR}/$1" + elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then + local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" + elif [ -r "$1" ]; then + local source="$1" + fi + + local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + + if [ -L "${source}" ]; then + echo "Symlinked..." + source="$(readlink "${source}")" + fi + + if [ -d "${source}/${BCSYMBOLMAP_DIR}" ]; then + # Locate and install any .bcsymbolmaps if present, and remove them from the .framework before the framework is copied + find "${source}/${BCSYMBOLMAP_DIR}" -name "*.bcsymbolmap"|while read f; do + echo "Installing $f" + install_bcsymbolmap "$f" "$destination" + rm "$f" + done + rmdir "${source}/${BCSYMBOLMAP_DIR}" + fi + + # Use filter instead of exclude so missing patterns don't throw errors. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" + + local basename + basename="$(basename -s .framework "$1")" + binary="${destination}/${basename}.framework/${basename}" + + if ! [ -r "$binary" ]; then + binary="${destination}/${basename}" + elif [ -L "${binary}" ]; then + echo "Destination binary is symlinked..." + dirname="$(dirname "${binary}")" + binary="${dirname}/$(readlink "${binary}")" + fi + + # Strip invalid architectures so "fat" simulator / device frameworks work on device + if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then + strip_invalid_archs "$binary" + fi + + # Resign the code if required by the build settings to avoid unstable apps + code_sign_if_enabled "${destination}/$(basename "$1")" + + # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. + if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then + local swift_runtime_libs + swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) + for lib in $swift_runtime_libs; do + echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" + rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" + code_sign_if_enabled "${destination}/${lib}" + done + fi +} +# Copies and strips a vendored dSYM +install_dsym() { + local source="$1" + warn_missing_arch=${2:-true} + if [ -r "$source" ]; then + # Copy the dSYM into the targets temp dir. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" + + local basename + basename="$(basename -s .dSYM "$source")" + binary_name="$(ls "$source/Contents/Resources/DWARF")" + binary="${DERIVED_FILES_DIR}/${basename}.dSYM/Contents/Resources/DWARF/${binary_name}" + + # Strip invalid architectures from the dSYM. + if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then + strip_invalid_archs "$binary" "$warn_missing_arch" + fi + if [[ $STRIP_BINARY_RETVAL == 0 ]]; then + # Move the stripped file into its final destination. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.dSYM" "${DWARF_DSYM_FOLDER_PATH}" + else + # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. + touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.dSYM" + fi + fi +} + +# Used as a return value for each invocation of `strip_invalid_archs` function. +STRIP_BINARY_RETVAL=0 + +# Strip invalid architectures +strip_invalid_archs() { + binary="$1" + warn_missing_arch=${2:-true} + # Get architectures for current target binary + binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" + # Intersect them with the architectures we are building for + intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" + # If there are no archs supported by this binary then warn the user + if [[ -z "$intersected_archs" ]]; then + if [[ "$warn_missing_arch" == "true" ]]; then + echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." + fi + STRIP_BINARY_RETVAL=1 + return + fi + stripped="" + for arch in $binary_archs; do + if ! [[ "${ARCHS}" == *"$arch"* ]]; then + # Strip non-valid architectures in-place + lipo -remove "$arch" -output "$binary" "$binary" + stripped="$stripped $arch" + fi + done + if [[ "$stripped" ]]; then + echo "Stripped $binary of architectures:$stripped" + fi + STRIP_BINARY_RETVAL=0 +} + +# Copies the bcsymbolmap files of a vendored framework +install_bcsymbolmap() { + local bcsymbolmap_path="$1" + local destination="${BUILT_PRODUCTS_DIR}" + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" +} + +# Signs a framework with the provided identity +code_sign_if_enabled() { + if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then + # Use the current code_sign_identity + echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" + local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" + + if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + code_sign_cmd="$code_sign_cmd &" + fi + echo "$code_sign_cmd" + eval "$code_sign_cmd" + fi +} + +if [[ "$CONFIGURATION" == "Debug" ]]; then + install_framework "${BUILT_PRODUCTS_DIR}/PSCBOnline/PSCBOnline.framework" +fi +if [[ "$CONFIGURATION" == "Release" ]]; then + install_framework "${BUILT_PRODUCTS_DIR}/PSCBOnline/PSCBOnline.framework" +fi +if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + wait +fi diff --git a/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-umbrella.h b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-umbrella.h new file mode 100644 index 0000000..0e44011 --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double Pods_PSCBOnlineSample_PSCBOnlineSampleUITestsVersionNumber; +FOUNDATION_EXPORT const unsigned char Pods_PSCBOnlineSample_PSCBOnlineSampleUITestsVersionString[]; + diff --git a/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.debug.xcconfig b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.debug.xcconfig new file mode 100644 index 0000000..413d190 --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.debug.xcconfig @@ -0,0 +1,14 @@ +ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PSCBOnline" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PSCBOnline/PSCBOnline.framework/Headers" +LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +OTHER_LDFLAGS = $(inherited) -framework "PSCBOnline" +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.modulemap b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.modulemap new file mode 100644 index 0000000..7e8c2f5 --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.modulemap @@ -0,0 +1,6 @@ +framework module Pods_PSCBOnlineSample_PSCBOnlineSampleUITests { + umbrella header "Pods-PSCBOnlineSample-PSCBOnlineSampleUITests-umbrella.h" + + export * + module * { export * } +} diff --git a/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.release.xcconfig b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.release.xcconfig new file mode 100644 index 0000000..413d190 --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests/Pods-PSCBOnlineSample-PSCBOnlineSampleUITests.release.xcconfig @@ -0,0 +1,14 @@ +ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PSCBOnline" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PSCBOnline/PSCBOnline.framework/Headers" +LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +OTHER_LDFLAGS = $(inherited) -framework "PSCBOnline" +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-Info.plist b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-Info.plist new file mode 100644 index 0000000..2243fe6 --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-acknowledgements.markdown b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-acknowledgements.markdown new file mode 100644 index 0000000..87be8da --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-acknowledgements.markdown @@ -0,0 +1,8 @@ +# Acknowledgements +This application makes use of the following third party libraries: + +## PSCBOnline + +LICENSE BE HERE + +Generated by CocoaPods - https://cocoapods.org diff --git a/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-acknowledgements.plist b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-acknowledgements.plist new file mode 100644 index 0000000..9f7ea6c --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-acknowledgements.plist @@ -0,0 +1,40 @@ + + + + + PreferenceSpecifiers + + + FooterText + This application makes use of the following third party libraries: + Title + Acknowledgements + Type + PSGroupSpecifier + + + FooterText + LICENSE BE HERE + + License + PSCB + Title + PSCBOnline + Type + PSGroupSpecifier + + + FooterText + Generated by CocoaPods - https://cocoapods.org + Title + + Type + PSGroupSpecifier + + + StringsTable + Acknowledgements + Title + Acknowledgements + + diff --git a/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-dummy.m b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-dummy.m new file mode 100644 index 0000000..9a2f487 --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Pods_PSCBOnlineSample : NSObject +@end +@implementation PodsDummy_Pods_PSCBOnlineSample +@end diff --git a/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-frameworks-Debug-input-files.xcfilelist b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-frameworks-Debug-input-files.xcfilelist new file mode 100644 index 0000000..7d3e641 --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-frameworks-Debug-input-files.xcfilelist @@ -0,0 +1,2 @@ +${PODS_ROOT}/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-frameworks.sh +${BUILT_PRODUCTS_DIR}/PSCBOnline/PSCBOnline.framework \ No newline at end of file diff --git a/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-frameworks-Debug-output-files.xcfilelist b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-frameworks-Debug-output-files.xcfilelist new file mode 100644 index 0000000..c0dee12 --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-frameworks-Debug-output-files.xcfilelist @@ -0,0 +1 @@ +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PSCBOnline.framework \ No newline at end of file diff --git a/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-frameworks-Release-input-files.xcfilelist b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-frameworks-Release-input-files.xcfilelist new file mode 100644 index 0000000..7d3e641 --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-frameworks-Release-input-files.xcfilelist @@ -0,0 +1,2 @@ +${PODS_ROOT}/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-frameworks.sh +${BUILT_PRODUCTS_DIR}/PSCBOnline/PSCBOnline.framework \ No newline at end of file diff --git a/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-frameworks-Release-output-files.xcfilelist b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-frameworks-Release-output-files.xcfilelist new file mode 100644 index 0000000..c0dee12 --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-frameworks-Release-output-files.xcfilelist @@ -0,0 +1 @@ +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PSCBOnline.framework \ No newline at end of file diff --git a/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-frameworks.sh b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-frameworks.sh new file mode 100644 index 0000000..356ff54 --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-frameworks.sh @@ -0,0 +1,185 @@ +#!/bin/sh +set -e +set -u +set -o pipefail + +function on_error { + echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" +} +trap 'on_error $LINENO' ERR + +if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then + # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy + # frameworks to, so exit 0 (signalling the script phase was successful). + exit 0 +fi + +echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" +mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + +COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" +SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" +BCSYMBOLMAP_DIR="BCSymbolMaps" + + +# This protects against multiple targets copying the same framework dependency at the same time. The solution +# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html +RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") + +# Copies and strips a vendored framework +install_framework() +{ + if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then + local source="${BUILT_PRODUCTS_DIR}/$1" + elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then + local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" + elif [ -r "$1" ]; then + local source="$1" + fi + + local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + + if [ -L "${source}" ]; then + echo "Symlinked..." + source="$(readlink "${source}")" + fi + + if [ -d "${source}/${BCSYMBOLMAP_DIR}" ]; then + # Locate and install any .bcsymbolmaps if present, and remove them from the .framework before the framework is copied + find "${source}/${BCSYMBOLMAP_DIR}" -name "*.bcsymbolmap"|while read f; do + echo "Installing $f" + install_bcsymbolmap "$f" "$destination" + rm "$f" + done + rmdir "${source}/${BCSYMBOLMAP_DIR}" + fi + + # Use filter instead of exclude so missing patterns don't throw errors. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" + + local basename + basename="$(basename -s .framework "$1")" + binary="${destination}/${basename}.framework/${basename}" + + if ! [ -r "$binary" ]; then + binary="${destination}/${basename}" + elif [ -L "${binary}" ]; then + echo "Destination binary is symlinked..." + dirname="$(dirname "${binary}")" + binary="${dirname}/$(readlink "${binary}")" + fi + + # Strip invalid architectures so "fat" simulator / device frameworks work on device + if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then + strip_invalid_archs "$binary" + fi + + # Resign the code if required by the build settings to avoid unstable apps + code_sign_if_enabled "${destination}/$(basename "$1")" + + # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. + if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then + local swift_runtime_libs + swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) + for lib in $swift_runtime_libs; do + echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" + rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" + code_sign_if_enabled "${destination}/${lib}" + done + fi +} +# Copies and strips a vendored dSYM +install_dsym() { + local source="$1" + warn_missing_arch=${2:-true} + if [ -r "$source" ]; then + # Copy the dSYM into the targets temp dir. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" + + local basename + basename="$(basename -s .dSYM "$source")" + binary_name="$(ls "$source/Contents/Resources/DWARF")" + binary="${DERIVED_FILES_DIR}/${basename}.dSYM/Contents/Resources/DWARF/${binary_name}" + + # Strip invalid architectures from the dSYM. + if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then + strip_invalid_archs "$binary" "$warn_missing_arch" + fi + if [[ $STRIP_BINARY_RETVAL == 0 ]]; then + # Move the stripped file into its final destination. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.dSYM" "${DWARF_DSYM_FOLDER_PATH}" + else + # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. + touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.dSYM" + fi + fi +} + +# Used as a return value for each invocation of `strip_invalid_archs` function. +STRIP_BINARY_RETVAL=0 + +# Strip invalid architectures +strip_invalid_archs() { + binary="$1" + warn_missing_arch=${2:-true} + # Get architectures for current target binary + binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" + # Intersect them with the architectures we are building for + intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" + # If there are no archs supported by this binary then warn the user + if [[ -z "$intersected_archs" ]]; then + if [[ "$warn_missing_arch" == "true" ]]; then + echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." + fi + STRIP_BINARY_RETVAL=1 + return + fi + stripped="" + for arch in $binary_archs; do + if ! [[ "${ARCHS}" == *"$arch"* ]]; then + # Strip non-valid architectures in-place + lipo -remove "$arch" -output "$binary" "$binary" + stripped="$stripped $arch" + fi + done + if [[ "$stripped" ]]; then + echo "Stripped $binary of architectures:$stripped" + fi + STRIP_BINARY_RETVAL=0 +} + +# Copies the bcsymbolmap files of a vendored framework +install_bcsymbolmap() { + local bcsymbolmap_path="$1" + local destination="${BUILT_PRODUCTS_DIR}" + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" +} + +# Signs a framework with the provided identity +code_sign_if_enabled() { + if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then + # Use the current code_sign_identity + echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" + local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" + + if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + code_sign_cmd="$code_sign_cmd &" + fi + echo "$code_sign_cmd" + eval "$code_sign_cmd" + fi +} + +if [[ "$CONFIGURATION" == "Debug" ]]; then + install_framework "${BUILT_PRODUCTS_DIR}/PSCBOnline/PSCBOnline.framework" +fi +if [[ "$CONFIGURATION" == "Release" ]]; then + install_framework "${BUILT_PRODUCTS_DIR}/PSCBOnline/PSCBOnline.framework" +fi +if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + wait +fi diff --git a/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-umbrella.h b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-umbrella.h new file mode 100644 index 0000000..74b48b4 --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double Pods_PSCBOnlineSampleVersionNumber; +FOUNDATION_EXPORT const unsigned char Pods_PSCBOnlineSampleVersionString[]; + diff --git a/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample.debug.xcconfig b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample.debug.xcconfig new file mode 100644 index 0000000..413d190 --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample.debug.xcconfig @@ -0,0 +1,14 @@ +ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PSCBOnline" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PSCBOnline/PSCBOnline.framework/Headers" +LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +OTHER_LDFLAGS = $(inherited) -framework "PSCBOnline" +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample.modulemap b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample.modulemap new file mode 100644 index 0000000..5c51921 --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample.modulemap @@ -0,0 +1,6 @@ +framework module Pods_PSCBOnlineSample { + umbrella header "Pods-PSCBOnlineSample-umbrella.h" + + export * + module * { export * } +} diff --git a/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample.release.xcconfig b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample.release.xcconfig new file mode 100644 index 0000000..413d190 --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSample/Pods-PSCBOnlineSample.release.xcconfig @@ -0,0 +1,14 @@ +ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PSCBOnline" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PSCBOnline/PSCBOnline.framework/Headers" +LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +OTHER_LDFLAGS = $(inherited) -framework "PSCBOnline" +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests-Info.plist b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests-Info.plist new file mode 100644 index 0000000..2243fe6 --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests-acknowledgements.markdown b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests-acknowledgements.markdown new file mode 100644 index 0000000..102af75 --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests-acknowledgements.markdown @@ -0,0 +1,3 @@ +# Acknowledgements +This application makes use of the following third party libraries: +Generated by CocoaPods - https://cocoapods.org diff --git a/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests-acknowledgements.plist b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests-acknowledgements.plist new file mode 100644 index 0000000..7acbad1 --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests-acknowledgements.plist @@ -0,0 +1,29 @@ + + + + + PreferenceSpecifiers + + + FooterText + This application makes use of the following third party libraries: + Title + Acknowledgements + Type + PSGroupSpecifier + + + FooterText + Generated by CocoaPods - https://cocoapods.org + Title + + Type + PSGroupSpecifier + + + StringsTable + Acknowledgements + Title + Acknowledgements + + diff --git a/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests-dummy.m b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests-dummy.m new file mode 100644 index 0000000..e4df30a --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Pods_PSCBOnlineSampleTests : NSObject +@end +@implementation PodsDummy_Pods_PSCBOnlineSampleTests +@end diff --git a/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests-umbrella.h b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests-umbrella.h new file mode 100644 index 0000000..5e00cc6 --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double Pods_PSCBOnlineSampleTestsVersionNumber; +FOUNDATION_EXPORT const unsigned char Pods_PSCBOnlineSampleTestsVersionString[]; + diff --git a/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests.debug.xcconfig b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests.debug.xcconfig new file mode 100644 index 0000000..22b5a1f --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests.debug.xcconfig @@ -0,0 +1,11 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PSCBOnline" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PSCBOnline/PSCBOnline.framework/Headers" +OTHER_LDFLAGS = $(inherited) -framework "PSCBOnline" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests.modulemap b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests.modulemap new file mode 100644 index 0000000..014c306 --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests.modulemap @@ -0,0 +1,6 @@ +framework module Pods_PSCBOnlineSampleTests { + umbrella header "Pods-PSCBOnlineSampleTests-umbrella.h" + + export * + module * { export * } +} diff --git a/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests.release.xcconfig b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests.release.xcconfig new file mode 100644 index 0000000..22b5a1f --- /dev/null +++ b/PSCBOnlineSample/Pods/Target Support Files/Pods-PSCBOnlineSampleTests/Pods-PSCBOnlineSampleTests.release.xcconfig @@ -0,0 +1,11 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PSCBOnline" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PSCBOnline/PSCBOnline.framework/Headers" +OTHER_LDFLAGS = $(inherited) -framework "PSCBOnline" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/PSCBOnlineTests/Helpers/DigestHelperTests.swift b/PSCBOnlineTests/Helpers/DigestHelperTests.swift new file mode 100644 index 0000000..e666314 --- /dev/null +++ b/PSCBOnlineTests/Helpers/DigestHelperTests.swift @@ -0,0 +1,57 @@ +// +// DigestHelperTests.swift +// PSCB-OOS-iOSTests +// +// Created by OA on 28.10.2020. +// + +import Foundation +import XCTest + +@testable import PSCBOnline + +final class DigestHelperTests: XCTestCase { + + func testSha256_sanity() { + // given: + let str = "" + + // when: + let sha = DigestHelper.sha256String(str) + + // then: + XCTAssertEqual(sha, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") + } + + func testSha256_mockSignature() { + // given: + let json = """ + {"payment":{"orderId":"XC-33345","showOrderId":"XC-33345","recurrentable":false,"amount":150.25},"marketPlace":"47607","cardData":"ewogICJwYXltZW50TWV0aG9kIiA6IHsKICAgICJkaXNwbGF5TmFtZSIgOiAiU2ltdWxhdGVkIEluc3RydW1lbnQiLAogICAgIm5ldHdvcmsiIDogIlZpc2EiLAogICAgInR5cGUiIDogImRlYml0IgogIH0sCiAgInRyYW5zYWN0aW9uSWRlbnRpZmllciIgOiAiU2ltdWxhdGVkIElkZW50aWZpZXIiLAogICJwYXltZW50RGF0YSIgOiBudWxsCn0="} + """ + + let salt = "111111" + + // when: + let sha = DigestHelper.sha256String("\(json)\(salt)") + + // then: + XCTAssertEqual(sha, "adbb177db83523ead0e77244afec2d943a3767af401424df56fc951b8bab02e3") + } + + func testMock() { + // given: + let json = """ + {"payment":{"orderId":"XC-33345","showOrderId":"XC-33345","recurrentable":false,"amount":150.25},"marketPlace":"47607","cardData":""} + """ + + let salt = "111111" + + // when: + let sha = DigestHelper.sha256String("\(json)\(salt)") + + // then: + XCTAssertEqual(sha, "8c7c620a70f470a343ff3e9e81496f895a91ffbd6c8a2fef819625794f204908") + + } + +} diff --git a/PSCBOnlineTests/Info.plist b/PSCBOnlineTests/Info.plist new file mode 100644 index 0000000..64d65ca --- /dev/null +++ b/PSCBOnlineTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/PSCBOnlineTests/Mocks/PassKitMocks.swift b/PSCBOnlineTests/Mocks/PassKitMocks.swift new file mode 100644 index 0000000..c0712b9 --- /dev/null +++ b/PSCBOnlineTests/Mocks/PassKitMocks.swift @@ -0,0 +1,118 @@ +// +// PassKitMocks.swift +// PSCB-OOS-iOSTests +// +// Created by OA on 22.10.2020. +// +#if canImport(PassKit) + +import Foundation +import PassKit + +// MARK: - Mock payment +internal class MockPayment: PKPayment { + + override var token: PKPaymentToken { + return self._token + } + + override var billingContact: PKContact? { + return self._billingContact + } + + override var shippingContact: PKContact? { + return self._shippingContact + } + + override var shippingMethod: PKShippingMethod? { + return self._shippingMethod + } + + let _token: MockPaymentToken + let _billingContact: PKContact? + let _shippingContact: PKContact? + let _shippingMethod: PKShippingMethod? + + init(token: MockPaymentToken, billingContact: PKContact? = nil, shippingContact: PKContact? = nil, shippingMethod: PKShippingMethod? = nil) { + self._token = token + self._billingContact = billingContact + self._shippingContact = shippingContact + self._shippingMethod = shippingMethod + } + + static func createPayment() -> PKPayment { + let token = MockPaymentToken.createToken() + + return MockPayment(token: token as! MockPaymentToken) + } +} + +// MARK: - Mock payment token +internal class MockPaymentToken: PKPaymentToken { + + override var paymentMethod: PKPaymentMethod { + return self._paymentMethod + } + + override var transactionIdentifier: String { + return self._transactionIdentifier + } + + override var paymentData: Data { + return self._paymentData + } + + let _paymentMethod: MockPaymentMethod + let _transactionIdentifier = "2C7B4FD8-7D1B-49D4-8AB5-AFE3CC79265A" + let _paymentData: Data = { + let dict: [AnyHashable: AnyHashable] = [ + "version": "EC_v1", + "data": "....", + "signature": "...." + ] + + return try! JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted) + }() + + init(paymentMethod: MockPaymentMethod) { + self._paymentMethod = paymentMethod + } + + static func createToken() -> PKPaymentToken { + return MockPaymentToken(paymentMethod: MockPaymentMethod.createMethod() as! MockPaymentMethod) + } + +} + +// MARK: - Mock payment method +internal class MockPaymentMethod: PKPaymentMethod { + + override var displayName: String? { + return self._displayName + } + + override var network: PKPaymentNetwork? { + return self._network + } + + override var type: PKPaymentMethodType { + return self._type + } + + let _displayName: String? + let _network: PKPaymentNetwork? + let _type: PKPaymentMethodType + + init(type: PKPaymentMethodType, network: PKPaymentNetwork? = nil, displayName: String? = nil) { + self._network = network + self._type = type + self._displayName = displayName + } + + static func createMethod() -> PKPaymentMethod { + return MockPaymentMethod(type: .debit, network: .visa, displayName: "PSCB Visa") + } + +} + +#endif diff --git a/PSCBOnlineTests/PSCBOnlineTests.swift b/PSCBOnlineTests/PSCBOnlineTests.swift new file mode 100644 index 0000000..e92d8c3 --- /dev/null +++ b/PSCBOnlineTests/PSCBOnlineTests.swift @@ -0,0 +1,33 @@ +// +// PSCB_OOS_iOSTests.swift +// PSCB-OOS-iOSTests +// +// Created by OA on 16.10.2020. +// + +import XCTest +@testable import PSCBOnline + +class PSCB_OOS_iOSTests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testPerformanceExample() throws { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/PSCBOnlineTests/Serializable/PKPaymentToken+SerializableTests.swift b/PSCBOnlineTests/Serializable/PKPaymentToken+SerializableTests.swift new file mode 100644 index 0000000..4531d49 --- /dev/null +++ b/PSCBOnlineTests/Serializable/PKPaymentToken+SerializableTests.swift @@ -0,0 +1,109 @@ +// +// PKPaymentToken+SerializableTests.swift +// PSCB-OOS-iOSTests +// +// Created by Antonov Ilia on 22.10.2020. +// +#if canImport(PassKit) + +import Foundation +import PassKit +import XCTest + +@testable import PSCBOnline + +final class PKPaymentTokenSerializableTests: XCTestCase { + + // MARK: - Test .serializeToJSON() + func testPaymentTokenSerialization() { + // given: + let token = MockPaymentToken.createToken() + + // when: + let json = token.serializeToJSON() + + print("JSON: >>") + print(json) + + // then: + XCTAssertEqual(json["transactionIdentifier"], "2C7B4FD8-7D1B-49D4-8AB5-AFE3CC79265A") + + // and: + let method = json["paymentMethod"] + let network = (method as! [String: AnyHashable])["network"] ?? "N/A" as AnyHashable + XCTAssertEqual(network, "Visa" as AnyHashable) + } + + // MARK: - Test .serializeToData() + func testSerializeToData() { + // given: + let token = MockPaymentToken.createToken() + + // expect: + XCTAssertNoThrow(try token.serializeToData()) + } + + // MARK: - Test .serializeToString() + func testSerializeToString() { + // given: + let token = MockPaymentToken.createToken() + + // expect: + XCTAssertNoThrow(try token.serializeToString()) + + // when: + let json = try! token.serializeToString() + + // debug: + print("JSON: >>") + print(json) + } + + // MARK: - Test PKPaymentToken.toCryptogramString() + func testToCryptogramString() { + // given: + let token = MockPaymentToken.createToken() + + // expect: + XCTAssertNoThrow(try token.toCryptogramString()) + + // when: + let string = try! token.toCryptogramString() + + // debug: + print("Cryptogram String: >> \(string)") + + // then: + XCTAssertFalse(string.isEmpty) + + // when: + let data = Data(base64Encoded: string, options: Data.Base64DecodingOptions(rawValue: 0))! + + // debug: + print("Decoded >>") + print(String(data: data, encoding: .utf8)!) + + // and: + let decoder = JSONDecoder() + let output = try! decoder.decode(PaymentTokenResult.self, from: data) + + // then: + XCTAssertEqual(output.version, "EC_v1") + XCTAssertEqual(output.signature, "....") + } + +} + +internal struct PaymentTokenResult: Decodable { + let data: String + let version: String + let signature: String +} + +internal struct PaymentMethodResult: Decodable { + let displayName: String + let network: String + let type: String +} + +#endif diff --git a/PSCBOnlineTests/Serializable/PKPaymentTokenTests.swift b/PSCBOnlineTests/Serializable/PKPaymentTokenTests.swift new file mode 100644 index 0000000..012e92a --- /dev/null +++ b/PSCBOnlineTests/Serializable/PKPaymentTokenTests.swift @@ -0,0 +1,35 @@ +// +// PKPaymentTokenTests.swift +// PSCB-OOS-iOSTests +// +// Created by Antonov Ilia on 22.10.2020. +// +#if canImport(PassKit) + +import Foundation +import PassKit +import XCTest + +@testable import PSCBOnline + +final class PKPaymentTokenTests: XCTestCase { + + func testToCryptogramString() { + // given: + let token = MockPaymentToken.createToken() + + // expect: No exception is thrown on creating a string + XCTAssertNoThrow(try token.toCryptogramString()) + + // when: + let cryptogram = try! token.toCryptogramString() + + print(">> Cryptogram string: \(cryptogram)") + + // then: + XCTAssertFalse(cryptogram.isEmpty) + } + +} + +#endif diff --git a/PSCBOnlineTests/Sources/Models/CardDataTests.swift b/PSCBOnlineTests/Sources/Models/CardDataTests.swift new file mode 100644 index 0000000..aebe925 --- /dev/null +++ b/PSCBOnlineTests/Sources/Models/CardDataTests.swift @@ -0,0 +1,47 @@ +// +// CardDataTests.swift +// PSCB-OOS-iOSTests +// +// Created by Antonov Ilia on 26.10.2020. +// + +import Foundation +import XCTest + +@testable import PSCBOnline + +final class CardDataTests: XCTestCase { + + internal static let testCard = CardData( + pan: "4200111122223333", + expiryYear: 2017, + expiryMonth: 5, + cvCode: "123", + cardholder: "IVANOV IVAN" + )! + + func testSanity() { + // given: + let card = Self.testCard + + // expect: + XCTAssertEqual("05", card.getExpMonthString()) + XCTAssertEqual("17", card.getExpYearString()) + } + + func testCryptogram() { + // given: + let card = Self.testCard + + // expect: + XCTAssertNoThrow(try card.toCryptgramString()) + + // when: + let string = try! card.toCryptgramString() + + // then: + XCTAssertFalse(string.isEmpty) + XCTAssertTrue(string.count == 88) + } + +} diff --git a/PSCBOnlineTests/Sources/Models/PaymentTests.swift b/PSCBOnlineTests/Sources/Models/PaymentTests.swift new file mode 100644 index 0000000..6e05cfa --- /dev/null +++ b/PSCBOnlineTests/Sources/Models/PaymentTests.swift @@ -0,0 +1,36 @@ +// +// PaymentTests.swift +// PSCB-OOS-iOSTests +// +// Created by OA on 12.10.2020. +// + +import Foundation +import XCTest + +@testable import PSCBOnline + +final class PaymentTests: XCTestCase { + + func testEncoding() { + // given: + let payment = Payment.example + let encoder = JSONEncoder() + + // expect: + XCTAssertNoThrow(try encoder.encode(payment)) + + // when: + let json = try? encoder.encode(payment) + let string = String(data: json ?? Data(), encoding: .utf8)! + + // then: + print("----") + print(string) + + XCTAssertEqual(string, """ + {"amount":150,"orderId":"\(payment.orderId)","recurrentable":false,"details":"Wonderful warm socks","customerAccount":"ID-12345","showOrderId":"\(payment.showOrderId!)","customerPhone":"+7 900 000 00 00","customerEmail":"foo@bar.com","customerComment":"By tomorrow please"} + """) + } + +} diff --git a/PSCBOnlineTests/Sources/Models/ResponseTests.swift b/PSCBOnlineTests/Sources/Models/ResponseTests.swift new file mode 100644 index 0000000..295b3b0 --- /dev/null +++ b/PSCBOnlineTests/Sources/Models/ResponseTests.swift @@ -0,0 +1,117 @@ +// +// ResponseTests.swift +// PSCB-OOS-iOSTests +// +// Created by Antonov Ilia on 23.10.2020. +// + +import Foundation +import XCTest + +@testable import PSCBOnline + +final class ResponseTests: XCTestCase { + + private let mockResponse: String = { + return """ + { + "status":"STATUS_SUCCESS", + "requestId":"PROD-141024-103898B", + "payment":{ + "orderId":"1111103", + "showOrderId":"1111103", + "paymentId":"8067739", + "account":"shpa", + "amount":1350.89, + "state":"sent", + "marketPlace":47607, + "paymentMethod":"ac-shpa", + "stateDate":"2014-10-24T10:38:40.501+04:00" + }, + "PaReq":"eJydk01TgzAQhu/+CgbPkpBihU6IU60fVWmr4jjjxcnAjkXLRwN12n/", + "order":"22040229", + "TermUrl":"https://oos.pscb.ru/merchantApi/confirmShpa", + "URL":"https://acs.multicarta.ru/PaReqVISA.jsp", + "MD":"NTJmZTFjMWEtNjQ0Mi00ODQ2LTlkMjMtZDU0MzdkYzA0N2Fk" + } + """ + }() + + private let anotherMockResponse: String = { + """ + {"status":"STATUS_SUCCESS","requestId":"DEMO-201102-1652-2BE12","payment":{"orderId":"XC-33353","showOrderId":"XC-33353","paymentId":"229359620","account":null,"amount":155.35,"state":"end","marketPlace":12345,"paymentMethod":"ac-shpa","stateDate":"2020-11-02T16:52:16.563+03:00"},"description":"Payment processed successfully"} + """ + }() + + private let dateFormatter: DateFormatter = { + let fmtString = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ" + let formatter = DateFormatter() + + formatter.dateFormat = fmtString + return formatter + }() + + func testDecodeResponse() { + // given: + let responseData = mockResponse.data(using: .utf8)! + let decoder = JSONDecoders.iso8601DateAwareDecoder() + + // expect: + XCTAssertNoThrow(try decoder.decode(Response.self, from: responseData)) + + // when: + let response = try! decoder.decode(Response.self, from: responseData) + + // then: + XCTAssertEqual(response.requestId, "PROD-141024-103898B") + XCTAssertEqual(response.status, .success) + + // when: + let payment = response.payment! + + // then: + XCTAssertEqual(payment.state, .sent) + XCTAssertEqual(payment.amount, Decimal(1350.89)) + XCTAssertEqual(payment.marketPlace, 47607) + XCTAssertEqual(payment.orderId, "1111103") + XCTAssertEqual(payment.showOrderId, "1111103") + XCTAssertEqual(payment.stateDate, dateFormatter.date(from: "2014-10-24T10:38:40.501+04:00")) + XCTAssertEqual(payment.paymentId, "8067739") + + // when: + let acquiringData = response.acquiringData + + // then: + XCTAssertEqual(acquiringData?.order, "22040229") + } + + func testDecodeResponse_another() { + // given: + let responseData = anotherMockResponse.data(using: .utf8)! + let decoder = JSONDecoders.iso8601DateAwareDecoder() + + // expect: + XCTAssertNoThrow(try decoder.decode(Response.self, from: responseData)) + + // when: + let response = try! decoder.decode(Response.self, from: responseData) + + // then: + XCTAssertEqual(response.requestId, "DEMO-201102-1652-2BE12") + XCTAssertEqual(response.status, .success) + XCTAssertEqual(response.description, "Payment processed successfully") + + // when: + let payment = response.payment! + + // then: + XCTAssertEqual(payment.state, .end) + XCTAssertEqual(payment.amount, Decimal(155.35)) + XCTAssertEqual(payment.marketPlace, 12345) + XCTAssertEqual(payment.orderId, "XC-33353") + XCTAssertEqual(payment.showOrderId, "XC-33353") + XCTAssertEqual(payment.stateDate, dateFormatter.date(from: "2020-11-02T16:52:16.563+03:00")) + XCTAssertEqual(payment.paymentId, "229359620") + } + +} diff --git a/PSCBOnlineTests/Sources/OOSAPIClientTests.swift b/PSCBOnlineTests/Sources/OOSAPIClientTests.swift new file mode 100644 index 0000000..0a11011 --- /dev/null +++ b/PSCBOnlineTests/Sources/OOSAPIClientTests.swift @@ -0,0 +1,114 @@ +// +// OOSAPIClientTests.swift +// PSCB-OOS-iOSTests +// +// Created by Antonov Ilia on 21.10.2020. +// +#if canImport(PassKit) + +import Foundation +import PassKit +import XCTest + +@testable import PSCBOnline + +final class OOSAPIClientTests: XCTestCase { + + static let marketPlaceId = "1234567" + + let subject = PSCBOnlineClient(environment: .sandbox, marketPlaceId: marketPlaceId, signingKey: "foobar") + + // MARK: - OOS Payment request for PKPayment + func testCreatingRequestWrapper_PKPayment() { + // given: + let amount = Decimal(100) + let orderId = "123" + let payment = Self.createMockPayment() + + // expect: No exception thrown + XCTAssertNoThrow(try subject.makeRequestWithPayment(payment: payment, amount: amount, orderId: orderId)) + + // when: + let request = try! subject.makeRequestWithPayment(payment: payment, amount: amount, orderId: orderId) + + // then: Values equals + XCTAssertEqual(amount, request.payment.amount) + XCTAssertEqual(orderId, request.payment.orderId) + XCTAssertEqual(Self.marketPlaceId, request.marketPlaceId) + + print("CardData : \(String(describing: request.cardData))") + + // and: Card data is ApplePayCardData + // XCTAssertTrue(request.cardData) + } + + // MARK: - OOS Payment request for CardData + func testCreatingRequestWrapper_CardData() { + // given: + let amount = Decimal(100) + let orderId = "123" + let payment = Payment(amount: amount, orderId: orderId) + let card = Self.createMockCard() + + // expect: No exception thrown + XCTAssertNoThrow(try subject.makeRequestWithCardData(card: card, payment: payment)) + + // when: + let request = try! subject.makeRequestWithCardData(card: card, payment: payment) + + // then: Values equals + XCTAssertEqual(amount, request.payment.amount) + XCTAssertEqual(orderId, request.payment.orderId) + XCTAssertEqual(Self.marketPlaceId, request.marketPlaceId) + + print("CardData : \(String(describing: request.cardData))") + + // and: Card data is ApplePayCardData + // XCTAssertTrue(request.cardData) + } + + // MARK: - Checking JSON from encoded data + func testEncodingPaymentRequest() { + // givent: + let amount = Decimal(100) + let orderId = "1234" + let payment = Self.createMockPayment() + + // and: + let request = try! subject.makeRequestWithPayment(payment: payment, amount: amount, orderId: orderId) + + // expect: + XCTAssertNoThrow(try request.serializeToString()) + + // when: + let jsonString = try! request.serializeToString() + + print("JSON Request: \(jsonString)") + + // then: + XCTAssertTrue(!jsonString.isEmpty) + } + + + private static func createMockPayment() -> PKPayment { + let paymentMethod = MockPaymentMethod(type: .debit, network: .visa, displayName: "PSCB Visa") + let token = MockPaymentToken(paymentMethod: paymentMethod) + + return MockPayment(token: token) + } + + private static func createMockCard() -> CardData { + let current = Date() + let calendar = Calendar.current + + return CardData( + pan: "4761120010000492", + expiryYear: calendar.component(.year, from: current) + 1, + expiryMonth: calendar.component(.month, from: current), + cvCode: "533" + )! + } + +} + +#endif diff --git a/README.md b/README.md new file mode 100644 index 0000000..94dbb5d --- /dev/null +++ b/README.md @@ -0,0 +1,217 @@ +# ПСКБ Платежи iOS SDK + +[![Platform](https://img.shields.io/badge/Support-iOS%2010.0+-brightgreen.svg)](https://img.shields.io/badge/Support-iOS%2010.3+-brightgreen.svg) + +Библиотека является дополнением к API системы интернет-эквайринга [ПСКБ "Платежи"](https://online.pscb.ru) +и позволяет подключить приём платежей по картам в мобильных приложениях iOS с минимальными усилиями. + +## Возможности + +На текущий момент библиотека поддерживает: + + - Apple Pay + - Оплата картами + +## Подключение зависимостей + +1. Установите CocoaPods 1.10.0 или выше + +```zsh +gem install cocoapods +``` +[Официальная документация по установке CocoaPods](https://guides.cocoapods.org/using/getting-started.html) + +2.  Создайте в своём приложении `Podfile` + +Это также можно сделать при помощи команды `pod init` , находясь в директории своего проекта. (В таком случае будет создан `Podfile` с настройками по умолчанию) + +3. Добавьте зависимости в `Podfile` + +```ruby +platform :ios, '10.0' + +target '' do + use_frameworks! + + pod 'PSCBOnline', :git => "https://git2dev.pscb.ru/pscb-dev/pscbonline-ios", :tag => "1.0.0" + +end +``` + +> `` - Название проекта вашего приложения в XCode + +4. Выполните команду `pod install` + +## Интеграция + +1. Для работы с библиотекой импортируйте зависимости в нужный файл проекта: + +```swift +import PSCBOnline +``` + +2. Создайте экземпляр `PSCBOnlineClient` с вашими настройками: + +```swift +let apiClient = PSCBOnlineClient( + environment: .sandbox, + marketPlaceId: "", + signingKey: "" +) +``` + +> `environment` -  окружение, в рамках которого библиотека взаимодействует с сервисом. `.sandbox` - тестовое окружение; `.production` - продуктовое. +> `Your MarketPlace ID` - ваш идентификатор в системе ПСКБ-Онлайн. +> `Your Signing Key` - ваш ключ подписи запросов к системе. + +3. Создайте экземпляр `Payment` + +```swift +let payment = Payment(amount: Decimal(1000.00), orderId: "Order-ID") +``` +> `amount` - сумма в рублях. (_На текущий момент другая валюта не поддерживается._) +> `orderId` - уникальный идентификатор заказа в рамках магазина. + +_Для детальной информации, какие параметры принимает смотрите документацию `Payment`._ + +---- + +Дальнейшая интеграция отличается от способа оплаты. + +## Доступные способы оплаты + +Сейчас в SDK доступна оплата: + + - Банковкой кратой + - Apple Pay + + Для их настройки реализации смотрите дальше. + +### Приём оплаты банковскимикартами + +1. Для приёма оплаты картами нужно создать экземпляр класса `CardData`. + +```swift +let card = CardData( + pan: "4761349750010326", + expiryYear: 2022, + expiryMonth: 12, + cvCode: "851" +) +``` + +2. Создать токен запроса, используя экземпляр `PSCBOnlineClient`, созданный ранее. + +```swift +let request = try! client.makeRequestWithCardData(card: card, payment: payment) +``` + +3. Отправить токен запроса на сервер ПСКБ-Онлайн. + +```swift +// Send request to backend +client.send(request, responseHandler: { (error, response) in + guard nil == error && nil != response else { + print("Error sending request: \(String(describing: error))") + return + } + + print("Successful request:") + print(response!) +}) +``` + +> В случае успешного выполнения запроса, `response` будет содержать информацию о принятом платеже, его ID, состояние и прочие данные. + +### Приём оплаты Apple Pay + +Для приёма платежей через Apple Pay вы должны зарегестрировать Merchant ID в Apple. + +Помимо `merchant ID` необходимо настроить сертификат обработки запросов (`Payment Processing Certificate`) и передать его ПСКБ-Онлайн. Этим сертификатом Apple будет шифровать данные банковских карт перед отправкой на сервер ПСКБ-Онлайн. + +Все пререквизиты описаны на сайте [официальной документации Apple](https://developer.apple.com/documentation/passkit/apple_pay/setting_up_apple_pay_requirements). + +Также вы можете ознакомиться сподробной инструкцией на сайте документации [ПСКБ Онлайн](https://docs.pscb.ru/oos/advanced.html#dopolnitelnye-opcii-apple-pay). + +--- + +После выполнения пререквизитов, в коде необходимо: + +1. Импортировать `PassKit`: + +```swift +import PassKit +``` + +2. Создать экземпляр класса `PKPaymentAuthorizationController` и настроить делегирующий класс `PKPaymentAuthorizationControllerDelegate`: + +Пример: + +```swift +import PassKit +import PSCBOnline + +class ApplePayHandler: NSObject { + + public func handleApplePay(payment: Payment) { + // Позиции оплаты для представления пользователя + let items = [PKPaymentSummaryItem(label: "Shoes", amount: NSDecimalNumber(intergerLiteral: 1000))] + + // Запрос на оплату для PKPaymentAuthorizationController + let paymentRequest = PSCBAPI.makePaymentRequest(merchantId: "", items: []) + + // Вызов модуля оплаты + let authorizationController = PKPaymentAuthorizationController(paymentRequest: paymentRequest) + + authorizationController?.delegate = self + authorizationController?.present(completion: { presented in + if presented { + print("Controller presented") + } else { + print("Controller could not be presented") + } + }) + } +} + +extension ApplePayHandler: PKPaymentAuthorizationControllerDelegate { + + public func paymentAuthorizationController(_ controller: PKPaymentAuthorizationController, + didAuthorizePayment payment: PKPayment, + handler completion: @escaping (PKPaymentAuthorizationResult) -> Void) { + + // 1. + // Вызывается после авторизации платежа плательщиком биометрическими данными или паролем + } + + public func paymentAuthorizationControllerDidFinish(_ controller: PKPaymentAuthorizationController) { + // 2. + // Вызывается после того, как платёж совершён или пользователь закрыл модуль оплаты. + } + +} +``` + +> `` - Ваш идентификатор мерчанта. + +3. Далее в примере выше в п.1 и п.2 реализовать необходимую логику отправки запроса на сервер ПСКБ-Онлайн. + Для пункта 1 - это создание токена запроса (используя `client.makeRequestWithPayment(pkPayment, pscbPayment)` и отправка его на сервер: + +```swift +// Request token +let request = try! client.makeRequestWithPayment(pkPayment: pkPayment, pscbPayment: payment) + +// Sending request to backend +client.send(request, responseHandler: { (error, response) in + if (error == nil && response != nil) { + completion(PKPaymentAuthorizationResult(status: .success, errors: nil)) + } else { + print("Error sending payment: \(String(describing: error))") + completion(PKPaymentAuthorizationResult(status: .failure, errors: nil)) + } +}) +``` + +4. А для пункта 2 - обработка закрытия модуля оплаты. + +## Описание классов и параметров diff --git a/Untitled.xcworkspace/contents.xcworkspacedata b/Untitled.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..cdda1a9 --- /dev/null +++ b/Untitled.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,16 @@ + + + + + + + + + + + diff --git a/Untitled.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Untitled.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Untitled.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Untitled.xcworkspace/xcuserdata/oa.xcuserdatad/UserInterfaceState.xcuserstate b/Untitled.xcworkspace/xcuserdata/oa.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..dbe9dc4c1ae95974d4d2866e270362b0242dfc73 GIT binary patch literal 57657 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