storj/cmd/satellite/billing.go
Kaloyan Raev 830817ec0d cmd/storj-sim: run gateway without --access flag
This makes it possible to remove of this obsolete flag from the
multi-tenant gateway.

As a consequence, displaying the GATEWAY_0_ACCESS env var will always
require a running storj-sim. Until now, it was required only the first
time. Then the value was stored in the 'access' config. But this is now
not possible anymore.

The changes in StripeMock are required to fix failures in integration
tests. StripeMock is in-memory and its data does not survive restarts of
storj-sim. The second and following starts of storj-sim had invalid
state of StripeMock, which failed requests that were required to
populate the GATEWAY_0_ACCESS env var. The changes in StripeMock makes
it repopulate the Stripe customers from the database.

Change-Id: I981a208172b76577f12ecdaae485f5ae4ea269bc
2020-10-13 14:45:04 +00:00

159 lines
4.0 KiB
Go

// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
package main
import (
"context"
"strconv"
"strings"
"time"
"github.com/zeebo/errs"
"go.uber.org/zap"
"storj.io/common/storj"
"storj.io/common/uuid"
"storj.io/storj/private/dbutil"
"storj.io/storj/satellite"
"storj.io/storj/satellite/payments/stripecoinpayments"
"storj.io/storj/satellite/satellitedb"
"storj.io/storj/satellite/satellitedb/dbx"
)
func runBillingCmd(cmdFunc func(*stripecoinpayments.Service, *dbx.DB) error) error {
// Open SatelliteDB for the Payment Service
logger := zap.L()
db, err := satellitedb.New(logger.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
}
defer func() {
err = errs.Combine(err, dbxDB.Close())
}()
logger.Debug("Connected to:", zap.String("db source", source))
payments, err := setupPayments(logger, db)
if err != nil {
return err
}
return cmdFunc(payments, dbxDB)
}
func setupPayments(log *zap.Logger, db satellite.DB) (*stripecoinpayments.Service, error) {
pc := runCfg.Payments
var stripeClient stripecoinpayments.StripeClient
switch pc.Provider {
default:
stripeClient = stripecoinpayments.NewStripeMock(
storj.NodeID{},
db.StripeCoinPayments().Customers(),
db.Console().Users(),
)
case "stripecoinpayments":
stripeClient = stripecoinpayments.NewStripeClient(log, pc.StripeCoinPayments)
}
return stripecoinpayments.NewService(
log.Named("payments.stripe:service"),
stripeClient,
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,
pc.PaywallProportion)
}
// parseBillingPeriodFromString parses provided date string and returns corresponding time.Time.
func parseBillingPeriod(s string) (time.Time, error) {
values := strings.Split(s, "/")
if len(values) != 2 {
return time.Time{}, errs.New("invalid date format %s, use mm/yyyy", s)
}
month, err := strconv.ParseInt(values[0], 10, 64)
if err != nil {
return time.Time{}, errs.New("can not parse month: %v", err)
}
year, err := strconv.ParseInt(values[1], 10, 64)
if err != nil {
return time.Time{}, errs.New("can not parse year: %v", err)
}
date := time.Date(int(year), time.Month(month), 1, 0, 0, 0, 0, time.UTC)
if date.Year() != int(year) || date.Month() != time.Month(month) || date.Day() != 1 {
return date, errs.New("dates mismatch have %s result %s", s, date)
}
return date, nil
}
// 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) {
return runBillingCmd(func(payments *stripecoinpayments.Service, dbxDB *dbx.DB) error {
accounts := payments.Accounts()
rows, err := dbxDB.Query(ctx, "SELECT id, email FROM users WHERE id NOT IN (SELECT user_id from stripe_customers) AND users.status=1")
if err != nil {
return err
}
defer func() {
err = errs.Combine(err, rows.Close())
}()
var n int64
for rows.Next() {
n++
var user userData
err := rows.Scan(&user.ID, &user.Email)
if err != nil {
return err
}
err = accounts.Setup(ctx, user.ID, user.Email)
if err != nil {
return err
}
}
zap.L().Info("Ensured Stripe-Customer", zap.Int64("created", n))
return err
})
}