You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
111 lines
3.3 KiB
Go
111 lines
3.3 KiB
Go
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
|
|
}
|