ducky/subscriptions

Paddy 2015-06-22 Parent:61c4ce5850da Child:c4cfceb2f2fb

3:b240b6123548 Go to Latest

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.

History
     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  	}