-
-
Notifications
You must be signed in to change notification settings - Fork 2
/
ciscomm.go
128 lines (112 loc) · 3.8 KB
/
ciscomm.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package fiskalhrgo
// SPDX-License-Identifier: MIT
// Copyright (c) 2024 L. D. T. d.o.o.
// Copyright (c) contributors for their respective contributions. See https://github.com/l-d-t/fiskalhrgo/graphs/contributors
import (
"bytes"
"crypto/tls"
"encoding/xml"
"errors"
"fmt"
"io"
"net/http"
"time"
)
// iSOAPEnvelope represents a SOAP envelope
type iSOAPEnvelope struct {
XMLName xml.Name `xml:"soapenv:Envelope"`
XmlnsT string `xml:"xmlns:tns,attr"` // Declare the tns namespace
Xmlns string `xml:"xmlns:soapenv,attr"`
Body iSOAPBody `xml:"soapenv:Body"`
}
// iSOAPBody represents the body of a SOAP envelope
type iSOAPBody struct {
XMLName xml.Name `xml:"soapenv:Body"`
Content []byte `xml:",innerxml"`
}
// iSOAPEnvelopeNoNamespace represents a SOAP envelope without namespace (for CIS responses)
// This to be more flexible and permissive on unmarhaling responses.
type iSOAPEnvelopeNoNamespace struct {
XMLName xml.Name `xml:"Envelope"`
Body iSOAPBodyNoNamespace `xml:"Body"`
}
// iSOAPBodyNoNamespace represents the body of a SOAP envelope without namespace (for CIS responses)
type iSOAPBodyNoNamespace struct {
XMLName xml.Name `xml:"Body"`
Content []byte `xml:",innerxml"`
}
// GetResponse wraps the XML payload in a SOAP envelope, makes an HTTPS request, and returns the extracted response body.
// - Input: XML payload
// - Output: Response body, error, HTTP status code
func (fe *FiskalEntity) GetResponse(xmlPayload []byte, sign bool) ([]byte, int, error) {
if fe.ciscert == nil || fe.ciscert.SSLverifyPoll == nil {
return nil, 0, errors.New("CIScert or SSLverifyPoll is not initialized")
}
// Create a custom TLS configuration using TLS 1.3 and the CA pool
tlsConfig := &tls.Config{
MinVersion: tls.VersionTLS13,
RootCAs: fe.ciscert.SSLverifyPoll,
}
// Create a custom HTTP client with the custom TLS configuration
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: tlsConfig,
},
Timeout: cistimeout * time.Second, // Set a timeout for the request
}
if sign {
// Sign the XML payload
signedXML, err := fe.signXML(xmlPayload)
if err != nil {
return nil, 0, fmt.Errorf("failed to sign XML: %w", err)
}
xmlPayload = signedXML
}
// Prepare the SOAP envelope with the payload
soapEnvelope := iSOAPEnvelope{
XmlnsT: DefaultNamespace,
Xmlns: "http://schemas.xmlsoap.org/soap/envelope/",
Body: iSOAPBody{Content: xmlPayload},
}
// Marshal the SOAP envelope to XML
marshaledEnvelope, err := xml.Marshal(soapEnvelope)
if err != nil {
return nil, 0, fmt.Errorf("failed to marshal SOAP envelope: %w", err)
}
// Create a new HTTP POST request
req, err := http.NewRequest("POST", fe.url, bytes.NewBuffer([]byte(marshaledEnvelope)))
if err != nil {
return nil, 0, fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("Content-Type", "text/xml")
// Send the request
resp, err := client.Do(req)
if err != nil {
return nil, 0, fmt.Errorf("failed to make request: %w", err)
}
defer resp.Body.Close()
// Read the response body
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, resp.StatusCode, fmt.Errorf("failed to read response: %w", err)
}
if sign {
// Verify the signature
_, err := fe.verifyXML(body)
if err != nil {
return body, resp.StatusCode, fmt.Errorf("failed to verify CIS signature: %w", err)
}
}
// Parse the SOAP response
var soapResp iSOAPEnvelopeNoNamespace
err = xml.Unmarshal(body, &soapResp)
if err != nil {
return body, resp.StatusCode, fmt.Errorf("failed to unmarshal SOAP response: %w", err)
}
// Return the inner content of the SOAP Body (the actual response)
if resp.StatusCode == http.StatusOK {
return soapResp.Body.Content, resp.StatusCode, nil
} else {
return soapResp.Body.Content, resp.StatusCode, fmt.Errorf("CIS returned an error: %v", resp.Status)
}
}