Skip to main content

Price Plan Matrix

The PricePlanMatrix is the modern, versioned pricing structure in Opencell v12+. It replaces the older single PricePlan object with a multi-layered, versioned structure.

Structure Overview​

PricePlanMatrix (parent container with filtering criteria)
└── PricePlanMatrixVersion (versioned, status: DRAFT/PUBLISHED/CLOSED)
├── validity: DatePeriod (when active)
├── isMatrix: boolean (single price vs matrix)
├── price: BigDecimal (if not matrix)
├── columns: Set<PricePlanMatrixColumn> (matrix dimensions)
└── lines: Set<PricePlanMatrixLine> (matrix rows)
├── priority: int (lower = higher)
├── ratingAccuracy: int (specificity score)
└── values: Set<PricePlanMatrixValue> (cell values)

PricePlanMatrix (Container)​

The parent entity that links to ChargeTemplates and defines filtering criteria.

Key Fields:

FieldTypeDescription
codeStringUnique identifier
chargeTemplatesSetLinked ChargeTemplates (many-to-many)
sellerSellerFilter by seller
offerTemplateOfferTemplateFilter by offer
tradingCountryCountryFilter by country
tradingCurrencyCurrencyFilter by currency
criteria1-3StringCustom criteria (EL expressions)
minQuantityBigDecimalMinimum quantity for this plan
maxQuantityBigDecimalMaximum quantity for this plan
subscriptionDateRangeDatePeriodFilter by subscription date
ratingDateRangeDatePeriodFilter by rating date

Charge Template Linkage:

@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "cat_price_plan_charge",
joinColumns = @JoinColumn(name = "price_plan_id"),
inverseJoinColumns = @JoinColumn(name = "charge_id"))
private Set<ChargeTemplate> chargeTemplates;

PricePlanMatrixVersion​

Versioned pricing with lifecycle status.

Status Lifecycle:

DRAFT → PUBLISHED → CLOSED

Key Fields:

FieldTypeDescription
validityDatePeriodWhen this version is active
statusVersionStatusEnumDRAFT, PUBLISHED, CLOSED
isMatrixbooleanfalse = single price, true = matrix
priceBigDecimalPrice if not matrix
columnsSetMatrix dimensions (if matrix)
linesSetMatrix rows (if matrix)

PricePlanMatrixLine​

Individual price lines within a matrix.

Key Fields:

FieldTypeDescription
priorityintLower = matched first
ratingAccuracyintSpecificity score (0 = default/fallback)
valuesSetColumn values for this line
priceBigDecimalPrice for this line
amountWithoutTaxELStringEL expression for dynamic pricing
amountWithTaxELStringEL expression for dynamic pricing
scriptInstanceScriptInstanceScript for complex pricing

ratingAccuracy Field​

The ratingAccuracy indicates specificity (number of non-null column values):

  • ratingAccuracy=0: Default/fallback line (matches anything)
  • ratingAccuracy=1: Line with 1 specific column value
  • ratingAccuracy=3: Line with 3 specific column values (most specific)

Selection Priority:

  1. priority ASC (explicit priority)
  2. ratingAccuracy DESC (more specific lines preferred)
  3. id ASC (deterministic order)

PricePlanMatrixColumn​

Defines dimensions for matrix pricing.

Column Types (ColumnTypeEnum):

TypeDescription
STRINGExact text match
LIST_TEXTMatch any in list
LIST_MULTIPLE_TEXTMatch all in list
EXPRESSION_LANGUAGEEL evaluation
LONGNumeric equality
LIST_NUMERICNumeric list match
DOUBLEDecimal with precision
Range_DateDate falls within from/to
Range_NumericValue falls within from/to
BooleanBoolean equality

Version Selection During Rating​

The rating service selects the appropriate price through a 3-step process:

Step 1: Find PricePlanMatrix Container​

SELECT ppm FROM PricePlanMatrix ppm
WHERE ppm.chargeTemplates CONTAINS :chargeTemplate
AND (ppm.seller IS NULL OR ppm.seller = :seller)
AND (ppm.offerTemplate IS NULL OR ppm.offerTemplate = :offer)
AND (ppm.tradingCountry IS NULL OR ppm.tradingCountry = :country)
AND (ppm.tradingCurrency IS NULL OR ppm.tradingCurrency = :currency)
AND (:quantity BETWEEN ppm.minQuantity AND ppm.maxQuantity)
ORDER BY ppm.priority ASC

Step 2: Select PUBLISHED Version​

SELECT ppmv FROM PricePlanMatrixVersion ppmv
WHERE ppmv.pricePlanMatrix = :ppm
AND ppmv.status = 'PUBLISHED'
AND :operationDate BETWEEN ppmv.validity.from AND ppmv.validity.to

Step 3: Match Line (if matrix)​

if (version.isMatrix()) {
// Load all lines ordered by priority ASC, ratingAccuracy DESC
List<PricePlanMatrixLine> lines = version.getLines()
.stream()
.sorted(Comparator.comparing(PricePlanMatrixLine::getPriority)
.thenComparing(PricePlanMatrixLine::getRatingAccuracy, Comparator.reverseOrder()))
.collect(Collectors.toList());

for (PricePlanMatrixLine line : lines) {
if (matchesAllColumns(line, ratingContext)) {
return line.getPrice();
}
}

// Fallback to default line (ratingAccuracy=0)
return findDefaultLine(lines).getPrice();
} else {
return version.getPrice();
}

Example Configurations​

Simple Per-Unit Pricing​

{
"pricePlanMatrix": {
"code": "API_CALL_PRICING",
"chargeTemplates": ["API_USAGE_CHARGE"],
"versions": [{
"status": "PUBLISHED",
"validity": { "from": "2024-01-01", "to": null },
"isMatrix": false,
"price": 0.10
}]
}
}

Tiered Pricing (OpenAI-style)​

{
"pricePlanMatrix": {
"code": "TOKEN_TIERED_PRICING",
"chargeTemplates": ["TOKEN_USAGE_CHARGE"],
"versions": [{
"status": "PUBLISHED",
"validity": { "from": "2024-01-01", "to": null },
"isMatrix": true,
"columns": [{
"code": "QUANTITY_TIER",
"type": "Range_Numeric"
}],
"lines": [
{ "priority": 1, "ratingAccuracy": 1, "values": [{"from": 0, "to": 1000000}], "price": 0.002 },
{ "priority": 2, "ratingAccuracy": 1, "values": [{"from": 1000001, "to": 10000000}], "price": 0.0015 },
{ "priority": 3, "ratingAccuracy": 1, "values": [{"from": 10000001, "to": null}], "price": 0.001 }
]
}]
}
}

Multi-Dimensional Matrix​

{
"pricePlanMatrix": {
"code": "ENTERPRISE_MATRIX",
"chargeTemplates": ["ENTERPRISE_CHARGE"],
"versions": [{
"status": "PUBLISHED",
"isMatrix": true,
"columns": [
{ "code": "REGION", "type": "STRING" },
{ "code": "CUSTOMER_TYPE", "type": "STRING" },
{ "code": "PRODUCT_TIER", "type": "STRING" }
],
"lines": [
{ "priority": 1, "ratingAccuracy": 3, "values": ["US", "Enterprise", "Pro"], "price": 500 },
{ "priority": 2, "ratingAccuracy": 2, "values": ["EU", "Government", null], "price": 450 },
{ "priority": 99, "ratingAccuracy": 0, "values": [null, null, null], "price": 600 }
]
}]
}
}