@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.
Files changed (44) hide show
  1. package/README.md +11 -4
  2. package/android/.gradle/8.10/checksums/checksums.lock +0 -0
  3. package/android/.gradle/8.10/dependencies-accessors/gc.properties +0 -0
  4. package/android/.gradle/8.10/fileChanges/last-build.bin +0 -0
  5. package/android/.gradle/8.10/fileHashes/fileHashes.bin +0 -0
  6. package/android/.gradle/8.10/fileHashes/fileHashes.lock +0 -0
  7. package/android/.gradle/8.10/gc.properties +0 -0
  8. package/android/.gradle/8.9/checksums/checksums.lock +0 -0
  9. package/android/.gradle/8.9/dependencies-accessors/gc.properties +0 -0
  10. package/android/.gradle/8.9/fileChanges/last-build.bin +0 -0
  11. package/android/.gradle/8.9/fileHashes/fileHashes.lock +0 -0
  12. package/android/.gradle/8.9/gc.properties +0 -0
  13. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  14. package/android/.gradle/buildOutputCleanup/cache.properties +2 -0
  15. package/android/.gradle/nb-cache/trust/0B5D6BE682AD6AEE9815EC13516BF075752CAE5AD5BECDCC00315C37622C2FD3 +1 -0
  16. package/android/.gradle/vcs-1/gc.properties +0 -0
  17. package/ios/Classes/EasyMerchantSdk.m +42 -29
  18. package/ios/Classes/EasyMerchantSdk.swift +68 -9
  19. package/ios/CustomComponents/PlanSelector.swift +28 -30
  20. package/ios/EnvironmentConfig.swift +30 -30
  21. package/ios/Example/ViewController.swift +47 -51
  22. package/ios/Models/AdditionalInfo.swift +43 -6
  23. package/ios/Models/BillingInfo.swift +50 -6
  24. package/ios/Models/RecurringIntervals.swift +34 -0
  25. package/ios/Models/RecurringStartDateType.swift +13 -0
  26. package/ios/Models/Request.swift +120 -11
  27. package/ios/Pods/ViewControllers/AdditionalInfoVC.swift +609 -79
  28. package/ios/Pods/ViewControllers/BillingInfoVC/BillingInfoVC.swift +72 -25
  29. package/ios/Pods/ViewControllers/EmailVerificationVC.swift +14 -0
  30. package/ios/Pods/ViewControllers/OTPVerificationVC.swift +459 -42
  31. package/ios/Pods/ViewControllers/PaymentDoneVC.swift +16 -2
  32. package/ios/Pods/ViewControllers/PaymentErrorVC.swift +0 -2
  33. package/ios/Pods/ViewControllers/PaymentInformation/PaymentInfoVC.swift +1553 -372
  34. package/ios/Pods/ViewControllers/PaymentInformation/SavedAccountsTVC/SavedAccountTVC.swift +6 -2
  35. package/ios/Pods/ViewControllers/PaymentInformation/SavedCardsTVC/SavedCardsTVC.swift +6 -1
  36. package/ios/Pods/ViewControllers/ThreeDSecurePaymentDoneVC.swift +105 -0
  37. package/ios/easymerchantsdk.podspec +1 -1
  38. package/ios/easymerchantsdk.storyboard +554 -57
  39. package/package.json +2 -2
  40. package/.idea/caches/deviceStreaming.xml +0 -571
  41. package/.idea/em-MobileCheckoutSDK-ReactNative.iml +0 -9
  42. package/.idea/misc.xml +0 -5
  43. package/.idea/modules.xml +0 -8
  44. package/.idea/vcs.xml +0 -6
