diff --git a/components/common-go/db/types.go b/components/common-go/db/types.go index 0ff4c3cf8c5d84..e48339511aaae8 100644 --- a/components/common-go/db/types.go +++ b/components/common-go/db/types.go @@ -10,6 +10,7 @@ import ( "time" "github.com/relvacode/iso8601" + "google.golang.org/protobuf/types/known/timestamppb" ) func NewVarCharTime(t time.Time) VarcharTime { @@ -129,3 +130,11 @@ const ISO8601Format = "2006-01-02T15:04:05.000Z" func TimeToISO8601(t time.Time) string { return t.UTC().Format(ISO8601Format) } + +func VarcharTimeToTimestamppb(t VarcharTime) *timestamppb.Timestamp { + if !t.IsSet() { + return nil + } + + return timestamppb.New(t.Time()) +} diff --git a/components/server/ee/src/user/stripe-service.ts b/components/server/ee/src/user/stripe-service.ts index e3276bde26bf41..f8a388351accea 100644 --- a/components/server/ee/src/user/stripe-service.ts +++ b/components/server/ee/src/user/stripe-service.ts @@ -154,15 +154,12 @@ export class StripeService { taxInformation: customer.tax, }); } - const startOfNextMonth = new Date(new Date().toISOString().slice(0, 7) + "-01"); // First day of this month (YYYY-MM-01) - startOfNextMonth.setMonth(startOfNextMonth.getMonth() + 1); // Add one month await reportStripeOutcome("subscriptions_create", () => { return this.getStripe().subscriptions.create({ customer: customer.id, items: [{ price: priceId }], automatic_tax: { enabled: isAutomaticTaxSupported }, - billing_cycle_anchor: Math.round(startOfNextMonth.getTime() / 1000), }); }); } diff --git a/components/usage/pkg/apiv1/usage.go b/components/usage/pkg/apiv1/usage.go index 004438aa7d67c4..678316690ae676 100644 --- a/components/usage/pkg/apiv1/usage.go +++ b/components/usage/pkg/apiv1/usage.go @@ -191,20 +191,12 @@ func (s *UsageService) GetCostCenter(ctx context.Context, in *v1.GetCostCenterRe } func dbCostCenterToAPI(c db.CostCenter) *v1.CostCenter { - NextBillingTime := timestamppb.New(c.NextBillingTime.Time()) - if !c.NextBillingTime.IsSet() { - NextBillingTime = nil - } - BillingCycleStart := timestamppb.New(c.BillingCycleStart.Time()) - if !c.BillingCycleStart.IsSet() { - BillingCycleStart = nil - } return &v1.CostCenter{ AttributionId: string(c.ID), SpendingLimit: c.SpendingLimit, BillingStrategy: convertBillingStrategyToAPI(c.BillingStrategy), - NextBillingTime: NextBillingTime, - BillingCycleStart: BillingCycleStart, + NextBillingTime: common_db.VarcharTimeToTimestamppb(c.NextBillingTime), + BillingCycleStart: common_db.VarcharTimeToTimestamppb(c.BillingCycleStart), } } diff --git a/components/usage/pkg/db/cost_center.go b/components/usage/pkg/db/cost_center.go index 2215ebeebaa56c..45b1be024da1b7 100644 --- a/components/usage/pkg/db/cost_center.go +++ b/components/usage/pkg/db/cost_center.go @@ -186,8 +186,8 @@ func (c *CostCenterManager) UpdateCostCenter(ctx context.Context, newCC CostCent } newCC.BillingCycleStart = common_db.NewVarCharTime(now) - // we don't manage stripe billing cycle - newCC.NextBillingTime = common_db.VarcharTime{} + // set an informative nextBillingTime, even though we don't manage Stripe billing cycle + newCC.NextBillingTime = common_db.NewVarCharTime(now.AddDate(0, 1, 0)) } } else if isTeam { // Billing strategy is Other, and it remains unchanged @@ -214,8 +214,8 @@ func (c *CostCenterManager) UpdateCostCenter(ctx context.Context, newCC CostCent } newCC.BillingCycleStart = common_db.NewVarCharTime(now) - // we don't manage stripe billing cycle - newCC.NextBillingTime = common_db.VarcharTime{} + // set an informative nextBillingTime, even though we don't manage Stripe billing cycle + newCC.NextBillingTime = common_db.NewVarCharTime(now.AddDate(0, 1, 0)) } } else { return CostCenter{}, status.Errorf(codes.InvalidArgument, "Unknown attribution entity %s", string(attributionID)) @@ -319,9 +319,6 @@ func (c *CostCenterManager) ResetUsage(ctx context.Context, cc CostCenter) (Cost now := time.Now().UTC() - // Resetting the usage always resets the billing cycle start time - billingCycleStart := now - // Default to 1 month from now, if there's no nextBillingTime set on the record. nextBillingTime := now.AddDate(0, 1, 0) if cc.NextBillingTime.IsSet() { @@ -334,12 +331,12 @@ func (c *CostCenterManager) ResetUsage(ctx context.Context, cc CostCenter) (Cost return CostCenter{}, fmt.Errorf("failed to compute invocie usage record for AttributonID: %s: %w", cc.ID, err) } - // All fields on the new cost center remain the same, except for CreationTime and NextBillingTime + // All fields on the new cost center remain the same, except for BillingCycleStart, NextBillingTime, and CreationTime newCostCenter := CostCenter{ ID: cc.ID, SpendingLimit: spendingLimit, BillingStrategy: cc.BillingStrategy, - BillingCycleStart: common_db.NewVarCharTime(billingCycleStart), + BillingCycleStart: common_db.NewVarCharTime(now), NextBillingTime: common_db.NewVarCharTime(nextBillingTime), CreationTime: common_db.NewVarCharTime(now), } diff --git a/components/usage/pkg/db/cost_center_test.go b/components/usage/pkg/db/cost_center_test.go index ad7a2886ce3854..cc410a9eef2bc5 100644 --- a/components/usage/pkg/db/cost_center_test.go +++ b/components/usage/pkg/db/cost_center_test.go @@ -289,7 +289,7 @@ func TestSaveCostCenterMovedToStripe(t *testing.T) { teamCC, err = mnr.UpdateCostCenter(context.Background(), teamCC) require.NoError(t, err) require.Equal(t, db.CostCenter_Stripe, teamCC.BillingStrategy) - require.Equal(t, common_db.VarcharTime{}, teamCC.NextBillingTime) + require.Equal(t, teamCC.CreationTime.Time().AddDate(0, 1, 0), teamCC.NextBillingTime.Time()) require.Equal(t, int32(400050), teamCC.SpendingLimit) teamCC.BillingStrategy = db.CostCenter_Other diff --git a/components/usage/pkg/stripe/stripe.go b/components/usage/pkg/stripe/stripe.go index c6894a1fe62da0..a531d3c86a22e2 100644 --- a/components/usage/pkg/stripe/stripe.go +++ b/components/usage/pkg/stripe/stripe.go @@ -338,8 +338,6 @@ func (c *Client) CreateSubscription(ctx context.Context, customerID string, pric return nil, fmt.Errorf("no priceID specified") } - startOfNextMonth := getStartOfNextMonth(time.Now()) - params := &stripe.SubscriptionParams{ Customer: stripe.String(customerID), Items: []*stripe.SubscriptionItemsParams{ @@ -350,7 +348,6 @@ func (c *Client) CreateSubscription(ctx context.Context, customerID string, pric AutomaticTax: &stripe.SubscriptionAutomaticTaxParams{ Enabled: stripe.Bool(isAutomaticTaxSupported), }, - BillingCycleAnchor: stripe.Int64(startOfNextMonth.Unix()), } subscription, err := c.sc.Subscriptions.New(params) @@ -361,15 +358,6 @@ func (c *Client) CreateSubscription(ctx context.Context, customerID string, pric return subscription, err } -func getStartOfNextMonth(t time.Time) time.Time { - currentYear, currentMonth, _ := t.Date() - - firstOfMonth := time.Date(currentYear, currentMonth, 1, 0, 0, 0, 0, time.UTC) - startOfNextMonth := firstOfMonth.AddDate(0, 1, 0) - - return startOfNextMonth -} - func (c *Client) SetDefaultPaymentForCustomer(ctx context.Context, customerID string, setupIntentId string) (*stripe.Customer, error) { if customerID == "" { return nil, fmt.Errorf("no customerID specified") diff --git a/components/usage/pkg/stripe/stripe_test.go b/components/usage/pkg/stripe/stripe_test.go index 8a4f3cc6a6d1b9..c3e9e4cb79dfa5 100644 --- a/components/usage/pkg/stripe/stripe_test.go +++ b/components/usage/pkg/stripe/stripe_test.go @@ -7,7 +7,6 @@ package stripe import ( "fmt" "testing" - "time" "github.com/gitpod-io/gitpod/usage/pkg/db" @@ -91,9 +90,3 @@ func TestCustomerQueriesForTeamIds_MultipleQueries(t *testing.T) { }) } } - -func TestStartOfNextMonth(t *testing.T) { - ts := time.Date(2022, 10, 1, 0, 0, 0, 0, time.UTC) - - require.Equal(t, time.Date(2022, 11, 1, 0, 0, 0, 0, time.UTC), getStartOfNextMonth(ts)) -}