Skip to content
This repository has been archived by the owner on Jan 2, 2024. It is now read-only.

Commit

Permalink
use "subscription.status" instead of trial_end. (#231)
Browse files Browse the repository at this point in the history
The trial_end field returned by stripe is always non-zero if there was a
trial, ever. This means trial_end is not reset after any transition out
of trialing.

Also, account for canceled_at status. This means lookup phases will now
automatically add a cancel phase to the pseudo phases, effective at
canceled_at. If there is a cancel_at and the subscription is yet to be
canceled, then use that as the start of the cancel phase, but mark it as
not current.
  • Loading branch information
bmizerany committed Jan 31, 2023
1 parent 1b05cfd commit 3d81f73
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 30 deletions.
79 changes: 52 additions & 27 deletions control/schedule.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ type subscription struct {
Effective time.Time
TrialEnd time.Time
EndDate time.Time
CanceledAt time.Time
Features []Feature
}

Expand All @@ -110,11 +111,12 @@ func (c *Client) lookupSubscription(ctx context.Context, org, name string) (sub

type T struct {
stripe.ID
Status string
StartDate int64 `json:"start_date"`
CancelAt int64 `json:"cancel_at"`
TrialEnd int64 `json:"trial_end"`
Items struct {
Status string
StartDate int64 `json:"start_date"`
CancelAt int64 `json:"cancel_at"`
CanceledAt int64 `json:"canceled_at"`
TrialEnd int64 `json:"trial_end"`
Items struct {
Data []struct {
ID string
Price stripePrice
Expand Down Expand Up @@ -169,6 +171,9 @@ func (c *Client) lookupSubscription(ctx context.Context, org, name string) (sub
if v.CancelAt > 0 {
s.EndDate = time.Unix(v.CancelAt, 0)
}
if v.CanceledAt > 0 {
s.CanceledAt = time.Unix(v.CanceledAt, 0)
}
return s, nil
}

Expand Down Expand Up @@ -216,28 +221,7 @@ func (c *Client) lookupPhases(ctx context.Context, org string, s subscription, n
defer errorfmt.Handlef("lookupPhases: %w", &err)

if s.ScheduleID == "" {
ps := []Phase{{
Org: org,
Effective: s.Effective,
Features: FeaturePlans(s.Features),
Current: true,
Trial: !s.TrialEnd.IsZero(),
}}
if !s.TrialEnd.IsZero() {
ps = append(ps, Phase{
Org: org,
Effective: s.TrialEnd,
Features: FeaturePlans(s.Features),
Current: false,
Trial: false,
})
}
if !s.EndDate.IsZero() {
ps = append(ps, Phase{
Org: org,
Effective: s.EndDate,
})
}
ps := subscriptionToPhases(org, s)
return ps[0], ps, nil
}

Expand Down Expand Up @@ -323,6 +307,47 @@ func (c *Client) lookupPhases(ctx context.Context, org string, s subscription, n
return current, all, nil
}

func subscriptionToPhases(org string, s subscription) []Phase {
ps := []Phase{{
Org: org,
Effective: s.Effective,
Features: FeaturePlans(s.Features),
Current: true,
}}

if !s.TrialEnd.IsZero() {
// Break the trial into a separate phase.
ps = []Phase{{
Org: org,
Effective: s.Effective,
Features: FeaturePlans(s.Features),
Current: s.Status == "trialing",
Trial: true,
}, {
Org: org,
Effective: s.TrialEnd,
Features: FeaturePlans(s.Features),
Current: s.Status != "trialing" && s.Status != "canceled",
}}
}

endDate := s.EndDate
if !s.CanceledAt.IsZero() {
endDate = s.CanceledAt
}

if !endDate.IsZero() {
ps = append(ps, Phase{
Org: org,
Effective: endDate,
Features: nil,
Current: s.Status == "canceled",
})
}

return ps
}

func (c *Client) updateSchedule(ctx context.Context, schedID, name string, phases []Phase) (err error) {
defer errorfmt.Handlef("stripe: updateSchedule: %q: %w", schedID, &err)
if schedID == "" {
Expand Down
37 changes: 34 additions & 3 deletions control/schedule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -768,28 +768,34 @@ func TestLookupPhasesNoSchedule(t *testing.T) {
},
{
s: `
"status": "active",
"start_date": 100000000,
"trial_end": 200000000,
"cancel_at": 300000000,
"cancel_at": 400000000,
`,
want: []Phase{{
Org: "org:test",
Effective: time.Unix(100000000, 0),
Current: true,
Current: false,
Trial: true,
Features: fs,
}, {
Org: "org:test",
Effective: time.Unix(200000000, 0),
Features: fs,
Current: true, // <---- current
Trial: false,
}, {
Org: "org:test",
Effective: time.Unix(300000000, 0),
Effective: time.Unix(400000000, 0),
Features: nil, // cancel plan
Current: false,
Trial: false,
}},
},
{
s: `
"status": "trialing",
"start_date": 100000000,
"trial_end": 200000000,
`,
Expand All @@ -805,6 +811,31 @@ func TestLookupPhasesNoSchedule(t *testing.T) {
Features: fs,
}},
},
{
s: `
// no cancel_at but having status "canceled" will add a cancel phase
"status": "canceled",
"start_date": 100000000,
"trial_end": 200000000,
"canceled_at": 400000000,
`,
want: []Phase{{
Org: "org:test",
Effective: time.Unix(100000000, 0),
Current: false,
Trial: true,
Features: fs,
}, {
Org: "org:test",
Effective: time.Unix(200000000, 0),
Features: fs,
}, {
Org: "org:test",
Effective: time.Unix(400000000, 0),
Features: nil,
Current: true,
}},
},
}

for _, tt := range cases {
Expand Down

0 comments on commit 3d81f73

Please sign in to comment.