You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
bit4sat/ln/invoice.go

192 lines
4.1 KiB
Go

package ln
import (
"encoding/hex"
"encoding/json"
"log"
"strconv"
"strings"
"time"
"github.com/lightningnetwork/lnd/lnrpc"
)
type Currency int
const (
CurMSat Currency = iota
CurSat
CurBTC
CurUSD
CurEur
)
const (
Paid int = iota
UnPaid
Expired
)
var InvoiceStatus = map[int]string{
UnPaid: "unpaid",
Paid: "paid",
Expired: "expired",
}
const (
InfoEndpoint = "info"
InvoiceEndpoint = "invoice"
)
var (
CurrencyString = map[Currency]string{
CurUSD: "USD",
CurSat: "SAT",
CurBTC: "BTC",
CurMSat: "MSAT",
CurEur: "EUR",
}
CurrencyID = map[string]Currency{
"USD": CurUSD,
"SAT": CurSat,
"MSAT": CurMSat,
"BTC": CurBTC,
"EUR": CurEur,
}
)
type timestamp time.Time
func (t *timestamp) UnmarshalJSON(in []byte) error {
if string(in) == "null" {
*t = timestamp(time.Unix(0, 0))
return nil
}
// Try to first parse in RFC3339
var val time.Time
var err error
// put the input in string mode
strIn := string(in)
// clean
strIn = strings.Replace(strIn, "\"", "", -1)
if val, err = time.Parse(time.RFC3339, strIn); err != nil {
val, err := strconv.Atoi(strIn)
if err != nil {
log.Printf("error unmarshal time %s", in)
return err
}
parsedTime := time.Unix(int64(val), 0)
*t = timestamp(parsedTime)
return nil
}
*t = timestamp(val)
return nil
}
func (t timestamp) MarshalJSON() ([]byte, error) {
str := strconv.Itoa(int(time.Time(t).Unix()))
return []byte(str), nil
}
func (t timestamp) String() string {
return time.Time(t).Format(time.RFC3339)
}
type Invoice struct {
AddIndex uint64 `json:"-"`
SettleIndex uint64 `json:"-"`
Description string `json:"description"`
Msatoshi float64 `json:"msatoshi"`
Payreq string `json:"payreq"`
RHash string `json:"rhash"`
PreImage string `json:"-"` // used as secret admin token
Status string `json:"status"`
Settled bool `json:"settled"`
PaidAt timestamp `json:"paid_at"`
CreatedAt timestamp `json:"created_at"`
ExpiresAt timestamp `json:"expires_at"`
Expired bool `json:"-"`
QuotedCurrency string `json:"-"`
QuotedAmount float64 `json:"-"`
}
func (i Invoice) MarshalBinary() ([]byte, error) {
return json.Marshal(i)
}
func (i *Invoice) UnmarshalBinary(b []byte) error {
return json.Unmarshal(b, &i)
}
// Create new Invoice from lnrpc.Invoice
func InvoiceFromLndIn(lndInvoice *lnrpc.Invoice) *Invoice {
//log.Printf("new invoice form lnd with value %d", lndInvoice.Value)
//log.Printf("setting msat to %f", float64(lndInvoice.Value*1000))
invoice := Invoice{
AddIndex: lndInvoice.AddIndex,
Description: lndInvoice.GetMemo(),
Msatoshi: float64(lndInvoice.Value * 1000),
Payreq: lndInvoice.PaymentRequest,
RHash: hex.EncodeToString(lndInvoice.RHash),
PreImage: hex.EncodeToString(lndInvoice.RPreimage),
CreatedAt: timestamp(time.Unix(lndInvoice.CreationDate, 0)),
PaidAt: timestamp(time.Unix(lndInvoice.SettleDate, 0)),
Status: InvoiceStatus[UnPaid],
}
invoice.ExpiresAt = timestamp(time.Time(invoice.CreatedAt).Add(time.Second * time.Duration(lndInvoice.Expiry)))
// Calculate status
if lndInvoice.Settled {
invoice.Settled = true
invoice.Status = InvoiceStatus[Paid]
}
if time.Now().After(time.Time(invoice.ExpiresAt)) {
invoice.Status = InvoiceStatus[Expired]
invoice.Expired = true
}
return &invoice
}
// Update stored invoice from lnd invoice
func UpdateInvoiceFromLnd(storedIn *Invoice, newIn *lnrpc.Invoice) *Invoice {
storedIn.SettleIndex = newIn.SettleIndex
storedIn.PaidAt = timestamp(time.Unix(newIn.SettleDate, 0))
// Calculate status
if newIn.Settled {
storedIn.Status = InvoiceStatus[Paid]
storedIn.Settled = true
}
// Handle expired status
if time.Now().After(time.Time(storedIn.ExpiresAt)) {
storedIn.Status = InvoiceStatus[Expired]
storedIn.Expired = true
}
return storedIn
}
func IsExpiredInvoice(creation, expiry int64) bool {
createdAt := time.Unix(int64(creation), 0)
expiresAt := createdAt.Add(time.Second * time.Duration(expiry))
return time.Now().After(expiresAt)
}