@jimrising/easymerchantsdk-react-native 1.8.4 → 1.8.6

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.
Files changed (88) hide show
  1. package/README.md +1015 -350
  2. package/android/build/.transforms/15b6a8a60a6b32d0dcaf609723cf365b/transformed/classes/classes_dex/classes.dex +0 -0
  3. package/android/build/.transforms/8508f1428f740032c45a43f48b1bbe1e/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/reactlibrary/RNEasymerchantsdkModule$1$1.dex +0 -0
  4. package/android/build/.transforms/8508f1428f740032c45a43f48b1bbe1e/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/reactlibrary/RNEasymerchantsdkModule$1.dex +0 -0
  5. package/android/build/.transforms/8508f1428f740032c45a43f48b1bbe1e/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/reactlibrary/RNEasymerchantsdkModule$2.dex +0 -0
  6. package/android/build/.transforms/8508f1428f740032c45a43f48b1bbe1e/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/reactlibrary/RNEasymerchantsdkModule.dex +0 -0
  7. package/android/build/.transforms/8508f1428f740032c45a43f48b1bbe1e/transformed/bundleLibRuntimeToDirDebug/desugar_graph.bin +0 -0
  8. package/android/build/.transforms/eef2d06269ef2e204b4f065513034fca/transformed/classes/classes_dex/classes.dex +0 -0
  9. package/android/build/.transforms/ffabb40f9809b32eb9546ce76fc51764/transformed/bundleLibRuntimeToDirRelease/bundleLibRuntimeToDirRelease_dex/com/reactlibrary/RNEasymerchantsdkModule$1$1.dex +0 -0
  10. package/android/build/.transforms/ffabb40f9809b32eb9546ce76fc51764/transformed/bundleLibRuntimeToDirRelease/bundleLibRuntimeToDirRelease_dex/com/reactlibrary/RNEasymerchantsdkModule$1.dex +0 -0
  11. package/android/build/.transforms/ffabb40f9809b32eb9546ce76fc51764/transformed/bundleLibRuntimeToDirRelease/bundleLibRuntimeToDirRelease_dex/com/reactlibrary/RNEasymerchantsdkModule$2.dex +0 -0
  12. package/android/build/.transforms/ffabb40f9809b32eb9546ce76fc51764/transformed/bundleLibRuntimeToDirRelease/bundleLibRuntimeToDirRelease_dex/com/reactlibrary/RNEasymerchantsdkModule.dex +0 -0
  13. package/android/build/.transforms/ffabb40f9809b32eb9546ce76fc51764/transformed/bundleLibRuntimeToDirRelease/desugar_graph.bin +0 -0
  14. package/android/build/intermediates/aar_main_jar/release/syncReleaseLibJars/classes.jar +0 -0
  15. package/android/build/intermediates/compile_library_classes_jar/debug/bundleLibCompileToJarDebug/classes.jar +0 -0
  16. package/android/build/intermediates/compile_library_classes_jar/release/bundleLibCompileToJarRelease/classes.jar +0 -0
  17. package/android/build/intermediates/full_jar/release/createFullJarRelease/full.jar +0 -0
  18. package/android/build/intermediates/incremental/lintVitalAnalyzeRelease/release-artifact-dependencies.xml +4 -4
  19. package/android/build/intermediates/incremental/lintVitalAnalyzeRelease/release-artifact-libraries.xml +4 -4
  20. package/android/build/intermediates/incremental/release/mergeReleaseResources/compile-file-map.properties +126 -21
  21. package/android/build/intermediates/incremental/release/mergeReleaseResources/merger.xml +3 -3
  22. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/reactlibrary/RNEasymerchantsdkModule$1$1.class +0 -0
  23. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/reactlibrary/RNEasymerchantsdkModule$1.class +0 -0
  24. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/reactlibrary/RNEasymerchantsdkModule$2.class +0 -0
  25. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/reactlibrary/RNEasymerchantsdkModule.class +0 -0
  26. package/android/build/intermediates/javac/release/compileReleaseJavaWithJavac/classes/com/reactlibrary/RNEasymerchantsdkModule$1$1.class +0 -0
  27. package/android/build/intermediates/javac/release/compileReleaseJavaWithJavac/classes/com/reactlibrary/RNEasymerchantsdkModule$1.class +0 -0
  28. package/android/build/intermediates/javac/release/compileReleaseJavaWithJavac/classes/com/reactlibrary/RNEasymerchantsdkModule$2.class +0 -0
  29. package/android/build/intermediates/javac/release/compileReleaseJavaWithJavac/classes/com/reactlibrary/RNEasymerchantsdkModule.class +0 -0
  30. package/android/build/intermediates/lint-cache/lintVitalAnalyzeRelease/maven.google/androidx/core/group-index.xml +4 -4
  31. package/android/build/intermediates/lint-cache/lintVitalAnalyzeRelease/maven.google/androidx/lifecycle/group-index.xml +2 -2
  32. package/android/build/intermediates/lint-cache/lintVitalAnalyzeRelease/maven.google/com/android/tools/build/group-index.xml +14 -14
  33. package/android/build/intermediates/lint-cache/lintVitalAnalyzeRelease/sdk_index/snapshot.gz +0 -0
  34. package/android/build/intermediates/lint_model/release/generateReleaseLintModel/release-artifact-dependencies.xml +4 -4
  35. package/android/build/intermediates/lint_model/release/generateReleaseLintModel/release-artifact-libraries.xml +4 -4
  36. package/android/build/intermediates/lint_vital_lint_model/release/generateReleaseLintVitalModel/release-artifact-dependencies.xml +4 -4
  37. package/android/build/intermediates/lint_vital_lint_model/release/generateReleaseLintVitalModel/release-artifact-libraries.xml +4 -4
  38. package/android/build/intermediates/local_aar_for_lint/release/out.aar +0 -0
  39. package/android/build/intermediates/merged_res/release/mergeReleaseResources/layout/activity_card_addition_ifo.xml +4 -5
  40. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/multi-v2/values-night-v8.json +2 -2
  41. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/multi-v2/values.json +38 -38
  42. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/drawable.json +62 -62
  43. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/font.json +9 -9
  44. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/layout.json +20 -20
  45. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/mipmap-anydpi-v26.json +2 -2
  46. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/mipmap-hdpi-v4.json +2 -2
  47. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/mipmap-mdpi-v4.json +2 -2
  48. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/mipmap-xhdpi-v4.json +2 -2
  49. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/mipmap-xxhdpi-v4.json +2 -2
  50. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/mipmap-xxxhdpi-v4.json +2 -2
  51. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/xml.json +2 -2
  52. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/reactlibrary/RNEasymerchantsdkModule$1$1.class +0 -0
  53. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/reactlibrary/RNEasymerchantsdkModule$1.class +0 -0
  54. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/reactlibrary/RNEasymerchantsdkModule$2.class +0 -0
  55. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/reactlibrary/RNEasymerchantsdkModule.class +0 -0
  56. package/android/build/intermediates/runtime_library_classes_dir/release/bundleLibRuntimeToDirRelease/com/reactlibrary/RNEasymerchantsdkModule$1$1.class +0 -0
  57. package/android/build/intermediates/runtime_library_classes_dir/release/bundleLibRuntimeToDirRelease/com/reactlibrary/RNEasymerchantsdkModule$1.class +0 -0
  58. package/android/build/intermediates/runtime_library_classes_dir/release/bundleLibRuntimeToDirRelease/com/reactlibrary/RNEasymerchantsdkModule$2.class +0 -0
  59. package/android/build/intermediates/runtime_library_classes_dir/release/bundleLibRuntimeToDirRelease/com/reactlibrary/RNEasymerchantsdkModule.class +0 -0
  60. package/android/build/intermediates/runtime_library_classes_jar/debug/bundleLibRuntimeToJarDebug/classes.jar +0 -0
  61. package/android/build/intermediates/runtime_library_classes_jar/release/bundleLibRuntimeToJarRelease/classes.jar +0 -0
  62. package/android/build/intermediates/source_set_path_map/release/mapReleaseSourceSetPaths/file-map.txt +2 -2
  63. package/android/build/intermediates/verified_library_resources/release/verifyReleaseResources/compiled/layout_activity_card_addition_ifo.xml.flat +0 -0
  64. package/android/build/outputs/aar/jimrising_easymerchantsdk-react-native-release.aar +0 -0
  65. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/RNEasymerchantsdkModule$1$1.class.uniqueId1 +0 -0
  66. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/RNEasymerchantsdkModule$1.class.uniqueId0 +0 -0
  67. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/RNEasymerchantsdkModule$2.class.uniqueId3 +0 -0
  68. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/RNEasymerchantsdkModule.class.uniqueId2 +0 -0
  69. package/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin +0 -0
  70. package/android/build/tmp/compileReleaseJavaWithJavac/compileTransaction/stash-dir/RNEasymerchantsdkModule$1$1.class.uniqueId1 +0 -0
  71. package/android/build/tmp/compileReleaseJavaWithJavac/compileTransaction/stash-dir/RNEasymerchantsdkModule$1.class.uniqueId3 +0 -0
  72. package/android/build/tmp/compileReleaseJavaWithJavac/compileTransaction/stash-dir/RNEasymerchantsdkModule$2.class.uniqueId0 +0 -0
  73. package/android/build/tmp/compileReleaseJavaWithJavac/compileTransaction/stash-dir/{RNEasymerchantsdkModule.class.uniqueId1 → RNEasymerchantsdkModule.class.uniqueId2} +0 -0
  74. package/android/build/tmp/compileReleaseJavaWithJavac/previous-compilation-data.bin +0 -0
  75. package/android/build.gradle +1 -1
  76. package/android/src/main/java/com/reactlibrary/RNEasymerchantsdkModule.java +74 -38
  77. package/ios/Classes/EasyPayViewController.swift +3 -2
  78. package/ios/Models/Request.swift +77 -221
  79. package/ios/Pods/ViewControllers/AdditionalInfoVC.swift +12 -1
  80. package/ios/Pods/ViewControllers/BillingInfoVC/BillingInfoVC.swift +17 -6
  81. package/ios/Pods/ViewControllers/OTPVerificationVC.swift +10 -1
  82. package/ios/Pods/ViewControllers/PaymentDoneVC.swift +5 -1
  83. package/ios/Pods/ViewControllers/PaymentInformation/PaymentInfoVC.swift +39 -11
  84. package/ios/Pods/ViewControllers/ThreeDSecurePaymentDoneVC.swift +15 -5
  85. package/ios/easymerchantsdk.podspec +1 -1
  86. package/package.json +1 -1
  87. package/android/build/tmp/compileReleaseJavaWithJavac/compileTransaction/stash-dir/RNEasymerchantsdkModule$1.class.uniqueId2 +0 -0
  88. /package/android/build/tmp/compileReleaseJavaWithJavac/compileTransaction/stash-dir/{RNEasymerchantsdkPackage.class.uniqueId3 → RNEasymerchantsdkPackage.class.uniqueId4} +0 -0
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.8.4"
10
+ "@jimrising/easymerchantsdk-react-native": "^1.8.6"
11
11
  },
