@jimrising/easymerchantsdk-react-native 1.8.4 → 1.8.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.
Files changed (87) hide show
  1. package/README.md +1007 -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 +77 -38
  77. package/ios/Classes/EasyPayViewController.swift +3 -2
  78. package/ios/Models/Request.swift +215 -218
  79. package/ios/Pods/ViewControllers/AdditionalInfoVC.swift +2 -1
  80. package/ios/Pods/ViewControllers/BillingInfoVC/BillingInfoVC.swift +7 -6
  81. package/ios/Pods/ViewControllers/OTPVerificationVC.swift +2 -1
  82. package/ios/Pods/ViewControllers/PaymentInformation/PaymentInfoVC.swift +21 -10
  83. package/ios/Pods/ViewControllers/ThreeDSecurePaymentDoneVC.swift +4 -3
  84. package/ios/easymerchantsdk.podspec +1 -1
  85. package/package.json +1 -1
  86. package/android/build/tmp/compileReleaseJavaWithJavac/compileTransaction/stash-dir/RNEasymerchantsdkModule$1.class.uniqueId2 +0 -0
  87. /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.5"
11
11
  },
12
12
  ```
13
13
 
@@ -128,56 +128,312 @@ 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);
275
+
276
+
277
+ const easyMerchantEvents = new NativeEventEmitter(RNEasymerchantsdk);
151
278
 
152
279
  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
- }
280
+ const successSub = easyMerchantEvents.addListener('PaymentSuccess', (data) => {
281
+ console.log('Payment success event:', data);
282
+ const parsed = JSON.parse(data.response);
283
+ console.log('Parsed PaymentSuccess:', parsed);
284
+
285
+ if (parsed.billingInfo) {
286
+ const billing = JSON.parse(parsed.billingInfo);
287
+ console.log('Billing Info:', billing);
176
288
  }
289
+
290
+ if (parsed.additional_info) {
291
+ const additional = JSON.parse(parsed.additional_info);
292
+ console.log('Additional Info:', additional);
293
+ }
294
+
295
+ setResult(JSON.stringify(parsed, null, 2));
296
+ });
297
+
298
+ const statusSub = easyMerchantEvents.addListener('PaymentStatus', (data) => {
299
+ console.log('Raw Payment status event:', data);
300
+ const parsed = JSON.parse(data.statusResponse);
301
+ console.log('Parsed PaymentStatus:', parsed);
302
+
303
+ setResult(JSON.stringify(parsed, null, 2));
304
+ });
305
+
306
+ const statusErrorSub = easyMerchantEvents.addListener('PaymentStatusError', (data) => {
307
+ console.log('Payment status error event:', data);
308
+ setResult(`Status Error: ${JSON.stringify(data)}`);
309
+ });
310
+
311
+ return () => {
312
+ successSub.remove();
313
+ statusSub.remove();
314
+ statusErrorSub.remove();
315
+ };
316
+ }, []);
317
+
318
+
319
+
320
+ useEffect(() => {
321
+ const updateEnvironment = async () => {
322
+ const key = environment === 'staging'
323
+ ? 'stagingApiKey'
324
+ : 'sandboxApiKey';
325
+ const secret = environment === 'staging'
326
+ ? 'stagingApiSecret'
327
+ : 'sandboxApiSecret';
328
+
329
+ setApiKey(key);
330
+ setApiSecret(secret);
331
+
332
+ if (Platform.OS === 'ios') {
333
+ setIsEnvironmentLoading(true);
334
+ try {
335
+ await EasyMerchantSdk.setViewController();
336
+ await EasyMerchantSdk.configureEnvironment(environment, key, secret);
337
+ console.log(`iOS Environment configured: ${environment} with key ${key}`);
338
+ } catch (err) {
339
+ console.error('iOS Initialization Error:', err);
340
+ Alert.alert('Error', `Failed to configure iOS environment: ${err.message}`);
341
+ } finally {
342
+ setIsEnvironmentLoading(false);
343
+ }
344
+ }
345
+ };
346
+
347
+ updateEnvironment();
348
+ }, [environment]);
349
+
350
+ useEffect(() => {
351
+ setBillingInfo(prev => ({
352
+ ...prev,
353
+ visibility: {
354
+ billing: isBillingVisible,
355
+ additional: isAdditionalVisible,
356
+ },
357
+ }));
358
+ }, [isBillingVisible, isAdditionalVisible]);
359
+
360
+ const updateBillingInfo = (section, field, value) => {
361
+ setBillingInfo(prev => ({
362
+ ...prev,
363
+ [section]: {
364
+ ...prev[section],
365
+ [field]: value,
366
+ },
367
+ }));
177
368
  };
178
- setupIOS();
179
- }, [environment]);
180
369
 
370
+ const updateAndroidConfig = (field, value) => {
371
+ setAndroidConfig(prev => ({
372
+ ...prev,
373
+ [field]: value,
374
+ }));
375
+ };
376
+
377
+ const updateAndroidConfigFields = (section, index, field, value) => {
378
+ setAndroidConfig(prev => ({
379
+ ...prev,
380
+ fields: {
381
+ ...prev.fields,
382
+ [section]: prev.fields[section].map((item, i) =>
383
+ i === index ? { ...item, [field]: value } : item
384
+ ),
385
+ },
386
+ }));
387
+ };
388
+
389
+ const updateAndroidConfigAppearanceSettings = (field, value) => {
390
+ setAndroidConfig(prev => ({
391
+ ...prev,
392
+ appearanceSettings: {
393
+ ...prev.appearanceSettings,
394
+ [field]: value,
395
+ },
396
+ }));
397
+ };
398
+
399
+ const updateThemeConfiguration = (field, value) => {
400
+ setThemeConfiguration(prev => ({
401
+ ...prev,
402
+ [field]: value,
403
+ }));
404
+ };
405
+
406
+ const updateGrailPayParams = (field, value) => {
407
+ setGrailPayParams(prev => ({
408
+ ...prev,
409
+ [field]: value,
410
+ }));
411
+ };
412
+
413
+ const updateRecurringData = (field, value) => {
414
+ setRecurringData(prev => ({
415
+ ...prev,
416
+ [field]: value,
417
+ }));
418
+ };
419
+
420
+ const togglePaymentMethod = (method) => {
421
+ setAndroidConfig(prev => ({
422
+ ...prev,
423
+ paymentMethod: prev.paymentMethod.includes(method)
424
+ ? prev.paymentMethod.filter(m => m !== method)
425
+ : [...prev.paymentMethod, method],
426
+ }));
427
+ };
428
+
429
+ const toggleInterval = (interval) => {
430
+ setRecurringData(prev => ({
431
+ ...prev,
432
+ intervals: prev.intervals.includes(interval)
433
+ ? prev.intervals.filter(i => i !== interval)
434
+ : [...prev.intervals, interval],
435
+ }));
436
+ };
181
437
 
182
438
  const handlePayment = async () => {
183
439
  if (!amount || isNaN(parseFloat(amount)) || parseFloat(amount) <= 0) {
@@ -188,183 +444,98 @@ useEffect(() => {
188
444
  }
189
445
 
190
446
  setLoading(true);
191
-
192
447
  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
- }
448
+ await handleAndroidBilling();
280
449
  } else {
281
- handleBilling();
450
+ await handleIosBilling();
282
451
  }
283
452
  };
284
453
 
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,
454
+ const handleAndroidBilling = async () => {
455
+ const config = {
456
+ amount,
457
+ apiKey,
458
+ secretKey: apiSecret,
459
+ jsonConfig: {
460
+ environment,
461
+ amount,
462
+ tokenOnly: false,
463
+ currency: androidConfig.currency,
464
+ saveCard: androidConfig.saveCard,
465
+ saveAccount: androidConfig.saveAccount,
466
+ authenticatedACH: isAuthenticatedACH,
467
+ secureAuthentication: isSecureAuthentication,
468
+ showReceipt: androidConfig.showReceipt,
469
+ showDonate: androidConfig.showDonate,
470
+ showTotal: androidConfig.showTotal,
471
+ showSubmitButton: androidConfig.showSubmitButton,
472
+ paymentMethod: androidConfig.paymentMethod,
473
+ emailEditable,
474
+ email,
475
+ name: androidConfig.name,
476
+ fields: {
477
+ ...androidConfig.fields,
478
+ visibility: {
479
+ billing: isBillingVisible,
480
+ additional: isAdditionalVisible,
481
+ },
313
482
  },
314
- };
483
+ ...(isRecurring && {
484
+ recurring: {
485
+ enableRecurring: true,
486
+ recurringData,
487
+ },
488
+ }),
489
+ grailPayParams,
490
+ appearanceSettings: androidConfig.appearanceSettings,
491
+ },
492
+ };
315
493
 
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',
494
+ try {
495
+ const response = await RNEasymerchantsdk.makePayment(config);
496
+ console.log('Full payment response:', response);
497
+
498
+ const parsedResponse = {
499
+ ...response,
500
+ billingInfo: response.billingInfo ? JSON.parse(response.billingInfo) : null,
501
+ additional_info: response.additional_info ? JSON.parse(response.additional_info) : null,
341
502
  };
342
503
 
504
+ setResult(JSON.stringify(parsedResponse, null, 2));
505
+ } catch (error) {
506
+ setResult(`Error: ${error.message}`);
507
+ Alert.alert('Payment Error', error.message);
508
+ } finally {
509
+ setLoading(false);
510
+ }
511
+ };
512
+
513
+ const handleIosBilling = async () => {
343
514
  try {
344
515
  const result = await EasyMerchantSdk.billing(
345
516
  amount,
346
- 'usd',
517
+ androidConfig.currency || 'usd',
347
518
  billingInfo,
348
- ['card', 'bank'],
519
+ androidConfig.paymentMethod,
349
520
  themeConfiguration,
350
- false, // tokenOnly
351
- true, // saveCard
352
- true, // saveAccount
353
- isAuthenticatedACH, // authenticatedACH
354
- authConfig, // grailPayParams
521
+ false, // tokenOnly
522
+ androidConfig.saveCard,
523
+ androidConfig.saveAccount,
524
+ isAuthenticatedACH,
525
+ grailPayParams,
355
526
  '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
527
+ isRecurring,
528
+ isRecurring ? recurringData.allowCycles : 0,
529
+ isRecurring ? recurringData.intervals : [],
530
+ isRecurring ? recurringData.recurringStartType : '',
531
+ isRecurring ? recurringData.recurringStartDate : '',
532
+ isSecureAuthentication,
533
+ androidConfig.showReceipt,
534
+ androidConfig.showTotal,
535
+ androidConfig.showSubmitButton,
365
536
  isEmail,
366
537
  email,
367
- "Pavan"
538
+ androidConfig.name
368
539
  );
369
540
 
370
541
  const refToken = result?.additionalInfo?.threeDSecureStatus?.data?.ref_token;
@@ -372,38 +543,42 @@ useEffect(() => {
372
543
  setResult(JSON.stringify(result, null, 2));
373
544
  } catch (error) {
374
545
  console.error('Billing Error:', error);
375
- setResult(`❌ Billing Error: ${error.message || JSON.stringify(error)}`);
546
+ setResult(`Billing Error: ${error.message || JSON.stringify(error)}`);
547
+ } finally {
548
+ setLoading(false);
376
549
  }
377
550
  };
378
551
 
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
- };
552
+ const handleCheckStatus = async () => {
553
+ setLoading(true);
554
+ try {
555
+ const response = await RNEasymerchantsdk.checkPaymentStatus();
556
+ console.log('Full payment response:', response);
557
+ setResult(JSON.stringify(response, null, 2));
558
+ } catch (error) {
559
+ setResult(`Error: ${error.message}`);
560
+ Alert.alert('Status Check Error', error.message);
561
+ } finally {
562
+ setLoading(false);
563
+ }
564
+ };
392
565
 
393
566
  const handlePaymentReference = async () => {
394
567
  if (Platform.OS === 'android') {
395
- setResult('Payment Reference not supported on Android');
568
+ setResult('Payment Reference not supported on Android');
396
569
  return;
397
570
  }
398
571
  if (!referenceToken) {
399
- setResult('No reference token available from billing');
572
+ setResult('No reference token available');
400
573
  return;
401
574
  }
402
575
  try {
403
- const result = await EasyMerchantSdk.paymentReference(referenceToken);
404
- setResult(`✅ Payment Reference:\n${JSON.stringify(result, null, 2)}`);
576
+ const response = await EasyMerchantSdk.paymentReference(referenceToken);
577
+ setResult(`Payment Reference:\n${JSON.stringify(response, null, 2)}`);
405
578
  } catch (error) {
406
- setResult(`❌ Payment Reference Error: ${error.message || JSON.stringify(error)}`);
579
+ setResult(`Payment Reference Error: ${error.message || JSON.stringify(error)}`);
580
+ } finally {
581
+ setLoading(false);
407
582
  }
408
583
  };
409
584
 
@@ -412,8 +587,8 @@ useEffect(() => {
412
587
  <ScrollView contentContainerStyle={styles.scrollContent}>
413
588
  <Text style={styles.title}>EasyMerchant SDK</Text>
414
589
 
415
- <Text style={styles.label}>amount</Text>
416
-
590
+ <Text style={styles.sectionTitle}>Basic Info</Text>
591
+ <Text style={styles.label}>Amount</Text>
417
592
  <TextInput
418
593
  style={styles.input}
419
594
  placeholder="Enter amount (e.g., 10.00)"
@@ -421,9 +596,7 @@ useEffect(() => {
421
596
  value={amount}
422
597
  onChangeText={setAmount}
423
598
  />
424
-
425
- <Text style={styles.label}>Email</Text>
426
-
599
+ <Text style={styles.label}>Email</Text>
427
600
  <TextInput
428
601
  style={styles.input}
429
602
  placeholder="Enter email"
@@ -432,147 +605,598 @@ useEffect(() => {
432
605
  onChangeText={setEmail}
433
606
  />
434
607
 
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'}
608
+ <Text style={styles.sectionTitle}>Environment</Text>
609
+ <View style={styles.pickerContainer}>
610
+ <Text style={styles.label}>Select Environment</Text>
611
+ <View style={styles.buttonGroup}>
612
+ <TouchableOpacity
613
+ style={[
614
+ styles.environmentButton,
615
+ { backgroundColor: environment === 'sandbox' ? '#2563EB' : '#ccc' },
616
+ ]}
617
+ onPress={() => {
618
+ console.log('Sandbox tapped, setting environment to sandbox');
619
+ setEnvironment('sandbox');
620
+ }}
621
+ disabled={isEnvironmentLoading || environment === 'sandbox'}
622
+ >
623
+ <Text style={styles.buttonText}>Sandbox</Text>
624
+ </TouchableOpacity>
625
+ <TouchableOpacity
626
+ style={[
627
+ styles.environmentButton,
628
+ { backgroundColor: environment === 'staging' ? '#2563EB' : '#ccc' },
629
+ ]}
630
+ onPress={() => {
631
+ console.log('Staging tapped, setting environment to staging');
632
+ setEnvironment('staging');
633
+ }}
634
+ disabled={isEnvironmentLoading || environment === 'staging'}
635
+ >
636
+ <Text style={styles.buttonText}>Staging</Text>
637
+ </TouchableOpacity>
638
+ </View>
639
+ </View>
640
+
641
+
642
+ <Text style={styles.sectionTitle}>Payment Options</Text>
643
+ <View style={styles.toggleContainer}>
644
+ <Text style={styles.label}>Recurring Payment</Text>
645
+ <Switch
646
+ value={isRecurring}
647
+ onValueChange={setIsRecurring}
648
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
649
+ thumbColor={isRecurring ? '#fff' : '#f4f3f4'}
650
+ />
651
+ </View>
652
+ <View style={styles.toggleContainer}>
653
+ <Text style={styles.label}>Authenticated ACH</Text>
654
+ <Switch
655
+ value={isAuthenticatedACH}
656
+ onValueChange={setAuthenticatedACH}
657
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
658
+ thumbColor={isAuthenticatedACH ? '#fff' : '#f4f3f4'}
659
+ />
660
+ </View>
661
+ <View style={styles.toggleContainer}>
662
+ <Text style={styles.label}>3DS</Text>
663
+ <Switch
664
+ value={isSecureAuthentication}
665
+ onValueChange={setSecureAuthentication}
666
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
667
+ thumbColor={isSecureAuthentication ? '#fff' : '#f4f3f4'}
668
+ />
669
+ </View>
670
+ <View style={styles.toggleContainer}>
671
+ <Text style={styles.label}>Billing Visible</Text>
672
+ <Switch
673
+ value={isBillingVisible}
674
+ onValueChange={setBillingVisible}
675
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
676
+ thumbColor={isBillingVisible ? '#fff' : '#f4f3f4'}
677
+ />
678
+ </View>
679
+ <View style={styles.toggleContainer}>
680
+ <Text style={styles.label}>Additional Info Visible</Text>
681
+ <Switch
682
+ value={isAdditionalVisible}
683
+ onValueChange={setAdditionalVisible}
684
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
685
+ thumbColor={isAdditionalVisible ? '#fff' : '#f4f3f4'}
686
+ />
687
+ </View>
688
+ <Text style={styles.label}>Payment Methods (Shared)</Text>
689
+ <View style={styles.buttonGroup}>
690
+ <Button
691
+ title="Card"
692
+ onPress={() => togglePaymentMethod('card')}
693
+ color={androidConfig.paymentMethod.includes('card') ? '#2563EB' : '#ccc'}
694
+ />
695
+ <Button
696
+ title="ACH"
697
+ onPress={() => togglePaymentMethod('ach')}
698
+ color={androidConfig.paymentMethod.includes('ach') ? '#2563EB' : '#ccc'}
699
+ />
700
+ </View>
701
+ {Platform.OS === 'android' && (
702
+ <View style={styles.toggleContainer}>
703
+ <Text style={styles.label}>Email Editable (Android)</Text>
704
+ <Switch
705
+ value={emailEditable}
706
+ onValueChange={setEmailEditable}
707
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
708
+ thumbColor={emailEditable ? '#fff' : '#f4f3f4'}
442
709
  />
443
- <Button
444
- title="Staging"
445
- onPress={() => setEnvironment('staging')}
446
- color={environment === 'staging' ? '#2563EB' : '#ccc'}
710
+ </View>
711
+ )}
712
+ {Platform.OS === 'ios' && (
713
+ <View style={styles.toggleContainer}>
714
+ <Text style={styles.label}>Allow Email (iOS)</Text>
715
+ <Switch
716
+ value={isEmail}
717
+ onValueChange={setIsEmail}
718
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
719
+ thumbColor={isEmail ? '#fff' : '#f4f3f4'}
447
720
  />
448
721
  </View>
449
- </View>
722
+ )}
450
723
 
724
+ <Text style={styles.sectionTitle}>Billing Info</Text>
725
+ <Text style={styles.label}>Billing Address</Text>
726
+ <TextInput
727
+ style={styles.input}
728
+ value={billingInfo.billing.address}
729
+ onChangeText={value => updateBillingInfo('billing', 'address', value)}
730
+ />
731
+ <Text style={styles.label}>Billing Country</Text>
732
+ <TextInput
733
+ style={styles.input}
734
+ value={billingInfo.billing.country}
735
+ onChangeText={value => updateBillingInfo('billing', 'country', value)}
736
+ />
737
+ <Text style={styles.label}>Billing State</Text>
738
+ <TextInput
739
+ style={styles.input}
740
+ value={billingInfo.billing.state}
741
+ onChangeText={value => updateBillingInfo('billing', 'state', value)}
742
+ />
743
+ <Text style={styles.label}>Billing City</Text>
744
+ <TextInput
745
+ style={styles.input}
746
+ value={billingInfo.billing.city}
747
+ onChangeText={value => updateBillingInfo('billing', 'city', value)}
748
+ />
749
+ <Text style={styles.label}>Billing Postal Code</Text>
750
+ <TextInput
751
+ style={styles.input}
752
+ value={billingInfo.billing.postal_code}
753
+ onChangeText={value => updateBillingInfo('billing', 'postal_code', value)}
754
+ />
755
+ <Text style={styles.label}>Billing Required: Address</Text>
756
+ <Switch
757
+ value={billingInfo.billingRequired.address}
758
+ onValueChange={value => updateBillingInfo('billingRequired', 'address', value)}
759
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
760
+ thumbColor={billingInfo.billingRequired.address ? '#fff' : '#f4f3f4'}
761
+ />
762
+ <Text style={styles.label}>Billing Required: Country</Text>
763
+ <Switch
764
+ value={billingInfo.billingRequired.country}
765
+ onValueChange={value => updateBillingInfo('billingRequired', 'country', value)}
766
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
767
+ thumbColor={billingInfo.billingRequired.country ? '#fff' : '#f4f3f4'}
768
+ />
769
+ <Text style={styles.label}>Billing Required: State</Text>
770
+ <Switch
771
+ value={billingInfo.billingRequired.state}
772
+ onValueChange={value => updateBillingInfo('billingRequired', 'state', value)}
773
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
774
+ thumbColor={billingInfo.billingRequired.state ? '#fff' : '#f4f3f4'}
775
+ />
776
+ <Text style={styles.label}>Billing Required: City</Text>
777
+ <Switch
778
+ value={billingInfo.billingRequired.city}
779
+ onValueChange={value => updateBillingInfo('billingRequired', 'city', value)}
780
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
781
+ thumbColor={billingInfo.billingRequired.city ? '#fff' : '#f4f3f4'}
782
+ />
783
+ <Text style={styles.label}>Billing Required: Postal Code</Text>
784
+ <Switch
785
+ value={billingInfo.billingRequired.postal_code}
786
+ onValueChange={value => updateBillingInfo('billingRequired', 'postal_code', value)}
787
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
788
+ thumbColor={billingInfo.billingRequired.postal_code ? '#fff' : '#f4f3f4'}
789
+ />
790
+ <Text style={styles.label}>Additional: Name</Text>
791
+ <TextInput
792
+ style={styles.input}
793
+ value={billingInfo.additional.name}
794
+ onChangeText={value => updateBillingInfo('additional', 'name', value)}
795
+ />
796
+ <Text style={styles.label}>Additional: Email Address</Text>
797
+ <TextInput
798
+ style={styles.input}
799
+ value={billingInfo.additional.email_address}
800
+ onChangeText={value => updateBillingInfo('additional', 'email_address', value)}
801
+ />
802
+ <Text style={styles.label}>Additional: Phone Number</Text>
803
+ <TextInput
804
+ style={styles.input}
805
+ value={billingInfo.additional.phone_number}
806
+ onChangeText={value => updateBillingInfo('additional', 'phone_number', value)}
807
+ />
808
+ <Text style={styles.label}>Additional: Description</Text>
809
+ <TextInput
810
+ style={styles.input}
811
+ value={billingInfo.additional.description}
812
+ onChangeText={value => updateBillingInfo('additional', 'description', value)}
813
+ />
814
+ <Text style={styles.label}>Additional Required: Name</Text>
815
+ <Switch
816
+ value={billingInfo.additionalRequired.name}
817
+ onValueChange={value => updateBillingInfo('additionalRequired', 'name', value)}
818
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
819
+ thumbColor={billingInfo.additionalRequired.name ? '#fff' : '#f4f3f4'}
820
+ />
821
+ <Text style={styles.label}>Additional Required: Email Address</Text>
822
+ <Switch
823
+ value={billingInfo.additionalRequired.email_address}
824
+ onValueChange={value => updateBillingInfo('additionalRequired', 'email_address', value)}
825
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
826
+ thumbColor={billingInfo.additionalRequired.email_address ? '#fff' : '#f4f3f4'}
827
+ />
828
+ <Text style={styles.label}>Additional Required: Phone Number</Text>
829
+ <Switch
830
+ value={billingInfo.additionalRequired.phone_number}
831
+ onValueChange={value => updateBillingInfo('additionalRequired', 'phone_number', value)}
832
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
833
+ thumbColor={billingInfo.additionalRequired.phone_number ? '#fff' : '#f4f3f4'}
834
+ />
835
+ <Text style={styles.label}>Additional Required: Description</Text>
836
+ <Switch
837
+ value={billingInfo.additionalRequired.description}
838
+ onValueChange={value => updateBillingInfo('additionalRequired', 'description', value)}
839
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
840
+ thumbColor={billingInfo.additionalRequired.description ? '#fff' : '#f4f3f4'}
841
+ />
451
842
 
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'}
843
+ {Platform.OS === 'android' && (
844
+ <>
845
+ <Text style={styles.sectionTitle}>Android Configuration</Text>
846
+ <Text style={styles.label}>Currency</Text>
847
+ <TextInput
848
+ style={styles.input}
849
+ value={androidConfig.currency}
850
+ onChangeText={value => updateAndroidConfig('currency', value)}
495
851
  />
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'}
852
+ <Text style={styles.label}>Save Card</Text>
853
+ <Switch
854
+ value={androidConfig.saveCard}
855
+ onValueChange={value => updateAndroidConfig('saveCard', value)}
856
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
857
+ thumbColor={androidConfig.saveCard ? '#fff' : '#f4f3f4'}
506
858
  />
507
- <Button
508
- title="No"
509
- onPress={() => setAuthenticatedACH(false)}
510
- color={!isAuthenticatedACH ? '#2563EB' : '#ccc'}
859
+ <Text style={styles.label}>Save Account</Text>
860
+ <Switch
861
+ value={androidConfig.saveAccount}
862
+ onValueChange={value => updateAndroidConfig('saveAccount', value)}
863
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
864
+ thumbColor={androidConfig.saveAccount ? '#fff' : '#f4f3f4'}
511
865
  />
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'}
866
+ <Text style={styles.label}>Show Receipt</Text>
867
+ <Switch
868
+ value={androidConfig.showReceipt}
869
+ onValueChange={value => updateAndroidConfig('showReceipt', value)}
870
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
871
+ thumbColor={androidConfig.showReceipt ? '#fff' : '#f4f3f4'}
522
872
  />
523
- <Button
524
- title="No"
525
- onPress={() => setSecureAuthentication(false)}
526
- color={!isSecureAuthentication ? '#2563EB' : '#ccc'}
873
+ <Text style={styles.label}>Show Donate</Text>
874
+ <Switch
875
+ value={androidConfig.showDonate}
876
+ onValueChange={value => updateAndroidConfig('showDonate', value)}
877
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
878
+ thumbColor={androidConfig.showDonate ? '#fff' : '#f4f3f4'}
527
879
  />
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>
880
+ <Text style={styles.label}>Show Total</Text>
881
+ <Switch
882
+ value={androidConfig.showTotal}
883
+ onValueChange={value => updateAndroidConfig('showTotal', value)}
884
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
885
+ thumbColor={androidConfig.showTotal ? '#fff' : '#f4f3f4'}
886
+ />
887
+ <Text style={styles.label}>Show Submit Button</Text>
888
+ <Switch
889
+ value={androidConfig.showSubmitButton}
890
+ onValueChange={value => updateAndroidConfig('showSubmitButton', value)}
891
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
892
+ thumbColor={androidConfig.showSubmitButton ? '#fff' : '#f4f3f4'}
893
+ />
894
+ <Text style={styles.label}>Name</Text>
895
+ <TextInput
896
+ style={styles.input}
897
+ value={androidConfig.name}
898
+ onChangeText={value => updateAndroidConfig('name', value)}
899
+ />
900
+ <Text style={styles.sectionTitle}>Android Fields</Text>
901
+ {androidConfig.fields.billing.map((field, index) => (
902
+ <View key={`billing-${index}`}>
903
+ <Text style={styles.label}>{`Billing ${field.name}`}</Text>
904
+ <TextInput
905
+ style={styles.input}
906
+ value={field.value}
907
+ onChangeText={value => updateAndroidConfigFields('billing', index, 'value', value)}
908
+ />
909
+ <Text style={styles.label}>{`Billing ${field.name} Required`}</Text>
910
+ <Switch
911
+ value={field.required}
912
+ onValueChange={value => updateAndroidConfigFields('billing', index, 'required', value)}
913
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
914
+ thumbColor={field.required ? '#fff' : '#f4f3f4'}
915
+ />
916
+ </View>
917
+ ))}
918
+ {androidConfig.fields.additional.map((field, index) => (
919
+ <View key={`additional-${index}`}>
920
+ <Text style={styles.label}>{`Additional ${field.name}`}</Text>
921
+ <TextInput
922
+ style={styles.input}
923
+ value={field.value}
924
+ onChangeText={value => updateAndroidConfigFields('additional', index, 'value', value)}
925
+ />
926
+ <Text style={styles.label}>{`Additional ${field.name} Required`}</Text>
927
+ <Switch
928
+ value={field.required}
929
+ onValueChange={value => updateAndroidConfigFields('additional', index, 'required', value)}
930
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
931
+ thumbColor={field.required ? '#fff' : '#f4f3f4'}
932
+ />
933
+ </View>
934
+ ))}
935
+ <Text style={styles.sectionTitle}>Android Appearance Settings</Text>
936
+ <Text style={styles.label}>Theme</Text>
937
+ <TextInput
938
+ style={styles.input}
939
+ value={androidConfig.appearanceSettings.theme}
940
+ onChangeText={value => updateAndroidConfigAppearanceSettings('theme', value)}
941
+ />
942
+ <Text style={styles.label}>Body Background Color</Text>
943
+ <TextInput
944
+ style={styles.input}
945
+ value={androidConfig.appearanceSettings.bodyBackgroundColor}
946
+ onChangeText={value => updateAndroidConfigAppearanceSettings('bodyBackgroundColor', value)}
947
+ />
948
+ <Text style={styles.label}>Container Background Color</Text>
949
+ <TextInput
950
+ style={styles.input}
951
+ value={androidConfig.appearanceSettings.containerBackgroundColor}
952
+ onChangeText={value => updateAndroidConfigAppearanceSettings('containerBackgroundColor', value)}
953
+ />
954
+ <Text style={styles.label}>Primary Font Color</Text>
955
+ <TextInput
956
+ style={styles.input}
957
+ value={androidConfig.appearanceSettings.primaryFontColor}
958
+ onChangeText={value => updateAndroidConfigAppearanceSettings('primaryFontColor', value)}
959
+ />
960
+ <Text style={styles.label}>Secondary Font Color</Text>
961
+ <TextInput
962
+ style={styles.input}
963
+ value={androidConfig.appearanceSettings.secondaryFontColor}
964
+ onChangeText={value => updateAndroidConfigAppearanceSettings('secondaryFontColor', value)}
965
+ />
966
+ <Text style={styles.label}>Primary Button Background Color</Text>
967
+ <TextInput
968
+ style={styles.input}
969
+ value={androidConfig.appearanceSettings.primaryButtonBackgroundColor}
970
+ onChangeText={value => updateAndroidConfigAppearanceSettings('primaryButtonBackgroundColor', value)}
971
+ />
972
+ <Text style={styles.label}>Primary Button Hover Color</Text>
973
+ <TextInput
974
+ style={styles.input}
975
+ value={androidConfig.appearanceSettings.primaryButtonHoverColor}
976
+ onChangeText={value => updateAndroidConfigAppearanceSettings('primaryButtonHoverColor', value)}
977
+ />
978
+ <Text style={styles.label}>Primary Button Font Color</Text>
979
+ <TextInput
980
+ style={styles.input}
981
+ value={androidConfig.appearanceSettings.primaryButtonFontColor}
982
+ onChangeText={value => updateAndroidConfigAppearanceSettings('primaryButtonFontColor', value)}
983
+ />
984
+ <Text style={styles.label}>Secondary Button Background Color</Text>
985
+ <TextInput
986
+ style={styles.input}
987
+ value={androidConfig.appearanceSettings.secondaryButtonBackgroundColor}
988
+ onChangeText={value => updateAndroidConfigAppearanceSettings('secondaryButtonBackgroundColor', value)}
989
+ />
990
+ <Text style={styles.label}>Secondary Button Hover Color</Text>
991
+ <TextInput
992
+ style={styles.input}
993
+ value={androidConfig.appearanceSettings.secondaryButtonHoverColor}
994
+ onChangeText={value => updateAndroidConfigAppearanceSettings('secondaryButtonHoverColor', value)}
995
+ />
996
+ <Text style={styles.label}>Secondary Button Font Color</Text>
997
+ <TextInput
998
+ style={styles.input}
999
+ value={androidConfig.appearanceSettings.secondaryButtonFontColor}
1000
+ onChangeText={value => updateAndroidConfigAppearanceSettings('secondaryButtonFontColor', value)}
1001
+ />
1002
+ <Text style={styles.label}>Border Radius</Text>
1003
+ <TextInput
1004
+ style={styles.input}
1005
+ value={androidConfig.appearanceSettings.borderRadius}
1006
+ onChangeText={value => updateAndroidConfigAppearanceSettings('borderRadius', value)}
1007
+ />
1008
+ <Text style={styles.label}>Font Size</Text>
1009
+ <TextInput
1010
+ style={styles.input}
1011
+ value={androidConfig.appearanceSettings.fontSize}
1012
+ onChangeText={value => updateAndroidConfigAppearanceSettings('fontSize', value)}
1013
+ />
1014
+ <Text style={styles.label}>Font Weight</Text>
1015
+ <TextInput
1016
+ style={styles.input}
1017
+ value={String(androidConfig.appearanceSettings.fontWeight)}
1018
+ onChangeText={value => updateAndroidConfigAppearanceSettings('fontWeight', parseInt(value) || '500')}
1019
+ />
1020
+ <Text style={styles.label}>Font Family</Text>
1021
+ <TextInput
1022
+ style={styles.input}
1023
+ value={androidConfig.appearanceSettings.fontFamily}
1024
+ onChangeText={value => updateAndroidConfigAppearanceSettings('fontFamily', value)}
1025
+ />
1026
+ </>
563
1027
  )}
564
1028
 
1029
+ {Platform.OS === 'ios' && (
1030
+ <>
1031
+ <Text style={styles.sectionTitle}>Theme Configuration (iOS)</Text>
1032
+ <Text style={styles.label}>Body Background Color</Text>
1033
+ <TextInput
1034
+ style={styles.input}
1035
+ value={themeConfiguration.bodyBackgroundColor}
1036
+ onChangeText={value => updateThemeConfiguration('bodyBackgroundColor', value)}
1037
+ />
1038
+ <Text style={styles.label}>Container Background Color</Text>
1039
+ <TextInput
1040
+ style={styles.input}
1041
+ value={themeConfiguration.containerBackgroundColor}
1042
+ onChangeText={value => updateThemeConfiguration('containerBackgroundColor', value)}
1043
+ />
1044
+ <Text style={styles.label}>Primary Font Color</Text>
1045
+ <TextInput
1046
+ style={styles.input}
1047
+ value={themeConfiguration.primaryFontColor}
1048
+ onChangeText={value => updateThemeConfiguration('primaryFontColor', value)}
1049
+ />
1050
+ <Text style={styles.label}>Secondary Font Color</Text>
1051
+ <TextInput
1052
+ style={styles.input}
1053
+ value={themeConfiguration.secondaryFontColor}
1054
+ onChangeText={value => updateThemeConfiguration('secondaryFontColor', value)}
1055
+ />
1056
+ <Text style={styles.label}>Primary Button Background Color</Text>
1057
+ <TextInput
1058
+ style={styles.input}
1059
+ value={themeConfiguration.primaryButtonBackgroundColor}
1060
+ onChangeText={value => updateThemeConfiguration('primaryButtonBackgroundColor', value)}
1061
+ />
1062
+ <Text style={styles.label}>Primary Button Hover Color</Text>
1063
+ <TextInput
1064
+ style={styles.input}
1065
+ value={themeConfiguration.primaryButtonHoverColor}
1066
+ onChangeText={value => updateThemeConfiguration('primaryButtonHoverColor', value)}
1067
+ />
1068
+ <Text style={styles.label}>Primary Button Font Color</Text>
1069
+ <TextInput
1070
+ style={styles.input}
1071
+ value={themeConfiguration.primaryButtonFontColor}
1072
+ onChangeText={value => updateThemeConfiguration('primaryButtonFontColor', value)}
1073
+ />
1074
+ <Text style={styles.label}>Secondary Button Background Color</Text>
1075
+ <TextInput
1076
+ style={styles.input}
1077
+ value={themeConfiguration.secondaryButtonBackgroundColor}
1078
+ onChangeText={value => updateThemeConfiguration('secondaryButtonBackgroundColor', value)}
1079
+ />
1080
+ <Text style={styles.label}>Secondary Button Hover Color</Text>
1081
+ <TextInput
1082
+ style={styles.input}
1083
+ value={themeConfiguration.secondaryButtonHoverColor}
1084
+ onChangeText={value => updateThemeConfiguration('secondaryButtonHoverColor', value)}
1085
+ />
1086
+ <Text style={styles.label}>Secondary Button Font Color</Text>
1087
+ <TextInput
1088
+ style={styles.input}
1089
+ value={themeConfiguration.secondaryButtonFontColor}
1090
+ onChangeText={value => updateThemeConfiguration('secondaryButtonFontColor', value)}
1091
+ />
1092
+ <Text style={styles.label}>Border Radius</Text>
1093
+ <TextInput
1094
+ style={styles.input}
1095
+ value={themeConfiguration.borderRadius}
1096
+ onChangeText={value => updateThemeConfiguration('borderRadius', value)}
1097
+ />
1098
+ <Text style={styles.label}>Font Size</Text>
1099
+ <TextInput
1100
+ style={styles.input}
1101
+ value={themeConfiguration.fontSize}
1102
+ onChangeText={value => updateThemeConfiguration('fontSize', value)}
1103
+ />
1104
+ <Text style={styles.label}>Font Weight</Text>
1105
+ <TextInput
1106
+ style={styles.input}
1107
+ value={String(themeConfiguration.fontWeight)}
1108
+ onChangeText={value => updateThemeConfiguration('fontWeight', parseInt(value) || 500)}
1109
+ />
1110
+ <Text style={styles.label}>Font Family</Text>
1111
+ <TextInput
1112
+ style={styles.input}
1113
+ value={themeConfiguration.fontFamily}
1114
+ onChangeText={value => updateThemeConfiguration('fontFamily', value)}
1115
+ />
1116
+ </>
1117
+ )}
565
1118
 
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>
1119
+ <Text style={styles.sectionTitle}>GrailPay Parameters</Text>
1120
+ <Text style={styles.label}>Role</Text>
1121
+ <TextInput
1122
+ style={styles.input}
1123
+ value={grailPayParams.role}
1124
+ onChangeText={value => updateGrailPayParams('role', value)}
1125
+ />
1126
+ <Text style={styles.label}>Timeout</Text>
1127
+ <TextInput
1128
+ style={styles.input}
1129
+ value={String(grailPayParams.timeout)}
1130
+ onChangeText={value => updateGrailPayParams('timeout', parseInt(value) || 10)}
1131
+ />
1132
+ <Text style={styles.label}>Is Sandbox</Text>
1133
+ <Switch
1134
+ value={grailPayParams.isSandbox}
1135
+ onValueChange={value => updateGrailPayParams('isSandbox', value)}
1136
+ trackColor={{ false: '#ccc', true: '#2563EB' }}
1137
+ thumbColor={grailPayParams.isSandbox ? '#fff' : '#f4f3f4'}
1138
+ />
1139
+ <Text style={styles.label}>Branding Name</Text>
1140
+ <TextInput
1141
+ style={styles.input}
1142
+ value={grailPayParams.brandingName}
1143
+ onChangeText={value => updateGrailPayParams('brandingName', value)}
1144
+ />
1145
+ <Text style={styles.label}>Finder Subtitle</Text>
1146
+ <TextInput
1147
+ style={styles.input}
1148
+ value={grailPayParams.finderSubtitle}
1149
+ onChangeText={value => updateGrailPayParams('finderSubtitle', value)}
1150
+ />
1151
+ <Text style={styles.label}>Search Placeholder</Text>
1152
+ <TextInput
1153
+ style={styles.input}
1154
+ value={grailPayParams.searchPlaceholder}
1155
+ onChangeText={value => updateGrailPayParams('searchPlaceholder', value)}
1156
+ />
574
1157
 
1158
+ <Text style={styles.sectionTitle}>Recurring Data</Text>
1159
+ <Text style={styles.label}>Allow Cycles</Text>
1160
+ <TextInput
1161
+ style={styles.input}
1162
+ value={String(recurringData.allowCycles)}
1163
+ onChangeText={value => updateRecurringData('allowCycles', parseInt(value) || 2)}
1164
+ />
1165
+ <Text style={styles.label}>Intervals</Text>
1166
+ <View style={styles.buttonGroup}>
1167
+ <Button
1168
+ title="Weekly"
1169
+ onPress={() => toggleInterval('weekly')}
1170
+ color={recurringData.intervals.includes('weekly') ? '#2563EB' : '#ccc'}
1171
+ />
1172
+ <Button
1173
+ title="Monthly"
1174
+ onPress={() => toggleInterval('monthly')}
1175
+ color={recurringData.intervals.includes('monthly') ? '#2563EB' : '#ccc'}
1176
+ />
1177
+ </View>
1178
+ <Text style={styles.label}>Recurring Start Type</Text>
1179
+ <TextInput
1180
+ style={styles.input}
1181
+ value={recurringData.recurringStartType}
1182
+ onChangeText={value => updateRecurringData('recurringStartType', value)}
1183
+ />
1184
+ <Text style={styles.label}>Recurring Start Date</Text>
1185
+ <TextInput
1186
+ style={styles.input}
1187
+ value={recurringData.recurringStartDate}
1188
+ onChangeText={value => updateRecurringData('recurringStartDate', value)}
1189
+ />
575
1190
 
1191
+ <View style={styles.buttonGroup}>
1192
+ <Button title="Pay" onPress={handlePayment} />
1193
+ {Platform.OS === 'ios' ? (
1194
+ <Button title="Payment Ref" onPress={handlePaymentReference} disabled={loading} />
1195
+ ) : (
1196
+ <></>
1197
+ )
1198
+ }
1199
+ </View>
576
1200
 
577
1201
  <Text selectable style={styles.result}>{result}</Text>
578
1202
  </ScrollView>
@@ -592,29 +1216,62 @@ const styles = StyleSheet.create({
592
1216
  marginTop: 50,
593
1217
  textAlign: 'center',
594
1218
  },
1219
+ sectionTitle: {
1220
+ fontSize: 20,
1221
+ fontWeight: 'bold',
1222
+ marginTop: 20,
1223
+ marginBottom: 10,
1224
+ },
595
1225
  input: {
596
1226
  height: 40,
597
1227
  borderColor: '#ccc',
598
1228
  borderWidth: 1,
599
1229
  borderRadius: 5,
600
1230
  paddingHorizontal: 10,
601
- marginBottom: 20,
1231
+ marginBottom: 10,
602
1232
  },
603
1233
  pickerContainer: {
604
1234
  marginBottom: 20,
605
1235
  },
1236
+ toggleContainer: {
1237
+ flexDirection: 'row',
1238
+ justifyContent: 'space-between',
1239
+ alignItems: 'center',
1240
+ marginBottom: 10,
1241
+ },
606
1242
  label: {
607
1243
  fontSize: 16,
608
- marginBottom: 8,
609
1244
  fontWeight: '500',
1245
+ marginBottom: 5,
610
1246
  },
611
1247
  buttonGroup: {
612
1248
  flexDirection: 'row',
613
1249
  justifyContent: 'space-between',
614
1250
  marginBottom: 20,
1251
+ marginTop: 20,
615
1252
  gap: 10,
1253
+
1254
+ },
1255
+ result: {
1256
+ fontSize: 14,
1257
+ fontFamily: 'monospace',
1258
+ color: '#333',
1259
+ marginTop: 20,
1260
+ },
1261
+ environmentButton: {
1262
+ flex: 1,
1263
+ paddingVertical: 10,
1264
+ paddingHorizontal: 20,
1265
+ borderRadius: 5,
1266
+ marginHorizontal: 5,
1267
+ alignItems: 'center',
1268
+ justifyContent: 'center',
1269
+ },
1270
+ buttonText: {
1271
+ color: '#fff',
1272
+ fontSize: 16,
1273
+ fontWeight: '500',
616
1274
  },
617
- result: { fontSize: 14, fontFamily: 'monospace', color: '#333' },
618
1275
  });
619
1276
 
620
1277
  ```