Skip to content
This repository was archived by the owner on Sep 22, 2023. It is now read-only.

Commit 3f79d17

Browse files
committedSep 21, 2021
melhorias de validação e back-end+mudança temporaria p/ gerar card token
1 parent 3716797 commit 3f79d17

File tree

8 files changed

+6202
-187
lines changed

8 files changed

+6202
-187
lines changed
 

‎controllers/sender.controller.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ const createSubscription = async (req, res) => {
66
cpf: req.body.cpf,
77
email: req.body.email,
88
cardToken: req.body.cardToken,
9-
subscription: req.body.subscription
9+
subscription: req.body.subscription,
10+
subscriptionStatus: req.body.subscriptionStatus
1011
};
1112

1213
try {

‎models/sender.model.js

+4
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,9 @@ exports.default = db.define('sender', {
2525
type: Sequelize.STRING,
2626
allowNull: true,
2727
unique: true
28+
},
29+
subscriptionStatus: {
30+
type: Sequelize.BOOLEAN,
31+
allowNull: true
2832
}
2933
});

‎public/index.html

+82-103
Original file line numberDiff line numberDiff line change
@@ -33,32 +33,9 @@ <h1 class="title">Assinatura Plano</h1>
3333
<div class="tile is-parent is-vertical" id="checkout-tile">
3434
<article class="tile is-child notification">
3535
<p class="title is-2">Checkout</p>
36+
<br />
3637

3738
<form method="POST" id="CheckoutForm">
38-
<div class="columns">
39-
<div class="field column is-two-thirds">
40-
<p class="control">
41-
<input
42-
readonly
43-
class="input is-link"
44-
type="text"
45-
placeholder="Sender Hash"
46-
id="senderHash"
47-
/>
48-
</p>
49-
</div>
50-
51-
<div class="buttons column">
52-
<button
53-
class="button is-link is-light"
54-
type="button"
55-
id="generateHash"
56-
>
57-
Gerar Hash
58-
</button>
59-
</div>
60-
</div>
61-
6239
<h2 class="subtitle is-4">Dados pessoais</h2>
6340
<div class="columns">
6441
<div class="field column is-half">
@@ -68,6 +45,7 @@ <h2 class="subtitle is-4">Dados pessoais</h2>
6845
class="input"
6946
type="email"
7047
placeholder="Email"
48+
id="senderMail"
7149
/>
7250
<span class="icon is-small is-left">
7351
<i class="fas fa-envelope"></i>
@@ -81,6 +59,7 @@ <h2 class="subtitle is-4">Dados pessoais</h2>
8159
class="input"
8260
type="text"
8361
placeholder="Nome"
62+
id="senderName"
8463
/>
8564
<span class="icon is-small is-left">
8665
<i class="fas fa-user"></i>
@@ -120,6 +99,7 @@ <h2 class="subtitle is-4">Dados pessoais</h2>
12099
class="input"
121100
type="text"
122101
placeholder="CPF"
102+
id="senderCpf"
123103
/>
124104
<span class="icon is-small is-left">
125105
<i class="fas fa-id-card"></i>
@@ -156,8 +136,7 @@ <h2 class="subtitle is-4">Dados pessoais</h2>
156136
<div class="field column">
157137
<p class="control">
158138
<input
159-
id="senderComplement"
160-
class="input"
139+
class="input addressComplement"
161140
type="text"
162141
placeholder="Complemento"
163142
/>
@@ -173,6 +152,7 @@ <h2 class="subtitle is-4">Dados pessoais</h2>
173152
class="input"
174153
type="text"
175154
placeholder="CEP"
155+
id="senderCep"
176156
/>
177157
</p>
178158
</div>
@@ -209,63 +189,76 @@ <h2 class="subtitle is-4">Dados pessoais</h2>
209189
</div>
210190

