Ver código fonte

Create separate functions for subscription hooks

Creating separate endpoints for each webhook simplifies value checking for
event.Type and HookKeys while being easy to test for errors.
master
Immanuel Onyeka 7 meses atrás
pai
commit
6a412e5a47
1 arquivos alterados com 104 adições e 59 exclusões
  1. +104
    -59
      skouter.go

+ 104
- 59
skouter.go Ver arquivo

@@ -571,6 +571,23 @@ func fetchFeesTemp(db *sql.DB, user int, branch int) ([]FeeTemplate, error) {
return fees, nil
}

func constructEvent(r *http.Request, key string) (*stripe.Event, error) {
b, err := io.ReadAll(r.Body)
if err != nil {
log.Printf("io.ReadAll: %v", err)
return nil, err
}
event, err :=
webhook.ConstructEvent(b, r.Header.Get("Stripe-Signature"), key)
if err != nil {
log.Printf("webhook.ConstructEvent: %v", err)
return nil, err
}
return &event, nil
}

// Fetch fees from the database
func getFeesTemp(w http.ResponseWriter, db *sql.DB, r *http.Request) {
var fees []FeeTemplate
@@ -3037,6 +3054,28 @@ func createSubscription(cid string) (*stripe.Subscription, error) {
return s, err
}

func ( user *User ) SyncSub( sub *stripe.Subscription, db *sql.DB ) error {
var err error
user.Sub.UserId = user.Id
user.Sub.StripeId = sub.ID
user.Sub.CustomerId = user.CustomerId
user.Sub.PriceId = standardPriceId
user.Sub.End = int(sub.CurrentPeriodEnd)
user.Sub.Start = int(sub.CurrentPeriodStart)
user.Sub.ClientSecret = sub.LatestInvoice.PaymentIntent.ClientSecret
user.Sub.PaymentStatus = string(sub.LatestInvoice.PaymentIntent.Status)
user.Sub.Status = string(sub.Status)
if user.Sub.Id != 0 {
err = user.Sub.insertSub(db)
} else {
user.Sub.updateSub(db)
}
return err
}

// Creates a new subscription instance for a new user or retrieves the
// existing instance if possible. It's main purpose is to supply a
// client secret used for sending billing information to stripe.
@@ -3164,25 +3203,14 @@ func invoiceFailed(w http.ResponseWriter, db *sql.DB, r *http.Request) {
}

// Important for catching subscription creation through Stripe dashboard
// although it already happens at subscribe(). It checks if the user already
// has a subscription and replaces those fields if necessary so a seperate
// subCreated() is not necessary.
func subUpdated(w http.ResponseWriter, db *sql.DB, r *http.Request) {

// although it already happens at subscribe().
func subCreated(w http.ResponseWriter, db *sql.DB, r *http.Request) {
var sub stripe.Subscription
b, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
log.Printf("io.ReadAll: %v", err)
return
}
event, err := webhook.ConstructEvent(b,
r.Header.Get("Stripe-Signature"),
hookKeys.SubUpdated)
var err error

event, err := constructEvent(r, hookKeys.SubCreated)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
log.Printf("webhook.ConstructEvent: %v", err)
return
}
@@ -3191,7 +3219,7 @@ func subUpdated(w http.ResponseWriter, db *sql.DB, r *http.Request) {
w.WriteHeader(http.StatusOK)
if event.Type != "customer.subscription.created" {
log.Println(
"Invalid event type sent to customer.subscription.created.")
"Invalid event type. Expecting customer.subscription.created.")
return
}
@@ -3212,21 +3240,7 @@ func subUpdated(w http.ResponseWriter, db *sql.DB, r *http.Request) {
user.update(db)
}
user.Sub.UserId = user.Id
user.Sub.StripeId = sub.ID
user.Sub.CustomerId = user.CustomerId
user.Sub.PriceId = standardPriceId
user.Sub.End = int(sub.CurrentPeriodEnd)
user.Sub.Start = int(sub.CurrentPeriodStart)
user.Sub.ClientSecret = sub.LatestInvoice.PaymentIntent.ClientSecret
user.Sub.PaymentStatus = string(sub.LatestInvoice.PaymentIntent.Status)
user.Sub.Status = string(sub.Status)
if user.Sub.Id != 0 {
err = user.Sub.insertSub(db)
} else {
user.Sub.updateSub(db)
}
err = user.SyncSub(&sub, db)
if err != nil {
http.Error(w, err.Error(), 500)
@@ -3236,31 +3250,71 @@ func subUpdated(w http.ResponseWriter, db *sql.DB, r *http.Request) {
log.Println("User subscription created:", user.Id, sub.ID)
}

// Handles changes to customer subscriptions sent by Stripe
func subDeleted(w http.ResponseWriter, db *sql.DB, r *http.Request) {
// Checks if the user already has a subscription and replaces those fields if
// necessary.
func subUpdated(w http.ResponseWriter, db *sql.DB, r *http.Request) {
var sub stripe.Subscription
b, err := io.ReadAll(r.Body)
var err error

event, err := constructEvent(r, hookKeys.SubUpdated)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
log.Printf("io.ReadAll: %v", err)
return
}
event, err := webhook.ConstructEvent(b,
r.Header.Get("Stripe-Signature"),
hookKeys.SubUpdated)
// OK should be sent before any processing to confirm with Stripe that
// the hook was received
w.WriteHeader(http.StatusOK)
if event.Type != "customer.subscription.updated" {
log.Println(
"Invalid event type sent. Expecting customer.subscription.updated.")
return
}
json.Unmarshal(event.Data.Raw, &sub)
log.Println(event.Type, sub.ID, sub.Customer.ID)
user, err := queryCustomer(db, sub.Customer.ID)
if err != nil {
log.Printf("Could not query customer: %v", err)
return
}
if statuses[user.Status] < 5 && sub.Status == "trialing" {
user.Status = "Trial"
user.update(db)
} else if sub.Status != "active" {
user.Status = "Unsubscribed"
user.update(db)
}
err = user.SyncSub(&sub, db)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
log.Println("User subscription created:", user.Id, sub.ID)
}

// Handles deleted subscriptions hooks sent by Stripe
func subDeleted(w http.ResponseWriter, db *sql.DB, r *http.Request) {
var sub stripe.Subscription
var err error

event, err := constructEvent(r, hookKeys.SubDeleted)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
log.Printf("webhook.ConstructEvent: %v", err)
return
}
// OK should be sent before any processing to confirm with Stripe that
// the hook was received
w.WriteHeader(http.StatusOK)
if event.Type != "customer.subscription.updated" {
if event.Type != "customer.subscription.deleted" {
log.Println(
"Invalid event type sent to customer.subscription.updated.")
"Invalid event type sent. Expecting customer.subscription.deleted.")
return
}
@@ -3281,21 +3335,9 @@ func subDeleted(w http.ResponseWriter, db *sql.DB, r *http.Request) {
user.update(db)
}
user.Sub.UserId = user.Id
user.Sub.StripeId = sub.ID
user.Sub.CustomerId = user.CustomerId
user.Sub.PriceId = standardPriceId
user.Sub.End = int(sub.CurrentPeriodEnd)
user.Sub.Start = int(sub.CurrentPeriodStart)
user.Sub.ClientSecret = sub.LatestInvoice.PaymentIntent.ClientSecret
user.Sub.PaymentStatus = string(sub.LatestInvoice.PaymentIntent.Status)
user.Sub.Status = string(sub.Status)
user.Sub.Status = "canceled"
if user.Sub.Id != 0 {
err = user.Sub.insertSub(db)
} else {
user.Sub.updateSub(db)
}
err = user.SyncSub(&sub, db)
if err != nil {
http.Error(w, err.Error(), 500)
@@ -3435,13 +3477,13 @@ func api(w http.ResponseWriter, r *http.Request) {
invoiceFailed(w, db, r)
case match(p, "/api/stripe/sub-created", &args) &&
r.Method == http.MethodPost:
subUpdated(w, db, r)
subCreated(w, db, r)
case match(p, "/api/stripe/sub-updated", &args) &&
r.Method == http.MethodPost:
subUpdated(w, db, r)
case match(p, "/api/stripe/sub-updated", &args) &&
case match(p, "/api/stripe/sub-deleted", &args) &&
r.Method == http.MethodPost:
subUpdated(w, db, r)
subDeleted(w, db, r)
default:
http.Error(w, "Invalid route or token", 404)
}
@@ -3774,6 +3816,9 @@ func dev(args []string) {
hookKeys = HookKeys{
InvoicePaid: os.Getenv("DEV_WEBHOOK_KEY"),
InvoiceFailed: os.Getenv("DEV_WEBHOOK_KEY"),
SubCreated: os.Getenv("DEV_WEBHOOK_KEY"),
SubUpdated: os.Getenv("DEV_WEBHOOK_KEY"),
SubDeleted: os.Getenv("DEV_WEBHOOK_KEY"),
}

db, err := sql.Open("mysql",


Carregando…
Cancelar
Salvar