@jimrising/easymerchantsdk-react-native 1.3.2 → 1.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.idea/caches/deviceStreaming.xml +22 -11
- package/README.md +60 -76
- package/ios/Classes/EasyMerchantSdk.m +52 -37
- package/ios/Classes/EasyMerchantSdk.swift +61 -59
- package/ios/CustomComponents/DatePickerHandler.swift +71 -0
- package/ios/CustomComponents/PlanSelector.swift +58 -0
- package/ios/EnvironmentConfig.swift +2 -1
- package/ios/Models/Request.swift +45 -43
- package/ios/Pods/ViewControllers/AdditionalInfoVC.swift +150 -165
- package/ios/Pods/ViewControllers/BillingInfoVC/BillingInfoVC.swift +56 -183
- package/ios/Pods/ViewControllers/CountryListVC.swift +12 -0
- package/ios/Pods/ViewControllers/EmailVerificationVC.swift +27 -17
- package/ios/Pods/ViewControllers/OTPVerificationVC.swift +200 -79
- package/ios/Pods/ViewControllers/PaymentDoneVC.swift +4 -1
- package/ios/Pods/ViewControllers/PaymentInformation/PaymentInfoVC.swift +2062 -1041
- package/ios/Pods/ViewControllers/PaymentInformation/RecurringTVC.swift +40 -0
- package/ios/easymerchantsdk.podspec +1 -1
- package/ios/easymerchantsdk.storyboard +2190 -853
- package/package.json +1 -1
- package/.idea/workspace.xml +0 -198
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
//
|
|
2
|
+
// DatePickerHandler.swift
|
|
3
|
+
// EasyPay
|
|
4
|
+
//
|
|
5
|
+
// Created by Mony's Mac on 14/05/25.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import UIKit
|
|
9
|
+
|
|
10
|
+
class DatePickerHandler: NSObject {
|
|
11
|
+
|
|
12
|
+
private let datePicker = UIDatePicker()
|
|
13
|
+
private var targetTextField: UITextField?
|
|
14
|
+
|
|
15
|
+
var onDateSelected: ((Date) -> Void)?
|
|
16
|
+
|
|
17
|
+
init(textField: UITextField, initialDate: Date? = nil) {
|
|
18
|
+
super.init()
|
|
19
|
+
|
|
20
|
+
self.targetTextField = textField
|
|
21
|
+
configureDatePicker()
|
|
22
|
+
|
|
23
|
+
if let date = initialDate {
|
|
24
|
+
datePicker.date = date
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
setupInputView(for: textField)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
private func configureDatePicker() {
|
|
31
|
+
if #available(iOS 14.0, *) {
|
|
32
|
+
datePicker.preferredDatePickerStyle = .inline
|
|
33
|
+
}
|
|
34
|
+
datePicker.datePickerMode = .date
|
|
35
|
+
datePicker.minimumDate = Date()
|
|
36
|
+
datePicker.addTarget(self, action: #selector(dateChanged(_:)), for: .valueChanged)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private func setupInputView(for textField: UITextField) {
|
|
40
|
+
textField.inputView = datePicker
|
|
41
|
+
|
|
42
|
+
let toolbar = UIToolbar()
|
|
43
|
+
toolbar.sizeToFit()
|
|
44
|
+
|
|
45
|
+
let doneButton = UIBarButtonItem(title: "Done", style: .done, target: self, action: #selector(doneDateSelection))
|
|
46
|
+
let space = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
|
|
47
|
+
|
|
48
|
+
toolbar.setItems([space, doneButton], animated: false)
|
|
49
|
+
textField.inputAccessoryView = toolbar
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@objc private func dateChanged(_ sender: UIDatePicker) {
|
|
53
|
+
let formatter = DateFormatter()
|
|
54
|
+
formatter.dateFormat = "dd/MM/yyyy"
|
|
55
|
+
targetTextField?.text = formatter.string(from: sender.date)
|
|
56
|
+
onDateSelected?(sender.date)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@objc private func doneDateSelection() {
|
|
60
|
+
if targetTextField?.text?.isEmpty ?? true {
|
|
61
|
+
let formatter = DateFormatter()
|
|
62
|
+
formatter.dateFormat = "dd/MM/yyyy"
|
|
63
|
+
let dateText = formatter.string(from: datePicker.date)
|
|
64
|
+
targetTextField?.text = dateText
|
|
65
|
+
onDateSelected?(datePicker.date)
|
|
66
|
+
}
|
|
67
|
+
targetTextField?.resignFirstResponder()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
}
|
|
71
|
+
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
//
|
|
2
|
+
// PlanSelector.swift
|
|
3
|
+
// EasyPay
|
|
4
|
+
//
|
|
5
|
+
// Created by Mony's Mac on 14/05/25.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import UIKit
|
|
9
|
+
|
|
10
|
+
class PlanSelector {
|
|
11
|
+
|
|
12
|
+
static func presentPlanOptions(from viewController: UIViewController, sourceView: UIView, onSelection: @escaping (String) -> Void) {
|
|
13
|
+
|
|
14
|
+
let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
|
|
15
|
+
|
|
16
|
+
// Create actions
|
|
17
|
+
let weeklyAction = UIAlertAction(title: "Weekly", style: .default) { _ in
|
|
18
|
+
onSelection("Weekly")
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let monthlyAction = UIAlertAction(title: "Monthly", style: .default) { _ in
|
|
22
|
+
onSelection("Monthly")
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
|
|
26
|
+
|
|
27
|
+
alertController.addAction(weeklyAction)
|
|
28
|
+
alertController.addAction(monthlyAction)
|
|
29
|
+
alertController.addAction(cancelAction)
|
|
30
|
+
|
|
31
|
+
// Set attributed title with secondary font color
|
|
32
|
+
if let secondaryColorHex = UserStoreSingleton.shared.secondary_font_col,
|
|
33
|
+
let secondaryColor = UIColor(hex: secondaryColorHex) {
|
|
34
|
+
let titleAttrString = NSAttributedString(string: "Select Plan", attributes: [
|
|
35
|
+
.foregroundColor: secondaryColor,
|
|
36
|
+
.font: UIFont.systemFont(ofSize: 16, weight: .medium)
|
|
37
|
+
])
|
|
38
|
+
alertController.setValue(titleAttrString, forKey: "attributedTitle")
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Set title colors for actions using primary font color
|
|
42
|
+
if let buttonColorHex = UserStoreSingleton.shared.primary_btn_bg_col,
|
|
43
|
+
let buttonColor = UIColor(hex: buttonColorHex) {
|
|
44
|
+
weeklyAction.setValue(buttonColor, forKey: "titleTextColor")
|
|
45
|
+
monthlyAction.setValue(buttonColor, forKey: "titleTextColor")
|
|
46
|
+
cancelAction.setValue(UIColor.red, forKey: "titleTextColor")
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// iPad Support
|
|
50
|
+
if let popoverController = alertController.popoverPresentationController {
|
|
51
|
+
popoverController.sourceView = sourceView
|
|
52
|
+
popoverController.sourceRect = sourceView.bounds
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
viewController.present(alertController, animated: true)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
@@ -57,7 +57,8 @@ public class EnvironmentConfig {
|
|
|
57
57
|
case .hostedCheckouts: return "/api/v1/hostedcheckouts"
|
|
58
58
|
case .emailVerification: return "/api/v1/customer/send_otp"
|
|
59
59
|
case .verifyOtp: return "/api/v1/customer/verify_otp"
|
|
60
|
-
case .getCards: return "/api/v1/customer/card"
|
|
60
|
+
// case .getCards: return "/api/v1/customer/card"
|
|
61
|
+
case .getCards: return "/api/v1/card"
|
|
61
62
|
case .creditCharges: return "/api/v1/customer/charges"
|
|
62
63
|
case .account: return "/api/v1/ach/account"
|
|
63
64
|
case .achCharge: return "/api/v1/ach/charge"
|
package/ios/Models/Request.swift
CHANGED
|
@@ -87,7 +87,7 @@ public final class Request: NSObject {
|
|
|
87
87
|
public let billingInfoData: Data?
|
|
88
88
|
let themeConfiguration: ThemeConfiguration?
|
|
89
89
|
let selectedPaymentMethods: [PaymentMethod]
|
|
90
|
-
|
|
90
|
+
|
|
91
91
|
// New parameters
|
|
92
92
|
public let tokenOnly: Bool?
|
|
93
93
|
public let saveCard: Bool?
|
|
@@ -95,10 +95,11 @@ public final class Request: NSObject {
|
|
|
95
95
|
public let submitButtonText: String?
|
|
96
96
|
public let authenticatedACH: Bool?
|
|
97
97
|
public let grailPayParams: GrailPayRequest?
|
|
98
|
-
|
|
98
|
+
public let enableRecurring: Bool?
|
|
99
|
+
|
|
99
100
|
public init(
|
|
100
101
|
amount: Double,
|
|
101
|
-
billingInfoData: Data,
|
|
102
|
+
billingInfoData: Data? = nil,
|
|
102
103
|
paymentMethods: [PaymentMethod]? = nil,
|
|
103
104
|
themeConfiguration: ThemeConfiguration? = nil,
|
|
104
105
|
tokenOnly: Bool = false,
|
|
@@ -106,7 +107,8 @@ public final class Request: NSObject {
|
|
|
106
107
|
saveAccount: Bool = false,
|
|
107
108
|
submitButtonText: String? = nil,
|
|
108
109
|
authenticatedACH: Bool = false,
|
|
109
|
-
grailPayParams: GrailPayRequest? = nil
|
|
110
|
+
grailPayParams: GrailPayRequest? = nil,
|
|
111
|
+
enableRecurring: Bool = false
|
|
110
112
|
) {
|
|
111
113
|
self.amount = amount
|
|
112
114
|
self.billingInfoData = billingInfoData
|
|
@@ -117,7 +119,8 @@ public final class Request: NSObject {
|
|
|
117
119
|
self.submitButtonText = submitButtonText
|
|
118
120
|
self.authenticatedACH = authenticatedACH
|
|
119
121
|
self.grailPayParams = authenticatedACH ? grailPayParams : nil
|
|
120
|
-
|
|
122
|
+
self.enableRecurring = enableRecurring
|
|
123
|
+
|
|
121
124
|
if let paymentMethods = paymentMethods {
|
|
122
125
|
self.selectedPaymentMethods = paymentMethods
|
|
123
126
|
UserStoreSingleton.shared.paymentMethods = paymentMethods.map { $0.rawValue }
|
|
@@ -140,9 +143,9 @@ public final class Request: NSObject {
|
|
|
140
143
|
UserStoreSingleton.shared.border_radious = themeConfig.borderRadius
|
|
141
144
|
UserStoreSingleton.shared.fontSize = themeConfig.fontSize
|
|
142
145
|
}
|
|
143
|
-
|
|
146
|
+
|
|
144
147
|
super.init()
|
|
145
|
-
|
|
148
|
+
|
|
146
149
|
// If tokenOnly is true, fetch only the clientToken and do not open the SDK
|
|
147
150
|
if tokenOnly {
|
|
148
151
|
self.paymentIntentApi { success in
|
|
@@ -163,29 +166,29 @@ public final class Request: NSObject {
|
|
|
163
166
|
}
|
|
164
167
|
}
|
|
165
168
|
}
|
|
166
|
-
|
|
169
|
+
|
|
167
170
|
}
|
|
168
|
-
|
|
171
|
+
|
|
169
172
|
//MARK: - Payment Intent Api
|
|
170
173
|
func paymentIntentApi(completion: @escaping (Bool) -> Void) {
|
|
171
174
|
// Start loader when API call starts
|
|
172
|
-
|
|
175
|
+
|
|
173
176
|
guard let serviceURL = URL(string: EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.paymentIntent.path()) else {
|
|
174
177
|
print("Invalid URL")
|
|
175
178
|
completion(false)
|
|
176
179
|
return
|
|
177
180
|
}
|
|
178
|
-
|
|
181
|
+
|
|
179
182
|
var request = URLRequest(url: serviceURL)
|
|
180
183
|
request.httpMethod = "POST"
|
|
181
184
|
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
182
185
|
request.addValue(EnvironmentConfig.apiKey ?? "", forHTTPHeaderField: "X-Api-Key")
|
|
183
186
|
request.addValue(EnvironmentConfig.apiSecret ?? "", forHTTPHeaderField: "X-Api-Secret")
|
|
184
|
-
|
|
187
|
+
|
|
185
188
|
let params: [String: Any] = [
|
|
186
189
|
"amount": amount,
|
|
187
190
|
]
|
|
188
|
-
|
|
191
|
+
|
|
189
192
|
do {
|
|
190
193
|
request.httpBody = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
191
194
|
} catch {
|
|
@@ -193,34 +196,34 @@ public final class Request: NSObject {
|
|
|
193
196
|
completion(false)
|
|
194
197
|
return
|
|
195
198
|
}
|
|
196
|
-
|
|
199
|
+
|
|
197
200
|
let task = URLSession.shared.dataTask(with: request) { data, response, error in
|
|
198
201
|
DispatchQueue.main.async {
|
|
199
202
|
// Stop loader when response is received
|
|
200
203
|
}
|
|
201
|
-
|
|
204
|
+
|
|
202
205
|
guard let httpResponse = response as? HTTPURLResponse, error == nil else {
|
|
203
206
|
print("Error: \(error?.localizedDescription ?? "Unknown error")")
|
|
204
207
|
completion(false)
|
|
205
208
|
return
|
|
206
209
|
}
|
|
207
|
-
|
|
210
|
+
|
|
208
211
|
if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
|
|
209
212
|
if let data = data {
|
|
210
213
|
do {
|
|
211
214
|
if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
|
|
212
215
|
print("Response Data: \(responseObject)")
|
|
213
|
-
|
|
216
|
+
|
|
214
217
|
// Save tokens
|
|
215
218
|
if let clientToken = responseObject["client_token"] as? String {
|
|
216
219
|
UserStoreSingleton.shared.clientToken = clientToken
|
|
217
220
|
print("Client Token successfully saved: \(UserStoreSingleton.shared.clientToken ?? "None")")
|
|
218
221
|
}
|
|
219
|
-
|
|
222
|
+
|
|
220
223
|
if let paymentIntent = responseObject["payment_intent"] as? String {
|
|
221
224
|
UserStoreSingleton.shared.paymentIntent = paymentIntent
|
|
222
225
|
}
|
|
223
|
-
|
|
226
|
+
|
|
224
227
|
self.hostedCheckoutsApi { success in
|
|
225
228
|
completion(success)
|
|
226
229
|
}
|
|
@@ -243,29 +246,29 @@ public final class Request: NSObject {
|
|
|
243
246
|
}
|
|
244
247
|
task.resume()
|
|
245
248
|
}
|
|
246
|
-
|
|
249
|
+
|
|
247
250
|
// MARK: - Hosted Checkout API
|
|
248
251
|
func hostedCheckoutsApi(completion: @escaping (Bool) -> Void) {
|
|
249
|
-
|
|
252
|
+
|
|
250
253
|
// Build the URL using EnvironmentConfig
|
|
251
254
|
guard let baseURL = URL(string: EnvironmentConfig.baseURL) else {
|
|
252
255
|
print("Invalid base URL")
|
|
253
256
|
completion(false)
|
|
254
257
|
return
|
|
255
258
|
}
|
|
256
|
-
|
|
259
|
+
|
|
257
260
|
let endpointPath = EnvironmentConfig.Endpoints.hostedCheckouts.path()
|
|
258
261
|
let serviceURL = baseURL.appendingPathComponent(endpointPath)
|
|
259
|
-
|
|
262
|
+
|
|
260
263
|
var request = URLRequest(url: serviceURL)
|
|
261
264
|
request.httpMethod = "POST"
|
|
262
265
|
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
263
266
|
request.addValue(UserStoreSingleton.shared.clientToken ?? "", forHTTPHeaderField: "clientToken")
|
|
264
|
-
|
|
267
|
+
|
|
265
268
|
let params: [String: Any] = [
|
|
266
269
|
"amount": amount,
|
|
267
270
|
]
|
|
268
|
-
|
|
271
|
+
|
|
269
272
|
do {
|
|
270
273
|
request.httpBody = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
271
274
|
} catch {
|
|
@@ -273,33 +276,33 @@ public final class Request: NSObject {
|
|
|
273
276
|
completion(false)
|
|
274
277
|
return
|
|
275
278
|
}
|
|
276
|
-
|
|
279
|
+
|
|
277
280
|
let task = URLSession.shared.dataTask(with: request) { data, response, error in
|
|
278
|
-
|
|
281
|
+
|
|
279
282
|
guard let httpResponse = response as? HTTPURLResponse, error == nil else {
|
|
280
283
|
print("Error: \(error?.localizedDescription ?? "Unknown error")")
|
|
281
284
|
completion(false)
|
|
282
285
|
return
|
|
283
286
|
}
|
|
284
|
-
|
|
287
|
+
|
|
285
288
|
if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
|
|
286
289
|
if let data = data {
|
|
287
290
|
do {
|
|
288
291
|
if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
|
|
289
292
|
let dataObject = responseObject["data"] as? [String: Any] {
|
|
290
293
|
print(dataObject)
|
|
291
|
-
|
|
294
|
+
|
|
292
295
|
// Save merchant details
|
|
293
296
|
if let merchantEmail = dataObject["merchant_email"] as? String {
|
|
294
297
|
UserStoreSingleton.shared.merchantEmail = merchantEmail
|
|
295
298
|
print("Saved merchantEmail: \(merchantEmail)")
|
|
296
299
|
}
|
|
297
|
-
|
|
300
|
+
|
|
298
301
|
if let merchantName = dataObject["merchant_name"] as? String {
|
|
299
302
|
UserStoreSingleton.shared.merchantName = merchantName
|
|
300
303
|
print("Saved merchantName: \(merchantName)")
|
|
301
304
|
}
|
|
302
|
-
|
|
305
|
+
|
|
303
306
|
// Save payment methods (crypto payment account)
|
|
304
307
|
if let paymentMethods = dataObject["payment_methods"] as? [String: Any],
|
|
305
308
|
let cryptoSection = paymentMethods["crypto"] as? [String: Any],
|
|
@@ -307,31 +310,31 @@ public final class Request: NSObject {
|
|
|
307
310
|
UserStoreSingleton.shared.paymentAccount = paymentAccount
|
|
308
311
|
print("Saved paymentAccount (Crypto): \(paymentAccount)")
|
|
309
312
|
}
|
|
310
|
-
|
|
313
|
+
|
|
311
314
|
// Save payment methods
|
|
312
315
|
if let paymentMethods = dataObject["payment_methods"] as? [String: Any] {
|
|
313
316
|
var paymentMethodNames: [String] = []
|
|
314
|
-
|
|
317
|
+
|
|
315
318
|
if paymentMethods["card"] is [String: Any] {
|
|
316
319
|
paymentMethodNames.append("Card")
|
|
317
320
|
}
|
|
318
|
-
|
|
321
|
+
|
|
319
322
|
if paymentMethods["ach"] is [String: Any] {
|
|
320
323
|
paymentMethodNames.append("Bank")
|
|
321
324
|
}
|
|
322
|
-
|
|
325
|
+
|
|
323
326
|
if paymentMethods["crypto"] is [String: Any] {
|
|
324
327
|
paymentMethodNames.append("Crypto")
|
|
325
328
|
}
|
|
326
|
-
|
|
329
|
+
|
|
327
330
|
if paymentMethods["wallet"] is [String: Any] {
|
|
328
331
|
paymentMethodNames.append("Wallet")
|
|
329
332
|
}
|
|
330
|
-
|
|
333
|
+
|
|
331
334
|
UserStoreSingleton.shared.paymentMethods = paymentMethodNames
|
|
332
335
|
print("Saved payment methods: \(paymentMethodNames)")
|
|
333
336
|
}
|
|
334
|
-
|
|
337
|
+
|
|
335
338
|
if self.themeConfiguration == nil, let appearanceSettings = dataObject["apperance_settings"] as? [String: Any] {
|
|
336
339
|
UserStoreSingleton.shared.body_bg_col = appearanceSettings["body_bg_col"] as? String
|
|
337
340
|
UserStoreSingleton.shared.border_radious = appearanceSettings["border_radious"] as? String
|
|
@@ -344,13 +347,13 @@ public final class Request: NSObject {
|
|
|
344
347
|
UserStoreSingleton.shared.secondary_btn_font_col = appearanceSettings["secondary_font_col"] as? String
|
|
345
348
|
UserStoreSingleton.shared.secondary_btn_hover_col = appearanceSettings["secondary_btn_hover_col"] as? String
|
|
346
349
|
UserStoreSingleton.shared.secondary_font_col = appearanceSettings["secondary_font_col"] as? String
|
|
347
|
-
|
|
350
|
+
|
|
348
351
|
// Clearing the font size value
|
|
349
352
|
UserStoreSingleton.shared.fontSize = nil
|
|
350
353
|
}
|
|
351
|
-
|
|
354
|
+
|
|
352
355
|
completion(true)
|
|
353
|
-
|
|
356
|
+
|
|
354
357
|
} else {
|
|
355
358
|
print("Invalid JSON format or 'data' key is missing")
|
|
356
359
|
completion(false)
|
|
@@ -370,6 +373,5 @@ public final class Request: NSObject {
|
|
|
370
373
|
}
|
|
371
374
|
task.resume()
|
|
372
375
|
}
|
|
373
|
-
|
|
376
|
+
|
|
374
377
|
}
|
|
375
|
-
|