diff --git a/events/models.go b/events/models.go index 68d53fd3..62b0a582 100644 --- a/events/models.go +++ b/events/models.go @@ -18,10 +18,6 @@ type Event struct { Properties interface{} `json:"properties,omitempty"` } -type PaymentReceivedEventProperties struct { - PaymentHash string `json:"payment_hash"` -} - type PaymentSentEventProperties struct { PaymentHash string `json:"payment_hash"` Duration uint64 `json:"duration"` diff --git a/lnclient/ldk/ldk.go b/lnclient/ldk/ldk.go index 3797daf3..2fb663e3 100644 --- a/lnclient/ldk/ldk.go +++ b/lnclient/ldk/ldk.go @@ -1256,13 +1256,27 @@ func (ls *LDKService) handleLdkEvent(event *ldk_node.Event) { }, }) case ldk_node.EventPaymentReceived: - // TODO: trigger transaction update (reuse below event?) + if eventType.PaymentId == nil { + logger.Logger.WithField("payment_hash", eventType.PaymentHash).Error("payment received event has no payment ID") + return + } + payment := ls.node.Payment(*eventType.PaymentId) + if payment == nil { + logger.Logger.WithField("payment_id", *eventType.PaymentId).Error("could not find LDK payment") + return + } + + transaction, err := ls.ldkPaymentToTransaction(payment) + if err != nil { + logger.Logger.WithField("payment_id", *eventType.PaymentId).Error("failed to convert LDK payment to transaction") + return + } + ls.eventPublisher.Publish(&events.Event{ - Event: "nwc_payment_received", - Properties: &events.PaymentReceivedEventProperties{ - PaymentHash: eventType.PaymentHash, - }, + Event: "nwc_payment_received", + Properties: transaction, }) + case ldk_node.EventPaymentSuccessful: // TODO: trigger transaction update // TODO: trigger transaction update for payment failed diff --git a/nip47/notifications/nip47_notifier.go b/nip47/notifications/nip47_notifier.go index dc51cdc3..24935295 100644 --- a/nip47/notifications/nip47_notifier.go +++ b/nip47/notifications/nip47_notifier.go @@ -49,18 +49,18 @@ func NewNip47Notifier(relay Relay, db *gorm.DB, cfg config.Config, keys keys.Key func (notifier *Nip47Notifier) ConsumeEvent(ctx context.Context, event *events.Event) error { switch event.Event { case "nwc_payment_received": - paymentReceivedEventProperties, ok := event.Properties.(*events.PaymentReceivedEventProperties) + lnClientTransaction, ok := event.Properties.(*lnclient.Transaction) if !ok { logger.Logger.WithField("event", event).Error("Failed to cast event") return errors.New("failed to cast event") } - transaction, err := notifier.transactionsService.LookupTransaction(ctx, paymentReceivedEventProperties.PaymentHash, notifier.lnClient, nil) + transaction, err := notifier.transactionsService.LookupTransaction(ctx, lnClientTransaction.PaymentHash, notifier.lnClient, nil) if err != nil { logger.Logger. - WithField("paymentHash", paymentReceivedEventProperties.PaymentHash). + WithField("paymentHash", lnClientTransaction.PaymentHash). WithError(err). - Error("Failed to lookup invoice by payment hash") + Error("Failed to lookup transaction by payment hash") return err } diff --git a/service/service.go b/service/service.go index 2e9b6a73..466125ad 100644 --- a/service/service.go +++ b/service/service.go @@ -110,6 +110,7 @@ func NewService(ctx context.Context) (*service, error) { } eventPublisher.RegisterSubscriber(svc.albyOAuthSvc) + eventPublisher.RegisterSubscriber(svc.transactionsService) eventPublisher.Publish(&events.Event{ Event: "nwc_started", diff --git a/transactions/transactions_service.go b/transactions/transactions_service.go index ef47fdbe..50929fce 100644 --- a/transactions/transactions_service.go +++ b/transactions/transactions_service.go @@ -8,6 +8,7 @@ import ( "time" "github.com/getAlby/hub/db" + "github.com/getAlby/hub/events" "github.com/getAlby/hub/lnclient" "github.com/getAlby/hub/logger" decodepay "github.com/nbd-wtf/ln-decodepay" @@ -20,6 +21,7 @@ type transactionsService struct { } type TransactionsService interface { + events.EventSubscriber MakeInvoice(ctx context.Context, amount int64, description string, descriptionHash string, expiry int64, lnClient lnclient.LNClient, appId *uint, requestEventId *uint) (*Transaction, error) LookupTransaction(ctx context.Context, paymentHash string, lnClient lnclient.LNClient, appId *uint) (*Transaction, error) ListTransactions(ctx context.Context, from, until, limit, offset uint64, unpaid bool, invoiceType string, lnClient lnclient.LNClient) (transactions []Transaction, err error) @@ -266,11 +268,7 @@ func (svc *transactionsService) SendKeysend(ctx context.Context, amount uint64, func (svc *transactionsService) LookupTransaction(ctx context.Context, paymentHash string, lnClient lnclient.LNClient, appId *uint) (*Transaction, error) { transaction := db.Transaction{} - // FIXME: this is not unique - // TODO: check if passing AppId: null works for the "Global" view - // - wallet page - // - notifications - // - etc. + // FIXME: this is currently not unique result := svc.db.Find(&transaction, &db.Transaction{ //Type: transactionType, PaymentHash: paymentHash, @@ -354,3 +352,57 @@ func (svc *transactionsService) checkUnsettledTransaction(ctx context.Context, t } } } + +func (svc *transactionsService) ConsumeEvent(ctx context.Context, event *events.Event, globalProperties map[string]interface{}) error { + switch event.Event { + case "nwc_payment_received": + lnClientTransaction, ok := event.Properties.(*lnclient.Transaction) + if !ok { + logger.Logger.WithField("event", event).Error("Failed to cast event") + return errors.New("failed to cast event") + } + + // TODO: copied from makeinvoice + var metadata string + if lnClientTransaction.Metadata != nil { + metadataBytes, err := json.Marshal(lnClientTransaction.Metadata) + if err != nil { + logger.Logger.WithError(err).Error("Failed to serialize transaction metadata") + return err + } + metadata = string(metadataBytes) + } + + // TODO: update the transaction if it already exists! + // Note: brand new payments (keysend) cannot be associated with an app + + settledAt := time.Now() + dbTransaction := &db.Transaction{ + Type: lnClientTransaction.Type, + State: TRANSACTION_STATE_SETTLED, + Amount: uint64(lnClientTransaction.Amount), + PaymentRequest: lnClientTransaction.Invoice, + PaymentHash: lnClientTransaction.PaymentHash, + Description: lnClientTransaction.Description, + DescriptionHash: lnClientTransaction.DescriptionHash, + // TODO: add missing fields + // ExpiresAt: lnClientTransaction.ExpiresAt, + // Fee: lnClientTransaction.FeesPaid, + // Preimage: , + SettledAt: &settledAt, + Metadata: metadata, + } + + err := svc.db.Create(dbTransaction).Error + if err != nil { + logger.Logger.WithFields(logrus.Fields{ + "payment_hash": lnClientTransaction.PaymentHash, + }).WithError(err).Error("Failed to create DB transaction") + return err + } + + // TODO: support nwc_payment_sent and nwc_payment_failed () + } + + return nil +}