@jimrising/easymerchantsdk-react-native 1.3.3 → 1.3.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.
@@ -47,17 +47,6 @@
47
47
  <option name="screenX" value="1080" />
48
48
  <option name="screenY" value="2160" />
49
49
  </PersistentDeviceSelectionData>
50
- <PersistentDeviceSelectionData>
51
- <option name="api" value="34" />
52
- <option name="brand" value="Lenovo" />
53
- <option name="codename" value="TB370FU" />
54
- <option name="id" value="TB370FU" />
55
- <option name="manufacturer" value="Lenovo" />
56
- <option name="name" value="Tab P12" />
57
- <option name="screenDensity" value="340" />
58
- <option name="screenX" value="1840" />
59
- <option name="screenY" value="2944" />
60
- </PersistentDeviceSelectionData>
61
50
  <PersistentDeviceSelectionData>
62
51
  <option name="api" value="34" />
63
52
  <option name="brand" value="samsung" />
@@ -69,6 +58,17 @@
69
58
  <option name="screenX" value="1080" />
70
59
  <option name="screenY" value="2340" />
71
60
  </PersistentDeviceSelectionData>
61
+ <PersistentDeviceSelectionData>
62
+ <option name="api" value="34" />
63
+ <option name="brand" value="samsung" />
64
+ <option name="codename" value="a15x" />
65
+ <option name="id" value="a15x" />
66
+ <option name="manufacturer" value="Samsung" />
67
+ <option name="name" value="A15 5G" />
68
+ <option name="screenDensity" value="450" />
69
+ <option name="screenX" value="1080" />
70
+ <option name="screenY" value="2340" />
71
+ </PersistentDeviceSelectionData>
72
72
  <PersistentDeviceSelectionData>
73
73
  <option name="api" value="34" />
74
74
  <option name="brand" value="samsung" />
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.3.3"
10
+ "@jimrising/easymerchantsdk-react-native": "^1.3.5"
11
11
  },
