@jimrising/easymerchantsdk-react-native 1.4.4 → 1.4.5

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 CHANGED
@@ -7,7 +7,7 @@ To add the path of sdk in your project. Open your `package.json` file and inside
7
7
 
8
8
  ```json
9
9
  "dependencies": {
10
- "@jimrising/easymerchantsdk-react-native": "^1.4.4"
10
+ "@jimrising/easymerchantsdk-react-native": "^1.4.5"
11
11
  },
12
12
  ```
13
13
 
@@ -2251,7 +2251,11 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2251
2251
  let showAdditional = fieldSection.visibility.additional
2252
2252
 
2253
2253
  if !showBilling && !showAdditional {
2254
- self.threeDSecurePaymentNewCardApi(customerId: UserStoreSingleton.shared.customerId ?? "")
2254
+ if self.request.secureAuthentication == true {
2255
+ self.threeDSecurePaymentNewCardApi(customerId: UserStoreSingleton.shared.customerId ?? "")
2256
+ } else {
2257
+ self.paymentIntentAddNewCardApi(customerId: UserStoreSingleton.shared.customerId)
2258
+ }
2255
2259
  }
2256
2260
  else if showBilling {
2257
2261
  // Push to BillingInfoVC
@@ -4177,7 +4181,11 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
4177
4181
  if self.isSavedForFuture {
4178
4182
  self.navigateCardDataToEmailVerificationVC()
4179
4183
  } else {
4180
- self.threeDSecurePaymentApi()
4184
+ if self.request.secureAuthentication == true {
4185
+ self.threeDSecurePaymentApi()
4186
+ } else {
4187
+ self.paymentIntentCardApi()
4188
+ }
4181
4189
  }
4182
4190
  }
4183
4191
  else if showBilling {
@@ -5825,6 +5833,214 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
5825
5833
  task.resume()
5826
5834
  }
5827
5835
 
