productcatalog

package
v1.0.0-beta.231 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 14, 2026 License: Apache-2.0 Imports: 23 Imported by: 0

Documentation

Index

Constants

View Source
const DefaultPaymentTerm = InAdvancePaymentTerm
View Source
const ErrCodeAddonHasNoRateCards models.ErrorCode = "addon_has_no_rate_cards"
View Source
const ErrCodeAddonInvalidInstanceType models.ErrorCode = "addon_invalid_instance_type"
View Source
const ErrCodeAddonInvalidPriceForMultiInstance models.ErrorCode = "addon_invalid_ratecard_price_for_multi_instance"
View Source
const ErrCodeAddonInvalidStatus models.ErrorCode = "addon_invalid_status"
View Source
const ErrCodeAddonInvalidStatusForPublish models.ErrorCode = "addon_invalid_status_for_publish"
View Source
const ErrCodeAddonKeyEmpty models.ErrorCode = "addon_key_empty"
View Source
const ErrCodeAddonNameEmpty models.ErrorCode = "addon_name_empty"
View Source
const ErrCodeAddonRateCardUnitConfigMismatch models.ErrorCode = "addon_rate_card_unit_config_mismatch"
View Source
const ErrCodeBillingCadenceInvalidValue models.ErrorCode = "billing_cadence_invalid_value"
View Source
const ErrCodeCurrencyInvalid models.ErrorCode = "currency_invalid"
View Source
const ErrCodeDeprecatedUnalignedSubscription models.ErrorCode = "deprecated_unaligned_subscription"
View Source
const ErrCodeEffectivePeriodFromAfterTo models.ErrorCode = "effective_period_from_after_to"
View Source
const ErrCodeEffectivePeriodFromNotSet models.ErrorCode = "effective_period_from_not_set"
View Source
const ErrCodeEntitlementTemplateInvalidIssueAfterResetWithPriority models.ErrorCode = "entitlement_template_invalid_issue_after_reset_with_priority"
View Source
const ErrCodeEntitlementTemplateInvalidJSONConfig models.ErrorCode = "entitlement_template_invalid_json_config"
View Source
const ErrCodeEntitlementTemplateIssueAfterResetRequired models.ErrorCode = "entitlement_template_issue_after_reset_required"
View Source
const ErrCodeEntitlementTemplateNegativeUsagePeriod models.ErrorCode = "entitlement_template_negative_usage_period"
View Source
const ErrCodeEntitlementTemplateUsagePeriodLessThenAnHour models.ErrorCode = "entitlement_template_usage_period_less_then_an_hour"
View Source
const ErrCodeIDEmpty models.ErrorCode = "resource_id_empty"
View Source
const ErrCodeNamespaceEmpty models.ErrorCode = "resource_namespace_empty"
View Source
const ErrCodePercentageDiscountInvalidValue models.ErrorCode = "percentage_discount_invalid_value"
View Source
const ErrCodePlanAddonCurrencyMismatch models.ErrorCode = "plan_addon_currency_mismatch"
View Source
const ErrCodePlanAddonIncompatibleStatus models.ErrorCode = "plan_addon_incompatible_status"
View Source
const ErrCodePlanAddonMaxQuantityMustBeSet models.ErrorCode = "plan_addon_max_quantity_must_be_set"
View Source
const ErrCodePlanAddonMaxQuantityMustNotBeSet models.ErrorCode = "plan_addon_max_quantity_must_not_be_set"
View Source
const ErrCodePlanAddonUnknownPlanPhaseKey models.ErrorCode = "plan_addon_unknown_plan_phase_key"
View Source
const ErrCodePlanBillingCadenceInvalid models.ErrorCode = "plan_billing_cadence_invalid"
View Source
const ErrCodePlanBillingCadenceNotCompatible models.ErrorCode = "plan_billing_cadence_not_compatible"
View Source
const ErrCodePlanHasIncompatibleAddon models.ErrorCode = "plan_has_incompatible_addon"
View Source
const ErrCodePlanHasLastPhaseWithDuration models.ErrorCode = "plan_has_last_phase_with_duration"
View Source
const ErrCodePlanHasNonLastPhaseWithNoDuration models.ErrorCode = "plan_has_non_last_phase_with_no_duration"
View Source
const ErrCodePlanInvalidStatus models.ErrorCode = "plan_invalid_status"
View Source
const ErrCodePlanPhaseDuplicatedKey models.ErrorCode = "plan_phase_duplicated_key"
View Source
const ErrCodePlanPhaseDurationLessThenAnHour models.ErrorCode = "plan_phase_duration_less_then_an_hour"
View Source
const ErrCodePlanPhaseHasNoRateCards models.ErrorCode = "plan_phase_has_no_rate_cards"
View Source
const ErrCodePlanPhaseWithNegativeDuration models.ErrorCode = "plan_phase_with_negative_duration"
View Source
const ErrCodePlanWithNoPhases models.ErrorCode = "plan_with_no_phases"
View Source
const ErrCodeRateCardBillingCadenceMismatch models.ErrorCode = "rate_card_billing_cadence_mismatch"
View Source
const ErrCodeRateCardBillingCadenceUnaligned models.ErrorCode = "rate_card_billing_cadence_unaligned"
View Source
const ErrCodeRateCardDuplicatedKey models.ErrorCode = "rate_card_duplicated_key"
View Source
const ErrCodeRateCardEntitlementTemplateTypeMismatch models.ErrorCode = "rate_card_entitlement_template_type_mismatch"
View Source
const ErrCodeRateCardEntitlementTemplateWithNoFeature models.ErrorCode = "entitlement_template_with_no_feature"
View Source
const ErrCodeRateCardFeatureArchived models.ErrorCode = "rate_card_feature_archived"
View Source
const ErrCodeRateCardFeatureIDMismatch models.ErrorCode = "rate_card_feature_id_mismatch"
View Source
const ErrCodeRateCardFeatureKeyMismatch models.ErrorCode = "rate_card_feature_key_mismatch"
View Source
const ErrCodeRateCardFeatureMismatch models.ErrorCode = "rate_card_feature_mismatch"
View Source
const ErrCodeRateCardFeatureNotFound models.ErrorCode = "rate_card_feature_not_found"
View Source
const ErrCodeRateCardKeyFeatureKeyMismatch models.ErrorCode = "rate_card_key_feature_key_mismatch"
View Source
const ErrCodeRateCardKeyMismatch models.ErrorCode = "rate_card_key_mismatch"
View Source
const ErrCodeRateCardMeteredEntitlementTemplateUsagePeriodMismatch models.ErrorCode = "rate_card_metered_entitlement_template_usage_period_mismatch"
View Source
const ErrCodeRateCardMultipleBillingCadence models.ErrorCode = "rate_card_multiple_billing_cadence"
View Source
const ErrCodeRateCardOnlyFlatPriceAllowed models.ErrorCode = "rate_card_only_flat_price_allowed"
View Source
const ErrCodeRateCardPercentageDiscountNotAllowed models.ErrorCode = "rate_card_percentage_discount_not_allowed"
View Source
const ErrCodeRateCardPricePaymentTermMismatch models.ErrorCode = "rate_card_price_payment_term_mismatch"
View Source
const ErrCodeRateCardPriceTypeMismatch models.ErrorCode = "rate_card_price_type_mismatch"
View Source
const ErrCodeRateCardStaticEntitlementTemplateNotAllowed models.ErrorCode = "rate_card_static_entitlement_template_not_allowed"
View Source
const ErrCodeRateCardUnitConfigRequiresUsageBasedPrice models.ErrorCode = "unit_config_requires_usage_based_price"
View Source
const ErrCodeRateCardUsageBasedPriceWithFeatureAndNoMeter models.ErrorCode = "usage_based_price_with_feature_and_no_meter"
View Source
const ErrCodeRateCardUsageBasedPriceWithNoFeature models.ErrorCode = "usage_based_price_with_no_feature"
View Source
const ErrCodeResourceKeyEmpty models.ErrorCode = "resource_key_empty"
View Source
const ErrCodeResourceNameEmpty models.ErrorCode = "resource_name_empty"
View Source
const ErrCodeUnitConfigNotRepresentable models.ErrorCode = "unit_config_not_representable"
View Source
const ErrCodeUsageDiscountNegativeQuantity models.ErrorCode = "usage_discount_negative_quantity"
View Source
const ErrCodeUsageDiscountWithFlatPrice models.ErrorCode = "usage_discount_with_flat_price"

Variables

View Source
var DynamicPriceDefaultMultiplier = decimal.NewFromFloat(1)
View Source
var ErrAddonInvalidPriceForMultiInstance = models.NewValidationIssue(
	ErrCodeAddonInvalidPriceForMultiInstance,
	"only free or flat price ratecards are allowed for add-on with multiple instance type",
	models.WithFieldString("price"),
	models.WithWarningSeverity(),
	commonhttp.WithHTTPStatusCodeAttribute(http.StatusBadRequest),
)

ErrAddonRateCardUnitConfigMismatch is raised when an addon rate card carries a unit_config that differs from the rate card it extends. Addons layer price/entitlement/discounts additively but do not redefine the unit conversion; a divergent unit_config would be silently dropped by the overlay, so it is rejected here rather than accepted and ignored.