12
12
  ```
13
13
 
@@ -120,11 +120,9 @@ import {
120
120
  StyleSheet,
121
121
  Platform,
122
122
  NativeModules,
123
- NativeEventEmitter
124
123
  } from 'react-native';
125
124
 
126
125
  const { RNEasymerchantsdk, EasyMerchantSdk } = NativeModules;
127
- const sdkEvents = Platform.OS === 'ios' ? new NativeEventEmitter(EasyMerchantSdk) : null;
128
126
 
129
127
  const App = () => {
130
128
  const [version, setVersion] = useState('');
@@ -136,38 +134,18 @@ const App = () => {
136
134
  if (Platform.OS === 'ios') {
137
135
  await EasyMerchantSdk.setViewController();
138
136
  EasyMerchantSdk.configureEnvironment(
139
- 'staging', // sandbox , staging , production
140
- 'apiKey',
141
- 'secretKey'
137
+ 'staging', // Options: sandbox, staging, production
138
+ '14fbf8a186091763e59c289c1',
139
+ 'f90863e7a853539f8ace8d6de'
142
140
  );
143
141
  }
144
-
145
- // Add listeners for iOS
146
- if (sdkEvents) {
147
- sdkEvents.addListener('EasyPaySuccess', (data) => {
148
- setResponse(`✅ Success: ${JSON.stringify(data)}`);
149
- });
150
- sdkEvents.addListener('EasyPayCancelled', () => {
151
- setResponse('⚠️ Payment Cancelled');
152
- });
153
- sdkEvents.addListener('EasyPayError', (err) => {
154
- setResponse(`❌ Error: ${JSON.stringify(err)}`);
155
- });
156
- }
157
142
  } catch (err) {
158
- console.error('Initialization Error:', err.message || err);
143
+ console.error('Initialization Error:', err);
144
+ setResponse(`❌ Initialization Error: ${err.message || err}`);
159
145
  }
160
146
  };
161
147
 
162
148
  setup();
163
-
164
- return () => {
165
- if (sdkEvents) {
166
- sdkEvents.removeAllListeners('EasyPaySuccess');
167
- sdkEvents.removeAllListeners('EasyPayCancelled');
168
- sdkEvents.removeAllListeners('EasyPayError');
169
- }
170
- };
171
149
  }, []);
172
150
 
173
151
  const getPlatformVersion = async () => {
@@ -179,11 +157,12 @@ const App = () => {
179
157
  setVersion(ver);
180
158
  } catch (err) {
181
159
  setVersion('Error fetching version');
160
+ console.error(err);
182
161
  }
183
162
  };
184
163
 
185
164
  const handleBilling = async () => {
186
- const amount = '72';
165
+ const amount = '64';
187
166
  const billingInfo = {
188
167
  address: 'Test',
189
168
  country: 'test',
@@ -193,36 +172,44 @@ const App = () => {
193
172
  additional_info: {
194
173
  name: 'Test User',
195
174
  email: 'test@gmail.com',
196
- phone_number: '7888821587',
175
+ phone_number: '234983789',
197
176
  country_code: '91',
198
- description: 'SDK Test'
199
- }
177
+ description: 'SDK Test',
178
+ },
200
179
  };
201
180
 
202
- const themeConfiguration = {
203
- bodyBackgroundColor: '#FFEEEE',
204
- containerBackgroundColor: '#FFCCCC',
205
- primaryFontColor: '#B30000',
206
- secondaryFontColor: '#660000',
207
- primaryButtonBackgroundColor: '#D50000',
208
- primaryButtonHoverColor: '#9C0000',
209
- primaryButtonFontColor: '#FFFFFF',
210
- secondaryButtonBackgroundColor: '#FF6666',
211
- secondaryButtonHoverColor: '#FF4D4D',
212
- secondaryButtonFontColor: '#FFFFFF',
213
- borderRadius: '12',
214
- fontSize: '16'
215
- };
216
-
217
-
181
+ const themeConfiguration = {
182
+ bodyBackgroundColor: '#FFEEEE',
183
+ containerBackgroundColor: '#FFCCCC',
184
+ primaryFontColor: '#B30000',
185
+ secondaryFontColor: '#660000',
186
+ primaryButtonBackgroundColor: '#D50000',
187
+ primaryButtonHoverColor: '#9C0000',
188
+ primaryButtonFontColor: '#FFFFFF',
189
+ secondaryButtonBackgroundColor: '#FF6666',
190
+ secondaryButtonHoverColor: '#FF4D4D',
191
+ secondaryButtonFontColor: '#FFFFFF',
192
+ borderRadius: '12',
193
+ fontSize: '16',
194
+ };
218
195
 
196
+ const authConfig = {
197
+ accessToken: '251|uTijpDGfrS88UR2V1cZNMQ8S4hUJA0sVzsnsoUZF',
198
+ vendorId: '251',
199
+ role: 'business',
200
+ timeout: 10,
201
+ isSandbox: true,
202
+ brandingName: 'Lyfecycle Payments',
203
+ finderSubtitle: 'Search for your bank',
204
+ searchPlaceholder: 'Enter bank name',
205
+ };
219
206
 
220
- try {
221
- if (Platform.OS === 'android') {
222
- const result = await RNEasymerchantsdk.billing(amount, null);
223
- setResponse(`✅ Android Payment Success: ${result}`);
224
- } else {
225
- await EasyMerchantSdk.billing(
207
+ try {
208
+ if (Platform.OS === 'android') {
209
+ const result = await RNEasymerchantsdk.billing(amount, null);
210
+ setResponse(`✅ Android Payment Success: ${result}`);
211
+ } else {
212
+ const result = await EasyMerchantSdk.billing(
226
213
  amount,
227
214
  JSON.stringify(billingInfo),
228
215
  ['card', 'bank', 'crypto'],
@@ -231,34 +218,30 @@ const App = () => {
231
218
  false, // saveCard
232
219
  false, // saveAccount
233
220
  true, // authenticatedACH
234
- {
235
- accessToken: "251|uTijpDGfrS88UR2V1cZNMQ8S4hUJA0sVzsnsoUZF",
236
- vendorId: "251",
237
- role: "business",
238
- timeout: 10,
239
- isSandbox: true,
240
- brandingName: "Lyfecycle Payments",
241
- finderSubtitle: "Search for your bank",
242
- searchPlaceholder: "Enter bank name"
243
- }
221
+ authConfig
244
222
  );
245
223
 
246
- setResponse('🔁 Billing started on iOS. Awaiting response...');
247
- }
248
- } catch (error) {
249
- setResponse(`❌ Billing Error: ${error.message || error}`);
250
- }
224
+ console.log("Billing success:", result);
225
+ setResponse(`✅ iOS Payment Success: ${JSON.stringify(result)}`);
226
+ }
227
+ } catch (error) {
228
+ console.error('Billing Error:', error);
229
+ setResponse(`❌ Billing Error: ${error.message || JSON.stringify(error)}`);
230
+ }
231
+
251
232
  };
252
233
 
253
234
  return (
254
235
  <View style={styles.container}>
255
236
  <Text style={styles.title}>EasyMerchant SDK</Text>
256
237
  <Text style={styles.version}>Platform Version: {version}</Text>
238
+
257
239
  <Button title="Get Platform Version" onPress={getPlatformVersion} />
258
240
  <View style={styles.space} />
259
241
  <Button title="Start Billing" onPress={handleBilling} />
260
242
  <View style={styles.space} />
261
- {response ? <Text style={styles.response}>{response}</Text> : null}
243
+
244
+ <Text style={styles.response}>{response}</Text>
262
245
  </View>
263
246
  );
264
247
  };
@@ -271,30 +254,31 @@ const styles = StyleSheet.create({
271
254
  backgroundColor: '#F9FAFB',
272
255
  alignItems: 'center',
273
256
  justifyContent: 'center',
274
- padding: 24
257
+ padding: 24,
275
258
  },
276
259
  title: {
277
260
  fontSize: 20,
278
261
  fontWeight: 'bold',
279
- marginBottom: 16
262
+ marginBottom: 16,
280
263
  },
281
264
  version: {
282
265
  fontSize: 14,
283
- marginBottom: 8
266
+ marginBottom: 8,
284
267
  },
285
268
  response: {
286
269
  marginTop: 20,
287
270
  paddingHorizontal: 12,
288
271
  textAlign: 'center',
289
272
  fontSize: 14,
290
- color: '#333'
273
+ color: '#333',
291
274
  },
292
275
  space: {
293
- height: 16
294
- }
276
+ height: 16,
277
+ },
295
278
  });
296
279
 
297
280
 
281
+
298
282
  ```
