package facturacionOperaciones import ( "api-soap-facturacion/internal/config" "api-soap-facturacion/internal/logger" mfo "api-soap-facturacion/internal/models/facturacionOperaciones" "api-soap-facturacion/pkg/soap" "bytes" "context" "encoding/xml" "fmt" "io" "net/http" "time" ) // Constantes de configuración const ( DefaultTimeout = 30 * time.Second ContentType = "text/xml;charset=UTF-8" SoapAction = "https://siat.impuestos.gob.bo/registroPuntoVenta" SiatEndpoint = "https://pilotosiatservicios.impuestos.gob.bo/v2/FacturacionOperaciones" ) // NewClient crea una nueva instancia del cliente SOAP func NewClient(options *ClientOptions, config config.APIConfig, log logger.Logger) *Client { // Configurar cliente SOAP factOperaciones := soap.ClientOptions{ Endpoint: config.Endpoint, Username: config.Username, Password: config.Password, Timeout: config.Timeout * time.Second, Headers: map[string]string{ "User-Agent": "soap-api-client/1.0", }, } client := &Client{ endpointURL: SiatEndpoint, soapClient: soap.NewClient(factOperaciones), logger: log, } if options != nil { if options.EndpointURL != "" { client.endpointURL = options.EndpointURL } if options.HTTPClient != nil { client.httpClient = options.HTTPClient } } if client.httpClient == nil { timeout := DefaultTimeout if options != nil && options.Timeout > 0 { timeout = options.Timeout } client.httpClient = &http.Client{ Timeout: timeout, } } return client } // envía una solicitud para registrar un punto de venta en SIAT func (c *Client) RegistrarPuntoVenta(ctx context.Context, solicitud mfo.SolicitudRegistroPuntoVenta) (*mfo.RespuestaRegistroPuntoVenta, error) { // Preparar la petición SOAP request := mfo.RegistroPuntoVentaRequest{ SoapEnv: "http://schemas.xmlsoap.org/soap/envelope/", Siat: "https://siat.impuestos.gob.bo/", } request.Body.RegistroPuntoVenta.Solicitud = solicitud // Convertir la petición a XML requestBody, err := xml.MarshalIndent(request, "", " ") if err != nil { return nil, fmt.Errorf("error al convertir la solicitud a XML: %w", err) } // Crear la petición HTTP con el contexto req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.endpointURL, bytes.NewReader(requestBody)) if err != nil { return nil, fmt.Errorf("error al crear la petición HTTP: %w", err) } // Configurar encabezados req.Header.Set("Content-Type", ContentType) req.Header.Set("SOAPAction", SoapAction) // Realizar la petición HTTP resp, err := c.httpClient.Do(req) if err != nil { return nil, fmt.Errorf("error al realizar la petición HTTP: %w", err) } defer resp.Body.Close() // Leer el cuerpo de la respuesta respBody, err := io.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("error al leer la respuesta: %w", err) } // Verificar el código de estado HTTP if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("error en la respuesta HTTP: %s, cuerpo: %s", resp.Status, respBody) } // Parsear la respuesta var soapResponse mfo.RegistroPuntoVentaResponse if err := xml.Unmarshal(respBody, &soapResponse); err != nil { return nil, fmt.Errorf("error al parsear la respuesta XML: %w, cuerpo: %s", err, respBody) } return &soapResponse.Body.RegistroPuntoVentaResponse.Respuesta, nil }