2
0
Эх сурвалжийг харах

Add missing internal abstractions

Mauricio Araujo 2 жил өмнө
parent
commit
2ff11286c8

+ 23 - 5
internal/billing/billing.go

@@ -9,14 +9,16 @@ import (
 
 // BillingManager contains methods for managing billing for a project
 type BillingManager interface {
-	// CreateTeam creates the concept of a billing "team". This is currently a one-to-one
-	// mapping with projects, but this may change in the future (i.e. multiple projects
-	// per same team)
 	CreateTeam(user *models.User, proj *models.Project) (teamID string, err error)
-
-	// DeleteTeam deletes a billing team.
 	DeleteTeam(user *models.User, proj *models.Project) (err error)
 
+	// CreateCustomer registers a project in the billing provider. This is currently a one-to-one
+	// mapping with projects and billing customers, because billing and usage are set per project.
+	CreateCustomer(userEmail string, proj *models.Project) (customerID string, err error)
+	ListPaymentMethod(proj *models.Project) (paymentMethods []types.PaymentMethod, err error)
+	CreatePaymentMethod(proj *models.Project) (clientSecret string, err error)
+	DeletePaymentMethod(paymentMethodID string) (err error)
+
 	// GetRedirectURI gets the redirect URI to send the user to the billing portal
 	GetRedirectURI(user *models.User, proj *models.Project) (url string, err error)
 
@@ -31,6 +33,22 @@ type BillingManager interface {
 // NoopBillingManager performs no billing operations
 type NoopBillingManager struct{}
 
+func (s *NoopBillingManager) CreateCustomer(userEmail string, proj *models.Project) (customerID string, err error) {
+	return "", nil
+}
+
+func (s *NoopBillingManager) ListPaymentMethod(proj *models.Project) (paymentMethods []types.PaymentMethod, err error) {
+	return []types.PaymentMethod{}, nil
+}
+
+func (s *NoopBillingManager) CreatePaymentMethod(proj *models.Project) (clientSecret string, err error) {
+	return "", nil
+}
+
+func (s *NoopBillingManager) DeletePaymentMethod(paymentMethodID string) (err error) {
+	return nil
+}
+
 func (n *NoopBillingManager) CreateTeam(user *models.User, proj *models.Project) (teamID string, err error) {
 	return fmt.Sprintf("%d", proj.ID), nil
 }

+ 106 - 0
internal/billing/stripe..go

@@ -0,0 +1,106 @@
+package billing
+
+import (
+	"fmt"
+
+	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/models"
+	"github.com/stripe/stripe-go/v76"
+	"github.com/stripe/stripe-go/v76/customer"
+	"github.com/stripe/stripe-go/v76/paymentmethod"
+	"github.com/stripe/stripe-go/v76/setupintent"
+)
+
+type StripeBillingManager struct {
+	StripeSecretKey string
+}
+
+func (s *StripeBillingManager) CreateCustomer(userEmail string, proj *models.Project) (customerID string, err error) {
+	stripe.Key = s.StripeSecretKey
+
+	if proj.BillingID == "" {
+		// Create customer if not exists
+		customerName := fmt.Sprintf("project_%s", proj.Name)
+		params := &stripe.CustomerParams{
+			Name:  stripe.String(customerName),
+			Email: stripe.String(userEmail),
+		}
+
+		// Create in Stripe
+		customer, err := customer.New(params)
+		if err != nil {
+			return "", err
+		}
+
+		customerID = customer.ID
+	}
+
+	return customerID, nil
+}
+
+func (s *StripeBillingManager) ListPaymentMethod(proj *models.Project) (paymentMethods []types.PaymentMethod, err error) {
+	stripe.Key = s.StripeSecretKey
+
+	params := &stripe.PaymentMethodListParams{
+		Customer: stripe.String(proj.BillingID),
+		Type:     stripe.String(string(stripe.PaymentMethodTypeCard)),
+	}
+	result := paymentmethod.List(params)
+
+	for result.Next() {
+		paymentMethods = append(paymentMethods, result.PaymentMethod())
+	}
+
+	return paymentMethods, nil
+}
+
+func (s *StripeBillingManager) CreatePaymentMethod(proj *models.Project) (clientSecret string, err error) {
+	stripe.Key = s.StripeSecretKey
+
+	params := &stripe.SetupIntentParams{
+		Customer: stripe.String(proj.BillingID),
+		AutomaticPaymentMethods: &stripe.SetupIntentAutomaticPaymentMethodsParams{
+			Enabled: stripe.Bool(false),
+		},
+		PaymentMethodTypes: []*string{stripe.String("card")},
+	}
+
+	intent, err := setupintent.New(params)
+	if err != nil {
+		return "", err
+	}
+
+	return intent.ClientSecret, nil
+}
+
+func (s *StripeBillingManager) DeletePaymentMethod(paymentMethodID string) (err error) {
+	stripe.Key = s.StripeSecretKey
+
+	_, err = paymentmethod.Detach(paymentMethodID, nil)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// TODO: remove these methods when the billing tech-debt is cleaned
+func (s *StripeBillingManager) CreateTeam(user *models.User, proj *models.Project) (teamID string, err error) {
+	return fmt.Sprintf("%d", proj.ID), nil
+}
+
+func (s *StripeBillingManager) DeleteTeam(user *models.User, proj *models.Project) (err error) {
+	return nil
+}
+
+func (s *StripeBillingManager) GetRedirectURI(user *models.User, proj *models.Project) (url string, err error) {
+	return "", nil
+}
+
+func (s *StripeBillingManager) ParseProjectUsageFromWebhook(payload []byte) (*models.ProjectUsage, *types.FeatureFlags, error) {
+	return nil, nil, nil
+}
+
+func (s *StripeBillingManager) VerifySignature(signature string, body []byte) bool {
+	return false
+}

+ 1 - 0
internal/models/project.go

@@ -129,6 +129,7 @@ type Project struct {
 	Name  string `json:"name"`
 	Roles []Role `json:"roles"`
 
+	BillingID           string
 	ProjectUsageID      uint
 	ProjectUsageCacheID uint