HandleWebhook is the HTTP handler for POST /webhook/stripe.
(w http.ResponseWriter, r *http.Request)
| 36 | |
| 37 | // HandleWebhook is the HTTP handler for POST /webhook/stripe. |
| 38 | func (d *WebhookDeps) HandleWebhook(w http.ResponseWriter, r *http.Request) { |
| 39 | body, err := io.ReadAll(io.LimitReader(r.Body, 65536)) |
| 40 | if err != nil { |
| 41 | http.Error(w, "read body failed", http.StatusBadRequest) |
| 42 | return |
| 43 | } |
| 44 | |
| 45 | event, err := ConstructEventAuto(body, r.Header.Get("Stripe-Signature"), d.Settings, d.EnvWebhookSecret) |
| 46 | if err != nil { |
| 47 | log.Printf("payment: webhook signature error: %v", err) |
| 48 | http.Error(w, "invalid signature", http.StatusBadRequest) |
| 49 | return |
| 50 | } |
| 51 | |
| 52 | // 幂等性和并发安全由数据库层保证(状态检查 + UpsertOrder),不依赖进程内锁。 |
| 53 | switch event.Type { |
| 54 | case "checkout.session.completed": |
| 55 | d.handleCheckoutCompleted(event) |
| 56 | case "invoice.paid": |
| 57 | d.handleInvoicePaid(event) |
| 58 | case "invoice.payment_failed": |
| 59 | d.handleInvoicePaymentFailed(event) |
| 60 | case "customer.subscription.deleted": |
| 61 | d.handleSubscriptionDeleted(event) |
| 62 | } |
| 63 | |
| 64 | w.WriteHeader(http.StatusOK) |
| 65 | } |
| 66 | |
| 67 | func (d *WebhookDeps) handleCheckoutCompleted(event stripe.Event) { |
| 68 | var sess stripe.CheckoutSession |
nothing calls this directly
no test coverage detected