expo-openpay 0.1.0

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 (225) hide show
  1. package/.eslintrc.js +5 -0
  2. package/LICENSE +21 -0
  3. package/README.md +87 -0
  4. package/android/build.gradle +76 -0
  5. package/android/gradle.properties +2 -0
  6. package/android/src/main/AndroidManifest.xml +2 -0
  7. package/android/src/main/java/expo/modules/openpay/ExpoOpenpayModule.kt +127 -0
  8. package/android/src/main/java/expo/modules/openpay/ExpoOpenpayView.kt +30 -0
  9. package/android/src/main/java/mx/openpay/android/BuildConfig.java +8 -0
  10. package/android/src/main/java/mx/openpay/android/DeviceCollectorDefaultImpl.java +70 -0
  11. package/android/src/main/java/mx/openpay/android/JavaScriptInterface.java +19 -0
  12. package/android/src/main/java/mx/openpay/android/OpCountry.java +24 -0
  13. package/android/src/main/java/mx/openpay/android/OpenPayResult.java +35 -0
  14. package/android/src/main/java/mx/openpay/android/Openpay.java +98 -0
  15. package/android/src/main/java/mx/openpay/android/OpenpayUrls.java +11 -0
  16. package/android/src/main/java/mx/openpay/android/OperationCallBack.java +13 -0
  17. package/android/src/main/java/mx/openpay/android/OperationResult.java +14 -0
  18. package/android/src/main/java/mx/openpay/android/exceptions/OpenpayServiceException.java +93 -0
  19. package/android/src/main/java/mx/openpay/android/exceptions/ServiceUnavailableException.java +23 -0
  20. package/android/src/main/java/mx/openpay/android/model/Address.java +123 -0
  21. package/android/src/main/java/mx/openpay/android/model/Card.java +219 -0
  22. package/android/src/main/java/mx/openpay/android/model/Token.java +43 -0
  23. package/android/src/main/java/mx/openpay/android/services/BaseService.java +93 -0
  24. package/android/src/main/java/mx/openpay/android/services/ServicesFactory.java +47 -0
  25. package/android/src/main/java/mx/openpay/android/services/TokenService.java +19 -0
  26. package/android/src/main/java/mx/openpay/android/validation/CardType.java +9 -0
  27. package/android/src/main/java/mx/openpay/android/validation/CardValidator.java +105 -0
  28. package/android/src/main/java/mx/openpay/android/validation/LuhnValidator.java +34 -0
  29. package/build/ExpoOpenpay.types.d.ts +42 -0
  30. package/build/ExpoOpenpay.types.d.ts.map +1 -0
  31. package/build/ExpoOpenpay.types.js +2 -0
  32. package/build/ExpoOpenpay.types.js.map +1 -0
  33. package/build/ExpoOpenpayModule.d.ts +10 -0
  34. package/build/ExpoOpenpayModule.d.ts.map +1 -0
  35. package/build/ExpoOpenpayModule.js +6 -0
  36. package/build/ExpoOpenpayModule.js.map +1 -0
  37. package/build/ExpoOpenpayModule.web.d.ts +10 -0
  38. package/build/ExpoOpenpayModule.web.d.ts.map +1 -0
  39. package/build/ExpoOpenpayModule.web.js +12 -0
  40. package/build/ExpoOpenpayModule.web.js.map +1 -0
  41. package/build/ExpoOpenpayView.d.ts +4 -0
  42. package/build/ExpoOpenpayView.d.ts.map +1 -0
  43. package/build/ExpoOpenpayView.js +7 -0
  44. package/build/ExpoOpenpayView.js.map +1 -0
  45. package/build/ExpoOpenpayView.web.d.ts +3 -0
  46. package/build/ExpoOpenpayView.web.d.ts.map +1 -0
  47. package/build/ExpoOpenpayView.web.js +5 -0
  48. package/build/ExpoOpenpayView.web.js.map +1 -0
  49. package/build/assets/AmexLogo.d.ts +4 -0
  50. package/build/assets/AmexLogo.d.ts.map +1 -0
  51. package/build/assets/AmexLogo.js +9 -0
  52. package/build/assets/AmexLogo.js.map +1 -0
  53. package/build/assets/MCLogo.d.ts +4 -0
  54. package/build/assets/MCLogo.d.ts.map +1 -0
  55. package/build/assets/MCLogo.js +9 -0
  56. package/build/assets/MCLogo.js.map +1 -0
  57. package/build/assets/VisaLogo.d.ts +4 -0
  58. package/build/assets/VisaLogo.d.ts.map +1 -0
  59. package/build/assets/VisaLogo.js +6 -0
  60. package/build/assets/VisaLogo.js.map +1 -0
  61. package/build/assets/index.d.ts +4 -0
  62. package/build/assets/index.d.ts.map +1 -0
  63. package/build/assets/index.js +4 -0
  64. package/build/assets/index.js.map +1 -0
  65. package/build/components/OPCardForm.d.ts +33 -0
  66. package/build/components/OPCardForm.d.ts.map +1 -0
  67. package/build/components/OPCardForm.js +120 -0
  68. package/build/components/OPCardForm.js.map +1 -0
  69. package/build/components/OPCardNumberInput.d.ts +17 -0
  70. package/build/components/OPCardNumberInput.d.ts.map +1 -0
  71. package/build/components/OPCardNumberInput.js +60 -0
  72. package/build/components/OPCardNumberInput.js.map +1 -0
  73. package/build/components/OPCvv2Input.d.ts +17 -0
  74. package/build/components/OPCvv2Input.d.ts.map +1 -0
  75. package/build/components/OPCvv2Input.js +13 -0
  76. package/build/components/OPCvv2Input.js.map +1 -0
  77. package/build/components/OPExpInput.d.ts +17 -0
  78. package/build/components/OPExpInput.d.ts.map +1 -0
  79. package/build/components/OPExpInput.js +22 -0
  80. package/build/components/OPExpInput.js.map +1 -0
  81. package/build/components/OPExpMonthInput.d.ts +17 -0
  82. package/build/components/OPExpMonthInput.d.ts.map +1 -0
  83. package/build/components/OPExpMonthInput.js +6 -0
  84. package/build/components/OPExpMonthInput.js.map +1 -0
  85. package/build/components/OPExpYearInput.d.ts +17 -0
  86. package/build/components/OPExpYearInput.d.ts.map +1 -0
  87. package/build/components/OPExpYearInput.js +6 -0
  88. package/build/components/OPExpYearInput.js.map +1 -0
  89. package/build/components/OPHolderNameInput.d.ts +17 -0
  90. package/build/components/OPHolderNameInput.d.ts.map +1 -0
  91. package/build/components/OPHolderNameInput.js +6 -0
  92. package/build/components/OPHolderNameInput.js.map +1 -0
  93. package/build/components/forms/ErrorMessage.d.ts +7 -0
  94. package/build/components/forms/ErrorMessage.d.ts.map +1 -0
  95. package/build/components/forms/ErrorMessage.js +12 -0
  96. package/build/components/forms/ErrorMessage.js.map +1 -0
  97. package/build/components/forms/FormField.d.ts +34 -0
  98. package/build/components/forms/FormField.d.ts.map +1 -0
  99. package/build/components/forms/FormField.js +33 -0
  100. package/build/components/forms/FormField.js.map +1 -0
  101. package/build/components/forms/SubmitButton.d.ts +11 -0
  102. package/build/components/forms/SubmitButton.d.ts.map +1 -0
  103. package/build/components/forms/SubmitButton.js +45 -0
  104. package/build/components/forms/SubmitButton.js.map +1 -0
  105. package/build/components/forms/Text.d.ts +7 -0
  106. package/build/components/forms/Text.d.ts.map +1 -0
  107. package/build/components/forms/Text.js +12 -0
  108. package/build/components/forms/Text.js.map +1 -0
  109. package/build/components/forms/TextInput.d.ts +30 -0
  110. package/build/components/forms/TextInput.d.ts.map +1 -0
  111. package/build/components/forms/TextInput.js +195 -0
  112. package/build/components/forms/TextInput.js.map +1 -0
  113. package/build/components/forms/index.d.ts +4 -0
  114. package/build/components/forms/index.d.ts.map +1 -0
  115. package/build/components/forms/index.js +5 -0
  116. package/build/components/forms/index.js.map +1 -0
  117. package/build/components/index.d.ts +8 -0
  118. package/build/components/index.d.ts.map +1 -0
  119. package/build/components/index.js +8 -0
  120. package/build/components/index.js.map +1 -0
  121. package/build/constants/Colors.d.ts +27 -0
  122. package/build/constants/Colors.d.ts.map +1 -0
  123. package/build/constants/Colors.js +27 -0
  124. package/build/constants/Colors.js.map +1 -0
  125. package/build/constants/Styles.d.ts +9 -0
  126. package/build/constants/Styles.d.ts.map +1 -0
  127. package/build/constants/Styles.js +9 -0
  128. package/build/constants/Styles.js.map +1 -0
  129. package/build/context/FormContext.d.ts +22 -0
  130. package/build/context/FormContext.d.ts.map +1 -0
  131. package/build/context/FormContext.js +63 -0
  132. package/build/context/FormContext.js.map +1 -0
  133. package/build/context/FormThemeContext.d.ts +20 -0
  134. package/build/context/FormThemeContext.d.ts.map +1 -0
  135. package/build/context/FormThemeContext.js +21 -0
  136. package/build/context/FormThemeContext.js.map +1 -0
  137. package/build/context/index.d.ts +4 -0
  138. package/build/context/index.d.ts.map +1 -0
  139. package/build/context/index.js +3 -0
  140. package/build/context/index.js.map +1 -0
  141. package/build/index.d.ts +6 -0
  142. package/build/index.d.ts.map +1 -0
  143. package/build/index.js +18 -0
  144. package/build/index.js.map +1 -0
  145. package/build/utility/cardBranding.d.ts +2 -0
  146. package/build/utility/cardBranding.d.ts.map +1 -0
  147. package/build/utility/cardBranding.js +2 -0
  148. package/build/utility/cardBranding.js.map +1 -0
  149. package/build/utility/formatting.d.ts +4 -0
  150. package/build/utility/formatting.d.ts.map +1 -0
  151. package/build/utility/formatting.js +20 -0
  152. package/build/utility/formatting.js.map +1 -0
  153. package/expo-module.config.json +9 -0
  154. package/ios/ExpoOpenpay.podspec +30 -0
  155. package/ios/Frameworks/OpenpayKit.xcframework/Info.plist +40 -0
  156. package/ios/Frameworks/OpenpayKit.xcframework/ios-arm64/OpenpayKit.framework/Headers/OpenpayKit-Swift.h +274 -0
  157. package/ios/Frameworks/OpenpayKit.xcframework/ios-arm64/OpenpayKit.framework/Info.plist +0 -0
  158. package/ios/Frameworks/OpenpayKit.xcframework/ios-arm64/OpenpayKit.framework/Modules/OpenpayKit.swiftmodule/arm64-apple-ios.abi.json +5179 -0
  159. package/ios/Frameworks/OpenpayKit.xcframework/ios-arm64/OpenpayKit.framework/Modules/OpenpayKit.swiftmodule/arm64-apple-ios.private.swiftinterface +127 -0
  160. package/ios/Frameworks/OpenpayKit.xcframework/ios-arm64/OpenpayKit.framework/Modules/OpenpayKit.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
  161. package/ios/Frameworks/OpenpayKit.xcframework/ios-arm64/OpenpayKit.framework/Modules/OpenpayKit.swiftmodule/arm64-apple-ios.swiftinterface +127 -0
  162. package/ios/Frameworks/OpenpayKit.xcframework/ios-arm64/OpenpayKit.framework/Modules/module.modulemap +4 -0
  163. package/ios/Frameworks/OpenpayKit.xcframework/ios-arm64/OpenpayKit.framework/OpenpayKit +0 -0
  164. package/ios/Frameworks/OpenpayKit.xcframework/ios-arm64/OpenpayKit.framework/_CodeSignature/CodeResources +287 -0
  165. package/ios/Frameworks/OpenpayKit.xcframework/ios-arm64/OpenpayKit.framework/en.lproj/CardView.nib +0 -0
  166. package/ios/Frameworks/OpenpayKit.xcframework/ios-arm64/OpenpayKit.framework/en.lproj/Localizable.strings +0 -0
  167. package/ios/Frameworks/OpenpayKit.xcframework/ios-arm64/OpenpayKit.framework/es-MX.lproj/CardView.nib +0 -0
  168. package/ios/Frameworks/OpenpayKit.xcframework/ios-arm64/OpenpayKit.framework/es-MX.lproj/Localizable.strings +0 -0
  169. package/ios/Frameworks/OpenpayKit.xcframework/ios-arm64/OpenpayKit.framework/es.lproj/CardView.nib +0 -0
  170. package/ios/Frameworks/OpenpayKit.xcframework/ios-arm64/OpenpayKit.framework/es.lproj/Localizable.strings +0 -0
  171. package/ios/Frameworks/OpenpayKit.xcframework/ios-arm64_x86_64-simulator/OpenpayKit.framework/Headers/OpenpayKit-Swift.h +544 -0
  172. package/ios/Frameworks/OpenpayKit.xcframework/ios-arm64_x86_64-simulator/OpenpayKit.framework/Info.plist +0 -0
  173. package/ios/Frameworks/OpenpayKit.xcframework/ios-arm64_x86_64-simulator/OpenpayKit.framework/Modules/OpenpayKit.swiftmodule/arm64-apple-ios-simulator.abi.json +5179 -0
  174. package/ios/Frameworks/OpenpayKit.xcframework/ios-arm64_x86_64-simulator/OpenpayKit.framework/Modules/OpenpayKit.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +127 -0
  175. package/ios/Frameworks/OpenpayKit.xcframework/ios-arm64_x86_64-simulator/OpenpayKit.framework/Modules/OpenpayKit.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
  176. package/ios/Frameworks/OpenpayKit.xcframework/ios-arm64_x86_64-simulator/OpenpayKit.framework/Modules/OpenpayKit.swiftmodule/arm64-apple-ios-simulator.swiftinterface +127 -0
  177. package/ios/Frameworks/OpenpayKit.xcframework/ios-arm64_x86_64-simulator/OpenpayKit.framework/Modules/OpenpayKit.swiftmodule/x86_64-apple-ios-simulator.abi.json +5179 -0
  178. package/ios/Frameworks/OpenpayKit.xcframework/ios-arm64_x86_64-simulator/OpenpayKit.framework/Modules/OpenpayKit.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +127 -0
  179. package/ios/Frameworks/OpenpayKit.xcframework/ios-arm64_x86_64-simulator/OpenpayKit.framework/Modules/OpenpayKit.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
  180. package/ios/Frameworks/OpenpayKit.xcframework/ios-arm64_x86_64-simulator/OpenpayKit.framework/Modules/OpenpayKit.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +127 -0
  181. package/ios/Frameworks/OpenpayKit.xcframework/ios-arm64_x86_64-simulator/OpenpayKit.framework/Modules/module.modulemap +4 -0
  182. package/ios/Frameworks/OpenpayKit.xcframework/ios-arm64_x86_64-simulator/OpenpayKit.framework/OpenpayKit +0 -0
  183. package/ios/Frameworks/OpenpayKit.xcframework/ios-arm64_x86_64-simulator/OpenpayKit.framework/_CodeSignature/CodeResources +342 -0
  184. package/ios/Frameworks/OpenpayKit.xcframework/ios-arm64_x86_64-simulator/OpenpayKit.framework/en.lproj/CardView.nib +0 -0
  185. package/ios/Frameworks/OpenpayKit.xcframework/ios-arm64_x86_64-simulator/OpenpayKit.framework/en.lproj/Localizable.strings +0 -0
  186. package/ios/Frameworks/OpenpayKit.xcframework/ios-arm64_x86_64-simulator/OpenpayKit.framework/es-MX.lproj/CardView.nib +0 -0
  187. package/ios/Frameworks/OpenpayKit.xcframework/ios-arm64_x86_64-simulator/OpenpayKit.framework/es-MX.lproj/Localizable.strings +0 -0
  188. package/ios/Frameworks/OpenpayKit.xcframework/ios-arm64_x86_64-simulator/OpenpayKit.framework/es.lproj/CardView.nib +0 -0
  189. package/ios/Frameworks/OpenpayKit.xcframework/ios-arm64_x86_64-simulator/OpenpayKit.framework/es.lproj/Localizable.strings +0 -0
  190. package/ios/src/ExpoOpenpayModule.swift +105 -0
  191. package/ios/src/ExpoOpenpayView.swift +38 -0
  192. package/package.json +46 -0
  193. package/src/ExpoOpenpay.types.ts +47 -0
  194. package/src/ExpoOpenpayModule.ts +29 -0
  195. package/src/ExpoOpenpayModule.web.ts +15 -0
  196. package/src/ExpoOpenpayView.tsx +11 -0
  197. package/src/ExpoOpenpayView.web.tsx +5 -0
  198. package/src/assets/AmexLogo.tsx +24 -0
  199. package/src/assets/MCLogo.tsx +19 -0
  200. package/src/assets/VisaLogo.tsx +11 -0
  201. package/src/assets/index.ts +3 -0
  202. package/src/assets/photos/discover_logo.png +0 -0
  203. package/src/components/OPCardForm.tsx +303 -0
  204. package/src/components/OPCardNumberInput.tsx +126 -0
  205. package/src/components/OPCvv2Input.tsx +62 -0
  206. package/src/components/OPExpInput.tsx +74 -0
  207. package/src/components/OPExpMonthInput.tsx +55 -0
  208. package/src/components/OPExpYearInput.tsx +55 -0
  209. package/src/components/OPHolderNameInput.tsx +53 -0
  210. package/src/components/forms/ErrorMessage.tsx +19 -0
  211. package/src/components/forms/FormField.tsx +96 -0
  212. package/src/components/forms/SubmitButton.tsx +81 -0
  213. package/src/components/forms/Text.tsx +20 -0
  214. package/src/components/forms/TextInput.tsx +329 -0
  215. package/src/components/forms/index.ts +4 -0
  216. package/src/components/index.ts +7 -0
  217. package/src/constants/Colors.ts +26 -0
  218. package/src/constants/Styles.ts +9 -0
  219. package/src/context/FormContext.tsx +109 -0
  220. package/src/context/FormThemeContext.tsx +55 -0
  221. package/src/context/index.ts +3 -0
  222. package/src/index.ts +19 -0
  223. package/src/utility/cardBranding.ts +0 -0
  224. package/src/utility/formatting.ts +23 -0
  225. package/tsconfig.json +9 -0
