UNPKG

@matheusflauzino/pix-brcode-parser

Version:

Parser para BRCode do PIX que segue as especificações do Banco Central do Brasil. Suporta códigos estáticos e dinâmicos, validação de CRC16 e extração de dados como chave PIX, valor, beneficiário e mais.

90 lines (89 loc) 2.92 kB
export function parseTLV(data) { const result = {}; let i = 0; while (i < data.length) { if (i + 4 > data.length) { throw new Error('Invalid TLV format: incomplete tag or length'); } const tag = data.slice(i, i + 2); const lengthStr = data.slice(i + 2, i + 4); if (!/^\d{2}$/.test(tag) || !/^\d{2}$/.test(lengthStr)) { throw new Error('Campo fora do padrão'); } const length = parseInt(lengthStr, 10); if (i + 4 + length > data.length) { throw new Error('Invalid TLV format: length exceeds data size'); } const value = data.slice(i + 4, i + 4 + length); result[tag] = value; i += 4 + length; } return result; } export function computeCRC16(data) { let crc = 0xffff; for (let i = 0; i < data.length; i++) { crc ^= data.charCodeAt(i) << 8; for (let j = 0; j < 8; j++) { if (crc & 0x8000) { crc = (crc << 1) ^ 0x1021; } else { crc <<= 1; } crc &= 0xffff; } } return crc.toString(16).toUpperCase().padStart(4, '0'); } export function parseBRCode(brCode) { const sanitized = brCode.replace(/\s+/g, ''); const tlv = parseTLV(sanitized); if (!Object.prototype.hasOwnProperty.call(tlv, '00')) { throw new Error('Tag obrigatória ausente: 00'); } if (tlv['63']) { const toCheck = sanitized.slice(0, sanitized.length - 4); const expected = tlv['63'].toUpperCase(); const calculated = computeCRC16(toCheck); if (calculated !== expected) { throw new Error(`Invalid CRC16: expected ${expected}, got ${calculated}`); } } const type = tlv['01'] === '12' ? 'DYNAMIC' : 'STATIC'; const additional = tlv['62'] ? parseTLV(tlv['62']) : {}; let pixKey; let infoAdicional; for (let i = 26; i <= 51; i++) { const tag = String(i).padStart(2, '0'); const value = tlv[tag]; if (value) { const sub = parseTLV(value); if (sub['00'] && sub['00'].toUpperCase() === 'BR.GOV.BCB.PIX') { pixKey = sub['01']; if (sub['02']) { infoAdicional = sub['02']; } break; } } } if (!pixKey) { throw new Error('Tag obrigatória ausente: 26'); } return { raw: sanitized, type, payloadFormatIndicator: tlv['00'] || '', merchantCategoryCode: tlv['52'], transactionCurrency: tlv['53'], transactionAmount: tlv['54'] ? parseFloat(tlv['54']) : undefined, countryCode: tlv['58'], merchantName: tlv['59'], merchantCity: tlv['60'], postalCode: tlv['61'], txid: additional['05'], pixKey, infoAdicional, }; }