package receiptService import ( "FamilyHub/src/domain" "FamilyHub/src/repositories" "FamilyHub/src/utils" "bytes" "context" "crypto/tls" "encoding/json" "errors" "fmt" "log" "mime/multipart" "net/http" "time" ) type ReceiptService struct { client *http.Client repo repositories.ReceiptsRepository } func NewReceiptService(repo repositories.ReceiptsRepository) *ReceiptService { return &ReceiptService{ client: &http.Client{ Timeout: 60 * time.Second, Transport: &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, }, }, }, repo: repo, } } func (s *ReceiptService) GetReceipt( ctx context.Context, date string, number string, ) (*domain.Receipt, error) { url := "https://ch.info-center.by/ajax/check1.php" var receipt domain.Receipt body, contentType := buildMultipartBody(date, number) req, err := http.NewRequestWithContext( ctx, http.MethodPost, url, body, ) if err != nil { log.Println(err.Error()) return nil, err } req.Header.Set("Content-Type", contentType) resp, err := s.client.Do(req) if err != nil { log.Println(err.Error()) return nil, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { log.Printf("external service returned %d\n", resp.StatusCode) return nil, fmt.Errorf("external service returned %d", resp.StatusCode) } var raw struct { Message map[string]interface{} `json:"message"` } if err := json.NewDecoder(resp.Body).Decode(&raw); err != nil { log.Printf("external service returned %s\n", err.Error()) return nil, err } bytes_, _ := json.Marshal(raw.Message) if err := json.Unmarshal(bytes_, &receipt); err != nil { return nil, err } if receipt.IssuedAtRaw == "" { return nil, errors.New("receipt not found") } positions, err := parsePositions(receipt.PositionsRaw) if err != nil { log.Printf("failed to parse positions: %s", err.Error()) return nil, err } receipt.IssuedAt, err = utils.ParseIssuedAt(receipt.IssuedAtRaw) if err != nil { log.Printf("failed to parse issued at: %s", err.Error()) return nil, err } receipt.Positions = positions for i := range receipt.Positions { p := &receipt.Positions[i] p.ProductCount, err = utils.ParseFloat(p.ProductCountRaw) if err != nil { log.Printf("failed to parse product count: %s", err.Error()) return nil, err } p.Amount, err = utils.ParseFloat(p.AmountRaw) if err != nil { log.Printf("failed to parse amount: %s", err.Error()) return nil, err } p.Discount, _ = utils.ParseFloat(p.DiscountRaw) p.Surcharge, _ = utils.ParseFloat(p.SurchargeRaw) } if _, err := s.repo.Create(ctx, &receipt); err != nil { return nil, err } return &receipt, nil } func buildMultipartBody(date, number string) (*bytes.Buffer, string) { body := &bytes.Buffer{} writer := multipart.NewWriter(body) _ = writer.WriteField("orig_date", date) _ = writer.WriteField("orig_ui", number) _ = writer.Close() return body, writer.FormDataContentType() } func parsePositions(raw string) ([]domain.Position, error) { var positions []domain.Position if raw == "" { return positions, nil } if err := json.Unmarshal([]byte(raw), &positions); err != nil { return nil, err } return positions, nil }