Files
MyClub/internal/services/packeta_service.go
T
Tomas Dvorak dfc079288f hot fix #1
2026-01-26 08:13:18 +01:00

215 lines
5.0 KiB
Go

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
}