Skip to content

Commit 4118011

Browse files
committed
add annual plan subscription option
1 parent 676908d commit 4118011

File tree

2 files changed

+70
-25
lines changed

2 files changed

+70
-25
lines changed

internal/account/account.go

+10-10
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,9 @@ type Discount struct {
8888
// Plan model.
8989
type Plan struct {
9090
ID string `json:"id"`
91+
Name string `json:"name"`
9192
Product string `json:"product"`
9293
Plan string `json:"plan"`
93-
PlanName string `json:"plan_name"`
9494
Amount int `json:"amount"`
9595
Interval string `json:"interval"`
9696
Status string `json:"status"`
@@ -209,15 +209,15 @@ func (c *Client) RemoveCard(token, id string) error {
209209
}
210210

211211
// AddPlan subscribes to plan.
212-
func (c *Client) AddPlan(token, product, plan, coupon string) error {
212+
func (c *Client) AddPlan(token, product, interval, coupon string) error {
213213
in := struct {
214-
Product string `json:"product"`
215-
Plan string `json:"plan"`
216-
Coupon string `json:"coupon"`
214+
Product string `json:"product"`
215+
Interval string `json:"interval"`
216+
Coupon string `json:"coupon"`
217217
}{
218-
Product: product,
219-
Plan: plan,
220-
Coupon: coupon,
218+
Product: product,
219+
Interval: interval,
220+
Coupon: coupon,
221221
}
222222

223223
res, err := c.requestJSON(token, "PUT", "/billing/plans", in)
@@ -283,8 +283,8 @@ func (c *Client) RemoveMember(token, email string) error {
283283
}
284284

285285
// RemovePlan unsubscribes from a plan.
286-
func (c *Client) RemovePlan(token, product, plan string) error {
287-
path := fmt.Sprintf("/billing/plans/%s/%s", product, plan)
286+
func (c *Client) RemovePlan(token, product string) error {
287+
path := fmt.Sprintf("/billing/plans/%s", product)
288288
res, err := c.request(token, "DELETE", path, nil)
289289
if err != nil {
290290
return err

internal/cli/team/team.go

+60-15
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,31 @@ import (
2323
"github.com/apex/log"
2424
"github.com/apex/up/internal/account"
2525
"github.com/apex/up/internal/cli/root"
26+
"github.com/apex/up/internal/colors"
2627
"github.com/apex/up/internal/stats"
2728
"github.com/apex/up/internal/userconfig"
2829
"github.com/apex/up/internal/util"
2930
"github.com/apex/up/platform/event"
3031
"github.com/apex/up/reporter"
3132
)
3233

33-
var (
34-
api = env.GetDefault("APEX_TEAMS_API", "https://teams.apex.sh")
35-
a = account.New(api)
36-
)
34+
// api endpoint.
35+
var api = env.GetDefault("APEX_TEAMS_API", "https://teams.apex.sh")
36+
37+
// api client.
38+
var a = account.New(api)
39+
40+
// plan amounts.
41+
var amounts = map[string]int{
42+
"monthly": 2000,
43+
"annually": 21600,
44+
}
45+
46+
// plan amount select options.
47+
var amountOptions = map[string]string{
48+
"Monthly at $20.00 USD": "monthly",
49+
"Annually at 216.00 USD": "annually",
50+
}
3751

3852
func init() {
3953
cmd := root.Command("team", "Manage team members, plans, and billing.")
@@ -207,7 +221,7 @@ func status(cmd *kingpin.Cmd) {
207221
util.LogName("coupon", d.Coupon.ID)
208222
}
209223

210-
util.LogName("amount", "$%0.2f/mo USD", float64(p.Amount)/100)
224+
util.LogName("amount", "%s USD per %s", currency(p.Amount), p.Interval)
211225
util.LogName("owner", team.Owner)
212226
util.LogName("created", p.CreatedAt.Format("January 2, 2006"))
213227
if p.Canceled {
@@ -301,8 +315,7 @@ func login(cmd *kingpin.Cmd) {
301315
// ensure we have a team if already signed-in
302316
if t != nil && *team == "" {
303317
util.Log("Already signed in as %s on team %s.", t.Email, t.ID)
304-
util.Log("Use `up team login --team <id>` to join a team")
305-
util.Log("if you have received an invite.")
318+
util.Log("Use `up team login --team <id>` to join a team.")
306319
return nil
307320
}
308321

@@ -394,8 +407,21 @@ func subscribe(cmd *kingpin.Cmd) {
394407

395408
defer util.Pad()()
396409

397-
// TODO: fetch from plan
398-
amount := 2000
410+
// plan
411+
util.LogTitle("Subscription")
412+
util.Log("Choose a monthly billing period, or 10%% off annually.")
413+
println()
414+
415+
var interval string
416+
err = survey.AskOne(&survey.Select{
417+
Message: "Plan:",
418+
Options: keys(amountOptions),
419+
}, &interval, survey.Required)
420+
421+
// amount
422+
interval = amountOptions[interval]
423+
amount := amounts[interval]
424+
399425
util.LogTitle("Coupon")
400426
util.Log("Enter a coupon, or press enter to skip this step")
401427
util.Log("and move on to adding a credit card.")
@@ -424,7 +450,8 @@ func subscribe(cmd *kingpin.Cmd) {
424450
util.LogClear("Coupon is invalid")
425451
} else {
426452
amount = coupon.Discount(amount)
427-
util.LogClear("Savings: %s", coupon.Description())
453+
msg := colors.Gray(fmt.Sprintf("%s — now %s %s", coupon.Description(), currency(amount), interval))
454+
util.LogClear("Savings: %s", msg)
428455
}
429456
}
430457

@@ -455,9 +482,8 @@ func subscribe(cmd *kingpin.Cmd) {
455482

456483
// confirm
457484
var ok bool
458-
total := fmt.Sprintf("%0.2f", float64(amount)/100)
459485
err = survey.AskOne(&survey.Confirm{
460-
Message: fmt.Sprintf("Subscribe to Up Pro for $%s/mo USD?", total),
486+
Message: fmt.Sprintf("Subscribe to Up Pro for %s USD %s?", currency(amount), interval),
461487
}, &ok, nil)
462488

463489
if err != nil {
@@ -471,10 +497,11 @@ func subscribe(cmd *kingpin.Cmd) {
471497
}
472498

473499
stats.Track("Subscribe", map[string]interface{}{
474-
"coupon": couponID,
500+
"coupon": couponID,
501+
"interval": interval,
475502
})
476503

477-
if err := a.AddPlan(t.Token, "up", "pro", couponID); err != nil {
504+
if err := a.AddPlan(t.Token, "up", interval, couponID); err != nil {
478505
return errors.Wrap(err, "subscribing")
479506
}
480507

@@ -527,7 +554,7 @@ func unsubscribe(cmd *kingpin.Cmd) {
527554

528555
stats.Track("Unsubscribe", nil)
529556

530-
if err := a.RemovePlan(config.Token, "up", "pro"); err != nil {
557+
if err := a.RemovePlan(config.Token, "up"); err != nil {
531558
return errors.Wrap(err, "unsubscribing")
532559
}
533560

@@ -701,3 +728,21 @@ func feedback() (string, error) {
701728
}
702729
return s, nil
703730
}
731+
732+
// currency returns formatted currency.
733+
func currency(n int) string {
734+
return fmt.Sprintf("$%0.2f", float64(n)/100)
735+
}
736+
737+
// keys returns the keys of a string map.
738+
func keys(m map[string]string) (v []string) {
739+
for k := range m {
740+
v = append(v, k)
741+
}
742+
743+
sort.Slice(v, func(i int, j int) bool {
744+
return v[i] > v[j]
745+
})
746+
747+
return
748+
}

0 commit comments

Comments
 (0)