facturas 0.10.0 → 0.11.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 +98 -850
- package/bin/facturas.mjs +4 -0
- package/dist/{chunk-QHHLHSBW.mjs → chunk-5KCTRQJX.mjs} +77 -165
- package/dist/chunk-5KCTRQJX.mjs.map +1 -0
- package/dist/{chunk-76WU5BVI.mjs → chunk-GX5ERXT6.mjs} +1 -1
- package/dist/chunk-GX5ERXT6.mjs.map +1 -0
- package/dist/chunk-LOWV7IF6.mjs +3758 -0
- package/dist/chunk-LOWV7IF6.mjs.map +1 -0
- package/dist/chunk-LR4M2AMG.mjs +169 -0
- package/dist/chunk-LR4M2AMG.mjs.map +1 -0
- package/dist/cli.d.ts +49 -0
- package/dist/cli.mjs +2316 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/client-BZxeScuK.d.ts +521 -0
- package/dist/constants.d.ts +1 -1
- package/dist/constants.mjs +1 -1
- package/dist/{wsfe-DDY9E1GJ.d.ts → decimal-B0HtrMFY.d.ts} +9 -42
- package/dist/errors.d.ts +2 -2
- package/dist/{fiscal-evidence-DBzakqBE.d.ts → fiscal-evidence-B1Qe6XMg.d.ts} +2 -2
- package/dist/{index-CVGQyyYM.d.ts → index-D8LGFvxV.d.ts} +1 -1
- package/dist/index.d.ts +11 -285
- package/dist/index.mjs +19 -2491
- package/dist/index.mjs.map +1 -1
- package/dist/padron.d.ts +2 -2
- package/dist/{types-BiznC1-X.d.ts → types-r2nf55gh.d.ts} +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/wsfe.d.ts +45 -4
- package/dist/wsfe.mjs +5 -3
- package/dist/wsmtxca.d.ts +3 -3
- package/package.json +5 -1
- package/dist/chunk-76WU5BVI.mjs.map +0 -1
- package/dist/chunk-QHHLHSBW.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -4,889 +4,137 @@
|
|
|
4
4
|
[](https://github.com/LaPyme/facturas/actions/workflows/ci.yml)
|
|
5
5
|
[](https://github.com/LaPyme/facturas/blob/main/LICENSE)
|
|
6
6
|
|
|
7
|
-
Node.js
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
- **
|
|
11
|
-
- **
|
|
12
|
-
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
15
|
-
|
|
16
|
-
|
|
7
|
+
SDK de Node.js para ARCA / AFIP: facturas, notas de crédito y Padrón, con
|
|
8
|
+
integración directa a WSFE y WSMTXCA.
|
|
9
|
+
|
|
10
|
+
- **Solo ESM**, Node.js **>= 20**
|
|
11
|
+
- **CLI incluido**: `npx facturas init` genera la clave y el CSR, y
|
|
12
|
+
`npx facturas check` nombra la capa de ARCA que falla
|
|
13
|
+
- **Integración directa con ARCA**, sin proxy ni dependencia alojada
|
|
14
|
+
- **Login WSAA resuelto**: caché de tickets en memoria, stores de sesión
|
|
15
|
+
durables opcionales, deduplicación de logins en vuelo y recuperación de
|
|
16
|
+
`coe.alreadyAuthenticated`
|
|
17
|
+
- **API pública en TypeScript estricto**, con nombres al estilo JS mapeados a
|
|
18
|
+
SOAP internamente
|
|
19
|
+
- **Datos de referencia comunes de ARCA** exportados como constantes, para que
|
|
20
|
+
los ejemplos y tu código no necesiten números mágicos
|
|
21
|
+
- **Ejemplos copiables**, escritos para que los lean personas y agentes de
|
|
22
|
+
código
|
|
23
|
+
|
|
24
|
+
La documentación está en castellano, en [docs/](./docs/README.md).
|
|
25
|
+
[English summary](./docs/en/README.md).
|
|
26
|
+
|
|
27
|
+
## Instalación
|
|
17
28
|
|
|
18
29
|
```bash
|
|
19
30
|
pnpm add facturas
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
```bash
|
|
31
|
+
# o
|
|
23
32
|
npm install facturas
|
|
24
33
|
```
|
|
25
34
|
|
|
26
|
-
##
|
|
35
|
+
## Emití tu primera factura
|
|
27
36
|
|
|
28
|
-
|
|
29
|
-
|
|
37
|
+
Definí las [variables de entorno](./docs/configuracion.md#variables-de-entorno)
|
|
38
|
+
con tu CUIT, certificado y clave. No hace falta nada más: ni base de datos, ni
|
|
39
|
+
tabla, ni servicio externo.
|
|
30
40
|
|
|
31
41
|
```ts
|
|
32
|
-
import { createArcaClient
|
|
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
|
-
### Check before you send
|
|
42
|
+
import { createArcaClient } from "facturas";
|
|
63
43
|
|
|
64
|
-
|
|
65
|
-
store, no WSAA, no SOAP and no next-number read. It throws the same input
|
|
66
|
-
errors `issue()` throws before its first call, so an input that previews
|
|
67
|
-
cleanly raises no new local error at issuance.
|
|
44
|
+
const arca = createArcaClient();
|
|
68
45
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
issuer: "monotributo" as const,
|
|
46
|
+
const factura = await arca.issue({
|
|
47
|
+
issuer: "monotributo",
|
|
72
48
|
salesPoint: 3,
|
|
73
|
-
to: { condition: "consumidor_final"
|
|
74
|
-
items: [{ amount: 150_000 }],
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
const previsualizacion = arca.preview(input);
|
|
78
|
-
if (previsualizacion.amounts.sentTotal !== venta.totalEnCentavos) {
|
|
79
|
-
throw new Error("The derived invoice does not match the sale total.");
|
|
80
|
-
}
|
|
81
|
-
const factura = await arca.issue(input, { idempotencyKey: venta.id });
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
`preview()` is synchronous and returns the derived `voucherClass`,
|
|
85
|
-
`voucherType`, the same `amounts` an authorized outcome reports, and `request`,
|
|
86
|
-
the exact `WsfeVoucherInput` that `issue()` would send. The voucher number is
|
|
87
|
-
absent because it is only known when the number is reserved at issuance.
|
|
88
|
-
Previewing a credit note needs the original and is not offered.
|
|
89
|
-
|
|
90
|
-
## Credit notes
|
|
91
|
-
|
|
92
|
-
ARCA has no cancellation. A correction is a credit note, and both modes of
|
|
93
|
-
`issueCreditNote()` write a real fiscal document that stays in ARCA's records.
|
|
94
|
-
|
|
95
|
-
A credit note names the invoice it corrects. The SDK does not offer associated
|
|
96
|
-
periods; use `wsfe.issue()` if you need one.
|
|
97
|
-
|
|
98
|
-
The common case is partial: a refund or a price correction that credits chosen
|
|
99
|
-
lines rather than the whole invoice.
|
|
100
|
-
|
|
101
|
-
```ts
|
|
102
|
-
const nota = await arca.issueCreditNote(
|
|
103
|
-
{
|
|
104
|
-
for: { salesPoint: 3, voucherType: 11, number: 41 },
|
|
105
|
-
items: [{ amount: 50_000 }], // ARS 500,00 of an ARS 1.500,00 invoice.
|
|
106
|
-
},
|
|
107
|
-
{ idempotencyKey: `nc:${devolucion.id}` },
|
|
108
|
-
);
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
`all: true` credits the whole original instead, mirroring its amounts and VAT
|
|
112
|
-
rates line by line:
|
|
113
|
-
|
|
114
|
-
```ts
|
|
115
|
-
const notaTotal = await arca.issueCreditNote(
|
|
116
|
-
{ for: { salesPoint: 3, voucherType: 11, number: 41 }, all: true },
|
|
117
|
-
{ idempotencyKey: `nc-total:${venta.id}` },
|
|
118
|
-
);
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
The mode is explicit and required: an input with neither `items` nor
|
|
122
|
-
`all: true`, or with both, is rejected before any I/O, so a forgotten field
|
|
123
|
-
cannot credit the whole invoice.
|
|
124
|
-
|
|
125
|
-
From the original the SDK takes the class and therefore the note type
|
|
126
|
-
(1 → 3, 6 → 8, 11 → 13), the receiver document type, number and VAT condition,
|
|
127
|
-
the currency and exchange rate, the concept and, for concepts 2 and 3, the
|
|
128
|
-
service dates with the due date raised to the note date. The caller provides
|
|
129
|
-
`for`, the mode (`items`, with an optional `total`, or `all: true`) and, at
|
|
130
|
-
most, the note's own `salesPoint`, which defaults to the original's, and
|
|
131
|
-
`date`, which defaults to today in Buenos Aires. There is no `issuer`, `to` or
|
|
132
|
-
`currency` field: a note to a different receiver or in a different currency is
|
|
133
|
-
a different document and belongs to the exact layer.
|
|
134
|
-
|
|
135
|
-
Item shapes follow the original's class. A class C note accepts `{ amount }`
|
|
136
|
-
items; class A and B notes accept `{ gross | net, vat }` items with the same
|
|
137
|
-
rates and reconciliation as `issue()`. The class is evidence from the original,
|
|
138
|
-
so a shape that contradicts it is rejected after the single lookup of the
|
|
139
|
-
original and before any write.
|
|
140
|
-
|
|
141
|
-
The note may not exceed the original's total. The SDK does not track earlier
|
|
142
|
-
notes against an original; preventing several notes from adding up beyond the
|
|
143
|
-
invoice is the application's job.
|
|
144
|
-
|
|
145
|
-
Only authorized invoices of type 1, 6 or 11 in ARS or USD can be corrected.
|
|
146
|
-
Originals with tributes, optional fields, buyers, activities or an associated
|
|
147
|
-
period, debit-note targets, and FCE all require the exact API.
|
|
148
|
-
|
|
149
|
-
`idempotencyKey` and `include` work exactly as for `issue()`; give the note its
|
|
150
|
-
own stable key, such as `nc:${devolucion.id}`. A keyed replay consults only the
|
|
151
|
-
reserved note and reports `amounts` from the stored request, so on that path
|
|
152
|
-
`computedTotal` equals `sentTotal` and `vatAdjustment` is `0`.
|
|
153
|
-
|
|
154
|
-
A production smoke test is an ARS 1 invoice followed by a full note. **Both
|
|
155
|
-
documents are real and remain in ARCA's records.** The note is a separate
|
|
156
|
-
operation; a failure leaves the invoice outstanding. Match `issuer` to your
|
|
157
|
-
actual tax condition.
|
|
158
|
-
|
|
159
|
-
## Stores
|
|
160
|
-
|
|
161
|
-
One `store` persists WSAA tickets and immutable invoice/credit-note reservations.
|
|
162
|
-
The SDK adds no database or Redis driver dependency. Store failures throw
|
|
163
|
-
`ArcaConfigurationError` with their cause attached and a content-free message.
|
|
164
|
-
|
|
165
|
-
### Postgres
|
|
166
|
-
|
|
167
|
-
Use your application's existing client. Neon, Supabase Postgres, Vercel Postgres,
|
|
168
|
-
`pg` and `postgres` can provide the parameterized query function. Results can
|
|
169
|
-
be an array of rows or `{ rows }`. With `postgres`, adapt `sql.unsafe(text, params)`.
|
|
170
|
-
Provision the default table once:
|
|
171
|
-
|
|
172
|
-
```sql
|
|
173
|
-
CREATE TABLE arca_store (
|
|
174
|
-
key text PRIMARY KEY,
|
|
175
|
-
value text NOT NULL,
|
|
176
|
-
updated_at timestamptz NOT NULL DEFAULT now()
|
|
177
|
-
);
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
```ts
|
|
181
|
-
const store = createPostgresStore({
|
|
182
|
-
query: (text, params) => sql.query(text, params),
|
|
183
|
-
table: "arca_store", // Optional simple SQL identifier.
|
|
49
|
+
to: { condition: "consumidor_final" },
|
|
50
|
+
items: [{ amount: 150_000 }], // ARS 1.500,00 en centavos
|
|
184
51
|
});
|
|
185
52
|
```
|
|
186
53
|
|
|
187
|
-
|
|
188
|
-
The adapter does not create the table or hold a database lock.
|
|
189
|
-
|
|
190
|
-
### Redis
|
|
191
|
-
|
|
192
|
-
```ts
|
|
193
|
-
import { createRedisStore } from "facturas";
|
|
194
|
-
const store = createRedisStore(redis);
|
|
195
|
-
// Optional override: createRedisStore(redis, { flavor: "upstash" });
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
A client with `call` uses ioredis `SET key value NX`; otherwise the adapter
|
|
199
|
-
uses Upstash `set(key, value, { nx: true })`. Use a durable Redis deployment
|
|
200
|
-
without eviction of reservation keys. Neither flavor applies a TTL or lock.
|
|
201
|
-
|
|
202
|
-
### Files
|
|
203
|
-
|
|
204
|
-
```ts
|
|
205
|
-
import { createFileStore } from "facturas";
|
|
206
|
-
const store = createFileStore("/private/durable/arca");
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
Use a private durable volume on a single server. Keys are hashed to filenames;
|
|
210
|
-
creation is exclusive, replacement uses a temporary file and rename. Files
|
|
211
|
-
have mode `0600`, new directories `0700`. No process lock is provided.
|
|
212
|
-
|
|
213
|
-
### Memory
|
|
214
|
-
|
|
215
|
-
```ts
|
|
216
|
-
import { createMemoryStore } from "facturas";
|
|
217
|
-
const store = createMemoryStore();
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
For tests and examples. It serializes ticket refreshes within the shared object,
|
|
221
|
-
but **does not survive a restart**. It does not make serverless retries durable.
|
|
222
|
-
|
|
223
|
-
### Custom store and record lifetime
|
|
224
|
-
|
|
225
|
-
```ts
|
|
226
|
-
type ArcaStore = {
|
|
227
|
-
get(key: string): Promise<string | null>;
|
|
228
|
-
set(key: string, value: string): Promise<void>;
|
|
229
|
-
add(key: string, value: string): Promise<boolean>;
|
|
230
|
-
delete?(key: string): Promise<void>;
|
|
231
|
-
withLock?<T>(key: string, fn: () => Promise<T>): Promise<T>;
|
|
232
|
-
};
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
`add` must atomically return false without changing an existing value. Optional
|
|
236
|
-
`withLock` coordinates WSAA ticket refreshes. Explicit `wsaaSessionStore` wins
|
|
237
|
-
for tickets when both options are provided.
|
|
238
|
-
|
|
239
|
-
Keys use `arca:v1:wsaa:{environment}:{service}:{fingerprint}` and
|
|
240
|
-
`arca:v1:attempt:{environment}:{taxId}:{idempotencyKey}`. Reservation records
|
|
241
|
-
contain the input hash, operation, reserved coordinates and exact sent input.
|
|
242
|
-
They contain fiscal/customer data: restrict access and protect backups.
|
|
243
|
-
|
|
244
|
-
**Do not prune, expire or rewrite reservation records.** The SDK only creates
|
|
245
|
-
them, never saves outcomes over them, and always consults ARCA on replay.
|
|
246
|
-
Deleting a reservation can let a later retry issue another invoice.
|
|
247
|
-
|
|
248
|
-
## Invoice inputs
|
|
249
|
-
|
|
250
|
-
The issuer is your legal assertion on each call; the SDK never infers it from
|
|
251
|
-
items or Padrón. An RI issuer produces A for RI or Monotributo receivers and B
|
|
252
|
-
for the other supported conditions. Monotributo, Exento and No Alcanzado issuers
|
|
253
|
-
produce C and use `items: [{ amount: 10_000 }]`. ARCA validates actual eligibility.
|
|
254
|
-
`to` is the fiscal receiver (the exact layer's receiver document/condition fields),
|
|
255
|
-
not a customer record.
|
|
54
|
+
Tratá siempre los cuatro resultados:
|
|
256
55
|
|
|
257
|
-
|
|
258
|
-
item and one of `0 | 2.5 | 5 | 10.5 | 21 | 27 | "exempt" | "untaxed"` for `vat`.
|
|
259
|
-
Numeric zero is a VAT rate; exempt and untaxed amounts have separate fiscal
|
|
260
|
-
fields. Items are grouped by rate before Round Half Even rounding.
|
|
261
|
-
|
|
262
|
-
`total`, when supplied, asserts the sent total. The SDK adjusts header VAT only
|
|
263
|
-
within one cent per emitted numeric rate, while keeping VAT non-negative.
|
|
264
|
-
Class C totals must match exactly. Authorized results expose `computedTotal`,
|
|
265
|
-
`sentTotal` and `vatAdjustment` in `voucher.amounts`, all in minor units.
|
|
266
|
-
|
|
267
|
-
The defaults are today's date in Buenos Aires, goods (concept 1), and `ARS` at
|
|
268
|
-
exchange rate `1`. Use `currency: "USD"` with a positive decimal-string
|
|
269
|
-
`exchangeRate`, or `service: { from, to, dueDate }` for concept 2. Dates accept
|
|
270
|
-
`YYYY-MM-DD` or `YYYYMMDD`; service end must be on or after its start and the payment due
|
|
271
|
-
date must be on or after the invoice date.
|
|
272
|
-
|
|
273
|
-
Non-final-consumer receivers require an 11-digit `cuit`. A final consumer accepts
|
|
274
|
-
one `cuit` or `dni`, or neither below the identification threshold. At or above
|
|
275
|
-
ARS 10,000,000 (including USD converted at the supplied rate), identification is
|
|
276
|
-
required under [RG 5866/2026](https://www.argentina.gob.ar/normativa/nacional/norma-427092/texto).
|
|
277
|
-
When the customer requests a CUIT for an income-tax deduction, supply it regardless
|
|
278
|
-
of amount. Document shape checks do not verify provider registration.
|
|
279
|
-
|
|
280
|
-
### Facade fiscal contract
|
|
281
|
-
|
|
282
|
-
Without a key, a call reads one next number and authorizes once, with at most
|
|
283
|
-
one identity lookup after an indeterminate response. This is the v0.8 behavior.
|
|
284
|
-
A first keyed call reserves that number before writing. A keyed replay looks
|
|
285
|
-
up the reservation: only `not_found` allows one authorization of the stored
|
|
286
|
-
number. A found voucher is never resubmitted. Indeterminate writes and keyed
|
|
287
|
-
10016 rejections can add one lookup; a 10016 without a complete match remains
|
|
288
|
-
rejected. `issueCreditNote()` adds the original's lookup only when creating a new
|
|
289
|
-
reservation.
|
|
290
|
-
|
|
291
|
-
| Outcome | Meaning and caller action |
|
|
56
|
+
| Resultado | Significado y acción del llamador |
|
|
292
57
|
| --- | --- |
|
|
293
|
-
| `authorized` |
|
|
294
|
-
| `rejected` |
|
|
295
|
-
| `indeterminate` |
|
|
296
|
-
| `conflict` |
|
|
297
|
-
|
|
298
|
-
The second argument accepts `idempotencyKey`, `representedTaxId`, `forceRefresh`
|
|
299
|
-
and `include: { raw: true, exactInput: true }`. Outcomes are raw-free by default;
|
|
300
|
-
`sent` is included only on authorized outcomes when requested. Replay without
|
|
301
|
-
an observed write outcome uses an indeterminate attempt with
|
|
302
|
-
`reason: "incomplete_response"`; the lookup provides the authorization evidence.
|
|
303
|
-
|
|
304
|
-
The identity matcher compares coordinates, date, concept, receiver, currency,
|
|
305
|
-
all header amounts, VAT rates, service dates, and note associations. Missing
|
|
306
|
-
fields stay incomplete; differences are conflicts. Unsupported exact extensions
|
|
307
|
-
remain incomplete. Use exact APIs for tributes, FCE, other receiver conditions,
|
|
308
|
-
same-currency foreign cancellation and WSMTXCA.
|
|
309
|
-
|
|
310
|
-
## Exact control
|
|
311
|
-
|
|
312
|
-
This example mirrors [examples/factura-b-consumidor-final.ts](./examples/factura-b-consumidor-final.ts).
|
|
313
|
-
|
|
314
|
-
```ts
|
|
315
|
-
import { buildFacturaB, createArcaClient } from "facturas";
|
|
316
|
-
import {
|
|
317
|
-
ARCA_CONCEPT_TYPES,
|
|
318
|
-
ARCA_DOCUMENT_TYPES,
|
|
319
|
-
ARCA_RECEIVER_VAT_CONDITIONS,
|
|
320
|
-
} from "facturas/constants";
|
|
321
|
-
|
|
322
|
-
const client = createArcaClient({
|
|
323
|
-
taxId: "20123456789",
|
|
324
|
-
certificatePem:
|
|
325
|
-
"-----BEGIN CERTIFICATE-----\nREPLACE_WITH_YOUR_CERTIFICATE\n-----END CERTIFICATE-----",
|
|
326
|
-
privateKeyPem:
|
|
327
|
-
"-----BEGIN PRIVATE KEY-----\nREPLACE_WITH_YOUR_PRIVATE_KEY\n-----END PRIVATE KEY-----",
|
|
328
|
-
environment: "test",
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
const data = buildFacturaB({
|
|
332
|
-
salesPoint: 1,
|
|
333
|
-
concept: ARCA_CONCEPT_TYPES.PRODUCTOS,
|
|
334
|
-
documentType: ARCA_DOCUMENT_TYPES.CONSUMIDOR_FINAL,
|
|
335
|
-
documentNumber: 0,
|
|
336
|
-
receiverVatConditionId: ARCA_RECEIVER_VAT_CONDITIONS.CONSUMIDOR_FINAL,
|
|
337
|
-
// Deterministic example date; use an ARCA-allowed current date in homologation.
|
|
338
|
-
voucherDate: "2026-09-02",
|
|
339
|
-
taxableAmount: 10_000, // Integer minor units: ARS 100.00.
|
|
340
|
-
vatRate: 21,
|
|
341
|
-
// currency is omitted, so the builder defaults to ISO ARS.
|
|
342
|
-
});
|
|
343
|
-
|
|
344
|
-
// Exact layer: reserve the number yourself, then issue it exactly once.
|
|
345
|
-
const voucherNumber = await client.wsfe.getNextVoucherNumber({
|
|
346
|
-
salesPoint: data.salesPoint,
|
|
347
|
-
voucherType: data.voucherType,
|
|
348
|
-
});
|
|
349
|
-
const issued = await client.wsfe.issue({ voucherNumber, data });
|
|
350
|
-
|
|
351
|
-
if (issued.kind === "authorized") {
|
|
352
|
-
console.log(issued.cae, issued.caeExpiry, issued.voucherNumber);
|
|
353
|
-
}
|
|
354
|
-
```
|
|
355
|
-
|
|
356
|
-
`buildFacturaB()` derives the Factura B type, net amount, IVA detail, IVA
|
|
357
|
-
amount, zero-value fields, and total without floating-point tax arithmetic.
|
|
358
|
-
`buildFacturaC()` separately builds the zero-IVA Factura C shape. Both builders
|
|
359
|
-
accept integer currency minor units and support ISO `ARS` (the default) and
|
|
360
|
-
`USD`. Factura B requires a positive `taxableAmount`; when `vatRate` is
|
|
361
|
-
positive, the amount must produce at least one currency minor unit of IVA after
|
|
362
|
-
rounding. IVA uses the Round Half Even criterion documented by ARCA, so an
|
|
363
|
-
exact half-cent is rounded to the even cent.
|
|
364
|
-
|
|
365
|
-
For a USD invoice, pass a decimal-string exchange rate:
|
|
366
|
-
|
|
367
|
-
```ts
|
|
368
|
-
const usdData = buildFacturaB({
|
|
369
|
-
salesPoint: 1,
|
|
370
|
-
concept: ARCA_CONCEPT_TYPES.PRODUCTOS,
|
|
371
|
-
documentType: ARCA_DOCUMENT_TYPES.CONSUMIDOR_FINAL,
|
|
372
|
-
documentNumber: 0,
|
|
373
|
-
receiverVatConditionId: ARCA_RECEIVER_VAT_CONDITIONS.CONSUMIDOR_FINAL,
|
|
374
|
-
voucherDate: "2026-09-02",
|
|
375
|
-
taxableAmount: 10_000, // USD 100.00.
|
|
376
|
-
vatRate: 21,
|
|
377
|
-
currency: "USD",
|
|
378
|
-
exchangeRate: "1095.500000",
|
|
379
|
-
});
|
|
380
|
-
```
|
|
381
|
-
|
|
382
|
-
## What You Can Do Today
|
|
383
|
-
|
|
384
|
-
### WSFE
|
|
385
|
-
|
|
386
|
-
- Build A/B/C invoices from explicit assertions with the facade above
|
|
387
|
-
- Issue invoices and credit notes with the existing exact WSFE methods
|
|
388
|
-
- Query voucher numbers and voucher details
|
|
389
|
-
- Read ARCA catalogs with methods like `getVoucherTypes()` and `getVatRates()`
|
|
390
|
-
- Check backend health with `getServerStatus()`
|
|
391
|
-
|
|
392
|
-
### Padrón
|
|
393
|
-
|
|
394
|
-
- Look up taxpayer data with `client.padron.getTaxpayerDetails(...)`
|
|
395
|
-
- Resolve CUITs from document numbers with `client.padron.getTaxIdByDocument(...)`
|
|
396
|
-
|
|
397
|
-
### WSMTXCA
|
|
398
|
-
|
|
399
|
-
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.
|
|
400
|
-
|
|
401
|
-
## Exact issuance and recovery evidence
|
|
402
|
-
|
|
403
|
-
`client.issue()` derives the WSFE request, reserves the number and recovers
|
|
404
|
-
after a crash for you. When you need something it does not derive (tributes,
|
|
405
|
-
FCE, an associated period, a note in another currency or to another receiver)
|
|
406
|
-
or your application owns the
|
|
407
|
-
numbering, use the exact layer: `client.wsfe.issue(...)` sends one
|
|
408
|
-
FECAESolicitar for a caller-owned, durably reserved voucher number and tells
|
|
409
|
-
you whether that exact attempt was authorized, rejected, or left
|
|
410
|
-
indeterminate. It preserves every structured error and observation with its
|
|
411
|
-
service, operation, code, source, and result level.
|
|
412
|
-
|
|
413
|
-
```ts
|
|
414
|
-
const outcome = await client.wsfe.issue({
|
|
415
|
-
voucherNumber: reservedVoucherNumber,
|
|
416
|
-
data,
|
|
417
|
-
});
|
|
418
|
-
|
|
419
|
-
if (outcome.kind === "authorized") {
|
|
420
|
-
console.log(outcome.cae, outcome.voucherNumber);
|
|
421
|
-
} else if (outcome.kind === "rejected") {
|
|
422
|
-
console.error(outcome.errors, outcome.observations);
|
|
423
|
-
} else {
|
|
424
|
-
if (outcome.reason === "authentication_rejected") {
|
|
425
|
-
console.error(outcome.authentication?.reason);
|
|
426
|
-
}
|
|
427
|
-
// Consult the same number before any new authorization attempt.
|
|
428
|
-
const lookup = await client.wsfe.lookupVoucher({
|
|
429
|
-
number: reservedVoucherNumber,
|
|
430
|
-
salesPoint: data.salesPoint,
|
|
431
|
-
voucherType: data.voucherType,
|
|
432
|
-
});
|
|
433
|
-
console.log(lookup.kind);
|
|
434
|
-
}
|
|
435
|
-
```
|
|
436
|
-
|
|
437
|
-
`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.
|
|
438
|
-
|
|
439
|
-
Authenticated read, catalog, and lookup operations may repeat once with a
|
|
440
|
-
forced credential refresh after an explicit typed authentication rejection.
|
|
441
|
-
Passing `forceRefresh: true` disables any further authentication recovery
|
|
442
|
-
attempt.
|
|
443
|
-
|
|
444
|
-
Exact lookup absence is operation-specific:
|
|
445
|
-
|
|
446
|
-
- WSFE `FECompConsultar` code 602 returns `not_found`.
|
|
447
|
-
- WSMTXCA `consultarComprobante` code 1503 returns `not_found`.
|
|
448
|
-
- WSMTXCA `consultarUltimoComprobanteAutorizado` code 1502 returns voucher number `0`.
|
|
449
|
-
- WSMTXCA code 602 is not exact-voucher absence and remains an error.
|
|
450
|
-
|
|
451
|
-
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.
|
|
452
|
-
|
|
453
|
-
For tributes, notes, FCE, or other advanced cases, use the exact
|
|
454
|
-
`WsfeVoucherInput` escape hatch. It also continues to support exemptions,
|
|
455
|
-
non-taxable amounts and multiple IVA rates. Exact
|
|
456
|
-
amounts remain major-unit numbers, are validated locally, and are serialized as
|
|
457
|
-
canonical two-decimal strings:
|
|
458
|
-
|
|
459
|
-
```ts
|
|
460
|
-
import type { WsfeVoucherInput } from "facturas/wsfe";
|
|
461
|
-
import {
|
|
462
|
-
ARCA_CONCEPT_TYPES,
|
|
463
|
-
ARCA_CURRENCY_IDS,
|
|
464
|
-
ARCA_DOCUMENT_TYPES,
|
|
465
|
-
ARCA_RECEIVER_VAT_CONDITIONS,
|
|
466
|
-
ARCA_VAT_RATES,
|
|
467
|
-
ARCA_VOUCHER_TYPES,
|
|
468
|
-
} from "facturas/constants";
|
|
469
|
-
|
|
470
|
-
const exactData: WsfeVoucherInput = {
|
|
471
|
-
salesPoint: 1,
|
|
472
|
-
voucherType: ARCA_VOUCHER_TYPES.FACTURA_B,
|
|
473
|
-
concept: ARCA_CONCEPT_TYPES.PRODUCTOS,
|
|
474
|
-
documentType: ARCA_DOCUMENT_TYPES.CONSUMIDOR_FINAL,
|
|
475
|
-
documentNumber: 0,
|
|
476
|
-
receiverVatConditionId: ARCA_RECEIVER_VAT_CONDITIONS.CONSUMIDOR_FINAL,
|
|
477
|
-
voucherDate: "2026-09-02",
|
|
478
|
-
totalAmount: 121,
|
|
479
|
-
nonTaxableAmount: 0,
|
|
480
|
-
netAmount: 100,
|
|
481
|
-
exemptAmount: 0,
|
|
482
|
-
taxAmount: 0,
|
|
483
|
-
vatAmount: 21,
|
|
484
|
-
currencyId: ARCA_CURRENCY_IDS.ARS,
|
|
485
|
-
exchangeRate: "1",
|
|
486
|
-
vatRates: [{ id: ARCA_VAT_RATES.IVA_21, baseAmount: 100, amount: 21 }],
|
|
487
|
-
};
|
|
488
|
-
```
|
|
489
|
-
|
|
490
|
-
Exact inputs and live catalog responses use ARCA protocol identifiers such as
|
|
491
|
-
`PES` and `DOL`; the facade and high-level builders accept ISO `ARS` and `USD`.
|
|
492
|
-
|
|
493
|
-
### Migrating amount and currency inputs
|
|
494
|
-
|
|
495
|
-
Applications that previously rounded decimal major-unit values and translated
|
|
496
|
-
currencies to ARCA IDs can move that provider-boundary work into a builder:
|
|
497
|
-
|
|
498
|
-
```ts
|
|
499
|
-
// Before: caller-owned decimal rounding and provider vocabulary.
|
|
500
|
-
const exactAmount = Number(sourceAmount.toFixed(2));
|
|
501
|
-
const exactCurrencyId = sourceCurrency === "ARS" ? "PES" : "DOL";
|
|
502
|
-
|
|
503
|
-
// After: integer minor units and ISO currency input.
|
|
504
|
-
const data = buildFacturaB({
|
|
505
|
-
salesPoint: 1,
|
|
506
|
-
concept: ARCA_CONCEPT_TYPES.PRODUCTOS,
|
|
507
|
-
documentType: ARCA_DOCUMENT_TYPES.CONSUMIDOR_FINAL,
|
|
508
|
-
documentNumber: 0,
|
|
509
|
-
receiverVatConditionId: ARCA_RECEIVER_VAT_CONDITIONS.CONSUMIDOR_FINAL,
|
|
510
|
-
voucherDate: "2026-09-02",
|
|
511
|
-
taxableAmount: 10_000, // 100.00 in the selected currency.
|
|
512
|
-
vatRate: 21,
|
|
513
|
-
currency: "USD",
|
|
514
|
-
exchangeRate: "1095.5",
|
|
515
|
-
});
|
|
516
|
-
```
|
|
517
|
-
|
|
518
|
-
Keep using `WsfeVoucherInput` when you need advanced exact fields. Its amounts
|
|
519
|
-
remain decimal major-unit values and its `currencyId` remains an ARCA ID.
|
|
520
|
-
|
|
521
|
-
## Examples
|
|
522
|
-
|
|
523
|
-
- [Keyed invoice](./examples/issue-invoice.ts)
|
|
524
|
-
- [Preview before issuing](./examples/preview.ts)
|
|
525
|
-
- [Partial credit note](./examples/nota-de-credito-parcial.ts)
|
|
526
|
-
- [Full credit note](./examples/nota-de-credito-total.ts)
|
|
527
|
-
|
|
528
|
-
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.
|
|
529
|
-
Issuance examples use deterministic dates for compilation; replace them with an
|
|
530
|
-
ARCA-allowed current date before a homologation request.
|
|
531
|
-
|
|
532
|
-
- [factura-b-consumidor-final.ts](./examples/factura-b-consumidor-final.ts)
|
|
533
|
-
- [factura-a-responsable-inscripto.ts](./examples/factura-a-responsable-inscripto.ts)
|
|
534
|
-
- [nota-de-credito-asociada.ts](./examples/nota-de-credito-asociada.ts)
|
|
535
|
-
- [factura-servicios-con-periodo.ts](./examples/factura-servicios-con-periodo.ts)
|
|
536
|
-
- [consultar-comprobante.ts](./examples/consultar-comprobante.ts)
|
|
537
|
-
- [consultar-contribuyente.ts](./examples/consultar-contribuyente.ts)
|
|
538
|
-
|
|
539
|
-
## Manual Setup Reality
|
|
540
|
-
|
|
541
|
-
This package does **not** provision ARCA credentials for you. You still need to do the official certificate and service setup outside the SDK.
|
|
542
|
-
|
|
543
|
-
Before using the SDK:
|
|
544
|
-
|
|
545
|
-
1. Obtain a valid CUIT.
|
|
546
|
-
2. Generate or receive a certificate and matching private key in PEM format.
|
|
547
|
-
3. Authorize the certificate for the target service and environment.
|
|
548
|
-
4. Start with `environment: "test"` and move to production only after end-to-end validation.
|
|
58
|
+
| `authorized` | Guardá el comprobante y el CAE. `recoveredByMatch: true` significa que el input guardado coincidió con la identidad consultada; prueba consistencia, no autoría. |
|
|
59
|
+
| `rejected` | Revisá los `issues` de ARCA. Una clave queda ligada a su input incluso después de un rechazo. |
|
|
60
|
+
| `indeterminate` | Conservá el número y la evidencia. Conciliá o repetí el input idéntico con su clave existente. |
|
|
61
|
+
| `conflict` | Hay otro comprobante en el número reservado. Detené el flujo e investigá. |
|
|
549
62
|
|
|
550
|
-
|
|
63
|
+
El paso a paso está en [Inicio rápido](./docs/inicio-rapido.md); el detalle, en
|
|
64
|
+
[Facturas](./docs/facturas.md).
|
|
551
65
|
|
|
552
|
-
|
|
553
|
-
- [Certificates for testing / homologation](https://www.afip.gob.ar/ws/documentacion/certificados.asp)
|
|
554
|
-
- [WSAA developer manual](https://www.afip.gob.ar/ws/WSAA/WSAAmanualDev.pdf)
|
|
555
|
-
- [WSASS service onboarding](https://www.afip.gob.ar/ws/WSASS/WSASS_como_adherirse.pdf)
|
|
556
|
-
- [WSFE developer manual](https://www.afip.gob.ar/ws/documentacion/manuales/manual-desarrollador-ARCA-COMPG.pdf)
|
|
66
|
+
## Reintentos seguros
|
|
557
67
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
68
|
+
Recomendado en toda aplicación real, opcional para empezar. Sin
|
|
69
|
+
`idempotencyKey`, un reintento después de una caída puede emitir la factura dos
|
|
70
|
+
veces. Con un `store` y el ID estable de la venta como clave, el reintento
|
|
71
|
+
consulta el número reservado y nunca vuelve a emitir.
|
|
561
72
|
|
|
562
73
|
```ts
|
|
563
|
-
import {
|
|
564
|
-
|
|
565
|
-
ARCA_CURRENCY_IDS,
|
|
566
|
-
ARCA_CURRENCIES,
|
|
567
|
-
ARCA_DOCUMENT_TYPES,
|
|
568
|
-
ARCA_RECEIVER_VAT_CONDITIONS,
|
|
569
|
-
ARCA_VAT_RATES,
|
|
570
|
-
ARCA_VOUCHER_TYPES,
|
|
571
|
-
ISO_CURRENCIES,
|
|
572
|
-
} from "facturas/constants";
|
|
573
|
-
|
|
574
|
-
ARCA_VOUCHER_TYPES.FACTURA_A; // 1
|
|
575
|
-
ARCA_VOUCHER_TYPES.FACTURA_B; // 6
|
|
576
|
-
ARCA_DOCUMENT_TYPES.CUIT; // 80
|
|
577
|
-
ARCA_DOCUMENT_TYPES.DNI; // 96
|
|
578
|
-
ARCA_DOCUMENT_TYPES.CONSUMIDOR_FINAL; // 99
|
|
579
|
-
ARCA_RECEIVER_VAT_CONDITIONS.RESPONSABLE_INSCRIPTO; // 1
|
|
580
|
-
ARCA_RECEIVER_VAT_CONDITIONS.CONSUMIDOR_FINAL; // 5
|
|
581
|
-
ARCA_CONCEPT_TYPES.SERVICIOS; // 2
|
|
582
|
-
ARCA_VAT_RATES.IVA_21; // 5
|
|
583
|
-
ISO_CURRENCIES.ARS; // "ARS"
|
|
584
|
-
ARCA_CURRENCY_IDS.USD; // "DOL"
|
|
585
|
-
ARCA_CURRENCIES.PES; // "PES"
|
|
586
|
-
ARCA_CURRENCIES.DOL; // "DOL"
|
|
587
|
-
```
|
|
588
|
-
|
|
589
|
-
The constants cover the most common values used by the README and examples:
|
|
590
|
-
|
|
591
|
-
- voucher types for invoice A/B/C, debit note A/B/C, and credit note A/B/C
|
|
592
|
-
- document types for CUIT, DNI, and final consumers
|
|
593
|
-
- common receiver IVA conditions, subject to voucher-class and live-catalog rules
|
|
594
|
-
- concept types for products, services, and products + services
|
|
595
|
-
- IVA rates for `0`, `2.5`, `5`, `10.5`, `21`, and `27`
|
|
596
|
-
- builder ISO currencies `ARS` and `USD`, with explicit ARCA mappings to `PES`
|
|
597
|
-
and `DOL`
|
|
598
|
-
|
|
599
|
-
`ARCA_CURRENCIES` remains a deprecated compatibility alias with its existing
|
|
600
|
-
`PES` and `DOL` values. If you need broader catalogs at runtime, WSFE methods
|
|
601
|
-
such as `getVoucherTypes()`, `getDocumentTypes()`, `getCurrencyTypes()`, and
|
|
602
|
-
`getVatRates()` are still available. `getCurrencyTypes()` returns live ARCA
|
|
603
|
-
identifiers, not ISO codes.
|
|
604
|
-
|
|
605
|
-
## Configuration
|
|
606
|
-
|
|
607
|
-
### Environment variables
|
|
608
|
-
|
|
609
|
-
`createArcaClient()` discovers missing fields using the same rules as
|
|
610
|
-
`createArcaClientConfigFromEnv()`. Explicit fields win; `process.env` is not changed:
|
|
611
|
-
|
|
612
|
-
| Variable | Required | Notes |
|
|
613
|
-
| --- | --- | --- |
|
|
614
|
-
| `ARCA_TAX_ID` | Yes | 11-digit CUIT |
|
|
615
|
-
| `ARCA_CERTIFICATE_PEM` | Yes | PEM certificate |
|
|
616
|
-
| `ARCA_PRIVATE_KEY_PEM` | Yes | PEM private key |
|
|
617
|
-
| `ARCA_ENVIRONMENT` | Yes | `test` or `production`; there is no default |
|
|
618
|
-
|
|
619
|
-
For logging without code changes, set `ARCA_LOG_LEVEL` to `debug`, `info`, `warn`, or `error`.
|
|
620
|
-
|
|
621
|
-
Pass a config object to `createArcaClient`:
|
|
622
|
-
|
|
623
|
-
```ts
|
|
624
|
-
import { createArcaClient } from "facturas";
|
|
625
|
-
|
|
626
|
-
const client = createArcaClient({
|
|
627
|
-
taxId: "20123456789",
|
|
628
|
-
certificatePem: process.env.ARCA_CERTIFICATE_PEM!,
|
|
629
|
-
privateKeyPem: process.env.ARCA_PRIVATE_KEY_PEM!,
|
|
630
|
-
environment: "test",
|
|
631
|
-
timeout: 30_000,
|
|
632
|
-
retries: 2,
|
|
633
|
-
retryDelay: 500,
|
|
634
|
-
logger: { level: "debug" },
|
|
635
|
-
// Optional: share WSAA login tickets across workers.
|
|
636
|
-
// wsaaSessionStore,
|
|
637
|
-
});
|
|
638
|
-
```
|
|
639
|
-
|
|
640
|
-
| Field | Default | Description |
|
|
641
|
-
| --- | --- | --- |
|
|
642
|
-
| `taxId` | `ARCA_TAX_ID` | 11-digit CUIT |
|
|
643
|
-
| `certificatePem` | `ARCA_CERTIFICATE_PEM` | PEM certificate |
|
|
644
|
-
| `privateKeyPem` | `ARCA_PRIVATE_KEY_PEM` | PEM private key |
|
|
645
|
-
| `environment` | `test` | `test` or `production` |
|
|
646
|
-
| `timeout` | `30000` | HTTP request timeout in milliseconds |
|
|
647
|
-
| `retries` | `0` | Extra attempts after transport failures only |
|
|
648
|
-
| `retryDelay` | `500` | Delay between transport retries in milliseconds |
|
|
649
|
-
| `logger` | — | Optional structured logger config |
|
|
650
|
-
| `store` | — | Unified durable tickets and reservations |
|
|
651
|
-
| `wsaaSessionStore` | — | Optional WSAA ticket store for multi-worker deployments |
|
|
652
|
-
|
|
653
|
-
### WSAA session stores
|
|
654
|
-
|
|
655
|
-
By default, WSAA login tickets are cached in the current process only. That keeps scripts and single-process apps zero-config:
|
|
74
|
+
import { createArcaClient, createPostgresStore } from "facturas";
|
|
75
|
+
import { sql } from "@vercel/postgres";
|
|
656
76
|
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
taxId: "20123456789",
|
|
660
|
-
certificatePem: process.env.ARCA_CERTIFICATE_PEM!,
|
|
661
|
-
privateKeyPem: process.env.ARCA_PRIVATE_KEY_PEM!,
|
|
662
|
-
environment: "production",
|
|
77
|
+
const arca = createArcaClient({
|
|
78
|
+
store: createPostgresStore({ query: (text, params) => sql.query(text, params) }),
|
|
663
79
|
});
|
|
664
|
-
```
|
|
665
|
-
|
|
666
|
-
A configured `store` supplies durable WSAA tickets automatically. An explicit
|
|
667
|
-
`wsaaSessionStore` remains supported and takes precedence for tickets only.
|
|
668
|
-
|
|
669
|
-
```ts
|
|
670
|
-
import {
|
|
671
|
-
type ArcaAuthCredentials,
|
|
672
|
-
type ArcaWsaaSessionKey,
|
|
673
|
-
createArcaClient,
|
|
674
|
-
} from "facturas";
|
|
675
80
|
|
|
676
|
-
const
|
|
677
|
-
async get(key: ArcaWsaaSessionKey): Promise<ArcaAuthCredentials | null> {
|
|
678
|
-
// Read from Postgres, Redis, or another shared store.
|
|
679
|
-
return null;
|
|
680
|
-
},
|
|
681
|
-
async set(
|
|
682
|
-
key: ArcaWsaaSessionKey,
|
|
683
|
-
credentials: ArcaAuthCredentials
|
|
684
|
-
): Promise<void> {
|
|
685
|
-
// Persist token, sign, and expiresAt for the key.
|
|
686
|
-
},
|
|
687
|
-
async withLock<T>(
|
|
688
|
-
key: ArcaWsaaSessionKey,
|
|
689
|
-
fn: () => Promise<T>
|
|
690
|
-
): Promise<T> {
|
|
691
|
-
// Optional but recommended: serialize cold-start refreshes.
|
|
692
|
-
return await fn();
|
|
693
|
-
},
|
|
694
|
-
};
|
|
695
|
-
|
|
696
|
-
const client = createArcaClient({
|
|
697
|
-
taxId: "20123456789",
|
|
698
|
-
certificatePem: process.env.ARCA_CERTIFICATE_PEM!,
|
|
699
|
-
privateKeyPem: process.env.ARCA_PRIVATE_KEY_PEM!,
|
|
700
|
-
environment: "production",
|
|
701
|
-
wsaaSessionStore,
|
|
702
|
-
});
|
|
81
|
+
const factura = await arca.issue(input, { idempotencyKey: venta.id });
|
|
703
82
|
```
|
|
704
83
|
|
|
705
|
-
|
|
84
|
+
Hay adaptadores para Postgres, Redis, archivos y memoria, y podés escribir el
|
|
85
|
+
tuyo. Ver [Stores](./docs/stores.md).
|
|
706
86
|
|
|
707
|
-
|
|
87
|
+
## Nota de crédito
|
|
708
88
|
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
89
|
+
ARCA no anula comprobantes: una corrección es una nota de crédito y también es
|
|
90
|
+
un documento real que queda en los registros de ARCA. Lo habitual es la nota
|
|
91
|
+
parcial, que acredita las líneas que elegís.
|
|
712
92
|
|
|
713
93
|
```ts
|
|
714
|
-
const
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
environment: "test",
|
|
719
|
-
logger: { level: "debug" },
|
|
720
|
-
});
|
|
721
|
-
```
|
|
722
|
-
|
|
723
|
-
Custom logger sinks receive `(level, message, ...args)`:
|
|
724
|
-
|
|
725
|
-
```ts
|
|
726
|
-
const client = createArcaClient({
|
|
727
|
-
taxId: "20123456789",
|
|
728
|
-
certificatePem: "...",
|
|
729
|
-
privateKeyPem: "...",
|
|
730
|
-
environment: "production",
|
|
731
|
-
logger: {
|
|
732
|
-
level: "info",
|
|
733
|
-
log(level, message, ...args) {
|
|
734
|
-
// forward to your logger
|
|
735
|
-
},
|
|
94
|
+
const nota = await arca.issueCreditNote(
|
|
95
|
+
{
|
|
96
|
+
for: { salesPoint: 3, voucherType: 11, number: 41 },
|
|
97
|
+
items: [{ amount: 50_000 }], // ARS 500,00 de una factura de ARS 1.500,00.
|
|
736
98
|
},
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
Disable logging entirely with `logger: { disabled: true }`.
|
|
741
|
-
|
|
742
|
-
## Retries and timeouts
|
|
743
|
-
|
|
744
|
-
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.
|
|
745
|
-
|
|
746
|
-
Separately, authenticated WSFE and WSMTXCA convenience operations may perform
|
|
747
|
-
one forced-refresh retry only after `ArcaAuthenticationError`. Timeouts,
|
|
748
|
-
connection loss, invalid SOAP, incomplete evidence, contradictory evidence, and
|
|
749
|
-
generic service rejections never unlock this recovery path. `wsfe.issue()` and
|
|
750
|
-
`wsmtxca.issue()` always perform one exact authorization attempt, and each
|
|
751
|
-
authorization SOAP attempt has transport retries set to zero.
|
|
752
|
-
|
|
753
|
-
## Service Surface
|
|
754
|
-
|
|
755
|
-
### `client.wsfe`
|
|
756
|
-
|
|
757
|
-
WSFE electronic invoicing. Inputs use JS-style names and the SDK maps them to AFIP / ARCA SOAP fields internally.
|
|
758
|
-
|
|
759
|
-
- Date fields accept `YYYY-MM-DD` or `YYYYMMDD`.
|
|
760
|
-
- `issue({ voucherNumber, data })` sends one exact authorization and returns `authorized`, `rejected` or `indeterminate` evidence.
|
|
761
|
-
- `getNextVoucherNumber({ salesPoint, voucherType })` reads the next number to reserve.
|
|
762
|
-
- `getVoucherInfo({ number, salesPoint, voucherType })` returns voucher details or `null`.
|
|
763
|
-
- Catalog methods are available for live reference data when you do not want to hardcode values.
|
|
764
|
-
- Authenticated methods accept `forceRefresh: true` to discard the cached WSAA TA and request a fresh Token Authorization for the same service.
|
|
765
|
-
|
|
766
|
-
### `client.padron`
|
|
767
|
-
|
|
768
|
-
- `getTaxpayerDetails(taxId)` returns taxpayer data or `null`
|
|
769
|
-
- `getTaxIdByDocument(documentNumber)` returns CUIT candidates or `null`
|
|
770
|
-
|
|
771
|
-
Padron "not found" handling currently depends on SOAP fault message text from ARCA and is therefore more fragile than WSFE code-based flows.
|
|
772
|
-
|
|
773
|
-
### `client.wsmtxca`
|
|
774
|
-
|
|
775
|
-
- `issue({ data })`
|
|
776
|
-
- `getLastAuthorizedVoucher({ voucherType, salesPoint })`
|
|
777
|
-
- `getVoucher({ voucherType, salesPoint, voucherNumber })`
|
|
778
|
-
- Authenticated methods accept `forceRefresh: true` to renew the WSMTXCA WSAA TA before the call.
|
|
779
|
-
|
|
780
|
-
The runtime support is stable and public. It is simply not the main documentation path in this SDK-focused pass.
|
|
781
|
-
|
|
782
|
-
## Error handling
|
|
783
|
-
|
|
784
|
-
All errors extend `ArcaError` and expose a stable `code` string.
|
|
785
|
-
|
|
786
|
-
| Class | When |
|
|
787
|
-
| --- | --- |
|
|
788
|
-
| `ArcaConfigurationError` | Invalid client config |
|
|
789
|
-
| `ArcaInputError` | Invalid caller input such as a malformed date |
|
|
790
|
-
| `ArcaAuthenticationError` | Explicit provider authentication rejection |
|
|
791
|
-
| `ArcaTransportError` | HTTP or transport failure |
|
|
792
|
-
| `ArcaSoapFaultError` | SOAP fault returned by ARCA |
|
|
793
|
-
| `ArcaServiceError` | Business-level service rejection, especially WSFE-style errors |
|
|
794
|
-
|
|
795
|
-
```ts
|
|
796
|
-
import {
|
|
797
|
-
ArcaAuthenticationError,
|
|
798
|
-
ArcaServiceError,
|
|
799
|
-
ArcaSoapFaultError,
|
|
800
|
-
ArcaTransportError,
|
|
801
|
-
} from "facturas";
|
|
802
|
-
|
|
803
|
-
try {
|
|
804
|
-
await client.wsfe.getNextVoucherNumber({ salesPoint: 1, voucherType: 6 });
|
|
805
|
-
} catch (error) {
|
|
806
|
-
if (error instanceof ArcaAuthenticationError) {
|
|
807
|
-
console.error(
|
|
808
|
-
error.reason,
|
|
809
|
-
error.service,
|
|
810
|
-
error.operation,
|
|
811
|
-
error.providerCode
|
|
812
|
-
);
|
|
813
|
-
} else if (error instanceof ArcaServiceError) {
|
|
814
|
-
console.error(error.serviceCode, error.message);
|
|
815
|
-
} else if (error instanceof ArcaSoapFaultError) {
|
|
816
|
-
console.error(error.faultCode, error.message);
|
|
817
|
-
} else if (error instanceof ArcaTransportError) {
|
|
818
|
-
console.error(error.statusCode, error.message);
|
|
819
|
-
}
|
|
820
|
-
throw error;
|
|
821
|
-
}
|
|
822
|
-
```
|
|
823
|
-
|
|
824
|
-
Import error classes from `facturas` or `facturas/errors`.
|
|
825
|
-
`isArcaAuthenticationError(error)` is also exported for predicate-style
|
|
826
|
-
routing. Authentication errors expose only the stable code
|
|
827
|
-
`ARCA_AUTHENTICATION_ERROR`, a typed `reason`, service, operation, and a safe
|
|
828
|
-
provider code when available; raw provider bodies and credential values are not
|
|
829
|
-
attached.
|
|
830
|
-
|
|
831
|
-
## Troubleshooting
|
|
832
|
-
|
|
833
|
-
- `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.
|
|
834
|
-
- `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.
|
|
835
|
-
- Expired certificate: replace the PEM certificate with a renewed one that matches the same private key expectations, then redeploy or restart the process.
|
|
836
|
-
- 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.
|
|
837
|
-
- 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.
|
|
838
|
-
- 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.
|
|
839
|
-
|
|
840
|
-
When an error is unclear, check these in order:
|
|
841
|
-
|
|
842
|
-
1. Certificate and private key match.
|
|
843
|
-
2. Environment is correct (`test` vs `production`).
|
|
844
|
-
3. Service authorization was done for that environment.
|
|
845
|
-
4. The voucher type, document type, and amount combination is valid.
|
|
846
|
-
5. Your process is not reusing stale assumptions about the next voucher number.
|
|
847
|
-
|
|
848
|
-
## Public API (semver)
|
|
849
|
-
|
|
850
|
-
Documented entrypoints:
|
|
851
|
-
|
|
852
|
-
- `facturas`
|
|
853
|
-
- `facturas/constants`
|
|
854
|
-
- `facturas/wsfe`
|
|
855
|
-
- `facturas/wsmtxca`
|
|
856
|
-
- `facturas/padron`
|
|
857
|
-
- `facturas/errors`
|
|
858
|
-
- `facturas/types`
|
|
859
|
-
|
|
860
|
-
Low-level SOAP, HTTP, and WSAA internals are not part of the semver contract.
|
|
861
|
-
|
|
862
|
-
Subpath example:
|
|
863
|
-
|
|
864
|
-
```ts
|
|
865
|
-
import { createWsfeService } from "facturas/wsfe";
|
|
866
|
-
import { ARCA_VOUCHER_TYPES } from "facturas/constants";
|
|
867
|
-
import { ArcaServiceError } from "facturas/errors";
|
|
868
|
-
```
|
|
869
|
-
|
|
870
|
-
## Security
|
|
871
|
-
|
|
872
|
-
- Treat certificates and private keys as secrets.
|
|
873
|
-
- By default, WSAA tickets are cached in memory only.
|
|
874
|
-
- 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.
|
|
875
|
-
- Production `wsaaSessionStore` implementations should encrypt credentials at rest or use a backend that provides encryption at rest.
|
|
876
|
-
|
|
877
|
-
## Development
|
|
878
|
-
|
|
879
|
-
```bash
|
|
880
|
-
pnpm install
|
|
881
|
-
pnpm typecheck
|
|
882
|
-
pnpm typecheck:examples
|
|
883
|
-
pnpm test
|
|
884
|
-
pnpm test:coverage
|
|
885
|
-
pnpm pack:check
|
|
99
|
+
{ idempotencyKey: `nc:${devolucion.id}` },
|
|
100
|
+
);
|
|
886
101
|
```
|
|
887
102
|
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
103
|
+
Con `all: true` acreditás el original completo. El modo es explícito y
|
|
104
|
+
obligatorio. La misma fachada emite [notas de débito y por
|
|
105
|
+
período](./docs/notas-de-credito.md), tributos, FCE, [detalle de ítems por
|
|
106
|
+
WSMTXCA](./docs/wsmtxca.md) y `recover()`, que concilia sin emitir.
|
|
107
|
+
|
|
108
|
+
## Documentación
|
|
109
|
+
|
|
110
|
+
- [Inicio rápido](./docs/inicio-rapido.md): de cero a la primera factura y su
|
|
111
|
+
nota de crédito.
|
|
112
|
+
- [Habilitación en ARCA](./docs/habilitacion-arca.md): CUIT, certificado, punto
|
|
113
|
+
de venta y referencias oficiales.
|
|
114
|
+
- [CLI](./docs/cli.md): `init`, `check` e `issue`, con la tabla de diagnósticos.
|
|
115
|
+
- [Facturas](./docs/facturas.md): `issue()`, `preview()`, datos de la factura y
|
|
116
|
+
contrato fiscal de la fachada.
|
|
117
|
+
- [Notas de crédito](./docs/notas-de-credito.md): `issueCreditNote()`, modo
|
|
118
|
+
parcial y modo total.
|
|
119
|
+
- [Stores](./docs/stores.md): Postgres, Redis, archivos, memoria, store propio
|
|
120
|
+
y vida de los registros.
|
|
121
|
+
- [Configuración](./docs/configuracion.md): variables de entorno, opciones del
|
|
122
|
+
cliente, sesiones WSAA, logging, reintentos y timeouts.
|
|
123
|
+
- [Capa exacta](./docs/capa-exacta.md): builders, superficie de servicios,
|
|
124
|
+
emisión exacta y evidencia de recuperación.
|
|
125
|
+
- [Errores](./docs/errores.md): clases de error y diagnóstico.
|
|
126
|
+
- [Referencia](./docs/referencia.md): constantes, API pública con semver y
|
|
127
|
+
seguridad.
|
|
128
|
+
- [Ejemplos](./docs/ejemplos.md): índice de [examples/](./examples).
|
|
129
|
+
|
|
130
|
+
## Estado del proyecto
|
|
131
|
+
|
|
132
|
+
Pre-1.0. Mientras la versión empiece en `0.`, un minor puede cambiar o quitar
|
|
133
|
+
partes de la API pública. Fijá la versión exacta y leé el
|
|
134
|
+
[changelog](./packages/arca/CHANGELOG.md) antes de actualizar.
|
|
135
|
+
|
|
136
|
+
Para contribuir, mirá [CONTRIBUTING.md](./CONTRIBUTING.md).
|
|
137
|
+
|
|
138
|
+
## Licencia
|
|
891
139
|
|
|
892
140
|
Apache-2.0
|