facturas 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +797 -37
- package/dist/{chunk-PPYGVNFA.mjs → chunk-4QVFXT35.mjs} +2 -2
- package/dist/{chunk-NUV5RZPZ.mjs → chunk-7LEKQ4P7.mjs} +2 -2
- package/dist/{chunk-TOVSOJ3G.mjs → chunk-RCM4NAAG.mjs} +25 -6
- package/dist/chunk-RCM4NAAG.mjs.map +1 -0
- package/dist/{chunk-ZX4OOCML.mjs → chunk-XNBD5NVL.mjs} +32 -31
- package/dist/chunk-XNBD5NVL.mjs.map +1 -0
- package/dist/{chunk-HUT3PFKF.mjs → chunk-ZOSNASXB.mjs} +1 -1
- package/dist/chunk-ZOSNASXB.mjs.map +1 -0
- package/dist/errors.d.ts +2 -2
- package/dist/errors.mjs +1 -1
- package/dist/{errors-B0uouRzR.d.ts → fiscal-evidence-DBzakqBE.d.ts} +77 -77
- package/dist/{index-CVpygFb0.d.ts → index-CVGQyyYM.d.ts} +1 -1
- package/dist/index.d.ts +57 -22
- package/dist/index.mjs +672 -61
- package/dist/index.mjs.map +1 -1
- package/dist/padron.d.ts +2 -2
- package/dist/padron.mjs +2 -2
- package/dist/{types-BXq0da71.d.ts → types-BiznC1-X.d.ts} +12 -1
- package/dist/types.d.ts +1 -1
- package/dist/{wsfe-CG_erq7V.d.ts → wsfe-DF0jAE2k.d.ts} +18 -7
- package/dist/wsfe.d.ts +4 -4
- package/dist/wsfe.mjs +3 -3
- package/dist/wsmtxca.d.ts +14 -5
- package/dist/wsmtxca.mjs +3 -3
- package/package.json +1 -1
- package/dist/chunk-HUT3PFKF.mjs.map +0 -1
- package/dist/chunk-TOVSOJ3G.mjs.map +0 -1
- package/dist/chunk-ZX4OOCML.mjs.map +0 -1
- /package/dist/{chunk-PPYGVNFA.mjs.map → chunk-4QVFXT35.mjs.map} +0 -0
- /package/dist/{chunk-NUV5RZPZ.mjs.map → chunk-7LEKQ4P7.mjs.map} +0 -0
package/README.md
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
# facturas
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/facturas)
|
|
4
|
+
[](https://github.com/LaPyme/facturas/actions/workflows/ci.yml)
|
|
5
|
+
[](https://github.com/LaPyme/facturas/blob/main/LICENSE)
|
|
6
|
+
|
|
7
|
+
Node.js SDK for ARCA / AFIP invoicing, credit notes and Padrón, with direct WSFE and WSMTXCA integration. [Inicio rápido en español](./docs/inicio-rapido.md).
|
|
8
|
+
|
|
9
|
+
- **ESM-only**, Node.js **>= 20**
|
|
10
|
+
- **Direct ARCA integration** with no proxy or hosted dependency
|
|
11
|
+
- **WSAA login handling** with in-memory ticket cache, optional durable session stores, in-flight deduplication, and recovery for `coe.alreadyAuthenticated`
|
|
12
|
+
- **Strict TypeScript** public API with JS-style field names mapped to SOAP internally
|
|
13
|
+
- **Common ARCA reference data** exported as constants so examples and app code do not need magic numbers
|
|
14
|
+
- **Copy-pasteable examples** designed to be readable by humans and coding agents
|
|
4
15
|
|
|
5
16
|
## Install
|
|
6
17
|
|
|
@@ -12,14 +23,770 @@ pnpm add facturas
|
|
|
12
23
|
npm install facturas
|
|
13
24
|
```
|
|
14
25
|
|
|
15
|
-
##
|
|
26
|
+
## Issue an invoice
|
|
27
|
+
|
|
28
|
+
Set the [environment variables](#configuration), provision the store table below,
|
|
29
|
+
and use the sale's stable ID as the key. The example assumes `venta` is your sale.
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { createArcaClient, createPostgresStore } from "facturas";
|
|
33
|
+
import { sql } from "@vercel/postgres";
|
|
34
|
+
|
|
35
|
+
const arca = createArcaClient({
|
|
36
|
+
store: createPostgresStore({ query: (text, params) => sql.query(text, params) }),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const factura = await arca.issue(
|
|
40
|
+
{
|
|
41
|
+
issuer: "monotributo",
|
|
42
|
+
salesPoint: 3,
|
|
43
|
+
to: { condition: "consumidor_final" },
|
|
44
|
+
items: [{ amount: 150_000 }], // ARS 1.500,00 en centavos
|
|
45
|
+
},
|
|
46
|
+
{ idempotencyKey: venta.id },
|
|
47
|
+
);
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Without `idempotencyKey`, a retry after a crash
|
|
51
|
+
can issue the invoice twice. Configure a `store` and pass the key so retries
|
|
52
|
+
are safe.
|
|
53
|
+
|
|
54
|
+
A key is 1–255 characters. Use the sale or order ID, never a new UUID for each
|
|
55
|
+
attempt. Do not put CUIT, DNI or other personal data in keys. Reuse the same
|
|
56
|
+
key and input on retries; changed input throws `ARCA_INPUT_IDEMPOTENCY_MISMATCH`.
|
|
57
|
+
Keys are scoped to the client's CUIT and environment. `representedTaxId` is
|
|
58
|
+
also checked as part of the input identity. A key without a store throws before
|
|
59
|
+
provider I/O. Different keys identify different business operations; a key does
|
|
60
|
+
not reserve the entire point-of-sale sequence against other writers.
|
|
61
|
+
|
|
62
|
+
## Production smoke test and cancellation
|
|
63
|
+
|
|
64
|
+
After enabling ARCA access and selecting a production point of sale, this emits
|
|
65
|
+
an ARS 1 invoice and a full associated credit note. **Both documents are real
|
|
66
|
+
and remain in ARCA's records.** The note is a separate operation; a failure
|
|
67
|
+
leaves the invoice outstanding. Match `issuer` to your actual tax condition.
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
const arca = createArcaClient({ environment: "production" });
|
|
71
|
+
const factura = await arca.issue({
|
|
72
|
+
issuer: "monotributo",
|
|
73
|
+
salesPoint: 3,
|
|
74
|
+
to: { condition: "consumidor_final" },
|
|
75
|
+
items: [{ amount: 100 }], // ARS 1.00
|
|
76
|
+
});
|
|
77
|
+
if (factura.kind === "authorized") {
|
|
78
|
+
const nota = await arca.cancel(factura.voucher);
|
|
79
|
+
console.log(nota); // Handle every outcome, including a failed credit note.
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`cancel({ salesPoint, voucherType, number }, options)` looks up the original,
|
|
84
|
+
then mirrors its full amount into credit note A, B or C (types 3, 8, 13).
|
|
85
|
+
It preserves the receiver, currency, exchange rate, VAT rates and service dates.
|
|
86
|
+
The note date defaults to today in Buenos Aires; use `options.date` to override.
|
|
87
|
+
The payment due date cannot precede the note date. `idempotencyKey` and `include`
|
|
88
|
+
work exactly as for `issue()`; give cancellation its own stable key, such as
|
|
89
|
+
`cancel:${venta.id}`. Replaying that key consults only the reserved note.
|
|
90
|
+
|
|
91
|
+
Only authorized invoice types 1, 6 and 11 in ARS or USD are supported.
|
|
92
|
+
Originals with tributes, optional fields, buyers, activities or associated
|
|
93
|
+
periods require the exact API. Partial notes, debit notes and FCE also require
|
|
94
|
+
exact control. Missing original evidence is an error, never guessed.
|
|
95
|
+
|
|
96
|
+
## Stores
|
|
97
|
+
|
|
98
|
+
One `store` persists WSAA tickets and immutable invoice/credit-note reservations.
|
|
99
|
+
The SDK adds no database or Redis driver dependency. Store failures throw
|
|
100
|
+
`ArcaConfigurationError` with their cause attached and a content-free message.
|
|
101
|
+
|
|
102
|
+
### Postgres
|
|
103
|
+
|
|
104
|
+
Use your application's existing client. Neon, Supabase Postgres, Vercel Postgres,
|
|
105
|
+
`pg` and `postgres` can provide the parameterized query function. Results can
|
|
106
|
+
be an array of rows or `{ rows }`. With `postgres`, adapt `sql.unsafe(text, params)`.
|
|
107
|
+
Provision the default table once:
|
|
108
|
+
|
|
109
|
+
```sql
|
|
110
|
+
CREATE TABLE arca_store (
|
|
111
|
+
key text PRIMARY KEY,
|
|
112
|
+
value text NOT NULL,
|
|
113
|
+
updated_at timestamptz NOT NULL DEFAULT now()
|
|
114
|
+
);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
const store = createPostgresStore({
|
|
119
|
+
query: (text, params) => sql.query(text, params),
|
|
120
|
+
table: "arca_store", // Optional simple SQL identifier.
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Atomic creation uses `INSERT ... ON CONFLICT DO NOTHING RETURNING key`.
|
|
125
|
+
The adapter does not create the table or hold a database lock.
|
|
126
|
+
|
|
127
|
+
### Redis
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
import { createRedisStore } from "facturas";
|
|
131
|
+
const store = createRedisStore(redis);
|
|
132
|
+
// Optional override: createRedisStore(redis, { flavor: "upstash" });
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
A client with `call` uses ioredis `SET key value NX`; otherwise the adapter
|
|
136
|
+
uses Upstash `set(key, value, { nx: true })`. Use a durable Redis deployment
|
|
137
|
+
without eviction of reservation keys. Neither flavor applies a TTL or lock.
|
|
138
|
+
|
|
139
|
+
### Files
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
import { createFileStore } from "facturas";
|
|
143
|
+
const store = createFileStore("/private/durable/arca");
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Use a private durable volume on a single server. Keys are hashed to filenames;
|
|
147
|
+
creation is exclusive, replacement uses a temporary file and rename. Files
|
|
148
|
+
have mode `0600`, new directories `0700`. No process lock is provided.
|
|
149
|
+
|
|
150
|
+
### Memory
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
import { createMemoryStore } from "facturas";
|
|
154
|
+
const store = createMemoryStore();
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
For tests and examples. It serializes ticket refreshes within the shared object,
|
|
158
|
+
but **does not survive a restart**. It does not make serverless retries durable.
|
|
159
|
+
|
|
160
|
+
### Custom store and record lifetime
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
type ArcaStore = {
|
|
164
|
+
get(key: string): Promise<string | null>;
|
|
165
|
+
set(key: string, value: string): Promise<void>;
|
|
166
|
+
add(key: string, value: string): Promise<boolean>;
|
|
167
|
+
delete?(key: string): Promise<void>;
|
|
168
|
+
withLock?<T>(key: string, fn: () => Promise<T>): Promise<T>;
|
|
169
|
+
};
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
`add` must atomically return false without changing an existing value. Optional
|
|
173
|
+
`withLock` coordinates WSAA ticket refreshes. Explicit `wsaaSessionStore` wins
|
|
174
|
+
for tickets when both options are provided.
|
|
175
|
+
|
|
176
|
+
Keys use `arca:v1:wsaa:{environment}:{service}:{fingerprint}` and
|
|
177
|
+
`arca:v1:attempt:{environment}:{taxId}:{idempotencyKey}`. Reservation records
|
|
178
|
+
contain the input hash, operation, reserved coordinates and exact sent input.
|
|
179
|
+
They contain fiscal/customer data: restrict access and protect backups.
|
|
180
|
+
|
|
181
|
+
**Do not prune, expire or rewrite reservation records.** The SDK only creates
|
|
182
|
+
them, never saves outcomes over them, and always consults ARCA on replay.
|
|
183
|
+
Deleting a reservation can let a later retry issue another invoice.
|
|
184
|
+
|
|
185
|
+
## Invoice inputs
|
|
186
|
+
|
|
187
|
+
The issuer is your legal assertion on each call; the SDK never infers it from
|
|
188
|
+
items or Padrón. An RI issuer produces A for RI or Monotributo receivers and B
|
|
189
|
+
for the other supported conditions. Monotributo, Exento and No Alcanzado issuers
|
|
190
|
+
produce C and use `items: [{ amount: 10_000 }]`. ARCA validates actual eligibility.
|
|
191
|
+
`to` is the fiscal receiver (the exact layer's receiver document/condition fields),
|
|
192
|
+
not a customer record.
|
|
193
|
+
|
|
194
|
+
Amounts are integer minor units. For RI items, choose `net` or `gross` on each
|
|
195
|
+
item and one of `0 | 2.5 | 5 | 10.5 | 21 | 27 | "exempt" | "untaxed"` for `vat`.
|
|
196
|
+
Numeric zero is a VAT rate; exempt and untaxed amounts have separate fiscal
|
|
197
|
+
fields. Items are grouped by rate before Round Half Even rounding.
|
|
198
|
+
|
|
199
|
+
`total`, when supplied, asserts the sent total. The SDK adjusts header VAT only
|
|
200
|
+
within one cent per emitted numeric rate, while keeping VAT non-negative.
|
|
201
|
+
Class C totals must match exactly. Authorized results expose `computedTotal`,
|
|
202
|
+
`sentTotal` and `vatAdjustment` in `voucher.amounts`, all in minor units.
|
|
203
|
+
|
|
204
|
+
The defaults are today's date in Buenos Aires, goods (concept 1), and `ARS` at
|
|
205
|
+
exchange rate `1`. Use `currency: "USD"` with a positive decimal-string
|
|
206
|
+
`exchangeRate`, or `service: { from, to, dueDate }` for concept 2. Dates accept
|
|
207
|
+
`YYYY-MM-DD` or `YYYYMMDD`; service end must be on or after its start and the payment due
|
|
208
|
+
date must be on or after the invoice date.
|
|
209
|
+
|
|
210
|
+
Non-final-consumer receivers require an 11-digit `cuit`. A final consumer accepts
|
|
211
|
+
one `cuit` or `dni`, or neither below the identification threshold. At or above
|
|
212
|
+
ARS 10,000,000 (including USD converted at the supplied rate), identification is
|
|
213
|
+
required under [RG 5866/2026](https://www.argentina.gob.ar/normativa/nacional/norma-427092/texto).
|
|
214
|
+
When the customer requests a CUIT for an income-tax deduction, supply it regardless
|
|
215
|
+
of amount. Document shape checks do not verify provider registration.
|
|
216
|
+
|
|
217
|
+
### Facade fiscal contract
|
|
218
|
+
|
|
219
|
+
Without a key, a call reads one next number and authorizes once, with at most
|
|
220
|
+
one identity lookup after an indeterminate response. This is the v0.8 behavior.
|
|
221
|
+
A first keyed call reserves that number before writing. A keyed replay looks
|
|
222
|
+
up the reservation: only `not_found` allows one authorization of the stored
|
|
223
|
+
number. A found voucher is never resubmitted. Indeterminate writes and keyed
|
|
224
|
+
10016 rejections can add one lookup; a 10016 without a complete match remains
|
|
225
|
+
rejected. Cancel adds the original lookup only when creating a new reservation.
|
|
226
|
+
|
|
227
|
+
| Outcome | Meaning and caller action |
|
|
228
|
+
| --- | --- |
|
|
229
|
+
| `authorized` | Save the voucher and CAE. `recoveredByMatch: true` means the stored input matched the consulted identity; it proves consistency, not authorship. |
|
|
230
|
+
| `rejected` | Review ARCA's `issues`. A key remains bound to its input even after rejection. |
|
|
231
|
+
| `indeterminate` | Preserve the number and evidence. Reconcile or retry the identical input with its existing key. |
|
|
232
|
+
| `conflict` | A different voucher occupies the reserved number. Stop and investigate. |
|
|
233
|
+
|
|
234
|
+
The second argument accepts `idempotencyKey`, `representedTaxId`, `forceRefresh`
|
|
235
|
+
and `include: { raw: true, exactInput: true }`. Outcomes are raw-free by default;
|
|
236
|
+
`sent` is included only on authorized outcomes when requested. Replay without
|
|
237
|
+
an observed write outcome uses an indeterminate attempt with
|
|
238
|
+
`reason: "incomplete_response"`; the lookup provides the authorization evidence.
|
|
239
|
+
|
|
240
|
+
The identity matcher compares coordinates, date, concept, receiver, currency,
|
|
241
|
+
all header amounts, VAT rates, service dates, and note associations. Missing
|
|
242
|
+
fields stay incomplete; differences are conflicts. Unsupported exact extensions
|
|
243
|
+
remain incomplete. Use exact APIs for tributes, FCE, other receiver conditions,
|
|
244
|
+
same-currency foreign cancellation and WSMTXCA.
|
|
245
|
+
|
|
246
|
+
## Exact control
|
|
247
|
+
|
|
248
|
+
This example mirrors [examples/factura-b-consumidor-final.ts](./examples/factura-b-consumidor-final.ts).
|
|
249
|
+
|
|
250
|
+
```ts
|
|
251
|
+
import { buildFacturaB, createArcaClient } from "facturas";
|
|
252
|
+
import {
|
|
253
|
+
ARCA_CONCEPT_TYPES,
|
|
254
|
+
ARCA_DOCUMENT_TYPES,
|
|
255
|
+
ARCA_RECEIVER_VAT_CONDITIONS,
|
|
256
|
+
} from "facturas/constants";
|
|
257
|
+
|
|
258
|
+
const client = createArcaClient({
|
|
259
|
+
taxId: "20123456789",
|
|
260
|
+
certificatePem:
|
|
261
|
+
"-----BEGIN CERTIFICATE-----\nREPLACE_WITH_YOUR_CERTIFICATE\n-----END CERTIFICATE-----",
|
|
262
|
+
privateKeyPem:
|
|
263
|
+
"-----BEGIN PRIVATE KEY-----\nREPLACE_WITH_YOUR_PRIVATE_KEY\n-----END PRIVATE KEY-----",
|
|
264
|
+
environment: "test",
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
const data = buildFacturaB({
|
|
268
|
+
salesPoint: 1,
|
|
269
|
+
concept: ARCA_CONCEPT_TYPES.PRODUCTOS,
|
|
270
|
+
documentType: ARCA_DOCUMENT_TYPES.CONSUMIDOR_FINAL,
|
|
271
|
+
documentNumber: 0,
|
|
272
|
+
receiverVatConditionId: ARCA_RECEIVER_VAT_CONDITIONS.CONSUMIDOR_FINAL,
|
|
273
|
+
// Deterministic example date; use an ARCA-allowed current date in homologation.
|
|
274
|
+
voucherDate: "2026-09-02",
|
|
275
|
+
taxableAmount: 10_000, // Integer minor units: ARS 100.00.
|
|
276
|
+
vatRate: 21,
|
|
277
|
+
// currency is omitted, so the builder defaults to ISO ARS.
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
// Exact layer: reserve the number yourself, then issue it exactly once.
|
|
281
|
+
const voucherNumber = await client.wsfe.getNextVoucherNumber({
|
|
282
|
+
salesPoint: data.salesPoint,
|
|
283
|
+
voucherType: data.voucherType,
|
|
284
|
+
});
|
|
285
|
+
const issued = await client.wsfe.issue({ voucherNumber, data });
|
|
286
|
+
|
|
287
|
+
if (issued.kind === "authorized") {
|
|
288
|
+
console.log(issued.cae, issued.caeExpiry, issued.voucherNumber);
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
`buildFacturaB()` derives the Factura B type, net amount, IVA detail, IVA
|
|
293
|
+
amount, zero-value fields, and total without floating-point tax arithmetic.
|
|
294
|
+
`buildFacturaC()` separately builds the zero-IVA Factura C shape. Both builders
|
|
295
|
+
accept integer currency minor units and support ISO `ARS` (the default) and
|
|
296
|
+
`USD`. Factura B requires a positive `taxableAmount`; when `vatRate` is
|
|
297
|
+
positive, the amount must produce at least one currency minor unit of IVA after
|
|
298
|
+
rounding. IVA uses the Round Half Even criterion documented by ARCA, so an
|
|
299
|
+
exact half-cent is rounded to the even cent.
|
|
300
|
+
|
|
301
|
+
For a USD invoice, pass a decimal-string exchange rate:
|
|
302
|
+
|
|
303
|
+
```ts
|
|
304
|
+
const usdData = buildFacturaB({
|
|
305
|
+
salesPoint: 1,
|
|
306
|
+
concept: ARCA_CONCEPT_TYPES.PRODUCTOS,
|
|
307
|
+
documentType: ARCA_DOCUMENT_TYPES.CONSUMIDOR_FINAL,
|
|
308
|
+
documentNumber: 0,
|
|
309
|
+
receiverVatConditionId: ARCA_RECEIVER_VAT_CONDITIONS.CONSUMIDOR_FINAL,
|
|
310
|
+
voucherDate: "2026-09-02",
|
|
311
|
+
taxableAmount: 10_000, // USD 100.00.
|
|
312
|
+
vatRate: 21,
|
|
313
|
+
currency: "USD",
|
|
314
|
+
exchangeRate: "1095.500000",
|
|
315
|
+
});
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
## What You Can Do Today
|
|
319
|
+
|
|
320
|
+
### WSFE
|
|
321
|
+
|
|
322
|
+
- Build A/B/C invoices from explicit assertions with the facade above
|
|
323
|
+
- Issue invoices and credit notes with the existing exact WSFE methods
|
|
324
|
+
- Query voucher numbers and voucher details
|
|
325
|
+
- Read ARCA catalogs with methods like `getVoucherTypes()` and `getVatRates()`
|
|
326
|
+
- Check backend health with `getServerStatus()`
|
|
327
|
+
|
|
328
|
+
### Padrón
|
|
329
|
+
|
|
330
|
+
- Look up taxpayer data with `client.padron.getTaxpayerDetails(...)`
|
|
331
|
+
- Resolve CUITs from document numbers with `client.padron.getTaxIdByDocument(...)`
|
|
332
|
+
|
|
333
|
+
### WSMTXCA
|
|
334
|
+
|
|
335
|
+
WSMTXCA remains supported and exported, but this package currently puts most editorial focus on WSFE and Padrón. If you need `issue`, `getLastAuthorizedVoucher`, `lookupVoucher`, `getVoucher`, or `getSalesPoints`, the runtime API is available and covered by tests.
|
|
336
|
+
|
|
337
|
+
## Exact issuance and recovery evidence
|
|
338
|
+
|
|
339
|
+
`client.issue()` derives the WSFE request, reserves the number and recovers
|
|
340
|
+
after a crash for you. When you need something it does not derive (tributes,
|
|
341
|
+
partial notes, FCE, an associated period) or your application owns the
|
|
342
|
+
numbering, use the exact layer: `client.wsfe.issue(...)` sends one
|
|
343
|
+
FECAESolicitar for a caller-owned, durably reserved voucher number and tells
|
|
344
|
+
you whether that exact attempt was authorized, rejected, or left
|
|
345
|
+
indeterminate. It preserves every structured error and observation with its
|
|
346
|
+
service, operation, code, source, and result level.
|
|
347
|
+
|
|
348
|
+
```ts
|
|
349
|
+
const outcome = await client.wsfe.issue({
|
|
350
|
+
voucherNumber: reservedVoucherNumber,
|
|
351
|
+
data,
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
if (outcome.kind === "authorized") {
|
|
355
|
+
console.log(outcome.cae, outcome.voucherNumber);
|
|
356
|
+
} else if (outcome.kind === "rejected") {
|
|
357
|
+
console.error(outcome.errors, outcome.observations);
|
|
358
|
+
} else {
|
|
359
|
+
if (outcome.reason === "authentication_rejected") {
|
|
360
|
+
console.error(outcome.authentication?.reason);
|
|
361
|
+
}
|
|
362
|
+
// Consult the same number before any new authorization attempt.
|
|
363
|
+
const lookup = await client.wsfe.lookupVoucher({
|
|
364
|
+
number: reservedVoucherNumber,
|
|
365
|
+
salesPoint: data.salesPoint,
|
|
366
|
+
voucherType: data.voucherType,
|
|
367
|
+
});
|
|
368
|
+
console.log(lookup.kind);
|
|
369
|
+
}
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
`wsfe.issue()` and `wsmtxca.issue()` force one SOAP transport attempt, even when the client has general transport retries configured. They never refresh credentials and resubmit automatically. An explicit provider authentication rejection is returned as `reason: "authentication_rejected"` with safe typed `authentication` evidence; a timeout, connection failure, invalid response, or incomplete/contradictory result remains indeterminate without resubmission. This prevents uncertain fiscal work from causing a hidden second authorization.
|
|
373
|
+
|
|
374
|
+
Authenticated read, catalog, and lookup operations may repeat once with a
|
|
375
|
+
forced credential refresh after an explicit typed authentication rejection.
|
|
376
|
+
Passing `forceRefresh: true` disables any further authentication recovery
|
|
377
|
+
attempt.
|
|
378
|
+
|
|
379
|
+
The deprecated `authorizeVoucher(...)` methods throw instead of returning
|
|
380
|
+
evidence, and the deprecated WSFE `createNextVoucher(...)` reads the next
|
|
381
|
+
number and authorizes it in one non-idempotent call. Both remain for one
|
|
382
|
+
release; applications use `client.issue()` or reserve a number and call
|
|
383
|
+
`wsfe.issue()`.
|
|
384
|
+
|
|
385
|
+
Exact lookup absence is operation-specific:
|
|
386
|
+
|
|
387
|
+
- WSFE `FECompConsultar` code 602 returns `not_found`.
|
|
388
|
+
- WSMTXCA `consultarComprobante` code 1503 returns `not_found`.
|
|
389
|
+
- WSMTXCA `consultarUltimoComprobanteAutorizado` code 1502 returns voucher number `0`.
|
|
390
|
+
- WSMTXCA code 602 is not exact-voucher absence and remains an error.
|
|
391
|
+
|
|
392
|
+
The SDK normalizes provider protocol evidence only. Your application remains responsible for persisting the exact request, owning its sequence or lane, and deciding when a retry is safe.
|
|
393
|
+
|
|
394
|
+
For tributes, notes, FCE, or other advanced cases, use the exact
|
|
395
|
+
`WsfeVoucherInput` escape hatch. It also continues to support exemptions,
|
|
396
|
+
non-taxable amounts and multiple IVA rates. Exact
|
|
397
|
+
amounts remain major-unit numbers, are validated locally, and are serialized as
|
|
398
|
+
canonical two-decimal strings:
|
|
399
|
+
|
|
400
|
+
```ts
|
|
401
|
+
import type { WsfeVoucherInput } from "facturas/wsfe";
|
|
402
|
+
import {
|
|
403
|
+
ARCA_CONCEPT_TYPES,
|
|
404
|
+
ARCA_CURRENCY_IDS,
|
|
405
|
+
ARCA_DOCUMENT_TYPES,
|
|
406
|
+
ARCA_RECEIVER_VAT_CONDITIONS,
|
|
407
|
+
ARCA_VAT_RATES,
|
|
408
|
+
ARCA_VOUCHER_TYPES,
|
|
409
|
+
} from "facturas/constants";
|
|
410
|
+
|
|
411
|
+
const exactData: WsfeVoucherInput = {
|
|
412
|
+
salesPoint: 1,
|
|
413
|
+
voucherType: ARCA_VOUCHER_TYPES.FACTURA_B,
|
|
414
|
+
concept: ARCA_CONCEPT_TYPES.PRODUCTOS,
|
|
415
|
+
documentType: ARCA_DOCUMENT_TYPES.CONSUMIDOR_FINAL,
|
|
416
|
+
documentNumber: 0,
|
|
417
|
+
receiverVatConditionId: ARCA_RECEIVER_VAT_CONDITIONS.CONSUMIDOR_FINAL,
|
|
418
|
+
voucherDate: "2026-09-02",
|
|
419
|
+
totalAmount: 121,
|
|
420
|
+
nonTaxableAmount: 0,
|
|
421
|
+
netAmount: 100,
|
|
422
|
+
exemptAmount: 0,
|
|
423
|
+
taxAmount: 0,
|
|
424
|
+
vatAmount: 21,
|
|
425
|
+
currencyId: ARCA_CURRENCY_IDS.ARS,
|
|
426
|
+
exchangeRate: "1",
|
|
427
|
+
vatRates: [{ id: ARCA_VAT_RATES.IVA_21, baseAmount: 100, amount: 21 }],
|
|
428
|
+
};
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
Exact inputs and live catalog responses use ARCA protocol identifiers such as
|
|
432
|
+
`PES` and `DOL`; the facade and high-level builders accept ISO `ARS` and `USD`.
|
|
433
|
+
|
|
434
|
+
### Migrating amount and currency inputs
|
|
435
|
+
|
|
436
|
+
Applications that previously rounded decimal major-unit values and translated
|
|
437
|
+
currencies to ARCA IDs can move that provider-boundary work into a builder:
|
|
438
|
+
|
|
439
|
+
```ts
|
|
440
|
+
// Before: caller-owned decimal rounding and provider vocabulary.
|
|
441
|
+
const exactAmount = Number(sourceAmount.toFixed(2));
|
|
442
|
+
const exactCurrencyId = sourceCurrency === "ARS" ? "PES" : "DOL";
|
|
443
|
+
|
|
444
|
+
// After: integer minor units and ISO currency input.
|
|
445
|
+
const data = buildFacturaB({
|
|
446
|
+
salesPoint: 1,
|
|
447
|
+
concept: ARCA_CONCEPT_TYPES.PRODUCTOS,
|
|
448
|
+
documentType: ARCA_DOCUMENT_TYPES.CONSUMIDOR_FINAL,
|
|
449
|
+
documentNumber: 0,
|
|
450
|
+
receiverVatConditionId: ARCA_RECEIVER_VAT_CONDITIONS.CONSUMIDOR_FINAL,
|
|
451
|
+
voucherDate: "2026-09-02",
|
|
452
|
+
taxableAmount: 10_000, // 100.00 in the selected currency.
|
|
453
|
+
vatRate: 21,
|
|
454
|
+
currency: "USD",
|
|
455
|
+
exchangeRate: "1095.5",
|
|
456
|
+
});
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
Keep using `WsfeVoucherInput` when you need advanced exact fields. Its amounts
|
|
460
|
+
remain decimal major-unit values and its `currencyId` remains an ARCA ID.
|
|
461
|
+
|
|
462
|
+
## Examples
|
|
463
|
+
|
|
464
|
+
- [Keyed invoice](./examples/issue-invoice.ts)
|
|
465
|
+
- [Full credit note](./examples/anular-factura.ts)
|
|
466
|
+
|
|
467
|
+
Examples live in [examples/](./examples) and are intentionally complete, hardcoded, and readable so they can be adapted quickly by a developer or a coding agent.
|
|
468
|
+
Issuance examples use deterministic dates for compilation; replace them with an
|
|
469
|
+
ARCA-allowed current date before a homologation request.
|
|
470
|
+
|
|
471
|
+
- [factura-b-consumidor-final.ts](./examples/factura-b-consumidor-final.ts)
|
|
472
|
+
- [factura-a-responsable-inscripto.ts](./examples/factura-a-responsable-inscripto.ts)
|
|
473
|
+
- [nota-de-credito-asociada.ts](./examples/nota-de-credito-asociada.ts)
|
|
474
|
+
- [factura-servicios-con-periodo.ts](./examples/factura-servicios-con-periodo.ts)
|
|
475
|
+
- [consultar-comprobante.ts](./examples/consultar-comprobante.ts)
|
|
476
|
+
- [consultar-contribuyente.ts](./examples/consultar-contribuyente.ts)
|
|
477
|
+
|
|
478
|
+
## Manual Setup Reality
|
|
479
|
+
|
|
480
|
+
This package does **not** provision ARCA credentials for you. You still need to do the official certificate and service setup outside the SDK.
|
|
481
|
+
|
|
482
|
+
Before using the SDK:
|
|
483
|
+
|
|
484
|
+
1. Obtain a valid CUIT.
|
|
485
|
+
2. Generate or receive a certificate and matching private key in PEM format.
|
|
486
|
+
3. Authorize the certificate for the target service and environment.
|
|
487
|
+
4. Start with `environment: "test"` and move to production only after end-to-end validation.
|
|
488
|
+
|
|
489
|
+
Official ARCA / AFIP references:
|
|
490
|
+
|
|
491
|
+
- [WSAA documentation](https://www.afip.gob.ar/ws/documentacion/wsaa.asp)
|
|
492
|
+
- [Certificates for testing / homologation](https://www.afip.gob.ar/ws/documentacion/certificados.asp)
|
|
493
|
+
- [WSAA developer manual](https://www.afip.gob.ar/ws/WSAA/WSAAmanualDev.pdf)
|
|
494
|
+
- [WSASS service onboarding](https://www.afip.gob.ar/ws/WSASS/WSASS_como_adherirse.pdf)
|
|
495
|
+
- [WSFE developer manual](https://www.afip.gob.ar/ws/documentacion/manuales/manual-desarrollador-ARCA-COMPG.pdf)
|
|
496
|
+
|
|
497
|
+
## Reference Data
|
|
498
|
+
|
|
499
|
+
The package exports a small, stable set of common ARCA codes from `facturas/constants`.
|
|
500
|
+
|
|
501
|
+
```ts
|
|
502
|
+
import {
|
|
503
|
+
ARCA_CONCEPT_TYPES,
|
|
504
|
+
ARCA_CURRENCY_IDS,
|
|
505
|
+
ARCA_CURRENCIES,
|
|
506
|
+
ARCA_DOCUMENT_TYPES,
|
|
507
|
+
ARCA_RECEIVER_VAT_CONDITIONS,
|
|
508
|
+
ARCA_VAT_RATES,
|
|
509
|
+
ARCA_VOUCHER_TYPES,
|
|
510
|
+
ISO_CURRENCIES,
|
|
511
|
+
} from "facturas/constants";
|
|
512
|
+
|
|
513
|
+
ARCA_VOUCHER_TYPES.FACTURA_A; // 1
|
|
514
|
+
ARCA_VOUCHER_TYPES.FACTURA_B; // 6
|
|
515
|
+
ARCA_DOCUMENT_TYPES.CUIT; // 80
|
|
516
|
+
ARCA_DOCUMENT_TYPES.DNI; // 96
|
|
517
|
+
ARCA_DOCUMENT_TYPES.CONSUMIDOR_FINAL; // 99
|
|
518
|
+
ARCA_RECEIVER_VAT_CONDITIONS.RESPONSABLE_INSCRIPTO; // 1
|
|
519
|
+
ARCA_RECEIVER_VAT_CONDITIONS.CONSUMIDOR_FINAL; // 5
|
|
520
|
+
ARCA_CONCEPT_TYPES.SERVICIOS; // 2
|
|
521
|
+
ARCA_VAT_RATES.IVA_21; // 5
|
|
522
|
+
ISO_CURRENCIES.ARS; // "ARS"
|
|
523
|
+
ARCA_CURRENCY_IDS.USD; // "DOL"
|
|
524
|
+
ARCA_CURRENCIES.PES; // "PES"
|
|
525
|
+
ARCA_CURRENCIES.DOL; // "DOL"
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
The constants cover the most common values used by the README and examples:
|
|
529
|
+
|
|
530
|
+
- voucher types for invoice A/B/C, debit note A/B/C, and credit note A/B/C
|
|
531
|
+
- document types for CUIT, DNI, and final consumers
|
|
532
|
+
- common receiver IVA conditions, subject to voucher-class and live-catalog rules
|
|
533
|
+
- concept types for products, services, and products + services
|
|
534
|
+
- IVA rates for `0`, `2.5`, `5`, `10.5`, `21`, and `27`
|
|
535
|
+
- builder ISO currencies `ARS` and `USD`, with explicit ARCA mappings to `PES`
|
|
536
|
+
and `DOL`
|
|
537
|
+
|
|
538
|
+
`ARCA_CURRENCIES` remains a deprecated compatibility alias with its existing
|
|
539
|
+
`PES` and `DOL` values. If you need broader catalogs at runtime, WSFE methods
|
|
540
|
+
such as `getVoucherTypes()`, `getDocumentTypes()`, `getCurrencyTypes()`, and
|
|
541
|
+
`getVatRates()` are still available. `getCurrencyTypes()` returns live ARCA
|
|
542
|
+
identifiers, not ISO codes.
|
|
543
|
+
|
|
544
|
+
## Configuration
|
|
545
|
+
|
|
546
|
+
### Environment variables
|
|
547
|
+
|
|
548
|
+
`createArcaClient()` discovers missing fields using the same rules as
|
|
549
|
+
`createArcaClientConfigFromEnv()`. Explicit fields win; `process.env` is not changed:
|
|
550
|
+
|
|
551
|
+
| Variable | Required | Notes |
|
|
552
|
+
| --- | --- | --- |
|
|
553
|
+
| `ARCA_TAX_ID` | Yes | 11-digit CUIT |
|
|
554
|
+
| `ARCA_CERTIFICATE_PEM` | Yes | PEM certificate |
|
|
555
|
+
| `ARCA_PRIVATE_KEY_PEM` | Yes | PEM private key |
|
|
556
|
+
| `ARCA_ENVIRONMENT` | Yes | `test` or `production`; there is no default |
|
|
557
|
+
|
|
558
|
+
For logging without code changes, set `ARCA_LOG_LEVEL` to `debug`, `info`, `warn`, or `error`.
|
|
559
|
+
|
|
560
|
+
Pass a config object to `createArcaClient`:
|
|
561
|
+
|
|
562
|
+
```ts
|
|
563
|
+
import { createArcaClient } from "facturas";
|
|
564
|
+
|
|
565
|
+
const client = createArcaClient({
|
|
566
|
+
taxId: "20123456789",
|
|
567
|
+
certificatePem: process.env.ARCA_CERTIFICATE_PEM!,
|
|
568
|
+
privateKeyPem: process.env.ARCA_PRIVATE_KEY_PEM!,
|
|
569
|
+
environment: "test",
|
|
570
|
+
timeout: 30_000,
|
|
571
|
+
retries: 2,
|
|
572
|
+
retryDelay: 500,
|
|
573
|
+
logger: { level: "debug" },
|
|
574
|
+
// Optional: share WSAA login tickets across workers.
|
|
575
|
+
// wsaaSessionStore,
|
|
576
|
+
});
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
| Field | Default | Description |
|
|
580
|
+
| --- | --- | --- |
|
|
581
|
+
| `taxId` | `ARCA_TAX_ID` | 11-digit CUIT |
|
|
582
|
+
| `certificatePem` | `ARCA_CERTIFICATE_PEM` | PEM certificate |
|
|
583
|
+
| `privateKeyPem` | `ARCA_PRIVATE_KEY_PEM` | PEM private key |
|
|
584
|
+
| `environment` | `test` | `test` or `production` |
|
|
585
|
+
| `timeout` | `30000` | HTTP request timeout in milliseconds |
|
|
586
|
+
| `retries` | `0` | Extra attempts after transport failures only |
|
|
587
|
+
| `retryDelay` | `500` | Delay between transport retries in milliseconds |
|
|
588
|
+
| `logger` | — | Optional structured logger config |
|
|
589
|
+
| `store` | — | Unified durable tickets and reservations |
|
|
590
|
+
| `wsaaSessionStore` | — | Optional WSAA ticket store for multi-worker deployments |
|
|
591
|
+
|
|
592
|
+
### WSAA session stores
|
|
593
|
+
|
|
594
|
+
By default, WSAA login tickets are cached in the current process only. That keeps scripts and single-process apps zero-config:
|
|
595
|
+
|
|
596
|
+
```ts
|
|
597
|
+
const client = createArcaClient({
|
|
598
|
+
taxId: "20123456789",
|
|
599
|
+
certificatePem: process.env.ARCA_CERTIFICATE_PEM!,
|
|
600
|
+
privateKeyPem: process.env.ARCA_PRIVATE_KEY_PEM!,
|
|
601
|
+
environment: "production",
|
|
602
|
+
});
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
A configured `store` supplies durable WSAA tickets automatically. An explicit
|
|
606
|
+
`wsaaSessionStore` remains supported and takes precedence for tickets only.
|
|
607
|
+
|
|
608
|
+
```ts
|
|
609
|
+
import {
|
|
610
|
+
type ArcaAuthCredentials,
|
|
611
|
+
type ArcaWsaaSessionKey,
|
|
612
|
+
createArcaClient,
|
|
613
|
+
} from "facturas";
|
|
614
|
+
|
|
615
|
+
const wsaaSessionStore = {
|
|
616
|
+
async get(key: ArcaWsaaSessionKey): Promise<ArcaAuthCredentials | null> {
|
|
617
|
+
// Read from Postgres, Redis, or another shared store.
|
|
618
|
+
return null;
|
|
619
|
+
},
|
|
620
|
+
async set(
|
|
621
|
+
key: ArcaWsaaSessionKey,
|
|
622
|
+
credentials: ArcaAuthCredentials
|
|
623
|
+
): Promise<void> {
|
|
624
|
+
// Persist token, sign, and expiresAt for the key.
|
|
625
|
+
},
|
|
626
|
+
async withLock<T>(
|
|
627
|
+
key: ArcaWsaaSessionKey,
|
|
628
|
+
fn: () => Promise<T>
|
|
629
|
+
): Promise<T> {
|
|
630
|
+
// Optional but recommended: serialize cold-start refreshes.
|
|
631
|
+
return await fn();
|
|
632
|
+
},
|
|
633
|
+
};
|
|
634
|
+
|
|
635
|
+
const client = createArcaClient({
|
|
636
|
+
taxId: "20123456789",
|
|
637
|
+
certificatePem: process.env.ARCA_CERTIFICATE_PEM!,
|
|
638
|
+
privateKeyPem: process.env.ARCA_PRIVATE_KEY_PEM!,
|
|
639
|
+
environment: "production",
|
|
640
|
+
wsaaSessionStore,
|
|
641
|
+
});
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
The store key is scoped by environment, WSAA service, and certificate fingerprint. Store reads are still checked with the SDK's expiration safety margin. A production store should share data across all workers, encrypt or rely on encrypted storage, enforce expiration on read, and implement locking with advisory locks, Redis locks, or equivalent.
|
|
645
|
+
|
|
646
|
+
For tests and local coordination through one shared object, the package also exports `createMemoryWsaaSessionStore()`.
|
|
647
|
+
|
|
648
|
+
## Logging
|
|
649
|
+
|
|
650
|
+
Default minimum level is `warn`. At `debug`, the SDK logs SOAP requests, response timings, WSAA login source (`cached` vs `fresh`), and retry attempts.
|
|
651
|
+
|
|
652
|
+
```ts
|
|
653
|
+
const client = createArcaClient({
|
|
654
|
+
taxId: "20123456789",
|
|
655
|
+
certificatePem: "...",
|
|
656
|
+
privateKeyPem: "...",
|
|
657
|
+
environment: "test",
|
|
658
|
+
logger: { level: "debug" },
|
|
659
|
+
});
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
Custom logger sinks receive `(level, message, ...args)`:
|
|
663
|
+
|
|
664
|
+
```ts
|
|
665
|
+
const client = createArcaClient({
|
|
666
|
+
taxId: "20123456789",
|
|
667
|
+
certificatePem: "...",
|
|
668
|
+
privateKeyPem: "...",
|
|
669
|
+
environment: "production",
|
|
670
|
+
logger: {
|
|
671
|
+
level: "info",
|
|
672
|
+
log(level, message, ...args) {
|
|
673
|
+
// forward to your logger
|
|
674
|
+
},
|
|
675
|
+
},
|
|
676
|
+
});
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
Disable logging entirely with `logger: { disabled: true }`.
|
|
680
|
+
|
|
681
|
+
## Retries and timeouts
|
|
682
|
+
|
|
683
|
+
Configured transport retries apply only to `ArcaTransportError`: timeouts, connection failures, and non-XML HTTP error responses. XML responses, including HTTP 500 SOAP faults, are parsed and surfaced as SOAP or service errors instead of being retried blindly.
|
|
684
|
+
|
|
685
|
+
Separately, authenticated WSFE and WSMTXCA convenience operations may perform
|
|
686
|
+
one forced-refresh retry only after `ArcaAuthenticationError`. Timeouts,
|
|
687
|
+
connection loss, invalid SOAP, incomplete evidence, contradictory evidence, and
|
|
688
|
+
generic service rejections never unlock this recovery path. `wsfe.issue()` and
|
|
689
|
+
`wsmtxca.issue()` always perform one exact authorization attempt, and each
|
|
690
|
+
authorization SOAP attempt has transport retries set to zero.
|
|
691
|
+
|
|
692
|
+
## Service Surface
|
|
693
|
+
|
|
694
|
+
### `client.wsfe`
|
|
695
|
+
|
|
696
|
+
WSFE electronic invoicing. Inputs use JS-style names and the SDK maps them to AFIP / ARCA SOAP fields internally.
|
|
697
|
+
|
|
698
|
+
- Date fields accept `YYYY-MM-DD` or `YYYYMMDD`.
|
|
699
|
+
- `issue({ voucherNumber, data })` sends one exact authorization and returns `authorized`, `rejected` or `indeterminate` evidence.
|
|
700
|
+
- `getNextVoucherNumber({ salesPoint, voucherType })` reads the next number to reserve.
|
|
701
|
+
- `getVoucherInfo({ number, salesPoint, voucherType })` returns voucher details or `null`.
|
|
702
|
+
- Catalog methods are available for live reference data when you do not want to hardcode values.
|
|
703
|
+
- Authenticated methods accept `forceRefresh: true` to discard the cached WSAA TA and request a fresh Token Authorization for the same service.
|
|
16
704
|
|
|
17
|
-
|
|
705
|
+
### `client.padron`
|
|
18
706
|
|
|
19
|
-
-
|
|
20
|
-
-
|
|
707
|
+
- `getTaxpayerDetails(taxId)` returns taxpayer data or `null`
|
|
708
|
+
- `getTaxIdByDocument(documentNumber)` returns CUIT candidates or `null`
|
|
21
709
|
|
|
22
|
-
|
|
710
|
+
Padron "not found" handling currently depends on SOAP fault message text from ARCA and is therefore more fragile than WSFE code-based flows.
|
|
711
|
+
|
|
712
|
+
### `client.wsmtxca`
|
|
713
|
+
|
|
714
|
+
- `issue({ data })`
|
|
715
|
+
- `getLastAuthorizedVoucher({ voucherType, salesPoint })`
|
|
716
|
+
- `getVoucher({ voucherType, salesPoint, voucherNumber })`
|
|
717
|
+
- Authenticated methods accept `forceRefresh: true` to renew the WSMTXCA WSAA TA before the call.
|
|
718
|
+
|
|
719
|
+
The runtime support is stable and public. It is simply not the main documentation path in this SDK-focused pass.
|
|
720
|
+
|
|
721
|
+
## Error handling
|
|
722
|
+
|
|
723
|
+
All errors extend `ArcaError` and expose a stable `code` string.
|
|
724
|
+
|
|
725
|
+
| Class | When |
|
|
726
|
+
| --- | --- |
|
|
727
|
+
| `ArcaConfigurationError` | Invalid client config |
|
|
728
|
+
| `ArcaInputError` | Invalid caller input such as a malformed date |
|
|
729
|
+
| `ArcaAuthenticationError` | Explicit provider authentication rejection |
|
|
730
|
+
| `ArcaTransportError` | HTTP or transport failure |
|
|
731
|
+
| `ArcaSoapFaultError` | SOAP fault returned by ARCA |
|
|
732
|
+
| `ArcaServiceError` | Business-level service rejection, especially WSFE-style errors |
|
|
733
|
+
|
|
734
|
+
```ts
|
|
735
|
+
import {
|
|
736
|
+
ArcaAuthenticationError,
|
|
737
|
+
ArcaServiceError,
|
|
738
|
+
ArcaSoapFaultError,
|
|
739
|
+
ArcaTransportError,
|
|
740
|
+
} from "facturas";
|
|
741
|
+
|
|
742
|
+
try {
|
|
743
|
+
await client.wsfe.getNextVoucherNumber({ salesPoint: 1, voucherType: 6 });
|
|
744
|
+
} catch (error) {
|
|
745
|
+
if (error instanceof ArcaAuthenticationError) {
|
|
746
|
+
console.error(
|
|
747
|
+
error.reason,
|
|
748
|
+
error.service,
|
|
749
|
+
error.operation,
|
|
750
|
+
error.providerCode
|
|
751
|
+
);
|
|
752
|
+
} else if (error instanceof ArcaServiceError) {
|
|
753
|
+
console.error(error.serviceCode, error.message);
|
|
754
|
+
} else if (error instanceof ArcaSoapFaultError) {
|
|
755
|
+
console.error(error.faultCode, error.message);
|
|
756
|
+
} else if (error instanceof ArcaTransportError) {
|
|
757
|
+
console.error(error.statusCode, error.message);
|
|
758
|
+
}
|
|
759
|
+
throw error;
|
|
760
|
+
}
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
Import error classes from `facturas` or `facturas/errors`.
|
|
764
|
+
`isArcaAuthenticationError(error)` is also exported for predicate-style
|
|
765
|
+
routing. Authentication errors expose only the stable code
|
|
766
|
+
`ARCA_AUTHENTICATION_ERROR`, a typed `reason`, service, operation, and a safe
|
|
767
|
+
provider code when available; raw provider bodies and credential values are not
|
|
768
|
+
attached.
|
|
769
|
+
|
|
770
|
+
## Troubleshooting
|
|
771
|
+
|
|
772
|
+
- `coe.alreadyAuthenticated`: the SDK deduplicates in-flight WSAA logins and reuses valid cached tickets. In serverless, queue workers, or any multi-process deployment, configure a durable `wsaaSessionStore` so cold workers can reuse the TA obtained by another process. Memory-only caching cannot recover across processes.
|
|
773
|
+
- `dh key too small`: WSFE production requests already use a legacy OpenSSL security level where needed. If you still see this, confirm you are not bypassing the SDK transport or terminating TLS in another layer.
|
|
774
|
+
- Expired certificate: replace the PEM certificate with a renewed one that matches the same private key expectations, then redeploy or restart the process.
|
|
775
|
+
- Unauthorized service: your certificate may be valid but not authorized for the target service or environment. Re-check WSASS / homologation setup for test and service relationships for production.
|
|
776
|
+
- WSFE `10015`: usually means the `DocTipo` / `DocNro` combination is inconsistent for the voucher type and amount. For example, Factura B has special receiver-document rules depending on the total amount.
|
|
777
|
+
- WSFE `10016`: the voucher number sent in `CbteDesde` is not the next valid one for that point of sale and voucher type. Call `getNextVoucherNumber()` immediately before authorizing when your numbering may have moved.
|
|
778
|
+
|
|
779
|
+
When an error is unclear, check these in order:
|
|
780
|
+
|
|
781
|
+
1. Certificate and private key match.
|
|
782
|
+
2. Environment is correct (`test` vs `production`).
|
|
783
|
+
3. Service authorization was done for that environment.
|
|
784
|
+
4. The voucher type, document type, and amount combination is valid.
|
|
785
|
+
5. Your process is not reusing stale assumptions about the next voucher number.
|
|
786
|
+
|
|
787
|
+
## Public API (semver)
|
|
788
|
+
|
|
789
|
+
Documented entrypoints:
|
|
23
790
|
|
|
24
791
|
- `facturas`
|
|
25
792
|
- `facturas/constants`
|
|
@@ -29,43 +796,36 @@ The package exports:
|
|
|
29
796
|
- `facturas/errors`
|
|
30
797
|
- `facturas/types`
|
|
31
798
|
|
|
32
|
-
|
|
33
|
-
`issuer`, a fiscal `to` receiver, a required `salesPoint`, and integer-minor-unit
|
|
34
|
-
items. It derives A/B/C, VAT and totals, and returns `authorized`, `rejected`,
|
|
35
|
-
`indeterminate`, or `conflict`. Raw provider evidence is opt-in.
|
|
799
|
+
Low-level SOAP, HTTP, and WSAA internals are not part of the semver contract.
|
|
36
800
|
|
|
37
|
-
|
|
38
|
-
voucherType)`. The SDK does not coordinate writers; concurrent calls collide on
|
|
39
|
-
10016. Servers and queues must persist attempts and use
|
|
40
|
-
`client.wsfe.authorizeVoucherOutcome()` directly. The facade makes one write
|
|
41
|
-
attempt and at most one identity-matched recovery lookup, never a resubmission.
|
|
801
|
+
Subpath example:
|
|
42
802
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
803
|
+
```ts
|
|
804
|
+
import { createWsfeService } from "facturas/wsfe";
|
|
805
|
+
import { ARCA_VOUCHER_TYPES } from "facturas/constants";
|
|
806
|
+
import { ArcaServiceError } from "facturas/errors";
|
|
807
|
+
```
|
|
48
808
|
|
|
49
|
-
|
|
809
|
+
## Security
|
|
50
810
|
|
|
51
|
-
|
|
811
|
+
- Treat certificates and private keys as secrets.
|
|
812
|
+
- By default, WSAA tickets are cached in memory only.
|
|
813
|
+
- The SDK persists WSAA tickets when you provide `store` or `wsaaSessionStore`. Keep their storage private; certificate and private-key configuration is never stored by the bundled adapters.
|
|
814
|
+
- Production `wsaaSessionStore` implementations should encrypt credentials at rest or use a backend that provides encryption at rest.
|
|
52
815
|
|
|
53
|
-
|
|
816
|
+
## Development
|
|
54
817
|
|
|
55
|
-
|
|
818
|
+
```bash
|
|
819
|
+
pnpm install
|
|
820
|
+
pnpm typecheck
|
|
821
|
+
pnpm typecheck:examples
|
|
822
|
+
pnpm test
|
|
823
|
+
pnpm test:coverage
|
|
824
|
+
pnpm pack:check
|
|
825
|
+
```
|
|
56
826
|
|
|
57
|
-
|
|
827
|
+
Optional for local DX: install Turbo globally with `pnpm add --global turbo`. The repo scripts still use the local workspace version.
|
|
58
828
|
|
|
59
|
-
|
|
829
|
+
## License
|
|
60
830
|
|
|
61
|
-
|
|
62
|
-
await client.wsfe.createNextVoucher({
|
|
63
|
-
data: {
|
|
64
|
-
// other voucher fields...
|
|
65
|
-
associatedPeriod: {
|
|
66
|
-
startDate: "2026-05-01",
|
|
67
|
-
endDate: "2026-05-31",
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
});
|
|
71
|
-
```
|
|
831
|
+
Apache-2.0
|