payment quotation v2 + payment orchestration v2 draft
This commit is contained in:
@@ -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.1
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.9
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.9
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.0
|
||||
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.1
|
||||
github.com/go-chi/chi/v5 v5.2.5
|
||||
github.com/go-chi/cors v1.2.2
|
||||
github.com/go-chi/jwtauth/v5 v5.3.3
|
||||
@@ -53,21 +53,21 @@ 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.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17 // 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.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.8 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.14 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.6 // indirect
|
||||
github.com/aws/smithy-go v1.24.0 // 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.9 // 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/smithy-go v1.24.1 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/casbin/mongodb-adapter/v4 v4.3.0 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
@@ -76,7 +76,7 @@ require (
|
||||
github.com/containerd/platforms v0.2.1 // indirect
|
||||
github.com/cpuguy83/dockercfg v0.3.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/docker/docker v27.3.1+incompatible // indirect
|
||||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
@@ -107,7 +107,7 @@ require (
|
||||
github.com/moby/term v0.5.0 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/nats-io/nats.go v1.48.0 // indirect
|
||||
github.com/nats-io/nats.go v1.49.0 // indirect
|
||||
github.com/nats-io/nkeys v0.4.15 // indirect
|
||||
github.com/nats-io/nuid v1.0.1 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
@@ -144,5 +144,5 @@ require (
|
||||
golang.org/x/sync v0.19.0 // indirect
|
||||
golang.org/x/sys v0.41.0 // indirect
|
||||
golang.org/x/text v0.34.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260223185530-2f722ef697dc // indirect
|
||||
)
|
||||
|
||||
@@ -6,44 +6,44 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/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.1 h1:ABlyEARCDLN034NhxlRUSZr4l71mh+T5KAeGh6cerhU=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.1/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4 h1:489krEF9xIGkOaaX3CE/Be2uWjiXrkCH6gUX+bZA/BU=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4/go.mod h1:IOAPF6oT9KCsceNTvvYMNHy0+kMF8akOjeDvPENWxp4=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.9 h1:ktda/mtAydeObvJXlHzyGpK1xcsLaP16zfUPDGoW90A=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.9/go.mod h1:U+fCQ+9QKsLW786BCfEjYRj34VVTbPdsLP3CHSYXMOI=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.9 h1:sWvTKsyrMlJGEuj/WgrwilpoJ6Xa1+KhIpGdzw7mMU8=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.9/go.mod h1:+J44MBhmfVY/lETFiKI+klz0Vym2aCmIjqgClMmW82w=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17 h1:I0GyV8wiYrP8XpA70g1HBcQO1JlQxCMTW9npl5UbDHY=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17/go.mod h1:tyw7BOl5bBe/oqvoIeECFJjMdzXoa/dfVz3QQ5lgHGA=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17 h1:xOLELNKGp2vsiteLsvLPwxC+mYmO6OZ8PYgiuPJzF8U=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17/go.mod h1:5M5CI3D12dNOtH3/mk6minaRwI2/37ifCURZISxA/IQ=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17 h1:WWLqlh79iO48yLkj1v3ISRNiv+3KdQoZ6JWyfcsyQik=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17/go.mod h1:EhG22vHRrvF8oXSTYStZhJc1aUgKtnJe+aOiFEV90cM=
|
||||
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.17 h1:JqcdRG//czea7Ppjb+g/n4o8i/R50aTBHkA7vu0lK+k=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.17/go.mod h1:CO+WeGmIdj/MlPel2KwID9Gt7CNq4M65HUfBW97liM0=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 h1:0ryTNEdJbzUCEWkVXEXoqlXV72J5keC1GvILMOuD00E=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4/go.mod h1:HQ4qwNZh32C3CBeO6iJLQlgtMzqeG17ziAA/3KDJFow=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.8 h1:Z5EiPIzXKewUQK0QTMkutjiaPVeVYXX7KIqhXu/0fXs=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.8/go.mod h1:FsTpJtvC4U1fyDXk7c71XoDv3HlRm8V3NiYLeYLh5YE=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17 h1:RuNSMoozM8oXlgLG/n6WLaFGoea7/CddrCfIiSA+xdY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17/go.mod h1:F2xxQ9TZz5gDWsclCtPQscGpP0VUOc8RqgFM3vDENmU=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.17 h1:bGeHBsGZx0Dvu/eJC0Lh9adJa3M1xREcndxLNZlve2U=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.17/go.mod h1:dcW24lbU0CzHusTE8LLHhRLI42ejmINN8Lcr22bwh/g=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.0 h1:oeu8VPlOre74lBA/PMhxa5vewaMIMmILM+RraSyB8KA=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.0/go.mod h1:5jggDlZ2CLQhwJBiZJb4vfk4f0GxWdEDruWKEJ1xOdo=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.5 h1:VrhDvQib/i0lxvr3zqlUwLwJP4fpmpyD9wYG1vfSu+Y=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.5/go.mod h1:k029+U8SY30/3/ras4G/Fnv/b88N4mAfliNn08Dem4M=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.10 h1:+VTRawC4iVY58pS/lzpo0lnoa/SYNGF4/B/3/U5ro8Y=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.10/go.mod h1:yifAsgBxgJWn3ggx70A3urX2AN49Y5sJTD1UQFlfqBw=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.14 h1:0jbJeuEHlwKJ9PfXtpSFc4MF+WIWORdhN1n30ITZGFM=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.14/go.mod h1:sTGThjphYE4Ohw8vJiRStAcu3rbjtXRsdNB0TvZ5wwo=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.6 h1:5fFjR/ToSOzB2OQ/XqWpZBmNvmP/pJ1jOWYlFDJTjRQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.6/go.mod h1:qgFDZQSD/Kys7nJnVqYlWKnh0SSdMjAi0uSwON4wgYQ=
|
||||
github.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk=
|
||||
github.com/aws/smithy-go v1.24.0/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0=
|
||||
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.9 h1:IJRzQTvdpjHRPItx9gzNcz7Y1F+xqAR+xiy9rr5ZYl8=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.9/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.1 h1:giB30dEeoar5bgDnkE0q+z7cFjcHaCjulpmPVmuKR84=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.1/go.mod h1:071TH4M3botFLWDbzQLfBR7tXYi7Fs2RsXSiH7nlUlY=
|
||||
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/smithy-go v1.24.1 h1:VbyeNfmYkWoxMVpGUAbQumkODcYmfMRfZ8yQiH30SK0=
|
||||
github.com/aws/smithy-go v1.24.1/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
@@ -73,8 +73,8 @@ github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 h1:5RVFMOWjMyRy8cARdy79nAmgYw3hK/4HUq48LQ6Wwqo=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/docker/docker v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI=
|
||||
@@ -175,8 +175,8 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/nats-io/nats.go v1.48.0 h1:pSFyXApG+yWU/TgbKCjmm5K4wrHu86231/w84qRVR+U=
|
||||
github.com/nats-io/nats.go v1.48.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
||||
github.com/nats-io/nats.go v1.49.0 h1:yh/WvY59gXqYpgl33ZI+XoVPKyut/IcEaqtsiuTJpoE=
|
||||
github.com/nats-io/nats.go v1.49.0/go.mod h1:fDCn3mN5cY8HooHwE2ukiLb4p4G4ImmzvXyJt+tGwdw=
|
||||
github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
|
||||
github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
|
||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||
@@ -363,8 +363,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-20260217215200-42d3e9bedb6d h1:EocjzKLywydp5uZ5tJ79iP6Q0UjDnyiHkGRWxuPBP8s=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260217215200-42d3e9bedb6d/go.mod h1:48U2I+QQUYhsFrg2SY6r+nJzeOtjey7j//WBESw+qyQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d h1:t/LOSXPJ9R0B6fnZNyALBRfZBH0Uy0gT+uR+SJ6syqQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260223185530-2f722ef697dc h1:51Wupg8spF+5FC6D+iMKbOddFjMckETnNnEiZ+HX37s=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260223185530-2f722ef697dc/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package srequest
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
)
|
||||
|
||||
@@ -23,8 +25,7 @@ type QuotePayment struct {
|
||||
}
|
||||
|
||||
func (r *QuotePayment) Validate() error {
|
||||
// base checks
|
||||
if err := r.PaymentBase.Validate(); err != nil {
|
||||
if err := validateQuoteIdempotency(r.PreviewOnly, r.IdempotencyKey); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -43,7 +44,7 @@ type QuotePayments struct {
|
||||
}
|
||||
|
||||
func (r *QuotePayments) Validate() error {
|
||||
if err := r.PaymentBase.Validate(); err != nil {
|
||||
if err := validateQuoteIdempotency(r.PreviewOnly, r.IdempotencyKey); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(r.Intents) == 0 {
|
||||
@@ -57,6 +58,20 @@ func (r *QuotePayments) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateQuoteIdempotency(previewOnly bool, idempotencyKey string) error {
|
||||
key := strings.TrimSpace(idempotencyKey)
|
||||
if previewOnly {
|
||||
if key != "" {
|
||||
return merrors.InvalidArgument("previewOnly requests must not include idempotencyKey", "idempotencyKey")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if key == "" {
|
||||
return merrors.InvalidArgument("idempotencyKey is required", "idempotencyKey")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type InitiatePayment struct {
|
||||
PaymentBase `json:",inline"`
|
||||
Intent *PaymentIntent `json:"intent,omitempty"`
|
||||
|
||||
29
api/server/interface/api/srequest/payment_validate_test.go
Normal file
29
api/server/interface/api/srequest/payment_validate_test.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package srequest
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestValidateQuoteIdempotency(t *testing.T) {
|
||||
t.Run("non-preview requires idempotency key", func(t *testing.T) {
|
||||
if err := validateQuoteIdempotency(false, ""); err == nil {
|
||||
t.Fatalf("expected error for empty idempotency key")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("preview rejects idempotency key", func(t *testing.T) {
|
||||
if err := validateQuoteIdempotency(true, "idem-1"); err == nil {
|
||||
t.Fatalf("expected error when preview request has idempotency key")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("preview accepts empty idempotency key", func(t *testing.T) {
|
||||
if err := validateQuoteIdempotency(true, ""); err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("non-preview accepts idempotency key", func(t *testing.T) {
|
||||
if err := validateQuoteIdempotency(false, "idem-1"); err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package sresponse
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -11,9 +12,9 @@ import (
|
||||
feesv1 "github.com/tech/sendico/pkg/proto/billing/fees/v1"
|
||||
paginationv1 "github.com/tech/sendico/pkg/proto/common/pagination/v1"
|
||||
oraclev1 "github.com/tech/sendico/pkg/proto/oracle/v1"
|
||||
orchestratorv1 "github.com/tech/sendico/pkg/proto/payments/orchestration/v1"
|
||||
quotationv1 "github.com/tech/sendico/pkg/proto/payments/quotation/v1"
|
||||
sharedv1 "github.com/tech/sendico/pkg/proto/payments/shared/v1"
|
||||
orchestrationv2 "github.com/tech/sendico/pkg/proto/payments/orchestration/v2"
|
||||
quotationv2 "github.com/tech/sendico/pkg/proto/payments/quotation/v2"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
type FeeLine struct {
|
||||
@@ -96,7 +97,7 @@ type paymentResponse struct {
|
||||
}
|
||||
|
||||
// PaymentQuote wraps a payment quote with refreshed access token.
|
||||
func PaymentQuoteResponse(logger mlogger.Logger, idempotencyKey string, quote *sharedv1.PaymentQuote, token *TokenData) http.HandlerFunc {
|
||||
func PaymentQuoteResponse(logger mlogger.Logger, idempotencyKey string, quote *quotationv2.PaymentQuote, token *TokenData) http.HandlerFunc {
|
||||
return response.Ok(logger, paymentQuoteResponse{
|
||||
Quote: toPaymentQuote(quote),
|
||||
IdempotencyKey: idempotencyKey,
|
||||
@@ -105,7 +106,7 @@ func PaymentQuoteResponse(logger mlogger.Logger, idempotencyKey string, quote *s
|
||||
}
|
||||
|
||||
// PaymentQuotes wraps batch quotes with refreshed access token.
|
||||
func PaymentQuotesResponse(logger mlogger.Logger, resp *quotationv1.QuotePaymentsResponse, token *TokenData) http.HandlerFunc {
|
||||
func PaymentQuotesResponse(logger mlogger.Logger, resp *quotationv2.QuotePaymentsResponse, token *TokenData) http.HandlerFunc {
|
||||
return response.Ok(logger, paymentQuotesResponse{
|
||||
Quote: toPaymentQuotes(resp),
|
||||
authResponse: authResponse{AccessToken: *token},
|
||||
@@ -113,7 +114,7 @@ func PaymentQuotesResponse(logger mlogger.Logger, resp *quotationv1.QuotePayment
|
||||
}
|
||||
|
||||
// Payments wraps a list of payments with refreshed access token.
|
||||
func PaymentsResponse(logger mlogger.Logger, payments []*sharedv1.Payment, token *TokenData) http.HandlerFunc {
|
||||
func PaymentsResponse(logger mlogger.Logger, payments []*orchestrationv2.Payment, token *TokenData) http.HandlerFunc {
|
||||
return response.Ok(logger, paymentsResponse{
|
||||
Payments: toPayments(payments),
|
||||
authResponse: authResponse{AccessToken: *token},
|
||||
@@ -121,7 +122,7 @@ func PaymentsResponse(logger mlogger.Logger, payments []*sharedv1.Payment, token
|
||||
}
|
||||
|
||||
// PaymentsList wraps a list of payments with refreshed access token and pagination data.
|
||||
func PaymentsListResponse(logger mlogger.Logger, resp *orchestratorv1.ListPaymentsResponse, token *TokenData) http.HandlerFunc {
|
||||
func PaymentsListResponse(logger mlogger.Logger, resp *orchestrationv2.ListPaymentsResponse, token *TokenData) http.HandlerFunc {
|
||||
return response.Ok(logger, paymentsResponse{
|
||||
Payments: toPayments(resp.GetPayments()),
|
||||
Page: resp.GetPage(),
|
||||
@@ -130,7 +131,7 @@ func PaymentsListResponse(logger mlogger.Logger, resp *orchestratorv1.ListPaymen
|
||||
}
|
||||
|
||||
// Payment wraps a payment with refreshed access token.
|
||||
func PaymentResponse(logger mlogger.Logger, payment *sharedv1.Payment, token *TokenData) http.HandlerFunc {
|
||||
func PaymentResponse(logger mlogger.Logger, payment *orchestrationv2.Payment, token *TokenData) http.HandlerFunc {
|
||||
return response.Ok(logger, paymentResponse{
|
||||
Payment: toPayment(payment),
|
||||
authResponse: authResponse{AccessToken: *token},
|
||||
@@ -191,33 +192,20 @@ func toFxQuote(q *oraclev1.Quote) *FxQuote {
|
||||
}
|
||||
}
|
||||
|
||||
func toPaymentQuote(q *sharedv1.PaymentQuote) *PaymentQuote {
|
||||
func toPaymentQuote(q *quotationv2.PaymentQuote) *PaymentQuote {
|
||||
if q == nil {
|
||||
return nil
|
||||
}
|
||||
return &PaymentQuote{
|
||||
QuoteRef: q.GetQuoteRef(),
|
||||
DebitAmount: toMoney(q.GetDebitAmount()),
|
||||
DebitSettlementAmount: toMoney(q.GetDebitSettlementAmount()),
|
||||
ExpectedSettlementAmount: toMoney(q.GetExpectedSettlementAmount()),
|
||||
ExpectedFeeTotal: toMoney(q.GetExpectedFeeTotal()),
|
||||
DebitAmount: toMoney(q.GetPayerTotalDebitAmount()),
|
||||
ExpectedSettlementAmount: toMoney(q.GetDestinationAmount()),
|
||||
FeeLines: toFeeLines(q.GetFeeLines()),
|
||||
FxQuote: toFxQuote(q.GetFxQuote()),
|
||||
}
|
||||
}
|
||||
|
||||
func toPaymentQuoteAggregate(q *sharedv1.PaymentQuoteAggregate) *PaymentQuoteAggregate {
|
||||
if q == nil {
|
||||
return nil
|
||||
}
|
||||
return &PaymentQuoteAggregate{
|
||||
DebitAmounts: toMoneyList(q.GetDebitAmounts()),
|
||||
ExpectedSettlementAmounts: toMoneyList(q.GetExpectedSettlementAmounts()),
|
||||
ExpectedFeeTotals: toMoneyList(q.GetExpectedFeeTotals()),
|
||||
}
|
||||
}
|
||||
|
||||
func toPaymentQuotes(resp *quotationv1.QuotePaymentsResponse) *PaymentQuotes {
|
||||
func toPaymentQuotes(resp *quotationv2.QuotePaymentsResponse) *PaymentQuotes {
|
||||
if resp == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -233,12 +221,11 @@ func toPaymentQuotes(resp *quotationv1.QuotePaymentsResponse) *PaymentQuotes {
|
||||
return &PaymentQuotes{
|
||||
IdempotencyKey: resp.GetIdempotencyKey(),
|
||||
QuoteRef: resp.GetQuoteRef(),
|
||||
Aggregate: toPaymentQuoteAggregate(resp.GetAggregate()),
|
||||
Quotes: quotes,
|
||||
}
|
||||
}
|
||||
|
||||
func toPayments(items []*sharedv1.Payment) []Payment {
|
||||
func toPayments(items []*orchestrationv2.Payment) []Payment {
|
||||
if len(items) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -254,22 +241,65 @@ func toPayments(items []*sharedv1.Payment) []Payment {
|
||||
return result
|
||||
}
|
||||
|
||||
func toPayment(p *sharedv1.Payment) *Payment {
|
||||
func toPayment(p *orchestrationv2.Payment) *Payment {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
failureCode, failureReason := firstFailure(p.GetStepExecutions())
|
||||
return &Payment{
|
||||
PaymentRef: p.GetPaymentRef(),
|
||||
IdempotencyKey: p.GetIdempotencyKey(),
|
||||
State: enumJSONName(p.GetState().String()),
|
||||
FailureCode: enumJSONName(p.GetFailureCode().String()),
|
||||
FailureReason: p.GetFailureReason(),
|
||||
LastQuote: toPaymentQuote(p.GetLastQuote()),
|
||||
CreatedAt: p.GetCreatedAt().AsTime(),
|
||||
Meta: p.GetMetadata(),
|
||||
FailureCode: failureCode,
|
||||
FailureReason: failureReason,
|
||||
LastQuote: toPaymentQuote(p.GetQuoteSnapshot()),
|
||||
CreatedAt: timestampAsTime(p.GetCreatedAt()),
|
||||
Meta: paymentMeta(p),
|
||||
IdempotencyKey: "",
|
||||
}
|
||||
}
|
||||
|
||||
func firstFailure(steps []*orchestrationv2.StepExecution) (string, string) {
|
||||
for _, step := range steps {
|
||||
if step == nil || step.GetFailure() == nil {
|
||||
continue
|
||||
}
|
||||
failure := step.GetFailure()
|
||||
message := strings.TrimSpace(failure.GetMessage())
|
||||
if message == "" {
|
||||
message = strings.TrimSpace(failure.GetCode())
|
||||
}
|
||||
return enumJSONName(failure.GetCategory().String()), message
|
||||
}
|
||||
return "", ""
|
||||
}
|
||||
|
||||
func paymentMeta(p *orchestrationv2.Payment) map[string]string {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
meta := make(map[string]string)
|
||||
if quotationRef := strings.TrimSpace(p.GetQuotationRef()); quotationRef != "" {
|
||||
meta["quotationRef"] = quotationRef
|
||||
}
|
||||
if clientPaymentRef := strings.TrimSpace(p.GetClientPaymentRef()); clientPaymentRef != "" {
|
||||
meta["clientPaymentRef"] = clientPaymentRef
|
||||
}
|
||||
if version := p.GetVersion(); version > 0 {
|
||||
meta["version"] = strconv.FormatUint(version, 10)
|
||||
}
|
||||
if len(meta) == 0 {
|
||||
return nil
|
||||
}
|
||||
return meta
|
||||
}
|
||||
|
||||
func timestampAsTime(ts *timestamppb.Timestamp) time.Time {
|
||||
if ts == nil {
|
||||
return time.Time{}
|
||||
}
|
||||
return ts.AsTime()
|
||||
}
|
||||
|
||||
func enumJSONName(value string) string {
|
||||
return strings.ToLower(strings.TrimSpace(value))
|
||||
}
|
||||
|
||||
@@ -4,18 +4,19 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/tech/sendico/pkg/api/http/response"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||
paginationv1 "github.com/tech/sendico/pkg/proto/common/pagination/v1"
|
||||
orchestratorv1 "github.com/tech/sendico/pkg/proto/payments/orchestration/v1"
|
||||
sharedv1 "github.com/tech/sendico/pkg/proto/payments/shared/v1"
|
||||
orchestrationv2 "github.com/tech/sendico/pkg/proto/payments/orchestration/v2"
|
||||
"github.com/tech/sendico/server/interface/api/sresponse"
|
||||
mutil "github.com/tech/sendico/server/internal/mutil/param"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
const maxInt32 = int64(1<<31 - 1)
|
||||
@@ -38,9 +39,7 @@ func (a *PaymentAPI) listPayments(r *http.Request, account *model.Account, token
|
||||
return response.AccessDenied(a.logger, a.Name(), "payments read permission denied")
|
||||
}
|
||||
|
||||
req := &orchestratorv1.ListPaymentsRequest{
|
||||
OrganizationRef: orgRef.Hex(),
|
||||
}
|
||||
req := &orchestrationv2.ListPaymentsRequest{Meta: requestMeta(orgRef.Hex(), "")}
|
||||
|
||||
if page, err := listPaymentsPage(r); err != nil {
|
||||
return response.Auto(a.logger, a.Name(), err)
|
||||
@@ -49,17 +48,33 @@ func (a *PaymentAPI) listPayments(r *http.Request, account *model.Account, token
|
||||
}
|
||||
|
||||
query := r.URL.Query()
|
||||
if sourceRef := strings.TrimSpace(query.Get("source_ref")); sourceRef != "" {
|
||||
req.SourceRef = sourceRef
|
||||
if quotationRef := firstNonEmpty(query.Get("quotation_ref"), query.Get("quote_ref")); quotationRef != "" {
|
||||
req.QuotationRef = quotationRef
|
||||
}
|
||||
if destinationRef := strings.TrimSpace(query.Get("destination_ref")); destinationRef != "" {
|
||||
req.DestinationRef = destinationRef
|
||||
createdFrom, err := parseRFC3339Timestamp(firstNonEmpty(query.Get("created_from"), query.Get("createdFrom")), "created_from")
|
||||
if err != nil {
|
||||
return response.Auto(a.logger, a.Name(), err)
|
||||
}
|
||||
if createdFrom != nil {
|
||||
req.CreatedFrom = createdFrom
|
||||
}
|
||||
createdTo, err := parseRFC3339Timestamp(firstNonEmpty(query.Get("created_to"), query.Get("createdTo")), "created_to")
|
||||
if err != nil {
|
||||
return response.Auto(a.logger, a.Name(), err)
|
||||
}
|
||||
if createdTo != nil {
|
||||
req.CreatedTo = createdTo
|
||||
}
|
||||
if req.GetCreatedFrom() != nil && req.GetCreatedTo() != nil {
|
||||
if !req.GetCreatedTo().AsTime().After(req.GetCreatedFrom().AsTime()) {
|
||||
return response.Auto(a.logger, a.Name(), merrors.InvalidArgument("created_to must be after created_from", "created_to"))
|
||||
}
|
||||
}
|
||||
|
||||
if states, err := parsePaymentStateFilters(r); err != nil {
|
||||
return response.Auto(a.logger, a.Name(), err)
|
||||
} else if len(states) > 0 {
|
||||
req.FilterStates = states
|
||||
req.States = states
|
||||
}
|
||||
|
||||
resp, err := a.execution.ListPayments(ctx, req)
|
||||
@@ -106,7 +121,7 @@ func listPaymentsPage(r *http.Request) (*paginationv1.CursorPageRequest, error)
|
||||
return page, nil
|
||||
}
|
||||
|
||||
func parsePaymentStateFilters(r *http.Request) ([]sharedv1.PaymentState, error) {
|
||||
func parsePaymentStateFilters(r *http.Request) ([]orchestrationv2.OrchestrationState, error) {
|
||||
query := r.URL.Query()
|
||||
values := append([]string{}, query["state"]...)
|
||||
values = append(values, query["states"]...)
|
||||
@@ -115,14 +130,14 @@ func parsePaymentStateFilters(r *http.Request) ([]sharedv1.PaymentState, error)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
states := make([]sharedv1.PaymentState, 0, len(values))
|
||||
states := make([]orchestrationv2.OrchestrationState, 0, len(values))
|
||||
for _, raw := range values {
|
||||
for _, part := range strings.Split(raw, ",") {
|
||||
trimmed := strings.TrimSpace(part)
|
||||
if trimmed == "" {
|
||||
continue
|
||||
}
|
||||
state, ok := paymentStateFromString(trimmed)
|
||||
state, ok := orchestrationStateFromString(trimmed)
|
||||
if !ok {
|
||||
return nil, merrors.InvalidArgument("unsupported payment state: "+trimmed, "state")
|
||||
}
|
||||
@@ -136,17 +151,49 @@ func parsePaymentStateFilters(r *http.Request) ([]sharedv1.PaymentState, error)
|
||||
return states, nil
|
||||
}
|
||||
|
||||
func paymentStateFromString(value string) (sharedv1.PaymentState, bool) {
|
||||
func orchestrationStateFromString(value string) (orchestrationv2.OrchestrationState, bool) {
|
||||
upper := strings.ToUpper(strings.TrimSpace(value))
|
||||
if upper == "" {
|
||||
return 0, false
|
||||
}
|
||||
if !strings.HasPrefix(upper, "PAYMENT_STATE_") {
|
||||
upper = "PAYMENT_STATE_" + upper
|
||||
switch upper {
|
||||
case "PAYMENT_STATE_ACCEPTED", "ACCEPTED":
|
||||
return orchestrationv2.OrchestrationState_ORCHESTRATION_STATE_CREATED, true
|
||||
case "PAYMENT_STATE_FUNDS_RESERVED", "FUNDS_RESERVED", "PAYMENT_STATE_SUBMITTED", "SUBMITTED":
|
||||
return orchestrationv2.OrchestrationState_ORCHESTRATION_STATE_EXECUTING, true
|
||||
case "PAYMENT_STATE_SETTLED", "SETTLED":
|
||||
return orchestrationv2.OrchestrationState_ORCHESTRATION_STATE_SETTLED, true
|
||||
case "PAYMENT_STATE_FAILED", "FAILED", "PAYMENT_STATE_CANCELLED", "CANCELLED":
|
||||
return orchestrationv2.OrchestrationState_ORCHESTRATION_STATE_FAILED, true
|
||||
}
|
||||
enumValue, ok := sharedv1.PaymentState_value[upper]
|
||||
if !strings.HasPrefix(upper, "ORCHESTRATION_STATE_") {
|
||||
upper = "ORCHESTRATION_STATE_" + upper
|
||||
}
|
||||
enumValue, ok := orchestrationv2.OrchestrationState_value[upper]
|
||||
if !ok {
|
||||
return 0, false
|
||||
}
|
||||
return sharedv1.PaymentState(enumValue), true
|
||||
return orchestrationv2.OrchestrationState(enumValue), true
|
||||
}
|
||||
|
||||
func firstNonEmpty(values ...string) string {
|
||||
for _, value := range values {
|
||||
trimmed := strings.TrimSpace(value)
|
||||
if trimmed != "" {
|
||||
return trimmed
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func parseRFC3339Timestamp(raw string, field string) (*timestamppb.Timestamp, error) {
|
||||
trimmed := strings.TrimSpace(raw)
|
||||
if trimmed == "" {
|
||||
return nil, nil
|
||||
}
|
||||
parsed, err := time.Parse(time.RFC3339, trimmed)
|
||||
if err != nil {
|
||||
return nil, merrors.InvalidArgument("invalid "+field+", expected RFC3339", field)
|
||||
}
|
||||
return timestamppb.New(parsed), nil
|
||||
}
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
package paymentapiimp
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
pkgmodel "github.com/tech/sendico/pkg/model"
|
||||
paymenttypes "github.com/tech/sendico/pkg/payments/types"
|
||||
fxv1 "github.com/tech/sendico/pkg/proto/common/fx/v1"
|
||||
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
|
||||
paymentv1 "github.com/tech/sendico/pkg/proto/common/payment/v1"
|
||||
chainv1 "github.com/tech/sendico/pkg/proto/gateway/chain/v1"
|
||||
sharedv1 "github.com/tech/sendico/pkg/proto/payments/shared/v1"
|
||||
endpointv1 "github.com/tech/sendico/pkg/proto/payments/endpoint/v1"
|
||||
quotationv2 "github.com/tech/sendico/pkg/proto/payments/quotation/v2"
|
||||
"github.com/tech/sendico/server/interface/api/srequest"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
)
|
||||
|
||||
func mapPaymentIntent(intent *srequest.PaymentIntent) (*sharedv1.PaymentIntent, error) {
|
||||
func mapQuoteIntent(intent *srequest.PaymentIntent) (*quotationv2.QuoteIntent, error) {
|
||||
if intent == nil {
|
||||
return nil, merrors.InvalidArgument("intent is required")
|
||||
}
|
||||
|
||||
kind, err := mapPaymentKind(intent.Kind)
|
||||
if err != nil {
|
||||
if err := validatePaymentKind(intent.Kind); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -33,33 +33,35 @@ func mapPaymentIntent(intent *srequest.PaymentIntent) (*sharedv1.PaymentIntent,
|
||||
settlementCurrency = resolveSettlementCurrency(intent)
|
||||
}
|
||||
|
||||
source, err := mapPaymentEndpoint(intent.Source, "source")
|
||||
source, err := mapQuoteEndpoint(intent.Source, "intent.source")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
destination, err := mapPaymentEndpoint(intent.Destination, "destination")
|
||||
destination, err := mapQuoteEndpoint(intent.Destination, "intent.destination")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fx, err := mapFXIntent(intent.FX)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &sharedv1.PaymentIntent{
|
||||
Ref: uuid.New().String(),
|
||||
Kind: kind,
|
||||
quoteIntent := "ationv2.QuoteIntent{
|
||||
Source: source,
|
||||
Destination: destination,
|
||||
Amount: mapMoney(intent.Amount),
|
||||
RequiresFx: fx != nil,
|
||||
Fx: fx,
|
||||
SettlementMode: settlementMode,
|
||||
SettlementCurrency: settlementCurrency,
|
||||
Attributes: copyStringMap(intent.Attributes),
|
||||
Customer: mapCustomer(intent.Customer),
|
||||
}, nil
|
||||
}
|
||||
if comment := strings.TrimSpace(intent.Attributes["comment"]); comment != "" {
|
||||
quoteIntent.Comment = comment
|
||||
}
|
||||
return quoteIntent, nil
|
||||
}
|
||||
|
||||
func validatePaymentKind(kind srequest.PaymentKind) error {
|
||||
switch strings.TrimSpace(string(kind)) {
|
||||
case string(srequest.PaymentKindPayout), string(srequest.PaymentKindInternalTransfer), string(srequest.PaymentKindFxConversion):
|
||||
return nil
|
||||
default:
|
||||
return merrors.InvalidArgument("unsupported payment kind: " + string(kind))
|
||||
}
|
||||
}
|
||||
|
||||
func resolveSettlementCurrency(intent *srequest.PaymentIntent) string {
|
||||
@@ -81,150 +83,147 @@ func resolveSettlementCurrency(intent *srequest.PaymentIntent) string {
|
||||
return quote
|
||||
}
|
||||
}
|
||||
if intent.Amount != nil {
|
||||
amountCurrency := strings.TrimSpace(intent.Amount.Currency)
|
||||
if amountCurrency != "" {
|
||||
switch {
|
||||
case strings.EqualFold(amountCurrency, base) && quote != "":
|
||||
return quote
|
||||
case strings.EqualFold(amountCurrency, quote) && base != "":
|
||||
return base
|
||||
default:
|
||||
return amountCurrency
|
||||
}
|
||||
}
|
||||
}
|
||||
if quote != "" {
|
||||
return quote
|
||||
}
|
||||
if base != "" {
|
||||
return base
|
||||
}
|
||||
}
|
||||
|
||||
if intent.Amount != nil {
|
||||
return strings.TrimSpace(intent.Amount.Currency)
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func mapPaymentEndpoint(endpoint *srequest.Endpoint, field string) (*sharedv1.PaymentEndpoint, error) {
|
||||
func mapQuoteEndpoint(endpoint *srequest.Endpoint, field string) (*endpointv1.PaymentEndpoint, error) {
|
||||
if endpoint == nil {
|
||||
return nil, nil
|
||||
return nil, merrors.InvalidArgument(field + " is required")
|
||||
}
|
||||
|
||||
var result sharedv1.PaymentEndpoint
|
||||
switch endpoint.Type {
|
||||
case srequest.EndpointTypeLedger:
|
||||
payload, err := endpoint.DecodeLedger()
|
||||
if err != nil {
|
||||
return nil, merrors.InvalidArgument(field + " endpoint: " + err.Error())
|
||||
return nil, merrors.InvalidArgument(field + ": " + err.Error())
|
||||
}
|
||||
result.Endpoint = &sharedv1.PaymentEndpoint_Ledger{
|
||||
Ledger: mapLedgerEndpoint(&payload),
|
||||
method := &ledgerMethodData{
|
||||
LedgerAccountRef: strings.TrimSpace(payload.LedgerAccountRef),
|
||||
ContraLedgerAccountRef: strings.TrimSpace(payload.ContraLedgerAccountRef),
|
||||
}
|
||||
if method.LedgerAccountRef == "" {
|
||||
return nil, merrors.InvalidArgument(field + ".ledger_account_ref is required")
|
||||
}
|
||||
return endpointFromMethod(endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_LEDGER, method)
|
||||
|
||||
case srequest.EndpointTypeManagedWallet:
|
||||
payload, err := endpoint.DecodeManagedWallet()
|
||||
if err != nil {
|
||||
return nil, merrors.InvalidArgument(field + " endpoint: " + err.Error())
|
||||
return nil, merrors.InvalidArgument(field + ": " + err.Error())
|
||||
}
|
||||
mw, err := mapManagedWalletEndpoint(&payload)
|
||||
method := &pkgmodel.WalletPaymentData{WalletID: strings.TrimSpace(payload.ManagedWalletRef)}
|
||||
if method.WalletID == "" {
|
||||
return nil, merrors.InvalidArgument(field + ".managed_wallet_ref is required")
|
||||
}
|
||||
return endpointFromMethod(endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_WALLET, method)
|
||||
|
||||
case srequest.EndpointTypeWallet:
|
||||
payload, err := endpoint.DecodeWallet()
|
||||
if err != nil {
|
||||
return nil, merrors.InvalidArgument(field + " endpoint: " + err.Error())
|
||||
return nil, merrors.InvalidArgument(field + ": " + err.Error())
|
||||
}
|
||||
result.Endpoint = &sharedv1.PaymentEndpoint_ManagedWallet{
|
||||
ManagedWallet: mw,
|
||||
method := &pkgmodel.WalletPaymentData{WalletID: strings.TrimSpace(payload.WalletID)}
|
||||
if method.WalletID == "" {
|
||||
return nil, merrors.InvalidArgument(field + ".walletId is required")
|
||||
}
|
||||
return endpointFromMethod(endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_WALLET, method)
|
||||
|
||||
case srequest.EndpointTypeExternalChain:
|
||||
payload, err := endpoint.DecodeExternalChain()
|
||||
if err != nil {
|
||||
return nil, merrors.InvalidArgument(field + " endpoint: " + err.Error())
|
||||
return nil, merrors.InvalidArgument(field + ": " + err.Error())
|
||||
}
|
||||
ext, err := mapExternalChainEndpoint(&payload)
|
||||
if err != nil {
|
||||
return nil, merrors.InvalidArgument(field + " endpoint: " + err.Error())
|
||||
}
|
||||
result.Endpoint = &sharedv1.PaymentEndpoint_ExternalChain{
|
||||
ExternalChain: ext,
|
||||
method, mapErr := mapExternalChainMethod(payload, field)
|
||||
if mapErr != nil {
|
||||
return nil, mapErr
|
||||
}
|
||||
return endpointFromMethod(endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_CRYPTO_ADDRESS, method)
|
||||
|
||||
case srequest.EndpointTypeCard:
|
||||
payload, err := endpoint.DecodeCard()
|
||||
if err != nil {
|
||||
return nil, merrors.InvalidArgument(field + " endpoint: " + err.Error())
|
||||
return nil, merrors.InvalidArgument(field + ": " + err.Error())
|
||||
}
|
||||
result.Endpoint = &sharedv1.PaymentEndpoint_Card{
|
||||
Card: mapCardEndpoint(&payload),
|
||||
method := &pkgmodel.CardPaymentData{
|
||||
Pan: strings.TrimSpace(payload.Pan),
|
||||
FirstName: strings.TrimSpace(payload.FirstName),
|
||||
LastName: strings.TrimSpace(payload.LastName),
|
||||
ExpMonth: uint32ToString(payload.ExpMonth),
|
||||
ExpYear: uint32ToString(payload.ExpYear),
|
||||
Country: strings.TrimSpace(payload.Country),
|
||||
}
|
||||
if method.Pan == "" {
|
||||
return nil, merrors.InvalidArgument(field + ".pan is required")
|
||||
}
|
||||
return endpointFromMethod(endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_CARD, method)
|
||||
|
||||
case srequest.EndpointTypeCardToken:
|
||||
payload, err := endpoint.DecodeCardToken()
|
||||
if err != nil {
|
||||
return nil, merrors.InvalidArgument(field + " endpoint: " + err.Error())
|
||||
return nil, merrors.InvalidArgument(field + ": " + err.Error())
|
||||
}
|
||||
result.Endpoint = &sharedv1.PaymentEndpoint_Card{
|
||||
Card: mapCardTokenEndpoint(&payload),
|
||||
method := &pkgmodel.TokenPaymentData{
|
||||
Token: strings.TrimSpace(payload.Token),
|
||||
Last4: strings.TrimSpace(payload.MaskedPan),
|
||||
}
|
||||
if method.Token == "" {
|
||||
return nil, merrors.InvalidArgument(field + ".token is required")
|
||||
}
|
||||
return endpointFromMethod(endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_CARD_TOKEN, method)
|
||||
|
||||
case "":
|
||||
return nil, merrors.InvalidArgument(field + " endpoint type is required")
|
||||
|
||||
default:
|
||||
return nil, merrors.InvalidArgument(field + " endpoint has unsupported type: " + string(endpoint.Type))
|
||||
}
|
||||
|
||||
result.Metadata = copyStringMap(endpoint.Metadata)
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func mapLedgerEndpoint(endpoint *srequest.LedgerEndpoint) *sharedv1.LedgerEndpoint {
|
||||
if endpoint == nil {
|
||||
return nil
|
||||
}
|
||||
return &sharedv1.LedgerEndpoint{
|
||||
LedgerAccountRef: endpoint.LedgerAccountRef,
|
||||
ContraLedgerAccountRef: endpoint.ContraLedgerAccountRef,
|
||||
return nil, merrors.InvalidArgument(field + " endpoint type is unsupported in v2: " + string(endpoint.Type))
|
||||
}
|
||||
}
|
||||
|
||||
func mapManagedWalletEndpoint(endpoint *srequest.ManagedWalletEndpoint) (*sharedv1.ManagedWalletEndpoint, error) {
|
||||
if endpoint == nil {
|
||||
return nil, nil
|
||||
func mapExternalChainMethod(payload srequest.ExternalChainEndpoint, field string) (*pkgmodel.CryptoAddressPaymentData, error) {
|
||||
address := strings.TrimSpace(payload.Address)
|
||||
if address == "" {
|
||||
return nil, merrors.InvalidArgument(field + ".address is required")
|
||||
}
|
||||
asset, err := mapAsset(endpoint.Asset)
|
||||
if payload.Asset == nil {
|
||||
return nil, merrors.InvalidArgument(field + ".asset is required")
|
||||
}
|
||||
token := strings.ToUpper(strings.TrimSpace(payload.Asset.TokenSymbol))
|
||||
if token == "" {
|
||||
return nil, merrors.InvalidArgument(field + ".asset.token_symbol is required")
|
||||
}
|
||||
if _, err := mapChainNetwork(payload.Asset.Chain); err != nil {
|
||||
return nil, merrors.InvalidArgument(field + ".asset.chain: " + err.Error())
|
||||
}
|
||||
|
||||
result := &pkgmodel.CryptoAddressPaymentData{
|
||||
Currency: pkgmodel.Currency(token),
|
||||
Address: address,
|
||||
Network: strings.ToUpper(strings.TrimSpace(string(payload.Asset.Chain))),
|
||||
}
|
||||
if memo := strings.TrimSpace(payload.Memo); memo != "" {
|
||||
result.DestinationTag = &memo
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func endpointFromMethod(methodType endpointv1.PaymentMethodType, data any) (*endpointv1.PaymentEndpoint, error) {
|
||||
raw, err := bson.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, merrors.InternalWrap(err, "failed to encode payment method data")
|
||||
}
|
||||
return &sharedv1.ManagedWalletEndpoint{
|
||||
ManagedWalletRef: endpoint.ManagedWalletRef,
|
||||
Asset: asset,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func mapExternalChainEndpoint(endpoint *srequest.ExternalChainEndpoint) (*sharedv1.ExternalChainEndpoint, error) {
|
||||
if endpoint == nil {
|
||||
return nil, nil
|
||||
method := &endpointv1.PaymentMethod{
|
||||
Type: methodType,
|
||||
Data: raw,
|
||||
}
|
||||
asset, err := mapAsset(endpoint.Asset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &sharedv1.ExternalChainEndpoint{
|
||||
Asset: asset,
|
||||
Address: endpoint.Address,
|
||||
Memo: endpoint.Memo,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func mapAsset(asset *srequest.Asset) (*chainv1.Asset, error) {
|
||||
if asset == nil {
|
||||
return nil, nil
|
||||
}
|
||||
chain, err := mapChainNetwork(asset.Chain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &chainv1.Asset{
|
||||
Chain: chain,
|
||||
TokenSymbol: asset.TokenSymbol,
|
||||
ContractAddress: asset.ContractAddress,
|
||||
return &endpointv1.PaymentEndpoint{
|
||||
Source: &endpointv1.PaymentEndpoint_PaymentMethod{
|
||||
PaymentMethod: method,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -238,94 +237,6 @@ func mapMoney(m *paymenttypes.Money) *moneyv1.Money {
|
||||
}
|
||||
}
|
||||
|
||||
func mapFXIntent(fx *srequest.FXIntent) (*sharedv1.FXIntent, error) {
|
||||
if fx == nil {
|
||||
return nil, nil
|
||||
}
|
||||
side, err := mapFXSide(fx.Side)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &sharedv1.FXIntent{
|
||||
Pair: mapCurrencyPair(fx.Pair),
|
||||
Side: side,
|
||||
Firm: fx.Firm,
|
||||
TtlMs: fx.TTLms,
|
||||
PreferredProvider: fx.PreferredProvider,
|
||||
MaxAgeMs: fx.MaxAgeMs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func mapCustomer(customer *srequest.Customer) *sharedv1.Customer {
|
||||
if customer == nil {
|
||||
return nil
|
||||
}
|
||||
return &sharedv1.Customer{
|
||||
Id: strings.TrimSpace(customer.ID),
|
||||
FirstName: strings.TrimSpace(customer.FirstName),
|
||||
MiddleName: strings.TrimSpace(customer.MiddleName),
|
||||
LastName: strings.TrimSpace(customer.LastName),
|
||||
Ip: strings.TrimSpace(customer.IP),
|
||||
Zip: strings.TrimSpace(customer.Zip),
|
||||
Country: strings.TrimSpace(customer.Country),
|
||||
State: strings.TrimSpace(customer.State),
|
||||
City: strings.TrimSpace(customer.City),
|
||||
Address: strings.TrimSpace(customer.Address),
|
||||
}
|
||||
}
|
||||
|
||||
func mapCurrencyPair(pair *srequest.CurrencyPair) *fxv1.CurrencyPair {
|
||||
if pair == nil {
|
||||
return nil
|
||||
}
|
||||
return &fxv1.CurrencyPair{
|
||||
Base: pair.Base,
|
||||
Quote: pair.Quote,
|
||||
}
|
||||
}
|
||||
|
||||
func mapCardEndpoint(card *srequest.CardEndpoint) *sharedv1.CardEndpoint {
|
||||
if card == nil {
|
||||
return nil
|
||||
}
|
||||
result := &sharedv1.CardEndpoint{
|
||||
CardholderName: strings.TrimSpace(card.FirstName),
|
||||
CardholderSurname: strings.TrimSpace(card.LastName),
|
||||
ExpMonth: card.ExpMonth,
|
||||
ExpYear: card.ExpYear,
|
||||
Country: strings.TrimSpace(card.Country),
|
||||
}
|
||||
if pan := strings.TrimSpace(card.Pan); pan != "" {
|
||||
result.Card = &sharedv1.CardEndpoint_Pan{Pan: pan}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func mapCardTokenEndpoint(card *srequest.CardTokenEndpoint) *sharedv1.CardEndpoint {
|
||||
if card == nil {
|
||||
return nil
|
||||
}
|
||||
return &sharedv1.CardEndpoint{
|
||||
Card: &sharedv1.CardEndpoint_Token{Token: strings.TrimSpace(card.Token)},
|
||||
MaskedPan: strings.TrimSpace(card.MaskedPan),
|
||||
}
|
||||
}
|
||||
|
||||
func mapPaymentKind(kind srequest.PaymentKind) (sharedv1.PaymentKind, error) {
|
||||
switch strings.TrimSpace(string(kind)) {
|
||||
case "", string(srequest.PaymentKindUnspecified):
|
||||
return sharedv1.PaymentKind_PAYMENT_KIND_UNSPECIFIED, nil
|
||||
case string(srequest.PaymentKindPayout):
|
||||
return sharedv1.PaymentKind_PAYMENT_KIND_PAYOUT, nil
|
||||
case string(srequest.PaymentKindInternalTransfer):
|
||||
return sharedv1.PaymentKind_PAYMENT_KIND_INTERNAL_TRANSFER, nil
|
||||
case string(srequest.PaymentKindFxConversion):
|
||||
return sharedv1.PaymentKind_PAYMENT_KIND_FX_CONVERSION, nil
|
||||
default:
|
||||
return sharedv1.PaymentKind_PAYMENT_KIND_UNSPECIFIED, merrors.InvalidArgument("unsupported payment kind: " + string(kind))
|
||||
}
|
||||
}
|
||||
|
||||
func mapSettlementMode(mode srequest.SettlementMode) (paymentv1.SettlementMode, error) {
|
||||
switch strings.TrimSpace(string(mode)) {
|
||||
case "", string(srequest.SettlementModeUnspecified):
|
||||
@@ -339,19 +250,6 @@ func mapSettlementMode(mode srequest.SettlementMode) (paymentv1.SettlementMode,
|
||||
}
|
||||
}
|
||||
|
||||
func mapFXSide(side srequest.FXSide) (fxv1.Side, error) {
|
||||
switch strings.TrimSpace(string(side)) {
|
||||
case "", string(srequest.FXSideUnspecified):
|
||||
return fxv1.Side_SIDE_UNSPECIFIED, nil
|
||||
case string(srequest.FXSideBuyBaseSellQuote):
|
||||
return fxv1.Side_BUY_BASE_SELL_QUOTE, nil
|
||||
case string(srequest.FXSideSellBaseBuyQuote):
|
||||
return fxv1.Side_SELL_BASE_BUY_QUOTE, nil
|
||||
default:
|
||||
return fxv1.Side_SIDE_UNSPECIFIED, merrors.InvalidArgument("unsupported fx side: " + string(side))
|
||||
}
|
||||
}
|
||||
|
||||
func mapChainNetwork(chain srequest.ChainNetwork) (chainv1.ChainNetwork, error) {
|
||||
switch strings.TrimSpace(string(chain)) {
|
||||
case "", string(srequest.ChainNetworkUnspecified):
|
||||
@@ -369,13 +267,14 @@ func mapChainNetwork(chain srequest.ChainNetwork) (chainv1.ChainNetwork, error)
|
||||
}
|
||||
}
|
||||
|
||||
func copyStringMap(src map[string]string) map[string]string {
|
||||
if len(src) == 0 {
|
||||
return nil
|
||||
func uint32ToString(v uint32) string {
|
||||
if v == 0 {
|
||||
return ""
|
||||
}
|
||||
dst := make(map[string]string, len(src))
|
||||
for k, v := range src {
|
||||
dst[k] = v
|
||||
}
|
||||
return dst
|
||||
return strconv.FormatUint(uint64(v), 10)
|
||||
}
|
||||
|
||||
type ledgerMethodData struct {
|
||||
LedgerAccountRef string `bson:"ledgerAccountRef"`
|
||||
ContraLedgerAccountRef string `bson:"contraLedgerAccountRef,omitempty"`
|
||||
}
|
||||
|
||||
@@ -9,7 +9,9 @@ import (
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||
orchestratorv1 "github.com/tech/sendico/pkg/proto/payments/orchestration/v1"
|
||||
tracev1 "github.com/tech/sendico/pkg/proto/common/trace/v1"
|
||||
orchestrationv2 "github.com/tech/sendico/pkg/proto/payments/orchestration/v2"
|
||||
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"
|
||||
"github.com/tech/sendico/server/interface/api/sresponse"
|
||||
@@ -58,26 +60,41 @@ func (a *PaymentAPI) initiatePayment(r *http.Request, account *model.Account, to
|
||||
}
|
||||
}
|
||||
|
||||
var intent *sharedv1.PaymentIntent
|
||||
quotationRef := strings.TrimSpace(payload.QuoteRef)
|
||||
intentRef := metadataValue(payload.Metadata, "intent_ref")
|
||||
if payload.Intent != nil {
|
||||
applyCustomerIP(payload.Intent, r.RemoteAddr)
|
||||
intent, err = mapPaymentIntent(payload.Intent)
|
||||
intent, err := mapQuoteIntent(payload.Intent)
|
||||
if err != nil {
|
||||
return response.BadPayload(a.logger, a.Name(), err)
|
||||
}
|
||||
quoteResp, qErr := a.quotation.QuotePayment(ctx, "ationv2.QuotePaymentRequest{
|
||||
Meta: requestMeta(orgRef.Hex(), payload.IdempotencyKey),
|
||||
IdempotencyKey: strings.TrimSpace(payload.IdempotencyKey),
|
||||
Intent: intent,
|
||||
InitiatorRef: initiatorRef(account),
|
||||
})
|
||||
if qErr != nil {
|
||||
a.logger.Warn("Failed to quote payment before execution", zap.Error(qErr), mzap.ObjRef("organization_ref", orgRef))
|
||||
return response.Auto(a.logger, a.Name(), qErr)
|
||||
}
|
||||
quotationRef = strings.TrimSpace(quoteResp.GetQuote().GetQuoteRef())
|
||||
if quotationRef == "" {
|
||||
return response.Auto(a.logger, a.Name(), merrors.DataConflict("quotation service returned empty quote_ref"))
|
||||
}
|
||||
if derived := strings.TrimSpace(quoteResp.GetQuote().GetIntentRef()); derived != "" {
|
||||
intentRef = derived
|
||||
}
|
||||
}
|
||||
|
||||
req := &orchestratorv1.InitiatePaymentRequest{
|
||||
Meta: &sharedv1.RequestMeta{
|
||||
OrganizationRef: orgRef.Hex(),
|
||||
},
|
||||
IdempotencyKey: strings.TrimSpace(payload.IdempotencyKey),
|
||||
Intent: intent,
|
||||
QuoteRef: strings.TrimSpace(payload.QuoteRef),
|
||||
Metadata: payload.Metadata,
|
||||
req := &orchestrationv2.ExecutePaymentRequest{
|
||||
Meta: requestMeta(orgRef.Hex(), payload.IdempotencyKey),
|
||||
QuotationRef: quotationRef,
|
||||
IntentRef: intentRef,
|
||||
ClientPaymentRef: metadataValue(payload.Metadata, "client_payment_ref"),
|
||||
}
|
||||
|
||||
resp, err := a.execution.InitiatePayment(ctx, req)
|
||||
resp, err := a.execution.ExecutePayment(ctx, req)
|
||||
if err != nil {
|
||||
a.logger.Warn("Failed to initiate payment", zap.Error(err), mzap.ObjRef("organization_ref", orgRef))
|
||||
return response.Auto(a.logger, a.Name(), err)
|
||||
@@ -101,3 +118,29 @@ func decodeInitiatePayload(r *http.Request) (*srequest.InitiatePayment, error) {
|
||||
}
|
||||
return payload, nil
|
||||
}
|
||||
|
||||
func requestMeta(organizationRef string, idempotencyKey string) *sharedv1.RequestMeta {
|
||||
return &sharedv1.RequestMeta{
|
||||
OrganizationRef: strings.TrimSpace(organizationRef),
|
||||
Trace: &tracev1.TraceContext{
|
||||
IdempotencyKey: strings.TrimSpace(idempotencyKey),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func metadataValue(meta map[string]string, key string) string {
|
||||
if len(meta) == 0 {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(meta[strings.TrimSpace(key)])
|
||||
}
|
||||
|
||||
func initiatorRef(account *model.Account) string {
|
||||
if account == nil {
|
||||
return ""
|
||||
}
|
||||
if account.ID != bson.NilObjectID {
|
||||
return account.ID.Hex()
|
||||
}
|
||||
return strings.TrimSpace(account.Login)
|
||||
}
|
||||
|
||||
@@ -8,8 +8,7 @@ import (
|
||||
"github.com/tech/sendico/pkg/api/http/response"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
orchestratorv1 "github.com/tech/sendico/pkg/proto/payments/orchestration/v1"
|
||||
sharedv1 "github.com/tech/sendico/pkg/proto/payments/shared/v1"
|
||||
orchestrationv2 "github.com/tech/sendico/pkg/proto/payments/orchestration/v2"
|
||||
"github.com/tech/sendico/server/interface/api/srequest"
|
||||
"github.com/tech/sendico/server/interface/api/sresponse"
|
||||
mutil "github.com/tech/sendico/server/internal/mutil/param"
|
||||
@@ -40,22 +39,24 @@ func (a *PaymentAPI) initiatePaymentsByQuote(r *http.Request, account *model.Acc
|
||||
return response.BadPayload(a.logger, a.Name(), err)
|
||||
}
|
||||
|
||||
req := &orchestratorv1.InitiatePaymentsRequest{
|
||||
Meta: &sharedv1.RequestMeta{
|
||||
OrganizationRef: orgRef.Hex(),
|
||||
},
|
||||
IdempotencyKey: strings.TrimSpace(payload.IdempotencyKey),
|
||||
QuoteRef: strings.TrimSpace(payload.QuoteRef),
|
||||
Metadata: payload.Metadata,
|
||||
req := &orchestrationv2.ExecutePaymentRequest{
|
||||
Meta: requestMeta(orgRef.Hex(), payload.IdempotencyKey),
|
||||
QuotationRef: strings.TrimSpace(payload.QuoteRef),
|
||||
IntentRef: metadataValue(payload.Metadata, "intent_ref"),
|
||||
ClientPaymentRef: metadataValue(payload.Metadata, "client_payment_ref"),
|
||||
}
|
||||
|
||||
resp, err := a.execution.InitiatePayments(ctx, req)
|
||||
resp, err := a.execution.ExecutePayment(ctx, req)
|
||||
if err != nil {
|
||||
a.logger.Warn("Failed to initiate batch payments", zap.Error(err), zap.String("organization_ref", orgRef.Hex()))
|
||||
return response.Auto(a.logger, a.Name(), err)
|
||||
}
|
||||
|
||||
return sresponse.PaymentsResponse(a.logger, resp.GetPayments(), token)
|
||||
payments := make([]*orchestrationv2.Payment, 0, 1)
|
||||
if payment := resp.GetPayment(); payment != nil {
|
||||
payments = append(payments, payment)
|
||||
}
|
||||
return sresponse.PaymentsResponse(a.logger, payments, token)
|
||||
}
|
||||
|
||||
func decodeInitiatePaymentsPayload(r *http.Request) (*srequest.InitiatePayments, error) {
|
||||
|
||||
@@ -8,8 +8,7 @@ import (
|
||||
"github.com/tech/sendico/pkg/api/http/response"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
quotationv1 "github.com/tech/sendico/pkg/proto/payments/quotation/v1"
|
||||
sharedv1 "github.com/tech/sendico/pkg/proto/payments/shared/v1"
|
||||
quotationv2 "github.com/tech/sendico/pkg/proto/payments/quotation/v2"
|
||||
"github.com/tech/sendico/server/interface/api/srequest"
|
||||
"github.com/tech/sendico/server/interface/api/sresponse"
|
||||
mutil "github.com/tech/sendico/server/internal/mutil/param"
|
||||
@@ -46,18 +45,18 @@ func (a *PaymentAPI) quotePayment(r *http.Request, account *model.Account, token
|
||||
}
|
||||
|
||||
applyCustomerIP(&payload.Intent, r.RemoteAddr)
|
||||
intent, err := mapPaymentIntent(&payload.Intent)
|
||||
intent, err := mapQuoteIntent(&payload.Intent)
|
||||
if err != nil {
|
||||
a.logger.Debug("Failed to map payment intent", zap.Error(err), mutil.PLog(a.oph, r))
|
||||
return response.BadPayload(a.logger, a.Name(), err)
|
||||
}
|
||||
|
||||
req := "ationv1.QuotePaymentRequest{
|
||||
Meta: &sharedv1.RequestMeta{
|
||||
OrganizationRef: orgRef.Hex(),
|
||||
},
|
||||
req := "ationv2.QuotePaymentRequest{
|
||||
Meta: requestMeta(orgRef.Hex(), payload.IdempotencyKey),
|
||||
IdempotencyKey: payload.IdempotencyKey,
|
||||
Intent: intent,
|
||||
PreviewOnly: payload.PreviewOnly,
|
||||
InitiatorRef: initiatorRef(account),
|
||||
}
|
||||
|
||||
resp, err := a.quotation.QuotePayment(ctx, req)
|
||||
@@ -97,10 +96,10 @@ func (a *PaymentAPI) quotePayments(r *http.Request, account *model.Account, toke
|
||||
return response.Auto(a.logger, a.Name(), err)
|
||||
}
|
||||
|
||||
intents := make([]*sharedv1.PaymentIntent, 0, len(payload.Intents))
|
||||
intents := make([]*quotationv2.QuoteIntent, 0, len(payload.Intents))
|
||||
for i := range payload.Intents {
|
||||
applyCustomerIP(&payload.Intents[i], r.RemoteAddr)
|
||||
intent, err := mapPaymentIntent(&payload.Intents[i])
|
||||
intent, err := mapQuoteIntent(&payload.Intents[i])
|
||||
if err != nil {
|
||||
a.logger.Debug("Failed to map payment intent", zap.Error(err), mutil.PLog(a.oph, r))
|
||||
return response.BadPayload(a.logger, a.Name(), err)
|
||||
@@ -108,13 +107,12 @@ func (a *PaymentAPI) quotePayments(r *http.Request, account *model.Account, toke
|
||||
intents = append(intents, intent)
|
||||
}
|
||||
|
||||
req := "ationv1.QuotePaymentsRequest{
|
||||
Meta: &sharedv1.RequestMeta{
|
||||
OrganizationRef: orgRef.Hex(),
|
||||
},
|
||||
req := "ationv2.QuotePaymentsRequest{
|
||||
Meta: requestMeta(orgRef.Hex(), payload.IdempotencyKey),
|
||||
IdempotencyKey: payload.IdempotencyKey,
|
||||
Intents: intents,
|
||||
PreviewOnly: payload.PreviewOnly,
|
||||
InitiatorRef: initiatorRef(account),
|
||||
}
|
||||
|
||||
resp, err := a.quotation.QuotePayments(ctx, req)
|
||||
|
||||
@@ -18,8 +18,8 @@ import (
|
||||
msgconsumer "github.com/tech/sendico/pkg/messaging/consumer"
|
||||
"github.com/tech/sendico/pkg/mlogger"
|
||||
"github.com/tech/sendico/pkg/mservice"
|
||||
orchestratorv1 "github.com/tech/sendico/pkg/proto/payments/orchestration/v1"
|
||||
quotationv1 "github.com/tech/sendico/pkg/proto/payments/quotation/v1"
|
||||
orchestrationv2 "github.com/tech/sendico/pkg/proto/payments/orchestration/v2"
|
||||
quotationv2 "github.com/tech/sendico/pkg/proto/payments/quotation/v2"
|
||||
eapi "github.com/tech/sendico/server/interface/api"
|
||||
mutil "github.com/tech/sendico/server/internal/mutil/param"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
@@ -30,15 +30,14 @@ import (
|
||||
)
|
||||
|
||||
type executionClient interface {
|
||||
InitiatePayments(ctx context.Context, req *orchestratorv1.InitiatePaymentsRequest) (*orchestratorv1.InitiatePaymentsResponse, error)
|
||||
InitiatePayment(ctx context.Context, req *orchestratorv1.InitiatePaymentRequest) (*orchestratorv1.InitiatePaymentResponse, error)
|
||||
ListPayments(ctx context.Context, req *orchestratorv1.ListPaymentsRequest) (*orchestratorv1.ListPaymentsResponse, error)
|
||||
ExecutePayment(ctx context.Context, req *orchestrationv2.ExecutePaymentRequest) (*orchestrationv2.ExecutePaymentResponse, error)
|
||||
ListPayments(ctx context.Context, req *orchestrationv2.ListPaymentsRequest) (*orchestrationv2.ListPaymentsResponse, error)
|
||||
Close() error
|
||||
}
|
||||
|
||||
type quotationClient interface {
|
||||
QuotePayment(ctx context.Context, req *quotationv1.QuotePaymentRequest) (*quotationv1.QuotePaymentResponse, error)
|
||||
QuotePayments(ctx context.Context, req *quotationv1.QuotePaymentsRequest) (*quotationv1.QuotePaymentsResponse, error)
|
||||
QuotePayment(ctx context.Context, req *quotationv2.QuotePaymentRequest) (*quotationv2.QuotePaymentResponse, error)
|
||||
QuotePayments(ctx context.Context, req *quotationv2.QuotePaymentsRequest) (*quotationv2.QuotePaymentsResponse, error)
|
||||
Close() error
|
||||
}
|
||||
|
||||
@@ -203,7 +202,7 @@ func (c *quotationClientConfig) setDefaults() {
|
||||
|
||||
type grpcQuotationClient struct {
|
||||
conn *grpc.ClientConn
|
||||
client quotationv1.QuotationServiceClient
|
||||
client quotationv2.QuotationServiceClient
|
||||
callTimeout time.Duration
|
||||
}
|
||||
|
||||
@@ -230,7 +229,7 @@ func newQuotationClient(ctx context.Context, cfg quotationClientConfig, opts ...
|
||||
}
|
||||
return &grpcQuotationClient{
|
||||
conn: conn,
|
||||
client: quotationv1.NewQuotationServiceClient(conn),
|
||||
client: quotationv2.NewQuotationServiceClient(conn),
|
||||
callTimeout: cfg.CallTimeout,
|
||||
}, nil
|
||||
}
|
||||
@@ -242,13 +241,13 @@ func (c *grpcQuotationClient) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
func (c *grpcQuotationClient) QuotePayment(ctx context.Context, req *quotationv1.QuotePaymentRequest) (*quotationv1.QuotePaymentResponse, error) {
|
||||
func (c *grpcQuotationClient) QuotePayment(ctx context.Context, req *quotationv2.QuotePaymentRequest) (*quotationv2.QuotePaymentResponse, error) {
|
||||
callCtx, cancel := c.callContext(ctx)
|
||||
defer cancel()
|
||||
return c.client.QuotePayment(callCtx, req)
|
||||
}
|
||||
|
||||
func (c *grpcQuotationClient) QuotePayments(ctx context.Context, req *quotationv1.QuotePaymentsRequest) (*quotationv1.QuotePaymentsResponse, error) {
|
||||
func (c *grpcQuotationClient) QuotePayments(ctx context.Context, req *quotationv2.QuotePaymentsRequest) (*quotationv2.QuotePaymentsResponse, error) {
|
||||
callCtx, cancel := c.callContext(ctx)
|
||||
defer cancel()
|
||||
return c.client.QuotePayments(callCtx, req)
|
||||
|
||||
Reference in New Issue
Block a user