162 lines
5.3 KiB
Protocol Buffer
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);
|
|
}
|