125 lines
3.3 KiB
Go
125 lines
3.3 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"encoding/hex"
|
|
"log"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/joho/godotenv"
|
|
"github.com/labstack/echo/v4"
|
|
"github.com/lightninglabs/lndclient"
|
|
"github.com/lightningnetwork/lnd/lnrpc"
|
|
"github.com/namsral/flag"
|
|
)
|
|
|
|
var (
|
|
LndCert string
|
|
LndMacaroonDir string
|
|
LndHost string
|
|
lnd *LndClient
|
|
)
|
|
|
|
type LndClient struct {
|
|
lnrpc.LightningClient
|
|
}
|
|
|
|
func init() {
|
|
err := godotenv.Load()
|
|
if err != nil {
|
|
log.Println("Error loading .env file")
|
|
}
|
|
flag.StringVar(&LndCert, "LND_CERT", "", "Path to LND TLS certificate")
|
|
flag.StringVar(&LndMacaroonDir, "LND_MACAROON_DIR", "", "LND macaroon directory")
|
|
flag.StringVar(&LndHost, "LND_HOST", "localhost:10001", "LND gRPC server address")
|
|
flag.Parse()
|
|
rpcClient, err := lndclient.NewBasicClient(LndHost, LndCert, LndMacaroonDir, "regtest")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
lnd = &LndClient{LightningClient: rpcClient}
|
|
if info, err := lnd.GetInfo(context.TODO(), &lnrpc.GetInfoRequest{}); err != nil {
|
|
panic(err)
|
|
} else {
|
|
version := strings.Split(info.Version, " ")[0]
|
|
log.Printf("Connected to %s running LND v%s", LndHost, version)
|
|
}
|
|
}
|
|
|
|
func (lnd *LndClient) CreateInvoice(pubkey string, msats int) (*Invoice, error) {
|
|
addInvoiceResponse, err := lnd.AddInvoice(context.TODO(), &lnrpc.Invoice{
|
|
ValueMsat: int64(msats),
|
|
Expiry: 3600,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
lnInvoice, err := lnd.LookupInvoice(context.TODO(), &lnrpc.PaymentHash{RHash: addInvoiceResponse.RHash})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
dbInvoice := Invoice{
|
|
Session: Session{pubkey},
|
|
Msats: msats,
|
|
Preimage: hex.EncodeToString(lnInvoice.RPreimage),
|
|
PaymentRequest: lnInvoice.PaymentRequest,
|
|
PaymentHash: hex.EncodeToString(lnInvoice.RHash),
|
|
CreatedAt: time.Unix(lnInvoice.CreationDate, 0),
|
|
ExpiresAt: time.Unix(lnInvoice.CreationDate+lnInvoice.Expiry, 0),
|
|
}
|
|
if err := db.CreateInvoice(&dbInvoice); err != nil {
|
|
return nil, err
|
|
}
|
|
return &dbInvoice, nil
|
|
}
|
|
|
|
func (lnd *LndClient) CheckInvoice(hash string) {
|
|
for {
|
|
log.Printf("lookup invoice: hash=%s", hash)
|
|
invoice, err := lnd.LookupInvoice(context.TODO(), &lnrpc.PaymentHash{RHashStr: hash})
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
if time.Now().After(time.Unix(invoice.CreationDate+invoice.Expiry, 0)) {
|
|
log.Printf("invoice expired: hash=%s", hash)
|
|
break
|
|
}
|
|
if invoice.SettleDate != 0 && invoice.AmtPaidMsat > 0 {
|
|
if err := db.ConfirmInvoice(hash, time.Unix(invoice.SettleDate, 0), int(invoice.AmtPaidMsat)); err != nil {
|
|
panic(err)
|
|
}
|
|
log.Printf("invoice confirmed: hash=%s", hash)
|
|
break
|
|
}
|
|
time.Sleep(5 * time.Second)
|
|
}
|
|
}
|
|
|
|
func invoice(c echo.Context) error {
|
|
invoiceId := c.Param("id")
|
|
var invoice Invoice
|
|
if err := db.FetchInvoice(invoiceId, &invoice); err == sql.ErrNoRows {
|
|
return echo.NewHTTPError(http.StatusNotFound, "Not Found")
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
session := c.Get("session").(Session)
|
|
if invoice.Pubkey != session.Pubkey {
|
|
return echo.NewHTTPError(http.StatusUnauthorized, "Unauthorized")
|
|
}
|
|
qr, err := ToQR(invoice.PaymentRequest)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
data := map[string]any{
|
|
"session": c.Get("session"),
|
|
"Invoice": invoice,
|
|
"lnurl": invoice.PaymentRequest,
|
|
"qr": qr,
|
|
}
|
|
return c.Render(http.StatusOK, "invoice.html", data)
|
|
}
|