@jimrising/easymerchantsdk-react-native 1.3.5 → 1.3.7
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/README.md +11 -4
- package/android/.gradle/8.10/checksums/checksums.lock +0 -0
- package/android/.gradle/8.10/dependencies-accessors/gc.properties +0 -0
- package/android/.gradle/8.10/fileChanges/last-build.bin +0 -0
- package/android/.gradle/8.10/fileHashes/fileHashes.bin +0 -0
- package/android/.gradle/8.10/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/8.10/gc.properties +0 -0
- package/android/.gradle/8.9/checksums/checksums.lock +0 -0
- package/android/.gradle/8.9/dependencies-accessors/gc.properties +0 -0
- package/android/.gradle/8.9/fileChanges/last-build.bin +0 -0
- package/android/.gradle/8.9/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/8.9/gc.properties +0 -0
- package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/buildOutputCleanup/cache.properties +2 -0
- package/android/.gradle/nb-cache/trust/0B5D6BE682AD6AEE9815EC13516BF075752CAE5AD5BECDCC00315C37622C2FD3 +1 -0
- package/android/.gradle/vcs-1/gc.properties +0 -0
- package/ios/Classes/EasyMerchantSdk.m +42 -29
- package/ios/Classes/EasyMerchantSdk.swift +68 -9
- package/ios/CustomComponents/PlanSelector.swift +28 -30
- package/ios/EnvironmentConfig.swift +30 -30
- package/ios/Example/ViewController.swift +47 -51
- package/ios/Models/AdditionalInfo.swift +43 -6
- package/ios/Models/BillingInfo.swift +50 -6
- package/ios/Models/RecurringIntervals.swift +34 -0
- package/ios/Models/RecurringStartDateType.swift +13 -0
- package/ios/Models/Request.swift +120 -11
- package/ios/Pods/ViewControllers/AdditionalInfoVC.swift +609 -79
- package/ios/Pods/ViewControllers/BillingInfoVC/BillingInfoVC.swift +72 -25
- package/ios/Pods/ViewControllers/EmailVerificationVC.swift +14 -0
- package/ios/Pods/ViewControllers/OTPVerificationVC.swift +459 -42
- package/ios/Pods/ViewControllers/PaymentDoneVC.swift +16 -2
- package/ios/Pods/ViewControllers/PaymentErrorVC.swift +0 -2
- package/ios/Pods/ViewControllers/PaymentInformation/PaymentInfoVC.swift +1553 -372
- package/ios/Pods/ViewControllers/PaymentInformation/SavedAccountsTVC/SavedAccountTVC.swift +6 -2
- package/ios/Pods/ViewControllers/PaymentInformation/SavedCardsTVC/SavedCardsTVC.swift +6 -1
- package/ios/Pods/ViewControllers/ThreeDSecurePaymentDoneVC.swift +105 -0
- package/ios/easymerchantsdk.podspec +1 -1
- package/ios/easymerchantsdk.storyboard +554 -57
- package/package.json +2 -2
- package/.idea/caches/deviceStreaming.xml +0 -571
- package/.idea/em-MobileCheckoutSDK-ReactNative.iml +0 -9
- package/.idea/misc.xml +0 -5
- package/.idea/modules.xml +0 -8
- package/.idea/vcs.xml +0 -6
|
@@ -25,6 +25,11 @@ class AdditionalInfoVC: BaseVC {
|
|
|
25
25
|
@IBOutlet weak var lblEasyMerchant: UILabel!
|
|
26
26
|
@IBOutlet weak var btnCountryCode: UIButton!
|
|
27
27
|
|
|
28
|
+
@IBOutlet weak var lblStarNameField: UILabel!
|
|
29
|
+
@IBOutlet weak var lblStarEmailField: UILabel!
|
|
30
|
+
@IBOutlet weak var lblStarPhoneField: UILabel!
|
|
31
|
+
@IBOutlet weak var lblStarDescriptionField: UILabel!
|
|
32
|
+
|
|
28
33
|
var cardNumber: String?
|
|
29
34
|
var expiryDate: String?
|
|
30
35
|
var cvv: String?
|
|
@@ -56,9 +61,26 @@ class AdditionalInfoVC: BaseVC {
|
|
|
56
61
|
|
|
57
62
|
var isSavedNewAccount: Bool?
|
|
58
63
|
|
|
64
|
+
var request: Request!
|
|
65
|
+
|
|
66
|
+
var chosenPlan: String?
|
|
67
|
+
var startDate: String?
|
|
68
|
+
|
|
59
69
|
override func viewDidLoad() {
|
|
60
70
|
super.viewDidLoad()
|
|
61
71
|
|
|
72
|
+
let amountValue = Double(amount ?? 0)
|
|
73
|
+
let amountText = String(format: "$%.2f", amountValue)
|
|
74
|
+
let submitText = request?.submitButtonText ?? ""
|
|
75
|
+
|
|
76
|
+
let defaultTitle = !submitText.isEmpty
|
|
77
|
+
? "\(submitText) (\(amountText))"
|
|
78
|
+
: "Pay Now (\(amountText))"
|
|
79
|
+
|
|
80
|
+
DispatchQueue.main.async {
|
|
81
|
+
self.btnPayNow.setTitle(defaultTitle, for: .normal)
|
|
82
|
+
}
|
|
83
|
+
|
|
62
84
|
uiFinishingTouchElements()
|
|
63
85
|
|
|
64
86
|
txtFieldName.delegate = self
|
|
@@ -103,15 +125,7 @@ class AdditionalInfoVC: BaseVC {
|
|
|
103
125
|
usaCountry.flag = String.flag(for: "US")
|
|
104
126
|
lblCountryCode.text = "\(usaCountry.flag ?? "") +\(usaCountry.extensionCode ?? "")"
|
|
105
127
|
|
|
106
|
-
|
|
107
|
-
// let am = Int(amount ?? "0")
|
|
108
|
-
// btnPayNow.setTitle("Pay Now ($\(am ?? 0))", for: .normal)
|
|
109
|
-
//
|
|
110
|
-
// print("Received customerID: \(customerID ?? "nil")")
|
|
111
|
-
// print("Received accountID: \(accountID ?? "nil")")
|
|
112
|
-
// print(billingInfoData ?? "")
|
|
113
|
-
// print(isFrom)
|
|
114
|
-
// print(selectedPaymentMethod ?? "")
|
|
128
|
+
print(cvvText ?? "")
|
|
115
129
|
}
|
|
116
130
|
|
|
117
131
|
override func viewWillAppear(_ animated: Bool) {
|
|
@@ -178,6 +192,23 @@ class AdditionalInfoVC: BaseVC {
|
|
|
178
192
|
btnPrevious.titleLabel?.font = UIFont.systemFont(ofSize: fontSize)
|
|
179
193
|
}
|
|
180
194
|
|
|
195
|
+
configureFieldVisibility()
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private func configureFieldVisibility() {
|
|
199
|
+
if let billingInfoData = billingInfoData,
|
|
200
|
+
let additionalInfo = billingInfoData["additional_info"] as? [String: Any] {
|
|
201
|
+
|
|
202
|
+
let name = additionalInfo["name"] as? String
|
|
203
|
+
let email = additionalInfo["email"] as? String
|
|
204
|
+
let phoneNumber = additionalInfo["phone_number"] as? String
|
|
205
|
+
let description = additionalInfo["description"] as? String
|
|
206
|
+
|
|
207
|
+
lblStarNameField.isHidden = name == nil || name?.isEmpty == true
|
|
208
|
+
lblStarEmailField.isHidden = email == nil || email?.isEmpty == true
|
|
209
|
+
lblStarPhoneField.isHidden = phoneNumber == nil || phoneNumber?.isEmpty == true
|
|
210
|
+
lblStarDescriptionField.isHidden = description == nil || description?.isEmpty == true
|
|
211
|
+
}
|
|
181
212
|
}
|
|
182
213
|
|
|
183
214
|
@objc func didTapOutside() {
|
|
@@ -192,26 +223,39 @@ class AdditionalInfoVC: BaseVC {
|
|
|
192
223
|
}
|
|
193
224
|
|
|
194
225
|
@IBAction func actionBtnPrevious(_ sender: UIButton) {
|
|
195
|
-
|
|
226
|
+
navigationController?.popViewController(animated: true)
|
|
196
227
|
}
|
|
197
228
|
|
|
198
229
|
@IBAction func actionBtnPayNow(_ sender: UIButton) {
|
|
199
|
-
if
|
|
200
|
-
|
|
230
|
+
if !lblStarNameField.isHidden &&
|
|
231
|
+
txtFieldName.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true {
|
|
232
|
+
showAlert(title: "Missing Information", message: "Please enter your name.")
|
|
233
|
+
return
|
|
201
234
|
}
|
|
202
|
-
else if
|
|
203
|
-
|
|
235
|
+
else if !lblStarEmailField.isHidden &&
|
|
236
|
+
txtFieldEmail.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true {
|
|
237
|
+
showAlert(title: "Missing Information", message: "Please enter your email address.")
|
|
238
|
+
return
|
|
204
239
|
}
|
|
205
|
-
else if
|
|
206
|
-
|
|
240
|
+
else if !lblStarPhoneField.isHidden &&
|
|
241
|
+
txtFieldPhoneNumber.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true {
|
|
242
|
+
showAlert(title: "Missing Information", message: "Please enter your phone number.")
|
|
243
|
+
return
|
|
207
244
|
}
|
|
208
|
-
else if
|
|
209
|
-
|
|
245
|
+
else if !lblStarDescriptionField.isHidden &&
|
|
246
|
+
txtFieldDescription.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true {
|
|
247
|
+
showAlert(title: "Missing Information", message: "Please enter description text.")
|
|
248
|
+
return
|
|
210
249
|
}
|
|
211
250
|
else {
|
|
212
251
|
if isSavedNewCard {
|
|
213
252
|
if isFrom == "AddNewCard" {
|
|
214
|
-
|
|
253
|
+
if request.enable3DS == true {
|
|
254
|
+
threeDSecurePaymentAddNewCardApi(customerId: UserStoreSingleton.shared.customerId)
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
self.paymentIntentAddNewCardApi(customerId: UserStoreSingleton.shared.customerId)
|
|
258
|
+
}
|
|
215
259
|
}
|
|
216
260
|
}
|
|
217
261
|
else {
|
|
@@ -227,6 +271,9 @@ class AdditionalInfoVC: BaseVC {
|
|
|
227
271
|
emailVerificationVC.billingInfoData = billingInfoData
|
|
228
272
|
emailVerificationVC.selectedPaymentMethod = selectedPaymentMethod
|
|
229
273
|
emailVerificationVC.easyPayDelegate = self.easyPayDelegate
|
|
274
|
+
emailVerificationVC.request = self.request
|
|
275
|
+
emailVerificationVC.chosenPlan = self.chosenPlan
|
|
276
|
+
emailVerificationVC.startDate = self.startDate
|
|
230
277
|
self.navigationController?.pushViewController(emailVerificationVC, animated: true)
|
|
231
278
|
}
|
|
232
279
|
} else if selectedPaymentMethod == "Bank" {
|
|
@@ -239,6 +286,9 @@ class AdditionalInfoVC: BaseVC {
|
|
|
239
286
|
emailVerificationVC.billingInfoData = billingInfoData
|
|
240
287
|
emailVerificationVC.selectedPaymentMethod = selectedPaymentMethod
|
|
241
288
|
emailVerificationVC.easyPayDelegate = self.easyPayDelegate
|
|
289
|
+
emailVerificationVC.request = self.request
|
|
290
|
+
emailVerificationVC.chosenPlan = self.chosenPlan
|
|
291
|
+
emailVerificationVC.startDate = self.startDate
|
|
242
292
|
self.navigationController?.pushViewController(emailVerificationVC, animated: true)
|
|
243
293
|
}
|
|
244
294
|
}
|
|
@@ -249,7 +299,12 @@ class AdditionalInfoVC: BaseVC {
|
|
|
249
299
|
paymentIntentFromShowCardApi()
|
|
250
300
|
}
|
|
251
301
|
else {
|
|
252
|
-
|
|
302
|
+
if request.enable3DS == true {
|
|
303
|
+
threeDSecurePaymentApi()
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
paymentIntentApi()
|
|
307
|
+
}
|
|
253
308
|
}
|
|
254
309
|
}
|
|
255
310
|
else if selectedPaymentMethod == "Bank" {
|
|
@@ -293,13 +348,13 @@ class AdditionalInfoVC: BaseVC {
|
|
|
293
348
|
return
|
|
294
349
|
}
|
|
295
350
|
|
|
296
|
-
var
|
|
297
|
-
|
|
298
|
-
|
|
351
|
+
var urlRequest = URLRequest(url: serviceURL)
|
|
352
|
+
urlRequest.httpMethod = "POST"
|
|
353
|
+
urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
299
354
|
|
|
300
355
|
let token = UserStoreSingleton.shared.clientToken
|
|
301
356
|
print("Setting clientToken header: \(token ?? "None")")
|
|
302
|
-
|
|
357
|
+
urlRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
|
|
303
358
|
|
|
304
359
|
guard let billingInfoData = billingInfoData else {
|
|
305
360
|
print("Billing info data is nil")
|
|
@@ -325,7 +380,7 @@ class AdditionalInfoVC: BaseVC {
|
|
|
325
380
|
"postal_code": billingInfoData["postal_code"] as? String ?? ""
|
|
326
381
|
]
|
|
327
382
|
|
|
328
|
-
|
|
383
|
+
var params: [String: Any] = [
|
|
329
384
|
"name": txtFieldName.text ?? "",
|
|
330
385
|
"email": txtFieldEmail.text ?? "",
|
|
331
386
|
"card_number": cardNumber?.replacingOccurrences(of: " ", with: "") ?? "",
|
|
@@ -341,9 +396,32 @@ class AdditionalInfoVC: BaseVC {
|
|
|
341
396
|
"save_card": 0
|
|
342
397
|
]
|
|
343
398
|
|
|
399
|
+
// Add these if recurring is enabled
|
|
400
|
+
if let req = request, req.is_recurring == true {
|
|
401
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
402
|
+
// Only send start_date if type is .custom and field is not empty
|
|
403
|
+
if let startDateText = startDate, !startDateText.isEmpty {
|
|
404
|
+
let inputFormatter = DateFormatter()
|
|
405
|
+
inputFormatter.dateFormat = "dd/MM/yyyy"
|
|
406
|
+
|
|
407
|
+
let outputFormatter = DateFormatter()
|
|
408
|
+
outputFormatter.dateFormat = "MM/dd/yyyy"
|
|
409
|
+
|
|
410
|
+
if let date = inputFormatter.date(from: startDateText) {
|
|
411
|
+
let apiFormattedDate = outputFormatter.string(from: date)
|
|
412
|
+
params["start_date"] = apiFormattedDate
|
|
413
|
+
} else {
|
|
414
|
+
print("Invalid date format in startDateText")
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
params["interval"] = chosenPlan?.lowercased()
|
|
420
|
+
}
|
|
421
|
+
|
|
344
422
|
do {
|
|
345
423
|
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
346
|
-
|
|
424
|
+
urlRequest.httpBody = jsonData
|
|
347
425
|
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
348
426
|
print("JSON Payload: \(jsonString)")
|
|
349
427
|
}
|
|
@@ -354,7 +432,7 @@ class AdditionalInfoVC: BaseVC {
|
|
|
354
432
|
}
|
|
355
433
|
|
|
356
434
|
let session = URLSession.shared
|
|
357
|
-
let task = session.dataTask(with:
|
|
435
|
+
let task = session.dataTask(with: urlRequest) { (serviceData, serviceResponse, error) in
|
|
358
436
|
|
|
359
437
|
DispatchQueue.main.async {
|
|
360
438
|
self.hideLoadingIndicator()
|
|
@@ -412,7 +490,7 @@ class AdditionalInfoVC: BaseVC {
|
|
|
412
490
|
}
|
|
413
491
|
task.resume()
|
|
414
492
|
}
|
|
415
|
-
|
|
493
|
+
|
|
416
494
|
//MARK: - Credit Card Charge Api from Saved cards
|
|
417
495
|
func paymentIntentFromShowCardApi() {
|
|
418
496
|
showLoadingIndicator()
|
|
@@ -425,24 +503,24 @@ class AdditionalInfoVC: BaseVC {
|
|
|
425
503
|
return
|
|
426
504
|
}
|
|
427
505
|
|
|
428
|
-
var
|
|
429
|
-
|
|
430
|
-
|
|
506
|
+
var uRLRequest = URLRequest(url: serviceURL)
|
|
507
|
+
uRLRequest.httpMethod = "POST"
|
|
508
|
+
uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
431
509
|
|
|
432
510
|
let token = UserStoreSingleton.shared.clientToken
|
|
433
511
|
print("Setting clientToken header: \(token ?? "None")")
|
|
434
|
-
|
|
512
|
+
uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
|
|
435
513
|
|
|
436
514
|
guard let billingInfoData = billingInfoData else {
|
|
437
515
|
print("Billing info data is nil")
|
|
438
516
|
return
|
|
439
517
|
}
|
|
440
518
|
|
|
441
|
-
// // Remove the flag and "+" sign from lblCountryCode.text
|
|
442
|
-
// let countryCode = lblCountryCode.text ?? ""
|
|
443
|
-
// let cleanedCountryCode = countryCode.replacingOccurrences(of: "[^0-9]", with: "", options: .regularExpression)
|
|
444
|
-
// // Format phone number parameter by combining cleaned country code and phone number
|
|
445
|
-
// let phoneNumber = "\(cleanedCountryCode)\(txtFieldPhoneNumber.text ?? "")"
|
|
519
|
+
// // Remove the flag and "+" sign from lblCountryCode.text
|
|
520
|
+
// let countryCode = lblCountryCode.text ?? ""
|
|
521
|
+
// let cleanedCountryCode = countryCode.replacingOccurrences(of: "[^0-9]", with: "", options: .regularExpression)
|
|
522
|
+
// // Format phone number parameter by combining cleaned country code and phone number
|
|
523
|
+
// let phoneNumber = "\(cleanedCountryCode)\(txtFieldPhoneNumber.text ?? "")"
|
|
446
524
|
|
|
447
525
|
// Extract only the digits from the phone number (local only, no country code)
|
|
448
526
|
let localPhone = txtFieldPhoneNumber.text?.components(separatedBy: CharacterSet.decimalDigits.inverted).joined() ?? ""
|
|
@@ -451,7 +529,7 @@ class AdditionalInfoVC: BaseVC {
|
|
|
451
529
|
"name": txtFieldName.text ?? "",
|
|
452
530
|
"email": txtFieldEmail.text ?? "",
|
|
453
531
|
"phone_number": localPhone,
|
|
454
|
-
// "phone_number": phoneNumber,
|
|
532
|
+
// "phone_number": phoneNumber,
|
|
455
533
|
"description": txtFieldDescription.text ?? ""
|
|
456
534
|
]
|
|
457
535
|
|
|
@@ -463,7 +541,7 @@ class AdditionalInfoVC: BaseVC {
|
|
|
463
541
|
"postal_code": billingInfoData["postal_code"] as? String ?? ""
|
|
464
542
|
]
|
|
465
543
|
|
|
466
|
-
|
|
544
|
+
var params: [String: Any] = [
|
|
467
545
|
"name": txtFieldName.text ?? "",
|
|
468
546
|
"email": txtFieldEmail.text ?? "",
|
|
469
547
|
"description": txtFieldDescription.text ?? "",
|
|
@@ -478,9 +556,32 @@ class AdditionalInfoVC: BaseVC {
|
|
|
478
556
|
"cvc" : cvvText ?? ""
|
|
479
557
|
]
|
|
480
558
|
|
|
559
|
+
// Add these if recurring is enabled
|
|
560
|
+
if let req = request, req.is_recurring == true {
|
|
561
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
562
|
+
// Only send start_date if type is .custom and field is not empty
|
|
563
|
+
if let startDateText = startDate, !startDateText.isEmpty {
|
|
564
|
+
let inputFormatter = DateFormatter()
|
|
565
|
+
inputFormatter.dateFormat = "dd/MM/yyyy"
|
|
566
|
+
|
|
567
|
+
let outputFormatter = DateFormatter()
|
|
568
|
+
outputFormatter.dateFormat = "MM/dd/yyyy"
|
|
569
|
+
|
|
570
|
+
if let date = inputFormatter.date(from: startDateText) {
|
|
571
|
+
let apiFormattedDate = outputFormatter.string(from: date)
|
|
572
|
+
params["start_date"] = apiFormattedDate
|
|
573
|
+
} else {
|
|
574
|
+
print("Invalid date format in startDateText")
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
params["interval"] = chosenPlan?.lowercased()
|
|
580
|
+
}
|
|
581
|
+
|
|
481
582
|
do {
|
|
482
583
|
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
483
|
-
|
|
584
|
+
uRLRequest.httpBody = jsonData
|
|
484
585
|
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
485
586
|
print("JSON Payload: \(jsonString)")
|
|
486
587
|
}
|
|
@@ -491,7 +592,7 @@ class AdditionalInfoVC: BaseVC {
|
|
|
491
592
|
}
|
|
492
593
|
|
|
493
594
|
let session = URLSession.shared
|
|
494
|
-
let task = session.dataTask(with:
|
|
595
|
+
let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
|
|
495
596
|
|
|
496
597
|
DispatchQueue.main.async {
|
|
497
598
|
self.hideLoadingIndicator() // Stop loader when response is received
|
|
@@ -561,13 +662,13 @@ class AdditionalInfoVC: BaseVC {
|
|
|
561
662
|
return
|
|
562
663
|
}
|
|
563
664
|
|
|
564
|
-
var
|
|
565
|
-
|
|
566
|
-
|
|
665
|
+
var uRLRequest = URLRequest(url: serviceURL)
|
|
666
|
+
uRLRequest.httpMethod = "POST"
|
|
667
|
+
uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
567
668
|
|
|
568
669
|
let token = UserStoreSingleton.shared.clientToken
|
|
569
670
|
print("Setting clientToken header: \(token ?? "None")")
|
|
570
|
-
|
|
671
|
+
uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
|
|
571
672
|
|
|
572
673
|
guard let billingInfoData = billingInfoData else {
|
|
573
674
|
print("Billing info data is nil")
|
|
@@ -613,6 +714,29 @@ class AdditionalInfoVC: BaseVC {
|
|
|
613
714
|
params["is_default"] = "1"
|
|
614
715
|
}
|
|
615
716
|
|
|
717
|
+
// Add these if recurring is enabled
|
|
718
|
+
if let req = request, req.is_recurring == true {
|
|
719
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
720
|
+
// Only send start_date if type is .custom and field is not empty
|
|
721
|
+
if let startDateText = startDate, !startDateText.isEmpty {
|
|
722
|
+
let inputFormatter = DateFormatter()
|
|
723
|
+
inputFormatter.dateFormat = "dd/MM/yyyy"
|
|
724
|
+
|
|
725
|
+
let outputFormatter = DateFormatter()
|
|
726
|
+
outputFormatter.dateFormat = "MM/dd/yyyy"
|
|
727
|
+
|
|
728
|
+
if let date = inputFormatter.date(from: startDateText) {
|
|
729
|
+
let apiFormattedDate = outputFormatter.string(from: date)
|
|
730
|
+
params["start_date"] = apiFormattedDate
|
|
731
|
+
} else {
|
|
732
|
+
print("Invalid date format in startDateText")
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
params["interval"] = chosenPlan?.lowercased()
|
|
738
|
+
}
|
|
739
|
+
|
|
616
740
|
if let customerId = customerId {
|
|
617
741
|
params["customer"] = customerId
|
|
618
742
|
params["customer_id"] = customerId
|
|
@@ -625,7 +749,7 @@ class AdditionalInfoVC: BaseVC {
|
|
|
625
749
|
|
|
626
750
|
do {
|
|
627
751
|
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
628
|
-
|
|
752
|
+
uRLRequest.httpBody = jsonData
|
|
629
753
|
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
630
754
|
print("JSON Payload: \(jsonString)")
|
|
631
755
|
}
|
|
@@ -636,7 +760,7 @@ class AdditionalInfoVC: BaseVC {
|
|
|
636
760
|
}
|
|
637
761
|
|
|
638
762
|
let session = URLSession.shared
|
|
639
|
-
let task = session.dataTask(with:
|
|
763
|
+
let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
|
|
640
764
|
|
|
641
765
|
DispatchQueue.main.async {
|
|
642
766
|
self.hideLoadingIndicator() // Stop loader when response is received
|
|
@@ -706,24 +830,24 @@ class AdditionalInfoVC: BaseVC {
|
|
|
706
830
|
return
|
|
707
831
|
}
|
|
708
832
|
|
|
709
|
-
var
|
|
710
|
-
|
|
711
|
-
|
|
833
|
+
var uRLRequest = URLRequest(url: serviceURL)
|
|
834
|
+
uRLRequest.httpMethod = "POST"
|
|
835
|
+
uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
712
836
|
|
|
713
837
|
let token = UserStoreSingleton.shared.clientToken
|
|
714
838
|
print("Setting clientToken header: \(token ?? "None")")
|
|
715
|
-
|
|
839
|
+
uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
|
|
716
840
|
|
|
717
841
|
guard let billingInfoData = billingInfoData else {
|
|
718
842
|
print("Billing info data is nil")
|
|
719
843
|
return
|
|
720
844
|
}
|
|
721
845
|
|
|
722
|
-
// // Remove the flag and "+" sign from lblCountryCode.text
|
|
723
|
-
// let countryCode = lblCountryCode.text ?? ""
|
|
724
|
-
// let cleanedCountryCode = countryCode.replacingOccurrences(of: "[^0-9]", with: "", options: .regularExpression)
|
|
725
|
-
// // Format phone number parameter by combining cleaned country code and phone number
|
|
726
|
-
// let phoneNumber = "\(cleanedCountryCode)\(txtFieldPhoneNumber.text ?? "")"
|
|
846
|
+
// // Remove the flag and "+" sign from lblCountryCode.text
|
|
847
|
+
// let countryCode = lblCountryCode.text ?? ""
|
|
848
|
+
// let cleanedCountryCode = countryCode.replacingOccurrences(of: "[^0-9]", with: "", options: .regularExpression)
|
|
849
|
+
// // Format phone number parameter by combining cleaned country code and phone number
|
|
850
|
+
// let phoneNumber = "\(cleanedCountryCode)\(txtFieldPhoneNumber.text ?? "")"
|
|
727
851
|
|
|
728
852
|
// Extract only the digits from the phone number (local only, no country code)
|
|
729
853
|
let localPhone = txtFieldPhoneNumber.text?.components(separatedBy: CharacterSet.decimalDigits.inverted).joined() ?? ""
|
|
@@ -732,7 +856,7 @@ class AdditionalInfoVC: BaseVC {
|
|
|
732
856
|
"name": txtFieldName.text ?? "",
|
|
733
857
|
"email": txtFieldEmail.text ?? "",
|
|
734
858
|
"phone_number": localPhone,
|
|
735
|
-
|
|
859
|
+
// "phone_number": phoneNumber,
|
|
736
860
|
"description": txtFieldDescription.text ?? ""
|
|
737
861
|
]
|
|
738
862
|
|
|
@@ -744,7 +868,7 @@ class AdditionalInfoVC: BaseVC {
|
|
|
744
868
|
"postal_code": billingInfoData["postal_code"] as? String ?? ""
|
|
745
869
|
]
|
|
746
870
|
|
|
747
|
-
|
|
871
|
+
var params: [String: Any] = [
|
|
748
872
|
"name": txtFieldName.text ?? "",
|
|
749
873
|
"email": txtFieldEmail.text ?? "",
|
|
750
874
|
"description": txtFieldDescription.text ?? "",
|
|
@@ -759,11 +883,34 @@ class AdditionalInfoVC: BaseVC {
|
|
|
759
883
|
"levelIndicator": 1,
|
|
760
884
|
]
|
|
761
885
|
|
|
886
|
+
// Add these if recurring is enabled
|
|
887
|
+
if let req = request, req.is_recurring == true {
|
|
888
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
889
|
+
// Only send start_date if type is .custom and field is not empty
|
|
890
|
+
if let startDateText = startDate, !startDateText.isEmpty {
|
|
891
|
+
let inputFormatter = DateFormatter()
|
|
892
|
+
inputFormatter.dateFormat = "dd/MM/yyyy"
|
|
893
|
+
|
|
894
|
+
let outputFormatter = DateFormatter()
|
|
895
|
+
outputFormatter.dateFormat = "MM/dd/yyyy"
|
|
896
|
+
|
|
897
|
+
if let date = inputFormatter.date(from: startDateText) {
|
|
898
|
+
let apiFormattedDate = outputFormatter.string(from: date)
|
|
899
|
+
params["start_date"] = apiFormattedDate
|
|
900
|
+
} else {
|
|
901
|
+
print("Invalid date format in startDateText")
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
params["interval"] = chosenPlan?.lowercased()
|
|
907
|
+
}
|
|
908
|
+
|
|
762
909
|
print(params)
|
|
763
910
|
|
|
764
911
|
do {
|
|
765
912
|
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
766
|
-
|
|
913
|
+
uRLRequest.httpBody = jsonData
|
|
767
914
|
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
768
915
|
print("JSON Payload: \(jsonString)")
|
|
769
916
|
}
|
|
@@ -774,7 +921,7 @@ class AdditionalInfoVC: BaseVC {
|
|
|
774
921
|
}
|
|
775
922
|
|
|
776
923
|
let session = URLSession.shared
|
|
777
|
-
let task = session.dataTask(with:
|
|
924
|
+
let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
|
|
778
925
|
|
|
779
926
|
DispatchQueue.main.async {
|
|
780
927
|
self.hideLoadingIndicator() // Stop loader when response is received
|
|
@@ -845,24 +992,24 @@ class AdditionalInfoVC: BaseVC {
|
|
|
845
992
|
return
|
|
846
993
|
}
|
|
847
994
|
|
|
848
|
-
var
|
|
849
|
-
|
|
850
|
-
|
|
995
|
+
var uRLRequest = URLRequest(url: serviceURL)
|
|
996
|
+
uRLRequest.httpMethod = "POST"
|
|
997
|
+
uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
851
998
|
|
|
852
999
|
let token = UserStoreSingleton.shared.clientToken
|
|
853
1000
|
print("Setting clientToken header: \(token ?? "None")")
|
|
854
|
-
|
|
1001
|
+
uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
|
|
855
1002
|
|
|
856
1003
|
guard let billingInfoData = billingInfoData else {
|
|
857
1004
|
print("Billing info data is nil")
|
|
858
1005
|
return
|
|
859
1006
|
}
|
|
860
1007
|
|
|
861
|
-
// // Remove the flag and "+" sign from lblCountryCode.text
|
|
862
|
-
// let countryCode = lblCountryCode.text ?? ""
|
|
863
|
-
// let cleanedCountryCode = countryCode.replacingOccurrences(of: "[^0-9]", with: "", options: .regularExpression)
|
|
864
|
-
// // Format phone number parameter by combining cleaned country code and phone number
|
|
865
|
-
// let phoneNumber = "\(cleanedCountryCode)\(txtFieldPhoneNumber.text ?? "")"
|
|
1008
|
+
// // Remove the flag and "+" sign from lblCountryCode.text
|
|
1009
|
+
// let countryCode = lblCountryCode.text ?? ""
|
|
1010
|
+
// let cleanedCountryCode = countryCode.replacingOccurrences(of: "[^0-9]", with: "", options: .regularExpression)
|
|
1011
|
+
// // Format phone number parameter by combining cleaned country code and phone number
|
|
1012
|
+
// let phoneNumber = "\(cleanedCountryCode)\(txtFieldPhoneNumber.text ?? "")"
|
|
866
1013
|
|
|
867
1014
|
// Extract only the digits from the phone number (local only, no country code)
|
|
868
1015
|
let localPhone = txtFieldPhoneNumber.text?.components(separatedBy: CharacterSet.decimalDigits.inverted).joined() ?? ""
|
|
@@ -871,7 +1018,7 @@ class AdditionalInfoVC: BaseVC {
|
|
|
871
1018
|
"name": txtFieldName.text ?? "",
|
|
872
1019
|
"email": txtFieldEmail.text ?? "",
|
|
873
1020
|
"phone_number": localPhone,
|
|
874
|
-
|
|
1021
|
+
// "phone_number": phoneNumber,
|
|
875
1022
|
"description": txtFieldDescription.text ?? ""
|
|
876
1023
|
]
|
|
877
1024
|
|
|
@@ -883,7 +1030,7 @@ class AdditionalInfoVC: BaseVC {
|
|
|
883
1030
|
"postal_code": billingInfoData["postal_code"] as? String ?? ""
|
|
884
1031
|
]
|
|
885
1032
|
|
|
886
|
-
|
|
1033
|
+
var params: [String: Any] = [
|
|
887
1034
|
"name": UserStoreSingleton.shared.merchantName ?? "",
|
|
888
1035
|
"account_id": accountID ?? "",
|
|
889
1036
|
"payment_method": "ach",
|
|
@@ -895,11 +1042,34 @@ class AdditionalInfoVC: BaseVC {
|
|
|
895
1042
|
"additional_info": additionalInfo
|
|
896
1043
|
]
|
|
897
1044
|
|
|
1045
|
+
// Add these if recurring is enabled
|
|
1046
|
+
if let req = request, req.is_recurring == true {
|
|
1047
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
1048
|
+
// Only send start_date if type is .custom and field is not empty
|
|
1049
|
+
if let startDateText = startDate, !startDateText.isEmpty {
|
|
1050
|
+
let inputFormatter = DateFormatter()
|
|
1051
|
+
inputFormatter.dateFormat = "dd/MM/yyyy"
|
|
1052
|
+
|
|
1053
|
+
let outputFormatter = DateFormatter()
|
|
1054
|
+
outputFormatter.dateFormat = "MM/dd/yyyy"
|
|
1055
|
+
|
|
1056
|
+
if let date = inputFormatter.date(from: startDateText) {
|
|
1057
|
+
let apiFormattedDate = outputFormatter.string(from: date)
|
|
1058
|
+
params["start_date"] = apiFormattedDate
|
|
1059
|
+
} else {
|
|
1060
|
+
print("Invalid date format in startDateText")
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
params["interval"] = chosenPlan?.lowercased()
|
|
1066
|
+
}
|
|
1067
|
+
|
|
898
1068
|
print(params)
|
|
899
1069
|
|
|
900
1070
|
do {
|
|
901
1071
|
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
902
|
-
|
|
1072
|
+
uRLRequest.httpBody = jsonData
|
|
903
1073
|
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
904
1074
|
print("JSON Payload: \(jsonString)")
|
|
905
1075
|
}
|
|
@@ -910,7 +1080,7 @@ class AdditionalInfoVC: BaseVC {
|
|
|
910
1080
|
}
|
|
911
1081
|
|
|
912
1082
|
let session = URLSession.shared
|
|
913
|
-
let task = session.dataTask(with:
|
|
1083
|
+
let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
|
|
914
1084
|
|
|
915
1085
|
DispatchQueue.main.async {
|
|
916
1086
|
self.hideLoadingIndicator() // Stop loader when response is received
|
|
@@ -981,13 +1151,13 @@ class AdditionalInfoVC: BaseVC {
|
|
|
981
1151
|
return
|
|
982
1152
|
}
|
|
983
1153
|
|
|
984
|
-
var
|
|
985
|
-
|
|
986
|
-
|
|
1154
|
+
var uRLRequest = URLRequest(url: serviceURL)
|
|
1155
|
+
uRLRequest.httpMethod = "POST"
|
|
1156
|
+
uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
987
1157
|
|
|
988
1158
|
let token = UserStoreSingleton.shared.clientToken
|
|
989
1159
|
print("Setting clientToken header: \(token ?? "None")")
|
|
990
|
-
|
|
1160
|
+
uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
|
|
991
1161
|
|
|
992
1162
|
guard let billingInfoData = billingInfoData else {
|
|
993
1163
|
print("Billing info data is nil")
|
|
@@ -1036,11 +1206,34 @@ class AdditionalInfoVC: BaseVC {
|
|
|
1036
1206
|
params["username"] = emailPrefix
|
|
1037
1207
|
}
|
|
1038
1208
|
|
|
1209
|
+
// Add these if recurring is enabled
|
|
1210
|
+
if let req = request, req.is_recurring == true {
|
|
1211
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
1212
|
+
// Only send start_date if type is .custom and field is not empty
|
|
1213
|
+
if let startDateText = startDate, !startDateText.isEmpty {
|
|
1214
|
+
let inputFormatter = DateFormatter()
|
|
1215
|
+
inputFormatter.dateFormat = "dd/MM/yyyy"
|
|
1216
|
+
|
|
1217
|
+
let outputFormatter = DateFormatter()
|
|
1218
|
+
outputFormatter.dateFormat = "MM/dd/yyyy"
|
|
1219
|
+
|
|
1220
|
+
if let date = inputFormatter.date(from: startDateText) {
|
|
1221
|
+
let apiFormattedDate = outputFormatter.string(from: date)
|
|
1222
|
+
params["start_date"] = apiFormattedDate
|
|
1223
|
+
} else {
|
|
1224
|
+
print("Invalid date format in startDateText")
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
params["interval"] = chosenPlan?.lowercased()
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1039
1232
|
print(params)
|
|
1040
1233
|
|
|
1041
1234
|
do {
|
|
1042
1235
|
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
1043
|
-
|
|
1236
|
+
uRLRequest.httpBody = jsonData
|
|
1044
1237
|
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
1045
1238
|
print("JSON Payload: \(jsonString)")
|
|
1046
1239
|
}
|
|
@@ -1051,7 +1244,7 @@ class AdditionalInfoVC: BaseVC {
|
|
|
1051
1244
|
}
|
|
1052
1245
|
|
|
1053
1246
|
let session = URLSession.shared
|
|
1054
|
-
let task = session.dataTask(with:
|
|
1247
|
+
let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
|
|
1055
1248
|
|
|
1056
1249
|
DispatchQueue.main.async {
|
|
1057
1250
|
self.hideLoadingIndicator() // Stop loader when response is received
|
|
@@ -1109,6 +1302,343 @@ class AdditionalInfoVC: BaseVC {
|
|
|
1109
1302
|
task.resume()
|
|
1110
1303
|
}
|
|
1111
1304
|
|
|
1305
|
+
// MARK: - 3DS Functionality
|
|
1306
|
+
|
|
1307
|
+
// MARK: - Credit Card Charge Api If Billing info is not nil and Without Login.
|
|
1308
|
+
func threeDSecurePaymentApi() {
|
|
1309
|
+
showLoadingIndicator()
|
|
1310
|
+
|
|
1311
|
+
let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.threeDSecure.path()
|
|
1312
|
+
|
|
1313
|
+
guard let serviceURL = URL(string: fullURL) else {
|
|
1314
|
+
print("Invalid URL")
|
|
1315
|
+
hideLoadingIndicator()
|
|
1316
|
+
return
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
var uRLRequest = URLRequest(url: serviceURL)
|
|
1320
|
+
uRLRequest.httpMethod = "POST"
|
|
1321
|
+
uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
1322
|
+
|
|
1323
|
+
let token = UserStoreSingleton.shared.clientToken
|
|
1324
|
+
print("Setting clientToken header: \(token ?? "None")")
|
|
1325
|
+
uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
|
|
1326
|
+
|
|
1327
|
+
// Add API headers
|
|
1328
|
+
if let apiKey = EnvironmentConfig.apiKey,
|
|
1329
|
+
let apiSecret = EnvironmentConfig.apiSecret {
|
|
1330
|
+
uRLRequest.addValue(apiKey, forHTTPHeaderField: "x-api-key")
|
|
1331
|
+
uRLRequest.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
guard let billingInfoData = billingInfoData else {
|
|
1335
|
+
print("Billing info data is nil")
|
|
1336
|
+
hideLoadingIndicator()
|
|
1337
|
+
return
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
// Extract only the digits from the phone number (local only, no country code)
|
|
1341
|
+
let localPhone = txtFieldPhoneNumber.text?.components(separatedBy: CharacterSet.decimalDigits.inverted).joined() ?? ""
|
|
1342
|
+
|
|
1343
|
+
let additionalInfo: [String: Any] = [
|
|
1344
|
+
"name": txtFieldName.text ?? "",
|
|
1345
|
+
"email": txtFieldEmail.text ?? "",
|
|
1346
|
+
"phone_number": localPhone,
|
|
1347
|
+
"description": txtFieldDescription.text ?? ""
|
|
1348
|
+
]
|
|
1349
|
+
|
|
1350
|
+
let billingInfo: [String: Any] = [
|
|
1351
|
+
"address": billingInfoData["address"] as? String ?? "",
|
|
1352
|
+
"country": billingInfoData["country"] as? String ?? "",
|
|
1353
|
+
"state": billingInfoData["state"] as? String ?? "",
|
|
1354
|
+
"city": billingInfoData["city"] as? String ?? "",
|
|
1355
|
+
"postal_code": billingInfoData["postal_code"] as? String ?? ""
|
|
1356
|
+
]
|
|
1357
|
+
|
|
1358
|
+
var params: [String: Any] = [
|
|
1359
|
+
"name": nameOnCard ?? "",
|
|
1360
|
+
"email": txtFieldEmail.text ?? "",
|
|
1361
|
+
"card_number": cardNumber?.replacingOccurrences(of: " ", with: "") ?? "",
|
|
1362
|
+
"cardholder_name": nameOnCard ?? "",
|
|
1363
|
+
"exp_month": expiryDate?.components(separatedBy: "/").first ?? "",
|
|
1364
|
+
"exp_year": expiryDate?.components(separatedBy: "/").last ?? "",
|
|
1365
|
+
"cvc": cvv ?? "",
|
|
1366
|
+
"description": "Test",
|
|
1367
|
+
"currency": "usd",
|
|
1368
|
+
"tokenize": request.tokenOnly ?? false,
|
|
1369
|
+
"address": billingInfoData["address"] as? String ?? "",
|
|
1370
|
+
"billing_info": billingInfo,
|
|
1371
|
+
"additional_info": additionalInfo
|
|
1372
|
+
]
|
|
1373
|
+
|
|
1374
|
+
// Add these if recurring is enabled
|
|
1375
|
+
if let req = request, req.is_recurring == true {
|
|
1376
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
1377
|
+
// Only send start_date if type is .custom and field is not empty
|
|
1378
|
+
if let startDateText = startDate, !startDateText.isEmpty {
|
|
1379
|
+
let inputFormatter = DateFormatter()
|
|
1380
|
+
inputFormatter.dateFormat = "dd/MM/yyyy"
|
|
1381
|
+
|
|
1382
|
+
let outputFormatter = DateFormatter()
|
|
1383
|
+
outputFormatter.dateFormat = "MM/dd/yyyy"
|
|
1384
|
+
|
|
1385
|
+
if let date = inputFormatter.date(from: startDateText) {
|
|
1386
|
+
let apiFormattedDate = outputFormatter.string(from: date)
|
|
1387
|
+
params["start_date"] = apiFormattedDate
|
|
1388
|
+
} else {
|
|
1389
|
+
print("Invalid date format in startDateText")
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
params["interval"] = chosenPlan?.lowercased()
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
do {
|
|
1398
|
+
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
1399
|
+
uRLRequest.httpBody = jsonData
|
|
1400
|
+
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
1401
|
+
print("JSON Payload: \(jsonString)")
|
|
1402
|
+
}
|
|
1403
|
+
} catch let error {
|
|
1404
|
+
print("Error creating JSON data: \(error)")
|
|
1405
|
+
hideLoadingIndicator()
|
|
1406
|
+
return
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
let session = URLSession.shared
|
|
1410
|
+
let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
|
|
1411
|
+
|
|
1412
|
+
DispatchQueue.main.async {
|
|
1413
|
+
self.hideLoadingIndicator() // Stop loader when response is received
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
if let error = error {
|
|
1417
|
+
print("Error: \(error.localizedDescription)")
|
|
1418
|
+
return
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
guard let httpResponse = serviceResponse as? HTTPURLResponse else {
|
|
1422
|
+
print("Invalid response")
|
|
1423
|
+
return
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
|
|
1427
|
+
if let data = serviceData {
|
|
1428
|
+
do {
|
|
1429
|
+
if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
|
|
1430
|
+
print("Response Data: \(responseObject)")
|
|
1431
|
+
|
|
1432
|
+
// Check if status is 0 and handle the error
|
|
1433
|
+
if let status = responseObject["status"] as? Int, status == 0 {
|
|
1434
|
+
let errorMessage = responseObject["message"] as? String ?? "Unknown error"
|
|
1435
|
+
self.presentPaymentErrorVC(errorMessage: errorMessage)
|
|
1436
|
+
} else {
|
|
1437
|
+
DispatchQueue.main.async {
|
|
1438
|
+
if let paymentDoneVC = self.storyboard?.instantiateViewController(withIdentifier: "ThreeDSecurePaymentDoneVC") as? ThreeDSecurePaymentDoneVC {
|
|
1439
|
+
|
|
1440
|
+
let urlString = responseObject["redirect_url"] as? String ?? responseObject["location_url"] as? String ?? ""
|
|
1441
|
+
paymentDoneVC.redirectURL = urlString
|
|
1442
|
+
paymentDoneVC.chargeData = responseObject
|
|
1443
|
+
paymentDoneVC.easyPayDelegate = self.easyPayDelegate
|
|
1444
|
+
|
|
1445
|
+
self.navigationController?.pushViewController(paymentDoneVC, animated: true)
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
} else {
|
|
1450
|
+
self.presentPaymentErrorVC(errorMessage: "Invalid JSON format")
|
|
1451
|
+
}
|
|
1452
|
+
} catch let jsonError {
|
|
1453
|
+
self.presentPaymentErrorVC(errorMessage: "Error parsing JSON: \(jsonError)")
|
|
1454
|
+
}
|
|
1455
|
+
} else {
|
|
1456
|
+
self.presentPaymentErrorVC(errorMessage: "No data received")
|
|
1457
|
+
}
|
|
1458
|
+
} else {
|
|
1459
|
+
if let data = serviceData,
|
|
1460
|
+
let responseObj = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
|
|
1461
|
+
let message = responseObj["message"] as? String {
|
|
1462
|
+
self.presentPaymentErrorVC(errorMessage: message)
|
|
1463
|
+
} else {
|
|
1464
|
+
self.presentPaymentErrorVC(errorMessage: "HTTP Status Code: \(httpResponse.statusCode)")
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
task.resume()
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
// MARK: - Credit Card Charge Api If Billing info is not nil With Login from Add New Card.
|
|
1472
|
+
func threeDSecurePaymentAddNewCardApi(customerId: String?) {
|
|
1473
|
+
showLoadingIndicator()
|
|
1474
|
+
|
|
1475
|
+
let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.threeDSecure.path()
|
|
1476
|
+
|
|
1477
|
+
guard let serviceURL = URL(string: fullURL) else {
|
|
1478
|
+
print("Invalid URL")
|
|
1479
|
+
hideLoadingIndicator()
|
|
1480
|
+
return
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
var uRLRequest = URLRequest(url: serviceURL)
|
|
1484
|
+
uRLRequest.httpMethod = "POST"
|
|
1485
|
+
uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
1486
|
+
|
|
1487
|
+
let token = UserStoreSingleton.shared.clientToken
|
|
1488
|
+
print("Setting clientToken header: \(token ?? "None")")
|
|
1489
|
+
uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
|
|
1490
|
+
|
|
1491
|
+
// Add API headers
|
|
1492
|
+
if let apiKey = EnvironmentConfig.apiKey,
|
|
1493
|
+
let apiSecret = EnvironmentConfig.apiSecret {
|
|
1494
|
+
uRLRequest.addValue(apiKey, forHTTPHeaderField: "x-api-key")
|
|
1495
|
+
uRLRequest.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
guard let billingInfoData = billingInfoData else {
|
|
1499
|
+
print("Billing info data is nil")
|
|
1500
|
+
hideLoadingIndicator()
|
|
1501
|
+
return
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
// Extract only the digits from the phone number (local only, no country code)
|
|
1505
|
+
let localPhone = txtFieldPhoneNumber.text?.components(separatedBy: CharacterSet.decimalDigits.inverted).joined() ?? ""
|
|
1506
|
+
|
|
1507
|
+
let additionalInfo: [String: Any] = [
|
|
1508
|
+
"name": txtFieldName.text ?? "",
|
|
1509
|
+
"email": txtFieldEmail.text ?? "",
|
|
1510
|
+
"phone_number": localPhone,
|
|
1511
|
+
"description": txtFieldDescription.text ?? ""
|
|
1512
|
+
]
|
|
1513
|
+
|
|
1514
|
+
let billingInfo: [String: Any] = [
|
|
1515
|
+
"address": billingInfoData["address"] as? String ?? "",
|
|
1516
|
+
"country": billingInfoData["country"] as? String ?? "",
|
|
1517
|
+
"state": billingInfoData["state"] as? String ?? "",
|
|
1518
|
+
"city": billingInfoData["city"] as? String ?? "",
|
|
1519
|
+
"postal_code": billingInfoData["postal_code"] as? String ?? ""
|
|
1520
|
+
]
|
|
1521
|
+
|
|
1522
|
+
var params: [String: Any] = [
|
|
1523
|
+
"name": nameOnCard ?? "",
|
|
1524
|
+
"email": txtFieldEmail.text ?? "",
|
|
1525
|
+
"card_number": cardNumber?.replacingOccurrences(of: " ", with: "") ?? "",
|
|
1526
|
+
"cardholder_name": nameOnCard ?? "",
|
|
1527
|
+
"exp_month": expiryDate?.components(separatedBy: "/").first ?? "",
|
|
1528
|
+
"exp_year": expiryDate?.components(separatedBy: "/").last ?? "",
|
|
1529
|
+
"cvc": cvv ?? "",
|
|
1530
|
+
"description": "Test",
|
|
1531
|
+
"currency": "usd",
|
|
1532
|
+
"tokenize": request.tokenOnly ?? false,
|
|
1533
|
+
"address": billingInfoData["address"] as? String ?? "",
|
|
1534
|
+
"billing_info": billingInfo,
|
|
1535
|
+
"additional_info": additionalInfo,
|
|
1536
|
+
"save_card": isSavedNewCard ? 1 : 0,
|
|
1537
|
+
"customer_id": customerId ?? ""
|
|
1538
|
+
]
|
|
1539
|
+
|
|
1540
|
+
// Add is_default parameter if save_card is 1
|
|
1541
|
+
if isSavedNewCard {
|
|
1542
|
+
params["is_default"] = "1"
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
// Add these if recurring is enabled
|
|
1546
|
+
if let req = request, req.is_recurring == true {
|
|
1547
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
1548
|
+
// Only send start_date if type is .custom and field is not empty
|
|
1549
|
+
if let startDateText = startDate, !startDateText.isEmpty {
|
|
1550
|
+
let inputFormatter = DateFormatter()
|
|
1551
|
+
inputFormatter.dateFormat = "dd/MM/yyyy"
|
|
1552
|
+
|
|
1553
|
+
let outputFormatter = DateFormatter()
|
|
1554
|
+
outputFormatter.dateFormat = "MM/dd/yyyy"
|
|
1555
|
+
|
|
1556
|
+
if let date = inputFormatter.date(from: startDateText) {
|
|
1557
|
+
let apiFormattedDate = outputFormatter.string(from: date)
|
|
1558
|
+
params["start_date"] = apiFormattedDate
|
|
1559
|
+
} else {
|
|
1560
|
+
print("Invalid date format in startDateText")
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
params["interval"] = chosenPlan?.lowercased()
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
do {
|
|
1569
|
+
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
1570
|
+
uRLRequest.httpBody = jsonData
|
|
1571
|
+
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
1572
|
+
print("JSON Payload: \(jsonString)")
|
|
1573
|
+
}
|
|
1574
|
+
} catch let error {
|
|
1575
|
+
print("Error creating JSON data: \(error)")
|
|
1576
|
+
hideLoadingIndicator()
|
|
1577
|
+
return
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
let session = URLSession.shared
|
|
1581
|
+
let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
|
|
1582
|
+
|
|
1583
|
+
DispatchQueue.main.async {
|
|
1584
|
+
self.hideLoadingIndicator() // Stop loader when response is received
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
if let error = error {
|
|
1588
|
+
print("Error: \(error.localizedDescription)")
|
|
1589
|
+
return
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
guard let httpResponse = serviceResponse as? HTTPURLResponse else {
|
|
1593
|
+
print("Invalid response")
|
|
1594
|
+
return
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
|
|
1598
|
+
if let data = serviceData {
|
|
1599
|
+
do {
|
|
1600
|
+
if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
|
|
1601
|
+
print("Response Data: \(responseObject)")
|
|
1602
|
+
|
|
1603
|
+
// Check if status is 0 and handle the error
|
|
1604
|
+
if let status = responseObject["status"] as? Int, status == 0 {
|
|
1605
|
+
let errorMessage = responseObject["message"] as? String ?? "Unknown error"
|
|
1606
|
+
self.presentPaymentErrorVC(errorMessage: errorMessage)
|
|
1607
|
+
} else {
|
|
1608
|
+
DispatchQueue.main.async {
|
|
1609
|
+
if let paymentDoneVC = self.storyboard?.instantiateViewController(withIdentifier: "ThreeDSecurePaymentDoneVC") as? ThreeDSecurePaymentDoneVC {
|
|
1610
|
+
|
|
1611
|
+
let urlString = responseObject["redirect_url"] as? String ?? responseObject["location_url"] as? String ?? ""
|
|
1612
|
+
paymentDoneVC.redirectURL = urlString
|
|
1613
|
+
paymentDoneVC.chargeData = responseObject
|
|
1614
|
+
paymentDoneVC.easyPayDelegate = self.easyPayDelegate
|
|
1615
|
+
|
|
1616
|
+
self.navigationController?.pushViewController(paymentDoneVC, animated: true)
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
} else {
|
|
1621
|
+
self.presentPaymentErrorVC(errorMessage: "Invalid JSON format")
|
|
1622
|
+
}
|
|
1623
|
+
} catch let jsonError {
|
|
1624
|
+
self.presentPaymentErrorVC(errorMessage: "Error parsing JSON: \(jsonError)")
|
|
1625
|
+
}
|
|
1626
|
+
} else {
|
|
1627
|
+
self.presentPaymentErrorVC(errorMessage: "No data received")
|
|
1628
|
+
}
|
|
1629
|
+
} else {
|
|
1630
|
+
if let data = serviceData,
|
|
1631
|
+
let responseObj = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
|
|
1632
|
+
let message = responseObj["message"] as? String {
|
|
1633
|
+
self.presentPaymentErrorVC(errorMessage: message)
|
|
1634
|
+
} else {
|
|
1635
|
+
self.presentPaymentErrorVC(errorMessage: "HTTP Status Code: \(httpResponse.statusCode)")
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
task.resume()
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1112
1642
|
}
|
|
1113
1643
|
|
|
1114
1644
|
extension AdditionalInfoVC: UITextFieldDelegate {
|