105 lines
2.6 KiB
Go
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
|
|
}
|