package payment import ( "encoding/json" "fmt" "io" "log" "net/http" "os" "git.linuxforward.com/byom/byom-onboard/internal/platform/mailer" "github.com/gin-gonic/gin" "github.com/stripe/stripe-go/v81" "github.com/stripe/stripe-go/v81/account" portalsession "github.com/stripe/stripe-go/v81/billingportal/session" "github.com/stripe/stripe-go/v81/checkout/session" "github.com/stripe/stripe-go/v81/webhook" ) type StripeHandler struct { domain string endpointSecret string mailer *mailer.Mailer } func NewStripeHandler(apiKey, endpointSecret, domain string, mailer *mailer.Mailer) (*StripeHandler, error) { stripe.Key = apiKey // Test connection by retrieving account information _, err := account.Get() if err != nil { return nil, fmt.Errorf("failed to connect to Stripe: %w", err) } return &StripeHandler{ domain: domain, endpointSecret: endpointSecret, mailer: mailer, }, nil } func (h *StripeHandler) CreateCheckoutSession(c *gin.Context) { // Get email from request customerEmail := c.PostForm("email") if customerEmail == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "Email is required"}) return } checkoutParams := &stripe.CheckoutSessionParams{ Mode: stripe.String(string(stripe.CheckoutSessionModePayment)), LineItems: []*stripe.CheckoutSessionLineItemParams{ { PriceData: &stripe.CheckoutSessionLineItemPriceDataParams{ Currency: stripe.String("eur"), ProductData: &stripe.CheckoutSessionLineItemPriceDataProductDataParams{ Name: stripe.String("Byom Subscription"), }, UnitAmount: stripe.Int64(2000), // 20€ en centimes }, Quantity: stripe.Int64(1), }, }, SuccessURL: stripe.String("http://192.168.1.35:5173/payment/success?session_id={CHECKOUT_SESSION_ID}"), CancelURL: stripe.String("http://192.168.1.35:5173/payment/cancel"), CustomerEmail: stripe.String(customerEmail), CustomerCreation: stripe.String(string(stripe.CheckoutSessionCustomerCreationAlways)), } s, err := session.New(checkoutParams) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Renvoyer uniquement l'URL et l'ID de session c.JSON(http.StatusOK, gin.H{ "url": s.URL, "sessionId": s.ID, }) } func (h *StripeHandler) CreatePortalSession(c *gin.Context) { sessionID := c.PostForm("session_id") s, err := session.Get(sessionID, nil) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) log.Printf("session.Get: %v", err) return } params := &stripe.BillingPortalSessionParams{ Customer: stripe.String(s.Customer.ID), ReturnURL: stripe.String(h.domain), } ps, err := portalsession.New(params) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) log.Printf("ps.New: %v", err) return } c.Redirect(http.StatusSeeOther, ps.URL) } func (h *StripeHandler) HandleWebhook(c *gin.Context) { const MaxBodyBytes = int64(65536) bodyReader := http.MaxBytesReader(c.Writer, c.Request.Body, MaxBodyBytes) payload, err := io.ReadAll(bodyReader) if err != nil { log.Printf("[Webhook Debug] Error reading body: %v", err) c.JSON(http.StatusServiceUnavailable, gin.H{"error": "Service unavailable"}) return } log.Printf("[Webhook Debug] Received payload: %s", string(payload)) signatureHeader := c.Request.Header.Get("Stripe-Signature") log.Printf("[Webhook Debug] Stripe-Signature header: %s", signatureHeader) log.Printf("[Webhook Debug] Using endpoint secret: %s", h.endpointSecret) event, err := webhook.ConstructEvent(payload, signatureHeader, h.endpointSecret) if err != nil { log.Printf("[Webhook Debug] Signature verification failed: %v", err) log.Printf("[Webhook Debug] Headers received: %+v", c.Request.Header) c.JSON(http.StatusBadRequest, gin.H{"error": "Bad request"}) return } log.Printf("[Webhook Debug] Event type received: %s", event.Type) log.Printf("[Webhook Debug] Event data: %s", string(event.Data.Raw)) switch event.Type { case "customer.subscription.deleted": var subscription stripe.Subscription if err := json.Unmarshal(event.Data.Raw, &subscription); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } log.Printf("Subscription deleted for %s.", subscription.ID) case "customer.subscription.updated": var subscription stripe.Subscription if err := json.Unmarshal(event.Data.Raw, &subscription); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } log.Printf("Subscription updated for %s.", subscription.ID) case "customer.subscription.created": var subscription stripe.Subscription if err := json.Unmarshal(event.Data.Raw, &subscription); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } log.Printf("Subscription created for %s.", subscription.ID) case "checkout.session.completed": var session stripe.CheckoutSession if err := json.Unmarshal(event.Data.Raw, &session); err != nil { log.Printf("[Webhook Debug] Failed to parse session data: %v", err) c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } log.Printf("[Webhook Debug] Payment successful for session: %s", session.ID) customerEmail := session.CustomerEmail log.Printf("[Webhook Debug] Customer email: %s", customerEmail) if err := h.mailer.SendWelcomeEmail(customerEmail, &mailer.EmailData{ Username: customerEmail, WebAppURL: fmt.Sprintf("http://test.byom.fr/login?email=%s", customerEmail), SetupGuide: "Votre espace de travail est prêt ! Connectez-vous pour commencer à utiliser Byom.", }); err != nil { log.Printf("[Webhook Debug] Failed to send welcome email: %v", err) } case "checkout.session.expired": var session stripe.CheckoutSession if err := json.Unmarshal(event.Data.Raw, &session); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } log.Printf("Payment session expired: %s", session.ID) // TODO: Gérer l'expiration de la session default: fmt.Fprintf(os.Stderr, "Unhandled event type: %s\n", event.Type) } c.JSON(http.StatusOK, gin.H{"status": "success"}) }