299
283
 
300
284
  You can send `null` if billing info not available.
@@ -2,66 +2,83 @@
2
2
  #import <React/RCTLog.h>
3
3
  #import <React/RCTBridgeModule.h>
4
4
  #import <easymerchantsdk/easymerchantsdk-Swift.h>
5
+
5
6
  //#import <easymerchantsdk-Swift.h>
6
7
 
8
+ @interface EasyMerchantSdk()
9
+ @property (nonatomic, strong) EasyMerchantSdkPlugin *sdkPluginInstance;
10
+ @end
11
+
7
12
  @implementation EasyMerchantSdk
8
13
 
9
14
  RCT_EXPORT_MODULE()
10
15
 
11
-
12
16
  RCT_EXPORT_METHOD(configureEnvironment:(NSString *)env
13
- apiKey:(NSString *)apiKey
14
- apiSecret:(NSString *)apiSecret)
17
+ apiKey:(NSString *)apiKey
18
+ apiSecret:(NSString *)apiSecret)
15
19
  {
16
- EasyMerchantSdkPlugin *sdkPlugin = [[EasyMerchantSdkPlugin alloc] init];
17
- [sdkPlugin configureEnvironment:env apiKey:apiKey apiSecret:apiSecret];
20
+ if (!self.sdkPluginInstance) {
21
+ self.sdkPluginInstance = [[EasyMerchantSdkPlugin alloc] init];
22
+ }
23
+ [self.sdkPluginInstance configureEnvironment:env apiKey:apiKey apiSecret:apiSecret];
18
24
  }
