cargos-sdk 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/LICENSE +21 -0
- package/README.md +655 -0
- package/dist/cargos-sdk.cjs +18444 -0
- package/dist/cargos-sdk.d.ts +171 -0
- package/dist/cargos-sdk.d.ts.map +1 -0
- package/dist/cargos-sdk.js +18449 -0
- package/package.json +74 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
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
ADDED
|
@@ -0,0 +1,655 @@
|
|
|
1
|
+
# CARGOS API TypeScript SDK
|
|
2
|
+
|
|
3
|
+
> **⚠️ Disclaimer**: This SDK is for **personal/internal use only**. It is not production-ready, not officially supported, and comes with no guarantees. Use at your own risk.
|
|
4
|
+
|
|
5
|
+
TypeScript SDK for the **Cargos Polizia di Stato** service - a mandatory system for Italian car rental companies to report rental contracts to the Italian State Police for terrorism prevention and public safety purposes.
|
|
6
|
+
|
|
7
|
+
**Legal Basis**: Art. 17, Law Decree October 4, 2018, n. 113
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
The Ca.R.G.O.S. (Centro Anagrafe Rifornimento Gestione Operativa Sicurezza) service requires all Italian car rental companies to report rental contract data including:
|
|
12
|
+
|
|
13
|
+
- Driver identification information
|
|
14
|
+
- Vehicle details
|
|
15
|
+
- Rental duration and locations
|
|
16
|
+
- Agency information
|
|
17
|
+
|
|
18
|
+
The system verifies driver eligibility and checks for security concerns in real-time.
|
|
19
|
+
|
|
20
|
+
## Features
|
|
21
|
+
|
|
22
|
+
- ✅ Full TypeScript support with strong typing
|
|
23
|
+
- ✅ AES-256-CBC encryption (3DES deprecated but supported)
|
|
24
|
+
- ✅ Token management with automatic refresh
|
|
25
|
+
- ✅ Contract validation before submission
|
|
26
|
+
- ✅ Batch processing (automatic chunking for 100+ contracts)
|
|
27
|
+
- ✅ Check contracts before sending
|
|
28
|
+
- ✅ Download and parse coding tables
|
|
29
|
+
- ✅ Comprehensive error handling
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install cargos-sdk
|
|
35
|
+
# or
|
|
36
|
+
yarn add cargos-sdk
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Or import directly:
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
import { CargosClient, RentalContract, PaymentType } from './cargos-sdk';
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Quick Start
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
import { CargosClient, RentalContract, PaymentType, VehicleType, DocumentType } from './cargos-sdk';
|
|
49
|
+
|
|
50
|
+
const client = new CargosClient(
|
|
51
|
+
'your_username',
|
|
52
|
+
'your_password',
|
|
53
|
+
'your_api_key_at_least_48_chars'
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
const contract: RentalContract = {
|
|
57
|
+
id: 'RENT-2024-001234',
|
|
58
|
+
createdDate: new Date('2024-02-01T10:30:00'),
|
|
59
|
+
paymentType: PaymentType.CARD,
|
|
60
|
+
checkoutDate: new Date('2024-02-01T14:00:00'),
|
|
61
|
+
checkoutLocation: { code: 80 }, // Rome
|
|
62
|
+
checkoutAddress: 'Via del Castro Pretorio 10, Roma',
|
|
63
|
+
checkinDate: new Date('2024-02-08T18:00:00'),
|
|
64
|
+
checkinLocation: { code: 80 },
|
|
65
|
+
checkinAddress: 'Via del Castro Pretorio 10, Roma',
|
|
66
|
+
operatorId: 'OP-001',
|
|
67
|
+
agency: {
|
|
68
|
+
id: 'AG-ROME-001',
|
|
69
|
+
name: 'RentCar Roma Center',
|
|
70
|
+
location: { code: 80 },
|
|
71
|
+
address: 'Via del Castro Pretorio 10, Roma',
|
|
72
|
+
phone: '+39-06-12345678'
|
|
73
|
+
},
|
|
74
|
+
vehicle: {
|
|
75
|
+
type: VehicleType.CAR,
|
|
76
|
+
brand: 'Fiat',
|
|
77
|
+
model: '500',
|
|
78
|
+
plate: 'AB123CD',
|
|
79
|
+
color: 'Nero',
|
|
80
|
+
hasGPS: true
|
|
81
|
+
},
|
|
82
|
+
mainDriver: {
|
|
83
|
+
surname: 'Rossi',
|
|
84
|
+
name: 'Mario',
|
|
85
|
+
birthDate: new Date('1980-05-15'),
|
|
86
|
+
birthPlace: { code: 80 },
|
|
87
|
+
citizenship: { code: 380 }, // Italy
|
|
88
|
+
documentType: DocumentType.ID_CARD,
|
|
89
|
+
documentNumber: 'AA123456789',
|
|
90
|
+
documentIssuePlace: { code: 80 },
|
|
91
|
+
licenseNumber: 'IT1234567890',
|
|
92
|
+
licenseIssuePlace: { code: 80 }
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// Send contract
|
|
97
|
+
const result = await client.sendContracts([contract]);
|
|
98
|
+
|
|
99
|
+
if (result.responses[0].esito) {
|
|
100
|
+
console.log('✓ Sent:', result.responses[0].transactionid);
|
|
101
|
+
} else {
|
|
102
|
+
console.error('✗ Error:', result.responses[0].errore);
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Authentication
|
|
107
|
+
|
|
108
|
+
Get credentials from **Questura** (Provincial Police Station):
|
|
109
|
+
|
|
110
|
+
1. Request **CARGOS portal activation** from your provincial police station
|
|
111
|
+
2. Receive: `username`, `password`, and `API_KEY` (48+ characters for AES)
|
|
112
|
+
|
|
113
|
+
**Important**:
|
|
114
|
+
- API Key must be **at least 48 characters** for AES encryption (recommended)
|
|
115
|
+
- For AES: First 32 chars are the key, last 16 are the IV
|
|
116
|
+
- 3DES (deprecated) requires minimum 24 characters
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
const client = new CargosClient(
|
|
120
|
+
process.env.CARGOS_USERNAME,
|
|
121
|
+
process.env.CARGOS_PASSWORD,
|
|
122
|
+
process.env.CARGOS_API_KEY // min 48 chars
|
|
123
|
+
);
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Core Methods
|
|
127
|
+
|
|
128
|
+
### checkContracts()
|
|
129
|
+
|
|
130
|
+
Validate contracts before sending (recommended for testing):
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
const checkResult = await client.checkContracts([contract]);
|
|
134
|
+
|
|
135
|
+
for (const response of checkResult.responses) {
|
|
136
|
+
if (!response.esito) {
|
|
137
|
+
console.error('Validation error:', response.errore?.error_description);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Returns row-by-row validation errors (syntax and semantics).
|
|
143
|
+
|
|
144
|
+
### sendContracts()
|
|
145
|
+
|
|
146
|
+
Send up to 100 contracts in a single request:
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
const sendResult = await client.sendContracts([contract1, contract2]);
|
|
150
|
+
|
|
151
|
+
for (const response of sendResult.responses) {
|
|
152
|
+
if (response.esito) {
|
|
153
|
+
console.log('Transaction ID:', response.transactionid);
|
|
154
|
+
} else {
|
|
155
|
+
console.error('Error:', response.errore);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Limits**:
|
|
161
|
+
- Maximum 100 contracts per request
|
|
162
|
+
- Use `batchSendContracts()` for 100+
|
|
163
|
+
|
|
164
|
+
### batchSendContracts()
|
|
165
|
+
|
|
166
|
+
Automatically batch large contract sets:
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
const contracts: RentalContract[] = [...]; // 250 contracts
|
|
170
|
+
|
|
171
|
+
const batchResults = await client.batchSendContracts(contracts);
|
|
172
|
+
// Automatically split into 3 requests (100, 100, 50)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### getTable()
|
|
176
|
+
|
|
177
|
+
Download specific coding table:
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
import { TableId } from './cargos-sdk';
|
|
181
|
+
|
|
182
|
+
const paymentTypes = await client.getTable(TableId.PAYMENT_TYPE);
|
|
183
|
+
const buffer = paymentTypes.file;
|
|
184
|
+
|
|
185
|
+
if (paymentTypes.esito && buffer) {
|
|
186
|
+
const csv = buffer.toString('utf-8');
|
|
187
|
+
console.log(csv);
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Available tables:
|
|
192
|
+
- `TableId.PAYMENT_TYPE` (0)
|
|
193
|
+
- `TableId.LOCATIONS` (1)
|
|
194
|
+
- `TableId.VEHICLE_TYPE` (2)
|
|
195
|
+
- `TableId.DOCUMENT_TYPE` (3)
|
|
196
|
+
|
|
197
|
+
### getAllTables()
|
|
198
|
+
|
|
199
|
+
Download all coding tables at once:
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
const tables = await client.getAllTables();
|
|
203
|
+
|
|
204
|
+
for (const [name, buffer] of tables) {
|
|
205
|
+
console.log(`${name}: ${buffer.length} bytes`);
|
|
206
|
+
|
|
207
|
+
// Parse as CSV with '#' separator
|
|
208
|
+
const map = parseTableCSV(buffer);
|
|
209
|
+
for (const [code, value] of map) {
|
|
210
|
+
console.log(` ${code} => ${value}`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Data Types
|
|
216
|
+
|
|
217
|
+
### PaymentType
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
enum PaymentType {
|
|
221
|
+
CASH = 'C',
|
|
222
|
+
CARD = 'T',
|
|
223
|
+
BANK = 'B',
|
|
224
|
+
OTHER = 'A'
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### VehicleType
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
enum VehicleType {
|
|
232
|
+
CAR = 'A',
|
|
233
|
+
MOTORCYCLE = 'M',
|
|
234
|
+
TRUCK = 'C',
|
|
235
|
+
OTHER = 'A'
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### DocumentType
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
enum DocumentType {
|
|
243
|
+
PASSPORT = 'P',
|
|
244
|
+
ID_CARD = 'C',
|
|
245
|
+
DRIVERS_LICENSE = 'P',
|
|
246
|
+
VISA = 'V',
|
|
247
|
+
RESIDENCE_PERMIT = 'S'
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Location
|
|
252
|
+
|
|
253
|
+
Reference locations by police code (Questura/Comune):
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
interface Location {
|
|
257
|
+
code: number; // Police location code (e.g., 80 for Rome)
|
|
258
|
+
name?: string; // Human-readable name
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Common location codes:
|
|
263
|
+
- Rome (Lazio): 80
|
|
264
|
+
- Milan (Lombardy): 108
|
|
265
|
+
- Naples (Campania): 63
|
|
266
|
+
- Turin (Piedmont): 1
|
|
267
|
+
- Florence (Tuscany): 48
|
|
268
|
+
|
|
269
|
+
Download the full `LOCATIONS` table to get all codes.
|
|
270
|
+
|
|
271
|
+
### Driver
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
interface Driver {
|
|
275
|
+
surname: string; // Max 50 chars
|
|
276
|
+
name: string; // Max 30 chars
|
|
277
|
+
birthDate: Date; // DD/MM/YYYY
|
|
278
|
+
birthPlace: Location; // Comune or foreign country
|
|
279
|
+
citizenship: Location; // Country code
|
|
280
|
+
residencePlace?: Location; // Optional location
|
|
281
|
+
residenceAddress?: string; // Optional, max 150 chars
|
|
282
|
+
documentType: DocumentType; // ID type
|
|
283
|
+
documentNumber: string; // Max 20 chars
|
|
284
|
+
documentIssuePlace: Location; // Where ID was issued
|
|
285
|
+
licenseNumber: string; // Driver's license, max 20 chars
|
|
286
|
+
licenseIssuePlace: Location; // Where license issued
|
|
287
|
+
phone?: string; // Optional, max 20 chars
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
**Important**: Secondary driver must have ALL fields populated or omitted entirely.
|
|
292
|
+
|
|
293
|
+
### Vehicle
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
interface Vehicle {
|
|
297
|
+
type: VehicleType; // From enum
|
|
298
|
+
brand: string; // Max 50 chars
|
|
299
|
+
model: string; // Max 100 chars
|
|
300
|
+
plate: string; // License plate, max 15 chars
|
|
301
|
+
color?: string; // Optional, max 50 chars
|
|
302
|
+
hasGPS?: boolean; // Optional, 0 or 1
|
|
303
|
+
hasEngineBlock?: boolean; // Optional, 0 or 1
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Agency
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
interface Agency {
|
|
311
|
+
id: string; // Unique, max 30 chars
|
|
312
|
+
name: string; // Max 70 chars
|
|
313
|
+
location: Location; // Office location
|
|
314
|
+
address: string; // Max 150 chars
|
|
315
|
+
phone: string; // Max 20 chars, > 3 chars
|
|
316
|
+
}
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### RentalContract
|
|
320
|
+
|
|
321
|
+
```typescript
|
|
322
|
+
interface RentalContract {
|
|
323
|
+
id: string; // Unique, max 50 chars
|
|
324
|
+
createdDate: Date; // Contract signature time
|
|
325
|
+
paymentType: PaymentType; // From enum
|
|
326
|
+
checkoutDate: Date; // Vehicle pickup time
|
|
327
|
+
checkoutLocation: Location; // Pickup location
|
|
328
|
+
checkoutAddress: string; // Pickup address, max 150 chars
|
|
329
|
+
checkinDate: Date; // Vehicle return time
|
|
330
|
+
checkinLocation: Location; // Return location
|
|
331
|
+
checkinAddress: string; // Return address, max 150 chars
|
|
332
|
+
operatorId: string; // Employee ID, max 50 chars
|
|
333
|
+
agency: Agency; // Agency details
|
|
334
|
+
vehicle: Vehicle; // Vehicle details
|
|
335
|
+
mainDriver: Driver; // Primary driver
|
|
336
|
+
secondaryDriver?: Driver; // Optional second driver
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## Validation
|
|
341
|
+
|
|
342
|
+
Use `isValidContractData()` to validate before sending:
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
import { isValidContractData } from './cargos-sdk';
|
|
346
|
+
|
|
347
|
+
const errors = isValidContractData(contract);
|
|
348
|
+
|
|
349
|
+
if (errors.length > 0) {
|
|
350
|
+
console.error('Validation errors:');
|
|
351
|
+
errors.forEach(err => console.error(` - ${err}`));
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
Checks:
|
|
356
|
+
- Contract ID length (1-50)
|
|
357
|
+
- Agency ID length (1-30)
|
|
358
|
+
- Address minimum length (3)
|
|
359
|
+
- Document number minimum length (5)
|
|
360
|
+
- License number minimum length (5)
|
|
361
|
+
- Vehicle plate minimum length (3)
|
|
362
|
+
- Secondary driver completeness
|
|
363
|
+
|
|
364
|
+
## Workflow
|
|
365
|
+
|
|
366
|
+
### Recommended Process
|
|
367
|
+
|
|
368
|
+
1. **Prepare** contract data from your rental management system
|
|
369
|
+
2. **Validate** with `isValidContractData()`
|
|
370
|
+
3. **Check** with `checkContracts()` (optional but recommended)
|
|
371
|
+
4. **Download** tables if you need to reference codes with `getAllTables()`
|
|
372
|
+
5. **Send** with `sendContracts()` or `batchSendContracts()`
|
|
373
|
+
6. **Store** transaction IDs for audit trail
|
|
374
|
+
|
|
375
|
+
### Code Example
|
|
376
|
+
|
|
377
|
+
```typescript
|
|
378
|
+
const client = new CargosClient(username, password, apiKey);
|
|
379
|
+
|
|
380
|
+
// Prepare contracts
|
|
381
|
+
const contracts = readContractsFromDatabase();
|
|
382
|
+
|
|
383
|
+
// Validate
|
|
384
|
+
for (const contract of contracts) {
|
|
385
|
+
const errors = isValidContractData(contract);
|
|
386
|
+
if (errors.length > 0) {
|
|
387
|
+
logger.error(`Contract ${contract.id} invalid:`, errors);
|
|
388
|
+
continue; // Skip invalid contracts
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Optional: Check before sending
|
|
393
|
+
const checkResult = await client.checkContracts(contracts);
|
|
394
|
+
for (const response of checkResult.responses) {
|
|
395
|
+
if (!response.esito) {
|
|
396
|
+
logger.warn(`Check failed: ${response.errore?.error_description}`);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Send in batches (auto-chunked to 100 max)
|
|
401
|
+
try {
|
|
402
|
+
const results = await client.batchSendContracts(contracts);
|
|
403
|
+
|
|
404
|
+
for (let i = 0; i < results.length; i++) {
|
|
405
|
+
const result = results[i];
|
|
406
|
+
for (let j = 0; j < result.responses.length; j++) {
|
|
407
|
+
const response = result.responses[j];
|
|
408
|
+
if (response.esito) {
|
|
409
|
+
// Store transaction ID
|
|
410
|
+
storeTransactionId(contracts[i * 100 + j].id, response.transactionid);
|
|
411
|
+
} else {
|
|
412
|
+
logger.error(`Failed: ${response.errore?.error_description}`);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
} catch (error) {
|
|
417
|
+
logger.error('Batch send failed:', error);
|
|
418
|
+
}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
## Error Handling
|
|
422
|
+
|
|
423
|
+
All responses include error information:
|
|
424
|
+
|
|
425
|
+
```typescript
|
|
426
|
+
interface ErrorResponse {
|
|
427
|
+
error: string;
|
|
428
|
+
error_description: string;
|
|
429
|
+
error_code: number;
|
|
430
|
+
timestamp: string;
|
|
431
|
+
}
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
Common error codes:
|
|
435
|
+
- **1**: Invalid token
|
|
436
|
+
- **2**: Invalid API key
|
|
437
|
+
- **3**: Syntax error in contract data
|
|
438
|
+
- **4**: Semantic error in contract data
|
|
439
|
+
- **5**: Contract already sent
|
|
440
|
+
- **6**: Invalid location code
|
|
441
|
+
- **7**: Invalid payment type
|
|
442
|
+
|
|
443
|
+
Example handling:
|
|
444
|
+
|
|
445
|
+
```typescript
|
|
446
|
+
try {
|
|
447
|
+
const result = await client.sendContracts([contract]);
|
|
448
|
+
|
|
449
|
+
if (result.error) {
|
|
450
|
+
console.error(`API Error (${result.error.error_code}):`, result.error.error_description);
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
for (const response of result.responses) {
|
|
455
|
+
if (!response.esito) {
|
|
456
|
+
console.error(`Contract Error (${response.errore?.error_code}):`,
|
|
457
|
+
response.errore?.error_description);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
} catch (error) {
|
|
461
|
+
console.error('Network or parsing error:', error);
|
|
462
|
+
}
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
## Security
|
|
466
|
+
|
|
467
|
+
### Token Management
|
|
468
|
+
|
|
469
|
+
Tokens are:
|
|
470
|
+
- Encrypted with your API Key using AES-256-CBC
|
|
471
|
+
- Cached in memory with 5-minute safety margin before expiry
|
|
472
|
+
- Automatically refreshed when expired
|
|
473
|
+
|
|
474
|
+
```typescript
|
|
475
|
+
// Token is automatically managed
|
|
476
|
+
const result = await client.sendContracts(contracts);
|
|
477
|
+
// Token obtained/refreshed internally as needed
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
### API Key Security
|
|
481
|
+
|
|
482
|
+
**Never**:
|
|
483
|
+
- Hardcode API keys in source
|
|
484
|
+
- Commit API keys to version control
|
|
485
|
+
- Log API keys or encrypted tokens
|
|
486
|
+
- Share credentials via email
|
|
487
|
+
|
|
488
|
+
**Always**:
|
|
489
|
+
- Use environment variables
|
|
490
|
+
- Rotate keys periodically
|
|
491
|
+
- Use secure key management (vaults, KMS)
|
|
492
|
+
- Log only transaction IDs, not tokens
|
|
493
|
+
|
|
494
|
+
```typescript
|
|
495
|
+
// ✅ Good
|
|
496
|
+
const apiKey = process.env.CARGOS_API_KEY;
|
|
497
|
+
const client = new CargosClient(username, password, apiKey);
|
|
498
|
+
|
|
499
|
+
// ❌ Bad
|
|
500
|
+
const client = new CargosClient('user', 'pass', 'HARDCODED_KEY_HERE');
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
### HTTPS
|
|
504
|
+
|
|
505
|
+
All communication with Cargos service is over HTTPS (TLS 1.2+).
|
|
506
|
+
|
|
507
|
+
## Data Retention
|
|
508
|
+
|
|
509
|
+
- Contract data is retained for **maximum 7 days**
|
|
510
|
+
- Transaction IDs should be stored locally for audit
|
|
511
|
+
- Implement your own retention policies per company requirements
|
|
512
|
+
|
|
513
|
+
## Troubleshooting
|
|
514
|
+
|
|
515
|
+
### "Invalid API Key" Error
|
|
516
|
+
|
|
517
|
+
- Check API Key length (must be 48+ for AES)
|
|
518
|
+
- Verify first 32 chars are the encryption key, last 16 are IV
|
|
519
|
+
- Confirm API Key hasn't expired (request new from police station)
|
|
520
|
+
|
|
521
|
+
### "Token not valid" Error
|
|
522
|
+
|
|
523
|
+
- Token is automatically refreshed; retry the operation
|
|
524
|
+
- Check username/password are correct
|
|
525
|
+
- Verify credentials haven't been reset
|
|
526
|
+
|
|
527
|
+
### "Invalid location code" Error
|
|
528
|
+
|
|
529
|
+
- Download `LOCATIONS` table to see valid codes
|
|
530
|
+
- Location codes reference specific police jurisdictions
|
|
531
|
+
- Use exact numeric code from the table
|
|
532
|
+
|
|
533
|
+
### "Semantic error" in contract data
|
|
534
|
+
|
|
535
|
+
- Use `checkContracts()` first to see detailed validation errors
|
|
536
|
+
- Check all required fields are populated
|
|
537
|
+
- Verify field lengths match specifications
|
|
538
|
+
- Confirm date formats are DD/MM/YYYY
|
|
539
|
+
|
|
540
|
+
### "Maximum 100 contracts per request"
|
|
541
|
+
|
|
542
|
+
- Use `batchSendContracts()` instead of `sendContracts()`
|
|
543
|
+
- Automatically handles batching
|
|
544
|
+
|
|
545
|
+
## Examples
|
|
546
|
+
|
|
547
|
+
See `cargos-example.ts` for:
|
|
548
|
+
- Basic single contract submission
|
|
549
|
+
- Batch processing (100+ contracts)
|
|
550
|
+
- Downloading and parsing tables
|
|
551
|
+
- Secondary driver handling
|
|
552
|
+
- Error handling patterns
|
|
553
|
+
|
|
554
|
+
## API Reference
|
|
555
|
+
|
|
556
|
+
### CargosClient
|
|
557
|
+
|
|
558
|
+
```typescript
|
|
559
|
+
class CargosClient {
|
|
560
|
+
constructor(username: string, password: string, apiKey: string);
|
|
561
|
+
|
|
562
|
+
// Methods
|
|
563
|
+
async getToken(): Promise<string>;
|
|
564
|
+
async checkContracts(contracts: RentalContract[]): Promise<CheckResponse>;
|
|
565
|
+
async sendContracts(contracts: RentalContract[]): Promise<SendResponse>;
|
|
566
|
+
async batchSendContracts(contracts: RentalContract[]): Promise<SendResponse[]>;
|
|
567
|
+
async getTable(tableId: TableId): Promise<TableResponse>;
|
|
568
|
+
async getAllTables(): Promise<Map<string, Buffer>>;
|
|
569
|
+
}
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
### Utilities
|
|
573
|
+
|
|
574
|
+
```typescript
|
|
575
|
+
// Validate contract data
|
|
576
|
+
function isValidContractData(contract: RentalContract): string[];
|
|
577
|
+
|
|
578
|
+
// Parse table CSV (# separated, UTF-8)
|
|
579
|
+
function parseTableCSV(data: Buffer): Map<string, string>;
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
## Technical Details
|
|
583
|
+
|
|
584
|
+
### Record Format
|
|
585
|
+
|
|
586
|
+
Contracts are formatted as fixed-width 1505-character records following CARGOS specifications:
|
|
587
|
+
|
|
588
|
+
- Fields: 46 data elements
|
|
589
|
+
- Total width: 1505 characters
|
|
590
|
+
- Multiple records per batch (max 100)
|
|
591
|
+
- Character set: UTF-8
|
|
592
|
+
- Date format: DD/MM/YYYY or DD/MM/YYYY HH:MM
|
|
593
|
+
|
|
594
|
+
### Encryption
|
|
595
|
+
|
|
596
|
+
**AES Method** (recommended, default):
|
|
597
|
+
- Algorithm: AES-256-CBC
|
|
598
|
+
- Mode: Cipher Block Chaining
|
|
599
|
+
- Padding: PKCS7
|
|
600
|
+
- Key size: 256 bits (32 bytes)
|
|
601
|
+
- IV size: 128 bits (16 bytes)
|
|
602
|
+
- Output: Base64
|
|
603
|
+
|
|
604
|
+
```
|
|
605
|
+
API Key (48+ chars):
|
|
606
|
+
├─ Chars 0-31: Encryption Key (32 bytes)
|
|
607
|
+
└─ Chars 32-47: Initialization Vector (16 bytes)
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
**3DES Method** (deprecated):
|
|
611
|
+
- Algorithm: Triple DES
|
|
612
|
+
- Mode: ECB
|
|
613
|
+
- Padding: PKCS7
|
|
614
|
+
- Key size: 192 bits (24 bytes)
|
|
615
|
+
- Output: Base64
|
|
616
|
+
|
|
617
|
+
### Authentication
|
|
618
|
+
|
|
619
|
+
Uses OAuth 2.0 with Bearer token:
|
|
620
|
+
|
|
621
|
+
```
|
|
622
|
+
Authorization: Bearer <encrypted_token>
|
|
623
|
+
Organization: <username>
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
## Legal Compliance
|
|
627
|
+
|
|
628
|
+
This SDK implements the technical specifications from:
|
|
629
|
+
|
|
630
|
+
- **Decreto Legge** 4 October 2018, n. 113 (Art. 17)
|
|
631
|
+
- **Decreto Ministeriale** 29 October 2021
|
|
632
|
+
- **CARGOS Architectural Documentation** (Rev. 01, 06/02/2024)
|
|
633
|
+
|
|
634
|
+
## Support
|
|
635
|
+
|
|
636
|
+
For issues or questions:
|
|
637
|
+
|
|
638
|
+
1. Check `checkContracts()` output for validation details
|
|
639
|
+
2. Download location/reference tables to verify codes
|
|
640
|
+
3. Review CARGOS documentation: https://cargos.poliziadistato.it
|
|
641
|
+
4. Contact your provincial Questura
|
|
642
|
+
|
|
643
|
+
## License
|
|
644
|
+
|
|
645
|
+
MIT
|
|
646
|
+
|
|
647
|
+
## Changelog
|
|
648
|
+
|
|
649
|
+
### 1.0.0
|
|
650
|
+
- Initial release
|
|
651
|
+
- Full CARGOS API implementation
|
|
652
|
+
- AES-256 encryption
|
|
653
|
+
- Batch processing
|
|
654
|
+
- Contract validation
|
|
655
|
+
- Table downloads
|