explicit fee execution
This commit is contained in:
106
Makefile
106
Makefile
@@ -43,6 +43,7 @@ COMPOSE := docker compose -f docker-compose.dev.yml --env-file .env.dev
|
|||||||
SERVICE ?=
|
SERVICE ?=
|
||||||
BACKEND_GOCACHE ?= $(CURDIR)/.gocache
|
BACKEND_GOCACHE ?= $(CURDIR)/.gocache
|
||||||
BACKEND_GOLANGCI_LINT_CACHE ?= $(CURDIR)/.golangci-cache
|
BACKEND_GOLANGCI_LINT_CACHE ?= $(CURDIR)/.golangci-cache
|
||||||
|
BACKEND_WAIT_TIMEOUT ?= 600
|
||||||
BACKEND_SERVICES := \
|
BACKEND_SERVICES := \
|
||||||
dev-discovery \
|
dev-discovery \
|
||||||
dev-fx-oracle \
|
dev-fx-oracle \
|
||||||
@@ -64,6 +65,19 @@ BACKEND_SERVICES := \
|
|||||||
dev-callbacks \
|
dev-callbacks \
|
||||||
dev-bff-vault-agent \
|
dev-bff-vault-agent \
|
||||||
dev-bff
|
dev-bff
|
||||||
|
BACKEND_STAGE_DISCOVERY := dev-discovery
|
||||||
|
BACKEND_STAGE_FX := dev-fx-oracle dev-fx-ingestor
|
||||||
|
BACKEND_STAGE_BILLING := dev-billing-fees dev-billing-documents
|
||||||
|
BACKEND_STAGE_GATEWAY_SIDECARS := dev-chain-gateway-vault-agent dev-tron-gateway-vault-agent
|
||||||
|
BACKEND_STAGE_GATEWAYS_LEDGER := dev-chain-gateway dev-tron-gateway dev-aurora-gateway dev-chsettle-gateway dev-ledger
|
||||||
|
BACKEND_STAGE_QUOTATION := dev-payments-quotation
|
||||||
|
BACKEND_STAGE_PAYMENTS_CORE := dev-payments-methods dev-payments-orchestrator
|
||||||
|
BACKEND_STAGE_CALLBACKS_AGENT := dev-callbacks-vault-agent
|
||||||
|
BACKEND_STAGE_CALLBACKS := dev-callbacks
|
||||||
|
BACKEND_STAGE_EDGE_FOUNDATION := dev-notification dev-bff-vault-agent
|
||||||
|
BACKEND_STAGE_EDGE := dev-bff
|
||||||
|
BACKEND_STAGE_EDGE_BUILD := dev-notification dev-bff
|
||||||
|
FRONTEND_SERVICE := dev-frontend
|
||||||
|
|
||||||
# Colors
|
# Colors
|
||||||
GREEN := \033[0;32m
|
GREEN := \033[0;32m
|
||||||
@@ -89,10 +103,10 @@ help:
|
|||||||
@echo ""
|
@echo ""
|
||||||
@echo "$(YELLOW)Selective Operations:$(NC)"
|
@echo "$(YELLOW)Selective Operations:$(NC)"
|
||||||
@echo " make infra-up Start infrastructure only (mongo, nats, vault)"
|
@echo " make infra-up Start infrastructure only (mongo, nats, vault)"
|
||||||
@echo " make services-up Start application services only"
|
@echo " make services-up Start application services only (ordered backend stages)"
|
||||||
@echo " make backend-up Start backend services only (no infrastructure/frontend)"
|
@echo " make backend-up Start backend services only in ordered stages (no infrastructure/frontend)"
|
||||||
@echo " make backend-down Stop backend services only"
|
@echo " make backend-down Stop backend services only"
|
||||||
@echo " make backend-rebuild Rebuild and restart backend services only"
|
@echo " make backend-rebuild Rebuild and restart backend services in ordered stages"
|
||||||
@echo " make list-services List all available services"
|
@echo " make list-services List all available services"
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo "$(YELLOW)Build Groups:$(NC)"
|
@echo "$(YELLOW)Build Groups:$(NC)"
|
||||||
@@ -166,7 +180,8 @@ build:
|
|||||||
# Start all services
|
# Start all services
|
||||||
up:
|
up:
|
||||||
@echo "$(GREEN)Starting development environment...$(NC)"
|
@echo "$(GREEN)Starting development environment...$(NC)"
|
||||||
@$(COMPOSE) up -d
|
@$(MAKE) --no-print-directory infra-up
|
||||||
|
@$(MAKE) --no-print-directory services-up
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo "$(GREEN)✅ Development environment started!$(NC)"
|
@echo "$(GREEN)✅ Development environment started!$(NC)"
|
||||||
@echo ""
|
@echo ""
|
||||||
@@ -272,39 +287,74 @@ infra-up:
|
|||||||
|
|
||||||
# Services only (assumes infra is running)
|
# Services only (assumes infra is running)
|
||||||
services-up:
|
services-up:
|
||||||
@echo "$(GREEN)Starting application services...$(NC)"
|
@echo "$(GREEN)Starting application services with ordered backend stages...$(NC)"
|
||||||
@$(COMPOSE) up -d \
|
@$(MAKE) --no-print-directory backend-up
|
||||||
dev-discovery \
|
@$(COMPOSE) up -d --no-deps --wait --wait-timeout $(BACKEND_WAIT_TIMEOUT) $(FRONTEND_SERVICE)
|
||||||
dev-fx-oracle \
|
|
||||||
dev-fx-ingestor \
|
|
||||||
dev-billing-fees \
|
|
||||||
dev-billing-documents \
|
|
||||||
dev-ledger \
|
|
||||||
dev-payments-orchestrator \
|
|
||||||
dev-payments-quotation \
|
|
||||||
dev-payments-methods \
|
|
||||||
dev-chain-gateway \
|
|
||||||
dev-tron-gateway \
|
|
||||||
dev-aurora-gateway \
|
|
||||||
dev-chsettle-gateway \
|
|
||||||
dev-notification \
|
|
||||||
dev-callbacks \
|
|
||||||
dev-bff \
|
|
||||||
dev-frontend
|
|
||||||
|
|
||||||
# Backend services only (no infrastructure, no frontend)
|
# Backend services only (no infrastructure, no frontend)
|
||||||
backend-up:
|
backend-up:
|
||||||
@echo "$(GREEN)Starting backend services only (no infra changes)...$(NC)"
|
@echo "$(GREEN)Starting backend services only (ordered build+start stages, no infra changes)...$(NC)"
|
||||||
@$(COMPOSE) up -d --no-deps $(BACKEND_SERVICES)
|
@echo "$(YELLOW)[1/8] discovery$(NC)"
|
||||||
|
@$(COMPOSE) build $(BACKEND_STAGE_DISCOVERY)
|
||||||
|
@$(COMPOSE) up -d --no-deps --wait --wait-timeout $(BACKEND_WAIT_TIMEOUT) $(BACKEND_STAGE_DISCOVERY)
|
||||||
|
@echo "$(YELLOW)[2/8] fx (oracle + ingestor)$(NC)"
|
||||||
|
@$(COMPOSE) build $(BACKEND_STAGE_FX)
|
||||||
|
@$(COMPOSE) up -d --no-deps --wait --wait-timeout $(BACKEND_WAIT_TIMEOUT) $(BACKEND_STAGE_FX)
|
||||||
|
@echo "$(YELLOW)[3/8] billing (fees + documents)$(NC)"
|
||||||
|
@$(COMPOSE) build $(BACKEND_STAGE_BILLING)
|
||||||
|
@$(COMPOSE) up -d --no-deps --wait --wait-timeout $(BACKEND_WAIT_TIMEOUT) $(BACKEND_STAGE_BILLING)
|
||||||
|
@echo "$(YELLOW)[4/8] gateways + ledger$(NC)"
|
||||||
|
@$(COMPOSE) build $(BACKEND_STAGE_GATEWAYS_LEDGER)
|
||||||
|
@$(COMPOSE) up -d --no-deps --wait --wait-timeout $(BACKEND_WAIT_TIMEOUT) $(BACKEND_STAGE_GATEWAY_SIDECARS)
|
||||||
|
@$(COMPOSE) up -d --no-deps --wait --wait-timeout $(BACKEND_WAIT_TIMEOUT) $(BACKEND_STAGE_GATEWAYS_LEDGER)
|
||||||
|
@echo "$(YELLOW)[5/8] quotation$(NC)"
|
||||||
|
@$(COMPOSE) build $(BACKEND_STAGE_QUOTATION)
|
||||||
|
@$(COMPOSE) up -d --no-deps --wait --wait-timeout $(BACKEND_WAIT_TIMEOUT) $(BACKEND_STAGE_QUOTATION)
|
||||||
|
@echo "$(YELLOW)[6/8] orchestrator + methods$(NC)"
|
||||||
|
@$(COMPOSE) build $(BACKEND_STAGE_PAYMENTS_CORE)
|
||||||
|
@$(COMPOSE) up -d --no-deps --wait --wait-timeout $(BACKEND_WAIT_TIMEOUT) $(BACKEND_STAGE_PAYMENTS_CORE)
|
||||||
|
@echo "$(YELLOW)[7/8] callbacks$(NC)"
|
||||||
|
@$(COMPOSE) build $(BACKEND_STAGE_CALLBACKS)
|
||||||
|
@$(COMPOSE) up -d --no-deps --wait --wait-timeout $(BACKEND_WAIT_TIMEOUT) $(BACKEND_STAGE_CALLBACKS_AGENT)
|
||||||
|
@$(COMPOSE) up -d --no-deps --wait --wait-timeout $(BACKEND_WAIT_TIMEOUT) $(BACKEND_STAGE_CALLBACKS)
|
||||||
|
@echo "$(YELLOW)[8/8] edge (notification + bff)$(NC)"
|
||||||
|
@$(COMPOSE) build $(BACKEND_STAGE_EDGE_BUILD)
|
||||||
|
@$(COMPOSE) up -d --no-deps --wait --wait-timeout $(BACKEND_WAIT_TIMEOUT) $(BACKEND_STAGE_EDGE_FOUNDATION)
|
||||||
|
@$(COMPOSE) up -d --no-deps --wait --wait-timeout $(BACKEND_WAIT_TIMEOUT) $(BACKEND_STAGE_EDGE)
|
||||||
|
|
||||||
backend-down:
|
backend-down:
|
||||||
@echo "$(YELLOW)Stopping backend services only...$(NC)"
|
@echo "$(YELLOW)Stopping backend services only...$(NC)"
|
||||||
@$(COMPOSE) stop $(BACKEND_SERVICES)
|
@$(COMPOSE) stop $(BACKEND_SERVICES)
|
||||||
|
|
||||||
backend-rebuild:
|
backend-rebuild:
|
||||||
@echo "$(GREEN)Rebuilding backend services only (no infra changes)...$(NC)"
|
@echo "$(GREEN)Rebuilding backend services only (ordered stages, no infra changes)...$(NC)"
|
||||||
@$(COMPOSE) build $(BACKEND_SERVICES)
|
@echo "$(YELLOW)[1/8] discovery$(NC)"
|
||||||
@$(COMPOSE) up -d --no-deps --force-recreate $(BACKEND_SERVICES)
|
@$(COMPOSE) build $(BACKEND_STAGE_DISCOVERY)
|
||||||
|
@$(COMPOSE) up -d --no-deps --force-recreate --wait --wait-timeout $(BACKEND_WAIT_TIMEOUT) $(BACKEND_STAGE_DISCOVERY)
|
||||||
|
@echo "$(YELLOW)[2/8] fx (oracle + ingestor)$(NC)"
|
||||||
|
@$(COMPOSE) build $(BACKEND_STAGE_FX)
|
||||||
|
@$(COMPOSE) up -d --no-deps --force-recreate --wait --wait-timeout $(BACKEND_WAIT_TIMEOUT) $(BACKEND_STAGE_FX)
|
||||||
|
@echo "$(YELLOW)[3/8] billing (fees + documents)$(NC)"
|
||||||
|
@$(COMPOSE) build $(BACKEND_STAGE_BILLING)
|
||||||
|
@$(COMPOSE) up -d --no-deps --force-recreate --wait --wait-timeout $(BACKEND_WAIT_TIMEOUT) $(BACKEND_STAGE_BILLING)
|
||||||
|
@echo "$(YELLOW)[4/8] gateways + ledger$(NC)"
|
||||||
|
@$(COMPOSE) build $(BACKEND_STAGE_GATEWAYS_LEDGER)
|
||||||
|
@$(COMPOSE) up -d --no-deps --wait --wait-timeout $(BACKEND_WAIT_TIMEOUT) $(BACKEND_STAGE_GATEWAY_SIDECARS)
|
||||||
|
@$(COMPOSE) up -d --no-deps --force-recreate --wait --wait-timeout $(BACKEND_WAIT_TIMEOUT) $(BACKEND_STAGE_GATEWAYS_LEDGER)
|
||||||
|
@echo "$(YELLOW)[5/8] quotation$(NC)"
|
||||||
|
@$(COMPOSE) build $(BACKEND_STAGE_QUOTATION)
|
||||||
|
@$(COMPOSE) up -d --no-deps --force-recreate --wait --wait-timeout $(BACKEND_WAIT_TIMEOUT) $(BACKEND_STAGE_QUOTATION)
|
||||||
|
@echo "$(YELLOW)[6/8] orchestrator + methods$(NC)"
|
||||||
|
@$(COMPOSE) build $(BACKEND_STAGE_PAYMENTS_CORE)
|
||||||
|
@$(COMPOSE) up -d --no-deps --force-recreate --wait --wait-timeout $(BACKEND_WAIT_TIMEOUT) $(BACKEND_STAGE_PAYMENTS_CORE)
|
||||||
|
@echo "$(YELLOW)[7/8] callbacks$(NC)"
|
||||||
|
@$(COMPOSE) build $(BACKEND_STAGE_CALLBACKS)
|
||||||
|
@$(COMPOSE) up -d --no-deps --wait --wait-timeout $(BACKEND_WAIT_TIMEOUT) $(BACKEND_STAGE_CALLBACKS_AGENT)
|
||||||
|
@$(COMPOSE) up -d --no-deps --force-recreate --wait --wait-timeout $(BACKEND_WAIT_TIMEOUT) $(BACKEND_STAGE_CALLBACKS)
|
||||||
|
@echo "$(YELLOW)[8/8] edge (notification + bff)$(NC)"
|
||||||
|
@$(COMPOSE) build $(BACKEND_STAGE_EDGE_BUILD)
|
||||||
|
@$(COMPOSE) up -d --no-deps --wait --wait-timeout $(BACKEND_WAIT_TIMEOUT) $(BACKEND_STAGE_EDGE_FOUNDATION)
|
||||||
|
@$(COMPOSE) up -d --no-deps --force-recreate --wait --wait-timeout $(BACKEND_WAIT_TIMEOUT) $(BACKEND_STAGE_EDGE)
|
||||||
@echo "$(GREEN)✅ Backend services rebuilt$(NC)"
|
@echo "$(GREEN)✅ Backend services rebuilt$(NC)"
|
||||||
|
|
||||||
# Status check
|
# Status check
|
||||||
|
|||||||
@@ -103,6 +103,108 @@ func TestCompile_ExternalToExternal_BridgeExpansion(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCompile_ExternalToExternal_WithWalletFee_InsertsFeeStep(t *testing.T) {
|
||||||
|
compiler := New()
|
||||||
|
|
||||||
|
graph, err := compiler.Compile(Input{
|
||||||
|
IntentSnapshot: testIntent(model.PaymentKindPayout),
|
||||||
|
QuoteSnapshot: &model.PaymentQuoteSnapshot{
|
||||||
|
DebitAmount: &paymenttypes.Money{Amount: "5.842042", Currency: "USDT"},
|
||||||
|
FeeLines: []*paymenttypes.FeeLine{
|
||||||
|
{
|
||||||
|
Money: &paymenttypes.Money{Amount: "0.42", Currency: "USDT"},
|
||||||
|
Side: paymenttypes.EntrySideDebit,
|
||||||
|
Meta: map[string]string{"fee_target": "wallet"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Route: &paymenttypes.QuoteRouteSpecification{
|
||||||
|
RouteRef: "route-1",
|
||||||
|
Hops: []*paymenttypes.QuoteRouteHop{
|
||||||
|
{
|
||||||
|
Index: 10,
|
||||||
|
Rail: "CRYPTO",
|
||||||
|
Gateway: "gw-crypto",
|
||||||
|
InstanceID: "crypto-1",
|
||||||
|
Role: paymenttypes.QuoteRouteHopRoleSource,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Index: 20,
|
||||||
|
Rail: "CARD",
|
||||||
|
Gateway: "gw-card",
|
||||||
|
InstanceID: "card-1",
|
||||||
|
Role: paymenttypes.QuoteRouteHopRoleDestination,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Compile returned error: %v", err)
|
||||||
|
}
|
||||||
|
if graph == nil {
|
||||||
|
t.Fatal("expected graph")
|
||||||
|
}
|
||||||
|
if got, want := len(graph.Steps), 10; got != want {
|
||||||
|
t.Fatalf("expected 10 steps, got %d", got)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertStep(t, graph.Steps[0], "hop.10.crypto.send", discovery.RailOperationSend, discovery.RailCrypto, model.ReportVisibilityBackoffice)
|
||||||
|
assertStep(t, graph.Steps[1], "hop.10.crypto.fee", discovery.RailOperationFee, discovery.RailCrypto, model.ReportVisibilityHidden)
|
||||||
|
assertStep(t, graph.Steps[2], "hop.10.crypto.observe", discovery.RailOperationObserveConfirm, discovery.RailCrypto, model.ReportVisibilityBackoffice)
|
||||||
|
assertStep(t, graph.Steps[3], "edge.10_20.ledger.credit", discovery.RailOperationExternalCredit, discovery.RailLedger, model.ReportVisibilityHidden)
|
||||||
|
assertStep(t, graph.Steps[4], "edge.10_20.ledger.block", discovery.RailOperationBlock, discovery.RailLedger, model.ReportVisibilityHidden)
|
||||||
|
assertStep(t, graph.Steps[5], "hop.20.card_payout.send", discovery.RailOperationSend, discovery.RailCardPayout, model.ReportVisibilityUser)
|
||||||
|
assertStep(t, graph.Steps[6], "hop.20.card_payout.observe", discovery.RailOperationObserveConfirm, discovery.RailCardPayout, model.ReportVisibilityUser)
|
||||||
|
assertStep(t, graph.Steps[7], "edge.10_20.ledger.debit", discovery.RailOperationExternalDebit, discovery.RailLedger, model.ReportVisibilityHidden)
|
||||||
|
assertStep(t, graph.Steps[8], "edge.10_20.ledger.release", discovery.RailOperationRelease, discovery.RailLedger, model.ReportVisibilityHidden)
|
||||||
|
assertStep(t, graph.Steps[9], "edge.10_20.ledger.release", discovery.RailOperationRelease, discovery.RailLedger, model.ReportVisibilityHidden)
|
||||||
|
|
||||||
|
if got, want := graph.Steps[1].DependsOn, []string{graph.Steps[0].StepRef}; !equalStringSlice(got, want) {
|
||||||
|
t.Fatalf("fee deps mismatch: got=%v want=%v", got, want)
|
||||||
|
}
|
||||||
|
if got, want := graph.Steps[2].DependsOn, []string{graph.Steps[1].StepRef}; !equalStringSlice(got, want) {
|
||||||
|
t.Fatalf("observe deps mismatch: got=%v want=%v", got, want)
|
||||||
|
}
|
||||||
|
if got, want := graph.Steps[3].DependsOn, []string{graph.Steps[2].StepRef}; !equalStringSlice(got, want) {
|
||||||
|
t.Fatalf("credit deps mismatch: got=%v want=%v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCompile_ExternalToExternal_IgnoresNonWalletFeeLines(t *testing.T) {
|
||||||
|
compiler := New()
|
||||||
|
|
||||||
|
graph, err := compiler.Compile(Input{
|
||||||
|
IntentSnapshot: testIntent(model.PaymentKindPayout),
|
||||||
|
QuoteSnapshot: &model.PaymentQuoteSnapshot{
|
||||||
|
DebitAmount: &paymenttypes.Money{Amount: "5.842042", Currency: "USDT"},
|
||||||
|
FeeLines: []*paymenttypes.FeeLine{
|
||||||
|
{
|
||||||
|
Money: &paymenttypes.Money{Amount: "0.42", Currency: "USDT"},
|
||||||
|
Side: paymenttypes.EntrySideDebit,
|
||||||
|
Meta: map[string]string{"fee_target": "ledger"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Route: &paymenttypes.QuoteRouteSpecification{
|
||||||
|
Hops: []*paymenttypes.QuoteRouteHop{
|
||||||
|
{Index: 10, Rail: "CRYPTO", Role: paymenttypes.QuoteRouteHopRoleSource},
|
||||||
|
{Index: 20, Rail: "CARD", Role: paymenttypes.QuoteRouteHopRoleDestination},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Compile returned error: %v", err)
|
||||||
|
}
|
||||||
|
if got, want := len(graph.Steps), 9; got != want {
|
||||||
|
t.Fatalf("expected 9 steps, got %d", got)
|
||||||
|
}
|
||||||
|
for i := range graph.Steps {
|
||||||
|
if got := graph.Steps[i].Action; got == discovery.RailOperationFee {
|
||||||
|
t.Fatalf("unexpected fee step at index %d: %+v", i, graph.Steps[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCompile_InternalToExternal_UsesHoldAndSettlementBranches(t *testing.T) {
|
func TestCompile_InternalToExternal_UsesHoldAndSettlementBranches(t *testing.T) {
|
||||||
compiler := New()
|
compiler := New()
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,30 @@ func (e *expansion) nextRef(base string) string {
|
|||||||
return token + "_" + itoa(count+1)
|
return token + "_" + itoa(count+1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *expansion) needsWalletFeeStep(hop normalizedHop) bool {
|
||||||
|
if e == nil || len(e.walletFeeHops) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
key := observedKey(hop)
|
||||||
|
if _, ok := e.walletFeeHops[key]; !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if _, emitted := e.walletFeeEmitted[key]; emitted {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *expansion) markWalletFeeEmitted(hop normalizedHop) {
|
||||||
|
if e == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if e.walletFeeEmitted == nil {
|
||||||
|
e.walletFeeEmitted = map[string]struct{}{}
|
||||||
|
}
|
||||||
|
e.walletFeeEmitted[observedKey(hop)] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
func normalizeStep(step Step) Step {
|
func normalizeStep(step Step) Step {
|
||||||
step.StepRef = strings.TrimSpace(step.StepRef)
|
step.StepRef = strings.TrimSpace(step.StepRef)
|
||||||
step.StepCode = strings.TrimSpace(step.StepCode)
|
step.StepCode = strings.TrimSpace(step.StepCode)
|
||||||
|
|||||||
@@ -0,0 +1,128 @@
|
|||||||
|
package xplan
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
|
|
||||||
|
"github.com/tech/sendico/payments/storage/model"
|
||||||
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
|
paymenttypes "github.com/tech/sendico/pkg/payments/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func planWalletFeeHops(
|
||||||
|
hops []normalizedHop,
|
||||||
|
intent model.PaymentIntent,
|
||||||
|
quote *model.PaymentQuoteSnapshot,
|
||||||
|
) (map[string]struct{}, error) {
|
||||||
|
_, hasWalletFee, err := walletFeeAmountFromSnapshots(intent, quote)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !hasWalletFee {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceHop, ok := sourceCryptoHop(hops)
|
||||||
|
if !ok {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return map[string]struct{}{
|
||||||
|
observedKey(sourceHop): {},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func sourceCryptoHop(hops []normalizedHop) (normalizedHop, bool) {
|
||||||
|
for i := range hops {
|
||||||
|
if hops[i].rail == discovery.RailCrypto && hops[i].role == paymenttypes.QuoteRouteHopRoleSource {
|
||||||
|
return hops[i], true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(hops) > 0 && hops[0].rail == discovery.RailCrypto {
|
||||||
|
return hops[0], true
|
||||||
|
}
|
||||||
|
return normalizedHop{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func walletFeeAmountFromSnapshots(
|
||||||
|
intent model.PaymentIntent,
|
||||||
|
quote *model.PaymentQuoteSnapshot,
|
||||||
|
) (*paymenttypes.Money, bool, error) {
|
||||||
|
if quote == nil || len(quote.FeeLines) == 0 {
|
||||||
|
return nil, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceCurrency := ""
|
||||||
|
if source := feePlanningSourceAmount(intent, quote); source != nil {
|
||||||
|
sourceCurrency = strings.TrimSpace(source.Currency)
|
||||||
|
}
|
||||||
|
|
||||||
|
total := decimal.Zero
|
||||||
|
currency := ""
|
||||||
|
for i, line := range quote.FeeLines {
|
||||||
|
if !isWalletDebitFeeLine(line) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
money := line.GetMoney()
|
||||||
|
if money == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
lineCurrency := strings.TrimSpace(money.GetCurrency())
|
||||||
|
if lineCurrency == "" {
|
||||||
|
return nil, false, merrors.InvalidArgument(fmt.Sprintf("quote_snapshot.fee_lines[%d].money.currency is required", i))
|
||||||
|
}
|
||||||
|
if sourceCurrency != "" && !strings.EqualFold(sourceCurrency, lineCurrency) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if currency == "" {
|
||||||
|
currency = lineCurrency
|
||||||
|
} else if !strings.EqualFold(currency, lineCurrency) {
|
||||||
|
return nil, false, merrors.InvalidArgument("quote_snapshot.fee_lines wallet fee currency mismatch")
|
||||||
|
}
|
||||||
|
|
||||||
|
amountRaw := strings.TrimSpace(money.GetAmount())
|
||||||
|
amount, err := decimal.NewFromString(amountRaw)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, merrors.InvalidArgument(fmt.Sprintf("quote_snapshot.fee_lines[%d].money.amount is invalid", i))
|
||||||
|
}
|
||||||
|
if amount.Sign() < 0 {
|
||||||
|
amount = amount.Neg()
|
||||||
|
}
|
||||||
|
if amount.Sign() == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
total = total.Add(amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
if total.Sign() <= 0 {
|
||||||
|
return nil, false, nil
|
||||||
|
}
|
||||||
|
return &paymenttypes.Money{
|
||||||
|
Amount: total.String(),
|
||||||
|
Currency: strings.ToUpper(strings.TrimSpace(currency)),
|
||||||
|
}, true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func feePlanningSourceAmount(intent model.PaymentIntent, quote *model.PaymentQuoteSnapshot) *paymenttypes.Money {
|
||||||
|
if quote != nil && quote.DebitAmount != nil {
|
||||||
|
return quote.DebitAmount
|
||||||
|
}
|
||||||
|
return intent.Amount
|
||||||
|
}
|
||||||
|
|
||||||
|
func isWalletDebitFeeLine(line *paymenttypes.FeeLine) bool {
|
||||||
|
if line == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if line.GetSide() != paymenttypes.EntrySideDebit {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
meta := line.Meta
|
||||||
|
if len(meta) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return strings.EqualFold(strings.TrimSpace(meta["fee_target"]), "wallet")
|
||||||
|
}
|
||||||
@@ -30,12 +30,16 @@ type expansion struct {
|
|||||||
lastMainRef string
|
lastMainRef string
|
||||||
refSeq map[string]int
|
refSeq map[string]int
|
||||||
externalObserved map[string]string
|
externalObserved map[string]string
|
||||||
|
walletFeeHops map[string]struct{}
|
||||||
|
walletFeeEmitted map[string]struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newExpansion() *expansion {
|
func newExpansion(walletFeeHops map[string]struct{}) *expansion {
|
||||||
return &expansion{
|
return &expansion{
|
||||||
refSeq: map[string]int{},
|
refSeq: map[string]int{},
|
||||||
externalObserved: map[string]string{},
|
externalObserved: map[string]string{},
|
||||||
|
walletFeeHops: walletFeeHops,
|
||||||
|
walletFeeEmitted: map[string]struct{}{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +90,12 @@ func (s *svc) Compile(in Input) (graph *Graph, err error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ex := newExpansion()
|
walletFeeHops, err := planWalletFeeHops(hops, in.IntentSnapshot, in.QuoteSnapshot)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ex := newExpansion(walletFeeHops)
|
||||||
appendGuards(ex, conditions)
|
appendGuards(ex, conditions)
|
||||||
|
|
||||||
if len(hops) == 1 {
|
if len(hops) == 1 {
|
||||||
|
|||||||
@@ -147,10 +147,19 @@ func (s *svc) ensureExternalObserved(ex *expansion, hop normalizedHop, intent mo
|
|||||||
|
|
||||||
sendStep := makeRailSendStep(hop, intent)
|
sendStep := makeRailSendStep(hop, intent)
|
||||||
sendRef := ex.appendMain(sendStep)
|
sendRef := ex.appendMain(sendStep)
|
||||||
|
observeDependency := sendRef
|
||||||
|
if ex.needsWalletFeeStep(hop) {
|
||||||
|
feeStep := makeRailFeeStep(hop)
|
||||||
|
if observeDependency != "" {
|
||||||
|
feeStep.DependsOn = []string{observeDependency}
|
||||||
|
}
|
||||||
|
observeDependency = ex.appendMain(feeStep)
|
||||||
|
ex.markWalletFeeEmitted(hop)
|
||||||
|
}
|
||||||
|
|
||||||
observeStep := makeRailObserveStep(hop, intent)
|
observeStep := makeRailObserveStep(hop, intent)
|
||||||
if sendRef != "" {
|
if observeDependency != "" {
|
||||||
observeStep.DependsOn = []string{sendRef}
|
observeStep.DependsOn = []string{observeDependency}
|
||||||
}
|
}
|
||||||
observeRef := ex.appendMain(observeStep)
|
observeRef := ex.appendMain(observeStep)
|
||||||
|
|
||||||
@@ -158,6 +167,20 @@ func (s *svc) ensureExternalObserved(ex *expansion, hop normalizedHop, intent mo
|
|||||||
return observeRef, nil
|
return observeRef, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeRailFeeStep(hop normalizedHop) Step {
|
||||||
|
return Step{
|
||||||
|
StepCode: singleHopCode(hop, "fee"),
|
||||||
|
Kind: StepKindRailSend,
|
||||||
|
Action: discovery.RailOperationFee,
|
||||||
|
Rail: hop.rail,
|
||||||
|
Gateway: hop.gateway,
|
||||||
|
InstanceID: hop.instanceID,
|
||||||
|
HopIndex: hop.index,
|
||||||
|
HopRole: hop.role,
|
||||||
|
Visibility: model.ReportVisibilityHidden,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func appendGuards(ex *expansion, conditions *paymenttypes.QuoteExecutionConditions) {
|
func appendGuards(ex *expansion, conditions *paymenttypes.QuoteExecutionConditions) {
|
||||||
if conditions == nil {
|
if conditions == nil {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ func resolveStepContext(
|
|||||||
|
|
||||||
func kindForAction(action model.RailOperation) StepKind {
|
func kindForAction(action model.RailOperation) StepKind {
|
||||||
switch action {
|
switch action {
|
||||||
case discovery.RailOperationSend, discovery.RailOperationFXConvert:
|
case discovery.RailOperationSend, discovery.RailOperationFXConvert, discovery.RailOperationFee:
|
||||||
return StepKindRailSend
|
return StepKindRailSend
|
||||||
case discovery.RailOperationObserveConfirm:
|
case discovery.RailOperationObserveConfirm:
|
||||||
return StepKindRailObserve
|
return StepKindRailObserve
|
||||||
|
|||||||
@@ -99,11 +99,6 @@ func (e *gatewayCryptoExecutor) ExecuteCrypto(ctx context.Context, req sexec.Ste
|
|||||||
}
|
}
|
||||||
step.ConvertedMoney = nil
|
step.ConvertedMoney = nil
|
||||||
|
|
||||||
if action == discovery.RailOperationSend {
|
|
||||||
if err := e.submitWalletFeeTransfer(ctx, req, client, gateway, sourceWalletRef, operationRef, idempotencyKey); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
step.State = agg.StepStateCompleted
|
step.State = agg.StepStateCompleted
|
||||||
step.FailureCode = ""
|
step.FailureCode = ""
|
||||||
step.FailureMsg = ""
|
step.FailureMsg = ""
|
||||||
@@ -207,64 +202,6 @@ func sourceAmount(payment *agg.Payment, action model.RailOperation) (*moneyv1.Mo
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *gatewayCryptoExecutor) submitWalletFeeTransfer(
|
|
||||||
ctx context.Context,
|
|
||||||
req sexec.StepRequest,
|
|
||||||
client chainclient.Client,
|
|
||||||
gateway *model.GatewayInstanceDescriptor,
|
|
||||||
sourceWalletRef string,
|
|
||||||
operationRef string,
|
|
||||||
idempotencyKey string,
|
|
||||||
) error {
|
|
||||||
if req.Payment == nil {
|
|
||||||
return merrors.InvalidArgument("crypto send: payment is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
feeAmount, ok, err := walletFeeAmount(req.Payment)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
destination, err := e.resolveDestination(ctx, client, req.Payment, discovery.RailOperationFee)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
feeMoney := &moneyv1.Money{
|
|
||||||
Amount: strings.TrimSpace(feeAmount.GetAmount()),
|
|
||||||
Currency: strings.TrimSpace(feeAmount.GetCurrency()),
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := client.SubmitTransfer(ctx, &chainv1.SubmitTransferRequest{
|
|
||||||
IdempotencyKey: strings.TrimSpace(idempotencyKey) + ":fee",
|
|
||||||
OrganizationRef: req.Payment.OrganizationRef.Hex(),
|
|
||||||
SourceWalletRef: sourceWalletRef,
|
|
||||||
Destination: destination,
|
|
||||||
Amount: feeMoney,
|
|
||||||
OperationRef: strings.TrimSpace(operationRef) + ":fee",
|
|
||||||
IntentRef: strings.TrimSpace(req.Payment.IntentSnapshot.Ref),
|
|
||||||
PaymentRef: strings.TrimSpace(req.Payment.PaymentRef),
|
|
||||||
Metadata: transferMetadata(req.Step),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if resp == nil || resp.GetTransfer() == nil {
|
|
||||||
return merrors.Internal("crypto send: fee transfer response is missing")
|
|
||||||
}
|
|
||||||
if _, err := transferExternalRefs(resp.GetTransfer(), firstNonEmpty(
|
|
||||||
strings.TrimSpace(req.Step.InstanceID),
|
|
||||||
strings.TrimSpace(gateway.InstanceID),
|
|
||||||
strings.TrimSpace(req.Step.Gateway),
|
|
||||||
strings.TrimSpace(gateway.ID),
|
|
||||||
)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func effectiveSourceAmount(payment *agg.Payment) *paymenttypes.Money {
|
func effectiveSourceAmount(payment *agg.Payment) *paymenttypes.Money {
|
||||||
if payment == nil {
|
if payment == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -204,32 +204,19 @@ func TestGatewayCryptoExecutor_ExecuteCrypto_MissingCardRoute(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGatewayCryptoExecutor_ExecuteCrypto_SubmitsWalletFeeTransferOnSend(t *testing.T) {
|
func TestGatewayCryptoExecutor_ExecuteCrypto_SendDoesNotSubmitWalletFeeTransfer(t *testing.T) {
|
||||||
orgID := bson.NewObjectID()
|
orgID := bson.NewObjectID()
|
||||||
|
|
||||||
submitRequests := make([]*chainv1.SubmitTransferRequest, 0, 2)
|
submitRequests := make([]*chainv1.SubmitTransferRequest, 0, 1)
|
||||||
client := &chainclient.Fake{
|
client := &chainclient.Fake{
|
||||||
SubmitTransferFn: func(_ context.Context, req *chainv1.SubmitTransferRequest) (*chainv1.SubmitTransferResponse, error) {
|
SubmitTransferFn: func(_ context.Context, req *chainv1.SubmitTransferRequest) (*chainv1.SubmitTransferResponse, error) {
|
||||||
submitRequests = append(submitRequests, req)
|
submitRequests = append(submitRequests, req)
|
||||||
switch len(submitRequests) {
|
return &chainv1.SubmitTransferResponse{
|
||||||
case 1:
|
Transfer: &chainv1.Transfer{
|
||||||
return &chainv1.SubmitTransferResponse{
|
TransferRef: "trf-principal",
|
||||||
Transfer: &chainv1.Transfer{
|
OperationRef: "op-principal",
|
||||||
TransferRef: "trf-principal",
|
},
|
||||||
OperationRef: "op-principal",
|
}, nil
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
case 2:
|
|
||||||
return &chainv1.SubmitTransferResponse{
|
|
||||||
Transfer: &chainv1.Transfer{
|
|
||||||
TransferRef: "trf-fee",
|
|
||||||
OperationRef: "op-fee",
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
default:
|
|
||||||
t.Fatalf("unexpected transfer submission call %d", len(submitRequests))
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
resolver := &fakeGatewayInvokeResolver{client: client}
|
resolver := &fakeGatewayInvokeResolver{client: client}
|
||||||
@@ -311,7 +298,7 @@ func TestGatewayCryptoExecutor_ExecuteCrypto_SubmitsWalletFeeTransferOnSend(t *t
|
|||||||
if out == nil {
|
if out == nil {
|
||||||
t.Fatal("expected output")
|
t.Fatal("expected output")
|
||||||
}
|
}
|
||||||
if got, want := len(submitRequests), 2; got != want {
|
if got, want := len(submitRequests), 1; got != want {
|
||||||
t.Fatalf("submit transfer calls mismatch: got=%d want=%d", got, want)
|
t.Fatalf("submit transfer calls mismatch: got=%d want=%d", got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,29 +309,12 @@ func TestGatewayCryptoExecutor_ExecuteCrypto_SubmitsWalletFeeTransferOnSend(t *t
|
|||||||
if got, want := principalReq.GetDestination().GetExternalAddress(), "TUA_DEST"; got != want {
|
if got, want := principalReq.GetDestination().GetExternalAddress(), "TUA_DEST"; got != want {
|
||||||
t.Fatalf("principal destination mismatch: got=%q want=%q", got, want)
|
t.Fatalf("principal destination mismatch: got=%q want=%q", got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
feeReq := submitRequests[1]
|
|
||||||
if got, want := feeReq.GetAmount().GetAmount(), "0.7"; got != want {
|
|
||||||
t.Fatalf("fee amount mismatch: got=%q want=%q", got, want)
|
|
||||||
}
|
|
||||||
if got, want := feeReq.GetAmount().GetCurrency(), "USDT"; got != want {
|
|
||||||
t.Fatalf("fee currency mismatch: got=%q want=%q", got, want)
|
|
||||||
}
|
|
||||||
if got, want := feeReq.GetDestination().GetExternalAddress(), "TUA_FEE"; got != want {
|
|
||||||
t.Fatalf("fee destination mismatch: got=%q want=%q", got, want)
|
|
||||||
}
|
|
||||||
if got, want := feeReq.GetOperationRef(), "payment-1:hop_1_crypto_send:fee"; got != want {
|
|
||||||
t.Fatalf("fee operation_ref mismatch: got=%q want=%q", got, want)
|
|
||||||
}
|
|
||||||
if got, want := feeReq.GetIdempotencyKey(), "idem-1:hop_1_crypto_send:fee"; got != want {
|
|
||||||
t.Fatalf("fee idempotency_key mismatch: got=%q want=%q", got, want)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGatewayCryptoExecutor_ExecuteCrypto_ResolvesFeeAddressFromFeeWalletRef(t *testing.T) {
|
func TestGatewayCryptoExecutor_ExecuteCrypto_FeeActionResolvesFeeAddressFromFeeWalletRef(t *testing.T) {
|
||||||
orgID := bson.NewObjectID()
|
orgID := bson.NewObjectID()
|
||||||
|
|
||||||
submitRequests := make([]*chainv1.SubmitTransferRequest, 0, 2)
|
submitRequests := make([]*chainv1.SubmitTransferRequest, 0, 1)
|
||||||
var managedWalletReq *chainv1.GetManagedWalletRequest
|
var managedWalletReq *chainv1.GetManagedWalletRequest
|
||||||
client := &chainclient.Fake{
|
client := &chainclient.Fake{
|
||||||
GetManagedWalletFn: func(_ context.Context, req *chainv1.GetManagedWalletRequest) (*chainv1.GetManagedWalletResponse, error) {
|
GetManagedWalletFn: func(_ context.Context, req *chainv1.GetManagedWalletRequest) (*chainv1.GetManagedWalletResponse, error) {
|
||||||
@@ -358,25 +328,12 @@ func TestGatewayCryptoExecutor_ExecuteCrypto_ResolvesFeeAddressFromFeeWalletRef(
|
|||||||
},
|
},
|
||||||
SubmitTransferFn: func(_ context.Context, req *chainv1.SubmitTransferRequest) (*chainv1.SubmitTransferResponse, error) {
|
SubmitTransferFn: func(_ context.Context, req *chainv1.SubmitTransferRequest) (*chainv1.SubmitTransferResponse, error) {
|
||||||
submitRequests = append(submitRequests, req)
|
submitRequests = append(submitRequests, req)
|
||||||
switch len(submitRequests) {
|
return &chainv1.SubmitTransferResponse{
|
||||||
case 1:
|
Transfer: &chainv1.Transfer{
|
||||||
return &chainv1.SubmitTransferResponse{
|
TransferRef: "trf-fee",
|
||||||
Transfer: &chainv1.Transfer{
|
OperationRef: "op-fee",
|
||||||
TransferRef: "trf-principal",
|
},
|
||||||
OperationRef: "op-principal",
|
}, nil
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
case 2:
|
|
||||||
return &chainv1.SubmitTransferResponse{
|
|
||||||
Transfer: &chainv1.Transfer{
|
|
||||||
TransferRef: "trf-fee",
|
|
||||||
OperationRef: "op-fee",
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
default:
|
|
||||||
t.Fatalf("unexpected transfer submission call %d", len(submitRequests))
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
resolver := &fakeGatewayInvokeResolver{client: client}
|
resolver := &fakeGatewayInvokeResolver{client: client}
|
||||||
@@ -437,16 +394,16 @@ func TestGatewayCryptoExecutor_ExecuteCrypto_ResolvesFeeAddressFromFeeWalletRef(
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Step: xplan.Step{
|
Step: xplan.Step{
|
||||||
StepRef: "hop_1_crypto_send",
|
StepRef: "hop_1_crypto_fee",
|
||||||
StepCode: "hop.1.crypto.send",
|
StepCode: "hop.1.crypto.fee",
|
||||||
Action: discovery.RailOperationSend,
|
Action: discovery.RailOperationFee,
|
||||||
Rail: discovery.RailCrypto,
|
Rail: discovery.RailCrypto,
|
||||||
Gateway: "crypto_rail_gateway_arbitrum_sepolia",
|
Gateway: "crypto_rail_gateway_arbitrum_sepolia",
|
||||||
InstanceID: "crypto_rail_gateway_arbitrum_sepolia",
|
InstanceID: "crypto_rail_gateway_arbitrum_sepolia",
|
||||||
},
|
},
|
||||||
StepExecution: agg.StepExecution{
|
StepExecution: agg.StepExecution{
|
||||||
StepRef: "hop_1_crypto_send",
|
StepRef: "hop_1_crypto_fee",
|
||||||
StepCode: "hop.1.crypto.send",
|
StepCode: "hop.1.crypto.fee",
|
||||||
Attempt: 1,
|
Attempt: 1,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -461,10 +418,13 @@ func TestGatewayCryptoExecutor_ExecuteCrypto_ResolvesFeeAddressFromFeeWalletRef(
|
|||||||
if got, want := managedWalletReq.GetWalletRef(), "fee-wallet-ref"; got != want {
|
if got, want := managedWalletReq.GetWalletRef(), "fee-wallet-ref"; got != want {
|
||||||
t.Fatalf("fee wallet ref lookup mismatch: got=%q want=%q", got, want)
|
t.Fatalf("fee wallet ref lookup mismatch: got=%q want=%q", got, want)
|
||||||
}
|
}
|
||||||
if got, want := len(submitRequests), 2; got != want {
|
if got, want := len(submitRequests), 1; got != want {
|
||||||
t.Fatalf("submit transfer calls mismatch: got=%d want=%d", got, want)
|
t.Fatalf("submit transfer calls mismatch: got=%d want=%d", got, want)
|
||||||
}
|
}
|
||||||
feeReq := submitRequests[1]
|
feeReq := submitRequests[0]
|
||||||
|
if got, want := feeReq.GetAmount().GetAmount(), "0.7"; got != want {
|
||||||
|
t.Fatalf("fee amount mismatch: got=%q want=%q", got, want)
|
||||||
|
}
|
||||||
if got, want := feeReq.GetDestination().GetExternalAddress(), "TUA_FEE_FROM_WALLET"; got != want {
|
if got, want := feeReq.GetDestination().GetExternalAddress(), "TUA_FEE_FROM_WALLET"; got != want {
|
||||||
t.Fatalf("fee destination mismatch: got=%q want=%q", got, want)
|
t.Fatalf("fee destination mismatch: got=%q want=%q", got, want)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -248,6 +248,12 @@ services:
|
|||||||
NATS_PASSWORD: ${NATS_PASSWORD}
|
NATS_PASSWORD: ${NATS_PASSWORD}
|
||||||
NATS_URL: nats://${NATS_USER}:${NATS_PASSWORD}@dev-nats:4222
|
NATS_URL: nats://${NATS_USER}:${NATS_PASSWORD}@dev-nats:4222
|
||||||
DISCOVERY_METRICS_PORT: 9407
|
DISCOVERY_METRICS_PORT: 9407
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "wget -q -O- http://127.0.0.1:9405/health | grep -q '\"status\":\"ok\"'"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 12
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# FX Oracle Service
|
# FX Oracle Service
|
||||||
@@ -286,6 +292,12 @@ services:
|
|||||||
NATS_URL: nats://${NATS_USER}:${NATS_PASSWORD}@dev-nats:4222
|
NATS_URL: nats://${NATS_USER}:${NATS_PASSWORD}@dev-nats:4222
|
||||||
FX_ORACLE_GRPC_PORT: 50051
|
FX_ORACLE_GRPC_PORT: 50051
|
||||||
FX_ORACLE_METRICS_PORT: 9400
|
FX_ORACLE_METRICS_PORT: 9400
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "wget -q -O- http://127.0.0.1:9400/health | grep -q '\"status\":\"ok\"'"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 12
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# Billing Fees Service
|
# Billing Fees Service
|
||||||
@@ -301,7 +313,7 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
dev-mongo-init: { condition: service_completed_successfully }
|
dev-mongo-init: { condition: service_completed_successfully }
|
||||||
dev-nats: { condition: service_started }
|
dev-nats: { condition: service_started }
|
||||||
dev-fx-oracle: { condition: service_started }
|
dev-fx-oracle: { condition: service_healthy }
|
||||||
volumes:
|
volumes:
|
||||||
- ./api/billing/fees:/src/api/billing/fees
|
- ./api/billing/fees:/src/api/billing/fees
|
||||||
- ./api/billing/fees/config.dev.yml:/app/config.yml:ro
|
- ./api/billing/fees/config.dev.yml:/app/config.yml:ro
|
||||||
@@ -325,6 +337,12 @@ services:
|
|||||||
NATS_URL: nats://${NATS_USER}:${NATS_PASSWORD}@dev-nats:4222
|
NATS_URL: nats://${NATS_USER}:${NATS_PASSWORD}@dev-nats:4222
|
||||||
FEES_GRPC_PORT: 50060
|
FEES_GRPC_PORT: 50060
|
||||||
FEES_METRICS_PORT: 9402
|
FEES_METRICS_PORT: 9402
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "wget -q -O- http://127.0.0.1:9402/health | grep -q '\"status\":\"ok\"'"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 12
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# Billing Documents Service
|
# Billing Documents Service
|
||||||
@@ -362,6 +380,12 @@ services:
|
|||||||
DOCUMENTS_MONGO_REPLICA_SET: dev-rs
|
DOCUMENTS_MONGO_REPLICA_SET: dev-rs
|
||||||
DOCUMENTS_GRPC_PORT: 50061
|
DOCUMENTS_GRPC_PORT: 50061
|
||||||
DOCUMENTS_METRICS_PORT: 9409
|
DOCUMENTS_METRICS_PORT: 9409
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "wget -q -O- http://127.0.0.1:9409/health | grep -q '\"status\":\"ok\"'"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 12
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# Ledger Service
|
# Ledger Service
|
||||||
@@ -377,8 +401,8 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
dev-mongo-init: { condition: service_completed_successfully }
|
dev-mongo-init: { condition: service_completed_successfully }
|
||||||
dev-nats: { condition: service_started }
|
dev-nats: { condition: service_started }
|
||||||
dev-discovery: { condition: service_started }
|
dev-discovery: { condition: service_healthy }
|
||||||
dev-billing-fees: { condition: service_started }
|
dev-billing-fees: { condition: service_healthy }
|
||||||
volumes:
|
volumes:
|
||||||
- ./api/ledger:/src/api/ledger
|
- ./api/ledger:/src/api/ledger
|
||||||
- ./api/ledger/config.dev.yml:/app/config.yml:ro
|
- ./api/ledger/config.dev.yml:/app/config.yml:ro
|
||||||
@@ -408,6 +432,12 @@ services:
|
|||||||
NATS_URL: nats://${NATS_USER}:${NATS_PASSWORD}@dev-nats:4222
|
NATS_URL: nats://${NATS_USER}:${NATS_PASSWORD}@dev-nats:4222
|
||||||
LEDGER_GRPC_PORT: 50052
|
LEDGER_GRPC_PORT: 50052
|
||||||
LEDGER_METRICS_PORT: 9401
|
LEDGER_METRICS_PORT: 9401
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "wget -q -O- http://127.0.0.1:9401/health | grep -q '\"status\":\"ok\"'"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 12
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# FX Ingestor Service
|
# FX Ingestor Service
|
||||||
@@ -444,6 +474,12 @@ services:
|
|||||||
NATS_PASSWORD: ${NATS_PASSWORD}
|
NATS_PASSWORD: ${NATS_PASSWORD}
|
||||||
NATS_URL: nats://${NATS_USER}:${NATS_PASSWORD}@dev-nats:4222
|
NATS_URL: nats://${NATS_USER}:${NATS_PASSWORD}@dev-nats:4222
|
||||||
FX_INGESTOR_METRICS_PORT: 9102
|
FX_INGESTOR_METRICS_PORT: 9102
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "wget -q -O- http://127.0.0.1:9102/health | grep -q '\"status\":\"ok\"'"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 12
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# Payments Orchestrator Service
|
# Payments Orchestrator Service
|
||||||
@@ -459,8 +495,8 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
dev-mongo-init: { condition: service_completed_successfully }
|
dev-mongo-init: { condition: service_completed_successfully }
|
||||||
dev-nats: { condition: service_started }
|
dev-nats: { condition: service_started }
|
||||||
dev-ledger: { condition: service_started }
|
dev-ledger: { condition: service_healthy }
|
||||||
dev-billing-fees: { condition: service_started }
|
dev-billing-fees: { condition: service_healthy }
|
||||||
volumes:
|
volumes:
|
||||||
- ./api/payments/orchestrator:/src/api/payments/orchestrator
|
- ./api/payments/orchestrator:/src/api/payments/orchestrator
|
||||||
- ./api/payments/storage:/src/api/payments/storage
|
- ./api/payments/storage:/src/api/payments/storage
|
||||||
@@ -485,6 +521,12 @@ services:
|
|||||||
NATS_URL: nats://${NATS_USER}:${NATS_PASSWORD}@dev-nats:4222
|
NATS_URL: nats://${NATS_USER}:${NATS_PASSWORD}@dev-nats:4222
|
||||||
PAYMENTS_GRPC_PORT: 50062
|
PAYMENTS_GRPC_PORT: 50062
|
||||||
PAYMENTS_METRICS_PORT: 9403
|
PAYMENTS_METRICS_PORT: 9403
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "wget -q -O- http://127.0.0.1:9403/health | grep -q '\"status\":\"ok\"'"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 12
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# Payments Quotation Service
|
# Payments Quotation Service
|
||||||
@@ -500,10 +542,10 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
dev-mongo-init: { condition: service_completed_successfully }
|
dev-mongo-init: { condition: service_completed_successfully }
|
||||||
dev-nats: { condition: service_started }
|
dev-nats: { condition: service_started }
|
||||||
dev-discovery: { condition: service_started }
|
dev-discovery: { condition: service_healthy }
|
||||||
dev-fx-oracle: { condition: service_started }
|
dev-fx-oracle: { condition: service_healthy }
|
||||||
dev-billing-fees: { condition: service_started }
|
dev-billing-fees: { condition: service_healthy }
|
||||||
dev-chain-gateway: { condition: service_started }
|
dev-chain-gateway: { condition: service_healthy }
|
||||||
volumes:
|
volumes:
|
||||||
- ./api/payments/quotation:/src/api/payments/quotation
|
- ./api/payments/quotation:/src/api/payments/quotation
|
||||||
- ./api/payments/storage:/src/api/payments/storage
|
- ./api/payments/storage:/src/api/payments/storage
|
||||||
@@ -529,6 +571,12 @@ services:
|
|||||||
FEES_ADDRESS: dev-billing-fees:50060
|
FEES_ADDRESS: dev-billing-fees:50060
|
||||||
ORACLE_ADDRESS: dev-fx-oracle:50051
|
ORACLE_ADDRESS: dev-fx-oracle:50051
|
||||||
CHAIN_GATEWAY_ADDRESS: dev-chain-gateway:50053
|
CHAIN_GATEWAY_ADDRESS: dev-chain-gateway:50053
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "wget -q -O- http://127.0.0.1:9414/health | grep -q '\"status\":\"ok\"'"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 12
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# Payments Methods Service
|
# Payments Methods Service
|
||||||
@@ -544,7 +592,7 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
dev-mongo-init: { condition: service_completed_successfully }
|
dev-mongo-init: { condition: service_completed_successfully }
|
||||||
dev-nats: { condition: service_started }
|
dev-nats: { condition: service_started }
|
||||||
dev-discovery: { condition: service_started }
|
dev-discovery: { condition: service_healthy }
|
||||||
volumes:
|
volumes:
|
||||||
- ./api/payments/methods:/src/api/payments/methods
|
- ./api/payments/methods:/src/api/payments/methods
|
||||||
- ./api/payments/storage:/src/api/payments/storage
|
- ./api/payments/storage:/src/api/payments/storage
|
||||||
@@ -576,6 +624,12 @@ services:
|
|||||||
NATS_URL: nats://${NATS_USER}:${NATS_PASSWORD}@dev-nats:4222
|
NATS_URL: nats://${NATS_USER}:${NATS_PASSWORD}@dev-nats:4222
|
||||||
PAYMENTS_METHODS_GRPC_PORT: 50066
|
PAYMENTS_METHODS_GRPC_PORT: 50066
|
||||||
PAYMENTS_METHODS_METRICS_PORT: 9416
|
PAYMENTS_METHODS_METRICS_PORT: 9416
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "wget -q -O- http://127.0.0.1:9416/health | grep -q '\"status\":\"ok\"'"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 12
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# Chain Gateway Vault Agent (sidecar for AppRole authentication)
|
# Chain Gateway Vault Agent (sidecar for AppRole authentication)
|
||||||
@@ -623,7 +677,7 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
dev-mongo-init: { condition: service_completed_successfully }
|
dev-mongo-init: { condition: service_completed_successfully }
|
||||||
dev-nats: { condition: service_started }
|
dev-nats: { condition: service_started }
|
||||||
dev-discovery: { condition: service_started }
|
dev-discovery: { condition: service_healthy }
|
||||||
dev-vault: { condition: service_healthy }
|
dev-vault: { condition: service_healthy }
|
||||||
dev-chain-gateway-vault-agent: { condition: service_healthy }
|
dev-chain-gateway-vault-agent: { condition: service_healthy }
|
||||||
volumes:
|
volumes:
|
||||||
@@ -654,6 +708,12 @@ services:
|
|||||||
VAULT_ADDR: ${VAULT_ADDR}
|
VAULT_ADDR: ${VAULT_ADDR}
|
||||||
VAULT_TOKEN_FILE: /run/vault/token
|
VAULT_TOKEN_FILE: /run/vault/token
|
||||||
CHAIN_GATEWAY_RPC_URL: ${CHAIN_GATEWAY_RPC_URL}
|
CHAIN_GATEWAY_RPC_URL: ${CHAIN_GATEWAY_RPC_URL}
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "wget -q -O- http://127.0.0.1:9406/health | grep -q '\"status\":\"ok\"'"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 12
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# TRON Gateway Vault Agent (sidecar for AppRole authentication)
|
# TRON Gateway Vault Agent (sidecar for AppRole authentication)
|
||||||
@@ -701,7 +761,7 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
dev-mongo-init: { condition: service_completed_successfully }
|
dev-mongo-init: { condition: service_completed_successfully }
|
||||||
dev-nats: { condition: service_started }
|
dev-nats: { condition: service_started }
|
||||||
dev-discovery: { condition: service_started }
|
dev-discovery: { condition: service_healthy }
|
||||||
dev-vault: { condition: service_healthy }
|
dev-vault: { condition: service_healthy }
|
||||||
dev-tron-gateway-vault-agent: { condition: service_healthy }
|
dev-tron-gateway-vault-agent: { condition: service_healthy }
|
||||||
volumes:
|
volumes:
|
||||||
@@ -734,6 +794,12 @@ services:
|
|||||||
TRON_GATEWAY_RPC_URL: ${TRON_GATEWAY_RPC_URL:-}
|
TRON_GATEWAY_RPC_URL: ${TRON_GATEWAY_RPC_URL:-}
|
||||||
TRON_GATEWAY_GRPC_URL: ${TRON_GATEWAY_GRPC_URL:-}
|
TRON_GATEWAY_GRPC_URL: ${TRON_GATEWAY_GRPC_URL:-}
|
||||||
TRON_GATEWAY_GRPC_TOKEN: ${TRON_GATEWAY_GRPC_TOKEN:-}
|
TRON_GATEWAY_GRPC_TOKEN: ${TRON_GATEWAY_GRPC_TOKEN:-}
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "wget -q -O- http://127.0.0.1:9408/health | grep -q '\"status\":\"ok\"'"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 12
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# Aurora Gateway Service (simulated card payouts)
|
# Aurora Gateway Service (simulated card payouts)
|
||||||
@@ -748,7 +814,7 @@ services:
|
|||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
depends_on:
|
depends_on:
|
||||||
dev-nats: { condition: service_started }
|
dev-nats: { condition: service_started }
|
||||||
dev-discovery: { condition: service_started }
|
dev-discovery: { condition: service_healthy }
|
||||||
dev-vault: { condition: service_healthy }
|
dev-vault: { condition: service_healthy }
|
||||||
volumes:
|
volumes:
|
||||||
- ./api/gateway/aurora:/src/api/gateway/aurora
|
- ./api/gateway/aurora:/src/api/gateway/aurora
|
||||||
@@ -777,6 +843,12 @@ services:
|
|||||||
AURORA_GATEWAY_METRICS_PORT: 9405
|
AURORA_GATEWAY_METRICS_PORT: 9405
|
||||||
AURORA_GATEWAY_HTTP_PORT: 8084
|
AURORA_GATEWAY_HTTP_PORT: 8084
|
||||||
VAULT_ADDR: ${VAULT_ADDR}
|
VAULT_ADDR: ${VAULT_ADDR}
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "wget -q -O- http://127.0.0.1:9405/health | grep -q '\"status\":\"ok\"'"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 12
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# ChimeraSettle Gateway Service (simulated settlements)
|
# ChimeraSettle Gateway Service (simulated settlements)
|
||||||
@@ -792,7 +864,7 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
dev-mongo-init: { condition: service_completed_successfully }
|
dev-mongo-init: { condition: service_completed_successfully }
|
||||||
dev-nats: { condition: service_started }
|
dev-nats: { condition: service_started }
|
||||||
dev-discovery: { condition: service_started }
|
dev-discovery: { condition: service_healthy }
|
||||||
dev-vault: { condition: service_healthy }
|
dev-vault: { condition: service_healthy }
|
||||||
volumes:
|
volumes:
|
||||||
- ./api/gateway/chsettle:/src/api/gateway/chsettle
|
- ./api/gateway/chsettle:/src/api/gateway/chsettle
|
||||||
@@ -819,6 +891,12 @@ services:
|
|||||||
CHSETTLE_GATEWAY_GRPC_PORT: 50080
|
CHSETTLE_GATEWAY_GRPC_PORT: 50080
|
||||||
CHSETTLE_GATEWAY_METRICS_PORT: 9406
|
CHSETTLE_GATEWAY_METRICS_PORT: 9406
|
||||||
VAULT_ADDR: ${VAULT_ADDR}
|
VAULT_ADDR: ${VAULT_ADDR}
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "wget -q -O- http://127.0.0.1:9406/health | grep -q '\"status\":\"ok\"'"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 12
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# Notification Service
|
# Notification Service
|
||||||
@@ -854,6 +932,12 @@ services:
|
|||||||
MONGO_PASSWORD: ${MONGO_PASSWORD}
|
MONGO_PASSWORD: ${MONGO_PASSWORD}
|
||||||
MONGO_AUTH_SOURCE: admin
|
MONGO_AUTH_SOURCE: admin
|
||||||
MONGO_REPLICA_SET: dev-rs
|
MONGO_REPLICA_SET: dev-rs
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "wget -q -O- \"http://127.0.0.1:8081$${API_ENDPOINT}/health\" | grep -q '\"status\":\"ok\"'"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 12
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# Callbacks Vault Agent (sidecar for AppRole authentication)
|
# Callbacks Vault Agent (sidecar for AppRole authentication)
|
||||||
@@ -928,6 +1012,12 @@ services:
|
|||||||
CALLBACKS_METRICS_PORT: 9420
|
CALLBACKS_METRICS_PORT: 9420
|
||||||
VAULT_ADDR: ${VAULT_ADDR}
|
VAULT_ADDR: ${VAULT_ADDR}
|
||||||
VAULT_TOKEN_FILE: /run/vault/token
|
VAULT_TOKEN_FILE: /run/vault/token
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "wget -q -O- http://127.0.0.1:9420/health | grep -q '\"status\":\"ok\"'"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 12
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# BFF Vault Agent (sidecar for AppRole authentication)
|
# BFF Vault Agent (sidecar for AppRole authentication)
|
||||||
@@ -976,11 +1066,11 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
dev-mongo-init: { condition: service_completed_successfully }
|
dev-mongo-init: { condition: service_completed_successfully }
|
||||||
dev-nats: { condition: service_started }
|
dev-nats: { condition: service_started }
|
||||||
dev-ledger: { condition: service_started }
|
dev-ledger: { condition: service_healthy }
|
||||||
dev-payments-orchestrator: { condition: service_started }
|
dev-payments-orchestrator: { condition: service_healthy }
|
||||||
dev-payments-quotation: { condition: service_started }
|
dev-payments-quotation: { condition: service_healthy }
|
||||||
dev-payments-methods: { condition: service_started }
|
dev-payments-methods: { condition: service_healthy }
|
||||||
dev-chain-gateway: { condition: service_started }
|
dev-chain-gateway: { condition: service_healthy }
|
||||||
dev-bff-vault-agent: { condition: service_healthy }
|
dev-bff-vault-agent: { condition: service_healthy }
|
||||||
volumes:
|
volumes:
|
||||||
- ./api/edge/bff:/src/api/edge/bff
|
- ./api/edge/bff:/src/api/edge/bff
|
||||||
@@ -1020,6 +1110,12 @@ services:
|
|||||||
API_ENDPOINT: /api/v1
|
API_ENDPOINT: /api/v1
|
||||||
VAULT_ADDR: ${VAULT_ADDR}
|
VAULT_ADDR: ${VAULT_ADDR}
|
||||||
VAULT_TOKEN_FILE: /run/vault/token
|
VAULT_TOKEN_FILE: /run/vault/token
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "wget -q -O- \"http://127.0.0.1:8080$${API_ENDPOINT}/health\" | grep -q '\"status\":\"ok\"'"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 12
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# Frontend (Flutter Web)
|
# Frontend (Flutter Web)
|
||||||
|
|||||||
Reference in New Issue
Block a user