@@ -60,6 +60,11 @@ class OTPVerificationVC: BaseVC {
60
60
  var selectedGrailPayAccountType: String?
61
61
  var selectedGrailPayAccountName: String?
62
62
 
63
+ var request: Request!
64
+
65
+ var chosenPlan: String?
66
+ var startDate: String?
67
+
63
68
  override func viewDidLoad() {
64
69
  super.viewDidLoad()
65
70
 
@@ -228,19 +233,19 @@ class OTPVerificationVC: BaseVC {
228
233
  return
229
234
  }
230
235
 
231
- var request = URLRequest(url: serviceURL)
232
- request.httpMethod = "POST"
233
- request.addValue("application/json", forHTTPHeaderField: "Content-Type")
236
+ var uRLRequest = URLRequest(url: serviceURL)
237
+ uRLRequest.httpMethod = "POST"
238
+ uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
234
239
 
235
240
  let token = UserStoreSingleton.shared.clientToken
236
241
  print("Setting clientToken header: \(token ?? "None")")
237
- request.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
242
+ uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
238
243
 
239
244
  // Add API headers
240
245
  if let apiKey = EnvironmentConfig.apiKey,
241
246
  let apiSecret = EnvironmentConfig.apiSecret {
242
- request.addValue(apiKey, forHTTPHeaderField: "x-api-key")
243
- request.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
247
+ uRLRequest.addValue(apiKey, forHTTPHeaderField: "x-api-key")
248
+ uRLRequest.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
244
249
  }
245
250
 
246
251
  let params: [String: Any] = [
@@ -251,7 +256,7 @@ class OTPVerificationVC: BaseVC {
251
256
 
252
257
  do {
253
258
  let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
254
- request.httpBody = jsonData
259
+ uRLRequest.httpBody = jsonData
255
260
  if let jsonString = String(data: jsonData, encoding: .utf8) {
256
261
  print("JSON Payload: \(jsonString)")
257
262
  }
@@ -262,7 +267,7 @@ class OTPVerificationVC: BaseVC {
262
267
  }
263
268
 
264
269
  let session = URLSession.shared
265
- let task = session.dataTask(with: request) { (serviceData, serviceResponse, error) in
270
+ let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
266
271
 
267
272
  DispatchQueue.main.async {
268
273
  self.hideLoadingIndicator() // Stop loader when response is received
@@ -289,10 +294,22 @@ class OTPVerificationVC: BaseVC {
289
294
  let customerId = innerData["customer_id"] as? String {
290
295
  print("Extracted customer_id: \(customerId)")
291
296
  if self.billingInfoData == nil {
292
- self.paymentIntentWithSavedCardApi(customerId: customerId)
297
+ if self.request.enable3DS == true {
298
+ self.threeDSecurePaymentApi(customerId: customerId)
299
+ }
300
+ else {
301
+ self.paymentIntentWithSavedCardApi(customerId: customerId)
302
+ }
293
303
  }
294
304
  else {
295
- self.paymentIntentApi(customerId: customerId)
305
+ // self.paymentIntentApi(customerId: customerId)
306
+ if let request = self.request {
307
+ if request.enable3DS == true {
308
+ self.threeDSecurePaymentSavedCardApi(customerId: customerId)
309
+ } else {
310
+ self.paymentIntentApi(customerId: customerId)
311
+ }
312
+ }
296
313
  }
297
314
  } else {
298
315
  print("customer_id not found in nested data. Falling back to nil.")
@@ -444,19 +461,19 @@ class OTPVerificationVC: BaseVC {
444
461
  return
445
462
  }
446
463
 
447
- var request = URLRequest(url: serviceURL)
448
- request.httpMethod = "POST"
449
- request.addValue("application/json", forHTTPHeaderField: "Content-Type")
464
+ var uRLRequest = URLRequest(url: serviceURL)
465
+ uRLRequest.httpMethod = "POST"
466
+ uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
450
467
 
451
468
  let token = UserStoreSingleton.shared.clientToken
452
469
  print("Setting clientToken header: \(token ?? "None")")
453
- request.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
470
+ uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
454
471
 
455
472
  // Add API headers
456
473
  if let apiKey = EnvironmentConfig.apiKey,
457
474
  let apiSecret = EnvironmentConfig.apiSecret {
458
- request.addValue(apiKey, forHTTPHeaderField: "x-api-key")
459
- request.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
475
+ uRLRequest.addValue(apiKey, forHTTPHeaderField: "x-api-key")
476
+ uRLRequest.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
460
477
  }
461
478
 
462
479
  guard let billingInfoData = billingInfoData else {
@@ -507,11 +524,34 @@ class OTPVerificationVC: BaseVC {
507
524
  params["email"] = email ?? ""
508
525
  }
509
526
 
527
+ // Add these if recurring is enabled
528
+ if let req = request, req.is_recurring == true {
529
+ if let recurringType = req.recurringStartDateType, recurringType == .custom {
530
+ // Only send start_date if type is .custom and field is not empty
531
+ if let startDateText = startDate, !startDateText.isEmpty {
532
+ let inputFormatter = DateFormatter()
533
+ inputFormatter.dateFormat = "dd/MM/yyyy"
534
+
535
+ let outputFormatter = DateFormatter()
536
+ outputFormatter.dateFormat = "MM/dd/yyyy"
537
+
538
+ if let date = inputFormatter.date(from: startDateText) {
539
+ let apiFormattedDate = outputFormatter.string(from: date)
540
+ params["start_date"] = apiFormattedDate
541
+ } else {
542
+ print("Invalid date format in startDateText")
543
+ }
544
+ }
545
+ }
546
+
547
+ params["interval"] = chosenPlan?.lowercased()
548
+ }
549
+
510
550
  print(params)
511
551
 
512
552
  do {
513
553
  let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
514
- request.httpBody = jsonData
554
+ uRLRequest.httpBody = jsonData
515
555
  if let jsonString = String(data: jsonData, encoding: .utf8) {
516
556
  print("JSON Payload: \(jsonString)")
517
557
  }
@@ -522,7 +562,7 @@ class OTPVerificationVC: BaseVC {
522
562
  }
523
563
 
524
564
  let session = URLSession.shared
525
- let task = session.dataTask(with: request) { (serviceData, serviceResponse, error) in
565
+ let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
526
566
 
527
567
  DispatchQueue.main.async {
528
568
  self.hideLoadingIndicator() // Stop loader when response is received
@@ -592,19 +632,19 @@ class OTPVerificationVC: BaseVC {
592
632
  return
593
633
  }
594
634
 
595
- var request = URLRequest(url: serviceURL)
596
- request.httpMethod = "POST"
597
- request.addValue("application/json", forHTTPHeaderField: "Content-Type")
635
+ var urlRequest = URLRequest(url: serviceURL)
636
+ urlRequest.httpMethod = "POST"
637
+ urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
598
638
 
599
639
  let token = UserStoreSingleton.shared.clientToken
600
640
  print("Setting clientToken header: \(token ?? "None")")
601
- request.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
641
+ urlRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
602
642
 
603
643
  // Add API headers
604
644
  if let apiKey = EnvironmentConfig.apiKey,
605
645
  let apiSecret = EnvironmentConfig.apiSecret {
606
- request.addValue(apiKey, forHTTPHeaderField: "x-api-key")
607
- request.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
646
+ urlRequest.addValue(apiKey, forHTTPHeaderField: "x-api-key")
647
+ urlRequest.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
608
648
  }
609
649
 
610
650
  let emailPrefix = email?.components(separatedBy: "@").first ?? ""
@@ -631,11 +671,34 @@ class OTPVerificationVC: BaseVC {
631
671
  params["email"] = email ?? ""
632
672
  }
633
673
 
674
+ // Add these if recurring is enabled
675
+ if let req = request, req.is_recurring == true {
676
+ if let recurringType = req.recurringStartDateType, recurringType == .custom {
677
+ // Only send start_date if type is .custom and field is not empty
678
+ if let startDateText = startDate, !startDateText.isEmpty {
679
+ let inputFormatter = DateFormatter()
680
+ inputFormatter.dateFormat = "dd/MM/yyyy"
681
+
682
+ let outputFormatter = DateFormatter()
683
+ outputFormatter.dateFormat = "MM/dd/yyyy"
684
+
685
+ if let date = inputFormatter.date(from: startDateText) {
686
+ let apiFormattedDate = outputFormatter.string(from: date)
687
+ params["start_date"] = apiFormattedDate
688
+ } else {
689
+ print("Invalid date format in startDateText")
690
+ }
691
+ }
692
+ }
693
+
694
+ params["interval"] = chosenPlan?.lowercased()
695
+ }
696
+
634
697
  print(params)
635
698
 
636
699
  do {
637
700
  let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
638
- request.httpBody = jsonData
701
+ urlRequest.httpBody = jsonData
639
702
  if let jsonString = String(data: jsonData, encoding: .utf8) {
640
703
  print("JSON Payload: \(jsonString)")
641
704
  }
@@ -646,7 +709,7 @@ class OTPVerificationVC: BaseVC {
646
709
  }
647
710
 
648
711
  let session = URLSession.shared
649
- let task = session.dataTask(with: request) { (serviceData, serviceResponse, error) in
712
+ let task = session.dataTask(with: urlRequest) { (serviceData, serviceResponse, error) in
650
713
 
651
714
  DispatchQueue.main.async {
652
715
  self.hideLoadingIndicator() // Stop loader when response is received
@@ -726,19 +789,19 @@ class OTPVerificationVC: BaseVC {
726
789
  return
727
790
  }
728
791
 
729
- var request = URLRequest(url: serviceURL)
730
- request.httpMethod = "POST"
731
- request.addValue("application/json", forHTTPHeaderField: "Content-Type")
792
+ var uRLRequest = URLRequest(url: serviceURL)
793
+ uRLRequest.httpMethod = "POST"
794
+ uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
732
795
 
733
796
  let token = UserStoreSingleton.shared.clientToken
734
797
  print("Setting clientToken header: \(token ?? "None")")
735
- request.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
798
+ uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
736
799
 
737
800
  // Add API headers
738
801
  if let apiKey = EnvironmentConfig.apiKey,
739
802
  let apiSecret = EnvironmentConfig.apiSecret {
740
- request.addValue(apiKey, forHTTPHeaderField: "x-api-key")
741
- request.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
803
+ uRLRequest.addValue(apiKey, forHTTPHeaderField: "x-api-key")
804
+ uRLRequest.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
742
805
  }
743
806
 
744
807
  guard let billingInfoData = billingInfoData else {
@@ -788,11 +851,34 @@ class OTPVerificationVC: BaseVC {
788
851
  params["username"] = emailPrefix
789
852
  }
790
853
 
854
+ // Add these if recurring is enabled
855
+ if let req = request, req.is_recurring == true {
856
+ if let recurringType = req.recurringStartDateType, recurringType == .custom {
857
+ // Only send start_date if type is .custom and field is not empty
858
+ if let startDateText = startDate, !startDateText.isEmpty {
859
+ let inputFormatter = DateFormatter()
860
+ inputFormatter.dateFormat = "dd/MM/yyyy"
861
+
862
+ let outputFormatter = DateFormatter()
863
+ outputFormatter.dateFormat = "MM/dd/yyyy"
864
+
865
+ if let date = inputFormatter.date(from: startDateText) {
866
+ let apiFormattedDate = outputFormatter.string(from: date)
867
+ params["start_date"] = apiFormattedDate
868
+ } else {
869
+ print("Invalid date format in startDateText")
870
+ }
871
+ }
872
+ }
873
+
874
+ params["interval"] = chosenPlan?.lowercased()
875
+ }
876
+
791
877
  print(params)
792
878
 
793
879
  do {
794
880
  let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
795
- request.httpBody = jsonData
881
+ uRLRequest.httpBody = jsonData
796
882
  if let jsonString = String(data: jsonData, encoding: .utf8) {
797
883
  print("JSON Payload: \(jsonString)")
798
884
  }
@@ -803,7 +889,7 @@ class OTPVerificationVC: BaseVC {
803
889
  }
804
890
 
805
891
  let session = URLSession.shared
806
- let task = session.dataTask(with: request) { (serviceData, serviceResponse, error) in
892
+ let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
807
893
 
808
894
  DispatchQueue.main.async {
809
895
  self.hideLoadingIndicator() // Stop loader when response is received
@@ -873,19 +959,19 @@ class OTPVerificationVC: BaseVC {
873
959
  return
874
960
  }
875
961
 
876
- var request = URLRequest(url: serviceURL)
877
- request.httpMethod = "POST"
878
- request.addValue("application/json", forHTTPHeaderField: "Content-Type")
962
+ var uRLRequest = URLRequest(url: serviceURL)
963
+ uRLRequest.httpMethod = "POST"
964
+ uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
879
965
 
880
966
  let token = UserStoreSingleton.shared.clientToken
881
967
  print("Setting clientToken header: \(token ?? "None")")
882
- request.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
968
+ uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
883
969
 
884
970
  // Add API headers
885
971
  if let apiKey = EnvironmentConfig.apiKey,
886
972
  let apiSecret = EnvironmentConfig.apiSecret {
887
- request.addValue(apiKey, forHTTPHeaderField: "x-api-key")
888
- request.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
973
+ uRLRequest.addValue(apiKey, forHTTPHeaderField: "x-api-key")
974
+ uRLRequest.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
889
975
  }
890
976
 
891
977
  let emailPrefix = email?.components(separatedBy: "@").first ?? ""
@@ -911,11 +997,34 @@ class OTPVerificationVC: BaseVC {
911
997
  params["username"] = emailPrefix
912
998
  }
913
999
 
1000
+ // Add these if recurring is enabled
1001
+ if let req = request, req.is_recurring == true {
1002
+ if let recurringType = req.recurringStartDateType, recurringType == .custom {
1003
+ // Only send start_date if type is .custom and field is not empty
1004
+ if let startDateText = startDate, !startDateText.isEmpty {
1005
+ let inputFormatter = DateFormatter()
1006
+ inputFormatter.dateFormat = "dd/MM/yyyy"
1007
+
1008
+ let outputFormatter = DateFormatter()
1009
+ outputFormatter.dateFormat = "MM/dd/yyyy"
1010
+
1011
+ if let date = inputFormatter.date(from: startDateText) {
1012
+ let apiFormattedDate = outputFormatter.string(from: date)
1013
+ params["start_date"] = apiFormattedDate
1014
+ } else {
1015
+ print("Invalid date format in startDateText")
1016
+ }
1017
+ }
1018
+ }
1019
+
1020
+ params["interval"] = chosenPlan?.lowercased()
1021
+ }
1022
+
914
1023
  print(params)
915
1024
 
916
1025
  do {
917
1026
  let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
918
- request.httpBody = jsonData
1027
+ uRLRequest.httpBody = jsonData
919
1028
  if let jsonString = String(data: jsonData, encoding: .utf8) {
920
1029
  print("JSON Payload: \(jsonString)")
921
1030
  }
@@ -926,7 +1035,7 @@ class OTPVerificationVC: BaseVC {
926
1035
  }
927
1036
 
928
1037
  let session = URLSession.shared
929
- let task = session.dataTask(with: request) { (serviceData, serviceResponse, error) in
1038
+ let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
930
1039
 
931
1040
  DispatchQueue.main.async {
932
1041
  self.hideLoadingIndicator() // Stop loader when response is received
@@ -1094,6 +1203,314 @@ class OTPVerificationVC: BaseVC {
1094
1203
  task.resume()
1095
1204
  }
1096
1205
 
1206
+ // MARK: - 3DS Functionality
1207
+
1208
+ // MARK: - Credit Card Charge Api If Billing info is nil and Without Login.
1209
+ func threeDSecurePaymentApi(customerId: String?) {
1210
+ showLoadingIndicator()
1211
+
1212
+ let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.threeDSecure.path()
1213
+
1214
+ guard let serviceURL = URL(string: fullURL) else {
1215
+ print("Invalid URL")
1216
+ hideLoadingIndicator()
1217
+ return
1218
+ }
1219
+
1220
+ var uRLRequest = URLRequest(url: serviceURL)
1221
+ uRLRequest.httpMethod = "POST"
1222
+ uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
1223
+
1224
+ let token = UserStoreSingleton.shared.clientToken
1225
+ print("Setting clientToken header: \(token ?? "None")")
1226
+ uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
1227
+
1228
+ // Add API headers
1229
+ if let apiKey = EnvironmentConfig.apiKey,
1230
+ let apiSecret = EnvironmentConfig.apiSecret {
1231
+ uRLRequest.addValue(apiKey, forHTTPHeaderField: "x-api-key")
1232
+ uRLRequest.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
1233
+ }
1234
+
1235
+ var params: [String: Any] = [
1236
+ "name": nameOnCard ?? "",
1237
+ "email": UserStoreSingleton.shared.merchantEmail ?? "",
1238
+ "card_number": cardNumber?.replacingOccurrences(of: " ", with: "") ?? "",
1239
+ "cardholder_name": nameOnCard ?? "",
1240
+ "exp_month": expiryDate?.components(separatedBy: "/").first ?? "",
1241
+ "exp_year": expiryDate?.components(separatedBy: "/").last ?? "",
1242
+ "cvc": cvv ?? "",
1243
+ "description": "payment checkout",
1244
+ "currency": "usd",
1245
+ "payment_method": "card",
1246
+ "tokenize": request.tokenOnly ?? false,
1247
+ "save_card": 1,
1248
+ "is_default": "1",
1249
+ "customer_id": customerId ?? ""
1250
+ ]
1251
+
1252
+ // Add these if recurring is enabled
1253
+ if let req = request, req.is_recurring == true {
1254
+ if let recurringType = req.recurringStartDateType, recurringType == .custom {
1255
+ // Only send start_date if type is .custom and field is not empty
1256
+ if let startDateText = startDate, !startDateText.isEmpty {
1257
+ let inputFormatter = DateFormatter()
1258
+ inputFormatter.dateFormat = "dd/MM/yyyy"
1259
+
1260
+ let outputFormatter = DateFormatter()
1261
+ outputFormatter.dateFormat = "MM/dd/yyyy"
1262
+
1263
+ if let date = inputFormatter.date(from: startDateText) {
1264
+ let apiFormattedDate = outputFormatter.string(from: date)
1265
+ params["start_date"] = apiFormattedDate
1266
+ } else {
1267
+ print("Invalid date format in startDateText")
1268
+ }
1269
+ }
1270
+ }
1271
+
1272
+ params["interval"] = chosenPlan?.lowercased()
1273
+ }
1274
+
1275
+ do {
1276
+ let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
1277
+ uRLRequest.httpBody = jsonData
1278
+ if let jsonString = String(data: jsonData, encoding: .utf8) {
1279
+ print("JSON Payload: \(jsonString)")
1280
+ }
1281
+ } catch let error {
1282
+ print("Error creating JSON data: \(error)")
1283
+ hideLoadingIndicator()
1284
+ return
1285
+ }
1286
+
1287
+ let session = URLSession.shared
1288
+ let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
1289
+
1290
+ DispatchQueue.main.async {
1291
+ self.hideLoadingIndicator() // Stop loader when response is received
1292
+ }
1293
+
1294
+ if let error = error {
1295
+ print("Error: \(error.localizedDescription)")
1296
+ return
1297
+ }
1298
+
1299
+ guard let httpResponse = serviceResponse as? HTTPURLResponse else {
1300
+ print("Invalid response")
1301
+ return
1302
+ }
1303
+
1304
+ if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
1305
+ if let data = serviceData {
1306
+ do {
1307
+ if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
1308
+ print("Response Data: \(responseObject)")
1309
+
1310
+ // Check if status is 0 and handle the error
1311
+ if let status = responseObject["status"] as? Int, status == 0 {
1312
+ let errorMessage = responseObject["message"] as? String ?? "Unknown error"
1313
+ self.presentPaymentErrorVC(errorMessage: errorMessage)
1314
+ } else {
1315
+ DispatchQueue.main.async {
1316
+ if let paymentDoneVC = self.storyboard?.instantiateViewController(withIdentifier: "ThreeDSecurePaymentDoneVC") as? ThreeDSecurePaymentDoneVC {
1317
+
1318
+ let urlString = responseObject["redirect_url"] as? String ?? responseObject["location_url"] as? String ?? ""
1319
+ paymentDoneVC.redirectURL = urlString
1320
+ paymentDoneVC.chargeData = responseObject
1321
+ paymentDoneVC.easyPayDelegate = self.easyPayDelegate
1322
+
1323
+ self.navigationController?.pushViewController(paymentDoneVC, animated: true)
1324
+ }
1325
+ }
1326
+ }
1327
+ } else {
1328
+ self.presentPaymentErrorVC(errorMessage: "Invalid JSON format")
1329
+ }
1330
+ } catch let jsonError {
1331
+ self.presentPaymentErrorVC(errorMessage: "Error parsing JSON: \(jsonError)")
1332
+ }
1333
+ } else {
1334
+ self.presentPaymentErrorVC(errorMessage: "No data received")
1335
+ }
1336
+ } else {
1337
+ if let data = serviceData,
1338
+ let responseObj = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
1339
+ let message = responseObj["message"] as? String {
1340
+ self.presentPaymentErrorVC(errorMessage: message)
1341
+ } else {
1342
+ self.presentPaymentErrorVC(errorMessage: "HTTP Status Code: \(httpResponse.statusCode)")
1343
+ }
1344
+ }
1345
+ }
1346
+ task.resume()
1347
+ }
1348
+
1349
+ // MARK: - Credit Card Charge Api If Billing info is not nil and Without Login.
1350
+ func threeDSecurePaymentSavedCardApi(customerId: String?) {
1351
+ showLoadingIndicator()
1352
+
1353
+ let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.threeDSecure.path()
1354
+
1355
+ guard let serviceURL = URL(string: fullURL) else {
1356
+ print("Invalid URL")
1357
+ hideLoadingIndicator()
1358
+ return
1359
+ }
1360
+
1361
+ var uRLRequest = URLRequest(url: serviceURL)
1362
+ uRLRequest.httpMethod = "POST"
1363
+ uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
1364
+
1365
+ let token = UserStoreSingleton.shared.clientToken
1366
+ print("Setting clientToken header: \(token ?? "None")")
1367
+ uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
1368
+
1369
+ // Add API headers
1370
+ if let apiKey = EnvironmentConfig.apiKey,
1371
+ let apiSecret = EnvironmentConfig.apiSecret {
1372
+ uRLRequest.addValue(apiKey, forHTTPHeaderField: "x-api-key")
1373
+ uRLRequest.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
1374
+ }
1375
+
1376
+ guard let billingInfoData = billingInfoData else {
1377
+ print("Billing info data is nil")
1378
+ return
1379
+ }
1380
+
1381
+ let additionalInfoFromBilling = billingInfoData["additional_info"] as? [String: Any] ?? [:]
1382
+
1383
+ let additionalInfo: [String: Any] = [
1384
+ "name": additionalInfoFromBilling["name"] as? String ?? "",
1385
+ "email": additionalInfoFromBilling["email"] as? String ?? "",
1386
+ "phone_number": additionalInfoFromBilling["phone_number"] as? String ?? "",
1387
+ "description": additionalInfoFromBilling["description"] as? String ?? ""
1388
+ ]
1389
+
1390
+ let billingInfo: [String: Any] = [
1391
+ "address": billingInfoData["address"] as? String ?? "",
1392
+ "country": billingInfoData["country"] as? String ?? "",
1393
+ "state": billingInfoData["state"] as? String ?? "",
1394
+ "city": billingInfoData["city"] as? String ?? "",
1395
+ "postal_code": billingInfoData["postal_code"] as? String ?? ""
1396
+ ]
1397
+
1398
+ var params: [String: Any] = [
1399
+ "name": nameOnCard ?? "",
1400
+ "email": email ?? "",
1401
+ "card_number": cardNumber?.replacingOccurrences(of: " ", with: "") ?? "",
1402
+ "cardholder_name": nameOnCard ?? "",
1403
+ "exp_month": expiryDate?.components(separatedBy: "/").first ?? "",
1404
+ "exp_year": expiryDate?.components(separatedBy: "/").last ?? "",
1405
+ "cvc": cvv ?? "",
1406
+ "description": "Test",
1407
+ "currency": "usd",
1408
+ "tokenize": request.tokenOnly ?? false,
1409
+ "address": billingInfoData["address"] as? String ?? "",
1410
+ "billing_info": billingInfo,
1411
+ "additional_info": additionalInfo,
1412
+ "save_card": "1",
1413
+ "is_default": "1",
1414
+ "customer_id": customerId ?? ""
1415
+ ]
1416
+
1417
+ // Add these if recurring is enabled
1418
+ if let req = request, req.is_recurring == true {
1419
+ if let recurringType = req.recurringStartDateType, recurringType == .custom {
1420
+ // Only send start_date if type is .custom and field is not empty
1421
+ if let startDateText = startDate, !startDateText.isEmpty {
1422
+ let inputFormatter = DateFormatter()
1423
+ inputFormatter.dateFormat = "dd/MM/yyyy"
1424
+
1425
+ let outputFormatter = DateFormatter()
1426
+ outputFormatter.dateFormat = "MM/dd/yyyy"
1427
+
1428
+ if let date = inputFormatter.date(from: startDateText) {
1429
+ let apiFormattedDate = outputFormatter.string(from: date)
1430
+ params["start_date"] = apiFormattedDate
1431
+ } else {
1432
+ print("Invalid date format in startDateText")
1433
+ }
1434
+ }
1435
+ }
1436
+
1437
+ params["interval"] = chosenPlan?.lowercased()
1438
+ }
1439
+
1440
+ do {
1441
+ let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
1442
+ uRLRequest.httpBody = jsonData
1443
+ if let jsonString = String(data: jsonData, encoding: .utf8) {
1444
+ print("JSON Payload: \(jsonString)")
1445
+ }
1446
+ } catch let error {
1447
+ print("Error creating JSON data: \(error)")
1448
+ hideLoadingIndicator()
1449
+ return
1450
+ }
1451
+
1452
+ let session = URLSession.shared
1453
+ let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
1454
+
1455
+ DispatchQueue.main.async {
1456
+ self.hideLoadingIndicator() // Stop loader when response is received
1457
+ }
1458
+
1459
+ if let error = error {
1460
+ print("Error: \(error.localizedDescription)")
1461
+ return
1462
+ }
1463
+
1464
+ guard let httpResponse = serviceResponse as? HTTPURLResponse else {
1465
+ print("Invalid response")
1466
+ return
1467
+ }
1468
+
1469
+ if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
1470
+ if let data = serviceData {
1471
+ do {
1472
+ if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
1473
+ print("Response Data: \(responseObject)")
1474
+
1475
+ // Check if status is 0 and handle the error
1476
+ if let status = responseObject["status"] as? Int, status == 0 {
1477
+ let errorMessage = responseObject["message"] as? String ?? "Unknown error"
1478
+ self.presentPaymentErrorVC(errorMessage: errorMessage)
1479
+ } else {
1480
+ DispatchQueue.main.async {
1481
+ if let paymentDoneVC = self.storyboard?.instantiateViewController(withIdentifier: "ThreeDSecurePaymentDoneVC") as? ThreeDSecurePaymentDoneVC {
1482
+
1483
+ let urlString = responseObject["redirect_url"] as? String ?? responseObject["location_url"] as? String ?? ""
1484
+ paymentDoneVC.redirectURL = urlString
1485
+ paymentDoneVC.chargeData = responseObject
1486
+ paymentDoneVC.easyPayDelegate = self.easyPayDelegate
1487
+
1488
+ self.navigationController?.pushViewController(paymentDoneVC, animated: true)
1489
+ }
1490
+ }
1491
+ }
1492
+ } else {
1493
+ self.presentPaymentErrorVC(errorMessage: "Invalid JSON format")
1494
+ }
1495
+ } catch let jsonError {
1496
+ self.presentPaymentErrorVC(errorMessage: "Error parsing JSON: \(jsonError)")
1497
+ }
1498
+ } else {
1499
+ self.presentPaymentErrorVC(errorMessage: "No data received")
1500
+ }
1501
+ } else {
1502
+ if let data = serviceData,
1503
+ let responseObj = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
1504
+ let message = responseObj["message"] as? String {
1505
+ self.presentPaymentErrorVC(errorMessage: message)
1506
+ } else {
1507
+ self.presentPaymentErrorVC(errorMessage: "HTTP Status Code: \(httpResponse.statusCode)")
1508
+ }
1509
+ }
1510
+ }
1511
+ task.resume()
1512
+ }
1513
+
1097
1514
  @IBAction func actionBtnCancel(_ sender: UIButton) {
1098
1515
  stopTimer()
1099
1516
  for viewController in self.navigationController?.viewControllers ?? [] {