verifactu: add client and XML structures for AEAT communication
This commit is contained in:
parent
fa59c984bc
commit
fbd5e72774
|
|
@ -0,0 +1,87 @@
|
||||||
|
package verifactu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Environment string
|
||||||
|
CertPath string
|
||||||
|
CertPass string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
cfg Config
|
||||||
|
httpClient *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient(cfg Config) *Client {
|
||||||
|
tr := &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||||
|
}
|
||||||
|
return &Client{
|
||||||
|
cfg: cfg,
|
||||||
|
httpClient: &http.Client{Transport: tr},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) GetEndpoint() string {
|
||||||
|
if c.cfg.Environment == "production" {
|
||||||
|
return "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tikeV1.0/cont/ws/SistemaFacturacion.wsdl"
|
||||||
|
}
|
||||||
|
return "https://prewww2.aeat.es/static_files/common/internet/dep/aplicaciones/es/aeat/tikeV1.0/cont/ws/SistemaFacturacion.wsdl"
|
||||||
|
}
|
||||||
|
|
||||||
|
type XMLRequest struct {
|
||||||
|
XMLName xml.Name `xml:"soapenv:Envelope"`
|
||||||
|
Xmlns string `xml:"xmlns:soapenv,attr"`
|
||||||
|
XmlnsXsi string `xml:"xmlns:xsi,attr"`
|
||||||
|
XmlnsSum string `xml:"xmlns:sum,attr"`
|
||||||
|
XmlnsSum1 string `xml:"xmlns:sum1,attr"`
|
||||||
|
XmlnsXd string `xml:"xmlns:xd,attr"`
|
||||||
|
Header interface{} `xml:"soapenv:Header"`
|
||||||
|
Body XMLBody `xml:"soapenv:Body"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type XMLBody struct {
|
||||||
|
Content interface{} `xml:",any"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Send(xmlPayload interface{}) ([]byte, error) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
enc := xml.NewEncoder(&buf)
|
||||||
|
err := enc.Encode(xmlPayload)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to encode XML: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest("POST", c.GetEndpoint(), &buf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create request: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Content-Type", "text/xml; charset=utf-8")
|
||||||
|
req.Header.Set("SOAPAction", "")
|
||||||
|
|
||||||
|
resp, err := c.httpClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("request failed: %w", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read response: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode >= 400 {
|
||||||
|
return nil, fmt.Errorf("HTTP error %d: %s", resp.StatusCode, string(body))
|
||||||
|
}
|
||||||
|
|
||||||
|
return body, nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,138 @@
|
||||||
|
package verifactu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AltaRequest struct {
|
||||||
|
Cabecera Cabecera `xml:"sum:RegFactuSistemaFacturacion>sum:Cabecera"`
|
||||||
|
RegistroAlta RegistroAlta `xml:"sum:RegFactuSistemaFacturacion>sum:RegistroFactura>sum1:RegistroAlta"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Cabecera struct {
|
||||||
|
ObligadoEmision ObligadoEmision `xml:"sum1:ObligadoEmision"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ObligadoEmision struct {
|
||||||
|
NombreRazon string `xml:"sum1:NombreRazon"`
|
||||||
|
NIF string `xml:"sum1:NIF"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RegistroAlta struct {
|
||||||
|
IDVersion string `xml:"sum1:IDVersion"`
|
||||||
|
IDFactura IDFactura `xml:"sum1:IDFactura"`
|
||||||
|
NombreRazonEmisor string `xml:"sum1:NombreRazonEmisor"`
|
||||||
|
TipoFactura string `xml:"sum1:TipoFactura"`
|
||||||
|
DescripcionOperacion string `xml:"sum1:DescripcionOperacion"`
|
||||||
|
Destinatarios *Destinatarios `xml:"sum1:Desglose>sum1:DetalleDesglose"`
|
||||||
|
Desglose Desglose `xml:"sum1:Desglose"`
|
||||||
|
CuotaTotal string `xml:"sum1:CuotaTotal"`
|
||||||
|
ImporteTotal string `xml:"sum1:ImporteTotal"`
|
||||||
|
Encadenamiento Encadenamiento `xml:"sum1:Encadenamiento"`
|
||||||
|
SistemaInformatico SistemaInformatico `xml:"sum1:SistemaInformatico"`
|
||||||
|
FechaHoraHusoGenRegistro string `xml:"sum1:FechaHoraHusoGenRegistro"`
|
||||||
|
TipoHuella string `xml:"sum1:TipoHuella"`
|
||||||
|
Huella string `xml:"sum1:Huella"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type IDFactura struct {
|
||||||
|
IDEmisorFactura string `xml:"sum1:IDEmisorFactura"`
|
||||||
|
NumSerieFactura string `xml:"sum1:NumSerieFactura"`
|
||||||
|
FechaExpedicionFactura string `xml:"sum1:FechaExpedicionFactura"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Destinatarios struct {
|
||||||
|
IDDestinatario []IDDestinatario `xml:"sum1:IDDestinatario"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type IDDestinatario struct {
|
||||||
|
NombreRazon string `xml:"sum1:NombreRazon"`
|
||||||
|
NIF string `xml:"sum1:NIF"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Desglose struct {
|
||||||
|
DetalleDesglose []DetalleDesglose `xml:"sum1:DetalleDesglose"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DetalleDesglose struct {
|
||||||
|
ClaveRegimen string `xml:"sum1:ClaveRegimen"`
|
||||||
|
CalificacionOperacion string `xml:"sum1:CalificacionOperacion"`
|
||||||
|
TipoImpositivo string `xml:"sum1:TipoImpositivo"`
|
||||||
|
BaseImponibleOimporteNoSujeto string `xml:"sum1:BaseImponibleOimporteNoSujeto"`
|
||||||
|
CuotaRepercutida string `xml:"sum1:CuotaRepercutida"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Encadenamiento struct {
|
||||||
|
PrimerRegistro string `xml:"sum1:PrimerRegistro,omitempty"`
|
||||||
|
RegistroAnterior *RegistroAnterior `xml:"sum1:RegistroAnterior,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RegistroAnterior struct {
|
||||||
|
IDEmisorFactura string `xml:"sum1:IDEmisorFactura"`
|
||||||
|
NumSerieFactura string `xml:"sum1:NumSerieFactura"`
|
||||||
|
FechaExpedicionFactura string `xml:"sum1:FechaExpedicionFactura"`
|
||||||
|
Huella string `xml:"sum1:Huella"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SistemaInformatico struct {
|
||||||
|
NombreRazon string `xml:"sum1:NombreRazon"`
|
||||||
|
NIF string `xml:"sum1:NIF"`
|
||||||
|
NombreSistemaInformatico string `xml:"sum1:NombreSistemaInformatico"`
|
||||||
|
IdSistemaInformatico string `xml:"sum1:IdSistemaInformatico"`
|
||||||
|
Version string `xml:"sum1:Version"`
|
||||||
|
NumeroInstalacion string `xml:"sum1:NumeroInstalacion"`
|
||||||
|
TipoUsoPosibleSoloVerifactu string `xml:"sum1:TipoUsoPosibleSoloVerifactu"`
|
||||||
|
TipoUsoPosibleMultiOT string `xml:"sum1:TipoUsoPosibleMultiOT"`
|
||||||
|
IndicadorMultiplesOT string `xml:"sum1:IndicadorMultiplesOT"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AnulacionRequest struct {
|
||||||
|
Cabecera Cabecera `xml:"sum:RegFactuSistemaFacturacion>sum:Cabecera"`
|
||||||
|
RegistroAnulacion RegistroAnulacion `xml:"sum:RegFactuSistemaFacturacion>sum:RegistroFactura>sum1:RegistroAnulacion"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RegistroAnulacion struct {
|
||||||
|
IDVersion string `xml:"sum1:IDVersion"`
|
||||||
|
IDFacturaAnulada IDFacturaAnulada `xml:"sum1:IDFacturaAnulada"`
|
||||||
|
Encadenamiento Encadenamiento `xml:"sum1:Encadenamiento"`
|
||||||
|
SistemaInformatico SistemaInformatico `xml:"sum1:SistemaInformatico"`
|
||||||
|
FechaHoraHusoGenRegistro string `xml:"sum1:FechaHoraHusoGenRegistro"`
|
||||||
|
TipoHuella string `xml:"sum1:TipoHuella"`
|
||||||
|
Huella string `xml:"sum1:Huella"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type IDFacturaAnulada struct {
|
||||||
|
IDEmisorFacturaAnulada string `xml:"sum1:IDEmisorFacturaAnulada"`
|
||||||
|
NumSerieFacturaAnulada string `xml:"sum1:NumSerieFacturaAnulada"`
|
||||||
|
FechaExpedicionFacturaAnulada string `xml:"sum1:FechaExpedicionFacturaAnulada"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
XMLName xml.Name `xml:"Envelope"`
|
||||||
|
Body ResponseBody
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResponseBody struct {
|
||||||
|
XMLName xml.Name `xml:"Body"`
|
||||||
|
Fault *Fault `xml:"Fault,omitempty"`
|
||||||
|
RegistroRespuesta *RegistroRespuesta `xml:"RegistroRespuesta,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Fault struct {
|
||||||
|
FaultCode string `xml:"faultcode"`
|
||||||
|
FaultString string `xml:"faultstring"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RegistroRespuesta struct {
|
||||||
|
CSV string `xml:"CSV"`
|
||||||
|
Estado string `xml:"Estado"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func BuildAltaRequest(emisorNombre string, data interface{}) (*AltaRequest, error) {
|
||||||
|
return nil, fmt.Errorf("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func BuildAnulacionRequest(emisorNombre string, data interface{}) (*AnulacionRequest, error) {
|
||||||
|
return nil, fmt.Errorf("not implemented")
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue