diff --git a/Cargo.lock b/Cargo.lock index 4267c9b3..11be5763 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -717,7 +717,7 @@ checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "sentry_protos" -version = "0.64.0" +version = "0.64.1" dependencies = [ "prost", "prost-types", diff --git a/proto/sentry_protos/billing/v1/services/contract/v2/billing_config.proto b/proto/sentry_protos/billing/v1/services/contract/v2/billing_config.proto new file mode 100644 index 00000000..bc3fdabb --- /dev/null +++ b/proto/sentry_protos/billing/v1/services/contract/v2/billing_config.proto @@ -0,0 +1,43 @@ +syntax = "proto3"; + +package sentry_protos.billing.v1.services.contract.v2; + +import "sentry_protos/billing/v1/common/v1/sponsored_type.proto"; +import "sentry_protos/billing/v1/services/contract/v1/billing_config.proto"; + +// Configuration for a sponsored contract. The presence of this message on a +// BillingConfig indicates the contract is sponsored; its absence means it is +// not. +message SponsorshipConfig { + // Whether this sponsored Contract is eligible to start trials. + bool can_trial = 1; + + // Whether this sponsored Contract can checkout. + bool can_checkout = 2; + + sentry_protos.billing.v1.common.v1.SponsoredType sponsorship_type = 3; +} + +message BillingConfig { + // The number-of-months interval the contract was signed under + // (1 = monthly, 12 = annual). Frozen for the life of the contract. + uint32 month_interval = 1; + + // Whether the org is allowed to incur pay-as-you-go usage. + // Credit-card orgs always support payg; invoiced orgs that should + // support it are an explicit override. + bool supports_payg = 2; + + // Whether this is a managed (sales-assisted) subscription rather than + // self-serve. + bool is_managed = 3; + + // Whether the org has a soft cap: usage past reserved volume is allowed + // (and billed) instead of hard-stopping ingestion. + bool has_soft_cap = 4; + + // Sponsorship configuration. Absent means the contract is not sponsored. + optional SponsorshipConfig sponsorship_config = 5; + + sentry_protos.billing.v1.services.contract.v1.ChargeConfig charge_config = 6; +} diff --git a/proto/sentry_protos/billing/v1/services/contract/v2/contract.proto b/proto/sentry_protos/billing/v1/services/contract/v2/contract.proto new file mode 100644 index 00000000..1d50ccdb --- /dev/null +++ b/proto/sentry_protos/billing/v1/services/contract/v2/contract.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package sentry_protos.billing.v1.services.contract.v2; + +import "sentry_protos/billing/v1/services/contract/v1/retention_config.proto"; +import "sentry_protos/billing/v1/services/contract/v2/billing_config.proto"; +import "sentry_protos/billing/v1/services/contract/v2/contract_metadata.proto"; +import "sentry_protos/billing/v1/services/contract/v2/pricing_config.proto"; + +message Contract { + ContractMetadata metadata = 1; + BillingConfig billing_config = 2; + PricingConfig pricing_config = 3; + sentry_protos.billing.v1.services.contract.v1.RetentionConfig retention_config = 4; +} diff --git a/proto/sentry_protos/billing/v1/services/contract/v2/contract_metadata.proto b/proto/sentry_protos/billing/v1/services/contract/v2/contract_metadata.proto new file mode 100644 index 00000000..07a823fe --- /dev/null +++ b/proto/sentry_protos/billing/v1/services/contract/v2/contract_metadata.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; + +package sentry_protos.billing.v1.services.contract.v2; + +import "sentry_protos/billing/v1/services/contract/v1/contract_metadata.proto"; + +message ContractMetadata { + uint64 id = 1; + uint64 organization_id = 2; + // The set of BillingRules that the BillingRulesEngine has to execute for this contract. + string ruleset_version = 3; + // Catch-all for overrides and information not covered above. + sentry_protos.billing.v1.services.contract.v1.MetadataOptions custom_options = 4; + string package_uid = 5; + + optional uint64 previous_id = 6; +} diff --git a/proto/sentry_protos/billing/v1/services/contract/v2/endpoint_create_contract.proto b/proto/sentry_protos/billing/v1/services/contract/v2/endpoint_create_contract.proto new file mode 100644 index 00000000..dd6e9d2a --- /dev/null +++ b/proto/sentry_protos/billing/v1/services/contract/v2/endpoint_create_contract.proto @@ -0,0 +1,53 @@ +syntax = "proto3"; + +package sentry_protos.billing.v1.services.contract.v2; + +import "sentry_protos/billing/v1/common/v1/address.proto"; +import "sentry_protos/billing/v1/common/v1/sponsored_type.proto"; +import "sentry_protos/billing/v1/services/contract/v1/billing_config.proto"; +import "sentry_protos/billing/v1/services/contract/v1/invoice.proto"; +import "sentry_protos/billing/v1/services/contract/v1/pricing_config.proto"; + +message CreateContractRequest { + uint64 organization_id = 1; + string package_uid = 2; + repeated sentry_protos.billing.v1.services.contract.v1.UserConfig user_configs = 3; + repeated sentry_protos.billing.v1.services.contract.v1.InvoiceLineItem line_items = 4; + sentry_protos.billing.v1.common.v1.Address address = 5; + // The customer's chosen cadence. Must be one of the package's + // supported_month_intervals. If unset, defaults to the package's first + // supported interval. + uint32 month_interval = 6; + + // How the contract is billed. If unset (BILLING_TYPE_UNSPECIFIED), the + // contract defaults to credit-card. + sentry_protos.billing.v1.services.contract.v1.BillingType billing_type = 7; + + // Whether usage past reserved volume is allowed (and billed) instead of + // hard-stopping ingestion. + bool has_soft_cap = 8; + + // The tax provider's reference for the tax document opened for this invoice. + // When set, the contract service records it on the created invoice as a + // pending tax transaction atomically with invoice creation. Unset means no + // tax document was opened. + optional string tax_transaction_code = 9; + + // The sponsored type of the contract. If unset, then this is a non-sponsored + // contract. + optional sentry_protos.billing.v1.common.v1.SponsoredType sponsorship_type = 10; + + // The customer's tax registration number (e.g. VAT ID) on file. + optional string tax_number = 11; + + // Whether the tax provider placed this invoice under reverse charge. + bool is_reverse_charge = 12; +} + +message CreateContractResponse { + uint64 id = 1; + uint64 invoice_id = 2; + string invoice_guid = 3; + bool needs_charge = 4; + uint64 amount_billed = 5; +} diff --git a/proto/sentry_protos/billing/v1/services/contract/v2/endpoint_get_all_contracts_for_organization.proto b/proto/sentry_protos/billing/v1/services/contract/v2/endpoint_get_all_contracts_for_organization.proto new file mode 100644 index 00000000..bea43b15 --- /dev/null +++ b/proto/sentry_protos/billing/v1/services/contract/v2/endpoint_get_all_contracts_for_organization.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; + +package sentry_protos.billing.v1.services.contract.v2; + +import "sentry_protos/billing/v1/services/contract/v2/contract.proto"; + +message GetAllContractsForOrganizationRequest { + uint64 organization_id = 1; +} + +message GetAllContractsForOrganizationResponse { + repeated Contract contracts = 1; +} diff --git a/proto/sentry_protos/billing/v1/services/contract/v2/endpoint_get_contract.proto b/proto/sentry_protos/billing/v1/services/contract/v2/endpoint_get_contract.proto new file mode 100644 index 00000000..fb7887ea --- /dev/null +++ b/proto/sentry_protos/billing/v1/services/contract/v2/endpoint_get_contract.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; + +package sentry_protos.billing.v1.services.contract.v2; + +import "sentry_protos/billing/v1/date.proto"; +import "sentry_protos/billing/v1/services/contract/v2/contract.proto"; + +message GetContractRequest { + uint64 organization_id = 1; + + // If provided, returns the Contract active on this date. Otherwise, returns the current Contract. + sentry_protos.billing.v1.Date date = 2; +} + +message GetSpecificContractRequest { + uint64 contract_id = 1; +} + +message GetContractResponse { + Contract contract = 1; +} diff --git a/proto/sentry_protos/billing/v1/services/contract/v2/endpoint_get_uninvoiced_contracts.proto b/proto/sentry_protos/billing/v1/services/contract/v2/endpoint_get_uninvoiced_contracts.proto new file mode 100644 index 00000000..88e949b7 --- /dev/null +++ b/proto/sentry_protos/billing/v1/services/contract/v2/endpoint_get_uninvoiced_contracts.proto @@ -0,0 +1,25 @@ +syntax = "proto3"; + +package sentry_protos.billing.v1.services.contract.v2; + +import "google/protobuf/timestamp.proto"; + +message GetUninvoicedContractsRequest { + // Returns Contracts whose current billing period ends before this time and + // have not yet been invoiced for the current period. + google.protobuf.Timestamp current_ts = 1; + + // Maximum number of contracts to return in the response. If more contracts + // match the request than this limit, the response will have truncated set + // to true. + uint32 max_items = 2; + + optional uint32 offset = 3; +} + +message GetUninvoicedContractsResponse { + // True if additional matching contracts existed beyond max_items and were + // not included in this response. + bool truncated = 1; + repeated uint64 contract_ids = 2; +} diff --git a/proto/sentry_protos/billing/v1/services/contract/v2/pricing_config.proto b/proto/sentry_protos/billing/v1/services/contract/v2/pricing_config.proto new file mode 100644 index 00000000..08224b08 --- /dev/null +++ b/proto/sentry_protos/billing/v1/services/contract/v2/pricing_config.proto @@ -0,0 +1,20 @@ +syntax = "proto3"; + +package sentry_protos.billing.v1.services.contract.v2; + +import "google/protobuf/timestamp.proto"; +import "sentry_protos/billing/v1/services/contract/v1/billing_config.proto"; +import "sentry_protos/billing/v1/services/contract/v1/pricing_config.proto"; + +message PricingConfig { + // Determines when to reset quotas and charge the subscription price. + sentry_protos.billing.v1.services.contract.v1.Date billing_period_start_date = 1; + sentry_protos.billing.v1.services.contract.v1.Date billing_period_end_date = 2; + + //Determines when the on demand period for invoicing the organization starts and ends, even for annual plans we bill ondemand monthly + sentry_protos.billing.v1.services.contract.v1.Date ondemand_period_start_date = 3; + sentry_protos.billing.v1.services.contract.v1.Date ondemand_period_end_date = 4; + optional google.protobuf.Timestamp usage_watermark_ts = 5; + + repeated sentry_protos.billing.v1.services.contract.v1.UserConfig user_config = 6; +} diff --git a/proto/sentry_protos/billing/v1/services/invoicer/v2/endpoint_bill_contract_change.proto b/proto/sentry_protos/billing/v1/services/invoicer/v2/endpoint_bill_contract_change.proto new file mode 100644 index 00000000..c2edbc14 --- /dev/null +++ b/proto/sentry_protos/billing/v1/services/invoicer/v2/endpoint_bill_contract_change.proto @@ -0,0 +1,29 @@ +syntax = "proto3"; + +package sentry_protos.billing.v1.services.invoicer.v2; + +import "sentry_protos/billing/v1/common/v1/pending_change.proto"; + +// Invoices an immediate mid-term contract change and rolls the contract over +// now. The caller supplies the change to apply (target package/interval and the +// caps taking effect now). The invoicer prices the prorated new-plan charge and +// the credit for unused time on the current plan itself, settles accrued +// pay-as-you-go usage at the current contract's rates, applies monetary grants +// and sales tax, closes the current contract immediately, and opens the new +// contract with the supplied change applied. +message BillContractChangeRequest { + uint64 contract_id = 1; + // The change applied to the new contract: the target package/interval and the + // PAYG caps taking effect now. Reuses the PendingChange shape the rollover + // consumes, but here it applies immediately rather than at a period boundary, + // so the caller does not stage it in the pending-change store. + sentry_protos.billing.v1.common.v1.PendingChange change = 2; + + // Whether the requested change should result in a new billing period. + bool start_new_term = 3; +} + +message BillContractChangeResponse { + // The invoice that settles the change (and closes the previous contract). + uint64 invoice_id = 1; +} diff --git a/rust/src/_include.rs b/rust/src/_include.rs index e7ec5d80..2c0cda89 100644 --- a/rust/src/_include.rs +++ b/rust/src/_include.rs @@ -33,6 +33,9 @@ pub mod sentry_protos { pub mod v1 { include!("sentry_protos.billing.v1.services.contract.v1.rs"); } + pub mod v2 { + include!("sentry_protos.billing.v1.services.contract.v2.rs"); + } } pub mod contract_budget_enforcer { pub mod v1 { @@ -53,6 +56,9 @@ pub mod sentry_protos { pub mod v1 { include!("sentry_protos.billing.v1.services.invoicer.v1.rs"); } + pub mod v2 { + include!("sentry_protos.billing.v1.services.invoicer.v2.rs"); + } } pub mod package { pub mod v1 { diff --git a/rust/src/sentry_protos.billing.v1.services.contract.v2.rs b/rust/src/sentry_protos.billing.v1.services.contract.v2.rs new file mode 100644 index 00000000..108cb8e1 --- /dev/null +++ b/rust/src/sentry_protos.billing.v1.services.contract.v2.rs @@ -0,0 +1,195 @@ +// This file is @generated by prost-build. +/// Configuration for a sponsored contract. The presence of this message on a +/// BillingConfig indicates the contract is sponsored; its absence means it is +/// not. +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct SponsorshipConfig { + /// Whether this sponsored Contract is eligible to start trials. + #[prost(bool, tag = "1")] + pub can_trial: bool, + /// Whether this sponsored Contract can checkout. + #[prost(bool, tag = "2")] + pub can_checkout: bool, + #[prost(enumeration = "super::super::super::common::v1::SponsoredType", tag = "3")] + pub sponsorship_type: i32, +} +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct BillingConfig { + /// The number-of-months interval the contract was signed under + /// (1 = monthly, 12 = annual). Frozen for the life of the contract. + #[prost(uint32, tag = "1")] + pub month_interval: u32, + /// Whether the org is allowed to incur pay-as-you-go usage. + /// Credit-card orgs always support payg; invoiced orgs that should + /// support it are an explicit override. + #[prost(bool, tag = "2")] + pub supports_payg: bool, + /// Whether this is a managed (sales-assisted) subscription rather than + /// self-serve. + #[prost(bool, tag = "3")] + pub is_managed: bool, + /// Whether the org has a soft cap: usage past reserved volume is allowed + /// (and billed) instead of hard-stopping ingestion. + #[prost(bool, tag = "4")] + pub has_soft_cap: bool, + /// Sponsorship configuration. Absent means the contract is not sponsored. + #[prost(message, optional, tag = "5")] + pub sponsorship_config: ::core::option::Option, + #[prost(message, optional, tag = "6")] + pub charge_config: ::core::option::Option, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ContractMetadata { + #[prost(uint64, tag = "1")] + pub id: u64, + #[prost(uint64, tag = "2")] + pub organization_id: u64, + /// The set of BillingRules that the BillingRulesEngine has to execute for this contract. + #[prost(string, tag = "3")] + pub ruleset_version: ::prost::alloc::string::String, + /// Catch-all for overrides and information not covered above. + #[prost(message, optional, tag = "4")] + pub custom_options: ::core::option::Option, + #[prost(string, tag = "5")] + pub package_uid: ::prost::alloc::string::String, + #[prost(uint64, optional, tag = "6")] + pub previous_id: ::core::option::Option, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PricingConfig { + /// Determines when to reset quotas and charge the subscription price. + #[prost(message, optional, tag = "1")] + pub billing_period_start_date: ::core::option::Option, + #[prost(message, optional, tag = "2")] + pub billing_period_end_date: ::core::option::Option, + /// Determines when the on demand period for invoicing the organization starts and ends, even for annual plans we bill ondemand monthly + #[prost(message, optional, tag = "3")] + pub ondemand_period_start_date: ::core::option::Option, + #[prost(message, optional, tag = "4")] + pub ondemand_period_end_date: ::core::option::Option, + #[prost(message, optional, tag = "5")] + pub usage_watermark_ts: ::core::option::Option<::prost_types::Timestamp>, + #[prost(message, repeated, tag = "6")] + pub user_config: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Contract { + #[prost(message, optional, tag = "1")] + pub metadata: ::core::option::Option, + #[prost(message, optional, tag = "2")] + pub billing_config: ::core::option::Option, + #[prost(message, optional, tag = "3")] + pub pricing_config: ::core::option::Option, + #[prost(message, optional, tag = "4")] + pub retention_config: ::core::option::Option, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreateContractRequest { + #[prost(uint64, tag = "1")] + pub organization_id: u64, + #[prost(string, tag = "2")] + pub package_uid: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "3")] + pub user_configs: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "4")] + pub line_items: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "5")] + pub address: ::core::option::Option, + /// The customer's chosen cadence. Must be one of the package's + /// supported_month_intervals. If unset, defaults to the package's first + /// supported interval. + #[prost(uint32, tag = "6")] + pub month_interval: u32, + /// How the contract is billed. If unset (BILLING_TYPE_UNSPECIFIED), the + /// contract defaults to credit-card. + #[prost(enumeration = "super::v1::BillingType", tag = "7")] + pub billing_type: i32, + /// Whether usage past reserved volume is allowed (and billed) instead of + /// hard-stopping ingestion. + #[prost(bool, tag = "8")] + pub has_soft_cap: bool, + /// The tax provider's reference for the tax document opened for this invoice. + /// When set, the contract service records it on the created invoice as a + /// pending tax transaction atomically with invoice creation. Unset means no + /// tax document was opened. + #[prost(string, optional, tag = "9")] + pub tax_transaction_code: ::core::option::Option<::prost::alloc::string::String>, + /// The sponsored type of the contract. If unset, then this is a non-sponsored + /// contract. + #[prost( + enumeration = "super::super::super::common::v1::SponsoredType", + optional, + tag = "10" + )] + pub sponsorship_type: ::core::option::Option, + /// The customer's tax registration number (e.g. VAT ID) on file. + #[prost(string, optional, tag = "11")] + pub tax_number: ::core::option::Option<::prost::alloc::string::String>, + /// Whether the tax provider placed this invoice under reverse charge. + #[prost(bool, tag = "12")] + pub is_reverse_charge: bool, +} +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct CreateContractResponse { + #[prost(uint64, tag = "1")] + pub id: u64, + #[prost(uint64, tag = "2")] + pub invoice_id: u64, + #[prost(string, tag = "3")] + pub invoice_guid: ::prost::alloc::string::String, + #[prost(bool, tag = "4")] + pub needs_charge: bool, + #[prost(uint64, tag = "5")] + pub amount_billed: u64, +} +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct GetAllContractsForOrganizationRequest { + #[prost(uint64, tag = "1")] + pub organization_id: u64, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetAllContractsForOrganizationResponse { + #[prost(message, repeated, tag = "1")] + pub contracts: ::prost::alloc::vec::Vec, +} +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct GetContractRequest { + #[prost(uint64, tag = "1")] + pub organization_id: u64, + /// If provided, returns the Contract active on this date. Otherwise, returns the current Contract. + #[prost(message, optional, tag = "2")] + pub date: ::core::option::Option, +} +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct GetSpecificContractRequest { + #[prost(uint64, tag = "1")] + pub contract_id: u64, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetContractResponse { + #[prost(message, optional, tag = "1")] + pub contract: ::core::option::Option, +} +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct GetUninvoicedContractsRequest { + /// Returns Contracts whose current billing period ends before this time and + /// have not yet been invoiced for the current period. + #[prost(message, optional, tag = "1")] + pub current_ts: ::core::option::Option<::prost_types::Timestamp>, + /// Maximum number of contracts to return in the response. If more contracts + /// match the request than this limit, the response will have truncated set + /// to true. + #[prost(uint32, tag = "2")] + pub max_items: u32, + #[prost(uint32, optional, tag = "3")] + pub offset: ::core::option::Option, +} +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct GetUninvoicedContractsResponse { + /// True if additional matching contracts existed beyond max_items and were + /// not included in this response. + #[prost(bool, tag = "1")] + pub truncated: bool, + #[prost(uint64, repeated, tag = "2")] + pub contract_ids: ::prost::alloc::vec::Vec, +} diff --git a/rust/src/sentry_protos.billing.v1.services.invoicer.v2.rs b/rust/src/sentry_protos.billing.v1.services.invoicer.v2.rs new file mode 100644 index 00000000..6adf1f38 --- /dev/null +++ b/rust/src/sentry_protos.billing.v1.services.invoicer.v2.rs @@ -0,0 +1,28 @@ +// This file is @generated by prost-build. +/// Invoices an immediate mid-term contract change and rolls the contract over +/// now. The caller supplies the change to apply (target package/interval and the +/// caps taking effect now). The invoicer prices the prorated new-plan charge and +/// the credit for unused time on the current plan itself, settles accrued +/// pay-as-you-go usage at the current contract's rates, applies monetary grants +/// and sales tax, closes the current contract immediately, and opens the new +/// contract with the supplied change applied. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BillContractChangeRequest { + #[prost(uint64, tag = "1")] + pub contract_id: u64, + /// The change applied to the new contract: the target package/interval and the + /// PAYG caps taking effect now. Reuses the PendingChange shape the rollover + /// consumes, but here it applies immediately rather than at a period boundary, + /// so the caller does not stage it in the pending-change store. + #[prost(message, optional, tag = "2")] + pub change: ::core::option::Option, + /// Whether the requested change should result in a new billing period. + #[prost(bool, tag = "3")] + pub start_new_term: bool, +} +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct BillContractChangeResponse { + /// The invoice that settles the change (and closes the previous contract). + #[prost(uint64, tag = "1")] + pub invoice_id: u64, +}