Skip to content

Latest commit

 

History

History
140 lines (106 loc) · 4.66 KB

File metadata and controls

140 lines (106 loc) · 4.66 KB

webhooks

Tier: Adapter · Status: Full · Java original: firefly-webhooks · .NET project: FireflyFramework.Webhooks.{Interfaces,Core,Processor,Web,Sdk}

Overview

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)

Built-in validators

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

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

Public surface

interfaces

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
}

core

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")

web

func Handler(p *core.Pipeline) http.Handler

The handler performs:

  1. Read body fully (up to the http.Server's body limit).
  2. Look up the provider's Validator. 404 if unknown provider.
  3. Verify the signature. 401 on mismatch.
  4. Build an Inbound. Call Pipeline.Process(ctx, ev).
  5. 202 Accepted on success; 500 on processor error.

sdk

type Client struct{ ... }
func New(baseURL string) *Client
func (*Client) Forward(ctx, provider string, payload []byte, headers map[string]string) error

Quick start

secret := []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)

Testing

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.