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

Commit

Permalink
api: add coupon support (#289)
Browse files Browse the repository at this point in the history
  • Loading branch information
bmizerany committed Apr 5, 2023
1 parent 8270536 commit e4c052f
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 2 deletions.
2 changes: 2 additions & 0 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ func (h *Handler) serveSubscribe(w http.ResponseWriter, r *http.Request) error {
Effective: p.Effective,
Features: fs,
AutomaticTax: sr.Tax.Automatic,
Coupon: p.Coupon,
})
}
}
Expand Down Expand Up @@ -349,6 +350,7 @@ func (h *Handler) servePhase(w http.ResponseWriter, r *http.Request) error {
Automatic: p.AutomaticTax,
},
Current: apitypes.Period(s.Current),
Coupon: p.Coupon,
})
}
}
Expand Down
26 changes: 24 additions & 2 deletions api/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,25 @@ var (
mpfs = refs.MustParseFeaturePlans
)

func newTestClient(t *testing.T) *tier.Client {
type testClient struct {
t *testing.T
*tier.Client
cc *control.Client
}

func (tc *testClient) createCoupon(ctx context.Context, code string) {
tc.t.Helper()
var f stripe.Form
f.Set("amount_off", 1)
f.Set("currency", "usd")
f.Set("duration", "once")
f.Set("id", code)
if err := tc.cc.Stripe.Do(ctx, "POST", "/v1/coupons", f, nil); err != nil {
tc.t.Fatal(err)
}
}

func newTestClient(t *testing.T) *testClient {
sc := stroke.Client(t)
sc = stroke.WithAccount(t, sc)
cc := &control.Client{
Expand All @@ -45,7 +63,7 @@ func newTestClient(t *testing.T) *tier.Client {
BaseURL: s.URL,
HTTPClient: s.Client(),
}
return tc
return &testClient{t, tc, cc}
}

func newTestClientWithStripe(t *testing.T, fakeStripe http.HandlerFunc) *tier.Client {
Expand Down Expand Up @@ -418,6 +436,8 @@ func TestPhase(t *testing.T) {
t.Fatal(err)
}

tc.createCoupon(ctx, "coupon_test")

cases := []struct {
phases []tier.Phase
want apitypes.PhaseResponse
Expand All @@ -426,6 +446,7 @@ func TestPhase(t *testing.T) {
phases: []tier.Phase{{
Trial: true,
Features: []string{"plan:test@0"},
Coupon: "coupon_test",
}, {
Effective: now.AddDate(0, 0, 14),
Trial: false,
Expand All @@ -437,6 +458,7 @@ func TestPhase(t *testing.T) {
Features: mpfs("feature:x@plan:test@0"),
Plans: mpps("plan:test@0"),
Trial: true,
Coupon: "coupon_test",
},
},
{
Expand Down
4 changes: 4 additions & 0 deletions api/apitypes/apitypes.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type Phase struct {
Trial bool `json:"trial,omitempty"`
Effective time.Time `json:"effective,omitempty"`
Features []string `json:"features,omitempty"`
Coupon string `json:"coupon,omitempty"`
}

type Taxation struct {
Expand All @@ -49,6 +50,7 @@ type PhaseResponse struct {
Trial bool `json:"trial,omitempty"`
Tax Taxation `json:"tax,omitempty"`
Current Period `json:"current,omitempty"`
Coupon string `json:"coupon,omitempty"`
}

func (pr PhaseResponse) MarshalJSON() ([]byte, error) {
Expand All @@ -59,12 +61,14 @@ func (pr PhaseResponse) MarshalJSON() ([]byte, error) {
End any `json:"end,omitempty"`
Current any `json:"current,omitempty"`
Tax any `json:"tax,omitempty"`
Coupon any `json:"coupon,omitempty"`
}{
Alias: (*Alias)(&pr),
Effective: nilIfZero(pr.Effective),
End: nilIfZero(pr.End),
Current: nilIfZero(pr.Current),
Tax: nilIfZero(pr.Tax),
Coupon: nilIfZero(pr.Coupon),
})
}

Expand Down
21 changes: 21 additions & 0 deletions control/schedule.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ type Phase struct {
Plans []refs.Plan

AutomaticTax bool

Coupon string
}

// Valid reports if the Phase is one that would be retured from the Stripe API.
Expand Down Expand Up @@ -117,6 +119,7 @@ type subscription struct {
Features []Feature
AutomaticTax bool // TODO(bmizerany): cheange to tax.Applied
Current Period
Coupon string
}

func (c *Client) lookupSubscription(ctx context.Context, org, name string) (sub subscription, err error) {
Expand Down Expand Up @@ -154,6 +157,11 @@ func (c *Client) lookupSubscription(ctx context.Context, org, name string) (sub
} `json:"automatic_tax"`
CurrentPeriodStart int64 `json:"current_period_start"`
CurrentPeriodEnd int64 `json:"current_period_end"`
Discount struct {
Coupon struct {
ID string
}
}
}

// TODO(bmizerany): cache the subscription ID and looked it up
Expand Down Expand Up @@ -195,6 +203,7 @@ func (c *Client) lookupSubscription(ctx context.Context, org, name string) (sub
Effective: timeUnix(v.CurrentPeriodStart),
End: timeUnix(v.CurrentPeriodEnd),
},
Coupon: v.Discount.Coupon.ID,
}
if v.TrialEnd > 0 {
s.TrialEnd = time.Unix(v.TrialEnd, 0)
Expand Down Expand Up @@ -266,6 +275,7 @@ type stripeSubSchedule struct {
Items []struct {
Price stripePrice
}
Coupon string
}
}

Expand Down Expand Up @@ -353,6 +363,8 @@ func (c *Client) lookupPhases(ctx context.Context, org string, s subscription, n
Trial: p.TrialEnd > 0,

AutomaticTax: s.AutomaticTax,

Coupon: p.Coupon,
}
all = append(all, p)
if p.Current {
Expand Down Expand Up @@ -403,6 +415,12 @@ func subscriptionToPhases(org string, s subscription) []Phase {
})
}

for i := range ps {
if ps[i].Current {
ps[i].Coupon = s.Coupon
}
}

return ps
}

Expand Down Expand Up @@ -467,6 +485,9 @@ func addPhases(ctx context.Context, c *Client, f *stripe.Form, update bool, name

f.Set("phases", i, "metadata[tier.subscription]", name)
f.Set("phases", i, "trial", p.Trial)
if p.Coupon != "" {
f.Set("phases", i, "coupon", p.Coupon)
}

if i == 0 {
if update {
Expand Down

0 comments on commit e4c052f

Please sign in to comment.