UNPKG

@pix.js/core

Version:
397 lines (391 loc) 14.3 kB
// src/dynamic.ts import { crc16ccitt } from "crc"; // src/types.ts var PointOfInitiationMethod = /* @__PURE__ */ ((PointOfInitiationMethod3) => { PointOfInitiationMethod3[PointOfInitiationMethod3["OnTimeOnly"] = 12] = "OnTimeOnly"; return PointOfInitiationMethod3; })(PointOfInitiationMethod || {}); var PixKeyType = /* @__PURE__ */ ((PixKeyType2) => { PixKeyType2["Cpf"] = "CPF"; PixKeyType2["Cnpj"] = "CNPJ"; PixKeyType2["Email"] = "EMAIL"; PixKeyType2["Phone"] = "PHONE"; PixKeyType2["Evp"] = "EVP"; return PixKeyType2; })(PixKeyType || {}); var PixQrCodeType = /* @__PURE__ */ ((PixQrCodeType2) => { PixQrCodeType2["Static"] = "STATIC"; PixQrCodeType2["Dynamic"] = "DYNAMIC"; return PixQrCodeType2; })(PixQrCodeType || {}); var ParserError = class extends Error { constructor(message) { super(message); this.name = "ParserError"; } }; // src/utils.ts var formatText = (text, maxLength) => text.substring(0, maxLength).normalize("NFD").replace(/[\u0300-\u036f]/g, ""); var appendToBrCode = (id, value) => { const length = value.length.toString().padStart(2, "0"); return `${id}${length}${value}`; }; var deconstructEntries = (payload, signatureLength = 4) => { const entries = {}; let position = 0; while (position < payload.length - signatureLength) { const id = payload.slice(position, position + 2); position += 2; const length = Number.parseInt(payload.slice(position, position + 2)); position += 2; const value = payload.slice(position, position + length); position += length; entries[id] = { id, length, value }; } return entries; }; // src/dynamic.ts var toDynamicPix = (payload) => { const buildPayloadParts = () => { const parts = [ ["00", "01"], ["01", payload.pointOfInitiationMethod?.toString().padStart(2, "0") ?? ""], [ "26", [ appendToBrCode("00", payload.merchantAccountInfo.gui ?? "br.gov.bcb.pix"), appendToBrCode("25", payload.merchantAccountInfo.url) ].join("") ], ["52", payload.merchantCategoryCode ?? "0000"], ["53", payload.transactionCurrency ?? "986"], ["54", payload.value?.toFixed(2) ?? ""], ["58", payload.countryCode ?? "BR"], ["59", formatText(payload.merchantName, 25)], ["60", formatText(payload.merchantCity, 15)], ["61", payload.postalCode ?? ""], ["62", appendToBrCode("05", payload.additionalData.txId)], payload.unreservedTemplate ? [ "80", appendToBrCode("00", payload.unreservedTemplate?.gui ?? ""), appendToBrCode("25", payload.unreservedTemplate?.url ?? "") ] : [] ]; return parts.filter(([_, value]) => value).map(([id, value]) => appendToBrCode(id, value)).join(""); }; const payloadString = `${buildPayloadParts()}6304`; const crcResult = crc16ccitt(payloadString).toString(16).toUpperCase().padStart(4, "0"); return `${payloadString}${crcResult}`; }; var parseDynamicPix = (payload) => { try { const entries = deconstructEntries(payload); entries["26"].subEntries = deconstructEntries(entries["26"]?.value ?? ""); entries["62"].subEntries = deconstructEntries(entries["62"]?.value ?? ""); if (entries["80"]) { entries["80"].subEntries = deconstructEntries(entries["80"]?.value ?? ""); } return { merchantAccountInfo: { gui: entries["26"].subEntries?.["00"]?.value, url: entries["26"].subEntries?.["25"]?.value }, pointOfInitiationMethod: entries["01"]?.value ? Number.parseInt(entries["01"]?.value) : void 0, merchantCategoryCode: entries["52"]?.value, transactionCurrency: entries["53"]?.value, countryCode: entries["58"]?.value, merchantName: entries["59"]?.value, merchantCity: entries["60"]?.value, postalCode: entries["61"]?.value, additionalData: { txId: entries["62"].subEntries?.["05"]?.value }, unreservedTemplate: entries["80"] ? { gui: entries["80"].subEntries?.["00"]?.value, url: entries["80"].subEntries?.["25"]?.value } : void 0 }; } catch (error) { throw new ParserError(`Invalid dynamic pix payload ${error}`); } }; var isDynamicPix = (payload) => { try { const entries = deconstructEntries(payload); entries["26"].subEntries = deconstructEntries(entries["26"].value); return !!entries["26"].subEntries["25"]?.value; } catch (error) { return false; } }; // src/key-detector.ts var pixKeyRegexes = { email: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/, phone: /^(\+)?([55]{2})?\s?\(?([0-9]{2})\)?\s?([9])([0-9]{4})-?([0-9]{4})$/, random: /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i }; function isCPF(cpf) { const cpfDigits = cpf.replace(/[^\d]/g, ""); if (cpfDigits.length !== 11) return false; let sum = 0; for (let i = 0; i < 9; i++) { sum += Number.parseInt(cpfDigits.charAt(i)) * (10 - i); } let digit = 11 - sum % 11; if (digit >= 10) digit = 0; if (digit !== Number.parseInt(cpfDigits.charAt(9))) return false; sum = 0; for (let i = 0; i < 10; i++) { sum += Number.parseInt(cpfDigits.charAt(i)) * (11 - i); } digit = 11 - sum % 11; if (digit >= 10) digit = 0; if (digit !== Number.parseInt(cpfDigits.charAt(10))) return false; return true; } function isCNPJ(cnpj) { const cnpjDigits = cnpj.replace(/[^\d]/g, ""); if (cnpjDigits.length !== 14) return false; let size = cnpjDigits.length - 2; let numbers = cnpjDigits.substring(0, size); const digits = cnpjDigits.substring(size); let sum = 0; let pos = size - 7; for (let i = size; i >= 1; i--) { sum += Number.parseInt(numbers.charAt(size - i)) * pos--; if (pos < 2) pos = 9; } let result = sum % 11 < 2 ? 0 : 11 - sum % 11; if (result !== Number.parseInt(digits.charAt(0))) return false; size = size + 1; numbers = cnpjDigits.substring(0, size); sum = 0; pos = size - 7; for (let i = size; i >= 1; i--) { sum += Number.parseInt(numbers.charAt(size - i)) * pos--; if (pos < 2) pos = 9; } result = sum % 11 < 2 ? 0 : 11 - sum % 11; if (result !== Number.parseInt(digits.charAt(1))) return false; return true; } var keyDetector = { isCPF: (key) => isCPF(key), isCNPJ: (key) => isCNPJ(key), isEmail: (key) => pixKeyRegexes.email.test(key), isPhone: (key) => pixKeyRegexes.phone.test(key), isRandom: (key) => pixKeyRegexes.random.test(key) }; var getPixKeyType = (key) => { switch (true) { case keyDetector.isCPF(key): return "CPF" /* Cpf */; case keyDetector.isCNPJ(key): return "CNPJ" /* Cnpj */; case keyDetector.isEmail(key): return "EMAIL" /* Email */; case keyDetector.isPhone(key): return "PHONE" /* Phone */; case keyDetector.isRandom(key): return "EVP" /* Evp */; default: throw new Error("Invalid Pix key"); } }; // src/schemas.ts import { z } from "zod"; var staticMerchantAccountInfoSchema = z.object({ gui: z.string(), key: z.string(), merchantAdditionalInfo: z.string().optional(), fss: z.string().optional() }).refine((schema) => { let totalLength = schema.gui.length + schema.key.length; if (schema.merchantAdditionalInfo) { totalLength += schema.merchantAdditionalInfo?.length; } if (schema.fss) { totalLength += schema.fss?.length; } return totalLength <= 99; }, "Combined length of gui, key, merchantAdditionalInfo and fss must not exceed 99 characters"); var dynamicMerchantAccountInfoSchema = z.object({ gui: z.string(), url: z.string().regex(/^[^:]+\.[^:]+/), merchantAdditionalInfo: z.string().optional(), fss: z.string().optional() }).refine((schema) => { let totalLength = schema.gui.length + schema.url.length; if (schema.merchantAdditionalInfo) { totalLength += schema.merchantAdditionalInfo?.length; } if (schema.fss) { totalLength += schema.fss?.length; } return totalLength <= 99; }, "Combined length of gui, url, merchantAdditionalInfo and fss must not exceed 99 characters"); var additionalData = z.object({ txId: z.string().min(1).max(25) }); var unreservedTemplate = z.object({ gui: z.string().optional(), url: z.string().url().optional() }).refine((schema) => { return (schema.gui?.length ?? 0) + (schema.url?.length ?? 0) <= 99; }, "Combined length of gui and url must not exceed 99 characters"); var staticPixSchema = z.object({ merchantAccountInfo: staticMerchantAccountInfoSchema, value: z.number().positive().min(1).max(13).optional(), merchantCategoryCode: z.string().max(4), transactionCurrency: z.string().max(3), countryCode: z.string().max(2), merchantName: z.string().max(25), merchantCity: z.string().max(15), additionalData, postalCode: z.string().min(1).max(99).optional(), unreservedTemplate: unreservedTemplate.optional() }).refine((schema) => { const totalLength = [ schema.merchantAccountInfo.gui.length, schema.merchantAccountInfo.key.length, schema.merchantAccountInfo.merchantAdditionalInfo?.length ?? 0, schema.merchantAccountInfo.fss?.length ?? 0, schema.value ? schema.value.toString().length : 0, schema.merchantCategoryCode.length, schema.transactionCurrency.length, schema.countryCode.length, schema.merchantName.length, schema.merchantCity.length, schema.additionalData.txId.length, schema.postalCode?.length ?? 0 ].reduce((acc, length) => acc + length, 0); return totalLength <= 396; }, "The qrcode length must be less than 396 characters"); var dynamicPixSchema = z.object({ merchantAccountInfo: dynamicMerchantAccountInfoSchema, pointOfInitiationMethod: z.nativeEnum(PointOfInitiationMethod).optional(), value: z.number().positive().min(1).max(13).optional(), merchantCategoryCode: z.string().max(4), transactionCurrency: z.string().max(3), countryCode: z.string().max(2), merchantName: z.string().max(25), merchantCity: z.string().max(15), additionalData, postalCode: z.string().min(1).max(8).optional(), unreservedTemplate: unreservedTemplate.optional() }).refine((schema) => { const totalLength = [ schema.merchantAccountInfo.gui.length, schema.merchantAccountInfo.url.length, schema.merchantAccountInfo.merchantAdditionalInfo?.length ?? 0, schema.merchantAccountInfo.fss?.length ?? 0, schema.pointOfInitiationMethod?.toString().length ?? 0, schema.value ? schema.value.toString().length : 0, schema.merchantCategoryCode.length, schema.transactionCurrency.length, schema.countryCode.length, schema.merchantName.length, schema.merchantCity.length, schema.additionalData.txId.length, schema.postalCode?.length ?? 0 ].reduce((acc, length) => acc + length, 0); return totalLength <= 396; }, "The qrcode length must be less than 396 characters"); // src/static.ts import { crc16ccitt as crc16ccitt2 } from "crc"; var toStaticPix = (payload) => { const buildPayloadParts = () => { const parts = [ ["00", "01"], [ "26", [ appendToBrCode("00", payload.merchantAccountInfo.gui ?? "br.gov.bcb.pix"), appendToBrCode("01", payload.merchantAccountInfo.key), payload.merchantAccountInfo.merchantAdditionalInfo ? appendToBrCode("02", payload.merchantAccountInfo.merchantAdditionalInfo) : "", payload.merchantAccountInfo.fss ? appendToBrCode("03", payload.merchantAccountInfo.fss) : "" ].join("") ], ["52", payload.merchantCategoryCode ?? "0000"], ["53", payload.transactionCurrency ?? "986"], ["54", payload.value?.toFixed(2) ?? ""], ["58", payload.countryCode ?? "BR"], ["59", formatText(payload.merchantName, 25)], ["60", formatText(payload.merchantCity, 15)], ["61", payload.postalCode ?? ""], ["62", appendToBrCode("05", payload.additionalData.txId)], payload.unreservedTemplate ? [ "80", appendToBrCode("00", payload.unreservedTemplate?.gui ?? ""), appendToBrCode("25", payload.unreservedTemplate?.url ?? "") ] : [] ]; return parts.filter(([_, value]) => value).map(([id, value]) => appendToBrCode(id, value)).join(""); }; const payloadString = `${buildPayloadParts()}6304`; const crcResult = crc16ccitt2(payloadString).toString(16).toUpperCase().padStart(4, "0"); return `${payloadString}${crcResult}`; }; var parseStaticPix = (payload) => { try { const entries = deconstructEntries(payload); entries["26"].subEntries = deconstructEntries(entries["26"]?.value ?? ""); entries["62"].subEntries = deconstructEntries(entries["62"]?.value ?? ""); if (entries["80"]) { entries["80"].subEntries = deconstructEntries(entries["80"]?.value ?? ""); } return { merchantAccountInfo: { gui: entries["26"].subEntries?.["00"]?.value, key: entries["26"].subEntries?.["01"]?.value, merchantAdditionalInfo: entries["26"].subEntries?.["02"]?.value, fss: entries["26"].subEntries?.["03"]?.value }, merchantCategoryCode: entries["52"]?.value, transactionCurrency: entries["53"]?.value, countryCode: entries["58"]?.value, merchantName: entries["59"]?.value, merchantCity: entries["60"]?.value, postalCode: entries["61"]?.value, additionalData: { txId: entries["62"].subEntries?.["05"]?.value }, value: entries["54"]?.value ? Number.parseFloat(entries["54"].value) : void 0, unreservedTemplate: entries["80"] ? { gui: entries["80"].subEntries?.["00"]?.value, url: entries["80"].subEntries?.["25"]?.value } : void 0 }; } catch (error) { throw new ParserError(`Invalid static pix payload ${error}`); } }; var isStaticPix = (payload) => { try { const entries = deconstructEntries(payload); entries["26"].subEntries = deconstructEntries(entries["26"].value); return !!entries["26"].subEntries["01"]?.value; } catch (error) { return false; } }; export { ParserError, PixKeyType, PixQrCodeType, PointOfInitiationMethod, dynamicPixSchema, getPixKeyType, isCNPJ, isCPF, isDynamicPix, isStaticPix, keyDetector, parseDynamicPix, parseStaticPix, pixKeyRegexes, staticPixSchema, toDynamicPix, toStaticPix }; //# sourceMappingURL=index.js.map