File size: 2,348 Bytes
1c4c66b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | package subscription
import (
"context"
"fmt"
"github.com/openmeterio/openmeter/openmeter/app"
"github.com/openmeterio/openmeter/openmeter/billing"
customerbilling "github.com/openmeterio/openmeter/openmeter/billing/validators/customerbilling"
"github.com/openmeterio/openmeter/openmeter/customer"
"github.com/openmeterio/openmeter/openmeter/subscription"
"github.com/openmeterio/openmeter/pkg/models"
)
type Validator struct {
subscription.NoOpSubscriptionCommandHook
billingService billing.Service
}
func NewValidator(billingService billing.Service) (subscription.SubscriptionCommandHook, error) {
if billingService == nil {
return nil, fmt.Errorf("billing service is required")
}
return &Validator{
billingService: billingService,
}, nil
}
func (v Validator) AfterCreate(ctx context.Context, view subscription.SubscriptionView) error {
err := v.validateBillingSetup(ctx, view)
if err != nil {
return models.NewGenericConflictError(fmt.Errorf("invalid billing setup: %w", err))
}
return nil
}
func (v Validator) AfterUpdate(ctx context.Context, view subscription.SubscriptionView) error {
err := v.validateBillingSetup(ctx, view)
if err != nil {
return models.NewGenericConflictError(fmt.Errorf("invalid billing setup: %w", err))
}
return nil
}
func (v Validator) validateBillingSetup(ctx context.Context, view subscription.SubscriptionView) error {
// If a subscription is going to be billed (e.g. there are phases with ratecards having prices)
// let's make sure that the billing setup is valid for the customer
if !v.hasBillableItems(view) {
return nil
}
return customerbilling.ValidateCustomerInvoicingApp(
ctx,
v.billingService,
customer.CustomerID{
Namespace: view.Subscription.Namespace,
ID: view.Subscription.CustomerId,
},
[]app.CapabilityType{
// For now we only support Stripe with automatic tax calculation and payment collection.
app.CapabilityTypeCalculateTax,
app.CapabilityTypeInvoiceCustomers,
app.CapabilityTypeCollectPayments,
},
)
}
func (v Validator) hasBillableItems(view subscription.SubscriptionView) bool {
for _, phase := range view.Phases {
for _, items := range phase.ItemsByKey {
for _, item := range items {
if item.SubscriptionItem.RateCard.AsMeta().Price != nil {
return true
}
}
}
}
return false
}
|