pix-utils-js 1.0.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.
- package/README.md +64 -0
- package/index.d.ts +27 -0
- package/index.js +70 -0
- package/package.json +23 -0
- package/tests/generator.js +107 -0
- package/tests/index.js +78 -0
- package/utils/validator.js +30 -0
package/README.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
|
|
2
|
+
# 💸 pix-utils-js
|
|
3
|
+
|
|
4
|
+
Uma pequena biblioteca que irá te ajudar a lidar com chaves pix
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
## Instalação
|
|
10
|
+
|
|
11
|
+
Instale pix-utils-js com npm ou yarn
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install pix-utils-js
|
|
15
|
+
yarn add pix-utils-js
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Documentação
|
|
19
|
+
|
|
20
|
+
#### Identifique uma chave pix
|
|
21
|
+
|
|
22
|
+
```js
|
|
23
|
+
const { identify } = 'pix-utils-js'
|
|
24
|
+
const { pix, type } = identify({pix: 'test@gmail.com'}) // {pix: 'test@gmail.com', type: 'email'}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
| Função | Parametro | Retorno |
|
|
28
|
+
| :---------- | :--------- | :---------------------------------- |
|
|
29
|
+
| `identify` | `input: {pix: string}` | Pix `{ pix: string, type: string }` |
|
|
30
|
+
|
|
31
|
+
#### Validar uma chave pix
|
|
32
|
+
|
|
33
|
+
```js
|
|
34
|
+
const { validate } = 'pix-utils-js'
|
|
35
|
+
console.log(validate({pix: 'test@gmail.com'})) // true
|
|
36
|
+
console.log(validate({pix: 'test'})) // false
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
| Função | Parametro | Retorno |
|
|
40
|
+
| :---------- | :--------- | :---------------------------------- |
|
|
41
|
+
| `validate` | `input: {pix: string}` | boolean |
|
|
42
|
+
|
|
43
|
+
#### Normalizar uma chave pix
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
const { normalize } = 'pix-utils-js'
|
|
47
|
+
console.log(normalize({pix: '000.000.000-00'})) // {pix: '00000000000', type: 'cpf'}
|
|
48
|
+
console.log(normalize({pix: '00.000.000/0000-00'})) // {pix: '00000000000000', type: 'cnpj'}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
| Função | Parametro | Retorno |
|
|
52
|
+
| :---------- | :--------- | :---------------------------------- |
|
|
53
|
+
| `normalize` | `input: {pix: string}` | Pix `{ pix: string, type: string }` |
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
## Rodando os testes
|
|
58
|
+
|
|
59
|
+
Para rodar os testes, rode o seguinte comando
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npm run test
|
|
63
|
+
```
|
|
64
|
+
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type PixType = "email" | "cpf" | "cnpj" | "random" | "phone"
|
|
2
|
+
|
|
3
|
+
export interface InvalidPix extends Error {
|
|
4
|
+
message: "chave pix invalida."
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export type Pix = {
|
|
8
|
+
type: PixType,
|
|
9
|
+
pix: string,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface IdentifyDto {
|
|
13
|
+
pix: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface NormalizeDto {
|
|
17
|
+
pix: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ValidateDto {
|
|
21
|
+
type?: PixType,
|
|
22
|
+
pix: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function identify(input: IdentifyDto): Pix | InvalidPix
|
|
26
|
+
export function normalize(input: NormalizeDto): Pix | InvalidPix
|
|
27
|
+
export function validate(input: ValidateDto): Boolean | InvalidPix
|
package/index.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
const { Validator } = require('./utils/validator')
|
|
2
|
+
|
|
3
|
+
const validator = new Validator();
|
|
4
|
+
|
|
5
|
+
const validationPixMap = {
|
|
6
|
+
email: validator.isEmail,
|
|
7
|
+
random: validator.isUUID,
|
|
8
|
+
phone: validator.isPhoneNumber,
|
|
9
|
+
cnpj: validator.isCNPJ,
|
|
10
|
+
cpf: validator.isCPF
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
const validate = (input) => {
|
|
15
|
+
const { pix } = input
|
|
16
|
+
let { type } = input
|
|
17
|
+
|
|
18
|
+
if(!pix) {
|
|
19
|
+
throw new Error("chave pix invalida.")
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if(!type) {
|
|
23
|
+
try {
|
|
24
|
+
let identifiedPix = identify({ pix })
|
|
25
|
+
type = identifiedPix.type
|
|
26
|
+
}catch {
|
|
27
|
+
return false
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return validationPixMap[type](pix)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const identify = (input) => {
|
|
35
|
+
const { pix } = input
|
|
36
|
+
|
|
37
|
+
if(!pix) {
|
|
38
|
+
throw new Error("chave pix invalida.")
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
for (const type in validationPixMap) {
|
|
42
|
+
if (validationPixMap[type](pix)) {
|
|
43
|
+
return { type, pix };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
throw new Error("chave pix invalida.")
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const normalize = (input) => {
|
|
51
|
+
let { pix } = input
|
|
52
|
+
|
|
53
|
+
if(!pix || !validate({ pix})) {
|
|
54
|
+
throw new Error("chave pix invalida.")
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const { type } = identify({ pix })
|
|
58
|
+
|
|
59
|
+
if(type === 'cpf' || type === 'cnpj') {
|
|
60
|
+
pix = pix.replace(/[^0-9]/g, '')
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return { pix, type }
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = {
|
|
67
|
+
identify,
|
|
68
|
+
validate,
|
|
69
|
+
normalize
|
|
70
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pix-utils-js",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"author": "Paulo Henrique",
|
|
5
|
+
"description": "Um pacote para fornecer utilidades e validações para chaves pix.",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/paulohenriquesn/pix-utils-js.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/paulohenriquesn/pix-utils-js/issues"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/paulohenriquesn/pix-utils-js#readme",
|
|
15
|
+
"scripts": {
|
|
16
|
+
"test": "node tests/index.js"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"pix",
|
|
20
|
+
"chave pix"
|
|
21
|
+
],
|
|
22
|
+
"license": "MIT"
|
|
23
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
class Generator {
|
|
2
|
+
static generateValidPhoneNumber() {
|
|
3
|
+
const dddList = ['11', '21', '31', '41', '51', '61', '71', '81', '91'];
|
|
4
|
+
const selectedDDD = dddList[Math.floor(Math.random() * dddList.length)];
|
|
5
|
+
|
|
6
|
+
const phoneNumber = `${selectedDDD}9${Generator.generateRandomNumber(8)}`;
|
|
7
|
+
return phoneNumber;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
static generateRandomNumber(length) {
|
|
11
|
+
let randomNumber = '';
|
|
12
|
+
for (let i = 0; i < length; i++) {
|
|
13
|
+
randomNumber += Math.floor(Math.random() * 10);
|
|
14
|
+
}
|
|
15
|
+
return randomNumber;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
static generateRandomCPF() {
|
|
19
|
+
function randomDigit() {
|
|
20
|
+
return Math.floor(Math.random() * 10);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const cpfDigits = [];
|
|
24
|
+
for (let i = 0; i < 9; i++) {
|
|
25
|
+
cpfDigits.push(randomDigit());
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const firstVerifierDigit = Generator.calculateCPFVerifierDigit(cpfDigits);
|
|
29
|
+
cpfDigits.push(firstVerifierDigit);
|
|
30
|
+
|
|
31
|
+
const secondVerifierDigit = Generator.calculateCPFVerifierDigit(cpfDigits);
|
|
32
|
+
cpfDigits.push(secondVerifierDigit);
|
|
33
|
+
|
|
34
|
+
return cpfDigits.join('');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static calculateCPFVerifierDigit(digits) {
|
|
38
|
+
const sum = digits.reduce((acc, digit, index) => {
|
|
39
|
+
const multiplier = digits.length + 1 - index;
|
|
40
|
+
return acc + digit * multiplier;
|
|
41
|
+
}, 0);
|
|
42
|
+
|
|
43
|
+
const remainder = sum % 11;
|
|
44
|
+
return remainder < 2 ? 0 : 11 - remainder;
|
|
45
|
+
}
|
|
46
|
+
static generateValidCNPJ() {
|
|
47
|
+
function randomDigit() {
|
|
48
|
+
return Math.floor(Math.random() * 10);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const cnpjBase = [];
|
|
52
|
+
for (let i = 0; i < 12; i++) {
|
|
53
|
+
cnpjBase.push(randomDigit());
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const verifierDigits = Generator.calculateCNPJVerifierDigits(cnpjBase);
|
|
57
|
+
cnpjBase.push(verifierDigits[0], verifierDigits[1]);
|
|
58
|
+
|
|
59
|
+
const formattedCNPJ = cnpjBase.join('');
|
|
60
|
+
|
|
61
|
+
return formattedCNPJ.replace(/(\d{2})(\d{3})(\d{3})(\d{4})(\d{2})/, '$1.$2.$3/$4-$5');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
static calculateCNPJVerifierDigits(digits) {
|
|
65
|
+
const multipliers1 = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
|
|
66
|
+
const multipliers2 = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3];
|
|
67
|
+
|
|
68
|
+
function calculateDigit(multipliers) {
|
|
69
|
+
const sum = digits.reduce((acc, digit, index) => acc + digit * multipliers[index], 0);
|
|
70
|
+
const remainder = sum % 11;
|
|
71
|
+
return remainder < 2 ? 0 : 11 - remainder;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const firstDigit = calculateDigit(multipliers1);
|
|
75
|
+
const secondDigit = calculateDigit(multipliers2);
|
|
76
|
+
|
|
77
|
+
return [firstDigit, secondDigit];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
static generateRandomUUID() {
|
|
81
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
|
82
|
+
const r = Math.random() * 16 | 0;
|
|
83
|
+
const v = c === 'x' ? r : (r & 0x3 | 0x8);
|
|
84
|
+
return v.toString(16);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
static generateRandomEmail() {
|
|
89
|
+
const domains = ['gmail.com', 'yahoo.com', 'hotmail.com', 'outlook.com', 'example.com'];
|
|
90
|
+
const username = Generator.generateRandomString(8);
|
|
91
|
+
const domain = domains[Math.floor(Math.random() * domains.length)];
|
|
92
|
+
return `${username}@${domain}`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
static generateRandomString(length) {
|
|
96
|
+
const characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
|
97
|
+
let randomString = '';
|
|
98
|
+
for (let i = 0; i < length; i++) {
|
|
99
|
+
const randomIndex = Math.floor(Math.random() * characters.length);
|
|
100
|
+
randomString += characters.charAt(randomIndex);
|
|
101
|
+
}
|
|
102
|
+
return randomString;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
module.exports = { Generator }
|
package/tests/index.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
const assert = require('assert');
|
|
2
|
+
const { describe, it } = require('node:test')
|
|
3
|
+
const { identify, normalize, validate } = require('../index')
|
|
4
|
+
const { Generator } = require('./generator')
|
|
5
|
+
|
|
6
|
+
describe('validate', () => {
|
|
7
|
+
it('should returns false if no pix key is provided', (t) => {
|
|
8
|
+
const sut = validate({pix: 'invalid'})
|
|
9
|
+
assert.strictEqual(sut, false);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('should returns true if email key is provided and is valid', (t) => {
|
|
13
|
+
const sut = validate({pix: Generator.generateRandomEmail()})
|
|
14
|
+
assert.strictEqual(sut, true);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should returns true if phone number key is provided and is valid', (t) => {
|
|
18
|
+
const sut = validate({pix: Generator.generateValidPhoneNumber()})
|
|
19
|
+
assert.strictEqual(sut, true);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should returns true if random key is provided and is valid', (t) => {
|
|
23
|
+
const sut = validate({pix: Generator.generateRandomUUID()})
|
|
24
|
+
assert.strictEqual(sut, true);
|
|
25
|
+
});
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
describe('identify', () => {
|
|
29
|
+
it('should returns email if email pix is provided', (t) => {
|
|
30
|
+
const sut = identify({ pix: 'test@gmail.com'})
|
|
31
|
+
assert.notStrictEqual(sut, { pix: 'test@gmail.com', type: 'email' });
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should returns cpf if cpf pix is provided', (t) => {
|
|
35
|
+
const cpf = Generator.generateRandomCPF()
|
|
36
|
+
const sut = identify({ pix: cpf})
|
|
37
|
+
assert.notStrictEqual(sut, { pix: cpf, type: 'cpf' });
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
it('should returns cnpj if cnpj pix is provided', (t) => {
|
|
42
|
+
const cnpj = Generator.generateValidCNPJ()
|
|
43
|
+
const sut = identify({ pix: cnpj})
|
|
44
|
+
assert.notStrictEqual(sut, { pix: cnpj, type: 'cnpj' });
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should returns random if random pix is provided', (t) => {
|
|
48
|
+
const random = Generator.generateRandomUUID()
|
|
49
|
+
const sut = identify({ pix: random })
|
|
50
|
+
assert.notStrictEqual(sut, { pix: random, type: 'random' });
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should returns phone if random pix is provided', (t) => {
|
|
54
|
+
const phone = Generator.generateValidPhoneNumber()
|
|
55
|
+
const sut = identify({ pix: phone })
|
|
56
|
+
assert.notStrictEqual(sut, { pix: phone, type: 'phone' });
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should throws if invalid pix is provided', (t) => {
|
|
60
|
+
let sut;
|
|
61
|
+
try {
|
|
62
|
+
sut = identify({ pix: 'abc' })
|
|
63
|
+
}catch (err) {
|
|
64
|
+
sut = err;
|
|
65
|
+
}
|
|
66
|
+
assert.notStrictEqual(sut, Error);
|
|
67
|
+
});
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
describe("normalize", () => {
|
|
71
|
+
it('should normalize cpf key', (t) => {
|
|
72
|
+
assert.deepEqual(normalize({ pix: "000.000.000-00"}), { pix: '00000000000', type: 'cpf'});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should normalize cnpj key', (t) => {
|
|
76
|
+
assert.deepEqual(normalize({ pix: "00.000.000/0000-00"}), { pix: '00000000000000', type: 'cnpj'});
|
|
77
|
+
});
|
|
78
|
+
})
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
class Validator {
|
|
2
|
+
isEmail(email) {
|
|
3
|
+
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
|
|
4
|
+
return emailRegex.test(email)
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
isUUID(uuid) {
|
|
8
|
+
const uuidRegex = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/
|
|
9
|
+
return uuidRegex.test(uuid)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
isPhoneNumber(phoneNumber) {
|
|
13
|
+
const phoneNumberRegex = /^(\d{3}-\d{3}-\d{4}|\d{10}|\(\d{3}\)\s*\d{3}-\d{4}|\d{3}\s\d{3}\s\d{4})$/;
|
|
14
|
+
return phoneNumberRegex.test(phoneNumber)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
isCPF(cpf) {
|
|
18
|
+
const cpfRegex = /^(\d{3}\.\d{3}\.\d{3}-\d{2}|\d{11})$/;
|
|
19
|
+
return cpfRegex.test(cpf)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
isCNPJ(cnpj) {
|
|
23
|
+
const cnpjRegex = /^(\d{2}\.\d{3}\.\d{3}\/\d{4}-\d{2}|\d{14})$/;
|
|
24
|
+
return cnpjRegex.test(cnpj)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
module.exports = {
|
|
29
|
+
Validator
|
|
30
|
+
}
|