Issues (50)

src/Services/AeatClient.php (1 issue)

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Squareetlabs\VeriFactu\Services;
6
7
use GuzzleHttp\Client;
8
use GuzzleHttp\Exception\GuzzleException;
9
use Squareetlabs\VeriFactu\Models\Invoice;
10
use Illuminate\Support\Facades\Log;
11
12
class AeatClient
13
{
14
    private string $baseUri;
15
    private string $certPath;
16
    private ?string $certPassword;
17
    private Client $client;
18
    private bool $production;
19
20
    public function __construct(string $certPath, ?string $certPassword = null, bool $production = false)
21
    {
22
        $this->certPath = $certPath;
23
        $this->certPassword = $certPassword;
24
        $this->production = $production;
25
        $this->baseUri = $production
26
            ? 'https://www1.aeat.es'
27
            : 'https://prewww1.aeat.es';
28
        $this->client = new Client([
29
            'cert' => ($certPassword === null) ? $certPath : [$certPath, $certPassword],
30
            'base_uri' => $this->baseUri,
31
            'headers' => [
32
                'User-Agent' => 'LaravelVerifactu/1.0',
33
            ],
34
        ]);
35
    }
36
37
    /**
38
     * Send invoice registration to AEAT (dummy implementation, extend as needed)
39
     *
40
     * @param Invoice $invoice
41
     * @return array
42
     */
43
    public function sendInvoice(Invoice $invoice): array
44
    {
45
        // 1. Obtener datos del emisor desde config
46
        $issuer = config('verifactu.issuer');
47
        $issuerName = $issuer['name'] ?? '';
48
        $issuerVat = $issuer['vat'] ?? '';
49
50
        // 2. Mapear Invoice a estructura AEAT (solo campos mínimos para ejemplo)
51
        $cabecera = [
52
            'ObligadoEmision' => [
53
                'NombreRazon' => $issuerName,
54
                'NIF' => $issuerVat,
55
            ],
56
        ];
57
58
        // 3. Mapear destinatarios
59
        $destinatarios = [];
60
        foreach ($invoice->recipients as $recipient) {
61
            $destinatarios[] = [
62
                'NombreRazon' => $recipient->name,
63
                'NIF' => $recipient->tax_id,
64
                // 'IDOtro' => ... // Si aplica
65
            ];
66
        }
67
68
        // 4. Mapear desgloses (Breakdown)
69
        $desgloses = [];
70
        foreach ($invoice->breakdowns as $breakdown) {
71
            $desgloses[] = [
72
                'TipoImpositivo' => $breakdown->tax_rate,
73
                'CuotaRepercutida' => $breakdown->tax_amount,
74
                'BaseImponibleOimporteNoSujeto' => $breakdown->base_amount,
75
                'Impuesto' => '01',
76
                'ClaveRegimen' => '01',
77
                'CalificacionOperacion' => 'S1'
78
            ];
79
        }
80
81
        // 5. Generar huella (hash) usando HashHelper
82
        $hashData = [
83
            'issuer_tax_id' => $issuerVat,
84
            'invoice_number' => $invoice->number,
85
            'issue_date' => $invoice->date->format('d-m-Y'),
86
            'invoice_type' => $invoice->type->value ?? (string)$invoice->type,
0 ignored issues
show
The property value does not exist on string.
Loading history...
87
            'total_tax' => (string)$invoice->tax,
88
            'total_amount' => (string)$invoice->total,
89
            'previous_hash' => '', // Si aplica, para encadenamiento
90
            'generated_at' => now()->format('c'),
91
        ];
92
        $hashResult = \Squareetlabs\VeriFactu\Helpers\HashHelper::generateInvoiceHash($hashData);
93
94
        // 6. Construir RegistroAlta
95
        $registroAlta = [
96
            'IDVersion' => '1.0',
97
            'IDFactura' => [
98
                'IDEmisorFactura' => $issuerVat,
99
                'NumSerieFactura' => $invoice->number,
100
                'FechaExpedicionFactura' => $invoice->date->format('Y-m-d'),
101
            ],
102
            'NombreRazonEmisor' => $issuerName,
103
            'TipoFactura' => $invoice->type->value ?? (string)$invoice->type,
104
            'DescripcionOperacion' => 'Invoice issued',
105
            'Destinatarios' => [
106
                'IDDestinatario' => $destinatarios,
107
            ],
108
            'Desglose' => $desgloses,
109
            'CuotaTotal' => (string)$invoice->tax,
110
            'ImporteTotal' => (string)$invoice->total,
111
            'Encadenamiento' => [
112
                'PrimerRegistro' => 'S',
113
            ],
114
            'SistemaInformatico' => [
115
                'NombreRazon' => $issuerName,
116
                'NIF' => $issuerVat,
117
                'NombreSistemaInformatico' => 'LaravelVerifactu',
118
                'IdSistemaInformatico' => '01',
119
                'Version' => '1.0',
120
                'NumeroInstalacion' => '001',
121
                'TipoUsoPosibleSoloVerifactu' => 'S',
122
                'TipoUsoPosibleMultiOT' => 'N',
123
                'IndicadorMultiplesOT' => 'N',
124
            ],
125
            'FechaHoraHusoGenRegistro' => now()->format('c'),
126
            'TipoHuella' => '01',
127
            'Huella' => $hashResult['hash'],
128
        ];
129
130
        $body = [
131
            'Cabecera' => $cabecera,
132
            'RegistroFactura' => [
133
                [ 'RegistroAlta' => $registroAlta ]
134
            ],
135
        ];
136
137
        // 7. Configurar SoapClient y enviar
138
        $wsdl = $this->production
139
            ? 'https://www1.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP?wsdl'
140
            : 'https://prewww2.aeat.es/static_files/common/internet/dep/aplicaciones/es/aeat/tikeV1.0/cont/ws/SistemaFacturacion.wsdl';
141
        $location = $this->production
142
            ? 'https://www1.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP'
143
            : 'https://prewww1.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP';
144
        $options = [
145
            'local_cert' => $this->certPath,
146
            'passphrase' => $this->certPassword,
147
            'trace' => true,
148
            'exceptions' => true,
149
            'cache_wsdl' => 0,
150
            'soap_version' => SOAP_1_1,
151
            'stream_context' => stream_context_create([
152
                'ssl' => [
153
                    'verify_peer' => true,
154
                    'verify_peer_name' => true,
155
                    'allow_self_signed' => false,
156
                    'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT,
157
                ],
158
            ]),
159
        ];
160
        try {
161
            $client = new \SoapClient($wsdl, $options);
162
            $client->__setLocation($location);
163
            $response = $client->__soapCall('RegFactuSistemaFacturacion', [$body]);
164
            return [
165
                'status' => 'success',
166
                'request' => $client->__getLastRequest(),
167
                'response' => $client->__getLastResponse(),
168
                'aeat_response' => $response,
169
            ];
170
        } catch (\SoapFault $e) {
171
            return [
172
                'status' => 'error',
173
                'message' => $e->getMessage(),
174
                'request' => isset($client) ? $client->__getLastRequest() : null,
175
                'response' => isset($client) ? $client->__getLastResponse() : null,
176
            ];
177
        }
178
    }
179
180
    // Métodos adicionales para anulación, consulta, etc. pueden añadirse aquí
181
}
182