View Source
var ErrBillingCadenceInvalidValue = models.NewValidationIssue(
	ErrCodeBillingCadenceInvalidValue,
	"billing cadence must be positive and 1 hour long duration at least",
	models.WithFieldString("billingCadence"),
	models.WithWarningSeverity(),
	commonhttp.WithHTTPStatusCodeAttribute(http.StatusBadRequest),
)
View Source
var ErrDeprecatedUnalignedSubscription = models.NewValidationIssue(
	ErrCodeDeprecatedUnalignedSubscription,
	"unaligned subscriptions are being deprecated",
	models.WithWarningSeverity(),
	commonhttp.WithHTTPStatusCodeAttribute(http.StatusBadRequest),
)
View Source
var ErrEffectivePeriodFromAfterTo = models.NewValidationIssue(
	ErrCodeEffectivePeriodFromAfterTo,
	"effectiveFrom is after effectiveTo",
	models.WithFieldString("effectiveFrom"),
	models.WithWarningSeverity(),
	commonhttp.WithHTTPStatusCodeAttribute(http.StatusBadRequest),
)
View Source
var ErrEffectivePeriodFromNotSet = models.NewValidationIssue(
	ErrCodeEffectivePeriodFromNotSet,
	"effectiveFrom is must be provided if effectiveTo is set",
	models.WithFieldString("effectiveFrom"),
	models.WithWarningSeverity(),
	commonhttp.WithHTTPStatusCodeAttribute(http.StatusBadRequest),
)
View Source
var ErrEntitlementTemplateInvalidIssueAfterResetWithPriority = models.NewValidationIssue(
	ErrCodeEntitlementTemplateInvalidIssueAfterResetWithPriority,
	"invalid entitlement template as issue after reset is required if issue after reset priority is set",
	models.WithFieldString("issueAfterReset"),
	models.WithWarningSeverity(),
	commonhttp.WithHTTPStatusCodeAttribute(http.StatusBadRequest),
)
View Source
var ErrEntitlementTemplateInvalidJSONConfig = models.NewValidationIssue(
	ErrCodeEntitlementTemplateInvalidJSONConfig,
	"invalid JSON in static entitlement config",
	models.WithFieldString("entitlementTemplate", "config"),
	models.WithCriticalSeverity(),
	commonhttp.WithHTTPStatusCodeAttribute(http.StatusBadRequest),
)
View Source
var ErrEntitlementTemplateIssueAfterResetRequired = models.NewValidationIssue(
	ErrCodeEntitlementTemplateIssueAfterResetRequired,
	"issueAfterReset is required for metered entitlement templates",
	models.WithFieldString("entitlementTemplate", "issueAfterReset"),
	models.WithWarningSeverity(),
	commonhttp.WithHTTPStatusCodeAttribute(http.StatusBadRequest),
)
View Source
var ErrEntitlementTemplateNegativeUsagePeriod = models.NewValidationIssue(
	ErrCodeEntitlementTemplateNegativeUsagePeriod,
	"usage period must be positive",
	models.WithFieldString("usagePeriod"),
	models.WithWarningSeverity(),
)
View Source
var ErrEntitlementTemplateUsagePeriodLessThenAnHour = models.NewValidationIssue(
	ErrCodeEntitlementTemplateUsagePeriodLessThenAnHour,
	"usage period must be at least 1 hour",
	models.WithFieldString("usagePeriod"),
	models.WithWarningSeverity(),
	commonhttp.WithHTTPStatusCodeAttribute(http.StatusBadRequest),
)
View Source
var ErrPlanAddonCurrencyMismatch = models.NewValidationIssue(
	ErrCodePlanAddonCurrencyMismatch,
	"currency of the plan and addon must match",
	models.WithFieldString("currency"),
	models.WithWarningSeverity(),
	commonhttp.WithHTTPStatusCodeAttribute(http.StatusBadRequest),
)
View Source
var ErrPlanAddonIncompatibleStatus = models.NewValidationIssue(
	ErrCodePlanAddonIncompatibleStatus,
	"plan status is incompatible with the addon status",
	models.WithFieldString("status"),
	models.WithWarningSeverity(),
	commonhttp.WithHTTPStatusCodeAttribute(http.StatusBadRequest),
)
View Source
var ErrPlanAddonMaxQuantityMustBeSet = models.NewValidationIssue(
	ErrCodePlanAddonMaxQuantityMustBeSet,
	"maximum quantity, when set, must be a positive integer for add-ons with multiple instance type",
	models.WithFieldString("maxQuantity"),
	models.WithWarningSeverity(),
	commonhttp.WithHTTPStatusCodeAttribute(http.StatusBadRequest),
)
View Source
var ErrPlanAddonMaxQuantityMustNotBeSet = models.NewValidationIssue(
	ErrCodePlanAddonMaxQuantityMustNotBeSet,
	"maximum quantity must not be set for add-on with single instance type",
	models.WithFieldString("maxQuantity"),
	models.WithWarningSeverity(),
	commonhttp.WithHTTPStatusCodeAttribute(http.StatusBadRequest),
)
View Source
var ErrPlanAddonUnknownPlanPhaseKey = models.NewValidationIssue(
	ErrCodePlanAddonUnknownPlanPhaseKey,
	"add-on must define valid/existing plan phase key from which the add-on is available for purchase",
	models.WithFieldString("fromPlanPhase"),
	models.WithWarningSeverity(),
)
View Source
var ErrPlanBillingCadenceAllowedValues = []datetime.ISODurationString{
	"P1W",
	"P2W",
	"P4W",
	"P1M",
	"P3M",
	"P6M",
	"P12M",
	"P1Y",
}
View Source
var ErrPlanBillingCadenceInvalid = models.NewValidationIssue(
	ErrCodePlanBillingCadenceInvalid,
	fmt.Sprintf("billing cadence must be one of the following: %s", strings.Join(lo.Map(ErrPlanBillingCadenceAllowedValues, func(v datetime.ISODurationString, _ int) string { return v.String() }), ", ")),
	models.WithFieldString("billingCadence"),
	models.WithCriticalSeverity(),
	commonhttp.WithHTTPStatusCodeAttribute(http.StatusBadRequest),
)
View Source
var ErrPlanBillingCadenceNotCompatible = models.NewValidationIssue(
	ErrCodePlanBillingCadenceNotCompatible,
	"plan billing cadence is not compatible with rate card billing cadence",
	models.WithFieldString("billingCadence"),
	models.WithWarningSeverity(),
	commonhttp.WithHTTPStatusCodeAttribute(http.StatusBadRequest),
)
View Source
var ErrPlanHasLastPhaseWithDuration = models.NewValidationIssue(
	ErrCodePlanHasLastPhaseWithDuration,
	"duration must not be set for the last plan phase",
	models.WithFieldString("duration"),
	models.WithWarningSeverity(),
	commonhttp.WithHTTPStatusCodeAttribute(http.StatusBadRequest),
)
View Source
var ErrPlanHasNonLastPhaseWithNoDuration = models.NewValidationIssue(
	ErrCodePlanHasNonLastPhaseWithNoDuration,
	"duration must be set for plan phase if it is not the last one",
	models.WithFieldString("duration"),
	models.WithWarningSeverity(),
	commonhttp.WithHTTPStatusCodeAttribute(http.StatusBadRequest),
)
View Source
var ErrPlanPhaseHasNoRateCards = models.NewValidationIssue(
	ErrCodePlanPhaseHasNoRateCards,
	"plan phase must have at least one rate card",
	models.WithFieldString("rateCards"),
	models.WithWarningSeverity(),
	commonhttp.WithHTTPStatusCodeAttribute(http.StatusBadRequest),
)
View Source
var ErrRateCardBillingCadenceUnaligned = models.NewValidationIssue(
	ErrCodeRateCardBillingCadenceUnaligned,
	"ratecards with prices must have compatible billing cadence",
	models.WithFieldString("billingCadence"),
	models.WithWarningSeverity(),
	commonhttp.WithHTTPStatusCodeAttribute(http.StatusBadRequest),
)
View Source
var ErrRateCardEntitlementTemplateWithNoFeature = models.NewValidationIssue(
	ErrCodeRateCardEntitlementTemplateWithNoFeature,
	"entitlement template requires feature to be associated with",
	models.WithFieldString("featureKey"),
	models.WithWarningSeverity(),
	commonhttp.WithHTTPStatusCodeAttribute(http.StatusBadRequest),
)
View Source
var ErrRateCardFeatureMismatch = models.NewValidationIssue(
	ErrCodeRateCardFeatureMismatch,
	"feature id and key must reference the same feature",
	models.WithFieldString("featureKey"),
	models.WithCriticalSeverity(),
	commonhttp.WithHTTPStatusCodeAttribute(http.StatusBadRequest),
)
View Source
var ErrRateCardMeteredEntitlementTemplateUsagePeriodMismatch = models.NewValidationIssue(
	ErrCodeRateCardMeteredEntitlementTemplateUsagePeriodMismatch,
	"usage period for metered entitlement template must match",
	models.WithFieldString("usagePeriod"),
	models.WithWarningSeverity(),
	commonhttp.WithHTTPStatusCodeAttribute(http.StatusBadRequest),
)
View Source
var ErrRateCardMultipleBillingCadence = models.NewValidationIssue(
	ErrCodeRateCardMultipleBillingCadence,
	"ratecards with prices must have the exact same billing cadence",
	models.WithFieldString("billingCadence"),
	models.WithWarningSeverity(),
	commonhttp.WithHTTPStatusCodeAttribute(http.StatusBadRequest),
)
View Source
var ErrRateCardOnlyFlatPriceAllowed = models.NewValidationIssue(
	ErrCodeRateCardOnlyFlatPriceAllowed,
	"only flat price is allowed",
	models.WithFieldString("price"),
	models.WithWarningSeverity(),
)
View Source
var ErrRateCardPercentageDiscountNotAllowed = models.NewValidationIssue(
	ErrCodeRateCardPercentageDiscountNotAllowed,
	"percentage discount is not allowed",
	models.WithFieldString("percentage"),
	models.WithWarningSeverity(),
)
View Source
var ErrRateCardPricePaymentTermMismatch = models.NewValidationIssue(
	ErrCodeRateCardPricePaymentTermMismatch,
	"price payment term must match",
	models.WithFieldString("price"),
	models.WithWarningSeverity(),
)
View Source
var ErrRateCardPriceTypeMismatch = models.NewValidationIssue(
	ErrCodeRateCardPriceTypeMismatch,
	"price type must match",
	models.WithFieldString("price"),
	models.WithWarningSeverity(),
)
View Source
var ErrRateCardStaticEntitlementTemplateNotAllowed = models.NewValidationIssue(
	ErrCodeRateCardStaticEntitlementTemplateNotAllowed,
	"static entitlement template is not allowed",
	models.WithFieldString("type"),
	models.WithWarningSeverity(),
)
View Source
var ErrRateCardUnitConfigRequiresUsageBasedPrice = models.NewValidationIssue(
	ErrCodeRateCardUnitConfigRequiresUsageBasedPrice,
	"unit config requires a usage-based price (unit, graduated, or volume)",
	models.WithFieldString("unit_config"),
	models.WithWarningSeverity(),
	commonhttp.WithHTTPStatusCodeAttribute(http.StatusBadRequest),
)
View Source
var ErrRateCardUsageBasedPriceWithFeatureAndNoMeter = models.NewValidationIssue(
	ErrCodeRateCardUsageBasedPriceWithFeatureAndNoMeter,
	"usage-based price requires feature with meter to be associated with",
	models.WithFieldString("featureKey"),
	models.WithWarningSeverity(),
	commonhttp.WithHTTPStatusCodeAttribute(http.StatusBadRequest),
)
View Source
var ErrRateCardUsageBasedPriceWithNoFeature = models.NewValidationIssue(
	ErrCodeRateCardUsageBasedPriceWithNoFeature,
	"usage-based price requires feature to be associated with",
	models.WithFieldString("featureKey"),
	models.WithWarningSeverity(),
	commonhttp.WithHTTPStatusCodeAttribute(http.StatusBadRequest),
)
View Source
var ErrUnitConfigNotRepresentable = models.NewValidationIssue(
	ErrCodeUnitConfigNotRepresentable,
	"this resource uses unit_config and is only available via the v3 API",
	models.WithCriticalSeverity(),
	commonhttp.WithHTTPStatusCodeAttribute(http.StatusBadRequest),
)
View Source
var ErrUsageDiscountWithFlatPrice = models.NewValidationIssue(
	ErrCodeUsageDiscountWithFlatPrice,
	"usage discount is not supported for flat price",
	models.WithWarningSeverity(),
	commonhttp.WithHTTPStatusCodeAttribute(http.StatusBadRequest),
)
View Source
var ValidateRateCardsHaveCompatibleBillingCadence = models.ValidatorFunc[RateCardWithOverlay](func(r RateCardWithOverlay) error {
	if r.base == nil || r.overlay == nil {
		return nil
	}

	var errs []error

	rBillingCadence, vBillingCadence := r.base.GetBillingCadence(), r.overlay.GetBillingCadence()

	fieldSelector := models.NewFieldSelectorGroup(models.NewFieldSelector("ratecards").
		WithExpression(models.NewFieldAttrValue("key", r.base.Key())))

	if rBillingCadence != nil && vBillingCadence != nil && !rBillingCadence.Equal(vBillingCadence) {
		errs = append(errs, models.ErrorWithFieldPrefix(fieldSelector, ErrRateCardBillingCadenceMismatch))
	}

	return errors.Join(errs...)
})
View Source
var ValidateRateCardsHaveCompatibleDiscounts = models.ValidatorFunc[RateCardWithOverlay](func(r RateCardWithOverlay) error {
	if r.base == nil || r.overlay == nil {
		return nil
	}

	var errs []error

	rMeta, vMeta := r.base.AsMeta(), r.overlay.AsMeta()

	if rMeta.Discounts.Percentage != nil && vMeta.Discounts.Percentage != nil {
		fieldSelector := models.NewFieldSelectorGroup(models.NewFieldSelector("discounts"))

		errs = append(errs, models.ErrorWithFieldPrefix(fieldSelector, ErrRateCardPercentageDiscountNotAllowed))
	}

	err := errors.Join(errs...)
	if err != nil {
		fieldSelector := models.NewFieldSelectorGroup(
			models.NewFieldSelector("ratecards").
				WithExpression(models.NewFieldAttrValue("key", r.base.Key())),
		)

		return models.ErrorWithFieldPrefix(fieldSelector, err)
	}

	return nil
})
View Source
var ValidateRateCardsHaveCompatibleEntitlementTemplate = models.ValidatorFunc[RateCardWithOverlay](func(r RateCardWithOverlay) error {
	if r.base == nil || r.overlay == nil {
		return nil
	}

	var errs []error

	rMeta, vMeta := r.base.AsMeta(), r.overlay.AsMeta()

	if rMeta.EntitlementTemplate != nil && vMeta.EntitlementTemplate != nil {
		if rMeta.EntitlementTemplate.Type() != vMeta.EntitlementTemplate.Type() {
			errs = append(errs, ErrRateCardEntitlementTemplateTypeMismatch)
		} else {
			switch rMeta.EntitlementTemplate.Type() {
			case entitlement.EntitlementTypeStatic:
				errs = append(errs, ErrRateCardStaticEntitlementTemplateNotAllowed)
			case entitlement.EntitlementTypeMetered:
				rMetered, err := rMeta.EntitlementTemplate.AsMetered()
				if err != nil {
					return err
				}

				vMetered, err := vMeta.EntitlementTemplate.AsMetered()
				if err != nil {
					return err
				}

				if !rMetered.UsagePeriod.Equal(&vMetered.UsagePeriod) {
					errs = append(errs, ErrRateCardMeteredEntitlementTemplateUsagePeriodMismatch)
				}
			case entitlement.EntitlementTypeBoolean:
			}
		}
	}

	err := errors.Join(errs...)
	if err != nil {
		fieldSelector := models.NewFieldSelectorGroup(
			models.NewFieldSelector("ratecards").
				WithExpression(models.NewFieldAttrValue("key", r.base.Key())),
			models.NewFieldSelector("entitlementTemplate"),
		)

		return models.ErrorWithFieldPrefix(fieldSelector, err)
	}

	return nil
})
View Source
var ValidateRateCardsHaveCompatibleFeatureID = models.ValidatorFunc[RateCardWithOverlay](func(r RateCardWithOverlay) error {
	if r.base == nil || r.overlay == nil {
		return nil
	}

	rMeta, vMeta := r.base.AsMeta(), r.overlay.AsMeta()

	fieldSelector := models.NewFieldSelectorGroup(models.NewFieldSelector("ratecards").
		WithExpression(models.NewFieldAttrValue("key", r.base.Key())))

	if rMeta.FeatureID != nil && vMeta.FeatureID != nil && *rMeta.FeatureID != *vMeta.FeatureID {
		return models.ErrorWithFieldPrefix(fieldSelector, ErrRateCardFeatureIDMismatch)
	}

	return nil
})
View Source
var ValidateRateCardsHaveCompatibleFeatureKey = models.ValidatorFunc[RateCardWithOverlay](func(r RateCardWithOverlay) error {
	if r.base == nil || r.overlay == nil {
		return nil
	}

	rMeta, vMeta := r.base.AsMeta(), r.overlay.AsMeta()

	fieldSelector := models.NewFieldSelectorGroup(models.NewFieldSelector("ratecards").
		WithExpression(models.NewFieldAttrValue("key", r.base.Key())))

	if rMeta.FeatureKey != nil && vMeta.FeatureKey != nil && *rMeta.FeatureKey != *vMeta.FeatureKey {
		return models.ErrorWithFieldPrefix(fieldSelector, ErrRateCardFeatureKeyMismatch)
	}

	return nil
})
View Source
var ValidateRateCardsHaveCompatiblePrice = models.ValidatorFunc[RateCardWithOverlay](func(r RateCardWithOverlay) error {
	if r.base == nil || r.overlay == nil {
		return nil
	}

	var errs []error

	rMeta, vMeta := r.base.AsMeta(), r.overlay.AsMeta()

	fieldSelector := models.NewFieldSelectorGroup(models.NewFieldSelector("ratecards").
		WithExpression(models.NewFieldAttrValue("key", r.base.Key())))

	if rMeta.Price != nil && vMeta.Price != nil {
		if rMeta.Price.Type() != vMeta.Price.Type() {
			errs = append(errs, models.ErrorWithFieldPrefix(fieldSelector, ErrRateCardPriceTypeMismatch))
		}

		switch rMeta.Price.Type() {
		case FlatPriceType:
			rFlat, _ := rMeta.Price.AsFlat()
			vFlat, _ := vMeta.Price.AsFlat()

			if rFlat.PaymentTerm != vFlat.PaymentTerm {
				errs = append(errs, models.ErrorWithFieldPrefix(fieldSelector, ErrRateCardPricePaymentTermMismatch))
			}
		default:
			errs = append(errs, models.ErrorWithFieldPrefix(fieldSelector, ErrRateCardOnlyFlatPriceAllowed))
		}
	}

	return errors.Join(errs...)
})
View Source
var ValidateRateCardsHaveCompatibleUnitConfig = models.ValidatorFunc[RateCardWithOverlay](func(r RateCardWithOverlay) error {
	if r.base == nil || r.overlay == nil {
		return nil
	}

	rMeta, vMeta := r.base.AsMeta(), r.overlay.AsMeta()

	if rMeta.UnitConfig != nil && vMeta.UnitConfig != nil && !rMeta.UnitConfig.Equal(vMeta.UnitConfig) {
		fieldSelector := models.NewFieldSelectorGroup(models.NewFieldSelector("ratecards").
			WithExpression(models.NewFieldAttrValue("key", r.base.Key())))

		return models.ErrorWithFieldPrefix(fieldSelector, ErrAddonRateCardUnitConfigMismatch)
	}

	return nil
})
View Source
var ValidateRateCardsShareSameKey = models.ValidatorFunc[RateCardWithOverlay](func(r RateCardWithOverlay) error {
	if r.base == nil || r.overlay == nil {
		return nil
	}

	fieldSelector := models.NewFieldSelectorGroup(models.NewFieldSelector("ratecards").
		WithExpression(models.NewFieldAttrValue("key", r.base.Key())))

	if r.base.Key() != r.overlay.Key() {
		return models.ErrorWithFieldPrefix(fieldSelector, ErrRateCardKeyMismatch)
	}

	return nil
})

