Browse Source

Fix Stripe webhook errors

master
Immanuel Onyeka 7 months ago
parent
commit
33e71edc8e
5 changed files with 72 additions and 77 deletions
  1. +1
    -0
      components/settings.vue
  2. +4
    -2
      components/unsubscribe.vue
  3. +1
    -1
      grav-admin/user/js/registration/billing.vue
  4. +0
    -2
      grav-admin/user/js/registration/registration.vue
  5. +66
    -72
      skouter.go

+ 1
- 0
components/settings.vue View File

@@ -84,6 +84,7 @@
</Dialog> </Dialog>


<UnsubPrompt v-if="unsubing" :token="token" <UnsubPrompt v-if="unsubing" :token="token"
:cancelAtEnd="props.user.sub.cancelAtEnd"
@close="() => unsubing = false" @close="() => unsubing = false"
@cancelSub="() => { $emit('updateUser'); unsubing = false }" @cancelSub="() => { $emit('updateUser'); unsubing = false }"
/> />


+ 4
- 2
components/unsubscribe.vue View File

@@ -1,7 +1,9 @@
<template> <template>


<Dialog @close="() => $emit('close')"> <Dialog @close="() => $emit('close')">
<h3>Are you sure you want to unsubscribe?</h3>
<h3>
Are you sure you want to {{cancelAtEnd ? "subscribe" : "unsubscribe"}}?
</h3>
<button @click="() => $emit('close')">Cancel</button> <button @click="() => $emit('close')">Cancel</button>
<button @click="unsubscribe">Confirm</button> <button @click="unsubscribe">Confirm</button>
</Dialog> </Dialog>
@@ -12,7 +14,7 @@
import Dialog from "./dialog.vue" import Dialog from "./dialog.vue"


const emit = defineEmits(['close', 'cancelSub']) const emit = defineEmits(['close', 'cancelSub'])
const props = defineProps(['token'])
const props = defineProps(['token', 'cancelAtEnd'])


function unsubscribe() { function unsubscribe() {
fetch(`/api/user/unsubscribe`, fetch(`/api/user/unsubscribe`,


+ 1
- 1
grav-admin/user/js/registration/billing.vue View File

@@ -23,7 +23,7 @@ function submit() {
//`Elements` instance that was used to create the Payment Element //`Elements` instance that was used to create the Payment Element
elements, elements,
confirmParams: { confirmParams: {
return_url: "https://skouter.net/register",
return_url: `https://${window.location.host}/app`,
} }
}) })
} }


+ 0
- 2
grav-admin/user/js/registration/registration.vue View File

@@ -50,7 +50,6 @@ function getUser() {
} }


