factuplan 0.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/README.md +199 -0
- package/dist/index.d.mts +218 -0
- package/dist/index.d.ts +218 -0
- package/dist/index.js +191 -0
- package/dist/index.mjs +161 -0
- package/package.json +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# Factuplan
|
|
2
|
+
|
|
3
|
+
SDK oficial de [Factuplan](https://tufactura.com) para JavaScript y TypeScript. Emite facturas electronicas en Ecuador (SRI) desde tu aplicacion.
|
|
4
|
+
|
|
5
|
+
## Instalacion
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install factuplan
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Inicio rapido
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { Factuplan } from 'factuplan';
|
|
15
|
+
|
|
16
|
+
const factuplan = new Factuplan('tu-api-key');
|
|
17
|
+
|
|
18
|
+
// Crear una factura
|
|
19
|
+
const invoice = await factuplan.invoices.create({
|
|
20
|
+
emissionPointId: 'ep_...',
|
|
21
|
+
customer: {
|
|
22
|
+
identificationType: 'RUC',
|
|
23
|
+
identification: '1790016919001',
|
|
24
|
+
legalName: 'Empresa ABC S.A.',
|
|
25
|
+
email: 'contabilidad@empresa.com',
|
|
26
|
+
},
|
|
27
|
+
items: [
|
|
28
|
+
{
|
|
29
|
+
code: 'SERV-001',
|
|
30
|
+
description: 'Servicio de consultoria',
|
|
31
|
+
quantity: 1,
|
|
32
|
+
unitPrice: 100.00,
|
|
33
|
+
taxType: 'IVA_RATE',
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
console.log(invoice.accessKey); // Clave de acceso SRI
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Recursos
|
|
42
|
+
|
|
43
|
+
### Clientes
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
// Crear cliente
|
|
47
|
+
const customer = await factuplan.customers.create({
|
|
48
|
+
identificationType: 'RUC',
|
|
49
|
+
identification: '1790016919001',
|
|
50
|
+
legalName: 'Empresa ABC S.A.',
|
|
51
|
+
email: 'info@empresa.com',
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Listar clientes (paginado)
|
|
55
|
+
const { data, meta } = await factuplan.customers.list({ page: 1, limit: 20 });
|
|
56
|
+
|
|
57
|
+
// Obtener cliente por ID
|
|
58
|
+
const customer = await factuplan.customers.get('customer-id');
|
|
59
|
+
|
|
60
|
+
// Actualizar cliente
|
|
61
|
+
await factuplan.customers.update('customer-id', { phone: '0991234567' });
|
|
62
|
+
|
|
63
|
+
// Eliminar cliente
|
|
64
|
+
await factuplan.customers.delete('customer-id');
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Productos
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
// Crear producto
|
|
71
|
+
const product = await factuplan.products.create({
|
|
72
|
+
code: 'PROD-001',
|
|
73
|
+
name: 'Laptop Dell XPS',
|
|
74
|
+
unitPrice: 1200.00,
|
|
75
|
+
taxType: 'IVA_RATE',
|
|
76
|
+
type: 'PRODUCT',
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Listar productos (paginado)
|
|
80
|
+
const { data, meta } = await factuplan.products.list({ page: 1, limit: 20 });
|
|
81
|
+
|
|
82
|
+
// Obtener producto por ID
|
|
83
|
+
const product = await factuplan.products.get('product-id');
|
|
84
|
+
|
|
85
|
+
// Actualizar producto
|
|
86
|
+
await factuplan.products.update('product-id', { unitPrice: 1150.00 });
|
|
87
|
+
|
|
88
|
+
// Eliminar producto
|
|
89
|
+
await factuplan.products.delete('product-id');
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Facturas
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
// Crear factura
|
|
96
|
+
const invoice = await factuplan.invoices.create({
|
|
97
|
+
emissionPointId: 'ep_...',
|
|
98
|
+
customer: {
|
|
99
|
+
identificationType: 'CEDULA',
|
|
100
|
+
identification: '1712345678',
|
|
101
|
+
legalName: 'Juan Perez',
|
|
102
|
+
email: 'juan@email.com',
|
|
103
|
+
saveToContacts: true,
|
|
104
|
+
},
|
|
105
|
+
items: [
|
|
106
|
+
{
|
|
107
|
+
code: 'SERV-001',
|
|
108
|
+
description: 'Desarrollo web',
|
|
109
|
+
quantity: 10,
|
|
110
|
+
unitPrice: 50.00,
|
|
111
|
+
discount: 25.00,
|
|
112
|
+
taxType: 'IVA_RATE',
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
paymentMethod: 'TRANSFER',
|
|
116
|
+
additionalInfo: {
|
|
117
|
+
'Orden de compra': 'OC-2024-001',
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// Consultar estado
|
|
122
|
+
const status = await factuplan.invoices.getStatus('invoice-id');
|
|
123
|
+
console.log(status.status); // 'AUTHORIZED', 'PENDING', etc.
|
|
124
|
+
|
|
125
|
+
// Descargar XML autorizado
|
|
126
|
+
const { url } = await factuplan.invoices.downloadXml('invoice-id');
|
|
127
|
+
|
|
128
|
+
// Descargar PDF (RIDE)
|
|
129
|
+
const { url } = await factuplan.invoices.downloadPdf('invoice-id');
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Uso y certificado
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
// Consultar uso de la API
|
|
136
|
+
const usage = await factuplan.usage();
|
|
137
|
+
console.log(`${usage.used}/${usage.quota} facturas este mes`);
|
|
138
|
+
|
|
139
|
+
// Estado del certificado de firma
|
|
140
|
+
const cert = await factuplan.certificateStatus();
|
|
141
|
+
console.log(`Expira en ${cert.daysUntilExpiry} dias`);
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Configuracion
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
const factuplan = new Factuplan('tu-api-key', {
|
|
148
|
+
baseUrl: 'https://api.factuplan.com/api/v1', // default
|
|
149
|
+
timeout: 30000, // 30s default
|
|
150
|
+
});
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Manejo de errores
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
import { Factuplan, FactuplanError, AuthenticationError, RateLimitError } from 'factuplan';
|
|
157
|
+
|
|
158
|
+
try {
|
|
159
|
+
await factuplan.invoices.create({ /* ... */ });
|
|
160
|
+
} catch (error) {
|
|
161
|
+
if (error instanceof AuthenticationError) {
|
|
162
|
+
// API key invalida (401)
|
|
163
|
+
} else if (error instanceof RateLimitError) {
|
|
164
|
+
// Limite de peticiones excedido (429)
|
|
165
|
+
} else if (error instanceof FactuplanError) {
|
|
166
|
+
console.error(error.message); // Mensaje de error
|
|
167
|
+
console.error(error.statusCode); // Codigo HTTP
|
|
168
|
+
console.error(error.code); // Codigo de error interno
|
|
169
|
+
console.error(error.details); // Detalles adicionales
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Tipos de identificacion
|
|
175
|
+
|
|
176
|
+
| Tipo | Descripcion |
|
|
177
|
+
|------|-------------|
|
|
178
|
+
| `RUC` | Registro Unico de Contribuyentes (13 digitos) |
|
|
179
|
+
| `CEDULA` | Cedula de identidad (10 digitos) |
|
|
180
|
+
| `PASSPORT` | Pasaporte |
|
|
181
|
+
| `FINAL_CONSUMER` | Consumidor final (9999999999999) |
|
|
182
|
+
|
|
183
|
+
## Tipos de impuesto
|
|
184
|
+
|
|
185
|
+
| Tipo | Descripcion |
|
|
186
|
+
|------|-------------|
|
|
187
|
+
| `IVA_RATE` | IVA 15% (tarifa vigente) |
|
|
188
|
+
| `IVA_0` | IVA 0% |
|
|
189
|
+
| `NOT_TAXABLE` | No objeto de impuesto |
|
|
190
|
+
| `EXEMPT` | Exento de IVA |
|
|
191
|
+
|
|
192
|
+
## Requisitos
|
|
193
|
+
|
|
194
|
+
- Node.js 18+ (usa `fetch` nativo)
|
|
195
|
+
- API key de [Factuplan](https://tufactura.com)
|
|
196
|
+
|
|
197
|
+
## Licencia
|
|
198
|
+
|
|
199
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
interface PaginatedResponse<T> {
|
|
2
|
+
data: T[];
|
|
3
|
+
meta: {
|
|
4
|
+
total: number;
|
|
5
|
+
page: number;
|
|
6
|
+
limit: number;
|
|
7
|
+
totalPages: number;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
interface Customer {
|
|
11
|
+
id: string;
|
|
12
|
+
identificationType: 'RUC' | 'CEDULA' | 'PASSPORT' | 'FINAL_CONSUMER';
|
|
13
|
+
identification: string;
|
|
14
|
+
legalName: string;
|
|
15
|
+
email?: string;
|
|
16
|
+
additionalEmail?: string;
|
|
17
|
+
phone?: string;
|
|
18
|
+
address?: string;
|
|
19
|
+
createdAt: string;
|
|
20
|
+
updatedAt: string;
|
|
21
|
+
}
|
|
22
|
+
interface CreateCustomerInput {
|
|
23
|
+
identificationType: 'RUC' | 'CEDULA' | 'PASSPORT' | 'FINAL_CONSUMER';
|
|
24
|
+
identification: string;
|
|
25
|
+
legalName: string;
|
|
26
|
+
email?: string;
|
|
27
|
+
additionalEmail?: string;
|
|
28
|
+
phone?: string;
|
|
29
|
+
address?: string;
|
|
30
|
+
}
|
|
31
|
+
interface UpdateCustomerInput {
|
|
32
|
+
legalName?: string;
|
|
33
|
+
email?: string;
|
|
34
|
+
additionalEmail?: string;
|
|
35
|
+
phone?: string;
|
|
36
|
+
address?: string;
|
|
37
|
+
}
|
|
38
|
+
interface CustomerListParams {
|
|
39
|
+
page?: number;
|
|
40
|
+
limit?: number;
|
|
41
|
+
search?: string;
|
|
42
|
+
}
|
|
43
|
+
interface Product {
|
|
44
|
+
id: string;
|
|
45
|
+
code: string;
|
|
46
|
+
auxiliaryCode?: string;
|
|
47
|
+
type: 'PRODUCT' | 'SERVICE';
|
|
48
|
+
name: string;
|
|
49
|
+
description?: string;
|
|
50
|
+
unitPrice: number | string;
|
|
51
|
+
taxType: 'IVA_0' | 'IVA_RATE' | 'NOT_TAXABLE' | 'EXEMPT';
|
|
52
|
+
iceCode?: string;
|
|
53
|
+
iceRate?: number | string;
|
|
54
|
+
irbpnr: boolean;
|
|
55
|
+
unitOfMeasure?: string;
|
|
56
|
+
isActive: boolean;
|
|
57
|
+
createdAt: string;
|
|
58
|
+
updatedAt: string;
|
|
59
|
+
}
|
|
60
|
+
interface CreateProductInput {
|
|
61
|
+
code: string;
|
|
62
|
+
auxiliaryCode?: string;
|
|
63
|
+
type?: 'PRODUCT' | 'SERVICE';
|
|
64
|
+
name: string;
|
|
65
|
+
description?: string;
|
|
66
|
+
unitPrice: number;
|
|
67
|
+
taxType?: 'IVA_0' | 'IVA_RATE' | 'NOT_TAXABLE' | 'EXEMPT';
|
|
68
|
+
iceCode?: string;
|
|
69
|
+
iceRate?: number;
|
|
70
|
+
irbpnr?: boolean;
|
|
71
|
+
unitOfMeasure?: string;
|
|
72
|
+
}
|
|
73
|
+
interface UpdateProductInput {
|
|
74
|
+
name?: string;
|
|
75
|
+
description?: string;
|
|
76
|
+
unitPrice?: number;
|
|
77
|
+
taxType?: 'IVA_0' | 'IVA_RATE' | 'NOT_TAXABLE' | 'EXEMPT';
|
|
78
|
+
type?: 'PRODUCT' | 'SERVICE';
|
|
79
|
+
unitOfMeasure?: string;
|
|
80
|
+
isActive?: boolean;
|
|
81
|
+
}
|
|
82
|
+
interface ProductListParams {
|
|
83
|
+
page?: number;
|
|
84
|
+
limit?: number;
|
|
85
|
+
search?: string;
|
|
86
|
+
}
|
|
87
|
+
interface InvoiceCustomer {
|
|
88
|
+
identificationType: 'RUC' | 'CEDULA' | 'PASSPORT' | 'FINAL_CONSUMER';
|
|
89
|
+
identification: string;
|
|
90
|
+
legalName: string;
|
|
91
|
+
email?: string;
|
|
92
|
+
address?: string;
|
|
93
|
+
phone?: string;
|
|
94
|
+
saveToContacts?: boolean;
|
|
95
|
+
}
|
|
96
|
+
interface InvoiceItem {
|
|
97
|
+
code: string;
|
|
98
|
+
description: string;
|
|
99
|
+
quantity: number;
|
|
100
|
+
unitPrice: number;
|
|
101
|
+
discount?: number;
|
|
102
|
+
taxType?: 'IVA_0' | 'IVA_RATE' | 'NOT_TAXABLE' | 'EXEMPT';
|
|
103
|
+
/** Tax rate override for IVA_RATE. Valid: 0, 5, 8, 12, 14, 15. Default: 15 */
|
|
104
|
+
tax?: number;
|
|
105
|
+
}
|
|
106
|
+
interface CreateInvoiceInput {
|
|
107
|
+
/** UUID of the emission point. Optional if using establishment + emissionPoint codes, or if the taxpayer has a single active emission point. */
|
|
108
|
+
emissionPointId?: string;
|
|
109
|
+
/** SRI establishment code (e.g. "001"). Must be used together with emissionPoint. */
|
|
110
|
+
establishment?: string;
|
|
111
|
+
/** SRI emission point code (e.g. "001"). Must be used together with establishment. */
|
|
112
|
+
emissionPoint?: string;
|
|
113
|
+
customer: InvoiceCustomer;
|
|
114
|
+
items: InvoiceItem[];
|
|
115
|
+
paymentMethod?: string;
|
|
116
|
+
additionalInfo?: Record<string, string>;
|
|
117
|
+
}
|
|
118
|
+
interface Invoice {
|
|
119
|
+
id: string;
|
|
120
|
+
accessKey: string;
|
|
121
|
+
sequential: string;
|
|
122
|
+
status: string;
|
|
123
|
+
total: number | string;
|
|
124
|
+
}
|
|
125
|
+
interface InvoiceStatus {
|
|
126
|
+
id: string;
|
|
127
|
+
status: string;
|
|
128
|
+
accessKey?: string;
|
|
129
|
+
authorizationNumber?: string;
|
|
130
|
+
authorizationDate?: string;
|
|
131
|
+
}
|
|
132
|
+
interface DownloadUrlResponse {
|
|
133
|
+
url: string;
|
|
134
|
+
s3Key?: string;
|
|
135
|
+
}
|
|
136
|
+
interface UsageResponse {
|
|
137
|
+
apiKeyId: string;
|
|
138
|
+
month: string;
|
|
139
|
+
quota: number;
|
|
140
|
+
used: number;
|
|
141
|
+
remaining: number;
|
|
142
|
+
costs: Record<string, unknown>;
|
|
143
|
+
}
|
|
144
|
+
interface CertificateStatus {
|
|
145
|
+
hasCertificate: boolean;
|
|
146
|
+
isExpired?: boolean;
|
|
147
|
+
daysUntilExpiry?: number | null;
|
|
148
|
+
ruc?: string;
|
|
149
|
+
legalName?: string;
|
|
150
|
+
expiresAt?: string;
|
|
151
|
+
}
|
|
152
|
+
interface FactuplanOptions {
|
|
153
|
+
/** Base URL of the API (default: https://api.factuplan.com/api/v1) */
|
|
154
|
+
baseUrl?: string;
|
|
155
|
+
/** Request timeout in milliseconds (default: 30000) */
|
|
156
|
+
timeout?: number;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
type Requester$2 = (method: string, path: string, body?: unknown, params?: Record<string, string | number | undefined>) => Promise<unknown>;
|
|
160
|
+
declare class CustomersResource {
|
|
161
|
+
private request;
|
|
162
|
+
constructor(request: Requester$2);
|
|
163
|
+
create(input: CreateCustomerInput): Promise<Customer>;
|
|
164
|
+
list(params?: CustomerListParams): Promise<PaginatedResponse<Customer>>;
|
|
165
|
+
get(id: string): Promise<Customer>;
|
|
166
|
+
update(id: string, input: UpdateCustomerInput): Promise<Customer>;
|
|
167
|
+
delete(id: string): Promise<void>;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
type Requester$1 = (method: string, path: string, body?: unknown, params?: Record<string, string | number | undefined>) => Promise<unknown>;
|
|
171
|
+
declare class ProductsResource {
|
|
172
|
+
private request;
|
|
173
|
+
constructor(request: Requester$1);
|
|
174
|
+
create(input: CreateProductInput): Promise<Product>;
|
|
175
|
+
list(params?: ProductListParams): Promise<PaginatedResponse<Product>>;
|
|
176
|
+
get(id: string): Promise<Product>;
|
|
177
|
+
update(id: string, input: UpdateProductInput): Promise<Product>;
|
|
178
|
+
delete(id: string): Promise<void>;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
type Requester = (method: string, path: string, body?: unknown) => Promise<unknown>;
|
|
182
|
+
declare class InvoicesResource {
|
|
183
|
+
private request;
|
|
184
|
+
constructor(request: Requester);
|
|
185
|
+
create(input: CreateInvoiceInput): Promise<Invoice>;
|
|
186
|
+
get(id: string): Promise<Record<string, unknown>>;
|
|
187
|
+
getStatus(id: string): Promise<InvoiceStatus>;
|
|
188
|
+
downloadXml(id: string): Promise<DownloadUrlResponse>;
|
|
189
|
+
downloadPdf(id: string): Promise<DownloadUrlResponse>;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
declare class Factuplan {
|
|
193
|
+
private readonly apiKey;
|
|
194
|
+
private readonly baseUrl;
|
|
195
|
+
private readonly timeout;
|
|
196
|
+
readonly customers: CustomersResource;
|
|
197
|
+
readonly products: ProductsResource;
|
|
198
|
+
readonly invoices: InvoicesResource;
|
|
199
|
+
constructor(apiKey: string, options?: FactuplanOptions);
|
|
200
|
+
usage(): Promise<UsageResponse>;
|
|
201
|
+
certificateStatus(): Promise<CertificateStatus>;
|
|
202
|
+
private request;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
declare class FactuplanError extends Error {
|
|
206
|
+
readonly statusCode: number;
|
|
207
|
+
readonly code: string;
|
|
208
|
+
readonly details?: Record<string, unknown>;
|
|
209
|
+
constructor(message: string, statusCode: number, code: string, details?: Record<string, unknown>);
|
|
210
|
+
}
|
|
211
|
+
declare class AuthenticationError extends FactuplanError {
|
|
212
|
+
constructor(message?: string);
|
|
213
|
+
}
|
|
214
|
+
declare class RateLimitError extends FactuplanError {
|
|
215
|
+
constructor(message?: string);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export { AuthenticationError, type CertificateStatus, type CreateCustomerInput, type CreateInvoiceInput, type CreateProductInput, type Customer, type CustomerListParams, type DownloadUrlResponse, Factuplan, FactuplanError, type FactuplanOptions, type Invoice, type InvoiceCustomer, type InvoiceItem, type InvoiceStatus, type PaginatedResponse, type Product, type ProductListParams, RateLimitError, type UpdateCustomerInput, type UpdateProductInput, type UsageResponse };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
interface PaginatedResponse<T> {
|
|
2
|
+
data: T[];
|
|
3
|
+
meta: {
|
|
4
|
+
total: number;
|
|
5
|
+
page: number;
|
|
6
|
+
limit: number;
|
|
7
|
+
totalPages: number;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
interface Customer {
|
|
11
|
+
id: string;
|
|
12
|
+
identificationType: 'RUC' | 'CEDULA' | 'PASSPORT' | 'FINAL_CONSUMER';
|
|
13
|
+
identification: string;
|
|
14
|
+
legalName: string;
|
|
15
|
+
email?: string;
|
|
16
|
+
additionalEmail?: string;
|
|
17
|
+
phone?: string;
|
|
18
|
+
address?: string;
|
|
19
|
+
createdAt: string;
|
|
20
|
+
updatedAt: string;
|
|
21
|
+
}
|
|
22
|
+
interface CreateCustomerInput {
|
|
23
|
+
identificationType: 'RUC' | 'CEDULA' | 'PASSPORT' | 'FINAL_CONSUMER';
|
|
24
|
+
identification: string;
|
|
25
|
+
legalName: string;
|
|
26
|
+
email?: string;
|
|
27
|
+
additionalEmail?: string;
|
|
28
|
+
phone?: string;
|
|
29
|
+
address?: string;
|
|
30
|
+
}
|
|
31
|
+
interface UpdateCustomerInput {
|
|
32
|
+
legalName?: string;
|
|
33
|
+
email?: string;
|
|
34
|
+
additionalEmail?: string;
|
|
35
|
+
phone?: string;
|
|
36
|
+
address?: string;
|
|
37
|
+
}
|
|
38
|
+
interface CustomerListParams {
|
|
39
|
+
page?: number;
|
|
40
|
+
limit?: number;
|
|
41
|
+
search?: string;
|
|
42
|
+
}
|
|
43
|
+
interface Product {
|
|
44
|
+
id: string;
|
|
45
|
+
code: string;
|
|
46
|
+
auxiliaryCode?: string;
|
|
47
|
+
type: 'PRODUCT' | 'SERVICE';
|
|
48
|
+
name: string;
|
|
49
|
+
description?: string;
|
|
50
|
+
unitPrice: number | string;
|
|
51
|
+
taxType: 'IVA_0' | 'IVA_RATE' | 'NOT_TAXABLE' | 'EXEMPT';
|
|
52
|
+
iceCode?: string;
|
|
53
|
+
iceRate?: number | string;
|
|
54
|
+
irbpnr: boolean;
|
|
55
|
+
unitOfMeasure?: string;
|
|
56
|
+
isActive: boolean;
|
|
57
|
+
createdAt: string;
|
|
58
|
+
updatedAt: string;
|
|
59
|
+
}
|
|
60
|
+
interface CreateProductInput {
|
|
61
|
+
code: string;
|
|
62
|
+
auxiliaryCode?: string;
|
|
63
|
+
type?: 'PRODUCT' | 'SERVICE';
|
|
64
|
+
name: string;
|
|
65
|
+
description?: string;
|
|
66
|
+
unitPrice: number;
|
|
67
|
+
taxType?: 'IVA_0' | 'IVA_RATE' | 'NOT_TAXABLE' | 'EXEMPT';
|
|
68
|
+
iceCode?: string;
|
|
69
|
+
iceRate?: number;
|
|
70
|
+
irbpnr?: boolean;
|
|
71
|
+
unitOfMeasure?: string;
|
|
72
|
+
}
|
|
73
|
+
interface UpdateProductInput {
|
|
74
|
+
name?: string;
|
|
75
|
+
description?: string;
|
|
76
|
+
unitPrice?: number;
|
|
77
|
+
taxType?: 'IVA_0' | 'IVA_RATE' | 'NOT_TAXABLE' | 'EXEMPT';
|
|
78
|
+
type?: 'PRODUCT' | 'SERVICE';
|
|
79
|
+
unitOfMeasure?: string;
|
|
80
|
+
isActive?: boolean;
|
|
81
|
+
}
|
|
82
|
+
interface ProductListParams {
|
|
83
|
+
page?: number;
|
|
84
|
+
limit?: number;
|
|
85
|
+
search?: string;
|
|
86
|
+
}
|
|
87
|
+
interface InvoiceCustomer {
|
|
88
|
+
identificationType: 'RUC' | 'CEDULA' | 'PASSPORT' | 'FINAL_CONSUMER';
|
|
89
|
+
identification: string;
|
|
90
|
+
legalName: string;
|
|
91
|
+
email?: string;
|
|
92
|
+
address?: string;
|
|
93
|
+
phone?: string;
|
|
94
|
+
saveToContacts?: boolean;
|
|
95
|
+
}
|
|
96
|
+
interface InvoiceItem {
|
|
97
|
+
code: string;
|
|
98
|
+
description: string;
|
|
99
|
+
quantity: number;
|
|
100
|
+
unitPrice: number;
|
|
101
|
+
discount?: number;
|
|
102
|
+
taxType?: 'IVA_0' | 'IVA_RATE' | 'NOT_TAXABLE' | 'EXEMPT';
|
|
103
|
+
/** Tax rate override for IVA_RATE. Valid: 0, 5, 8, 12, 14, 15. Default: 15 */
|
|
104
|
+
tax?: number;
|
|
105
|
+
}
|
|
106
|
+
interface CreateInvoiceInput {
|
|
107
|
+
/** UUID of the emission point. Optional if using establishment + emissionPoint codes, or if the taxpayer has a single active emission point. */
|
|
108
|
+
emissionPointId?: string;
|
|
109
|
+
/** SRI establishment code (e.g. "001"). Must be used together with emissionPoint. */
|
|
110
|
+
establishment?: string;
|
|
111
|
+
/** SRI emission point code (e.g. "001"). Must be used together with establishment. */
|
|
112
|
+
emissionPoint?: string;
|
|
113
|
+
customer: InvoiceCustomer;
|
|
114
|
+
items: InvoiceItem[];
|
|
115
|
+
paymentMethod?: string;
|
|
116
|
+
additionalInfo?: Record<string, string>;
|
|
117
|
+
}
|
|
118
|
+
interface Invoice {
|
|
119
|
+
id: string;
|
|
120
|
+
accessKey: string;
|
|
121
|
+
sequential: string;
|
|
122
|
+
status: string;
|
|
123
|
+
total: number | string;
|
|
124
|
+
}
|
|
125
|
+
interface InvoiceStatus {
|
|
126
|
+
id: string;
|
|
127
|
+
status: string;
|
|
128
|
+
accessKey?: string;
|
|
129
|
+
authorizationNumber?: string;
|
|
130
|
+
authorizationDate?: string;
|
|
131
|
+
}
|
|
132
|
+
interface DownloadUrlResponse {
|
|
133
|
+
url: string;
|
|
134
|
+
s3Key?: string;
|
|
135
|
+
}
|
|
136
|
+
interface UsageResponse {
|
|
137
|
+
apiKeyId: string;
|
|
138
|
+
month: string;
|
|
139
|
+
quota: number;
|
|
140
|
+
used: number;
|
|
141
|
+
remaining: number;
|
|
142
|
+
costs: Record<string, unknown>;
|
|
143
|
+
}
|
|
144
|
+
interface CertificateStatus {
|
|
145
|
+
hasCertificate: boolean;
|
|
146
|
+
isExpired?: boolean;
|
|
147
|
+
daysUntilExpiry?: number | null;
|
|
148
|
+
ruc?: string;
|
|
149
|
+
legalName?: string;
|
|
150
|
+
expiresAt?: string;
|
|
151
|
+
}
|
|
152
|
+
interface FactuplanOptions {
|
|
153
|
+
/** Base URL of the API (default: https://api.factuplan.com/api/v1) */
|
|
154
|
+
baseUrl?: string;
|
|
155
|
+
/** Request timeout in milliseconds (default: 30000) */
|
|
156
|
+
timeout?: number;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
type Requester$2 = (method: string, path: string, body?: unknown, params?: Record<string, string | number | undefined>) => Promise<unknown>;
|
|
160
|
+
declare class CustomersResource {
|
|
161
|
+
private request;
|
|
162
|
+
constructor(request: Requester$2);
|
|
163
|
+
create(input: CreateCustomerInput): Promise<Customer>;
|
|
164
|
+
list(params?: CustomerListParams): Promise<PaginatedResponse<Customer>>;
|
|
165
|
+
get(id: string): Promise<Customer>;
|
|
166
|
+
update(id: string, input: UpdateCustomerInput): Promise<Customer>;
|
|
167
|
+
delete(id: string): Promise<void>;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
type Requester$1 = (method: string, path: string, body?: unknown, params?: Record<string, string | number | undefined>) => Promise<unknown>;
|
|
171
|
+
declare class ProductsResource {
|
|
172
|
+
private request;
|
|
173
|
+
constructor(request: Requester$1);
|
|
174
|
+
create(input: CreateProductInput): Promise<Product>;
|
|
175
|
+
list(params?: ProductListParams): Promise<PaginatedResponse<Product>>;
|
|
176
|
+
get(id: string): Promise<Product>;
|
|
177
|
+
update(id: string, input: UpdateProductInput): Promise<Product>;
|
|
178
|
+
delete(id: string): Promise<void>;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
type Requester = (method: string, path: string, body?: unknown) => Promise<unknown>;
|
|
182
|
+
declare class InvoicesResource {
|
|
183
|
+
private request;
|
|
184
|
+
constructor(request: Requester);
|
|
185
|
+
create(input: CreateInvoiceInput): Promise<Invoice>;
|
|
186
|
+
get(id: string): Promise<Record<string, unknown>>;
|
|
187
|
+
getStatus(id: string): Promise<InvoiceStatus>;
|
|
188
|
+
downloadXml(id: string): Promise<DownloadUrlResponse>;
|
|
189
|
+
downloadPdf(id: string): Promise<DownloadUrlResponse>;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
declare class Factuplan {
|
|
193
|
+
private readonly apiKey;
|
|
194
|
+
private readonly baseUrl;
|
|
195
|
+
private readonly timeout;
|
|
196
|
+
readonly customers: CustomersResource;
|
|
197
|
+
readonly products: ProductsResource;
|
|
198
|
+
readonly invoices: InvoicesResource;
|
|
199
|
+
constructor(apiKey: string, options?: FactuplanOptions);
|
|
200
|
+
usage(): Promise<UsageResponse>;
|
|
201
|
+
certificateStatus(): Promise<CertificateStatus>;
|
|
202
|
+
private request;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
declare class FactuplanError extends Error {
|
|
206
|
+
readonly statusCode: number;
|
|
207
|
+
readonly code: string;
|
|
208
|
+
readonly details?: Record<string, unknown>;
|
|
209
|
+
constructor(message: string, statusCode: number, code: string, details?: Record<string, unknown>);
|
|
210
|
+
}
|
|
211
|
+
declare class AuthenticationError extends FactuplanError {
|
|
212
|
+
constructor(message?: string);
|
|
213
|
+
}
|
|
214
|
+
declare class RateLimitError extends FactuplanError {
|
|
215
|
+
constructor(message?: string);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export { AuthenticationError, type CertificateStatus, type CreateCustomerInput, type CreateInvoiceInput, type CreateProductInput, type Customer, type CustomerListParams, type DownloadUrlResponse, Factuplan, FactuplanError, type FactuplanOptions, type Invoice, type InvoiceCustomer, type InvoiceItem, type InvoiceStatus, type PaginatedResponse, type Product, type ProductListParams, RateLimitError, type UpdateCustomerInput, type UpdateProductInput, type UsageResponse };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
AuthenticationError: () => AuthenticationError,
|
|
24
|
+
Factuplan: () => Factuplan,
|
|
25
|
+
FactuplanError: () => FactuplanError,
|
|
26
|
+
RateLimitError: () => RateLimitError
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(index_exports);
|
|
29
|
+
|
|
30
|
+
// src/errors.ts
|
|
31
|
+
var FactuplanError = class extends Error {
|
|
32
|
+
constructor(message, statusCode, code, details) {
|
|
33
|
+
super(message);
|
|
34
|
+
this.name = "FactuplanError";
|
|
35
|
+
this.statusCode = statusCode;
|
|
36
|
+
this.code = code;
|
|
37
|
+
this.details = details;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
var AuthenticationError = class extends FactuplanError {
|
|
41
|
+
constructor(message = "Invalid API key") {
|
|
42
|
+
super(message, 401, "AUTH_ERROR");
|
|
43
|
+
this.name = "AuthenticationError";
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
var RateLimitError = class extends FactuplanError {
|
|
47
|
+
constructor(message = "Rate limit exceeded") {
|
|
48
|
+
super(message, 429, "RATE_LIMIT");
|
|
49
|
+
this.name = "RateLimitError";
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// src/resources/customers.ts
|
|
54
|
+
var CustomersResource = class {
|
|
55
|
+
constructor(request) {
|
|
56
|
+
this.request = request;
|
|
57
|
+
}
|
|
58
|
+
async create(input) {
|
|
59
|
+
return this.request("POST", "/developer/customers", input);
|
|
60
|
+
}
|
|
61
|
+
async list(params) {
|
|
62
|
+
return this.request("GET", "/developer/customers", void 0, params);
|
|
63
|
+
}
|
|
64
|
+
async get(id) {
|
|
65
|
+
return this.request("GET", `/developer/customers/${id}`);
|
|
66
|
+
}
|
|
67
|
+
async update(id, input) {
|
|
68
|
+
return this.request("PATCH", `/developer/customers/${id}`, input);
|
|
69
|
+
}
|
|
70
|
+
async delete(id) {
|
|
71
|
+
await this.request("DELETE", `/developer/customers/${id}`);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// src/resources/products.ts
|
|
76
|
+
var ProductsResource = class {
|
|
77
|
+
constructor(request) {
|
|
78
|
+
this.request = request;
|
|
79
|
+
}
|
|
80
|
+
async create(input) {
|
|
81
|
+
return this.request("POST", "/developer/products", input);
|
|
82
|
+
}
|
|
83
|
+
async list(params) {
|
|
84
|
+
return this.request("GET", "/developer/products", void 0, params);
|
|
85
|
+
}
|
|
86
|
+
async get(id) {
|
|
87
|
+
return this.request("GET", `/developer/products/${id}`);
|
|
88
|
+
}
|
|
89
|
+
async update(id, input) {
|
|
90
|
+
return this.request("PATCH", `/developer/products/${id}`, input);
|
|
91
|
+
}
|
|
92
|
+
async delete(id) {
|
|
93
|
+
await this.request("DELETE", `/developer/products/${id}`);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// src/resources/invoices.ts
|
|
98
|
+
var InvoicesResource = class {
|
|
99
|
+
constructor(request) {
|
|
100
|
+
this.request = request;
|
|
101
|
+
}
|
|
102
|
+
async create(input) {
|
|
103
|
+
return this.request("POST", "/developer/invoices", input);
|
|
104
|
+
}
|
|
105
|
+
async get(id) {
|
|
106
|
+
return this.request("GET", `/developer/receipts/${id}`);
|
|
107
|
+
}
|
|
108
|
+
async getStatus(id) {
|
|
109
|
+
return this.request("GET", `/developer/receipts/${id}/status`);
|
|
110
|
+
}
|
|
111
|
+
async downloadXml(id) {
|
|
112
|
+
return this.request("GET", `/developer/receipts/${id}/xml`);
|
|
113
|
+
}
|
|
114
|
+
async downloadPdf(id) {
|
|
115
|
+
return this.request("GET", `/developer/receipts/${id}/pdf`);
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// src/client.ts
|
|
120
|
+
var DEFAULT_BASE_URL = "https://api.factuplan.com/api/v1";
|
|
121
|
+
var DEFAULT_TIMEOUT = 3e4;
|
|
122
|
+
var Factuplan = class {
|
|
123
|
+
constructor(apiKey, options) {
|
|
124
|
+
if (!apiKey) throw new Error("API key is required");
|
|
125
|
+
this.apiKey = apiKey;
|
|
126
|
+
this.baseUrl = (options?.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
127
|
+
this.timeout = options?.timeout ?? DEFAULT_TIMEOUT;
|
|
128
|
+
const requester = this.request.bind(this);
|
|
129
|
+
this.customers = new CustomersResource(requester);
|
|
130
|
+
this.products = new ProductsResource(requester);
|
|
131
|
+
this.invoices = new InvoicesResource(requester);
|
|
132
|
+
}
|
|
133
|
+
async usage() {
|
|
134
|
+
return this.request("GET", "/developer/usage");
|
|
135
|
+
}
|
|
136
|
+
async certificateStatus() {
|
|
137
|
+
return this.request("GET", "/developer/certificate/status");
|
|
138
|
+
}
|
|
139
|
+
async request(method, path, body, params) {
|
|
140
|
+
let url = `${this.baseUrl}${path}`;
|
|
141
|
+
if (params) {
|
|
142
|
+
const searchParams = new URLSearchParams();
|
|
143
|
+
for (const [key, value] of Object.entries(params)) {
|
|
144
|
+
if (value !== void 0 && value !== null) {
|
|
145
|
+
searchParams.set(key, String(value));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const qs = searchParams.toString();
|
|
149
|
+
if (qs) url += `?${qs}`;
|
|
150
|
+
}
|
|
151
|
+
const headers = {
|
|
152
|
+
"x-api-key": this.apiKey,
|
|
153
|
+
"Accept": "application/json"
|
|
154
|
+
};
|
|
155
|
+
if (body !== void 0) {
|
|
156
|
+
headers["Content-Type"] = "application/json";
|
|
157
|
+
}
|
|
158
|
+
const response = await fetch(url, {
|
|
159
|
+
method,
|
|
160
|
+
headers,
|
|
161
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0,
|
|
162
|
+
signal: AbortSignal.timeout(this.timeout)
|
|
163
|
+
});
|
|
164
|
+
if (response.status === 204) return void 0;
|
|
165
|
+
const json = await response.json().catch(() => null);
|
|
166
|
+
if (!response.ok) {
|
|
167
|
+
const err = json?.error ?? json;
|
|
168
|
+
const message = err?.message ?? `Request failed with status ${response.status}`;
|
|
169
|
+
const code = err?.code ?? "UNKNOWN";
|
|
170
|
+
const details = err?.details;
|
|
171
|
+
if (response.status === 401) throw new AuthenticationError(message);
|
|
172
|
+
if (response.status === 429) throw new RateLimitError(message);
|
|
173
|
+
throw new FactuplanError(message, response.status, code, details);
|
|
174
|
+
}
|
|
175
|
+
if (json && typeof json === "object" && "data" in json && "meta" in json) {
|
|
176
|
+
const meta = json.meta;
|
|
177
|
+
if (Array.isArray(json.data) && typeof meta?.total !== "undefined") {
|
|
178
|
+
return { data: json.data, meta };
|
|
179
|
+
}
|
|
180
|
+
return json.data;
|
|
181
|
+
}
|
|
182
|
+
return json;
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
186
|
+
0 && (module.exports = {
|
|
187
|
+
AuthenticationError,
|
|
188
|
+
Factuplan,
|
|
189
|
+
FactuplanError,
|
|
190
|
+
RateLimitError
|
|
191
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
// src/errors.ts
|
|
2
|
+
var FactuplanError = class extends Error {
|
|
3
|
+
constructor(message, statusCode, code, details) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "FactuplanError";
|
|
6
|
+
this.statusCode = statusCode;
|
|
7
|
+
this.code = code;
|
|
8
|
+
this.details = details;
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
var AuthenticationError = class extends FactuplanError {
|
|
12
|
+
constructor(message = "Invalid API key") {
|
|
13
|
+
super(message, 401, "AUTH_ERROR");
|
|
14
|
+
this.name = "AuthenticationError";
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
var RateLimitError = class extends FactuplanError {
|
|
18
|
+
constructor(message = "Rate limit exceeded") {
|
|
19
|
+
super(message, 429, "RATE_LIMIT");
|
|
20
|
+
this.name = "RateLimitError";
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// src/resources/customers.ts
|
|
25
|
+
var CustomersResource = class {
|
|
26
|
+
constructor(request) {
|
|
27
|
+
this.request = request;
|
|
28
|
+
}
|
|
29
|
+
async create(input) {
|
|
30
|
+
return this.request("POST", "/developer/customers", input);
|
|
31
|
+
}
|
|
32
|
+
async list(params) {
|
|
33
|
+
return this.request("GET", "/developer/customers", void 0, params);
|
|
34
|
+
}
|
|
35
|
+
async get(id) {
|
|
36
|
+
return this.request("GET", `/developer/customers/${id}`);
|
|
37
|
+
}
|
|
38
|
+
async update(id, input) {
|
|
39
|
+
return this.request("PATCH", `/developer/customers/${id}`, input);
|
|
40
|
+
}
|
|
41
|
+
async delete(id) {
|
|
42
|
+
await this.request("DELETE", `/developer/customers/${id}`);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// src/resources/products.ts
|
|
47
|
+
var ProductsResource = class {
|
|
48
|
+
constructor(request) {
|
|
49
|
+
this.request = request;
|
|
50
|
+
}
|
|
51
|
+
async create(input) {
|
|
52
|
+
return this.request("POST", "/developer/products", input);
|
|
53
|
+
}
|
|
54
|
+
async list(params) {
|
|
55
|
+
return this.request("GET", "/developer/products", void 0, params);
|
|
56
|
+
}
|
|
57
|
+
async get(id) {
|
|
58
|
+
return this.request("GET", `/developer/products/${id}`);
|
|
59
|
+
}
|
|
60
|
+
async update(id, input) {
|
|
61
|
+
return this.request("PATCH", `/developer/products/${id}`, input);
|
|
62
|
+
}
|
|
63
|
+
async delete(id) {
|
|
64
|
+
await this.request("DELETE", `/developer/products/${id}`);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// src/resources/invoices.ts
|
|
69
|
+
var InvoicesResource = class {
|
|
70
|
+
constructor(request) {
|
|
71
|
+
this.request = request;
|
|
72
|
+
}
|
|
73
|
+
async create(input) {
|
|
74
|
+
return this.request("POST", "/developer/invoices", input);
|
|
75
|
+
}
|
|
76
|
+
async get(id) {
|
|
77
|
+
return this.request("GET", `/developer/receipts/${id}`);
|
|
78
|
+
}
|
|
79
|
+
async getStatus(id) {
|
|
80
|
+
return this.request("GET", `/developer/receipts/${id}/status`);
|
|
81
|
+
}
|
|
82
|
+
async downloadXml(id) {
|
|
83
|
+
return this.request("GET", `/developer/receipts/${id}/xml`);
|
|
84
|
+
}
|
|
85
|
+
async downloadPdf(id) {
|
|
86
|
+
return this.request("GET", `/developer/receipts/${id}/pdf`);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// src/client.ts
|
|
91
|
+
var DEFAULT_BASE_URL = "https://api.factuplan.com/api/v1";
|
|
92
|
+
var DEFAULT_TIMEOUT = 3e4;
|
|
93
|
+
var Factuplan = class {
|
|
94
|
+
constructor(apiKey, options) {
|
|
95
|
+
if (!apiKey) throw new Error("API key is required");
|
|
96
|
+
this.apiKey = apiKey;
|
|
97
|
+
this.baseUrl = (options?.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
98
|
+
this.timeout = options?.timeout ?? DEFAULT_TIMEOUT;
|
|
99
|
+
const requester = this.request.bind(this);
|
|
100
|
+
this.customers = new CustomersResource(requester);
|
|
101
|
+
this.products = new ProductsResource(requester);
|
|
102
|
+
this.invoices = new InvoicesResource(requester);
|
|
103
|
+
}
|
|
104
|
+
async usage() {
|
|
105
|
+
return this.request("GET", "/developer/usage");
|
|
106
|
+
}
|
|
107
|
+
async certificateStatus() {
|
|
108
|
+
return this.request("GET", "/developer/certificate/status");
|
|
109
|
+
}
|
|
110
|
+
async request(method, path, body, params) {
|
|
111
|
+
let url = `${this.baseUrl}${path}`;
|
|
112
|
+
if (params) {
|
|
113
|
+
const searchParams = new URLSearchParams();
|
|
114
|
+
for (const [key, value] of Object.entries(params)) {
|
|
115
|
+
if (value !== void 0 && value !== null) {
|
|
116
|
+
searchParams.set(key, String(value));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const qs = searchParams.toString();
|
|
120
|
+
if (qs) url += `?${qs}`;
|
|
121
|
+
}
|
|
122
|
+
const headers = {
|
|
123
|
+
"x-api-key": this.apiKey,
|
|
124
|
+
"Accept": "application/json"
|
|
125
|
+
};
|
|
126
|
+
if (body !== void 0) {
|
|
127
|
+
headers["Content-Type"] = "application/json";
|
|
128
|
+
}
|
|
129
|
+
const response = await fetch(url, {
|
|
130
|
+
method,
|
|
131
|
+
headers,
|
|
132
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0,
|
|
133
|
+
signal: AbortSignal.timeout(this.timeout)
|
|
134
|
+
});
|
|
135
|
+
if (response.status === 204) return void 0;
|
|
136
|
+
const json = await response.json().catch(() => null);
|
|
137
|
+
if (!response.ok) {
|
|
138
|
+
const err = json?.error ?? json;
|
|
139
|
+
const message = err?.message ?? `Request failed with status ${response.status}`;
|
|
140
|
+
const code = err?.code ?? "UNKNOWN";
|
|
141
|
+
const details = err?.details;
|
|
142
|
+
if (response.status === 401) throw new AuthenticationError(message);
|
|
143
|
+
if (response.status === 429) throw new RateLimitError(message);
|
|
144
|
+
throw new FactuplanError(message, response.status, code, details);
|
|
145
|
+
}
|
|
146
|
+
if (json && typeof json === "object" && "data" in json && "meta" in json) {
|
|
147
|
+
const meta = json.meta;
|
|
148
|
+
if (Array.isArray(json.data) && typeof meta?.total !== "undefined") {
|
|
149
|
+
return { data: json.data, meta };
|
|
150
|
+
}
|
|
151
|
+
return json.data;
|
|
152
|
+
}
|
|
153
|
+
return json;
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
export {
|
|
157
|
+
AuthenticationError,
|
|
158
|
+
Factuplan,
|
|
159
|
+
FactuplanError,
|
|
160
|
+
RateLimitError
|
|
161
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "factuplan",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Official Factuplan SDK for JavaScript & TypeScript — Create electronic invoices for Ecuador (SRI)",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"tsup": "^8.0.0",
|
|
20
|
+
"typescript": "^5.5.0"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"factuplan",
|
|
24
|
+
"ecuador",
|
|
25
|
+
"sri",
|
|
26
|
+
"invoice",
|
|
27
|
+
"factura",
|
|
28
|
+
"electronic-billing"
|
|
29
|
+
],
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
33
|
+
"dev": "tsup src/index.ts --format cjs,esm --dts --watch"
|
|
34
|
+
}
|
|
35
|
+
}
|