Functions

func AddonRateCardMatcherForAGivenPlanRateCard

func AddonRateCardMatcherForAGivenPlanRateCard(planRateCard RateCard) func(addonRateCard RateCard) bool

Determines if an Addon RateCard will effect a given Plan RateCard Right now we only support a single RateCard per addon effecting a single plan RateCard and we match them by key. FIXME(galexi): matching like this is unwieldy as sometimes we'd want to match productcatalog.RateCard, sometimes addon.RateCard, or subscriptionaddon.RateCard...

func PercentageDiscountWithValidValue

func PercentageDiscountWithValidValue() models.ValidatorFunc[PercentageDiscount]

func ResolveTaxConfig

func ResolveTaxConfig(ctx context.Context, svc taxcode.Service, namespace string, cfg *TaxConfig) error

ResolveTaxConfig cross-populates TaxCodeID and provider-specific codes on the pointed-to config so the persisted record is internally consistent. Four input cases:

  • Only TaxCodeID: looks up the entity, validates it exists (400 if not), and sets Stripe from the entity's Stripe app mapping (or clears Stripe if the entity has no mapping).
  • Only Stripe.Code: upserts the TaxCode entity via GetOrCreateByAppMapping and stamps TaxCodeID (idempotent; updating the code txcd_A → txcd_B updates the FK).
  • Both TaxCodeID and Stripe.Code: TaxCodeID wins. Stripe is overridden from the entity's Stripe app mapping (or cleared if the entity has no mapping); the caller-supplied Stripe.Code is discarded.
  • Neither: no-op.

