@beel_es/sdk 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 +167 -76
- package/dist/index.d.mts +9744 -0
- package/dist/index.d.ts +9736 -18
- package/dist/index.js +801 -50
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +756 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +48 -19
- package/dist/client.d.ts +0 -54
- package/dist/client.d.ts.map +0 -1
- package/dist/client.js +0 -94
- package/dist/client.js.map +0 -1
- package/dist/generated/client/client.gen.d.ts +0 -3
- package/dist/generated/client/client.gen.d.ts.map +0 -1
- package/dist/generated/client/client.gen.js +0 -240
- package/dist/generated/client/client.gen.js.map +0 -1
- package/dist/generated/client/index.d.ts +0 -9
- package/dist/generated/client/index.d.ts.map +0 -1
- package/dist/generated/client/index.js +0 -18
- package/dist/generated/client/index.js.map +0 -1
- package/dist/generated/client/types.gen.d.ts +0 -118
- package/dist/generated/client/types.gen.d.ts.map +0 -1
- package/dist/generated/client/types.gen.js +0 -4
- package/dist/generated/client/types.gen.js.map +0 -1
- package/dist/generated/client/utils.gen.d.ts +0 -34
- package/dist/generated/client/utils.gen.d.ts.map +0 -1
- package/dist/generated/client/utils.gen.js +0 -242
- package/dist/generated/client/utils.gen.js.map +0 -1
- package/dist/generated/client.gen.d.ts +0 -13
- package/dist/generated/client.gen.d.ts.map +0 -1
- package/dist/generated/client.gen.js +0 -7
- package/dist/generated/client.gen.js.map +0 -1
- package/dist/generated/core/auth.gen.d.ts +0 -19
- package/dist/generated/core/auth.gen.d.ts.map +0 -1
- package/dist/generated/core/auth.gen.js +0 -19
- package/dist/generated/core/auth.gen.js.map +0 -1
- package/dist/generated/core/bodySerializer.gen.d.ts +0 -26
- package/dist/generated/core/bodySerializer.gen.d.ts.map +0 -1
- package/dist/generated/core/bodySerializer.gen.js +0 -61
- package/dist/generated/core/bodySerializer.gen.js.map +0 -1
- package/dist/generated/core/params.gen.d.ts +0 -44
- package/dist/generated/core/params.gen.d.ts.map +0 -1
- package/dist/generated/core/params.gen.js +0 -105
- package/dist/generated/core/params.gen.js.map +0 -1
- package/dist/generated/core/pathSerializer.gen.d.ts +0 -34
- package/dist/generated/core/pathSerializer.gen.d.ts.map +0 -1
- package/dist/generated/core/pathSerializer.gen.js +0 -116
- package/dist/generated/core/pathSerializer.gen.js.map +0 -1
- package/dist/generated/core/queryKeySerializer.gen.d.ts +0 -19
- package/dist/generated/core/queryKeySerializer.gen.d.ts.map +0 -1
- package/dist/generated/core/queryKeySerializer.gen.js +0 -99
- package/dist/generated/core/queryKeySerializer.gen.js.map +0 -1
- package/dist/generated/core/serverSentEvents.gen.d.ts +0 -72
- package/dist/generated/core/serverSentEvents.gen.d.ts.map +0 -1
- package/dist/generated/core/serverSentEvents.gen.js +0 -138
- package/dist/generated/core/serverSentEvents.gen.js.map +0 -1
- package/dist/generated/core/types.gen.d.ts +0 -79
- package/dist/generated/core/types.gen.d.ts.map +0 -1
- package/dist/generated/core/types.gen.js +0 -4
- package/dist/generated/core/types.gen.js.map +0 -1
- package/dist/generated/core/utils.gen.d.ts +0 -20
- package/dist/generated/core/utils.gen.d.ts.map +0 -1
- package/dist/generated/core/utils.gen.js +0 -94
- package/dist/generated/core/utils.gen.js.map +0 -1
- package/dist/generated/index.d.ts +0 -3
- package/dist/generated/index.d.ts.map +0 -1
- package/dist/generated/index.js +0 -84
- package/dist/generated/index.js.map +0 -1
- package/dist/generated/sdk.gen.d.ts +0 -966
- package/dist/generated/sdk.gen.d.ts.map +0 -1
- package/dist/generated/sdk.gen.js +0 -1490
- package/dist/generated/sdk.gen.js.map +0 -1
- package/dist/generated/types.gen.d.ts +0 -5646
- package/dist/generated/types.gen.d.ts.map +0 -1
- package/dist/generated/types.gen.js +0 -4
- package/dist/generated/types.gen.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/resources/companies.d.ts +0 -75
- package/dist/resources/companies.d.ts.map +0 -1
- package/dist/resources/companies.js +0 -77
- package/dist/resources/companies.js.map +0 -1
- package/dist/resources/config.d.ts +0 -106
- package/dist/resources/config.d.ts.map +0 -1
- package/dist/resources/config.js +0 -86
- package/dist/resources/config.js.map +0 -1
- package/dist/resources/customers.d.ts +0 -172
- package/dist/resources/customers.d.ts.map +0 -1
- package/dist/resources/customers.js +0 -96
- package/dist/resources/customers.js.map +0 -1
- package/dist/resources/invoices.d.ts +0 -370
- package/dist/resources/invoices.d.ts.map +0 -1
- package/dist/resources/invoices.js +0 -174
- package/dist/resources/invoices.js.map +0 -1
- package/dist/resources/products.d.ts +0 -155
- package/dist/resources/products.d.ts.map +0 -1
- package/dist/resources/products.js +0 -96
- package/dist/resources/products.js.map +0 -1
- package/dist/resources/series.d.ts +0 -98
- package/dist/resources/series.d.ts.map +0 -1
- package/dist/resources/series.js +0 -83
- package/dist/resources/series.js.map +0 -1
- package/dist/webhooks/index.d.ts +0 -20
- package/dist/webhooks/index.d.ts.map +0 -1
- package/dist/webhooks/index.js +0 -70
- package/dist/webhooks/index.js.map +0 -1
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,756 @@
|
|
|
1
|
+
// src/http/client.ts
|
|
2
|
+
import createClient from "openapi-fetch";
|
|
3
|
+
|
|
4
|
+
// src/http/middleware.ts
|
|
5
|
+
import { randomUUID } from "crypto";
|
|
6
|
+
|
|
7
|
+
// src/core/errors.ts
|
|
8
|
+
var BeeLApiError = class extends Error {
|
|
9
|
+
constructor(message, statusCode, code, details, requestId) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "BeeLApiError";
|
|
12
|
+
this.statusCode = statusCode;
|
|
13
|
+
this.code = code;
|
|
14
|
+
this.details = details;
|
|
15
|
+
this.requestId = requestId;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
var BeeLRateLimitError = class extends BeeLApiError {
|
|
19
|
+
constructor(message, retryAfterSeconds, requestId) {
|
|
20
|
+
super(message, 429, "RATE_LIMIT_EXCEEDED", void 0, requestId);
|
|
21
|
+
this.name = "BeeLRateLimitError";
|
|
22
|
+
this.retryAfterSeconds = retryAfterSeconds;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
var BeeLValidationError = class extends BeeLApiError {
|
|
26
|
+
constructor(message, details, requestId) {
|
|
27
|
+
super(message, 422, "UNPROCESSABLE_ENTITY", details, requestId);
|
|
28
|
+
this.name = "BeeLValidationError";
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
var BeeLAuthError = class extends BeeLApiError {
|
|
32
|
+
constructor(message, statusCode, requestId) {
|
|
33
|
+
super(
|
|
34
|
+
message,
|
|
35
|
+
statusCode,
|
|
36
|
+
statusCode === 401 ? "UNAUTHORIZED" : "FORBIDDEN",
|
|
37
|
+
void 0,
|
|
38
|
+
requestId
|
|
39
|
+
);
|
|
40
|
+
this.name = "BeeLAuthError";
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
var BeeLNotFoundError = class extends BeeLApiError {
|
|
44
|
+
constructor(message, requestId) {
|
|
45
|
+
super(message, 404, "NOT_FOUND", void 0, requestId);
|
|
46
|
+
this.name = "BeeLNotFoundError";
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
var BeeLConflictError = class extends BeeLApiError {
|
|
50
|
+
constructor(message, requestId) {
|
|
51
|
+
super(message, 409, "CONFLICT", void 0, requestId);
|
|
52
|
+
this.name = "BeeLConflictError";
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
function throwApiError(status, body) {
|
|
56
|
+
const parsed = body;
|
|
57
|
+
const errorData = parsed?.error ?? parsed;
|
|
58
|
+
const message = errorData?.message ?? `API error ${status}`;
|
|
59
|
+
const code = errorData?.code ?? "UNKNOWN";
|
|
60
|
+
const details = errorData?.details;
|
|
61
|
+
const requestId = parsed?.meta?.request_id;
|
|
62
|
+
switch (status) {
|
|
63
|
+
case 401:
|
|
64
|
+
case 403:
|
|
65
|
+
throw new BeeLAuthError(message, status, requestId);
|
|
66
|
+
case 404:
|
|
67
|
+
throw new BeeLNotFoundError(message, requestId);
|
|
68
|
+
case 409:
|
|
69
|
+
throw new BeeLConflictError(message, requestId);
|
|
70
|
+
case 422:
|
|
71
|
+
throw new BeeLValidationError(message, details, requestId);
|
|
72
|
+
case 429: {
|
|
73
|
+
const retryAfter = errorData?.retry_after ?? 60;
|
|
74
|
+
throw new BeeLRateLimitError(message, retryAfter, requestId);
|
|
75
|
+
}
|
|
76
|
+
default:
|
|
77
|
+
throw new BeeLApiError(message, status, code, details, requestId);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// src/http/middleware.ts
|
|
82
|
+
var requestMiddleware = {
|
|
83
|
+
async onRequest({ request }) {
|
|
84
|
+
const method = request.method;
|
|
85
|
+
if (["POST", "PUT", "PATCH"].includes(method)) {
|
|
86
|
+
const hasBody = request.headers.get("content-type")?.includes("application/json");
|
|
87
|
+
if (!hasBody) {
|
|
88
|
+
const clone = new Request(request, {
|
|
89
|
+
body: request.body ?? "{}",
|
|
90
|
+
headers: new Headers(request.headers)
|
|
91
|
+
});
|
|
92
|
+
clone.headers.set("Content-Type", "application/json");
|
|
93
|
+
if (method === "POST" && !clone.headers.has("Idempotency-Key")) {
|
|
94
|
+
clone.headers.set("Idempotency-Key", randomUUID());
|
|
95
|
+
}
|
|
96
|
+
return clone;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (method === "POST" && !request.headers.has("Idempotency-Key")) {
|
|
100
|
+
request.headers.set("Idempotency-Key", randomUUID());
|
|
101
|
+
}
|
|
102
|
+
return request;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
var errorMiddleware = {
|
|
106
|
+
async onResponse({ response }) {
|
|
107
|
+
if (!response.ok) {
|
|
108
|
+
const body = await response.json().catch(() => null);
|
|
109
|
+
throwApiError(response.status, body);
|
|
110
|
+
}
|
|
111
|
+
return response;
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
function createRetryMiddleware(options) {
|
|
115
|
+
return {
|
|
116
|
+
async onError({ error, request }) {
|
|
117
|
+
if (!(error instanceof BeeLApiError)) return;
|
|
118
|
+
if (!(error instanceof BeeLRateLimitError) && error.statusCode < 500) return;
|
|
119
|
+
for (let attempt = 0; attempt < options.maxRetries; attempt++) {
|
|
120
|
+
const delay = error instanceof BeeLRateLimitError ? error.retryAfterSeconds * 1e3 : Math.min(
|
|
121
|
+
options.initialDelayMs * Math.pow(2, attempt) + Math.random() * options.initialDelayMs,
|
|
122
|
+
options.maxDelayMs
|
|
123
|
+
);
|
|
124
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
125
|
+
try {
|
|
126
|
+
if (request.method === "POST") {
|
|
127
|
+
request.headers.set("Idempotency-Key", randomUUID());
|
|
128
|
+
}
|
|
129
|
+
const response = await fetch(request.clone());
|
|
130
|
+
if (response.ok) return response;
|
|
131
|
+
const body = await response.json().catch(() => null);
|
|
132
|
+
throwApiError(response.status, body);
|
|
133
|
+
} catch (retryError) {
|
|
134
|
+
if (!(retryError instanceof BeeLApiError)) throw retryError;
|
|
135
|
+
if (!(retryError instanceof BeeLRateLimitError) && retryError.statusCode < 500) throw retryError;
|
|
136
|
+
if (attempt === options.maxRetries - 1) throw retryError;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// src/http/client.ts
|
|
144
|
+
function createBeeLClient(config) {
|
|
145
|
+
const client = createClient({
|
|
146
|
+
baseUrl: config.baseUrl ?? "https://app.beel.es/api",
|
|
147
|
+
headers: {
|
|
148
|
+
Authorization: `Bearer ${config.apiKey}`
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
client.use(requestMiddleware);
|
|
152
|
+
client.use(errorMiddleware);
|
|
153
|
+
client.use(
|
|
154
|
+
createRetryMiddleware({
|
|
155
|
+
maxRetries: config.maxRetries ?? 3,
|
|
156
|
+
initialDelayMs: config.retryDelayMs ?? 500,
|
|
157
|
+
maxDelayMs: config.maxRetryDelayMs ?? 3e4
|
|
158
|
+
})
|
|
159
|
+
);
|
|
160
|
+
return client;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// src/resources/invoices.ts
|
|
164
|
+
var InvoicesResource = class {
|
|
165
|
+
constructor(client) {
|
|
166
|
+
this.client = client;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* List invoices with optional filters and pagination.
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* const { invoices, pagination } = await beel.invoices.list({ status: 'ISSUED', limit: 20 });
|
|
173
|
+
*/
|
|
174
|
+
async list(params) {
|
|
175
|
+
const { data } = await this.client.GET("/v1/invoices", { params: { query: params } });
|
|
176
|
+
return data.data;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Create a new invoice. An idempotency key is automatically added.
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* const invoice = await beel.invoices.create({
|
|
183
|
+
* type: 'STANDARD',
|
|
184
|
+
* recipient: { customer_id: 'uuid' },
|
|
185
|
+
* lines: [{ description: 'Service', quantity: 1, unit_price: 100, discount_percentage: 0 }],
|
|
186
|
+
* });
|
|
187
|
+
*/
|
|
188
|
+
async create(body) {
|
|
189
|
+
const { data } = await this.client.POST("/v1/invoices", { body });
|
|
190
|
+
return data.data;
|
|
191
|
+
}
|
|
192
|
+
/** Get an invoice by ID. */
|
|
193
|
+
async get(id) {
|
|
194
|
+
const { data } = await this.client.GET("/v1/invoices/{invoice_id}", { params: { path: { invoice_id: id } } });
|
|
195
|
+
return data.data;
|
|
196
|
+
}
|
|
197
|
+
/** Update a draft invoice. Only draft invoices can be updated. */
|
|
198
|
+
async update(id, body) {
|
|
199
|
+
const { data } = await this.client.PUT("/v1/invoices/{invoice_id}", { params: { path: { invoice_id: id } }, body });
|
|
200
|
+
return data.data;
|
|
201
|
+
}
|
|
202
|
+
/** Delete a draft invoice. Only draft invoices can be deleted. */
|
|
203
|
+
async delete(id) {
|
|
204
|
+
await this.client.DELETE("/v1/invoices/{invoice_id}", { params: { path: { invoice_id: id } } });
|
|
205
|
+
}
|
|
206
|
+
/** Duplicate an invoice, optionally into a different series. */
|
|
207
|
+
async duplicate(id, options) {
|
|
208
|
+
const { data } = await this.client.POST("/v1/invoices/{invoice_id}/duplicate", { params: { path: { invoice_id: id } }, body: options });
|
|
209
|
+
return data.data;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Issue a draft invoice. Assigns an invoice number and triggers VeriFactu submission.
|
|
213
|
+
* @param options.wait_for_pdf - If true, waits for PDF generation before returning.
|
|
214
|
+
*/
|
|
215
|
+
async issue(id, options) {
|
|
216
|
+
const { data } = await this.client.POST("/v1/invoices/{invoice_id}/issue", { params: { path: { invoice_id: id }, query: options } });
|
|
217
|
+
return data.data;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Mark an invoice as paid.
|
|
221
|
+
* @param options.payment_date - Payment date (defaults to today).
|
|
222
|
+
* @param options.payment_method - Payment method details.
|
|
223
|
+
*/
|
|
224
|
+
async markPaid(id, options) {
|
|
225
|
+
const { data } = await this.client.POST("/v1/invoices/{invoice_id}/mark-paid", { params: { path: { invoice_id: id } }, body: options });
|
|
226
|
+
return data.data;
|
|
227
|
+
}
|
|
228
|
+
/** Mark an invoice as sent (e.g. delivered by other means). */
|
|
229
|
+
async markSent(id, options) {
|
|
230
|
+
const { data } = await this.client.POST("/v1/invoices/{invoice_id}/mark-sent", { params: { path: { invoice_id: id } }, body: options });
|
|
231
|
+
return data.data;
|
|
232
|
+
}
|
|
233
|
+
/** Revert a paid/sent invoice back to issued status. */
|
|
234
|
+
async revertToIssued(id) {
|
|
235
|
+
const { data } = await this.client.POST("/v1/invoices/{invoice_id}/revert-to-issued", { params: { path: { invoice_id: id } } });
|
|
236
|
+
return data.data;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Void an issued invoice. Requires a reason (min 10 chars).
|
|
240
|
+
*
|
|
241
|
+
* @example
|
|
242
|
+
* await beel.invoices.void('id', { reason: 'Duplicate invoice sent in error' });
|
|
243
|
+
*/
|
|
244
|
+
async void(id, params) {
|
|
245
|
+
const { data } = await this.client.POST("/v1/invoices/{invoice_id}/void", { params: { path: { invoice_id: id } }, body: params });
|
|
246
|
+
return data.data;
|
|
247
|
+
}
|
|
248
|
+
/** Create a corrective invoice that references the original. */
|
|
249
|
+
async createCorrective(id, body) {
|
|
250
|
+
const { data } = await this.client.POST("/v1/invoices/{invoice_id}/corrective", { params: { path: { invoice_id: id } }, body });
|
|
251
|
+
return data.data;
|
|
252
|
+
}
|
|
253
|
+
/** Schedule a draft invoice to be issued on a future date. */
|
|
254
|
+
async schedule(id, params) {
|
|
255
|
+
const { data } = await this.client.POST("/v1/invoices/{invoice_id}/schedule", { params: { path: { invoice_id: id } }, body: params });
|
|
256
|
+
return data.data;
|
|
257
|
+
}
|
|
258
|
+
/** Cancel a scheduled invoice, returning it to draft. */
|
|
259
|
+
async unschedule(id) {
|
|
260
|
+
const { data } = await this.client.POST("/v1/invoices/{invoice_id}/unschedule", { params: { path: { invoice_id: id } } });
|
|
261
|
+
return data.data;
|
|
262
|
+
}
|
|
263
|
+
/** Change the scheduled date of a scheduled invoice. */
|
|
264
|
+
async reschedule(id, params) {
|
|
265
|
+
const { data } = await this.client.PATCH("/v1/invoices/{invoice_id}/reschedule", { params: { path: { invoice_id: id } }, body: params });
|
|
266
|
+
return data.data;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Get a pre-signed URL to download the invoice PDF.
|
|
270
|
+
* The URL expires in 5 minutes. Use `beel.downloadPdf(id)` for a simpler Buffer download.
|
|
271
|
+
*/
|
|
272
|
+
async getPdf(id) {
|
|
273
|
+
const { data } = await this.client.GET("/v1/invoices/{invoice_id}/pdf", { params: { path: { invoice_id: id } } });
|
|
274
|
+
return data.data;
|
|
275
|
+
}
|
|
276
|
+
/** Send an invoice by email. Uses the customer's email by default. */
|
|
277
|
+
async sendEmail(id, options) {
|
|
278
|
+
const { data } = await this.client.POST("/v1/invoices/{invoice_id}/send", { params: { path: { invoice_id: id } }, body: options });
|
|
279
|
+
return data.data;
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
// src/resources/customers.ts
|
|
284
|
+
var CustomersResource = class {
|
|
285
|
+
constructor(client) {
|
|
286
|
+
this.client = client;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* List customers with optional filters and pagination.
|
|
290
|
+
*
|
|
291
|
+
* @example
|
|
292
|
+
* const { customers, pagination } = await beel.customers.list({ search: 'Acme', limit: 10 });
|
|
293
|
+
*/
|
|
294
|
+
async list(params) {
|
|
295
|
+
const { data } = await this.client.GET("/v1/customers", { params: { query: params } });
|
|
296
|
+
return data.data;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Create a new customer.
|
|
300
|
+
*
|
|
301
|
+
* @example
|
|
302
|
+
* const customer = await beel.customers.create({
|
|
303
|
+
* legal_name: 'Acme SL',
|
|
304
|
+
* nif: 'B12345678',
|
|
305
|
+
* address: { street: 'Calle Mayor', number: '1', postal_code: '28001', city: 'Madrid', province: 'Madrid', country: 'Spain', country_code: 'ES' },
|
|
306
|
+
* });
|
|
307
|
+
*/
|
|
308
|
+
async create(body) {
|
|
309
|
+
const { data } = await this.client.POST("/v1/customers", { body });
|
|
310
|
+
return data.data;
|
|
311
|
+
}
|
|
312
|
+
/** Get a customer by ID. */
|
|
313
|
+
async get(id) {
|
|
314
|
+
const { data } = await this.client.GET("/v1/customers/{customer_id}", { params: { path: { customer_id: id } } });
|
|
315
|
+
return data.data;
|
|
316
|
+
}
|
|
317
|
+
/** Update a customer. Only provided fields are changed. */
|
|
318
|
+
async update(id, body) {
|
|
319
|
+
const { data } = await this.client.PUT("/v1/customers/{customer_id}", { params: { path: { customer_id: id } }, body });
|
|
320
|
+
return data.data;
|
|
321
|
+
}
|
|
322
|
+
/** Deactivate (soft-delete) a customer. */
|
|
323
|
+
async deactivate(id) {
|
|
324
|
+
await this.client.DELETE("/v1/customers/{customer_id}", { params: { path: { customer_id: id } } });
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
// src/resources/products.ts
|
|
329
|
+
var ProductsResource = class {
|
|
330
|
+
constructor(client) {
|
|
331
|
+
this.client = client;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* List products with optional filters and pagination.
|
|
335
|
+
*
|
|
336
|
+
* @example
|
|
337
|
+
* const { products, pagination } = await beel.products.list({ category: 'SERVICE' });
|
|
338
|
+
*/
|
|
339
|
+
async list(params) {
|
|
340
|
+
const { data } = await this.client.GET("/v1/products", { params: { query: params } });
|
|
341
|
+
return data.data;
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Create a new product or service.
|
|
345
|
+
*
|
|
346
|
+
* @example
|
|
347
|
+
* const product = await beel.products.create({
|
|
348
|
+
* name: 'Consulting',
|
|
349
|
+
* code: 'CONS-001',
|
|
350
|
+
* category: 'SERVICE',
|
|
351
|
+
* default_price: 100,
|
|
352
|
+
* main_tax: { type: 'IVA', percentage: 21 },
|
|
353
|
+
* });
|
|
354
|
+
*/
|
|
355
|
+
async create(body) {
|
|
356
|
+
const { data } = await this.client.POST("/v1/products", { body });
|
|
357
|
+
return data.data;
|
|
358
|
+
}
|
|
359
|
+
/** Get a product by ID. */
|
|
360
|
+
async get(id) {
|
|
361
|
+
const { data } = await this.client.GET("/v1/products/{product_id}", { params: { path: { product_id: id } } });
|
|
362
|
+
return data.data;
|
|
363
|
+
}
|
|
364
|
+
/** Update a product. Only provided fields are changed. */
|
|
365
|
+
async update(id, body) {
|
|
366
|
+
const { data } = await this.client.PUT("/v1/products/{product_id}", { params: { path: { product_id: id } }, body });
|
|
367
|
+
return data.data;
|
|
368
|
+
}
|
|
369
|
+
/** Delete a product permanently. */
|
|
370
|
+
async delete(id) {
|
|
371
|
+
await this.client.DELETE("/v1/products/{product_id}", { params: { path: { product_id: id } } });
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Search products by name or code. Returns up to `limit` results.
|
|
375
|
+
*
|
|
376
|
+
* @example
|
|
377
|
+
* const results = await beel.products.search('consulting', 5);
|
|
378
|
+
*/
|
|
379
|
+
async search(q, limit) {
|
|
380
|
+
const { data } = await this.client.GET("/v1/products/search", { params: { query: { q, limit } } });
|
|
381
|
+
return data.data;
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
// src/resources/configuration.ts
|
|
386
|
+
var ConfigurationResource = class {
|
|
387
|
+
constructor(client) {
|
|
388
|
+
this.client = client;
|
|
389
|
+
}
|
|
390
|
+
/** Get the current tax configuration (default VAT, IRPF, etc.). */
|
|
391
|
+
async getTaxConfig() {
|
|
392
|
+
const { data } = await this.client.GET("/v1/configuration/taxes");
|
|
393
|
+
return data.data;
|
|
394
|
+
}
|
|
395
|
+
/** Update tax configuration. */
|
|
396
|
+
async updateTaxConfig(body) {
|
|
397
|
+
const { data } = await this.client.PUT("/v1/configuration/taxes", { body });
|
|
398
|
+
return data.data;
|
|
399
|
+
}
|
|
400
|
+
/** Get available tax types (IVA/IGIC rates, IRPF types, equivalence surcharges). */
|
|
401
|
+
async getTaxTypes() {
|
|
402
|
+
const { data } = await this.client.GET("/v1/configuration/tax-types");
|
|
403
|
+
return data.data;
|
|
404
|
+
}
|
|
405
|
+
/** Get invoice customization options (logo, colors, footer text, etc.). */
|
|
406
|
+
async getInvoiceCustomizationOptions() {
|
|
407
|
+
const { data } = await this.client.GET("/v1/configuration/invoice-customization-options");
|
|
408
|
+
return data.data;
|
|
409
|
+
}
|
|
410
|
+
/** Update the user's preferred language (es, en, ca). */
|
|
411
|
+
async updateLanguage(language) {
|
|
412
|
+
const { data } = await this.client.PUT("/v1/configuration/language", { body: { language } });
|
|
413
|
+
return data.data;
|
|
414
|
+
}
|
|
415
|
+
/** Get VeriFactu configuration (enabled, certificate status, etc.). */
|
|
416
|
+
async getVerifactu() {
|
|
417
|
+
const { data } = await this.client.GET("/v1/configuration/verifactu");
|
|
418
|
+
return data.data;
|
|
419
|
+
}
|
|
420
|
+
/** Update VeriFactu configuration. */
|
|
421
|
+
async updateVerifactu(body) {
|
|
422
|
+
const { data } = await this.client.PUT("/v1/configuration/verifactu", { body });
|
|
423
|
+
return data.data;
|
|
424
|
+
}
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
// src/resources/series.ts
|
|
428
|
+
var SeriesResource = class {
|
|
429
|
+
constructor(client) {
|
|
430
|
+
this.client = client;
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* List all invoice series. Optionally filter by active status.
|
|
434
|
+
*
|
|
435
|
+
* @example
|
|
436
|
+
* const series = await beel.series.list();
|
|
437
|
+
* const activeSeries = await beel.series.list(true);
|
|
438
|
+
*/
|
|
439
|
+
async list(active) {
|
|
440
|
+
const { data } = await this.client.GET("/v1/configuration/series", {
|
|
441
|
+
params: { query: active !== void 0 ? { active } : void 0 }
|
|
442
|
+
});
|
|
443
|
+
return data.data;
|
|
444
|
+
}
|
|
445
|
+
/** Create a new invoice series (e.g. "A-2026", "RECT"). */
|
|
446
|
+
async create(body) {
|
|
447
|
+
const { data } = await this.client.POST("/v1/configuration/series", { body });
|
|
448
|
+
return data.data;
|
|
449
|
+
}
|
|
450
|
+
/** Update a series (name, description, etc.). */
|
|
451
|
+
async update(id, body) {
|
|
452
|
+
const { data } = await this.client.PUT("/v1/configuration/series/{series_id}", {
|
|
453
|
+
params: { path: { series_id: id } },
|
|
454
|
+
body
|
|
455
|
+
});
|
|
456
|
+
return data.data;
|
|
457
|
+
}
|
|
458
|
+
/** Delete an invoice series. Only empty series can be deleted. */
|
|
459
|
+
async delete(id) {
|
|
460
|
+
await this.client.DELETE("/v1/configuration/series/{series_id}", {
|
|
461
|
+
params: { path: { series_id: id } }
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
/** Set a series as the default for new invoices. */
|
|
465
|
+
async setDefault(id) {
|
|
466
|
+
const { data } = await this.client.POST("/v1/configuration/series/{series_id}/default", {
|
|
467
|
+
params: { path: { series_id: id } }
|
|
468
|
+
});
|
|
469
|
+
return data.data;
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
// src/resources/nif.ts
|
|
474
|
+
var NifResource = class {
|
|
475
|
+
constructor(client) {
|
|
476
|
+
this.client = client;
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Validate a Spanish NIF/CIF against the AEAT tax registry.
|
|
480
|
+
*
|
|
481
|
+
* @example
|
|
482
|
+
* const result = await beel.nif.validate('B12345678');
|
|
483
|
+
* console.log(result.valid, result.name);
|
|
484
|
+
*/
|
|
485
|
+
async validate(nif) {
|
|
486
|
+
const { data } = await this.client.POST("/v1/nif/validate", { body: { nif } });
|
|
487
|
+
return data.data;
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
// src/client.ts
|
|
492
|
+
var BeeL = class {
|
|
493
|
+
constructor(config) {
|
|
494
|
+
const client = createBeeLClient(config);
|
|
495
|
+
this.invoices = new InvoicesResource(client);
|
|
496
|
+
this.customers = new CustomersResource(client);
|
|
497
|
+
this.products = new ProductsResource(client);
|
|
498
|
+
this.configuration = new ConfigurationResource(client);
|
|
499
|
+
this.series = new SeriesResource(client);
|
|
500
|
+
this.nif = new NifResource(client);
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Download an invoice PDF as a Buffer.
|
|
504
|
+
*
|
|
505
|
+
* @example
|
|
506
|
+
* ```typescript
|
|
507
|
+
* const { buffer, fileName } = await beel.downloadPdf('invoice-uuid');
|
|
508
|
+
* fs.writeFileSync(fileName, buffer);
|
|
509
|
+
* ```
|
|
510
|
+
*/
|
|
511
|
+
async downloadPdf(invoiceId) {
|
|
512
|
+
const pdf = await this.invoices.getPdf(invoiceId);
|
|
513
|
+
const response = await fetch(pdf.download_url);
|
|
514
|
+
if (!response.ok) throw new Error(`Failed to download PDF: ${response.status}`);
|
|
515
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
516
|
+
return { buffer: Buffer.from(arrayBuffer), fileName: pdf.file_name };
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
// src/webhooks/verifier.ts
|
|
521
|
+
import { createHmac, timingSafeEqual } from "crypto";
|
|
522
|
+
var WebhookVerifier = class {
|
|
523
|
+
/**
|
|
524
|
+
* Creates a new WebhookVerifier
|
|
525
|
+
*
|
|
526
|
+
* @param secret - Your webhook secret from BeeL dashboard
|
|
527
|
+
* @param toleranceSeconds - Maximum age of webhook events (default: 300 = 5 minutes)
|
|
528
|
+
*/
|
|
529
|
+
constructor(secret, toleranceSeconds = 300) {
|
|
530
|
+
if (!secret || secret.trim().length === 0) {
|
|
531
|
+
throw new Error("Webhook secret is required");
|
|
532
|
+
}
|
|
533
|
+
this.secret = secret;
|
|
534
|
+
this.toleranceSeconds = toleranceSeconds;
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Verify a webhook signature and return the parsed event
|
|
538
|
+
*
|
|
539
|
+
* @param rawBody - Raw request body (string or Buffer, before JSON parsing)
|
|
540
|
+
* @param signatureHeader - Value of the `BeeL-Signature` header
|
|
541
|
+
* @returns Parsed webhook event
|
|
542
|
+
* @throws {WebhookVerificationError} If signature is invalid or event is too old
|
|
543
|
+
*
|
|
544
|
+
* @example Basic usage
|
|
545
|
+
* ```typescript
|
|
546
|
+
* const verifier = new WebhookVerifier('your_secret');
|
|
547
|
+
* const event = verifier.verify(rawBody, signature);
|
|
548
|
+
*
|
|
549
|
+
* if (event.type === 'verifactu.status.updated') {
|
|
550
|
+
* console.log('Invoice:', event.data.invoice_number);
|
|
551
|
+
* console.log('Status:', event.data.new_status);
|
|
552
|
+
* }
|
|
553
|
+
* ```
|
|
554
|
+
*/
|
|
555
|
+
verify(rawBody, signatureHeader) {
|
|
556
|
+
if (!signatureHeader) {
|
|
557
|
+
throw new WebhookVerificationError("Missing BeeL-Signature header");
|
|
558
|
+
}
|
|
559
|
+
const { timestamp, signature } = this.parseSignatureHeader(signatureHeader);
|
|
560
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
561
|
+
const age = Math.abs(now - timestamp);
|
|
562
|
+
if (age > this.toleranceSeconds) {
|
|
563
|
+
throw new WebhookVerificationError(
|
|
564
|
+
`Webhook timestamp too old (${age}s ago, max ${this.toleranceSeconds}s allowed)`
|
|
565
|
+
);
|
|
566
|
+
}
|
|
567
|
+
const bodyString = Buffer.isBuffer(rawBody) ? rawBody.toString("utf8") : rawBody;
|
|
568
|
+
const expectedSignature = this.computeSignature(timestamp, bodyString);
|
|
569
|
+
if (!this.secureCompare(signature, expectedSignature)) {
|
|
570
|
+
throw new WebhookVerificationError("Invalid webhook signature");
|
|
571
|
+
}
|
|
572
|
+
try {
|
|
573
|
+
return JSON.parse(bodyString);
|
|
574
|
+
} catch (error) {
|
|
575
|
+
throw new WebhookVerificationError("Invalid JSON in webhook body");
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Parse the BeeL-Signature header
|
|
580
|
+
*
|
|
581
|
+
* Format: `t=1741362026,v1=3c4f7a2e...`
|
|
582
|
+
*/
|
|
583
|
+
parseSignatureHeader(header) {
|
|
584
|
+
const parts = header.split(",");
|
|
585
|
+
let timestamp;
|
|
586
|
+
let signature;
|
|
587
|
+
for (const part of parts) {
|
|
588
|
+
const [key, value] = part.split("=", 2);
|
|
589
|
+
if (key === "t") {
|
|
590
|
+
timestamp = parseInt(value, 10);
|
|
591
|
+
} else if (key === "v1") {
|
|
592
|
+
signature = value;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
if (!timestamp || !signature) {
|
|
596
|
+
throw new WebhookVerificationError(
|
|
597
|
+
"Invalid signature header format (expected: t=timestamp,v1=signature)"
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
if (isNaN(timestamp)) {
|
|
601
|
+
throw new WebhookVerificationError("Invalid timestamp in signature header");
|
|
602
|
+
}
|
|
603
|
+
return { timestamp, signature };
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
* Compute HMAC-SHA256 signature
|
|
607
|
+
*
|
|
608
|
+
* Algorithm:
|
|
609
|
+
* 1. Build signed payload: `{timestamp}.{raw_body}`
|
|
610
|
+
* 2. Compute: HMAC-SHA256(secret, signed_payload)
|
|
611
|
+
* 3. Return hex digest
|
|
612
|
+
*/
|
|
613
|
+
computeSignature(timestamp, body) {
|
|
614
|
+
const signedPayload = `${timestamp}.${body}`;
|
|
615
|
+
return createHmac("sha256", this.secret).update(signedPayload, "utf8").digest("hex");
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* Constant-time string comparison to prevent timing attacks
|
|
619
|
+
*/
|
|
620
|
+
secureCompare(a, b) {
|
|
621
|
+
if (a.length !== b.length) {
|
|
622
|
+
return false;
|
|
623
|
+
}
|
|
624
|
+
const bufferA = Buffer.from(a, "utf8");
|
|
625
|
+
const bufferB = Buffer.from(b, "utf8");
|
|
626
|
+
return timingSafeEqual(bufferA, bufferB);
|
|
627
|
+
}
|
|
628
|
+
};
|
|
629
|
+
var WebhookVerificationError = class extends Error {
|
|
630
|
+
constructor(message) {
|
|
631
|
+
super(message);
|
|
632
|
+
this.name = "WebhookVerificationError";
|
|
633
|
+
}
|
|
634
|
+
};
|
|
635
|
+
|
|
636
|
+
// src/builders/customer-builder.ts
|
|
637
|
+
var CustomerBuilder = class _CustomerBuilder {
|
|
638
|
+
constructor() {
|
|
639
|
+
}
|
|
640
|
+
static create() {
|
|
641
|
+
return new _CustomerBuilder();
|
|
642
|
+
}
|
|
643
|
+
name(name) {
|
|
644
|
+
this._name = name;
|
|
645
|
+
return this;
|
|
646
|
+
}
|
|
647
|
+
nif(nif) {
|
|
648
|
+
this._nif = nif;
|
|
649
|
+
return this;
|
|
650
|
+
}
|
|
651
|
+
email(email) {
|
|
652
|
+
this._email = email;
|
|
653
|
+
return this;
|
|
654
|
+
}
|
|
655
|
+
phone(phone) {
|
|
656
|
+
this._phone = phone;
|
|
657
|
+
return this;
|
|
658
|
+
}
|
|
659
|
+
address(street, number, postalCode, city, province, country, countryCode = "ES") {
|
|
660
|
+
this._address = { street, number, postal_code: postalCode, city, province, country, country_code: countryCode };
|
|
661
|
+
return this;
|
|
662
|
+
}
|
|
663
|
+
notes(notes) {
|
|
664
|
+
this._notes = notes;
|
|
665
|
+
return this;
|
|
666
|
+
}
|
|
667
|
+
build() {
|
|
668
|
+
if (!this._name || this._name.trim().length === 0) throw new Error("Legal name is required");
|
|
669
|
+
if (!this._nif || this._nif.trim().length === 0) throw new Error("NIF/CIF is required");
|
|
670
|
+
if (!this._address) throw new Error("Address is required");
|
|
671
|
+
return {
|
|
672
|
+
legal_name: this._name,
|
|
673
|
+
nif: this._nif,
|
|
674
|
+
email: this._email,
|
|
675
|
+
phone: this._phone,
|
|
676
|
+
address: this._address,
|
|
677
|
+
notes: this._notes
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
};
|
|
681
|
+
|
|
682
|
+
// src/builders/invoice-builder.ts
|
|
683
|
+
var InvoiceBuilder = class _InvoiceBuilder {
|
|
684
|
+
constructor() {
|
|
685
|
+
this._type = "STANDARD";
|
|
686
|
+
this._lines = [];
|
|
687
|
+
}
|
|
688
|
+
static create() {
|
|
689
|
+
return new _InvoiceBuilder();
|
|
690
|
+
}
|
|
691
|
+
type(type) {
|
|
692
|
+
this._type = type;
|
|
693
|
+
return this;
|
|
694
|
+
}
|
|
695
|
+
forCustomer(customerId) {
|
|
696
|
+
this._customerId = customerId;
|
|
697
|
+
return this;
|
|
698
|
+
}
|
|
699
|
+
operationDate(date) {
|
|
700
|
+
this._operationDate = date instanceof Date ? date.toISOString().split("T")[0] : date;
|
|
701
|
+
return this;
|
|
702
|
+
}
|
|
703
|
+
dueDate(date) {
|
|
704
|
+
this._dueDate = date instanceof Date ? date.toISOString().split("T")[0] : date;
|
|
705
|
+
return this;
|
|
706
|
+
}
|
|
707
|
+
addLine(description, quantity, unitPrice, discountPercentage = 0) {
|
|
708
|
+
this._lines.push({ description, quantity, unit_price: unitPrice, discount_percentage: discountPercentage });
|
|
709
|
+
return this;
|
|
710
|
+
}
|
|
711
|
+
addLineObject(line) {
|
|
712
|
+
this._lines.push(line);
|
|
713
|
+
return this;
|
|
714
|
+
}
|
|
715
|
+
notes(notes) {
|
|
716
|
+
this._notes = notes;
|
|
717
|
+
return this;
|
|
718
|
+
}
|
|
719
|
+
series(id) {
|
|
720
|
+
this._seriesId = id;
|
|
721
|
+
return this;
|
|
722
|
+
}
|
|
723
|
+
metadata(data) {
|
|
724
|
+
this._metadata = data;
|
|
725
|
+
return this;
|
|
726
|
+
}
|
|
727
|
+
build() {
|
|
728
|
+
if (!this._customerId) throw new Error("Customer ID is required");
|
|
729
|
+
if (this._lines.length === 0) throw new Error("At least one invoice line is required");
|
|
730
|
+
const recipient = { customer_id: this._customerId };
|
|
731
|
+
return {
|
|
732
|
+
type: this._type,
|
|
733
|
+
operation_date: this._operationDate,
|
|
734
|
+
due_date: this._dueDate,
|
|
735
|
+
recipient,
|
|
736
|
+
lines: this._lines,
|
|
737
|
+
notes: this._notes,
|
|
738
|
+
series_id: this._seriesId,
|
|
739
|
+
metadata: this._metadata
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
};
|
|
743
|
+
export {
|
|
744
|
+
BeeL,
|
|
745
|
+
BeeLApiError,
|
|
746
|
+
BeeLAuthError,
|
|
747
|
+
BeeLConflictError,
|
|
748
|
+
BeeLNotFoundError,
|
|
749
|
+
BeeLRateLimitError,
|
|
750
|
+
BeeLValidationError,
|
|
751
|
+
CustomerBuilder,
|
|
752
|
+
InvoiceBuilder,
|
|
753
|
+
WebhookVerificationError,
|
|
754
|
+
WebhookVerifier
|
|
755
|
+
};
|
|
756
|
+
//# sourceMappingURL=index.mjs.map
|