package telegram import ( "bytes" "encoding/json" "fmt" "io" "net/http" "time" ) // httpClient is a shared client with sensible timeouts for Telegram Bot API calls. var httpClient = &http.Client{ Timeout: 10 * time.Second, } // apiResponse is the generic envelope returned by every Bot API method. type apiResponse struct { OK bool `json:"ok"` Result json.RawMessage `json:"result,omitempty"` Description string `json:"description,omitempty"` ErrorCode int `json:"error_code,omitempty"` } // CallBotAPI sends a JSON request to the Telegram Bot API and returns the result field. func CallBotAPI(botToken, method string, payload any) (json.RawMessage, error) { url := fmt.Sprintf("https://api.telegram.org/bot%s/%s", botToken, method) body, err := json.Marshal(payload) if err != nil { return nil, fmt.Errorf("telegram: marshal payload: %w", err) } resp, err := httpClient.Post(url, "application/json", bytes.NewReader(body)) if err != nil { return nil, fmt.Errorf("telegram: POST %s: %w", method, err) } defer resp.Body.Close() respBody, err := io.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("telegram: read response: %w", err) } var apiResp apiResponse if err := json.Unmarshal(respBody, &apiResp); err != nil { return nil, fmt.Errorf("telegram: unmarshal response: %w", err) } if !apiResp.OK { return nil, fmt.Errorf("telegram: %s failed (%d): %s", method, apiResp.ErrorCode, apiResp.Description) } return apiResp.Result, nil } // LabeledAmount represents one price component in a Telegram invoice. type LabeledAmount struct { Label string `json:"label"` Amount int `json:"amount"` // smallest currency unit (kopecks for RUB) } // InvoiceLinkParams holds the parameters for createInvoiceLink. type InvoiceLinkParams struct { Title string `json:"title"` Description string `json:"description"` Payload string `json:"payload"` ProviderToken string `json:"provider_token"` Currency string `json:"currency"` Prices []LabeledAmount `json:"prices"` } // CreateInvoiceLink calls the Telegram Bot API createInvoiceLink method // and returns the HTTPS invoice URL the client can pass to openInvoice(). func CreateInvoiceLink(botToken string, params InvoiceLinkParams) (string, error) { raw, err := CallBotAPI(botToken, "createInvoiceLink", params) if err != nil { return "", err } var link string if err := json.Unmarshal(raw, &link); err != nil { return "", fmt.Errorf("telegram: unmarshal invoice link: %w", err) } return link, nil } // AnswerPreCheckoutQuery responds to a Telegram pre_checkout_query. // ok=true approves; ok=false + errorMsg declines. func AnswerPreCheckoutQuery(botToken, queryID string, ok bool, errorMsg string) error { payload := map[string]any{ "pre_checkout_query_id": queryID, "ok": ok, } if !ok && errorMsg != "" { payload["error_message"] = errorMsg } _, err := CallBotAPI(botToken, "answerPreCheckoutQuery", payload) return err } // SetWebhook configures the Telegram webhook URL for payment callbacks. func SetWebhook(botToken, webhookURL string) error { payload := map[string]string{ "url": webhookURL, } _, err := CallBotAPI(botToken, "setWebhook", payload) return err }