ducky/subscriptions
ducky/subscriptions/stripe.go
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.
1.1 --- a/stripe.go Tue Jun 16 23:09:59 2015 -0400 1.2 +++ b/stripe.go Mon Jun 22 18:34:07 2015 -0400 1.3 @@ -1,6 +1,7 @@ 1.4 package subscriptions 1.5 1.6 import ( 1.7 + "log" 1.8 "time" 1.9 1.10 "code.secondbit.org/uuid.hg" 1.11 @@ -58,19 +59,20 @@ 1.12 return resp, nil 1.13 } 1.14 1.15 -func CreateSubscription(token, email string, subscription Subscription, s Stripe, store subscriptionStore) error { 1.16 +func New(req SubscriptionRequest, s Stripe, store SubscriptionStore) (Subscription, error) { 1.17 + subscription := SubscriptionFromRequest(req) 1.18 // create the subscription in our datastore 1.19 // this will fail if they already have a subscription, which prevents duplicate/orphaned Stripe customers being created 1.20 - err := store.createSubscription(subscription) 1.21 + err := store.CreateSubscription(subscription) 1.22 if err != nil { 1.23 - return err 1.24 + return subscription, err 1.25 } 1.26 1.27 // create the customer in Stripe, storing the token for reuse 1.28 - customer, err := CreateStripeCustomer(token, email, subscription.UserID, s) 1.29 + customer, err := CreateStripeCustomer(req.StripeToken, req.Email, req.UserID, s) 1.30 if err != nil { 1.31 // TODO: delete subscription object 1.32 - return err 1.33 + return subscription, err 1.34 } 1.35 1.36 // create the subscription in Stripe, storing the ID for tracking and associating purposes 1.37 @@ -78,17 +80,18 @@ 1.38 if err != nil { 1.39 // TODO: delete customer 1.40 // TODO: delete subscription object 1.41 - return err 1.42 + return subscription, err 1.43 } 1.44 1.45 // update our subscription in the datastore with the latest information from Stripe 1.46 change := StripeSubscriptionChange(subscription, *stripeSub) 1.47 - err = store.updateSubscription(subscription.UserID, change) 1.48 + err = store.UpdateSubscription(subscription.UserID, change) 1.49 if err != nil { 1.50 - // TODO: log an error, manually retry later? 1.51 - return err 1.52 + log.Printf("Error pairing Stripe subscription %s to user %s: %+v\nUser needs to have their subscription updated manually.", change.StripeSubscription, req.UserID, err) 1.53 + return subscription, nil 1.54 } 1.55 - return nil 1.56 + subscription.ApplyChange(change) 1.57 + return subscription, nil 1.58 } 1.59 1.60 func StripeSubscriptionChange(orig Subscription, subscription stripe.Sub) SubscriptionChange { 1.61 @@ -103,23 +106,23 @@ 1.62 if subscription.EndCancel != orig.Canceling { 1.63 change.Canceling = &subscription.EndCancel 1.64 } 1.65 - if !time.Unix(subscription.TrialStart, 0).Equal(orig.TrialStart) { 1.66 + if !time.Unix(subscription.TrialStart, 0).Equal(orig.TrialStart) && !(subscription.TrialStart == 0 && orig.TrialStart.IsZero()) { 1.67 trialStart := time.Unix(subscription.TrialStart, 0) 1.68 change.TrialStart = &trialStart 1.69 } 1.70 - if !time.Unix(subscription.TrialEnd, 0).Equal(orig.TrialEnd) { 1.71 + if !time.Unix(subscription.TrialEnd, 0).Equal(orig.TrialEnd) && !(subscription.TrialEnd == 0 && orig.TrialEnd.IsZero()) { 1.72 trialEnd := time.Unix(subscription.TrialEnd, 0) 1.73 change.TrialEnd = &trialEnd 1.74 } 1.75 - if !time.Unix(subscription.PeriodStart, 0).Equal(orig.PeriodStart) { 1.76 + if !time.Unix(subscription.PeriodStart, 0).Equal(orig.PeriodStart) && !(subscription.PeriodStart == 0 && orig.PeriodStart.IsZero()) { 1.77 periodStart := time.Unix(subscription.PeriodStart, 0) 1.78 change.PeriodStart = &periodStart 1.79 } 1.80 - if !time.Unix(subscription.PeriodEnd, 0).Equal(orig.PeriodEnd) { 1.81 + if !time.Unix(subscription.PeriodEnd, 0).Equal(orig.PeriodEnd) && !(subscription.PeriodEnd == 0 && orig.PeriodEnd.IsZero()) { 1.82 periodEnd := time.Unix(subscription.PeriodEnd, 0) 1.83 change.PeriodEnd = &periodEnd 1.84 } 1.85 - if !time.Unix(subscription.Canceled, 0).Equal(orig.CanceledAt) { 1.86 + if !time.Unix(subscription.Canceled, 0).Equal(orig.CanceledAt) && !(subscription.Canceled == 0 && orig.CanceledAt.IsZero()) { 1.87 canceledAt := time.Unix(subscription.Canceled, 0) 1.88 change.CanceledAt = &canceledAt 1.89 }