19
25
 
20
26
  RCT_EXPORT_METHOD(
21
- billing:(NSString *)amount
22
- billingInfo:(NSString *)billingInfo
23
- paymentMethods:(NSArray *)paymentMethods
24
- themeConfiguration:(NSDictionary *)themeConfiguration
25
- tokenOnly:(BOOL)tokenOnly
26
- saveCard:(BOOL)saveCard
27
- saveAccount:(BOOL)saveAccount
28
- authenticatedACH:(BOOL)authenticatedACH
29
- grailPayParams:(NSDictionary *)grailPayParams
30
- resolver:(RCTPromiseResolveBlock)resolve
31
- rejecter:(RCTPromiseRejectBlock)reject
27
+ billing:(NSString *)amount
28
+ billingInfo:(NSString *)billingInfo
29
+ paymentMethods:(NSArray *)paymentMethods
30
+ themeConfiguration:(NSDictionary *)themeConfiguration
31
+ tokenOnly:(BOOL)tokenOnly
32
+ saveCard:(BOOL)saveCard
33
+ saveAccount:(BOOL)saveAccount
34
+ authenticatedACH:(BOOL)authenticatedACH
35
+ grailPayParams:(NSDictionary *)grailPayParams
36
+ resolver:(RCTPromiseResolveBlock)resolve
37
+ rejecter:(RCTPromiseRejectBlock)reject
32
38
  )
33
39
  {
34
- EasyMerchantSdkPlugin *sdkPlugin = [[EasyMerchantSdkPlugin alloc] init];
40
+ if (!self.sdkPluginInstance) {
41
+ self.sdkPluginInstance = [[EasyMerchantSdkPlugin alloc] init];
42
+ }
35
43
 
36
- [sdkPlugin billing:amount
37
- billinginfo:billingInfo
38
- paymentMethods:paymentMethods
39
- themeConfiguration:themeConfiguration
40
- tokenOnly:tokenOnly
41
- saveCard:saveCard
42
- saveAccount:saveAccount
43
- authenticatedACH:authenticatedACH
44
- grailPayParams:grailPayParams
45
- resolver:resolve
46
- rejecter:reject];
44
+ [self.sdkPluginInstance billing:amount
45
+ billinginfo:billingInfo
46
+ paymentMethods:paymentMethods
47
+ themeConfiguration:themeConfiguration
48
+ tokenOnly:tokenOnly
49
+ saveCard:saveCard
50
+ saveAccount:saveAccount
51
+ authenticatedACH:authenticatedACH
52
+ grailPayParams:grailPayParams
53
+ resolver:^(id result) {
54
+ resolve(result);
55
+ self.sdkPluginInstance = nil;
56
+ }
57
+ rejecter:^(NSString *code, NSString *message, NSError *error) {
58
+ reject(code, message, error);
59
+ self.sdkPluginInstance = nil;
60
+ }];
47
61
  }
48
62
 
49
63
 
50
- RCT_EXPORT_METHOD(setViewController:(RCTPromiseResolveBlock)resolve
51
- rejecter:(RCTPromiseRejectBlock)reject)
64
+
65
+
66
+ RCT_REMAP_METHOD(setViewController,
67
+ setViewControllerWithResolver:(RCTPromiseResolveBlock)resolve
68
+ rejecter:(RCTPromiseRejectBlock)reject)
52
69
  {
53
- UIViewController *rootViewController = [UIApplication sharedApplication].delegate.window.rootViewController;
70
+ UIViewController *rootVC = [UIApplication sharedApplication].delegate.window.rootViewController;
54
71
  EasyMerchantSdkPlugin *sdkPlugin = [[EasyMerchantSdkPlugin alloc] init];
55
- [sdkPlugin setViewController:rootViewController];
56
- resolve(@"ViewController set successfully");
72
+ [sdkPlugin setViewController:rootVC];
73
+ resolve(@"ViewController set");
57
74
  }
