Files
sendico/api/proto/billing/fees/v1/fees.proto
Stephan D 62a6631b9a
All checks were successful
ci/woodpecker/push/db Pipeline was successful
ci/woodpecker/push/nats Pipeline was successful
service backend
2025-11-07 18:35:26 +01:00

162 lines
5.3 KiB
Protocol Buffer

// 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<string,string> 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<string,string> 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<string,string> 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);
}