Просмотр исходного кода

Add subscription creation webhook for Stripe

master
Immanuel Onyeka 8 месяцев назад
Родитель
Сommit
7e919e1a41
2 измененных файлов: 110 добавлений и 18 удалений
  1. +10
    -2
      migrations/0_29092022_setup_tables.sql
  2. +100
    -16
      skouter.go

+ 10
- 2
migrations/0_29092022_setup_tables.sql Просмотреть файл

@@ -45,9 +45,10 @@ CREATE TABLE user (
'Mortgage Broker',
'Executive',
'Other') NOT NULL,
status ENUM('Trial',
status ENUM('Unsubscribed',
'Trial',
'Free',
'Subscribed',
'Subscriber',
'Branch',
'Admin') DEFAULT 'Trial',
role ENUM('User', 'Manager', 'Admin') NOT NULL,
@@ -66,6 +67,13 @@ CREATE TABLE subscription (
price_id VARCHAR(255) NOT NULL,
/* Key used by stripejs */
client_secret VARCHAR(255) NOT NULL,
status ENUM('incomplete',
'incomplete_expired',
'trialing',
'active',
'past_due',
'canceled',
'unpaid'),
payment_status VARCHAR(50) NOT NULL,
current_period_end INT DEFAULT 0,
current_period_start INT DEFAULT 0,


+ 100
- 16
skouter.go Просмотреть файл

@@ -75,6 +75,7 @@ type Subscription struct {
End int `json:"end"`
ClientSecret string `json:"clientSecret,omitempty"`
PaymentStatus string `json:"paymentStatus"`
Status string `json:"status"`
}

type User struct {
@@ -236,6 +237,9 @@ type Endpoint func(http.ResponseWriter, *sql.DB, *http.Request)
type HookKeys struct {
InvoicePaid string
InvoiceFailed string
SubCreated string
SubUpdated string
SubDeleted string
}

var (
@@ -265,6 +269,15 @@ var roles = map[string]int{
"Admin": 3,
}

var statuses = map[string]int{
"Unsubscribed": 1,
"Trial": 2,
"Free": 3,
"Subscriber": 4,
"Branch": 5,
"Admin": 6,
}

var propertyTypes = []string{
"Single Detached",
"Single Attached",
@@ -283,6 +296,9 @@ var feeTypes = []string{
var hookKeys = HookKeys{
InvoicePaid: "",
InvoiceFailed: "",
SubCreated: "",
SubUpdated: "",
SubDeleted: "",
}

var standardPriceId = "price_1OZLK9BPMoXn2pf9kuTAf8rs"
@@ -1485,9 +1501,10 @@ func (sub *Subscription) insertSub(db *sql.DB) (error) {
current_period_end,
current_period_start,
client_secret,
payment_status
payment_status,
status
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
RETURNING id
`
@@ -1500,6 +1517,7 @@ func (sub *Subscription) insertSub(db *sql.DB) (error) {
sub.Start,
sub.ClientSecret,
sub.PaymentStatus,
sub.Status,
)

err = row.Scan(&sub.Id)
@@ -1511,7 +1529,7 @@ func (sub *Subscription) updateSub(db *sql.DB) error {
var err error

query = `UPDATE subscription
SET client_secret = ?, payment_status = ?
SET client_secret = ?, payment_status = ?,
WHERE id = ?
`
@@ -1585,7 +1603,7 @@ func updateAddress(address Address, db *sql.DB) error {
return err
}

func updateUser(user User, db *sql.DB) error {
func (user *User) update(db *sql.DB) error {
query := `
UPDATE user
SET
@@ -1647,7 +1665,7 @@ func setUser(user User, db *sql.DB) error {
return errors.New("Invalid role")
}

err = updateUser(user, db)
err = user.update(db)
if err != nil {
return err
}
@@ -3069,23 +3087,19 @@ func subscribe(w http.ResponseWriter, db *sql.DB, r *http.Request) {
user.Sub.ClientSecret = s.LatestInvoice.PaymentIntent.ClientSecret
user.Sub.PaymentStatus = string(s.LatestInvoice.PaymentIntent.Status)
// Inserting from here is probably unnecessary and confusing because
// new subs are already handled by the stripe hook
err = user.Sub.insertSub(db)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
json.NewEncoder(w).Encode(user.Sub)
} else {
err = user.Sub.updateSub(db)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
json.NewEncoder(w).Encode(user.Sub)
// This should handle creating a new subscription when the old one
// has an incomplete_expired status and cannot be paid
}
// Should check that subscription is still valid and has payment intent
// here
json.NewEncoder(w).Encode(user.Sub)
}

@@ -3132,7 +3146,6 @@ func invoicePaid(w http.ResponseWriter, db *sql.DB, r *http.Request) {
}
log.Println(user.Id, s.ID)
}

func invoiceFailed(w http.ResponseWriter, db *sql.DB, r *http.Request) {
@@ -3154,6 +3167,77 @@ func invoiceFailed(w http.ResponseWriter, db *sql.DB, r *http.Request) {
log.Println(event.Data)
}

// A successful subscription payment should be confirmed by Stripe and
// Updated through this hook.
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.SubCreated)
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.created" {
log.Println(
"Invalid event type sent to customer.subscription.created.")
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)
}
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 {
user.Sub.updateSub(db)
} else {
err = user.Sub.insertSub(db)
}
if err != nil {
http.Error(w, err.Error(), 500)
return
}
log.Println("User subscription created:", user.Id, sub.ID)
}

func api(w http.ResponseWriter, r *http.Request) {
var args []string

@@ -3443,7 +3527,7 @@ func seedUsers(db *sql.DB, addresses []Address, branches []Branch) []User {
users[i].Password = "test123"
users[i].Verified = true
users[i].Title = "Loan Officer"
users[i].Status = "Subscribed"
users[i].Status = "Subscriber"
users[i].Role = "User"
}



Загрузка…
Отмена
Сохранить