package yookassa import ( "bytes" "context" "encoding/base64" "encoding/json" "fmt" "net/http" "time" "github.com/google/uuid" "github.com/shopspring/decimal" ) type Client struct { shopID string secretKey string baseURL string httpClient *http.Client } func NewClient(shopID, secretKey string) *Client { return &Client{ shopID: shopID, secretKey: secretKey, baseURL: "https://api.yookassa.ru/v3", httpClient: &http.Client{Timeout: 15 * time.Second}, } } func (c *Client) CreatePayment(ctx context.Context, amount decimal.Decimal, description string, orderID uuid.UUID, returnURL string) (*PaymentResponse, error) { reqBody := PaymentRequest{ Amount: Amount{ Value: amount.StringFixed(2), Currency: "RUB", }, Capture: true, // Автоматическое списание Confirmation: Confirmation{ Type: "redirect", ReturnURL: returnURL, }, Metadata: map[string]string{ "order_id": orderID.String(), }, Description: description, } body, _ := json.Marshal(reqBody) req, err := http.NewRequestWithContext(ctx, "POST", c.baseURL+"/payments", bytes.NewReader(body)) if err != nil { return nil, err } // Basic Auth auth := base64.StdEncoding.EncodeToString([]byte(c.shopID + ":" + c.secretKey)) req.Header.Set("Authorization", "Basic "+auth) req.Header.Set("Content-Type", "application/json") // Идемпотентность: используем наш order_id как ключ req.Header.Set("Idempotence-Key", orderID.String()) resp, err := c.httpClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("yookassa error: status %d", resp.StatusCode) } var result PaymentResponse if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return nil, err } return &result, nil }