Skip to content

Commit 7d25b83

Browse files
committed
fix: rate card reject unit config
1 parent c7cfea6 commit 7d25b83

2 files changed

Lines changed: 33 additions & 0 deletions

File tree

api/v3/handlers/plans/convert.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,14 @@ func FromAPIBillingPlanPhase(p api.BillingPlanPhase) (productcatalog.Phase, erro
527527
}
528528

529529
func FromAPIBillingRateCard(rc api.BillingRateCard) (productcatalog.RateCard, error) {
530+
// unit_config is read-only: it is synthesized from v1 dynamic and package
531+
// prices on the response path. Accepting it on create/update would silently
532+
// drop the original v1 price semantics, so reject it explicitly until v3
533+
// authoring is designed.
534+
if rc.UnitConfig != nil {
535+
return nil, models.NewGenericValidationError(fmt.Errorf("unit_config is not accepted on create or update for rate card %q", rc.Key))
536+
}
537+
530538
priceType, err := rc.Price.Discriminator()
531539
if err != nil {
532540
return nil, fmt.Errorf("failed to read price type: %w", err)

api/v3/handlers/plans/convert_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1300,6 +1300,31 @@ func TestToRateCard(t *testing.T) {
13001300
require.NoError(t, err)
13011301
assert.Equal(t, productcatalog.VolumeTieredPrice, tiered.Mode)
13021302
})
1303+
1304+
t.Run("rejects unit_config on create/update", func(t *testing.T) {
1305+
var price api.BillingPrice
1306+
require.NoError(t, price.FromBillingPriceUnit(api.BillingPriceUnit{Amount: "1", Type: "unit"}))
1307+
1308+
bc := api.ISO8601Duration("P1M")
1309+
1310+
rc := api.BillingRateCard{
1311+
Key: "tokens",
1312+
Name: "Tokens",
1313+
Price: price,
1314+
BillingCadence: &bc,
1315+
UnitConfig: &api.BillingUnitConfig{
1316+
Operation: api.BillingUnitConfigOperationMultiply,
1317+
ConversionFactor: "1.2",
1318+
},
1319+
}
1320+
1321+
_, err := FromAPIBillingRateCard(rc)
1322+
require.Error(t, err)
1323+
var validationErr *models.GenericValidationError
1324+
assert.ErrorAs(t, err, &validationErr)
1325+
assert.Contains(t, err.Error(), "unit_config")
1326+
assert.Contains(t, err.Error(), "tokens")
1327+
})
13031328
}
13041329

13051330
func TestToBillingPrice(t *testing.T) {

0 commit comments

Comments
 (0)