From e3d0872ea4e04713ba89abf580b8e64741ce5a8a Mon Sep 17 00:00:00 2001 From: Peter Turi Date: Tue, 27 Oct 2026 13:52:25 +0100 Subject: [PATCH 1/6] feat(api): add customer charge invoicing action --- .../packages/aip-client-javascript/README.md | 1 + .../src/funcs/customers.ts | 52 + .../aip-client-javascript/src/index.ts | 15 +- .../src/models/operations/customers.ts | 9 + .../src/models/schemas.ts | 508 +++++---- .../aip-client-javascript/src/models/types.ts | 219 ++-- .../src/sdk/customers.ts | 23 + .../aip/src/customers/charges/operations.tsp | 43 + .../typespec-go/src/components/GoResource.tsx | 5 +- .../typespec-go/test/resource-render.test.ts | 23 + api/v3/api.gen.go | 1006 +++++++++-------- api/v3/client/README.md | 1 + api/v3/client/customers_charges.go | 29 + api/v3/client/models_customers.go | 9 + api/v3/client/models_invoices.go | 448 -------- api/v3/client/models_shared.go | 447 ++++++++ api/v3/handlers/billinginvoices/handler.go | 1 + .../billinginvoices/invoice_charges.go | 86 ++ .../billinginvoices/invoice_charges_test.go | 198 ++++ api/v3/openapi.yaml | 58 + api/v3/server/routes.go | 4 + api/v3/test/charges_test.go | 38 + .../charges/service/pendinglines_test.go | 115 ++ 23 files changed, 2072 insertions(+), 1266 deletions(-) create mode 100644 api/v3/handlers/billinginvoices/invoice_charges.go create mode 100644 api/v3/handlers/billinginvoices/invoice_charges_test.go diff --git a/api/spec/packages/aip-client-javascript/README.md b/api/spec/packages/aip-client-javascript/README.md index d3031a3577..e6bac49ee3 100644 --- a/api/spec/packages/aip-client-javascript/README.md +++ b/api/spec/packages/aip-client-javascript/README.md @@ -316,6 +316,7 @@ The full call path, HTTP route, and a short description are listed below. | `client.customers.credits.transactions.list` | `GET /openmeter/customers/{customerId}/credits/transactions` | List credit transactions for a customer. Returns an immutable, chronological record of credit movements: funded credits and consumed credits. Transactions are returned in reverse chronological order by default. | | `client.customers.charges.list` | `GET /openmeter/customers/{customerId}/charges` | List customer charges. Returns the customer's charges that are represented as either flat fee or usage-based charges. | | `client.customers.charges.create` | `POST /openmeter/customers/{customerId}/charges` | Create customer charge. | +| `client.customers.charges.invoice` | `POST /openmeter/customers/{customerId}/charges/invoice` | Invoice a customer's charges outside the normal billing cycle. Collection alignment is bypassed. The customer's progressive billing setting applies unless explicitly overridden. All eligible pending lines are considered. Returns the created standard invoices, one per collected currency. Lines blocked by their owning engine remain pending; if all candidates are blocked, the response is empty. If no lines are eligible, returns a validation error. | ### Entitlements diff --git a/api/spec/packages/aip-client-javascript/src/funcs/customers.ts b/api/spec/packages/aip-client-javascript/src/funcs/customers.ts index 8e12027bf9..4e1dcb0269 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/customers.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/customers.ts @@ -73,6 +73,8 @@ import type { ListCustomerChargesResponse, CreateCustomerChargesRequest, CreateCustomerChargesResponse, + InvoiceCustomerChargesRequest, + InvoiceCustomerChargesResponse, } from '../models/operations/customers.js' /** @@ -1607,3 +1609,53 @@ export function createCustomerCharges( }) }) } + +/** + * Invoice a customer's charges + * + * Invoice a customer's charges outside the normal billing cycle. Collection + * alignment is bypassed. The customer's progressive billing setting applies unless + * explicitly overridden. All eligible pending lines are considered. + * + * Returns the created standard invoices, one per collected currency. Lines blocked + * by their owning engine remain pending; if all candidates are blocked, the + * response is empty. If no lines are eligible, returns a validation error. + * + * POST /openmeter/customers/{customerId}/charges/invoice + */ +export function invoiceCustomerCharges( + client: Client, + req: InvoiceCustomerChargesRequest, + options?: RequestOptions, +): Promise> { + return request(() => { + const pathParamsInput = { + customerId: req.customerId, + } + const pathParams = client._options.validate + ? toPathWire(pathParamsInput, schemas.invoiceCustomerChargesPathParams) + : pathParamsInput + if (client._options.validate) { + assertValid(schemas.invoiceCustomerChargesPathParamsWire, pathParams) + } + const path = `openmeter/customers/${(() => { + if (pathParams.customerId === undefined) { + throw new Error('missing path parameter: customerId') + } + return encodeURIComponent(String(pathParams.customerId)) + })()}/charges/invoice` + const body = toWire(req.body, schemas.invoiceCustomerChargesBody) + if (client._options.validate) { + assertValid(schemas.invoiceCustomerChargesBodyWire, body) + } + return http(client) + .post(path, { ...options, json: body }) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.invoiceCustomerChargesResponseWire, data) + } + return fromWire(data, schemas.invoiceCustomerChargesResponse) + }) + }) +} diff --git a/api/spec/packages/aip-client-javascript/src/index.ts b/api/spec/packages/aip-client-javascript/src/index.ts index 7cb416fc5c..cfc28dec2f 100644 --- a/api/spec/packages/aip-client-javascript/src/index.ts +++ b/api/spec/packages/aip-client-javascript/src/index.ts @@ -91,6 +91,7 @@ export type { WorkflowPaymentChargeAutomaticallySettings, WorkflowPaymentSendInvoiceSettings, ChargeFlatFeeDiscounts, + InvoiceLineExternalReferences, SubscriptionEditRemoveItem, SubscriptionEditUnscheduleEdit, UpdateLabels, @@ -99,7 +100,6 @@ export type { InstallAppSandbox, InstallAppExternalInvoicing, WorkflowCollectionAlignmentSubscription, - InvoiceLineExternalReferences, UpdateBillingInvoiceWorkflowInvoicingSettings, UpdateBillingWorkflowPaymentChargeAutomaticallySettings, UpdateBillingWorkflowPaymentSendInvoiceSettings, @@ -166,8 +166,8 @@ export type { SubscriptionReference, AppReference, ChargeRealizationInvoiceReference, - AddonReference, ChargeReference, + AddonReference, UpdateResourceReference, NotificationChannelReference, NotificationEventFeatureReference, @@ -179,6 +179,7 @@ export type { AppStripeCreateCustomerPortalSessionResult, ClosedPeriod, ChargeResolvedCostBasis, + InvoiceCustomerChargesInput, SubscriptionAddonTimelineSegment, UpdateClosedPeriod, CostBasis, @@ -238,6 +239,9 @@ export type { UpdateBillingPartyTaxIdentity, InvoiceAvailableActions, ChargeRealizationPayment, + InvoiceLineAmountDiscount, + InvoiceLineUsageDiscount, + InvoiceLineBaseDiscount, SubscriptionEditRemovePhase, TaxCodeAppMapping, AppCapability, @@ -246,9 +250,6 @@ export type { UpdateAppSandboxRequest, UpdateAppExternalInvoicingRequest, WorkflowInvoicingSettings, - InvoiceLineAmountDiscount, - InvoiceLineUsageDiscount, - InvoiceLineBaseDiscount, ListCurrenciesParamsFilter, EntitlementAccessQueryRequest, EntitlementFeatureAccessReason, @@ -342,12 +343,12 @@ export type { SubscriptionBase, InvoiceStatusDetails, InvoiceWorkflow, + InvoiceLineDiscounts, SubscriptionCancel, SubscriptionMigrate, SubscriptionAddonUpdate, TaxCode, AppCatalogItem, - InvoiceLineDiscounts, UpdateBillingInvoiceWorkflow, EntitlementFeatureAccess, NotificationChannelPagePaginatedResponse, @@ -386,6 +387,7 @@ export type { CreateEntitlementMeteredRequest, EntitlementGrantPagePaginatedResponse, InvoiceWorkflowSettings, + InvoiceDetailedLine, TaxCodePagePaginatedResponse, AppStripe, AppSandbox, @@ -394,7 +396,6 @@ export type { InstalledAppStripe, InstalledAppSandbox, InstalledAppExternalInvoicing, - InvoiceDetailedLine, UpdateInvoiceWorkflowSettings, EntitlementAccessQueryResult, NotificationEventBalanceThresholdPayload, diff --git a/api/spec/packages/aip-client-javascript/src/models/operations/customers.ts b/api/spec/packages/aip-client-javascript/src/models/operations/customers.ts index 5ee8491074..32c0743785 100644 --- a/api/spec/packages/aip-client-javascript/src/models/operations/customers.ts +++ b/api/spec/packages/aip-client-javascript/src/models/operations/customers.ts @@ -34,6 +34,7 @@ import type { EntitlementHistoryWindowSize, EntitlementPagePaginatedResponse, GetCreditBalanceParamsFilter, + InvoiceCustomerChargesInput, ListCreditGrantsParamsFilter, ListCreditTransactionsParamsFilter, ListCustomerChargesParamsFilter, @@ -391,3 +392,11 @@ export type CreateCustomerChargesRequest = AcceptDateStrings<{ body: CreateChargeRequestInput }> export type CreateCustomerChargesResponse = Charge + +export type InvoiceCustomerChargesRequest = AcceptDateStrings<{ + customerId: string + body: InvoiceCustomerChargesInput +}> +export type InvoiceCustomerChargesResponse = z.output< + typeof schemas.invoiceCustomerChargesResponse +> diff --git a/api/spec/packages/aip-client-javascript/src/models/schemas.ts b/api/spec/packages/aip-client-javascript/src/models/schemas.ts index 65d9224d33..b40f5a6282 100644 --- a/api/spec/packages/aip-client-javascript/src/models/schemas.ts +++ b/api/spec/packages/aip-client-javascript/src/models/schemas.ts @@ -983,6 +983,26 @@ export const featureLlmTokenType = z ]) .describe('Token type for LLM cost lookup.') +export const invoiceDiscountReason = z + .enum(['maximum_spend', 'ratecard_percentage', 'ratecard_usage']) + .describe('The reason a discount was applied to an invoice line.') + +export const invoiceLineExternalReferences = z + .object({ + invoicingId: z + .string() + .optional() + .describe('The ID assigned by the external invoicing app.'), + }) + + .describe( + 'External identifiers for an invoice line item assigned by third-party systems.', + ) + +export const invoiceDetailedLineCostCategory = z + .enum(['regular', 'commitment']) + .describe('Cost category of a detailed invoice line item.') + export const subscriptionCreateTimingEnum = z .enum(['immediate']) @@ -1127,26 +1147,6 @@ export const workflowInvoicingSubscriptionEndProrationMode = z .enum(['bill_full_period', 'bill_actual_period']) .describe('Billing workflow subscription end proration mode.') -export const invoiceDiscountReason = z - .enum(['maximum_spend', 'ratecard_percentage', 'ratecard_usage']) - .describe('The reason a discount was applied to an invoice line.') - -export const invoiceLineExternalReferences = z - .object({ - invoicingId: z - .string() - .optional() - .describe('The ID assigned by the external invoicing app.'), - }) - - .describe( - 'External identifiers for an invoice line item assigned by third-party systems.', - ) - -export const invoiceDetailedLineCostCategory = z - .enum(['regular', 'commitment']) - .describe('Cost category of a detailed invoice line item.') - export const updateBillingInvoiceWorkflowInvoicingSettings = z .object({ autoAdvance: z @@ -2320,17 +2320,17 @@ export const chargeRealizationInvoiceReference = z }) .describe('ChargeRealizationInvoice reference.') -export const addonReference = z +export const chargeReference = z .object({ id: ulid, }) - .describe('Addon reference.') + .describe('Reference to a charge associated with an invoice line.') -export const chargeReference = z +export const addonReference = z .object({ id: ulid, }) - .describe('Reference to a charge associated with an invoice line.') + .describe('Addon reference.') export const updateResourceReference = z .object({ @@ -2570,6 +2570,22 @@ export const chargeResolvedCostBasis = z 'Fiat conversion rate a custom-currency charge is invoiced at. Present once the cost basis is resolved; dynamic cost bases are exposed only after the service period has started.', ) +export const invoiceCustomerChargesInput = z + .object({ + asOf: dateTime.optional(), + progressiveBillingOverride: z + .boolean() + .optional() + + .describe( + "Override the customer's progressive billing setting for this collection. When enabled, eligible usage-based lines can be collected for a partial period.", + ), + }) + + .describe( + "Options for manually collecting a customer's pending lines into invoices.", + ) + export const subscriptionAddonTimelineSegment = z .object({ activeFrom: dateTime, @@ -3514,6 +3530,44 @@ export const chargeRealizationPayment = z }) .describe('Payment state of a charge realization.') +export const invoiceLineAmountDiscount = z + .object({ + id: ulid, + reason: invoiceDiscountReason, + description: z + .string() + .optional() + .describe('Optional human-readable description of the discount.'), + externalReferences: invoiceLineExternalReferences.optional(), + amount: numeric, + }) + .describe('A monetary amount discount applied to an invoice line item.') + +export const invoiceLineUsageDiscount = z + .object({ + id: ulid, + reason: invoiceDiscountReason, + description: z + .string() + .optional() + .describe('Optional human-readable description of the discount.'), + externalReferences: invoiceLineExternalReferences.optional(), + quantity: numeric, + }) + .describe('A usage quantity discount applied to an invoice line item.') + +export const invoiceLineBaseDiscount = z + .object({ + id: ulid, + reason: invoiceDiscountReason, + description: z + .string() + .optional() + .describe('Optional human-readable description of the discount.'), + externalReferences: invoiceLineExternalReferences.optional(), + }) + .describe('Base fields shared by all invoice line item discounts.') + export const subscriptionCreateTiming = z .union([subscriptionCreateTimingEnum, dateTime]) @@ -3680,44 +3734,6 @@ export const workflowInvoicingSettings = z }) .describe('Invoice settings for a billing workflow.') -export const invoiceLineAmountDiscount = z - .object({ - id: ulid, - reason: invoiceDiscountReason, - description: z - .string() - .optional() - .describe('Optional human-readable description of the discount.'), - externalReferences: invoiceLineExternalReferences.optional(), - amount: numeric, - }) - .describe('A monetary amount discount applied to an invoice line item.') - -export const invoiceLineUsageDiscount = z - .object({ - id: ulid, - reason: invoiceDiscountReason, - description: z - .string() - .optional() - .describe('Optional human-readable description of the discount.'), - externalReferences: invoiceLineExternalReferences.optional(), - quantity: numeric, - }) - .describe('A usage quantity discount applied to an invoice line item.') - -export const invoiceLineBaseDiscount = z - .object({ - id: ulid, - reason: invoiceDiscountReason, - description: z - .string() - .optional() - .describe('Optional human-readable description of the discount.'), - externalReferences: invoiceLineExternalReferences.optional(), - }) - .describe('Base fields shared by all invoice line item discounts.') - export const updateBillingWorkflowPaymentSettings = z .discriminatedUnion('collectionMethod', [ updateBillingWorkflowPaymentChargeAutomaticallySettings, @@ -5552,6 +5568,22 @@ export const invoiceWorkflow = z 'Invoice-level snapshot of the workflow configuration. Contains only the settings that are meaningful for an already-created invoice: invoicing behaviour and payment settings. Collection alignment and tax policy are gather-time / profile-wide concerns and are not included.', ) +export const invoiceLineDiscounts = z + .object({ + amount: z + .array(invoiceLineAmountDiscount) + .optional() + + .describe( + 'Monetary amount discounts (e.g. from maximum spend commitments).', + ), + usage: z + .array(invoiceLineUsageDiscount) + .optional() + .describe('Usage quantity discounts (e.g. free tier usage allowances).'), + }) + .describe('Discounts applied to an invoice line item.') + export const subscriptionCancel = z .object({ timing: subscriptionEditTiming.optional().default('immediate'), @@ -5713,22 +5745,6 @@ export const updateAppRequest = z ]) .describe('Request to update an installed app.') -export const invoiceLineDiscounts = z - .object({ - amount: z - .array(invoiceLineAmountDiscount) - .optional() - - .describe( - 'Monetary amount discounts (e.g. from maximum spend commitments).', - ), - usage: z - .array(invoiceLineUsageDiscount) - .optional() - .describe('Usage quantity discounts (e.g. free tier usage allowances).'), - }) - .describe('Discounts applied to an invoice line item.') - export const updateBillingInvoiceWorkflow = z .object({ invoicing: updateBillingInvoiceWorkflowInvoicingSettings.optional(), @@ -6445,6 +6461,43 @@ export const invoiceWorkflowSettings = z 'Snapshot of the billing workflow configuration captured at invoice creation.', ) +export const invoiceDetailedLine = z + .object({ + id: ulid, + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labels.optional(), + createdAt: dateTime, + updatedAt: dateTime, + deletedAt: dateTime.optional(), + servicePeriod: closedPeriod, + totals: totals, + category: invoiceDetailedLineCostCategory.default('regular'), + discounts: invoiceLineDiscounts.optional(), + creditsApplied: z + .array(invoiceLineCreditsApplied) + .optional() + .describe('Credit applied to this detailed line.'), + externalReferences: invoiceLineExternalReferences.optional(), + quantity: numeric, + unitPrice: numeric, + }) + + .describe( + 'A detailed (child) sub-line belonging to a parent invoice line. Detailed lines represent the individual flat-fee components that make up a usage-based parent line after quantity snapshotting.', + ) + export const taxCodePagePaginatedResponse = z .object({ data: z.array(taxCode), @@ -6729,43 +6782,6 @@ export const installedAppExternalInvoicing = z }) .describe('Response of an installed app.') -export const invoiceDetailedLine = z - .object({ - id: ulid, - name: z - .string() - .min(1) - .max(256) - .describe('Display name of the resource. Between 1 and 256 characters.'), - description: z - .string() - .max(1024) - .optional() - - .describe( - 'Optional description of the resource. Maximum 1024 characters.', - ), - labels: labels.optional(), - createdAt: dateTime, - updatedAt: dateTime, - deletedAt: dateTime.optional(), - servicePeriod: closedPeriod, - totals: totals, - category: invoiceDetailedLineCostCategory.default('regular'), - discounts: invoiceLineDiscounts.optional(), - creditsApplied: z - .array(invoiceLineCreditsApplied) - .optional() - .describe('Credit applied to this detailed line.'), - externalReferences: invoiceLineExternalReferences.optional(), - quantity: numeric, - unitPrice: numeric, - }) - - .describe( - 'A detailed (child) sub-line belonging to a parent invoice line. Detailed lines represent the individual flat-fee components that make up a usage-based parent line after quantity snapshotting.', - ) - export const updateInvoiceWorkflowSettings = z .object({ workflow: updateBillingInvoiceWorkflow, @@ -8970,6 +8986,14 @@ export const createCustomerChargesBody = createChargeRequest export const createCustomerChargesResponse = charge +export const invoiceCustomerChargesPathParams = z.object({ + customerId: ulid, +}) + +export const invoiceCustomerChargesBody = invoiceCustomerChargesInput + +export const invoiceCustomerChargesResponse = z.array(invoice) + export const createSubscriptionBody = subscriptionCreate export const createSubscriptionResponse = subscription @@ -10815,6 +10839,26 @@ export const featureLlmTokenTypeWire = z ]) .describe('Token type for LLM cost lookup.') +export const invoiceDiscountReasonWire = z + .enum(['maximum_spend', 'ratecard_percentage', 'ratecard_usage']) + .describe('The reason a discount was applied to an invoice line.') + +export const invoiceLineExternalReferencesWire = z + .strictObject({ + invoicing_id: z + .string() + .optional() + .describe('The ID assigned by the external invoicing app.'), + }) + + .describe( + 'External identifiers for an invoice line item assigned by third-party systems.', + ) + +export const invoiceDetailedLineCostCategoryWire = z + .enum(['regular', 'commitment']) + .describe('Cost category of a detailed invoice line item.') + export const subscriptionCreateTimingEnumWire = z .enum(['immediate']) @@ -10959,26 +11003,6 @@ export const workflowInvoicingSubscriptionEndProrationModeWire = z .enum(['bill_full_period', 'bill_actual_period']) .describe('Billing workflow subscription end proration mode.') -export const invoiceDiscountReasonWire = z - .enum(['maximum_spend', 'ratecard_percentage', 'ratecard_usage']) - .describe('The reason a discount was applied to an invoice line.') - -export const invoiceLineExternalReferencesWire = z - .strictObject({ - invoicing_id: z - .string() - .optional() - .describe('The ID assigned by the external invoicing app.'), - }) - - .describe( - 'External identifiers for an invoice line item assigned by third-party systems.', - ) - -export const invoiceDetailedLineCostCategoryWire = z - .enum(['regular', 'commitment']) - .describe('Cost category of a detailed invoice line item.') - export const updateBillingInvoiceWorkflowInvoicingSettingsWire = z .strictObject({ auto_advance: z @@ -12148,17 +12172,17 @@ export const chargeRealizationInvoiceReferenceWire = z }) .describe('ChargeRealizationInvoice reference.') -export const addonReferenceWire = z +export const chargeReferenceWire = z .strictObject({ id: ulidWire, }) - .describe('Addon reference.') + .describe('Reference to a charge associated with an invoice line.') -export const chargeReferenceWire = z +export const addonReferenceWire = z .strictObject({ id: ulidWire, }) - .describe('Reference to a charge associated with an invoice line.') + .describe('Addon reference.') export const updateResourceReferenceWire = z .strictObject({ @@ -12396,6 +12420,22 @@ export const chargeResolvedCostBasisWire = z 'Fiat conversion rate a custom-currency charge is invoiced at. Present once the cost basis is resolved; dynamic cost bases are exposed only after the service period has started.', ) +export const invoiceCustomerChargesInputWire = z + .strictObject({ + as_of: dateTimeWire.optional(), + progressive_billing_override: z + .boolean() + .optional() + + .describe( + "Override the customer's progressive billing setting for this collection. When enabled, eligible usage-based lines can be collected for a partial period.", + ), + }) + + .describe( + "Options for manually collecting a customer's pending lines into invoices.", + ) + export const subscriptionAddonTimelineSegmentWire = z .strictObject({ active_from: dateTimeWire, @@ -13332,6 +13372,44 @@ export const chargeRealizationPaymentWire = z }) .describe('Payment state of a charge realization.') +export const invoiceLineAmountDiscountWire = z + .strictObject({ + id: ulidWire, + reason: invoiceDiscountReasonWire, + description: z + .string() + .optional() + .describe('Optional human-readable description of the discount.'), + external_references: invoiceLineExternalReferencesWire.optional(), + amount: numericWire, + }) + .describe('A monetary amount discount applied to an invoice line item.') + +export const invoiceLineUsageDiscountWire = z + .strictObject({ + id: ulidWire, + reason: invoiceDiscountReasonWire, + description: z + .string() + .optional() + .describe('Optional human-readable description of the discount.'), + external_references: invoiceLineExternalReferencesWire.optional(), + quantity: numericWire, + }) + .describe('A usage quantity discount applied to an invoice line item.') + +export const invoiceLineBaseDiscountWire = z + .strictObject({ + id: ulidWire, + reason: invoiceDiscountReasonWire, + description: z + .string() + .optional() + .describe('Optional human-readable description of the discount.'), + external_references: invoiceLineExternalReferencesWire.optional(), + }) + .describe('Base fields shared by all invoice line item discounts.') + export const subscriptionCreateTimingWire = z .union([subscriptionCreateTimingEnumWire, dateTimeWire]) @@ -13496,44 +13574,6 @@ export const workflowInvoicingSettingsWire = z }) .describe('Invoice settings for a billing workflow.') -export const invoiceLineAmountDiscountWire = z - .strictObject({ - id: ulidWire, - reason: invoiceDiscountReasonWire, - description: z - .string() - .optional() - .describe('Optional human-readable description of the discount.'), - external_references: invoiceLineExternalReferencesWire.optional(), - amount: numericWire, - }) - .describe('A monetary amount discount applied to an invoice line item.') - -export const invoiceLineUsageDiscountWire = z - .strictObject({ - id: ulidWire, - reason: invoiceDiscountReasonWire, - description: z - .string() - .optional() - .describe('Optional human-readable description of the discount.'), - external_references: invoiceLineExternalReferencesWire.optional(), - quantity: numericWire, - }) - .describe('A usage quantity discount applied to an invoice line item.') - -export const invoiceLineBaseDiscountWire = z - .strictObject({ - id: ulidWire, - reason: invoiceDiscountReasonWire, - description: z - .string() - .optional() - .describe('Optional human-readable description of the discount.'), - external_references: invoiceLineExternalReferencesWire.optional(), - }) - .describe('Base fields shared by all invoice line item discounts.') - export const updateBillingWorkflowPaymentSettingsWire = z .discriminatedUnion('collection_method', [ updateBillingWorkflowPaymentChargeAutomaticallySettingsWire, @@ -15355,6 +15395,22 @@ export const invoiceWorkflowWire = z 'Invoice-level snapshot of the workflow configuration. Contains only the settings that are meaningful for an already-created invoice: invoicing behaviour and payment settings. Collection alignment and tax policy are gather-time / profile-wide concerns and are not included.', ) +export const invoiceLineDiscountsWire = z + .strictObject({ + amount: z + .array(invoiceLineAmountDiscountWire) + .optional() + + .describe( + 'Monetary amount discounts (e.g. from maximum spend commitments).', + ), + usage: z + .array(invoiceLineUsageDiscountWire) + .optional() + .describe('Usage quantity discounts (e.g. free tier usage allowances).'), + }) + .describe('Discounts applied to an invoice line item.') + export const subscriptionCancelWire = z .strictObject({ timing: subscriptionEditTimingWire.optional(), @@ -15518,22 +15574,6 @@ export const updateAppRequestWire = z ]) .describe('Request to update an installed app.') -export const invoiceLineDiscountsWire = z - .strictObject({ - amount: z - .array(invoiceLineAmountDiscountWire) - .optional() - - .describe( - 'Monetary amount discounts (e.g. from maximum spend commitments).', - ), - usage: z - .array(invoiceLineUsageDiscountWire) - .optional() - .describe('Usage quantity discounts (e.g. free tier usage allowances).'), - }) - .describe('Discounts applied to an invoice line item.') - export const updateBillingInvoiceWorkflowWire = z .strictObject({ invoicing: updateBillingInvoiceWorkflowInvoicingSettingsWire.optional(), @@ -16243,6 +16283,43 @@ export const invoiceWorkflowSettingsWire = z 'Snapshot of the billing workflow configuration captured at invoice creation.', ) +export const invoiceDetailedLineWire = z + .strictObject({ + id: ulidWire, + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + created_at: dateTimeWire, + updated_at: dateTimeWire, + deleted_at: dateTimeWire.optional(), + service_period: closedPeriodWire, + totals: totalsWire, + category: invoiceDetailedLineCostCategoryWire, + discounts: invoiceLineDiscountsWire.optional(), + credits_applied: z + .array(invoiceLineCreditsAppliedWire) + .optional() + .describe('Credit applied to this detailed line.'), + external_references: invoiceLineExternalReferencesWire.optional(), + quantity: numericWire, + unit_price: numericWire, + }) + + .describe( + 'A detailed (child) sub-line belonging to a parent invoice line. Detailed lines represent the individual flat-fee components that make up a usage-based parent line after quantity snapshotting.', + ) + export const taxCodePagePaginatedResponseWire = z .strictObject({ data: z.array(taxCodeWire), @@ -16527,43 +16604,6 @@ export const installedAppExternalInvoicingWire = z }) .describe('Response of an installed app.') -export const invoiceDetailedLineWire = z - .strictObject({ - id: ulidWire, - name: z - .string() - .min(1) - .max(256) - .describe('Display name of the resource. Between 1 and 256 characters.'), - description: z - .string() - .max(1024) - .optional() - - .describe( - 'Optional description of the resource. Maximum 1024 characters.', - ), - labels: labelsWire.optional(), - created_at: dateTimeWire, - updated_at: dateTimeWire, - deleted_at: dateTimeWire.optional(), - service_period: closedPeriodWire, - totals: totalsWire, - category: invoiceDetailedLineCostCategoryWire, - discounts: invoiceLineDiscountsWire.optional(), - credits_applied: z - .array(invoiceLineCreditsAppliedWire) - .optional() - .describe('Credit applied to this detailed line.'), - external_references: invoiceLineExternalReferencesWire.optional(), - quantity: numericWire, - unit_price: numericWire, - }) - - .describe( - 'A detailed (child) sub-line belonging to a parent invoice line. Detailed lines represent the individual flat-fee components that make up a usage-based parent line after quantity snapshotting.', - ) - export const updateInvoiceWorkflowSettingsWire = z .strictObject({ workflow: updateBillingInvoiceWorkflowWire, @@ -18826,6 +18866,14 @@ export const createCustomerChargesBodyWire = createChargeRequestWire export const createCustomerChargesResponseWire = chargeWire +export const invoiceCustomerChargesPathParamsWire = z.object({ + customerId: ulidWire, +}) + +export const invoiceCustomerChargesBodyWire = invoiceCustomerChargesInputWire + +export const invoiceCustomerChargesResponseWire = z.array(invoiceWire) + export const createSubscriptionBodyWire = subscriptionCreateWire export const createSubscriptionResponseWire = subscriptionWire diff --git a/api/spec/packages/aip-client-javascript/src/models/types.ts b/api/spec/packages/aip-client-javascript/src/models/types.ts index 198f35fefa..8847cb2e0f 100644 --- a/api/spec/packages/aip-client-javascript/src/models/types.ts +++ b/api/spec/packages/aip-client-javascript/src/models/types.ts @@ -276,6 +276,12 @@ export interface ChargeFlatFeeDiscounts { percentage?: number } +/** External identifiers for an invoice line item assigned by third-party systems. */ +export interface InvoiceLineExternalReferences { + /** The ID assigned by the external invoicing app. */ + invoicingId?: string +} + /** Remove a rate card from a phase. */ export interface SubscriptionEditRemoveItem { /** Discriminator for the remove-item operation. */ @@ -363,12 +369,6 @@ export interface WorkflowCollectionAlignmentSubscription { type: 'subscription' } -/** External identifiers for an invoice line item assigned by third-party systems. */ -export interface InvoiceLineExternalReferences { - /** The ID assigned by the external invoicing app. */ - invoicingId?: string -} - /** * Invoice-level invoicing settings. * @@ -1133,17 +1133,17 @@ export interface ChargeRealizationInvoiceReference { id: string } -/** Addon reference. */ -export interface AddonReference { - id: string -} - /** Reference to a charge associated with an invoice line. */ export interface ChargeReference { /** Unique identifier for the charge. */ id: string } +/** Addon reference. */ +export interface AddonReference { + id: string +} + /** TaxCode reference. */ export interface UpdateResourceReference { id: string @@ -1370,6 +1370,17 @@ export interface ChargeResolvedCostBasis { resolvedAt: Date } +/** Options for manually collecting a customer's pending lines into invoices. */ +export interface InvoiceCustomerChargesInput { + /** Collection cutoff. Defaults to the current time and cannot be in the future. */ + asOf?: Date + /** + * Override the customer's progressive billing setting for this collection. When + * enabled, eligible usage-based lines can be collected for a partial period. + */ + progressiveBillingOverride?: boolean +} + /** A subscription add-on event. */ export interface SubscriptionAddonTimelineSegment { /** An ISO-8601 timestamp representation of the cadence start of the resource. */ @@ -2335,6 +2346,46 @@ export interface ChargeRealizationPayment { status: 'authorized' | 'settled' } +/** A monetary amount discount applied to an invoice line item. */ +export interface InvoiceLineAmountDiscount { + /** Unique identifier for the discount. */ + id: string + /** The reason this discount was applied. */ + reason: 'maximum_spend' | 'ratecard_percentage' | 'ratecard_usage' + /** Optional human-readable description of the discount. */ + description?: string + /** External identifiers for this discount. */ + externalReferences?: InvoiceLineExternalReferences + /** The monetary amount deducted. */ + amount: string +} + +/** A usage quantity discount applied to an invoice line item. */ +export interface InvoiceLineUsageDiscount { + /** Unique identifier for the discount. */ + id: string + /** The reason this discount was applied. */ + reason: 'maximum_spend' | 'ratecard_percentage' | 'ratecard_usage' + /** Optional human-readable description of the discount. */ + description?: string + /** External identifiers for this discount. */ + externalReferences?: InvoiceLineExternalReferences + /** The usage quantity deducted (in billing units). */ + quantity: string +} + +/** Base fields shared by all invoice line item discounts. */ +export interface InvoiceLineBaseDiscount { + /** Unique identifier for the discount. */ + id: string + /** The reason this discount was applied. */ + reason: 'maximum_spend' | 'ratecard_percentage' | 'ratecard_usage' + /** Optional human-readable description of the discount. */ + description?: string + /** External identifiers for this discount. */ + externalReferences?: InvoiceLineExternalReferences +} + /** Remove a phase from the subscription. */ export interface SubscriptionEditRemovePhase { /** Discriminator for the remove-phase operation. */ @@ -2473,46 +2524,6 @@ export interface WorkflowInvoicingSettings { subscriptionEndProrationMode: 'bill_full_period' | 'bill_actual_period' } -/** A monetary amount discount applied to an invoice line item. */ -export interface InvoiceLineAmountDiscount { - /** Unique identifier for the discount. */ - id: string - /** The reason this discount was applied. */ - reason: 'maximum_spend' | 'ratecard_percentage' | 'ratecard_usage' - /** Optional human-readable description of the discount. */ - description?: string - /** External identifiers for this discount. */ - externalReferences?: InvoiceLineExternalReferences - /** The monetary amount deducted. */ - amount: string -} - -/** A usage quantity discount applied to an invoice line item. */ -export interface InvoiceLineUsageDiscount { - /** Unique identifier for the discount. */ - id: string - /** The reason this discount was applied. */ - reason: 'maximum_spend' | 'ratecard_percentage' | 'ratecard_usage' - /** Optional human-readable description of the discount. */ - description?: string - /** External identifiers for this discount. */ - externalReferences?: InvoiceLineExternalReferences - /** The usage quantity deducted (in billing units). */ - quantity: string -} - -/** Base fields shared by all invoice line item discounts. */ -export interface InvoiceLineBaseDiscount { - /** Unique identifier for the discount. */ - id: string - /** The reason this discount was applied. */ - reason: 'maximum_spend' | 'ratecard_percentage' | 'ratecard_usage' - /** Optional human-readable description of the discount. */ - description?: string - /** External identifiers for this discount. */ - externalReferences?: InvoiceLineExternalReferences -} - /** Filter options for listing currencies. */ export interface ListCurrenciesParamsFilter { type?: 'fiat' | 'custom' @@ -4574,6 +4585,14 @@ export interface InvoiceWorkflow { payment?: WorkflowPaymentSettings } +/** Discounts applied to an invoice line item. */ +export interface InvoiceLineDiscounts { + /** Monetary amount discounts (e.g. from maximum spend commitments). */ + amount?: InvoiceLineAmountDiscount[] + /** Usage quantity discounts (e.g. free tier usage allowances). */ + usage?: InvoiceLineUsageDiscount[] +} + /** Request for canceling a subscription. */ export interface SubscriptionCancel { /** If not provided the subscription is canceled immediately. */ @@ -4721,14 +4740,6 @@ export interface AppCatalogItem { installMethods: ('with_oauth2' | 'with_api_key' | 'no_credentials_required')[] } -/** Discounts applied to an invoice line item. */ -export interface InvoiceLineDiscounts { - /** Monetary amount discounts (e.g. from maximum spend commitments). */ - amount?: InvoiceLineAmountDiscount[] - /** Usage quantity discounts (e.g. free tier usage allowances). */ - usage?: InvoiceLineUsageDiscount[] -} - /** * Invoice-level snapshot of the workflow configuration. * @@ -5502,6 +5513,51 @@ export interface InvoiceWorkflowSettings { workflow: InvoiceWorkflow } +/** + * A detailed (child) sub-line belonging to a parent invoice line. + * + * Detailed lines represent the individual flat-fee components that make up a + * usage-based parent line after quantity snapshotting. + */ +export interface InvoiceDetailedLine { + id: string + /** + * Display name of the resource. + * + * Between 1 and 256 characters. + */ + name: string + /** + * Optional description of the resource. + * + * Maximum 1024 characters. + */ + description?: string + labels?: Labels + /** An ISO-8601 timestamp representation of entity creation date. */ + createdAt: Date + /** An ISO-8601 timestamp representation of entity last update date. */ + updatedAt: Date + /** An ISO-8601 timestamp representation of entity deletion date. */ + deletedAt?: Date + /** The service period covered by this detailed line. */ + servicePeriod: ClosedPeriod + /** Aggregated financial totals for the detailed line. */ + totals: Totals + /** The cost category of this detailed line. */ + category: 'regular' | 'commitment' + /** Discounts applied to this detailed line. */ + discounts?: InvoiceLineDiscounts + /** Credit applied to this detailed line. */ + creditsApplied?: InvoiceLineCreditsApplied[] + /** External identifiers for this detailed line. */ + externalReferences?: InvoiceLineExternalReferences + /** The quantity of the detailed line. */ + quantity: string + /** The unit price of the detailed line. */ + unitPrice: string +} + /** Page paginated response. */ export interface TaxCodePagePaginatedResponse { data: TaxCode[] @@ -5826,51 +5882,6 @@ export interface InstalledAppExternalInvoicing { )[] } -/** - * A detailed (child) sub-line belonging to a parent invoice line. - * - * Detailed lines represent the individual flat-fee components that make up a - * usage-based parent line after quantity snapshotting. - */ -export interface InvoiceDetailedLine { - id: string - /** - * Display name of the resource. - * - * Between 1 and 256 characters. - */ - name: string - /** - * Optional description of the resource. - * - * Maximum 1024 characters. - */ - description?: string - labels?: Labels - /** An ISO-8601 timestamp representation of entity creation date. */ - createdAt: Date - /** An ISO-8601 timestamp representation of entity last update date. */ - updatedAt: Date - /** An ISO-8601 timestamp representation of entity deletion date. */ - deletedAt?: Date - /** The service period covered by this detailed line. */ - servicePeriod: ClosedPeriod - /** Aggregated financial totals for the detailed line. */ - totals: Totals - /** The cost category of this detailed line. */ - category: 'regular' | 'commitment' - /** Discounts applied to this detailed line. */ - discounts?: InvoiceLineDiscounts - /** Credit applied to this detailed line. */ - creditsApplied?: InvoiceLineCreditsApplied[] - /** External identifiers for this detailed line. */ - externalReferences?: InvoiceLineExternalReferences - /** The quantity of the detailed line. */ - quantity: string - /** The unit price of the detailed line. */ - unitPrice: string -} - /** Snapshot of the billing workflow configuration captured at invoice creation. */ export interface UpdateInvoiceWorkflowSettings { /** diff --git a/api/spec/packages/aip-client-javascript/src/sdk/customers.ts b/api/spec/packages/aip-client-javascript/src/sdk/customers.ts index f86473b53d..f410efb414 100644 --- a/api/spec/packages/aip-client-javascript/src/sdk/customers.ts +++ b/api/spec/packages/aip-client-javascript/src/sdk/customers.ts @@ -34,6 +34,7 @@ import { listCreditTransactions, listCustomerCharges, createCustomerCharges, + invoiceCustomerCharges, } from '../funcs/customers.js' import type { CreateCustomerRequest, @@ -96,6 +97,8 @@ import type { ListCustomerChargesResponse, CreateCustomerChargesRequest, CreateCustomerChargesResponse, + InvoiceCustomerChargesRequest, + InvoiceCustomerChargesResponse, } from '../models/operations/customers.js' import type { Charge, @@ -838,4 +841,24 @@ export class CustomersCharges { ): Promise { return unwrap(await createCustomerCharges(this._client, request, options)) } + + /** + * Invoice a customer's charges + * + * Invoice a customer's charges outside the normal billing cycle. Collection + * alignment is bypassed. The customer's progressive billing setting applies unless + * explicitly overridden. All eligible pending lines are considered. + * + * Returns the created standard invoices, one per collected currency. Lines blocked + * by their owning engine remain pending; if all candidates are blocked, the + * response is empty. If no lines are eligible, returns a validation error. + * + * POST /openmeter/customers/{customerId}/charges/invoice + */ + async invoice( + request: InvoiceCustomerChargesRequest, + options?: RequestOptions, + ): Promise { + return unwrap(await invoiceCustomerCharges(this._client, request, options)) + } } diff --git a/api/spec/packages/aip/src/customers/charges/operations.tsp b/api/spec/packages/aip/src/customers/charges/operations.tsp index 206f5e76c6..b02bd2c609 100644 --- a/api/spec/packages/aip/src/customers/charges/operations.tsp +++ b/api/spec/packages/aip/src/customers/charges/operations.tsp @@ -6,6 +6,7 @@ import "../../common/pagination.tsp"; import "../../shared/index.tsp"; import "../../common/parameters.tsp"; import "./charges.tsp"; +import "../../invoices/index.tsp"; using TypeSpec.Http; using TypeSpec.OpenAPI; @@ -53,6 +54,23 @@ model ListCustomerChargesParamsFilter { service_period_to?: Common.DateTimeFieldFilter; } +/** + * Options for manually collecting a customer's pending lines into invoices. + */ +@friendlyName("InvoiceCustomerChargesInput") +model InvoiceCustomerChargesInput { + /** + * Collection cutoff. Defaults to the current time and cannot be in the future. + */ + as_of?: Shared.DateTime; + + /** + * Override the customer's progressive billing setting for this collection. When + * enabled, eligible usage-based lines can be collected for a partial period. + */ + progressive_billing_override?: boolean; +} + namespace Charges { interface CustomerChargesOperations { /** @@ -129,5 +147,30 @@ namespace Charges { create(@path customerId: Shared.ULID, @body charge: CreateCharge): | Shared.CreateResponse | Common.ErrorResponses; + + /** + * Invoice a customer's charges outside the normal billing cycle. Collection + * alignment is bypassed. The customer's progressive billing setting applies unless + * explicitly overridden. All eligible pending lines are considered. + * + * Returns the created standard invoices, one per collected currency. Lines blocked + * by their owning engine remain pending; if all candidates are blocked, the + * response is empty. If no lines are eligible, returns a validation error. + */ + @post + @route("/invoice") + @operationId("invoice-customer-charges") + @summary("Invoice a customer's charges") + @extension(Shared.RPCEndpointExtension, true) + invoiceCharges( + @path customerId: Shared.ULID, + @body request?: InvoiceCustomerChargesInput, + ): + | { + @statusCode _: 201; + @body body: Invoices.Invoice[]; + } + | Common.NotFound + | Common.ErrorResponses; } } diff --git a/api/spec/packages/typespec-go/src/components/GoResource.tsx b/api/spec/packages/typespec-go/src/components/GoResource.tsx index 72c950c6c0..534af4ad56 100644 --- a/api/spec/packages/typespec-go/src/components/GoResource.tsx +++ b/api/spec/packages/typespec-go/src/components/GoResource.tsx @@ -963,9 +963,8 @@ function pageElement(program: Program, response: Type): string { return readReferenceName(program, element) } -function readReferenceName(program: Program, type: Type): string { - const mapped = goType(program, type).type - return typeof mapped === 'string' ? mapped : typeName(program, type) +export function readReferenceName(program: Program, type: Type): string { + return goType(program, type).text ?? typeName(program, type) } // Locals every generated method body may declare (including the receiver and diff --git a/api/spec/packages/typespec-go/test/resource-render.test.ts b/api/spec/packages/typespec-go/test/resource-render.test.ts index bc2adc9990..4955ad4105 100644 --- a/api/spec/packages/typespec-go/test/resource-render.test.ts +++ b/api/spec/packages/typespec-go/test/resource-render.test.ts @@ -13,6 +13,7 @@ import { isPlainPageEnvelope, localName, queryScalarValue, + readReferenceName, resolveListParamsNames, } from '../dist/components/GoResource.js' @@ -70,6 +71,28 @@ const pageFixturePreamble = ` } ` +describe('response collection types', () => { + it('uses slice types for anonymous and named array responses', async () => { + const { program, operations } = await compileResource(` + @service namespace Test; + model Item { id: string; } + model Items is Array; + + @route("/items") + interface Operations { + @get op list(): Item[]; + @get @route("/named") op named(): Items; + } + `) + + expect(operations).toHaveLength(2) + for (const operation of operations) { + expect(operation.response).toBeDefined() + expect(readReferenceName(program, operation.response!)).toBe('[]Item') + } + }) +}) + describe('list params struct naming', () => { it('splits shared-element params structs when query shapes differ', async () => { const { program, operations } = await compileResource(` diff --git a/api/v3/api.gen.go b/api/v3/api.gen.go index 4a198e16fd..099fcce34f 100644 --- a/api/v3/api.gen.go +++ b/api/v3/api.gen.go @@ -9601,6 +9601,16 @@ type InvalidParameters_Item struct { // InvalidRules invalid parameters rules type InvalidRules string +// InvoiceCustomerChargesInput Options for manually collecting a customer's pending lines into invoices. +type InvoiceCustomerChargesInput struct { + // AsOf Collection cutoff. Defaults to the current time and cannot be in the future. + AsOf *DateTime `json:"as_of,omitempty"` + + // ProgressiveBillingOverride Override the customer's progressive billing setting for this collection. When + // enabled, eligible usage-based lines can be collected for a partial period. + ProgressiveBillingOverride *bool `json:"progressive_billing_override,omitempty"` +} + // InvoicePagePaginatedResponse Page paginated response. type InvoicePagePaginatedResponse struct { Data []BillingInvoice `json:"data"` @@ -12393,6 +12403,9 @@ type CreateCustomerStripePortalSessionJSONRequestBody = BillingCustomerStripeCre // CreateCustomerChargesJSONRequestBody defines body for CreateCustomerCharges for application/json ContentType. type CreateCustomerChargesJSONRequestBody = CreateChargeRequest +// InvoiceCustomerChargesJSONRequestBody defines body for InvoiceCustomerCharges for application/json ContentType. +type InvoiceCustomerChargesJSONRequestBody = InvoiceCustomerChargesInput + // CreateCreditAdjustmentJSONRequestBody defines body for CreateCreditAdjustment for application/json ContentType. type CreateCreditAdjustmentJSONRequestBody = CreateCreditAdjustmentRequest @@ -16488,6 +16501,9 @@ type ServerInterface interface { // Create customer charge // (POST /openmeter/customers/{customerId}/charges) CreateCustomerCharges(w http.ResponseWriter, r *http.Request, customerId ULID) + // Invoice a customer's charges + // (POST /openmeter/customers/{customerId}/charges/invoice) + InvoiceCustomerCharges(w http.ResponseWriter, r *http.Request, customerId ULID) // Create a credit adjustment // (POST /openmeter/customers/{customerId}/credits/adjustments) CreateCreditAdjustment(w http.ResponseWriter, r *http.Request, customerId ULID) @@ -17037,6 +17053,12 @@ func (_ Unimplemented) CreateCustomerCharges(w http.ResponseWriter, r *http.Requ w.WriteHeader(http.StatusNotImplemented) } +// Invoice a customer's charges +// (POST /openmeter/customers/{customerId}/charges/invoice) +func (_ Unimplemented) InvoiceCustomerCharges(w http.ResponseWriter, r *http.Request, customerId ULID) { + w.WriteHeader(http.StatusNotImplemented) +} + // Create a credit adjustment // (POST /openmeter/customers/{customerId}/credits/adjustments) func (_ Unimplemented) CreateCreditAdjustment(w http.ResponseWriter, r *http.Request, customerId ULID) { @@ -18947,6 +18969,32 @@ func (siw *ServerInterfaceWrapper) CreateCustomerCharges(w http.ResponseWriter, handler.ServeHTTP(w, r) } +// InvoiceCustomerCharges operation middleware +func (siw *ServerInterfaceWrapper) InvoiceCustomerCharges(w http.ResponseWriter, r *http.Request) { + + var err error + _ = err + + // ------------- Path parameter "customerId" ------------- + var customerId ULID + + err = runtime.BindStyledParameterWithOptions("simple", "customerId", chi.URLParam(r, "customerId"), &customerId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: ""}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "customerId", Err: err}) + return + } + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.InvoiceCustomerCharges(w, r, customerId) + })) + + for i := len(siw.HandlerMiddlewares) - 1; i >= 0; i-- { + handler = siw.HandlerMiddlewares[i](handler) + } + + handler.ServeHTTP(w, r) +} + // CreateCreditAdjustment operation middleware func (siw *ServerInterfaceWrapper) CreateCreditAdjustment(w http.ResponseWriter, r *http.Request) { @@ -22476,6 +22524,9 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl r.Group(func(r chi.Router) { r.Post(options.BaseURL+"/openmeter/customers/{customerId}/charges", wrapper.CreateCustomerCharges) }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/openmeter/customers/{customerId}/charges/invoice", wrapper.InvoiceCustomerCharges) + }) r.Group(func(r chi.Router) { r.Post(options.BaseURL+"/openmeter/customers/{customerId}/credits/adjustments", wrapper.CreateCreditAdjustment) }) @@ -23657,480 +23708,487 @@ var swaggerSpec = []string{ "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", - "z6Q29d0xg4/vP/5/AwA=", + "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", + "YwYf33/8/wYA", } // decodeSpec returns the embedded OpenAPI spec as raw JSON bytes, diff --git a/api/v3/client/README.md b/api/v3/client/README.md index 71d2218c3a..8de2050329 100644 --- a/api/v3/client/README.md +++ b/api/v3/client/README.md @@ -188,6 +188,7 @@ The full call path, HTTP route, and a short description are listed below. | `om.Customers.Credits.Transactions.List` | `GET /openmeter/customers/{customerId}/credits/transactions` | List credit transactions for a customer. Returns an immutable, chronological record of credit movements: funded credits and consumed credits. Transactions are returned in reverse chronological order by default. | | `om.Customers.Charges.List` | `GET /openmeter/customers/{customerId}/charges` | List customer charges. Returns the customer's charges that are represented as either flat fee or usage-based charges. | | `om.Customers.Charges.Create` | `POST /openmeter/customers/{customerId}/charges` | Create customer charge. | +| `om.Customers.Charges.Invoice` | `POST /openmeter/customers/{customerId}/charges/invoice` | Invoice a customer's charges outside the normal billing cycle. Collection alignment is bypassed. The customer's progressive billing setting applies unless explicitly overridden. All eligible pending lines are considered. Returns the created standard invoices, one per collected currency. Lines blocked by their owning engine remain pending; if all candidates are blocked, the response is empty. If no lines are eligible, returns a validation error. | ### Entitlements diff --git a/api/v3/client/customers_charges.go b/api/v3/client/customers_charges.go index e8d77a679a..dad73d29ad 100644 --- a/api/v3/client/customers_charges.go +++ b/api/v3/client/customers_charges.go @@ -133,3 +133,32 @@ func (s *CustomersChargesService) Create(ctx context.Context, customerID string, return &out, nil } + +// Invoice a customer's charges outside the normal billing cycle. Collection +// alignment is bypassed. The customer's progressive billing setting applies unless +// explicitly overridden. All eligible pending lines are considered. +// +// Returns the created standard invoices, one per collected currency. Lines blocked +// by their owning engine remain pending; if all candidates are blocked, the +// response is empty. If no lines are eligible, returns a validation error. +func (s *CustomersChargesService) Invoice(ctx context.Context, customerID string, request *InvoiceCustomerChargesInput) (*[]Invoice, error) { + if customerID == "" { + return nil, fmt.Errorf("openmeter: %s must not be empty: %w", "customerID", ErrEmptyID) + } + + path := "/openmeter/customers/{customerId}/charges/invoice" + + path = replacePathParam(path, "customerId", customerID) + + req, err := s.client.newRequestWithContentType(ctx, http.MethodPost, path, nil, optionalBody(request), "application/json", "application/json") + if err != nil { + return nil, err + } + + var out []Invoice + if err := s.client.doJSON(req, &out); err != nil { + return nil, err + } + + return &out, nil +} diff --git a/api/v3/client/models_customers.go b/api/v3/client/models_customers.go index 58aecf53b2..5a506dba1f 100644 --- a/api/v3/client/models_customers.go +++ b/api/v3/client/models_customers.go @@ -1753,6 +1753,15 @@ func (value EntitlementMeasureUsageFromPreset) Valid() bool { } } +// Options for manually collecting a customer's pending lines into invoices. +type InvoiceCustomerChargesInput struct { + // Collection cutoff. Defaults to the current time and cannot be in the future. + AsOf *time.Time `json:"as_of,omitempty"` + // Override the customer's progressive billing setting for this collection. When + // enabled, eligible usage-based lines can be collected for a partial period. + ProgressiveBillingOverride *bool `json:"progressive_billing_override,omitempty"` +} + // Recurring period input. The anchor is optional; the owning resource defines the // default, typically its creation time. type RecurringPeriodInput struct { diff --git a/api/v3/client/models_invoices.go b/api/v3/client/models_invoices.go index 2b93208b45..4f18c1a959 100644 --- a/api/v3/client/models_invoices.go +++ b/api/v3/client/models_invoices.go @@ -5,462 +5,14 @@ package openmeter import ( "encoding/json" "fmt" - "time" ) -// Reference to a charge associated with an invoice line. -type ChargeReference struct { - // Unique identifier for the charge. - ID string `json:"id"` -} - -// An invoice issued to a customer. -// -// The `type` field determines the concrete variant: -// -// - `standard`: a standard invoice for charges owed. -// -// Invoice is a JSON-preserving tagged union: its zero value marshals as JSON null, and values must be built with the InvoiceFrom* constructors. -// The exported Type field is decode-side metadata; MarshalJSON round-trips the original payload and ignores writes to it. -type Invoice struct { - Type string `json:"type"` - raw json.RawMessage -} - -func (u *Invoice) UnmarshalJSON(data []byte) error { - u.raw = append([]byte(nil), data...) - if string(data) == "null" { - u.Type = "" - return nil - } - - var envelope struct { - Value string `json:"type"` - } - if err := json.Unmarshal(data, &envelope); err != nil { - return err - } - u.Type = envelope.Value - return nil -} - -func (u Invoice) MarshalJSON() ([]byte, error) { - if len(u.raw) == 0 { - return []byte("null"), nil - } - return append([]byte(nil), u.raw...), nil -} - -func (u Invoice) AsInvoiceStandard() (*InvoiceStandard, error) { - if u.Type != "standard" { - return nil, fmt.Errorf("Invoice: expected type %q, got %q", "standard", u.Type) - } - var value InvoiceStandard - if err := json.Unmarshal(u.raw, &value); err != nil { - return nil, err - } - return &value, nil -} - -func InvoiceFromInvoiceStandard(value InvoiceStandard) (Invoice, error) { - value.Type = "standard" - raw, err := json.Marshal(value) - if err != nil { - return Invoice{}, err - } - var result Invoice - if err := result.UnmarshalJSON(raw); err != nil { - return Invoice{}, err - } - return result, nil -} - -// Snapshot of the customer's information at the time the invoice was issued. -type InvoiceCustomer struct { - // Display name of the resource. - // - // Between 1 and 256 characters. - Name string `json:"name"` - // Mapping to attribute metered usage to the customer by the event subject. - UsageAttribution *CustomerUsageAttribution `json:"usage_attribution,omitempty"` - // The billing address of the customer. Used for tax and invoicing. - BillingAddress *Address `json:"billing_address,omitempty"` - ID string `json:"id"` - // Optional external resource key for the customer. - // - // Omitted when the customer was created without a key. Unlike on the customer - // resource itself, the key is optional here because the invoice snapshot may - // predate or omit it. - Key *string `json:"key,omitempty"` -} - -// A detailed (child) sub-line belonging to a parent invoice line. -// -// Detailed lines represent the individual flat-fee components that make up a -// usage-based parent line after quantity snapshotting. -type InvoiceDetailedLine struct { - ID string `json:"id"` - // Display name of the resource. - // - // Between 1 and 256 characters. - Name string `json:"name"` - // Optional description of the resource. - // - // Maximum 1024 characters. - Description *string `json:"description,omitempty"` - Labels map[string]string `json:"labels,omitempty"` - // An ISO-8601 timestamp representation of entity creation date. - CreatedAt time.Time `json:"created_at"` - // An ISO-8601 timestamp representation of entity last update date. - UpdatedAt time.Time `json:"updated_at"` - // An ISO-8601 timestamp representation of entity deletion date. - DeletedAt *time.Time `json:"deleted_at,omitempty"` - // The service period covered by this detailed line. - ServicePeriod ClosedPeriod `json:"service_period"` - // Aggregated financial totals for the detailed line. - Totals Totals `json:"totals"` - // The cost category of this detailed line. - Category InvoiceDetailedLineCostCategory `json:"category"` - // Discounts applied to this detailed line. - Discounts *InvoiceLineDiscounts `json:"discounts,omitempty"` - // Credit applied to this detailed line. - CreditsApplied []InvoiceLineCreditsApplied `json:"credits_applied,omitempty"` - // External identifiers for this detailed line. - ExternalReferences *InvoiceLineExternalReferences `json:"external_references,omitempty"` - // The quantity of the detailed line. - Quantity Numeric `json:"quantity"` - // The unit price of the detailed line. - UnitPrice Numeric `json:"unit_price"` -} - -// Cost category of a detailed invoice line item. -type InvoiceDetailedLineCostCategory string - -const ( - InvoiceDetailedLineCostCategoryRegular InvoiceDetailedLineCostCategory = "regular" - InvoiceDetailedLineCostCategoryCommitment InvoiceDetailedLineCostCategory = "commitment" -) - -func (value InvoiceDetailedLineCostCategory) Valid() bool { - switch value { - case InvoiceDetailedLineCostCategoryRegular, InvoiceDetailedLineCostCategoryCommitment: - return true - default: - return false - } -} - -// The reason a discount was applied to an invoice line. -type InvoiceDiscountReason string - -const ( - InvoiceDiscountReasonMaximumSpend InvoiceDiscountReason = "maximum_spend" - InvoiceDiscountReasonRatecardPercentage InvoiceDiscountReason = "ratecard_percentage" - InvoiceDiscountReasonRatecardUsage InvoiceDiscountReason = "ratecard_usage" -) - -func (value InvoiceDiscountReason) Valid() bool { - switch value { - case InvoiceDiscountReasonMaximumSpend, InvoiceDiscountReasonRatecardPercentage, InvoiceDiscountReasonRatecardUsage: - return true - default: - return false - } -} - -// A top-level line item on an invoice. -// -// Each line represents a single charge, typically associated with a rate card from -// a subscription. Detailed (child) lines are nested under `detailed_lines` when -// present. -// -// InvoiceLine is a JSON-preserving tagged union: its zero value marshals as JSON null, and values must be built with the InvoiceLineFrom* constructors. -// The exported Type field is decode-side metadata; MarshalJSON round-trips the original payload and ignores writes to it. -type InvoiceLine struct { - Type string `json:"type"` - raw json.RawMessage -} - -func (u *InvoiceLine) UnmarshalJSON(data []byte) error { - u.raw = append([]byte(nil), data...) - if string(data) == "null" { - u.Type = "" - return nil - } - - var envelope struct { - Value string `json:"type"` - } - if err := json.Unmarshal(data, &envelope); err != nil { - return err - } - u.Type = envelope.Value - return nil -} - -func (u InvoiceLine) MarshalJSON() ([]byte, error) { - if len(u.raw) == 0 { - return []byte("null"), nil - } - return append([]byte(nil), u.raw...), nil -} - -func (u InvoiceLine) AsInvoiceStandardLine() (*InvoiceStandardLine, error) { - if u.Type != "standard_line" { - return nil, fmt.Errorf("InvoiceLine: expected type %q, got %q", "standard_line", u.Type) - } - var value InvoiceStandardLine - if err := json.Unmarshal(u.raw, &value); err != nil { - return nil, err - } - return &value, nil -} - -func InvoiceLineFromInvoiceStandardLine(value InvoiceStandardLine) (InvoiceLine, error) { - value.Type = "standard_line" - raw, err := json.Marshal(value) - if err != nil { - return InvoiceLine{}, err - } - var result InvoiceLine - if err := result.UnmarshalJSON(raw); err != nil { - return InvoiceLine{}, err - } - return result, nil -} - -// A monetary amount discount applied to an invoice line item. -type InvoiceLineAmountDiscount struct { - // Unique identifier for the discount. - ID string `json:"id"` - // The reason this discount was applied. - Reason InvoiceDiscountReason `json:"reason"` - // Optional human-readable description of the discount. - Description *string `json:"description,omitempty"` - // External identifiers for this discount. - ExternalReferences *InvoiceLineExternalReferences `json:"external_references,omitempty"` - // The monetary amount deducted. - Amount Numeric `json:"amount"` -} - -// A credit allocation applied to an invoice line item. -type InvoiceLineCreditsApplied struct { - // The monetary amount credited. - Amount Numeric `json:"amount"` - // Optional human-readable description of the credit allocation. - Description *string `json:"description,omitempty"` -} - -// Discounts applied to an invoice line item. -type InvoiceLineDiscounts struct { - // Monetary amount discounts (e.g. from maximum spend commitments). - Amount []InvoiceLineAmountDiscount `json:"amount,omitempty"` - // Usage quantity discounts (e.g. free tier usage allowances). - Usage []InvoiceLineUsageDiscount `json:"usage,omitempty"` -} - -// External identifiers for an invoice line item assigned by third-party systems. -type InvoiceLineExternalReferences struct { - // The ID assigned by the external invoicing app. - InvoicingID *string `json:"invoicing_id,omitempty"` -} - -// Rate card configuration snapshot for a usage-based invoice line. -type InvoiceLineRateCard struct { - // The price definition used to calculate charges for this line. - Price Price `json:"price"` - // Tax configuration snapshot for this line. - TaxConfig *TaxCodeConfig `json:"tax_config,omitempty"` - // The feature key associated with this line's rate card. - FeatureKey *string `json:"feature_key,omitempty"` - // Discount configuration from the rate card. - Discounts *RateCardDiscounts `json:"discounts,omitempty"` - // Unit config snapshot applied when this line was billed, converting the raw - // metered quantity into the billed quantity. Frozen at billing time; read-only. - UnitConfig *UnitConfig `json:"unit_config,omitempty"` -} - -// Line item type discriminator. -type InvoiceLineType string - -const ( - InvoiceLineTypeStandardLine InvoiceLineType = "standard_line" -) - -func (value InvoiceLineType) Valid() bool { - switch value { - case InvoiceLineTypeStandardLine: - return true - default: - return false - } -} - -// A usage quantity discount applied to an invoice line item. -type InvoiceLineUsageDiscount struct { - // Unique identifier for the discount. - ID string `json:"id"` - // The reason this discount was applied. - Reason InvoiceDiscountReason `json:"reason"` - // Optional human-readable description of the discount. - Description *string `json:"description,omitempty"` - // External identifiers for this discount. - ExternalReferences *InvoiceLineExternalReferences `json:"external_references,omitempty"` - // The usage quantity deducted (in billing units). - Quantity Numeric `json:"quantity"` -} - // Page paginated response. type InvoicePagePaginatedResponse struct { Data []Invoice `json:"data"` Meta PaginatedMeta `json:"meta"` } -// A standard invoice for charges owed by the customer. -type InvoiceStandard struct { - ID string `json:"id"` - // Optional description of the resource. - // - // Maximum 1024 characters. - Description *string `json:"description,omitempty"` - Labels map[string]string `json:"labels,omitempty"` - // An ISO-8601 timestamp representation of entity creation date. - CreatedAt time.Time `json:"created_at"` - // An ISO-8601 timestamp representation of entity last update date. - UpdatedAt time.Time `json:"updated_at"` - // An ISO-8601 timestamp representation of entity deletion date. - DeletedAt *time.Time `json:"deleted_at,omitempty"` - // Human-readable invoice number generated by the invoicing app. - Number string `json:"number"` - // Three-letter ISO 4217 currency code for the invoice. - Currency string `json:"currency"` - // Snapshot of the supplier's contact information at the time the invoice was - // issued. - Supplier Supplier `json:"supplier"` - // Snapshot of the customer's information at the time the invoice was issued. - Customer InvoiceCustomer `json:"customer"` - // Aggregated financial totals for the invoice. - Totals Totals `json:"totals"` - // The service period covered by this invoice. - // - // For flat fee the service period can be empty which means `from` will be equals - // to `to`. In other cases those fields will be filled with the actual service - // period. - ServicePeriod ClosedPeriod `json:"service_period"` - // Validation issues found during invoice processing. - // - // Present only when there are one or more validation findings. An empty list is - // omitted. - ValidationIssues []ValidationIssue `json:"validation_issues,omitempty"` - // External identifiers assigned to this invoice by third-party systems. - ExternalReferences *InvoiceExternalReferences `json:"external_references,omitempty"` - // Discriminator field identifying this as a standard invoice. - Type InvoiceType `json:"type"` - // Current lifecycle status of the invoice. - Status InvoiceStandardStatus `json:"status"` - // Detailed status information including available actions and workflow state. - StatusDetails InvoiceStatusDetails `json:"status_details"` - // Timestamp when the invoice was issued to the customer. - IssuedAt *time.Time `json:"issued_at,omitempty"` - // Timestamp until which the invoice remains in draft state. - // - // The invoice advances automatically once this time is reached. - DraftUntil *time.Time `json:"draft_until,omitempty"` - // Timestamp when usage quantities were last snapshotted for this invoice. - QuantitySnapshottedAt *time.Time `json:"quantity_snapshotted_at,omitempty"` - // Timestamp when collection was initiated for this invoice. - CollectionAt *time.Time `json:"collection_at,omitempty"` - // Timestamp when payment is due. - DueAt *time.Time `json:"due_at,omitempty"` - // Timestamp when the invoice was sent to the customer. - SentToCustomerAt *time.Time `json:"sent_to_customer_at,omitempty"` - // Workflow configuration snapshot captured at invoice creation time. - Workflow InvoiceWorkflowSettings `json:"workflow"` - // Line items on this invoice. - // - // Always returned on single-resource GET; omitted on list endpoints unless - // explicitly expanded. Editable via update: existing lines are matched by `id`, - // lines without an `id` are created, and lines present on the invoice but omitted - // from the update request are deleted. Detailed (child) lines are always computed - // and cannot be edited directly. - Lines []InvoiceLine `json:"lines,omitempty"` -} - -// A top-level line item on an invoice. -// -// Each line represents a single charge, typically associated with a rate card from -// a subscription. Detailed (child) lines are nested under `detailed_lines` when -// present. -type InvoiceStandardLine struct { - // Display name of the resource. - // - // Between 1 and 256 characters. - Name string `json:"name"` - // Optional description of the resource. - // - // Maximum 1024 characters. - Description *string `json:"description,omitempty"` - Labels map[string]string `json:"labels,omitempty"` - // An ISO-8601 timestamp representation of entity creation date. - CreatedAt time.Time `json:"created_at"` - // An ISO-8601 timestamp representation of entity last update date. - UpdatedAt time.Time `json:"updated_at"` - // An ISO-8601 timestamp representation of entity deletion date. - DeletedAt *time.Time `json:"deleted_at,omitempty"` - // ID of the line. - // - // Optional on update: omit to create a new line, or supply the ID of an existing - // line to edit it. Existing lines omitted from an update's `lines` array are - // deleted. - ID *string `json:"id,omitempty"` - // The type of charge this line item represents. - Type InvoiceLineType `json:"type"` - // Indicates whether this line item's lifecycle is controlled by OpenMeter or - // manually overridden by the API user. - LifecycleController LifecycleController `json:"lifecycle_controller"` - // The service period covered by this invoice, spanning the earliest line start to - // the latest line end across all of its lines. - // - // For an invoice with no lines the period is empty, which means `from` will be - // equal to `to`. - ServicePeriod ClosedPeriod `json:"service_period"` - // Aggregated financial totals for the line item. - Totals Totals `json:"totals"` - // Discounts applied to this line item. - Discounts *InvoiceLineDiscounts `json:"discounts,omitempty"` - // Credit applied to this line item. - CreditsApplied []InvoiceLineCreditsApplied `json:"credits_applied,omitempty"` - // External identifiers for this line item assigned by third-party systems. - ExternalReferences *InvoiceLineExternalReferences `json:"external_references,omitempty"` - // Reference to the subscription item that generated this line. - Subscription *SubscriptionReference `json:"subscription,omitempty"` - // The rate card configuration snapshot used to price this line item. - RateCard InvoiceLineRateCard `json:"rate_card"` - // Detailed sub-lines that this line has been broken down into. - // - // Present when line has individual details. - DetailedLines []InvoiceDetailedLine `json:"detailed_lines"` - // Usage quantity details for this line when UnitConfig is in effect. - // - // Read-only; omitted for lines without unit conversion. - UsageQuantityDetail *InvoiceUsageQuantityDetail `json:"usage_quantity_detail,omitempty"` - // Reference to the charge associated with this line item. - Charge *ChargeReference `json:"charge,omitempty"` -} - -// Usage quantity details on an invoice line item when UnitConfig is in effect. -// -// Provides the full audit trail from raw meter output to the invoiced amount. -type InvoiceUsageQuantityDetail struct { - // The raw quantity as reported by the meter (native units). - RawQuantity Numeric `json:"raw_quantity"` - // The net billed quantity for this line in converted units, after rounding and any - // usage discounts. - InvoicedQuantity Numeric `json:"invoiced_quantity"` - // The display unit label (e.g., "GB", "hours", "M tokens"). - DisplayUnit *string `json:"display_unit,omitempty"` -} - // Invoice-level snapshot of the workflow configuration. // // Contains only the settings that are meaningful for an already-created invoice: diff --git a/api/v3/client/models_shared.go b/api/v3/client/models_shared.go index 3eb7c6565a..8ed0afc5a2 100644 --- a/api/v3/client/models_shared.go +++ b/api/v3/client/models_shared.go @@ -766,6 +766,12 @@ func (value ChargeRealizationType) Valid() bool { } } +// Reference to a charge associated with an invoice line. +type ChargeReference struct { + // Unique identifier for the charge. + ID string `json:"id"` +} + // Fiat conversion rate a custom-currency charge is invoiced at. Present once the // cost basis is resolved; dynamic cost bases are exposed only after the service // period has started. @@ -1811,6 +1817,67 @@ func (value FeatureUnitCostType) Valid() bool { } } +// An invoice issued to a customer. +// +// The `type` field determines the concrete variant: +// +// - `standard`: a standard invoice for charges owed. +// +// Invoice is a JSON-preserving tagged union: its zero value marshals as JSON null, and values must be built with the InvoiceFrom* constructors. +// The exported Type field is decode-side metadata; MarshalJSON round-trips the original payload and ignores writes to it. +type Invoice struct { + Type string `json:"type"` + raw json.RawMessage +} + +func (u *Invoice) UnmarshalJSON(data []byte) error { + u.raw = append([]byte(nil), data...) + if string(data) == "null" { + u.Type = "" + return nil + } + + var envelope struct { + Value string `json:"type"` + } + if err := json.Unmarshal(data, &envelope); err != nil { + return err + } + u.Type = envelope.Value + return nil +} + +func (u Invoice) MarshalJSON() ([]byte, error) { + if len(u.raw) == 0 { + return []byte("null"), nil + } + return append([]byte(nil), u.raw...), nil +} + +func (u Invoice) AsInvoiceStandard() (*InvoiceStandard, error) { + if u.Type != "standard" { + return nil, fmt.Errorf("Invoice: expected type %q, got %q", "standard", u.Type) + } + var value InvoiceStandard + if err := json.Unmarshal(u.raw, &value); err != nil { + return nil, err + } + return &value, nil +} + +func InvoiceFromInvoiceStandard(value InvoiceStandard) (Invoice, error) { + value.Type = "standard" + raw, err := json.Marshal(value) + if err != nil { + return Invoice{}, err + } + var result Invoice + if err := result.UnmarshalJSON(raw); err != nil { + return Invoice{}, err + } + return result, nil +} + // Details about an available invoice action including the resulting state. type InvoiceAvailableActionDetails struct { // The extended status the invoice will transition to after performing this action. @@ -1834,6 +1901,99 @@ type InvoiceAvailableActions struct { SnapshotQuantities *InvoiceAvailableActionDetails `json:"snapshot_quantities,omitempty"` } +// Snapshot of the customer's information at the time the invoice was issued. +type InvoiceCustomer struct { + // Display name of the resource. + // + // Between 1 and 256 characters. + Name string `json:"name"` + // Mapping to attribute metered usage to the customer by the event subject. + UsageAttribution *CustomerUsageAttribution `json:"usage_attribution,omitempty"` + // The billing address of the customer. Used for tax and invoicing. + BillingAddress *Address `json:"billing_address,omitempty"` + ID string `json:"id"` + // Optional external resource key for the customer. + // + // Omitted when the customer was created without a key. Unlike on the customer + // resource itself, the key is optional here because the invoice snapshot may + // predate or omit it. + Key *string `json:"key,omitempty"` +} + +// A detailed (child) sub-line belonging to a parent invoice line. +// +// Detailed lines represent the individual flat-fee components that make up a +// usage-based parent line after quantity snapshotting. +type InvoiceDetailedLine struct { + ID string `json:"id"` + // Display name of the resource. + // + // Between 1 and 256 characters. + Name string `json:"name"` + // Optional description of the resource. + // + // Maximum 1024 characters. + Description *string `json:"description,omitempty"` + Labels map[string]string `json:"labels,omitempty"` + // An ISO-8601 timestamp representation of entity creation date. + CreatedAt time.Time `json:"created_at"` + // An ISO-8601 timestamp representation of entity last update date. + UpdatedAt time.Time `json:"updated_at"` + // An ISO-8601 timestamp representation of entity deletion date. + DeletedAt *time.Time `json:"deleted_at,omitempty"` + // The service period covered by this detailed line. + ServicePeriod ClosedPeriod `json:"service_period"` + // Aggregated financial totals for the detailed line. + Totals Totals `json:"totals"` + // The cost category of this detailed line. + Category InvoiceDetailedLineCostCategory `json:"category"` + // Discounts applied to this detailed line. + Discounts *InvoiceLineDiscounts `json:"discounts,omitempty"` + // Credit applied to this detailed line. + CreditsApplied []InvoiceLineCreditsApplied `json:"credits_applied,omitempty"` + // External identifiers for this detailed line. + ExternalReferences *InvoiceLineExternalReferences `json:"external_references,omitempty"` + // The quantity of the detailed line. + Quantity Numeric `json:"quantity"` + // The unit price of the detailed line. + UnitPrice Numeric `json:"unit_price"` +} + +// Cost category of a detailed invoice line item. +type InvoiceDetailedLineCostCategory string + +const ( + InvoiceDetailedLineCostCategoryRegular InvoiceDetailedLineCostCategory = "regular" + InvoiceDetailedLineCostCategoryCommitment InvoiceDetailedLineCostCategory = "commitment" +) + +func (value InvoiceDetailedLineCostCategory) Valid() bool { + switch value { + case InvoiceDetailedLineCostCategoryRegular, InvoiceDetailedLineCostCategoryCommitment: + return true + default: + return false + } +} + +// The reason a discount was applied to an invoice line. +type InvoiceDiscountReason string + +const ( + InvoiceDiscountReasonMaximumSpend InvoiceDiscountReason = "maximum_spend" + InvoiceDiscountReasonRatecardPercentage InvoiceDiscountReason = "ratecard_percentage" + InvoiceDiscountReasonRatecardUsage InvoiceDiscountReason = "ratecard_usage" +) + +func (value InvoiceDiscountReason) Valid() bool { + switch value { + case InvoiceDiscountReasonMaximumSpend, InvoiceDiscountReasonRatecardPercentage, InvoiceDiscountReasonRatecardUsage: + return true + default: + return false + } +} + // External identifiers assigned to an invoice by third-party systems. type InvoiceExternalReferences struct { // The ID assigned by the external invoicing app (e.g. Stripe invoice ID). @@ -1842,6 +2002,280 @@ type InvoiceExternalReferences struct { PaymentID *string `json:"payment_id,omitempty"` } +// A top-level line item on an invoice. +// +// Each line represents a single charge, typically associated with a rate card from +// a subscription. Detailed (child) lines are nested under `detailed_lines` when +// present. +// +// InvoiceLine is a JSON-preserving tagged union: its zero value marshals as JSON null, and values must be built with the InvoiceLineFrom* constructors. +// The exported Type field is decode-side metadata; MarshalJSON round-trips the original payload and ignores writes to it. +type InvoiceLine struct { + Type string `json:"type"` + raw json.RawMessage +} + +func (u *InvoiceLine) UnmarshalJSON(data []byte) error { + u.raw = append([]byte(nil), data...) + if string(data) == "null" { + u.Type = "" + return nil + } + + var envelope struct { + Value string `json:"type"` + } + if err := json.Unmarshal(data, &envelope); err != nil { + return err + } + u.Type = envelope.Value + return nil +} + +func (u InvoiceLine) MarshalJSON() ([]byte, error) { + if len(u.raw) == 0 { + return []byte("null"), nil + } + return append([]byte(nil), u.raw...), nil +} + +func (u InvoiceLine) AsInvoiceStandardLine() (*InvoiceStandardLine, error) { + if u.Type != "standard_line" { + return nil, fmt.Errorf("InvoiceLine: expected type %q, got %q", "standard_line", u.Type) + } + var value InvoiceStandardLine + if err := json.Unmarshal(u.raw, &value); err != nil { + return nil, err + } + return &value, nil +} + +func InvoiceLineFromInvoiceStandardLine(value InvoiceStandardLine) (InvoiceLine, error) { + value.Type = "standard_line" + raw, err := json.Marshal(value) + if err != nil { + return InvoiceLine{}, err + } + var result InvoiceLine + if err := result.UnmarshalJSON(raw); err != nil { + return InvoiceLine{}, err + } + return result, nil +} + +// A monetary amount discount applied to an invoice line item. +type InvoiceLineAmountDiscount struct { + // Unique identifier for the discount. + ID string `json:"id"` + // The reason this discount was applied. + Reason InvoiceDiscountReason `json:"reason"` + // Optional human-readable description of the discount. + Description *string `json:"description,omitempty"` + // External identifiers for this discount. + ExternalReferences *InvoiceLineExternalReferences `json:"external_references,omitempty"` + // The monetary amount deducted. + Amount Numeric `json:"amount"` +} + +// A credit allocation applied to an invoice line item. +type InvoiceLineCreditsApplied struct { + // The monetary amount credited. + Amount Numeric `json:"amount"` + // Optional human-readable description of the credit allocation. + Description *string `json:"description,omitempty"` +} + +// Discounts applied to an invoice line item. +type InvoiceLineDiscounts struct { + // Monetary amount discounts (e.g. from maximum spend commitments). + Amount []InvoiceLineAmountDiscount `json:"amount,omitempty"` + // Usage quantity discounts (e.g. free tier usage allowances). + Usage []InvoiceLineUsageDiscount `json:"usage,omitempty"` +} + +// External identifiers for an invoice line item assigned by third-party systems. +type InvoiceLineExternalReferences struct { + // The ID assigned by the external invoicing app. + InvoicingID *string `json:"invoicing_id,omitempty"` +} + +// Rate card configuration snapshot for a usage-based invoice line. +type InvoiceLineRateCard struct { + // The price definition used to calculate charges for this line. + Price Price `json:"price"` + // Tax configuration snapshot for this line. + TaxConfig *TaxCodeConfig `json:"tax_config,omitempty"` + // The feature key associated with this line's rate card. + FeatureKey *string `json:"feature_key,omitempty"` + // Discount configuration from the rate card. + Discounts *RateCardDiscounts `json:"discounts,omitempty"` + // Unit config snapshot applied when this line was billed, converting the raw + // metered quantity into the billed quantity. Frozen at billing time; read-only. + UnitConfig *UnitConfig `json:"unit_config,omitempty"` +} + +// Line item type discriminator. +type InvoiceLineType string + +const ( + InvoiceLineTypeStandardLine InvoiceLineType = "standard_line" +) + +func (value InvoiceLineType) Valid() bool { + switch value { + case InvoiceLineTypeStandardLine: + return true + default: + return false + } +} + +// A usage quantity discount applied to an invoice line item. +type InvoiceLineUsageDiscount struct { + // Unique identifier for the discount. + ID string `json:"id"` + // The reason this discount was applied. + Reason InvoiceDiscountReason `json:"reason"` + // Optional human-readable description of the discount. + Description *string `json:"description,omitempty"` + // External identifiers for this discount. + ExternalReferences *InvoiceLineExternalReferences `json:"external_references,omitempty"` + // The usage quantity deducted (in billing units). + Quantity Numeric `json:"quantity"` +} + +// A standard invoice for charges owed by the customer. +type InvoiceStandard struct { + ID string `json:"id"` + // Optional description of the resource. + // + // Maximum 1024 characters. + Description *string `json:"description,omitempty"` + Labels map[string]string `json:"labels,omitempty"` + // An ISO-8601 timestamp representation of entity creation date. + CreatedAt time.Time `json:"created_at"` + // An ISO-8601 timestamp representation of entity last update date. + UpdatedAt time.Time `json:"updated_at"` + // An ISO-8601 timestamp representation of entity deletion date. + DeletedAt *time.Time `json:"deleted_at,omitempty"` + // Human-readable invoice number generated by the invoicing app. + Number string `json:"number"` + // Three-letter ISO 4217 currency code for the invoice. + Currency string `json:"currency"` + // Snapshot of the supplier's contact information at the time the invoice was + // issued. + Supplier Supplier `json:"supplier"` + // Snapshot of the customer's information at the time the invoice was issued. + Customer InvoiceCustomer `json:"customer"` + // Aggregated financial totals for the invoice. + Totals Totals `json:"totals"` + // The service period covered by this invoice. + // + // For flat fee the service period can be empty which means `from` will be equals + // to `to`. In other cases those fields will be filled with the actual service + // period. + ServicePeriod ClosedPeriod `json:"service_period"` + // Validation issues found during invoice processing. + // + // Present only when there are one or more validation findings. An empty list is + // omitted. + ValidationIssues []ValidationIssue `json:"validation_issues,omitempty"` + // External identifiers assigned to this invoice by third-party systems. + ExternalReferences *InvoiceExternalReferences `json:"external_references,omitempty"` + // Discriminator field identifying this as a standard invoice. + Type InvoiceType `json:"type"` + // Current lifecycle status of the invoice. + Status InvoiceStandardStatus `json:"status"` + // Detailed status information including available actions and workflow state. + StatusDetails InvoiceStatusDetails `json:"status_details"` + // Timestamp when the invoice was issued to the customer. + IssuedAt *time.Time `json:"issued_at,omitempty"` + // Timestamp until which the invoice remains in draft state. + // + // The invoice advances automatically once this time is reached. + DraftUntil *time.Time `json:"draft_until,omitempty"` + // Timestamp when usage quantities were last snapshotted for this invoice. + QuantitySnapshottedAt *time.Time `json:"quantity_snapshotted_at,omitempty"` + // Timestamp when collection was initiated for this invoice. + CollectionAt *time.Time `json:"collection_at,omitempty"` + // Timestamp when payment is due. + DueAt *time.Time `json:"due_at,omitempty"` + // Timestamp when the invoice was sent to the customer. + SentToCustomerAt *time.Time `json:"sent_to_customer_at,omitempty"` + // Workflow configuration snapshot captured at invoice creation time. + Workflow InvoiceWorkflowSettings `json:"workflow"` + // Line items on this invoice. + // + // Always returned on single-resource GET; omitted on list endpoints unless + // explicitly expanded. Editable via update: existing lines are matched by `id`, + // lines without an `id` are created, and lines present on the invoice but omitted + // from the update request are deleted. Detailed (child) lines are always computed + // and cannot be edited directly. + Lines []InvoiceLine `json:"lines,omitempty"` +} + +// A top-level line item on an invoice. +// +// Each line represents a single charge, typically associated with a rate card from +// a subscription. Detailed (child) lines are nested under `detailed_lines` when +// present. +type InvoiceStandardLine struct { + // Display name of the resource. + // + // Between 1 and 256 characters. + Name string `json:"name"` + // Optional description of the resource. + // + // Maximum 1024 characters. + Description *string `json:"description,omitempty"` + Labels map[string]string `json:"labels,omitempty"` + // An ISO-8601 timestamp representation of entity creation date. + CreatedAt time.Time `json:"created_at"` + // An ISO-8601 timestamp representation of entity last update date. + UpdatedAt time.Time `json:"updated_at"` + // An ISO-8601 timestamp representation of entity deletion date. + DeletedAt *time.Time `json:"deleted_at,omitempty"` + // ID of the line. + // + // Optional on update: omit to create a new line, or supply the ID of an existing + // line to edit it. Existing lines omitted from an update's `lines` array are + // deleted. + ID *string `json:"id,omitempty"` + // The type of charge this line item represents. + Type InvoiceLineType `json:"type"` + // Indicates whether this line item's lifecycle is controlled by OpenMeter or + // manually overridden by the API user. + LifecycleController LifecycleController `json:"lifecycle_controller"` + // The service period covered by this invoice, spanning the earliest line start to + // the latest line end across all of its lines. + // + // For an invoice with no lines the period is empty, which means `from` will be + // equal to `to`. + ServicePeriod ClosedPeriod `json:"service_period"` + // Aggregated financial totals for the line item. + Totals Totals `json:"totals"` + // Discounts applied to this line item. + Discounts *InvoiceLineDiscounts `json:"discounts,omitempty"` + // Credit applied to this line item. + CreditsApplied []InvoiceLineCreditsApplied `json:"credits_applied,omitempty"` + // External identifiers for this line item assigned by third-party systems. + ExternalReferences *InvoiceLineExternalReferences `json:"external_references,omitempty"` + // Reference to the subscription item that generated this line. + Subscription *SubscriptionReference `json:"subscription,omitempty"` + // The rate card configuration snapshot used to price this line item. + RateCard InvoiceLineRateCard `json:"rate_card"` + // Detailed sub-lines that this line has been broken down into. + // + // Present when line has individual details. + DetailedLines []InvoiceDetailedLine `json:"detailed_lines"` + // Usage quantity details for this line when UnitConfig is in effect. + // + // Read-only; omitted for lines without unit conversion. + UsageQuantityDetail *InvoiceUsageQuantityDetail `json:"usage_quantity_detail,omitempty"` + // Reference to the charge associated with this line item. + Charge *ChargeReference `json:"charge,omitempty"` +} + // Lifecycle status of a standard invoice. type InvoiceStandardStatus string @@ -1894,6 +2328,19 @@ func (value InvoiceType) Valid() bool { } } +// Usage quantity details on an invoice line item when UnitConfig is in effect. +// +// Provides the full audit trail from raw meter output to the invoiced amount. +type InvoiceUsageQuantityDetail struct { + // The raw quantity as reported by the meter (native units). + RawQuantity Numeric `json:"raw_quantity"` + // The net billed quantity for this line in converted units, after rounding and any + // usage discounts. + InvoicedQuantity Numeric `json:"invoiced_quantity"` + // The display unit label (e.g., "GB", "hours", "M tokens"). + DisplayUnit *string `json:"display_unit,omitempty"` +} + // Invoice-level snapshot of the workflow configuration. // // Contains only the settings that are meaningful for an already-created invoice: diff --git a/api/v3/handlers/billinginvoices/handler.go b/api/v3/handlers/billinginvoices/handler.go index 92fc1b28ce..7b2d7317bc 100644 --- a/api/v3/handlers/billinginvoices/handler.go +++ b/api/v3/handlers/billinginvoices/handler.go @@ -8,6 +8,7 @@ import ( ) type Handler interface { + InvoiceCustomerCharges() InvoiceCustomerChargesHandler ListBillingInvoices() ListBillingInvoicesHandler GetBillingInvoice() GetBillingInvoiceHandler UpdateBillingInvoice() UpdateBillingInvoiceHandler diff --git a/api/v3/handlers/billinginvoices/invoice_charges.go b/api/v3/handlers/billinginvoices/invoice_charges.go new file mode 100644 index 0000000000..0cc9f1b0c4 --- /dev/null +++ b/api/v3/handlers/billinginvoices/invoice_charges.go @@ -0,0 +1,86 @@ +package billinginvoices + +import ( + "context" + "fmt" + "net/http" + + api "github.com/openmeterio/openmeter/api/v3" + "github.com/openmeterio/openmeter/api/v3/apierrors" + "github.com/openmeterio/openmeter/api/v3/request" + "github.com/openmeterio/openmeter/openmeter/billing" + "github.com/openmeterio/openmeter/openmeter/customer" + "github.com/openmeterio/openmeter/pkg/framework/commonhttp" + "github.com/openmeterio/openmeter/pkg/framework/transport/httptransport" +) + +type ( + InvoiceCustomerChargesRequest struct { + billing.InvoicePendingLinesInput + Options []billing.InvoicePendingLinesOption + } + InvoiceCustomerChargesResponse = []api.BillingInvoice + InvoiceCustomerChargesHandler = httptransport.HandlerWithArgs[InvoiceCustomerChargesRequest, InvoiceCustomerChargesResponse, string] +) + +func (h *handler) InvoiceCustomerCharges() InvoiceCustomerChargesHandler { + return httptransport.NewHandlerWithArgs( + func(ctx context.Context, r *http.Request, customerID string) (InvoiceCustomerChargesRequest, error) { + ns, err := h.resolveNamespace(ctx) + if err != nil { + return InvoiceCustomerChargesRequest{}, err + } + + var body api.InvoiceCustomerChargesInput + if err := request.ParseOptionalBody(r, &body); err != nil { + return InvoiceCustomerChargesRequest{}, err + } + + input := InvoiceCustomerChargesRequest{ + InvoicePendingLinesInput: billing.InvoicePendingLinesInput{ + Customer: customer.CustomerID{ + Namespace: ns, + ID: customerID, + }, + AsOf: body.AsOf, + }, + Options: []billing.InvoicePendingLinesOption{billing.WithBypassCollectionAlignment()}, + } + + if body.ProgressiveBillingOverride != nil { + if *body.ProgressiveBillingOverride { + input.Options = append(input.Options, billing.WithPartialInvoiceLinesEnabled()) + } else { + input.Options = append(input.Options, billing.WithPartialInvoiceLinesDisabled()) + } + } + + return input, nil + }, + func(ctx context.Context, input InvoiceCustomerChargesRequest) (InvoiceCustomerChargesResponse, error) { + invoices, err := h.service.InvoicePendingLines(ctx, input.InvoicePendingLinesInput, input.Options...) + if err != nil { + return nil, fmt.Errorf("collecting customer pending lines: %w", err) + } + + out := make(InvoiceCustomerChargesResponse, 0, len(invoices)) + for _, invoice := range invoices { + mappedInvoice, err := ToAPIStandardInvoice(invoice) + if err != nil { + return nil, fmt.Errorf("mapping collected invoice: %w", err) + } + + out = append(out, mappedInvoice) + } + + return out, nil + }, + commonhttp.JSONResponseEncoderWithStatus[InvoiceCustomerChargesResponse](http.StatusCreated), + httptransport.AppendOptions( + h.options, + httptransport.WithOperationName("invoice-customer-charges"), + httptransport.WithErrorEncoder(apierrors.GenericErrorEncoder()), + httptransport.WithErrorEncoder(errorEncoder()), + )..., + ) +} diff --git a/api/v3/handlers/billinginvoices/invoice_charges_test.go b/api/v3/handlers/billinginvoices/invoice_charges_test.go new file mode 100644 index 0000000000..5d95459505 --- /dev/null +++ b/api/v3/handlers/billinginvoices/invoice_charges_test.go @@ -0,0 +1,198 @@ +package billinginvoices + +import ( + "context" + "encoding/json" + "errors" + "net/http" + "net/http/httptest" + "strings" + "testing" + "time" + + "github.com/samber/lo" + "github.com/stretchr/testify/require" + + api "github.com/openmeterio/openmeter/api/v3" + "github.com/openmeterio/openmeter/openmeter/billing" + "github.com/openmeterio/openmeter/openmeter/customer" +) + +const invoiceChargesCustomerID = "01K3Q8QG4B9YJYJVQ3MZ3V8KT6" + +type invoiceChargesBillingService struct { + billing.Service + collect func(context.Context, billing.InvoicePendingLinesInput, ...billing.InvoicePendingLinesOption) ([]billing.StandardInvoice, error) +} + +func (s invoiceChargesBillingService) InvoicePendingLines(ctx context.Context, input billing.InvoicePendingLinesInput, opts ...billing.InvoicePendingLinesOption) ([]billing.StandardInvoice, error) { + return s.collect(ctx, input, opts...) +} + +type invoiceChargesServer struct { + api.Unimplemented + handler Handler +} + +func (s invoiceChargesServer) InvoiceCustomerCharges(w http.ResponseWriter, r *http.Request, customerID api.ULID) { + s.handler.InvoiceCustomerCharges().With(customerID).ServeHTTP(w, r) +} + +func TestInvoiceCustomerChargesManualCollection(t *testing.T) { + asOf := time.Date(2026, 1, 1, 12, 0, 0, 0, time.UTC) + tests := []struct { + name string + body string + asOf *time.Time + progressive *bool + }{ + { + name: "empty body uses customer settings", + }, + { + name: "empty object uses customer settings", + body: `{}`, + }, + { + name: "cutoff and progressive billing enabled", + body: `{"as_of":"2026-01-01T12:00:00Z","progressive_billing_override":true}`, + asOf: &asOf, + progressive: lo.ToPtr(true), + }, + { + name: "progressive billing explicitly disabled", + body: `{"progressive_billing_override":false}`, + progressive: lo.ToPtr(false), + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + // given: the customer has eligible lines in two currencies. + called := false + service := invoiceChargesBillingService{ + collect: func(_ context.Context, input billing.InvoicePendingLinesInput, opts ...billing.InvoicePendingLinesOption) ([]billing.StandardInvoice, error) { + called = true + require.Equal(t, customer.CustomerID{Namespace: "ns", ID: invoiceChargesCustomerID}, input.Customer) + require.Equal(t, test.asOf, input.AsOf) + require.False(t, input.IncludePendingLines.IsPresent()) + require.False(t, input.ForceAsyncAdvance) + require.Equal(t, billing.InvoicePendingLinesOptions{ + BypassCollectionAlignment: true, + PartialInvoiceLinesEnabled: test.progressive, + }, billing.NewInvoicePendingLinesOptions(opts...)) + + return []billing.StandardInvoice{ + {StandardInvoiceBase: billing.StandardInvoiceBase{ID: "usd-invoice", Currency: "USD", Status: billing.StandardInvoiceStatusDraftCreated}}, + {StandardInvoiceBase: billing.StandardInvoiceBase{ID: "eur-invoice", Currency: "EUR", Status: billing.StandardInvoiceStatusDraftCreated}}, + }, nil + }, + } + handler := New(func(context.Context) (string, error) { return "ns", nil }, service) + router := api.Handler(invoiceChargesServer{handler: handler}) + + // when: manually collecting through the generated customer route. + req := httptest.NewRequest(http.MethodPost, "/openmeter/customers/"+invoiceChargesCustomerID+"/charges/invoice", strings.NewReader(test.body)) + req.Header.Set("Content-Type", "application/json") + res := httptest.NewRecorder() + router.ServeHTTP(res, req) + + // then: both standard invoices are returned after forwarding the manual options. + require.Equal(t, http.StatusCreated, res.Code, res.Body.String()) + require.True(t, called) + var invoices []api.BillingInvoice + require.NoError(t, json.Unmarshal(res.Body.Bytes(), &invoices)) + require.Len(t, invoices, 2) + usdInvoice, err := invoices[0].AsBillingInvoiceStandard() + require.NoError(t, err) + require.Equal(t, "usd-invoice", usdInvoice.Id) + require.Equal(t, api.CurrencyCode("USD"), usdInvoice.Currency) + eurInvoice, err := invoices[1].AsBillingInvoiceStandard() + require.NoError(t, err) + require.Equal(t, api.CurrencyCode("EUR"), eurInvoice.Currency) + }) + } +} + +func TestInvoiceCustomerChargesAllBlockedReturnsEmptyArray(t *testing.T) { + handler := New(func(context.Context) (string, error) { return "ns", nil }, invoiceChargesBillingService{ + collect: func(context.Context, billing.InvoicePendingLinesInput, ...billing.InvoicePendingLinesOption) ([]billing.StandardInvoice, error) { + return nil, nil + }, + }) + req := httptest.NewRequest(http.MethodPost, "/", nil) + res := httptest.NewRecorder() + + handler.InvoiceCustomerCharges().With(invoiceChargesCustomerID).ServeHTTP(res, req) + + require.Equal(t, http.StatusCreated, res.Code, res.Body.String()) + require.JSONEq(t, `[]`, res.Body.String()) +} + +func TestInvoiceCustomerChargesErrors(t *testing.T) { + tests := []struct { + name string + body string + serviceErr error + status int + called bool + }{ + { + name: "malformed JSON", + body: `{`, + status: http.StatusBadRequest, + }, + { + name: "malformed cutoff", + body: `{"as_of":"yesterday"}`, + status: http.StatusBadRequest, + }, + { + name: "future cutoff", + body: `{"as_of":"9999-01-01T00:00:00Z"}`, + status: http.StatusBadRequest, + called: true, + }, + { + name: "no eligible lines", + serviceErr: billing.ValidationError{Err: billing.ErrInvoiceCreateNoLines}, + status: http.StatusBadRequest, + called: true, + }, + { + name: "line not found", + serviceErr: billing.NotFoundError{Entity: billing.EntityInvoiceLine, ID: "missing-line"}, + status: http.StatusNotFound, + called: true, + }, + { + name: "service failure", + serviceErr: errors.New("collection failed"), + status: http.StatusInternalServerError, + called: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + called := false + handler := New(func(context.Context) (string, error) { return "ns", nil }, invoiceChargesBillingService{ + collect: func(_ context.Context, input billing.InvoicePendingLinesInput, _ ...billing.InvoicePendingLinesOption) ([]billing.StandardInvoice, error) { + called = true + if err := input.Validate(); err != nil { + return nil, billing.ValidationError{Err: err} + } + + return nil, test.serviceErr + }, + }) + req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(test.body)) + res := httptest.NewRecorder() + + handler.InvoiceCustomerCharges().With(invoiceChargesCustomerID).ServeHTTP(res, req) + + require.Equal(t, test.status, res.Code, res.Body.String()) + require.Equal(t, test.called, called) + }) + } +} diff --git a/api/v3/openapi.yaml b/api/v3/openapi.yaml index d85912e994..b83cf7c1e6 100644 --- a/api/v3/openapi.yaml +++ b/api/v3/openapi.yaml @@ -1611,6 +1611,50 @@ paths: schema: $ref: '#/components/schemas/CreateChargeRequest' x-unstable: true + /openmeter/customers/{customerId}/charges/invoice: + post: + operationId: invoice-customer-charges + summary: Invoice a customer's charges + description: |- + Invoice a customer's charges outside the normal billing cycle. Collection + alignment is bypassed. The customer's progressive billing setting applies unless + explicitly overridden. All eligible pending lines are considered. + + Returns the created standard invoices, one per collected currency. Lines blocked + by their owning engine remain pending; if all candidates are blocked, the + response is empty. If no lines are eligible, returns a validation error. + parameters: + - name: customerId + in: path + required: true + schema: + $ref: '#/components/schemas/ULID' + responses: + '201': + description: The request has succeeded and a new resource has been created as a result. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/BillingInvoice' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + tags: + - OpenMeter Customers + requestBody: + required: false + content: + application/json: + schema: + $ref: '#/components/schemas/InvoiceCustomerChargesInput' + x-rpc-endpoint: true /openmeter/customers/{customerId}/credits/adjustments: post: operationId: create-credit-adjustment @@ -17054,6 +17098,20 @@ components: $ref: '#/components/schemas/CursorMeta' additionalProperties: false description: Cursor paginated response. + InvoiceCustomerChargesInput: + type: object + properties: + as_of: + allOf: + - $ref: '#/components/schemas/DateTime' + description: Collection cutoff. Defaults to the current time and cannot be in the future. + progressive_billing_override: + type: boolean + description: |- + Override the customer's progressive billing setting for this collection. When + enabled, eligible usage-based lines can be collected for a partial period. + additionalProperties: false + description: Options for manually collecting a customer's pending lines into invoices. InvoicePagePaginatedResponse: type: object required: diff --git a/api/v3/server/routes.go b/api/v3/server/routes.go index b37497b633..88fc38a026 100644 --- a/api/v3/server/routes.go +++ b/api/v3/server/routes.go @@ -720,6 +720,10 @@ func (s *Server) CreateCustomerCharges(w http.ResponseWriter, r *http.Request, c }).ServeHTTP(w, r) } +func (s *Server) InvoiceCustomerCharges(w http.ResponseWriter, r *http.Request, customerId api.ULID) { + s.billingInvoicesHandler.InvoiceCustomerCharges().With(customerId).ServeHTTP(w, r) +} + // Organization Default Tax Codes func (s *Server) GetOrganizationDefaultTaxCodes(w http.ResponseWriter, r *http.Request) { diff --git a/api/v3/test/charges_test.go b/api/v3/test/charges_test.go index e743312a62..009707a40d 100644 --- a/api/v3/test/charges_test.go +++ b/api/v3/test/charges_test.go @@ -58,3 +58,41 @@ func TestCreateFlatFeeChargeRejectsUsageBasedPrice(t *testing.T) { require.Equal(t, http.StatusBadRequest, response.Code) require.Contains(t, response.Body.String(), "price") } + +func TestInvoiceCustomerChargesRejectsLineIDs(t *testing.T) { + // given: the public action accepts collection settings without a line filter. + doc, err := api.GetSpec() + require.NoError(t, err) + + validationRouter, err := oasmiddleware.NewValidationRouter(t.Context(), doc, &oasmiddleware.ValidationRouterOpts{ + DeleteServers: true, + }) + require.NoError(t, err) + + router := chi.NewRouter() + router.Use(oasmiddleware.ValidateRequest(validationRouter, oasmiddleware.ValidateRequestOption{ + RouteNotFoundHook: oasmiddleware.OasRouteNotFoundErrorHook, + RouteValidationErrorHook: func(err error, w http.ResponseWriter, request *http.Request) bool { + return oasmiddleware.OasValidationErrorHook(request.Context(), err, w, request) + }, + FilterOptions: &openapi3filter.Options{ + AuthenticationFunc: openapi3filter.NoopAuthenticationFunc, + MultiError: true, + }, + })) + router.Post("/openmeter/customers/{customerId}/charges/invoice", func(http.ResponseWriter, *http.Request) { + t.Fatal("an unsupported line filter must not reach collection") + }) + + // when: a caller supplies the removed field. + request := httptest.NewRequest(http.MethodPost, "/openmeter/customers/01K3Q8QG4B9YJYJVQ3MZ3V8KT6/charges/invoice", strings.NewReader(`{ + "line_ids":["01K3Q8QG4B9YJYJVQ3MZ3V8KT7"] + }`)) + request.Header.Set("Content-Type", "application/json") + response := httptest.NewRecorder() + router.ServeHTTP(response, request) + + // then: validation rejects it instead of silently collecting every line. + require.Equal(t, http.StatusBadRequest, response.Code, response.Body.String()) + require.Contains(t, response.Body.String(), "line_ids") +} diff --git a/openmeter/billing/charges/service/pendinglines_test.go b/openmeter/billing/charges/service/pendinglines_test.go index 7b4d3b697c..47bb088b17 100644 --- a/openmeter/billing/charges/service/pendinglines_test.go +++ b/openmeter/billing/charges/service/pendinglines_test.go @@ -1,20 +1,27 @@ package service import ( + "bytes" "context" "encoding/json" "errors" + "net/http" + "net/http/httptest" "time" "github.com/alpacahq/alpacadecimal" + "github.com/oklog/ulid/v2" "github.com/samber/lo" + api "github.com/openmeterio/openmeter/api/v3" + "github.com/openmeterio/openmeter/api/v3/handlers/billinginvoices" "github.com/openmeterio/openmeter/openmeter/app" "github.com/openmeterio/openmeter/openmeter/billing" "github.com/openmeterio/openmeter/openmeter/billing/charges" "github.com/openmeterio/openmeter/openmeter/billing/charges/flatfee" "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" "github.com/openmeterio/openmeter/openmeter/billing/charges/models/creditrealization" + "github.com/openmeterio/openmeter/openmeter/billing/charges/models/ledgertransaction" "github.com/openmeterio/openmeter/openmeter/productcatalog" featurepkg "github.com/openmeterio/openmeter/openmeter/productcatalog/feature" "github.com/openmeterio/openmeter/openmeter/taxcode" @@ -716,3 +723,111 @@ func (s *InvoicableChargesTestSuite) TestCreatePendingInvoiceLinesRollsBackParti s.NoError(err) s.Empty(listedInvoices.Items) } + +func (s *InvoicableChargesTestSuite) TestInvoiceCustomerChargesAPIUsesChargeEngine() { + // given: credits are enabled, with a due charge and a future charge pending. + ctx := s.T().Context() + ns := s.GetUniqueNamespace("manual-collection-api") + s.TaxCodeEnv.ProvisionDefaultTaxCodes(s.T(), ns) + customInvoicing := s.SetupCustomInvoicing(ns) + s.ProvisionBillingProfile(ctx, ns, customInvoicing.App.GetID(), billingtest.WithManualApproval()) + cust := s.CreateTestCustomer(ns, "test-subject") + period := timeutil.ClosedPeriod{ + From: time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC), + To: time.Date(2026, 2, 1, 0, 0, 0, 0, time.UTC), + } + clock.FreezeTime(period.To) + defer clock.UnFreeze() + + allocationCalls := 0 + s.FlatFeeTestHandler.onAllocateCredits = func(_ context.Context, input flatfee.OnAllocateCreditsInput) (creditrealization.CreateAllocationInputs, error) { + allocationCalls++ + + return creditrealization.CreateAllocationInputs{ + { + ServicePeriod: input.ServicePeriod, + LedgerTransaction: ledgertransaction.GroupReference{ + TransactionGroupID: ulid.Make().String(), + }, + Amount: alpacadecimal.NewFromInt(4), + }, + }, nil + } + defer s.FlatFeeTestHandler.Reset() + + dueLine := billing.NewFlatFeeGatheringLine(billing.NewFlatFeeLineInput{ + Namespace: ns, + Period: period, + InvoiceAt: period.To, + ManagedBy: billing.ManuallyManagedLine, + Name: "due charge", + Currency: currencyx.FiatCode(USD), + PerUnitAmount: alpacadecimal.NewFromInt(10), + PaymentTerm: productcatalog.InAdvancePaymentTerm, + }) + futureLine := billing.NewFlatFeeGatheringLine(billing.NewFlatFeeLineInput{ + Namespace: ns, + Period: period, + InvoiceAt: period.To.Add(time.Hour), + ManagedBy: billing.ManuallyManagedLine, + Name: "future charge", + Currency: currencyx.FiatCode(USD), + PerUnitAmount: alpacadecimal.NewFromInt(10), + PaymentTerm: productcatalog.InAdvancePaymentTerm, + }) + pending, err := s.Charges.CreatePendingInvoiceLines(ctx, charges.CreatePendingInvoiceLinesInput{ + Customer: cust.GetID(), + Currency: currencyx.FiatCode(USD), + Lines: billing.NewCreatePendingInvoiceLines([]billing.GatheringLine{dueLine, futureLine}), + }) + s.Require().NoError(err) + s.Require().NotNil(pending) + s.Require().Len(pending.Lines, 2) + s.Equal(billing.LineEngineTypeChargeFlatFee, pending.Lines[0].Engine) + s.Require().NotNil(pending.Lines[0].ChargeID) + + // when: manually collecting through the v3 handler at the due line's cutoff. + body, err := json.Marshal(api.InvoiceCustomerChargesInput{ + AsOf: lo.ToPtr(period.To), + ProgressiveBillingOverride: lo.ToPtr(false), + }) + s.Require().NoError(err) + req := httptest.NewRequest(http.MethodPost, "/openmeter/customers/"+cust.ID+"/charges/invoice", bytes.NewReader(body)).WithContext(ctx) + req.Header.Set("Content-Type", "application/json") + res := httptest.NewRecorder() + handler := billinginvoices.New(func(context.Context) (string, error) { return ns, nil }, s.BillingService) + handler.InvoiceCustomerCharges().With(cust.ID).ServeHTTP(res, req) + + // then: collection invokes the charge engine, applies credits, and retains the future line. + s.Require().Equal(http.StatusCreated, res.Code, res.Body.String()) + var invoices []api.BillingInvoice + s.Require().NoError(json.Unmarshal(res.Body.Bytes(), &invoices)) + s.Require().Len(invoices, 1) + invoice, err := invoices[0].AsBillingInvoiceStandard() + s.Require().NoError(err) + s.Equal("6", invoice.Totals.Total) + s.Equal("4", invoice.Totals.CreditsTotal) + s.Equal(1, allocationCalls) + s.Require().NotNil(invoice.Lines) + s.Require().Len(*invoice.Lines, 1) + line, err := (*invoice.Lines)[0].AsBillingInvoiceStandardLine() + s.Require().NoError(err) + s.Require().NotNil(line.Charge) + s.Equal(*pending.Lines[0].ChargeID, line.Charge.Id) + s.Equal(lo.ToPtr(pending.Lines[0].ID), line.Id) + + charge := s.mustGetChargeByID(meta.ChargeID{Namespace: ns, ID: *pending.Lines[0].ChargeID}) + flatFeeCharge, err := charge.AsFlatFeeCharge() + s.Require().NoError(err) + s.Require().NotNil(flatFeeCharge.Realizations.CurrentRun) + s.Equal(lo.ToPtr(invoice.Id), flatFeeCharge.Realizations.CurrentRun.InvoiceID) + s.Equal(lo.ToPtr(pending.Lines[0].ID), flatFeeCharge.Realizations.CurrentRun.LineID) + + gathering, err := s.BillingService.GetGatheringInvoiceById(ctx, billing.GetGatheringInvoiceByIdInput{ + Invoice: pending.Invoice.GetInvoiceID(), + Expand: billing.GatheringInvoiceExpands{billing.GatheringInvoiceExpandLines}, + }) + s.Require().NoError(err) + s.Require().Len(gathering.Lines.OrEmpty(), 1) + s.Equal(pending.Lines[1].ID, gathering.Lines.OrEmpty()[0].ID) +} From 36c2d5838dbb778a1222a62a8a35771d81c0244c Mon Sep 17 00:00:00 2001 From: Peter Turi Date: Tue, 27 Oct 2026 14:48:38 +0100 Subject: [PATCH 2/6] fix(sdk): preserve documented invoice response types --- .../src/models/operations/customers.ts | 5 +-- .../typespec-typescript/src/emitter.tsx | 1 + .../typespec-typescript/src/request-types.ts | 22 ++++++----- .../typespec-typescript/src/sdk-operations.ts | 2 + .../test/response-types.test.ts | 37 +++++++++++++++++++ 5 files changed, 54 insertions(+), 13 deletions(-) create mode 100644 api/spec/packages/typespec-typescript/test/response-types.test.ts diff --git a/api/spec/packages/aip-client-javascript/src/models/operations/customers.ts b/api/spec/packages/aip-client-javascript/src/models/operations/customers.ts index 32c0743785..40fa507a0d 100644 --- a/api/spec/packages/aip-client-javascript/src/models/operations/customers.ts +++ b/api/spec/packages/aip-client-javascript/src/models/operations/customers.ts @@ -34,6 +34,7 @@ import type { EntitlementHistoryWindowSize, EntitlementPagePaginatedResponse, GetCreditBalanceParamsFilter, + Invoice, InvoiceCustomerChargesInput, ListCreditGrantsParamsFilter, ListCreditTransactionsParamsFilter, @@ -397,6 +398,4 @@ export type InvoiceCustomerChargesRequest = AcceptDateStrings<{ customerId: string body: InvoiceCustomerChargesInput }> -export type InvoiceCustomerChargesResponse = z.output< - typeof schemas.invoiceCustomerChargesResponse -> +export type InvoiceCustomerChargesResponse = Invoice[] diff --git a/api/spec/packages/typespec-typescript/src/emitter.tsx b/api/spec/packages/typespec-typescript/src/emitter.tsx index 5df7e461a8..1f0d6bfac8 100644 --- a/api/spec/packages/typespec-typescript/src/emitter.tsx +++ b/api/spec/packages/typespec-typescript/src/emitter.tsx @@ -231,6 +231,7 @@ export async function $onEmit(context: EmitContext) { ops, sdkOps, interfaces.refNameInput, + resolveInterface, bodyOverrides, ) sdkFiles.push({ diff --git a/api/spec/packages/typespec-typescript/src/request-types.ts b/api/spec/packages/typespec-typescript/src/request-types.ts index c3abe39287..457492acaa 100644 --- a/api/spec/packages/typespec-typescript/src/request-types.ts +++ b/api/spec/packages/typespec-typescript/src/request-types.ts @@ -100,17 +100,14 @@ function requestDecl(op: SdkOperation, bodyRef: string): string { return `${decl} Record` } -function responseDecl(op: SdkOperation): string { +function responseDecl(op: SdkOperation, responseRef: string): string { if (!op.hasResponse) { return `export type ${op.base}Response = void` } if (op.textResponseContentType) { return `export type ${op.base}Response = string` } - if (op.responseInterface) { - return `export type ${op.base}Response = ${op.responseInterface}` - } - return `export type ${op.base}Response = z.output` + return `export type ${op.base}Response = ${responseRef}` } export interface RequestTypes { @@ -131,6 +128,7 @@ export function requestTypesFor( ops: Operation[], sdkOps: SdkOperation[], refNameInput: RefName, + refNameOutput: RefName, jsonBodyOverrides: Map, ): RequestTypes { const opByBase = new Map( @@ -178,8 +176,15 @@ export function requestTypesFor( bodyRef = 'Record' } } + let responseRef = 'void' if (sdkOp.responseInterface) { - importAlias(sdkOp.responseInterface) + responseRef = importAlias(sdkOp.responseInterface) + } else if (sdkOp.responseType) { + const responseRefName: RefName = (type) => { + const name = refNameOutput(type) + return name ? importAlias(name) : undefined + } + responseRef = tsTypeOf(program, sdkOp.responseType, responseRefName) } if (sdkOp.queryParams.length > 0) { @@ -201,10 +206,7 @@ export function requestTypesFor( } } - const response = responseDecl(sdkOp) - if (response.includes('z.output')) { - usesZod = true - } + const response = responseDecl(sdkOp, responseRef) blocks.push(`${requestDecl(sdkOp, bodyRef)}\n${response}`) } diff --git a/api/spec/packages/typespec-typescript/src/sdk-operations.ts b/api/spec/packages/typespec-typescript/src/sdk-operations.ts index 229621a076..0348cdccf3 100644 --- a/api/spec/packages/typespec-typescript/src/sdk-operations.ts +++ b/api/spec/packages/typespec-typescript/src/sdk-operations.ts @@ -26,6 +26,7 @@ export interface SdkOperation { hasBody: boolean bodyOptional: boolean hasResponse: boolean + responseType?: Type /** Documented interface name for the success body, when it is a named model. */ responseInterface?: string /** The operation's `@summary` decorator text — a short, one-line label. */ @@ -190,6 +191,7 @@ export function sdkOperation( httpOp.parameters.body?.type !== undefined || bodyOverrides.has(base), bodyOptional: httpOp.parameters.body?.property?.optional ?? false, hasResponse: responseBody !== undefined, + responseType: responseBody?.type, requestBodyInterface, responseInterface, textResponseContentType: responseBody?.contentType?.startsWith('text/') diff --git a/api/spec/packages/typespec-typescript/test/response-types.test.ts b/api/spec/packages/typespec-typescript/test/response-types.test.ts new file mode 100644 index 0000000000..180efeea9c --- /dev/null +++ b/api/spec/packages/typespec-typescript/test/response-types.test.ts @@ -0,0 +1,37 @@ +import { describe, expect, it } from 'vitest' +import { EmitterTester } from './emit.js' + +describe('documented array responses', () => { + it('references the documented model for anonymous and named arrays', async () => { + const [result, diagnostics] = await EmitterTester.compileAndDiagnose(` + import "@typespec/http"; + import "@typespec/openapi"; + using TypeSpec.Http; + using TypeSpec.OpenAPI; + + namespace Widgets { + model Widget { @doc("The widget identifier.") id: string; } + model WidgetArray is Array; + interface Operations { + @get @operationId("list-widgets") list(): Widget[]; + @get @route("/named") @operationId("named-widgets") named(): WidgetArray; + } + } + @service namespace Api { + @route("/widgets") interface Endpoints extends Widgets.Operations {} + } + `) + expect(diagnostics.filter((d) => d.severity === 'error')).toEqual([]) + const operations = result.outputs['src/models/operations/widgets.ts']! + expect(operations).toContain('ListWidgetsResponse = Widget[]') + expect(operations).toContain('NamedWidgetsResponse = WidgetArray') + expect(result.outputs['src/models/types.ts']).toContain( + 'WidgetArray = Widget[]', + ) + expect(operations).toMatch(/import type \{[^}]*Widget[^}]*\} from/) + expect(operations).not.toContain('z.output') + expect(result.outputs['src/models/types.ts']).toContain( + 'The widget identifier.', + ) + }) +}) From 1bf59fd15912ec98056070e2b96223f39ee19613 Mon Sep 17 00:00:00 2001 From: Peter Turi Date: Tue, 27 Oct 2026 16:53:49 +0100 Subject: [PATCH 3/6] fix(billing): report fully gated collection and wrap invoice results --- .../packages/aip-client-javascript/README.md | 2 +- .../src/funcs/customers.ts | 6 +- .../aip-client-javascript/src/index.ts | 2 + .../src/models/operations/customers.ts | 6 +- .../src/models/schemas.ts | 27 +- .../aip-client-javascript/src/models/types.ts | 12 + .../src/sdk/customers.ts | 4 +- .../tests/nesting.spec.ts | 24 + .../aip/src/customers/charges/operations.tsp | 17 +- .../typespec-go/src/components/GoResource.tsx | 5 +- .../typespec-go/test/resource-render.test.ts | 23 - .../typespec-typescript/src/emitter.tsx | 1 - .../typespec-typescript/src/request-types.ts | 22 +- .../typespec-typescript/src/sdk-operations.ts | 2 - .../templates/tests/nesting.spec.ts | 24 + .../test/response-types.test.ts | 37 - api/v3/api.gen.go | 968 +++++++++--------- api/v3/client/README.md | 2 +- api/v3/client/customers_charges.go | 8 +- api/v3/client/models_customers.go | 6 + api/v3/client/transport_test.go | 46 + .../billinginvoices/invoice_charges.go | 12 +- .../billinginvoices/invoice_charges_test.go | 18 +- api/v3/openapi.yaml | 20 +- openmeter/billing/README.md | 10 +- openmeter/billing/charges/service/create.go | 5 + .../charges/service/pendinglines_test.go | 163 ++- openmeter/billing/errors.go | 1 + .../service/gatheringinvoicependinglines.go | 13 +- .../worker/collect/asynccollect_test.go | 31 + openmeter/billing/worker/collect/collect.go | 4 + .../worker/subscriptionsync/service/sync.go | 2 +- .../subscriptionsync/service/sync_test.go | 35 + test/billing/lineengine_test.go | 54 +- 34 files changed, 1005 insertions(+), 607 deletions(-) delete mode 100644 api/spec/packages/typespec-typescript/test/response-types.test.ts diff --git a/api/spec/packages/aip-client-javascript/README.md b/api/spec/packages/aip-client-javascript/README.md index e6bac49ee3..7fcda47bbd 100644 --- a/api/spec/packages/aip-client-javascript/README.md +++ b/api/spec/packages/aip-client-javascript/README.md @@ -316,7 +316,7 @@ The full call path, HTTP route, and a short description are listed below. | `client.customers.credits.transactions.list` | `GET /openmeter/customers/{customerId}/credits/transactions` | List credit transactions for a customer. Returns an immutable, chronological record of credit movements: funded credits and consumed credits. Transactions are returned in reverse chronological order by default. | | `client.customers.charges.list` | `GET /openmeter/customers/{customerId}/charges` | List customer charges. Returns the customer's charges that are represented as either flat fee or usage-based charges. | | `client.customers.charges.create` | `POST /openmeter/customers/{customerId}/charges` | Create customer charge. | -| `client.customers.charges.invoice` | `POST /openmeter/customers/{customerId}/charges/invoice` | Invoice a customer's charges outside the normal billing cycle. Collection alignment is bypassed. The customer's progressive billing setting applies unless explicitly overridden. All eligible pending lines are considered. Returns the created standard invoices, one per collected currency. Lines blocked by their owning engine remain pending; if all candidates are blocked, the response is empty. If no lines are eligible, returns a validation error. | +| `client.customers.charges.invoice` | `POST /openmeter/customers/{customerId}/charges/invoice` | Invoice a customer's charges outside the normal billing cycle. Collection alignment is bypassed. The customer's progressive billing setting applies unless explicitly overridden. All eligible pending lines are considered. Returns the created standard invoices, one per collected currency. Lines blocked by their owning engine remain pending. If all eligible lines are blocked or no lines are eligible, returns a validation error. | ### Entitlements diff --git a/api/spec/packages/aip-client-javascript/src/funcs/customers.ts b/api/spec/packages/aip-client-javascript/src/funcs/customers.ts index 4e1dcb0269..1bd8ed111c 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/customers.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/customers.ts @@ -1618,8 +1618,8 @@ export function createCustomerCharges( * explicitly overridden. All eligible pending lines are considered. * * Returns the created standard invoices, one per collected currency. Lines blocked - * by their owning engine remain pending; if all candidates are blocked, the - * response is empty. If no lines are eligible, returns a validation error. + * by their owning engine remain pending. If all eligible lines are blocked or no + * lines are eligible, returns a validation error. * * POST /openmeter/customers/{customerId}/charges/invoice */ @@ -1645,7 +1645,7 @@ export function invoiceCustomerCharges( return encodeURIComponent(String(pathParams.customerId)) })()}/charges/invoice` const body = toWire(req.body, schemas.invoiceCustomerChargesBody) - if (client._options.validate) { + if (client._options.validate && body !== undefined) { assertValid(schemas.invoiceCustomerChargesBodyWire, body) } return http(client) diff --git a/api/spec/packages/aip-client-javascript/src/index.ts b/api/spec/packages/aip-client-javascript/src/index.ts index cfc28dec2f..15ce587180 100644 --- a/api/spec/packages/aip-client-javascript/src/index.ts +++ b/api/spec/packages/aip-client-javascript/src/index.ts @@ -454,6 +454,7 @@ export type { SubscriptionMigrateResponse, ChargeFlatFee, ChargeUsageBased, + InvoiceCustomerChargesResult, InvoicePagePaginatedResponse, ChargePagePaginatedResponse, StringFieldFilter, @@ -605,6 +606,7 @@ export type { SubscriptionMigrateResponseInput, ChargeFlatFeeInput, ChargeUsageBasedInput, + InvoiceCustomerChargesResultInput, InvoicePagePaginatedResponseInput, ChargePagePaginatedResponseInput, WorkflowPaymentSettingsInput, diff --git a/api/spec/packages/aip-client-javascript/src/models/operations/customers.ts b/api/spec/packages/aip-client-javascript/src/models/operations/customers.ts index 40fa507a0d..1f4d42232d 100644 --- a/api/spec/packages/aip-client-javascript/src/models/operations/customers.ts +++ b/api/spec/packages/aip-client-javascript/src/models/operations/customers.ts @@ -34,8 +34,8 @@ import type { EntitlementHistoryWindowSize, EntitlementPagePaginatedResponse, GetCreditBalanceParamsFilter, - Invoice, InvoiceCustomerChargesInput, + InvoiceCustomerChargesResult, ListCreditGrantsParamsFilter, ListCreditTransactionsParamsFilter, ListCustomerChargesParamsFilter, @@ -396,6 +396,6 @@ export type CreateCustomerChargesResponse = Charge export type InvoiceCustomerChargesRequest = AcceptDateStrings<{ customerId: string - body: InvoiceCustomerChargesInput + body?: InvoiceCustomerChargesInput }> -export type InvoiceCustomerChargesResponse = Invoice[] +export type InvoiceCustomerChargesResponse = InvoiceCustomerChargesResult diff --git a/api/spec/packages/aip-client-javascript/src/models/schemas.ts b/api/spec/packages/aip-client-javascript/src/models/schemas.ts index b40f5a6282..5425c057c8 100644 --- a/api/spec/packages/aip-client-javascript/src/models/schemas.ts +++ b/api/spec/packages/aip-client-javascript/src/models/schemas.ts @@ -8441,6 +8441,17 @@ export const chargeUsageBased = z }) .describe('A usage-based charge for a customer.') +export const invoiceCustomerChargesResult = z + .object({ + invoices: z + .array(invoice) + .describe('Created standard invoices, one per collected currency.'), + }) + + .describe( + "Result of manually collecting a customer's pending lines into invoices.", + ) + export const invoicePagePaginatedResponse = z .object({ data: z.array(invoice), @@ -8992,7 +9003,7 @@ export const invoiceCustomerChargesPathParams = z.object({ export const invoiceCustomerChargesBody = invoiceCustomerChargesInput -export const invoiceCustomerChargesResponse = z.array(invoice) +export const invoiceCustomerChargesResponse = invoiceCustomerChargesResult export const createSubscriptionBody = subscriptionCreate @@ -18260,6 +18271,17 @@ export const chargeUsageBasedWire = z }) .describe('A usage-based charge for a customer.') +export const invoiceCustomerChargesResultWire = z + .strictObject({ + invoices: z + .array(invoiceWire) + .describe('Created standard invoices, one per collected currency.'), + }) + + .describe( + "Result of manually collecting a customer's pending lines into invoices.", + ) + export const invoicePagePaginatedResponseWire = z .strictObject({ data: z.array(invoiceWire), @@ -18872,7 +18894,8 @@ export const invoiceCustomerChargesPathParamsWire = z.object({ export const invoiceCustomerChargesBodyWire = invoiceCustomerChargesInputWire -export const invoiceCustomerChargesResponseWire = z.array(invoiceWire) +export const invoiceCustomerChargesResponseWire = + invoiceCustomerChargesResultWire export const createSubscriptionBodyWire = subscriptionCreateWire diff --git a/api/spec/packages/aip-client-javascript/src/models/types.ts b/api/spec/packages/aip-client-javascript/src/models/types.ts index 8847cb2e0f..d8f804f62b 100644 --- a/api/spec/packages/aip-client-javascript/src/models/types.ts +++ b/api/spec/packages/aip-client-javascript/src/models/types.ts @@ -8008,6 +8008,12 @@ export interface ChargeUsageBased { systemIntent?: ChargeUsageBasedSystemIntent } +/** Result of manually collecting a customer's pending lines into invoices. */ +export interface InvoiceCustomerChargesResult { + /** Created standard invoices, one per collected currency. */ + invoices: Invoice[] +} + /** Page paginated response. */ export interface InvoicePagePaginatedResponse { data: Invoice[] @@ -11357,6 +11363,12 @@ export interface ChargeUsageBasedInput { systemIntent?: ChargeUsageBasedSystemIntentInput } +/** Result of manually collecting a customer's pending lines into invoices. */ +export interface InvoiceCustomerChargesResultInput { + /** Created standard invoices, one per collected currency. */ + invoices: InvoiceInput[] +} + /** Page paginated response. */ export interface InvoicePagePaginatedResponseInput { data: InvoiceInput[] diff --git a/api/spec/packages/aip-client-javascript/src/sdk/customers.ts b/api/spec/packages/aip-client-javascript/src/sdk/customers.ts index f410efb414..d436a1de50 100644 --- a/api/spec/packages/aip-client-javascript/src/sdk/customers.ts +++ b/api/spec/packages/aip-client-javascript/src/sdk/customers.ts @@ -850,8 +850,8 @@ export class CustomersCharges { * explicitly overridden. All eligible pending lines are considered. * * Returns the created standard invoices, one per collected currency. Lines blocked - * by their owning engine remain pending; if all candidates are blocked, the - * response is empty. If no lines are eligible, returns a validation error. + * by their owning engine remain pending. If all eligible lines are blocked or no + * lines are eligible, returns a validation error. * * POST /openmeter/customers/{customerId}/charges/invoice */ diff --git a/api/spec/packages/aip-client-javascript/tests/nesting.spec.ts b/api/spec/packages/aip-client-javascript/tests/nesting.spec.ts index 79f63abd52..a52423845a 100644 --- a/api/spec/packages/aip-client-javascript/tests/nesting.spec.ts +++ b/api/spec/packages/aip-client-javascript/tests/nesting.spec.ts @@ -15,6 +15,30 @@ function lastUrl(): string { } describe('nested sub-clients', () => { + it('returns collected invoices inside the customer charges result', async () => { + fetchMock.route('*', { + status: 201, + body: { invoices: [] }, + headers: { 'Content-Type': 'application/json' }, + }) + const sdk = new OpenMeter({ + baseUrl: 'https://eu.api.konghq.com/v3', + apiKey: 'k', + fetch, + validate: true, + }) + + const result = await sdk.customers.charges.invoice({ + customerId: '01ARZ3NDEKTSV4RRFFQ69G5FAV', + body: {}, + }) + + expect(result.invoices).toEqual([]) + expect(lastUrl()).toBe( + 'https://eu.api.konghq.com/v3/openmeter/customers/01ARZ3NDEKTSV4RRFFQ69G5FAV/charges/invoice', + ) + }) + it('routes customers.charges.list() to the charges sub-resource', async () => { fetchMock.route('*', { body: { data: [], meta: { page: { number: 1, size: 10, total: 0 } } }, diff --git a/api/spec/packages/aip/src/customers/charges/operations.tsp b/api/spec/packages/aip/src/customers/charges/operations.tsp index b02bd2c609..8d55dd65e7 100644 --- a/api/spec/packages/aip/src/customers/charges/operations.tsp +++ b/api/spec/packages/aip/src/customers/charges/operations.tsp @@ -71,6 +71,17 @@ model InvoiceCustomerChargesInput { progressive_billing_override?: boolean; } +/** + * Result of manually collecting a customer's pending lines into invoices. + */ +@friendlyName("InvoiceCustomerChargesResult") +model InvoiceCustomerChargesResult { + /** + * Created standard invoices, one per collected currency. + */ + invoices: Invoices.Invoice[]; +} + namespace Charges { interface CustomerChargesOperations { /** @@ -154,8 +165,8 @@ namespace Charges { * explicitly overridden. All eligible pending lines are considered. * * Returns the created standard invoices, one per collected currency. Lines blocked - * by their owning engine remain pending; if all candidates are blocked, the - * response is empty. If no lines are eligible, returns a validation error. + * by their owning engine remain pending. If all eligible lines are blocked or no + * lines are eligible, returns a validation error. */ @post @route("/invoice") @@ -168,7 +179,7 @@ namespace Charges { ): | { @statusCode _: 201; - @body body: Invoices.Invoice[]; + @body body: InvoiceCustomerChargesResult; } | Common.NotFound | Common.ErrorResponses; diff --git a/api/spec/packages/typespec-go/src/components/GoResource.tsx b/api/spec/packages/typespec-go/src/components/GoResource.tsx index 534af4ad56..72c950c6c0 100644 --- a/api/spec/packages/typespec-go/src/components/GoResource.tsx +++ b/api/spec/packages/typespec-go/src/components/GoResource.tsx @@ -963,8 +963,9 @@ function pageElement(program: Program, response: Type): string { return readReferenceName(program, element) } -export function readReferenceName(program: Program, type: Type): string { - return goType(program, type).text ?? typeName(program, type) +function readReferenceName(program: Program, type: Type): string { + const mapped = goType(program, type).type + return typeof mapped === 'string' ? mapped : typeName(program, type) } // Locals every generated method body may declare (including the receiver and diff --git a/api/spec/packages/typespec-go/test/resource-render.test.ts b/api/spec/packages/typespec-go/test/resource-render.test.ts index 4955ad4105..bc2adc9990 100644 --- a/api/spec/packages/typespec-go/test/resource-render.test.ts +++ b/api/spec/packages/typespec-go/test/resource-render.test.ts @@ -13,7 +13,6 @@ import { isPlainPageEnvelope, localName, queryScalarValue, - readReferenceName, resolveListParamsNames, } from '../dist/components/GoResource.js' @@ -71,28 +70,6 @@ const pageFixturePreamble = ` } ` -describe('response collection types', () => { - it('uses slice types for anonymous and named array responses', async () => { - const { program, operations } = await compileResource(` - @service namespace Test; - model Item { id: string; } - model Items is Array; - - @route("/items") - interface Operations { - @get op list(): Item[]; - @get @route("/named") op named(): Items; - } - `) - - expect(operations).toHaveLength(2) - for (const operation of operations) { - expect(operation.response).toBeDefined() - expect(readReferenceName(program, operation.response!)).toBe('[]Item') - } - }) -}) - describe('list params struct naming', () => { it('splits shared-element params structs when query shapes differ', async () => { const { program, operations } = await compileResource(` diff --git a/api/spec/packages/typespec-typescript/src/emitter.tsx b/api/spec/packages/typespec-typescript/src/emitter.tsx index 1f0d6bfac8..5df7e461a8 100644 --- a/api/spec/packages/typespec-typescript/src/emitter.tsx +++ b/api/spec/packages/typespec-typescript/src/emitter.tsx @@ -231,7 +231,6 @@ export async function $onEmit(context: EmitContext) { ops, sdkOps, interfaces.refNameInput, - resolveInterface, bodyOverrides, ) sdkFiles.push({ diff --git a/api/spec/packages/typespec-typescript/src/request-types.ts b/api/spec/packages/typespec-typescript/src/request-types.ts index 457492acaa..c3abe39287 100644 --- a/api/spec/packages/typespec-typescript/src/request-types.ts +++ b/api/spec/packages/typespec-typescript/src/request-types.ts @@ -100,14 +100,17 @@ function requestDecl(op: SdkOperation, bodyRef: string): string { return `${decl} Record` } -function responseDecl(op: SdkOperation, responseRef: string): string { +function responseDecl(op: SdkOperation): string { if (!op.hasResponse) { return `export type ${op.base}Response = void` } if (op.textResponseContentType) { return `export type ${op.base}Response = string` } - return `export type ${op.base}Response = ${responseRef}` + if (op.responseInterface) { + return `export type ${op.base}Response = ${op.responseInterface}` + } + return `export type ${op.base}Response = z.output` } export interface RequestTypes { @@ -128,7 +131,6 @@ export function requestTypesFor( ops: Operation[], sdkOps: SdkOperation[], refNameInput: RefName, - refNameOutput: RefName, jsonBodyOverrides: Map, ): RequestTypes { const opByBase = new Map( @@ -176,15 +178,8 @@ export function requestTypesFor( bodyRef = 'Record' } } - let responseRef = 'void' if (sdkOp.responseInterface) { - responseRef = importAlias(sdkOp.responseInterface) - } else if (sdkOp.responseType) { - const responseRefName: RefName = (type) => { - const name = refNameOutput(type) - return name ? importAlias(name) : undefined - } - responseRef = tsTypeOf(program, sdkOp.responseType, responseRefName) + importAlias(sdkOp.responseInterface) } if (sdkOp.queryParams.length > 0) { @@ -206,7 +201,10 @@ export function requestTypesFor( } } - const response = responseDecl(sdkOp, responseRef) + const response = responseDecl(sdkOp) + if (response.includes('z.output')) { + usesZod = true + } blocks.push(`${requestDecl(sdkOp, bodyRef)}\n${response}`) } diff --git a/api/spec/packages/typespec-typescript/src/sdk-operations.ts b/api/spec/packages/typespec-typescript/src/sdk-operations.ts index 0348cdccf3..229621a076 100644 --- a/api/spec/packages/typespec-typescript/src/sdk-operations.ts +++ b/api/spec/packages/typespec-typescript/src/sdk-operations.ts @@ -26,7 +26,6 @@ export interface SdkOperation { hasBody: boolean bodyOptional: boolean hasResponse: boolean - responseType?: Type /** Documented interface name for the success body, when it is a named model. */ responseInterface?: string /** The operation's `@summary` decorator text — a short, one-line label. */ @@ -191,7 +190,6 @@ export function sdkOperation( httpOp.parameters.body?.type !== undefined || bodyOverrides.has(base), bodyOptional: httpOp.parameters.body?.property?.optional ?? false, hasResponse: responseBody !== undefined, - responseType: responseBody?.type, requestBodyInterface, responseInterface, textResponseContentType: responseBody?.contentType?.startsWith('text/') diff --git a/api/spec/packages/typespec-typescript/templates/tests/nesting.spec.ts b/api/spec/packages/typespec-typescript/templates/tests/nesting.spec.ts index feb73019f3..fe5a815f5c 100644 --- a/api/spec/packages/typespec-typescript/templates/tests/nesting.spec.ts +++ b/api/spec/packages/typespec-typescript/templates/tests/nesting.spec.ts @@ -13,6 +13,30 @@ function lastUrl(): string { } describe('nested sub-clients', () => { + it('returns collected invoices inside the customer charges result', async () => { + fetchMock.route('*', { + status: 201, + body: { invoices: [] }, + headers: { 'Content-Type': 'application/json' }, + }) + const sdk = new OpenMeter({ + baseUrl: 'https://eu.api.konghq.com/v3', + apiKey: 'k', + fetch, + validate: true, + }) + + const result = await sdk.customers.charges.invoice({ + customerId: '01ARZ3NDEKTSV4RRFFQ69G5FAV', + body: {}, + }) + + expect(result.invoices).toEqual([]) + expect(lastUrl()).toBe( + 'https://eu.api.konghq.com/v3/openmeter/customers/01ARZ3NDEKTSV4RRFFQ69G5FAV/charges/invoice', + ) + }) + it('routes customers.charges.list() to the charges sub-resource', async () => { fetchMock.route('*', { body: { data: [], meta: { page: { number: 1, size: 10, total: 0 } } }, diff --git a/api/spec/packages/typespec-typescript/test/response-types.test.ts b/api/spec/packages/typespec-typescript/test/response-types.test.ts deleted file mode 100644 index 180efeea9c..0000000000 --- a/api/spec/packages/typespec-typescript/test/response-types.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { EmitterTester } from './emit.js' - -describe('documented array responses', () => { - it('references the documented model for anonymous and named arrays', async () => { - const [result, diagnostics] = await EmitterTester.compileAndDiagnose(` - import "@typespec/http"; - import "@typespec/openapi"; - using TypeSpec.Http; - using TypeSpec.OpenAPI; - - namespace Widgets { - model Widget { @doc("The widget identifier.") id: string; } - model WidgetArray is Array; - interface Operations { - @get @operationId("list-widgets") list(): Widget[]; - @get @route("/named") @operationId("named-widgets") named(): WidgetArray; - } - } - @service namespace Api { - @route("/widgets") interface Endpoints extends Widgets.Operations {} - } - `) - expect(diagnostics.filter((d) => d.severity === 'error')).toEqual([]) - const operations = result.outputs['src/models/operations/widgets.ts']! - expect(operations).toContain('ListWidgetsResponse = Widget[]') - expect(operations).toContain('NamedWidgetsResponse = WidgetArray') - expect(result.outputs['src/models/types.ts']).toContain( - 'WidgetArray = Widget[]', - ) - expect(operations).toMatch(/import type \{[^}]*Widget[^}]*\} from/) - expect(operations).not.toContain('z.output') - expect(result.outputs['src/models/types.ts']).toContain( - 'The widget identifier.', - ) - }) -}) diff --git a/api/v3/api.gen.go b/api/v3/api.gen.go index 099fcce34f..5aaedaf4c3 100644 --- a/api/v3/api.gen.go +++ b/api/v3/api.gen.go @@ -9611,6 +9611,12 @@ type InvoiceCustomerChargesInput struct { ProgressiveBillingOverride *bool `json:"progressive_billing_override,omitempty"` } +// InvoiceCustomerChargesResult Result of manually collecting a customer's pending lines into invoices. +type InvoiceCustomerChargesResult struct { + // Invoices Created standard invoices, one per collected currency. + Invoices []BillingInvoice `json:"invoices"` +} + // InvoicePagePaginatedResponse Page paginated response. type InvoicePagePaginatedResponse struct { Data []BillingInvoice `json:"data"` @@ -23708,487 +23714,487 @@ var swaggerSpec = []string{ "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", - "YwYf33/8/wYA", + "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", } // decodeSpec returns the embedded OpenAPI spec as raw JSON bytes, diff --git a/api/v3/client/README.md b/api/v3/client/README.md index 8de2050329..389058b1c7 100644 --- a/api/v3/client/README.md +++ b/api/v3/client/README.md @@ -188,7 +188,7 @@ The full call path, HTTP route, and a short description are listed below. | `om.Customers.Credits.Transactions.List` | `GET /openmeter/customers/{customerId}/credits/transactions` | List credit transactions for a customer. Returns an immutable, chronological record of credit movements: funded credits and consumed credits. Transactions are returned in reverse chronological order by default. | | `om.Customers.Charges.List` | `GET /openmeter/customers/{customerId}/charges` | List customer charges. Returns the customer's charges that are represented as either flat fee or usage-based charges. | | `om.Customers.Charges.Create` | `POST /openmeter/customers/{customerId}/charges` | Create customer charge. | -| `om.Customers.Charges.Invoice` | `POST /openmeter/customers/{customerId}/charges/invoice` | Invoice a customer's charges outside the normal billing cycle. Collection alignment is bypassed. The customer's progressive billing setting applies unless explicitly overridden. All eligible pending lines are considered. Returns the created standard invoices, one per collected currency. Lines blocked by their owning engine remain pending; if all candidates are blocked, the response is empty. If no lines are eligible, returns a validation error. | +| `om.Customers.Charges.Invoice` | `POST /openmeter/customers/{customerId}/charges/invoice` | Invoice a customer's charges outside the normal billing cycle. Collection alignment is bypassed. The customer's progressive billing setting applies unless explicitly overridden. All eligible pending lines are considered. Returns the created standard invoices, one per collected currency. Lines blocked by their owning engine remain pending. If all eligible lines are blocked or no lines are eligible, returns a validation error. | ### Entitlements diff --git a/api/v3/client/customers_charges.go b/api/v3/client/customers_charges.go index dad73d29ad..682eb76d88 100644 --- a/api/v3/client/customers_charges.go +++ b/api/v3/client/customers_charges.go @@ -139,9 +139,9 @@ func (s *CustomersChargesService) Create(ctx context.Context, customerID string, // explicitly overridden. All eligible pending lines are considered. // // Returns the created standard invoices, one per collected currency. Lines blocked -// by their owning engine remain pending; if all candidates are blocked, the -// response is empty. If no lines are eligible, returns a validation error. -func (s *CustomersChargesService) Invoice(ctx context.Context, customerID string, request *InvoiceCustomerChargesInput) (*[]Invoice, error) { +// by their owning engine remain pending. If all eligible lines are blocked or no +// lines are eligible, returns a validation error. +func (s *CustomersChargesService) Invoice(ctx context.Context, customerID string, request *InvoiceCustomerChargesInput) (*InvoiceCustomerChargesResult, error) { if customerID == "" { return nil, fmt.Errorf("openmeter: %s must not be empty: %w", "customerID", ErrEmptyID) } @@ -155,7 +155,7 @@ func (s *CustomersChargesService) Invoice(ctx context.Context, customerID string return nil, err } - var out []Invoice + var out InvoiceCustomerChargesResult if err := s.client.doJSON(req, &out); err != nil { return nil, err } diff --git a/api/v3/client/models_customers.go b/api/v3/client/models_customers.go index 5a506dba1f..2a7194bac1 100644 --- a/api/v3/client/models_customers.go +++ b/api/v3/client/models_customers.go @@ -1762,6 +1762,12 @@ type InvoiceCustomerChargesInput struct { ProgressiveBillingOverride *bool `json:"progressive_billing_override,omitempty"` } +// Result of manually collecting a customer's pending lines into invoices. +type InvoiceCustomerChargesResult struct { + // Created standard invoices, one per collected currency. + Invoices []Invoice `json:"invoices"` +} + // Recurring period input. The anchor is optional; the owning resource defines the // default, typically its creation time. type RecurringPeriodInput struct { diff --git a/api/v3/client/transport_test.go b/api/v3/client/transport_test.go index 77fc101106..13962edcbd 100644 --- a/api/v3/client/transport_test.go +++ b/api/v3/client/transport_test.go @@ -82,6 +82,52 @@ func newTestClient(t *testing.T, handler http.Handler, opts ...openmeter.Option) return c } +func TestInvoiceCustomerChargesResponse(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + body string + invoiceIDs []string + }{ + { + name: "collected invoices", + body: `{"invoices":[{"type":"standard","id":"invoice-1"}]}`, + invoiceIDs: []string{"invoice-1"}, + }, + { + name: "empty invoices", + body: `{"invoices":[]}`, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + rec := &requestRecorder{} + om := newTestClient(t, rec.handler(http.StatusCreated, test.body)) + + result, err := om.Customers.Charges.Invoice(t.Context(), "customer-1", nil) + if err != nil { + t.Fatalf("Customers.Charges.Invoice: %v", err) + } + + if result == nil || result.Invoices == nil || len(result.Invoices) != len(test.invoiceIDs) { + t.Fatalf("unexpected invoice result: %+v", result) + } + + for i, id := range test.invoiceIDs { + invoice, err := result.Invoices[i].AsInvoiceStandard() + if err != nil { + t.Fatalf("decoding collected invoice: %v", err) + } + + if invoice.ID != id { + t.Errorf("invoice ID = %q, want %q", invoice.ID, id) + } + } + }) + } +} + func TestBaseURLJoining(t *testing.T) { t.Parallel() diff --git a/api/v3/handlers/billinginvoices/invoice_charges.go b/api/v3/handlers/billinginvoices/invoice_charges.go index 0cc9f1b0c4..9e4fa5466b 100644 --- a/api/v3/handlers/billinginvoices/invoice_charges.go +++ b/api/v3/handlers/billinginvoices/invoice_charges.go @@ -19,7 +19,7 @@ type ( billing.InvoicePendingLinesInput Options []billing.InvoicePendingLinesOption } - InvoiceCustomerChargesResponse = []api.BillingInvoice + InvoiceCustomerChargesResponse = api.InvoiceCustomerChargesResult InvoiceCustomerChargesHandler = httptransport.HandlerWithArgs[InvoiceCustomerChargesRequest, InvoiceCustomerChargesResponse, string] ) @@ -60,17 +60,19 @@ func (h *handler) InvoiceCustomerCharges() InvoiceCustomerChargesHandler { func(ctx context.Context, input InvoiceCustomerChargesRequest) (InvoiceCustomerChargesResponse, error) { invoices, err := h.service.InvoicePendingLines(ctx, input.InvoicePendingLinesInput, input.Options...) if err != nil { - return nil, fmt.Errorf("collecting customer pending lines: %w", err) + return InvoiceCustomerChargesResponse{}, fmt.Errorf("collecting customer pending lines: %w", err) } - out := make(InvoiceCustomerChargesResponse, 0, len(invoices)) + out := InvoiceCustomerChargesResponse{ + Invoices: make([]api.BillingInvoice, 0, len(invoices)), + } for _, invoice := range invoices { mappedInvoice, err := ToAPIStandardInvoice(invoice) if err != nil { - return nil, fmt.Errorf("mapping collected invoice: %w", err) + return InvoiceCustomerChargesResponse{}, fmt.Errorf("mapping collected invoice: %w", err) } - out = append(out, mappedInvoice) + out.Invoices = append(out.Invoices, mappedInvoice) } return out, nil diff --git a/api/v3/handlers/billinginvoices/invoice_charges_test.go b/api/v3/handlers/billinginvoices/invoice_charges_test.go index 5d95459505..c39785e2df 100644 --- a/api/v3/handlers/billinginvoices/invoice_charges_test.go +++ b/api/v3/handlers/billinginvoices/invoice_charges_test.go @@ -100,24 +100,24 @@ func TestInvoiceCustomerChargesManualCollection(t *testing.T) { // then: both standard invoices are returned after forwarding the manual options. require.Equal(t, http.StatusCreated, res.Code, res.Body.String()) require.True(t, called) - var invoices []api.BillingInvoice - require.NoError(t, json.Unmarshal(res.Body.Bytes(), &invoices)) - require.Len(t, invoices, 2) - usdInvoice, err := invoices[0].AsBillingInvoiceStandard() + var result api.InvoiceCustomerChargesResult + require.NoError(t, json.Unmarshal(res.Body.Bytes(), &result)) + require.Len(t, result.Invoices, 2) + usdInvoice, err := result.Invoices[0].AsBillingInvoiceStandard() require.NoError(t, err) require.Equal(t, "usd-invoice", usdInvoice.Id) require.Equal(t, api.CurrencyCode("USD"), usdInvoice.Currency) - eurInvoice, err := invoices[1].AsBillingInvoiceStandard() + eurInvoice, err := result.Invoices[1].AsBillingInvoiceStandard() require.NoError(t, err) require.Equal(t, api.CurrencyCode("EUR"), eurInvoice.Currency) }) } } -func TestInvoiceCustomerChargesAllBlockedReturnsEmptyArray(t *testing.T) { +func TestInvoiceCustomerChargesAllBlockedReturnsValidationError(t *testing.T) { handler := New(func(context.Context) (string, error) { return "ns", nil }, invoiceChargesBillingService{ collect: func(context.Context, billing.InvoicePendingLinesInput, ...billing.InvoicePendingLinesOption) ([]billing.StandardInvoice, error) { - return nil, nil + return nil, billing.ValidationError{Err: billing.ErrInvoiceCreateAllLinesGated} }, }) req := httptest.NewRequest(http.MethodPost, "/", nil) @@ -125,8 +125,8 @@ func TestInvoiceCustomerChargesAllBlockedReturnsEmptyArray(t *testing.T) { handler.InvoiceCustomerCharges().With(invoiceChargesCustomerID).ServeHTTP(res, req) - require.Equal(t, http.StatusCreated, res.Code, res.Body.String()) - require.JSONEq(t, `[]`, res.Body.String()) + require.Equal(t, http.StatusBadRequest, res.Code, res.Body.String()) + require.Contains(t, res.Body.String(), "all eligible pending lines are blocked from invoice assignment") } func TestInvoiceCustomerChargesErrors(t *testing.T) { diff --git a/api/v3/openapi.yaml b/api/v3/openapi.yaml index b83cf7c1e6..922c1211e9 100644 --- a/api/v3/openapi.yaml +++ b/api/v3/openapi.yaml @@ -1621,8 +1621,8 @@ paths: explicitly overridden. All eligible pending lines are considered. Returns the created standard invoices, one per collected currency. Lines blocked - by their owning engine remain pending; if all candidates are blocked, the - response is empty. If no lines are eligible, returns a validation error. + by their owning engine remain pending. If all eligible lines are blocked or no + lines are eligible, returns a validation error. parameters: - name: customerId in: path @@ -1635,9 +1635,7 @@ paths: content: application/json: schema: - type: array - items: - $ref: '#/components/schemas/BillingInvoice' + $ref: '#/components/schemas/InvoiceCustomerChargesResult' '400': $ref: '#/components/responses/BadRequest' '401': @@ -17112,6 +17110,18 @@ components: enabled, eligible usage-based lines can be collected for a partial period. additionalProperties: false description: Options for manually collecting a customer's pending lines into invoices. + InvoiceCustomerChargesResult: + type: object + required: + - invoices + properties: + invoices: + type: array + items: + $ref: '#/components/schemas/BillingInvoice' + description: Created standard invoices, one per collected currency. + additionalProperties: false + description: Result of manually collecting a customer's pending lines into invoices. InvoicePagePaginatedResponse: type: object required: diff --git a/openmeter/billing/README.md b/openmeter/billing/README.md index c9cd182590..78c895da5c 100644 --- a/openmeter/billing/README.md +++ b/openmeter/billing/README.md @@ -157,9 +157,13 @@ charge engines handle charge-backed lines. Before splitting or materializing otherwise billable gathering lines, billing asks each line engine to gate their invoice assignment. Blocked lines remain on the gathering invoice and do not consume the invoice line limit. A gate may -persist engine-owned state explaining its decision; when every candidate is -blocked, collection succeeds without creating an invoice so those writes can -commit. Gate errors abort collection and roll back its writes. Charge-backed +persist engine-owned state explaining its decision. When every candidate is +blocked, collection finishes its transaction without creating an invoice, then +returns `ErrInvoiceCreateAllLinesGated` as a validation error. Callers with an +enclosing transaction must handle this deferred outcome so gate diagnostics can +commit. Automatic collection, subscription sync, and invoice-now during charge +creation treat it as deferred work. Gate errors abort collection and roll back +its writes. Charge-backed usage lines whose required feature or meter is unavailable are blocked here; the charge records the dependency issue and a later collection attempt re-evaluates it. diff --git a/openmeter/billing/charges/service/create.go b/openmeter/billing/charges/service/create.go index e205e7c940..c338973a59 100644 --- a/openmeter/billing/charges/service/create.go +++ b/openmeter/billing/charges/service/create.go @@ -2,6 +2,7 @@ package service import ( "context" + "errors" "fmt" "sync" "time" @@ -453,6 +454,10 @@ func (s *service) invokeInvoiceNowOnCreate(ctx context.Context, invoiceNowLines }, billing.WithBypassCollectionAlignment(), ); err != nil { + if errors.Is(err, billing.ErrInvoiceCreateAllLinesGated) { + continue + } + return fmt.Errorf("invoking invoice now on create: %w", err) } } diff --git a/openmeter/billing/charges/service/pendinglines_test.go b/openmeter/billing/charges/service/pendinglines_test.go index 47bb088b17..acca61fbc4 100644 --- a/openmeter/billing/charges/service/pendinglines_test.go +++ b/openmeter/billing/charges/service/pendinglines_test.go @@ -5,13 +5,16 @@ import ( "context" "encoding/json" "errors" + "fmt" "net/http" "net/http/httptest" + "testing" "time" "github.com/alpacahq/alpacadecimal" "github.com/oklog/ulid/v2" "github.com/samber/lo" + "github.com/stretchr/testify/require" api "github.com/openmeterio/openmeter/api/v3" "github.com/openmeterio/openmeter/api/v3/handlers/billinginvoices" @@ -22,17 +25,169 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" "github.com/openmeterio/openmeter/openmeter/billing/charges/models/creditrealization" "github.com/openmeterio/openmeter/openmeter/billing/charges/models/ledgertransaction" + "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased" + "github.com/openmeterio/openmeter/openmeter/customer" + "github.com/openmeterio/openmeter/openmeter/meter" "github.com/openmeterio/openmeter/openmeter/productcatalog" featurepkg "github.com/openmeterio/openmeter/openmeter/productcatalog/feature" "github.com/openmeterio/openmeter/openmeter/taxcode" "github.com/openmeterio/openmeter/pkg/clock" "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/datetime" + "github.com/openmeterio/openmeter/pkg/framework/transaction" "github.com/openmeterio/openmeter/pkg/models" "github.com/openmeterio/openmeter/pkg/timeutil" billingtest "github.com/openmeterio/openmeter/test/billing" ) +type invoiceNowErrorBillingService struct { + billing.Service + err error + calls int +} + +func (s *invoiceNowErrorBillingService) InvoicePendingLines(context.Context, billing.InvoicePendingLinesInput, ...billing.InvoicePendingLinesOption) ([]billing.StandardInvoice, error) { + s.calls++ + return nil, s.err +} + +func TestInvoiceNowOnCreateDefersFullyGatedCollection(t *testing.T) { + unexpectedErr := errors.New("unexpected collection failure") + tests := []struct { + name string + err error + wantErr error + wantCalls int + }{ + {name: "fully gated customers are deferred", err: fmt.Errorf("wrapped: %w", billing.ValidationError{Err: billing.ErrInvoiceCreateAllLinesGated}), wantCalls: 2}, + {name: "no eligible lines remains an error", err: billing.ValidationError{Err: billing.ErrInvoiceCreateNoLines}, wantErr: billing.ErrInvoiceCreateNoLines, wantCalls: 1}, + {name: "operational failure", err: unexpectedErr, wantErr: unexpectedErr, wantCalls: 1}, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + billingService := &invoiceNowErrorBillingService{err: test.err} + svc := &service{billingService: billingService} + + err := svc.invokeInvoiceNowOnCreate(t.Context(), []invoicePendingLinesInput{ + {CustomerID: customer.CustomerID{Namespace: "ns", ID: "customer-1"}, LineID: "line-1"}, + {CustomerID: customer.CustomerID{Namespace: "ns", ID: "customer-2"}, LineID: "line-2"}, + }) + require.ErrorIs(t, err, test.wantErr) + require.Equal(t, test.wantCalls, billingService.calls) + }) + } +} + +func (s *InvoicableChargesTestSuite) TestInvoicePendingLinesAllGatedReturnsErrorAndPersistsChargeIssues() { + tests := []struct { + name string + enclosingTransaction bool + }{ + {name: "standalone transaction"}, + {name: "caller handles deferral in enclosing transaction", enclosingTransaction: true}, + } + for _, test := range tests { + s.Run(test.name, func() { + // given: a due charge's required meter becomes unavailable. + ctx := s.T().Context() + ns := s.GetUniqueNamespace("all-gated-charge-validation") + s.TaxCodeEnv.ProvisionDefaultTaxCodes(s.T(), ns) + customInvoicing := s.SetupCustomInvoicing(ns) + s.ProvisionBillingProfile(ctx, ns, customInvoicing.App.GetID(), billingtest.WithManualApproval()) + cust := s.CreateTestCustomer(ns, "test-subject") + period := timeutil.ClosedPeriod{ + From: time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC), + To: time.Date(2026, 2, 1, 0, 0, 0, 0, time.UTC), + } + clock.FreezeTime(period.To) + defer clock.UnFreeze() + + featureKey := "temporarily-unavailable-meter" + featureMeters := s.createFeatureMeters(ctx, ns, featureKey) + meters := []meter.Meter{*featureMeters.ByKey[featureKey].Meter} + pending, err := s.Charges.CreatePendingInvoiceLines(ctx, charges.CreatePendingInvoiceLinesInput{ + Customer: cust.GetID(), + Currency: currencyx.FiatCode(USD), + Lines: billing.CreatePendingInvoiceLines{{ + GatheringLine: billing.GatheringLine{ + GatheringLineBase: billing.GatheringLineBase{ + ManagedResource: models.NewManagedResource(models.ManagedResourceInput{ + Namespace: ns, + Name: "blocked usage", + }), + ManagedBy: billing.ManuallyManagedLine, + Currency: currencyx.FiatCode(USD), + ServicePeriod: period, + InvoiceAt: period.To, + FeatureKey: featureKey, + Price: *productcatalog.NewPriceFrom(productcatalog.UnitPrice{ + Amount: alpacadecimal.NewFromInt(2), + }), + }, + }, + }}, + }) + s.Require().NoError(err) + s.Require().Len(pending.Lines, 1) + s.Require().NotNil(pending.Lines[0].ChargeID) + s.Require().NoError(s.MeterAdapter.ReplaceMeters(ctx, nil)) + defer func() { + s.Require().NoError(s.MeterAdapter.ReplaceMeters(ctx, meters)) + }() + chargeID := meta.ChargeID{Namespace: ns, ID: *pending.Lines[0].ChargeID} + input := billing.InvoicePendingLinesInput{Customer: cust.GetID(), AsOf: &period.To} + + // when: collection is fully gated, including when the caller owns the transaction. + var collectionErr error + if test.enclosingTransaction { + _, err = transaction.Run(ctx, s.BillingAdapter, func(ctx context.Context) (struct{}, error) { + _, collectionErr = s.BillingService.InvoicePendingLines(ctx, input, billing.WithBypassCollectionAlignment()) + if errors.Is(collectionErr, billing.ErrInvoiceCreateAllLinesGated) { + return struct{}{}, nil + } + + return struct{}{}, collectionErr + }) + s.Require().NoError(err) + } else { + _, collectionErr = s.BillingService.InvoicePendingLines(ctx, input, billing.WithBypassCollectionAlignment()) + } + + // then: collection returns an error, the charge stores its validation issues, and the line remains pending. + s.Require().ErrorIs(collectionErr, billing.ErrInvoiceCreateAllLinesGated) + s.ErrorAs(collectionErr, &billing.ValidationError{}) + charge, err := s.UsageBasedAdapter.GetByID(ctx, usagebased.GetByIDInput{ChargeID: chargeID}) + s.Require().NoError(err) + s.Require().True(lo.ContainsBy(charge.ValidationIssues, func(issue billing.ValidationIssue) bool { + return issue.Code == billing.ErrInvoiceLineFeatureHasNoMeters.Code + })) + gathering, err := s.BillingService.GetGatheringInvoiceById(ctx, billing.GetGatheringInvoiceByIdInput{ + Invoice: pending.Invoice.GetInvoiceID(), + Expand: billing.GatheringInvoiceExpands{billing.GatheringInvoiceExpandLines}, + }) + s.Require().NoError(err) + s.Require().Len(gathering.Lines.OrEmpty(), 1) + s.Equal(pending.Lines[0].ID, gathering.Lines.OrEmpty()[0].ID) + + // when: the missing dependency is repaired and collection is retried. + s.Require().NoError(s.MeterAdapter.ReplaceMeters(ctx, meters)) + s.MockStreamingConnector.AddSimpleEvent(meters[0].Key, 1, period.From.Add(time.Hour)) + s.UsageBasedTestHandler.onCreditsOnlyUsageAccrued, _ = newCappedCreditAllocator(0) + defer s.UsageBasedTestHandler.Reset() + invoices, err := s.BillingService.InvoicePendingLines(ctx, input, billing.WithBypassCollectionAlignment()) + s.Require().NoError(err) + + // then: collection creates one invoice and clears the stale dependency issue. + s.Require().Len(invoices, 1) + charge, err = s.UsageBasedAdapter.GetByID(ctx, usagebased.GetByIDInput{ChargeID: chargeID}) + s.Require().NoError(err) + s.False(lo.ContainsBy(charge.ValidationIssues, func(issue billing.ValidationIssue) bool { + return issue.Code == billing.ErrInvoiceLineFeatureHasNoMeters.Code + })) + }) + } +} + func (s *InvoicableChargesTestSuite) TestCreatePendingInvoiceLinesResolvesStripeTaxCodeBeforeCreatingCharge() { ctx := s.T().Context() ns := s.GetUniqueNamespace("charges-service-create-pending-lines-stripe-tax") @@ -800,10 +955,10 @@ func (s *InvoicableChargesTestSuite) TestInvoiceCustomerChargesAPIUsesChargeEngi // then: collection invokes the charge engine, applies credits, and retains the future line. s.Require().Equal(http.StatusCreated, res.Code, res.Body.String()) - var invoices []api.BillingInvoice - s.Require().NoError(json.Unmarshal(res.Body.Bytes(), &invoices)) - s.Require().Len(invoices, 1) - invoice, err := invoices[0].AsBillingInvoiceStandard() + var result api.InvoiceCustomerChargesResult + s.Require().NoError(json.Unmarshal(res.Body.Bytes(), &result)) + s.Require().Len(result.Invoices, 1) + invoice, err := result.Invoices[0].AsBillingInvoiceStandard() s.Require().NoError(err) s.Equal("6", invoice.Totals.Total) s.Equal("4", invoice.Totals.CreditsTotal) diff --git a/openmeter/billing/errors.go b/openmeter/billing/errors.go index e88fd2e094..3a429021e4 100644 --- a/openmeter/billing/errors.go +++ b/openmeter/billing/errors.go @@ -54,6 +54,7 @@ var ( ErrInvoiceLineNoPeriodChangeForSubscriptionManagedLine = NewValidationError("invoice_line_no_period_change_for_subscription_managed_line", "invoice line period cannot be changed for subscription managed lines") ErrInvoiceLineProgressiveBillingUsageDiscountUpdateForbidden = NewValidationError("invoice_line_progressive_billing_usage_discount_update_forbidden", "usage discount cannot be updated on a partially invoiced line") ErrInvoiceCreateNoLines = NewValidationError("invoice_create_no_lines", "the new invoice would have no lines") + ErrInvoiceCreateAllLinesGated = NewValidationError("invoice_create_all_lines_gated", "all eligible pending lines are blocked from invoice assignment") ErrInvoiceCreateUBPLineCustomerUsageAttributionInvalid = NewValidationError("invoice_create_ubp_line_customer_has_no_subjects", "creating an usage based line: customer usage attribution is invalid") ErrInvoiceCreateUBPLinePeriodIsEmpty = NewValidationError("invoice_create_ubp_line_period_is_empty", "creating an usage based line: truncated period is empty") ErrInvoiceLineCurrencyMismatch = NewValidationError("invoice_line_currency_mismatch", "invoice line currency mismatch") diff --git a/openmeter/billing/service/gatheringinvoicependinglines.go b/openmeter/billing/service/gatheringinvoicependinglines.go index 7f940c0165..ddc4019414 100644 --- a/openmeter/billing/service/gatheringinvoicependinglines.go +++ b/openmeter/billing/service/gatheringinvoicependinglines.go @@ -59,7 +59,7 @@ func (s *Service) InvoicePendingLines(ctx context.Context, input billing.Invoice } } - return transactionForInvoiceManipulation( + invoices, err := transactionForInvoiceManipulation( ctx, s, input.Customer, @@ -96,6 +96,17 @@ func (s *Service) InvoicePendingLines(ctx context.Context, input billing.Invoice return createdInvoices, nil }, ) + if err != nil { + return nil, err + } + + // Gates record why a charge is blocked. Finish their transaction before + // reporting a fully gated collection, so those reasons remain available. + if len(invoices) == 0 { + return nil, billing.ValidationError{Err: billing.ErrInvoiceCreateAllLinesGated} + } + + return invoices, nil } func (s *Service) prepareBillableLines(ctx context.Context, input billing.PrepareBillableLinesInput, options billing.InvoicePendingLinesOptions) (*billing.PrepareBillableLinesResult, error) { diff --git a/openmeter/billing/worker/collect/asynccollect_test.go b/openmeter/billing/worker/collect/asynccollect_test.go index eb9f23cd75..dcf735a5ef 100644 --- a/openmeter/billing/worker/collect/asynccollect_test.go +++ b/openmeter/billing/worker/collect/asynccollect_test.go @@ -1,13 +1,44 @@ package billingworkercollect import ( + "context" + "fmt" + "log/slog" "testing" + "time" "github.com/stretchr/testify/require" "github.com/openmeterio/openmeter/openmeter/billing" + "github.com/openmeterio/openmeter/openmeter/customer" ) +type gatedCollectionBillingService struct { + billing.Service +} + +func (gatedCollectionBillingService) InvoicePendingLines(context.Context, billing.InvoicePendingLinesInput, ...billing.InvoicePendingLinesOption) ([]billing.StandardInvoice, error) { + return nil, fmt.Errorf("collection deferred: %w", billing.ValidationError{Err: billing.ErrInvoiceCreateAllLinesGated}) +} + +func TestInvoiceCollectorDefersFullyGatedCollection(t *testing.T) { + // given: a fully gated collection has already recorded its diagnostics. + collector := &InvoiceCollector{ + billingService: gatedCollectionBillingService{}, + logger: slog.Default(), + } + + // when: the automatic collector encounters that deferred outcome. + invoices, err := collector.CollectCustomerInvoice(t.Context(), CollectCustomerInvoiceInput{ + CustomerID: customer.CustomerID{Namespace: "ns", ID: "customer-id"}, + AsOf: time.Now(), + }) + require.NoError(t, err) + + // then: it neither fails nor calls gathering-invoice recalculation. + require.Empty(t, invoices) +} + func TestInvoiceCollectorHandleCollectCustomerInvoicesEvent(t *testing.T) { collector := &InvoiceCollector{} diff --git a/openmeter/billing/worker/collect/collect.go b/openmeter/billing/worker/collect/collect.go index fe3135ed9b..4b2e4048b8 100644 --- a/openmeter/billing/worker/collect/collect.go +++ b/openmeter/billing/worker/collect/collect.go @@ -104,6 +104,10 @@ func (a *InvoiceCollector) CollectCustomerInvoice(ctx context.Context, params Co return nil, nil } + if errors.Is(err, billing.ErrInvoiceCreateAllLinesGated) { + return nil, nil + } + if errors.Is(err, billing.ErrInvoiceCreateNoLines) { a.logger.WarnContext(ctx, "no invoices generated for customer during collection (possible data inconsistency), recalculating gathering invoices", "customer", params.CustomerID) diff --git a/openmeter/billing/worker/subscriptionsync/service/sync.go b/openmeter/billing/worker/subscriptionsync/service/sync.go index aaebc1566b..55a975e5bb 100644 --- a/openmeter/billing/worker/subscriptionsync/service/sync.go +++ b/openmeter/billing/worker/subscriptionsync/service/sync.go @@ -44,7 +44,7 @@ func (s *Service) invoicePendingLines(ctx context.Context, customer customer.Cus billing.WithMaxLinesPerInvoice(s.featureFlags.MaxLinesPerCollectedInvoice), ) if err != nil { - if errors.Is(err, billing.ErrInvoiceCreateNoLines) { + if errors.Is(err, billing.ErrInvoiceCreateNoLines) || errors.Is(err, billing.ErrInvoiceCreateAllLinesGated) { return nil } diff --git a/openmeter/billing/worker/subscriptionsync/service/sync_test.go b/openmeter/billing/worker/subscriptionsync/service/sync_test.go index d741fdefb3..b618f3acc1 100644 --- a/openmeter/billing/worker/subscriptionsync/service/sync_test.go +++ b/openmeter/billing/worker/subscriptionsync/service/sync_test.go @@ -2,6 +2,7 @@ package service import ( "context" + "errors" "fmt" "testing" "time" @@ -41,6 +42,40 @@ func TestSubscriptionHandlerScenarios(t *testing.T) { suite.Run(t, new(SubscriptionHandlerTestSuite)) } +type pendingLinesErrorBillingService struct { + billing.Service + err error +} + +func (s pendingLinesErrorBillingService) InvoicePendingLines(context.Context, billing.InvoicePendingLinesInput, ...billing.InvoicePendingLinesOption) ([]billing.StandardInvoice, error) { + return nil, s.err +} + +func (s *SubscriptionHandlerTestSuite) TestInvoicePendingLinesDeferredOutcomes() { + unexpectedErr := errors.New("unexpected collection failure") + tests := []struct { + name string + err error + wantErr error + }{ + {name: "all lines gated", err: fmt.Errorf("wrapped: %w", billing.ValidationError{Err: billing.ErrInvoiceCreateAllLinesGated})}, + {name: "no billable lines", err: billing.ValidationError{Err: billing.ErrInvoiceCreateNoLines}}, + {name: "operational failure", err: unexpectedErr, wantErr: unexpectedErr}, + } + for _, test := range tests { + s.Run(test.name, func() { + billingService := s.Service.billingService + s.Service.billingService = pendingLinesErrorBillingService{err: test.err} + defer func() { + s.Service.billingService = billingService + }() + + err := s.Service.invoicePendingLines(s.T().Context(), s.Customer.GetID()) + s.Require().ErrorIs(err, test.wantErr) + }) + } +} + func (s *SubscriptionHandlerTestSuite) TestSyncByIDWithLegacyItemCadence() { ctx := s.T().Context() start := s.mustParseTime("2024-01-01T00:00:00Z") diff --git a/test/billing/lineengine_test.go b/test/billing/lineengine_test.go index f20346fad8..2092338f6e 100644 --- a/test/billing/lineengine_test.go +++ b/test/billing/lineengine_test.go @@ -288,7 +288,7 @@ func (s *LineEngineTestSuite) TestGateInvoiceAssignmentDuringCollection() { s.registerMockLineEngine(s.T(), mockEngine) defer s.unregisterLineEngine(s.T(), mockEngine) - s.Run("all otherwise billable lines blocked returns an empty successful result", func() { + s.Run("all otherwise billable lines blocked returns a validation error", func() { // given mockEngine.Reset() namespace := s.GetUniqueNamespace("ns-line-engine-gate-all-blocked") @@ -318,7 +318,8 @@ func (s *LineEngineTestSuite) TestGateInvoiceAssignmentDuringCollection() { }, ombilling.WithBypassCollectionAlignment()) // then - s.Require().NoError(err) + s.Require().ErrorIs(err, ombilling.ErrInvoiceCreateAllLinesGated) + s.ErrorAs(err, &ombilling.ValidationError{}) s.Empty(invoices) s.True(gateCalled) s.False(buildCalled) @@ -379,6 +380,55 @@ func (s *LineEngineTestSuite) TestGateInvoiceAssignmentDuringCollection() { s.Equal(pendingLines[1].ID, invoices[0].Lines.OrEmpty()[0].ID) }) + s.Run("a blocked currency does not prevent another currency from invoicing", func() { + // given: the USD line is blocked while the EUR line is allowed. + mockEngine.Reset() + namespace := s.GetUniqueNamespace("ns-line-engine-gate-currencies") + customerID, blockedLines := s.createInvoiceAssignmentGateFixture(ctx, namespace, mockEngine.GetLineEngineType(), now) + allowedLine := ombilling.NewFlatFeeGatheringLine(ombilling.NewFlatFeeLineInput{ + Namespace: namespace, + Period: timeutil.ClosedPeriod{From: now.Add(-time.Hour), To: now}, + InvoiceAt: now, + Name: "allowed EUR line", + ManagedBy: ombilling.ManuallyManagedLine, + PerUnitAmount: alpacadecimal.NewFromInt(1), + PaymentTerm: productcatalog.InArrearsPaymentTerm, + }) + allowedLine.Currency = currencyx.FiatCode(currency.EUR) + allowedLine.Engine = mockEngine.GetLineEngineType() + _, err := s.BillingService.CreatePendingInvoiceLines(ctx, ombilling.CreatePendingInvoiceLinesInput{ + Customer: customerID, + Currency: currencyx.FiatCode(currency.EUR), + Lines: ombilling.NewCreatePendingInvoiceLines([]ombilling.GatheringLine{allowedLine}), + }) + s.Require().NoError(err) + mockEngine.gateInvoiceAssignment = func(_ context.Context, input ombilling.GateInvoiceAssignmentInput) (ombilling.GateInvoiceAssignmentResult, error) { + result := make(ombilling.GateInvoiceAssignmentResult) + for _, line := range input.Lines { + if line.ID == blockedLines[0].ID { + result[line.GetLineID()] = ombilling.InvoiceAssignmentGateResponse{ExcludeFromInvoice: true} + } + } + + return result, nil + } + mockEngine.buildStandardInvoiceLines = func(_ context.Context, input ombilling.BuildStandardInvoiceLinesInput) (ombilling.StandardLines, error) { + return mustAsNewStandardLines(input), nil + } + + // when: collection considers both currencies. + invoices, err := s.BillingService.InvoicePendingLines(ctx, ombilling.InvoicePendingLinesInput{ + Customer: customerID, + AsOf: &now, + ForceAsyncAdvance: true, + }, ombilling.WithBypassCollectionAlignment()) + s.Require().NoError(err) + + // then: the allowed currency produces its invoice. + s.Require().Len(invoices, 1) + s.Equal(currencyx.FiatCode(currency.EUR), invoices[0].Currency) + }) + s.Run("gate errors abort invoice creation", func() { // given mockEngine.Reset() From 92f992f9be19653cf787222adf5ed19daabe4854 Mon Sep 17 00:00:00 2001 From: Peter Turi Date: Tue, 27 Oct 2026 17:08:26 +0100 Subject: [PATCH 4/6] test(billing): expect validation errors for fully gated collections --- openmeter/billing/charges/service/usagebased_test.go | 6 +++--- .../subscriptionsync/service/sync_credittheninvoice_test.go | 4 ++-- test/credits/credit_then_invoice_test.go | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/openmeter/billing/charges/service/usagebased_test.go b/openmeter/billing/charges/service/usagebased_test.go index 88357b2431..8ee71c6d07 100644 --- a/openmeter/billing/charges/service/usagebased_test.go +++ b/openmeter/billing/charges/service/usagebased_test.go @@ -2028,7 +2028,7 @@ func (s *UsageBasedChargesTestSuite) TestUsageBasedCreditThenInvoicePartialInvoi }) // then - s.NoError(err) + s.Require().ErrorIs(err, billing.ErrInvoiceCreateAllLinesGated) s.Empty(invoices) charge := s.mustGetUsageBasedChargeByID(usageBasedChargeID) @@ -2417,7 +2417,7 @@ func (s *UsageBasedChargesTestSuite) TestUsageBasedCreditThenInvoicePendingParti }) // then - s.NoError(err) + s.Require().ErrorIs(err, billing.ErrInvoiceCreateAllLinesGated) s.Empty(invoices) charge := s.mustGetUsageBasedChargeByID(usageBasedChargeID) @@ -2439,7 +2439,7 @@ func (s *UsageBasedChargesTestSuite) TestUsageBasedCreditThenInvoicePendingParti // then // the gate remains idempotent - s.NoError(err) + s.Require().ErrorIs(err, billing.ErrInvoiceCreateAllLinesGated) s.Empty(invoices) charge = s.mustGetUsageBasedChargeByID(usageBasedChargeID) s.Len(charge.ValidationIssues, 1) diff --git a/openmeter/billing/worker/subscriptionsync/service/sync_credittheninvoice_test.go b/openmeter/billing/worker/subscriptionsync/service/sync_credittheninvoice_test.go index c386ad2fd2..ac6c5f1d23 100644 --- a/openmeter/billing/worker/subscriptionsync/service/sync_credittheninvoice_test.go +++ b/openmeter/billing/worker/subscriptionsync/service/sync_credittheninvoice_test.go @@ -301,7 +301,7 @@ func (s *CreditThenInvoiceTestSuite) TestSubscriptionSyncPersistsMeterlessUsageC // then: // - the gathering line remains pending and no financial processing starts - s.Require().NoError(err) + s.Require().ErrorIs(err, billing.ErrInvoiceCreateAllLinesGated) s.Empty(invoices) s.Require().Len(s.gatheringInvoice(ctx, s.Namespace, s.Customer.ID).Lines.OrEmpty(), 1) @@ -472,7 +472,7 @@ func (s *CreditThenInvoiceTestSuite) TestSubscriptionSyncPersistsMissingFeatureU // then: // - the gathering line remains pending and no financial processing starts - s.Require().NoError(err) + s.Require().ErrorIs(err, billing.ErrInvoiceCreateAllLinesGated) s.Empty(invoices) s.Require().Len(s.gatheringInvoice(ctx, s.Namespace, s.Customer.ID).Lines.OrEmpty(), 1) diff --git a/test/credits/credit_then_invoice_test.go b/test/credits/credit_then_invoice_test.go index 644e6fe455..ab70500f76 100644 --- a/test/credits/credit_then_invoice_test.go +++ b/test/credits/credit_then_invoice_test.go @@ -233,7 +233,7 @@ func (s *CreditThenInvoiceTestSuite) TestUsageBasedCreditThenInvoiceCollectionPe Customer: cust.GetID(), AsOf: lo.ToPtr(servicePeriod.To), }) - s.Require().NoError(err) + s.Require().ErrorIs(err, billing.ErrInvoiceCreateAllLinesGated) s.Empty(invoices) charge := s.RequireUsageBasedChargeStatus(usageBasedChargeID, usagebased.StatusCreated) @@ -552,7 +552,7 @@ func (s *CreditThenInvoiceTestSuite) TestUsageBasedCreditThenInvoiceCollectionPe // then: // - collection excludes both lines and records a distinct critical issue on each charge - s.Require().NoError(err) + s.Require().ErrorIs(err, billing.ErrInvoiceCreateAllLinesGated) s.Empty(invoices) issueFeatureKeys := make([]string, 0, len(created)) @@ -850,7 +850,7 @@ func (s *CreditThenInvoiceTestSuite) TestUsageBasedCreditThenInvoiceCollectionPe Customer: cust.GetID(), AsOf: lo.ToPtr(servicePeriod.To), }) - s.Require().NoError(err) + s.Require().ErrorIs(err, billing.ErrInvoiceCreateAllLinesGated) s.Empty(invoices) charge := s.RequireUsageBasedChargeStatus(usageBasedChargeID, usagebased.StatusActiveRealizationProcessing) From a5d994027632be574d78623c4e4f8cf99e63c2d6 Mon Sep 17 00:00:00 2001 From: Robert Boros Date: Wed, 28 Oct 2026 11:13:20 +0100 Subject: [PATCH 5/6] fix: add missing extensions to the new endpoint --- .../packages/aip-client-javascript/README.md | 8 +- .../src/sdk/customers.ts | 23 -- .../aip-client-javascript/src/sdk/internal.ts | 43 +++ .../aip/src/customers/charges/operations.tsp | 3 + api/v3/api.gen.go | 324 +++++++++--------- api/v3/openapi.yaml | 3 + 6 files changed, 218 insertions(+), 186 deletions(-) diff --git a/api/spec/packages/aip-client-javascript/README.md b/api/spec/packages/aip-client-javascript/README.md index 7fcda47bbd..7a6d557137 100644 --- a/api/spec/packages/aip-client-javascript/README.md +++ b/api/spec/packages/aip-client-javascript/README.md @@ -41,6 +41,7 @@ TypeSpec definitions and ships fully-typed request and response models. - [EntitlementAccess](#entitlementaccess) - [Notifications](#notifications) - [Internal Operations](#internal-operations) + - [Internal Customers](#internal-customers) - [Internal Subscriptions](#internal-subscriptions) - [Internal Apps](#internal-apps) - [Internal Notifications](#internal-notifications) @@ -316,7 +317,6 @@ The full call path, HTTP route, and a short description are listed below. | `client.customers.credits.transactions.list` | `GET /openmeter/customers/{customerId}/credits/transactions` | List credit transactions for a customer. Returns an immutable, chronological record of credit movements: funded credits and consumed credits. Transactions are returned in reverse chronological order by default. | | `client.customers.charges.list` | `GET /openmeter/customers/{customerId}/charges` | List customer charges. Returns the customer's charges that are represented as either flat fee or usage-based charges. | | `client.customers.charges.create` | `POST /openmeter/customers/{customerId}/charges` | Create customer charge. | -| `client.customers.charges.invoice` | `POST /openmeter/customers/{customerId}/charges/invoice` | Invoice a customer's charges outside the normal billing cycle. Collection alignment is bypassed. The customer's progressive billing setting applies unless explicitly overridden. All eligible pending lines are considered. Returns the created standard invoices, one per collected currency. Lines blocked by their owning engine remain pending. If all eligible lines are blocked or no lines are eligible, returns a validation error. | ### Entitlements @@ -499,6 +499,12 @@ Operations marked internal in the API definition are exposed under intended for customer use: they may require additional permissions, and they can change or be removed without notice or semver consideration. +### Internal Customers + +| Method | HTTP | Description | +| ------------------------------------------- | -------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `client.internal.customers.charges.invoice` | `POST /openmeter/customers/{customerId}/charges/invoice` | Invoice a customer's charges outside the normal billing cycle. Collection alignment is bypassed. The customer's progressive billing setting applies unless explicitly overridden. All eligible pending lines are considered. Returns the created standard invoices, one per collected currency. Lines blocked by their owning engine remain pending. If all eligible lines are blocked or no lines are eligible, returns a validation error. | + ### Internal Subscriptions | Method | HTTP | Description | diff --git a/api/spec/packages/aip-client-javascript/src/sdk/customers.ts b/api/spec/packages/aip-client-javascript/src/sdk/customers.ts index d436a1de50..f86473b53d 100644 --- a/api/spec/packages/aip-client-javascript/src/sdk/customers.ts +++ b/api/spec/packages/aip-client-javascript/src/sdk/customers.ts @@ -34,7 +34,6 @@ import { listCreditTransactions, listCustomerCharges, createCustomerCharges, - invoiceCustomerCharges, } from '../funcs/customers.js' import type { CreateCustomerRequest, @@ -97,8 +96,6 @@ import type { ListCustomerChargesResponse, CreateCustomerChargesRequest, CreateCustomerChargesResponse, - InvoiceCustomerChargesRequest, - InvoiceCustomerChargesResponse, } from '../models/operations/customers.js' import type { Charge, @@ -841,24 +838,4 @@ export class CustomersCharges { ): Promise { return unwrap(await createCustomerCharges(this._client, request, options)) } - - /** - * Invoice a customer's charges - * - * Invoice a customer's charges outside the normal billing cycle. Collection - * alignment is bypassed. The customer's progressive billing setting applies unless - * explicitly overridden. All eligible pending lines are considered. - * - * Returns the created standard invoices, one per collected currency. Lines blocked - * by their owning engine remain pending. If all eligible lines are blocked or no - * lines are eligible, returns a validation error. - * - * POST /openmeter/customers/{customerId}/charges/invoice - */ - async invoice( - request: InvoiceCustomerChargesRequest, - options?: RequestOptions, - ): Promise { - return unwrap(await invoiceCustomerCharges(this._client, request, options)) - } } diff --git a/api/spec/packages/aip-client-javascript/src/sdk/internal.ts b/api/spec/packages/aip-client-javascript/src/sdk/internal.ts index 5abd235a2a..9e669d9f75 100644 --- a/api/spec/packages/aip-client-javascript/src/sdk/internal.ts +++ b/api/spec/packages/aip-client-javascript/src/sdk/internal.ts @@ -3,6 +3,7 @@ import { type Client } from '../core.js' import { unwrap, type RequestOptions } from '../lib/types.js' import { paginatePages } from '../lib/paginate.js' +import { invoiceCustomerCharges } from '../funcs/customers.js' import { createSubscriptionAddon, updateSubscriptionAddon, @@ -19,6 +20,10 @@ import { getBillingNotificationEvent, resendBillingNotificationEvent, } from '../funcs/notifications.js' +import type { + InvoiceCustomerChargesRequest, + InvoiceCustomerChargesResponse, +} from '../models/operations/customers.js' import type { CreateSubscriptionAddonRequest, CreateSubscriptionAddonResponse, @@ -59,6 +64,11 @@ import type { NotificationEvent, NotificationRule } from '../models/types.js' export class Internal { constructor(private readonly _client: Client) {} + private _customers?: InternalCustomers + get customers(): InternalCustomers { + return (this._customers ??= new InternalCustomers(this._client)) + } + private _subscriptions?: InternalSubscriptions get subscriptions(): InternalSubscriptions { return (this._subscriptions ??= new InternalSubscriptions(this._client)) @@ -75,6 +85,39 @@ export class Internal { } } +export class InternalCustomers { + constructor(private readonly _client: Client) {} + + private _charges?: InternalCustomersCharges + get charges(): InternalCustomersCharges { + return (this._charges ??= new InternalCustomersCharges(this._client)) + } +} + +export class InternalCustomersCharges { + constructor(private readonly _client: Client) {} + + /** + * Invoice a customer's charges + * + * Invoice a customer's charges outside the normal billing cycle. Collection + * alignment is bypassed. The customer's progressive billing setting applies unless + * explicitly overridden. All eligible pending lines are considered. + * + * Returns the created standard invoices, one per collected currency. Lines blocked + * by their owning engine remain pending. If all eligible lines are blocked or no + * lines are eligible, returns a validation error. + * + * POST /openmeter/customers/{customerId}/charges/invoice + */ + async invoice( + request: InvoiceCustomerChargesRequest, + options?: RequestOptions, + ): Promise { + return unwrap(await invoiceCustomerCharges(this._client, request, options)) + } +} + export class InternalSubscriptions { constructor(private readonly _client: Client) {} diff --git a/api/spec/packages/aip/src/customers/charges/operations.tsp b/api/spec/packages/aip/src/customers/charges/operations.tsp index 8d55dd65e7..394f75db49 100644 --- a/api/spec/packages/aip/src/customers/charges/operations.tsp +++ b/api/spec/packages/aip/src/customers/charges/operations.tsp @@ -173,6 +173,9 @@ namespace Charges { @operationId("invoice-customer-charges") @summary("Invoice a customer's charges") @extension(Shared.RPCEndpointExtension, true) + @extension(Shared.UnstableExtension, true) + @extension(Shared.PrivateExtension, true) + @extension(Shared.InternalExtension, true) invoiceCharges( @path customerId: Shared.ULID, @body request?: InvoiceCustomerChargesInput, diff --git a/api/v3/api.gen.go b/api/v3/api.gen.go index 5aaedaf4c3..36707155ba 100644 --- a/api/v3/api.gen.go +++ b/api/v3/api.gen.go @@ -24033,168 +24033,168 @@ var swaggerSpec = []string{ "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", + "SMQWQJwl4lKeugHgFxMJxTOpTX13zODTh0//3wA=", } // decodeSpec returns the embedded OpenAPI spec as raw JSON bytes, diff --git a/api/v3/openapi.yaml b/api/v3/openapi.yaml index 922c1211e9..8af6301be1 100644 --- a/api/v3/openapi.yaml +++ b/api/v3/openapi.yaml @@ -1652,6 +1652,9 @@ paths: application/json: schema: $ref: '#/components/schemas/InvoiceCustomerChargesInput' + x-internal: true + x-private: true + x-unstable: true x-rpc-endpoint: true /openmeter/customers/{customerId}/credits/adjustments: post: From 8d81a5b454f7d3525612fb927f92b874f5881b85 Mon Sep 17 00:00:00 2001 From: Robert Boros Date: Wed, 28 Oct 2026 12:12:37 +0100 Subject: [PATCH 6/6] refactor: use map in the handler instead of foreach --- .../billinginvoices/invoice_charges.go | 29 ++++++++----------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/api/v3/handlers/billinginvoices/invoice_charges.go b/api/v3/handlers/billinginvoices/invoice_charges.go index 9e4fa5466b..1cd7bf4429 100644 --- a/api/v3/handlers/billinginvoices/invoice_charges.go +++ b/api/v3/handlers/billinginvoices/invoice_charges.go @@ -12,6 +12,8 @@ import ( "github.com/openmeterio/openmeter/openmeter/customer" "github.com/openmeterio/openmeter/pkg/framework/commonhttp" "github.com/openmeterio/openmeter/pkg/framework/transport/httptransport" + "github.com/openmeterio/openmeter/pkg/slicesx" + "github.com/samber/lo" ) type ( @@ -37,13 +39,11 @@ func (h *handler) InvoiceCustomerCharges() InvoiceCustomerChargesHandler { } input := InvoiceCustomerChargesRequest{ - InvoicePendingLinesInput: billing.InvoicePendingLinesInput{ - Customer: customer.CustomerID{ - Namespace: ns, - ID: customerID, - }, - AsOf: body.AsOf, + Customer: customer.CustomerID{ + Namespace: ns, + ID: customerID, }, + AsOf: body.AsOf, Options: []billing.InvoicePendingLinesOption{billing.WithBypassCollectionAlignment()}, } @@ -63,19 +63,14 @@ func (h *handler) InvoiceCustomerCharges() InvoiceCustomerChargesHandler { return InvoiceCustomerChargesResponse{}, fmt.Errorf("collecting customer pending lines: %w", err) } - out := InvoiceCustomerChargesResponse{ - Invoices: make([]api.BillingInvoice, 0, len(invoices)), - } - for _, invoice := range invoices { - mappedInvoice, err := ToAPIStandardInvoice(invoice) - if err != nil { - return InvoiceCustomerChargesResponse{}, fmt.Errorf("mapping collected invoice: %w", err) - } - - out.Invoices = append(out.Invoices, mappedInvoice) + apiInvoices, err := lo.MapErr(invoices, slicesx.WrapMapFn(ToAPIStandardInvoice)) + if err != nil { + return InvoiceCustomerChargesResponse{}, fmt.Errorf("mapping collected invoice: %w", err) } - return out, nil + return InvoiceCustomerChargesResponse{ + Invoices: apiInvoices, + }, nil }, commonhttp.JSONResponseEncoderWithStatus[InvoiceCustomerChargesResponse](http.StatusCreated), httptransport.AppendOptions(