Plan creation fails for any non-AWS provider with: invalid payment option: monthly (valid: no-upfront, partial-upfront, all-upfront). internal/config/validation.go:14 defines a single AWS-only ValidPaymentOptions slice; ServiceConfig.validatePayment uses it regardless of provider. After PR #682 the Azure recommendation emitters stamp PaymentOption = upfront/monthly (Azure-canonical, the rest of the Azure code accepts both), but the PLAN validator was missed.
Repro
Create a new Plan with Provider=Azure (or GCP) and any Azure service (vm) → Failed to save plan: validation error: invalid service config 'azure/vm': invalid payment option: monthly (valid: no-upfront, partial-upfront, all-upfront).
Fix direction
Replace the single ValidPaymentOptions slice with a provider-scoped map: aws → {no-upfront, partial-upfront, all-upfront}, azure → {upfront, monthly}, gcp → <confirm GCP CUD payment values>. ServiceConfig.validatePayment looks up the slice via ServiceConfig.Provider. Defense-in-depth: an Azure service config must NOT accept AWS terms. Add tests for (azure, upfront), (azure, monthly), and the cross-provider rejection cases.
Note: internal/api/handler_purchases_guards_test.go:48-54 already treats (azure, monthly) as valid in another layer, confirming the canonical Azure set — the plan validator is the outlier.
Blocks non-AWS plan creation. Surfaced by QA spreadsheet row 4.1.
Plan creation fails for any non-AWS provider with:
invalid payment option: monthly (valid: no-upfront, partial-upfront, all-upfront).internal/config/validation.go:14defines a single AWS-onlyValidPaymentOptionsslice;ServiceConfig.validatePaymentuses it regardless of provider. After PR #682 the Azure recommendation emitters stampPaymentOption = upfront/monthly(Azure-canonical, the rest of the Azure code accepts both), but the PLAN validator was missed.Repro
Create a new Plan with Provider=Azure (or GCP) and any Azure service (vm) →
Failed to save plan: validation error: invalid service config 'azure/vm': invalid payment option: monthly (valid: no-upfront, partial-upfront, all-upfront).Fix direction
Replace the single
ValidPaymentOptionsslice with a provider-scoped map:aws → {no-upfront, partial-upfront, all-upfront},azure → {upfront, monthly},gcp → <confirm GCP CUD payment values>.ServiceConfig.validatePaymentlooks up the slice viaServiceConfig.Provider. Defense-in-depth: an Azure service config must NOT accept AWS terms. Add tests for (azure, upfront), (azure, monthly), and the cross-provider rejection cases.Note:
internal/api/handler_purchases_guards_test.go:48-54already treats(azure, monthly)as valid in another layer, confirming the canonical Azure set — the plan validator is the outlier.Blocks non-AWS plan creation. Surfaced by QA spreadsheet row 4.1.