ducky/subscriptions

Paddy 2015-07-18 Parent:8eb19bcbf17d Child:0ae1ff0ee306

10:2c8250237566 Browse Files

Update subscription_creator to use the new strategy. When creating subscriptions through the client, detect when the returned error is saying the account already has a subscription, or the subscription already exists in stripe. Add an UpdateSubscription function that will update a subscription through the API. Update our subscription_creator listener to listen for profile creation and login verification messages. This mainly involved fixing the constants (the system, model, and topic) that the listener for profile creation was listening for. It also meant adding a new updateMessageHandler that listens for login verification, tries to create a subscription that has the user ID and email of the login that was verified, and if a subscription already exists, updates it instead to use the email address that was just verified. This will ensure that users get their receipts automatically emailed to them by Stripe.

client/subscription.go listeners/subscription_creator/subscription_creator.go

     1.1 --- a/client/subscription.go	Sat Jul 18 03:26:56 2015 -0400
     1.2 +++ b/client/subscription.go	Sat Jul 18 03:28:51 2015 -0400
     1.3 @@ -1,6 +1,7 @@
     1.4  package client
     1.5  
     1.6  import (
     1.7 +	commonAPI "code.secondbit.org/api.hg"
     1.8  	"code.secondbit.org/auth.hg"
     1.9  
    1.10  	"code.secondbit.org/ducky/subscriptions.hg"
    1.11 @@ -10,6 +11,18 @@
    1.12  func (c *Client) CreateSubscription(change subscriptions.SubscriptionChange) (subscriptions.Subscription, error) {
    1.13  	resp, err := c.Post("/subscriptions/", change, auth.Scopes{api.ScopeSubscription, api.ScopeSubscriptionAdmin}.Strings(), change.UserID)
    1.14  	if err != nil {
    1.15 +		hErr, ok := err.(httpErrors)
    1.16 +		if ok {
    1.17 +			for _, e := range hErr {
    1.18 +				if e.Slug == commonAPI.RequestErrConflict &&
    1.19 +					e.Field == "/user_id" {
    1.20 +					return subscriptions.Subscription{}, subscriptions.ErrSubscriptionAlreadyExists
    1.21 +				} else if e.Slug == commonAPI.RequestErrConflict &&
    1.22 +					e.Field == "/stripe_token" {
    1.23 +					return subscriptions.Subscription{}, subscriptions.ErrStripeSubscriptionAlreadyExists
    1.24 +				}
    1.25 +			}
    1.26 +		}
    1.27  		return subscriptions.Subscription{}, err
    1.28  	}
    1.29  	if len(resp.Subscriptions) < 1 {
    1.30 @@ -17,3 +30,14 @@
    1.31  	}
    1.32  	return resp.Subscriptions[0], nil
    1.33  }
    1.34 +
    1.35 +func (c *Client) UpdateSubscription(change subscriptions.SubscriptionChange) (subscriptions.Subscription, error) {
    1.36 +	resp, err := c.Patch("/subscriptions/"+change.UserID.String(), change, auth.Scopes{api.ScopeSubscription, api.ScopeSubscriptionAdmin}.Strings(), change.UserID)
    1.37 +	if err != nil {
    1.38 +		return subscriptions.Subscription{}, err
    1.39 +	}
    1.40 +	if len(resp.Subscriptions) < 1 {
    1.41 +		return subscriptions.Subscription{}, subscriptions.ErrSubscriptionNotFound
    1.42 +	}
    1.43 +	return resp.Subscriptions[0], nil
    1.44 +}
     2.1 --- a/listeners/subscription_creator/subscription_creator.go	Sat Jul 18 03:26:56 2015 -0400
     2.2 +++ b/listeners/subscription_creator/subscription_creator.go	Sat Jul 18 03:28:51 2015 -0400
     2.3 @@ -54,25 +54,30 @@
     2.4  	}
     2.5  	authClient = client.New(authAddr, clientID)
     2.6  	subsClient = subsclient.New(subsAddr, clientID)
     2.7 -	sub, err := events.NewNSQSubscriber(lookupds, nsq.HandlerFunc(messageHandler), auth.EventTopicProfileCreated, channel, "subscription_creator/"+subscriptions.Version)
     2.8 +	newSub, err := events.NewNSQSubscriber(lookupds, nsq.HandlerFunc(newMessageHandler), "profiles", channel, "subscription_creator/"+subscriptions.Version)
     2.9  	if err != nil {
    2.10  		log.Fatalf("Error creating subscriber: %+v\n", err)
    2.11  	}
    2.12 -	sub.Block()
    2.13 +	updateSub, err := events.NewNSQSubscriber(lookupds, nsq.HandlerFunc(updateMessageHandler), "logins", channel, "subscription_creator/"+subscriptions.Version)
    2.14 +	if err != nil {
    2.15 +		log.Fatalf("Error creating subscriber: %+v\n", err)
    2.16 +	}
    2.17 +	newSub.Block()
    2.18 +	updateSub.Block()
    2.19  }
    2.20  
    2.21 -func messageHandler(msg *nsq.Message) error {
    2.22 +func newMessageHandler(msg *nsq.Message) error {
    2.23  	var event events.Event
    2.24  	err := json.Unmarshal(msg.Body, &event)
    2.25  	if err != nil {
    2.26  		log.Printf("Error decoding event (%s), discarding: %+v\n", err)
    2.27  		return nil
    2.28  	}
    2.29 -	if event.System != auth.EventSystem {
    2.30 +	if event.System != "code.secondbit.org/auth" {
    2.31  		log.Printf("Ignoring event originating from %s\n", event.System)
    2.32  		return nil
    2.33  	}
    2.34 -	if event.Model != auth.EventModelProfile {
    2.35 +	if event.Model != "profiles" {
    2.36  		log.Printf("Ignoring event for model %s\n", event.Model)
    2.37  		return nil
    2.38  	}
    2.39 @@ -92,6 +97,56 @@
    2.40  		log.Printf("Error creating subscription for %s: %+v\n", event.ID, err)
    2.41  		return err
    2.42  	}
    2.43 -	log.Println("Created subscription", event.ID)
    2.44 +	log.Println("Created subscription", subscription.UserID)
    2.45  	return nil
    2.46  }
    2.47 +
    2.48 +func updateMessageHandler(msg *nsq.Message) error {
    2.49 +	var event events.Event
    2.50 +	err := json.Unmarshal(msg.Body, &event)
    2.51 +	if err != nil {
    2.52 +		log.Printf("Error decoding event (%s), discarding: %+v\n", err)
    2.53 +		return nil
    2.54 +	}
    2.55 +	if event.System != "code.secondbit.org/auth" {
    2.56 +		log.Printf("Ignoring event originating from %s\n", event.System)
    2.57 +		return nil
    2.58 +	}
    2.59 +	if event.Model != "logins" {
    2.60 +		log.Printf("Ignoring event for model %s\n", event.Model)
    2.61 +		return nil
    2.62 +	}
    2.63 +	if event.Action != auth.ActionLoginVerified {
    2.64 +		log.Printf("Ignoring event caused by %s\n", event.Action)
    2.65 +		return nil
    2.66 +	}
    2.67 +	login, err := authClient.GetLogin(event.ID)
    2.68 +	if err != nil {
    2.69 +		log.Printf("Error retrieving login (%s): %+v\n", event.ID)
    2.70 +		return err
    2.71 +	}
    2.72 +	if login.Type != "email" {
    2.73 +		log.Printf("Non-email login, ignoring: %+v\n", login)
    2.74 +		return nil
    2.75 +	}
    2.76 +	subscription, err := subsClient.CreateSubscription(subscriptions.SubscriptionChange{
    2.77 +		UserID: login.ProfileID,
    2.78 +		Email:  &login.Value,
    2.79 +	})
    2.80 +	if err != nil && err != subscriptions.ErrSubscriptionAlreadyExists {
    2.81 +		log.Printf("Error creating subscription: %+v\n", err)
    2.82 +	} else if err == nil {
    2.83 +		return nil
    2.84 +	}
    2.85 +	log.Printf("Subscription already exists, adding email...\n")
    2.86 +	subscription, err = subsClient.UpdateSubscription(subscriptions.SubscriptionChange{
    2.87 +		UserID: login.ProfileID,
    2.88 +		Email:  &login.Value,
    2.89 +	})
    2.90 +	if err != nil {
    2.91 +		log.Printf("Error updating subscription (%s): %+v\n", login.ProfileID, err)
    2.92 +		return err
    2.93 +	}
    2.94 +	log.Println("Updated subscription", subscription.UserID)
    2.95 +	return nil
    2.96 +}