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/
|
||||
|
||||
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_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/
|
||||
|
||||
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_WSFE_URL=https://servicios1.afip.gov.ar/wsfev1/service.asmx
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { IArcaCredentialDocument } from "./ArcaCredentials.Adapter.Mongoose";
|
||||
import { ARCA_WSAA_ENVIRONMENT, ARCA_WSAA_SERVICE } from "../ArcaWsaaTokens/ArcaWsaaTokens.Interface";
|
||||
|
||||
export enum ARCA_CREDENTIAL_STATUS {
|
||||
CSR_GENERATED = "CSR_GENERATED",
|
||||
@@ -26,8 +27,9 @@ export type TestArcaWsaaLoginParams = {
|
||||
|
||||
export type ArcaWsaaLoginTestResult = {
|
||||
status: "cached" | "created";
|
||||
environment: "homologation";
|
||||
service: "wsfe";
|
||||
environment: ARCA_WSAA_ENVIRONMENT;
|
||||
service: ARCA_WSAA_SERVICE;
|
||||
endpoints: ArcaEndpointMetadata;
|
||||
expirationTime: Date;
|
||||
cached: boolean;
|
||||
};
|
||||
@@ -38,8 +40,9 @@ export type ArcaWsfeLastVoucherParams = {
|
||||
};
|
||||
|
||||
export type ArcaWsfeLastVoucherResult = {
|
||||
environment: "homologation";
|
||||
service: "wsfe";
|
||||
environment: ARCA_WSAA_ENVIRONMENT;
|
||||
service: ARCA_WSAA_SERVICE;
|
||||
endpoints: ArcaEndpointMetadata;
|
||||
pointOfSale: number;
|
||||
voucherType: 11;
|
||||
lastVoucherNumber: number;
|
||||
@@ -58,8 +61,9 @@ export type ArcaWsfeIssueFacturaCParams = {
|
||||
};
|
||||
|
||||
export type ArcaWsfeIssueFacturaCResult = {
|
||||
environment: "homologation";
|
||||
service: "wsfe";
|
||||
environment: ARCA_WSAA_ENVIRONMENT;
|
||||
service: ARCA_WSAA_SERVICE;
|
||||
endpoints: ArcaEndpointMetadata;
|
||||
pointOfSale: number;
|
||||
voucherType: 11;
|
||||
voucherNumber: number;
|
||||
@@ -78,9 +82,15 @@ export type ArcaWsfeDiagnosticsParams = {
|
||||
sessionUser: string;
|
||||
};
|
||||
|
||||
export type ArcaEndpointMetadata = {
|
||||
wsaaHost?: string;
|
||||
wsfeHost?: string;
|
||||
};
|
||||
|
||||
export type ArcaWsfeDiagnosticsResult = {
|
||||
environment: "homologation";
|
||||
service: "wsfe";
|
||||
environment: ARCA_WSAA_ENVIRONMENT;
|
||||
service: ARCA_WSAA_SERVICE;
|
||||
endpoints: ArcaEndpointMetadata;
|
||||
dummy: {
|
||||
ok: boolean;
|
||||
appServer?: string;
|
||||
|
||||
@@ -200,6 +200,35 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
||||
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 {
|
||||
const p7 = forge.pkcs7.createSignedData();
|
||||
p7.content = forge.util.createBuffer(traXml, "utf8");
|
||||
@@ -270,7 +299,7 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
||||
cached: boolean;
|
||||
}> {
|
||||
const service = ARCA_WSAA_SERVICE.WSFE;
|
||||
const environment = ARCA_WSAA_ENVIRONMENT.HOMOLOGATION;
|
||||
const environment = this.getArcaEnvironment();
|
||||
const cachedToken = await ArcaWsaaTokens.getValidToken({ companyId, service, environment });
|
||||
|
||||
if (cachedToken) {
|
||||
@@ -582,7 +611,8 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
||||
private parseWsfeIssueFacturaCReturn(
|
||||
xml: string,
|
||||
pointOfSale: number,
|
||||
voucherNumber: number
|
||||
voucherNumber: number,
|
||||
environment: ARCA_WSAA_ENVIRONMENT
|
||||
): ArcaWsfeIssueFacturaCResult {
|
||||
const faultString = this.extractXmlValue(xml, "faultstring");
|
||||
|
||||
@@ -600,8 +630,9 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
||||
}
|
||||
|
||||
return {
|
||||
environment: ARCA_WSAA_ENVIRONMENT.HOMOLOGATION,
|
||||
environment,
|
||||
service: ARCA_WSAA_SERVICE.WSFE,
|
||||
endpoints: this.getArcaEndpointMetadata(),
|
||||
pointOfSale,
|
||||
voucherType: FACTURA_C_VOUCHER_TYPE,
|
||||
voucherNumber,
|
||||
@@ -807,13 +838,14 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
||||
await this.validateCompanyAdmin(data.companyId, data.sessionUser);
|
||||
|
||||
const service = ARCA_WSAA_SERVICE.WSFE;
|
||||
const environment = ARCA_WSAA_ENVIRONMENT.HOMOLOGATION;
|
||||
const environment = this.getArcaEnvironment();
|
||||
const loginTicket = await this.getWsaaLoginTicket(data.companyId);
|
||||
|
||||
return {
|
||||
status: loginTicket.cached ? "cached" : "created",
|
||||
environment,
|
||||
service,
|
||||
endpoints: this.getArcaEndpointMetadata(),
|
||||
expirationTime: loginTicket.expirationTime,
|
||||
cached: loginTicket.cached,
|
||||
};
|
||||
@@ -823,6 +855,7 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
||||
await this.validateCompanyAdmin(data.companyId, data.sessionUser);
|
||||
const fiscalProfile = await this.requireActiveFiscalProfile(data.companyId, data.sessionUser);
|
||||
const loginTicket = await this.getWsaaLoginTicket(data.companyId);
|
||||
const environment = this.getArcaEnvironment();
|
||||
const voucherType = FACTURA_C_VOUCHER_TYPE;
|
||||
let responseXml: string;
|
||||
|
||||
@@ -842,8 +875,9 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
||||
}
|
||||
|
||||
return {
|
||||
environment: ARCA_WSAA_ENVIRONMENT.HOMOLOGATION,
|
||||
environment,
|
||||
service: ARCA_WSAA_SERVICE.WSFE,
|
||||
endpoints: this.getArcaEndpointMetadata(),
|
||||
pointOfSale: fiscalProfile.pointOfSale,
|
||||
voucherType,
|
||||
lastVoucherNumber: this.parseWsfeLastVoucherReturn(responseXml),
|
||||
@@ -859,6 +893,7 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
||||
|
||||
const fiscalProfile = await this.requireActiveFiscalProfile(data.companyId, data.sessionUser);
|
||||
const loginTicket = await this.getWsaaLoginTicket(data.companyId);
|
||||
const environment = this.getArcaEnvironment();
|
||||
const voucherType = 11;
|
||||
const receiverDocumentType = this.parseXmlInteger(
|
||||
data.receiverDocumentType ?? FINAL_CONSUMER_DOCUMENT_TYPE,
|
||||
@@ -908,7 +943,8 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
||||
"FECAESolicitar"
|
||||
),
|
||||
fiscalProfile.pointOfSale,
|
||||
voucherNumber
|
||||
voucherNumber,
|
||||
environment
|
||||
);
|
||||
} catch (error) {
|
||||
throw new ArcaWsfeIssueFacturaCError(
|
||||
@@ -927,11 +963,13 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
||||
await this.validateCompanyAdmin(data.companyId, data.sessionUser);
|
||||
const fiscalProfile = await this.requireActiveFiscalProfile(data.companyId, data.sessionUser);
|
||||
const loginTicket = await this.getWsaaLoginTicket(data.companyId);
|
||||
const environment = this.getArcaEnvironment();
|
||||
const voucherType = 11;
|
||||
|
||||
const diagnostics: ArcaWsfeDiagnosticsResult = {
|
||||
environment: ARCA_WSAA_ENVIRONMENT.HOMOLOGATION,
|
||||
environment,
|
||||
service: ARCA_WSAA_SERVICE.WSFE,
|
||||
endpoints: this.getArcaEndpointMetadata(),
|
||||
dummy: { ok: false },
|
||||
voucherTypes: { ok: false },
|
||||
pointsOfSale: {
|
||||
|
||||
@@ -6,6 +6,7 @@ export enum ARCA_WSAA_SERVICE {
|
||||
|
||||
export enum ARCA_WSAA_ENVIRONMENT {
|
||||
HOMOLOGATION = "homologation",
|
||||
PRODUCTION = "production",
|
||||
}
|
||||
|
||||
export type ArcaWsaaTokenLookupParams = {
|
||||
|
||||
+5
-1
@@ -19,8 +19,12 @@ export type ArcaWsfeLastVoucherParams = {
|
||||
};
|
||||
|
||||
export type ArcaWsfeLastVoucherResult = {
|
||||
environment: "homologation";
|
||||
environment: "homologation" | "production";
|
||||
service: "wsfe";
|
||||
endpoints?: {
|
||||
wsaaHost?: string;
|
||||
wsfeHost?: string;
|
||||
};
|
||||
pointOfSale: number;
|
||||
voucherType: 11;
|
||||
lastVoucherNumber: number;
|
||||
|
||||
+11
-2
@@ -4,15 +4,22 @@ import QuestionCard from "../../QuestionCard/QuestionCard";
|
||||
|
||||
export type ArcaWsaaTestResult = {
|
||||
status?: string;
|
||||
environment?: string;
|
||||
environment?: "homologation" | "production";
|
||||
service?: string;
|
||||
endpoints?: ArcaEndpointMetadata;
|
||||
expirationTime?: string;
|
||||
cached?: boolean;
|
||||
};
|
||||
|
||||
type ArcaEndpointMetadata = {
|
||||
wsaaHost?: string;
|
||||
wsfeHost?: string;
|
||||
};
|
||||
|
||||
export type ArcaWsfeDiagnosticsResult = {
|
||||
environment?: string;
|
||||
environment?: "homologation" | "production";
|
||||
service?: string;
|
||||
endpoints?: ArcaEndpointMetadata;
|
||||
dummy?: {
|
||||
ok: boolean;
|
||||
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>
|
||||
<InfoRow label="Ambiente" value={result?.environment || "homologation"} />
|
||||
<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="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>}
|
||||
@@ -125,6 +133,7 @@ function WsfeDiagnosticsPanel({ diagnostics }: { diagnostics: ArcaWsfeDiagnostic
|
||||
return (
|
||||
<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"}`} />
|
||||
{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"})`} />
|
||||
{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}
|
||||
|
||||
Reference in New Issue
Block a user