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