feat: support ARCA production environment with dynamic endpoint configuration and environment selection
This commit is contained in:
@@ -69,6 +69,7 @@ FILE_SERVER_URL = http://localhost:4000/
|
|||||||
FILE_SERVER_URL_prod = https://files.turnosxpress.com.ar/
|
FILE_SERVER_URL_prod = https://files.turnosxpress.com.ar/
|
||||||
|
|
||||||
SYS_ADMIN_API_PUBLIC_KEY = keys/tx-sysadmin-public.key
|
SYS_ADMIN_API_PUBLIC_KEY = keys/tx-sysadmin-public.key
|
||||||
|
ARCA_ENVIRONMENT=homologation
|
||||||
ARCA_WSAA_URL=https://wsaahomo.afip.gov.ar/ws/services/LoginCms
|
ARCA_WSAA_URL=https://wsaahomo.afip.gov.ar/ws/services/LoginCms
|
||||||
ARCA_WSFE_URL=https://wswhomo.afip.gov.ar/wsfev1/service.asmx
|
ARCA_WSFE_URL=https://wswhomo.afip.gov.ar/wsfev1/service.asmx
|
||||||
|
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ SYSTEM_KEY = ad7c956a-76bf-45hdr60-8a50-2ca7b2180997
|
|||||||
FILE_SERVER_URL = https://files.turnosxpress.com.ar/
|
FILE_SERVER_URL = https://files.turnosxpress.com.ar/
|
||||||
|
|
||||||
SYS_ADMIN_API_PUBLIC_KEY = keys/tx-sysadmin-public.key
|
SYS_ADMIN_API_PUBLIC_KEY = keys/tx-sysadmin-public.key
|
||||||
|
ARCA_ENVIRONMENT=production
|
||||||
ARCA_WSAA_URL=https://wsaa.afip.gov.ar/ws/services/LoginCms
|
ARCA_WSAA_URL=https://wsaa.afip.gov.ar/ws/services/LoginCms
|
||||||
ARCA_WSFE_URL=https://servicios1.afip.gov.ar/wsfev1/service.asmx
|
ARCA_WSFE_URL=https://servicios1.afip.gov.ar/wsfev1/service.asmx
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { IArcaCredentialDocument } from "./ArcaCredentials.Adapter.Mongoose";
|
import { IArcaCredentialDocument } from "./ArcaCredentials.Adapter.Mongoose";
|
||||||
|
import { ARCA_WSAA_ENVIRONMENT, ARCA_WSAA_SERVICE } from "../ArcaWsaaTokens/ArcaWsaaTokens.Interface";
|
||||||
|
|
||||||
export enum ARCA_CREDENTIAL_STATUS {
|
export enum ARCA_CREDENTIAL_STATUS {
|
||||||
CSR_GENERATED = "CSR_GENERATED",
|
CSR_GENERATED = "CSR_GENERATED",
|
||||||
@@ -26,8 +27,9 @@ export type TestArcaWsaaLoginParams = {
|
|||||||
|
|
||||||
export type ArcaWsaaLoginTestResult = {
|
export type ArcaWsaaLoginTestResult = {
|
||||||
status: "cached" | "created";
|
status: "cached" | "created";
|
||||||
environment: "homologation";
|
environment: ARCA_WSAA_ENVIRONMENT;
|
||||||
service: "wsfe";
|
service: ARCA_WSAA_SERVICE;
|
||||||
|
endpoints: ArcaEndpointMetadata;
|
||||||
expirationTime: Date;
|
expirationTime: Date;
|
||||||
cached: boolean;
|
cached: boolean;
|
||||||
};
|
};
|
||||||
@@ -38,8 +40,9 @@ export type ArcaWsfeLastVoucherParams = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type ArcaWsfeLastVoucherResult = {
|
export type ArcaWsfeLastVoucherResult = {
|
||||||
environment: "homologation";
|
environment: ARCA_WSAA_ENVIRONMENT;
|
||||||
service: "wsfe";
|
service: ARCA_WSAA_SERVICE;
|
||||||
|
endpoints: ArcaEndpointMetadata;
|
||||||
pointOfSale: number;
|
pointOfSale: number;
|
||||||
voucherType: 11;
|
voucherType: 11;
|
||||||
lastVoucherNumber: number;
|
lastVoucherNumber: number;
|
||||||
@@ -58,8 +61,9 @@ export type ArcaWsfeIssueFacturaCParams = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type ArcaWsfeIssueFacturaCResult = {
|
export type ArcaWsfeIssueFacturaCResult = {
|
||||||
environment: "homologation";
|
environment: ARCA_WSAA_ENVIRONMENT;
|
||||||
service: "wsfe";
|
service: ARCA_WSAA_SERVICE;
|
||||||
|
endpoints: ArcaEndpointMetadata;
|
||||||
pointOfSale: number;
|
pointOfSale: number;
|
||||||
voucherType: 11;
|
voucherType: 11;
|
||||||
voucherNumber: number;
|
voucherNumber: number;
|
||||||
@@ -78,9 +82,15 @@ export type ArcaWsfeDiagnosticsParams = {
|
|||||||
sessionUser: string;
|
sessionUser: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ArcaEndpointMetadata = {
|
||||||
|
wsaaHost?: string;
|
||||||
|
wsfeHost?: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type ArcaWsfeDiagnosticsResult = {
|
export type ArcaWsfeDiagnosticsResult = {
|
||||||
environment: "homologation";
|
environment: ARCA_WSAA_ENVIRONMENT;
|
||||||
service: "wsfe";
|
service: ARCA_WSAA_SERVICE;
|
||||||
|
endpoints: ArcaEndpointMetadata;
|
||||||
dummy: {
|
dummy: {
|
||||||
ok: boolean;
|
ok: boolean;
|
||||||
appServer?: string;
|
appServer?: string;
|
||||||
|
|||||||
@@ -200,6 +200,35 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
|||||||
return parsedValue;
|
return parsedValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getArcaEnvironment(): ARCA_WSAA_ENVIRONMENT {
|
||||||
|
const environment = (process.env.ARCA_ENVIRONMENT || ARCA_WSAA_ENVIRONMENT.HOMOLOGATION).trim().toLowerCase();
|
||||||
|
|
||||||
|
if (!Object.values(ARCA_WSAA_ENVIRONMENT).includes(environment as ARCA_WSAA_ENVIRONMENT)) {
|
||||||
|
throw new Error("ARCA_ENVIRONMENT debe ser 'homologation' o 'production'");
|
||||||
|
}
|
||||||
|
|
||||||
|
return environment as ARCA_WSAA_ENVIRONMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getEndpointHost(value?: string): string | undefined {
|
||||||
|
if (!value) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return new URL(value).host;
|
||||||
|
} catch {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getArcaEndpointMetadata() {
|
||||||
|
return {
|
||||||
|
wsaaHost: this.getEndpointHost(process.env.ARCA_WSAA_URL),
|
||||||
|
wsfeHost: this.getEndpointHost(process.env.ARCA_WSFE_URL),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private signTraCms(traXml: string, certificatePem: string, privateKeyPem: string): string {
|
private signTraCms(traXml: string, certificatePem: string, privateKeyPem: string): string {
|
||||||
const p7 = forge.pkcs7.createSignedData();
|
const p7 = forge.pkcs7.createSignedData();
|
||||||
p7.content = forge.util.createBuffer(traXml, "utf8");
|
p7.content = forge.util.createBuffer(traXml, "utf8");
|
||||||
@@ -270,7 +299,7 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
|||||||
cached: boolean;
|
cached: boolean;
|
||||||
}> {
|
}> {
|
||||||
const service = ARCA_WSAA_SERVICE.WSFE;
|
const service = ARCA_WSAA_SERVICE.WSFE;
|
||||||
const environment = ARCA_WSAA_ENVIRONMENT.HOMOLOGATION;
|
const environment = this.getArcaEnvironment();
|
||||||
const cachedToken = await ArcaWsaaTokens.getValidToken({ companyId, service, environment });
|
const cachedToken = await ArcaWsaaTokens.getValidToken({ companyId, service, environment });
|
||||||
|
|
||||||
if (cachedToken) {
|
if (cachedToken) {
|
||||||
@@ -582,7 +611,8 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
|||||||
private parseWsfeIssueFacturaCReturn(
|
private parseWsfeIssueFacturaCReturn(
|
||||||
xml: string,
|
xml: string,
|
||||||
pointOfSale: number,
|
pointOfSale: number,
|
||||||
voucherNumber: number
|
voucherNumber: number,
|
||||||
|
environment: ARCA_WSAA_ENVIRONMENT
|
||||||
): ArcaWsfeIssueFacturaCResult {
|
): ArcaWsfeIssueFacturaCResult {
|
||||||
const faultString = this.extractXmlValue(xml, "faultstring");
|
const faultString = this.extractXmlValue(xml, "faultstring");
|
||||||
|
|
||||||
@@ -600,8 +630,9 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
environment: ARCA_WSAA_ENVIRONMENT.HOMOLOGATION,
|
environment,
|
||||||
service: ARCA_WSAA_SERVICE.WSFE,
|
service: ARCA_WSAA_SERVICE.WSFE,
|
||||||
|
endpoints: this.getArcaEndpointMetadata(),
|
||||||
pointOfSale,
|
pointOfSale,
|
||||||
voucherType: FACTURA_C_VOUCHER_TYPE,
|
voucherType: FACTURA_C_VOUCHER_TYPE,
|
||||||
voucherNumber,
|
voucherNumber,
|
||||||
@@ -807,13 +838,14 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
|||||||
await this.validateCompanyAdmin(data.companyId, data.sessionUser);
|
await this.validateCompanyAdmin(data.companyId, data.sessionUser);
|
||||||
|
|
||||||
const service = ARCA_WSAA_SERVICE.WSFE;
|
const service = ARCA_WSAA_SERVICE.WSFE;
|
||||||
const environment = ARCA_WSAA_ENVIRONMENT.HOMOLOGATION;
|
const environment = this.getArcaEnvironment();
|
||||||
const loginTicket = await this.getWsaaLoginTicket(data.companyId);
|
const loginTicket = await this.getWsaaLoginTicket(data.companyId);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
status: loginTicket.cached ? "cached" : "created",
|
status: loginTicket.cached ? "cached" : "created",
|
||||||
environment,
|
environment,
|
||||||
service,
|
service,
|
||||||
|
endpoints: this.getArcaEndpointMetadata(),
|
||||||
expirationTime: loginTicket.expirationTime,
|
expirationTime: loginTicket.expirationTime,
|
||||||
cached: loginTicket.cached,
|
cached: loginTicket.cached,
|
||||||
};
|
};
|
||||||
@@ -823,6 +855,7 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
|||||||
await this.validateCompanyAdmin(data.companyId, data.sessionUser);
|
await this.validateCompanyAdmin(data.companyId, data.sessionUser);
|
||||||
const fiscalProfile = await this.requireActiveFiscalProfile(data.companyId, data.sessionUser);
|
const fiscalProfile = await this.requireActiveFiscalProfile(data.companyId, data.sessionUser);
|
||||||
const loginTicket = await this.getWsaaLoginTicket(data.companyId);
|
const loginTicket = await this.getWsaaLoginTicket(data.companyId);
|
||||||
|
const environment = this.getArcaEnvironment();
|
||||||
const voucherType = FACTURA_C_VOUCHER_TYPE;
|
const voucherType = FACTURA_C_VOUCHER_TYPE;
|
||||||
let responseXml: string;
|
let responseXml: string;
|
||||||
|
|
||||||
@@ -842,8 +875,9 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
environment: ARCA_WSAA_ENVIRONMENT.HOMOLOGATION,
|
environment,
|
||||||
service: ARCA_WSAA_SERVICE.WSFE,
|
service: ARCA_WSAA_SERVICE.WSFE,
|
||||||
|
endpoints: this.getArcaEndpointMetadata(),
|
||||||
pointOfSale: fiscalProfile.pointOfSale,
|
pointOfSale: fiscalProfile.pointOfSale,
|
||||||
voucherType,
|
voucherType,
|
||||||
lastVoucherNumber: this.parseWsfeLastVoucherReturn(responseXml),
|
lastVoucherNumber: this.parseWsfeLastVoucherReturn(responseXml),
|
||||||
@@ -859,6 +893,7 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
|||||||
|
|
||||||
const fiscalProfile = await this.requireActiveFiscalProfile(data.companyId, data.sessionUser);
|
const fiscalProfile = await this.requireActiveFiscalProfile(data.companyId, data.sessionUser);
|
||||||
const loginTicket = await this.getWsaaLoginTicket(data.companyId);
|
const loginTicket = await this.getWsaaLoginTicket(data.companyId);
|
||||||
|
const environment = this.getArcaEnvironment();
|
||||||
const voucherType = 11;
|
const voucherType = 11;
|
||||||
const receiverDocumentType = this.parseXmlInteger(
|
const receiverDocumentType = this.parseXmlInteger(
|
||||||
data.receiverDocumentType ?? FINAL_CONSUMER_DOCUMENT_TYPE,
|
data.receiverDocumentType ?? FINAL_CONSUMER_DOCUMENT_TYPE,
|
||||||
@@ -908,7 +943,8 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
|||||||
"FECAESolicitar"
|
"FECAESolicitar"
|
||||||
),
|
),
|
||||||
fiscalProfile.pointOfSale,
|
fiscalProfile.pointOfSale,
|
||||||
voucherNumber
|
voucherNumber,
|
||||||
|
environment
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new ArcaWsfeIssueFacturaCError(
|
throw new ArcaWsfeIssueFacturaCError(
|
||||||
@@ -927,11 +963,13 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
|||||||
await this.validateCompanyAdmin(data.companyId, data.sessionUser);
|
await this.validateCompanyAdmin(data.companyId, data.sessionUser);
|
||||||
const fiscalProfile = await this.requireActiveFiscalProfile(data.companyId, data.sessionUser);
|
const fiscalProfile = await this.requireActiveFiscalProfile(data.companyId, data.sessionUser);
|
||||||
const loginTicket = await this.getWsaaLoginTicket(data.companyId);
|
const loginTicket = await this.getWsaaLoginTicket(data.companyId);
|
||||||
|
const environment = this.getArcaEnvironment();
|
||||||
const voucherType = 11;
|
const voucherType = 11;
|
||||||
|
|
||||||
const diagnostics: ArcaWsfeDiagnosticsResult = {
|
const diagnostics: ArcaWsfeDiagnosticsResult = {
|
||||||
environment: ARCA_WSAA_ENVIRONMENT.HOMOLOGATION,
|
environment,
|
||||||
service: ARCA_WSAA_SERVICE.WSFE,
|
service: ARCA_WSAA_SERVICE.WSFE,
|
||||||
|
endpoints: this.getArcaEndpointMetadata(),
|
||||||
dummy: { ok: false },
|
dummy: { ok: false },
|
||||||
voucherTypes: { ok: false },
|
voucherTypes: { ok: false },
|
||||||
pointsOfSale: {
|
pointsOfSale: {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export enum ARCA_WSAA_SERVICE {
|
|||||||
|
|
||||||
export enum ARCA_WSAA_ENVIRONMENT {
|
export enum ARCA_WSAA_ENVIRONMENT {
|
||||||
HOMOLOGATION = "homologation",
|
HOMOLOGATION = "homologation",
|
||||||
|
PRODUCTION = "production",
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ArcaWsaaTokenLookupParams = {
|
export type ArcaWsaaTokenLookupParams = {
|
||||||
|
|||||||
+5
-1
@@ -19,8 +19,12 @@ export type ArcaWsfeLastVoucherParams = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type ArcaWsfeLastVoucherResult = {
|
export type ArcaWsfeLastVoucherResult = {
|
||||||
environment: "homologation";
|
environment: "homologation" | "production";
|
||||||
service: "wsfe";
|
service: "wsfe";
|
||||||
|
endpoints?: {
|
||||||
|
wsaaHost?: string;
|
||||||
|
wsfeHost?: string;
|
||||||
|
};
|
||||||
pointOfSale: number;
|
pointOfSale: number;
|
||||||
voucherType: 11;
|
voucherType: 11;
|
||||||
lastVoucherNumber: number;
|
lastVoucherNumber: number;
|
||||||
|
|||||||
+11
-2
@@ -4,15 +4,22 @@ import QuestionCard from "../../QuestionCard/QuestionCard";
|
|||||||
|
|
||||||
export type ArcaWsaaTestResult = {
|
export type ArcaWsaaTestResult = {
|
||||||
status?: string;
|
status?: string;
|
||||||
environment?: string;
|
environment?: "homologation" | "production";
|
||||||
service?: string;
|
service?: string;
|
||||||
|
endpoints?: ArcaEndpointMetadata;
|
||||||
expirationTime?: string;
|
expirationTime?: string;
|
||||||
cached?: boolean;
|
cached?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type ArcaEndpointMetadata = {
|
||||||
|
wsaaHost?: string;
|
||||||
|
wsfeHost?: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type ArcaWsfeDiagnosticsResult = {
|
export type ArcaWsfeDiagnosticsResult = {
|
||||||
environment?: string;
|
environment?: "homologation" | "production";
|
||||||
service?: string;
|
service?: string;
|
||||||
|
endpoints?: ArcaEndpointMetadata;
|
||||||
dummy?: {
|
dummy?: {
|
||||||
ok: boolean;
|
ok: boolean;
|
||||||
appServer?: string;
|
appServer?: string;
|
||||||
@@ -84,6 +91,7 @@ export default function ArcaTestConnectionFlow({ currentStepIndex, loading, diag
|
|||||||
<InfoPanel>WSAA está OK. El diagnóstico WSFE no emite comprobantes: solo consulta salud, puntos de venta, tipos de comprobante y último número autorizado.</InfoPanel>
|
<InfoPanel>WSAA está OK. El diagnóstico WSFE no emite comprobantes: solo consulta salud, puntos de venta, tipos de comprobante y último número autorizado.</InfoPanel>
|
||||||
<InfoRow label="Ambiente" value={result?.environment || "homologation"} />
|
<InfoRow label="Ambiente" value={result?.environment || "homologation"} />
|
||||||
<InfoRow label="Servicio" value={result?.service || "wsfe"} />
|
<InfoRow label="Servicio" value={result?.service || "wsfe"} />
|
||||||
|
{result?.endpoints?.wsaaHost ? <InfoRow label="WSAA host" value={result.endpoints.wsaaHost} /> : null}
|
||||||
<InfoRow label="Vencimiento" value={formatExpiration(result?.expirationTime)} />
|
<InfoRow label="Vencimiento" value={formatExpiration(result?.expirationTime)} />
|
||||||
<InfoRow label="Token" value={result?.cached ? "Reutilizado desde caché" : "Creado en esta prueba"} />
|
<InfoRow label="Token" value={result?.cached ? "Reutilizado desde caché" : "Creado en esta prueba"} />
|
||||||
{diagnosticsLoading && <InfoPanel>Consultando FEDummy, puntos de venta, tipos de comprobante y último comprobante autorizado...</InfoPanel>}
|
{diagnosticsLoading && <InfoPanel>Consultando FEDummy, puntos de venta, tipos de comprobante y último comprobante autorizado...</InfoPanel>}
|
||||||
@@ -125,6 +133,7 @@ function WsfeDiagnosticsPanel({ diagnostics }: { diagnostics: ArcaWsfeDiagnostic
|
|||||||
return (
|
return (
|
||||||
<div style={{ display: "flex", flexDirection: "column", gap: "10px" }}>
|
<div style={{ display: "flex", flexDirection: "column", gap: "10px" }}>
|
||||||
<InfoRow label="WSFE FEDummy" value={diagnostics.dummy?.ok ? `OK (${[diagnostics.dummy.appServer, diagnostics.dummy.dbServer, diagnostics.dummy.authServer].filter(Boolean).join(" / ") || "sin detalle"})` : `Error: ${diagnostics.dummy?.error || "sin detalle"}`} />
|
<InfoRow label="WSFE FEDummy" value={diagnostics.dummy?.ok ? `OK (${[diagnostics.dummy.appServer, diagnostics.dummy.dbServer, diagnostics.dummy.authServer].filter(Boolean).join(" / ") || "sin detalle"})` : `Error: ${diagnostics.dummy?.error || "sin detalle"}`} />
|
||||||
|
{diagnostics.endpoints?.wsfeHost ? <InfoRow label="WSFE host" value={diagnostics.endpoints.wsfeHost} /> : null}
|
||||||
<InfoRow label="Punto de venta configurado" value={diagnostics.pointsOfSale?.includesConfiguredPointOfSale ? `OK (${diagnostics.pointsOfSale.configuredPointOfSale})` : `No aparece (${diagnostics.pointsOfSale?.configuredPointOfSale || "sin configurar"})`} />
|
<InfoRow label="Punto de venta configurado" value={diagnostics.pointsOfSale?.includesConfiguredPointOfSale ? `OK (${diagnostics.pointsOfSale.configuredPointOfSale})` : `No aparece (${diagnostics.pointsOfSale?.configuredPointOfSale || "sin configurar"})`} />
|
||||||
{diagnostics.pointsOfSale?.points?.length ? <InfoRow label="Puntos de venta ARCA" value={diagnostics.pointsOfSale.points.join(", ")} /> : null}
|
{diagnostics.pointsOfSale?.points?.length ? <InfoRow label="Puntos de venta ARCA" value={diagnostics.pointsOfSale.points.join(", ")} /> : null}
|
||||||
{diagnostics.pointsOfSale?.error ? <StatusMessage error={diagnostics.pointsOfSale.error} /> : null}
|
{diagnostics.pointsOfSale?.error ? <StatusMessage error={diagnostics.pointsOfSale.error} /> : null}
|
||||||
|
|||||||
Reference in New Issue
Block a user