Skip to content

Commit

Permalink
GPTUBE 43: Billing endpoints & subscriptions server update (#47)
Browse files Browse the repository at this point in the history
* feat: adding run command to run in vercel

* feat: added api flder for serverless functions for express

* feat: renaming endpoints

* documentation: redeploying

* feat: subscription object updated from lemonsqueezy-go library, added correctly webhooks and saving data to firestore

* feat: updating the endpoints and adding extra meta field in the calling function

* feat: fixing lemon squeezy webhook issues and adding documentation to some endpoints

* feat: adding billing endpoints doucmentation and refactoring. Generated typescript models for later use

* feat: adding log print in lemonsqueezy webhooks
  • Loading branch information
webtaken authored Nov 6, 2023
1 parent fbddca8 commit ff0532b
Show file tree
Hide file tree
Showing 57 changed files with 7,651 additions and 179 deletions.
23 changes: 18 additions & 5 deletions go-server/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,24 @@ HUGGING_FACE_TOKEN="" # Token for the hugging face service
# CHATGPT OPENAI Configurations
OPENAI_API_KEY = "" # Secret API key

# LEMON SQUEEZY Config
LEMON_SQUEEZY_API_URL=""
LEMON_SQUEEZY_API_KEY=""
LEMON_SQUEEZY_STORE_ID=""
LEMON_SQUEEZY_WEBHOOK_PASSWORD=""
# LEMON SQUEEZY Config Global
LEMON_SQUEEZY_API_URL="https://api.lemonsqueezy.com/v1"

# LEMON SQUEEZY Config Test
LEMON_SQUEEZY_TEST_API_KEY=""
LEMON_SQUEEZY_TEST_STORE_ID=""
LEMON_SQUEEZY_TEST_WEBHOOK_PASSWORD=""
LEMON_SQUEEZY_TEST_FREE_PLAN_DATA=""
LEMON_SQUEEZY_TEST_HOBBY_PLAN_DATA=""
LEMON_SQUEEZY_TEST_POPULAR_PLAN_DATA=""

# LEMON SQUEEZY Config Production
LEMON_SQUEEZY_MAIN_API_KEY=""
LEMON_SQUEEZY_MAIN_STORE_ID=""
LEMON_SQUEEZY_MAIN_WEBHOOK_PASSWORD=""
LEMON_SQUEEZY_MAIN_FREE_PLAN_DATA=""
LEMON_SQUEEZY_MAIN_HOBBY_PLAN_DATA=""
LEMON_SQUEEZY_MAIN_POPULAR_PLAN_DATA=""

# FIREBASE SETUP
DB_KEYS_DEVELOPMENT=""
Expand Down
5 changes: 4 additions & 1 deletion go-server/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,7 @@ acme.json
# VEGETA tests
landing.json
pre_analysis.json
target.list
target.list

# Commands
commands
2 changes: 2 additions & 0 deletions go-server/config/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ const (
DEFAULT_PAGE_SIZE = 10
MIN_PAGE_SIZE = 1
MAX_PAGE_SIZE = 50
ENV_DEVELOPMENT = "development"
ENV_PRODUCTION = "production"
)

// use godot package to load/read the .env file and
Expand Down
141 changes: 130 additions & 11 deletions go-server/database/billing.go
Original file line number Diff line number Diff line change
@@ -1,35 +1,154 @@
package database

import (
firebase "firebase.google.com/go"
"encoding/json"
"errors"
"fmt"
"gptube/config"
"gptube/models"

"cloud.google.com/go/firestore"
"google.golang.org/api/iterator"
)

func RetrieveSubscriptions(email string) (*[]map[string]interface{}, error) {
app, err := firebase.NewApp(Ctx, nil, Sa)
func GetSubscriptionPlans() (map[models.SubscriptionPlanSlug]*models.SubscriptionPlan, error) {
envMode := config.Config("ENV_MODE")
if envMode == config.ENV_DEVELOPMENT {
return getSubscriptionPlansDevelopment()
} else if envMode == config.ENV_PRODUCTION {
return getSubscriptionPlansProduction()
}
return nil, errors.New("no valid env mode in env vars")
}

func getSubscriptionPlansDevelopment() (map[models.SubscriptionPlanSlug]*models.SubscriptionPlan, error) {
freePlan := models.SubscriptionPlan{}
err := json.Unmarshal(
[]byte(config.Config("LEMON_SQUEEZY_TEST_FREE_PLAN_DATA")),
&freePlan,
)
if err != nil {
return nil, err
}

client, err := app.Firestore(Ctx)
hobbyPlan := models.SubscriptionPlan{}
err = json.Unmarshal(
[]byte(config.Config("LEMON_SQUEEZY_TEST_HOBBY_PLAN_DATA")),
&hobbyPlan,
)
if err != nil {
return nil, err
}

defer client.Close()
popularPlan := models.SubscriptionPlan{}
err = json.Unmarshal(
[]byte(config.Config("LEMON_SQUEEZY_TEST_POPULAR_PLAN_DATA")),
&popularPlan,
)
if err != nil {
return nil, err
}

subscriptionPlans := make(map[models.SubscriptionPlanSlug]*models.SubscriptionPlan)
subscriptionPlans[models.FREE] = &freePlan
subscriptionPlans[models.HOBBY] = &hobbyPlan
subscriptionPlans[models.POPULAR] = &popularPlan
return subscriptionPlans, nil
}

func getSubscriptionPlansProduction() (map[models.SubscriptionPlanSlug]*models.SubscriptionPlan, error) {
freePlan := models.SubscriptionPlan{}
err := json.Unmarshal(
[]byte(config.Config("LEMON_SQUEEZY_MAIN_FREE_PLAN_DATA")),
&freePlan,
)
if err != nil {
return nil, err
}

hobbyPlan := models.SubscriptionPlan{}
err = json.Unmarshal(
[]byte(config.Config("LEMON_SQUEEZY_MAIN_HOBBY_PLAN_DATA")),
&hobbyPlan,
)
if err != nil {
return nil, err
}

popularPlan := models.SubscriptionPlan{}
err = json.Unmarshal(
[]byte(config.Config("LEMON_SQUEEZY_MAIN_POPULAR_PLAN_DATA")),
&popularPlan,
)
if err != nil {
return nil, err
}

subscriptionPlans := make(map[models.SubscriptionPlanSlug]*models.SubscriptionPlan)
subscriptionPlans[models.FREE] = &freePlan
subscriptionPlans[models.HOBBY] = &hobbyPlan
subscriptionPlans[models.POPULAR] = &popularPlan
return subscriptionPlans, nil
}

subscriptionsQuery := client.Collection("subscriptions").Where("user_email", "==", email)
subscriptions := subscriptionsQuery.Documents(Ctx)
results := make([]map[string]interface{}, 0)
func GetAllSubscriptions(userId string) ([]models.Subscription, error) {
client, err := GetClient()
if err != nil {
return nil, err
}

defer client.Close()
userDoc := client.Collection(USERS_COLLECTION).Doc(userId)
iter := userDoc.Collection(SUBSCRIPTIONS_COLLECTION).
OrderBy("subscription_id", firestore.Desc).Documents(Ctx)
results := make([]models.Subscription, 0)
for {
doc, err := subscriptions.Next()
doc, err := iter.Next()
if err == iterator.Done {
break
}
if err != nil {
return nil, err
}
results = append(results, doc.Data())
subscription := models.Subscription{}
err = doc.DataTo(&subscription)
if err != nil {
fmt.Println(err.Error())
return nil, err
}
results = append(results, subscription)
}
return results, nil
}

func CreateSubscription(userId string, subscription *models.Subscription) error {
client, err := GetClient()
if err != nil {
return err
}
defer client.Close()

userDoc := client.Collection(USERS_COLLECTION).Doc(userId)
subscriptionDoc := userDoc.Collection(SUBSCRIPTIONS_COLLECTION).Doc(subscription.SubscriptionId)
_, err = subscriptionDoc.Set(Ctx, subscription)
if err != nil {
return fmt.Errorf("error while creating subscription: %s", err)
}
return nil
}

func UpdateSubscription(userId string, subscription *models.Subscription) error {
client, err := GetClient()
if err != nil {
return err
}
defer client.Close()

userDoc := client.Collection(USERS_COLLECTION).Doc(userId)
subscriptionDoc := userDoc.Collection(SUBSCRIPTIONS_COLLECTION).Doc(subscription.SubscriptionId)
_, err = subscriptionDoc.Set(Ctx, subscription)
if err != nil {
return fmt.Errorf("error while creating subscription: %s", err)
}
return &results, nil
return nil
}
11 changes: 11 additions & 0 deletions go-server/database/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,17 @@ import (
"google.golang.org/api/option"
)

const (
USERS_COLLECTION = "Users"
// Billing collection names
SUBSCRIPTION_PLANS_COLLECTION = "SubscriptionPlans"
SUBSCRIPTIONS_COLLECTION = "Subscriptions"
// Youtube Analyses Collections
YOUTUBE_ANALYSES_COLLECTION = "YoutubeAnalyses"
YOUTUBE_NEGATIVE_COMMENTS_COLLECTION = "NegativeComments"
YOUTUBE_VIDEOS_COLLECTION = "Videos"
)

var Ctx context.Context
var Sa option.ClientOption

Expand Down
Loading

1 comment on commit ff0532b

@vercel
Copy link

@vercel vercel bot commented on ff0532b Nov 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

gptube-subscriptions – ./subscriptions-server

gptube-subscriptions-luckly083-gmailcom.vercel.app
gptube-subscriptions.vercel.app
gptube-subscriptions-git-main-luckly083-gmailcom.vercel.app

Please sign in to comment.