-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsubscription.go
154 lines (123 loc) · 3.65 KB
/
subscription.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package stripeutil
import (
"database/sql"
"encoding/json"
"strings"
"time"
"github.com/stripe/stripe-go/v72"
)
// Subscription is the Subscription resource from Stripe. Embedded in this
// struct is the stripe.Subscription struct from Stripe.
type Subscription struct {
*stripe.Subscription
EndsAt sql.NullTime // EndsAt is the time the Subscription ends if it was cancelled.
}
var (
_ Resource = (*Subscription)(nil)
subscriptionEndpoint = "/v1/subscriptions"
validSubscriptionStatuses = map[stripe.SubscriptionStatus]struct{}{
stripe.SubscriptionStatusAll: {},
stripe.SubscriptionStatusActive: {},
stripe.SubscriptionStatusTrialing: {},
}
)
func postSubscription(st *Stripe, uri string, params map[string]interface{}) (*Subscription, error) {
sub := &Subscription{}
resp, err := st.Post(uri, params)
if err != nil {
return sub, err
}
defer resp.Body.Close()
if !respCode2xx(resp.StatusCode) {
return sub, st.Error(resp)
}
err = json.NewDecoder(resp.Body).Decode(&sub.Subscription)
return sub, err
}
// CreateSubscription will create a new Subscription in Stripe with the given
// request Params.
func CreateSubscription(st *Stripe, params Params) (*Subscription, error) {
return postSubscription(st, subscriptionEndpoint, params)
}
// Reactivate will reactivate the current subscription by setting the property
// cancel_at_period_end to false. This will set the EndsAt field to be invalid.
func (s *Subscription) Reactivate(st *Stripe) error {
if err := s.Update(st, Params{"cancel_at_period_end": false}); err != nil {
return err
}
s.EndsAt = sql.NullTime{}
return nil
}
// Cancel will cancel the current Subscription at the end of the Subscription
// Period. This will set the EndsAt field to the CurrentPeriodEnd of the
// Subscription.
func (s *Subscription) Cancel(st *Stripe) error {
if err := s.Update(st, Params{"cancel_at_period_end": true}); err != nil {
return err
}
s.EndsAt = sql.NullTime{
Time: time.Unix(s.CurrentPeriodEnd, 0),
Valid: true,
}
return nil
}
// Update will update the current Subscription in Stripe with the given Params.
func (s *Subscription) Update(st *Stripe, params Params) error {
s1, err := postSubscription(st, s.Endpoint(), params)
if err != nil {
return err
}
(*s) = (*s1)
return nil
}
// Endpoint implements the Resource interface.
func (s *Subscription) Endpoint(uris ...string) string {
endpoint := subscriptionEndpoint
if s.ID != "" {
endpoint += "/" + s.ID
}
if len(uris) > 0 {
endpoint += "/"
}
return endpoint + strings.Join(uris, "/")
}
// WithinGrace will return true if the current Subscription has been canceled
// but stil lies within the grace period. If the Subscription has not been
// canceled then this will always return true.
func (s *Subscription) WithinGrace() bool {
if s == nil {
return false
}
if !s.EndsAt.Valid {
return false
}
return time.Now().Before(s.EndsAt.Time)
}
// Valid will return whether or not the current Subscription is valid. A
// Subscription is considered valid if the status is one of, "all", "active",
// or "trialing", or if the Subscription was cancelled but the current time
// is before the EndsAt date.
func (s *Subscription) Valid() bool {
if s == nil {
return false
}
if s.EndsAt.Valid {
return time.Now().Before(s.EndsAt.Time)
}
if _, ok := validSubscriptionStatuses[s.Status]; ok {
return ok
}
return false
}
// Load implements the Resource interface.
func (s *Subscription) Load(st *Stripe) error {
resp, err := st.Client.Get(s.Endpoint())
if err != nil {
return err
}
defer resp.Body.Close()
if !respCode2xx(resp.StatusCode) {
return st.Error(resp)
}
return json.NewDecoder(resp.Body).Decode(s)
}