Merge pull request 'Linting' (#509) from main into dis-474

Reviewed-on: #509
This commit was merged in pull request #509.
This commit is contained in:
2026-02-13 16:14:14 +00:00
220 changed files with 5632 additions and 1123 deletions

View File

@@ -12,6 +12,9 @@ when:
path: path:
include: include:
- api/server/** - api/server/**
- api/payments/methods/client/**
- api/payments/methods/go.mod
- api/payments/methods/go.sum
- api/proto/** - api/proto/**
- api/pkg/** - api/pkg/**
ignore_message: '[rebuild]' ignore_message: '[rebuild]'
@@ -41,6 +44,14 @@ steps:
- export PATH="$(go env GOPATH)/bin:$PATH" - export PATH="$(go env GOPATH)/bin:$PATH"
- bash ci/scripts/proto/generate.sh - bash ci/scripts/proto/generate.sh
- name: backend-tests
image: golang:alpine
depends_on: [ proto ]
commands:
- set -eu
- apk add --no-cache bash git build-base
- sh ci/scripts/common/run_backend_tests.sh bff
- name: secrets - name: secrets
image: alpine:latest image: alpine:latest
depends_on: [ version ] depends_on: [ version ]
@@ -61,7 +72,7 @@ steps:
- name: build-image - name: build-image
image: gcr.io/kaniko-project/executor:debug image: gcr.io/kaniko-project/executor:debug
depends_on: [ proto, secrets ] depends_on: [ backend-tests, secrets ]
commands: commands:
- sh ci/scripts/bff/build-image.sh - sh ci/scripts/bff/build-image.sh

View File

@@ -40,6 +40,14 @@ steps:
- export PATH="$(go env GOPATH)/bin:$PATH" - export PATH="$(go env GOPATH)/bin:$PATH"
- bash ci/scripts/proto/generate.sh - bash ci/scripts/proto/generate.sh
- name: backend-tests
image: golang:alpine
depends_on: [ proto ]
commands:
- set -eu
- apk add --no-cache bash git build-base
- sh ci/scripts/common/run_backend_tests.sh billing_documents
- name: secrets - name: secrets
image: alpine:latest image: alpine:latest
depends_on: [ version ] depends_on: [ version ]
@@ -60,7 +68,7 @@ steps:
- name: build-image - name: build-image
image: gcr.io/kaniko-project/executor:debug image: gcr.io/kaniko-project/executor:debug
depends_on: [ proto, secrets ] depends_on: [ backend-tests, secrets ]
commands: commands:
- sh ci/scripts/billing_documents/build-image.sh - sh ci/scripts/billing_documents/build-image.sh

View File

@@ -40,6 +40,14 @@ steps:
- export PATH="$(go env GOPATH)/bin:$PATH" - export PATH="$(go env GOPATH)/bin:$PATH"
- bash ci/scripts/proto/generate.sh - bash ci/scripts/proto/generate.sh
- name: backend-tests
image: golang:alpine
depends_on: [ proto ]
commands:
- set -eu
- apk add --no-cache bash git build-base
- sh ci/scripts/common/run_backend_tests.sh billing_fees
- name: secrets - name: secrets
image: alpine:latest image: alpine:latest
depends_on: [ version ] depends_on: [ version ]
@@ -60,7 +68,7 @@ steps:
- name: build-image - name: build-image
image: gcr.io/kaniko-project/executor:debug image: gcr.io/kaniko-project/executor:debug
depends_on: [ proto, secrets ] depends_on: [ backend-tests, secrets ]
commands: commands:
- sh ci/scripts/billing_fees/build-image.sh - sh ci/scripts/billing_fees/build-image.sh

View File

@@ -39,6 +39,14 @@ steps:
- export PATH="$(go env GOPATH)/bin:$PATH" - export PATH="$(go env GOPATH)/bin:$PATH"
- bash ci/scripts/proto/generate.sh - bash ci/scripts/proto/generate.sh
- name: backend-tests
image: golang:alpine
depends_on: [ proto ]
commands:
- set -eu
- apk add --no-cache bash git build-base
- sh ci/scripts/common/run_backend_tests.sh discovery
- name: secrets - name: secrets
image: alpine:latest image: alpine:latest
depends_on: [ version ] depends_on: [ version ]
@@ -59,7 +67,7 @@ steps:
- name: build-image - name: build-image
image: gcr.io/kaniko-project/executor:debug image: gcr.io/kaniko-project/executor:debug
depends_on: [ proto, secrets ] depends_on: [ backend-tests, secrets ]
commands: commands:
- sh ci/scripts/discovery/build-image.sh - sh ci/scripts/discovery/build-image.sh

View File

@@ -45,6 +45,14 @@ steps:
- export PATH="$(go env GOPATH)/bin:$PATH" - export PATH="$(go env GOPATH)/bin:$PATH"
- bash ci/scripts/proto/generate.sh - bash ci/scripts/proto/generate.sh
- name: backend-tests
image: golang:alpine
depends_on: [ proto ]
commands:
- set -eu
- apk add --no-cache bash git build-base
- sh ci/scripts/common/run_backend_tests.sh fx_ingestor
- name: secrets - name: secrets
image: alpine:latest image: alpine:latest
depends_on: [ version ] depends_on: [ version ]
@@ -65,7 +73,7 @@ steps:
- name: build-image - name: build-image
image: gcr.io/kaniko-project/executor:debug image: gcr.io/kaniko-project/executor:debug
depends_on: [ proto, secrets ] depends_on: [ backend-tests, secrets ]
commands: commands:
- sh ci/scripts/fx/build-image.sh - sh ci/scripts/fx/build-image.sh

View File

@@ -46,6 +46,14 @@ steps:
- export PATH="$(go env GOPATH)/bin:$PATH" - export PATH="$(go env GOPATH)/bin:$PATH"
- bash ci/scripts/proto/generate.sh - bash ci/scripts/proto/generate.sh
- name: backend-tests
image: golang:alpine
depends_on: [ proto ]
commands:
- set -eu
- apk add --no-cache bash git build-base
- sh ci/scripts/common/run_backend_tests.sh fx_oracle
- name: secrets - name: secrets
image: alpine:latest image: alpine:latest
depends_on: [ version ] depends_on: [ version ]
@@ -66,7 +74,7 @@ steps:
- name: build-image - name: build-image
image: gcr.io/kaniko-project/executor:debug image: gcr.io/kaniko-project/executor:debug
depends_on: [ proto, secrets ] depends_on: [ backend-tests, secrets ]
commands: commands:
- sh ci/scripts/fx/build-image.sh - sh ci/scripts/fx/build-image.sh

View File

@@ -43,6 +43,14 @@ steps:
- export PATH="$(go env GOPATH)/bin:$PATH" - export PATH="$(go env GOPATH)/bin:$PATH"
- bash ci/scripts/proto/generate.sh - bash ci/scripts/proto/generate.sh
- name: backend-tests
image: golang:alpine
depends_on: [ proto ]
commands:
- set -eu
- apk add --no-cache bash git build-base
- sh ci/scripts/common/run_backend_tests.sh gateway_chain
- name: secrets - name: secrets
image: alpine:latest image: alpine:latest
depends_on: [ version ] depends_on: [ version ]
@@ -63,7 +71,7 @@ steps:
- name: build-image - name: build-image
image: gcr.io/kaniko-project/executor:debug image: gcr.io/kaniko-project/executor:debug
depends_on: [ proto, secrets ] depends_on: [ backend-tests, secrets ]
commands: commands:
- sh ci/scripts/chain_gateway/build-image.sh - sh ci/scripts/chain_gateway/build-image.sh

View File

@@ -42,6 +42,14 @@ steps:
- export PATH="$(go env GOPATH)/bin:$PATH" - export PATH="$(go env GOPATH)/bin:$PATH"
- bash ci/scripts/proto/generate.sh - bash ci/scripts/proto/generate.sh
- name: backend-tests
image: golang:alpine
depends_on: [ proto ]
commands:
- set -eu
- apk add --no-cache bash git build-base
- sh ci/scripts/common/run_backend_tests.sh gateway_mntx
- name: secrets - name: secrets
image: alpine:latest image: alpine:latest
depends_on: [ version ] depends_on: [ version ]
@@ -62,7 +70,7 @@ steps:
- name: build-image - name: build-image
image: gcr.io/kaniko-project/executor:debug image: gcr.io/kaniko-project/executor:debug
depends_on: [ proto, secrets ] depends_on: [ backend-tests, secrets ]
commands: commands:
- sh ci/scripts/mntx/build-image.sh - sh ci/scripts/mntx/build-image.sh

View File

@@ -40,6 +40,14 @@ steps:
- export PATH="$(go env GOPATH)/bin:$PATH" - export PATH="$(go env GOPATH)/bin:$PATH"
- bash ci/scripts/proto/generate.sh - bash ci/scripts/proto/generate.sh
- name: backend-tests
image: golang:alpine
depends_on: [ proto ]
commands:
- set -eu
- apk add --no-cache bash git build-base
- sh ci/scripts/common/run_backend_tests.sh gateway_tgsettle
- name: secrets - name: secrets
image: alpine:latest image: alpine:latest
depends_on: [ version ] depends_on: [ version ]
@@ -60,7 +68,7 @@ steps:
- name: build-image - name: build-image
image: gcr.io/kaniko-project/executor:debug image: gcr.io/kaniko-project/executor:debug
depends_on: [ proto, secrets ] depends_on: [ backend-tests, secrets ]
commands: commands:
- sh ci/scripts/tgsettle/build-image.sh - sh ci/scripts/tgsettle/build-image.sh

View File

@@ -43,6 +43,14 @@ steps:
- export PATH="$(go env GOPATH)/bin:$PATH" - export PATH="$(go env GOPATH)/bin:$PATH"
- bash ci/scripts/proto/generate.sh - bash ci/scripts/proto/generate.sh
- name: backend-tests
image: golang:alpine
depends_on: [ proto ]
commands:
- set -eu
- apk add --no-cache bash git build-base
- sh ci/scripts/common/run_backend_tests.sh gateway_tron
- name: secrets - name: secrets
image: alpine:latest image: alpine:latest
depends_on: [ version ] depends_on: [ version ]
@@ -63,7 +71,7 @@ steps:
- name: build-image - name: build-image
image: gcr.io/kaniko-project/executor:debug image: gcr.io/kaniko-project/executor:debug
depends_on: [ proto, secrets ] depends_on: [ backend-tests, secrets ]
commands: commands:
- sh ci/scripts/tron_gateway/build-image.sh - sh ci/scripts/tron_gateway/build-image.sh

View File

@@ -40,6 +40,14 @@ steps:
- export PATH="$(go env GOPATH)/bin:$PATH" - export PATH="$(go env GOPATH)/bin:$PATH"
- bash ci/scripts/proto/generate.sh - bash ci/scripts/proto/generate.sh
- name: backend-tests
image: golang:alpine
depends_on: [ proto ]
commands:
- set -eu
- apk add --no-cache bash git build-base
- sh ci/scripts/common/run_backend_tests.sh ledger
- name: secrets - name: secrets
image: alpine:latest image: alpine:latest
depends_on: [ version ] depends_on: [ version ]
@@ -60,7 +68,7 @@ steps:
- name: build-image - name: build-image
image: gcr.io/kaniko-project/executor:debug image: gcr.io/kaniko-project/executor:debug
depends_on: [ proto, secrets ] depends_on: [ backend-tests, secrets ]
commands: commands:
- sh ci/scripts/ledger/build-image.sh - sh ci/scripts/ledger/build-image.sh

View File

@@ -43,6 +43,14 @@ steps:
- export PATH="$(go env GOPATH)/bin:$PATH" - export PATH="$(go env GOPATH)/bin:$PATH"
- bash ci/scripts/proto/generate.sh - bash ci/scripts/proto/generate.sh
- name: backend-tests
image: golang:alpine
depends_on: [ proto ]
commands:
- set -eu
- apk add --no-cache bash git build-base
- sh ci/scripts/common/run_backend_tests.sh notification
- name: secrets - name: secrets
image: alpine:latest image: alpine:latest
depends_on: [ version ] depends_on: [ version ]
@@ -63,7 +71,7 @@ steps:
- name: build-image - name: build-image
image: gcr.io/kaniko-project/executor:debug image: gcr.io/kaniko-project/executor:debug
depends_on: [ proto, secrets ] depends_on: [ backend-tests, secrets ]
commands: commands:
- sh ci/scripts/notification/build-image.sh - sh ci/scripts/notification/build-image.sh

View File

@@ -0,0 +1,87 @@
matrix:
include:
- PAYMENTS_METHODS_IMAGE_PATH: payments/methods
PAYMENTS_METHODS_DOCKERFILE: ci/prod/compose/payments_methods.dockerfile
PAYMENTS_METHODS_MONGO_SECRET_PATH: sendico/db
PAYMENTS_METHODS_ENV: prod
when:
- event: push
branch: main
path:
include:
- api/payments/methods/**
- api/proto/**
- api/pkg/**
ignore_message: '[rebuild]'
steps:
- name: version
image: alpine:latest
commands:
- set -euo pipefail 2>/dev/null || set -eu
- apk add --no-cache git
- GIT_REV="$(git rev-parse --short HEAD)"
- BUILD_BRANCH="$(git rev-parse --abbrev-ref HEAD)"
- APP_V="$(cat version)"
- BUILD_DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
- BUILD_USER="${WOODPECKER_MACHINE:-woodpecker}"
- printf "GIT_REV=%s\nBUILD_BRANCH=%s\nAPP_V=%s\nBUILD_DATE=%s\nBUILD_USER=%s\n" \
"$GIT_REV" "$BUILD_BRANCH" "$APP_V" "$BUILD_DATE" "$BUILD_USER" | tee .env.version
- name: proto
image: golang:alpine
depends_on: [ version ]
commands:
- set -eu
- apk add --no-cache bash git build-base protoc protobuf-dev
- go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
- go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
- export PATH="$(go env GOPATH)/bin:$PATH"
- bash ci/scripts/proto/generate.sh
- name: backend-tests
image: golang:alpine
depends_on: [ proto ]
commands:
- set -eu
- apk add --no-cache bash git build-base
- sh ci/scripts/common/run_backend_tests.sh payments_methods
- name: secrets
image: alpine:latest
depends_on: [ version ]
environment:
VAULT_ADDR: { from_secret: VAULT_ADDR }
VAULT_ROLE_ID: { from_secret: VAULT_APP_ROLE }
VAULT_SECRET_ID: { from_secret: VAULT_SECRET_ID }
commands:
- set -euo pipefail
- apk add --no-cache bash coreutils openssh-keygen curl sed python3
- mkdir -p secrets
- ./ci/vlt kv_to_file kv ops/deploy/ssh_key private_b64 secrets/SSH_KEY.b64 600
- base64 -d secrets/SSH_KEY.b64 > secrets/SSH_KEY
- chmod 600 secrets/SSH_KEY
- ssh-keygen -y -f secrets/SSH_KEY >/dev/null
- ./ci/vlt kv_get kv registry user > secrets/REGISTRY_USER
- ./ci/vlt kv_get kv registry password > secrets/REGISTRY_PASSWORD
- name: build-image
image: gcr.io/kaniko-project/executor:debug
depends_on: [ backend-tests, secrets ]
commands:
- sh ci/scripts/payments_methods/build-image.sh
- name: deploy
image: alpine:latest
depends_on: [ secrets, build-image ]
environment:
VAULT_ADDR: { from_secret: VAULT_ADDR }
VAULT_ROLE_ID: { from_secret: VAULT_APP_ROLE }
VAULT_SECRET_ID: { from_secret: VAULT_SECRET_ID }
commands:
- set -euo pipefail
- apk add --no-cache bash openssh-client rsync coreutils curl sed python3
- mkdir -p /root/.ssh
- install -m 600 secrets/SSH_KEY /root/.ssh/id_rsa
- sh ci/scripts/payments_methods/deploy.sh

View File

@@ -40,6 +40,14 @@ steps:
- export PATH="$(go env GOPATH)/bin:$PATH" - export PATH="$(go env GOPATH)/bin:$PATH"
- bash ci/scripts/proto/generate.sh - bash ci/scripts/proto/generate.sh
- name: backend-tests
image: golang:alpine
depends_on: [ proto ]
commands:
- set -eu
- apk add --no-cache bash git build-base
- sh ci/scripts/common/run_backend_tests.sh payments_orchestrator
- name: secrets - name: secrets
image: alpine:latest image: alpine:latest
depends_on: [ version ] depends_on: [ version ]
@@ -60,7 +68,7 @@ steps:
- name: build-image - name: build-image
image: gcr.io/kaniko-project/executor:debug image: gcr.io/kaniko-project/executor:debug
depends_on: [ proto, secrets ] depends_on: [ backend-tests, secrets ]
commands: commands:
- sh ci/scripts/payments_orchestrator/build-image.sh - sh ci/scripts/payments_orchestrator/build-image.sh

View File

@@ -42,6 +42,14 @@ steps:
- export PATH="$(go env GOPATH)/bin:$PATH" - export PATH="$(go env GOPATH)/bin:$PATH"
- bash ci/scripts/proto/generate.sh - bash ci/scripts/proto/generate.sh
- name: backend-tests
image: golang:alpine
depends_on: [ proto ]
commands:
- set -eu
- apk add --no-cache bash git build-base
- sh ci/scripts/common/run_backend_tests.sh payments_quotation
- name: secrets - name: secrets
image: alpine:latest image: alpine:latest
depends_on: [ version ] depends_on: [ version ]
@@ -62,7 +70,7 @@ steps:
- name: build-image - name: build-image
image: gcr.io/kaniko-project/executor:debug image: gcr.io/kaniko-project/executor:debug
depends_on: [ proto, secrets ] depends_on: [ backend-tests, secrets ]
commands: commands:
- sh ci/scripts/payments_quotation/build-image.sh - sh ci/scripts/payments_quotation/build-image.sh

View File

@@ -218,6 +218,8 @@ services-up:
dev-billing-documents \ dev-billing-documents \
dev-ledger \ dev-ledger \
dev-payments-orchestrator \ dev-payments-orchestrator \
dev-payments-quotation \
dev-payments-methods \
dev-chain-gateway \ dev-chain-gateway \
dev-tron-gateway \ dev-tron-gateway \
dev-mntx-gateway \ dev-mntx-gateway \
@@ -245,6 +247,8 @@ list-services:
@echo " - dev-billing-documents :50061, :9409 (Billing Documents)" @echo " - dev-billing-documents :50061, :9409 (Billing Documents)"
@echo " - dev-ledger :50052, :9401 (Double-Entry Ledger)" @echo " - dev-ledger :50052, :9401 (Double-Entry Ledger)"
@echo " - dev-payments-orchestrator :50062, :9403 (Payment Orchestration)" @echo " - dev-payments-orchestrator :50062, :9403 (Payment Orchestration)"
@echo " - dev-payments-quotation :50064, :9414 (Payment Quotation)"
@echo " - dev-payments-methods :50066, :9416 (Payment Methods)"
@echo " - dev-chain-gateway :50070, :9404 (EVM Blockchain Gateway)" @echo " - dev-chain-gateway :50070, :9404 (EVM Blockchain Gateway)"
@echo " - dev-tron-gateway :50071, :9408 (TRON Blockchain Gateway)" @echo " - dev-tron-gateway :50071, :9408 (TRON Blockchain Gateway)"
@echo " - dev-mntx-gateway :50075, :9405, :8084 (Card Payouts)" @echo " - dev-mntx-gateway :50075, :9405, :8084 (Card Payouts)"
@@ -273,7 +277,7 @@ build-fx:
build-payments: build-payments:
@echo "$(GREEN)Building payment services...$(NC)" @echo "$(GREEN)Building payment services...$(NC)"
@$(COMPOSE) build dev-payments-orchestrator @$(COMPOSE) build dev-payments-orchestrator dev-payments-quotation dev-payments-methods
build-gateways: build-gateways:
@echo "$(GREEN)Building gateway services...$(NC)" @echo "$(GREEN)Building gateway services...$(NC)"

View File

@@ -17,6 +17,7 @@ Financial services platform providing payment orchestration, ledger accounting,
| Ledger | `api/ledger/` | Double-entry accounting | | Ledger | `api/ledger/` | Double-entry accounting |
| Orchestrator | `api/payments/orchestrator/` | Payment orchestration | | Orchestrator | `api/payments/orchestrator/` | Payment orchestration |
| Quotation | `api/payments/quotation/` | Payment quotation | | Quotation | `api/payments/quotation/` | Payment quotation |
| Payment Methods | `api/payments/methods/` | Payment methods |
| Billing Fees | `api/billing/fees/` | Fee calculation | | Billing Fees | `api/billing/fees/` | Fee calculation |
| Billing Documents | `api/billing/documents/` | Billing documents | | Billing Documents | `api/billing/documents/` | Billing documents |
| FX Oracle | `api/fx/oracle/` | FX quote provider | | FX Oracle | `api/fx/oracle/` | FX quote provider |
@@ -24,7 +25,7 @@ Financial services platform providing payment orchestration, ledger accounting,
| Gateway Chain | `api/gateway/chain/` | EVM blockchain gateway | | Gateway Chain | `api/gateway/chain/` | EVM blockchain gateway |
| Gateway TRON | `api/gateway/tron/` | TRON blockchain gateway | | Gateway TRON | `api/gateway/tron/` | TRON blockchain gateway |
| Gateway MNTX | `api/gateway/mntx/` | Card payouts | | Gateway MNTX | `api/gateway/mntx/` | Card payouts |
| Gateway TGSettle | `api/gateway/tgsettle/` | Settlements | | Gateway TGSettle | `api/gateway/tgsettle/` | Telegram settlements with MNTX |
| Notification | `api/notification/` | Notifications | | Notification | `api/notification/` | Notifications |
| BFF | `api/server/` | Backend for frontend | | BFF | `api/server/` | Backend for frontend |
| Frontend | `frontend/pweb/` | Flutter web UI | | Frontend | `frontend/pweb/` | Flutter web UI |

View File

@@ -15,7 +15,7 @@ require (
github.com/tech/sendico/pkg v0.1.0 github.com/tech/sendico/pkg v0.1.0
go.mongodb.org/mongo-driver/v2 v2.5.0 go.mongodb.org/mongo-driver/v2 v2.5.0
go.uber.org/zap v1.27.1 go.uber.org/zap v1.27.1
google.golang.org/grpc v1.78.0 google.golang.org/grpc v1.79.1
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )

View File

@@ -201,16 +201,16 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
@@ -260,8 +260,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= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac= google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@@ -10,7 +10,7 @@ require (
github.com/tech/sendico/fx/oracle v0.0.0 github.com/tech/sendico/fx/oracle v0.0.0
github.com/tech/sendico/pkg v0.1.0 github.com/tech/sendico/pkg v0.1.0
go.uber.org/zap v1.27.1 go.uber.org/zap v1.27.1
google.golang.org/grpc v1.78.0 google.golang.org/grpc v1.79.1
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )

View File

@@ -152,16 +152,16 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
@@ -210,8 +210,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= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac= google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@@ -44,6 +44,6 @@ require (
golang.org/x/sys v0.41.0 // indirect golang.org/x/sys v0.41.0 // indirect
golang.org/x/text v0.34.0 // indirect golang.org/x/text v0.34.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect
google.golang.org/grpc v1.78.0 // indirect google.golang.org/grpc v1.79.1 // indirect
google.golang.org/protobuf v1.36.11 // indirect google.golang.org/protobuf v1.36.11 // indirect
) )

View File

@@ -152,16 +152,16 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
@@ -210,8 +210,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= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac= google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@@ -48,6 +48,6 @@ require (
golang.org/x/sys v0.41.0 // indirect golang.org/x/sys v0.41.0 // indirect
golang.org/x/text v0.34.0 // indirect golang.org/x/text v0.34.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect
google.golang.org/grpc v1.78.0 // indirect google.golang.org/grpc v1.79.1 // indirect
google.golang.org/protobuf v1.36.11 // indirect google.golang.org/protobuf v1.36.11 // indirect
) )

View File

@@ -152,16 +152,16 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
@@ -210,8 +210,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= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac= google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@@ -68,6 +68,7 @@ type Quote struct {
BaseAmount *moneyv1.Money BaseAmount *moneyv1.Money
QuoteAmount *moneyv1.Money QuoteAmount *moneyv1.Money
ExpiresAt time.Time ExpiresAt time.Time
PricedAt time.Time
Provider string Provider string
RateRef string RateRef string
Firm bool Firm bool
@@ -237,6 +238,10 @@ func fromProtoQuote(quote *oraclev1.Quote) *Quote {
if quote == nil { if quote == nil {
return nil return nil
} }
pricedAt := time.Time{}
if ts := quote.GetPricedAt(); ts != nil {
pricedAt = ts.AsTime()
}
return &Quote{ return &Quote{
QuoteRef: quote.GetQuoteRef(), QuoteRef: quote.GetQuoteRef(),
Pair: quote.Pair, Pair: quote.Pair,
@@ -245,6 +250,7 @@ func fromProtoQuote(quote *oraclev1.Quote) *Quote {
BaseAmount: quote.BaseAmount, BaseAmount: quote.BaseAmount,
QuoteAmount: quote.QuoteAmount, QuoteAmount: quote.QuoteAmount,
ExpiresAt: time.UnixMilli(quote.GetExpiresAtUnixMs()), ExpiresAt: time.UnixMilli(quote.GetExpiresAtUnixMs()),
PricedAt: pricedAt,
Provider: quote.GetProvider(), Provider: quote.GetProvider(),
RateRef: quote.GetRateRef(), RateRef: quote.GetRateRef(),
Firm: quote.GetFirm(), Firm: quote.GetFirm(),

View File

@@ -9,6 +9,7 @@ import (
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1" moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
oraclev1 "github.com/tech/sendico/pkg/proto/oracle/v1" oraclev1 "github.com/tech/sendico/pkg/proto/oracle/v1"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/protobuf/types/known/timestamppb"
) )
type stubOracle struct { type stubOracle struct {
@@ -75,6 +76,7 @@ func TestLatestRate(t *testing.T) {
func TestGetQuote(t *testing.T) { func TestGetQuote(t *testing.T) {
expiresAt := time.Date(2024, 2, 2, 12, 0, 0, 0, time.UTC) expiresAt := time.Date(2024, 2, 2, 12, 0, 0, 0, time.UTC)
pricedAt := time.Date(2024, 2, 2, 11, 59, 0, 0, time.UTC)
stub := &stubOracle{ stub := &stubOracle{
quoteResp: &oraclev1.GetQuoteResponse{ quoteResp: &oraclev1.GetQuoteResponse{
Quote: &oraclev1.Quote{ Quote: &oraclev1.Quote{
@@ -85,6 +87,7 @@ func TestGetQuote(t *testing.T) {
BaseAmount: &moneyv1.Money{Amount: "100.00", Currency: "GBP"}, BaseAmount: &moneyv1.Money{Amount: "100.00", Currency: "GBP"},
QuoteAmount: &moneyv1.Money{Amount: "125.00", Currency: "USD"}, QuoteAmount: &moneyv1.Money{Amount: "125.00", Currency: "USD"},
ExpiresAtUnixMs: expiresAt.UnixMilli(), ExpiresAtUnixMs: expiresAt.UnixMilli(),
PricedAt: timestamppb.New(pricedAt),
Provider: "Test", Provider: "Test",
RateRef: "test-ref", RateRef: "test-ref",
Firm: true, Firm: true,
@@ -113,4 +116,7 @@ func TestGetQuote(t *testing.T) {
if resp.QuoteRef != "quote-123" || resp.Price != "1.2500" || !resp.ExpiresAt.Equal(expiresAt) { if resp.QuoteRef != "quote-123" || resp.Price != "1.2500" || !resp.ExpiresAt.Equal(expiresAt) {
t.Fatalf("unexpected quote response: %+v", resp) t.Fatalf("unexpected quote response: %+v", resp)
} }
if !resp.PricedAt.Equal(pricedAt) {
t.Fatalf("expected priced_at %s, got %s", pricedAt, resp.PricedAt)
}
} }

View File

@@ -13,7 +13,7 @@ require (
github.com/tech/sendico/pkg v0.1.0 github.com/tech/sendico/pkg v0.1.0
go.mongodb.org/mongo-driver/v2 v2.5.0 go.mongodb.org/mongo-driver/v2 v2.5.0
go.uber.org/zap v1.27.1 go.uber.org/zap v1.27.1
google.golang.org/grpc v1.78.0 google.golang.org/grpc v1.79.1
google.golang.org/protobuf v1.36.11 google.golang.org/protobuf v1.36.11
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )

View File

@@ -152,16 +152,16 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
@@ -210,8 +210,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= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac= google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@@ -130,6 +130,10 @@ func (qc *quoteComputation) buildModelQuote(firm bool, expiryMillis int64, req *
if qc.baseRounded == nil || qc.quoteRounded == nil || qc.priceRounded == nil { if qc.baseRounded == nil || qc.quoteRounded == nil || qc.priceRounded == nil {
return nil, merrors.Internal("oracle: computation not executed") return nil, merrors.Internal("oracle: computation not executed")
} }
pricedAtUnixMs := qc.rate.AsOfUnixMs
if pricedAtUnixMs <= 0 {
pricedAtUnixMs = time.Now().UnixMilli()
}
quote := &model.Quote{ quote := &model.Quote{
QuoteRef: uuid.NewString(), QuoteRef: uuid.NewString(),
@@ -147,6 +151,7 @@ func (qc *quoteComputation) buildModelQuote(firm bool, expiryMillis int64, req *
Amount: formatRat(qc.quoteRounded, qc.quoteScale), Amount: formatRat(qc.quoteRounded, qc.quoteScale),
}, },
AmountType: qc.amountType, AmountType: qc.amountType,
PricedAtUnixMs: pricedAtUnixMs,
RateRef: qc.rate.RateRef, RateRef: qc.rate.RateRef,
Provider: qc.provider, Provider: qc.provider,
PreferredProvider: req.GetPreferredProvider(), PreferredProvider: req.GetPreferredProvider(),

View File

@@ -111,6 +111,7 @@ func (currencyStoreStub) List(ctx context.Context, codes ...string) ([]*model.Cu
func (currencyStoreStub) Upsert(ctx context.Context, currency *model.Currency) error { return nil } func (currencyStoreStub) Upsert(ctx context.Context, currency *model.Currency) error { return nil }
func TestServiceGetQuoteFirm(t *testing.T) { func TestServiceGetQuoteFirm(t *testing.T) {
pricedAt := time.Date(2024, 1, 2, 3, 4, 5, 0, time.UTC)
repo := &repositoryStub{} repo := &repositoryStub{}
repo.pairs = &pairStoreStub{ repo.pairs = &pairStoreStub{
getFn: func(ctx context.Context, pair model.CurrencyPair) (*model.Pair, error) { getFn: func(ctx context.Context, pair model.CurrencyPair) (*model.Pair, error) {
@@ -129,7 +130,7 @@ func TestServiceGetQuoteFirm(t *testing.T) {
Ask: "1.10", Ask: "1.10",
Bid: "1.08", Bid: "1.08",
RateRef: "rate#1", RateRef: "rate#1",
AsOfUnixMs: time.Now().UnixMilli(), AsOfUnixMs: pricedAt.UnixMilli(),
}, nil }, nil
}, },
} }
@@ -169,9 +170,15 @@ func TestServiceGetQuoteFirm(t *testing.T) {
if resp.GetQuote().GetQuoteAmount().GetAmount() != "110.00" { if resp.GetQuote().GetQuoteAmount().GetAmount() != "110.00" {
t.Fatalf("unexpected quote amount: %s", resp.GetQuote().GetQuoteAmount().GetAmount()) t.Fatalf("unexpected quote amount: %s", resp.GetQuote().GetQuoteAmount().GetAmount())
} }
if got := resp.GetQuote().GetPricedAt(); got == nil || !got.AsTime().Equal(pricedAt) {
t.Fatalf("expected priced_at %s, got %v", pricedAt, got)
}
if savedQuote.QuoteRef == "" { if savedQuote.QuoteRef == "" {
t.Fatalf("expected quote persisted") t.Fatalf("expected quote persisted")
} }
if savedQuote.PricedAtUnixMs != pricedAt.UnixMilli() {
t.Fatalf("expected stored pricedAtUnixMs %d, got %d", pricedAt.UnixMilli(), savedQuote.PricedAtUnixMs)
}
} }
func TestServiceGetQuoteRateNotFound(t *testing.T) { func TestServiceGetQuoteRateNotFound(t *testing.T) {

View File

@@ -2,12 +2,14 @@ package oracle
import ( import (
"strings" "strings"
"time"
"github.com/tech/sendico/fx/storage/model" "github.com/tech/sendico/fx/storage/model"
paymenttypes "github.com/tech/sendico/pkg/payments/types" paymenttypes "github.com/tech/sendico/pkg/payments/types"
fxv1 "github.com/tech/sendico/pkg/proto/common/fx/v1" fxv1 "github.com/tech/sendico/pkg/proto/common/fx/v1"
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1" moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
oraclev1 "github.com/tech/sendico/pkg/proto/oracle/v1" oraclev1 "github.com/tech/sendico/pkg/proto/oracle/v1"
"google.golang.org/protobuf/types/known/timestamppb"
) )
func buildResponseMeta(meta *oraclev1.RequestMeta) *oraclev1.ResponseMeta { func buildResponseMeta(meta *oraclev1.RequestMeta) *oraclev1.ResponseMeta {
@@ -36,6 +38,7 @@ func quoteModelToProto(q *model.Quote) *oraclev1.Quote {
BaseAmount: moneyModelToProto(&q.BaseAmount), BaseAmount: moneyModelToProto(&q.BaseAmount),
QuoteAmount: moneyModelToProto(&q.QuoteAmount), QuoteAmount: moneyModelToProto(&q.QuoteAmount),
ExpiresAtUnixMs: q.ExpiresAtUnixMs, ExpiresAtUnixMs: q.ExpiresAtUnixMs,
PricedAt: timestampFromUnixMillis(q.PricedAtUnixMs, q.CreatedAt),
Provider: q.Provider, Provider: q.Provider,
RateRef: q.RateRef, RateRef: q.RateRef,
Firm: q.Firm, Firm: q.Firm,
@@ -117,3 +120,13 @@ func decimalStringToProto(value string) *moneyv1.Decimal {
} }
return &moneyv1.Decimal{Value: value} return &moneyv1.Decimal{Value: value}
} }
func timestampFromUnixMillis(ms int64, fallback time.Time) *timestamppb.Timestamp {
if ms > 0 {
return timestamppb.New(time.UnixMilli(ms).UTC())
}
if !fallback.IsZero() {
return timestamppb.New(fallback.UTC())
}
return nil
}

View File

@@ -16,6 +16,8 @@ github.com/casbin/mongodb-adapter/v4 v4.3.0 h1:yYXky9v1by6vj/0QK7OyHyd/xpz4vzh0l
github.com/casbin/mongodb-adapter/v4 v4.3.0/go.mod h1:bOTSYZUjX7I9E0ExEvgq46m3mcDNRII7g8iWjrM1BHE= github.com/casbin/mongodb-adapter/v4 v4.3.0/go.mod h1:bOTSYZUjX7I9E0ExEvgq46m3mcDNRII7g8iWjrM1BHE=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
@@ -120,12 +122,12 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=

View File

@@ -21,6 +21,7 @@ type Quote struct {
QuoteAmount paymenttypes.Money `bson:"quoteAmount" json:"quoteAmount"` QuoteAmount paymenttypes.Money `bson:"quoteAmount" json:"quoteAmount"`
AmountType QuoteAmountType `bson:"amountType" json:"amountType"` AmountType QuoteAmountType `bson:"amountType" json:"amountType"`
ExpiresAtUnixMs int64 `bson:"expiresAtUnixMs" json:"expiresAtUnixMs"` ExpiresAtUnixMs int64 `bson:"expiresAtUnixMs" json:"expiresAtUnixMs"`
PricedAtUnixMs int64 `bson:"pricedAtUnixMs,omitempty" json:"pricedAtUnixMs,omitempty"`
ExpiresAt *time.Time `bson:"expiresAt,omitempty" json:"expiresAt,omitempty"` ExpiresAt *time.Time `bson:"expiresAt,omitempty" json:"expiresAt,omitempty"`
RateRef string `bson:"rateRef" json:"rateRef"` RateRef string `bson:"rateRef" json:"rateRef"`
Provider string `bson:"provider" json:"provider"` Provider string `bson:"provider" json:"provider"`

View File

@@ -95,6 +95,9 @@ func (q *quotesStore) Issue(ctx context.Context, quote *model.Quote) error {
expiry := time.UnixMilli(quote.ExpiresAtUnixMs) expiry := time.UnixMilli(quote.ExpiresAtUnixMs)
quote.ExpiresAt = &expiry quote.ExpiresAt = &expiry
} }
if quote.PricedAtUnixMs <= 0 {
quote.PricedAtUnixMs = time.Now().UnixMilli()
}
quote.Status = model.QuoteStatusIssued quote.Status = model.QuoteStatusIssued
quote.ConsumedByLedgerTxnRef = "" quote.ConsumedByLedgerTxnRef = ""

View File

@@ -32,6 +32,9 @@ func TestQuotesStoreIssue(t *testing.T) {
if inserted == nil || inserted.Status != model.QuoteStatusIssued { if inserted == nil || inserted.Status != model.QuoteStatusIssued {
t.Fatalf("expected issued quote to be inserted") t.Fatalf("expected issued quote to be inserted")
} }
if inserted.PricedAtUnixMs <= 0 {
t.Fatalf("expected pricedAtUnixMs to be populated")
}
} }
func TestQuotesStoreIssueSetsExpiryDate(t *testing.T) { func TestQuotesStoreIssueSetsExpiryDate(t *testing.T) {

View File

@@ -22,6 +22,7 @@ type repoStub struct {
findOneFn func(ctx context.Context, query builder.Query, result storable.Storable) error findOneFn func(ctx context.Context, query builder.Query, result storable.Storable) error
findManyFn func(ctx context.Context, query builder.Query, decoder rd.DecodingFunc) error findManyFn func(ctx context.Context, query builder.Query, decoder rd.DecodingFunc) error
updateFn func(ctx context.Context, obj storable.Storable) error updateFn func(ctx context.Context, obj storable.Storable) error
upsertFn func(ctx context.Context, obj storable.Storable) error
patchManyFn func(ctx context.Context, filter builder.Query, patch builder.Patch) (int, error) patchManyFn func(ctx context.Context, filter builder.Query, patch builder.Patch) (int, error)
createIdxFn func(def *ri.Definition) error createIdxFn func(def *ri.Definition) error
} }
@@ -69,6 +70,13 @@ func (r *repoStub) Update(ctx context.Context, obj storable.Storable) error {
return nil return nil
} }
func (r *repoStub) Upsert(ctx context.Context, obj storable.Storable) error {
if r.upsertFn != nil {
return r.upsertFn(ctx, obj)
}
return nil
}
func (r *repoStub) Patch(ctx context.Context, id bson.ObjectID, patch builder.Patch) error { func (r *repoStub) Patch(ctx context.Context, id bson.ObjectID, patch builder.Patch) error {
return merrors.NotImplemented("Patch not used") return merrors.NotImplemented("Patch not used")
} }

View File

@@ -2,22 +2,17 @@ package storage
import ( import (
"context" "context"
"errors"
"time" "time"
"github.com/tech/sendico/fx/storage/model" "github.com/tech/sendico/fx/storage/model"
) )
type storageError string
func (e storageError) Error() string {
return string(e)
}
var ( var (
ErrQuoteExpired = storageError("fx.storage: quote expired") ErrQuoteExpired = errors.New("fx.storage: quote expired")
ErrQuoteConsumed = storageError("fx.storage: quote consumed") ErrQuoteConsumed = errors.New("fx.storage: quote consumed")
ErrQuoteNotFirm = storageError("fx.storage: quote is not firm") ErrQuoteNotFirm = errors.New("fx.storage: quote is not firm")
ErrQuoteConsumptionRace = storageError("fx.storage: quote consumption collision") ErrQuoteConsumptionRace = errors.New("fx.storage: quote consumption collision")
) )
type Repository interface { type Repository interface {

View File

@@ -15,14 +15,14 @@ require (
github.com/tech/sendico/pkg v0.1.0 github.com/tech/sendico/pkg v0.1.0
go.mongodb.org/mongo-driver/v2 v2.5.0 go.mongodb.org/mongo-driver/v2 v2.5.0
go.uber.org/zap v1.27.1 go.uber.org/zap v1.27.1
google.golang.org/grpc v1.78.0 google.golang.org/grpc v1.79.1
google.golang.org/protobuf v1.36.11 google.golang.org/protobuf v1.36.11
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )
require ( require (
github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260212005555-3a7e5700f354 // indirect github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260213131322-086e44a26cf3 // indirect
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.24.4 // indirect github.com/bits-and-blooms/bitset v1.24.4 // indirect
github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect

View File

@@ -6,8 +6,8 @@ github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260212005555-3a7e5700f354 h1:BgaMXBpcqcW74afzqI3iKo07K3tC+VuyWU3/FIvLlNI= github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260213131322-086e44a26cf3 h1:QD30TjDPWtvXb5PBZGZ6Wdvaq7HQixIBtZ/yuseNXc8=
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260212005555-3a7e5700f354/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI= github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260213131322-086e44a26cf3/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0= github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0=
github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU= github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@@ -298,16 +298,16 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
@@ -362,8 +362,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= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac= google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@@ -11,7 +11,7 @@ require (
github.com/tech/sendico/pkg v0.1.0 github.com/tech/sendico/pkg v0.1.0
go.mongodb.org/mongo-driver/v2 v2.5.0 go.mongodb.org/mongo-driver/v2 v2.5.0
go.uber.org/zap v1.27.1 go.uber.org/zap v1.27.1
google.golang.org/grpc v1.78.0 google.golang.org/grpc v1.79.1
google.golang.org/protobuf v1.36.11 google.golang.org/protobuf v1.36.11
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )

View File

@@ -154,16 +154,16 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
@@ -212,8 +212,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= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac= google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@@ -8,7 +8,7 @@ require (
github.com/tech/sendico/pkg v0.1.0 github.com/tech/sendico/pkg v0.1.0
go.mongodb.org/mongo-driver/v2 v2.5.0 go.mongodb.org/mongo-driver/v2 v2.5.0
go.uber.org/zap v1.27.1 go.uber.org/zap v1.27.1
google.golang.org/grpc v1.78.0 google.golang.org/grpc v1.79.1
google.golang.org/protobuf v1.36.11 google.golang.org/protobuf v1.36.11
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )

View File

@@ -152,16 +152,16 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
@@ -210,8 +210,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= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac= google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@@ -17,14 +17,14 @@ require (
github.com/tech/sendico/pkg v0.1.0 github.com/tech/sendico/pkg v0.1.0
go.mongodb.org/mongo-driver/v2 v2.5.0 go.mongodb.org/mongo-driver/v2 v2.5.0
go.uber.org/zap v1.27.1 go.uber.org/zap v1.27.1
google.golang.org/grpc v1.78.0 google.golang.org/grpc v1.79.1
google.golang.org/protobuf v1.36.11 google.golang.org/protobuf v1.36.11
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )
require ( require (
github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260212005555-3a7e5700f354 // indirect github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260213131322-086e44a26cf3 // indirect
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.24.4 // indirect github.com/bits-and-blooms/bitset v1.24.4 // indirect
github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect

View File

@@ -6,8 +6,8 @@ github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260212005555-3a7e5700f354 h1:BgaMXBpcqcW74afzqI3iKo07K3tC+VuyWU3/FIvLlNI= github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260213131322-086e44a26cf3 h1:QD30TjDPWtvXb5PBZGZ6Wdvaq7HQixIBtZ/yuseNXc8=
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260212005555-3a7e5700f354/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI= github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260213131322-086e44a26cf3/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0= github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0=
github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU= github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@@ -313,16 +313,16 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
@@ -383,8 +383,8 @@ google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 h1:
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY= google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac= google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@@ -11,7 +11,7 @@ require (
github.com/tech/sendico/pkg v0.1.0 github.com/tech/sendico/pkg v0.1.0
go.mongodb.org/mongo-driver/v2 v2.5.0 go.mongodb.org/mongo-driver/v2 v2.5.0
go.uber.org/zap v1.27.1 go.uber.org/zap v1.27.1
google.golang.org/grpc v1.78.0 google.golang.org/grpc v1.79.1
google.golang.org/protobuf v1.36.11 google.golang.org/protobuf v1.36.11
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )

View File

@@ -154,16 +154,16 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
@@ -212,8 +212,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= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac= google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@@ -19,6 +19,7 @@ type repositoryStub struct {
InsertFunc func(ctx context.Context, object storable.Storable, filter builder.Query) error InsertFunc func(ctx context.Context, object storable.Storable, filter builder.Query) error
InsertManyFunc func(ctx context.Context, objects []storable.Storable) error InsertManyFunc func(ctx context.Context, objects []storable.Storable) error
UpdateFunc func(ctx context.Context, object storable.Storable) error UpdateFunc func(ctx context.Context, object storable.Storable) error
UpsertFunc func(ctx context.Context, object storable.Storable) error
DeleteFunc func(ctx context.Context, id bson.ObjectID) error DeleteFunc func(ctx context.Context, id bson.ObjectID) error
FindOneByFilterFunc func(ctx context.Context, filter builder.Query, result storable.Storable) error FindOneByFilterFunc func(ctx context.Context, filter builder.Query, result storable.Storable) error
FindManyByFilterFunc func(ctx context.Context, filter builder.Query, decoder rd.DecodingFunc) error FindManyByFilterFunc func(ctx context.Context, filter builder.Query, decoder rd.DecodingFunc) error
@@ -64,6 +65,13 @@ func (r *repositoryStub) Update(ctx context.Context, object storable.Storable) e
return nil return nil
} }
func (r *repositoryStub) Upsert(ctx context.Context, object storable.Storable) error {
if r.UpsertFunc != nil {
return r.UpsertFunc(ctx, object)
}
return nil
}
func (r *repositoryStub) Delete(ctx context.Context, id bson.ObjectID) error { func (r *repositoryStub) Delete(ctx context.Context, id bson.ObjectID) error {
if r.DeleteFunc != nil { if r.DeleteFunc != nil {
return r.DeleteFunc(ctx, id) return r.DeleteFunc(ctx, id)

View File

@@ -51,6 +51,6 @@ require (
golang.org/x/sync v0.19.0 // indirect golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.41.0 // indirect golang.org/x/sys v0.41.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect
google.golang.org/grpc v1.78.0 // indirect google.golang.org/grpc v1.79.1 // indirect
google.golang.org/protobuf v1.36.11 // indirect google.golang.org/protobuf v1.36.11 // indirect
) )

View File

@@ -167,16 +167,16 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
@@ -227,8 +227,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= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac= google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@@ -0,0 +1,46 @@
root = "."
testdata_dir = "testdata"
tmp_dir = "tmp"
[build]
args_bin = []
entrypoint = "./tmp/main"
cmd = "go build -o ./tmp/main ."
delay = 1000
exclude_dir = ["assets", "tmp", "vendor", "testdata"]
exclude_file = []
exclude_regex = ["_test.go", "_templ.go"]
exclude_unchanged = false
follow_symlink = false
full_bin = ""
include_dir = []
include_ext = ["go", "tpl", "tmpl", "html"]
include_file = []
kill_delay = "0s"
log = "build-errors.log"
poll = false
poll_interval = 0
post_cmd = []
pre_cmd = []
rerun = false
rerun_delay = 500
send_interrupt = false
stop_on_error = false
[color]
app = ""
build = "yellow"
main = "magenta"
runner = "green"
watcher = "cyan"
[log]
main_only = false
time = false
[misc]
clean_on_exit = false
[screen]
clear_on_rebuild = false
keep_scroll = true

View File

@@ -0,0 +1,131 @@
package client
import (
"context"
"crypto/tls"
"fmt"
"strings"
"time"
"github.com/tech/sendico/pkg/merrors"
methodsv1 "github.com/tech/sendico/pkg/proto/payments/methods/v1"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
)
// Client exposes typed helpers around the payment methods gRPC API.
type Client interface {
CreatePaymentMethod(ctx context.Context, req *methodsv1.CreatePaymentMethodRequest) (*methodsv1.CreatePaymentMethodResponse, error)
GetPaymentMethod(ctx context.Context, req *methodsv1.GetPaymentMethodRequest) (*methodsv1.GetPaymentMethodResponse, error)
UpdatePaymentMethod(ctx context.Context, req *methodsv1.UpdatePaymentMethodRequest) (*methodsv1.UpdatePaymentMethodResponse, error)
DeletePaymentMethod(ctx context.Context, req *methodsv1.DeletePaymentMethodRequest) (*methodsv1.DeletePaymentMethodResponse, error)
SetPaymentMethodArchived(ctx context.Context, req *methodsv1.SetPaymentMethodArchivedRequest) (*methodsv1.SetPaymentMethodArchivedResponse, error)
ListPaymentMethods(ctx context.Context, req *methodsv1.ListPaymentMethodsRequest) (*methodsv1.ListPaymentMethodsResponse, error)
Close() error
}
type grpcPaymentMethodsClient interface {
CreatePaymentMethod(ctx context.Context, in *methodsv1.CreatePaymentMethodRequest, opts ...grpc.CallOption) (*methodsv1.CreatePaymentMethodResponse, error)
GetPaymentMethod(ctx context.Context, in *methodsv1.GetPaymentMethodRequest, opts ...grpc.CallOption) (*methodsv1.GetPaymentMethodResponse, error)
UpdatePaymentMethod(ctx context.Context, in *methodsv1.UpdatePaymentMethodRequest, opts ...grpc.CallOption) (*methodsv1.UpdatePaymentMethodResponse, error)
DeletePaymentMethod(ctx context.Context, in *methodsv1.DeletePaymentMethodRequest, opts ...grpc.CallOption) (*methodsv1.DeletePaymentMethodResponse, error)
SetPaymentMethodArchived(ctx context.Context, in *methodsv1.SetPaymentMethodArchivedRequest, opts ...grpc.CallOption) (*methodsv1.SetPaymentMethodArchivedResponse, error)
ListPaymentMethods(ctx context.Context, in *methodsv1.ListPaymentMethodsRequest, opts ...grpc.CallOption) (*methodsv1.ListPaymentMethodsResponse, error)
}
type paymentMethodsClient struct {
cfg Config
conn *grpc.ClientConn
client grpcPaymentMethodsClient
}
// New dials the payment methods endpoint and returns a ready client.
func New(ctx context.Context, cfg Config, opts ...grpc.DialOption) (Client, error) {
cfg.setDefaults()
if strings.TrimSpace(cfg.Address) == "" {
return nil, merrors.InvalidArgument("payment-methods: address is required")
}
dialCtx, cancel := context.WithTimeout(ctx, cfg.DialTimeout)
defer cancel()
dialOpts := make([]grpc.DialOption, 0, len(opts)+1)
dialOpts = append(dialOpts, opts...)
if cfg.Insecure {
dialOpts = append(dialOpts, grpc.WithTransportCredentials(insecure.NewCredentials()))
} else {
dialOpts = append(dialOpts, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})))
}
conn, err := grpc.DialContext(dialCtx, cfg.Address, dialOpts...)
if err != nil {
return nil, merrors.InternalWrap(err, fmt.Sprintf("payment-methods: dial %s", cfg.Address))
}
return &paymentMethodsClient{
cfg: cfg,
conn: conn,
client: methodsv1.NewPaymentMethodsServiceClient(conn),
}, nil
}
// NewWithClient injects a pre-built payment methods client (useful for tests).
func NewWithClient(cfg Config, c grpcPaymentMethodsClient) Client {
cfg.setDefaults()
return &paymentMethodsClient{
cfg: cfg,
client: c,
}
}
func (c *paymentMethodsClient) Close() error {
if c.conn != nil {
return c.conn.Close()
}
return nil
}
func (c *paymentMethodsClient) callContext(ctx context.Context) (context.Context, context.CancelFunc) {
timeout := c.cfg.CallTimeout
if timeout <= 0 {
timeout = 3 * time.Second
}
return context.WithTimeout(ctx, timeout)
}
func (c *paymentMethodsClient) CreatePaymentMethod(ctx context.Context, req *methodsv1.CreatePaymentMethodRequest) (*methodsv1.CreatePaymentMethodResponse, error) {
callCtx, cancel := c.callContext(ctx)
defer cancel()
return c.client.CreatePaymentMethod(callCtx, req)
}
func (c *paymentMethodsClient) GetPaymentMethod(ctx context.Context, req *methodsv1.GetPaymentMethodRequest) (*methodsv1.GetPaymentMethodResponse, error) {
callCtx, cancel := c.callContext(ctx)
defer cancel()
return c.client.GetPaymentMethod(callCtx, req)
}
func (c *paymentMethodsClient) UpdatePaymentMethod(ctx context.Context, req *methodsv1.UpdatePaymentMethodRequest) (*methodsv1.UpdatePaymentMethodResponse, error) {
callCtx, cancel := c.callContext(ctx)
defer cancel()
return c.client.UpdatePaymentMethod(callCtx, req)
}
func (c *paymentMethodsClient) DeletePaymentMethod(ctx context.Context, req *methodsv1.DeletePaymentMethodRequest) (*methodsv1.DeletePaymentMethodResponse, error) {
callCtx, cancel := c.callContext(ctx)
defer cancel()
return c.client.DeletePaymentMethod(callCtx, req)
}
func (c *paymentMethodsClient) SetPaymentMethodArchived(ctx context.Context, req *methodsv1.SetPaymentMethodArchivedRequest) (*methodsv1.SetPaymentMethodArchivedResponse, error) {
callCtx, cancel := c.callContext(ctx)
defer cancel()
return c.client.SetPaymentMethodArchived(callCtx, req)
}
func (c *paymentMethodsClient) ListPaymentMethods(ctx context.Context, req *methodsv1.ListPaymentMethodsRequest) (*methodsv1.ListPaymentMethodsResponse, error) {
callCtx, cancel := c.callContext(ctx)
defer cancel()
return c.client.ListPaymentMethods(callCtx, req)
}

View File

@@ -0,0 +1,20 @@
package client
import "time"
// Config captures connection settings for the payment methods gRPC service.
type Config struct {
Address string
DialTimeout time.Duration
CallTimeout time.Duration
Insecure bool
}
func (c *Config) setDefaults() {
if c.DialTimeout <= 0 {
c.DialTimeout = 5 * time.Second
}
if c.CallTimeout <= 0 {
c.CallTimeout = 3 * time.Second
}
}

View File

@@ -0,0 +1,49 @@
runtime:
shutdown_timeout_seconds: 15
grpc:
network: tcp
address: ":50066"
advertise_host: "dev-payments-methods"
enable_reflection: true
enable_health: true
metrics:
address: ":9416"
messaging:
driver: NATS
settings:
url_env: NATS_URL
host_env: NATS_HOST
port_env: NATS_PORT
username_env: NATS_USER
password_env: NATS_PASSWORD
broker_name: Payments Methods Service
max_reconnects: 10
reconnect_wait: 5
buffer_size: 1024
database:
driver: mongodb
settings:
host_env: PAYMENTS_MONGO_HOST
port_env: PAYMENTS_MONGO_PORT
database_env: PAYMENTS_MONGO_DATABASE
user_env: PAYMENTS_MONGO_USER
password_env: PAYMENTS_MONGO_PASSWORD
auth_source_env: PAYMENTS_MONGO_AUTH_SOURCE
replica_set_env: PAYMENTS_MONGO_REPLICA_SET
permissions_database:
driver: mongodb
settings:
host_env: MONGO_HOST
port_env: MONGO_PORT
database_env: MONGO_DATABASE
user_env: MONGO_USER
password_env: MONGO_PASSWORD
auth_source_env: MONGO_AUTH_SOURCE
replica_set_env: MONGO_REPLICA_SET
enforcer:
driver: native

View File

@@ -0,0 +1,49 @@
runtime:
shutdown_timeout_seconds: 15
grpc:
network: tcp
address: ":50066"
advertise_host: "sendico_payments_methods"
enable_reflection: true
enable_health: true
metrics:
address: ":9416"
messaging:
driver: NATS
settings:
url_env: NATS_URL
host_env: NATS_HOST
port_env: NATS_PORT
username_env: NATS_USER
password_env: NATS_PASSWORD
broker_name: Payments Methods Service
max_reconnects: 10
reconnect_wait: 5
buffer_size: 1024
database:
driver: mongodb
settings:
host_env: PAYMENTS_MONGO_HOST
port_env: PAYMENTS_MONGO_PORT
database_env: PAYMENTS_MONGO_DATABASE
user_env: PAYMENTS_MONGO_USER
password_env: PAYMENTS_MONGO_PASSWORD
auth_source_env: PAYMENTS_MONGO_AUTH_SOURCE
replica_set_env: PAYMENTS_MONGO_REPLICA_SET
permissions_database:
driver: mongodb
settings:
host_env: MONGO_HOST
port_env: MONGO_PORT
database_env: MONGO_DATABASE
user_env: MONGO_USER
password_env: MONGO_PASSWORD
auth_source_env: MONGO_AUTH_SOURCE
replica_set_env: MONGO_REPLICA_SET
enforcer:
driver: native

1
api/payments/methods/env/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.env.api

View File

@@ -0,0 +1,52 @@
module github.com/tech/sendico/payments/methods
go 1.25.7
replace github.com/tech/sendico/pkg => ../../pkg
replace github.com/tech/sendico/payments/storage => ../storage
require (
github.com/tech/sendico/payments/storage v0.0.0-00010101000000-000000000000
github.com/tech/sendico/pkg v0.1.0
go.mongodb.org/mongo-driver/v2 v2.5.0
go.uber.org/zap v1.27.1
google.golang.org/grpc v1.79.1
google.golang.org/protobuf v1.36.11
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect
github.com/casbin/casbin/v2 v2.135.0 // indirect
github.com/casbin/govaluate v1.10.0 // indirect
github.com/casbin/mongodb-adapter/v4 v4.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/go-chi/chi/v5 v5.2.5 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/klauspost/compress v1.18.4 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/mapstructure v1.5.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/nkeys v0.4.15 // indirect
github.com/nats-io/nuid v1.0.1 // indirect
github.com/prometheus/client_golang v1.23.2 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.67.5 // indirect
github.com/prometheus/procfs v0.19.2 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.2.0 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
golang.org/x/crypto v0.48.0 // indirect
golang.org/x/net v0.50.0 // indirect
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-20260209200024-4cfbd4190f57 // indirect
)

221
api/payments/methods/go.sum Normal file
View File

@@ -0,0 +1,221 @@
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
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/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/bmatcuk/doublestar/v4 v4.10.0 h1:zU9WiOla1YA122oLM6i4EXvGW62DvKZVxIe6TYWexEs=
github.com/bmatcuk/doublestar/v4 v4.10.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/casbin/casbin/v2 v2.135.0 h1:6BLkMQiGotYyS5yYeWgW19vxqugUlvHFkFiLnLR/bxk=
github.com/casbin/casbin/v2 v2.135.0/go.mod h1:FmcfntdXLTcYXv/hxgNntcRPqAbwOG9xsism0yXT+18=
github.com/casbin/govaluate v1.3.0/go.mod h1:G/UnbIjZk/0uMNaLwZZmFQrR72tYRZWQkO70si/iR7A=
github.com/casbin/govaluate v1.10.0 h1:ffGw51/hYH3w3rZcxO/KcaUIDOLP84w7nsidMVgaDG0=
github.com/casbin/govaluate v1.10.0/go.mod h1:G/UnbIjZk/0uMNaLwZZmFQrR72tYRZWQkO70si/iR7A=
github.com/casbin/mongodb-adapter/v4 v4.3.0 h1:yYXky9v1by6vj/0QK7OyHyd/xpz4vzh0lCi7JKrS4qQ=
github.com/casbin/mongodb-adapter/v4 v4.3.0/go.mod h1:bOTSYZUjX7I9E0ExEvgq46m3mcDNRII7g8iWjrM1BHE=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA=
github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
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/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=
github.com/docker/docker v27.3.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/go-chi/chi/v5 v5.2.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug=
github.com/go-chi/chi/v5 v5.2.5/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c=
github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lufia/plan9stats v0.0.0-20250827001030-24949be3fa54 h1:mFWunSatvkQQDhpdyuFAYwyAan3hzCuma+Pz8sqvOfg=
github.com/lufia/plan9stats v0.0.0-20250827001030-24949be3fa54/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo=
github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/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/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=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/testcontainers/testcontainers-go v0.33.0 h1:zJS9PfXYT5O0ZFXM2xxXfk4J5UMw/kRiISng037Gxdw=
github.com/testcontainers/testcontainers-go v0.33.0/go.mod h1:W80YpTa8D5C3Yy16icheD01UTDu+LmXIA2Keo+jWtT8=
github.com/testcontainers/testcontainers-go/modules/mongodb v0.33.0 h1:iXVA84s5hKMS5gn01GWOYHE3ymy/2b+0YkpFeTxB2XY=
github.com/testcontainers/testcontainers-go/modules/mongodb v0.33.0/go.mod h1:R6tMjTojRiaoo89fh/hf7tOmfzohdqSU17R9DwSVSog=
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.2.0 h1:bYKF2AEwG5rqd1BumT4gAnvwU/M9nBp2pTSxeZw7Wvs=
github.com/xdg-go/scram v1.2.0/go.mod h1:3dlrS0iBaWKYVt2ZfA4cj48umJZ+cAEbR6/SjLA88I8=
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF3xaE=
go.mongodb.org/mongo-driver/v2 v2.5.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
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=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -0,0 +1,28 @@
package appversion
import (
"github.com/tech/sendico/pkg/version"
vf "github.com/tech/sendico/pkg/version/factory"
)
// Build information populated via ldflags.
var (
Version string
Revision string
Branch string
BuildUser string
BuildDate string
)
// Create returns a printer configured for the payment methods service.
func Create() version.Printer {
vi := version.Info{
Program: "Sendico Payment Methods Service",
Revision: Revision,
Branch: Branch,
BuildUser: BuildUser,
BuildDate: BuildDate,
Version: Version,
}
return vf.Create(&vi)
}

View File

@@ -0,0 +1,62 @@
package serverimp
import (
"os"
"strings"
"github.com/tech/sendico/pkg/api/routers"
"github.com/tech/sendico/pkg/db"
"github.com/tech/sendico/pkg/server/grpcapp"
"go.uber.org/zap"
"gopkg.in/yaml.v3"
)
type config struct {
*grpcapp.Config `yaml:",inline"`
// PermissionsDatabase points to the authorization store (policies/roles/assignments).
// If omitted, startup falls back to Database for backward compatibility.
PermissionsDatabase *db.Config `yaml:"permissions_database"`
}
func (i *Imp) loadConfig() (*config, error) {
data, err := os.ReadFile(i.file)
if err != nil {
i.logger.Error("Could not read configuration file", zap.String("config_file", i.file), zap.Error(err))
return nil, err
}
cfg := &config{Config: &grpcapp.Config{}}
if err := yaml.Unmarshal(data, cfg); err != nil {
i.logger.Error("Failed to parse configuration", zap.Error(err))
return nil, err
}
if cfg.Runtime == nil {
cfg.Runtime = &grpcapp.RuntimeConfig{ShutdownTimeoutSeconds: 15}
}
if cfg.GRPC == nil {
cfg.GRPC = &routers.GRPCConfig{
Network: "tcp",
Address: ":50066",
EnableReflection: true,
EnableHealth: true,
}
} else {
if strings.TrimSpace(cfg.GRPC.Address) == "" {
cfg.GRPC.Address = ":50066"
}
if strings.TrimSpace(cfg.GRPC.Network) == "" {
cfg.GRPC.Network = "tcp"
}
}
if cfg.Metrics == nil {
cfg.Metrics = &grpcapp.MetricsConfig{Address: ":9416"}
} else if strings.TrimSpace(cfg.Metrics.Address) == "" {
cfg.Metrics.Address = ":9416"
}
return cfg, nil
}

View File

@@ -0,0 +1,88 @@
package serverimp
import (
"strings"
"github.com/tech/sendico/payments/methods/internal/appversion"
"github.com/tech/sendico/pkg/discovery"
msg "github.com/tech/sendico/pkg/messaging"
"go.uber.org/zap"
)
const methodsDiscoverySender = "payment_methods"
func (i *Imp) initDiscovery(cfg *config) {
if i == nil || cfg == nil || cfg.Messaging == nil || cfg.Messaging.Driver == "" {
return
}
logger := i.logger.Named("discovery")
broker, err := msg.CreateMessagingBroker(logger.Named("bus"), cfg.Messaging)
if err != nil {
i.logger.Warn("Failed to initialise discovery broker", zap.Error(err))
return
}
registry := discovery.NewRegistry()
watcher, err := discovery.NewRegistryWatcher(logger, broker, registry)
if err != nil {
i.logger.Warn("Failed to initialise discovery registry watcher", zap.Error(err))
return
}
if err := watcher.Start(); err != nil {
i.logger.Warn("Failed to start discovery registry watcher", zap.Error(err))
return
}
i.discoveryWatcher = watcher
i.discoveryReg = registry
i.logger.Info("Discovery registry watcher started")
}
func (i *Imp) startDiscoveryAnnouncer(cfg *config, producer msg.Producer) {
if i == nil || cfg == nil || producer == nil || cfg.GRPC == nil {
return
}
invokeURI := strings.TrimSpace(cfg.GRPC.DiscoveryInvokeURI())
if invokeURI == "" {
i.logger.Warn("Skipping discovery announcement: missing advertise host/port in gRPC config")
return
}
announce := discovery.Announcement{
Service: "PAYMENTS_METHODS",
Operations: []string{
"payment_methods.manage",
"payment_methods.read",
},
InvokeURI: invokeURI,
Version: appversion.Create().Short(),
}
i.discoveryAnnouncer = discovery.NewAnnouncer(i.logger, producer, methodsDiscoverySender, announce)
i.discoveryAnnouncer.Start()
i.logger.Info("Discovery announcer started",
zap.String("service", announce.Service),
zap.String("invoke_uri", announce.InvokeURI))
}
func (i *Imp) stopDiscoveryAnnouncer() {
if i == nil || i.discoveryAnnouncer == nil {
return
}
i.discoveryAnnouncer.Stop()
i.discoveryAnnouncer = nil
}
func (i *Imp) stopDiscovery() {
if i == nil {
return
}
i.stopDiscoveryAnnouncer()
if i.discoveryWatcher != nil {
i.discoveryWatcher.Stop()
i.discoveryWatcher = nil
}
i.discoveryReg = nil
}

View File

@@ -0,0 +1,16 @@
package serverimp
import "context"
func (i *Imp) shutdownApp() {
if i == nil || i.app == nil {
return
}
timeout := i.config.Runtime.ShutdownTimeout()
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
i.app.Shutdown(ctx)
i.app = nil
}

View File

@@ -0,0 +1,117 @@
package serverimp
import (
"context"
"github.com/tech/sendico/payments/methods/internal/service/methods"
"github.com/tech/sendico/payments/storage"
mongostorage "github.com/tech/sendico/payments/storage/mongo"
"github.com/tech/sendico/pkg/db"
"github.com/tech/sendico/pkg/merrors"
msg "github.com/tech/sendico/pkg/messaging"
mb "github.com/tech/sendico/pkg/messaging/broker"
"github.com/tech/sendico/pkg/mlogger"
"github.com/tech/sendico/pkg/mservice"
"github.com/tech/sendico/pkg/server/grpcapp"
"go.uber.org/zap"
)
func Create(logger mlogger.Logger, file string, debug bool) (*Imp, error) {
return &Imp{
logger: logger.Named("server"),
file: file,
debug: debug,
}, nil
}
func (i *Imp) Shutdown() {
i.stopDiscovery()
if i.service != nil {
i.service.Shutdown()
}
i.shutdownApp()
if i.dbFactory != nil {
i.dbFactory.CloseConnection()
i.dbFactory = nil
}
}
func (i *Imp) Start() error {
cfg, err := i.loadConfig()
if err != nil {
return err
}
i.config = cfg
i.initDiscovery(cfg)
if cfg.Database == nil {
return merrors.InvalidArgument("database configuration is required")
}
permissionsDB := cfg.PermissionsDatabase
if permissionsDB == nil {
i.logger.Info("permissions_database is not configured, falling back to database settings")
permissionsDB = cfg.Database
}
i.dbFactory, err = db.NewConnection(i.logger, permissionsDB)
if err != nil {
return err
}
policy, err := i.dbFactory.Permissions().GetPolicyDescription(context.Background(), mservice.PaymentMethods)
if err != nil {
i.dbFactory.CloseConnection()
i.dbFactory = nil
return err
}
var broker mb.Broker
if cfg.Messaging != nil && cfg.Messaging.Driver != "" {
broker, err = msg.CreateMessagingBroker(i.logger, cfg.Messaging)
if err != nil {
i.logger.Warn("Failed to create recipient notifications broker", zap.Error(err))
}
}
repoFactory := func(logger mlogger.Logger, conn *db.MongoConnection) (storage.Repository, error) {
return mongostorage.New(
logger,
conn,
mongostorage.WithPaymentMethodsAuth(i.dbFactory.Permissions().Enforcer(), policy.ID),
)
}
serviceFactory := func(logger mlogger.Logger, repo storage.Repository, producer msg.Producer) (grpcapp.Service, error) {
opts := []methods.Option{}
if broker != nil {
opts = append(opts, methods.WithRecipientEventsBroker(broker))
}
i.startDiscoveryAnnouncer(cfg, producer)
svc, err := methods.NewService(logger, repo, opts...)
if err != nil {
return nil, err
}
i.service = svc
return svc, nil
}
app, err := grpcapp.NewApp(i.logger, "payments_methods", cfg.Config, i.debug, repoFactory, serviceFactory)
if err != nil {
i.dbFactory.CloseConnection()
i.dbFactory = nil
return err
}
i.app = app
if err := i.app.Start(); err != nil {
if i.dbFactory != nil {
i.dbFactory.CloseConnection()
i.dbFactory = nil
}
return err
}
return nil
}

View File

@@ -0,0 +1,29 @@
package serverimp
import (
"github.com/tech/sendico/payments/storage"
"github.com/tech/sendico/pkg/db"
"github.com/tech/sendico/pkg/discovery"
"github.com/tech/sendico/pkg/mlogger"
"github.com/tech/sendico/pkg/server/grpcapp"
)
type methodsService interface {
grpcapp.Service
Shutdown()
}
type Imp struct {
logger mlogger.Logger
file string
debug bool
config *config
app *grpcapp.App[storage.Repository]
service methodsService
dbFactory db.Factory
discoveryWatcher *discovery.RegistryWatcher
discoveryReg *discovery.Registry
discoveryAnnouncer *discovery.Announcer
}

View File

@@ -0,0 +1,12 @@
package server
import (
serverimp "github.com/tech/sendico/payments/methods/internal/server/internal"
"github.com/tech/sendico/pkg/mlogger"
"github.com/tech/sendico/pkg/server"
)
// Create initialises the payment methods server implementation.
func Create(logger mlogger.Logger, file string, debug bool) (server.Application, error) {
return serverimp.Create(logger, file, debug)
}

View File

@@ -0,0 +1,36 @@
package methods
import (
"context"
"github.com/tech/sendico/pkg/merrors"
methodsv1 "github.com/tech/sendico/pkg/proto/payments/methods/v1"
)
func (s *Service) SetPaymentMethodArchived(ctx context.Context, req *methodsv1.SetPaymentMethodArchivedRequest) (*methodsv1.SetPaymentMethodArchivedResponse, error) {
if req == nil {
return autoError[methodsv1.SetPaymentMethodArchivedResponse](ctx, s.logger, merrors.InvalidArgument("request is required"))
}
if s.pmstore == nil {
return autoError[methodsv1.SetPaymentMethodArchivedResponse](ctx, s.logger, errStoreUnavailable)
}
accountRef, err := parseObjectID(req.GetAccountRef(), "account_ref")
if err != nil {
return autoError[methodsv1.SetPaymentMethodArchivedResponse](ctx, s.logger, err)
}
organizationRef, err := parseObjectID(req.GetOrganizationRef(), "organization_ref")
if err != nil {
return autoError[methodsv1.SetPaymentMethodArchivedResponse](ctx, s.logger, err)
}
methodRef, err := parseObjectID(req.GetPaymentMethodRef(), "payment_method_ref")
if err != nil {
return autoError[methodsv1.SetPaymentMethodArchivedResponse](ctx, s.logger, err)
}
if err := s.pmstore.SetArchived(ctx, accountRef, organizationRef, methodRef, req.GetArchived(), req.GetCascade()); err != nil {
return autoError[methodsv1.SetPaymentMethodArchivedResponse](ctx, s.logger, err)
}
return &methodsv1.SetPaymentMethodArchivedResponse{}, nil
}

View File

@@ -0,0 +1,41 @@
package methods
import (
"context"
"github.com/tech/sendico/pkg/merrors"
methodsv1 "github.com/tech/sendico/pkg/proto/payments/methods/v1"
)
func (s *Service) CreatePaymentMethod(ctx context.Context, req *methodsv1.CreatePaymentMethodRequest) (*methodsv1.CreatePaymentMethodResponse, error) {
if req == nil {
return autoError[methodsv1.CreatePaymentMethodResponse](ctx, s.logger, merrors.InvalidArgument("request is required"))
}
if s.pmstore == nil {
return autoError[methodsv1.CreatePaymentMethodResponse](ctx, s.logger, errStoreUnavailable)
}
accountRef, err := parseObjectID(req.GetAccountRef(), "account_ref")
if err != nil {
return autoError[methodsv1.CreatePaymentMethodResponse](ctx, s.logger, err)
}
organizationRef, err := parseObjectID(req.GetOrganizationRef(), "organization_ref")
if err != nil {
return autoError[methodsv1.CreatePaymentMethodResponse](ctx, s.logger, err)
}
pm, err := decodePaymentMethodPayload(req.GetPaymentMethod(), "payment_method")
if err != nil {
return autoError[methodsv1.CreatePaymentMethodResponse](ctx, s.logger, err)
}
if err := s.pmstore.Create(ctx, accountRef, organizationRef, pm); err != nil {
return autoError[methodsv1.CreatePaymentMethodResponse](ctx, s.logger, err)
}
record, err := encodePaymentMethodRecord(pm)
if err != nil {
return autoError[methodsv1.CreatePaymentMethodResponse](ctx, s.logger, err)
}
return &methodsv1.CreatePaymentMethodResponse{PaymentMethodRecord: record}, nil
}

View File

@@ -0,0 +1,37 @@
package methods
import (
"context"
"github.com/tech/sendico/pkg/merrors"
methodsv1 "github.com/tech/sendico/pkg/proto/payments/methods/v1"
)
func (s *Service) DeletePaymentMethod(ctx context.Context, req *methodsv1.DeletePaymentMethodRequest) (*methodsv1.DeletePaymentMethodResponse, error) {
if req == nil {
return autoError[methodsv1.DeletePaymentMethodResponse](ctx, s.logger, merrors.InvalidArgument("request is required"))
}
if s.pmstore == nil {
return autoError[methodsv1.DeletePaymentMethodResponse](ctx, s.logger, errStoreUnavailable)
}
accountRef, err := parseObjectID(req.GetAccountRef(), "account_ref")
if err != nil {
return autoError[methodsv1.DeletePaymentMethodResponse](ctx, s.logger, err)
}
methodRef, err := parseObjectID(req.GetPaymentMethodRef(), "payment_method_ref")
if err != nil {
return autoError[methodsv1.DeletePaymentMethodResponse](ctx, s.logger, err)
}
if req.GetCascade() {
err = s.pmstore.DeleteCascade(ctx, accountRef, methodRef)
} else {
err = s.pmstore.Delete(ctx, accountRef, methodRef)
}
if err != nil {
return autoError[methodsv1.DeletePaymentMethodResponse](ctx, s.logger, err)
}
return &methodsv1.DeletePaymentMethodResponse{}, nil
}

View File

@@ -0,0 +1,38 @@
package methods
import (
"context"
"github.com/tech/sendico/pkg/merrors"
methodsv1 "github.com/tech/sendico/pkg/proto/payments/methods/v1"
)
func (s *Service) GetPaymentMethod(ctx context.Context, req *methodsv1.GetPaymentMethodRequest) (*methodsv1.GetPaymentMethodResponse, error) {
if req == nil {
return autoError[methodsv1.GetPaymentMethodResponse](ctx, s.logger, merrors.InvalidArgument("request is required"))
}
if s.pmstore == nil {
return autoError[methodsv1.GetPaymentMethodResponse](ctx, s.logger, errStoreUnavailable)
}
accountRef, err := parseObjectID(req.GetAccountRef(), "account_ref")
if err != nil {
return autoError[methodsv1.GetPaymentMethodResponse](ctx, s.logger, err)
}
methodRef, err := parseObjectID(req.GetPaymentMethodRef(), "payment_method_ref")
if err != nil {
return autoError[methodsv1.GetPaymentMethodResponse](ctx, s.logger, err)
}
pm, err := s.pmstore.Get(ctx, accountRef, methodRef)
if err != nil {
return autoError[methodsv1.GetPaymentMethodResponse](ctx, s.logger, err)
}
record, err := encodePaymentMethodRecord(pm)
if err != nil {
return autoError[methodsv1.GetPaymentMethodResponse](ctx, s.logger, err)
}
return &methodsv1.GetPaymentMethodResponse{PaymentMethodRecord: record}, nil
}

View File

@@ -0,0 +1,49 @@
package methods
import (
"context"
"github.com/tech/sendico/pkg/merrors"
endpointv1 "github.com/tech/sendico/pkg/proto/payments/endpoint/v1"
methodsv1 "github.com/tech/sendico/pkg/proto/payments/methods/v1"
)
func (s *Service) ListPaymentMethods(ctx context.Context, req *methodsv1.ListPaymentMethodsRequest) (*methodsv1.ListPaymentMethodsResponse, error) {
if req == nil {
return autoError[methodsv1.ListPaymentMethodsResponse](ctx, s.logger, merrors.InvalidArgument("request is required"))
}
if s.pmstore == nil {
return autoError[methodsv1.ListPaymentMethodsResponse](ctx, s.logger, errStoreUnavailable)
}
accountRef, err := parseObjectID(req.GetAccountRef(), "account_ref")
if err != nil {
return autoError[methodsv1.ListPaymentMethodsResponse](ctx, s.logger, err)
}
organizationRef, err := parseObjectID(req.GetOrganizationRef(), "organization_ref")
if err != nil {
return autoError[methodsv1.ListPaymentMethodsResponse](ctx, s.logger, err)
}
recipientRef, err := parseObjectID(req.GetRecipientRef(), "recipient_ref")
if err != nil {
return autoError[methodsv1.ListPaymentMethodsResponse](ctx, s.logger, err)
}
items, err := s.pmstore.List(ctx, accountRef, organizationRef, recipientRef, toModelCursor(req.GetCursor()))
if err != nil {
return autoError[methodsv1.ListPaymentMethodsResponse](ctx, s.logger, err)
}
result := make([]*endpointv1.PaymentMethodRecord, 0, len(items))
for i := range items {
record, err := encodePaymentMethodRecord(&items[i])
if err != nil {
return autoError[methodsv1.ListPaymentMethodsResponse](ctx, s.logger, err)
}
result = append(result, record)
}
return &methodsv1.ListPaymentMethodsResponse{
PaymentMethods: result,
}, nil
}

View File

@@ -0,0 +1,87 @@
package methods
import (
"context"
cons "github.com/tech/sendico/pkg/messaging/consumer"
objectnotifications "github.com/tech/sendico/pkg/messaging/notifications/object"
np "github.com/tech/sendico/pkg/messaging/notifications/processor"
nm "github.com/tech/sendico/pkg/model/notification"
"github.com/tech/sendico/pkg/mservice"
"go.mongodb.org/mongo-driver/v2/bson"
"go.uber.org/zap"
)
func (s *Service) startRecipientConsumers() {
if s == nil || s.recipientBroker == nil {
s.logger.Warn("Missing broker. Recipient cascade consumers have NOT started")
return
}
s.consumeRecipientProcessor(
objectnotifications.NewObjectChangedMessageProcessor(s.logger, mservice.Recipients, nm.NAArchived, s.onRecipientNotification),
)
s.consumeRecipientProcessor(
objectnotifications.NewObjectChangedMessageProcessor(s.logger, mservice.Recipients, nm.NADeleted, s.onRecipientNotification),
)
s.logger.Info("Recipient cascade consumers started")
}
func (s *Service) consumeRecipientProcessor(processor np.EnvelopeProcessor) {
consumer, err := cons.NewConsumer(s.logger, s.recipientBroker, processor.GetSubject())
if err != nil {
s.logger.Warn("Failed to create recipient consumer", zap.Error(err), zap.String("event", processor.GetSubject().ToString()))
return
}
s.recipientConsumers = append(s.recipientConsumers, consumer)
go func() {
if err := consumer.ConsumeMessages(processor.Process); err != nil {
s.logger.Warn("Recipient consumer stopped", zap.Error(err), zap.String("event", processor.GetSubject().ToString()))
}
}()
}
func (s *Service) onRecipientNotification(
ctx context.Context,
objectType mservice.Type,
recipientRef, actorAccountRef bson.ObjectID,
action nm.NotificationAction,
) error {
if s.pmstore == nil {
return errStoreUnavailable
}
if objectType != mservice.Recipients || recipientRef == bson.NilObjectID {
return nil
}
switch action {
case nm.NAArchived:
updated, err := s.pmstore.SetArchivedByRecipient(ctx, recipientRef, true)
if err != nil {
s.logger.Warn("Failed to cascade archive payment methods by recipient",
zap.Error(err),
zap.String("recipient_ref", recipientRef.Hex()),
zap.String("actor_account_ref", actorAccountRef.Hex()))
return err
}
s.logger.Info("Recipient archive cascade applied to payment methods",
zap.String("recipient_ref", recipientRef.Hex()),
zap.String("actor_account_ref", actorAccountRef.Hex()),
zap.Int("updated_count", updated))
case nm.NADeleted:
if err := s.pmstore.DeleteByRecipient(ctx, recipientRef); err != nil {
s.logger.Warn("Failed to cascade delete payment methods by recipient",
zap.Error(err),
zap.String("recipient_ref", recipientRef.Hex()),
zap.String("actor_account_ref", actorAccountRef.Hex()))
return err
}
s.logger.Info("Recipient delete cascade applied to payment methods",
zap.String("recipient_ref", recipientRef.Hex()),
zap.String("actor_account_ref", actorAccountRef.Hex()))
}
return nil
}

View File

@@ -0,0 +1,90 @@
package methods
import (
"github.com/tech/sendico/payments/storage"
"github.com/tech/sendico/pkg/api/routers"
"github.com/tech/sendico/pkg/merrors"
msg "github.com/tech/sendico/pkg/messaging"
mb "github.com/tech/sendico/pkg/messaging/broker"
"github.com/tech/sendico/pkg/mlogger"
methodsv1 "github.com/tech/sendico/pkg/proto/payments/methods/v1"
"google.golang.org/grpc"
)
var errStoreUnavailable = merrors.Internal("payment-methods: storage is not initialised")
// Option configures service dependencies.
type Option func(*Service)
// WithRecipientEventsBroker wires the broker used to consume recipient events.
func WithRecipientEventsBroker(broker mb.Broker) Option {
return func(s *Service) {
if broker != nil {
s.recipientBroker = broker
}
}
}
// Service implements payments.methods.v1.PaymentMethodsService.
type Service struct {
logger mlogger.Logger
storage storage.Repository
pmstore storage.PaymentMethodsStore
recipientBroker mb.Broker
recipientConsumers []msg.Consumer
methodsv1.UnimplementedPaymentMethodsServiceServer
}
// NewService creates a payment methods gRPC service.
func NewService(logger mlogger.Logger, repo storage.Repository, opts ...Option) (*Service, error) {
if logger == nil {
return nil, merrors.InvalidArgument("payment-methods: logger is required")
}
if repo == nil {
return nil, merrors.InvalidArgument("payment-methods: storage repository is required")
}
pmstore := repo.PaymentMethods()
if pmstore == nil {
return nil, errStoreUnavailable
}
svc := &Service{
logger: logger.Named("payment_methods"),
storage: repo,
pmstore: pmstore,
}
for _, opt := range opts {
if opt != nil {
opt(svc)
}
}
svc.startRecipientConsumers()
return svc, nil
}
// Register attaches the service to the supplied gRPC router.
func (s *Service) Register(router routers.GRPC) error {
return router.Register(func(reg grpc.ServiceRegistrar) {
methodsv1.RegisterPaymentMethodsServiceServer(reg, s)
})
}
// Shutdown releases underlying resources.
func (s *Service) Shutdown() {
if s == nil {
return
}
for _, consumer := range s.recipientConsumers {
if consumer != nil {
consumer.Close()
}
}
s.recipientConsumers = nil
s.pmstore = nil
s.storage = nil
}

View File

@@ -0,0 +1,37 @@
package methods
import (
"context"
"github.com/tech/sendico/pkg/merrors"
methodsv1 "github.com/tech/sendico/pkg/proto/payments/methods/v1"
)
func (s *Service) UpdatePaymentMethod(ctx context.Context, req *methodsv1.UpdatePaymentMethodRequest) (*methodsv1.UpdatePaymentMethodResponse, error) {
if req == nil {
return autoError[methodsv1.UpdatePaymentMethodResponse](ctx, s.logger, merrors.InvalidArgument("request is required"))
}
if s.pmstore == nil {
return autoError[methodsv1.UpdatePaymentMethodResponse](ctx, s.logger, errStoreUnavailable)
}
accountRef, err := parseObjectID(req.GetAccountRef(), "account_ref")
if err != nil {
return autoError[methodsv1.UpdatePaymentMethodResponse](ctx, s.logger, err)
}
pm, err := decodePaymentMethodRecord(req.GetPaymentMethodRecord())
if err != nil {
return autoError[methodsv1.UpdatePaymentMethodResponse](ctx, s.logger, err)
}
if err := s.pmstore.Update(ctx, accountRef, pm); err != nil {
return autoError[methodsv1.UpdatePaymentMethodResponse](ctx, s.logger, err)
}
record, err := encodePaymentMethodRecord(pm)
if err != nil {
return autoError[methodsv1.UpdatePaymentMethodResponse](ctx, s.logger, err)
}
return &methodsv1.UpdatePaymentMethodResponse{PaymentMethodRecord: record}, nil
}

View File

@@ -0,0 +1,292 @@
package methods
import (
"context"
"fmt"
"strings"
"time"
"github.com/tech/sendico/pkg/api/routers/gsresponse"
"github.com/tech/sendico/pkg/merrors"
"github.com/tech/sendico/pkg/mlogger"
"github.com/tech/sendico/pkg/model"
"github.com/tech/sendico/pkg/mservice"
archivablev1 "github.com/tech/sendico/pkg/proto/common/archivable/v1"
describablev1 "github.com/tech/sendico/pkg/proto/common/describable/v1"
oboundv1 "github.com/tech/sendico/pkg/proto/common/organization_bound/v1"
paginationv2 "github.com/tech/sendico/pkg/proto/common/pagination/v2"
pboundv1 "github.com/tech/sendico/pkg/proto/common/permission_bound/v1"
storablev1 "github.com/tech/sendico/pkg/proto/common/storable/v1"
endpointv1 "github.com/tech/sendico/pkg/proto/payments/endpoint/v1"
"go.mongodb.org/mongo-driver/v2/bson"
"google.golang.org/protobuf/types/known/timestamppb"
)
func autoError[T any](ctx context.Context, logger mlogger.Logger, err error) (*T, error) {
return gsresponse.Execute(ctx, gsresponse.Auto[T](logger, mservice.PaymentMethods, err))
}
func parseObjectID(value, field string) (bson.ObjectID, error) {
trimmed := strings.TrimSpace(value)
if trimmed == "" {
return bson.NilObjectID, merrors.InvalidArgument(fmt.Sprintf("%s is required", field), field)
}
ref, err := bson.ObjectIDFromHex(trimmed)
if err != nil {
return bson.NilObjectID, merrors.InvalidArgument(fmt.Sprintf("%s must be a valid object id", field), field)
}
return ref, nil
}
func decodePaymentMethodRecord(record *endpointv1.PaymentMethodRecord) (*model.PaymentMethod, error) {
if record == nil {
return nil, merrors.InvalidArgument("payment_method_record is required", "payment_method_record")
}
res, err := decodePaymentMethodPayload(record.GetPaymentMethod(), "payment_method_record.payment_method")
if err != nil {
return nil, err
}
if err := applyPermissionBoundRecord(res, record.GetPermissionBound()); err != nil {
return nil, err
}
return res, nil
}
func decodePaymentMethodPayload(method *endpointv1.PaymentMethod, field string) (*model.PaymentMethod, error) {
if method == nil {
return nil, merrors.InvalidArgument(field+" is required", field)
}
recipientRef, err := parseObjectID(method.GetRecipientRef(), field+".recipient_ref")
if err != nil {
return nil, err
}
pt, err := paymentTypeFromProto(method.GetType(), field+".type")
if err != nil {
return nil, err
}
return &model.PaymentMethod{
Describable: describableFromProto(method.GetDescribable()),
RecipientRef: recipientRef,
Type: pt,
Data: cloneBytes(method.GetData()),
IsMain: method.GetIsMain(),
}, nil
}
func encodePaymentMethodRecord(pm *model.PaymentMethod) (*endpointv1.PaymentMethodRecord, error) {
if pm == nil {
return nil, merrors.InvalidArgument("payment method is required")
}
pt, err := paymentTypeToProto(pm.Type)
if err != nil {
return nil, err
}
return &endpointv1.PaymentMethodRecord{
PermissionBound: permissionBoundFromModel(pm),
PaymentMethod: &endpointv1.PaymentMethod{
Describable: describableToProto(pm.Describable),
RecipientRef: toObjectHex(pm.RecipientRef),
Type: pt,
Data: cloneBytes(pm.Data),
IsMain: pm.IsMain,
},
}, nil
}
func paymentTypeFromProto(value endpointv1.PaymentMethodType, field string) (model.PaymentType, error) {
switch value {
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_IBAN:
return model.PaymentTypeIban, nil
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_CARD:
return model.PaymentTypeCard, nil
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_CARD_TOKEN:
return model.PaymentTypeCardToken, nil
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_BANK_ACCOUNT:
return model.PaymentTypeBankAccount, nil
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_WALLET:
return model.PaymentTypeWallet, nil
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_CRYPTO_ADDRESS:
return model.PaymentTypeCryptoAddress, nil
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_LEDGER:
return model.PaymentTypeLedger, nil
default:
return model.PaymentTypeIban, merrors.InvalidArgument(fmt.Sprintf("%s has unsupported value: %s", field, value.String()), field)
}
}
func paymentTypeToProto(value model.PaymentType) (endpointv1.PaymentMethodType, error) {
switch value {
case model.PaymentTypeIban:
return endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_IBAN, nil
case model.PaymentTypeCard:
return endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_CARD, nil
case model.PaymentTypeCardToken:
return endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_CARD_TOKEN, nil
case model.PaymentTypeBankAccount:
return endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_BANK_ACCOUNT, nil
case model.PaymentTypeWallet:
return endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_WALLET, nil
case model.PaymentTypeCryptoAddress:
return endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_CRYPTO_ADDRESS, nil
case model.PaymentTypeLedger:
return endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_LEDGER, nil
default:
return endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_UNSPECIFIED, merrors.InvalidArgument(fmt.Sprintf("unsupported payment method type: %s", value.String()), "type")
}
}
func describableFromProto(src *describablev1.Describable) model.Describable {
if src == nil {
return model.Describable{}
}
res := model.Describable{Name: src.GetName()}
if src.Description != nil {
v := src.GetDescription()
res.Description = &v
}
return res
}
func describableToProto(src model.Describable) *describablev1.Describable {
if strings.TrimSpace(src.Name) == "" && src.Description == nil {
return nil
}
res := &describablev1.Describable{
Name: src.Name,
}
if src.Description != nil {
v := *src.Description
res.Description = &v
}
return res
}
func cloneBytes(src []byte) []byte {
if len(src) == 0 {
return nil
}
dst := make([]byte, len(src))
copy(dst, src)
return dst
}
func applyPermissionBoundRecord(pm *model.PaymentMethod, src *pboundv1.PermissionBound) error {
if pm == nil || src == nil {
return nil
}
if storable := src.GetStorable(); storable != nil {
if methodRef, err := parseOptionalObjectID(storable.GetId(), "payment_method_record.permission_bound.storable.id"); err != nil {
return err
} else if methodRef != bson.NilObjectID {
pm.ID = methodRef
}
pm.CreatedAt = fromProtoTime(storable.GetCreatedAt())
pm.UpdatedAt = fromProtoTime(storable.GetUpdatedAt())
}
if archivable := src.GetArchivable(); archivable != nil {
pm.Archived = archivable.GetIsArchived()
}
if orgBound := src.GetOrganizationBound(); orgBound != nil {
if orgRef, err := parseOptionalObjectID(orgBound.GetOrganizationRef(), "payment_method_record.permission_bound.organization_bound.organization_ref"); err != nil {
return err
} else if orgRef != bson.NilObjectID {
pm.SetOrganizationRef(orgRef)
}
}
if permissionRef, err := parseOptionalObjectID(src.GetPermissionRef(), "payment_method_record.permission_bound.permission_ref"); err != nil {
return err
} else if permissionRef != bson.NilObjectID {
pm.SetPermissionRef(permissionRef)
}
return nil
}
func permissionBoundFromModel(pm *model.PaymentMethod) *pboundv1.PermissionBound {
if pm == nil {
return nil
}
return &pboundv1.PermissionBound{
Storable: &storablev1.Storable{
Id: toObjectHex(pm.ID),
CreatedAt: toProtoTime(pm.CreatedAt),
UpdatedAt: toProtoTime(pm.UpdatedAt),
},
Archivable: &archivablev1.Archivable{
IsArchived: pm.Archived,
},
OrganizationBound: &oboundv1.OrganizationBound{
OrganizationRef: toObjectHex(pm.GetOrganizationRef()),
},
PermissionRef: toObjectHex(pm.GetPermissionRef()),
}
}
func parseOptionalObjectID(value, field string) (bson.ObjectID, error) {
trimmed := strings.TrimSpace(value)
if trimmed == "" {
return bson.NilObjectID, nil
}
ref, err := bson.ObjectIDFromHex(trimmed)
if err != nil {
return bson.NilObjectID, merrors.InvalidArgument(fmt.Sprintf("%s must be a valid object id", field), field)
}
return ref, nil
}
func toObjectHex(value bson.ObjectID) string {
if value == bson.NilObjectID {
return ""
}
return value.Hex()
}
func toProtoTime(value time.Time) *timestamppb.Timestamp {
if value.IsZero() {
return nil
}
return timestamppb.New(value)
}
func fromProtoTime(value *timestamppb.Timestamp) time.Time {
if value == nil {
return time.Time{}
}
return value.AsTime()
}
func toModelCursor(cursor *paginationv2.ViewCursor) *model.ViewCursor {
if cursor == nil {
return nil
}
res := &model.ViewCursor{}
hasAny := false
if limit := cursor.GetLimit(); limit != nil {
v := limit.GetValue()
res.Limit = &v
hasAny = true
}
if offset := cursor.GetOffset(); offset != nil {
v := offset.GetValue()
res.Offset = &v
hasAny = true
}
if archived := cursor.GetIsArchived(); archived != nil {
v := archived.GetValue()
res.IsArchived = &v
hasAny = true
}
if !hasAny {
return nil
}
return res
}

View File

@@ -0,0 +1,17 @@
package main
import (
"github.com/tech/sendico/payments/methods/internal/appversion"
si "github.com/tech/sendico/payments/methods/internal/server"
"github.com/tech/sendico/pkg/mlogger"
"github.com/tech/sendico/pkg/server"
smain "github.com/tech/sendico/pkg/server/main"
)
func factory(logger mlogger.Logger, file string, debug bool) (server.Application, error) {
return si.Create(logger, file, debug)
}
func main() {
smain.RunServer("main", appversion.Create(), factory)
}

View File

@@ -28,7 +28,7 @@ require (
github.com/tech/sendico/pkg v0.1.0 github.com/tech/sendico/pkg v0.1.0
go.mongodb.org/mongo-driver/v2 v2.5.0 go.mongodb.org/mongo-driver/v2 v2.5.0
go.uber.org/zap v1.27.1 go.uber.org/zap v1.27.1
google.golang.org/grpc v1.78.0 google.golang.org/grpc v1.79.1
google.golang.org/protobuf v1.36.11 google.golang.org/protobuf v1.36.11
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )
@@ -57,8 +57,6 @@ require (
github.com/xdg-go/scram v1.2.0 // indirect github.com/xdg-go/scram v1.2.0 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
go.opentelemetry.io/otel v1.39.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.39.0 // indirect
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect
golang.org/x/crypto v0.48.0 // indirect golang.org/x/crypto v0.48.0 // indirect

View File

@@ -213,8 +213,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= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac= google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@@ -12,6 +12,7 @@ import (
fxv1 "github.com/tech/sendico/pkg/proto/common/fx/v1" fxv1 "github.com/tech/sendico/pkg/proto/common/fx/v1"
gatewayv1 "github.com/tech/sendico/pkg/proto/common/gateway/v1" gatewayv1 "github.com/tech/sendico/pkg/proto/common/gateway/v1"
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/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" chainv1 "github.com/tech/sendico/pkg/proto/gateway/chain/v1"
oraclev1 "github.com/tech/sendico/pkg/proto/oracle/v1" oraclev1 "github.com/tech/sendico/pkg/proto/oracle/v1"
orchestratorv1 "github.com/tech/sendico/pkg/proto/payments/orchestration/v1" orchestratorv1 "github.com/tech/sendico/pkg/proto/payments/orchestration/v1"
@@ -553,25 +554,25 @@ func protoFailureFromModel(code model.PaymentFailureCode) sharedv1.PaymentFailur
} }
} }
func settlementModeFromProto(mode sharedv1.SettlementMode) model.SettlementMode { func settlementModeFromProto(mode paymentv1.SettlementMode) model.SettlementMode {
switch mode { switch mode {
case sharedv1.SettlementMode_SETTLEMENT_FIX_SOURCE: case paymentv1.SettlementMode_SETTLEMENT_FIX_SOURCE:
return model.SettlementModeFixSource return model.SettlementModeFixSource
case sharedv1.SettlementMode_SETTLEMENT_FIX_RECEIVED: case paymentv1.SettlementMode_SETTLEMENT_FIX_RECEIVED:
return model.SettlementModeFixReceived return model.SettlementModeFixReceived
default: default:
return model.SettlementModeUnspecified return model.SettlementModeUnspecified
} }
} }
func settlementModeToProto(mode model.SettlementMode) sharedv1.SettlementMode { func settlementModeToProto(mode model.SettlementMode) paymentv1.SettlementMode {
switch mode { switch mode {
case model.SettlementModeFixSource: case model.SettlementModeFixSource:
return sharedv1.SettlementMode_SETTLEMENT_FIX_SOURCE return paymentv1.SettlementMode_SETTLEMENT_FIX_SOURCE
case model.SettlementModeFixReceived: case model.SettlementModeFixReceived:
return sharedv1.SettlementMode_SETTLEMENT_FIX_RECEIVED return paymentv1.SettlementMode_SETTLEMENT_FIX_RECEIVED
default: default:
return sharedv1.SettlementMode_SETTLEMENT_UNSPECIFIED return paymentv1.SettlementMode_SETTLEMENT_UNSPECIFIED
} }
} }
@@ -685,6 +686,10 @@ func fxQuoteFromProto(quote *oraclev1.Quote) *paymenttypes.FXQuote {
if quote == nil { if quote == nil {
return nil return nil
} }
pricedAtUnixMs := int64(0)
if ts := quote.GetPricedAt(); ts != nil {
pricedAtUnixMs = ts.AsTime().UnixMilli()
}
return &paymenttypes.FXQuote{ return &paymenttypes.FXQuote{
QuoteRef: strings.TrimSpace(quote.GetQuoteRef()), QuoteRef: strings.TrimSpace(quote.GetQuoteRef()),
Pair: pairFromProto(quote.GetPair()), Pair: pairFromProto(quote.GetPair()),
@@ -693,6 +698,7 @@ func fxQuoteFromProto(quote *oraclev1.Quote) *paymenttypes.FXQuote {
BaseAmount: moneyFromProto(quote.GetBaseAmount()), BaseAmount: moneyFromProto(quote.GetBaseAmount()),
QuoteAmount: moneyFromProto(quote.GetQuoteAmount()), QuoteAmount: moneyFromProto(quote.GetQuoteAmount()),
ExpiresAtUnixMs: quote.GetExpiresAtUnixMs(), ExpiresAtUnixMs: quote.GetExpiresAtUnixMs(),
PricedAtUnixMs: pricedAtUnixMs,
Provider: strings.TrimSpace(quote.GetProvider()), Provider: strings.TrimSpace(quote.GetProvider()),
RateRef: strings.TrimSpace(quote.GetRateRef()), RateRef: strings.TrimSpace(quote.GetRateRef()),
Firm: quote.GetFirm(), Firm: quote.GetFirm(),
@@ -703,6 +709,10 @@ func fxQuoteToProto(quote *paymenttypes.FXQuote) *oraclev1.Quote {
if quote == nil { if quote == nil {
return nil return nil
} }
var pricedAt *timestamppb.Timestamp
if quote.PricedAtUnixMs > 0 {
pricedAt = timestamppb.New(time.UnixMilli(quote.PricedAtUnixMs).UTC())
}
return &oraclev1.Quote{ return &oraclev1.Quote{
QuoteRef: strings.TrimSpace(quote.QuoteRef), QuoteRef: strings.TrimSpace(quote.QuoteRef),
Pair: pairToProto(quote.Pair), Pair: pairToProto(quote.Pair),
@@ -711,6 +721,7 @@ func fxQuoteToProto(quote *paymenttypes.FXQuote) *oraclev1.Quote {
BaseAmount: protoMoney(quote.BaseAmount), BaseAmount: protoMoney(quote.BaseAmount),
QuoteAmount: protoMoney(quote.QuoteAmount), QuoteAmount: protoMoney(quote.QuoteAmount),
ExpiresAtUnixMs: quote.ExpiresAtUnixMs, ExpiresAtUnixMs: quote.ExpiresAtUnixMs,
PricedAt: pricedAt,
Provider: strings.TrimSpace(quote.Provider), Provider: strings.TrimSpace(quote.Provider),
RateRef: strings.TrimSpace(quote.RateRef), RateRef: strings.TrimSpace(quote.RateRef),
Firm: quote.Firm, Firm: quote.Firm,

View File

@@ -2,6 +2,7 @@ package orchestrator
import ( import (
"testing" "testing"
"time"
paymenttypes "github.com/tech/sendico/pkg/payments/types" paymenttypes "github.com/tech/sendico/pkg/payments/types"
feesv1 "github.com/tech/sendico/pkg/proto/billing/fees/v1" feesv1 "github.com/tech/sendico/pkg/proto/billing/fees/v1"
@@ -10,6 +11,7 @@ import (
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1" moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
chainv1 "github.com/tech/sendico/pkg/proto/gateway/chain/v1" chainv1 "github.com/tech/sendico/pkg/proto/gateway/chain/v1"
oraclev1 "github.com/tech/sendico/pkg/proto/oracle/v1" oraclev1 "github.com/tech/sendico/pkg/proto/oracle/v1"
"google.golang.org/protobuf/types/known/timestamppb"
) )
func TestMoneyConversionRoundTrip(t *testing.T) { func TestMoneyConversionRoundTrip(t *testing.T) {
@@ -69,6 +71,7 @@ func TestFeeLineConversionRoundTrip(t *testing.T) {
} }
func TestFXQuoteConversionRoundTrip(t *testing.T) { func TestFXQuoteConversionRoundTrip(t *testing.T) {
pricedAt := int64(1700000000000)
proto := &oraclev1.Quote{ proto := &oraclev1.Quote{
QuoteRef: "q1", QuoteRef: "q1",
Pair: &fxv1.CurrencyPair{Base: "USD", Quote: "EUR"}, Pair: &fxv1.CurrencyPair{Base: "USD", Quote: "EUR"},
@@ -77,6 +80,7 @@ func TestFXQuoteConversionRoundTrip(t *testing.T) {
BaseAmount: &moneyv1.Money{Currency: "USD", Amount: "100"}, BaseAmount: &moneyv1.Money{Currency: "USD", Amount: "100"},
QuoteAmount: &moneyv1.Money{Currency: "EUR", Amount: "90"}, QuoteAmount: &moneyv1.Money{Currency: "EUR", Amount: "90"},
ExpiresAtUnixMs: 1700000000000, ExpiresAtUnixMs: 1700000000000,
PricedAt: timestamppb.New(time.UnixMilli(pricedAt).UTC()),
Provider: "provider", Provider: "provider",
RateRef: "rate", RateRef: "rate",
Firm: true, Firm: true,
@@ -88,6 +92,9 @@ func TestFXQuoteConversionRoundTrip(t *testing.T) {
if model.Side != paymenttypes.FXSideSellBaseBuyQuote || model.Price.GetValue() != "0.9" { if model.Side != paymenttypes.FXSideSellBaseBuyQuote || model.Price.GetValue() != "0.9" {
t.Fatalf("fxQuoteFromProto enums mismatch: %#v", model) t.Fatalf("fxQuoteFromProto enums mismatch: %#v", model)
} }
if model.PricedAtUnixMs != pricedAt {
t.Fatalf("fxQuoteFromProto priced_at mismatch: %#v", model)
}
back := fxQuoteToProto(model) back := fxQuoteToProto(model)
if back == nil || back.GetQuoteRef() != "q1" || back.GetPair().GetBase() != "USD" || back.GetPair().GetQuote() != "EUR" { if back == nil || back.GetQuoteRef() != "q1" || back.GetPair().GetBase() != "USD" || back.GetPair().GetQuote() != "EUR" {
t.Fatalf("fxQuoteToProto mismatch: %#v", back) t.Fatalf("fxQuoteToProto mismatch: %#v", back)
@@ -95,6 +102,9 @@ func TestFXQuoteConversionRoundTrip(t *testing.T) {
if back.GetSide() != fxv1.Side_SELL_BASE_BUY_QUOTE || back.GetPrice().GetValue() != "0.9" { if back.GetSide() != fxv1.Side_SELL_BASE_BUY_QUOTE || back.GetPrice().GetValue() != "0.9" {
t.Fatalf("fxQuoteToProto enums mismatch: %#v", back) t.Fatalf("fxQuoteToProto enums mismatch: %#v", back)
} }
if got := back.GetPricedAt(); got == nil || got.AsTime().UnixMilli() != pricedAt {
t.Fatalf("fxQuoteToProto priced_at mismatch: %#v", back)
}
} }
func TestAssetConversionRoundTrip(t *testing.T) { func TestAssetConversionRoundTrip(t *testing.T) {

View File

@@ -14,6 +14,7 @@ import (
accountingv1 "github.com/tech/sendico/pkg/proto/common/accounting/v1" accountingv1 "github.com/tech/sendico/pkg/proto/common/accounting/v1"
fxv1 "github.com/tech/sendico/pkg/proto/common/fx/v1" fxv1 "github.com/tech/sendico/pkg/proto/common/fx/v1"
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1" moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
paymentv1 "github.com/tech/sendico/pkg/proto/common/payment/v1"
) )
type moneyGetter interface { type moneyGetter interface {
@@ -100,7 +101,7 @@ func resolveTradeAmounts(intentAmount *moneyv1.Money, fxQuote *oraclev1.Quote, s
} }
} }
func computeAggregates(pay, settlement, fee *moneyv1.Money, network *chainv1.EstimateTransferFeeResponse, fxQuote *oraclev1.Quote, mode sharedv1.SettlementMode) (*moneyv1.Money, *moneyv1.Money) { func computeAggregates(pay, settlement, fee *moneyv1.Money, network *chainv1.EstimateTransferFeeResponse, fxQuote *oraclev1.Quote, mode paymentv1.SettlementMode) (*moneyv1.Money, *moneyv1.Money) {
if pay == nil { if pay == nil {
return nil, nil return nil, nil
} }
@@ -142,7 +143,7 @@ func computeAggregates(pay, settlement, fee *moneyv1.Money, network *chainv1.Est
} }
switch mode { switch mode {
case sharedv1.SettlementMode_SETTLEMENT_FIX_RECEIVED: case paymentv1.SettlementMode_SETTLEMENT_FIX_RECEIVED:
// Sender pays the fee: keep settlement fixed, increase debit. // Sender pays the fee: keep settlement fixed, increase debit.
applyChargeToDebit(fee) applyChargeToDebit(fee)
default: default:
@@ -152,7 +153,7 @@ func computeAggregates(pay, settlement, fee *moneyv1.Money, network *chainv1.Est
if network != nil && network.GetNetworkFee() != nil { if network != nil && network.GetNetworkFee() != nil {
switch mode { switch mode {
case sharedv1.SettlementMode_SETTLEMENT_FIX_RECEIVED: case paymentv1.SettlementMode_SETTLEMENT_FIX_RECEIVED:
applyChargeToDebit(network.GetNetworkFee()) applyChargeToDebit(network.GetNetworkFee())
default: default:
applyChargeToSettlement(network.GetNetworkFee()) applyChargeToSettlement(network.GetNetworkFee())

View File

@@ -7,9 +7,9 @@ import (
accountingv1 "github.com/tech/sendico/pkg/proto/common/accounting/v1" accountingv1 "github.com/tech/sendico/pkg/proto/common/accounting/v1"
fxv1 "github.com/tech/sendico/pkg/proto/common/fx/v1" fxv1 "github.com/tech/sendico/pkg/proto/common/fx/v1"
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/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" chainv1 "github.com/tech/sendico/pkg/proto/gateway/chain/v1"
oraclev1 "github.com/tech/sendico/pkg/proto/oracle/v1" oraclev1 "github.com/tech/sendico/pkg/proto/oracle/v1"
sharedv1 "github.com/tech/sendico/pkg/proto/payments/shared/v1"
) )
func TestResolveTradeAmountsBuyBase(t *testing.T) { func TestResolveTradeAmountsBuyBase(t *testing.T) {
@@ -50,7 +50,7 @@ func TestComputeAggregatesConvertsCurrencies(t *testing.T) {
}, },
} }
debit, settlement := computeAggregates(pay, settle, fee, network, fxQuote, sharedv1.SettlementMode_SETTLEMENT_FIX_RECEIVED) debit, settlement := computeAggregates(pay, settle, fee, network, fxQuote, paymentv1.SettlementMode_SETTLEMENT_FIX_RECEIVED)
if debit.GetCurrency() != "USD" || debit.GetAmount() != "115" { if debit.GetCurrency() != "USD" || debit.GetAmount() != "115" {
t.Fatalf("expected debit 115 USD, got %s %s", debit.GetCurrency(), debit.GetAmount()) t.Fatalf("expected debit 115 USD, got %s %s", debit.GetCurrency(), debit.GetAmount())
} }
@@ -71,7 +71,7 @@ func TestComputeAggregatesRecipientPaysFee(t *testing.T) {
}, },
} }
debit, settlement := computeAggregates(pay, settle, fee, nil, fxQuote, sharedv1.SettlementMode_SETTLEMENT_FIX_SOURCE) debit, settlement := computeAggregates(pay, settle, fee, nil, fxQuote, paymentv1.SettlementMode_SETTLEMENT_FIX_SOURCE)
if debit.GetCurrency() != "USDT" || debit.GetAmount() != "100" { if debit.GetCurrency() != "USDT" || debit.GetAmount() != "100" {
t.Fatalf("expected debit 100 USDT, got %s %s", debit.GetCurrency(), debit.GetAmount()) t.Fatalf("expected debit 100 USDT, got %s %s", debit.GetCurrency(), debit.GetAmount())
} }

View File

@@ -2,7 +2,6 @@ package orchestrator
import ( import (
"context" "context"
"strings"
"testing" "testing"
mntxclient "github.com/tech/sendico/gateway/mntx/client" mntxclient "github.com/tech/sendico/gateway/mntx/client"
@@ -216,64 +215,3 @@ func TestExecutePaymentPlan_SourceBeforeDestination(t *testing.T) {
} }
} }
func TestExecutePaymentPlan_RejectsLegacyLedgerOperations(t *testing.T) {
ctx := context.Background()
store := newStubPaymentsStore()
repo := &stubRepository{store: store}
ledgerFake := &ledgerclient.Fake{}
svc := &Service{
logger: zap.NewNop(),
storage: repo,
deps: serviceDependencies{
ledger: ledgerDependency{
client: ledgerFake,
internal: ledgerFake,
},
},
}
executor := newPaymentExecutor(&svc.deps, svc.logger, svc)
payment := &model.Payment{
PaymentRef: "pay-legacy-1",
IdempotencyKey: "pay-legacy-1",
OrganizationBoundBase: mo.OrganizationBoundBase{
OrganizationRef: bson.NewObjectID(),
},
Intent: model.PaymentIntent{
Ref: "ref-legacy-1",
Kind: model.PaymentKindPayout,
Source: model.PaymentEndpoint{
Type: model.EndpointTypeManagedWallet,
ManagedWallet: &model.ManagedWalletEndpoint{
ManagedWalletRef: "wallet-src",
},
},
Destination: model.PaymentEndpoint{
Type: model.EndpointTypeCard,
Card: &model.CardEndpoint{MaskedPan: "4111"},
},
},
PaymentPlan: &model.PaymentPlan{
ID: "pay-legacy-1",
IdempotencyKey: "pay-legacy-1",
Steps: []*model.PaymentStep{
{StepID: "ledger_block", Rail: model.RailLedger, Action: model.RailOperationBlock, Amount: &paymenttypes.Money{Currency: "USD", Amount: "100"}},
},
},
}
store.payments[payment.PaymentRef] = payment
err := executor.executePaymentPlan(ctx, store, payment, &sharedv1.PaymentQuote{})
if err == nil {
t.Fatal("expected legacy ledger operation error")
}
if !strings.Contains(err.Error(), "unsupported action") {
t.Fatalf("unexpected error: %v", err)
}
}

View File

@@ -69,6 +69,7 @@ func cloneStoredFXQuote(src *paymenttypes.FXQuote) *paymenttypes.FXQuote {
QuoteRef: strings.TrimSpace(src.QuoteRef), QuoteRef: strings.TrimSpace(src.QuoteRef),
Side: src.Side, Side: src.Side,
ExpiresAtUnixMs: src.ExpiresAtUnixMs, ExpiresAtUnixMs: src.ExpiresAtUnixMs,
PricedAtUnixMs: src.PricedAtUnixMs,
Provider: strings.TrimSpace(src.Provider), Provider: strings.TrimSpace(src.Provider),
RateRef: strings.TrimSpace(src.RateRef), RateRef: strings.TrimSpace(src.RateRef),
Firm: src.Firm, Firm: src.Firm,

View File

@@ -7,6 +7,7 @@ import (
"github.com/tech/sendico/payments/storage" "github.com/tech/sendico/payments/storage"
"github.com/tech/sendico/payments/storage/model" "github.com/tech/sendico/payments/storage/model"
quotestorage "github.com/tech/sendico/payments/storage/quote"
"github.com/tech/sendico/pkg/api/routers/gsresponse" "github.com/tech/sendico/pkg/api/routers/gsresponse"
"github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/merrors"
"github.com/tech/sendico/pkg/mlogger" "github.com/tech/sendico/pkg/mlogger"
@@ -72,7 +73,7 @@ func ensurePaymentsStore(repo storage.Repository) (storage.PaymentsStore, error)
return store, nil return store, nil
} }
func ensureQuotesStore(repo storage.Repository) (storage.QuotesStore, error) { func ensureQuotesStore(repo storage.Repository) (quotestorage.QuotesStore, error) {
if repo == nil { if repo == nil {
return nil, errStorageUnavailable return nil, errStorageUnavailable
} }

View File

@@ -8,11 +8,13 @@ import (
ledgerclient "github.com/tech/sendico/ledger/client" ledgerclient "github.com/tech/sendico/ledger/client"
"github.com/tech/sendico/payments/storage" "github.com/tech/sendico/payments/storage"
"github.com/tech/sendico/payments/storage/model" "github.com/tech/sendico/payments/storage/model"
quotestorage "github.com/tech/sendico/payments/storage/quote"
clockpkg "github.com/tech/sendico/pkg/clock" clockpkg "github.com/tech/sendico/pkg/clock"
mloggerfactory "github.com/tech/sendico/pkg/mlogger/factory" mloggerfactory "github.com/tech/sendico/pkg/mlogger/factory"
"github.com/tech/sendico/pkg/model/account_role" "github.com/tech/sendico/pkg/model/account_role"
paymenttypes "github.com/tech/sendico/pkg/payments/types" paymenttypes "github.com/tech/sendico/pkg/payments/types"
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1" moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
paymentv1 "github.com/tech/sendico/pkg/proto/common/payment/v1"
connectorv1 "github.com/tech/sendico/pkg/proto/connector/v1" connectorv1 "github.com/tech/sendico/pkg/proto/connector/v1"
ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1" ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
orchestratorv1 "github.com/tech/sendico/pkg/proto/payments/orchestration/v1" orchestratorv1 "github.com/tech/sendico/pkg/proto/payments/orchestration/v1"
@@ -59,7 +61,7 @@ func TestNewPayment(t *testing.T) {
intent := &sharedv1.PaymentIntent{ intent := &sharedv1.PaymentIntent{
Ref: "ref-1", Ref: "ref-1",
Amount: &moneyv1.Money{Currency: "USD", Amount: "10"}, Amount: &moneyv1.Money{Currency: "USD", Amount: "10"},
SettlementMode: sharedv1.SettlementMode_SETTLEMENT_FIX_RECEIVED, SettlementMode: paymentv1.SettlementMode_SETTLEMENT_FIX_RECEIVED,
SettlementCurrency: "USD", SettlementCurrency: "USD",
} }
quote := &sharedv1.PaymentQuote{QuoteRef: "q1"} quote := &sharedv1.PaymentQuote{QuoteRef: "q1"}
@@ -380,7 +382,7 @@ func TestInitiatePaymentByQuoteRef(t *testing.T) {
type stubRepo struct { type stubRepo struct {
payments storage.PaymentsStore payments storage.PaymentsStore
quotes storage.QuotesStore quotes quotestorage.QuotesStore
routes storage.RoutesStore routes storage.RoutesStore
plans storage.PlanTemplatesStore plans storage.PlanTemplatesStore
pingErr error pingErr error
@@ -388,8 +390,11 @@ type stubRepo struct {
func (s stubRepo) Ping(context.Context) error { return s.pingErr } func (s stubRepo) Ping(context.Context) error { return s.pingErr }
func (s stubRepo) Payments() storage.PaymentsStore { return s.payments } func (s stubRepo) Payments() storage.PaymentsStore { return s.payments }
func (s stubRepo) Quotes() storage.QuotesStore { return s.quotes } func (s stubRepo) PaymentMethods() storage.PaymentMethodsStore {
func (s stubRepo) Routes() storage.RoutesStore { return s.routes } return nil
}
func (s stubRepo) Quotes() quotestorage.QuotesStore { return s.quotes }
func (s stubRepo) Routes() storage.RoutesStore { return s.routes }
func (s stubRepo) PlanTemplates() storage.PlanTemplatesStore { func (s stubRepo) PlanTemplates() storage.PlanTemplatesStore {
if s.plans != nil { if s.plans != nil {
return s.plans return s.plans

View File

@@ -10,6 +10,7 @@ import (
ledgerclient "github.com/tech/sendico/ledger/client" ledgerclient "github.com/tech/sendico/ledger/client"
"github.com/tech/sendico/payments/storage" "github.com/tech/sendico/payments/storage"
"github.com/tech/sendico/payments/storage/model" "github.com/tech/sendico/payments/storage/model"
quotestorage "github.com/tech/sendico/payments/storage/quote"
"github.com/tech/sendico/pkg/api/routers/gsresponse" "github.com/tech/sendico/pkg/api/routers/gsresponse"
"github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/merrors"
mo "github.com/tech/sendico/pkg/model" mo "github.com/tech/sendico/pkg/model"
@@ -399,14 +400,17 @@ func TestProcessDepositObservedHandler_MatchesPayment(t *testing.T) {
type stubRepository struct { type stubRepository struct {
store *stubPaymentsStore store *stubPaymentsStore
quotes storage.QuotesStore quotes quotestorage.QuotesStore
routes storage.RoutesStore routes storage.RoutesStore
plans storage.PlanTemplatesStore plans storage.PlanTemplatesStore
} }
func (r *stubRepository) Ping(context.Context) error { return nil } func (r *stubRepository) Ping(context.Context) error { return nil }
func (r *stubRepository) Payments() storage.PaymentsStore { return r.store } func (r *stubRepository) Payments() storage.PaymentsStore { return r.store }
func (r *stubRepository) Quotes() storage.QuotesStore { func (r *stubRepository) PaymentMethods() storage.PaymentMethodsStore {
return nil
}
func (r *stubRepository) Quotes() quotestorage.QuotesStore {
if r.quotes != nil { if r.quotes != nil {
return r.quotes return r.quotes
} }

View File

@@ -2,6 +2,7 @@ package plan_builder
import ( import (
"strings" "strings"
"time"
"github.com/shopspring/decimal" "github.com/shopspring/decimal"
"github.com/tech/sendico/payments/storage/model" "github.com/tech/sendico/payments/storage/model"
@@ -15,6 +16,7 @@ import (
chainv1 "github.com/tech/sendico/pkg/proto/gateway/chain/v1" chainv1 "github.com/tech/sendico/pkg/proto/gateway/chain/v1"
oraclev1 "github.com/tech/sendico/pkg/proto/oracle/v1" oraclev1 "github.com/tech/sendico/pkg/proto/oracle/v1"
sharedv1 "github.com/tech/sendico/pkg/proto/payments/shared/v1" sharedv1 "github.com/tech/sendico/pkg/proto/payments/shared/v1"
"google.golang.org/protobuf/types/known/timestamppb"
) )
type moneyGetter interface { type moneyGetter interface {
@@ -226,6 +228,10 @@ func fxQuoteFromProto(quote *oraclev1.Quote) *paymenttypes.FXQuote {
if quote == nil { if quote == nil {
return nil return nil
} }
pricedAtUnixMs := int64(0)
if ts := quote.GetPricedAt(); ts != nil {
pricedAtUnixMs = ts.AsTime().UnixMilli()
}
return &paymenttypes.FXQuote{ return &paymenttypes.FXQuote{
QuoteRef: strings.TrimSpace(quote.GetQuoteRef()), QuoteRef: strings.TrimSpace(quote.GetQuoteRef()),
Pair: pairFromProto(quote.GetPair()), Pair: pairFromProto(quote.GetPair()),
@@ -234,6 +240,7 @@ func fxQuoteFromProto(quote *oraclev1.Quote) *paymenttypes.FXQuote {
BaseAmount: moneyFromProto(quote.GetBaseAmount()), BaseAmount: moneyFromProto(quote.GetBaseAmount()),
QuoteAmount: moneyFromProto(quote.GetQuoteAmount()), QuoteAmount: moneyFromProto(quote.GetQuoteAmount()),
ExpiresAtUnixMs: quote.GetExpiresAtUnixMs(), ExpiresAtUnixMs: quote.GetExpiresAtUnixMs(),
PricedAtUnixMs: pricedAtUnixMs,
Provider: strings.TrimSpace(quote.GetProvider()), Provider: strings.TrimSpace(quote.GetProvider()),
RateRef: strings.TrimSpace(quote.GetRateRef()), RateRef: strings.TrimSpace(quote.GetRateRef()),
Firm: quote.GetFirm(), Firm: quote.GetFirm(),
@@ -244,6 +251,10 @@ func fxQuoteToProto(quote *paymenttypes.FXQuote) *oraclev1.Quote {
if quote == nil { if quote == nil {
return nil return nil
} }
var pricedAt *timestamppb.Timestamp
if quote.PricedAtUnixMs > 0 {
pricedAt = timestamppb.New(time.UnixMilli(quote.PricedAtUnixMs).UTC())
}
return &oraclev1.Quote{ return &oraclev1.Quote{
QuoteRef: strings.TrimSpace(quote.QuoteRef), QuoteRef: strings.TrimSpace(quote.QuoteRef),
Pair: pairToProto(quote.Pair), Pair: pairToProto(quote.Pair),
@@ -252,6 +263,7 @@ func fxQuoteToProto(quote *paymenttypes.FXQuote) *oraclev1.Quote {
BaseAmount: protoMoney(quote.BaseAmount), BaseAmount: protoMoney(quote.BaseAmount),
QuoteAmount: protoMoney(quote.QuoteAmount), QuoteAmount: protoMoney(quote.QuoteAmount),
ExpiresAtUnixMs: quote.ExpiresAtUnixMs, ExpiresAtUnixMs: quote.ExpiresAtUnixMs,
PricedAt: pricedAt,
Provider: strings.TrimSpace(quote.Provider), Provider: strings.TrimSpace(quote.Provider),
RateRef: strings.TrimSpace(quote.RateRef), RateRef: strings.TrimSpace(quote.RateRef),
Firm: quote.Firm, Firm: quote.Firm,

View File

@@ -26,7 +26,7 @@ require (
github.com/tech/sendico/pkg v0.1.0 github.com/tech/sendico/pkg v0.1.0
go.mongodb.org/mongo-driver/v2 v2.5.0 go.mongodb.org/mongo-driver/v2 v2.5.0
go.uber.org/zap v1.27.1 go.uber.org/zap v1.27.1
google.golang.org/grpc v1.78.0 google.golang.org/grpc v1.79.1
google.golang.org/protobuf v1.36.11 google.golang.org/protobuf v1.36.11
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )

View File

@@ -155,16 +155,16 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
@@ -213,8 +213,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= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac= google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@@ -6,7 +6,7 @@ import (
"github.com/tech/sendico/pkg/server" "github.com/tech/sendico/pkg/server"
) )
// Create initialises the payment quotation server implementation. // Create initialises the payment quotation server implementation
func Create(logger mlogger.Logger, file string, debug bool) (server.Application, error) { func Create(logger mlogger.Logger, file string, debug bool) (server.Application, error) {
return serverimp.Create(logger, file, debug) return serverimp.Create(logger, file, debug)
} }

View File

@@ -2,6 +2,7 @@ package plan
import ( import (
"strings" "strings"
"time"
"github.com/shopspring/decimal" "github.com/shopspring/decimal"
"github.com/tech/sendico/payments/storage/model" "github.com/tech/sendico/payments/storage/model"
@@ -13,6 +14,7 @@ import (
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1" moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
oraclev1 "github.com/tech/sendico/pkg/proto/oracle/v1" oraclev1 "github.com/tech/sendico/pkg/proto/oracle/v1"
sharedv1 "github.com/tech/sendico/pkg/proto/payments/shared/v1" sharedv1 "github.com/tech/sendico/pkg/proto/payments/shared/v1"
"google.golang.org/protobuf/types/known/timestamppb"
) )
type moneyGetter interface { type moneyGetter interface {
@@ -158,6 +160,10 @@ func fxQuoteFromProto(quote *oraclev1.Quote) *paymenttypes.FXQuote {
if quote == nil { if quote == nil {
return nil return nil
} }
pricedAtUnixMs := int64(0)
if ts := quote.GetPricedAt(); ts != nil {
pricedAtUnixMs = ts.AsTime().UnixMilli()
}
return &paymenttypes.FXQuote{ return &paymenttypes.FXQuote{
QuoteRef: strings.TrimSpace(quote.GetQuoteRef()), QuoteRef: strings.TrimSpace(quote.GetQuoteRef()),
Pair: pairFromProto(quote.GetPair()), Pair: pairFromProto(quote.GetPair()),
@@ -166,6 +172,7 @@ func fxQuoteFromProto(quote *oraclev1.Quote) *paymenttypes.FXQuote {
BaseAmount: moneyFromProto(quote.GetBaseAmount()), BaseAmount: moneyFromProto(quote.GetBaseAmount()),
QuoteAmount: moneyFromProto(quote.GetQuoteAmount()), QuoteAmount: moneyFromProto(quote.GetQuoteAmount()),
ExpiresAtUnixMs: quote.GetExpiresAtUnixMs(), ExpiresAtUnixMs: quote.GetExpiresAtUnixMs(),
PricedAtUnixMs: pricedAtUnixMs,
Provider: strings.TrimSpace(quote.GetProvider()), Provider: strings.TrimSpace(quote.GetProvider()),
RateRef: strings.TrimSpace(quote.GetRateRef()), RateRef: strings.TrimSpace(quote.GetRateRef()),
Firm: quote.GetFirm(), Firm: quote.GetFirm(),
@@ -176,6 +183,10 @@ func fxQuoteToProto(quote *paymenttypes.FXQuote) *oraclev1.Quote {
if quote == nil { if quote == nil {
return nil return nil
} }
var pricedAt *timestamppb.Timestamp
if quote.PricedAtUnixMs > 0 {
pricedAt = timestamppb.New(time.UnixMilli(quote.PricedAtUnixMs).UTC())
}
return &oraclev1.Quote{ return &oraclev1.Quote{
QuoteRef: strings.TrimSpace(quote.QuoteRef), QuoteRef: strings.TrimSpace(quote.QuoteRef),
Pair: pairToProto(quote.Pair), Pair: pairToProto(quote.Pair),
@@ -184,6 +195,7 @@ func fxQuoteToProto(quote *paymenttypes.FXQuote) *oraclev1.Quote {
BaseAmount: protoMoney(quote.BaseAmount), BaseAmount: protoMoney(quote.BaseAmount),
QuoteAmount: protoMoney(quote.QuoteAmount), QuoteAmount: protoMoney(quote.QuoteAmount),
ExpiresAtUnixMs: quote.ExpiresAtUnixMs, ExpiresAtUnixMs: quote.ExpiresAtUnixMs,
PricedAt: pricedAt,
Provider: strings.TrimSpace(quote.Provider), Provider: strings.TrimSpace(quote.Provider),
RateRef: strings.TrimSpace(quote.RateRef), RateRef: strings.TrimSpace(quote.RateRef),
Firm: quote.Firm, Firm: quote.Firm,

View File

@@ -2,6 +2,7 @@ package quotation
import ( import (
"strings" "strings"
"time"
"github.com/tech/sendico/payments/storage/model" "github.com/tech/sendico/payments/storage/model"
chainasset "github.com/tech/sendico/pkg/chain" chainasset "github.com/tech/sendico/pkg/chain"
@@ -10,9 +11,11 @@ import (
accountingv1 "github.com/tech/sendico/pkg/proto/common/accounting/v1" accountingv1 "github.com/tech/sendico/pkg/proto/common/accounting/v1"
fxv1 "github.com/tech/sendico/pkg/proto/common/fx/v1" fxv1 "github.com/tech/sendico/pkg/proto/common/fx/v1"
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/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" chainv1 "github.com/tech/sendico/pkg/proto/gateway/chain/v1"
oraclev1 "github.com/tech/sendico/pkg/proto/oracle/v1" oraclev1 "github.com/tech/sendico/pkg/proto/oracle/v1"
sharedv1 "github.com/tech/sendico/pkg/proto/payments/shared/v1" sharedv1 "github.com/tech/sendico/pkg/proto/payments/shared/v1"
"google.golang.org/protobuf/types/known/timestamppb"
) )
func intentFromProto(src *sharedv1.PaymentIntent) model.PaymentIntent { func intentFromProto(src *sharedv1.PaymentIntent) model.PaymentIntent {
@@ -291,25 +294,25 @@ func modelKindFromProto(kind sharedv1.PaymentKind) model.PaymentKind {
} }
} }
func settlementModeFromProto(mode sharedv1.SettlementMode) model.SettlementMode { func settlementModeFromProto(mode paymentv1.SettlementMode) model.SettlementMode {
switch mode { switch mode {
case sharedv1.SettlementMode_SETTLEMENT_FIX_SOURCE: case paymentv1.SettlementMode_SETTLEMENT_FIX_SOURCE:
return model.SettlementModeFixSource return model.SettlementModeFixSource
case sharedv1.SettlementMode_SETTLEMENT_FIX_RECEIVED: case paymentv1.SettlementMode_SETTLEMENT_FIX_RECEIVED:
return model.SettlementModeFixReceived return model.SettlementModeFixReceived
default: default:
return model.SettlementModeUnspecified return model.SettlementModeUnspecified
} }
} }
func settlementModeToProto(mode model.SettlementMode) sharedv1.SettlementMode { func settlementModeToProto(mode model.SettlementMode) paymentv1.SettlementMode {
switch mode { switch mode {
case model.SettlementModeFixSource: case model.SettlementModeFixSource:
return sharedv1.SettlementMode_SETTLEMENT_FIX_SOURCE return paymentv1.SettlementMode_SETTLEMENT_FIX_SOURCE
case model.SettlementModeFixReceived: case model.SettlementModeFixReceived:
return sharedv1.SettlementMode_SETTLEMENT_FIX_RECEIVED return paymentv1.SettlementMode_SETTLEMENT_FIX_RECEIVED
default: default:
return sharedv1.SettlementMode_SETTLEMENT_UNSPECIFIED return paymentv1.SettlementMode_SETTLEMENT_UNSPECIFIED
} }
} }
@@ -423,6 +426,10 @@ func fxQuoteFromProto(quote *oraclev1.Quote) *paymenttypes.FXQuote {
if quote == nil { if quote == nil {
return nil return nil
} }
pricedAtUnixMs := int64(0)
if ts := quote.GetPricedAt(); ts != nil {
pricedAtUnixMs = ts.AsTime().UnixMilli()
}
return &paymenttypes.FXQuote{ return &paymenttypes.FXQuote{
QuoteRef: strings.TrimSpace(quote.GetQuoteRef()), QuoteRef: strings.TrimSpace(quote.GetQuoteRef()),
Pair: pairFromProto(quote.GetPair()), Pair: pairFromProto(quote.GetPair()),
@@ -431,6 +438,7 @@ func fxQuoteFromProto(quote *oraclev1.Quote) *paymenttypes.FXQuote {
BaseAmount: moneyFromProto(quote.GetBaseAmount()), BaseAmount: moneyFromProto(quote.GetBaseAmount()),
QuoteAmount: moneyFromProto(quote.GetQuoteAmount()), QuoteAmount: moneyFromProto(quote.GetQuoteAmount()),
ExpiresAtUnixMs: quote.GetExpiresAtUnixMs(), ExpiresAtUnixMs: quote.GetExpiresAtUnixMs(),
PricedAtUnixMs: pricedAtUnixMs,
Provider: strings.TrimSpace(quote.GetProvider()), Provider: strings.TrimSpace(quote.GetProvider()),
RateRef: strings.TrimSpace(quote.GetRateRef()), RateRef: strings.TrimSpace(quote.GetRateRef()),
Firm: quote.GetFirm(), Firm: quote.GetFirm(),
@@ -441,6 +449,10 @@ func fxQuoteToProto(quote *paymenttypes.FXQuote) *oraclev1.Quote {
if quote == nil { if quote == nil {
return nil return nil
} }
var pricedAt *timestamppb.Timestamp
if quote.PricedAtUnixMs > 0 {
pricedAt = timestamppb.New(time.UnixMilli(quote.PricedAtUnixMs).UTC())
}
return &oraclev1.Quote{ return &oraclev1.Quote{
QuoteRef: strings.TrimSpace(quote.QuoteRef), QuoteRef: strings.TrimSpace(quote.QuoteRef),
Pair: pairToProto(quote.Pair), Pair: pairToProto(quote.Pair),
@@ -449,6 +461,7 @@ func fxQuoteToProto(quote *paymenttypes.FXQuote) *oraclev1.Quote {
BaseAmount: protoMoney(quote.BaseAmount), BaseAmount: protoMoney(quote.BaseAmount),
QuoteAmount: protoMoney(quote.QuoteAmount), QuoteAmount: protoMoney(quote.QuoteAmount),
ExpiresAtUnixMs: quote.ExpiresAtUnixMs, ExpiresAtUnixMs: quote.ExpiresAtUnixMs,
PricedAt: pricedAt,
Provider: strings.TrimSpace(quote.Provider), Provider: strings.TrimSpace(quote.Provider),
RateRef: strings.TrimSpace(quote.RateRef), RateRef: strings.TrimSpace(quote.RateRef),
Firm: quote.Firm, Firm: quote.Firm,

View File

@@ -11,6 +11,7 @@ import (
"github.com/tech/sendico/payments/storage" "github.com/tech/sendico/payments/storage"
"github.com/tech/sendico/payments/storage/model" "github.com/tech/sendico/payments/storage/model"
quotestorage "github.com/tech/sendico/payments/storage/quote"
"github.com/tech/sendico/pkg/api/routers/gsresponse" "github.com/tech/sendico/pkg/api/routers/gsresponse"
"github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/merrors"
"github.com/tech/sendico/pkg/mlogger" "github.com/tech/sendico/pkg/mlogger"
@@ -114,7 +115,7 @@ func (h *quotePaymentCommand) prepareQuoteCtx(req *quotationv1.QuotePaymentReque
func (h *quotePaymentCommand) quotePayment( func (h *quotePaymentCommand) quotePayment(
ctx context.Context, ctx context.Context,
quotesStore storage.QuotesStore, quotesStore quotestorage.QuotesStore,
qc *quoteCtx, qc *quoteCtx,
req *quotationv1.QuotePaymentRequest, req *quotationv1.QuotePaymentRequest,
) (*quotePaymentResult, error) { ) (*quotePaymentResult, error) {
@@ -415,7 +416,7 @@ func (h *quotePaymentsCommand) prepare(req *quotationv1.QuotePaymentsRequest) (*
func (h *quotePaymentsCommand) tryReuse( func (h *quotePaymentsCommand) tryReuse(
ctx context.Context, ctx context.Context,
quotesStore storage.QuotesStore, quotesStore quotestorage.QuotesStore,
qc *quotePaymentsCtx, qc *quotePaymentsCtx,
) (*model.PaymentQuoteRecord, bool, error) { ) (*model.PaymentQuoteRecord, bool, error) {
@@ -515,7 +516,7 @@ func (h *quotePaymentsCommand) aggregate(
func (h *quotePaymentsCommand) storeBatch( func (h *quotePaymentsCommand) storeBatch(
ctx context.Context, ctx context.Context,
quotesStore storage.QuotesStore, quotesStore quotestorage.QuotesStore,
qc *quotePaymentsCtx, qc *quotePaymentsCtx,
quoteRef string, quoteRef string,
intents []*sharedv1.PaymentIntent, intents []*sharedv1.PaymentIntent,

View File

@@ -8,6 +8,7 @@ import (
"github.com/tech/sendico/payments/storage" "github.com/tech/sendico/payments/storage"
"github.com/tech/sendico/payments/storage/model" "github.com/tech/sendico/payments/storage/model"
quotestorage "github.com/tech/sendico/payments/storage/quote"
"github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/merrors"
mloggerfactory "github.com/tech/sendico/pkg/mlogger/factory" mloggerfactory "github.com/tech/sendico/pkg/mlogger/factory"
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1" moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
@@ -155,14 +156,15 @@ func (e *quoteCommandTestEngine) ResolvePaymentQuote(context.Context, quoteResol
func (e *quoteCommandTestEngine) Repository() storage.Repository { return e.repo } func (e *quoteCommandTestEngine) Repository() storage.Repository { return e.repo }
type quoteCommandTestRepo struct { type quoteCommandTestRepo struct {
quotes storage.QuotesStore quotes quotestorage.QuotesStore
} }
func (r quoteCommandTestRepo) Ping(context.Context) error { return nil } func (r quoteCommandTestRepo) Ping(context.Context) error { return nil }
func (r quoteCommandTestRepo) Payments() storage.PaymentsStore { return nil } func (r quoteCommandTestRepo) Payments() storage.PaymentsStore { return nil }
func (r quoteCommandTestRepo) Quotes() storage.QuotesStore { return r.quotes } func (r quoteCommandTestRepo) PaymentMethods() storage.PaymentMethodsStore { return nil }
func (r quoteCommandTestRepo) Routes() storage.RoutesStore { return nil } func (r quoteCommandTestRepo) Quotes() quotestorage.QuotesStore { return r.quotes }
func (r quoteCommandTestRepo) PlanTemplates() storage.PlanTemplatesStore { return nil } func (r quoteCommandTestRepo) Routes() storage.RoutesStore { return nil }
func (r quoteCommandTestRepo) PlanTemplates() storage.PlanTemplatesStore { return nil }
type quoteCommandTestQuotesStore struct { type quoteCommandTestQuotesStore struct {
byID map[string]*model.PaymentQuoteRecord byID map[string]*model.PaymentQuoteRecord

View File

@@ -8,13 +8,14 @@ import (
oracleclient "github.com/tech/sendico/fx/oracle/client" oracleclient "github.com/tech/sendico/fx/oracle/client"
chainv1 "github.com/tech/sendico/pkg/proto/gateway/chain/v1" chainv1 "github.com/tech/sendico/pkg/proto/gateway/chain/v1"
oraclev1 "github.com/tech/sendico/pkg/proto/oracle/v1" oraclev1 "github.com/tech/sendico/pkg/proto/oracle/v1"
sharedv1 "github.com/tech/sendico/pkg/proto/payments/shared/v1"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/timestamppb"
feesv1 "github.com/tech/sendico/pkg/proto/billing/fees/v1" feesv1 "github.com/tech/sendico/pkg/proto/billing/fees/v1"
accountingv1 "github.com/tech/sendico/pkg/proto/common/accounting/v1" accountingv1 "github.com/tech/sendico/pkg/proto/common/accounting/v1"
fxv1 "github.com/tech/sendico/pkg/proto/common/fx/v1" fxv1 "github.com/tech/sendico/pkg/proto/common/fx/v1"
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1" moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
paymentv1 "github.com/tech/sendico/pkg/proto/common/payment/v1"
) )
type moneyGetter interface { type moneyGetter interface {
@@ -173,7 +174,7 @@ func resolveTradeAmounts(intentAmount *moneyv1.Money, fxQuote *oraclev1.Quote, s
} }
} }
func computeAggregates(pay, settlement, fee *moneyv1.Money, network *chainv1.EstimateTransferFeeResponse, fxQuote *oraclev1.Quote, mode sharedv1.SettlementMode) (*moneyv1.Money, *moneyv1.Money) { func computeAggregates(pay, settlement, fee *moneyv1.Money, network *chainv1.EstimateTransferFeeResponse, fxQuote *oraclev1.Quote, mode paymentv1.SettlementMode) (*moneyv1.Money, *moneyv1.Money) {
if pay == nil { if pay == nil {
return nil, nil return nil, nil
} }
@@ -215,7 +216,7 @@ func computeAggregates(pay, settlement, fee *moneyv1.Money, network *chainv1.Est
} }
switch mode { switch mode {
case sharedv1.SettlementMode_SETTLEMENT_FIX_RECEIVED: case paymentv1.SettlementMode_SETTLEMENT_FIX_RECEIVED:
// Sender pays the fee: keep settlement fixed, increase debit. // Sender pays the fee: keep settlement fixed, increase debit.
applyChargeToDebit(fee) applyChargeToDebit(fee)
default: default:
@@ -225,7 +226,7 @@ func computeAggregates(pay, settlement, fee *moneyv1.Money, network *chainv1.Est
if network != nil && network.GetNetworkFee() != nil { if network != nil && network.GetNetworkFee() != nil {
switch mode { switch mode {
case sharedv1.SettlementMode_SETTLEMENT_FIX_RECEIVED: case paymentv1.SettlementMode_SETTLEMENT_FIX_RECEIVED:
applyChargeToDebit(network.GetNetworkFee()) applyChargeToDebit(network.GetNetworkFee())
default: default:
applyChargeToSettlement(network.GetNetworkFee()) applyChargeToSettlement(network.GetNetworkFee())
@@ -293,6 +294,10 @@ func quoteToProto(src *oracleclient.Quote) *oraclev1.Quote {
if src == nil { if src == nil {
return nil return nil
} }
var pricedAt *timestamppb.Timestamp
if !src.PricedAt.IsZero() {
pricedAt = timestamppb.New(src.PricedAt.UTC())
}
return &oraclev1.Quote{ return &oraclev1.Quote{
QuoteRef: src.QuoteRef, QuoteRef: src.QuoteRef,
Pair: src.Pair, Pair: src.Pair,
@@ -301,6 +306,7 @@ func quoteToProto(src *oracleclient.Quote) *oraclev1.Quote {
BaseAmount: cloneProtoMoney(src.BaseAmount), BaseAmount: cloneProtoMoney(src.BaseAmount),
QuoteAmount: cloneProtoMoney(src.QuoteAmount), QuoteAmount: cloneProtoMoney(src.QuoteAmount),
ExpiresAtUnixMs: src.ExpiresAt.UnixMilli(), ExpiresAtUnixMs: src.ExpiresAt.UnixMilli(),
PricedAt: pricedAt,
Provider: src.Provider, Provider: src.Provider,
RateRef: src.RateRef, RateRef: src.RateRef,
Firm: src.Firm, Firm: src.Firm,

View File

@@ -119,6 +119,7 @@ func cloneStoredFXQuote(src *paymenttypes.FXQuote) *paymenttypes.FXQuote {
QuoteRef: strings.TrimSpace(src.QuoteRef), QuoteRef: strings.TrimSpace(src.QuoteRef),
Side: src.Side, Side: src.Side,
ExpiresAtUnixMs: src.ExpiresAtUnixMs, ExpiresAtUnixMs: src.ExpiresAtUnixMs,
PricedAtUnixMs: src.PricedAtUnixMs,
Provider: strings.TrimSpace(src.Provider), Provider: strings.TrimSpace(src.Provider),
RateRef: strings.TrimSpace(src.RateRef), RateRef: strings.TrimSpace(src.RateRef),
Firm: src.Firm, Firm: src.Firm,

View File

@@ -7,6 +7,7 @@ import (
"github.com/tech/sendico/payments/storage" "github.com/tech/sendico/payments/storage"
"github.com/tech/sendico/payments/storage/model" "github.com/tech/sendico/payments/storage/model"
quotestorage "github.com/tech/sendico/payments/storage/quote"
"github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/merrors"
quotationv1 "github.com/tech/sendico/pkg/proto/payments/quotation/v1" quotationv1 "github.com/tech/sendico/pkg/proto/payments/quotation/v1"
sharedv1 "github.com/tech/sendico/pkg/proto/payments/shared/v1" sharedv1 "github.com/tech/sendico/pkg/proto/payments/shared/v1"
@@ -42,7 +43,7 @@ func requireNonNilIntent(intent *sharedv1.PaymentIntent) error {
return nil return nil
} }
func ensureQuotesStore(repo storage.Repository) (storage.QuotesStore, error) { func ensureQuotesStore(repo storage.Repository) (quotestorage.QuotesStore, error) {
if repo == nil { if repo == nil {
return nil, errStorageUnavailable return nil, errStorageUnavailable
} }

View File

@@ -16,6 +16,8 @@ github.com/casbin/mongodb-adapter/v4 v4.3.0 h1:yYXky9v1by6vj/0QK7OyHyd/xpz4vzh0l
github.com/casbin/mongodb-adapter/v4 v4.3.0/go.mod h1:bOTSYZUjX7I9E0ExEvgq46m3mcDNRII7g8iWjrM1BHE= github.com/casbin/mongodb-adapter/v4 v4.3.0/go.mod h1:bOTSYZUjX7I9E0ExEvgq46m3mcDNRII7g8iWjrM1BHE=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
@@ -120,12 +122,12 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=

View File

@@ -7,11 +7,15 @@ import (
"github.com/tech/sendico/payments/storage" "github.com/tech/sendico/payments/storage"
"github.com/tech/sendico/payments/storage/model" "github.com/tech/sendico/payments/storage/model"
"github.com/tech/sendico/payments/storage/mongo/store" "github.com/tech/sendico/payments/storage/mongo/store"
quotestorage "github.com/tech/sendico/payments/storage/quote"
quotemongo "github.com/tech/sendico/payments/storage/quote/mongo" quotemongo "github.com/tech/sendico/payments/storage/quote/mongo"
"github.com/tech/sendico/pkg/auth"
"github.com/tech/sendico/pkg/db" "github.com/tech/sendico/pkg/db"
"github.com/tech/sendico/pkg/db/repository" "github.com/tech/sendico/pkg/db/repository"
"github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/merrors"
"github.com/tech/sendico/pkg/mlogger" "github.com/tech/sendico/pkg/mlogger"
"github.com/tech/sendico/pkg/mservice"
"go.mongodb.org/mongo-driver/v2/bson"
) )
// Store implements storage.Repository backed by MongoDB. // Store implements storage.Repository backed by MongoDB.
@@ -20,13 +24,20 @@ type Store struct {
ping func(context.Context) error ping func(context.Context) error
payments storage.PaymentsStore payments storage.PaymentsStore
quotes storage.QuotesStore methods storage.PaymentMethodsStore
quotes quotestorage.QuotesStore
routes storage.RoutesStore routes storage.RoutesStore
plans storage.PlanTemplatesStore plans storage.PlanTemplatesStore
} }
type paymentMethodsConfig struct {
enforcer auth.Enforcer
permissionRef bson.ObjectID
}
type options struct { type options struct {
quoteRetention time.Duration quoteRetention time.Duration
paymentMethodsAuth *paymentMethodsConfig
} }
// Option configures the Mongo-backed payments repository. // Option configures the Mongo-backed payments repository.
@@ -39,6 +50,16 @@ func WithQuoteRetention(retention time.Duration) Option {
} }
} }
// WithPaymentMethodsAuth enables the payment-methods store and permission checks.
func WithPaymentMethodsAuth(enforcer auth.Enforcer, permissionRef bson.ObjectID) Option {
return func(opts *options) {
opts.paymentMethodsAuth = &paymentMethodsConfig{
enforcer: enforcer,
permissionRef: permissionRef,
}
}
}
// New constructs a Mongo-backed payments repository from a Mongo connection. // New constructs a Mongo-backed payments repository from a Mongo connection.
func New(logger mlogger.Logger, conn *db.MongoConnection, opts ...Option) (*Store, error) { func New(logger mlogger.Logger, conn *db.MongoConnection, opts ...Option) (*Store, error) {
if conn == nil { if conn == nil {
@@ -48,11 +69,22 @@ func New(logger mlogger.Logger, conn *db.MongoConnection, opts ...Option) (*Stor
quotesRepo := repository.CreateMongoRepository(conn.Database(), (&model.PaymentQuoteRecord{}).Collection()) quotesRepo := repository.CreateMongoRepository(conn.Database(), (&model.PaymentQuoteRecord{}).Collection())
routesRepo := repository.CreateMongoRepository(conn.Database(), (&model.PaymentRoute{}).Collection()) routesRepo := repository.CreateMongoRepository(conn.Database(), (&model.PaymentRoute{}).Collection())
plansRepo := repository.CreateMongoRepository(conn.Database(), (&model.PaymentPlanTemplate{}).Collection()) plansRepo := repository.CreateMongoRepository(conn.Database(), (&model.PaymentPlanTemplate{}).Collection())
return NewWithRepository(logger, conn.Ping, paymentsRepo, quotesRepo, routesRepo, plansRepo, opts...) methodsRepo := repository.CreateMongoRepository(conn.Database(), mservice.PaymentMethods)
return newWithRepository(logger, conn.Ping, paymentsRepo, methodsRepo, quotesRepo, routesRepo, plansRepo, opts...)
} }
// NewWithRepository constructs a payments repository using the provided primitives. // NewWithRepository constructs a payments repository using the provided primitives.
func NewWithRepository(logger mlogger.Logger, ping func(context.Context) error, paymentsRepo repository.Repository, quotesRepo repository.Repository, routesRepo repository.Repository, plansRepo repository.Repository, opts ...Option) (*Store, error) { func NewWithRepository(logger mlogger.Logger, ping func(context.Context) error, paymentsRepo repository.Repository, quotesRepo repository.Repository, routesRepo repository.Repository, plansRepo repository.Repository, opts ...Option) (*Store, error) {
return newWithRepository(logger, ping, paymentsRepo, nil, quotesRepo, routesRepo, plansRepo, opts...)
}
func newWithRepository(
logger mlogger.Logger,
ping func(context.Context) error,
paymentsRepo, methodsRepo, quotesRepo, routesRepo, plansRepo repository.Repository,
opts ...Option,
) (*Store, error) {
if ping == nil { if ping == nil {
return nil, merrors.InvalidArgument("payments.storage.mongo: ping func is nil") return nil, merrors.InvalidArgument("payments.storage.mongo: ping func is nil")
} }
@@ -93,10 +125,30 @@ func NewWithRepository(logger mlogger.Logger, ping func(context.Context) error,
if err != nil { if err != nil {
return nil, err return nil, err
} }
var methodsStore storage.PaymentMethodsStore
if cfg.paymentMethodsAuth != nil {
if methodsRepo == nil {
return nil, merrors.InvalidArgument("payments.storage.mongo: payment methods repository is nil")
}
if cfg.paymentMethodsAuth.enforcer == nil {
return nil, merrors.InvalidArgument("payments.storage.mongo: payment methods enforcer is nil")
}
if cfg.paymentMethodsAuth.permissionRef == bson.NilObjectID {
return nil, merrors.InvalidArgument("payments.storage.mongo: payment methods permission reference is required")
}
methodsStore, err = store.NewPaymentMethods(childLogger, methodsRepo, cfg.paymentMethodsAuth.enforcer, cfg.paymentMethodsAuth.permissionRef)
if err != nil {
return nil, err
}
}
result := &Store{ result := &Store{
logger: childLogger, logger: childLogger,
ping: ping, ping: ping,
payments: paymentsStore, payments: paymentsStore,
methods: methodsStore,
quotes: quotesRepoStore.Quotes(), quotes: quotesRepoStore.Quotes(),
routes: routesStore, routes: routesStore,
plans: plansStore, plans: plansStore,
@@ -118,8 +170,13 @@ func (s *Store) Payments() storage.PaymentsStore {
return s.payments return s.payments
} }
// PaymentMethods returns the payment-methods store.
func (s *Store) PaymentMethods() storage.PaymentMethodsStore {
return s.methods
}
// Quotes returns the quotes store. // Quotes returns the quotes store.
func (s *Store) Quotes() storage.QuotesStore { func (s *Store) Quotes() quotestorage.QuotesStore {
return s.quotes return s.quotes
} }

Some files were not shown because too many files have changed in this diff Show More