diff --git a/fix_initialized_payments/main.go b/fix_initialized_payments/main.go new file mode 100644 index 0000000..dd642d7 --- /dev/null +++ b/fix_initialized_payments/main.go @@ -0,0 +1,65 @@ +package main + +import ( + "context" + "log" + + "github.com/getAlby/lndhub.go/db" + "github.com/getAlby/lndhub.go/lib" + "github.com/getAlby/lndhub.go/lib/service" + "github.com/getAlby/lndhub.go/lnd" + "github.com/kelseyhightower/envconfig" +) + +func main() { + //hardcode hash and user id for this fix + //TODO: first test on testnet, then run with real user id and hash: + //hash := "5cbde6f7ea043470c1b05d1b9fc2fbe50e5a86ad9782c8991ef33aca4496829b" + //userId := 4285 + hash := "" + userId := 0 + + ctx := context.Background() + c := &service.Config{} + + err := envconfig.Process("", c) + if err != nil { + log.Fatalf("Error loading environment variables: %v", err) + } + + // Setup logging to STDOUT or a configrued log file + logger := lib.Logger(c.LogFilePath) + + // Open a DB connection based on the configured DATABASE_URI + dbConn, err := db.Open(c.DatabaseUri) + if err != nil { + logger.Fatalf("Error initializing db connection: %v", err) + } + // Init new LND client + lndClient, err := lnd.NewLNDclient(lnd.LNDoptions{ + Address: c.LNDAddress, + MacaroonFile: c.LNDMacaroonFile, + MacaroonHex: c.LNDMacaroonHex, + CertFile: c.LNDCertFile, + CertHex: c.LNDCertHex, + }) + if err != nil { + logger.Fatalf("Error initializing the LND connection: %v", err) + } + svc := &service.LndhubService{ + Config: c, + DB: dbConn, + LndClient: lndClient, + Logger: logger, + InvoicePubSub: service.NewPubsub(), + } + invoice, err := svc.FindInvoiceByPaymentHash(ctx, int64(userId), hash) + if err != nil { + logger.Fatal(err) + } + //call svc.TrackPayment + err = svc.TrackOutgoingPaymentstatus(ctx, invoice) + if err != nil { + logger.Error(err) + } +} diff --git a/integration_tests/lnd_mock.go b/integration_tests/lnd_mock.go index 77327ac..a2a390c 100644 --- a/integration_tests/lnd_mock.go +++ b/integration_tests/lnd_mock.go @@ -216,6 +216,10 @@ func (mlnd *MockLND) GetInfo(ctx context.Context, req *lnrpc.GetInfoRequest, opt }, nil } +func (mlnd *MockLND) TrackPayment(ctx context.Context, hash string, options ...grpc.CallOption) (*lnrpc.Payment, error) { + return nil, nil +} + func (mlnd *MockLND) DecodeBolt11(ctx context.Context, bolt11 string, options ...grpc.CallOption) (*lnrpc.PayReq, error) { inv, err := zpay32.Decode(bolt11, &chaincfg.RegressionNetParams) if err != nil { diff --git a/integration_tests/subscription_start_test.go b/integration_tests/subscription_start_test.go index 2cde79f..3dd4f7d 100644 --- a/integration_tests/subscription_start_test.go +++ b/integration_tests/subscription_start_test.go @@ -136,3 +136,7 @@ func (mock *lndSubscriptionStartMockClient) GetInfo(ctx context.Context, req *ln func (mock *lndSubscriptionStartMockClient) DecodeBolt11(ctx context.Context, bolt11 string, options ...grpc.CallOption) (*lnrpc.PayReq, error) { panic("not implemented") // TODO: Implement } + +func (mlnd *lndSubscriptionStartMockClient) TrackPayment(ctx context.Context, hash string, options ...grpc.CallOption) (*lnrpc.Payment, error) { + return nil, nil +} diff --git a/lib/service/checkpayments.go b/lib/service/checkpayments.go new file mode 100644 index 0000000..a218231 --- /dev/null +++ b/lib/service/checkpayments.go @@ -0,0 +1,42 @@ +package service + +import ( + "context" + "fmt" + + "github.com/getAlby/lndhub.go/db/models" + "github.com/lightningnetwork/lnd/lnrpc" +) + +func (svc *LndhubService) TrackOutgoingPaymentstatus(ctx context.Context, invoice *models.Invoice) error { + + //fetch the tx entry for the invoice + entry := models.TransactionEntry{} + err := svc.DB.NewSelect().Model(&entry).Where("invoice_id = ?", invoice.ID).Limit(1).Scan(ctx) + if err != nil { + return err + } + if entry.UserID != invoice.UserID { + return fmt.Errorf("User ID's don't match : entry %v, invoice %v", entry, invoice) + } + //ask lnd using TrackPaymentV2 by hash of payment + payment, err := svc.LndClient.TrackPayment(ctx, invoice.RHash) + if err != nil { + return err + } + //call HandleFailedPayment or HandleSuccesfulPayment + if payment.Status == lnrpc.Payment_FAILED { + return svc.HandleFailedPayment(ctx, invoice, entry, fmt.Errorf(payment.FailureReason.String())) + } + if payment.Status == lnrpc.Payment_SUCCEEDED { + invoice.Fee = payment.FeeSat + invoice.Preimage = payment.PaymentPreimage + //is it needed to update the hash here? + return svc.HandleSuccessfulPayment(ctx, invoice, entry) + } + if payment.Status == lnrpc.Payment_IN_FLIGHT { + //todo, we need to keep calling Recv() in this case, in a seperate goroutine maybe? + return nil + } + return nil +} diff --git a/lnd/interface.go b/lnd/interface.go index 0c3733b..a8bfe13 100644 --- a/lnd/interface.go +++ b/lnd/interface.go @@ -14,6 +14,7 @@ type LightningClientWrapper interface { SubscribeInvoices(ctx context.Context, req *lnrpc.InvoiceSubscription, options ...grpc.CallOption) (SubscribeInvoicesWrapper, error) GetInfo(ctx context.Context, req *lnrpc.GetInfoRequest, options ...grpc.CallOption) (*lnrpc.GetInfoResponse, error) DecodeBolt11(ctx context.Context, bolt11 string, options ...grpc.CallOption) (*lnrpc.PayReq, error) + TrackPayment(ctx context.Context, hash string, options ...grpc.CallOption) (*lnrpc.Payment, error) } type SubscribeInvoicesWrapper interface { diff --git a/lnd/lnd.go b/lnd/lnd.go index bdfe887..0d01a1f 100644 --- a/lnd/lnd.go +++ b/lnd/lnd.go @@ -9,6 +9,7 @@ import ( "io/ioutil" "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightningnetwork/lnd/lnrpc/routerrpc" "github.com/lightningnetwork/lnd/macaroons" "google.golang.org/grpc" "google.golang.org/grpc/credentials" @@ -30,7 +31,8 @@ type LNDoptions struct { } type LNDWrapper struct { - client lnrpc.LightningClient + client lnrpc.LightningClient + routerClient routerrpc.RouterClient } func NewLNDclient(lndOptions LNDoptions) (result *LNDWrapper, err error) { @@ -92,7 +94,8 @@ func NewLNDclient(lndOptions LNDoptions) (result *LNDWrapper, err error) { } return &LNDWrapper{ - client: lnrpc.NewLightningClient(conn), + client: lnrpc.NewLightningClient(conn), + routerClient: routerrpc.NewRouterClient(conn), }, nil } @@ -117,7 +120,19 @@ func (wrapper *LNDWrapper) GetInfo(ctx context.Context, req *lnrpc.GetInfoReques } func (wrapper *LNDWrapper) DecodeBolt11(ctx context.Context, bolt11 string, options ...grpc.CallOption) (*lnrpc.PayReq, error) { + return wrapper.client.DecodePayReq(ctx, &lnrpc.PayReqString{ PayReq: bolt11, }) } + +func (wrapper *LNDWrapper) TrackPayment(ctx context.Context, hash string, options ...grpc.CallOption) (*lnrpc.Payment, error) { + client, err := wrapper.routerClient.TrackPaymentV2(ctx, &routerrpc.TrackPaymentRequest{ + PaymentHash: []byte(hash), + NoInflightUpdates: true, + }, options...) + if err != nil { + return nil, err + } + return client.Recv() +}