@pix.js/core
Version:
Core functions for Pix.js
439 lines (431 loc) • 16.3 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
ParserError: () => ParserError,
PixKeyType: () => PixKeyType,
PixQrCodeType: () => PixQrCodeType,
PointOfInitiationMethod: () => PointOfInitiationMethod,
dynamicPixSchema: () => dynamicPixSchema,
getPixKeyType: () => getPixKeyType,
isCNPJ: () => isCNPJ,
isCPF: () => isCPF,
isDynamicPix: () => isDynamicPix,
isStaticPix: () => isStaticPix,
keyDetector: () => keyDetector,
parseDynamicPix: () => parseDynamicPix,
parseStaticPix: () => parseStaticPix,
pixKeyRegexes: () => pixKeyRegexes,
staticPixSchema: () => staticPixSchema,
toDynamicPix: () => toDynamicPix,
toStaticPix: () => toStaticPix
});
module.exports = __toCommonJS(index_exports);
// src/dynamic.ts
var import_crc = require("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 = (0, import_crc.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
var import_zod = require("zod");
var staticMerchantAccountInfoSchema = import_zod.z.object({
gui: import_zod.z.string(),
key: import_zod.z.string(),
merchantAdditionalInfo: import_zod.z.string().optional(),
fss: import_zod.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 = import_zod.z.object({
gui: import_zod.z.string(),
url: import_zod.z.string().regex(/^[^:]+\.[^:]+/),
merchantAdditionalInfo: import_zod.z.string().optional(),
fss: import_zod.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 = import_zod.z.object({
txId: import_zod.z.string().min(1).max(25)
});
var unreservedTemplate = import_zod.z.object({
gui: import_zod.z.string().optional(),
url: import_zod.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 = import_zod.z.object({
merchantAccountInfo: staticMerchantAccountInfoSchema,
value: import_zod.z.number().positive().min(1).max(13).optional(),
merchantCategoryCode: import_zod.z.string().max(4),
transactionCurrency: import_zod.z.string().max(3),
countryCode: import_zod.z.string().max(2),
merchantName: import_zod.z.string().max(25),
merchantCity: import_zod.z.string().max(15),
additionalData,
postalCode: import_zod.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 = import_zod.z.object({
merchantAccountInfo: dynamicMerchantAccountInfoSchema,
pointOfInitiationMethod: import_zod.z.nativeEnum(PointOfInitiationMethod).optional(),
value: import_zod.z.number().positive().min(1).max(13).optional(),
merchantCategoryCode: import_zod.z.string().max(4),
transactionCurrency: import_zod.z.string().max(3),
countryCode: import_zod.z.string().max(2),
merchantName: import_zod.z.string().max(25),
merchantCity: import_zod.z.string().max(15),
additionalData,
postalCode: import_zod.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
var import_crc2 = require("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 = (0, import_crc2.crc16ccitt)(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;
}
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
ParserError,
PixKeyType,
PixQrCodeType,
PointOfInitiationMethod,
dynamicPixSchema,
getPixKeyType,
isCNPJ,
isCPF,
isDynamicPix,
isStaticPix,
keyDetector,
parseDynamicPix,
parseStaticPix,
pixKeyRegexes,
staticPixSchema,
toDynamicPix,
toStaticPix
});
//# sourceMappingURL=index.cjs.map