2019-03-29 18:50:11 +00:00
|
|
|
package ln
|
|
|
|
|
|
|
|
import (
|
2019-04-03 18:18:51 +00:00
|
|
|
"encoding/hex"
|
2019-04-02 13:53:52 +00:00
|
|
|
"encoding/json"
|
2019-04-04 00:27:09 +00:00
|
|
|
"log"
|
2019-04-01 10:12:29 +00:00
|
|
|
"strconv"
|
2019-04-04 18:26:43 +00:00
|
|
|
"strings"
|
2019-03-29 18:50:11 +00:00
|
|
|
"time"
|
2019-04-03 18:18:51 +00:00
|
|
|
|
|
|
|
"github.com/lightningnetwork/lnd/lnrpc"
|
2019-03-30 18:42:41 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type Currency int
|
|
|
|
|
|
|
|
const (
|
|
|
|
CurMSat Currency = iota
|
|
|
|
CurSat
|
|
|
|
CurBTC
|
|
|
|
CurUSD
|
|
|
|
CurEur
|
|
|
|
)
|
|
|
|
|
2019-04-03 18:18:51 +00:00
|
|
|
const (
|
|
|
|
Paid int = iota
|
|
|
|
UnPaid
|
|
|
|
Expired
|
|
|
|
)
|
|
|
|
|
|
|
|
var InvoiceStatus = map[int]string{
|
|
|
|
UnPaid: "unpaid",
|
|
|
|
Paid: "paid",
|
|
|
|
Expired: "expired",
|
|
|
|
}
|
|
|
|
|
2019-03-30 18:42:41 +00:00
|
|
|
const (
|
2019-04-02 17:40:25 +00:00
|
|
|
InfoEndpoint = "info"
|
|
|
|
InvoiceEndpoint = "invoice"
|
2019-03-30 18:42:41 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
CurrencyString = map[Currency]string{
|
|
|
|
CurUSD: "USD",
|
|
|
|
CurSat: "SAT",
|
|
|
|
CurBTC: "BTC",
|
|
|
|
CurMSat: "MSAT",
|
|
|
|
CurEur: "EUR",
|
|
|
|
}
|
2019-03-29 18:50:11 +00:00
|
|
|
|
2019-03-30 18:42:41 +00:00
|
|
|
CurrencyID = map[string]Currency{
|
|
|
|
"USD": CurUSD,
|
|
|
|
"SAT": CurSat,
|
|
|
|
"MSAT": CurMSat,
|
|
|
|
"BTC": CurBTC,
|
|
|
|
"EUR": CurEur,
|
|
|
|
}
|
2019-03-29 18:50:11 +00:00
|
|
|
)
|
|
|
|
|
2019-04-01 10:12:29 +00:00
|
|
|
type timestamp time.Time
|
|
|
|
|
|
|
|
func (t *timestamp) UnmarshalJSON(in []byte) error {
|
2019-04-02 17:40:25 +00:00
|
|
|
|
|
|
|
if string(in) == "null" {
|
|
|
|
*t = timestamp(time.Unix(0, 0))
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-04-04 18:26:43 +00:00
|
|
|
// 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
|
2019-04-02 17:40:25 +00:00
|
|
|
|
2019-04-01 10:12:29 +00:00
|
|
|
}
|
|
|
|
|
2019-04-04 18:26:43 +00:00
|
|
|
*t = timestamp(val)
|
2019-04-01 10:12:29 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-04-02 13:53:52 +00:00
|
|
|
func (t timestamp) MarshalJSON() ([]byte, error) {
|
|
|
|
str := strconv.Itoa(int(time.Time(t).Unix()))
|
|
|
|
return []byte(str), nil
|
|
|
|
}
|
|
|
|
|
2019-04-01 10:12:29 +00:00
|
|
|
func (t timestamp) String() string {
|
|
|
|
return time.Time(t).Format(time.RFC3339)
|
|
|
|
}
|
|
|
|
|
2019-03-29 18:50:11 +00:00
|
|
|
type Invoice struct {
|
2019-04-03 18:18:51 +00:00
|
|
|
AddIndex uint64 `json:"-"`
|
2019-04-03 22:12:03 +00:00
|
|
|
SettleIndex uint64 `json:"-"`
|
2019-03-31 20:53:53 +00:00
|
|
|
Description string `json:"description"`
|
2019-04-03 18:18:51 +00:00
|
|
|
Msatoshi float64 `json:"msatoshi"`
|
2019-03-31 20:53:53 +00:00
|
|
|
Payreq string `json:"payreq"`
|
|
|
|
RHash string `json:"rhash"`
|
2019-04-04 00:27:09 +00:00
|
|
|
PreImage string `json:"-"` // used as secret admin token
|
2019-03-31 20:53:53 +00:00
|
|
|
Status string `json:"status"`
|
2019-04-07 10:30:43 +00:00
|
|
|
Settled bool `json:"settled"`
|
2019-04-01 10:12:29 +00:00
|
|
|
PaidAt timestamp `json:"paid_at"`
|
|
|
|
CreatedAt timestamp `json:"created_at"`
|
|
|
|
ExpiresAt timestamp `json:"expires_at"`
|
2019-04-02 13:53:52 +00:00
|
|
|
Expired bool `json:"-"`
|
2019-04-07 11:54:41 +00:00
|
|
|
QuotedCurrency string `json:"-"`
|
|
|
|
QuotedAmount float64 `json:"-"`
|
2019-04-02 13:53:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (i Invoice) MarshalBinary() ([]byte, error) {
|
|
|
|
return json.Marshal(i)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i *Invoice) UnmarshalBinary(b []byte) error {
|
|
|
|
return json.Unmarshal(b, &i)
|
2019-03-29 18:50:11 +00:00
|
|
|
}
|
2019-04-03 18:18:51 +00:00
|
|
|
|
2019-04-03 22:12:03 +00:00
|
|
|
// Create new Invoice from lnrpc.Invoice
|
2019-04-03 18:18:51 +00:00
|
|
|
func InvoiceFromLndIn(lndInvoice *lnrpc.Invoice) *Invoice {
|
|
|
|
|
2019-04-07 12:19:04 +00:00
|
|
|
//log.Printf("new invoice form lnd with value %d", lndInvoice.Value)
|
|
|
|
//log.Printf("setting msat to %f", float64(lndInvoice.Value*1000))
|
2019-04-03 18:18:51 +00:00
|
|
|
invoice := Invoice{
|
|
|
|
AddIndex: lndInvoice.AddIndex,
|
|
|
|
Description: lndInvoice.GetMemo(),
|
|
|
|
Msatoshi: float64(lndInvoice.Value * 1000),
|
|
|
|
Payreq: lndInvoice.PaymentRequest,
|
|
|
|
RHash: hex.EncodeToString(lndInvoice.RHash),
|
2019-04-04 00:27:09 +00:00
|
|
|
PreImage: hex.EncodeToString(lndInvoice.RPreimage),
|
2019-04-03 18:18:51 +00:00
|
|
|
CreatedAt: timestamp(time.Unix(lndInvoice.CreationDate, 0)),
|
2019-04-09 07:20:46 +00:00
|
|
|
PaidAt: timestamp(time.Unix(lndInvoice.SettleDate, 0)),
|
2019-04-03 18:18:51 +00:00
|
|
|
Status: InvoiceStatus[UnPaid],
|
|
|
|
}
|
|
|
|
|
|
|
|
invoice.ExpiresAt = timestamp(time.Time(invoice.CreatedAt).Add(time.Second * time.Duration(lndInvoice.Expiry)))
|
|
|
|
|
|
|
|
// Calculate status
|
|
|
|
if lndInvoice.Settled {
|
2019-04-07 10:30:43 +00:00
|
|
|
invoice.Settled = true
|
2019-04-03 18:18:51 +00:00
|
|
|
invoice.Status = InvoiceStatus[Paid]
|
|
|
|
}
|
|
|
|
|
|
|
|
if time.Now().After(time.Time(invoice.ExpiresAt)) {
|
|
|
|
invoice.Status = InvoiceStatus[Expired]
|
|
|
|
invoice.Expired = true
|
|
|
|
}
|
|
|
|
|
|
|
|
return &invoice
|
|
|
|
}
|
2019-04-03 22:12:03 +00:00
|
|
|
|
|
|
|
// 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]
|
2019-04-07 10:30:43 +00:00
|
|
|
storedIn.Settled = true
|
2019-04-03 22:12:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Handle expired status
|
|
|
|
if time.Now().After(time.Time(storedIn.ExpiresAt)) {
|
|
|
|
storedIn.Status = InvoiceStatus[Expired]
|
|
|
|
storedIn.Expired = true
|
|
|
|
}
|
|
|
|
|
|
|
|
return storedIn
|
|
|
|
}
|
2019-04-07 20:31:35 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
}
|