No-op when cfg is nil.

func UsageDiscountWithPrice

func UsageDiscountWithPrice(price *Price) models.ValidatorFunc[UsageDiscount]

func UsageDiscountWithValidValue

func UsageDiscountWithValidValue() models.ValidatorFunc[UsageDiscount]

func ValidateAddonBillingCadenceAreAlignedWithPlan

func ValidateAddonBillingCadenceAreAlignedWithPlan(addonRateCards RateCards) models.ValidatorFunc[Plan]

func ValidateAddonHasCompatiblePrices

func ValidateAddonHasCompatiblePrices() models.ValidatorFunc[Addon]

func ValidateAddonHasSingleBillingCadence

func ValidateAddonHasSingleBillingCadence() models.ValidatorFunc[Addon]

func ValidateAddonMeta

func ValidateAddonMeta() models.ValidatorFunc[Addon]

ValidateAddonMeta returns a validation function can be passed to the object which implements models.CustomValidator interface. It validates attributes in AddonMeta of Addon.

func ValidateAddonRateCards

func ValidateAddonRateCards() models.ValidatorFunc[Addon]

ValidateAddonRateCards returns a validation function can be passed to the object which implements models.CustomValidator interface. It checks for invalid and duplicated ratecards.

func ValidateAddonStatusPublishable

func ValidateAddonStatusPublishable() models.ValidatorFunc[Addon]

func ValidateAddonWithFeatures

func ValidateAddonWithFeatures(ctx context.Context, resolver NamespacedFeatureResolver) models.ValidatorFunc[Addon]

func ValidateAddonWithStatus

func ValidateAddonWithStatus(allowed ...AddonStatus) models.ValidatorFunc[Addon]

func ValidateBillingCadencesAlign

func ValidateBillingCadencesAlign(planBillingCadence datetime.ISODuration, rateCardBillingCadence datetime.ISODuration) error

Alignment means that either - the two cadences are identical - if a RateCard's cadence is "longer" than the Plan's cadence, the plan cadence must "divide" without remainder the ratecard's cadence - if a RateCard's cadence is "shorter" than the Plan's cadence, the ratecard's cadence must "divide" without remainder the plan's cadence "longer" and "shorter" are not generally meaningful terms for periods, as for instance sometimes P1M equals P4W, sometimes its longer.

func ValidateEffectivePeriod

func ValidateEffectivePeriod() models.ValidatorFunc[EffectivePeriod]

func ValidatePhaseMeta

func ValidatePhaseMeta() models.ValidatorFunc[Phase]

ValidatePhaseMeta returns a validation function can be passed to the object which implements models.CustomValidator interface. It validates attributes in PhaseMeta of Phase.

func ValidatePhaseRateCards

func ValidatePhaseRateCards() models.ValidatorFunc[Phase]

ValidatePhaseRateCards returns a validation function can be passed to the object which implements models.CustomValidator interface. It checks for invalid and duplicated ratecards in Phase.

func ValidatePlanBillingCadenceLiteral

func ValidatePlanBillingCadenceLiteral() models.ValidatorFunc[Plan]

ValidatePlanBillingCadenceLiteral validates that the billing cadence of the plan is at least a month.

func ValidatePlanHasAlignedBillingCadences

func ValidatePlanHasAlignedBillingCadences() models.ValidatorFunc[Plan]

ValidatePlanHasAlignedBillingCadences validates that the billing cadence of the plan is aligned with the billing cadence of the rate cards.

func ValidatePlanMeta

func ValidatePlanMeta() models.ValidatorFunc[Plan]

func ValidatePlanPhaseAndAddonRateCardsAreCompatible

func ValidatePlanPhaseAndAddonRateCardsAreCompatible(addonRateCards RateCards) models.ValidatorFunc[Phase]

func ValidatePlanPhases

func ValidatePlanPhases() models.ValidatorFunc[Plan]

func ValidatePlanWithFeatures

func ValidatePlanWithFeatures(ctx context.Context, resolver NamespacedFeatureResolver) models.ValidatorFunc[Plan]

func ValidateRateCards

func ValidateRateCards() models.ValidatorFunc[RateCards]

ValidateRateCards returns a validation function can be passed to the object which implements models.CustomValidator interface. It checks for invalid and duplicated ratecards in the RateCards collection.

func ValidateRateCardsWithFeatures

func ValidateRateCardsWithFeatures(ctx context.Context, resolver NamespacedFeatureResolver) func(cards RateCards) error

Types

type Addon

type Addon struct {
	AddonMeta

	// RateCards
	RateCards RateCards `json:"rateCards"`
}

func (Addon) Equal

func (a Addon) Equal(v Addon) bool

func (Addon) HasUnitConfig

func (a Addon) HasUnitConfig() bool

func (Addon) Publishable

func (a Addon) Publishable() error

Publishable validates the Addon to ensure that it meets all requirements needed for being published. It is a stricter version of Validate. It is the caller's responsibility to handle managed resource-specific parameters to ensure the Addon is eligible for publishing. E.g. checking the DeletedAt attribute of the addon.Addon.

func (Addon) Validate

func (a Addon) Validate() error

func (Addon) ValidateWith

func (a Addon) ValidateWith(validators ...models.ValidatorFunc[Addon]) error

func (Addon) ValidationErrors

func (a Addon) ValidationErrors() (models.ValidationIssues, error)

ValidationErrors returns a list of possible validation errors for the add-on. It returns nil if the add-on has no validation issues.

type AddonInstanceType

type AddonInstanceType string
const (
	AddonInstanceTypeSingle   AddonInstanceType = "single"
	AddonInstanceTypeMultiple AddonInstanceType = "multiple"
)

func (AddonInstanceType) Validate

func (a AddonInstanceType) Validate() error

func (AddonInstanceType) Values

func (a AddonInstanceType) Values() []string

type AddonMeta

