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/watchers/invoices.go

198 lines
4.7 KiB
Go

package watchers
import (
"encoding/hex"
"fmt"
"io"
"log"
"regexp"
"git.sp4ke.com/sp4ke/bit4sat/bus"
"git.sp4ke.com/sp4ke/bit4sat/db"
"git.sp4ke.com/sp4ke/bit4sat/ln"
"git.sp4ke.com/sp4ke/bit4sat/lndrpc"
"git.sp4ke.com/sp4ke/bit4sat/storage"
lnrpc "github.com/lightningnetwork/lnd/lnrpc"
"github.com/mediocregopher/radix/v3"
"golang.org/x/net/context"
)
const (
LastSettledInvoiceIndexKey = "last_invoice_settled_index_cursor"
ReUploadInvoice = `bit4sat upload`
ReDownInvoice = `bit4sat download`
)
// Last watched invoice
func getLastSettledInvoiceIndex() uint64 {
var cursor uint64
err := db.DB.Redis.Do(radix.FlatCmd(&cursor, "GET", LastSettledInvoiceIndexKey))
if err != nil {
return uint64(0)
}
return cursor
}
func setLastSettledInvoiceIndex(index uint64) {
err := db.DB.Redis.Do(radix.FlatCmd(nil, "SET", LastSettledInvoiceIndexKey, index))
if err != nil {
panic(err)
}
}
// Runs in a goroutine and keep swatching invoices
func WatchInvoice() {
// Get the last invoice index cursor
cursor := getLastSettledInvoiceIndex()
log.Printf("watching settled from index %d", cursor)
ctxb := context.Background()
client, cleanUp := lndrpc.GetClient()
defer cleanUp()
subscription := &lnrpc.InvoiceSubscription{
SettleIndex: cursor,
}
invoiceStream, err := client.SubscribeInvoices(ctxb, subscription)
if err != nil {
//return nil, err
}
for {
invoice, err := invoiceStream.Recv()
if err == io.EOF {
break
}
if err != nil {
log.Printf("error invoice stream %s", err)
break
}
log.Printf("watcher, received invoice \n%s\n", invoice)
// We are only interested in settled invoices
if !invoice.Settled {
continue
}
// Invoice was paid, we handle it
handleSettledInvoice(invoice)
}
}
func handleSettledInvoice(invoice *lnrpc.Invoice) {
// we need to save it and also notify the pubsub channel
// Save last settled index
setLastSettledInvoiceIndex(invoice.SettleIndex)
matchUp, err := regexp.MatchString(ReUploadInvoice, invoice.Memo)
if err != nil {
log.Printf("Error regex match: %s", err)
}
matchDown, err := regexp.MatchString(ReDownInvoice, invoice.Memo)
if err != nil {
log.Printf("Error regex match: %s", err)
}
// Handle upload related invoices
if matchUp {
// Update upload status to "paid"
uploadId, err := storage.GetUploadIdForInvoice(hex.EncodeToString(invoice.RHash))
log.Printf("watcher: found upload id %s for invoice %s", uploadId,
hex.EncodeToString(invoice.RHash))
if err != nil {
log.Printf("error handleSettledInvoice: %s", err)
return
}
err = storage.SetUploadStatus(uploadId, storage.UpPaid)
if err != nil {
log.Printf("error handleSettledInvoice: %s", err)
return
}
// Get stored invoice for upload
storedInvoice, err := storage.GetUploadInvoice(uploadId)
if err != nil {
log.Printf("error handleSettledInvoice: %s", err)
return
}
//log.Printf("stored invoice %#v", storedInvoice)
// Update stored invoice fields
newInvoice := ln.UpdateInvoiceFromLnd(storedInvoice, invoice)
//log.Printf("new invoice %#v", newInvoice)
// Set invoice for upload
err = storage.SetUploadInvoice(uploadId, newInvoice)
if err != nil {
log.Printf("error handleSettledInvoice: %s", err)
return
}
log.Printf("Notifying invoice paid for upload %s", uploadId)
// publish invoice was updated to upload_id_paid channel
key := fmt.Sprintf("%s:%s", bus.InvoicePaidChannelPrefix,
newInvoice.RHash)
err = db.DB.Redis.Do(radix.FlatCmd(nil, "PUBLISH",
key, newInvoice))
}
// This is a download invoice
if matchDown {
// Store the full invoice
key := fmt.Sprintf("invoice_%s", hex.EncodeToString(invoice.RHash))
storedInvoice := ln.InvoiceFromLndIn(invoice)
err = db.DB.Redis.Do(radix.FlatCmd(nil, "SET", key, storedInvoice))
if err != nil {
log.Printf("error handleSettledInvoice: %s", err)
return
}
// Get upload_id related to this invoice
key = fmt.Sprintf("invoice_id_%s_upload_id", storedInvoice.RHash)
//log.Printf("looking for %s", key)
var uploadId string
err := db.DB.Redis.Do(radix.FlatCmd(&uploadId, "GET", key))
if err != nil {
log.Printf("error handleSettledInvoice: %s", err)
return
}
// Add payment to the corresponding upload_id (user account)
//log.Printf("updating payment for upload %s", uploadId)
err = storage.AddPaymentToUpload(uploadId, storedInvoice.RHash,
storedInvoice.Msatoshi)
if err != nil {
log.Printf("error handleSettledInvoice: %s", err)
return
}
log.Printf("Notifying invoice paid for download")
key = fmt.Sprintf("%s:%s", bus.InvoicePaidChannelPrefix,
storedInvoice.RHash)
err = db.DB.Redis.Do(radix.FlatCmd(nil, "PUBLISH",
key, storedInvoice))
}
if err != nil {
log.Printf("error handleSettledInvoice: %s", err)
return
}
}