diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index fd52f01a..b12eb09a 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -10,7 +10,7 @@ jobs:
lint:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Run linting
uses: norio-nomura/action-swiftlint@3.2.1
with:
@@ -21,7 +21,7 @@ jobs:
env:
BUILD_WRAPPER_OUT_DIR: buildwrapper # Directory where build-wrapper output will be placed
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Run xcodebuild with tests
run: xcodebuild -project dev.xcodeproj/ -scheme OmiseSDK -derivedDataPath Build/ -destination 'platform=iOS Simulator,name=iPhone 11,OS=16.2' -enableCodeCoverage YES clean build test CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO
@@ -33,7 +33,7 @@ jobs:
run: xattr -w com.apple.xcode.CreatedByBuildSystem true ./build
- name: Upload coverage report
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v3
with:
path: sonarqube-generic-coverage.xml
retention-days: 5 # Artifact will be available only for 5 days.
@@ -51,7 +51,7 @@ jobs:
needs: test
steps:
- name: Checkout repository on branch
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
with:
ref: ${{ github.HEAD_REF }}
fetch-depth: 0
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 00000000..5bdb7e50
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "editor.inlayHints.fontSize": 12
+}
\ No newline at end of file
diff --git a/ExampleApp.xcodeproj/project.pbxproj b/ExampleApp.xcodeproj/project.pbxproj
index 1a4bba02..be84f8ad 100644
--- a/ExampleApp.xcodeproj/project.pbxproj
+++ b/ExampleApp.xcodeproj/project.pbxproj
@@ -453,7 +453,7 @@
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/omise/omise-ios.git";
requirement = {
- branch = feature/v5.0.0_improvements;
+ branch = "feature/MIT-2251_v5_AdaptableTableView";
kind = branch;
};
};
diff --git a/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
index fbcb06fd..ff610e98 100644
--- a/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
+++ b/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -5,8 +5,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/omise/omise-ios.git",
"state" : {
- "branch" : "feature/v5.0.0_improvements",
- "revision" : "6ff5a91c43fb163949ffc0369eb4a471fe0edba6"
+ "branch" : "feature/MIT-2251_v5_AdaptableTableView",
+ "revision" : "bf2e8840edf4cd937fdb1daf2d8e1d3a030b96bb"
}
}
],
diff --git a/ExampleApp/AppDelegate.swift b/ExampleApp/AppDelegate.swift
index 81918099..6838358f 100644
--- a/ExampleApp/AppDelegate.swift
+++ b/ExampleApp/AppDelegate.swift
@@ -6,6 +6,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window?.tintColor = UIColor(named: "App Tint") ?? UIColor.blue
+
return true
}
}
diff --git a/ExampleApp/Models/LocalConfig.swift b/ExampleApp/Models/LocalConfig.swift
index d815c643..d9de56ab 100644
--- a/ExampleApp/Models/LocalConfig.swift
+++ b/ExampleApp/Models/LocalConfig.swift
@@ -23,16 +23,16 @@ struct LocalConfig: Codable {
devVaultBaseURL = nil
devApiBaseURL = nil
}
-
- var env: Environment {
+
+ var configuration: Configuration? {
if devMode,
let vaultBaseURLString = devVaultBaseURL,
let vaultBaseURL = URL(string: vaultBaseURLString),
let apiBaseURLString = devApiBaseURL,
let apiBaseURL = URL(string: apiBaseURLString) {
- return .dev(vaultURL: vaultBaseURL, apiURL: apiBaseURL)
+ return .init(vaultURL: vaultBaseURL, apiURL: apiBaseURL)
} else {
- return .production
+ return nil
}
}
diff --git a/ExampleApp/Models/Tools.swift b/ExampleApp/Models/Tools.swift
index 601cf8dc..ef4bf806 100644
--- a/ExampleApp/Models/Tools.swift
+++ b/ExampleApp/Models/Tools.swift
@@ -4,50 +4,60 @@ import OmiseSDK
struct PaymentPreset {
var paymentAmount: Int64
var paymentCurrency: Currency
- var allowedPaymentMethods: [SourceTypeValue]
+ var allowedPaymentMethods: [SourceType]
+ static let allPreset = PaymentPreset(
+ paymentAmount: 5_000_00,
+ paymentCurrency: .thb,
+ allowedPaymentMethods: SourceType.allCases
+ )
+
static let thailandPreset = PaymentPreset(
paymentAmount: 5_000_00,
paymentCurrency: .thb,
- allowedPaymentMethods: PaymentCreatorController.thailandDefaultAvailableSourceMethods
+ allowedPaymentMethods: SourceType.availableByDefaultInThailand
)
-
+
static let japanPreset = PaymentPreset(
paymentAmount: 5_000,
paymentCurrency: .jpy,
- allowedPaymentMethods: PaymentCreatorController.japanDefaultAvailableSourceMethods
+ allowedPaymentMethods: SourceType.availableByDefaultInJapan
)
static let singaporePreset = PaymentPreset(
paymentAmount: 5_000_00,
paymentCurrency: .sgd,
- allowedPaymentMethods: PaymentCreatorController.singaporeDefaultAvailableSourceMethods
+ allowedPaymentMethods: SourceType.availableByDefaultSingapore
)
static let malaysiaPreset = PaymentPreset(
paymentAmount: 5_000_00,
paymentCurrency: .myr,
- allowedPaymentMethods: PaymentCreatorController.malaysiaDefaultAvailableSourceMethods
+ allowedPaymentMethods: SourceType.availableByDefaultMalaysia
)
}
class Tool: NSObject {
+ static let allPaymentAmount: Int64 = PaymentPreset.allPreset.paymentAmount
+ static let allPaymentCurrency: String = PaymentPreset.allPreset.paymentCurrency.code
+ static let allAllowedPaymentMethods: [SourceType] = PaymentPreset.allPreset.allowedPaymentMethods
+
static let thailandPaymentAmount: Int64 = PaymentPreset.thailandPreset.paymentAmount
static let thailandPaymentCurrency: String = PaymentPreset.thailandPreset.paymentCurrency.code
- static let thailandAllowedPaymentMethods: [SourceTypeValue] = PaymentPreset.thailandPreset.allowedPaymentMethods
+ static let thailandAllowedPaymentMethods: [SourceType] = PaymentPreset.thailandPreset.allowedPaymentMethods
static let japanPaymentAmount: Int64 = PaymentPreset.japanPreset.paymentAmount
static let japanPaymentCurrency: String = PaymentPreset.japanPreset.paymentCurrency.code
- static let japanAllowedPaymentMethods: [SourceTypeValue] = PaymentPreset.japanPreset.allowedPaymentMethods
+ static let japanAllowedPaymentMethods: [SourceType] = PaymentPreset.japanPreset.allowedPaymentMethods
static let singaporePaymentAmount: Int64 = PaymentPreset.singaporePreset.paymentAmount
static let singaporePaymentCurrency: String = PaymentPreset.singaporePreset.paymentCurrency.code
- static let singaporeAllowedPaymentMethods: [SourceTypeValue] = PaymentPreset.singaporePreset.allowedPaymentMethods
+ static let singaporeAllowedPaymentMethods: [SourceType] = PaymentPreset.singaporePreset.allowedPaymentMethods
static let malaysiaPaymentAmount: Int64 = PaymentPreset.malaysiaPreset.paymentAmount
static let malaysiaPaymentCurrency: String = PaymentPreset.malaysiaPreset.paymentCurrency.code
- static let malaysiaAllowedPaymentMethods: [SourceTypeValue] = PaymentPreset.malaysiaPreset.allowedPaymentMethods
+ static let malaysiaAllowedPaymentMethods: [SourceType] = PaymentPreset.malaysiaPreset.allowedPaymentMethods
static func imageWith(size: CGSize, color: UIColor) -> UIImage? {
return Tool.imageWith(size: size) { (context) in
diff --git a/ExampleApp/Resources/Main.storyboard b/ExampleApp/Resources/Main.storyboard
index 18cd0100..c14fb6e6 100644
--- a/ExampleApp/Resources/Main.storyboard
+++ b/ExampleApp/Resources/Main.storyboard
@@ -30,18 +30,18 @@
-
+
-
+
-
@@ -121,8 +102,8 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
-
+
+
@@ -426,14 +407,14 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
-
+
-
+
-
+
-
-
+
-
+
-
-
+
+
@@ -477,14 +458,14 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
-
+
-
-
+
+
@@ -494,14 +475,14 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
-
+
-
-
+
+
@@ -511,14 +492,14 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
-
-
+
+
@@ -528,14 +509,14 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
-
-
+
+
@@ -545,14 +526,14 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
-
-
+
+
@@ -562,14 +543,14 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
-
-
+
+
@@ -579,14 +560,14 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
-
-
+
+
@@ -596,14 +577,14 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
-
-
+
+
@@ -613,14 +594,14 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
-
-
+
+
@@ -630,14 +611,14 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
-
-
+
+
@@ -647,14 +628,14 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
-
-
+
+
@@ -664,14 +645,14 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
-
-
+
+
@@ -681,14 +662,14 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
-
-
+
+
@@ -698,14 +679,14 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
-
-
+
+
@@ -715,14 +696,14 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
-
-
+
+
@@ -732,14 +713,14 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
-
-
+
+
@@ -749,7 +730,7 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
@@ -766,7 +747,7 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
@@ -783,7 +764,7 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
@@ -800,7 +781,7 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
@@ -817,7 +798,7 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
@@ -834,7 +815,7 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
@@ -851,7 +832,7 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
@@ -868,7 +849,7 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
@@ -885,7 +866,7 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
@@ -902,7 +883,7 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
@@ -919,7 +900,7 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
@@ -936,7 +917,7 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
@@ -953,7 +934,7 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
@@ -970,7 +951,7 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
@@ -987,7 +968,7 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
@@ -1004,7 +985,7 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
@@ -1021,7 +1002,7 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
@@ -1038,7 +1019,7 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
@@ -1055,7 +1036,7 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
+
@@ -1071,6 +1052,23 @@ You can present via either Storyboard or Code and you can see the code in the Ex
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1134,6 +1132,7 @@ You can present via either Storyboard or Code and you can see the code in the Ex
+
@@ -1163,147 +1162,6 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -1321,24 +1179,6 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -1357,23 +1197,6 @@ You can present via either Storyboard or Code and you can see the code in the Ex
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/ExampleApp/Views/BaseViewController.swift b/ExampleApp/Views/BaseViewController.swift
index 6ebf9e7c..b5572e28 100644
--- a/ExampleApp/Views/BaseViewController.swift
+++ b/ExampleApp/Views/BaseViewController.swift
@@ -9,20 +9,14 @@
import UIKit
import OmiseSDK
-enum CodePathMode: Int {
- case storyboard = 0
- case code = 1
-}
-
class BaseViewController: UIViewController {
@IBOutlet private var modeChooser: UISegmentedControl!
- var currentCodePathMode: CodePathMode = .code
var paymentAmount: Int64 = 0
var paymentCurrencyCode: String = ""
var usesCapabilityDataForPaymentMethods = false
- var allowedPaymentMethods: [SourceTypeValue] = []
+ var allowedPaymentMethods: [SourceType] = []
required init?(coder: NSCoder) {
self.usesCapabilityDataForPaymentMethods = true
@@ -42,12 +36,6 @@ class BaseViewController: UIViewController {
updateUIColors()
- modeChooser.selectedSegmentIndex = CodePathMode.code.rawValue
-
- #if INTERNAL_TEST
- modeChooser.isEnabled = false
- #endif
-
let localeCountryCode: String? = {
if #available(iOS 16, *) {
return Locale.current.region?.identifier
@@ -183,13 +171,4 @@ class BaseViewController: UIViewController {
self.usesCapabilityDataForPaymentMethods = settingViewController.usesCapabilityDataForPaymentMethods
self.allowedPaymentMethods = Array(settingViewController.allowedPaymentMethods)
}
-
- @IBAction private func codePathModeChangedHandler(_ sender: UISegmentedControl) {
- if sender.selectedSegmentIndex == CodePathMode.code.rawValue {
- self.currentCodePathMode = .code
- } else {
- self.currentCodePathMode = .storyboard
- }
- }
-
}
diff --git a/ExampleApp/Views/CustomCreditCardFormViewController.swift b/ExampleApp/Views/CustomCreditCardFormViewController.swift
index 0840a965..d7e74443 100644
--- a/ExampleApp/Views/CustomCreditCardFormViewController.swift
+++ b/ExampleApp/Views/CustomCreditCardFormViewController.swift
@@ -2,24 +2,21 @@ import UIKit
import OmiseSDK
// swiftlint:disable:next type_name
-protocol CustomCreditCardFormViewControllerDelegate: AnyObject {
+protocol CustomCreditCardPaymentControllerDelegate: AnyObject {
/// Delegate method for receiving token data when card tokenization succeeds.
/// - parameter token: `OmiseToken` instance created from supplied credit card data.
/// - seealso: [Tokens API](https://www.omise.co/tokens-api)
- func creditCardFormViewController(_ controller: CustomCreditCardFormViewController, didSucceedWithToken token: Token)
+ func creditCardFormViewController(_ controller: CustomCreditCardPaymentController, didSucceedWithToken token: Token)
/// Delegate method for receiving error information when card tokenization failed.
/// This allows you to have fine-grained control over error handling when setting
/// `handleErrors` to `false`.
/// - parameter error: The error that occurred during tokenization.
/// - note: This delegate method will *never* be called if `handleErrors` property is set to `true`.
- func creditCardFormViewController(_ controller: CustomCreditCardFormViewController, didFailWithError error: Error)
+ func creditCardFormViewController(_ controller: CustomCreditCardPaymentController, didFailWithError error: Error)
}
-class CustomCreditCardFormViewController: UIViewController {
-
- let omiseClient = Client(publicKey: LocalConfig.default.publicKey)
-
+class CustomCreditCardPaymentController: UIViewController {
@IBOutlet private var cardNumberField: CardNumberTextField!
@IBOutlet private var cardNameField: CardNameTextField!
@IBOutlet private var cardExpiryField: CardExpiryDateTextField!
@@ -36,90 +33,88 @@ class CustomCreditCardFormViewController: UIViewController {
@IBOutlet private var doneButton: UIBarButtonItem!
- weak var delegate: CustomCreditCardFormViewControllerDelegate?
+ weak var delegate: CustomCreditCardPaymentControllerDelegate?
// need to refactor loadView, removing super results in crash
// swiftlint:disable:next prohibited_super_call function_body_length
override func loadView() {
super.loadView()
- if storyboard == nil {
- view.backgroundColor = .background
-
- billingStackView = UIStackView()
- billingStackView.axis = .vertical
- billingStackView.spacing = 24
- billingStackView.distribution = .equalSpacing
- billingStackView.alignment = .fill
-
- cardNumberField = CardNumberTextField()
- cardNumberField.translatesAutoresizingMaskIntoConstraints = false
- cardNumberField.placeholder = "1234567812345678"
- cardNameField = CardNameTextField()
- cardNameField.translatesAutoresizingMaskIntoConstraints = false
- cardNameField.placeholder = "John Appleseed"
- cardExpiryField = CardExpiryDateTextField()
- cardExpiryField.translatesAutoresizingMaskIntoConstraints = false
- cardExpiryField.placeholder = "MM/yy Date Format"
- cardCVVField = CardCVVTextField()
- cardCVVField.translatesAutoresizingMaskIntoConstraints = false
- cardCVVField.placeholder = "321"
-
- let cardNumberLabel = UILabel()
- cardNumberLabel.text = "Card Number"
- cardNumberLabel.translatesAutoresizingMaskIntoConstraints = false
- let cardNumberStackView = UIStackView(arrangedSubviews: [cardNumberLabel, cardNumberField])
- let cardNameLabel = UILabel()
- cardNameLabel.text = "Card Name"
- cardNameLabel.translatesAutoresizingMaskIntoConstraints = false
- let cardNameStackView = UIStackView(arrangedSubviews: [cardNameLabel, cardNameField])
- let cardExpiryLabel = UILabel()
- cardExpiryLabel.text = "Card Expiry"
- cardExpiryLabel.translatesAutoresizingMaskIntoConstraints = false
- let cardExpiryStackView = UIStackView(arrangedSubviews: [cardExpiryLabel, cardExpiryField])
- let cardCVVLabel = UILabel()
- cardCVVLabel.text = "Card CVV"
- cardCVVLabel.translatesAutoresizingMaskIntoConstraints = false
- let cardCVVStackView = UIStackView(arrangedSubviews: [cardCVVLabel, cardCVVField])
-
- let lowerRowStackView = UIStackView(arrangedSubviews: [cardExpiryStackView, cardCVVStackView])
-
- cardNumberStackView.axis = .vertical
- cardNumberStackView.distribution = .fill
- cardNumberStackView.alignment = .fill
- cardNumberStackView.spacing = 10
- cardNameStackView.axis = .vertical
- cardNameStackView.distribution = .fill
- cardNameStackView.alignment = .fill
- cardNameStackView.spacing = 10
- cardExpiryStackView.axis = .vertical
- cardExpiryStackView.distribution = .fill
- cardExpiryStackView.alignment = .fill
- cardExpiryStackView.spacing = 10
- cardCVVStackView.axis = .vertical
- cardCVVStackView.distribution = .fill
- cardCVVStackView.alignment = .fill
- cardCVVStackView.spacing = 10
- lowerRowStackView.axis = .horizontal
- lowerRowStackView.distribution = .fillEqually
- lowerRowStackView.alignment = .fill
- lowerRowStackView.spacing = 10
-
- let stackView = UIStackView(arrangedSubviews: [cardNumberStackView, cardNameStackView, lowerRowStackView])
- stackView.translatesAutoresizingMaskIntoConstraints = false
- stackView.axis = .vertical
- stackView.distribution = .fill
- stackView.alignment = .fill
- stackView.spacing = 20
-
- view.addSubview(stackView)
- NSLayoutConstraint.activate([
- stackView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor),
- stackView.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor),
- stackView.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor, constant: 20),
- stackView.bottomAnchor.constraint(lessThanOrEqualTo: view.layoutMarginsGuide.bottomAnchor)
- ])
- }
+ view.backgroundColor = .background
+
+ billingStackView = UIStackView()
+ billingStackView.axis = .vertical
+ billingStackView.spacing = 24
+ billingStackView.distribution = .equalSpacing
+ billingStackView.alignment = .fill
+
+ cardNumberField = CardNumberTextField()
+ cardNumberField.translatesAutoresizingMaskIntoConstraints = false
+ cardNumberField.placeholder = "1234567812345678"
+ cardNameField = CardNameTextField()
+ cardNameField.translatesAutoresizingMaskIntoConstraints = false
+ cardNameField.placeholder = "John Appleseed"
+ cardExpiryField = CardExpiryDateTextField()
+ cardExpiryField.translatesAutoresizingMaskIntoConstraints = false
+ cardExpiryField.placeholder = "MM/yy Date Format"
+ cardCVVField = CardCVVTextField()
+ cardCVVField.translatesAutoresizingMaskIntoConstraints = false
+ cardCVVField.placeholder = "321"
+
+ let cardNumberLabel = UILabel()
+ cardNumberLabel.text = "Card Number"
+ cardNumberLabel.translatesAutoresizingMaskIntoConstraints = false
+ let cardNumberStackView = UIStackView(arrangedSubviews: [cardNumberLabel, cardNumberField])
+ let cardNameLabel = UILabel()
+ cardNameLabel.text = "Card Name"
+ cardNameLabel.translatesAutoresizingMaskIntoConstraints = false
+ let cardNameStackView = UIStackView(arrangedSubviews: [cardNameLabel, cardNameField])
+ let cardExpiryLabel = UILabel()
+ cardExpiryLabel.text = "Card Expiry"
+ cardExpiryLabel.translatesAutoresizingMaskIntoConstraints = false
+ let cardExpiryStackView = UIStackView(arrangedSubviews: [cardExpiryLabel, cardExpiryField])
+ let cardCVVLabel = UILabel()
+ cardCVVLabel.text = "Card CVV"
+ cardCVVLabel.translatesAutoresizingMaskIntoConstraints = false
+ let cardCVVStackView = UIStackView(arrangedSubviews: [cardCVVLabel, cardCVVField])
+
+ let lowerRowStackView = UIStackView(arrangedSubviews: [cardExpiryStackView, cardCVVStackView])
+
+ cardNumberStackView.axis = .vertical
+ cardNumberStackView.distribution = .fill
+ cardNumberStackView.alignment = .fill
+ cardNumberStackView.spacing = 10
+ cardNameStackView.axis = .vertical
+ cardNameStackView.distribution = .fill
+ cardNameStackView.alignment = .fill
+ cardNameStackView.spacing = 10
+ cardExpiryStackView.axis = .vertical
+ cardExpiryStackView.distribution = .fill
+ cardExpiryStackView.alignment = .fill
+ cardExpiryStackView.spacing = 10
+ cardCVVStackView.axis = .vertical
+ cardCVVStackView.distribution = .fill
+ cardCVVStackView.alignment = .fill
+ cardCVVStackView.spacing = 10
+ lowerRowStackView.axis = .horizontal
+ lowerRowStackView.distribution = .fillEqually
+ lowerRowStackView.alignment = .fill
+ lowerRowStackView.spacing = 10
+
+ let verticalContainerStack = UIStackView(arrangedSubviews: [cardNumberStackView, cardNameStackView, lowerRowStackView])
+ verticalContainerStack.translatesAutoresizingMaskIntoConstraints = false
+ verticalContainerStack.axis = .vertical
+ verticalContainerStack.distribution = .fill
+ verticalContainerStack.alignment = .fill
+ verticalContainerStack.spacing = 20
+
+ view.addSubview(verticalContainerStack)
+ NSLayoutConstraint.activate([
+ verticalContainerStack.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor),
+ verticalContainerStack.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor),
+ verticalContainerStack.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor, constant: 20),
+ verticalContainerStack.bottomAnchor.constraint(lessThanOrEqualTo: view.layoutMarginsGuide.bottomAnchor)
+ ])
setupBillingAddressFields()
}
@@ -127,12 +122,10 @@ class CustomCreditCardFormViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
- if storyboard == nil {
- let saveButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(self.proceed))
- navigationItem.rightBarButtonItem = saveButtonItem
- self.doneButton = saveButtonItem
- navigationItem.title = "Custom Credit Card Form"
- }
+ let saveButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(self.proceed))
+ navigationItem.rightBarButtonItem = saveButtonItem
+ self.doneButton = saveButtonItem
+ navigationItem.title = "Custom Credit Card Form"
}
private func setupBillingAddressFields() {
@@ -158,15 +151,17 @@ class CustomCreditCardFormViewController: UIViewController {
@IBAction private func proceed(_ sender: UIBarButtonItem) {
guard let name = cardNameField.text, cardNumberField.isValid,
let expiryMonth = cardExpiryField.selectedMonth, let expiryYear = cardExpiryField.selectedYear,
- let cvv = cardCVVField.text else {
+ let cvv = cardCVVField.text, let number = cardNumberField.text else {
return
}
- let tokenRequest = Request(
+
+ let card = CreateTokenPayload.Card(
name: name,
- pan: cardNumberField.pan,
+ number: number,
expirationMonth: expiryMonth,
expirationYear: expiryYear,
securityCode: cvv,
+ phoneNumber: nil,
countryCode: countryCodeField.text ?? "",
city: cityField.text ?? "",
state: stateField.text ?? "",
@@ -175,8 +170,9 @@ class CustomCreditCardFormViewController: UIViewController {
postalCode: postalCodeField.text ?? ""
)
+ let payload = CreateTokenPayload(card: card)
doneButton.isEnabled = false
- omiseClient.send(tokenRequest) { [weak self] (result) in
+ OmiseSDK.shared.client.createToken(payload: payload) { [weak self] (result) in
guard let self = self else { return }
self.doneButton.isEnabled = false
switch result {
diff --git a/ExampleApp/Views/PaymentSettingTableViewController.swift b/ExampleApp/Views/PaymentSettingTableViewController.swift
index cb98e60b..24c33268 100644
--- a/ExampleApp/Views/PaymentSettingTableViewController.swift
+++ b/ExampleApp/Views/PaymentSettingTableViewController.swift
@@ -54,7 +54,7 @@ class PaymentSettingTableViewController: UITableViewController {
}
}
- var allowedPaymentMethods: Set = [] {
+ var allowedPaymentMethods: Set = [] {
willSet {
guard isViewLoaded else {
return
@@ -122,6 +122,7 @@ class PaymentSettingTableViewController: UITableViewController {
@IBOutlet private var grabPayPaymentCell: UITableViewCell!
@IBOutlet private var boostPaymentCell: UITableViewCell!
@IBOutlet private var shopeePayPaymentCell: UITableViewCell!
+ @IBOutlet private var shopeePayJumpAppPaymentCell: UITableViewCell!
@IBOutlet private var maybankQRPayPaymentCell: UITableViewCell!
@IBOutlet private var duitNowQRPaymentCell: UITableViewCell!
@IBOutlet private var duitNowOBWPaymentCell: UITableViewCell!
@@ -264,7 +265,7 @@ extension PaymentSettingTableViewController {
}
// swiftlint:disable:next function_body_length
- func paymentSource(for cell: UITableViewCell) -> SourceTypeValue? {
+ func paymentSource(for cell: UITableViewCell) -> SourceType? {
switch cell {
case internetBankingBAYPaymentCell:
return .internetBankingBAY
@@ -325,7 +326,7 @@ extension PaymentSettingTableViewController {
case paynowPaymentCell:
return .payNow
case truemoneyPaymentCell:
- return .trueMoney
+ return .trueMoneyWallet
case truemoneyJumpAppPaymentCell:
return .trueMoneyJumpApp
case pointsCitiCell:
@@ -335,13 +336,15 @@ extension PaymentSettingTableViewController {
case rabbitLinepayCell:
return .rabbitLinepay
case OCBCDigitalPaymentCell:
- return .mobileBankingOCBC
+ return .ocbcDigital
case grabPayPaymentCell:
return .grabPay
case boostPaymentCell:
return .boost
case shopeePayPaymentCell:
return .shopeePay
+ case shopeePayJumpAppPaymentCell:
+ return .shopeePayJumpApp
case maybankQRPayPaymentCell:
return .maybankQRPay
case duitNowQRPaymentCell:
@@ -356,7 +359,7 @@ extension PaymentSettingTableViewController {
}
// swiftlint:disable:next function_body_length
- func cell(for paymentSource: SourceTypeValue) -> UITableViewCell? {
+ func cell(for paymentSource: SourceType) -> UITableViewCell? {
switch paymentSource {
case .internetBankingBAY:
return internetBankingBAYPaymentCell
@@ -400,7 +403,7 @@ extension PaymentSettingTableViewController {
return installmentUOBPaymentCell
case .mobileBankingSCB:
return mobileBankingSCBPaymentCell
- case .mobileBankingOCBC:
+ case .ocbcDigital:
return OCBCDigitalPaymentCell
case .mobileBankingKBank:
return mobileBankingKBankPaymentCell
@@ -418,7 +421,7 @@ extension PaymentSettingTableViewController {
return paynowPaymentCell
case .payPay:
return payPayPaymentCell
- case .trueMoney:
+ case .trueMoneyWallet:
return truemoneyPaymentCell
case .trueMoneyJumpApp:
return truemoneyJumpAppPaymentCell
@@ -434,6 +437,8 @@ extension PaymentSettingTableViewController {
return boostPaymentCell
case .shopeePay:
return shopeePayPaymentCell
+ case .shopeePayJumpApp:
+ return shopeePayJumpAppPaymentCell
case .maybankQRPay:
return maybankQRPayPaymentCell
case .duitNowQR:
diff --git a/ExampleApp/Views/ProductDetailViewController.swift b/ExampleApp/Views/ProductDetailViewController.swift
index 4daa28ad..77a7ff42 100644
--- a/ExampleApp/Views/ProductDetailViewController.swift
+++ b/ExampleApp/Views/ProductDetailViewController.swift
@@ -2,118 +2,54 @@ import UIKit
import OmiseSDK
class ProductDetailViewController: BaseViewController {
- private let publicKey = LocalConfig.default.publicKey
-
- private var capability: Capability?
-
- override func viewDidLoad() {
- super.viewDidLoad()
- setupClient()
- }
-
- private func setupClient() {
- // Setup dev environment for staging
- Configuration.setDefault(Configuration(environment: LocalConfig.default.env))
-
- let client = Client(publicKey: publicKey)
+ let omiseSDK = {
+ // Initialize OmiseSDK instance with given publicKey and optional configuration used for testing
+ let omiseSDK = OmiseSDK(
+ publicKey: LocalConfig.default.publicKey,
+ configuration: LocalConfig.default.configuration
+ )
+ // Setup shared instance to use from other screens if required
+ OmiseSDK.shared = omiseSDK
+ return omiseSDK
+ }()
- client.capabilityDataWithCompletionHandler { (result) in
+ private func loadCapability() {
+ omiseSDK.client.capability { (result) in
if case .success(let capability) = result {
- self.capability = capability
print("Capability Country: \(capability.countryCode)")
}
}
}
-
- override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
- if identifier == "PresentCreditFormWithModal" ||
- identifier == "ShowCreditForm" ||
- identifier == "PresentPaymentCreator" ||
- identifier == "ShowCreditFormWithCustomFields" {
- return currentCodePathMode == .storyboard
- }
-
- return true
- }
-
- override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
- super.prepare(for: segue, sender: sender)
-
- if segue.identifier == "PresentCreditFormWithModal",
- let creditCardFormNavigationController = segue.destination as? UINavigationController,
- let creditCardFormController = creditCardFormNavigationController.topViewController as? CreditCardFormViewController {
- creditCardFormController.publicKey = publicKey
- creditCardFormController.handleErrors = true
- creditCardFormController.delegate = self
- } else if segue.identifier == "ShowCreditForm",
- let creditCardFormController = segue.destination as? CreditCardFormViewController {
- creditCardFormController.publicKey = publicKey
- creditCardFormController.handleErrors = true
- creditCardFormController.delegate = self
- } else if segue.identifier == "PresentPaymentCreator",
- let paymentCreatorController = segue.destination as? PaymentCreatorController {
- paymentCreatorController.publicKey = self.publicKey
- paymentCreatorController.paymentAmount = paymentAmount
- paymentCreatorController.paymentCurrency = Currency(code: paymentCurrencyCode)
- if usesCapabilityDataForPaymentMethods, let capability = self.capability {
- paymentCreatorController.applyPaymentMethods(from: capability)
- } else {
- paymentCreatorController.allowedPaymentMethods = allowedPaymentMethods
- }
- paymentCreatorController.paymentDelegate = self
- } else if segue.identifier == "ShowCreditFormWithCustomFields",
- let vc = segue.destination as? CustomCreditCardFormViewController {
- vc.delegate = self
- }
- }
-
- @IBAction private func showModalCreditCardForm(_ sender: Any) {
- guard currentCodePathMode == .code else {
- return
- }
- let creditCardFormController = CreditCardFormViewController.makeCreditCardFormViewController(withPublicKey: publicKey)
- creditCardFormController.handleErrors = true
- creditCardFormController.delegate = self
- let navigationController = UINavigationController(rootViewController: creditCardFormController)
- present(navigationController, animated: true, completion: nil)
- }
-
- @IBAction private func showCreditCardForm(_ sender: UIButton) {
- guard currentCodePathMode == .code else {
- return
- }
- let creditCardFormController = CreditCardFormViewController.makeCreditCardFormViewController(withPublicKey: publicKey)
- creditCardFormController.handleErrors = true
- creditCardFormController.delegate = self
- show(creditCardFormController, sender: self)
+
+ @IBAction private func showModalCreditCardPayment(_ sender: Any) {
+ let viewController = omiseSDK.creditCardController(delegate: self)
+ present(viewController, animated: true, completion: nil)
}
@IBAction private func showModalPaymentCreator(_ sender: Any) {
- guard currentCodePathMode == .code else {
- return
- }
- let paymentCreatorController = PaymentCreatorController.makePaymentCreatorControllerWith(
- publicKey: publicKey,
- amount: paymentAmount,
- currency: Currency(code: paymentCurrencyCode),
- allowedPaymentMethods: allowedPaymentMethods,
- paymentDelegate: self
- )
-
- if usesCapabilityDataForPaymentMethods, let capability = self.capability {
- paymentCreatorController.applyPaymentMethods(from: capability)
+ if usesCapabilityDataForPaymentMethods {
+ let viewController = omiseSDK.choosePaymentMethodFromCapabilityController(
+ amount: paymentAmount,
+ currency: paymentCurrencyCode,
+ delegate: self
+ )
+ present(viewController, animated: true, completion: nil)
+ } else {
+ let viewController = omiseSDK.choosePaymentMethodController(
+ amount: paymentAmount,
+ currency: paymentCurrencyCode,
+ allowedPaymentMethods: allowedPaymentMethods,
+ allowedCardPayment: true,
+ delegate: self
+ )
+ present(viewController, animated: true, completion: nil)
}
-
- present(paymentCreatorController, animated: true, completion: nil)
}
- @IBAction private func showCustomCreditCardForm(_ sender: Any) {
- guard currentCodePathMode == .code else {
- return
- }
- let customCreditCardFormController = CustomCreditCardFormViewController(nibName: nil, bundle: nil)
- customCreditCardFormController.delegate = self
- show(customCreditCardFormController, sender: sender)
+ @IBAction private func showCustomCreditCardPayment(_ sender: Any) {
+ let customCreditCardPaymentController = CustomCreditCardPaymentController(nibName: nil, bundle: nil)
+ customCreditCardPaymentController.delegate = self
+ show(customCreditCardPaymentController, sender: sender)
}
@IBAction private func handlingAuthorizingPayment(_ sender: UIBarButtonItem) {
@@ -122,30 +58,41 @@ class ProductDetailViewController: BaseViewController {
preferredStyle: .alert)
alertController.addTextField(configurationHandler: nil)
alertController.addAction(UIAlertAction(title: "Cancel", style: UIAlertAction.Style.cancel, handler: nil))
- alertController.addAction(UIAlertAction(title: "Go", style: UIAlertAction.Style.default) { (_) in
- guard let textField = alertController.textFields?.first,
+ alertController.addAction(UIAlertAction(title: "Go", style: UIAlertAction.Style.default) { [weak self ](_) in
+ guard let self = self,
+ let textField = alertController.textFields?.first,
let text = textField.text,
let url = URL(string: text),
let expectedReturnURL = URLComponents(string: "https://opn.ooo/") else { return }
- let handlerController =
- AuthorizingPaymentViewController
- .makeAuthorizingPaymentViewControllerNavigationWithAuthorizedURL(
- url, expectedReturnURLPatterns: [expectedReturnURL], delegate: self)
+ let handlerController = self.omiseSDK.authorizedController(
+ authorizedURL: url,
+ expectedReturnURLPatterns: [expectedReturnURL],
+ delegate: self
+ )
self.present(handlerController, animated: true, completion: nil)
})
present(alertController, animated: true, completion: nil)
}
}
-// MARK: - Credit Card Form View Controller Delegate
+// MARK: - Authorizing Payment View Controller Delegate
-extension ProductDetailViewController: CreditCardFormViewControllerDelegate {
- func creditCardFormViewControllerDidCancel(_ controller: CreditCardFormViewController) {
- dismissForm()
+extension ProductDetailViewController: AuthorizingPaymentViewControllerDelegate {
+ func authorizingPaymentViewController(_ viewController: AuthorizingPaymentViewController, didCompleteAuthorizingPaymentWithRedirectedURL redirectedURL: URL) {
+ print(redirectedURL)
+ dismiss(animated: true, completion: nil)
}
- func creditCardFormViewController(_ controller: CreditCardFormViewController, didSucceedWithToken token: Token) {
+ func authorizingPaymentViewControllerDidCancel(_ viewController: AuthorizingPaymentViewController) {
+ dismiss(animated: true, completion: nil)
+ }
+}
+
+// MARK: - Custom Credit Card Form View Controller Delegate
+
+extension ProductDetailViewController: CustomCreditCardPaymentControllerDelegate {
+ func creditCardFormViewController(_ controller: CustomCreditCardPaymentController, didSucceedWithToken token: Token) {
dismissForm {
let alertController = UIAlertController(
title: "Token Created",
@@ -158,7 +105,7 @@ extension ProductDetailViewController: CreditCardFormViewControllerDelegate {
}
}
- func creditCardFormViewController(_ controller: CreditCardFormViewController, didFailWithError error: Error) {
+ func creditCardFormViewController(_ controller: CustomCreditCardPaymentController, didFailWithError error: Error) {
dismissForm {
let alertController = UIAlertController(
title: "Error",
@@ -172,40 +119,13 @@ extension ProductDetailViewController: CreditCardFormViewControllerDelegate {
}
}
-// MARK: - Authorizing Payment View Controller Delegate
-
-extension ProductDetailViewController: AuthorizingPaymentViewControllerDelegate {
- func authorizingPaymentViewController(_ viewController: AuthorizingPaymentViewController, didCompleteAuthorizingPaymentWithRedirectedURL redirectedURL: URL) {
- print(redirectedURL)
- dismiss(animated: true, completion: nil)
- }
-
- func authorizingPaymentViewControllerDidCancel(_ viewController: AuthorizingPaymentViewController) {
- dismiss(animated: true, completion: nil)
- }
-}
-
-// MARK: - Payment Creator Controller Delegate
-
-extension ProductDetailViewController: PaymentCreatorControllerDelegate {
-
- func paymentCreatorController(_ paymentCreatorController: PaymentCreatorController, didCreatePayment payment: Payment) {
+/// Processing result of choosing Payment Method screen
+extension ProductDetailViewController: ChoosePaymentMethodDelegate {
+ func choosePaymentMethodDidComplete(with source: Source) {
dismissForm {
- let title: String
- let message: String
-
- switch payment {
- case .token(let token):
- title = "Token Created"
- message = "A token with id of \(token.id) was successfully created. Please send this id to server to create a charge."
- case .source(let source):
- title = "Token Created"
- message = "A source with id of \(source.id) was successfully created. Please send this id to server to create a charge."
- }
-
let alertController = UIAlertController(
- title: title,
- message: message,
+ title: "Source Created\n(\(source.paymentInformation.sourceType.rawValue))",
+ message: "A source with id of \(source.id) was successfully created. Please send this id to server to create a charge.",
preferredStyle: .alert
)
let okAction = UIAlertAction(title: "OK", style: .cancel, handler: nil)
@@ -213,23 +133,8 @@ extension ProductDetailViewController: PaymentCreatorControllerDelegate {
self.present(alertController, animated: true, completion: nil)
}
}
-
- func paymentCreatorController(_ paymentCreatorController: PaymentCreatorController, didFailWithError error: Error) {
- let alertController = UIAlertController(title: "Error", message: error.localizedDescription, preferredStyle: .alert)
- let okAction = UIAlertAction(title: "OK", style: .cancel, handler: nil)
- alertController.addAction(okAction)
- paymentCreatorController.present(alertController, animated: true, completion: nil)
- }
-
- func paymentCreatorControllerDidCancel(_ paymentCreatorController: PaymentCreatorController) {
- dismissForm()
- }
-}
-
-// MARK: - Custom Credit Card Form View Controller Delegate
-extension ProductDetailViewController: CustomCreditCardFormViewControllerDelegate {
- func creditCardFormViewController(_ controller: CustomCreditCardFormViewController, didSucceedWithToken token: Token) {
+ func choosePaymentMethodDidComplete(with token: Token) {
dismissForm {
let alertController = UIAlertController(
title: "Token Created",
@@ -241,17 +146,17 @@ extension ProductDetailViewController: CustomCreditCardFormViewControllerDelegat
self.present(alertController, animated: true, completion: nil)
}
}
-
- func creditCardFormViewController(_ controller: CustomCreditCardFormViewController, didFailWithError error: Error) {
+
+ func choosePaymentMethodDidComplete(with error: Error) {
dismissForm {
- let alertController = UIAlertController(
- title: "Error",
- message: error.localizedDescription,
- preferredStyle: .alert
- )
+ let alertController = UIAlertController(title: "Error", message: error.localizedDescription, preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .cancel, handler: nil)
alertController.addAction(okAction)
self.present(alertController, animated: true, completion: nil)
}
}
+
+ func choosePaymentMethodDidCancel() {
+ dismissForm()
+ }
}
diff --git a/Helpers/OmiseSwiftUIKit/.swiftpm/xcode/xcshareddata/xcschemes/OmiseSwiftUIKit.xcscheme b/Helpers/OmiseSwiftUIKit/.swiftpm/xcode/xcshareddata/xcschemes/OmiseSwiftUIKit.xcscheme
new file mode 100644
index 00000000..c18d801b
--- /dev/null
+++ b/Helpers/OmiseSwiftUIKit/.swiftpm/xcode/xcshareddata/xcschemes/OmiseSwiftUIKit.xcscheme
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Helpers/OmiseSwiftUIKit/Sources/OmiseSwiftUIKit/UIKitLiveKit/UIKitViewControllerPresentable.swift b/Helpers/OmiseSwiftUIKit/Sources/OmiseSwiftUIKit/UIKitLiveKit/UIKitViewControllerPresentable.swift
index cfe602db..e249072d 100644
--- a/Helpers/OmiseSwiftUIKit/Sources/OmiseSwiftUIKit/UIKitLiveKit/UIKitViewControllerPresentable.swift
+++ b/Helpers/OmiseSwiftUIKit/Sources/OmiseSwiftUIKit/UIKitLiveKit/UIKitViewControllerPresentable.swift
@@ -8,14 +8,14 @@
import SwiftUI
import UIKit
-struct UIKitViewControllerPresentable: UIViewControllerRepresentable {
- let viewController: VC
+public struct UIKitViewControllerPresentable: UIViewControllerRepresentable {
+ public let viewController: VC
- func makeUIViewController(context: Context) -> VC {
+ public func makeUIViewController(context: Context) -> VC {
viewController
}
- func updateUIViewController(_ uiViewController: VC, context: Context) {
+ public func updateUIViewController(_ uiViewController: VC, context: Context) {
// viewController
}
}
diff --git a/Helpers/OmiseSwiftUIKit/Sources/OmiseSwiftUIKit/UIKitLiveKit/UIKitViewPresentable.swift b/Helpers/OmiseSwiftUIKit/Sources/OmiseSwiftUIKit/UIKitLiveKit/UIKitViewPresentable.swift
index 2a4e8e6c..e9be9702 100644
--- a/Helpers/OmiseSwiftUIKit/Sources/OmiseSwiftUIKit/UIKitLiveKit/UIKitViewPresentable.swift
+++ b/Helpers/OmiseSwiftUIKit/Sources/OmiseSwiftUIKit/UIKitLiveKit/UIKitViewPresentable.swift
@@ -8,13 +8,13 @@
import SwiftUI
import UIKit
-struct UIKitViewPresentable: UIViewRepresentable {
- let view: V
+public struct UIKitViewPresentable: UIViewRepresentable {
+ public let view: V
- func makeUIView(context: Context) -> V {
+ public func makeUIView(context: Context) -> V {
return view
}
- func updateUIView(_ uiView: V, context: Context) {
+ public func updateUIView(_ uiView: V, context: Context) {
}
}
diff --git a/Helpers/OmiseUnitTestKit/.swiftpm/xcode/xcshareddata/xcschemes/OmiseUnitTestKit.xcscheme b/Helpers/OmiseUnitTestKit/.swiftpm/xcode/xcshareddata/xcschemes/OmiseUnitTestKit.xcscheme
new file mode 100644
index 00000000..ca4a4fa3
--- /dev/null
+++ b/Helpers/OmiseUnitTestKit/.swiftpm/xcode/xcshareddata/xcschemes/OmiseUnitTestKit.xcscheme
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/OmiseSDK/AdaptableDynamicTableViewController.swift b/OmiseSDK/AdaptableDynamicTableViewController.swift
deleted file mode 100644
index cdef1477..00000000
--- a/OmiseSDK/AdaptableDynamicTableViewController.swift
+++ /dev/null
@@ -1,20 +0,0 @@
-import UIKit
-
-public class AdaptableDynamicTableViewController: UITableViewController {
-
- public var showingValues: [Element] = [] {
- didSet {
- if isViewLoaded {
- tableView.reloadData()
- }
- }
- }
-
- public override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
- return showingValues.count
- }
-
- public func element(forUIIndexPath indexPath: IndexPath) -> Element {
- return showingValues[indexPath.row]
- }
-}
diff --git a/OmiseSDK/AdaptableStaticTableViewController.swift b/OmiseSDK/AdaptableStaticTableViewController.swift
deleted file mode 100644
index ef3b4680..00000000
--- a/OmiseSDK/AdaptableStaticTableViewController.swift
+++ /dev/null
@@ -1,35 +0,0 @@
-import UIKit
-
-public class AdaptableStaticTableViewController: UITableViewController {
-
- public var showingValues: [Element] = [] {
- didSet {
- if isViewLoaded {
- tableView.reloadData()
- }
- }
- }
-
- public override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
- return showingValues.count
- }
-
- public override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
- let staticIndexPath = calculateStaticIndexPath(fromUIIndexPath: indexPath)
- return super.tableView(tableView, cellForRowAt: staticIndexPath)
- }
-
- public func calculateStaticIndexPath(fromUIIndexPath indexPath: IndexPath) -> IndexPath {
- return staticIndexPath(forValue: element(forUIIndexPath: indexPath))
- }
-
- public func staticIndexPath(forValue value: Element) -> IndexPath {
- let allCases = Element.allCases
- let index = allCases.firstIndex(of: value)! // swiftlint:disable:this force_unwrapping
- return IndexPath(row: allCases.distance(from: allCases.startIndex, to: index), section: 0)
- }
-
- public func element(forUIIndexPath indexPath: IndexPath) -> Element {
- return showingValues[indexPath.row]
- }
-}
diff --git a/OmiseSDK/Capability.swift b/OmiseSDK/Capability.swift
deleted file mode 100644
index 3060387d..00000000
--- a/OmiseSDK/Capability.swift
+++ /dev/null
@@ -1,502 +0,0 @@
-// swiftlint:disable file_length
-import Foundation
-
-public struct Capability: Object {
- public let countryCode: String
- public let location: String
- public let object: String
-
- public let supportedBanks: Set
-
- public let supportedBackends: [Backend]
-
- private let backends: [Capability.Backend.BackendType: Backend]
-
- public var creditCardBackend: Capability.Backend? {
- return backends[.card]
- }
-
- public subscript(type: SourceTypeValue) -> Capability.Backend? {
- return backends[.source(type)]
- }
-}
-
-extension Capability {
- public static func ~= (lhs: Capability, rhs: CreateSourceParameter) -> Bool {
- func backend(from capability: Capability, for payment: PaymentInformation) -> Backend? {
- if let paymentSourceType = SourceTypeValue(payment.sourceType) {
- return capability[paymentSourceType]
- } else {
- return nil
- }
- }
-
- guard let backend = backend(from: lhs, for: rhs.paymentInformation) else {
- return false
- }
-
- let isValidValue = backend.supportedCurrencies.contains(rhs.currency)
-
- let isPaymentValid: Bool
- switch backend.payment {
- case .installment(_, availableNumberOfTerms: let availableNumberofTerms):
- if case .installment(let installment) = rhs.paymentInformation {
- isPaymentValid = availableNumberofTerms.contains(installment.numberOfTerms)
- } else {
- isPaymentValid = false
- }
- default:
- isPaymentValid = true
- }
-
- return isValidValue && isPaymentValid
- }
-}
-
-extension Capability {
- public struct Backend: Codable, Equatable {
- public let payment: Payment
- public let supportedCurrencies: Set
- public let banks: [Bank]?
-
- public enum Payment: Equatable {
- case card(Set)
- case installment(PaymentInformation.Installment.Brand, availableNumberOfTerms: IndexSet)
- case internetBanking(PaymentInformation.InternetBanking)
- case mobileBanking(PaymentInformation.MobileBanking)
- case billPayment(PaymentInformation.BillPayment)
- case alipay
- case alipayCN
- case alipayHK
- case atome
- case dana
- case gcash
- case kakaoPay
- case touchNGoAlipayPlus
- case touchNGo
- case promptpay
- case paynow
- case truemoney
- case truemoneyJumpApp
- case points(PaymentInformation.Points)
- case eContext
- case fpx
- case rabbitLinepay
- case ocbcDigital
- case grabPay
- case grabPayRms
- case boost
- case shopeePay
- case shopeePayJumpApp
- case maybankQRPay
- case duitNowQR
- case duitNowOBW
- case payPay
- case weChat
- case unknownSource(String, configurations: [String: Any])
- }
-
- public struct Bank: Codable, Equatable {
- // swiftlint:disable:next nesting
- enum CodingKeys: String, CodingKey {
- case name, code
- case isActive = "active"
- }
-
- public let name: String
- public let code: String
- public let isActive: Bool
- }
- }
-}
-
-extension Capability: Codable {
- private enum CodingKeys: String, CodingKey {
- case countryCode = "country"
- case object
- case location
- case supportedBanks = "banks"
- case paymentBackends = "payment_methods"
- }
-}
-
-extension Capability.Backend {
- private enum CodingKeys: String, CodingKey {
- case object
- case name
- case supportedCurrencies = "currencies"
- case allowedInstallmentTerms = "installment_terms"
- case cardBrands = "card_brands"
- case banks
- case provider
- }
-}
-
-extension Capability.Backend.Payment {
- // swiftlint:disable:next function_body_length
- public static func == (lhs: Capability.Backend.Payment, rhs: Capability.Backend.Payment) -> Bool {
- switch (lhs, rhs) {
- case (.card, .card), (.alipay, .alipay), (.alipayCN, .alipayCN), (.alipayHK, .alipayHK):
- return true
- case (.atome, .atome):
- return true
- case (.dana, .dana), (.gcash, .gcash), (.kakaoPay, .kakaoPay), (.touchNGoAlipayPlus, .touchNGoAlipayPlus):
- return true
- case (.touchNGo, .touchNGo):
- return true
- case (.promptpay, .promptpay), (.paynow, .paynow):
- return true
- case (.truemoney, .truemoney):
- return true
- case (.eContext, .eContext):
- return true
- case (.points(let lhsValue), .points(let rhsValue)):
- return lhsValue == rhsValue
- case (.installment(let lhsValue, _), .installment(let rhsValue, _)):
- return lhsValue == rhsValue
- case (.internetBanking(let lhsValue), .internetBanking(let rhsValue)):
- return lhsValue == rhsValue
- case (.mobileBanking(let lhsValue), .mobileBanking(let rhsValue)):
- return lhsValue == rhsValue
- case (.billPayment(let lhsValue), .billPayment(let rhsValue)):
- return lhsValue == rhsValue
- case (.fpx, .fpx):
- return true
- case (.rabbitLinepay, .rabbitLinepay):
- return true
- case (.ocbcDigital, .ocbcDigital):
- return true
- case (.grabPay, .grabPay):
- return true
- case (.grabPayRms, .grabPayRms):
- return true
- case (.boost, .boost):
- return true
- case (.shopeePay, .shopeePay):
- return true
- case (.shopeePayJumpApp, .shopeePayJumpApp):
- return true
- case (.maybankQRPay, .maybankQRPay):
- return true
- case (.duitNowQR, .duitNowQR):
- return true
- case (.duitNowOBW, .duitNowOBW):
- return true
- case (.payPay, .payPay):
- return true
- case (.weChat, .weChat):
- return true
- default:
- return false
- }
- }
-}
-
-extension Capability {
- public init(from decoder: Decoder) throws {
- let container = try decoder.container(keyedBy: CodingKeys.self)
-
- location = try container.decode(String.self, forKey: .location)
- countryCode = try container.decode(String.self, forKey: .countryCode)
- object = try container.decode(String.self, forKey: .object)
-
- supportedBanks = try container.decode(Set.self, forKey: .supportedBanks)
-
- var backendsContainer = try container.nestedUnkeyedContainer(forKey: .paymentBackends)
-
- var backends: [Capability.Backend] = []
-
- while !backendsContainer.isAtEnd {
- let backend = try backendsContainer.decode(Capability.Backend.self)
- backends.append(backend)
- }
-
- self.supportedBackends = backends
-
- let backendTypes = backends.compactMap { Capability.Backend.BackendType(payment: $0.payment) }
- self.backends = Dictionary(uniqueKeysWithValues: zip(backendTypes, backends))
- }
-
- public func encode(to encoder: Encoder) throws {
- var container = encoder.container(keyedBy: CodingKeys.self)
-
- try container.encode(location, forKey: .location)
- try container.encode(countryCode, forKey: .countryCode)
- try container.encode(object, forKey: .object)
-
- try container.encode(supportedBanks, forKey: .supportedBanks)
-
- var backendsContainer = container.nestedUnkeyedContainer(forKey: .paymentBackends)
- try supportedBackends.forEach { backend in
- try backendsContainer.encode(backend)
- }
- }
-}
-
-extension Capability.Backend {
- // swiftlint:disable:next function_body_length
- public init(from decoder: Decoder) throws {
- let container = try decoder.container(keyedBy: CodingKeys.self)
-
- let type = try container.decode(BackendType.self, forKey: .name)
- let provider = try container.decodeIfPresent(Provider.self, forKey: .provider)
- supportedCurrencies = try container.decode(Set.self, forKey: .supportedCurrencies)
-
- switch type {
- case .unknown(let sourceType):
- self.payment = .unknownSource(sourceType, configurations: [:])
- case .card:
- let supportedBrand = try container.decode(Set.self, forKey: .cardBrands)
- let cardBrands = supportedBrand.compactMap {
- CardBrand(rawValue: $0)
- }
- self.payment = .card(Set(cardBrands))
- case .source(let value) where value.isInstallmentSource:
- let allowedInstallmentTerms = IndexSet(try container.decode(Array.self, forKey: .allowedInstallmentTerms))
- // swiftlint:disable:next force_unwrapping
- self.payment = .installment(value.installmentBrand!, availableNumberOfTerms: allowedInstallmentTerms)
- case .source(.alipay):
- self.payment = .alipay
- case .source(.alipayCN):
- self.payment = .alipayCN
- case .source(.alipayHK):
- self.payment = .alipayHK
- case .source(.atome):
- self.payment = .atome
- case .source(.dana):
- self.payment = .dana
- case .source(.gcash):
- self.payment = .gcash
- case .source(.kakaoPay):
- self.payment = .kakaoPay
- case .source(.touchNGo):
- switch provider {
- case .alipayPlus:
- self.payment = .touchNGoAlipayPlus
- default:
- self.payment = .touchNGo
- }
- case .source(let value) where value.isInternetBankingSource:
- // swiftlint:disable:next force_unwrapping
- self.payment = .internetBanking(value.internetBankingSource!)
- case .source(let value) where value.isMobileBankingSource:
- // swiftlint:disable:next force_unwrapping
- self.payment = .mobileBanking(value.mobileBankingSource!)
- case .source(.promptPay):
- self.payment = .promptpay
- case .source(.payNow):
- self.payment = .paynow
- case .source(.trueMoney):
- self.payment = .truemoney
- case .source(.trueMoneyJumpApp):
- self.payment = .truemoneyJumpApp
- case .source(.pointsCiti):
- self.payment = .points(.citiPoints)
- case .source(.billPaymentTescoLotus):
- self.payment = .billPayment(.tescoLotus)
- case .source(.eContext):
- self.payment = .eContext
- case .source(.fpx):
- self.payment = .fpx
- case .source(.rabbitLinepay):
- self.payment = .rabbitLinepay
- case .source(.mobileBankingOCBC):
- self.payment = .ocbcDigital
- case .source(.grabPay):
- switch provider {
- case .rms:
- self.payment = .grabPayRms
- default:
- self.payment = .grabPay
- }
- case .source(.boost):
- self.payment = .boost
- case .source(.shopeePay):
- self.payment = .shopeePay
- case .source(.shopeePayJumpApp):
- self.payment = .shopeePayJumpApp
- case .source(.maybankQRPay):
- self.payment = .maybankQRPay
- case .source(.duitNowQR):
- self.payment = .duitNowQR
- case .source(.duitNowOBW):
- self.payment = .duitNowOBW
- case .source(.payPay):
- self.payment = .payPay
- case .source(.weChat):
- self.payment = .weChat
- case .source(let value):
- let configurations = try container.decodeJSONDictionary()
- self.payment = .unknownSource(value.rawValue, configurations: configurations)
- }
-
- banks = try? container.decode([Bank].self, forKey: .banks)
- }
-
- public func encode(to encoder: Encoder) throws {
- var container = encoder.container(keyedBy: CodingKeys.self)
-
- try container.encode(BackendType(payment: payment), forKey: .name)
-
- switch payment {
- case .card(let brands):
- try container.encode(brands, forKey: .cardBrands)
- try container.encode(Array(supportedCurrencies), forKey: .supportedCurrencies)
- case .installment(_, availableNumberOfTerms: let availableNumberOfTerms):
- try container.encode(Array(availableNumberOfTerms), forKey: .allowedInstallmentTerms)
- try container.encode(Array(supportedCurrencies), forKey: .supportedCurrencies)
- case .internetBanking, .alipay, .alipayCN, .alipayHK, .atome, .dana, .gcash, .kakaoPay, .touchNGoAlipayPlus, .touchNGo, .promptpay, .paynow, .truemoney, .truemoneyJumpApp, .points, .billPayment, .eContext, .mobileBanking, .fpx, .rabbitLinepay, .ocbcDigital, .grabPay, .grabPayRms, .boost, .shopeePay, .shopeePayJumpApp, .maybankQRPay, .duitNowQR, .duitNowOBW, .payPay, .weChat:
- // swiftlint:disable:previous line_length
- try container.encode(Array(supportedCurrencies), forKey: .supportedCurrencies)
- case .unknownSource(_, configurations: let configurations):
- try encoder.encodeJSONDictionary(configurations)
- try container.encode(Array(supportedCurrencies), forKey: .supportedCurrencies)
- }
- }
-}
-
-private let creditCardBackendTypeValue = "card"
-extension Capability.Backend {
- fileprivate enum BackendType: Codable, Hashable {
- case card
- case source(SourceTypeValue)
- case unknown(sourceType: String)
-
- init(from decoder: Decoder) throws {
- let container = try decoder.singleValueContainer()
- switch try container.decode(String.self) {
- case creditCardBackendTypeValue:
- self = .card
- case let value:
- if let sourceType = SourceTypeValue(value) {
- self = .source(sourceType)
- } else {
- self = .unknown(sourceType: value)
- }
- }
- }
-
- func encode(to encoder: Encoder) throws {
- var container = encoder.singleValueContainer()
- let type: String
- switch self {
- case .unknown(let sourceType):
- type = sourceType
- case .card:
- type = creditCardBackendTypeValue
- case .source(let sourceType):
- type = sourceType.rawValue
- }
-
- try container.encode(type)
- }
-
- init?(sourceType string: String) {
- if let sourceType = SourceTypeValue(string) {
- self = .source(sourceType)
- } else {
- return nil
- }
- }
-
- // swiftlint:disable:next function_body_length
- init?(payment: Capability.Backend.Payment) {
- switch payment {
- case .card:
- self = .card
- case .alipay:
- self = .source(.alipay)
- case .alipayCN:
- self = .source(.alipayCN)
- case .alipayHK:
- self = .source(.alipayHK)
- case .atome:
- self = .source(.atome)
- case .dana:
- self = .source(.dana)
- case .gcash:
- self = .source(.gcash)
- case .kakaoPay:
- self = .source(.kakaoPay)
- case .touchNGoAlipayPlus:
- self = .source(.touchNGoAlipayPlus)
- case .touchNGo:
- self = .source(.touchNGo)
- case .installment(let brand, availableNumberOfTerms: _):
- self.init(sourceType: brand.type)
- case .internetBanking(let banking):
- self.init(sourceType: banking.type)
- case .mobileBanking(let banking):
- self.init(sourceType: banking.type)
- case .billPayment(let billPayment):
- self.init(sourceType: billPayment.type)
- case .promptpay:
- self = .source(.promptPay)
- case .paynow:
- self = .source(.payNow)
- case .truemoney:
- self = .source(.trueMoney)
- case .truemoneyJumpApp:
- self = .source(.trueMoneyJumpApp)
- case .points(let points):
- self.init(sourceType: points.type)
- case .eContext:
- self = .source(.eContext)
- case .fpx:
- self = .source(.fpx)
- case .rabbitLinepay:
- self = .source(.rabbitLinepay)
- case .ocbcDigital:
- self = .source(.mobileBankingOCBC)
- case .grabPay:
- self = .source(.grabPay)
- case .grabPayRms:
- self = .source(.grabPayRms)
- case .boost:
- self = .source(.boost)
- case .shopeePay:
- self = .source(.shopeePay)
- case .shopeePayJumpApp:
- self = .source(.shopeePayJumpApp)
- case .maybankQRPay:
- self = .source(.maybankQRPay)
- case .duitNowQR:
- self = .source(.duitNowQR)
- case .duitNowOBW:
- self = .source(.duitNowOBW)
- case .payPay:
- self = .source(.payPay)
- case .weChat:
- self = .source(.weChat)
- case .unknownSource(let sourceType, configurations: _):
- self.init(sourceType: sourceType)
- }
- }
-
- var type: String {
- switch self {
- case .unknown(let sourceType):
- return sourceType
- case .card:
- return "card"
- case .source(let sourceType):
- let sourceTypeValuePrefix = sourceType.sourceTypePrefix
- if sourceTypeValuePrefix.hasSuffix("_") {
- return sourceTypeValuePrefix
- .lastIndex(of: "_")
- .map(sourceTypeValuePrefix.prefix(upTo:))
- .map(String.init) ?? sourceTypeValuePrefix
- } else {
- return sourceTypeValuePrefix
- }
- }
- }
- }
-}
-
-extension Capability.Backend {
- fileprivate enum Provider: String, Codable, Hashable {
- case alipayPlus = "Alipay_plus"
- case rms = "RMS"
- }
-}
diff --git a/OmiseSDK/Client.swift b/OmiseSDK/Client.swift
deleted file mode 100644
index 40cda5dd..00000000
--- a/OmiseSDK/Client.swift
+++ /dev/null
@@ -1,420 +0,0 @@
-import UIKit
-import os
-
-// swiftlint:disable file_length
-public class Client: NSObject {
- // Shared latest capability requested from API
- static var sharedCapability: Capability? {
- didSet {
- if let countryCode = sharedCapability?.countryCode {
- CountryInfo.setDefaultCountryCode(countryCode)
- }
- }
- }
-
- let session: URLSession
- let queue: OperationQueue
- let publicKey: String
-
- var userAgent: String?
-
- enum HTTPHeaders: String {
- case authorization = "Authorization"
- case userAgent = "User-Agent"
- case contentType = "Content-Type"
- case omiseVersion = "Omise-Version"
- }
-
- struct HTTPHeaderss {
- let authorization = "Authorization"
- }
- /// Initializes a new Client with the given Public Key and Operating OperationQueue
- ///
- /// - Parameters:
- /// - publicKey: Public Key for this Client used for calling the Omise API. The key must have `pkey` prefix
- /// - queue: OperationQueue which the client uses for the network related operations
- public init(publicKey: String, queue: OperationQueue) {
- if publicKey.hasPrefix("pkey_") {
- self.publicKey = publicKey
- } else {
- os_log("Refusing to initialize sdk client with a non-public key: %{private}@", log: sdkLogObject, type: .error, publicKey)
- assertionFailure("Refusing to initialize sdk client with a non-public key.")
- self.publicKey = ""
- }
-
- self.queue = queue
- self.session = URLSession(
- configuration: URLSessionConfiguration.ephemeral,
- delegate: nil,
- delegateQueue: queue
- )
- }
-
- /// Initializes a new Client with the given Public Key
- ///
- /// - Parameters:
- /// - publicKey: Public Key for this Client used for calling the Omise API. The key must have `pkey` prefix
- public convenience init(publicKey: String) {
- self.init(publicKey: publicKey, queue: OperationQueue())
- }
-
- /// Create a new Reqeust Task with the given request and compleation handler closure
- ///
- /// - Parameters:
- /// - request: A Request to create a requrest task
- /// - completionHandler: Compleation Handler closure that will be called when the request task is finished.
- /// The completion handler will be called on the main queue
- /// - Returns: A new Request Task
- public func requestTask(with request: Request, completionHandler: Request.Callback?) -> RequestTask {
- let dataTask = session.dataTask(with: buildURLRequest(for: request)) { (data, response, error) in
- DispatchQueue.main.async {
- Client.completeRequest(request, callback: completionHandler)(data, response, error)
- }
- }
- return RequestTask(request: request, dataTask: dataTask)
- }
-
- /// Send the given Request to Omise API
- ///
- /// - Parameters:
- /// - request: Request with a parameter to create a new Omise API object
- /// - completionHandler: Compleation Handler closure that will be called when the request task is finished
- /// - Returns: A new Request Task
- @discardableResult
- public func send(_ request: Request, completionHandler: Request.Callback?) -> RequestTask {
- let task = requestTask(with: request, completionHandler: completionHandler)
- defer {
- task.resume()
- }
- return task
- }
-
- // swiftlint:disable:next function_body_length
- public func capabilityDataWithCompletionHandler(_ completionHandler: ((RequestResult) -> Void)?) {
- // swiftlint:disable:next closure_body_length
- let dataTask = session.dataTask(with: buildCapabilityAPIURLRequest()) { (data, response, error) in
- guard let completionHandler = completionHandler else { return } // nobody around to hear the leaf falls
-
- var result: RequestResult
- defer {
- switch result {
- case .success:
- os_log("Request succeed: Capability", log: sdkLogObject, type: .debug)
- case .failure(let error):
- os_log("Request failed %{public}@", log: sdkLogObject, type: .info, error.localizedDescription)
- }
-
- DispatchQueue.main.async {
- completionHandler(result)
- }
- }
-
- if let error = error {
- result = .failure(error)
- return
- }
-
- guard let httpResponse = response as? HTTPURLResponse else {
- let error = OmiseError.unexpected(error: .noErrorNorResponse, underlying: nil)
- result = .failure(error)
- return
- }
-
- let decoder = JSONDecoder()
- decoder.dateDecodingStrategy = .formatted(Client.jsonDateFormatter)
-
- switch httpResponse.statusCode {
- case 400..<600:
- guard let data = data else {
- let error = OmiseError.unexpected(error: .httpErrorWithNoData, underlying: nil)
- result = .failure(error)
- return
- }
-
- do {
- result = .failure(try decoder.decode(OmiseError.self, from: data))
- } catch {
- let omiseError = OmiseError.unexpected(error: .httpErrorResponseWithInvalidData, underlying: error)
- result = .failure(omiseError)
- }
-
- case 200..<300:
- guard let data = data else {
- let error = OmiseError.unexpected(error: .httpSuccessWithNoData, underlying: nil)
- result = .failure(error)
- return
- }
-
- do {
- let capability = try decoder.decode(Capability.self, from: data)
- Self.sharedCapability = capability
- result = .success(capability)
- } catch {
- let omiseError = OmiseError.unexpected(error: .httpSuccessWithInvalidData, underlying: error)
- result = .failure(omiseError)
- }
-
- default:
- let error = OmiseError.unexpected(error: .unrecognizedHTTPStatusCode(code: httpResponse.statusCode), underlying: nil)
- result = .failure(error)
- }
- }
- dataTask.resume()
- }
-
- public func retrieveChargeStatusWithCompletionHandler(from tokenID: String, completionHandler: @escaping ((Result) -> Void)) {
- // swiftlint:disable:next closure_body_length
- let dataTask = session.dataTask(with: buildRetrieveTokenURLRequest(from: tokenID)) { (data, response, error) in
- var result: Result
- defer {
- DispatchQueue.main.async {
- completionHandler(result)
- }
- }
-
- if let error = error {
- result = .failure(error)
- return
- }
-
- guard let httpResponse = response as? HTTPURLResponse else {
- let error = OmiseError.unexpected(error: .noErrorNorResponse, underlying: nil)
- result = .failure(error)
- return
- }
-
- let decoder = JSONDecoder()
- decoder.dateDecodingStrategy = .formatted(Client.jsonDateFormatter)
-
- switch httpResponse.statusCode {
- case 400..<600:
- guard let data = data else {
- let error = OmiseError.unexpected(error: .httpErrorWithNoData, underlying: nil)
- result = .failure(error)
- return
- }
-
- do {
- result = .failure(try decoder.decode(OmiseError.self, from: data))
- } catch {
- let omiseError = OmiseError.unexpected(error: .httpErrorResponseWithInvalidData, underlying: error)
- result = .failure(omiseError)
- }
-
- case 200..<300:
- guard let data = data else {
- let error = OmiseError.unexpected(error: .httpSuccessWithNoData, underlying: nil)
- result = .failure(error)
- return
- }
-
- do {
- let token = try decoder.decode(Token.self, from: data)
- result = .success(token.chargeStatus)
- } catch {
- let omiseError = OmiseError.unexpected(error: .httpSuccessWithInvalidData, underlying: error)
- result = .failure(omiseError)
- }
-
- default:
- let error = OmiseError.unexpected(error: .unrecognizedHTTPStatusCode(code: httpResponse.statusCode), underlying: nil)
- result = .failure(error)
- }
- }
- dataTask.resume()
- }
-
- public func pollingChargeStatusWithCompletionHandler(from tokenID: String, completionHandler: @escaping (Result) -> Void) {
- let maximumNumberOfPollingAttempts = 10
- var currentPollingAttempt = 0
-
- var isPolling = true
-
- let timer = Timer.scheduledTimer(withTimeInterval: 3.0, repeats: true) { (timer) in
- if !isPolling {
- firePolling(with: timer)
- }
- }
-
- func firePolling(with timer: Timer) {
- currentPollingAttempt += 1
- isPolling = true
-
- retrieveChargeStatusWithCompletionHandler(from: tokenID) { (result) in
- isPolling = false
-
- switch result {
- case .success(let latestChargeStatus):
- switch latestChargeStatus {
- case .successful, .failed, .expired, .reversed:
- timer.invalidate()
- completionHandler(.success(latestChargeStatus))
- default:
- break
- }
-
- if currentPollingAttempt == maximumNumberOfPollingAttempts {
- timer.invalidate()
- completionHandler(.success(latestChargeStatus))
- }
- case .failure(let error):
- timer.invalidate()
- completionHandler(.failure(error))
- }
- }
- }
-
- // Start polling
- firePolling(with: timer)
- }
-}
-
-// MARK: - URL Request related methods
-extension Client {
-
- private static let omiseAPIContentType = "application/json; charset=utf8"
- private static let omiseAPIVersion = "2019-05-29"
-
- private func buildURLRequest(for request: Request) -> URLRequest {
- var urlRequest = URLRequest(url: T.postURL)
- urlRequest.httpMethod = "POST"
- let encoder = Client.makeJSONEncoder()
- urlRequest.httpBody = try? encoder.encode(request.parameter)
- urlRequest.setValue(Client.encodeAuthorizationHeader(publicKey), forHTTPHeaderField: HTTPHeaders.authorization.rawValue)
- urlRequest.setValue(userAgent ?? Client.defaultUserAgent, forHTTPHeaderField: HTTPHeaders.userAgent.rawValue)
- urlRequest.setValue(Client.omiseAPIContentType, forHTTPHeaderField: HTTPHeaders.contentType.rawValue)
- urlRequest.setValue(Client.omiseAPIVersion, forHTTPHeaderField: HTTPHeaders.omiseVersion.rawValue)
- return urlRequest
- }
-
- private func buildCapabilityAPIURLRequest() -> URLRequest {
- var urlRequest = URLRequest(url: Configuration.default.environment.capabilityURL)
- urlRequest.httpMethod = "GET"
- urlRequest.setValue(Client.encodeAuthorizationHeader(publicKey), forHTTPHeaderField: HTTPHeaders.authorization.rawValue)
- urlRequest.setValue(userAgent ?? Client.defaultUserAgent, forHTTPHeaderField: HTTPHeaders.userAgent.rawValue)
- urlRequest.setValue(Client.omiseAPIContentType, forHTTPHeaderField: HTTPHeaders.contentType.rawValue)
- urlRequest.setValue(Client.omiseAPIVersion, forHTTPHeaderField: HTTPHeaders.omiseVersion.rawValue)
- return urlRequest
- }
-
- private func buildRetrieveTokenURLRequest(from tokenID: String) -> URLRequest {
- var urlRequest = URLRequest(url: Configuration.default.environment.tokenURL.appendingPathComponent(tokenID))
- urlRequest.httpMethod = "GET"
- urlRequest.setValue(Client.encodeAuthorizationHeader(publicKey), forHTTPHeaderField: HTTPHeaders.authorization.rawValue)
- urlRequest.setValue(userAgent ?? Client.defaultUserAgent, forHTTPHeaderField: HTTPHeaders.userAgent.rawValue)
- urlRequest.setValue(Client.omiseAPIContentType, forHTTPHeaderField: HTTPHeaders.contentType.rawValue)
- urlRequest.setValue(Client.omiseAPIVersion, forHTTPHeaderField: HTTPHeaders.omiseVersion.rawValue)
- return urlRequest
- }
-
- private static func encodeAuthorizationHeader(_ publicKey: String) -> String {
- let data = publicKey.data(using: String.Encoding.utf8, allowLossyConversion: false)
- let base64 = data?.base64EncodedString()
- return "Basic \(base64 ?? "")"
- }
-
- static func makeJSONEncoder() -> JSONEncoder {
- let encoder = JSONEncoder()
- encoder.dateEncodingStrategy = .formatted(Client.jsonDateFormatter)
- return encoder
- }
-
- static func makeJSONDecoder(for request: Request?) -> JSONDecoder {
- let decoder = JSONDecoder()
- if let request = request as? Request