service backend
This commit is contained in:
161
api/proto/billing/fees/v1/fees.proto
Normal file
161
api/proto/billing/fees/v1/fees.proto
Normal file
@@ -0,0 +1,161 @@
|
||||
// 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);
|
||||
}
|
||||
Reference in New Issue
Block a user