1414use LibreCodeCoop \NfsePHP \Contracts \XmlSignerInterface ;
1515use LibreCodeCoop \NfsePHP \Dto \DpsData ;
1616use LibreCodeCoop \NfsePHP \Dto \ReceiptData ;
17+ use LibreCodeCoop \NfsePHP \Exception \ArtifactException ;
1718use LibreCodeCoop \NfsePHP \Exception \CancellationException ;
1819use LibreCodeCoop \NfsePHP \Exception \IssuanceException ;
1920use LibreCodeCoop \NfsePHP \Exception \NetworkException ;
@@ -80,7 +81,7 @@ public function query(string $chaveAcesso): ReceiptData
8081
8182 public function cancel (string $ chaveAcesso , string $ motivo ): bool
8283 {
83- $ eventoXml = $ this ->buildCancelEventXml ($ chaveAcesso , $ motivo );
84+ $ eventoXml = $ this ->buildCancelEventXml ($ chaveAcesso , $ motivo );
8485 $ signedEventoXml = $ this ->signer ->sign ($ eventoXml , $ this ->cert ->cnpj );
8586
8687 $ compressedEventoXml = gzencode ($ signedEventoXml );
@@ -106,6 +107,34 @@ public function cancel(string $chaveAcesso, string $motivo): bool
106107 return true ;
107108 }
108109
110+ #[\Override]
111+ public function getDanfse (string $ chaveAcesso ): string
112+ {
113+ $ url = $ this ->environment ->danfseBaseUrl . '/ ' . $ chaveAcesso ;
114+
115+ [$ httpStatus , $ body ] = $ this ->getRawBytes ($ url );
116+
117+ if ($ httpStatus >= 400 ) {
118+ throw new ArtifactException (
119+ 'ADN gateway returned error for DANFSE retrieval (HTTP ' . $ httpStatus . ') ' ,
120+ NfseErrorCode::ArtifactRetrievalFailed,
121+ $ httpStatus ,
122+ [],
123+ );
124+ }
125+
126+ if ($ body === '' ) {
127+ throw new ArtifactException (
128+ 'ADN gateway returned empty body for DANFSE retrieval ' ,
129+ NfseErrorCode::ArtifactRetrievalFailed,
130+ $ httpStatus ,
131+ [],
132+ );
133+ }
134+
135+ return $ body ;
136+ }
137+
109138 // -------------------------------------------------------------------------
110139 // Internal HTTP helpers
111140 // -------------------------------------------------------------------------
@@ -211,13 +240,13 @@ private function buildCancelEventXml(string $chaveAcesso, string $motivo): strin
211240 private function sslContextOptions (): array
212241 {
213242 $ options = [
214- 'verify_peer ' => true ,
243+ 'verify_peer ' => true ,
215244 'verify_peer_name ' => true ,
216245 ];
217246
218247 if ($ this ->cert ->transportCertificatePath !== null && $ this ->cert ->transportPrivateKeyPath !== null ) {
219248 $ options ['local_cert ' ] = $ this ->cert ->transportCertificatePath ;
220- $ options ['local_pk ' ] = $ this ->cert ->transportPrivateKeyPath ;
249+ $ options ['local_pk ' ] = $ this ->cert ->transportPrivateKeyPath ;
221250 }
222251
223252 return $ options ;
@@ -257,6 +286,35 @@ private function fetchAndDecode(string $path, mixed $context): array
257286 return [$ httpStatus , $ decoded ];
258287 }
259288
289+ /**
290+ * Fetch a URL and return raw response bytes without JSON decoding.
291+ *
292+ * Used for binary endpoints such as ADN DANFSE (PDF artifact retrieval).
293+ * No mTLS is applied — DANFSE is accessible without client certificate.
294+ *
295+ * @return array{int, string}
296+ */
297+ private function getRawBytes (string $ url ): array
298+ {
299+ $ context = stream_context_create ([
300+ 'http ' => [
301+ 'method ' => 'GET ' ,
302+ 'header ' => "Accept: application/pdf \r\n" ,
303+ 'ignore_errors ' => true ,
304+ ],
305+ ]);
306+
307+ $ http_response_header = [];
308+ $ body = file_get_contents ($ url , false , $ context );
309+ $ httpStatus = $ this ->parseHttpStatus ($ http_response_header );
310+
311+ if ($ body === false ) {
312+ throw new NetworkException ('Failed to connect to ADN DANFSE gateway at ' . $ url );
313+ }
314+
315+ return [$ httpStatus , $ body ];
316+ }
317+
260318 /**
261319 * Extract the HTTP status code from the first response header line.
262320 *
0 commit comments