package services import ( "bytes" "encoding/xml" "fmt" "io" "net/http" "fotbal-club/internal/config" "fotbal-club/internal/models" ) type PacketaService struct { ApiPassword string ApiUrl string EshopName string } func NewPacketaService(cfg *config.Config) *PacketaService { // Defaults apiUrl := "https://www.zasilkovna.cz/api/rest" eshopName := "MyClubEshop" if cfg.PacketaEshopName != "" { eshopName = cfg.PacketaEshopName } return &PacketaService{ ApiPassword: cfg.PacketaAPIPassword, ApiUrl: apiUrl, EshopName: eshopName, } } // XML Structures type CreatePacketRequest struct { XMLName xml.Name `xml:"createPacket"` ApiPassword string `xml:"apiPassword"` PacketAttributes PacketAttributes `xml:"packetAttributes"` } type PacketAttributes struct { Number string `xml:"number"` Name string `xml:"name"` Surname string `xml:"surname"` Email string `xml:"email"` Phone string `xml:"phone"` AddressId string `xml:"addressId"` Value string `xml:"value"` Currency string `xml:"currency"` Weight string `xml:"weight"` Eshop string `xml:"eshop"` } type CreatePacketResponse struct { XMLName xml.Name `xml:"response"` Status string `xml:"status"` Result PacketResult `xml:"result"` Fault *PacketFault `xml:"fault"` } type PacketResult struct { PacketId string `xml:"packetId"` LabelUrl string `xml:"labelUrl"` // Not standard, usually requires separate call Barcode string `xml:"barcode"` } type PacketFault struct { String string `xml:"string"` Detail string `xml:"detail"` } // CreatePacket calls Packeta API to register the shipment func (s *PacketaService) CreatePacket(order *models.EshopOrder, addressId string) (string, error) { if s.ApiPassword == "" { return "", fmt.Errorf("Packeta API password not configured") } req := CreatePacketRequest{ ApiPassword: s.ApiPassword, PacketAttributes: PacketAttributes{ Number: order.OrderNumber, Name: order.FirstName, Surname: order.LastName, Email: order.Email, Phone: "", // Add phone to order model if needed AddressId: addressId, Value: fmt.Sprintf("%.2f", float64(order.TotalAmountCents)/100.0), Currency: order.Currency, Weight: "1.0", // Default weight, should come from product sum Eshop: s.EshopName, }, } xmlData, err := xml.Marshal(req) if err != nil { return "", err } resp, err := http.Post(s.ApiUrl, "text/xml", bytes.NewBuffer(xmlData)) if err != nil { return "", err } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) var xmlResp CreatePacketResponse if err := xml.Unmarshal(body, &xmlResp); err != nil { return "", fmt.Errorf("failed to parse response: %v", err) } if xmlResp.Status != "ok" { msg := "unknown error" if xmlResp.Fault != nil { msg = xmlResp.Fault.String + ": " + xmlResp.Fault.Detail } return "", fmt.Errorf("packeta error: %s", msg) } return xmlResp.Result.PacketId, nil } // Helper struct for packet status type PacketStatusRequest struct { XMLName xml.Name `xml:"packetStatus"` ApiPassword string `xml:"apiPassword"` PacketId string `xml:"packetId"` } type PacketStatusResponse struct { XMLName xml.Name `xml:"response"` Status string `xml:"status"` Result PacketStatusResult `xml:"result"` } type PacketStatusResult struct { StatusCode string `xml:"statusCode"` StatusText string `xml:"statusText"` } func (s *PacketaService) GetPacketLabel(packetId string) ([]byte, error) { if s.ApiPassword == "" { return nil, fmt.Errorf("Packeta API password not configured") } type PacketLabelPdfRequest struct { XMLName xml.Name `xml:"packetLabelPdf"` ApiPassword string `xml:"apiPassword"` PacketId string `xml:"packetId"` Format string `xml:"format"` Offset int `xml:"offset"` } req := PacketLabelPdfRequest{ ApiPassword: s.ApiPassword, PacketId: packetId, Format: "A6 on A4", Offset: 0, } xmlData, err := xml.Marshal(req) if err != nil { return nil, err } resp, err := http.Post(s.ApiUrl, "text/xml", bytes.NewBuffer(xmlData)) if err != nil { return nil, err } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return nil, err } if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("label request failed with status %d", resp.StatusCode) } return body, nil } func (s *PacketaService) GetPacketStatus(packetId string) (string, error) { req := PacketStatusRequest{ ApiPassword: s.ApiPassword, PacketId: packetId, } xmlData, err := xml.Marshal(req) if err != nil { return "", err } resp, err := http.Post(s.ApiUrl, "text/xml", bytes.NewBuffer(xmlData)) if err != nil { return "", err } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) var xmlResp PacketStatusResponse if err := xml.Unmarshal(body, &xmlResp); err != nil { return "", err } if xmlResp.Status != "ok" { return "", fmt.Errorf("status check failed") } return xmlResp.Result.StatusText, nil }