stripe.go (3025B)
1 package backend 2 3 import ( 4 "encoding/json" 5 "errors" 6 "log" 7 "github.com/stripe/stripe-go/v72" 8 ) 9 10 /* 11 * Don't need customer.subscription.paused without free trial like stuff 12 * <https://docs.stripe.com/billing/subscriptions/pause-payment> 13 */ 14 func (e *Env) StripeEventHandler(ev *stripe.Event) error { 15 // Not sure whether I need to store in persistent database, 16 // maybe I should 17 v, exists := e.stripeProcessedEvents[ev.ID] 18 if exists && v { 19 log.Printf("StripeEventHandler: %s event already processed, ignoring (and returning success)\n", ev.ID) 20 return nil 21 } 22 e.stripeProcessedEvents[ev.ID] = true 23 24 var err error 25 switch ev.Type { 26 case "customer.subscription.created": 27 err = stripeHandle(e.stripeCreateSub, ev) 28 case "customer.subscription.deleted": 29 err = stripeHandle(e.stripeDeleteSub, ev) 30 case "customer.subscription.updated": 31 err = stripeHandle(e.stripeUpdateSub, ev) 32 default: 33 err = errors.New(ev.Type + ": Event type is not being processed") 34 } 35 36 if err == nil { 37 log.Printf("StripeEventHandler: Handled %s\n", ev.ID) 38 } else { 39 log.Printf("StripeEventHandler: %s: error: %s\n", ev.ID, err.Error()) 40 } 41 return err 42 } 43 44 func (e *Env) stripeCreateSub(s *stripe.Subscription) error { 45 return e.stripeUpdateSub(s) 46 } 47 48 func (e *Env) stripeUpdateSub(s *stripe.Subscription) error { 49 update, err := e.CacheStmt("create_sub", 50 `INSERT INTO spaceplanner.subscriptions (id, customer_id, price_id, product_id, active) 51 VALUES ($1, $2, $3, $4, $5) 52 ON CONFLICT (id) DO UPDATE SET (price_id, product_id, active) = ($3, $4, $5)`) 53 if err != nil { 54 return err 55 } 56 57 price, err := e.subPrice(s) 58 if err != nil { 59 return err 60 } 61 62 log.Printf("stripeHandleUpdateSub sub %s for %s (active: %v)\n", s.ID, s.Customer.ID, subIsActive(s)) 63 _, err = update.Exec(s.ID, s.Customer.ID, price.ID, price.Product.ID, subIsActive(s)) 64 return err 65 } 66 67 func (e *Env) stripeDeleteSub(s *stripe.Subscription) error { 68 delete, err := e.CacheStmt("delete_sub", 69 `DELETE FROM spaceplanner.subscriptions 70 WHERE id = $1`) 71 if err != nil { 72 return err 73 } 74 75 _, err = delete.Exec(s.ID) 76 return err 77 78 } 79 80 // Fetch all subscriptions anew 81 func (e *Env) StripeUpdateSubs() error { 82 log.Printf("Fetching subscriptions") 83 84 tx, err := e.DB.Begin() 85 if err != nil { 86 return err 87 } 88 defer tx.Rollback() 89 90 subs := e.Stripe.Subscriptions.List(nil) 91 for subs.Next() { 92 err := e.stripeUpdateSub(subs.Subscription()) 93 if err != nil { 94 return err 95 } 96 } 97 if err := subs.Err(); err != nil { 98 return err 99 } 100 101 return tx.Commit() 102 } 103 104 func stripeHandle[T any](handler func(*T) error, ev *stripe.Event) error { 105 var d T 106 if err := json.Unmarshal(ev.Data.Raw, &d); err != nil { 107 return err 108 } 109 110 return handler(&d) 111 } 112 113 func subIsActive(s *stripe.Subscription) bool { 114 return s.Status == stripe.SubscriptionStatusActive 115 } 116 117 func (e *Env) subPrice(s *stripe.Subscription) (*stripe.Price, error) { 118 if len(s.Items.Data) != 1 { 119 return nil, errors.New("Expected exactly one subscription item") 120 } 121 return s.Items.Data[0].Price, nil 122 }