diff --git a/api/billing/fees/go.mod b/api/billing/fees/go.mod index e215c9ce..bb520c4d 100644 --- a/api/billing/fees/go.mod +++ b/api/billing/fees/go.mod @@ -49,6 +49,6 @@ require ( golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.40.0 // indirect golang.org/x/text v0.33.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 // indirect google.golang.org/protobuf v1.36.11 ) diff --git a/api/billing/fees/go.sum b/api/billing/fees/go.sum index 517a5d98..6c366ee7 100644 --- a/api/billing/fees/go.sum +++ b/api/billing/fees/go.sum @@ -212,8 +212,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= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3 h1:C4WAdL+FbjnGlpp2S+HMVhBeCq2Lcib4xZqfPNF6OoQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 h1:vzOYHDZEHIsPYYnaSYo60AqHkJronSu0rzTz/s4quL0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= diff --git a/api/discovery/go.mod b/api/discovery/go.mod index dd54c094..1f9502b0 100644 --- a/api/discovery/go.mod +++ b/api/discovery/go.mod @@ -45,7 +45,7 @@ require ( golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.40.0 // indirect golang.org/x/text v0.33.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 // indirect google.golang.org/grpc v1.78.0 // indirect google.golang.org/protobuf v1.36.11 // indirect ) diff --git a/api/discovery/go.sum b/api/discovery/go.sum index 517a5d98..6c366ee7 100644 --- a/api/discovery/go.sum +++ b/api/discovery/go.sum @@ -212,8 +212,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= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3 h1:C4WAdL+FbjnGlpp2S+HMVhBeCq2Lcib4xZqfPNF6OoQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 h1:vzOYHDZEHIsPYYnaSYo60AqHkJronSu0rzTz/s4quL0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= diff --git a/api/fx/ingestor/go.mod b/api/fx/ingestor/go.mod index 315e0498..fe2c2307 100644 --- a/api/fx/ingestor/go.mod +++ b/api/fx/ingestor/go.mod @@ -49,7 +49,7 @@ require ( golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.40.0 // indirect golang.org/x/text v0.33.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 // indirect google.golang.org/grpc v1.78.0 // indirect google.golang.org/protobuf v1.36.11 // indirect ) diff --git a/api/fx/ingestor/go.sum b/api/fx/ingestor/go.sum index 517a5d98..6c366ee7 100644 --- a/api/fx/ingestor/go.sum +++ b/api/fx/ingestor/go.sum @@ -212,8 +212,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= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3 h1:C4WAdL+FbjnGlpp2S+HMVhBeCq2Lcib4xZqfPNF6OoQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 h1:vzOYHDZEHIsPYYnaSYo60AqHkJronSu0rzTz/s4quL0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= diff --git a/api/fx/oracle/go.mod b/api/fx/oracle/go.mod index b466ffc3..8a687451 100644 --- a/api/fx/oracle/go.mod +++ b/api/fx/oracle/go.mod @@ -50,5 +50,5 @@ require ( golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.40.0 // indirect golang.org/x/text v0.33.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 // indirect ) diff --git a/api/fx/oracle/go.sum b/api/fx/oracle/go.sum index 517a5d98..6c366ee7 100644 --- a/api/fx/oracle/go.sum +++ b/api/fx/oracle/go.sum @@ -212,8 +212,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= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3 h1:C4WAdL+FbjnGlpp2S+HMVhBeCq2Lcib4xZqfPNF6OoQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 h1:vzOYHDZEHIsPYYnaSYo60AqHkJronSu0rzTz/s4quL0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= diff --git a/api/gateway/chain/go.mod b/api/gateway/chain/go.mod index 66305866..964b39b5 100644 --- a/api/gateway/chain/go.mod +++ b/api/gateway/chain/go.mod @@ -22,7 +22,7 @@ require ( require ( github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260119175649-084b3e29eee1 // indirect + github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260120135015-58f41aaaee16 // 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.9.2 // indirect @@ -86,5 +86,5 @@ require ( golang.org/x/sys v0.40.0 // indirect golang.org/x/text v0.33.0 // indirect golang.org/x/time v0.14.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 // indirect ) diff --git a/api/gateway/chain/go.sum b/api/gateway/chain/go.sum index d08bb83f..f8bfe9ce 100644 --- a/api/gateway/chain/go.sum +++ b/api/gateway/chain/go.sum @@ -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-20260119175649-084b3e29eee1 h1:uq8tHimZusJXFqiZG2EyQhyLYUWXkOumwNRFAyWcyHw= -github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260119175649-084b3e29eee1/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI= +github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260120135015-58f41aaaee16 h1:zhGT51SYKfpAlhdhfHqJbNMpjT5Q0E6/31byd8yTnxY= +github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260120135015-58f41aaaee16/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= @@ -362,8 +362,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= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3 h1:C4WAdL+FbjnGlpp2S+HMVhBeCq2Lcib4xZqfPNF6OoQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 h1:vzOYHDZEHIsPYYnaSYo60AqHkJronSu0rzTz/s4quL0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= diff --git a/api/gateway/mntx/go.mod b/api/gateway/mntx/go.mod index f70d32d3..9ccfe1dc 100644 --- a/api/gateway/mntx/go.mod +++ b/api/gateway/mntx/go.mod @@ -50,5 +50,5 @@ require ( golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.40.0 // indirect golang.org/x/text v0.33.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 // indirect ) diff --git a/api/gateway/mntx/go.sum b/api/gateway/mntx/go.sum index 9ad3265d..35298ad4 100644 --- a/api/gateway/mntx/go.sum +++ b/api/gateway/mntx/go.sum @@ -214,8 +214,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= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3 h1:C4WAdL+FbjnGlpp2S+HMVhBeCq2Lcib4xZqfPNF6OoQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 h1:vzOYHDZEHIsPYYnaSYo60AqHkJronSu0rzTz/s4quL0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= diff --git a/api/gateway/tgsettle/config.yml b/api/gateway/tgsettle/config.yml index 4360f9d2..e3208662 100644 --- a/api/gateway/tgsettle/config.yml +++ b/api/gateway/tgsettle/config.yml @@ -38,5 +38,5 @@ messaging: gateway: rail: "provider_settlement" target_chat_id_env: TGSETTLE_GATEWAY_CHAT_ID - timeout_seconds: 120 + timeout_seconds: 259200 accepted_user_ids: [] diff --git a/api/gateway/tgsettle/go.mod b/api/gateway/tgsettle/go.mod index f035a33e..d44133e1 100644 --- a/api/gateway/tgsettle/go.mod +++ b/api/gateway/tgsettle/go.mod @@ -47,5 +47,5 @@ require ( golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.40.0 // indirect golang.org/x/text v0.33.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 // indirect ) diff --git a/api/gateway/tgsettle/go.sum b/api/gateway/tgsettle/go.sum index 517a5d98..6c366ee7 100644 --- a/api/gateway/tgsettle/go.sum +++ b/api/gateway/tgsettle/go.sum @@ -212,8 +212,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= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3 h1:C4WAdL+FbjnGlpp2S+HMVhBeCq2Lcib4xZqfPNF6OoQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 h1:vzOYHDZEHIsPYYnaSYo60AqHkJronSu0rzTz/s4quL0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= diff --git a/api/gateway/tgsettle/internal/service/gateway/service.go b/api/gateway/tgsettle/internal/service/gateway/service.go index 505111a3..463cc784 100644 --- a/api/gateway/tgsettle/internal/service/gateway/service.go +++ b/api/gateway/tgsettle/internal/service/gateway/service.go @@ -18,6 +18,7 @@ import ( confirmations "github.com/tech/sendico/pkg/messaging/notifications/confirmations" paymentgateway "github.com/tech/sendico/pkg/messaging/notifications/paymentgateway" np "github.com/tech/sendico/pkg/messaging/notifications/processor" + tnotifications "github.com/tech/sendico/pkg/messaging/notifications/telegram" "github.com/tech/sendico/pkg/mlogger" "github.com/tech/sendico/pkg/model" "github.com/tech/sendico/pkg/mservice" @@ -34,8 +35,9 @@ import ( ) const ( - defaultConfirmationTimeoutSeconds = 120 + defaultConfirmationTimeoutSeconds = 259200 executedStatus = "executed" + telegramSuccessReaction = "\u2705" ) const ( @@ -43,6 +45,8 @@ const ( metadataQuoteRef = "quote_ref" metadataTargetChatID = "target_chat_id" metadataOutgoingLeg = "outgoing_leg" + metadataSourceAmount = "source_amount" + metadataSourceCurrency = "source_currency" ) type Config struct { @@ -77,14 +81,14 @@ func NewService(logger mlogger.Logger, repo storage.Repository, producer msg.Pro } logger = logger.Named("service") svc := &Service{ - logger: logger, - repo: repo, - producer: producer, - broker: broker, - cfg: cfg, - rail: strings.TrimSpace(cfg.Rail), + logger: logger, + repo: repo, + producer: producer, + broker: broker, + cfg: cfg, + rail: strings.TrimSpace(cfg.Rail), invokeURI: strings.TrimSpace(cfg.InvokeURI), - pending: map[string]*model.PaymentGatewayIntent{}, + pending: map[string]*model.PaymentGatewayIntent{}, } svc.chatID = strings.TrimSpace(readEnv(cfg.TargetChatIDEnv)) svc.startConsumers() @@ -322,6 +326,7 @@ func (s *Service) onConfirmationResult(ctx context.Context, result *model.Confir } s.publishExecution(intent, result) + s.publishTelegramReaction(result) s.removeIntent(requestID) return nil } @@ -416,6 +421,39 @@ func (s *Service) publishExecution(intent *model.PaymentGatewayIntent, result *m zap.String("status", string(result.Status))) } +func (s *Service) publishTelegramReaction(result *model.ConfirmationResult) { + if s == nil || s.producer == nil || result == nil || result.RawReply == nil { + return + } + if result.Status != model.ConfirmationStatusConfirmed && result.Status != model.ConfirmationStatusClarified { + return + } + request := &model.TelegramReactionRequest{ + RequestID: strings.TrimSpace(result.RequestID), + ChatID: strings.TrimSpace(result.RawReply.ChatID), + MessageID: strings.TrimSpace(result.RawReply.MessageID), + Emoji: telegramSuccessReaction, + } + if request.ChatID == "" || request.MessageID == "" || request.Emoji == "" { + return + } + env := tnotifications.TelegramReaction(string(mservice.PaymentGateway), request) + if err := s.producer.SendMessage(env); err != nil { + s.logger.Warn("Failed to publish telegram reaction", + zap.Error(err), + zap.String("request_id", request.RequestID), + zap.String("chat_id", request.ChatID), + zap.String("message_id", request.MessageID), + zap.String("emoji", request.Emoji)) + return + } + s.logger.Info("Published telegram reaction", + zap.String("request_id", request.RequestID), + zap.String("chat_id", request.ChatID), + zap.String("message_id", request.MessageID), + zap.String("emoji", request.Emoji)) +} + func (s *Service) trackIntent(requestID string, intent *model.PaymentGatewayIntent) { if s == nil || intent == nil { return @@ -507,6 +545,7 @@ func intentFromSubmitTransfer(req *chainv1.SubmitTransferRequest, defaultRail, d if amount == nil { return nil, merrors.InvalidArgument("submit_transfer: amount is required") } + metadata := req.GetMetadata() requestedMoney := &paymenttypes.Money{ Amount: strings.TrimSpace(amount.GetAmount()), Currency: strings.TrimSpace(amount.GetCurrency()), @@ -514,7 +553,14 @@ func intentFromSubmitTransfer(req *chainv1.SubmitTransferRequest, defaultRail, d if requestedMoney.Amount == "" || requestedMoney.Currency == "" { return nil, merrors.InvalidArgument("submit_transfer: amount is required") } - metadata := req.GetMetadata() + sourceAmount := strings.TrimSpace(metadata[metadataSourceAmount]) + sourceCurrency := strings.TrimSpace(metadata[metadataSourceCurrency]) + if sourceAmount != "" && sourceCurrency != "" { + requestedMoney = &paymenttypes.Money{ + Amount: sourceAmount, + Currency: sourceCurrency, + } + } paymentIntentID := strings.TrimSpace(req.GetClientReference()) if paymentIntentID == "" { paymentIntentID = strings.TrimSpace(metadata[metadataPaymentIntentID]) diff --git a/api/gateway/tgsettle/internal/service/gateway/service_test.go b/api/gateway/tgsettle/internal/service/gateway/service_test.go index 66914224..3d5d4448 100644 --- a/api/gateway/tgsettle/internal/service/gateway/service_test.go +++ b/api/gateway/tgsettle/internal/service/gateway/service_test.go @@ -9,11 +9,13 @@ import ( "github.com/tech/sendico/gateway/tgsettle/storage" storagemodel "github.com/tech/sendico/gateway/tgsettle/storage/model" envelope "github.com/tech/sendico/pkg/messaging/envelope" + mloggerfactory "github.com/tech/sendico/pkg/mlogger/factory" "github.com/tech/sendico/pkg/model" notification "github.com/tech/sendico/pkg/model/notification" "github.com/tech/sendico/pkg/mservice" - mloggerfactory "github.com/tech/sendico/pkg/mlogger/factory" paymenttypes "github.com/tech/sendico/pkg/payments/types" + moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1" + chainv1 "github.com/tech/sendico/pkg/proto/gateway/chain/v1" ) type fakePaymentsStore struct { @@ -147,6 +149,25 @@ func TestOnIntentCreatesConfirmationRequest(t *testing.T) { } } +func TestIntentFromSubmitTransferUsesSourceMoney(t *testing.T) { + req := &chainv1.SubmitTransferRequest{ + IdempotencyKey: "idem-1", + ClientReference: "pi-1", + Amount: &moneyv1.Money{Amount: "10.00", Currency: "EUR"}, + Metadata: map[string]string{ + metadataSourceAmount: "12.34", + metadataSourceCurrency: "USD", + }, + } + intent, err := intentFromSubmitTransfer(req, "provider_settlement", "") + if err != nil { + t.Fatalf("intentFromSubmitTransfer error: %v", err) + } + if intent.RequestedMoney == nil || intent.RequestedMoney.Amount != "12.34" || intent.RequestedMoney.Currency != "USD" { + t.Fatalf("expected source money override, got: %#v", intent.RequestedMoney) + } +} + func TestConfirmationResultPersistsExecutionAndReply(t *testing.T) { logger := mloggerfactory.NewLogger(false) repo := &fakeRepo{payments: &fakePaymentsStore{}, tg: &fakeTelegramStore{}} diff --git a/api/ledger/go.mod b/api/ledger/go.mod index 6c72cd52..764ee67e 100644 --- a/api/ledger/go.mod +++ b/api/ledger/go.mod @@ -51,5 +51,5 @@ require ( golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.40.0 // indirect golang.org/x/text v0.33.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 // indirect ) diff --git a/api/ledger/go.sum b/api/ledger/go.sum index 57cc5807..1571bd2e 100644 --- a/api/ledger/go.sum +++ b/api/ledger/go.sum @@ -214,8 +214,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= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3 h1:C4WAdL+FbjnGlpp2S+HMVhBeCq2Lcib4xZqfPNF6OoQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 h1:vzOYHDZEHIsPYYnaSYo60AqHkJronSu0rzTz/s4quL0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= diff --git a/api/notification/go.mod b/api/notification/go.mod index d162bd47..949d5742 100644 --- a/api/notification/go.mod +++ b/api/notification/go.mod @@ -52,7 +52,7 @@ require ( golang.org/x/net v0.49.0 // indirect golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.40.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 // indirect google.golang.org/grpc v1.78.0 // indirect google.golang.org/protobuf v1.36.11 // indirect ) diff --git a/api/notification/go.sum b/api/notification/go.sum index 3adcd3cc..5444e385 100644 --- a/api/notification/go.sum +++ b/api/notification/go.sum @@ -229,8 +229,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= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3 h1:C4WAdL+FbjnGlpp2S+HMVhBeCq2Lcib4xZqfPNF6OoQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 h1:vzOYHDZEHIsPYYnaSYo60AqHkJronSu0rzTz/s4quL0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= diff --git a/api/notification/internal/server/notificationimp/confirmation.go b/api/notification/internal/server/notificationimp/confirmation.go index a25971d3..22aa2aa0 100644 --- a/api/notification/internal/server/notificationimp/confirmation.go +++ b/api/notification/internal/server/notificationimp/confirmation.go @@ -9,9 +9,9 @@ import ( "time" "github.com/tech/sendico/notification/internal/server/notificationimp/telegram" + "github.com/tech/sendico/pkg/merrors" msg "github.com/tech/sendico/pkg/messaging" confirmations "github.com/tech/sendico/pkg/messaging/notifications/confirmations" - "github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/mlogger" "github.com/tech/sendico/pkg/model" "github.com/tech/sendico/pkg/mservice" @@ -35,12 +35,12 @@ type confirmationManager struct { } type confirmationState struct { - request model.ConfirmationRequest - requestMessageID string - targetChatID string - callbackSubject string - clarified bool - timer *time.Timer + request model.ConfirmationRequest + requestMessageID string + targetChatID string + callbackSubject string + clarified bool + timer *time.Timer } func newConfirmationManager(logger mlogger.Logger, tg telegram.Client, outbox msg.Producer) *confirmationManager { diff --git a/api/notification/internal/server/notificationimp/notification.go b/api/notification/internal/server/notificationimp/notification.go index 03b09d7d..97d0475f 100644 --- a/api/notification/internal/server/notificationimp/notification.go +++ b/api/notification/internal/server/notificationimp/notification.go @@ -11,10 +11,11 @@ import ( "github.com/tech/sendico/pkg/domainprovider" "github.com/tech/sendico/pkg/merrors" na "github.com/tech/sendico/pkg/messaging/notifications/account" - confirmations "github.com/tech/sendico/pkg/messaging/notifications/confirmations" cnotifications "github.com/tech/sendico/pkg/messaging/notifications/confirmation" + confirmations "github.com/tech/sendico/pkg/messaging/notifications/confirmations" ni "github.com/tech/sendico/pkg/messaging/notifications/invitation" snotifications "github.com/tech/sendico/pkg/messaging/notifications/site" + tnotifications "github.com/tech/sendico/pkg/messaging/notifications/telegram" "github.com/tech/sendico/pkg/mlogger" "github.com/tech/sendico/pkg/model" "github.com/tech/sendico/pkg/mservice" @@ -91,6 +92,10 @@ func CreateAPI(a api.API) (*NotificationAPI, error) { p.logger.Error("Failed to register confirmation request handler", zap.Error(err)) return nil, err } + if err := a.Register().Consumer(tnotifications.NewTelegramReactionProcessor(p.logger, p.onTelegramReaction)); err != nil { + p.logger.Error("Failed to register telegram reaction handler", zap.Error(err)) + return nil, err + } idb, err := a.DBFactory().NewInvitationsDB() if err != nil { diff --git a/api/notification/internal/server/notificationimp/telegram/client.go b/api/notification/internal/server/notificationimp/telegram/client.go index 43a2ef02..7472d990 100644 --- a/api/notification/internal/server/notificationimp/telegram/client.go +++ b/api/notification/internal/server/notificationimp/telegram/client.go @@ -26,6 +26,7 @@ type Client interface { SendContactRequest(ctx context.Context, request *model.ContactRequest) error SendCallRequest(ctx context.Context, request *model.CallRequest) error SendText(ctx context.Context, chatID string, text string, replyToMessageID string) (*model.TelegramMessage, error) + SetMessageReaction(ctx context.Context, chatID string, messageID string, emoji string) error } type client struct { @@ -132,6 +133,25 @@ type messageUser struct { Username string `json:"username,omitempty"` } +type setMessageReactionPayload struct { + ChatID string `json:"chat_id"` + MessageID int64 `json:"message_id"` + Reaction []reactionType `json:"reaction,omitempty"` + IsBig bool `json:"is_big,omitempty"` +} + +type reactionType struct { + Type string `json:"type"` + Emoji string `json:"emoji,omitempty"` + CustomEmojiID string `json:"custom_emoji_id,omitempty"` +} + +type setMessageReactionResponse struct { + OK bool `json:"ok"` + Result bool `json:"result,omitempty"` + Description string `json:"description,omitempty"` +} + func (c *client) sendMessage(ctx context.Context, payload sendMessagePayload) (*model.TelegramMessage, error) { body, err := json.Marshal(&payload) if err != nil { @@ -193,7 +213,12 @@ func (c *client) sendMessage(ctx context.Context, payload sendMessagePayload) (* } func (c *client) endpoint() string { - return fmt.Sprintf("%s/bot%s/sendMessage", c.apiURL, c.botToken) + return c.endpointFor("sendMessage") +} + +func (c *client) endpointFor(method string) string { + method = strings.TrimPrefix(strings.TrimSpace(method), "/") + return fmt.Sprintf("%s/bot%s/%s", c.apiURL, c.botToken, method) } func (c *client) SendContactRequest(ctx context.Context, request *model.ContactRequest) error { @@ -248,6 +273,80 @@ func (c *client) SendText(ctx context.Context, chatID string, text string, reply return c.sendMessage(ctx, payload) } +func (c *client) SetMessageReaction(ctx context.Context, chatID string, messageID string, emoji string) error { + chatID = strings.TrimSpace(chatID) + if chatID == "" { + return merrors.InvalidArgument("telegram chat id is empty", "chat_id") + } + msgID, err := strconv.ParseInt(strings.TrimSpace(messageID), 10, 64) + if err != nil { + return merrors.InvalidArgumentWrap(err, "invalid message_id", "message_id") + } + emoji = strings.TrimSpace(emoji) + reaction := []reactionType{} + if emoji != "" { + reaction = append(reaction, reactionType{Type: "emoji", Emoji: emoji}) + } + payload := setMessageReactionPayload{ + ChatID: chatID, + MessageID: msgID, + Reaction: reaction, + } + return c.sendReaction(ctx, payload) +} + +func (c *client) sendReaction(ctx context.Context, payload setMessageReactionPayload) error { + body, err := json.Marshal(&payload) + if err != nil { + c.logger.Warn("Failed to marshal telegram reaction payload", zap.Error(err)) + return err + } + req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.endpointFor("setMessageReaction"), bytes.NewReader(body)) + if err != nil { + c.logger.Warn("Failed to create telegram reaction request", zap.Error(err)) + return err + } + req.Header.Set("Content-Type", "application/json") + + fields := []zap.Field{ + zap.String("chat_id", payload.ChatID), + zap.Int64("message_id", payload.MessageID), + zap.Int("payload_size_bytes", len(body)), + } + c.logger.Debug("Sending Telegram reaction", fields...) + start := time.Now() + + resp, err := c.httpClient.Do(req) + if err != nil { + c.logger.Warn("Telegram reaction request failed", zap.Error(err)) + return err + } + defer resp.Body.Close() + + respBody, _ := io.ReadAll(io.LimitReader(resp.Body, 16<<10)) + if resp.StatusCode >= http.StatusOK && resp.StatusCode < http.StatusMultipleChoices { + var parsed setMessageReactionResponse + if err := json.Unmarshal(respBody, &parsed); err != nil { + c.logger.Warn("Failed to decode telegram reaction response", zap.Error(err)) + return err + } + if !parsed.OK || !parsed.Result { + msg := "telegram setMessageReaction response missing result" + if parsed.Description != "" { + msg = parsed.Description + } + return merrors.Internal(msg) + } + c.logger.Debug("Telegram reaction sent", zap.Int("status_code", resp.StatusCode), zap.Duration("latency", time.Since(start))) + return nil + } + c.logger.Warn("Telegram API returned non-success status for reaction", + zap.Int("status_code", resp.StatusCode), + zap.ByteString("response_body", respBody), + zap.String("chat_id", payload.ChatID)) + return merrors.Internal(fmt.Sprintf("telegram setMessageReaction failed with status %d: %s", resp.StatusCode, string(respBody))) +} + func toTelegramMessage(msg *messageResponse) *model.TelegramMessage { if msg == nil { return nil diff --git a/api/notification/internal/server/notificationimp/telegram_reaction.go b/api/notification/internal/server/notificationimp/telegram_reaction.go new file mode 100644 index 00000000..e8565e90 --- /dev/null +++ b/api/notification/internal/server/notificationimp/telegram_reaction.go @@ -0,0 +1,46 @@ +package notificationimp + +import ( + "context" + "strings" + + "github.com/tech/sendico/pkg/merrors" + "github.com/tech/sendico/pkg/model" + "go.uber.org/zap" +) + +func (a *NotificationAPI) onTelegramReaction(ctx context.Context, request *model.TelegramReactionRequest) error { + if request == nil { + return merrors.InvalidArgument("telegram reaction request is nil", "request") + } + if a.tg == nil { + return merrors.Internal("telegram client is not configured") + } + chatID := strings.TrimSpace(request.ChatID) + messageID := strings.TrimSpace(request.MessageID) + if chatID == "" { + return merrors.InvalidArgument("telegram chat_id is required", "chat_id") + } + if messageID == "" { + return merrors.InvalidArgument("telegram message_id is required", "message_id") + } + emoji := strings.TrimSpace(request.Emoji) + if emoji == "" { + return merrors.InvalidArgument("telegram emoji is required", "emoji") + } + if err := a.tg.SetMessageReaction(ctx, chatID, messageID, emoji); err != nil { + a.logger.Warn("Failed to send telegram reaction", + zap.Error(err), + zap.String("request_id", strings.TrimSpace(request.RequestID)), + zap.String("chat_id", chatID), + zap.String("message_id", messageID), + zap.String("emoji", emoji)) + return err + } + a.logger.Info("Telegram reaction sent", + zap.String("request_id", strings.TrimSpace(request.RequestID)), + zap.String("chat_id", chatID), + zap.String("message_id", messageID), + zap.String("emoji", emoji)) + return nil +} diff --git a/api/payments/orchestrator/go.mod b/api/payments/orchestrator/go.mod index 104d650d..4e2d8667 100644 --- a/api/payments/orchestrator/go.mod +++ b/api/payments/orchestrator/go.mod @@ -62,5 +62,5 @@ require ( golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.40.0 // indirect golang.org/x/text v0.33.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 // indirect ) diff --git a/api/payments/orchestrator/go.sum b/api/payments/orchestrator/go.sum index caaa6023..b2d58631 100644 --- a/api/payments/orchestrator/go.sum +++ b/api/payments/orchestrator/go.sum @@ -215,8 +215,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= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3 h1:C4WAdL+FbjnGlpp2S+HMVhBeCq2Lcib4xZqfPNF6OoQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 h1:vzOYHDZEHIsPYYnaSYo60AqHkJronSu0rzTz/s4quL0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= diff --git a/api/payments/orchestrator/internal/service/orchestrator/provider_settlement.go b/api/payments/orchestrator/internal/service/orchestrator/provider_settlement.go index 8912c0ce..66034c8f 100644 --- a/api/payments/orchestrator/internal/service/orchestrator/provider_settlement.go +++ b/api/payments/orchestrator/internal/service/orchestrator/provider_settlement.go @@ -15,6 +15,8 @@ const ( providerSettlementMetaQuoteRef = "quote_ref" providerSettlementMetaTargetChatID = "target_chat_id" providerSettlementMetaOutgoingLeg = "outgoing_leg" + providerSettlementMetaSourceAmount = "source_amount" + providerSettlementMetaSourceCurrency = "source_currency" ) func (p *paymentExecutor) buildProviderSettlementTransferRequest(payment *model.Payment, step *model.PaymentStep, amount *paymenttypes.Money, quote *orchestratorv1.PaymentQuote, idx int) (rail.TransferRequest, error) { @@ -46,6 +48,15 @@ func (p *paymentExecutor) buildProviderSettlementTransferRequest(payment *model. if strings.TrimSpace(metadata[providerSettlementMetaOutgoingLeg]) == "" { metadata[providerSettlementMetaOutgoingLeg] = strings.ToLower(strings.TrimSpace(string(step.Rail))) } + sourceAmount := resolveDebitAmount(payment, quote, payment.Intent.Amount) + if sourceAmount != nil { + if strings.TrimSpace(metadata[providerSettlementMetaSourceAmount]) == "" { + metadata[providerSettlementMetaSourceAmount] = strings.TrimSpace(sourceAmount.Amount) + } + if strings.TrimSpace(metadata[providerSettlementMetaSourceCurrency]) == "" { + metadata[providerSettlementMetaSourceCurrency] = strings.TrimSpace(sourceAmount.Currency) + } + } sourceWalletRef := "" if payment.Intent.Source.ManagedWallet != nil { diff --git a/api/pkg/go.mod b/api/pkg/go.mod index c3f933a5..78ca6977 100644 --- a/api/pkg/go.mod +++ b/api/pkg/go.mod @@ -93,6 +93,6 @@ require ( golang.org/x/sys v0.40.0 // indirect golang.org/x/text v0.33.0 // indirect golang.org/x/time v0.5.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/api/pkg/go.sum b/api/pkg/go.sum index 7878e568..d55eaa64 100644 --- a/api/pkg/go.sum +++ b/api/pkg/go.sum @@ -269,8 +269,8 @@ gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= google.golang.org/genproto/googleapis/api v0.0.0-20251029180050-ab9386a59fda h1:+2XxjfsAu6vqFxwGBRcHiMaDCuZiqXGDUDVWVtrFAnE= google.golang.org/genproto/googleapis/api v0.0.0-20251029180050-ab9386a59fda/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3 h1:C4WAdL+FbjnGlpp2S+HMVhBeCq2Lcib4xZqfPNF6OoQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 h1:vzOYHDZEHIsPYYnaSYo60AqHkJronSu0rzTz/s4quL0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= diff --git a/api/pkg/messaging/internal/notifications/telegram/notification.go b/api/pkg/messaging/internal/notifications/telegram/notification.go new file mode 100644 index 00000000..519b38ef --- /dev/null +++ b/api/pkg/messaging/internal/notifications/telegram/notification.go @@ -0,0 +1,38 @@ +package notifications + +import ( + "encoding/json" + + messaging "github.com/tech/sendico/pkg/messaging/envelope" + "github.com/tech/sendico/pkg/model" + nm "github.com/tech/sendico/pkg/model/notification" + "github.com/tech/sendico/pkg/mservice" +) + +type TelegramReactionNotification struct { + messaging.Envelope + payload model.TelegramReactionRequest +} + +func (trn *TelegramReactionNotification) Serialize() ([]byte, error) { + data, err := json.Marshal(trn.payload) + if err != nil { + return nil, err + } + return trn.Envelope.Wrap(data) +} + +func telegramReactionEvent() model.NotificationEvent { + return model.NewNotification(mservice.Notifications, nm.NATelegramReaction) +} + +func NewTelegramReactionEnvelope(sender string, request *model.TelegramReactionRequest) messaging.Envelope { + var payload model.TelegramReactionRequest + if request != nil { + payload = *request + } + return &TelegramReactionNotification{ + Envelope: messaging.CreateEnvelope(sender, telegramReactionEvent()), + payload: payload, + } +} diff --git a/api/pkg/messaging/internal/notifications/telegram/processor.go b/api/pkg/messaging/internal/notifications/telegram/processor.go new file mode 100644 index 00000000..77aad526 --- /dev/null +++ b/api/pkg/messaging/internal/notifications/telegram/processor.go @@ -0,0 +1,47 @@ +package notifications + +import ( + "context" + "encoding/json" + + me "github.com/tech/sendico/pkg/messaging/envelope" + np "github.com/tech/sendico/pkg/messaging/notifications/processor" + ch "github.com/tech/sendico/pkg/messaging/notifications/telegram/handler" + "github.com/tech/sendico/pkg/mlogger" + "github.com/tech/sendico/pkg/model" + "go.uber.org/zap" +) + +type TelegramReactionProcessor struct { + logger mlogger.Logger + handler ch.TelegramReactionHandler + event model.NotificationEvent +} + +func (trp *TelegramReactionProcessor) Process(ctx context.Context, envelope me.Envelope) error { + var msg model.TelegramReactionRequest + if err := json.Unmarshal(envelope.GetData(), &msg); err != nil { + trp.logger.Warn("Failed to decode telegram reaction envelope", zap.Error(err), zap.String("topic", trp.event.ToString())) + return err + } + if trp.handler == nil { + trp.logger.Warn("Telegram reaction handler is not configured", zap.String("topic", trp.event.ToString())) + return nil + } + return trp.handler(ctx, &msg) +} + +func (trp *TelegramReactionProcessor) GetSubject() model.NotificationEvent { + return trp.event +} + +func NewTelegramReactionProcessor(logger mlogger.Logger, handler ch.TelegramReactionHandler) np.EnvelopeProcessor { + if logger != nil { + logger = logger.Named("telegram_reaction_processor") + } + return &TelegramReactionProcessor{ + logger: logger, + handler: handler, + event: telegramReactionEvent(), + } +} diff --git a/api/pkg/messaging/notifications/telegram/handler/interface.go b/api/pkg/messaging/notifications/telegram/handler/interface.go new file mode 100644 index 00000000..7d636cf5 --- /dev/null +++ b/api/pkg/messaging/notifications/telegram/handler/interface.go @@ -0,0 +1,9 @@ +package handler + +import ( + "context" + + "github.com/tech/sendico/pkg/model" +) + +type TelegramReactionHandler = func(context.Context, *model.TelegramReactionRequest) error diff --git a/api/pkg/messaging/notifications/telegram/telegram.go b/api/pkg/messaging/notifications/telegram/telegram.go new file mode 100644 index 00000000..fcafc577 --- /dev/null +++ b/api/pkg/messaging/notifications/telegram/telegram.go @@ -0,0 +1,18 @@ +package notifications + +import ( + messaging "github.com/tech/sendico/pkg/messaging/envelope" + tinternal "github.com/tech/sendico/pkg/messaging/internal/notifications/telegram" + np "github.com/tech/sendico/pkg/messaging/notifications/processor" + ch "github.com/tech/sendico/pkg/messaging/notifications/telegram/handler" + "github.com/tech/sendico/pkg/mlogger" + "github.com/tech/sendico/pkg/model" +) + +func TelegramReaction(sender string, request *model.TelegramReactionRequest) messaging.Envelope { + return tinternal.NewTelegramReactionEnvelope(sender, request) +} + +func NewTelegramReactionProcessor(logger mlogger.Logger, handler ch.TelegramReactionHandler) np.EnvelopeProcessor { + return tinternal.NewTelegramReactionProcessor(logger, handler) +} diff --git a/api/pkg/model/confirmation.go b/api/pkg/model/confirmation.go index 6cdba30b..5b358c9d 100644 --- a/api/pkg/model/confirmation.go +++ b/api/pkg/model/confirmation.go @@ -27,16 +27,6 @@ type ConfirmationResult struct { RequestID string `bson:"requestId,omitempty" json:"request_id,omitempty"` Money *paymenttypes.Money `bson:"money,omitempty" json:"money,omitempty"` RawReply *TelegramMessage `bson:"rawReply,omitempty" json:"raw_reply,omitempty"` - Status ConfirmationStatus `bson:"status,omitempty" json:"status,omitempty"` + Status ConfirmationStatus `bson:"status,omitempty" json:"status,omitempty"` ParseError string `bson:"parseError,omitempty" json:"parse_error,omitempty"` } - -type TelegramMessage struct { - ChatID string `bson:"chatId,omitempty" json:"chat_id,omitempty"` - MessageID string `bson:"messageId,omitempty" json:"message_id,omitempty"` - ReplyToMessageID string `bson:"replyToMessageId,omitempty" json:"reply_to_message_id,omitempty"` - FromUserID string `bson:"fromUserId,omitempty" json:"from_user_id,omitempty"` - FromUsername string `bson:"fromUsername,omitempty" json:"from_username,omitempty"` - Text string `bson:"text,omitempty" json:"text,omitempty"` - SentAt int64 `bson:"sentAt,omitempty" json:"sent_at,omitempty"` -} diff --git a/api/pkg/model/internal/notificationevent.go b/api/pkg/model/internal/notificationevent.go index 7ddf0040..56230793 100644 --- a/api/pkg/model/internal/notificationevent.go +++ b/api/pkg/model/internal/notificationevent.go @@ -63,7 +63,7 @@ func FromStringImp(s string) (*NotificationEventImp, error) { func StringToNotificationAction(s string) (nm.NotificationAction, error) { switch nm.NotificationAction(s) { - case nm.NACreated, nm.NAPending, nm.NAUpdated, nm.NADeleted, nm.NAAssigned, nm.NAPasswordReset: + case nm.NACreated, nm.NAPending, nm.NAUpdated, nm.NADeleted, nm.NAAssigned, nm.NAPasswordReset, nm.NAConfirmationRequest, nm.NATelegramReaction, nm.NAPaymentGatewayIntent, nm.NAPaymentGatewayExecution, nm.NADiscoveryServiceAnnounce, nm.NADiscoveryGatewayAnnounce, nm.NADiscoveryHeartbeat, nm.NADiscoveryLookupRequest, nm.NADiscoveryLookupResponse, nm.NADiscoveryRefreshUI: return nm.NotificationAction(s), nil default: return "", merrors.DataConflict("invalid Notification action: " + s) diff --git a/api/pkg/model/notification/notification.go b/api/pkg/model/notification/notification.go index a3f4da02..ba542393 100644 --- a/api/pkg/model/notification/notification.go +++ b/api/pkg/model/notification/notification.go @@ -12,8 +12,9 @@ const ( NASent NotificationAction = "sent" NAPasswordReset NotificationAction = "password_reset" - NAConfirmationRequest NotificationAction = "confirmation.request" - NAPaymentGatewayIntent NotificationAction = "intent.request" + NAConfirmationRequest NotificationAction = "confirmation.request" + NATelegramReaction NotificationAction = "telegram.reaction" + NAPaymentGatewayIntent NotificationAction = "intent.request" NAPaymentGatewayExecution NotificationAction = "execution.result" NADiscoveryServiceAnnounce NotificationAction = "service.announce" diff --git a/api/pkg/model/notificationevent.go b/api/pkg/model/notificationevent.go index 58c7d5c1..612a0ede 100644 --- a/api/pkg/model/notificationevent.go +++ b/api/pkg/model/notificationevent.go @@ -82,6 +82,7 @@ func StringToNotificationAction(s string) (nm.NotificationAction, error) { nm.NAAssigned, nm.NAPasswordReset, nm.NAConfirmationRequest, + nm.NATelegramReaction, nm.NAPaymentGatewayIntent, nm.NAPaymentGatewayExecution, nm.NADiscoveryServiceAnnounce, diff --git a/api/pkg/model/telegram.go b/api/pkg/model/telegram.go new file mode 100644 index 00000000..025ccd21 --- /dev/null +++ b/api/pkg/model/telegram.go @@ -0,0 +1,18 @@ +package model + +type TelegramMessage struct { + ChatID string `bson:"chatId,omitempty" json:"chat_id,omitempty"` + MessageID string `bson:"messageId,omitempty" json:"message_id,omitempty"` + ReplyToMessageID string `bson:"replyToMessageId,omitempty" json:"reply_to_message_id,omitempty"` + FromUserID string `bson:"fromUserId,omitempty" json:"from_user_id,omitempty"` + FromUsername string `bson:"fromUsername,omitempty" json:"from_username,omitempty"` + Text string `bson:"text,omitempty" json:"text,omitempty"` + SentAt int64 `bson:"sentAt,omitempty" json:"sent_at,omitempty"` +} + +type TelegramReactionRequest struct { + RequestID string `bson:"requestId,omitempty" json:"request_id,omitempty"` + ChatID string `bson:"chatId,omitempty" json:"chat_id,omitempty"` + MessageID string `bson:"messageId,omitempty" json:"message_id,omitempty"` + Emoji string `bson:"emoji,omitempty" json:"emoji,omitempty"` +} diff --git a/api/server/go.mod b/api/server/go.mod index 749ca031..3ed8d9ec 100644 --- a/api/server/go.mod +++ b/api/server/go.mod @@ -139,6 +139,6 @@ require ( golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.40.0 // indirect golang.org/x/text v0.33.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 // indirect google.golang.org/grpc v1.78.0 // indirect ) diff --git a/api/server/go.sum b/api/server/go.sum index f321c43f..d465d072 100644 --- a/api/server/go.sum +++ b/api/server/go.sum @@ -361,8 +361,8 @@ gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= google.golang.org/genproto/googleapis/api v0.0.0-20251029180050-ab9386a59fda h1:+2XxjfsAu6vqFxwGBRcHiMaDCuZiqXGDUDVWVtrFAnE= google.golang.org/genproto/googleapis/api v0.0.0-20251029180050-ab9386a59fda/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3 h1:C4WAdL+FbjnGlpp2S+HMVhBeCq2Lcib4xZqfPNF6OoQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 h1:vzOYHDZEHIsPYYnaSYo60AqHkJronSu0rzTz/s4quL0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= diff --git a/frontend/pweb/caddy/Caddyfile b/frontend/pweb/caddy/Caddyfile index 966c8345..9ea9bdd5 100644 --- a/frontend/pweb/caddy/Caddyfile +++ b/frontend/pweb/caddy/Caddyfile @@ -35,6 +35,11 @@ header Expires "0" } + # Telegram webhook -> notification service. + handle /telegram/webhook { + reverse_proxy sendico-notification:8081 + } + # Monetix payout callbacks -> mntx gateway (IP allow-listed) @monetixSuccess { path /gateway/m/success*