package eshop import ( "bytes" "encoding/json" "fmt" "io" "net/http" "strings" "fotbal-club/internal/config" "fotbal-club/internal/models" "fotbal-club/pkg/httpclient" ) // PaymentResult represents a generic result from a payment provider type PaymentResult struct { RedirectURL string ProviderPaymentID string RawPayloadJSON string } // StripeService handles Stripe payment integration type StripeService struct { cfg *config.Config client *http.Client } // NewStripeService creates a new Stripe service instance func NewStripeService(cfg *config.Config) *StripeService { return &StripeService{ cfg: cfg, client: httpclient.DefaultClient(), } } // CreatePayment creates a Stripe PaymentIntent for the given order func (s *StripeService) CreatePayment(order *models.EshopOrder) (*PaymentResult, error) { if !s.cfg.StripeEnabled { return nil, fmt.Errorf("Stripe payment provider is disabled") } secretKey := strings.TrimSpace(s.cfg.StripeSecretKey) if secretKey == "" { return nil, fmt.Errorf("Stripe secret key not configured") } // Create PaymentIntent request paymentIntentReq := map[string]interface{}{ "amount": order.TotalAmountCents, "currency": strings.ToLower(order.Currency), "metadata": map[string]string{ "order_id": fmt.Sprintf("%d", order.ID), "order_number": order.OrderNumber, }, "automatic_payment_methods": map[string]interface{}{ "enabled": true, }, } paymentJSON, err := json.Marshal(paymentIntentReq) if err != nil { return nil, fmt.Errorf("failed to marshal Stripe PaymentIntent request: %w", err) } // Create PaymentIntent via Stripe API req, err := http.NewRequest("POST", "https://api.stripe.com/v1/payment_intents", bytes.NewBuffer(paymentJSON)) if err != nil { return nil, fmt.Errorf("failed to create Stripe PaymentIntent request: %w", err) } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("Authorization", "Bearer "+secretKey) // Convert JSON to form-encoded for Stripe API formData := fmt.Sprintf("amount=%d¤cy=%s&automatic_payment_methods[enabled]=true", order.TotalAmountCents, strings.ToLower(order.Currency)) req.Body = io.NopCloser(strings.NewReader(formData)) resp, err := s.client.Do(req) if err != nil { return nil, fmt.Errorf("failed to execute Stripe PaymentIntent request: %w", err) } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("failed to read Stripe PaymentIntent response: %w", err) } if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("Stripe API error: status %d, response: %s", resp.StatusCode, string(body)) } var paymentIntent map[string]interface{} if err := json.Unmarshal(body, &paymentIntent); err != nil { return nil, fmt.Errorf("failed to parse Stripe PaymentIntent response: %w", err) } clientSecret, ok := paymentIntent["client_secret"].(string) if !ok || clientSecret == "" { return nil, fmt.Errorf("Stripe PaymentIntent response missing client_secret") } paymentID, ok := paymentIntent["id"].(string) if !ok || paymentID == "" { return nil, fmt.Errorf("Stripe PaymentIntent response missing id") } return &PaymentResult{ RedirectURL: "", // Stripe uses client_secret, not redirect ProviderPaymentID: paymentID, RawPayloadJSON: string(body), }, nil } // GetClientSecret returns the client secret for a PaymentIntent (for frontend use) func (s *StripeService) GetClientSecret(paymentIntentID string) (string, error) { if s.cfg.StripeSecretKey == "" { return "", fmt.Errorf("Stripe secret key not configured") } url := fmt.Sprintf("https://api.stripe.com/v1/payment_intents/%s", paymentIntentID) req, err := http.NewRequest("GET", url, nil) if err != nil { return "", fmt.Errorf("failed to create request: %w", err) } req.SetBasicAuth(s.cfg.StripeSecretKey, "") req.Header.Set("Stripe-Version", "2023-10-16") resp, err := s.client.Do(req) if err != nil { return "", fmt.Errorf("failed to make request to Stripe: %w", err) } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return "", fmt.Errorf("failed to read response: %w", err) } if resp.StatusCode != http.StatusOK { return "", fmt.Errorf("Stripe API error: %d - %s", resp.StatusCode, string(body)) } var result map[string]interface{} if err := json.Unmarshal(body, &result); err != nil { return "", fmt.Errorf("failed to parse response: %w", err) } clientSecret, ok := result["client_secret"].(string) if !ok { return "", fmt.Errorf("no client secret in response") } return clientSecret, nil }