Files
telecom-platform/apps/carrier-connector/internal/services/service_subscription.go
T
nutcas3 f112aa4c52 refactor: Replace map[string]interface{} with map[string]any across currency, handlers, integration, and rateplan packages
- Update BillingSummary.Breakdown field type from map[string]interface{} to map[string]any
- Update Transaction.Metadata field type from map[string]interface{} to map[string]any
- Update RatePlanCurrencyIntegrator subscription and billing metadata to use map[string]any
- Update handler request/response types to use map[string]any (CreateRatePlanRequest, UpdateRatePlanRequest,
2026-05-02 03:21:42 +03:00

198 lines
6.0 KiB
Go

package services
import (
"context"
"fmt"
"time"
"github.com/google/uuid"
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/repository"
"github.com/sirupsen/logrus"
)
func (s *Service) SubscribeToPlan(ctx context.Context, req *repository.SubscribeRequest) (*repository.RatePlanSubscription, error) {
if err := s.validateSubscribeRequest(req); err != nil {
return nil, fmt.Errorf("validation failed: %w", err)
}
plan, err := s.repo.GetRatePlan(ctx, req.RatePlanID)
if err != nil {
return nil, err
}
if !plan.IsActive || plan.Status != repository.PlanStatusActive {
return nil, fmt.Errorf("rate plan is not available for subscription")
}
activeSub, err := s.repo.GetActiveSubscription(ctx, req.ProfileID)
if err != nil {
return nil, err
}
if activeSub != nil {
return nil, fmt.Errorf("profile already has an active subscription")
}
// Create subscription
subscription := &repository.RatePlanSubscription{
ID: uuid.New().String(),
ProfileID: req.ProfileID,
RatePlanID: req.RatePlanID,
Status: repository.SubscriptionStatusActive,
StartedAt: time.Now(),
BillingCycle: plan.BillingCycle,
NextBillingDate: s.calculateNextBillingDate(plan.BillingCycle, time.Now()),
AutoRenew: req.AutoRenew,
CurrentCycle: time.Now(),
AppliedDiscounts: req.AppliedDiscounts,
Metadata: req.Metadata,
}
if err := s.repo.CreateSubscription(ctx, subscription); err != nil {
s.logger.WithError(err).Error("Failed to create subscription")
return nil, err
}
s.logger.WithFields(logrus.Fields{
"subscription_id": subscription.ID,
"profile_id": req.ProfileID,
"rate_plan_id": req.RatePlanID,
}).Info("Subscription created successfully")
return subscription, nil
}
// GetSubscription retrieves a subscription by ID
func (s *Service) GetSubscription(ctx context.Context, id string) (*repository.RatePlanSubscription, error) {
subscription, err := s.repo.GetSubscription(ctx, id)
if err != nil {
s.logger.WithError(err).WithField("subscription_id", id).Error("Failed to get subscription")
return nil, err
}
return subscription, nil
}
// UpdateSubscription updates an existing subscription
func (s *Service) UpdateSubscription(ctx context.Context, subscription *repository.RatePlanSubscription) (*repository.RatePlanSubscription, error) {
if err := s.repo.UpdateSubscription(ctx, subscription); err != nil {
s.logger.WithError(err).Error("Failed to update subscription")
return nil, err
}
s.logger.WithField("subscription_id", subscription.ID).Info("Subscription updated successfully")
return subscription, nil
}
// CancelSubscription cancels a subscription
func (s *Service) CancelSubscription(ctx context.Context, subscriptionID string, reason string) error {
subscription, err := s.repo.GetSubscription(ctx, subscriptionID)
if err != nil {
return err
}
if subscription.Status != repository.SubscriptionStatusActive {
return fmt.Errorf("subscription is not active")
}
now := time.Now()
subscription.Status = repository.SubscriptionStatusCancelled
subscription.EndedAt = &now
subscription.UpdatedAt = now
if subscription.Metadata == nil {
subscription.Metadata = make(map[string]any)
}
subscription.Metadata["cancellation_reason"] = reason
if err := s.repo.UpdateSubscription(ctx, subscription); err != nil {
s.logger.WithError(err).Error("Failed to cancel subscription")
return err
}
s.logger.WithField("subscription_id", subscriptionID).Info("Subscription cancelled successfully")
return nil
}
// GetActiveSubscription retrieves the active subscription for a profile
func (s *Service) GetActiveSubscription(ctx context.Context, profileID string) (*repository.RatePlanSubscription, error) {
subscription, err := s.repo.GetActiveSubscription(ctx, profileID)
if err != nil {
s.logger.WithError(err).WithField("profile_id", profileID).Error("Failed to get active subscription")
return nil, err
}
return subscription, nil
}
// ListSubscriptions retrieves subscriptions for a profile
func (s *Service) ListSubscriptions(ctx context.Context, profileID string, filter *repository.SubscriptionFilter) ([]*repository.RatePlanSubscription, error) {
subscriptions, err := s.repo.ListSubscriptions(ctx, profileID, filter)
if err != nil {
s.logger.WithError(err).Error("Failed to list subscriptions")
return nil, err
}
return subscriptions, nil
}
func (s *Service) RecordUsage(ctx context.Context, req *repository.RecordUsageRequest) (*repository.RatePlanUsage, error) {
// Get active subscription
subscription, err := s.repo.GetActiveSubscription(ctx, req.ProfileID)
if err != nil {
return nil, err
}
if subscription == nil {
return nil, fmt.Errorf("no active subscription found")
}
// Get current usage
currentUsage, err := s.repo.GetCurrentUsage(ctx, req.ProfileID)
if err != nil {
return nil, err
}
// Create or update usage record
var usage *repository.RatePlanUsage
if currentUsage == nil {
// Create new usage record
usage = &repository.RatePlanUsage{
ID: uuid.New().String(),
RatePlanID: subscription.RatePlanID,
ProfileID: req.ProfileID,
CycleStart: subscription.CurrentCycle,
CycleEnd: s.calculateCycleEnd(subscription.BillingCycle, subscription.CurrentCycle),
DataUsed: req.DataUsed,
VoiceUsed: req.VoiceUsed,
SMSUsed: req.SMSUsed,
}
if err := s.repo.CreateUsage(ctx, usage); err != nil {
s.logger.WithError(err).Error("Failed to create usage record")
return nil, err
}
} else {
// Update existing usage record
currentUsage.DataUsed += req.DataUsed
currentUsage.VoiceUsed += req.VoiceUsed
currentUsage.SMSUsed += req.SMSUsed
if err := s.repo.UpdateUsage(ctx, currentUsage); err != nil {
s.logger.WithError(err).Error("Failed to update usage record")
return nil, err
}
usage = currentUsage
}
s.logger.WithFields(logrus.Fields{
"profile_id": req.ProfileID,
"data_used": req.DataUsed,
"voice_used": req.VoiceUsed,
"sms_used": req.SMSUsed,
}).Info("Usage recorded successfully")
return usage, nil
}