Documentation
¶
Overview ¶
Package tariff implements the tariff-manager built-in plugin. All tariff logic is self-contained here — the platform only calls Plugin() and RegisterRoutes().
Index ¶
- Constants
- func DerivePlanID(docID string, durationDays int, hasPricingPeriods bool) (string, error)
- func Plugin() plugin.BuiltInPluginDef
- func RegisterRoutes(registry *gateway.BuiltinRouteRegistry, h *Handler)
- func SupportedCurrencies() []string
- type ABExperimentInput
- type ABExperimentResponse
- type ABMetrics
- type ABVariant
- type ABVariantAssignedPayload
- type BasePrice
- type BillingModel
- type CohortInput
- type CohortResponse
- type CohortRule
- type CohortSelector
- type DiscountSpec
- type EligibilityRules
- type Handler
- func (h *Handler) ArchiveTariff(w http.ResponseWriter, r *http.Request)
- func (h *Handler) BulkGeneratePromoCodes(w http.ResponseWriter, r *http.Request)
- func (h *Handler) CloneTariff(w http.ResponseWriter, r *http.Request)
- func (h *Handler) ConcludeABExperiment(w http.ResponseWriter, r *http.Request)
- func (h *Handler) CreateABExperiment(w http.ResponseWriter, r *http.Request)
- func (h *Handler) CreateCohort(w http.ResponseWriter, r *http.Request)
- func (h *Handler) CreatePricingRule(w http.ResponseWriter, r *http.Request)
- func (h *Handler) CreatePromoCode(w http.ResponseWriter, r *http.Request)
- func (h *Handler) CreateTariff(w http.ResponseWriter, r *http.Request)
- func (h *Handler) CustomizeReseller(w http.ResponseWriter, r *http.Request)
- func (h *Handler) DeleteCohort(w http.ResponseWriter, r *http.Request)
- func (h *Handler) DeletePricingRule(w http.ResponseWriter, r *http.Request)
- func (h *Handler) DeletePromoCode(w http.ResponseWriter, r *http.Request)
- func (h *Handler) DeleteTariff(w http.ResponseWriter, r *http.Request)
- func (h *Handler) ExportTariffs(w http.ResponseWriter, r *http.Request)
- func (h *Handler) GetABExperimentResults(w http.ResponseWriter, r *http.Request)
- func (h *Handler) GetCohort(w http.ResponseWriter, r *http.Request)
- func (h *Handler) GetCohortUsers(w http.ResponseWriter, r *http.Request)
- func (h *Handler) GetConversionFunnel(w http.ResponseWriter, r *http.Request)
- func (h *Handler) GetMRRByTariff(w http.ResponseWriter, r *http.Request)
- func (h *Handler) GetPricingRule(w http.ResponseWriter, r *http.Request)
- func (h *Handler) GetPromoCode(w http.ResponseWriter, r *http.Request)
- func (h *Handler) GetPromoCodeStats(w http.ResponseWriter, r *http.Request)
- func (h *Handler) GetResellerCatalog(w http.ResponseWriter, r *http.Request)
- func (h *Handler) GetTariff(w http.ResponseWriter, r *http.Request)
- func (h *Handler) GetTariffByPlanID(ctx context.Context, planID string) (*TariffResponse, error)
- func (h *Handler) GetTariffPrice(w http.ResponseWriter, r *http.Request)
- func (h *Handler) GetTariffStats(w http.ResponseWriter, r *http.Request)
- func (h *Handler) GetTariffVersionHistory(w http.ResponseWriter, r *http.Request)
- func (h *Handler) ImportTariffs(w http.ResponseWriter, r *http.Request)
- func (h *Handler) ListABExperiments(w http.ResponseWriter, r *http.Request)
- func (h *Handler) ListCohorts(w http.ResponseWriter, r *http.Request)
- func (h *Handler) ListExternalSquads(w http.ResponseWriter, r *http.Request)
- func (h *Handler) ListInternalSquads(w http.ResponseWriter, r *http.Request)
- func (h *Handler) ListNodes(w http.ResponseWriter, r *http.Request)
- func (h *Handler) ListPanelsForTariff(w http.ResponseWriter, r *http.Request)
- func (h *Handler) ListPricingRules(w http.ResponseWriter, r *http.Request)
- func (h *Handler) ListPromoCodes(w http.ResponseWriter, r *http.Request)
- func (h *Handler) ListTariffCatalog(w http.ResponseWriter, r *http.Request)
- func (h *Handler) ListTariffs(w http.ResponseWriter, r *http.Request)
- func (h *Handler) ListVisibleTariffs(ctx context.Context, channel string) ([]TariffResponse, error)
- func (h *Handler) PauseABExperiment(w http.ResponseWriter, r *http.Request)
- func (h *Handler) SimulatePricingRule(w http.ResponseWriter, r *http.Request)
- func (h *Handler) StartABExperiment(w http.ResponseWriter, r *http.Request)
- func (h *Handler) SyncAllTariffs(w http.ResponseWriter, r *http.Request)
- func (h *Handler) UpdateCohort(w http.ResponseWriter, r *http.Request)
- func (h *Handler) UpdatePricingRule(w http.ResponseWriter, r *http.Request)
- func (h *Handler) UpdatePromoCode(w http.ResponseWriter, r *http.Request)
- func (h *Handler) UpdateTariff(w http.ResponseWriter, r *http.Request)
- func (h *Handler) ValidatePromoCode(w http.ResponseWriter, r *http.Request)
- type ModifierAction
- type ModifierOp
- type PriceContext
- type PriceModification
- type PricingOverride
- type PricingPeriod
- type PricingPipeline
- type PricingRuleInput
- type PricingRuleModifier
- type PricingRuleResponse
- type PricingStage
- type PromoCodeInput
- type PromoCodeResponse
- type PromoCodeUsedPayload
- type PromoEligibility
- type PromoStacking
- type PromoUsage
- type PromoValidity
- type StackingPolicy
- type TariffArchivedPayload
- type TariffInput
- type TariffPublishedPayload
- type TariffPurchasedPayload
- type TariffReaderAdapter
- type TariffResponse
Constants ¶
const ( EventTariffCreated = "tariff.created" EventTariffUpdated = "tariff.updated" EventTariffArchived = "tariff.archived" EventTariffStockDepleted = "tariff.stock_depleted" EventTariffPublished = "tariff.published" )
Tariff lifecycle events.
const ( EventPricingRuleApplied = "pricing.rule_applied" EventPricingModifierFired = "pricing.modifier_fired" )
Pricing events.
const ( EventPromoCodeCreated = "promo.code_created" EventPromoCodeUsed = "promo.code_used" EventPromoCodeExhausted = "promo.code_exhausted" EventPromoCampaignStarted = "promo.campaign_started" EventPromoCampaignEnded = "promo.campaign_ended" )
Promo code events.
const ( EventABExperimentStarted = "ab.experiment_started" EventABVariantAssigned = "ab.variant_assigned" EventABExperimentConcluded = "ab.experiment_concluded" )
A/B experiment events.
const ( EventCohortUserAdded = "cohort.user_added" EventCohortUserRemoved = "cohort.user_removed" EventCohortRecalculated = "cohort.recalculated" )
Cohort events.
const ( HookCalculatePrice = "tariff.calculate_price" HookValidateEligibility = "tariff.validate_eligibility" HookListVisible = "tariff.list_visible" HookPromoValidate = "promo.validate" HookPromoApply = "promo.apply" HookUserResolveGroups = "user.resolve_groups" HookUserResolveTier = "user.resolve_tier" )
Sync hook names — for use with hook dispatcher.
const ( HookTariffPurchased = "tariff.purchased" HookTariffExpired = "tariff.expired" HookPromoCodeUsed = "promo.code_used" HookCohortUserAdded = "cohort.user_added" HookABVariantAssigned = "ab.variant_assigned" )
Async hook names.
const ( // PluginSlug is the canonical slug for the tariff-manager plugin. PluginSlug = "tariff-manager" // CollectionName is the plugin collection where tariffs are stored. CollectionName = "tariffs" )
const ( CollectionPricingRules = "pricing_rules" CollectionPromoCodes = "promo_codes" CollectionABExperiments = "ab_experiments" CollectionCohorts = "cohorts" CollectionAudienceRules = "audience_rules" CollectionPurchaseHistory = "purchase_history" )
const ( AudienceAll = "all" AudienceB2C = "b2c" AudienceB2B = "b2b" AudienceReseller = "reseller" )
const ( UpgradePolicyImmediate = "immediate" UpgradePolicyNextPeriod = "next_period" UpgradePolicyProrated = "prorated" DowngradePolicyNextPeriod = "next_period" DowngradePolicyImmediateRefund = "immediate_refund" CancellationPolicyImmediate = "immediate" CancellationPolicyEndOfPeriod = "end_of_period" CharmStrategyNone = "none" CharmStrategy99 = "99" CharmStrategy95 = "95" OverageRoundingGB = "gb" OverageRoundingMB = "mb" OverageRounding100MB = "100mb" // Predefined period labels PeriodLabelMonth = "1 month" PeriodLabel3Months = "3 months" PeriodLabel6Months = "6 months" PeriodLabelYear = "1 year" TrafficResetNoReset = "NO_RESET" TrafficResetDay = "DAY" TrafficResetWeek = "WEEK" TrafficResetMonth = "MONTH" TrafficResetMonthRolling = "MONTH_ROLLING" )
Variables ¶
This section is empty.
Functions ¶
func DerivePlanID ¶
DerivePlanID computes the billing PlanID for one period of a tariff.
For single-period tariffs (hasPricingPeriods == false) the PlanID equals the tariff document ID directly. For multi-period tariffs (hasPricingPeriods == true) the PlanID is a deterministic UUIDv5 derived from the document ID and the period duration in days.
The derivation is the canonical source of truth shared by syncTariffToPlan and the tariff reader: plans.list PlanIDs MUST equal the PlanIDs checkout resolves, so both sides must call this function — never inline the logic.
func Plugin ¶
func Plugin() plugin.BuiltInPluginDef
Plugin returns the built-in plugin definition for the tariff-manager.
func RegisterRoutes ¶
func RegisterRoutes(registry *gateway.BuiltinRouteRegistry, h *Handler)
RegisterRoutes maps the plugin's manifest route functions to native Go handlers.
func SupportedCurrencies ¶
func SupportedCurrencies() []string
SupportedCurrencies returns the list of supported currency codes.
Types ¶
type ABExperimentInput ¶
type ABExperimentInput struct {
Name string `json:"name"`
TariffID string `json:"tariff_id"`
Status string `json:"status"` // "draft" | "running" | "paused" | "completed" | "archived"
Variants []ABVariant `json:"variants"`
AssignmentStrategy string `json:"assignment_strategy"` // "hash_user_id" | "hash_tenant_id" | "random"
StartedAt string `json:"started_at,omitempty"`
EndedAt string `json:"ended_at,omitempty"`
Metrics map[string]ABMetrics `json:"metrics,omitempty"`
StatSignificance float64 `json:"stat_significance"`
Winner string `json:"winner,omitempty"`
StopRule string `json:"stop_rule"`
}
ABExperimentInput is the request body for creating or updating an A/B experiment.
type ABExperimentResponse ¶
type ABExperimentResponse struct {
ID string `json:"id"`
ABExperimentInput
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
ABExperimentResponse wraps ABExperimentInput with server-assigned fields.
type ABMetrics ¶
type ABMetrics struct {
Exposures int `json:"exposures"`
Conversions int `json:"conversions"`
RevenueCents int64 `json:"revenue_cents"`
}
ABMetrics tracks conversion data for a single variant.
type ABVariant ¶
type ABVariant struct {
Key string `json:"key"`
PriceCents int64 `json:"price_cents"`
TrafficPct int `json:"traffic_pct"` // basis points, total must = 10000
}
ABVariant is a single price variant in an experiment.
type ABVariantAssignedPayload ¶
type ABVariantAssignedPayload struct {
ExperimentID string `json:"experiment_id"`
UserID string `json:"user_id"`
VariantKey string `json:"variant_key"`
PriceCents int64 `json:"price_cents"`
}
ABVariantAssignedPayload is emitted for observability when a user is assigned to an experiment variant.
type BillingModel ¶
type BillingModel string
BillingModel defines how a tariff charges the customer.
const ( BillingModelFixed BillingModel = "fixed" BillingModelRecurring BillingModel = "recurring" BillingModelLifetime BillingModel = "lifetime" BillingModelPAYG BillingModel = "payg" BillingModelCredit BillingModel = "credit" BillingModelHybrid BillingModel = "hybrid" )
func (BillingModel) IsValid ¶
func (b BillingModel) IsValid() bool
IsValid returns true if b is one of the known billing models.
type CohortInput ¶
type CohortInput struct {
Name string `json:"name"`
Selector CohortSelector `json:"selector"`
PricingOverride PricingOverride `json:"pricing_override"`
UserCount int `json:"user_count"`
}
CohortInput is the request body for creating or updating a cohort.
type CohortResponse ¶
type CohortResponse struct {
ID string `json:"id"`
CohortInput
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
CohortResponse wraps CohortInput with server-assigned identity and timestamps.
type CohortRule ¶
type CohortRule struct {
Event string `json:"event,omitempty"` // e.g. "SubCancelled"
WithinDays int `json:"within_days,omitempty"`
UserAttr string `json:"user_attr,omitempty"` // e.g. "lifetime_months"
Gte int `json:"gte,omitempty"`
Lte int `json:"lte,omitempty"`
Eq string `json:"eq,omitempty"`
}
CohortRule is a single selection rule within a CohortSelector.
type CohortSelector ¶
type CohortSelector struct {
Type string `json:"type"` // "event_based" | "attribute_based" | "manual"
Rules []CohortRule `json:"rules"`
}
CohortSelector defines how users are selected into a cohort.
type DiscountSpec ¶
type DiscountSpec struct {
Type string `json:"type"` // "percent_off" | "fixed_off"
Value int64 `json:"value"` // basis points for %, cents for fixed
MaxDiscountCents int64 `json:"max_discount_cents"` // cap on discount amount
}
DiscountSpec defines the discount type and value.
type EligibilityRules ¶
type EligibilityRules struct {
RequirePromoGroups []string `json:"require_promo_groups,omitempty"`
RequireAnyOfGroups []string `json:"require_any_of_groups,omitempty"`
ExcludeGroups []string `json:"exclude_groups,omitempty"`
RequireTiers []string `json:"require_tiers,omitempty"`
MinLifetimeMonths int `json:"min_lifetime_months,omitempty"`
RequireVerifiedEmail bool `json:"require_verified_email,omitempty"`
RequireKYC bool `json:"require_kyc,omitempty"`
CustomRuleIDs []string `json:"custom_rule_ids,omitempty"`
}
EligibilityRules captures constraints that determine whether a user is eligible to purchase a tariff.
type Handler ¶
type Handler struct {
// contains filtered or unexported fields
}
Handler provides HTTP endpoints for tariff CRUD, pricing, promo codes, A/B experiments, cohorts, analytics, and Remnawave data lookups.
func NewHandler ¶
func NewHandler(collections pluginstore.Store, pluginRepo plugin.PluginRepository, planRepo billing.PlanRepository, logger *slog.Logger) *Handler
NewHandler creates a tariff Handler.
func (*Handler) ArchiveTariff ¶
func (h *Handler) ArchiveTariff(w http.ResponseWriter, r *http.Request)
ArchiveTariff soft-deletes a tariff by setting is_active=false.
func (*Handler) BulkGeneratePromoCodes ¶
func (h *Handler) BulkGeneratePromoCodes(w http.ResponseWriter, r *http.Request)
BulkGeneratePromoCodes generates N unique promo codes with a shared template.
func (*Handler) CloneTariff ¶
func (h *Handler) CloneTariff(w http.ResponseWriter, r *http.Request)
CloneTariff duplicates a tariff with a new ID.
func (*Handler) ConcludeABExperiment ¶
func (h *Handler) ConcludeABExperiment(w http.ResponseWriter, r *http.Request)
ConcludeABExperiment marks an experiment as completed with a winner.
func (*Handler) CreateABExperiment ¶
func (h *Handler) CreateABExperiment(w http.ResponseWriter, r *http.Request)
CreateABExperiment creates a new A/B experiment.
func (*Handler) CreateCohort ¶
func (h *Handler) CreateCohort(w http.ResponseWriter, r *http.Request)
CreateCohort decodes, validates, and inserts a new cohort.
func (*Handler) CreatePricingRule ¶
func (h *Handler) CreatePricingRule(w http.ResponseWriter, r *http.Request)
CreatePricingRule creates a new pricing rule.
func (*Handler) CreatePromoCode ¶
func (h *Handler) CreatePromoCode(w http.ResponseWriter, r *http.Request)
CreatePromoCode creates a new promo code.
func (*Handler) CreateTariff ¶
func (h *Handler) CreateTariff(w http.ResponseWriter, r *http.Request)
func (*Handler) CustomizeReseller ¶
func (h *Handler) CustomizeReseller(w http.ResponseWriter, r *http.Request)
func (*Handler) DeleteCohort ¶
func (h *Handler) DeleteCohort(w http.ResponseWriter, r *http.Request)
DeleteCohort removes a cohort by ID.
func (*Handler) DeletePricingRule ¶
func (h *Handler) DeletePricingRule(w http.ResponseWriter, r *http.Request)
DeletePricingRule removes a pricing rule by ID.
func (*Handler) DeletePromoCode ¶
func (h *Handler) DeletePromoCode(w http.ResponseWriter, r *http.Request)
DeletePromoCode removes a promo code by ID.
func (*Handler) DeleteTariff ¶
func (h *Handler) DeleteTariff(w http.ResponseWriter, r *http.Request)
DeleteTariff deletes a tariff document and deactivates linked billing plans.
func (*Handler) ExportTariffs ¶
func (h *Handler) ExportTariffs(w http.ResponseWriter, r *http.Request)
ExportTariffs exports all tariffs as JSON.
func (*Handler) GetABExperimentResults ¶
func (h *Handler) GetABExperimentResults(w http.ResponseWriter, r *http.Request)
GetABExperimentResults returns experiment data + variant assignment for a test user.
func (*Handler) GetCohort ¶
func (h *Handler) GetCohort(w http.ResponseWriter, r *http.Request)
GetCohort returns a single cohort by its ID.
func (*Handler) GetCohortUsers ¶
func (h *Handler) GetCohortUsers(w http.ResponseWriter, r *http.Request)
GetCohortUsers returns a placeholder response for the cohort's user membership. Actual cohort membership is recalculated asynchronously.
func (*Handler) GetConversionFunnel ¶
func (h *Handler) GetConversionFunnel(w http.ResponseWriter, r *http.Request)
func (*Handler) GetMRRByTariff ¶
func (h *Handler) GetMRRByTariff(w http.ResponseWriter, r *http.Request)
func (*Handler) GetPricingRule ¶
func (h *Handler) GetPricingRule(w http.ResponseWriter, r *http.Request)
GetPricingRule returns a single pricing rule by ID.
func (*Handler) GetPromoCode ¶
func (h *Handler) GetPromoCode(w http.ResponseWriter, r *http.Request)
GetPromoCode returns a single promo code by ID.
func (*Handler) GetPromoCodeStats ¶
func (h *Handler) GetPromoCodeStats(w http.ResponseWriter, r *http.Request)
GetPromoCodeStats returns usage statistics for a promo code.
func (*Handler) GetResellerCatalog ¶
func (h *Handler) GetResellerCatalog(w http.ResponseWriter, r *http.Request)
func (*Handler) GetTariffByPlanID ¶
GetTariffByPlanID resolves a planID to a TariffResponse by scanning active telegram-visible tariffs. planID may be either a tariff document ID (single-period tariff) or a derived UUIDv5 (one period of a multi-period tariff).
Returns pluginstore.ErrDocumentNotFound (wrapped) when no matching tariff is found. ctx is forwarded to ListVisibleTariffs without modification.
func (*Handler) GetTariffPrice ¶
func (h *Handler) GetTariffPrice(w http.ResponseWriter, r *http.Request)
GetTariffPrice calculates the personalized price for a tariff.
func (*Handler) GetTariffStats ¶
func (h *Handler) GetTariffStats(w http.ResponseWriter, r *http.Request)
GetTariffStats returns basic sales stats for a tariff (placeholder).
func (*Handler) GetTariffVersionHistory ¶
func (h *Handler) GetTariffVersionHistory(w http.ResponseWriter, r *http.Request)
GetTariffVersionHistory returns change history for a tariff (placeholder).
func (*Handler) ImportTariffs ¶
func (h *Handler) ImportTariffs(w http.ResponseWriter, r *http.Request)
ImportTariffs imports tariffs from a JSON array.
func (*Handler) ListABExperiments ¶
func (h *Handler) ListABExperiments(w http.ResponseWriter, r *http.Request)
ListABExperiments returns all A/B experiments.
func (*Handler) ListCohorts ¶
func (h *Handler) ListCohorts(w http.ResponseWriter, r *http.Request)
ListCohorts returns all cohorts in the CollectionCohorts collection.
func (*Handler) ListExternalSquads ¶
func (h *Handler) ListExternalSquads(w http.ResponseWriter, r *http.Request)
func (*Handler) ListInternalSquads ¶
func (h *Handler) ListInternalSquads(w http.ResponseWriter, r *http.Request)
func (*Handler) ListPanelsForTariff ¶
func (h *Handler) ListPanelsForTariff(w http.ResponseWriter, r *http.Request)
ListPanelsForTariff returns panel connections for tariff form dropdown.
func (*Handler) ListPricingRules ¶
func (h *Handler) ListPricingRules(w http.ResponseWriter, r *http.Request)
ListPricingRules returns all pricing rules.
func (*Handler) ListPromoCodes ¶
func (h *Handler) ListPromoCodes(w http.ResponseWriter, r *http.Request)
ListPromoCodes returns all promo codes.
func (*Handler) ListTariffCatalog ¶
func (h *Handler) ListTariffCatalog(w http.ResponseWriter, r *http.Request)
ListTariffCatalog returns tariffs grouped and sorted for storefront display.
func (*Handler) ListTariffs ¶
func (h *Handler) ListTariffs(w http.ResponseWriter, r *http.Request)
func (*Handler) ListVisibleTariffs ¶
ListVisibleTariffs returns the active tariffs visible in the given channel for the tenant encoded in ctx.
ctx must carry the tenant GUC — the caller (e.g. a bot op wrapped in RunInTx(WithTenantID(...))) owns that contract; this method does NOT set the GUC itself.
Channel mapping:
- "telegram" → VisibleInTelegram
- "cabinet" → VisibleInCabinet
- "public" → VisibleInPublic
- unknown → VisibleInTelegram (default)
func (*Handler) PauseABExperiment ¶
func (h *Handler) PauseABExperiment(w http.ResponseWriter, r *http.Request)
PauseABExperiment transitions a running experiment to paused.
func (*Handler) SimulatePricingRule ¶
func (h *Handler) SimulatePricingRule(w http.ResponseWriter, r *http.Request)
SimulatePricingRule performs a dry-run pricing calculation using the rule's modifiers.
func (*Handler) StartABExperiment ¶
func (h *Handler) StartABExperiment(w http.ResponseWriter, r *http.Request)
StartABExperiment transitions an experiment from draft/paused to running.
func (*Handler) SyncAllTariffs ¶
func (h *Handler) SyncAllTariffs(w http.ResponseWriter, r *http.Request)
SyncAllTariffs forces a sync of all existing tariffs to billing Plans. Returns detailed results including errors for debugging.
func (*Handler) UpdateCohort ¶
func (h *Handler) UpdateCohort(w http.ResponseWriter, r *http.Request)
UpdateCohort updates an existing cohort by ID.
func (*Handler) UpdatePricingRule ¶
func (h *Handler) UpdatePricingRule(w http.ResponseWriter, r *http.Request)
UpdatePricingRule updates an existing pricing rule by ID.
func (*Handler) UpdatePromoCode ¶
func (h *Handler) UpdatePromoCode(w http.ResponseWriter, r *http.Request)
UpdatePromoCode updates an existing promo code by ID.
func (*Handler) UpdateTariff ¶
func (h *Handler) UpdateTariff(w http.ResponseWriter, r *http.Request)
func (*Handler) ValidatePromoCode ¶
func (h *Handler) ValidatePromoCode(w http.ResponseWriter, r *http.Request)
ValidatePromoCode checks whether a promo code is valid without applying it.
type ModifierAction ¶
type ModifierAction struct {
Op ModifierOp `json:"op"`
Value int64 `json:"value"` // basis points for percent (1000=10%), cents for fixed
}
ModifierAction describes the arithmetic to apply.
type ModifierOp ¶
type ModifierOp string
ModifierOp enumerates the operations a pricing modifier can perform.
const ( ModifierPercentOff ModifierOp = "percent_off" ModifierPercentUp ModifierOp = "percent_up" ModifierFixedOff ModifierOp = "fixed_off" ModifierFixedUp ModifierOp = "fixed_up" ModifierReplacePrice ModifierOp = "replace_price" ModifierMultiply ModifierOp = "multiply" )
type PriceContext ¶
type PriceContext struct {
TariffID string `json:"tariff_id"`
UserID string `json:"user_id"`
TenantID string `json:"tenant_id"`
Country string `json:"country"` // ISO-3166
Currency string `json:"currency"`
Duration int `json:"duration"` // days
Quantity int `json:"quantity"` // seats (B2B)
PromoCode string `json:"promo_code,omitempty"`
// Extension cache: populated by early hooks, read by later stages.
UserGroups []string `json:"user_groups,omitempty"`
UserCohort string `json:"user_cohort,omitempty"`
UserTier string `json:"user_tier,omitempty"`
BasePrice int64 `json:"base_price"`
Modifiers []PriceModification `json:"modifiers"`
FinalPrice int64 `json:"final_price"`
Breakdown map[string]int64 `json:"breakdown"`
}
PriceContext flows through the pricing pipeline, accumulating modifications and producing a final price with full audit trail.
type PriceModification ¶
type PriceModification struct {
Step string `json:"step"`
RuleID string `json:"rule_id,omitempty"`
AmountCents int64 `json:"amount_cents"`
Reason string `json:"reason"`
}
PriceModification is a single audit entry in the pricing pipeline.
type PricingOverride ¶
type PricingOverride struct {
PricingRuleID string `json:"pricing_rule_id,omitempty"`
PromoCodeAuto string `json:"promo_code_auto,omitempty"` // auto-apply promo
}
PricingOverride links a cohort to a specific pricing rule or auto-applied promo code.
type PricingPeriod ¶
type PricingPeriod struct {
DurationDays int `json:"duration_days"`
PriceAmount int64 `json:"price_amount"` // in cents
Label string `json:"label"` // "1 month", "3 months", "1 year"
SavePercent int `json:"save_percent"` // 0, 10, 17 — discount vs monthly
IsDefault bool `json:"is_default"` // highlighted in UI
}
PricingPeriod represents one subscription period option for a tariff. A tariff can offer multiple periods (e.g., 1 month, 3 months, 1 year) each with its own price and optional discount.
type PricingPipeline ¶
type PricingPipeline struct {
// contains filtered or unexported fields
}
PricingPipeline orchestrates a sequence of pricing stages.
func DefaultPipeline ¶
func DefaultPipeline(tariff *TariffInput, rule *PricingRuleInput, promo *PromoCodeInput) *PricingPipeline
DefaultPipeline creates the standard 8-stage pricing pipeline.
func NewPricingPipeline ¶
func NewPricingPipeline(stages ...PricingStage) *PricingPipeline
NewPricingPipeline creates a pipeline from the given stages.
func (*PricingPipeline) Calculate ¶
func (p *PricingPipeline) Calculate(ctx *PriceContext) error
Calculate runs every stage and ensures the final price is non-negative.
type PricingRuleInput ¶
type PricingRuleInput struct {
Name string `json:"name"`
BasePrices []BasePrice `json:"base_prices"`
Modifiers []PricingRuleModifier `json:"modifiers"`
StackingPolicy string `json:"stacking_policy"` // "additive" | "multiplicative" | "best_only"
PriceFloorCents int64 `json:"price_floor_cents"`
Rounding string `json:"rounding"` // "none" | "charm_99" | "charm_95"
}
PricingRuleInput is the request body for creating or updating a pricing rule.
type PricingRuleModifier ¶
type PricingRuleModifier struct {
Type string `json:"type"` // "geo", "loyalty", "bulk_duration", "quantity"
Priority int `json:"priority"`
Conditions map[string]any `json:"conditions"`
Action ModifierAction `json:"action"`
}
PricingRuleModifier is a single modifier entry inside a PricingRule.
type PricingRuleResponse ¶
type PricingRuleResponse struct {
ID string `json:"id"`
PricingRuleInput
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
PricingRuleResponse wraps PricingRuleInput with server-assigned fields.
type PricingStage ¶
type PricingStage interface {
Name() string
Apply(ctx *PriceContext) error
}
PricingStage is a single step in the pricing pipeline.
type PromoCodeInput ¶
type PromoCodeInput struct {
Code string `json:"code"`
DiscountSpec DiscountSpec `json:"discount_spec"`
Eligibility PromoEligibility `json:"eligibility"`
Usage PromoUsage `json:"usage"`
Validity PromoValidity `json:"validity"`
Stacking PromoStacking `json:"stacking"`
CampaignID string `json:"campaign_id"`
Source string `json:"source"`
CreatedBy string `json:"created_by"`
IsActive bool `json:"is_active"`
}
PromoCodeInput is the request body for creating or updating a promo code.
type PromoCodeResponse ¶
type PromoCodeResponse struct {
ID string `json:"id"`
PromoCodeInput
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
PromoCodeResponse wraps PromoCodeInput with server-assigned fields.
type PromoCodeUsedPayload ¶
type PromoCodeUsedPayload struct {
PromoCodeID string `json:"promo_code_id"`
Code string `json:"code"`
UserID string `json:"user_id"`
TariffID string `json:"tariff_id"`
DiscountCents int64 `json:"discount_cents"`
}
PromoCodeUsedPayload is emitted when a promo code is successfully used.
type PromoEligibility ¶
type PromoEligibility struct {
TariffIDs []string `json:"tariff_ids,omitempty"`
TariffIDsExclude []string `json:"tariff_ids_exclude,omitempty"`
AudienceSegments []string `json:"audience_segments,omitempty"`
Countries []string `json:"countries,omitempty"`
CountriesExclude []string `json:"countries_exclude,omitempty"`
FirstPurchaseOnly bool `json:"first_purchase_only"`
MinPurchaseCents int64 `json:"min_purchase_cents"`
MinDurationDays int `json:"min_duration_days"`
RequiresTier string `json:"requires_tier"`
UserIDsWhitelist []string `json:"user_ids_whitelist,omitempty"`
UserIDsBlacklist []string `json:"user_ids_blacklist,omitempty"`
PromoGroupIDs []string `json:"promo_group_ids,omitempty"`
PromoGroupExclude []string `json:"promo_group_exclude,omitempty"`
}
PromoEligibility defines who can use a promo code.
type PromoStacking ¶
type PromoStacking struct {
StackableWithOthers bool `json:"stackable_with_others"`
StackableWithPricingRules bool `json:"stackable_with_pricing_rules"`
Priority int `json:"priority"`
}
PromoStacking controls how promo codes interact with other discounts.
type PromoUsage ¶
type PromoUsage struct {
MaxUsesTotal int `json:"max_uses_total"`
MaxUsesPerUser int `json:"max_uses_per_user"`
MaxUsesPerTenant int `json:"max_uses_per_tenant"`
UsesCount int `json:"uses_count"`
UniqueUsersCount int `json:"unique_users_count"`
}
PromoUsage tracks how many times a promo code has been used.
type PromoValidity ¶
type PromoValidity struct {
ValidFrom string `json:"valid_from,omitempty"`
ValidUntil string `json:"valid_until,omitempty"`
ValidOnDaysOfWeek []int `json:"valid_on_days_of_week,omitempty"`
}
PromoValidity defines when a promo code is valid.
type StackingPolicy ¶
type StackingPolicy string
StackingPolicy determines how multiple discounts combine.
const ( StackingAdditive StackingPolicy = "additive" StackingMultiplicative StackingPolicy = "multiplicative" StackingBestOnly StackingPolicy = "best_only" )
type TariffArchivedPayload ¶
type TariffArchivedPayload struct {
TariffID string `json:"tariff_id"`
ArchivedAt string `json:"archived_at"` // RFC3339
Reason string `json:"reason"` // "admin_action" | "stock_depleted" | "sunset"
}
TariffArchivedPayload is emitted when a tariff is archived (soft-deleted).
type TariffInput ¶
type TariffInput struct {
// --- Core (original 13 fields) ---
Name string `json:"name"`
Description string `json:"description"`
PriceAmount int64 `json:"price_amount"`
PriceCurrency string `json:"price_currency"`
DurationDays int `json:"duration_days"`
TrafficLimitGB float64 `json:"traffic_limit_gb"`
DeviceLimit int `json:"device_limit"`
MaxPurchasesPerUser int `json:"max_purchases_per_user"`
VPNPanelID string `json:"vpn_panel_id,omitempty"`
TrafficResetStrategy string `json:"traffic_reset_strategy,omitempty"`
InternalSquadUUIDs []string `json:"internal_squad_uuids"`
ExternalSquadUUIDs []string `json:"external_squad_uuids"`
Features []string `json:"features"`
IsActive bool `json:"is_active"`
SortOrder int `json:"sort_order"`
// --- Subscription periods ---
PricingPeriods []PricingPeriod `json:"pricing_periods,omitempty"`
// --- Billing model ---
BillingModel string `json:"billing_model"`
AutoRenewal bool `json:"auto_renewal"`
RenewalGraceDays int `json:"renewal_grace_days"`
// --- Pricing ---
PricingRuleID string `json:"pricing_rule_id"`
MultiCurrencyPrices map[string]int64 `json:"multi_currency_prices,omitempty"`
PriceCharmStrategy string `json:"price_charm_strategy"`
// --- PAYG / Hybrid ---
IncludedTrafficGB float64 `json:"included_traffic_gb"`
OveragePerGBCents int64 `json:"overage_per_gb_cents"`
OverageRounding string `json:"overage_rounding"`
// --- Credit model ---
CreditCostPerDay int64 `json:"credit_cost_per_day"`
CreditCostPerGB int64 `json:"credit_cost_per_gb"`
// --- Audience ---
AudienceSegment string `json:"audience_segment"`
MinSeats int `json:"min_seats"`
MaxSeats int `json:"max_seats"`
RequiresKYC bool `json:"requires_kyc"`
// --- Trial & conversion ---
TrialDays int `json:"trial_days"`
TrialRequiresCard bool `json:"trial_requires_card"`
TrialToPlanID string `json:"trial_to_plan_id"`
// --- Lifecycle rules ---
UpgradePolicy string `json:"upgrade_policy"`
DowngradePolicy string `json:"downgrade_policy"`
CancellationPolicy string `json:"cancellation_policy"`
RefundWindowDays int `json:"refund_window_days"`
// --- Purchase constraints ---
AvailableFrom string `json:"available_from,omitempty"`
AvailableUntil string `json:"available_until,omitempty"`
StockLimit int `json:"stock_limit"`
StockRemaining int `json:"stock_remaining"`
RequiresInviteCode bool `json:"requires_invite_code"`
EligibilityRules EligibilityRules `json:"eligibility_rules"`
// --- Visibility ---
VisibleInPublic bool `json:"visible_in_public"`
VisibleInTelegram bool `json:"visible_in_telegram"`
VisibleInCabinet bool `json:"visible_in_cabinet"`
VisibleInResellerPortal bool `json:"visible_in_reseller_portal"`
// --- Reseller ---
ResellerMarkupPct int64 `json:"reseller_markup_pct"`
ResellerMinPrice int64 `json:"reseller_min_price"`
ResellerMaxDiscount int64 `json:"reseller_max_discount"`
// --- Localization ---
LocalizedNames map[string]string `json:"localized_names,omitempty"`
LocalizedDescriptions map[string]string `json:"localized_descriptions,omitempty"`
// --- Tax ---
TaxCategory string `json:"tax_category"`
TaxInclusive bool `json:"tax_inclusive"`
// --- Metadata ---
Tags []string `json:"tags,omitempty"`
BadgeText string `json:"badge_text"`
BadgeColor string `json:"badge_color"`
ExternalRefs map[string]string `json:"external_refs,omitempty"`
// --- Tenancy (RBAC Phase C6) ---
// IsTemplate marks a platform-managed shared tariff that is readable by
// every shop (read-filter in ListTariffs). Server-controlled: a shop actor
// can never set this true (CreateTariff forces it false). Writes to a
// stored template are platform-admin-only.
IsTemplate bool `json:"is_template"`
}
TariffInput is the full set of fields accepted when creating or updating a tariff. The first 13 fields match the original schema; the remaining fields extend it with billing, pricing, audience, trial, lifecycle, visibility, reseller, localisation, tax, and metadata capabilities.
type TariffPublishedPayload ¶
type TariffPublishedPayload struct {
TariffID string `json:"tariff_id"`
BillingModel string `json:"billing_model"`
PriceAmount int64 `json:"price_amount"`
Currency string `json:"currency"`
DurationDays int `json:"duration_days"`
Version int `json:"version"`
PricingRuleID string `json:"pricing_rule_id,omitempty"`
Metadata map[string]string `json:"metadata,omitempty"`
}
TariffPublishedPayload is emitted when a tariff is created or updated, to sync with the billing domain's Plan aggregate.
type TariffPurchasedPayload ¶
type TariffPurchasedPayload struct {
TariffID string `json:"tariff_id"`
UserID string `json:"user_id"`
PriceBreakdown map[string]int64 `json:"price_breakdown"`
AppliedRules []string `json:"applied_rules,omitempty"`
ABExperiment string `json:"ab_experiment,omitempty"`
ABVariant string `json:"ab_variant,omitempty"`
Cohort string `json:"cohort,omitempty"`
PromoCode string `json:"promo_code,omitempty"`
}
TariffPurchasedPayload is emitted asynchronously when a tariff is purchased.
type TariffReaderAdapter ¶
type TariffReaderAdapter struct {
// contains filtered or unexported fields
}
TariffReaderAdapter adapts the tariff Handler to the bothost.TariffReader interface. It maps TariffResponse → bothost.TariffOffer, building per-period PlanIDs via DerivePlanID so that the planIDs presented to bot plugins match exactly the planIDs the checkout path resolves.
Tenant scoping is the caller's responsibility: the ctx passed to each method must carry the tenant GUC (wrapped in RunInTx(WithTenantID(...))); this adapter does not set the GUC itself.
func NewTariffReaderAdapter ¶
func NewTariffReaderAdapter(lister visibleTariffLister, logger *slog.Logger) *TariffReaderAdapter
NewTariffReaderAdapter returns an adapter backed by lister. In production, pass the *tariff.Handler directly.
func (*TariffReaderAdapter) Get ¶
func (a *TariffReaderAdapter) Get(ctx context.Context, planID string) (*bothost.TariffOffer, error)
Get implements bothost.TariffReader.
func (*TariffReaderAdapter) ListVisible ¶
func (a *TariffReaderAdapter) ListVisible(ctx context.Context, channel string) ([]bothost.TariffOffer, error)
ListVisible implements bothost.TariffReader.
type TariffResponse ¶
type TariffResponse struct {
ID string `json:"id"`
TariffInput
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
TariffResponse wraps TariffInput with server-assigned identity and timestamps.