package services import ( "encoding/json" "fmt" "log" "attune-heart-therapy/internal/config" "github.com/stripe/stripe-go/v76" "github.com/stripe/stripe-go/v76/paymentintent" "github.com/stripe/stripe-go/v76/webhook" ) // paymentService implements the PaymentService interface type paymentService struct { config *config.Config } // NewPaymentService creates a new instance of PaymentService func NewPaymentService(cfg *config.Config) PaymentService { // Set Stripe API key stripe.Key = cfg.Stripe.SecretKey return &paymentService{ config: cfg, } } // CreatePaymentIntent creates a new Stripe payment intent for payment initialization func (s *paymentService) CreatePaymentIntent(amount float64, currency string) (*stripe.PaymentIntent, error) { if amount <= 0 { return nil, fmt.Errorf("amount must be greater than 0") } if currency == "" { currency = "usd" // Default to USD } // Convert amount to cents (Stripe expects amounts in smallest currency unit) amountCents := int64(amount * 100) params := &stripe.PaymentIntentParams{ Amount: stripe.Int64(amountCents), Currency: stripe.String(currency), AutomaticPaymentMethods: &stripe.PaymentIntentAutomaticPaymentMethodsParams{ Enabled: stripe.Bool(true), }, } pi, err := paymentintent.New(params) if err != nil { log.Printf("Failed to create payment intent: %v", err) return nil, fmt.Errorf("failed to create payment intent: %w", err) } return pi, nil } // ConfirmPayment confirms a payment intent for payment completion func (s *paymentService) ConfirmPayment(paymentIntentID string) (*stripe.PaymentIntent, error) { if paymentIntentID == "" { return nil, fmt.Errorf("payment intent ID is required") } params := &stripe.PaymentIntentConfirmParams{} pi, err := paymentintent.Confirm(paymentIntentID, params) if err != nil { log.Printf("Failed to confirm payment intent %s: %v", paymentIntentID, err) return nil, fmt.Errorf("failed to confirm payment: %w", err) } return pi, nil } // HandleWebhook processes Stripe webhook events for webhook processing func (s *paymentService) HandleWebhook(payload []byte, signature string) error { if len(payload) == 0 { return fmt.Errorf("webhook payload is empty") } if signature == "" { return fmt.Errorf("webhook signature is required") } // Verify webhook signature event, err := webhook.ConstructEvent(payload, signature, s.config.Stripe.WebhookSecret) if err != nil { log.Printf("Failed to verify webhook signature: %v", err) return fmt.Errorf("failed to verify webhook signature: %w", err) } // Handle different event types switch event.Type { case "payment_intent.succeeded": var paymentIntent stripe.PaymentIntent objectBytes, err := json.Marshal(event.Data.Object) if err != nil { log.Printf("Failed to marshal event data: %v", err) return fmt.Errorf("failed to parse event data: %w", err) } if err := json.Unmarshal(objectBytes, &paymentIntent); err != nil { log.Printf("Failed to unmarshal payment intent: %v", err) return fmt.Errorf("failed to parse payment intent: %w", err) } log.Printf("Payment succeeded for payment intent: %s", paymentIntent.ID) // TODO: Update booking status to confirmed // This will be handled when booking service is integrated case "payment_intent.payment_failed": var paymentIntent stripe.PaymentIntent objectBytes, err := json.Marshal(event.Data.Object) if err != nil { log.Printf("Failed to marshal event data: %v", err) return fmt.Errorf("failed to parse event data: %w", err) } if err := json.Unmarshal(objectBytes, &paymentIntent); err != nil { log.Printf("Failed to unmarshal payment intent: %v", err) return fmt.Errorf("failed to parse payment intent: %w", err) } log.Printf("Payment failed for payment intent: %s", paymentIntent.ID) // TODO: Update booking status to failed // This will be handled when booking service is integrated case "payment_intent.canceled": var paymentIntent stripe.PaymentIntent objectBytes, err := json.Marshal(event.Data.Object) if err != nil { log.Printf("Failed to marshal event data: %v", err) return fmt.Errorf("failed to parse event data: %w", err) } if err := json.Unmarshal(objectBytes, &paymentIntent); err != nil { log.Printf("Failed to unmarshal payment intent: %v", err) return fmt.Errorf("failed to parse payment intent: %w", err) } log.Printf("Payment canceled for payment intent: %s", paymentIntent.ID) // TODO: Update booking status to canceled // This will be handled when booking service is integrated default: log.Printf("Unhandled webhook event type: %s", event.Type) } return nil }