diff --git a/satellite/api.go b/satellite/api.go index 5eaa5e8ce..c0968e61c 100644 --- a/satellite/api.go +++ b/satellite/api.go @@ -547,7 +547,9 @@ func NewAPI(log *zap.Logger, full *identity.FullIdentity, db DB, peer.Payments.StorjscanService = storjscan.NewService(log.Named("storjscan-service"), peer.DB.Wallets(), peer.DB.StorjscanPayments(), - peer.Payments.StorjscanClient) + peer.Payments.StorjscanClient, + pc.Storjscan.Confirmations, + pc.BonusRate) if err != nil { return nil, errs.Combine(err, peer.Close()) } @@ -610,6 +612,7 @@ func NewAPI(log *zap.Logger, full *identity.FullIdentity, db DB, accountFreezeService, peer.Console.Listener, config.Payments.StripeCoinPayments.StripePublicKey, + config.Payments.Storjscan.Confirmations, peer.URL(), config.Payments.PackagePlans, ) diff --git a/satellite/console/consoleweb/config.go b/satellite/console/consoleweb/config.go index daa87bcff..f0b1bce49 100644 --- a/satellite/console/consoleweb/config.go +++ b/satellite/console/consoleweb/config.go @@ -48,6 +48,7 @@ type FrontendConfig struct { PricingPackagesEnabled bool `json:"pricingPackagesEnabled"` NewUploadModalEnabled bool `json:"newUploadModalEnabled"` GalleryViewEnabled bool `json:"galleryViewEnabled"` + NeededTransactionConfirmations int `json:"neededTransactionConfirmations"` } // Satellites is a configuration value that contains a list of satellite names and addresses. diff --git a/satellite/console/consoleweb/consoleapi/payments.go b/satellite/console/consoleweb/consoleapi/payments.go index 25d2b92b4..03f79a36b 100644 --- a/satellite/console/consoleweb/consoleapi/payments.go +++ b/satellite/console/consoleweb/consoleapi/payments.go @@ -466,6 +466,30 @@ func (p *Payments) WalletPayments(w http.ResponseWriter, r *http.Request) { } } +// WalletPaymentsWithConfirmations returns with the list of storjscan transactions (including confirmations count) for user`s wallet. +func (p *Payments) WalletPaymentsWithConfirmations(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + var err error + defer mon.Task()(&ctx)(&err) + + w.Header().Set("Content-Type", "application/json") + + walletPayments, err := p.service.Payments().WalletPaymentsWithConfirmations(ctx) + if err != nil { + if console.ErrUnauthorized.Has(err) { + p.serveJSONError(ctx, w, http.StatusUnauthorized, err) + return + } + + p.serveJSONError(ctx, w, http.StatusInternalServerError, err) + return + } + + if err = json.NewEncoder(w).Encode(walletPayments); err != nil { + p.log.Error("failed to encode wallet payments with confirmations", zap.Error(ErrPaymentsAPI.Wrap(err))) + } +} + // GetProjectUsagePriceModel returns the project usage price model for the user. func (p *Payments) GetProjectUsagePriceModel(w http.ResponseWriter, r *http.Request) { ctx := r.Context() diff --git a/satellite/console/consoleweb/server.go b/satellite/console/consoleweb/server.go index 672d3fbb5..0a163a406 100644 --- a/satellite/console/consoleweb/server.go +++ b/satellite/console/consoleweb/server.go @@ -144,7 +144,8 @@ type Server struct { userIDRateLimiter *web.RateLimiter nodeURL storj.NodeURL - stripePublicKey string + stripePublicKey string + neededTokenPaymentConfirmations int packagePlans paymentsconfig.PackagePlans @@ -210,20 +211,21 @@ func (a *apiAuth) RemoveAuthCookie(w http.ResponseWriter) { } // NewServer creates new instance of console server. -func NewServer(logger *zap.Logger, config Config, service *console.Service, oidcService *oidc.Service, mailService *mailservice.Service, analytics *analytics.Service, abTesting *abtesting.Service, accountFreezeService *console.AccountFreezeService, listener net.Listener, stripePublicKey string, nodeURL storj.NodeURL, packagePlans paymentsconfig.PackagePlans) *Server { +func NewServer(logger *zap.Logger, config Config, service *console.Service, oidcService *oidc.Service, mailService *mailservice.Service, analytics *analytics.Service, abTesting *abtesting.Service, accountFreezeService *console.AccountFreezeService, listener net.Listener, stripePublicKey string, neededTokenPaymentConfirmations int, nodeURL storj.NodeURL, packagePlans paymentsconfig.PackagePlans) *Server { server := Server{ - log: logger, - config: config, - listener: listener, - service: service, - mailService: mailService, - analytics: analytics, - abTesting: abTesting, - stripePublicKey: stripePublicKey, - ipRateLimiter: web.NewIPRateLimiter(config.RateLimit, logger), - userIDRateLimiter: NewUserIDRateLimiter(config.RateLimit, logger), - nodeURL: nodeURL, - packagePlans: packagePlans, + log: logger, + config: config, + listener: listener, + service: service, + mailService: mailService, + analytics: analytics, + abTesting: abTesting, + stripePublicKey: stripePublicKey, + neededTokenPaymentConfirmations: neededTokenPaymentConfirmations, + ipRateLimiter: web.NewIPRateLimiter(config.RateLimit, logger), + userIDRateLimiter: NewUserIDRateLimiter(config.RateLimit, logger), + nodeURL: nodeURL, + packagePlans: packagePlans, } logger.Debug("Starting Satellite Console server.", zap.Stringer("Address", server.listener.Addr())) @@ -332,6 +334,7 @@ func NewServer(logger *zap.Logger, config Config, service *console.Service, oidc paymentsRouter.HandleFunc("/wallet", paymentController.GetWallet).Methods(http.MethodGet, http.MethodOptions) paymentsRouter.HandleFunc("/wallet", paymentController.ClaimWallet).Methods(http.MethodPost, http.MethodOptions) paymentsRouter.HandleFunc("/wallet/payments", paymentController.WalletPayments).Methods(http.MethodGet, http.MethodOptions) + paymentsRouter.HandleFunc("/wallet/payments-with-confirmations", paymentController.WalletPaymentsWithConfirmations).Methods(http.MethodGet, http.MethodOptions) paymentsRouter.HandleFunc("/billing-history", paymentController.BillingHistory).Methods(http.MethodGet, http.MethodOptions) paymentsRouter.Handle("/coupon/apply", server.userIDRateLimiter.Limit(http.HandlerFunc(paymentController.ApplyCouponCode))).Methods(http.MethodPatch, http.MethodOptions) paymentsRouter.HandleFunc("/coupon", paymentController.GetCoupon).Methods(http.MethodGet, http.MethodOptions) @@ -718,6 +721,7 @@ func (server *Server) frontendConfigHandler(w http.ResponseWriter, r *http.Reque PricingPackagesEnabled: server.config.PricingPackagesEnabled, NewUploadModalEnabled: server.config.NewUploadModalEnabled, GalleryViewEnabled: server.config.GalleryViewEnabled, + NeededTransactionConfirmations: server.neededTokenPaymentConfirmations, } err := json.NewEncoder(w).Encode(&cfg) diff --git a/satellite/console/service.go b/satellite/console/service.go index 8bdbb0597..014eb6b9c 100644 --- a/satellite/console/service.go +++ b/satellite/console/service.go @@ -3091,6 +3091,12 @@ func EtherscanURL(tx string) string { // ErrWalletNotClaimed shows that no address is claimed by the user. var ErrWalletNotClaimed = errs.Class("wallet is not claimed") +// TestSwapDepositWallets replaces the existing handler for deposit wallets with +// the one specified for use in testing. +func (payment Payments) TestSwapDepositWallets(dw payments.DepositWallets) { + payment.service.depositWallets = dw +} + // ClaimWallet requests a new wallet for the users to be used for payments. If wallet is already claimed, // it will return with the info without error. func (payment Payments) ClaimWallet(ctx context.Context) (_ WalletInfo, err error) { @@ -3211,6 +3217,27 @@ func (payment Payments) WalletPayments(ctx context.Context) (_ WalletPayments, e }, nil } +// WalletPaymentsWithConfirmations returns with all the native blockchain payments (including pending) for a user's wallet. +func (payment Payments) WalletPaymentsWithConfirmations(ctx context.Context) (paymentsWithConfirmations []payments.WalletPaymentWithConfirmations, err error) { + defer mon.Task()(&ctx)(&err) + + user, err := GetUser(ctx) + if err != nil { + return nil, Error.Wrap(err) + } + address, err := payment.service.depositWallets.Get(ctx, user.ID) + if err != nil { + return nil, Error.Wrap(err) + } + + paymentsWithConfirmations, err = payment.service.depositWallets.PaymentsWithConfirmations(ctx, address) + if err != nil { + return nil, Error.Wrap(err) + } + + return +} + // Purchase makes a purchase of `price` amount with description of `desc` and payment method with id of `paymentMethodID`. // If a paid invoice with the same description exists, then we assume this is a retried request and don't create and pay // another invoice. diff --git a/satellite/console/service_test.go b/satellite/console/service_test.go index b6c2580a5..fa0a647e1 100644 --- a/satellite/console/service_test.go +++ b/satellite/console/service_test.go @@ -1702,6 +1702,86 @@ func TestPaymentsWalletPayments(t *testing.T) { }) } +type mockDepositWallets struct { + address blockchain.Address + payments []payments.WalletPaymentWithConfirmations +} + +func (dw mockDepositWallets) Claim(_ context.Context, _ uuid.UUID) (blockchain.Address, error) { + return dw.address, nil +} + +func (dw mockDepositWallets) Get(_ context.Context, _ uuid.UUID) (blockchain.Address, error) { + return dw.address, nil +} + +func (dw mockDepositWallets) Payments(_ context.Context, _ blockchain.Address, _ int, _ int64) (p []payments.WalletPayment, err error) { + return +} + +func (dw mockDepositWallets) PaymentsWithConfirmations(_ context.Context, _ blockchain.Address) ([]payments.WalletPaymentWithConfirmations, error) { + return dw.payments, nil +} + +func TestWalletPaymentsWithConfirmations(t *testing.T) { + testplanet.Run(t, testplanet.Config{ + SatelliteCount: 1, StorageNodeCount: 0, UplinkCount: 0, + }, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) { + sat := planet.Satellites[0] + service := sat.API.Console.Service + paymentsService := service.Payments() + + user, err := sat.AddUser(ctx, console.CreateUser{ + FullName: "Test User", + Email: "test@mail.test", + Password: "example", + }, 1) + require.NoError(t, err) + + now := time.Now() + wallet := blockchaintest.NewAddress() + + var expected []payments.WalletPaymentWithConfirmations + for i := 0; i < 3; i++ { + expected = append(expected, payments.WalletPaymentWithConfirmations{ + From: blockchaintest.NewAddress().Hex(), + To: wallet.Hex(), + TokenValue: currency.AmountFromBaseUnits(int64(i), currency.StorjToken).AsDecimal(), + USDValue: currency.AmountFromBaseUnits(int64(i), currency.USDollarsMicro).AsDecimal(), + Status: payments.PaymentStatusConfirmed, + BlockHash: blockchaintest.NewHash().Hex(), + BlockNumber: int64(i), + Transaction: blockchaintest.NewHash().Hex(), + LogIndex: i, + Timestamp: now, + Confirmations: int64(i), + BonusTokens: decimal.NewFromInt(int64(i)), + }) + } + + paymentsService.TestSwapDepositWallets(mockDepositWallets{address: wallet, payments: expected}) + + reqCtx := console.WithUser(ctx, user) + + walletPayments, err := paymentsService.WalletPaymentsWithConfirmations(reqCtx) + require.NoError(t, err) + require.NotZero(t, len(walletPayments)) + + for i, wp := range walletPayments { + require.Equal(t, expected[i].From, wp.From) + require.Equal(t, expected[i].To, wp.To) + require.Equal(t, expected[i].TokenValue, wp.TokenValue) + require.Equal(t, expected[i].USDValue, wp.USDValue) + require.Equal(t, expected[i].Status, wp.Status) + require.Equal(t, expected[i].BlockHash, wp.BlockHash) + require.Equal(t, expected[i].BlockNumber, wp.BlockNumber) + require.Equal(t, expected[i].Transaction, wp.Transaction) + require.Equal(t, expected[i].LogIndex, wp.LogIndex) + require.Equal(t, expected[i].Timestamp, wp.Timestamp) + } + }) +} + func TestPaymentsPurchase(t *testing.T) { testplanet.Run(t, testplanet.Config{ SatelliteCount: 1, StorageNodeCount: 0, UplinkCount: 0, diff --git a/satellite/core.go b/satellite/core.go index b37a9f751..f44f6eca8 100644 --- a/satellite/core.go +++ b/satellite/core.go @@ -496,7 +496,9 @@ func New(log *zap.Logger, full *identity.FullIdentity, db DB, peer.Payments.StorjscanService = storjscan.NewService(log.Named("storjscan-service"), peer.DB.Wallets(), peer.DB.StorjscanPayments(), - peer.Payments.StorjscanClient) + peer.Payments.StorjscanClient, + pc.Storjscan.Confirmations, + pc.BonusRate) if err != nil { return nil, errs.Combine(err, peer.Close()) } diff --git a/satellite/payments/billing/transactions.go b/satellite/payments/billing/transactions.go index 7f8f114f7..1660b3069 100644 --- a/satellite/payments/billing/transactions.go +++ b/satellite/payments/billing/transactions.go @@ -105,6 +105,12 @@ type Transaction struct { CreatedAt time.Time } +// CalculateBonusAmount calculates bonus for given currency amount and bonus rate. +func CalculateBonusAmount(amount currency.Amount, bonusRate int64) currency.Amount { + bonusUnits := amount.BaseUnits() * bonusRate / 100 + return currency.AmountFromBaseUnits(bonusUnits, amount.Currency()) +} + func prepareBonusTransaction(bonusRate int64, source string, transaction Transaction) (Transaction, bool) { // Bonus transactions only apply when enabled (i.e. positive rate) and // for StorjScan transactions. @@ -120,7 +126,7 @@ func prepareBonusTransaction(bonusRate int64, source string, transaction Transac return Transaction{ UserID: transaction.UserID, - Amount: calculateBonusAmount(transaction.Amount, bonusRate), + Amount: CalculateBonusAmount(transaction.Amount, bonusRate), Description: fmt.Sprintf("STORJ Token Bonus (%d%%)", bonusRate), Source: StorjScanBonusSource, Status: TransactionStatusCompleted, @@ -129,8 +135,3 @@ func prepareBonusTransaction(bonusRate int64, source string, transaction Transac Metadata: append([]byte(nil), transaction.Metadata...), }, true } - -func calculateBonusAmount(amount currency.Amount, bonusRate int64) currency.Amount { - bonusUnits := amount.BaseUnits() * bonusRate / 100 - return currency.AmountFromBaseUnits(bonusUnits, amount.Currency()) -} diff --git a/satellite/payments/storjscan/chore.go b/satellite/payments/storjscan/chore.go index 050bf6500..2e0d88fae 100644 --- a/satellite/payments/storjscan/chore.go +++ b/satellite/payments/storjscan/chore.go @@ -65,7 +65,7 @@ func (chore *Chore) Run(ctx context.Context) (err error) { return nil } - latestPayments, err := chore.client.Payments(ctx, from) + latestPayments, err := chore.client.AllPayments(ctx, from) if err != nil { chore.log.Error("error retrieving payments", zap.Error(ChoreErr.Wrap(err))) return nil diff --git a/satellite/payments/storjscan/client.go b/satellite/payments/storjscan/client.go index 3796bc930..d0a69245b 100644 --- a/satellite/payments/storjscan/client.go +++ b/satellite/payments/storjscan/client.go @@ -67,13 +67,30 @@ func NewClient(endpoint, identifier, secret string) *Client { } } -// Payments retrieves all payments after specified block for wallets associated with particular API key. -func (client *Client) Payments(ctx context.Context, from int64) (_ LatestPayments, err error) { +// AllPayments retrieves all payments after specified block for wallets associated with particular API key. +func (client *Client) AllPayments(ctx context.Context, from int64) (payments LatestPayments, err error) { defer mon.Task()(&ctx)(&err) p := client.endpoint + "/api/v0/tokens/payments" - req, err := http.NewRequestWithContext(ctx, http.MethodGet, p, nil) + payments, err = client.getPayments(ctx, p, from) + + return +} + +// Payments retrieves payments after specified block for given address associated with particular API key. +func (client *Client) Payments(ctx context.Context, from int64, address string) (payments LatestPayments, err error) { + defer mon.Task()(&ctx)(&err) + + p := client.endpoint + "/api/v0/tokens/payments/" + address + + payments, err = client.getPayments(ctx, p, from) + + return +} + +func (client *Client) getPayments(ctx context.Context, path string, from int64) (_ LatestPayments, err error) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, path, nil) if err != nil { return LatestPayments{}, ClientErr.Wrap(err) } diff --git a/satellite/payments/storjscan/client_test.go b/satellite/payments/storjscan/client_test.go index 9f9842a78..a9d3d3dd5 100644 --- a/satellite/payments/storjscan/client_test.go +++ b/satellite/payments/storjscan/client_test.go @@ -69,17 +69,17 @@ func TestClientMocked(t *testing.T) { })) defer server.Close() - client := storjscan.NewClient(server.URL, "eu", "secret") + client := storjscan.NewClient(server.URL, identifier, secret) t.Run("all payments from 0", func(t *testing.T) { - actual, err := client.Payments(ctx, 0) + actual, err := client.AllPayments(ctx, 0) require.NoError(t, err) require.Equal(t, latestBlock, actual.LatestBlock) require.Equal(t, len(payments), len(actual.Payments)) require.Equal(t, payments, actual.Payments) }) - t.Run("payments from 50", func(t *testing.T) { - actual, err := client.Payments(ctx, 50) + t.Run("all payments from 50", func(t *testing.T) { + actual, err := client.AllPayments(ctx, 50) require.NoError(t, err) require.Equal(t, latestBlock, actual.LatestBlock) require.Equal(t, 50, len(actual.Payments)) @@ -104,7 +104,7 @@ func TestClientMockedUnauthorized(t *testing.T) { t.Run("empty credentials", func(t *testing.T) { client := storjscan.NewClient(server.URL, "", "") - _, err := client.Payments(ctx, 0) + _, err := client.AllPayments(ctx, 0) require.Error(t, err) require.True(t, storjscan.ClientErrUnauthorized.Has(err)) require.Equal(t, "identifier is invalid", errs.Unwrap(err).Error()) @@ -112,7 +112,7 @@ func TestClientMockedUnauthorized(t *testing.T) { t.Run("invalid identifier", func(t *testing.T) { client := storjscan.NewClient(server.URL, "invalid", "secret") - _, err := client.Payments(ctx, 0) + _, err := client.AllPayments(ctx, 0) require.Error(t, err) require.True(t, storjscan.ClientErrUnauthorized.Has(err)) require.Equal(t, "identifier is invalid", errs.Unwrap(err).Error()) @@ -120,7 +120,7 @@ func TestClientMockedUnauthorized(t *testing.T) { t.Run("invalid secret", func(t *testing.T) { client := storjscan.NewClient(server.URL, "eu", "invalid") - _, err := client.Payments(ctx, 0) + _, err := client.AllPayments(ctx, 0) require.Error(t, err) require.True(t, storjscan.ClientErrUnauthorized.Has(err)) require.Equal(t, "secret is invalid", errs.Unwrap(err).Error()) diff --git a/satellite/payments/storjscan/service.go b/satellite/payments/storjscan/service.go index 0e81def1d..9b91509cb 100644 --- a/satellite/payments/storjscan/service.go +++ b/satellite/payments/storjscan/service.go @@ -35,19 +35,23 @@ var _ billing.PaymentType = (*Service)(nil) // Service exposes API to interact with storjscan payments provider. type Service struct { - log *zap.Logger - walletsDB WalletsDB - paymentsDB PaymentsDB - client *Client + log *zap.Logger + walletsDB WalletsDB + paymentsDB PaymentsDB + client *Client + neededConfirmations int + bonusRate int64 } // NewService creates new storjscan service instance. -func NewService(log *zap.Logger, walletsDB WalletsDB, paymentsDB PaymentsDB, client *Client) *Service { +func NewService(log *zap.Logger, walletsDB WalletsDB, paymentsDB PaymentsDB, client *Client, neededConfirmations int, bonusRate int64) *Service { return &Service{ - log: log, - walletsDB: walletsDB, - paymentsDB: paymentsDB, - client: client, + log: log, + walletsDB: walletsDB, + paymentsDB: paymentsDB, + client: client, + neededConfirmations: neededConfirmations, + bonusRate: bonusRate, } } @@ -116,6 +120,45 @@ func (service *Service) Payments(ctx context.Context, wallet blockchain.Address, return walletPayments, nil } +// PaymentsWithConfirmations returns payments with confirmations count for a particular wallet. +func (service *Service) PaymentsWithConfirmations(ctx context.Context, wallet blockchain.Address) (_ []payments.WalletPaymentWithConfirmations, err error) { + defer mon.Task()(&ctx)(&err) + + latestPayments, err := service.client.Payments(ctx, 0, wallet.Hex()) + if err != nil { + return nil, ErrService.Wrap(err) + } + + var walletPayments []payments.WalletPaymentWithConfirmations + for _, pmnt := range latestPayments.Payments { + confirmations := latestPayments.LatestBlock.Number - pmnt.BlockNumber + + var status payments.PaymentStatus + if confirmations >= int64(service.neededConfirmations) { + status = payments.PaymentStatusConfirmed + } else { + status = payments.PaymentStatusPending + } + + walletPayments = append(walletPayments, payments.WalletPaymentWithConfirmations{ + From: pmnt.From.Hex(), + To: pmnt.To.Hex(), + TokenValue: pmnt.TokenValue.AsDecimal(), + USDValue: pmnt.USDValue.AsDecimal(), + Status: status, + BlockHash: pmnt.BlockHash.Hex(), + BlockNumber: pmnt.BlockNumber, + Transaction: pmnt.Transaction.Hex(), + LogIndex: pmnt.LogIndex, + Timestamp: pmnt.Timestamp, + Confirmations: confirmations, + BonusTokens: billing.CalculateBonusAmount(pmnt.TokenValue, service.bonusRate).AsDecimal(), + }) + } + + return walletPayments, nil +} + // Source defines the billing transaction source for storjscan payments. func (service *Service) Source() string { return billing.StorjScanSource diff --git a/satellite/payments/storjscan/service_test.go b/satellite/payments/storjscan/service_test.go index 2c522e042..1c490e62c 100644 --- a/satellite/payments/storjscan/service_test.go +++ b/satellite/payments/storjscan/service_test.go @@ -99,7 +99,7 @@ func TestServicePayments(t *testing.T) { err := paymentsDB.InsertBatch(ctx, cachedPayments) require.NoError(t, err) - service := storjscan.NewService(zaptest.NewLogger(t), db.Wallets(), paymentsDB, nil) + service := storjscan.NewService(zaptest.NewLogger(t), db.Wallets(), paymentsDB, nil, 15, 10) t.Run("wallet 1", func(t *testing.T) { expected := []payments.WalletPayment{walletPayments[3], walletPayments[1], walletPayments[0]} @@ -151,7 +151,7 @@ func TestServiceWallets(t *testing.T) { err = db.Wallets().Add(ctx, userID3, walletAddress3) require.NoError(t, err) - service := storjscan.NewService(zaptest.NewLogger(t), db.Wallets(), db.StorjscanPayments(), nil) + service := storjscan.NewService(zaptest.NewLogger(t), db.Wallets(), db.StorjscanPayments(), nil, 15, 10) t.Run("get Wallet", func(t *testing.T) { actual, err := service.Get(ctx, userID1) diff --git a/satellite/payments/tokens.go b/satellite/payments/tokens.go index 5919ed32a..d9059f38e 100644 --- a/satellite/payments/tokens.go +++ b/satellite/payments/tokens.go @@ -34,6 +34,8 @@ type DepositWallets interface { Get(ctx context.Context, userID uuid.UUID) (blockchain.Address, error) // Payments returns payments for a particular wallet. Payments(ctx context.Context, wallet blockchain.Address, limit int, offset int64) ([]WalletPayment, error) + // PaymentsWithConfirmations returns payments with confirmations count for a particular wallet. + PaymentsWithConfirmations(ctx context.Context, wallet blockchain.Address) ([]WalletPaymentWithConfirmations, error) } // TransactionStatus defines allowed statuses @@ -121,3 +123,19 @@ type WalletPayment struct { LogIndex int `json:"logIndex"` Timestamp time.Time `json:"timestamp"` } + +// WalletPaymentWithConfirmations holds storj token payment data with confirmations count. +type WalletPaymentWithConfirmations struct { + From string `json:"from"` + To string `json:"to"` + TokenValue decimal.Decimal `json:"tokenValue"` + USDValue decimal.Decimal `json:"usdValue"` + Status PaymentStatus `json:"status"` + BlockHash string `json:"blockHash"` + BlockNumber int64 `json:"blockNumber"` + Transaction string `json:"transaction"` + LogIndex int `json:"logIndex"` + Timestamp time.Time `json:"timestamp"` + Confirmations int64 `json:"confirmations"` + BonusTokens decimal.Decimal `json:"bonusTokens"` +} diff --git a/testsuite/storjscan/client_test.go b/testsuite/storjscan/client_test.go index 09426db7c..c8178ad89 100644 --- a/testsuite/storjscan/client_test.go +++ b/testsuite/storjscan/client_test.go @@ -68,7 +68,7 @@ func TestClientPayments(t *testing.T) { err = stack.App.TokenPrice.Service.SavePrice(ctx, blockTime.Add(-30*time.Second), price) require.NoError(t, err) - pmnts, err := planet.Satellites[0].API.Payments.StorjscanClient.Payments(ctx, 0) + pmnts, err := planet.Satellites[0].API.Payments.StorjscanClient.AllPayments(ctx, 0) require.NoError(t, err) require.Equal(t, block.Number().Int64(), pmnts.LatestBlock.Number) require.Len(t, pmnts.Payments, 1)