@jimrising/easymerchantsdk-react-native 1.5.1 → 1.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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.5.1"
10
+ "@jimrising/easymerchantsdk-react-native": "^1.5.2"
11
11
  },
12
12
  ```
13
13
 
@@ -0,0 +1,31 @@
1
+ //
2
+ // APIHeaders.swift
3
+ // EasyPay
4
+ //
5
+ // Created by Mony's Mac on 19/06/25.
6
+ //
7
+
8
+ import Foundation
9
+
10
+ struct APIHeaders {
11
+ static func commonHeaders(contentType: String = "application/json") -> [String: String] {
12
+ return ["Content-Type": contentType]
13
+ }
14
+
15
+ static func clientTokenHeader() -> [String: String] {
16
+ guard let token = UserStoreSingleton.shared.clientToken else { return [:] }
17
+ return ["Client-Token": token]
18
+ }
19
+
20
+ static func customerTokenHeader() -> [String: String] {
21
+ guard let token = UserStoreSingleton.shared.customerToken else { return [:] }
22
+ return ["Customer-Token": token]
23
+ }
24
+
25
+ static func apiAuthHeaders() -> [String: String] {
26
+ guard let key = EnvironmentConfig.apiKey,
27
+ let secret = EnvironmentConfig.apiSecret else { return [:] }
28
+ return ["x-api-key": key, "x-api-secret": secret]
29
+ }
30
+ }
31
+
@@ -0,0 +1,63 @@
1
+ //
2
+ // APIRequest.swift
3
+ // EasyPay
4
+ //
5
+ // Created by Mony's Mac on 19/06/25.
6
+ //
7
+
8
+ import Foundation
9
+
10
+ enum HTTPMethod: String {
11
+ case get = "GET"
12
+ case post = "POST"
13
+ }
14
+
15
+ struct APIRequest {
16
+ let endpoint: EnvironmentConfig.Endpoints
17
+ let method: HTTPMethod
18
+ var headers: [String: String] = [:]
19
+ var body: [String: Any]?
20
+
21
+ func buildURLRequest() -> URLRequest? {
22
+ let fullURL = EnvironmentConfig.baseURL + endpoint.path()
23
+ guard let url = URL(string: fullURL) else {
24
+ print("❌ Invalid URL: \(fullURL)")
25
+ return nil
26
+ }
27
+
28
+ var request = URLRequest(url: url)
29
+ request.httpMethod = method.rawValue
30
+
31
+ // Combine default headers with passed-in headers
32
+ var allHeaders = APIHeaders.commonHeaders()
33
+ allHeaders.merge(APIHeaders.clientTokenHeader(), uniquingKeysWith: { $1 })
34
+ allHeaders.merge(APIHeaders.customerTokenHeader(), uniquingKeysWith: { $1 })
35
+ allHeaders.merge(APIHeaders.apiAuthHeaders(), uniquingKeysWith: { $1 })
36
+ allHeaders.merge(headers, uniquingKeysWith: { $1 }) // override if needed
37
+
38
+ // Apply to request
39
+ allHeaders.forEach { key, value in
40
+ request.setValue(value, forHTTPHeaderField: key)
41
+ }
42
+
43
+ // Encode JSON body if present
44
+ if let body = body {
45
+ do {
46
+ let jsonData = try JSONSerialization.data(withJSONObject: body, options: [])
47
+ request.httpBody = jsonData
48
+
49
+ // Debug
50
+ if let jsonString = String(data: jsonData, encoding: .utf8) {
51
+ print("JSON Payload Sent:\n\(jsonString)")
52
+ }
53
+
54
+ } catch {
55
+ print("Failed to serialize request body: \(error)")
56
+ return nil
57
+ }
58
+ }
59
+
60
+ return request
61
+ }
62
+
63
+ }
@@ -0,0 +1,105 @@
1
+ //
2
+ // APIService.swift
3
+ // EasyPay
4
+ //
5
+ // Created by Mony's Mac on 19/06/25.
6
+ //
7
+
8
+ import Foundation
9
+
10
+ /// Singleton class to manage API requests
11
+ class APIService {
12
+ static let shared = APIService()
13
+ private init() {}
14
+
15
+ // MARK: - Raw JSON Request (Dictionary Response)
16
+ /// Performs a request and returns a raw [String: Any] JSON response
17
+ func requestRaw(
18
+ _ apiRequest: APIRequest,
19
+ completion: @escaping (Result<[String: Any], Error>, Data?, HTTPURLResponse?) -> Void
20
+ ) {
21
+ guard let urlRequest = apiRequest.buildURLRequest() else {
22
+ completion(.failure(NSError(domain: "Invalid URLRequest", code: 0)), nil, nil)
23
+ return
24
+ }
25
+
26
+ let task = URLSession.shared.dataTask(with: urlRequest) { data, response, error in
27
+ let httpResponse = response as? HTTPURLResponse
28
+
29
+ // Debug print
30
+ if let data = data, let rawString = String(data: data, encoding: .utf8) {
31
+ print("🧾 Raw Response:\n\(rawString)")
32
+ }
33
+
34
+ if let error = error {
35
+ completion(.failure(error), data, httpResponse)
36
+ return
37
+ }
38
+
39
+ guard let data = data else {
40
+ completion(.failure(NSError(domain: "No data", code: 0)), nil, httpResponse)
41
+ return
42
+ }
43
+
44
+ do {
45
+ if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
46
+ if let statusCode = httpResponse?.statusCode, (200..<300).contains(statusCode) {
47
+ completion(.success(json), data, httpResponse)
48
+ } else {
49
+ let apiError = NSError(domain: "", code: httpResponse?.statusCode ?? 0, userInfo: json)
50
+ completion(.failure(apiError), data, httpResponse)
51
+ }
52
+ } else {
53
+ completion(.failure(NSError(domain: "Invalid JSON structure", code: 0)), data, httpResponse)
54
+ }
55
+ } catch {
56
+ completion(.failure(error), data, httpResponse)
57
+ }
58
+ }
59
+
60
+ task.resume()
61
+ }
62
+
63
+ // MARK: - Decodable Model Request
64
+ /// Performs a request and decodes a Codable model from the response
65
+ func request<T: Decodable>(
66
+ _ apiRequest: APIRequest,
67
+ responseType: T.Type,
68
+ completion: @escaping (Result<T, Error>) -> Void
69
+ ) {
70
+ guard let urlRequest = apiRequest.buildURLRequest() else {
71
+ completion(.failure(NSError(domain: "Invalid URLRequest", code: 0)))
72
+ return
73
+ }
74
+
75
+ let task = URLSession.shared.dataTask(with: urlRequest) { data, response, error in
76
+ // Handle error
77
+ if let error = error {
78
+ completion(.failure(error))
79
+ return
80
+ }
81
+
82
+ // Handle missing data
83
+ guard let data = data else {
84
+ completion(.failure(NSError(domain: "No data", code: 0)))
85
+ return
86
+ }
87
+
88
+ // Debug: optional - print raw response
89
+ if let rawString = String(data: data, encoding: .utf8) {
90
+ print("🧾 Raw Response:\n\(rawString)")
91
+ }
92
+
93
+ // Attempt decoding
94
+ do {
95
+ let decodedObject = try JSONDecoder().decode(T.self, from: data)
96
+ completion(.success(decodedObject))
97
+ } catch {
98
+ completion(.failure(error))
99
+ }
100
+ }
101
+
102
+ task.resume()
103
+ }
104
+
105
+ }
@@ -330,7 +330,7 @@ public class EasyMerchantSdkPlugin: NSObject, RCTBridgeModule {
330
330
 
331
331
  // MARK: - Delegate Implementation
332
332
  extension EasyMerchantSdkPlugin: EasyPayViewControllerDelegate {
333
- public func easyPayController(_ controller: EasyPayViewController, didFinishWith result: Result) {
333
+ public func easyPayController(_ controller: EasyPayViewController, didFinishWith result: SDKResult) {
334
334
  DispatchQueue.main.async {
335
335
  switch result.type {
336
336
  case .cancelled:
@@ -10,7 +10,7 @@ import UIKit
10
10
  // @available(iOS 16.0, *)
11
11
  @objc
12
12
  public protocol EasyPayViewControllerDelegate {
13
- func easyPayController(_ controller: EasyPayViewController, didFinishWith result: Result)
13
+ func easyPayController(_ controller: EasyPayViewController, didFinishWith result: SDKResult)
14
14
  }
15
15
 
16
16
  // @available(iOS 16.0, *)
@@ -9,7 +9,7 @@ public class GrailPayHelper {
9
9
  public static func presentGrailPay(
10
10
  from viewController: UIViewController,
11
11
  request: GrailPayRequest,
12
- completion: @escaping (Result) -> Void
12
+ completion: @escaping (SDKResult) -> Void
13
13
  ) {
14
14
  let grailPayVC = GrailPayVC(request: request)
15
15
  grailPayVC.onCompletion = completion
@@ -340,7 +340,7 @@ public final class Request: NSObject {
340
340
 
341
341
  let params: [String: Any] = [
342
342
  "amount": amount ?? 0,
343
- // "allowed_cycles": String(recurringIntervals?.count ?? 0),
343
+ // "allowed_cycles": String(recurringIntervals?.count ?? 0),
344
344
  "allowed_cycles": numOfCycle ?? 0,
345
345
  "intervals": recurringIntervals?.map { $0.rawValue } ?? [],
346
346
  "is_recurring": self.is_recurring ?? false,
@@ -1,8 +1,8 @@
1
1
  //
2
- // Result.swift
2
+ // SDKResult.swift
3
3
  // EasyPay
4
4
  //
5
- // Created by iftekhar on 14/07/24.
5
+ // Created by Mony's Mac on 18/06/25.
6
6
  //
7
7
 
8
8
  import UIKit
@@ -15,7 +15,7 @@ public enum ResultType: Int {
15
15
  }
16
16
 
17
17
  @objc
18
- public final class Result: NSObject {
18
+ public final class SDKResult: NSObject {
19
19
 
20
20
  @objc public let type: ResultType
21
21
  @objc public let error: NSError?
@@ -48,3 +48,4 @@ public final class Result: NSObject {
48
48
  }
49
49
  }
50
50
 
51
+
@@ -1389,7 +1389,7 @@ class AdditionalInfoVC: BaseVC {
1389
1389
 
1390
1390
  var params: [String: Any] = [
1391
1391
  "name": accountName ?? "",
1392
- "email": UserStoreSingleton.shared.merchantEmail ?? "",
1392
+ "email": userEmail ?? "",
1393
1393
  "currency": "usd",
1394
1394
  "account_type": accountType?.lowercased() ?? "",
1395
1395
  "routing_number": routingNumber ?? "",
@@ -1777,7 +1777,7 @@ class BillingInfoVC: BaseVC {
1777
1777
  uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
1778
1778
 
1779
1779
  var params: [String: Any] = [
1780
- "name": UserStoreSingleton.shared.merchantName ?? "",
1780
+ "name": nameOnCard ?? "",
1781
1781
  "account_id": accountID ?? "",
1782
1782
  "payment_method": "ach",
1783
1783
  "customer": customerID ?? "",
@@ -2110,7 +2110,7 @@ class BillingInfoVC: BaseVC {
2110
2110
 
2111
2111
  var params: [String: Any] = [
2112
2112
  "name": accountName ?? "",
2113
- "email": UserStoreSingleton.shared.merchantEmail ?? "",
2113
+ "email": userEmail ?? "",
2114
2114
  "currency": "usd",
2115
2115
  "account_type": accountType?.lowercased() ?? "",
2116
2116
  "routing_number": routingNumber ?? "",
@@ -2294,7 +2294,7 @@ class BillingInfoVC: BaseVC {
2294
2294
  "account_type": self.selectedGrailPayAccountType ?? "",
2295
2295
  "name": self.selectedGrailPayAccountName ?? "",
2296
2296
  "description": "payment checkout",
2297
- "email": UserStoreSingleton.shared.merchantEmail ?? ""
2297
+ "email": userEmail ?? ""
2298
2298
  ]
2299
2299
 
2300
2300
  // Conditionally add billing info
@@ -14,7 +14,7 @@ public class GrailPayVC: UIViewController {
14
14
  private var loadingIndicator: UIActivityIndicatorView!
15
15
  private var request: GrailPayRequest
16
16
 
17
- public var onCompletion: ((Result) -> Void)?
17
+ public var onCompletion: ((SDKResult) -> Void)?
18
18
  private var didHandleBankConnection = false
19
19
 
20
20
  public init(request: GrailPayRequest) {
@@ -158,7 +158,7 @@ public class GrailPayVC: UIViewController {
158
158
  private func handleError(_ error: Error) {
159
159
  DispatchQueue.main.async {
160
160
  self.loadingIndicator.stopAnimating()
161
- self.onCompletion?(Result(error: error as NSError))
161
+ self.onCompletion?(SDKResult(error: error as NSError))
162
162
  self.dismiss(animated: true)
163
163
  }
164
164
  }
@@ -204,7 +204,7 @@ extension GrailPayVC: WKScriptMessageHandler {
204
204
  didHandleBankConnection = true
205
205
  let dataArray: [[String: Any]] = [dataG]
206
206
  DispatchQueue.main.async {
207
- self.onCompletion?(Result(type: .success, chargeData: ["data": dataArray]))
207
+ self.onCompletion?(SDKResult(type: .success, chargeData: ["data": dataArray]))
208
208
  self.dismiss(animated: true)
209
209
  }
210
210
  }
@@ -216,7 +216,7 @@ extension GrailPayVC: WKScriptMessageHandler {
216
216
  }
217
217
  print("User exited linking flow")
218
218
  DispatchQueue.main.async {
219
- self.onCompletion?(Result(type: .cancelled))
219
+ self.onCompletion?(SDKResult(type: .cancelled))
220
220
  self.dismiss(animated: true)
221
221
  }
222
222
 
@@ -52,7 +52,7 @@ class OTPVerificationVC: BaseVC {
52
52
 
53
53
  var isSavedForFuture: Bool = false
54
54
 
55
- private var timeRemaining = 180 // 3 minutes
55
+ private var timeRemaining = 60 // 3 minutes
56
56
  private var timer: Timer?
57
57
 
58
58
  //GrailPay Params
@@ -233,7 +233,7 @@ class OTPVerificationVC: BaseVC {
233
233
  }
234
234
 
235
235
  private func startTimer() {
236
- timeRemaining = 180 // 3 minutes
236
+ timeRemaining = 60 // 3 minutes
237
237
  lblOtpTimer.text = formatTime(timeRemaining)
238
238
  timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateTimer), userInfo: nil, repeats: true)
239
239
  }
@@ -490,17 +490,6 @@ class OTPVerificationVC: BaseVC {
490
490
  self.grailPayAccountChargeApi(customerId: nil)
491
491
  }
492
492
  }
493
- // else if self.selectedPaymentMethod == "NewGrailPayAccount" {
494
- // if let dataSection = responseObject["data"] as? [String: Any],
495
- // let innerData = dataSection["data"] as? [String: Any],
496
- // let customerId = innerData["customer_id"] as? String {
497
- // print("Extracted customer_id: \(customerId)")
498
- // self.grailPayAccountChargeApi(customerId: customerId)
499
- // } else {
500
- // print("customer_id not found. Sending empty customerId.")
501
- // self.grailPayAccountChargeApi(customerId: nil)
502
- // }
503
- // }
504
493
  } else {
505
494
  print("Invalid JSON format")
506
495
  }
@@ -515,9 +504,8 @@ class OTPVerificationVC: BaseVC {
515
504
  if let data = serviceData,
516
505
  let responseObj = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
517
506
  let message = responseObj["message"] as? String {
518
- self.presentPaymentErrorVC(errorMessage: message)
519
- } else {
520
- self.presentPaymentErrorVC(errorMessage: "HTTP Status Code: \(httpResponse.statusCode)")
507
+ // self.presentPaymentErrorVC(errorMessage: message)
508
+ self.showToast(message: message)
521
509
  }
522
510
  }
523
511
  }
@@ -565,7 +553,7 @@ class OTPVerificationVC: BaseVC {
565
553
  "save_card": 1,
566
554
  "is_default": "1",
567
555
  // "create_customer": "1"
568
- // "email": userEmail ?? ""
556
+ "email": userEmail ?? ""
569
557
  ]
570
558
 
571
559
  if let customerId = customerId {
@@ -941,7 +929,7 @@ class OTPVerificationVC: BaseVC {
941
929
 
942
930
  var params: [String: Any] = [
943
931
  "name": accountName ?? "",
944
- "email": email ?? "",
932
+ "email": userEmail ?? "",
945
933
  "currency": "usd",
946
934
  "account_type": accountType?.lowercased() ?? "",
947
935
  "routing_number": routingNumber ?? "",
@@ -203,7 +203,7 @@ class PaymentDoneVC: UIViewController {
203
203
  }
204
204
 
205
205
  @IBAction func actionBtnDone(_ sender: UIButton) {
206
- let result = Result(type: .success,
206
+ let result = SDKResult(type: .success,
207
207
  chargeData: chargeData,
208
208
  billingInfo: billingInfo,
209
209
  additionalInfo: additionalInfo)
@@ -66,7 +66,7 @@ class PaymentErrorVC: UIViewController {
66
66
  }
67
67
 
68
68
  @IBAction func actionBtnDone(_ sender: UIButton) {
69
- let result = Result(type: .error, chargeData: nil)
69
+ let result = SDKResult(type: .error, chargeData: nil)
70
70
  // Pass the error message back using the delegate
71
71
  easyPayDelegate?.easyPayController(self.navigationController as! EasyPayViewController, didFinishWith: result)
72
72
 
@@ -75,7 +75,7 @@ class PaymentErrorVC: UIViewController {
75
75
  // Ensure the error message is sent back to the starting screen
76
76
  if let delegate = easyPayVC.easyPayDelegate {
77
77
  UserStoreSingleton.shared.isLoggedIn = false
78
- let errorResult = Result(type: .error, chargeData: ["errorMessage": self.errorMessage ?? "Unknown error"])
78
+ let errorResult = SDKResult(type: .error, chargeData: ["errorMessage": self.errorMessage ?? "Unknown error"])
79
79
  delegate.easyPayController(easyPayVC, didFinishWith: errorResult)
80
80
  }
81
81
  })