type AddonMeta struct {
	EffectivePeriod

	// Key is the unique key for Add-on.
	Key string `json:"key"`

	// Version
	Version int `json:"version"`

	// Name
	Name string `json:"name"`

	// Description
	Description *string `json:"description,omitempty"`

	// Currency
	Currency currency.Code `json:"currency"`

	// InstanceType
	InstanceType AddonInstanceType `json:"instanceType"`

	// Metadata
	Metadata models.Metadata `json:"metadata,omitempty"`

	// Annotations
	Annotations models.Annotations `json:"annotations,omitempty"`
}

func (AddonMeta) Equal

func (m AddonMeta) Equal(v AddonMeta) bool

func (AddonMeta) Status

func (m AddonMeta) Status() AddonStatus

Status returns the current status of the Addons

func (AddonMeta) StatusAt

func (m AddonMeta) StatusAt(t time.Time) AddonStatus

StatusAt returns the Addon status relative to time t.

func (AddonMeta) Validate

func (m AddonMeta) Validate() error

type AddonStatus

type AddonStatus string
const (
	AddonStatusDraft    AddonStatus = "draft"
	AddonStatusActive   AddonStatus = "active"
	AddonStatusArchived AddonStatus = "archived"
	AddonStatusInvalid  AddonStatus = "invalid"
)

func (AddonStatus) Validate

func (s AddonStatus) Validate() error

func (AddonStatus) Values

func (s AddonStatus) Values() []AddonStatus

type BooleanEntitlementTemplate

type BooleanEntitlementTemplate struct {
	// Metadata a set of key/value pairs describing metadata for the RateCard.
	Metadata models.Metadata `json:"metadata,omitempty"`
}

func (*BooleanEntitlementTemplate) Equal

func (*BooleanEntitlementTemplate) Validate

func (t *BooleanEntitlementTemplate) Validate() error

type Commitments

type Commitments struct {
	// MinimumAmount defines the least amount the customer committed to spend.
	MinimumAmount *decimal.Decimal `json:"minimumAmount,omitempty"`

	// MaximumAmount defines the upper limit of amount the customer entitled to spend.
	MaximumAmount *decimal.Decimal `json:"maximumAmount,omitempty"`
}

func (Commitments) Equal

func (c Commitments) Equal(v Commitments) bool

func (Commitments) Validate

func (c Commitments) Validate() error

type Discounts

type Discounts struct {
	Percentage *PercentageDiscount `json:"percentage,omitempty"`
	Usage      *UsageDiscount      `json:"usage,omitempty"`
}

func (Discounts) Clone

func (d Discounts) Clone() Discounts

func (Discounts) Equal

func (d Discounts) Equal(v Discounts) bool

func (Discounts) IsEmpty

func (d Discounts) IsEmpty() bool

func (*Discounts) Validate

func (d *Discounts) Validate() error

func (Discounts) ValidateForPrice

func (d Discounts) ValidateForPrice(price *Price) error

type DynamicPrice

type DynamicPrice struct {
	Commitments `json:",inline"`

	// Multiplier defines the multiplier applied to the price.
	Multiplier decimal.Decimal `json:"multiplier"`
}

func (DynamicPrice) Clone

func (p DynamicPrice) Clone() DynamicPrice

func (*DynamicPrice) Equal

func (p *DynamicPrice) Equal(v *DynamicPrice) bool

func (DynamicPrice) Validate

func (p DynamicPrice) Validate() error

type EffectivePeriod

type EffectivePeriod struct {
	// EffectiveFrom defines the time from the Plan or Addon becomes active.
	EffectiveFrom *time.Time `json:"effectiveFrom,omitempty"`

	// EffectiveTo defines the time from the Plan or Addon becomes archived.
	EffectiveTo *time.Time `json:"effectiveTo,omitempty"`
}

EffectivePeriod describes lifecycle of resource based on the time period defined by it.

func (EffectivePeriod) AsPeriod

func (p EffectivePeriod) AsPeriod() timeutil.OpenPeriod

func (EffectivePeriod) Equal

Equal returns true if the two EffectivePeriod objects are equal.

func (EffectivePeriod) Validate

func (p EffectivePeriod) Validate() error

func (EffectivePeriod) ValidateWith

type EntitlementTemplate

type EntitlementTemplate struct {
	// contains filtered or unexported fields
}

EntitlementTemplate is the template used for instantiating entitlement.Entitlement for RateCard.

func (*EntitlementTemplate) AsBoolean

func (*EntitlementTemplate) AsMetered

func (*EntitlementTemplate) AsStatic

func (*EntitlementTemplate) Equal

func (*EntitlementTemplate) FromBoolean

func (*EntitlementTemplate) FromMetered

func (*EntitlementTemplate) FromStatic

func (*EntitlementTemplate) MarshalJSON

func (e *EntitlementTemplate) MarshalJSON() ([]byte, error)

func (*EntitlementTemplate) Type

func (*EntitlementTemplate) UnmarshalJSON

func (e *EntitlementTemplate) UnmarshalJSON(bytes []byte) error

func (*EntitlementTemplate) Validate

func (e *EntitlementTemplate) Validate() error

type FeatureResolver

type FeatureResolver interface {
	Resolve(ctx context.Context, namespace string, id, key *string) (*feature.Feature, error)
	BatchResolve(ctx context.Context, namespace string, idOrKeys ...string) (map[string]*feature.Feature, error)
	WithNamespace(namespace string) NamespacedFeatureResolver
}

type FlatFeeRateCard

type FlatFeeRateCard struct {
	RateCardMeta

	// BillingCadence defines the billing cadence of the RateCard in ISO8601 format.
	// When nil (null) it means it is a one time fee.
	// Example: "P1D12H"
	BillingCadence *datetime.ISODuration `json:"billingCadence"`
}

func (*FlatFeeRateCard) AsMeta

func (r *FlatFeeRateCard) AsMeta() RateCardMeta

func (*FlatFeeRateCard) ChangeMeta

func (r *FlatFeeRateCard) ChangeMeta(fn func(m RateCardMeta) (RateCardMeta, error)) error

func (*FlatFeeRateCard) Clone

func (r *FlatFeeRateCard) Clone() RateCard

func (*FlatFeeRateCard) Compatible

func (r *FlatFeeRateCard) Compatible(v RateCard) error

func (*FlatFeeRateCard) Equal

func (r *FlatFeeRateCard) Equal(v RateCard) bool

func (*FlatFeeRateCard) GetBillingCadence

func (r *FlatFeeRateCard) GetBillingCadence() *datetime.ISODuration

func (*FlatFeeRateCard) Key

func (r *FlatFeeRateCard) Key() string

func (*FlatFeeRateCard) MarshalJSON

func (r *FlatFeeRateCard) MarshalJSON() ([]byte, error)

func (*FlatFeeRateCard) Merge

func (r *FlatFeeRateCard) Merge(v RateCard) error

func (*FlatFeeRateCard) Type

func (r *FlatFeeRateCard) Type() RateCardType

func (*FlatFeeRateCard) Validate

func (r *FlatFeeRateCard) Validate() error

func (*FlatFeeRateCard) ValidateWith

func (r *FlatFeeRateCard) ValidateWith(v ...models.ValidatorFunc[*FlatFeeRateCard]) error

type FlatPrice

type FlatPrice struct {
	// Amount of the flat price.
	Amount decimal.Decimal `json:"amount"`

	// PaymentTerm defines the payment term of the flat price.
	// Defaults to InAdvancePaymentTerm.
	PaymentTerm PaymentTermType `json:"paymentTerm,omitempty"`
}

func (*FlatPrice) Clone

func (f *FlatPrice) Clone() FlatPrice

func (*FlatPrice) Equal

func (f *FlatPrice) Equal(v *FlatPrice) bool

func (*FlatPrice) Validate

func (f *FlatPrice) Validate() error

type MeteredEntitlementTemplate

type MeteredEntitlementTemplate struct {
	// Metadata a set of key/value pairs describing metadata for the RateCard.
	Metadata models.Metadata `json:"metadata,omitempty"`

	// IsSoftLimit set to `true` for allowing the subject to use the feature even if the entitlement is exhausted.
	IsSoftLimit bool `json:"isSoftLimit,omitempty"`

	// IssueAfterReset defines the amount to be automatically granted at entitlement.Entitlement creation or reset.
	IssueAfterReset *float64 `json:"issueAfterReset,omitempty"`

	// IssueAfterResetPriority defines the grant priority for the default grant.
	IssueAfterResetPriority *uint8 `json:"issueAfterResetPriority,omitempty"`

	// PreserveOverageAtReset defines whether the overage is preserved after reset.
	PreserveOverageAtReset *bool `json:"preserveOverageAtReset,omitempty"`

	// UsagePeriod defines the interval of the entitlement in ISO8601 format.
	// Defaults to the billing cadence of the rate card.
	// Example: "P1D12H"
	UsagePeriod datetime.ISODuration `json:"usagePeriod,omitempty"`
}

func (*MeteredEntitlementTemplate) Equal

func (*MeteredEntitlementTemplate) Validate

func (t *MeteredEntitlementTemplate) Validate() error

type NamespacedFeatureResolver

type NamespacedFeatureResolver interface {
	Resolve(ctx context.Context, id, key *string) (*feature.Feature, error)
	BatchResolve(ctx context.Context, idOrKeys ...string) (map[string]*feature.Feature, error)
	Namespace() string
}

