Skip to content

Commit

Permalink
Fixes Stripe Billing Anchor Edge Case (#3201)
Browse files Browse the repository at this point in the history
  • Loading branch information
nickzelei authored Jan 29, 2025

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent be0adca commit b97eaab
Showing 10 changed files with 51 additions and 18 deletions.
2 changes: 1 addition & 1 deletion backend/internal/cmds/mgmt/serve/connect/cmd.go
Original file line number Diff line number Diff line change
@@ -86,7 +86,7 @@ import (
promv1 "github.com/prometheus/client_golang/api/prometheus/v1"
promconfig "github.com/prometheus/common/config"

stripeapiclient "github.com/stripe/stripe-go/v79/client"
stripeapiclient "github.com/stripe/stripe-go/v81/client"
)

func NewCmd() *cobra.Command {
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ import (
integrationtests_test "github.com/nucleuscloud/neosync/backend/pkg/integration-test"
"github.com/nucleuscloud/neosync/internal/gotypeutil"
"github.com/stretchr/testify/require"
"github.com/stripe/stripe-go/v79"
"github.com/stripe/stripe-go/v81"
)

func (s *IntegrationTestSuite) Test_AnonymizeService_AnonymizeMany() {
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/stripe/stripe-go/v79"
"github.com/stripe/stripe-go/v81"
)

var (
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@ import (
"github.com/nucleuscloud/neosync/backend/internal/neosyncdb"
"github.com/nucleuscloud/neosync/backend/internal/userdata"
"github.com/nucleuscloud/neosync/internal/billing"
"github.com/stripe/stripe-go/v79"
"github.com/stripe/stripe-go/v81"
"google.golang.org/protobuf/types/known/timestamppb"
)

@@ -240,9 +240,6 @@ func (s *Service) GetAccountBillingCheckoutSession(
if err != nil {
return nil, err
}
if err != nil {
return nil, err
}

// retrieve the account, creates a customer id if one doesn't already exist
account, err := s.db.UpsertStripeCustomerId(
Original file line number Diff line number Diff line change
@@ -23,7 +23,7 @@ import (
"github.com/nucleuscloud/neosync/backend/internal/userdata"
"github.com/nucleuscloud/neosync/backend/internal/version"
"github.com/nucleuscloud/neosync/internal/billing"
"github.com/stripe/stripe-go/v79"
"github.com/stripe/stripe-go/v81"
"golang.org/x/sync/errgroup"
"google.golang.org/protobuf/types/known/timestamppb"
)
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -61,7 +61,7 @@ require (
github.com/spf13/cobra v1.8.1
github.com/spf13/viper v1.19.0
github.com/stretchr/testify v1.10.0
github.com/stripe/stripe-go/v79 v79.12.0
github.com/stripe/stripe-go/v81 v81.3.0
github.com/testcontainers/testcontainers-go v0.35.0
github.com/testcontainers/testcontainers-go/modules/mongodb v0.35.0
github.com/testcontainers/testcontainers-go/modules/mssql v0.35.0
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -2103,8 +2103,8 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stripe/stripe-go/v79 v79.12.0 h1:HQs/kxNEB3gYA7FnkSFkp0kSOeez0fsmCWev6SxftYs=
github.com/stripe/stripe-go/v79 v79.12.0/go.mod h1:cuH6X0zC8peY6f1AubHwgJ/fJSn2dh5pfiCr6CjyKVU=
github.com/stripe/stripe-go/v81 v81.3.0 h1:tvNgK3RcX0oKE/hB6oifpa+InEA/UVDbU/Xjwydz+nk=
github.com/stripe/stripe-go/v81 v81.3.0/go.mod h1:C/F4jlmnGNacvYtBp/LUHCvVUJEZffFQCobkzwY1WOo=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/testcontainers/testcontainers-go v0.35.0 h1:uADsZpTKFAtp8SLK+hMwSaa+X+JiERHtd4sQAFmXeMo=
2 changes: 1 addition & 1 deletion internal/billing/mock_Interface.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 7 additions & 5 deletions internal/billing/stripe-billing.go
Original file line number Diff line number Diff line change
@@ -5,8 +5,8 @@ import (
"log/slog"
"time"

"github.com/stripe/stripe-go/v79"
stripeapiclient "github.com/stripe/stripe-go/v79/client"
"github.com/stripe/stripe-go/v81"
stripeapiclient "github.com/stripe/stripe-go/v81/client"
)

type SubscriptionIter interface {
@@ -130,9 +130,11 @@ func (c *Client) NewMeterEvent(req *MeterEventRequest) (*stripe.BillingMeterEven
}

func getNextMonthBillingCycleAnchor(date time.Time) int64 {
// Calculate the first day of the next month
nextMonth := date.AddDate(0, 1, 0)
firstOfNextMonth := time.Date(nextMonth.Year(), nextMonth.Month(), 1, 0, 0, 0, 0, date.Location())
dateUtc := date.UTC()
// First set to the 1st of the current month
firstOfMonth := time.Date(dateUtc.Year(), dateUtc.Month(), 1, 0, 0, 0, 0, dateUtc.Location())
// Then add one month - this avoids day rollover issues
firstOfNextMonth := firstOfMonth.AddDate(0, 1, 0)
return firstOfNextMonth.Unix()
}

36 changes: 35 additions & 1 deletion internal/billing/stripe-billing_test.go
Original file line number Diff line number Diff line change
@@ -23,7 +23,41 @@ func Test_getNextMonthBillingCycleAnchor(t *testing.T) {
t.Run("timezone", func(t *testing.T) {
date := time.Date(2024, time.December, 1, 0, 0, 0, 0, time.Local)
actual := getNextMonthBillingCycleAnchor(date)
expected := time.Date(2025, time.January, 1, 0, 0, 0, 0, time.Local).Unix()
expected := time.Date(2025, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()
require.Equal(t, expected, actual)
})

t.Run("handles utc conversion", func(t *testing.T) {
la, err := time.LoadLocation("America/Los_Angeles")
require.NoError(t, err)
date := time.Date(2025, time.January, 31, 23, 59, 59, 0, la)
actual := getNextMonthBillingCycleAnchor(date.UTC())
expected := time.Date(2025, time.March, 1, 0, 0, 0, 0, time.UTC).Unix()
require.Equal(t, expected, actual)
})

t.Run("handles non existent days", func(t *testing.T) {
date := time.Date(2025, time.January, 29, 0, 0, 0, 0, time.UTC)
actual := getNextMonthBillingCycleAnchor(date)
expected := time.Date(2025, time.February, 1, 0, 0, 0, 0, time.UTC).Unix()
require.Equal(t, expected, actual)
})
t.Run("handles leap year", func(t *testing.T) {
date := time.Date(2024, time.January, 29, 0, 0, 0, 0, time.UTC)
actual := getNextMonthBillingCycleAnchor(date)
expected := time.Date(2024, time.February, 1, 0, 0, 0, 0, time.UTC).Unix()
require.Equal(t, expected, actual)
})
t.Run("handles february leap year", func(t *testing.T) {
date := time.Date(2024, time.February, 29, 0, 0, 0, 0, time.UTC)
actual := getNextMonthBillingCycleAnchor(date)
expected := time.Date(2024, time.March, 1, 0, 0, 0, 0, time.UTC).Unix()
require.Equal(t, expected, actual)
})
t.Run("middle of the month", func(t *testing.T) {
date := time.Date(2024, time.February, 15, 0, 0, 0, 0, time.UTC)
actual := getNextMonthBillingCycleAnchor(date)
expected := time.Date(2024, time.March, 1, 0, 0, 0, 0, time.UTC).Unix()
require.Equal(t, expected, actual)
})
}

0 comments on commit b97eaab

Please sign in to comment.