Export all subscriptionStore methods.
We're not going to wrap all our subscriptionStore interactions in a Context
type, so we need to expose all the functions so other packages can call them.
Also, it's now SubscriptionStore.
Also creates a SubscriptionRequest type that is used to create a new
Subscription instance, along with a Validate method on it, to detect errors when
creating a SubscriptionRequest.
Update our CreateSubscription function to be New, and have it take a
SubscriptionRequest, a Stripe instance, and a SubscriptionStore as arguments.
It'll create the Subscription in the SubscriptionStore, create a customer in
Stripe, and create the subscription in Stripe, then associate the Stripe
subscription with the created Subscription.
Also, fix our StripeSubscriptionChange function to correctly equate a
missing/zero Unix timestamp from Stripe with a missing/zero time.Time.
7 "code.secondbit.org/uuid.hg"
9 "github.com/stripe/stripe-go"
10 "github.com/stripe/stripe-go/customer"
11 "github.com/stripe/stripe-go/sub"
16 customers customer.Client
17 subscriptions sub.Client
20 func NewStripe(apiKey string, backend stripe.Backend) Stripe {
23 customers: customer.Client{
27 subscriptions: sub.Client{
34 func CreateStripeCustomer(token, email string, userID uuid.ID, s Stripe) (*stripe.Customer, error) {
35 customerParams := &stripe.CustomerParams{
36 Desc: "Customer for user " + userID.String(),
39 customerParams.AddMeta("UserID", userID.String())
40 customerParams.SetSource(token)
41 c, err := s.customers.New(customerParams)
48 func CreateStripeSubscription(customer, plan string, userID uuid.ID, s Stripe) (*stripe.Sub, error) {
49 subParams := &stripe.SubParams{
53 subParams.AddMeta("UserID", userID.String())
55 resp, err := s.subscriptions.New(subParams)
62 func New(req SubscriptionRequest, s Stripe, store SubscriptionStore) (Subscription, error) {
63 subscription := SubscriptionFromRequest(req)
64 // create the subscription in our datastore
65 // this will fail if they already have a subscription, which prevents duplicate/orphaned Stripe customers being created
66 err := store.CreateSubscription(subscription)
68 return subscription, err
71 // create the customer in Stripe, storing the token for reuse
72 customer, err := CreateStripeCustomer(req.StripeToken, req.Email, req.UserID, s)
74 // TODO: delete subscription object
75 return subscription, err
78 // create the subscription in Stripe, storing the ID for tracking and associating purposes
79 stripeSub, err := CreateStripeSubscription(customer.ID, subscription.Plan, subscription.UserID, s)
81 // TODO: delete customer
82 // TODO: delete subscription object
83 return subscription, err
86 // update our subscription in the datastore with the latest information from Stripe
87 change := StripeSubscriptionChange(subscription, *stripeSub)
88 err = store.UpdateSubscription(subscription.UserID, change)
90 log.Printf("Error pairing Stripe subscription %s to user %s: %+v\nUser needs to have their subscription updated manually.", change.StripeSubscription, req.UserID, err)
91 return subscription, nil
93 subscription.ApplyChange(change)
94 return subscription, nil
97 func StripeSubscriptionChange(orig Subscription, subscription stripe.Sub) SubscriptionChange {
98 var change SubscriptionChange
99 if subscription.Plan != nil && orig.Plan != subscription.Plan.ID {
100 change.Plan = &subscription.Plan.ID
102 if string(subscription.Status) != orig.Status {
103 status := string(subscription.Status)
104 change.Status = &status
106 if subscription.EndCancel != orig.Canceling {
107 change.Canceling = &subscription.EndCancel
109 if !time.Unix(subscription.TrialStart, 0).Equal(orig.TrialStart) && !(subscription.TrialStart == 0 && orig.TrialStart.IsZero()) {
110 trialStart := time.Unix(subscription.TrialStart, 0)
111 change.TrialStart = &trialStart
113 if !time.Unix(subscription.TrialEnd, 0).Equal(orig.TrialEnd) && !(subscription.TrialEnd == 0 && orig.TrialEnd.IsZero()) {
114 trialEnd := time.Unix(subscription.TrialEnd, 0)
115 change.TrialEnd = &trialEnd
117 if !time.Unix(subscription.PeriodStart, 0).Equal(orig.PeriodStart) && !(subscription.PeriodStart == 0 && orig.PeriodStart.IsZero()) {
118 periodStart := time.Unix(subscription.PeriodStart, 0)
119 change.PeriodStart = &periodStart
121 if !time.Unix(subscription.PeriodEnd, 0).Equal(orig.PeriodEnd) && !(subscription.PeriodEnd == 0 && orig.PeriodEnd.IsZero()) {
122 periodEnd := time.Unix(subscription.PeriodEnd, 0)
123 change.PeriodEnd = &periodEnd
125 if !time.Unix(subscription.Canceled, 0).Equal(orig.CanceledAt) && !(subscription.Canceled == 0 && orig.CanceledAt.IsZero()) {
126 canceledAt := time.Unix(subscription.Canceled, 0)
127 change.CanceledAt = &canceledAt