@jimrising/easymerchantsdk-react-native 1.3.9 → 1.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.idea/caches/deviceStreaming.xml +77 -0
- package/README.md +140 -81
- package/android/.gradle/8.10/checksums/checksums.lock +0 -0
- package/android/.gradle/8.10/checksums/md5-checksums.bin +0 -0
- package/android/.gradle/8.10/checksums/sha1-checksums.bin +0 -0
- package/android/.gradle/8.10/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/8.9/checksums/checksums.lock +0 -0
- package/android/.gradle/8.9/checksums/md5-checksums.bin +0 -0
- package/android/.gradle/8.9/checksums/sha1-checksums.bin +0 -0
- package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/buildOutputCleanup/cache.properties +1 -1
- package/android/build/.transforms/20e1216b87bd06eaab3c9e5c68d4267a/results.bin +1 -0
- package/android/build/.transforms/20e1216b87bd06eaab3c9e5c68d4267a/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/reactlibrary/BuildConfig.dex +0 -0
- package/android/build/.transforms/20e1216b87bd06eaab3c9e5c68d4267a/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/reactlibrary/RNEasymerchantsdkModule$1.dex +0 -0
- package/android/build/.transforms/20e1216b87bd06eaab3c9e5c68d4267a/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/reactlibrary/RNEasymerchantsdkModule$2.dex +0 -0
- package/android/build/.transforms/20e1216b87bd06eaab3c9e5c68d4267a/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/reactlibrary/RNEasymerchantsdkModule.dex +0 -0
- package/android/build/.transforms/20e1216b87bd06eaab3c9e5c68d4267a/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/reactlibrary/RNEasymerchantsdkPackage.dex +0 -0
- package/android/build/.transforms/20e1216b87bd06eaab3c9e5c68d4267a/transformed/bundleLibRuntimeToDirDebug/desugar_graph.bin +0 -0
- package/android/build/.transforms/e9a664a11ce12edf79cd87b1e07aa243/results.bin +1 -0
- package/android/build/.transforms/e9a664a11ce12edf79cd87b1e07aa243/transformed/classes/classes_dex/classes.dex +0 -0
- package/android/build/generated/source/buildConfig/debug/com/reactlibrary/BuildConfig.java +10 -0
- package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/AndroidManifest.xml +7 -0
- package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/output-metadata.json +18 -0
- package/android/build/intermediates/aar_metadata/debug/writeDebugAarMetadata/aar-metadata.properties +6 -0
- package/android/build/intermediates/annotation_processor_list/debug/javaPreCompileDebug/annotationProcessors.json +1 -0
- package/android/build/intermediates/compile_library_classes_jar/debug/bundleLibCompileToJarDebug/classes.jar +0 -0
- package/android/build/intermediates/compile_r_class_jar/debug/generateDebugRFile/R.jar +0 -0
- package/android/build/intermediates/compile_symbol_list/debug/generateDebugRFile/R.txt +0 -0
- package/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +1 -0
- package/android/build/intermediates/incremental/debug/packageDebugResources/merger.xml +2 -0
- package/android/build/intermediates/incremental/mergeDebugJniLibFolders/merger.xml +2 -0
- package/android/build/intermediates/incremental/mergeDebugShaders/merger.xml +2 -0
- package/android/build/intermediates/incremental/packageDebugAssets/merger.xml +2 -0
- package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/reactlibrary/BuildConfig.class +0 -0
- package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/reactlibrary/RNEasymerchantsdkModule$1.class +0 -0
- package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/reactlibrary/RNEasymerchantsdkModule$2.class +0 -0
- package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/reactlibrary/RNEasymerchantsdkModule.class +0 -0
- package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/reactlibrary/RNEasymerchantsdkPackage.class +0 -0
- package/android/build/intermediates/local_only_symbol_list/debug/parseDebugLocalResources/R-def.txt +2 -0
- package/android/build/intermediates/manifest_merge_blame_file/debug/processDebugManifest/manifest-merger-blame-debug-report.txt +7 -0
- package/android/build/intermediates/merged_manifest/debug/processDebugManifest/AndroidManifest.xml +7 -0
- package/android/build/intermediates/navigation_json/debug/extractDeepLinksDebug/navigation.json +1 -0
- package/android/build/intermediates/nested_resources_validation_report/debug/generateDebugResources/nestedResourcesValidationReport.txt +1 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/reactlibrary/BuildConfig.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/reactlibrary/RNEasymerchantsdkModule$1.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/reactlibrary/RNEasymerchantsdkModule$2.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/reactlibrary/RNEasymerchantsdkModule.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/reactlibrary/RNEasymerchantsdkPackage.class +0 -0
- package/android/build/intermediates/runtime_library_classes_jar/debug/bundleLibRuntimeToJarDebug/classes.jar +0 -0
- package/android/build/intermediates/symbol_list_with_package_name/debug/generateDebugRFile/package-aware-r.txt +1 -0
- package/android/build/outputs/logs/manifest-merger-debug-report.txt +17 -0
- package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/RNEasymerchantsdkModule$1.class.uniqueId2 +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/RNEasymerchantsdkModule$2.class.uniqueId0 +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/RNEasymerchantsdkModule.class.uniqueId3 +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/RNEasymerchantsdkPackage.class.uniqueId1 +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin +0 -0
- package/android/build.gradle +4 -1
- package/android/src/main/java/com/reactlibrary/RNEasymerchantsdkModule.java +158 -36
- package/android/src/main/java/com/reactlibrary/RNEasymerchantsdkPackage.java +45 -13
- package/ios/Classes/EasyMerchantSdk.m +106 -55
- package/ios/Classes/EasyMerchantSdk.swift +199 -77
- package/ios/Classes/EasyPayViewController.swift +1 -1
- package/ios/CustomComponents/DatePickerHandler.swift +15 -4
- package/ios/EnvironmentConfig.swift +32 -30
- package/ios/Models/Request.swift +176 -14
- package/ios/Models/Result.swift +12 -5
- package/ios/Pods/ViewControllers/AdditionalInfoVC.swift +855 -366
- package/ios/Pods/ViewControllers/BaseVC.swift +51 -36
- package/ios/Pods/ViewControllers/BillingInfoVC/BillingInfoVC.swift +1985 -178
- package/ios/Pods/ViewControllers/CountryListVC.swift +20 -1
- package/ios/Pods/ViewControllers/CustomOverlay.swift +199 -0
- package/ios/Pods/ViewControllers/EmailVerificationVC.swift +74 -5
- package/ios/Pods/ViewControllers/GrailPayVC.swift +131 -107
- package/ios/Pods/ViewControllers/OTPVerificationVC.swift +296 -106
- package/ios/Pods/ViewControllers/PaymentDoneVC.swift +35 -26
- package/ios/Pods/ViewControllers/PaymentInformation/PaymentInfoVC.swift +1276 -545
- package/ios/Pods/ViewControllers/ThreeDSecurePaymentDoneVC.swift +607 -24
- package/ios/easymerchantsdk.podspec +1 -1
- package/ios/easymerchantsdk.storyboard +1388 -1165
- package/package.json +1 -1
|
@@ -15,7 +15,7 @@ protocol BillingInfoVCDelegate: AnyObject {
|
|
|
15
15
|
|
|
16
16
|
class BillingInfoVC: BaseVC {
|
|
17
17
|
|
|
18
|
-
@IBOutlet weak var viewBillingInfo: UIView!
|
|
18
|
+
// @IBOutlet weak var viewBillingInfo: UIView!
|
|
19
19
|
@IBOutlet weak var btnPrevious: UIButton!
|
|
20
20
|
@IBOutlet weak var viewCountryList: UIView!
|
|
21
21
|
@IBOutlet weak var tblViewCountryList: UITableView!
|
|
@@ -51,12 +51,14 @@ class BillingInfoVC: BaseVC {
|
|
|
51
51
|
var stateList: [[String: Any]] = []
|
|
52
52
|
var cityList: [[String: Any]] = []
|
|
53
53
|
|
|
54
|
-
var billingInfoData: [String: Any]?
|
|
54
|
+
// var billingInfoData: [String: Any]?
|
|
55
|
+
var billingInfoData: Data?
|
|
55
56
|
|
|
56
57
|
var cardNumber: String?
|
|
57
58
|
var expiryDate: String?
|
|
58
59
|
var cvv: String?
|
|
59
60
|
var nameOnCard: String?
|
|
61
|
+
var userEmail: String?
|
|
60
62
|
|
|
61
63
|
//Banking Params
|
|
62
64
|
var accountName: String?
|
|
@@ -77,6 +79,7 @@ class BillingInfoVC: BaseVC {
|
|
|
77
79
|
var cvvText: String?
|
|
78
80
|
|
|
79
81
|
var isFrom = String()
|
|
82
|
+
var isFromm = String()
|
|
80
83
|
|
|
81
84
|
//From Regular Saved Bank Accounts
|
|
82
85
|
var customerID: String?
|
|
@@ -89,13 +92,26 @@ class BillingInfoVC: BaseVC {
|
|
|
89
92
|
var chosenPlan: String?
|
|
90
93
|
var startDate: String?
|
|
91
94
|
|
|
95
|
+
//GrailPay Params
|
|
96
|
+
var grailPayAccountID: String?
|
|
97
|
+
var selectedGrailPayAccountType: String?
|
|
98
|
+
var selectedGrailPayAccountName: String?
|
|
99
|
+
|
|
100
|
+
var billingInfo: [FieldItem]?
|
|
101
|
+
var additionalInfo: [FieldItem]?
|
|
102
|
+
var visibility: FieldsVisibility?
|
|
103
|
+
var fieldSection: FieldSection?
|
|
104
|
+
|
|
105
|
+
var easyPayDelegate: EasyPayViewControllerDelegate?
|
|
106
|
+
|
|
92
107
|
override func viewDidLoad() {
|
|
93
108
|
super.viewDidLoad()
|
|
109
|
+
updateNextButtonTitle()
|
|
94
110
|
btnSelectState.isHidden = true
|
|
95
111
|
btnSelectCity.isHidden = true
|
|
96
112
|
|
|
97
113
|
uiFinishingTouchElements()
|
|
98
|
-
configureFieldVisibility()
|
|
114
|
+
// configureFieldVisibility()
|
|
99
115
|
setupShadowForListViews()
|
|
100
116
|
|
|
101
117
|
tblViewCountryList.delegate = self
|
|
@@ -123,37 +139,44 @@ class BillingInfoVC: BaseVC {
|
|
|
123
139
|
tapGesture.cancelsTouchesInView = false
|
|
124
140
|
self.view.addGestureRecognizer(tapGesture)
|
|
125
141
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
if let country = billingInfoData["country"] as? String {
|
|
135
|
-
txtFieldCountry.text = country
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if let state = billingInfoData["state"] as? String {
|
|
139
|
-
txtFieldState.text = state
|
|
140
|
-
}
|
|
142
|
+
guard let billingInfoData = billingInfoData else {
|
|
143
|
+
print("No billing info data")
|
|
144
|
+
return
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
do {
|
|
148
|
+
let decodedFieldSection = try JSONDecoder().decode(FieldSection.self, from: billingInfoData)
|
|
149
|
+
self.fieldSection = decodedFieldSection // <--- Assign here!
|
|
141
150
|
|
|
142
|
-
|
|
143
|
-
|
|
151
|
+
// Fill Billing Fields UI
|
|
152
|
+
for item in decodedFieldSection.billing {
|
|
153
|
+
switch item.name {
|
|
154
|
+
case "address":
|
|
155
|
+
txtFieldAddress.text = item.value
|
|
156
|
+
case "country":
|
|
157
|
+
txtFieldCountry.text = item.value
|
|
158
|
+
case "state":
|
|
159
|
+
txtFieldState.text = item.value
|
|
160
|
+
case "city":
|
|
161
|
+
txtFieldCity.text = item.value
|
|
162
|
+
case "postal_code":
|
|
163
|
+
txtFieldPostalCode.text = item.value
|
|
164
|
+
default:
|
|
165
|
+
break
|
|
166
|
+
}
|
|
144
167
|
}
|
|
168
|
+
// Now that fieldSection is set, update star labels visibility
|
|
169
|
+
configureFieldVisibility()
|
|
145
170
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
171
|
+
} catch {
|
|
172
|
+
print("Failed to decode billing info data: \(error)")
|
|
149
173
|
}
|
|
150
174
|
|
|
151
|
-
print(selectedCard ?? "")
|
|
152
175
|
}
|
|
153
176
|
|
|
154
177
|
override func viewWillAppear(_ animated: Bool) {
|
|
155
178
|
super.viewWillAppear(animated)
|
|
156
|
-
|
|
179
|
+
updateNextButtonTitle()
|
|
157
180
|
uiFinishingTouchElements()
|
|
158
181
|
|
|
159
182
|
keyboardObserver.animateChanges({ [self] height in
|
|
@@ -171,6 +194,38 @@ class BillingInfoVC: BaseVC {
|
|
|
171
194
|
keyboardObserver.invalidate()
|
|
172
195
|
}
|
|
173
196
|
|
|
197
|
+
private func updateNextButtonTitle() {
|
|
198
|
+
guard let request = request else { return }
|
|
199
|
+
|
|
200
|
+
if let billingInfoData = request.billingInfoData,
|
|
201
|
+
let fieldSection = try? JSONDecoder().decode(FieldSection.self, from: billingInfoData) {
|
|
202
|
+
|
|
203
|
+
let isAdditionalVisible = fieldSection.visibility.additional
|
|
204
|
+
let isBillingVisible = fieldSection.visibility.billing
|
|
205
|
+
let amountText = String(format: "$%.2f", request.amount ?? 0)
|
|
206
|
+
let submitText = request.submitButtonText ?? "Submit"
|
|
207
|
+
|
|
208
|
+
if !isAdditionalVisible {
|
|
209
|
+
// Only billing info is visible
|
|
210
|
+
btnNext.setTitle("\(submitText) (\(amountText))", for: .normal)
|
|
211
|
+
} else {
|
|
212
|
+
// Additional info is visible
|
|
213
|
+
let suffix = isBillingVisible ? "(Additional Info)" : ""
|
|
214
|
+
btnNext.setTitle("\(submitText) \(suffix)", for: .normal)
|
|
215
|
+
}
|
|
216
|
+
} else {
|
|
217
|
+
let amountValue = request.amount ?? 0
|
|
218
|
+
let amountText = String(format: "$%.2f", amountValue)
|
|
219
|
+
let submitText = request.submitButtonText
|
|
220
|
+
|
|
221
|
+
let defaultTitle = (submitText?.isEmpty == false)
|
|
222
|
+
? "\(submitText!) (\(amountText))"
|
|
223
|
+
: "Pay Now (\(amountText))"
|
|
224
|
+
|
|
225
|
+
btnNext.setTitle(defaultTitle, for: .normal)
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
174
229
|
func uiFinishingTouchElements() {
|
|
175
230
|
// Set background color for the main view
|
|
176
231
|
if let containerBGcolor = UserStoreSingleton.shared.container_bg_col,
|
|
@@ -199,10 +254,10 @@ class BillingInfoVC: BaseVC {
|
|
|
199
254
|
if let secondaryFontColor = UserStoreSingleton.shared.secondary_font_col,
|
|
200
255
|
let placeholderColor = UIColor(hex: secondaryFontColor) {
|
|
201
256
|
lblEasyMerchant.textColor = placeholderColor
|
|
202
|
-
viewBillingInfo.layer.borderColor = placeholderColor.cgColor
|
|
257
|
+
// viewBillingInfo.layer.borderColor = placeholderColor.cgColor
|
|
203
258
|
}
|
|
204
259
|
else {
|
|
205
|
-
viewBillingInfo.layer.borderColor = UIColor.systemGray.cgColor
|
|
260
|
+
// viewBillingInfo.layer.borderColor = UIColor.systemGray.cgColor
|
|
206
261
|
}
|
|
207
262
|
|
|
208
263
|
if let borderRadiusString = UserStoreSingleton.shared.border_radious,
|
|
@@ -210,17 +265,17 @@ class BillingInfoVC: BaseVC {
|
|
|
210
265
|
btnNext.layer.cornerRadius = CGFloat(borderRadius) // Set corner radius
|
|
211
266
|
btnPrevious.layer.cornerRadius = CGFloat(borderRadius)
|
|
212
267
|
btnPrevious.layer.borderWidth = 1
|
|
213
|
-
viewBillingInfo.layer.cornerRadius = CGFloat(borderRadius)
|
|
214
|
-
viewBillingInfo.layer.borderWidth = 1
|
|
268
|
+
// viewBillingInfo.layer.cornerRadius = CGFloat(borderRadius)
|
|
269
|
+
// viewBillingInfo.layer.borderWidth = 1
|
|
215
270
|
} else {
|
|
216
271
|
btnNext.layer.cornerRadius = 8 // Default value
|
|
217
272
|
btnPrevious.layer.cornerRadius = 8
|
|
218
|
-
viewBillingInfo.layer.borderWidth = 1
|
|
219
|
-
viewBillingInfo.layer.cornerRadius = 8
|
|
273
|
+
// viewBillingInfo.layer.borderWidth = 1
|
|
274
|
+
// viewBillingInfo.layer.cornerRadius = 8
|
|
220
275
|
}
|
|
221
276
|
btnNext.layer.masksToBounds = true // Ensure the corners are clipped properly
|
|
222
277
|
btnPrevious.layer.masksToBounds = true
|
|
223
|
-
viewBillingInfo.layer.masksToBounds = true
|
|
278
|
+
// viewBillingInfo.layer.masksToBounds = true
|
|
224
279
|
|
|
225
280
|
if let primaryFontColor = UserStoreSingleton.shared.primary_font_col,
|
|
226
281
|
let uiColor = UIColor(hex: primaryFontColor) {
|
|
@@ -236,19 +291,34 @@ class BillingInfoVC: BaseVC {
|
|
|
236
291
|
}
|
|
237
292
|
}
|
|
238
293
|
|
|
294
|
+
private func getFieldValue(for name: String) -> String? {
|
|
295
|
+
return fieldSection?.billing.first(where: { $0.name == name })?.value
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
private func setFieldValue(_ name: String, to value: String?) {
|
|
299
|
+
guard let index = fieldSection?.billing.firstIndex(where: { $0.name == name }) else { return }
|
|
300
|
+
fieldSection?.billing[index].value = value ?? ""
|
|
301
|
+
}
|
|
302
|
+
|
|
239
303
|
private func configureFieldVisibility() {
|
|
240
|
-
let
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
304
|
+
guard let billingFields = fieldSection?.billing else {
|
|
305
|
+
print("No billing fields found")
|
|
306
|
+
return
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
func isFieldRequired(_ name: String) -> Bool {
|
|
310
|
+
let required = billingFields.first(where: { $0.name == name })?.required == true
|
|
311
|
+
print("Field \(name) required: \(required)")
|
|
312
|
+
return required
|
|
313
|
+
}
|
|
245
314
|
|
|
246
315
|
DispatchQueue.main.async {
|
|
247
|
-
self.lblStarAddressField.isHidden = (address
|
|
248
|
-
self.lblStarCountryField.isHidden = (country
|
|
249
|
-
self.lblStarStateField.isHidden = (state
|
|
250
|
-
self.lblStarCityField.isHidden = (city
|
|
251
|
-
self.lblStarPostalCodeField.isHidden = (
|
|
316
|
+
self.lblStarAddressField.isHidden = !isFieldRequired("address")
|
|
317
|
+
self.lblStarCountryField.isHidden = !isFieldRequired("country")
|
|
318
|
+
self.lblStarStateField.isHidden = !isFieldRequired("state")
|
|
319
|
+
self.lblStarCityField.isHidden = !isFieldRequired("city")
|
|
320
|
+
self.lblStarPostalCodeField.isHidden = !isFieldRequired("postal_code")
|
|
321
|
+
print("Star visibility set")
|
|
252
322
|
self.view.layoutIfNeeded()
|
|
253
323
|
}
|
|
254
324
|
}
|
|
@@ -502,11 +572,13 @@ class BillingInfoVC: BaseVC {
|
|
|
502
572
|
}
|
|
503
573
|
|
|
504
574
|
func updateBillingInfoData() {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
575
|
+
setFieldValue("address", to: txtFieldAddress.text)
|
|
576
|
+
setFieldValue("country", to: txtFieldCountry.text)
|
|
577
|
+
setFieldValue("state", to: txtFieldState.text)
|
|
578
|
+
setFieldValue("city", to: txtFieldCity.text)
|
|
579
|
+
setFieldValue("postal_code", to: txtFieldPostalCode.text)
|
|
580
|
+
|
|
581
|
+
billingInfo = fieldSection?.billing
|
|
510
582
|
}
|
|
511
583
|
|
|
512
584
|
@IBAction func actionBtnSelectCountry(_ sender: UIButton) {
|
|
@@ -534,190 +606,1925 @@ class BillingInfoVC: BaseVC {
|
|
|
534
606
|
}
|
|
535
607
|
|
|
536
608
|
@IBAction func actionBtnNext(_ sender: UIButton) {
|
|
609
|
+
guard let request = request else { return }
|
|
610
|
+
|
|
611
|
+
// MARK: - Validate Billing Fields
|
|
537
612
|
if !lblStarAddressField.isHidden &&
|
|
538
613
|
txtFieldAddress.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true {
|
|
539
614
|
showAlert(title: "Missing Information", message: "Please enter your address.")
|
|
540
615
|
return
|
|
541
|
-
}
|
|
542
|
-
else if !lblStarCountryField.isHidden &&
|
|
616
|
+
} else if !lblStarCountryField.isHidden &&
|
|
543
617
|
txtFieldCountry.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true {
|
|
544
618
|
showAlert(title: "Missing Information", message: "Please select your country.")
|
|
545
619
|
return
|
|
546
|
-
}
|
|
547
|
-
else if !lblStarStateField.isHidden &&
|
|
620
|
+
} else if !lblStarStateField.isHidden &&
|
|
548
621
|
txtFieldState.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true {
|
|
549
622
|
showAlert(title: "Missing Information", message: "Please select your state.")
|
|
550
623
|
return
|
|
551
|
-
}
|
|
552
|
-
else if !lblStarCityField.isHidden &&
|
|
624
|
+
} else if !lblStarCityField.isHidden &&
|
|
553
625
|
txtFieldCity.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true {
|
|
554
626
|
showAlert(title: "Missing Information", message: "Please select your city.")
|
|
555
627
|
return
|
|
556
|
-
}
|
|
557
|
-
else if !lblStarPostalCodeField.isHidden &&
|
|
628
|
+
} else if !lblStarPostalCodeField.isHidden &&
|
|
558
629
|
txtFieldPostalCode.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true {
|
|
559
630
|
showAlert(title: "Missing Information", message: "Please enter your postal code.")
|
|
560
631
|
return
|
|
561
632
|
}
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
vc.
|
|
595
|
-
vc.
|
|
596
|
-
vc.
|
|
597
|
-
vc.
|
|
633
|
+
|
|
634
|
+
// Update billing info
|
|
635
|
+
updateBillingInfoData()
|
|
636
|
+
|
|
637
|
+
guard let updatedFieldSection = fieldSection else {
|
|
638
|
+
print("Missing updated fieldSection")
|
|
639
|
+
return
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
let isAdditionalVisible = updatedFieldSection.visibility.additional
|
|
643
|
+
|
|
644
|
+
// Get updated billingInfoData after updateBillingInfoData()
|
|
645
|
+
guard let updatedBillingData = try? JSONEncoder().encode(fieldSection),
|
|
646
|
+
let updatedFieldSection = try? JSONDecoder().decode(FieldSection.self, from: updatedBillingData) else {
|
|
647
|
+
print("Failed to encode or decode updated billing info")
|
|
648
|
+
return
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// MARK: - Card Flow
|
|
652
|
+
if selectedPaymentMethod == "Card" {
|
|
653
|
+
|
|
654
|
+
if isAdditionalVisible {
|
|
655
|
+
// ✅ Go to AdditionalInfoVC
|
|
656
|
+
let vc = easymerchantsdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
|
|
657
|
+
vc.cardNumber = cardNumber
|
|
658
|
+
vc.expiryDate = expiryDate
|
|
659
|
+
vc.cvv = cvv
|
|
660
|
+
vc.nameOnCard = nameOnCard
|
|
661
|
+
vc.userEmail = userEmail
|
|
662
|
+
vc.billingInfoData = updatedBillingData
|
|
663
|
+
vc.fieldSection = updatedFieldSection
|
|
664
|
+
vc.billingInfo = updatedFieldSection.billing
|
|
665
|
+
vc.additionalInfo = updatedFieldSection.additional
|
|
666
|
+
vc.visibility = updatedFieldSection.visibility
|
|
667
|
+
vc.selectedPaymentMethod = selectedPaymentMethod
|
|
668
|
+
vc.isSavedForFuture = isSavedForFuture
|
|
669
|
+
vc.amount = Int(request.amount ?? 0)
|
|
670
|
+
vc.request = request
|
|
671
|
+
vc.chosenPlan = chosenPlan
|
|
672
|
+
vc.startDate = startDate
|
|
673
|
+
vc.isFrom = isFrom
|
|
674
|
+
if isFrom == "SavedCards" {
|
|
675
|
+
vc.selectedCard = selectedCard
|
|
676
|
+
vc.cvvText = cvvText
|
|
677
|
+
}
|
|
678
|
+
else if isFrom == "AddNewCard" {
|
|
679
|
+
vc.isSavedNewCard = isSavedNewCard
|
|
680
|
+
}
|
|
681
|
+
navigationController?.pushViewController(vc, animated: true)
|
|
598
682
|
|
|
599
|
-
|
|
683
|
+
}
|
|
684
|
+
else if !isAdditionalVisible && isSavedForFuture {
|
|
685
|
+
let vc = easymerchantsdk.instantiateViewController(withIdentifier: "EmailVerificationVC") as! EmailVerificationVC
|
|
686
|
+
vc.cardNumber = cardNumber
|
|
687
|
+
vc.expiryDate = expiryDate
|
|
688
|
+
vc.cvv = cvv
|
|
689
|
+
vc.nameOnCard = nameOnCard
|
|
690
|
+
vc.userEmail = userEmail
|
|
691
|
+
vc.billingInfoData = updatedBillingData
|
|
692
|
+
vc.fieldSection = updatedFieldSection
|
|
693
|
+
vc.selectedPaymentMethod = selectedPaymentMethod
|
|
694
|
+
vc.easyPayDelegate = easyPayDelegate
|
|
695
|
+
vc.request = request
|
|
600
696
|
vc.chosenPlan = chosenPlan
|
|
601
697
|
vc.startDate = startDate
|
|
698
|
+
vc.billingInfo = updatedFieldSection.billing
|
|
699
|
+
vc.additionalInfo = updatedFieldSection.additional
|
|
700
|
+
vc.visibility = updatedFieldSection.visibility
|
|
701
|
+
vc.isSavedForFuture = true
|
|
702
|
+
vc.amount = amount
|
|
703
|
+
navigationController?.pushViewController(vc, animated: true)
|
|
602
704
|
}
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
705
|
+
else if !isAdditionalVisible && isFrom == "SavedCards" {
|
|
706
|
+
paymentIntentFromShowCardApi()
|
|
707
|
+
}
|
|
708
|
+
else if !isAdditionalVisible && isSavedNewCard {
|
|
709
|
+
if isFrom == "AddNewCard" {
|
|
710
|
+
if request.secureAuthentication == true {
|
|
711
|
+
threeDSecurePaymentAddNewCardApi(customerId: UserStoreSingleton.shared.customerId)
|
|
712
|
+
}
|
|
713
|
+
else {
|
|
714
|
+
paymentIntentAddNewCardApi(customerId: UserStoreSingleton.shared.customerId)
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
else {
|
|
719
|
+
// ✅ Skip to Payment directly
|
|
720
|
+
if request.secureAuthentication == true {
|
|
721
|
+
threeDSecurePaymentApi()
|
|
722
|
+
} else {
|
|
723
|
+
paymentIntentApi()
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
else if selectedPaymentMethod == "Bank" {
|
|
728
|
+
if isAdditionalVisible {
|
|
729
|
+
// ✅ Go to AdditionalInfoVC
|
|
730
|
+
let vc = easymerchantsdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
|
|
731
|
+
vc.userEmail = userEmail
|
|
732
|
+
vc.billingInfoData = updatedBillingData
|
|
733
|
+
vc.fieldSection = updatedFieldSection
|
|
734
|
+
vc.billingInfo = updatedFieldSection.billing
|
|
735
|
+
vc.additionalInfo = updatedFieldSection.additional
|
|
736
|
+
vc.visibility = updatedFieldSection.visibility
|
|
737
|
+
vc.selectedPaymentMethod = selectedPaymentMethod
|
|
738
|
+
vc.isSavedForFuture = isSavedForFuture
|
|
739
|
+
vc.accountName = accountName
|
|
740
|
+
vc.routingNumber = routingNumber
|
|
741
|
+
vc.accountType = accountType
|
|
742
|
+
vc.accountNumber = accountNumber
|
|
743
|
+
vc.customerID = customerID
|
|
744
|
+
vc.accountID = accountID
|
|
745
|
+
vc.isFrom = isFrom
|
|
746
|
+
vc.isSavedNewAccount = isSavedNewAccount
|
|
747
|
+
vc.amount = Int(request.amount ?? 0)
|
|
748
|
+
vc.request = request
|
|
610
749
|
vc.chosenPlan = chosenPlan
|
|
611
750
|
vc.startDate = startDate
|
|
751
|
+
vc.grailPayAccountID = grailPayAccountID
|
|
752
|
+
vc.selectedGrailPayAccountType = selectedGrailPayAccountType
|
|
753
|
+
vc.selectedGrailPayAccountName = selectedGrailPayAccountName
|
|
754
|
+
navigationController?.pushViewController(vc, animated: true)
|
|
755
|
+
}
|
|
756
|
+
else if !isAdditionalVisible && isSavedForFuture {
|
|
757
|
+
let vc = easymerchantsdk.instantiateViewController(withIdentifier: "EmailVerificationVC") as! EmailVerificationVC
|
|
758
|
+
vc.accountName = accountName
|
|
759
|
+
vc.routingNumber = routingNumber
|
|
760
|
+
vc.accountType = accountType
|
|
761
|
+
vc.accountNumber = accountNumber
|
|
762
|
+
vc.userEmail = userEmail
|
|
763
|
+
vc.billingInfoData = updatedBillingData
|
|
764
|
+
vc.fieldSection = updatedFieldSection
|
|
765
|
+
vc.selectedPaymentMethod = selectedPaymentMethod
|
|
766
|
+
vc.easyPayDelegate = easyPayDelegate
|
|
767
|
+
vc.request = request
|
|
768
|
+
vc.chosenPlan = chosenPlan
|
|
769
|
+
vc.startDate = startDate
|
|
770
|
+
vc.billingInfo = updatedFieldSection.billing
|
|
771
|
+
vc.additionalInfo = updatedFieldSection.additional
|
|
772
|
+
vc.visibility = updatedFieldSection.visibility
|
|
773
|
+
vc.isSavedForFuture = isSavedForFuture
|
|
774
|
+
vc.isSavedNewAccount = isSavedNewAccount
|
|
775
|
+
vc.amount = amount
|
|
776
|
+
vc.grailPayAccountID = grailPayAccountID
|
|
777
|
+
vc.selectedGrailPayAccountType = selectedGrailPayAccountType
|
|
778
|
+
vc.selectedGrailPayAccountName = selectedGrailPayAccountName
|
|
779
|
+
navigationController?.pushViewController(vc, animated: true)
|
|
780
|
+
}
|
|
781
|
+
else if isFrom == "SavedBank" {
|
|
782
|
+
accountChargeSavedBankAccountApi()
|
|
783
|
+
}
|
|
784
|
+
else if isFrom == "NormalBankPayWithoutSave" {
|
|
785
|
+
accountChargeApi()
|
|
786
|
+
}
|
|
787
|
+
else if isFrom == "AddNewAccountWithoutSave" {
|
|
788
|
+
accountChargeApi()
|
|
789
|
+
}
|
|
790
|
+
else if isFrom == "AddNewAccountWithSave" {
|
|
791
|
+
accountChargeApi(customerId: UserStoreSingleton.shared.customerId)
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
else if selectedPaymentMethod == "GrailPay" {
|
|
795
|
+
if isAdditionalVisible {
|
|
796
|
+
// ✅ Go to AdditionalInfoVC
|
|
797
|
+
let vc = easymerchantsdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
|
|
798
|
+
vc.billingInfoData = updatedBillingData
|
|
799
|
+
vc.fieldSection = updatedFieldSection
|
|
800
|
+
vc.billingInfo = updatedFieldSection.billing
|
|
801
|
+
vc.additionalInfo = updatedFieldSection.additional
|
|
802
|
+
vc.visibility = updatedFieldSection.visibility
|
|
803
|
+
vc.selectedPaymentMethod = selectedPaymentMethod
|
|
804
|
+
vc.isSavedForFuture = isSavedForFuture
|
|
805
|
+
vc.isFrom = isFrom
|
|
806
|
+
vc.isSavedNewAccount = isSavedNewAccount
|
|
807
|
+
vc.amount = Int(request.amount ?? 0)
|
|
808
|
+
vc.request = request
|
|
809
|
+
vc.chosenPlan = chosenPlan
|
|
810
|
+
vc.startDate = startDate
|
|
811
|
+
vc.grailPayAccountID = grailPayAccountID
|
|
812
|
+
vc.selectedGrailPayAccountType = selectedGrailPayAccountType
|
|
813
|
+
vc.selectedGrailPayAccountName = selectedGrailPayAccountName
|
|
814
|
+
navigationController?.pushViewController(vc, animated: true)
|
|
815
|
+
}
|
|
816
|
+
else if !isAdditionalVisible && isSavedForFuture {
|
|
817
|
+
let vc = easymerchantsdk.instantiateViewController(withIdentifier: "EmailVerificationVC") as! EmailVerificationVC
|
|
818
|
+
vc.billingInfoData = updatedBillingData
|
|
819
|
+
vc.fieldSection = updatedFieldSection
|
|
820
|
+
vc.billingInfo = updatedFieldSection.billing
|
|
821
|
+
vc.additionalInfo = updatedFieldSection.additional
|
|
822
|
+
vc.visibility = updatedFieldSection.visibility
|
|
823
|
+
vc.selectedPaymentMethod = selectedPaymentMethod
|
|
824
|
+
vc.isSavedForFuture = isSavedForFuture
|
|
825
|
+
vc.isFrom = isFrom
|
|
826
|
+
vc.isSavedNewAccount = isSavedNewAccount
|
|
827
|
+
vc.amount = Int(request.amount ?? 0)
|
|
828
|
+
vc.request = request
|
|
829
|
+
vc.chosenPlan = chosenPlan
|
|
830
|
+
vc.startDate = startDate
|
|
831
|
+
vc.grailPayAccountID = grailPayAccountID
|
|
832
|
+
vc.selectedGrailPayAccountType = selectedGrailPayAccountType
|
|
833
|
+
vc.selectedGrailPayAccountName = selectedGrailPayAccountName
|
|
834
|
+
navigationController?.pushViewController(vc, animated: true)
|
|
835
|
+
}
|
|
836
|
+
else {
|
|
837
|
+
grailPayAccountChargeApi()
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
else if selectedPaymentMethod == "NewGrailPayAccount" {
|
|
841
|
+
if isAdditionalVisible {
|
|
842
|
+
// ✅ Go to AdditionalInfoVC
|
|
843
|
+
let vc = easymerchantsdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
|
|
844
|
+
vc.billingInfoData = updatedBillingData
|
|
845
|
+
vc.fieldSection = updatedFieldSection
|
|
846
|
+
vc.billingInfo = updatedFieldSection.billing
|
|
847
|
+
vc.additionalInfo = updatedFieldSection.additional
|
|
848
|
+
vc.visibility = updatedFieldSection.visibility
|
|
849
|
+
vc.selectedPaymentMethod = selectedPaymentMethod
|
|
850
|
+
vc.isSavedForFuture = isSavedForFuture
|
|
851
|
+
vc.isFrom = isFrom
|
|
852
|
+
vc.isSavedNewAccount = isSavedNewAccount
|
|
853
|
+
vc.amount = Int(request.amount ?? 0)
|
|
854
|
+
vc.request = request
|
|
855
|
+
vc.chosenPlan = chosenPlan
|
|
856
|
+
vc.startDate = startDate
|
|
857
|
+
vc.grailPayAccountID = grailPayAccountID
|
|
858
|
+
vc.selectedGrailPayAccountType = selectedGrailPayAccountType
|
|
859
|
+
vc.selectedGrailPayAccountName = selectedGrailPayAccountName
|
|
860
|
+
navigationController?.pushViewController(vc, animated: true)
|
|
861
|
+
}
|
|
862
|
+
else if !isAdditionalVisible && isSavedForFuture {
|
|
863
|
+
let vc = easymerchantsdk.instantiateViewController(withIdentifier: "EmailVerificationVC") as! EmailVerificationVC
|
|
864
|
+
vc.billingInfoData = updatedBillingData
|
|
865
|
+
vc.fieldSection = updatedFieldSection
|
|
866
|
+
vc.billingInfo = updatedFieldSection.billing
|
|
867
|
+
vc.additionalInfo = updatedFieldSection.additional
|
|
868
|
+
vc.visibility = updatedFieldSection.visibility
|
|
869
|
+
vc.selectedPaymentMethod = selectedPaymentMethod
|
|
870
|
+
vc.isSavedForFuture = isSavedForFuture
|
|
871
|
+
vc.isFrom = isFrom
|
|
872
|
+
vc.isSavedNewAccount = isSavedNewAccount
|
|
873
|
+
vc.amount = Int(request.amount ?? 0)
|
|
874
|
+
vc.request = request
|
|
875
|
+
vc.chosenPlan = chosenPlan
|
|
876
|
+
vc.startDate = startDate
|
|
877
|
+
vc.grailPayAccountID = grailPayAccountID
|
|
878
|
+
vc.selectedGrailPayAccountType = selectedGrailPayAccountType
|
|
879
|
+
vc.selectedGrailPayAccountName = selectedGrailPayAccountName
|
|
880
|
+
navigationController?.pushViewController(vc, animated: true)
|
|
881
|
+
}
|
|
882
|
+
else {
|
|
883
|
+
grailPayAccountChargeApi()
|
|
612
884
|
}
|
|
613
|
-
|
|
614
|
-
self.navigationController?.pushViewController(vc, animated: true)
|
|
615
885
|
}
|
|
616
886
|
}
|
|
617
887
|
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
888
|
+
// MARK: - Credit Card Charge Api
|
|
889
|
+
func paymentIntentApi() {
|
|
890
|
+
showLoadingIndicator()
|
|
891
|
+
|
|
892
|
+
let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.charges.path()
|
|
893
|
+
|
|
894
|
+
guard let serviceURL = URL(string: fullURL) else {
|
|
895
|
+
print("Invalid URL")
|
|
896
|
+
hideLoadingIndicator()
|
|
897
|
+
return
|
|
626
898
|
}
|
|
627
|
-
|
|
628
|
-
|
|
899
|
+
|
|
900
|
+
var urlRequest = URLRequest(url: serviceURL)
|
|
901
|
+
urlRequest.httpMethod = "POST"
|
|
902
|
+
urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
903
|
+
|
|
904
|
+
let token = UserStoreSingleton.shared.clientToken
|
|
905
|
+
print("Setting clientToken header: \(token ?? "None")")
|
|
906
|
+
urlRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
|
|
907
|
+
|
|
908
|
+
var params: [String: Any] = [
|
|
909
|
+
"name": nameOnCard ?? "",
|
|
910
|
+
"email": userEmail ?? "",
|
|
911
|
+
"card_number": cardNumber?.replacingOccurrences(of: " ", with: "") ?? "",
|
|
912
|
+
"cardholder_name": nameOnCard ?? "",
|
|
913
|
+
"exp_month": expiryDate?.components(separatedBy: "/").first ?? "",
|
|
914
|
+
"exp_year": expiryDate?.components(separatedBy: "/").last ?? "",
|
|
915
|
+
"cvc": cvv ?? "",
|
|
916
|
+
"currency": "usd",
|
|
917
|
+
"description": "Hosted payment checkout"
|
|
918
|
+
]
|
|
919
|
+
|
|
920
|
+
// Conditionally add billing info
|
|
921
|
+
if let visibility = visibility, visibility.billing == true,
|
|
922
|
+
let billing = billingInfo, !billing.isEmpty {
|
|
923
|
+
|
|
924
|
+
var billingInfoDict: [String: Any] = [:]
|
|
925
|
+
for item in billing {
|
|
926
|
+
billingInfoDict[item.name] = item.value
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
params["address"] = billingInfoDict["address"] as? String ?? ""
|
|
930
|
+
params["country"] = billingInfoDict["country"] as? String ?? ""
|
|
931
|
+
params["state"] = billingInfoDict["state"] as? String ?? ""
|
|
932
|
+
params["city"] = billingInfoDict["city"] as? String ?? ""
|
|
933
|
+
params["zip"] = billingInfoDict["postal_code"] as? String ?? ""
|
|
629
934
|
}
|
|
630
|
-
|
|
631
|
-
|
|
935
|
+
|
|
936
|
+
// Add these if recurring is enabled
|
|
937
|
+
if let req = request, req.is_recurring == true {
|
|
938
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
939
|
+
// Only send start_date if type is .custom and field is not empty
|
|
940
|
+
if let startDateText = startDate, !startDateText.isEmpty {
|
|
941
|
+
let inputFormatter = DateFormatter()
|
|
942
|
+
inputFormatter.dateFormat = "dd/MM/yyyy"
|
|
943
|
+
|
|
944
|
+
let outputFormatter = DateFormatter()
|
|
945
|
+
outputFormatter.dateFormat = "MM/dd/yyyy"
|
|
946
|
+
|
|
947
|
+
if let date = inputFormatter.date(from: startDateText) {
|
|
948
|
+
let apiFormattedDate = outputFormatter.string(from: date)
|
|
949
|
+
params["start_date"] = apiFormattedDate
|
|
950
|
+
} else {
|
|
951
|
+
print("Invalid date format in startDateText")
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
params["interval"] = chosenPlan?.lowercased()
|
|
632
957
|
}
|
|
633
|
-
|
|
634
|
-
|
|
958
|
+
|
|
959
|
+
print(params)
|
|
960
|
+
|
|
961
|
+
do {
|
|
962
|
+
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
963
|
+
urlRequest.httpBody = jsonData
|
|
964
|
+
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
965
|
+
print("JSON Payload: \(jsonString)")
|
|
966
|
+
}
|
|
967
|
+
} catch let error {
|
|
968
|
+
print("Error creating JSON data: \(error)")
|
|
969
|
+
hideLoadingIndicator()
|
|
970
|
+
return
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
let session = URLSession.shared
|
|
974
|
+
let task = session.dataTask(with: urlRequest) { (serviceData, serviceResponse, error) in
|
|
975
|
+
|
|
976
|
+
DispatchQueue.main.async {
|
|
977
|
+
self.hideLoadingIndicator()
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
if let error = error {
|
|
981
|
+
print("Error: \(error.localizedDescription)")
|
|
982
|
+
self.presentPaymentErrorVC(errorMessage: error.localizedDescription)
|
|
983
|
+
return
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
guard let httpResponse = serviceResponse as? HTTPURLResponse else {
|
|
987
|
+
print("Invalid response")
|
|
988
|
+
self.presentPaymentErrorVC(errorMessage: "Invalid response from server.")
|
|
989
|
+
return
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
|
|
993
|
+
if let data = serviceData {
|
|
994
|
+
do {
|
|
995
|
+
if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
|
|
996
|
+
print("Response Data: \(responseObject)")
|
|
997
|
+
|
|
998
|
+
if let status = responseObject["status"] as? Int, status == 0 {
|
|
999
|
+
let errorMessage = responseObject["message"] as? String ?? "Unknown error occurred."
|
|
1000
|
+
self.presentPaymentErrorVC(errorMessage: errorMessage)
|
|
1001
|
+
} else {
|
|
1002
|
+
DispatchQueue.main.async {
|
|
1003
|
+
if let paymentDoneVC = self.storyboard?.instantiateViewController(withIdentifier: "PaymentDoneVC") as? PaymentDoneVC {
|
|
1004
|
+
paymentDoneVC.chargeData = responseObject
|
|
1005
|
+
paymentDoneVC.selectedPaymentMethod = self.selectedPaymentMethod
|
|
1006
|
+
paymentDoneVC.easyPayDelegate = self.easyPayDelegate
|
|
1007
|
+
// Pass billing and additional info
|
|
1008
|
+
// Conditionally pass raw FieldItem array
|
|
1009
|
+
paymentDoneVC.visibility = self.visibility
|
|
1010
|
+
|
|
1011
|
+
if self.visibility?.billing == true {
|
|
1012
|
+
paymentDoneVC.billingInfoData = self.billingInfo
|
|
1013
|
+
var billingDict: [String: Any] = [:]
|
|
1014
|
+
self.billingInfo?.forEach { billingDict[$0.name] = $0.value }
|
|
1015
|
+
paymentDoneVC.billingInfo = billingDict
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
if self.visibility?.additional == true {
|
|
1019
|
+
paymentDoneVC.additionalInfoData = self.additionalInfo
|
|
1020
|
+
var additionalDict: [String: Any] = [:]
|
|
1021
|
+
self.additionalInfo?.forEach { additionalDict[$0.name] = $0.value }
|
|
1022
|
+
paymentDoneVC.additionalInfo = additionalDict
|
|
1023
|
+
}
|
|
1024
|
+
self.navigationController?.pushViewController(paymentDoneVC, animated: true)
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
} else {
|
|
1029
|
+
self.presentPaymentErrorVC(errorMessage: "Invalid JSON format")
|
|
1030
|
+
}
|
|
1031
|
+
} catch let jsonError {
|
|
1032
|
+
self.presentPaymentErrorVC(errorMessage: "Error parsing response: \(jsonError.localizedDescription)")
|
|
1033
|
+
}
|
|
1034
|
+
} else {
|
|
1035
|
+
self.presentPaymentErrorVC(errorMessage: "No data received from server.")
|
|
1036
|
+
}
|
|
1037
|
+
} else {
|
|
1038
|
+
if let data = serviceData,
|
|
1039
|
+
let responseObj = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
|
|
1040
|
+
let message = responseObj["message"] as? String {
|
|
1041
|
+
self.presentPaymentErrorVC(errorMessage: message)
|
|
1042
|
+
} else {
|
|
1043
|
+
self.presentPaymentErrorVC(errorMessage: "HTTP Status Code: \(httpResponse.statusCode)")
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
635
1046
|
}
|
|
1047
|
+
task.resume()
|
|
636
1048
|
}
|
|
637
1049
|
|
|
638
|
-
func
|
|
639
|
-
|
|
640
|
-
let
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
let cell = tableView.dequeueReusableCell(withIdentifier: "StateListTVC") as! StateListTVC
|
|
646
|
-
let state = stateList[indexPath.row]
|
|
647
|
-
cell.lblStateName.text = state["name"] as? String
|
|
648
|
-
return cell
|
|
649
|
-
} else if tableView == tblViewCityList {
|
|
650
|
-
let cell = tableView.dequeueReusableCell(withIdentifier: "CityListTVC") as! CityListTVC
|
|
651
|
-
let city = cityList[indexPath.row]
|
|
652
|
-
cell.lblCityName.text = city["city_name"] as? String
|
|
653
|
-
return cell
|
|
654
|
-
} else {
|
|
655
|
-
return UITableViewCell()
|
|
1050
|
+
func presentPaymentErrorVC(errorMessage: String) {
|
|
1051
|
+
DispatchQueue.main.async {
|
|
1052
|
+
if let paymentErrorVC = self.storyboard?.instantiateViewController(withIdentifier: "PaymentErrorVC") as? PaymentErrorVC {
|
|
1053
|
+
paymentErrorVC.errorMessage = errorMessage
|
|
1054
|
+
paymentErrorVC.easyPayDelegate = self.easyPayDelegate // Pass the reference here
|
|
1055
|
+
self.navigationController?.pushViewController(paymentErrorVC, animated: true)
|
|
1056
|
+
}
|
|
656
1057
|
}
|
|
657
1058
|
}
|
|
658
1059
|
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
1060
|
+
//MARK: - 3DSecure
|
|
1061
|
+
// MARK: - Credit Card Charge Api If Billing info is not nil and Without Login.
|
|
1062
|
+
func threeDSecurePaymentApi() {
|
|
1063
|
+
showLoadingIndicator()
|
|
1064
|
+
|
|
1065
|
+
let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.threeDSecure.path()
|
|
1066
|
+
|
|
1067
|
+
guard let serviceURL = URL(string: fullURL) else {
|
|
1068
|
+
print("Invalid URL")
|
|
1069
|
+
hideLoadingIndicator()
|
|
1070
|
+
return
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
var uRLRequest = URLRequest(url: serviceURL)
|
|
1074
|
+
uRLRequest.httpMethod = "POST"
|
|
1075
|
+
uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
1076
|
+
|
|
1077
|
+
let token = UserStoreSingleton.shared.clientToken
|
|
1078
|
+
print("Setting clientToken header: \(token ?? "None")")
|
|
1079
|
+
uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
|
|
1080
|
+
|
|
1081
|
+
// Add API headers
|
|
1082
|
+
if let apiKey = EnvironmentConfig.apiKey,
|
|
1083
|
+
let apiSecret = EnvironmentConfig.apiSecret {
|
|
1084
|
+
uRLRequest.addValue(apiKey, forHTTPHeaderField: "x-api-key")
|
|
1085
|
+
uRLRequest.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
var params: [String: Any] = [
|
|
1089
|
+
"name": nameOnCard ?? "",
|
|
1090
|
+
"email": userEmail ?? "",
|
|
1091
|
+
"card_number": cardNumber?.replacingOccurrences(of: " ", with: "") ?? "",
|
|
1092
|
+
"cardholder_name": nameOnCard ?? "",
|
|
1093
|
+
"exp_month": expiryDate?.components(separatedBy: "/").first ?? "",
|
|
1094
|
+
"exp_year": expiryDate?.components(separatedBy: "/").last ?? "",
|
|
1095
|
+
"cvc": cvv ?? "",
|
|
1096
|
+
"currency": "usd",
|
|
1097
|
+
"tokenize": request.tokenOnly ?? false
|
|
1098
|
+
]
|
|
1099
|
+
|
|
1100
|
+
// Conditionally add billing info
|
|
1101
|
+
if let visibility = visibility, visibility.billing == true,
|
|
1102
|
+
let billing = billingInfo, !billing.isEmpty {
|
|
664
1103
|
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
1104
|
+
var billingInfoDict: [String: Any] = [:]
|
|
1105
|
+
for item in billing {
|
|
1106
|
+
billingInfoDict[item.name] = item.value
|
|
668
1107
|
}
|
|
669
|
-
} else if tableView == tblViewStateList {
|
|
670
|
-
let selectedState = stateList[indexPath.row]["name"] as? String
|
|
671
|
-
txtFieldState.text = selectedState
|
|
672
|
-
viewStateList.isHidden = true
|
|
673
1108
|
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
1109
|
+
params["address"] = billingInfoDict["address"] as? String ?? ""
|
|
1110
|
+
params["country"] = billingInfoDict["country"] as? String ?? ""
|
|
1111
|
+
params["state"] = billingInfoDict["state"] as? String ?? ""
|
|
1112
|
+
params["city"] = billingInfoDict["city"] as? String ?? ""
|
|
1113
|
+
params["zip"] = billingInfoDict["postal_code"] as? String ?? ""
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
// Set default description if additional info is not visible
|
|
1117
|
+
if let visibility = visibility, visibility.additional == false {
|
|
1118
|
+
params["description"] = "Hosted payment checkout"
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
// Add these if recurring is enabled
|
|
1122
|
+
if let req = request, req.is_recurring == true {
|
|
1123
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
1124
|
+
// Only send start_date if type is .custom and field is not empty
|
|
1125
|
+
if let startDateText = startDate, !startDateText.isEmpty {
|
|
1126
|
+
let inputFormatter = DateFormatter()
|
|
1127
|
+
inputFormatter.dateFormat = "dd/MM/yyyy"
|
|
1128
|
+
|
|
1129
|
+
let outputFormatter = DateFormatter()
|
|
1130
|
+
outputFormatter.dateFormat = "MM/dd/yyyy"
|
|
1131
|
+
|
|
1132
|
+
if let date = inputFormatter.date(from: startDateText) {
|
|
1133
|
+
let apiFormattedDate = outputFormatter.string(from: date)
|
|
1134
|
+
params["start_date"] = apiFormattedDate
|
|
1135
|
+
} else {
|
|
1136
|
+
print("Invalid date format in startDateText")
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
677
1139
|
}
|
|
678
|
-
} else if tableView == tblViewCityList {
|
|
679
|
-
let selectedCity = cityList[indexPath.row]["city_name"] as? String
|
|
680
|
-
txtFieldCity.text = selectedCity
|
|
681
|
-
viewCityList.isHidden = true
|
|
682
1140
|
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
1141
|
+
params["interval"] = chosenPlan?.lowercased()
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
print(params)
|
|
1145
|
+
|
|
1146
|
+
do {
|
|
1147
|
+
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
1148
|
+
uRLRequest.httpBody = jsonData
|
|
1149
|
+
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
1150
|
+
print("JSON Payload: \(jsonString)")
|
|
686
1151
|
}
|
|
1152
|
+
} catch let error {
|
|
1153
|
+
print("Error creating JSON data: \(error)")
|
|
1154
|
+
hideLoadingIndicator()
|
|
1155
|
+
return
|
|
687
1156
|
}
|
|
688
1157
|
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
1158
|
+
let session = URLSession.shared
|
|
1159
|
+
let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
|
|
1160
|
+
|
|
1161
|
+
DispatchQueue.main.async {
|
|
1162
|
+
self.hideLoadingIndicator() // Stop loader when response is received
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
if let error = error {
|
|
1166
|
+
print("Error: \(error.localizedDescription)")
|
|
1167
|
+
return
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
guard let httpResponse = serviceResponse as? HTTPURLResponse else {
|
|
1171
|
+
print("Invalid response")
|
|
1172
|
+
return
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
|
|
1176
|
+
if let data = serviceData {
|
|
1177
|
+
do {
|
|
1178
|
+
if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
|
|
1179
|
+
print("Response Data: \(responseObject)")
|
|
1180
|
+
|
|
1181
|
+
// Check if status is 0 and handle the error
|
|
1182
|
+
if let status = responseObject["status"] as? Int, status == 0 {
|
|
1183
|
+
let errorMessage = responseObject["message"] as? String ?? "Unknown error"
|
|
1184
|
+
self.presentPaymentErrorVC(errorMessage: errorMessage)
|
|
1185
|
+
} else {
|
|
1186
|
+
DispatchQueue.main.async {
|
|
1187
|
+
if let paymentDoneVC = self.storyboard?.instantiateViewController(withIdentifier: "ThreeDSecurePaymentDoneVC") as? ThreeDSecurePaymentDoneVC {
|
|
1188
|
+
|
|
1189
|
+
let urlString = responseObject["redirect_url"] as? String ?? responseObject["location_url"] as? String ?? ""
|
|
1190
|
+
paymentDoneVC.redirectURL = urlString
|
|
1191
|
+
paymentDoneVC.chargeData = responseObject
|
|
1192
|
+
paymentDoneVC.easyPayDelegate = self.easyPayDelegate
|
|
1193
|
+
// Pass billing and additional info
|
|
1194
|
+
// Conditionally pass raw FieldItem array
|
|
1195
|
+
paymentDoneVC.visibility = self.visibility
|
|
1196
|
+
paymentDoneVC.amount = self.amount
|
|
1197
|
+
|
|
1198
|
+
if self.visibility?.billing == true {
|
|
1199
|
+
paymentDoneVC.billingInfoData = self.billingInfo
|
|
1200
|
+
var billingDict: [String: Any] = [:]
|
|
1201
|
+
self.billingInfo?.forEach { billingDict[$0.name] = $0.value }
|
|
1202
|
+
paymentDoneVC.billingInfo = billingDict
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
if self.visibility?.additional == true {
|
|
1206
|
+
paymentDoneVC.additionalInfoData = self.additionalInfo
|
|
1207
|
+
var additionalDict: [String: Any] = [:]
|
|
1208
|
+
self.additionalInfo?.forEach { additionalDict[$0.name] = $0.value }
|
|
1209
|
+
paymentDoneVC.additionalInfo = additionalDict
|
|
1210
|
+
}
|
|
1211
|
+
self.navigationController?.pushViewController(paymentDoneVC, animated: true)
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
} else {
|
|
1216
|
+
self.presentPaymentErrorVC(errorMessage: "Invalid JSON format")
|
|
1217
|
+
}
|
|
1218
|
+
} catch let jsonError {
|
|
1219
|
+
self.presentPaymentErrorVC(errorMessage: "Error parsing JSON: \(jsonError)")
|
|
1220
|
+
}
|
|
1221
|
+
} else {
|
|
1222
|
+
self.presentPaymentErrorVC(errorMessage: "No data received")
|
|
1223
|
+
}
|
|
1224
|
+
} else {
|
|
1225
|
+
if let data = serviceData,
|
|
1226
|
+
let responseObj = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
|
|
1227
|
+
let message = responseObj["message"] as? String {
|
|
1228
|
+
self.presentPaymentErrorVC(errorMessage: message)
|
|
1229
|
+
} else {
|
|
1230
|
+
self.presentPaymentErrorVC(errorMessage: "HTTP Status Code: \(httpResponse.statusCode)")
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
task.resume()
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
// MARK: - Credit Card Charge Api If Billing info is not nil With Login from Add New Card.
|
|
1238
|
+
func threeDSecurePaymentAddNewCardApi(customerId: String?) {
|
|
1239
|
+
showLoadingIndicator()
|
|
1240
|
+
|
|
1241
|
+
let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.threeDSecure.path()
|
|
1242
|
+
|
|
1243
|
+
guard let serviceURL = URL(string: fullURL) else {
|
|
1244
|
+
print("Invalid URL")
|
|
1245
|
+
hideLoadingIndicator()
|
|
1246
|
+
return
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
var uRLRequest = URLRequest(url: serviceURL)
|
|
1250
|
+
uRLRequest.httpMethod = "POST"
|
|
1251
|
+
uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
1252
|
+
|
|
1253
|
+
let token = UserStoreSingleton.shared.clientToken
|
|
1254
|
+
print("Setting clientToken header: \(token ?? "None")")
|
|
1255
|
+
uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
|
|
1256
|
+
|
|
1257
|
+
// Add API headers
|
|
1258
|
+
if let apiKey = EnvironmentConfig.apiKey,
|
|
1259
|
+
let apiSecret = EnvironmentConfig.apiSecret {
|
|
1260
|
+
uRLRequest.addValue(apiKey, forHTTPHeaderField: "x-api-key")
|
|
1261
|
+
uRLRequest.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
var params: [String: Any] = [
|
|
1265
|
+
"name": nameOnCard ?? "",
|
|
1266
|
+
"email": userEmail ?? "",
|
|
1267
|
+
"card_number": cardNumber?.replacingOccurrences(of: " ", with: "") ?? "",
|
|
1268
|
+
"cardholder_name": nameOnCard ?? "",
|
|
1269
|
+
"exp_month": expiryDate?.components(separatedBy: "/").first ?? "",
|
|
1270
|
+
"exp_year": expiryDate?.components(separatedBy: "/").last ?? "",
|
|
1271
|
+
"cvc": cvv ?? "",
|
|
1272
|
+
"description": "Hosted payment checkout",
|
|
1273
|
+
"currency": "usd",
|
|
1274
|
+
"tokenize": request.tokenOnly ?? false,
|
|
1275
|
+
"save_card": isSavedNewCard ? 1 : 0,
|
|
1276
|
+
"customer_id": customerId ?? ""
|
|
1277
|
+
]
|
|
1278
|
+
|
|
1279
|
+
// Add is_default parameter if save_card is 1
|
|
1280
|
+
if isSavedNewCard {
|
|
1281
|
+
params["is_default"] = "1"
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
// Conditionally add billing info
|
|
1285
|
+
if let visibility = visibility, visibility.billing == true,
|
|
1286
|
+
let billing = billingInfo, !billing.isEmpty {
|
|
1287
|
+
|
|
1288
|
+
var billingInfoDict: [String: Any] = [:]
|
|
1289
|
+
for item in billing {
|
|
1290
|
+
billingInfoDict[item.name] = item.value
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
params["address"] = billingInfoDict["address"] as? String ?? ""
|
|
1294
|
+
params["country"] = billingInfoDict["country"] as? String ?? ""
|
|
1295
|
+
params["state"] = billingInfoDict["state"] as? String ?? ""
|
|
1296
|
+
params["city"] = billingInfoDict["city"] as? String ?? ""
|
|
1297
|
+
params["zip"] = billingInfoDict["postal_code"] as? String ?? ""
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
// Add these if recurring is enabled
|
|
1301
|
+
if let req = request, req.is_recurring == true {
|
|
1302
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
1303
|
+
// Only send start_date if type is .custom and field is not empty
|
|
1304
|
+
if let startDateText = startDate, !startDateText.isEmpty {
|
|
1305
|
+
let inputFormatter = DateFormatter()
|
|
1306
|
+
inputFormatter.dateFormat = "dd/MM/yyyy"
|
|
1307
|
+
|
|
1308
|
+
let outputFormatter = DateFormatter()
|
|
1309
|
+
outputFormatter.dateFormat = "MM/dd/yyyy"
|
|
1310
|
+
|
|
1311
|
+
if let date = inputFormatter.date(from: startDateText) {
|
|
1312
|
+
let apiFormattedDate = outputFormatter.string(from: date)
|
|
1313
|
+
params["start_date"] = apiFormattedDate
|
|
1314
|
+
} else {
|
|
1315
|
+
print("Invalid date format in startDateText")
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
params["interval"] = chosenPlan?.lowercased()
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
do {
|
|
1324
|
+
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
1325
|
+
uRLRequest.httpBody = jsonData
|
|
1326
|
+
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
1327
|
+
print("JSON Payload: \(jsonString)")
|
|
1328
|
+
}
|
|
1329
|
+
} catch let error {
|
|
1330
|
+
print("Error creating JSON data: \(error)")
|
|
1331
|
+
hideLoadingIndicator()
|
|
1332
|
+
return
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
let session = URLSession.shared
|
|
1336
|
+
let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
|
|
1337
|
+
|
|
1338
|
+
DispatchQueue.main.async {
|
|
1339
|
+
self.hideLoadingIndicator() // Stop loader when response is received
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
if let error = error {
|
|
1343
|
+
print("Error: \(error.localizedDescription)")
|
|
1344
|
+
return
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
guard let httpResponse = serviceResponse as? HTTPURLResponse else {
|
|
1348
|
+
print("Invalid response")
|
|
1349
|
+
return
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
|
|
1353
|
+
if let data = serviceData {
|
|
1354
|
+
do {
|
|
1355
|
+
if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
|
|
1356
|
+
print("Response Data: \(responseObject)")
|
|
1357
|
+
|
|
1358
|
+
// Check if status is 0 and handle the error
|
|
1359
|
+
if let status = responseObject["status"] as? Int, status == 0 {
|
|
1360
|
+
let errorMessage = responseObject["message"] as? String ?? "Unknown error"
|
|
1361
|
+
self.presentPaymentErrorVC(errorMessage: errorMessage)
|
|
1362
|
+
} else {
|
|
1363
|
+
DispatchQueue.main.async {
|
|
1364
|
+
if let paymentDoneVC = self.storyboard?.instantiateViewController(withIdentifier: "ThreeDSecurePaymentDoneVC") as? ThreeDSecurePaymentDoneVC {
|
|
1365
|
+
|
|
1366
|
+
let urlString = responseObject["redirect_url"] as? String ?? responseObject["location_url"] as? String ?? ""
|
|
1367
|
+
paymentDoneVC.redirectURL = urlString
|
|
1368
|
+
paymentDoneVC.chargeData = responseObject
|
|
1369
|
+
paymentDoneVC.easyPayDelegate = self.easyPayDelegate
|
|
1370
|
+
// Pass billing and additional info
|
|
1371
|
+
// Conditionally pass raw FieldItem array
|
|
1372
|
+
paymentDoneVC.visibility = self.visibility
|
|
1373
|
+
paymentDoneVC.amount = self.amount
|
|
1374
|
+
|
|
1375
|
+
if self.visibility?.billing == true {
|
|
1376
|
+
paymentDoneVC.billingInfoData = self.billingInfo
|
|
1377
|
+
var billingDict: [String: Any] = [:]
|
|
1378
|
+
self.billingInfo?.forEach { billingDict[$0.name] = $0.value }
|
|
1379
|
+
paymentDoneVC.billingInfo = billingDict
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
if self.visibility?.additional == true {
|
|
1383
|
+
paymentDoneVC.additionalInfoData = self.additionalInfo
|
|
1384
|
+
var additionalDict: [String: Any] = [:]
|
|
1385
|
+
self.additionalInfo?.forEach { additionalDict[$0.name] = $0.value }
|
|
1386
|
+
paymentDoneVC.additionalInfo = additionalDict
|
|
1387
|
+
}
|
|
1388
|
+
self.navigationController?.pushViewController(paymentDoneVC, animated: true)
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
} else {
|
|
1393
|
+
self.presentPaymentErrorVC(errorMessage: "Invalid JSON format")
|
|
1394
|
+
}
|
|
1395
|
+
} catch let jsonError {
|
|
1396
|
+
self.presentPaymentErrorVC(errorMessage: "Error parsing JSON: \(jsonError)")
|
|
1397
|
+
}
|
|
1398
|
+
} else {
|
|
1399
|
+
self.presentPaymentErrorVC(errorMessage: "No data received")
|
|
1400
|
+
}
|
|
1401
|
+
} else {
|
|
1402
|
+
if let data = serviceData,
|
|
1403
|
+
let responseObj = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
|
|
1404
|
+
let message = responseObj["message"] as? String {
|
|
1405
|
+
self.presentPaymentErrorVC(errorMessage: message)
|
|
1406
|
+
} else {
|
|
1407
|
+
self.presentPaymentErrorVC(errorMessage: "HTTP Status Code: \(httpResponse.statusCode)")
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
task.resume()
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
//MARK: - Credit Card Charge Api from Add new card from saved cards.
|
|
1415
|
+
func paymentIntentAddNewCardApi(customerId: String?) {
|
|
1416
|
+
showLoadingIndicator()
|
|
1417
|
+
|
|
1418
|
+
let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.charges.path()
|
|
1419
|
+
|
|
1420
|
+
guard let serviceURL = URL(string: fullURL) else {
|
|
1421
|
+
print("Invalid URL")
|
|
1422
|
+
hideLoadingIndicator()
|
|
1423
|
+
return
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
var uRLRequest = URLRequest(url: serviceURL)
|
|
1427
|
+
uRLRequest.httpMethod = "POST"
|
|
1428
|
+
uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
1429
|
+
|
|
1430
|
+
let token = UserStoreSingleton.shared.clientToken
|
|
1431
|
+
print("Setting clientToken header: \(token ?? "None")")
|
|
1432
|
+
uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
|
|
1433
|
+
|
|
1434
|
+
let emailPrefix = UserStoreSingleton.shared.verificationEmail?.components(separatedBy: "@").first ?? ""
|
|
1435
|
+
|
|
1436
|
+
var params: [String: Any] = [
|
|
1437
|
+
"name": nameOnCard ?? "",
|
|
1438
|
+
"email": userEmail ?? "",
|
|
1439
|
+
"card_number": cardNumber?.replacingOccurrences(of: " ", with: "") ?? "",
|
|
1440
|
+
"cardholder_name": nameOnCard ?? "",
|
|
1441
|
+
"exp_month": expiryDate?.components(separatedBy: "/").first ?? "",
|
|
1442
|
+
"exp_year": expiryDate?.components(separatedBy: "/").last ?? "",
|
|
1443
|
+
"cvc": cvv ?? "",
|
|
1444
|
+
"currency": "usd",
|
|
1445
|
+
"payment_method": selectedPaymentMethod ?? "",
|
|
1446
|
+
"save_card": isSavedNewCard ? 1 : 0
|
|
1447
|
+
]
|
|
1448
|
+
|
|
1449
|
+
// Add is_default parameter if save_card is 1
|
|
1450
|
+
if isSavedNewCard {
|
|
1451
|
+
params["is_default"] = "1"
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
// Conditionally add billing info
|
|
1455
|
+
if let visibility = visibility, visibility.billing == true,
|
|
1456
|
+
let billing = billingInfo, !billing.isEmpty {
|
|
1457
|
+
|
|
1458
|
+
var billingInfoDict: [String: Any] = [:]
|
|
1459
|
+
for item in billing {
|
|
1460
|
+
billingInfoDict[item.name] = item.value
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
params["address"] = billingInfoDict["address"] as? String ?? ""
|
|
1464
|
+
params["country"] = billingInfoDict["country"] as? String ?? ""
|
|
1465
|
+
params["state"] = billingInfoDict["state"] as? String ?? ""
|
|
1466
|
+
params["city"] = billingInfoDict["city"] as? String ?? ""
|
|
1467
|
+
params["zip"] = billingInfoDict["postal_code"] as? String ?? ""
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
// Set default description if additional info is not visible
|
|
1471
|
+
if let visibility = visibility, visibility.additional == false {
|
|
1472
|
+
params["description"] = "Hosted payment checkout"
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
// Add these if recurring is enabled
|
|
1476
|
+
if let req = request, req.is_recurring == true {
|
|
1477
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
1478
|
+
// Only send start_date if type is .custom and field is not empty
|
|
1479
|
+
if let startDateText = startDate, !startDateText.isEmpty {
|
|
1480
|
+
let inputFormatter = DateFormatter()
|
|
1481
|
+
inputFormatter.dateFormat = "dd/MM/yyyy"
|
|
1482
|
+
|
|
1483
|
+
let outputFormatter = DateFormatter()
|
|
1484
|
+
outputFormatter.dateFormat = "MM/dd/yyyy"
|
|
1485
|
+
|
|
1486
|
+
if let date = inputFormatter.date(from: startDateText) {
|
|
1487
|
+
let apiFormattedDate = outputFormatter.string(from: date)
|
|
1488
|
+
params["start_date"] = apiFormattedDate
|
|
1489
|
+
} else {
|
|
1490
|
+
print("Invalid date format in startDateText")
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
params["interval"] = chosenPlan?.lowercased()
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
if let customerId = customerId {
|
|
1499
|
+
params["customer"] = customerId
|
|
1500
|
+
params["customer_id"] = customerId
|
|
1501
|
+
} else {
|
|
1502
|
+
params["username"] = emailPrefix
|
|
1503
|
+
params["email"] = UserStoreSingleton.shared.verificationEmail
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
print(params)
|
|
1507
|
+
|
|
1508
|
+
do {
|
|
1509
|
+
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
1510
|
+
uRLRequest.httpBody = jsonData
|
|
1511
|
+
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
1512
|
+
print("JSON Payload: \(jsonString)")
|
|
1513
|
+
}
|
|
1514
|
+
} catch let error {
|
|
1515
|
+
print("Error creating JSON data: \(error)")
|
|
1516
|
+
hideLoadingIndicator()
|
|
1517
|
+
return
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
let session = URLSession.shared
|
|
1521
|
+
let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
|
|
1522
|
+
|
|
1523
|
+
DispatchQueue.main.async {
|
|
1524
|
+
self.hideLoadingIndicator() // Stop loader when response is received
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
if let error = error {
|
|
1528
|
+
self.presentPaymentErrorVC(errorMessage: error.localizedDescription)
|
|
1529
|
+
return
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
guard let httpResponse = serviceResponse as? HTTPURLResponse else {
|
|
1533
|
+
self.presentPaymentErrorVC(errorMessage: "Invalid response")
|
|
1534
|
+
return
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
|
|
1538
|
+
if let data = serviceData {
|
|
1539
|
+
do {
|
|
1540
|
+
if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
|
|
1541
|
+
print("Response Data: \(responseObject)")
|
|
1542
|
+
|
|
1543
|
+
// Check if status is 0 and handle the error
|
|
1544
|
+
if let status = responseObject["status"] as? Int, status == 0 {
|
|
1545
|
+
let errorMessage = responseObject["message"] as? String ?? "Unknown error"
|
|
1546
|
+
self.presentPaymentErrorVC(errorMessage: errorMessage)
|
|
1547
|
+
} else {
|
|
1548
|
+
DispatchQueue.main.async {
|
|
1549
|
+
if let paymentDoneVC = self.storyboard?.instantiateViewController(withIdentifier: "PaymentDoneVC") as? PaymentDoneVC {
|
|
1550
|
+
paymentDoneVC.chargeData = responseObject
|
|
1551
|
+
paymentDoneVC.selectedPaymentMethod = self.selectedPaymentMethod
|
|
1552
|
+
paymentDoneVC.easyPayDelegate = self.easyPayDelegate
|
|
1553
|
+
// Pass billing and additional info
|
|
1554
|
+
// Conditionally pass raw FieldItem array
|
|
1555
|
+
paymentDoneVC.visibility = self.visibility
|
|
1556
|
+
|
|
1557
|
+
if self.visibility?.billing == true {
|
|
1558
|
+
paymentDoneVC.billingInfoData = self.billingInfo
|
|
1559
|
+
var billingDict: [String: Any] = [:]
|
|
1560
|
+
self.billingInfo?.forEach { billingDict[$0.name] = $0.value }
|
|
1561
|
+
paymentDoneVC.billingInfo = billingDict
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
if self.visibility?.additional == true {
|
|
1565
|
+
paymentDoneVC.additionalInfoData = self.additionalInfo
|
|
1566
|
+
var additionalDict: [String: Any] = [:]
|
|
1567
|
+
self.additionalInfo?.forEach { additionalDict[$0.name] = $0.value }
|
|
1568
|
+
paymentDoneVC.additionalInfo = additionalDict
|
|
1569
|
+
}
|
|
1570
|
+
self.navigationController?.pushViewController(paymentDoneVC, animated: true)
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
} else {
|
|
1575
|
+
self.presentPaymentErrorVC(errorMessage: "Invalid JSON format")
|
|
1576
|
+
}
|
|
1577
|
+
} catch let jsonError {
|
|
1578
|
+
self.presentPaymentErrorVC(errorMessage: "Error parsing JSON: \(jsonError)")
|
|
1579
|
+
}
|
|
1580
|
+
} else {
|
|
1581
|
+
self.presentPaymentErrorVC(errorMessage: "No data received")
|
|
1582
|
+
}
|
|
1583
|
+
} else {
|
|
1584
|
+
if let data = serviceData,
|
|
1585
|
+
let responseObj = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
|
|
1586
|
+
let message = responseObj["message"] as? String {
|
|
1587
|
+
self.presentPaymentErrorVC(errorMessage: message)
|
|
1588
|
+
} else {
|
|
1589
|
+
self.presentPaymentErrorVC(errorMessage: "HTTP Status Code: \(httpResponse.statusCode)")
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
task.resume()
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
//MARK: - Credit Card Charge Api from Saved cards
|
|
1597
|
+
func paymentIntentFromShowCardApi() {
|
|
1598
|
+
showLoadingIndicator()
|
|
1599
|
+
|
|
1600
|
+
let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.charges.path()
|
|
1601
|
+
|
|
1602
|
+
guard let serviceURL = URL(string: fullURL) else {
|
|
1603
|
+
print("Invalid URL")
|
|
1604
|
+
hideLoadingIndicator()
|
|
1605
|
+
return
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
var uRLRequest = URLRequest(url: serviceURL)
|
|
1609
|
+
uRLRequest.httpMethod = "POST"
|
|
1610
|
+
uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
1611
|
+
|
|
1612
|
+
let token = UserStoreSingleton.shared.clientToken
|
|
1613
|
+
print("Setting clientToken header: \(token ?? "None")")
|
|
1614
|
+
uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
|
|
1615
|
+
|
|
1616
|
+
var params: [String: Any] = [
|
|
1617
|
+
"description": "Hosted payment checkout",
|
|
1618
|
+
"currency": "usd",
|
|
1619
|
+
"payment_method": "card",
|
|
1620
|
+
"save_card": 0,
|
|
1621
|
+
"customer" : selectedCard?.customerId ?? "",
|
|
1622
|
+
"customer_id" : selectedCard?.customerId ?? "",
|
|
1623
|
+
"card_id" : selectedCard?.cardId ?? "",
|
|
1624
|
+
"cvc" : cvvText ?? ""
|
|
1625
|
+
]
|
|
1626
|
+
|
|
1627
|
+
// Conditionally add billing info
|
|
1628
|
+
if let visibility = visibility, visibility.billing == true,
|
|
1629
|
+
let billing = billingInfo, !billing.isEmpty {
|
|
1630
|
+
|
|
1631
|
+
var billingInfoDict: [String: Any] = [:]
|
|
1632
|
+
for item in billing {
|
|
1633
|
+
billingInfoDict[item.name] = item.value
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
params["address"] = billingInfoDict["address"] as? String ?? ""
|
|
1637
|
+
params["country"] = billingInfoDict["country"] as? String ?? ""
|
|
1638
|
+
params["state"] = billingInfoDict["state"] as? String ?? ""
|
|
1639
|
+
params["city"] = billingInfoDict["city"] as? String ?? ""
|
|
1640
|
+
params["zip"] = billingInfoDict["postal_code"] as? String ?? ""
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
// Conditionally add additional info
|
|
1644
|
+
if let visibility = visibility, visibility.additional == true,
|
|
1645
|
+
let additional = additionalInfo, !additional.isEmpty {
|
|
1646
|
+
|
|
1647
|
+
var additionalInfoDict: [String: Any] = [:]
|
|
1648
|
+
for item in additional {
|
|
1649
|
+
additionalInfoDict[item.name] = item.value
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
params["description"] = additionalInfoDict["description"] as? String ?? ""
|
|
1653
|
+
params["phone_number"] = additionalInfoDict["phone_number"] as? String ?? ""
|
|
1654
|
+
params["name"] = additionalInfoDict["name"] as? String ?? ""
|
|
1655
|
+
params["email"] = additionalInfoDict["email"] as? String ?? ""
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
// Set default description if additional info is not visible
|
|
1659
|
+
if let visibility = visibility, visibility.additional == false {
|
|
1660
|
+
params["description"] = "Hosted payment checkout"
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
// Add these if recurring is enabled
|
|
1664
|
+
if let req = request, req.is_recurring == true {
|
|
1665
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
1666
|
+
// Only send start_date if type is .custom and field is not empty
|
|
1667
|
+
if let startDateText = startDate, !startDateText.isEmpty {
|
|
1668
|
+
let inputFormatter = DateFormatter()
|
|
1669
|
+
inputFormatter.dateFormat = "dd/MM/yyyy"
|
|
1670
|
+
|
|
1671
|
+
let outputFormatter = DateFormatter()
|
|
1672
|
+
outputFormatter.dateFormat = "MM/dd/yyyy"
|
|
1673
|
+
|
|
1674
|
+
if let date = inputFormatter.date(from: startDateText) {
|
|
1675
|
+
let apiFormattedDate = outputFormatter.string(from: date)
|
|
1676
|
+
params["start_date"] = apiFormattedDate
|
|
1677
|
+
} else {
|
|
1678
|
+
print("Invalid date format in startDateText")
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1683
|
+
params["interval"] = chosenPlan?.lowercased()
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1686
|
+
do {
|
|
1687
|
+
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
1688
|
+
uRLRequest.httpBody = jsonData
|
|
1689
|
+
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
1690
|
+
print("JSON Payload: \(jsonString)")
|
|
1691
|
+
}
|
|
1692
|
+
} catch let error {
|
|
1693
|
+
print("Error creating JSON data: \(error)")
|
|
1694
|
+
hideLoadingIndicator()
|
|
1695
|
+
return
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
let session = URLSession.shared
|
|
1699
|
+
let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
|
|
1700
|
+
|
|
1701
|
+
DispatchQueue.main.async {
|
|
1702
|
+
self.hideLoadingIndicator() // Stop loader when response is received
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
if let error = error {
|
|
1706
|
+
print("Error: \(error.localizedDescription)")
|
|
1707
|
+
return
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1710
|
+
guard let httpResponse = serviceResponse as? HTTPURLResponse else {
|
|
1711
|
+
print("Invalid response")
|
|
1712
|
+
return
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
|
|
1716
|
+
if let data = serviceData {
|
|
1717
|
+
do {
|
|
1718
|
+
if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
|
|
1719
|
+
print("Response Data: \(responseObject)")
|
|
1720
|
+
|
|
1721
|
+
// Check if status is 0 and handle the error
|
|
1722
|
+
if let status = responseObject["status"] as? Int, status == 0 {
|
|
1723
|
+
let errorMessage = responseObject["message"] as? String ?? "Unknown error"
|
|
1724
|
+
self.presentPaymentErrorVC(errorMessage: errorMessage)
|
|
1725
|
+
} else {
|
|
1726
|
+
DispatchQueue.main.async {
|
|
1727
|
+
if let paymentDoneVC = self.storyboard?.instantiateViewController(withIdentifier: "PaymentDoneVC") as? PaymentDoneVC {
|
|
1728
|
+
paymentDoneVC.chargeData = responseObject
|
|
1729
|
+
paymentDoneVC.selectedPaymentMethod = self.selectedPaymentMethod
|
|
1730
|
+
paymentDoneVC.easyPayDelegate = self.easyPayDelegate
|
|
1731
|
+
// Pass billing and additional info
|
|
1732
|
+
// Conditionally pass raw FieldItem array
|
|
1733
|
+
paymentDoneVC.visibility = self.visibility
|
|
1734
|
+
|
|
1735
|
+
if self.visibility?.billing == true {
|
|
1736
|
+
paymentDoneVC.billingInfoData = self.billingInfo
|
|
1737
|
+
var billingDict: [String: Any] = [:]
|
|
1738
|
+
self.billingInfo?.forEach { billingDict[$0.name] = $0.value }
|
|
1739
|
+
paymentDoneVC.billingInfo = billingDict
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
if self.visibility?.additional == true {
|
|
1743
|
+
paymentDoneVC.additionalInfoData = self.additionalInfo
|
|
1744
|
+
var additionalDict: [String: Any] = [:]
|
|
1745
|
+
self.additionalInfo?.forEach { additionalDict[$0.name] = $0.value }
|
|
1746
|
+
paymentDoneVC.additionalInfo = additionalDict
|
|
1747
|
+
}
|
|
1748
|
+
self.navigationController?.pushViewController(paymentDoneVC, animated: true)
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
} else {
|
|
1753
|
+
self.presentPaymentErrorVC(errorMessage: "Invalid JSON format")
|
|
1754
|
+
}
|
|
1755
|
+
} catch let jsonError {
|
|
1756
|
+
self.presentPaymentErrorVC(errorMessage: "Error parsing JSON: \(jsonError)")
|
|
1757
|
+
}
|
|
1758
|
+
} else {
|
|
1759
|
+
self.presentPaymentErrorVC(errorMessage: "No data received")
|
|
1760
|
+
}
|
|
1761
|
+
} else {
|
|
1762
|
+
if let data = serviceData,
|
|
1763
|
+
let responseObj = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
|
|
1764
|
+
let message = responseObj["message"] as? String {
|
|
1765
|
+
self.presentPaymentErrorVC(errorMessage: message)
|
|
1766
|
+
} else {
|
|
1767
|
+
self.presentPaymentErrorVC(errorMessage: "HTTP Status Code: \(httpResponse.statusCode)")
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
task.resume()
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
//MARK: - Banking Account Charge Api from Regular saved bank account
|
|
1775
|
+
func accountChargeSavedBankAccountApi() {
|
|
1776
|
+
showLoadingIndicator()
|
|
1777
|
+
|
|
1778
|
+
let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.achCharge.path()
|
|
1779
|
+
|
|
1780
|
+
guard let serviceURL = URL(string: fullURL) else {
|
|
1781
|
+
print("Invalid URL")
|
|
1782
|
+
hideLoadingIndicator()
|
|
1783
|
+
return
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
var uRLRequest = URLRequest(url: serviceURL)
|
|
1787
|
+
uRLRequest.httpMethod = "POST"
|
|
1788
|
+
uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
1789
|
+
|
|
1790
|
+
let token = UserStoreSingleton.shared.clientToken
|
|
1791
|
+
print("Setting clientToken header: \(token ?? "None")")
|
|
1792
|
+
uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
|
|
1793
|
+
|
|
1794
|
+
var params: [String: Any] = [
|
|
1795
|
+
"name": UserStoreSingleton.shared.merchantName ?? "",
|
|
1796
|
+
"account_id": accountID ?? "",
|
|
1797
|
+
"payment_method": "ach",
|
|
1798
|
+
"customer": customerID ?? "",
|
|
1799
|
+
"currency": "usd",
|
|
1800
|
+
]
|
|
1801
|
+
|
|
1802
|
+
// Conditionally add billing info
|
|
1803
|
+
if let visibility = visibility, visibility.billing == true,
|
|
1804
|
+
let billing = billingInfo, !billing.isEmpty {
|
|
1805
|
+
|
|
1806
|
+
var billingInfoDict: [String: Any] = [:]
|
|
1807
|
+
for item in billing {
|
|
1808
|
+
billingInfoDict[item.name] = item.value
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
params["address"] = billingInfoDict["address"] as? String ?? ""
|
|
1812
|
+
params["country"] = billingInfoDict["country"] as? String ?? ""
|
|
1813
|
+
params["state"] = billingInfoDict["state"] as? String ?? ""
|
|
1814
|
+
params["city"] = billingInfoDict["city"] as? String ?? ""
|
|
1815
|
+
params["zip"] = billingInfoDict["postal_code"] as? String ?? ""
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1818
|
+
// Set default description if additional info is not visible
|
|
1819
|
+
if let visibility = visibility, visibility.additional == false {
|
|
1820
|
+
params["description"] = "Hosted payment checkout"
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1823
|
+
// Add these if recurring is enabled
|
|
1824
|
+
if let req = request, req.is_recurring == true {
|
|
1825
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
1826
|
+
// Only send start_date if type is .custom and field is not empty
|
|
1827
|
+
if let startDateText = startDate, !startDateText.isEmpty {
|
|
1828
|
+
let inputFormatter = DateFormatter()
|
|
1829
|
+
inputFormatter.dateFormat = "dd/MM/yyyy"
|
|
1830
|
+
|
|
1831
|
+
let outputFormatter = DateFormatter()
|
|
1832
|
+
outputFormatter.dateFormat = "MM/dd/yyyy"
|
|
1833
|
+
|
|
1834
|
+
if let date = inputFormatter.date(from: startDateText) {
|
|
1835
|
+
let apiFormattedDate = outputFormatter.string(from: date)
|
|
1836
|
+
params["start_date"] = apiFormattedDate
|
|
1837
|
+
} else {
|
|
1838
|
+
print("Invalid date format in startDateText")
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
params["interval"] = chosenPlan?.lowercased()
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
print(params)
|
|
1847
|
+
|
|
1848
|
+
do {
|
|
1849
|
+
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
1850
|
+
uRLRequest.httpBody = jsonData
|
|
1851
|
+
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
1852
|
+
print("JSON Payload: \(jsonString)")
|
|
1853
|
+
}
|
|
1854
|
+
} catch let error {
|
|
1855
|
+
print("Error creating JSON data: \(error)")
|
|
1856
|
+
hideLoadingIndicator()
|
|
1857
|
+
return
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1860
|
+
let session = URLSession.shared
|
|
1861
|
+
let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
|
|
1862
|
+
|
|
1863
|
+
DispatchQueue.main.async {
|
|
1864
|
+
self.hideLoadingIndicator() // Stop loader when response is received
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1867
|
+
if let error = error {
|
|
1868
|
+
print("Error: \(error.localizedDescription)")
|
|
1869
|
+
return
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
guard let httpResponse = serviceResponse as? HTTPURLResponse else {
|
|
1873
|
+
print("Invalid response")
|
|
1874
|
+
return
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
|
|
1878
|
+
if let data = serviceData {
|
|
1879
|
+
do {
|
|
1880
|
+
if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
|
|
1881
|
+
print("Response Data: \(responseObject)")
|
|
1882
|
+
|
|
1883
|
+
// Check if status is 0 and handle the error
|
|
1884
|
+
if let status = responseObject["status"] as? Int, status == 0 {
|
|
1885
|
+
let errorMessage = responseObject["message"] as? String ?? "Unknown error"
|
|
1886
|
+
self.presentPaymentErrorVC(errorMessage: errorMessage)
|
|
1887
|
+
} else {
|
|
1888
|
+
DispatchQueue.main.async {
|
|
1889
|
+
if let paymentDoneVC = self.storyboard?.instantiateViewController(withIdentifier: "PaymentDoneVC") as? PaymentDoneVC {
|
|
1890
|
+
paymentDoneVC.chargeData = responseObject
|
|
1891
|
+
// Pass the selected payment method
|
|
1892
|
+
paymentDoneVC.selectedPaymentMethod = self.selectedPaymentMethod
|
|
1893
|
+
paymentDoneVC.easyPayDelegate = self.easyPayDelegate // Pass the delegate
|
|
1894
|
+
// Pass billing and additional info
|
|
1895
|
+
// Conditionally pass raw FieldItem array
|
|
1896
|
+
paymentDoneVC.visibility = self.visibility
|
|
1897
|
+
|
|
1898
|
+
if self.visibility?.billing == true {
|
|
1899
|
+
paymentDoneVC.billingInfoData = self.billingInfo
|
|
1900
|
+
var billingDict: [String: Any] = [:]
|
|
1901
|
+
self.billingInfo?.forEach { billingDict[$0.name] = $0.value }
|
|
1902
|
+
paymentDoneVC.billingInfo = billingDict
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
self.navigationController?.pushViewController(paymentDoneVC, animated: true)
|
|
1906
|
+
}
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
} else {
|
|
1910
|
+
self.presentPaymentErrorVC(errorMessage: "Invalid JSON format")
|
|
1911
|
+
}
|
|
1912
|
+
} catch let jsonError {
|
|
1913
|
+
self.presentPaymentErrorVC(errorMessage: "Error parsing JSON: \(jsonError)")
|
|
1914
|
+
}
|
|
1915
|
+
} else {
|
|
1916
|
+
self.presentPaymentErrorVC(errorMessage: "No data received")
|
|
1917
|
+
}
|
|
1918
|
+
} else {
|
|
1919
|
+
if let data = serviceData,
|
|
1920
|
+
let responseObj = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
|
|
1921
|
+
let message = responseObj["message"] as? String {
|
|
1922
|
+
self.presentPaymentErrorVC(errorMessage: message)
|
|
1923
|
+
} else {
|
|
1924
|
+
self.presentPaymentErrorVC(errorMessage: "HTTP Status Code: \(httpResponse.statusCode)")
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
task.resume()
|
|
1929
|
+
}
|
|
1930
|
+
|
|
1931
|
+
//MARK: - Banking Account Charge Api
|
|
1932
|
+
func accountChargeApi() {
|
|
1933
|
+
showLoadingIndicator()
|
|
1934
|
+
|
|
1935
|
+
let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.achCharge.path()
|
|
1936
|
+
|
|
1937
|
+
guard let serviceURL = URL(string: fullURL) else {
|
|
1938
|
+
print("Invalid URL")
|
|
1939
|
+
hideLoadingIndicator()
|
|
1940
|
+
return
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1943
|
+
var uRLRequest = URLRequest(url: serviceURL)
|
|
1944
|
+
uRLRequest.httpMethod = "POST"
|
|
1945
|
+
uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
1946
|
+
|
|
1947
|
+
let token = UserStoreSingleton.shared.clientToken
|
|
1948
|
+
print("Setting clientToken header: \(token ?? "None")")
|
|
1949
|
+
uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
|
|
1950
|
+
|
|
1951
|
+
var params: [String: Any] = [
|
|
1952
|
+
"name": accountName ?? "",
|
|
1953
|
+
"email": userEmail ?? "",
|
|
1954
|
+
"currency": "usd",
|
|
1955
|
+
"account_type": accountType?.lowercased() ?? "",
|
|
1956
|
+
"routing_number": routingNumber ?? "",
|
|
1957
|
+
"account_number": accountNumber ?? "",
|
|
1958
|
+
"payment_mode": "auth_and_capture",
|
|
1959
|
+
"payment_intent": UserStoreSingleton.shared.paymentIntent ?? "",
|
|
1960
|
+
"levelIndicator": 1,
|
|
1961
|
+
]
|
|
1962
|
+
|
|
1963
|
+
// Conditionally add billing info
|
|
1964
|
+
if let visibility = visibility, visibility.billing == true,
|
|
1965
|
+
let billing = billingInfo, !billing.isEmpty {
|
|
1966
|
+
|
|
1967
|
+
var billingInfoDict: [String: Any] = [:]
|
|
1968
|
+
for item in billing {
|
|
1969
|
+
billingInfoDict[item.name] = item.value
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
params["address"] = billingInfoDict["address"] as? String ?? ""
|
|
1973
|
+
params["country"] = billingInfoDict["country"] as? String ?? ""
|
|
1974
|
+
params["state"] = billingInfoDict["state"] as? String ?? ""
|
|
1975
|
+
params["city"] = billingInfoDict["city"] as? String ?? ""
|
|
1976
|
+
params["zip"] = billingInfoDict["postal_code"] as? String ?? ""
|
|
1977
|
+
}
|
|
1978
|
+
|
|
1979
|
+
// Set default description if additional info is not visible
|
|
1980
|
+
if let visibility = visibility, visibility.additional == false {
|
|
1981
|
+
params["description"] = "Hosted payment checkout"
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
// Add these if recurring is enabled
|
|
1985
|
+
if let req = request, req.is_recurring == true {
|
|
1986
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
1987
|
+
// Only send start_date if type is .custom and field is not empty
|
|
1988
|
+
if let startDateText = startDate, !startDateText.isEmpty {
|
|
1989
|
+
let inputFormatter = DateFormatter()
|
|
1990
|
+
inputFormatter.dateFormat = "dd/MM/yyyy"
|
|
1991
|
+
|
|
1992
|
+
let outputFormatter = DateFormatter()
|
|
1993
|
+
outputFormatter.dateFormat = "MM/dd/yyyy"
|
|
1994
|
+
|
|
1995
|
+
if let date = inputFormatter.date(from: startDateText) {
|
|
1996
|
+
let apiFormattedDate = outputFormatter.string(from: date)
|
|
1997
|
+
params["start_date"] = apiFormattedDate
|
|
1998
|
+
} else {
|
|
1999
|
+
print("Invalid date format in startDateText")
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
|
|
2004
|
+
params["interval"] = chosenPlan?.lowercased()
|
|
2005
|
+
}
|
|
2006
|
+
|
|
2007
|
+
print(params)
|
|
2008
|
+
|
|
2009
|
+
do {
|
|
2010
|
+
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
2011
|
+
uRLRequest.httpBody = jsonData
|
|
2012
|
+
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
2013
|
+
print("JSON Payload: \(jsonString)")
|
|
2014
|
+
}
|
|
2015
|
+
} catch let error {
|
|
2016
|
+
print("Error creating JSON data: \(error)")
|
|
2017
|
+
hideLoadingIndicator()
|
|
2018
|
+
return
|
|
2019
|
+
}
|
|
2020
|
+
|
|
2021
|
+
let session = URLSession.shared
|
|
2022
|
+
let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
|
|
2023
|
+
|
|
2024
|
+
DispatchQueue.main.async {
|
|
2025
|
+
self.hideLoadingIndicator() // Stop loader when response is received
|
|
2026
|
+
}
|
|
2027
|
+
|
|
2028
|
+
if let error = error {
|
|
2029
|
+
print("Error: \(error.localizedDescription)")
|
|
2030
|
+
return
|
|
2031
|
+
}
|
|
2032
|
+
|
|
2033
|
+
guard let httpResponse = serviceResponse as? HTTPURLResponse else {
|
|
2034
|
+
print("Invalid response")
|
|
2035
|
+
return
|
|
2036
|
+
}
|
|
2037
|
+
|
|
2038
|
+
if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
|
|
2039
|
+
if let data = serviceData {
|
|
2040
|
+
do {
|
|
2041
|
+
if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
|
|
2042
|
+
print("Response Data: \(responseObject)")
|
|
2043
|
+
|
|
2044
|
+
// Check if status is 0 and handle the error
|
|
2045
|
+
if let status = responseObject["status"] as? Int, status == 0 {
|
|
2046
|
+
let errorMessage = responseObject["message"] as? String ?? "Unknown error"
|
|
2047
|
+
self.presentPaymentErrorVC(errorMessage: errorMessage)
|
|
2048
|
+
} else {
|
|
2049
|
+
DispatchQueue.main.async {
|
|
2050
|
+
if let paymentDoneVC = self.storyboard?.instantiateViewController(withIdentifier: "PaymentDoneVC") as? PaymentDoneVC {
|
|
2051
|
+
paymentDoneVC.chargeData = responseObject
|
|
2052
|
+
// Pass the selected payment method
|
|
2053
|
+
paymentDoneVC.selectedPaymentMethod = self.selectedPaymentMethod
|
|
2054
|
+
paymentDoneVC.easyPayDelegate = self.easyPayDelegate // Pass the delegate
|
|
2055
|
+
// Pass billing and additional info
|
|
2056
|
+
// Conditionally pass raw FieldItem array
|
|
2057
|
+
paymentDoneVC.visibility = self.visibility
|
|
2058
|
+
|
|
2059
|
+
if self.visibility?.billing == true {
|
|
2060
|
+
paymentDoneVC.billingInfoData = self.billingInfo
|
|
2061
|
+
var billingDict: [String: Any] = [:]
|
|
2062
|
+
self.billingInfo?.forEach { billingDict[$0.name] = $0.value }
|
|
2063
|
+
paymentDoneVC.billingInfo = billingDict
|
|
2064
|
+
}
|
|
2065
|
+
self.navigationController?.pushViewController(paymentDoneVC, animated: true)
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
} else {
|
|
2070
|
+
self.presentPaymentErrorVC(errorMessage: "Invalid JSON format")
|
|
2071
|
+
}
|
|
2072
|
+
} catch let jsonError {
|
|
2073
|
+
self.presentPaymentErrorVC(errorMessage: "Error parsing JSON: \(jsonError)")
|
|
2074
|
+
}
|
|
2075
|
+
} else {
|
|
2076
|
+
self.presentPaymentErrorVC(errorMessage: "No data received")
|
|
2077
|
+
}
|
|
2078
|
+
} else {
|
|
2079
|
+
if let data = serviceData,
|
|
2080
|
+
let responseObj = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
|
|
2081
|
+
let message = responseObj["message"] as? String {
|
|
2082
|
+
self.presentPaymentErrorVC(errorMessage: message)
|
|
2083
|
+
} else {
|
|
2084
|
+
self.presentPaymentErrorVC(errorMessage: "HTTP Status Code: \(httpResponse.statusCode)")
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
task.resume()
|
|
2089
|
+
}
|
|
2090
|
+
|
|
2091
|
+
//MARK: - Account Charge Api if user saved the account.
|
|
2092
|
+
func accountChargeApi(customerId: String?) {
|
|
2093
|
+
showLoadingIndicator()
|
|
2094
|
+
|
|
2095
|
+
let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.achCharge.path()
|
|
2096
|
+
|
|
2097
|
+
guard let serviceURL = URL(string: fullURL) else {
|
|
2098
|
+
print("Invalid URL")
|
|
2099
|
+
hideLoadingIndicator()
|
|
2100
|
+
return
|
|
2101
|
+
}
|
|
2102
|
+
|
|
2103
|
+
var uRLRequest = URLRequest(url: serviceURL)
|
|
2104
|
+
uRLRequest.httpMethod = "POST"
|
|
2105
|
+
uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
2106
|
+
|
|
2107
|
+
let token = UserStoreSingleton.shared.clientToken
|
|
2108
|
+
print("Setting clientToken header: \(token ?? "None")")
|
|
2109
|
+
uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
|
|
2110
|
+
|
|
2111
|
+
let emailPrefix = UserStoreSingleton.shared.verificationEmail?.components(separatedBy: "@").first ?? ""
|
|
2112
|
+
|
|
2113
|
+
var params: [String: Any] = [
|
|
2114
|
+
"name": accountName ?? "",
|
|
2115
|
+
"email": UserStoreSingleton.shared.merchantEmail ?? "",
|
|
2116
|
+
"currency": "usd",
|
|
2117
|
+
"account_type": accountType?.lowercased() ?? "",
|
|
2118
|
+
"routing_number": routingNumber ?? "",
|
|
2119
|
+
"account_number": accountNumber ?? "",
|
|
2120
|
+
"payment_mode": "auth_and_capture",
|
|
2121
|
+
"payment_intent": UserStoreSingleton.shared.paymentIntent ?? "",
|
|
2122
|
+
"levelIndicator": 1,
|
|
2123
|
+
"save_account": 1,
|
|
2124
|
+
"payment_method": "ach"
|
|
2125
|
+
]
|
|
2126
|
+
|
|
2127
|
+
if let customerId = customerId {
|
|
2128
|
+
params["customer"] = customerId
|
|
2129
|
+
} else {
|
|
2130
|
+
params["username"] = emailPrefix
|
|
2131
|
+
}
|
|
2132
|
+
|
|
2133
|
+
// Conditionally add billing info
|
|
2134
|
+
if let visibility = visibility, visibility.billing == true,
|
|
2135
|
+
let billing = billingInfo, !billing.isEmpty {
|
|
2136
|
+
|
|
2137
|
+
var billingInfoDict: [String: Any] = [:]
|
|
2138
|
+
for item in billing {
|
|
2139
|
+
billingInfoDict[item.name] = item.value
|
|
2140
|
+
}
|
|
2141
|
+
|
|
2142
|
+
params["address"] = billingInfoDict["address"] as? String ?? ""
|
|
2143
|
+
params["country"] = billingInfoDict["country"] as? String ?? ""
|
|
2144
|
+
params["state"] = billingInfoDict["state"] as? String ?? ""
|
|
2145
|
+
params["city"] = billingInfoDict["city"] as? String ?? ""
|
|
2146
|
+
params["zip"] = billingInfoDict["postal_code"] as? String ?? ""
|
|
2147
|
+
}
|
|
2148
|
+
|
|
2149
|
+
// Set default description if additional info is not visible
|
|
2150
|
+
if let visibility = visibility, visibility.additional == false {
|
|
2151
|
+
params["description"] = "Hosted payment checkout"
|
|
2152
|
+
}
|
|
2153
|
+
|
|
2154
|
+
// Add these if recurring is enabled
|
|
2155
|
+
if let req = request, req.is_recurring == true {
|
|
2156
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
2157
|
+
// Only send start_date if type is .custom and field is not empty
|
|
2158
|
+
if let startDateText = startDate, !startDateText.isEmpty {
|
|
2159
|
+
let inputFormatter = DateFormatter()
|
|
2160
|
+
inputFormatter.dateFormat = "dd/MM/yyyy"
|
|
2161
|
+
|
|
2162
|
+
let outputFormatter = DateFormatter()
|
|
2163
|
+
outputFormatter.dateFormat = "MM/dd/yyyy"
|
|
2164
|
+
|
|
2165
|
+
if let date = inputFormatter.date(from: startDateText) {
|
|
2166
|
+
let apiFormattedDate = outputFormatter.string(from: date)
|
|
2167
|
+
params["start_date"] = apiFormattedDate
|
|
2168
|
+
} else {
|
|
2169
|
+
print("Invalid date format in startDateText")
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
|
|
2174
|
+
params["interval"] = chosenPlan?.lowercased()
|
|
2175
|
+
}
|
|
2176
|
+
|
|
2177
|
+
print(params)
|
|
2178
|
+
|
|
2179
|
+
do {
|
|
2180
|
+
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
2181
|
+
uRLRequest.httpBody = jsonData
|
|
2182
|
+
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
2183
|
+
print("JSON Payload: \(jsonString)")
|
|
2184
|
+
}
|
|
2185
|
+
} catch let error {
|
|
2186
|
+
print("Error creating JSON data: \(error)")
|
|
2187
|
+
hideLoadingIndicator()
|
|
2188
|
+
return
|
|
2189
|
+
}
|
|
2190
|
+
|
|
2191
|
+
let session = URLSession.shared
|
|
2192
|
+
let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
|
|
2193
|
+
|
|
2194
|
+
DispatchQueue.main.async {
|
|
2195
|
+
self.hideLoadingIndicator() // Stop loader when response is received
|
|
2196
|
+
}
|
|
2197
|
+
|
|
2198
|
+
if let error = error {
|
|
2199
|
+
self.presentPaymentErrorVC(errorMessage: error.localizedDescription)
|
|
2200
|
+
return
|
|
2201
|
+
}
|
|
2202
|
+
|
|
2203
|
+
guard let httpResponse = serviceResponse as? HTTPURLResponse else {
|
|
2204
|
+
self.presentPaymentErrorVC(errorMessage: "Invalid response")
|
|
2205
|
+
return
|
|
2206
|
+
}
|
|
2207
|
+
|
|
2208
|
+
if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
|
|
2209
|
+
if let data = serviceData {
|
|
2210
|
+
do {
|
|
2211
|
+
if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
|
|
2212
|
+
print("Response Data: \(responseObject)")
|
|
2213
|
+
|
|
2214
|
+
// Check if status is 0 and handle the error
|
|
2215
|
+
if let status = responseObject["status"] as? Int, status == 0 {
|
|
2216
|
+
let errorMessage = responseObject["message"] as? String ?? "Unknown error"
|
|
2217
|
+
self.presentPaymentErrorVC(errorMessage: errorMessage)
|
|
2218
|
+
} else {
|
|
2219
|
+
DispatchQueue.main.async {
|
|
2220
|
+
if let paymentDoneVC = self.storyboard?.instantiateViewController(withIdentifier: "PaymentDoneVC") as? PaymentDoneVC {
|
|
2221
|
+
paymentDoneVC.chargeData = responseObject
|
|
2222
|
+
paymentDoneVC.selectedPaymentMethod = self.selectedPaymentMethod
|
|
2223
|
+
paymentDoneVC.easyPayDelegate = self.easyPayDelegate
|
|
2224
|
+
// Pass billing and additional info
|
|
2225
|
+
// Conditionally pass raw FieldItem array
|
|
2226
|
+
paymentDoneVC.visibility = self.visibility
|
|
2227
|
+
|
|
2228
|
+
if self.visibility?.billing == true {
|
|
2229
|
+
paymentDoneVC.billingInfoData = self.billingInfo
|
|
2230
|
+
var billingDict: [String: Any] = [:]
|
|
2231
|
+
self.billingInfo?.forEach { billingDict[$0.name] = $0.value }
|
|
2232
|
+
paymentDoneVC.billingInfo = billingDict
|
|
2233
|
+
}
|
|
2234
|
+
self.navigationController?.pushViewController(paymentDoneVC, animated: true)
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
}
|
|
2238
|
+
} else {
|
|
2239
|
+
self.presentPaymentErrorVC(errorMessage: "Invalid JSON format")
|
|
2240
|
+
}
|
|
2241
|
+
} catch let jsonError {
|
|
2242
|
+
self.presentPaymentErrorVC(errorMessage: "Error parsing JSON: \(jsonError)")
|
|
2243
|
+
}
|
|
2244
|
+
} else {
|
|
2245
|
+
self.presentPaymentErrorVC(errorMessage: "No data received")
|
|
2246
|
+
}
|
|
2247
|
+
} else {
|
|
2248
|
+
if let data = serviceData,
|
|
2249
|
+
let responseObj = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
|
|
2250
|
+
let message = responseObj["message"] as? String {
|
|
2251
|
+
self.presentPaymentErrorVC(errorMessage: message)
|
|
2252
|
+
} else {
|
|
2253
|
+
self.presentPaymentErrorVC(errorMessage: "HTTP Status Code: \(httpResponse.statusCode)")
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
task.resume()
|
|
2258
|
+
}
|
|
2259
|
+
|
|
2260
|
+
//MARK: - GrailPay Account Charge Api if user not saved account but billing info available
|
|
2261
|
+
func grailPayAccountChargeApi() {
|
|
2262
|
+
showLoadingIndicator()
|
|
2263
|
+
|
|
2264
|
+
let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.achCharge.path()
|
|
2265
|
+
|
|
2266
|
+
guard let serviceURL = URL(string: fullURL) else {
|
|
2267
|
+
print("Invalid URL")
|
|
2268
|
+
hideLoadingIndicator()
|
|
2269
|
+
return
|
|
2270
|
+
}
|
|
2271
|
+
|
|
2272
|
+
var uRLRequest = URLRequest(url: serviceURL)
|
|
2273
|
+
uRLRequest.httpMethod = "POST"
|
|
2274
|
+
uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
2275
|
+
|
|
2276
|
+
let token = UserStoreSingleton.shared.clientToken
|
|
2277
|
+
print("Setting clientToken header: \(token ?? "None")")
|
|
2278
|
+
uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
|
|
2279
|
+
|
|
2280
|
+
if let apiKey = EnvironmentConfig.apiKey {
|
|
2281
|
+
uRLRequest.addValue(apiKey, forHTTPHeaderField: "X-Api-Key")
|
|
2282
|
+
}
|
|
2283
|
+
if let apiSecret = EnvironmentConfig.apiSecret {
|
|
2284
|
+
uRLRequest.addValue(apiSecret, forHTTPHeaderField: "X-Api-Secret")
|
|
2285
|
+
}
|
|
2286
|
+
|
|
2287
|
+
var params: [String: Any] = [
|
|
2288
|
+
"account_id": self.grailPayAccountID ?? "",
|
|
2289
|
+
"account_type": self.selectedGrailPayAccountType ?? "",
|
|
2290
|
+
"name": self.selectedGrailPayAccountName ?? "",
|
|
2291
|
+
"description": "payment checkout",
|
|
2292
|
+
"email": UserStoreSingleton.shared.merchantEmail ?? ""
|
|
2293
|
+
]
|
|
2294
|
+
|
|
2295
|
+
// Conditionally add billing info
|
|
2296
|
+
if let visibility = visibility, visibility.billing == true,
|
|
2297
|
+
let billing = billingInfo, !billing.isEmpty {
|
|
2298
|
+
|
|
2299
|
+
var billingInfoDict: [String: Any] = [:]
|
|
2300
|
+
for item in billing {
|
|
2301
|
+
billingInfoDict[item.name] = item.value
|
|
2302
|
+
}
|
|
2303
|
+
|
|
2304
|
+
params["address"] = billingInfoDict["address"] as? String ?? ""
|
|
2305
|
+
params["country"] = billingInfoDict["country"] as? String ?? ""
|
|
2306
|
+
params["state"] = billingInfoDict["state"] as? String ?? ""
|
|
2307
|
+
params["city"] = billingInfoDict["city"] as? String ?? ""
|
|
2308
|
+
params["zip"] = billingInfoDict["postal_code"] as? String ?? ""
|
|
2309
|
+
}
|
|
2310
|
+
|
|
2311
|
+
// Set default description if additional info is not visible
|
|
2312
|
+
if let visibility = visibility, visibility.additional == false {
|
|
2313
|
+
params["description"] = "Hosted payment checkout"
|
|
2314
|
+
}
|
|
2315
|
+
|
|
2316
|
+
// Add these if recurring is enabled
|
|
2317
|
+
if let req = request, req.is_recurring == true {
|
|
2318
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
2319
|
+
// Only send start_date if type is .custom and field is not empty
|
|
2320
|
+
if let startDateText = startDate, !startDateText.isEmpty {
|
|
2321
|
+
let inputFormatter = DateFormatter()
|
|
2322
|
+
inputFormatter.dateFormat = "dd/MM/yyyy"
|
|
2323
|
+
|
|
2324
|
+
let outputFormatter = DateFormatter()
|
|
2325
|
+
outputFormatter.dateFormat = "MM/dd/yyyy"
|
|
2326
|
+
|
|
2327
|
+
if let date = inputFormatter.date(from: startDateText) {
|
|
2328
|
+
let apiFormattedDate = outputFormatter.string(from: date)
|
|
2329
|
+
params["start_date"] = apiFormattedDate
|
|
2330
|
+
} else {
|
|
2331
|
+
print("Invalid date format in startDateText")
|
|
2332
|
+
}
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
|
|
2336
|
+
params["interval"] = chosenPlan?.lowercased()
|
|
2337
|
+
}
|
|
2338
|
+
|
|
2339
|
+
print(params)
|
|
2340
|
+
|
|
2341
|
+
do {
|
|
2342
|
+
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
2343
|
+
uRLRequest.httpBody = jsonData
|
|
2344
|
+
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
2345
|
+
print("JSON Payload: \(jsonString)")
|
|
2346
|
+
}
|
|
2347
|
+
} catch let error {
|
|
2348
|
+
print("Error creating JSON data: \(error)")
|
|
2349
|
+
hideLoadingIndicator()
|
|
2350
|
+
return
|
|
2351
|
+
}
|
|
2352
|
+
|
|
2353
|
+
let session = URLSession.shared
|
|
2354
|
+
let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
|
|
2355
|
+
|
|
2356
|
+
DispatchQueue.main.async {
|
|
2357
|
+
self.hideLoadingIndicator() // Stop loader when response is received
|
|
2358
|
+
}
|
|
2359
|
+
|
|
2360
|
+
if let error = error {
|
|
2361
|
+
self.presentPaymentErrorVC(errorMessage: error.localizedDescription)
|
|
2362
|
+
return
|
|
2363
|
+
}
|
|
2364
|
+
|
|
2365
|
+
guard let httpResponse = serviceResponse as? HTTPURLResponse else {
|
|
2366
|
+
self.presentPaymentErrorVC(errorMessage: "Invalid response")
|
|
2367
|
+
return
|
|
2368
|
+
}
|
|
2369
|
+
|
|
2370
|
+
if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
|
|
2371
|
+
if let data = serviceData {
|
|
2372
|
+
do {
|
|
2373
|
+
if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
|
|
2374
|
+
print("Response Data: \(responseObject)")
|
|
2375
|
+
|
|
2376
|
+
// Check if status is 0 and handle the error
|
|
2377
|
+
if let status = responseObject["status"] as? Int, status == 0 {
|
|
2378
|
+
let errorMessage = responseObject["message"] as? String ?? "Unknown error"
|
|
2379
|
+
self.presentPaymentErrorVC(errorMessage: errorMessage)
|
|
2380
|
+
} else {
|
|
2381
|
+
DispatchQueue.main.async {
|
|
2382
|
+
if let paymentDoneVC = self.storyboard?.instantiateViewController(withIdentifier: "PaymentDoneVC") as? PaymentDoneVC {
|
|
2383
|
+
paymentDoneVC.chargeData = responseObject
|
|
2384
|
+
paymentDoneVC.selectedPaymentMethod = self.selectedPaymentMethod
|
|
2385
|
+
paymentDoneVC.easyPayDelegate = self.easyPayDelegate
|
|
2386
|
+
// Pass billing and additional info
|
|
2387
|
+
// Conditionally pass raw FieldItem array
|
|
2388
|
+
paymentDoneVC.visibility = self.visibility
|
|
2389
|
+
|
|
2390
|
+
if self.visibility?.billing == true {
|
|
2391
|
+
paymentDoneVC.billingInfoData = self.billingInfo
|
|
2392
|
+
var billingDict: [String: Any] = [:]
|
|
2393
|
+
self.billingInfo?.forEach { billingDict[$0.name] = $0.value }
|
|
2394
|
+
paymentDoneVC.billingInfo = billingDict
|
|
2395
|
+
}
|
|
2396
|
+
self.navigationController?.pushViewController(paymentDoneVC, animated: true)
|
|
2397
|
+
}
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2400
|
+
} else {
|
|
2401
|
+
self.presentPaymentErrorVC(errorMessage: "Invalid JSON format")
|
|
2402
|
+
}
|
|
2403
|
+
} catch let jsonError {
|
|
2404
|
+
self.presentPaymentErrorVC(errorMessage: "Error parsing JSON: \(jsonError)")
|
|
2405
|
+
}
|
|
2406
|
+
} else {
|
|
2407
|
+
self.presentPaymentErrorVC(errorMessage: "No data received")
|
|
2408
|
+
}
|
|
2409
|
+
} else {
|
|
2410
|
+
if let data = serviceData,
|
|
2411
|
+
let responseObj = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
|
|
2412
|
+
let message = responseObj["message"] as? String {
|
|
2413
|
+
self.presentPaymentErrorVC(errorMessage: message)
|
|
2414
|
+
} else {
|
|
2415
|
+
self.presentPaymentErrorVC(errorMessage: "HTTP Status Code: \(httpResponse.statusCode)")
|
|
2416
|
+
}
|
|
2417
|
+
}
|
|
2418
|
+
}
|
|
2419
|
+
task.resume()
|
|
2420
|
+
}
|
|
2421
|
+
|
|
2422
|
+
}
|
|
2423
|
+
|
|
2424
|
+
//MARK: - Table View
|
|
2425
|
+
@available(iOS 16.0, *)
|
|
2426
|
+
extension BillingInfoVC: UITableViewDelegate, UITableViewDataSource {
|
|
2427
|
+
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
|
2428
|
+
if tableView == tblViewCountryList {
|
|
2429
|
+
return countryList.count
|
|
2430
|
+
}
|
|
2431
|
+
else if tableView == tblViewStateList {
|
|
2432
|
+
return stateList.count
|
|
2433
|
+
}
|
|
2434
|
+
else if tableView == tblViewCityList {
|
|
2435
|
+
return cityList.count
|
|
2436
|
+
}
|
|
2437
|
+
else {
|
|
2438
|
+
return 0
|
|
2439
|
+
}
|
|
2440
|
+
}
|
|
2441
|
+
|
|
2442
|
+
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
|
2443
|
+
if tableView == tblViewCountryList {
|
|
2444
|
+
let cell = tableView.dequeueReusableCell(withIdentifier: "CountryListTVC") as! CountryListTVC
|
|
2445
|
+
let country = countryList[indexPath.row]
|
|
2446
|
+
cell.lblCountryName.text = country["name"] as? String
|
|
2447
|
+
return cell
|
|
2448
|
+
} else if tableView == tblViewStateList {
|
|
2449
|
+
let cell = tableView.dequeueReusableCell(withIdentifier: "StateListTVC") as! StateListTVC
|
|
2450
|
+
let state = stateList[indexPath.row]
|
|
2451
|
+
cell.lblStateName.text = state["name"] as? String
|
|
2452
|
+
return cell
|
|
2453
|
+
} else if tableView == tblViewCityList {
|
|
2454
|
+
let cell = tableView.dequeueReusableCell(withIdentifier: "CityListTVC") as! CityListTVC
|
|
2455
|
+
let city = cityList[indexPath.row]
|
|
2456
|
+
cell.lblCityName.text = city["city_name"] as? String
|
|
2457
|
+
return cell
|
|
2458
|
+
} else {
|
|
2459
|
+
return UITableViewCell()
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2462
|
+
|
|
2463
|
+
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
|
2464
|
+
if tableView == tblViewCountryList {
|
|
2465
|
+
let selectedCountry = countryList[indexPath.row]["name"] as? String
|
|
2466
|
+
txtFieldCountry.text = selectedCountry
|
|
2467
|
+
viewCountryList.isHidden = true
|
|
2468
|
+
|
|
2469
|
+
// Fetch states for the selected country
|
|
2470
|
+
if let country = selectedCountry {
|
|
2471
|
+
getStateListApi(for: country)
|
|
2472
|
+
}
|
|
2473
|
+
} else if tableView == tblViewStateList {
|
|
2474
|
+
let selectedState = stateList[indexPath.row]["name"] as? String
|
|
2475
|
+
txtFieldState.text = selectedState
|
|
2476
|
+
viewStateList.isHidden = true
|
|
2477
|
+
|
|
2478
|
+
// Fetch cities for the selected state and country
|
|
2479
|
+
if let state = selectedState, let country = txtFieldCountry.text {
|
|
2480
|
+
getCityListListApi(for: country, state: state)
|
|
2481
|
+
}
|
|
2482
|
+
} else if tableView == tblViewCityList {
|
|
2483
|
+
let selectedCity = cityList[indexPath.row]["city_name"] as? String
|
|
2484
|
+
txtFieldCity.text = selectedCity
|
|
2485
|
+
viewCityList.isHidden = true
|
|
2486
|
+
|
|
2487
|
+
// Fetch the city list again based on selected state & country
|
|
2488
|
+
if let state = txtFieldState.text, let country = txtFieldCountry.text {
|
|
2489
|
+
getCityListListApi(for: country, state: state)
|
|
2490
|
+
}
|
|
2491
|
+
}
|
|
2492
|
+
|
|
2493
|
+
updateBillingInfoData()
|
|
2494
|
+
}
|
|
2495
|
+
|
|
2496
|
+
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
|
|
2497
|
+
return 50
|
|
2498
|
+
}
|
|
2499
|
+
|
|
2500
|
+
}
|
|
2501
|
+
|
|
2502
|
+
@available(iOS 16.0, *)
|
|
2503
|
+
extension BillingInfoVC: UITextFieldDelegate {
|
|
2504
|
+
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
|
2505
|
+
textField.resignFirstResponder() // Dismiss the keyboard
|
|
2506
|
+
return true
|
|
2507
|
+
}
|
|
2508
|
+
|
|
2509
|
+
// Update billingInfoData when user finishes editing a field
|
|
2510
|
+
func textFieldDidEndEditing(_ textField: UITextField) {
|
|
707
2511
|
switch textField {
|
|
708
2512
|
case txtFieldAddress:
|
|
709
|
-
|
|
2513
|
+
setFieldValue("address", to: textField.text)
|
|
710
2514
|
case txtFieldCountry:
|
|
711
|
-
|
|
2515
|
+
setFieldValue("country", to: textField.text)
|
|
712
2516
|
case txtFieldState:
|
|
713
|
-
|
|
2517
|
+
setFieldValue("state", to: textField.text)
|
|
714
2518
|
case txtFieldCity:
|
|
715
|
-
|
|
2519
|
+
setFieldValue("city", to: textField.text)
|
|
716
2520
|
case txtFieldPostalCode:
|
|
717
|
-
|
|
2521
|
+
setFieldValue("postal_code", to: textField.text)
|
|
718
2522
|
default:
|
|
719
2523
|
break
|
|
720
2524
|
}
|
|
2525
|
+
|
|
2526
|
+
billingInfo = fieldSection?.billing
|
|
721
2527
|
}
|
|
2528
|
+
|
|
722
2529
|
}
|
|
723
2530
|
|