function create(user) { function create(user) {
console.log(user)
fetch(`/api/user`, fetch(`/api/user`,
{method: 'POST', {method: 'POST',
@@ -89,7 +88,6 @@ function intent(u) {
if (resp.ok) { if (resp.ok) {
resp.json().then(s => { resp.json().then(s => {
err.value = "" err.value = ""
console.log(s)
sub.value = s sub.value = s
if (["processing", "succeeded"].includes(s.paymentStatus) && if (["processing", "succeeded"].includes(s.paymentStatus) &&


+ 66
- 72
skouter.go View File

@@ -32,6 +32,8 @@ import (
"github.com/stripe/stripe-go/v76/customer" "github.com/stripe/stripe-go/v76/customer"
"github.com/stripe/stripe-go/v76/subscription" "github.com/stripe/stripe-go/v76/subscription"
"github.com/stripe/stripe-go/v76/webhook" "github.com/stripe/stripe-go/v76/webhook"
"github.com/stripe/stripe-go/v76/invoice"
"github.com/stripe/stripe-go/v76/paymentintent"
"image" "image"
_ "image/jpeg" _ "image/jpeg"
"image/png" "image/png"
@@ -297,10 +299,10 @@ var feeTypes = []string{
} }


var hookKeys = HookKeys{ var hookKeys = HookKeys{
InvoicePaid: "",
InvoicePaid: os.Getenv("STRIPE_SIGNING_SECRET_INVOICE_PAID"),
InvoiceFailed: "", InvoiceFailed: "",
SubCreated: "",
SubUpdated: "",
SubCreated: os.Getenv("STRIPE_SIGNING_SECRET_SUB_CREATED"),
SubUpdated: os.Getenv("STRIPE_SIGNING_SECRET_SUB_UPDATED"),
SubDeleted: "", SubDeleted: "",
} }


@@ -1269,58 +1271,22 @@ func queryCustomer(db *sql.DB, id string) (User, error) {
var err error var err error


query = `SELECT query = `SELECT
u.id,
u.email,
u.first_name,
u.last_name,
coalesce(u.branch_id, 0),
u.country,
u.title,
coalesce(u.status, ''),
coalesce(u.customer_id, ''),
u.verified,
u.role,
u.address,
u.phone
u.id
FROM user u WHERE u.customer_id = ? FROM user u WHERE u.customer_id = ?
` `
row := db.QueryRow(query, id) row := db.QueryRow(query, id)


if err != nil {
return user, err
}

err = row.Scan( err = row.Scan(
&user.Id, &user.Id,
&user.Email,
&user.FirstName,
&user.LastName,
&user.Branch.Id,
&user.Country,
&user.Title,
&user.Status,
&user.CustomerId,
&user.Verified,
&user.Role,
&user.Address.Id,
&user.Phone,
) )


if err != nil { if err != nil {
return user, err return user, err
} }


user.Address, err = queryAddress(db, user.Address.Id)
if err != nil {
return user, err
}

user.Branch, err = queryBranch(db, user.Branch.Id)
if err != nil {
return user, err
}
user, err = queryUser(db, user.Id)


return user, nil
return user, err
} }


// Can probably be deleted. // Can probably be deleted.
@@ -1610,22 +1576,25 @@ func (sub *Subscription) updateSub(db *sql.DB) error {
var err error var err error


query = `UPDATE subscription query = `UPDATE subscription
SET client_secret = CASE @a := ? WHEN '' THEN client_secret ELSE @a END,
current_period_end = CASE
@b := ? WHEN 0 THEN current_period_end ELSE @b END,
current_period_start = CASE
@c := ? WHEN 0 THEN current_period_start ELSE @c END,
payment_status = CASE @d := ? WHEN '' THEN payment_status ELSE @d END,
status = CASE @e := ? WHEN '' THEN status ELSE @e END,
SET client_secret = IF(? = '', client_secret, ?),
current_period_end = IF(? = 0, current_period_end, ?),
current_period_start = IF(? = 0, current_period_start, ?),
payment_status = IF(? = '', payment_status, ?),
status = IF(? = '', status, ?),
cancel_at_end = ? cancel_at_end = ?
WHERE id = ? WHERE id = ?
` `
_, err = db.Exec(query, _, err = db.Exec(query,
sub.ClientSecret,
sub.ClientSecret, sub.ClientSecret,
sub.End, sub.End,
sub.End,
sub.Start,
sub.Start, sub.Start,
sub.PaymentStatus, sub.PaymentStatus,
sub.PaymentStatus,
sub.Status,
sub.Status, sub.Status,
sub.CancelAtEnd, sub.CancelAtEnd,
sub.Id, sub.Id,
@@ -1691,7 +1660,8 @@ func (user *User) update(db *sql.DB) error {
first_name = CASE @fn := ? WHEN '' THEN first_name ELSE @fn END, first_name = CASE @fn := ? WHEN '' THEN first_name ELSE @fn END,
last_name = CASE @ln := ? WHEN '' THEN last_name ELSE @ln END, last_name = CASE @ln := ? WHEN '' THEN last_name ELSE @ln END,
role = CASE @r := ? WHEN '' THEN role ELSE @r END, role = CASE @r := ? WHEN '' THEN role ELSE @r END,
password = CASE @p := ? WHEN '' THEN password ELSE sha2(@p, 256) END
password = CASE @p := ? WHEN '' THEN password ELSE sha2(@p, 256) END,
verified = ?
WHERE id = ? WHERE id = ?
` `


@@ -1701,6 +1671,7 @@ func (user *User) update(db *sql.DB) error {
user.LastName, user.LastName,
user.Role, user.Role,
user.Password, user.Password,
user.Verified,
user.Id, user.Id,
) )


@@ -3193,24 +3164,36 @@ func (sub *Subscription) CancelSubscription(cancel bool) (*stripe.Subscription,
return s, err return s, err
} }


// Retrieves additional invoice information from Stripe
func ( user *User ) SyncSub( sub *stripe.Subscription, db *sql.DB ) error { func ( user *User ) SyncSub( sub *stripe.Subscription, db *sql.DB ) error {
var err error var err error
stripe.Key = os.Getenv("STRIPE_SECRET_KEY")
i, err := invoice.Get(sub.LatestInvoice.ID, &stripe.InvoiceParams{})
p, err := paymentintent.Get(i.PaymentIntent.ID,
&stripe.PaymentIntentParams{})
if err != nil {
return err
}

user.Sub.UserId = user.Id user.Sub.UserId = user.Id
user.Sub.StripeId = sub.ID user.Sub.StripeId = sub.ID
user.Sub.CustomerId = user.CustomerId user.Sub.CustomerId = user.CustomerId
user.Sub.PriceId = standardPriceId user.Sub.PriceId = standardPriceId
user.Sub.End = int(sub.CurrentPeriodEnd) user.Sub.End = int(sub.CurrentPeriodEnd)
user.Sub.Start = int(sub.CurrentPeriodStart) user.Sub.Start = int(sub.CurrentPeriodStart)
user.Sub.ClientSecret = sub.LatestInvoice.PaymentIntent.ClientSecret
user.Sub.PaymentStatus = string(sub.LatestInvoice.PaymentIntent.Status)
user.Sub.ClientSecret = p.ClientSecret
user.Sub.PaymentStatus = string(p.Status)
user.Sub.Status = string(sub.Status) user.Sub.Status = string(sub.Status)
user.Sub.CancelAtEnd = sub.CancelAtPeriodEnd user.Sub.CancelAtEnd = sub.CancelAtPeriodEnd
log.Println("New status of sub:", sub.Status, p.Status, user.Sub.Id)
if user.Sub.Id != 0 {
if user.Sub.Id == 0 {
err = user.Sub.insertSub(db) err = user.Sub.insertSub(db)
} else { } else {
user.Sub.updateSub(db)
err = user.Sub.updateSub(db)
} }
return err return err
@@ -3378,6 +3361,8 @@ func unsubscribe(w http.ResponseWriter, db *sql.DB, r *http.Request) {
// Updated through this hook. // Updated through this hook.
func invoicePaid(w http.ResponseWriter, db *sql.DB, r *http.Request) { func invoicePaid(w http.ResponseWriter, db *sql.DB, r *http.Request) {
var invoice stripe.Invoice var invoice stripe.Invoice
stripe.Key = os.Getenv("STRIPE_SECRET_KEY")
b, err := io.ReadAll(r.Body) b, err := io.ReadAll(r.Body)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) http.Error(w, err.Error(), http.StatusBadRequest)
@@ -3416,7 +3401,13 @@ func invoicePaid(w http.ResponseWriter, db *sql.DB, r *http.Request) {
return return
} }
log.Println(user.Id, s.ID)
err = user.SyncSub(s, db)
if err != nil {
log.Printf("Could not sync subscription: %v", err)
return
}
log.Println("User's invoice paid: ", user.Id, s.ID)
} }


func invoiceFailed(w http.ResponseWriter, db *sql.DB, r *http.Request) { func invoiceFailed(w http.ResponseWriter, db *sql.DB, r *http.Request) {
@@ -3479,7 +3470,8 @@ func subCreated(w http.ResponseWriter, db *sql.DB, r *http.Request) {
err = user.SyncSub(&sub, db) err = user.SyncSub(&sub, db)
if err != nil { if err != nil {
http.Error(w, err.Error(), 500)
log.Println("Could not create subscription due to error: ",
err.Error())
return return
} }
@@ -3518,20 +3510,21 @@ func subUpdated(w http.ResponseWriter, db *sql.DB, r *http.Request) {
if statuses[user.Status] < 5 && sub.Status == "trialing" { if statuses[user.Status] < 5 && sub.Status == "trialing" {
user.Status = "Trial" user.Status = "Trial"
user.update(db)
err = user.update(db)
} else if sub.Status != "active" { } else if sub.Status != "active" {
user.Status = "Unsubscribed" user.Status = "Unsubscribed"
user.update(db)
err = user.update(db)
} }
err = user.SyncSub(&sub, db) err = user.SyncSub(&sub, db)
if err != nil { if err != nil {
http.Error(w, err.Error(), 500)
log.Println("Could not update subscription due to error: ",
err.Error())
return return
} }
log.Println("User subscription created:", user.Id, sub.ID)
log.Println("User subscription updated:", user.Id, sub.ID)
} }


// Handles deleted subscriptions hooks sent by Stripe // Handles deleted subscriptions hooks sent by Stripe
@@ -3565,10 +3558,10 @@ func subDeleted(w http.ResponseWriter, db *sql.DB, r *http.Request) {
if statuses[user.Status] < 5 && sub.Status == "trialing" { if statuses[user.Status] < 5 && sub.Status == "trialing" {
user.Status = "Trial" user.Status = "Trial"
user.update(db)
err = user.update(db)
} else if sub.Status != "active" { } else if sub.Status != "active" {
user.Status = "Unsubscribed" user.Status = "Unsubscribed"
user.update(db)
err = user.update(db)
} }
user.Sub.Status = "canceled" user.Sub.Status = "canceled"
@@ -3576,11 +3569,12 @@ func subDeleted(w http.ResponseWriter, db *sql.DB, r *http.Request) {
err = user.SyncSub(&sub, db) err = user.SyncSub(&sub, db)
if err != nil { if err != nil {
http.Error(w, err.Error(), 500)
log.Println("Could not delete subscription due to error: ",
err.Error())
return return
} }
log.Println("User subscription created:", user.Id, sub.ID)
log.Println("User subscription deleted:", user.Id, sub.ID)
} }


func verificationToken(id int) (string, error) { func verificationToken(id int) (string, error) {
@@ -3626,6 +3620,13 @@ func verifyUser(w http.ResponseWriter, db *sql.DB, r *http.Request) {
log.Println("Verification claim invalid. ID:", claims.Id) log.Println("Verification claim invalid. ID:", claims.Id)
return return
} }
user, err := queryUser(db, claims.Id)
user.Verified = true
err = user.update(db)
log.Println("User verified:", user.Id, user.Email)
w.Write([]byte("Verification complete."))
} }


func (user *User) sendVerificationEmail() { func (user *User) sendVerificationEmail() {
@@ -3654,7 +3655,7 @@ func (user *User) sendVerificationEmail() {


message := `Welcome %s, message := `Welcome %s,
Click the link below to verify your email address Click the link below to verify your email address
https://%s?verification_token=%s`
https://%s/api/user/verify?verification_token=%s`
t, err := verificationToken(user.Id) t, err := verificationToken(user.Id)


@@ -4161,13 +4162,6 @@ func dbSeed(db *sql.DB) {
func dev(args []string) { func dev(args []string) {
stripe.Key = os.Getenv("STRIPE_SECRET_KEY") stripe.Key = os.Getenv("STRIPE_SECRET_KEY")
standardPriceId = "price_1OZLK9BPMoXn2pf9kuTAf8rs" standardPriceId = "price_1OZLK9BPMoXn2pf9kuTAf8rs"
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", db, err := sql.Open("mysql",
fmt.Sprintf("%s:%s@tcp(127.0.0.1:3306)/%s?multiStatements=true", fmt.Sprintf("%s:%s@tcp(127.0.0.1:3306)/%s?multiStatements=true",


Loading…
Cancel
Save