@jimrising/easymerchantsdk-react-native 2.0.7 → 2.0.9

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 (121) hide show
  1. package/README.md +129 -1806
  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/eef2d06269ef2e204b4f065513034fca/transformed/classes/classes_dex/classes.dex +0 -0
  8. package/android/build/.transforms/ffabb40f9809b32eb9546ce76fc51764/transformed/bundleLibRuntimeToDirRelease/bundleLibRuntimeToDirRelease_dex/com/reactlibrary/RNEasymerchantsdkModule$1$1.dex +0 -0
  9. package/android/build/.transforms/ffabb40f9809b32eb9546ce76fc51764/transformed/bundleLibRuntimeToDirRelease/bundleLibRuntimeToDirRelease_dex/com/reactlibrary/RNEasymerchantsdkModule$1.dex +0 -0
  10. package/android/build/.transforms/ffabb40f9809b32eb9546ce76fc51764/transformed/bundleLibRuntimeToDirRelease/bundleLibRuntimeToDirRelease_dex/com/reactlibrary/RNEasymerchantsdkModule$2.dex +0 -0
  11. package/android/build/.transforms/ffabb40f9809b32eb9546ce76fc51764/transformed/bundleLibRuntimeToDirRelease/bundleLibRuntimeToDirRelease_dex/com/reactlibrary/RNEasymerchantsdkModule.dex +0 -0
  12. package/android/build/intermediates/aar_main_jar/release/syncReleaseLibJars/classes.jar +0 -0
  13. package/android/build/intermediates/compile_library_classes_jar/debug/bundleLibCompileToJarDebug/classes.jar +0 -0
  14. package/android/build/intermediates/compile_library_classes_jar/release/bundleLibCompileToJarRelease/classes.jar +0 -0
  15. package/android/build/intermediates/full_jar/release/createFullJarRelease/full.jar +0 -0
  16. package/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +1 -1
  17. package/android/build/intermediates/incremental/lintVitalAnalyzeRelease/release-artifact-dependencies.xml +4 -4
  18. package/android/build/intermediates/incremental/lintVitalAnalyzeRelease/release-artifact-libraries.xml +4 -4
  19. package/android/build/intermediates/incremental/release/mergeReleaseResources/compile-file-map.properties +838 -838
  20. package/android/build/intermediates/incremental/release/mergeReleaseResources/merger.xml +3 -3
  21. package/android/build/intermediates/incremental/release/packageReleaseResources/compile-file-map.properties +1 -1
  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 -3
  31. package/android/build/intermediates/lint-cache/lintVitalAnalyzeRelease/maven.google/androidx/lifecycle/group-index.xml +165 -165
  32. package/android/build/intermediates/lint-cache/lintVitalAnalyzeRelease/maven.google/com/android/tools/build/group-index.xml +15 -15
  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 +1 -0
  40. package/android/build/intermediates/merged_res/release/mergeReleaseResources/layout/activity_card_billing_info.xml +1 -0
  41. package/android/build/intermediates/merged_res/release/mergeReleaseResources/layout/activity_card_scan.xml +1 -0
  42. package/android/build/intermediates/merged_res/release/mergeReleaseResources/layout/activity_payment_done.xml +1 -0
  43. package/android/build/intermediates/merged_res/release/mergeReleaseResources/layout/activity_payment_done_url.xml +1 -0
  44. package/android/build/intermediates/merged_res/release/mergeReleaseResources/layout/activity_payment_error.xml +1 -0
  45. package/android/build/intermediates/merged_res/release/mergeReleaseResources/layout/activity_select_payment_method.xml +1 -0
  46. package/android/build/intermediates/merged_res/release/mergeReleaseResources/layout/activity_verify_email.xml +1 -0
  47. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/multi-v2/values-night-v8.json +19 -19
  48. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/multi-v2/values.json +38 -38
  49. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/anim-v21.json +9 -9
  50. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/anim.json +24 -24
  51. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/animator-v21.json +1 -1
  52. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/animator.json +34 -34
  53. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/color-night-v8.json +3 -3
  54. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/color-v31.json +10 -10
  55. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/color.json +153 -153
  56. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/drawable-hdpi-v4.json +1 -1
  57. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/drawable-mdpi-v4.json +1 -1
  58. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/drawable-v21.json +3 -3
  59. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/drawable-v23.json +7 -7
  60. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/drawable-xhdpi-v4.json +1 -1
  61. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/drawable-xxhdpi-v4.json +1 -1
  62. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/drawable-xxxhdpi-v4.json +1 -1
  63. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/drawable.json +391 -391
  64. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/font.json +9 -9
  65. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/interpolator-v21.json +10 -10
  66. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/interpolator.json +11 -11
  67. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/layout-land.json +3 -3
  68. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/layout-sw600dp-v13.json +2 -2
  69. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/layout-v26.json +1 -1
  70. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/layout.json +98 -98
  71. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/mipmap-anydpi-v26.json +2 -2
  72. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/mipmap-hdpi-v4.json +2 -2
  73. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/mipmap-mdpi-v4.json +2 -2
  74. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/mipmap-xhdpi-v4.json +2 -2
  75. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/mipmap-xxhdpi-v4.json +2 -2
  76. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/mipmap-xxxhdpi-v4.json +2 -2
  77. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/raw.json +46 -46
  78. package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/xml.json +3 -3
  79. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/reactlibrary/RNEasymerchantsdkModule$1$1.class +0 -0
  80. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/reactlibrary/RNEasymerchantsdkModule$1.class +0 -0
  81. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/reactlibrary/RNEasymerchantsdkModule$2.class +0 -0
  82. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/reactlibrary/RNEasymerchantsdkModule.class +0 -0
  83. package/android/build/intermediates/runtime_library_classes_dir/release/bundleLibRuntimeToDirRelease/com/reactlibrary/RNEasymerchantsdkModule$1$1.class +0 -0
  84. package/android/build/intermediates/runtime_library_classes_dir/release/bundleLibRuntimeToDirRelease/com/reactlibrary/RNEasymerchantsdkModule$1.class +0 -0
  85. package/android/build/intermediates/runtime_library_classes_dir/release/bundleLibRuntimeToDirRelease/com/reactlibrary/RNEasymerchantsdkModule$2.class +0 -0
  86. package/android/build/intermediates/runtime_library_classes_dir/release/bundleLibRuntimeToDirRelease/com/reactlibrary/RNEasymerchantsdkModule.class +0 -0
  87. package/android/build/intermediates/runtime_library_classes_jar/debug/bundleLibRuntimeToJarDebug/classes.jar +0 -0
  88. package/android/build/intermediates/runtime_library_classes_jar/release/bundleLibRuntimeToJarRelease/classes.jar +0 -0
  89. package/android/build/intermediates/source_set_path_map/release/mapReleaseSourceSetPaths/file-map.txt +24 -24
  90. package/android/build/intermediates/verified_library_resources/release/verifyReleaseResources/compiled/layout_activity_card_addition_ifo.xml.flat +0 -0
  91. package/android/build/intermediates/verified_library_resources/release/verifyReleaseResources/compiled/layout_activity_card_billing_info.xml.flat +0 -0
  92. package/android/build/intermediates/verified_library_resources/release/verifyReleaseResources/compiled/layout_activity_card_scan.xml.flat +0 -0
  93. package/android/build/intermediates/verified_library_resources/release/verifyReleaseResources/compiled/layout_activity_payment_done.xml.flat +0 -0
  94. package/android/build/intermediates/verified_library_resources/release/verifyReleaseResources/compiled/layout_activity_payment_done_url.xml.flat +0 -0
  95. package/android/build/intermediates/verified_library_resources/release/verifyReleaseResources/compiled/layout_activity_payment_error.xml.flat +0 -0
  96. package/android/build/intermediates/verified_library_resources/release/verifyReleaseResources/compiled/layout_activity_select_payment_method.xml.flat +0 -0
  97. package/android/build/intermediates/verified_library_resources/release/verifyReleaseResources/compiled/layout_activity_verify_email.xml.flat +0 -0
  98. package/android/build/outputs/aar/jimrising_easymerchantsdk-react-native-release.aar +0 -0
  99. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/RNEasymerchantsdkModule$1$1.class.uniqueId1 +0 -0
  100. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/RNEasymerchantsdkModule$1.class.uniqueId0 +0 -0
  101. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/RNEasymerchantsdkModule$2.class.uniqueId3 +0 -0
  102. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/RNEasymerchantsdkModule.class.uniqueId2 +0 -0
  103. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/RNEasymerchantsdkPackage.class.uniqueId4 +0 -0
  104. package/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin +0 -0
  105. package/android/build/tmp/compileReleaseJavaWithJavac/previous-compilation-data.bin +0 -0
  106. package/android/build.gradle +1 -1
  107. package/android/src/main/java/com/reactlibrary/RNEasymerchantsdkModule.java +93 -26
  108. package/ios/Classes/EasyMerchantSdk.m +4 -4
  109. package/ios/Classes/EasyMerchantSdk.swift +116 -60
  110. package/ios/Classes/EasyPayViewController.swift +3 -5
  111. package/ios/CustomComponents/CheckboxButton.swift +66 -0
  112. package/ios/Helper/GrailPayHelper.swift +1 -0
  113. package/ios/Models/Request.swift +8 -8
  114. package/ios/Pods/ViewControllers/AdditionalInfoVC.swift +233 -97
  115. package/ios/Pods/ViewControllers/BillingInfoVC/BillingInfoVC.swift +376 -104
  116. package/ios/Pods/ViewControllers/OTPVerificationVC.swift +28 -27
  117. package/ios/Pods/ViewControllers/PaymentErrorVC.swift +22 -25
  118. package/ios/Pods/ViewControllers/PaymentInformation/PaymentInfoVC.swift +575 -190
  119. package/ios/easymerchantsdk.podspec +1 -1
  120. package/ios/easymerchantsdk.storyboard +15 -15
  121. package/package.json +1 -1
package/README.md CHANGED
@@ -1,41 +1,62 @@
1
1
  # EasyMerchantSdk React Native Implementation
2
2
 
3
- To implement the EasyMerchantSdk in your React Native App project. Follow the below steps:
3
+ This guide provides step-by-step instructions to integrate the EasyMerchantSdk into your React Native application for both Android and iOS platforms.
4
4
 
5
- ## Add the sdk path in your project.
6
- To add the path of sdk in your project. Open your `package.json` file and inside the `dependencies` section, add the below code and set the path of the sdk where you store on your disk.
5
+ ## Prerequisites
6
+
7
+ - Node.js and npm installed
8
+ - React Native development environment set up
9
+ - Ruby 3.2.8 (for iOS setup)
10
+ - Xcode (for iOS development)
11
+ - Android Studio (for Android development)
12
+
13
+ ## Installation
14
+
15
+ ### 1. Add the SDK to Your Project
16
+
17
+ Add the EasyMerchantSdk to your project by including it in your `package.json` file under the `dependencies` section:
7
18
 
8
19
  ```json
9
20
  "dependencies": {
10
- "@jimrising/easymerchantsdk-react-native": "^2.0.7"
11
- },
21
+ "@jimrising/easymerchantsdk-react-native": "^2.0.9"
22
+ }
12
23
  ```
13
24
 
14
- or using command
15
- ```cmd
16
- npm i @jimrising/easymerchantsdk-react-native
25
+ Alternatively, install it using the following command:
26
+
27
+ ```bash
28
+ npm install @jimrising/easymerchantsdk-react-native
17
29
  ```
18
30
 
19
- ## Changes in android side.
20
- Now open your `android` folder and there is a `build.gradle` file. Open it and add the below code in it.
31
+ ### 2. Android Configuration
32
+
33
+ 1. Open the `android/build.gradle` file in your project.
34
+ 2. Add the following code to the `allprojects.repositories` section to ensure the SDK can access required dependencies:
35
+
21
36
  ```gradle
22
37
  allprojects {
23
38
  repositories {
24
39
  google()
25
40
  mavenCentral()
26
-
41
+ maven { url 'https://jitpack.io' }
42
+ maven {
43
+ url = uri(properties.getProperty('GITHUB_URL'))
44
+ credentials {
45
+ username = properties.getProperty('GITHUB_USERNAME')
46
+ password = properties.getProperty('GITHUB_PASSWORD')
47
+ }
48
+ }
27
49
  }
28
50
  }
