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,126 @@
1
+ import { useState } from "react";
2
+ import { StyleProp, TextStyle, ViewStyle } from "react-native";
3
+
4
+ import { FormField } from "./forms";
5
+
6
+ type IconType =
7
+ | {
8
+ type: "fa6";
9
+ name:
10
+ | "cc-visa"
11
+ | "cc-mastercard"
12
+ | "cc-amex"
13
+ | "cc-discover"
14
+ | "credit-card";
15
+ }
16
+ | { type: "material"; name: "credit-card" }
17
+ | { type: "png"; name: "amex" | "discover" | "mc" | "visa" };
18
+
19
+ type CardInputOptions = {
20
+ options?: {
21
+ formFieldStyle?: StyleProp<ViewStyle>;
22
+ defaultStyling?: boolean;
23
+ inputStyle?: StyleProp<TextStyle>;
24
+ labelStyle?: StyleProp<TextStyle>;
25
+ textInputStyle?: StyleProp<TextStyle>;
26
+ };
27
+ withAnimatedText?: boolean;
28
+ withIcon?: boolean;
29
+ withLabel?: boolean;
30
+ withPlaceholder?: boolean;
31
+ };
32
+
33
+ function getCardBrandIcon(cardType: string | null): IconType {
34
+ switch (cardType) {
35
+ case "visa":
36
+ return { type: "png", name: "visa" };
37
+ case "mastercard":
38
+ return { type: "png", name: "mc" };
39
+ case "amex":
40
+ return { type: "png", name: "amex" };
41
+ case "discover":
42
+ return { type: "png", name: "discover" };
43
+ default:
44
+ return { type: "material", name: "credit-card" };
45
+ }
46
+ }
47
+
48
+ function detectCardBrand(cardNumber: string): string | null {
49
+ const sanitized = cardNumber.replace(/\s+/g, "");
50
+
51
+ if (/^4\d{0,15}$/.test(sanitized)) return "visa";
52
+ if (/^(5[1-5]|2[2-7])\d{0,14}$/.test(sanitized)) return "mastercard";
53
+ if (/^3[47]\d{0,13}$/.test(sanitized)) return "amex";
54
+ if (/^6(?:011|5\d{2})\d{0,12}$/.test(sanitized)) return "discover";
55
+
56
+ return null;
57
+ }
58
+
59
+ // Luhn algorithm validator
60
+ function validateCardNumber(value: string) {
61
+ if (!value) return "Card Number is required";
62
+
63
+ const sanitized = value.replace(/\s+/g, "");
64
+ if (sanitized.length < 13 || sanitized.length > 19) {
65
+ return "Card Number must be 13-19 digits";
66
+ }
67
+
68
+ let sum = 0;
69
+ let shouldDouble = false;
70
+ for (let i = sanitized.length - 1; i >= 0; i--) {
71
+ let digit = parseInt(sanitized[i], 10);
72
+ if (shouldDouble) {
73
+ digit *= 2;
74
+ if (digit > 9) digit -= 9;
75
+ }
76
+ sum += digit;
77
+ shouldDouble = !shouldDouble;
78
+ }
79
+ if (sum % 10 !== 0) return "Invalid card number";
80
+
81
+ return undefined; // valid card
82
+ }
83
+
84
+ export default function OPCardNumberInput({
85
+ options = {},
86
+ withAnimatedText = true,
87
+ withIcon = true,
88
+ withLabel = false,
89
+ withPlaceholder = true,
90
+ ...otherProps
91
+ }: CardInputOptions) {
92
+ const [cardType, setCardType] = useState<string | null>(null);
93
+
94
+ const {
95
+ defaultStyling = true,
96
+ formFieldStyle = {},
97
+ inputStyle = {},
98
+ labelStyle = {},
99
+ textInputStyle = {},
100
+ } = options;
101
+
102
+ return (
103
+ <FormField
104
+ autoCapitalize="none"
105
+ autoComplete="cc-number"
106
+ autoCorrect={false}
107
+ defaultStyling={defaultStyling}
108
+ icon={getCardBrandIcon(cardType)}
109
+ keyboardType="number-pad"
110
+ labelStyle={labelStyle}
111
+ maxLength={19}
112
+ name="cardNumber"
113
+ onValueChange={(text) => {
114
+ setCardType(detectCardBrand(text));
115
+ }}
116
+ placeholder="Card Number"
117
+ styling={{ formFieldStyle, inputStyle, textInputStyle }}
118
+ validate={validateCardNumber}
119
+ withAnimatedText={withAnimatedText}
120
+ withIcon={withIcon}
121
+ withLabel={withLabel}
122
+ withPlaceholder={withPlaceholder}
123
+ {...otherProps}
124
+ />
125
+ );
126
+ }
@@ -0,0 +1,62 @@
1
+ import { StyleProp, ViewStyle, TextStyle } from "react-native";
2
+
3
+ import { FormField } from "./forms";
4
+
5
+ type CardInputOptions = {
6
+ options?: {
7
+ formFieldStyle?: StyleProp<ViewStyle>;
8
+ defaultStyling?: boolean;
9
+ inputStyle?: StyleProp<TextStyle>;
10
+ labelStyle?: StyleProp<TextStyle>;
11
+ textInputStyle?: StyleProp<TextStyle>;
12
+ };
13
+ withAnimatedText?: boolean;
14
+ withIcon?: boolean;
15
+ withLabel?: boolean;
16
+ withPlaceholder?: boolean;
17
+ };
18
+
19
+ function validateCvv(value: string) {
20
+ if (!value) return "CVV is required";
21
+ if (!/^\d{3,4}$/.test(value)) return "Invalid CVV";
22
+ return undefined;
23
+ }
24
+
25
+ export default function OPExpYearInput({
26
+ options = {},
27
+ withAnimatedText = true,
28
+ withIcon = true,
29
+ withLabel = false,
30
+ withPlaceholder = true,
31
+ ...otherProps
32
+ }: CardInputOptions) {
33
+ const {
34
+ defaultStyling = true,
35
+ formFieldStyle = {},
36
+ inputStyle = {},
37
+ labelStyle = {},
38
+ textInputStyle = {},
39
+ } = options;
40
+
41
+ return (
42
+ <FormField
43
+ autoCapitalize="none"
44
+ autoComplete="cc-csc"
45
+ autoCorrect={false}
46
+ defaultStyling={defaultStyling}
47
+ icon={{ type: "material", name: "lock" }}
48
+ keyboardType="number-pad"
49
+ labelStyle={labelStyle}
50
+ maxLength={4}
51
+ name="cvc"
52
+ placeholder="CVC"
53
+ styling={{ formFieldStyle, inputStyle, textInputStyle }}
54
+ validate={validateCvv}
55
+ withAnimatedText={withAnimatedText}
56
+ withIcon={withIcon}
57
+ withLabel={withLabel}
58
+ withPlaceholder={withPlaceholder}
59
+ {...otherProps}
60
+ />
61
+ );
62
+ }
@@ -0,0 +1,74 @@
1
+ import { StyleProp, ViewStyle, TextStyle } from "react-native";
2
+
3
+ import { FormField } from "./forms";
4
+
5
+ type CardInputOptions = {
6
+ options?: {
7
+ formFieldStyle?: StyleProp<ViewStyle>;
8
+ defaultStyling?: boolean;
9
+ inputStyle?: StyleProp<TextStyle>;
10
+ labelStyle?: StyleProp<TextStyle>;
11
+ textInputStyle?: StyleProp<TextStyle>;
12
+ };
13
+ withAnimatedText?: boolean;
14
+ withIcon?: boolean;
15
+ withLabel?: boolean;
16
+ withPlaceholder?: boolean;
17
+ };
18
+
19
+ function validateExpiry(value: string) {
20
+ if (!value) return "Expiry is required";
21
+
22
+ const match = /^(\d{2})\/(\d{2})$/.exec(value);
23
+ if (!match) return "Use MM/YY format";
24
+
25
+ const month = parseInt(match[1], 10);
26
+ const year = 2000 + parseInt(match[2], 10);
27
+
28
+ if (month < 1 || month > 12) return "Invalid month";
29
+
30
+ const now = new Date();
31
+ const expiry = new Date(year, month);
32
+ if (expiry <= now) return "Card expired";
33
+
34
+ return undefined;
35
+ }
36
+
37
+ export default function OPExpYearInput({
38
+ options = {},
39
+ withAnimatedText = true,
40
+ withIcon = true,
41
+ withLabel = false,
42
+ withPlaceholder = true,
43
+ ...otherProps
44
+ }: CardInputOptions) {
45
+ const {
46
+ defaultStyling = true,
47
+ formFieldStyle = {},
48
+ inputStyle = {},
49
+ labelStyle = {},
50
+ textInputStyle = {},
51
+ } = options;
52
+
53
+ return (
54
+ <FormField
55
+ autoCapitalize="none"
56
+ autoComplete="cc-exp"
57
+ autoCorrect={false}
58
+ defaultStyling={defaultStyling}
59
+ icon={{ type: "material", name: "calendar-blank" }}
60
+ keyboardType="number-pad"
61
+ labelStyle={labelStyle}
62
+ maxLength={5}
63
+ name="exp"
64
+ placeholder="MM/YY"
65
+ styling={{ formFieldStyle, inputStyle, textInputStyle }}
66
+ validate={validateExpiry}
67
+ withAnimatedText={withAnimatedText}
68
+ withIcon={withIcon}
69
+ withLabel={withLabel}
70
+ withPlaceholder={withPlaceholder}
71
+ {...otherProps}
72
+ />
73
+ );
74
+ }
@@ -0,0 +1,55 @@
1
+ import { StyleProp, ViewStyle, TextStyle } from "react-native";
2
+
3
+ import { FormField } from "./forms";
4
+
5
+ type CardInputOptions = {
6
+ options?: {
7
+ formFieldStyle?: StyleProp<ViewStyle>;
8
+ defaultStyling?: boolean;
9
+ inputStyle?: StyleProp<TextStyle>;
10
+ labelStyle?: StyleProp<TextStyle>;
11
+ textInputStyle?: StyleProp<TextStyle>;
12
+ };
13
+ withAnimatedText?: boolean;
14
+ withIcon?: boolean;
15
+ withLabel?: boolean;
16
+ withPlaceholder?: boolean;
17
+ };
18
+
19
+ export default function OPExpMonthInput({
20
+ options = {},
21
+ withAnimatedText = true,
22
+ withIcon = true,
23
+ withLabel = false,
24
+ withPlaceholder = true,
25
+ ...otherProps
26
+ }: CardInputOptions) {
27
+ const {
28
+ defaultStyling = true,
29
+ formFieldStyle = {},
30
+ inputStyle = {},
31
+ labelStyle = {},
32
+ textInputStyle = {},
33
+ } = options;
34
+
35
+ return (
36
+ <FormField
37
+ autoCapitalize="none"
38
+ autoComplete="cc-exp-month"
39
+ autoCorrect={false}
40
+ defaultStyling={defaultStyling}
41
+ icon={{ type: "material", name: "calendar-blank" }}
42
+ keyboardType="number-pad"
43
+ labelStyle={labelStyle}
44
+ maxLength={2}
45
+ name="expMonth"
46
+ placeholder="MM"
47
+ styling={{ formFieldStyle, inputStyle, textInputStyle }}
48
+ withAnimatedText={withAnimatedText}
49
+ withIcon={withIcon}
50
+ withLabel={withLabel}
51
+ withPlaceholder={withPlaceholder}
52
+ {...otherProps}
53
+ />
54
+ );
55
+ }
@@ -0,0 +1,55 @@
1
+ import { StyleProp, ViewStyle, TextStyle } from "react-native";
2
+
3
+ import { FormField } from "./forms";
4
+
5
+ type CardInputOptions = {
6
+ options?: {
7
+ formFieldStyle?: StyleProp<ViewStyle>;
8
+ defaultStyling?: boolean;
9
+ inputStyle?: StyleProp<TextStyle>;
10
+ labelStyle?: StyleProp<TextStyle>;
11
+ textInputStyle?: StyleProp<TextStyle>;
12
+ };
13
+ withAnimatedText?: boolean;
14
+ withIcon?: boolean;
15
+ withLabel?: boolean;
16
+ withPlaceholder?: boolean;
17
+ };
18
+
19
+ export default function OPExpYearInput({
20
+ options = {},
21
+ withAnimatedText = true,
22
+ withIcon = true,
23
+ withLabel = false,
24
+ withPlaceholder = true,
25
+ ...otherProps
26
+ }: CardInputOptions) {
27
+ const {
28
+ defaultStyling = true,
29
+ formFieldStyle = {},
30
+ inputStyle = {},
31
+ labelStyle = {},
32
+ textInputStyle = {},
33
+ } = options;
34
+
35
+ return (
36
+ <FormField
37
+ autoCapitalize="none"
38
+ autoComplete="cc-exp-year"
39
+ autoCorrect={false}
40
+ defaultStyling={defaultStyling}
41
+ icon={{ type: "material", name: "calendar-blank" }}
42
+ keyboardType="number-pad"
43
+ labelStyle={labelStyle}
44
+ maxLength={2}
45
+ name="expYear"
46
+ placeholder="YY"
47
+ styling={{ formFieldStyle, inputStyle, textInputStyle }}
48
+ withAnimatedText={withAnimatedText}
49
+ withIcon={withIcon}
50
+ withLabel={withLabel}
51
+ withPlaceholder={withPlaceholder}
52
+ {...otherProps}
53
+ />
54
+ );
55
+ }
@@ -0,0 +1,53 @@
1
+ import { StyleProp, ViewStyle, TextStyle } from "react-native";
2
+
3
+ import { FormField } from "./forms";
4
+
5
+ type CardInputOptions = {
6
+ options?: {
7
+ formFieldStyle?: StyleProp<ViewStyle>;
8
+ defaultStyling?: boolean;
9
+ inputStyle?: StyleProp<TextStyle>;
10
+ labelStyle?: StyleProp<TextStyle>;
11
+ textInputStyle?: StyleProp<TextStyle>;
12
+ };
13
+ withAnimatedText?: boolean;
14
+ withIcon?: boolean;
15
+ withLabel?: boolean;
16
+ withPlaceholder?: boolean;
17
+ };
18
+
19
+ export default function OPHolderNameInput({
20
+ options = {},
21
+ withAnimatedText = true,
22
+ withIcon = true,
23
+ withLabel = false,
24
+ withPlaceholder = true,
25
+ ...otherProps
26
+ }: CardInputOptions) {
27
+ const {
28
+ defaultStyling = true,
29
+ formFieldStyle = {},
30
+ inputStyle = {},
31
+ labelStyle = {},
32
+ textInputStyle = {},
33
+ } = options;
34
+
35
+ return (
36
+ <FormField
37
+ autoCapitalize="words"
38
+ autoComplete="cc-name"
39
+ autoCorrect={false}
40
+ defaultStyling={defaultStyling}
41
+ icon={{ type: "material", name: "account" }}
42
+ labelStyle={labelStyle}
43
+ name="cardHolderName"
44
+ placeholder="Cardholder Name"
45
+ styling={{ formFieldStyle, inputStyle, textInputStyle }}
46
+ withAnimatedText={withAnimatedText}
47
+ withIcon={withIcon}
48
+ withLabel={withLabel}
49
+ withPlaceholder={withPlaceholder}
50
+ {...otherProps}
51
+ />
52
+ );
53
+ }
@@ -0,0 +1,19 @@
1
+ import { StyleSheet } from "react-native";
2
+
3
+ import Text from "./Text";
4
+
5
+ type ErrorMessageProps = {
6
+ error?: string;
7
+ visible: boolean;
8
+ };
9
+
10
+ function ErrorMessage({ error, visible }: ErrorMessageProps): React.ReactNode {
11
+ if (!visible || !error) return null;
12
+ return <Text style={styles.error}>{error}</Text>;
13
+ }
14
+
15
+ const styles = StyleSheet.create({
16
+ error: { color: "red", fontSize: 18, marginTop: 4, marginLeft: 10 },
17
+ });
18
+
19
+ export default ErrorMessage;
@@ -0,0 +1,96 @@
1
+ import {
2
+ StyleProp,
3
+ StyleSheet,
4
+ Text,
5
+ TextInputProps,
6
+ TextStyle,
7
+ ViewStyle,
8
+ } from "react-native";
9
+ import { FontAwesome6, MaterialCommunityIcons } from "@expo/vector-icons";
10
+
11
+ import TextInput from "./TextInput";
12
+ import { formatCreditCardNumber, formatExp } from "../../utility/formatting";
13
+ import { useFormContext } from "../../context/FormContext";
14
+
15
+ type StylingProps = {
16
+ formFieldStyle?: StyleProp<ViewStyle>;
17
+ inputStyle?: StyleProp<ViewStyle>;
18
+ textInputStyle?: StyleProp<TextStyle>;
19
+ };
20
+
21
+ type AppFormFieldProps = TextInputProps & {
22
+ customStyles?: StyleProp<ViewStyle>;
23
+ defaultStyling?: boolean;
24
+ icon?:
25
+ | { type: "material"; name: keyof typeof MaterialCommunityIcons.glyphMap }
26
+ | { type: "fa6"; name: keyof typeof FontAwesome6.glyphMap }
27
+ | { type: "png"; name: "amex" | "discover" | "mc" | "visa" };
28
+ name: string;
29
+ placeholder?: string;
30
+ styling?: StylingProps;
31
+ labelStyle?: StyleProp<TextStyle>;
32
+ onValueChange?: (value: string) => void;
33
+ validate?: (value: string) => string | undefined;
34
+ withAnimatedText: boolean;
35
+ withIcon: boolean;
36
+ withLabel?: boolean;
37
+ withPlaceholder?: boolean;
38
+ };
39
+
40
+ export default function AppFormField({
41
+ defaultStyling,
42
+ name,
43
+ labelStyle = {},
44
+ placeholder,
45
+ validate,
46
+ withLabel = false,
47
+ ...otherProps
48
+ }: AppFormFieldProps) {
49
+ const {
50
+ errors,
51
+ setFieldTouched,
52
+ setFieldValue,
53
+ setFieldError,
54
+ touched,
55
+ values,
56
+ } = useFormContext();
57
+ const hasError: boolean = !!touched[name] && !!errors[name];
58
+
59
+ return (
60
+ <>
61
+ {withLabel && (
62
+ <Text style={defaultStyling ? [styles.label, labelStyle] : labelStyle}>
63
+ {placeholder}
64
+ </Text>
65
+ )}
66
+ <TextInput
67
+ defaultStyling={defaultStyling}
68
+ onBlur={() => {
69
+ setFieldTouched(name);
70
+ if (validate) {
71
+ const errorMessage = validate(values[name]);
72
+ setFieldError(name, errorMessage);
73
+ }
74
+ }}
75
+ onChangeText={(text) => {
76
+ if (name === "cardNumber") text = formatCreditCardNumber(text);
77
+ if (name === "exp") text = formatExp(text);
78
+ setFieldValue(name, text);
79
+
80
+ if (otherProps.onValueChange) {
81
+ otherProps.onValueChange(text);
82
+ }
83
+ }}
84
+ hasError={hasError}
85
+ errorText={errors[name]}
86
+ placeholder={hasError ? errors[name] : placeholder}
87
+ value={values[name]}
88
+ {...otherProps}
89
+ />
90
+ </>
91
+ );
92
+ }
93
+
94
+ const styles = StyleSheet.create({
95
+ label: { fontSize: 18, fontWeight: "500", marginTop: 12, paddingLeft: 6 },
96
+ });
@@ -0,0 +1,81 @@
1
+ import {
2
+ StyleProp,
3
+ StyleSheet,
4
+ Text,
5
+ TextStyle,
6
+ TouchableOpacity,
7
+ ViewStyle,
8
+ } from "react-native";
9
+
10
+ import { useFormContext } from "../../context/FormContext";
11
+ import { useFormThemeContext } from "../../context/FormThemeContext";
12
+
13
+ type SubmitButtonProps = {
14
+ buttonStyle?: StyleProp<ViewStyle>;
15
+ buttonTextStyle?: StyleProp<TextStyle>;
16
+ defaultStyling?: boolean;
17
+ onPress?: () => void;
18
+ title?: string;
19
+ };
20
+
21
+ export default function SubmitButton({
22
+ buttonStyle = {},
23
+ buttonTextStyle = {},
24
+ defaultStyling,
25
+ onPress,
26
+ title = "Submit",
27
+ }: SubmitButtonProps) {
28
+ const { errors, handleSubmit, isSubmitting } = useFormContext();
29
+ const { button, buttonText } = useFormThemeContext();
30
+
31
+ return (
32
+ <TouchableOpacity
33
+ disabled={Object.values(errors).some(Boolean) || isSubmitting}
34
+ style={
35
+ defaultStyling
36
+ ? [
37
+ Object.values(errors).some(Boolean) || isSubmitting
38
+ ? styles.buttonDisabled
39
+ : styles.button,
40
+ { backgroundColor: button },
41
+ buttonStyle,
42
+ ]
43
+ : buttonStyle
44
+ }
45
+ onPress={onPress || handleSubmit}
46
+ >
47
+ <Text
48
+ style={
49
+ defaultStyling
50
+ ? [styles.text, { color: buttonText }, buttonTextStyle]
51
+ : buttonTextStyle
52
+ }
53
+ >
54
+ {title}
55
+ </Text>
56
+ </TouchableOpacity>
57
+ );
58
+ }
59
+
60
+ const styles = StyleSheet.create({
61
+ button: {
62
+ alignItems: "center",
63
+ borderRadius: 25,
64
+ justifyContent: "center",
65
+ marginVertical: 10,
66
+ padding: 15,
67
+ },
68
+ buttonDisabled: {
69
+ alignItems: "center",
70
+ borderRadius: 25,
71
+ justifyContent: "center",
72
+ marginVertical: 10,
73
+ opacity: 0.7,
74
+ padding: 15,
75
+ },
76
+ text: {
77
+ fontSize: 18,
78
+ textTransform: "uppercase",
79
+ fontWeight: "bold",
80
+ },
81
+ });
@@ -0,0 +1,20 @@
1
+ import { StyleSheet, Text, TextProps } from "react-native";
2
+ import Styles from "../../constants/Styles";
3
+
4
+ type AppTextProps = TextProps & {
5
+ children?: React.ReactNode;
6
+ };
7
+
8
+ function AppText({ children, ...otherProps }: AppTextProps) {
9
+ return (
10
+ <Text style={[styles.text]} {...otherProps}>
11
+ {children}
12
+ </Text>
13
+ );
14
+ }
15
+
16
+ export default AppText;
17
+
18
+ const styles = StyleSheet.create({
19
+ text: { ...Styles.text },
20
+ });