58
75
 
76
+
59
77
  RCT_EXPORT_METHOD(getPlatformVersion:(RCTPromiseResolveBlock)resolve
60
- rejecter:(RCTPromiseRejectBlock)reject)
78
+ rejecter:(RCTPromiseRejectBlock)reject)
61
79
  {
62
80
  NSString *platformVersion = [NSString stringWithFormat:@"iOS %@", [[UIDevice currentDevice] systemVersion]];
63
81
  resolve(platformVersion);
64
82
  }
65
83
 
66
84
  @end
67
-
@@ -3,11 +3,32 @@ import UIKit
3
3
  import React
4
4
 
5
5
  @objc(EasyMerchantSdkPlugin)
6
- public class EasyMerchantSdkPlugin: NSObject {
6
+ public class EasyMerchantSdkPlugin: NSObject, RCTBridgeModule {
7
+ public static func moduleName() -> String { "EasyMerchantSdk" }
8
+ public static func requiresMainQueueSetup() -> Bool { true }
9
+
10
+ public var bridge: RCTBridge?
7
11
 
8
12
  private var viewController: UIViewController?
9
13
  private let viewControllerQueue = DispatchQueue(label: "com.easymerchantsdk.viewController")
10
14
 
15
+ private var billingResolver: RCTPromiseResolveBlock?
16
+ private var billingRejecter: RCTPromiseRejectBlock?
17
+
18
+ // ✅ Default initializer for Objective-C compatibility
19
+ @objc public override init() {
20
+ super.init()
21
+ }
22
+
23
+ // Optional: Called when the bridge finishes loading
24
+ public func bridgeDidFinishLoading() {
25
+ if self.bridge != nil {
26
+ print("Bridge has been initialized successfully.")
27
+ } else {
28
+ print("Bridge initialization failed.")
29
+ }
30
+ }
31
+
11
32
  @objc public func setViewController(_ viewController: UIViewController) {
12
33
  viewControllerQueue.sync {
13
34
  self.viewController = viewController
@@ -15,12 +36,11 @@ public class EasyMerchantSdkPlugin: NSObject {
15
36
  print("ViewController set: \(String(describing: self.viewController))")
16
37
  }
17
38
 
18
-
19
39
  @objc public func billing(
20
40
  _ amount: String,
21
- billinginfo: String,
41
+ billinginfo: String?,
22
42
  paymentMethods: [String],
23
- themeConfiguration: [String: Any], // Accept dynamic theme from JS
43
+ themeConfiguration: [String: Any],
24
44
  tokenOnly: Bool,
25
45
  saveCard: Bool,
26
46
  saveAccount: Bool,
@@ -34,10 +54,12 @@ public class EasyMerchantSdkPlugin: NSObject {
34
54
  return
35
55
  }
36
56
 
37
- guard let billingInfoData = billinginfo.data(using: .utf8),
38
- let _ = try? JSONSerialization.jsonObject(with: billingInfoData, options: []) else {
39
- rejecter("INVALID_BILLING_INFO", "Billing info must be valid JSON", nil)
40
- return
57
+ if let billinginfo = billinginfo {
58
+ guard let billingInfoData = billinginfo.data(using: .utf8),
59
+ let _ = try? JSONSerialization.jsonObject(with: billingInfoData, options: []) else {
60
+ rejecter("INVALID_BILLING_INFO", "Billing info must be valid JSON", nil)
61
+ return
62
+ }
41
63
  }
42
64
 
43
65
  let methods: [PaymentMethod] = paymentMethods.compactMap {
@@ -63,35 +85,23 @@ public class EasyMerchantSdkPlugin: NSObject {
63
85
  )
64
86
  }
65
87
 
66
- // Extract themeConfiguration values from JS
67
- let bodyBackgroundColor = themeConfiguration["bodyBackgroundColor"] as? String ?? "#EBF8FF"
68
- let containerBackgroundColor = themeConfiguration["containerBackgroundColor"] as? String ?? "#FFFFFF"
69
- let primaryFontColor = themeConfiguration["primaryFontColor"] as? String ?? "#1E3A8A"
70
- let secondaryFontColor = themeConfiguration["secondaryFontColor"] as? String ?? "#696969"
71
- let primaryButtonBackgroundColor = themeConfiguration["primaryButtonBackgroundColor"] as? String ?? "#1D4ED8"
72
- let primaryButtonHoverColor = themeConfiguration["primaryButtonHoverColor"] as? String ?? "#2563EB"
73
- let primaryButtonFontColor = themeConfiguration["primaryButtonFontColor"] as? String ?? "#FFFFFF"
74
- let secondaryButtonBackgroundColor = themeConfiguration["secondaryButtonBackgroundColor"] as? String ?? "#FFFFFF"
75
- let secondaryButtonHoverColor = themeConfiguration["secondaryButtonHoverColor"] as? String ?? "#BFDBFE"
76
- let secondaryButtonFontColor = themeConfiguration["secondaryButtonFontColor"] as? String ?? "#1E40AF"
77
- let borderRadius = themeConfiguration["borderRadius"] as? String ?? "8"
78
- let fontSize = themeConfiguration["fontSize"] as? String ?? "14"
79
-
80
88
  let themeConfig = ThemeConfiguration(
81
- bodyBackgroundColor: bodyBackgroundColor,
82
- containerBackgroundColor: containerBackgroundColor,
83
- primaryFontColor: primaryFontColor,
84
- secondaryFontColor: secondaryFontColor,
85
- primaryButtonBackgroundColor: primaryButtonBackgroundColor,
86
- primaryButtonHoverColor: primaryButtonHoverColor,
87
- primaryButtonFontColor: primaryButtonFontColor,
88
- secondaryButtonBackgroundColor: secondaryButtonBackgroundColor,
89
- secondaryButtonHoverColor: secondaryButtonHoverColor,
90
- secondaryButtonFontColor: secondaryButtonFontColor,
91
- borderRadius: borderRadius,
92
- fontSize: fontSize
89
+ bodyBackgroundColor: themeConfiguration["bodyBackgroundColor"] as? String ?? "#EBF8FF",
90
+ containerBackgroundColor: themeConfiguration["containerBackgroundColor"] as? String ?? "#FFFFFF",
91
+ primaryFontColor: themeConfiguration["primaryFontColor"] as? String ?? "#1E3A8A",
92
+ secondaryFontColor: themeConfiguration["secondaryFontColor"] as? String ?? "#696969",
93
+ primaryButtonBackgroundColor: themeConfiguration["primaryButtonBackgroundColor"] as? String ?? "#1D4ED8",
94
+ primaryButtonHoverColor: themeConfiguration["primaryButtonHoverColor"] as? String ?? "#2563EB",
95
+ primaryButtonFontColor: themeConfiguration["primaryButtonFontColor"] as? String ?? "#FFFFFF",
96
+ secondaryButtonBackgroundColor: themeConfiguration["secondaryButtonBackgroundColor"] as? String ?? "#FFFFFF",
97
+ secondaryButtonHoverColor: themeConfiguration["secondaryButtonHoverColor"] as? String ?? "#BFDBFE",
98
+ secondaryButtonFontColor: themeConfiguration["secondaryButtonFontColor"] as? String ?? "#1E40AF",
99
+ borderRadius: themeConfiguration["borderRadius"] as? String ?? "8",
100
+ fontSize: themeConfiguration["fontSize"] as? String ?? "14"
93
101
  )
94
102
 
103
+ let billingInfoData = billinginfo?.data(using: .utf8) ?? Data()
104
+
95
105
  let request = Request(
96
106
  amount: amountValue,
97
107
  billingInfoData: billingInfoData,
@@ -105,23 +115,22 @@ public class EasyMerchantSdkPlugin: NSObject {
105
115
  grailPayParams: authenticatedACH ? grailParams : nil
106
116
  )
107
117
 
118
+ self.billingResolver = resolver
119
+ self.billingRejecter = rejecter
120
+
108
121
  DispatchQueue.main.async {
109
122
  let controller = EasyPayViewController(request: request, delegate: self)
110
123
  if let vc = self.viewController {
111
- vc.present(controller, animated: true) {
112
- resolver("Payment UI presented")
113
- }
124
+ vc.present(controller, animated: true, completion: nil)
114
125
  } else if let rootVC = UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.rootViewController {
115
- rootVC.present(controller, animated: true) {
116
- resolver("Payment UI presented (fallback)")
117
- }
126
+ rootVC.present(controller, animated: true, completion: nil)
118
127
  } else {
119
128
  rejecter("NO_VIEW_CONTROLLER", "No view controller available to present payment screen.", nil)
129
+ self.clearResolvers()
120
130
  }
121
131
  }
122
132
  }
123
133
 
124
-
125
134
  @objc public func configureEnvironment(_ env: String, apiKey: String, apiSecret: String) {
126
135
  switch env.lowercased() {
127
136
  case "production":
@@ -131,7 +140,6 @@ public class EasyMerchantSdkPlugin: NSObject {
131
140
  default:
132
141
  EnvironmentConfig.setEnvironment(.staging)
133
142
  }
134
-
135
143
  EnvironmentConfig.configure(apiKey: apiKey, apiSecret: apiSecret)
136
144
  }
137
145
 
@@ -141,41 +149,35 @@ public class EasyMerchantSdkPlugin: NSObject {
141
149
  ) {
142
150
  resolver("iOS \(UIDevice.current.systemVersion)")
143
151
  }
144
- }
145
152
 
146
- // MARK: - React Native Bridge
147
- @objc(EasyMerchantSdkPlugin)
148
- extension EasyMerchantSdkPlugin: RCTBridgeModule {
149
- public static func moduleName() -> String { "EasyMerchantSdk" }
150
- public static func requiresMainQueueSetup() -> Bool { true }
153
+ private func clearResolvers() {
154
+ self.billingResolver = nil
155
+ self.billingRejecter = nil
156
+ }
151
157
  }
152
158
 
153
- // MARK: - Payment Delegate
154
159
  extension EasyMerchantSdkPlugin: EasyPayViewControllerDelegate {
155
160
  public func easyPayController(_ controller: EasyPayViewController, didFinishWith result: Result) {
156
161
  DispatchQueue.main.async {
157
162
  switch result.type {
158
163
  case .cancelled:
159
- self.sendEvent(name: "EasyPayCancelled", body: ["message": "User cancelled"])
164
+ self.billingResolver?(["status": "cancelled", "message": "User cancelled"])
160
165
  case .success:
161
166
  if let chargeData = result.chargeData,
162
167
  let clientToken = chargeData["clientToken"] as? String {
163
- self.sendEvent(name: "EasyPaySuccess", body: ["clientToken": clientToken])
168
+ self.billingResolver?(["status": "success", "clientToken": clientToken])
164
169
  } else {
165
- self.sendEvent(name: "EasyPaySuccess", body: ["chargeData": result.chargeData ?? [:]])
170
+ self.billingResolver?(["status": "success", "chargeData": result.chargeData ?? [:]])
166
171
  }
167
172
  case .error:
168
173
  if let error = result.chargeData?["errorMessage"] as? String {
169
- self.sendEvent(name: "EasyPayError", body: ["error": error])
174
+ self.billingRejecter?("PAYMENT_ERROR", error, nil)
170
175
  } else {
171
- self.sendEvent(name: "EasyPayError", body: ["error": "Unknown error"])
176
+ self.billingRejecter?("PAYMENT_ERROR", "Unknown error", nil)
172
177
  }
173
178
  }
179
+ self.clearResolvers()
180
+ controller.dismiss(animated: true)
174
181
  }
175
182
  }
176
-
177
- private func sendEvent(name: String, body: [String: Any]) {
178
- NotificationCenter.default.post(name: NSNotification.Name(name), object: nil, userInfo: body)
179
- }
180
183
  }
181
-
@@ -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
+