Orchestration / payments v2 #554
@@ -14,6 +14,7 @@ when:
|
|||||||
path:
|
path:
|
||||||
include:
|
include:
|
||||||
- api/gateway/chain/**
|
- api/gateway/chain/**
|
||||||
|
- api/gateway/common/**
|
||||||
- api/proto/**
|
- api/proto/**
|
||||||
- api/pkg/**
|
- api/pkg/**
|
||||||
ignore_message: '[rebuild]'
|
ignore_message: '[rebuild]'
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ when:
|
|||||||
path:
|
path:
|
||||||
include:
|
include:
|
||||||
- api/gateway/mntx/**
|
- api/gateway/mntx/**
|
||||||
|
- api/gateway/common/**
|
||||||
- api/proto/**
|
- api/proto/**
|
||||||
- api/pkg/**
|
- api/pkg/**
|
||||||
ignore_message: '[rebuild]'
|
ignore_message: '[rebuild]'
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ when:
|
|||||||
path:
|
path:
|
||||||
include:
|
include:
|
||||||
- api/gateway/tgsettle/**
|
- api/gateway/tgsettle/**
|
||||||
|
- api/gateway/common/**
|
||||||
- api/proto/**
|
- api/proto/**
|
||||||
- api/pkg/**
|
- api/pkg/**
|
||||||
ignore_message: '[rebuild]'
|
ignore_message: '[rebuild]'
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ when:
|
|||||||
path:
|
path:
|
||||||
include:
|
include:
|
||||||
- api/gateway/tron/**
|
- api/gateway/tron/**
|
||||||
|
- api/gateway/common/**
|
||||||
- api/proto/**
|
- api/proto/**
|
||||||
- api/pkg/**
|
- api/pkg/**
|
||||||
ignore_message: '[rebuild]'
|
ignore_message: '[rebuild]'
|
||||||
|
|||||||
6
Makefile
6
Makefile
@@ -85,7 +85,7 @@ init:
|
|||||||
@echo "$(GREEN)Verifying .env.dev...$(NC)"
|
@echo "$(GREEN)Verifying .env.dev...$(NC)"
|
||||||
@cat .env.dev | grep -q "MONGO_USER=" || (echo "$(YELLOW)Error: .env.dev is incomplete$(NC)" && exit 1)
|
@cat .env.dev | grep -q "MONGO_USER=" || (echo "$(YELLOW)Error: .env.dev is incomplete$(NC)" && exit 1)
|
||||||
@echo "$(GREEN)Running proto generation...$(NC)"
|
@echo "$(GREEN)Running proto generation...$(NC)"
|
||||||
@./generate_protos.sh
|
@./ci/scripts/proto/generate.sh
|
||||||
@echo "$(GREEN)Building Docker images...$(NC)"
|
@echo "$(GREEN)Building Docker images...$(NC)"
|
||||||
@$(COMPOSE) build
|
@$(COMPOSE) build
|
||||||
@echo "$(GREEN)✅ Initialization complete!$(NC)"
|
@echo "$(GREEN)✅ Initialization complete!$(NC)"
|
||||||
@@ -97,7 +97,7 @@ init:
|
|||||||
# Build all images
|
# Build all images
|
||||||
build:
|
build:
|
||||||
@echo "$(GREEN)Building all service images...$(NC)"
|
@echo "$(GREEN)Building all service images...$(NC)"
|
||||||
@./generate_protos.sh
|
@./ci/scripts/proto/generate.sh
|
||||||
@$(COMPOSE) build
|
@$(COMPOSE) build
|
||||||
|
|
||||||
# Start all services
|
# Start all services
|
||||||
@@ -154,7 +154,7 @@ generate: generate-api generate-frontend
|
|||||||
# Generate protobuf code
|
# Generate protobuf code
|
||||||
generate-api:
|
generate-api:
|
||||||
@echo "$(GREEN)Generating protobuf code...$(NC)"
|
@echo "$(GREEN)Generating protobuf code...$(NC)"
|
||||||
@./generate_protos.sh
|
@./ci/scripts/proto/generate.sh
|
||||||
@echo "$(GREEN)✅ Protobuf generation complete$(NC)"
|
@echo "$(GREEN)✅ Protobuf generation complete$(NC)"
|
||||||
|
|
||||||
# Generate Flutter code (json_serializable, etc.)
|
# Generate Flutter code (json_serializable, etc.)
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ go 1.25.7
|
|||||||
replace github.com/tech/sendico/pkg => ../../pkg
|
replace github.com/tech/sendico/pkg => ../../pkg
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/aws/aws-sdk-go-v2 v1.41.1
|
github.com/aws/aws-sdk-go-v2 v1.41.2
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.32.9
|
github.com/aws/aws-sdk-go-v2/config v1.32.10
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.9
|
github.com/aws/aws-sdk-go-v2/credentials v1.19.10
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.0
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.2
|
||||||
github.com/jung-kurt/gofpdf v1.16.2
|
github.com/jung-kurt/gofpdf v1.16.2
|
||||||
github.com/prometheus/client_golang v1.23.2
|
github.com/prometheus/client_golang v1.23.2
|
||||||
github.com/shopspring/decimal v1.4.0
|
github.com/shopspring/decimal v1.4.0
|
||||||
@@ -20,21 +20,21 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4 // indirect
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.5 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17 // indirect
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.18 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.17 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.18 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.8 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.10 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.17 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.18 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.5 // indirect
|
github.com/aws/aws-sdk-go-v2/service/signin v1.0.6 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.10 // indirect
|
github.com/aws/aws-sdk-go-v2/service/sso v1.30.11 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.14 // indirect
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.6 // indirect
|
github.com/aws/aws-sdk-go-v2/service/sts v1.41.7 // indirect
|
||||||
github.com/aws/smithy-go v1.24.0 // indirect
|
github.com/aws/smithy-go v1.24.1 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect
|
github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect
|
||||||
github.com/casbin/casbin/v2 v2.135.0 // indirect
|
github.com/casbin/casbin/v2 v2.135.0 // indirect
|
||||||
@@ -48,12 +48,12 @@ require (
|
|||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/nats-io/nats.go v1.48.0 // indirect
|
github.com/nats-io/nats.go v1.49.0 // indirect
|
||||||
github.com/nats-io/nkeys v0.4.15 // indirect
|
github.com/nats-io/nkeys v0.4.15 // indirect
|
||||||
github.com/nats-io/nuid v1.0.1 // indirect
|
github.com/nats-io/nuid v1.0.1 // indirect
|
||||||
github.com/prometheus/client_model v0.6.2 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
github.com/prometheus/common v0.67.5 // indirect
|
github.com/prometheus/common v0.67.5 // indirect
|
||||||
github.com/prometheus/procfs v0.19.2 // indirect
|
github.com/prometheus/procfs v0.20.0 // indirect
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
github.com/xdg-go/scram v1.2.0 // indirect
|
github.com/xdg-go/scram v1.2.0 // indirect
|
||||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||||
@@ -61,10 +61,10 @@ require (
|
|||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||||
golang.org/x/crypto v0.48.0 // indirect
|
golang.org/x/crypto v0.48.0 // indirect
|
||||||
golang.org/x/net v0.50.0 // indirect
|
golang.org/x/net v0.51.0 // indirect
|
||||||
golang.org/x/sync v0.19.0 // indirect
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.41.0 // indirect
|
golang.org/x/sys v0.41.0 // indirect
|
||||||
golang.org/x/text v0.34.0 // indirect
|
golang.org/x/text v0.34.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect
|
||||||
google.golang.org/protobuf v1.36.11 // indirect
|
google.golang.org/protobuf v1.36.11 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,44 +4,44 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25
|
|||||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||||
github.com/aws/aws-sdk-go-v2 v1.41.1 h1:ABlyEARCDLN034NhxlRUSZr4l71mh+T5KAeGh6cerhU=
|
github.com/aws/aws-sdk-go-v2 v1.41.2 h1:LuT2rzqNQsauaGkPK/7813XxcZ3o3yePY0Iy891T2ls=
|
||||||
github.com/aws/aws-sdk-go-v2 v1.41.1/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0=
|
github.com/aws/aws-sdk-go-v2 v1.41.2/go.mod h1:IvvlAZQXvTXznUPfRVfryiG1fbzE2NGK6m9u39YQ+S4=
|
||||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4 h1:489krEF9xIGkOaaX3CE/Be2uWjiXrkCH6gUX+bZA/BU=
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.5 h1:zWFmPmgw4sveAYi1mRqG+E/g0461cJ5M4bJ8/nc6d3Q=
|
||||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4/go.mod h1:IOAPF6oT9KCsceNTvvYMNHy0+kMF8akOjeDvPENWxp4=
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.5/go.mod h1:nVUlMLVV8ycXSb7mSkcNu9e3v/1TJq2RTlrPwhYWr5c=
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.32.9 h1:ktda/mtAydeObvJXlHzyGpK1xcsLaP16zfUPDGoW90A=
|
github.com/aws/aws-sdk-go-v2/config v1.32.10 h1:9DMthfO6XWZYLfzZglAgW5Fyou2nRI5CuV44sTedKBI=
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.32.9/go.mod h1:U+fCQ+9QKsLW786BCfEjYRj34VVTbPdsLP3CHSYXMOI=
|
github.com/aws/aws-sdk-go-v2/config v1.32.10/go.mod h1:2rUIOnA2JaiqYmSKYmRJlcMWy6qTj1vuRFscppSBMcw=
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.9 h1:sWvTKsyrMlJGEuj/WgrwilpoJ6Xa1+KhIpGdzw7mMU8=
|
github.com/aws/aws-sdk-go-v2/credentials v1.19.10 h1:EEhmEUFCE1Yhl7vDhNOI5OCL/iKMdkkYFTRpZXNw7m8=
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.9/go.mod h1:+J44MBhmfVY/lETFiKI+klz0Vym2aCmIjqgClMmW82w=
|
github.com/aws/aws-sdk-go-v2/credentials v1.19.10/go.mod h1:RnnlFCAlxQCkN2Q379B67USkBMu1PipEEiibzYN5UTE=
|
||||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17 h1:I0GyV8wiYrP8XpA70g1HBcQO1JlQxCMTW9npl5UbDHY=
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18 h1:Ii4s+Sq3yDfaMLpjrJsqD6SmG/Wq/P5L/hw2qa78UAY=
|
||||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17/go.mod h1:tyw7BOl5bBe/oqvoIeECFJjMdzXoa/dfVz3QQ5lgHGA=
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18/go.mod h1:6x81qnY++ovptLE6nWQeWrpXxbnlIex+4H4eYYGcqfc=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17 h1:xOLELNKGp2vsiteLsvLPwxC+mYmO6OZ8PYgiuPJzF8U=
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.18 h1:F43zk1vemYIqPAwhjTjYIz0irU2EY7sOb/F5eJ3HuyM=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17/go.mod h1:5M5CI3D12dNOtH3/mk6minaRwI2/37ifCURZISxA/IQ=
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.18/go.mod h1:w1jdlZXrGKaJcNoL+Nnrj+k5wlpGXqnNrKoP22HvAug=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17 h1:WWLqlh79iO48yLkj1v3ISRNiv+3KdQoZ6JWyfcsyQik=
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18 h1:xCeWVjj0ki0l3nruoyP2slHsGArMxeiiaoPN5QZH6YQ=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17/go.mod h1:EhG22vHRrvF8oXSTYStZhJc1aUgKtnJe+aOiFEV90cM=
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18/go.mod h1:r/eLGuGCBw6l36ZRWiw6PaZwPXb6YOj+i/7MizNl5/k=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk=
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc=
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.17 h1:JqcdRG//czea7Ppjb+g/n4o8i/R50aTBHkA7vu0lK+k=
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.18 h1:eZioDaZGJ0tMM4gzmkNIO2aAoQd+je7Ug7TkvAzlmkU=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.17/go.mod h1:CO+WeGmIdj/MlPel2KwID9Gt7CNq4M65HUfBW97liM0=
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.18/go.mod h1:CCXwUKAJdoWr6/NcxZ+zsiPr6oH/Q5aTooRGYieAyj4=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 h1:0ryTNEdJbzUCEWkVXEXoqlXV72J5keC1GvILMOuD00E=
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5 h1:CeY9LUdur+Dxoeldqoun6y4WtJ3RQtzk0JMP2gfUay0=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4/go.mod h1:HQ4qwNZh32C3CBeO6iJLQlgtMzqeG17ziAA/3KDJFow=
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5/go.mod h1:AZLZf2fMaahW5s/wMRciu1sYbdsikT/UHwbUjOdEVTc=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.8 h1:Z5EiPIzXKewUQK0QTMkutjiaPVeVYXX7KIqhXu/0fXs=
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.10 h1:fJvQ5mIBVfKtiyx0AHY6HeWcRX5LGANLpq8SVR+Uazs=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.8/go.mod h1:FsTpJtvC4U1fyDXk7c71XoDv3HlRm8V3NiYLeYLh5YE=
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.10/go.mod h1:Kzm5e6OmNH8VMkgK9t+ry5jEih4Y8whqs+1hrkxim1I=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17 h1:RuNSMoozM8oXlgLG/n6WLaFGoea7/CddrCfIiSA+xdY=
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18 h1:LTRCYFlnnKFlKsyIQxKhJuDuA3ZkrDQMRYm6rXiHlLY=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17/go.mod h1:F2xxQ9TZz5gDWsclCtPQscGpP0VUOc8RqgFM3vDENmU=
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18/go.mod h1:XhwkgGG6bHSd00nO/mexWTcTjgd6PjuvWQMqSn2UaEk=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.17 h1:bGeHBsGZx0Dvu/eJC0Lh9adJa3M1xREcndxLNZlve2U=
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.18 h1:/A/xDuZAVD2BpsS2fftFRo/NoEKQJ8YTnJDEHBy2Gtg=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.17/go.mod h1:dcW24lbU0CzHusTE8LLHhRLI42ejmINN8Lcr22bwh/g=
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.18/go.mod h1:hWe9b4f+djUQGmyiGEeOnZv69dtMSgpDRIvNMvuvzvY=
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.0 h1:oeu8VPlOre74lBA/PMhxa5vewaMIMmILM+RraSyB8KA=
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.2 h1:M1A9AjcFwlxTLuf0Faj88L8Iqw0n/AJHjpZTQzMMsSc=
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.0/go.mod h1:5jggDlZ2CLQhwJBiZJb4vfk4f0GxWdEDruWKEJ1xOdo=
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.2/go.mod h1:KsdTV6Q9WKUZm2mNJnUFmIoXfZux91M3sr/a4REX8e0=
|
||||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.5 h1:VrhDvQib/i0lxvr3zqlUwLwJP4fpmpyD9wYG1vfSu+Y=
|
github.com/aws/aws-sdk-go-v2/service/signin v1.0.6 h1:MzORe+J94I+hYu2a6XmV5yC9huoTv8NRcCrUNedDypQ=
|
||||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.5/go.mod h1:k029+U8SY30/3/ras4G/Fnv/b88N4mAfliNn08Dem4M=
|
github.com/aws/aws-sdk-go-v2/service/signin v1.0.6/go.mod h1:hXzcHLARD7GeWnifd8j9RWqtfIgxj4/cAtIVIK7hg8g=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.10 h1:+VTRawC4iVY58pS/lzpo0lnoa/SYNGF4/B/3/U5ro8Y=
|
github.com/aws/aws-sdk-go-v2/service/sso v1.30.11 h1:7oGD8KPfBOJGXiCoRKrrrQkbvCp8N++u36hrLMPey6o=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.10/go.mod h1:yifAsgBxgJWn3ggx70A3urX2AN49Y5sJTD1UQFlfqBw=
|
github.com/aws/aws-sdk-go-v2/service/sso v1.30.11/go.mod h1:0DO9B5EUJQlIDif+XJRWCljZRKsAFKh3gpFz7UnDtOo=
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.14 h1:0jbJeuEHlwKJ9PfXtpSFc4MF+WIWORdhN1n30ITZGFM=
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15 h1:edCcNp9eGIUDUCrzoCu1jWAXLGFIizeqkdkKgRlJwWc=
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.14/go.mod h1:sTGThjphYE4Ohw8vJiRStAcu3rbjtXRsdNB0TvZ5wwo=
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15/go.mod h1:lyRQKED9xWfgkYC/wmmYfv7iVIM68Z5OQ88ZdcV1QbU=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.6 h1:5fFjR/ToSOzB2OQ/XqWpZBmNvmP/pJ1jOWYlFDJTjRQ=
|
github.com/aws/aws-sdk-go-v2/service/sts v1.41.7 h1:NITQpgo9A5NrDZ57uOWj+abvXSb83BbyggcUBVksN7c=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.6/go.mod h1:qgFDZQSD/Kys7nJnVqYlWKnh0SSdMjAi0uSwON4wgYQ=
|
github.com/aws/aws-sdk-go-v2/service/sts v1.41.7/go.mod h1:sks5UWBhEuWYDPdwlnRFn1w7xWdH29Jcpe+/PJQefEs=
|
||||||
github.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk=
|
github.com/aws/smithy-go v1.24.1 h1:VbyeNfmYkWoxMVpGUAbQumkODcYmfMRfZ8yQiH30SK0=
|
||||||
github.com/aws/smithy-go v1.24.0/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0=
|
github.com/aws/smithy-go v1.24.1/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||||
@@ -134,8 +134,8 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
|||||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/nats-io/nats.go v1.48.0 h1:pSFyXApG+yWU/TgbKCjmm5K4wrHu86231/w84qRVR+U=
|
github.com/nats-io/nats.go v1.49.0 h1:yh/WvY59gXqYpgl33ZI+XoVPKyut/IcEaqtsiuTJpoE=
|
||||||
github.com/nats-io/nats.go v1.48.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
github.com/nats-io/nats.go v1.49.0/go.mod h1:fDCn3mN5cY8HooHwE2ukiLb4p4G4ImmzvXyJt+tGwdw=
|
||||||
github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
|
github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
|
||||||
github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
|
github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
|
||||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||||
@@ -158,8 +158,8 @@ github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNw
|
|||||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||||
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
||||||
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
||||||
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
github.com/prometheus/procfs v0.20.0 h1:AA7aCvjxwAquZAlonN7888f2u4IN8WVeFgBi4k82M4Q=
|
||||||
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
github.com/prometheus/procfs v0.20.0/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo=
|
||||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||||
github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
|
github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
|
||||||
@@ -229,8 +229,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
@@ -258,8 +258,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
|
|||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d h1:t/LOSXPJ9R0B6fnZNyALBRfZBH0Uy0gT+uR+SJ6syqQ=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
|
|||||||
@@ -392,7 +392,7 @@ func (s *Service) startDiscoveryAnnouncer() {
|
|||||||
|
|
||||||
announce := discovery.Announcement{
|
announce := discovery.Announcement{
|
||||||
Service: "BILLING_DOCUMENTS",
|
Service: "BILLING_DOCUMENTS",
|
||||||
Operations: []string{"documents.batch_resolve", "documents.get"},
|
Operations: []string{discovery.OperationDocumentsBatchResolve, discovery.OperationDocumentsGet},
|
||||||
InvokeURI: s.invokeURI,
|
InvokeURI: s.invokeURI,
|
||||||
Version: appversion.Create().Short(),
|
Version: appversion.Create().Short(),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,13 +32,13 @@ require (
|
|||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/nats-io/nats.go v1.48.0 // indirect
|
github.com/nats-io/nats.go v1.49.0 // indirect
|
||||||
github.com/nats-io/nkeys v0.4.15 // indirect
|
github.com/nats-io/nkeys v0.4.15 // indirect
|
||||||
github.com/nats-io/nuid v1.0.1 // indirect
|
github.com/nats-io/nuid v1.0.1 // indirect
|
||||||
github.com/prometheus/client_golang v1.23.2
|
github.com/prometheus/client_golang v1.23.2
|
||||||
github.com/prometheus/client_model v0.6.2 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
github.com/prometheus/common v0.67.5 // indirect
|
github.com/prometheus/common v0.67.5 // indirect
|
||||||
github.com/prometheus/procfs v0.19.2 // indirect
|
github.com/prometheus/procfs v0.20.0 // indirect
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
github.com/xdg-go/scram v1.2.0 // indirect
|
github.com/xdg-go/scram v1.2.0 // indirect
|
||||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||||
@@ -46,10 +46,10 @@ require (
|
|||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||||
golang.org/x/crypto v0.48.0 // indirect
|
golang.org/x/crypto v0.48.0 // indirect
|
||||||
golang.org/x/net v0.50.0 // indirect
|
golang.org/x/net v0.51.0 // indirect
|
||||||
golang.org/x/sync v0.19.0 // indirect
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.41.0 // indirect
|
golang.org/x/sys v0.41.0 // indirect
|
||||||
golang.org/x/text v0.34.0 // indirect
|
golang.org/x/text v0.34.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect
|
||||||
google.golang.org/protobuf v1.36.11
|
google.golang.org/protobuf v1.36.11
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -91,8 +91,8 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
|||||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/nats-io/nats.go v1.48.0 h1:pSFyXApG+yWU/TgbKCjmm5K4wrHu86231/w84qRVR+U=
|
github.com/nats-io/nats.go v1.49.0 h1:yh/WvY59gXqYpgl33ZI+XoVPKyut/IcEaqtsiuTJpoE=
|
||||||
github.com/nats-io/nats.go v1.48.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
github.com/nats-io/nats.go v1.49.0/go.mod h1:fDCn3mN5cY8HooHwE2ukiLb4p4G4ImmzvXyJt+tGwdw=
|
||||||
github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
|
github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
|
||||||
github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
|
github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
|
||||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||||
@@ -113,8 +113,8 @@ github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNw
|
|||||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||||
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
||||||
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
||||||
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
github.com/prometheus/procfs v0.20.0 h1:AA7aCvjxwAquZAlonN7888f2u4IN8WVeFgBi4k82M4Q=
|
||||||
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
github.com/prometheus/procfs v0.20.0/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo=
|
||||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||||
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
|
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
|
||||||
@@ -179,8 +179,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
@@ -208,8 +208,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
|
|||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d h1:t/LOSXPJ9R0B6fnZNyALBRfZBH0Uy0gT+uR+SJ6syqQ=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/tech/sendico/billing/fees/storage"
|
"github.com/tech/sendico/billing/fees/storage"
|
||||||
"github.com/tech/sendico/billing/fees/storage/model"
|
"github.com/tech/sendico/billing/fees/storage/model"
|
||||||
"github.com/tech/sendico/pkg/merrors"
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
|
"github.com/tech/sendico/pkg/mlogger"
|
||||||
"github.com/tech/sendico/pkg/mutil/mzap"
|
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||||
"go.mongodb.org/mongo-driver/v2/bson"
|
"go.mongodb.org/mongo-driver/v2/bson"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
@@ -22,10 +23,10 @@ type planFinder interface {
|
|||||||
type feeResolver struct {
|
type feeResolver struct {
|
||||||
plans storage.PlansStore
|
plans storage.PlansStore
|
||||||
finder planFinder
|
finder planFinder
|
||||||
logger *zap.Logger
|
logger mlogger.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(plans storage.PlansStore, logger *zap.Logger) *feeResolver {
|
func New(plans storage.PlansStore, logger mlogger.Logger) *feeResolver {
|
||||||
var finder planFinder
|
var finder planFinder
|
||||||
if pf, ok := plans.(planFinder); ok {
|
if pf, ok := plans.(planFinder); ok {
|
||||||
finder = pf
|
finder = pf
|
||||||
|
|||||||
@@ -564,7 +564,7 @@ func (s *Service) startDiscoveryAnnouncer() {
|
|||||||
|
|
||||||
announce := discovery.Announcement{
|
announce := discovery.Announcement{
|
||||||
Service: "BILLING_FEES",
|
Service: "BILLING_FEES",
|
||||||
Operations: []string{"fee.calc"},
|
Operations: []string{discovery.OperationFeeCalc},
|
||||||
InvokeURI: s.invokeURI,
|
InvokeURI: s.invokeURI,
|
||||||
Version: appversion.Create().Short(),
|
Version: appversion.Create().Short(),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,12 +25,12 @@ require (
|
|||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/nats-io/nats.go v1.48.0 // indirect
|
github.com/nats-io/nats.go v1.49.0 // indirect
|
||||||
github.com/nats-io/nkeys v0.4.15 // indirect
|
github.com/nats-io/nkeys v0.4.15 // indirect
|
||||||
github.com/nats-io/nuid v1.0.1 // indirect
|
github.com/nats-io/nuid v1.0.1 // indirect
|
||||||
github.com/prometheus/client_model v0.6.2 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
github.com/prometheus/common v0.67.5 // indirect
|
github.com/prometheus/common v0.67.5 // indirect
|
||||||
github.com/prometheus/procfs v0.19.2 // indirect
|
github.com/prometheus/procfs v0.20.0 // indirect
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
github.com/xdg-go/scram v1.2.0 // indirect
|
github.com/xdg-go/scram v1.2.0 // indirect
|
||||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||||
@@ -39,11 +39,11 @@ require (
|
|||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||||
golang.org/x/crypto v0.48.0 // indirect
|
golang.org/x/crypto v0.48.0 // indirect
|
||||||
golang.org/x/net v0.50.0 // indirect
|
golang.org/x/net v0.51.0 // indirect
|
||||||
golang.org/x/sync v0.19.0 // indirect
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.41.0 // indirect
|
golang.org/x/sys v0.41.0 // indirect
|
||||||
golang.org/x/text v0.34.0 // indirect
|
golang.org/x/text v0.34.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect
|
||||||
google.golang.org/grpc v1.79.1 // indirect
|
google.golang.org/grpc v1.79.1 // indirect
|
||||||
google.golang.org/protobuf v1.36.11 // indirect
|
google.golang.org/protobuf v1.36.11 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -91,8 +91,8 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
|||||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/nats-io/nats.go v1.48.0 h1:pSFyXApG+yWU/TgbKCjmm5K4wrHu86231/w84qRVR+U=
|
github.com/nats-io/nats.go v1.49.0 h1:yh/WvY59gXqYpgl33ZI+XoVPKyut/IcEaqtsiuTJpoE=
|
||||||
github.com/nats-io/nats.go v1.48.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
github.com/nats-io/nats.go v1.49.0/go.mod h1:fDCn3mN5cY8HooHwE2ukiLb4p4G4ImmzvXyJt+tGwdw=
|
||||||
github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
|
github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
|
||||||
github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
|
github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
|
||||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||||
@@ -113,8 +113,8 @@ github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNw
|
|||||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||||
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
||||||
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
||||||
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
github.com/prometheus/procfs v0.20.0 h1:AA7aCvjxwAquZAlonN7888f2u4IN8WVeFgBi4k82M4Q=
|
||||||
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
github.com/prometheus/procfs v0.20.0/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo=
|
||||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||||
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
|
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
|
||||||
@@ -179,8 +179,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
@@ -208,8 +208,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
|
|||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d h1:t/LOSXPJ9R0B6fnZNyALBRfZBH0Uy0gT+uR+SJ6syqQ=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ func (i *Imp) startDiscovery(cfg *config) error {
|
|||||||
announce := discovery.Announcement{
|
announce := discovery.Announcement{
|
||||||
Service: "DISCOVERY",
|
Service: "DISCOVERY",
|
||||||
InstanceID: discovery.InstanceID(),
|
InstanceID: discovery.InstanceID(),
|
||||||
Operations: []string{"discovery.lookup"},
|
Operations: []string{discovery.OperationDiscoveryLookup},
|
||||||
Version: appversion.Create().Short(),
|
Version: appversion.Create().Short(),
|
||||||
}
|
}
|
||||||
i.announcer = discovery.NewAnnouncer(i.logger, producer, mservice.Discovery, announce)
|
i.announcer = discovery.NewAnnouncer(i.logger, producer, mservice.Discovery, announce)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ require (
|
|||||||
github.com/tech/sendico/fx/storage v0.0.0
|
github.com/tech/sendico/fx/storage v0.0.0
|
||||||
github.com/tech/sendico/pkg v0.1.0
|
github.com/tech/sendico/pkg v0.1.0
|
||||||
go.uber.org/zap v1.27.1
|
go.uber.org/zap v1.27.1
|
||||||
golang.org/x/net v0.50.0
|
golang.org/x/net v0.51.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -30,12 +30,12 @@ require (
|
|||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/nats-io/nats.go v1.48.0 // indirect
|
github.com/nats-io/nats.go v1.49.0 // indirect
|
||||||
github.com/nats-io/nkeys v0.4.15 // indirect
|
github.com/nats-io/nkeys v0.4.15 // indirect
|
||||||
github.com/nats-io/nuid v1.0.1 // indirect
|
github.com/nats-io/nuid v1.0.1 // indirect
|
||||||
github.com/prometheus/client_model v0.6.2 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
github.com/prometheus/common v0.67.5 // indirect
|
github.com/prometheus/common v0.67.5 // indirect
|
||||||
github.com/prometheus/procfs v0.19.2 // indirect
|
github.com/prometheus/procfs v0.20.0 // indirect
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
github.com/xdg-go/scram v1.2.0 // indirect
|
github.com/xdg-go/scram v1.2.0 // indirect
|
||||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||||
@@ -47,7 +47,7 @@ require (
|
|||||||
golang.org/x/sync v0.19.0 // indirect
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.41.0 // indirect
|
golang.org/x/sys v0.41.0 // indirect
|
||||||
golang.org/x/text v0.34.0 // indirect
|
golang.org/x/text v0.34.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect
|
||||||
google.golang.org/grpc v1.79.1 // indirect
|
google.golang.org/grpc v1.79.1 // indirect
|
||||||
google.golang.org/protobuf v1.36.11 // indirect
|
google.golang.org/protobuf v1.36.11 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -91,8 +91,8 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
|||||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/nats-io/nats.go v1.48.0 h1:pSFyXApG+yWU/TgbKCjmm5K4wrHu86231/w84qRVR+U=
|
github.com/nats-io/nats.go v1.49.0 h1:yh/WvY59gXqYpgl33ZI+XoVPKyut/IcEaqtsiuTJpoE=
|
||||||
github.com/nats-io/nats.go v1.48.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
github.com/nats-io/nats.go v1.49.0/go.mod h1:fDCn3mN5cY8HooHwE2ukiLb4p4G4ImmzvXyJt+tGwdw=
|
||||||
github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
|
github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
|
||||||
github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
|
github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
|
||||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||||
@@ -113,8 +113,8 @@ github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNw
|
|||||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||||
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
||||||
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
||||||
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
github.com/prometheus/procfs v0.20.0 h1:AA7aCvjxwAquZAlonN7888f2u4IN8WVeFgBi4k82M4Q=
|
||||||
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
github.com/prometheus/procfs v0.20.0/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo=
|
||||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||||
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
|
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
|
||||||
@@ -179,8 +179,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
@@ -208,8 +208,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
|
|||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d h1:t/LOSXPJ9R0B6fnZNyALBRfZBH0Uy0gT+uR+SJ6syqQ=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ func (a *App) Run(ctx context.Context) error {
|
|||||||
producer := msgproducer.NewProducer(a.logger.Named("discovery_producer"), broker)
|
producer := msgproducer.NewProducer(a.logger.Named("discovery_producer"), broker)
|
||||||
announce := discovery.Announcement{
|
announce := discovery.Announcement{
|
||||||
Service: "FX_INGESTOR",
|
Service: "FX_INGESTOR",
|
||||||
Operations: []string{"fx.ingest"},
|
Operations: []string{discovery.OperationFXIngest},
|
||||||
Version: appversion.Create().Short(),
|
Version: appversion.Create().Short(),
|
||||||
}
|
}
|
||||||
announcer = discovery.NewAnnouncer(a.logger, producer, "fx_ingestor", announce)
|
announcer = discovery.NewAnnouncer(a.logger, producer, "fx_ingestor", announce)
|
||||||
|
|||||||
@@ -415,7 +415,7 @@ type valuteMapping struct {
|
|||||||
byID map[string]valuteInfo
|
byID map[string]valuteInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildValuteMapping(logger *zap.Logger, items []valuteItem) (*valuteMapping, error) { //nolint:cyclop,gocognit,nestif
|
func buildValuteMapping(logger mlogger.Logger, items []valuteItem) (*valuteMapping, error) { //nolint:cyclop,gocognit,nestif
|
||||||
byISO := make(map[string]valuteInfo, len(items))
|
byISO := make(map[string]valuteInfo, len(items))
|
||||||
byID := make(map[string]valuteInfo, len(items))
|
byID := make(map[string]valuteInfo, len(items))
|
||||||
byNum := make(map[string]string, len(items))
|
byNum := make(map[string]string, len(items))
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/tech/sendico/pkg/mlogger"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -17,7 +18,7 @@ const (
|
|||||||
type httpClient struct {
|
type httpClient struct {
|
||||||
client *http.Client
|
client *http.Client
|
||||||
headers http.Header
|
headers http.Header
|
||||||
logger *zap.Logger
|
logger mlogger.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
type httpClientOptions struct {
|
type httpClientOptions struct {
|
||||||
@@ -26,7 +27,7 @@ type httpClientOptions struct {
|
|||||||
referer string
|
referer string
|
||||||
}
|
}
|
||||||
|
|
||||||
func newHTTPClient(logger *zap.Logger, client *http.Client, opts httpClientOptions) *httpClient {
|
func newHTTPClient(logger mlogger.Logger, client *http.Client, opts httpClientOptions) *httpClient {
|
||||||
userAgent := opts.userAgent
|
userAgent := opts.userAgent
|
||||||
if strings.TrimSpace(userAgent) == "" {
|
if strings.TrimSpace(userAgent) == "" {
|
||||||
userAgent = defaultUserAgent
|
userAgent = defaultUserAgent
|
||||||
|
|||||||
@@ -31,12 +31,12 @@ require (
|
|||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/nats-io/nats.go v1.48.0 // indirect
|
github.com/nats-io/nats.go v1.49.0 // indirect
|
||||||
github.com/nats-io/nkeys v0.4.15 // indirect
|
github.com/nats-io/nkeys v0.4.15 // indirect
|
||||||
github.com/nats-io/nuid v1.0.1 // indirect
|
github.com/nats-io/nuid v1.0.1 // indirect
|
||||||
github.com/prometheus/client_model v0.6.2 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
github.com/prometheus/common v0.67.5 // indirect
|
github.com/prometheus/common v0.67.5 // indirect
|
||||||
github.com/prometheus/procfs v0.19.2 // indirect
|
github.com/prometheus/procfs v0.20.0 // indirect
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
github.com/xdg-go/scram v1.2.0 // indirect
|
github.com/xdg-go/scram v1.2.0 // indirect
|
||||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||||
@@ -44,9 +44,9 @@ require (
|
|||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||||
golang.org/x/crypto v0.48.0 // indirect
|
golang.org/x/crypto v0.48.0 // indirect
|
||||||
golang.org/x/net v0.50.0 // indirect
|
golang.org/x/net v0.51.0 // indirect
|
||||||
golang.org/x/sync v0.19.0 // indirect
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.41.0 // indirect
|
golang.org/x/sys v0.41.0 // indirect
|
||||||
golang.org/x/text v0.34.0 // indirect
|
golang.org/x/text v0.34.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -91,8 +91,8 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
|||||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/nats-io/nats.go v1.48.0 h1:pSFyXApG+yWU/TgbKCjmm5K4wrHu86231/w84qRVR+U=
|
github.com/nats-io/nats.go v1.49.0 h1:yh/WvY59gXqYpgl33ZI+XoVPKyut/IcEaqtsiuTJpoE=
|
||||||
github.com/nats-io/nats.go v1.48.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
github.com/nats-io/nats.go v1.49.0/go.mod h1:fDCn3mN5cY8HooHwE2ukiLb4p4G4ImmzvXyJt+tGwdw=
|
||||||
github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
|
github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
|
||||||
github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
|
github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
|
||||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||||
@@ -113,8 +113,8 @@ github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNw
|
|||||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||||
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
||||||
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
||||||
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
github.com/prometheus/procfs v0.20.0 h1:AA7aCvjxwAquZAlonN7888f2u4IN8WVeFgBi4k82M4Q=
|
||||||
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
github.com/prometheus/procfs v0.20.0/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo=
|
||||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||||
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
|
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
|
||||||
@@ -179,8 +179,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
@@ -208,8 +208,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
|
|||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d h1:t/LOSXPJ9R0B6fnZNyALBRfZBH0Uy0gT+uR+SJ6syqQ=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ func (s *Service) startDiscoveryAnnouncer() {
|
|||||||
}
|
}
|
||||||
announce := discovery.Announcement{
|
announce := discovery.Announcement{
|
||||||
Service: "FX_ORACLE",
|
Service: "FX_ORACLE",
|
||||||
Operations: []string{"fx.quote"},
|
Operations: []string{discovery.OperationFXQuote},
|
||||||
InvokeURI: s.invokeURI,
|
InvokeURI: s.invokeURI,
|
||||||
Version: appversion.Create().Short(),
|
Version: appversion.Create().Short(),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ replace github.com/tech/sendico/pkg => ../../pkg
|
|||||||
replace github.com/tech/sendico/gateway/common => ../common
|
replace github.com/tech/sendico/gateway/common => ../common
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1
|
||||||
github.com/ethereum/go-ethereum v1.17.0
|
github.com/ethereum/go-ethereum v1.17.0
|
||||||
github.com/hashicorp/vault/api v1.22.0
|
github.com/hashicorp/vault/api v1.22.0
|
||||||
github.com/mitchellh/mapstructure v1.5.0
|
github.com/mitchellh/mapstructure v1.5.0
|
||||||
@@ -25,7 +25,7 @@ require (
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260218040609-6f1c0c95351b // indirect
|
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260225065256-91dd007ecddc // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/bits-and-blooms/bitset v1.24.4 // indirect
|
github.com/bits-and-blooms/bitset v1.24.4 // indirect
|
||||||
github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect
|
github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect
|
||||||
@@ -38,7 +38,7 @@ require (
|
|||||||
github.com/crate-crypto/go-eth-kzg v1.5.0 // indirect
|
github.com/crate-crypto/go-eth-kzg v1.5.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/deckarep/golang-set/v2 v2.8.0 // indirect
|
github.com/deckarep/golang-set/v2 v2.8.0 // indirect
|
||||||
github.com/ethereum/c-kzg-4844/v2 v2.1.5 // indirect
|
github.com/ethereum/c-kzg-4844/v2 v2.1.6 // indirect
|
||||||
github.com/go-chi/chi/v5 v5.2.5 // indirect
|
github.com/go-chi/chi/v5 v5.2.5 // indirect
|
||||||
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
|
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
|
||||||
github.com/go-logr/logr v1.4.3 // indirect
|
github.com/go-logr/logr v1.4.3 // indirect
|
||||||
@@ -61,13 +61,13 @@ require (
|
|||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/nats-io/nats.go v1.48.0 // indirect
|
github.com/nats-io/nats.go v1.49.0 // indirect
|
||||||
github.com/nats-io/nkeys v0.4.15 // indirect
|
github.com/nats-io/nkeys v0.4.15 // indirect
|
||||||
github.com/nats-io/nuid v1.0.1 // indirect
|
github.com/nats-io/nuid v1.0.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/prometheus/client_model v0.6.2 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
github.com/prometheus/common v0.67.5 // indirect
|
github.com/prometheus/common v0.67.5 // indirect
|
||||||
github.com/prometheus/procfs v0.19.2 // indirect
|
github.com/prometheus/procfs v0.20.0 // indirect
|
||||||
github.com/ryanuber/go-glob v1.0.0 // indirect
|
github.com/ryanuber/go-glob v1.0.0 // indirect
|
||||||
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
|
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
|
||||||
github.com/supranational/blst v0.3.16 // indirect
|
github.com/supranational/blst v0.3.16 // indirect
|
||||||
@@ -86,10 +86,10 @@ require (
|
|||||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||||
golang.org/x/crypto v0.48.0 // indirect
|
golang.org/x/crypto v0.48.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect
|
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect
|
||||||
golang.org/x/net v0.50.0 // indirect
|
golang.org/x/net v0.51.0 // indirect
|
||||||
golang.org/x/sync v0.19.0 // indirect
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.41.0 // indirect
|
golang.org/x/sys v0.41.0 // indirect
|
||||||
golang.org/x/text v0.34.0 // indirect
|
golang.org/x/text v0.34.0 // indirect
|
||||||
golang.org/x/time v0.14.0 // indirect
|
golang.org/x/time v0.14.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
|
|||||||
github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
|
github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
|
||||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260218040609-6f1c0c95351b h1:RVnS+OZmBJbbNeqejAksq3Mxc73y0IEzyTUHPPWZuj8=
|
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260225065256-91dd007ecddc h1:1stW1OipdBj8Me+nj26SzT8yil7OYve0r3cWobzk1JQ=
|
||||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260218040609-6f1c0c95351b/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
|
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260225065256-91dd007ecddc/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
|
||||||
github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0=
|
github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0=
|
||||||
github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU=
|
github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
@@ -60,8 +60,8 @@ github.com/deckarep/golang-set/v2 v2.8.0 h1:swm0rlPCmdWn9mESxKOjWk8hXSqoxOp+Zlfu
|
|||||||
github.com/deckarep/golang-set/v2 v2.8.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
|
github.com/deckarep/golang-set/v2 v2.8.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
|
||||||
github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8=
|
github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8=
|
||||||
github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
|
github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 h1:5RVFMOWjMyRy8cARdy79nAmgYw3hK/4HUq48LQ6Wwqo=
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
|
||||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||||
github.com/docker/docker v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI=
|
github.com/docker/docker v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI=
|
||||||
@@ -72,8 +72,8 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4
|
|||||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A=
|
github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A=
|
||||||
github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s=
|
github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s=
|
||||||
github.com/ethereum/c-kzg-4844/v2 v2.1.5 h1:aVtoLK5xwJ6c5RiqO8g8ptJ5KU+2Hdquf6G3aXiHh5s=
|
github.com/ethereum/c-kzg-4844/v2 v2.1.6 h1:xQymkKCT5E2Jiaoqf3v4wsNgjZLY0lRSkZn27fRjSls=
|
||||||
github.com/ethereum/c-kzg-4844/v2 v2.1.5/go.mod h1:u59hRTTah4Co6i9fDWtiCjTrblJv0UwsqZKCc0GfgUs=
|
github.com/ethereum/c-kzg-4844/v2 v2.1.6/go.mod h1:8HMkUZ5JRv4hpw/XUrYWSQNAUzhHMg2UDb/U+5m+XNw=
|
||||||
github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab h1:rvv6MJhy07IMfEKuARQ9TKojGqLVNxQajaXEp/BoqSk=
|
github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab h1:rvv6MJhy07IMfEKuARQ9TKojGqLVNxQajaXEp/BoqSk=
|
||||||
github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab/go.mod h1:IuLm4IsPipXKF7CW5Lzf68PIbZ5yl7FFd74l/E0o9A8=
|
github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab/go.mod h1:IuLm4IsPipXKF7CW5Lzf68PIbZ5yl7FFd74l/E0o9A8=
|
||||||
github.com/ethereum/go-ethereum v1.17.0 h1:2D+1Fe23CwZ5tQoAS5DfwKFNI1HGcTwi65/kRlAVxes=
|
github.com/ethereum/go-ethereum v1.17.0 h1:2D+1Fe23CwZ5tQoAS5DfwKFNI1HGcTwi65/kRlAVxes=
|
||||||
@@ -204,8 +204,8 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
|||||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/nats-io/nats.go v1.48.0 h1:pSFyXApG+yWU/TgbKCjmm5K4wrHu86231/w84qRVR+U=
|
github.com/nats-io/nats.go v1.49.0 h1:yh/WvY59gXqYpgl33ZI+XoVPKyut/IcEaqtsiuTJpoE=
|
||||||
github.com/nats-io/nats.go v1.48.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
github.com/nats-io/nats.go v1.49.0/go.mod h1:fDCn3mN5cY8HooHwE2ukiLb4p4G4ImmzvXyJt+tGwdw=
|
||||||
github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
|
github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
|
||||||
github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
|
github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
|
||||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||||
@@ -236,8 +236,8 @@ github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNw
|
|||||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||||
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
||||||
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
||||||
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
github.com/prometheus/procfs v0.20.0 h1:AA7aCvjxwAquZAlonN7888f2u4IN8WVeFgBi4k82M4Q=
|
||||||
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
github.com/prometheus/procfs v0.20.0/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo=
|
||||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||||
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
|
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
|
||||||
@@ -322,8 +322,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
@@ -355,8 +355,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
|
|||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d h1:t/LOSXPJ9R0B6fnZNyALBRfZBH0Uy0gT+uR+SJ6syqQ=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
|
|||||||
@@ -224,8 +224,8 @@ func (s *Service) startDiscoveryAnnouncers() {
|
|||||||
announce := discovery.Announcement{
|
announce := discovery.Announcement{
|
||||||
ID: discovery.StableCryptoRailGatewayID(string(network.Name)),
|
ID: discovery.StableCryptoRailGatewayID(string(network.Name)),
|
||||||
Service: "CRYPTO_RAIL_GATEWAY",
|
Service: "CRYPTO_RAIL_GATEWAY",
|
||||||
Rail: "CRYPTO",
|
Rail: discovery.RailCrypto,
|
||||||
Operations: []string{"balance.read", "payin.crypto", "payout.crypto", "fee.send", "observe.confirm"},
|
Operations: discovery.CryptoRailGatewayOperations(),
|
||||||
Currencies: currencies,
|
Currencies: currencies,
|
||||||
InvokeURI: s.invokeURI,
|
InvokeURI: s.invokeURI,
|
||||||
Version: version,
|
Version: version,
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ require (
|
|||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/klauspost/compress v1.18.4 // indirect
|
github.com/klauspost/compress v1.18.4 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/nats-io/nats.go v1.48.0 // indirect
|
github.com/nats-io/nats.go v1.49.0 // indirect
|
||||||
github.com/nats-io/nkeys v0.4.15 // indirect
|
github.com/nats-io/nkeys v0.4.15 // indirect
|
||||||
github.com/nats-io/nuid v1.0.1 // indirect
|
github.com/nats-io/nuid v1.0.1 // indirect
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
|
|||||||
@@ -60,8 +60,8 @@ github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
|||||||
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
github.com/nats-io/nats.go v1.48.0 h1:pSFyXApG+yWU/TgbKCjmm5K4wrHu86231/w84qRVR+U=
|
github.com/nats-io/nats.go v1.49.0 h1:yh/WvY59gXqYpgl33ZI+XoVPKyut/IcEaqtsiuTJpoE=
|
||||||
github.com/nats-io/nats.go v1.48.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
github.com/nats-io/nats.go v1.49.0/go.mod h1:fDCn3mN5cY8HooHwE2ukiLb4p4G4ImmzvXyJt+tGwdw=
|
||||||
github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
|
github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
|
||||||
github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
|
github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
|
||||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/shopspring/decimal"
|
"github.com/shopspring/decimal"
|
||||||
"github.com/tech/sendico/pkg/merrors"
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
|
"github.com/tech/sendico/pkg/mlogger"
|
||||||
"github.com/tech/sendico/pkg/model/account_role"
|
"github.com/tech/sendico/pkg/model/account_role"
|
||||||
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
|
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
|
||||||
connectorv1 "github.com/tech/sendico/pkg/proto/connector/v1"
|
connectorv1 "github.com/tech/sendico/pkg/proto/connector/v1"
|
||||||
@@ -35,7 +36,7 @@ type gatewayClient struct {
|
|||||||
conn *grpc.ClientConn
|
conn *grpc.ClientConn
|
||||||
client grpcConnectorClient
|
client grpcConnectorClient
|
||||||
cfg Config
|
cfg Config
|
||||||
logger *zap.Logger
|
logger mlogger.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// New dials the Monetix gateway.
|
// New dials the Monetix gateway.
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package client
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/tech/sendico/pkg/mlogger"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -11,7 +12,7 @@ type Config struct {
|
|||||||
Address string
|
Address string
|
||||||
DialTimeout time.Duration
|
DialTimeout time.Duration
|
||||||
CallTimeout time.Duration
|
CallTimeout time.Duration
|
||||||
Logger *zap.Logger
|
Logger mlogger.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) setDefaults() {
|
func (c *Config) setDefaults() {
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ messaging:
|
|||||||
reconnect_wait: 5
|
reconnect_wait: 5
|
||||||
buffer_size: 1024
|
buffer_size: 1024
|
||||||
|
|
||||||
monetix:
|
mcards:
|
||||||
base_url_env: MONETIX_BASE_URL
|
base_url_env: MONETIX_BASE_URL
|
||||||
project_id_env: MONETIX_PROJECT_ID
|
project_id_env: MONETIX_PROJECT_ID
|
||||||
secret_key_env: MONETIX_SECRET_KEY
|
secret_key_env: MONETIX_SECRET_KEY
|
||||||
@@ -46,7 +46,7 @@ monetix:
|
|||||||
status_processing: "processing"
|
status_processing: "processing"
|
||||||
|
|
||||||
gateway:
|
gateway:
|
||||||
id: "monetix"
|
id: "mcards"
|
||||||
is_enabled: true
|
is_enabled: true
|
||||||
network: "MIR"
|
network: "MIR"
|
||||||
currencies: ["RUB"]
|
currencies: ["RUB"]
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ messaging:
|
|||||||
reconnect_wait: 5
|
reconnect_wait: 5
|
||||||
buffer_size: 1024
|
buffer_size: 1024
|
||||||
|
|
||||||
monetix:
|
mcards:
|
||||||
base_url_env: MONETIX_BASE_URL
|
base_url_env: MONETIX_BASE_URL
|
||||||
project_id_env: MONETIX_PROJECT_ID
|
project_id_env: MONETIX_PROJECT_ID
|
||||||
secret_key_env: MONETIX_SECRET_KEY
|
secret_key_env: MONETIX_SECRET_KEY
|
||||||
@@ -46,7 +46,7 @@ monetix:
|
|||||||
status_processing: "processing"
|
status_processing: "processing"
|
||||||
|
|
||||||
gateway:
|
gateway:
|
||||||
id: "monetix"
|
id: "mcards"
|
||||||
is_enabled: true
|
is_enabled: true
|
||||||
network: "MIR"
|
network: "MIR"
|
||||||
currencies: ["RUB"]
|
currencies: ["RUB"]
|
||||||
|
|||||||
@@ -32,12 +32,12 @@ require (
|
|||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/nats-io/nats.go v1.48.0 // indirect
|
github.com/nats-io/nats.go v1.49.0 // indirect
|
||||||
github.com/nats-io/nkeys v0.4.15 // indirect
|
github.com/nats-io/nkeys v0.4.15 // indirect
|
||||||
github.com/nats-io/nuid v1.0.1 // indirect
|
github.com/nats-io/nuid v1.0.1 // indirect
|
||||||
github.com/prometheus/client_model v0.6.2 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
github.com/prometheus/common v0.67.5 // indirect
|
github.com/prometheus/common v0.67.5 // indirect
|
||||||
github.com/prometheus/procfs v0.19.2 // indirect
|
github.com/prometheus/procfs v0.20.0 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
||||||
github.com/tklauser/go-sysconf v0.3.16 // indirect
|
github.com/tklauser/go-sysconf v0.3.16 // indirect
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
@@ -47,9 +47,9 @@ require (
|
|||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||||
golang.org/x/crypto v0.48.0 // indirect
|
golang.org/x/crypto v0.48.0 // indirect
|
||||||
golang.org/x/net v0.50.0 // indirect
|
golang.org/x/net v0.51.0 // indirect
|
||||||
golang.org/x/sync v0.19.0 // indirect
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.41.0 // indirect
|
golang.org/x/sys v0.41.0 // indirect
|
||||||
golang.org/x/text v0.34.0 // indirect
|
golang.org/x/text v0.34.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -91,8 +91,8 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
|||||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/nats-io/nats.go v1.48.0 h1:pSFyXApG+yWU/TgbKCjmm5K4wrHu86231/w84qRVR+U=
|
github.com/nats-io/nats.go v1.49.0 h1:yh/WvY59gXqYpgl33ZI+XoVPKyut/IcEaqtsiuTJpoE=
|
||||||
github.com/nats-io/nats.go v1.48.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
github.com/nats-io/nats.go v1.49.0/go.mod h1:fDCn3mN5cY8HooHwE2ukiLb4p4G4ImmzvXyJt+tGwdw=
|
||||||
github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
|
github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
|
||||||
github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
|
github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
|
||||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||||
@@ -113,8 +113,8 @@ github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNw
|
|||||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||||
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
||||||
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
||||||
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
github.com/prometheus/procfs v0.20.0 h1:AA7aCvjxwAquZAlonN7888f2u4IN8WVeFgBi4k82M4Q=
|
||||||
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
github.com/prometheus/procfs v0.20.0/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo=
|
||||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||||
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
|
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
|
||||||
@@ -181,8 +181,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
@@ -210,8 +210,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
|
|||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d h1:t/LOSXPJ9R0B6fnZNyALBRfZBH0Uy0gT+uR+SJ6syqQ=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import (
|
|||||||
"github.com/tech/sendico/pkg/merrors"
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
msg "github.com/tech/sendico/pkg/messaging"
|
msg "github.com/tech/sendico/pkg/messaging"
|
||||||
"github.com/tech/sendico/pkg/mlogger"
|
"github.com/tech/sendico/pkg/mlogger"
|
||||||
|
paymenttypes "github.com/tech/sendico/pkg/payments/types"
|
||||||
gatewayv1 "github.com/tech/sendico/pkg/proto/common/gateway/v1"
|
gatewayv1 "github.com/tech/sendico/pkg/proto/common/gateway/v1"
|
||||||
"github.com/tech/sendico/pkg/server/grpcapp"
|
"github.com/tech/sendico/pkg/server/grpcapp"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
@@ -41,7 +42,7 @@ type Imp struct {
|
|||||||
|
|
||||||
type config struct {
|
type config struct {
|
||||||
*grpcapp.Config `yaml:",inline"`
|
*grpcapp.Config `yaml:",inline"`
|
||||||
Monetix monetixConfig `yaml:"monetix"`
|
Monetix monetixConfig `yaml:"mcards"`
|
||||||
Gateway gatewayConfig `yaml:"gateway"`
|
Gateway gatewayConfig `yaml:"gateway"`
|
||||||
HTTP httpConfig `yaml:"http"`
|
HTTP httpConfig `yaml:"http"`
|
||||||
}
|
}
|
||||||
@@ -216,7 +217,7 @@ func (i *Imp) Start() error {
|
|||||||
return gatewaymongo.New(logger, conn)
|
return gatewaymongo.New(logger, conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
app, err := grpcapp.NewApp(i.logger, "monetix", cfg.Config, i.debug, repoFactory, serviceFactory)
|
app, err := grpcapp.NewApp(i.logger, paymenttypes.DefaultCardsGatewayID, cfg.Config, i.debug, repoFactory, serviceFactory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -275,7 +276,7 @@ func (i *Imp) resolveMonetixConfig(cfg monetixConfig) (monetix.Config, error) {
|
|||||||
if id, err := strconv.ParseInt(raw, 10, 64); err == nil {
|
if id, err := strconv.ParseInt(raw, 10, 64); err == nil {
|
||||||
projectID = id
|
projectID = id
|
||||||
} else {
|
} else {
|
||||||
return monetix.Config{}, merrors.InvalidArgument("invalid project id in env "+cfg.ProjectIDEnv, "monetix.project_id")
|
return monetix.Config{}, merrors.InvalidArgument("invalid project id in env "+cfg.ProjectIDEnv, "mcards.project_id")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -310,7 +311,7 @@ func (i *Imp) resolveMonetixConfig(cfg monetixConfig) (monetix.Config, error) {
|
|||||||
func resolveGatewayDescriptor(cfg gatewayConfig, monetixCfg monetix.Config) *gatewayv1.GatewayInstanceDescriptor {
|
func resolveGatewayDescriptor(cfg gatewayConfig, monetixCfg monetix.Config) *gatewayv1.GatewayInstanceDescriptor {
|
||||||
id := strings.TrimSpace(cfg.ID)
|
id := strings.TrimSpace(cfg.ID)
|
||||||
if id == "" {
|
if id == "" {
|
||||||
id = "monetix"
|
id = paymenttypes.DefaultCardsGatewayID
|
||||||
}
|
}
|
||||||
|
|
||||||
network := strings.ToUpper(strings.TrimSpace(cfg.Network))
|
network := strings.ToUpper(strings.TrimSpace(cfg.Network))
|
||||||
@@ -333,7 +334,7 @@ func resolveGatewayDescriptor(cfg gatewayConfig, monetixCfg monetix.Config) *gat
|
|||||||
|
|
||||||
return &gatewayv1.GatewayInstanceDescriptor{
|
return &gatewayv1.GatewayInstanceDescriptor{
|
||||||
Id: id,
|
Id: id,
|
||||||
Rail: gatewayv1.Rail_RAIL_CARD_PAYOUT,
|
Rail: gatewayv1.Rail_RAIL_CARD,
|
||||||
Network: network,
|
Network: network,
|
||||||
Currencies: currencies,
|
Currencies: currencies,
|
||||||
Capabilities: &gatewayv1.RailCapabilities{
|
Capabilities: &gatewayv1.RailCapabilities{
|
||||||
@@ -444,7 +445,7 @@ func (i *Imp) resolveCallbackConfig(cfg callbackConfig) (callbackRuntimeConfig,
|
|||||||
}
|
}
|
||||||
path := strings.TrimSpace(cfg.Path)
|
path := strings.TrimSpace(cfg.Path)
|
||||||
if path == "" {
|
if path == "" {
|
||||||
path = "/monetix/callback"
|
path = "/" + paymenttypes.DefaultCardsGatewayID + "/callback"
|
||||||
}
|
}
|
||||||
maxBody := cfg.MaxBodyBytes
|
maxBody := cfg.MaxBodyBytes
|
||||||
if maxBody <= 0 {
|
if maxBody <= 0 {
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ func (p *cardPayoutProcessor) resolveProjectID(requestProjectID int64, logFieldK
|
|||||||
}
|
}
|
||||||
if projectID == 0 {
|
if projectID == 0 {
|
||||||
p.logger.Warn("Monetix project_id is not configured", zap.String(logFieldKey, logFieldValue))
|
p.logger.Warn("Monetix project_id is not configured", zap.String(logFieldKey, logFieldValue))
|
||||||
return 0, merrors.Internal("monetix project_id is not configured")
|
return 0, merrors.Internal("mcards project_id is not configured")
|
||||||
}
|
}
|
||||||
return projectID, nil
|
return projectID, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -151,9 +151,9 @@ func (s *Service) startDiscoveryAnnouncer() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
announce := discovery.Announcement{
|
announce := discovery.Announcement{
|
||||||
Service: "CARD_PAYOUT_RAIL_GATEWAY",
|
Service: "CARD_RAIL_GATEWAY",
|
||||||
Rail: "CARD_PAYOUT",
|
Rail: discovery.RailCardPayout,
|
||||||
Operations: []string{"payout.card", "observe.confirm"},
|
Operations: discovery.CardPayoutRailGatewayOperations(),
|
||||||
InvokeURI: s.invokeURI,
|
InvokeURI: s.invokeURI,
|
||||||
Version: appversion.Create().Short(),
|
Version: appversion.Create().Short(),
|
||||||
}
|
}
|
||||||
@@ -164,7 +164,7 @@ func (s *Service) startDiscoveryAnnouncer() {
|
|||||||
announce.Currencies = currenciesFromDescriptor(s.gatewayDescriptor)
|
announce.Currencies = currenciesFromDescriptor(s.gatewayDescriptor)
|
||||||
}
|
}
|
||||||
if strings.TrimSpace(announce.ID) == "" {
|
if strings.TrimSpace(announce.ID) == "" {
|
||||||
announce.ID = "card_payout_rail_gateway"
|
announce.ID = discovery.StablePaymentGatewayID(discovery.RailCardPayout)
|
||||||
}
|
}
|
||||||
s.announcer = discovery.NewAnnouncer(s.logger, s.producer, string(mservice.MntxGateway), announce)
|
s.announcer = discovery.NewAnnouncer(s.logger, s.producer, string(mservice.MntxGateway), announce)
|
||||||
s.announcer.Start()
|
s.announcer.Start()
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tech/sendico/pkg/merrors"
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
|
"github.com/tech/sendico/pkg/mlogger"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -312,10 +313,7 @@ func clearSignature(req any) (func(string), error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func logRequestDeadline(logger *zap.Logger, ctx context.Context, url string) {
|
func logRequestDeadline(logger mlogger.Logger, ctx context.Context, url string) {
|
||||||
if logger == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
logger.Info("Monetix request context is nil", zap.String("url", url))
|
logger.Info("Monetix request context is nil", zap.String("url", url))
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ messaging:
|
|||||||
buffer_size: 1024
|
buffer_size: 1024
|
||||||
|
|
||||||
gateway:
|
gateway:
|
||||||
rail: "provider_settlement"
|
rail: "SETTLEMENT"
|
||||||
target_chat_id_env: TGSETTLE_GATEWAY_CHAT_ID
|
target_chat_id_env: TGSETTLE_GATEWAY_CHAT_ID
|
||||||
timeout_seconds: 345600
|
timeout_seconds: 345600
|
||||||
accepted_user_ids: []
|
accepted_user_ids: []
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ messaging:
|
|||||||
buffer_size: 1024
|
buffer_size: 1024
|
||||||
|
|
||||||
gateway:
|
gateway:
|
||||||
rail: "provider_settlement"
|
rail: "SETTLEMENT"
|
||||||
target_chat_id_env: TGSETTLE_GATEWAY_CHAT_ID
|
target_chat_id_env: TGSETTLE_GATEWAY_CHAT_ID
|
||||||
timeout_seconds: 345600
|
timeout_seconds: 345600
|
||||||
accepted_user_ids: []
|
accepted_user_ids: []
|
||||||
|
|||||||
@@ -30,13 +30,13 @@ require (
|
|||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/nats-io/nats.go v1.48.0 // indirect
|
github.com/nats-io/nats.go v1.49.0 // indirect
|
||||||
github.com/nats-io/nkeys v0.4.15 // indirect
|
github.com/nats-io/nkeys v0.4.15 // indirect
|
||||||
github.com/nats-io/nuid v1.0.1 // indirect
|
github.com/nats-io/nuid v1.0.1 // indirect
|
||||||
github.com/prometheus/client_golang v1.23.2 // indirect
|
github.com/prometheus/client_golang v1.23.2 // indirect
|
||||||
github.com/prometheus/client_model v0.6.2 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
github.com/prometheus/common v0.67.5 // indirect
|
github.com/prometheus/common v0.67.5 // indirect
|
||||||
github.com/prometheus/procfs v0.19.2 // indirect
|
github.com/prometheus/procfs v0.20.0 // indirect
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
github.com/xdg-go/scram v1.2.0 // indirect
|
github.com/xdg-go/scram v1.2.0 // indirect
|
||||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||||
@@ -44,9 +44,9 @@ require (
|
|||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||||
golang.org/x/crypto v0.48.0 // indirect
|
golang.org/x/crypto v0.48.0 // indirect
|
||||||
golang.org/x/net v0.50.0 // indirect
|
golang.org/x/net v0.51.0 // indirect
|
||||||
golang.org/x/sync v0.19.0 // indirect
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.41.0 // indirect
|
golang.org/x/sys v0.41.0 // indirect
|
||||||
golang.org/x/text v0.34.0 // indirect
|
golang.org/x/text v0.34.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -91,8 +91,8 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
|||||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/nats-io/nats.go v1.48.0 h1:pSFyXApG+yWU/TgbKCjmm5K4wrHu86231/w84qRVR+U=
|
github.com/nats-io/nats.go v1.49.0 h1:yh/WvY59gXqYpgl33ZI+XoVPKyut/IcEaqtsiuTJpoE=
|
||||||
github.com/nats-io/nats.go v1.48.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
github.com/nats-io/nats.go v1.49.0/go.mod h1:fDCn3mN5cY8HooHwE2ukiLb4p4G4ImmzvXyJt+tGwdw=
|
||||||
github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
|
github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
|
||||||
github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
|
github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
|
||||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||||
@@ -113,8 +113,8 @@ github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNw
|
|||||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||||
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
||||||
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
||||||
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
github.com/prometheus/procfs v0.20.0 h1:AA7aCvjxwAquZAlonN7888f2u4IN8WVeFgBi4k82M4Q=
|
||||||
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
github.com/prometheus/procfs v0.20.0/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo=
|
||||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||||
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
|
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
|
||||||
@@ -179,8 +179,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
@@ -208,8 +208,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
|
|||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d h1:t/LOSXPJ9R0B6fnZNyALBRfZBH0Uy0gT+uR+SJ6syqQ=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
gatewaymongo "github.com/tech/sendico/gateway/tgsettle/storage/mongo"
|
gatewaymongo "github.com/tech/sendico/gateway/tgsettle/storage/mongo"
|
||||||
"github.com/tech/sendico/pkg/api/routers"
|
"github.com/tech/sendico/pkg/api/routers"
|
||||||
"github.com/tech/sendico/pkg/db"
|
"github.com/tech/sendico/pkg/db"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"github.com/tech/sendico/pkg/merrors"
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
msg "github.com/tech/sendico/pkg/messaging"
|
msg "github.com/tech/sendico/pkg/messaging"
|
||||||
mb "github.com/tech/sendico/pkg/messaging/broker"
|
mb "github.com/tech/sendico/pkg/messaging/broker"
|
||||||
@@ -141,8 +142,12 @@ func (i *Imp) loadConfig() (*config, error) {
|
|||||||
if cfg.Metrics == nil {
|
if cfg.Metrics == nil {
|
||||||
cfg.Metrics = &grpcapp.MetricsConfig{Address: ":9406"}
|
cfg.Metrics = &grpcapp.MetricsConfig{Address: ":9406"}
|
||||||
}
|
}
|
||||||
|
cfg.Gateway.Rail = discovery.NormalizeRail(cfg.Gateway.Rail)
|
||||||
if cfg.Gateway.Rail == "" {
|
if cfg.Gateway.Rail == "" {
|
||||||
return nil, merrors.InvalidArgument("gateway rail is required", "gateway.rail")
|
return nil, merrors.InvalidArgument("gateway rail is required", "gateway.rail")
|
||||||
}
|
}
|
||||||
|
if !discovery.IsKnownRail(cfg.Gateway.Rail) {
|
||||||
|
return nil, merrors.InvalidArgument("gateway rail must be a known token", "gateway.rail")
|
||||||
|
}
|
||||||
return cfg, nil
|
return cfg, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -313,7 +313,7 @@ func (s *Service) publishPendingConfirmationResult(pending *storagemodel.Pending
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) sendTelegramText(ctx context.Context, request *model.TelegramTextRequest) error {
|
func (s *Service) sendTelegramText(_ context.Context, request *model.TelegramTextRequest) error {
|
||||||
if request == nil {
|
if request == nil {
|
||||||
return merrors.InvalidArgument("telegram text request is nil", "request")
|
return merrors.InvalidArgument("telegram text request is nil", "request")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,9 +95,12 @@ func NewService(logger mlogger.Logger, repo storage.Repository, producer msg.Pro
|
|||||||
broker: broker,
|
broker: broker,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
msgCfg: cfg.MessagingSettings,
|
msgCfg: cfg.MessagingSettings,
|
||||||
rail: strings.TrimSpace(cfg.Rail),
|
rail: discovery.NormalizeRail(cfg.Rail),
|
||||||
invokeURI: strings.TrimSpace(cfg.InvokeURI),
|
invokeURI: strings.TrimSpace(cfg.InvokeURI),
|
||||||
}
|
}
|
||||||
|
if svc.rail == "" {
|
||||||
|
svc.rail = strings.TrimSpace(cfg.Rail)
|
||||||
|
}
|
||||||
svc.chatID = strings.TrimSpace(readEnv(cfg.TargetChatIDEnv))
|
svc.chatID = strings.TrimSpace(readEnv(cfg.TargetChatIDEnv))
|
||||||
svc.successReaction = strings.TrimSpace(cfg.SuccessReaction)
|
svc.successReaction = strings.TrimSpace(cfg.SuccessReaction)
|
||||||
if svc.successReaction == "" {
|
if svc.successReaction == "" {
|
||||||
@@ -526,14 +529,12 @@ func (s *Service) startAnnouncer() {
|
|||||||
if s == nil || s.producer == nil {
|
if s == nil || s.producer == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
caps := []string{"telegram_confirmation", "money_persistence", "observe.confirm", "payout.fiat"}
|
rail := discovery.RailProviderSettlement
|
||||||
if s.rail != "" {
|
caps := discovery.ProviderSettlementRailGatewayOperations()
|
||||||
caps = append(caps, "confirmations."+strings.ToLower(string(mservice.PaymentGateway))+"."+strings.ToLower(s.rail))
|
|
||||||
}
|
|
||||||
announce := discovery.Announcement{
|
announce := discovery.Announcement{
|
||||||
ID: discovery.StablePaymentGatewayID(s.rail),
|
ID: discovery.StablePaymentGatewayID(rail),
|
||||||
Service: string(mservice.PaymentGateway),
|
Service: string(mservice.PaymentGateway),
|
||||||
Rail: s.rail,
|
Rail: rail,
|
||||||
Operations: caps,
|
Operations: caps,
|
||||||
InvokeURI: s.invokeURI,
|
InvokeURI: s.invokeURI,
|
||||||
}
|
}
|
||||||
@@ -755,27 +756,3 @@ func readEnv(env string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var _ grpcapp.Service = (*Service)(nil)
|
var _ grpcapp.Service = (*Service)(nil)
|
||||||
|
|
||||||
func statusFromConfirmationResult(r *model.ConfirmationResult) storagemodel.PaymentStatus {
|
|
||||||
if r == nil {
|
|
||||||
return storagemodel.PaymentStatusWaiting
|
|
||||||
}
|
|
||||||
|
|
||||||
switch r.Status {
|
|
||||||
|
|
||||||
case model.ConfirmationStatusConfirmed:
|
|
||||||
return storagemodel.PaymentStatusProcessing
|
|
||||||
|
|
||||||
case model.ConfirmationStatusClarified:
|
|
||||||
return storagemodel.PaymentStatusWaiting
|
|
||||||
|
|
||||||
case model.ConfirmationStatusRejected:
|
|
||||||
return storagemodel.PaymentStatusFailed
|
|
||||||
|
|
||||||
case model.ConfirmationStatusTimeout:
|
|
||||||
return storagemodel.PaymentStatusFailed
|
|
||||||
|
|
||||||
default:
|
|
||||||
return storagemodel.PaymentStatusFailed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ replace github.com/tech/sendico/pkg => ../../pkg
|
|||||||
replace github.com/tech/sendico/gateway/common => ../common
|
replace github.com/tech/sendico/gateway/common => ../common
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1
|
||||||
github.com/ethereum/go-ethereum v1.17.0
|
github.com/ethereum/go-ethereum v1.17.0
|
||||||
github.com/fbsobreira/gotron-sdk v0.24.1
|
github.com/fbsobreira/gotron-sdk v0.24.1
|
||||||
github.com/hashicorp/vault/api v1.22.0
|
github.com/hashicorp/vault/api v1.22.0
|
||||||
@@ -27,7 +27,7 @@ require (
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260218040609-6f1c0c95351b // indirect
|
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260225065256-91dd007ecddc // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/bits-and-blooms/bitset v1.24.4 // indirect
|
github.com/bits-and-blooms/bitset v1.24.4 // indirect
|
||||||
github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect
|
github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect
|
||||||
@@ -42,7 +42,7 @@ require (
|
|||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/deckarep/golang-set v1.8.0 // indirect
|
github.com/deckarep/golang-set v1.8.0 // indirect
|
||||||
github.com/deckarep/golang-set/v2 v2.8.0 // indirect
|
github.com/deckarep/golang-set/v2 v2.8.0 // indirect
|
||||||
github.com/ethereum/c-kzg-4844/v2 v2.1.5 // indirect
|
github.com/ethereum/c-kzg-4844/v2 v2.1.6 // indirect
|
||||||
github.com/go-chi/chi/v5 v5.2.5 // indirect
|
github.com/go-chi/chi/v5 v5.2.5 // indirect
|
||||||
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
|
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
|
||||||
github.com/go-logr/logr v1.4.3 // indirect
|
github.com/go-logr/logr v1.4.3 // indirect
|
||||||
@@ -65,7 +65,7 @@ require (
|
|||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/nats-io/nats.go v1.48.0 // indirect
|
github.com/nats-io/nats.go v1.49.0 // indirect
|
||||||
github.com/nats-io/nkeys v0.4.15 // indirect
|
github.com/nats-io/nkeys v0.4.15 // indirect
|
||||||
github.com/nats-io/nuid v1.0.1 // indirect
|
github.com/nats-io/nuid v1.0.1 // indirect
|
||||||
github.com/pborman/uuid v1.2.1 // indirect
|
github.com/pborman/uuid v1.2.1 // indirect
|
||||||
@@ -73,7 +73,7 @@ require (
|
|||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/prometheus/client_model v0.6.2 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
github.com/prometheus/common v0.67.5 // indirect
|
github.com/prometheus/common v0.67.5 // indirect
|
||||||
github.com/prometheus/procfs v0.19.2 // indirect
|
github.com/prometheus/procfs v0.20.0 // indirect
|
||||||
github.com/rjeczalik/notify v0.9.3 // indirect
|
github.com/rjeczalik/notify v0.9.3 // indirect
|
||||||
github.com/ryanuber/go-glob v1.0.0 // indirect
|
github.com/ryanuber/go-glob v1.0.0 // indirect
|
||||||
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
|
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
|
||||||
@@ -94,11 +94,11 @@ require (
|
|||||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||||
golang.org/x/crypto v0.48.0 // indirect
|
golang.org/x/crypto v0.48.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect
|
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect
|
||||||
golang.org/x/net v0.50.0 // indirect
|
golang.org/x/net v0.51.0 // indirect
|
||||||
golang.org/x/sync v0.19.0 // indirect
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.41.0 // indirect
|
golang.org/x/sys v0.41.0 // indirect
|
||||||
golang.org/x/text v0.34.0 // indirect
|
golang.org/x/text v0.34.0 // indirect
|
||||||
golang.org/x/time v0.14.0 // indirect
|
golang.org/x/time v0.14.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20260217215200-42d3e9bedb6d // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20260226221140-a57be14db171 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
|
|||||||
github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
|
github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
|
||||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260218040609-6f1c0c95351b h1:RVnS+OZmBJbbNeqejAksq3Mxc73y0IEzyTUHPPWZuj8=
|
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260225065256-91dd007ecddc h1:1stW1OipdBj8Me+nj26SzT8yil7OYve0r3cWobzk1JQ=
|
||||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260218040609-6f1c0c95351b/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
|
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260225065256-91dd007ecddc/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
|
||||||
github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0=
|
github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0=
|
||||||
github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU=
|
github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
@@ -64,8 +64,8 @@ github.com/deckarep/golang-set/v2 v2.8.0 h1:swm0rlPCmdWn9mESxKOjWk8hXSqoxOp+Zlfu
|
|||||||
github.com/deckarep/golang-set/v2 v2.8.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
|
github.com/deckarep/golang-set/v2 v2.8.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
|
||||||
github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8=
|
github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8=
|
||||||
github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
|
github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 h1:5RVFMOWjMyRy8cARdy79nAmgYw3hK/4HUq48LQ6Wwqo=
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
|
||||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||||
github.com/docker/docker v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI=
|
github.com/docker/docker v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI=
|
||||||
@@ -76,8 +76,8 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4
|
|||||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A=
|
github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A=
|
||||||
github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s=
|
github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s=
|
||||||
github.com/ethereum/c-kzg-4844/v2 v2.1.5 h1:aVtoLK5xwJ6c5RiqO8g8ptJ5KU+2Hdquf6G3aXiHh5s=
|
github.com/ethereum/c-kzg-4844/v2 v2.1.6 h1:xQymkKCT5E2Jiaoqf3v4wsNgjZLY0lRSkZn27fRjSls=
|
||||||
github.com/ethereum/c-kzg-4844/v2 v2.1.5/go.mod h1:u59hRTTah4Co6i9fDWtiCjTrblJv0UwsqZKCc0GfgUs=
|
github.com/ethereum/c-kzg-4844/v2 v2.1.6/go.mod h1:8HMkUZ5JRv4hpw/XUrYWSQNAUzhHMg2UDb/U+5m+XNw=
|
||||||
github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab h1:rvv6MJhy07IMfEKuARQ9TKojGqLVNxQajaXEp/BoqSk=
|
github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab h1:rvv6MJhy07IMfEKuARQ9TKojGqLVNxQajaXEp/BoqSk=
|
||||||
github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab/go.mod h1:IuLm4IsPipXKF7CW5Lzf68PIbZ5yl7FFd74l/E0o9A8=
|
github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab/go.mod h1:IuLm4IsPipXKF7CW5Lzf68PIbZ5yl7FFd74l/E0o9A8=
|
||||||
github.com/ethereum/go-ethereum v1.17.0 h1:2D+1Fe23CwZ5tQoAS5DfwKFNI1HGcTwi65/kRlAVxes=
|
github.com/ethereum/go-ethereum v1.17.0 h1:2D+1Fe23CwZ5tQoAS5DfwKFNI1HGcTwi65/kRlAVxes=
|
||||||
@@ -211,8 +211,8 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
|||||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/nats-io/nats.go v1.48.0 h1:pSFyXApG+yWU/TgbKCjmm5K4wrHu86231/w84qRVR+U=
|
github.com/nats-io/nats.go v1.49.0 h1:yh/WvY59gXqYpgl33ZI+XoVPKyut/IcEaqtsiuTJpoE=
|
||||||
github.com/nats-io/nats.go v1.48.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
github.com/nats-io/nats.go v1.49.0/go.mod h1:fDCn3mN5cY8HooHwE2ukiLb4p4G4ImmzvXyJt+tGwdw=
|
||||||
github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
|
github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
|
||||||
github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
|
github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
|
||||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||||
@@ -245,8 +245,8 @@ github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNw
|
|||||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||||
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
||||||
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
||||||
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
github.com/prometheus/procfs v0.20.0 h1:AA7aCvjxwAquZAlonN7888f2u4IN8WVeFgBi4k82M4Q=
|
||||||
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
github.com/prometheus/procfs v0.20.0/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo=
|
||||||
github.com/rjeczalik/notify v0.9.3 h1:6rJAzHTGKXGj76sbRgDiDcYj/HniypXmSJo1SWakZeY=
|
github.com/rjeczalik/notify v0.9.3 h1:6rJAzHTGKXGj76sbRgDiDcYj/HniypXmSJo1SWakZeY=
|
||||||
github.com/rjeczalik/notify v0.9.3/go.mod h1:gF3zSOrafR9DQEWSE8TjfI9NkooDxbyT4UgRGKZA0lc=
|
github.com/rjeczalik/notify v0.9.3/go.mod h1:gF3zSOrafR9DQEWSE8TjfI9NkooDxbyT4UgRGKZA0lc=
|
||||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||||
@@ -339,8 +339,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
@@ -374,10 +374,10 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
|
|||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20260217215200-42d3e9bedb6d h1:EocjzKLywydp5uZ5tJ79iP6Q0UjDnyiHkGRWxuPBP8s=
|
google.golang.org/genproto/googleapis/api v0.0.0-20260226221140-a57be14db171 h1:tu/dtnW1o3wfaxCOjSLn5IRX4YDcJrtlpzYkhHhGaC4=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20260217215200-42d3e9bedb6d/go.mod h1:48U2I+QQUYhsFrg2SY6r+nJzeOtjey7j//WBESw+qyQ=
|
google.golang.org/genproto/googleapis/api v0.0.0-20260226221140-a57be14db171/go.mod h1:M5krXqk4GhBKvB596udGL3UyjL4I1+cTbK0orROM9ng=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d h1:t/LOSXPJ9R0B6fnZNyALBRfZBH0Uy0gT+uR+SJ6syqQ=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
|
|||||||
@@ -229,8 +229,8 @@ func (s *Service) startDiscoveryAnnouncers() {
|
|||||||
announce := discovery.Announcement{
|
announce := discovery.Announcement{
|
||||||
ID: discovery.StableCryptoRailGatewayID(network.Name.String()),
|
ID: discovery.StableCryptoRailGatewayID(network.Name.String()),
|
||||||
Service: "CRYPTO_RAIL_GATEWAY",
|
Service: "CRYPTO_RAIL_GATEWAY",
|
||||||
Rail: "CRYPTO",
|
Rail: discovery.RailCrypto,
|
||||||
Operations: []string{"balance.read", "payin.crypto", "payout.crypto", "fee.send", "observe.confirm"},
|
Operations: discovery.CryptoRailGatewayOperations(),
|
||||||
Currencies: currencies,
|
Currencies: currencies,
|
||||||
InvokeURI: s.invokeURI,
|
InvokeURI: s.invokeURI,
|
||||||
Version: version,
|
Version: version,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"github.com/tech/sendico/pkg/ledgerconv"
|
"github.com/tech/sendico/pkg/ledgerconv"
|
||||||
"github.com/tech/sendico/pkg/merrors"
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
"github.com/tech/sendico/pkg/model/account_role"
|
"github.com/tech/sendico/pkg/model/account_role"
|
||||||
@@ -23,7 +24,36 @@ import (
|
|||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
)
|
)
|
||||||
|
|
||||||
const ledgerConnectorID = "ledger"
|
const (
|
||||||
|
ledgerConnectorID = "ledger"
|
||||||
|
ledgerRailName = "LEDGER"
|
||||||
|
|
||||||
|
opParamOperation = "operation"
|
||||||
|
opParamToMoney = "to_money"
|
||||||
|
opParamAmount = "amount"
|
||||||
|
opParamCurrency = "currency"
|
||||||
|
opParamOrganizationRef = "organization_ref"
|
||||||
|
opParamAccountType = "account_type"
|
||||||
|
opParamStatus = "status"
|
||||||
|
opParamAllowNegative = "allow_negative"
|
||||||
|
opParamRole = "role"
|
||||||
|
opParamDescription = "description"
|
||||||
|
opParamMetadata = "metadata"
|
||||||
|
opParamCharges = "charges"
|
||||||
|
opParamEventTime = "event_time"
|
||||||
|
opParamContraLedgerAccountRef = "contra_ledger_account_ref"
|
||||||
|
opParamLedgerAccountRef = "ledger_account_ref"
|
||||||
|
opParamLineType = "line_type"
|
||||||
|
opParamAccountCode = "account_code"
|
||||||
|
opParamIsSettlement = "is_settlement"
|
||||||
|
|
||||||
|
txMetaPaymentPlanID = "payment_plan_id"
|
||||||
|
txMetaFromRail = "from_rail"
|
||||||
|
txMetaToRail = "to_rail"
|
||||||
|
txMetaExternalReference = "external_reference_id"
|
||||||
|
txMetaFXRateUsed = "fx_rate_used"
|
||||||
|
txMetaFeeAmount = "fee_amount"
|
||||||
|
)
|
||||||
|
|
||||||
// Client exposes typed helpers around the ledger gRPC API.
|
// Client exposes typed helpers around the ledger gRPC API.
|
||||||
type Client interface {
|
type Client interface {
|
||||||
@@ -36,6 +66,8 @@ type Client interface {
|
|||||||
ListConnectorAccounts(ctx context.Context, req *connectorv1.ListAccountsRequest) (*connectorv1.ListAccountsResponse, error)
|
ListConnectorAccounts(ctx context.Context, req *connectorv1.ListAccountsRequest) (*connectorv1.ListAccountsResponse, error)
|
||||||
PostCreditWithCharges(ctx context.Context, req *ledgerv1.PostCreditRequest) (*ledgerv1.PostResponse, error)
|
PostCreditWithCharges(ctx context.Context, req *ledgerv1.PostCreditRequest) (*ledgerv1.PostResponse, error)
|
||||||
PostDebitWithCharges(ctx context.Context, req *ledgerv1.PostDebitRequest) (*ledgerv1.PostResponse, error)
|
PostDebitWithCharges(ctx context.Context, req *ledgerv1.PostDebitRequest) (*ledgerv1.PostResponse, error)
|
||||||
|
PostExternalCreditWithCharges(ctx context.Context, req *ledgerv1.PostCreditRequest) (*ledgerv1.PostResponse, error)
|
||||||
|
PostExternalDebitWithCharges(ctx context.Context, req *ledgerv1.PostDebitRequest) (*ledgerv1.PostResponse, error)
|
||||||
TransferInternal(ctx context.Context, req *ledgerv1.TransferRequest) (*ledgerv1.PostResponse, error)
|
TransferInternal(ctx context.Context, req *ledgerv1.TransferRequest) (*ledgerv1.PostResponse, error)
|
||||||
ApplyFXWithCharges(ctx context.Context, req *ledgerv1.FXRequest) (*ledgerv1.PostResponse, error)
|
ApplyFXWithCharges(ctx context.Context, req *ledgerv1.FXRequest) (*ledgerv1.PostResponse, error)
|
||||||
|
|
||||||
@@ -148,7 +180,7 @@ func (c *ledgerClient) CreateTransaction(ctx context.Context, tx rail.LedgerTx)
|
|||||||
metadata := ledgerTxMetadata(tx.Metadata, tx)
|
metadata := ledgerTxMetadata(tx.Metadata, tx)
|
||||||
extraParams := map[string]interface{}{}
|
extraParams := map[string]interface{}{}
|
||||||
if op := strings.TrimSpace(tx.Operation); op != "" {
|
if op := strings.TrimSpace(tx.Operation); op != "" {
|
||||||
extraParams["operation"] = op
|
extraParams[opParamOperation] = op
|
||||||
}
|
}
|
||||||
if len(extraParams) == 0 {
|
if len(extraParams) == 0 {
|
||||||
extraParams = nil
|
extraParams = nil
|
||||||
@@ -204,13 +236,13 @@ func (c *ledgerClient) CreateAccount(ctx context.Context, req *ledgerv1.CreateAc
|
|||||||
return nil, merrors.InvalidArgument("ledger: currency is required")
|
return nil, merrors.InvalidArgument("ledger: currency is required")
|
||||||
}
|
}
|
||||||
params := map[string]interface{}{
|
params := map[string]interface{}{
|
||||||
"organization_ref": strings.TrimSpace(req.GetOrganizationRef()),
|
opParamOrganizationRef: strings.TrimSpace(req.GetOrganizationRef()),
|
||||||
"account_type": req.GetAccountType().String(),
|
opParamAccountType: req.GetAccountType().String(),
|
||||||
"status": req.GetStatus().String(),
|
opParamStatus: req.GetStatus().String(),
|
||||||
"allow_negative": req.GetAllowNegative(),
|
opParamAllowNegative: req.GetAllowNegative(),
|
||||||
}
|
}
|
||||||
if role := req.GetRole(); role != ledgerv1.AccountRole_ACCOUNT_ROLE_UNSPECIFIED {
|
if role := req.GetRole(); role != ledgerv1.AccountRole_ACCOUNT_ROLE_UNSPECIFIED {
|
||||||
params["role"] = role.String()
|
params[opParamRole] = role.String()
|
||||||
}
|
}
|
||||||
label := ""
|
label := ""
|
||||||
if desc := req.GetDescribable(); desc != nil {
|
if desc := req.GetDescribable(); desc != nil {
|
||||||
@@ -218,12 +250,12 @@ func (c *ledgerClient) CreateAccount(ctx context.Context, req *ledgerv1.CreateAc
|
|||||||
if desc.Description != nil {
|
if desc.Description != nil {
|
||||||
trimmed := strings.TrimSpace(desc.GetDescription())
|
trimmed := strings.TrimSpace(desc.GetDescription())
|
||||||
if trimmed != "" {
|
if trimmed != "" {
|
||||||
params["description"] = trimmed
|
params[opParamDescription] = trimmed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(req.GetMetadata()) > 0 {
|
if len(req.GetMetadata()) > 0 {
|
||||||
params["metadata"] = mapStringToInterface(req.GetMetadata())
|
params[opParamMetadata] = mapStringToInterface(req.GetMetadata())
|
||||||
}
|
}
|
||||||
resp, err := c.client.OpenAccount(ctx, &connectorv1.OpenAccountRequest{
|
resp, err := c.client.OpenAccount(ctx, &connectorv1.OpenAccountRequest{
|
||||||
Kind: connectorv1.AccountKind_LEDGER_ACCOUNT,
|
Kind: connectorv1.AccountKind_LEDGER_ACCOUNT,
|
||||||
@@ -277,6 +309,30 @@ func (c *ledgerClient) PostDebitWithCharges(ctx context.Context, req *ledgerv1.P
|
|||||||
return c.submitLedgerOperation(ctx, connectorv1.OperationType_DEBIT, req.GetLedgerAccountRef(), "", req.GetMoney(), req)
|
return c.submitLedgerOperation(ctx, connectorv1.OperationType_DEBIT, req.GetLedgerAccountRef(), "", req.GetMoney(), req)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ledgerClient) PostExternalCreditWithCharges(ctx context.Context, req *ledgerv1.PostCreditRequest) (*ledgerv1.PostResponse, error) {
|
||||||
|
return c.submitLedgerOperationWithExtras(
|
||||||
|
ctx,
|
||||||
|
connectorv1.OperationType_CREDIT,
|
||||||
|
"",
|
||||||
|
req.GetLedgerAccountRef(),
|
||||||
|
req.GetMoney(),
|
||||||
|
req,
|
||||||
|
map[string]interface{}{opParamOperation: discovery.OperationExternalCredit},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ledgerClient) PostExternalDebitWithCharges(ctx context.Context, req *ledgerv1.PostDebitRequest) (*ledgerv1.PostResponse, error) {
|
||||||
|
return c.submitLedgerOperationWithExtras(
|
||||||
|
ctx,
|
||||||
|
connectorv1.OperationType_DEBIT,
|
||||||
|
req.GetLedgerAccountRef(),
|
||||||
|
"",
|
||||||
|
req.GetMoney(),
|
||||||
|
req,
|
||||||
|
map[string]interface{}{opParamOperation: discovery.OperationExternalDebit},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *ledgerClient) TransferInternal(ctx context.Context, req *ledgerv1.TransferRequest) (*ledgerv1.PostResponse, error) {
|
func (c *ledgerClient) TransferInternal(ctx context.Context, req *ledgerv1.TransferRequest) (*ledgerv1.PostResponse, error) {
|
||||||
return c.submitLedgerOperation(ctx, connectorv1.OperationType_TRANSFER, req.GetFromLedgerAccountRef(), req.GetToLedgerAccountRef(), req.GetMoney(), req)
|
return c.submitLedgerOperation(ctx, connectorv1.OperationType_TRANSFER, req.GetFromLedgerAccountRef(), req.GetToLedgerAccountRef(), req.GetMoney(), req)
|
||||||
}
|
}
|
||||||
@@ -292,7 +348,7 @@ func (c *ledgerClient) ApplyFXWithCharges(ctx context.Context, req *ledgerv1.FXR
|
|||||||
}
|
}
|
||||||
params := ledgerOperationParams(req.GetOrganizationRef(), req.GetDescription(), req.GetMetadata(), req.GetCharges(), req.GetEventTime())
|
params := ledgerOperationParams(req.GetOrganizationRef(), req.GetDescription(), req.GetMetadata(), req.GetCharges(), req.GetEventTime())
|
||||||
params["rate"] = strings.TrimSpace(req.GetRate())
|
params["rate"] = strings.TrimSpace(req.GetRate())
|
||||||
params["to_money"] = map[string]interface{}{"amount": req.GetToMoney().GetAmount(), "currency": req.GetToMoney().GetCurrency()}
|
params[opParamToMoney] = map[string]interface{}{opParamAmount: req.GetToMoney().GetAmount(), opParamCurrency: req.GetToMoney().GetCurrency()}
|
||||||
operation := &connectorv1.Operation{
|
operation := &connectorv1.Operation{
|
||||||
Type: connectorv1.OperationType_FX,
|
Type: connectorv1.OperationType_FX,
|
||||||
IdempotencyKey: strings.TrimSpace(req.GetIdempotencyKey()),
|
IdempotencyKey: strings.TrimSpace(req.GetIdempotencyKey()),
|
||||||
@@ -466,7 +522,7 @@ func (c *ledgerClient) submitLedgerOperationWithExtras(ctx context.Context, opTy
|
|||||||
|
|
||||||
params := ledgerOperationParams(orgRef, description, metadata, charges, eventTime)
|
params := ledgerOperationParams(orgRef, description, metadata, charges, eventTime)
|
||||||
if contraRef != "" {
|
if contraRef != "" {
|
||||||
params["contra_ledger_account_ref"] = strings.TrimSpace(contraRef)
|
params[opParamContraLedgerAccountRef] = strings.TrimSpace(contraRef)
|
||||||
}
|
}
|
||||||
if len(extraParams) > 0 {
|
if len(extraParams) > 0 {
|
||||||
for key, value := range extraParams {
|
for key, value := range extraParams {
|
||||||
@@ -534,17 +590,17 @@ func accountRoleFromLedgerProto(role ledgerv1.AccountRole) account_role.AccountR
|
|||||||
|
|
||||||
func ledgerOperationParams(orgRef, description string, metadata map[string]string, charges []*ledgerv1.PostingLine, eventTime *timestamppb.Timestamp) map[string]interface{} {
|
func ledgerOperationParams(orgRef, description string, metadata map[string]string, charges []*ledgerv1.PostingLine, eventTime *timestamppb.Timestamp) map[string]interface{} {
|
||||||
params := map[string]interface{}{
|
params := map[string]interface{}{
|
||||||
"organization_ref": strings.TrimSpace(orgRef),
|
opParamOrganizationRef: strings.TrimSpace(orgRef),
|
||||||
"description": strings.TrimSpace(description),
|
opParamDescription: strings.TrimSpace(description),
|
||||||
}
|
}
|
||||||
if len(metadata) > 0 {
|
if len(metadata) > 0 {
|
||||||
params["metadata"] = mapStringToInterface(metadata)
|
params[opParamMetadata] = mapStringToInterface(metadata)
|
||||||
}
|
}
|
||||||
if len(charges) > 0 {
|
if len(charges) > 0 {
|
||||||
params["charges"] = chargesToInterface(charges)
|
params[opParamCharges] = chargesToInterface(charges)
|
||||||
}
|
}
|
||||||
if eventTime != nil {
|
if eventTime != nil {
|
||||||
params["event_time"] = eventTime.AsTime().UTC().Format(time.RFC3339Nano)
|
params[opParamEventTime] = eventTime.AsTime().UTC().Format(time.RFC3339Nano)
|
||||||
}
|
}
|
||||||
return params
|
return params
|
||||||
}
|
}
|
||||||
@@ -580,25 +636,25 @@ func ledgerAccountFromConnector(account *connectorv1.Account) *ledgerv1.LedgerAc
|
|||||||
details = account.GetProviderDetails().AsMap()
|
details = account.GetProviderDetails().AsMap()
|
||||||
}
|
}
|
||||||
accountType := ledgerv1.AccountType_ACCOUNT_TYPE_UNSPECIFIED
|
accountType := ledgerv1.AccountType_ACCOUNT_TYPE_UNSPECIFIED
|
||||||
if v := strings.TrimSpace(fmt.Sprint(details["account_type"])); v != "" {
|
if v := strings.TrimSpace(fmt.Sprint(details[opParamAccountType])); v != "" {
|
||||||
accountType = parseAccountType(v)
|
accountType = parseAccountType(v)
|
||||||
}
|
}
|
||||||
status := ledgerv1.AccountStatus_ACCOUNT_STATUS_UNSPECIFIED
|
status := ledgerv1.AccountStatus_ACCOUNT_STATUS_UNSPECIFIED
|
||||||
if v := strings.TrimSpace(fmt.Sprint(details["status"])); v != "" {
|
if v := strings.TrimSpace(fmt.Sprint(details[opParamStatus])); v != "" {
|
||||||
status = parseAccountStatus(v)
|
status = parseAccountStatus(v)
|
||||||
}
|
}
|
||||||
allowNegative := false
|
allowNegative := false
|
||||||
if v, ok := details["allow_negative"].(bool); ok {
|
if v, ok := details[opParamAllowNegative].(bool); ok {
|
||||||
allowNegative = v
|
allowNegative = v
|
||||||
}
|
}
|
||||||
role := ledgerv1.AccountRole_ACCOUNT_ROLE_UNSPECIFIED
|
role := ledgerv1.AccountRole_ACCOUNT_ROLE_UNSPECIFIED
|
||||||
if v := strings.TrimSpace(fmt.Sprint(details["role"])); v != "" {
|
if v := strings.TrimSpace(fmt.Sprint(details[opParamRole])); v != "" {
|
||||||
if parsed, ok := ledgerconv.ParseAccountRole(v); ok {
|
if parsed, ok := ledgerconv.ParseAccountRole(v); ok {
|
||||||
role = parsed
|
role = parsed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if role == ledgerv1.AccountRole_ACCOUNT_ROLE_UNSPECIFIED {
|
if role == ledgerv1.AccountRole_ACCOUNT_ROLE_UNSPECIFIED {
|
||||||
switch v := details["is_settlement"].(type) {
|
switch v := details[opParamIsSettlement].(type) {
|
||||||
case bool:
|
case bool:
|
||||||
if v {
|
if v {
|
||||||
role = ledgerv1.AccountRole_ACCOUNT_ROLE_SETTLEMENT
|
role = ledgerv1.AccountRole_ACCOUNT_ROLE_SETTLEMENT
|
||||||
@@ -609,13 +665,13 @@ func ledgerAccountFromConnector(account *connectorv1.Account) *ledgerv1.LedgerAc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
accountCode := strings.TrimSpace(fmt.Sprint(details["account_code"]))
|
accountCode := strings.TrimSpace(fmt.Sprint(details[opParamAccountCode]))
|
||||||
accountID := ""
|
accountID := ""
|
||||||
if ref := account.GetRef(); ref != nil {
|
if ref := account.GetRef(); ref != nil {
|
||||||
accountID = strings.TrimSpace(ref.GetAccountId())
|
accountID = strings.TrimSpace(ref.GetAccountId())
|
||||||
}
|
}
|
||||||
organizationRef := strings.TrimSpace(account.GetOwnerRef())
|
organizationRef := strings.TrimSpace(account.GetOwnerRef())
|
||||||
if v := strings.TrimSpace(fmt.Sprint(details["organization_ref"])); v != "" {
|
if v := strings.TrimSpace(fmt.Sprint(details[opParamOrganizationRef])); v != "" {
|
||||||
organizationRef = v
|
organizationRef = v
|
||||||
}
|
}
|
||||||
describable := account.GetDescribable()
|
describable := account.GetDescribable()
|
||||||
@@ -674,7 +730,7 @@ func operationDescription(op *connectorv1.Operation) string {
|
|||||||
if op == nil || op.GetParams() == nil {
|
if op == nil || op.GetParams() == nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
if value, ok := op.GetParams().AsMap()["description"]; ok {
|
if value, ok := op.GetParams().AsMap()[opParamDescription]; ok {
|
||||||
return strings.TrimSpace(fmt.Sprint(value))
|
return strings.TrimSpace(fmt.Sprint(value))
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
@@ -731,10 +787,10 @@ func chargesToInterface(charges []*ledgerv1.PostingLine) []interface{} {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
result = append(result, map[string]interface{}{
|
result = append(result, map[string]interface{}{
|
||||||
"ledger_account_ref": strings.TrimSpace(line.GetLedgerAccountRef()),
|
opParamLedgerAccountRef: strings.TrimSpace(line.GetLedgerAccountRef()),
|
||||||
"amount": strings.TrimSpace(line.GetMoney().GetAmount()),
|
opParamAmount: strings.TrimSpace(line.GetMoney().GetAmount()),
|
||||||
"currency": strings.TrimSpace(line.GetMoney().GetCurrency()),
|
opParamCurrency: strings.TrimSpace(line.GetMoney().GetCurrency()),
|
||||||
"line_type": line.GetLineType().String(),
|
opParamLineType: line.GetLineType().String(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if len(result) == 0 {
|
if len(result) == 0 {
|
||||||
@@ -793,7 +849,7 @@ func (c *ledgerClient) callContext(ctx context.Context) (context.Context, contex
|
|||||||
}
|
}
|
||||||
|
|
||||||
func isLedgerRail(value string) bool {
|
func isLedgerRail(value string) bool {
|
||||||
return strings.EqualFold(strings.TrimSpace(value), "LEDGER")
|
return strings.EqualFold(strings.TrimSpace(value), ledgerRailName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func cloneMoney(input *moneyv1.Money) *moneyv1.Money {
|
func cloneMoney(input *moneyv1.Money) *moneyv1.Money {
|
||||||
@@ -823,22 +879,22 @@ func ledgerTxMetadata(base map[string]string, tx rail.LedgerTx) map[string]strin
|
|||||||
meta = map[string]string{}
|
meta = map[string]string{}
|
||||||
}
|
}
|
||||||
if val := strings.TrimSpace(tx.PaymentPlanID); val != "" {
|
if val := strings.TrimSpace(tx.PaymentPlanID); val != "" {
|
||||||
meta["payment_plan_id"] = val
|
meta[txMetaPaymentPlanID] = val
|
||||||
}
|
}
|
||||||
if val := strings.TrimSpace(tx.FromRail); val != "" {
|
if val := strings.TrimSpace(tx.FromRail); val != "" {
|
||||||
meta["from_rail"] = val
|
meta[txMetaFromRail] = val
|
||||||
}
|
}
|
||||||
if val := strings.TrimSpace(tx.ToRail); val != "" {
|
if val := strings.TrimSpace(tx.ToRail); val != "" {
|
||||||
meta["to_rail"] = val
|
meta[txMetaToRail] = val
|
||||||
}
|
}
|
||||||
if val := strings.TrimSpace(tx.ExternalReferenceID); val != "" {
|
if val := strings.TrimSpace(tx.ExternalReferenceID); val != "" {
|
||||||
meta["external_reference_id"] = val
|
meta[txMetaExternalReference] = val
|
||||||
}
|
}
|
||||||
if val := strings.TrimSpace(tx.FXRateUsed); val != "" {
|
if val := strings.TrimSpace(tx.FXRateUsed); val != "" {
|
||||||
meta["fx_rate_used"] = val
|
meta[txMetaFXRateUsed] = val
|
||||||
}
|
}
|
||||||
if val := strings.TrimSpace(tx.FeeAmount); val != "" {
|
if val := strings.TrimSpace(tx.FeeAmount); val != "" {
|
||||||
meta["fee_amount"] = val
|
meta[txMetaFeeAmount] = val
|
||||||
}
|
}
|
||||||
if len(meta) == 0 {
|
if len(meta) == 0 {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
|
accountrolev1 "github.com/tech/sendico/pkg/proto/common/account_role/v1"
|
||||||
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
|
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
|
||||||
connectorv1 "github.com/tech/sendico/pkg/proto/connector/v1"
|
connectorv1 "github.com/tech/sendico/pkg/proto/connector/v1"
|
||||||
ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
|
ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
|
||||||
@@ -92,3 +94,65 @@ func TestTransferInternal_SubmitsTransferOperation(t *testing.T) {
|
|||||||
assert.Equal(t, "op-1", resp.GetJournalEntryRef())
|
assert.Equal(t, "op-1", resp.GetJournalEntryRef())
|
||||||
assert.Equal(t, ledgerv1.EntryType_ENTRY_TRANSFER, resp.GetEntryType())
|
assert.Equal(t, ledgerv1.EntryType_ENTRY_TRANSFER, resp.GetEntryType())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPostExternalCreditWithCharges_SubmitsExternalOperation(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
var captured *connectorv1.Operation
|
||||||
|
stub := &stubConnector{
|
||||||
|
submitFn: func(ctx context.Context, req *connectorv1.SubmitOperationRequest) (*connectorv1.SubmitOperationResponse, error) {
|
||||||
|
captured = req.GetOperation()
|
||||||
|
return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{OperationId: "op-ext-credit"}}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
client := NewWithClient(Config{}, stub)
|
||||||
|
resp, err := client.PostExternalCreditWithCharges(ctx, &ledgerv1.PostCreditRequest{
|
||||||
|
IdempotencyKey: "id-ext-credit",
|
||||||
|
OrganizationRef: "org-1",
|
||||||
|
Money: &moneyv1.Money{Currency: "USDT", Amount: "1.0"},
|
||||||
|
Role: ledgerv1.AccountRole_ACCOUNT_ROLE_OPERATING,
|
||||||
|
})
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, resp)
|
||||||
|
require.NotNil(t, captured)
|
||||||
|
|
||||||
|
assert.Equal(t, connectorv1.OperationType_CREDIT, captured.GetType())
|
||||||
|
assert.Equal(t, "", captured.GetTo().GetAccount().GetAccountId())
|
||||||
|
assert.Equal(t, accountrolev1.AccountRole_OPERATING, captured.GetToRole())
|
||||||
|
assert.Equal(t, discovery.OperationExternalCredit, captured.GetParams().AsMap()["operation"])
|
||||||
|
assert.Equal(t, "op-ext-credit", resp.GetJournalEntryRef())
|
||||||
|
assert.Equal(t, ledgerv1.EntryType_ENTRY_CREDIT, resp.GetEntryType())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPostExternalDebitWithCharges_SubmitsExternalOperation(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
var captured *connectorv1.Operation
|
||||||
|
stub := &stubConnector{
|
||||||
|
submitFn: func(ctx context.Context, req *connectorv1.SubmitOperationRequest) (*connectorv1.SubmitOperationResponse, error) {
|
||||||
|
captured = req.GetOperation()
|
||||||
|
return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{OperationId: "op-ext-debit"}}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
client := NewWithClient(Config{}, stub)
|
||||||
|
resp, err := client.PostExternalDebitWithCharges(ctx, &ledgerv1.PostDebitRequest{
|
||||||
|
IdempotencyKey: "id-ext-debit",
|
||||||
|
OrganizationRef: "org-1",
|
||||||
|
Money: &moneyv1.Money{Currency: "RUB", Amount: "77.14"},
|
||||||
|
Role: ledgerv1.AccountRole_ACCOUNT_ROLE_HOLD,
|
||||||
|
})
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, resp)
|
||||||
|
require.NotNil(t, captured)
|
||||||
|
|
||||||
|
assert.Equal(t, connectorv1.OperationType_DEBIT, captured.GetType())
|
||||||
|
assert.Equal(t, "", captured.GetFrom().GetAccount().GetAccountId())
|
||||||
|
assert.Equal(t, accountrolev1.AccountRole_HOLD, captured.GetFromRole())
|
||||||
|
assert.Equal(t, discovery.OperationExternalDebit, captured.GetParams().AsMap()["operation"])
|
||||||
|
assert.Equal(t, "op-ext-debit", resp.GetJournalEntryRef())
|
||||||
|
assert.Equal(t, ledgerv1.EntryType_ENTRY_DEBIT, resp.GetEntryType())
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/tech/sendico/pkg/payments/rail"
|
"github.com/tech/sendico/pkg/payments/rail"
|
||||||
connectorv1 "github.com/tech/sendico/pkg/proto/connector/v1"
|
|
||||||
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
|
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
|
||||||
|
connectorv1 "github.com/tech/sendico/pkg/proto/connector/v1"
|
||||||
ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
|
ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -19,6 +19,8 @@ type Fake struct {
|
|||||||
ListConnectorAccountsFn func(ctx context.Context, req *connectorv1.ListAccountsRequest) (*connectorv1.ListAccountsResponse, error)
|
ListConnectorAccountsFn func(ctx context.Context, req *connectorv1.ListAccountsRequest) (*connectorv1.ListAccountsResponse, error)
|
||||||
PostCreditWithChargesFn func(ctx context.Context, req *ledgerv1.PostCreditRequest) (*ledgerv1.PostResponse, error)
|
PostCreditWithChargesFn func(ctx context.Context, req *ledgerv1.PostCreditRequest) (*ledgerv1.PostResponse, error)
|
||||||
PostDebitWithChargesFn func(ctx context.Context, req *ledgerv1.PostDebitRequest) (*ledgerv1.PostResponse, error)
|
PostDebitWithChargesFn func(ctx context.Context, req *ledgerv1.PostDebitRequest) (*ledgerv1.PostResponse, error)
|
||||||
|
PostExternalCreditWithChargesFn func(ctx context.Context, req *ledgerv1.PostCreditRequest) (*ledgerv1.PostResponse, error)
|
||||||
|
PostExternalDebitWithChargesFn func(ctx context.Context, req *ledgerv1.PostDebitRequest) (*ledgerv1.PostResponse, error)
|
||||||
TransferInternalFn func(ctx context.Context, req *ledgerv1.TransferRequest) (*ledgerv1.PostResponse, error)
|
TransferInternalFn func(ctx context.Context, req *ledgerv1.TransferRequest) (*ledgerv1.PostResponse, error)
|
||||||
ApplyFXWithChargesFn func(ctx context.Context, req *ledgerv1.FXRequest) (*ledgerv1.PostResponse, error)
|
ApplyFXWithChargesFn func(ctx context.Context, req *ledgerv1.FXRequest) (*ledgerv1.PostResponse, error)
|
||||||
BlockAccountFn func(ctx context.Context, req *ledgerv1.BlockAccountRequest) (*ledgerv1.BlockAccountResponse, error)
|
BlockAccountFn func(ctx context.Context, req *ledgerv1.BlockAccountRequest) (*ledgerv1.BlockAccountResponse, error)
|
||||||
@@ -85,6 +87,20 @@ func (f *Fake) PostDebitWithCharges(ctx context.Context, req *ledgerv1.PostDebit
|
|||||||
return &ledgerv1.PostResponse{}, nil
|
return &ledgerv1.PostResponse{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *Fake) PostExternalCreditWithCharges(ctx context.Context, req *ledgerv1.PostCreditRequest) (*ledgerv1.PostResponse, error) {
|
||||||
|
if f.PostExternalCreditWithChargesFn != nil {
|
||||||
|
return f.PostExternalCreditWithChargesFn(ctx, req)
|
||||||
|
}
|
||||||
|
return &ledgerv1.PostResponse{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Fake) PostExternalDebitWithCharges(ctx context.Context, req *ledgerv1.PostDebitRequest) (*ledgerv1.PostResponse, error) {
|
||||||
|
if f.PostExternalDebitWithChargesFn != nil {
|
||||||
|
return f.PostExternalDebitWithChargesFn(ctx, req)
|
||||||
|
}
|
||||||
|
return &ledgerv1.PostResponse{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (f *Fake) TransferInternal(ctx context.Context, req *ledgerv1.TransferRequest) (*ledgerv1.PostResponse, error) {
|
func (f *Fake) TransferInternal(ctx context.Context, req *ledgerv1.TransferRequest) (*ledgerv1.PostResponse, error) {
|
||||||
if f.TransferInternalFn != nil {
|
if f.TransferInternalFn != nil {
|
||||||
return f.TransferInternalFn(ctx, req)
|
return f.TransferInternalFn(ctx, req)
|
||||||
|
|||||||
@@ -31,13 +31,13 @@ require (
|
|||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/nats-io/nats.go v1.48.0 // indirect
|
github.com/nats-io/nats.go v1.49.0 // indirect
|
||||||
github.com/nats-io/nkeys v0.4.15 // indirect
|
github.com/nats-io/nkeys v0.4.15 // indirect
|
||||||
github.com/nats-io/nuid v1.0.1 // indirect
|
github.com/nats-io/nuid v1.0.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/prometheus/client_model v0.6.2 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
github.com/prometheus/common v0.67.5 // indirect
|
github.com/prometheus/common v0.67.5 // indirect
|
||||||
github.com/prometheus/procfs v0.19.2 // indirect
|
github.com/prometheus/procfs v0.20.0 // indirect
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
github.com/xdg-go/scram v1.2.0 // indirect
|
github.com/xdg-go/scram v1.2.0 // indirect
|
||||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||||
@@ -45,9 +45,9 @@ require (
|
|||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||||
golang.org/x/crypto v0.48.0 // indirect
|
golang.org/x/crypto v0.48.0 // indirect
|
||||||
golang.org/x/net v0.50.0 // indirect
|
golang.org/x/net v0.51.0 // indirect
|
||||||
golang.org/x/sync v0.19.0 // indirect
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.41.0 // indirect
|
golang.org/x/sys v0.41.0 // indirect
|
||||||
golang.org/x/text v0.34.0 // indirect
|
golang.org/x/text v0.34.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -91,8 +91,8 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
|||||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/nats-io/nats.go v1.48.0 h1:pSFyXApG+yWU/TgbKCjmm5K4wrHu86231/w84qRVR+U=
|
github.com/nats-io/nats.go v1.49.0 h1:yh/WvY59gXqYpgl33ZI+XoVPKyut/IcEaqtsiuTJpoE=
|
||||||
github.com/nats-io/nats.go v1.48.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
github.com/nats-io/nats.go v1.49.0/go.mod h1:fDCn3mN5cY8HooHwE2ukiLb4p4G4ImmzvXyJt+tGwdw=
|
||||||
github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
|
github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
|
||||||
github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
|
github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
|
||||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||||
@@ -113,8 +113,8 @@ github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNw
|
|||||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||||
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
||||||
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
||||||
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
github.com/prometheus/procfs v0.20.0 h1:AA7aCvjxwAquZAlonN7888f2u4IN8WVeFgBi4k82M4Q=
|
||||||
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
github.com/prometheus/procfs v0.20.0/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo=
|
||||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||||
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
|
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
|
||||||
@@ -181,8 +181,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
@@ -210,8 +210,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
|
|||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d h1:t/LOSXPJ9R0B6fnZNyALBRfZBH0Uy0gT+uR+SJ6syqQ=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"github.com/tech/sendico/ledger/internal/appversion"
|
"github.com/tech/sendico/ledger/internal/appversion"
|
||||||
"github.com/tech/sendico/pkg/connector/params"
|
"github.com/tech/sendico/pkg/connector/params"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"github.com/tech/sendico/pkg/ledgerconv"
|
"github.com/tech/sendico/pkg/ledgerconv"
|
||||||
"github.com/tech/sendico/pkg/merrors"
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
accountrolev1 "github.com/tech/sendico/pkg/proto/common/account_role/v1"
|
accountrolev1 "github.com/tech/sendico/pkg/proto/common/account_role/v1"
|
||||||
@@ -16,6 +17,7 @@ import (
|
|||||||
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
|
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
|
||||||
connectorv1 "github.com/tech/sendico/pkg/proto/connector/v1"
|
connectorv1 "github.com/tech/sendico/pkg/proto/connector/v1"
|
||||||
ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
|
ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
|
||||||
|
"go.uber.org/zap"
|
||||||
"google.golang.org/protobuf/types/known/structpb"
|
"google.golang.org/protobuf/types/known/structpb"
|
||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
)
|
)
|
||||||
@@ -222,7 +224,7 @@ func (c *connectorAdapter) SubmitOperation(ctx context.Context, req *connectorv1
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(connectorv1.ErrorCode_INVALID_PARAMS, err.Error(), op, "")}}, nil
|
return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(connectorv1.ErrorCode_INVALID_PARAMS, err.Error(), op, "")}}, nil
|
||||||
}
|
}
|
||||||
operation := strings.ToLower(strings.TrimSpace(reader.String("operation")))
|
operation := discovery.NormalizeOperation(reader.String("operation"))
|
||||||
|
|
||||||
switch op.GetType() {
|
switch op.GetType() {
|
||||||
case connectorv1.OperationType_CREDIT:
|
case connectorv1.OperationType_CREDIT:
|
||||||
@@ -230,11 +232,11 @@ func (c *connectorAdapter) SubmitOperation(ctx context.Context, req *connectorv1
|
|||||||
if accountID == "" && op.GetToRole() == accountrolev1.AccountRole_ACCOUNT_ROLE_UNSPECIFIED {
|
if accountID == "" && op.GetToRole() == accountrolev1.AccountRole_ACCOUNT_ROLE_UNSPECIFIED {
|
||||||
return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(connectorv1.ErrorCode_INVALID_PARAMS, "credit: to.account or to_role is required", op, "")}}, nil
|
return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(connectorv1.ErrorCode_INVALID_PARAMS, "credit: to.account or to_role is required", op, "")}}, nil
|
||||||
}
|
}
|
||||||
if operation != "" && operation != "external.credit" {
|
if operation != "" && operation != discovery.OperationExternalCredit {
|
||||||
return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(connectorv1.ErrorCode_INVALID_PARAMS, "credit: unsupported operation override", op, "")}}, nil
|
return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(connectorv1.ErrorCode_INVALID_PARAMS, "credit: unsupported operation override", op, "")}}, nil
|
||||||
}
|
}
|
||||||
creditFn := c.svc.PostCreditWithCharges
|
creditFn := c.svc.PostCreditWithCharges
|
||||||
if operation == "external.credit" {
|
if operation == discovery.OperationExternalCredit {
|
||||||
creditFn = c.svc.PostExternalCreditWithCharges
|
creditFn = c.svc.PostExternalCreditWithCharges
|
||||||
}
|
}
|
||||||
resp, err := creditFn(ctx, &ledgerv1.PostCreditRequest{
|
resp, err := creditFn(ctx, &ledgerv1.PostCreditRequest{
|
||||||
@@ -250,6 +252,10 @@ func (c *connectorAdapter) SubmitOperation(ctx context.Context, req *connectorv1
|
|||||||
Role: accountRoleFromConnectorRole(op.GetToRole()),
|
Role: accountRoleFromConnectorRole(op.GetToRole()),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
c.svc.logger.Warn("Operation failed", zap.Error(err), zap.String("operation", operation),
|
||||||
|
zap.String("idempotency_key", op.IdempotencyKey), zap.String("description", description),
|
||||||
|
zap.String("organization_ref", orgRef), zap.String("ledger_account_ref", accountID),
|
||||||
|
)
|
||||||
return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(mapErrorCode(err), err.Error(), op, accountID)}}, nil
|
return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(mapErrorCode(err), err.Error(), op, accountID)}}, nil
|
||||||
}
|
}
|
||||||
return &connectorv1.SubmitOperationResponse{Receipt: ledgerReceipt(resp.GetJournalEntryRef(), connectorv1.OperationStatus_OPERATION_SUCCESS)}, nil
|
return &connectorv1.SubmitOperationResponse{Receipt: ledgerReceipt(resp.GetJournalEntryRef(), connectorv1.OperationStatus_OPERATION_SUCCESS)}, nil
|
||||||
@@ -258,11 +264,11 @@ func (c *connectorAdapter) SubmitOperation(ctx context.Context, req *connectorv1
|
|||||||
if accountID == "" && op.GetFromRole() == accountrolev1.AccountRole_ACCOUNT_ROLE_UNSPECIFIED {
|
if accountID == "" && op.GetFromRole() == accountrolev1.AccountRole_ACCOUNT_ROLE_UNSPECIFIED {
|
||||||
return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(connectorv1.ErrorCode_INVALID_PARAMS, "debit: from.account or from_role is required", op, "")}}, nil
|
return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(connectorv1.ErrorCode_INVALID_PARAMS, "debit: from.account or from_role is required", op, "")}}, nil
|
||||||
}
|
}
|
||||||
if operation != "" && operation != "external.debit" {
|
if operation != "" && operation != discovery.OperationExternalDebit {
|
||||||
return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(connectorv1.ErrorCode_INVALID_PARAMS, "debit: unsupported operation override", op, "")}}, nil
|
return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(connectorv1.ErrorCode_INVALID_PARAMS, "debit: unsupported operation override", op, "")}}, nil
|
||||||
}
|
}
|
||||||
debitFn := c.svc.PostDebitWithCharges
|
debitFn := c.svc.PostDebitWithCharges
|
||||||
if operation == "external.debit" {
|
if operation == discovery.OperationExternalDebit {
|
||||||
debitFn = c.svc.PostExternalDebitWithCharges
|
debitFn = c.svc.PostExternalDebitWithCharges
|
||||||
}
|
}
|
||||||
resp, err := debitFn(ctx, &ledgerv1.PostDebitRequest{
|
resp, err := debitFn(ctx, &ledgerv1.PostDebitRequest{
|
||||||
@@ -393,14 +399,14 @@ func ledgerOperationParams() []*connectorv1.OperationParamSpec {
|
|||||||
Type: connectorv1.ParamType_STRING,
|
Type: connectorv1.ParamType_STRING,
|
||||||
Required: false,
|
Required: false,
|
||||||
Description: "Optional ledger operation override (external.credit).",
|
Description: "Optional ledger operation override (external.credit).",
|
||||||
AllowedValues: []string{"external.credit"},
|
AllowedValues: []string{discovery.OperationExternalCredit},
|
||||||
}
|
}
|
||||||
externalDebit := &connectorv1.ParamSpec{
|
externalDebit := &connectorv1.ParamSpec{
|
||||||
Key: "operation",
|
Key: "operation",
|
||||||
Type: connectorv1.ParamType_STRING,
|
Type: connectorv1.ParamType_STRING,
|
||||||
Required: false,
|
Required: false,
|
||||||
Description: "Optional ledger operation override (external.debit).",
|
Description: "Optional ledger operation override (external.debit).",
|
||||||
AllowedValues: []string{"external.debit"},
|
AllowedValues: []string{discovery.OperationExternalDebit},
|
||||||
}
|
}
|
||||||
return []*connectorv1.OperationParamSpec{
|
return []*connectorv1.OperationParamSpec{
|
||||||
{OperationType: connectorv1.OperationType_CREDIT, Params: append(common, externalCredit, &connectorv1.ParamSpec{Key: "contra_ledger_account_ref", Type: connectorv1.ParamType_STRING, Required: false})},
|
{OperationType: connectorv1.OperationType_CREDIT, Params: append(common, externalCredit, &connectorv1.ParamSpec{Key: "contra_ledger_account_ref", Type: connectorv1.ParamType_STRING, Required: false})},
|
||||||
|
|||||||
@@ -7,6 +7,40 @@ import (
|
|||||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type journalEntryType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
journalEntryTypeCredit journalEntryType = "credit"
|
||||||
|
journalEntryTypeDebit journalEntryType = "debit"
|
||||||
|
journalEntryTypeTransfer journalEntryType = "transfer"
|
||||||
|
journalEntryTypeFX journalEntryType = "fx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type journalEntryStatus string
|
||||||
|
|
||||||
|
const (
|
||||||
|
journalEntryStatusAttempted journalEntryStatus = "attempted"
|
||||||
|
journalEntryStatusSuccess journalEntryStatus = "success"
|
||||||
|
journalEntryStatusError journalEntryStatus = "error"
|
||||||
|
)
|
||||||
|
|
||||||
|
type journalEntryErrorType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
journalEntryErrorNotImplemented journalEntryErrorType = "not_implemented"
|
||||||
|
journalEntryErrorFailed journalEntryErrorType = "failed"
|
||||||
|
journalEntryErrorIdempotencyCheck journalEntryErrorType = "idempotency_check_failed"
|
||||||
|
journalEntryErrorAccountResolve journalEntryErrorType = "account_resolve_failed"
|
||||||
|
journalEntryErrorAccountInvalid journalEntryErrorType = "account_invalid"
|
||||||
|
journalEntryErrorContraResolve journalEntryErrorType = "contra_resolve_failed"
|
||||||
|
journalEntryErrorContraMissingID journalEntryErrorType = "contra_missing_id"
|
||||||
|
journalEntryErrorSystemAccountResolve journalEntryErrorType = "system_account_resolve_failed"
|
||||||
|
journalEntryErrorSystemAccountInvalid journalEntryErrorType = "system_account_invalid"
|
||||||
|
journalEntryErrorSystemAccountMissing journalEntryErrorType = "system_account_missing_id"
|
||||||
|
journalEntryErrorUnbalancedAfterContra journalEntryErrorType = "unbalanced_after_contra"
|
||||||
|
journalEntryErrorTransactionFailed journalEntryErrorType = "transaction_failed"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
metricsOnce sync.Once
|
metricsOnce sync.Once
|
||||||
|
|
||||||
@@ -110,16 +144,16 @@ func initMetrics() {
|
|||||||
|
|
||||||
// Metric recording helpers
|
// Metric recording helpers
|
||||||
|
|
||||||
func recordJournalEntry(entryType, status string, durationSeconds float64) {
|
func recordJournalEntry(entryType journalEntryType, status journalEntryStatus, durationSeconds float64) {
|
||||||
initMetrics()
|
initMetrics()
|
||||||
journalEntriesTotal.WithLabelValues(entryType, status).Inc()
|
journalEntriesTotal.WithLabelValues(string(entryType), string(status)).Inc()
|
||||||
journalEntryLatency.WithLabelValues(entryType).Observe(durationSeconds)
|
journalEntryLatency.WithLabelValues(string(entryType)).Observe(durationSeconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
func recordJournalEntryError(entryType, errorType string) {
|
func recordJournalEntryError(entryType journalEntryType, errorType journalEntryErrorType) {
|
||||||
initMetrics()
|
initMetrics()
|
||||||
journalEntryErrors.WithLabelValues(entryType, errorType).Inc()
|
journalEntryErrors.WithLabelValues(string(entryType), string(errorType)).Inc()
|
||||||
journalEntriesTotal.WithLabelValues(entryType, "error").Inc()
|
journalEntriesTotal.WithLabelValues(string(entryType), string(journalEntryStatusError)).Inc()
|
||||||
}
|
}
|
||||||
|
|
||||||
func recordBalanceQuery(status string, durationSeconds float64) {
|
func recordBalanceQuery(status string, durationSeconds float64) {
|
||||||
@@ -128,9 +162,9 @@ func recordBalanceQuery(status string, durationSeconds float64) {
|
|||||||
balanceQueryLatency.WithLabelValues(status).Observe(durationSeconds)
|
balanceQueryLatency.WithLabelValues(status).Observe(durationSeconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
func recordTransactionAmount(currency, entryType string, amount float64) {
|
func recordTransactionAmount(currency string, entryType journalEntryType, amount float64) {
|
||||||
initMetrics()
|
initMetrics()
|
||||||
transactionAmounts.WithLabelValues(currency, entryType).Observe(amount)
|
transactionAmounts.WithLabelValues(currency, string(entryType)).Observe(amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
func recordAccountOperation(operation, status string) {
|
func recordAccountOperation(operation, status string) {
|
||||||
@@ -138,7 +172,7 @@ func recordAccountOperation(operation, status string) {
|
|||||||
accountOperationsTotal.WithLabelValues(operation, status).Inc()
|
accountOperationsTotal.WithLabelValues(operation, status).Inc()
|
||||||
}
|
}
|
||||||
|
|
||||||
func recordDuplicateRequest(entryType string) {
|
func recordDuplicateRequest(entryType journalEntryType) {
|
||||||
initMetrics()
|
initMetrics()
|
||||||
duplicateRequestsTotal.WithLabelValues(entryType).Inc()
|
duplicateRequestsTotal.WithLabelValues(string(entryType)).Inc()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ func (s *Service) postCreditResponder(_ context.Context, req *ledgerv1.PostCredi
|
|||||||
|
|
||||||
existingEntry, err := s.storage.JournalEntries().GetByIdempotencyKey(ctx, orgRef, req.IdempotencyKey)
|
existingEntry, err := s.storage.JournalEntries().GetByIdempotencyKey(ctx, orgRef, req.IdempotencyKey)
|
||||||
if err == nil && existingEntry != nil {
|
if err == nil && existingEntry != nil {
|
||||||
recordDuplicateRequest("credit")
|
recordDuplicateRequest(journalEntryTypeCredit)
|
||||||
logger.Info("Duplicate credit request (idempotency)",
|
logger.Info("Duplicate credit request (idempotency)",
|
||||||
zap.String("existingEntryID", existingEntry.GetID().Hex()))
|
zap.String("existingEntryID", existingEntry.GetID().Hex()))
|
||||||
return &ledgerv1.PostResponse{
|
return &ledgerv1.PostResponse{
|
||||||
@@ -75,18 +75,18 @@ func (s *Service) postCreditResponder(_ context.Context, req *ledgerv1.PostCredi
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
if err != nil && err != storage.ErrJournalEntryNotFound {
|
if err != nil && err != storage.ErrJournalEntryNotFound {
|
||||||
recordJournalEntryError("credit", "idempotency_check_failed")
|
recordJournalEntryError(journalEntryTypeCredit, journalEntryErrorIdempotencyCheck)
|
||||||
logger.Warn("Failed to check idempotency", zap.Error(err))
|
logger.Warn("Failed to check idempotency", zap.Error(err))
|
||||||
return nil, merrors.Internal("failed to check idempotency")
|
return nil, merrors.Internal("failed to check idempotency")
|
||||||
}
|
}
|
||||||
|
|
||||||
account, accountRef, err := s.resolveAccount(ctx, strings.TrimSpace(req.LedgerAccountRef), roleModel, orgRef, req.Money.Currency, "account")
|
account, accountRef, err := s.resolveAccount(ctx, strings.TrimSpace(req.LedgerAccountRef), roleModel, orgRef, req.Money.Currency, "account")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
recordJournalEntryError("credit", "account_resolve_failed")
|
recordJournalEntryError(journalEntryTypeCredit, journalEntryErrorAccountResolve)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := validateAccountForOrg(account, orgRef, req.Money.Currency); err != nil {
|
if err := validateAccountForOrg(account, orgRef, req.Money.Currency); err != nil {
|
||||||
recordJournalEntryError("credit", "account_invalid")
|
recordJournalEntryError(journalEntryTypeCredit, journalEntryErrorAccountInvalid)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,12 +159,12 @@ func (s *Service) postCreditResponder(_ context.Context, req *ledgerv1.PostCredi
|
|||||||
|
|
||||||
contraAccount, err := s.resolveSettlementAccount(ctx, orgRef, req.Money.Currency, req.ContraLedgerAccountRef, accountsByRef)
|
contraAccount, err := s.resolveSettlementAccount(ctx, orgRef, req.Money.Currency, req.ContraLedgerAccountRef, accountsByRef)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
recordJournalEntryError("credit", "contra_resolve_failed")
|
recordJournalEntryError(journalEntryTypeCredit, journalEntryErrorContraResolve)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
contraAccountID := contraAccount.GetID()
|
contraAccountID := contraAccount.GetID()
|
||||||
if contraAccountID == nil {
|
if contraAccountID == nil {
|
||||||
recordJournalEntryError("credit", "contra_missing_id")
|
recordJournalEntryError(journalEntryTypeCredit, journalEntryErrorContraMissingID)
|
||||||
return nil, merrors.Internal("contra account missing identifier")
|
return nil, merrors.Internal("contra account missing identifier")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,7 +183,7 @@ func (s *Service) postCreditResponder(_ context.Context, req *ledgerv1.PostCredi
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !entryTotal.IsZero() {
|
if !entryTotal.IsZero() {
|
||||||
recordJournalEntryError("credit", "unbalanced_after_contra")
|
recordJournalEntryError(journalEntryTypeCredit, journalEntryErrorUnbalancedAfterContra)
|
||||||
return nil, merrors.Internal("failed to balance journal entry")
|
return nil, merrors.Internal("failed to balance journal entry")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,13 +237,13 @@ func (s *Service) postCreditResponder(_ context.Context, req *ledgerv1.PostCredi
|
|||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
recordJournalEntryError("credit", "transaction_failed")
|
recordJournalEntryError(journalEntryTypeCredit, journalEntryErrorTransactionFailed)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
amountFloat, _ := creditAmount.Float64()
|
amountFloat, _ := creditAmount.Float64()
|
||||||
recordTransactionAmount(req.Money.Currency, "credit", amountFloat)
|
recordTransactionAmount(req.Money.Currency, journalEntryTypeCredit, amountFloat)
|
||||||
recordJournalEntry("credit", "success", 0)
|
recordJournalEntry(journalEntryTypeCredit, journalEntryStatusSuccess, 0)
|
||||||
return result.(*ledgerv1.PostResponse), nil
|
return result.(*ledgerv1.PostResponse), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ func (s *Service) postDebitResponder(_ context.Context, req *ledgerv1.PostDebitR
|
|||||||
|
|
||||||
existingEntry, err := s.storage.JournalEntries().GetByIdempotencyKey(ctx, orgRef, req.IdempotencyKey)
|
existingEntry, err := s.storage.JournalEntries().GetByIdempotencyKey(ctx, orgRef, req.IdempotencyKey)
|
||||||
if err == nil && existingEntry != nil {
|
if err == nil && existingEntry != nil {
|
||||||
recordDuplicateRequest("debit")
|
recordDuplicateRequest(journalEntryTypeDebit)
|
||||||
logger.Info("Duplicate debit request (idempotency)",
|
logger.Info("Duplicate debit request (idempotency)",
|
||||||
zap.String("existingEntryID", existingEntry.GetID().Hex()))
|
zap.String("existingEntryID", existingEntry.GetID().Hex()))
|
||||||
return &ledgerv1.PostResponse{
|
return &ledgerv1.PostResponse{
|
||||||
@@ -79,11 +79,11 @@ func (s *Service) postDebitResponder(_ context.Context, req *ledgerv1.PostDebitR
|
|||||||
|
|
||||||
account, accountRef, err := s.resolveAccount(ctx, strings.TrimSpace(req.LedgerAccountRef), roleModel, orgRef, req.Money.Currency, "account")
|
account, accountRef, err := s.resolveAccount(ctx, strings.TrimSpace(req.LedgerAccountRef), roleModel, orgRef, req.Money.Currency, "account")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
recordJournalEntryError("debit", "account_resolve_failed")
|
recordJournalEntryError(journalEntryTypeDebit, journalEntryErrorAccountResolve)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := validateAccountForOrg(account, orgRef, req.Money.Currency); err != nil {
|
if err := validateAccountForOrg(account, orgRef, req.Money.Currency); err != nil {
|
||||||
recordJournalEntryError("debit", "account_invalid")
|
recordJournalEntryError(journalEntryTypeDebit, journalEntryErrorAccountInvalid)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,12 +156,12 @@ func (s *Service) postDebitResponder(_ context.Context, req *ledgerv1.PostDebitR
|
|||||||
|
|
||||||
contraAccount, err := s.resolveSettlementAccount(ctx, orgRef, req.Money.Currency, req.ContraLedgerAccountRef, accountsByRef)
|
contraAccount, err := s.resolveSettlementAccount(ctx, orgRef, req.Money.Currency, req.ContraLedgerAccountRef, accountsByRef)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
recordJournalEntryError("debit", "contra_resolve_failed")
|
recordJournalEntryError(journalEntryTypeDebit, journalEntryErrorContraResolve)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
contraAccountID := contraAccount.GetID()
|
contraAccountID := contraAccount.GetID()
|
||||||
if contraAccountID == nil {
|
if contraAccountID == nil {
|
||||||
recordJournalEntryError("debit", "contra_missing_id")
|
recordJournalEntryError(journalEntryTypeDebit, journalEntryErrorContraMissingID)
|
||||||
return nil, merrors.Internal("contra account missing identifier")
|
return nil, merrors.Internal("contra account missing identifier")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,7 +180,7 @@ func (s *Service) postDebitResponder(_ context.Context, req *ledgerv1.PostDebitR
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !entryTotal.IsZero() {
|
if !entryTotal.IsZero() {
|
||||||
recordJournalEntryError("debit", "unbalanced_after_contra")
|
recordJournalEntryError(journalEntryTypeDebit, journalEntryErrorUnbalancedAfterContra)
|
||||||
return nil, merrors.Internal("failed to balance journal entry")
|
return nil, merrors.Internal("failed to balance journal entry")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,13 +234,13 @@ func (s *Service) postDebitResponder(_ context.Context, req *ledgerv1.PostDebitR
|
|||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
recordJournalEntryError("debit", "transaction_failed")
|
recordJournalEntryError(journalEntryTypeDebit, journalEntryErrorTransactionFailed)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
amountFloat, _ := debitAmount.Float64()
|
amountFloat, _ := debitAmount.Float64()
|
||||||
recordTransactionAmount(req.Money.Currency, "debit", amountFloat)
|
recordTransactionAmount(req.Money.Currency, journalEntryTypeDebit, amountFloat)
|
||||||
recordJournalEntry("debit", "success", 0)
|
recordJournalEntry(journalEntryTypeDebit, journalEntryStatusSuccess, 0)
|
||||||
return result.(*ledgerv1.PostResponse), nil
|
return result.(*ledgerv1.PostResponse), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ func (s *Service) postExternalCreditResponder(_ context.Context, req *ledgerv1.P
|
|||||||
|
|
||||||
existingEntry, err := s.storage.JournalEntries().GetByIdempotencyKey(ctx, orgRef, req.IdempotencyKey)
|
existingEntry, err := s.storage.JournalEntries().GetByIdempotencyKey(ctx, orgRef, req.IdempotencyKey)
|
||||||
if err == nil && existingEntry != nil {
|
if err == nil && existingEntry != nil {
|
||||||
recordDuplicateRequest("credit")
|
recordDuplicateRequest(journalEntryTypeCredit)
|
||||||
logger.Info("Duplicate external credit request (idempotency)",
|
logger.Info("Duplicate external credit request (idempotency)",
|
||||||
zap.String("existingEntryID", existingEntry.GetID().Hex()))
|
zap.String("existingEntryID", existingEntry.GetID().Hex()))
|
||||||
return &ledgerv1.PostResponse{
|
return &ledgerv1.PostResponse{
|
||||||
@@ -70,34 +70,34 @@ func (s *Service) postExternalCreditResponder(_ context.Context, req *ledgerv1.P
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
if err != nil && err != storage.ErrJournalEntryNotFound {
|
if err != nil && err != storage.ErrJournalEntryNotFound {
|
||||||
recordJournalEntryError("credit", "idempotency_check_failed")
|
recordJournalEntryError(journalEntryTypeCredit, journalEntryErrorIdempotencyCheck)
|
||||||
logger.Warn("Failed to check idempotency", zap.Error(err))
|
logger.Warn("Failed to check idempotency", zap.Error(err))
|
||||||
return nil, merrors.Internal("failed to check idempotency")
|
return nil, merrors.Internal("failed to check idempotency")
|
||||||
}
|
}
|
||||||
|
|
||||||
account, accountRef, err := s.resolveAccount(ctx, strings.TrimSpace(req.LedgerAccountRef), roleModel, orgRef, req.Money.Currency, "account")
|
account, accountRef, err := s.resolveAccount(ctx, strings.TrimSpace(req.LedgerAccountRef), roleModel, orgRef, req.Money.Currency, "account")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
recordJournalEntryError("credit", "account_resolve_failed")
|
recordJournalEntryError(journalEntryTypeCredit, journalEntryErrorAccountResolve)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := validateAccountForOrg(account, orgRef, req.Money.Currency); err != nil {
|
if err := validateAccountForOrg(account, orgRef, req.Money.Currency); err != nil {
|
||||||
recordJournalEntryError("credit", "account_invalid")
|
recordJournalEntryError(journalEntryTypeCredit, journalEntryErrorAccountInvalid)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
systemAccount, err := s.systemAccount(ctx, pmodel.SystemAccountPurposeExternalSource, req.Money.Currency)
|
systemAccount, err := s.systemAccount(ctx, pmodel.SystemAccountPurposeExternalSource, req.Money.Currency)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
recordJournalEntryError("credit", "system_account_resolve_failed")
|
recordJournalEntryError(journalEntryTypeCredit, journalEntryErrorSystemAccountResolve)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := validateSystemAccount(systemAccount, pmodel.SystemAccountPurposeExternalSource, req.Money.Currency); err != nil {
|
if err := validateSystemAccount(systemAccount, pmodel.SystemAccountPurposeExternalSource, req.Money.Currency); err != nil {
|
||||||
recordJournalEntryError("credit", "system_account_invalid")
|
recordJournalEntryError(journalEntryTypeCredit, journalEntryErrorSystemAccountInvalid)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
systemAccountID := systemAccount.GetID()
|
systemAccountID := systemAccount.GetID()
|
||||||
if systemAccountID == nil {
|
if systemAccountID == nil {
|
||||||
recordJournalEntryError("credit", "system_account_missing_id")
|
recordJournalEntryError(journalEntryTypeCredit, journalEntryErrorSystemAccountMissing)
|
||||||
return nil, merrors.Internal("system account missing identifier")
|
return nil, merrors.Internal("system account missing identifier")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,7 +186,7 @@ func (s *Service) postExternalCreditResponder(_ context.Context, req *ledgerv1.P
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !entryTotal.IsZero() {
|
if !entryTotal.IsZero() {
|
||||||
recordJournalEntryError("credit", "unbalanced_after_contra")
|
recordJournalEntryError(journalEntryTypeCredit, journalEntryErrorUnbalancedAfterContra)
|
||||||
return nil, merrors.Internal("failed to balance journal entry")
|
return nil, merrors.Internal("failed to balance journal entry")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,13 +240,13 @@ func (s *Service) postExternalCreditResponder(_ context.Context, req *ledgerv1.P
|
|||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
recordJournalEntryError("credit", "transaction_failed")
|
recordJournalEntryError(journalEntryTypeCredit, journalEntryErrorTransactionFailed)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
amountFloat, _ := creditAmount.Float64()
|
amountFloat, _ := creditAmount.Float64()
|
||||||
recordTransactionAmount(req.Money.Currency, "credit", amountFloat)
|
recordTransactionAmount(req.Money.Currency, journalEntryTypeCredit, amountFloat)
|
||||||
recordJournalEntry("credit", "success", 0)
|
recordJournalEntry(journalEntryTypeCredit, journalEntryStatusSuccess, 0)
|
||||||
return result.(*ledgerv1.PostResponse), nil
|
return result.(*ledgerv1.PostResponse), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -293,7 +293,7 @@ func (s *Service) postExternalDebitResponder(_ context.Context, req *ledgerv1.Po
|
|||||||
|
|
||||||
existingEntry, err := s.storage.JournalEntries().GetByIdempotencyKey(ctx, orgRef, req.IdempotencyKey)
|
existingEntry, err := s.storage.JournalEntries().GetByIdempotencyKey(ctx, orgRef, req.IdempotencyKey)
|
||||||
if err == nil && existingEntry != nil {
|
if err == nil && existingEntry != nil {
|
||||||
recordDuplicateRequest("debit")
|
recordDuplicateRequest(journalEntryTypeDebit)
|
||||||
logger.Info("Duplicate external debit request (idempotency)",
|
logger.Info("Duplicate external debit request (idempotency)",
|
||||||
zap.String("existingEntryID", existingEntry.GetID().Hex()))
|
zap.String("existingEntryID", existingEntry.GetID().Hex()))
|
||||||
return &ledgerv1.PostResponse{
|
return &ledgerv1.PostResponse{
|
||||||
@@ -303,34 +303,34 @@ func (s *Service) postExternalDebitResponder(_ context.Context, req *ledgerv1.Po
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
if err != nil && err != storage.ErrJournalEntryNotFound {
|
if err != nil && err != storage.ErrJournalEntryNotFound {
|
||||||
recordJournalEntryError("debit", "idempotency_check_failed")
|
recordJournalEntryError(journalEntryTypeDebit, journalEntryErrorIdempotencyCheck)
|
||||||
logger.Warn("Failed to check idempotency", zap.Error(err))
|
logger.Warn("Failed to check idempotency", zap.Error(err))
|
||||||
return nil, merrors.Internal("failed to check idempotency")
|
return nil, merrors.Internal("failed to check idempotency")
|
||||||
}
|
}
|
||||||
|
|
||||||
account, accountRef, err := s.resolveAccount(ctx, strings.TrimSpace(req.LedgerAccountRef), roleModel, orgRef, req.Money.Currency, "account")
|
account, accountRef, err := s.resolveAccount(ctx, strings.TrimSpace(req.LedgerAccountRef), roleModel, orgRef, req.Money.Currency, "account")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
recordJournalEntryError("debit", "account_resolve_failed")
|
recordJournalEntryError(journalEntryTypeDebit, journalEntryErrorAccountResolve)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := validateAccountForOrg(account, orgRef, req.Money.Currency); err != nil {
|
if err := validateAccountForOrg(account, orgRef, req.Money.Currency); err != nil {
|
||||||
recordJournalEntryError("debit", "account_invalid")
|
recordJournalEntryError(journalEntryTypeDebit, journalEntryErrorAccountInvalid)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
systemAccount, err := s.systemAccount(ctx, pmodel.SystemAccountPurposeExternalSink, req.Money.Currency)
|
systemAccount, err := s.systemAccount(ctx, pmodel.SystemAccountPurposeExternalSink, req.Money.Currency)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
recordJournalEntryError("debit", "system_account_resolve_failed")
|
recordJournalEntryError(journalEntryTypeDebit, journalEntryErrorSystemAccountResolve)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := validateSystemAccount(systemAccount, pmodel.SystemAccountPurposeExternalSink, req.Money.Currency); err != nil {
|
if err := validateSystemAccount(systemAccount, pmodel.SystemAccountPurposeExternalSink, req.Money.Currency); err != nil {
|
||||||
recordJournalEntryError("debit", "system_account_invalid")
|
recordJournalEntryError(journalEntryTypeDebit, journalEntryErrorSystemAccountInvalid)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
systemAccountID := systemAccount.GetID()
|
systemAccountID := systemAccount.GetID()
|
||||||
if systemAccountID == nil {
|
if systemAccountID == nil {
|
||||||
recordJournalEntryError("debit", "system_account_missing_id")
|
recordJournalEntryError(journalEntryTypeDebit, journalEntryErrorSystemAccountMissing)
|
||||||
return nil, merrors.Internal("system account missing identifier")
|
return nil, merrors.Internal("system account missing identifier")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -419,7 +419,7 @@ func (s *Service) postExternalDebitResponder(_ context.Context, req *ledgerv1.Po
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !entryTotal.IsZero() {
|
if !entryTotal.IsZero() {
|
||||||
recordJournalEntryError("debit", "unbalanced_after_contra")
|
recordJournalEntryError(journalEntryTypeDebit, journalEntryErrorUnbalancedAfterContra)
|
||||||
return nil, merrors.Internal("failed to balance journal entry")
|
return nil, merrors.Internal("failed to balance journal entry")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -473,13 +473,13 @@ func (s *Service) postExternalDebitResponder(_ context.Context, req *ledgerv1.Po
|
|||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
recordJournalEntryError("debit", "transaction_failed")
|
recordJournalEntryError(journalEntryTypeDebit, journalEntryErrorTransactionFailed)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
amountFloat, _ := debitAmount.Float64()
|
amountFloat, _ := debitAmount.Float64()
|
||||||
recordTransactionAmount(req.Money.Currency, "debit", amountFloat)
|
recordTransactionAmount(req.Money.Currency, journalEntryTypeDebit, amountFloat)
|
||||||
recordJournalEntry("debit", "success", 0)
|
recordJournalEntry(journalEntryTypeDebit, journalEntryStatusSuccess, 0)
|
||||||
return result.(*ledgerv1.PostResponse), nil
|
return result.(*ledgerv1.PostResponse), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ func (s *Service) fxResponder(_ context.Context, req *ledgerv1.FXRequest) gsresp
|
|||||||
// Check for duplicate idempotency key
|
// Check for duplicate idempotency key
|
||||||
existingEntry, err := s.storage.JournalEntries().GetByIdempotencyKey(ctx, orgRef, req.IdempotencyKey)
|
existingEntry, err := s.storage.JournalEntries().GetByIdempotencyKey(ctx, orgRef, req.IdempotencyKey)
|
||||||
if err == nil && existingEntry != nil {
|
if err == nil && existingEntry != nil {
|
||||||
recordDuplicateRequest("fx")
|
recordDuplicateRequest(journalEntryTypeFX)
|
||||||
logger.Info("Duplicate FX request (idempotency)",
|
logger.Info("Duplicate FX request (idempotency)",
|
||||||
zap.String("existingEntryID", existingEntry.GetID().Hex()))
|
zap.String("existingEntryID", existingEntry.GetID().Hex()))
|
||||||
return &ledgerv1.PostResponse{
|
return &ledgerv1.PostResponse{
|
||||||
@@ -244,15 +244,15 @@ func (s *Service) fxResponder(_ context.Context, req *ledgerv1.FXRequest) gsresp
|
|||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
recordJournalEntryError("fx", "transaction_failed")
|
recordJournalEntryError(journalEntryTypeFX, journalEntryErrorTransactionFailed)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
fromAmountFloat, _ := fromAmount.Float64()
|
fromAmountFloat, _ := fromAmount.Float64()
|
||||||
toAmountFloat, _ := toAmount.Float64()
|
toAmountFloat, _ := toAmount.Float64()
|
||||||
recordTransactionAmount(req.FromMoney.Currency, "fx", fromAmountFloat)
|
recordTransactionAmount(req.FromMoney.Currency, journalEntryTypeFX, fromAmountFloat)
|
||||||
recordTransactionAmount(req.ToMoney.Currency, "fx", toAmountFloat)
|
recordTransactionAmount(req.ToMoney.Currency, journalEntryTypeFX, toAmountFloat)
|
||||||
recordJournalEntry("fx", "success", 0)
|
recordJournalEntry(journalEntryTypeFX, journalEntryStatusSuccess, 0)
|
||||||
return result.(*ledgerv1.PostResponse), nil
|
return result.(*ledgerv1.PostResponse), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ func (s *Service) transferResponder(_ context.Context, req *ledgerv1.TransferReq
|
|||||||
// Check for duplicate idempotency key
|
// Check for duplicate idempotency key
|
||||||
existingEntry, err := s.storage.JournalEntries().GetByIdempotencyKey(ctx, orgRef, req.IdempotencyKey)
|
existingEntry, err := s.storage.JournalEntries().GetByIdempotencyKey(ctx, orgRef, req.IdempotencyKey)
|
||||||
if err == nil && existingEntry != nil {
|
if err == nil && existingEntry != nil {
|
||||||
recordDuplicateRequest("transfer")
|
recordDuplicateRequest(journalEntryTypeTransfer)
|
||||||
logger.Info("Duplicate transfer request (idempotency)",
|
logger.Info("Duplicate transfer request (idempotency)",
|
||||||
zap.String("existingEntryID", existingEntry.GetID().Hex()))
|
zap.String("existingEntryID", existingEntry.GetID().Hex()))
|
||||||
return &ledgerv1.PostResponse{
|
return &ledgerv1.PostResponse{
|
||||||
@@ -246,13 +246,13 @@ func (s *Service) transferResponder(_ context.Context, req *ledgerv1.TransferReq
|
|||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
recordJournalEntryError("transfer", "failed")
|
recordJournalEntryError(journalEntryTypeTransfer, journalEntryErrorFailed)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
amountFloat, _ := transferAmount.Float64()
|
amountFloat, _ := transferAmount.Float64()
|
||||||
recordTransactionAmount(req.Money.Currency, "transfer", amountFloat)
|
recordTransactionAmount(req.Money.Currency, journalEntryTypeTransfer, amountFloat)
|
||||||
recordJournalEntry("transfer", "success", 0)
|
recordJournalEntry(journalEntryTypeTransfer, journalEntryStatusSuccess, 0)
|
||||||
return result.(*ledgerv1.PostResponse), nil
|
return result.(*ledgerv1.PostResponse), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ func NewService(logger mlogger.Logger, repo storage.Repository, prod pmessaging.
|
|||||||
initMetrics()
|
initMetrics()
|
||||||
|
|
||||||
service := &Service{
|
service := &Service{
|
||||||
logger: logger.Named("ledger"),
|
logger: logger.Named("service"),
|
||||||
storage: repo,
|
storage: repo,
|
||||||
producer: prod,
|
producer: prod,
|
||||||
msgCfg: msgCfg,
|
msgCfg: msgCfg,
|
||||||
@@ -117,17 +117,10 @@ func (s *Service) CreateAccount(ctx context.Context, req *ledgerv1.CreateAccount
|
|||||||
func (s *Service) PostCreditWithCharges(ctx context.Context, req *ledgerv1.PostCreditRequest) (*ledgerv1.PostResponse, error) {
|
func (s *Service) PostCreditWithCharges(ctx context.Context, req *ledgerv1.PostCreditRequest) (*ledgerv1.PostResponse, error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
defer func() {
|
defer func() {
|
||||||
recordJournalEntry("credit", "attempted", time.Since(start).Seconds())
|
recordJournalEntry(journalEntryTypeCredit, journalEntryStatusAttempted, time.Since(start).Seconds())
|
||||||
}()
|
}()
|
||||||
|
|
||||||
responder := s.postCreditResponder(ctx, req)
|
logger := s.logger.With(zap.String("operation", discovery.OperationLedgerCredit))
|
||||||
resp, err := responder(ctx)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
recordJournalEntryError("credit", "not_implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
logger := s.logger.With(zap.String("operation", "credit"))
|
|
||||||
if req != nil {
|
if req != nil {
|
||||||
logger = logger.With(
|
logger = logger.With(
|
||||||
zap.String("idempotency_key", strings.TrimSpace(req.GetIdempotencyKey())),
|
zap.String("idempotency_key", strings.TrimSpace(req.GetIdempotencyKey())),
|
||||||
@@ -147,7 +140,16 @@ func (s *Service) PostCreditWithCharges(ctx context.Context, req *ledgerv1.PostC
|
|||||||
logger = logger.With(zap.String("contra_ledger_account_ref", contra))
|
logger = logger.With(zap.String("contra_ledger_account_ref", contra))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.logLedgerOperation("credit", logger, resp, err)
|
s.logLedgerOperationStart(discovery.OperationLedgerCredit, logger)
|
||||||
|
|
||||||
|
responder := s.postCreditResponder(ctx, req)
|
||||||
|
resp, err := responder(ctx)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
recordJournalEntryError(journalEntryTypeCredit, journalEntryErrorNotImplemented)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.logLedgerOperation(discovery.OperationLedgerCredit, logger, resp, err, time.Since(start))
|
||||||
|
|
||||||
return resp, err
|
return resp, err
|
||||||
}
|
}
|
||||||
@@ -156,17 +158,10 @@ func (s *Service) PostCreditWithCharges(ctx context.Context, req *ledgerv1.PostC
|
|||||||
func (s *Service) PostExternalCreditWithCharges(ctx context.Context, req *ledgerv1.PostCreditRequest) (*ledgerv1.PostResponse, error) {
|
func (s *Service) PostExternalCreditWithCharges(ctx context.Context, req *ledgerv1.PostCreditRequest) (*ledgerv1.PostResponse, error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
defer func() {
|
defer func() {
|
||||||
recordJournalEntry("credit", "attempted", time.Since(start).Seconds())
|
recordJournalEntry(journalEntryTypeCredit, journalEntryStatusAttempted, time.Since(start).Seconds())
|
||||||
}()
|
}()
|
||||||
|
|
||||||
responder := s.postExternalCreditResponder(ctx, req)
|
logger := s.logger.With(zap.String("operation", discovery.OperationExternalCredit))
|
||||||
resp, err := responder(ctx)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
recordJournalEntryError("credit", "failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
logger := s.logger.With(zap.String("operation", "external_credit"))
|
|
||||||
if req != nil {
|
if req != nil {
|
||||||
logger = logger.With(
|
logger = logger.With(
|
||||||
zap.String("idempotency_key", strings.TrimSpace(req.GetIdempotencyKey())),
|
zap.String("idempotency_key", strings.TrimSpace(req.GetIdempotencyKey())),
|
||||||
@@ -183,7 +178,16 @@ func (s *Service) PostExternalCreditWithCharges(ctx context.Context, req *ledger
|
|||||||
logger = logger.With(zap.String("role", role.String()))
|
logger = logger.With(zap.String("role", role.String()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.logLedgerOperation("external_credit", logger, resp, err)
|
s.logLedgerOperationStart(discovery.OperationExternalCredit, logger)
|
||||||
|
|
||||||
|
responder := s.postExternalCreditResponder(ctx, req)
|
||||||
|
resp, err := responder(ctx)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
recordJournalEntryError(journalEntryTypeCredit, journalEntryErrorFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.logLedgerOperation(discovery.OperationExternalCredit, logger, resp, err, time.Since(start))
|
||||||
|
|
||||||
return resp, err
|
return resp, err
|
||||||
}
|
}
|
||||||
@@ -192,17 +196,10 @@ func (s *Service) PostExternalCreditWithCharges(ctx context.Context, req *ledger
|
|||||||
func (s *Service) PostDebitWithCharges(ctx context.Context, req *ledgerv1.PostDebitRequest) (*ledgerv1.PostResponse, error) {
|
func (s *Service) PostDebitWithCharges(ctx context.Context, req *ledgerv1.PostDebitRequest) (*ledgerv1.PostResponse, error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
defer func() {
|
defer func() {
|
||||||
recordJournalEntry("debit", "attempted", time.Since(start).Seconds())
|
recordJournalEntry(journalEntryTypeDebit, journalEntryStatusAttempted, time.Since(start).Seconds())
|
||||||
}()
|
}()
|
||||||
|
|
||||||
responder := s.postDebitResponder(ctx, req)
|
logger := s.logger.With(zap.String("operation", discovery.OperationLedgerDebit))
|
||||||
resp, err := responder(ctx)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
recordJournalEntryError("debit", "failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
logger := s.logger.With(zap.String("operation", "debit"))
|
|
||||||
if req != nil {
|
if req != nil {
|
||||||
logger = logger.With(
|
logger = logger.With(
|
||||||
zap.String("idempotency_key", strings.TrimSpace(req.GetIdempotencyKey())),
|
zap.String("idempotency_key", strings.TrimSpace(req.GetIdempotencyKey())),
|
||||||
@@ -222,7 +219,16 @@ func (s *Service) PostDebitWithCharges(ctx context.Context, req *ledgerv1.PostDe
|
|||||||
logger = logger.With(zap.String("contra_ledger_account_ref", contra))
|
logger = logger.With(zap.String("contra_ledger_account_ref", contra))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.logLedgerOperation("debit", logger, resp, err)
|
s.logLedgerOperationStart(discovery.OperationLedgerDebit, logger)
|
||||||
|
|
||||||
|
responder := s.postDebitResponder(ctx, req)
|
||||||
|
resp, err := responder(ctx)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
recordJournalEntryError(journalEntryTypeDebit, journalEntryErrorFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.logLedgerOperation(discovery.OperationLedgerDebit, logger, resp, err, time.Since(start))
|
||||||
|
|
||||||
return resp, err
|
return resp, err
|
||||||
}
|
}
|
||||||
@@ -231,17 +237,10 @@ func (s *Service) PostDebitWithCharges(ctx context.Context, req *ledgerv1.PostDe
|
|||||||
func (s *Service) PostExternalDebitWithCharges(ctx context.Context, req *ledgerv1.PostDebitRequest) (*ledgerv1.PostResponse, error) {
|
func (s *Service) PostExternalDebitWithCharges(ctx context.Context, req *ledgerv1.PostDebitRequest) (*ledgerv1.PostResponse, error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
defer func() {
|
defer func() {
|
||||||
recordJournalEntry("debit", "attempted", time.Since(start).Seconds())
|
recordJournalEntry(journalEntryTypeDebit, journalEntryStatusAttempted, time.Since(start).Seconds())
|
||||||
}()
|
}()
|
||||||
|
|
||||||
responder := s.postExternalDebitResponder(ctx, req)
|
logger := s.logger.With(zap.String("operation", discovery.OperationExternalDebit))
|
||||||
resp, err := responder(ctx)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
recordJournalEntryError("debit", "failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
logger := s.logger.With(zap.String("operation", "external_debit"))
|
|
||||||
if req != nil {
|
if req != nil {
|
||||||
logger = logger.With(
|
logger = logger.With(
|
||||||
zap.String("idempotency_key", strings.TrimSpace(req.GetIdempotencyKey())),
|
zap.String("idempotency_key", strings.TrimSpace(req.GetIdempotencyKey())),
|
||||||
@@ -258,7 +257,16 @@ func (s *Service) PostExternalDebitWithCharges(ctx context.Context, req *ledgerv
|
|||||||
logger = logger.With(zap.String("role", role.String()))
|
logger = logger.With(zap.String("role", role.String()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.logLedgerOperation("external_debit", logger, resp, err)
|
s.logLedgerOperationStart(discovery.OperationExternalDebit, logger)
|
||||||
|
|
||||||
|
responder := s.postExternalDebitResponder(ctx, req)
|
||||||
|
resp, err := responder(ctx)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
recordJournalEntryError(journalEntryTypeDebit, journalEntryErrorFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.logLedgerOperation(discovery.OperationExternalDebit, logger, resp, err, time.Since(start))
|
||||||
|
|
||||||
return resp, err
|
return resp, err
|
||||||
}
|
}
|
||||||
@@ -267,17 +275,10 @@ func (s *Service) PostExternalDebitWithCharges(ctx context.Context, req *ledgerv
|
|||||||
func (s *Service) TransferInternal(ctx context.Context, req *ledgerv1.TransferRequest) (*ledgerv1.PostResponse, error) {
|
func (s *Service) TransferInternal(ctx context.Context, req *ledgerv1.TransferRequest) (*ledgerv1.PostResponse, error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
defer func() {
|
defer func() {
|
||||||
recordJournalEntry("transfer", "attempted", time.Since(start).Seconds())
|
recordJournalEntry(journalEntryTypeTransfer, journalEntryStatusAttempted, time.Since(start).Seconds())
|
||||||
}()
|
}()
|
||||||
|
|
||||||
responder := s.transferResponder(ctx, req)
|
logger := s.logger.With(zap.String("operation", discovery.OperationLedgerTransfer))
|
||||||
resp, err := responder(ctx)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
recordJournalEntryError("transfer", "failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
logger := s.logger.With(zap.String("operation", "transfer"))
|
|
||||||
if req != nil {
|
if req != nil {
|
||||||
logger = logger.With(
|
logger = logger.With(
|
||||||
zap.String("idempotency_key", strings.TrimSpace(req.GetIdempotencyKey())),
|
zap.String("idempotency_key", strings.TrimSpace(req.GetIdempotencyKey())),
|
||||||
@@ -298,7 +299,16 @@ func (s *Service) TransferInternal(ctx context.Context, req *ledgerv1.TransferRe
|
|||||||
logger = logger.With(zap.String("to_role", role.String()))
|
logger = logger.With(zap.String("to_role", role.String()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.logLedgerOperation("transfer", logger, resp, err)
|
s.logLedgerOperationStart(discovery.OperationLedgerTransfer, logger)
|
||||||
|
|
||||||
|
responder := s.transferResponder(ctx, req)
|
||||||
|
resp, err := responder(ctx)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
recordJournalEntryError(journalEntryTypeTransfer, journalEntryErrorFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.logLedgerOperation(discovery.OperationLedgerTransfer, logger, resp, err, time.Since(start))
|
||||||
|
|
||||||
return resp, err
|
return resp, err
|
||||||
}
|
}
|
||||||
@@ -307,17 +317,10 @@ func (s *Service) TransferInternal(ctx context.Context, req *ledgerv1.TransferRe
|
|||||||
func (s *Service) ApplyFXWithCharges(ctx context.Context, req *ledgerv1.FXRequest) (*ledgerv1.PostResponse, error) {
|
func (s *Service) ApplyFXWithCharges(ctx context.Context, req *ledgerv1.FXRequest) (*ledgerv1.PostResponse, error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
defer func() {
|
defer func() {
|
||||||
recordJournalEntry("fx", "attempted", time.Since(start).Seconds())
|
recordJournalEntry(journalEntryTypeFX, journalEntryStatusAttempted, time.Since(start).Seconds())
|
||||||
}()
|
}()
|
||||||
|
|
||||||
responder := s.fxResponder(ctx, req)
|
logger := s.logger.With(zap.String("operation", discovery.OperationLedgerFX))
|
||||||
resp, err := responder(ctx)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
recordJournalEntryError("fx", "failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
logger := s.logger.With(zap.String("operation", "fx"))
|
|
||||||
if req != nil {
|
if req != nil {
|
||||||
logger = logger.With(
|
logger = logger.With(
|
||||||
zap.String("idempotency_key", strings.TrimSpace(req.GetIdempotencyKey())),
|
zap.String("idempotency_key", strings.TrimSpace(req.GetIdempotencyKey())),
|
||||||
@@ -341,7 +344,16 @@ func (s *Service) ApplyFXWithCharges(ctx context.Context, req *ledgerv1.FXReques
|
|||||||
logger = logger.With(zap.String("rate", rate))
|
logger = logger.With(zap.String("rate", rate))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.logLedgerOperation("fx", logger, resp, err)
|
s.logLedgerOperationStart(discovery.OperationLedgerFX, logger)
|
||||||
|
|
||||||
|
responder := s.fxResponder(ctx, req)
|
||||||
|
resp, err := responder(ctx)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
recordJournalEntryError(journalEntryTypeFX, journalEntryErrorFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.logLedgerOperation(discovery.OperationLedgerFX, logger, resp, err, time.Since(start))
|
||||||
|
|
||||||
return resp, err
|
return resp, err
|
||||||
}
|
}
|
||||||
@@ -365,23 +377,42 @@ func (s *Service) GetJournalEntry(ctx context.Context, req *ledgerv1.GetEntryReq
|
|||||||
return responder(ctx)
|
return responder(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) logLedgerOperation(op string, logger *zap.Logger, resp *ledgerv1.PostResponse, err error) {
|
func (s *Service) logLedgerOperationStart(op string, logger mlogger.Logger) {
|
||||||
if logger == nil {
|
if logger == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err != nil {
|
logger.Debug("Ledger operation execution started", zap.String("operation_name", op))
|
||||||
logger.Warn(fmt.Sprintf("ledger %s failed", op), zap.Error(err))
|
}
|
||||||
|
|
||||||
|
func (s *Service) logLedgerOperation(op string, logger mlogger.Logger, resp *ledgerv1.PostResponse, err error, duration time.Duration) {
|
||||||
|
if logger == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
entryRef := ""
|
entryRef := ""
|
||||||
if resp != nil {
|
if resp != nil {
|
||||||
entryRef = strings.TrimSpace(resp.GetJournalEntryRef())
|
entryRef = strings.TrimSpace(resp.GetJournalEntryRef())
|
||||||
}
|
}
|
||||||
if entryRef == "" {
|
status := "succeeded"
|
||||||
logger.Info(fmt.Sprintf("ledger %s posted", op))
|
fields := []zap.Field{
|
||||||
|
zap.String("operation_name", op),
|
||||||
|
zap.String("status", status),
|
||||||
|
zap.Int64("duration_ms", duration.Milliseconds()),
|
||||||
|
}
|
||||||
|
if entryRef != "" {
|
||||||
|
fields = append(fields, zap.String("journal_entry_ref", entryRef))
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
fields[1] = zap.String("status", "failed")
|
||||||
|
logger.Debug("Ledger operation execution completed", append(fields, zap.Error(err))...)
|
||||||
|
logger.Warn("Ledger operation failed", zap.String("operation_name", op), zap.Error(err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
logger.Info(fmt.Sprintf("ledger %s posted", op), zap.String("journal_entry_ref", entryRef))
|
logger.Debug("Ledger operation execution completed", fields...)
|
||||||
|
if entryRef == "" {
|
||||||
|
logger.Info("Ledger operation posted", zap.String("operation_name", op))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Info("Ledger operation posted", zap.String("operation_name", op), zap.String("journal_entry_ref", entryRef))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Shutdown() {
|
func (s *Service) Shutdown() {
|
||||||
@@ -402,7 +433,7 @@ func (s *Service) startDiscoveryAnnouncer() {
|
|||||||
}
|
}
|
||||||
announce := discovery.Announcement{
|
announce := discovery.Announcement{
|
||||||
Service: "LEDGER",
|
Service: "LEDGER",
|
||||||
Operations: []string{"balance.read", "ledger.debit", "ledger.credit", "external.credit", "external.debit"},
|
Operations: discovery.LedgerServiceOperations(),
|
||||||
InvokeURI: s.invokeURI,
|
InvokeURI: s.invokeURI,
|
||||||
Version: appversion.Create().Short(),
|
Version: appversion.Create().Short(),
|
||||||
}
|
}
|
||||||
@@ -428,8 +459,7 @@ func (s *Service) startOutboxReliableProducer() error {
|
|||||||
}
|
}
|
||||||
s.outbox.producer = reliableProducer
|
s.outbox.producer = reliableProducer
|
||||||
if s.outbox.producer == nil || s.producer == nil {
|
if s.outbox.producer == nil || s.producer == nil {
|
||||||
s.logger.Info("Outbox reliable publisher disabled",
|
s.logger.Info("Outbox reliable publisher disabled", zap.Bool("enabled", settings.Enabled))
|
||||||
zap.Bool("enabled", settings.Enabled))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.logger.Info("Outbox reliable publisher configured",
|
s.logger.Info("Outbox reliable publisher configured",
|
||||||
|
|||||||
@@ -31,13 +31,13 @@ require (
|
|||||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/nats-io/nats.go v1.48.0 // indirect
|
github.com/nats-io/nats.go v1.49.0 // indirect
|
||||||
github.com/nats-io/nkeys v0.4.15 // indirect
|
github.com/nats-io/nkeys v0.4.15 // indirect
|
||||||
github.com/nats-io/nuid v1.0.1 // indirect
|
github.com/nats-io/nuid v1.0.1 // indirect
|
||||||
github.com/prometheus/client_golang v1.23.2 // indirect
|
github.com/prometheus/client_golang v1.23.2 // indirect
|
||||||
github.com/prometheus/client_model v0.6.2 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
github.com/prometheus/common v0.67.5 // indirect
|
github.com/prometheus/common v0.67.5 // indirect
|
||||||
github.com/prometheus/procfs v0.19.2 // indirect
|
github.com/prometheus/procfs v0.20.0 // indirect
|
||||||
github.com/sendgrid/rest v2.6.9+incompatible // indirect
|
github.com/sendgrid/rest v2.6.9+incompatible // indirect
|
||||||
github.com/toorop/go-dkim v0.0.0-20250226130143-9025cce95817 // indirect
|
github.com/toorop/go-dkim v0.0.0-20250226130143-9025cce95817 // indirect
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
@@ -47,10 +47,10 @@ require (
|
|||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||||
golang.org/x/crypto v0.48.0 // indirect
|
golang.org/x/crypto v0.48.0 // indirect
|
||||||
golang.org/x/net v0.50.0 // indirect
|
golang.org/x/net v0.51.0 // indirect
|
||||||
golang.org/x/sync v0.19.0 // indirect
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.41.0 // indirect
|
golang.org/x/sys v0.41.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect
|
||||||
google.golang.org/grpc v1.79.1 // indirect
|
google.golang.org/grpc v1.79.1 // indirect
|
||||||
google.golang.org/protobuf v1.36.11 // indirect
|
google.golang.org/protobuf v1.36.11 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -95,8 +95,8 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
|||||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/nats-io/nats.go v1.48.0 h1:pSFyXApG+yWU/TgbKCjmm5K4wrHu86231/w84qRVR+U=
|
github.com/nats-io/nats.go v1.49.0 h1:yh/WvY59gXqYpgl33ZI+XoVPKyut/IcEaqtsiuTJpoE=
|
||||||
github.com/nats-io/nats.go v1.48.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
github.com/nats-io/nats.go v1.49.0/go.mod h1:fDCn3mN5cY8HooHwE2ukiLb4p4G4ImmzvXyJt+tGwdw=
|
||||||
github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
|
github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
|
||||||
github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
|
github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
|
||||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||||
@@ -119,8 +119,8 @@ github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNw
|
|||||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||||
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
||||||
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
||||||
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
github.com/prometheus/procfs v0.20.0 h1:AA7aCvjxwAquZAlonN7888f2u4IN8WVeFgBi4k82M4Q=
|
||||||
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
github.com/prometheus/procfs v0.20.0/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo=
|
||||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||||
github.com/sendgrid/rest v2.6.9+incompatible h1:1EyIcsNdn9KIisLW50MKwmSRSK+ekueiEMJ7NEoxJo0=
|
github.com/sendgrid/rest v2.6.9+incompatible h1:1EyIcsNdn9KIisLW50MKwmSRSK+ekueiEMJ7NEoxJo0=
|
||||||
@@ -196,8 +196,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
@@ -225,8 +225,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
|
|||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d h1:t/LOSXPJ9R0B6fnZNyALBRfZBH0Uy0gT+uR+SJ6syqQ=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
|
|||||||
@@ -23,6 +23,6 @@ func (a *NotificationAPI) onConfirmationCode(ctx context.Context, account *model
|
|||||||
a.logger.Warn("Failed to send confirmation code email", zap.Error(err), mzap.Login(account))
|
a.logger.Warn("Failed to send confirmation code email", zap.Error(err), mzap.Login(account))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
a.logger.Info("Confirmation code email sent", mzap.Login(account), zap.String("destination", target), zap.String("target", string(purpose)))
|
a.logger.Info("Confirmation code email sent", mzap.Login(account), mzap.MaskEmail("destination", target), zap.String("target", string(purpose)))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ func CreateAPI(a api.API) (*NotificationAPI, error) {
|
|||||||
|
|
||||||
announce := discovery.Announcement{
|
announce := discovery.Announcement{
|
||||||
Service: "NOTIFICATIONS",
|
Service: "NOTIFICATIONS",
|
||||||
Operations: []string{"notify.send"},
|
Operations: []string{discovery.OperationNotifySend},
|
||||||
Version: appversion.Create().Short(),
|
Version: appversion.Create().Short(),
|
||||||
}
|
}
|
||||||
p.announcer = discovery.NewAnnouncer(p.logger, a.Register().Producer(), string(mservice.Notifications), announce)
|
p.announcer = discovery.NewAnnouncer(p.logger, a.Register().Producer(), string(mservice.Notifications), announce)
|
||||||
|
|||||||
@@ -30,13 +30,14 @@ require (
|
|||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/nats-io/nats.go v1.48.0 // indirect
|
github.com/nats-io/nats.go v1.49.0 // indirect
|
||||||
github.com/nats-io/nkeys v0.4.15 // indirect
|
github.com/nats-io/nkeys v0.4.15 // indirect
|
||||||
github.com/nats-io/nuid v1.0.1 // indirect
|
github.com/nats-io/nuid v1.0.1 // indirect
|
||||||
github.com/prometheus/client_golang v1.23.2 // indirect
|
github.com/prometheus/client_golang v1.23.2 // indirect
|
||||||
github.com/prometheus/client_model v0.6.2 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
github.com/prometheus/common v0.67.5 // indirect
|
github.com/prometheus/common v0.67.5 // indirect
|
||||||
github.com/prometheus/procfs v0.19.2 // indirect
|
github.com/prometheus/procfs v0.20.0 // indirect
|
||||||
|
github.com/shopspring/decimal v1.4.0 // indirect
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
github.com/xdg-go/scram v1.2.0 // indirect
|
github.com/xdg-go/scram v1.2.0 // indirect
|
||||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||||
@@ -44,9 +45,9 @@ require (
|
|||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||||
golang.org/x/crypto v0.48.0 // indirect
|
golang.org/x/crypto v0.48.0 // indirect
|
||||||
golang.org/x/net v0.50.0 // indirect
|
golang.org/x/net v0.51.0 // indirect
|
||||||
golang.org/x/sync v0.19.0 // indirect
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.41.0 // indirect
|
golang.org/x/sys v0.41.0 // indirect
|
||||||
golang.org/x/text v0.34.0 // indirect
|
golang.org/x/text v0.34.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -91,8 +91,8 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
|||||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/nats-io/nats.go v1.48.0 h1:pSFyXApG+yWU/TgbKCjmm5K4wrHu86231/w84qRVR+U=
|
github.com/nats-io/nats.go v1.49.0 h1:yh/WvY59gXqYpgl33ZI+XoVPKyut/IcEaqtsiuTJpoE=
|
||||||
github.com/nats-io/nats.go v1.48.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
github.com/nats-io/nats.go v1.49.0/go.mod h1:fDCn3mN5cY8HooHwE2ukiLb4p4G4ImmzvXyJt+tGwdw=
|
||||||
github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
|
github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
|
||||||
github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
|
github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
|
||||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||||
@@ -113,14 +113,16 @@ github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNw
|
|||||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||||
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
||||||
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
||||||
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
github.com/prometheus/procfs v0.20.0 h1:AA7aCvjxwAquZAlonN7888f2u4IN8WVeFgBi4k82M4Q=
|
||||||
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
github.com/prometheus/procfs v0.20.0/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo=
|
||||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||||
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
|
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
|
||||||
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
|
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
|
||||||
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
||||||
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
||||||
|
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||||
|
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||||
@@ -179,8 +181,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
@@ -208,8 +210,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
|
|||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d h1:t/LOSXPJ9R0B6fnZNyALBRfZBH0Uy0gT+uR+SJ6syqQ=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ func (i *Imp) startDiscoveryAnnouncer(cfg *config, producer msg.Producer) {
|
|||||||
announce := discovery.Announcement{
|
announce := discovery.Announcement{
|
||||||
Service: "PAYMENTS_METHODS",
|
Service: "PAYMENTS_METHODS",
|
||||||
Operations: []string{
|
Operations: []string{
|
||||||
"payment_methods.manage",
|
discovery.OperationPaymentMethodsManage,
|
||||||
"payment_methods.read",
|
discovery.OperationPaymentMethodsRead,
|
||||||
},
|
},
|
||||||
InvokeURI: invokeURI,
|
InvokeURI: invokeURI,
|
||||||
Version: appversion.Create().Short(),
|
Version: appversion.Create().Short(),
|
||||||
|
|||||||
@@ -8,8 +8,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tech/sendico/pkg/merrors"
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
orchestrationv1 "github.com/tech/sendico/pkg/proto/payments/orchestration/v1"
|
orchestrationv2 "github.com/tech/sendico/pkg/proto/payments/orchestration/v2"
|
||||||
orchestratorv1 "github.com/tech/sendico/pkg/proto/payments/orchestration/v1"
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
"google.golang.org/grpc/credentials/insecure"
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
@@ -17,32 +16,23 @@ import (
|
|||||||
|
|
||||||
// Client exposes typed helpers around the payment orchestration and quotation gRPC APIs.
|
// Client exposes typed helpers around the payment orchestration and quotation gRPC APIs.
|
||||||
type Client interface {
|
type Client interface {
|
||||||
InitiatePayments(ctx context.Context, req *orchestratorv1.InitiatePaymentsRequest) (*orchestratorv1.InitiatePaymentsResponse, error)
|
ExecutePayment(ctx context.Context, req *orchestrationv2.ExecutePaymentRequest) (*orchestrationv2.ExecutePaymentResponse, error)
|
||||||
InitiatePayment(ctx context.Context, req *orchestratorv1.InitiatePaymentRequest) (*orchestratorv1.InitiatePaymentResponse, error)
|
ExecuteBatchPayment(ctx context.Context, req *orchestrationv2.ExecuteBatchPaymentRequest) (*orchestrationv2.ExecuteBatchPaymentResponse, error)
|
||||||
CancelPayment(ctx context.Context, req *orchestratorv1.CancelPaymentRequest) (*orchestratorv1.CancelPaymentResponse, error)
|
GetPayment(ctx context.Context, req *orchestrationv2.GetPaymentRequest) (*orchestrationv2.GetPaymentResponse, error)
|
||||||
GetPayment(ctx context.Context, req *orchestratorv1.GetPaymentRequest) (*orchestratorv1.GetPaymentResponse, error)
|
ListPayments(ctx context.Context, req *orchestrationv2.ListPaymentsRequest) (*orchestrationv2.ListPaymentsResponse, error)
|
||||||
ListPayments(ctx context.Context, req *orchestratorv1.ListPaymentsRequest) (*orchestratorv1.ListPaymentsResponse, error)
|
|
||||||
InitiateConversion(ctx context.Context, req *orchestratorv1.InitiateConversionRequest) (*orchestratorv1.InitiateConversionResponse, error)
|
|
||||||
ProcessTransferUpdate(ctx context.Context, req *orchestratorv1.ProcessTransferUpdateRequest) (*orchestratorv1.ProcessTransferUpdateResponse, error)
|
|
||||||
ProcessDepositObserved(ctx context.Context, req *orchestratorv1.ProcessDepositObservedRequest) (*orchestratorv1.ProcessDepositObservedResponse, error)
|
|
||||||
Close() error
|
Close() error
|
||||||
}
|
}
|
||||||
|
|
||||||
type grpcOrchestratorClient interface {
|
type grpcOrchestratorClient interface {
|
||||||
InitiatePayments(ctx context.Context, in *orchestratorv1.InitiatePaymentsRequest, opts ...grpc.CallOption) (*orchestratorv1.InitiatePaymentsResponse, error)
|
ExecutePayment(ctx context.Context, in *orchestrationv2.ExecutePaymentRequest, opts ...grpc.CallOption) (*orchestrationv2.ExecutePaymentResponse, error)
|
||||||
InitiatePayment(ctx context.Context, in *orchestratorv1.InitiatePaymentRequest, opts ...grpc.CallOption) (*orchestratorv1.InitiatePaymentResponse, error)
|
ExecuteBatchPayment(ctx context.Context, in *orchestrationv2.ExecuteBatchPaymentRequest, opts ...grpc.CallOption) (*orchestrationv2.ExecuteBatchPaymentResponse, error)
|
||||||
CancelPayment(ctx context.Context, in *orchestratorv1.CancelPaymentRequest, opts ...grpc.CallOption) (*orchestratorv1.CancelPaymentResponse, error)
|
GetPayment(ctx context.Context, in *orchestrationv2.GetPaymentRequest, opts ...grpc.CallOption) (*orchestrationv2.GetPaymentResponse, error)
|
||||||
GetPayment(ctx context.Context, in *orchestratorv1.GetPaymentRequest, opts ...grpc.CallOption) (*orchestratorv1.GetPaymentResponse, error)
|
ListPayments(ctx context.Context, in *orchestrationv2.ListPaymentsRequest, opts ...grpc.CallOption) (*orchestrationv2.ListPaymentsResponse, error)
|
||||||
ListPayments(ctx context.Context, in *orchestratorv1.ListPaymentsRequest, opts ...grpc.CallOption) (*orchestratorv1.ListPaymentsResponse, error)
|
|
||||||
InitiateConversion(ctx context.Context, in *orchestratorv1.InitiateConversionRequest, opts ...grpc.CallOption) (*orchestratorv1.InitiateConversionResponse, error)
|
|
||||||
ProcessTransferUpdate(ctx context.Context, in *orchestratorv1.ProcessTransferUpdateRequest, opts ...grpc.CallOption) (*orchestratorv1.ProcessTransferUpdateResponse, error)
|
|
||||||
ProcessDepositObserved(ctx context.Context, in *orchestratorv1.ProcessDepositObservedRequest, opts ...grpc.CallOption) (*orchestratorv1.ProcessDepositObservedResponse, error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type orchestratorClient struct {
|
type orchestratorClient struct {
|
||||||
cfg Config
|
cfg Config
|
||||||
conn *grpc.ClientConn
|
conn *grpc.ClientConn
|
||||||
quoteConn *grpc.ClientConn
|
|
||||||
client grpcOrchestratorClient
|
client grpcOrchestratorClient
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,29 +42,16 @@ func New(ctx context.Context, cfg Config, opts ...grpc.DialOption) (Client, erro
|
|||||||
if strings.TrimSpace(cfg.Address) == "" {
|
if strings.TrimSpace(cfg.Address) == "" {
|
||||||
return nil, merrors.InvalidArgument("payment-orchestrator: address is required")
|
return nil, merrors.InvalidArgument("payment-orchestrator: address is required")
|
||||||
}
|
}
|
||||||
if strings.TrimSpace(cfg.QuoteAddress) == "" {
|
|
||||||
cfg.QuoteAddress = cfg.Address
|
|
||||||
}
|
|
||||||
|
|
||||||
conn, err := dial(ctx, cfg, cfg.Address, opts...)
|
conn, err := dial(ctx, cfg, cfg.Address, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
quoteConn := conn
|
|
||||||
if cfg.QuoteAddress != cfg.Address {
|
|
||||||
quoteConn, err = dial(ctx, cfg, cfg.QuoteAddress, opts...)
|
|
||||||
if err != nil {
|
|
||||||
_ = conn.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &orchestratorClient{
|
return &orchestratorClient{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
conn: conn,
|
conn: conn,
|
||||||
quoteConn: quoteConn,
|
client: orchestrationv2.NewPaymentOrchestratorServiceClient(conn),
|
||||||
client: orchestrationv1.NewPaymentExecutionServiceClient(conn),
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,11 +76,6 @@ func dial(ctx context.Context, cfg Config, address string, opts ...grpc.DialOpti
|
|||||||
|
|
||||||
// NewWithClient injects a pre-built orchestrator client (useful for tests).
|
// NewWithClient injects a pre-built orchestrator client (useful for tests).
|
||||||
func NewWithClient(cfg Config, oc grpcOrchestratorClient) Client {
|
func NewWithClient(cfg Config, oc grpcOrchestratorClient) Client {
|
||||||
return NewWithClients(cfg, oc)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewWithClients injects pre-built orchestrator and quotation clients (useful for tests).
|
|
||||||
func NewWithClients(cfg Config, oc grpcOrchestratorClient) Client {
|
|
||||||
cfg.setDefaults()
|
cfg.setDefaults()
|
||||||
return &orchestratorClient{
|
return &orchestratorClient{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
@@ -111,69 +83,42 @@ func NewWithClients(cfg Config, oc grpcOrchestratorClient) Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewWithClients injects pre-built orchestrator and quotation clients (useful for tests).
|
||||||
|
func NewWithClients(cfg Config, oc grpcOrchestratorClient) Client {
|
||||||
|
return NewWithClient(cfg, oc)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *orchestratorClient) Close() error {
|
func (c *orchestratorClient) Close() error {
|
||||||
var firstErr error
|
if c == nil || c.conn == nil {
|
||||||
if c.quoteConn != nil && c.quoteConn != c.conn {
|
return nil
|
||||||
if err := c.quoteConn.Close(); err != nil {
|
|
||||||
firstErr = err
|
|
||||||
}
|
}
|
||||||
}
|
return c.conn.Close()
|
||||||
if c.conn != nil {
|
|
||||||
if err := c.conn.Close(); err != nil && firstErr == nil {
|
|
||||||
firstErr = err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return firstErr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *orchestratorClient) InitiatePayments(ctx context.Context, req *orchestratorv1.InitiatePaymentsRequest) (*orchestratorv1.InitiatePaymentsResponse, error) {
|
func (c *orchestratorClient) ExecutePayment(ctx context.Context, req *orchestrationv2.ExecutePaymentRequest) (*orchestrationv2.ExecutePaymentResponse, error) {
|
||||||
ctx, cancel := c.callContext(ctx)
|
ctx, cancel := c.callContext(ctx)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
return c.client.InitiatePayments(ctx, req)
|
return c.client.ExecutePayment(ctx, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *orchestratorClient) InitiatePayment(ctx context.Context, req *orchestratorv1.InitiatePaymentRequest) (*orchestratorv1.InitiatePaymentResponse, error) {
|
func (c *orchestratorClient) ExecuteBatchPayment(ctx context.Context, req *orchestrationv2.ExecuteBatchPaymentRequest) (*orchestrationv2.ExecuteBatchPaymentResponse, error) {
|
||||||
ctx, cancel := c.callContext(ctx)
|
ctx, cancel := c.callContext(ctx)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
return c.client.InitiatePayment(ctx, req)
|
return c.client.ExecuteBatchPayment(ctx, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *orchestratorClient) CancelPayment(ctx context.Context, req *orchestratorv1.CancelPaymentRequest) (*orchestratorv1.CancelPaymentResponse, error) {
|
func (c *orchestratorClient) GetPayment(ctx context.Context, req *orchestrationv2.GetPaymentRequest) (*orchestrationv2.GetPaymentResponse, error) {
|
||||||
ctx, cancel := c.callContext(ctx)
|
|
||||||
defer cancel()
|
|
||||||
return c.client.CancelPayment(ctx, req)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *orchestratorClient) GetPayment(ctx context.Context, req *orchestratorv1.GetPaymentRequest) (*orchestratorv1.GetPaymentResponse, error) {
|
|
||||||
ctx, cancel := c.callContext(ctx)
|
ctx, cancel := c.callContext(ctx)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
return c.client.GetPayment(ctx, req)
|
return c.client.GetPayment(ctx, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *orchestratorClient) ListPayments(ctx context.Context, req *orchestratorv1.ListPaymentsRequest) (*orchestratorv1.ListPaymentsResponse, error) {
|
func (c *orchestratorClient) ListPayments(ctx context.Context, req *orchestrationv2.ListPaymentsRequest) (*orchestrationv2.ListPaymentsResponse, error) {
|
||||||
ctx, cancel := c.callContext(ctx)
|
ctx, cancel := c.callContext(ctx)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
return c.client.ListPayments(ctx, req)
|
return c.client.ListPayments(ctx, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *orchestratorClient) InitiateConversion(ctx context.Context, req *orchestratorv1.InitiateConversionRequest) (*orchestratorv1.InitiateConversionResponse, error) {
|
|
||||||
ctx, cancel := c.callContext(ctx)
|
|
||||||
defer cancel()
|
|
||||||
return c.client.InitiateConversion(ctx, req)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *orchestratorClient) ProcessTransferUpdate(ctx context.Context, req *orchestratorv1.ProcessTransferUpdateRequest) (*orchestratorv1.ProcessTransferUpdateResponse, error) {
|
|
||||||
ctx, cancel := c.callContext(ctx)
|
|
||||||
defer cancel()
|
|
||||||
return c.client.ProcessTransferUpdate(ctx, req)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *orchestratorClient) ProcessDepositObserved(ctx context.Context, req *orchestratorv1.ProcessDepositObservedRequest) (*orchestratorv1.ProcessDepositObservedResponse, error) {
|
|
||||||
ctx, cancel := c.callContext(ctx)
|
|
||||||
defer cancel()
|
|
||||||
return c.client.ProcessDepositObserved(ctx, req)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *orchestratorClient) callContext(ctx context.Context) (context.Context, context.CancelFunc) {
|
func (c *orchestratorClient) callContext(ctx context.Context) (context.Context, context.CancelFunc) {
|
||||||
timeout := c.cfg.CallTimeout
|
timeout := c.cfg.CallTimeout
|
||||||
if timeout <= 0 {
|
if timeout <= 0 {
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import "time"
|
|||||||
// Config captures connection settings for the payment orchestrator gRPC service.
|
// Config captures connection settings for the payment orchestrator gRPC service.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Address string
|
Address string
|
||||||
QuoteAddress string
|
|
||||||
DialTimeout time.Duration
|
DialTimeout time.Duration
|
||||||
CallTimeout time.Duration
|
CallTimeout time.Duration
|
||||||
Insecure bool
|
Insecure bool
|
||||||
|
|||||||
@@ -3,76 +3,44 @@ package client
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
orchestratorv1 "github.com/tech/sendico/pkg/proto/payments/orchestration/v1"
|
orchestrationv2 "github.com/tech/sendico/pkg/proto/payments/orchestration/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Fake implements Client for tests.
|
// Fake implements Client for tests.
|
||||||
type Fake struct {
|
type Fake struct {
|
||||||
InitiatePaymentsFn func(ctx context.Context, req *orchestratorv1.InitiatePaymentsRequest) (*orchestratorv1.InitiatePaymentsResponse, error)
|
ExecutePaymentFn func(ctx context.Context, req *orchestrationv2.ExecutePaymentRequest) (*orchestrationv2.ExecutePaymentResponse, error)
|
||||||
InitiatePaymentFn func(ctx context.Context, req *orchestratorv1.InitiatePaymentRequest) (*orchestratorv1.InitiatePaymentResponse, error)
|
ExecuteBatchPaymentFn func(ctx context.Context, req *orchestrationv2.ExecuteBatchPaymentRequest) (*orchestrationv2.ExecuteBatchPaymentResponse, error)
|
||||||
CancelPaymentFn func(ctx context.Context, req *orchestratorv1.CancelPaymentRequest) (*orchestratorv1.CancelPaymentResponse, error)
|
GetPaymentFn func(ctx context.Context, req *orchestrationv2.GetPaymentRequest) (*orchestrationv2.GetPaymentResponse, error)
|
||||||
GetPaymentFn func(ctx context.Context, req *orchestratorv1.GetPaymentRequest) (*orchestratorv1.GetPaymentResponse, error)
|
ListPaymentsFn func(ctx context.Context, req *orchestrationv2.ListPaymentsRequest) (*orchestrationv2.ListPaymentsResponse, error)
|
||||||
ListPaymentsFn func(ctx context.Context, req *orchestratorv1.ListPaymentsRequest) (*orchestratorv1.ListPaymentsResponse, error)
|
|
||||||
InitiateConversionFn func(ctx context.Context, req *orchestratorv1.InitiateConversionRequest) (*orchestratorv1.InitiateConversionResponse, error)
|
|
||||||
ProcessTransferUpdateFn func(ctx context.Context, req *orchestratorv1.ProcessTransferUpdateRequest) (*orchestratorv1.ProcessTransferUpdateResponse, error)
|
|
||||||
ProcessDepositObservedFn func(ctx context.Context, req *orchestratorv1.ProcessDepositObservedRequest) (*orchestratorv1.ProcessDepositObservedResponse, error)
|
|
||||||
CloseFn func() error
|
CloseFn func() error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Fake) InitiatePayments(ctx context.Context, req *orchestratorv1.InitiatePaymentsRequest) (*orchestratorv1.InitiatePaymentsResponse, error) {
|
func (f *Fake) ExecutePayment(ctx context.Context, req *orchestrationv2.ExecutePaymentRequest) (*orchestrationv2.ExecutePaymentResponse, error) {
|
||||||
if f.InitiatePaymentsFn != nil {
|
if f.ExecutePaymentFn != nil {
|
||||||
return f.InitiatePaymentsFn(ctx, req)
|
return f.ExecutePaymentFn(ctx, req)
|
||||||
}
|
}
|
||||||
return &orchestratorv1.InitiatePaymentsResponse{}, nil
|
return &orchestrationv2.ExecutePaymentResponse{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Fake) InitiatePayment(ctx context.Context, req *orchestratorv1.InitiatePaymentRequest) (*orchestratorv1.InitiatePaymentResponse, error) {
|
func (f *Fake) ExecuteBatchPayment(ctx context.Context, req *orchestrationv2.ExecuteBatchPaymentRequest) (*orchestrationv2.ExecuteBatchPaymentResponse, error) {
|
||||||
if f.InitiatePaymentFn != nil {
|
if f.ExecuteBatchPaymentFn != nil {
|
||||||
return f.InitiatePaymentFn(ctx, req)
|
return f.ExecuteBatchPaymentFn(ctx, req)
|
||||||
}
|
}
|
||||||
return &orchestratorv1.InitiatePaymentResponse{}, nil
|
return &orchestrationv2.ExecuteBatchPaymentResponse{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Fake) CancelPayment(ctx context.Context, req *orchestratorv1.CancelPaymentRequest) (*orchestratorv1.CancelPaymentResponse, error) {
|
func (f *Fake) GetPayment(ctx context.Context, req *orchestrationv2.GetPaymentRequest) (*orchestrationv2.GetPaymentResponse, error) {
|
||||||
if f.CancelPaymentFn != nil {
|
|
||||||
return f.CancelPaymentFn(ctx, req)
|
|
||||||
}
|
|
||||||
return &orchestratorv1.CancelPaymentResponse{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Fake) GetPayment(ctx context.Context, req *orchestratorv1.GetPaymentRequest) (*orchestratorv1.GetPaymentResponse, error) {
|
|
||||||
if f.GetPaymentFn != nil {
|
if f.GetPaymentFn != nil {
|
||||||
return f.GetPaymentFn(ctx, req)
|
return f.GetPaymentFn(ctx, req)
|
||||||
}
|
}
|
||||||
return &orchestratorv1.GetPaymentResponse{}, nil
|
return &orchestrationv2.GetPaymentResponse{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Fake) ListPayments(ctx context.Context, req *orchestratorv1.ListPaymentsRequest) (*orchestratorv1.ListPaymentsResponse, error) {
|
func (f *Fake) ListPayments(ctx context.Context, req *orchestrationv2.ListPaymentsRequest) (*orchestrationv2.ListPaymentsResponse, error) {
|
||||||
if f.ListPaymentsFn != nil {
|
if f.ListPaymentsFn != nil {
|
||||||
return f.ListPaymentsFn(ctx, req)
|
return f.ListPaymentsFn(ctx, req)
|
||||||
}
|
}
|
||||||
return &orchestratorv1.ListPaymentsResponse{}, nil
|
return &orchestrationv2.ListPaymentsResponse{}, nil
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Fake) InitiateConversion(ctx context.Context, req *orchestratorv1.InitiateConversionRequest) (*orchestratorv1.InitiateConversionResponse, error) {
|
|
||||||
if f.InitiateConversionFn != nil {
|
|
||||||
return f.InitiateConversionFn(ctx, req)
|
|
||||||
}
|
|
||||||
return &orchestratorv1.InitiateConversionResponse{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Fake) ProcessTransferUpdate(ctx context.Context, req *orchestratorv1.ProcessTransferUpdateRequest) (*orchestratorv1.ProcessTransferUpdateResponse, error) {
|
|
||||||
if f.ProcessTransferUpdateFn != nil {
|
|
||||||
return f.ProcessTransferUpdateFn(ctx, req)
|
|
||||||
}
|
|
||||||
return &orchestratorv1.ProcessTransferUpdateResponse{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Fake) ProcessDepositObserved(ctx context.Context, req *orchestratorv1.ProcessDepositObservedRequest) (*orchestratorv1.ProcessDepositObservedResponse, error) {
|
|
||||||
if f.ProcessDepositObservedFn != nil {
|
|
||||||
return f.ProcessDepositObservedFn(ctx, req)
|
|
||||||
}
|
|
||||||
return &orchestratorv1.ProcessDepositObservedResponse{}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Fake) Close() error {
|
func (f *Fake) Close() error {
|
||||||
|
|||||||
@@ -38,15 +38,10 @@ messaging:
|
|||||||
# Retain quote records after expiry to allow long-running payments to complete.
|
# Retain quote records after expiry to allow long-running payments to complete.
|
||||||
quote_retention_hours: 72
|
quote_retention_hours: 72
|
||||||
|
|
||||||
max_fx_quote_ttl_ms: 600000
|
|
||||||
|
|
||||||
# Service endpoints are sourced from discovery; no static overrides.
|
# Service endpoints are sourced from discovery; no static overrides.
|
||||||
card_gateways:
|
card_gateways:
|
||||||
monetix:
|
mcards:
|
||||||
funding_address: "TUaWaCkiXwYPKm5qjcB27Lhwv976vPvedE"
|
funding_address: "TUaWaCkiXwYPKm5qjcB27Lhwv976vPvedE"
|
||||||
fee_wallet_ref: "697a062a248dc785125ccb9e"
|
fee_wallet_ref: "697a062a248dc785125ccb9e"
|
||||||
|
|
||||||
fee_ledger_accounts:
|
|
||||||
monetix: "697a15cc72e95c92d4c5db01"
|
|
||||||
|
|
||||||
# Gateway instances and capabilities are sourced from service discovery.
|
# Gateway instances and capabilities are sourced from service discovery.
|
||||||
|
|||||||
@@ -38,15 +38,10 @@ messaging:
|
|||||||
# Retain quote records after expiry to allow long-running payments to complete.
|
# Retain quote records after expiry to allow long-running payments to complete.
|
||||||
quote_retention_hours: 72
|
quote_retention_hours: 72
|
||||||
|
|
||||||
max_fx_quote_ttl_ms: 600000
|
|
||||||
|
|
||||||
# Service endpoints are sourced from discovery; no static overrides.
|
# Service endpoints are sourced from discovery; no static overrides.
|
||||||
card_gateways:
|
card_gateways:
|
||||||
monetix:
|
mcards:
|
||||||
funding_address: "TGBDXEg9rxSqGFJDcb889zqTjDwx1bmLRF"
|
funding_address: "TGBDXEg9rxSqGFJDcb889zqTjDwx1bmLRF"
|
||||||
fee_wallet_ref: "694c124ed76f9f811ac57133"
|
fee_wallet_ref: "694c124ed76f9f811ac57133"
|
||||||
|
|
||||||
fee_ledger_accounts:
|
|
||||||
monetix: "ledger:fees:monetix"
|
|
||||||
|
|
||||||
# Gateway instances and capabilities are sourced from service discovery.
|
# Gateway instances and capabilities are sourced from service discovery.
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ replace github.com/tech/sendico/payments/storage => ../storage
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/prometheus/client_golang v1.23.2
|
|
||||||
github.com/shopspring/decimal v1.4.0
|
github.com/shopspring/decimal v1.4.0
|
||||||
github.com/tech/sendico/fx/oracle v0.0.0-00010101000000-000000000000
|
github.com/tech/sendico/fx/oracle v0.0.0-00010101000000-000000000000
|
||||||
github.com/tech/sendico/gateway/chain v0.0.0-00010101000000-000000000000
|
github.com/tech/sendico/gateway/chain v0.0.0-00010101000000-000000000000
|
||||||
@@ -46,12 +45,13 @@ require (
|
|||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/nats-io/nats.go v1.48.0 // indirect
|
github.com/nats-io/nats.go v1.49.0 // indirect
|
||||||
github.com/nats-io/nkeys v0.4.15 // indirect
|
github.com/nats-io/nkeys v0.4.15 // indirect
|
||||||
github.com/nats-io/nuid v1.0.1 // indirect
|
github.com/nats-io/nuid v1.0.1 // indirect
|
||||||
|
github.com/prometheus/client_golang v1.23.2 // indirect
|
||||||
github.com/prometheus/client_model v0.6.2 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
github.com/prometheus/common v0.67.5 // indirect
|
github.com/prometheus/common v0.67.5 // indirect
|
||||||
github.com/prometheus/procfs v0.19.2 // indirect
|
github.com/prometheus/procfs v0.20.0 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
github.com/xdg-go/scram v1.2.0 // indirect
|
github.com/xdg-go/scram v1.2.0 // indirect
|
||||||
@@ -60,9 +60,9 @@ require (
|
|||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||||
golang.org/x/crypto v0.48.0 // indirect
|
golang.org/x/crypto v0.48.0 // indirect
|
||||||
golang.org/x/net v0.50.0 // indirect
|
golang.org/x/net v0.51.0 // indirect
|
||||||
golang.org/x/sync v0.19.0 // indirect
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.41.0 // indirect
|
golang.org/x/sys v0.41.0 // indirect
|
||||||
golang.org/x/text v0.34.0 // indirect
|
golang.org/x/text v0.34.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -91,8 +91,8 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
|||||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/nats-io/nats.go v1.48.0 h1:pSFyXApG+yWU/TgbKCjmm5K4wrHu86231/w84qRVR+U=
|
github.com/nats-io/nats.go v1.49.0 h1:yh/WvY59gXqYpgl33ZI+XoVPKyut/IcEaqtsiuTJpoE=
|
||||||
github.com/nats-io/nats.go v1.48.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
github.com/nats-io/nats.go v1.49.0/go.mod h1:fDCn3mN5cY8HooHwE2ukiLb4p4G4ImmzvXyJt+tGwdw=
|
||||||
github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
|
github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
|
||||||
github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
|
github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
|
||||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||||
@@ -113,8 +113,8 @@ github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNw
|
|||||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||||
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
||||||
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
||||||
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
github.com/prometheus/procfs v0.20.0 h1:AA7aCvjxwAquZAlonN7888f2u4IN8WVeFgBi4k82M4Q=
|
||||||
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
github.com/prometheus/procfs v0.20.0/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo=
|
||||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||||
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
|
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
|
||||||
@@ -182,8 +182,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
@@ -211,8 +211,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
|
|||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d h1:t/LOSXPJ9R0B6fnZNyALBRfZBH0Uy0gT+uR+SJ6syqQ=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
|
|||||||
@@ -27,22 +27,6 @@ func buildCardGatewayRoutes(src map[string]cardGatewayRouteConfig) map[string]or
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildFeeLedgerAccounts(src map[string]string) map[string]string {
|
|
||||||
if len(src) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
result := make(map[string]string, len(src))
|
|
||||||
for key, account := range src {
|
|
||||||
k := strings.ToLower(strings.TrimSpace(key))
|
|
||||||
v := strings.TrimSpace(account)
|
|
||||||
if k == "" || v == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
result[k] = v
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildGatewayRegistry(logger mlogger.Logger, src []gatewayInstanceConfig, registry *discovery.Registry) orchestrator.GatewayRegistry {
|
func buildGatewayRegistry(logger mlogger.Logger, src []gatewayInstanceConfig, registry *discovery.Registry) orchestrator.GatewayRegistry {
|
||||||
if logger != nil {
|
if logger != nil {
|
||||||
logger = logger.Named("gateway_registry")
|
logger = logger.Named("gateway_registry")
|
||||||
|
|||||||
@@ -13,24 +13,9 @@ import (
|
|||||||
|
|
||||||
type config struct {
|
type config struct {
|
||||||
*grpcapp.Config `yaml:",inline"`
|
*grpcapp.Config `yaml:",inline"`
|
||||||
Fees clientConfig `yaml:"fees"`
|
|
||||||
Ledger clientConfig `yaml:"ledger"`
|
|
||||||
Gateway clientConfig `yaml:"gateway"`
|
|
||||||
PaymentGateway clientConfig `yaml:"payment_gateway"`
|
|
||||||
Mntx clientConfig `yaml:"mntx"`
|
|
||||||
Oracle clientConfig `yaml:"oracle"`
|
|
||||||
CardGateways map[string]cardGatewayRouteConfig `yaml:"card_gateways"`
|
CardGateways map[string]cardGatewayRouteConfig `yaml:"card_gateways"`
|
||||||
FeeAccounts map[string]string `yaml:"fee_ledger_accounts"`
|
|
||||||
GatewayInstances []gatewayInstanceConfig `yaml:"gateway_instances"`
|
GatewayInstances []gatewayInstanceConfig `yaml:"gateway_instances"`
|
||||||
QuoteRetentionHrs int `yaml:"quote_retention_hours"`
|
QuoteRetentionHrs int `yaml:"quote_retention_hours"`
|
||||||
MaxFXQuoteTTLMs int64 `yaml:"max_fx_quote_ttl_ms"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type clientConfig struct {
|
|
||||||
Address string `yaml:"address"`
|
|
||||||
DialTimeoutSecs int `yaml:"dial_timeout_seconds"`
|
|
||||||
CallTimeoutSecs int `yaml:"call_timeout_seconds"`
|
|
||||||
InsecureTransport bool `yaml:"insecure"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type cardGatewayRouteConfig struct {
|
type cardGatewayRouteConfig struct {
|
||||||
@@ -79,18 +64,6 @@ type limitsOverrideCfg struct {
|
|||||||
MaxOps int `yaml:"max_ops"`
|
MaxOps int `yaml:"max_ops"`
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
|
||||||
defaultMaxFXQuoteTTL = 10 * time.Minute
|
|
||||||
defaultMaxFXQuoteTTLMillis = int64(defaultMaxFXQuoteTTL / time.Millisecond)
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c clientConfig) callTimeout() time.Duration {
|
|
||||||
if c.CallTimeoutSecs <= 0 {
|
|
||||||
return 3 * time.Second
|
|
||||||
}
|
|
||||||
return time.Duration(c.CallTimeoutSecs) * time.Second
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *config) quoteRetention() time.Duration {
|
func (c *config) quoteRetention() time.Duration {
|
||||||
if c == nil || c.QuoteRetentionHrs <= 0 {
|
if c == nil || c.QuoteRetentionHrs <= 0 {
|
||||||
return 72 * time.Hour
|
return 72 * time.Hour
|
||||||
@@ -98,13 +71,6 @@ func (c *config) quoteRetention() time.Duration {
|
|||||||
return time.Duration(c.QuoteRetentionHrs) * time.Hour
|
return time.Duration(c.QuoteRetentionHrs) * time.Hour
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *config) maxFXQuoteTTLMillis() int64 {
|
|
||||||
if c == nil || c.MaxFXQuoteTTLMs <= 0 {
|
|
||||||
return defaultMaxFXQuoteTTLMillis
|
|
||||||
}
|
|
||||||
return c.MaxFXQuoteTTLMs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *Imp) loadConfig() (*config, error) {
|
func (i *Imp) loadConfig() (*config, error) {
|
||||||
data, err := os.ReadFile(i.file)
|
data, err := os.ReadFile(i.file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,20 +1,14 @@
|
|||||||
package serverimp
|
package serverimp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
oracleclient "github.com/tech/sendico/fx/oracle/client"
|
|
||||||
mntxclient "github.com/tech/sendico/gateway/mntx/client"
|
mntxclient "github.com/tech/sendico/gateway/mntx/client"
|
||||||
ledgerclient "github.com/tech/sendico/ledger/client"
|
ledgerclient "github.com/tech/sendico/ledger/client"
|
||||||
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrator"
|
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrator"
|
||||||
feesv1 "github.com/tech/sendico/pkg/proto/billing/fees/v1"
|
|
||||||
quotationv1 "github.com/tech/sendico/pkg/proto/payments/quotation/v1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type orchestratorDeps struct {
|
type orchestratorDeps struct {
|
||||||
feesClient feesv1.FeeEngineClient
|
|
||||||
ledgerClient ledgerclient.Client
|
ledgerClient ledgerclient.Client
|
||||||
mntxClient mntxclient.Client
|
mntxClient mntxclient.Client
|
||||||
oracleClient oracleclient.Client
|
|
||||||
quotationClient quotationv1.QuotationServiceClient
|
|
||||||
gatewayInvokeResolver orchestrator.GatewayInvokeResolver
|
gatewayInvokeResolver orchestrator.GatewayInvokeResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,11 +22,8 @@ func (i *Imp) initDependencies(_ *config) *orchestratorDeps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
i.discoveryClients = newDiscoveryClientResolver(i.logger, i.discoveryReg)
|
i.discoveryClients = newDiscoveryClientResolver(i.logger, i.discoveryReg)
|
||||||
deps.feesClient = &discoveryFeeClient{resolver: i.discoveryClients}
|
|
||||||
deps.ledgerClient = &discoveryLedgerClient{resolver: i.discoveryClients}
|
deps.ledgerClient = &discoveryLedgerClient{resolver: i.discoveryClients}
|
||||||
deps.oracleClient = &discoveryOracleClient{resolver: i.discoveryClients}
|
|
||||||
deps.mntxClient = &discoveryMntxClient{resolver: i.discoveryClients}
|
deps.mntxClient = &discoveryMntxClient{resolver: i.discoveryClients}
|
||||||
deps.quotationClient = &discoveryQuotationClient{resolver: i.discoveryClients}
|
|
||||||
deps.gatewayInvokeResolver = discoveryGatewayInvokeResolver{resolver: i.discoveryClients}
|
deps.gatewayInvokeResolver = discoveryGatewayInvokeResolver{resolver: i.discoveryClients}
|
||||||
return deps
|
return deps
|
||||||
}
|
}
|
||||||
@@ -42,9 +33,6 @@ func (i *Imp) buildServiceOptions(cfg *config, deps *orchestratorDeps) []orchest
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
opts := []orchestrator.Option{}
|
opts := []orchestrator.Option{}
|
||||||
if deps.feesClient != nil {
|
|
||||||
opts = append(opts, orchestrator.WithFeeEngine(deps.feesClient, cfg.Fees.callTimeout()))
|
|
||||||
}
|
|
||||||
if deps.ledgerClient != nil {
|
if deps.ledgerClient != nil {
|
||||||
opts = append(opts, orchestrator.WithLedgerClient(deps.ledgerClient))
|
opts = append(opts, orchestrator.WithLedgerClient(deps.ledgerClient))
|
||||||
}
|
}
|
||||||
@@ -52,19 +40,12 @@ func (i *Imp) buildServiceOptions(cfg *config, deps *orchestratorDeps) []orchest
|
|||||||
opts = append(opts, orchestrator.WithMntxGateway(deps.mntxClient))
|
opts = append(opts, orchestrator.WithMntxGateway(deps.mntxClient))
|
||||||
}
|
}
|
||||||
|
|
||||||
if deps.quotationClient != nil {
|
|
||||||
opts = append(opts, orchestrator.WithQuotationService(deps.quotationClient))
|
|
||||||
}
|
|
||||||
opts = append(opts, orchestrator.WithMaxFXQuoteTTLMillis(cfg.maxFXQuoteTTLMillis()))
|
|
||||||
if deps.gatewayInvokeResolver != nil {
|
if deps.gatewayInvokeResolver != nil {
|
||||||
opts = append(opts, orchestrator.WithGatewayInvokeResolver(deps.gatewayInvokeResolver))
|
opts = append(opts, orchestrator.WithGatewayInvokeResolver(deps.gatewayInvokeResolver))
|
||||||
}
|
}
|
||||||
if routes := buildCardGatewayRoutes(cfg.CardGateways); len(routes) > 0 {
|
if routes := buildCardGatewayRoutes(cfg.CardGateways); len(routes) > 0 {
|
||||||
opts = append(opts, orchestrator.WithCardGatewayRoutes(routes))
|
opts = append(opts, orchestrator.WithCardGatewayRoutes(routes))
|
||||||
}
|
}
|
||||||
if feeAccounts := buildFeeLedgerAccounts(cfg.FeeAccounts); len(feeAccounts) > 0 {
|
|
||||||
opts = append(opts, orchestrator.WithFeeLedgerAccounts(feeAccounts))
|
|
||||||
}
|
|
||||||
if registry := buildGatewayRegistry(i.logger, cfg.GatewayInstances, i.discoveryReg); registry != nil {
|
if registry := buildGatewayRegistry(i.logger, cfg.GatewayInstances, i.discoveryReg); registry != nil {
|
||||||
opts = append(opts, orchestrator.WithGatewayRegistry(registry))
|
opts = append(opts, orchestrator.WithGatewayRegistry(registry))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ func (i *Imp) initDiscovery(cfg *config) {
|
|||||||
}
|
}
|
||||||
announce := discovery.Announcement{
|
announce := discovery.Announcement{
|
||||||
Service: "PAYMENTS_ORCHESTRATOR",
|
Service: "PAYMENTS_ORCHESTRATOR",
|
||||||
Operations: []string{"payment.initiate"},
|
Operations: []string{discovery.OperationPaymentInitiate},
|
||||||
InvokeURI: cfg.GRPC.DiscoveryInvokeURI(),
|
InvokeURI: cfg.GRPC.DiscoveryInvokeURI(),
|
||||||
Version: appversion.Create().Short(),
|
Version: appversion.Create().Short(),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import (
|
|||||||
"github.com/tech/sendico/pkg/mlogger"
|
"github.com/tech/sendico/pkg/mlogger"
|
||||||
"github.com/tech/sendico/pkg/mservice"
|
"github.com/tech/sendico/pkg/mservice"
|
||||||
feesv1 "github.com/tech/sendico/pkg/proto/billing/fees/v1"
|
feesv1 "github.com/tech/sendico/pkg/proto/billing/fees/v1"
|
||||||
quotationv1 "github.com/tech/sendico/pkg/proto/payments/quotation/v1"
|
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
@@ -32,8 +31,12 @@ var (
|
|||||||
feesServiceNames = []string{"BILLING_FEES", string(mservice.FeePlans)}
|
feesServiceNames = []string{"BILLING_FEES", string(mservice.FeePlans)}
|
||||||
ledgerServiceNames = []string{"LEDGER", string(mservice.Ledger)}
|
ledgerServiceNames = []string{"LEDGER", string(mservice.Ledger)}
|
||||||
oracleServiceNames = []string{"FX_ORACLE", string(mservice.FXOracle)}
|
oracleServiceNames = []string{"FX_ORACLE", string(mservice.FXOracle)}
|
||||||
mntxServiceNames = []string{"CARD_PAYOUT_RAIL_GATEWAY", string(mservice.MntxGateway)}
|
mntxServiceNames = []string{"CARD_RAIL_GATEWAY", string(mservice.MntxGateway)}
|
||||||
quoteServiceNames = []string{"PAYMENT_QUOTATION", "payment_quotation"}
|
|
||||||
|
feesRequiredOps = []string{discovery.OperationFeeCalc}
|
||||||
|
ledgerRequiredOps = discovery.LedgerServiceOperations()
|
||||||
|
oracleRequiredOps = []string{discovery.OperationFXQuote}
|
||||||
|
mntxRequiredOps = discovery.CardPayoutRailGatewayOperations()
|
||||||
)
|
)
|
||||||
|
|
||||||
type discoveryEndpoint struct {
|
type discoveryEndpoint struct {
|
||||||
@@ -55,9 +58,6 @@ type discoveryClientResolver struct {
|
|||||||
feesConn *grpc.ClientConn
|
feesConn *grpc.ClientConn
|
||||||
feesEndpoint discoveryEndpoint
|
feesEndpoint discoveryEndpoint
|
||||||
|
|
||||||
quoteConn *grpc.ClientConn
|
|
||||||
quoteEndpoint discoveryEndpoint
|
|
||||||
|
|
||||||
ledgerClient ledgerclient.Client
|
ledgerClient ledgerclient.Client
|
||||||
ledgerEndpoint discoveryEndpoint
|
ledgerEndpoint discoveryEndpoint
|
||||||
|
|
||||||
@@ -93,10 +93,6 @@ func (r *discoveryClientResolver) Close() {
|
|||||||
_ = r.feesConn.Close()
|
_ = r.feesConn.Close()
|
||||||
r.feesConn = nil
|
r.feesConn = nil
|
||||||
}
|
}
|
||||||
if r.quoteConn != nil {
|
|
||||||
_ = r.quoteConn.Close()
|
|
||||||
r.quoteConn = nil
|
|
||||||
}
|
|
||||||
if r.ledgerClient != nil {
|
if r.ledgerClient != nil {
|
||||||
_ = r.ledgerClient.Close()
|
_ = r.ledgerClient.Close()
|
||||||
r.ledgerClient = nil
|
r.ledgerClient = nil
|
||||||
@@ -118,32 +114,27 @@ func (r *discoveryClientResolver) Close() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *discoveryClientResolver) FeesAvailable() bool {
|
func (r *discoveryClientResolver) FeesAvailable() bool {
|
||||||
_, ok := r.findEntry("fees", feesServiceNames, "", "")
|
_, ok := r.findEntry("fees", feesServiceNames, "", "", feesRequiredOps)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *discoveryClientResolver) LedgerAvailable() bool {
|
func (r *discoveryClientResolver) LedgerAvailable() bool {
|
||||||
_, ok := r.findEntry("ledger", ledgerServiceNames, "", "")
|
_, ok := r.findEntry("ledger", ledgerServiceNames, "", "", ledgerRequiredOps)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *discoveryClientResolver) OracleAvailable() bool {
|
func (r *discoveryClientResolver) OracleAvailable() bool {
|
||||||
_, ok := r.findEntry("oracle", oracleServiceNames, "", "")
|
_, ok := r.findEntry("oracle", oracleServiceNames, "", "", oracleRequiredOps)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *discoveryClientResolver) MntxAvailable() bool {
|
func (r *discoveryClientResolver) MntxAvailable() bool {
|
||||||
_, ok := r.findEntry("mntx", mntxServiceNames, "", "")
|
_, ok := r.findEntry("mntx", mntxServiceNames, "", "", mntxRequiredOps)
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *discoveryClientResolver) QuotationAvailable() bool {
|
|
||||||
_, ok := r.findEntry("quotation", quoteServiceNames, "", "")
|
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *discoveryClientResolver) FeesClient(ctx context.Context) (feesv1.FeeEngineClient, error) {
|
func (r *discoveryClientResolver) FeesClient(ctx context.Context) (feesv1.FeeEngineClient, error) {
|
||||||
entry, ok := r.findEntry("fees", feesServiceNames, "", "")
|
entry, ok := r.findEntry("fees", feesServiceNames, "", "", feesRequiredOps)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, merrors.NoData("discovery: fees service unavailable")
|
return nil, merrors.NoData("discovery: fees service unavailable")
|
||||||
}
|
}
|
||||||
@@ -173,39 +164,8 @@ func (r *discoveryClientResolver) FeesClient(ctx context.Context) (feesv1.FeeEng
|
|||||||
return feesv1.NewFeeEngineClient(r.feesConn), nil
|
return feesv1.NewFeeEngineClient(r.feesConn), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *discoveryClientResolver) QuotationClient(ctx context.Context) (quotationv1.QuotationServiceClient, error) {
|
|
||||||
entry, ok := r.findEntry("quotation", quoteServiceNames, "", "")
|
|
||||||
if !ok {
|
|
||||||
return nil, merrors.NoData("discovery: quotation service unavailable")
|
|
||||||
}
|
|
||||||
endpoint, err := parseDiscoveryEndpoint(entry.InvokeURI)
|
|
||||||
if err != nil {
|
|
||||||
r.logMissing("quotation", "invalid quotation invoke uri", entry.InvokeURI, err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
r.mu.Lock()
|
|
||||||
defer r.mu.Unlock()
|
|
||||||
|
|
||||||
if r.quoteConn == nil || r.quoteEndpoint.key() != endpoint.key() || r.quoteEndpoint.address != endpoint.address {
|
|
||||||
if r.quoteConn != nil {
|
|
||||||
_ = r.quoteConn.Close()
|
|
||||||
r.quoteConn = nil
|
|
||||||
}
|
|
||||||
conn, dialErr := dialGrpc(ctx, endpoint)
|
|
||||||
if dialErr != nil {
|
|
||||||
r.logMissing("quotation", "failed to dial quotation service", endpoint.raw, dialErr)
|
|
||||||
return nil, dialErr
|
|
||||||
}
|
|
||||||
r.quoteConn = conn
|
|
||||||
r.quoteEndpoint = endpoint
|
|
||||||
}
|
|
||||||
|
|
||||||
return quotationv1.NewQuotationServiceClient(r.quoteConn), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *discoveryClientResolver) LedgerClient(ctx context.Context) (ledgerclient.Client, error) {
|
func (r *discoveryClientResolver) LedgerClient(ctx context.Context) (ledgerclient.Client, error) {
|
||||||
entry, ok := r.findEntry("ledger", ledgerServiceNames, "", "")
|
entry, ok := r.findEntry("ledger", ledgerServiceNames, "", "", ledgerRequiredOps)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, merrors.NoData("discovery: ledger service unavailable")
|
return nil, merrors.NoData("discovery: ledger service unavailable")
|
||||||
}
|
}
|
||||||
@@ -239,7 +199,7 @@ func (r *discoveryClientResolver) LedgerClient(ctx context.Context) (ledgerclien
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *discoveryClientResolver) OracleClient(ctx context.Context) (oracleclient.Client, error) {
|
func (r *discoveryClientResolver) OracleClient(ctx context.Context) (oracleclient.Client, error) {
|
||||||
entry, ok := r.findEntry("oracle", oracleServiceNames, "", "")
|
entry, ok := r.findEntry("oracle", oracleServiceNames, "", "", oracleRequiredOps)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, merrors.NoData("discovery: oracle service unavailable")
|
return nil, merrors.NoData("discovery: oracle service unavailable")
|
||||||
}
|
}
|
||||||
@@ -273,7 +233,7 @@ func (r *discoveryClientResolver) OracleClient(ctx context.Context) (oracleclien
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *discoveryClientResolver) MntxClient(ctx context.Context) (mntxclient.Client, error) {
|
func (r *discoveryClientResolver) MntxClient(ctx context.Context) (mntxclient.Client, error) {
|
||||||
entry, ok := r.findEntry("mntx", mntxServiceNames, "", "")
|
entry, ok := r.findEntry("mntx", mntxServiceNames, "", "", mntxRequiredOps)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, merrors.NoData("discovery: mntx service unavailable")
|
return nil, merrors.NoData("discovery: mntx service unavailable")
|
||||||
}
|
}
|
||||||
@@ -361,14 +321,19 @@ func (r *discoveryClientResolver) PaymentGatewayClient(ctx context.Context, invo
|
|||||||
return client, nil
|
return client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *discoveryClientResolver) findEntry(key string, services []string, rail string, network string) (*discovery.RegistryEntry, bool) {
|
func (r *discoveryClientResolver) findEntry(key string, services []string, rail string, network string, requiredOps []string) (*discovery.RegistryEntry, bool) {
|
||||||
if r == nil || r.registry == nil {
|
if r == nil || r.registry == nil {
|
||||||
r.logMissing(key, "discovery registry unavailable", "", nil)
|
r.logMissing(key, "discovery registry unavailable", "", nil)
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type discoveryMatch struct {
|
||||||
|
entry discovery.RegistryEntry
|
||||||
|
opMatch bool
|
||||||
|
}
|
||||||
|
|
||||||
entries := r.registry.List(time.Now(), true)
|
entries := r.registry.List(time.Now(), true)
|
||||||
matches := make([]discovery.RegistryEntry, 0)
|
matches := make([]discoveryMatch, 0)
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
if !matchesService(entry.Service, services) {
|
if !matchesService(entry.Service, services) {
|
||||||
continue
|
continue
|
||||||
@@ -379,7 +344,10 @@ func (r *discoveryClientResolver) findEntry(key string, services []string, rail
|
|||||||
if network != "" && !strings.EqualFold(strings.TrimSpace(entry.Network), network) {
|
if network != "" && !strings.EqualFold(strings.TrimSpace(entry.Network), network) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
matches = append(matches, entry)
|
matches = append(matches, discoveryMatch{
|
||||||
|
entry: entry,
|
||||||
|
opMatch: discovery.HasAnyOperation(entry.Operations, requiredOps),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(matches) == 0 {
|
if len(matches) == 0 {
|
||||||
@@ -388,25 +356,25 @@ func (r *discoveryClientResolver) findEntry(key string, services []string, rail
|
|||||||
}
|
}
|
||||||
|
|
||||||
sort.Slice(matches, func(i, j int) bool {
|
sort.Slice(matches, func(i, j int) bool {
|
||||||
if matches[i].RoutingPriority != matches[j].RoutingPriority {
|
if matches[i].opMatch != matches[j].opMatch {
|
||||||
return matches[i].RoutingPriority > matches[j].RoutingPriority
|
return matches[i].opMatch
|
||||||
}
|
}
|
||||||
if matches[i].ID != matches[j].ID {
|
if matches[i].entry.RoutingPriority != matches[j].entry.RoutingPriority {
|
||||||
return matches[i].ID < matches[j].ID
|
return matches[i].entry.RoutingPriority > matches[j].entry.RoutingPriority
|
||||||
}
|
}
|
||||||
return matches[i].InstanceID < matches[j].InstanceID
|
if matches[i].entry.ID != matches[j].entry.ID {
|
||||||
|
return matches[i].entry.ID < matches[j].entry.ID
|
||||||
|
}
|
||||||
|
return matches[i].entry.InstanceID < matches[j].entry.InstanceID
|
||||||
})
|
})
|
||||||
|
|
||||||
entry := matches[0]
|
entry := matches[0].entry
|
||||||
entryKey := discoveryEntryKey(entry)
|
entryKey := discoveryEntryKey(entry)
|
||||||
r.logSelection(key, entryKey, entry)
|
r.logSelection(key, entryKey, entry)
|
||||||
return &entry, true
|
return &entry, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *discoveryClientResolver) logSelection(key, entryKey string, entry discovery.RegistryEntry) {
|
func (r *discoveryClientResolver) logSelection(key, entryKey string, entry discovery.RegistryEntry) {
|
||||||
if r.logger == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r.mu.Lock()
|
r.mu.Lock()
|
||||||
last := r.lastSelection[key]
|
last := r.lastSelection[key]
|
||||||
if last == entryKey {
|
if last == entryKey {
|
||||||
@@ -426,9 +394,6 @@ func (r *discoveryClientResolver) logSelection(key, entryKey string, entry disco
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *discoveryClientResolver) logMissing(key, message, invokeURI string, err error) {
|
func (r *discoveryClientResolver) logMissing(key, message, invokeURI string, err error) {
|
||||||
if r.logger == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
r.mu.Lock()
|
r.mu.Lock()
|
||||||
last := r.lastMissing[key]
|
last := r.lastMissing[key]
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import (
|
|||||||
chainv1 "github.com/tech/sendico/pkg/proto/gateway/chain/v1"
|
chainv1 "github.com/tech/sendico/pkg/proto/gateway/chain/v1"
|
||||||
mntxv1 "github.com/tech/sendico/pkg/proto/gateway/mntx/v1"
|
mntxv1 "github.com/tech/sendico/pkg/proto/gateway/mntx/v1"
|
||||||
ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
|
ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
|
||||||
quotationv1 "github.com/tech/sendico/pkg/proto/payments/quotation/v1"
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -52,33 +51,6 @@ func (c *discoveryFeeClient) ValidateFeeToken(ctx context.Context, req *feesv1.V
|
|||||||
return client.ValidateFeeToken(ctx, req, opts...)
|
return client.ValidateFeeToken(ctx, req, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
type discoveryQuotationClient struct {
|
|
||||||
resolver *discoveryClientResolver
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *discoveryQuotationClient) Available() bool {
|
|
||||||
if c == nil || c.resolver == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return c.resolver.QuotationAvailable()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *discoveryQuotationClient) QuotePayment(ctx context.Context, req *quotationv1.QuotePaymentRequest, opts ...grpc.CallOption) (*quotationv1.QuotePaymentResponse, error) {
|
|
||||||
client, err := c.resolver.QuotationClient(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return client.QuotePayment(ctx, req, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *discoveryQuotationClient) QuotePayments(ctx context.Context, req *quotationv1.QuotePaymentsRequest, opts ...grpc.CallOption) (*quotationv1.QuotePaymentsResponse, error) {
|
|
||||||
client, err := c.resolver.QuotationClient(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return client.QuotePayments(ctx, req, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
type discoveryLedgerClient struct {
|
type discoveryLedgerClient struct {
|
||||||
resolver *discoveryClientResolver
|
resolver *discoveryClientResolver
|
||||||
}
|
}
|
||||||
@@ -162,6 +134,22 @@ func (c *discoveryLedgerClient) PostDebitWithCharges(ctx context.Context, req *l
|
|||||||
return client.PostDebitWithCharges(ctx, req)
|
return client.PostDebitWithCharges(ctx, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *discoveryLedgerClient) PostExternalCreditWithCharges(ctx context.Context, req *ledgerv1.PostCreditRequest) (*ledgerv1.PostResponse, error) {
|
||||||
|
client, err := c.resolver.LedgerClient(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return client.PostExternalCreditWithCharges(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *discoveryLedgerClient) PostExternalDebitWithCharges(ctx context.Context, req *ledgerv1.PostDebitRequest) (*ledgerv1.PostResponse, error) {
|
||||||
|
client, err := c.resolver.LedgerClient(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return client.PostExternalDebitWithCharges(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *discoveryLedgerClient) ApplyFXWithCharges(ctx context.Context, req *ledgerv1.FXRequest) (*ledgerv1.PostResponse, error) {
|
func (c *discoveryLedgerClient) ApplyFXWithCharges(ctx context.Context, req *ledgerv1.FXRequest) (*ledgerv1.PostResponse, error) {
|
||||||
client, err := c.resolver.LedgerClient(ctx)
|
client, err := c.resolver.LedgerClient(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -58,12 +58,12 @@ func (i *Imp) Start() error {
|
|||||||
if broker != nil {
|
if broker != nil {
|
||||||
opts = append(opts, orchestrator.WithPaymentGatewayBroker(broker))
|
opts = append(opts, orchestrator.WithPaymentGatewayBroker(broker))
|
||||||
}
|
}
|
||||||
svc := orchestrator.NewService(logger, repo, opts...)
|
svc, err := orchestrator.NewService(logger, repo, opts...)
|
||||||
i.service = svc
|
i.service = svc
|
||||||
return svc, nil
|
return svc, err
|
||||||
}
|
}
|
||||||
|
|
||||||
app, err := grpcapp.NewApp(i.logger, "payments_orchestrator", cfg.Config, i.debug, repoFactory, serviceFactory)
|
app, err := grpcapp.NewApp(i.logger, "payments.orchestrator", cfg.Config, i.debug, repoFactory, serviceFactory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,10 @@ import (
|
|||||||
|
|
||||||
"github.com/tech/sendico/payments/storage/model"
|
"github.com/tech/sendico/payments/storage/model"
|
||||||
"github.com/tech/sendico/pkg/db/storable"
|
"github.com/tech/sendico/pkg/db/storable"
|
||||||
|
"github.com/tech/sendico/pkg/mlogger"
|
||||||
pm "github.com/tech/sendico/pkg/model"
|
pm "github.com/tech/sendico/pkg/model"
|
||||||
"go.mongodb.org/mongo-driver/v2/bson"
|
"go.mongodb.org/mongo-driver/v2/bson"
|
||||||
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Factory builds initial orchestration-v2 payment aggregates.
|
// Factory builds initial orchestration-v2 payment aggregates.
|
||||||
@@ -41,58 +43,76 @@ const (
|
|||||||
|
|
||||||
// StepShell defines one initial step telemetry item.
|
// StepShell defines one initial step telemetry item.
|
||||||
type StepShell struct {
|
type StepShell struct {
|
||||||
StepRef string
|
StepRef string `bson:"stepRef" json:"stepRef"`
|
||||||
StepCode string
|
StepCode string `bson:"stepCode" json:"stepCode"`
|
||||||
|
ReportVisibility model.ReportVisibility `bson:"reportVisibility,omitempty" json:"reportVisibility,omitempty"`
|
||||||
|
UserLabel string `bson:"userLabel,omitempty" json:"userLabel,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// StepExecution is runtime telemetry for one step.
|
// StepExecution is runtime telemetry for one step.
|
||||||
type StepExecution struct {
|
type StepExecution struct {
|
||||||
StepRef string
|
StepRef string `bson:"stepRef" json:"stepRef"`
|
||||||
StepCode string
|
StepCode string `bson:"stepCode" json:"stepCode"`
|
||||||
State StepState
|
ReportVisibility model.ReportVisibility `bson:"reportVisibility,omitempty" json:"reportVisibility,omitempty"`
|
||||||
Attempt uint32
|
UserLabel string `bson:"userLabel,omitempty" json:"userLabel,omitempty"`
|
||||||
StartedAt *time.Time
|
State StepState `bson:"state" json:"state"`
|
||||||
CompletedAt *time.Time
|
Attempt uint32 `bson:"attempt" json:"attempt"`
|
||||||
FailureCode string
|
StartedAt *time.Time `bson:"startedAt,omitempty" json:"startedAt,omitempty"`
|
||||||
FailureMsg string
|
CompletedAt *time.Time `bson:"completedAt,omitempty" json:"completedAt,omitempty"`
|
||||||
ExternalRefs []ExternalRef
|
FailureCode string `bson:"failureCode,omitempty" json:"failureCode,omitempty"`
|
||||||
|
FailureMsg string `bson:"failureMsg,omitempty" json:"failureMsg,omitempty"`
|
||||||
|
ExternalRefs []ExternalRef `bson:"externalRefs,omitempty" json:"externalRefs,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExternalRef links step execution to an external operation.
|
// ExternalRef links step execution to an external operation.
|
||||||
type ExternalRef struct {
|
type ExternalRef struct {
|
||||||
GatewayInstanceID string
|
GatewayInstanceID string `bson:"gatewayInstanceId,omitempty" json:"gatewayInstanceId,omitempty"`
|
||||||
Kind string
|
Kind string `bson:"kind" json:"kind"`
|
||||||
Ref string
|
Ref string `bson:"ref" json:"ref"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Input defines payload for creating an initial payment aggregate.
|
// Input defines payload for creating an initial payment aggregate.
|
||||||
type Input struct {
|
type Input struct {
|
||||||
OrganizationRef bson.ObjectID
|
OrganizationRef bson.ObjectID `bson:"organizationRef" json:"organizationRef"`
|
||||||
IdempotencyKey string
|
IdempotencyKey string `bson:"idempotencyKey" json:"idempotencyKey"`
|
||||||
QuotationRef string
|
QuotationRef string `bson:"quotationRef" json:"quotationRef"`
|
||||||
ClientPaymentRef string
|
ClientPaymentRef string `bson:"clientPaymentRef,omitempty" json:"clientPaymentRef,omitempty"`
|
||||||
IntentSnapshot model.PaymentIntent
|
IntentSnapshot model.PaymentIntent `bson:"intentSnapshot" json:"intentSnapshot"`
|
||||||
QuoteSnapshot *model.PaymentQuoteSnapshot
|
QuoteSnapshot *model.PaymentQuoteSnapshot `bson:"quoteSnapshot" json:"quoteSnapshot"`
|
||||||
Steps []StepShell
|
Steps []StepShell `bson:"steps,omitempty" json:"steps,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Payment is orchestration-v2 runtime aggregate.
|
// Payment is orchestration-v2 runtime aggregate.
|
||||||
type Payment struct {
|
type Payment struct {
|
||||||
storable.Base
|
storable.Base `bson:",inline" json:",inline"`
|
||||||
pm.OrganizationBoundBase
|
pm.OrganizationBoundBase `bson:",inline" json:",inline"`
|
||||||
PaymentRef string
|
PaymentRef string `bson:"paymentRef" json:"paymentRef"`
|
||||||
IdempotencyKey string
|
IdempotencyKey string `bson:"idempotencyKey" json:"idempotencyKey"`
|
||||||
QuotationRef string
|
QuotationRef string `bson:"quotationRef" json:"quotationRef"`
|
||||||
ClientPaymentRef string
|
ClientPaymentRef string `bson:"clientPaymentRef,omitempty" json:"clientPaymentRef,omitempty"`
|
||||||
IntentSnapshot model.PaymentIntent
|
IntentSnapshot model.PaymentIntent `bson:"intentSnapshot" json:"intentSnapshot"`
|
||||||
QuoteSnapshot *model.PaymentQuoteSnapshot
|
QuoteSnapshot *model.PaymentQuoteSnapshot `bson:"quoteSnapshot" json:"quoteSnapshot"`
|
||||||
State State
|
State State `bson:"state" json:"state"`
|
||||||
Version uint64
|
Version uint64 `bson:"version" json:"version"`
|
||||||
StepExecutions []StepExecution
|
StepExecutions []StepExecution `bson:"stepExecutions,omitempty" json:"stepExecutions,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() Factory {
|
// Dependencies configures aggregate factory integrations.
|
||||||
|
type Dependencies struct {
|
||||||
|
Logger mlogger.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(deps ...Dependencies) Factory {
|
||||||
|
var dep Dependencies
|
||||||
|
if len(deps) > 0 {
|
||||||
|
dep = deps[0]
|
||||||
|
}
|
||||||
|
logger := dep.Logger
|
||||||
|
if logger == nil {
|
||||||
|
logger = zap.NewNop()
|
||||||
|
}
|
||||||
return &svc{
|
return &svc{
|
||||||
|
logger: logger.Named("aggregator"),
|
||||||
now: func() time.Time { return time.Now().UTC() },
|
now: func() time.Time { return time.Now().UTC() },
|
||||||
newID: func() bson.ObjectID {
|
newID: func() bson.ObjectID {
|
||||||
return bson.NewObjectID()
|
return bson.NewObjectID()
|
||||||
|
|||||||
@@ -7,18 +7,45 @@ import (
|
|||||||
"github.com/tech/sendico/payments/storage/model"
|
"github.com/tech/sendico/payments/storage/model"
|
||||||
"github.com/tech/sendico/pkg/db/storable"
|
"github.com/tech/sendico/pkg/db/storable"
|
||||||
"github.com/tech/sendico/pkg/merrors"
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
|
"github.com/tech/sendico/pkg/mlogger"
|
||||||
pm "github.com/tech/sendico/pkg/model"
|
pm "github.com/tech/sendico/pkg/model"
|
||||||
"go.mongodb.org/mongo-driver/v2/bson"
|
"go.mongodb.org/mongo-driver/v2/bson"
|
||||||
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
const initialVersion uint64 = 1
|
const initialVersion uint64 = 1
|
||||||
|
|
||||||
type svc struct {
|
type svc struct {
|
||||||
|
logger mlogger.Logger
|
||||||
now func() time.Time
|
now func() time.Time
|
||||||
newID func() bson.ObjectID
|
newID func() bson.ObjectID
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *svc) Create(in Input) (*Payment, error) {
|
func (s *svc) Create(in Input) (payment *Payment, err error) {
|
||||||
|
logger := s.logger
|
||||||
|
logger.Debug("Starting Create",
|
||||||
|
zap.String("organization_ref", in.OrganizationRef.Hex()),
|
||||||
|
zap.String("quotation_ref", strings.TrimSpace(in.QuotationRef)),
|
||||||
|
zap.Int("steps_count", len(in.Steps)),
|
||||||
|
)
|
||||||
|
defer func(start time.Time) {
|
||||||
|
fields := []zap.Field{zap.Int64("duration_ms", time.Since(start).Milliseconds())}
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn("Failed to create", append(fields, zap.Error(err))...)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if payment == nil {
|
||||||
|
logger.Debug("Completed Create", append(fields, zap.Bool("payment_nil", true))...)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fields = append(fields,
|
||||||
|
zap.String("payment_ref", strings.TrimSpace(payment.PaymentRef)),
|
||||||
|
zap.String("state", string(payment.State)),
|
||||||
|
zap.Uint64("version", payment.Version),
|
||||||
|
)
|
||||||
|
logger.Debug("Completed Create", fields...)
|
||||||
|
}(time.Now())
|
||||||
|
|
||||||
if in.OrganizationRef.IsZero() {
|
if in.OrganizationRef.IsZero() {
|
||||||
return nil, merrors.InvalidArgument("organization_id is required")
|
return nil, merrors.InvalidArgument("organization_id is required")
|
||||||
}
|
}
|
||||||
@@ -67,7 +94,7 @@ func (s *svc) Create(in Input) (*Payment, error) {
|
|||||||
now := s.now().UTC()
|
now := s.now().UTC()
|
||||||
id := s.newID()
|
id := s.newID()
|
||||||
|
|
||||||
return &Payment{
|
payment = &Payment{
|
||||||
Base: storable.Base{
|
Base: storable.Base{
|
||||||
ID: id,
|
ID: id,
|
||||||
CreatedAt: now,
|
CreatedAt: now,
|
||||||
@@ -85,7 +112,8 @@ func (s *svc) Create(in Input) (*Payment, error) {
|
|||||||
State: StateCreated,
|
State: StateCreated,
|
||||||
Version: initialVersion,
|
Version: initialVersion,
|
||||||
StepExecutions: stepExecutions,
|
StepExecutions: stepExecutions,
|
||||||
}, nil
|
}
|
||||||
|
return payment, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildInitialStepTelemetry(shell []StepShell) ([]StepExecution, error) {
|
func buildInitialStepTelemetry(shell []StepShell) ([]StepExecution, error) {
|
||||||
@@ -109,10 +137,17 @@ func buildInitialStepTelemetry(shell []StepShell) ([]StepExecution, error) {
|
|||||||
if stepCode == "" {
|
if stepCode == "" {
|
||||||
return nil, merrors.InvalidArgument("steps[" + itoa(i) + "].step_code is required")
|
return nil, merrors.InvalidArgument("steps[" + itoa(i) + "].step_code is required")
|
||||||
}
|
}
|
||||||
|
visibility := model.NormalizeReportVisibility(shell[i].ReportVisibility)
|
||||||
|
if !model.IsValidReportVisibility(visibility) {
|
||||||
|
return nil, merrors.InvalidArgument("steps[" + itoa(i) + "].report_visibility is invalid")
|
||||||
|
}
|
||||||
|
userLabel := strings.TrimSpace(shell[i].UserLabel)
|
||||||
|
|
||||||
out = append(out, StepExecution{
|
out = append(out, StepExecution{
|
||||||
StepRef: stepRef,
|
StepRef: stepRef,
|
||||||
StepCode: stepCode,
|
StepCode: stepCode,
|
||||||
|
ReportVisibility: visibility,
|
||||||
|
UserLabel: userLabel,
|
||||||
State: StepStatePending,
|
State: StepStatePending,
|
||||||
Attempt: 1,
|
Attempt: 1,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/tech/sendico/pkg/merrors"
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
paymenttypes "github.com/tech/sendico/pkg/payments/types"
|
paymenttypes "github.com/tech/sendico/pkg/payments/types"
|
||||||
"go.mongodb.org/mongo-driver/v2/bson"
|
"go.mongodb.org/mongo-driver/v2/bson"
|
||||||
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCreate_OK(t *testing.T) {
|
func TestCreate_OK(t *testing.T) {
|
||||||
@@ -17,6 +18,7 @@ func TestCreate_OK(t *testing.T) {
|
|||||||
paymentID := bson.NewObjectID()
|
paymentID := bson.NewObjectID()
|
||||||
|
|
||||||
factory := &svc{
|
factory := &svc{
|
||||||
|
logger: zap.NewNop(),
|
||||||
now: func() time.Time { return now },
|
now: func() time.Time { return now },
|
||||||
newID: func() bson.ObjectID {
|
newID: func() bson.ObjectID {
|
||||||
return paymentID
|
return paymentID
|
||||||
@@ -39,8 +41,8 @@ func TestCreate_OK(t *testing.T) {
|
|||||||
IntentSnapshot: intent,
|
IntentSnapshot: intent,
|
||||||
QuoteSnapshot: quote,
|
QuoteSnapshot: quote,
|
||||||
Steps: []StepShell{
|
Steps: []StepShell{
|
||||||
{StepRef: " s1 ", StepCode: " reserve_funds "},
|
{StepRef: " s1 ", StepCode: " reserve_funds ", ReportVisibility: model.ReportVisibilityHidden},
|
||||||
{StepRef: "s2", StepCode: "submit_gateway"},
|
{StepRef: "s2", StepCode: "submit_gateway", ReportVisibility: model.ReportVisibilityUser, UserLabel: " Card payout "},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -100,6 +102,15 @@ func TestCreate_OK(t *testing.T) {
|
|||||||
if payment.StepExecutions[0].State != StepStatePending || payment.StepExecutions[0].Attempt != 1 {
|
if payment.StepExecutions[0].State != StepStatePending || payment.StepExecutions[0].Attempt != 1 {
|
||||||
t.Fatalf("unexpected first step shell state: %+v", payment.StepExecutions[0])
|
t.Fatalf("unexpected first step shell state: %+v", payment.StepExecutions[0])
|
||||||
}
|
}
|
||||||
|
if got, want := payment.StepExecutions[0].ReportVisibility, model.ReportVisibilityHidden; got != want {
|
||||||
|
t.Fatalf("unexpected first step visibility: got=%q want=%q", got, want)
|
||||||
|
}
|
||||||
|
if got, want := payment.StepExecutions[1].ReportVisibility, model.ReportVisibilityUser; got != want {
|
||||||
|
t.Fatalf("unexpected second step visibility: got=%q want=%q", got, want)
|
||||||
|
}
|
||||||
|
if got, want := payment.StepExecutions[1].UserLabel, "Card payout"; got != want {
|
||||||
|
t.Fatalf("unexpected second step user label: got=%q want=%q", got, want)
|
||||||
|
}
|
||||||
|
|
||||||
// Verify immutable snapshot semantics by ensuring clones were created.
|
// Verify immutable snapshot semantics by ensuring clones were created.
|
||||||
payment.IntentSnapshot.Ref = "changed"
|
payment.IntentSnapshot.Ref = "changed"
|
||||||
@@ -231,6 +242,19 @@ func TestCreate_InputValidation(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "step report visibility invalid",
|
||||||
|
in: Input{
|
||||||
|
OrganizationRef: bson.NewObjectID(),
|
||||||
|
IdempotencyKey: "idem-1",
|
||||||
|
QuotationRef: "quote-1",
|
||||||
|
IntentSnapshot: model.PaymentIntent{Kind: model.PaymentKindPayout, Amount: testMoney()},
|
||||||
|
QuoteSnapshot: &model.PaymentQuoteSnapshot{QuoteRef: "quote-1"},
|
||||||
|
Steps: []StepShell{
|
||||||
|
{StepRef: "s1", StepCode: "code-1", ReportVisibility: model.ReportVisibility("invalid")},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "step ref must be unique",
|
name: "step ref must be unique",
|
||||||
in: Input{
|
in: Input{
|
||||||
|
|||||||
@@ -0,0 +1,296 @@
|
|||||||
|
package batchmeta
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/tech/sendico/payments/storage/model"
|
||||||
|
paymenttypes "github.com/tech/sendico/pkg/payments/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// AttrPayoutTargets stores serialized payout target descriptors in intent attributes.
|
||||||
|
AttrPayoutTargets = "orchestrator.v2.batch_payout_targets"
|
||||||
|
|
||||||
|
MetaPayoutTargetRef = "orchestrator.v2.payout_target_ref"
|
||||||
|
MetaPayoutTargetIndex = "orchestrator.v2.payout_target_index"
|
||||||
|
MetaAmount = "orchestrator.v2.amount"
|
||||||
|
MetaCurrency = "orchestrator.v2.currency"
|
||||||
|
|
||||||
|
MetaCardPan = "orchestrator.v2.card_pan"
|
||||||
|
MetaCardToken = "orchestrator.v2.card_token"
|
||||||
|
MetaCardholder = "orchestrator.v2.cardholder"
|
||||||
|
MetaCardholderSurname = "orchestrator.v2.cardholder_surname"
|
||||||
|
MetaCardExpMonth = "orchestrator.v2.card_exp_month"
|
||||||
|
MetaCardExpYear = "orchestrator.v2.card_exp_year"
|
||||||
|
MetaCardCountry = "orchestrator.v2.card_country"
|
||||||
|
MetaCardMaskedPan = "orchestrator.v2.card_masked_pan"
|
||||||
|
MetaCustomerID = "orchestrator.v2.customer_id"
|
||||||
|
MetaCustomerFirstName = "orchestrator.v2.customer_first_name"
|
||||||
|
MetaCustomerMiddleName = "orchestrator.v2.customer_middle_name"
|
||||||
|
MetaCustomerLastName = "orchestrator.v2.customer_last_name"
|
||||||
|
MetaCustomerIP = "orchestrator.v2.customer_ip"
|
||||||
|
MetaCustomerZip = "orchestrator.v2.customer_zip"
|
||||||
|
MetaCustomerCountry = "orchestrator.v2.customer_country"
|
||||||
|
MetaCustomerState = "orchestrator.v2.customer_state"
|
||||||
|
MetaCustomerCity = "orchestrator.v2.customer_city"
|
||||||
|
MetaCustomerAddress = "orchestrator.v2.customer_address"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PayoutTarget carries one destination-level payout branch payload for batch execution.
|
||||||
|
type PayoutTarget struct {
|
||||||
|
TargetRef string `json:"target_ref,omitempty"`
|
||||||
|
IntentRef []string `json:"intent_refs,omitempty"`
|
||||||
|
Amount *paymenttypes.Money `json:"amount,omitempty"`
|
||||||
|
Card *model.CardEndpoint `json:"card,omitempty"`
|
||||||
|
Customer *model.Customer `json:"customer,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func EncodePayoutTargets(targets []PayoutTarget) (string, error) {
|
||||||
|
norm := normalizeTargets(targets)
|
||||||
|
if len(norm) == 0 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
data, err := json.Marshal(norm)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(data), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DecodePayoutTargets(raw string) ([]PayoutTarget, error) {
|
||||||
|
raw = strings.TrimSpace(raw)
|
||||||
|
if raw == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
var targets []PayoutTarget
|
||||||
|
if err := json.Unmarshal([]byte(raw), &targets); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return normalizeTargets(targets), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func StepMetadataForTarget(target PayoutTarget, index int) map[string]string {
|
||||||
|
target = normalizeTarget(target, index)
|
||||||
|
out := map[string]string{}
|
||||||
|
|
||||||
|
if ref := strings.TrimSpace(target.TargetRef); ref != "" {
|
||||||
|
out[MetaPayoutTargetRef] = ref
|
||||||
|
}
|
||||||
|
if index >= 0 {
|
||||||
|
out[MetaPayoutTargetIndex] = strconv.Itoa(index + 1)
|
||||||
|
}
|
||||||
|
if target.Amount != nil {
|
||||||
|
if amount := strings.TrimSpace(target.Amount.Amount); amount != "" {
|
||||||
|
out[MetaAmount] = amount
|
||||||
|
}
|
||||||
|
if currency := strings.ToUpper(strings.TrimSpace(target.Amount.Currency)); currency != "" {
|
||||||
|
out[MetaCurrency] = currency
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if target.Card != nil {
|
||||||
|
appendIfNotEmpty(out, MetaCardPan, target.Card.Pan)
|
||||||
|
appendIfNotEmpty(out, MetaCardToken, target.Card.Token)
|
||||||
|
appendIfNotEmpty(out, MetaCardholder, target.Card.Cardholder)
|
||||||
|
appendIfNotEmpty(out, MetaCardholderSurname, target.Card.CardholderSurname)
|
||||||
|
appendIfNotEmpty(out, MetaCardCountry, target.Card.Country)
|
||||||
|
appendIfNotEmpty(out, MetaCardMaskedPan, target.Card.MaskedPan)
|
||||||
|
if target.Card.ExpMonth != 0 {
|
||||||
|
out[MetaCardExpMonth] = strconv.FormatUint(uint64(target.Card.ExpMonth), 10)
|
||||||
|
}
|
||||||
|
if target.Card.ExpYear != 0 {
|
||||||
|
out[MetaCardExpYear] = strconv.FormatUint(uint64(target.Card.ExpYear), 10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if target.Customer != nil {
|
||||||
|
appendIfNotEmpty(out, MetaCustomerID, target.Customer.ID)
|
||||||
|
appendIfNotEmpty(out, MetaCustomerFirstName, target.Customer.FirstName)
|
||||||
|
appendIfNotEmpty(out, MetaCustomerMiddleName, target.Customer.MiddleName)
|
||||||
|
appendIfNotEmpty(out, MetaCustomerLastName, target.Customer.LastName)
|
||||||
|
appendIfNotEmpty(out, MetaCustomerIP, target.Customer.IP)
|
||||||
|
appendIfNotEmpty(out, MetaCustomerZip, target.Customer.Zip)
|
||||||
|
appendIfNotEmpty(out, MetaCustomerCountry, target.Customer.Country)
|
||||||
|
appendIfNotEmpty(out, MetaCustomerState, target.Customer.State)
|
||||||
|
appendIfNotEmpty(out, MetaCustomerCity, target.Customer.City)
|
||||||
|
appendIfNotEmpty(out, MetaCustomerAddress, target.Customer.Address)
|
||||||
|
}
|
||||||
|
if len(out) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func AmountFromMetadata(metadata map[string]string) (*paymenttypes.Money, bool) {
|
||||||
|
if len(metadata) == 0 {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
amount := strings.TrimSpace(metadata[MetaAmount])
|
||||||
|
currency := strings.ToUpper(strings.TrimSpace(metadata[MetaCurrency]))
|
||||||
|
if amount == "" || currency == "" {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return &paymenttypes.Money{Amount: amount, Currency: currency}, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func CardFromMetadata(metadata map[string]string) (*model.CardEndpoint, bool) {
|
||||||
|
if len(metadata) == 0 {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
card := &model.CardEndpoint{
|
||||||
|
Pan: strings.TrimSpace(metadata[MetaCardPan]),
|
||||||
|
Token: strings.TrimSpace(metadata[MetaCardToken]),
|
||||||
|
Cardholder: strings.TrimSpace(metadata[MetaCardholder]),
|
||||||
|
CardholderSurname: strings.TrimSpace(metadata[MetaCardholderSurname]),
|
||||||
|
Country: strings.ToUpper(strings.TrimSpace(metadata[MetaCardCountry])),
|
||||||
|
MaskedPan: strings.TrimSpace(metadata[MetaCardMaskedPan]),
|
||||||
|
}
|
||||||
|
if parsed, ok := parseUint32(metadata[MetaCardExpMonth]); ok {
|
||||||
|
card.ExpMonth = parsed
|
||||||
|
}
|
||||||
|
if parsed, ok := parseUint32(metadata[MetaCardExpYear]); ok {
|
||||||
|
card.ExpYear = parsed
|
||||||
|
}
|
||||||
|
if card.Pan == "" && card.Token == "" && card.Cardholder == "" &&
|
||||||
|
card.CardholderSurname == "" && card.ExpMonth == 0 && card.ExpYear == 0 &&
|
||||||
|
card.Country == "" && card.MaskedPan == "" {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return card, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func CustomerFromMetadata(metadata map[string]string) (*model.Customer, bool) {
|
||||||
|
if len(metadata) == 0 {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
customer := &model.Customer{
|
||||||
|
ID: strings.TrimSpace(metadata[MetaCustomerID]),
|
||||||
|
FirstName: strings.TrimSpace(metadata[MetaCustomerFirstName]),
|
||||||
|
MiddleName: strings.TrimSpace(metadata[MetaCustomerMiddleName]),
|
||||||
|
LastName: strings.TrimSpace(metadata[MetaCustomerLastName]),
|
||||||
|
IP: strings.TrimSpace(metadata[MetaCustomerIP]),
|
||||||
|
Zip: strings.TrimSpace(metadata[MetaCustomerZip]),
|
||||||
|
Country: strings.ToUpper(strings.TrimSpace(metadata[MetaCustomerCountry])),
|
||||||
|
State: strings.TrimSpace(metadata[MetaCustomerState]),
|
||||||
|
City: strings.TrimSpace(metadata[MetaCustomerCity]),
|
||||||
|
Address: strings.TrimSpace(metadata[MetaCustomerAddress]),
|
||||||
|
}
|
||||||
|
if customer.ID == "" && customer.FirstName == "" && customer.MiddleName == "" &&
|
||||||
|
customer.LastName == "" && customer.IP == "" && customer.Zip == "" &&
|
||||||
|
customer.Country == "" && customer.State == "" && customer.City == "" &&
|
||||||
|
customer.Address == "" {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return customer, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeTargets(targets []PayoutTarget) []PayoutTarget {
|
||||||
|
if len(targets) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := make([]PayoutTarget, 0, len(targets))
|
||||||
|
for i := range targets {
|
||||||
|
target := normalizeTarget(targets[i], i)
|
||||||
|
if target.TargetRef == "" && target.Amount == nil && target.Card == nil && target.Customer == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out = append(out, target)
|
||||||
|
}
|
||||||
|
if len(out) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeTarget(target PayoutTarget, index int) PayoutTarget {
|
||||||
|
target.TargetRef = strings.TrimSpace(target.TargetRef)
|
||||||
|
if target.TargetRef == "" {
|
||||||
|
target.TargetRef = "target-" + strconv.Itoa(index+1)
|
||||||
|
}
|
||||||
|
target.IntentRef = normalizeStringSlice(target.IntentRef)
|
||||||
|
if target.Amount != nil {
|
||||||
|
amount := strings.TrimSpace(target.Amount.Amount)
|
||||||
|
currency := strings.ToUpper(strings.TrimSpace(target.Amount.Currency))
|
||||||
|
if amount == "" || currency == "" {
|
||||||
|
target.Amount = nil
|
||||||
|
} else {
|
||||||
|
target.Amount = &paymenttypes.Money{
|
||||||
|
Amount: amount,
|
||||||
|
Currency: currency,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if target.Card != nil {
|
||||||
|
target.Card = &model.CardEndpoint{
|
||||||
|
Pan: strings.TrimSpace(target.Card.Pan),
|
||||||
|
Token: strings.TrimSpace(target.Card.Token),
|
||||||
|
Cardholder: strings.TrimSpace(target.Card.Cardholder),
|
||||||
|
CardholderSurname: strings.TrimSpace(target.Card.CardholderSurname),
|
||||||
|
ExpMonth: target.Card.ExpMonth,
|
||||||
|
ExpYear: target.Card.ExpYear,
|
||||||
|
Country: strings.ToUpper(strings.TrimSpace(target.Card.Country)),
|
||||||
|
MaskedPan: strings.TrimSpace(target.Card.MaskedPan),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if target.Customer != nil {
|
||||||
|
target.Customer = &model.Customer{
|
||||||
|
ID: strings.TrimSpace(target.Customer.ID),
|
||||||
|
FirstName: strings.TrimSpace(target.Customer.FirstName),
|
||||||
|
MiddleName: strings.TrimSpace(target.Customer.MiddleName),
|
||||||
|
LastName: strings.TrimSpace(target.Customer.LastName),
|
||||||
|
IP: strings.TrimSpace(target.Customer.IP),
|
||||||
|
Zip: strings.TrimSpace(target.Customer.Zip),
|
||||||
|
Country: strings.ToUpper(strings.TrimSpace(target.Customer.Country)),
|
||||||
|
State: strings.TrimSpace(target.Customer.State),
|
||||||
|
City: strings.TrimSpace(target.Customer.City),
|
||||||
|
Address: strings.TrimSpace(target.Customer.Address),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return target
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeStringSlice(values []string) []string {
|
||||||
|
if len(values) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
seen := make(map[string]struct{}, len(values))
|
||||||
|
out := make([]string, 0, len(values))
|
||||||
|
for i := range values {
|
||||||
|
token := strings.TrimSpace(values[i])
|
||||||
|
if token == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, exists := seen[token]; exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[token] = struct{}{}
|
||||||
|
out = append(out, token)
|
||||||
|
}
|
||||||
|
if len(out) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendIfNotEmpty(dst map[string]string, key string, value string) {
|
||||||
|
if dst == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
trimmed := strings.TrimSpace(value)
|
||||||
|
if trimmed == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dst[key] = trimmed
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseUint32(raw string) (uint32, bool) {
|
||||||
|
trimmed := strings.TrimSpace(raw)
|
||||||
|
if trimmed == "" {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
parsed, err := strconv.ParseUint(trimmed, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
return uint32(parsed), true
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package erecon
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrStepNotFound = errors.New("step execution not found")
|
||||||
|
ErrAmbiguousStepMatch = errors.New("ambiguous step execution match")
|
||||||
|
ErrStepTransitionInvalid = errors.New("step transition invalid")
|
||||||
|
)
|
||||||
@@ -0,0 +1,326 @@
|
|||||||
|
package erecon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/agg"
|
||||||
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type normalizedEvent struct {
|
||||||
|
stepRef string `bson:"stepRef"`
|
||||||
|
matchRefs []agg.ExternalRef `bson:"matchRefs"`
|
||||||
|
appendRefs []agg.ExternalRef `bson:"appendRefs"`
|
||||||
|
targetState agg.StepState `bson:"targetState"`
|
||||||
|
failureInfo *failureInfo `bson:"failure,omitempty"`
|
||||||
|
forceAggregate *forceAggregate `bson:"forceAggregate,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type failureInfo struct {
|
||||||
|
code string `bson:"code"`
|
||||||
|
msg string `bson:"message"`
|
||||||
|
occurredAt *time.Time `bson:"occurredAt,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type forceAggregate struct {
|
||||||
|
failed bool `bson:"failed"`
|
||||||
|
needsAttention bool `bson:"needsAttention"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeEvent(event Event) (*normalizedEvent, error) {
|
||||||
|
if countPayloads(event) != 1 {
|
||||||
|
return nil, merrors.InvalidArgument("exactly one event payload is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
if event.Gateway != nil {
|
||||||
|
return normalizeGatewayEvent(*event.Gateway)
|
||||||
|
}
|
||||||
|
if event.Ledger != nil {
|
||||||
|
return normalizeLedgerEvent(*event.Ledger)
|
||||||
|
}
|
||||||
|
return normalizeCardEvent(*event.Card)
|
||||||
|
}
|
||||||
|
|
||||||
|
func countPayloads(event Event) int {
|
||||||
|
count := 0
|
||||||
|
if event.Gateway != nil {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
if event.Ledger != nil {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
if event.Card != nil {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *normalizedEvent) failureCodeValue() string {
|
||||||
|
if e == nil || e.failureInfo == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(e.failureInfo.code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *normalizedEvent) failureMsgValue() string {
|
||||||
|
if e == nil || e.failureInfo == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(e.failureInfo.msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *normalizedEvent) occurredAtValue() *time.Time {
|
||||||
|
if e == nil || e.failureInfo == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return e.failureInfo.occurredAt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *normalizedEvent) forceAggregateFailedValue() bool {
|
||||||
|
return e != nil && e.forceAggregate != nil && e.forceAggregate.failed
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *normalizedEvent) forceAggregateNeedsAttentionValue() bool {
|
||||||
|
return e != nil && e.forceAggregate != nil && e.forceAggregate.needsAttention
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildFailureInfo(code, msg string, occurredAt *time.Time) *failureInfo {
|
||||||
|
if code == "" && msg == "" && occurredAt == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &failureInfo{
|
||||||
|
code: code,
|
||||||
|
msg: msg,
|
||||||
|
occurredAt: occurredAt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildForceAggregate(failed, needsAttention bool) *forceAggregate {
|
||||||
|
if !failed && !needsAttention {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &forceAggregate{
|
||||||
|
failed: failed,
|
||||||
|
needsAttention: needsAttention,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeGatewayEvent(src GatewayEvent) (*normalizedEvent, error) {
|
||||||
|
status, ok := normalizeGatewayStatus(src.Status)
|
||||||
|
if !ok {
|
||||||
|
return nil, merrors.InvalidArgument("gateway status is invalid")
|
||||||
|
}
|
||||||
|
|
||||||
|
target, needsAttention := mapFailureTarget(status, src.Retryable)
|
||||||
|
failureCode := strings.TrimSpace(src.FailureCode)
|
||||||
|
failureMsg := strings.TrimSpace(src.FailureMsg)
|
||||||
|
if target == agg.StepStateFailed && failureMsg == "" {
|
||||||
|
failureMsg = "gateway operation failed"
|
||||||
|
}
|
||||||
|
ev := &normalizedEvent{
|
||||||
|
stepRef: strings.TrimSpace(src.StepRef),
|
||||||
|
targetState: target,
|
||||||
|
failureInfo: buildFailureInfo(failureCode, failureMsg, normalizeTimePtr(src.OccurredAt)),
|
||||||
|
forceAggregate: buildForceAggregate(src.TerminalFailure, needsAttention),
|
||||||
|
}
|
||||||
|
ev.matchRefs = normalizeRefList([]agg.ExternalRef{
|
||||||
|
{
|
||||||
|
GatewayInstanceID: strings.TrimSpace(src.GatewayInstanceID),
|
||||||
|
Kind: ExternalRefKindOperation,
|
||||||
|
Ref: strings.TrimSpace(src.OperationRef),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
GatewayInstanceID: strings.TrimSpace(src.GatewayInstanceID),
|
||||||
|
Kind: ExternalRefKindTransfer,
|
||||||
|
Ref: strings.TrimSpace(src.TransferRef),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
ev.appendRefs = cloneRefs(ev.matchRefs)
|
||||||
|
|
||||||
|
if ev.stepRef == "" && len(ev.matchRefs) == 0 {
|
||||||
|
return nil, merrors.InvalidArgument("gateway event must include step_ref or operation/transfer reference")
|
||||||
|
}
|
||||||
|
return ev, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeLedgerEvent(src LedgerEvent) (*normalizedEvent, error) {
|
||||||
|
status, ok := normalizeLedgerStatus(src.Status)
|
||||||
|
if !ok {
|
||||||
|
return nil, merrors.InvalidArgument("ledger status is invalid")
|
||||||
|
}
|
||||||
|
|
||||||
|
target, needsAttention := mapFailureTarget(status, src.Retryable)
|
||||||
|
failureCode := strings.TrimSpace(src.FailureCode)
|
||||||
|
failureMsg := strings.TrimSpace(src.FailureMsg)
|
||||||
|
if target == agg.StepStateFailed && failureMsg == "" {
|
||||||
|
failureMsg = "ledger operation failed"
|
||||||
|
}
|
||||||
|
ev := &normalizedEvent{
|
||||||
|
stepRef: strings.TrimSpace(src.StepRef),
|
||||||
|
targetState: target,
|
||||||
|
failureInfo: buildFailureInfo(failureCode, failureMsg, normalizeTimePtr(src.OccurredAt)),
|
||||||
|
forceAggregate: buildForceAggregate(src.TerminalFailure, needsAttention),
|
||||||
|
}
|
||||||
|
ev.matchRefs = normalizeRefList([]agg.ExternalRef{
|
||||||
|
{
|
||||||
|
Kind: ExternalRefKindLedger,
|
||||||
|
Ref: strings.TrimSpace(src.EntryRef),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
ev.appendRefs = cloneRefs(ev.matchRefs)
|
||||||
|
|
||||||
|
if ev.stepRef == "" && len(ev.matchRefs) == 0 {
|
||||||
|
return nil, merrors.InvalidArgument("ledger event must include step_ref or entry_ref")
|
||||||
|
}
|
||||||
|
return ev, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeCardEvent(src CardEvent) (*normalizedEvent, error) {
|
||||||
|
status, ok := normalizeCardStatus(src.Status)
|
||||||
|
if !ok {
|
||||||
|
return nil, merrors.InvalidArgument("card status is invalid")
|
||||||
|
}
|
||||||
|
|
||||||
|
target, needsAttention := mapFailureTarget(status, src.Retryable)
|
||||||
|
failureCode := strings.TrimSpace(src.FailureCode)
|
||||||
|
failureMsg := strings.TrimSpace(src.FailureMsg)
|
||||||
|
if target == agg.StepStateFailed && failureMsg == "" {
|
||||||
|
failureMsg = "card payout failed"
|
||||||
|
}
|
||||||
|
ev := &normalizedEvent{
|
||||||
|
stepRef: strings.TrimSpace(src.StepRef),
|
||||||
|
targetState: target,
|
||||||
|
failureInfo: buildFailureInfo(failureCode, failureMsg, normalizeTimePtr(src.OccurredAt)),
|
||||||
|
forceAggregate: buildForceAggregate(src.TerminalFailure, needsAttention),
|
||||||
|
}
|
||||||
|
ev.matchRefs = normalizeRefList([]agg.ExternalRef{
|
||||||
|
{
|
||||||
|
GatewayInstanceID: strings.TrimSpace(src.GatewayInstanceID),
|
||||||
|
Kind: ExternalRefKindCardPayout,
|
||||||
|
Ref: strings.TrimSpace(src.PayoutRef),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
ev.appendRefs = cloneRefs(ev.matchRefs)
|
||||||
|
|
||||||
|
if ev.stepRef == "" && len(ev.matchRefs) == 0 {
|
||||||
|
return nil, merrors.InvalidArgument("card event must include step_ref or payout_ref")
|
||||||
|
}
|
||||||
|
return ev, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeGatewayStatus(status GatewayStatus) (GatewayStatus, bool) {
|
||||||
|
switch strings.ToLower(strings.TrimSpace(string(status))) {
|
||||||
|
case string(GatewayStatusCreated):
|
||||||
|
return GatewayStatusCreated, true
|
||||||
|
case string(GatewayStatusProcessing):
|
||||||
|
return GatewayStatusProcessing, true
|
||||||
|
case string(GatewayStatusWaiting):
|
||||||
|
return GatewayStatusWaiting, true
|
||||||
|
case string(GatewayStatusSuccess):
|
||||||
|
return GatewayStatusSuccess, true
|
||||||
|
case string(GatewayStatusFailed):
|
||||||
|
return GatewayStatusFailed, true
|
||||||
|
case string(GatewayStatusCancelled):
|
||||||
|
return GatewayStatusCancelled, true
|
||||||
|
default:
|
||||||
|
return GatewayStatusUnspecified, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeLedgerStatus(status LedgerStatus) (LedgerStatus, bool) {
|
||||||
|
switch strings.ToLower(strings.TrimSpace(string(status))) {
|
||||||
|
case string(LedgerStatusPending):
|
||||||
|
return LedgerStatusPending, true
|
||||||
|
case string(LedgerStatusProcessing):
|
||||||
|
return LedgerStatusProcessing, true
|
||||||
|
case string(LedgerStatusPosted):
|
||||||
|
return LedgerStatusPosted, true
|
||||||
|
case string(LedgerStatusFailed):
|
||||||
|
return LedgerStatusFailed, true
|
||||||
|
case string(LedgerStatusCancelled):
|
||||||
|
return LedgerStatusCancelled, true
|
||||||
|
default:
|
||||||
|
return LedgerStatusUnspecified, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeCardStatus(status CardStatus) (CardStatus, bool) {
|
||||||
|
switch strings.ToLower(strings.TrimSpace(string(status))) {
|
||||||
|
case string(CardStatusCreated):
|
||||||
|
return CardStatusCreated, true
|
||||||
|
case string(CardStatusProcessing):
|
||||||
|
return CardStatusProcessing, true
|
||||||
|
case string(CardStatusWaiting):
|
||||||
|
return CardStatusWaiting, true
|
||||||
|
case string(CardStatusSuccess):
|
||||||
|
return CardStatusSuccess, true
|
||||||
|
case string(CardStatusFailed):
|
||||||
|
return CardStatusFailed, true
|
||||||
|
case string(CardStatusCancelled):
|
||||||
|
return CardStatusCancelled, true
|
||||||
|
default:
|
||||||
|
return CardStatusUnspecified, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapFailureTarget(status any, retryable *bool) (agg.StepState, bool) {
|
||||||
|
switch status {
|
||||||
|
case GatewayStatusCreated, GatewayStatusProcessing, GatewayStatusWaiting:
|
||||||
|
return agg.StepStateRunning, false
|
||||||
|
case LedgerStatusPending, LedgerStatusProcessing:
|
||||||
|
return agg.StepStateRunning, false
|
||||||
|
case CardStatusCreated, CardStatusProcessing, CardStatusWaiting:
|
||||||
|
return agg.StepStateRunning, false
|
||||||
|
case GatewayStatusSuccess, LedgerStatusPosted, CardStatusSuccess:
|
||||||
|
return agg.StepStateCompleted, false
|
||||||
|
case GatewayStatusFailed, GatewayStatusCancelled, LedgerStatusFailed, LedgerStatusCancelled, CardStatusFailed, CardStatusCancelled:
|
||||||
|
if retryable != nil && !*retryable {
|
||||||
|
return agg.StepStateNeedsAttention, true
|
||||||
|
}
|
||||||
|
return agg.StepStateFailed, false
|
||||||
|
default:
|
||||||
|
return agg.StepStateUnspecified, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeTimePtr(ts *time.Time) *time.Time {
|
||||||
|
if ts == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
val := ts.UTC()
|
||||||
|
return &val
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeRefList(refs []agg.ExternalRef) []agg.ExternalRef {
|
||||||
|
if len(refs) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := make([]agg.ExternalRef, 0, len(refs))
|
||||||
|
seen := map[string]struct{}{}
|
||||||
|
for i := range refs {
|
||||||
|
ref := refs[i]
|
||||||
|
ref.GatewayInstanceID = strings.TrimSpace(ref.GatewayInstanceID)
|
||||||
|
ref.Kind = strings.TrimSpace(ref.Kind)
|
||||||
|
ref.Ref = strings.TrimSpace(ref.Ref)
|
||||||
|
if ref.Kind == "" || ref.Ref == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
key := ref.GatewayInstanceID + "\x1f" + strings.ToLower(ref.Kind) + "\x1f" + strings.ToLower(ref.Ref)
|
||||||
|
if _, ok := seen[key]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[key] = struct{}{}
|
||||||
|
out = append(out, ref)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func cloneRefs(refs []agg.ExternalRef) []agg.ExternalRef {
|
||||||
|
if len(refs) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := make([]agg.ExternalRef, 0, len(refs))
|
||||||
|
out = append(out, refs...)
|
||||||
|
return out
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
package erecon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/agg"
|
||||||
|
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/xerr"
|
||||||
|
)
|
||||||
|
|
||||||
|
func findStepIndex(payment *agg.Payment, event *normalizedEvent) (int, error) {
|
||||||
|
if payment == nil {
|
||||||
|
return -1, ErrStepNotFound
|
||||||
|
}
|
||||||
|
if event == nil {
|
||||||
|
return -1, ErrStepNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
if stepRef := strings.TrimSpace(event.stepRef); stepRef != "" {
|
||||||
|
for i := range payment.StepExecutions {
|
||||||
|
if strings.EqualFold(strings.TrimSpace(payment.StepExecutions[i].StepRef), stepRef) {
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1, xerr.Wrapf(ErrStepNotFound, "step_ref=%s", stepRef)
|
||||||
|
}
|
||||||
|
|
||||||
|
matches := make([]int, 0, 1)
|
||||||
|
for i := range payment.StepExecutions {
|
||||||
|
if stepMatchesAnyRef(payment.StepExecutions[i], event.matchRefs) {
|
||||||
|
matches = append(matches, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch len(matches) {
|
||||||
|
case 0:
|
||||||
|
return -1, ErrStepNotFound
|
||||||
|
case 1:
|
||||||
|
return matches[0], nil
|
||||||
|
default:
|
||||||
|
return -1, ErrAmbiguousStepMatch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stepMatchesAnyRef(step agg.StepExecution, refs []agg.ExternalRef) bool {
|
||||||
|
if len(refs) == 0 || len(step.ExternalRefs) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i := range refs {
|
||||||
|
if hasExternalRef(step.ExternalRefs, refs[i]) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasExternalRef(existing []agg.ExternalRef, ref agg.ExternalRef) bool {
|
||||||
|
kind := strings.TrimSpace(ref.Kind)
|
||||||
|
value := strings.TrimSpace(ref.Ref)
|
||||||
|
gatewayID := strings.TrimSpace(ref.GatewayInstanceID)
|
||||||
|
if kind == "" || value == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i := range existing {
|
||||||
|
candidate := existing[i]
|
||||||
|
if !strings.EqualFold(strings.TrimSpace(candidate.Kind), kind) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !strings.EqualFold(strings.TrimSpace(candidate.Ref), value) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if gatewayID != "" && strings.TrimSpace(candidate.GatewayInstanceID) != "" && !strings.EqualFold(strings.TrimSpace(candidate.GatewayInstanceID), gatewayID) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func mergeExternalRefs(existing []agg.ExternalRef, additions []agg.ExternalRef) ([]agg.ExternalRef, bool) {
|
||||||
|
if len(additions) == 0 {
|
||||||
|
return cloneRefs(existing), false
|
||||||
|
}
|
||||||
|
out := cloneRefs(existing)
|
||||||
|
changed := false
|
||||||
|
for i := range additions {
|
||||||
|
ref := additions[i]
|
||||||
|
if hasExternalRef(out, ref) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out = append(out, agg.ExternalRef{
|
||||||
|
GatewayInstanceID: strings.TrimSpace(ref.GatewayInstanceID),
|
||||||
|
Kind: strings.TrimSpace(ref.Kind),
|
||||||
|
Ref: strings.TrimSpace(ref.Ref),
|
||||||
|
})
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
return out, changed
|
||||||
|
}
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
package erecon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/agg"
|
||||||
|
|
||||||
|
"github.com/tech/sendico/pkg/mlogger"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Reconciler applies external async events to payment runtime state.
|
||||||
|
type Reconciler interface {
|
||||||
|
Reconcile(in Input) (*Output, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input is the reconciliation payload.
|
||||||
|
type Input struct {
|
||||||
|
Payment *agg.Payment
|
||||||
|
Event Event
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output is the reconciliation result.
|
||||||
|
type Output struct {
|
||||||
|
Payment *agg.Payment
|
||||||
|
MatchedStepRef string
|
||||||
|
StepChanged bool
|
||||||
|
AggregateChanged bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event is one transport-agnostic external event envelope.
|
||||||
|
// Exactly one payload must be set.
|
||||||
|
type Event struct {
|
||||||
|
Gateway *GatewayEvent
|
||||||
|
Ledger *LedgerEvent
|
||||||
|
Card *CardEvent
|
||||||
|
}
|
||||||
|
|
||||||
|
// GatewayStatus is gateway operation lifecycle status.
|
||||||
|
type GatewayStatus string
|
||||||
|
|
||||||
|
const (
|
||||||
|
GatewayStatusUnspecified GatewayStatus = "unspecified"
|
||||||
|
GatewayStatusCreated GatewayStatus = "created"
|
||||||
|
GatewayStatusProcessing GatewayStatus = "processing"
|
||||||
|
GatewayStatusWaiting GatewayStatus = "waiting"
|
||||||
|
GatewayStatusSuccess GatewayStatus = "success"
|
||||||
|
GatewayStatusFailed GatewayStatus = "failed"
|
||||||
|
GatewayStatusCancelled GatewayStatus = "cancelled"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LedgerStatus is ledger operation lifecycle status.
|
||||||
|
type LedgerStatus string
|
||||||
|
|
||||||
|
const (
|
||||||
|
LedgerStatusUnspecified LedgerStatus = "unspecified"
|
||||||
|
LedgerStatusPending LedgerStatus = "pending"
|
||||||
|
LedgerStatusProcessing LedgerStatus = "processing"
|
||||||
|
LedgerStatusPosted LedgerStatus = "posted"
|
||||||
|
LedgerStatusFailed LedgerStatus = "failed"
|
||||||
|
LedgerStatusCancelled LedgerStatus = "cancelled"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CardStatus is card payout lifecycle status.
|
||||||
|
type CardStatus string
|
||||||
|
|
||||||
|
const (
|
||||||
|
CardStatusUnspecified CardStatus = "unspecified"
|
||||||
|
CardStatusCreated CardStatus = "created"
|
||||||
|
CardStatusProcessing CardStatus = "processing"
|
||||||
|
CardStatusWaiting CardStatus = "waiting"
|
||||||
|
CardStatusSuccess CardStatus = "success"
|
||||||
|
CardStatusFailed CardStatus = "failed"
|
||||||
|
CardStatusCancelled CardStatus = "cancelled"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GatewayEvent is one async event from gateway execution flow.
|
||||||
|
type GatewayEvent struct {
|
||||||
|
StepRef string
|
||||||
|
OperationRef string
|
||||||
|
TransferRef string
|
||||||
|
GatewayInstanceID string
|
||||||
|
Status GatewayStatus
|
||||||
|
FailureCode string
|
||||||
|
FailureMsg string
|
||||||
|
Retryable *bool
|
||||||
|
TerminalFailure bool
|
||||||
|
OccurredAt *time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// LedgerEvent is one async event from ledger flow.
|
||||||
|
type LedgerEvent struct {
|
||||||
|
StepRef string
|
||||||
|
EntryRef string
|
||||||
|
Status LedgerStatus
|
||||||
|
FailureCode string
|
||||||
|
FailureMsg string
|
||||||
|
Retryable *bool
|
||||||
|
TerminalFailure bool
|
||||||
|
OccurredAt *time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// CardEvent is one async event from card payout flow.
|
||||||
|
type CardEvent struct {
|
||||||
|
StepRef string
|
||||||
|
PayoutRef string
|
||||||
|
GatewayInstanceID string
|
||||||
|
Status CardStatus
|
||||||
|
FailureCode string
|
||||||
|
FailureMsg string
|
||||||
|
Retryable *bool
|
||||||
|
TerminalFailure bool
|
||||||
|
OccurredAt *time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
ExternalRefKindOperation = "operation_ref"
|
||||||
|
ExternalRefKindTransfer = "transfer_ref"
|
||||||
|
ExternalRefKindLedger = "ledger_entry_ref"
|
||||||
|
ExternalRefKindCardPayout = "card_payout_ref"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Dependencies configures reconciliation service integrations.
|
||||||
|
type Dependencies struct {
|
||||||
|
Logger mlogger.Logger
|
||||||
|
Now func() time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(deps ...Dependencies) Reconciler {
|
||||||
|
var dep Dependencies
|
||||||
|
if len(deps) > 0 {
|
||||||
|
dep = deps[0]
|
||||||
|
}
|
||||||
|
logger := dep.Logger
|
||||||
|
if logger == nil {
|
||||||
|
logger = zap.NewNop()
|
||||||
|
}
|
||||||
|
now := dep.Now
|
||||||
|
if now == nil {
|
||||||
|
now = defaultNow
|
||||||
|
}
|
||||||
|
return &svc{
|
||||||
|
logger: logger.Named("reconciler"),
|
||||||
|
now: now,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
package erecon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/agg"
|
||||||
|
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/ostate"
|
||||||
|
)
|
||||||
|
|
||||||
|
func reduceAggregateState(payment *agg.Payment, event *normalizedEvent, sm ostate.StateMachine) (bool, error) {
|
||||||
|
if payment == nil || sm == nil {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
target := deriveAggregateTarget(payment, event, sm)
|
||||||
|
return applyAggregateTarget(payment, target, sm)
|
||||||
|
}
|
||||||
|
|
||||||
|
func deriveAggregateTarget(payment *agg.Payment, event *normalizedEvent, sm ostate.StateMachine) agg.State {
|
||||||
|
if payment == nil {
|
||||||
|
return agg.StateUnspecified
|
||||||
|
}
|
||||||
|
if event != nil && event.forceAggregateFailedValue() {
|
||||||
|
return agg.StateFailed
|
||||||
|
}
|
||||||
|
|
||||||
|
hasNeedsAttention := false
|
||||||
|
hasWork := false
|
||||||
|
allTerminalSuccessOrSkipped := len(payment.StepExecutions) > 0
|
||||||
|
|
||||||
|
for i := range payment.StepExecutions {
|
||||||
|
state := payment.StepExecutions[i].State
|
||||||
|
switch state {
|
||||||
|
case agg.StepStateCompleted, agg.StepStateSkipped:
|
||||||
|
hasWork = true
|
||||||
|
case agg.StepStatePending, agg.StepStateUnspecified:
|
||||||
|
allTerminalSuccessOrSkipped = false
|
||||||
|
case agg.StepStateNeedsAttention:
|
||||||
|
hasWork = true
|
||||||
|
hasNeedsAttention = true
|
||||||
|
allTerminalSuccessOrSkipped = false
|
||||||
|
case agg.StepStateRunning, agg.StepStateFailed:
|
||||||
|
hasWork = true
|
||||||
|
allTerminalSuccessOrSkipped = false
|
||||||
|
default:
|
||||||
|
allTerminalSuccessOrSkipped = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if allTerminalSuccessOrSkipped {
|
||||||
|
return agg.StateSettled
|
||||||
|
}
|
||||||
|
if hasNeedsAttention || (event != nil && event.forceAggregateNeedsAttentionValue()) {
|
||||||
|
return agg.StateNeedsAttention
|
||||||
|
}
|
||||||
|
if hasWork {
|
||||||
|
return agg.StateExecuting
|
||||||
|
}
|
||||||
|
if sm.IsAggregateTerminal(payment.State) {
|
||||||
|
return payment.State
|
||||||
|
}
|
||||||
|
return agg.StateCreated
|
||||||
|
}
|
||||||
|
|
||||||
|
func applyAggregateTarget(payment *agg.Payment, target agg.State, sm ostate.StateMachine) (bool, error) {
|
||||||
|
if payment == nil || sm == nil {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
current := payment.State
|
||||||
|
if current == target {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
if sm.IsAggregateTerminal(current) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
original := current
|
||||||
|
for i := 0; i < 6 && current != target; i++ {
|
||||||
|
if sm.EnsureAggregateTransition(current, target) == nil {
|
||||||
|
current = target
|
||||||
|
break
|
||||||
|
}
|
||||||
|
next, ok := nextAggregateHop(current, target)
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if sm.EnsureAggregateTransition(current, next) != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
current = next
|
||||||
|
}
|
||||||
|
if current != target {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
payment.State = current
|
||||||
|
return payment.State != original, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func nextAggregateHop(current, target agg.State) (agg.State, bool) {
|
||||||
|
switch current {
|
||||||
|
case agg.StateUnspecified:
|
||||||
|
return agg.StateCreated, true
|
||||||
|
case agg.StateCreated:
|
||||||
|
if target == agg.StateFailed {
|
||||||
|
return agg.StateFailed, true
|
||||||
|
}
|
||||||
|
if target == agg.StateExecuting {
|
||||||
|
return agg.StateExecuting, true
|
||||||
|
}
|
||||||
|
return agg.StateExecuting, true
|
||||||
|
case agg.StateExecuting:
|
||||||
|
return target, true
|
||||||
|
case agg.StateNeedsAttention:
|
||||||
|
if target == agg.StateCreated {
|
||||||
|
return agg.StateExecuting, true
|
||||||
|
}
|
||||||
|
return target, true
|
||||||
|
default:
|
||||||
|
return agg.StateUnspecified, false
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,259 @@
|
|||||||
|
package erecon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/agg"
|
||||||
|
|
||||||
|
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/ostate"
|
||||||
|
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/xerr"
|
||||||
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
|
"github.com/tech/sendico/pkg/mlogger"
|
||||||
|
"go.mongodb.org/mongo-driver/v2/bson"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type svc struct {
|
||||||
|
logger mlogger.Logger
|
||||||
|
now func() time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultNow() time.Time {
|
||||||
|
return time.Now().UTC()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *svc) Reconcile(in Input) (out *Output, err error) {
|
||||||
|
logger := s.logger
|
||||||
|
paymentRef := ""
|
||||||
|
if in.Payment != nil {
|
||||||
|
paymentRef = strings.TrimSpace(in.Payment.PaymentRef)
|
||||||
|
}
|
||||||
|
logger.Debug("Starting Reconcile",
|
||||||
|
zap.String("payment_ref", paymentRef),
|
||||||
|
zap.String("event_source", eventSource(in.Event)),
|
||||||
|
)
|
||||||
|
defer func(start time.Time) {
|
||||||
|
fields := []zap.Field{zap.Int64("duration_ms", time.Since(start).Milliseconds())}
|
||||||
|
if out != nil {
|
||||||
|
fields = append(fields,
|
||||||
|
zap.String("matched_step_ref", strings.TrimSpace(out.MatchedStepRef)),
|
||||||
|
zap.Bool("step_changed", out.StepChanged),
|
||||||
|
zap.Bool("aggregate_changed", out.AggregateChanged),
|
||||||
|
)
|
||||||
|
if out.Payment != nil {
|
||||||
|
fields = append(fields,
|
||||||
|
zap.String("payment_state", string(out.Payment.State)),
|
||||||
|
zap.Uint64("version", out.Payment.Version),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn("Failed to reconcile", append(fields, zap.Error(err))...)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Debug("Completed Reconcile", fields...)
|
||||||
|
}(time.Now())
|
||||||
|
|
||||||
|
if in.Payment == nil {
|
||||||
|
return nil, merrors.InvalidArgument("payment is required")
|
||||||
|
}
|
||||||
|
if len(in.Payment.StepExecutions) == 0 {
|
||||||
|
return nil, merrors.InvalidArgument("payment.step_executions are required")
|
||||||
|
}
|
||||||
|
|
||||||
|
event, err := normalizeEvent(in.Event)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
payment, err := clonePayment(in.Payment)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
idx, err := findStepIndex(payment, event)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sm := ostate.New(ostate.Dependencies{Logger: logger.Named("ostate")})
|
||||||
|
stepChanged, err := s.applyStepEvent(&payment.StepExecutions[idx], event, sm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
aggregateChanged, err := reduceAggregateState(payment, event, sm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if stepChanged || aggregateChanged {
|
||||||
|
payment.Version++
|
||||||
|
payment.UpdatedAt = s.now().UTC()
|
||||||
|
}
|
||||||
|
|
||||||
|
out = &Output{
|
||||||
|
Payment: payment,
|
||||||
|
MatchedStepRef: payment.StepExecutions[idx].StepRef,
|
||||||
|
StepChanged: stepChanged,
|
||||||
|
AggregateChanged: aggregateChanged,
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *svc) applyStepEvent(step *agg.StepExecution, event *normalizedEvent, sm ostate.StateMachine) (bool, error) {
|
||||||
|
if step == nil || event == nil {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
changed := false
|
||||||
|
out := *step
|
||||||
|
|
||||||
|
refs, refsChanged := mergeExternalRefs(out.ExternalRefs, event.appendRefs)
|
||||||
|
if refsChanged {
|
||||||
|
out.ExternalRefs = refs
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
target := event.targetState
|
||||||
|
if target == agg.StepStateUnspecified {
|
||||||
|
*step = out
|
||||||
|
return changed, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if out.State == target {
|
||||||
|
changed = s.applyStepDiagnostics(&out, event) || changed
|
||||||
|
*step = out
|
||||||
|
return changed, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if sm.IsStepTerminal(out.State) {
|
||||||
|
*step = out
|
||||||
|
return changed, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
next, transitionChanged, err := transitionStepState(out, target, sm)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
out = next
|
||||||
|
changed = changed || transitionChanged
|
||||||
|
changed = s.applyStepDiagnostics(&out, event) || changed
|
||||||
|
|
||||||
|
*step = out
|
||||||
|
return changed, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func transitionStepState(step agg.StepExecution, target agg.StepState, sm ostate.StateMachine) (agg.StepExecution, bool, error) {
|
||||||
|
if step.State == target {
|
||||||
|
return step, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if sm.EnsureStepTransition(step.State, target) == nil {
|
||||||
|
step.State = target
|
||||||
|
return step, true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
original := step.State
|
||||||
|
bridge := []agg.StepState{agg.StepStateRunning, target}
|
||||||
|
for i := range bridge {
|
||||||
|
next := bridge[i]
|
||||||
|
if step.State == next {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if sm.EnsureStepTransition(step.State, next) != nil {
|
||||||
|
return step, false, xerr.Wrapf(ErrStepTransitionInvalid, "%s -> %s", original, target)
|
||||||
|
}
|
||||||
|
step.State = next
|
||||||
|
}
|
||||||
|
return step, step.State != original, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *svc) applyStepDiagnostics(step *agg.StepExecution, event *normalizedEvent) bool {
|
||||||
|
if step == nil || event == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
now := s.now().UTC()
|
||||||
|
at := now
|
||||||
|
if eventAt := event.occurredAtValue(); eventAt != nil {
|
||||||
|
at = eventAt.UTC()
|
||||||
|
}
|
||||||
|
|
||||||
|
changed := false
|
||||||
|
switch step.State {
|
||||||
|
case agg.StepStateRunning:
|
||||||
|
if step.StartedAt == nil {
|
||||||
|
step.StartedAt = &at
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
if step.CompletedAt != nil {
|
||||||
|
step.CompletedAt = nil
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
if step.FailureCode != "" || step.FailureMsg != "" {
|
||||||
|
step.FailureCode = ""
|
||||||
|
step.FailureMsg = ""
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
case agg.StepStateCompleted:
|
||||||
|
if step.StartedAt == nil {
|
||||||
|
step.StartedAt = &at
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
if step.CompletedAt == nil || !step.CompletedAt.Equal(at) {
|
||||||
|
step.CompletedAt = &at
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
if step.FailureCode != "" || step.FailureMsg != "" {
|
||||||
|
step.FailureCode = ""
|
||||||
|
step.FailureMsg = ""
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
case agg.StepStateFailed, agg.StepStateNeedsAttention:
|
||||||
|
if step.StartedAt == nil {
|
||||||
|
step.StartedAt = &at
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
if step.CompletedAt == nil || !step.CompletedAt.Equal(at) {
|
||||||
|
step.CompletedAt = &at
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
fc := event.failureCodeValue()
|
||||||
|
fm := event.failureMsgValue()
|
||||||
|
if step.FailureCode != fc || step.FailureMsg != fm {
|
||||||
|
step.FailureCode = fc
|
||||||
|
step.FailureMsg = fm
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return changed
|
||||||
|
}
|
||||||
|
|
||||||
|
func clonePayment(payment *agg.Payment) (*agg.Payment, error) {
|
||||||
|
data, err := bson.Marshal(payment)
|
||||||
|
if err != nil {
|
||||||
|
return nil, merrors.Internal("payment clone failed")
|
||||||
|
}
|
||||||
|
var out agg.Payment
|
||||||
|
if err := bson.Unmarshal(data, &out); err != nil {
|
||||||
|
return nil, merrors.Internal("payment clone failed")
|
||||||
|
}
|
||||||
|
return &out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func eventSource(event Event) string {
|
||||||
|
switch {
|
||||||
|
case event.Gateway != nil:
|
||||||
|
return "gateway"
|
||||||
|
case event.Ledger != nil:
|
||||||
|
return "ledger"
|
||||||
|
case event.Card != nil:
|
||||||
|
return "card"
|
||||||
|
default:
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,366 @@
|
|||||||
|
package erecon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/agg"
|
||||||
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestReconcile_GatewayWaiting_UpdatesRunningAndRefs(t *testing.T) {
|
||||||
|
now := time.Date(2026, time.January, 10, 11, 12, 13, 0, time.UTC)
|
||||||
|
reconciler := New(Dependencies{Logger: zap.NewNop(), Now: func() time.Time { return now }})
|
||||||
|
|
||||||
|
in := &agg.Payment{
|
||||||
|
PaymentRef: "p1",
|
||||||
|
State: agg.StateCreated,
|
||||||
|
Version: 7,
|
||||||
|
StepExecutions: []agg.StepExecution{
|
||||||
|
{StepRef: "s1", StepCode: "send", State: agg.StepStatePending, Attempt: 1},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := reconciler.Reconcile(Input{
|
||||||
|
Payment: in,
|
||||||
|
Event: Event{
|
||||||
|
Gateway: &GatewayEvent{
|
||||||
|
StepRef: "s1",
|
||||||
|
OperationRef: "op-1",
|
||||||
|
TransferRef: "tx-1",
|
||||||
|
GatewayInstanceID: "gw-1",
|
||||||
|
Status: GatewayStatusWaiting,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Reconcile returned error: %v", err)
|
||||||
|
}
|
||||||
|
if !out.StepChanged {
|
||||||
|
t.Fatal("expected step_changed")
|
||||||
|
}
|
||||||
|
if !out.AggregateChanged {
|
||||||
|
t.Fatal("expected aggregate_changed")
|
||||||
|
}
|
||||||
|
|
||||||
|
got := out.Payment.StepExecutions[0]
|
||||||
|
if got.State != agg.StepStateRunning {
|
||||||
|
t.Fatalf("step state mismatch: got=%q want=%q", got.State, agg.StepStateRunning)
|
||||||
|
}
|
||||||
|
if got.StartedAt == nil || !got.StartedAt.Equal(now) {
|
||||||
|
t.Fatalf("started_at mismatch: got=%v want=%v", got.StartedAt, now)
|
||||||
|
}
|
||||||
|
if got.CompletedAt != nil {
|
||||||
|
t.Fatalf("expected nil completed_at, got %v", got.CompletedAt)
|
||||||
|
}
|
||||||
|
if !hasRef(got.ExternalRefs, agg.ExternalRef{GatewayInstanceID: "gw-1", Kind: ExternalRefKindOperation, Ref: "op-1"}) {
|
||||||
|
t.Fatalf("expected operation_ref external reference")
|
||||||
|
}
|
||||||
|
if !hasRef(got.ExternalRefs, agg.ExternalRef{GatewayInstanceID: "gw-1", Kind: ExternalRefKindTransfer, Ref: "tx-1"}) {
|
||||||
|
t.Fatalf("expected transfer_ref external reference")
|
||||||
|
}
|
||||||
|
if out.Payment.State != agg.StateExecuting {
|
||||||
|
t.Fatalf("aggregate state mismatch: got=%q want=%q", out.Payment.State, agg.StateExecuting)
|
||||||
|
}
|
||||||
|
if out.Payment.Version != 8 {
|
||||||
|
t.Fatalf("version mismatch: got=%d want=%d", out.Payment.Version, 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
if in.StepExecutions[0].State != agg.StepStatePending {
|
||||||
|
t.Fatalf("input payment was mutated")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReconcile_GatewaySuccess_SettlesPayment(t *testing.T) {
|
||||||
|
now := time.Date(2026, time.January, 10, 11, 12, 13, 0, time.UTC)
|
||||||
|
reconciler := New(Dependencies{Logger: zap.NewNop(), Now: func() time.Time { return now }})
|
||||||
|
|
||||||
|
out, err := reconciler.Reconcile(Input{
|
||||||
|
Payment: &agg.Payment{
|
||||||
|
PaymentRef: "p1",
|
||||||
|
State: agg.StateCreated,
|
||||||
|
Version: 1,
|
||||||
|
StepExecutions: []agg.StepExecution{
|
||||||
|
{StepRef: "s1", StepCode: "observe", State: agg.StepStatePending, Attempt: 1},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Event: Event{
|
||||||
|
Gateway: &GatewayEvent{
|
||||||
|
StepRef: "s1",
|
||||||
|
Status: GatewayStatusSuccess,
|
||||||
|
OperationRef: "op-1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Reconcile returned error: %v", err)
|
||||||
|
}
|
||||||
|
step := out.Payment.StepExecutions[0]
|
||||||
|
if step.State != agg.StepStateCompleted {
|
||||||
|
t.Fatalf("step state mismatch: got=%q want=%q", step.State, agg.StepStateCompleted)
|
||||||
|
}
|
||||||
|
if step.CompletedAt == nil || !step.CompletedAt.Equal(now) {
|
||||||
|
t.Fatalf("completed_at mismatch: got=%v want=%v", step.CompletedAt, now)
|
||||||
|
}
|
||||||
|
if out.Payment.State != agg.StateSettled {
|
||||||
|
t.Fatalf("aggregate state mismatch: got=%q want=%q", out.Payment.State, agg.StateSettled)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReconcile_GatewayFailureMapping(t *testing.T) {
|
||||||
|
now := time.Date(2026, time.January, 10, 11, 12, 13, 0, time.UTC)
|
||||||
|
reconciler := New(Dependencies{Logger: zap.NewNop(), Now: func() time.Time { return now }})
|
||||||
|
|
||||||
|
retryable := true
|
||||||
|
out, err := reconciler.Reconcile(Input{
|
||||||
|
Payment: &agg.Payment{
|
||||||
|
PaymentRef: "p1",
|
||||||
|
State: agg.StateExecuting,
|
||||||
|
StepExecutions: []agg.StepExecution{
|
||||||
|
{StepRef: "s1", StepCode: "observe", State: agg.StepStateRunning, Attempt: 1},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Event: Event{
|
||||||
|
Gateway: &GatewayEvent{
|
||||||
|
StepRef: "s1",
|
||||||
|
Status: GatewayStatusFailed,
|
||||||
|
Retryable: &retryable,
|
||||||
|
FailureCode: "gw_timeout",
|
||||||
|
FailureMsg: "timeout",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Reconcile returned error: %v", err)
|
||||||
|
}
|
||||||
|
step := out.Payment.StepExecutions[0]
|
||||||
|
if step.State != agg.StepStateFailed {
|
||||||
|
t.Fatalf("step state mismatch: got=%q want=%q", step.State, agg.StepStateFailed)
|
||||||
|
}
|
||||||
|
if step.FailureCode != "gw_timeout" || step.FailureMsg != "timeout" {
|
||||||
|
t.Fatalf("failure details mismatch: code=%q msg=%q", step.FailureCode, step.FailureMsg)
|
||||||
|
}
|
||||||
|
if out.Payment.State != agg.StateExecuting {
|
||||||
|
t.Fatalf("aggregate state mismatch: got=%q want=%q", out.Payment.State, agg.StateExecuting)
|
||||||
|
}
|
||||||
|
|
||||||
|
nonRetryable := false
|
||||||
|
out, err = reconciler.Reconcile(Input{
|
||||||
|
Payment: &agg.Payment{
|
||||||
|
PaymentRef: "p2",
|
||||||
|
State: agg.StateExecuting,
|
||||||
|
StepExecutions: []agg.StepExecution{
|
||||||
|
{StepRef: "s1", StepCode: "observe", State: agg.StepStateRunning, Attempt: 1},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Event: Event{
|
||||||
|
Gateway: &GatewayEvent{
|
||||||
|
StepRef: "s1",
|
||||||
|
Status: GatewayStatusFailed,
|
||||||
|
Retryable: &nonRetryable,
|
||||||
|
FailureCode: "gw_rejected",
|
||||||
|
FailureMsg: "rejected",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Reconcile returned error: %v", err)
|
||||||
|
}
|
||||||
|
step = out.Payment.StepExecutions[0]
|
||||||
|
if step.State != agg.StepStateNeedsAttention {
|
||||||
|
t.Fatalf("step state mismatch: got=%q want=%q", step.State, agg.StepStateNeedsAttention)
|
||||||
|
}
|
||||||
|
if out.Payment.State != agg.StateNeedsAttention {
|
||||||
|
t.Fatalf("aggregate state mismatch: got=%q want=%q", out.Payment.State, agg.StateNeedsAttention)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReconcile_LedgerTerminalFailure_ForcesAggregateFailed(t *testing.T) {
|
||||||
|
now := time.Date(2026, time.January, 10, 11, 12, 13, 0, time.UTC)
|
||||||
|
reconciler := New(Dependencies{Logger: zap.NewNop(), Now: func() time.Time { return now }})
|
||||||
|
|
||||||
|
out, err := reconciler.Reconcile(Input{
|
||||||
|
Payment: &agg.Payment{
|
||||||
|
PaymentRef: "p1",
|
||||||
|
State: agg.StateExecuting,
|
||||||
|
StepExecutions: []agg.StepExecution{
|
||||||
|
{
|
||||||
|
StepRef: "s1", StepCode: "ledger.debit", State: agg.StepStateRunning, Attempt: 1,
|
||||||
|
ExternalRefs: []agg.ExternalRef{{Kind: ExternalRefKindLedger, Ref: "entry-1"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Event: Event{
|
||||||
|
Ledger: &LedgerEvent{
|
||||||
|
EntryRef: "entry-1",
|
||||||
|
Status: LedgerStatusFailed,
|
||||||
|
FailureCode: "ledger_declined",
|
||||||
|
TerminalFailure: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Reconcile returned error: %v", err)
|
||||||
|
}
|
||||||
|
if out.Payment.StepExecutions[0].State != agg.StepStateFailed {
|
||||||
|
t.Fatalf("step state mismatch: got=%q want=%q", out.Payment.StepExecutions[0].State, agg.StepStateFailed)
|
||||||
|
}
|
||||||
|
if out.Payment.State != agg.StateFailed {
|
||||||
|
t.Fatalf("aggregate state mismatch: got=%q want=%q", out.Payment.State, agg.StateFailed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReconcile_CardMatchByExternalRef(t *testing.T) {
|
||||||
|
reconciler := New(Dependencies{Logger: zap.NewNop(), Now: defaultNow})
|
||||||
|
|
||||||
|
out, err := reconciler.Reconcile(Input{
|
||||||
|
Payment: &agg.Payment{
|
||||||
|
PaymentRef: "p1",
|
||||||
|
State: agg.StateExecuting,
|
||||||
|
StepExecutions: []agg.StepExecution{
|
||||||
|
{
|
||||||
|
StepRef: "s1", StepCode: "card.observe", State: agg.StepStateRunning, Attempt: 1,
|
||||||
|
ExternalRefs: []agg.ExternalRef{
|
||||||
|
{Kind: ExternalRefKindCardPayout, Ref: "payout-1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Event: Event{
|
||||||
|
Card: &CardEvent{
|
||||||
|
PayoutRef: "payout-1",
|
||||||
|
Status: CardStatusSuccess,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Reconcile returned error: %v", err)
|
||||||
|
}
|
||||||
|
if out.MatchedStepRef != "s1" {
|
||||||
|
t.Fatalf("matched step mismatch: got=%q want=%q", out.MatchedStepRef, "s1")
|
||||||
|
}
|
||||||
|
if out.Payment.StepExecutions[0].State != agg.StepStateCompleted {
|
||||||
|
t.Fatalf("step state mismatch: got=%q want=%q", out.Payment.StepExecutions[0].State, agg.StepStateCompleted)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReconcile_MatchingErrors(t *testing.T) {
|
||||||
|
reconciler := New(Dependencies{Logger: zap.NewNop(), Now: defaultNow})
|
||||||
|
|
||||||
|
_, err := reconciler.Reconcile(Input{
|
||||||
|
Payment: &agg.Payment{
|
||||||
|
PaymentRef: "p1",
|
||||||
|
State: agg.StateExecuting,
|
||||||
|
StepExecutions: []agg.StepExecution{
|
||||||
|
{
|
||||||
|
StepRef: "s1", StepCode: "a", State: agg.StepStateRunning,
|
||||||
|
ExternalRefs: []agg.ExternalRef{{Kind: ExternalRefKindTransfer, Ref: "tx-1"}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StepRef: "s2", StepCode: "b", State: agg.StepStateRunning,
|
||||||
|
ExternalRefs: []agg.ExternalRef{{Kind: ExternalRefKindTransfer, Ref: "tx-1"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Event: Event{
|
||||||
|
Gateway: &GatewayEvent{
|
||||||
|
TransferRef: "tx-1",
|
||||||
|
Status: GatewayStatusSuccess,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if !errors.Is(err, ErrAmbiguousStepMatch) {
|
||||||
|
t.Fatalf("expected ErrAmbiguousStepMatch, got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = reconciler.Reconcile(Input{
|
||||||
|
Payment: &agg.Payment{
|
||||||
|
PaymentRef: "p1",
|
||||||
|
State: agg.StateExecuting,
|
||||||
|
StepExecutions: []agg.StepExecution{{StepRef: "s1", StepCode: "a", State: agg.StepStateRunning}},
|
||||||
|
},
|
||||||
|
Event: Event{
|
||||||
|
Gateway: &GatewayEvent{
|
||||||
|
TransferRef: "missing",
|
||||||
|
Status: GatewayStatusSuccess,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if !errors.Is(err, ErrStepNotFound) {
|
||||||
|
t.Fatalf("expected ErrStepNotFound, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReconcile_ValidationErrors(t *testing.T) {
|
||||||
|
reconciler := New(Dependencies{Logger: zap.NewNop(), Now: defaultNow})
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
in Input
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "missing payment",
|
||||||
|
in: Input{
|
||||||
|
Event: Event{Gateway: &GatewayEvent{StepRef: "s1", Status: GatewayStatusSuccess}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "missing step executions",
|
||||||
|
in: Input{
|
||||||
|
Payment: &agg.Payment{},
|
||||||
|
Event: Event{Gateway: &GatewayEvent{StepRef: "s1", Status: GatewayStatusSuccess}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple payloads",
|
||||||
|
in: Input{
|
||||||
|
Payment: &agg.Payment{
|
||||||
|
StepExecutions: []agg.StepExecution{{StepRef: "s1", StepCode: "a", State: agg.StepStatePending}},
|
||||||
|
},
|
||||||
|
Event: Event{
|
||||||
|
Gateway: &GatewayEvent{StepRef: "s1", Status: GatewayStatusSuccess},
|
||||||
|
Ledger: &LedgerEvent{StepRef: "s1", Status: LedgerStatusPosted},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid status",
|
||||||
|
in: Input{
|
||||||
|
Payment: &agg.Payment{
|
||||||
|
StepExecutions: []agg.StepExecution{{StepRef: "s1", StepCode: "a", State: agg.StepStatePending}},
|
||||||
|
},
|
||||||
|
Event: Event{
|
||||||
|
Card: &CardEvent{StepRef: "s1", Status: CardStatus("bad")},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
_, err := reconciler.Reconcile(tt.in)
|
||||||
|
if !errors.Is(err, merrors.ErrInvalidArg) {
|
||||||
|
t.Fatalf("expected invalid argument error, got %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasRef(refs []agg.ExternalRef, wanted agg.ExternalRef) bool {
|
||||||
|
for i := range refs {
|
||||||
|
ref := refs[i]
|
||||||
|
if ref.Kind != wanted.Kind {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ref.Ref != wanted.Ref {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ref.GatewayInstanceID != wanted.GatewayInstanceID {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package idem
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/tech/sendico/payments/storage"
|
||||||
|
"github.com/tech/sendico/payments/storage/model"
|
||||||
|
"go.mongodb.org/mongo-driver/v2/bson"
|
||||||
|
)
|
||||||
|
|
||||||
|
type fakeStore struct {
|
||||||
|
createFn func(ctx context.Context, payment *model.Payment) error
|
||||||
|
getByIdempotencyKeyFn func(ctx context.Context, orgRef bson.ObjectID, idempotencyKey string) (*model.Payment, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeStore) Create(ctx context.Context, payment *model.Payment) error {
|
||||||
|
if f.createFn == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return f.createFn(ctx, payment)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeStore) GetByIdempotencyKey(ctx context.Context, orgRef bson.ObjectID, idempotencyKey string) (*model.Payment, error) {
|
||||||
|
if f.getByIdempotencyKeyFn == nil {
|
||||||
|
return nil, storage.ErrPaymentNotFound
|
||||||
|
}
|
||||||
|
return f.getByIdempotencyKeyFn(ctx, orgRef, idempotencyKey)
|
||||||
|
}
|
||||||
@@ -4,13 +4,31 @@ import (
|
|||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/tech/sendico/pkg/merrors"
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
const hashSep = "\x1f"
|
const hashSep = "\x1f"
|
||||||
|
|
||||||
func (s *svc) Fingerprint(in FPInput) (string, error) {
|
func (s *svc) Fingerprint(in FPInput) (fingerprint string, err error) {
|
||||||
|
logger := s.logger
|
||||||
|
logger.Debug("Starting Fingerprint",
|
||||||
|
zap.String("organization_ref", strings.ToLower(strings.TrimSpace(in.OrganizationRef))),
|
||||||
|
zap.String("quotation_ref", strings.TrimSpace(in.QuotationRef)),
|
||||||
|
zap.String("intent_ref", strings.TrimSpace(in.IntentRef)),
|
||||||
|
zap.Bool("has_client_payment_ref", strings.TrimSpace(in.ClientPaymentRef) != ""),
|
||||||
|
)
|
||||||
|
defer func(start time.Time) {
|
||||||
|
fields := []zap.Field{zap.Int64("duration_ms", time.Since(start).Milliseconds())}
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn("Failed to fingerprint", append(fields, zap.Error(err))...)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Debug("Completed Fingerprint", append(fields, zap.Bool("generated", strings.TrimSpace(fingerprint) != ""))...)
|
||||||
|
}(time.Now())
|
||||||
|
|
||||||
orgRef := strings.ToLower(strings.TrimSpace(in.OrganizationRef))
|
orgRef := strings.ToLower(strings.TrimSpace(in.OrganizationRef))
|
||||||
if orgRef == "" {
|
if orgRef == "" {
|
||||||
return "", merrors.InvalidArgument("organization_ref is required")
|
return "", merrors.InvalidArgument("organization_ref is required")
|
||||||
@@ -29,7 +47,8 @@ func (s *svc) Fingerprint(in FPInput) (string, error) {
|
|||||||
"client=" + clientPaymentRef,
|
"client=" + clientPaymentRef,
|
||||||
}, hashSep)
|
}, hashSep)
|
||||||
|
|
||||||
return hashBytes([]byte(payload)), nil
|
fingerprint = hashBytes([]byte(payload))
|
||||||
|
return fingerprint, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func hashBytes(data []byte) string {
|
func hashBytes(data []byte) string {
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
package idem
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFingerprint_StableAndTrimmed(t *testing.T) {
|
||||||
|
svc := New()
|
||||||
|
|
||||||
|
a, err := svc.Fingerprint(FPInput{
|
||||||
|
OrganizationRef: " 65f1a2c6f3c5e2e7a1b2c3d4 ",
|
||||||
|
QuotationRef: " quote-1 ",
|
||||||
|
IntentRef: " intent-1 ",
|
||||||
|
ClientPaymentRef: " client-1 ",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Fingerprint returned error: %v", err)
|
||||||
|
}
|
||||||
|
b, err := svc.Fingerprint(FPInput{
|
||||||
|
OrganizationRef: "65F1A2C6F3C5E2E7A1B2C3D4",
|
||||||
|
QuotationRef: "quote-1",
|
||||||
|
IntentRef: "intent-1",
|
||||||
|
ClientPaymentRef: "client-1",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Fingerprint returned error: %v", err)
|
||||||
|
}
|
||||||
|
if a != b {
|
||||||
|
t.Fatalf("expected deterministic fingerprint, got %q vs %q", a, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFingerprint_ChangesOnPayload(t *testing.T) {
|
||||||
|
svc := New()
|
||||||
|
|
||||||
|
base, err := svc.Fingerprint(FPInput{
|
||||||
|
OrganizationRef: "65f1a2c6f3c5e2e7a1b2c3d4",
|
||||||
|
QuotationRef: "quote-1",
|
||||||
|
IntentRef: "intent-1",
|
||||||
|
ClientPaymentRef: "client-1",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Fingerprint returned error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
diffQuote, err := svc.Fingerprint(FPInput{
|
||||||
|
OrganizationRef: "65f1a2c6f3c5e2e7a1b2c3d4",
|
||||||
|
QuotationRef: "quote-2",
|
||||||
|
IntentRef: "intent-1",
|
||||||
|
ClientPaymentRef: "client-1",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Fingerprint returned error: %v", err)
|
||||||
|
}
|
||||||
|
if base == diffQuote {
|
||||||
|
t.Fatalf("expected different fingerprint for different quotation_ref")
|
||||||
|
}
|
||||||
|
|
||||||
|
diffClient, err := svc.Fingerprint(FPInput{
|
||||||
|
OrganizationRef: "65f1a2c6f3c5e2e7a1b2c3d4",
|
||||||
|
QuotationRef: "quote-1",
|
||||||
|
IntentRef: "intent-1",
|
||||||
|
ClientPaymentRef: "client-2",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Fingerprint returned error: %v", err)
|
||||||
|
}
|
||||||
|
if base == diffClient {
|
||||||
|
t.Fatalf("expected different fingerprint for different client_payment_ref")
|
||||||
|
}
|
||||||
|
|
||||||
|
diffIntent, err := svc.Fingerprint(FPInput{
|
||||||
|
OrganizationRef: "65f1a2c6f3c5e2e7a1b2c3d4",
|
||||||
|
QuotationRef: "quote-1",
|
||||||
|
IntentRef: "intent-2",
|
||||||
|
ClientPaymentRef: "client-1",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Fingerprint returned error: %v", err)
|
||||||
|
}
|
||||||
|
if base == diffIntent {
|
||||||
|
t.Fatalf("expected different fingerprint for different intent_ref")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFingerprint_RequiresBusinessFields(t *testing.T) {
|
||||||
|
svc := New()
|
||||||
|
|
||||||
|
if _, err := svc.Fingerprint(FPInput{
|
||||||
|
QuotationRef: "quote-1",
|
||||||
|
}); err == nil {
|
||||||
|
t.Fatal("expected error for empty organization_ref")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := svc.Fingerprint(FPInput{
|
||||||
|
OrganizationRef: "65f1a2c6f3c5e2e7a1b2c3d4",
|
||||||
|
}); err == nil {
|
||||||
|
t.Fatal("expected error for empty quotation_ref")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,9 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/tech/sendico/payments/storage/model"
|
"github.com/tech/sendico/payments/storage/model"
|
||||||
|
"github.com/tech/sendico/pkg/mlogger"
|
||||||
"go.mongodb.org/mongo-driver/v2/bson"
|
"go.mongodb.org/mongo-driver/v2/bson"
|
||||||
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Store is the minimal payment store contract required for idempotency handling.
|
// Store is the minimal payment store contract required for idempotency handling.
|
||||||
@@ -41,6 +43,19 @@ type CreateInput struct {
|
|||||||
Reuse ReuseInput
|
Reuse ReuseInput
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() Service {
|
// Dependencies configures idempotency service integrations.
|
||||||
return &svc{}
|
type Dependencies struct {
|
||||||
|
Logger mlogger.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(deps ...Dependencies) Service {
|
||||||
|
var dep Dependencies
|
||||||
|
if len(deps) > 0 {
|
||||||
|
dep = deps[0]
|
||||||
|
}
|
||||||
|
logger := dep.Logger
|
||||||
|
if logger == nil {
|
||||||
|
logger = zap.NewNop()
|
||||||
|
}
|
||||||
|
return &svc{logger: logger.Named("idempotency")}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,101 +10,6 @@ import (
|
|||||||
"go.mongodb.org/mongo-driver/v2/bson"
|
"go.mongodb.org/mongo-driver/v2/bson"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFingerprint_StableAndTrimmed(t *testing.T) {
|
|
||||||
svc := New()
|
|
||||||
|
|
||||||
a, err := svc.Fingerprint(FPInput{
|
|
||||||
OrganizationRef: " 65f1a2c6f3c5e2e7a1b2c3d4 ",
|
|
||||||
QuotationRef: " quote-1 ",
|
|
||||||
IntentRef: " intent-1 ",
|
|
||||||
ClientPaymentRef: " client-1 ",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Fingerprint returned error: %v", err)
|
|
||||||
}
|
|
||||||
b, err := svc.Fingerprint(FPInput{
|
|
||||||
OrganizationRef: "65F1A2C6F3C5E2E7A1B2C3D4",
|
|
||||||
QuotationRef: "quote-1",
|
|
||||||
IntentRef: "intent-1",
|
|
||||||
ClientPaymentRef: "client-1",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Fingerprint returned error: %v", err)
|
|
||||||
}
|
|
||||||
if a != b {
|
|
||||||
t.Fatalf("expected deterministic fingerprint, got %q vs %q", a, b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFingerprint_ChangesOnPayload(t *testing.T) {
|
|
||||||
svc := New()
|
|
||||||
|
|
||||||
base, err := svc.Fingerprint(FPInput{
|
|
||||||
OrganizationRef: "65f1a2c6f3c5e2e7a1b2c3d4",
|
|
||||||
QuotationRef: "quote-1",
|
|
||||||
IntentRef: "intent-1",
|
|
||||||
ClientPaymentRef: "client-1",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Fingerprint returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
diffQuote, err := svc.Fingerprint(FPInput{
|
|
||||||
OrganizationRef: "65f1a2c6f3c5e2e7a1b2c3d4",
|
|
||||||
QuotationRef: "quote-2",
|
|
||||||
IntentRef: "intent-1",
|
|
||||||
ClientPaymentRef: "client-1",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Fingerprint returned error: %v", err)
|
|
||||||
}
|
|
||||||
if base == diffQuote {
|
|
||||||
t.Fatalf("expected different fingerprint for different quotation_ref")
|
|
||||||
}
|
|
||||||
|
|
||||||
diffClient, err := svc.Fingerprint(FPInput{
|
|
||||||
OrganizationRef: "65f1a2c6f3c5e2e7a1b2c3d4",
|
|
||||||
QuotationRef: "quote-1",
|
|
||||||
IntentRef: "intent-1",
|
|
||||||
ClientPaymentRef: "client-2",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Fingerprint returned error: %v", err)
|
|
||||||
}
|
|
||||||
if base == diffClient {
|
|
||||||
t.Fatalf("expected different fingerprint for different client_payment_ref")
|
|
||||||
}
|
|
||||||
|
|
||||||
diffIntent, err := svc.Fingerprint(FPInput{
|
|
||||||
OrganizationRef: "65f1a2c6f3c5e2e7a1b2c3d4",
|
|
||||||
QuotationRef: "quote-1",
|
|
||||||
IntentRef: "intent-2",
|
|
||||||
ClientPaymentRef: "client-1",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Fingerprint returned error: %v", err)
|
|
||||||
}
|
|
||||||
if base == diffIntent {
|
|
||||||
t.Fatalf("expected different fingerprint for different intent_ref")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFingerprint_RequiresBusinessFields(t *testing.T) {
|
|
||||||
svc := New()
|
|
||||||
|
|
||||||
if _, err := svc.Fingerprint(FPInput{
|
|
||||||
QuotationRef: "quote-1",
|
|
||||||
}); err == nil {
|
|
||||||
t.Fatal("expected error for empty organization_ref")
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := svc.Fingerprint(FPInput{
|
|
||||||
OrganizationRef: "65f1a2c6f3c5e2e7a1b2c3d4",
|
|
||||||
}); err == nil {
|
|
||||||
t.Fatal("expected error for empty quotation_ref")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTryReuse_NotFound(t *testing.T) {
|
func TestTryReuse_NotFound(t *testing.T) {
|
||||||
svc := New()
|
svc := New()
|
||||||
store := &fakeStore{
|
store := &fakeStore{
|
||||||
@@ -294,22 +199,3 @@ func TestCreateOrReuse_DuplicateWithoutReusableRecordReturnsDuplicate(t *testing
|
|||||||
t.Fatalf("expected ErrDuplicatePayment, got %v", err)
|
t.Fatalf("expected ErrDuplicatePayment, got %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type fakeStore struct {
|
|
||||||
createFn func(ctx context.Context, payment *model.Payment) error
|
|
||||||
getByIdempotencyKeyFn func(ctx context.Context, orgRef bson.ObjectID, idempotencyKey string) (*model.Payment, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *fakeStore) Create(ctx context.Context, payment *model.Payment) error {
|
|
||||||
if f.createFn == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return f.createFn(ctx, payment)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *fakeStore) GetByIdempotencyKey(ctx context.Context, orgRef bson.ObjectID, idempotencyKey string) (*model.Payment, error) {
|
|
||||||
if f.getByIdempotencyKeyFn == nil {
|
|
||||||
return nil, storage.ErrPaymentNotFound
|
|
||||||
}
|
|
||||||
return f.getByIdempotencyKeyFn(ctx, orgRef, idempotencyKey)
|
|
||||||
}
|
|
||||||
@@ -4,21 +4,46 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/tech/sendico/payments/storage"
|
"github.com/tech/sendico/payments/storage"
|
||||||
"github.com/tech/sendico/payments/storage/model"
|
"github.com/tech/sendico/payments/storage/model"
|
||||||
"github.com/tech/sendico/pkg/merrors"
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
|
"github.com/tech/sendico/pkg/mlogger"
|
||||||
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
const reqHashMetaKey = "_orchestrator_v2_req_hash"
|
const reqHashMetaKey = "_orchestrator_v2_req_hash"
|
||||||
|
|
||||||
type svc struct{}
|
type svc struct {
|
||||||
|
logger mlogger.Logger
|
||||||
|
}
|
||||||
|
|
||||||
func (s *svc) TryReuse(
|
func (s *svc) TryReuse(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
store Store,
|
store Store,
|
||||||
in ReuseInput,
|
in ReuseInput,
|
||||||
) (*model.Payment, bool, error) {
|
) (payment *model.Payment, reused bool, err error) {
|
||||||
|
logger := s.logger
|
||||||
|
logger.Debug("Starting Try reuse",
|
||||||
|
zap.String("organization_ref", in.OrganizationID.Hex()),
|
||||||
|
zap.Bool("has_idempotency_key", strings.TrimSpace(in.IdempotencyKey) != ""),
|
||||||
|
)
|
||||||
|
defer func(start time.Time) {
|
||||||
|
fields := []zap.Field{
|
||||||
|
zap.Int64("duration_ms", time.Since(start).Milliseconds()),
|
||||||
|
zap.Bool("reused", reused),
|
||||||
|
}
|
||||||
|
if payment != nil {
|
||||||
|
fields = append(fields, zap.String("payment_ref", strings.TrimSpace(payment.PaymentRef)))
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn("Failed to try reuse", append(fields, zap.Error(err))...)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Debug("Completed Try reuse", fields...)
|
||||||
|
}(time.Now())
|
||||||
|
|
||||||
if store == nil {
|
if store == nil {
|
||||||
return nil, false, merrors.InvalidArgument("payments store is required")
|
return nil, false, merrors.InvalidArgument("payments store is required")
|
||||||
}
|
}
|
||||||
@@ -28,7 +53,7 @@ func (s *svc) TryReuse(
|
|||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
payment, err := store.GetByIdempotencyKey(ctx, in.OrganizationID, idempotencyKey)
|
payment, err = store.GetByIdempotencyKey(ctx, in.OrganizationID, idempotencyKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, storage.ErrPaymentNotFound) || errors.Is(err, merrors.ErrNoData) {
|
if errors.Is(err, storage.ErrPaymentNotFound) || errors.Is(err, merrors.ErrNoData) {
|
||||||
return nil, false, nil
|
return nil, false, nil
|
||||||
@@ -50,7 +75,28 @@ func (s *svc) CreateOrReuse(
|
|||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
store Store,
|
store Store,
|
||||||
in CreateInput,
|
in CreateInput,
|
||||||
) (*model.Payment, bool, error) {
|
) (payment *model.Payment, reused bool, err error) {
|
||||||
|
logger := s.logger
|
||||||
|
logger.Debug("Starting Create or reuse",
|
||||||
|
zap.String("organization_ref", in.Reuse.OrganizationID.Hex()),
|
||||||
|
zap.Bool("has_payment", in.Payment != nil),
|
||||||
|
zap.Bool("has_idempotency_key", strings.TrimSpace(in.Reuse.IdempotencyKey) != ""),
|
||||||
|
)
|
||||||
|
defer func(start time.Time) {
|
||||||
|
fields := []zap.Field{
|
||||||
|
zap.Int64("duration_ms", time.Since(start).Milliseconds()),
|
||||||
|
zap.Bool("reused", reused),
|
||||||
|
}
|
||||||
|
if payment != nil {
|
||||||
|
fields = append(fields, zap.String("payment_ref", strings.TrimSpace(payment.PaymentRef)))
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn("Failed to create or reuse", append(fields, zap.Error(err))...)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Debug("Completed Create or reuse", fields...)
|
||||||
|
}(time.Now())
|
||||||
|
|
||||||
if store == nil {
|
if store == nil {
|
||||||
return nil, false, merrors.InvalidArgument("payments store is required")
|
return nil, false, merrors.InvalidArgument("payments store is required")
|
||||||
}
|
}
|
||||||
@@ -64,19 +110,19 @@ func (s *svc) CreateOrReuse(
|
|||||||
}
|
}
|
||||||
setPaymentReqHash(in.Payment, fingerprint)
|
setPaymentReqHash(in.Payment, fingerprint)
|
||||||
|
|
||||||
if err := store.Create(ctx, in.Payment); err != nil {
|
if createErr := store.Create(ctx, in.Payment); createErr != nil {
|
||||||
if !errors.Is(err, storage.ErrDuplicatePayment) {
|
if !errors.Is(createErr, storage.ErrDuplicatePayment) {
|
||||||
return nil, false, err
|
return nil, false, createErr
|
||||||
}
|
}
|
||||||
|
|
||||||
payment, reused, reuseErr := s.TryReuse(ctx, store, in.Reuse)
|
payment, reused, err = s.TryReuse(ctx, store, in.Reuse)
|
||||||
if reuseErr != nil {
|
if err != nil {
|
||||||
return nil, false, reuseErr
|
return nil, false, err
|
||||||
}
|
}
|
||||||
if reused {
|
if reused {
|
||||||
return payment, true, nil
|
return payment, true, nil
|
||||||
}
|
}
|
||||||
return nil, false, err
|
return nil, false, createErr
|
||||||
}
|
}
|
||||||
|
|
||||||
return in.Payment, false, nil
|
return in.Payment, false, nil
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
package oobs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type memoryAuditStore struct {
|
||||||
|
mu sync.RWMutex
|
||||||
|
byPayment map[string][]TimelineEntry
|
||||||
|
byStep map[string][]TimelineEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMemoryAuditStore() AuditStore {
|
||||||
|
return &memoryAuditStore{
|
||||||
|
byPayment: map[string][]TimelineEntry{},
|
||||||
|
byStep: map[string][]TimelineEntry{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *memoryAuditStore) Append(entry TimelineEntry) error {
|
||||||
|
paymentRef := strings.TrimSpace(entry.PaymentRef)
|
||||||
|
if paymentRef == "" {
|
||||||
|
return merrors.InvalidArgument("timeline.payment_ref is required")
|
||||||
|
}
|
||||||
|
stepRef := strings.TrimSpace(entry.StepRef)
|
||||||
|
entry.PaymentRef = paymentRef
|
||||||
|
entry.StepRef = stepRef
|
||||||
|
entry.StepCode = strings.TrimSpace(entry.StepCode)
|
||||||
|
entry.Event = strings.TrimSpace(entry.Event)
|
||||||
|
entry.State = strings.TrimSpace(entry.State)
|
||||||
|
entry.Message = strings.TrimSpace(entry.Message)
|
||||||
|
entry.Fields = trimStringMap(entry.Fields)
|
||||||
|
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
|
||||||
|
s.byPayment[paymentRef] = append(s.byPayment[paymentRef], cloneTimelineEntry(entry))
|
||||||
|
if stepRef == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
key := stepAttemptKey(paymentRef, stepRef, entry.Attempt)
|
||||||
|
s.byStep[key] = append(s.byStep[key], cloneTimelineEntry(entry))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *memoryAuditStore) ListByPayment(paymentRef string, limit int32, desc bool) ([]TimelineEntry, error) {
|
||||||
|
ref := strings.TrimSpace(paymentRef)
|
||||||
|
if ref == "" {
|
||||||
|
return nil, merrors.InvalidArgument("payment_ref is required")
|
||||||
|
}
|
||||||
|
limit = normalizeLimit(limit)
|
||||||
|
|
||||||
|
s.mu.RLock()
|
||||||
|
items := append([]TimelineEntry(nil), s.byPayment[ref]...)
|
||||||
|
s.mu.RUnlock()
|
||||||
|
|
||||||
|
return paginateEntries(items, limit, desc), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *memoryAuditStore) ListByStepAttempt(
|
||||||
|
paymentRef string,
|
||||||
|
stepRef string,
|
||||||
|
attempt uint32,
|
||||||
|
limit int32,
|
||||||
|
desc bool,
|
||||||
|
) ([]TimelineEntry, error) {
|
||||||
|
ref := strings.TrimSpace(paymentRef)
|
||||||
|
if ref == "" {
|
||||||
|
return nil, merrors.InvalidArgument("payment_ref is required")
|
||||||
|
}
|
||||||
|
step := strings.TrimSpace(stepRef)
|
||||||
|
if step == "" {
|
||||||
|
return nil, merrors.InvalidArgument("step_ref is required")
|
||||||
|
}
|
||||||
|
if attempt == 0 {
|
||||||
|
return nil, merrors.InvalidArgument("attempt is required")
|
||||||
|
}
|
||||||
|
limit = normalizeLimit(limit)
|
||||||
|
|
||||||
|
key := stepAttemptKey(ref, step, attempt)
|
||||||
|
s.mu.RLock()
|
||||||
|
items := append([]TimelineEntry(nil), s.byStep[key]...)
|
||||||
|
s.mu.RUnlock()
|
||||||
|
|
||||||
|
return paginateEntries(items, limit, desc), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func paginateEntries(items []TimelineEntry, limit int32, desc bool) []TimelineEntry {
|
||||||
|
if desc {
|
||||||
|
items = reverseEntries(items)
|
||||||
|
}
|
||||||
|
if len(items) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if int32(len(items)) > limit {
|
||||||
|
items = items[:limit]
|
||||||
|
}
|
||||||
|
out := make([]TimelineEntry, 0, len(items))
|
||||||
|
for i := range items {
|
||||||
|
out = append(out, cloneTimelineEntry(items[i]))
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func cloneTimelineEntry(entry TimelineEntry) TimelineEntry {
|
||||||
|
entry.Fields = cloneStringMap(entry.Fields)
|
||||||
|
return entry
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user