mirror of
https://github.com/getAlby/lndhub.go.git
synced 2025-12-24 08:05:02 +01:00
@@ -7,3 +7,4 @@ JWT_REFRESH_EXPIRY=604800
|
||||
LND_ADDRESS=
|
||||
LND_MACAROON_HEX=
|
||||
LND_CERT_HEX=
|
||||
FIXED_FEE=10
|
||||
@@ -34,6 +34,7 @@ func init() {
|
||||
DECLARE
|
||||
sum BIGINT;
|
||||
debit_account_type VARCHAR;
|
||||
credit_account_type VARCHAR;
|
||||
BEGIN
|
||||
|
||||
-- LOCK the account if the transaction is not from an incoming account
|
||||
@@ -48,8 +49,18 @@ func init() {
|
||||
-- This can happen when two transactions try to access the same account
|
||||
FOR UPDATE NOWAIT;
|
||||
|
||||
-- If it is an incoming account return; otherwise check the balance
|
||||
IF debit_account_type IS NULL
|
||||
-- check if credit_account type is fees, if it's fees we don't check for negative balance constraint
|
||||
SELECT INTO credit_account_type type
|
||||
FROM accounts
|
||||
WHERE id = NEW.credit_account_id AND type <> 'fees'
|
||||
-- IMPORTANT: lock rows but do not wait for another lock to be released.
|
||||
-- Waiting would result in a deadlock because two parallel transactions could try to lock the same rows
|
||||
-- NOWAIT reports an error rather than waiting for the lock to be released
|
||||
-- This can happen when two transactions try to access the same account
|
||||
FOR UPDATE NOWAIT;
|
||||
|
||||
-- If it is an debit incoming account or fees credit account return; otherwise check the balance
|
||||
IF debit_account_type IS NULL OR credit_account_type IS NULL
|
||||
THEN
|
||||
RETURN NEW;
|
||||
END IF;
|
||||
@@ -60,7 +71,7 @@ func init() {
|
||||
WHERE account_ledgers.account_id = NEW.debit_account_id;
|
||||
|
||||
-- IF the account would go negative raise an exception
|
||||
IF sum < 0 AND debit_account_type != 'incoming'
|
||||
IF sum < 0
|
||||
THEN
|
||||
RAISE EXCEPTION 'invalid balance [user_id:%] [debit_account_id:%] balance [%]',
|
||||
NEW.user_id,
|
||||
|
||||
1
db/migrations/20220301100000_invoice_add_fee.up.sql
Normal file
1
db/migrations/20220301100000_invoice_add_fee.up.sql
Normal file
@@ -0,0 +1 @@
|
||||
alter table invoices ADD COLUMN fee bigint;
|
||||
@@ -14,6 +14,7 @@ type Invoice struct {
|
||||
UserID int64 `json:"user_id" validate:"required"`
|
||||
User *User `bun:"rel:belongs-to,join:user_id=id"`
|
||||
Amount int64 `json:"amount" validate:"gte=0"`
|
||||
Fee int64 `json:"fee" bun:",nullzero"`
|
||||
Memo string `json:"memo" bun:",nullzero"`
|
||||
DescriptionHash string `json:"description_hash" bun:",nullzero"`
|
||||
PaymentRequest string `json:"payment_request" bun:",nullzero"`
|
||||
|
||||
@@ -34,8 +34,8 @@ type PaymentTestSuite struct {
|
||||
|
||||
func (suite *PaymentTestSuite) SetupSuite() {
|
||||
lndClient, err := lnd.NewLNDclient(lnd.LNDoptions{
|
||||
Address: lnd2RegtestAddress,
|
||||
MacaroonHex: lnd2RegtestMacaroonHex,
|
||||
Address: lnd3RegtestAddress,
|
||||
MacaroonHex: lnd3RegtestMacaroonHex,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("Error setting up funding client: %v", err)
|
||||
@@ -85,6 +85,8 @@ func (suite *PaymentTestSuite) TearDownTest() {
|
||||
func (suite *PaymentTestSuite) TestInternalPayment() {
|
||||
aliceFundingSats := 1000
|
||||
bobSatRequested := 500
|
||||
// currently fee is 0 for internal payments
|
||||
fee := 0
|
||||
//fund alice account
|
||||
invoiceResponse := suite.createAddInvoiceReq(aliceFundingSats, "integration test internal payment alice", suite.aliceToken)
|
||||
sendPaymentRequest := lnrpc.SendRequest{
|
||||
@@ -102,17 +104,38 @@ func (suite *PaymentTestSuite) TestInternalPayment() {
|
||||
//pay bob from alice
|
||||
payResponse := suite.createPayInvoiceReq(bobInvoice.PayReq, suite.aliceToken)
|
||||
assert.NotEmpty(suite.T(), payResponse.PaymentPreimage)
|
||||
|
||||
aliceId := getUserIdFromToken(suite.aliceToken)
|
||||
bobId := getUserIdFromToken(suite.bobToken)
|
||||
|
||||
//try to pay Bob more than we currently have
|
||||
//create invoice for bob
|
||||
tooMuch := suite.createAddInvoiceReq(10000, "integration test internal payment bob", suite.bobToken)
|
||||
//pay bob from alice
|
||||
errorResp := suite.createPayInvoiceReqError(tooMuch.PayReq, suite.aliceToken)
|
||||
assert.Equal(suite.T(), responses.NotEnoughBalanceError.Code, errorResp.Code)
|
||||
|
||||
transactonEntriesAlice, _ := suite.service.TransactionEntriesFor(context.Background(), aliceId)
|
||||
aliceBalance, _ := suite.service.CurrentUserBalance(context.Background(), aliceId)
|
||||
assert.Equal(suite.T(), 3, len(transactonEntriesAlice))
|
||||
assert.Equal(suite.T(), int64(aliceFundingSats), transactonEntriesAlice[0].Amount)
|
||||
assert.Equal(suite.T(), int64(bobSatRequested), transactonEntriesAlice[1].Amount)
|
||||
assert.Equal(suite.T(), int64(fee), transactonEntriesAlice[2].Amount)
|
||||
assert.Equal(suite.T(), transactonEntriesAlice[1].ID, transactonEntriesAlice[2].ParentID)
|
||||
assert.Equal(suite.T(), int64(aliceFundingSats-bobSatRequested-fee), aliceBalance)
|
||||
|
||||
bobBalance, _ := suite.service.CurrentUserBalance(context.Background(), bobId)
|
||||
transactionEntriesBob, _ := suite.service.TransactionEntriesFor(context.Background(), bobId)
|
||||
assert.Equal(suite.T(), 1, len(transactionEntriesBob))
|
||||
assert.Equal(suite.T(), int64(bobSatRequested), transactionEntriesBob[0].Amount)
|
||||
assert.Equal(suite.T(), int64(bobSatRequested), bobBalance)
|
||||
}
|
||||
|
||||
func (suite *PaymentTestSuite) TestInternalPaymentFail() {
|
||||
aliceFundingSats := 1000
|
||||
bobSatRequested := 500
|
||||
// currently fee is 0 for internal payments
|
||||
fee := 0
|
||||
//fund alice account
|
||||
invoiceResponse := suite.createAddInvoiceReq(aliceFundingSats, "integration test internal payment alice", suite.aliceToken)
|
||||
sendPaymentRequest := lnrpc.SendRequest{
|
||||
@@ -153,14 +176,17 @@ func (suite *PaymentTestSuite) TestInternalPaymentFail() {
|
||||
fmt.Printf("Error when getting balance %v\n", err.Error())
|
||||
}
|
||||
|
||||
// check if there are 4 transaction entries, with reversed credit and debit account ids for last 2
|
||||
assert.Equal(suite.T(), 4, len(transactonEntries))
|
||||
assert.Equal(suite.T(), transactonEntries[2].CreditAccountID, transactonEntries[3].DebitAccountID)
|
||||
assert.Equal(suite.T(), transactonEntries[2].DebitAccountID, transactonEntries[3].CreditAccountID)
|
||||
assert.Equal(suite.T(), transactonEntries[2].Amount, int64(bobSatRequested))
|
||||
// check if there are 5 transaction entries, with reversed credit and debit account ids for last 2
|
||||
assert.Equal(suite.T(), 5, len(transactonEntries))
|
||||
assert.Equal(suite.T(), int64(aliceFundingSats), transactonEntries[0].Amount)
|
||||
assert.Equal(suite.T(), int64(bobSatRequested), transactonEntries[1].Amount)
|
||||
assert.Equal(suite.T(), int64(fee), transactonEntries[2].Amount)
|
||||
assert.Equal(suite.T(), transactonEntries[3].CreditAccountID, transactonEntries[4].DebitAccountID)
|
||||
assert.Equal(suite.T(), transactonEntries[3].DebitAccountID, transactonEntries[4].CreditAccountID)
|
||||
assert.Equal(suite.T(), transactonEntries[3].Amount, int64(bobSatRequested))
|
||||
assert.Equal(suite.T(), transactonEntries[4].Amount, int64(bobSatRequested))
|
||||
// assert that balance was reduced only once
|
||||
assert.Equal(suite.T(), int64(aliceFundingSats)-int64(bobSatRequested), int64(aliceBalance))
|
||||
assert.Equal(suite.T(), int64(aliceFundingSats)-int64(bobSatRequested+fee), int64(aliceBalance))
|
||||
}
|
||||
|
||||
func TestInternalPaymentTestSuite(t *testing.T) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/getAlby/lndhub.go/common"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@@ -12,6 +13,8 @@ import (
|
||||
func (suite *PaymentTestSuite) TestOutGoingPayment() {
|
||||
aliceFundingSats := 1000
|
||||
externalSatRequested := 500
|
||||
// 1 sat + 1 ppm
|
||||
fee := 1
|
||||
//fund alice account
|
||||
invoiceResponse := suite.createAddInvoiceReq(aliceFundingSats, "integration test external payment alice", suite.aliceToken)
|
||||
sendPaymentRequest := lnrpc.SendRequest{
|
||||
@@ -41,12 +44,121 @@ func (suite *PaymentTestSuite) TestOutGoingPayment() {
|
||||
if err != nil {
|
||||
fmt.Printf("Error when getting balance %v\n", err.Error())
|
||||
}
|
||||
assert.Equal(suite.T(), int64(aliceFundingSats)-int64(externalSatRequested), aliceBalance)
|
||||
assert.Equal(suite.T(), int64(aliceFundingSats)-int64(externalSatRequested+fee), aliceBalance)
|
||||
|
||||
// check that no additional transaction entry was created
|
||||
transactonEntries, err := suite.service.TransactionEntriesFor(context.Background(), userId)
|
||||
if err != nil {
|
||||
fmt.Printf("Error when getting transaction entries %v\n", err.Error())
|
||||
}
|
||||
assert.Equal(suite.T(), 2, len(transactonEntries))
|
||||
// verify transaction entries data
|
||||
feeAccount, _ := suite.service.AccountFor(context.Background(), common.AccountTypeFees, userId)
|
||||
incomingAccount, _ := suite.service.AccountFor(context.Background(), common.AccountTypeIncoming, userId)
|
||||
outgoingAccount, _ := suite.service.AccountFor(context.Background(), common.AccountTypeOutgoing, userId)
|
||||
currentAccount, _ := suite.service.AccountFor(context.Background(), common.AccountTypeCurrent, userId)
|
||||
|
||||
outgoingInvoices, _ := suite.service.InvoicesFor(context.Background(), userId, common.InvoiceTypeOutgoing)
|
||||
incomingInvoices, _ := suite.service.InvoicesFor(context.Background(), userId, common.InvoiceTypeIncoming)
|
||||
assert.Equal(suite.T(), 1, len(outgoingInvoices))
|
||||
assert.Equal(suite.T(), 1, len(incomingInvoices))
|
||||
|
||||
assert.Equal(suite.T(), 3, len(transactonEntries))
|
||||
|
||||
assert.Equal(suite.T(), int64(aliceFundingSats), transactonEntries[0].Amount)
|
||||
assert.Equal(suite.T(), currentAccount.ID, transactonEntries[0].CreditAccountID)
|
||||
assert.Equal(suite.T(), incomingAccount.ID, transactonEntries[0].DebitAccountID)
|
||||
assert.Equal(suite.T(), int64(0), transactonEntries[0].ParentID)
|
||||
assert.Equal(suite.T(), incomingInvoices[0].ID, transactonEntries[0].InvoiceID)
|
||||
|
||||
assert.Equal(suite.T(), int64(externalSatRequested), transactonEntries[1].Amount)
|
||||
assert.Equal(suite.T(), outgoingAccount.ID, transactonEntries[1].CreditAccountID)
|
||||
assert.Equal(suite.T(), currentAccount.ID, transactonEntries[1].DebitAccountID)
|
||||
assert.Equal(suite.T(), int64(0), transactonEntries[1].ParentID)
|
||||
assert.Equal(suite.T(), outgoingInvoices[0].ID, transactonEntries[1].InvoiceID)
|
||||
|
||||
assert.Equal(suite.T(), int64(fee), transactonEntries[2].Amount)
|
||||
assert.Equal(suite.T(), feeAccount.ID, transactonEntries[2].CreditAccountID)
|
||||
assert.Equal(suite.T(), currentAccount.ID, transactonEntries[2].DebitAccountID)
|
||||
assert.Equal(suite.T(), outgoingInvoices[0].ID, transactonEntries[2].InvoiceID)
|
||||
|
||||
// make sure fee entry parent id is previous entry
|
||||
assert.Equal(suite.T(), transactonEntries[1].ID, transactonEntries[2].ParentID)
|
||||
}
|
||||
|
||||
func (suite *PaymentTestSuite) TestOutGoingPaymentWithNegativeBalance() {
|
||||
// this will cause balance to go to -1
|
||||
aliceFundingSats := 1000
|
||||
externalSatRequested := 1000
|
||||
// 1 sat + 1 ppm
|
||||
fee := 1
|
||||
//fund alice account
|
||||
invoiceResponse := suite.createAddInvoiceReq(aliceFundingSats, "integration test external payment alice", suite.aliceToken)
|
||||
sendPaymentRequest := lnrpc.SendRequest{
|
||||
PaymentRequest: invoiceResponse.PayReq,
|
||||
FeeLimit: nil,
|
||||
}
|
||||
_, err := suite.fundingClient.SendPaymentSync(context.Background(), &sendPaymentRequest)
|
||||
assert.NoError(suite.T(), err)
|
||||
|
||||
//wait a bit for the callback event to hit
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
//create external invoice
|
||||
externalInvoice := lnrpc.Invoice{
|
||||
Memo: "integration tests: external pay from alice",
|
||||
Value: int64(externalSatRequested),
|
||||
}
|
||||
invoice, err := suite.fundingClient.AddInvoice(context.Background(), &externalInvoice)
|
||||
assert.NoError(suite.T(), err)
|
||||
//pay external from alice
|
||||
payResponse := suite.createPayInvoiceReq(invoice.PaymentRequest, suite.aliceToken)
|
||||
assert.NotEmpty(suite.T(), payResponse.PaymentPreimage)
|
||||
|
||||
// check that balance was reduced
|
||||
userId := getUserIdFromToken(suite.aliceToken)
|
||||
|
||||
aliceBalance, err := suite.service.CurrentUserBalance(context.Background(), userId)
|
||||
if err != nil {
|
||||
fmt.Printf("Error when getting balance %v\n", err.Error())
|
||||
}
|
||||
assert.Equal(suite.T(), int64(aliceFundingSats)-int64(externalSatRequested+fee), aliceBalance)
|
||||
assert.Equal(suite.T(), int64(-1), aliceBalance)
|
||||
|
||||
// check that no additional transaction entry was created
|
||||
transactonEntries, err := suite.service.TransactionEntriesFor(context.Background(), userId)
|
||||
if err != nil {
|
||||
fmt.Printf("Error when getting transaction entries %v\n", err.Error())
|
||||
}
|
||||
// verify transaction entries data
|
||||
feeAccount, _ := suite.service.AccountFor(context.Background(), common.AccountTypeFees, userId)
|
||||
incomingAccount, _ := suite.service.AccountFor(context.Background(), common.AccountTypeIncoming, userId)
|
||||
outgoingAccount, _ := suite.service.AccountFor(context.Background(), common.AccountTypeOutgoing, userId)
|
||||
currentAccount, _ := suite.service.AccountFor(context.Background(), common.AccountTypeCurrent, userId)
|
||||
|
||||
outgoingInvoices, _ := suite.service.InvoicesFor(context.Background(), userId, common.InvoiceTypeOutgoing)
|
||||
incomingInvoices, _ := suite.service.InvoicesFor(context.Background(), userId, common.InvoiceTypeIncoming)
|
||||
assert.Equal(suite.T(), 1, len(outgoingInvoices))
|
||||
assert.Equal(suite.T(), 1, len(incomingInvoices))
|
||||
|
||||
assert.Equal(suite.T(), 3, len(transactonEntries))
|
||||
|
||||
assert.Equal(suite.T(), int64(aliceFundingSats), transactonEntries[0].Amount)
|
||||
assert.Equal(suite.T(), currentAccount.ID, transactonEntries[0].CreditAccountID)
|
||||
assert.Equal(suite.T(), incomingAccount.ID, transactonEntries[0].DebitAccountID)
|
||||
assert.Equal(suite.T(), int64(0), transactonEntries[0].ParentID)
|
||||
assert.Equal(suite.T(), incomingInvoices[0].ID, transactonEntries[0].InvoiceID)
|
||||
|
||||
assert.Equal(suite.T(), int64(externalSatRequested), transactonEntries[1].Amount)
|
||||
assert.Equal(suite.T(), outgoingAccount.ID, transactonEntries[1].CreditAccountID)
|
||||
assert.Equal(suite.T(), currentAccount.ID, transactonEntries[1].DebitAccountID)
|
||||
assert.Equal(suite.T(), int64(0), transactonEntries[1].ParentID)
|
||||
assert.Equal(suite.T(), outgoingInvoices[0].ID, transactonEntries[1].InvoiceID)
|
||||
|
||||
assert.Equal(suite.T(), int64(fee), transactonEntries[2].Amount)
|
||||
assert.Equal(suite.T(), feeAccount.ID, transactonEntries[2].CreditAccountID)
|
||||
assert.Equal(suite.T(), currentAccount.ID, transactonEntries[2].DebitAccountID)
|
||||
assert.Equal(suite.T(), outgoingInvoices[0].ID, transactonEntries[2].InvoiceID)
|
||||
|
||||
// make sure fee entry parent id is previous entry
|
||||
assert.Equal(suite.T(), transactonEntries[1].ID, transactonEntries[2].ParentID)
|
||||
}
|
||||
|
||||
@@ -29,6 +29,10 @@ const (
|
||||
lnd1RegtestMacaroonHex = "0201036c6e6402f801030a10e2133a1cac2c5b4d56e44e32dc64c8551201301a160a0761646472657373120472656164120577726974651a130a04696e666f120472656164120577726974651a170a08696e766f69636573120472656164120577726974651a210a086d616361726f6f6e120867656e6572617465120472656164120577726974651a160a076d657373616765120472656164120577726974651a170a086f6666636861696e120472656164120577726974651a160a076f6e636861696e120472656164120577726974651a140a057065657273120472656164120577726974651a180a067369676e6572120867656e657261746512047265616400000620c4f9783e0873fa50a2091806f5ebb919c5dc432e33800b401463ada6485df0ed"
|
||||
lnd2RegtestAddress = "rpc.lnd2.regtest.getalby.com:443"
|
||||
lnd2RegtestMacaroonHex = "0201036C6E6402F801030A101782922F4358E80655920FC7A7C3E9291201301A160A0761646472657373120472656164120577726974651A130A04696E666F120472656164120577726974651A170A08696E766F69636573120472656164120577726974651A210A086D616361726F6F6E120867656E6572617465120472656164120577726974651A160A076D657373616765120472656164120577726974651A170A086F6666636861696E120472656164120577726974651A160A076F6E636861696E120472656164120577726974651A140A057065657273120472656164120577726974651A180A067369676E6572120867656E657261746512047265616400000620628FFB2938C8540DD3AA5E578D9B43456835FAA176E175FFD4F9FBAE540E3BE9"
|
||||
// Use lnd3 for a funding client when testing out fee handling for payments done by lnd-1, since lnd3 doesn't have a direct channel to lnd1.
|
||||
// This will cause payment to be routed through lnd2, which will charge a fee (lnd default fee 1 sat base + 1 ppm).
|
||||
lnd3RegtestAddress = "rpc.lnd3.regtest.getalby.com:443"
|
||||
lnd3RegtestMacaroonHex = "0201036c6e6402f801030a102a5aa69a5efdf4b4a55a5304b164641f1201301a160a0761646472657373120472656164120577726974651a130a04696e666f120472656164120577726974651a170a08696e766f69636573120472656164120577726974651a210a086d616361726f6f6e120867656e6572617465120472656164120577726974651a160a076d657373616765120472656164120577726974651a170a086f6666636861696e120472656164120577726974651a160a076f6e636861696e120472656164120577726974651a140a057065657273120472656164120577726974651a180a067369676e6572120867656e657261746512047265616400000620defbb5a809262297fd661a9ab6d3deb4b7acca4f1309c79addb952f0dc2d8c82"
|
||||
simnetLnd1PubKey = "0242898f86064c2fd72de22059c947a83ba23e9d97aedeae7b6dba647123f1d71b"
|
||||
simnetLnd2PubKey = "025c1d5d1b4c983cc6350fc2d756fbb59b4dc365e45e87f8e3afe07e24013e8220"
|
||||
simnetLnd3PubKey = "03c7092d076f799ab18806743634b4c9bb34e351bdebc91d5b35963f3dc63ec5aa"
|
||||
@@ -37,7 +41,7 @@ const (
|
||||
func LndHubTestServiceInit(lndClientMock lnd.LightningClientWrapper) (svc *service.LndhubService, err error) {
|
||||
// change this if you want to run tests using sqlite
|
||||
// dbUri := "file:data_test.db"
|
||||
//make sure the datbase is empty every time you run the test suite
|
||||
// make sure the datbase is empty every time you run the test suite
|
||||
dbUri := "postgresql://user:password@localhost/lndhub?sslmode=disable"
|
||||
c := &service.Config{
|
||||
DatabaseUri: dbUri,
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
@@ -218,8 +219,10 @@ func (svc *LndhubService) PayInvoice(ctx context.Context, invoice *models.Invoic
|
||||
paymentResponse.TransactionEntry = &entry
|
||||
|
||||
// The payment was successful.
|
||||
// These changes to the invoice are persisted in the `HandleSuccessfulPayment` function
|
||||
invoice.Preimage = paymentResponse.PaymentPreimageStr
|
||||
err = svc.HandleSuccessfulPayment(context.Background(), invoice)
|
||||
invoice.Fee = paymentResponse.PaymentRoute.TotalFees
|
||||
err = svc.HandleSuccessfulPayment(context.Background(), invoice, entry)
|
||||
return &paymentResponse, err
|
||||
}
|
||||
|
||||
@@ -252,7 +255,7 @@ func (svc *LndhubService) HandleFailedPayment(ctx context.Context, invoice *mode
|
||||
return err
|
||||
}
|
||||
|
||||
func (svc *LndhubService) HandleSuccessfulPayment(ctx context.Context, invoice *models.Invoice) error {
|
||||
func (svc *LndhubService) HandleSuccessfulPayment(ctx context.Context, invoice *models.Invoice, parentEntry models.TransactionEntry) error {
|
||||
invoice.State = common.InvoiceStateSettled
|
||||
invoice.SettledAt = schema.NullTime{Time: time.Now()}
|
||||
|
||||
@@ -261,7 +264,44 @@ func (svc *LndhubService) HandleSuccessfulPayment(ctx context.Context, invoice *
|
||||
sentry.CaptureException(err)
|
||||
svc.Logger.Errorf("Could not update sucessful payment invoice user_id:%v invoice_id:%v", invoice.UserID, invoice.ID)
|
||||
}
|
||||
return err
|
||||
|
||||
// Get the user's fee account for the transaction entry, current account is already there in parent entry
|
||||
feeAccount, err := svc.AccountFor(ctx, common.AccountTypeFees, invoice.UserID)
|
||||
if err != nil {
|
||||
svc.Logger.Errorf("Could not find fees account user_id:%v", invoice.UserID)
|
||||
return err
|
||||
}
|
||||
|
||||
// add transaction entry for fee
|
||||
entry := models.TransactionEntry{
|
||||
UserID: invoice.UserID,
|
||||
InvoiceID: invoice.ID,
|
||||
CreditAccountID: feeAccount.ID,
|
||||
DebitAccountID: parentEntry.DebitAccountID,
|
||||
Amount: int64(invoice.Fee),
|
||||
ParentID: parentEntry.ID,
|
||||
}
|
||||
_, err = svc.DB.NewInsert().Model(&entry).Exec(ctx)
|
||||
if err != nil {
|
||||
sentry.CaptureException(err)
|
||||
svc.Logger.Errorf("Could not insert fee transaction entry user_id:%v invoice_id:%v", invoice.UserID, invoice.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
userBalance, err := svc.CurrentUserBalance(ctx, entry.UserID)
|
||||
if err != nil {
|
||||
sentry.CaptureException(err)
|
||||
svc.Logger.Errorf("Could not fetch user balance user_id:%v invoice_id:%v", invoice.UserID, invoice.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
if userBalance < 0 {
|
||||
amountMsg := fmt.Sprintf("User balance is negative transaction_entry_id:%v user_id:%v amount:%v", entry.ID, entry.UserID, userBalance)
|
||||
svc.Logger.Info(amountMsg)
|
||||
sentry.CaptureMessage(amountMsg)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc *LndhubService) AddOutgoingInvoice(ctx context.Context, userID int64, paymentRequest string, lnPayReq *lnd.LNPayReq) (*models.Invoice, error) {
|
||||
|
||||
Reference in New Issue
Block a user