211191
<br />
212-
<h2 class="subtitle is-4">Dados de pagamento</h2>
213-
<div class="columns">
214-
<div class="field column is-one-quarter">
215-
<p class="control has-icons-left">
216-
<input
217-
required
218-
class="input"
219-
type="text"
220-
placeholder="Nº cartão"
221-
/>
222-
<span class="icon is-small is-left">
223-
<i class="fas fa-credit-card"></i>
224-
</span>
225-
</p>
226-
</div>
227-
<div class="field column">
228-
<p class="control">
229-
<input
230-
required
231-
class="input"
232-
type="text"
233-
placeholder="Bandeira"
234-
/>
235-
</p>
236-
</div>
237-
<div class="field column is-one-fifth">
238-
<p class="control">
239-
<input
240-
required
241-
class="input"
242-
type="text"
243-
placeholder="CVV"
244-
/>
245-
</p>
246-
</div>
247-
<div class="field column is-one-fifth">
248-
<p class="control">
249-
<input
250-
required
251-
class="input"
252-
type="text"
253-
placeholder="Mês exp"
254-
/>
255-
</p>
256-
</div>
257-
<div class="field column is-one-fifth">
258-
<p class="control">
259-
<input
260-
required
261-
class="input"
262-
type="text"
263-
placeholder="Ano exp"
264-
/>
265-
</p>
192+
<div id="CreditCardData">
193+
<h2 class="subtitle is-4">Dados de pagamento</h2>
194+
<div class="columns">
195+
<div class="field column is-one-third">
196+
<p class="control has-icons-left">
197+
<input
198+
required
199+
class="input"
200+
type="text"
201+
placeholder="Nº cartão"
202+
/>
203+
<span class="icon is-small is-left">
204+
<i class="fas fa-credit-card"></i>
205+
</span>
206+
</p>
207+
</div>
208+
<div class="field column is-one-quarter">
209+
<p class="control">
210+
<input
211+
required
212+
class="input"
213+
type="text"
214+
placeholder="Bandeira"
215+
/>
216+
</p>
217+
</div>
218+
<div class="field column is-one-sixth">
219+
<p class="control">
220+
<input
221+
required
222+
class="input"
223+
type="text"
224+
placeholder="CVV"
225+
/>
226+
</p>
227+
</div>
228+
<div class="field column">
229+
<p class="control">
230+
<input
231+
required
232+
class="input"
233+
type="text"
234+
placeholder="Mês exp"
235+
/>
236+
</p>
237+
</div>
238+
<div class="field column">
239+
<p class="control">
240+
<input
241+
required
242+
class="input"
243+
type="text"
244+
placeholder="Ano exp"
245+
/>
246+
</p>
247+
</div>
248+
</div>
249+
<div class="buttons">
250+
<button
251+
class="button is-info"
252+
type="button"
253+
id="confirmCard"
254+
>
255+
Validar cartão de crédito
256+
</button>
266257
</div>
267258
</div>
268259

260+
<br />
261+
<h2 class="subtitle is-4">Dados de faturamento</h2>
269262
<div class="columns">
270263
<div class="field column is-half">
271264
<p class="control has-icons-left has-icons-right">
@@ -299,6 +292,7 @@ <h2 class="subtitle is-4">Dados de pagamento</h2>
299292
class="input"
300293
type="text"
301294
placeholder="CPF"
295+
id="paymentCpf"
302296
/>
303297
<small>*Apenas números</small>
304298
</p>
@@ -330,16 +324,14 @@ <h2 class="subtitle is-4">Dados de pagamento</h2>
330324
</p>
331325
</div>
332326
<div class="field column">
333-
<p class="control has-icons-left">
327+
<p class="control">
334328
<input
335329
required
336330
class="input"
337331
type="text"
338-
placeholder="CPF"
332+
placeholder="CEP"
333+
id="paymentCep"
339334
/>
340-
<span class="icon is-small is-left">
341-
<i class="fas fa-id-card"></i>
342-
</span>
343335
</p>
344336
</div>
345337
</div>
@@ -371,8 +363,7 @@ <h2 class="subtitle is-4">Dados de pagamento</h2>
371363
<div class="field column">
372364
<p class="control">
373365
<input
374-
id="paymentComplement"
375-
class="input"
366+
class="input addressComplement"
376367
type="text"
377368
placeholder="Complemento"
378369
/>
@@ -381,16 +372,6 @@ <h2 class="subtitle is-4">Dados de pagamento</h2>
381372
</div>
382373

383374
<div class="columns">
384-
<div class="field column is-one-quarter">
385-
<p class="control has-icons-left">
386-
<input
387-
required
388-
class="input"
389-
type="text"
390-
placeholder="CEP"
391-
/>
392-
</p>
393-
</div>
394375
<div class="field column">
395376
<p class="control has-icons-left has-icons-right">
396377
<input
@@ -427,17 +408,13 @@ <h2 class="subtitle is-4">Dados de pagamento</h2>
427408
<button
428409
class="button is-primary is-large"
429410
type="button"
430-
id="sendCheckout"
411+
id="paySub"
431412
>
432413
Enviar
433414
</button>
434415
</div>
435416
</form>
436417
</article>
437-
<article class="tile is-child notification">
438-
<p class="title">...tiles</p>
439-
<p class="subtitle">Bottom tile</p>
440-
</article>
441418
</div>
442419
</div>
443420
</div>
@@ -454,7 +431,7 @@ <h2 class="subtitle is-4">Dados de pagamento</h2>
454431
</p>
455432
<div class="columns">
456433
<h2 class="title column is-half">Valor:</h2>
457-
<h4 class="title column">R$ 150,00</h4>
434+
<h4 class="title column">R$ 350,00</h4>
458435
</div>
459436
</div>
460437
</div>
@@ -464,6 +441,7 @@ <h4 class="title column">R$ 150,00</h4>
464441
</div>
465442
</section>
466443

