diff --git a/api/billing/documents/go.mod b/api/billing/documents/go.mod index d2b97783..202aa1de 100644 --- a/api/billing/documents/go.mod +++ b/api/billing/documents/go.mod @@ -5,10 +5,10 @@ go 1.25.7 replace github.com/tech/sendico/pkg => ../../pkg require ( - github.com/aws/aws-sdk-go-v2 v1.41.2 - github.com/aws/aws-sdk-go-v2/config v1.32.10 - github.com/aws/aws-sdk-go-v2/credentials v1.19.10 - github.com/aws/aws-sdk-go-v2/service/s3 v1.96.2 + github.com/aws/aws-sdk-go-v2 v1.41.3 + github.com/aws/aws-sdk-go-v2/config v1.32.11 + github.com/aws/aws-sdk-go-v2/credentials v1.19.11 + github.com/aws/aws-sdk-go-v2/service/s3 v1.96.3 github.com/jung-kurt/gofpdf v1.16.2 github.com/prometheus/client_golang v1.23.2 github.com/shopspring/decimal v1.4.0 @@ -20,20 +20,20 @@ require ( ) require ( - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.5 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.18 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.18 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.10 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.18 // indirect - github.com/aws/aws-sdk-go-v2/service/signin v1.0.6 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.30.11 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.41.7 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.6 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.19 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.19 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.19 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.5 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.19 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.6 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.11 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.19 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.19 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.0.7 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.30.12 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.16 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.41.8 // indirect github.com/aws/smithy-go v1.24.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect diff --git a/api/billing/documents/go.sum b/api/billing/documents/go.sum index 0c29597b..0499d07f 100644 --- a/api/billing/documents/go.sum +++ b/api/billing/documents/go.sum @@ -4,42 +4,42 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25 github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/aws/aws-sdk-go-v2 v1.41.2 h1:LuT2rzqNQsauaGkPK/7813XxcZ3o3yePY0Iy891T2ls= -github.com/aws/aws-sdk-go-v2 v1.41.2/go.mod h1:IvvlAZQXvTXznUPfRVfryiG1fbzE2NGK6m9u39YQ+S4= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.5 h1:zWFmPmgw4sveAYi1mRqG+E/g0461cJ5M4bJ8/nc6d3Q= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.5/go.mod h1:nVUlMLVV8ycXSb7mSkcNu9e3v/1TJq2RTlrPwhYWr5c= -github.com/aws/aws-sdk-go-v2/config v1.32.10 h1:9DMthfO6XWZYLfzZglAgW5Fyou2nRI5CuV44sTedKBI= -github.com/aws/aws-sdk-go-v2/config v1.32.10/go.mod h1:2rUIOnA2JaiqYmSKYmRJlcMWy6qTj1vuRFscppSBMcw= -github.com/aws/aws-sdk-go-v2/credentials v1.19.10 h1:EEhmEUFCE1Yhl7vDhNOI5OCL/iKMdkkYFTRpZXNw7m8= -github.com/aws/aws-sdk-go-v2/credentials v1.19.10/go.mod h1:RnnlFCAlxQCkN2Q379B67USkBMu1PipEEiibzYN5UTE= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18 h1:Ii4s+Sq3yDfaMLpjrJsqD6SmG/Wq/P5L/hw2qa78UAY= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18/go.mod h1:6x81qnY++ovptLE6nWQeWrpXxbnlIex+4H4eYYGcqfc= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.18 h1:F43zk1vemYIqPAwhjTjYIz0irU2EY7sOb/F5eJ3HuyM= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.18/go.mod h1:w1jdlZXrGKaJcNoL+Nnrj+k5wlpGXqnNrKoP22HvAug= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18 h1:xCeWVjj0ki0l3nruoyP2slHsGArMxeiiaoPN5QZH6YQ= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18/go.mod h1:r/eLGuGCBw6l36ZRWiw6PaZwPXb6YOj+i/7MizNl5/k= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.18 h1:eZioDaZGJ0tMM4gzmkNIO2aAoQd+je7Ug7TkvAzlmkU= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.18/go.mod h1:CCXwUKAJdoWr6/NcxZ+zsiPr6oH/Q5aTooRGYieAyj4= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5 h1:CeY9LUdur+Dxoeldqoun6y4WtJ3RQtzk0JMP2gfUay0= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5/go.mod h1:AZLZf2fMaahW5s/wMRciu1sYbdsikT/UHwbUjOdEVTc= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.10 h1:fJvQ5mIBVfKtiyx0AHY6HeWcRX5LGANLpq8SVR+Uazs= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.10/go.mod h1:Kzm5e6OmNH8VMkgK9t+ry5jEih4Y8whqs+1hrkxim1I= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18 h1:LTRCYFlnnKFlKsyIQxKhJuDuA3ZkrDQMRYm6rXiHlLY= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18/go.mod h1:XhwkgGG6bHSd00nO/mexWTcTjgd6PjuvWQMqSn2UaEk= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.18 h1:/A/xDuZAVD2BpsS2fftFRo/NoEKQJ8YTnJDEHBy2Gtg= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.18/go.mod h1:hWe9b4f+djUQGmyiGEeOnZv69dtMSgpDRIvNMvuvzvY= -github.com/aws/aws-sdk-go-v2/service/s3 v1.96.2 h1:M1A9AjcFwlxTLuf0Faj88L8Iqw0n/AJHjpZTQzMMsSc= -github.com/aws/aws-sdk-go-v2/service/s3 v1.96.2/go.mod h1:KsdTV6Q9WKUZm2mNJnUFmIoXfZux91M3sr/a4REX8e0= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.6 h1:MzORe+J94I+hYu2a6XmV5yC9huoTv8NRcCrUNedDypQ= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.6/go.mod h1:hXzcHLARD7GeWnifd8j9RWqtfIgxj4/cAtIVIK7hg8g= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.11 h1:7oGD8KPfBOJGXiCoRKrrrQkbvCp8N++u36hrLMPey6o= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.11/go.mod h1:0DO9B5EUJQlIDif+XJRWCljZRKsAFKh3gpFz7UnDtOo= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15 h1:edCcNp9eGIUDUCrzoCu1jWAXLGFIizeqkdkKgRlJwWc= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15/go.mod h1:lyRQKED9xWfgkYC/wmmYfv7iVIM68Z5OQ88ZdcV1QbU= -github.com/aws/aws-sdk-go-v2/service/sts v1.41.7 h1:NITQpgo9A5NrDZ57uOWj+abvXSb83BbyggcUBVksN7c= -github.com/aws/aws-sdk-go-v2/service/sts v1.41.7/go.mod h1:sks5UWBhEuWYDPdwlnRFn1w7xWdH29Jcpe+/PJQefEs= +github.com/aws/aws-sdk-go-v2 v1.41.3 h1:4kQ/fa22KjDt13QCy1+bYADvdgcxpfH18f0zP542kZA= +github.com/aws/aws-sdk-go-v2 v1.41.3/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.6 h1:N4lRUXZpZ1KVEUn6hxtco/1d2lgYhNn1fHkkl8WhlyQ= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.6/go.mod h1:lyw7GFp3qENLh7kwzf7iMzAxDn+NzjXEAGjKS2UOKqI= +github.com/aws/aws-sdk-go-v2/config v1.32.11 h1:ftxI5sgz8jZkckuUHXfC/wMUc8u3fG1vQS0plr2F2Zs= +github.com/aws/aws-sdk-go-v2/config v1.32.11/go.mod h1:twF11+6ps9aNRKEDimksp923o44w/Thk9+8YIlzWMmo= +github.com/aws/aws-sdk-go-v2/credentials v1.19.11 h1:NdV8cwCcAXrCWyxArt58BrvZJ9pZ9Fhf9w6Uh5W3Uyc= +github.com/aws/aws-sdk-go-v2/credentials v1.19.11/go.mod h1:30yY2zqkMPdrvxBqzI9xQCM+WrlrZKSOpSJEsylVU+8= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.19 h1:INUvJxmhdEbVulJYHI061k4TVuS3jzzthNvjqvVvTKM= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.19/go.mod h1:FpZN2QISLdEBWkayloda+sZjVJL+e9Gl0k1SyTgcswU= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.19 h1:/sECfyq2JTifMI2JPyZ4bdRN77zJmr6SrS1eL3augIA= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.19/go.mod h1:dMf8A5oAqr9/oxOfLkC/c2LU/uMcALP0Rgn2BD5LWn0= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.19 h1:AWeJMk33GTBf6J20XJe6qZoRSJo0WfUhsMdUKhoODXE= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.19/go.mod h1:+GWrYoaAsV7/4pNHpwh1kiNLXkKaSoppxQq9lbH8Ejw= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.5 h1:clHU5fm//kWS1C2HgtgWxfQbFbx4b6rx+5jzhgX9HrI= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.5/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.19 h1:3Y4oma5TiV7tT9wa8zRcdoXwZkGz9Q/wxbEUK7cMuAM= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.19/go.mod h1:V1K+TeJVD5JOk3D9e5tsX2KUdL7BlB+FV6cBhdobN8c= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.6 h1:XAq62tBTJP/85lFD5oqOOe7YYgWxY9LvWq8plyDvDVg= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.6/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.11 h1:BYf7XNsJMzl4mObARUBUib+j2tf0U//JAAtTnYqvqCw= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.11/go.mod h1:aEUS4WrNk/+FxkBZZa7tVgp4pGH+kFGW40Y8rCPqt5g= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.19 h1:X1Tow7suZk9UCJHE1Iw9GMZJJl0dAnKXXP1NaSDHwmw= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.19/go.mod h1:/rARO8psX+4sfjUQXp5LLifjUt8DuATZ31WptNJTyQA= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.19 h1:JnQeStZvPHFHeyky/7LbMlyQjUa+jIBj36OlWm0pzIk= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.19/go.mod h1:HGyasyHvYdFQeJhvDHfH7HXkHh57htcJGKDZ+7z+I24= +github.com/aws/aws-sdk-go-v2/service/s3 v1.96.3 h1:+d0SsTvxtIJt4tSJ6wr+jrxEMDa6XeupjRv8H7Qitkk= +github.com/aws/aws-sdk-go-v2/service/s3 v1.96.3/go.mod h1:ROUNFvFWPwBlOu687WJNQ9cPvd2ccpFrnCiA1YGz50o= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.7 h1:Y2cAXlClHsXkkOvWZFXATr34b0hxxloeQu/pAZz2row= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.7/go.mod h1:idzZ7gmDeqeNrSPkdbtMp9qWMgcBwykA7P7Rzh5DXVU= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.12 h1:iSsvB9EtQ09YrsmIc44Heqlx5ByGErqhPK1ZQLppias= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.12/go.mod h1:fEWYKTRGoZNl8tZ77i61/ccwOMJdGxwOhWCkp6TXAr0= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.16 h1:EnUdUqRP1CNzt2DkV67tJx6XDN4xlfBFm+bzeNOQVb0= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.16/go.mod h1:Jic/xv0Rq/pFNCh3WwpH4BEqdbSAl+IyHro8LbibHD8= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.8 h1:XQTQTF75vnug2TXS8m7CVJfC2nniYPZnO1D4Np761Oo= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.8/go.mod h1:Xgx+PR1NUOjNmQY+tRMnouRp83JRM8pRMw/vCaVhPkI= github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng= github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= diff --git a/api/edge/bff/go.mod b/api/edge/bff/go.mod index aa0b2d04..ff2f3d16 100644 --- a/api/edge/bff/go.mod +++ b/api/edge/bff/go.mod @@ -15,10 +15,10 @@ replace github.com/tech/sendico/payments/storage => ../../payments/storage replace github.com/tech/sendico/gateway/tron => ../../gateway/tron require ( - github.com/aws/aws-sdk-go-v2 v1.41.2 - github.com/aws/aws-sdk-go-v2/config v1.32.10 - github.com/aws/aws-sdk-go-v2/credentials v1.19.10 - github.com/aws/aws-sdk-go-v2/service/s3 v1.96.2 + github.com/aws/aws-sdk-go-v2 v1.41.3 + github.com/aws/aws-sdk-go-v2/config v1.32.11 + github.com/aws/aws-sdk-go-v2/credentials v1.19.11 + github.com/aws/aws-sdk-go-v2/service/s3 v1.96.3 github.com/go-chi/chi/v5 v5.2.5 github.com/go-chi/cors v1.2.2 github.com/go-chi/jwtauth/v5 v5.4.0 @@ -54,20 +54,20 @@ require ( dario.cat/mergo v1.0.1 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.5 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.18 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.18 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.10 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.18 // indirect - github.com/aws/aws-sdk-go-v2/service/signin v1.0.6 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.30.11 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.41.7 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.6 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.19 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.19 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.19 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.5 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.19 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.6 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.11 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.19 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.19 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.0.7 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.30.12 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.16 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.41.8 // indirect github.com/aws/smithy-go v1.24.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/casbin/mongodb-adapter/v4 v4.3.0 // indirect diff --git a/api/edge/bff/go.sum b/api/edge/bff/go.sum index 9b96025e..7891bbc4 100644 --- a/api/edge/bff/go.sum +++ b/api/edge/bff/go.sum @@ -6,42 +6,42 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25 github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/aws/aws-sdk-go-v2 v1.41.2 h1:LuT2rzqNQsauaGkPK/7813XxcZ3o3yePY0Iy891T2ls= -github.com/aws/aws-sdk-go-v2 v1.41.2/go.mod h1:IvvlAZQXvTXznUPfRVfryiG1fbzE2NGK6m9u39YQ+S4= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.5 h1:zWFmPmgw4sveAYi1mRqG+E/g0461cJ5M4bJ8/nc6d3Q= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.5/go.mod h1:nVUlMLVV8ycXSb7mSkcNu9e3v/1TJq2RTlrPwhYWr5c= -github.com/aws/aws-sdk-go-v2/config v1.32.10 h1:9DMthfO6XWZYLfzZglAgW5Fyou2nRI5CuV44sTedKBI= -github.com/aws/aws-sdk-go-v2/config v1.32.10/go.mod h1:2rUIOnA2JaiqYmSKYmRJlcMWy6qTj1vuRFscppSBMcw= -github.com/aws/aws-sdk-go-v2/credentials v1.19.10 h1:EEhmEUFCE1Yhl7vDhNOI5OCL/iKMdkkYFTRpZXNw7m8= -github.com/aws/aws-sdk-go-v2/credentials v1.19.10/go.mod h1:RnnlFCAlxQCkN2Q379B67USkBMu1PipEEiibzYN5UTE= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18 h1:Ii4s+Sq3yDfaMLpjrJsqD6SmG/Wq/P5L/hw2qa78UAY= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18/go.mod h1:6x81qnY++ovptLE6nWQeWrpXxbnlIex+4H4eYYGcqfc= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.18 h1:F43zk1vemYIqPAwhjTjYIz0irU2EY7sOb/F5eJ3HuyM= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.18/go.mod h1:w1jdlZXrGKaJcNoL+Nnrj+k5wlpGXqnNrKoP22HvAug= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18 h1:xCeWVjj0ki0l3nruoyP2slHsGArMxeiiaoPN5QZH6YQ= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18/go.mod h1:r/eLGuGCBw6l36ZRWiw6PaZwPXb6YOj+i/7MizNl5/k= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.18 h1:eZioDaZGJ0tMM4gzmkNIO2aAoQd+je7Ug7TkvAzlmkU= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.18/go.mod h1:CCXwUKAJdoWr6/NcxZ+zsiPr6oH/Q5aTooRGYieAyj4= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5 h1:CeY9LUdur+Dxoeldqoun6y4WtJ3RQtzk0JMP2gfUay0= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5/go.mod h1:AZLZf2fMaahW5s/wMRciu1sYbdsikT/UHwbUjOdEVTc= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.10 h1:fJvQ5mIBVfKtiyx0AHY6HeWcRX5LGANLpq8SVR+Uazs= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.10/go.mod h1:Kzm5e6OmNH8VMkgK9t+ry5jEih4Y8whqs+1hrkxim1I= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18 h1:LTRCYFlnnKFlKsyIQxKhJuDuA3ZkrDQMRYm6rXiHlLY= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18/go.mod h1:XhwkgGG6bHSd00nO/mexWTcTjgd6PjuvWQMqSn2UaEk= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.18 h1:/A/xDuZAVD2BpsS2fftFRo/NoEKQJ8YTnJDEHBy2Gtg= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.18/go.mod h1:hWe9b4f+djUQGmyiGEeOnZv69dtMSgpDRIvNMvuvzvY= -github.com/aws/aws-sdk-go-v2/service/s3 v1.96.2 h1:M1A9AjcFwlxTLuf0Faj88L8Iqw0n/AJHjpZTQzMMsSc= -github.com/aws/aws-sdk-go-v2/service/s3 v1.96.2/go.mod h1:KsdTV6Q9WKUZm2mNJnUFmIoXfZux91M3sr/a4REX8e0= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.6 h1:MzORe+J94I+hYu2a6XmV5yC9huoTv8NRcCrUNedDypQ= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.6/go.mod h1:hXzcHLARD7GeWnifd8j9RWqtfIgxj4/cAtIVIK7hg8g= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.11 h1:7oGD8KPfBOJGXiCoRKrrrQkbvCp8N++u36hrLMPey6o= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.11/go.mod h1:0DO9B5EUJQlIDif+XJRWCljZRKsAFKh3gpFz7UnDtOo= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15 h1:edCcNp9eGIUDUCrzoCu1jWAXLGFIizeqkdkKgRlJwWc= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15/go.mod h1:lyRQKED9xWfgkYC/wmmYfv7iVIM68Z5OQ88ZdcV1QbU= -github.com/aws/aws-sdk-go-v2/service/sts v1.41.7 h1:NITQpgo9A5NrDZ57uOWj+abvXSb83BbyggcUBVksN7c= -github.com/aws/aws-sdk-go-v2/service/sts v1.41.7/go.mod h1:sks5UWBhEuWYDPdwlnRFn1w7xWdH29Jcpe+/PJQefEs= +github.com/aws/aws-sdk-go-v2 v1.41.3 h1:4kQ/fa22KjDt13QCy1+bYADvdgcxpfH18f0zP542kZA= +github.com/aws/aws-sdk-go-v2 v1.41.3/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.6 h1:N4lRUXZpZ1KVEUn6hxtco/1d2lgYhNn1fHkkl8WhlyQ= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.6/go.mod h1:lyw7GFp3qENLh7kwzf7iMzAxDn+NzjXEAGjKS2UOKqI= +github.com/aws/aws-sdk-go-v2/config v1.32.11 h1:ftxI5sgz8jZkckuUHXfC/wMUc8u3fG1vQS0plr2F2Zs= +github.com/aws/aws-sdk-go-v2/config v1.32.11/go.mod h1:twF11+6ps9aNRKEDimksp923o44w/Thk9+8YIlzWMmo= +github.com/aws/aws-sdk-go-v2/credentials v1.19.11 h1:NdV8cwCcAXrCWyxArt58BrvZJ9pZ9Fhf9w6Uh5W3Uyc= +github.com/aws/aws-sdk-go-v2/credentials v1.19.11/go.mod h1:30yY2zqkMPdrvxBqzI9xQCM+WrlrZKSOpSJEsylVU+8= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.19 h1:INUvJxmhdEbVulJYHI061k4TVuS3jzzthNvjqvVvTKM= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.19/go.mod h1:FpZN2QISLdEBWkayloda+sZjVJL+e9Gl0k1SyTgcswU= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.19 h1:/sECfyq2JTifMI2JPyZ4bdRN77zJmr6SrS1eL3augIA= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.19/go.mod h1:dMf8A5oAqr9/oxOfLkC/c2LU/uMcALP0Rgn2BD5LWn0= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.19 h1:AWeJMk33GTBf6J20XJe6qZoRSJo0WfUhsMdUKhoODXE= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.19/go.mod h1:+GWrYoaAsV7/4pNHpwh1kiNLXkKaSoppxQq9lbH8Ejw= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.5 h1:clHU5fm//kWS1C2HgtgWxfQbFbx4b6rx+5jzhgX9HrI= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.5/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.19 h1:3Y4oma5TiV7tT9wa8zRcdoXwZkGz9Q/wxbEUK7cMuAM= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.19/go.mod h1:V1K+TeJVD5JOk3D9e5tsX2KUdL7BlB+FV6cBhdobN8c= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.6 h1:XAq62tBTJP/85lFD5oqOOe7YYgWxY9LvWq8plyDvDVg= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.6/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.11 h1:BYf7XNsJMzl4mObARUBUib+j2tf0U//JAAtTnYqvqCw= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.11/go.mod h1:aEUS4WrNk/+FxkBZZa7tVgp4pGH+kFGW40Y8rCPqt5g= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.19 h1:X1Tow7suZk9UCJHE1Iw9GMZJJl0dAnKXXP1NaSDHwmw= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.19/go.mod h1:/rARO8psX+4sfjUQXp5LLifjUt8DuATZ31WptNJTyQA= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.19 h1:JnQeStZvPHFHeyky/7LbMlyQjUa+jIBj36OlWm0pzIk= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.19/go.mod h1:HGyasyHvYdFQeJhvDHfH7HXkHh57htcJGKDZ+7z+I24= +github.com/aws/aws-sdk-go-v2/service/s3 v1.96.3 h1:+d0SsTvxtIJt4tSJ6wr+jrxEMDa6XeupjRv8H7Qitkk= +github.com/aws/aws-sdk-go-v2/service/s3 v1.96.3/go.mod h1:ROUNFvFWPwBlOu687WJNQ9cPvd2ccpFrnCiA1YGz50o= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.7 h1:Y2cAXlClHsXkkOvWZFXATr34b0hxxloeQu/pAZz2row= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.7/go.mod h1:idzZ7gmDeqeNrSPkdbtMp9qWMgcBwykA7P7Rzh5DXVU= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.12 h1:iSsvB9EtQ09YrsmIc44Heqlx5ByGErqhPK1ZQLppias= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.12/go.mod h1:fEWYKTRGoZNl8tZ77i61/ccwOMJdGxwOhWCkp6TXAr0= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.16 h1:EnUdUqRP1CNzt2DkV67tJx6XDN4xlfBFm+bzeNOQVb0= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.16/go.mod h1:Jic/xv0Rq/pFNCh3WwpH4BEqdbSAl+IyHro8LbibHD8= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.8 h1:XQTQTF75vnug2TXS8m7CVJfC2nniYPZnO1D4Np761Oo= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.8/go.mod h1:Xgx+PR1NUOjNmQY+tRMnouRp83JRM8pRMw/vCaVhPkI= github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng= github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= diff --git a/api/edge/bff/internal/server/paymentapiimp/mapper.go b/api/edge/bff/internal/server/paymentapiimp/mapper.go index a2e76f37..2a90fbac 100644 --- a/api/edge/bff/internal/server/paymentapiimp/mapper.go +++ b/api/edge/bff/internal/server/paymentapiimp/mapper.go @@ -14,6 +14,7 @@ import ( chainv1 "github.com/tech/sendico/pkg/proto/gateway/chain/v1" endpointv1 "github.com/tech/sendico/pkg/proto/payments/endpoint/v1" quotationv2 "github.com/tech/sendico/pkg/proto/payments/quotation/v2" + sharedv1 "github.com/tech/sendico/pkg/proto/payments/shared/v1" "github.com/tech/sendico/server/interface/api/srequest" "go.mongodb.org/mongo-driver/v2/bson" ) @@ -59,7 +60,7 @@ func mapQuoteIntent(intent *srequest.PaymentIntent) (*quotationv2.QuoteIntent, e SettlementMode: resolvedSettlementMode, FeeTreatment: resolvedFeeTreatment, SettlementCurrency: settlementCurrency, - FxSide: mapFXSide(intent), + Fx: mapFXIntent(intent), } if comment := strings.TrimSpace(intent.Attributes["comment"]); comment != "" { quoteIntent.Comment = comment @@ -67,17 +68,30 @@ func mapQuoteIntent(intent *srequest.PaymentIntent) (*quotationv2.QuoteIntent, e return quoteIntent, nil } -func mapFXSide(intent *srequest.PaymentIntent) fxv1.Side { - if intent == nil || intent.FX == nil { - return fxv1.Side_SIDE_UNSPECIFIED +func mapFXIntent(intent *srequest.PaymentIntent) *sharedv1.FXIntent { + if intent == nil || intent.FX == nil || intent.FX.Pair == nil { + return nil } + side := fxv1.Side_SIDE_UNSPECIFIED switch strings.TrimSpace(string(intent.FX.Side)) { case string(srequest.FXSideBuyBaseSellQuote): - return fxv1.Side_BUY_BASE_SELL_QUOTE + side = fxv1.Side_BUY_BASE_SELL_QUOTE case string(srequest.FXSideSellBaseBuyQuote): - return fxv1.Side_SELL_BASE_BUY_QUOTE - default: - return fxv1.Side_SIDE_UNSPECIFIED + side = fxv1.Side_SELL_BASE_BUY_QUOTE + } + if side == fxv1.Side_SIDE_UNSPECIFIED { + side = fxv1.Side_SELL_BASE_BUY_QUOTE + } + return &sharedv1.FXIntent{ + Pair: &fxv1.CurrencyPair{ + Base: strings.ToUpper(strings.TrimSpace(intent.FX.Pair.Base)), + Quote: strings.ToUpper(strings.TrimSpace(intent.FX.Pair.Quote)), + }, + Side: side, + Firm: intent.FX.Firm, + TtlMs: intent.FX.TTLms, + PreferredProvider: strings.TrimSpace(intent.FX.PreferredProvider), + MaxAgeMs: intent.FX.MaxAgeMs, } } diff --git a/api/edge/bff/internal/server/paymentapiimp/mapper_fee_treatment_test.go b/api/edge/bff/internal/server/paymentapiimp/mapper_fee_treatment_test.go index b1d65c8f..90a3e890 100644 --- a/api/edge/bff/internal/server/paymentapiimp/mapper_fee_treatment_test.go +++ b/api/edge/bff/internal/server/paymentapiimp/mapper_fee_treatment_test.go @@ -202,8 +202,14 @@ func TestMapQuoteIntent_DerivesSettlementCurrencyFromFX(t *testing.T) { if got.GetSettlementCurrency() != "RUB" { t.Fatalf("unexpected settlement currency: got=%q", got.GetSettlementCurrency()) } - if got.GetFxSide() != fxv1.Side_SELL_BASE_BUY_QUOTE { - t.Fatalf("unexpected fx_side: got=%s", got.GetFxSide().String()) + if got.GetFx() == nil || got.GetFx().GetPair() == nil { + t.Fatalf("expected fx intent") + } + if got.GetFx().GetSide() != fxv1.Side_SELL_BASE_BUY_QUOTE { + t.Fatalf("unexpected fx side: got=%s", got.GetFx().GetSide().String()) + } + if got.GetFx().GetPair().GetBase() != "USDT" || got.GetFx().GetPair().GetQuote() != "RUB" { + t.Fatalf("unexpected fx pair: got=%s/%s", got.GetFx().GetPair().GetBase(), got.GetFx().GetPair().GetQuote()) } } @@ -246,8 +252,11 @@ func TestMapQuoteIntent_PropagatesFXSideBuyBaseSellQuote(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } - if got.GetFxSide() != fxv1.Side_BUY_BASE_SELL_QUOTE { - t.Fatalf("unexpected fx_side: got=%s", got.GetFxSide().String()) + if got.GetFx() == nil || got.GetFx().GetPair() == nil { + t.Fatalf("expected fx intent") + } + if got.GetFx().GetSide() != fxv1.Side_BUY_BASE_SELL_QUOTE { + t.Fatalf("unexpected fx side: got=%s", got.GetFx().GetSide().String()) } if got.GetSettlementCurrency() != "RUB" { t.Fatalf("unexpected settlement currency: got=%q", got.GetSettlementCurrency()) diff --git a/api/gateway/chain/go.mod b/api/gateway/chain/go.mod index dfabf36f..69e185ec 100644 --- a/api/gateway/chain/go.mod +++ b/api/gateway/chain/go.mod @@ -7,7 +7,7 @@ replace github.com/tech/sendico/pkg => ../../pkg replace github.com/tech/sendico/gateway/common => ../common require ( - github.com/ethereum/go-ethereum v1.17.0 + github.com/ethereum/go-ethereum v1.17.1 github.com/mitchellh/mapstructure v1.5.0 github.com/prometheus/client_golang v1.23.2 github.com/shopspring/decimal v1.4.0 diff --git a/api/gateway/chain/go.sum b/api/gateway/chain/go.sum index e72e1738..f91d85cf 100644 --- a/api/gateway/chain/go.sum +++ b/api/gateway/chain/go.sum @@ -76,8 +76,8 @@ github.com/ethereum/c-kzg-4844/v2 v2.1.6 h1:xQymkKCT5E2Jiaoqf3v4wsNgjZLY0lRSkZn2 github.com/ethereum/c-kzg-4844/v2 v2.1.6/go.mod h1:8HMkUZ5JRv4hpw/XUrYWSQNAUzhHMg2UDb/U+5m+XNw= github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab h1:rvv6MJhy07IMfEKuARQ9TKojGqLVNxQajaXEp/BoqSk= github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab/go.mod h1:IuLm4IsPipXKF7CW5Lzf68PIbZ5yl7FFd74l/E0o9A8= -github.com/ethereum/go-ethereum v1.17.0 h1:2D+1Fe23CwZ5tQoAS5DfwKFNI1HGcTwi65/kRlAVxes= -github.com/ethereum/go-ethereum v1.17.0/go.mod h1:2W3msvdosS/MCWytpqTcqgFiRYbTH59FxDJzqah120o= +github.com/ethereum/go-ethereum v1.17.1 h1:IjlQDjgxg2uL+GzPRkygGULPMLzcYWncEI7wbaizvho= +github.com/ethereum/go-ethereum v1.17.1/go.mod h1:7UWOVHL7K3b8RfVRea022btnzLCaanwHtBuH1jUCH/I= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= diff --git a/api/gateway/mntx/storage/mongo/store/payouts.go b/api/gateway/mntx/storage/mongo/store/payouts.go index 51c249f0..156ed0a1 100644 --- a/api/gateway/mntx/storage/mongo/store/payouts.go +++ b/api/gateway/mntx/storage/mongo/store/payouts.go @@ -43,8 +43,7 @@ func NewPayouts(logger mlogger.Logger, db *mongo.Database) (*Payouts, error) { Sparse: true, }); err != nil { logger.Error("Failed to create payouts operation index", - zap.Error(err), - zap.String("index_field", payoutOpField)) + zap.Error(err), zap.String("index_field", payoutOpField)) return nil, err } if err := repo.CreateIndex(&ri.Definition{ @@ -52,8 +51,7 @@ func NewPayouts(logger mlogger.Logger, db *mongo.Database) (*Payouts, error) { Unique: true, }); err != nil { logger.Error("Failed to create payouts idempotency index", - zap.Error(err), - zap.String("index_field", payoutIdemField)) + zap.Error(err), zap.String("index_field", payoutIdemField)) return nil, err } diff --git a/api/gateway/tron/go.sum b/api/gateway/tron/go.sum index 70e8e103..286e6285 100644 --- a/api/gateway/tron/go.sum +++ b/api/gateway/tron/go.sum @@ -80,8 +80,6 @@ github.com/ethereum/c-kzg-4844/v2 v2.1.6 h1:xQymkKCT5E2Jiaoqf3v4wsNgjZLY0lRSkZn2 github.com/ethereum/c-kzg-4844/v2 v2.1.6/go.mod h1:8HMkUZ5JRv4hpw/XUrYWSQNAUzhHMg2UDb/U+5m+XNw= github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab h1:rvv6MJhy07IMfEKuARQ9TKojGqLVNxQajaXEp/BoqSk= github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab/go.mod h1:IuLm4IsPipXKF7CW5Lzf68PIbZ5yl7FFd74l/E0o9A8= -github.com/ethereum/go-ethereum v1.17.0 h1:2D+1Fe23CwZ5tQoAS5DfwKFNI1HGcTwi65/kRlAVxes= -github.com/ethereum/go-ethereum v1.17.0/go.mod h1:2W3msvdosS/MCWytpqTcqgFiRYbTH59FxDJzqah120o= github.com/ethereum/go-ethereum v1.17.1 h1:IjlQDjgxg2uL+GzPRkygGULPMLzcYWncEI7wbaizvho= github.com/ethereum/go-ethereum v1.17.1/go.mod h1:7UWOVHL7K3b8RfVRea022btnzLCaanwHtBuH1jUCH/I= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= diff --git a/api/payments/quotation/internal/server/internal/dependencies.go b/api/payments/quotation/internal/server/internal/dependencies.go index 200fa805..72008d31 100644 --- a/api/payments/quotation/internal/server/internal/dependencies.go +++ b/api/payments/quotation/internal/server/internal/dependencies.go @@ -51,6 +51,12 @@ func (i *Imp) initDependencies(cfg *config) *clientDependencies { i.discoveryClients = newDiscoveryClientResolver(i.logger, i.discoveryReg) deps.gatewayResolver = discoveryChainGatewayResolver{resolver: i.discoveryClients} deps.gatewayInvokeResolver = discoveryGatewayInvokeResolver{resolver: i.discoveryClients} + ledgerClient, ledgerErr := i.discoveryClients.LedgerClient(context.Background()) + if ledgerErr != nil { + i.logger.Warn("Failed to initialise ledger client from discovery", zap.Error(ledgerErr)) + } else { + deps.ledgerClient = ledgerClient + } } else if i != nil && i.logger != nil { i.logger.Warn("Discovery registry unavailable; chain gateway clients disabled") } diff --git a/api/payments/quotation/internal/server/internal/discovery_clients.go b/api/payments/quotation/internal/server/internal/discovery_clients.go index 80d28754..57efa465 100644 --- a/api/payments/quotation/internal/server/internal/discovery_clients.go +++ b/api/payments/quotation/internal/server/internal/discovery_clients.go @@ -11,9 +11,11 @@ import ( "time" chainclient "github.com/tech/sendico/gateway/chain/client" + ledgerclient "github.com/tech/sendico/ledger/client" "github.com/tech/sendico/pkg/discovery" "github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/mlogger" + "github.com/tech/sendico/pkg/mservice" "go.uber.org/zap" ) @@ -35,7 +37,9 @@ type discoveryClientResolver struct { mu sync.Mutex - chainClients map[string]chainclient.Client + chainClients map[string]chainclient.Client + ledgerClient ledgerclient.Client + ledgerEndpoint discoveryEndpoint lastSelection map[string]string lastMissing map[string]time.Time @@ -66,6 +70,10 @@ func (r *discoveryClientResolver) Close() { } delete(r.chainClients, key) } + if r.ledgerClient != nil { + _ = r.ledgerClient.Close() + r.ledgerClient = nil + } } type discoveryGatewayInvokeResolver struct { @@ -130,6 +138,43 @@ func (r *discoveryClientResolver) ChainClientByNetwork(ctx context.Context, netw return r.ChainClientByInvokeURI(ctx, entry.InvokeURI) } +func (r *discoveryClientResolver) LedgerClient(ctx context.Context) (ledgerclient.Client, error) { + entry, ok := r.findLedgerEntry() + if !ok { + return nil, merrors.NoData("discovery: ledger service unavailable") + } + endpoint, err := parseDiscoveryEndpoint(entry.InvokeURI) + if err != nil { + r.logMissing("ledger", "invalid ledger invoke uri", entry.InvokeURI, err) + return nil, err + } + if ctx == nil { + ctx = context.Background() + } + + r.mu.Lock() + defer r.mu.Unlock() + + if r.ledgerClient == nil || r.ledgerEndpoint.key() != endpoint.key() || r.ledgerEndpoint.address != endpoint.address { + if r.ledgerClient != nil { + _ = r.ledgerClient.Close() + r.ledgerClient = nil + } + client, dialErr := ledgerclient.New(ctx, ledgerclient.Config{ + Address: endpoint.address, + Insecure: endpoint.insecure, + }) + if dialErr != nil { + r.logMissing("ledger", "failed to dial ledger service", endpoint.raw, dialErr) + return nil, dialErr + } + r.ledgerClient = client + r.ledgerEndpoint = endpoint + } + + return r.ledgerClient, nil +} + func (r *discoveryClientResolver) findChainEntry(network string) (*discovery.RegistryEntry, bool) { if r == nil || r.registry == nil { r.logMissing("chain", "discovery registry unavailable", "", nil) @@ -172,6 +217,44 @@ func (r *discoveryClientResolver) findChainEntry(network string) (*discovery.Reg return &entry, true } +func (r *discoveryClientResolver) findLedgerEntry() (*discovery.RegistryEntry, bool) { + if r == nil || r.registry == nil { + r.logMissing("ledger", "discovery registry unavailable", "", nil) + return nil, false + } + + entries := r.registry.List(time.Now(), true) + matches := make([]discovery.RegistryEntry, 0) + for _, entry := range entries { + if !strings.EqualFold(strings.TrimSpace(entry.Service), string(mservice.Ledger)) { + continue + } + if strings.TrimSpace(entry.InvokeURI) == "" { + continue + } + matches = append(matches, entry) + } + if len(matches) == 0 { + r.logMissing("ledger", "discovery ledger entry missing", "", nil) + return nil, false + } + + sort.Slice(matches, func(i, j int) bool { + if matches[i].RoutingPriority != matches[j].RoutingPriority { + return matches[i].RoutingPriority > matches[j].RoutingPriority + } + if matches[i].ID != matches[j].ID { + return matches[i].ID < matches[j].ID + } + return matches[i].InstanceID < matches[j].InstanceID + }) + + entry := matches[0] + entryKey := discoveryEntryKey(entry) + r.logSelection("ledger", entryKey, entry) + return &entry, true +} + func (r *discoveryClientResolver) logSelection(key, entryKey string, entry discovery.RegistryEntry) { if r == nil { return diff --git a/api/payments/quotation/internal/server/internal/serverimp.go b/api/payments/quotation/internal/server/internal/serverimp.go index 86c0276c..83ede7a8 100644 --- a/api/payments/quotation/internal/server/internal/serverimp.go +++ b/api/payments/quotation/internal/server/internal/serverimp.go @@ -51,6 +51,9 @@ func (i *Imp) Start() error { if i.deps.oracleClient != nil { opts = append(opts, quotesvc.WithOracleClient(i.deps.oracleClient)) } + if i.deps.ledgerClient != nil { + opts = append(opts, quotesvc.WithLedgerClient(i.deps.ledgerClient)) + } if i.deps.gatewayResolver != nil { opts = append(opts, quotesvc.WithChainGatewayResolver(i.deps.gatewayResolver)) } diff --git a/api/payments/quotation/internal/server/internal/types.go b/api/payments/quotation/internal/server/internal/types.go index 07c6a957..fbc0882e 100644 --- a/api/payments/quotation/internal/server/internal/types.go +++ b/api/payments/quotation/internal/server/internal/types.go @@ -2,6 +2,7 @@ package serverimp import ( oracleclient "github.com/tech/sendico/fx/oracle/client" + ledgerclient "github.com/tech/sendico/ledger/client" quotesvc "github.com/tech/sendico/payments/quotation/internal/service/quotation" "github.com/tech/sendico/payments/storage" "github.com/tech/sendico/pkg/discovery" @@ -20,6 +21,7 @@ type clientDependencies struct { feesConn *grpc.ClientConn feesClient feesv1.FeeEngineClient oracleClient oracleclient.Client + ledgerClient ledgerclient.Client gatewayResolver quotesvc.ChainGatewayResolver gatewayInvokeResolver quotesvc.GatewayInvokeResolver } diff --git a/api/payments/quotation/internal/service/quotation/ledger_account_currency_resolver.go b/api/payments/quotation/internal/service/quotation/ledger_account_currency_resolver.go new file mode 100644 index 00000000..36878659 --- /dev/null +++ b/api/payments/quotation/internal/service/quotation/ledger_account_currency_resolver.go @@ -0,0 +1,90 @@ +package quotation + +import ( + "context" + "strings" + + "github.com/tech/sendico/pkg/merrors" + ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1" + "go.uber.org/zap" +) + +type ledgerAccountCurrencyResolver struct { + client ledgerClientForCurrency + logger *zap.Logger +} + +type ledgerClientForCurrency interface { + GetBalance(ctx context.Context, req *ledgerv1.GetBalanceRequest) (*ledgerv1.BalanceResponse, error) + ListAccounts(ctx context.Context, req *ledgerv1.ListAccountsRequest) (*ledgerv1.ListAccountsResponse, error) +} + +func newLedgerAccountCurrencyResolver(core *Service) *ledgerAccountCurrencyResolver { + if core == nil || core.deps.ledger == nil { + return nil + } + logger := core.logger + if logger == nil { + logger = zap.NewNop() + } + return &ledgerAccountCurrencyResolver{ + client: core.deps.ledger, + logger: logger, + } +} + +func (r *ledgerAccountCurrencyResolver) ResolveLedgerAccountCurrency( + ctx context.Context, + organizationRef string, + ledgerAccountRef string, +) (string, error) { + if r == nil || r.client == nil { + return "", merrors.NoData("ledger client unavailable") + } + + accountRef := strings.TrimSpace(ledgerAccountRef) + if accountRef == "" { + return "", merrors.InvalidArgument("ledger_account_ref is required") + } + + balanceResp, err := r.client.GetBalance(ctx, &ledgerv1.GetBalanceRequest{ + LedgerAccountRef: accountRef, + }) + if err != nil { + return "", err + } + if balance := balanceResp.GetBalance(); balance != nil { + if currency := strings.ToUpper(strings.TrimSpace(balance.GetCurrency())); currency != "" { + return currency, nil + } + } + + orgRef := strings.TrimSpace(organizationRef) + if orgRef == "" { + return "", merrors.NoData("ledger account currency is missing") + } + + listResp, err := r.client.ListAccounts(ctx, &ledgerv1.ListAccountsRequest{ + OrganizationRef: orgRef, + }) + if err != nil { + return "", err + } + for _, account := range listResp.GetAccounts() { + if strings.TrimSpace(account.GetLedgerAccountRef()) != accountRef { + continue + } + if currency := strings.ToUpper(strings.TrimSpace(account.GetCurrency())); currency != "" { + return currency, nil + } + break + } + + if r.logger != nil { + r.logger.Warn("Failed to resolve ledger account currency", + zap.String("organization_ref", orgRef), + zap.String("ledger_account_ref", accountRef), + ) + } + return "", merrors.NoData("ledger account currency is missing") +} diff --git a/api/payments/quotation/internal/service/quotation/options.go b/api/payments/quotation/internal/service/quotation/options.go index 5c94253f..54f31837 100644 --- a/api/payments/quotation/internal/service/quotation/options.go +++ b/api/payments/quotation/internal/service/quotation/options.go @@ -159,9 +159,13 @@ func WithClock(clock clockpkg.Clock) Option { } } -// WithLedgerClient is retained for backward compatibility and is currently a no-op. -func WithLedgerClient(_ ledgerclient.Client) Option { - return func(*Service) {} +// WithLedgerClient wires the ledger client used for account-currency inference. +func WithLedgerClient(client ledgerclient.Client) Option { + return func(s *Service) { + if s != nil && client != nil { + s.deps.ledger = client + } + } } // WithProviderSettlementGatewayClient is retained for backward compatibility and is currently a no-op. diff --git a/api/payments/quotation/internal/service/quotation/quotation_service_v2/logging.go b/api/payments/quotation/internal/service/quotation/quotation_service_v2/logging.go index a74b68ea..fc86ff90 100644 --- a/api/payments/quotation/internal/service/quotation/quotation_service_v2/logging.go +++ b/api/payments/quotation/internal/service/quotation/quotation_service_v2/logging.go @@ -9,6 +9,7 @@ import ( moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1" endpointv1 "github.com/tech/sendico/pkg/proto/payments/endpoint/v1" quotationv2 "github.com/tech/sendico/pkg/proto/payments/quotation/v2" + sharedv1 "github.com/tech/sendico/pkg/proto/payments/shared/v1" "go.mongodb.org/mongo-driver/v2/bson" "go.uber.org/zap" ) @@ -29,6 +30,9 @@ type quoteIntentLogSummary struct { SettlementMode string `json:"settlementMode,omitempty"` FeeTreatment string `json:"feeTreatment,omitempty"` SettlementCurrency string `json:"settlementCurrency,omitempty"` + HasFX bool `json:"hasFx"` + FXPair string `json:"fxPair,omitempty"` + FXSide string `json:"fxSide,omitempty"` HasComment bool `json:"hasComment"` } @@ -59,6 +63,7 @@ func summarizeQuoteIntent(intent *quotationv2.QuoteIntent) *quoteIntentLogSummar if intent == nil { return nil } + fxPair, fxSide, hasFX := summarizeFXIntent(intent.GetFx()) return "eIntentLogSummary{ Source: summarizeEndpoint(intent.GetSource()), Destination: summarizeEndpoint(intent.GetDestination()), @@ -66,6 +71,9 @@ func summarizeQuoteIntent(intent *quotationv2.QuoteIntent) *quoteIntentLogSummar SettlementMode: enumLogValue(intent.GetSettlementMode().String()), FeeTreatment: enumLogValue(intent.GetFeeTreatment().String()), SettlementCurrency: strings.ToUpper(strings.TrimSpace(intent.GetSettlementCurrency())), + HasFX: hasFX, + FXPair: fxPair, + FXSide: fxSide, HasComment: strings.TrimSpace(intent.GetComment()) != "", } } @@ -142,3 +150,15 @@ func moneyLogValue(m *moneyv1.Money) string { func enumLogValue(value string) string { return strings.ToLower(strings.TrimSpace(value)) } + +func summarizeFXIntent(fx *sharedv1.FXIntent) (string, string, bool) { + if fx == nil || fx.GetPair() == nil { + return "", "", false + } + base := strings.ToUpper(strings.TrimSpace(fx.GetPair().GetBase())) + quote := strings.ToUpper(strings.TrimSpace(fx.GetPair().GetQuote())) + if base == "" && quote == "" { + return "", enumLogValue(fx.GetSide().String()), false + } + return strings.TrimSpace(base + "/" + quote), enumLogValue(fx.GetSide().String()), true +} diff --git a/api/payments/quotation/internal/service/quotation/quotation_v2_wiring.go b/api/payments/quotation/internal/service/quotation/quotation_v2_wiring.go index 912fa9e5..ef0e474a 100644 --- a/api/payments/quotation/internal/service/quotation/quotation_v2_wiring.go +++ b/api/payments/quotation/internal/service/quotation/quotation_v2_wiring.go @@ -56,6 +56,9 @@ func newQuoteComputationService(core *Service) *quote_computation_service.QuoteC if resolver := newManagedWalletNetworkResolver(core); resolver != nil { opts = append(opts, quote_computation_service.WithManagedWalletNetworkResolver(resolver)) } + if resolver := newLedgerAccountCurrencyResolver(core); resolver != nil { + opts = append(opts, quote_computation_service.WithLedgerAccountCurrencyResolver(resolver)) + } if resolver := fundingProfileResolver(core); resolver != nil { opts = append(opts, quote_computation_service.WithFundingProfileResolver(resolver)) } diff --git a/api/payments/quotation/internal/service/quotation/quote_computation_service/endpoint_currency_inference.go b/api/payments/quotation/internal/service/quotation/quote_computation_service/endpoint_currency_inference.go new file mode 100644 index 00000000..b7ec2331 --- /dev/null +++ b/api/payments/quotation/internal/service/quotation/quote_computation_service/endpoint_currency_inference.go @@ -0,0 +1,87 @@ +package quote_computation_service + +import ( + "context" + "strings" + + "github.com/tech/sendico/payments/storage/model" +) + +type endpointCurrencyInference struct { + SourceCurrency string + DestinationCurrency string + SourceInferred bool + DestinationInferred bool +} + +func (s *QuoteComputationService) inferEndpointCurrencies( + ctx context.Context, + organizationRef string, + intent model.PaymentIntent, + ledgerCurrencyCache map[string]string, +) (endpointCurrencyInference, error) { + sourceCurrency, sourceInferred, err := s.inferEndpointCurrency( + ctx, + organizationRef, + intent.Source, + ledgerCurrencyCache, + ) + if err != nil { + return endpointCurrencyInference{}, err + } + destinationCurrency, destinationInferred, err := s.inferEndpointCurrency( + ctx, + organizationRef, + intent.Destination, + ledgerCurrencyCache, + ) + if err != nil { + return endpointCurrencyInference{}, err + } + return endpointCurrencyInference{ + SourceCurrency: sourceCurrency, + DestinationCurrency: destinationCurrency, + SourceInferred: sourceInferred, + DestinationInferred: destinationInferred, + }, nil +} + +func (s *QuoteComputationService) inferEndpointCurrency( + ctx context.Context, + organizationRef string, + endpoint model.PaymentEndpoint, + ledgerCurrencyCache map[string]string, +) (string, bool, error) { + if token := sourceAssetToken(endpoint); token != "" { + return token, true, nil + } + if endpoint.Ledger == nil { + return "", false, nil + } + ledgerAccountRef := strings.TrimSpace(endpoint.Ledger.LedgerAccountRef) + if ledgerAccountRef == "" { + return "", false, nil + } + if cached := normalizeAsset(ledgerCurrencyCache[ledgerAccountRef]); cached != "" { + return cached, true, nil + } + if s == nil || s.ledgerAccountCurrencyResolver == nil { + return "", false, nil + } + currency, err := s.ledgerAccountCurrencyResolver.ResolveLedgerAccountCurrency( + ctx, + strings.TrimSpace(organizationRef), + ledgerAccountRef, + ) + if err != nil { + return "", false, err + } + currency = normalizeAsset(currency) + if currency == "" { + return "", false, nil + } + if ledgerCurrencyCache != nil { + ledgerCurrencyCache[ledgerAccountRef] = currency + } + return currency, true, nil +} diff --git a/api/payments/quotation/internal/service/quotation/quote_computation_service/endpoint_currency_inference_test.go b/api/payments/quotation/internal/service/quotation/quote_computation_service/endpoint_currency_inference_test.go new file mode 100644 index 00000000..8e4f9ae3 --- /dev/null +++ b/api/payments/quotation/internal/service/quotation/quote_computation_service/endpoint_currency_inference_test.go @@ -0,0 +1,86 @@ +package quote_computation_service + +import ( + "context" + "testing" + + "github.com/tech/sendico/payments/storage/model" + paymenttypes "github.com/tech/sendico/pkg/payments/types" +) + +func TestInferEndpointCurrencies_UsesEndpointAssets(t *testing.T) { + svc := New(nil) + out, err := svc.inferEndpointCurrencies(context.Background(), "org-1", model.PaymentIntent{ + Source: model.PaymentEndpoint{ + Type: model.EndpointTypeManagedWallet, + ManagedWallet: &model.ManagedWalletEndpoint{ + ManagedWalletRef: "mw-src", + Asset: &paymenttypes.Asset{TokenSymbol: "USDT"}, + }, + }, + Destination: model.PaymentEndpoint{ + Type: model.EndpointTypeExternalChain, + ExternalChain: &model.ExternalChainEndpoint{ + Asset: &paymenttypes.Asset{TokenSymbol: "RUB"}, + }, + }, + }, map[string]string{}) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if got, want := out.SourceCurrency, "USDT"; got != want { + t.Fatalf("unexpected source currency: got=%q want=%q", got, want) + } + if got, want := out.DestinationCurrency, "RUB"; got != want { + t.Fatalf("unexpected destination currency: got=%q want=%q", got, want) + } + if !out.SourceInferred || !out.DestinationInferred { + t.Fatalf("expected both currencies inferred: %#v", out) + } +} + +func TestInferEndpointCurrencies_UsesLedgerResolver(t *testing.T) { + resolver := &fakeLedgerCurrencyResolver{ + currencies: map[string]string{ + "ledger-src": "USDT", + "ledger-dst": "RUB", + }, + } + svc := New(nil, WithLedgerAccountCurrencyResolver(resolver)) + out, err := svc.inferEndpointCurrencies(context.Background(), "org-1", model.PaymentIntent{ + Source: model.PaymentEndpoint{ + Type: model.EndpointTypeLedger, + Ledger: &model.LedgerEndpoint{ + LedgerAccountRef: "ledger-src", + }, + }, + Destination: model.PaymentEndpoint{ + Type: model.EndpointTypeLedger, + Ledger: &model.LedgerEndpoint{ + LedgerAccountRef: "ledger-dst", + }, + }, + }, map[string]string{}) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if got, want := out.SourceCurrency, "USDT"; got != want { + t.Fatalf("unexpected source currency: got=%q want=%q", got, want) + } + if got, want := out.DestinationCurrency, "RUB"; got != want { + t.Fatalf("unexpected destination currency: got=%q want=%q", got, want) + } + if resolver.calls != 2 { + t.Fatalf("unexpected resolver calls: got=%d want=%d", resolver.calls, 2) + } +} + +type fakeLedgerCurrencyResolver struct { + currencies map[string]string + calls int +} + +func (f *fakeLedgerCurrencyResolver) ResolveLedgerAccountCurrency(_ context.Context, _ string, ledgerAccountRef string) (string, error) { + f.calls++ + return f.currencies[ledgerAccountRef], nil +} diff --git a/api/payments/quotation/internal/service/quotation/quote_computation_service/intent_adapters.go b/api/payments/quotation/internal/service/quotation/quote_computation_service/intent_adapters.go index 213ccd41..4edb5bb7 100644 --- a/api/payments/quotation/internal/service/quotation/quote_computation_service/intent_adapters.go +++ b/api/payments/quotation/internal/service/quotation/quote_computation_service/intent_adapters.go @@ -8,6 +8,15 @@ import ( paymenttypes "github.com/tech/sendico/pkg/payments/types" ) +type fxDerivationResult struct { + InferredSourceCurrency string + InferredDestinationCurrency string + EffectiveSourceCurrency string + EffectiveDestinationCurrency string + ExplicitOverrideApplied bool + RequiresFXInferred bool +} + func modelIntentFromQuoteIntent(src *transfer_intent_hydrator.QuoteIntent) model.PaymentIntent { if src == nil { return model.PaymentIntent{} @@ -33,18 +42,44 @@ func modelIntentFromQuoteIntent(src *transfer_intent_hydrator.QuoteIntent) model } func fxIntentFromHydratedIntent(src *transfer_intent_hydrator.QuoteIntent) *model.FXIntent { - if src == nil { + if src == nil || src.FX == nil { return nil } - if strings.TrimSpace(string(src.FXSide)) == "" || src.FXSide == paymenttypes.FXSideUnspecified { + result := &model.FXIntent{ + Side: src.FX.Side, + Firm: src.FX.Firm, + TTLMillis: src.FX.TTLms, + PreferredProvider: strings.TrimSpace(src.FX.PreferredProvider), + MaxAgeMillis: src.FX.MaxAgeMs, + } + if src.FX.Pair != nil { + result.Pair = &paymenttypes.CurrencyPair{ + Base: normalizeAsset(src.FX.Pair.GetBase()), + Quote: normalizeAsset(src.FX.Pair.GetQuote()), + } + } + if result.Side == paymenttypes.FXSideUnspecified && + result.Pair == nil && + !result.Firm && + result.TTLMillis == 0 && + result.PreferredProvider == "" && + result.MaxAgeMillis == 0 { return nil } - return &model.FXIntent{Side: src.FXSide} + return result } -func ensureDerivedFXIntent(intent *model.PaymentIntent) { +func ensureDerivedFXIntent( + intent *model.PaymentIntent, + inferredSourceCurrency string, + inferredDestinationCurrency string, +) fxDerivationResult { + result := fxDerivationResult{ + InferredSourceCurrency: normalizeAsset(inferredSourceCurrency), + InferredDestinationCurrency: normalizeAsset(inferredDestinationCurrency), + } if intent == nil { - return + return result } amountCurrency := "" @@ -52,6 +87,9 @@ func ensureDerivedFXIntent(intent *model.PaymentIntent) { amountCurrency = normalizeAsset(intent.Amount.GetCurrency()) } settlementCurrency := normalizeAsset(intent.SettlementCurrency) + if result.InferredDestinationCurrency != "" { + settlementCurrency = result.InferredDestinationCurrency + } if settlementCurrency == "" { settlementCurrency = amountCurrency } @@ -59,42 +97,98 @@ func ensureDerivedFXIntent(intent *model.PaymentIntent) { intent.SettlementCurrency = settlementCurrency } - sourceCurrency := sourceAssetToken(intent.Source) + sourceCurrency := firstNonEmpty(result.InferredSourceCurrency, sourceAssetToken(intent.Source)) + sourceCurrencyBeforeExplicit := sourceCurrency + settlementCurrencyBeforeExplicit := settlementCurrency + requiresFXBeforeExplicit := intent.RequiresFX - // For FIX_RECEIVED, destination amounts can be provided in payout currency. - // Derive FX necessity from source asset currency when available. - if !intent.RequiresFX && - intent.SettlementMode == model.SettlementModeFixReceived && + explicitSourceCurrency, explicitDestinationCurrency := fxTradeCurrencies(intent.FX) + if explicitSourceCurrency != "" && explicitDestinationCurrency != "" { + sourceCurrency = explicitSourceCurrency + settlementCurrency = explicitDestinationCurrency + intent.SettlementCurrency = settlementCurrency + intent.RequiresFX = true + if sourceCurrencyBeforeExplicit == "" || + settlementCurrencyBeforeExplicit == "" || + !strings.EqualFold(sourceCurrencyBeforeExplicit, explicitSourceCurrency) || + !strings.EqualFold(settlementCurrencyBeforeExplicit, explicitDestinationCurrency) || + !requiresFXBeforeExplicit { + result.ExplicitOverrideApplied = true + } + } else if !intent.RequiresFX && sourceCurrency != "" && settlementCurrency != "" && !strings.EqualFold(sourceCurrency, settlementCurrency) { intent.RequiresFX = true + result.RequiresFXInferred = true } if !intent.RequiresFX { - return + result.EffectiveSourceCurrency = sourceCurrency + result.EffectiveDestinationCurrency = settlementCurrency + return result } baseCurrency := firstNonEmpty(sourceCurrency, amountCurrency) quoteCurrency := settlementCurrency if baseCurrency == "" || quoteCurrency == "" { - return + result.EffectiveSourceCurrency = sourceCurrency + result.EffectiveDestinationCurrency = settlementCurrency + return result } if intent.FX == nil { intent.FX = &model.FXIntent{} } + if strings.TrimSpace(string(intent.FX.Side)) == "" || intent.FX.Side == paymenttypes.FXSideUnspecified { + intent.FX.Side = paymenttypes.FXSideSellBaseBuyQuote + } if intent.FX.Pair == nil { intent.FX.Pair = &paymenttypes.CurrencyPair{} } + desiredBase, desiredQuote := fxPairFromTradeCurrencies(intent.FX.Side, baseCurrency, quoteCurrency) if normalizeAsset(intent.FX.Pair.Base) == "" { - intent.FX.Pair.Base = baseCurrency + intent.FX.Pair.Base = desiredBase } if normalizeAsset(intent.FX.Pair.Quote) == "" { - intent.FX.Pair.Quote = quoteCurrency + intent.FX.Pair.Quote = desiredQuote } - if strings.TrimSpace(string(intent.FX.Side)) == "" || intent.FX.Side == paymenttypes.FXSideUnspecified { - intent.FX.Side = paymenttypes.FXSideSellBaseBuyQuote + + result.EffectiveSourceCurrency, result.EffectiveDestinationCurrency = fxTradeCurrencies(intent.FX) + if result.EffectiveSourceCurrency == "" { + result.EffectiveSourceCurrency = sourceCurrency + } + if result.EffectiveDestinationCurrency == "" { + result.EffectiveDestinationCurrency = settlementCurrency + } + return result +} + +func fxPairFromTradeCurrencies(side paymenttypes.FXSide, sourceCurrency, destinationCurrency string) (string, string) { + sourceCurrency = normalizeAsset(sourceCurrency) + destinationCurrency = normalizeAsset(destinationCurrency) + switch side { + case paymenttypes.FXSideBuyBaseSellQuote: + return destinationCurrency, sourceCurrency + default: + return sourceCurrency, destinationCurrency + } +} + +func fxTradeCurrencies(fx *model.FXIntent) (string, string) { + if fx == nil || fx.Pair == nil { + return "", "" + } + base := normalizeAsset(fx.Pair.GetBase()) + quote := normalizeAsset(fx.Pair.GetQuote()) + if base == "" || quote == "" { + return "", "" + } + switch fx.Side { + case paymenttypes.FXSideBuyBaseSellQuote: + return quote, base + default: + return base, quote } } diff --git a/api/payments/quotation/internal/service/quotation/quote_computation_service/intent_adapters_test.go b/api/payments/quotation/internal/service/quotation/quote_computation_service/intent_adapters_test.go index 16524937..06b38eab 100644 --- a/api/payments/quotation/internal/service/quotation/quote_computation_service/intent_adapters_test.go +++ b/api/payments/quotation/internal/service/quotation/quote_computation_service/intent_adapters_test.go @@ -16,7 +16,7 @@ func TestEnsureDerivedFXIntent_DefaultsSideWhenEmpty(t *testing.T) { FX: &model.FXIntent{}, } - ensureDerivedFXIntent(intent) + ensureDerivedFXIntent(intent, "", "") if intent.FX == nil { t.Fatal("expected fx intent") @@ -34,7 +34,7 @@ func TestEnsureDerivedFXIntent_DefaultsSideWhenUnspecified(t *testing.T) { FX: &model.FXIntent{Side: paymenttypes.FXSideUnspecified}, } - ensureDerivedFXIntent(intent) + ensureDerivedFXIntent(intent, "", "") if intent.FX == nil { t.Fatal("expected fx intent") @@ -56,11 +56,17 @@ func TestEnsureDerivedFXIntent_PreservesExplicitSideFromHydratedIntent(t *testin Amount: &paymenttypes.Money{Amount: "100", Currency: "USDT"}, SettlementCurrency: "RUB", RequiresFX: true, - FXSide: paymenttypes.FXSideBuyBaseSellQuote, + FX: &transfer_intent_hydrator.QuoteFXIntent{ + Pair: &paymenttypes.CurrencyPair{ + Base: "RUB", + Quote: "USDT", + }, + Side: paymenttypes.FXSideBuyBaseSellQuote, + }, } intent := modelIntentFromQuoteIntent(hydrated) - ensureDerivedFXIntent(&intent) + ensureDerivedFXIntent(&intent, "", "") if intent.FX == nil { t.Fatal("expected fx intent") @@ -69,3 +75,45 @@ func TestEnsureDerivedFXIntent_PreservesExplicitSideFromHydratedIntent(t *testin t.Fatalf("unexpected side: got=%q want=%q", got, want) } } + +func TestEnsureDerivedFXIntent_ExplicitFXOverridesInferredCurrencies(t *testing.T) { + intent := &model.PaymentIntent{ + RequiresFX: false, + SettlementCurrency: "EUR", + Amount: &paymenttypes.Money{Amount: "10", Currency: "EUR"}, + FX: &model.FXIntent{ + Pair: &paymenttypes.CurrencyPair{ + Base: "USDT", + Quote: "RUB", + }, + Side: paymenttypes.FXSideSellBaseBuyQuote, + }, + } + + out := ensureDerivedFXIntent(intent, "BTC", "EUR") + + if !out.ExplicitOverrideApplied { + t.Fatalf("expected explicit override flag") + } + if !intent.RequiresFX { + t.Fatalf("expected requires_fx=true") + } + if got, want := intent.SettlementCurrency, "RUB"; got != want { + t.Fatalf("unexpected settlement currency: got=%q want=%q", got, want) + } + if intent.FX == nil || intent.FX.Pair == nil { + t.Fatalf("expected fx pair") + } + if got, want := intent.FX.Pair.GetBase(), "USDT"; got != want { + t.Fatalf("unexpected base currency: got=%q want=%q", got, want) + } + if got, want := intent.FX.Pair.GetQuote(), "RUB"; got != want { + t.Fatalf("unexpected quote currency: got=%q want=%q", got, want) + } + if got, want := out.EffectiveSourceCurrency, "USDT"; got != want { + t.Fatalf("unexpected effective source currency: got=%q want=%q", got, want) + } + if got, want := out.EffectiveDestinationCurrency, "RUB"; got != want { + t.Fatalf("unexpected effective destination currency: got=%q want=%q", got, want) + } +} diff --git a/api/payments/quotation/internal/service/quotation/quote_computation_service/planner.go b/api/payments/quotation/internal/service/quotation/quote_computation_service/planner.go index beb55655..d9010ccf 100644 --- a/api/payments/quotation/internal/service/quotation/quote_computation_service/planner.go +++ b/api/payments/quotation/internal/service/quotation/quote_computation_service/planner.go @@ -53,9 +53,10 @@ func (s *QuoteComputationService) BuildPlan(ctx context.Context, in ComputeInput Items: make([]*QuoteComputationPlanItem, 0, len(in.Intents)), } managedWalletNetworks := map[string]string{} + ledgerAccountCurrencies := map[string]string{} for i, intent := range in.Intents { - item, err := s.buildPlanItem(ctx, in, i, intent, managedWalletNetworks) + item, err := s.buildPlanItem(ctx, in, i, intent, managedWalletNetworks, ledgerAccountCurrencies) if err != nil { s.logger.Warn("Computation plan item build failed", zap.String("org_ref", in.OrganizationRef), @@ -84,6 +85,7 @@ func (s *QuoteComputationService) buildPlanItem( index int, intent *transfer_intent_hydrator.QuoteIntent, managedWalletNetworks map[string]string, + ledgerAccountCurrencies map[string]string, ) (*QuoteComputationPlanItem, error) { if intent == nil { s.logger.Warn("Plan item build failed: intent is nil", zap.Int("index", index)) @@ -137,7 +139,59 @@ func (s *QuoteComputationService) buildPlanItem( } modelIntent.Source = clonePaymentEndpoint(source) modelIntent.Destination = clonePaymentEndpoint(destination) - ensureDerivedFXIntent(&modelIntent) + currencyInference, err := s.inferEndpointCurrencies( + ctx, + strings.TrimSpace(in.OrganizationRef), + modelIntent, + ledgerAccountCurrencies, + ) + if err != nil { + return nil, merrors.InternalWrap(err, "resolve endpoint currencies") + } + fxDecision := ensureDerivedFXIntent( + &modelIntent, + currencyInference.SourceCurrency, + currencyInference.DestinationCurrency, + ) + if currencyInference.SourceInferred || currencyInference.DestinationInferred { + s.logger.Info("Resolved endpoint currencies for quote intent", + zap.Int("index", index), + zap.String("intent_ref", strings.TrimSpace(modelIntent.Ref)), + zap.String("inferred_source_currency", fxDecision.InferredSourceCurrency), + zap.String("inferred_destination_currency", fxDecision.InferredDestinationCurrency), + zap.String("effective_source_currency", fxDecision.EffectiveSourceCurrency), + zap.String("effective_destination_currency", fxDecision.EffectiveDestinationCurrency), + ) + } + if fxDecision.ExplicitOverrideApplied { + fxBase, fxQuote, fxSide := "", "", "" + if modelIntent.FX != nil { + fxSide = strings.TrimSpace(string(modelIntent.FX.Side)) + if modelIntent.FX.Pair != nil { + fxBase = strings.TrimSpace(modelIntent.FX.Pair.GetBase()) + fxQuote = strings.TrimSpace(modelIntent.FX.Pair.GetQuote()) + } + } + s.logger.Info("Applied explicit FX override to inferred endpoint currencies", + zap.Int("index", index), + zap.String("intent_ref", strings.TrimSpace(modelIntent.Ref)), + zap.String("inferred_source_currency", fxDecision.InferredSourceCurrency), + zap.String("inferred_destination_currency", fxDecision.InferredDestinationCurrency), + zap.String("effective_source_currency", fxDecision.EffectiveSourceCurrency), + zap.String("effective_destination_currency", fxDecision.EffectiveDestinationCurrency), + zap.String("fx_base_currency", fxBase), + zap.String("fx_quote_currency", fxQuote), + zap.String("fx_side", fxSide), + ) + } + if fxDecision.RequiresFXInferred { + s.logger.Info("Inferred FX requirement from endpoint currencies", + zap.Int("index", index), + zap.String("intent_ref", strings.TrimSpace(modelIntent.Ref)), + zap.String("source_currency", fxDecision.EffectiveSourceCurrency), + zap.String("destination_currency", fxDecision.EffectiveDestinationCurrency), + ) + } sourceRail, sourceNetwork, err := plan.RailFromEndpoint(source, modelIntent.Attributes, true) if err != nil { diff --git a/api/payments/quotation/internal/service/quotation/quote_computation_service/service.go b/api/payments/quotation/internal/service/quotation/quote_computation_service/service.go index 6f07935b..2747dac2 100644 --- a/api/payments/quotation/internal/service/quotation/quote_computation_service/service.go +++ b/api/payments/quotation/internal/service/quotation/quote_computation_service/service.go @@ -19,16 +19,21 @@ type ManagedWalletNetworkResolver interface { ResolveManagedWalletNetwork(ctx context.Context, managedWalletRef string) (string, error) } +type LedgerAccountCurrencyResolver interface { + ResolveLedgerAccountCurrency(ctx context.Context, organizationRef, ledgerAccountRef string) (string, error) +} + type Option func(*QuoteComputationService) type QuoteComputationService struct { - core Core - fundingResolver gateway_funding_profile.FundingProfileResolver - gatewayRegistry plan.GatewayRegistry - managedWalletNetworkResolver ManagedWalletNetworkResolver - routeStore plan.RouteStore - pathFinder *graph_path_finder.GraphPathFinder - logger mlogger.Logger + core Core + fundingResolver gateway_funding_profile.FundingProfileResolver + gatewayRegistry plan.GatewayRegistry + managedWalletNetworkResolver ManagedWalletNetworkResolver + ledgerAccountCurrencyResolver LedgerAccountCurrencyResolver + routeStore plan.RouteStore + pathFinder *graph_path_finder.GraphPathFinder + logger mlogger.Logger } func New(core Core, opts ...Option) *QuoteComputationService { @@ -69,6 +74,14 @@ func WithManagedWalletNetworkResolver(resolver ManagedWalletNetworkResolver) Opt } } +func WithLedgerAccountCurrencyResolver(resolver LedgerAccountCurrencyResolver) Option { + return func(svc *QuoteComputationService) { + if svc != nil { + svc.ledgerAccountCurrencyResolver = resolver + } + } +} + func WithRouteStore(store plan.RouteStore) Option { return func(svc *QuoteComputationService) { if svc != nil { diff --git a/api/payments/quotation/internal/service/quotation/service.go b/api/payments/quotation/internal/service/quotation/service.go index d0195418..6d28080f 100644 --- a/api/payments/quotation/internal/service/quotation/service.go +++ b/api/payments/quotation/internal/service/quotation/service.go @@ -1,6 +1,7 @@ package quotation import ( + ledgerclient "github.com/tech/sendico/ledger/client" "github.com/tech/sendico/payments/storage" clockpkg "github.com/tech/sendico/pkg/clock" "github.com/tech/sendico/pkg/mlogger" @@ -35,6 +36,7 @@ type serviceDependencies struct { fees feesDependency gateway gatewayDependency oracle oracleDependency + ledger ledgerclient.Client gatewayRegistry GatewayRegistry gatewayInvokeResolver GatewayInvokeResolver cardRoutes map[string]CardGatewayRoute diff --git a/api/payments/quotation/internal/service/quotation/transfer_intent_hydrator/hydrator.go b/api/payments/quotation/internal/service/quotation/transfer_intent_hydrator/hydrator.go index 02cc48db..177b20b0 100644 --- a/api/payments/quotation/internal/service/quotation/transfer_intent_hydrator/hydrator.go +++ b/api/payments/quotation/internal/service/quotation/transfer_intent_hydrator/hydrator.go @@ -13,6 +13,7 @@ import ( paymentv1 "github.com/tech/sendico/pkg/proto/common/payment/v1" methodsv1 "github.com/tech/sendico/pkg/proto/payments/methods/v1" quotationv2 "github.com/tech/sendico/pkg/proto/payments/quotation/v2" + sharedv1 "github.com/tech/sendico/pkg/proto/payments/shared/v1" "go.mongodb.org/mongo-driver/v2/bson" "google.golang.org/grpc" ) @@ -123,7 +124,16 @@ func (h *TransferIntentHydrator) HydrateOne(ctx context.Context, in HydrateOneIn if settlementCurrency == "" { settlementCurrency = strings.ToUpper(strings.TrimSpace(amount.Currency)) } - requiresFX := !strings.EqualFold(amount.Currency, settlementCurrency) + fxIntent := fxIntentFromProto(in.Intent.GetFx()) + if settlementCurrency == "" { + settlementCurrency = settlementCurrencyFromFX(fxIntent) + } + requiresFX := false + if fxIntent != nil && fxIntent.Pair != nil { + requiresFX = true + } else { + requiresFX = !strings.EqualFold(amount.Currency, settlementCurrency) + } intent := &QuoteIntent{ Ref: h.newRef(), @@ -134,7 +144,7 @@ func (h *TransferIntentHydrator) HydrateOne(ctx context.Context, in HydrateOneIn Comment: strings.TrimSpace(in.Intent.GetComment()), SettlementMode: settlementMode, FeeTreatment: feeTreatment, - FXSide: fxSideFromProto(in.Intent.GetFxSide()), + FX: fxIntent, SettlementCurrency: settlementCurrency, RequiresFX: requiresFX, Attributes: map[string]string{ @@ -223,3 +233,55 @@ func fxSideFromProto(side fxv1.Side) paymenttypes.FXSide { return paymenttypes.FXSideUnspecified } } + +func fxIntentFromProto(src *sharedv1.FXIntent) *QuoteFXIntent { + if src == nil || src.GetPair() == nil { + return nil + } + base := strings.ToUpper(strings.TrimSpace(src.GetPair().GetBase())) + quote := strings.ToUpper(strings.TrimSpace(src.GetPair().GetQuote())) + if base == "" || quote == "" { + return nil + } + side := fxSideFromProto(src.GetSide()) + if side == paymenttypes.FXSideUnspecified { + side = paymenttypes.FXSideSellBaseBuyQuote + } + return &QuoteFXIntent{ + Pair: &paymenttypes.CurrencyPair{ + Base: base, + Quote: quote, + }, + Side: side, + Firm: src.GetFirm(), + TTLms: src.GetTtlMs(), + PreferredProvider: strings.TrimSpace(src.GetPreferredProvider()), + MaxAgeMs: src.GetMaxAgeMs(), + } +} + +func settlementCurrencyFromFX(fx *QuoteFXIntent) string { + if fx == nil || fx.Pair == nil { + return "" + } + base := strings.ToUpper(strings.TrimSpace(fx.Pair.GetBase())) + quote := strings.ToUpper(strings.TrimSpace(fx.Pair.GetQuote())) + switch fx.Side { + case paymenttypes.FXSideBuyBaseSellQuote: + return firstNonEmpty(base, quote) + case paymenttypes.FXSideSellBaseBuyQuote: + return firstNonEmpty(quote, base) + default: + return firstNonEmpty(quote, base) + } +} + +func firstNonEmpty(values ...string) string { + for _, value := range values { + trimmed := strings.TrimSpace(value) + if trimmed != "" { + return trimmed + } + } + return "" +} diff --git a/api/payments/quotation/internal/service/quotation/transfer_intent_hydrator/quote_intent.go b/api/payments/quotation/internal/service/quotation/transfer_intent_hydrator/quote_intent.go index 84dd3657..c79694ad 100644 --- a/api/payments/quotation/internal/service/quotation/transfer_intent_hydrator/quote_intent.go +++ b/api/payments/quotation/internal/service/quotation/transfer_intent_hydrator/quote_intent.go @@ -64,6 +64,15 @@ type QuoteCardEndpoint struct { MaskedPan string } +type QuoteFXIntent struct { + Pair *paymenttypes.CurrencyPair + Side paymenttypes.FXSide + Firm bool + TTLms int64 + PreferredProvider string + MaxAgeMs int32 +} + type QuoteEndpoint struct { Type QuoteEndpointType PaymentMethodRef string @@ -84,7 +93,7 @@ type QuoteIntent struct { Comment string SettlementMode QuoteSettlementMode FeeTreatment QuoteFeeTreatment - FXSide paymenttypes.FXSide + FX *QuoteFXIntent SettlementCurrency string RequiresFX bool Attributes map[string]string diff --git a/api/payments/quotation/internal/service/quotation/transfer_intent_hydrator/transfer_intent_hydrator_test.go b/api/payments/quotation/internal/service/quotation/transfer_intent_hydrator/transfer_intent_hydrator_test.go index baa51757..97d654d0 100644 --- a/api/payments/quotation/internal/service/quotation/transfer_intent_hydrator/transfer_intent_hydrator_test.go +++ b/api/payments/quotation/internal/service/quotation/transfer_intent_hydrator/transfer_intent_hydrator_test.go @@ -16,6 +16,7 @@ import ( endpointv1 "github.com/tech/sendico/pkg/proto/payments/endpoint/v1" methodsv1 "github.com/tech/sendico/pkg/proto/payments/methods/v1" quotationv2 "github.com/tech/sendico/pkg/proto/payments/quotation/v2" + sharedv1 "github.com/tech/sendico/pkg/proto/payments/shared/v1" "go.mongodb.org/mongo-driver/v2/bson" "google.golang.org/grpc" ) @@ -100,7 +101,7 @@ func TestHydrateOne_SuccessWithInlineMethods(t *testing.T) { } } -func TestHydrateOne_PropagatesFXSide(t *testing.T) { +func TestHydrateOne_PropagatesFXIntent(t *testing.T) { h := New(nil, WithRefFactory(func() string { return "q-intent-fx-side" })) intent := "ationv2.QuoteIntent{ Source: &endpointv1.PaymentEndpoint{ @@ -126,7 +127,17 @@ func TestHydrateOne_PropagatesFXSide(t *testing.T) { }, Amount: newMoney("10", "USDT"), SettlementCurrency: "RUB", - FxSide: fxv1.Side_BUY_BASE_SELL_QUOTE, + Fx: &sharedv1.FXIntent{ + Pair: &fxv1.CurrencyPair{ + Base: "USDT", + Quote: "RUB", + }, + Side: fxv1.Side_BUY_BASE_SELL_QUOTE, + Firm: true, + TtlMs: 12_000, + PreferredProvider: "bestfx", + MaxAgeMs: 1_000, + }, } got, err := h.HydrateOne(context.Background(), HydrateOneInput{ @@ -140,8 +151,65 @@ func TestHydrateOne_PropagatesFXSide(t *testing.T) { if got == nil { t.Fatalf("expected hydrated intent") } - if got.FXSide != paymenttypes.FXSideBuyBaseSellQuote { - t.Fatalf("unexpected fx side: got=%q", got.FXSide) + if got.FX == nil || got.FX.Pair == nil { + t.Fatalf("expected hydrated fx intent") + } + if got.FX.Side != paymenttypes.FXSideBuyBaseSellQuote { + t.Fatalf("unexpected fx side: got=%q", got.FX.Side) + } + if got.FX.Pair.GetBase() != "USDT" || got.FX.Pair.GetQuote() != "RUB" { + t.Fatalf("unexpected fx pair: got=%s/%s", got.FX.Pair.GetBase(), got.FX.Pair.GetQuote()) + } + if !got.FX.Firm || got.FX.TTLms != 12_000 || got.FX.PreferredProvider != "bestfx" || got.FX.MaxAgeMs != 1_000 { + t.Fatalf("unexpected fx extras: %#v", got.FX) + } +} + +func TestHydrateOne_RequiresFXWhenExplicitFXProvided(t *testing.T) { + h := New(nil, WithRefFactory(func() string { return "q-intent-fx-required" })) + intent := "ationv2.QuoteIntent{ + Source: &endpointv1.PaymentEndpoint{ + Source: &endpointv1.PaymentEndpoint_PaymentMethod{ + PaymentMethod: &endpointv1.PaymentMethod{ + Type: endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_LEDGER, + Data: mustMarshalBSON(t, map[string]string{"ledgerAccountRef": "ledger-src"}), + }, + }, + }, + Destination: &endpointv1.PaymentEndpoint{ + Source: &endpointv1.PaymentEndpoint_PaymentMethod{ + PaymentMethod: &endpointv1.PaymentMethod{ + Type: endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_CARD, + Data: mustMarshalBSON(t, pkgmodel.CardPaymentData{ + Pan: "4111111111111111", + ExpMonth: "12", + ExpYear: "2030", + Country: "US", + }), + }, + }, + }, + Amount: newMoney("10", "RUB"), + SettlementCurrency: "RUB", + Fx: &sharedv1.FXIntent{ + Pair: &fxv1.CurrencyPair{Base: "USDT", Quote: "RUB"}, + Side: fxv1.Side_SELL_BASE_BUY_QUOTE, + }, + } + + got, err := h.HydrateOne(context.Background(), HydrateOneInput{ + OrganizationRef: bson.NewObjectID().Hex(), + InitiatorRef: bson.NewObjectID().Hex(), + Intent: intent, + }) + if err != nil { + t.Fatalf("HydrateOne returned error: %v", err) + } + if got == nil { + t.Fatalf("expected hydrated intent") + } + if !got.RequiresFX { + t.Fatalf("expected requires_fx=true when explicit fx is supplied") } } diff --git a/api/pkg/go.mod b/api/pkg/go.mod index f2dcaee2..c808b8c1 100644 --- a/api/pkg/go.mod +++ b/api/pkg/go.mod @@ -5,7 +5,7 @@ go 1.25.0 require ( github.com/casbin/casbin/v2 v2.135.0 github.com/casbin/mongodb-adapter/v4 v4.3.0 - github.com/ethereum/go-ethereum v1.17.0 + github.com/ethereum/go-ethereum v1.17.1 github.com/go-chi/chi/v5 v5.2.5 github.com/google/uuid v1.6.0 github.com/hashicorp/vault/api v1.22.0 diff --git a/api/pkg/go.sum b/api/pkg/go.sum index 446a7bae..f21243dc 100644 --- a/api/pkg/go.sum +++ b/api/pkg/go.sum @@ -61,8 +61,8 @@ github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A= github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= github.com/ethereum/c-kzg-4844/v2 v2.1.6 h1:xQymkKCT5E2Jiaoqf3v4wsNgjZLY0lRSkZn27fRjSls= github.com/ethereum/c-kzg-4844/v2 v2.1.6/go.mod h1:8HMkUZ5JRv4hpw/XUrYWSQNAUzhHMg2UDb/U+5m+XNw= -github.com/ethereum/go-ethereum v1.17.0 h1:2D+1Fe23CwZ5tQoAS5DfwKFNI1HGcTwi65/kRlAVxes= -github.com/ethereum/go-ethereum v1.17.0/go.mod h1:2W3msvdosS/MCWytpqTcqgFiRYbTH59FxDJzqah120o= +github.com/ethereum/go-ethereum v1.17.1 h1:IjlQDjgxg2uL+GzPRkygGULPMLzcYWncEI7wbaizvho= +github.com/ethereum/go-ethereum v1.17.1/go.mod h1:7UWOVHL7K3b8RfVRea022btnzLCaanwHtBuH1jUCH/I= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= diff --git a/api/proto/payments/quotation/v2/quotation.proto b/api/proto/payments/quotation/v2/quotation.proto index ccce05eb..c04a85b8 100644 --- a/api/proto/payments/quotation/v2/quotation.proto +++ b/api/proto/payments/quotation/v2/quotation.proto @@ -6,7 +6,6 @@ option go_package = "github.com/tech/sendico/pkg/proto/payments/quotation/v2;quo import "api/proto/payments/shared/v1/shared.proto"; import "api/proto/common/money/v1/money.proto"; -import "api/proto/common/fx/v1/fx.proto"; import "api/proto/common/payment/v1/settlement.proto"; import "api/proto/payments/endpoint/v1/endpoint.proto"; import "api/proto/payments/quotation/v2/interface.proto"; @@ -20,7 +19,7 @@ message QuoteIntent { payments.quotation.v2.FeeTreatment fee_treatment = 5; string settlement_currency = 6; string comment = 7; - common.fx.v1.Side fx_side = 8; + payments.shared.v1.FXIntent fx = 8; } // QuotePaymentRequest is the request to quote a single v2 payment. diff --git a/frontend/pshared/lib/provider/payment/provider.dart b/frontend/pshared/lib/provider/payment/provider.dart index 995208dc..32f7e604 100644 --- a/frontend/pshared/lib/provider/payment/provider.dart +++ b/frontend/pshared/lib/provider/payment/provider.dart @@ -39,8 +39,7 @@ class PaymentProvider extends ChangeNotifier { String? clientPaymentRef, Map? metadata, }) async { - if (!_organization.isOrganizationSet) - throw StateError('Organization is not set'); + if (!_organization.isOrganizationSet) throw StateError('Organization is not set'); final quoteRef = _quotation.quotation?.quoteRef; if (quoteRef == null || quoteRef.isEmpty) { throw StateError('Quotation reference is not set'); diff --git a/frontend/pshared/lib/provider/payment/quotation/intent_builder.dart b/frontend/pshared/lib/provider/payment/quotation/intent_builder.dart index d3f14800..fb278b4b 100644 --- a/frontend/pshared/lib/provider/payment/quotation/intent_builder.dart +++ b/frontend/pshared/lib/provider/payment/quotation/intent_builder.dart @@ -98,7 +98,7 @@ class QuotationIntentBuilder { } return FxIntent( pair: CurrencyPair(base: base, quote: quote), - side: FxSide.buyBaseSellQuote, + side: FxSide.sellBaseBuyQuote, ); } diff --git a/t.log b/req.json similarity index 100% rename from t.log rename to req.json diff --git a/test.csv b/test.csv deleted file mode 100644 index b860632e..00000000 --- a/test.csv +++ /dev/null @@ -1,5 +0,0 @@ -pan;first_name;last_name;exp_month;exp_year;amount -2204310159722456;Anastasiia;Limonova;06;2028;100 -2204320167919754;Anastasiia;Limonova;10;2027;100 -2200242558874568;Anastasiia;Limonova;07;2032;100 -2203410113188371;Vladimir;Burmakin;02;2033;100 \ No newline at end of file