@jimrising/easymerchantsdk-react-native 1.4.3 → 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 +15 -4
- package/ios/Classes/EasyMerchantSdk.m +4 -2
- package/ios/Classes/EasyMerchantSdk.swift +2 -0
- package/ios/Models/Request.swift +45 -54
- package/ios/Models/Result.swift +1 -0
- package/ios/Pods/ViewControllers/AdditionalInfoVC.swift +262 -37
- package/ios/Pods/ViewControllers/BillingInfoVC/BillingInfoVC.swift +219 -18
- package/ios/Pods/ViewControllers/EmailVerificationVC.swift +41 -86
- package/ios/Pods/ViewControllers/OTPVerificationVC.swift +99 -96
- package/ios/Pods/ViewControllers/PaymentInformation/PaymentInfoVC.swift +782 -79
- package/ios/Pods/ViewControllers/ThreeDSecurePaymentDoneVC.swift +24 -10
- package/ios/easymerchantsdk.podspec +1 -1
- package/ios/easymerchantsdk.storyboard +92 -210
- package/package.json +1 -1
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.
|
|
10
|
+
"@jimrising/easymerchantsdk-react-native": "^1.4.5"
|
|
11
11
|
},
|
|
12
12
|
```
|
|
13
13
|
|
|
@@ -165,7 +165,7 @@ const App = () => {
|
|
|
165
165
|
|
|
166
166
|
const handleBilling = async () => {
|
|
167
167
|
const billingInfo = {
|
|
168
|
-
visibility: { billing:
|
|
168
|
+
visibility: { billing: false, additional: false },
|
|
169
169
|
billing: {
|
|
170
170
|
address: 'Mohali, Punjab',
|
|
171
171
|
country: 'India',
|
|
@@ -211,6 +211,16 @@ const App = () => {
|
|
|
211
211
|
fontFamily: '"Inter", sans-serif',
|
|
212
212
|
};
|
|
213
213
|
|
|
214
|
+
const authConfig = {
|
|
215
|
+
accessToken: '251|uTijpDGfrS88UR2V1cZNMQ8S4hUJA0sVzsnsoUZF',
|
|
216
|
+
vendorId: '251',
|
|
217
|
+
role: 'business',
|
|
218
|
+
timeout: 10,
|
|
219
|
+
isSandbox: true,
|
|
220
|
+
brandingName: 'Lyfecycle Payments',
|
|
221
|
+
finderSubtitle: 'Search for your bank',
|
|
222
|
+
searchPlaceholder: 'Enter bank name',
|
|
223
|
+
};
|
|
214
224
|
try {
|
|
215
225
|
if (Platform.OS === 'android') {
|
|
216
226
|
const result = await RNEasymerchantsdk.billing('72', null);
|
|
@@ -225,10 +235,11 @@ const App = () => {
|
|
|
225
235
|
false, // tokenOnly
|
|
226
236
|
true, // saveCard
|
|
227
237
|
true, // saveAccount
|
|
228
|
-
|
|
229
|
-
|
|
238
|
+
true, // authenticatedACH
|
|
239
|
+
authConfig, // grailPayParams
|
|
230
240
|
'Submit',
|
|
231
241
|
false, // isRecurring
|
|
242
|
+
2, // if isRecurring == true then numOfCycle required
|
|
232
243
|
['weekly', 'monthly'],
|
|
233
244
|
'custom',
|
|
234
245
|
'07/07/2025',
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
#import <React/RCTLog.h>
|
|
3
3
|
#import <React/RCTBridgeModule.h>
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
//#import <easymerchantsdk-Swift.h>
|
|
6
|
+
#import <easymerchantsdk/easymerchantsdk-Swift.h>
|
|
7
7
|
|
|
8
8
|
@interface EasyMerchantSdk ()
|
|
9
9
|
@property (nonatomic, strong) EasyMerchantSdkPlugin *sdkPluginInstance;
|
|
@@ -40,6 +40,7 @@ RCT_EXPORT_METHOD(
|
|
|
40
40
|
grailPayParams:(NSDictionary *)grailPayParams
|
|
41
41
|
submitButtonText:(NSString *)submitButtonText
|
|
42
42
|
isRecurring:(BOOL)isRecurring
|
|
43
|
+
numOfCycle:(int)numOfCycle
|
|
43
44
|
recurringIntervals:(NSArray *)recurringIntervals
|
|
44
45
|
recurringStartDateType:(NSString *)recurringStartDateType
|
|
45
46
|
recurringStartDate:(NSString *)recurringStartDate
|
|
@@ -67,6 +68,7 @@ RCT_EXPORT_METHOD(
|
|
|
67
68
|
grailPayParams:grailPayParams
|
|
68
69
|
submitButtonText:submitButtonText
|
|
69
70
|
isRecurring:isRecurring
|
|
71
|
+
numOfCycle:numOfCycle
|
|
70
72
|
recurringIntervals:recurringIntervals
|
|
71
73
|
recurringStartDateType:recurringStartDateType
|
|
72
74
|
recurringStartDate:recurringStartDate
|
|
@@ -69,6 +69,7 @@ public class EasyMerchantSdkPlugin: NSObject, RCTBridgeModule {
|
|
|
69
69
|
grailPayParams: [String: Any]?,
|
|
70
70
|
submitButtonText: String?,
|
|
71
71
|
isRecurring: Bool,
|
|
72
|
+
numOfCycle: Int,
|
|
72
73
|
recurringIntervals: [String]?,
|
|
73
74
|
recurringStartDateType: String?,
|
|
74
75
|
recurringStartDate: String?,
|
|
@@ -215,6 +216,7 @@ public class EasyMerchantSdkPlugin: NSObject, RCTBridgeModule {
|
|
|
215
216
|
authenticatedACH: authenticatedACH,
|
|
216
217
|
grailPayParams: grailParams,
|
|
217
218
|
is_recurring: isRecurring,
|
|
219
|
+
numOfCycle: numOfCycle,
|
|
218
220
|
recurringIntervals: intervals,
|
|
219
221
|
recurringStartDateType: startType,
|
|
220
222
|
recurringStartDate: recurringStartDate,
|
package/ios/Models/Request.swift
CHANGED
|
@@ -106,14 +106,14 @@ public struct FieldItem: Codable {
|
|
|
106
106
|
public let name: String
|
|
107
107
|
public let required: Bool
|
|
108
108
|
public var value: String
|
|
109
|
-
|
|
109
|
+
|
|
110
110
|
// Overloaded initializer for BillingFieldName
|
|
111
111
|
public init(name: BillingFieldName, required: Bool, value: String) {
|
|
112
112
|
self.name = name.rawValue
|
|
113
113
|
self.required = required
|
|
114
114
|
self.value = value
|
|
115
115
|
}
|
|
116
|
-
|
|
116
|
+
|
|
117
117
|
// Overloaded initializer for AdditionalFieldName
|
|
118
118
|
public init(name: AdditionalFieldName, required: Bool, value: String) {
|
|
119
119
|
self.name = name.rawValue
|
|
@@ -125,7 +125,7 @@ public struct FieldItem: Codable {
|
|
|
125
125
|
public struct FieldsVisibility: Codable {
|
|
126
126
|
public let billing: Bool
|
|
127
127
|
public let additional: Bool
|
|
128
|
-
|
|
128
|
+
|
|
129
129
|
public init(billing: Bool, additional: Bool) {
|
|
130
130
|
self.billing = billing
|
|
131
131
|
self.additional = additional
|
|
@@ -136,7 +136,7 @@ public struct FieldSection: Codable {
|
|
|
136
136
|
public let visibility: FieldsVisibility
|
|
137
137
|
public var billing: [FieldItem]
|
|
138
138
|
public var additional: [FieldItem]
|
|
139
|
-
|
|
139
|
+
|
|
140
140
|
public init(visibility: FieldsVisibility, billing: [FieldItem], additional: [FieldItem]) {
|
|
141
141
|
self.visibility = visibility
|
|
142
142
|
self.billing = billing
|
|
@@ -158,6 +158,7 @@ public final class Request: NSObject {
|
|
|
158
158
|
public let authenticatedACH: Bool?
|
|
159
159
|
public let grailPayParams: GrailPayRequest?
|
|
160
160
|
public var is_recurring: Bool?
|
|
161
|
+
public var numOfCycle: Int?
|
|
161
162
|
public var recurringIntervals: [RecurringIntervals]?
|
|
162
163
|
public var recurringStartDateType: RecurringStartDateType?
|
|
163
164
|
public var recurringStartDate: String?
|
|
@@ -167,7 +168,7 @@ public final class Request: NSObject {
|
|
|
167
168
|
public let showSubmitButton: Bool?
|
|
168
169
|
public let referenceID: Bool?
|
|
169
170
|
public let referenceToken: String?
|
|
170
|
-
|
|
171
|
+
|
|
171
172
|
public init(
|
|
172
173
|
amount: Double? = nil,
|
|
173
174
|
currency: String? = nil,
|
|
@@ -181,6 +182,7 @@ public final class Request: NSObject {
|
|
|
181
182
|
authenticatedACH: Bool = false,
|
|
182
183
|
grailPayParams: GrailPayRequest? = nil,
|
|
183
184
|
is_recurring: Bool = false,
|
|
185
|
+
numOfCycle: Int? = nil,
|
|
184
186
|
recurringIntervals: [RecurringIntervals]? = nil,
|
|
185
187
|
recurringStartDateType: RecurringStartDateType? = nil,
|
|
186
188
|
recurringStartDate: String? = nil,
|
|
@@ -191,7 +193,6 @@ public final class Request: NSObject {
|
|
|
191
193
|
referenceID: Bool = false,
|
|
192
194
|
referenceToken: String? = nil
|
|
193
195
|
) {
|
|
194
|
-
|
|
195
196
|
// Validate if amount is provided, must be ≥ 0.50
|
|
196
197
|
if let amt = amount, amt < 0.50 {
|
|
197
198
|
DispatchQueue.main.async {
|
|
@@ -223,6 +224,7 @@ public final class Request: NSObject {
|
|
|
223
224
|
self.authenticatedACH = authenticatedACH
|
|
224
225
|
self.grailPayParams = authenticatedACH ? grailPayParams : nil
|
|
225
226
|
self.is_recurring = is_recurring
|
|
227
|
+
self.numOfCycle = numOfCycle
|
|
226
228
|
self.recurringIntervals = recurringIntervals
|
|
227
229
|
self.recurringStartDateType = recurringStartDateType
|
|
228
230
|
self.recurringStartDate = recurringStartDate
|
|
@@ -236,11 +238,13 @@ public final class Request: NSObject {
|
|
|
236
238
|
// Conditionally assign recurring fields only if is_recurring == true
|
|
237
239
|
if is_recurring {
|
|
238
240
|
self.is_recurring = true
|
|
241
|
+
self.numOfCycle = numOfCycle
|
|
239
242
|
self.recurringIntervals = recurringIntervals
|
|
240
243
|
self.recurringStartDateType = recurringStartDateType
|
|
241
244
|
self.recurringStartDate = recurringStartDate
|
|
242
245
|
} else {
|
|
243
246
|
self.is_recurring = false
|
|
247
|
+
self.numOfCycle = nil
|
|
244
248
|
self.recurringIntervals = nil
|
|
245
249
|
self.recurringStartDateType = nil
|
|
246
250
|
self.recurringStartDate = nil
|
|
@@ -304,7 +308,7 @@ public final class Request: NSObject {
|
|
|
304
308
|
//MARK: - Payment Intent Api
|
|
305
309
|
func paymentIntentApi(completion: @escaping (Bool) -> Void) {
|
|
306
310
|
guard let serviceURL = URL(string: EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.paymentIntent.path()) else {
|
|
307
|
-
|
|
311
|
+
showErrorAndDismiss(message: "Invalid payment URL.")
|
|
308
312
|
completion(false)
|
|
309
313
|
return
|
|
310
314
|
}
|
|
@@ -315,31 +319,14 @@ public final class Request: NSObject {
|
|
|
315
319
|
request.addValue(EnvironmentConfig.apiKey ?? "", forHTTPHeaderField: "X-Api-Key")
|
|
316
320
|
request.addValue(EnvironmentConfig.apiSecret ?? "", forHTTPHeaderField: "X-Api-Secret")
|
|
317
321
|
|
|
318
|
-
//
|
|
322
|
+
// Recurring date validation
|
|
319
323
|
if let startDateString = recurringStartDate,
|
|
320
324
|
let startDate = DateFormatter.recurringDateFormatter.date(from: startDateString) {
|
|
321
|
-
|
|
322
325
|
let today = Calendar.current.startOfDay(for: Date())
|
|
323
326
|
let startDay = Calendar.current.startOfDay(for: startDate)
|
|
324
327
|
|
|
325
328
|
if startDay < today {
|
|
326
|
-
|
|
327
|
-
if let topVC = UIApplication.topViewController() {
|
|
328
|
-
let alert = UIAlertController(title: "Invalid Date",
|
|
329
|
-
message: "The recurring start date cannot be in the past. Please select today or a future date.",
|
|
330
|
-
preferredStyle: .alert)
|
|
331
|
-
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { _ in
|
|
332
|
-
if let easyPayVC = UIApplication.findEasyPayViewController(from: topVC) {
|
|
333
|
-
easyPayVC.dismiss(animated: true)
|
|
334
|
-
} else {
|
|
335
|
-
// fallback: dismiss topVC itself if it's presented modally
|
|
336
|
-
topVC.dismiss(animated: true)
|
|
337
|
-
}
|
|
338
|
-
}))
|
|
339
|
-
topVC.present(alert, animated: true)
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
|
|
329
|
+
showErrorAndDismiss(message: "The recurring start date cannot be in the past. Please select today or a future date.")
|
|
343
330
|
completion(false)
|
|
344
331
|
return
|
|
345
332
|
}
|
|
@@ -347,7 +334,8 @@ public final class Request: NSObject {
|
|
|
347
334
|
|
|
348
335
|
let params: [String: Any] = [
|
|
349
336
|
"amount": amount ?? 0,
|
|
350
|
-
"allowed_cycles": String(recurringIntervals?.count ?? 0),
|
|
337
|
+
// "allowed_cycles": String(recurringIntervals?.count ?? 0),
|
|
338
|
+
"allowed_cycles": numOfCycle ?? 0,
|
|
351
339
|
"intervals": recurringIntervals?.map { $0.rawValue } ?? [],
|
|
352
340
|
"is_recurring": self.is_recurring ?? false,
|
|
353
341
|
"recurring_start_date": recurringStartDate ?? "",
|
|
@@ -355,21 +343,16 @@ public final class Request: NSObject {
|
|
|
355
343
|
]
|
|
356
344
|
|
|
357
345
|
do {
|
|
358
|
-
|
|
359
346
|
request.httpBody = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
360
347
|
} catch {
|
|
361
|
-
|
|
348
|
+
showErrorAndDismiss(message: "Failed to encode payment data.")
|
|
362
349
|
completion(false)
|
|
363
350
|
return
|
|
364
351
|
}
|
|
365
352
|
|
|
366
353
|
let task = URLSession.shared.dataTask(with: request) { data, response, error in
|
|
367
|
-
DispatchQueue.main.async {
|
|
368
|
-
// Stop loader when response is received
|
|
369
|
-
}
|
|
370
|
-
|
|
371
354
|
guard let httpResponse = response as? HTTPURLResponse, error == nil else {
|
|
372
|
-
|
|
355
|
+
self.showErrorAndDismiss(message: error?.localizedDescription ?? "An unknown error occurred.")
|
|
373
356
|
completion(false)
|
|
374
357
|
return
|
|
375
358
|
}
|
|
@@ -378,51 +361,60 @@ public final class Request: NSObject {
|
|
|
378
361
|
if let data = data {
|
|
379
362
|
do {
|
|
380
363
|
if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
|
|
381
|
-
print("Response Data: \(responseObject)")
|
|
382
|
-
|
|
383
|
-
// Save tokens
|
|
384
364
|
if let clientToken = responseObject["client_token"] as? String {
|
|
385
365
|
UserStoreSingleton.shared.clientToken = clientToken
|
|
386
|
-
print("Client Token successfully saved: \(UserStoreSingleton.shared.clientToken ?? "None")")
|
|
387
366
|
}
|
|
388
|
-
|
|
389
367
|
if let paymentIntent = responseObject["payment_intent"] as? String {
|
|
390
368
|
UserStoreSingleton.shared.paymentIntent = paymentIntent
|
|
391
369
|
}
|
|
392
|
-
|
|
393
370
|
self.hostedCheckoutsApi { success in
|
|
394
371
|
completion(success)
|
|
395
372
|
}
|
|
373
|
+
return
|
|
396
374
|
} else {
|
|
397
|
-
|
|
398
|
-
completion(false)
|
|
375
|
+
self.showErrorAndDismiss(message: "Invalid response format.")
|
|
399
376
|
}
|
|
400
377
|
} catch {
|
|
401
|
-
|
|
402
|
-
completion(false)
|
|
378
|
+
self.showErrorAndDismiss(message: "Failed to parse response.")
|
|
403
379
|
}
|
|
404
380
|
} else {
|
|
405
|
-
|
|
406
|
-
completion(false)
|
|
381
|
+
self.showErrorAndDismiss(message: "No response data received.")
|
|
407
382
|
}
|
|
408
383
|
} else {
|
|
384
|
+
var message = "Payment request failed with status code: \(httpResponse.statusCode)"
|
|
409
385
|
if let data = data,
|
|
410
386
|
let responseObj = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
|
|
411
|
-
let
|
|
412
|
-
|
|
413
|
-
} else {
|
|
414
|
-
print("HTTP Status Code: \(httpResponse.statusCode)")
|
|
387
|
+
let msg = responseObj["message"] as? String {
|
|
388
|
+
message = msg
|
|
415
389
|
}
|
|
416
|
-
|
|
417
|
-
completion(false)
|
|
390
|
+
self.showErrorAndDismiss(message: message)
|
|
418
391
|
}
|
|
392
|
+
completion(false)
|
|
419
393
|
}
|
|
394
|
+
|
|
420
395
|
task.resume()
|
|
421
396
|
}
|
|
422
397
|
|
|
398
|
+
private func showErrorAndDismiss(message: String) {
|
|
399
|
+
DispatchQueue.main.async {
|
|
400
|
+
if let topVC = UIApplication.topViewController() {
|
|
401
|
+
let alert = UIAlertController(title: "Payment Error",
|
|
402
|
+
message: message,
|
|
403
|
+
preferredStyle: .alert)
|
|
404
|
+
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { _ in
|
|
405
|
+
if let easyPayVC = UIApplication.findEasyPayViewController(from: topVC) {
|
|
406
|
+
easyPayVC.dismiss(animated: true)
|
|
407
|
+
} else {
|
|
408
|
+
topVC.dismiss(animated: true)
|
|
409
|
+
}
|
|
410
|
+
}))
|
|
411
|
+
topVC.present(alert, animated: true)
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
423
416
|
// MARK: - Hosted Checkout API
|
|
424
417
|
func hostedCheckoutsApi(completion: @escaping (Bool) -> Void) {
|
|
425
|
-
|
|
426
418
|
// Build the URL using EnvironmentConfig
|
|
427
419
|
guard let baseURL = URL(string: EnvironmentConfig.baseURL) else {
|
|
428
420
|
print("Invalid base URL")
|
|
@@ -645,4 +637,3 @@ extension UIApplication {
|
|
|
645
637
|
}
|
|
646
638
|
}
|
|
647
639
|
|
|
648
|
-
|
package/ios/Models/Result.swift
CHANGED
|
@@ -319,44 +319,51 @@ class AdditionalInfoVC: BaseVC {
|
|
|
319
319
|
|
|
320
320
|
// MARK: - Flow Based on Conditions
|
|
321
321
|
if isSavedForFuture {
|
|
322
|
-
if
|
|
323
|
-
//
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
emailVerificationVC
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
emailVerificationVC.
|
|
339
|
-
emailVerificationVC.
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
322
|
+
if selectedPaymentMethod == "NewGrailPayAccount" {
|
|
323
|
+
// Only call API, do NOT navigate
|
|
324
|
+
grailPayAccountChargeApi(customerId: UserStoreSingleton.shared.customerId)
|
|
325
|
+
} else {
|
|
326
|
+
// Navigate to EmailVerificationVC for all other payment methods
|
|
327
|
+
if let emailVerificationVC = self.storyboard?.instantiateViewController(withIdentifier: "EmailVerificationVC") as? EmailVerificationVC {
|
|
328
|
+
|
|
329
|
+
emailVerificationVC.billingInfoData = billingInfoData
|
|
330
|
+
emailVerificationVC.selectedPaymentMethod = selectedPaymentMethod
|
|
331
|
+
emailVerificationVC.easyPayDelegate = easyPayDelegate
|
|
332
|
+
emailVerificationVC.request = request
|
|
333
|
+
emailVerificationVC.chosenPlan = chosenPlan
|
|
334
|
+
emailVerificationVC.startDate = startDate
|
|
335
|
+
emailVerificationVC.userEmail = userEmail
|
|
336
|
+
emailVerificationVC.billingInfo = fieldSection?.billing
|
|
337
|
+
emailVerificationVC.additionalInfo = fieldSection?.additional
|
|
338
|
+
emailVerificationVC.visibility = fieldSection?.visibility
|
|
339
|
+
emailVerificationVC.amount = amount
|
|
340
|
+
|
|
341
|
+
// Payment method-specific data
|
|
342
|
+
switch selectedPaymentMethod {
|
|
343
|
+
case "Card":
|
|
344
|
+
emailVerificationVC.cardNumber = cardNumber
|
|
345
|
+
emailVerificationVC.expiryDate = expiryDate
|
|
346
|
+
emailVerificationVC.cvv = cvv
|
|
347
|
+
emailVerificationVC.nameOnCard = nameOnCard
|
|
348
|
+
|
|
349
|
+
case "Bank":
|
|
350
|
+
emailVerificationVC.accountName = accountName
|
|
351
|
+
emailVerificationVC.routingNumber = routingNumber
|
|
352
|
+
emailVerificationVC.accountType = accountType
|
|
353
|
+
emailVerificationVC.accountNumber = accountNumber
|
|
354
|
+
|
|
355
|
+
case "GrailPay":
|
|
356
|
+
emailVerificationVC.grailPayAccountID = grailPayAccountID
|
|
357
|
+
emailVerificationVC.selectedGrailPayAccountType = selectedGrailPayAccountType
|
|
358
|
+
emailVerificationVC.selectedGrailPayAccountName = selectedGrailPayAccountName
|
|
359
|
+
emailVerificationVC.isSavedForFuture = true
|
|
360
|
+
|
|
361
|
+
default:
|
|
362
|
+
break
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
navigationController?.pushViewController(emailVerificationVC, animated: true)
|
|
357
366
|
}
|
|
358
|
-
|
|
359
|
-
navigationController?.pushViewController(emailVerificationVC, animated: true)
|
|
360
367
|
}
|
|
361
368
|
}
|
|
362
369
|
else {
|
|
@@ -2134,6 +2141,224 @@ class AdditionalInfoVC: BaseVC {
|
|
|
2134
2141
|
task.resume()
|
|
2135
2142
|
}
|
|
2136
2143
|
|
|
2144
|
+
//MARK: - GrailPay Account Charge Api if user saved account
|
|
2145
|
+
func grailPayAccountChargeApi(customerId: String?) {
|
|
2146
|
+
showLoadingIndicator()
|
|
2147
|
+
|
|
2148
|
+
let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.achCharge.path()
|
|
2149
|
+
|
|
2150
|
+
guard let serviceURL = URL(string: fullURL) else {
|
|
2151
|
+
print("Invalid URL")
|
|
2152
|
+
hideLoadingIndicator()
|
|
2153
|
+
return
|
|
2154
|
+
}
|
|
2155
|
+
|
|
2156
|
+
var uRLRequest = URLRequest(url: serviceURL)
|
|
2157
|
+
uRLRequest.httpMethod = "POST"
|
|
2158
|
+
uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
2159
|
+
|
|
2160
|
+
let token = UserStoreSingleton.shared.clientToken
|
|
2161
|
+
print("Setting clientToken header: \(token ?? "None")")
|
|
2162
|
+
uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
|
|
2163
|
+
|
|
2164
|
+
if let apiKey = EnvironmentConfig.apiKey {
|
|
2165
|
+
uRLRequest.addValue(apiKey, forHTTPHeaderField: "X-Api-Key")
|
|
2166
|
+
}
|
|
2167
|
+
if let apiSecret = EnvironmentConfig.apiSecret {
|
|
2168
|
+
uRLRequest.addValue(apiSecret, forHTTPHeaderField: "X-Api-Secret")
|
|
2169
|
+
}
|
|
2170
|
+
|
|
2171
|
+
let emailPrefix = userEmail?.components(separatedBy: "@").first ?? ""
|
|
2172
|
+
|
|
2173
|
+
var params: [String: Any] = [
|
|
2174
|
+
"account_id": self.grailPayAccountID ?? "",
|
|
2175
|
+
"account_type": self.selectedGrailPayAccountType ?? "",
|
|
2176
|
+
"name": self.selectedGrailPayAccountName ?? "",
|
|
2177
|
+
"save_account": 1,
|
|
2178
|
+
"is_default": 1,
|
|
2179
|
+
"customer_id": customerId ?? "",
|
|
2180
|
+
"email": userEmail ?? "",
|
|
2181
|
+
"create_customer": "1",
|
|
2182
|
+
]
|
|
2183
|
+
|
|
2184
|
+
if let customerId = customerId {
|
|
2185
|
+
params["customer"] = customerId
|
|
2186
|
+
} else {
|
|
2187
|
+
params["username"] = emailPrefix
|
|
2188
|
+
}
|
|
2189
|
+
|
|
2190
|
+
// // Billing Info
|
|
2191
|
+
// if let visibility = visibility, visibility.billing == true,
|
|
2192
|
+
// let billing = billingInfo, !billing.isEmpty {
|
|
2193
|
+
// var billingDict: [String: Any] = [:]
|
|
2194
|
+
// billing.forEach { billingDict[$0.name] = $0.value }
|
|
2195
|
+
//
|
|
2196
|
+
// params["address"] = billingDict["address"] as? String ?? ""
|
|
2197
|
+
// params["country"] = billingDict["country"] as? String ?? ""
|
|
2198
|
+
// params["state"] = billingDict["state"] as? String ?? ""
|
|
2199
|
+
// params["city"] = billingDict["city"] as? String ?? ""
|
|
2200
|
+
// params["zip"] = billingDict["postal_code"] as? String ?? ""
|
|
2201
|
+
// }
|
|
2202
|
+
|
|
2203
|
+
// Always include Billing Info if available
|
|
2204
|
+
if let billing = billingInfo, !billing.isEmpty {
|
|
2205
|
+
var billingDict: [String: Any] = [:]
|
|
2206
|
+
billing.forEach { billingDict[$0.name] = $0.value }
|
|
2207
|
+
|
|
2208
|
+
params["address"] = billingDict["address"] as? String ?? ""
|
|
2209
|
+
params["country"] = billingDict["country"] as? String ?? ""
|
|
2210
|
+
params["state"] = billingDict["state"] as? String ?? ""
|
|
2211
|
+
params["city"] = billingDict["city"] as? String ?? ""
|
|
2212
|
+
params["zip"] = billingDict["postal_code"] as? String ?? ""
|
|
2213
|
+
}
|
|
2214
|
+
|
|
2215
|
+
// // Additional Info or default description
|
|
2216
|
+
// var descriptionValue: String = "Hosted payment checkout" // default
|
|
2217
|
+
// if let visibility = visibility, visibility.additional == true,
|
|
2218
|
+
// let additional = additionalInfo, !additional.isEmpty {
|
|
2219
|
+
//
|
|
2220
|
+
// var additionalDict: [String: Any] = [:]
|
|
2221
|
+
// additional.forEach { additionalDict[$0.name] = $0.value }
|
|
2222
|
+
//
|
|
2223
|
+
// if let desc = additionalDict["description"] as? String, !desc.isEmpty {
|
|
2224
|
+
// descriptionValue = desc
|
|
2225
|
+
// }
|
|
2226
|
+
//
|
|
2227
|
+
// if let phone = additionalDict["phone_number"] as? String, !phone.isEmpty {
|
|
2228
|
+
// params["phone_number"] = phone
|
|
2229
|
+
// }
|
|
2230
|
+
// }
|
|
2231
|
+
// params["description"] = descriptionValue
|
|
2232
|
+
|
|
2233
|
+
// Always include Additional Info if available
|
|
2234
|
+
var descriptionValue: String = "Hosted payment checkout"
|
|
2235
|
+
if let additional = additionalInfo, !additional.isEmpty {
|
|
2236
|
+
var additionalDict: [String: Any] = [:]
|
|
2237
|
+
additional.forEach { additionalDict[$0.name] = $0.value }
|
|
2238
|
+
|
|
2239
|
+
if let desc = additionalDict["description"] as? String, !desc.isEmpty {
|
|
2240
|
+
descriptionValue = desc
|
|
2241
|
+
}
|
|
2242
|
+
|
|
2243
|
+
if let phone = additionalDict["phone_number"] as? String, !phone.isEmpty {
|
|
2244
|
+
params["phone_number"] = phone
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
params["description"] = descriptionValue
|
|
2248
|
+
|
|
2249
|
+
// Add these if recurring is enabled
|
|
2250
|
+
if let req = request, req.is_recurring == true {
|
|
2251
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
2252
|
+
// Only send start_date if type is .custom and field is not empty
|
|
2253
|
+
if let startDateText = startDate, !startDateText.isEmpty {
|
|
2254
|
+
let inputFormatter = DateFormatter()
|
|
2255
|
+
inputFormatter.dateFormat = "dd/MM/yyyy"
|
|
2256
|
+
|
|
2257
|
+
let outputFormatter = DateFormatter()
|
|
2258
|
+
outputFormatter.dateFormat = "MM/dd/yyyy"
|
|
2259
|
+
|
|
2260
|
+
if let date = inputFormatter.date(from: startDateText) {
|
|
2261
|
+
let apiFormattedDate = outputFormatter.string(from: date)
|
|
2262
|
+
params["start_date"] = apiFormattedDate
|
|
2263
|
+
} else {
|
|
2264
|
+
print("Invalid date format in startDateText")
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2267
|
+
}
|
|
2268
|
+
|
|
2269
|
+
params["interval"] = chosenPlan?.lowercased()
|
|
2270
|
+
}
|
|
2271
|
+
|
|
2272
|
+
print(params)
|
|
2273
|
+
|
|
2274
|
+
do {
|
|
2275
|
+
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
2276
|
+
uRLRequest.httpBody = jsonData
|
|
2277
|
+
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
2278
|
+
print("JSON Payload: \(jsonString)")
|
|
2279
|
+
}
|
|
2280
|
+
} catch let error {
|
|
2281
|
+
print("Error creating JSON data: \(error)")
|
|
2282
|
+
hideLoadingIndicator()
|
|
2283
|
+
return
|
|
2284
|
+
}
|
|
2285
|
+
|
|
2286
|
+
let session = URLSession.shared
|
|
2287
|
+
let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
|
|
2288
|
+
|
|
2289
|
+
DispatchQueue.main.async {
|
|
2290
|
+
self.hideLoadingIndicator() // Stop loader when response is received
|
|
2291
|
+
}
|
|
2292
|
+
|
|
2293
|
+
if let error = error {
|
|
2294
|
+
self.presentPaymentErrorVC(errorMessage: error.localizedDescription)
|
|
2295
|
+
return
|
|
2296
|
+
}
|
|
2297
|
+
|
|
2298
|
+
guard let httpResponse = serviceResponse as? HTTPURLResponse else {
|
|
2299
|
+
self.presentPaymentErrorVC(errorMessage: "Invalid response")
|
|
2300
|
+
return
|
|
2301
|
+
}
|
|
2302
|
+
|
|
2303
|
+
if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
|
|
2304
|
+
if let data = serviceData {
|
|
2305
|
+
do {
|
|
2306
|
+
if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
|
|
2307
|
+
print("Response Data: \(responseObject)")
|
|
2308
|
+
|
|
2309
|
+
// Check if status is 0 and handle the error
|
|
2310
|
+
if let status = responseObject["status"] as? Int, status == 0 {
|
|
2311
|
+
let errorMessage = responseObject["message"] as? String ?? "Unknown error"
|
|
2312
|
+
self.presentPaymentErrorVC(errorMessage: errorMessage)
|
|
2313
|
+
} else {
|
|
2314
|
+
DispatchQueue.main.async {
|
|
2315
|
+
if let paymentDoneVC = self.storyboard?.instantiateViewController(withIdentifier: "PaymentDoneVC") as? PaymentDoneVC {
|
|
2316
|
+
paymentDoneVC.chargeData = responseObject
|
|
2317
|
+
paymentDoneVC.selectedPaymentMethod = self.selectedPaymentMethod
|
|
2318
|
+
paymentDoneVC.easyPayDelegate = self.easyPayDelegate
|
|
2319
|
+
// Pass billing and additional info
|
|
2320
|
+
// Conditionally pass raw FieldItem array
|
|
2321
|
+
paymentDoneVC.visibility = self.visibility
|
|
2322
|
+
|
|
2323
|
+
// if self.visibility?.billing == true {
|
|
2324
|
+
paymentDoneVC.billingInfoData = self.billingInfo
|
|
2325
|
+
var billingDict: [String: Any] = [:]
|
|
2326
|
+
self.billingInfo?.forEach { billingDict[$0.name] = $0.value }
|
|
2327
|
+
paymentDoneVC.billingInfo = billingDict
|
|
2328
|
+
// }
|
|
2329
|
+
|
|
2330
|
+
// if self.visibility?.additional == true {
|
|
2331
|
+
paymentDoneVC.additionalInfoData = self.additionalInfo
|
|
2332
|
+
var additionalDict: [String: Any] = [:]
|
|
2333
|
+
self.additionalInfo?.forEach { additionalDict[$0.name] = $0.value }
|
|
2334
|
+
paymentDoneVC.additionalInfo = additionalDict
|
|
2335
|
+
// }
|
|
2336
|
+
self.navigationController?.pushViewController(paymentDoneVC, animated: true)
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
} else {
|
|
2341
|
+
self.presentPaymentErrorVC(errorMessage: "Invalid JSON format")
|
|
2342
|
+
}
|
|
2343
|
+
} catch let jsonError {
|
|
2344
|
+
self.presentPaymentErrorVC(errorMessage: "Error parsing JSON: \(jsonError)")
|
|
2345
|
+
}
|
|
2346
|
+
} else {
|
|
2347
|
+
self.presentPaymentErrorVC(errorMessage: "No data received")
|
|
2348
|
+
}
|
|
2349
|
+
} else {
|
|
2350
|
+
if let data = serviceData,
|
|
2351
|
+
let responseObj = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
|
|
2352
|
+
let message = responseObj["message"] as? String {
|
|
2353
|
+
self.presentPaymentErrorVC(errorMessage: message)
|
|
2354
|
+
} else {
|
|
2355
|
+
self.presentPaymentErrorVC(errorMessage: "HTTP Status Code: \(httpResponse.statusCode)")
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2359
|
+
task.resume()
|
|
2360
|
+
}
|
|
2361
|
+
|
|
2137
2362
|
}
|
|
2138
2363
|
|
|
2139
2364
|
extension AdditionalInfoVC: UITextFieldDelegate {
|