keycae-ts 1.0.0 → 1.1.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/LICENSE +21 -0
- package/README.md +158 -71
- package/dist/index.d.ts +173 -4
- package/dist/index.js +136 -10
- package/package.json +6 -1
- package/src/index.ts +293 -13
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 KeyCAE Software S.A.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,96 +1,183 @@
|
|
|
1
1
|
# keycae-ts 🚀
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/keycae-ts)
|
|
4
|
+
[](https://www.npmjs.com/package/keycae-ts)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
Cliente SDK oficial en **TypeScript** y **JavaScript** para interactuar con **KeyCAE.ar** y emitir facturación electrónica oficial ante **ARCA (ex AFIP)**.
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
## Instalación
|
|
8
10
|
|
|
9
11
|
```bash
|
|
10
12
|
npm install keycae-ts
|
|
11
|
-
# O usando yarn / pnpm
|
|
12
|
-
yarn add keycae-ts
|
|
13
|
-
pnpm add keycae-ts
|
|
14
13
|
```
|
|
15
14
|
|
|
16
|
-
## Inicio Rápido
|
|
15
|
+
## Inicio Rápido
|
|
16
|
+
|
|
17
|
+
El SDK soporta tanto el **Sandbox Público** (sin cuenta, ideal para desarrollo rápido) como el entorno de **Producción** (con cuenta registrada):
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
* **Para Probar en Sandbox (Sin Cuenta):** Usa la clave pública `sk_test_public_sandbox_cuit_20999999999` y el CUIT de pruebas `20999999999`.
|
|
20
|
+
* **Para Producción (Con Cuenta):** Regístrate en [keycae.ar](https://keycae.ar) para obtener tu API Key `sk_live_...` y configura tu CUIT real.
|
|
19
21
|
|
|
20
22
|
```typescript
|
|
21
23
|
import { KeyCaeClient } from 'keycae-ts';
|
|
22
24
|
|
|
23
|
-
//
|
|
24
|
-
const client = new KeyCaeClient('
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
conceptos: [
|
|
48
|
-
{
|
|
49
|
-
descripcion: 'Servicios de Consultoría de Software',
|
|
50
|
-
precio: 150000.00
|
|
51
|
-
}
|
|
52
|
-
]
|
|
53
|
-
}, 'idempotency_key_factura_42'); // Idempotency key para prevenir duplicaciones de red
|
|
54
|
-
|
|
55
|
-
console.log('¡Factura autorizada exitosamente!');
|
|
56
|
-
console.log(`Comprobante: Nro. ${invoice.numero_factura}`);
|
|
57
|
-
console.log(`CAE Otorgado: ${invoice.cae}`);
|
|
58
|
-
console.log(`Vencimiento CAE: ${invoice.cae_vencimiento}`);
|
|
59
|
-
console.log(`PDF de Impresión: ${invoice.url_pdf}`);
|
|
60
|
-
console.log(`Código QR Fiscal: ${invoice.url_qr}`);
|
|
61
|
-
|
|
62
|
-
} catch (error) {
|
|
63
|
-
console.error('Error durante la operación:', error);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
main();
|
|
25
|
+
// Ejemplo para Sandbox Público:
|
|
26
|
+
const client = new KeyCaeClient('sk_test_public_sandbox_cuit_20999999999');
|
|
27
|
+
// O para producción: const client = new KeyCaeClient('sk_live_tu_api_key');
|
|
28
|
+
|
|
29
|
+
// Consultar contribuyente
|
|
30
|
+
const taxpayer = await client.getTaxpayer('20254459306');
|
|
31
|
+
console.log(`${taxpayer.nombre} | ${taxpayer.estado}`);
|
|
32
|
+
|
|
33
|
+
// Verificar qué tipos de factura puede emitir
|
|
34
|
+
const capability = await client.checkEmissionCapability('20254459306');
|
|
35
|
+
console.log(`Tipos compatibles: ${capability.compatible_types.join(', ')}`);
|
|
36
|
+
console.log(capability.recommendation);
|
|
37
|
+
|
|
38
|
+
// Emitir factura
|
|
39
|
+
const invoice = await client.emitInvoice({
|
|
40
|
+
cuit_emisor: '20254459306',
|
|
41
|
+
punto_de_venta: 1,
|
|
42
|
+
tipo_comprobante: 'B',
|
|
43
|
+
receptor: { tipo_doc: 'CUIT', nro_doc: '20333444555' },
|
|
44
|
+
conceptos: [{ descripcion: 'Servicios', precio: 15000 }]
|
|
45
|
+
}, 'idempotency_key_42');
|
|
46
|
+
|
|
47
|
+
console.log(`CAE: ${invoice.cae}`);
|
|
48
|
+
console.log(`PDF: ${invoice.url_pdf}`);
|
|
68
49
|
```
|
|
69
50
|
|
|
70
|
-
##
|
|
51
|
+
## Modelos de Conexión
|
|
71
52
|
|
|
72
|
-
|
|
53
|
+
### Modalidad A: Delegación Directa (Recomendada)
|
|
54
|
+
Sin certificados, sin llaves privadas. En minutos facturás.
|
|
73
55
|
|
|
74
56
|
```typescript
|
|
75
|
-
|
|
57
|
+
const delegation = await client.createDelegation({
|
|
58
|
+
cuit: '20254459306',
|
|
59
|
+
organization: 'Tu Empresa'
|
|
60
|
+
});
|
|
61
|
+
```
|
|
76
62
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
// Consultar configuración actual (retorna enmascarada)
|
|
87
|
-
const settings = await client.getTelegramSettings();
|
|
88
|
-
console.log('Ajustes de alertas activos:', settings);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
setupAlerts();
|
|
63
|
+
### Modalidad B: Bóvedas KMS
|
|
64
|
+
Generación segura de claves RSA y CSR.
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
const credential = await client.createCredential({
|
|
68
|
+
cuit: '20254459306',
|
|
69
|
+
organization: 'Tu Empresa'
|
|
70
|
+
});
|
|
92
71
|
```
|
|
93
72
|
|
|
73
|
+
### Modalidad C: Importar Certificado
|
|
74
|
+
Subida directa de certificados (.crt) y llaves (.key).
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
await client.importCredential({
|
|
78
|
+
cuit: '20254459306',
|
|
79
|
+
organization: 'Tu Empresa',
|
|
80
|
+
certificate: certificatePem,
|
|
81
|
+
private_key: privateKeyPem
|
|
82
|
+
});
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Métodos Disponibles
|
|
86
|
+
|
|
87
|
+
### Taxpayers
|
|
88
|
+
| Método | Descripción |
|
|
89
|
+
|--------|-------------|
|
|
90
|
+
| `getTaxpayer(cuit)` | Consultar contribuyente en padrón ARCA |
|
|
91
|
+
| `checkEmissionCapability(cuit)` | Verificar tipos de factura compatibles |
|
|
92
|
+
|
|
93
|
+
### Delegations
|
|
94
|
+
| Método | Descripción |
|
|
95
|
+
|--------|-------------|
|
|
96
|
+
| `createDelegation(data)` | Iniciar delegación directa |
|
|
97
|
+
| `checkDelegationStatus()` | Consultar estado de delegación |
|
|
98
|
+
|
|
99
|
+
### Credentials (KMS)
|
|
100
|
+
| Método | Descripción |
|
|
101
|
+
|--------|-------------|
|
|
102
|
+
| `createCredential(data)` | Generar RSA keypair + CSR |
|
|
103
|
+
| `listCredentials()` | Listar certificados registrados |
|
|
104
|
+
| `importCredential(data)` | Importar certificado existente |
|
|
105
|
+
| `activateCredential(id, cert)` | Activar bóveda con certificado ARCA |
|
|
106
|
+
| `deleteCredential(id)` | Eliminar bóveda de claves |
|
|
107
|
+
|
|
108
|
+
### Invoices
|
|
109
|
+
| Método | Descripción |
|
|
110
|
+
|--------|-------------|
|
|
111
|
+
| `emitInvoice(data, idempotencyKey?)` | Emitir factura electrónica (CAE) |
|
|
112
|
+
| `getInvoice(id)` | Obtener detalles de factura |
|
|
113
|
+
| `listInvoices(limit?, offset?)` | Listar facturas recientes |
|
|
114
|
+
| `getInvoicePdfBuffer(id)` | Descargar PDF de factura |
|
|
115
|
+
|
|
116
|
+
### Puntos de Venta
|
|
117
|
+
| Método | Descripción |
|
|
118
|
+
|--------|-------------|
|
|
119
|
+
| `listPuntosDeVenta()` | Listar puntos de venta habilitados |
|
|
120
|
+
|
|
121
|
+
### Billing & Settings
|
|
122
|
+
| Método | Descripción |
|
|
123
|
+
|--------|-------------|
|
|
124
|
+
| `getBillingStatus()` | Estado del plan y consumo |
|
|
125
|
+
| `getTelegramSettings()` | Obtener configuración Telegram |
|
|
126
|
+
| `saveTelegramSettings(data)` | Guardar configuración Telegram |
|
|
127
|
+
| `health()` | Health check de la API |
|
|
128
|
+
|
|
129
|
+
### Partners (ERPs / SaaS)
|
|
130
|
+
| Método | Descripción |
|
|
131
|
+
|--------|-------------|
|
|
132
|
+
| `createPartnerSubaccount(data)` | Crear subcuenta completa para un cliente final (usuario + API Key) |
|
|
133
|
+
| `listPartnerSubaccounts()` | Listar tus clientes con plan y consumo mensual |
|
|
134
|
+
| `preauthorizeCuit(cuit)` | Pre-autorizar un CUIT para facturación consolidada |
|
|
135
|
+
| `getPartnerConfig(partnerId)` | Configuración pública de co-branding (sin auth) |
|
|
136
|
+
|
|
137
|
+
> Los métodos de Partners requieren instanciar el cliente con tu **API Key de Partner** (`sk_partner_live_...`) y solo deben usarse desde tu backend:
|
|
138
|
+
>
|
|
139
|
+
> ```typescript
|
|
140
|
+
> const partnerClient = new KeyCaeClient('sk_partner_live_mi-erp_...');
|
|
141
|
+
>
|
|
142
|
+
> const sub = await partnerClient.createPartnerSubaccount({
|
|
143
|
+
> cuit: '20304567891',
|
|
144
|
+
> organization: 'Cliente S.A.',
|
|
145
|
+
> email: 'cliente@empresa.com'
|
|
146
|
+
> });
|
|
147
|
+
> // sub.api_key y sub.password se devuelven UNA sola vez
|
|
148
|
+
>
|
|
149
|
+
> const { clients, total } = await partnerClient.listPartnerSubaccounts();
|
|
150
|
+
> ```
|
|
151
|
+
>
|
|
152
|
+
> 📖 Guía completa del programa de partners: [keycae.ar/docs/partners.md](https://keycae.ar/docs/partners.md)
|
|
153
|
+
|
|
154
|
+
## Tipos de Comprobante
|
|
155
|
+
|
|
156
|
+
| Tipo | Descripción |
|
|
157
|
+
|------|-------------|
|
|
158
|
+
| `A` | IVA discriminado (Responsable Inscripto → RI) |
|
|
159
|
+
| `B` | Consumidor Final (RI → CF) |
|
|
160
|
+
| `C` | Exento (Monotributo / Exento) |
|
|
161
|
+
| `M` | Monotributo |
|
|
162
|
+
| `E` | Exportación |
|
|
163
|
+
| `NCA` `NCB` `NCC` `NCE` `NCM` | Notas de Crédito (A, B, C, E, M) |
|
|
164
|
+
| `NDA` `NDB` `NDC` `NDE` `NDM` | Notas de Débito (A, B, C, E, M) |
|
|
165
|
+
|
|
166
|
+
## Alertas Telegram
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
await client.saveTelegramSettings({
|
|
170
|
+
telegram_bot_token: '123456:***',
|
|
171
|
+
telegram_chat_id: '987654321'
|
|
172
|
+
});
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Documentación
|
|
176
|
+
|
|
177
|
+
- 📖 [docs.keycae.ar](https://docs.keycae.ar) — Documentación interactiva
|
|
178
|
+
- 📄 [llms.txt](https://keycae.ar/llms.txt) — Para AI agents
|
|
179
|
+
- 🔌 [MCP Server](https://www.npmjs.com/package/keycae-mcp) — Para Cursor/Claude
|
|
180
|
+
|
|
94
181
|
## Licencia
|
|
95
182
|
|
|
96
183
|
MIT
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
export interface DomicilioFiscal {
|
|
2
|
+
direccion: string | null;
|
|
3
|
+
localidad: string | null;
|
|
4
|
+
cod_postal: string | null;
|
|
5
|
+
provincia: string | null;
|
|
6
|
+
}
|
|
1
7
|
export interface TaxpayerResponse {
|
|
2
8
|
cuit: string;
|
|
3
9
|
nombre: string;
|
|
@@ -5,23 +11,63 @@ export interface TaxpayerResponse {
|
|
|
5
11
|
categoria_monotributo?: string;
|
|
6
12
|
es_iva_inscripto: boolean;
|
|
7
13
|
estado: string;
|
|
14
|
+
condicion_iva?: string;
|
|
15
|
+
actividades?: string[];
|
|
16
|
+
domicilio_fiscal?: DomicilioFiscal;
|
|
8
17
|
}
|
|
9
18
|
export interface InvoiceReceptor {
|
|
10
|
-
tipo_doc: 'DNI' | 'CUIT' | 'CUIL' | 'PASAPORTE';
|
|
19
|
+
tipo_doc: 'DNI' | 'CUIT' | 'CUIL' | 'PASAPORTE' | 'SIN_IDENTIFICAR';
|
|
11
20
|
nro_doc: string;
|
|
21
|
+
razon_social?: string;
|
|
22
|
+
condicion_iva?: string;
|
|
12
23
|
}
|
|
13
24
|
export interface InvoiceItem {
|
|
14
25
|
descripcion: string;
|
|
15
26
|
precio: number;
|
|
27
|
+
cantidad?: number;
|
|
28
|
+
alicuota_iva?: number;
|
|
29
|
+
}
|
|
30
|
+
export interface Tributo {
|
|
31
|
+
id: number;
|
|
32
|
+
descripcion: string;
|
|
33
|
+
base_imponible: number;
|
|
34
|
+
alicuota: number;
|
|
35
|
+
importe: number;
|
|
36
|
+
}
|
|
37
|
+
export interface Opcional {
|
|
38
|
+
id: string;
|
|
39
|
+
valor: string;
|
|
40
|
+
}
|
|
41
|
+
export interface CondicionesIvaResponse {
|
|
42
|
+
condiciones: {
|
|
43
|
+
codigo: number;
|
|
44
|
+
nombre: string;
|
|
45
|
+
}[];
|
|
16
46
|
}
|
|
17
47
|
export interface InvoiceInput {
|
|
18
48
|
cuit_emisor: string;
|
|
19
49
|
punto_de_venta: number;
|
|
20
|
-
tipo_comprobante: 'A' | 'B' | 'C' | 'M';
|
|
50
|
+
tipo_comprobante: 'A' | 'B' | 'C' | 'M' | 'E' | 'NCA' | 'NCB' | 'NCC' | 'NCE' | 'NCM' | 'NDA' | 'NDB' | 'NDC' | 'NDE' | 'NDM' | 'FCE_A' | 'FCE_B' | 'FCE_C' | 'FCE_NDA' | 'FCE_NDB' | 'FCE_NDC' | 'FCE_NCA' | 'FCE_NCB' | 'FCE_NCC';
|
|
21
51
|
receptor: InvoiceReceptor;
|
|
22
52
|
conceptos: InvoiceItem[];
|
|
53
|
+
tributos?: Tributo[];
|
|
54
|
+
moneda?: string;
|
|
55
|
+
moneda_cotizacion?: number;
|
|
56
|
+
fecha_comprobante?: string;
|
|
57
|
+
fecha_servicio_desde?: string;
|
|
58
|
+
fecha_servicio_hasta?: string;
|
|
59
|
+
fecha_vto_pago?: string;
|
|
60
|
+
condicion_iva_receptor?: number;
|
|
61
|
+
opcionales?: Opcional[];
|
|
23
62
|
brand_logo_url?: string;
|
|
24
63
|
brand_color?: string;
|
|
64
|
+
cbtes_asociados?: {
|
|
65
|
+
tipo: string | number;
|
|
66
|
+
punto_de_venta: number;
|
|
67
|
+
numero: number;
|
|
68
|
+
cuit?: string;
|
|
69
|
+
fecha?: string;
|
|
70
|
+
}[];
|
|
25
71
|
}
|
|
26
72
|
export interface InvoiceResponse {
|
|
27
73
|
id: string;
|
|
@@ -61,7 +107,7 @@ export interface DelegationInput {
|
|
|
61
107
|
}
|
|
62
108
|
export interface DelegationResponse {
|
|
63
109
|
id: string;
|
|
64
|
-
status: 'pending' | 'accepted' | 'rejected';
|
|
110
|
+
status: 'pending' | 'accepted' | 'rejected' | 'pending_auto';
|
|
65
111
|
cuit: string;
|
|
66
112
|
organization: string;
|
|
67
113
|
representativeCuit: string;
|
|
@@ -89,6 +135,70 @@ export interface KmsImportInput {
|
|
|
89
135
|
certificate: string;
|
|
90
136
|
private_key: string;
|
|
91
137
|
}
|
|
138
|
+
export interface PuntoDeVenta {
|
|
139
|
+
numero: number;
|
|
140
|
+
tipoEmision: string;
|
|
141
|
+
tipoAutomatizacion?: string;
|
|
142
|
+
fechaServicioDesde?: string;
|
|
143
|
+
fechaServicioHasta?: string;
|
|
144
|
+
}
|
|
145
|
+
export interface EmissionCapability {
|
|
146
|
+
cuit: string;
|
|
147
|
+
condicion_iva: string;
|
|
148
|
+
compatible_types: string[];
|
|
149
|
+
recommendation: string;
|
|
150
|
+
tip: string;
|
|
151
|
+
}
|
|
152
|
+
export interface HealthResponse {
|
|
153
|
+
status: string;
|
|
154
|
+
version?: string;
|
|
155
|
+
uptime?: number;
|
|
156
|
+
}
|
|
157
|
+
export interface PartnerSubaccountInput {
|
|
158
|
+
/** CUIT fiscal del cliente final (11 dígitos, sin guiones) */
|
|
159
|
+
cuit: string;
|
|
160
|
+
/** Razón social del cliente final */
|
|
161
|
+
organization: string;
|
|
162
|
+
/** Email de acceso del cliente final */
|
|
163
|
+
email: string;
|
|
164
|
+
}
|
|
165
|
+
export interface PartnerSubaccountResponse {
|
|
166
|
+
id: string;
|
|
167
|
+
cuit: string;
|
|
168
|
+
organization: string;
|
|
169
|
+
email: string;
|
|
170
|
+
/** Contraseña generada — se devuelve UNA sola vez. Entregar por canal seguro. */
|
|
171
|
+
password: string;
|
|
172
|
+
/** API Key live de la subcuenta — se devuelve UNA sola vez. */
|
|
173
|
+
api_key: string;
|
|
174
|
+
partner_id: string;
|
|
175
|
+
}
|
|
176
|
+
export interface PartnerClient {
|
|
177
|
+
cuit: string;
|
|
178
|
+
organization: string;
|
|
179
|
+
email: string;
|
|
180
|
+
plan: 'free' | 'developer' | 'platform';
|
|
181
|
+
invoices_this_month: number;
|
|
182
|
+
created_at: string | null;
|
|
183
|
+
}
|
|
184
|
+
export interface PartnerClientsResponse {
|
|
185
|
+
partner_id: string;
|
|
186
|
+
total: number;
|
|
187
|
+
clients: PartnerClient[];
|
|
188
|
+
}
|
|
189
|
+
export interface PartnerPreauthResponse {
|
|
190
|
+
success: boolean;
|
|
191
|
+
message: string;
|
|
192
|
+
partner_id: string;
|
|
193
|
+
cuit: string;
|
|
194
|
+
}
|
|
195
|
+
export interface PartnerPublicConfig {
|
|
196
|
+
id: string;
|
|
197
|
+
name: string;
|
|
198
|
+
logo_url: string;
|
|
199
|
+
brand_color: string;
|
|
200
|
+
billing_mode: 'decentralized' | 'consolidated';
|
|
201
|
+
}
|
|
92
202
|
export declare class KeyCaeClient {
|
|
93
203
|
private apiKey;
|
|
94
204
|
private baseUrl;
|
|
@@ -98,6 +208,10 @@ export declare class KeyCaeClient {
|
|
|
98
208
|
* Consultar Contribuyente en Padrón ARCA
|
|
99
209
|
*/
|
|
100
210
|
getTaxpayer(cuit: string): Promise<TaxpayerResponse>;
|
|
211
|
+
/**
|
|
212
|
+
* Verificar qué tipos de factura puede emitir un CUIT según su condición fiscal
|
|
213
|
+
*/
|
|
214
|
+
checkEmissionCapability(cuit: string): Promise<EmissionCapability>;
|
|
101
215
|
/**
|
|
102
216
|
* Registrar / Iniciar Direct Delegation
|
|
103
217
|
*/
|
|
@@ -110,6 +224,10 @@ export declare class KeyCaeClient {
|
|
|
110
224
|
* Crear Bóveda de Claves KMS & Generar CSR
|
|
111
225
|
*/
|
|
112
226
|
createCredential(data: KmsCredentialInput): Promise<KmsCredentialResponse>;
|
|
227
|
+
/**
|
|
228
|
+
* Listar Credenciales Digitales Registradas
|
|
229
|
+
*/
|
|
230
|
+
listCredentials(): Promise<KmsCredentialResponse[]>;
|
|
113
231
|
/**
|
|
114
232
|
* Importar Credenciales de Firma Existentes (.key y .crt)
|
|
115
233
|
*/
|
|
@@ -126,10 +244,27 @@ export declare class KeyCaeClient {
|
|
|
126
244
|
* Emitir Factura Electrónica Homologada (CAE)
|
|
127
245
|
*/
|
|
128
246
|
emitInvoice(data: InvoiceInput, idempotencyKey?: string): Promise<InvoiceResponse>;
|
|
247
|
+
/**
|
|
248
|
+
* Obtener Detalles de una Factura por ID
|
|
249
|
+
*/
|
|
250
|
+
getInvoice(id: string): Promise<InvoiceResponse>;
|
|
251
|
+
/**
|
|
252
|
+
* Listar Facturas Recientes
|
|
253
|
+
*/
|
|
254
|
+
listInvoices(limit?: number, offset?: number): Promise<{
|
|
255
|
+
invoices: InvoiceResponse[];
|
|
256
|
+
total: number;
|
|
257
|
+
}>;
|
|
129
258
|
/**
|
|
130
259
|
* Descargar PDF de Factura (Retorna el Stream / ArrayBuffer)
|
|
131
260
|
*/
|
|
132
|
-
getInvoicePdfBuffer(id: string
|
|
261
|
+
getInvoicePdfBuffer(id: string, options?: {
|
|
262
|
+
format?: 'a4' | 'ticket';
|
|
263
|
+
}): Promise<Buffer>;
|
|
264
|
+
/**
|
|
265
|
+
* Listar Puntos de Venta Habilitados para Facturación Electrónica
|
|
266
|
+
*/
|
|
267
|
+
listPuntosDeVenta(): Promise<PuntoDeVenta[]>;
|
|
133
268
|
/**
|
|
134
269
|
* Obtener Consumos y Estado del Plan (Billing)
|
|
135
270
|
*/
|
|
@@ -142,4 +277,38 @@ export declare class KeyCaeClient {
|
|
|
142
277
|
* Guardar / Actualizar Ajustes de Telegram
|
|
143
278
|
*/
|
|
144
279
|
saveTelegramSettings(data: TelegramSettingsInput): Promise<any>;
|
|
280
|
+
/**
|
|
281
|
+
* Health Check de la API
|
|
282
|
+
*/
|
|
283
|
+
health(): Promise<HealthResponse>;
|
|
284
|
+
/**
|
|
285
|
+
* Obtener tabla de condiciones IVA del receptor (15 códigos oficiales ARCA)
|
|
286
|
+
*/
|
|
287
|
+
getCondicionesIva(): Promise<CondicionesIvaResponse>;
|
|
288
|
+
/**
|
|
289
|
+
* Consultar cotización de una moneda extranjera vs peso (vía ARCA)
|
|
290
|
+
*/
|
|
291
|
+
getCotizacionMoneda(moneda: string): Promise<{
|
|
292
|
+
moneda: string;
|
|
293
|
+
cotizacion: number;
|
|
294
|
+
}>;
|
|
295
|
+
/**
|
|
296
|
+
* Crear una subcuenta completa (usuario + perfil + API Key) para un
|
|
297
|
+
* cliente final de tu ERP. La password y api_key se devuelven una sola vez.
|
|
298
|
+
*/
|
|
299
|
+
createPartnerSubaccount(data: PartnerSubaccountInput): Promise<PartnerSubaccountResponse>;
|
|
300
|
+
/**
|
|
301
|
+
* Listar los clientes vinculados a tu partner, con plan y consumo mensual.
|
|
302
|
+
*/
|
|
303
|
+
listPartnerSubaccounts(): Promise<PartnerClientsResponse>;
|
|
304
|
+
/**
|
|
305
|
+
* Pre-autorizar el CUIT de un cliente para facturación consolidada
|
|
306
|
+
* (el cliente no pasará por la pasarela de pago al registrarse).
|
|
307
|
+
*/
|
|
308
|
+
preauthorizeCuit(cuit: string): Promise<PartnerPreauthResponse>;
|
|
309
|
+
/**
|
|
310
|
+
* Consultar la configuración pública de co-branding de un partner
|
|
311
|
+
* (endpoint público — no requiere autenticación).
|
|
312
|
+
*/
|
|
313
|
+
getPartnerConfig(partnerId: string): Promise<PartnerPublicConfig>;
|
|
145
314
|
}
|
package/dist/index.js
CHANGED
|
@@ -5,10 +5,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.KeyCaeClient = void 0;
|
|
7
7
|
const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
8
|
+
// ════════════════════════════════════════════════════════════════════
|
|
9
|
+
// CLIENT
|
|
10
|
+
// ════════════════════════════════════════════════════════════════════
|
|
8
11
|
class KeyCaeClient {
|
|
9
|
-
constructor(apiKey, baseUrl = 'https://
|
|
12
|
+
constructor(apiKey, baseUrl = 'https://keycae.ar') {
|
|
10
13
|
this.apiKey = apiKey;
|
|
11
|
-
this.baseUrl = baseUrl;
|
|
14
|
+
this.baseUrl = baseUrl.replace(/\/$/, '');
|
|
12
15
|
}
|
|
13
16
|
async request(method, path, body, idempotencyKey) {
|
|
14
17
|
const headers = {
|
|
@@ -24,21 +27,60 @@ class KeyCaeClient {
|
|
|
24
27
|
body: body ? JSON.stringify(body) : undefined
|
|
25
28
|
});
|
|
26
29
|
if (!response.ok) {
|
|
27
|
-
let
|
|
30
|
+
let errorData;
|
|
28
31
|
try {
|
|
29
|
-
|
|
32
|
+
errorData = await response.json();
|
|
30
33
|
}
|
|
31
|
-
catch
|
|
32
|
-
|
|
34
|
+
catch {
|
|
35
|
+
const text = await response.text().catch(() => '');
|
|
36
|
+
throw new Error(`KeyCAE API Error (${response.status}): ${text || response.statusText}`);
|
|
37
|
+
}
|
|
38
|
+
const message = errorData?.error?.message || JSON.stringify(errorData);
|
|
39
|
+
const hint = errorData?.error?.ai_action_hint || '';
|
|
40
|
+
throw new Error(`KeyCAE API Error (${response.status}): ${message}${hint ? ` [hint: ${hint}]` : ''}`);
|
|
33
41
|
}
|
|
34
42
|
return response.json();
|
|
35
43
|
}
|
|
44
|
+
// ── Taxpayers ─────────────────────────────────────────────────────
|
|
36
45
|
/**
|
|
37
46
|
* Consultar Contribuyente en Padrón ARCA
|
|
38
47
|
*/
|
|
39
48
|
async getTaxpayer(cuit) {
|
|
40
49
|
return this.request('GET', `/v1/taxpayers/${cuit}`);
|
|
41
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Verificar qué tipos de factura puede emitir un CUIT según su condición fiscal
|
|
53
|
+
*/
|
|
54
|
+
async checkEmissionCapability(cuit) {
|
|
55
|
+
const taxpayer = await this.getTaxpayer(cuit);
|
|
56
|
+
const condition = taxpayer.condicion_iva || taxpayer.estado || '';
|
|
57
|
+
let compatibleTypes = [];
|
|
58
|
+
let recommendation = '';
|
|
59
|
+
if (condition.toLowerCase().includes('monotributo')) {
|
|
60
|
+
compatibleTypes = ['C', 'M'];
|
|
61
|
+
recommendation = 'Como Monotributista, podés emitir Factura C (exenta) o Factura M (monotributo). NO podés emitir Factura A o B.';
|
|
62
|
+
}
|
|
63
|
+
else if (condition.toLowerCase().includes('responsable inscripto') || condition.toLowerCase().includes('ri')) {
|
|
64
|
+
compatibleTypes = ['A', 'B'];
|
|
65
|
+
recommendation = 'Como Responsable Inscripto, podés emitir Factura A (IVA discriminado) o Factura B (consumidor final).';
|
|
66
|
+
}
|
|
67
|
+
else if (condition.toLowerCase().includes('exento')) {
|
|
68
|
+
compatibleTypes = ['C'];
|
|
69
|
+
recommendation = 'Como Exento, solo podés emitir Factura C (exenta).';
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
compatibleTypes = ['C'];
|
|
73
|
+
recommendation = 'No se pudo determinar la condición impositiva. Se recomienda Factura C por defecto.';
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
cuit,
|
|
77
|
+
condicion_iva: condition,
|
|
78
|
+
compatible_types: compatibleTypes,
|
|
79
|
+
recommendation,
|
|
80
|
+
tip: 'Usá estos tipos al emitir facturas con emitInvoice para evitar errores de ARCA.'
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
// ── Delegations ───────────────────────────────────────────────────
|
|
42
84
|
/**
|
|
43
85
|
* Registrar / Iniciar Direct Delegation
|
|
44
86
|
*/
|
|
@@ -51,12 +93,19 @@ class KeyCaeClient {
|
|
|
51
93
|
async checkDelegationStatus() {
|
|
52
94
|
return this.request('GET', '/v1/delegations/status');
|
|
53
95
|
}
|
|
96
|
+
// ── Credentials (KMS) ────────────────────────────────────────────
|
|
54
97
|
/**
|
|
55
98
|
* Crear Bóveda de Claves KMS & Generar CSR
|
|
56
99
|
*/
|
|
57
100
|
async createCredential(data) {
|
|
58
101
|
return this.request('POST', '/v1/credentials', data);
|
|
59
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* Listar Credenciales Digitales Registradas
|
|
105
|
+
*/
|
|
106
|
+
async listCredentials() {
|
|
107
|
+
return this.request('GET', '/v1/credentials');
|
|
108
|
+
}
|
|
60
109
|
/**
|
|
61
110
|
* Importar Credenciales de Firma Existentes (.key y .crt)
|
|
62
111
|
*/
|
|
@@ -75,17 +124,35 @@ class KeyCaeClient {
|
|
|
75
124
|
async deleteCredential(id) {
|
|
76
125
|
return this.request('DELETE', `/v1/credentials/${id}`);
|
|
77
126
|
}
|
|
127
|
+
// ── Invoices ──────────────────────────────────────────────────────
|
|
78
128
|
/**
|
|
79
129
|
* Emitir Factura Electrónica Homologada (CAE)
|
|
80
130
|
*/
|
|
81
131
|
async emitInvoice(data, idempotencyKey) {
|
|
82
132
|
return this.request('POST', '/v1/invoices', data, idempotencyKey);
|
|
83
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* Obtener Detalles de una Factura por ID
|
|
136
|
+
*/
|
|
137
|
+
async getInvoice(id) {
|
|
138
|
+
return this.request('GET', `/v1/invoices/${id}`);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Listar Facturas Recientes
|
|
142
|
+
*/
|
|
143
|
+
async listInvoices(limit = 10, offset = 0) {
|
|
144
|
+
const params = new URLSearchParams({ limit: String(limit), offset: String(offset) });
|
|
145
|
+
return this.request('GET', `/v1/invoices?${params}`);
|
|
146
|
+
}
|
|
84
147
|
/**
|
|
85
148
|
* Descargar PDF de Factura (Retorna el Stream / ArrayBuffer)
|
|
86
149
|
*/
|
|
87
|
-
async getInvoicePdfBuffer(id) {
|
|
88
|
-
const
|
|
150
|
+
async getInvoicePdfBuffer(id, options) {
|
|
151
|
+
const url = new URL(`${this.baseUrl}/v1/invoices/${id}/pdf`);
|
|
152
|
+
if (options?.format) {
|
|
153
|
+
url.searchParams.append('format', options.format);
|
|
154
|
+
}
|
|
155
|
+
const response = await (0, node_fetch_1.default)(url.toString(), {
|
|
89
156
|
method: 'GET',
|
|
90
157
|
headers: {
|
|
91
158
|
'Authorization': `Bearer ${this.apiKey}`
|
|
@@ -94,15 +161,23 @@ class KeyCaeClient {
|
|
|
94
161
|
if (!response.ok) {
|
|
95
162
|
throw new Error(`Failed to fetch PDF (${response.status})`);
|
|
96
163
|
}
|
|
97
|
-
|
|
98
|
-
|
|
164
|
+
return Buffer.from(await response.arrayBuffer());
|
|
165
|
+
}
|
|
166
|
+
// ── Puntos de Venta ───────────────────────────────────────────────
|
|
167
|
+
/**
|
|
168
|
+
* Listar Puntos de Venta Habilitados para Facturación Electrónica
|
|
169
|
+
*/
|
|
170
|
+
async listPuntosDeVenta() {
|
|
171
|
+
return this.request('GET', '/v1/pos');
|
|
99
172
|
}
|
|
173
|
+
// ── Billing ───────────────────────────────────────────────────────
|
|
100
174
|
/**
|
|
101
175
|
* Obtener Consumos y Estado del Plan (Billing)
|
|
102
176
|
*/
|
|
103
177
|
async getBillingStatus() {
|
|
104
178
|
return this.request('GET', '/v1/billing/status');
|
|
105
179
|
}
|
|
180
|
+
// ── Telegram Settings ─────────────────────────────────────────────
|
|
106
181
|
/**
|
|
107
182
|
* Obtener Ajustes de Telegram por Tenant
|
|
108
183
|
*/
|
|
@@ -115,5 +190,56 @@ class KeyCaeClient {
|
|
|
115
190
|
async saveTelegramSettings(data) {
|
|
116
191
|
return this.request('POST', '/v1/settings/telegram', data);
|
|
117
192
|
}
|
|
193
|
+
// ── Health ────────────────────────────────────────────────────────
|
|
194
|
+
/**
|
|
195
|
+
* Health Check de la API
|
|
196
|
+
*/
|
|
197
|
+
async health() {
|
|
198
|
+
return this.request('GET', '/health');
|
|
199
|
+
}
|
|
200
|
+
// ── Condición IVA Receptor ──────────────────────────────────────
|
|
201
|
+
/**
|
|
202
|
+
* Obtener tabla de condiciones IVA del receptor (15 códigos oficiales ARCA)
|
|
203
|
+
*/
|
|
204
|
+
async getCondicionesIva() {
|
|
205
|
+
return this.request('GET', '/v1/taxpayers/condiciones-iva');
|
|
206
|
+
}
|
|
207
|
+
// ── Cotización de Moneda ────────────────────────────────────────
|
|
208
|
+
/**
|
|
209
|
+
* Consultar cotización de una moneda extranjera vs peso (vía ARCA)
|
|
210
|
+
*/
|
|
211
|
+
async getCotizacionMoneda(moneda) {
|
|
212
|
+
return this.request('GET', `/v1/cotizacion/${moneda}`);
|
|
213
|
+
}
|
|
214
|
+
// ── Partners (ERP / SaaS) ────────────────────────────────────────
|
|
215
|
+
// Estos métodos requieren instanciar el cliente con tu API Key de
|
|
216
|
+
// Partner (sk_partner_live_...) — solo desde tu backend.
|
|
217
|
+
/**
|
|
218
|
+
* Crear una subcuenta completa (usuario + perfil + API Key) para un
|
|
219
|
+
* cliente final de tu ERP. La password y api_key se devuelven una sola vez.
|
|
220
|
+
*/
|
|
221
|
+
async createPartnerSubaccount(data) {
|
|
222
|
+
return this.request('POST', '/v1/partners/subaccounts', data);
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Listar los clientes vinculados a tu partner, con plan y consumo mensual.
|
|
226
|
+
*/
|
|
227
|
+
async listPartnerSubaccounts() {
|
|
228
|
+
return this.request('GET', '/v1/partners/subaccounts');
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Pre-autorizar el CUIT de un cliente para facturación consolidada
|
|
232
|
+
* (el cliente no pasará por la pasarela de pago al registrarse).
|
|
233
|
+
*/
|
|
234
|
+
async preauthorizeCuit(cuit) {
|
|
235
|
+
return this.request('POST', '/v1/partners/preauthorize', { cuit });
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Consultar la configuración pública de co-branding de un partner
|
|
239
|
+
* (endpoint público — no requiere autenticación).
|
|
240
|
+
*/
|
|
241
|
+
async getPartnerConfig(partnerId) {
|
|
242
|
+
return this.request('GET', `/v1/partners/${encodeURIComponent(partnerId)}/config`);
|
|
243
|
+
}
|
|
118
244
|
}
|
|
119
245
|
exports.KeyCaeClient = KeyCaeClient;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "keycae-ts",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Official TypeScript & JavaScript SDK client for KeyCAE.ar impositive compliance API (ARCA)",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -20,6 +20,11 @@
|
|
|
20
20
|
],
|
|
21
21
|
"author": "KeyCAE Community",
|
|
22
22
|
"license": "MIT",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "https://github.com/AMKRS3/keycae-packages",
|
|
26
|
+
"directory": "keycae-ts"
|
|
27
|
+
},
|
|
23
28
|
"dependencies": {
|
|
24
29
|
"node-fetch": "^2.6.9"
|
|
25
30
|
},
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import fetch from 'node-fetch';
|
|
2
2
|
|
|
3
|
+
// ════════════════════════════════════════════════════════════════════
|
|
4
|
+
// INTERFACES
|
|
5
|
+
// ════════════════════════════════════════════════════════════════════
|
|
6
|
+
|
|
7
|
+
export interface DomicilioFiscal {
|
|
8
|
+
direccion: string | null;
|
|
9
|
+
localidad: string | null;
|
|
10
|
+
cod_postal: string | null;
|
|
11
|
+
provincia: string | null;
|
|
12
|
+
}
|
|
13
|
+
|
|
3
14
|
export interface TaxpayerResponse {
|
|
4
15
|
cuit: string;
|
|
5
16
|
nombre: string;
|
|
@@ -7,26 +18,71 @@ export interface TaxpayerResponse {
|
|
|
7
18
|
categoria_monotributo?: string;
|
|
8
19
|
es_iva_inscripto: boolean;
|
|
9
20
|
estado: string;
|
|
21
|
+
condicion_iva?: string;
|
|
22
|
+
actividades?: string[];
|
|
23
|
+
domicilio_fiscal?: DomicilioFiscal;
|
|
10
24
|
}
|
|
11
25
|
|
|
12
26
|
export interface InvoiceReceptor {
|
|
13
|
-
tipo_doc: 'DNI' | 'CUIT' | 'CUIL' | 'PASAPORTE';
|
|
27
|
+
tipo_doc: 'DNI' | 'CUIT' | 'CUIL' | 'PASAPORTE' | 'SIN_IDENTIFICAR';
|
|
14
28
|
nro_doc: string;
|
|
29
|
+
razon_social?: string;
|
|
30
|
+
condicion_iva?: string;
|
|
15
31
|
}
|
|
16
32
|
|
|
17
33
|
export interface InvoiceItem {
|
|
18
34
|
descripcion: string;
|
|
19
35
|
precio: number;
|
|
36
|
+
cantidad?: number;
|
|
37
|
+
alicuota_iva?: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface Tributo {
|
|
41
|
+
id: number;
|
|
42
|
+
descripcion: string;
|
|
43
|
+
base_imponible: number;
|
|
44
|
+
alicuota: number;
|
|
45
|
+
importe: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface Opcional {
|
|
49
|
+
id: string;
|
|
50
|
+
valor: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface CondicionesIvaResponse {
|
|
54
|
+
condiciones: { codigo: number; nombre: string }[];
|
|
20
55
|
}
|
|
21
56
|
|
|
22
57
|
export interface InvoiceInput {
|
|
23
58
|
cuit_emisor: string;
|
|
24
59
|
punto_de_venta: number;
|
|
25
|
-
tipo_comprobante: 'A' | 'B' | 'C' | 'M'
|
|
60
|
+
tipo_comprobante: 'A' | 'B' | 'C' | 'M' | 'E'
|
|
61
|
+
| 'NCA' | 'NCB' | 'NCC' | 'NCE' | 'NCM'
|
|
62
|
+
| 'NDA' | 'NDB' | 'NDC' | 'NDE' | 'NDM'
|
|
63
|
+
| 'FCE_A' | 'FCE_B' | 'FCE_C'
|
|
64
|
+
| 'FCE_NDA' | 'FCE_NDB' | 'FCE_NDC'
|
|
65
|
+
| 'FCE_NCA' | 'FCE_NCB' | 'FCE_NCC';
|
|
26
66
|
receptor: InvoiceReceptor;
|
|
27
67
|
conceptos: InvoiceItem[];
|
|
68
|
+
tributos?: Tributo[];
|
|
69
|
+
moneda?: string;
|
|
70
|
+
moneda_cotizacion?: number;
|
|
71
|
+
fecha_comprobante?: string;
|
|
72
|
+
fecha_servicio_desde?: string;
|
|
73
|
+
fecha_servicio_hasta?: string;
|
|
74
|
+
fecha_vto_pago?: string;
|
|
75
|
+
condicion_iva_receptor?: number;
|
|
76
|
+
opcionales?: Opcional[];
|
|
28
77
|
brand_logo_url?: string;
|
|
29
78
|
brand_color?: string;
|
|
79
|
+
cbtes_asociados?: {
|
|
80
|
+
tipo: string | number;
|
|
81
|
+
punto_de_venta: number;
|
|
82
|
+
numero: number;
|
|
83
|
+
cuit?: string;
|
|
84
|
+
fecha?: string;
|
|
85
|
+
}[];
|
|
30
86
|
}
|
|
31
87
|
|
|
32
88
|
export interface InvoiceResponse {
|
|
@@ -72,7 +128,7 @@ export interface DelegationInput {
|
|
|
72
128
|
|
|
73
129
|
export interface DelegationResponse {
|
|
74
130
|
id: string;
|
|
75
|
-
status: 'pending' | 'accepted' | 'rejected';
|
|
131
|
+
status: 'pending' | 'accepted' | 'rejected' | 'pending_auto';
|
|
76
132
|
cuit: string;
|
|
77
133
|
organization: string;
|
|
78
134
|
representativeCuit: string;
|
|
@@ -104,13 +160,92 @@ export interface KmsImportInput {
|
|
|
104
160
|
private_key: string;
|
|
105
161
|
}
|
|
106
162
|
|
|
163
|
+
export interface PuntoDeVenta {
|
|
164
|
+
numero: number;
|
|
165
|
+
tipoEmision: string;
|
|
166
|
+
tipoAutomatizacion?: string;
|
|
167
|
+
fechaServicioDesde?: string;
|
|
168
|
+
fechaServicioHasta?: string;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export interface EmissionCapability {
|
|
172
|
+
cuit: string;
|
|
173
|
+
condicion_iva: string;
|
|
174
|
+
compatible_types: string[];
|
|
175
|
+
recommendation: string;
|
|
176
|
+
tip: string;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export interface HealthResponse {
|
|
180
|
+
status: string;
|
|
181
|
+
version?: string;
|
|
182
|
+
uptime?: number;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ── Partners (ERP / SaaS Integrations) ──────────────────────────────
|
|
186
|
+
|
|
187
|
+
export interface PartnerSubaccountInput {
|
|
188
|
+
/** CUIT fiscal del cliente final (11 dígitos, sin guiones) */
|
|
189
|
+
cuit: string;
|
|
190
|
+
/** Razón social del cliente final */
|
|
191
|
+
organization: string;
|
|
192
|
+
/** Email de acceso del cliente final */
|
|
193
|
+
email: string;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export interface PartnerSubaccountResponse {
|
|
197
|
+
id: string;
|
|
198
|
+
cuit: string;
|
|
199
|
+
organization: string;
|
|
200
|
+
email: string;
|
|
201
|
+
/** Contraseña generada — se devuelve UNA sola vez. Entregar por canal seguro. */
|
|
202
|
+
password: string;
|
|
203
|
+
/** API Key live de la subcuenta — se devuelve UNA sola vez. */
|
|
204
|
+
api_key: string;
|
|
205
|
+
partner_id: string;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export interface PartnerClient {
|
|
209
|
+
cuit: string;
|
|
210
|
+
organization: string;
|
|
211
|
+
email: string;
|
|
212
|
+
plan: 'free' | 'developer' | 'platform';
|
|
213
|
+
invoices_this_month: number;
|
|
214
|
+
created_at: string | null;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export interface PartnerClientsResponse {
|
|
218
|
+
partner_id: string;
|
|
219
|
+
total: number;
|
|
220
|
+
clients: PartnerClient[];
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export interface PartnerPreauthResponse {
|
|
224
|
+
success: boolean;
|
|
225
|
+
message: string;
|
|
226
|
+
partner_id: string;
|
|
227
|
+
cuit: string;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export interface PartnerPublicConfig {
|
|
231
|
+
id: string;
|
|
232
|
+
name: string;
|
|
233
|
+
logo_url: string;
|
|
234
|
+
brand_color: string;
|
|
235
|
+
billing_mode: 'decentralized' | 'consolidated';
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// ════════════════════════════════════════════════════════════════════
|
|
239
|
+
// CLIENT
|
|
240
|
+
// ════════════════════════════════════════════════════════════════════
|
|
241
|
+
|
|
107
242
|
export class KeyCaeClient {
|
|
108
243
|
private apiKey: string;
|
|
109
244
|
private baseUrl: string;
|
|
110
245
|
|
|
111
|
-
constructor(apiKey: string, baseUrl: string = 'https://
|
|
246
|
+
constructor(apiKey: string, baseUrl: string = 'https://keycae.ar') {
|
|
112
247
|
this.apiKey = apiKey;
|
|
113
|
-
this.baseUrl = baseUrl;
|
|
248
|
+
this.baseUrl = baseUrl.replace(/\/$/, '');
|
|
114
249
|
}
|
|
115
250
|
|
|
116
251
|
private async request<T>(
|
|
@@ -135,16 +270,23 @@ export class KeyCaeClient {
|
|
|
135
270
|
});
|
|
136
271
|
|
|
137
272
|
if (!response.ok) {
|
|
138
|
-
let
|
|
273
|
+
let errorData: any;
|
|
139
274
|
try {
|
|
140
|
-
|
|
141
|
-
} catch
|
|
142
|
-
|
|
275
|
+
errorData = await response.json();
|
|
276
|
+
} catch {
|
|
277
|
+
const text = await response.text().catch(() => '');
|
|
278
|
+
throw new Error(`KeyCAE API Error (${response.status}): ${text || response.statusText}`);
|
|
279
|
+
}
|
|
280
|
+
const message = errorData?.error?.message || JSON.stringify(errorData);
|
|
281
|
+
const hint = errorData?.error?.ai_action_hint || '';
|
|
282
|
+
throw new Error(`KeyCAE API Error (${response.status}): ${message}${hint ? ` [hint: ${hint}]` : ''}`);
|
|
143
283
|
}
|
|
144
284
|
|
|
145
285
|
return response.json() as Promise<T>;
|
|
146
286
|
}
|
|
147
287
|
|
|
288
|
+
// ── Taxpayers ─────────────────────────────────────────────────────
|
|
289
|
+
|
|
148
290
|
/**
|
|
149
291
|
* Consultar Contribuyente en Padrón ARCA
|
|
150
292
|
*/
|
|
@@ -152,6 +294,40 @@ export class KeyCaeClient {
|
|
|
152
294
|
return this.request<TaxpayerResponse>('GET', `/v1/taxpayers/${cuit}`);
|
|
153
295
|
}
|
|
154
296
|
|
|
297
|
+
/**
|
|
298
|
+
* Verificar qué tipos de factura puede emitir un CUIT según su condición fiscal
|
|
299
|
+
*/
|
|
300
|
+
async checkEmissionCapability(cuit: string): Promise<EmissionCapability> {
|
|
301
|
+
const taxpayer = await this.getTaxpayer(cuit);
|
|
302
|
+
const condition = taxpayer.condicion_iva || taxpayer.estado || '';
|
|
303
|
+
let compatibleTypes: string[] = [];
|
|
304
|
+
let recommendation = '';
|
|
305
|
+
|
|
306
|
+
if (condition.toLowerCase().includes('monotributo')) {
|
|
307
|
+
compatibleTypes = ['C', 'M'];
|
|
308
|
+
recommendation = 'Como Monotributista, podés emitir Factura C (exenta) o Factura M (monotributo). NO podés emitir Factura A o B.';
|
|
309
|
+
} else if (condition.toLowerCase().includes('responsable inscripto') || condition.toLowerCase().includes('ri')) {
|
|
310
|
+
compatibleTypes = ['A', 'B'];
|
|
311
|
+
recommendation = 'Como Responsable Inscripto, podés emitir Factura A (IVA discriminado) o Factura B (consumidor final).';
|
|
312
|
+
} else if (condition.toLowerCase().includes('exento')) {
|
|
313
|
+
compatibleTypes = ['C'];
|
|
314
|
+
recommendation = 'Como Exento, solo podés emitir Factura C (exenta).';
|
|
315
|
+
} else {
|
|
316
|
+
compatibleTypes = ['C'];
|
|
317
|
+
recommendation = 'No se pudo determinar la condición impositiva. Se recomienda Factura C por defecto.';
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return {
|
|
321
|
+
cuit,
|
|
322
|
+
condicion_iva: condition,
|
|
323
|
+
compatible_types: compatibleTypes,
|
|
324
|
+
recommendation,
|
|
325
|
+
tip: 'Usá estos tipos al emitir facturas con emitInvoice para evitar errores de ARCA.'
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// ── Delegations ───────────────────────────────────────────────────
|
|
330
|
+
|
|
155
331
|
/**
|
|
156
332
|
* Registrar / Iniciar Direct Delegation
|
|
157
333
|
*/
|
|
@@ -166,6 +342,8 @@ export class KeyCaeClient {
|
|
|
166
342
|
return this.request<DelegationResponse>('GET', '/v1/delegations/status');
|
|
167
343
|
}
|
|
168
344
|
|
|
345
|
+
// ── Credentials (KMS) ────────────────────────────────────────────
|
|
346
|
+
|
|
169
347
|
/**
|
|
170
348
|
* Crear Bóveda de Claves KMS & Generar CSR
|
|
171
349
|
*/
|
|
@@ -173,6 +351,13 @@ export class KeyCaeClient {
|
|
|
173
351
|
return this.request<KmsCredentialResponse>('POST', '/v1/credentials', data);
|
|
174
352
|
}
|
|
175
353
|
|
|
354
|
+
/**
|
|
355
|
+
* Listar Credenciales Digitales Registradas
|
|
356
|
+
*/
|
|
357
|
+
async listCredentials(): Promise<KmsCredentialResponse[]> {
|
|
358
|
+
return this.request<KmsCredentialResponse[]>('GET', '/v1/credentials');
|
|
359
|
+
}
|
|
360
|
+
|
|
176
361
|
/**
|
|
177
362
|
* Importar Credenciales de Firma Existentes (.key y .crt)
|
|
178
363
|
*/
|
|
@@ -194,6 +379,8 @@ export class KeyCaeClient {
|
|
|
194
379
|
return this.request<any>('DELETE', `/v1/credentials/${id}`);
|
|
195
380
|
}
|
|
196
381
|
|
|
382
|
+
// ── Invoices ──────────────────────────────────────────────────────
|
|
383
|
+
|
|
197
384
|
/**
|
|
198
385
|
* Emitir Factura Electrónica Homologada (CAE)
|
|
199
386
|
*/
|
|
@@ -201,11 +388,30 @@ export class KeyCaeClient {
|
|
|
201
388
|
return this.request<InvoiceResponse>('POST', '/v1/invoices', data, idempotencyKey);
|
|
202
389
|
}
|
|
203
390
|
|
|
391
|
+
/**
|
|
392
|
+
* Obtener Detalles de una Factura por ID
|
|
393
|
+
*/
|
|
394
|
+
async getInvoice(id: string): Promise<InvoiceResponse> {
|
|
395
|
+
return this.request<InvoiceResponse>('GET', `/v1/invoices/${id}`);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Listar Facturas Recientes
|
|
400
|
+
*/
|
|
401
|
+
async listInvoices(limit: number = 10, offset: number = 0): Promise<{ invoices: InvoiceResponse[]; total: number }> {
|
|
402
|
+
const params = new URLSearchParams({ limit: String(limit), offset: String(offset) });
|
|
403
|
+
return this.request<any>('GET', `/v1/invoices?${params}`);
|
|
404
|
+
}
|
|
405
|
+
|
|
204
406
|
/**
|
|
205
407
|
* Descargar PDF de Factura (Retorna el Stream / ArrayBuffer)
|
|
206
408
|
*/
|
|
207
|
-
async getInvoicePdfBuffer(id: string): Promise<Buffer> {
|
|
208
|
-
const
|
|
409
|
+
async getInvoicePdfBuffer(id: string, options?: { format?: 'a4' | 'ticket' }): Promise<Buffer> {
|
|
410
|
+
const url = new URL(`${this.baseUrl}/v1/invoices/${id}/pdf`);
|
|
411
|
+
if (options?.format) {
|
|
412
|
+
url.searchParams.append('format', options.format);
|
|
413
|
+
}
|
|
414
|
+
const response = await fetch(url.toString(), {
|
|
209
415
|
method: 'GET',
|
|
210
416
|
headers: {
|
|
211
417
|
'Authorization': `Bearer ${this.apiKey}`
|
|
@@ -216,10 +422,20 @@ export class KeyCaeClient {
|
|
|
216
422
|
throw new Error(`Failed to fetch PDF (${response.status})`);
|
|
217
423
|
}
|
|
218
424
|
|
|
219
|
-
|
|
220
|
-
return Buffer.from(arrayBuffer);
|
|
425
|
+
return Buffer.from(await response.arrayBuffer());
|
|
221
426
|
}
|
|
222
427
|
|
|
428
|
+
// ── Puntos de Venta ───────────────────────────────────────────────
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Listar Puntos de Venta Habilitados para Facturación Electrónica
|
|
432
|
+
*/
|
|
433
|
+
async listPuntosDeVenta(): Promise<PuntoDeVenta[]> {
|
|
434
|
+
return this.request<PuntoDeVenta[]>('GET', '/v1/pos');
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// ── Billing ───────────────────────────────────────────────────────
|
|
438
|
+
|
|
223
439
|
/**
|
|
224
440
|
* Obtener Consumos y Estado del Plan (Billing)
|
|
225
441
|
*/
|
|
@@ -227,6 +443,8 @@ export class KeyCaeClient {
|
|
|
227
443
|
return this.request<BillingStatusResponse>('GET', '/v1/billing/status');
|
|
228
444
|
}
|
|
229
445
|
|
|
446
|
+
// ── Telegram Settings ─────────────────────────────────────────────
|
|
447
|
+
|
|
230
448
|
/**
|
|
231
449
|
* Obtener Ajustes de Telegram por Tenant
|
|
232
450
|
*/
|
|
@@ -240,4 +458,66 @@ export class KeyCaeClient {
|
|
|
240
458
|
async saveTelegramSettings(data: TelegramSettingsInput): Promise<any> {
|
|
241
459
|
return this.request<any>('POST', '/v1/settings/telegram', data);
|
|
242
460
|
}
|
|
461
|
+
|
|
462
|
+
// ── Health ────────────────────────────────────────────────────────
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Health Check de la API
|
|
466
|
+
*/
|
|
467
|
+
async health(): Promise<HealthResponse> {
|
|
468
|
+
return this.request<HealthResponse>('GET', '/health');
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// ── Condición IVA Receptor ──────────────────────────────────────
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Obtener tabla de condiciones IVA del receptor (15 códigos oficiales ARCA)
|
|
475
|
+
*/
|
|
476
|
+
async getCondicionesIva(): Promise<CondicionesIvaResponse> {
|
|
477
|
+
return this.request<CondicionesIvaResponse>('GET', '/v1/taxpayers/condiciones-iva');
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// ── Cotización de Moneda ────────────────────────────────────────
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Consultar cotización de una moneda extranjera vs peso (vía ARCA)
|
|
484
|
+
*/
|
|
485
|
+
async getCotizacionMoneda(moneda: string): Promise<{ moneda: string; cotizacion: number }> {
|
|
486
|
+
return this.request<any>('GET', `/v1/cotizacion/${moneda}`);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// ── Partners (ERP / SaaS) ────────────────────────────────────────
|
|
490
|
+
// Estos métodos requieren instanciar el cliente con tu API Key de
|
|
491
|
+
// Partner (sk_partner_live_...) — solo desde tu backend.
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Crear una subcuenta completa (usuario + perfil + API Key) para un
|
|
495
|
+
* cliente final de tu ERP. La password y api_key se devuelven una sola vez.
|
|
496
|
+
*/
|
|
497
|
+
async createPartnerSubaccount(data: PartnerSubaccountInput): Promise<PartnerSubaccountResponse> {
|
|
498
|
+
return this.request<PartnerSubaccountResponse>('POST', '/v1/partners/subaccounts', data);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Listar los clientes vinculados a tu partner, con plan y consumo mensual.
|
|
503
|
+
*/
|
|
504
|
+
async listPartnerSubaccounts(): Promise<PartnerClientsResponse> {
|
|
505
|
+
return this.request<PartnerClientsResponse>('GET', '/v1/partners/subaccounts');
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Pre-autorizar el CUIT de un cliente para facturación consolidada
|
|
510
|
+
* (el cliente no pasará por la pasarela de pago al registrarse).
|
|
511
|
+
*/
|
|
512
|
+
async preauthorizeCuit(cuit: string): Promise<PartnerPreauthResponse> {
|
|
513
|
+
return this.request<PartnerPreauthResponse>('POST', '/v1/partners/preauthorize', { cuit });
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Consultar la configuración pública de co-branding de un partner
|
|
518
|
+
* (endpoint público — no requiere autenticación).
|
|
519
|
+
*/
|
|
520
|
+
async getPartnerConfig(partnerId: string): Promise<PartnerPublicConfig> {
|
|
521
|
+
return this.request<PartnerPublicConfig>('GET', `/v1/partners/${encodeURIComponent(partnerId)}/config`);
|
|
522
|
+
}
|
|
243
523
|
}
|