|
@@ -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", |
|
|