@@ -0,0 +1,329 @@
1
+ import { useEffect, useRef, useState } from "react";
2
+ import {
3
+ Animated,
4
+ Image,
5
+ StyleProp,
6
+ StyleSheet,
7
+ TextInput,
8
+ TextInputProps,
9
+ TextStyle,
10
+ View,
11
+ ViewStyle,
12
+ } from "react-native";
13
+ import { FontAwesome6, MaterialCommunityIcons } from "@expo/vector-icons";
14
+
15
+ import Styles from "../../constants/Styles";
16
+ import { useFormThemeContext } from "../../context/FormThemeContext";
17
+ import { AmexLogo, MCLogo, VisaLogo } from "../../assets";
18
+
19
+ type StylingProps = {
20
+ formFieldStyle?: StyleProp<ViewStyle>;
21
+ inputStyle?: StyleProp<ViewStyle>;
22
+ textInputStyle?: StyleProp<TextStyle>;
23
+ };
24
+
25
+ type AppTextInputProps = TextInputProps & {
26
+ defaultStyling?: boolean;
27
+ fieldName?: string;
28
+ icon?:
29
+ | { type: "material"; name: keyof typeof MaterialCommunityIcons.glyphMap }
30
+ | { type: "fa6"; name: keyof typeof FontAwesome6.glyphMap }
31
+ | { type: "png"; name: "amex" | "discover" | "mc" | "visa" };
32
+ hasError?: boolean;
33
+ errorText?: string;
34
+ styling?: StylingProps;
35
+ withAnimatedText?: boolean;
36
+ withIcon?: boolean;
37
+ withPlaceholder?: boolean;
38
+ };
39
+
40
+ const cardIcons: Record<string, any> = {
41
+ amex: AmexLogo,
42
+ discover: require("../../assets/photos/discover_logo.png"),
43
+ mc: MCLogo,
44
+ visa: VisaLogo,
45
+ };
46
+
47
+ export default function AppTextInput({
48
+ defaultStyling,
49
+ fieldName,
50
+ icon,
51
+ onBlur,
52
+ onChangeText,
53
+ placeholder,
54
+ hasError = false,
55
+ errorText = "",
56
+ styling: { formFieldStyle = {}, inputStyle = {}, textInputStyle = {} } = {},
57
+ value,
58
+ withAnimatedText,
59
+ withIcon,
60
+ withPlaceholder,
61
+ ...otherProps
62
+ }: AppTextInputProps) {
63
+ const {
64
+ formFieldBackground,
65
+ formFieldErrorBorder,
66
+ iconColor,
67
+ iconError,
68
+ placeholderColor,
69
+ placeholderError,
70
+ textInput,
71
+ textInputCarot,
72
+ } = useFormThemeContext();
73
+
74
+ const [isFocused, setIsFocused] = useState<boolean>(false);
75
+ const [renderedIcon, setRenderedIcon] = useState(icon);
76
+
77
+ const activeAnim = useRef<Animated.CompositeAnimation | null>(null);
78
+ const animatedIsFlipped = useRef(
79
+ new Animated.Value(icon?.type === "png" ? 1 : 0)
80
+ ).current;
81
+ const animatedIsFocused = useRef(new Animated.Value(value ? 1 : 0)).current;
82
+
83
+ useEffect(() => {
84
+ Animated.timing(animatedIsFocused, {
85
+ toValue: isFocused || !!value ? 1 : 0,
86
+ duration: 200,
87
+ useNativeDriver: false,
88
+ }).start();
89
+ }, [isFocused, value, animatedIsFocused]);
90
+
91
+ const placeholderStyle: Animated.WithAnimatedObject<StyleProp<TextStyle>> = {
92
+ position: "absolute",
93
+ left: animatedIsFocused.interpolate({
94
+ inputRange: [0, 1],
95
+ outputRange: [withIcon ? 40 : 15, withIcon ? 40 : 15],
96
+ }),
97
+ top: animatedIsFocused.interpolate({
98
+ inputRange: [0, 1],
99
+ outputRange: [30, 5],
100
+ }),
101
+ fontSize: animatedIsFocused.interpolate({
102
+ inputRange: [0, 1],
103
+ outputRange: [18, 12],
104
+ }),
105
+ color: animatedIsFocused.interpolate({
106
+ inputRange: [0, 1],
107
+ outputRange: [placeholderColor, placeholderColor],
108
+ }),
109
+ };
110
+
111
+ useEffect(() => {
112
+ if (icon === renderedIcon) return;
113
+
114
+ const hasBrandIcon = icon?.type === "png";
115
+
116
+ if (activeAnim.current) {
117
+ try {
118
+ activeAnim.current.stop();
119
+ } catch (e) {
120
+ // ignore
121
+ }
122
+ activeAnim.current = null;
123
+ }
124
+
125
+ const anim = Animated.timing(animatedIsFlipped, {
126
+ toValue: hasBrandIcon ? 1 : 0,
127
+ duration: 500,
128
+ useNativeDriver: true,
129
+ });
130
+
131
+ activeAnim.current = anim;
132
+ anim.start(({ finished }) => {
133
+ activeAnim.current = null;
134
+ if (finished) {
135
+ setRenderedIcon(icon);
136
+ } else {
137
+ setRenderedIcon(icon);
138
+ }
139
+ });
140
+ }, [icon]);
141
+
142
+ const rotateY = animatedIsFlipped.interpolate({
143
+ inputRange: [0, 1],
144
+ outputRange: ["0deg", "180deg"],
145
+ });
146
+
147
+ const rotateYBack = animatedIsFlipped.interpolate({
148
+ inputRange: [0, 1],
149
+ outputRange: ["180deg", "360deg"],
150
+ });
151
+
152
+ const backImageSource =
153
+ icon?.type === "png"
154
+ ? cardIcons[icon.name]
155
+ : renderedIcon?.type === "png"
156
+ ? cardIcons[renderedIcon.name]
157
+ : null;
158
+
159
+ const Icon = backImageSource;
160
+ console.log(Icon);
161
+ return (
162
+ <View
163
+ style={
164
+ defaultStyling && [
165
+ styles.formFieldContainer,
166
+ { backgroundColor: formFieldBackground },
167
+ formFieldStyle,
168
+ hasError && { borderColor: formFieldErrorBorder },
169
+ ]
170
+ }
171
+ >
172
+ {defaultStyling && withIcon && (
173
+ <View style={styles.iconContainer}>
174
+ <View style={styles.flipContainer}>
175
+ {/* Front side: generic card icon or previously rendered icon.
176
+ We use renderedIcon so the old front stays visible until animation completes */}
177
+ <Animated.View
178
+ style={[
179
+ styles.face,
180
+ styles.front,
181
+ {
182
+ transform: [{ rotateY }],
183
+ },
184
+ ]}
185
+ >
186
+ {/* If previously renderedIcon was a material/fa6 show it; otherwise show generic credit-card */}
187
+ {renderedIcon?.type === "material" && renderedIcon?.name ? (
188
+ <MaterialCommunityIcons
189
+ name={renderedIcon.name}
190
+ size={25}
191
+ color={hasError ? iconError : iconColor}
192
+ />
193
+ ) : renderedIcon?.type === "fa6" && renderedIcon?.name ? (
194
+ <FontAwesome6
195
+ name={renderedIcon.name}
196
+ size={21}
197
+ color={hasError ? iconError : iconColor}
198
+ />
199
+ ) : (
200
+ <MaterialCommunityIcons
201
+ name="credit-card"
202
+ size={25}
203
+ color={hasError ? iconError : iconColor}
204
+ />
205
+ )}
206
+ </Animated.View>
207
+ <Animated.View
208
+ style={[
209
+ styles.face,
210
+ {
211
+ transform: [
212
+ {
213
+ rotateY: rotateYBack,
214
+ },
215
+ ],
216
+ },
217
+ ]}
218
+ >
219
+ {Icon ? (
220
+ typeof Icon === "function" ? (
221
+ <Icon width="100%" height="100%" />
222
+ ) : (
223
+ <Image
224
+ source={Icon}
225
+ style={[
226
+ styles.logo,
227
+ (icon?.name === "visa" ||
228
+ icon?.name === "discover" ||
229
+ renderedIcon?.name === "visa" ||
230
+ renderedIcon?.name === "discover") && {
231
+ width: "70%",
232
+ height: "70%",
233
+ },
234
+ ]}
235
+ />
236
+ )
237
+ ) : (
238
+ <View style={styles.logo} />
239
+ )}
240
+ </Animated.View>
241
+ </View>
242
+ </View>
243
+ )}
244
+ {withPlaceholder && withAnimatedText && (
245
+ <Animated.Text
246
+ pointerEvents="none"
247
+ style={
248
+ defaultStyling && [
249
+ placeholderStyle,
250
+ hasError && { color: placeholderError },
251
+ ]
252
+ }
253
+ >
254
+ {hasError ? errorText : placeholder}
255
+ </Animated.Text>
256
+ )}
257
+ <TextInput
258
+ onBlur={(e) => {
259
+ setIsFocused(false);
260
+ onBlur?.(e);
261
+ }}
262
+ onChangeText={onChangeText}
263
+ onFocus={() => setIsFocused(true)}
264
+ placeholder={
265
+ withPlaceholder && !withAnimatedText ? placeholder : undefined
266
+ }
267
+ placeholderTextColor={!withAnimatedText ? placeholderColor : undefined}
268
+ selectionColor={textInputCarot}
269
+ style={
270
+ defaultStyling
271
+ ? [
272
+ styles.textInput,
273
+ { color: textInput },
274
+ !withIcon && { marginLeft: 15 },
275
+ textInputStyle,
276
+ ]
277
+ : textInputStyle
278
+ }
279
+ value={value}
280
+ {...otherProps}
281
+ />
282
+ </View>
283
+ );
284
+ }
285
+
286
+ const styles = StyleSheet.create({
287
+ formFieldContainer: {
288
+ borderColor: "#3c3c3c",
289
+ borderRadius: 25,
290
+ borderWidth: 1,
291
+ flexDirection: "row",
292
+ paddingVertical: 20,
293
+ marginTop: 12,
294
+ },
295
+ iconContainer: {
296
+ marginTop: 8,
297
+ width: 40,
298
+ height: 25,
299
+ justifyContent: "center",
300
+ alignItems: "center",
301
+ padding: 1,
302
+ },
303
+ textInput: { height: 40, ...Styles.text },
304
+
305
+ // Flip animation styles
306
+ flipContainer: {
307
+ width: "100%",
308
+ height: "100%",
309
+ ...({ transform: [{ perspective: 1000 }] } as any),
310
+ },
311
+ flipCard: {
312
+ width: "100%",
313
+ height: "100%",
314
+ },
315
+ face: {
316
+ position: "absolute",
317
+ width: "100%",
318
+ height: "100%",
319
+ justifyContent: "center",
320
+ alignItems: "center",
321
+ backfaceVisibility: "hidden",
322
+ },
323
+ front: { zIndex: 2 },
324
+ logo: {
325
+ width: "100%",
326
+ height: "100%",
327
+ resizeMode: "contain",
328
+ },
329
+ });
@@ -0,0 +1,4 @@
1
+ // export { default as ErrorMessage } from "./ErrorMessage";
2
+ export { default as FormField } from "./FormField";
3
+ export { default as SubmitButton } from "./SubmitButton";
4
+ export { default as TextInput } from "./TextInput";
@@ -0,0 +1,7 @@
1
+ export { default as OPCardForm } from "./OPCardForm";
2
+ export { default as OPCardNumberInput } from "./OPCardNumberInput";
3
+ export { default as OPCvv2Input } from "./OPCvv2Input";
4
+ export { default as OPExpInput } from "./OPExpInput";
5
+ export { default as OPExpMonthInput } from "./OPExpMonthInput";
6
+ export { default as OPExpYearInput } from "./OPExpYearInput";
7
+ export { default as OPHolderNameInput } from "./OPHolderNameInput";
@@ -0,0 +1,26 @@
1
+ export const Colors = {
2
+ light: {
3
+ button: "#16c8bf",
4
+ buttonText: "#014481",
5
+ formFieldBackground: "#f8f4f4",
6
+ formFieldErrorBorder: "#fc5c65",
7
+ iconColor: "#6e6969",
8
+ iconError: "#fc5c65",
9
+ placeholderColor: "#6e6969",
10
+ placeholderError: "#fc5c65",
11
+ textInput: "#6e6969",
12
+ textInputCarot: "#014481",
13
+ },
14
+ dark: {
15
+ button: "#16c8bf",
16
+ buttonText: "#014481",
17
+ formFieldBackground: "#242526",
18
+ formFieldErrorBorder: "#fc5c65",
19
+ iconColor: "#e5e5e5",
20
+ iconError: "#fc5c65",
21
+ placeholderColor: "#6e6969",
22
+ placeholderError: "#fc5c65",
23
+ textInput: "#e5e5e5",
24
+ textInputCarot: "#014481",
25
+ },
26
+ };
@@ -0,0 +1,9 @@
1
+ import { Platform } from "react-native";
2
+
3
+ export default {
4
+ text: {
5
+ flex: 1,
6
+ fontSize: 18,
7
+ fontFamily: Platform.OS === "android" ? "Roboto" : "Avenir",
8
+ },
9
+ };
@@ -0,0 +1,109 @@
1
+ import { createContext, useContext, useState } from "react";
2
+
3
+ import { tokenizeCard } from "../ExpoOpenpayModule";
4
+ import { unformat } from "../utility/formatting";
5
+
6
+ type FormValues = Record<string, any>;
7
+ type FormErrors = Record<string, string | undefined>;
8
+ type FormTouched = Record<string, boolean>;
9
+
10
+ interface FormContextType {
11
+ errors: FormErrors;
12
+ isSubmitting: boolean;
13
+ touched: FormTouched;
14
+ values: FormValues;
15
+ handleSubmit: () => Promise<any>;
16
+ resetForm: () => void;
17
+ setFieldValue: (field: string, value: any) => void;
18
+ setFieldTouched: (field: string, touched?: boolean) => void;
19
+ setFieldError: (field: string, error: string | undefined) => void;
20
+ }
21
+
22
+ const FormContext = createContext<FormContextType | undefined>(undefined);
23
+
24
+ export const useFormContext = () => {
25
+ const context = useContext(FormContext);
26
+ if (!context) {
27
+ throw new Error("useFormContext must be used within a FormProvider");
28
+ }
29
+ return context;
30
+ };
31
+
32
+ export default function FormProvider({
33
+ children,
34
+ initialValues = {},
35
+ onSubmit,
36
+ }: {
37
+ children: React.ReactNode;
38
+ initialValues?: FormValues;
39
+ onSubmit?: () => any;
40
+ }) {
41
+ const [values, setValues] = useState<FormValues>(initialValues);
42
+ const [errors, setErrors] = useState<FormErrors>({});
43
+ const [touched, setTouched] = useState<FormTouched>({});
44
+ const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
45
+
46
+ const handleSubmit = async (): Promise<any> => {
47
+ try {
48
+ setIsSubmitting(true);
49
+
50
+ if (onSubmit) onSubmit();
51
+
52
+ if (values["exp"]) {
53
+ values["expMonth"] = values["exp"].slice(0, 2);
54
+ values["expYear"] = values["exp"].slice(3);
55
+ }
56
+
57
+ await tokenizeCard(
58
+ unformat(values["cardNumber"]),
59
+ values["cardHolderName"],
60
+ values["expYear"],
61
+ values["expMonth"],
62
+ values["cvc"]
63
+ );
64
+
65
+ resetForm();
66
+ } catch (e) {
67
+ if (e instanceof Error) {
68
+ console.error("Error during submission:", e.message);
69
+ } else {
70
+ console.error("Unknown error during submission", e);
71
+ }
72
+ } finally {
73
+ setIsSubmitting(false);
74
+ }
75
+ };
76
+
77
+ const resetForm = () => {
78
+ setValues({});
79
+ setErrors({});
80
+ setTouched({});
81
+ };
82
+
83
+ const setFieldValue = (field: string, value: any) =>
84
+ setValues((prev) => ({ ...prev, [field]: value }));
85
+
86
+ const setFieldTouched = (field: string, isTouched: boolean = true) =>
87
+ setTouched((prev) => ({ ...prev, [field]: isTouched }));
88
+
89
+ const setFieldError = (field: string, error: string | undefined) =>
90
+ setErrors((prev) => ({ ...prev, [field]: error }));
91
+
92
+ return (
93
+ <FormContext.Provider
94
+ value={{
95
+ errors,
96
+ isSubmitting,
97
+ touched,
98
+ values,
99
+ handleSubmit,
100
+ resetForm,
101
+ setFieldValue,
102
+ setFieldTouched,
103
+ setFieldError,
104
+ }}
105
+ >
106
+ {children}
107
+ </FormContext.Provider>
108
+ );
109
+ }
@@ -0,0 +1,55 @@
1
+ import { createContext, useContext, useMemo } from "react";
2
+ import { useColorScheme } from "react-native";
3
+
4
+ import { Colors } from "../constants/Colors";
5
+
6
+ export interface FormThemeColors {
7
+ button: string;
8
+ buttonText: string;
9
+ formFieldBackground: string;
10
+ formFieldErrorBorder: string;
11
+ iconColor: string;
12
+ iconError: string;
13
+ placeholderColor: string;
14
+ placeholderError: string;
15
+ textInput: string;
16
+ textInputCarot: string;
17
+ }
18
+
19
+ const FormThemeContext = createContext<FormThemeColors | undefined>(undefined);
20
+
21
+ export const useFormThemeContext = () => {
22
+ const context = useContext(FormThemeContext);
23
+ if (!context) {
24
+ throw new Error(
25
+ "useFormThemeContext must be used within a FormThemeProvider"
26
+ );
27
+ }
28
+ return context;
29
+ };
30
+
31
+ export default function FormThemeProvider({
32
+ children,
33
+ dark,
34
+ light,
35
+ }: {
36
+ children: React.ReactNode;
37
+ colorScheme: "light" | "dark";
38
+ dark?: FormThemeColors;
39
+ light?: FormThemeColors;
40
+ }) {
41
+ const mergedDark = useMemo(() => ({ ...Colors.dark, ...dark }), [dark]);
42
+ const mergedLight = useMemo(() => ({ ...Colors.light, ...light }), [light]);
43
+
44
+ const colorScheme = useColorScheme();
45
+ const colorTheme = useMemo(
46
+ () => (colorScheme === "dark" ? mergedDark : mergedLight),
47
+ [colorScheme, mergedDark, mergedLight]
48
+ );
49
+
50
+ return (
51
+ <FormThemeContext.Provider value={colorTheme}>
52
+ {children}
53
+ </FormThemeContext.Provider>
54
+ );
55
+ }
@@ -0,0 +1,3 @@
1
+ export { default as FormProvider } from "./FormContext";
2
+ export { default as FormThemeProvider } from "./FormThemeContext";
3
+ export { FormThemeColors } from "./FormThemeContext";
package/src/index.ts ADDED
@@ -0,0 +1,19 @@
1
+ // Runtime check for react-native-svg
2
+ try {
3
+ require("react-native-svg");
4
+ } catch (e) {
5
+ throw new Error(
6
+ "expo-openpay requires react-native-svg. Please install it:\n" +
7
+ " yarn add react-native-svg\n" +
8
+ "or\n" +
9
+ " npm install react-native-svg"
10
+ );
11
+ }
12
+
13
+ // Reexport the native module. On web, it will be resolved to ExpoOpenpayModule.web.ts
14
+ // and on native platforms to ExpoOpenpayModule.ts
15
+ export { default } from "./ExpoOpenpayModule";
16
+ export { default as ExpoOpenpayView } from "./ExpoOpenpayView";
17
+ export * from "./ExpoOpenpayModule";
18
+ export * from "./components";
19
+ export * from "./ExpoOpenpay.types";
File without changes
@@ -0,0 +1,23 @@
1
+ export const formatCreditCardNumber = (value: string): string => {
2
+ // Remove all non-digit characters
3
+ const digitsOnly = value.replace(/\D/g, "");
4
+
5
+ // Group digits into chunks of 4
6
+ const formatted = digitsOnly.match(/.{1,4}/g)?.join(" ") || "";
7
+
8
+ return formatted;
9
+ };
10
+
11
+ export const formatExp = (value: string): string => {
12
+ // Remove all non-digit characters
13
+ const digits = value.replace(/\D/g, "");
14
+ // Limit to 4 digits (MMYY)
15
+ const limited = digits.slice(0, 4);
16
+
17
+ if (limited.length < 3) return limited;
18
+ return `${limited.slice(0, 2)}/${limited.slice(2)}`;
19
+ };
20
+
21
+ export const unformat = (formattedText: string): string => {
22
+ return formattedText.replace(/\D/g, "");
23
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ // @generated by expo-module-scripts
2
+ {
3
+ "extends": "expo-module-scripts/tsconfig.base",
4
+ "compilerOptions": {
5
+ "outDir": "./build"
6
+ },
7
+ "include": ["./src"],
8
+ "exclude": ["**/__mocks__/*", "**/__tests__/*", "**/__rsc_tests__/*"]
9
+ }