From aa584d2cb5dd0018c1536b07e9532d9ee347f04e Mon Sep 17 00:00:00 2001 From: ekzyis Date: Fri, 27 Dec 2024 01:52:47 +0100 Subject: [PATCH] Phoenixd webhook handler --- lightning/lightning.go | 17 ++++++- lightning/phoenixd/phoenixd.go | 88 +++++++++++++++++++++++++++++++++- server/server.go | 12 +++++ 3 files changed, 113 insertions(+), 4 deletions(-) diff --git a/lightning/lightning.go b/lightning/lightning.go index a6fc31e..c7ee255 100644 --- a/lightning/lightning.go +++ b/lightning/lightning.go @@ -1,7 +1,20 @@ package lightning -type Bolt11 string +import "time" + +type PaymentRequest string type Lightning interface { - CreateInvoice(msats int64, description string) (Bolt11, error) + CreateInvoice(msats int64, description string) (PaymentRequest, error) + GetInvoice(paymentHash string) (*Invoice, error) +} + +type Invoice struct { + PaymentHash string + Preimage string + Msats int64 + Description string + PaymentRequest string + CreatedAt time.Time + ConfirmedAt time.Time } diff --git a/lightning/phoenixd/phoenixd.go b/lightning/phoenixd/phoenixd.go index 7f0ca99..c2d2eb1 100644 --- a/lightning/phoenixd/phoenixd.go +++ b/lightning/phoenixd/phoenixd.go @@ -9,8 +9,10 @@ import ( "net/url" "strconv" "strings" + "time" "github.com/ekzyis/zaply/lightning" + "github.com/labstack/echo/v4" ) type Phoenixd struct { @@ -53,7 +55,7 @@ func WithPhoenixdWebhookUrl(webhookUrl string) func(*Phoenixd) *Phoenixd { } } -func (p *Phoenixd) CreateInvoice(msats int64, description string) (lightning.Bolt11, error) { +func (p *Phoenixd) CreateInvoice(msats int64, description string) (lightning.PaymentRequest, error) { values := url.Values{} values.Add("amountSat", strconv.FormatInt(msats/1000, 10)) values.Add("description", description) @@ -94,5 +96,87 @@ func (p *Phoenixd) CreateInvoice(msats int64, description string) (lightning.Bol return "", err } - return lightning.Bolt11(response.Serialized), nil + return lightning.PaymentRequest(response.Serialized), nil +} + +func (p *Phoenixd) GetInvoice(paymentHash string) (*lightning.Invoice, error) { + endpoint := p.url.JoinPath("payments/incoming", paymentHash) + + req, err := http.NewRequest("GET", endpoint.String(), nil) + if err != nil { + return nil, err + } + if p.limitedAccessToken != "" { + req.SetBasicAuth("", p.limitedAccessToken) + } + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("phoenixd %s: %s", resp.Status, string(body)) + } + + var response struct { + PaymentHash string `json:"paymentHash"` + Preimage string `json:"preimage"` + Msats int64 `json:"receivedSat"` + Description string `json:"description"` + CreatedAt int64 `json:"createdAt"` + ConfirmedAt int64 `json:"completedAt"` + } + if err := json.Unmarshal(body, &response); err != nil { + return nil, err + } + + createdAt := time.Unix(response.CreatedAt/1000, 0) + var confirmedAt time.Time + if response.ConfirmedAt != 0 { + confirmedAt = time.Unix(response.ConfirmedAt/1000, 0) + } + + return &lightning.Invoice{ + PaymentHash: response.PaymentHash, + Preimage: response.Preimage, + Msats: response.Msats, + Description: response.Description, + CreatedAt: createdAt, + ConfirmedAt: confirmedAt, + }, nil +} + +func (p *Phoenixd) WebhookHandler(c echo.Context) error { + go func() { + var webhook struct { + Type string `json:"type"` + AmountSat int64 `json:"amountSat"` + PaymentHash string `json:"paymentHash"` + } + if err := c.Bind(&webhook); err != nil { + c.Logger().Error(err) + return + } + + inv, err := p.GetInvoice(webhook.PaymentHash) + if err != nil { + c.Logger().Error(err) + return + } + + log.Printf( + "payment received: %s | %d msats | %s | %s | %s", + inv.PaymentHash, inv.Msats, inv.Description, + inv.CreatedAt.Format(time.RFC3339), inv.ConfirmedAt.Format(time.RFC3339), + ) + }() + + return c.NoContent(http.StatusOK) } diff --git a/server/server.go b/server/server.go index aa7c94e..28bd644 100644 --- a/server/server.go +++ b/server/server.go @@ -1,6 +1,9 @@ package server import ( + "log" + "net/url" + "github.com/ekzyis/zaply/env" "github.com/ekzyis/zaply/lightning/phoenixd" "github.com/ekzyis/zaply/lnurl" @@ -22,11 +25,20 @@ func NewServer() *Server { CustomTimeFormat: "2006-01-02 15:04:05.00000-0700", })) + webhookPath := "/overlay/webhook" + webhookUrl, err := url.JoinPath(env.PublicUrl, webhookPath) + if err != nil { + log.Fatal(err) + } + p := phoenixd.NewPhoenixd( phoenixd.WithPhoenixdURL(env.PhoenixdURL), phoenixd.WithPhoenixdLimitedAccessToken(env.PhoenixdLimitedAccessToken), + phoenixd.WithPhoenixdWebhookUrl(webhookUrl), ) + s.POST(webhookPath, p.WebhookHandler) + lnurl.Router(s.Echo, p) return s