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 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