5836
+ // MARK: - Credit Card Charge Api if billing info is available but visibility of billing and additinal is false
5837
+ func paymentIntentCardApi() {
5838
+ showLoadingIndicator()
5839
+
5840
+ let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.charges.path()
5841
+
5842
+ guard let serviceURL = URL(string: fullURL) else {
5843
+ print("Invalid URL")
5844
+ hideLoadingIndicator()
5845
+ return
5846
+ }
5847
+
5848
+ var urlRequest = URLRequest(url: serviceURL)
5849
+ urlRequest.httpMethod = "POST"
5850
+ urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
5851
+
5852
+ let token = UserStoreSingleton.shared.clientToken
5853
+ print("Setting clientToken header: \(token ?? "None")")
5854
+ urlRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
5855
+
5856
+ var params: [String: Any] = [
5857
+ "name": cardNameTextField.text,
5858
+ "email": UserStoreSingleton.shared.merchantEmail ?? "",
5859
+ "card_number": cardNumberTextField.text.replacingOccurrences(of: " ", with: ""),
5860
+ "cardholder_name": cardNameTextField.text,
5861
+ "exp_month": cardExpiryTextField.text.components(separatedBy: "/").first ?? "",
5862
+ "exp_year": cardExpiryTextField.text.components(separatedBy: "/").last ?? "",
5863
+ "cvc": cardCvvTextField.text,
5864
+ "currency": "usd"
5865
+ ]
5866
+
5867
+ if let billingInfoData = request.billingInfoData {
5868
+ do {
5869
+ let fieldSection = try JSONDecoder().decode(FieldSection.self, from: billingInfoData)
5870
+
5871
+ // Billing Info
5872
+ let billing = fieldSection.billing
5873
+ if !billing.isEmpty {
5874
+ var billingDict: [String: Any] = [:]
5875
+ billing.forEach { billingDict[$0.name] = $0.value }
5876
+
5877
+ if let address = billingDict["address"] as? String, !address.isEmpty {
5878
+ params["address"] = address
5879
+ }
5880
+ if let country = billingDict["country"] as? String, !country.isEmpty {
5881
+ params["country"] = country
5882
+ }
5883
+ if let state = billingDict["state"] as? String, !state.isEmpty {
5884
+ params["state"] = state
5885
+ }
5886
+ if let city = billingDict["city"] as? String, !city.isEmpty {
5887
+ params["city"] = city
5888
+ }
5889
+ if let postalCode = billingDict["postal_code"] as? String, !postalCode.isEmpty {
5890
+ params["zip"] = postalCode
5891
+ }
5892
+ }
5893
+
5894
+ // Additional Info
5895
+ let additional = fieldSection.additional
5896
+ if !additional.isEmpty {
5897
+ var additionalDict: [String: Any] = [:]
5898
+ additional.forEach { additionalDict[$0.name] = $0.value }
5899
+
5900
+ if let desc = additionalDict["description"] as? String, !desc.isEmpty {
5901
+ params["description"] = desc
5902
+ } else {
5903
+ params["description"] = "Hosted payment checkout"
5904
+ }
5905
+
5906
+ if let phone = additionalDict["phone_number"] as? String, !phone.isEmpty {
5907
+ params["phone_number"] = phone
5908
+ }
5909
+ if let email = additionalDict["email"] as? String, !email.isEmpty {
5910
+ params["email"] = email
5911
+ }
5912
+ if let name = additionalDict["name"] as? String, !name.isEmpty {
5913
+ params["name"] = name
5914
+ }
5915
+ } else {
5916
+ // If no description in additional info, set default
5917
+ params["description"] = "Hosted payment checkout"
5918
+ }
5919
+
5920
+ } catch {
5921
+ print("Failed to decode FieldSection: \(error)")
5922
+ params["description"] = "Hosted payment checkout"
5923
+ }
5924
+ } else {
5925
+ // Fallback if billingInfoData is missing
5926
+ params["description"] = "Hosted payment checkout"
5927
+ }
5928
+
5929
+ // Add these if recurring is enabled
5930
+ if let req = request, req.is_recurring == true {
5931
+ if let recurringType = req.recurringStartDateType, recurringType == .custom {
5932
+ // Only send start_date if type is .custom and field is not empty
5933
+ if let startDateText = txtFieldStartDateCard?.text, !startDateText.isEmpty {
5934
+ let inputFormatter = DateFormatter()
5935
+ inputFormatter.dateFormat = "dd/MM/yyyy"
5936
+
5937
+ let outputFormatter = DateFormatter()
5938
+ outputFormatter.dateFormat = "MM/dd/yyyy"
5939
+
5940
+ if let date = inputFormatter.date(from: startDateText) {
5941
+ let apiFormattedDate = outputFormatter.string(from: date)
5942
+ params["start_date"] = apiFormattedDate
5943
+ } else {
5944
+ print("Invalid date format in startDateText")
5945
+ }
5946
+ }
5947
+ }
5948
+
5949
+ params["interval"] = txtFieldChosePlanCard.text.lowercased()
5950
+ }
5951
+
5952
+ print(params)
5953
+
5954
+ do {
5955
+ let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
5956
+ urlRequest.httpBody = jsonData
5957
+ if let jsonString = String(data: jsonData, encoding: .utf8) {
5958
+ print("JSON Payload: \(jsonString)")
5959
+ }
5960
+ } catch let error {
5961
+ print("Error creating JSON data: \(error)")
5962
+ hideLoadingIndicator()
5963
+ return
5964
+ }
5965
+
5966
+ let session = URLSession.shared
5967
+ let task = session.dataTask(with: urlRequest) { (serviceData, serviceResponse, error) in
5968
+
5969
+ DispatchQueue.main.async {
5970
+ self.hideLoadingIndicator()
5971
+ }
5972
+
5973
+ if let error = error {
5974
+ print("Error: \(error.localizedDescription)")
5975
+ self.presentPaymentErrorVC(errorMessage: error.localizedDescription)
5976
+ return
5977
+ }
5978
+
5979
+ guard let httpResponse = serviceResponse as? HTTPURLResponse else {
5980
+ print("Invalid response")
5981
+ self.presentPaymentErrorVC(errorMessage: "Invalid response from server.")
5982
+ return
5983
+ }
5984
+
5985
+ if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
5986
+ if let data = serviceData {
5987
+ do {
5988
+ if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
5989
+ print("Response Data: \(responseObject)")
5990
+
5991
+ if let status = responseObject["status"] as? Int, status == 0 {
5992
+ let errorMessage = responseObject["message"] as? String ?? "Unknown error occurred."
5993
+ self.presentPaymentErrorVC(errorMessage: errorMessage)
5994
+ } else {
5995
+ DispatchQueue.main.async {
5996
+ if let paymentDoneVC = self.storyboard?.instantiateViewController(withIdentifier: "PaymentDoneVC") as? PaymentDoneVC {
5997
+ paymentDoneVC.chargeData = responseObject
5998
+ paymentDoneVC.selectedPaymentMethod = self.selectedPaymentMethod
5999
+ paymentDoneVC.easyPayDelegate = self.easyPayDelegate
6000
+ // Pass billing info and additional info if available
6001
+ if let billingInfoData = self.request.billingInfoData,
6002
+ let fieldSection = try? JSONDecoder().decode(FieldSection.self, from: billingInfoData) {
6003
+
6004
+ // Filter billing info: only include non-empty values
6005
+ let filteredBilling = fieldSection.billing.filter { !($0.value.trimmingCharacters(in: .whitespaces).isEmpty) }
6006
+ paymentDoneVC.billingInfoData = filteredBilling
6007
+ var billingDict: [String: Any] = [:]
6008
+ filteredBilling.forEach { billingDict[$0.name] = $0.value }
6009
+ paymentDoneVC.billingInfo = billingDict
6010
+
6011
+ // Filter additional info: only include non-empty values
6012
+ let filteredAdditional = fieldSection.additional.filter { !($0.value.trimmingCharacters(in: .whitespaces).isEmpty) }
6013
+ paymentDoneVC.additionalInfoData = filteredAdditional
6014
+ var additionalDict: [String: Any] = [:]
6015
+ filteredAdditional.forEach { additionalDict[$0.name] = $0.value }
6016
+ paymentDoneVC.additionalInfo = additionalDict
6017
+ }
6018
+ self.navigationController?.pushViewController(paymentDoneVC, animated: true)
6019
+ }
6020
+ }
6021
+ }
6022
+ } else {
6023
+ self.presentPaymentErrorVC(errorMessage: "Invalid JSON format")
6024
+ }
6025
+ } catch let jsonError {
6026
+ self.presentPaymentErrorVC(errorMessage: "Error parsing response: \(jsonError.localizedDescription)")
6027
+ }
6028
+ } else {
6029
+ self.presentPaymentErrorVC(errorMessage: "No data received from server.")
6030
+ }
6031
+ } else {
6032
+ if let data = serviceData,
6033
+ let responseObj = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
6034
+ let message = responseObj["message"] as? String {
6035
+ self.presentPaymentErrorVC(errorMessage: message)
6036
+ } else {
6037
+ self.presentPaymentErrorVC(errorMessage: "HTTP Status Code: \(httpResponse.statusCode)")
6038
+ }
6039
+ }
6040
+ }
6041
+ task.resume()
6042
+ }
6043
+
5828
6044
  // MARK: - Credit Card Charge API (Saved Cards, No Billing Info, Logged In)
