|
@@ -0,0 +1,193 @@
|
|
|
+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"})
|
|
|
+}
|