From 117ee78b24773f358ac0b6cbcc762d0b20d8f9dc Mon Sep 17 00:00:00 2001 From: Alberto Leal Date: Thu, 18 Jun 2026 13:05:02 -0400 Subject: [PATCH] chore(billing-platform): add protos for the tax-transaction settlement endpoints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the contract-service messages backing getsentry PR #20632 (tax-transaction settlement), where the platform commits an invoice's provider-side tax document once the invoice is paid and voids it once the dunning schedule is exhausted. New messages, one endpoint_*.proto per request/response pair, mirroring MarkInvoicePaid: - GetTaxTransaction — reads the invoice's tax-document linkage (external reference, status, needs_retry) so settlement can decide commit vs void. - MarkTaxTransactionCommitted / MarkTaxTransactionVoided — record the settled status, carrying needs_retry so the reconciliation sweep can re-drive a vendor call that did not confirm. Plus a standalone TaxTransactionStatus enum (PENDING=1, COMMITTED=2, VOID=3, with the proto3 UNSPECIFIED=0 zero value) matching the persisted statuses. These back ContractService methods currently prototyped with proto-free dataclasses; the dataclasses are intentionally left in place, and migrating them to these messages is a later step. SetPendingTaxTransaction is already covered by #315. Only the .proto sources are authored; the rust stubs are regenerated and python stubs build at package time. --- Cargo.lock | 2 +- .../v1/endpoint_get_tax_transaction.proto | 23 +++++ ...point_mark_tax_transaction_committed.proto | 17 ++++ ...endpoint_mark_tax_transaction_voided.proto | 19 ++++ .../contract/v1/tax_transaction_status.proto | 14 +++ ..._protos.billing.v1.services.contract.v1.rs | 94 +++++++++++++++++++ 6 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 proto/sentry_protos/billing/v1/services/contract/v1/endpoint_get_tax_transaction.proto create mode 100644 proto/sentry_protos/billing/v1/services/contract/v1/endpoint_mark_tax_transaction_committed.proto create mode 100644 proto/sentry_protos/billing/v1/services/contract/v1/endpoint_mark_tax_transaction_voided.proto create mode 100644 proto/sentry_protos/billing/v1/services/contract/v1/tax_transaction_status.proto diff --git a/Cargo.lock b/Cargo.lock index e8698790..ddccf172 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -717,7 +717,7 @@ checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "sentry_protos" -version = "0.30.1" +version = "0.31.2" dependencies = [ "prost", "prost-types", diff --git a/proto/sentry_protos/billing/v1/services/contract/v1/endpoint_get_tax_transaction.proto b/proto/sentry_protos/billing/v1/services/contract/v1/endpoint_get_tax_transaction.proto new file mode 100644 index 00000000..8eec1ac9 --- /dev/null +++ b/proto/sentry_protos/billing/v1/services/contract/v1/endpoint_get_tax_transaction.proto @@ -0,0 +1,23 @@ +syntax = "proto3"; + +package sentry_protos.billing.v1.services.contract.v1; + +import "sentry_protos/billing/v1/services/contract/v1/tax_transaction_status.proto"; + +// Reads the tax-document linkage recorded on an invoice, so charge-outcome +// settlement can decide whether to commit or void the provider-side document. +message GetTaxTransactionRequest { + uint64 invoice_id = 1; +} + +message GetTaxTransactionResponse { + // The tax provider's reference for the opened tax document. Unset when the + // invoice carries no tax document (e.g. a shadow or untaxed invoice). + optional string external_reference = 1; + // The tax document's lifecycle status. Unset when the invoice carries no tax + // document, or when a document is linked but no status has been recorded yet. + optional TaxTransactionStatus status = 2; + // True if a prior commit or void vendor call failed and must be re-driven by + // the reconciliation sweep. + bool needs_retry = 3; +} diff --git a/proto/sentry_protos/billing/v1/services/contract/v1/endpoint_mark_tax_transaction_committed.proto b/proto/sentry_protos/billing/v1/services/contract/v1/endpoint_mark_tax_transaction_committed.proto new file mode 100644 index 00000000..1314891b --- /dev/null +++ b/proto/sentry_protos/billing/v1/services/contract/v1/endpoint_mark_tax_transaction_committed.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; + +package sentry_protos.billing.v1.services.contract.v1; + +// Marks an invoice's tax document as committed once the invoice is paid. The +// caller sets needs_retry when the vendor commit call could not be confirmed, +// so the reconciliation sweep can re-drive it. +message MarkTaxTransactionCommittedRequest { + uint64 invoice_id = 1; + // True if the vendor commit call did not confirm and must be re-driven. + bool needs_retry = 2; +} + +message MarkTaxTransactionCommittedResponse { + // True if a matching invoice was found and updated. + bool updated = 1; +} diff --git a/proto/sentry_protos/billing/v1/services/contract/v1/endpoint_mark_tax_transaction_voided.proto b/proto/sentry_protos/billing/v1/services/contract/v1/endpoint_mark_tax_transaction_voided.proto new file mode 100644 index 00000000..9504e5c6 --- /dev/null +++ b/proto/sentry_protos/billing/v1/services/contract/v1/endpoint_mark_tax_transaction_voided.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; + +package sentry_protos.billing.v1.services.contract.v1; + +// Marks an invoice's tax document as voided once the invoice is abandoned +// (dunning exhausted). The caller sets needs_retry when the vendor void call +// could not be confirmed, so the reconciliation sweep can re-drive it. A +// committed document is never voided here — unwinding a filed document is +// refund handling, not settlement. +message MarkTaxTransactionVoidedRequest { + uint64 invoice_id = 1; + // True if the vendor void call did not confirm and must be re-driven. + bool needs_retry = 2; +} + +message MarkTaxTransactionVoidedResponse { + // True if a matching invoice was found and updated. + bool updated = 1; +} diff --git a/proto/sentry_protos/billing/v1/services/contract/v1/tax_transaction_status.proto b/proto/sentry_protos/billing/v1/services/contract/v1/tax_transaction_status.proto new file mode 100644 index 00000000..37135256 --- /dev/null +++ b/proto/sentry_protos/billing/v1/services/contract/v1/tax_transaction_status.proto @@ -0,0 +1,14 @@ +syntax = "proto3"; + +package sentry_protos.billing.v1.services.contract.v1; + +// The lifecycle status of an invoice's provider-side tax document. The integer +// values mirror the statuses persisted on the invoice (PENDING = 1, +// COMMITTED = 2, VOID = 3); UNSPECIFIED = 0 is the proto3 zero value and is +// never a stored status. +enum TaxTransactionStatus { + TAX_TRANSACTION_STATUS_UNSPECIFIED = 0; + TAX_TRANSACTION_STATUS_PENDING = 1; + TAX_TRANSACTION_STATUS_COMMITTED = 2; + TAX_TRANSACTION_STATUS_VOID = 3; +} diff --git a/rust/src/sentry_protos.billing.v1.services.contract.v1.rs b/rust/src/sentry_protos.billing.v1.services.contract.v1.rs index 0a75a6e7..cc5bc2bf 100644 --- a/rust/src/sentry_protos.billing.v1.services.contract.v1.rs +++ b/rust/src/sentry_protos.billing.v1.services.contract.v1.rs @@ -557,6 +557,64 @@ pub struct GetInvoiceResponse { #[prost(message, optional, tag = "1")] pub invoice: ::core::option::Option, } +/// The lifecycle status of an invoice's provider-side tax document. The integer +/// values mirror the statuses persisted on the invoice (PENDING = 1, +/// COMMITTED = 2, VOID = 3); UNSPECIFIED = 0 is the proto3 zero value and is +/// never a stored status. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum TaxTransactionStatus { + Unspecified = 0, + Pending = 1, + Committed = 2, + Void = 3, +} +impl TaxTransactionStatus { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "TAX_TRANSACTION_STATUS_UNSPECIFIED", + Self::Pending => "TAX_TRANSACTION_STATUS_PENDING", + Self::Committed => "TAX_TRANSACTION_STATUS_COMMITTED", + Self::Void => "TAX_TRANSACTION_STATUS_VOID", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "TAX_TRANSACTION_STATUS_UNSPECIFIED" => Some(Self::Unspecified), + "TAX_TRANSACTION_STATUS_PENDING" => Some(Self::Pending), + "TAX_TRANSACTION_STATUS_COMMITTED" => Some(Self::Committed), + "TAX_TRANSACTION_STATUS_VOID" => Some(Self::Void), + _ => None, + } + } +} +/// Reads the tax-document linkage recorded on an invoice, so charge-outcome +/// settlement can decide whether to commit or void the provider-side document. +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct GetTaxTransactionRequest { + #[prost(uint64, tag = "1")] + pub invoice_id: u64, +} +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct GetTaxTransactionResponse { + /// The tax provider's reference for the opened tax document. Unset when the + /// invoice carries no tax document (e.g. a shadow or untaxed invoice). + #[prost(string, optional, tag = "1")] + pub external_reference: ::core::option::Option<::prost::alloc::string::String>, + /// The tax document's lifecycle status. Unset when the invoice carries no tax + /// document, or when a document is linked but no status has been recorded yet. + #[prost(enumeration = "TaxTransactionStatus", optional, tag = "2")] + pub status: ::core::option::Option, + /// True if a prior commit or void vendor call failed and must be re-driven by + /// the reconciliation sweep. + #[prost(bool, tag = "3")] + pub needs_retry: bool, +} #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct GetUnchargedInvoicesRequest { /// Returns Invoices whose current billing period ends before this time and @@ -654,6 +712,42 @@ pub struct MarkInvoicePaidResponse { #[prost(bool, tag = "1")] pub updated: bool, } +/// Marks an invoice's tax document as committed once the invoice is paid. The +/// caller sets needs_retry when the vendor commit call could not be confirmed, +/// so the reconciliation sweep can re-drive it. +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct MarkTaxTransactionCommittedRequest { + #[prost(uint64, tag = "1")] + pub invoice_id: u64, + /// True if the vendor commit call did not confirm and must be re-driven. + #[prost(bool, tag = "2")] + pub needs_retry: bool, +} +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct MarkTaxTransactionCommittedResponse { + /// True if a matching invoice was found and updated. + #[prost(bool, tag = "1")] + pub updated: bool, +} +/// Marks an invoice's tax document as voided once the invoice is abandoned +/// (dunning exhausted). The caller sets needs_retry when the vendor void call +/// could not be confirmed, so the reconciliation sweep can re-drive it. A +/// committed document is never voided here — unwinding a filed document is +/// refund handling, not settlement. +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct MarkTaxTransactionVoidedRequest { + #[prost(uint64, tag = "1")] + pub invoice_id: u64, + /// True if the vendor void call did not confirm and must be re-driven. + #[prost(bool, tag = "2")] + pub needs_retry: bool, +} +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct MarkTaxTransactionVoidedResponse { + /// True if a matching invoice was found and updated. + #[prost(bool, tag = "1")] + pub updated: bool, +} /// Records a failed charge attempt against an invoice. #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct RecordFailedChargeAttemptRequest {