@evervault/react-native 2.2.0 → 2.3.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.
@@ -6774,7 +6774,7 @@ const h = [
6774
6774
  isLocal: false,
6775
6775
  numberValidationRules: {
6776
6776
  luhnCheck: true,
6777
- ranges: [[300, 305], 36, 38, 39],
6777
+ ranges: [[300, 305], 3095, 36, 38, 39],
6778
6778
  lengths: [14, 16, 19]
6779
6779
  },
6780
6780
  securityCodeValidationRules: {
@@ -6786,7 +6786,7 @@ const h = [
6786
6786
  isLocal: false,
6787
6787
  numberValidationRules: {
6788
6788
  luhnCheck: true,
6789
- ranges: [6011, [644, 649], 65],
6789
+ ranges: [6011, [644, 649], [65e4, 651999], [653150, 659999], 622],
6790
6790
  lengths: [16, 19]
6791
6791
  },
6792
6792
  securityCodeValidationRules: {
@@ -6798,7 +6798,16 @@ const h = [
6798
6798
  isLocal: false,
6799
6799
  numberValidationRules: {
6800
6800
  luhnCheck: true,
6801
- ranges: [2131, 1800, [3528, 3589]],
6801
+ ranges: [
6802
+ 2131,
6803
+ 1800,
6804
+ [3088, 3094],
6805
+ [3096, 3102],
6806
+ [3112, 3120],
6807
+ [3158, 3159],
6808
+ [3337, 3349],
6809
+ [3528, 3589]
6810
+ ],
6802
6811
  lengths: [16, 17, 18, 19]
6803
6812
  },
6804
6813
  securityCodeValidationRules: {
@@ -6846,16 +6855,20 @@ const h = [
6846
6855
  numberValidationRules: {
6847
6856
  luhnCheck: true,
6848
6857
  ranges: [
6858
+ 5018,
6859
+ 5020,
6860
+ 5038,
6861
+ 5893,
6862
+ 6101,
6863
+ 6304,
6864
+ 6759,
6865
+ 6761,
6866
+ 6762,
6867
+ 6763,
6849
6868
  493698,
6850
6869
  [5e5, 504174],
6851
6870
  [504176, 506698],
6852
6871
  [506779, 508999],
6853
- [56, 59],
6854
- 60,
6855
- 61,
6856
- 63,
6857
- 64,
6858
- 67,
6859
6872
  69
6860
6873
  ],
6861
6874
  lengths: [12, 13, 14, 15, 16, 17, 18, 19]
@@ -6961,6 +6974,18 @@ const h = [
6961
6974
  securityCodeValidationRules: {
6962
6975
  lengths: [0]
6963
6976
  }
6977
+ },
6978
+ {
6979
+ name: "rupay",
6980
+ isLocal: false,
6981
+ numberValidationRules: {
6982
+ luhnCheck: true,
6983
+ ranges: [60, 81, 82, 508, [652100, 653149], [817200, 819899]],
6984
+ lengths: [16]
6985
+ },
6986
+ securityCodeValidationRules: {
6987
+ lengths: [3]
6988
+ }
6964
6989
  }
6965
6990
  ];
6966
6991
  function g(n, e, t) {
@@ -7027,7 +7052,7 @@ function C(n, e) {
7027
7052
  isValid: l
7028
7053
  };
7029
7054
  }
7030
- function b(n) {
7055
+ function R(n) {
7031
7056
  var r;
7032
7057
  const e = /^(0[1-9]|1[[0-2]).*$/, t = n.match(e), s = t ? parseInt(t[1].toString(), 10) : null, l = /^(0[1-9]|1[[0-2])(\d{2})$/, i = n.match(l), a = i ? parseInt(i[2].toString(), 10) : null;
7033
7058
  if (s) {
@@ -7093,7 +7118,7 @@ function areValuesComplete(values) {
7093
7118
  if ("number" in values && !f(values.number ?? "").isValid) {
7094
7119
  return false;
7095
7120
  }
7096
- if ("expiry" in values && !b(values.expiry ?? "").isValid) {
7121
+ if ("expiry" in values && !R(values.expiry ?? "").isValid) {
7097
7122
  return false;
7098
7123
  }
7099
7124
  if ("cvc" in values &&
@@ -7105,6 +7130,8 @@ function areValuesComplete(values) {
7105
7130
  function isAcceptedBrand(acceptedBrands, cardNumberValidationResult) {
7106
7131
  if (!acceptedBrands?.length)
7107
7132
  return true;
7133
+ if (!cardNumberValidationResult.isValid)
7134
+ return false;
7108
7135
  const { brand, localBrands } = cardNumberValidationResult;
7109
7136
  const acceptedBrandsSet = new Set(acceptedBrands);
7110
7137
  const isBrandAccepted = brand !== null && acceptedBrandsSet.has(brand);
@@ -7112,7 +7139,7 @@ function isAcceptedBrand(acceptedBrands, cardNumberValidationResult) {
7112
7139
  return isBrandAccepted || isLocalBrandAccepted;
7113
7140
  }
7114
7141
  function formatExpiry(expiry) {
7115
- const parsedExpiry = b(expiry);
7142
+ const parsedExpiry = R(expiry);
7116
7143
  if (!parsedExpiry.isValid) {
7117
7144
  return null;
7118
7145
  }
@@ -7123,19 +7150,19 @@ function formatExpiry(expiry) {
7123
7150
  }
7124
7151
 
7125
7152
  function getCardFormSchema(acceptedBrands) {
7126
- return z
7127
- .object({
7153
+ return z.object({
7128
7154
  name: z.string().min(1, "Missing name"),
7129
7155
  number: z
7130
7156
  .string()
7131
7157
  .min(1, "Required")
7132
7158
  .refine((value) => f(value).isValid, {
7133
7159
  message: "Invalid card number",
7134
- }),
7160
+ })
7161
+ .refine((value) => isAcceptedBrand(acceptedBrands, f(value)), { message: "Brand not accepted" }),
7135
7162
  expiry: z
7136
7163
  .string()
7137
7164
  .min(1, "Required")
7138
- .refine((value) => b(value).isValid, {
7165
+ .refine((value) => R(value).isValid, {
7139
7166
  message: "Invalid expiry",
7140
7167
  }),
7141
7168
  cvc: z
@@ -7144,19 +7171,6 @@ function getCardFormSchema(acceptedBrands) {
7144
7171
  .refine((value) => C(value).isValid, {
7145
7172
  message: "Invalid CVC",
7146
7173
  }),
7147
- })
7148
- .superRefine((value, ctx) => {
7149
- const validation = f(value.number);
7150
- if (!validation.isValid)
7151
- return;
7152
- const isAccepted = isAcceptedBrand(acceptedBrands, validation);
7153
- if (!isAccepted) {
7154
- ctx.addIssue({
7155
- code: z.ZodIssueCode.custom,
7156
- message: "Brand not accepted",
7157
- path: ["number"],
7158
- });
7159
- }
7160
7174
  });
7161
7175
  }
7162
7176
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@evervault/react-native",
3
3
  "description": "Evervault SDK for React Native",
4
- "version": "2.2.0",
4
+ "version": "2.3.0",
5
5
  "source": "./src/index.ts",
6
6
  "main": "./build/index.cjs.js",
7
7
  "module": "./build/index.esm.js",
@@ -73,7 +73,7 @@
73
73
  "react-hook-form": "^7.54.2",
74
74
  "react-native-mask-input": "^1.2.3",
75
75
  "zod": "^3.24.2",
76
- "@evervault/card-validator": "1.3.0"
76
+ "@evervault/card-validator": "1.4.0"
77
77
  },
78
78
  "scripts": {
79
79
  "prebuild": "pnpm codegen",
@@ -258,3 +258,84 @@ it("adds Invalid error when input is blurred with an invalid value", async () =>
258
258
  });
259
259
  });
260
260
  });
261
+
262
+ it("adds 'Brand not accepted' error when brand is not accepted", async () => {
263
+ const onChange = vi.fn();
264
+ const { getByTestId } = render(
265
+ <Card onChange={onChange} acceptedBrands={["american-express"]}>
266
+ <CardNumber testID="number" />
267
+ </Card>,
268
+ { wrapper }
269
+ );
270
+
271
+ const number = getByTestId("number");
272
+
273
+ const user = userEvent.setup();
274
+ await user.type(number, "4242");
275
+ fireEvent(number, "blur");
276
+
277
+ await waitFor(() => {
278
+ expect(onChange).toHaveBeenLastCalledWith({
279
+ card: {
280
+ name: null,
281
+ brand: "visa",
282
+ localBrands: [],
283
+ number: null,
284
+ lastFour: null,
285
+ bin: null,
286
+ expiry: null,
287
+ cvc: null,
288
+ },
289
+ isValid: false,
290
+ isComplete: false,
291
+ errors: {
292
+ number: "Invalid card number",
293
+ },
294
+ });
295
+ });
296
+
297
+ await user.type(number, "4242 4242 4242");
298
+ fireEvent(number, "blur");
299
+
300
+ await waitFor(() => {
301
+ expect(onChange).toHaveBeenLastCalledWith({
302
+ card: {
303
+ name: null,
304
+ brand: "visa",
305
+ localBrands: [],
306
+ number: expect.any(String),
307
+ lastFour: "4242",
308
+ bin: "42424242",
309
+ expiry: null,
310
+ cvc: null,
311
+ },
312
+ isValid: false,
313
+ isComplete: true,
314
+ errors: {
315
+ number: "Brand not accepted",
316
+ },
317
+ });
318
+ });
319
+
320
+ await user.clear(number);
321
+ await user.type(number, "3782 822463 10005");
322
+ fireEvent(number, "blur");
323
+
324
+ await waitFor(() => {
325
+ expect(onChange).toHaveBeenLastCalledWith({
326
+ card: {
327
+ name: null,
328
+ brand: "american-express",
329
+ localBrands: [],
330
+ number: expect.any(String),
331
+ lastFour: "0005",
332
+ bin: "378282",
333
+ expiry: null,
334
+ cvc: null,
335
+ },
336
+ isValid: true,
337
+ isComplete: true,
338
+ errors: {},
339
+ });
340
+ });
341
+ });
@@ -8,44 +8,34 @@ import { CardBrandName } from "./types";
8
8
  import { isAcceptedBrand } from "./utils";
9
9
 
10
10
  export function getCardFormSchema(acceptedBrands: CardBrandName[]) {
11
- return z
12
- .object({
13
- name: z.string().min(1, "Missing name"),
11
+ return z.object({
12
+ name: z.string().min(1, "Missing name"),
14
13
 
15
- number: z
16
- .string()
17
- .min(1, "Required")
18
- .refine((value) => validateNumber(value).isValid, {
19
- message: "Invalid card number",
20
- }),
14
+ number: z
15
+ .string()
16
+ .min(1, "Required")
17
+ .refine((value) => validateNumber(value).isValid, {
18
+ message: "Invalid card number",
19
+ })
20
+ .refine(
21
+ (value) => isAcceptedBrand(acceptedBrands, validateNumber(value)),
22
+ { message: "Brand not accepted" }
23
+ ),
21
24
 
22
- expiry: z
23
- .string()
24
- .min(1, "Required")
25
- .refine((value) => validateExpiry(value).isValid, {
26
- message: "Invalid expiry",
27
- }),
25
+ expiry: z
26
+ .string()
27
+ .min(1, "Required")
28
+ .refine((value) => validateExpiry(value).isValid, {
29
+ message: "Invalid expiry",
30
+ }),
28
31
 
29
- cvc: z
30
- .string()
31
- .min(1, "Required")
32
- .refine((value) => validateCVC(value).isValid, {
33
- message: "Invalid CVC",
34
- }),
35
- })
36
- .superRefine((value, ctx) => {
37
- const validation = validateNumber(value.number);
38
- if (!validation.isValid) return;
39
-
40
- const isAccepted = isAcceptedBrand(acceptedBrands, validation);
41
- if (!isAccepted) {
42
- ctx.addIssue({
43
- code: z.ZodIssueCode.custom,
44
- message: "Brand not accepted",
45
- path: ["number"],
46
- });
47
- }
48
- });
32
+ cvc: z
33
+ .string()
34
+ .min(1, "Required")
35
+ .refine((value) => validateCVC(value).isValid, {
36
+ message: "Invalid CVC",
37
+ }),
38
+ });
49
39
  }
50
40
 
51
41
  export type CardFormValues = z.infer<ReturnType<typeof getCardFormSchema>>;
package/src/Card/types.ts CHANGED
@@ -13,11 +13,18 @@ export const CARD_BRAND_NAMES = [
13
13
  "hiper",
14
14
  "szep",
15
15
  "uatp",
16
+ "rupay",
16
17
  ] as const;
17
18
 
18
19
  export type CardBrandName = (typeof CARD_BRAND_NAMES)[number];
19
20
 
20
21
  export interface CardConfig {
22
+ /**
23
+ * The brands that are accepted by the card form.
24
+ * Pass an empty array to accept all brands.
25
+ *
26
+ * @default []
27
+ */
21
28
  acceptedBrands?: CardBrandName[];
22
29
  }
23
30
 
package/src/Card/utils.ts CHANGED
@@ -101,6 +101,8 @@ export function isAcceptedBrand(
101
101
  cardNumberValidationResult: CardNumberValidationResult
102
102
  ): boolean {
103
103
  if (!acceptedBrands?.length) return true;
104
+
105
+ if (!cardNumberValidationResult.isValid) return false;
104
106
  const { brand, localBrands } = cardNumberValidationResult;
105
107
 
106
108
  const acceptedBrandsSet = new Set(acceptedBrands);