// fees/v1/fees.proto syntax = "proto3"; package fees.v1; option go_package = "github.com/tech/sendico/pkg/proto/billing/fees/v1;feesv1"; import "google/protobuf/timestamp.proto"; import "common/money/v1/money.proto"; import "common/fx/v1/fx.proto"; import "common/accounting/v1/posting.proto"; import "common/trace/v1/trace.proto"; // -------------------- // Core business enums // -------------------- enum Trigger { TRIGGER_UNSPECIFIED = 0; TRIGGER_CAPTURE = 1; TRIGGER_REFUND = 2; TRIGGER_DISPUTE = 3; TRIGGER_PAYOUT = 4; TRIGGER_FX_CONVERSION = 5; } // What to do if net-payable is insufficient to collect fees now. enum InsufficientNetPolicy { INSUFFICIENT_NET_UNSPECIFIED = 0; BLOCK_POSTING = 1; // fail the request SWEEP_ORG_CASH = 2; // charge an org cash account INVOICE_LATER = 3; // return zero lines; AR later } // Optional per-call overrides (rare). message PolicyOverrides { InsufficientNetPolicy insufficient_net = 1; } // -------------------- // Request/response meta // -------------------- message RequestMeta { string organization_ref = 1; // org scope; tenant resolved internally common.trace.v1.TraceContext trace = 2; } message ResponseMeta { common.trace.v1.TraceContext trace = 1; } // -------------------- // Intent & outputs // -------------------- // What the ledger/PO intends to do; used to select plan rules. message Intent { Trigger trigger = 1; common.money.v1.Money base_amount = 2; // fee base (e.g., captured gross) google.protobuf.Timestamp booked_at = 3; // for effective-dated plan string origin_type = 4; // e.g., "charge.capture" string origin_ref = 5; // gateway or business id map attributes = 6; // e.g., mcc, card_present, country } // FX details actually used during fee calc (if any). message FXUsed { common.fx.v1.CurrencyPair pair = 1; common.fx.v1.Side side = 2; common.money.v1.Decimal rate = 3; // applied rate int64 asof_unix_ms = 4; // source timestamp (ms) string provider = 5; // e.g., "ECB", "XE" string rate_ref = 6; // provider ref/id common.money.v1.Decimal spread_bps = 7; // applied spread } // A derived posting line ready for the ledger to post as-is. message DerivedPostingLine { string ledger_account_ref = 1; // resolved account common.money.v1.Money money = 2; // amount/currency common.accounting.v1.PostingLineType line_type = 3; // FEE/TAX/SPREAD/REVERSAL common.accounting.v1.EntrySide side = 4; // DEBIT/CREDIT map meta = 5; // fee_rule_id, rule_version, tax_code, tax_rate, fx_rate_used, etc. } // Snapshot of rules applied for audit/replay. message AppliedRule { string rule_id = 1; string rule_version = 2; string formula = 3; // e.g., "2.90% + 0.30 (min 0.50)" common.money.v1.RoundingMode rounding = 4; string tax_code = 5; // if applicable string tax_rate = 6; // decimal string map parameters = 7; // thresholds, tiers, etc. } // -------------------- // RPC: synchronous quote for posting // -------------------- message QuoteFeesRequest { RequestMeta meta = 1; Intent intent = 2; PolicyOverrides policy = 3; } message QuoteFeesResponse { ResponseMeta meta = 1; repeated DerivedPostingLine lines = 2; // derived fee/tax/spread lines repeated AppliedRule applied = 3; // rules snapshot FXUsed fx_used = 4; // optional if FX participated } // -------------------- // RPC: pre-pricing (UI/PO) with signed token // -------------------- message PrecomputeFeesRequest { RequestMeta meta = 1; Intent intent = 2; int64 ttl_ms = 3; // token validity window } message PrecomputeFeesResponse { ResponseMeta meta = 1; string fee_quote_token = 2; // opaque, signed google.protobuf.Timestamp expires_at = 3; // Optional preview so UI can render exact numbers now: repeated DerivedPostingLine lines = 4; repeated AppliedRule applied = 5; FXUsed fx_used = 6; } // -------------------- // RPC: validate/decode a token before posting // -------------------- message ValidateFeeTokenRequest { RequestMeta meta = 1; string fee_quote_token = 2; } message ValidateFeeTokenResponse { ResponseMeta meta = 1; bool valid = 2; string reason = 3; // if invalid // If valid, return normalized content embedded in the token: Intent intent = 4; repeated DerivedPostingLine lines = 5; repeated AppliedRule applied = 6; FXUsed fx_used = 7; } // -------------------- // Service // -------------------- service FeeEngine { // Compute derived fee/tax/spread lines for immediate posting. rpc QuoteFees (QuoteFeesRequest) returns (QuoteFeesResponse); // Pre-price for UX/PO and return a signed token to post later. rpc PrecomputeFees (PrecomputeFeesRequest) returns (PrecomputeFeesResponse); // Verify/expand a token just before posting (prevents policy drift). rpc ValidateFeeToken (ValidateFeeTokenRequest) returns (ValidateFeeTokenResponse); }