Tier: Adapter · Status: Full · Java original:
firefly-webhooks· .NET project:FireflyFramework.Webhooks.{Interfaces,Core,Processor,Web,Sdk}
webhooks is the framework's inbound webhook ingestion subsystem.
HTTP requests arriving at POST /api/webhooks/{provider} are
validated against the registered signature scheme, optionally
enriched, and dispatched to per-provider Processors; failures are
sent to a dead-letter queue for replay.
Sub-packages mirror the .NET project split:
| Sub-package | What it provides |
|---|---|
webhooks/interfaces |
Inbound DTO + Validator, Processor ports |
webhooks/core |
Pipeline, in-memory DLQ, four canonical signature validators |
webhooks/processor |
SPI surface for service-supplied per-provider processors |
webhooks/web |
POST /api/webhooks/{provider} ingestion http.Handler |
webhooks/sdk |
Typed forwarder (replay DLQ entries; cross-service composition) |
| Validator | Header(s) | Algorithm |
|---|---|---|
NewHMACValidator |
X-Signature (default) |
HMAC-SHA256 hex (with optional sha256= prefix) |
NewStripeValidator |
Stripe-Signature |
t=<unix>,v1=<hmac-hex> with 5-min tolerance |
NewGitHubValidator |
X-Hub-Signature-256 |
HMAC-SHA256 hex |
NewTwilioValidator |
X-Twilio-Signature |
HMAC-SHA1 base64 of URL + sorted(form k+v) |
Bring your own by implementing the Validator interface.
Pipeline.Process(ctx, ev)
│
├─ enrich (optional hook)
│
├─ for each registered Processor for ev.Provider:
│ │ Process(ctx, ev)
│ │ on error → DLQ.Push, abort downstream processors
│
└─ return joined error
type Inbound struct {
ID, Provider, EventType string
Headers map[string]string
Payload []byte
ReceivedAt time.Time
}
type Validator interface {
Provider() string
Verify(req *http.Request, body []byte) error
}
type Processor interface {
Provider() string
Process(ctx, Inbound) error
}type DLQ interface { Push(ctx, Inbound, error) error }
type MemoryDLQ struct{ Events []DLQEntry }
func NewMemoryDLQ() *MemoryDLQ
type Pipeline struct{ ... }
func NewPipeline(dlq DLQ) *Pipeline
func (*Pipeline) RegisterValidator(Validator)
func (*Pipeline) RegisterProcessor(Processor)
func (*Pipeline) Enrich(func(*Inbound))
func (*Pipeline) Validators() map[string]Validator
func (*Pipeline) Process(ctx, Inbound) error
func NewHMACValidator(provider string, secret []byte) *HMACValidator
func NewStripeValidator(secret []byte) *StripeValidator
func NewGitHubValidator(secret []byte) *GitHubValidator
func NewTwilioValidator(authToken []byte, postURL string) *TwilioValidator
var ErrSignatureMismatch = errors.New("…signature mismatch")func Handler(p *core.Pipeline) http.HandlerThe handler performs:
- Read body fully (up to the http.Server's body limit).
- Look up the provider's
Validator. 404 if unknown provider. - Verify the signature. 401 on mismatch.
- Build an
Inbound. CallPipeline.Process(ctx, ev). - 202 Accepted on success; 500 on processor error.
type Client struct{ ... }
func New(baseURL string) *Client
func (*Client) Forward(ctx, provider string, payload []byte, headers map[string]string) errorsecret := []byte(os.Getenv("STRIPE_WEBHOOK_SECRET"))
p := core.NewPipeline(core.NewMemoryDLQ())
p.RegisterValidator(core.NewStripeValidator(secret))
p.RegisterProcessor(stripeProcessor{}) // your business handler
mux := http.NewServeMux()
mux.Handle("/api/webhooks/", whweb.Handler(p))
http.ListenAndServe(":8080", mux)cd webhooks
go test ./...Covers HMAC validator success + tamper detection, the pipeline DLQ flow, the enrichment hook, and the ingestion HTTP handler against a known-good signature plus 401 / 404 negative paths.