satellite/payments: More Cleanup and Satellite command to ensure we have stripe customers (#3805)

This commit is contained in:
Stefan Benten 2020-03-16 20:34:15 +01:00 committed by GitHub
parent 3d6518081a
commit 52590197c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 253 additions and 94 deletions

View File

@ -120,7 +120,6 @@ var (
Args: cobra.MinimumNArgs(2),
RunE: cmdGracefulExit,
}
verifyGracefulExitReceiptCmd = &cobra.Command{
Use: "verify-exit-receipt [storage node ID] [receipt]",
Short: "Verify a graceful exit receipt",
@ -128,6 +127,12 @@ var (
Args: cobra.MinimumNArgs(2),
RunE: cmdVerifyGracefulExitReceipt,
}
stripeCustomerCmd = &cobra.Command{
Use: "ensure-stripe-customer",
Short: "Ensures that we have a stripe customer for every user",
Long: "Ensures that we have a stripe customer for every satellite user",
RunE: cmdStripeCustomer,
}
runCfg Satellite
setupCfg Satellite
@ -173,6 +178,7 @@ func init() {
reportsCmd.AddCommand(partnerAttributionCmd)
reportsCmd.AddCommand(gracefulExitCmd)
reportsCmd.AddCommand(verifyGracefulExitReceiptCmd)
reportsCmd.AddCommand(stripeCustomerCmd)
process.Bind(runCmd, &runCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir))
process.Bind(runMigrationCmd, &runCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir))
process.Bind(runAPICmd, &runCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir))
@ -183,6 +189,7 @@ func init() {
process.Bind(nodeUsageCmd, &nodeUsageCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir))
process.Bind(gracefulExitCmd, &gracefulExitCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir))
process.Bind(verifyGracefulExitReceiptCmd, &verifyGracefulExitReceiptCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir))
process.Bind(stripeCustomerCmd, &runCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir))
process.Bind(partnerAttributionCmd, &partnerAttribtionCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir))
}
@ -415,6 +422,12 @@ func cmdNodeUsage(cmd *cobra.Command, args []string) (err error) {
return generateNodeUsageCSV(ctx, start, end, file)
}
func cmdStripeCustomer(cmd *cobra.Command, args []string) (err error) {
ctx, _ := process.Ctx(cmd)
return generateStripeCustomers(ctx)
}
func cmdValueAttribution(cmd *cobra.Command, args []string) (err error) {
ctx, _ := process.Ctx(cmd)
log := zap.L().Named("satellite-cli")

109
cmd/satellite/stripe.go Normal file
View File

@ -0,0 +1,109 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
package main
import (
"context"
"github.com/prometheus/common/log"
"github.com/skyrings/skyring-common/tools/uuid"
"github.com/zeebo/errs"
"go.uber.org/zap"
"storj.io/storj/private/dbutil"
"storj.io/storj/satellite"
"storj.io/storj/satellite/payments"
"storj.io/storj/satellite/payments/stripecoinpayments"
"storj.io/storj/satellite/satellitedb"
"storj.io/storj/satellite/satellitedb/dbx"
)
// UserData contains the uuid and email of a satellite user.
type UserData struct {
ID uuid.UUID
Email string
}
// generateStripeCustomers creates missing stripe-customers for users in our database.
func generateStripeCustomers(ctx context.Context) (err error) {
//Open SatelliteDB for the Payment Service
db, err := satellitedb.New(zap.L().Named("db"), runCfg.Database, satellitedb.Options{})
if err != nil {
return errs.New("error connecting to master database on satellite: %+v", err)
}
defer func() {
err = errs.Combine(err, db.Close())
}()
//Open direct DB connection to execute custom queries
driver, source, implementation, err := dbutil.SplitConnStr(runCfg.Database)
if err != nil {
return err
}
if implementation != dbutil.Postgres && implementation != dbutil.Cockroach {
return errs.New("unsupported driver %q", driver)
}
dbxDB, err := dbx.Open(driver, source)
if err != nil {
return err
}
log.Debug("Connected to:", zap.String("db source", source))
defer func() {
err = errs.Combine(err, dbxDB.Close())
}()
handler, err := setupPayments(zap.L().Named("payments"), db)
if err != nil {
return err
}
rows, err := dbxDB.Query(ctx, "SELECT id, email FROM users WHERE id NOT IN (SELECT user_id from stripe_customers)")
if err != nil {
return err
}
defer func() {
err = errs.Combine(err, rows.Close())
}()
for rows.Next() {
var user UserData
err := rows.Scan(&user)
if err != nil {
return err
}
err = handler.Setup(ctx, user.ID, user.Email)
if err != nil {
return err
}
}
return err
}
func setupPayments(log *zap.Logger, db satellite.DB) (handler payments.Accounts, err error) {
pc := runCfg.Payments
service, err := stripecoinpayments.NewService(
log.Named("payments.stripe:service"),
pc.StripeCoinPayments,
db.StripeCoinPayments(),
db.Console().Projects(),
db.ProjectAccounting(),
pc.StorageTBPrice,
pc.EgressTBPrice,
pc.ObjectPrice,
pc.BonusRate,
pc.CouponValue,
pc.CouponDuration,
pc.CouponProjectLimit,
pc.MinCoinPayment)
if err != nil {
return nil, err
}
handler = service.Accounts()
return handler, err
}

3
go.sum
View File

@ -35,7 +35,9 @@ github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMx
github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM=
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alessio/shellescape v0.0.0-20190409004728-b115ca0f9053 h1:H/GMMKYPkEIC3DF/JWQz8Pdd+Feifov2EIgGfNpeogI=
github.com/alessio/shellescape v0.0.0-20190409004728-b115ca0f9053/go.mod h1:xW8sBma2LE3QxFSzCnH9qe6gAE2yO9GvQaWwX89HxbE=
@ -583,6 +585,7 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=

View File

@ -529,7 +529,11 @@ func NewAPI(log *zap.Logger, full *identity.FullIdentity, db DB,
pc.StorageTBPrice,
pc.EgressTBPrice,
pc.ObjectPrice,
pc.BonusRate)
pc.BonusRate,
pc.CouponValue,
pc.CouponDuration,
pc.CouponProjectLimit,
pc.MinCoinPayment)
if err != nil {
return nil, errs.Combine(err, peer.Close())
@ -583,6 +587,7 @@ func NewAPI(log *zap.Logger, full *identity.FullIdentity, db DB,
peer.Marketing.PartnersService,
peer.Payments.Accounts,
consoleConfig.Config,
config.Payments.MinCoinPayment,
)
if err != nil {
return nil, errs.Combine(err, peer.Close())

View File

@ -24,7 +24,7 @@ import (
"storj.io/storj/satellite/console/consoleauth"
"storj.io/storj/satellite/console/consoleweb/consoleql"
"storj.io/storj/satellite/mailservice"
"storj.io/storj/satellite/payments/stripecoinpayments"
"storj.io/storj/satellite/payments/mockpayments"
"storj.io/storj/satellite/rewards"
"storj.io/storj/satellite/satellitedb/satellitedbtest"
"storj.io/storj/storage/redis/redisserver"
@ -57,16 +57,6 @@ func TestGrapqhlMutation(t *testing.T) {
},
)
payments, err := stripecoinpayments.NewService(
log.Named("payments"),
stripecoinpayments.Config{},
db.StripeCoinPayments(),
db.Console().Projects(),
db.ProjectAccounting(),
"0", "0", "0", 10,
)
require.NoError(t, err)
redis, err := redisserver.Mini()
require.NoError(t, err)
defer ctx.Check(redis.Close)
@ -84,8 +74,9 @@ func TestGrapqhlMutation(t *testing.T) {
projectUsage,
db.Rewards(),
partnersService,
payments.Accounts(),
mockpayments.Accounts(),
console.Config{PasswordCost: console.TestPasswordCost},
5000,
)
require.NoError(t, err)

View File

@ -22,7 +22,7 @@ import (
"storj.io/storj/satellite/console/consoleauth"
"storj.io/storj/satellite/console/consoleweb/consoleql"
"storj.io/storj/satellite/mailservice"
"storj.io/storj/satellite/payments/stripecoinpayments"
"storj.io/storj/satellite/payments/mockpayments"
"storj.io/storj/satellite/rewards"
"storj.io/storj/satellite/satellitedb/satellitedbtest"
"storj.io/storj/storage/redis/redisserver"
@ -42,16 +42,6 @@ func TestGraphqlQuery(t *testing.T) {
},
)
payments, err := stripecoinpayments.NewService(
log.Named("payments"),
stripecoinpayments.Config{},
db.StripeCoinPayments(),
db.Console().Projects(),
db.ProjectAccounting(),
"0", "0", "0", 10,
)
require.NoError(t, err)
redis, err := redisserver.Mini()
require.NoError(t, err)
defer ctx.Check(redis.Close)
@ -69,8 +59,9 @@ func TestGraphqlQuery(t *testing.T) {
projectUsage,
db.Rewards(),
partnersService,
payments.Accounts(),
mockpayments.Accounts(),
console.Config{PasswordCost: console.TestPasswordCost},
5000,
)
require.NoError(t, err)

View File

@ -87,6 +87,8 @@ type Service struct {
accounts payments.Accounts
config Config
minCoinPayment int64
}
// Config keeps track of core console service configuration parameters
@ -101,7 +103,7 @@ type PaymentsService struct {
}
// NewService returns new instance of Service.
func NewService(log *zap.Logger, signer Signer, store DB, projectAccounting accounting.ProjectAccounting, projectUsage *accounting.Service, rewards rewards.DB, partners *rewards.PartnersService, accounts payments.Accounts, config Config) (*Service, error) {
func NewService(log *zap.Logger, signer Signer, store DB, projectAccounting accounting.ProjectAccounting, projectUsage *accounting.Service, rewards rewards.DB, partners *rewards.PartnersService, accounts payments.Accounts, config Config, minCoinPayment int64) (*Service, error) {
if signer == nil {
return nil, errs.New("signer can't be nil")
}
@ -125,6 +127,7 @@ func NewService(log *zap.Logger, signer Signer, store DB, projectAccounting acco
partners: partners,
accounts: accounts,
config: config,
minCoinPayment: minCoinPayment,
}, nil
}
@ -369,7 +372,7 @@ func (paymentService PaymentsService) AddPromotionalCoupon(ctx context.Context,
return errs.New("user don't have a payment method")
}
return paymentService.service.accounts.Coupons().AddPromotionalCoupon(ctx, userID, duration, amount, limit)
return paymentService.service.accounts.Coupons().AddPromotionalCoupon(ctx, userID)
}
// checkRegistrationSecret returns a RegistrationToken if applicable (nil if not), and an error
@ -864,6 +867,24 @@ func (s *Service) CreateProject(ctx context.Context, projectInfo ProjectInfo) (p
return nil, ErrProjLimit.Wrap(err)
}
cards, err := s.accounts.CreditCards().List(ctx, auth.User.ID)
if err != nil {
s.log.Debug(fmt.Sprintf("could not add promotional coupon for user %s", auth.User.ID.String()), zap.Error(Error.Wrap(err)))
return nil, Error.Wrap(err)
}
balance, err := s.accounts.Balance(ctx, auth.User.ID)
if err != nil {
s.log.Debug(fmt.Sprintf("could not add promotional coupon for user %s", auth.User.ID.String()), zap.Error(Error.Wrap(err)))
return nil, Error.Wrap(err)
}
if len(cards) == 0 && balance < s.minCoinPayment {
err = errs.New("no valid payment methods found")
s.log.Debug(fmt.Sprintf("could not add promotional coupon for user %s", auth.User.ID.String()), zap.Error(Error.Wrap(err)))
return nil, Error.Wrap(err)
}
err = s.store.WithTx(ctx, func(ctx context.Context, tx DBTx) error {
p, err = tx.Projects().Insert(ctx,
&Project{
@ -884,21 +905,11 @@ func (s *Service) CreateProject(ctx context.Context, projectInfo ProjectInfo) (p
return nil
})
if err != nil {
return nil, err
return nil, Error.Wrap(err)
}
cards, err := s.accounts.CreditCards().List(ctx, auth.User.ID)
if err != nil {
s.log.Debug(fmt.Sprintf("could not add promotional coupon for user %s", auth.User.ID.String()), zap.Error(Error.Wrap(err)))
return p, nil
}
if len(cards) == 0 {
s.log.Debug(fmt.Sprintf("could not add promotional coupon for user %s - no payment methods", auth.User.ID.String()), zap.Error(Error.Wrap(err)))
return p, nil
}
err = s.accounts.Coupons().AddPromotionalCoupon(ctx, auth.User.ID, 2, 5500, memory.TB)
err = s.accounts.Coupons().AddPromotionalCoupon(ctx, auth.User.ID)
if err != nil {
s.log.Debug(fmt.Sprintf("could not add promotional coupon for user %s", auth.User.ID.String()), zap.Error(Error.Wrap(err)))
}

View File

@ -432,7 +432,11 @@ func New(log *zap.Logger, full *identity.FullIdentity, db DB,
pc.StorageTBPrice,
pc.EgressTBPrice,
pc.ObjectPrice,
pc.BonusRate)
pc.BonusRate,
pc.CouponValue,
pc.CouponDuration,
pc.CouponProjectLimit,
pc.MinCoinPayment)
if err != nil {
return nil, errs.Combine(err, peer.Close())

View File

@ -25,7 +25,7 @@ type Coupons interface {
// AddPromotionalCoupon is used to add a promotional coupon for specified users who already have
// a project and do not have a promotional coupon yet.
// And updates project limits to selected size.
AddPromotionalCoupon(ctx context.Context, userID uuid.UUID, duration int, amount int64, projectLimit memory.Size) error
AddPromotionalCoupon(ctx context.Context, userID uuid.UUID) error
// PopulatePromotionalCoupons is used to populate promotional coupons through all active users who already have
// a project, payment method and do not have a promotional coupon yet.

View File

@ -128,7 +128,14 @@ func (accounts accounts) Charges(ctx context.Context, userID uuid.UUID) (_ []pay
func (creditCards *creditCards) List(ctx context.Context, userID uuid.UUID) (_ []payments.CreditCard, err error) {
defer mon.Task()(&ctx, userID)(&err)
return []payments.CreditCard{}, nil
return []payments.CreditCard{{
ID: "pm_card_mastercard",
ExpMonth: 12,
ExpYear: 2050,
Brand: "Mastercard",
Last4: "4444",
IsDefault: true,
}}, nil
}
// Add is used to save new credit card, attach it to payment account and make it default.
@ -198,8 +205,8 @@ func (coupons *coupons) PopulatePromotionalCoupons(ctx context.Context, duration
// AddPromotionalCoupon is used to add a promotional coupon for specified users who already have
// a project and do not have a promotional coupon yet.
// And updates project limits to selected size.
func (coupons *coupons) AddPromotionalCoupon(ctx context.Context, userID uuid.UUID, duration int, amount int64, projectLimit memory.Size) (err error) {
defer mon.Task()(&ctx, userID, duration, amount, projectLimit)(&err)
func (coupons *coupons) AddPromotionalCoupon(ctx context.Context, userID uuid.UUID) (err error) {
defer mon.Task()(&ctx, userID)(&err)
return nil
}

View File

@ -4,6 +4,7 @@
package paymentsconfig
import (
"storj.io/common/memory"
"storj.io/storj/satellite/payments/stripecoinpayments"
)
@ -11,8 +12,12 @@ import (
type Config struct {
Provider string `help:"payments provider to use" default:""`
StripeCoinPayments stripecoinpayments.Config
StorageTBPrice string `help:"price user should pay for storing TB per month" default:"10"`
EgressTBPrice string `help:"price user should pay for each TB of egress" default:"45"`
ObjectPrice string `help:"price user should pay for each object stored in network per month" default:"0.0000022"`
BonusRate int64 `help:"amount of percents that user will earn as bonus credits by depositing in STORJ tokens" default:"10"`
StorageTBPrice string `help:"price user should pay for storing TB per month" default:"10"`
EgressTBPrice string `help:"price user should pay for each TB of egress" default:"45"`
ObjectPrice string `help:"price user should pay for each object stored in network per month" default:"0.0000022"`
BonusRate int64 `help:"amount of percents that user will earn as bonus credits by depositing in STORJ tokens" default:"10"`
CouponValue int64 `help:"coupon value in cents" default:"5500"`
CouponDuration int64 `help:"duration a new coupon is valid in months/billing cycles" default:"2"`
CouponProjectLimit memory.Size `help:"project limit to which increase to after applying the coupon, 0 B means not changing it from the default" default:"0 B"`
MinCoinPayment int64 `help:"minimum value of coin payments in cents before coupon is applied" default:"5000"`
}

View File

@ -187,8 +187,8 @@ func (coupons *coupons) PopulatePromotionalCoupons(ctx context.Context, duration
// AddPromotionalCoupon is used to add a promotional coupon for specified users who already have
// a project and do not have a promotional coupon yet.
// And updates project limits to selected size.
func (coupons *coupons) AddPromotionalCoupon(ctx context.Context, userID uuid.UUID, duration int, amount int64, projectLimit memory.Size) (err error) {
defer mon.Task()(&ctx, userID, duration, amount, projectLimit)(&err)
func (coupons *coupons) AddPromotionalCoupon(ctx context.Context, userID uuid.UUID) (err error) {
defer mon.Task()(&ctx, userID)(&err)
return Error.Wrap(coupons.service.db.Coupons().PopulatePromotionalCoupons(ctx, []uuid.UUID{userID}, duration, amount, projectLimit))
return Error.Wrap(coupons.service.db.Coupons().PopulatePromotionalCoupons(ctx, []uuid.UUID{userID}, int(coupons.service.CouponDuration), coupons.service.CouponValue, coupons.service.CouponProjectLimit))
}

View File

@ -34,6 +34,9 @@ var (
mon = monkit.Package()
)
// fetchLimit sets the maximum amount of items before we start paging on requests
const fetchLimit = 100
// Config stores needed information for payment service initialization.
type Config struct {
StripeSecretKey string `help:"stripe API secret key" default:""`
@ -62,6 +65,12 @@ type Service struct {
ObjectHourCents decimal.Decimal
// BonusRate amount of percents
BonusRate int64
// Coupon Values
CouponValue int64
CouponDuration int64
CouponProjectLimit memory.Size
// Minimum CoinPayment to create a coupon
MinCoinPayment int64
//Stripe Extended Features
AutoAdvance bool
@ -72,7 +81,7 @@ type Service struct {
}
// NewService creates a Service instance.
func NewService(log *zap.Logger, config Config, db DB, projectsDB console.Projects, usageDB accounting.ProjectAccounting, storageTBPrice, egressTBPrice, objectPrice string, bonusRate int64) (*Service, error) {
func NewService(log *zap.Logger, config Config, db DB, projectsDB console.Projects, usageDB accounting.ProjectAccounting, storageTBPrice, egressTBPrice, objectPrice string, bonusRate, couponValue, couponDuration int64, couponProjectLimit memory.Size, minCoinPayment int64) (*Service, error) {
backendConfig := &stripe.BackendConfig{
LeveledLogger: log.Sugar(),
}
@ -121,17 +130,21 @@ func NewService(log *zap.Logger, config Config, db DB, projectsDB console.Projec
egressByteCents := egressTBCents.Div(decimal.New(1000000000000, 0))
return &Service{
log: log,
db: db,
projectsDB: projectsDB,
usageDB: usageDB,
stripeClient: stripeClient,
coinPayments: coinPaymentsClient,
ByteHourCents: byteHourCents,
EgressByteCents: egressByteCents,
ObjectHourCents: objectHourCents,
BonusRate: bonusRate,
AutoAdvance: config.AutoAdvance,
log: log,
db: db,
projectsDB: projectsDB,
usageDB: usageDB,
stripeClient: stripeClient,
coinPayments: coinPaymentsClient,
ByteHourCents: byteHourCents,
EgressByteCents: egressByteCents,
ObjectHourCents: objectHourCents,
BonusRate: bonusRate,
CouponValue: couponValue,
CouponDuration: couponDuration,
CouponProjectLimit: couponProjectLimit,
MinCoinPayment: minCoinPayment,
AutoAdvance: config.AutoAdvance,
}, nil
}
@ -144,10 +157,9 @@ func (service *Service) Accounts() payments.Accounts {
func (service *Service) updateTransactionsLoop(ctx context.Context) (err error) {
defer mon.Task()(&ctx)(&err)
const limit = 100
before := time.Now()
txsPage, err := service.db.Transactions().ListPending(ctx, 0, limit, before)
txsPage, err := service.db.Transactions().ListPending(ctx, 0, fetchLimit, before)
if err != nil {
return err
}
@ -161,7 +173,7 @@ func (service *Service) updateTransactionsLoop(ctx context.Context) (err error)
return err
}
txsPage, err = service.db.Transactions().ListPending(ctx, txsPage.NextOffset, limit, before)
txsPage, err = service.db.Transactions().ListPending(ctx, txsPage.NextOffset, fetchLimit, before)
if err != nil {
return err
}
@ -218,8 +230,8 @@ func (service *Service) updateTransactions(ctx context.Context, ids TransactionA
cents := convertToCents(rate, &info.Received)
if cents >= 5000 {
err = service.Accounts().Coupons().AddPromotionalCoupon(ctx, userID, 2, 5500, memory.TB)
if cents >= service.MinCoinPayment {
err = service.Accounts().Coupons().AddPromotionalCoupon(ctx, userID)
if err != nil {
service.log.Error(fmt.Sprintf("could not add promotional coupon for user %s", userID.String()), zap.Error(err))
continue
@ -235,10 +247,9 @@ func (service *Service) updateTransactions(ctx context.Context, ids TransactionA
func (service *Service) updateAccountBalanceLoop(ctx context.Context) (err error) {
defer mon.Task()(&ctx)(&err)
const limit = 100
before := time.Now()
txsPage, err := service.db.Transactions().ListUnapplied(ctx, 0, limit, before)
txsPage, err := service.db.Transactions().ListUnapplied(ctx, 0, fetchLimit, before)
if err != nil {
return err
}
@ -258,7 +269,7 @@ func (service *Service) updateAccountBalanceLoop(ctx context.Context) (err error
return err
}
txsPage, err = service.db.Transactions().ListUnapplied(ctx, txsPage.NextOffset, limit, before)
txsPage, err = service.db.Transactions().ListUnapplied(ctx, txsPage.NextOffset, fetchLimit, before)
if err != nil {
return err
}
@ -364,8 +375,6 @@ func (service *Service) GetRate(ctx context.Context, curr1, curr2 coinpayments.C
func (service *Service) PrepareInvoiceProjectRecords(ctx context.Context, period time.Time) (err error) {
defer mon.Task()(&ctx)(&err)
const limit = 25
now := time.Now().UTC()
utc := period.UTC()
@ -376,7 +385,7 @@ func (service *Service) PrepareInvoiceProjectRecords(ctx context.Context, period
return Error.New("prepare is for past periods only")
}
projsPage, err := service.projectsDB.List(ctx, 0, limit, end)
projsPage, err := service.projectsDB.List(ctx, 0, fetchLimit, end)
if err != nil {
return Error.Wrap(err)
}
@ -390,7 +399,7 @@ func (service *Service) PrepareInvoiceProjectRecords(ctx context.Context, period
return Error.Wrap(err)
}
projsPage, err = service.projectsDB.List(ctx, projsPage.NextOffset, limit, end)
projsPage, err = service.projectsDB.List(ctx, projsPage.NextOffset, fetchLimit, end)
if err != nil {
return Error.Wrap(err)
}
@ -517,10 +526,9 @@ func (service *Service) createProjectRecords(ctx context.Context, projects []con
func (service *Service) InvoiceApplyProjectRecords(ctx context.Context) (err error) {
defer mon.Task()(&ctx)(&err)
const limit = 25
before := time.Now().UTC()
recordsPage, err := service.db.ProjectRecords().ListUnapplied(ctx, 0, limit, before)
recordsPage, err := service.db.ProjectRecords().ListUnapplied(ctx, 0, fetchLimit, before)
if err != nil {
return Error.Wrap(err)
}
@ -534,7 +542,7 @@ func (service *Service) InvoiceApplyProjectRecords(ctx context.Context) (err err
return Error.Wrap(err)
}
recordsPage, err = service.db.ProjectRecords().ListUnapplied(ctx, recordsPage.NextOffset, limit, before)
recordsPage, err = service.db.ProjectRecords().ListUnapplied(ctx, recordsPage.NextOffset, fetchLimit, before)
if err != nil {
return Error.Wrap(err)
}
@ -623,10 +631,9 @@ func (service *Service) createInvoiceItems(ctx context.Context, cusID, projName
func (service *Service) InvoiceApplyCoupons(ctx context.Context) (err error) {
defer mon.Task()(&ctx)(&err)
const limit = 25
before := time.Now().UTC()
usagePage, err := service.db.Coupons().ListUnapplied(ctx, 0, limit, before)
usagePage, err := service.db.Coupons().ListUnapplied(ctx, 0, fetchLimit, before)
if err != nil {
return Error.Wrap(err)
}
@ -640,7 +647,7 @@ func (service *Service) InvoiceApplyCoupons(ctx context.Context) (err error) {
return Error.Wrap(err)
}
usagePage, err = service.db.Coupons().ListUnapplied(ctx, usagePage.NextOffset, limit, before)
usagePage, err = service.db.Coupons().ListUnapplied(ctx, usagePage.NextOffset, fetchLimit, before)
if err != nil {
return Error.Wrap(err)
}
@ -728,10 +735,9 @@ func (service *Service) createInvoiceCouponItems(ctx context.Context, coupon pay
func (service *Service) InvoiceApplyCredits(ctx context.Context) (err error) {
defer mon.Task()(&ctx)(&err)
const limit = 25
before := time.Now().UTC()
spendingsPage, err := service.db.Credits().ListCreditsSpendingsPaged(ctx, int(CreditsSpendingStatusUnapplied), 0, limit, before)
spendingsPage, err := service.db.Credits().ListCreditsSpendingsPaged(ctx, int(CreditsSpendingStatusUnapplied), 0, fetchLimit, before)
if err != nil {
return Error.Wrap(err)
}
@ -745,7 +751,7 @@ func (service *Service) InvoiceApplyCredits(ctx context.Context) (err error) {
return Error.Wrap(err)
}
spendingsPage, err = service.db.Credits().ListCreditsSpendingsPaged(ctx, int(CreditsSpendingStatusUnapplied), spendingsPage.NextOffset, limit, before)
spendingsPage, err = service.db.Credits().ListCreditsSpendingsPaged(ctx, int(CreditsSpendingStatusUnapplied), spendingsPage.NextOffset, fetchLimit, before)
if err != nil {
return Error.Wrap(err)
}
@ -808,10 +814,9 @@ func (service *Service) createInvoiceCreditItem(ctx context.Context, spending Cr
func (service *Service) CreateInvoices(ctx context.Context) (err error) {
defer mon.Task()(&ctx)(&err)
const limit = 25
before := time.Now()
cusPage, err := service.db.Customers().List(ctx, 0, limit, before)
cusPage, err := service.db.Customers().List(ctx, 0, fetchLimit, before)
if err != nil {
return Error.Wrap(err)
}
@ -831,7 +836,7 @@ func (service *Service) CreateInvoices(ctx context.Context) (err error) {
return Error.Wrap(err)
}
cusPage, err = service.db.Customers().List(ctx, cusPage.NextOffset, limit, before)
cusPage, err = service.db.Customers().List(ctx, cusPage.NextOffset, fetchLimit, before)
if err != nil {
return Error.Wrap(err)
}

View File

@ -385,12 +385,15 @@ func (coupons *coupons) PopulatePromotionalCoupons(ctx context.Context, users []
return err
}
_, err = coupons.db.Update_Project_By_Id(ctx,
dbx.Project_Id(id.ProjectID[:]),
dbx.Project_Update_Fields{
UsageLimit: dbx.Project_UsageLimit(projectLimit.Int64()),
},
)
// if projectLimit specified, set it, else omit change the existing value
if projectLimit.Int64() > 0 {
_, err = coupons.db.Update_Project_By_Id(ctx,
dbx.Project_Id(id.ProjectID[:]),
dbx.Project_Update_Fields{
UsageLimit: dbx.Project_UsageLimit(projectLimit.Int64()),
},
)
}
if err != nil {
return err
}

View File

@ -397,9 +397,21 @@ identity.key-path: /root/.local/share/storj/identity/satellite/identity.key
# amount of percents that user will earn as bonus credits by depositing in STORJ tokens
# payments.bonus-rate: 10
# duration a new coupon is valid in months/billing cycles
# payments.coupon-duration: 2
# project limit to which increase to after applying the coupon, 0 B means not changing it from the default
# payments.coupon-project-limit: 0 B
# coupon value in cents
# payments.coupon-value: 5500
# price user should pay for each TB of egress
# payments.egress-tb-price: "45"
# minimum value of coin payments in cents before coupon is applied
# payments.min-coin-payment: 5000
# price user should pay for each object stored in network per month
# payments.object-price: "0.0000022"