Files
Nip-05-api/internal/invoice/lnbits.go
2026-04-29 02:35:00 +00:00

105 lines
2.6 KiB
Go

package invoice
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"time"
)
type LNbitsClient struct {
baseURL string
invoiceKey string
hc *http.Client
}
func NewLNbits(baseURL, invoiceKey string) *LNbitsClient {
return &LNbitsClient{
baseURL: strings.TrimRight(baseURL, "/"),
invoiceKey: invoiceKey,
hc: &http.Client{Timeout: 15 * time.Second},
}
}
type createReq struct {
Out bool `json:"out"`
Amount int64 `json:"amount"`
Memo string `json:"memo"`
Expiry int `json:"expiry,omitempty"`
}
type createResp struct {
PaymentHash string `json:"payment_hash"`
PaymentRequest string `json:"payment_request"`
BOLT11 string `json:"bolt11"`
}
type statusResp struct {
Paid bool `json:"paid"`
Pending bool `json:"pending"`
Details *struct {
Pending bool `json:"pending"`
Status string `json:"status"`
} `json:"details"`
}
func (c *LNbitsClient) Create(ctx context.Context, amountSats int64, memo string, expirySecs int) (string, string, error) {
body, err := json.Marshal(createReq{Out: false, Amount: amountSats, Memo: memo, Expiry: expirySecs})
if err != nil {
return "", "", err
}
req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.baseURL+"/api/v1/payments", bytes.NewReader(body))
if err != nil {
return "", "", err
}
req.Header.Set("X-Api-Key", c.invoiceKey)
req.Header.Set("Content-Type", "application/json")
resp, err := c.hc.Do(req)
if err != nil {
return "", "", fmt.Errorf("%w: %v", ErrLNbits, err)
}
defer resp.Body.Close()
b, _ := io.ReadAll(resp.Body)
if resp.StatusCode/100 != 2 {
return "", "", fmt.Errorf("%w: %s", ErrLNbits, string(b))
}
var cr createResp
if err := json.Unmarshal(b, &cr); err != nil {
return "", "", fmt.Errorf("%w: decode: %v", ErrLNbits, err)
}
pr := cr.PaymentRequest
if pr == "" {
pr = cr.BOLT11
}
return cr.PaymentHash, pr, nil
}
func (c *LNbitsClient) Status(ctx context.Context, hash string) (bool, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.baseURL+"/api/v1/payments/"+hash, nil)
if err != nil {
return false, err
}
req.Header.Set("X-Api-Key", c.invoiceKey)
resp, err := c.hc.Do(req)
if err != nil {
return false, fmt.Errorf("%w: %v", ErrLNbits, err)
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusNotFound {
return false, nil
}
if resp.StatusCode/100 != 2 {
b, _ := io.ReadAll(resp.Body)
return false, fmt.Errorf("%w: %s", ErrLNbits, string(b))
}
var sr statusResp
if err := json.NewDecoder(resp.Body).Decode(&sr); err != nil {
return false, err
}
return sr.Paid, nil
}