From 2be76aa5193169688bbfe91baa4b962c9ccb1f9b Mon Sep 17 00:00:00 2001 From: Stephan D Date: Mon, 2 Mar 2026 16:27:33 +0100 Subject: [PATCH] Callbacks service docs updated --- .../bff/interface/api/sresponse/callback.go | 33 ++ .../bff/internal/api/routers/tokens/tokens.go | 4 +- .../internal/server/callbacksimp/create.go | 47 +++ .../callbacksimp/{handlers.go => rotate.go} | 160 +++----- .../internal/server/callbacksimp/secrets.go | 2 +- .../internal/server/callbacksimp/service.go | 3 + .../internal/server/callbacksimp/update.go | 59 +++ .../callbacks/internal/delivery/service.go | 3 +- api/edge/callbacks/internal/events/module.go | 26 +- api/edge/callbacks/internal/events/service.go | 16 +- api/edge/callbacks/internal/ingest/service.go | 19 +- api/edge/callbacks/internal/model/callback.go | 8 + api/edge/callbacks/internal/model/endpoint.go | 22 + api/edge/callbacks/internal/model/task.go | 37 ++ api/edge/callbacks/internal/storage/module.go | 52 +-- .../callbacks/internal/storage/service.go | 121 +++--- .../internal/subscriptions/module.go | 4 +- .../internal/subscriptions/service.go | 8 +- api/gateway/chain/go.mod | 2 +- api/gateway/chain/go.sum | 4 +- api/gateway/tron/go.mod | 2 +- api/gateway/tron/go.sum | 4 +- .../orchestrationv2/psvc/status_publish.go | 3 +- api/pkg/db/callbacks/callbacks.go | 3 + .../db/internal/mongo/callbacksdb/callback.go | 8 + api/pkg/db/internal/mongo/callbacksdb/db.go | 35 +- .../internal/mongo/callbacksdb/secretref.go | 60 +++ .../internal/mongo/verificationimp/consume.go | 33 +- .../internal/mongo/verificationimp/create.go | 8 - api/pkg/go.mod | 2 +- api/pkg/go.sum | 4 +- api/pkg/model/callback.go | 10 +- api/pkg/model/payment_orchestrator_status.go | 27 +- api/pkg/model/refresh.go | 15 +- api/server/go.sum | 388 ------------------ ci/dev/callbacks.dockerfile | 4 +- ci/dev/entrypoints/callbacks.sh | 17 + interface/api.yaml | 2 - interface/api/accounts/bodies/auth.yaml | 3 + interface/api/accounts/request/auth.yaml | 1 + interface/api/accounts/response/auth.yaml | 13 + interface/api/callbacks/bodies/callback.yaml | 2 +- interface/api/callbacks/request/callback.yaml | 4 +- .../api/callbacks/response/callback.yaml | 5 +- interface/api/callbacks/rotate_secret.yaml | 2 +- .../organizations/response/organization.yaml | 3 + interface/api/parameters/callbacks_ref.yaml | 2 +- interface/api/parameters/org_ref.yaml | 2 +- .../api/parameters/payment_methods_ref.yaml | 2 +- interface/api/parameters/recipients_ref.yaml | 2 +- .../bodies/payment_method.yaml | 1 + .../request/payment_method.yaml | 1 + .../response/payment_method.yaml | 3 + interface/api/payments/bodies/payment.yaml | 4 + interface/api/payments/request/payment.yaml | 18 + interface/api/payments/response/payment.yaml | 17 + .../api/recipients/bodies/recipient.yaml | 1 + .../api/recipients/request/recipient.yaml | 1 + .../api/recipients/response/recipient.yaml | 3 + interface/api/response/error.yaml | 5 + .../api/verification/bodies/verification.yaml | 2 + .../verification/response/verification.yaml | 6 + interface/models/account/account.yaml | 3 + interface/models/account/account_public.yaml | 7 + .../models/auth/client_refresh_token.yaml | 3 + interface/models/auth/login_data.yaml | 4 + interface/models/auth/session_identifier.yaml | 3 + interface/models/auth/token_data.yaml | 3 + interface/models/callback/callback.yaml | 54 ++- interface/models/common/describable.yaml | 3 + interface/models/common/money.yaml | 2 + interface/models/common/pagination.yaml | 2 + interface/models/objectid.yaml | 2 + .../models/organization/organization.yaml | 3 - interface/models/payment/payment.yaml | 111 +++++ interface/models/permission_bound.yaml | 5 + interface/models/storable.yaml | 4 + 77 files changed, 803 insertions(+), 764 deletions(-) create mode 100644 api/edge/bff/interface/api/sresponse/callback.go create mode 100644 api/edge/bff/internal/server/callbacksimp/create.go rename api/edge/bff/internal/server/callbacksimp/{handlers.go => rotate.go} (59%) create mode 100644 api/edge/bff/internal/server/callbacksimp/update.go create mode 100644 api/edge/callbacks/internal/model/callback.go create mode 100644 api/edge/callbacks/internal/model/endpoint.go create mode 100644 api/edge/callbacks/internal/model/task.go create mode 100644 api/pkg/db/internal/mongo/callbacksdb/callback.go create mode 100644 api/pkg/db/internal/mongo/callbacksdb/secretref.go delete mode 100644 api/server/go.sum create mode 100644 ci/dev/entrypoints/callbacks.sh diff --git a/api/edge/bff/interface/api/sresponse/callback.go b/api/edge/bff/interface/api/sresponse/callback.go new file mode 100644 index 00000000..7a7a2eb0 --- /dev/null +++ b/api/edge/bff/interface/api/sresponse/callback.go @@ -0,0 +1,33 @@ +package sresponse + +import ( + "net/http" + + "github.com/tech/sendico/pkg/api/http/response" + "github.com/tech/sendico/pkg/mlogger" + "github.com/tech/sendico/pkg/model" +) + +type callbackWriteResponse struct { + AccessToken TokenData `json:"accessToken"` + Callbacks []model.Callback `json:"callbacks"` + GeneratedSigningSecret string `json:"generatedSigningSecret,omitempty"` +} + +func Callback( + logger mlogger.Logger, + callback *model.Callback, + accessToken *TokenData, + generatedSecret string, + created bool, +) http.HandlerFunc { + resp := callbackWriteResponse{ + AccessToken: *accessToken, + Callbacks: []model.Callback{*callback}, + GeneratedSigningSecret: generatedSecret, + } + if created { + return response.Created(logger, resp) + } + return response.Ok(logger, resp) +} diff --git a/api/edge/bff/internal/api/routers/tokens/tokens.go b/api/edge/bff/internal/api/routers/tokens/tokens.go index e7685321..cc57d734 100644 --- a/api/edge/bff/internal/api/routers/tokens/tokens.go +++ b/api/edge/bff/internal/api/routers/tokens/tokens.go @@ -42,9 +42,7 @@ func PrepareRefreshToken( } token := &model.RefreshToken{ - AccountBoundBase: model.AccountBoundBase{ - AccountRef: account.GetID(), - }, + AccountRef: account.GetID(), ClientRefreshToken: model.ClientRefreshToken{ SessionIdentifier: *session, RefreshToken: refreshToken, diff --git a/api/edge/bff/internal/server/callbacksimp/create.go b/api/edge/bff/internal/server/callbacksimp/create.go new file mode 100644 index 00000000..6dff4e6a --- /dev/null +++ b/api/edge/bff/internal/server/callbacksimp/create.go @@ -0,0 +1,47 @@ +package callbacksimp + +import ( + "context" + "encoding/json" + "net/http" + + "github.com/tech/sendico/pkg/api/http/response" + "github.com/tech/sendico/pkg/model" + "github.com/tech/sendico/server/interface/api/sresponse" + mutil "github.com/tech/sendico/server/internal/mutil/param" + "go.uber.org/zap" +) + +func (a *CallbacksAPI) create(r *http.Request, account *model.Account, accessToken *sresponse.TokenData) http.HandlerFunc { + organizationRef, err := a.Oph.GetRef(r) + if err != nil { + a.Logger.Warn("Failed to parse organization reference", zap.Error(err), mutil.PLog(a.Oph, r)) + return response.BadReference(a.Logger, a.Name(), a.Oph.Name(), a.Oph.GetID(r), err) + } + + var callback model.Callback + if err := json.NewDecoder(r.Body).Decode(&callback); err != nil { + a.Logger.Warn("Failed to decode callback payload", zap.Error(err)) + return response.BadPayload(a.Logger, a.Name(), err) + } + + mutation, err := a.normalizeAndPrepare(r.Context(), &callback, organizationRef, "", true) + if err != nil { + return response.Auto(a.Logger, a.Name(), err) + } + + if _, err := a.tf.CreateTransaction().Execute(r.Context(), func(ctx context.Context) (any, error) { + if err := a.DB.Create(ctx, *account.GetID(), organizationRef, &callback); err != nil { + return nil, err + } + if err := a.applySigningSecretMutation(ctx, *account.GetID(), *callback.GetID(), mutation); err != nil { + return nil, err + } + return nil, nil + }); err != nil { + a.Logger.Warn("Failed to create callback transaction", zap.Error(err)) + return response.Auto(a.Logger, a.Name(), err) + } + + return a.callbackResponse(&callback, accessToken, mutation.Generated, true) +} diff --git a/api/edge/bff/internal/server/callbacksimp/handlers.go b/api/edge/bff/internal/server/callbacksimp/rotate.go similarity index 59% rename from api/edge/bff/internal/server/callbacksimp/handlers.go rename to api/edge/bff/internal/server/callbacksimp/rotate.go index 20e7a111..4ff35c0d 100644 --- a/api/edge/bff/internal/server/callbacksimp/handlers.go +++ b/api/edge/bff/internal/server/callbacksimp/rotate.go @@ -2,7 +2,7 @@ package callbacksimp import ( "context" - "encoding/json" + "errors" "net/http" "net/url" "strings" @@ -16,68 +16,10 @@ import ( "go.uber.org/zap" ) -type callbackWriteResponse struct { - AccessToken sresponse.TokenData `json:"accessToken"` - Callbacks []model.Callback `json:"callbacks"` - GeneratedSigningSecret string `json:"generatedSigningSecret,omitempty"` -} - -func (a *CallbacksAPI) create(r *http.Request, account *model.Account, accessToken *sresponse.TokenData) http.HandlerFunc { - organizationRef, err := a.Oph.GetRef(r) - if err != nil { - a.Logger.Warn("Failed to parse organization reference", zap.Error(err), mutil.PLog(a.Oph, r)) - return response.BadReference(a.Logger, a.Name(), a.Oph.Name(), a.Oph.GetID(r), err) - } - - var callback model.Callback - if err := json.NewDecoder(r.Body).Decode(&callback); err != nil { - a.Logger.Warn("Failed to decode callback payload", zap.Error(err)) - return response.BadPayload(a.Logger, a.Name(), err) - } - - generatedSecret, err := a.normalizeAndPrepare(r.Context(), &callback, organizationRef, true) - if err != nil { - return response.Auto(a.Logger, a.Name(), err) - } - - if err := a.DB.Create(r.Context(), *account.GetID(), organizationRef, &callback); err != nil { - a.Logger.Warn("Failed to create callback", zap.Error(err)) - return response.Auto(a.Logger, a.Name(), err) - } - - return a.callbackResponse(&callback, accessToken, generatedSecret, true) -} - -func (a *CallbacksAPI) update(r *http.Request, account *model.Account, accessToken *sresponse.TokenData) http.HandlerFunc { - var input model.Callback - if err := json.NewDecoder(r.Body).Decode(&input); err != nil { - a.Logger.Warn("Failed to decode callback payload", zap.Error(err)) - return response.BadPayload(a.Logger, a.Name(), err) - } - - callbackRef := *input.GetID() - if callbackRef.IsZero() { - return response.Auto(a.Logger, a.Name(), merrors.InvalidArgument("callback id is required", "id")) - } - - var existing model.Callback - if err := a.db.Get(r.Context(), *account.GetID(), callbackRef, &existing); err != nil { - a.Logger.Warn("Failed to fetch callback before update", zap.Error(err)) - return response.Auto(a.Logger, a.Name(), err) - } - - mergeCallbackMutable(&existing, &input) - generatedSecret, err := a.normalizeAndPrepare(r.Context(), &existing, existing.OrganizationRef, true) - if err != nil { - return response.Auto(a.Logger, a.Name(), err) - } - - if err := a.DB.Update(r.Context(), *account.GetID(), &existing); err != nil { - a.Logger.Warn("Failed to update callback", zap.Error(err)) - return response.Auto(a.Logger, a.Name(), err) - } - - return a.callbackResponse(&existing, accessToken, generatedSecret, false) +type signingSecretMutation struct { + SetSecretRef string + Clear bool + Generated string } func (a *CallbacksAPI) rotateSecret(r *http.Request, account *model.Account, accessToken *sresponse.TokenData) http.HandlerFunc { @@ -102,10 +44,8 @@ func (a *CallbacksAPI) rotateSecret(r *http.Request, account *model.Account, acc a.Logger.Warn("Failed to rotate callback signing secret", zap.Error(err)) return response.Auto(a.Logger, a.Name(), err) } - callback.RetryPolicy.SecretRef = secretRef - - if err := a.DB.Update(r.Context(), *account.GetID(), &callback); err != nil { - a.Logger.Warn("Failed to persist rotated callback secret reference", zap.Error(err)) + if err := a.db.SetSigningSecretRef(r.Context(), *account.GetID(), callbackRef, secretRef); err != nil { + a.Logger.Warn("Failed to persist rotated callback signing secret reference", zap.Error(err)) return response.Auto(a.Logger, a.Name(), err) } @@ -116,29 +56,25 @@ func (a *CallbacksAPI) normalizeAndPrepare( ctx context.Context, callback *model.Callback, organizationRef bson.ObjectID, + existingSecretRef string, allowSecretGeneration bool, -) (string, error) { +) (signingSecretMutation, error) { if callback == nil { - return "", merrors.InvalidArgument("callback payload is required") + return signingSecretMutation{}, merrors.InvalidArgument("callback payload is required") } if organizationRef.IsZero() { - return "", merrors.InvalidArgument("organization reference is required", "organizationRef") + return signingSecretMutation{}, merrors.InvalidArgument("organization reference is required", "organizationRef") } - callback.SetOrganizationRef(organizationRef) callback.Name = strings.TrimSpace(callback.Name) callback.Description = trimDescription(callback.Description) - callback.ClientID = strings.TrimSpace(callback.ClientID) - if callback.ClientID == "" { - callback.ClientID = organizationRef.Hex() - } callback.URL = strings.TrimSpace(callback.URL) if callback.URL == "" { - return "", merrors.InvalidArgument("url is required", "url") + return signingSecretMutation{}, merrors.InvalidArgument("url is required", "url") } if err := validateCallbackURL(callback.URL); err != nil { - return "", err + return signingSecretMutation{}, err } if callback.Name == "" { callback.Name = callback.URL @@ -146,15 +82,15 @@ func (a *CallbacksAPI) normalizeAndPrepare( status, err := normalizeStatus(callback.Status, a.config.DefaultStatus) if err != nil { - return "", err + return signingSecretMutation{}, err } callback.Status = status callback.EventTypes = normalizeEventTypes(callback.EventTypes, a.config.DefaultEventTypes) - callback.RetryPolicy.MinDelayMS = defaultInt(callback.RetryPolicy.MinDelayMS, defaultRetryMinDelayMS) - callback.RetryPolicy.MaxDelayMS = defaultInt(callback.RetryPolicy.MaxDelayMS, defaultRetryMaxDelayMS) - if callback.RetryPolicy.MaxDelayMS < callback.RetryPolicy.MinDelayMS { - callback.RetryPolicy.MaxDelayMS = callback.RetryPolicy.MinDelayMS + callback.RetryPolicy.Backoff.MinDelayMS = defaultInt(callback.RetryPolicy.Backoff.MinDelayMS, defaultRetryMinDelayMS) + callback.RetryPolicy.Backoff.MaxDelayMS = defaultInt(callback.RetryPolicy.Backoff.MaxDelayMS, defaultRetryMaxDelayMS) + if callback.RetryPolicy.Backoff.MaxDelayMS < callback.RetryPolicy.Backoff.MinDelayMS { + callback.RetryPolicy.Backoff.MaxDelayMS = callback.RetryPolicy.Backoff.MinDelayMS } callback.RetryPolicy.MaxAttempts = defaultInt(callback.RetryPolicy.MaxAttempts, defaultRetryMaxAttempts) callback.RetryPolicy.RequestTimeoutMS = defaultInt(callback.RetryPolicy.RequestTimeoutMS, defaultRetryRequestTimeoutMS) @@ -162,36 +98,55 @@ func (a *CallbacksAPI) normalizeAndPrepare( mode, err := normalizeSigningMode(callback.RetryPolicy.SigningMode) if err != nil { - return "", err + return signingSecretMutation{}, err } callback.RetryPolicy.SigningMode = mode - callback.RetryPolicy.SecretRef = strings.TrimSpace(callback.RetryPolicy.SecretRef) + existingSecretRef = strings.TrimSpace(existingSecretRef) switch callback.RetryPolicy.SigningMode { case model.CallbackSigningModeNone: - callback.RetryPolicy.SecretRef = "" - return "", nil + return signingSecretMutation{Clear: existingSecretRef != ""}, nil case model.CallbackSigningModeHMACSHA256: - if callback.RetryPolicy.SecretRef != "" { - return "", nil + if existingSecretRef != "" { + return signingSecretMutation{SetSecretRef: existingSecretRef}, nil } if !allowSecretGeneration { - return "", merrors.InvalidArgument("secretRef is required for hmac_sha256 callbacks", "retryPolicy.secretRef") + return signingSecretMutation{}, merrors.InvalidArgument("signing secret is required for hmac_sha256 callbacks", "retryPolicy.signingMode") } if callback.GetID().IsZero() { callback.SetID(bson.NewObjectID()) } secretRef, generatedSecret, err := a.secrets.Provision(ctx, organizationRef, *callback.GetID()) if err != nil { - return "", err + return signingSecretMutation{}, err } - callback.RetryPolicy.SecretRef = secretRef - return generatedSecret, nil + return signingSecretMutation{SetSecretRef: secretRef, Generated: generatedSecret}, nil default: - return "", merrors.InvalidArgument("unsupported signing mode", "retryPolicy.signingMode") + return signingSecretMutation{}, merrors.InvalidArgument("unsupported signing mode", "retryPolicy.signingMode") } } +func (a *CallbacksAPI) applySigningSecretMutation( + ctx context.Context, + accountRef, + callbackRef bson.ObjectID, + mutation signingSecretMutation, +) error { + if callbackRef.IsZero() { + return merrors.InvalidArgument("callback reference is required", "callbackRef") + } + if strings.TrimSpace(mutation.SetSecretRef) != "" { + return a.db.SetSigningSecretRef(ctx, accountRef, callbackRef, mutation.SetSecretRef) + } + if mutation.Clear { + err := a.db.ClearSigningSecretRef(ctx, accountRef, callbackRef) + if err != nil && !errors.Is(err, merrors.ErrNoData) { + return err + } + } + return nil +} + func (a *CallbacksAPI) callbackResponse( callback *model.Callback, accessToken *sresponse.TokenData, @@ -202,15 +157,7 @@ func (a *CallbacksAPI) callbackResponse( return response.Internal(a.Logger, a.Name(), merrors.Internal("failed to build callback response")) } - resp := callbackWriteResponse{ - AccessToken: *accessToken, - Callbacks: []model.Callback{*callback}, - GeneratedSigningSecret: generatedSecret, - } - if created { - return response.Created(a.Logger, resp) - } - return response.Ok(a.Logger, resp) + return sresponse.Callback(a.Logger, callback, accessToken, generatedSecret, created) } func normalizeStatus(raw, fallback model.CallbackStatus) (model.CallbackStatus, error) { @@ -286,16 +233,17 @@ func normalizeHeaders(headers map[string]string) map[string]string { } func mergeCallbackMutable(dst, src *model.Callback) { + dst.OrganizationRef = src.OrganizationRef dst.Describable = src.Describable - dst.ClientID = src.ClientID dst.Status = src.Status dst.URL = src.URL dst.EventTypes = append([]string(nil), src.EventTypes...) dst.RetryPolicy = model.CallbackRetryPolicy{ - MinDelayMS: src.RetryPolicy.MinDelayMS, - MaxDelayMS: src.RetryPolicy.MaxDelayMS, + Backoff: model.CallbackBackoff{ + MinDelayMS: src.RetryPolicy.Backoff.MinDelayMS, + MaxDelayMS: src.RetryPolicy.Backoff.MaxDelayMS, + }, SigningMode: src.RetryPolicy.SigningMode, - SecretRef: src.RetryPolicy.SecretRef, Headers: normalizeHeaders(src.RetryPolicy.Headers), MaxAttempts: src.RetryPolicy.MaxAttempts, RequestTimeoutMS: src.RetryPolicy.RequestTimeoutMS, diff --git a/api/edge/bff/internal/server/callbacksimp/secrets.go b/api/edge/bff/internal/server/callbacksimp/secrets.go index 1467cad2..bb8de148 100644 --- a/api/edge/bff/internal/server/callbacksimp/secrets.go +++ b/api/edge/bff/internal/server/callbacksimp/secrets.go @@ -81,7 +81,7 @@ func newSigningSecretManager(logger mlogger.Logger, cfg callbacksConfig) (signin } if isVaultConfigEmpty(cfg.Vault) { - manager.logger.Warn("Callbacks Vault config is not set; secret generation requires explicit secretRef in payloads") + manager.logger.Warn("Callbacks Vault config is not set; hmac signing secret generation is disabled") ensureSigningSecretMetrics() return manager, nil } diff --git a/api/edge/bff/internal/server/callbacksimp/service.go b/api/edge/bff/internal/server/callbacksimp/service.go index b6d00baf..c31ee162 100644 --- a/api/edge/bff/internal/server/callbacksimp/service.go +++ b/api/edge/bff/internal/server/callbacksimp/service.go @@ -5,6 +5,7 @@ import ( api "github.com/tech/sendico/pkg/api/http" "github.com/tech/sendico/pkg/db/callbacks" + "github.com/tech/sendico/pkg/db/transaction" "github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/model" "github.com/tech/sendico/pkg/mservice" @@ -16,6 +17,7 @@ import ( type CallbacksAPI struct { papitemplate.ProtectedAPI[model.Callback] db callbacks.DB + tf transaction.Factory secrets signingSecretManager config callbacksConfig } @@ -35,6 +37,7 @@ func CreateAPI(apiCtx eapi.API) (*CallbacksAPI, error) { res := &CallbacksAPI{ config: newCallbacksConfig(apiCtx.Config().Callbacks), + tf: apiCtx.DBFactory().TransactionFactory(), } p, err := papitemplate.CreateAPI(apiCtx, dbFactory, mservice.Organizations, mservice.Callbacks) diff --git a/api/edge/bff/internal/server/callbacksimp/update.go b/api/edge/bff/internal/server/callbacksimp/update.go new file mode 100644 index 00000000..4ee3190e --- /dev/null +++ b/api/edge/bff/internal/server/callbacksimp/update.go @@ -0,0 +1,59 @@ +package callbacksimp + +import ( + "context" + "encoding/json" + "errors" + "net/http" + + "github.com/tech/sendico/pkg/api/http/response" + "github.com/tech/sendico/pkg/merrors" + "github.com/tech/sendico/pkg/model" + "github.com/tech/sendico/server/interface/api/sresponse" + "go.uber.org/zap" +) + +func (a *CallbacksAPI) update(r *http.Request, account *model.Account, accessToken *sresponse.TokenData) http.HandlerFunc { + var input model.Callback + if err := json.NewDecoder(r.Body).Decode(&input); err != nil { + a.Logger.Warn("Failed to decode callback payload", zap.Error(err)) + return response.BadPayload(a.Logger, a.Name(), err) + } + + callbackRef := *input.GetID() + if callbackRef.IsZero() { + return response.Auto(a.Logger, a.Name(), merrors.InvalidArgument("callback ref is required", "id")) + } + + var existing model.Callback + if err := a.db.Get(r.Context(), *account.GetID(), callbackRef, &existing); err != nil { + a.Logger.Warn("Failed to fetch callback before update", zap.Error(err)) + return response.Auto(a.Logger, a.Name(), err) + } + existingSecretRef, err := a.db.GetSigningSecretRef(r.Context(), *account.GetID(), callbackRef) + if err != nil && !errors.Is(err, merrors.ErrNoData) { + a.Logger.Warn("Failed to fetch callback signing secret metadata", zap.Error(err)) + return response.Auto(a.Logger, a.Name(), err) + } + + mergeCallbackMutable(&existing, &input) + mutation, err := a.normalizeAndPrepare(r.Context(), &existing, existing.OrganizationRef, existingSecretRef, true) + if err != nil { + return response.Auto(a.Logger, a.Name(), err) + } + + if _, err := a.tf.CreateTransaction().Execute(r.Context(), func(ctx context.Context) (any, error) { + if err := a.DB.Update(ctx, *account.GetID(), &existing); err != nil { + return nil, err + } + if err := a.applySigningSecretMutation(ctx, *account.GetID(), callbackRef, mutation); err != nil { + return nil, err + } + return nil, nil + }); err != nil { + a.Logger.Warn("Failed to update callback transaction", zap.Error(err)) + return response.Auto(a.Logger, a.Name(), err) + } + + return a.callbackResponse(&existing, accessToken, mutation.Generated, false) +} diff --git a/api/edge/callbacks/internal/delivery/service.go b/api/edge/callbacks/internal/delivery/service.go index 2b6f2322..e6cf2a98 100644 --- a/api/edge/callbacks/internal/delivery/service.go +++ b/api/edge/callbacks/internal/delivery/service.go @@ -10,6 +10,7 @@ import ( "sync" "time" + "github.com/tech/sendico/edge/callbacks/internal/model" "github.com/tech/sendico/edge/callbacks/internal/signing" "github.com/tech/sendico/edge/callbacks/internal/storage" "github.com/tech/sendico/pkg/merrors" @@ -154,7 +155,7 @@ func (s *service) runWorker(ctx context.Context, workerID string) { } } -func (s *service) handleTask(ctx context.Context, workerID string, task *storage.Task) { +func (s *service) handleTask(ctx context.Context, workerID string, task *model.Task) { started := time.Now() statusCode := 0 result := "failed" diff --git a/api/edge/callbacks/internal/events/module.go b/api/edge/callbacks/internal/events/module.go index f38a52e0..394a4722 100644 --- a/api/edge/callbacks/internal/events/module.go +++ b/api/edge/callbacks/internal/events/module.go @@ -4,16 +4,18 @@ import ( "context" "encoding/json" "time" + + "go.mongodb.org/mongo-driver/v2/bson" ) // Envelope is the canonical incoming event envelope. type Envelope struct { - EventID string `json:"event_id"` - Type string `json:"type"` - ClientID string `json:"client_id"` - OccurredAt time.Time `json:"occurred_at"` - PublishedAt time.Time `json:"published_at,omitempty"` - Data json.RawMessage `json:"data"` + EventID string `json:"event_id"` + Type string `json:"type"` + OrganizationRef bson.ObjectID `json:"organization_ref"` + OccurredAt time.Time `json:"occurred_at"` + PublishedAt time.Time `json:"published_at,omitempty"` + Data json.RawMessage `json:"data"` } // Service parses incoming messages and builds outbound payload bytes. @@ -24,10 +26,10 @@ type Service interface { // Payload is the stable outbound JSON body. type Payload struct { - EventID string `json:"event_id"` - Type string `json:"type"` - ClientID string `json:"client_id"` - OccurredAt string `json:"occurred_at"` - PublishedAt string `json:"published_at,omitempty"` - Data json.RawMessage `json:"data"` + EventID string `json:"event_id"` + Type string `json:"type"` + OrganizationRef bson.ObjectID `json:"organization_ref"` + OccurredAt string `json:"occurred_at"` + PublishedAt string `json:"published_at,omitempty"` + Data json.RawMessage `json:"data"` } diff --git a/api/edge/callbacks/internal/events/service.go b/api/edge/callbacks/internal/events/service.go index d2bb4ca9..96e311c4 100644 --- a/api/edge/callbacks/internal/events/service.go +++ b/api/edge/callbacks/internal/events/service.go @@ -8,6 +8,7 @@ import ( "github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/mlogger" + "go.mongodb.org/mongo-driver/v2/bson" "go.uber.org/zap" ) @@ -39,8 +40,8 @@ func (s *parserService) Parse(data []byte) (*Envelope, error) { if strings.TrimSpace(envelope.Type) == "" { return nil, merrors.InvalidArgument("type is required", "type") } - if strings.TrimSpace(envelope.ClientID) == "" { - return nil, merrors.InvalidArgument("client_id is required", "client_id") + if envelope.OrganizationRef == bson.NilObjectID { + return nil, merrors.InvalidArgument("organization_ref is required", "organization_ref") } if envelope.OccurredAt.IsZero() { return nil, merrors.InvalidArgument("occurred_at is required", "occurred_at") @@ -51,7 +52,6 @@ func (s *parserService) Parse(data []byte) (*Envelope, error) { envelope.EventID = strings.TrimSpace(envelope.EventID) envelope.Type = strings.TrimSpace(envelope.Type) - envelope.ClientID = strings.TrimSpace(envelope.ClientID) envelope.OccurredAt = envelope.OccurredAt.UTC() if !envelope.PublishedAt.IsZero() { envelope.PublishedAt = envelope.PublishedAt.UTC() @@ -66,11 +66,11 @@ func (s *parserService) BuildPayload(_ context.Context, envelope *Envelope) ([]b } payload := Payload{ - EventID: envelope.EventID, - Type: envelope.Type, - ClientID: envelope.ClientID, - OccurredAt: envelope.OccurredAt.UTC().Format(time.RFC3339Nano), - Data: envelope.Data, + EventID: envelope.EventID, + Type: envelope.Type, + OrganizationRef: envelope.OrganizationRef, + OccurredAt: envelope.OccurredAt.UTC().Format(time.RFC3339Nano), + Data: envelope.Data, } if !envelope.PublishedAt.IsZero() { payload.PublishedAt = envelope.PublishedAt.UTC().Format(time.RFC3339Nano) diff --git a/api/edge/callbacks/internal/ingest/service.go b/api/edge/callbacks/internal/ingest/service.go index 773eb831..875d5915 100644 --- a/api/edge/callbacks/internal/ingest/service.go +++ b/api/edge/callbacks/internal/ingest/service.go @@ -17,6 +17,7 @@ import ( np "github.com/tech/sendico/pkg/messaging/notifications/processor" "github.com/tech/sendico/pkg/mlogger" "github.com/tech/sendico/pkg/model" + "go.mongodb.org/mongo-driver/v2/bson" "go.uber.org/zap" ) @@ -178,7 +179,7 @@ func (s *service) handlePaymentStatusUpdated(ctx context.Context, msg *model.Pay result = ingestResultEmptyPayload return nil } - if strings.TrimSpace(msg.EventID) == "" || strings.TrimSpace(msg.ClientID) == "" || msg.OccurredAt.IsZero() { + if strings.TrimSpace(msg.EventID) == "" || msg.Data.OrganizationRef == bson.NilObjectID || msg.OccurredAt.IsZero() { result = ingestResultInvalidEvent return nil } @@ -195,15 +196,15 @@ func (s *service) handlePaymentStatusUpdated(ctx context.Context, msg *model.Pay } parsed := &events.Envelope{ - EventID: strings.TrimSpace(msg.EventID), - Type: eventType, - ClientID: strings.TrimSpace(msg.ClientID), - OccurredAt: msg.OccurredAt.UTC(), - PublishedAt: msg.PublishedAt.UTC(), - Data: data, + EventID: strings.TrimSpace(msg.EventID), + Type: eventType, + OrganizationRef: msg.Data.OrganizationRef, + OccurredAt: msg.OccurredAt.UTC(), + PublishedAt: msg.PublishedAt.UTC(), + Data: data, } - inserted, err := s.deps.InboxRepo.TryInsert(ctx, parsed.EventID, parsed.ClientID, parsed.Type, time.Now().UTC()) + inserted, err := s.deps.InboxRepo.TryInsert(ctx, parsed.EventID, parsed.Type, parsed.OrganizationRef, time.Now().UTC()) if err != nil { result = ingestResultInboxError return err @@ -213,7 +214,7 @@ func (s *service) handlePaymentStatusUpdated(ctx context.Context, msg *model.Pay return nil } - endpoints, err := s.deps.Resolver.Resolve(ctx, parsed.ClientID, parsed.Type) + endpoints, err := s.deps.Resolver.Resolve(ctx, parsed.Type, parsed.OrganizationRef) if err != nil { result = ingestResultResolveError return err diff --git a/api/edge/callbacks/internal/model/callback.go b/api/edge/callbacks/internal/model/callback.go new file mode 100644 index 00000000..e6dd7877 --- /dev/null +++ b/api/edge/callbacks/internal/model/callback.go @@ -0,0 +1,8 @@ +package model + +import pmodel "github.com/tech/sendico/pkg/model" + +type CallbackInternal struct { + pmodel.Callback `bson:",inline" json:",inline"` + SecretRef string `bson:"secretRef"` +} diff --git a/api/edge/callbacks/internal/model/endpoint.go b/api/edge/callbacks/internal/model/endpoint.go new file mode 100644 index 00000000..eccef268 --- /dev/null +++ b/api/edge/callbacks/internal/model/endpoint.go @@ -0,0 +1,22 @@ +package model + +import ( + "time" + + "github.com/tech/sendico/pkg/db/storable" + pmodel "github.com/tech/sendico/pkg/model" +) + +// Endpoint describes one target callback endpoint. +type Endpoint struct { + storable.Base + pmodel.OrganizationBoundBase + URL string + SigningMode string + SecretRef string + Headers map[string]string + MaxAttempts int + MinDelay time.Duration + MaxDelay time.Duration + RequestTimeout time.Duration +} diff --git a/api/edge/callbacks/internal/model/task.go b/api/edge/callbacks/internal/model/task.go new file mode 100644 index 00000000..0c6e999c --- /dev/null +++ b/api/edge/callbacks/internal/model/task.go @@ -0,0 +1,37 @@ +package model + +import ( + "time" + + "github.com/tech/sendico/pkg/db/storable" + "go.mongodb.org/mongo-driver/v2/bson" +) + +// TaskStatus tracks delivery task lifecycle. +type TaskStatus string + +const ( + TaskStatusPending TaskStatus = "PENDING" + TaskStatusRetry TaskStatus = "RETRY" + TaskStatusDelivered TaskStatus = "DELIVERED" + TaskStatusFailed TaskStatus = "FAILED" +) + +// Task is one callback delivery job. +type Task struct { + storable.Base + EventID string + EndpointRef bson.ObjectID + EndpointURL string + SigningMode string + SecretRef string + Headers map[string]string + Payload []byte + Attempt int + MaxAttempts int + MinDelay time.Duration + MaxDelay time.Duration + RequestTimeout time.Duration + Status TaskStatus + NextAttemptAt time.Time +} diff --git a/api/edge/callbacks/internal/storage/module.go b/api/edge/callbacks/internal/storage/module.go index c0648161..c8ca7018 100644 --- a/api/edge/callbacks/internal/storage/module.go +++ b/api/edge/callbacks/internal/storage/module.go @@ -4,54 +4,12 @@ import ( "context" "time" + "github.com/tech/sendico/edge/callbacks/internal/model" "github.com/tech/sendico/pkg/db" "github.com/tech/sendico/pkg/mlogger" "go.mongodb.org/mongo-driver/v2/bson" ) -// TaskStatus tracks delivery task lifecycle. -type TaskStatus string - -const ( - TaskStatusPending TaskStatus = "PENDING" - TaskStatusRetry TaskStatus = "RETRY" - TaskStatusDelivered TaskStatus = "DELIVERED" - TaskStatusFailed TaskStatus = "FAILED" -) - -// Endpoint describes one target callback endpoint. -type Endpoint struct { - ID bson.ObjectID - ClientID string - URL string - SigningMode string - SecretRef string - Headers map[string]string - MaxAttempts int - MinDelay time.Duration - MaxDelay time.Duration - RequestTimeout time.Duration -} - -// Task is one callback delivery job. -type Task struct { - ID bson.ObjectID - EventID string - EndpointID bson.ObjectID - EndpointURL string - SigningMode string - SecretRef string - Headers map[string]string - Payload []byte - Attempt int - MaxAttempts int - MinDelay time.Duration - MaxDelay time.Duration - RequestTimeout time.Duration - Status TaskStatus - NextAttemptAt time.Time -} - // TaskDefaults are applied when creating tasks. type TaskDefaults struct { MaxAttempts int @@ -69,18 +27,18 @@ type Options struct { // InboxRepo controls event dedupe state. type InboxRepo interface { - TryInsert(ctx context.Context, eventID, clientID, eventType string, at time.Time) (bool, error) + TryInsert(ctx context.Context, eventID, ceventType string, organizationRef bson.ObjectID, at time.Time) (bool, error) } // EndpointRepo resolves endpoints for events. type EndpointRepo interface { - FindActiveByClientAndType(ctx context.Context, clientID, eventType string) ([]Endpoint, error) + FindActive(ctx context.Context, eventType string, organizationRef bson.ObjectID) ([]model.Endpoint, error) } // TaskRepo manages callback tasks. type TaskRepo interface { - UpsertTasks(ctx context.Context, eventID string, endpoints []Endpoint, payload []byte, defaults TaskDefaults, at time.Time) error - LockNextTask(ctx context.Context, now time.Time, workerID string, lockTTL time.Duration) (*Task, error) + UpsertTasks(ctx context.Context, eventID string, endpoints []model.Endpoint, payload []byte, defaults TaskDefaults, at time.Time) error + LockNextTask(ctx context.Context, now time.Time, workerID string, lockTTL time.Duration) (*model.Task, error) MarkDelivered(ctx context.Context, taskID bson.ObjectID, httpCode int, latency time.Duration, at time.Time) error MarkRetry(ctx context.Context, taskID bson.ObjectID, attempt int, nextAttemptAt time.Time, lastError string, httpCode int, at time.Time) error MarkFailed(ctx context.Context, taskID bson.ObjectID, attempt int, lastError string, httpCode int, at time.Time) error diff --git a/api/edge/callbacks/internal/storage/service.go b/api/edge/callbacks/internal/storage/service.go index 549eb948..6b832861 100644 --- a/api/edge/callbacks/internal/storage/service.go +++ b/api/edge/callbacks/internal/storage/service.go @@ -6,6 +6,7 @@ import ( "strings" "time" + "github.com/tech/sendico/edge/callbacks/internal/model" "github.com/tech/sendico/pkg/db" "github.com/tech/sendico/pkg/db/repository" "github.com/tech/sendico/pkg/db/repository/builder" @@ -13,6 +14,7 @@ import ( "github.com/tech/sendico/pkg/db/storable" "github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/mlogger" + pmodel "github.com/tech/sendico/pkg/model" "github.com/tech/sendico/pkg/mservice" mutil "github.com/tech/sendico/pkg/mutil/db" "go.mongodb.org/mongo-driver/v2/bson" @@ -39,10 +41,10 @@ type mongoRepository struct { } type inboxDoc struct { - storable.Base `bson:",inline"` - EventID string `bson:"event_id"` - ClientID string `bson:"client_id"` - EventType string `bson:"event_type"` + storable.Base `bson:",inline"` + pmodel.OrganizationBoundBase `bson:",inline"` + EventID string `bson:"event_id"` + EventType string `bson:"event_type"` } func (d *inboxDoc) Collection() string { @@ -64,12 +66,12 @@ type deliveryPolicy struct { } type endpointDoc struct { - storable.Base `bson:",inline"` - deliveryPolicy `bson:"retry_policy"` - ClientID string `bson:"client_id"` - Status string `bson:"status"` - URL string `bson:"url"` - EventTypes []string `bson:"event_types"` + storable.Base `bson:",inline"` + pmodel.OrganizationBoundBase `bson:",inline"` + deliveryPolicy `bson:"retry_policy"` + Status string `bson:"status"` + URL string `bson:"url"` + EventTypes []string `bson:"event_types"` } func (d *endpointDoc) Collection() string { @@ -79,18 +81,18 @@ func (d *endpointDoc) Collection() string { type taskDoc struct { storable.Base `bson:",inline"` deliveryPolicy `bson:"retry_policy"` - EventID string `bson:"event_id"` - EndpointID bson.ObjectID `bson:"endpoint_id"` - EndpointURL string `bson:"endpoint_url"` - Payload []byte `bson:"payload"` - Status TaskStatus `bson:"status"` - Attempt int `bson:"attempt"` - LastError string `bson:"last_error,omitempty"` - LastHTTPCode int `bson:"last_http_code,omitempty"` - NextAttemptAt time.Time `bson:"next_attempt_at"` - LockedUntil *time.Time `bson:"locked_until,omitempty"` - WorkerID string `bson:"worker_id,omitempty"` - DeliveredAt *time.Time `bson:"delivered_at,omitempty"` + EventID string `bson:"event_id"` + EndpointRef bson.ObjectID `bson:"endpoint_ref"` + EndpointURL string `bson:"endpoint_url"` + Payload []byte `bson:"payload"` + Status model.TaskStatus `bson:"status"` + Attempt int `bson:"attempt"` + LastError string `bson:"last_error,omitempty"` + LastHTTPCode int `bson:"last_http_code,omitempty"` + NextAttemptAt time.Time `bson:"next_attempt_at"` + LockedUntil *time.Time `bson:"locked_until,omitempty"` + WorkerID string `bson:"worker_id,omitempty"` + DeliveredAt *time.Time `bson:"delivered_at,omitempty"` } func (d *taskDoc) Collection() string { @@ -152,7 +154,7 @@ func (m *mongoRepository) ensureIndexes() error { Unique: true, Keys: []ri.Key{ {Field: "event_id", Sort: ri.Asc}, - {Field: "endpoint_id", Sort: ri.Asc}, + {Field: "endpoint_ref", Sort: ri.Asc}, }, }, { @@ -172,7 +174,7 @@ func (m *mongoRepository) ensureIndexes() error { if err := m.endpointsRepo.CreateIndex(&ri.Definition{ Name: "idx_client_event", Keys: []ri.Key{ - {Field: "client_id", Sort: ri.Asc}, + {Field: "organization_ref", Sort: ri.Asc}, {Field: "status", Sort: ri.Asc}, {Field: "event_types", Sort: ri.Asc}, }, @@ -188,11 +190,11 @@ type inboxStore struct { repo repository.Repository } -func (r *inboxStore) TryInsert(ctx context.Context, eventID, clientID, eventType string, at time.Time) (bool, error) { +func (r *inboxStore) TryInsert(ctx context.Context, eventID, eventType string, organizationRef bson.ObjectID, at time.Time) (bool, error) { doc := &inboxDoc{ - EventID: strings.TrimSpace(eventID), - ClientID: strings.TrimSpace(clientID), - EventType: strings.TrimSpace(eventType), + OrganizationBoundBase: pmodel.OrganizationBoundBase{OrganizationRef: organizationRef}, + EventID: strings.TrimSpace(eventID), + EventType: strings.TrimSpace(eventType), } filter := repository.Filter("event_id", doc.EventID) @@ -212,21 +214,20 @@ type endpointStore struct { repo repository.Repository } -func (r *endpointStore) FindActiveByClientAndType(ctx context.Context, clientID, eventType string) ([]Endpoint, error) { - clientID = strings.TrimSpace(clientID) +func (r *endpointStore) FindActive(ctx context.Context, eventType string, organizationRef bson.ObjectID) ([]model.Endpoint, error) { eventType = strings.TrimSpace(eventType) - if clientID == "" { - return nil, merrors.InvalidArgument("client_id is required", "client_id") + if organizationRef == bson.NilObjectID { + return nil, merrors.InvalidArgument("organization_ref is required", "organization_ref") } if eventType == "" { return nil, merrors.InvalidArgument("event type is required", "event_type") } query := repository.Query(). - Filter(repository.Field("client_id"), clientID). + Filter(repository.OrgField(), organizationRef). In(repository.Field("status"), "active", "enabled") - out := make([]Endpoint, 0) + out := make([]model.Endpoint, 0) err := r.repo.FindManyByFilter(ctx, query, func(cur *mongo.Cursor) error { doc := &endpointDoc{} if err := cur.Decode(doc); err != nil { @@ -238,17 +239,17 @@ func (r *endpointStore) FindActiveByClientAndType(ctx context.Context, clientID, if !supportsEventType(doc.EventTypes, eventType) { return nil } - out = append(out, Endpoint{ - ID: doc.ID, - ClientID: doc.ClientID, - URL: strings.TrimSpace(doc.URL), - SigningMode: strings.TrimSpace(doc.SigningMode), - SecretRef: strings.TrimSpace(doc.SecretRef), - Headers: cloneHeaders(doc.Headers), - MaxAttempts: doc.MaxAttempts, - MinDelay: time.Duration(doc.MinDelayMS) * time.Millisecond, - MaxDelay: time.Duration(doc.MaxDelayMS) * time.Millisecond, - RequestTimeout: time.Duration(doc.RequestTimeoutMS) * time.Millisecond, + out = append(out, model.Endpoint{ + Base: doc.Base, + OrganizationBoundBase: doc.OrganizationBoundBase, + URL: strings.TrimSpace(doc.URL), + SigningMode: strings.TrimSpace(doc.SigningMode), + SecretRef: strings.TrimSpace(doc.SecretRef), + Headers: cloneHeaders(doc.Headers), + MaxAttempts: doc.MaxAttempts, + MinDelay: time.Duration(doc.MinDelayMS) * time.Millisecond, + MaxDelay: time.Duration(doc.MaxDelayMS) * time.Millisecond, + RequestTimeout: time.Duration(doc.RequestTimeoutMS) * time.Millisecond, }) return nil }) @@ -281,7 +282,7 @@ type taskStore struct { repo repository.Repository } -func (r *taskStore) UpsertTasks(ctx context.Context, eventID string, endpoints []Endpoint, payload []byte, defaults TaskDefaults, at time.Time) error { +func (r *taskStore) UpsertTasks(ctx context.Context, eventID string, endpoints []model.Endpoint, payload []byte, defaults TaskDefaults, at time.Time) error { eventID = strings.TrimSpace(eventID) if eventID == "" { return merrors.InvalidArgument("event id is required", "event_id") @@ -292,7 +293,7 @@ func (r *taskStore) UpsertTasks(ctx context.Context, eventID string, endpoints [ now := at.UTC() for _, endpoint := range endpoints { - if endpoint.ID == bson.NilObjectID { + if endpoint.GetID() == nil || *endpoint.GetID() == bson.NilObjectID { continue } @@ -327,13 +328,13 @@ func (r *taskStore) UpsertTasks(ctx context.Context, eventID string, endpoints [ doc := &taskDoc{} doc.EventID = eventID - doc.EndpointID = endpoint.ID + doc.EndpointRef = *endpoint.GetID() doc.EndpointURL = strings.TrimSpace(endpoint.URL) doc.SigningMode = strings.TrimSpace(endpoint.SigningMode) doc.SecretRef = strings.TrimSpace(endpoint.SecretRef) doc.Headers = cloneHeaders(endpoint.Headers) doc.Payload = append([]byte(nil), payload...) - doc.Status = TaskStatusPending + doc.Status = model.TaskStatusPending doc.Attempt = 0 doc.MaxAttempts = maxAttempts doc.MinDelayMS = int(minDelay / time.Millisecond) @@ -341,7 +342,7 @@ func (r *taskStore) UpsertTasks(ctx context.Context, eventID string, endpoints [ doc.RequestTimeoutMS = int(requestTimeout / time.Millisecond) doc.NextAttemptAt = now - filter := repository.Filter("event_id", eventID).And(repository.Filter("endpoint_id", endpoint.ID)) + filter := repository.Filter("event_id", eventID).And(repository.Filter("endpoint_ref", endpoint.ID)) if err := r.repo.Insert(ctx, doc, filter); err != nil { if errors.Is(err, merrors.ErrDataConflict) { continue @@ -353,7 +354,7 @@ func (r *taskStore) UpsertTasks(ctx context.Context, eventID string, endpoints [ return nil } -func (r *taskStore) LockNextTask(ctx context.Context, now time.Time, workerID string, lockTTL time.Duration) (*Task, error) { +func (r *taskStore) LockNextTask(ctx context.Context, now time.Time, workerID string, lockTTL time.Duration) (*model.Task, error) { workerID = strings.TrimSpace(workerID) if workerID == "" { return nil, merrors.InvalidArgument("worker id is required", "worker_id") @@ -368,7 +369,7 @@ func (r *taskStore) LockNextTask(ctx context.Context, now time.Time, workerID st ) query := repository.Query(). - In(repository.Field("status"), string(TaskStatusPending), string(TaskStatusRetry)). + In(repository.Field("status"), string(model.TaskStatusPending), string(model.TaskStatusRetry)). Comparison(repository.Field("next_attempt_at"), builder.Lte, now). And(lockFilter). Sort(repository.Field("next_attempt_at"), true). @@ -390,7 +391,7 @@ func (r *taskStore) LockNextTask(ctx context.Context, now time.Time, workerID st Set(repository.Field("worker_id"), workerID) conditional := repository.IDFilter(candidate.ID).And( - repository.Query().In(repository.Field("status"), string(TaskStatusPending), string(TaskStatusRetry)), + repository.Query().In(repository.Field("status"), string(model.TaskStatusPending), string(model.TaskStatusRetry)), repository.Query().Comparison(repository.Field("next_attempt_at"), builder.Lte, now), lockFilter, ) @@ -427,7 +428,7 @@ func (r *taskStore) MarkDelivered(ctx context.Context, taskID bson.ObjectID, htt } patch := repository.Patch(). - Set(repository.Field("status"), TaskStatusDelivered). + Set(repository.Field("status"), model.TaskStatusDelivered). Set(repository.Field("last_http_code"), httpCode). Set(repository.Field("delivered_at"), time.Now()). Set(repository.Field("locked_until"), nil). @@ -446,7 +447,7 @@ func (r *taskStore) MarkRetry(ctx context.Context, taskID bson.ObjectID, attempt } patch := repository.Patch(). - Set(repository.Field("status"), TaskStatusRetry). + Set(repository.Field("status"), model.TaskStatusRetry). Set(repository.Field("attempt"), attempt). Set(repository.Field("next_attempt_at"), nextAttemptAt.UTC()). Set(repository.Field("last_error"), strings.TrimSpace(lastError)). @@ -466,7 +467,7 @@ func (r *taskStore) MarkFailed(ctx context.Context, taskID bson.ObjectID, attemp } patch := repository.Patch(). - Set(repository.Field("status"), TaskStatusFailed). + Set(repository.Field("status"), model.TaskStatusFailed). Set(repository.Field("attempt"), attempt). Set(repository.Field("last_error"), strings.TrimSpace(lastError)). Set(repository.Field("last_http_code"), httpCode). @@ -479,14 +480,14 @@ func (r *taskStore) MarkFailed(ctx context.Context, taskID bson.ObjectID, attemp return nil } -func mapTaskDoc(doc *taskDoc) *Task { +func mapTaskDoc(doc *taskDoc) *model.Task { if doc == nil { return nil } - return &Task{ - ID: doc.ID, + return &model.Task{ + Base: doc.Base, EventID: doc.EventID, - EndpointID: doc.EndpointID, + EndpointRef: doc.EndpointRef, EndpointURL: doc.EndpointURL, SigningMode: doc.SigningMode, SecretRef: doc.SecretRef, diff --git a/api/edge/callbacks/internal/subscriptions/module.go b/api/edge/callbacks/internal/subscriptions/module.go index 7d7c2ecc..f3cab9a7 100644 --- a/api/edge/callbacks/internal/subscriptions/module.go +++ b/api/edge/callbacks/internal/subscriptions/module.go @@ -3,12 +3,14 @@ package subscriptions import ( "context" + "github.com/tech/sendico/edge/callbacks/internal/model" "github.com/tech/sendico/edge/callbacks/internal/storage" + "go.mongodb.org/mongo-driver/v2/bson" ) // Resolver resolves active webhook endpoints for an event. type Resolver interface { - Resolve(ctx context.Context, clientID, eventType string) ([]storage.Endpoint, error) + Resolve(ctx context.Context, eventType string, organizationRef bson.ObjectID) ([]model.Endpoint, error) } // Dependencies defines subscriptions resolver dependencies. diff --git a/api/edge/callbacks/internal/subscriptions/service.go b/api/edge/callbacks/internal/subscriptions/service.go index cee1e604..e5a21637 100644 --- a/api/edge/callbacks/internal/subscriptions/service.go +++ b/api/edge/callbacks/internal/subscriptions/service.go @@ -4,8 +4,10 @@ import ( "context" "strings" + "github.com/tech/sendico/edge/callbacks/internal/model" "github.com/tech/sendico/edge/callbacks/internal/storage" "github.com/tech/sendico/pkg/merrors" + "go.mongodb.org/mongo-driver/v2/bson" ) type service struct { @@ -21,15 +23,15 @@ func New(deps Dependencies) (Resolver, error) { return &service{repo: deps.EndpointRepo}, nil } -func (s *service) Resolve(ctx context.Context, clientID, eventType string) ([]storage.Endpoint, error) { - if strings.TrimSpace(clientID) == "" { +func (s *service) Resolve(ctx context.Context, eventType string, organizationRef bson.ObjectID) ([]model.Endpoint, error) { + if organizationRef == bson.NilObjectID { return nil, merrors.InvalidArgument("subscriptions: client id is required", "clientID") } if strings.TrimSpace(eventType) == "" { return nil, merrors.InvalidArgument("subscriptions: event type is required", "eventType") } - endpoints, err := s.repo.FindActiveByClientAndType(ctx, clientID, eventType) + endpoints, err := s.repo.FindActive(ctx, eventType, organizationRef) if err != nil { return nil, err } diff --git a/api/gateway/chain/go.mod b/api/gateway/chain/go.mod index 88bc8b62..90eeffc8 100644 --- a/api/gateway/chain/go.mod +++ b/api/gateway/chain/go.mod @@ -23,7 +23,7 @@ require ( require ( github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260225065256-91dd007ecddc // indirect + github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260302101851-b43f15d4ea3b // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.24.4 // indirect github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect diff --git a/api/gateway/chain/go.sum b/api/gateway/chain/go.sum index 3a59ac90..89aeb0e0 100644 --- a/api/gateway/chain/go.sum +++ b/api/gateway/chain/go.sum @@ -6,8 +6,8 @@ github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260225065256-91dd007ecddc h1:1stW1OipdBj8Me+nj26SzT8yil7OYve0r3cWobzk1JQ= -github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260225065256-91dd007ecddc/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI= +github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260302101851-b43f15d4ea3b h1:zoMPWbn27kscAQflTWzSHhm9fuex5SXSpyMlhCFPfxk= +github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260302101851-b43f15d4ea3b/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI= github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0= github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= diff --git a/api/gateway/tron/go.mod b/api/gateway/tron/go.mod index a9b8f264..67f9a912 100644 --- a/api/gateway/tron/go.mod +++ b/api/gateway/tron/go.mod @@ -26,7 +26,7 @@ require ( require ( github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260225065256-91dd007ecddc // indirect + github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260302101851-b43f15d4ea3b // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.24.4 // indirect github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect diff --git a/api/gateway/tron/go.sum b/api/gateway/tron/go.sum index 02a57d7c..691b0874 100644 --- a/api/gateway/tron/go.sum +++ b/api/gateway/tron/go.sum @@ -6,8 +6,8 @@ github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260225065256-91dd007ecddc h1:1stW1OipdBj8Me+nj26SzT8yil7OYve0r3cWobzk1JQ= -github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260225065256-91dd007ecddc/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI= +github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260302101851-b43f15d4ea3b h1:zoMPWbn27kscAQflTWzSHhm9fuex5SXSpyMlhCFPfxk= +github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260302101851-b43f15d4ea3b/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI= github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0= github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= diff --git a/api/payments/orchestrator/internal/service/orchestrationv2/psvc/status_publish.go b/api/payments/orchestrator/internal/service/orchestrationv2/psvc/status_publish.go index 1b334b98..e1eb3542 100644 --- a/api/payments/orchestrator/internal/service/orchestrationv2/psvc/status_publish.go +++ b/api/payments/orchestrator/internal/service/orchestrationv2/psvc/status_publish.go @@ -81,11 +81,10 @@ func (p *brokerPaymentStatusPublisher) Publish(_ context.Context, in paymentStat message := &model.PaymentStatusUpdated{ EventID: buildPaymentStatusEventID(paymentRef, payment.Version, in.CurrentState), Type: model.PaymentStatusUpdatedType, - ClientID: payment.OrganizationRef.Hex(), OccurredAt: occurredAt, PublishedAt: time.Now().UTC(), Data: model.PaymentStatusUpdatedData{ - OrganizationRef: payment.OrganizationRef.Hex(), + OrganizationRef: payment.OrganizationRef, PaymentRef: paymentRef, QuotationRef: strings.TrimSpace(payment.QuotationRef), ClientPaymentRef: strings.TrimSpace(payment.ClientPaymentRef), diff --git a/api/pkg/db/callbacks/callbacks.go b/api/pkg/db/callbacks/callbacks.go index 6b013853..e79108d6 100644 --- a/api/pkg/db/callbacks/callbacks.go +++ b/api/pkg/db/callbacks/callbacks.go @@ -12,4 +12,7 @@ type DB interface { auth.ProtectedDB[*model.Callback] SetArchived(ctx context.Context, accountRef, organizationRef, callbackRef bson.ObjectID, archived, cascade bool) error List(ctx context.Context, accountRef, organizationRef, _ bson.ObjectID, cursor *model.ViewCursor) ([]model.Callback, error) + GetSigningSecretRef(ctx context.Context, accountRef, callbackRef bson.ObjectID) (string, error) + SetSigningSecretRef(ctx context.Context, accountRef, callbackRef bson.ObjectID, secretRef string) error + ClearSigningSecretRef(ctx context.Context, accountRef, callbackRef bson.ObjectID) error } diff --git a/api/pkg/db/internal/mongo/callbacksdb/callback.go b/api/pkg/db/internal/mongo/callbacksdb/callback.go new file mode 100644 index 00000000..15370339 --- /dev/null +++ b/api/pkg/db/internal/mongo/callbacksdb/callback.go @@ -0,0 +1,8 @@ +package callbacksdb + +import "github.com/tech/sendico/pkg/model" + +type callbackInternal struct { + model.Callback `bson:",inline" json:",inline"` + SecretRef string `bson:"secret_ref" json:"-"` +} diff --git a/api/pkg/db/internal/mongo/callbacksdb/db.go b/api/pkg/db/internal/mongo/callbacksdb/db.go index 0e4231b1..8c3d2bf9 100644 --- a/api/pkg/db/internal/mongo/callbacksdb/db.go +++ b/api/pkg/db/internal/mongo/callbacksdb/db.go @@ -2,14 +2,12 @@ package callbacksdb import ( "context" - "errors" "github.com/tech/sendico/pkg/auth" "github.com/tech/sendico/pkg/db/callbacks" "github.com/tech/sendico/pkg/db/policy" ri "github.com/tech/sendico/pkg/db/repository/index" "github.com/tech/sendico/pkg/db/storable" - "github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/mlogger" "github.com/tech/sendico/pkg/model" "github.com/tech/sendico/pkg/mservice" @@ -29,10 +27,6 @@ func Create( pdb policy.DB, db *mongo.Database, ) (*CallbacksDB, error) { - if err := ensureBuiltInPolicy(ctx, logger, pdb); err != nil { - return nil, err - } - p, err := auth.CreateDBImp[*model.Callback](ctx, logger, pdb, enforcer, mservice.Callbacks, db) if err != nil { return nil, err @@ -43,7 +37,7 @@ func Create( Name: "uq_callbacks_client_url", Keys: []ri.Key{ {Field: storable.OrganizationRefField, Sort: ri.Asc}, - {Field: "client_id", Sort: ri.Asc}, + {Field: "organization_ref", Sort: ri.Asc}, {Field: "url", Sort: ri.Asc}, }, Unique: true, @@ -82,31 +76,4 @@ func Create( }, nil } -func ensureBuiltInPolicy(ctx context.Context, logger mlogger.Logger, pdb policy.DB) error { - var existing model.PolicyDescription - if err := pdb.GetBuiltInPolicy(ctx, mservice.Callbacks, &existing); err == nil { - return nil - } else if !errors.Is(err, merrors.ErrNoData) { - return err - } - - description := "Callbacks subscription management" - resourceTypes := []mservice.Type{mservice.Callbacks} - policyDescription := &model.PolicyDescription{ - Describable: model.Describable{ - Name: "Callbacks", - Description: &description, - }, - ResourceTypes: &resourceTypes, - } - if err := pdb.Create(ctx, policyDescription); err != nil && !errors.Is(err, merrors.ErrDataConflict) { - if logger != nil { - logger.Warn("Failed to create built-in callbacks policy", zap.Error(err)) - } - return err - } - - return pdb.GetBuiltInPolicy(ctx, mservice.Callbacks, &existing) -} - var _ callbacks.DB = (*CallbacksDB)(nil) diff --git a/api/pkg/db/internal/mongo/callbacksdb/secretref.go b/api/pkg/db/internal/mongo/callbacksdb/secretref.go new file mode 100644 index 00000000..071f536a --- /dev/null +++ b/api/pkg/db/internal/mongo/callbacksdb/secretref.go @@ -0,0 +1,60 @@ +package callbacksdb + +import ( + "context" + "strings" + + "github.com/tech/sendico/pkg/db/repository" + "github.com/tech/sendico/pkg/merrors" + "github.com/tech/sendico/pkg/model" + "go.mongodb.org/mongo-driver/v2/bson" +) + +func (db *CallbacksDB) GetSigningSecretRef(ctx context.Context, accountRef, callbackRef bson.ObjectID) (string, error) { + if callbackRef.IsZero() { + return "", merrors.InvalidArgument("callback reference is required", "callbackRef") + } + + // Enforce read permissions through the public callback object first. + var callback model.Callback + if err := db.Get(ctx, accountRef, callbackRef, &callback); err != nil { + return "", err + } + + internal := &callbackInternal{} + if err := db.DBImp.Repository.Get(ctx, callbackRef, internal); err != nil { + return "", err + } + + return strings.TrimSpace(internal.SecretRef), nil +} + +func (db *CallbacksDB) SetSigningSecretRef(ctx context.Context, accountRef, callbackRef bson.ObjectID, secretRef string) error { + if callbackRef.IsZero() { + return merrors.InvalidArgument("callback reference is required", "callbackRef") + } + value := strings.TrimSpace(secretRef) + if value == "" { + return merrors.InvalidArgument("secret reference is required", "secretRef") + } + + return db.Patch( + ctx, + accountRef, + callbackRef, + repository.Patch().Set(repository.Field("secretRef"), value), + ) +} + +func (db *CallbacksDB) ClearSigningSecretRef(ctx context.Context, accountRef, callbackRef bson.ObjectID) error { + if callbackRef.IsZero() { + return merrors.InvalidArgument("callback reference is required", "callbackRef") + } + + return db.Patch( + ctx, + accountRef, + callbackRef, + repository.Patch().Unset(repository.Field("secretRef")), + ) +} diff --git a/api/pkg/db/internal/mongo/verificationimp/consume.go b/api/pkg/db/internal/mongo/verificationimp/consume.go index d93d7d51..34ed97be 100644 --- a/api/pkg/db/internal/mongo/verificationimp/consume.go +++ b/api/pkg/db/internal/mongo/verificationimp/consume.go @@ -12,6 +12,7 @@ import ( "github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/model" mutil "github.com/tech/sendico/pkg/mutil/db" + "github.com/tech/sendico/pkg/mutil/mzap" "go.mongodb.org/mongo-driver/v2/bson" "go.uber.org/zap" ) @@ -54,7 +55,6 @@ func (db *verificationDB) Consume( zap.String("purpose", string(purpose)), zap.Bool("account_scoped", accountScoped), zap.String("account_ref", accountRefHex), - zap.Any("scope_filter", scopeFilter.BuildQuery()), ) // 1) Fast path for magic-link tokens: hash is deterministic and globally unique. @@ -69,7 +69,6 @@ func (db *verificationDB) Consume( zap.String("purpose", string(purpose)), zap.Bool("account_scoped", accountScoped), zap.String("account_ref", accountRefHex), - zap.Any("magic_filter", magicFilter.BuildQuery()), ) var direct model.VerificationToken err := db.DBImp.FindOne(ctx, magicFilter, &direct) @@ -152,7 +151,7 @@ func (db *verificationDB) Consume( db.Logger.Debug("Verification consume OTP candidate evaluated", zap.Int("candidate_index", i), zap.Int("candidate_total", len(tokens)), - zap.String("candidate_id", t.ID.Hex()), + mzap.ObjRef("candidate_ref", t.ID), zap.Bool("candidate_has_salt", t.Salt != nil), zap.Bool("candidate_used", t.UsedAt != nil), zap.Time("candidate_expires_at", t.ExpiresAt), @@ -181,7 +180,6 @@ func (db *verificationDB) Consume( zap.String("purpose", string(purpose)), zap.Bool("account_scoped", accountScoped), zap.String("account_ref", accountRefHex), - zap.Any("active_filter", activeFilter.BuildQuery()), ) incremented, patchErr := db.DBImp.PatchMany( @@ -271,8 +269,7 @@ func (db *verificationDB) Consume( zap.String("purpose", string(purpose)), zap.Bool("account_scoped", accountScoped), zap.String("account_ref", accountRefHex), - zap.String("token_id", token.ID.Hex()), - zap.Any("consume_filter", consumeFilter.BuildQuery()), + mzap.StorableRef(token), ) updated, err := db.DBImp.PatchMany( @@ -285,7 +282,7 @@ func (db *verificationDB) Consume( zap.String("purpose", string(purpose)), zap.Bool("account_scoped", accountScoped), zap.String("account_ref", accountRefHex), - zap.String("token_id", token.ID.Hex()), + mzap.StorableRef(token), zap.Error(err), ) return nil, err @@ -294,7 +291,7 @@ func (db *verificationDB) Consume( zap.String("purpose", string(purpose)), zap.Bool("account_scoped", accountScoped), zap.String("account_ref", accountRefHex), - zap.String("token_id", token.ID.Hex()), + mzap.StorableRef(token), zap.Int("updated_count", updated), ) @@ -322,7 +319,7 @@ func (db *verificationDB) Consume( zap.String("purpose", string(purpose)), zap.Bool("account_scoped", accountScoped), zap.String("account_ref", accountRefHex), - zap.String("token_id", token.ID.Hex()), + mzap.StorableRef(token), zap.Error(incrementErr), ) } else { @@ -330,7 +327,7 @@ func (db *verificationDB) Consume( zap.String("purpose", string(purpose)), zap.Bool("account_scoped", accountScoped), zap.String("account_ref", accountRefHex), - zap.String("token_id", token.ID.Hex()), + mzap.StorableRef(token), zap.Int("updated_count", incremented), zap.String("transaction_note", "this update occurs inside transaction and may roll back depending on returned error"), ) @@ -343,7 +340,7 @@ func (db *verificationDB) Consume( zap.String("purpose", string(purpose)), zap.Bool("account_scoped", accountScoped), zap.String("account_ref", accountRefHex), - zap.String("token_id", token.ID.Hex()), + mzap.StorableRef(token), zap.Error(err), ) return nil, merrors.Internal("failed to re-check token state") @@ -353,7 +350,7 @@ func (db *verificationDB) Consume( zap.String("purpose", string(purpose)), zap.Bool("account_scoped", accountScoped), zap.String("account_ref", accountRefHex), - zap.String("token_id", token.ID.Hex()), + mzap.StorableRef(token), }, tokenStateFields(&fresh)..., )..., ) @@ -363,7 +360,7 @@ func (db *verificationDB) Consume( zap.String("purpose", string(purpose)), zap.Bool("account_scoped", accountScoped), zap.String("account_ref", accountRefHex), - zap.String("token_id", token.ID.Hex()), + mzap.StorableRef(token), ) return nil, verification.ErorrTokenAlreadyUsed() } @@ -372,7 +369,7 @@ func (db *verificationDB) Consume( zap.String("purpose", string(purpose)), zap.Bool("account_scoped", accountScoped), zap.String("account_ref", accountRefHex), - zap.String("token_id", token.ID.Hex()), + mzap.StorableRef(token), zap.Time("now_utc", now), zap.Time("token_expires_at", fresh.ExpiresAt), ) @@ -383,7 +380,7 @@ func (db *verificationDB) Consume( zap.String("purpose", string(purpose)), zap.Bool("account_scoped", accountScoped), zap.String("account_ref", accountRefHex), - zap.String("token_id", token.ID.Hex()), + mzap.StorableRef(token), zap.Int("token_attempts", fresh.Attempts), zap.Int("token_max_retries", *fresh.MaxRetries), ) @@ -394,7 +391,7 @@ func (db *verificationDB) Consume( zap.String("purpose", string(purpose)), zap.Bool("account_scoped", accountScoped), zap.String("account_ref", accountRefHex), - zap.String("token_id", token.ID.Hex()), + mzap.StorableRef(token), ) return nil, verification.ErorrTokenNotFound() }, @@ -457,8 +454,8 @@ func tokenIsDigitsOnly(value string) bool { func tokenStateFields(token *model.VerificationToken) []zap.Field { fields := []zap.Field{ - zap.String("token_id", token.ID.Hex()), - zap.String("token_account_ref", token.AccountRef.Hex()), + mzap.StorableRef(token), + mzap.ObjRef("token_account_ref", token.AccountRef), zap.String("token_purpose", string(token.Purpose)), zap.Bool("token_has_target", strings.TrimSpace(token.Target) != ""), zap.Bool("token_has_idempotency_key", token.IdempotencyKey != nil), diff --git a/api/pkg/db/internal/mongo/verificationimp/create.go b/api/pkg/db/internal/mongo/verificationimp/create.go index 13f15948..45edea59 100644 --- a/api/pkg/db/internal/mongo/verificationimp/create.go +++ b/api/pkg/db/internal/mongo/verificationimp/create.go @@ -29,14 +29,6 @@ func syntheticIdempotencyKey() string { return "auto:" + bson.NewObjectID().Hex() } -func verificationContextFilter(request *verification.Request) builder.Query { - return repository.Query().And( - repository.Filter("accountRef", request.AccountRef), - repository.Filter("purpose", request.Purpose), - repository.Filter("target", request.Target), - ) -} - func activeContextFilter(request *verification.Request, now time.Time) builder.Query { return repository.Query().And( repository.Filter("accountRef", request.AccountRef), diff --git a/api/pkg/go.mod b/api/pkg/go.mod index f76f2486..73f60725 100644 --- a/api/pkg/go.mod +++ b/api/pkg/go.mod @@ -28,7 +28,7 @@ require ( github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260225065256-91dd007ecddc // indirect + github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260302101851-b43f15d4ea3b // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.24.4 // indirect github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect diff --git a/api/pkg/go.sum b/api/pkg/go.sum index 694424dd..42eea590 100644 --- a/api/pkg/go.sum +++ b/api/pkg/go.sum @@ -6,8 +6,8 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25 github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260225065256-91dd007ecddc h1:1stW1OipdBj8Me+nj26SzT8yil7OYve0r3cWobzk1JQ= -github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260225065256-91dd007ecddc/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI= +github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260302101851-b43f15d4ea3b h1:zoMPWbn27kscAQflTWzSHhm9fuex5SXSpyMlhCFPfxk= +github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260302101851-b43f15d4ea3b/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI= github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= diff --git a/api/pkg/model/callback.go b/api/pkg/model/callback.go index ec7f8e55..cc64642f 100644 --- a/api/pkg/model/callback.go +++ b/api/pkg/model/callback.go @@ -16,11 +16,14 @@ const ( CallbackSigningModeHMACSHA256 CallbackSigningMode = "hmac_sha256" ) +type CallbackBackoff struct { + MinDelayMS int `bson:"min_ms" json:"minDelayMs"` + MaxDelayMS int `bson:"max_ms" json:"maxDelayMs"` +} + type CallbackRetryPolicy struct { - MinDelayMS int `bson:"min_ms" json:"minDelayMs"` - MaxDelayMS int `bson:"max_ms" json:"maxDelayMs"` + Backoff CallbackBackoff `bson:"backoff" json:"backoff"` SigningMode CallbackSigningMode `bson:"signing_mode" json:"signingMode"` - SecretRef string `bson:"secret_ref,omitempty" json:"secretRef,omitempty"` Headers map[string]string `bson:"headers,omitempty" json:"headers,omitempty"` MaxAttempts int `bson:"max_attempts" json:"maxAttempts"` RequestTimeoutMS int `bson:"request_timeout_ms" json:"requestTimeoutMs"` @@ -29,7 +32,6 @@ type CallbackRetryPolicy struct { type Callback struct { PermissionBound `bson:",inline" json:",inline"` Describable `bson:",inline" json:",inline"` - ClientID string `bson:"client_id" json:"clientId"` Status CallbackStatus `bson:"status" json:"status"` URL string `bson:"url" json:"url"` EventTypes []string `bson:"event_types" json:"eventTypes"` diff --git a/api/pkg/model/payment_orchestrator_status.go b/api/pkg/model/payment_orchestrator_status.go index eb27271d..6d54edfe 100644 --- a/api/pkg/model/payment_orchestrator_status.go +++ b/api/pkg/model/payment_orchestrator_status.go @@ -1,6 +1,10 @@ package model -import "time" +import ( + "time" + + "go.mongodb.org/mongo-driver/v2/bson" +) const ( PaymentStatusUpdatedType = "payment.status.updated" @@ -9,21 +13,20 @@ const ( type PaymentStatusUpdated struct { EventID string `json:"event_id,omitempty"` Type string `json:"type,omitempty"` - ClientID string `json:"client_id,omitempty"` OccurredAt time.Time `json:"occurred_at,omitempty"` PublishedAt time.Time `json:"published_at,omitempty"` Data PaymentStatusUpdatedData `json:"data"` } type PaymentStatusUpdatedData struct { - OrganizationRef string `json:"organization_ref,omitempty"` - PaymentRef string `json:"payment_ref,omitempty"` - QuotationRef string `json:"quotation_ref,omitempty"` - ClientPaymentRef string `json:"client_payment_ref,omitempty"` - IdempotencyKey string `json:"idempotency_key,omitempty"` - State string `json:"state,omitempty"` - PreviousState string `json:"previous_state,omitempty"` - Version uint64 `json:"version,omitempty"` - IsTerminal bool `json:"is_terminal"` - Event string `json:"event,omitempty"` + OrganizationRef bson.ObjectID `json:"organization_ref,omitempty"` + PaymentRef string `json:"payment_ref,omitempty"` + QuotationRef string `json:"quotation_ref,omitempty"` + ClientPaymentRef string `json:"client_payment_ref,omitempty"` + IdempotencyKey string `json:"idempotency_key,omitempty"` + State string `json:"state,omitempty"` + PreviousState string `json:"previous_state,omitempty"` + Version uint64 `json:"version,omitempty"` + IsTerminal bool `json:"is_terminal"` + Event string `json:"event,omitempty"` } diff --git a/api/pkg/model/refresh.go b/api/pkg/model/refresh.go index 89e2e352..43e956be 100644 --- a/api/pkg/model/refresh.go +++ b/api/pkg/model/refresh.go @@ -3,7 +3,9 @@ package model import ( "time" + "github.com/tech/sendico/pkg/db/storable" "github.com/tech/sendico/pkg/mservice" + "go.mongodb.org/mongo-driver/v2/bson" ) type ClientRefreshToken struct { @@ -12,13 +14,14 @@ type ClientRefreshToken struct { } type RefreshToken struct { - AccountBoundBase `bson:",inline" json:",inline"` + storable.Base `bson:",inline" json:",inline"` ClientRefreshToken `bson:",inline" json:",inline"` - ExpiresAt time.Time `bson:"expiresAt"` - IsRevoked bool `bson:"isRevoked"` - LastUsedAt time.Time `bson:"lastUsedAt,omitempty"` - UserAgent string `bson:"userAgent"` - IPAddress string `bson:"ipAddress"` + AccountRef *bson.ObjectID `bson:"accountRef,omitempty" json:"accountRef,omitempty"` + ExpiresAt time.Time `bson:"expiresAt"` + IsRevoked bool `bson:"isRevoked"` + LastUsedAt time.Time `bson:"lastUsedAt,omitempty"` + UserAgent string `bson:"userAgent"` + IPAddress string `bson:"ipAddress"` } func (*RefreshToken) Collection() string { diff --git a/api/server/go.sum b/api/server/go.sum deleted file mode 100644 index d17cbdc0..00000000 --- a/api/server/go.sum +++ /dev/null @@ -1,388 +0,0 @@ -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/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= -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/aws/aws-sdk-go-v2 v1.41.2 h1:LuT2rzqNQsauaGkPK/7813XxcZ3o3yePY0Iy891T2ls= -github.com/aws/aws-sdk-go-v2 v1.41.2/go.mod h1:IvvlAZQXvTXznUPfRVfryiG1fbzE2NGK6m9u39YQ+S4= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.5 h1:zWFmPmgw4sveAYi1mRqG+E/g0461cJ5M4bJ8/nc6d3Q= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.5/go.mod h1:nVUlMLVV8ycXSb7mSkcNu9e3v/1TJq2RTlrPwhYWr5c= -github.com/aws/aws-sdk-go-v2/config v1.32.10 h1:9DMthfO6XWZYLfzZglAgW5Fyou2nRI5CuV44sTedKBI= -github.com/aws/aws-sdk-go-v2/config v1.32.10/go.mod h1:2rUIOnA2JaiqYmSKYmRJlcMWy6qTj1vuRFscppSBMcw= -github.com/aws/aws-sdk-go-v2/credentials v1.19.10 h1:EEhmEUFCE1Yhl7vDhNOI5OCL/iKMdkkYFTRpZXNw7m8= -github.com/aws/aws-sdk-go-v2/credentials v1.19.10/go.mod h1:RnnlFCAlxQCkN2Q379B67USkBMu1PipEEiibzYN5UTE= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18 h1:Ii4s+Sq3yDfaMLpjrJsqD6SmG/Wq/P5L/hw2qa78UAY= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18/go.mod h1:6x81qnY++ovptLE6nWQeWrpXxbnlIex+4H4eYYGcqfc= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.18 h1:F43zk1vemYIqPAwhjTjYIz0irU2EY7sOb/F5eJ3HuyM= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.18/go.mod h1:w1jdlZXrGKaJcNoL+Nnrj+k5wlpGXqnNrKoP22HvAug= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18 h1:xCeWVjj0ki0l3nruoyP2slHsGArMxeiiaoPN5QZH6YQ= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18/go.mod h1:r/eLGuGCBw6l36ZRWiw6PaZwPXb6YOj+i/7MizNl5/k= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.18 h1:eZioDaZGJ0tMM4gzmkNIO2aAoQd+je7Ug7TkvAzlmkU= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.18/go.mod h1:CCXwUKAJdoWr6/NcxZ+zsiPr6oH/Q5aTooRGYieAyj4= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5 h1:CeY9LUdur+Dxoeldqoun6y4WtJ3RQtzk0JMP2gfUay0= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5/go.mod h1:AZLZf2fMaahW5s/wMRciu1sYbdsikT/UHwbUjOdEVTc= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.10 h1:fJvQ5mIBVfKtiyx0AHY6HeWcRX5LGANLpq8SVR+Uazs= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.10/go.mod h1:Kzm5e6OmNH8VMkgK9t+ry5jEih4Y8whqs+1hrkxim1I= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18 h1:LTRCYFlnnKFlKsyIQxKhJuDuA3ZkrDQMRYm6rXiHlLY= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18/go.mod h1:XhwkgGG6bHSd00nO/mexWTcTjgd6PjuvWQMqSn2UaEk= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.18 h1:/A/xDuZAVD2BpsS2fftFRo/NoEKQJ8YTnJDEHBy2Gtg= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.18/go.mod h1:hWe9b4f+djUQGmyiGEeOnZv69dtMSgpDRIvNMvuvzvY= -github.com/aws/aws-sdk-go-v2/service/s3 v1.96.2 h1:M1A9AjcFwlxTLuf0Faj88L8Iqw0n/AJHjpZTQzMMsSc= -github.com/aws/aws-sdk-go-v2/service/s3 v1.96.2/go.mod h1:KsdTV6Q9WKUZm2mNJnUFmIoXfZux91M3sr/a4REX8e0= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.6 h1:MzORe+J94I+hYu2a6XmV5yC9huoTv8NRcCrUNedDypQ= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.6/go.mod h1:hXzcHLARD7GeWnifd8j9RWqtfIgxj4/cAtIVIK7hg8g= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.11 h1:7oGD8KPfBOJGXiCoRKrrrQkbvCp8N++u36hrLMPey6o= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.11/go.mod h1:0DO9B5EUJQlIDif+XJRWCljZRKsAFKh3gpFz7UnDtOo= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15 h1:edCcNp9eGIUDUCrzoCu1jWAXLGFIizeqkdkKgRlJwWc= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15/go.mod h1:lyRQKED9xWfgkYC/wmmYfv7iVIM68Z5OQ88ZdcV1QbU= -github.com/aws/aws-sdk-go-v2/service/sts v1.41.7 h1:NITQpgo9A5NrDZ57uOWj+abvXSb83BbyggcUBVksN7c= -github.com/aws/aws-sdk-go-v2/service/sts v1.41.7/go.mod h1:sks5UWBhEuWYDPdwlnRFn1w7xWdH29Jcpe+/PJQefEs= -github.com/aws/smithy-go v1.24.1 h1:VbyeNfmYkWoxMVpGUAbQumkODcYmfMRfZ8yQiH30SK0= -github.com/aws/smithy-go v1.24.1/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= -github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -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/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= -github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 h1:5RVFMOWjMyRy8cARdy79nAmgYw3hK/4HUq48LQ6Wwqo= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= -github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= -github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI= -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 v1.5.4/go.mod h1:uaf8YgoFazUOkPBG7fxPftUylNumIev9awIWOENIuEg= -github.com/go-chi/chi v1.5.5 h1:vOB/HbEMt9QqBqErz07QehcOKHaWFtuj87tTDVz2qXE= -github.com/go-chi/chi v1.5.5/go.mod h1:C9JqLr3tIYjDOZpzn+BCuxY8z8vmca43EeMgyZt7irw= -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-chi/cors v1.2.2 h1:Jmey33TE+b+rB7fT8MUy1u0I4L+NARQlK6LhzKPSyQE= -github.com/go-chi/cors v1.2.2/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= -github.com/go-chi/jwtauth/v5 v5.4.0 h1:Ieh0xMJsFvqylqJ02/mQHKzbbKO9DYNBh4DPKCwTwYI= -github.com/go-chi/jwtauth/v5 v5.4.0/go.mod h1:w6yjqUUXz1b8+oiJel64Sz1KJwduQM6qUA5QNzO5+bQ= -github.com/go-chi/metrics v0.1.1 h1:CXhbnkAVVjb0k73EBRQ6Z2YdWFnbXZgNtg1Mboguibk= -github.com/go-chi/metrics v0.1.1/go.mod h1:mcGTM1pPalP7WCtb+akNYFO/lwNwBBLCuedepqjoPn4= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -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.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -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/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= -github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= -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/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= -github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -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/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -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.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -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/lestrrat-go/blackmagic v1.0.4 h1:IwQibdnf8l2KoO+qC3uT4OaTWsW7tuRQXy9TRN9QanA= -github.com/lestrrat-go/blackmagic v1.0.4/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw= -github.com/lestrrat-go/dsig v1.0.0 h1:OE09s2r9Z81kxzJYRn07TFM9XA4akrUdoMwr0L8xj38= -github.com/lestrrat-go/dsig v1.0.0/go.mod h1:dEgoOYYEJvW6XGbLasr8TFcAxoWrKlbQvmJgCR0qkDo= -github.com/lestrrat-go/dsig-secp256k1 v1.0.0 h1:JpDe4Aybfl0soBvoVwjqDbp+9S1Y2OM7gcrVVMFPOzY= -github.com/lestrrat-go/dsig-secp256k1 v1.0.0/go.mod h1:CxUgAhssb8FToqbL8NjSPoGQlnO4w3LG1P0qPWQm/NU= -github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= -github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= -github.com/lestrrat-go/httprc/v3 v3.0.4 h1:pXyH2ppK8GYYggygxJ3TvxpCZnbEUWc9qSwRTTApaLA= -github.com/lestrrat-go/httprc/v3 v3.0.4/go.mod h1:mSMtkZW92Z98M5YoNNztbRGxbXHql7tSitCvaxvo9l0= -github.com/lestrrat-go/jwx/v3 v3.0.13 h1:AdHKiPIYeCSnOJtvdpipPg/0SuFh9rdkN+HF3O0VdSk= -github.com/lestrrat-go/jwx/v3 v3.0.13/go.mod h1:2m0PV1A9tM4b/jVLMx8rh6rBl7F6WGb3EG2hufN9OQU= -github.com/lestrrat-go/option/v2 v2.0.0 h1:XxrcaJESE1fokHy3FpaQ/cXW8ZsIdWcdFzzLOcID3Ss= -github.com/lestrrat-go/option/v2 v2.0.0/go.mod h1:oSySsmzMoR0iRzCDCaUfsCzxQHUEuhOViQObyy7S6Vg= -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/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= -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.49.0 h1:yh/WvY59gXqYpgl33ZI+XoVPKyut/IcEaqtsiuTJpoE= -github.com/nats-io/nats.go v1.49.0/go.mod h1:fDCn3mN5cY8HooHwE2ukiLb4p4G4ImmzvXyJt+tGwdw= -github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4= -github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs= -github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= -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/diff v0.0.0-20200914180035-5b29258ca4f7/go.mod h1:zO8QMzTeZd5cpnIkz/Gn6iK0jDfGicM1nynOkkPIl28= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -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.20.0 h1:AA7aCvjxwAquZAlonN7888f2u4IN8WVeFgBi4k82M4Q= -github.com/prometheus/procfs v0.20.0/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0= -github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -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/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= -github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= -github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= -github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= -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.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -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/tailscale/depaware v0.0.0-20210622194025-720c4b409502/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8= -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.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= -github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= -github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= -github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ= -github.com/valyala/fastjson v1.6.10 h1:/yjJg8jaVQdYR3arGxPE2X5z89xrlhS0eGXdv+ADTh4= -github.com/valyala/fastjson v1.6.10/go.mod h1:e6FubmQouUNP73jtMLmcbxS6ydWIpOfhz34TSfO3JaE= -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.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -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 v1.17.8 h1:BDP3+U3Y8K0vTrpqDJIRaXNhb/bKyoVeg6tIJsW5EhM= -go.mongodb.org/mongo-driver v1.17.8/go.mod h1:LlOhpH5NUEfhxcAwG0UEkMqwYcc4JU18gtCdGudk/tQ= -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.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms= -go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 h1:QKdN8ly8zEMrByybbQgv8cWBcdAarwmIPZ6FThrWXJs= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0/go.mod h1:bTdK1nhqF76qiPoCCdyFIV+N/sRHYXYCTQc+3VCi3MI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= -go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g= -go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc= -go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8= -go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE= -go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw= -go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg= -go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw= -go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA= -go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= -go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.8.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -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.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= -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.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= -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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -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/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -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-20190404232315-eb5bcb51f2a3/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-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo= -golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/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-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/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/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= -golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= -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/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= -golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -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.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -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= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/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/api v0.0.0-20260226221140-a57be14db171 h1:tu/dtnW1o3wfaxCOjSLn5IRX4YDcJrtlpzYkhHhGaC4= -google.golang.org/genproto/googleapis/api v0.0.0-20260226221140-a57be14db171/go.mod h1:M5krXqk4GhBKvB596udGL3UyjL4I1+cTbK0orROM9ng= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= -google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY= -google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= -google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= -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-20180628173108-788fd7840127/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.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= -gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= -moul.io/chizap v1.0.3 h1:mliXvvuS5HVo3QP8qPXczWtRM5dQ9UmK3bBVIkZo6ek= -moul.io/chizap v1.0.3/go.mod h1:pq4R9kGLwz4XjBc4hodQYuoE7Yc9RUabLBFyyi2uErk= diff --git a/ci/dev/callbacks.dockerfile b/ci/dev/callbacks.dockerfile index 6179c603..855aed47 100644 --- a/ci/dev/callbacks.dockerfile +++ b/ci/dev/callbacks.dockerfile @@ -26,8 +26,8 @@ WORKDIR /src COPY --from=builder /src/api/proto ./api/proto COPY --from=builder /src/api/pkg ./api/pkg -# Copy vault-aware entrypoint wrapper -COPY api/edge/callbacks/entrypoint.sh /app/entrypoint.sh +# Copy dev-specific entrypoint script +COPY ci/dev/entrypoints/callbacks.sh /app/entrypoint.sh RUN chmod +x /app/entrypoint.sh # Source code will be mounted at runtime diff --git a/ci/dev/entrypoints/callbacks.sh b/ci/dev/entrypoints/callbacks.sh new file mode 100644 index 00000000..d79043c2 --- /dev/null +++ b/ci/dev/entrypoints/callbacks.sh @@ -0,0 +1,17 @@ +#!/bin/sh +set -eu + +# Load Vault token from file if VAULT_TOKEN_FILE is set +if [ -n "${VAULT_TOKEN_FILE:-}" ] && [ -f "${VAULT_TOKEN_FILE}" ]; then + token="$(cat "${VAULT_TOKEN_FILE}" 2>/dev/null | tr -d '[:space:]')" + if [ -n "${token}" ]; then + export VAULT_TOKEN="${token}" + fi +fi + +if [ -z "${VAULT_TOKEN:-}" ]; then + echo "[entrypoint] VAULT_TOKEN is not set; expected Vault Agent sink to write a token to ${VAULT_TOKEN_FILE:-/run/vault/token}" >&2 +fi + +# Execute the command passed as arguments (e.g., air) +exec "$@" diff --git a/interface/api.yaml b/interface/api.yaml index 75f4e636..6fdf4f0a 100644 --- a/interface/api.yaml +++ b/interface/api.yaml @@ -89,8 +89,6 @@ paths: $ref: ./api/payments/quote.yaml /payments/multiquote/{organizations_ref}: $ref: ./api/payments/multiquote.yaml - /payments/immediate/{organizations_ref}: - $ref: ./api/payments/immediate.yaml /payments/by-quote/{organizations_ref}: $ref: ./api/payments/by_quote.yaml /payments/by-multiquote/{organizations_ref}: diff --git a/interface/api/accounts/bodies/auth.yaml b/interface/api/accounts/bodies/auth.yaml index 1b738514..f7a723d2 100644 --- a/interface/api/accounts/bodies/auth.yaml +++ b/interface/api/accounts/bodies/auth.yaml @@ -1,12 +1,14 @@ components: requestBodies: LoginBody: + description: JSON credentials payload for standard login. required: true content: application/json: schema: $ref: ../request/auth.yaml#/components/schemas/LoginRequest ApiLoginBody: + description: JSON credentials payload for API client login. required: true content: application/json: @@ -14,6 +16,7 @@ components: $ref: ../request/auth.yaml#/components/schemas/ApiLoginRequest RefreshTokenBody: + description: JSON payload containing session and refresh token data. required: true content: application/json: diff --git a/interface/api/accounts/request/auth.yaml b/interface/api/accounts/request/auth.yaml index 7471deda..b000fd83 100644 --- a/interface/api/accounts/request/auth.yaml +++ b/interface/api/accounts/request/auth.yaml @@ -11,6 +11,7 @@ components: description: Client identifier bound to refresh token lifecycle and client policy checks. deviceId: type: string + description: Device/server identifier associated with the client session. login: $ref: ../../../models/auth/login_data.yaml#/components/schemas/LoginData diff --git a/interface/api/accounts/response/auth.yaml b/interface/api/accounts/response/auth.yaml index 9649a73e..dd326542 100644 --- a/interface/api/accounts/response/auth.yaml +++ b/interface/api/accounts/response/auth.yaml @@ -1,6 +1,7 @@ components: schemas: AccountAuthData: + description: Authentication response containing account profile and access token. type: object additionalProperties: false required: @@ -8,11 +9,14 @@ components: - account properties: accessToken: + description: Access token used for authenticated requests. $ref: ../../../models/auth/token_data.yaml#/components/schemas/TokenData account: + description: Authenticated account data. $ref: ../../../models/account/account.yaml#/components/schemas/AccountData LoginData: + description: Full login response including access and refresh tokens. type: object additionalProperties: false required: @@ -21,13 +25,17 @@ components: - refreshToken properties: accessToken: + description: Access token used for authenticated requests. $ref: ../../../models/auth/token_data.yaml#/components/schemas/TokenData account: + description: Authenticated account data. $ref: ../../../models/account/account.yaml#/components/schemas/AccountData refreshToken: + description: Refresh token used to obtain a new access token. $ref: ../../../models/auth/token_data.yaml#/components/schemas/TokenData PendingLoginData: + description: Pending login response requiring additional verification. type: object additionalProperties: false required: @@ -36,14 +44,19 @@ components: - target properties: account: + description: Interim authentication payload prepared for verification completion. type: object additionalProperties: false properties: accessToken: + description: Temporary access token issued before verification is completed. $ref: ../../../models/auth/token_data.yaml#/components/schemas/TokenData account: + description: Account data associated with the pending login flow. $ref: ../../../models/account/account.yaml#/components/schemas/AccountData pendingToken: + description: Token proving the pending verification session. $ref: ../../../models/auth/token_data.yaml#/components/schemas/TokenData target: + description: Verification target destination (for example email or phone). type: string diff --git a/interface/api/callbacks/bodies/callback.yaml b/interface/api/callbacks/bodies/callback.yaml index b6735a0a..dade878c 100644 --- a/interface/api/callbacks/bodies/callback.yaml +++ b/interface/api/callbacks/bodies/callback.yaml @@ -1,9 +1,9 @@ components: requestBodies: CallbackBody: + description: JSON body containing callback endpoint configuration. required: true content: application/json: schema: $ref: ../request/callback.yaml#/components/schemas/CallbackRequest - diff --git a/interface/api/callbacks/request/callback.yaml b/interface/api/callbacks/request/callback.yaml index a6e90fc6..62701282 100644 --- a/interface/api/callbacks/request/callback.yaml +++ b/interface/api/callbacks/request/callback.yaml @@ -1,5 +1,5 @@ components: schemas: CallbackRequest: - $ref: ../../../models/callback/callback.yaml#/components/schemas/Callback - + description: Request payload used to create or update a callback configuration. + $ref: ../../../models/callback/callback.yaml#/components/schemas/CallbackContent diff --git a/interface/api/callbacks/response/callback.yaml b/interface/api/callbacks/response/callback.yaml index de16bfa8..e8b713f5 100644 --- a/interface/api/callbacks/response/callback.yaml +++ b/interface/api/callbacks/response/callback.yaml @@ -1,6 +1,7 @@ components: schemas: CallbacksAuthData: + description: Authenticated response payload containing callback configurations. type: object additionalProperties: false required: @@ -8,12 +9,14 @@ components: - callbacks properties: accessToken: + description: Refreshed access token to be used in subsequent API calls. $ref: ../../../models/auth/token_data.yaml#/components/schemas/TokenData callbacks: + description: Collection of callbacks configured for the authenticated scope. type: array items: $ref: ../../../models/callback/callback.yaml#/components/schemas/Callback generatedSigningSecret: + description: Newly generated signing secret when secret rotation is requested. type: string nullable: true - diff --git a/interface/api/callbacks/rotate_secret.yaml b/interface/api/callbacks/rotate_secret.yaml index cf16c4ea..02492c9a 100644 --- a/interface/api/callbacks/rotate_secret.yaml +++ b/interface/api/callbacks/rotate_secret.yaml @@ -1,7 +1,7 @@ post: tags: [Callbacks] summary: Rotate callback signing secret - description: Generates and stores a new HMAC secret for the callback in Vault and returns it once. + description: Generates and stores a new HMAC secret for the callback and returns it once. operationId: callbacksRotateSecret security: - bearerAuth: [] diff --git a/interface/api/organizations/response/organization.yaml b/interface/api/organizations/response/organization.yaml index 5ef7b813..56fc0f76 100644 --- a/interface/api/organizations/response/organization.yaml +++ b/interface/api/organizations/response/organization.yaml @@ -1,6 +1,7 @@ components: schemas: OrganizationsAuthData: + description: Authenticated response payload containing organizations. type: object additionalProperties: false required: @@ -8,8 +9,10 @@ components: - organizations properties: accessToken: + description: Refreshed access token to be used in subsequent API calls. $ref: ../../../models/auth/token_data.yaml#/components/schemas/TokenData organizations: + description: Collection of organizations available to the authenticated account. type: array items: $ref: ../../../models/organization/organization.yaml#/components/schemas/Organization diff --git a/interface/api/parameters/callbacks_ref.yaml b/interface/api/parameters/callbacks_ref.yaml index c8fdd7b7..18ba8143 100644 --- a/interface/api/parameters/callbacks_ref.yaml +++ b/interface/api/parameters/callbacks_ref.yaml @@ -4,7 +4,7 @@ components: name: callbacks_ref in: path required: true - description: Callback subscription reference (Mongo ObjectId, 24 hex chars). + description: Callback subscription reference, 24 hex chars. schema: $ref: ../../models/objectid.yaml#/components/schemas/ObjectId diff --git a/interface/api/parameters/org_ref.yaml b/interface/api/parameters/org_ref.yaml index 289630b9..3ef0c64a 100644 --- a/interface/api/parameters/org_ref.yaml +++ b/interface/api/parameters/org_ref.yaml @@ -4,6 +4,6 @@ components: name: org_ref in: path required: true - description: Organization reference in BFF route format (Mongo ObjectId, 24 hex chars). + description: Organization reference in BFF route format, 24 hex chars. schema: $ref: ../../models/objectid.yaml#/components/schemas/ObjectId diff --git a/interface/api/parameters/payment_methods_ref.yaml b/interface/api/parameters/payment_methods_ref.yaml index 38ded8fa..6690d537 100644 --- a/interface/api/parameters/payment_methods_ref.yaml +++ b/interface/api/parameters/payment_methods_ref.yaml @@ -4,6 +4,6 @@ components: name: payment_methods_ref in: path required: true - description: Payment method reference (Mongo ObjectId, 24 hex chars). + description: Payment method reference, 24 hex chars. schema: $ref: ../../models/objectid.yaml#/components/schemas/ObjectId diff --git a/interface/api/parameters/recipients_ref.yaml b/interface/api/parameters/recipients_ref.yaml index a679b628..945bca14 100644 --- a/interface/api/parameters/recipients_ref.yaml +++ b/interface/api/parameters/recipients_ref.yaml @@ -4,6 +4,6 @@ components: name: recipients_ref in: path required: true - description: Recipient reference (Mongo ObjectId, 24 hex chars). + description: Recipient reference, 24 hex chars. schema: $ref: ../../models/objectid.yaml#/components/schemas/ObjectId diff --git a/interface/api/payment_methods/bodies/payment_method.yaml b/interface/api/payment_methods/bodies/payment_method.yaml index 090086de..a956c322 100644 --- a/interface/api/payment_methods/bodies/payment_method.yaml +++ b/interface/api/payment_methods/bodies/payment_method.yaml @@ -1,6 +1,7 @@ components: requestBodies: PaymentMethodBody: + description: JSON body containing a payment method definition. required: true content: application/json: diff --git a/interface/api/payment_methods/request/payment_method.yaml b/interface/api/payment_methods/request/payment_method.yaml index ed55b7f6..dfcfbf60 100644 --- a/interface/api/payment_methods/request/payment_method.yaml +++ b/interface/api/payment_methods/request/payment_method.yaml @@ -1,4 +1,5 @@ components: schemas: PaymentMethodRequest: + description: Request payload used to create or update a payment method. $ref: ../../../models/payment_method/payment_method.yaml#/components/schemas/PaymentMethod diff --git a/interface/api/payment_methods/response/payment_method.yaml b/interface/api/payment_methods/response/payment_method.yaml index fbf9c235..40cb7aaa 100644 --- a/interface/api/payment_methods/response/payment_method.yaml +++ b/interface/api/payment_methods/response/payment_method.yaml @@ -1,6 +1,7 @@ components: schemas: Payment MethodsAuthData: + description: Authenticated response payload containing payment methods. type: object additionalProperties: false required: @@ -8,8 +9,10 @@ components: - payment_methods properties: accessToken: + description: Refreshed access token to be used in subsequent API calls. $ref: ../../../models/auth/token_data.yaml#/components/schemas/TokenData payment_methods: + description: Collection of payment methods available to the authenticated account. type: array items: $ref: ../../../models/payment_method/payment_method.yaml#/components/schemas/PaymentMethod diff --git a/interface/api/payments/bodies/payment.yaml b/interface/api/payments/bodies/payment.yaml index bd772ddb..4dc0349c 100644 --- a/interface/api/payments/bodies/payment.yaml +++ b/interface/api/payments/bodies/payment.yaml @@ -1,6 +1,7 @@ components: requestBodies: QuotePaymentBody: + description: JSON payload used to request a quote for one payment intent. required: true content: application/json: @@ -8,6 +9,7 @@ components: $ref: ../request/payment.yaml#/components/schemas/QuotePaymentRequest QuotePaymentsBody: + description: JSON payload used to request quotes for multiple payment intents. required: true content: application/json: @@ -15,6 +17,7 @@ components: $ref: ../request/payment.yaml#/components/schemas/QuotePaymentsRequest InitiatePaymentBody: + description: JSON payload used to initiate a single payment. required: true content: application/json: @@ -22,6 +25,7 @@ components: $ref: ../request/payment.yaml#/components/schemas/InitiatePaymentRequest InitiatePaymentsBody: + description: JSON payload used to initiate multiple payments from a quote reference. required: true content: application/json: diff --git a/interface/api/payments/request/payment.yaml b/interface/api/payments/request/payment.yaml index 1d4a9807..a93eb217 100644 --- a/interface/api/payments/request/payment.yaml +++ b/interface/api/payments/request/payment.yaml @@ -1,33 +1,41 @@ components: schemas: PaymentBase: + description: Base fields shared by payment initiation request payloads. type: object additionalProperties: false required: - idempotencyKey properties: idempotencyKey: + description: Client-supplied key used to safely deduplicate requests. type: string metadata: + description: Optional request metadata forwarded through payment processing. type: object additionalProperties: type: string QuotePaymentRequest: + description: Request payload to quote a single payment intent. type: object additionalProperties: false required: - intent properties: idempotencyKey: + description: Idempotency key used when persisting quote context. type: string metadata: + description: Optional metadata associated with the quote request. type: object additionalProperties: type: string intent: + description: Payment intent to be priced. $ref: ../../../models/payment/payment.yaml#/components/schemas/PaymentIntent previewOnly: + description: If true, returns a preview quote without requiring idempotency. type: boolean allOf: - if: @@ -45,23 +53,28 @@ components: - idempotencyKey QuotePaymentsRequest: + description: Request payload to quote multiple payment intents in a single call. type: object additionalProperties: false required: - intents properties: idempotencyKey: + description: Idempotency key used when persisting batch quote context. type: string metadata: + description: Optional metadata associated with the quote request. type: object additionalProperties: type: string intents: + description: List of payment intents to be priced. type: array minItems: 1 items: $ref: ../../../models/payment/payment.yaml#/components/schemas/PaymentIntent previewOnly: + description: If true, returns preview quotes without requiring idempotency. type: boolean allOf: - if: @@ -79,6 +92,7 @@ components: - idempotencyKey InitiatePaymentRequest: + description: Request payload to initiate a single payment. allOf: - $ref: ./payment.yaml#/components/schemas/PaymentBase - type: object @@ -88,11 +102,14 @@ components: - required: [quoteRef] properties: intent: + description: Payment intent to execute directly. $ref: ../../../models/payment/payment.yaml#/components/schemas/PaymentIntent quoteRef: + description: Reference to a previously generated quote to execute. type: string InitiatePaymentsRequest: + description: Request payload to initiate multiple payments from a multi-quote reference. allOf: - $ref: ./payment.yaml#/components/schemas/PaymentBase - type: object @@ -101,4 +118,5 @@ components: - quoteRef properties: quoteRef: + description: Reference to a previously generated multi-quote. type: string diff --git a/interface/api/payments/response/payment.yaml b/interface/api/payments/response/payment.yaml index a1e4d7e8..2c3ab0e7 100644 --- a/interface/api/payments/response/payment.yaml +++ b/interface/api/payments/response/payment.yaml @@ -1,6 +1,7 @@ components: schemas: PaymentQuoteData: + description: Response payload for a single payment quote. type: object additionalProperties: false required: @@ -8,13 +9,17 @@ components: - quote properties: accessToken: + description: Refreshed access token to be used in subsequent API calls. $ref: ../../../models/auth/token_data.yaml#/components/schemas/TokenData idempotencyKey: + description: Idempotency key associated with the quote response. type: string quote: + description: Generated quote data for the requested payment intent. $ref: ../../../models/payment/payment.yaml#/components/schemas/PaymentQuote PaymentQuotesData: + description: Response payload for a batch quote request. type: object additionalProperties: false required: @@ -22,21 +27,27 @@ components: - quote properties: accessToken: + description: Refreshed access token to be used in subsequent API calls. $ref: ../../../models/auth/token_data.yaml#/components/schemas/TokenData quote: + description: Batch quote summary and quoted items. type: object additionalProperties: false properties: idempotencyKey: + description: Idempotency key associated with the batch quote response. type: string quoteRef: + description: Reference to the generated batch quote. type: string items: + description: Collection of quotes for each requested payment intent. type: array items: $ref: ../../../models/payment/payment.yaml#/components/schemas/PaymentQuote PaymentsData: + description: Response payload containing a list of payments. type: object additionalProperties: false required: @@ -44,15 +55,19 @@ components: - payments properties: accessToken: + description: Refreshed access token to be used in subsequent API calls. $ref: ../../../models/auth/token_data.yaml#/components/schemas/TokenData payments: + description: Collection of payment records. type: array items: $ref: ../../../models/payment/payment.yaml#/components/schemas/Payment page: + description: Pagination cursor metadata for payment listing endpoints. $ref: ../../../models/common/pagination.yaml#/components/schemas/CursorPageResponse PaymentData: + description: Response payload containing a single payment record. type: object additionalProperties: false required: @@ -60,6 +75,8 @@ components: - payment properties: accessToken: + description: Refreshed access token to be used in subsequent API calls. $ref: ../../../models/auth/token_data.yaml#/components/schemas/TokenData payment: + description: Requested payment record. $ref: ../../../models/payment/payment.yaml#/components/schemas/Payment diff --git a/interface/api/recipients/bodies/recipient.yaml b/interface/api/recipients/bodies/recipient.yaml index c4c6e152..cf3162e3 100644 --- a/interface/api/recipients/bodies/recipient.yaml +++ b/interface/api/recipients/bodies/recipient.yaml @@ -1,6 +1,7 @@ components: requestBodies: RecipientBody: + description: JSON body containing recipient details. required: true content: application/json: diff --git a/interface/api/recipients/request/recipient.yaml b/interface/api/recipients/request/recipient.yaml index 7a96a78b..7a87efb2 100644 --- a/interface/api/recipients/request/recipient.yaml +++ b/interface/api/recipients/request/recipient.yaml @@ -1,4 +1,5 @@ components: schemas: RecipientRequest: + description: Request payload used to create or update a recipient. $ref: ../../../models/recipient/recipient.yaml#/components/schemas/Recipient diff --git a/interface/api/recipients/response/recipient.yaml b/interface/api/recipients/response/recipient.yaml index 7b32cee2..337b850b 100644 --- a/interface/api/recipients/response/recipient.yaml +++ b/interface/api/recipients/response/recipient.yaml @@ -1,6 +1,7 @@ components: schemas: RecipientsAuthData: + description: Authenticated response payload containing recipients. type: object additionalProperties: false required: @@ -8,8 +9,10 @@ components: - recipients properties: accessToken: + description: Refreshed access token to be used in subsequent API calls. $ref: ../../../models/auth/token_data.yaml#/components/schemas/TokenData recipients: + description: Collection of recipients available to the authenticated account. type: array items: $ref: ../../../models/recipient/recipient.yaml#/components/schemas/Recipient diff --git a/interface/api/response/error.yaml b/interface/api/response/error.yaml index 7801100e..c4a70141 100644 --- a/interface/api/response/error.yaml +++ b/interface/api/response/error.yaml @@ -1,6 +1,7 @@ components: schemas: ApiError: + description: Standard API error envelope with optional debugging details. type: object additionalProperties: false required: @@ -9,11 +10,15 @@ components: - source properties: code: + description: Application-specific numeric error code. type: integer format: int32 error: + description: Human-readable error summary. type: string source: + description: Component or subsystem that produced the error. type: string details: + description: Optional detailed message providing additional failure context. type: string diff --git a/interface/api/verification/bodies/verification.yaml b/interface/api/verification/bodies/verification.yaml index 79cc79f9..da9dbd6b 100644 --- a/interface/api/verification/bodies/verification.yaml +++ b/interface/api/verification/bodies/verification.yaml @@ -1,6 +1,7 @@ components: requestBodies: VerificationCodeBody: + description: JSON payload used to request a new verification code. required: true content: application/json: @@ -8,6 +9,7 @@ components: $ref: ../request/verification.yaml#/components/schemas/VerificationCodeRequest VerifyCodeBody: + description: JSON payload containing a verification code to validate. required: true content: application/json: diff --git a/interface/api/verification/response/verification.yaml b/interface/api/verification/response/verification.yaml index 121f51f7..cbc7fe33 100644 --- a/interface/api/verification/response/verification.yaml +++ b/interface/api/verification/response/verification.yaml @@ -1,6 +1,7 @@ components: schemas: VerificationResponseData: + description: Response data returned after requesting a verification code. type: object additionalProperties: false required: @@ -10,17 +11,22 @@ components: - target properties: idempotencyKey: + description: Idempotency key associated with the verification request. type: string ttl_seconds: + description: Verification code validity period in seconds. type: integer format: int32 cooldown_seconds: + description: Cooldown period before another code can be requested. type: integer format: int32 target: + description: Destination where the verification code was sent. type: string VerifyResultData: + description: Result payload returned after code verification succeeds. oneOf: - $ref: ../../accounts/response/auth.yaml#/components/schemas/LoginData - $ref: ../../response/response.yaml#/components/schemas/SuccessResultData diff --git a/interface/models/account/account.yaml b/interface/models/account/account.yaml index 33b0d001..b47ae25d 100644 --- a/interface/models/account/account.yaml +++ b/interface/models/account/account.yaml @@ -1,12 +1,15 @@ components: schemas: AccountData: + description: Extended account payload including visibility-specific metadata. allOf: - $ref: ./account_public.yaml#/components/schemas/AccountPublic - type: object + description: Account attributes that are not part of the public profile. additionalProperties: false required: - isAnonymous properties: isAnonymous: + description: Indicates whether the account is marked as anonymous. type: boolean diff --git a/interface/models/account/account_public.yaml b/interface/models/account/account_public.yaml index 1e98a6c3..3f807896 100644 --- a/interface/models/account/account_public.yaml +++ b/interface/models/account/account_public.yaml @@ -1,10 +1,12 @@ components: schemas: AccountPublic: + description: Public account profile data exposed in API responses. allOf: - $ref: ../storable.yaml#/components/schemas/Storable - $ref: ../common/describable.yaml#/components/schemas/Describable - type: object + description: Account-specific public attributes. additionalProperties: false required: - login @@ -13,14 +15,19 @@ components: - isArchived properties: login: + description: Account login identifier represented as an email address. type: string format: email locale: + description: Preferred locale used for account-facing content. type: string lastName: + description: Account holder last name. type: string avatarUrl: + description: Optional URL of the account avatar image. type: string nullable: true isArchived: + description: Indicates whether the account is archived and inactive. type: boolean diff --git a/interface/models/auth/client_refresh_token.yaml b/interface/models/auth/client_refresh_token.yaml index 34bd14a1..43408e36 100644 --- a/interface/models/auth/client_refresh_token.yaml +++ b/interface/models/auth/client_refresh_token.yaml @@ -1,12 +1,15 @@ components: schemas: ClientRefreshToken: + description: Refresh token bound to a specific client and device session. allOf: - $ref: ./session_identifier.yaml#/components/schemas/SessionIdentifier - type: object + description: Refresh-token payload associated with the session identifier. additionalProperties: false required: - token properties: token: + description: Long-lived token used to obtain a new access token. type: string diff --git a/interface/models/auth/login_data.yaml b/interface/models/auth/login_data.yaml index 5123b778..480b3d20 100644 --- a/interface/models/auth/login_data.yaml +++ b/interface/models/auth/login_data.yaml @@ -1,6 +1,7 @@ components: schemas: LoginData: + description: Credentials and optional locale used during user sign-in. type: object additionalProperties: false required: @@ -8,10 +9,13 @@ components: - password properties: login: + description: User login identifier, represented as an email address. type: string format: email password: + description: User password submitted for authentication. type: string format: password locale: + description: Optional locale preference provided at login time. type: string diff --git a/interface/models/auth/session_identifier.yaml b/interface/models/auth/session_identifier.yaml index 1b0f0ebd..fba55b0e 100644 --- a/interface/models/auth/session_identifier.yaml +++ b/interface/models/auth/session_identifier.yaml @@ -1,6 +1,7 @@ components: schemas: SessionIdentifier: + description: Identifies an authentication session by client and device. type: object additionalProperties: false required: @@ -8,6 +9,8 @@ components: - deviceId properties: clientId: + description: Client application identifier associated with the session. type: string deviceId: + description: Device/server identifier associated with the client session. type: string diff --git a/interface/models/auth/token_data.yaml b/interface/models/auth/token_data.yaml index 4cdd414e..5e6264d6 100644 --- a/interface/models/auth/token_data.yaml +++ b/interface/models/auth/token_data.yaml @@ -1,6 +1,7 @@ components: schemas: TokenData: + description: Authentication token payload with expiration metadata. type: object additionalProperties: false required: @@ -8,7 +9,9 @@ components: - expiration properties: token: + description: Issued authentication token value. type: string expiration: + description: RFC 3339 timestamp when the token expires. type: string format: date-time diff --git a/interface/models/callback/callback.yaml b/interface/models/callback/callback.yaml index fa66e4a4..cda3b935 100644 --- a/interface/models/callback/callback.yaml +++ b/interface/models/callback/callback.yaml @@ -1,67 +1,89 @@ components: schemas: - CallbackRetryPolicy: + CallbackBackoff: + description: Backoff window used between callback retry attempts. type: object additionalProperties: false required: - minDelayMs - maxDelayMs + properties: + minDelayMs: + description: Minimum delay in milliseconds before scheduling a retry attempt. + type: integer + minimum: 1 + maxDelayMs: + description: Maximum delay in milliseconds before scheduling a retry attempt. + type: integer + minimum: 1 + + CallbackRetryPolicy: + description: Retry and delivery behavior for callback requests. + type: object + additionalProperties: false + required: + - backoff - signingMode - maxAttempts - requestTimeoutMs properties: - minDelayMs: - type: integer - minimum: 1 - maxDelayMs: - type: integer - minimum: 1 + backoff: + description: Delay boundaries used to compute retry backoff between attempts. + $ref: '#/components/schemas/CallbackBackoff' signingMode: + description: Request-signing strategy applied when dispatching callback payloads. type: string enum: - none - hmac_sha256 - secretRef: - type: string - nullable: true headers: + description: Additional HTTP headers included with every callback request. type: object additionalProperties: type: string maxAttempts: + description: Maximum number of delivery attempts before marking dispatch as failed. type: integer minimum: 1 requestTimeoutMs: + description: Per-request timeout in milliseconds for callback HTTP calls. type: integer minimum: 1 - Callback: + CallbackContent: + description: Callback endpoint configuration and delivery parameters. allOf: - - $ref: ../permission_bound.yaml#/components/schemas/PermissionBound - - $ref: ../common/describable.yaml#/components/schemas/Describable - type: object additionalProperties: false required: - - clientId - status - url - eventTypes - retryPolicy properties: - clientId: - type: string status: + description: Operational state controlling whether callback delivery is enabled. type: string enum: - active - disabled url: + description: Absolute HTTPS endpoint URL that receives callback events. type: string format: uri eventTypes: + description: Event type names that trigger callback delivery to this endpoint. type: array items: type: string retryPolicy: + description: Retry and timeout parameters used when dispatching callback events. $ref: '#/components/schemas/CallbackRetryPolicy' + + Callback: + description: Stored callback configuration bound to an organization permission scope. + allOf: + - $ref: ../permission_bound.yaml#/components/schemas/PermissionBound + - $ref: ../common/describable.yaml#/components/schemas/Describable + - $ref: '#/components/schemas/CallbackContent' diff --git a/interface/models/common/describable.yaml b/interface/models/common/describable.yaml index 30a7dabd..1bf6d5b7 100644 --- a/interface/models/common/describable.yaml +++ b/interface/models/common/describable.yaml @@ -1,13 +1,16 @@ components: schemas: Describable: + description: Common naming metadata for user-defined entities. type: object additionalProperties: false required: - name properties: name: + description: Human-readable name of the entity. type: string description: + description: Optional free-form description of the entity. type: string nullable: true diff --git a/interface/models/common/money.yaml b/interface/models/common/money.yaml index 379433d8..b130b4c9 100644 --- a/interface/models/common/money.yaml +++ b/interface/models/common/money.yaml @@ -1,6 +1,7 @@ components: schemas: Money: + description: Monetary value represented by a decimal amount and ISO currency code. type: object additionalProperties: false required: @@ -12,5 +13,6 @@ components: description: Decimal string amount. example: '125.50' currency: + description: ISO 4217 currency code for the provided amount. type: string example: USD diff --git a/interface/models/common/pagination.yaml b/interface/models/common/pagination.yaml index bd6e8b04..2a923c33 100644 --- a/interface/models/common/pagination.yaml +++ b/interface/models/common/pagination.yaml @@ -1,8 +1,10 @@ components: schemas: CursorPageResponse: + description: Cursor-based pagination metadata returned with list responses. type: object additionalProperties: false properties: next_cursor: + description: Opaque cursor to request the next page; omitted when no more items exist. type: string diff --git a/interface/models/objectid.yaml b/interface/models/objectid.yaml index 4ec8dd47..01f1c3dd 100644 --- a/interface/models/objectid.yaml +++ b/interface/models/objectid.yaml @@ -1,7 +1,9 @@ components: schemas: ObjectId: + description: Object identifier represented as a 24-character hexadecimal string. type: string + format: objectid pattern: '^[a-fA-F0-9]{24}$' examples: - 64f85f5f4c7dbf7cfb8f3f10 diff --git a/interface/models/organization/organization.yaml b/interface/models/organization/organization.yaml index 6bd6f12b..6f80c99e 100644 --- a/interface/models/organization/organization.yaml +++ b/interface/models/organization/organization.yaml @@ -7,11 +7,8 @@ components: - type: object additionalProperties: false required: - - tenantRef - timeZone properties: - tenantRef: - $ref: ../objectid.yaml#/components/schemas/ObjectId timeZone: type: string logoUrl: diff --git a/interface/models/payment/payment.yaml b/interface/models/payment/payment.yaml index 0044b9cc..7685511c 100644 --- a/interface/models/payment/payment.yaml +++ b/interface/models/payment/payment.yaml @@ -1,39 +1,50 @@ components: schemas: Asset: + description: Blockchain asset identifier used for on-chain transfers. type: object additionalProperties: false properties: chain: + description: Chain/network where the asset exists. $ref: ../../external/chain_network.yaml#/components/schemas/ChainNetwork token_symbol: + description: Symbol of the token on the selected chain. type: string contract_address: + description: Smart-contract address for tokenized assets. type: string LedgerEndpoint: + description: Internal ledger-based endpoint used as payment source or destination. type: object additionalProperties: false required: - ledger_account_ref properties: ledger_account_ref: + description: Reference of the primary ledger account. type: string contra_ledger_account_ref: + description: Optional contra ledger account reference for balancing entries. type: string ManagedWalletEndpoint: + description: Endpoint referencing a managed wallet within the platform. type: object additionalProperties: false required: - managed_wallet_ref properties: managed_wallet_ref: + description: Reference of the managed wallet. type: string asset: + description: Asset details for the managed wallet transfer. $ref: ./payment.yaml#/components/schemas/Asset ExternalChainEndpoint: + description: Endpoint representing an external blockchain address. type: object additionalProperties: false required: @@ -41,56 +52,72 @@ components: - address properties: asset: + description: Asset to be transferred to or from the external address. $ref: ./payment.yaml#/components/schemas/Asset address: + description: Target blockchain address. type: string memo: + description: Optional destination memo or tag required by some chains. type: string CardEndpoint: + description: Raw card details endpoint used for card-based payment flows. type: object additionalProperties: false required: - pan properties: pan: + description: Primary account number of the card. type: string firstName: + description: Cardholder first name. type: string lastName: + description: Cardholder last name. type: string exp_month: + description: Card expiration month. type: integer format: int32 minimum: 0 exp_year: + description: Card expiration year. type: integer format: int32 minimum: 0 country: + description: Card issuing country code. type: string CardTokenEndpoint: + description: Tokenized card endpoint used instead of raw PAN details. type: object additionalProperties: false required: - token properties: token: + description: Provider-issued card token. type: string masked_pan: + description: Masked PAN representation for display and auditing. type: string WalletEndpoint: + description: Generic wallet endpoint identified by wallet ID. type: object additionalProperties: false required: - walletId properties: walletId: + description: Unique identifier of the wallet. type: string Endpoint: + description: Polymorphic payment endpoint definition. type: object additionalProperties: false required: @@ -98,8 +125,10 @@ components: - data properties: type: + description: Endpoint type determining the shape of the `data` field. $ref: ../../external/endpoint_type.yaml#/components/schemas/EndpointType data: + description: Endpoint payload corresponding to the selected endpoint type. oneOf: - $ref: ./payment.yaml#/components/schemas/LedgerEndpoint - $ref: ./payment.yaml#/components/schemas/ManagedWalletEndpoint @@ -108,36 +137,49 @@ components: - $ref: ./payment.yaml#/components/schemas/CardTokenEndpoint - $ref: ./payment.yaml#/components/schemas/WalletEndpoint metadata: + description: Optional key-value metadata attached to the endpoint. type: object additionalProperties: type: string Customer: + description: Customer identity and address attributes for compliance and routing. type: object additionalProperties: false properties: id: + description: External or internal customer identifier. type: string first_name: + description: Customer first name. type: string middle_name: + description: Customer middle name. type: string last_name: + description: Customer last name. type: string ip: + description: Source IP address associated with the payment request. type: string zip: + description: Postal or ZIP code. type: string country: + description: Country code of customer residence or billing address. type: string state: + description: State or region. type: string city: + description: City name. type: string address: + description: Street address line. type: string CurrencyPair: + description: Foreign-exchange currency pair. type: object additionalProperties: false required: @@ -145,11 +187,14 @@ components: - quote properties: base: + description: Base currency code. type: string quote: + description: Quote currency code. type: string FxIntent: + description: FX execution preferences associated with a payment. type: object additionalProperties: false required: @@ -157,21 +202,28 @@ components: - side properties: pair: + description: Currency pair for FX conversion. $ref: ./payment.yaml#/components/schemas/CurrencyPair side: + description: Side of the FX trade intent. $ref: ../../external/fx_side.yaml#/components/schemas/FxSide firm: + description: Whether a firm quote is required. type: boolean ttl_ms: + description: Desired quote time-to-live in milliseconds. type: integer format: int64 preferred_provider: + description: Preferred FX liquidity provider identifier. type: string max_age_ms: + description: Maximum accepted quote age in milliseconds. type: integer format: int32 PaymentIntent: + description: Requested payment execution parameters before processing. type: object additionalProperties: false required: @@ -181,160 +233,219 @@ components: - amount properties: kind: + description: High-level payment kind that drives orchestration behavior. $ref: ../../external/payment_kind.yaml#/components/schemas/PaymentKind source: + description: Source endpoint from which funds are debited. $ref: ./payment.yaml#/components/schemas/Endpoint destination: + description: Destination endpoint to which funds are credited. $ref: ./payment.yaml#/components/schemas/Endpoint amount: + description: Requested payment amount. $ref: ../common/money.yaml#/components/schemas/Money fx: + description: Optional FX preferences when conversion is needed. $ref: ./payment.yaml#/components/schemas/FxIntent settlement_mode: + description: Settlement strategy for the transfer. $ref: ../../external/settlement_mode.yaml#/components/schemas/SettlementMode fee_treatment: + description: How fees should be applied to source/destination amounts. type: string enum: - unspecified - add_to_source - deduct_from_destination attributes: + description: Additional key-value attributes used by downstream processors. type: object additionalProperties: type: string customer: + description: Optional customer information attached to the payment intent. $ref: ./payment.yaml#/components/schemas/Customer FeeLine: + description: Single fee component included in a payment quote. type: object additionalProperties: false properties: ledgerAccountRef: + description: Ledger account reference receiving or charging this fee. type: string amount: + description: Monetary amount of the fee line. $ref: ../common/money.yaml#/components/schemas/Money lineType: + description: Fee line classification. type: string side: + description: Indicates whether the fee applies on debit or credit side. type: string meta: + description: Optional metadata for fee calculation traceability. type: object additionalProperties: type: string QuoteFees: + description: Collection of fee lines for a quoted payment. type: object additionalProperties: false properties: lines: + description: List of individual quoted fee lines. type: array items: $ref: ./payment.yaml#/components/schemas/FeeLine QuoteAmounts: + description: Amount breakdown returned with a payment quote. type: object additionalProperties: false properties: sourcePrincipal: + description: Principal amount debited from the source before fees. $ref: ../common/money.yaml#/components/schemas/Money sourceDebitTotal: + description: Total amount debited from the source including adjustments. $ref: ../common/money.yaml#/components/schemas/Money destinationSettlement: + description: Net amount expected to settle at the destination. $ref: ../common/money.yaml#/components/schemas/Money FxQuote: + description: FX quote details used to price converted payment amounts. type: object additionalProperties: false properties: quoteRef: + description: Identifier of the FX quote. type: string baseCurrency: + description: Base currency of the quote pair. type: string quoteCurrency: + description: Quote currency of the quote pair. type: string side: + description: FX side used for pricing. type: string price: + description: Quoted FX price as a decimal string. type: string baseAmount: + description: Base currency amount covered by this quote. $ref: ../common/money.yaml#/components/schemas/Money quoteAmount: + description: Quote currency amount corresponding to the base amount. $ref: ../common/money.yaml#/components/schemas/Money expiresAtUnixMs: + description: Quote expiration timestamp in Unix milliseconds. type: integer format: int64 pricedAtUnixMs: + description: Quote pricing timestamp in Unix milliseconds. type: integer format: int64 provider: + description: Provider that supplied the FX quote. type: string rateRef: + description: Provider-specific rate reference identifier. type: string firm: + description: Indicates whether the FX quote is firm. type: boolean PaymentQuote: + description: Pricing result for a specific payment intent. type: object additionalProperties: false properties: quoteRef: + description: Unique identifier of the payment quote. type: string intentRef: + description: Reference to the payment intent used for quoting. type: string amounts: + description: Amount breakdown for source and destination values. $ref: ./payment.yaml#/components/schemas/QuoteAmounts fees: + description: Fee breakdown included in the quote. $ref: ./payment.yaml#/components/schemas/QuoteFees fxQuote: + description: Associated FX quote when conversion is part of the payment. $ref: ./payment.yaml#/components/schemas/FxQuote PaymentOperation: + description: Execution step record captured during payment processing. type: object additionalProperties: false properties: stepRef: + description: Identifier of the orchestration step. type: string code: + description: Operation code representing the executed action. type: string state: + description: Current state of the operation. type: string label: + description: Human-readable operation label. type: string failureCode: + description: Machine-readable failure code when operation fails. type: string failureReason: + description: Human-readable failure reason when operation fails. type: string startedAt: + description: RFC 3339 timestamp when execution of the operation started. type: string format: date-time completedAt: + description: RFC 3339 timestamp when execution of the operation completed. type: string format: date-time Payment: + description: Persisted payment aggregate with status, quote, and operation history. type: object additionalProperties: false properties: paymentRef: + description: Unique payment reference identifier. type: string idempotencyKey: + description: Idempotency key used to safely deduplicate create requests. type: string state: + description: Current lifecycle state of the payment. $ref: ../../external/payment_state.yaml#/components/schemas/PaymentState failureCode: + description: Failure code set when the payment cannot be completed. type: string failureReason: + description: Human-readable explanation of the failure. type: string operations: + description: Chronological list of operations executed for this payment. type: array items: $ref: ./payment.yaml#/components/schemas/PaymentOperation lastQuote: + description: Most recent payment quote used or generated for the payment. $ref: ./payment.yaml#/components/schemas/PaymentQuote createdAt: + description: RFC 3339 timestamp when the payment was created. type: string format: date-time meta: + description: Additional metadata captured for the payment. type: object additionalProperties: type: string diff --git a/interface/models/permission_bound.yaml b/interface/models/permission_bound.yaml index 5a889b22..c574a133 100644 --- a/interface/models/permission_bound.yaml +++ b/interface/models/permission_bound.yaml @@ -1,9 +1,11 @@ components: schemas: PermissionBound: + description: Association between an organization and a permission, including archival state. allOf: - $ref: ./storable.yaml#/components/schemas/Storable - type: object + description: Permission binding details scoped to an organization. additionalProperties: false required: - organizationRef @@ -11,8 +13,11 @@ components: - isArchived properties: organizationRef: + description: Identifier of the organization that owns this permission binding. $ref: ./objectid.yaml#/components/schemas/ObjectId permissionRef: + description: Identifier of the permission granted to the organization. $ref: ./objectid.yaml#/components/schemas/ObjectId isArchived: + description: Indicates whether this object is archived and inactive. type: boolean diff --git a/interface/models/storable.yaml b/interface/models/storable.yaml index cf2e4966..325b2914 100644 --- a/interface/models/storable.yaml +++ b/interface/models/storable.yaml @@ -1,6 +1,7 @@ components: schemas: Storable: + description: Common persistence metadata included on stored entities. type: object additionalProperties: false required: @@ -9,10 +10,13 @@ components: - updatedAt properties: id: + description: Unique identifier assigned to the stored entity. $ref: ./objectid.yaml#/components/schemas/ObjectId createdAt: + description: RFC 3339 timestamp indicating when the entity was created. type: string format: date-time updatedAt: + description: RFC 3339 timestamp indicating the most recent entity update. type: string format: date-time