web,satellite: Remove paywall-related functionality

Because of our free/paid tier plan, we do not need a paywall anymore. We
have not used it in a while, but still have leftover code laying around.

Change-Id: Iaea8c39faf042a2f7a6b837727bb135c8bdf2907
This commit is contained in:
Moby von Briesen 2021-06-28 18:57:41 -04:00
parent 4267a958d4
commit 4e95d27033
22 changed files with 10 additions and 307 deletions

View File

@ -67,8 +67,7 @@ func setupPayments(log *zap.Logger, db satellite.DB) (*stripecoinpayments.Servic
pc.CouponValue,
pc.CouponDuration.IntPointer(),
pc.CouponProjectLimit,
pc.MinCoinPayment,
pc.PaywallProportion)
pc.MinCoinPayment)
}
// parseBillingPeriodFromString parses provided date string and returns corresponding time.Time.

View File

@ -137,8 +137,7 @@ func NewAdmin(log *zap.Logger, full *identity.FullIdentity, db DB,
pc.CouponValue,
pc.CouponDuration.IntPointer(),
pc.CouponProjectLimit,
pc.MinCoinPayment,
pc.PaywallProportion)
pc.MinCoinPayment)
if err != nil {
return nil, errs.Combine(err, peer.Close())

View File

@ -525,8 +525,7 @@ func NewAPI(log *zap.Logger, full *identity.FullIdentity, db DB,
pc.CouponValue,
pc.CouponDuration.IntPointer(),
pc.CouponProjectLimit,
pc.MinCoinPayment,
pc.PaywallProportion)
pc.MinCoinPayment)
if err != nil {
return nil, errs.Combine(err, peer.Close())

View File

@ -15,7 +15,6 @@ import (
"github.com/zeebo/errs"
"go.uber.org/zap"
"storj.io/common/uuid"
"storj.io/storj/satellite/console"
)
@ -309,34 +308,6 @@ func (p *Payments) TokenDeposit(w http.ResponseWriter, r *http.Request) {
}
}
// PaywallEnabled returns is paywall enabled status.
func (p *Payments) PaywallEnabled(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var err error
defer mon.Task()(&ctx)(&err)
vars := mux.Vars(r)
reqID := vars["userId"]
if reqID == "" {
p.serveJSONError(w, http.StatusBadRequest, err)
return
}
userID, err := uuid.FromString(reqID)
if err != nil {
p.serveJSONError(w, http.StatusInternalServerError, err)
return
}
paywallEnabled := p.service.PaywallEnabled(userID)
err = json.NewEncoder(w).Encode(paywallEnabled)
if err != nil {
p.log.Error("failed to write json paywall enabled response", zap.Error(ErrPaymentsAPI.Wrap(err)))
}
}
// serveJSONError writes JSON error to response output stream.
func (p *Payments) serveJSONError(w http.ResponseWriter, status int, err error) {
if status == http.StatusInternalServerError {

View File

@ -1,37 +0,0 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
package consoleapi_test
import (
"testing"
"github.com/stretchr/testify/assert"
"go.uber.org/zap"
"storj.io/common/testcontext"
"storj.io/common/uuid"
"storj.io/storj/private/testplanet"
"storj.io/storj/satellite"
)
// TestPaywallEnabled ensures that the Paywall A/B test config works.
func TestPaywallEnabled(t *testing.T) {
lowUUID := uuid.UUID{0}
highUUID := uuid.UUID{255}
testplanet.Run(t, testplanet.Config{
SatelliteCount: 3, Reconfigure: testplanet.Reconfigure{
Satellite: func(log *zap.Logger, index int, config *satellite.Config) {
proportions := []float64{0, .5, 1}
config.Payments.PaywallProportion = proportions[index]
},
},
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
assert.False(t, planet.Satellites[0].API.Console.Service.PaywallEnabled(highUUID))
assert.False(t, planet.Satellites[0].API.Console.Service.PaywallEnabled(lowUUID))
assert.False(t, planet.Satellites[1].API.Console.Service.PaywallEnabled(highUUID))
assert.True(t, planet.Satellites[1].API.Console.Service.PaywallEnabled(lowUUID))
assert.True(t, planet.Satellites[2].API.Console.Service.PaywallEnabled(highUUID))
assert.True(t, planet.Satellites[2].API.Console.Service.PaywallEnabled(lowUUID))
})
}

View File

@ -93,8 +93,7 @@ func TestGraphqlMutation(t *testing.T) {
pc.CouponValue,
pc.CouponDuration.IntPointer(),
pc.CouponProjectLimit,
pc.MinCoinPayment,
pc.PaywallProportion)
pc.MinCoinPayment)
require.NoError(t, err)
service, err := console.NewService(

View File

@ -77,8 +77,7 @@ func TestGraphqlQuery(t *testing.T) {
pc.CouponValue,
pc.CouponDuration.IntPointer(),
pc.CouponProjectLimit,
pc.MinCoinPayment,
pc.PaywallProportion)
pc.MinCoinPayment)
require.NoError(t, err)
service, err := console.NewService(

View File

@ -240,7 +240,6 @@ func NewServer(logger *zap.Logger, config Config, service *console.Service, mail
paymentsRouter.HandleFunc("/account", paymentController.SetupAccount).Methods(http.MethodPost)
paymentsRouter.HandleFunc("/billing-history", paymentController.BillingHistory).Methods(http.MethodGet)
paymentsRouter.HandleFunc("/tokens/deposit", paymentController.TokenDeposit).Methods(http.MethodPost)
paymentsRouter.HandleFunc("/paywall-enabled/{userId}", paymentController.PaywallEnabled).Methods(http.MethodGet)
bucketsController := consoleapi.NewBuckets(logger, service)
bucketsRouter := router.PathPrefix("/api/v0/buckets").Subrouter()

View File

@ -244,10 +244,6 @@ func (paymentService PaymentsService) AddCreditCard(ctx context.Context, creditC
return Error.Wrap(err)
}
if !paymentService.service.accounts.PaywallEnabled(auth.User.ID) {
return nil
}
return nil
}
@ -493,16 +489,6 @@ func (paymentService PaymentsService) checkProjectInvoicingStatus(ctx context.Co
func (paymentService PaymentsService) AddPromotionalCoupon(ctx context.Context, userID uuid.UUID) (err error) {
defer mon.Task()(&ctx, userID)(&err)
if paymentService.service.accounts.PaywallEnabled(userID) {
cards, err := paymentService.ListCreditCards(ctx)
if err != nil {
return Error.Wrap(err)
}
if len(cards) == 0 {
return Error.New("user don't have a payment method")
}
}
return paymentService.service.accounts.Coupons().AddPromotionalCoupon(ctx, userID)
}
@ -686,10 +672,6 @@ func (s *Service) ActivateAccount(ctx context.Context, activationToken string) (
}
s.auditLog(ctx, "activate account", &user.ID, user.Email)
if s.accounts.PaywallEnabled(user.ID) {
return nil
}
s.analytics.TrackAccountVerified(user.ID, user.Email)
return nil
@ -995,32 +977,6 @@ func (s *Service) CreateProject(ctx context.Context, projectInfo ProjectInfo) (p
return nil, ErrProjLimit.Wrap(err)
}
if s.accounts.PaywallEnabled(auth.User.ID) {
cards, err := s.accounts.CreditCards().List(ctx, auth.User.ID)
if err != nil {
s.log.Debug(fmt.Sprintf("could not list credit cards 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 get balance for user %s", auth.User.ID.String()), zap.Error(Error.Wrap(err)))
return nil, Error.Wrap(err)
}
coupons, err := s.accounts.Coupons().ListByUserID(ctx, auth.User.ID)
if err != nil {
s.log.Debug(fmt.Sprintf("could not list coupons for user %s", auth.User.ID.String()), zap.Error(Error.Wrap(err)))
return nil, Error.Wrap(err)
}
if len(cards) == 0 && balance.Coins < s.minCoinPayment && len(coupons) == 0 {
err = errs.New("no valid payment methods found")
s.log.Debug(fmt.Sprintf("could not create project for user %s", auth.User.ID.String()), zap.Error(Error.Wrap(err)))
return nil, Error.Wrap(err)
}
}
var projectID uuid.UUID
err = s.store.WithTx(ctx, func(ctx context.Context, tx DBTx) error {
p, err = tx.Projects().Insert(ctx,
@ -1807,9 +1763,3 @@ func findMembershipByProjectID(memberships []ProjectMember, projectID uuid.UUID)
}
return ProjectMember{}, false
}
// PaywallEnabled returns a true if a credit card or account
// balance is required to create projects.
func (s *Service) PaywallEnabled(userID uuid.UUID) bool {
return s.accounts.PaywallEnabled(userID)
}

View File

@ -428,8 +428,7 @@ func New(log *zap.Logger, full *identity.FullIdentity, db DB,
pc.CouponValue,
pc.CouponDuration.IntPointer(),
pc.CouponProjectLimit,
pc.MinCoinPayment,
pc.PaywallProportion)
pc.MinCoinPayment)
if err != nil {
return nil, errs.Combine(err, peer.Close())
}

View File

@ -47,8 +47,4 @@ type Accounts interface {
// Coupons exposes all needed functionality to manage coupons.
Coupons() Coupons
// PaywallEnabled returns a true if a credit card or account
// balance is required to create projects
PaywallEnabled(uuid.UUID) bool
}

View File

@ -26,7 +26,6 @@ type Config struct {
NodeRepairBandwidthPrice int64 `help:"price node receive for storing TB of repair in cents" default:"1000"`
NodeAuditBandwidthPrice int64 `help:"price node receive for storing TB of audit in cents" default:"1000"`
NodeDiskSpacePrice int64 `help:"price node receive for storing disk space in cents/TB" default:"150"`
PaywallProportion float64 `help:"proportion of users which require a balance to create projects [0-1]" default:"0" testDefault:"1"`
}
// CouponDuration is a configuration struct that keeps details about default

View File

@ -234,14 +234,3 @@ func (accounts *accounts) StorjTokens() payments.StorjTokens {
func (accounts *accounts) Coupons() payments.Coupons {
return &coupons{service: accounts.service}
}
// PaywallEnabled returns a true if a credit card or account
// balance is required to create projects.
func (accounts *accounts) PaywallEnabled(userID uuid.UUID) bool {
return BytesAreWithinProportion(userID, accounts.service.PaywallProportion)
}
// BytesAreWithinProportion returns true if first byte is less than the normalized proportion [0..1].
func BytesAreWithinProportion(uuidBytes [16]byte, proportion float64) bool {
return int(uuidBytes[0]) < int(proportion*256)
}

View File

@ -1,39 +0,0 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
package stripecoinpayments_test
import (
"testing"
"github.com/stretchr/testify/assert"
"storj.io/common/uuid"
"storj.io/storj/satellite/payments/stripecoinpayments"
)
func TestBytesAreWithinProportion(t *testing.T) {
f := stripecoinpayments.BytesAreWithinProportion
assert.False(t, f(uuid.UUID{0}, 0.0))
assert.False(t, f(uuid.UUID{255}, 0.25))
assert.False(t, f(uuid.UUID{192}, 0.25))
assert.False(t, f(uuid.UUID{128}, 0.25))
assert.False(t, f(uuid.UUID{64}, 0.25))
assert.True(t, f(uuid.UUID{63, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, 0.25))
assert.True(t, f(uuid.UUID{32}, 0.25))
assert.False(t, f(uuid.UUID{129}, 0.5))
assert.False(t, f(uuid.UUID{128}, 0.5))
assert.True(t, f(uuid.UUID{127, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, 0.5))
assert.True(t, f(uuid.UUID{127}, 0.5))
assert.False(t, f(uuid.UUID{255}, 0.75))
assert.False(t, f(uuid.UUID{192}, 0.75))
assert.True(t, f(uuid.UUID{191, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, 0.75))
assert.True(t, f(uuid.UUID{128}, 0.75))
assert.True(t, f(uuid.UUID{64}, 0.75))
assert.True(t, f(uuid.UUID{32}, 0.75))
assert.True(t, f(uuid.UUID{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, 1.0))
}

View File

@ -83,13 +83,12 @@ type Service struct {
rates coinpayments.CurrencyRateInfos
ratesErr error
listingLimit int
nowFn func() time.Time
PaywallProportion float64
listingLimit int
nowFn func() time.Time
}
// NewService creates a Service instance.
func NewService(log *zap.Logger, stripeClient StripeClient, config Config, db DB, projectsDB console.Projects, usageDB accounting.ProjectAccounting, storageTBPrice, egressTBPrice, objectPrice string, bonusRate, couponValue int64, couponDuration *int64, couponProjectLimit memory.Size, minCoinPayment int64, paywallProportion float64) (*Service, error) {
func NewService(log *zap.Logger, stripeClient StripeClient, config Config, db DB, projectsDB console.Projects, usageDB accounting.ProjectAccounting, storageTBPrice, egressTBPrice, objectPrice string, bonusRate, couponValue int64, couponDuration *int64, couponProjectLimit memory.Size, minCoinPayment int64) (*Service, error) {
coinPaymentsClient := coinpayments.NewClient(
coinpayments.Credentials{
@ -135,7 +134,6 @@ func NewService(log *zap.Logger, stripeClient StripeClient, config Config, db DB
AutoAdvance: config.AutoAdvance,
listingLimit: config.ListingLimit,
nowFn: time.Now,
PaywallProportion: paywallProportion,
}, nil
}

View File

@ -577,9 +577,6 @@ identity.key-path: /root/.local/share/storj/identity/satellite/identity.key
# price user should pay for each object stored in network per month
# payments.object-price: "0"
# proportion of users which require a balance to create projects [0-1]
# payments.paywall-proportion: 0
# payments provider to use
# payments.provider: ""

View File

@ -253,25 +253,4 @@ export class PaymentsHttpApi implements PaymentsApi {
return new TokenDeposit(result.amount, result.address, result.link);
}
/**
* Indicates if paywall is enabled.
*
* @param userId
* @throws Error
*/
public async getPaywallStatus(userId: string): Promise<boolean> {
const path = `${this.ROOT_PATH}/paywall-enabled/${userId}`;
const response = await this.client.get(path);
if (!response.ok) {
if (response.status === 401) {
throw new ErrorUnauthorized();
}
throw new Error('can not get paywall status');
}
return await response.json();
}
}

View File

@ -27,7 +27,6 @@ export const PAYMENTS_MUTATIONS = {
SET_PREVIOUS_ROLLUP_PRICE: 'SET_PREVIOUS_ROLLUP_PRICE',
SET_PRICE_SUMMARY: 'SET_PRICE_SUMMARY',
SET_PRICE_SUMMARY_FOR_SELECTED_PROJECT: 'SET_PRICE_SUMMARY_FOR_SELECTED_PROJECT',
SET_PAYWALL_ENABLED_STATUS: 'SET_PAYWALL_ENABLED_STATUS',
};
export const PAYMENTS_ACTIONS = {
@ -45,7 +44,6 @@ export const PAYMENTS_ACTIONS = {
GET_PROJECT_USAGE_AND_CHARGES: 'getProjectUsageAndCharges',
GET_PROJECT_USAGE_AND_CHARGES_CURRENT_ROLLUP: 'getProjectUsageAndChargesCurrentRollup',
GET_PROJECT_USAGE_AND_CHARGES_PREVIOUS_ROLLUP: 'getProjectUsageAndChargesPreviousRollup',
GET_PAYWALL_ENABLED_STATUS: 'getPaywallEnabledStatus',
};
const {
@ -59,7 +57,6 @@ const {
SET_PROJECT_USAGE_AND_CHARGES,
SET_PRICE_SUMMARY,
SET_PRICE_SUMMARY_FOR_SELECTED_PROJECT,
SET_PAYWALL_ENABLED_STATUS,
} = PAYMENTS_MUTATIONS;
const {
@ -76,7 +73,6 @@ const {
MAKE_TOKEN_DEPOSIT,
GET_PROJECT_USAGE_AND_CHARGES_CURRENT_ROLLUP,
GET_PROJECT_USAGE_AND_CHARGES_PREVIOUS_ROLLUP,
GET_PAYWALL_ENABLED_STATUS,
} = PAYMENTS_ACTIONS;
export class PaymentsState {
@ -91,7 +87,6 @@ export class PaymentsState {
public priceSummaryForSelectedProject: number = 0;
public startDate: Date = new Date();
public endDate: Date = new Date();
public isPaywallEnabled: boolean = true;
}
/**
@ -170,9 +165,6 @@ export function makePaymentsModule(api: PaymentsApi): StoreModule<PaymentsState>
state.priceSummaryForSelectedProject = usageAndChargesForSelectedProject.summary();
},
[SET_PAYWALL_ENABLED_STATUS](state: PaymentsState, isPaywallEnabled: boolean): void {
state.isPaywallEnabled = isPaywallEnabled;
},
[CLEAR](state: PaymentsState) {
state.balance = new AccountBalance();
state.paymentsHistory = [];
@ -181,7 +173,6 @@ export function makePaymentsModule(api: PaymentsApi): StoreModule<PaymentsState>
state.creditCards = [];
state.startDate = new Date();
state.endDate = new Date();
state.isPaywallEnabled = true;
},
},
actions: {
@ -255,11 +246,6 @@ export function makePaymentsModule(api: PaymentsApi): StoreModule<PaymentsState>
commit(SET_PROJECT_USAGE_AND_CHARGES, usageAndCharges);
commit(SET_PRICE_SUMMARY, usageAndCharges);
},
[GET_PAYWALL_ENABLED_STATUS]: async function({commit, rootGetters}: any): Promise<void> {
const isPaywallEnabled: boolean = await api.getPaywallStatus(rootGetters.user.id);
commit(SET_PAYWALL_ENABLED_STATUS, isPaywallEnabled);
},
},
getters: {
canUserCreateFirstProject: (state: PaymentsState): boolean => {

View File

@ -69,14 +69,6 @@ export interface PaymentsApi {
* @throws Error
*/
makeTokenDeposit(amount: number): Promise<TokenDeposit>;
/**
* Indicates if paywall is enabled.
*
* @param userId
* @throws Error
*/
getPaywallStatus(userId: string): Promise<boolean>;
}
export class AccountBalance {

View File

@ -47,7 +47,6 @@ import { LocalData } from '@/utils/localData';
import { MetaUtils } from '@/utils/meta';
const {
GET_PAYWALL_ENABLED_STATUS,
SETUP_ACCOUNT,
GET_BALANCE,
GET_PROJECT_USAGE_AND_CHARGES_CURRENT_ROLLUP,
@ -92,12 +91,6 @@ export default class DashboardArea extends Vue {
return;
}
try {
await this.$store.dispatch(GET_PAYWALL_ENABLED_STATUS);
} catch (error) {
await this.$notify.error(`Unable to get paywall enabled status. ${error.message}`);
}
try {
await this.$store.dispatch(SETUP_ACCOUNT);
} catch (error) {

View File

@ -23,19 +23,13 @@ const paymentsModule = makePaymentsModule(paymentsApi);
const store = new Vuex.Store({ modules: { projectsModule, paymentsModule }});
describe('OverviewStep.vue', (): void => {
it('renders correctly', async (): Promise<void> => {
it('renders correctly', (): void => {
const wrapper = mount(OverviewStep, {
localVue,
router,
store,
});
await store.commit(PAYMENTS_MUTATIONS.SET_PAYWALL_ENABLED_STATUS, true);
expect(wrapper).toMatchSnapshot();
await store.commit(PAYMENTS_MUTATIONS.SET_PAYWALL_ENABLED_STATUS, false);
expect(wrapper).toMatchSnapshot();
});
});

View File

@ -57,61 +57,3 @@ exports[`OverviewStep.vue renders correctly 1`] = `
<div class="overview-area__skip-button container blue-white" style="width: 100px; height: inherit;"><span class="label">Skip</span></div>
</div>
`;
exports[`OverviewStep.vue renders correctly 2`] = `
<div class="overview-area">
<h2 class="overview-area__header">Welcome to Storj DCS</h2>
<div class="overview-area__continue__area"><img src="@/../static/images/onboardingTour/continue-bg.png" alt="continue image" class="overview-area__continue__img">
<div class="overview-area__continue__text-area">
<div class="overview-area__continue__container">
<p class="overview-area__label continue-label server-side-label">Server-Side Encrypted</p>
<h3 class="overview-area__continue__header">Upload in Browser</h3>
<p class="overview-area__continue__text">
Start uploading files in the browser and instantly see how your data gets distributed over our
global storage network. You can always use other upload methods later.
</p>
<div class="overview-area__continue__button container" style="width: 234px; height: 48px;"><span class="label">Upload in Browser</span></div>
</div>
</div>
</div>
<h3 class="overview-area__second-header">More Ways To Upload</h3>
<div class="overview-area__path-area">
<div class="overview-area__path-section"><svg width="57" height="57" viewBox="0 0 57 57" fill="none" xmlns="http://www.w3.org/2000/svg" class="overview-area__path-section__icon">
<g clip-path="url(#clip0)">
<path d="M28.5606 41.3316L4.18677 49.5986V14.7212L28.5606 19.6944V41.3316Z" fill="#0046A5"></path>
<path d="M27.1045 18.0503L21.6953 18.4123L4.18676 14.7212L21.5304 11.8601L26.9508 13.6279L27.1045 18.0503Z" fill="#001D44"></path>
<path d="M0.61591 7.56298L0.631838 7.55864L4.1877 6.32031L28.5606 0.2229V11.063V55.7488L20.2017 54.1285V12.0347L4.1877 14.7212V49.6514L0.631838 48.414V15.465L0.61591 15.4674V7.56298Z" fill="#4494FF"></path>
<path d="M43.4352 46.8003L28.5606 55.5275V0L43.4352 8.72781V46.8003Z" fill="#0046A5"></path>
<path d="M48.6571 44.3283L56.4912 40.6448V15.3264L48.6571 11.644V44.3283Z" fill="#0046A5"></path>
<path d="M43.4352 44.1108L48.6571 44.2272V11.644L43.4352 11.7614V44.1108Z" fill="#4494FF"></path>
</g>
<defs>
<clipPath id="clip0">
<rect width="57" height="57" fill="white"></rect>
</clipPath>
</defs>
</svg>
<p class="overview-area__label server-side-label">Server-Side Encrypted</p>
<h4 class="overview-area__path-section__title">GatewayMT</h4>
<p class="overview-area__path-section__text">Backwards S3-Compatible API for uploading data programatically.</p>
<div class="overview-area__path-section__button container blue-white" style="height: inherit;"><span class="label">Continue</span></div>
</div>
<div class="overview-area__path-section"><img src="@/../static/images/onboardingTour/command-line-icon.png" alt="uplink icon">
<p class="overview-area__label">End-to-End Encrypted</p>
<h4 class="overview-area__path-section__title">Uplink CLI</h4>
<p class="overview-area__path-section__text">Natively installed client for interacting with the Storj Network.</p>
<div class="overview-area__path-section__button container blue-white" style="height: inherit;"><span class="label">Continue</span></div>
</div>
<div class="overview-area__path-section"><img src="@/../static/images/onboardingTour/rclone.png" alt="rclone image" class="overview-area__path-section__icon">
<p class="overview-area__label">End-to-End Encrypted</p>
<h4 class="overview-area__path-section__title">Sync with Rclone</h4>
<p class="overview-area__path-section__text">Map your filesystem to the decentralized cloud.</p> <a href="https://docs.storj.io/how-tos/sync-files-with-rclone" target="_blank" rel="noopener noreferrer" class="overview-area__path-section__button">
Continue
</a>
</div>
</div> <a href="https://storj.io/integrations/" target="_blank" rel="noopener noreferrer" class="overview-area__integrations-button">
More Integrations
</a>
<div class="overview-area__skip-button container blue-white" style="width: 100px; height: inherit;"><span class="label">Skip</span></div>
</div>
`;