ducky/subscriptions

Paddy 2015-09-27 Parent:c4cfceb2f2fb Child:1ff031bebf9e

12:39625b37485d Go to Latest

ducky/subscriptions/stripe.go

Fix go vet errors. We had a few logging statements that used placeholders but didn't provide any variables to fill them. We now specify the appropriate variables.

History
paddy@2 1 package subscriptions
paddy@2 2
paddy@2 3 import (
paddy@6 4 "errors"
paddy@2 5 "time"
paddy@2 6
paddy@2 7 "code.secondbit.org/uuid.hg"
paddy@2 8
paddy@2 9 "github.com/stripe/stripe-go"
paddy@2 10 "github.com/stripe/stripe-go/customer"
paddy@2 11 "github.com/stripe/stripe-go/sub"
paddy@2 12 )
paddy@2 13
paddy@6 14 const (
paddy@6 15 PendingPlan = "pending"
paddy@6 16 )
paddy@6 17
paddy@6 18 var (
paddy@6 19 ErrNilCustomer = errors.New("nil customer passed")
paddy@6 20 ErrNilCustomerSubs = errors.New("customer with nil subscriptions list passed")
paddy@6 21 ErrWrongNumberOfCustomerSubs = errors.New("customer with wrong number of subscriptions passed")
paddy@6 22 ErrNilSubscription = errors.New("nil subscription passed")
paddy@6 23 )
paddy@6 24
paddy@2 25 type Stripe struct {
paddy@2 26 apiKey string
paddy@2 27 customers customer.Client
paddy@2 28 subscriptions sub.Client
paddy@2 29 }
paddy@2 30
paddy@2 31 func NewStripe(apiKey string, backend stripe.Backend) Stripe {
paddy@2 32 return Stripe{
paddy@2 33 apiKey: apiKey,
paddy@2 34 customers: customer.Client{
paddy@2 35 B: backend,
paddy@2 36 Key: apiKey,
paddy@2 37 },
paddy@2 38 subscriptions: sub.Client{
paddy@2 39 B: backend,
paddy@2 40 Key: apiKey,
paddy@2 41 },
paddy@2 42 }
paddy@2 43 }
paddy@2 44
paddy@6 45 func CreateStripeCustomer(plan, email string, userID uuid.ID, s Stripe) (*stripe.Customer, error) {
paddy@2 46 customerParams := &stripe.CustomerParams{
paddy@2 47 Desc: "Customer for user " + userID.String(),
paddy@2 48 Email: email,
paddy@6 49 Plan: plan,
paddy@2 50 }
paddy@2 51 customerParams.AddMeta("UserID", userID.String())
paddy@2 52 c, err := s.customers.New(customerParams)
paddy@2 53 if err != nil {
paddy@2 54 return nil, err
paddy@2 55 }
paddy@2 56 return c, nil
paddy@2 57 }
paddy@2 58
paddy@6 59 func UpdateStripeSubscription(customerID string, plan, token *string, s Stripe) (*stripe.Sub, error) {
paddy@6 60 params := &stripe.SubParams{}
paddy@6 61 if plan != nil {
paddy@6 62 params.Plan = *plan
paddy@2 63 }
paddy@6 64 if token != nil {
paddy@6 65 params.Token = *token
paddy@6 66 }
paddy@6 67 subscription, err := s.subscriptions.Update(customerID, params)
paddy@2 68 if err != nil {
paddy@2 69 return nil, err
paddy@2 70 }
paddy@6 71 return subscription, nil
paddy@2 72 }
paddy@2 73
paddy@6 74 // New should be called when a user's profile is created. At this point, we know nothing about the subscription
paddy@6 75 // they actually _want_. We just sign them up for the dedicated "pending" plan. This is to make their free trial begin
paddy@6 76 // immediately and not have to worry about automatically locking them out until they actually create a subscription.
paddy@6 77 // Basically, we want everyone to have a subscription at all times, but some users will have placeholders until they
paddy@6 78 // actually update their subscription with a desired plan and payment method.
paddy@6 79 func New(req SubscriptionChange, s Stripe, store SubscriptionStore) (Subscription, error) {
paddy@6 80 subscription := Subscription{}
paddy@6 81 subscription.ApplyChange(req)
paddy@6 82 // BUG(paddy): need to validate the change
paddy@6 83
paddy@6 84 // create the customer in Stripe, storing the token for reuse
paddy@6 85 customer, err := CreateStripeCustomer(PendingPlan, *req.Email, req.UserID, s)
paddy@6 86 if err != nil {
paddy@6 87 return subscription, err
paddy@6 88 }
paddy@6 89 if customer == nil {
paddy@6 90 return subscription, ErrNilCustomer
paddy@6 91 }
paddy@6 92 if customer.Subs == nil {
paddy@6 93 return subscription, ErrNilCustomerSubs
paddy@6 94 }
paddy@6 95 if len(customer.Subs.Values) != 1 {
paddy@6 96 return subscription, ErrWrongNumberOfCustomerSubs
paddy@6 97 }
paddy@6 98 if customer.Subs.Values[0] == nil {
paddy@6 99 return subscription, ErrNilSubscription
paddy@6 100 }
paddy@6 101
paddy@6 102 change := StripeSubscriptionChange(subscription, *customer.Subs.Values[0])
paddy@6 103 subscription.ApplyChange(change)
paddy@6 104
paddy@6 105 err = store.CreateSubscription(subscription)
paddy@2 106 if err != nil {
paddy@3 107 return subscription, err
paddy@2 108 }
paddy@2 109
paddy@3 110 return subscription, nil
paddy@2 111 }
paddy@2 112
paddy@2 113 func StripeSubscriptionChange(orig Subscription, subscription stripe.Sub) SubscriptionChange {
paddy@2 114 var change SubscriptionChange
paddy@6 115 if subscription.ID != orig.StripeSubscription {
paddy@6 116 change.StripeSubscription = &subscription.ID
paddy@6 117 }
paddy@2 118 if subscription.Plan != nil && orig.Plan != subscription.Plan.ID {
paddy@2 119 change.Plan = &subscription.Plan.ID
paddy@2 120 }
paddy@2 121 if string(subscription.Status) != orig.Status {
paddy@2 122 status := string(subscription.Status)
paddy@2 123 change.Status = &status
paddy@2 124 }
paddy@2 125 if subscription.EndCancel != orig.Canceling {
paddy@2 126 change.Canceling = &subscription.EndCancel
paddy@2 127 }
paddy@3 128 if !time.Unix(subscription.TrialStart, 0).Equal(orig.TrialStart) && !(subscription.TrialStart == 0 && orig.TrialStart.IsZero()) {
paddy@2 129 trialStart := time.Unix(subscription.TrialStart, 0)
paddy@2 130 change.TrialStart = &trialStart
paddy@2 131 }
paddy@3 132 if !time.Unix(subscription.TrialEnd, 0).Equal(orig.TrialEnd) && !(subscription.TrialEnd == 0 && orig.TrialEnd.IsZero()) {
paddy@2 133 trialEnd := time.Unix(subscription.TrialEnd, 0)
paddy@2 134 change.TrialEnd = &trialEnd
paddy@2 135 }
paddy@3 136 if !time.Unix(subscription.PeriodStart, 0).Equal(orig.PeriodStart) && !(subscription.PeriodStart == 0 && orig.PeriodStart.IsZero()) {
paddy@2 137 periodStart := time.Unix(subscription.PeriodStart, 0)
paddy@2 138 change.PeriodStart = &periodStart
paddy@2 139 }
paddy@3 140 if !time.Unix(subscription.PeriodEnd, 0).Equal(orig.PeriodEnd) && !(subscription.PeriodEnd == 0 && orig.PeriodEnd.IsZero()) {
paddy@2 141 periodEnd := time.Unix(subscription.PeriodEnd, 0)
paddy@2 142 change.PeriodEnd = &periodEnd
paddy@2 143 }
paddy@3 144 if !time.Unix(subscription.Canceled, 0).Equal(orig.CanceledAt) && !(subscription.Canceled == 0 && orig.CanceledAt.IsZero()) {
paddy@2 145 canceledAt := time.Unix(subscription.Canceled, 0)
paddy@2 146 change.CanceledAt = &canceledAt
paddy@2 147 }
paddy@2 148 return change
paddy@2 149 }