stripe.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. package payment
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io"
  6. "log"
  7. "net/http"
  8. "os"
  9. "git.linuxforward.com/byom/byom-onboard/internal/platform/mailer"
  10. "github.com/gin-gonic/gin"
  11. "github.com/stripe/stripe-go/v81"
  12. "github.com/stripe/stripe-go/v81/account"
  13. portalsession "github.com/stripe/stripe-go/v81/billingportal/session"
  14. "github.com/stripe/stripe-go/v81/checkout/session"
  15. "github.com/stripe/stripe-go/v81/webhook"
  16. )
  17. type StripeHandler struct {
  18. domain string
  19. endpointSecret string
  20. mailer *mailer.Mailer
  21. }
  22. func NewStripeHandler(apiKey, endpointSecret, domain string, mailer *mailer.Mailer) (*StripeHandler, error) {
  23. stripe.Key = apiKey
  24. // Test connection by retrieving account information
  25. _, err := account.Get()
  26. if err != nil {
  27. return nil, fmt.Errorf("failed to connect to Stripe: %w", err)
  28. }
  29. return &StripeHandler{
  30. domain: domain,
  31. endpointSecret: endpointSecret,
  32. mailer: mailer,
  33. }, nil
  34. }
  35. func (h *StripeHandler) CreateCheckoutSession(c *gin.Context) {
  36. // Get email from request
  37. customerEmail := c.PostForm("email")
  38. if customerEmail == "" {
  39. c.JSON(http.StatusBadRequest, gin.H{"error": "Email is required"})
  40. return
  41. }
  42. checkoutParams := &stripe.CheckoutSessionParams{
  43. Mode: stripe.String(string(stripe.CheckoutSessionModePayment)),
  44. LineItems: []*stripe.CheckoutSessionLineItemParams{
  45. {
  46. PriceData: &stripe.CheckoutSessionLineItemPriceDataParams{
  47. Currency: stripe.String("eur"),
  48. ProductData: &stripe.CheckoutSessionLineItemPriceDataProductDataParams{
  49. Name: stripe.String("Byom Subscription"),
  50. },
  51. UnitAmount: stripe.Int64(2000), // 20€ en centimes
  52. },
  53. Quantity: stripe.Int64(1),
  54. },
  55. },
  56. SuccessURL: stripe.String("http://192.168.1.35:5173/payment/success?session_id={CHECKOUT_SESSION_ID}"),
  57. CancelURL: stripe.String("http://192.168.1.35:5173/payment/cancel"),
  58. CustomerEmail: stripe.String(customerEmail),
  59. CustomerCreation: stripe.String(string(stripe.CheckoutSessionCustomerCreationAlways)),
  60. }
  61. s, err := session.New(checkoutParams)
  62. if err != nil {
  63. c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
  64. return
  65. }
  66. // Renvoyer uniquement l'URL et l'ID de session
  67. c.JSON(http.StatusOK, gin.H{
  68. "url": s.URL,
  69. "sessionId": s.ID,
  70. })
  71. }
  72. func (h *StripeHandler) CreatePortalSession(c *gin.Context) {
  73. sessionID := c.PostForm("session_id")
  74. s, err := session.Get(sessionID, nil)
  75. if err != nil {
  76. c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
  77. log.Printf("session.Get: %v", err)
  78. return
  79. }
  80. params := &stripe.BillingPortalSessionParams{
  81. Customer: stripe.String(s.Customer.ID),
  82. ReturnURL: stripe.String(h.domain),
  83. }
  84. ps, err := portalsession.New(params)
  85. if err != nil {
  86. c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
  87. log.Printf("ps.New: %v", err)
  88. return
  89. }
  90. c.Redirect(http.StatusSeeOther, ps.URL)
  91. }
  92. func (h *StripeHandler) HandleWebhook(c *gin.Context) {
  93. const MaxBodyBytes = int64(65536)
  94. bodyReader := http.MaxBytesReader(c.Writer, c.Request.Body, MaxBodyBytes)
  95. payload, err := io.ReadAll(bodyReader)
  96. if err != nil {
  97. log.Printf("[Webhook Debug] Error reading body: %v", err)
  98. c.JSON(http.StatusServiceUnavailable, gin.H{"error": "Service unavailable"})
  99. return
  100. }
  101. log.Printf("[Webhook Debug] Received payload: %s", string(payload))
  102. signatureHeader := c.Request.Header.Get("Stripe-Signature")
  103. log.Printf("[Webhook Debug] Stripe-Signature header: %s", signatureHeader)
  104. log.Printf("[Webhook Debug] Using endpoint secret: %s", h.endpointSecret)
  105. event, err := webhook.ConstructEvent(payload, signatureHeader, h.endpointSecret)
  106. if err != nil {
  107. log.Printf("[Webhook Debug] Signature verification failed: %v", err)
  108. log.Printf("[Webhook Debug] Headers received: %+v", c.Request.Header)
  109. c.JSON(http.StatusBadRequest, gin.H{"error": "Bad request"})
  110. return
  111. }
  112. log.Printf("[Webhook Debug] Event type received: %s", event.Type)
  113. log.Printf("[Webhook Debug] Event data: %s", string(event.Data.Raw))
  114. switch event.Type {
  115. case "customer.subscription.deleted":
  116. var subscription stripe.Subscription
  117. if err := json.Unmarshal(event.Data.Raw, &subscription); err != nil {
  118. c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
  119. return
  120. }
  121. log.Printf("Subscription deleted for %s.", subscription.ID)
  122. case "customer.subscription.updated":
  123. var subscription stripe.Subscription
  124. if err := json.Unmarshal(event.Data.Raw, &subscription); err != nil {
  125. c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
  126. return
  127. }
  128. log.Printf("Subscription updated for %s.", subscription.ID)
  129. case "customer.subscription.created":
  130. var subscription stripe.Subscription
  131. if err := json.Unmarshal(event.Data.Raw, &subscription); err != nil {
  132. c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
  133. return
  134. }
  135. log.Printf("Subscription created for %s.", subscription.ID)
  136. case "checkout.session.completed":
  137. var session stripe.CheckoutSession
  138. if err := json.Unmarshal(event.Data.Raw, &session); err != nil {
  139. log.Printf("[Webhook Debug] Failed to parse session data: %v", err)
  140. c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
  141. return
  142. }
  143. log.Printf("[Webhook Debug] Payment successful for session: %s", session.ID)
  144. customerEmail := session.CustomerEmail
  145. log.Printf("[Webhook Debug] Customer email: %s", customerEmail)
  146. if err := h.mailer.SendWelcomeEmail(customerEmail, &mailer.EmailData{
  147. Username: customerEmail,
  148. WebAppURL: fmt.Sprintf("http://test.byom.fr/login?email=%s", customerEmail),
  149. SetupGuide: "Votre espace de travail est prêt ! Connectez-vous pour commencer à utiliser Byom.",
  150. }); err != nil {
  151. log.Printf("[Webhook Debug] Failed to send welcome email: %v", err)
  152. }
  153. case "checkout.session.expired":
  154. var session stripe.CheckoutSession
  155. if err := json.Unmarshal(event.Data.Raw, &session); err != nil {
  156. c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
  157. return
  158. }
  159. log.Printf("Payment session expired: %s", session.ID)
  160. // TODO: Gérer l'expiration de la session
  161. default:
  162. fmt.Fprintf(os.Stderr, "Unhandled event type: %s\n", event.Type)
  163. }
  164. c.JSON(http.StatusOK, gin.H{"status": "success"})
  165. }