From 358df2153b7631bcb93d75915f56a685e6d9addb Mon Sep 17 00:00:00 2001 From: nutcas3 Date: Sat, 2 May 2026 19:09:54 +0300 Subject: [PATCH] feat: Add production MVNO provisioner with tenant, carrier, billing, and API access setup - Add ProductionProvisioner struct with tenant, carrier, billing, and storage service dependencies - Add TenantService, CarrierManager, BillingService, and StorageService interfaces for external integrations - Add TenantConfig and CarrierInfo structs for tenant and carrier data models - Add NewProductionProvisioner constructor with service dependency injection - Add ProvisionResources method with tenant context --- .../internal/mvno/provisioner_production.go | 196 ++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 apps/carrier-connector/internal/mvno/provisioner_production.go diff --git a/apps/carrier-connector/internal/mvno/provisioner_production.go b/apps/carrier-connector/internal/mvno/provisioner_production.go new file mode 100644 index 0000000..87b75a6 --- /dev/null +++ b/apps/carrier-connector/internal/mvno/provisioner_production.go @@ -0,0 +1,196 @@ +package mvno + +import ( + "context" + "fmt" + "time" + + "github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/id" + "github.com/sirupsen/logrus" +) + +// ProductionProvisioner implements production-ready MVNO provisioning +type ProductionProvisioner struct { + logger *logrus.Logger + tenantService TenantService + carrierManager CarrierManager + billingService BillingService + storageService StorageService +} + +// TenantService interface for tenant management +type TenantService interface { + CreateTenant(ctx context.Context, tenant *TenantConfig) error + GetTenant(ctx context.Context, tenantID string) (*TenantConfig, error) +} + +// CarrierManager interface for carrier operations +type CarrierManager interface { + GetCarriersByCountry(ctx context.Context, country string) ([]CarrierInfo, error) + ConfigureCarrier(ctx context.Context, mvnoID, carrierID string) error +} + +// BillingService interface for billing operations +type BillingService interface { + CreateBillingAccount(ctx context.Context, mvnoID string, plan MVNOPlan) (string, error) + CreateRatePlans(ctx context.Context, mvnoID, billingID string, plan MVNOPlan) error + SetupPaymentGateway(ctx context.Context, mvnoID, billingID string) error +} + +// StorageService interface for storage operations +type StorageService interface { + CreateStorageBucket(ctx context.Context, mvnoID string, sizeGB int) error + CreateDatabaseSchema(ctx context.Context, mvnoID string) error +} + +// TenantConfig represents tenant configuration +type TenantConfig struct { + ID string `json:"id"` + Name string `json:"name"` + Plan MVNOPlan `json:"plan"` + Settings map[string]any `json:"settings"` +} + +// CarrierInfo represents carrier information +type CarrierInfo struct { + ID string `json:"id"` + Name string `json:"name"` + Countries []string `json:"countries"` + SMDEndpoint string `json:"smd_endpoint"` + IsActive bool `json:"is_active"` +} + +// NewProductionProvisioner creates a new production provisioner +func NewProductionProvisioner( + logger *logrus.Logger, + tenantService TenantService, + carrierManager CarrierManager, + billingService BillingService, + storageService StorageService, +) *ProductionProvisioner { + return &ProductionProvisioner{ + logger: logger, + tenantService: tenantService, + carrierManager: carrierManager, + billingService: billingService, + storageService: storageService, + } +} + +// ProvisionResources provisions core resources for the MVNO +func (p *ProductionProvisioner) ProvisionResources(ctx context.Context, mvno *MVNO) error { + p.logger.WithField("mvno_id", mvno.ID).Info("Provisioning production resources") + + // Create tenant context + if err := p.provisionTenantContext(ctx, mvno); err != nil { + return fmt.Errorf("failed to provision tenant context: %w", err) + } + + // Provision database schema + if err := p.provisionDatabaseSchema(ctx, mvno); err != nil { + return fmt.Errorf("failed to provision database schema: %w", err) + } + + // Provision storage resources + if err := p.provisionStorageResources(ctx, mvno); err != nil { + return fmt.Errorf("failed to provision storage resources: %w", err) + } + + return nil +} + +// SetupCarriers configures carrier connections for the MVNO +func (p *ProductionProvisioner) SetupCarriers(ctx context.Context, mvno *MVNO) error { + p.logger.WithField("mvno_id", mvno.ID).Info("Setting up production carrier connections") + + selectedCarriers, err := p.selectCarriers(mvno.Config.AllowedCountries) + if err != nil { + return fmt.Errorf("failed to select carriers: %w", err) + } + + // Configure each carrier + for _, carrierID := range selectedCarriers { + if err := p.configureCarrier(ctx, mvno, carrierID); err != nil { + p.logger.WithError(err).WithField("carrier_id", carrierID).Error("Failed to configure carrier") + return fmt.Errorf("failed to configure carrier %s: %w", carrierID, err) + } + } + + mvno.Config.CarrierPool = selectedCarriers + p.logger.WithFields(map[string]any{ + "mvno_id": mvno.ID, + "carrier_count": len(selectedCarriers), + }).Info("Carriers configured successfully") + + return nil +} + +// SetupBilling configures billing system for the MVNO +func (p *ProductionProvisioner) SetupBilling(ctx context.Context, mvno *MVNO) error { + p.logger.WithField("mvno_id", mvno.ID).Info("Setting up production billing system") + + // Create billing account + billingID, err := p.billingService.CreateBillingAccount(ctx, mvno.ID, mvno.Plan) + if err != nil { + return fmt.Errorf("failed to create billing account: %w", err) + } + + // Configure rate plans + if err := p.configureRatePlans(ctx, mvno, billingID); err != nil { + return fmt.Errorf("failed to configure rate plans: %w", err) + } + + // Setup payment processing + if err := p.setupPaymentProcessing(ctx, mvno, billingID); err != nil { + return fmt.Errorf("failed to setup payment processing: %w", err) + } + + p.logger.WithFields(map[string]any{ + "mvno_id": mvno.ID, + "billing_id": billingID, + "plan": mvno.Plan, + }).Info("Billing system configured successfully") + + return nil +} + +// SetupAPIAccess configures API access for the MVNO +func (p *ProductionProvisioner) SetupAPIAccess(ctx context.Context, mvno *MVNO) error { + if !mvno.Config.APIAccess { + p.logger.WithField("mvno_id", mvno.ID).Info("API access not included in plan") + return nil + } + + p.logger.WithField("mvno_id", mvno.ID).Info("Setting up production API access") + + // Generate API credentials + apiKey := id.GeneratePrefixed("api") + secretKey := id.GeneratePrefixed("sec") + permissions := p.getAPIPermissions(mvno.Plan) + + // Store API configuration in tenant service + tenant, err := p.tenantService.GetTenant(ctx, mvno.ID) + if err != nil { + return fmt.Errorf("failed to get tenant for API setup: %w", err) + } + + tenant.Settings["api_access"] = map[string]any{ + "api_key": apiKey, + "secret_key": secretKey, + "permissions": permissions, + "created_at": time.Now(), + "expires_at": time.Now().AddDate(1, 0, 0), // 1 year expiry + } + + if err := p.tenantService.CreateTenant(ctx, tenant); err != nil { + return fmt.Errorf("failed to store API configuration: %w", err) + } + + p.logger.WithFields(map[string]any{ + "mvno_id": mvno.ID, + "api_key": apiKey[:8] + "...", + "permissions": len(permissions), + }).Info("API access configured successfully") + + return nil +}