type PackagePrice

type PackagePrice struct {
	Commitments `json:",inline"`

	Amount             decimal.Decimal `json:"amount"`
	QuantityPerPackage decimal.Decimal `json:"quantityPerPackage"`
}

func (PackagePrice) Clone

func (p PackagePrice) Clone() PackagePrice

func (*PackagePrice) Equal

func (p *PackagePrice) Equal(v *PackagePrice) bool

func (PackagePrice) Validate

func (p PackagePrice) Validate() error

type PaymentTermType

type PaymentTermType string
const (
	InAdvancePaymentTerm PaymentTermType = "in_advance"
	InArrearsPaymentTerm PaymentTermType = "in_arrears"
)

func (PaymentTermType) StringValues

func (p PaymentTermType) StringValues() []string

func (PaymentTermType) Validate

func (p PaymentTermType) Validate() error

func (PaymentTermType) Values

func (p PaymentTermType) Values() []string

type PercentageDiscount

type PercentageDiscount struct {
	// Percentage defines percentage of the discount.
	Percentage models.Percentage `json:"percentage"`
}

func (PercentageDiscount) Clone

func (PercentageDiscount) Hash

func (d PercentageDiscount) Hash() hasher.Hash

func (PercentageDiscount) Validate

func (d PercentageDiscount) Validate() error

func (PercentageDiscount) ValidateForPrice

func (d PercentageDiscount) ValidateForPrice(_ *Price) error

func (PercentageDiscount) ValidateWith

type Phase

type Phase struct {
	PhaseMeta

	// RateCards
	RateCards RateCards `json:"rateCards"`
}

func (Phase) Equal

func (p Phase) Equal(v Phase) bool

Equal returns true if the two Phases are equal.

func (Phase) Validate

func (p Phase) Validate() error

Validate validates the Phase.

func (Phase) ValidateWith

func (p Phase) ValidateWith(v ...models.ValidatorFunc[Phase]) error

type PhaseMeta

type PhaseMeta struct {
	// Key is the unique key for Phase.
	Key string `json:"key"`

	// Name is the name of the Phase.
	Name string `json:"name"`

	// Description is the detailed description of the Phase.
	Description *string `json:"description,omitempty"`

	// Metadata stores user defined metadata for Phase.
	Metadata models.Metadata `json:"metadata,omitempty"`

	// Duration is the duration of the Phase.
	Duration *datetime.ISODuration `json:"duration"`
}

func (PhaseMeta) Equal

func (p PhaseMeta) Equal(v PhaseMeta) bool

Equal returns true if the two PhaseMetas are equal.

func (PhaseMeta) Validate

func (p PhaseMeta) Validate() error

Validate validates the PhaseMeta.

type Plan

type Plan struct {
	PlanMeta

	// Phases
	Phases []Phase `json:"phases"`
}

func (Plan) Equal

func (p Plan) Equal(o Plan) bool

Equal returns true if the two Plans are equal.

func (Plan) HasUnitConfig

func (p Plan) HasUnitConfig() bool

HasUnitConfig reports whether any rate card in any phase carries a unit_config conversion. The v1 API cannot represent unit_config, so v1 read and mutation surfaces use this to reject such plans instead of silently stripping the field.

func (Plan) Validate

func (p Plan) Validate() error

func (Plan) ValidateWith

func (p Plan) ValidateWith(validators ...models.ValidatorFunc[Plan]) error

func (Plan) ValidationErrors

func (p Plan) ValidationErrors() (models.ValidationIssues, error)

ValidationErrors returns a list of possible validation errors for the plan. It returns nil if the plan has no validation issues.

type PlanAddon

type PlanAddon struct {
	PlanAddonMeta

	// Plan
	Plan Plan `json:"plan"`

	// Addon
	Addon Addon `json:"addon"`
}

func (PlanAddon) Validate

func (c PlanAddon) Validate() error

func (PlanAddon) ValidateWith

func (c PlanAddon) ValidateWith(validators ...models.ValidatorFunc[PlanAddon]) error

func (PlanAddon) ValidationErrors

func (c PlanAddon) ValidationErrors() (models.ValidationIssues, error)

ValidationErrors returns a list of possible validation error(s) regarding to compatibility of the plan and add-on in the assignment. It returns nil if the plan and add-on are compatible.

type PlanAddonConfig

type PlanAddonConfig struct {
	// FromPlanPhase
	FromPlanPhase string `json:"fromPlanPhase"`

	// MaxQuantity caps how many units a customer may assign for multiple-instance addons.
	// nil means unlimited; a positive integer enforces a hard cap; zero or negative is invalid.
	MaxQuantity *int `json:"maxQuantity"`
}

type PlanAddonMeta

type PlanAddonMeta struct {
	models.Metadata
	models.Annotations

	PlanAddonConfig
}

type PlanMeta

type PlanMeta struct {
	EffectivePeriod

	// Key is the unique key for Plan.
	Key string `json:"key"`

	// Version
	Version int `json:"version"`

	// Name
	Name string `json:"name"`

	// Description
	Description *string `json:"description,omitempty"`

	// Currency
	Currency currency.Code `json:"currency"`

	// BillingCadence is the default billing cadence for subscriptions using this plan.
	BillingCadence datetime.ISODuration `json:"billing_cadence"`

	// ProRatingConfig is the default pro-rating configuration for subscriptions using this plan.
	ProRatingConfig ProRatingConfig `json:"pro_rating_config"`

	// SettlementMode is the settlement mode for subscriptions using this plan.
	SettlementMode SettlementMode `json:"settlement_mode"`

	// Metadata
	Metadata models.Metadata `json:"metadata,omitempty"`
}

func (PlanMeta) Equal

func (p PlanMeta) Equal(o PlanMeta) bool

Equal returns true if the two PlanMetas are equal.

func (PlanMeta) Status

func (p PlanMeta) Status() PlanStatus

Status returns the current status of the Plan

func (PlanMeta) StatusAt

func (p PlanMeta) StatusAt(t time.Time) PlanStatus

StatusAt returns the plan status relative to time t.

func (PlanMeta) Validate

func (p PlanMeta) Validate() error

Validate validates the PlanMeta.

type PlanStatus

type PlanStatus string
const (
	PlanStatusDraft     PlanStatus = "draft"
	PlanStatusActive    PlanStatus = "active"
	PlanStatusArchived  PlanStatus = "archived"
	PlanStatusScheduled PlanStatus = "scheduled"
	PlanStatusInvalid   PlanStatus = "invalid"
)

func (PlanStatus) Validate

func (s PlanStatus) Validate() error

func (PlanStatus) Values

func (s PlanStatus) Values() []PlanStatus

type Price

type Price struct {
	// contains filtered or unexported fields
}

func NewPriceFrom

func NewPriceFrom[T FlatPrice | UnitPrice | TieredPrice | DynamicPrice | PackagePrice](v T) *Price

func (*Price) AsDynamic

func (p *Price) AsDynamic() (DynamicPrice, error)

func (*Price) AsFlat

func (p *Price) AsFlat() (FlatPrice, error)

func (*Price) AsPackage

func (p *Price) AsPackage() (PackagePrice, error)

func (*Price) AsTiered

func (p *Price) AsTiered() (TieredPrice, error)

func (*Price) AsUnit

func (p *Price) AsUnit() (UnitPrice, error)

func (*Price) Clone

func (p *Price) Clone() *Price

func (*Price) Equal

func (p *Price) Equal(v *Price) bool

func (*Price) FromDynamic

func (p *Price) FromDynamic(price DynamicPrice)

func (*Price) FromFlat

func (p *Price) FromFlat(price FlatPrice)

func (*Price) FromPackage

func (p *Price) FromPackage(price PackagePrice)

func (*Price) FromTiered

func (p *Price) FromTiered(price TieredPrice)

func (*Price) FromUnit

func (p *Price) FromUnit(price UnitPrice)

func (*Price) GetCommitments

func (p *Price) GetCommitments() Commitments

func (*Price) GetPaymentTerm

func (p *Price) GetPaymentTerm() PaymentTermType

func (Price) MarshalJSON

func (p Price) MarshalJSON() ([]byte, error)

func (*Price) SupportsUnitConfig

func (p *Price) SupportsUnitConfig() bool

SupportsUnitConfig reports whether a unit_config conversion can be applied to this price. unit_config is a per-quantity conversion applied to the raw metered quantity before rating, so it is only meaningful on per-unit prices (unit and the graduated/volume tiered modes). Flat prices are not per-quantity, and package/dynamic prices already carry their own conversion that unit_config would double up on.

func (*Price) Type

func (p *Price) Type() PriceType

func (*Price) UnmarshalJSON

func (p *Price) UnmarshalJSON(bytes []byte) error

func (*Price) Validate

func (p *Price) Validate() error

type PriceTier

type PriceTier struct {
	// UpToAmount defines the quantity will be contained in the tier. Inclusive.
	// If null, the tier is open-ended.
	UpToAmount *decimal.Decimal `json:"upToAmount,omitempty"`

	// FlatPrice defines the flat price component of the tier.
	FlatPrice *PriceTierFlatPrice `json:"flatPrice,omitempty"`

	// UnitPrice defines the unit price component of the tier.
	UnitPrice *PriceTierUnitPrice `json:"unitPrice,omitempty"`
}

