fix: validações de tipo, receber na conta da empresa, og image e idioma.
This commit is contained in:
parent
16a9720a12
commit
72bbbeea4a
@ -21,8 +21,8 @@ namespace QRRapidoApp.Controllers
|
||||
private readonly MongoDbContext _context;
|
||||
private readonly AdDisplayService _adDisplayService;
|
||||
private readonly StripeService _stripeService; // Injected StripeService
|
||||
private readonly string _pixKey = "chave-pix-padrao@qrrapido.site";
|
||||
private readonly string _merchantName = "QR Rapido";
|
||||
private readonly string _pixKey = "12048391000101";
|
||||
private readonly string _merchantName = "RRCG Gerenciamento";
|
||||
private readonly string _merchantCity = "SAO PAULO";
|
||||
|
||||
public PagamentoController(
|
||||
|
||||
@ -237,7 +237,6 @@ builder.Services.Configure<RequestLocalizationOptions>(options =>
|
||||
options.RequestCultureProviders.Clear();
|
||||
options.RequestCultureProviders.Add(new CustomRouteDataRequestCultureProvider());
|
||||
options.RequestCultureProviders.Add(new QueryStringRequestCultureProvider());
|
||||
options.RequestCultureProviders.Add(new CookieRequestCultureProvider());
|
||||
});
|
||||
|
||||
// Custom Services
|
||||
|
||||
@ -91,7 +91,7 @@
|
||||
<!-- Open Graph -->
|
||||
<meta property="og:title" content="@(ViewBag.Title ?? "QR Rapido") - @Localizer["FastestQRGeneratorWeb"]">
|
||||
<meta property="og:description" content="@(ViewBag.Description ?? Localizer["QRGenerateDescription"])">
|
||||
<meta property="og:image" content="https://qrrapido.site/images/qrrapido-og-image.png">
|
||||
<meta property="og:image" content="https://qrrapido.site/images/pix-og.png">
|
||||
<meta property="og:url" content="@canonicalUrl">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:site_name" content="QR Rapido">
|
||||
|
||||
|
Before Width: | Height: | Size: 392 KiB After Width: | Height: | Size: 392 KiB |
@ -44,6 +44,15 @@ class PixQRGenerator {
|
||||
const previewElement = document.getElementById('pix-preview-text');
|
||||
|
||||
if (previewElement) {
|
||||
// Validação de "Preguiça" (Laziness Validation)
|
||||
// Checks if user selected one type but entered another
|
||||
const validationWarning = this.validateKeyType(data.keyType, data.key);
|
||||
|
||||
if (validationWarning) {
|
||||
previewElement.innerHTML = `<span class="text-danger"><i class="fas fa-exclamation-triangle"></i> ${validationWarning}</span>`;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data.key || !data.name || !data.city) {
|
||||
previewElement.textContent = "Preencha os campos obrigatórios (Chave, Nome, Cidade) para ver o preview.";
|
||||
return;
|
||||
@ -58,6 +67,157 @@ class PixQRGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
validatePixData() {
|
||||
const data = this.collectPixData();
|
||||
const errors = [];
|
||||
|
||||
// Required fields
|
||||
if (!data.key.trim()) errors.push('Chave PIX é obrigatória');
|
||||
if (!data.name.trim()) errors.push('Nome do beneficiário é obrigatório');
|
||||
if (!data.city.trim()) errors.push('Cidade do beneficiário é obrigatória');
|
||||
|
||||
// Length limits
|
||||
if (data.name.length > 25) errors.push('Nome deve ter no máximo 25 caracteres');
|
||||
if (data.city.length > 15) errors.push('Cidade deve ter no máximo 15 caracteres (Regra do Banco Central)');
|
||||
|
||||
// Amount validation
|
||||
if (data.amount) {
|
||||
const amount = parseFloat(data.amount.replace(/\./g, '').replace(',', '.'));
|
||||
if (isNaN(amount) || amount <= 0) {
|
||||
errors.push('Valor deve ser um número maior que zero');
|
||||
}
|
||||
}
|
||||
|
||||
// Smart Key Validation
|
||||
const keyWarning = this.validateKeyType(data.keyType, data.key);
|
||||
if (keyWarning) {
|
||||
errors.push(keyWarning);
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
validateKeyType(type, key) {
|
||||
if (!key) return null;
|
||||
const cleanKey = key.replace(/\D/g, ''); // Numbers only
|
||||
|
||||
// Case 1: Selected CPF/CNPJ
|
||||
if (type === 'cpf') {
|
||||
if (cleanKey.length === 11) {
|
||||
if (!this.isValidCPF(cleanKey)) {
|
||||
// Check if looks like phone (DDD + 9...)
|
||||
if (cleanKey[2] === '9') {
|
||||
return "Selecionado CPF, mas parece um Celular. Altere o tipo para 'Celular' ou corrija.";
|
||||
}
|
||||
return "CPF Inválido. Verifique os dígitos.";
|
||||
}
|
||||
} else if (cleanKey.length === 14) {
|
||||
if (!this.isValidCNPJ(cleanKey)) {
|
||||
return "CNPJ Inválido. Verifique os dígitos.";
|
||||
}
|
||||
} else {
|
||||
if (cleanKey.length > 0) return `CPF/CNPJ deve ter 11 ou 14 números. (Atual: ${cleanKey.length})`;
|
||||
}
|
||||
}
|
||||
|
||||
// Case 2: Selected Phone
|
||||
if (type === 'phone') {
|
||||
if (cleanKey.length > 0) {
|
||||
if (cleanKey.length !== 10 && cleanKey.length !== 11) {
|
||||
return `Telefone deve ter 10 ou 11 números (DDD + Número). (Atual: ${cleanKey.length})`;
|
||||
}
|
||||
|
||||
// Smart check: Typed a CPF in Phone field?
|
||||
if (cleanKey.length === 11 && this.isValidCPF(cleanKey)) {
|
||||
// Check if it's NOT a typical mobile number structure
|
||||
// Mobile: DDD (11-99) + 9 + 8 digits.
|
||||
const ddd = parseInt(cleanKey.substring(0, 2));
|
||||
const ninthDigit = cleanKey[2];
|
||||
|
||||
// If DDD is invalid or 9th digit is not 9, but it IS a valid CPF -> Warning
|
||||
if (ddd < 11 || ddd > 99 || ninthDigit !== '9') {
|
||||
return "Isso parece ser um CPF válido, mas o tipo selecionado é 'Celular'.";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Case 3: Email
|
||||
if (type === 'email') {
|
||||
if (key.length > 0 && !key.includes('@')) {
|
||||
return "Email inválido. Deve conter '@'.";
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
isValidCPF(cpf) {
|
||||
if (typeof cpf !== "string") return false;
|
||||
cpf = cpf.replace(/[\s.-]*/igm, '');
|
||||
if (cpf.length !== 11 || !Array.from(cpf).filter(e => e !== cpf[0]).length) {
|
||||
return false;
|
||||
}
|
||||
var sum = 0;
|
||||
var remainder;
|
||||
for (var i = 1; i <= 9; i++)
|
||||
sum = sum + parseInt(cpf.substring(i-1, i)) * (11 - i);
|
||||
remainder = (sum * 10) % 11;
|
||||
if ((remainder == 10) || (remainder == 11)) remainder = 0;
|
||||
if (remainder != parseInt(cpf.substring(9, 10)) ) return false;
|
||||
sum = 0;
|
||||
for (i = 1; i <= 10; i++)
|
||||
sum = sum + parseInt(cpf.substring(i-1, i)) * (12 - i);
|
||||
remainder = (sum * 10) % 11;
|
||||
if ((remainder == 10) || (remainder == 11)) remainder = 0;
|
||||
if (remainder != parseInt(cpf.substring(10, 11)) ) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
isValidCNPJ(cnpj) {
|
||||
if (!cnpj) return false;
|
||||
cnpj = cnpj.replace(/[^\d]+/g,'');
|
||||
if (cnpj.length != 14) return false;
|
||||
// Elimina CNPJs invalidos conhecidos
|
||||
if (cnpj == "00000000000000" ||
|
||||
cnpj == "11111111111111" ||
|
||||
cnpj == "22222222222222" ||
|
||||
cnpj == "33333333333333" ||
|
||||
cnpj == "44444444444444" ||
|
||||
cnpj == "55555555555555" ||
|
||||
cnpj == "66666666666666" ||
|
||||
cnpj == "77777777777777" ||
|
||||
cnpj == "88888888888888" ||
|
||||
cnpj == "99999999999999")
|
||||
return false;
|
||||
|
||||
// Valida DVs
|
||||
let tamanho = cnpj.length - 2
|
||||
let numeros = cnpj.substring(0,tamanho);
|
||||
let digitos = cnpj.substring(tamanho);
|
||||
let soma = 0;
|
||||
let pos = tamanho - 7;
|
||||
for (let i = tamanho; i >= 1; i--) {
|
||||
soma += numeros.charAt(tamanho - i) * pos--;
|
||||
if (pos < 2) pos = 9;
|
||||
}
|
||||
let resultado = soma % 11 < 2 ? 0 : 11 - soma % 11;
|
||||
if (resultado != digitos.charAt(0)) return false;
|
||||
|
||||
tamanho = tamanho + 1;
|
||||
numeros = cnpj.substring(0,tamanho);
|
||||
soma = 0;
|
||||
pos = tamanho - 7;
|
||||
for (let i = tamanho; i >= 1; i--) {
|
||||
soma += numeros.charAt(tamanho - i) * pos--;
|
||||
if (pos < 2) pos = 9;
|
||||
}
|
||||
resultado = soma % 11 < 2 ? 0 : 11 - soma % 11;
|
||||
if (resultado != digitos.charAt(1)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
collectPixData() {
|
||||
return {
|
||||
keyType: document.getElementById('pix-key-type')?.value || 'cpf',
|
||||
@ -70,28 +230,6 @@ class PixQRGenerator {
|
||||
};
|
||||
}
|
||||
|
||||
validatePixData() {
|
||||
const data = this.collectPixData();
|
||||
const errors = [];
|
||||
|
||||
if (!data.key.trim()) errors.push('Chave PIX é obrigatória');
|
||||
if (!data.name.trim()) errors.push('Nome do beneficiário é obrigatório');
|
||||
if (!data.city.trim()) errors.push('Cidade do beneficiário é obrigatória');
|
||||
|
||||
if (data.name.length > 25) errors.push('Nome deve ter no máximo 25 caracteres');
|
||||
if (data.city.length > 15) errors.push('Cidade deve ter no máximo 15 caracteres (Regra do Banco Central)');
|
||||
|
||||
if (data.amount) {
|
||||
// Remove thousands separators (.) and replace decimal comma (,) with dot (.)
|
||||
const amount = parseFloat(data.amount.replace(/\./g, '').replace(',', '.'));
|
||||
if (isNaN(amount) || amount <= 0) {
|
||||
errors.push('Valor deve ser um número maior que zero');
|
||||
}
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
generateCRC16(payload) {
|
||||
let crc = 0xFFFF;
|
||||
const polynomial = 0x1021;
|
||||
@ -124,7 +262,6 @@ class PixQRGenerator {
|
||||
case 'phone':
|
||||
// Remove non-digits
|
||||
let nums = cleanKey.replace(/\D/g, '');
|
||||
// If it starts with 55 and is long enough, keep it
|
||||
// If 10 or 11 digits (DDD+Number), add +55
|
||||
if ((nums.length === 10 || nums.length === 11) && !nums.startsWith('55')) {
|
||||
return `+55${nums}`;
|
||||
@ -170,11 +307,6 @@ class PixQRGenerator {
|
||||
const gui = this.formatField('00', 'br.gov.bcb.pix');
|
||||
const keyField = this.formatField('01', key);
|
||||
|
||||
// Field 26: Merchant Account Information
|
||||
// Subfields: 00 (GUI), 01 (Key), 02 (Description - optional/standard varies)
|
||||
// Standard EMVCo for PIX puts Description in field 02 of ID 26?
|
||||
// Actually, BR Code standard puts "InfoAdicional" in Field 26, ID 02.
|
||||
|
||||
let merchantAccountInfo = gui + keyField;
|
||||
if (description) {
|
||||
merchantAccountInfo += this.formatField('02', description);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user