From 3dc4634ed985c57e7c97be12bcf324bd3ba2bd2a Mon Sep 17 00:00:00 2001 From: Andrew McKnight Date: Thu, 2 Jul 2026 14:48:09 -0800 Subject: [PATCH 1/3] feat(billing): protos for master-dataclass promotion Promotes several dataclass prototypes already in use on getsentry master to real protos. No behavioral change on its own -- this is the sentry-protos side of the getsentry-side promotion PR. Included: - ``charge.proto`` -- adds ``PlatformCharge.date_added_st`` (mirrors ``PlatformRefund.date_added_st`` so mixed charge+refund timelines can sort off a single time axis). - ``endpoint_list_charges_for_invoice_ids.proto`` (new) -- used by ``ChargeService.list_charges_for_invoice_ids`` on master. - ``endpoint_list_charges_for_organization.proto`` (new) -- used by ``ChargeService.list_charges_for_organization`` on master. - ``endpoint_get_invoice_guids_for_ids.proto`` (new) -- used by ``ContractService.get_invoice_guids_for_ids`` on master. - ``endpoint_start_manual_payment.proto`` (new) -- used by ``ContractService.start_manual_payment`` on master (added in getsentry/getsentry#20790). Split out from the original combined branch. The remaining Pay-Now-loop protos (release_manual_payment_lock, modifications to handle_charge_succeeded / capture_charge / mark_invoice_paid) live on a separate branch that partners with getsentry/getsentry#20833. --- .../v1/services/charge/v1/charge.proto | 4 ++++ ...ndpoint_list_charges_for_invoice_ids.proto | 17 ++++++++++++++++ ...dpoint_list_charges_for_organization.proto | 15 ++++++++++++++ .../endpoint_get_invoice_guids_for_ids.proto | 17 ++++++++++++++++ .../v1/endpoint_start_manual_payment.proto | 20 +++++++++++++++++++ 5 files changed, 73 insertions(+) create mode 100644 proto/sentry_protos/billing/v1/services/charge/v1/endpoint_list_charges_for_invoice_ids.proto create mode 100644 proto/sentry_protos/billing/v1/services/charge/v1/endpoint_list_charges_for_organization.proto create mode 100644 proto/sentry_protos/billing/v1/services/contract/v1/endpoint_get_invoice_guids_for_ids.proto create mode 100644 proto/sentry_protos/billing/v1/services/contract/v1/endpoint_start_manual_payment.proto diff --git a/proto/sentry_protos/billing/v1/services/charge/v1/charge.proto b/proto/sentry_protos/billing/v1/services/charge/v1/charge.proto index 69a1cba8..473cce96 100644 --- a/proto/sentry_protos/billing/v1/services/charge/v1/charge.proto +++ b/proto/sentry_protos/billing/v1/services/charge/v1/charge.proto @@ -31,6 +31,10 @@ message PlatformCharge { // sum ``refunds[*].amount_cents`` if they need the aggregate, and // ``len(refunds) > 0`` (or sum == amount) signals "refunded." repeated PlatformRefund refunds = 10; + // Unix epoch seconds when the charge row was created locally. Mirrors + // PlatformRefund.date_added_st so consumers that render mixed charge + + // refund timelines can sort them off a single time axis. + int64 date_added_st = 11; } // Canonical projection of a stored platform refund. One row per recorded diff --git a/proto/sentry_protos/billing/v1/services/charge/v1/endpoint_list_charges_for_invoice_ids.proto b/proto/sentry_protos/billing/v1/services/charge/v1/endpoint_list_charges_for_invoice_ids.proto new file mode 100644 index 00000000..867e8c45 --- /dev/null +++ b/proto/sentry_protos/billing/v1/services/charge/v1/endpoint_list_charges_for_invoice_ids.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; + +package sentry_protos.billing.v1.services.charge.v1; + +import "sentry_protos/billing/v1/services/charge/v1/charge.proto"; + +// Lists every recorded PlatformCharge for a batch of invoice ids, ordered by +// (invoice_id, date_added_st) ascending, with each charge's refunds +// pre-attached. Used by presentation surfaces that render full charge + +// refund history for a customer's invoices. +message ListChargesForInvoiceIdsRequest { + repeated uint64 invoice_ids = 1; +} + +message ListChargesForInvoiceIdsResponse { + repeated PlatformCharge charges = 1; +} diff --git a/proto/sentry_protos/billing/v1/services/charge/v1/endpoint_list_charges_for_organization.proto b/proto/sentry_protos/billing/v1/services/charge/v1/endpoint_list_charges_for_organization.proto new file mode 100644 index 00000000..bc094f66 --- /dev/null +++ b/proto/sentry_protos/billing/v1/services/charge/v1/endpoint_list_charges_for_organization.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package sentry_protos.billing.v1.services.charge.v1; + +import "sentry_protos/billing/v1/services/charge/v1/charge.proto"; + +// Lists every recorded PlatformCharge owned by an organization, ordered by +// date_added_st ascending, with each charge's refunds pre-attached. +message ListChargesForOrganizationRequest { + uint64 organization_id = 1; +} + +message ListChargesForOrganizationResponse { + repeated PlatformCharge charges = 1; +} diff --git a/proto/sentry_protos/billing/v1/services/contract/v1/endpoint_get_invoice_guids_for_ids.proto b/proto/sentry_protos/billing/v1/services/contract/v1/endpoint_get_invoice_guids_for_ids.proto new file mode 100644 index 00000000..3e07f541 --- /dev/null +++ b/proto/sentry_protos/billing/v1/services/contract/v1/endpoint_get_invoice_guids_for_ids.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; + +package sentry_protos.billing.v1.services.contract.v1; + +// Batch-resolves PlatformInvoice ids to their guids. Caller is responsible +// for passing only ids it has the right to see; this endpoint does not +// filter by organization. Missing ids are silently omitted from the +// response. +message GetInvoiceGuidsForIdsRequest { + repeated uint64 invoice_ids = 1; +} + +message GetInvoiceGuidsForIdsResponse { + // Mapping from invoice id to guid for every invoice that was found. + // Empty when the request supplied no ids or none of them matched. + map invoice_guids = 1; +} diff --git a/proto/sentry_protos/billing/v1/services/contract/v1/endpoint_start_manual_payment.proto b/proto/sentry_protos/billing/v1/services/contract/v1/endpoint_start_manual_payment.proto new file mode 100644 index 00000000..9317e510 --- /dev/null +++ b/proto/sentry_protos/billing/v1/services/contract/v1/endpoint_start_manual_payment.proto @@ -0,0 +1,20 @@ +syntax = "proto3"; + +package sentry_protos.billing.v1.services.contract.v1; + +// Atomically stamps manual_payment_started_at on an unpaid PlatformInvoice, +// locking it from automated billing for 24h while the user completes a manual +// Pay Now flow via Stripe.js. +message StartManualPaymentRequest { + uint64 invoice_id = 1; +} + +message StartManualPaymentResponse { + // False when no row was updated. That covers three cases: invoice id + // unknown, the row is already paid, or the row was paid in the window + // between the caller's last read and this call. The third case is the + // race that the atomic ``WHERE paid=false`` filter closes -- callers + // can treat ``updated=false`` as "the invoice is no longer in a payable + // state, discard any side effects you took in the meantime." + bool updated = 1; +} From 615aef3353cf95b5b4d24b2a26655fcf262c8f15 Mon Sep 17 00:00:00 2001 From: Andrew McKnight Date: Thu, 2 Jul 2026 15:45:31 -0800 Subject: [PATCH 2/3] chore(billing): trim prose comments from master-dataclass promotion protos Field-level context moved to PR #344's description; kept short one-line comments for the semantic hint. --- .../billing/v1/services/charge/v1/charge.proto | 4 +--- .../v1/endpoint_list_charges_for_invoice_ids.proto | 5 +---- .../v1/endpoint_list_charges_for_organization.proto | 3 +-- .../v1/endpoint_get_invoice_guids_for_ids.proto | 8 ++------ .../contract/v1/endpoint_start_manual_payment.proto | 11 ++--------- 5 files changed, 7 insertions(+), 24 deletions(-) diff --git a/proto/sentry_protos/billing/v1/services/charge/v1/charge.proto b/proto/sentry_protos/billing/v1/services/charge/v1/charge.proto index 473cce96..bb03561b 100644 --- a/proto/sentry_protos/billing/v1/services/charge/v1/charge.proto +++ b/proto/sentry_protos/billing/v1/services/charge/v1/charge.proto @@ -31,9 +31,7 @@ message PlatformCharge { // sum ``refunds[*].amount_cents`` if they need the aggregate, and // ``len(refunds) > 0`` (or sum == amount) signals "refunded." repeated PlatformRefund refunds = 10; - // Unix epoch seconds when the charge row was created locally. Mirrors - // PlatformRefund.date_added_st so consumers that render mixed charge + - // refund timelines can sort them off a single time axis. + // Unix epoch seconds when the charge row was created locally. int64 date_added_st = 11; } diff --git a/proto/sentry_protos/billing/v1/services/charge/v1/endpoint_list_charges_for_invoice_ids.proto b/proto/sentry_protos/billing/v1/services/charge/v1/endpoint_list_charges_for_invoice_ids.proto index 867e8c45..087add16 100644 --- a/proto/sentry_protos/billing/v1/services/charge/v1/endpoint_list_charges_for_invoice_ids.proto +++ b/proto/sentry_protos/billing/v1/services/charge/v1/endpoint_list_charges_for_invoice_ids.proto @@ -4,10 +4,7 @@ package sentry_protos.billing.v1.services.charge.v1; import "sentry_protos/billing/v1/services/charge/v1/charge.proto"; -// Lists every recorded PlatformCharge for a batch of invoice ids, ordered by -// (invoice_id, date_added_st) ascending, with each charge's refunds -// pre-attached. Used by presentation surfaces that render full charge + -// refund history for a customer's invoices. +// Lists PlatformCharges for a batch of invoice ids, refunds pre-attached. message ListChargesForInvoiceIdsRequest { repeated uint64 invoice_ids = 1; } diff --git a/proto/sentry_protos/billing/v1/services/charge/v1/endpoint_list_charges_for_organization.proto b/proto/sentry_protos/billing/v1/services/charge/v1/endpoint_list_charges_for_organization.proto index bc094f66..ca2e833d 100644 --- a/proto/sentry_protos/billing/v1/services/charge/v1/endpoint_list_charges_for_organization.proto +++ b/proto/sentry_protos/billing/v1/services/charge/v1/endpoint_list_charges_for_organization.proto @@ -4,8 +4,7 @@ package sentry_protos.billing.v1.services.charge.v1; import "sentry_protos/billing/v1/services/charge/v1/charge.proto"; -// Lists every recorded PlatformCharge owned by an organization, ordered by -// date_added_st ascending, with each charge's refunds pre-attached. +// Lists PlatformCharges for an organization, refunds pre-attached. message ListChargesForOrganizationRequest { uint64 organization_id = 1; } diff --git a/proto/sentry_protos/billing/v1/services/contract/v1/endpoint_get_invoice_guids_for_ids.proto b/proto/sentry_protos/billing/v1/services/contract/v1/endpoint_get_invoice_guids_for_ids.proto index 3e07f541..94334039 100644 --- a/proto/sentry_protos/billing/v1/services/contract/v1/endpoint_get_invoice_guids_for_ids.proto +++ b/proto/sentry_protos/billing/v1/services/contract/v1/endpoint_get_invoice_guids_for_ids.proto @@ -2,16 +2,12 @@ syntax = "proto3"; package sentry_protos.billing.v1.services.contract.v1; -// Batch-resolves PlatformInvoice ids to their guids. Caller is responsible -// for passing only ids it has the right to see; this endpoint does not -// filter by organization. Missing ids are silently omitted from the -// response. +// Batch-resolves PlatformInvoice ids to their guids. Missing ids are +// silently omitted from the response. message GetInvoiceGuidsForIdsRequest { repeated uint64 invoice_ids = 1; } message GetInvoiceGuidsForIdsResponse { - // Mapping from invoice id to guid for every invoice that was found. - // Empty when the request supplied no ids or none of them matched. map invoice_guids = 1; } diff --git a/proto/sentry_protos/billing/v1/services/contract/v1/endpoint_start_manual_payment.proto b/proto/sentry_protos/billing/v1/services/contract/v1/endpoint_start_manual_payment.proto index 9317e510..e1e81eca 100644 --- a/proto/sentry_protos/billing/v1/services/contract/v1/endpoint_start_manual_payment.proto +++ b/proto/sentry_protos/billing/v1/services/contract/v1/endpoint_start_manual_payment.proto @@ -2,19 +2,12 @@ syntax = "proto3"; package sentry_protos.billing.v1.services.contract.v1; -// Atomically stamps manual_payment_started_at on an unpaid PlatformInvoice, -// locking it from automated billing for 24h while the user completes a manual -// Pay Now flow via Stripe.js. +// Atomically stamps manual_payment_started_at on an unpaid PlatformInvoice. message StartManualPaymentRequest { uint64 invoice_id = 1; } message StartManualPaymentResponse { - // False when no row was updated. That covers three cases: invoice id - // unknown, the row is already paid, or the row was paid in the window - // between the caller's last read and this call. The third case is the - // race that the atomic ``WHERE paid=false`` filter closes -- callers - // can treat ``updated=false`` as "the invoice is no longer in a payable - // state, discard any side effects you took in the meantime." + // False when no row matched paid=false (invoice paid, missing, or raced). bool updated = 1; } From 5c80b8da1738a0d52126bcd9494eb604e2cd82dd Mon Sep 17 00:00:00 2001 From: "getsantry[bot]" <66042841+getsantry[bot]@users.noreply.github.com> Date: Thu, 2 Jul 2026 23:46:36 +0000 Subject: [PATCH 3/3] chore: Regenerate Rust bindings --- Cargo.lock | 2 +- ...ry_protos.billing.v1.services.charge.v1.rs | 25 ++++++++++++++++++ ..._protos.billing.v1.services.contract.v1.rs | 26 +++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 11fd409c..0d35f961 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -717,7 +717,7 @@ checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "sentry_protos" -version = "0.37.0" +version = "0.38.0" dependencies = [ "prost", "prost-types", diff --git a/rust/src/sentry_protos.billing.v1.services.charge.v1.rs b/rust/src/sentry_protos.billing.v1.services.charge.v1.rs index 07f1219c..4fc5f64f 100644 --- a/rust/src/sentry_protos.billing.v1.services.charge.v1.rs +++ b/rust/src/sentry_protos.billing.v1.services.charge.v1.rs @@ -41,6 +41,9 @@ pub struct PlatformCharge { /// `len(refunds) > 0` (or sum == amount) signals "refunded." #[prost(message, repeated, tag = "10")] pub refunds: ::prost::alloc::vec::Vec, + /// Unix epoch seconds when the charge row was created locally. + #[prost(int64, tag = "11")] + pub date_added_st: i64, } /// Canonical projection of a stored platform refund. One row per recorded /// refund against a `PlatformCharge`. @@ -168,6 +171,28 @@ pub struct ListChargesForInvoiceResponse { #[prost(message, repeated, tag = "1")] pub charges: ::prost::alloc::vec::Vec, } +/// Lists PlatformCharges for a batch of invoice ids, refunds pre-attached. +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct ListChargesForInvoiceIdsRequest { + #[prost(uint64, repeated, tag = "1")] + pub invoice_ids: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ListChargesForInvoiceIdsResponse { + #[prost(message, repeated, tag = "1")] + pub charges: ::prost::alloc::vec::Vec, +} +/// Lists PlatformCharges for an organization, refunds pre-attached. +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct ListChargesForOrganizationRequest { + #[prost(uint64, tag = "1")] + pub organization_id: u64, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ListChargesForOrganizationResponse { + #[prost(message, repeated, tag = "1")] + pub charges: ::prost::alloc::vec::Vec, +} /// Lists every recorded refund associated with the charges for a single /// platform invoice. Callers in the presentation layer use this to render /// invoice-level refund state without crossing the charge service boundary. 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 65224b73..3d4853ac 100644 --- a/rust/src/sentry_protos.billing.v1.services.contract.v1.rs +++ b/rust/src/sentry_protos.billing.v1.services.contract.v1.rs @@ -472,6 +472,8 @@ pub struct ContractMetadata { pub billing_features: ::core::option::Option, #[prost(string, tag = "9")] pub package_uid: ::prost::alloc::string::String, + #[prost(uint64, optional, tag = "10")] + pub previous_id: ::core::option::Option, #[deprecated] #[prost(uint64, tag = "8")] pub package_id: u64, @@ -584,6 +586,18 @@ pub struct GetInvoiceResponse { #[prost(message, optional, tag = "1")] pub invoice: ::core::option::Option, } +/// Batch-resolves PlatformInvoice ids to their guids. Missing ids are +/// silently omitted from the response. +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct GetInvoiceGuidsForIdsRequest { + #[prost(uint64, repeated, tag = "1")] + pub invoice_ids: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetInvoiceGuidsForIdsResponse { + #[prost(map = "uint64, string", tag = "1")] + pub invoice_guids: ::std::collections::HashMap, +} #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct GetUnchargedInvoicesRequest { /// Returns Invoices whose current billing period ends before this time and @@ -746,3 +760,15 @@ pub struct RolloverContractResponse { #[prost(uint64, tag = "4")] pub new_contract_id: u64, } +/// Atomically stamps manual_payment_started_at on an unpaid PlatformInvoice. +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct StartManualPaymentRequest { + #[prost(uint64, tag = "1")] + pub invoice_id: u64, +} +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct StartManualPaymentResponse { + /// False when no row matched paid=false (invoice paid, missing, or raced). + #[prost(bool, tag = "1")] + pub updated: bool, +}