5829
6045
  func paymentIntentFromShowSavedCardApi() {
5830
6046
  showLoadingIndicator()
@@ -6432,6 +6648,237 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
6432
6648
  task.resume()
6433
6649
  }
6434
6650
 
6651
+ //MARK: - Credit Card Charge Api from Add new card from saved cards if billing info is available but their visibility is off
6652
+ func paymentIntentAddNewCardApi(customerId: String?) {
6653
+ showLoadingIndicator()
6654
+
6655
+ let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.charges.path()
6656
+
6657
+ guard let serviceURL = URL(string: fullURL) else {
6658
+ print("Invalid URL")
6659
+ hideLoadingIndicator()
6660
+ return
6661
+ }
6662
+
6663
+ var uRLRequest = URLRequest(url: serviceURL)
6664
+ uRLRequest.httpMethod = "POST"
6665
+ uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
6666
+
6667
+ let token = UserStoreSingleton.shared.clientToken
6668
+ print("Setting clientToken header: \(token ?? "None")")
6669
+ uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
6670
+
6671
+ let emailPrefix = UserStoreSingleton.shared.verificationEmail?.components(separatedBy: "@").first ?? ""
6672
+
6673
+ // Get the text fields from the selected cell and trim whitespace
6674
+ let nameText = txtFieldNameOnCardNewCardView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
6675
+ let cvvText = txtFieldCVVNewCardView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
6676
+ let cardNumberText = (txtFieldCardNumberNewCardView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "").replacingOccurrences(of: " ", with: "")
6677
+ let expiryText = txtFieldExpiryDateNewCardView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
6678
+
6679
+ var params: [String: Any] = [
6680
+ "name": nameText,
6681
+ "card_number": cardNumberText,
6682
+ "cardholder_name": nameText,
6683
+ "exp_month": expiryText.components(separatedBy: "/").first ?? "",
6684
+ "exp_year": expiryText.components(separatedBy: "/").last ?? "",
6685
+ "cvc": cvvText,
6686
+ "description": "TestDescription",
6687
+ "currency": "usd",
6688
+ "payment_method": "card",
6689
+ "save_card": isSavedNewCardForFuture ? 1 : 0,
6690
+ "email" : UserStoreSingleton.shared.verificationEmail ?? ""
6691
+ ]
6692
+
6693
+ // Add is_default parameter if save_card is 1
6694
+ if isSavedNewCard {
6695
+ params["is_default"] = "1"
6696
+ }
6697
+
6698
+ if let billingInfoData = request.billingInfoData {
6699
+ do {
6700
+ let fieldSection = try JSONDecoder().decode(FieldSection.self, from: billingInfoData)
6701
+
6702
+ // Billing Info
6703
+ let billing = fieldSection.billing
6704
+ if !billing.isEmpty {
6705
+ var billingDict: [String: Any] = [:]
6706
+ billing.forEach { billingDict[$0.name] = $0.value }
6707
+
6708
+ if let address = billingDict["address"] as? String, !address.isEmpty {
6709
+ params["address"] = address
6710
+ }
6711
+ if let country = billingDict["country"] as? String, !country.isEmpty {
6712
+ params["country"] = country
6713
+ }
6714
+ if let state = billingDict["state"] as? String, !state.isEmpty {
6715
+ params["state"] = state
6716
+ }
6717
+ if let city = billingDict["city"] as? String, !city.isEmpty {
6718
+ params["city"] = city
6719
+ }
6720
+ if let postalCode = billingDict["postal_code"] as? String, !postalCode.isEmpty {
6721
+ params["zip"] = postalCode
6722
+ }
6723
+ }
6724
+
6725
+ // Additional Info
6726
+ let additional = fieldSection.additional
6727
+ if !additional.isEmpty {
6728
+ var additionalDict: [String: Any] = [:]
6729
+ additional.forEach { additionalDict[$0.name] = $0.value }
6730
+
6731
+ if let desc = additionalDict["description"] as? String, !desc.isEmpty {
6732
+ params["description"] = desc
6733
+ } else {
6734
+ params["description"] = "Hosted payment checkout"
6735
+ }
6736
+
6737
+ if let phone = additionalDict["phone_number"] as? String, !phone.isEmpty {
6738
+ params["phone_number"] = phone
6739
+ }
6740
+ if let email = additionalDict["email"] as? String, !email.isEmpty {
6741
+ params["email"] = email
6742
+ }
6743
+ if let name = additionalDict["name"] as? String, !name.isEmpty {
6744
+ params["name"] = name
6745
+ }
6746
+ } else {
6747
+ // If no description in additional info, set default
6748
+ params["description"] = "Hosted payment checkout"
6749
+ }
6750
+
6751
+ } catch {
6752
+ print("Failed to decode FieldSection: \(error)")
6753
+ params["description"] = "Hosted payment checkout"
6754
+ }
6755
+ } else {
6756
+ // Fallback if billingInfoData is missing
6757
+ params["description"] = "Hosted payment checkout"
6758
+ }
6759
+
6760
+ // Add these if recurring is enabled
6761
+ if let req = request, req.is_recurring == true {
6762
+ if let recurringType = req.recurringStartDateType, recurringType == .custom {
6763
+ // Only send start_date if type is .custom and field is not empty
6764
+ if let startDateText = txtFieldSelectDateNewCardView?.text, !startDateText.isEmpty {
6765
+ let inputFormatter = DateFormatter()
6766
+ inputFormatter.dateFormat = "dd/MM/yyyy"
6767
+
6768
+ let outputFormatter = DateFormatter()
6769
+ outputFormatter.dateFormat = "MM/dd/yyyy"
6770
+
6771
+ if let date = inputFormatter.date(from: startDateText) {
6772
+ let apiFormattedDate = outputFormatter.string(from: date)
6773
+ params["start_date"] = apiFormattedDate
6774
+ } else {
6775
+ print("Invalid date format in startDateText")
6776
+ }
6777
+ }
6778
+ }
6779
+
6780
+ params["interval"] = txtFieldSelectPlanNewCardView.text.lowercased()
6781
+ }
6782
+
6783
+ if let customerId = customerId {
6784
+ params["customer"] = customerId
6785
+ params["customer_id"] = customerId
6786
+ } else {
6787
+ params["username"] = emailPrefix
6788
+ params["email"] = UserStoreSingleton.shared.verificationEmail
6789
+ }
6790
+
6791
+ print(params)
6792
+
6793
+ do {
6794
+ let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
6795
+ uRLRequest.httpBody = jsonData
6796
+ if let jsonString = String(data: jsonData, encoding: .utf8) {
6797
+ print("JSON Payload: \(jsonString)")
6798
+ }
6799
+ } catch let error {
6800
+ print("Error creating JSON data: \(error)")
6801
+ hideLoadingIndicator()
6802
+ return
6803
+ }
6804
+
6805
+ let session = URLSession.shared
6806
+ let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
6807
+
6808
+ DispatchQueue.main.async {
6809
+ self.hideLoadingIndicator() // Stop loader when response is received
6810
+ }
6811
+
6812
+ if let error = error {
6813
+ self.presentPaymentErrorVC(errorMessage: error.localizedDescription)
6814
+ return
6815
+ }
6816
+
6817
+ guard let httpResponse = serviceResponse as? HTTPURLResponse else {
6818
+ self.presentPaymentErrorVC(errorMessage: "Invalid response")
6819
+ return
6820
+ }
6821
+
6822
+ if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
6823
+ if let data = serviceData {
6824
+ do {
6825
+ if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
6826
+ print("Response Data: \(responseObject)")
6827
+
6828
+ // Check if status is 0 and handle the error
6829
+ if let status = responseObject["status"] as? Int, status == 0 {
6830
+ let errorMessage = responseObject["message"] as? String ?? "Unknown error"
6831
+ self.presentPaymentErrorVC(errorMessage: errorMessage)
6832
+ } else {
6833
+ DispatchQueue.main.async {
6834
+ if let paymentDoneVC = self.storyboard?.instantiateViewController(withIdentifier: "PaymentDoneVC") as? PaymentDoneVC {
6835
+ paymentDoneVC.chargeData = responseObject
6836
+ paymentDoneVC.selectedPaymentMethod = self.selectedPaymentMethod
6837
+ paymentDoneVC.easyPayDelegate = self.easyPayDelegate
6838
+ // Pass billing info and additional info if available
6839
+ if let billingInfoData = self.request.billingInfoData,
6840
+ let fieldSection = try? JSONDecoder().decode(FieldSection.self, from: billingInfoData) {
6841
+
6842
+ // Filter billing info: only include non-empty values
6843
+ let filteredBilling = fieldSection.billing.filter { !($0.value.trimmingCharacters(in: .whitespaces).isEmpty) }
6844
+ paymentDoneVC.billingInfoData = filteredBilling
6845
+ var billingDict: [String: Any] = [:]
6846
+ filteredBilling.forEach { billingDict[$0.name] = $0.value }
6847
+ paymentDoneVC.billingInfo = billingDict
6848
+
6849
+ // Filter additional info: only include non-empty values
6850
+ let filteredAdditional = fieldSection.additional.filter { !($0.value.trimmingCharacters(in: .whitespaces).isEmpty) }
6851
+ paymentDoneVC.additionalInfoData = filteredAdditional
6852
+ var additionalDict: [String: Any] = [:]
6853
+ filteredAdditional.forEach { additionalDict[$0.name] = $0.value }
6854
+ paymentDoneVC.additionalInfo = additionalDict
6855
+ }
6856
+ self.navigationController?.pushViewController(paymentDoneVC, animated: true)
6857
+ }
6858
+ }
6859
+ }
6860
+ } else {
6861
+ self.presentPaymentErrorVC(errorMessage: "Invalid JSON format")
6862
+ }
6863
+ } catch let jsonError {
6864
+ self.presentPaymentErrorVC(errorMessage: "Error parsing JSON: \(jsonError)")
6865
+ }
6866
+ } else {
6867
+ self.presentPaymentErrorVC(errorMessage: "No data received")
6868
+ }
6869
+ } else {
6870
+ if let data = serviceData,
6871
+ let responseObj = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
6872
+ let message = responseObj["message"] as? String {
6873
+ self.presentPaymentErrorVC(errorMessage: message)
6874
+ } else {
6875
+ self.presentPaymentErrorVC(errorMessage: "HTTP Status Code: \(httpResponse.statusCode)")
6876
+ }
6877
+ }
6878
+ }
6879
+ task.resume()
6880
+ }
6881
+
6435
6882
  //MARK: - Update Cards Api.
6436
6883
  func updateCardApi(cardID: String) {
6437
6884
  showLoadingIndicator()
@@ -1,6 +1,6 @@
1
1
  Pod::Spec.new do |s|
2
2
  s.name = 'easymerchantsdk'
3
- s.version = '1.4.4'
3
+ s.version = '1.4.5'
4
4
  s.summary = 'A React Native SDK for Easy Merchant.'
5
5
  s.description = <<-DESC
6
6
  A React Native SDK to enable Easy Merchant functionality in mobile applications.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jimrising/easymerchantsdk-react-native",
3
- "version": "1.4.4",
3
+ "version": "1.4.5",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {