Skip to content

Commit

Permalink
fix: map alby invoice to nip47 invoice
Browse files Browse the repository at this point in the history
  • Loading branch information
rolznz committed Dec 15, 2023
1 parent ee4a87e commit e304dba
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 29 deletions.
Binary file removed .env.swp
Binary file not shown.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand All @@ -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)`

Expand Down Expand Up @@ -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)`
53 changes: 47 additions & 6 deletions alby.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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))
}
Expand All @@ -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
Expand All @@ -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{}
Expand All @@ -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)
}

Expand Down
16 changes: 9 additions & 7 deletions handle_list_transactions_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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)
Expand Down
59 changes: 45 additions & 14 deletions models.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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"`
}
Expand Down

0 comments on commit e304dba

Please sign in to comment.