12
12
  ```
13
13
 
@@ -128,56 +128,320 @@ import {
128
128
  ScrollView,
129
129
  Platform,
130
130
  NativeModules,
131
+ Switch,
132
+ TouchableOpacity,
133
+ NativeEventEmitter,
131
134
  } from 'react-native';
132
135
 
133
136
  const { RNEasymerchantsdk, EasyMerchantSdk } = NativeModules;
134
137
 
138
+ const externalConfig = {
139
+ amount: '',
140
+ email: '',
141
+ isRecurring: false,
142
+ isAuthenticatedACH: false,
143
+ isSecureAuthentication: false,
144
+ isBillingVisible: false,
145
+ isAdditionalVisible: false,
146
+ emailEditable: true,
147
+ isEmail: true,
148
+ billingInfo: {
149
+ visibility: { billing: false, additional: false },
150
+ billing: {
151
+ address: 'San Fran, Punjab',
152
+ country: 'USA',
153
+ state: 'California',
154
+ city: 'Paris',
155
+ postal_code: '234234',
156
+ },
157
+ billingRequired: {
158
+ address: true,
159
+ country: true,
160
+ state: true,
161
+ city: false,
162
+ postal_code: true,
163
+ },
164
+ additional: {
165
+ name: 'Test User',
166
+ email_address: 'test@gmail.com',
167
+ phone_number: '21408713290',
168
+ description: 'Test',
169
+ },
170
+ additionalRequired: {
171
+ name: true,
172
+ email_address: true,
173
+ phone_number: true,
174
+ description: false,
175
+ },
176
+ },
177
+ themeConfiguration: {
178
+ bodyBackgroundColor: '#0f1715',
179
+ containerBackgroundColor: '#152321',
180
+ primaryFontColor: '#FFFFFF',
181
+ secondaryFontColor: '#A0B5A4',
182
+ primaryButtonBackgroundColor: '#10B981',
183
+ primaryButtonHoverColor: '#059669',
184
+ primaryButtonFontColor: '#FFFFFF',
185
+ secondaryButtonBackgroundColor: '#374151',
186
+ secondaryButtonHoverColor: '#4B5563',
187
+ secondaryButtonFontColor: '#E5E7EB',
188
+ borderRadius: '8',
189
+ fontSize: '14',
190
+ fontWeight: 500,
191
+ fontFamily: '"Inter", sans-serif',
192
+ },
193
+ grailPayParams: {
194
+ role: 'business',
195
+ timeout: 10,
196
+ isSandbox: true,
197
+ brandingName: 'Lyfecycle Payments',
198
+ finderSubtitle: 'Search for your bank',
199
+ searchPlaceholder: 'Enter bank name',
200
+ },
201
+ recurringData: {
202
+ allowCycles: 2,
203
+ intervals: ['weekly', 'monthly'],
204
+ recurringStartType: 'custom',
205
+ recurringStartDate: '07/08/2025',
206
+ },
207
+ androidConfig: {
208
+ currency: 'usd',
209
+ saveCard: true,
210
+ saveAccount: true,
211
+ showReceipt: true,
212
+ showDonate: false,
213
+ showTotal: true,
214
+ showSubmitButton: true,
215
+ paymentMethod: ['card', 'ach'],
216
+ name: 'Pavan',
217
+ fields: {
218
+ visibility: { billing: false, additional: false },
219
+ billing: [
220
+ { name: 'address', required: true, value: 'New Address' },
221
+ { name: 'country', required: true, value: 'India' },
222
+ { name: 'state', required: true, value: 'California' },
223
+ { name: 'city', required: true, value: 'Goa' },
224
+ { name: 'postal_code', required: true, value: '1432456' },
225
+ ],
226
+ additional: [
227
+ { name: 'name', required: true, value: 'Test User 7' },
228
+ { name: 'email_address', required: true, value: 'usertest@gmail.com' },
229
+ { name: 'phone_number', required: true, value: '8978967895' },
230
+ { name: 'description', required: true, value: 'Hi This is description' },
231
+ ],
232
+ },
233
+ appearanceSettings: {
234
+ theme: 'dark',
235
+ bodyBackgroundColor: '#121212',
236
+ containerBackgroundColor: '#1E1E1E',
237
+ primaryFontColor: '#FFFFFF',
238
+ secondaryFontColor: '#B0B0B0',
239
+ primaryButtonBackgroundColor: '#2563EB',
240
+ primaryButtonHoverColor: '#1D4ED8',
241
+ primaryButtonFontColor: '#FFFFFF',
242
+ secondaryButtonBackgroundColor: '#374151',
243
+ secondaryButtonHoverColor: '#4B5563',
244
+ secondaryButtonFontColor: '#E5E7EB',
245
+ borderRadius: '8',
246
+ fontSize: '14',
247
+ fontWeight: '500',
248
+ fontFamily: 'Inter, sans-serif',
249
+ },
250
+ },
251
+ };
252
+
135
253
  const App = () => {
136
- const [amount, setAmount] = useState('');
137
- const [email, setEmail] = useState('');
254
+ const [amount, setAmount] = useState(externalConfig.amount);
255
+ const [email, setEmail] = useState(externalConfig.email);
138
256
  const [environment, setEnvironment] = useState('sandbox');
139
- const [isRecurring, setIsRecurring] = useState(false);
140
- const [isAuthenticatedACH, setAuthenticatedACH] = useState(false);
141
- const [isSecureAuthentication, setSecureAuthentication] = useState(false);
142
- const [isBillingVisible, setBillingVisible] = useState(false);
143
- const [isAdditionalVisible, setAdditionalVisible] = useState(false);
144
- const [emailEditable, setEmailEditable] = useState(true);
145
- const [isEmail, setIsEmail] = useState(true);
257
+ const [isRecurring, setIsRecurring] = useState(externalConfig.isRecurring);
258
+ const [isAuthenticatedACH, setAuthenticatedACH] = useState(externalConfig.isAuthenticatedACH);
259
+ const [isSecureAuthentication, setSecureAuthentication] = useState(externalConfig.isSecureAuthentication);
260
+ const [isBillingVisible, setBillingVisible] = useState(externalConfig.isBillingVisible);
261
+ const [isAdditionalVisible, setAdditionalVisible] = useState(externalConfig.isAdditionalVisible);
262
+ const [emailEditable, setEmailEditable] = useState(externalConfig.emailEditable); // Android
263
+ const [isEmail, setIsEmail] = useState(externalConfig.isEmail); // iOS
264
+ const [billingInfo, setBillingInfo] = useState(externalConfig.billingInfo);
265
+ const [themeConfiguration, setThemeConfiguration] = useState(externalConfig.themeConfiguration);
266
+ const [grailPayParams, setGrailPayParams] = useState(externalConfig.grailPayParams);
267
+ const [recurringData, setRecurringData] = useState(externalConfig.recurringData);
268
+ const [androidConfig, setAndroidConfig] = useState(externalConfig.androidConfig);
146
269
  const [result, setResult] = useState('');
147
270
  const [referenceToken, setReferenceToken] = useState('');
148
271
  const [loading, setLoading] = useState(false);
149
- const [apiKey, setApiKey] = useState('');
150
- const [apiSecret, setApiSecret] = useState('');
272
+ const [apiKey, setApiKey] = useState('apiKey');
273
+ const [apiSecret, setApiSecret] = useState('apiSecret');
274
+ const [isEnvironmentLoading, setIsEnvironmentLoading] = useState(false);
151
275
 
152
276
  useEffect(() => {
153
- const key = environment === 'staging'
154
- ? 'stagingApiKey'
155
- : 'sandboxApiKey';
156
-
157
- const secret = environment === 'staging'
158
- ? 'stagingApiSecretKey'
159
- : 'sandboxApiSecretKey';
160
-
161
- setApiKey(key);
162
- setApiSecret(secret);
163
-
164
- const setupIOS = async () => {
165
- if (Platform.OS === 'ios') {
166
- try {
167
- await EasyMerchantSdk.setViewController();
168
- await EasyMerchantSdk.configureEnvironment(
169
- environment,
170
- key,
171
- secret
172
- );
173
- } catch (err) {
174
- console.error('iOS Initialization Error:', err);
175
- }
277
+ if (Platform.OS !== 'android') {
278
+ console.log('Event listeners skipped: not Android');
279
+ return;
280
+ }
281
+
282
+ if (!RNEasymerchantsdk) {
283
+ console.warn('RNEasymerchantsdk native module is not available.');
284
+ return;
285
+ }
286
+
287
+ const easyMerchantEvents = new NativeEventEmitter(RNEasymerchantsdk);
288
+ const successSub = easyMerchantEvents.addListener('PaymentSuccess', (data) => {
289
+ console.log('Payment success event:', data);
290
+ const parsed = JSON.parse(data.response);
291
+ console.log('Parsed PaymentSuccess:', parsed);
292
+
293
+ if (parsed.billingInfo) {
294
+ const billing = JSON.parse(parsed.billingInfo);
295
+ console.log('Billing Info:', billing);
296
+ }
297
+
298
+ if (parsed.additional_info) {
299
+ const additional = JSON.parse(parsed.additional_info);
300
+ console.log('Additional Info:', additional);
176
301
  }
302
+
303
+ setResult(JSON.stringify(parsed, null, 2));
304
+ });
305
+
306
+ const statusSub = easyMerchantEvents.addListener('PaymentStatus', (data) => {
307
+ console.log('Raw Payment status event:', data);
308
+ const parsed = JSON.parse(data.statusResponse);
309
+ console.log('Parsed PaymentStatus:', parsed);
310
+
311
+ setResult(JSON.stringify(parsed, null, 2));
312
+ });
313
+
314
+ const statusErrorSub = easyMerchantEvents.addListener('PaymentStatusError', (data) => {
315
+ console.log('Payment status error event:', data);
316
+ setResult(`Status Error: ${JSON.stringify(data)}`);
317
+ });
318
+
319
+ return () => {
320
+ successSub.remove();
321
+ statusSub.remove();
322
+ statusErrorSub.remove();
323
+ };
324
+ }, []);
325
+
326
+
327
+
328
+ useEffect(() => {
329
+ const updateEnvironment = async () => {
330
+ const key = environment === 'staging'
331
+ ? 'stagingApiKey'
332
+ : 'sandboxApiKey';
333
+ const secret = environment === 'staging'
334
+ ? 'stagingApiSecret'
335
+ : 'sandboxApiSecret';
336
+
337
+ setApiKey(key);
338
+ setApiSecret(secret);
339
+
340
+ if (Platform.OS === 'ios') {
341
+ setIsEnvironmentLoading(true);
342
+ try {
343
+ await EasyMerchantSdk.setViewController();
344
+ await EasyMerchantSdk.configureEnvironment(environment, key, secret);
345
+ console.log(`iOS Environment configured: ${environment} with key ${key}`);
346
+ } catch (err) {
347
+ console.error('iOS Initialization Error:', err);
348
+ Alert.alert('Error', `Failed to configure iOS environment: ${err.message}`);
349
+ } finally {
350
+ setIsEnvironmentLoading(false);
351
+ }
352
+ }
353
+ };
354
+
355
+ updateEnvironment();
356
+ }, [environment]);
357
+
358
+ useEffect(() => {
359
+ setBillingInfo(prev => ({
360
+ ...prev,
361
+ visibility: {
362
+ billing: isBillingVisible,
363
+ additional: isAdditionalVisible,
364
+ },
365
+ }));
366
+ }, [isBillingVisible, isAdditionalVisible]);
367
+
368
+ const updateBillingInfo = (section, field, value) => {
369
+ setBillingInfo(prev => ({
370
+ ...prev,
371
+ [section]: {
372
+ ...prev[section],
373
+ [field]: value,
374
+ },
375
+ }));
376
+ };
377
+
378
+ const updateAndroidConfig = (field, value) => {
379
+ setAndroidConfig(prev => ({
380
+ ...prev,
381
+ [field]: value,
382
+ }));
177
383
  };
178
- setupIOS();
179
- }, [environment]);
180
384
 
385
+ const updateAndroidConfigFields = (section, index, field, value) => {
386
+ setAndroidConfig(prev => ({
387
+ ...prev,
388
+ fields: {
389
+ ...prev.fields,
390
+ [section]: prev.fields[section].map((item, i) =>
391
+ i === index ? { ...item, [field]: value } : item
392
+ ),
393
+ },
394
+ }));
395
+ };
396
+
397
+ const updateAndroidConfigAppearanceSettings = (field, value) => {
398
+ setAndroidConfig(prev => ({
399
+ ...prev,
400
+ appearanceSettings: {
401
+ ...prev.appearanceSettings,
402
+ [field]: value,
403
+ },
404
+ }));
405
+ };
406
+
407
+ const updateThemeConfiguration = (field, value) => {
408
+ setThemeConfiguration(prev => ({
409
+ ...prev,
410
+ [field]: value,
411
+ }));
412
+ };
413
+
414
+ const updateGrailPayParams = (field, value) => {
415
+ setGrailPayParams(prev => ({
416
+ ...prev,
417
+ [field]: value,
418
+ }));
419
+ };
420
+
421
+ const updateRecurringData = (field, value) => {
422
+ setRecurringData(prev => ({
423
+ ...prev,
424
+ [field]: value,
425
+ }));
426
+ };
427
+
428
+ const togglePaymentMethod = (method) => {
429
+ setAndroidConfig(prev => ({
430
+ ...prev,
431
+ paymentMethod: prev.paymentMethod.includes(method)
432
+ ? prev.paymentMethod.filter(m => m !== method)
433
+ : [...prev.paymentMethod, method],
434
+ }));
435
+ };
436
+
437
+ const toggleInterval = (interval) => {
438
+ setRecurringData(prev => ({
439
+ ...prev,
440
+ intervals: prev.intervals.includes(interval)
441
+ ? prev.intervals.filter(i => i !== interval)
442
+ : [...prev.intervals, interval],
443
+ }));
444
+ };
181
445
 
182
446
  const handlePayment = async () => {
183
447
  if (!amount || isNaN(parseFloat(amount)) || parseFloat(amount) <= 0) {
@@ -188,183 +452,98 @@ useEffect(() => {
188
452
  }
189
453
 
190
454
  setLoading(true);
191
-
192
455
  if (Platform.OS === 'android') {
193
- const config = {
194
- amount,
195
- apiKey: apiKey,
196
- secretKey: apiSecret,
197
- jsonConfig: {
198
- environment,
199
- amount,
200
- tokenOnly: false,
201
- currency: 'usd',
202
- saveCard: true,
203
- saveAccount: true,
204
- authenticatedACH: isAuthenticatedACH,
205
- secureAuthentication: isSecureAuthentication,
206
- showReceipt: true,
207
- showDonate: false,
208
- showTotal: true,
209
- showSubmitButton: true,
210
- paymentMethod: ['card', 'ach'],
211
- emailEditable,
212
- email,
213
- name: 'Pavan',
214
- fields: {
215
- visibility: { billing: isBillingVisible, additional: isAdditionalVisible },
216
- billing: [
217
- { name: 'address', required: true, value: 'New Address' },
218
- { name: 'country', required: true, value: 'India' },
219
- { name: 'state', required: true, value: 'California' },
220
- { name: 'city', required: true, value: 'Goa' },
221
- { name: 'postal_code', required: true, value: '1432456' },
222
- ],
223
- additional: [
224
- { name: 'name', required: true, value: 'Test User 7' },
225
- { name: 'email_address', required: true, value: 'usertest@gmail.com' },
226
- { name: 'phone_number', required: true, value: '8978967895' },
227
- { name: 'description', required: true, value: 'Hi This is description' },
228
- ],
229
- },
230
- ...(isRecurring && {
231
- recurring: {
232
- enableRecurring: true,
233
- recurringData: {
234
- allowCycles: 2,
235
- intervals: ['Weekly', 'Monthly'],
236
- recurringStartType: 'Custom',
237
- recurringStartDate: '31/12/2026',
238
- },
239
- },
240
- }),
241
- grailPayParams: {
242
- role: 'business',
243
- timeout: 10,
244
- isSandbox: true,
245
- brandingName: 'Lyfecycle Payments',
246
- finderSubtitle: 'Search for your bank',
247
- searchPlaceholder: 'Enter bank name',
248
- },
249
- appearanceSettings: {
250
- theme: 'dark',
251
- bodyBackgroundColor: '#121212',
252
- containerBackgroundColor: '#1E1E1E',
253
- primaryFontColor: '#FFFFFF',
254
- secondaryFontColor: '#B0B0B0',
255
- primaryButtonBackgroundColor: '#2563EB',
256
- primaryButtonHoverColor: '#1D4ED8',
257
- primaryButtonFontColor: '#FFFFFF',
258
- secondaryButtonBackgroundColor: '#374151',
259
- secondaryButtonHoverColor: '#4B5563',
260
- secondaryButtonFontColor: '#E5E7EB',
261
- borderRadius: '8',
262
- fontSize: '14',
263
- fontWeight: '500',
264
- fontFamily: 'Inter, sans-serif'
265
- }
266
- },
267
- };
268
-
269
- try {
270
- const response = await RNEasymerchantsdk.makePayment(config);
271
- console.log('Full payment response:', response);
272
- setResult(JSON.stringify(response, null, 2));
273
-
274
- } catch (error) {
275
- setResult(`Error: ${error.message}`);
276
- Alert.alert('Payment Error', error.message);
277
- } finally {
278
- setLoading(false);
279
- }
456
+ await handleAndroidBilling();
280
457
  } else {
281
- handleBilling();
458
+ await handleIosBilling();
282
459
  }
283
460
  };
284
461
 
285
- const handleBilling = async () => {
286
- const billingInfo = {
287
- visibility: { billing: isBillingVisible, additional: isAdditionalVisible },
288
- billing: {
289
- address: 'San Fransisco',
290
- country: 'USA',
291
- state: 'California',
292
- city: 'Paris',
293
- postal_code: '234234',
294
- },
295
- billingRequired: {
296
- address: true,
297
- country: true,
298
- state: true,
299
- city: false,
300
- postal_code: true,
301
- },
302
- additional: {
303
- name: 'Test User',
304
- email_address: 'test@gmail.com',
305
- phone_number: '21408713290',
306
- description: 'Test',
307
- },
308
- additionalRequired: {
309
- name: true,
310
- email_address: true,
311
- phone_number: true,
312
- description: false,
462
+ const handleAndroidBilling = async () => {
463
+ const config = {
464
+ amount,
465
+ apiKey,
466
+ secretKey: apiSecret,
467
+ jsonConfig: {
468
+ environment,
469
+ amount,
470
+ tokenOnly: false,
471
+ currency: androidConfig.currency,
472
+ saveCard: androidConfig.saveCard,
473
+ saveAccount: androidConfig.saveAccount,
474
+ authenticatedACH: isAuthenticatedACH,
475
+ secureAuthentication: isSecureAuthentication,
476
+ showReceipt: androidConfig.showReceipt,
477
+ showDonate: androidConfig.showDonate,
478
+ showTotal: androidConfig.showTotal,
479
+ showSubmitButton: androidConfig.showSubmitButton,
480
+ paymentMethod: androidConfig.paymentMethod,
481
+ emailEditable,
482
+ email,
483
+ name: androidConfig.name,
484
+ fields: {
485
+ ...androidConfig.fields,
486
+ visibility: {
487
+ billing: isBillingVisible,
488
+ additional: isAdditionalVisible,
489
+ },
313
490
  },
314
- };
491
+ ...(isRecurring && {
492
+ recurring: {
493
+ enableRecurring: true,
494
+ recurringData,
495
+ },
496
+ }),
497
+ grailPayParams,
498
+ appearanceSettings: androidConfig.appearanceSettings,
499
+ },
500
+ };
315
501
 
316
- const themeConfiguration = {
317
- bodyBackgroundColor: "#0f1715",
318
- containerBackgroundColor: "#152321",
319
- primaryFontColor: "#FFFFFF",
320
- secondaryFontColor: "#A0B5A4",
321
- primaryButtonBackgroundColor: "#10B981",
322
- primaryButtonHoverColor: "#059669",
323
- primaryButtonFontColor: "#FFFFFF",
324
- secondaryButtonBackgroundColor: "#374151",
325
- secondaryButtonHoverColor: "#4B5563",
326
- secondaryButtonFontColor: "#E5E7EB",
327
- borderRadius: "8",
328
- fontSize: "14",
329
- fontWeight: 500,
330
- fontFamily: "\"Inter\", sans-serif"
331
- };
332
-
333
-
334
- const authConfig = {
335
- role: 'business',
336
- timeout: 10,
337
- isSandbox: true,
338
- brandingName: 'Lyfecycle Payments',
339
- finderSubtitle: 'Search for your bank',
340
- searchPlaceholder: 'Enter bank name',
502
+ try {
503
+ const response = await RNEasymerchantsdk.makePayment(config);
504
+ console.log('Full payment response:', response);
505
+
506
+ const parsedResponse = {
507
+ ...response,
508
+ billingInfo: response.billingInfo ? JSON.parse(response.billingInfo) : null,
509
+ additional_info: response.additional_info ? JSON.parse(response.additional_info) : null,
341
510
  };
342
511
 
512
+ setResult(JSON.stringify(parsedResponse, null, 2));
513
+ } catch (error) {
514
+ setResult(`Error: ${error.message}`);
515
+ Alert.alert('Payment Error', error.message);
516
+ } finally {
517
+ setLoading(false);
518
+ }
519
+ };
520
+
521
+ const handleIosBilling = async () => {
343
522
  try {
344
523
  const result = await EasyMerchantSdk.billing(
345
524
  amount,
346
- 'usd',
525
+ androidConfig.currency || 'usd',
347
526
  billingInfo,
348
- ['card', 'bank'],
527
+ androidConfig.paymentMethod,
349
528
  themeConfiguration,
350
- false, // tokenOnly
351
- true, // saveCard
352
- true, // saveAccount
353
- isAuthenticatedACH, // authenticatedACH
354
- authConfig, // grailPayParams
529
+ false, // tokenOnly
530
+ androidConfig.saveCard,
531
+ androidConfig.saveAccount,
532
+ isAuthenticatedACH,
533
+ grailPayParams,
355
534
  'Submit',
356
- isRecurring, // isRecurring
357
- isRecurring ? 2 : 0, // if isRecurring == true then numOfCycle required
358
- isRecurring ? ['weekly', 'monthly'] : [], // if isRecurring == true then it is required
359
- isRecurring ? 'custom' : '', // if isRecurring == true then it is required
360
- isRecurring ? '07/08/2025' : '', // if isRecurring == true then it is required
361
- isSecureAuthentication, // secureAuthentication
362
- true, // showReceipt
363
- true, // showTotal
364
- true, // showSubmitButton
535
+ isRecurring,
536
+ isRecurring ? recurringData.allowCycles : 0,
537
+ isRecurring ? recurringData.intervals : [],
538
+ isRecurring ? recurringData.recurringStartType : '',
539
+ isRecurring ? recurringData.recurringStartDate : '',
540
+ isSecureAuthentication,
541
+ androidConfig.showReceipt,
542
+ androidConfig.showTotal,
543
+ androidConfig.showSubmitButton,
365
544
  isEmail,
366
545
  email,
367
- "Pavan"
546
+ androidConfig.name
368
547
  );
369
548
 
370
549
  const refToken = result?.additionalInfo?.threeDSecureStatus?.data?.ref_token;
@@ -372,38 +551,42 @@ useEffect(() => {
372
551
  setResult(JSON.stringify(result, null, 2));
373
552
  } catch (error) {
374
553
  console.error('Billing Error:', error);
375
- setResult(`❌ Billing Error: ${error.message || JSON.stringify(error)}`);
554
+ setResult(`Billing Error: ${error.message || JSON.stringify(error)}`);
555
+ } finally {
556
+ setLoading(false);
376
557
  }
377
558
  };
378
559
 
379
- const handleCheckStatus = async () => {
380
- setLoading(true);
381
- try {
382
- const response = await RNEasymerchantsdk.checkPaymentStatus();
383
- console.log('Full payment response:', response);
384
- setResult(JSON.stringify(response, null, 2));
385
- } catch (error) {
386
- setResult(`Error: ${error.message}`);
387
- Alert.alert('Status Check Error', error.message);
388
- } finally {
389
- setLoading(false);
390
- }
391
- };
560
+ const handleCheckStatus = async () => {
561
+ setLoading(true);
562
+ try {
563
+ const response = await RNEasymerchantsdk.checkPaymentStatus();
564
+ console.log('Full payment response:', response);
565
+ setResult(JSON.stringify(response, null, 2));
566
+ } catch (error) {
567
+ setResult(`Error: ${error.message}`);
568
+ Alert.alert('Status Check Error', error.message);
569
+ } finally {
570
+ setLoading(false);
571
+ }
572
+ };
392
573
 
393
574
  const handlePaymentReference = async () => {
394
575
  if (Platform.OS === 'android') {
395
- setResult('Payment Reference not supported on Android');
576
+ setResult('Payment Reference not supported on Android');
396
577
  return;
397
578
  }
398
579
  if (!referenceToken) {
399
- setResult('No reference token available from billing');
580
+ setResult('No reference token available');
400
581
  return;
401
582
  }
402
583
  try {
403
- const result = await EasyMerchantSdk.paymentReference(referenceToken);
404
- setResult(`✅ Payment Reference:\n${JSON.stringify(result, null, 2)}`);
584
+ const response = await EasyMerchantSdk.paymentReference(referenceToken);
585
+ setResult(`Payment Reference:\n${JSON.stringify(response, null, 2)}`);
405
586
  } catch (error) {
406
- setResult(`❌ Payment Reference Error: ${error.message || JSON.stringify(error)}`);
587
+ setResult(`Payment Reference Error: ${error.message || JSON.stringify(error)}`);
588
+ } finally {
589
+ setLoading(false);
407
590
  }
408
591
  };
409
592
 
@@ -412,8 +595,8 @@ useEffect(() => {
412
595
  <ScrollView contentContainerStyle={styles.scrollContent}>
413
596
  <Text style={styles.title}>EasyMerchant SDK</Text>
414
597
 
415
- <Text style={styles.label}>amount</Text>
416
-
598
+ <Text style={styles.sectionTitle}>Basic Info</Text>
599
+ <Text style={styles.label}>Amount</Text>
417
600
  <TextInput
418
601
  style={styles.input}
419
602
  placeholder="Enter amount (e.g., 10.00)"
@@ -421,9 +604,7 @@ useEffect(() => {
421
604
  value={amount}
422
605
  onChangeText={setAmount}
423
606
  />
424
-
425
- <Text style={styles.label}>Email</Text>
426
-
607
+ <Text style={styles.label}>Email</Text>
427
608
  <TextInput
428
609
  style={styles.input}
429
610
  placeholder="Enter email"
@@ -432,147 +613,598 @@ useEffect(() => {
432
613
  onChangeText={setEmail}
433
614
  />
434
615
 
435
- <View style={styles.pickerContainer}>
436
- <Text style={styles.label}>Environment:</Text>
437
- <View style={styles.buttonGroup}>
438
- <Button
439
- title="Sandbox"
440
- onPress={() => setEnvironment('sandbox')}
441
- color={environment === 'sandbox' ? '#2563EB' : '#ccc'}
616
+ <Text style={styles.sectionTitle}>Environment</Text>
617
+ <View style={styles.pickerContainer}>
618
+ <Text style={styles.label}>Select Environment</Text>
619
+ <View style={styles.buttonGroup}>
620
+ <TouchableOpacity
621
+ style={[
622
+ styles.environmentButton,
623
+ { backgroundColor: environment === 'sandbox' ? '#2563EB' : '#ccc' },
624
+ ]}
625
+ onPress={() => {
626
+ console.log('Sandbox tapped, setting environment to sandbox');
627
+ setEnvironment('sandbox');
628
+ }}
629
+ disabled={isEnvironmentLoading || environment === 'sandbox'}
630
+ >
631
+ <Text style={styles.buttonText}>Sandbox</Text>
632
+ </TouchableOpacity>
633
+ <TouchableOpacity
634
+ style={[
635
+ styles.environmentButton,
636
+ { backgroundColor: environment === 'staging' ? '#2563EB' : '#ccc' },
637
+ ]}
638
+ onPress={() => {
639
+ console.log('Staging tapped, setting environment to staging');
640
+ setEnvironment('staging');
641
+ }}
642
+ disabled={isEnvironmentLoading || environment === 'staging'}
643
+ >
644
+ <Text style={styles.buttonText}>Staging</Text>
645
+ </TouchableOpacity>
646
+ </View>
647
+ </View>
648
+
649
+
650
+ <Text style={styles.sectionTitle}>Payment Options</Text>
651
+ <View style={styles.toggleContainer}>
652
+ <Text style={styles.label}>Recurring Payment</Text>
653
+ <Switch
654
+ value={isRecurring}
655
+ onValueChange={setIsRecurring}
656
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
657
+ thumbColor={isRecurring ? '#fff' : '#f4f3f4'}
658
+ />
659
+ </View>
660
+ <View style={styles.toggleContainer}>
661
+ <Text style={styles.label}>Authenticated ACH</Text>
662
+ <Switch
663
+ value={isAuthenticatedACH}
664
+ onValueChange={setAuthenticatedACH}
665
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
666
+ thumbColor={isAuthenticatedACH ? '#fff' : '#f4f3f4'}
667
+ />
668
+ </View>
669
+ <View style={styles.toggleContainer}>
670
+ <Text style={styles.label}>3DS</Text>
671
+ <Switch
672
+ value={isSecureAuthentication}
673
+ onValueChange={setSecureAuthentication}
674
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
675
+ thumbColor={isSecureAuthentication ? '#fff' : '#f4f3f4'}
676
+ />
677
+ </View>
678
+ <View style={styles.toggleContainer}>
679
+ <Text style={styles.label}>Billing Visible</Text>
680
+ <Switch
681
+ value={isBillingVisible}
682
+ onValueChange={setBillingVisible}
683
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
684
+ thumbColor={isBillingVisible ? '#fff' : '#f4f3f4'}
685
+ />
686
+ </View>
687
+ <View style={styles.toggleContainer}>
688
+ <Text style={styles.label}>Additional Info Visible</Text>
689
+ <Switch
690
+ value={isAdditionalVisible}
691
+ onValueChange={setAdditionalVisible}
692
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
693
+ thumbColor={isAdditionalVisible ? '#fff' : '#f4f3f4'}
694
+ />
695
+ </View>
696
+ <Text style={styles.label}>Payment Methods (Shared)</Text>
697
+ <View style={styles.buttonGroup}>
698
+ <Button
699
+ title="Card"
700
+ onPress={() => togglePaymentMethod('card')}
701
+ color={androidConfig.paymentMethod.includes('card') ? '#2563EB' : '#ccc'}
702
+ />
703
+ <Button
704
+ title="ACH"
705
+ onPress={() => togglePaymentMethod('ach')}
706
+ color={androidConfig.paymentMethod.includes('ach') ? '#2563EB' : '#ccc'}
707
+ />
708
+ </View>
709
+ {Platform.OS === 'android' && (
710
+ <View style={styles.toggleContainer}>
711
+ <Text style={styles.label}>Email Editable (Android)</Text>
712
+ <Switch
713
+ value={emailEditable}
714
+ onValueChange={setEmailEditable}
715
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
716
+ thumbColor={emailEditable ? '#fff' : '#f4f3f4'}
442
717
  />
443
- <Button
444
- title="Staging"
445
- onPress={() => setEnvironment('staging')}
446
- color={environment === 'staging' ? '#2563EB' : '#ccc'}
718
+ </View>
719
+ )}
720
+ {Platform.OS === 'ios' && (
721
+ <View style={styles.toggleContainer}>
722
+ <Text style={styles.label}>Allow Email (iOS)</Text>
723
+ <Switch
724
+ value={isEmail}
725
+ onValueChange={setIsEmail}
726
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
727
+ thumbColor={isEmail ? '#fff' : '#f4f3f4'}
447
728
  />
448
729
  </View>
449
- </View>
730
+ )}
450
731
 
732
+ <Text style={styles.sectionTitle}>Billing Info</Text>
733
+ <Text style={styles.label}>Billing Address</Text>
734
+ <TextInput
735
+ style={styles.input}
736
+ value={billingInfo.billing.address}
737
+ onChangeText={value => updateBillingInfo('billing', 'address', value)}
738
+ />
739
+ <Text style={styles.label}>Billing Country</Text>
740
+ <TextInput
741
+ style={styles.input}
742
+ value={billingInfo.billing.country}
743
+ onChangeText={value => updateBillingInfo('billing', 'country', value)}
744
+ />
745
+ <Text style={styles.label}>Billing State</Text>
746
+ <TextInput
747
+ style={styles.input}
748
+ value={billingInfo.billing.state}
749
+ onChangeText={value => updateBillingInfo('billing', 'state', value)}
750
+ />
751
+ <Text style={styles.label}>Billing City</Text>
752
+ <TextInput
753
+ style={styles.input}
754
+ value={billingInfo.billing.city}
755
+ onChangeText={value => updateBillingInfo('billing', 'city', value)}
756
+ />
757
+ <Text style={styles.label}>Billing Postal Code</Text>
758
+ <TextInput
759
+ style={styles.input}
760
+ value={billingInfo.billing.postal_code}
761
+ onChangeText={value => updateBillingInfo('billing', 'postal_code', value)}
762
+ />
763
+ <Text style={styles.label}>Billing Required: Address</Text>
764
+ <Switch
765
+ value={billingInfo.billingRequired.address}
766
+ onValueChange={value => updateBillingInfo('billingRequired', 'address', value)}
767
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
768
+ thumbColor={billingInfo.billingRequired.address ? '#fff' : '#f4f3f4'}
769
+ />
770
+ <Text style={styles.label}>Billing Required: Country</Text>
771
+ <Switch
772
+ value={billingInfo.billingRequired.country}
773
+ onValueChange={value => updateBillingInfo('billingRequired', 'country', value)}
774
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
775
+ thumbColor={billingInfo.billingRequired.country ? '#fff' : '#f4f3f4'}
776
+ />
777
+ <Text style={styles.label}>Billing Required: State</Text>
778
+ <Switch
779
+ value={billingInfo.billingRequired.state}
780
+ onValueChange={value => updateBillingInfo('billingRequired', 'state', value)}
781
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
782
+ thumbColor={billingInfo.billingRequired.state ? '#fff' : '#f4f3f4'}
783
+ />
784
+ <Text style={styles.label}>Billing Required: City</Text>
785
+ <Switch
786
+ value={billingInfo.billingRequired.city}
787
+ onValueChange={value => updateBillingInfo('billingRequired', 'city', value)}
788
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
789
+ thumbColor={billingInfo.billingRequired.city ? '#fff' : '#f4f3f4'}
790
+ />
791
+ <Text style={styles.label}>Billing Required: Postal Code</Text>
792
+ <Switch
793
+ value={billingInfo.billingRequired.postal_code}
794
+ onValueChange={value => updateBillingInfo('billingRequired', 'postal_code', value)}
795
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
796
+ thumbColor={billingInfo.billingRequired.postal_code ? '#fff' : '#f4f3f4'}
797
+ />
798
+ <Text style={styles.label}>Additional: Name</Text>
799
+ <TextInput
800
+ style={styles.input}
801
+ value={billingInfo.additional.name}
802
+ onChangeText={value => updateBillingInfo('additional', 'name', value)}
803
+ />
804
+ <Text style={styles.label}>Additional: Email Address</Text>
805
+ <TextInput
806
+ style={styles.input}
807
+ value={billingInfo.additional.email_address}
808
+ onChangeText={value => updateBillingInfo('additional', 'email_address', value)}
809
+ />
810
+ <Text style={styles.label}>Additional: Phone Number</Text>
811
+ <TextInput
812
+ style={styles.input}
813
+ value={billingInfo.additional.phone_number}
814
+ onChangeText={value => updateBillingInfo('additional', 'phone_number', value)}
815
+ />
816
+ <Text style={styles.label}>Additional: Description</Text>
817
+ <TextInput
818
+ style={styles.input}
819
+ value={billingInfo.additional.description}
820
+ onChangeText={value => updateBillingInfo('additional', 'description', value)}
821
+ />
822
+ <Text style={styles.label}>Additional Required: Name</Text>
823
+ <Switch
824
+ value={billingInfo.additionalRequired.name}
825
+ onValueChange={value => updateBillingInfo('additionalRequired', 'name', value)}
826
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
827
+ thumbColor={billingInfo.additionalRequired.name ? '#fff' : '#f4f3f4'}
828
+ />
829
+ <Text style={styles.label}>Additional Required: Email Address</Text>
830
+ <Switch
831
+ value={billingInfo.additionalRequired.email_address}
832
+ onValueChange={value => updateBillingInfo('additionalRequired', 'email_address', value)}
833
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
834
+ thumbColor={billingInfo.additionalRequired.email_address ? '#fff' : '#f4f3f4'}
835
+ />
836
+ <Text style={styles.label}>Additional Required: Phone Number</Text>
837
+ <Switch
838
+ value={billingInfo.additionalRequired.phone_number}
839
+ onValueChange={value => updateBillingInfo('additionalRequired', 'phone_number', value)}
840
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
841
+ thumbColor={billingInfo.additionalRequired.phone_number ? '#fff' : '#f4f3f4'}
842
+ />
843
+ <Text style={styles.label}>Additional Required: Description</Text>
844
+ <Switch
845
+ value={billingInfo.additionalRequired.description}
846
+ onValueChange={value => updateBillingInfo('additionalRequired', 'description', value)}
847
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
848
+ thumbColor={billingInfo.additionalRequired.description ? '#fff' : '#f4f3f4'}
849
+ />
451
850
 
452
- <View style={styles.pickerContainer}>
453
- <Text style={styles.label}>Billing Visible:</Text>
454
- <View style={styles.buttonGroup}>
455
- <Button
456
- title="Yes"
457
- onPress={() => setBillingVisible(true)}
458
- color={isBillingVisible ? '#2563EB' : '#ccc'}
459
- />
460
- <Button
461
- title="No"
462
- onPress={() => setBillingVisible(false)}
463
- color={!isBillingVisible ? '#2563EB' : '#ccc'}
464
- />
465
- </View>
466
- </View>
467
-
468
- <View style={styles.pickerContainer}>
469
- <Text style={styles.label}>Additional Info Visible:</Text>
470
- <View style={styles.buttonGroup}>
471
- <Button
472
- title="Yes"
473
- onPress={() => setAdditionalVisible(true)}
474
- color={isAdditionalVisible ? '#2563EB' : '#ccc'}
475
- />
476
- <Button
477
- title="No"
478
- onPress={() => setAdditionalVisible(false)}
479
- color={!isAdditionalVisible ? '#2563EB' : '#ccc'}
480
- />
481
- </View>
482
- </View>
483
- <View style={styles.pickerContainer}>
484
- <Text style={styles.label}>Recurring Payment:</Text>
485
- <View style={styles.buttonGroup}>
486
- <Button
487
- title="Yes"
488
- onPress={() => setIsRecurring(true)}
489
- color={isRecurring ? '#2563EB' : '#ccc'}
490
- />
491
- <Button
492
- title="No"
493
- onPress={() => setIsRecurring(false)}
494
- color={!isRecurring ? '#2563EB' : '#ccc'}
851
+ {Platform.OS === 'android' && (
852
+ <>
853
+ <Text style={styles.sectionTitle}>Android Configuration</Text>
854
+ <Text style={styles.label}>Currency</Text>
855
+ <TextInput
856
+ style={styles.input}
857
+ value={androidConfig.currency}
858
+ onChangeText={value => updateAndroidConfig('currency', value)}
495
859
  />
496
- </View>
497
- </View>
498
-
499
- <View style={styles.pickerContainer}>
500
- <Text style={styles.label}>AuthenticatedACH:</Text>
501
- <View style={styles.buttonGroup}>
502
- <Button
503
- title="Yes"
504
- onPress={() => setAuthenticatedACH(true)}
505
- color={isAuthenticatedACH ? '#2563EB' : '#ccc'}
860
+ <Text style={styles.label}>Save Card</Text>
861
+ <Switch
862
+ value={androidConfig.saveCard}
863
+ onValueChange={value => updateAndroidConfig('saveCard', value)}
864
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
865
+ thumbColor={androidConfig.saveCard ? '#fff' : '#f4f3f4'}
506
866
  />
507
- <Button
508
- title="No"
509
- onPress={() => setAuthenticatedACH(false)}
510
- color={!isAuthenticatedACH ? '#2563EB' : '#ccc'}
867
+ <Text style={styles.label}>Save Account</Text>
868
+ <Switch
869
+ value={androidConfig.saveAccount}
870
+ onValueChange={value => updateAndroidConfig('saveAccount', value)}
871
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
872
+ thumbColor={androidConfig.saveAccount ? '#fff' : '#f4f3f4'}
511
873
  />
512
- </View>
513
- </View>
514
-
515
- <View style={styles.pickerContainer}>
516
- <Text style={styles.label}>SecureAuthentication:</Text>
517
- <View style={styles.buttonGroup}>
518
- <Button
519
- title="Yes"
520
- onPress={() => setSecureAuthentication(true)}
521
- color={isSecureAuthentication ? '#2563EB' : '#ccc'}
874
+ <Text style={styles.label}>Show Receipt</Text>
875
+ <Switch
876
+ value={androidConfig.showReceipt}
877
+ onValueChange={value => updateAndroidConfig('showReceipt', value)}
878
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
879
+ thumbColor={androidConfig.showReceipt ? '#fff' : '#f4f3f4'}
522
880
  />
523
- <Button
524
- title="No"
525
- onPress={() => setSecureAuthentication(false)}
526
- color={!isSecureAuthentication ? '#2563EB' : '#ccc'}
881
+ <Text style={styles.label}>Show Donate</Text>
882
+ <Switch
883
+ value={androidConfig.showDonate}
884
+ onValueChange={value => updateAndroidConfig('showDonate', value)}
885
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
886
+ thumbColor={androidConfig.showDonate ? '#fff' : '#f4f3f4'}
527
887
  />
528
- </View>
529
- </View>
530
-
531
- {Platform.OS === 'android' ? (
532
- <View style={styles.pickerContainer}>
533
- <Text style={styles.label}>Email Editable (Android):</Text>
534
- <View style={styles.buttonGroup}>
535
- <Button
536
- title="True"
537
- onPress={() => setEmailEditable(true)}
538
- color={emailEditable ? '#2563EB' : '#ccc'}
539
- />
540
- <Button
541
- title="False"
542
- onPress={() => setEmailEditable(false)}
543
- color={!emailEditable ? '#2563EB' : '#ccc'}
544
- />
545
- </View>
546
- </View>
547
- ) : (
548
- <View style={styles.pickerContainer}>
549
- <Text style={styles.label}>Allow Email (iOS):</Text>
550
- <View style={styles.buttonGroup}>
551
- <Button
552
- title="Yes"
553
- onPress={() => setIsEmail(true)}
554
- color={isEmail ? '#2563EB' : '#ccc'}
555
- />
556
- <Button
557
- title="No"
558
- onPress={() => setIsEmail(false)}
559
- color={!isEmail ? '#2563EB' : '#ccc'}
560
- />
561
- </View>
562
- </View>
888
+ <Text style={styles.label}>Show Total</Text>
889
+ <Switch
890
+ value={androidConfig.showTotal}
891
+ onValueChange={value => updateAndroidConfig('showTotal', value)}
892
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
893
+ thumbColor={androidConfig.showTotal ? '#fff' : '#f4f3f4'}
894
+ />
895
+ <Text style={styles.label}>Show Submit Button</Text>
896
+ <Switch
897
+ value={androidConfig.showSubmitButton}
898
+ onValueChange={value => updateAndroidConfig('showSubmitButton', value)}
899
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
900
+ thumbColor={androidConfig.showSubmitButton ? '#fff' : '#f4f3f4'}
901
+ />
902
+ <Text style={styles.label}>Name</Text>
903
+ <TextInput
904
+ style={styles.input}
905
+ value={androidConfig.name}
906
+ onChangeText={value => updateAndroidConfig('name', value)}
907
+ />
908
+ <Text style={styles.sectionTitle}>Android Fields</Text>
909
+ {androidConfig.fields.billing.map((field, index) => (
910
+ <View key={`billing-${index}`}>
911
+ <Text style={styles.label}>{`Billing ${field.name}`}</Text>
912
+ <TextInput
913
+ style={styles.input}
914
+ value={field.value}
915
+ onChangeText={value => updateAndroidConfigFields('billing', index, 'value', value)}
916
+ />
917
+ <Text style={styles.label}>{`Billing ${field.name} Required`}</Text>
918
+ <Switch
919
+ value={field.required}
920
+ onValueChange={value => updateAndroidConfigFields('billing', index, 'required', value)}
921
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
922
+ thumbColor={field.required ? '#fff' : '#f4f3f4'}
923
+ />
924
+ </View>
925
+ ))}
926
+ {androidConfig.fields.additional.map((field, index) => (
927
+ <View key={`additional-${index}`}>
928
+ <Text style={styles.label}>{`Additional ${field.name}`}</Text>
929
+ <TextInput
930
+ style={styles.input}
931
+ value={field.value}
932
+ onChangeText={value => updateAndroidConfigFields('additional', index, 'value', value)}
933
+ />
934
+ <Text style={styles.label}>{`Additional ${field.name} Required`}</Text>
935
+ <Switch
936
+ value={field.required}
937
+ onValueChange={value => updateAndroidConfigFields('additional', index, 'required', value)}
938
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
939
+ thumbColor={field.required ? '#fff' : '#f4f3f4'}
940
+ />
941
+ </View>
942
+ ))}
943
+ <Text style={styles.sectionTitle}>Android Appearance Settings</Text>
944
+ <Text style={styles.label}>Theme</Text>
945
+ <TextInput
946
+ style={styles.input}
947
+ value={androidConfig.appearanceSettings.theme}
948
+ onChangeText={value => updateAndroidConfigAppearanceSettings('theme', value)}
949
+ />
950
+ <Text style={styles.label}>Body Background Color</Text>
951
+ <TextInput
952
+ style={styles.input}
953
+ value={androidConfig.appearanceSettings.bodyBackgroundColor}
954
+ onChangeText={value => updateAndroidConfigAppearanceSettings('bodyBackgroundColor', value)}
955
+ />
956
+ <Text style={styles.label}>Container Background Color</Text>
957
+ <TextInput
958
+ style={styles.input}
959
+ value={androidConfig.appearanceSettings.containerBackgroundColor}
960
+ onChangeText={value => updateAndroidConfigAppearanceSettings('containerBackgroundColor', value)}
961
+ />
962
+ <Text style={styles.label}>Primary Font Color</Text>
963
+ <TextInput
964
+ style={styles.input}
965
+ value={androidConfig.appearanceSettings.primaryFontColor}
966
+ onChangeText={value => updateAndroidConfigAppearanceSettings('primaryFontColor', value)}
967
+ />
968
+ <Text style={styles.label}>Secondary Font Color</Text>
969
+ <TextInput
970
+ style={styles.input}
971
+ value={androidConfig.appearanceSettings.secondaryFontColor}
972
+ onChangeText={value => updateAndroidConfigAppearanceSettings('secondaryFontColor', value)}
973
+ />
974
+ <Text style={styles.label}>Primary Button Background Color</Text>
975
+ <TextInput
976
+ style={styles.input}
977
+ value={androidConfig.appearanceSettings.primaryButtonBackgroundColor}
978
+ onChangeText={value => updateAndroidConfigAppearanceSettings('primaryButtonBackgroundColor', value)}
979
+ />
980
+ <Text style={styles.label}>Primary Button Hover Color</Text>
981
+ <TextInput
982
+ style={styles.input}
983
+ value={androidConfig.appearanceSettings.primaryButtonHoverColor}
984
+ onChangeText={value => updateAndroidConfigAppearanceSettings('primaryButtonHoverColor', value)}
985
+ />
986
+ <Text style={styles.label}>Primary Button Font Color</Text>
987
+ <TextInput
988
+ style={styles.input}
989
+ value={androidConfig.appearanceSettings.primaryButtonFontColor}
990
+ onChangeText={value => updateAndroidConfigAppearanceSettings('primaryButtonFontColor', value)}
991
+ />
992
+ <Text style={styles.label}>Secondary Button Background Color</Text>
993
+ <TextInput
994
+ style={styles.input}
995
+ value={androidConfig.appearanceSettings.secondaryButtonBackgroundColor}
996
+ onChangeText={value => updateAndroidConfigAppearanceSettings('secondaryButtonBackgroundColor', value)}
997
+ />
998
+ <Text style={styles.label}>Secondary Button Hover Color</Text>
999
+ <TextInput
1000
+ style={styles.input}
1001
+ value={androidConfig.appearanceSettings.secondaryButtonHoverColor}
1002
+ onChangeText={value => updateAndroidConfigAppearanceSettings('secondaryButtonHoverColor', value)}
1003
+ />
1004
+ <Text style={styles.label}>Secondary Button Font Color</Text>
1005
+ <TextInput
1006
+ style={styles.input}
1007
+ value={androidConfig.appearanceSettings.secondaryButtonFontColor}
1008
+ onChangeText={value => updateAndroidConfigAppearanceSettings('secondaryButtonFontColor', value)}
1009
+ />
1010
+ <Text style={styles.label}>Border Radius</Text>
1011
+ <TextInput
1012
+ style={styles.input}
1013
+ value={androidConfig.appearanceSettings.borderRadius}
1014
+ onChangeText={value => updateAndroidConfigAppearanceSettings('borderRadius', value)}
1015
+ />
1016
+ <Text style={styles.label}>Font Size</Text>
1017
+ <TextInput
1018
+ style={styles.input}
1019
+ value={androidConfig.appearanceSettings.fontSize}
1020
+ onChangeText={value => updateAndroidConfigAppearanceSettings('fontSize', value)}
1021
+ />
1022
+ <Text style={styles.label}>Font Weight</Text>
1023
+ <TextInput
1024
+ style={styles.input}
1025
+ value={String(androidConfig.appearanceSettings.fontWeight)}
1026
+ onChangeText={value => updateAndroidConfigAppearanceSettings('fontWeight', parseInt(value) || '500')}
1027
+ />
1028
+ <Text style={styles.label}>Font Family</Text>
1029
+ <TextInput
1030
+ style={styles.input}
1031
+ value={androidConfig.appearanceSettings.fontFamily}
1032
+ onChangeText={value => updateAndroidConfigAppearanceSettings('fontFamily', value)}
1033
+ />
1034
+ </>
563
1035
  )}
564
1036
 
1037
+ {Platform.OS === 'ios' && (
1038
+ <>
1039
+ <Text style={styles.sectionTitle}>Theme Configuration (iOS)</Text>
1040
+ <Text style={styles.label}>Body Background Color</Text>
1041
+ <TextInput
1042
+ style={styles.input}
1043
+ value={themeConfiguration.bodyBackgroundColor}
1044
+ onChangeText={value => updateThemeConfiguration('bodyBackgroundColor', value)}
1045
+ />
1046
+ <Text style={styles.label}>Container Background Color</Text>
1047
+ <TextInput
1048
+ style={styles.input}
1049
+ value={themeConfiguration.containerBackgroundColor}
1050
+ onChangeText={value => updateThemeConfiguration('containerBackgroundColor', value)}
1051
+ />
1052
+ <Text style={styles.label}>Primary Font Color</Text>
1053
+ <TextInput
1054
+ style={styles.input}
1055
+ value={themeConfiguration.primaryFontColor}
1056
+ onChangeText={value => updateThemeConfiguration('primaryFontColor', value)}
1057
+ />
1058
+ <Text style={styles.label}>Secondary Font Color</Text>
1059
+ <TextInput
1060
+ style={styles.input}
1061
+ value={themeConfiguration.secondaryFontColor}
1062
+ onChangeText={value => updateThemeConfiguration('secondaryFontColor', value)}
1063
+ />
1064
+ <Text style={styles.label}>Primary Button Background Color</Text>
1065
+ <TextInput
1066
+ style={styles.input}
1067
+ value={themeConfiguration.primaryButtonBackgroundColor}
1068
+ onChangeText={value => updateThemeConfiguration('primaryButtonBackgroundColor', value)}
1069
+ />
1070
+ <Text style={styles.label}>Primary Button Hover Color</Text>
1071
+ <TextInput
1072
+ style={styles.input}
1073
+ value={themeConfiguration.primaryButtonHoverColor}
1074
+ onChangeText={value => updateThemeConfiguration('primaryButtonHoverColor', value)}
1075
+ />
1076
+ <Text style={styles.label}>Primary Button Font Color</Text>
1077
+ <TextInput
1078
+ style={styles.input}
1079
+ value={themeConfiguration.primaryButtonFontColor}
1080
+ onChangeText={value => updateThemeConfiguration('primaryButtonFontColor', value)}
1081
+ />
1082
+ <Text style={styles.label}>Secondary Button Background Color</Text>
1083
+ <TextInput
1084
+ style={styles.input}
1085
+ value={themeConfiguration.secondaryButtonBackgroundColor}
1086
+ onChangeText={value => updateThemeConfiguration('secondaryButtonBackgroundColor', value)}
1087
+ />
1088
+ <Text style={styles.label}>Secondary Button Hover Color</Text>
1089
+ <TextInput
1090
+ style={styles.input}
1091
+ value={themeConfiguration.secondaryButtonHoverColor}
1092
+ onChangeText={value => updateThemeConfiguration('secondaryButtonHoverColor', value)}
1093
+ />
1094
+ <Text style={styles.label}>Secondary Button Font Color</Text>
1095
+ <TextInput
1096
+ style={styles.input}
1097
+ value={themeConfiguration.secondaryButtonFontColor}
1098
+ onChangeText={value => updateThemeConfiguration('secondaryButtonFontColor', value)}
1099
+ />
1100
+ <Text style={styles.label}>Border Radius</Text>
1101
+ <TextInput
1102
+ style={styles.input}
1103
+ value={themeConfiguration.borderRadius}
1104
+ onChangeText={value => updateThemeConfiguration('borderRadius', value)}
1105
+ />
1106
+ <Text style={styles.label}>Font Size</Text>
1107
+ <TextInput
1108
+ style={styles.input}
1109
+ value={themeConfiguration.fontSize}
1110
+ onChangeText={value => updateThemeConfiguration('fontSize', value)}
1111
+ />
1112
+ <Text style={styles.label}>Font Weight</Text>
1113
+ <TextInput
1114
+ style={styles.input}
1115
+ value={String(themeConfiguration.fontWeight)}
1116
+ onChangeText={value => updateThemeConfiguration('fontWeight', parseInt(value) || 500)}
1117
+ />
1118
+ <Text style={styles.label}>Font Family</Text>
1119
+ <TextInput
1120
+ style={styles.input}
1121
+ value={themeConfiguration.fontFamily}
1122
+ onChangeText={value => updateThemeConfiguration('fontFamily', value)}
1123
+ />
1124
+ </>
1125
+ )}
565
1126
 
566
- <View style={styles.buttonGroup}>
567
- <Button title="Pay" onPress={handlePayment} />
568
- {Platform.OS === 'ios' ? (
569
- <Button title="Payment Ref" onPress={handlePaymentReference} />
570
- ) : (
571
- <Button title="Check Status" onPress={handleCheckStatus} disabled={loading} />
572
- )}
573
- </View>
1127
+ <Text style={styles.sectionTitle}>GrailPay Parameters</Text>
1128
+ <Text style={styles.label}>Role</Text>
1129
+ <TextInput
1130
+ style={styles.input}
1131
+ value={grailPayParams.role}
1132
+ onChangeText={value => updateGrailPayParams('role', value)}
1133
+ />
1134
+ <Text style={styles.label}>Timeout</Text>
1135
+ <TextInput
1136
+ style={styles.input}
1137
+ value={String(grailPayParams.timeout)}
1138
+ onChangeText={value => updateGrailPayParams('timeout', parseInt(value) || 10)}
1139
+ />
1140
+ <Text style={styles.label}>Is Sandbox</Text>
1141
+ <Switch
1142
+ value={grailPayParams.isSandbox}
1143
+ onValueChange={value => updateGrailPayParams('isSandbox', value)}
1144
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
1145
+ thumbColor={grailPayParams.isSandbox ? '#fff' : '#f4f3f4'}
1146
+ />
1147
+ <Text style={styles.label}>Branding Name</Text>
1148
+ <TextInput
1149
+ style={styles.input}
1150
+ value={grailPayParams.brandingName}
1151
+ onChangeText={value => updateGrailPayParams('brandingName', value)}
1152
+ />
1153
+ <Text style={styles.label}>Finder Subtitle</Text>
1154
+ <TextInput
1155
+ style={styles.input}
1156
+ value={grailPayParams.finderSubtitle}
1157
+ onChangeText={value => updateGrailPayParams('finderSubtitle', value)}
1158
+ />
1159
+ <Text style={styles.label}>Search Placeholder</Text>
1160
+ <TextInput
1161
+ style={styles.input}
1162
+ value={grailPayParams.searchPlaceholder}
1163
+ onChangeText={value => updateGrailPayParams('searchPlaceholder', value)}
1164
+ />
574
1165
 
1166
+ <Text style={styles.sectionTitle}>Recurring Data</Text>
1167
+ <Text style={styles.label}>Allow Cycles</Text>
1168
+ <TextInput
1169
+ style={styles.input}
1170
+ value={String(recurringData.allowCycles)}
1171
+ onChangeText={value => updateRecurringData('allowCycles', parseInt(value) || 2)}
1172
+ />
1173
+ <Text style={styles.label}>Intervals</Text>
1174
+ <View style={styles.buttonGroup}>
1175
+ <Button
1176
+ title="Weekly"
1177
+ onPress={() => toggleInterval('weekly')}
1178
+ color={recurringData.intervals.includes('weekly') ? '#2563EB' : '#ccc'}
1179
+ />
1180
+ <Button
1181
+ title="Monthly"
1182
+ onPress={() => toggleInterval('monthly')}
1183
+ color={recurringData.intervals.includes('monthly') ? '#2563EB' : '#ccc'}
1184
+ />
1185
+ </View>
1186
+ <Text style={styles.label}>Recurring Start Type</Text>
1187
+ <TextInput
1188
+ style={styles.input}
1189
+ value={recurringData.recurringStartType}
1190
+ onChangeText={value => updateRecurringData('recurringStartType', value)}
1191
+ />
1192
+ <Text style={styles.label}>Recurring Start Date</Text>
1193
+ <TextInput
1194
+ style={styles.input}
1195
+ value={recurringData.recurringStartDate}
1196
+ onChangeText={value => updateRecurringData('recurringStartDate', value)}
1197
+ />
575
1198
 
1199
+ <View style={styles.buttonGroup}>
1200
+ <Button title="Pay" onPress={handlePayment} />
1201
+ {Platform.OS === 'ios' ? (
1202
+ <Button title="Payment Ref" onPress={handlePaymentReference} disabled={loading} />
1203
+ ) : (
1204
+ <></>
1205
+ )
1206
+ }
1207
+ </View>
576
1208
 
577
1209
  <Text selectable style={styles.result}>{result}</Text>
578
1210
  </ScrollView>
@@ -592,29 +1224,62 @@ const styles = StyleSheet.create({
592
1224
  marginTop: 50,
593
1225
  textAlign: 'center',
594
1226
  },
1227
+ sectionTitle: {
1228
+ fontSize: 20,
1229
+ fontWeight: 'bold',
1230
+ marginTop: 20,
1231
+ marginBottom: 10,
1232
+ },
595
1233
  input: {
596
1234
  height: 40,
597
1235
  borderColor: '#ccc',
598
1236
  borderWidth: 1,
599
1237
  borderRadius: 5,
600
1238
  paddingHorizontal: 10,
601
- marginBottom: 20,
1239
+ marginBottom: 10,
602
1240
  },
603
1241
  pickerContainer: {
604
1242
  marginBottom: 20,
605
1243
  },
1244
+ toggleContainer: {
1245
+ flexDirection: 'row',
1246
+ justifyContent: 'space-between',
1247
+ alignItems: 'center',
1248
+ marginBottom: 10,
1249
+ },
606
1250
  label: {
607
1251
  fontSize: 16,
608
- marginBottom: 8,
609
1252
  fontWeight: '500',
1253
+ marginBottom: 5,
610
1254
  },
611
1255
  buttonGroup: {
612
1256
  flexDirection: 'row',
613
1257
  justifyContent: 'space-between',
614
1258
  marginBottom: 20,
1259
+ marginTop: 20,
615
1260
  gap: 10,
1261
+
1262
+ },
1263
+ result: {
1264
+ fontSize: 14,
1265
+ fontFamily: 'monospace',
1266
+ color: '#333',
1267
+ marginTop: 20,
1268
+ },
1269
+ environmentButton: {
1270
+ flex: 1,
1271
+ paddingVertical: 10,
1272
+ paddingHorizontal: 20,
1273
+ borderRadius: 5,
1274
+ marginHorizontal: 5,
1275
+ alignItems: 'center',
1276
+ justifyContent: 'center',
1277
+ },
1278
+ buttonText: {
1279
+ color: '#fff',
1280
+ fontSize: 16,
1281
+ fontWeight: '500',
616
1282
  },
617
- result: { fontSize: 14, fontFamily: 'monospace', color: '#333' },
618
1283
  });
619
1284
 
620
1285
  ```