29
51
  ```
30
52
 
31
- ## Changes in IOS side.
32
- Add below content inside the AppDelegate.swift File :-
53
+ 3. Sync your project with Gradle to apply the changes.
33
54
 
34
- ## Requirements
35
- - Ruby 3.2.8
55
+ ### 3. iOS Configuration
36
56
 
57
+ 1. **Update AppDelegate.swift**
58
+ Create or modify the `AppDelegate.swift` file in your iOS project to include the following code:
37
59
 
38
- Create a new file named AppDelegate.swift
39
60
  ```swift
40
61
  import UIKit
41
62
  import easymerchantsdk
@@ -43,7 +64,7 @@ import React
43
64
 
44
65
  @UIApplicationMain
45
66
  class AppDelegate: UIResponder, UIApplicationDelegate {
46
- var window: UIWindow?
67
+ var window: UIWindow?
47
68
 
48
69
  func application(
49
70
  _ application: UIApplication,
@@ -69,7 +90,7 @@ var window: UIWindow?
69
90
 
70
91
  let rootView = RCTRootView(
71
92
  bridge: validBridge,
72
- moduleName: "EasyMerchantTestApp", // replace it with your app name
93
+ moduleName: "EasyMerchantTestApp", // Replace with your app name
73
94
  initialProperties: nil
74
95
  )
75
96
 
@@ -78,8 +99,8 @@ var window: UIWindow?
78
99
  rootViewController.view = rootView
79
100
  self.window?.rootViewController = rootViewController
80
101
  self.window?.makeKeyAndVisible()
81
-
82
- if let easyMerchantSdkPlugin = bridge?.module(for: EasyMerchantSdkPlugin.self) as? EasyMerchantSdkPlugin {
102
+
103
+ if let easyMerchantSdkPlugin = bridge?.module(for: EasyMerchantSdkPlugin.self) as? EasyMerchantSdkPlugin {
83
104
  easyMerchantSdkPlugin.setViewController(rootViewController)
84
105
  } else {
85
106
  print("Failed to retrieve EasyMerchantSdkPlugin instance from React Native bridge.")
@@ -89,154 +110,43 @@ var window: UIWindow?
89
110
  }
90
111
  ```
91
112
 
92
- inside the PodFile add below
93
- ```pod
113
+ 2. **Update Podfile**
114
+ Open your `ios/Podfile` and add the following to include the EasyMerchantSdk pod:
94
115
 
95
- require_relative '../node_modules/react-native/scripts/react_native_pods'
96
- require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
116
+ ```ruby
117
+ require_relative '../node_modules/react-native/scripts/react_native_pods'
118
+ require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
97
119
 
98
- platform :ios, 16.0
99
-
100
- pod 'easymerchantsdk', :path => '../node_modules/easymerchantsdk-react-native/ios'
120
+ platform :ios, '16.0'
121
+
122
+ pod 'easymerchantsdk', :path => '../node_modules/easymerchantsdk-react-native/ios'
101
123
  ```
102
124
 
125
+ 3. Run `pod install` in the `ios` directory to install the dependencies:
103
126
 
127
+ ```bash
128
+ cd ios
129
+ pod install
130
+ ```
104
131
 
105
- ## How to call the sdk in `App.js`.
106
- you can call the sdk using below example:
132
+ ## Usage in Your React Native App
107
133
 
134
+ To integrate the EasyMerchantSdk into your React Native application, you can use the provided `App.js` as a reference. Below is a summary of how to call the SDK in your `App.js`:
108
135
 
109
- ```javascript
136
+ 1. **Import Necessary Modules**
137
+ Ensure you import the required React Native components and the EasyMerchantSdk NativeModules:
110
138
 
139
+ ```javascript
111
140
  import React, { useState, useEffect } from 'react';
112
- import {
113
- StyleSheet,
114
- Text,
115
- View,
116
- TextInput,
117
- Button,
118
- Alert,
119
- ScrollView,
120
- Platform,
121
- NativeModules,
122
- Switch,
123
- TouchableOpacity,
124
- NativeEventEmitter,
125
- KeyboardAvoidingView,
126
- Keyboard,
127
- TouchableWithoutFeedback,
128
- Modal,
129
- } from 'react-native';
141
+ import { StyleSheet, Text, View, TextInput, Button, Alert, ScrollView, Platform, NativeModules, Switch, TouchableOpacity, NativeEventEmitter, KeyboardAvoidingView, Keyboard, TouchableWithoutFeedback, Modal } from 'react-native';
130
142
 
131
143
  const { RNEasymerchantsdk, EasyMerchantSdk } = NativeModules;
144
+ ```
132
145
 
133
- // Dropdown Component
134
- const Dropdown = ({ value, onValueChange, options, placeholder }) => {
135
- const [isOpen, setIsOpen] = useState(false);
136
-
137
- // Handler to close modal when tapping outside
138
- const handleClose = () => setIsOpen(false);
139
-
140
- return (
141
- <View style={styles.dropdownContainer} pointerEvents="box-none">
142
- <TouchableOpacity
143
- style={styles.dropdownButton}
144
- onPress={() => setIsOpen(!isOpen)}
145
- >
146
- <Text style={styles.dropdownButtonText}>
147
- {value || placeholder}
148
- </Text>
149
- <Text style={styles.dropdownArrow}>
150
- {isOpen ? '▲' : '▼'}
151
- </Text>
152
- </TouchableOpacity>
153
- {Platform.OS === 'android' ? (
154
- <Modal
155
- visible={isOpen}
156
- transparent={true}
157
- animationType="fade"
158
- onRequestClose={handleClose}
159
- >
160
- <TouchableWithoutFeedback onPress={handleClose}>
161
- <View style={{ flex: 1, backgroundColor: 'rgba(0,0,0,0.2)' }}>
162
- <View style={[styles.dropdownOptions, { position: 'absolute', top: '30%', left: 20, right: 20, maxHeight: 300, elevation: 20 }]}>
163
- <ScrollView
164
- style={[styles.dropdownScrollView, { maxHeight: 280 }]}
165
- showsVerticalScrollIndicator={true}
166
- persistentScrollbar={true}
167
- nestedScrollEnabled={true}
168
- keyboardShouldPersistTaps="handled"
169
- >
170
- {options.map((option, index) => (
171
- <TouchableOpacity
172
- key={index}
173
- style={styles.dropdownOption}
174
- onPress={() => {
175
- onValueChange(option);
176
- setIsOpen(false);
177
- }}
178
- >
179
- <Text style={styles.dropdownOptionText}>{option}</Text>
180
- </TouchableOpacity>
181
- ))}
182
- </ScrollView>
183
- </View>
184
- </View>
185
- </TouchableWithoutFeedback>
186
- </Modal>
187
- ) : (
188
- isOpen && (
189
- <View style={styles.dropdownOptions}>
190
- <ScrollView
191
- style={styles.dropdownScrollView}
192
- showsVerticalScrollIndicator={true}
193
- persistentScrollbar={true}
194
- nestedScrollEnabled={true}
195
- keyboardShouldPersistTaps="handled"
196
- >
197
- {options.map((option, index) => (
198
- <TouchableOpacity
199
- key={index}
200
- style={styles.dropdownOption}
201
- onPress={() => {
202
- onValueChange(option);
203
- setIsOpen(false);
204
- }}
205
- >
206
- <Text style={styles.dropdownOptionText}>{option}</Text>
207
- </TouchableOpacity>
208
- ))}
209
- </ScrollView>
210
- </View>
211
- )
212
- )}
213
- </View>
214
- );
215
- };
216
-
217
- // Custom Filled Button Component
218
- const FilledButton = ({ title, onPress, disabled = false, style = {}, textStyle = {} }) => {
219
- return (
220
- <TouchableOpacity
221
- style={[
222
- styles.filledButton,
223
- disabled && styles.filledButtonDisabled,
224
- style,
225
- ]}
226
- onPress={onPress}
227
- disabled={disabled}
228
- >
229
- <Text style={[
230
- styles.filledButtonText,
231
- disabled && styles.filledButtonTextDisabled,
232
- textStyle,
233
- ]}>
234
- {title}
235
- </Text>
236
- </TouchableOpacity>
237
- );
238
- };
146
+ 2. **Set Up Configuration**
147
+ Define the configuration object for the SDK, including payment details, theme settings, and other parameters. The provided `App.js` includes a comprehensive `externalConfig` object that you can customize:
239
148
 
149
+ ```javascript
240
150
  const externalConfig = {
241
151
  amount: '',
242
152
  email: '',
@@ -250,11 +160,11 @@ const externalConfig = {
250
160
  billingInfo: {
251
161
  visibility: { billing: false, additional: false },
252
162
  billing: {
253
- address: 'San Fran, Punjab',
254
- country: 'USA',
255
- state: 'California',
256
- city: 'Paris',
257
- postal_code: '234234',
163
+ address: 'Address',
164
+ country: 'Country',
165
+ state: 'State',
166
+ city: 'City',
167
+ postal_code: '000000',
258
168
  },
259
169
  billingRequired: {
260
170
  address: true,
@@ -276,20 +186,20 @@ const externalConfig = {
276
186
  description: false,
277
187
  },
278
188
  },
279
- themeConfiguration: {
280
- bodyBackgroundColor: '#1E3A8A',
281
- containerBackgroundColor: '#1E40AF',
282
- primaryFontColor: '#FFFFFF',
283
- secondaryFontColor: '#BFDBFE',
284
- primaryButtonBackgroundColor: '#3B82F6',
285
- primaryButtonHoverColor: '#2563EB',
286
- primaryButtonFontColor: '#FFFFFF',
287
- secondaryButtonBackgroundColor: '#1D4ED8',
288
- secondaryButtonHoverColor: '#1E40AF',
289
- secondaryButtonFontColor: '#FFFFFF',
290
- borderRadius: '8',
291
- fontSize: '14',
292
- },
189
+ themeConfiguration: {
190
+ bodyBackgroundColor: '#1E3A8A',
191
+ containerBackgroundColor: '#1E40AF',
192
+ primaryFontColor: '#FFFFFF',
193
+ secondaryFontColor: '#BFDBFE',
194
+ primaryButtonBackgroundColor: '#3B82F6',
195
+ primaryButtonHoverColor: '#2563EB',
196
+ primaryButtonFontColor: '#FFFFFF',
197
+ secondaryButtonBackgroundColor: '#1D4ED8',
198
+ secondaryButtonHoverColor: '#1E40AF',
199
+ secondaryButtonFontColor: '#FFFFFF',
200
+ borderRadius: '8',
201
+ fontSize: '14',
202
+ },
293
203
  grailPayParams: {
294
204
  role: 'business',
295
205
  timeout: 10,
@@ -301,7 +211,7 @@ const externalConfig = {
301
211
  recurringData: {
302
212
  allowCycles: 2,
303
213
  intervals: ['daily', 'weekly', 'monthly'],
304
- recurringStartType: Platform.OS === 'android' ? ['Custom', 'Fixed'] : ['custom', 'fixed'],
214
+ recurringStartType: 'custom',
305
215
  recurringStartDate: new Date().toLocaleDateString('en-US', { month: '2-digit', day: '2-digit', year: 'numeric' }),
306
216
  },
307
217
  androidConfig: {
@@ -349,139 +259,56 @@ const externalConfig = {
349
259
  },
350
260
  },
351
261
  };
262
+ ```
352
263
 
353
- const App = () => {
354
- const [amount, setAmount] = useState(externalConfig.amount);
355
- const [email, setEmail] = useState(externalConfig.email);
356
- const [name, setName] = useState('');
357
- const [environment, setEnvironment] = useState('sandbox');
358
- const [isRecurring, setIsRecurring] = useState(externalConfig.isRecurring);
359
- const [isAuthenticatedACH, setAuthenticatedACH] = useState(externalConfig.isAuthenticatedACH);
360
- const [isSecureAuthentication, setSecureAuthentication] = useState(externalConfig.isSecureAuthentication);
361
- const [isBillingVisible, setBillingVisible] = useState(externalConfig.isBillingVisible);
362
- const [isAdditionalVisible, setAdditionalVisible] = useState(externalConfig.isAdditionalVisible);
363
- const [emailEditable, setEmailEditable] = useState(externalConfig.emailEditable); // Android
364
- const [isEmail, setIsEmail] = useState(externalConfig.isEmail); // iOS
365
- const [billingInfo, setBillingInfo] = useState(externalConfig.billingInfo);
366
- const [themeConfiguration, setThemeConfiguration] = useState(externalConfig.themeConfiguration);
367
- const [grailPayParams, setGrailPayParams] = useState(externalConfig.grailPayParams);
368
- const [recurringData, setRecurringData] = useState(externalConfig.recurringData);
369
- const [androidConfig, setAndroidConfig] = useState(externalConfig.androidConfig);
370
- const [result, setResult] = useState('');
371
- const [referenceToken, setReferenceToken] = useState('');
372
- const [loading, setLoading] = useState(false);
373
- const [showSecretKey, setShowSecretKey] = useState(false);
374
- const metadata = {
375
- metaKey: 'metaValue',
376
- metaKLey1: 'metaValue1',
377
- metaKLey2: 'metaValue2',
378
- metaKLey3: 'metaValue3',
379
- metaKLey4: 'metaValue4',
380
- };
381
-
382
- const [apiKeys, setApiKeys] = useState({
383
- sandbox: {
384
- apiKey: 'sandboxApiKey',
385
- secretKey: 'sandboxSecretKey',
386
- },
387
- staging: {
388
- apiKey: 'stagingApiKey',
389
- secretKey: 'stagingSecretKey',
390
- },
391
- });
392
- const [isEnvironmentLoading, setIsEnvironmentLoading] = useState(false);
393
- const [showConfig, setShowConfig] = useState(true);
394
-
395
- // Debug useEffect to track payment method changes
396
- useEffect(() => {
397
- console.log('Payment methods state changed:', androidConfig.paymentMethod);
398
- }, [androidConfig.paymentMethod]);
399
-
400
- // Debug useEffect to track theme configuration changes
401
- useEffect(() => {
402
- console.log('Theme configuration changed:', themeConfiguration);
403
- }, [themeConfiguration]);
264
+ 3. **Handle Payments**
265
+ Use the `handlePayment` function to initiate payments. This function validates inputs and calls platform-specific billing functions (`handleAndroidBilling` for Android and `handleIosBilling` for iOS). Example:
404
266
 
405
- // Debug useEffect to track Android appearance settings changes
406
- useEffect(() => {
407
- console.log('Android appearance settings changed:', androidConfig.appearanceSettings);
408
- }, [androidConfig.appearanceSettings]);
267
+ ```javascript
268
+ const handlePayment = async () => {
269
+ if (!amount || isNaN(parseFloat(amount)) || parseFloat(amount) <= 0) {
270
+ return Alert.alert('Error', 'Please enter a valid amount');
271
+ }
409
272
 
273
+ if (isRecurring && (!recurringData.intervals || recurringData.intervals.length === 0)) {
274
+ return Alert.alert('Error', 'Please select at least one interval for recurring payment');
275
+ }
410
276
 
411
- useEffect(() => {
412
- const updateEnvironment = async () => {
413
- const { apiKey, secretKey } = apiKeys[environment];
277
+ setLoading(true);
414
278
 
415
- if (Platform.OS === 'ios') {
416
- setIsEnvironmentLoading(true);
417
- try {
418
- await EasyMerchantSdk.setViewController();
419
- await EasyMerchantSdk.configureEnvironment(environment, apiKey, secretKey);
420
- console.log(`iOS Environment configured: ${environment} with key ${apiKey}`);
421
- } catch (err) {
422
- console.error('iOS Initialization Error:', err);
423
- Alert.alert('Error', `Failed to configure iOS environment: ${err.message}`);
424
- } finally {
425
- setIsEnvironmentLoading(false);
426
- }
427
- }
428
- };
279
+ try {
280
+ if (Platform.OS === 'android') {
281
+ await handleAndroidBilling();
282
+ } else {
283
+ await handleIosBilling();
284
+ }
285
+ } finally {
286
+ setLoading(false);
287
+ }
288
+ };
289
+ ```
429
290
 
430
- updateEnvironment();
431
- }, [environment, apiKeys]);
291
+ 4. **Event Listeners for Android**
292
+ Set up event listeners for payment success, status, and errors using `NativeEventEmitter`:
432
293
 
294
+ ```javascript
433
295
  useEffect(() => {
434
- if (Platform.OS !== 'android') {
435
- console.log('Event listeners skipped: not Android');
436
- return;
437
- }
438
-
439
- if (!RNEasymerchantsdk) {
440
- console.warn('RNEasymerchantsdk native module is not available.');
441
- return;
442
- }
296
+ if (Platform.OS !== 'android') return;
443
297
 
444
298
  const easyMerchantEvents = new NativeEventEmitter(RNEasymerchantsdk);
445
299
 
446
300
  const successSub = easyMerchantEvents.addListener('PaymentSuccess', (data) => {
447
- console.log('Payment success event:', data);
448
-
449
- try {
450
- const parsed = JSON.parse(data.response);
451
- console.log('Parsed PaymentSuccess:', parsed);
452
-
453
- const billing = safeParseMaybeJSON(parsed.billingInfo);
454
- const additional = safeParseMaybeJSON(parsed.additional_info);
455
-
456
- console.log('Billing Info:', billing);
457
- console.log('Additional Info:', additional);
458
-
459
- // Replace raw with parsed versions
460
- parsed.billingInfo = billing;
461
- parsed.additional_info = additional;
462
-
463
- setResult(JSON.stringify(parsed, null, 2));
464
- } catch (err) {
465
- console.error('Error parsing PaymentSuccess response:', err);
466
- }
301
+ const parsed = JSON.parse(data.response);
302
+ setResult(JSON.stringify(parsed, null, 2));
467
303
  });
468
304
 
469
305
  const statusSub = easyMerchantEvents.addListener('PaymentStatus', (data) => {
470
- console.log('Raw Payment status event:', data);
471
-
472
- try {
473
- const parsed = JSON.parse(data.statusResponse);
474
- console.log('Parsed PaymentStatus:', parsed);
475
-
476
- setResult(JSON.stringify(parsed, null, 2));
477
- } catch (err) {
478
- console.error('Error parsing PaymentStatus response:', err);
479
- }
306
+ const parsed = JSON.parse(data.statusResponse);
307
+ setResult(JSON.stringify(parsed, null, 2));
480
308
  });
481
309
 
482
310
  const statusErrorSub = easyMerchantEvents.addListener('PaymentStatusError', (data) => {
483
- console.log('Payment status error event:', data);
484
- setResult(`Status Error: ${JSON.stringify(data)}`);
311
+ setResult(`Status Error: ${JSON.stringify(data.error, null, 2)}`);
485
312
  });
486
313
 
487
314
  return () => {
@@ -490,1525 +317,21 @@ useEffect(() => {
490
317
  statusErrorSub.remove();
491
318
  };
492
319
  }, []);
320
+ ```
493
321
 
322
+ 5. **UI Components**
323
+ The provided `App.js` includes custom components like `Dropdown` and `FilledButton` for a user-friendly interface. Customize the styles in the `StyleSheet` to match your app's design.
494
324
 
495
- const safeParseMaybeJSON = (value) => {
496
- if (typeof value === 'string') {
497
- try {
498
- return JSON.parse(value);
499
- } catch (e) {
500
- console.warn('Failed to parse JSON string:', value);
501
- return value;
502
- }
503
- }
504
- return value;
505
- };
506
-
507
- useEffect(() => {
508
- setBillingInfo(prev => ({
509
- ...prev,
510
- visibility: {
511
- billing: isBillingVisible,
512
- additional: isAdditionalVisible,
513
- },
514
- }));
515
- }, [isBillingVisible, isAdditionalVisible]);
516
-
517
- const updateBillingInfo = (section, field, value) => {
518
- setBillingInfo(prev => ({
519
- ...prev,
520
- [section]: {
521
- ...prev[section],
522
- [field]: value,
523
- },
524
- }));
525
- };
526
-
527
- const updateAndroidConfig = (field, value) => {
528
- setAndroidConfig(prev => ({
529
- ...prev,
530
- [field]: value,
531
- }));
532
- };
533
-
534
- const updateAndroidConfigFields = (section, index, field, value) => {
535
- setAndroidConfig(prev => ({
536
- ...prev,
537
- fields: {
538
- ...prev.fields,
539
- [section]: prev.fields[section].map((item, i) =>
540
- i === index ? { ...item, [field]: value } : item
541
- ),
542
- },
543
- }));
544
- };
545
-
546
- const updateAndroidConfigAppearanceSettings = (field, value) => {
547
- setAndroidConfig(prev => ({
548
- ...prev,
549
- appearanceSettings: {
550
- ...prev.appearanceSettings,
551
- [field]: value,
552
- },
553
- }));
554
- };
555
-
556
- const updateThemeConfiguration = (field, value) => {
557
- setThemeConfiguration(prev => ({
558
- ...prev,
559
- [field]: value,
560
- }));
561
- };
562
-
563
- const updateGrailPayParams = (field, value) => {
564
- setGrailPayParams(prev => ({
565
- ...prev,
566
- [field]: value,
567
- }));
568
- };
569
-
570
- const updateRecurringData = (field, value) => {
571
- setRecurringData(prev => ({
572
- ...prev,
573
- [field]: value,
574
- }));
575
- };
576
-
577
- const togglePaymentMethod = (method) => {
578
- console.log('Toggling payment method:', method);
579
- setAndroidConfig(prev => {
580
- const currentMethods = prev.paymentMethod;
581
- console.log('Current payment methods before toggle:', currentMethods);
582
- const isCurrentlySelected = currentMethods.includes(method);
583
- console.log('Is currently selected:', isCurrentlySelected);
584
-
585
- let newMethods;
586
- if (isCurrentlySelected) {
587
- // If deselecting and it's the only method, don't allow deselection
588
- if (currentMethods.length === 1) {
589
- console.log('Cannot deselect last payment method, keeping current state');
590
- return prev; // Keep the current state
591
- }
592
- // Remove the method
593
- newMethods = currentMethods.filter(m => m !== method);
594
- console.log('Removing method, new methods:', newMethods);
595
- } else {
596
- // Add the method
597
- newMethods = [...currentMethods, method];
598
- console.log('Adding method, new methods:', newMethods);
599
- }
600
-
601
- const updatedConfig = {
602
- ...prev,
603
- paymentMethod: newMethods,
604
- };
605
- console.log('Updated android config payment methods:', updatedConfig.paymentMethod);
606
- return updatedConfig;
607
- });
608
- };
609
-
610
- const toggleInterval = (interval) => {
611
- setRecurringData(prev => ({
612
- ...prev,
613
- intervals: prev.intervals.includes(interval)
614
- ? prev.intervals.filter(i => i !== interval)
615
- : [...prev.intervals, interval],
616
- }));
617
- };
618
-
619
- const handlePayment = async () => {
620
- if (!amount || isNaN(parseFloat(amount)) || parseFloat(amount) <= 0) {
621
- return Alert.alert('Error', 'Please enter a valid amount');
622
- }
623
-
624
- // Validate that at least one interval is selected when recurring payment is enabled
625
- if (isRecurring && (!recurringData.intervals || recurringData.intervals.length === 0)) {
626
- return Alert.alert('Error', 'Please select at least one interval for recurring payment');
627
- }
628
-
629
- setLoading(true);
630
-
631
- // Add a timeout to ensure loading state is reset even if SDK blocks execution
632
- const timeoutId = setTimeout(() => {
633
- setLoading(false);
634
- }, 3000); // 3 second timeout
635
-
636
- try {
637
- if (Platform.OS === 'android') {
638
- await handleAndroidBilling();
639
- } else {
640
- await handleIosBilling();
641
- }
642
- } finally {
643
- clearTimeout(timeoutId);
644
- setLoading(false);
645
- }
646
- };
647
-
648
- const handleAndroidBilling = async () => {
649
-
650
- const { apiKey, secretKey } = apiKeys[environment];
651
-
652
- console.log('Selected payment methods for Android:', androidConfig.paymentMethod);
653
- console.log('Full androidConfig.paymentMethod:', JSON.stringify(androidConfig.paymentMethod));
654
-
655
- // Create a fresh copy of payment methods to ensure no reference issues
656
- const selectedPaymentMethods = [...androidConfig.paymentMethod];
657
- console.log('Fresh copy of payment methods:', selectedPaymentMethods);
658
- console.log('Payment method contains "ach":', selectedPaymentMethods.includes('ach'));
659
- console.log('Payment method contains "card":', selectedPaymentMethods.includes('card'));
660
-
661
- // Log ACH-specific configurations when ACH is selected
662
- if (selectedPaymentMethods.includes('ach')) {
663
- console.log('=== ACH CONFIGURATION DEBUG ===');
664
- console.log('authenticatedACH setting:', isAuthenticatedACH);
665
- console.log('secureAuthentication setting:', isSecureAuthentication);
666
- console.log('saveAccount setting:', androidConfig.saveAccount);
667
- }
668
-
669
- // Try different payment method format for ACH-only scenario
670
- let finalPaymentMethods = selectedPaymentMethods;
671
- if (selectedPaymentMethods.length === 1 && selectedPaymentMethods.includes('ach')) {
672
- // Try using a different format for ACH-only
673
- finalPaymentMethods = ['ach'];
674
- console.log('Using ACH-only format:', finalPaymentMethods);
675
- }
676
-
677
- const config = {
678
- amount,
679
- apiKey: apiKey,
680
- secretKey: secretKey,
681
- jsonConfig: {
682
- environment,
683
- amount,
684
- tokenOnly: false,
685
- currency: androidConfig.currency,
686
- saveCard: androidConfig.saveCard,
687
- saveAccount: androidConfig.saveAccount,
688
- authenticatedACH: isAuthenticatedACH,
689
- secureAuthentication: isSecureAuthentication,
690
-
691
- showReceipt: androidConfig.showReceipt,
692
- showDonate: androidConfig.showDonate,
693
- showTotal: androidConfig.showTotal,
694
- showSubmitButton: androidConfig.showSubmitButton,
695
- paymentMethod: finalPaymentMethods,
696
- // Try different configuration approaches for ACH-only
697
- ...(finalPaymentMethods.length === 1 && finalPaymentMethods.includes('ach') && {
698
- paymentMethods: ['ach'],
699
- allowedPaymentMethods: ['ach'],
700
- disableCard: true,
701
- }),
702
-
703
- emailEditable,
704
- email,
705
- name: name,
706
- fields: {
707
- ...androidConfig.fields,
708
- visibility: {
709
- billing: isBillingVisible,
710
- additional: isAdditionalVisible,
711
- },
712
- },
713
-
714
- metadata,
715
-
716
-
717
- ...(isRecurring && {
718
- recurring: {
719
- enableRecurring: true,
720
- recurringData,
721
- },
722
- }),
723
- grailPayParams,
724
- appearanceSettings: androidConfig.appearanceSettings,
725
-
726
-
727
- },
728
- };
729
-
730
- console.log('ENV API Keys =>', {
731
- env: environment,
732
- apiKeyFromEnv: apiKey,
733
- secretKeyFromEnv: secretKey,
734
- });
735
-
736
- console.log('Config API Key =>', config.apiKey);
737
- console.log('Config Secret Key =>', config.secretKey);
738
- console.log('Full config being sent to Android SDK:', JSON.stringify(config, null, 2));
739
- console.log('=== THEME CONFIGURATION DEBUG ===');
740
- console.log('Android appearanceSettings:', JSON.stringify(androidConfig.appearanceSettings, null, 2));
741
-
742
-
743
-
744
- try {
745
- const response = await RNEasymerchantsdk.makePayment(config);
746
- console.log('Full payment response:', response);
747
-
748
- const parsedResponse = {
749
- ...response,
750
- billingInfo: safeParseMaybeJSON(response.billingInfo),
751
- additional_info: safeParseMaybeJSON(response.additional_info),
752
- };
753
-
754
- console.log('Parsed safe response:', parsedResponse);
755
-
756
- setResult(JSON.stringify(parsedResponse, null, 2));
757
- } catch (error) {
758
- setResult(`Error: ${error.message}`);
759
- Alert.alert('Payment Error', error.message);
760
- } finally {
761
- setLoading(false);
762
- }
763
- };
764
-
765
- const handleIosBilling = async () => {
766
-
767
- console.log('Selected payment methods for iOS:', androidConfig.paymentMethod);
768
- console.log('Full androidConfig.paymentMethod for iOS:', JSON.stringify(androidConfig.paymentMethod));
769
-
770
- // Create a fresh copy of payment methods for iOS and convert to iOS format
771
- const selectedPaymentMethodsIOS = [...androidConfig.paymentMethod];
772
- console.log('Fresh copy of payment methods for iOS:', selectedPaymentMethodsIOS);
773
- console.log('Payment method contains "ach" (iOS):', selectedPaymentMethodsIOS.includes('ach'));
774
- console.log('Payment method contains "card" (iOS):', selectedPaymentMethodsIOS.includes('card'));
775
-
776
- // Convert Android payment method names to iOS format
777
- let finalPaymentMethodsIOS = selectedPaymentMethodsIOS.map(method => {
778
- if (method === 'card') return 'Card';
779
- if (method === 'ach') return 'Bank';
780
- return method;
781
- });
782
-
783
- console.log('Converted payment methods for iOS:', finalPaymentMethodsIOS);
784
- console.log('=== iOS THEME CONFIGURATION DEBUG ===');
785
- console.log('iOS themeConfiguration:', JSON.stringify(themeConfiguration, null, 2));
786
-
787
- try {
788
- const result = await EasyMerchantSdk.billing(
789
- amount,
790
- androidConfig.currency || 'usd',
791
- billingInfo,
792
- finalPaymentMethodsIOS,
793
- themeConfiguration,
794
- false, // tokenOnly
795
- androidConfig.saveCard,
796
- androidConfig.saveAccount,
797
- isAuthenticatedACH,
798
- grailPayParams,
799
- 'Submit',
800
- isRecurring,
801
- isRecurring ? recurringData.allowCycles : 0,
802
- isRecurring ? recurringData.intervals : [],
803
- isRecurring ? recurringData.recurringStartType : '',
804
- isRecurring ? recurringData.recurringStartDate : '',
805
- isSecureAuthentication,
806
- androidConfig.showReceipt,
807
- androidConfig.showTotal,
808
- androidConfig.showSubmitButton,
809
- isEmail,
810
- email,
811
- name,
812
- metadata
813
- );
814
-
815
- const refToken = result?.additionalInfo?.threeDSecureStatus?.data?.ref_token;
816
- if (refToken) setReferenceToken(refToken);
817
- setResult(JSON.stringify(result, null, 2));
818
- } catch (error) {
819
- console.error('Billing Error:', error);
820
- setResult(`Billing Error: ${error.message || JSON.stringify(error)}`);
821
- } finally {
822
- setLoading(false);
823
- }
824
- };
825
-
826
- const handleCheckStatus = async () => {
827
- setLoading(true);
828
- try {
829
- const response = await RNEasymerchantsdk.checkPaymentStatus();
830
- console.log('Full payment response:', response);
831
- setResult(JSON.stringify(response, null, 2));
832
- } catch (error) {
833
- setResult(`Error: ${error.message}`);
834
- Alert.alert('Status Check Error', error.message);
835
- } finally {
836
- setLoading(false);
837
- }
838
- };
839
-
840
- const handlePaymentReference = async () => {
841
- if (Platform.OS === 'android') {
842
- setResult('Payment Reference not supported on Android');
843
- return;
844
- }
845
- try {
846
- const response = await EasyMerchantSdk.paymentReference(referenceToken);
847
- setResult(`Payment Reference:\n${JSON.stringify(response, null, 2)}`);
848
- } catch (error) {
849
- setResult(`Payment Reference Error: ${error.message || JSON.stringify(error)}`);
850
- } finally {
851
- setLoading(false);
852
- }
853
- };
854
-
855
- return (
856
- <KeyboardAvoidingView
857
- style={styles.container}
858
- behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
859
- keyboardVerticalOffset={Platform.OS === 'ios' ? 0 : 20}
860
- >
861
- <ScrollView
862
- contentContainerStyle={styles.scrollContent}
863
- keyboardShouldPersistTaps="handled"
864
- showsVerticalScrollIndicator={false}
865
- >
866
- <Text style={styles.title}>EasyMerchant SDK</Text>
867
-
868
-
869
-
870
- <Text style={styles.sectionTitle}>Basic Info</Text>
871
- <Text style={styles.label}>Amount</Text>
872
- <TextInput
873
- style={styles.input}
874
- placeholder="Enter amount (e.g., 10.00)"
875
- placeholderTextColor="#999999"
876
- keyboardType="decimal-pad"
877
- value={amount}
878
- onChangeText={setAmount}
879
- />
880
- <Text style={styles.label}>Email</Text>
881
- <TextInput
882
- style={styles.input}
883
- placeholder="Enter email"
884
- placeholderTextColor="#999999"
885
- keyboardType="email-address"
886
- value={email}
887
- onChangeText={setEmail}
888
- />
889
- <Text style={styles.label}>Name</Text>
890
- <TextInput
891
- style={styles.input}
892
- placeholder="Enter name"
893
- placeholderTextColor="#999999"
894
- value={name}
895
- onChangeText={setName}
896
- />
897
-
898
- <View style={styles.buttonGroup}>
899
- <FilledButton
900
- title="Pay"
901
- onPress={handlePayment}
902
- disabled={loading}
903
- />
904
- {Platform.OS === 'ios' && (
905
- <FilledButton
906
- title="Payment Ref"
907
- onPress={handlePaymentReference}
908
- disabled={loading}
909
- />
910
- )}
911
- </View>
912
-
913
-
914
-
915
-
916
-
917
- <View style={styles.toggleContainer}>
918
- <Text style={styles.label}>Show Configurations</Text>
919
- <Switch
920
- value={showConfig}
921
- onValueChange={setShowConfig}
922
- trackColor={{ false: '#ccc', true: '#2563EB' }}
923
- thumbColor={showConfig ? '#fff' : '#f4f3f4'}
924
- />
925
- </View>
926
-
927
- {showConfig && (
928
- <>
929
- <Text style={styles.sectionTitle}>Environment</Text>
930
- <View style={styles.pickerContainer}>
931
- <Text style={styles.label}>Select Environment</Text>
932
- <View style={styles.buttonGroup}>
933
- <TouchableOpacity
934
- style={[
935
- styles.environmentButton,
936
- { backgroundColor: environment === 'sandbox' ? '#2563EB' : '#ccc' },
937
- ]}
938
- onPress={() => {
939
- console.log('Sandbox tapped, setting environment to sandbox');
940
- setEnvironment('sandbox');
941
- }}
942
- disabled={isEnvironmentLoading || environment === 'sandbox'}
943
- >
944
- <Text style={styles.buttonText}>Sandbox</Text>
945
- </TouchableOpacity>
946
- <TouchableOpacity
947
- style={[
948
- styles.environmentButton,
949
- { backgroundColor: environment === 'staging' ? '#2563EB' : '#ccc' },
950
- ]}
951
- onPress={() => {
952
- console.log('Staging tapped, setting environment to staging');
953
- setEnvironment('staging');
954
- }}
955
- disabled={isEnvironmentLoading || environment === 'staging'}
956
- >
957
- <Text style={styles.buttonText}>Staging</Text>
958
- </TouchableOpacity>
959
- </View>
960
- </View>
961
-
962
- <Text style={styles.sectionTitle}>{environment === 'sandbox' ? 'Sandbox' : 'Staging'} API Credentials</Text>
963
- <Text style={styles.label}>API Key</Text>
964
- <TextInput
965
- style={styles.input}
966
- value={apiKeys[environment].apiKey}
967
- onChangeText={value =>
968
- setApiKeys(prev => ({
969
- ...prev,
970
- [environment]: { ...prev[environment], apiKey: value },
971
- }))
972
- }
973
- placeholder={`Enter ${environment === 'sandbox' ? 'Sandbox' : 'Staging'} API Key`}
974
- placeholderTextColor="#999999"
975
- />
976
- <Text style={styles.label}>Secret Key</Text>
977
- <View style={styles.inputContainer}>
978
- <TextInput
979
- style={[styles.input, { flex: 1 }]}
980
- value={apiKeys[environment].secretKey}
981
- onChangeText={value =>
982
- setApiKeys(prev => ({
983
- ...prev,
984
- [environment]: { ...prev[environment], secretKey: value },
985
- }))
986
- }
987
- placeholder={`Enter ${environment === 'sandbox' ? 'Sandbox' : 'Staging'} Secret Key`}
988
- placeholderTextColor="#999999"
989
- secureTextEntry={!showSecretKey}
990
- />
991
- <TouchableOpacity
992
- style={styles.eyeButton}
993
- onPress={() => setShowSecretKey(prev => !prev)}
994
- >
995
- <Text style={styles.eyeIcon}>{showSecretKey ? '👁️' : '👁️‍🗨️'}</Text>
996
- </TouchableOpacity>
997
- </View>
998
-
999
-
1000
-
1001
-
1002
-
1003
- <Text style={styles.sectionTitle}>Payment Options</Text>
1004
- <View style={styles.toggleContainer}>
1005
- <Text style={styles.label}>Recurring Payment</Text>
1006
- <Switch
1007
- value={isRecurring}
1008
- onValueChange={setIsRecurring}
1009
- trackColor={{ false: '#ccc', true: '#2563EB' }}
1010
- thumbColor={isRecurring ? '#fff' : '#f4f3f4'}
1011
- />
1012
- </View>
1013
- <View style={styles.toggleContainer}>
1014
- <Text style={styles.label}>Authenticated ACH</Text>
1015
- <Switch
1016
- value={isAuthenticatedACH}
1017
- onValueChange={setAuthenticatedACH}
1018
- trackColor={{ false: '#ccc', true: '#2563EB' }}
1019
- thumbColor={isAuthenticatedACH ? '#fff' : '#f4f3f4'}
1020
- />
1021
- </View>
1022
- <View style={styles.toggleContainer}>
1023
- <Text style={styles.label}>3DS</Text>
1024
- <Switch
1025
- value={isSecureAuthentication}
1026
- onValueChange={setSecureAuthentication}
1027
- trackColor={{ false: '#ccc', true: '#2563EB' }}
1028
- thumbColor={isSecureAuthentication ? '#fff' : '#f4f3f4'}
1029
- />
1030
- </View>
1031
- <View style={styles.toggleContainer}>
1032
- <Text style={styles.label}>Billing Visible</Text>
1033
- <Switch
1034
- value={isBillingVisible}
1035
- onValueChange={setBillingVisible}
1036
- trackColor={{ false: '#ccc', true: '#2563EB' }}
1037
- thumbColor={isBillingVisible ? '#fff' : '#f4f3f4'}
1038
- />
1039
- </View>
1040
- <View style={styles.toggleContainer}>
1041
- <Text style={styles.label}>Additional Info Visible</Text>
1042
- <Switch
1043
- value={isAdditionalVisible}
1044
- onValueChange={setAdditionalVisible}
1045
- trackColor={{ false: '#ccc', true: '#2563EB' }}
1046
- thumbColor={isAdditionalVisible ? '#fff' : '#f4f3f4'}
1047
- />
1048
- </View>
1049
- <Text style={styles.label}>Payment Methods (Shared)</Text>
1050
- <Text style={styles.debugText}>
1051
- Selected: {androidConfig.paymentMethod.join(', ') || 'None'}
1052
- </Text>
1053
- <View style={styles.buttonGroup}>
1054
- <FilledButton
1055
- title="Card"
1056
- onPress={() => togglePaymentMethod('card')}
1057
- disabled={loading}
1058
- style={[
1059
- { flex: 1 },
1060
- androidConfig.paymentMethod.includes('card')
1061
- ? { backgroundColor: '#2563EB' }
1062
- : { backgroundColor: '#E5E7EB' }
1063
- ]}
1064
- textStyle={androidConfig.paymentMethod.includes('card')
1065
- ? { color: '#fff' }
1066
- : { color: '#374151' }
1067
- }
1068
- />
1069
- <FilledButton
1070
- title="ACH"
1071
- onPress={() => togglePaymentMethod('ach')}
1072
- disabled={loading}
1073
- style={[
1074
- { flex: 1 },
1075
- androidConfig.paymentMethod.includes('ach')
1076
- ? { backgroundColor: '#2563EB' }
1077
- : { backgroundColor: '#E5E7EB' }
1078
- ]}
1079
- textStyle={androidConfig.paymentMethod.includes('ach')
1080
- ? { color: '#fff' }
1081
- : { color: '#374151' }
1082
- }
1083
- />
1084
- </View>
1085
- {Platform.OS === 'android' && (
1086
- <View style={styles.toggleContainer}>
1087
- <Text style={styles.label}>Email Editable</Text>
1088
- <Switch
1089
- value={emailEditable}
1090
- onValueChange={setEmailEditable}
1091
- trackColor={{ false: '#ccc', true: '#2563EB' }}
1092
- thumbColor={emailEditable ? '#fff' : '#f4f3f4'}
1093
- />
1094
- </View>
1095
- )}
1096
- {Platform.OS === 'ios' && (
1097
- <View style={styles.toggleContainer}>
1098
- <Text style={styles.label}>Allow Email Editable</Text>
1099
- <Switch
1100
- value={isEmail}
1101
- onValueChange={setIsEmail}
1102
- trackColor={{ false: '#ccc', true: '#2563EB' }}
1103
- thumbColor={isEmail ? '#fff' : '#f4f3f4'}
1104
- />
1105
- </View>
1106
- )}
1107
-
1108
- <Text style={styles.sectionTitle}>Billing Info</Text>
1109
-
1110
- {/* Billing Required Switches */}
1111
- <Text style={styles.subsectionTitle}>Billing Required Fields</Text>
1112
- <Text style={styles.label}>Billing Required: Address</Text>
1113
- <Switch
1114
- value={billingInfo.billingRequired.address}
1115
- onValueChange={value => updateBillingInfo('billingRequired', 'address', value)}
1116
- trackColor={{ false: '#ccc', true: '#2563EB' }}
1117
- thumbColor={billingInfo.billingRequired.address ? '#fff' : '#f4f3f4'}
1118
- />
1119
- <Text style={styles.label}>Billing Required: Country</Text>
1120
- <Switch
1121
- value={billingInfo.billingRequired.country}
1122
- onValueChange={value => updateBillingInfo('billingRequired', 'country', value)}
1123
- trackColor={{ false: '#ccc', true: '#2563EB' }}
1124
- thumbColor={billingInfo.billingRequired.country ? '#fff' : '#f4f3f4'}
1125
- />
1126
- <Text style={styles.label}>Billing Required: State</Text>
1127
- <Switch
1128
- value={billingInfo.billingRequired.state}
1129
- onValueChange={value => updateBillingInfo('billingRequired', 'state', value)}
1130
- trackColor={{ false: '#ccc', true: '#2563EB' }}
1131
- thumbColor={billingInfo.billingRequired.state ? '#fff' : '#f4f3f4'}
1132
- />
1133
- <Text style={styles.label}>Billing Required: City</Text>
1134
- <Switch
1135
- value={billingInfo.billingRequired.city}
1136
- onValueChange={value => updateBillingInfo('billingRequired', 'city', value)}
1137
- trackColor={{ false: '#ccc', true: '#2563EB' }}
1138
- thumbColor={billingInfo.billingRequired.city ? '#fff' : '#f4f3f4'}
1139
- />
1140
- <Text style={styles.label}>Billing Required: Postal Code</Text>
1141
- <Switch
1142
- value={billingInfo.billingRequired.postal_code}
1143
- onValueChange={value => updateBillingInfo('billingRequired', 'postal_code', value)}
1144
- trackColor={{ false: '#ccc', true: '#2563EB' }}
1145
- thumbColor={billingInfo.billingRequired.postal_code ? '#fff' : '#f4f3f4'}
1146
- />
1147
-
1148
- {/* Billing Fields Section */}
1149
- <Text style={styles.subsectionTitle}>Billing Fields</Text>
1150
- {billingInfo.billingRequired.address && (
1151
- <>
1152
- <Text style={styles.label}>Billing Address</Text>
1153
- <TextInput
1154
- style={styles.input}
1155
- value={billingInfo.billing.address}
1156
- onChangeText={value => updateBillingInfo('billing', 'address', value)}
1157
- placeholder="Enter billing address"
1158
- placeholderTextColor="#999999"
1159
- />
1160
- </>
1161
- )}
1162
- {billingInfo.billingRequired.country && (
1163
- <>
1164
- <Text style={styles.label}>Billing Country</Text>
1165
- <Dropdown
1166
- value={billingInfo.billing.country}
1167
- onValueChange={value => updateBillingInfo('billing', 'country', value)}
1168
- options={[
1169
- "Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra",
1170
- "Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina",
1171
- "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan",
1172
- "Bahamas", "Bahrain", "Bangladesh", "Barbados", "Belarus",
1173
- "Belgium", "Belize", "Benin", "Bermuda", "Bhutan",
1174
- "Bolivia", "Bosnia and Herzegovina", "Botswana", "Bouvet Island", "Brazil",
1175
- "British Indian Ocean Territory", "Brunei Darussalam", "Bulgaria", "Burkina Faso", "Burundi",
1176
- "Cabo Verde", "Cambodia", "Cameroon", "Canada", "Cayman Islands",
1177
- "Central African Republic", "Chad", "Chile", "China", "Christmas Island",
1178
- "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo", "Congo, Democratic Republic of the",
1179
- "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Curaçao",
1180
- "Cyprus", "Czech Republic", "Côte d'Ivoire", "Denmark", "Djibouti",
1181
- "Dominica", "Dominican Republic", "Ecuador", "Egypt", "El Salvador",
1182
- "Equatorial Guinea", "Eritrea", "Estonia", "Eswatini", "Ethiopia",
1183
- "Falkland Islands (Malvinas)", "Faroe Islands", "Fiji", "Finland", "France",
1184
- "French Guiana", "French Polynesia", "French Southern Territories", "Gabon", "Gambia",
1185
- "Georgia", "Germany", "Ghana", "Gibraltar", "Greece",
1186
- "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala",
1187
- "Guernsey", "Guinea", "Guinea-Bissau", "Guyana", "Haiti",
1188
- "Heard Island and McDonald Islands", "Holy See", "Honduras", "Hong Kong", "Hungary",
1189
- "Iceland", "India", "Indonesia", "Iran", "Iraq",
1190
- "Ireland", "Isle of Man", "Israel", "Italy", "Jamaica",
1191
- "Japan", "Jersey", "Jordan", "Kazakhstan", "Kenya",
1192
- "Kiribati", "Korea (Democratic People's Republic of)", "Korea, Republic of", "Kuwait", "Kyrgyzstan",
1193
- "Lao People's Democratic Republic", "Latvia", "Lebanon", "Lesotho", "Liberia",
1194
- "Libya", "Liechtenstein", "Lithuania", "Luxembourg", "Macao",
1195
- "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali",
1196
- "Malta", "Marshall Islands", "Martinique", "Mauritania", "Mauritius",
1197
- "Mayotte", "Mexico", "Micronesia (Federated States of)", "Moldova", "Monaco",
1198
- "Mongolia", "Montenegro", "Montserrat", "Morocco", "Mozambique",
1199
- "Myanmar", "Namibia", "Nauru", "Nepal", "Netherlands",
1200
- "New Caledonia", "New Zealand", "Nicaragua", "Niger", "Nigeria",
1201
- "Niue", "Norfolk Island", "North Macedonia", "Northern Mariana Islands", "Norway",
1202
- "Oman", "Pakistan", "Palau", "Palestine, State of", "Panama",
1203
- "Papua New Guinea", "Paraguay", "Peru", "Philippines", "Pitcairn",
1204
- "Poland", "Portugal", "Puerto Rico", "Qatar", "Romania",
1205
- "Russian Federation", "Rwanda", "Réunion", "Saint Barthélemy", "Saint Helena, Ascension and Tristan da Cunha",
1206
- "Saint Kitts and Nevis", "Saint Lucia", "Saint Martin (French part)", "Saint Pierre and Miquelon", "Saint Vincent and the Grenadines",
1207
- "Samoa", "San Marino", "Sao Tome and Principe", "Saudi Arabia", "Senegal",
1208
- "Serbia", "Seychelles", "Sierra Leone", "Singapore", "Sint Maarten (Dutch part)",
1209
- "Slovakia", "Slovenia", "Solomon Islands", "Somalia", "South Africa",
1210
- "South Georgia and the South Sandwich Islands", "South Sudan", "Spain", "Sri Lanka", "Sudan",
1211
- "Suriname", "Svalbard and Jan Mayen", "Sweden", "Switzerland", "Syrian Arab Republic",
1212
- "Taiwan, Province of China", "Tajikistan", "Tanzania", "Thailand", "Timor-Leste",
1213
- "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia",
1214
- "Turkey", "Turkmenistan", "Tuvalu", "Uganda", "Ukraine",
1215
- "United Arab Emirates", "United Kingdom", "United States", "Uruguay", "Uzbekistan",
1216
- "Vanuatu", "Venezuela", "Viet Nam", "Western Sahara", "Yemen",
1217
- "Zambia", "Zimbabwe", "Åland Islands"
1218
- ]}
1219
- placeholder="Select country"
1220
- />
1221
- </>
1222
- )}
1223
- {billingInfo.billingRequired.state && (
1224
- <>
1225
- <Text style={styles.label}>Billing State</Text>
1226
- <TextInput
1227
- style={styles.input}
1228
- value={billingInfo.billing.state}
1229
- onChangeText={value => updateBillingInfo('billing', 'state', value)}
1230
- placeholder="Enter state"
1231
- placeholderTextColor="#999999"
1232
- />
1233
- </>
1234
- )}
1235
- {billingInfo.billingRequired.city && (
1236
- <>
1237
- <Text style={styles.label}>Billing City</Text>
1238
- <TextInput
1239
- style={styles.input}
1240
- value={billingInfo.billing.city}
1241
- onChangeText={value => updateBillingInfo('billing', 'city', value)}
1242
- placeholder="Enter city"
1243
- placeholderTextColor="#999999"
1244
- />
1245
- </>
1246
- )}
1247
- {billingInfo.billingRequired.postal_code && (
1248
- <>
1249
- <Text style={styles.label}>Billing Postal Code</Text>
1250
- <TextInput
1251
- style={styles.input}
1252
- value={billingInfo.billing.postal_code}
1253
- onChangeText={value => updateBillingInfo('billing', 'postal_code', value)}
1254
- placeholder="Enter postal code"
1255
- placeholderTextColor="#999999"
1256
- />
1257
- </>
1258
- )}
1259
-
1260
- <Text style={styles.sectionTitle}>Additional Info</Text>
1261
-
1262
- {/* Additional Required Switches */}
1263
- <Text style={styles.subsectionTitle}>Additional Required Fields</Text>
1264
- <Text style={styles.label}>Additional Required: Name</Text>
1265
- <Switch
1266
- value={billingInfo.additionalRequired.name}
1267
- onValueChange={value => updateBillingInfo('additionalRequired', 'name', value)}
1268
- trackColor={{ false: '#ccc', true: '#2563EB' }}
1269
- thumbColor={billingInfo.additionalRequired.name ? '#fff' : '#f4f3f4'}
1270
- />
1271
- <Text style={styles.label}>Additional Required: Email Address</Text>
1272
- <Switch
1273
- value={billingInfo.additionalRequired.email_address}
1274
- onValueChange={value => updateBillingInfo('additionalRequired', 'email_address', value)}
1275
- trackColor={{ false: '#ccc', true: '#2563EB' }}
1276
- thumbColor={billingInfo.additionalRequired.email_address ? '#fff' : '#f4f3f4'}
1277
- />
1278
- <Text style={styles.label}>Additional Required: Phone Number</Text>
1279
- <Switch
1280
- value={billingInfo.additionalRequired.phone_number}
1281
- onValueChange={value => updateBillingInfo('additionalRequired', 'phone_number', value)}
1282
- trackColor={{ false: '#ccc', true: '#2563EB' }}
1283
- thumbColor={billingInfo.additionalRequired.phone_number ? '#fff' : '#f4f3f4'}
1284
- />
1285
- <Text style={styles.label}>Additional Required: Description</Text>
1286
- <Switch
1287
- value={billingInfo.additionalRequired.description}
1288
- onValueChange={value => updateBillingInfo('additionalRequired', 'description', value)}
1289
- trackColor={{ false: '#ccc', true: '#2563EB' }}
1290
- thumbColor={billingInfo.additionalRequired.description ? '#fff' : '#f4f3f4'}
1291
- />
1292
-
1293
- {/* Additional Fields Section */}
1294
- <Text style={styles.subsectionTitle}>Additional Fields</Text>
1295
- {billingInfo.additionalRequired.name && (
1296
- <>
1297
- <Text style={styles.label}>Additional: Name</Text>
1298
- <TextInput
1299
- style={styles.input}
1300
- value={billingInfo.additional.name}
1301
- onChangeText={value => updateBillingInfo('additional', 'name', value)}
1302
- placeholder="Enter name"
1303
- placeholderTextColor="#999999"
1304
- />
1305
- </>
1306
- )}
1307
- {billingInfo.additionalRequired.email_address && (
1308
- <>
1309
- <Text style={styles.label}>Additional: Email Address</Text>
1310
- <TextInput
1311
- style={styles.input}
1312
- value={billingInfo.additional.email_address}
1313
- onChangeText={value => updateBillingInfo('additional', 'email_address', value)}
1314
- placeholder="Enter email address"
1315
- placeholderTextColor="#999999"
1316
- />
1317
- </>
1318
- )}
1319
- {billingInfo.additionalRequired.phone_number && (
1320
- <>
1321
- <Text style={styles.label}>Additional: Phone Number</Text>
1322
- <View style={styles.phoneRow}>
1323
- <TextInput
1324
- style={styles.countryCodeInput}
1325
- value={billingInfo.additional.country_code || '+1'}
1326
- onChangeText={value => {
1327
- // Only allow a '+' at the start, followed by up to 4 digits
1328
- let filtered = value.replace(/[^+0-9]/g, '');
1329
- if (!filtered.startsWith('+')) filtered = '+' + filtered.replace(/\+/g, '');
1330
- filtered = filtered.slice(0, 5);
1331
- // Update country code and combined phone number
1332
- const phone = billingInfo.additional.phone_number || '';
1333
- const phoneOnly = phone.slice((billingInfo.additional.country_code || '+1').replace('+', '').length);
1334
- const combined = filtered.replace('+', '') + phoneOnly;
1335
- updateBillingInfo('additional', 'country_code', filtered);
1336
- updateBillingInfo('additional', 'phone_number', combined);
1337
- }}
1338
- placeholder="+1"
1339
- placeholderTextColor="#999999"
1340
- keyboardType="phone-pad"
1341
- maxLength={5}
1342
- />
1343
- <TextInput
1344
- style={[styles.input, { flex: 1, marginBottom: 0 }]}
1345
- value={(billingInfo.additional.phone_number || '').slice((billingInfo.additional.country_code || '+1').replace('+', '').length)}
1346
- onChangeText={value => {
1347
- // Only allow digits and limit to 10 characters
1348
- const numericValue = value.replace(/[^0-9]/g, '').slice(0, 10);
1349
- const code = (billingInfo.additional.country_code || '+1').replace('+', '');
1350
- updateBillingInfo('additional', 'phone_number', code + numericValue);
1351
- }}
1352
- placeholder="Enter phone number"
1353
- placeholderTextColor="#999999"
1354
- keyboardType="phone-pad"
1355
- maxLength={10}
1356
- />
1357
- </View>
1358
- </>
1359
- )}
1360
- {billingInfo.additionalRequired.description && (
1361
- <>
1362
- <Text style={styles.label}>Additional: Description</Text>
1363
- <TextInput
1364
- style={styles.input}
1365
- value={billingInfo.additional.description}
1366
- onChangeText={value => updateBillingInfo('additional', 'description', value)}
1367
- placeholder="Enter description"
1368
- placeholderTextColor="#999999"
1369
- />
1370
- </>
1371
- )}
1372
-
1373
- {/* Android Appearance Settings */}
1374
- {Platform.OS === 'android' && (
1375
- <>
1376
- <Text style={styles.sectionTitle}>Android Appearance Settings</Text>
1377
- <Text style={styles.label}>Body Background Color</Text>
1378
- <View style={styles.colorInputContainer}>
1379
- <TextInput
1380
- style={[styles.input, { flex: 1 }]}
1381
- value={androidConfig.appearanceSettings.bodyBackgroundColor}
1382
- onChangeText={value => updateAndroidConfigAppearanceSettings('bodyBackgroundColor', value)}
1383
- placeholder="Enter body background color"
1384
- placeholderTextColor="#999999"
1385
- />
1386
- <View style={[styles.colorPreview, { backgroundColor: androidConfig.appearanceSettings.bodyBackgroundColor }]} />
1387
- </View>
1388
- <Text style={styles.label}>Container Background Color</Text>
1389
- <View style={styles.colorInputContainer}>
1390
- <TextInput
1391
- style={[styles.input, { flex: 1 }]}
1392
- value={androidConfig.appearanceSettings.containerBackgroundColor}
1393
- onChangeText={value => updateAndroidConfigAppearanceSettings('containerBackgroundColor', value)}
1394
- placeholder="Enter container background color"
1395
- placeholderTextColor="#999999"
1396
- />
1397
- <View style={[styles.colorPreview, { backgroundColor: androidConfig.appearanceSettings.containerBackgroundColor }]} />
1398
- </View>
1399
- <Text style={styles.label}>Primary Font Color</Text>
1400
- <View style={styles.colorInputContainer}>
1401
- <TextInput
1402
- style={[styles.input, { flex: 1 }]}
1403
- value={androidConfig.appearanceSettings.primaryFontColor}
1404
- onChangeText={value => updateAndroidConfigAppearanceSettings('primaryFontColor', value)}
1405
- placeholder="Enter primary font color"
1406
- placeholderTextColor="#999999"
1407
- />
1408
- <View style={[styles.colorPreview, { backgroundColor: androidConfig.appearanceSettings.primaryFontColor }]} />
1409
- </View>
1410
- <Text style={styles.label}>Secondary Font Color</Text>
1411
- <View style={styles.colorInputContainer}>
1412
- <TextInput
1413
- style={[styles.input, { flex: 1 }]}
1414
- value={androidConfig.appearanceSettings.secondaryFontColor}
1415
- onChangeText={value => updateAndroidConfigAppearanceSettings('secondaryFontColor', value)}
1416
- placeholder="Enter secondary font color"
1417
- placeholderTextColor="#999999"
1418
- />
1419
- <View style={[styles.colorPreview, { backgroundColor: androidConfig.appearanceSettings.secondaryFontColor }]} />
1420
- </View>
1421
- <Text style={styles.label}>Primary Button Background Color</Text>
1422
- <View style={styles.colorInputContainer}>
1423
- <TextInput
1424
- style={[styles.input, { flex: 1 }]}
1425
- value={androidConfig.appearanceSettings.primaryButtonBackgroundColor}
1426
- onChangeText={value => updateAndroidConfigAppearanceSettings('primaryButtonBackgroundColor', value)}
1427
- placeholder="Enter primary button background color"
1428
- placeholderTextColor="#999999"
1429
- />
1430
- <View style={[styles.colorPreview, { backgroundColor: androidConfig.appearanceSettings.primaryButtonBackgroundColor }]} />
1431
- </View>
1432
- <Text style={styles.label}>Primary Button Hover Color</Text>
1433
- <View style={styles.colorInputContainer}>
1434
- <TextInput
1435
- style={[styles.input, { flex: 1 }]}
1436
- value={androidConfig.appearanceSettings.primaryButtonHoverColor}
1437
- onChangeText={value => updateAndroidConfigAppearanceSettings('primaryButtonHoverColor', value)}
1438
- placeholder="Enter primary button hover color"
1439
- placeholderTextColor="#999999"
1440
- />
1441
- <View style={[styles.colorPreview, { backgroundColor: androidConfig.appearanceSettings.primaryButtonHoverColor }]} />
1442
- </View>
1443
- <Text style={styles.label}>Primary Button Font Color</Text>
1444
- <View style={styles.colorInputContainer}>
1445
- <TextInput
1446
- style={[styles.input, { flex: 1 }]}
1447
- value={androidConfig.appearanceSettings.primaryButtonFontColor}
1448
- onChangeText={value => updateAndroidConfigAppearanceSettings('primaryButtonFontColor', value)}
1449
- placeholder="Enter primary button font color"
1450
- placeholderTextColor="#999999"
1451
- />
1452
- <View style={[styles.colorPreview, { backgroundColor: androidConfig.appearanceSettings.primaryButtonFontColor }]} />
1453
- </View>
1454
- <Text style={styles.label}>Secondary Button Background Color</Text>
1455
- <View style={styles.colorInputContainer}>
1456
- <TextInput
1457
- style={[styles.input, { flex: 1 }]}
1458
- value={androidConfig.appearanceSettings.secondaryButtonBackgroundColor}
1459
- onChangeText={value => updateAndroidConfigAppearanceSettings('secondaryButtonBackgroundColor', value)}
1460
- placeholder="Enter secondary button background color"
1461
- placeholderTextColor="#999999"
1462
- />
1463
- <View style={[styles.colorPreview, { backgroundColor: androidConfig.appearanceSettings.secondaryButtonBackgroundColor }]} />
1464
- </View>
1465
- <Text style={styles.label}>Secondary Button Hover Color</Text>
1466
- <View style={styles.colorInputContainer}>
1467
- <TextInput
1468
- style={[styles.input, { flex: 1 }]}
1469
- value={androidConfig.appearanceSettings.secondaryButtonHoverColor}
1470
- onChangeText={value => updateAndroidConfigAppearanceSettings('secondaryButtonHoverColor', value)}
1471
- placeholder="Enter secondary button hover color"
1472
- placeholderTextColor="#999999"
1473
- />
1474
- <View style={[styles.colorPreview, { backgroundColor: androidConfig.appearanceSettings.secondaryButtonHoverColor }]} />
1475
- </View>
1476
- <Text style={styles.label}>Secondary Button Font Color</Text>
1477
- <View style={styles.colorInputContainer}>
1478
- <TextInput
1479
- style={[styles.input, { flex: 1 }]}
1480
- value={androidConfig.appearanceSettings.secondaryButtonFontColor}
1481
- onChangeText={value => updateAndroidConfigAppearanceSettings('secondaryButtonFontColor', value)}
1482
- placeholder="Enter secondary button font color"
1483
- placeholderTextColor="#999999"
1484
- />
1485
- <View style={[styles.colorPreview, { backgroundColor: androidConfig.appearanceSettings.secondaryButtonFontColor }]} />
1486
- </View>
1487
- <Text style={styles.label}>Border Radius</Text>
1488
- <TextInput
1489
- style={styles.input}
1490
- value={androidConfig.appearanceSettings.borderRadius}
1491
- onChangeText={value => updateAndroidConfigAppearanceSettings('borderRadius', value)}
1492
- placeholder="Enter border radius"
1493
- placeholderTextColor="#999999"
1494
- />
1495
- <Text style={styles.label}>Font Size</Text>
1496
- <TextInput
1497
- style={styles.input}
1498
- value={androidConfig.appearanceSettings.fontSize}
1499
- onChangeText={value => updateAndroidConfigAppearanceSettings('fontSize', value)}
1500
- placeholder="Enter font size"
1501
- placeholderTextColor="#999999"
1502
- />
1503
-
1504
- </>
1505
- )}
1506
-
1507
- {/* iOS Theme Configuration */}
1508
- {Platform.OS === 'ios' && (
1509
- <>
1510
- <Text style={styles.sectionTitle}>Theme Configuration (iOS)</Text>
1511
- <Text style={styles.label}>Body Background Color</Text>
1512
- <View style={styles.colorInputContainer}>
1513
- <TextInput
1514
- style={[styles.input, { flex: 1 }]}
1515
- value={themeConfiguration.bodyBackgroundColor}
1516
- onChangeText={value => updateThemeConfiguration('bodyBackgroundColor', value)}
1517
- placeholder="Enter body background color"
1518
- placeholderTextColor="#999999"
1519
- />
1520
- <View style={[styles.colorPreview, { backgroundColor: themeConfiguration.bodyBackgroundColor }]} />
1521
- </View>
1522
- <Text style={styles.label}>Container Background Color</Text>
1523
- <View style={styles.colorInputContainer}>
1524
- <TextInput
1525
- style={[styles.input, { flex: 1 }]}
1526
- value={themeConfiguration.containerBackgroundColor}
1527
- onChangeText={value => updateThemeConfiguration('containerBackgroundColor', value)}
1528
- placeholder="Enter container background color"
1529
- placeholderTextColor="#999999"
1530
- />
1531
- <View style={[styles.colorPreview, { backgroundColor: themeConfiguration.containerBackgroundColor }]} />
1532
- </View>
1533
- <Text style={styles.label}>Primary Font Color</Text>
1534
- <View style={styles.colorInputContainer}>
1535
- <TextInput
1536
- style={[styles.input, { flex: 1 }]}
1537
- value={themeConfiguration.primaryFontColor}
1538
- onChangeText={value => updateThemeConfiguration('primaryFontColor', value)}
1539
- placeholder="Enter primary font color"
1540
- placeholderTextColor="#999999"
1541
- />
1542
- <View style={[styles.colorPreview, { backgroundColor: themeConfiguration.primaryFontColor }]} />
1543
- </View>
1544
- <Text style={styles.label}>Secondary Font Color</Text>
1545
- <View style={styles.colorInputContainer}>
1546
- <TextInput
1547
- style={[styles.input, { flex: 1 }]}
1548
- value={themeConfiguration.secondaryFontColor}
1549
- onChangeText={value => updateThemeConfiguration('secondaryFontColor', value)}
1550
- placeholder="Enter secondary font color"
1551
- placeholderTextColor="#999999"
1552
- />
1553
- <View style={[styles.colorPreview, { backgroundColor: themeConfiguration.secondaryFontColor }]} />
1554
- </View>
1555
- <Text style={styles.label}>Primary Button Background Color</Text>
1556
- <View style={styles.colorInputContainer}>
1557
- <TextInput
1558
- style={[styles.input, { flex: 1 }]}
1559
- value={themeConfiguration.primaryButtonBackgroundColor}
1560
- onChangeText={value => updateThemeConfiguration('primaryButtonBackgroundColor', value)}
1561
- placeholder="Enter primary button background color"
1562
- placeholderTextColor="#999999"
1563
- />
1564
- <View style={[styles.colorPreview, { backgroundColor: themeConfiguration.primaryButtonBackgroundColor }]} />
1565
- </View>
1566
- <Text style={styles.label}>Primary Button Hover Color</Text>
1567
- <View style={styles.colorInputContainer}>
1568
- <TextInput
1569
- style={[styles.input, { flex: 1 }]}
1570
- value={themeConfiguration.primaryButtonHoverColor}
1571
- onChangeText={value => updateThemeConfiguration('primaryButtonHoverColor', value)}
1572
- placeholder="Enter primary button hover color"
1573
- placeholderTextColor="#999999"
1574
- />
1575
- <View style={[styles.colorPreview, { backgroundColor: themeConfiguration.primaryButtonHoverColor }]} />
1576
- </View>
1577
- <Text style={styles.label}>Primary Button Font Color</Text>
1578
- <View style={styles.colorInputContainer}>
1579
- <TextInput
1580
- style={[styles.input, { flex: 1 }]}
1581
- value={themeConfiguration.primaryButtonFontColor}
1582
- onChangeText={value => updateThemeConfiguration('primaryButtonFontColor', value)}
1583
- placeholder="Enter primary button font color"
1584
- placeholderTextColor="#999999"
1585
- />
1586
- <View style={[styles.colorPreview, { backgroundColor: themeConfiguration.primaryButtonFontColor }]} />
1587
- </View>
1588
- <Text style={styles.label}>Secondary Button Background Color</Text>
1589
- <View style={styles.colorInputContainer}>
1590
- <TextInput
1591
- style={[styles.input, { flex: 1 }]}
1592
- value={themeConfiguration.secondaryButtonBackgroundColor}
1593
- onChangeText={value => updateThemeConfiguration('secondaryButtonBackgroundColor', value)}
1594
- placeholder="Enter secondary button background color"
1595
- placeholderTextColor="#999999"
1596
- />
1597
- <View style={[styles.colorPreview, { backgroundColor: themeConfiguration.secondaryButtonBackgroundColor }]} />
1598
- </View>
1599
- <Text style={styles.label}>Secondary Button Hover Color</Text>
1600
- <View style={styles.colorInputContainer}>
1601
- <TextInput
1602
- style={[styles.input, { flex: 1 }]}
1603
- value={themeConfiguration.secondaryButtonHoverColor}
1604
- onChangeText={value => updateThemeConfiguration('secondaryButtonHoverColor', value)}
1605
- placeholder="Enter secondary button hover color"
1606
- placeholderTextColor="#999999"
1607
- />
1608
- <View style={[styles.colorPreview, { backgroundColor: themeConfiguration.secondaryButtonHoverColor }]} />
1609
- </View>
1610
- <Text style={styles.label}>Secondary Button Font Color</Text>
1611
- <View style={styles.colorInputContainer}>
1612
- <TextInput
1613
- style={[styles.input, { flex: 1 }]}
1614
- value={themeConfiguration.secondaryButtonFontColor}
1615
- onChangeText={value => updateThemeConfiguration('secondaryButtonFontColor', value)}
1616
- placeholder="Enter secondary button font color"
1617
- placeholderTextColor="#999999"
1618
- />
1619
- <View style={[styles.colorPreview, { backgroundColor: themeConfiguration.secondaryButtonFontColor }]} />
1620
- </View>
1621
- <Text style={styles.label}>Border Radius</Text>
1622
- <TextInput
1623
- style={styles.input}
1624
- value={themeConfiguration.borderRadius}
1625
- onChangeText={value => updateThemeConfiguration('borderRadius', value)}
1626
- placeholder="Enter border radius"
1627
- placeholderTextColor="#999999"
1628
- />
1629
- <Text style={styles.label}>Font Size</Text>
1630
- <TextInput
1631
- style={styles.input}
1632
- value={themeConfiguration.fontSize}
1633
- onChangeText={value => updateThemeConfiguration('fontSize', value)}
1634
- placeholder="Enter font size"
1635
- placeholderTextColor="#999999"
1636
- />
1637
-
1638
- </>
1639
- )}
1640
-
1641
- {/* GrailPay Parameters */}
1642
- <Text style={styles.sectionTitle}>GrailPay Parameters</Text>
1643
- <Text style={styles.label}>Role</Text>
1644
- <TextInput
1645
- style={styles.input}
1646
- value={grailPayParams.role}
1647
- onChangeText={value => updateGrailPayParams('role', value)}
1648
- placeholder="Enter role"
1649
- placeholderTextColor="#999999"
1650
- />
1651
- <Text style={styles.label}>Timeout</Text>
1652
- <TextInput
1653
- style={styles.input}
1654
- value={String(grailPayParams.timeout)}
1655
- onChangeText={value => updateGrailPayParams('timeout', parseInt(value) || 10)}
1656
- placeholder="Enter timeout"
1657
- placeholderTextColor="#999999"
1658
- />
1659
- <Text style={styles.label}>Is Sandbox</Text>
1660
- <Switch
1661
- value={grailPayParams.isSandbox}
1662
- onValueChange={value => updateGrailPayParams('isSandbox', value)}
1663
- trackColor={{ false: '#ccc', true: '#2563EB' }}
1664
- thumbColor={grailPayParams.isSandbox ? '#fff' : '#f4f3f4'}
1665
- />
1666
- <Text style={styles.label}>Branding Name</Text>
1667
- <TextInput
1668
- style={styles.input}
1669
- value={grailPayParams.brandingName}
1670
- onChangeText={value => updateGrailPayParams('brandingName', value)}
1671
- placeholder="Enter branding name"
1672
- placeholderTextColor="#999999"
1673
- />
1674
- <Text style={styles.label}>Finder Subtitle</Text>
1675
- <TextInput
1676
- style={styles.input}
1677
- value={grailPayParams.finderSubtitle}
1678
- onChangeText={value => updateGrailPayParams('finderSubtitle', value)}
1679
- placeholder="Enter finder subtitle"
1680
- placeholderTextColor="#999999"
1681
- />
1682
- <Text style={styles.label}>Search Placeholder</Text>
1683
- <TextInput
1684
- style={styles.input}
1685
- value={grailPayParams.searchPlaceholder}
1686
- onChangeText={value => updateGrailPayParams('searchPlaceholder', value)}
1687
- placeholder="Enter search placeholder"
1688
- placeholderTextColor="#999999"
1689
- />
1690
-
1691
- {/* Recurring Data */}
1692
- <Text style={styles.sectionTitle}>Recurring Data</Text>
1693
- <Text style={styles.label}>Allow Cycles (Minimum 2)</Text>
1694
- <TextInput
1695
- style={styles.input}
1696
- value={String(recurringData.allowCycles)}
1697
- onChangeText={value => updateRecurringData('allowCycles', parseInt(value) || 2)}
1698
- placeholder="Enter allowed cycles"
1699
- placeholderTextColor="#999999"
1700
- />
1701
- <Text style={styles.label}>Intervals</Text>
1702
- <View style={styles.buttonGroup}>
1703
- <FilledButton
1704
- title="Daily"
1705
- onPress={() => toggleInterval('daily')}
1706
- disabled={loading}
1707
- style={[
1708
- { flex: 1 },
1709
- recurringData.intervals.includes('daily')
1710
- ? { backgroundColor: '#2563EB' }
1711
- : { backgroundColor: '#E5E7EB' }
1712
- ]}
1713
- textStyle={[
1714
- { fontSize: 14 },
1715
- recurringData.intervals.includes('daily')
1716
- ? { color: '#fff' }
1717
- : { color: '#374151' }
1718
- ]}
1719
- />
1720
- <FilledButton
1721
- title="Weekly"
1722
- onPress={() => toggleInterval('weekly')}
1723
- disabled={loading}
1724
- style={[
1725
- { flex: 1 },
1726
- recurringData.intervals.includes('weekly')
1727
- ? { backgroundColor: '#2563EB' }
1728
- : { backgroundColor: '#E5E7EB' }
1729
- ]}
1730
- textStyle={[
1731
- { fontSize: 14 },
1732
- recurringData.intervals.includes('weekly')
1733
- ? { color: '#fff' }
1734
- : { color: '#374151' }
1735
- ]}
1736
- />
1737
- <FilledButton
1738
- title="Monthly"
1739
- onPress={() => toggleInterval('monthly')}
1740
- disabled={loading}
1741
- style={[
1742
- { flex: 1 },
1743
- recurringData.intervals.includes('monthly')
1744
- ? { backgroundColor: '#2563EB' }
1745
- : { backgroundColor: '#E5E7EB' }
1746
- ]}
1747
- textStyle={[
1748
- { fontSize: 14 },
1749
- recurringData.intervals.includes('monthly')
1750
- ? { color: '#fff' }
1751
- : { color: '#374151' }
1752
- ]}
1753
- />
1754
- </View>
1755
- <Text style={styles.label}>Recurring Start Type</Text>
1756
- <Dropdown
1757
- value={recurringData.recurringStartType}
1758
- onValueChange={value => updateRecurringData('recurringStartType', value)}
1759
- options={Platform.OS === 'android' ? ['Custom', 'Fixed'] : ['custom', 'fixed']}
1760
- placeholder="Select start type"
1761
- />
1762
- <Text style={styles.label}>Recurring Start Date</Text>
1763
- <TextInput
1764
- style={[styles.input, styles.dateInput]}
1765
- value={recurringData.recurringStartDate}
1766
- onChangeText={value => updateRecurringData('recurringStartDate', value)}
1767
- placeholder="MM/DD/YYYY"
1768
- placeholderTextColor="#999999"
1769
- returnKeyType="done"
1770
- onSubmitEditing={() => Keyboard.dismiss()}
1771
- />
1772
- </>
1773
- )}
1774
-
1775
- <Text style={styles.sectionTitle}>SDK Response</Text>
1776
- <Text selectable style={styles.result}>{result || 'No response yet'}</Text>
1777
- </ScrollView>
1778
- </KeyboardAvoidingView>
1779
- );
1780
-
1781
- };
1782
-
1783
- export default App;
1784
-
1785
- const styles = StyleSheet.create({
1786
- container: { flex: 1, backgroundColor: '#F9FAFB' },
1787
- scrollContent: { flexGrow: 1, padding: 20 },
1788
- title: {
1789
- fontSize: 24,
1790
- fontWeight: 'bold',
1791
- marginBottom: 16,
1792
- marginTop: 50,
1793
- textAlign: 'center',
1794
- },
1795
- sectionTitle: {
1796
- fontSize: 20,
1797
- fontWeight: 'bold',
1798
- marginTop: 20,
1799
- marginBottom: 10,
1800
- },
1801
- subsectionTitle: {
1802
- fontSize: 16,
1803
- fontWeight: '600',
1804
- marginTop: 15,
1805
- marginBottom: 8,
1806
- color: '#374151',
1807
- },
1808
- input: {
1809
- height: 40,
1810
- borderColor: '#ccc',
1811
- borderWidth: 1,
1812
- borderRadius: 5,
1813
- paddingHorizontal: 10,
1814
- marginBottom: 10,
1815
- backgroundColor: '#FFFFFF',
1816
- color: '#000000',
1817
- },
1818
- dateInput: {
1819
- backgroundColor: '#fff',
1820
- shadowColor: '#000',
1821
- shadowOffset: {
1822
- width: 0,
1823
- height: 1,
1824
- },
1825
- shadowOpacity: 0.1,
1826
- shadowRadius: 2,
1827
- elevation: 2,
1828
- },
1829
- pickerContainer: {
1830
- marginBottom: 20,
1831
- },
1832
- toggleContainer: {
1833
- flexDirection: 'row',
1834
- justifyContent: 'space-between',
1835
- alignItems: 'center',
1836
- marginBottom: 10,
1837
- },
1838
- label: {
1839
- fontSize: 16,
1840
- fontWeight: '500',
1841
- marginBottom: 5,
1842
- },
1843
- buttonGroup: {
1844
- flexDirection: 'row',
1845
- justifyContent: 'space-between',
1846
- marginBottom: 20,
1847
- marginTop: 20,
1848
- gap: 10,
1849
-
1850
- },
1851
- result: {
1852
- fontSize: 14,
1853
- fontFamily: 'monospace',
1854
- color: '#333',
1855
- marginTop: 20,
1856
- },
1857
- environmentButton: {
1858
- flex: 1,
1859
- paddingVertical: 10,
1860
- paddingHorizontal: 20,
1861
- borderRadius: 5,
1862
- marginHorizontal: 5,
1863
- alignItems: 'center',
1864
- justifyContent: 'center',
1865
- },
1866
- buttonText: {
1867
- color: '#fff',
1868
- fontSize: 16,
1869
- fontWeight: '500',
1870
- },
1871
- inputContainer: {
1872
- flexDirection: 'row',
1873
- alignItems: 'center',
1874
- borderColor: '#ccc',
1875
- borderWidth: 1,
1876
- borderRadius: 5,
1877
- marginBottom: 10,
1878
- },
1879
- eyeButton: {
1880
- padding: 10,
1881
- },
1882
- eyeIcon: {
1883
- fontSize: 20,
1884
- color: '#333',
1885
- },
1886
- // New styles for FilledButton
1887
- filledButton: {
1888
- flex: 1,
1889
- paddingVertical: 10,
1890
- paddingHorizontal: 20,
1891
- borderRadius: 5,
1892
- marginHorizontal: 5,
1893
- alignItems: 'center',
1894
- justifyContent: 'center',
1895
- backgroundColor: '#2563EB', // Primary color for filled buttons
1896
- },
1897
- filledButtonDisabled: {
1898
- backgroundColor: '#ccc',
1899
- opacity: 0.7,
1900
- },
1901
- filledButtonText: {
1902
- color: '#fff',
1903
- fontSize: 16,
1904
- fontWeight: '500',
1905
- },
1906
- filledButtonTextDisabled: {
1907
- color: '#888',
1908
- },
1909
- debugText: {
1910
- fontSize: 12,
1911
- color: '#666',
1912
- fontStyle: 'italic',
1913
- marginBottom: 5,
1914
- },
1915
- // Dropdown Styles
1916
- dropdownContainer: {
1917
- position: 'relative',
1918
- marginBottom: 20,
1919
- zIndex: 9998,
1920
- },
1921
- dropdownButton: {
1922
- height: 40,
1923
- borderColor: '#ccc',
1924
- borderWidth: 1,
1925
- borderRadius: 5,
1926
- paddingHorizontal: 10,
1927
- flexDirection: 'row',
1928
- justifyContent: 'space-between',
1929
- alignItems: 'center',
1930
- backgroundColor: '#fff',
1931
- },
1932
- dropdownButtonText: {
1933
- fontSize: 16,
1934
- color: '#333',
1935
- },
1936
- dropdownArrow: {
1937
- fontSize: 12,
1938
- color: '#666',
1939
- },
1940
- dropdownOptions: {
1941
- position: 'absolute',
1942
- top: 45,
1943
- left: 0,
1944
- right: 0,
1945
- backgroundColor: '#fff',
1946
- borderColor: '#ccc',
1947
- borderWidth: 1,
1948
- borderRadius: 5,
1949
- zIndex: 9999,
1950
- maxHeight: 200,
1951
- elevation: 5,
1952
- shadowColor: '#000',
1953
- shadowOffset: {
1954
- width: 0,
1955
- height: 2,
1956
- },
1957
- shadowOpacity: 0.25,
1958
- shadowRadius: 3.84,
1959
- },
1960
- dropdownScrollView: {
1961
- maxHeight: 180,
1962
- },
1963
- dropdownOption: {
1964
- paddingVertical: 12,
1965
- paddingHorizontal: 15,
1966
- borderBottomWidth: 1,
1967
- borderBottomColor: '#f0f0f0',
1968
- backgroundColor: '#fff',
1969
- },
1970
- dropdownOptionText: {
1971
- fontSize: 16,
1972
- color: '#333',
1973
- },
1974
- // Color preview styles
1975
- colorInputContainer: {
1976
- flexDirection: 'row',
1977
- alignItems: 'center',
1978
- marginBottom: 10,
1979
- gap: 10,
1980
- },
1981
- colorPreview: {
1982
- width: 30,
1983
- height: 30,
1984
- borderRadius: 4,
1985
- borderWidth: 1,
1986
- borderColor: '#ccc',
1987
- },
1988
- phoneRow: {
1989
- flexDirection: 'row',
1990
- alignItems: 'center',
1991
- marginBottom: 10,
1992
- gap: 8,
1993
- },
1994
- countryCodeInput: {
1995
- width: 70,
1996
- height: 40,
1997
- borderColor: '#ccc',
1998
- borderWidth: 1,
1999
- borderRadius: 5,
2000
- paddingHorizontal: 10,
2001
- backgroundColor: '#FFFFFF',
2002
- color: '#000000',
2003
- textAlign: 'center',
2004
- alignSelf: 'stretch',
2005
- marginBottom: 0,
2006
- },
2007
- });
2008
-
2009
- ```
325
+ ## Notes
2010
326
 
2011
- You can send `null` if billing info not available.
327
+ - **Billing Info**: You can send `null` for billing info if it is not available.
328
+ - **Environment Configuration**: The SDK supports `sandbox` and `staging` environments. Ensure you configure the correct API keys for each environment.
329
+ - **Payment Methods**: The SDK supports `card` and `ach` payment methods, with options to toggle them in the UI.
330
+ - **Recurring Payments**: Ensure at least one interval (`daily`, `weekly`, `monthly`) is selected when enabling recurring payments.
331
+ - **Theming**: Customize the appearance using `themeConfiguration` (iOS) or `androidConfig.appearanceSettings` (Android) to match your app's branding.
2012
332
 
333
+ ## Troubleshooting
2013
334
 
2014
-
335
+ - **iOS Bridge Initialization**: If you see "Failed to retrieve EasyMerchantSdkPlugin instance" in the console, ensure the `easymerchantsdk` pod is correctly installed and linked.
336
+ - **Android Payment Issues**: Verify that `paymentMethod` is correctly formatted (`['card', 'ach']`) and that API keys are valid.
337
+ - **Pod Installation**: If `pod install` fails, ensure Ruby 3.2.8 is installed and run `pod install --repo-update`.