From e304dba4b7ff661bf8da446b3cc09fea875f8412 Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Fri, 15 Dec 2023 15:24:09 +0700 Subject: [PATCH] fix: map alby invoice to nip47 invoice --- .env.swp | Bin 4096 -> 0 bytes README.md | 7 +++- alby.go | 53 ++++++++++++++++++++++--- handle_list_transactions_request.go | 16 ++++---- models.go | 59 +++++++++++++++++++++------- 5 files changed, 106 insertions(+), 29 deletions(-) delete mode 100644 .env.swp diff --git a/.env.swp b/.env.swp deleted file mode 100644 index 3dd710f69022b6df33ac1427428c7f429742829d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4096 zcmYc?2=nw+u+%eP00IF9hKcioQ#;dG7{0SJFeE1CWfp_P2>?f*01rn3YCzKZDf;;Z zsd>7^`K3k4srq^O#U(|$<%u~tsU^C}`FVM%$tC)Fsd;5Y7&WS7Gz3ONfaDP1WiU1} v1ZQ<+B}D~cp-_@d7}YQu0;3@?8UmvsFd71*Aut*OqaiRF0;3@?bVC3DN3t4j diff --git a/README.md b/README.md index c1501031..da0d1243 100644 --- a/README.md +++ b/README.md @@ -123,6 +123,8 @@ You can also contribute to our [bounty program](https://github.com/getAlby/light ✅ NIP-47 info event +❌ `expires` tag in requests + ### LND ✅ `get_info` @@ -142,7 +144,7 @@ You can also contribute to our [bounty program](https://github.com/getAlby/light ✅ `list_transactions` - ⚠️ from and until in request not supported -❌ `multi_pay_invoice (TBC)` +❌ `multi_pay_invoice` ❌ `multi_pay_keysend (TBC)` @@ -171,7 +173,8 @@ You can also contribute to our [bounty program](https://github.com/getAlby/light ✅ `list_transactions` - ⚠️ offset and unpaid in request not supported +- ⚠️ fees_paid in response not supported -❌ `multi_pay_invoice (TBC)` +❌ `multi_pay_invoice` ❌ `multi_pay_keysend (TBC)` diff --git a/alby.go b/alby.go index 96024e18..8dc916fe 100644 --- a/alby.go +++ b/alby.go @@ -359,7 +359,7 @@ func (svc *AlbyOAuthService) GetBalance(ctx context.Context, senderPubkey string return 0, errors.New(errorPayload.Message) } -func (svc *AlbyOAuthService) ListTransactions(ctx context.Context, senderPubkey string, from, until, limit, offset uint64, unpaid bool, invoiceType string) (invoices []Nip47Transaction, err error) { +func (svc *AlbyOAuthService) ListTransactions(ctx context.Context, senderPubkey string, from, until, limit, offset uint64, unpaid bool, invoiceType string) (transactions []Nip47Transaction, err error) { app := App{} err = svc.db.Preload("User").First(&app, &App{ NostrPubkey: senderPubkey, @@ -377,7 +377,9 @@ func (svc *AlbyOAuthService) ListTransactions(ctx context.Context, senderPubkey client := svc.oauthConf.Client(ctx, tok) urlParams := url.Values{} - urlParams.Add("page", "1") + //urlParams.Add("page", "1") + + // TODO: clarify gt/lt vs from to in NWC spec if from != 0 { urlParams.Add("q[created_at_gt]", strconv.FormatUint(from, 10)) } @@ -398,7 +400,9 @@ func (svc *AlbyOAuthService) ListTransactions(ctx context.Context, senderPubkey endpoint += "/outgoing" } - req, err := http.NewRequest("GET", fmt.Sprintf("%s%s?%s", svc.cfg.AlbyAPIURL, endpoint, urlParams.Encode()), nil) + requestUrl := fmt.Sprintf("%s%s?%s", svc.cfg.AlbyAPIURL, endpoint, urlParams.Encode()) + + req, err := http.NewRequest("GET", requestUrl, nil) if err != nil { svc.Logger.WithError(err).Error("Error creating request /invoices") return nil, err @@ -412,21 +416,57 @@ func (svc *AlbyOAuthService) ListTransactions(ctx context.Context, senderPubkey "senderPubkey": senderPubkey, "appId": app.ID, "userId": app.User.ID, + "requestUrl": requestUrl, }).Errorf("Failed to fetch invoices: %v", err) return nil, err } + var invoices []AlbyInvoice + if resp.StatusCode < 300 { err = json.NewDecoder(resp.Body).Decode(&invoices) if err != nil { + svc.Logger.WithFields(logrus.Fields{ + "senderPubkey": senderPubkey, + "appId": app.ID, + "userId": app.User.ID, + "requestUrl": requestUrl, + }).Errorf("Failed to decode invoices: %v", err) return nil, err } + + transactions = []Nip47Transaction{} + for _, invoice := range invoices { + description := invoice.Comment + if description == "" { + description = invoice.Memo + } + + transaction := Nip47Transaction{ + Type: invoice.Type, + Invoice: invoice.PaymentRequest, + Description: description, + DescriptionHash: invoice.DescriptionHash, + Preimage: invoice.Preimage, + PaymentHash: invoice.PaymentHash, + Amount: invoice.Amount, + FeesPaid: 0, // TODO: support fees + CreatedAt: invoice.CreatedAt, + ExpiresAt: invoice.ExpiresAt, + SettledAt: invoice.SettledAt, + Metadata: invoice.Metadata, + } + + transactions = append(transactions, transaction) + } + svc.Logger.WithFields(logrus.Fields{ "senderPubkey": senderPubkey, "appId": app.ID, "userId": app.User.ID, - }).Info("Invoices listing successful") - return invoices, nil + "requestUrl": requestUrl, + }).Info("List transactions successful") + return transactions, nil } errorPayload := &ErrorResponse{} @@ -436,7 +476,8 @@ func (svc *AlbyOAuthService) ListTransactions(ctx context.Context, senderPubkey "appId": app.ID, "userId": app.User.ID, "APIHttpStatus": resp.StatusCode, - }).Errorf("Invoices listing failed %s", string(errorPayload.Message)) + "requestUrl": requestUrl, + }).Errorf("List transactions failed %s", string(errorPayload.Message)) return nil, errors.New(errorPayload.Message) } diff --git a/handle_list_transactions_request.go b/handle_list_transactions_request.go index d4f50fb0..46bd3a1c 100644 --- a/handle_list_transactions_request.go +++ b/handle_list_transactions_request.go @@ -37,6 +37,7 @@ func (svc *Service) HandleListTransactionsEvent(ctx context.Context, request *Ni if !hasPermission { svc.Logger.WithFields(logrus.Fields{ + // TODO: log request fields from listParams "eventId": event.ID, "eventKind": event.Kind, "appId": app.ID, @@ -51,34 +52,35 @@ func (svc *Service) HandleListTransactionsEvent(ctx context.Context, request *Ni } svc.Logger.WithFields(logrus.Fields{ + // TODO: log request fields from listParams "eventId": event.ID, "eventKind": event.Kind, "appId": app.ID, - }).Info("Fetching invoices") + }).Info("Fetching transactions") - invoices, err := svc.lnClient.ListTransactions(ctx, event.PubKey, listParams.From, listParams.Until, listParams.Limit, listParams.Offset, listParams.Unpaid, listParams.Type) + transactions, err := svc.lnClient.ListTransactions(ctx, event.PubKey, listParams.From, listParams.Until, listParams.Limit, listParams.Offset, listParams.Unpaid, listParams.Type) if err != nil { svc.Logger.WithFields(logrus.Fields{ + // TODO: log request fields from listParams "eventId": event.ID, "eventKind": event.Kind, "appId": app.ID, - }).Infof("Failed to fetch invoices: %v", err) + }).Infof("Failed to fetch transactions: %v", err) nostrEvent.State = NOSTR_EVENT_STATE_HANDLER_ERROR svc.db.Save(&nostrEvent) return svc.createResponse(event, Nip47Response{ ResultType: request.Method, Error: &Nip47Error{ Code: NIP_47_ERROR_INTERNAL, - Message: fmt.Sprintf("Something went wrong while fetching invoices: %s", err.Error()), + Message: fmt.Sprintf("Something went wrong while fetching transactions: %s", err.Error()), }, }, ss) } - // TODO: Nip47ListListTransactionsResponse responsePayload := &Nip47ListTransactionsResponse{ - Transactions: invoices, + Transactions: transactions, } - fmt.Println(responsePayload) + // fmt.Println(responsePayload) nostrEvent.State = NOSTR_EVENT_STATE_HANDLER_EXECUTED svc.db.Save(&nostrEvent) diff --git a/models.go b/models.go index 014ae931..7b0ebf52 100644 --- a/models.go +++ b/models.go @@ -127,18 +127,47 @@ type Payment struct { // TODO: move to models/Nip47 type Nip47Transaction struct { - Type string `json:"type"` - Invoice string `json:"invoice"` - Description string `json:"description"` - DescriptionHash string `json:"description_hash"` - Preimage string `json:"preimage"` - PaymentHash string `json:"payment_hash"` - Amount int64 `json:"amount"` - FeesPaid int64 `json:"fees_paid"` - CreatedAt time.Time `json:"created_at"` - ExpiresAt time.Time `json:"expires_at"` - SettledAt time.Time `json:"settled_at"` - Metadata map[string]interface{} `json:"metadata,omitempty"` + Type string `json:"type"` + Invoice string `json:"invoice"` + Description string `json:"description"` + DescriptionHash string `json:"description_hash"` + Preimage string `json:"preimage"` + PaymentHash string `json:"payment_hash"` + Amount int64 `json:"amount"` + FeesPaid int64 `json:"fees_paid"` + CreatedAt time.Time `json:"created_at"` + ExpiresAt time.Time `json:"expires_at"` + SettledAt time.Time `json:"settled_at"` + Metadata interface{} `json:"metadata,omitempty"` +} + +// TODO: move to models/Alby +type AlbyInvoice struct { + Amount int64 `json:"amount"` + // Boostagram AlbyInvoiceBoostagram `json:"boostagram"` + Comment string `json:"comment"` + CreatedAt time.Time `json:"created_at"` + // CreationDate uint64 `json:"creation_date"` + Currency string `json:"currency"` + // custom_records + DescriptionHash string `json:"description_hash"` + ExpiresAt time.Time `json:"expires_at"` + Expiry uint32 `json:"expiry"` + // Identifier string + KeysendMessage string `json:"keysend_message"` + Memo string `json:"memo"` + Metadata interface{} `json:"metadata"` + PayerName string `json:"payer_name"` + PayerPubkey string `json:"payer_pubkey"` + PaymentHash string `json:"payment_hash"` + PaymentRequest string `json:"payment_request"` + Preimage string `json:"preimage"` + // r_hash_str + Settled bool `json:"settled"` + SettledAt time.Time `json:"settled_at"` + State string `json:"state"` + Type string `json:"type"` + // value } type PayRequest struct { @@ -169,14 +198,16 @@ type MakeInvoiceRequest struct { DescriptionHash string `json:"description_hash"` } -// TODO: this should have the same content as Nip46Transaction +// TODO: this should have the same content as Nip47Transaction type MakeInvoiceResponse struct { + // Nip47Transaction PaymentRequest string `json:"payment_request"` PaymentHash string `json:"payment_hash"` } -// TODO: this should have the same content as Nip46Transaction +// TODO: this should have the same content as Nip47Transaction type LookupInvoiceResponse struct { + // Nip47Transaction PaymentRequest string `json:"payment_request"` Settled bool `json:"settled"` }