444+
<script src="./js-brasil.js"></script>
467445
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
468446
<script
469447
type="text/javascript"
@@ -476,6 +454,7 @@ <h4 class="title column">R$ 150,00</h4>
476454
referrerpolicy="no-referrer"
477455
></script>
478456
<script src="./index.js" type="module"></script>
457+
<script src="./validations.js" type="module"></script>
479458
<script src="./paymentBodies.js" type="module"></script>
480459
</body>
481460
</html>

‎public/index.js

+72-74
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { createPlanBody } from './paymentBodies.js';
21
import { checkEmptyInputs } from './utils.js';
32
import { XmlBodyBuilder } from './xmlBuilder.js';
43

@@ -29,80 +28,19 @@ window.addEventListener('DOMContentLoaded', async () => {
2928
}
3029
});
3130

32-
PagSeguroDirectPayment.createCardToken({
33-
cardNumber: '4111111111111111', // Número do cartão de crédito
34-
brand: 'visa', // Bandeira do cartão
35-
cvv: '123', // CVV do cartão
36-
expirationMonth: '12', // Mês da expiração do cartão
37-
expirationYear: '2030', // Ano da expiração do cartão, é necessário os 4 dígitos.
38-
success: (res) => {
39-
console.log(res);
40-
localStorage.cardToken = res.card.token;
41-
},
42-
error: (res) => {
43-
console.log(res);
44-
},
45-
complete: (res) => {
46-
console.log('Token criado com sucesso');
47-
}
48-
});
49-
});
50-
51-
document.getElementById('generateHash').addEventListener('click', () => {
52-
PagSeguroDirectPayment.onSenderHashReady((res) => {
53-
if (res.status == 'error') {
54-
console.log(res.message);
55-
return false;
56-
}
57-
58-
localStorage.senderHash = res.senderHash;
59-
document.getElementById('senderHash').value = res.senderHash;
60-
console.log('Sender hash gerado!');
61-
});
62-
});
63-
64-
document.getElementById('sendCheckout').addEventListener('click', () => {
65-
const formData = Array.from(
66-
document.forms['CheckoutForm'].getElementsByTagName('input')
67-
);
68-
69-
if (checkEmptyInputs(formData)) return;
70-
71-
const sender = formData.slice(0, 13).map((input, i) => {
72-
if (i <= 12) return input.value;
73-
74-
return;
75-
});
31+
document.getElementById('senderName').addEventListener('blur', () => {
32+
PagSeguroDirectPayment.onSenderHashReady((res) => {
33+
if (res.status == 'error') {
34+
console.log(res.message);
35+
return false;
36+
}
7637

77-
const payment = formData.slice(13).map((input) => {
78-
return input.value;
38+
localStorage.senderHash = res.senderHash;
39+
console.log('Sender hash gerado!');
40+
});
7941
});
8042

81-
console.log(sender, payment);
82-
83-
/* paymentBody.payment.sender.hash = localStorage.getItem('senderHash');
84-
paymentBody.payment.creditCard.token = localStorage.getItem('cardToken');
85-
86-
const paymentXmlBody = xmlBodyParser(paymentBody);
87-
88-
console.log(domParser.parseFromString(paymentXmlBody, 'text/xml'));
89-
90-
axios
91-
.request({
92-
url: `http://localhost:3000/transactions`,
93-
method: 'post',
94-
data: { paymentXmlBody: paymentXmlBody }
95-
})
96-
.then(({ data }) => {
97-
console.log(data);
98-
99-
localStorage.removeItem('cardToken');
100-
localStorage.removeItem('senderHash');
101-
})
102-
.catch((err) => console.log(err)); */
103-
});
104-
105-
/* document.getElementById('createPlan').addEventListener('click', async () => {
43+
/* document.getElementById('createPlan').addEventListener('click', async () => {
10644
const planXmlBody = xmlBodyParser(createPlanBody());
10745
10846
console.log(domParser.parseFromString(planXmlBody, 'text/xml'));
@@ -120,7 +58,65 @@ document.getElementById('sendCheckout').addEventListener('click', () => {
12058
}
12159
});
12260
123-
document.getElementById('paySub').addEventListener('click', async () => {
61+
*/
62+
63+
//TODO dá pra melhorar. Tem uma função que descobre a bandeira após 5 numeros. Fazer a validação sem precisar da interação do user pra isso
64+
document.getElementById('confirmCard').addEventListener('click', () => {
65+
const creditCardData = Array.from(
66+
document.getElementById('CreditCardData').querySelectorAll('[required]')
67+
);
68+
69+
if (checkEmptyInputs(creditCardData)) return;
70+
71+
PagSeguroDirectPayment.createCardToken({
72+
cardNumber: creditCardData[0].value, // Número do cartão de crédito
73+
brand: creditCardData[1].value, // Bandeira do cartão
74+
cvv: creditCardData[2].value, // CVV do cartão
75+
expirationMonth: creditCardData[3].value, // Mês da expiração do cartão
76+
expirationYear: creditCardData[4].value, // Ano da expiração do cartão, é necessário os 4 dígitos.
77+
success: (res) => {
78+
console.log(res);
79+
localStorage.cardToken = res.card.token;
80+
},
81+
error: (res) => {
82+
console.log(res);
83+
},
84+
complete: (res) => {
85+
console.log('Token criado com sucesso');
86+
}
87+
});
88+
});
89+
90+
document.getElementById('paySub').addEventListener('click', async () => {
91+
const formData = Array.from(
92+
document.forms['CheckoutForm'].querySelectorAll('[required]')
93+
);
94+
95+
const complements = Array.from(
96+
document.querySelectorAll('.addressComplement')
97+
).map((input) => (input.value === '' ? (input.value = '-') : null));
98+
99+
if (checkEmptyInputs(formData)) return;
100+
101+
const sender = formData.slice(0, 13).map((input, i) => {
102+
if (i <= 12) return input.value;
103+
104+
return;
105+
});
106+
107+
const payment = formData.slice(13).map((input) => {
108+
return input.value;
109+
});
110+
111+
console.log(sender, payment);
112+
113+
const senderHash = localStorage.getItem('senderHash');
114+
});
115+
/*
116+
const paymentXmlBody = xmlBodyParser(paymentBody);
117+
118+
console.log(domParser.parseFromString(paymentXmlBody, 'text/xml'));
119+
124120
try {
125121
const { data } = await axios.request({
126122
url: 'http://localhost:3000/subscribe',
@@ -131,9 +127,11 @@ document.getElementById('paySub').addEventListener('click', async () => {
131127
console.log(data);
132128
} catch (err) {
133129
console.log(err);
134-
}
130+
} */
135131
});
136132

