Files
telecom-platform/apps/api-server/internal/payment/webhook.go
T
nutcas3 cb1d7171e0 feat: Add PaymentController with modular handlers for payment operations, refunds, and webhook processing
- Add PaymentController struct with paymentService/db dependencies and NewPaymentController constructor
- Add SetupRoutes with /payments endpoints (POST /top-up, POST/DELETE/GET /payment-methods, GET /transactions/:id, POST /transactions/:id/refund) and /webhooks/stripe endpoint
- Add ProcessTopUp handler with amount/paymentMethodId validation, subscriberID authentication, and PaymentService.
2026-04-24 08:53:34 +03:00

95 lines
2.5 KiB
Go

package payment
import (
"encoding/json"
"log"
"net/http"
"github.com/gin-gonic/gin"
"github.com/nutcas3/telecom-platform/apps/api-server/internal/database"
)
// WebhookEvent represents a payment webhook event
type WebhookEvent struct {
ID string `json:"id"`
Type string `json:"type"`
Object string `json:"object"`
Created int64 `json:"created"`
Livemode bool `json:"livemode"`
Data map[string]any `json:"data"`
}
// WebhookHandler handles payment webhooks
type WebhookHandler struct {
paymentService *PaymentService
db *database.Database
}
// NewWebhookHandler creates a new webhook handler
func NewWebhookHandler(paymentService *PaymentService, db *database.Database) *WebhookHandler {
return &WebhookHandler{
paymentService: paymentService,
db: db,
}
}
// HandleStripeWebhook handles Stripe webhooks
func (wh *WebhookHandler) HandleStripeWebhook(c *gin.Context) {
body, err := c.GetRawData()
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to read request body"})
return
}
signature := c.GetHeader("Stripe-Signature")
if signature == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "Missing signature"})
return
}
// Validate webhook
valid, err := wh.paymentService.ValidateWebhook(c.Request.Context(), signature, body)
if err != nil {
log.Printf("Webhook validation error: %v", err)
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid webhook"})
return
}
if !valid {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid signature"})
return
}
// Parse event
var event WebhookEvent
if err := json.Unmarshal(body, &event); err != nil {
log.Printf("Failed to parse webhook event: %v", err)
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid event format"})
return
}
// Handle different event types
switch event.Type {
case "payment_intent.succeeded":
err = wh.handlePaymentSucceeded(c.Request.Context(), event)
case "payment_intent.payment_failed":
err = wh.handlePaymentFailed(c.Request.Context(), event)
case "payment_intent.canceled":
err = wh.handlePaymentCanceled(c.Request.Context(), event)
case "charge.dispute.created":
err = wh.handleDisputeCreated(c.Request.Context(), event)
default:
log.Printf("Unhandled webhook event type: %s", event.Type)
}
if err != nil {
log.Printf("Error handling webhook event %s: %v", event.Type, err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to process webhook"})
return
}
c.JSON(http.StatusOK, gin.H{"status": "received"})
}