Orchestration / payments v2 #554

Merged
tech merged 23 commits from pqpov2-547 into main 2026-02-26 22:45:55 +00:00
42 changed files with 1521 additions and 74 deletions
Showing only changes of commit 53abb24482 - Show all commits

View File

@@ -53,7 +53,7 @@ require (
github.com/nats-io/nuid v1.0.1 // indirect
github.com/prometheus/client_model v0.6.2 // 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/scram v1.2.0 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect

View File

@@ -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/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
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.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
github.com/prometheus/procfs v0.20.0 h1:AA7aCvjxwAquZAlonN7888f2u4IN8WVeFgBi4k82M4Q=
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/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=

View File

@@ -38,7 +38,7 @@ require (
github.com/prometheus/client_golang v1.23.2
github.com/prometheus/client_model v0.6.2 // 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/scram v1.2.0 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect

View File

@@ -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/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
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.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
github.com/prometheus/procfs v0.20.0 h1:AA7aCvjxwAquZAlonN7888f2u4IN8WVeFgBi4k82M4Q=
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/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=

View File

@@ -30,7 +30,7 @@ require (
github.com/nats-io/nuid v1.0.1 // indirect
github.com/prometheus/client_model v0.6.2 // 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/scram v1.2.0 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect

View File

@@ -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/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
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.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
github.com/prometheus/procfs v0.20.0 h1:AA7aCvjxwAquZAlonN7888f2u4IN8WVeFgBi4k82M4Q=
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/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=

View File

@@ -35,7 +35,7 @@ require (
github.com/nats-io/nuid v1.0.1 // indirect
github.com/prometheus/client_model v0.6.2 // 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/scram v1.2.0 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect

View File

@@ -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/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
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.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
github.com/prometheus/procfs v0.20.0 h1:AA7aCvjxwAquZAlonN7888f2u4IN8WVeFgBi4k82M4Q=
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/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=

View File

@@ -36,7 +36,7 @@ require (
github.com/nats-io/nuid v1.0.1 // indirect
github.com/prometheus/client_model v0.6.2 // 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/scram v1.2.0 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect

View File

@@ -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/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
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.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
github.com/prometheus/procfs v0.20.0 h1:AA7aCvjxwAquZAlonN7888f2u4IN8WVeFgBi4k82M4Q=
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/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=

View File

@@ -25,7 +25,7 @@ require (
require (
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260224171832-d0e24df9ee63 // 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/bits-and-blooms/bitset v1.24.4 // indirect
github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect
@@ -67,7 +67,7 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.6.2 // 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/shirou/gopsutil v3.21.11+incompatible // indirect
github.com/supranational/blst v0.3.16 // indirect

View File

@@ -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/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260224171832-d0e24df9ee63 h1:kLumy+keYsmuByIG8/G7Iay1vGCd1/WBq8a3vvPJWTM=
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260224171832-d0e24df9ee63/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
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-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/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@@ -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/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
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.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
github.com/prometheus/procfs v0.20.0 h1:AA7aCvjxwAquZAlonN7888f2u4IN8WVeFgBi4k82M4Q=
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/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=

View File

@@ -37,7 +37,7 @@ require (
github.com/nats-io/nuid v1.0.1 // indirect
github.com/prometheus/client_model v0.6.2 // 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/tklauser/go-sysconf v0.3.16 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect

View File

@@ -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/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
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.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
github.com/prometheus/procfs v0.20.0 h1:AA7aCvjxwAquZAlonN7888f2u4IN8WVeFgBi4k82M4Q=
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/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=

View File

@@ -36,7 +36,7 @@ require (
github.com/prometheus/client_golang v1.23.2 // indirect
github.com/prometheus/client_model v0.6.2 // 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/scram v1.2.0 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect

View File

@@ -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/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
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.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
github.com/prometheus/procfs v0.20.0 h1:AA7aCvjxwAquZAlonN7888f2u4IN8WVeFgBi4k82M4Q=
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/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=

View File

@@ -27,7 +27,7 @@ require (
require (
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260224171832-d0e24df9ee63 // 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/bits-and-blooms/bitset v1.24.4 // indirect
github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect
@@ -73,7 +73,7 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.6.2 // 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/ryanuber/go-glob v1.0.0 // indirect
github.com/shirou/gopsutil v3.21.11+incompatible // indirect

View File

@@ -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/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260224171832-d0e24df9ee63 h1:kLumy+keYsmuByIG8/G7Iay1vGCd1/WBq8a3vvPJWTM=
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260224171832-d0e24df9ee63/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
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-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/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@@ -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/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
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.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
github.com/prometheus/procfs v0.20.0 h1:AA7aCvjxwAquZAlonN7888f2u4IN8WVeFgBi4k82M4Q=
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/go.mod h1:gF3zSOrafR9DQEWSE8TjfI9NkooDxbyT4UgRGKZA0lc=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=

View File

@@ -37,7 +37,7 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.6.2 // 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/scram v1.2.0 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect

View File

@@ -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/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
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.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
github.com/prometheus/procfs v0.20.0 h1:AA7aCvjxwAquZAlonN7888f2u4IN8WVeFgBi4k82M4Q=
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/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=

View File

@@ -37,7 +37,7 @@ require (
github.com/prometheus/client_golang v1.23.2 // indirect
github.com/prometheus/client_model v0.6.2 // 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/toorop/go-dkim v0.0.0-20250226130143-9025cce95817 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect

View File

@@ -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/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
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.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
github.com/prometheus/procfs v0.20.0 h1:AA7aCvjxwAquZAlonN7888f2u4IN8WVeFgBi4k82M4Q=
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/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/sendgrid/rest v2.6.9+incompatible h1:1EyIcsNdn9KIisLW50MKwmSRSK+ekueiEMJ7NEoxJo0=

View File

@@ -36,7 +36,7 @@ require (
github.com/prometheus/client_golang v1.23.2 // indirect
github.com/prometheus/client_model v0.6.2 // 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/scram v1.2.0 // indirect

View File

@@ -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/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
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.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
github.com/prometheus/procfs v0.20.0 h1:AA7aCvjxwAquZAlonN7888f2u4IN8WVeFgBi4k82M4Q=
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/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=

View File

@@ -51,7 +51,7 @@ require (
github.com/prometheus/client_golang v1.23.2 // indirect
github.com/prometheus/client_model v0.6.2 // 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/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.2.0 // indirect

View File

@@ -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/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
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.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
github.com/prometheus/procfs v0.20.0 h1:AA7aCvjxwAquZAlonN7888f2u4IN8WVeFgBi4k82M4Q=
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/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=

View File

@@ -0,0 +1,419 @@
package orchestrator
import (
"context"
"strconv"
"strings"
"github.com/shopspring/decimal"
mntxclient "github.com/tech/sendico/gateway/mntx/client"
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/agg"
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/erecon"
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/sexec"
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/xplan"
"github.com/tech/sendico/payments/storage/model"
"github.com/tech/sendico/pkg/merrors"
paymenttypes "github.com/tech/sendico/pkg/payments/types"
mntxv1 "github.com/tech/sendico/pkg/proto/gateway/mntx/v1"
)
type gatewayCardPayoutExecutor struct {
mntxClient mntxclient.Client
}
type cardPayoutCustomer struct {
id string
firstName string
middleName string
lastName string
ip string
zip string
country string
state string
city string
address string
}
func (e *gatewayCardPayoutExecutor) ExecuteCardPayout(ctx context.Context, req sexec.StepRequest) (*sexec.ExecuteOutput, error) {
if req.Payment == nil {
return nil, merrors.InvalidArgument("card payout send: payment is required")
}
if e == nil || e.mntxClient == nil {
return nil, merrors.InvalidArgument("card payout send: mntx client is required")
}
if model.ParseRailOperation(string(req.Step.Action)) != model.RailOperationSend {
return nil, merrors.InvalidArgument("card payout send: unsupported action")
}
card, err := payoutDestinationCard(req.Payment)
if err != nil {
return nil, err
}
amountMinor, currency, err := cardPayoutAmountMinor(req.Payment)
if err != nil {
return nil, err
}
stepToken := cardPayoutStepToken(req.Step)
operationRef := cardPayoutOperationRef(req.Payment, stepToken)
payoutRef := cardPayoutRef(req.Payment, stepToken)
idempotencyKey := cardPayoutIdempotencyKey(req.Payment, stepToken)
projectID := cardPayoutProjectID(req.Payment)
customer := cardPayoutCustomerFromPayment(req.Payment, card)
cardHolder := cardPayoutCardholder(card, customer)
metadata := cardPayoutMetadata(req.Payment, req.Step)
intentRef := strings.TrimSpace(req.Payment.IntentSnapshot.Ref)
var responsePayout *mntxv1.CardPayoutState
if token := strings.TrimSpace(card.Token); token != "" {
resp, createErr := e.mntxClient.CreateCardTokenPayout(ctx, &mntxv1.CardTokenPayoutRequest{
PayoutId: payoutRef,
ProjectId: projectID,
CustomerId: customer.id,
CustomerFirstName: customer.firstName,
CustomerMiddleName: customer.middleName,
CustomerLastName: customer.lastName,
CustomerIp: customer.ip,
CustomerZip: customer.zip,
CustomerCountry: customer.country,
CustomerState: customer.state,
CustomerCity: customer.city,
CustomerAddress: customer.address,
AmountMinor: amountMinor,
Currency: currency,
CardToken: token,
CardHolder: cardHolder,
MaskedPan: strings.TrimSpace(card.MaskedPan),
Metadata: metadata,
OperationRef: operationRef,
IdempotencyKey: idempotencyKey,
IntentRef: intentRef,
})
if createErr != nil {
return nil, createErr
}
if resp == nil {
return nil, merrors.Internal("card payout send: card token payout response is missing")
}
responsePayout = resp.GetPayout()
} else {
pan := strings.TrimSpace(card.Pan)
if pan == "" {
return nil, merrors.InvalidArgument("card payout send: card pan is required")
}
if card.ExpMonth == 0 || card.ExpYear == 0 {
return nil, merrors.InvalidArgument("card payout send: card expiry is required")
}
resp, createErr := e.mntxClient.CreateCardPayout(ctx, &mntxv1.CardPayoutRequest{
PayoutId: payoutRef,
ProjectId: projectID,
CustomerId: customer.id,
CustomerFirstName: customer.firstName,
CustomerMiddleName: customer.middleName,
CustomerLastName: customer.lastName,
CustomerIp: customer.ip,
CustomerZip: customer.zip,
CustomerCountry: customer.country,
CustomerState: customer.state,
CustomerCity: customer.city,
CustomerAddress: customer.address,
AmountMinor: amountMinor,
Currency: currency,
CardPan: pan,
CardExpYear: card.ExpYear,
CardExpMonth: card.ExpMonth,
CardHolder: cardHolder,
Metadata: metadata,
OperationRef: operationRef,
IdempotencyKey: idempotencyKey,
IntentRef: intentRef,
})
if createErr != nil {
return nil, createErr
}
if resp == nil {
return nil, merrors.Internal("card payout send: card payout response is missing")
}
responsePayout = resp.GetPayout()
}
resolvedPayoutRef := firstNonEmpty(strings.TrimSpace(responsePayout.GetPayoutId()), payoutRef)
resolvedOperationRef := firstNonEmpty(strings.TrimSpace(responsePayout.GetOperationRef()), operationRef)
gatewayInstanceID := firstNonEmpty(strings.TrimSpace(req.Step.InstanceID), strings.TrimSpace(req.Step.Gateway))
externalRefs, refsErr := cardPayoutExternalRefs(resolvedPayoutRef, resolvedOperationRef, gatewayInstanceID)
if refsErr != nil {
return nil, refsErr
}
step := req.StepExecution
step.State = agg.StepStateCompleted
step.ExternalRefs = externalRefs
step.FailureCode = ""
step.FailureMsg = ""
return &sexec.ExecuteOutput{StepExecution: step}, nil
}
func payoutDestinationCard(payment *agg.Payment) (*model.CardEndpoint, error) {
if payment == nil {
return nil, merrors.InvalidArgument("card payout send: payment is required")
}
destination := payment.IntentSnapshot.Destination
if destination.Type != model.EndpointTypeCard || destination.Card == nil {
return nil, merrors.InvalidArgument("card payout send: destination card is required")
}
return destination.Card, nil
}
func cardPayoutMoney(payment *agg.Payment) *paymenttypes.Money {
if payment != nil && payment.QuoteSnapshot != nil && payment.QuoteSnapshot.ExpectedSettlementAmount != nil {
return payment.QuoteSnapshot.ExpectedSettlementAmount
}
if payment == nil {
return nil
}
return payment.IntentSnapshot.Amount
}
func cardPayoutAmountMinor(payment *agg.Payment) (int64, string, error) {
money := cardPayoutMoney(payment)
if money == nil {
return 0, "", merrors.InvalidArgument("card payout send: payout amount is required")
}
amountText := strings.TrimSpace(money.GetAmount())
currency := strings.ToUpper(strings.TrimSpace(money.GetCurrency()))
if idx := strings.Index(currency, "-"); idx > 0 {
currency = currency[:idx]
}
if amountText == "" || currency == "" {
return 0, "", merrors.InvalidArgument("card payout send: payout amount is invalid")
}
value, err := decimal.NewFromString(amountText)
if err != nil || !value.IsPositive() {
return 0, "", merrors.InvalidArgument("card payout send: payout amount is invalid")
}
minor := value.Mul(decimal.NewFromInt(100))
if !minor.Equal(minor.Truncate(0)) {
return 0, "", merrors.InvalidArgument("card payout send: payout amount supports at most 2 fractional digits")
}
return minor.IntPart(), currency, nil
}
func cardPayoutStepToken(step xplan.Step) string {
return firstNonEmpty(strings.TrimSpace(step.StepRef), strings.TrimSpace(step.StepCode), "card_payout")
}
func cardPayoutOperationRef(payment *agg.Payment, stepToken string) string {
base := ""
if payment != nil {
base = firstNonEmpty(strings.TrimSpace(payment.PaymentRef), strings.TrimSpace(payment.IdempotencyKey))
}
return joinRef(base, stepToken)
}
func cardPayoutRef(payment *agg.Payment, stepToken string) string {
base := ""
if payment != nil {
base = firstNonEmpty(strings.TrimSpace(payment.PaymentRef), strings.TrimSpace(payment.IdempotencyKey), "card_payout")
}
return joinRef(base, stepToken)
}
func cardPayoutIdempotencyKey(payment *agg.Payment, stepToken string) string {
base := ""
if payment != nil {
base = strings.TrimSpace(payment.IdempotencyKey)
if base == "" {
base = strings.TrimSpace(payment.PaymentRef)
}
}
if base == "" {
base = "card_payout"
}
return joinRef(base, stepToken)
}
func joinRef(base, suffix string) string {
base = strings.TrimSpace(base)
suffix = strings.TrimSpace(suffix)
switch {
case base == "":
return suffix
case suffix == "":
return base
default:
return base + ":" + suffix
}
}
func cardPayoutProjectID(payment *agg.Payment) int64 {
if payment == nil {
return 0
}
raw := cardPayoutAttribute(payment.IntentSnapshot.Attributes, "project_id", "projectId")
if raw == "" {
return 0
}
value, err := strconv.ParseInt(raw, 10, 64)
if err != nil || value < 0 {
return 0
}
return value
}
func cardPayoutCustomerFromPayment(payment *agg.Payment, card *model.CardEndpoint) cardPayoutCustomer {
customer := cardPayoutCustomer{}
if payment == nil {
return customer
}
cardholder := ""
cardholderSurname := ""
cardCountry := ""
if card != nil {
cardholder = strings.TrimSpace(card.Cardholder)
cardholderSurname = strings.TrimSpace(card.CardholderSurname)
cardCountry = strings.ToUpper(strings.TrimSpace(card.Country))
}
attrs := payment.IntentSnapshot.Attributes
intentCustomer := payment.IntentSnapshot.Customer
if intentCustomer != nil {
customer.id = strings.TrimSpace(intentCustomer.ID)
customer.firstName = strings.TrimSpace(intentCustomer.FirstName)
customer.middleName = strings.TrimSpace(intentCustomer.MiddleName)
customer.lastName = strings.TrimSpace(intentCustomer.LastName)
customer.ip = strings.TrimSpace(intentCustomer.IP)
customer.zip = strings.TrimSpace(intentCustomer.Zip)
customer.country = strings.ToUpper(strings.TrimSpace(intentCustomer.Country))
customer.state = strings.TrimSpace(intentCustomer.State)
customer.city = strings.TrimSpace(intentCustomer.City)
customer.address = strings.TrimSpace(intentCustomer.Address)
}
customer.id = firstNonEmpty(customer.id,
cardPayoutAttribute(attrs, "customer_id", "customerId", "initiator_ref", "initiatorRef"),
strings.TrimSpace(payment.PaymentRef),
strings.TrimSpace(payment.IdempotencyKey),
"unknown_customer")
customer.firstName = firstNonEmpty(customer.firstName, cardholder, "UNKNOWN")
customer.middleName = firstNonEmpty(customer.middleName, cardPayoutAttribute(attrs, "customer_middle_name", "customerMiddleName"))
customer.lastName = firstNonEmpty(customer.lastName, cardholderSurname, "UNKNOWN")
customer.ip = firstNonEmpty(customer.ip,
cardPayoutAttribute(attrs, "customer_ip", "customerIp", "ip", "ip_address", "ipAddress"),
"0.0.0.0")
customer.zip = firstNonEmpty(customer.zip, cardPayoutAttribute(attrs, "customer_zip", "customerZip"))
customer.country = firstNonEmpty(customer.country, cardCountry, cardPayoutAttribute(attrs, "customer_country", "customerCountry"))
customer.state = firstNonEmpty(customer.state, cardPayoutAttribute(attrs, "customer_state", "customerState"))
customer.city = firstNonEmpty(customer.city, cardPayoutAttribute(attrs, "customer_city", "customerCity"))
customer.address = firstNonEmpty(customer.address, cardPayoutAttribute(attrs, "customer_address", "customerAddress"))
return customer
}
func cardPayoutCardholder(card *model.CardEndpoint, customer cardPayoutCustomer) string {
holder := ""
if card != nil {
holder = strings.TrimSpace(card.Cardholder)
surname := strings.TrimSpace(card.CardholderSurname)
if holder != "" && surname != "" && !strings.Contains(strings.ToLower(holder), strings.ToLower(surname)) {
holder = holder + " " + surname
}
}
if holder == "" {
holder = strings.TrimSpace(firstNonEmpty(spaceJoin(customer.firstName, customer.lastName), customer.firstName, customer.lastName))
}
if holder == "" {
holder = "UNKNOWN"
}
return holder
}
func spaceJoin(values ...string) string {
parts := make([]string, 0, len(values))
for i := range values {
item := strings.TrimSpace(values[i])
if item == "" {
continue
}
parts = append(parts, item)
}
return strings.Join(parts, " ")
}
func cardPayoutAttribute(attrs map[string]string, keys ...string) string {
if len(attrs) == 0 {
return ""
}
for i := range keys {
key := strings.TrimSpace(keys[i])
if key == "" {
continue
}
if value, ok := attrs[key]; ok {
if trimmed := strings.TrimSpace(value); trimmed != "" {
return trimmed
}
}
for attrKey, value := range attrs {
if !strings.EqualFold(strings.TrimSpace(attrKey), key) {
continue
}
if trimmed := strings.TrimSpace(value); trimmed != "" {
return trimmed
}
}
}
return ""
}
func cardPayoutMetadata(payment *agg.Payment, step xplan.Step) map[string]string {
out := transferMetadata(step)
if out == nil {
out = map[string]string{}
}
if payment != nil {
if quoteRef := firstNonEmpty(
strings.TrimSpace(payment.QuotationRef),
strings.TrimSpace(quoteRefFromSnapshot(payment.QuoteSnapshot)),
); quoteRef != "" {
out[settlementMetadataQuoteRef] = quoteRef
}
}
if outgoingLeg := strings.TrimSpace(string(step.Rail)); outgoingLeg != "" {
out[settlementMetadataOutgoingLeg] = outgoingLeg
}
if len(out) == 0 {
return nil
}
return out
}
func cardPayoutExternalRefs(payoutRef, operationRef, gatewayInstanceID string) ([]agg.ExternalRef, error) {
gatewayInstanceID = strings.TrimSpace(gatewayInstanceID)
refs := make([]agg.ExternalRef, 0, 3)
if operationRef = strings.TrimSpace(operationRef); operationRef != "" {
refs = append(refs, agg.ExternalRef{
GatewayInstanceID: gatewayInstanceID,
Kind: erecon.ExternalRefKindOperation,
Ref: operationRef,
})
}
if payoutRef = strings.TrimSpace(payoutRef); payoutRef != "" {
refs = append(refs, agg.ExternalRef{
GatewayInstanceID: gatewayInstanceID,
Kind: erecon.ExternalRefKindTransfer,
Ref: payoutRef,
})
refs = append(refs, agg.ExternalRef{
GatewayInstanceID: gatewayInstanceID,
Kind: erecon.ExternalRefKindCardPayout,
Ref: payoutRef,
})
}
if len(refs) == 0 {
return nil, merrors.Internal("card payout send: payout response does not contain references")
}
return refs, nil
}
var _ sexec.CardPayoutExecutor = (*gatewayCardPayoutExecutor)(nil)

View File

@@ -0,0 +1,181 @@
package orchestrator
import (
"context"
"strings"
"testing"
mntxclient "github.com/tech/sendico/gateway/mntx/client"
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/agg"
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/erecon"
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/sexec"
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/xplan"
"github.com/tech/sendico/payments/storage/model"
pm "github.com/tech/sendico/pkg/model"
paymenttypes "github.com/tech/sendico/pkg/payments/types"
mntxv1 "github.com/tech/sendico/pkg/proto/gateway/mntx/v1"
"go.mongodb.org/mongo-driver/v2/bson"
)
func TestGatewayCardPayoutExecutor_ExecuteCardPayout_SubmitsCardPayout(t *testing.T) {
orgID := bson.NewObjectID()
var payoutReq *mntxv1.CardPayoutRequest
executor := &gatewayCardPayoutExecutor{
mntxClient: &mntxclient.Fake{
CreateCardPayoutFn: func(_ context.Context, req *mntxv1.CardPayoutRequest) (*mntxv1.CardPayoutResponse, error) {
payoutReq = req
return &mntxv1.CardPayoutResponse{
Payout: &mntxv1.CardPayoutState{
PayoutId: "payout-remote-1",
},
}, nil
},
},
}
req := sexec.StepRequest{
Payment: &agg.Payment{
OrganizationBoundBase: pm.OrganizationBoundBase{OrganizationRef: orgID},
PaymentRef: "payment-1",
IdempotencyKey: "idem-1",
QuotationRef: "quote-1",
IntentSnapshot: model.PaymentIntent{
Ref: "intent-1",
Destination: model.PaymentEndpoint{
Type: model.EndpointTypeCard,
Card: &model.CardEndpoint{
Pan: "2200700142860161",
Cardholder: "Stephan",
CardholderSurname: "Deshevikh",
ExpMonth: 3,
ExpYear: 2030,
},
},
Customer: &model.Customer{
ID: "cust-1",
FirstName: "Stephan",
LastName: "Deshevikh",
IP: "198.51.100.10",
},
Amount: &paymenttypes.Money{
Amount: "1.000000",
Currency: "USDT",
},
Attributes: map[string]string{
"initiator_ref": "user-123",
},
},
QuoteSnapshot: &model.PaymentQuoteSnapshot{
ExpectedSettlementAmount: &paymenttypes.Money{
Amount: "76.50",
Currency: "RUB",
},
QuoteRef: "quote-1",
},
},
Step: xplan.Step{
StepRef: "hop_4_card_payout_send",
StepCode: "hop.4.card_payout.send",
Action: model.RailOperationSend,
Rail: model.RailCardPayout,
Gateway: "monetix",
InstanceID: "monetix",
},
StepExecution: agg.StepExecution{
StepRef: "hop_4_card_payout_send",
StepCode: "hop.4.card_payout.send",
Attempt: 1,
},
}
out, err := executor.ExecuteCardPayout(context.Background(), req)
if err != nil {
t.Fatalf("ExecuteCardPayout returned error: %v", err)
}
if out == nil {
t.Fatal("expected output")
}
if out.StepExecution.State != agg.StepStateCompleted {
t.Fatalf("expected completed state, got=%q", out.StepExecution.State)
}
if payoutReq == nil {
t.Fatal("expected payout request to be submitted")
}
if got, want := payoutReq.GetPayoutId(), "payment-1:hop_4_card_payout_send"; got != want {
t.Fatalf("payout_id mismatch: got=%q want=%q", got, want)
}
if got, want := payoutReq.GetOperationRef(), "payment-1:hop_4_card_payout_send"; got != want {
t.Fatalf("operation_ref mismatch: got=%q want=%q", got, want)
}
if got, want := payoutReq.GetIdempotencyKey(), "idem-1:hop_4_card_payout_send"; got != want {
t.Fatalf("idempotency mismatch: got=%q want=%q", got, want)
}
if got, want := payoutReq.GetAmountMinor(), int64(7650); got != want {
t.Fatalf("amount_minor mismatch: got=%d want=%d", got, want)
}
if got, want := payoutReq.GetCurrency(), "RUB"; got != want {
t.Fatalf("currency mismatch: got=%q want=%q", got, want)
}
if got, want := payoutReq.GetMetadata()[settlementMetadataQuoteRef], "quote-1"; got != want {
t.Fatalf("quote_ref metadata mismatch: got=%q want=%q", got, want)
}
if got, want := payoutReq.GetMetadata()[settlementMetadataOutgoingLeg], string(model.RailCardPayout); got != want {
t.Fatalf("outgoing_leg metadata mismatch: got=%q want=%q", got, want)
}
if len(out.StepExecution.ExternalRefs) != 3 {
t.Fatalf("expected 3 external refs, got=%d", len(out.StepExecution.ExternalRefs))
}
if out.StepExecution.ExternalRefs[0].Kind != erecon.ExternalRefKindOperation {
t.Fatalf("expected first external ref to be operation, got=%q", out.StepExecution.ExternalRefs[0].Kind)
}
if out.StepExecution.ExternalRefs[1].Kind != erecon.ExternalRefKindTransfer {
t.Fatalf("expected second external ref to be transfer, got=%q", out.StepExecution.ExternalRefs[1].Kind)
}
if out.StepExecution.ExternalRefs[2].Kind != erecon.ExternalRefKindCardPayout {
t.Fatalf("expected third external ref to be card payout, got=%q", out.StepExecution.ExternalRefs[2].Kind)
}
if got, want := out.StepExecution.ExternalRefs[1].Ref, "payout-remote-1"; got != want {
t.Fatalf("transfer_ref mismatch: got=%q want=%q", got, want)
}
}
func TestGatewayCardPayoutExecutor_ExecuteCardPayout_RequiresMntxClient(t *testing.T) {
orgID := bson.NewObjectID()
executor := &gatewayCardPayoutExecutor{}
_, err := executor.ExecuteCardPayout(context.Background(), sexec.StepRequest{
Payment: &agg.Payment{
OrganizationBoundBase: pm.OrganizationBoundBase{OrganizationRef: orgID},
PaymentRef: "payment-2",
IntentSnapshot: model.PaymentIntent{
Destination: model.PaymentEndpoint{
Type: model.EndpointTypeCard,
Card: &model.CardEndpoint{
Pan: "4111111111111111",
ExpMonth: 3,
ExpYear: 2030,
},
},
Amount: &paymenttypes.Money{Amount: "10", Currency: "RUB"},
},
},
Step: xplan.Step{
StepRef: "hop_4_card_payout_send",
StepCode: "hop.4.card_payout.send",
Action: model.RailOperationSend,
Rail: model.RailCardPayout,
},
StepExecution: agg.StepExecution{
StepRef: "hop_4_card_payout_send",
StepCode: "hop.4.card_payout.send",
Attempt: 1,
},
})
if err == nil {
t.Fatal("expected error")
}
if !strings.Contains(err.Error(), "mntx client is required") {
t.Fatalf("unexpected error: %v", err)
}
}

View File

@@ -192,6 +192,9 @@ func matchExecutionStep(payment *agg.Payment, msg *pmodel.PaymentGatewayExecutio
if stepRef, gatewayInstanceID, ok := findStepByExternalRef(payment, erecon.ExternalRefKindTransfer, transferRef); ok {
return stepRef, gatewayInstanceID
}
if stepRef, gatewayInstanceID, ok := findStepByExternalRef(payment, erecon.ExternalRefKindCardPayout, transferRef); ok {
return stepRef, gatewayInstanceID
}
}
operationRef := strings.TrimSpace(msg.OperationRef)
@@ -361,6 +364,11 @@ func buildObserveCandidate(step agg.StepExecution) (runningObserveCandidate, boo
candidate.transferRef = value
candidate.gatewayInstanceID = strings.TrimSpace(ref.GatewayInstanceID)
}
case strings.EqualFold(kind, erecon.ExternalRefKindCardPayout):
if candidate.transferRef == "" {
candidate.transferRef = value
candidate.gatewayInstanceID = strings.TrimSpace(ref.GatewayInstanceID)
}
case strings.EqualFold(kind, erecon.ExternalRefKindOperation):
if candidate.operationRef == "" {
candidate.operationRef = value

View File

@@ -96,6 +96,43 @@ func TestBuildGatewayExecutionEvent_FailedSetsTerminalNeedsAttentionHint(t *test
}
}
func TestBuildGatewayExecutionEvent_MatchesCardObserveByCardPayoutRef(t *testing.T) {
orgID := bson.NewObjectID()
payment := &agg.Payment{
OrganizationBoundBase: pm.OrganizationBoundBase{OrganizationRef: orgID},
PaymentRef: "payment-card-1",
StepExecutions: []agg.StepExecution{
{
StepRef: "hop_4_card_payout_observe",
StepCode: "hop.4.card_payout.observe",
State: agg.StepStateRunning,
ExternalRefs: []agg.ExternalRef{
{
GatewayInstanceID: "monetix",
Kind: erecon.ExternalRefKindCardPayout,
Ref: "payout-1",
},
},
},
},
}
event, ok := buildGatewayExecutionEvent(payment, &pm.PaymentGatewayExecution{
PaymentRef: payment.PaymentRef,
Status: rail.OperationResultSuccess,
TransferRef: "payout-1",
})
if !ok {
t.Fatal("expected gateway execution event to be accepted")
}
if got, want := event.StepRef, "hop_4_card_payout_observe"; got != want {
t.Fatalf("step_ref mismatch: got=%q want=%q", got, want)
}
if got, want := event.GatewayInstanceID, "monetix"; got != want {
t.Fatalf("gateway_instance_id mismatch: got=%q want=%q", got, want)
}
}
func TestOnPaymentGatewayExecution_ReconcilesUsingGlobalPaymentLookup(t *testing.T) {
orgID := bson.NewObjectID()
payment := &agg.Payment{
@@ -273,6 +310,36 @@ func TestRunningObserveCandidates(t *testing.T) {
}
}
func TestRunningObserveCandidates_UsesCardPayoutRefAsTransfer(t *testing.T) {
payment := &agg.Payment{
StepExecutions: []agg.StepExecution{
{
StepRef: "hop_4_card_payout_observe",
StepCode: "hop.4.card_payout.observe",
State: agg.StepStateRunning,
ExternalRefs: []agg.ExternalRef{
{
GatewayInstanceID: "monetix",
Kind: erecon.ExternalRefKindCardPayout,
Ref: "payout-2",
},
},
},
},
}
candidates := runningObserveCandidates(payment)
if len(candidates) != 1 {
t.Fatalf("candidate count mismatch: got=%d want=1", len(candidates))
}
if got, want := candidates[0].transferRef, "payout-2"; got != want {
t.Fatalf("transfer_ref mismatch: got=%q want=%q", got, want)
}
if got, want := candidates[0].gatewayInstanceID, "monetix"; got != want {
t.Fatalf("gateway_instance_id mismatch: got=%q want=%q", got, want)
}
}
func TestResolveObserveGateway_UsesExternalRefGatewayInstanceAcrossRails(t *testing.T) {
svc := &Service{
gatewayRegistry: &fakeGatewayRegistry{

View File

@@ -0,0 +1,371 @@
package orchestrator
import (
"context"
"fmt"
"strings"
ledgerclient "github.com/tech/sendico/ledger/client"
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/agg"
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/erecon"
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/sexec"
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/xplan"
"github.com/tech/sendico/payments/storage/model"
"github.com/tech/sendico/pkg/ledgerconv"
"github.com/tech/sendico/pkg/merrors"
"github.com/tech/sendico/pkg/model/account_role"
paymenttypes "github.com/tech/sendico/pkg/payments/types"
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
)
const (
ledgerMetadataMode = "mode"
)
type gatewayLedgerExecutor struct {
ledgerClient ledgerclient.Client
}
type ledgerRoles struct {
from account_role.AccountRole
to account_role.AccountRole
}
func (e *gatewayLedgerExecutor) ExecuteLedger(ctx context.Context, req sexec.StepRequest) (*sexec.ExecuteOutput, error) {
if req.Payment == nil {
return nil, merrors.InvalidArgument("ledger step: payment is required")
}
if e == nil || e.ledgerClient == nil {
return nil, merrors.InvalidArgument("ledger step: ledger client is required")
}
action := model.ParseRailOperation(string(req.Step.Action))
switch action {
case model.RailOperationDebit,
model.RailOperationCredit,
model.RailOperationExternalDebit,
model.RailOperationExternalCredit,
model.RailOperationMove,
model.RailOperationBlock,
model.RailOperationRelease:
default:
return nil, merrors.InvalidArgument("ledger step: unsupported action")
}
amount, err := ledgerAmountForStep(req.Payment, req.Step, action)
if err != nil {
return nil, err
}
roles, err := ledgerRolesForStep(req.Step, action)
if err != nil {
return nil, err
}
transferReq := &ledgerv1.TransferRequest{
IdempotencyKey: ledgerStepIdempotencyKey(req.Payment, req.Step),
OrganizationRef: req.Payment.OrganizationRef.Hex(),
Money: amount,
Description: ledgerDescription(req.Step),
Metadata: ledgerTransferMetadata(req.Payment, req.Step, roles),
FromRole: ledgerRoleToProto(roles.from),
ToRole: ledgerRoleToProto(roles.to),
}
resp, err := e.ledgerClient.TransferInternal(ctx, transferReq)
if err != nil {
return nil, err
}
if resp == nil || strings.TrimSpace(resp.GetJournalEntryRef()) == "" {
return nil, merrors.Internal("ledger step: journal entry reference is missing")
}
step := req.StepExecution
step.State = agg.StepStateCompleted
step.FailureCode = ""
step.FailureMsg = ""
step.ExternalRefs = appendLedgerExternalRef(step.ExternalRefs, agg.ExternalRef{
GatewayInstanceID: firstNonEmpty(strings.TrimSpace(req.Step.InstanceID), strings.TrimSpace(req.Step.Gateway)),
Kind: erecon.ExternalRefKindLedger,
Ref: strings.TrimSpace(resp.GetJournalEntryRef()),
})
return &sexec.ExecuteOutput{StepExecution: step}, nil
}
func ledgerAmountForStep(
payment *agg.Payment,
step xplan.Step,
action model.RailOperation,
) (*moneyv1.Money, error) {
sourceMoney := sourceMoneyForLedger(payment)
settlementMoney := settlementMoneyForLedger(payment, sourceMoney)
payoutMoney := payoutMoneyForLedger(payment, settlementMoney)
if fromRail, toRail, ok := ledgerBoundaryRails(payment, step); ok {
switch {
case isLedgerExternalRail(fromRail) && isLedgerExternalRail(toRail):
return protoMoneyRequired(sourceMoney, "ledger step: source amount is required")
case isLedgerExternalRail(fromRail) && isLedgerInternalRail(toRail):
return protoMoneyRequired(settlementMoney, "ledger step: settlement amount is required")
case isLedgerInternalRail(fromRail) && isLedgerExternalRail(toRail):
if toRail == model.RailCardPayout {
return protoMoneyRequired(payoutMoney, "ledger step: payout amount is required")
}
return protoMoneyRequired(settlementMoney, "ledger step: settlement amount is required")
case isLedgerInternalRail(fromRail) && isLedgerInternalRail(toRail):
return protoMoneyRequired(settlementMoney, "ledger step: settlement amount is required")
}
}
switch action {
case model.RailOperationCredit, model.RailOperationExternalCredit:
return protoMoneyRequired(settlementMoney, "ledger step: settlement amount is required")
case model.RailOperationDebit, model.RailOperationExternalDebit:
if sourceMoney != nil {
return protoMoneyRequired(sourceMoney, "ledger step: source amount is required")
}
return protoMoneyRequired(settlementMoney, "ledger step: settlement amount is required")
case model.RailOperationMove, model.RailOperationBlock, model.RailOperationRelease:
if settlementMoney != nil {
return protoMoneyRequired(settlementMoney, "ledger step: settlement amount is required")
}
return protoMoneyRequired(sourceMoney, "ledger step: source amount is required")
default:
return nil, merrors.InvalidArgument("ledger step: unsupported action")
}
}
func sourceMoneyForLedger(payment *agg.Payment) *paymenttypes.Money {
if payment == nil {
return nil
}
if payment.QuoteSnapshot != nil && payment.QuoteSnapshot.DebitAmount != nil {
return payment.QuoteSnapshot.DebitAmount
}
return payment.IntentSnapshot.Amount
}
func settlementMoneyForLedger(payment *agg.Payment, source *paymenttypes.Money) *paymenttypes.Money {
if payment != nil && payment.QuoteSnapshot != nil && payment.QuoteSnapshot.ExpectedSettlementAmount != nil {
return payment.QuoteSnapshot.ExpectedSettlementAmount
}
return source
}
func payoutMoneyForLedger(_ *agg.Payment, settlement *paymenttypes.Money) *paymenttypes.Money {
return settlement
}
func protoMoneyRequired(m *paymenttypes.Money, errMsg string) (*moneyv1.Money, error) {
if m == nil {
return nil, merrors.InvalidArgument(errMsg)
}
amount := strings.TrimSpace(m.GetAmount())
currency := strings.TrimSpace(m.GetCurrency())
if amount == "" || currency == "" {
return nil, merrors.InvalidArgument(errMsg)
}
return &moneyv1.Money{Amount: amount, Currency: currency}, nil
}
func ledgerRolesForStep(step xplan.Step, action model.RailOperation) (ledgerRoles, error) {
roles, ok, err := ledgerRolesFromMetadata(step.Metadata)
if err != nil {
return ledgerRoles{}, err
}
if ok {
return roles, nil
}
mode := strings.ToLower(strings.TrimSpace(step.Metadata[ledgerMetadataMode]))
switch action {
case model.RailOperationBlock:
return ledgerRoles{from: account_role.AccountRoleOperating, to: account_role.AccountRoleHold}, nil
case model.RailOperationRelease:
return ledgerRoles{from: account_role.AccountRoleHold, to: account_role.AccountRoleOperating}, nil
case model.RailOperationCredit, model.RailOperationExternalCredit:
return ledgerRoles{from: account_role.AccountRolePending, to: account_role.AccountRoleOperating}, nil
case model.RailOperationDebit, model.RailOperationExternalDebit:
if mode == "finalize_debit" {
return ledgerRoles{from: account_role.AccountRoleHold, to: account_role.AccountRoleTransit}, nil
}
return ledgerRoles{from: account_role.AccountRoleOperating, to: account_role.AccountRoleTransit}, nil
case model.RailOperationMove:
return ledgerRoles{from: account_role.AccountRoleOperating, to: account_role.AccountRoleTransit}, nil
default:
return ledgerRoles{}, merrors.InvalidArgument("ledger step: unsupported action")
}
}
func ledgerRolesFromMetadata(metadata map[string]string) (ledgerRoles, bool, error) {
fromValue, fromFound := metadataLookup(metadata, "from_role", "fromRole")
toValue, toFound := metadataLookup(metadata, "to_role", "toRole")
if !fromFound && !toFound {
return ledgerRoles{}, false, nil
}
if strings.TrimSpace(fromValue) == "" || strings.TrimSpace(toValue) == "" {
return ledgerRoles{}, false, merrors.InvalidArgument("ledger step: from_role and to_role must both be provided")
}
fromRole, ok := account_role.Parse(fromValue)
if !ok || fromRole == "" {
return ledgerRoles{}, false, merrors.InvalidArgument("ledger step: invalid from_role")
}
toRole, ok := account_role.Parse(toValue)
if !ok || toRole == "" {
return ledgerRoles{}, false, merrors.InvalidArgument("ledger step: invalid to_role")
}
if fromRole == toRole {
return ledgerRoles{}, false, merrors.InvalidArgument("ledger step: from_role and to_role must differ")
}
return ledgerRoles{from: fromRole, to: toRole}, true, nil
}
func metadataLookup(metadata map[string]string, keys ...string) (string, bool) {
if len(metadata) == 0 {
return "", false
}
for i := range keys {
key := strings.TrimSpace(keys[i])
if key == "" {
continue
}
value, ok := metadata[key]
if !ok {
continue
}
return value, true
}
return "", false
}
func ledgerRoleToProto(role account_role.AccountRole) ledgerv1.AccountRole {
parsed, _ := ledgerconv.ParseAccountRole(string(role))
return parsed
}
func ledgerTransferMetadata(payment *agg.Payment, step xplan.Step, roles ledgerRoles) map[string]string {
out := transferMetadata(step)
if out == nil {
out = map[string]string{}
}
if quoteRef := firstNonEmpty(strings.TrimSpace(payment.QuotationRef), strings.TrimSpace(quoteRefFromSnapshot(payment.QuoteSnapshot))); quoteRef != "" {
out[settlementMetadataQuoteRef] = quoteRef
}
if roles.from != "" {
out[account_role.MetadataKeyFromRole] = string(roles.from)
}
if roles.to != "" {
out[account_role.MetadataKeyToRole] = string(roles.to)
}
if mode := strings.TrimSpace(step.Metadata[ledgerMetadataMode]); mode != "" {
out[ledgerMetadataMode] = mode
}
if len(out) == 0 {
return nil
}
return out
}
func ledgerStepIdempotencyKey(payment *agg.Payment, step xplan.Step) string {
base := strings.TrimSpace(payment.IdempotencyKey)
if base == "" {
base = strings.TrimSpace(payment.PaymentRef)
}
stepToken := firstNonEmpty(strings.TrimSpace(step.StepRef), strings.TrimSpace(step.StepCode), "ledger")
if base == "" {
return "ledger:" + stepToken
}
return base + ":" + stepToken
}
func ledgerDescription(step xplan.Step) string {
code := strings.TrimSpace(step.StepCode)
if code == "" {
code = strings.TrimSpace(step.StepRef)
}
action := strings.ToLower(strings.TrimSpace(string(step.Action)))
if code == "" {
return "orchestration ledger " + action
}
return fmt.Sprintf("orchestration ledger %s %s", action, code)
}
func appendLedgerExternalRef(existing []agg.ExternalRef, ref agg.ExternalRef) []agg.ExternalRef {
ref.GatewayInstanceID = strings.TrimSpace(ref.GatewayInstanceID)
ref.Kind = strings.TrimSpace(ref.Kind)
ref.Ref = strings.TrimSpace(ref.Ref)
if ref.Kind == "" || ref.Ref == "" {
return existing
}
for i := range existing {
item := existing[i]
if strings.EqualFold(strings.TrimSpace(item.GatewayInstanceID), ref.GatewayInstanceID) &&
strings.EqualFold(strings.TrimSpace(item.Kind), ref.Kind) &&
strings.EqualFold(strings.TrimSpace(item.Ref), ref.Ref) {
return existing
}
}
return append(existing, ref)
}
func ledgerBoundaryRails(payment *agg.Payment, step xplan.Step) (model.Rail, model.Rail, bool) {
fromIndex, toIndex, ok := parseLedgerEdgeStepCode(step.StepCode)
if !ok || payment == nil || payment.QuoteSnapshot == nil || payment.QuoteSnapshot.Route == nil {
return model.RailUnspecified, model.RailUnspecified, false
}
fromRail := model.RailUnspecified
toRail := model.RailUnspecified
for i := range payment.QuoteSnapshot.Route.Hops {
hop := payment.QuoteSnapshot.Route.Hops[i]
if hop == nil {
continue
}
if hop.Index == fromIndex {
fromRail = model.ParseRail(hop.Rail)
}
if hop.Index == toIndex {
toRail = model.ParseRail(hop.Rail)
}
}
if fromRail == model.RailUnspecified || toRail == model.RailUnspecified {
return model.RailUnspecified, model.RailUnspecified, false
}
return fromRail, toRail, true
}
func parseLedgerEdgeStepCode(stepCode string) (uint32, uint32, bool) {
code := strings.ToLower(strings.TrimSpace(stepCode))
if !strings.HasPrefix(code, "edge.") || !strings.Contains(code, ".ledger.") {
return 0, 0, false
}
var (
from uint32
to uint32
op string
)
if _, err := fmt.Sscanf(code, "edge.%d_%d.ledger.%s", &from, &to, &op); err != nil {
return 0, 0, false
}
if strings.TrimSpace(op) == "" {
return 0, 0, false
}
return from, to, true
}
func isLedgerInternalRail(rail model.Rail) bool {
return rail == model.RailLedger
}
func isLedgerExternalRail(rail model.Rail) bool {
switch rail {
case model.RailCrypto, model.RailProviderSettlement, model.RailCardPayout, model.RailFiatOnRamp:
return true
default:
return false
}
}
var _ sexec.LedgerExecutor = (*gatewayLedgerExecutor)(nil)

View File

@@ -0,0 +1,275 @@
package orchestrator
import (
"context"
"strings"
"testing"
ledgerclient "github.com/tech/sendico/ledger/client"
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/agg"
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/erecon"
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/sexec"
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/xplan"
"github.com/tech/sendico/payments/storage/model"
pm "github.com/tech/sendico/pkg/model"
paymenttypes "github.com/tech/sendico/pkg/payments/types"
ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
"go.mongodb.org/mongo-driver/v2/bson"
)
func TestGatewayLedgerExecutor_ExecuteLedger_CreditUsesSourceAmountAndDefaultRoles(t *testing.T) {
orgID := bson.NewObjectID()
payment := testLedgerExecutorPayment(orgID)
var transferReq *ledgerv1.TransferRequest
executor := &gatewayLedgerExecutor{
ledgerClient: &ledgerclient.Fake{
TransferInternalFn: func(_ context.Context, req *ledgerv1.TransferRequest) (*ledgerv1.PostResponse, error) {
transferReq = req
return &ledgerv1.PostResponse{JournalEntryRef: "entry-1"}, nil
},
},
}
out, err := executor.ExecuteLedger(context.Background(), sexec.StepRequest{
Payment: payment,
Step: xplan.Step{
StepRef: "edge_1_2_ledger_credit",
StepCode: "edge.1_2.ledger.credit",
Action: model.RailOperationCredit,
Rail: model.RailLedger,
},
StepExecution: agg.StepExecution{
StepRef: "edge_1_2_ledger_credit",
StepCode: "edge.1_2.ledger.credit",
Attempt: 1,
},
})
if err != nil {
t.Fatalf("ExecuteLedger returned error: %v", err)
}
if out == nil {
t.Fatal("expected output")
}
if transferReq == nil {
t.Fatal("expected ledger transfer request")
}
if got, want := transferReq.GetMoney().GetAmount(), "1.000000"; got != want {
t.Fatalf("money.amount mismatch: got=%q want=%q", got, want)
}
if got, want := transferReq.GetMoney().GetCurrency(), "USDT"; got != want {
t.Fatalf("money.currency mismatch: got=%q want=%q", got, want)
}
if got, want := transferReq.GetFromRole(), ledgerv1.AccountRole_ACCOUNT_ROLE_PENDING; got != want {
t.Fatalf("from_role mismatch: got=%v want=%v", got, want)
}
if got, want := transferReq.GetToRole(), ledgerv1.AccountRole_ACCOUNT_ROLE_OPERATING; got != want {
t.Fatalf("to_role mismatch: got=%v want=%v", got, want)
}
if got, want := out.StepExecution.State, agg.StepStateCompleted; got != want {
t.Fatalf("state mismatch: got=%q want=%q", got, want)
}
if len(out.StepExecution.ExternalRefs) != 1 {
t.Fatalf("expected one external ref, got=%d", len(out.StepExecution.ExternalRefs))
}
if got, want := out.StepExecution.ExternalRefs[0].Kind, erecon.ExternalRefKindLedger; got != want {
t.Fatalf("external ref kind mismatch: got=%q want=%q", got, want)
}
if got, want := out.StepExecution.ExternalRefs[0].Ref, "entry-1"; got != want {
t.Fatalf("external ref value mismatch: got=%q want=%q", got, want)
}
}
func TestGatewayLedgerExecutor_ExecuteLedger_FinalizeDebitUsesHoldToTransitAndSettlementAmount(t *testing.T) {
orgID := bson.NewObjectID()
payment := testLedgerExecutorPayment(orgID)
var transferReq *ledgerv1.TransferRequest
executor := &gatewayLedgerExecutor{
ledgerClient: &ledgerclient.Fake{
TransferInternalFn: func(_ context.Context, req *ledgerv1.TransferRequest) (*ledgerv1.PostResponse, error) {
transferReq = req
return &ledgerv1.PostResponse{JournalEntryRef: "entry-2"}, nil
},
},
}
out, err := executor.ExecuteLedger(context.Background(), sexec.StepRequest{
Payment: payment,
Step: xplan.Step{
StepRef: "edge_3_4_ledger_debit",
StepCode: "edge.3_4.ledger.debit",
Action: model.RailOperationDebit,
Rail: model.RailLedger,
Metadata: map[string]string{"mode": "finalize_debit"},
HopIndex: 4,
HopRole: paymenttypes.QuoteRouteHopRoleTransit,
DependsOn: []string{"hop_4_card_payout_observe"},
},
StepExecution: agg.StepExecution{
StepRef: "edge_3_4_ledger_debit",
StepCode: "edge.3_4.ledger.debit",
Attempt: 1,
},
})
if err != nil {
t.Fatalf("ExecuteLedger returned error: %v", err)
}
if out == nil {
t.Fatal("expected output")
}
if transferReq == nil {
t.Fatal("expected ledger transfer request")
}
if got, want := transferReq.GetMoney().GetAmount(), "76.5"; got != want {
t.Fatalf("money.amount mismatch: got=%q want=%q", got, want)
}
if got, want := transferReq.GetMoney().GetCurrency(), "RUB"; got != want {
t.Fatalf("money.currency mismatch: got=%q want=%q", got, want)
}
if got, want := transferReq.GetFromRole(), ledgerv1.AccountRole_ACCOUNT_ROLE_HOLD; got != want {
t.Fatalf("from_role mismatch: got=%v want=%v", got, want)
}
if got, want := transferReq.GetToRole(), ledgerv1.AccountRole_ACCOUNT_ROLE_TRANSIT; got != want {
t.Fatalf("to_role mismatch: got=%v want=%v", got, want)
}
if got, want := transferReq.GetMetadata()[ledgerMetadataMode], "finalize_debit"; got != want {
t.Fatalf("mode metadata mismatch: got=%q want=%q", got, want)
}
}
func TestGatewayLedgerExecutor_ExecuteLedger_UsesMetadataRoleOverrides(t *testing.T) {
orgID := bson.NewObjectID()
payment := testLedgerExecutorPayment(orgID)
var transferReq *ledgerv1.TransferRequest
executor := &gatewayLedgerExecutor{
ledgerClient: &ledgerclient.Fake{
TransferInternalFn: func(_ context.Context, req *ledgerv1.TransferRequest) (*ledgerv1.PostResponse, error) {
transferReq = req
return &ledgerv1.PostResponse{JournalEntryRef: "entry-3"}, nil
},
},
}
_, err := executor.ExecuteLedger(context.Background(), sexec.StepRequest{
Payment: payment,
Step: xplan.Step{
StepRef: "edge_2_3_ledger_credit",
StepCode: "edge.2_3.ledger.credit",
Action: model.RailOperationCredit,
Rail: model.RailLedger,
Metadata: map[string]string{
"from_role": "reserve",
"to_role": "liquidity",
},
},
StepExecution: agg.StepExecution{
StepRef: "edge_2_3_ledger_credit",
StepCode: "edge.2_3.ledger.credit",
Attempt: 1,
},
})
if err != nil {
t.Fatalf("ExecuteLedger returned error: %v", err)
}
if transferReq == nil {
t.Fatal("expected ledger transfer request")
}
if got, want := transferReq.GetFromRole(), ledgerv1.AccountRole_ACCOUNT_ROLE_RESERVE; got != want {
t.Fatalf("from_role mismatch: got=%v want=%v", got, want)
}
if got, want := transferReq.GetToRole(), ledgerv1.AccountRole_ACCOUNT_ROLE_LIQUIDITY; got != want {
t.Fatalf("to_role mismatch: got=%v want=%v", got, want)
}
}
func TestGatewayLedgerExecutor_ExecuteLedger_ValidatesMetadataRoles(t *testing.T) {
orgID := bson.NewObjectID()
payment := testLedgerExecutorPayment(orgID)
executor := &gatewayLedgerExecutor{
ledgerClient: &ledgerclient.Fake{},
}
_, err := executor.ExecuteLedger(context.Background(), sexec.StepRequest{
Payment: payment,
Step: xplan.Step{
StepRef: "edge_2_3_ledger_credit",
StepCode: "edge.2_3.ledger.credit",
Action: model.RailOperationCredit,
Rail: model.RailLedger,
Metadata: map[string]string{
"from_role": "bad_role",
"to_role": "operating",
},
},
StepExecution: agg.StepExecution{StepRef: "edge_2_3_ledger_credit", StepCode: "edge.2_3.ledger.credit", Attempt: 1},
})
if err == nil {
t.Fatal("expected error")
}
if !strings.Contains(err.Error(), "invalid from_role") {
t.Fatalf("unexpected error: %v", err)
}
}
func TestGatewayLedgerExecutor_ExecuteLedger_RequiresLedgerClient(t *testing.T) {
orgID := bson.NewObjectID()
payment := testLedgerExecutorPayment(orgID)
executor := &gatewayLedgerExecutor{}
_, err := executor.ExecuteLedger(context.Background(), sexec.StepRequest{
Payment: payment,
Step: xplan.Step{
StepRef: "edge_1_2_ledger_credit",
StepCode: "edge.1_2.ledger.credit",
Action: model.RailOperationCredit,
Rail: model.RailLedger,
},
StepExecution: agg.StepExecution{StepRef: "edge_1_2_ledger_credit", StepCode: "edge.1_2.ledger.credit", Attempt: 1},
})
if err == nil {
t.Fatal("expected error")
}
if !strings.Contains(err.Error(), "ledger client is required") {
t.Fatalf("unexpected error: %v", err)
}
}
func testLedgerExecutorPayment(orgID bson.ObjectID) *agg.Payment {
return &agg.Payment{
OrganizationBoundBase: pm.OrganizationBoundBase{OrganizationRef: orgID},
PaymentRef: "payment-ledger-1",
IdempotencyKey: "idem-ledger-1",
QuotationRef: "quote-ledger-1",
IntentSnapshot: model.PaymentIntent{
Ref: "intent-ledger-1",
Source: model.PaymentEndpoint{
Type: model.EndpointTypeManagedWallet,
ManagedWallet: &model.ManagedWalletEndpoint{
ManagedWalletRef: "wallet-src",
},
},
Destination: model.PaymentEndpoint{
Type: model.EndpointTypeCard,
Card: &model.CardEndpoint{Pan: "4111111111111111"},
},
Amount: &paymenttypes.Money{Amount: "1", Currency: "USDT"},
},
QuoteSnapshot: &model.PaymentQuoteSnapshot{
DebitAmount: &paymenttypes.Money{Amount: "1.000000", Currency: "USDT"},
ExpectedSettlementAmount: &paymenttypes.Money{Amount: "76.5", Currency: "RUB"},
QuoteRef: "quote-ledger-1",
Route: &paymenttypes.QuoteRouteSpecification{
Hops: []*paymenttypes.QuoteRouteHop{
{Index: 1, Rail: "CRYPTO", Role: paymenttypes.QuoteRouteHopRoleSource},
{Index: 2, Rail: "SETTLEMENT", Role: paymenttypes.QuoteRouteHopRoleTransit},
{Index: 3, Rail: "LEDGER", Role: paymenttypes.QuoteRouteHopRoleTransit},
{Index: 4, Rail: "CARD", Role: paymenttypes.QuoteRouteHopRoleDestination},
},
},
},
}
}

View File

@@ -44,14 +44,24 @@ func WithFeeEngine(_ feesv1.FeeEngineClient, _ time.Duration) Option {
return func(*Service) {}
}
// WithLedgerClient is retained for backward-compatible wiring and is currently a no-op.
func WithLedgerClient(_ ledgerclient.Client) Option {
return func(*Service) {}
// WithLedgerClient configures internal ledger execution for ledger-bound steps.
func WithLedgerClient(client ledgerclient.Client) Option {
return func(s *Service) {
if s == nil {
return
}
s.ledgerClient = client
}
}
// WithMntxGateway is retained for backward-compatible wiring and is currently a no-op.
func WithMntxGateway(_ mntxclient.Client) Option {
return func(*Service) {}
// WithMntxGateway configures card payout execution for card-bound steps.
func WithMntxGateway(client mntxclient.Client) Option {
return func(s *Service) {
if s == nil {
return
}
s.mntxClient = client
}
}
// WithPaymentGatewayBroker wires broker subscription for payment gateway execution events.

View File

@@ -3,6 +3,8 @@ package orchestrator
import (
"context"
mntxclient "github.com/tech/sendico/gateway/mntx/client"
ledgerclient "github.com/tech/sendico/ledger/client"
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/prepo"
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/psvc"
"github.com/tech/sendico/payments/storage"
@@ -22,6 +24,8 @@ type Service struct {
v2 psvc.Service
paymentRepo prepo.Repository
ledgerClient ledgerclient.Client
mntxClient mntxclient.Client
gatewayInvokeResolver GatewayInvokeResolver
gatewayRegistry GatewayRegistry
cardGatewayRoutes map[string]CardGatewayRoute
@@ -49,6 +53,8 @@ func NewService(logger mlogger.Logger, repo storage.Repository, opts ...Option)
var err error
svc.v2, svc.paymentRepo, err = newOrchestrationV2Service(svc.logger, repo, v2RuntimeDeps{
LedgerClient: svc.ledgerClient,
MntxClient: svc.mntxClient,
GatewayInvokeResolver: svc.gatewayInvokeResolver,
GatewayRegistry: svc.gatewayRegistry,
CardGatewayRoutes: svc.cardGatewayRoutes,

View File

@@ -3,6 +3,8 @@ package orchestrator
import (
"context"
mntxclient "github.com/tech/sendico/gateway/mntx/client"
ledgerclient "github.com/tech/sendico/ledger/client"
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/oobs"
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/pquery"
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/prepo"
@@ -22,6 +24,8 @@ type v2MongoDBProvider interface {
}
type v2RuntimeDeps struct {
LedgerClient ledgerclient.Client
MntxClient mntxclient.Client
GatewayInvokeResolver GatewayInvokeResolver
GatewayRegistry GatewayRegistry
CardGatewayRoutes map[string]CardGatewayRoute
@@ -34,6 +38,14 @@ func newOrchestrationV2Service(logger mlogger.Logger, repo storage.Repository, r
if repo == nil {
return nil, nil, merrors.Internal("No repo for orchestrator v2 provided")
}
if runtimeDeps.LedgerClient == nil {
logger.Error("Orchestration v2 disabled: ledger client is missing")
return nil, nil, merrors.Internal("ledger client is required")
}
if checker, ok := runtimeDeps.LedgerClient.(interface{ Available() bool }); ok && !checker.Available() {
logger.Error("Orchestration v2 disabled: ledger client is unavailable")
return nil, nil, merrors.Internal("ledger client is unavailable")
}
paymentRepo, err := buildPaymentRepositoryV2(repo, logger)
if paymentRepo == nil || err != nil {
@@ -72,27 +84,42 @@ func newOrchestrationV2Service(logger mlogger.Logger, repo storage.Repository, r
}
func buildOrchestrationV2Executors(logger mlogger.Logger, runtimeDeps v2RuntimeDeps) sexec.Registry {
if runtimeDeps.GatewayInvokeResolver == nil || runtimeDeps.GatewayRegistry == nil {
return nil
}
execLogger := logger.Named("v2")
cryptoExecutor := &gatewayCryptoExecutor{
var cryptoExecutor sexec.CryptoExecutor
var providerSettlementExecutor sexec.ProviderSettlementExecutor
var guardExecutor sexec.GuardExecutor
if runtimeDeps.GatewayInvokeResolver != nil && runtimeDeps.GatewayRegistry != nil {
cryptoExecutor = &gatewayCryptoExecutor{
gatewayInvokeResolver: runtimeDeps.GatewayInvokeResolver,
gatewayRegistry: runtimeDeps.GatewayRegistry,
cardGatewayRoutes: cloneCardGatewayRoutes(runtimeDeps.CardGatewayRoutes),
}
providerSettlementExecutor := &gatewayProviderSettlementExecutor{
providerSettlementExecutor = &gatewayProviderSettlementExecutor{
gatewayInvokeResolver: runtimeDeps.GatewayInvokeResolver,
gatewayRegistry: runtimeDeps.GatewayRegistry,
}
guardExecutor := &gatewayGuardExecutor{
guardExecutor = &gatewayGuardExecutor{
logger: execLogger.Named("guard"),
gatewayInvokeResolver: runtimeDeps.GatewayInvokeResolver,
gatewayRegistry: runtimeDeps.GatewayRegistry,
}
}
ledgerExecutor := &gatewayLedgerExecutor{
ledgerClient: runtimeDeps.LedgerClient,
}
var cardPayoutExecutor sexec.CardPayoutExecutor
if runtimeDeps.MntxClient != nil {
cardPayoutExecutor = &gatewayCardPayoutExecutor{
mntxClient: runtimeDeps.MntxClient,
}
}
return psvc.NewDefaultExecutors(execLogger, sexec.Dependencies{
Ledger: ledgerExecutor,
Crypto: cryptoExecutor,
ProviderSettlement: providerSettlementExecutor,
CardPayout: cardPayoutExecutor,
Guard: guardExecutor,
})
}

View File

@@ -0,0 +1,83 @@
package orchestrator
import (
"context"
"strings"
"testing"
ledgerclient "github.com/tech/sendico/ledger/client"
"github.com/tech/sendico/payments/storage"
quotestorage "github.com/tech/sendico/payments/storage/quote"
"go.uber.org/zap"
)
func TestNewOrchestrationV2Service_FailsWhenLedgerClientMissing(t *testing.T) {
svc, repo, err := newOrchestrationV2Service(zap.NewNop(), fakeStorageRepo{}, v2RuntimeDeps{})
if err == nil {
t.Fatal("expected error")
}
if !strings.Contains(err.Error(), "ledger client is required") {
t.Fatalf("unexpected error: %v", err)
}
if svc != nil {
t.Fatal("expected nil service")
}
if repo != nil {
t.Fatal("expected nil payment repo")
}
}
func TestNewOrchestrationV2Service_FailsWhenLedgerClientUnavailable(t *testing.T) {
ledger := unavailableLedgerClient{Fake: &ledgerclient.Fake{}}
svc, repo, err := newOrchestrationV2Service(zap.NewNop(), fakeStorageRepo{}, v2RuntimeDeps{
LedgerClient: ledger,
})
if err == nil {
t.Fatal("expected error")
}
if !strings.Contains(err.Error(), "ledger client is unavailable") {
t.Fatalf("unexpected error: %v", err)
}
if svc != nil {
t.Fatal("expected nil service")
}
if repo != nil {
t.Fatal("expected nil payment repo")
}
}
type unavailableLedgerClient struct {
*ledgerclient.Fake
}
func (u unavailableLedgerClient) Available() bool {
return false
}
type fakeStorageRepo struct{}
func (fakeStorageRepo) Ping(context.Context) error {
return nil
}
func (fakeStorageRepo) Payments() storage.PaymentsStore {
return nil
}
func (fakeStorageRepo) PaymentMethods() storage.PaymentMethodsStore {
return nil
}
func (fakeStorageRepo) Quotes() quotestorage.QuotesStore {
return nil
}
func (fakeStorageRepo) Routes() storage.RoutesStore {
return nil
}
func (fakeStorageRepo) PlanTemplates() storage.PlanTemplatesStore {
return nil
}
var _ storage.Repository = fakeStorageRepo{}

View File

@@ -50,7 +50,7 @@ require (
github.com/prometheus/client_golang v1.23.2 // indirect
github.com/prometheus/client_model v0.6.2 // 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/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.2.0 // indirect

View File

@@ -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/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
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.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
github.com/prometheus/procfs v0.20.0 h1:AA7aCvjxwAquZAlonN7888f2u4IN8WVeFgBi4k82M4Q=
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/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=

View File

@@ -66,7 +66,7 @@ require (
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/prometheus/client_model v0.6.2 // 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/shirou/gopsutil/v3 v3.24.5 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect

View File

@@ -128,8 +128,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/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
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.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
github.com/prometheus/procfs v0.20.0 h1:AA7aCvjxwAquZAlonN7888f2u4IN8WVeFgBi4k82M4Q=
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/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=

View File

@@ -118,7 +118,7 @@ require (
github.com/prometheus/client_golang v1.23.2 // indirect
github.com/prometheus/client_model v0.6.2 // 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/segmentio/asm v1.2.1 // indirect
github.com/shirou/gopsutil/v3 v3.24.5 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect

View File

@@ -200,8 +200,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/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
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.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
github.com/prometheus/procfs v0.20.0 h1:AA7aCvjxwAquZAlonN7888f2u4IN8WVeFgBi4k82M4Q=
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/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0=