133+
/*
134+
137135
// valor obtido como resultado do request acima, caso cancele uma sub terá que gerar outra
138136
const subCode = '69566C53E7E78A1334A6FFB2EA060915';
139137

‎public/js-brasil.js

+5,911
Large diffs are not rendered by default.

‎public/utils.js

+70-7
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
11
export const checkEmptyInputs = (formData) => {
2-
const emptyInput = formData.some(
3-
(input) =>
4-
input.value === '' &&
5-
input.id !== 'paymentComplement' &&
6-
input.id !== 'senderComplement'
7-
),
2+
const emptyInput = formData.some((input) => input.value === ''),
83
errorElement = `<article id="errorForm" class="message is-danger">
94
<div class="message-header">
105
<p>Erro</p>
116
</div>
127
<div class="message-body">
13-
Obrigatório preencher todos os dados.
8+
Preencha os campos obrigatórios
149
</div>
1510
</article>`;
1611

1712
if (emptyInput) {
1813
window.scrollTo({ top: 110, behavior: 'smooth' });
1914

15+
formData.map((input) => {
16+
return input.value !== ''
17+
? input.classList.remove('is-danger')
18+
: input.classList.add('is-danger');
19+
});
20+
2021
document
2122
.getElementById('checkout-tile')
2223
.insertAdjacentHTML('afterbegin', errorElement);
@@ -30,3 +31,65 @@ export const checkEmptyInputs = (formData) => {
3031

3132
return false;
3233
};
34+
35+
export const buildPaymentBody = (complements, ...inputData) => {
36+
return {
37+
plan: '94FE62572C2C682334694F9FE8F5300F',
38+
reference: `test${Math.random()}`,
39+
sender: {
40+
name: inputData[1],
41+
email: inputData[0],
42+
hash: localStorage.getItem('senderHash'),
43+
phone: {
44+
areaCode: inputData[2],
45+
number: inputData[3]
46+
},
47+
address: {
48+
street: inputData[5],
49+
number: inputData[6],
50+
complement: complements[0],
51+
district: 'It',
52+
city: 'Sao Paulo',
53+
state: 'SP',
54+
country: 'BRA',
55+
postalCode: '06240300'
56+
},
57+
documents: [
58+
{
59+
type: 'CPF',
60+
value: '00000000000'
61+
}
62+
]
63+
},
64+
paymentMethod: {
65+
type: 'CREDITCARD',
66+
creditCard: {
67+
token: localStorage.getItem('cardToken'),
68+
holder: {
69+
name: 'teste Teste',
70+
birthDate: '04/12/1991',
71+
documents: [
72+
{
73+
type: 'CPF',
74+
value: '87770392543'
75+
}
76+
],
77+
phone: {
78+
areaCode: '11',
79+
number: '20516250'
80+
},
81+
billingAddress: {
82+
street: 'Vila Lygia Fagundes Teles',
83+
number: '125',
84+
complement: '',
85+
district: 'Jurunas',
86+
city: 'Belém',
87+
state: 'PA',
88+
country: 'BRA',
89+
postalCode: '66030402'
90+
}
91+
}
92+
}
93+
}
94+
};
95+
};

‎public/validations.js

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/**
2+
* Closure que pode ser especializada para validar diferentes tipos de dados de acordo com a função de validação passada
3+
* @param {function} validateFunction função de validação, como alguma do jsbrasil.validateBr (que sempre retornam booleans)
4+
* @returns {function} validatorFunction função de validação especializada que recebe:
5+
* @param {string} inputData valor a ser validado
6+
* @param {string} errorMessage mensagem de erro caso a validação retorne false
7+
*/
8+
const validateInputFunction = (validateFunction) => {
9+
return (inputData, errorMessage) => {
10+
const errorElement = `<article id="errorForm" class="message is-danger">
11+
<div class="message-header">
12+
<p>Erro</p>
13+
</div>
14+
<div class="message-body">
15+
${errorMessage}
16+
</div>
17+
</article>`;
18+
19+
if (!validateFunction(inputData)) {
20+
window.scrollTo({ top: 110, behavior: 'smooth' });
21+
22+
document
23+
.getElementById('checkout-tile')
24+
.insertAdjacentHTML('afterbegin', errorElement);
25+
26+
setTimeout(() => {
27+
document.getElementById('errorForm').remove();
28+
}, 3500);
29+
30+
return true;
31+
}
32+
33+
return false;
34+
};
35+
};
36+
37+
/**
38+
* Valida o valor recebido no input após o usuário clicar fora do elemento e exibe um feedback visual + mensagem em caso de valor inválido
39+
* @param {string} element elemento input cujo valor recebido será validado
40+
* @param {string} dataTypeToValidate tipo de dado a ser recebido e validado, por exemplo CPF, CEP, etc (deve estar de acordo com os métodos da lib jsbrasil)
41+
* @param {string} errorMessage mensagem de erro
42+
*/
43+
const addValidationEvent = (element, dataTypeToValidate, errorMessage) => {
44+
document.getElementById(element).addEventListener('blur', (e) => {
45+
const isInputValueInvalid = validateInputFunction(
46+
jsbrasil.validateBr[dataTypeToValidate]
47+
);
48+
49+
isInputValueInvalid(e.target.value, errorMessage) === true
50+
? e.target.classList.add('is-danger')
51+
: e.target.classList.remove('is-danger');
52+
});
53+
};
54+
55+
addValidationEvent('senderMail', 'email', 'Endereço de email inválido');
56+
addValidationEvent('senderCpf', 'cpf', 'Numero do CPF inválido');
57+
addValidationEvent('senderCep', 'cep', 'Número do CEP inválido');
58+
addValidationEvent('paymentCpf', 'cpf', 'Número do CPF inválido');
59+
addValidationEvent('paymentCep', 'cep', 'Número do CEP inválid.');

‎validations/validate-request.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ const { validateBr } = require('js-brasil');
22

33
module.exports = (req, res, next) => {
44
console.log(req.body);
5-
5+
/*
66
const isCpfValid = validateBr.cpf(req.body.cpf);
77
const isCepValid = validateBr.cep(req.body.cep);
88
const isEmailValid = validateBr.email(req.body.email);
99
1010
if (!isCpfValid || !isCepValid || !isEmailValid)
11-
throw new Error('Invalid document');
11+
return res.status(400).json('Invalid document'); */
1212

1313
next();
1414
};

0 commit comments

Comments
 (0)
This repository has been archived.