Skip to content

Commit cc1cac2

Browse files
committed
refactor(webhooks): use a secure unified webhook handler
1 parent a124fe5 commit cc1cac2

File tree

1 file changed

+135
-0
lines changed

1 file changed

+135
-0
lines changed

api/webhook_handler.go

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package api
2+
3+
import (
4+
"encoding/json"
5+
"io"
6+
"net/http"
7+
"time"
8+
9+
"github.com/gorilla/mux"
10+
"github.com/malwarebo/gopay/providers"
11+
)
12+
13+
type WebhookHandler struct {
14+
stripeProvider *providers.StripeProvider
15+
xenditProvider *providers.XenditProvider
16+
}
17+
18+
func CreateWebhookHandler(stripeProvider *providers.StripeProvider, xenditProvider *providers.XenditProvider) *WebhookHandler {
19+
return &WebhookHandler{
20+
stripeProvider: stripeProvider,
21+
xenditProvider: xenditProvider,
22+
}
23+
}
24+
25+
func (h *WebhookHandler) HandleStripeWebhook(w http.ResponseWriter, r *http.Request) {
26+
payload, err := io.ReadAll(r.Body)
27+
if err != nil {
28+
http.Error(w, "Failed to read webhook payload", http.StatusBadRequest)
29+
return
30+
}
31+
32+
signature := r.Header.Get("Stripe-Signature")
33+
if signature == "" {
34+
http.Error(w, "Missing Stripe signature", http.StatusUnauthorized)
35+
return
36+
}
37+
38+
if err := h.stripeProvider.ValidateWebhookSignature(payload, signature); err != nil {
39+
http.Error(w, "Invalid webhook signature", http.StatusUnauthorized)
40+
return
41+
}
42+
43+
var event map[string]interface{}
44+
if err := json.Unmarshal(payload, &event); err != nil {
45+
http.Error(w, "Invalid webhook payload", http.StatusBadRequest)
46+
return
47+
}
48+
49+
eventType, ok := event["type"].(string)
50+
if !ok {
51+
http.Error(w, "Missing event type", http.StatusBadRequest)
52+
return
53+
}
54+
55+
h.processStripeEvent(eventType, event)
56+
57+
response := map[string]interface{}{
58+
"received": true,
59+
"event_type": eventType,
60+
"timestamp": time.Now(),
61+
}
62+
63+
w.Header().Set("Content-Type", "application/json")
64+
w.WriteHeader(http.StatusOK)
65+
json.NewEncoder(w).Encode(response)
66+
}
67+
68+
func (h *WebhookHandler) HandleXenditWebhook(w http.ResponseWriter, r *http.Request) {
69+
payload, err := io.ReadAll(r.Body)
70+
if err != nil {
71+
http.Error(w, "Failed to read webhook payload", http.StatusBadRequest)
72+
return
73+
}
74+
75+
signature := r.Header.Get("x-callback-token")
76+
if signature == "" {
77+
http.Error(w, "Missing Xendit signature", http.StatusUnauthorized)
78+
return
79+
}
80+
81+
if err := h.xenditProvider.ValidateWebhookSignature(payload, signature); err != nil {
82+
http.Error(w, "Invalid webhook signature", http.StatusUnauthorized)
83+
return
84+
}
85+
86+
var event map[string]interface{}
87+
if err := json.Unmarshal(payload, &event); err != nil {
88+
http.Error(w, "Invalid webhook payload", http.StatusBadRequest)
89+
return
90+
}
91+
92+
eventType, ok := event["event"].(string)
93+
if !ok {
94+
eventType = "unknown"
95+
}
96+
97+
h.processXenditEvent(eventType, event)
98+
99+
response := map[string]interface{}{
100+
"received": true,
101+
"event_type": eventType,
102+
"timestamp": time.Now(),
103+
}
104+
105+
w.Header().Set("Content-Type", "application/json")
106+
w.WriteHeader(http.StatusOK)
107+
json.NewEncoder(w).Encode(response)
108+
}
109+
110+
func (h *WebhookHandler) processStripeEvent(eventType string, event map[string]interface{}) {
111+
switch eventType {
112+
case "payment_intent.succeeded":
113+
case "payment_intent.payment_failed":
114+
case "payment_intent.requires_action":
115+
case "payment_intent.canceled":
116+
case "charge.refunded":
117+
case "charge.dispute.created":
118+
default:
119+
}
120+
}
121+
122+
func (h *WebhookHandler) processXenditEvent(eventType string, event map[string]interface{}) {
123+
switch eventType {
124+
case "payment.succeeded":
125+
case "payment.failed":
126+
case "payment.pending":
127+
case "refund.succeeded":
128+
default:
129+
}
130+
}
131+
132+
func (h *WebhookHandler) RegisterRoutes(router *mux.Router) {
133+
router.HandleFunc("/webhooks/stripe", h.HandleStripeWebhook).Methods("POST")
134+
router.HandleFunc("/webhooks/xendit", h.HandleXenditWebhook).Methods("POST")
135+
}

0 commit comments

Comments
 (0)