PriceTier describes a tier of price(s).

func (PriceTier) Clone

func (p PriceTier) Clone() PriceTier

func (PriceTier) Equal

func (p PriceTier) Equal(v PriceTier) bool

func (PriceTier) Validate

func (p PriceTier) Validate() error

type PriceTierFlatPrice

type PriceTierFlatPrice struct {
	// Amount of the flat price.
	Amount decimal.Decimal `json:"amount"`
}

func (PriceTierFlatPrice) Clone

func (PriceTierFlatPrice) Equal

func (PriceTierFlatPrice) Validate

func (f PriceTierFlatPrice) Validate() error

type PriceTierUnitPrice

type PriceTierUnitPrice struct {
	// Amount of the flat price.
	Amount decimal.Decimal `json:"amount"`
}

func (PriceTierUnitPrice) Clone

func (PriceTierUnitPrice) Equal

func (PriceTierUnitPrice) Validate

func (u PriceTierUnitPrice) Validate() error

type PriceType

type PriceType string
const (
	FlatPriceType    PriceType = "flat"
	UnitPriceType    PriceType = "unit"
	TieredPriceType  PriceType = "tiered"
	DynamicPriceType PriceType = "dynamic"
	PackagePriceType PriceType = "package"
)

func (PriceType) Values

func (p PriceType) Values() []string

type ProRatingConfig

type ProRatingConfig struct {
	// Enabled indicates whether pro-rating is enabled.
	Enabled bool `json:"enabled"`

	// Mode specifies how pro-rating should be calculated.
	Mode ProRatingMode `json:"mode"`
}

ProRatingConfig defines the pro-rating behavior configuration.

func (ProRatingConfig) Equal

Equal returns true if the two ProRatingConfigs are equal.

func (ProRatingConfig) Validate

func (p ProRatingConfig) Validate() error

Validate validates the ProRatingConfig.

type ProRatingMode

type ProRatingMode string
const (
	ProRatingModeProratePrices ProRatingMode = "prorate_prices"
)

func (ProRatingMode) Values

func (m ProRatingMode) Values() []string

type RateCard

type RateCard interface {
	models.Validator
	models.Equaler[RateCard]

	RateCardFeature

	Type() RateCardType
	AsMeta() RateCardMeta
	Key() string
	Merge(RateCard) error
	ChangeMeta(func(m RateCardMeta) (RateCardMeta, error)) error
	Clone() RateCard
	Compatible(RateCard) error
	GetBillingCadence() *datetime.ISODuration
	IsBillable() bool
}

type RateCardFeature

type RateCardFeature interface {
	HasFeature() bool
	GetFeatureID() *string
	GetFeatureKey() *string
	SetFeature(id, key *string)
}

type RateCardMeta

type RateCardMeta struct {
	// Key is the unique key for Plan.
	Key string `json:"key"`

	// Name of the RateCard
	Name string `json:"name"`

	// Description for the RateCard
	Description *string `json:"description,omitempty"`

	// Metadata a set of key/value pairs describing metadata for the RateCard
	Metadata models.Metadata `json:"metadata,omitempty"`

	// FeatureKey is the key of the feature assigned to the RateCard
	FeatureKey *string `json:"featureKey,omitempty"`

	// FeatureID is the ID of the feature assigned to the RateCard
	FeatureID *string `json:"featureID,omitempty"`

	// EntitlementTemplate defines the template used for instantiating entitlement.Entitlement.
	// If Feature is set then template must be provided as well.
	EntitlementTemplate *EntitlementTemplate `json:"entitlementTemplate,omitempty"`

	// TaxConfig defines provider specific tax information.
	TaxConfig *TaxConfig `json:"taxConfig,omitempty"`

	// TaxCode is the tax code assigned to the RateCard. It is only populated if the tax code was eagerly loaded.
	TaxCode *taxcode.TaxCode `json:"taxCode,omitempty"`

	// Price defines the price for the RateCard
	Price *Price `json:"price"`

	// Discounts defines a list of discounts for the RateCard
	Discounts Discounts `json:"discounts,omitempty"`

	// UnitConfig defines an optional per-rate-card conversion applied to the raw
	// metered quantity before pricing and entitlement evaluation. Shared by plan
	// and addon rate cards.
	UnitConfig *UnitConfig `json:"unitConfig,omitempty"`
}

func (RateCardMeta) Clone

func (r RateCardMeta) Clone() RateCardMeta

func (RateCardMeta) Equal

func (r RateCardMeta) Equal(v RateCardMeta) bool

func (RateCardMeta) GetFeatureID

func (r RateCardMeta) GetFeatureID() *string

func (RateCardMeta) GetFeatureKey

func (r RateCardMeta) GetFeatureKey() *string

func (RateCardMeta) HasFeature

func (r RateCardMeta) HasFeature() bool

func (RateCardMeta) IsBillable

func (r RateCardMeta) IsBillable() bool

func (*RateCardMeta) SetFeature

func (r *RateCardMeta) SetFeature(id, key *string)

func (RateCardMeta) Validate

func (r RateCardMeta) Validate() error

func (RateCardMeta) ValidateWith

func (r RateCardMeta) ValidateWith(v ...models.ValidatorFunc[RateCardMeta]) error

type RateCardSerde

type RateCardSerde struct {
	Type RateCardType `json:"type"`
}

type RateCardType

type RateCardType string
const (
	FlatFeeRateCardType    RateCardType = "flat_fee"
	UsageBasedRateCardType RateCardType = "usage_based"
)

func (RateCardType) Values

func (s RateCardType) Values() []string

type RateCardWithOverlay

type RateCardWithOverlay struct {
	// contains filtered or unexported fields
}

func NewRateCardWithOverlay

func NewRateCardWithOverlay(base, overlay RateCard) RateCardWithOverlay

func (RateCardWithOverlay) Validate

func (r RateCardWithOverlay) Validate() error

func (RateCardWithOverlay) ValidateWith

func (r RateCardWithOverlay) ValidateWith(validators ...models.ValidatorFunc[RateCardWithOverlay]) error

type RateCards

type RateCards []RateCard

func (RateCards) At

func (c RateCards) At(idx int) RateCard

func (RateCards) Billables

func (c RateCards) Billables() RateCards

func (RateCards) Clone

func (c RateCards) Clone() RateCards

func (RateCards) Equal

func (c RateCards) Equal(v RateCards) bool

func (RateCards) HasUnitConfig

func (c RateCards) HasUnitConfig() bool

func (RateCards) SingleBillingCadence

func (c RateCards) SingleBillingCadence() bool

SingleBillingCadence returns true if all ratecards in the collection has the same billing cadence.

func (RateCards) Validate

func (c RateCards) Validate() error

func (RateCards) ValidateWith

func (c RateCards) ValidateWith(v ...models.ValidatorFunc[RateCards]) error

type SettlementMode

type SettlementMode string
const (
	CreditThenInvoiceSettlementMode SettlementMode = "credit_then_invoice"
	CreditOnlySettlementMode        SettlementMode = "credit_only"
)

func (SettlementMode) Validate

func (s SettlementMode) Validate() error

func (SettlementMode) Values

func (s SettlementMode) Values() []string

type StaticEntitlementTemplate

type StaticEntitlementTemplate struct {
	// Metadata a set of key/value pairs describing metadata for the RateCard.
	Metadata models.Metadata `json:"metadata,omitempty"`

	// Config stores a JSON parsable configuration for the entitlement.Entitlement.
	// This value is also returned when checking entitlement access, and
	// it is useful for configuring fine-grained access settings to the feature implemented in customers own system.
	Config json.RawMessage `json:"config,omitempty"`
}

func (*StaticEntitlementTemplate) Equal

func (*StaticEntitlementTemplate) Validate

func (t *StaticEntitlementTemplate) Validate() error

type StripeTaxConfig

type StripeTaxConfig struct {
	// Code stores the product tax code.
	// See: https://docs.stripe.com/tax/tax-codes
	// Example:"txcd_10000000"
	Code string `json:"code"`
}

func (StripeTaxConfig) Clone

func (s StripeTaxConfig) Clone() StripeTaxConfig

func (*StripeTaxConfig) Equal

func (s *StripeTaxConfig) Equal(v *StripeTaxConfig) bool

func (*StripeTaxConfig) Validate

func (s *StripeTaxConfig) Validate() error

type TaxBehavior

type TaxBehavior string
const (
	InclusiveTaxBehavior TaxBehavior = "inclusive"
	ExclusiveTaxBehavior TaxBehavior = "exclusive"
)

func (TaxBehavior) Validate

func (t TaxBehavior) Validate() error

func (TaxBehavior) Values

func (t TaxBehavior) Values() []string

type TaxCodeConfig

type TaxCodeConfig struct {
	Behavior  *TaxBehavior `json:"behavior,omitempty"`
	TaxCodeID string       `json:"tax_code_id"`
}

TaxCodeConfig holds a lean reference to a tax code entry — only the FK and the behavior flag. Used in charge intents where provider-specific fields (e.g. Stripe.Code) are not stored and are resolved at invoice snapshot time via BackfillTaxConfig.

func TaxCodeConfigFrom

func TaxCodeConfigFrom(cfg *TaxConfig) TaxCodeConfig

TaxCodeConfigFrom extracts the lean reference fields from a full TaxConfig. Returns the zero value when cfg is nil or when neither Behavior nor TaxCodeID is set (e.g. Stripe-only config).

