HashHelper::generateInvoiceHash()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 11
c 1
b 0
f 0
dl 0
loc 13
rs 9.9
cc 1
nc 1
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Squareetlabs\VeriFactu\Helpers;
6
7
class HashHelper
8
{
9
    private static array $invoiceRequiredFields = [
10
        'issuer_tax_id',
11
        'invoice_number',
12
        'issue_date',
13
        'invoice_type',
14
        'total_tax',
15
        'total_amount',
16
        'previous_hash',
17
        'generated_at',
18
    ];
19
20
    /**
21
     * Generates the hash for an invoice record according to AEAT specifications.
22
     * 
23
     * CRITICAL: Field names MUST match the official AEAT XML field names as per
24
     * "Detalle de las especificaciones técnicas para generación de la huella o hash
25
     * de los registros de facturación" v0.1.2 (27/08/2024), page 6.
26
     *
27
     * @param array $data Invoice record data with snake_case keys (for compatibility).
28
     * @return array ['hash' => string, 'inputString' => string]
29
     */
30
    public static function generateInvoiceHash(array $data): array
31
    {
32
        self::validateData(self::$invoiceRequiredFields, $data);                
33
        $inputString = self::field('IDEmisorFactura', $data['issuer_tax_id']);
34
        $inputString .= self::field('NumSerieFactura', $data['invoice_number']);
35
        $inputString .= self::field('FechaExpedicionFactura', $data['issue_date']);
36
        $inputString .= self::field('TipoFactura', $data['invoice_type']);
37
        $inputString .= self::field('CuotaTotal', $data['total_tax']);
38
        $inputString .= self::field('ImporteTotal', $data['total_amount']);
39
        $inputString .= self::field('Huella', $data['previous_hash']);
40
        $inputString .= self::field('FechaHoraHusoGenRegistro', $data['generated_at'], false);                
41
        $hash = strtoupper(hash('sha256', $inputString, false));        
42
        return ['hash' => $hash, 'inputString' => $inputString];
43
    }
44
45
    private static function validateData(array $requiredFields, array $data): void
46
    {
47
        $missing = array_diff($requiredFields, array_keys($data));
48
        if (!empty($missing)) {
49
            throw new \InvalidArgumentException('Missing required fields: ' . implode(', ', $missing));
50
        }
51
        $extra = array_diff(array_keys($data), $requiredFields);
52
        if (!empty($extra)) {
53
            throw new \InvalidArgumentException('Unexpected fields: ' . implode(', ', $extra));
54
        }
55
    }
56
57
    private static function field(string $name, string $value, bool $includeSeparator = true): string
58
    {
59
        $value = trim($value);
60
        return "{$name}={$value}" . ($includeSeparator ? '&' : '');
61
    }
62
}