func (TaxCodeConfig) ToTaxConfig

func (c TaxCodeConfig) ToTaxConfig() TaxConfig

ToTaxConfig converts TaxCodeConfig to TaxConfig (without provider-specific fields).

func (TaxCodeConfig) Validate

func (c TaxCodeConfig) Validate() error

type TaxConfig

type TaxConfig struct {
	Behavior  *TaxBehavior     `json:"behavior,omitempty"`
	Stripe    *StripeTaxConfig `json:"stripe,omitempty"`
	TaxCodeID *string          `json:"tax_code_id,omitempty"`
}

TaxConfig stores the provider-specific tax configs.

func BackfillTaxConfig

func BackfillTaxConfig(cfg *TaxConfig, taxBehavior *TaxBehavior, tc *taxcode.TaxCode) *TaxConfig

BackfillTaxConfig fills in missing legacy TaxConfig fields from the new tax_behavior column and the TaxCode entity's app mappings.

func MergeTaxConfigs

func MergeTaxConfigs(base, overrides *TaxConfig) *TaxConfig

MergeTaxConfigs merges two TaxConfigs with overrides taking precedence.

Stripe and TaxCodeID are two encodings of the same intent-level tax-code identity, so they merge as a unit: a config that overrides only the Stripe code must not inherit the base's (different) TaxCodeID, which would leave the result pointing at two different tax entities.

func (TaxConfig) Clone

func (c TaxConfig) Clone() TaxConfig

func (*TaxConfig) Equal

func (c *TaxConfig) Equal(v *TaxConfig) bool

func (*TaxConfig) Validate

func (c *TaxConfig) Validate() error

type TieredPrice

type TieredPrice struct {
	Commitments `json:",inline"`

	// Mode defines whether the tier is volume-based or graduated.
	// * VolumeTieredPrice: the maximum quantity within a period determines the per-unit price
	// * GraduatedTieredPrice: pricing can change as the quantity grows
	Mode TieredPriceMode `json:"mode"`

	// Tiers defines the list of PriceTier.
	Tiers []PriceTier `json:"tiers"`
}

func (*TieredPrice) Clone

func (t *TieredPrice) Clone() TieredPrice

func (*TieredPrice) Equal

func (t *TieredPrice) Equal(v *TieredPrice) bool

func (*TieredPrice) Validate

func (t *TieredPrice) Validate() error

func (*TieredPrice) WithSortedTiers

func (t *TieredPrice) WithSortedTiers() TieredPrice

type TieredPriceMode

type TieredPriceMode string
const (
	// In `volume`-based tiering, the maximum quantity within a period determines the per unit price.
	VolumeTieredPrice TieredPriceMode = "volume"
	// In `graduated` tiering, pricing can change as the quantity grows.
	GraduatedTieredPrice TieredPriceMode = "graduated"
)

func NewTieredPriceMode

func NewTieredPriceMode(s string) (TieredPriceMode, error)

func (TieredPriceMode) StringValues

func (p TieredPriceMode) StringValues() []string

func (TieredPriceMode) Values

func (p TieredPriceMode) Values() []TieredPriceMode

type UnitConfig

type UnitConfig struct {
	// Operation is the arithmetic conversion to apply: multiply or divide.
	Operation UnitConfigOperation `json:"operation"`

	// ConversionFactor is the positive non-zero factor used by Operation. Full
	// precision is retained; the factor is never capped.
	ConversionFactor decimal.Decimal `json:"conversionFactor"`

	// Rounding controls how the converted quantity is rounded for invoicing.
	// Defaults to "none". Entitlement balance checks always use the unrounded
	// (converted) value, never the rounded one.
	Rounding UnitConfigRoundingMode `json:"rounding,omitempty"`

	// Precision is the number of decimal places retained when rounding. It is
	// only meaningful when Rounding is set to a value other than "none"; it is
	// ignored otherwise. Defaults to 0 (whole units).
	Precision int `json:"precision,omitempty"`

	// DisplayUnit is a human-readable label for the converted unit shown on
	// invoices and the customer portal (e.g. "GB", "hours").
	DisplayUnit *string `json:"displayUnit,omitempty"`
}

UnitConfig transforms a raw metered quantity into a billing quantity before pricing and entitlement evaluation. It is a self-contained domain DTO with no dependency on the persistence layer — persistence maps to/from it.

Rounding and Precision are value types with sensible defaults (no rounding, zero decimal places); only DisplayUnit is optional and pointer-typed.

func (*UnitConfig) Apply

func (c *UnitConfig) Apply(raw decimal.Decimal) (converted, invoiced decimal.Decimal)

Apply transforms a raw metered quantity into the converted (precise) and invoiced (rounded) billing quantities.

  • converted: raw with the operation × conversion_factor applied. Used for entitlement balance checks, which always see the precise value.
  • invoiced: converted with the configured rounding/precision applied. Used as the line quantity at billing time. Equal to converted when no rounding is set.

When c is nil, both return values equal raw. Callers must have already run Validate so the operation and rounding enums are known values; an unrecognized operation or rounding mode falls back to identity rather than panicking mid-billing.

func (UnitConfig) Clone

func (c UnitConfig) Clone() UnitConfig

func (*UnitConfig) Equal

func (c *UnitConfig) Equal(v *UnitConfig) bool

func (*UnitConfig) Validate

func (c *UnitConfig) Validate() error

type UnitConfigOperation

type UnitConfigOperation string
const (
	UnitConfigOperationMultiply UnitConfigOperation = "multiply"
	UnitConfigOperationDivide   UnitConfigOperation = "divide"
)

func (UnitConfigOperation) Validate

func (o UnitConfigOperation) Validate() error

func (UnitConfigOperation) Values

func (o UnitConfigOperation) Values() []string

type UnitConfigRoundingMode

type UnitConfigRoundingMode string
const (
	UnitConfigRoundingModeNone    UnitConfigRoundingMode = "none"
	UnitConfigRoundingModeCeiling UnitConfigRoundingMode = "ceiling"
	UnitConfigRoundingModeFloor   UnitConfigRoundingMode = "floor"
	UnitConfigRoundingModeHalfUp  UnitConfigRoundingMode = "half_up"
)

func (UnitConfigRoundingMode) IsNone

func (r UnitConfigRoundingMode) IsNone() bool

IsNone reports whether the mode applies no rounding. The empty string is the zero value of the field and is treated as the default, "none".

func (UnitConfigRoundingMode) Validate

func (r UnitConfigRoundingMode) Validate() error

func (UnitConfigRoundingMode) Values

func (r UnitConfigRoundingMode) Values() []string

type UnitPrice

type UnitPrice struct {
	Commitments `json:",inline"`

	// Amount of the unit price.
	Amount decimal.Decimal `json:"amount"`
}

func (*UnitPrice) Clone

func (u *UnitPrice) Clone() UnitPrice

func (*UnitPrice) Equal

func (u *UnitPrice) Equal(v *UnitPrice) bool

func (*UnitPrice) Validate

func (u *UnitPrice) Validate() error

type UsageBasedRateCard

type UsageBasedRateCard struct {
	RateCardMeta

	// BillingCadence defines the billing cadence of the RateCard in ISO8601 format.
	// Example: "P1D12H"
	BillingCadence datetime.ISODuration `json:"billingCadence"`
}

func (*UsageBasedRateCard) AsMeta

func (r *UsageBasedRateCard) AsMeta() RateCardMeta

func (*UsageBasedRateCard) ChangeMeta

func (r *UsageBasedRateCard) ChangeMeta(fn func(m RateCardMeta) (RateCardMeta, error)) error

func (*UsageBasedRateCard) Clone

func (r *UsageBasedRateCard) Clone() RateCard

func (*UsageBasedRateCard) Compatible

func (r *UsageBasedRateCard) Compatible(v RateCard) error

func (*UsageBasedRateCard) Equal

func (r *UsageBasedRateCard) Equal(v RateCard) bool

func (*UsageBasedRateCard) GetBillingCadence

func (r *UsageBasedRateCard) GetBillingCadence() *datetime.ISODuration

func (*UsageBasedRateCard) Key

func (r *UsageBasedRateCard) Key() string

func (*UsageBasedRateCard) MarshalJSON

func (r *UsageBasedRateCard) MarshalJSON() ([]byte, error)

func (*UsageBasedRateCard) Merge

func (r *UsageBasedRateCard) Merge(v RateCard) error

func (*UsageBasedRateCard) Type

func (r *UsageBasedRateCard) Type() RateCardType

func (*UsageBasedRateCard) Validate

func (r *UsageBasedRateCard) Validate() error

func (*UsageBasedRateCard) ValidateWith

type UsageDiscount

type UsageDiscount struct {
	Quantity decimal.Decimal `json:"quantity"`
}

func (UsageDiscount) Clone

func (d UsageDiscount) Clone() UsageDiscount

func (UsageDiscount) Hash

func (d UsageDiscount) Hash() hasher.Hash

func (UsageDiscount) Validate

func (d UsageDiscount) Validate() error

func (UsageDiscount) ValidateForPrice

func (d UsageDiscount) ValidateForPrice(price *Price) error

func (UsageDiscount) ValidateWith

func (d UsageDiscount) ValidateWith(v ...models.ValidatorFunc[UsageDiscount]) error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL