From bae4cd6e35578dcfb8f6910c9dee561c6fa8b9ff Mon Sep 17 00:00:00 2001 From: Stephan D Date: Tue, 3 Mar 2026 01:07:35 +0100 Subject: [PATCH] improved logging in callbacks --- .../fees/internal/service/fees/service.go | 3 +- .../bff/interface/api/srequest/payment.go | 12 ++++---- .../internal/server/accountapiimp/password.go | 6 ++-- .../internal/server/callbacksimp/secrets.go | 3 +- .../internal/server/ledgerapiimp/create.go | 3 +- .../bff/internal/server/ledgerapiimp/list.go | 2 +- .../bff/internal/server/paymentapiimp/pay.go | 3 +- .../internal/server/paymentapiimp/pay_test.go | 22 +++++++++++++-- .../internal/server/paymentapiimp/paybatch.go | 6 ++-- .../server/paymentapiimp/paybatch_test.go | 21 +++++++++++++- .../internal/server/paymentapiimp/quote.go | 5 ++-- .../internal/server/walletapiimp/balance.go | 17 +++++------ .../internal/server/walletapiimp/create.go | 4 +-- .../bff/internal/server/walletapiimp/list.go | 4 +-- .../callbacks/internal/delivery/service.go | 27 +++++++++--------- .../internal/subscriptions/service.go | 5 ++-- api/ledger/internal/service/ledger/posting.go | 3 +- .../internal/service/ledger/posting_debit.go | 5 ++-- .../service/ledger/posting_external.go | 9 +++--- .../internal/service/ledger/posting_fx.go | 5 ++-- .../service/ledger/posting_support.go | 2 +- .../service/ledger/posting_transfer.go | 5 ++-- api/ledger/storage/mongo/store/balances.go | 6 ++-- .../internal/service/methods/get_private.go | 2 +- .../service/methods/recipient_consumer.go | 17 +++++------ .../service/orchestrationv2/agg/service.go | 3 +- .../service/orchestrationv2/idem/service.go | 3 +- .../service/orchestrationv2/pquery/service.go | 5 ++-- .../service/orchestrationv2/prepo/service.go | 12 ++++---- .../orchestrationv2/psvc/execute_batch.go | 7 +++-- .../orchestrationv2/psvc/status_publish.go | 3 +- .../service/orchestrationv2/qsnap/module.go | 4 +-- .../orchestrationv2/qsnap/resolve_all_test.go | 28 +++++++++---------- .../service/orchestrationv2/qsnap/service.go | 9 +++--- .../service/orchestrator/external_runtime.go | 8 ++++-- .../quotation/quotation_service_v2/logging.go | 5 ++-- .../db/internal/mongo/indexable/indexable.go | 17 +++++------ .../lib/api/requests/payment/initiate.dart | 6 ++-- .../requests/payment/initiate_payments.dart | 6 ++-- .../provider/payment/multiple/provider.dart | 3 +- .../lib/provider/payment/provider.dart | 28 ++++++++++++++----- .../pshared/lib/service/payment/multiple.dart | 3 +- .../pshared/lib/service/payment/service.dart | 2 ++ .../test/payment/request_dto_format_test.dart | 14 +++------- interface/api/payments/request/payment.yaml | 9 ++++++ 45 files changed, 226 insertions(+), 146 deletions(-) diff --git a/api/billing/fees/internal/service/fees/service.go b/api/billing/fees/internal/service/fees/service.go index 2cdec8d7..a72f6bec 100644 --- a/api/billing/fees/internal/service/fees/service.go +++ b/api/billing/fees/internal/service/fees/service.go @@ -21,6 +21,7 @@ import ( msg "github.com/tech/sendico/pkg/messaging" "github.com/tech/sendico/pkg/mlogger" "github.com/tech/sendico/pkg/mservice" + "github.com/tech/sendico/pkg/mutil/mzap" feesv1 "github.com/tech/sendico/pkg/proto/billing/fees/v1" tracev1 "github.com/tech/sendico/pkg/proto/common/trace/v1" "go.mongodb.org/mongo-driver/v2/bson" @@ -385,7 +386,7 @@ func (s *Service) computeQuoteWithTime(ctx context.Context, orgRef bson.ObjectID logFields := []zap.Field{zap.Time("booked_at_used", bookedAt)} if !orgRef.IsZero() { - logFields = append(logFields, zap.String("organization_ref", orgRef.Hex())) + logFields = append(logFields, mzap.ObjRef("organization_ref", orgRef)) } logFields = append(logFields, logFieldsFromIntent(intent)...) diff --git a/api/edge/bff/interface/api/srequest/payment.go b/api/edge/bff/interface/api/srequest/payment.go index b7c51c8b..328543d0 100644 --- a/api/edge/bff/interface/api/srequest/payment.go +++ b/api/edge/bff/interface/api/srequest/payment.go @@ -73,9 +73,10 @@ func validateQuoteIdempotency(previewOnly bool, idempotencyKey string) error { } type InitiatePayment struct { - PaymentBase `json:",inline"` - Intent *PaymentIntent `json:"intent,omitempty"` - QuoteRef string `json:"quoteRef,omitempty"` + PaymentBase `json:",inline"` + Intent *PaymentIntent `json:"intent,omitempty"` + QuoteRef string `json:"quoteRef,omitempty"` + ClientPaymentRef string `json:"clientPaymentRef,omitempty"` } func (r InitiatePayment) Validate() error { @@ -106,8 +107,9 @@ func (r InitiatePayment) Validate() error { } type InitiatePayments struct { - PaymentBase `json:",inline"` - QuoteRef string `json:"quoteRef,omitempty"` + PaymentBase `json:",inline"` + QuoteRef string `json:"quoteRef,omitempty"` + ClientPaymentRef string `json:"clientPaymentRef,omitempty"` } func (r *InitiatePayments) Validate() error { diff --git a/api/edge/bff/internal/server/accountapiimp/password.go b/api/edge/bff/internal/server/accountapiimp/password.go index eb96794c..b429621b 100644 --- a/api/edge/bff/internal/server/accountapiimp/password.go +++ b/api/edge/bff/internal/server/accountapiimp/password.go @@ -120,11 +120,11 @@ func (a *AccountAPI) resetPassword(r *http.Request) http.HandlerFunc { var user model.Account err = a.db.Get(ctx, accountRef, &user) if errors.Is(err, merrors.ErrNoData) { - a.logger.Info("User not found for password reset", zap.String("account_ref", accountRef.Hex())) + a.logger.Info("User not found for password reset", mzap.ObjRef("account_ref", accountRef)) return response.NotFound(a.logger, a.Name(), "User not found") } if err != nil { - a.logger.Warn("Failed to get user for password reset", zap.Error(err), zap.String("account_ref", accountRef.Hex())) + a.logger.Warn("Failed to get user for password reset", zap.Error(err), mzap.ObjRef("account_ref", accountRef)) return response.Auto(a.logger, a.Name(), err) } @@ -140,7 +140,7 @@ func (a *AccountAPI) resetPassword(r *http.Request) http.HandlerFunc { } if t.AccountRef != accountRef { - a.logger.Warn("Token account reference does not match request account reference", zap.String("token_account_ref", t.AccountRef.Hex()), zap.String("request_account_ref", accountRef.Hex())) + a.logger.Warn("Token account reference does not match request account reference", mzap.ObjRef("token_account_ref", t.AccountRef), mzap.ObjRef("request_account_ref", accountRef)) return response.DataConflict(a.logger, a.Name(), "Token does not match account") } diff --git a/api/edge/bff/internal/server/callbacksimp/secrets.go b/api/edge/bff/internal/server/callbacksimp/secrets.go index bb8de148..743f8e99 100644 --- a/api/edge/bff/internal/server/callbacksimp/secrets.go +++ b/api/edge/bff/internal/server/callbacksimp/secrets.go @@ -13,6 +13,7 @@ import ( "github.com/prometheus/client_golang/prometheus/promauto" "github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/mlogger" + "github.com/tech/sendico/pkg/mutil/mzap" "github.com/tech/sendico/pkg/vault/kv" "go.mongodb.org/mongo-driver/v2/bson" "go.uber.org/zap" @@ -152,7 +153,7 @@ func (m *vaultSigningSecretManager) Provision( } secretRef := "vault:" + secretPath + "#" + m.field - m.logger.Info("Callback signing secret stored", zap.String("secret_ref", secretRef), zap.String("callback_ref", callbackRef.Hex())) + m.logger.Info("Callback signing secret stored", zap.String("secret_ref", secretRef), mzap.ObjRef("callback_ref", callbackRef)) return secretRef, secret, nil } diff --git a/api/edge/bff/internal/server/ledgerapiimp/create.go b/api/edge/bff/internal/server/ledgerapiimp/create.go index 876a72e0..08bb8fc1 100644 --- a/api/edge/bff/internal/server/ledgerapiimp/create.go +++ b/api/edge/bff/internal/server/ledgerapiimp/create.go @@ -11,6 +11,7 @@ import ( "github.com/tech/sendico/pkg/model" "github.com/tech/sendico/pkg/model/account_role" "github.com/tech/sendico/pkg/mservice" + "github.com/tech/sendico/pkg/mutil/mzap" describablev1 "github.com/tech/sendico/pkg/proto/common/describable/v1" ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1" "github.com/tech/sendico/server/interface/api/srequest" @@ -88,7 +89,7 @@ func (a *LedgerAPI) createAccount(r *http.Request, account *model.Account, token Describable: describable, }) if err != nil { - a.logger.Warn("Failed to create ledger account", zap.Error(err), zap.String("organization_ref", orgRef.Hex())) + a.logger.Warn("Failed to create ledger account", zap.Error(err), mzap.ObjRef("organization_ref", orgRef)) return response.Auto(a.logger, mservice.Ledger, err) } diff --git a/api/edge/bff/internal/server/ledgerapiimp/list.go b/api/edge/bff/internal/server/ledgerapiimp/list.go index 708ccee4..00095b42 100644 --- a/api/edge/bff/internal/server/ledgerapiimp/list.go +++ b/api/edge/bff/internal/server/ledgerapiimp/list.go @@ -47,7 +47,7 @@ func (a *LedgerAPI) listAccounts(r *http.Request, account *model.Account, token resp, err := a.client.ListAccounts(ctx, req) if err != nil { - a.logger.Warn("Failed to list ledger accounts", zap.Error(err), zap.String("organization_ref", orgRef.Hex())) + a.logger.Warn("Failed to list ledger accounts", zap.Error(err), mzap.ObjRef("organization_ref", orgRef)) return response.Auto(a.logger, mservice.Ledger, err) } diff --git a/api/edge/bff/internal/server/paymentapiimp/pay.go b/api/edge/bff/internal/server/paymentapiimp/pay.go index f74ddb76..88c68896 100644 --- a/api/edge/bff/internal/server/paymentapiimp/pay.go +++ b/api/edge/bff/internal/server/paymentapiimp/pay.go @@ -89,7 +89,7 @@ func (a *PaymentAPI) initiatePayment(r *http.Request, account *model.Account, to req := &orchestrationv2.ExecutePaymentRequest{ Meta: requestMeta(orgRef.Hex(), payload.IdempotencyKey), QuotationRef: quotationRef, - ClientPaymentRef: metadataValue(payload.Metadata, "client_payment_ref"), + ClientPaymentRef: strings.TrimSpace(payload.ClientPaymentRef), } resp, err := a.execution.ExecutePayment(ctx, req) @@ -110,6 +110,7 @@ func decodeInitiatePayload(r *http.Request) (*srequest.InitiatePayment, error) { } payload.IdempotencyKey = strings.TrimSpace(payload.IdempotencyKey) payload.QuoteRef = strings.TrimSpace(payload.QuoteRef) + payload.ClientPaymentRef = strings.TrimSpace(payload.ClientPaymentRef) if err := payload.Validate(); err != nil { return nil, err diff --git a/api/edge/bff/internal/server/paymentapiimp/pay_test.go b/api/edge/bff/internal/server/paymentapiimp/pay_test.go index 417853a7..97d1175a 100644 --- a/api/edge/bff/internal/server/paymentapiimp/pay_test.go +++ b/api/edge/bff/internal/server/paymentapiimp/pay_test.go @@ -14,12 +14,12 @@ import ( "go.mongodb.org/mongo-driver/v2/bson" ) -func TestInitiateByQuote_DoesNotUseIntentRef(t *testing.T) { +func TestInitiateByQuote_ForwardsClientPaymentRef(t *testing.T) { orgRef := bson.NewObjectID() exec := &fakeExecutionClientForBatch{} api := newBatchAPI(exec) - body := `{"idempotencyKey":"idem-by-quote","quoteRef":"quote-1","metadata":{"client_payment_ref":"client-ref-1"}}` + body := `{"idempotencyKey":"idem-by-quote","quoteRef":"quote-1","clientPaymentRef":"client-ref-1"}` rr := invokeInitiateByQuote(t, api, orgRef, body) if got, want := rr.Code, http.StatusOK; got != want { t.Fatalf("status mismatch: got=%d want=%d body=%s", got, want, rr.Body.String()) @@ -32,6 +32,24 @@ func TestInitiateByQuote_DoesNotUseIntentRef(t *testing.T) { } } +func TestInitiateByQuote_DoesNotForwardLegacyClientPaymentRefFromMetadata(t *testing.T) { + orgRef := bson.NewObjectID() + exec := &fakeExecutionClientForBatch{} + api := newBatchAPI(exec) + + body := `{"idempotencyKey":"idem-by-quote","quoteRef":"quote-1","metadata":{"client_payment_ref":"legacy-client-ref"}}` + rr := invokeInitiateByQuote(t, api, orgRef, body) + if got, want := rr.Code, http.StatusOK; got != want { + t.Fatalf("status mismatch: got=%d want=%d body=%s", got, want, rr.Body.String()) + } + if got, want := len(exec.executeReqs), 1; got != want { + t.Fatalf("execute calls mismatch: got=%d want=%d", got, want) + } + if got := exec.executeReqs[0].GetClientPaymentRef(); got != "" { + t.Fatalf("expected empty client_payment_ref, got=%q", got) + } +} + func TestInitiateByQuote_RejectsMetadataIntentRef(t *testing.T) { orgRef := bson.NewObjectID() exec := &fakeExecutionClientForBatch{} diff --git a/api/edge/bff/internal/server/paymentapiimp/paybatch.go b/api/edge/bff/internal/server/paymentapiimp/paybatch.go index 2b01d42c..71b81779 100644 --- a/api/edge/bff/internal/server/paymentapiimp/paybatch.go +++ b/api/edge/bff/internal/server/paymentapiimp/paybatch.go @@ -8,6 +8,7 @@ import ( "github.com/tech/sendico/pkg/api/http/response" "github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/model" + "github.com/tech/sendico/pkg/mutil/mzap" orchestrationv2 "github.com/tech/sendico/pkg/proto/payments/orchestration/v2" "github.com/tech/sendico/server/interface/api/srequest" "github.com/tech/sendico/server/interface/api/sresponse" @@ -39,7 +40,7 @@ func (a *PaymentAPI) initiatePaymentsByQuote(r *http.Request, account *model.Acc return response.BadPayload(a.logger, a.Name(), err) } - clientPaymentRef := metadataValue(payload.Metadata, "client_payment_ref") + clientPaymentRef := strings.TrimSpace(payload.ClientPaymentRef) idempotencyKey := strings.TrimSpace(payload.IdempotencyKey) quotationRef := strings.TrimSpace(payload.QuoteRef) @@ -50,7 +51,7 @@ func (a *PaymentAPI) initiatePaymentsByQuote(r *http.Request, account *model.Acc } resp, err := a.execution.ExecuteBatchPayment(ctx, req) if err != nil { - a.logger.Warn("Failed to initiate batch payments", zap.Error(err), zap.String("organization_ref", orgRef.Hex())) + a.logger.Warn("Failed to initiate batch payments", zap.Error(err), mzap.ObjRef("organization_ref", orgRef)) return grpcErrorResponse(a.logger, a.Name(), err) } @@ -72,6 +73,7 @@ func decodeInitiatePaymentsPayload(r *http.Request) (*srequest.InitiatePayments, } payload.IdempotencyKey = strings.TrimSpace(payload.IdempotencyKey) payload.QuoteRef = strings.TrimSpace(payload.QuoteRef) + payload.ClientPaymentRef = strings.TrimSpace(payload.ClientPaymentRef) if err := payload.Validate(); err != nil { return nil, err diff --git a/api/edge/bff/internal/server/paymentapiimp/paybatch_test.go b/api/edge/bff/internal/server/paymentapiimp/paybatch_test.go index 886f12e6..f59f1201 100644 --- a/api/edge/bff/internal/server/paymentapiimp/paybatch_test.go +++ b/api/edge/bff/internal/server/paymentapiimp/paybatch_test.go @@ -50,7 +50,7 @@ func TestInitiatePaymentsByQuote_ForwardsClientPaymentRef(t *testing.T) { exec := &fakeExecutionClientForBatch{} api := newBatchAPI(exec) - body := `{"idempotencyKey":"idem-batch","quoteRef":"quote-1","metadata":{"client_payment_ref":"client-ref-1"}}` + body := `{"idempotencyKey":"idem-batch","quoteRef":"quote-1","clientPaymentRef":"client-ref-1"}` rr := invokeInitiatePaymentsByQuote(t, api, orgRef, body) if got, want := rr.Code, http.StatusOK; got != want { t.Fatalf("status mismatch: got=%d want=%d body=%s", got, want, rr.Body.String()) @@ -67,6 +67,25 @@ func TestInitiatePaymentsByQuote_ForwardsClientPaymentRef(t *testing.T) { } } +func TestInitiatePaymentsByQuote_DoesNotForwardLegacyClientPaymentRefFromMetadata(t *testing.T) { + orgRef := bson.NewObjectID() + exec := &fakeExecutionClientForBatch{} + api := newBatchAPI(exec) + + body := `{"idempotencyKey":"idem-batch","quoteRef":"quote-1","metadata":{"client_payment_ref":"legacy-client-ref"}}` + rr := invokeInitiatePaymentsByQuote(t, api, orgRef, body) + if got, want := rr.Code, http.StatusOK; got != want { + t.Fatalf("status mismatch: got=%d want=%d body=%s", got, want, rr.Body.String()) + } + + if got, want := len(exec.executeBatchReqs), 1; got != want { + t.Fatalf("execute batch calls mismatch: got=%d want=%d", got, want) + } + if got := exec.executeBatchReqs[0].GetClientPaymentRef(); got != "" { + t.Fatalf("expected empty client_payment_ref, got=%q", got) + } +} + func TestInitiatePaymentsByQuote_RejectsDeprecatedIntentRefField(t *testing.T) { orgRef := bson.NewObjectID() exec := &fakeExecutionClientForBatch{} diff --git a/api/edge/bff/internal/server/paymentapiimp/quote.go b/api/edge/bff/internal/server/paymentapiimp/quote.go index f9b91bcd..425ccd7a 100644 --- a/api/edge/bff/internal/server/paymentapiimp/quote.go +++ b/api/edge/bff/internal/server/paymentapiimp/quote.go @@ -8,6 +8,7 @@ import ( "github.com/tech/sendico/pkg/api/http/response" "github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/model" + "github.com/tech/sendico/pkg/mutil/mzap" quotationv2 "github.com/tech/sendico/pkg/proto/payments/quotation/v2" "github.com/tech/sendico/server/interface/api/srequest" "github.com/tech/sendico/server/interface/api/sresponse" @@ -61,7 +62,7 @@ func (a *PaymentAPI) quotePayment(r *http.Request, account *model.Account, token resp, err := a.quotation.QuotePayment(ctx, req) if err != nil { - a.logger.Warn("Failed to quote payment", zap.Error(err), zap.String("organization_ref", orgRef.Hex())) + a.logger.Warn("Failed to quote payment", zap.Error(err), mzap.ObjRef("organization_ref", orgRef)) return grpcErrorResponse(a.logger, a.Name(), err) } @@ -117,7 +118,7 @@ func (a *PaymentAPI) quotePayments(r *http.Request, account *model.Account, toke resp, err := a.quotation.QuotePayments(ctx, req) if err != nil { - a.logger.Warn("Failed to quote payments", zap.Error(err), zap.String("organization_ref", orgRef.Hex())) + a.logger.Warn("Failed to quote payments", zap.Error(err), mzap.ObjRef("organization_ref", orgRef)) return grpcErrorResponse(a.logger, a.Name(), err) } diff --git a/api/edge/bff/internal/server/walletapiimp/balance.go b/api/edge/bff/internal/server/walletapiimp/balance.go index fc45960a..b71b3b28 100644 --- a/api/edge/bff/internal/server/walletapiimp/balance.go +++ b/api/edge/bff/internal/server/walletapiimp/balance.go @@ -12,6 +12,7 @@ import ( "github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/model" "github.com/tech/sendico/pkg/mservice" + "github.com/tech/sendico/pkg/mutil/mzap" connectorv1 "github.com/tech/sendico/pkg/proto/connector/v1" "github.com/tech/sendico/server/interface/api/sresponse" mutil "github.com/tech/sendico/server/internal/mutil/param" @@ -65,24 +66,24 @@ func (a *WalletAPI) getWalletBalance(r *http.Request, account *model.Account, to return response.Auto(a.logger, a.Name(), merrors.NoData("no crypto gateways available")) } a.logger.Debug("Resolved CRYPTO gateways for wallet balance lookup", - zap.String("organization_ref", orgRef.Hex()), + mzap.ObjRef("organization_ref", orgRef), zap.String("wallet_ref", walletRef), zap.Int("gateway_count", len(cryptoGateways))) route, routeErr := a.walletRoute(ctx, orgRef.Hex(), walletRef) if routeErr != nil { - a.logger.Warn("Failed to resolve wallet route", zap.Error(routeErr), zap.String("wallet_ref", walletRef), zap.String("organization_ref", orgRef.Hex())) + a.logger.Warn("Failed to resolve wallet route", zap.Error(routeErr), zap.String("wallet_ref", walletRef), mzap.ObjRef("organization_ref", orgRef)) } if route != nil { a.logger.Debug("Resolved stored wallet route", - zap.String("organization_ref", orgRef.Hex()), + mzap.ObjRef("organization_ref", orgRef), zap.String("wallet_ref", walletRef), zap.String("route_network", route.Network), zap.String("route_gateway_id", route.GatewayID)) preferred := findGatewayForRoute(cryptoGateways, route) if preferred != nil { a.logger.Debug("Using preferred gateway from stored wallet route", - zap.String("organization_ref", orgRef.Hex()), + mzap.ObjRef("organization_ref", orgRef), zap.String("wallet_ref", walletRef), zap.String("gateway_id", preferred.ID), zap.String("network", preferred.Network), @@ -91,7 +92,7 @@ func (a *WalletAPI) getWalletBalance(r *http.Request, account *model.Account, to if preferredErr == nil && bal != nil { a.rememberWalletRoute(ctx, orgRef.Hex(), walletRef, preferred.Network, preferred.ID) a.logger.Debug("Wallet balance resolved via preferred gateway", - zap.String("organization_ref", orgRef.Hex()), + mzap.ObjRef("organization_ref", orgRef), zap.String("wallet_ref", walletRef), zap.String("gateway_id", preferred.ID), zap.String("network", preferred.Network)) @@ -124,20 +125,20 @@ func (a *WalletAPI) getWalletBalance(r *http.Request, account *model.Account, to } } else { a.logger.Warn("Stored wallet route did not match any healthy discovery gateway", - zap.String("organization_ref", orgRef.Hex()), + mzap.ObjRef("organization_ref", orgRef), zap.String("wallet_ref", walletRef), zap.String("route_network", route.Network), zap.String("route_gateway_id", route.GatewayID)) } } else { a.logger.Debug("Stored wallet route not found; using gateway fallback", - zap.String("organization_ref", orgRef.Hex()), + mzap.ObjRef("organization_ref", orgRef), zap.String("wallet_ref", walletRef)) } // Fall back to querying remaining gateways in parallel. a.logger.Debug("Starting fallback wallet balance fan-out", - zap.String("organization_ref", orgRef.Hex()), + mzap.ObjRef("organization_ref", orgRef), zap.String("wallet_ref", walletRef), zap.Int("gateway_count", len(cryptoGateways))) bal, err := a.queryBalanceFromGateways(ctx, cryptoGateways, orgRef.Hex(), walletRef) diff --git a/api/edge/bff/internal/server/walletapiimp/create.go b/api/edge/bff/internal/server/walletapiimp/create.go index 8d22f7c0..2f49ed31 100644 --- a/api/edge/bff/internal/server/walletapiimp/create.go +++ b/api/edge/bff/internal/server/walletapiimp/create.go @@ -81,7 +81,7 @@ func (a *WalletAPI) create(r *http.Request, account *model.Account, token *sresp return response.Auto(a.logger, a.Name(), merrors.InvalidArgument("no gateway available for network: "+networkName)) } a.logger.Debug("Selected gateway for wallet creation", - zap.String("organization_ref", orgRef.Hex()), + mzap.ObjRef("organization_ref", orgRef), zap.String("network", networkName), zap.String("gateway_id", gateway.ID), zap.String("gateway_network", gateway.Network), @@ -134,7 +134,7 @@ func (a *WalletAPI) create(r *http.Request, account *model.Account, token *sresp a.rememberWalletRoute(ctx, orgRef.Hex(), walletRef, networkName, gateway.ID) a.rememberWalletRoute(ctx, orgRef.Hex(), walletRef, gateway.Network, gateway.ID) a.logger.Debug("Persisted wallet route after wallet creation", - zap.String("organization_ref", orgRef.Hex()), + mzap.ObjRef("organization_ref", orgRef), zap.String("wallet_ref", walletRef), zap.String("network", networkName), zap.String("gateway_id", gateway.ID)) diff --git a/api/edge/bff/internal/server/walletapiimp/list.go b/api/edge/bff/internal/server/walletapiimp/list.go index 07c3d6eb..33e8084a 100644 --- a/api/edge/bff/internal/server/walletapiimp/list.go +++ b/api/edge/bff/internal/server/walletapiimp/list.go @@ -59,7 +59,7 @@ func (a *WalletAPI) listWallets(r *http.Request, account *model.Account, token * return sresponse.Wallets(a.logger, nil, token) } a.logger.Debug("Resolved CRYPTO gateways for wallet list", - zap.String("organization_ref", orgRef.Hex()), + mzap.ObjRef("organization_ref", orgRef), zap.Int("gateway_count", len(cryptoGateways))) // Build request @@ -80,7 +80,7 @@ func (a *WalletAPI) listWallets(r *http.Request, account *model.Account, token * allAccounts := a.queryAllGateways(ctx, cryptoGateways, req) dedupedAccounts := dedupeAccountsByWalletRef(allAccounts) a.logger.Debug("Wallet list fan-out completed", - zap.String("organization_ref", orgRef.Hex()), + mzap.ObjRef("organization_ref", orgRef), zap.Int("accounts_raw", len(allAccounts)), zap.Int("accounts_deduped", len(dedupedAccounts)), zap.Int("gateway_count", len(cryptoGateways))) diff --git a/api/edge/callbacks/internal/delivery/service.go b/api/edge/callbacks/internal/delivery/service.go index 6aaee3c7..60857870 100644 --- a/api/edge/callbacks/internal/delivery/service.go +++ b/api/edge/callbacks/internal/delivery/service.go @@ -15,6 +15,7 @@ import ( "github.com/tech/sendico/edge/callbacks/internal/storage" "github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/mlogger" + "github.com/tech/sendico/pkg/mutil/mzap" "go.uber.org/zap" ) @@ -171,14 +172,14 @@ func (s *service) handleTask(ctx context.Context, workerID string, task *model.T result = "blocked" s.logger.Warn("Blocked task delivery due to URL validation failure", zap.String("worker_id", workerID), - zap.String("task_id", task.ID.Hex()), + mzap.ObjRef("task_ref", task.ID), zap.String("event_id", task.EventID), zap.Error(err), ) if markErr := s.tasks.MarkFailed(ctx, task.ID, attempt, err.Error(), statusCode, time.Now().UTC()); markErr != nil { s.logger.Warn("Failed to mark blocked task as failed", zap.String("worker_id", workerID), - zap.String("task_id", task.ID.Hex()), + mzap.ObjRef("task_ref", task.ID), zap.Error(markErr), ) } @@ -195,7 +196,7 @@ func (s *service) handleTask(ctx context.Context, workerID string, task *model.T result = "sign_error" s.logger.Warn("Failed to sign task payload", zap.String("worker_id", workerID), - zap.String("task_id", task.ID.Hex()), + mzap.ObjRef("task_ref", task.ID), zap.String("event_id", task.EventID), zap.String("signing_mode", task.SigningMode), zap.Error(err), @@ -203,7 +204,7 @@ func (s *service) handleTask(ctx context.Context, workerID string, task *model.T if markErr := s.tasks.MarkFailed(ctx, task.ID, attempt, err.Error(), statusCode, time.Now().UTC()); markErr != nil { s.logger.Warn("Failed to mark signing-error task as failed", zap.String("worker_id", workerID), - zap.String("task_id", task.ID.Hex()), + mzap.ObjRef("task_ref", task.ID), zap.Error(markErr), ) } @@ -218,7 +219,7 @@ func (s *service) handleTask(ctx context.Context, workerID string, task *model.T result = "request_error" s.logger.Warn("Failed to build callback request", zap.String("worker_id", workerID), - zap.String("task_id", task.ID.Hex()), + mzap.ObjRef("task_ref", task.ID), zap.String("event_id", task.EventID), zap.String("endpoint_url", task.EndpointURL), zap.Error(err), @@ -226,7 +227,7 @@ func (s *service) handleTask(ctx context.Context, workerID string, task *model.T if markErr := s.tasks.MarkFailed(ctx, task.ID, attempt, err.Error(), statusCode, time.Now().UTC()); markErr != nil { s.logger.Warn("Failed to mark request-error task as failed", zap.String("worker_id", workerID), - zap.String("task_id", task.ID.Hex()), + mzap.ObjRef("task_ref", task.ID), zap.Error(markErr), ) } @@ -253,7 +254,7 @@ func (s *service) handleTask(ctx context.Context, workerID string, task *model.T case outcomeDelivered: result = string(outcomeDelivered) if err := s.tasks.MarkDelivered(ctx, task.ID, statusCode, time.Since(started), now); err != nil { - s.logger.Warn("Failed to mark task delivered", zap.String("worker_id", workerID), zap.String("task_id", task.ID.Hex()), zap.Error(err)) + s.logger.Warn("Failed to mark task delivered", zap.String("worker_id", workerID), mzap.ObjRef("task_ref", task.ID), zap.Error(err)) } case outcomeRetry: if attempt < task.MaxAttempts { @@ -265,7 +266,7 @@ func (s *service) handleTask(ctx context.Context, workerID string, task *model.T } s.logger.Warn("Task delivery retry scheduled", zap.String("worker_id", workerID), - zap.String("task_id", task.ID.Hex()), + mzap.ObjRef("task_ref", task.ID), zap.String("event_id", task.EventID), zap.Int("attempt", attempt), zap.Int("status_code", statusCode), @@ -273,7 +274,7 @@ func (s *service) handleTask(ctx context.Context, workerID string, task *model.T zap.Time("next_attempt_at", next), ) if err := s.tasks.MarkRetry(ctx, task.ID, attempt, next, lastErr, statusCode, now); err != nil { - s.logger.Warn("Failed to mark task retry", zap.String("worker_id", workerID), zap.String("task_id", task.ID.Hex()), zap.Error(err)) + s.logger.Warn("Failed to mark task retry", zap.String("worker_id", workerID), mzap.ObjRef("task_ref", task.ID), zap.Error(err)) } } else { result = string(outcomeFailed) @@ -283,7 +284,7 @@ func (s *service) handleTask(ctx context.Context, workerID string, task *model.T } s.logger.Warn("Task delivery failed after reaching max attempts", zap.String("worker_id", workerID), - zap.String("task_id", task.ID.Hex()), + mzap.ObjRef("task_ref", task.ID), zap.String("event_id", task.EventID), zap.Int("attempt", attempt), zap.Int("max_attempts", task.MaxAttempts), @@ -291,7 +292,7 @@ func (s *service) handleTask(ctx context.Context, workerID string, task *model.T zap.String("reason", lastErr), ) if err := s.tasks.MarkFailed(ctx, task.ID, attempt, lastErr, statusCode, now); err != nil { - s.logger.Warn("Failed to mark task failed", zap.String("worker_id", workerID), zap.String("task_id", task.ID.Hex()), zap.Error(err)) + s.logger.Warn("Failed to mark task failed", zap.String("worker_id", workerID), mzap.ObjRef("task_ref", task.ID), zap.Error(err)) } } default: @@ -302,14 +303,14 @@ func (s *service) handleTask(ctx context.Context, workerID string, task *model.T } s.logger.Warn("Task delivery failed", zap.String("worker_id", workerID), - zap.String("task_id", task.ID.Hex()), + mzap.ObjRef("task_ref", task.ID), zap.String("event_id", task.EventID), zap.Int("attempt", attempt), zap.Int("status_code", statusCode), zap.String("reason", lastErr), ) if err := s.tasks.MarkFailed(ctx, task.ID, attempt, lastErr, statusCode, now); err != nil { - s.logger.Warn("Failed to mark task failed", zap.String("worker_id", workerID), zap.String("task_id", task.ID.Hex()), zap.Error(err)) + s.logger.Warn("Failed to mark task failed", zap.String("worker_id", workerID), mzap.ObjRef("task_ref", task.ID), zap.Error(err)) } } } diff --git a/api/edge/callbacks/internal/subscriptions/service.go b/api/edge/callbacks/internal/subscriptions/service.go index 6d56ebb4..24f4b304 100644 --- a/api/edge/callbacks/internal/subscriptions/service.go +++ b/api/edge/callbacks/internal/subscriptions/service.go @@ -8,6 +8,7 @@ import ( "github.com/tech/sendico/edge/callbacks/internal/storage" "github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/mlogger" + "github.com/tech/sendico/pkg/mutil/mzap" "go.mongodb.org/mongo-driver/v2/bson" "go.uber.org/zap" ) @@ -45,7 +46,7 @@ func (s *service) Resolve(ctx context.Context, eventType string, organizationRef if err != nil { s.logger.Warn("Failed to resolve active endpoints", zap.String("event_type", eventType), - zap.String("organization_ref", organizationRef.Hex()), + mzap.ObjRef("organization_ref", organizationRef), zap.Error(err), ) return nil, err @@ -53,7 +54,7 @@ func (s *service) Resolve(ctx context.Context, eventType string, organizationRef s.logger.Debug("Resolved active endpoints", zap.String("event_type", eventType), - zap.String("organization_ref", organizationRef.Hex()), + mzap.ObjRef("organization_ref", organizationRef), zap.Int("endpoints", len(endpoints)), ) diff --git a/api/ledger/internal/service/ledger/posting.go b/api/ledger/internal/service/ledger/posting.go index c8e70a33..a8ca63b1 100644 --- a/api/ledger/internal/service/ledger/posting.go +++ b/api/ledger/internal/service/ledger/posting.go @@ -66,8 +66,7 @@ func (s *Service) postCreditResponder(_ context.Context, req *ledgerv1.PostCredi existingEntry, err := s.storage.JournalEntries().GetByIdempotencyKey(ctx, orgRef, req.IdempotencyKey) if err == nil && existingEntry != nil { recordDuplicateRequest(journalEntryTypeCredit) - logger.Info("Duplicate credit request (idempotency)", - zap.String("existingEntryID", existingEntry.GetID().Hex())) + logger.Info("Duplicate credit request (idempotency)", mzap.StorableRef(existingEntry)) return &ledgerv1.PostResponse{ JournalEntryRef: existingEntry.GetID().Hex(), Version: existingEntry.Version, diff --git a/api/ledger/internal/service/ledger/posting_debit.go b/api/ledger/internal/service/ledger/posting_debit.go index 4534c671..eea05f71 100644 --- a/api/ledger/internal/service/ledger/posting_debit.go +++ b/api/ledger/internal/service/ledger/posting_debit.go @@ -64,8 +64,7 @@ func (s *Service) postDebitResponder(_ context.Context, req *ledgerv1.PostDebitR existingEntry, err := s.storage.JournalEntries().GetByIdempotencyKey(ctx, orgRef, req.IdempotencyKey) if err == nil && existingEntry != nil { recordDuplicateRequest(journalEntryTypeDebit) - logger.Info("Duplicate debit request (idempotency)", - zap.String("existingEntryID", existingEntry.GetID().Hex())) + logger.Info("Duplicate debit request (idempotency)", mzap.StorableRef(existingEntry)) return &ledgerv1.PostResponse{ JournalEntryRef: existingEntry.GetID().Hex(), Version: existingEntry.Version, @@ -123,7 +122,7 @@ func (s *Service) postDebitResponder(_ context.Context, req *ledgerv1.PostDebitR if err == storage.ErrAccountNotFound { return nil, merrors.NoData(fmt.Sprintf("charges[%d]: account not found", i)) } - logger.Warn("Failed to get charge account", zap.Error(err), zap.String("chargeAccountRef", chargeAccountRef.Hex())) + logger.Warn("Failed to get charge account", zap.Error(err), mzap.ObjRef("charge_account_ref", chargeAccountRef)) return nil, merrors.Internal("failed to get charge account") } if err := validateAccountForOrg(chargeAccount, orgRef, charge.Money.Currency); err != nil { diff --git a/api/ledger/internal/service/ledger/posting_external.go b/api/ledger/internal/service/ledger/posting_external.go index 5dc6a133..12397810 100644 --- a/api/ledger/internal/service/ledger/posting_external.go +++ b/api/ledger/internal/service/ledger/posting_external.go @@ -62,7 +62,7 @@ func (s *Service) postExternalCreditResponder(_ context.Context, req *ledgerv1.P if err == nil && existingEntry != nil { recordDuplicateRequest(journalEntryTypeCredit) logger.Info("Duplicate external credit request (idempotency)", - zap.String("existingEntryID", existingEntry.GetID().Hex())) + mzap.StorableRef(existingEntry)) return &ledgerv1.PostResponse{ JournalEntryRef: existingEntry.GetID().Hex(), Version: existingEntry.Version, @@ -140,7 +140,7 @@ func (s *Service) postExternalCreditResponder(_ context.Context, req *ledgerv1.P if err == storage.ErrAccountNotFound { return nil, merrors.NoData(fmt.Sprintf("charges[%d]: account not found", i)) } - logger.Warn("Failed to get charge account", zap.Error(err), zap.String("chargeAccountRef", chargeAccountRef.Hex())) + logger.Warn("Failed to get charge account", zap.Error(err), mzap.ObjRef("charge_account_ref", chargeAccountRef)) return nil, merrors.Internal("failed to get charge account") } if err := validateAccountForOrg(chargeAccount, orgRef, charge.Money.Currency); err != nil { @@ -287,8 +287,7 @@ func (s *Service) postExternalDebitResponder(_ context.Context, req *ledgerv1.Po existingEntry, err := s.storage.JournalEntries().GetByIdempotencyKey(ctx, orgRef, req.IdempotencyKey) if err == nil && existingEntry != nil { recordDuplicateRequest(journalEntryTypeDebit) - logger.Info("Duplicate external debit request (idempotency)", - zap.String("existingEntryID", existingEntry.GetID().Hex())) + logger.Info("Duplicate external debit request (idempotency)", mzap.StorableRef(existingEntry)) return &ledgerv1.PostResponse{ JournalEntryRef: existingEntry.GetID().Hex(), Version: existingEntry.Version, @@ -366,7 +365,7 @@ func (s *Service) postExternalDebitResponder(_ context.Context, req *ledgerv1.Po if err == storage.ErrAccountNotFound { return nil, merrors.NoData(fmt.Sprintf("charges[%d]: account not found", i)) } - logger.Warn("Failed to get charge account", zap.Error(err), zap.String("chargeAccountRef", chargeAccountRef.Hex())) + logger.Warn("Failed to get charge account", zap.Error(err), mzap.ObjRef("charge_account_ref", chargeAccountRef)) return nil, merrors.Internal("failed to get charge account") } if err := validateAccountForOrg(chargeAccount, orgRef, charge.Money.Currency); err != nil { diff --git a/api/ledger/internal/service/ledger/posting_fx.go b/api/ledger/internal/service/ledger/posting_fx.go index 515fa7d9..5f7d0a79 100644 --- a/api/ledger/internal/service/ledger/posting_fx.go +++ b/api/ledger/internal/service/ledger/posting_fx.go @@ -77,8 +77,7 @@ func (s *Service) fxResponder(_ context.Context, req *ledgerv1.FXRequest) gsresp existingEntry, err := s.storage.JournalEntries().GetByIdempotencyKey(ctx, orgRef, req.IdempotencyKey) if err == nil && existingEntry != nil { recordDuplicateRequest(journalEntryTypeFX) - logger.Info("Duplicate FX request (idempotency)", - zap.String("existingEntryID", existingEntry.GetID().Hex())) + logger.Info("Duplicate FX request (idempotency)", mzap.StorableRef(existingEntry)) return &ledgerv1.PostResponse{ JournalEntryRef: existingEntry.GetID().Hex(), Version: existingEntry.Version, @@ -162,7 +161,7 @@ func (s *Service) fxResponder(_ context.Context, req *ledgerv1.FXRequest) gsresp if err == storage.ErrAccountNotFound { return nil, merrors.NoData(fmt.Sprintf("charges[%d]: account not found", i)) } - logger.Warn("Failed to get FX charge account", zap.Error(err), zap.String("chargeAccountRef", chargeAccountRef.Hex())) + logger.Warn("Failed to get FX charge account", zap.Error(err), mzap.ObjRef("charge_account_ref", chargeAccountRef)) return nil, merrors.Internal("failed to get charge account") } if err := validateAccountForOrg(chargeAccount, orgRef, charge.Money.Currency); err != nil { diff --git a/api/ledger/internal/service/ledger/posting_support.go b/api/ledger/internal/service/ledger/posting_support.go index d347a610..b2d81b6a 100644 --- a/api/ledger/internal/service/ledger/posting_support.go +++ b/api/ledger/internal/service/ledger/posting_support.go @@ -139,7 +139,7 @@ func (s *Service) resolveSettlementAccount(ctx context.Context, orgRef bson.Obje if errors.Is(err, storage.ErrAccountNotFound) { return nil, merrors.NoData("contra account not found") } - s.logger.Warn("Failed to load override contra account", zap.Error(err), zap.String("accountRef", overrideRef.Hex())) + s.logger.Warn("Failed to load override contra account", zap.Error(err), mzap.ObjRef("account_ref", overrideRef)) return nil, merrors.Internal("failed to load contra account") } if err := validateAccountForOrg(account, orgRef, currency); err != nil { diff --git a/api/ledger/internal/service/ledger/posting_transfer.go b/api/ledger/internal/service/ledger/posting_transfer.go index 4cd8d9c1..969af2e8 100644 --- a/api/ledger/internal/service/ledger/posting_transfer.go +++ b/api/ledger/internal/service/ledger/posting_transfer.go @@ -87,8 +87,7 @@ func (s *Service) transferResponder(_ context.Context, req *ledgerv1.TransferReq existingEntry, err := s.storage.JournalEntries().GetByIdempotencyKey(ctx, orgRef, req.IdempotencyKey) if err == nil && existingEntry != nil { recordDuplicateRequest(journalEntryTypeTransfer) - logger.Info("Duplicate transfer request (idempotency)", - zap.String("existingEntryID", existingEntry.GetID().Hex())) + logger.Info("Duplicate transfer request (idempotency)", mzap.StorableRef(existingEntry)) return &ledgerv1.PostResponse{ JournalEntryRef: existingEntry.GetID().Hex(), Version: existingEntry.Version, @@ -172,7 +171,7 @@ func (s *Service) transferResponder(_ context.Context, req *ledgerv1.TransferReq if err == storage.ErrAccountNotFound { return nil, merrors.NoData(fmt.Sprintf("charges[%d]: account not found", i)) } - logger.Warn("Failed to get charge account", zap.Error(err), zap.String("chargeAccountRef", chargeAccountRef.Hex())) + logger.Warn("Failed to get charge account", zap.Error(err), mzap.ObjRef("charge_account_ref", chargeAccountRef)) return nil, merrors.Internal("failed to get charge account") } if err := validateAccountForOrg(chargeAccount, orgRef, charge.Money.Currency); err != nil { diff --git a/api/ledger/storage/mongo/store/balances.go b/api/ledger/storage/mongo/store/balances.go index 95e2fd52..37837171 100644 --- a/api/ledger/storage/mongo/store/balances.go +++ b/api/ledger/storage/mongo/store/balances.go @@ -83,17 +83,17 @@ func (b *balancesStore) Upsert(ctx context.Context, balance *model.AccountBalanc if err := b.repo.FindOneByFilter(ctx, filter, existing); err != nil { if errors.Is(err, merrors.ErrNoData) { - b.logger.Debug("Inserting new balance", zap.String("accountRef", balance.AccountRef.Hex())) + b.logger.Debug("Inserting new balance", mzap.ObjRef("account_ref", balance.AccountRef)) return b.repo.Insert(ctx, balance, filter) } - b.logger.Warn("Failed to fetch balance", zap.Error(err), zap.String("accountRef", balance.AccountRef.Hex())) + b.logger.Warn("Failed to fetch balance", zap.Error(err), mzap.ObjRef("account_ref", balance.AccountRef)) return err } if existing.GetID() != nil { balance.SetID(*existing.GetID()) } - b.logger.Debug("Updating balance", zap.String("accountRef", balance.AccountRef.Hex()), + b.logger.Debug("Updating balance", mzap.ObjRef("account_ref", balance.AccountRef), zap.String("balance", balance.Balance)) return b.repo.Update(ctx, balance) } diff --git a/api/payments/methods/internal/service/methods/get_private.go b/api/payments/methods/internal/service/methods/get_private.go index 67b2abeb..2668dc53 100644 --- a/api/payments/methods/internal/service/methods/get_private.go +++ b/api/payments/methods/internal/service/methods/get_private.go @@ -11,7 +11,7 @@ import ( const ( paymentTypeAccount pkgmodel.PaymentType = 8 - maxPrivateMethodResolutionDepth = 8 + maxPrivateMethodResolutionDepth int = 8 ) func (s *Service) GetPaymentMethodPrivate(ctx context.Context, req *methodsv1.GetPaymentMethodPrivateRequest) (*methodsv1.GetPaymentMethodPrivateResponse, error) { diff --git a/api/payments/methods/internal/service/methods/recipient_consumer.go b/api/payments/methods/internal/service/methods/recipient_consumer.go index 19800e94..b92d7def 100644 --- a/api/payments/methods/internal/service/methods/recipient_consumer.go +++ b/api/payments/methods/internal/service/methods/recipient_consumer.go @@ -8,6 +8,7 @@ import ( np "github.com/tech/sendico/pkg/messaging/notifications/processor" nm "github.com/tech/sendico/pkg/model/notification" "github.com/tech/sendico/pkg/mservice" + "github.com/tech/sendico/pkg/mutil/mzap" "go.mongodb.org/mongo-driver/v2/bson" "go.uber.org/zap" ) @@ -62,25 +63,25 @@ func (s *Service) onRecipientNotification( if err != nil { s.logger.Warn("Failed to cascade archive payment methods by recipient", zap.Error(err), - zap.String("recipient_ref", recipientRef.Hex()), - zap.String("actor_account_ref", actorAccountRef.Hex())) + mzap.ObjRef("recipient_ref", recipientRef), + mzap.ObjRef("actor_account_ref", actorAccountRef)) return err } s.logger.Info("Recipient archive cascade applied to payment methods", - zap.String("recipient_ref", recipientRef.Hex()), - zap.String("actor_account_ref", actorAccountRef.Hex()), + mzap.ObjRef("recipient_ref", recipientRef), + mzap.ObjRef("actor_account_ref", actorAccountRef), zap.Int("updated_count", updated)) case nm.NADeleted: if err := s.pmstore.DeleteByRecipient(ctx, recipientRef); err != nil { s.logger.Warn("Failed to cascade delete payment methods by recipient", zap.Error(err), - zap.String("recipient_ref", recipientRef.Hex()), - zap.String("actor_account_ref", actorAccountRef.Hex())) + mzap.ObjRef("recipient_ref", recipientRef), + mzap.ObjRef("actor_account_ref", actorAccountRef)) return err } s.logger.Info("Recipient delete cascade applied to payment methods", - zap.String("recipient_ref", recipientRef.Hex()), - zap.String("actor_account_ref", actorAccountRef.Hex())) + mzap.ObjRef("recipient_ref", recipientRef), + mzap.ObjRef("actor_account_ref", actorAccountRef)) } return nil diff --git a/api/payments/orchestrator/internal/service/orchestrationv2/agg/service.go b/api/payments/orchestrator/internal/service/orchestrationv2/agg/service.go index 38d16240..b3affe03 100644 --- a/api/payments/orchestrator/internal/service/orchestrationv2/agg/service.go +++ b/api/payments/orchestrator/internal/service/orchestrationv2/agg/service.go @@ -9,6 +9,7 @@ import ( "github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/mlogger" pm "github.com/tech/sendico/pkg/model" + "github.com/tech/sendico/pkg/mutil/mzap" "go.mongodb.org/mongo-driver/v2/bson" "go.uber.org/zap" ) @@ -24,7 +25,7 @@ type svc struct { func (s *svc) Create(in Input) (payment *Payment, err error) { logger := s.logger logger.Debug("Starting Create", - zap.String("organization_ref", in.OrganizationRef.Hex()), + mzap.ObjRef("organization_ref", in.OrganizationRef), zap.String("quotation_ref", strings.TrimSpace(in.QuotationRef)), zap.Int("steps_count", len(in.Steps)), ) diff --git a/api/payments/orchestrator/internal/service/orchestrationv2/idem/service.go b/api/payments/orchestrator/internal/service/orchestrationv2/idem/service.go index 8d6b0d1d..07d24f3d 100644 --- a/api/payments/orchestrator/internal/service/orchestrationv2/idem/service.go +++ b/api/payments/orchestrator/internal/service/orchestrationv2/idem/service.go @@ -10,6 +10,7 @@ import ( "github.com/tech/sendico/payments/storage/model" "github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/mlogger" + "github.com/tech/sendico/pkg/mutil/mzap" "go.uber.org/zap" ) @@ -26,7 +27,7 @@ func (s *svc) TryReuse( ) (payment *model.Payment, reused bool, err error) { logger := s.logger logger.Debug("Starting Try reuse", - zap.String("organization_ref", in.OrganizationID.Hex()), + mzap.ObjRef("organization_ref", payment.OrganizationRef), zap.Bool("has_idempotency_key", strings.TrimSpace(in.IdempotencyKey) != ""), ) defer func(start time.Time) { diff --git a/api/payments/orchestrator/internal/service/orchestrationv2/pquery/service.go b/api/payments/orchestrator/internal/service/orchestrationv2/pquery/service.go index 5f88ecee..18c4bd4a 100644 --- a/api/payments/orchestrator/internal/service/orchestrationv2/pquery/service.go +++ b/api/payments/orchestrator/internal/service/orchestrationv2/pquery/service.go @@ -12,6 +12,7 @@ import ( "github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/prepo" "github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/mlogger" + "github.com/tech/sendico/pkg/mutil/mzap" "go.mongodb.org/mongo-driver/v2/bson" "go.uber.org/zap" ) @@ -61,7 +62,7 @@ func newService(deps Dependencies) (Service, error) { func (s *svc) GetPayment(ctx context.Context, in GetPaymentInput) (payment *agg.Payment, err error) { logger := s.logger logger.Debug("Starting Get payment", - zap.String("organization_ref", in.OrganizationRef.Hex()), + mzap.ObjRef("organization_ref", in.OrganizationRef), zap.String("payment_ref", strings.TrimSpace(in.PaymentRef)), ) defer func(start time.Time) { @@ -93,7 +94,7 @@ func (s *svc) GetPayment(ctx context.Context, in GetPaymentInput) (payment *agg. func (s *svc) ListPayments(ctx context.Context, in ListPaymentsInput) (out *ListPaymentsOutput, err error) { logger := s.logger logger.Debug("Starting List payments", - zap.String("organization_ref", in.OrganizationRef.Hex()), + mzap.ObjRef("organization_ref", in.OrganizationRef), zap.String("quotation_ref", strings.TrimSpace(in.QuotationRef)), zap.Int("states_count", len(in.States)), zap.Int32("limit", in.Limit), diff --git a/api/payments/orchestrator/internal/service/orchestrationv2/prepo/service.go b/api/payments/orchestrator/internal/service/orchestrationv2/prepo/service.go index 3d6f1501..9191d418 100644 --- a/api/payments/orchestrator/internal/service/orchestrationv2/prepo/service.go +++ b/api/payments/orchestrator/internal/service/orchestrationv2/prepo/service.go @@ -175,13 +175,13 @@ func (s *svc) GetByPaymentRef(ctx context.Context, orgRef bson.ObjectID, payment logger := s.logger requestPaymentRef := strings.TrimSpace(paymentRef) logger.Debug("Starting Get by payment ref", - zap.String("organization_ref", orgRef.Hex()), + mzap.ObjRef("organization_ref", orgRef), zap.String("payment_ref", requestPaymentRef), ) defer func(start time.Time) { fields := []zap.Field{ zap.Int64("duration_ms", time.Since(start).Milliseconds()), - zap.String("organization_ref", orgRef.Hex()), + mzap.ObjRef("organization_ref", orgRef), zap.String("payment_ref", requestPaymentRef), } if payment != nil { @@ -225,7 +225,7 @@ func (s *svc) GetByPaymentRefGlobal(ctx context.Context, paymentRef string) (pay } if payment != nil { fields = append(fields, - zap.String("organization_ref", payment.OrganizationRef.Hex()), + mzap.ObjRef("organization_ref", payment.OrganizationRef), zap.String("state", string(payment.State)), zap.Uint64("version", payment.Version), ) @@ -253,7 +253,7 @@ func (s *svc) GetByIdempotencyKey(ctx context.Context, orgRef bson.ObjectID, ide logger := s.logger hasKey := strings.TrimSpace(idempotencyKey) != "" logger.Debug("Starting Get by idempotency key", - zap.String("organization_ref", orgRef.Hex()), + mzap.ObjRef("organization_ref", orgRef), zap.Bool("has_idempotency_key", hasKey), ) defer func(start time.Time) { @@ -298,7 +298,7 @@ func (s *svc) GetByIdempotencyKey(ctx context.Context, orgRef bson.ObjectID, ide func (s *svc) ListByQuotationRef(ctx context.Context, in ListByQuotationRefInput) (out *ListOutput, err error) { logger := s.logger logger.Debug("Starting List by quotation ref", - zap.String("organization_ref", in.OrganizationRef.Hex()), + mzap.ObjRef("organization_ref", in.OrganizationRef), zap.String("quotation_ref", strings.TrimSpace(in.QuotationRef)), zap.Int32("limit", in.Limit), ) @@ -337,7 +337,7 @@ func (s *svc) ListByQuotationRef(ctx context.Context, in ListByQuotationRefInput func (s *svc) ListByState(ctx context.Context, in ListByStateInput) (out *ListOutput, err error) { logger := s.logger logger.Debug("Starting List by state", - zap.String("organization_ref", in.OrganizationRef.Hex()), + mzap.ObjRef("organization_ref", in.OrganizationRef), zap.String("state", string(in.State)), zap.Int32("limit", in.Limit), ) diff --git a/api/payments/orchestrator/internal/service/orchestrationv2/psvc/execute_batch.go b/api/payments/orchestrator/internal/service/orchestrationv2/psvc/execute_batch.go index 3beed487..faaee287 100644 --- a/api/payments/orchestrator/internal/service/orchestrationv2/psvc/execute_batch.go +++ b/api/payments/orchestrator/internal/service/orchestrationv2/psvc/execute_batch.go @@ -5,12 +5,13 @@ import ( "crypto/sha256" "encoding/hex" "errors" - "github.com/tech/sendico/pkg/discovery" "sort" "strconv" "strings" "time" + "github.com/tech/sendico/pkg/discovery" + "github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/agg" "github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/batchmeta" "github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/idem" @@ -60,8 +61,8 @@ func (s *svc) ExecuteBatchPayment(ctx context.Context, req *orchestrationv2.Exec } resolved, err := s.quote.ResolveAll(ctx, s.quoteStore, qsnap.ResolveAllInput{ - OrganizationID: requestCtx.OrganizationID, - QuotationRef: requestCtx.QuotationRef, + OrganizationRef: requestCtx.OrganizationID, + QuotationRef: requestCtx.QuotationRef, }) if err != nil { return nil, remapResolveError(err) 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 e1eb3542..5c020209 100644 --- a/api/payments/orchestrator/internal/service/orchestrationv2/psvc/status_publish.go +++ b/api/payments/orchestrator/internal/service/orchestrationv2/psvc/status_publish.go @@ -11,6 +11,7 @@ import ( pon "github.com/tech/sendico/pkg/messaging/notifications/paymentorchestrator" "github.com/tech/sendico/pkg/mlogger" "github.com/tech/sendico/pkg/model" + "github.com/tech/sendico/pkg/mutil/mzap" "go.uber.org/zap" ) @@ -64,7 +65,7 @@ func (p *brokerPaymentStatusPublisher) Publish(_ context.Context, in paymentStat if paymentRef == "" || payment.OrganizationRef.IsZero() { p.logger.Warn("Skipping payment status publish due to missing identifiers", zap.String("payment_ref", paymentRef), - zap.String("organization_ref", payment.OrganizationRef.Hex()), + mzap.ObjRef("organization_ref", payment.OrganizationRef), ) return nil } diff --git a/api/payments/orchestrator/internal/service/orchestrationv2/qsnap/module.go b/api/payments/orchestrator/internal/service/orchestrationv2/qsnap/module.go index 4ed3886a..c7500df2 100644 --- a/api/payments/orchestrator/internal/service/orchestrationv2/qsnap/module.go +++ b/api/payments/orchestrator/internal/service/orchestrationv2/qsnap/module.go @@ -36,8 +36,8 @@ type Output struct { // ResolveAllInput defines lookup scope for resolving all items in a batch quotation. type ResolveAllInput struct { - OrganizationID bson.ObjectID - QuotationRef string + OrganizationRef bson.ObjectID + QuotationRef string } // ResolveAllOutput contains all resolved items from a batch quotation. diff --git a/api/payments/orchestrator/internal/service/orchestrationv2/qsnap/resolve_all_test.go b/api/payments/orchestrator/internal/service/orchestrationv2/qsnap/resolve_all_test.go index 4341a793..b33d6e57 100644 --- a/api/payments/orchestrator/internal/service/orchestrationv2/qsnap/resolve_all_test.go +++ b/api/payments/orchestrator/internal/service/orchestrationv2/qsnap/resolve_all_test.go @@ -41,8 +41,8 @@ func TestResolveAll_BatchReturnsAllItems(t *testing.T) { return record, nil }, }, ResolveAllInput{ - OrganizationID: orgID, - QuotationRef: "batch-quote-ref", + OrganizationRef: orgID, + QuotationRef: "batch-quote-ref", }) if err != nil { t.Fatalf("ResolveAll returned error: %v", err) @@ -94,8 +94,8 @@ func TestResolveAll_SingleShapeReturnsOneItem(t *testing.T) { return record, nil }, }, ResolveAllInput{ - OrganizationID: orgID, - QuotationRef: "single-quote-ref", + OrganizationRef: orgID, + QuotationRef: "single-quote-ref", }) if err != nil { t.Fatalf("ResolveAll returned error: %v", err) @@ -137,8 +137,8 @@ func TestResolveAll_NonExecutableItemFails(t *testing.T) { return record, nil }, }, ResolveAllInput{ - OrganizationID: orgID, - QuotationRef: "batch-mixed", + OrganizationRef: orgID, + QuotationRef: "batch-mixed", }) if err == nil { t.Fatal("expected error for non-executable item") @@ -172,8 +172,8 @@ func TestResolveAll_ExpiredQuoteFails(t *testing.T) { return record, nil }, }, ResolveAllInput{ - OrganizationID: orgID, - QuotationRef: "expired-quote", + OrganizationRef: orgID, + QuotationRef: "expired-quote", }) if err == nil { t.Fatal("expected error for expired quote") @@ -187,8 +187,8 @@ func TestResolveAll_EmptyQuotationRefFails(t *testing.T) { resolver := New(Dependencies{Logger: zap.NewNop()}) _, err := resolver.ResolveAll(context.Background(), &fakeStore{}, ResolveAllInput{ - OrganizationID: bson.NewObjectID(), - QuotationRef: "", + OrganizationRef: bson.NewObjectID(), + QuotationRef: "", }) if err == nil { t.Fatal("expected error for empty quotation_ref") @@ -199,8 +199,8 @@ func TestResolveAll_QuoteNotFoundFails(t *testing.T) { resolver := New(Dependencies{Logger: zap.NewNop()}) _, err := resolver.ResolveAll(context.Background(), &fakeStore{}, ResolveAllInput{ - OrganizationID: bson.NewObjectID(), - QuotationRef: "nonexistent", + OrganizationRef: bson.NewObjectID(), + QuotationRef: "nonexistent", }) if err == nil { t.Fatal("expected error for not-found quote") @@ -234,8 +234,8 @@ func TestResolveAll_SetsQuoteRefWhenEmpty(t *testing.T) { return record, nil }, }, ResolveAllInput{ - OrganizationID: orgID, - QuotationRef: "batch-ref", + OrganizationRef: orgID, + QuotationRef: "batch-ref", }) if err != nil { t.Fatalf("ResolveAll returned error: %v", err) diff --git a/api/payments/orchestrator/internal/service/orchestrationv2/qsnap/service.go b/api/payments/orchestrator/internal/service/orchestrationv2/qsnap/service.go index 6fc89177..5080bec8 100644 --- a/api/payments/orchestrator/internal/service/orchestrationv2/qsnap/service.go +++ b/api/payments/orchestrator/internal/service/orchestrationv2/qsnap/service.go @@ -11,6 +11,7 @@ import ( quotestorage "github.com/tech/sendico/payments/storage/quote" "github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/mlogger" + "github.com/tech/sendico/pkg/mutil/mzap" "go.mongodb.org/mongo-driver/v2/bson" "go.uber.org/zap" ) @@ -33,7 +34,7 @@ func (s *svc) Resolve( ) (out *Output, err error) { logger := s.logger logger.Debug("Starting Resolve", - zap.String("organization_ref", in.OrganizationID.Hex()), + mzap.ObjRef("organization_ref", in.OrganizationID), zap.String("quotation_ref", strings.TrimSpace(in.QuotationRef)), ) defer func(start time.Time) { @@ -105,7 +106,7 @@ func (s *svc) ResolveAll( ) (out *ResolveAllOutput, err error) { logger := s.logger logger.Debug("Starting ResolveAll", - zap.String("organization_ref", in.OrganizationID.Hex()), + mzap.ObjRef("organization_ref", in.OrganizationRef), zap.String("quotation_ref", strings.TrimSpace(in.QuotationRef)), ) defer func(start time.Time) { @@ -126,7 +127,7 @@ func (s *svc) ResolveAll( if store == nil { return nil, merrors.InvalidArgument("quotes store is required") } - if in.OrganizationID.IsZero() { + if in.OrganizationRef.IsZero() { return nil, merrors.InvalidArgument("organization_id is required") } quoteRef := strings.TrimSpace(in.QuotationRef) @@ -134,7 +135,7 @@ func (s *svc) ResolveAll( return nil, merrors.InvalidArgument("quotation_ref is required") } - record, err := store.GetByRef(ctx, in.OrganizationID, quoteRef) + record, err := store.GetByRef(ctx, in.OrganizationRef, quoteRef) if err != nil { if errors.Is(err, quotestorage.ErrQuoteNotFound) || errors.Is(err, merrors.ErrNoData) { return nil, ErrQuoteNotFound diff --git a/api/payments/orchestrator/internal/service/orchestrator/external_runtime.go b/api/payments/orchestrator/internal/service/orchestrator/external_runtime.go index 11516612..6e1da294 100644 --- a/api/payments/orchestrator/internal/service/orchestrator/external_runtime.go +++ b/api/payments/orchestrator/internal/service/orchestrator/external_runtime.go @@ -3,10 +3,12 @@ package orchestrator import ( "context" "errors" - "github.com/tech/sendico/pkg/discovery" "strings" "time" + "github.com/tech/sendico/pkg/discovery" + "github.com/tech/sendico/pkg/mutil/mzap" + "github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/agg" "github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/erecon" "github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/prepo" @@ -111,7 +113,7 @@ func (s *Service) onPaymentGatewayExecution(ctx context.Context, msg *pmodel.Pay s.logger.Debug("Reconciling payment from gateway execution event", zap.String("payment_ref", strings.TrimSpace(payment.PaymentRef)), - zap.String("organization_ref", payment.OrganizationRef.Hex()), + mzap.ObjRef("organization_ref", payment.OrganizationRef), zap.String("step_ref", strings.TrimSpace(event.StepRef)), zap.String("status", strings.TrimSpace(string(event.Status))), zap.String("transfer_ref", strings.TrimSpace(event.TransferRef)), @@ -457,7 +459,7 @@ func (s *Service) pollObserveCandidate(ctx context.Context, payment *agg.Payment s.logger.Debug("Reconciling payment from observe polling result", zap.String("payment_ref", strings.TrimSpace(payment.PaymentRef)), - zap.String("organization_ref", payment.OrganizationRef.Hex()), + mzap.ObjRef("organization_ref", payment.OrganizationRef), zap.String("step_ref", candidate.stepRef), zap.String("status", strings.TrimSpace(string(event.Status))), zap.String("transfer_ref", candidate.transferRef), diff --git a/api/payments/quotation/internal/service/quotation/quotation_service_v2/logging.go b/api/payments/quotation/internal/service/quotation/quotation_service_v2/logging.go index df90ceda..a74b68ea 100644 --- a/api/payments/quotation/internal/service/quotation/quotation_service_v2/logging.go +++ b/api/payments/quotation/internal/service/quotation/quotation_service_v2/logging.go @@ -5,6 +5,7 @@ import ( "strings" "github.com/tech/sendico/pkg/mlogger" + "github.com/tech/sendico/pkg/mutil/mzap" moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1" endpointv1 "github.com/tech/sendico/pkg/proto/payments/endpoint/v1" quotationv2 "github.com/tech/sendico/pkg/proto/payments/quotation/v2" @@ -33,7 +34,7 @@ type quoteIntentLogSummary struct { func (s *QuotationServiceV2) quotePaymentLogger(req *quotationv2.QuotePaymentRequest) mlogger.Logger { return s.logger.With( - zap.String("flow_ref", bson.NewObjectID().Hex()), + mzap.ObjRef("flow_ref", bson.NewObjectID()), zap.String("rpc_method", "QuotePayment"), zap.String("organization_ref", strings.TrimSpace(req.GetMeta().GetOrganizationRef())), zap.String("idempotency_key", strings.TrimSpace(req.GetIdempotencyKey())), @@ -44,7 +45,7 @@ func (s *QuotationServiceV2) quotePaymentLogger(req *quotationv2.QuotePaymentReq func (s *QuotationServiceV2) quotePaymentsLogger(req *quotationv2.QuotePaymentsRequest) mlogger.Logger { return s.logger.With( - zap.String("flow_ref", bson.NewObjectID().Hex()), + mzap.ObjRef("flow_ref", bson.NewObjectID()), zap.String("rpc_method", "QuotePayments"), zap.String("organization_ref", strings.TrimSpace(req.GetMeta().GetOrganizationRef())), zap.String("idempotency_key", strings.TrimSpace(req.GetIdempotencyKey())), diff --git a/api/pkg/db/internal/mongo/indexable/indexable.go b/api/pkg/db/internal/mongo/indexable/indexable.go index 4cbdbcea..03241524 100644 --- a/api/pkg/db/internal/mongo/indexable/indexable.go +++ b/api/pkg/db/internal/mongo/indexable/indexable.go @@ -8,6 +8,7 @@ import ( "github.com/tech/sendico/pkg/db/storable" "github.com/tech/sendico/pkg/mlogger" "github.com/tech/sendico/pkg/model" + "github.com/tech/sendico/pkg/mutil/mzap" "go.mongodb.org/mongo-driver/v2/bson" "go.uber.org/zap" ) @@ -43,7 +44,7 @@ func (db *IndexableDB[T]) Reorder(ctx context.Context, objectRef bson.ObjectID, if err != nil { db.logger.Error("Failed to get object for reordering", zap.Error(err), - zap.String("object_ref", objectRef.Hex()), + mzap.ObjRef("object_ref", objectRef), zap.Int("new_index", newIndex)) return err } @@ -53,7 +54,7 @@ func (db *IndexableDB[T]) Reorder(ctx context.Context, objectRef bson.ObjectID, currentIndex := indexable.Index if currentIndex == newIndex { db.logger.Debug("No reordering needed - same index", - zap.String("object_ref", objectRef.Hex()), + mzap.ObjRef("object_ref", objectRef), zap.Int("current_index", currentIndex), zap.Int("new_index", newIndex)) return nil // No change needed @@ -71,14 +72,14 @@ func (db *IndexableDB[T]) Reorder(ctx context.Context, objectRef bson.ObjectID, if err != nil { db.logger.Error("Failed to shift objects during reordering (moving down)", zap.Error(err), - zap.String("object_ref", objectRef.Hex()), + mzap.ObjRef("object_ref", objectRef), zap.Int("current_index", currentIndex), zap.Int("new_index", newIndex), zap.Int("updated_count", updatedCount)) return err } db.logger.Debug("Successfully shifted objects (moving down)", - zap.String("object_ref", objectRef.Hex()), + mzap.ObjRef("object_ref", objectRef), zap.Int("updated_count", updatedCount)) } else { // Moving up: shift items between newIndex and currentIndex-1 down by +1 @@ -91,14 +92,14 @@ func (db *IndexableDB[T]) Reorder(ctx context.Context, objectRef bson.ObjectID, if err != nil { db.logger.Error("Failed to shift objects during reordering (moving up)", zap.Error(err), - zap.String("object_ref", objectRef.Hex()), + mzap.ObjRef("object_ref", objectRef), zap.Int("current_index", currentIndex), zap.Int("new_index", newIndex), zap.Int("updated_count", updatedCount)) return err } db.logger.Debug("Successfully shifted objects (moving up)", - zap.String("object_ref", objectRef.Hex()), + mzap.ObjRef("object_ref", objectRef), zap.Int("updated_count", updatedCount)) } @@ -108,14 +109,14 @@ func (db *IndexableDB[T]) Reorder(ctx context.Context, objectRef bson.ObjectID, if err != nil { db.logger.Error("Failed to update target object index", zap.Error(err), - zap.String("object_ref", objectRef.Hex()), + mzap.ObjRef("object_ref", objectRef), zap.Int("current_index", currentIndex), zap.Int("new_index", newIndex)) return err } db.logger.Info("Successfully reordered object", - zap.String("object_ref", objectRef.Hex()), + mzap.ObjRef("object_ref", objectRef), zap.Int("old_index", currentIndex), zap.Int("new_index", newIndex)) return nil diff --git a/frontend/pshared/lib/api/requests/payment/initiate.dart b/frontend/pshared/lib/api/requests/payment/initiate.dart index 10a1c7d3..d7cd9ef5 100644 --- a/frontend/pshared/lib/api/requests/payment/initiate.dart +++ b/frontend/pshared/lib/api/requests/payment/initiate.dart @@ -5,20 +5,22 @@ import 'package:pshared/data/dto/payment/intent/payment.dart'; part 'initiate.g.dart'; - @JsonSerializable() class InitiatePaymentRequest extends PaymentBaseRequest { final PaymentIntentDTO? intent; final String? quoteRef; + final String? clientPaymentRef; const InitiatePaymentRequest({ required super.idempotencyKey, super.metadata, this.intent, this.quoteRef, + this.clientPaymentRef, }); - factory InitiatePaymentRequest.fromJson(Map json) => _$InitiatePaymentRequestFromJson(json); + factory InitiatePaymentRequest.fromJson(Map json) => + _$InitiatePaymentRequestFromJson(json); @override Map toJson() => _$InitiatePaymentRequestToJson(this); } diff --git a/frontend/pshared/lib/api/requests/payment/initiate_payments.dart b/frontend/pshared/lib/api/requests/payment/initiate_payments.dart index 9e4b08a0..fbc6ee8c 100644 --- a/frontend/pshared/lib/api/requests/payment/initiate_payments.dart +++ b/frontend/pshared/lib/api/requests/payment/initiate_payments.dart @@ -3,18 +3,20 @@ import 'package:pshared/api/requests/payment/base.dart'; part 'initiate_payments.g.dart'; - @JsonSerializable() class InitiatePaymentsRequest extends PaymentBaseRequest { final String quoteRef; + final String? clientPaymentRef; const InitiatePaymentsRequest({ required super.idempotencyKey, super.metadata, required this.quoteRef, + this.clientPaymentRef, }); - factory InitiatePaymentsRequest.fromJson(Map json) => _$InitiatePaymentsRequestFromJson(json); + factory InitiatePaymentsRequest.fromJson(Map json) => + _$InitiatePaymentsRequestFromJson(json); @override Map toJson() => _$InitiatePaymentsRequestToJson(this); } diff --git a/frontend/pshared/lib/provider/payment/multiple/provider.dart b/frontend/pshared/lib/provider/payment/multiple/provider.dart index 9c0cd15c..6f4e8c45 100644 --- a/frontend/pshared/lib/provider/payment/multiple/provider.dart +++ b/frontend/pshared/lib/provider/payment/multiple/provider.dart @@ -7,7 +7,6 @@ import 'package:pshared/provider/resource.dart'; import 'package:pshared/service/payment/multiple.dart'; import 'package:pshared/utils/exception.dart'; - class MultiPaymentProvider extends ChangeNotifier { late OrganizationsProvider _organization; late MultiQuotationProvider _quotation; @@ -30,6 +29,7 @@ class MultiPaymentProvider extends ChangeNotifier { Future> pay({ String? idempotencyKey, + String? clientPaymentRef, Map? metadata, }) async { if (!_organization.isOrganizationSet) { @@ -52,6 +52,7 @@ class MultiPaymentProvider extends ChangeNotifier { _organization.current.id, quoteRef, idempotencyKey: idempotencyKey, + clientPaymentRef: clientPaymentRef, metadata: metadata, ); diff --git a/frontend/pshared/lib/provider/payment/provider.dart b/frontend/pshared/lib/provider/payment/provider.dart index 8b29e903..995208dc 100644 --- a/frontend/pshared/lib/provider/payment/provider.dart +++ b/frontend/pshared/lib/provider/payment/provider.dart @@ -7,12 +7,15 @@ import 'package:pshared/provider/resource.dart'; import 'package:pshared/service/payment/service.dart'; import 'package:pshared/utils/exception.dart'; - class PaymentProvider extends ChangeNotifier { late OrganizationsProvider _organization; late QuotationProvider _quotation; - Resource _payment = Resource(data: null, isLoading: false, error: null); + Resource _payment = Resource( + data: null, + isLoading: false, + error: null, + ); bool _isLoaded = false; void update(OrganizationsProvider organization, QuotationProvider quotation) { @@ -23,15 +26,21 @@ class PaymentProvider extends ChangeNotifier { Payment? get payment => _payment.data; bool get isLoading => _payment.isLoading; Exception? get error => _payment.error; - bool get isReady => _isLoaded && !_payment.isLoading && _payment.error == null; + bool get isReady => + _isLoaded && !_payment.isLoading && _payment.error == null; void _setResource(Resource payment) { _payment = payment; notifyListeners(); } - Future pay({String? idempotencyKey, Map? metadata}) async { - if (!_organization.isOrganizationSet) throw StateError('Organization is not set'); + Future pay({ + String? idempotencyKey, + String? clientPaymentRef, + Map? metadata, + }) async { + if (!_organization.isOrganizationSet) + throw StateError('Organization is not set'); final quoteRef = _quotation.quotation?.quoteRef; if (quoteRef == null || quoteRef.isEmpty) { throw StateError('Quotation reference is not set'); @@ -49,12 +58,17 @@ class PaymentProvider extends ChangeNotifier { _organization.current.id, quoteRef, idempotencyKey: resolvedIdempotencyKey, + clientPaymentRef: clientPaymentRef, metadata: metadata, ); _isLoaded = true; - _setResource(_payment.copyWith(data: response, isLoading: false, error: null)); + _setResource( + _payment.copyWith(data: response, isLoading: false, error: null), + ); } catch (e) { - _setResource(_payment.copyWith(data: null, error: toException(e), isLoading: false)); + _setResource( + _payment.copyWith(data: null, error: toException(e), isLoading: false), + ); } return _payment.data; } diff --git a/frontend/pshared/lib/service/payment/multiple.dart b/frontend/pshared/lib/service/payment/multiple.dart index a181e68d..525ece3d 100644 --- a/frontend/pshared/lib/service/payment/multiple.dart +++ b/frontend/pshared/lib/service/payment/multiple.dart @@ -13,7 +13,6 @@ import 'package:pshared/models/payment/quote/quotes.dart'; import 'package:pshared/service/authorization/service.dart'; import 'package:pshared/service/services.dart'; - class MultiplePaymentsService { static final _logger = Logger('service.payment.multiple'); static const String _objectType = Services.payments; @@ -37,6 +36,7 @@ class MultiplePaymentsService { String organizationRef, String quoteRef, { String? idempotencyKey, + String? clientPaymentRef, Map? metadata, }) async { _logger.fine( @@ -45,6 +45,7 @@ class MultiplePaymentsService { final request = InitiatePaymentsRequest( idempotencyKey: idempotencyKey ?? const Uuid().v4(), quoteRef: quoteRef, + clientPaymentRef: clientPaymentRef, metadata: metadata, ); diff --git a/frontend/pshared/lib/service/payment/service.dart b/frontend/pshared/lib/service/payment/service.dart index 844fc95d..febd82b4 100644 --- a/frontend/pshared/lib/service/payment/service.dart +++ b/frontend/pshared/lib/service/payment/service.dart @@ -82,6 +82,7 @@ class PaymentService { String organizationRef, String quotationRef, { String? idempotencyKey, + String? clientPaymentRef, Map? metadata, }) async { _logger.fine( @@ -90,6 +91,7 @@ class PaymentService { final request = InitiatePaymentRequest( idempotencyKey: idempotencyKey ?? Uuid().v4(), quoteRef: quotationRef, + clientPaymentRef: clientPaymentRef, metadata: metadata, ); final response = await AuthorizationService.getPOSTResponse( diff --git a/frontend/pshared/test/payment/request_dto_format_test.dart b/frontend/pshared/test/payment/request_dto_format_test.dart index e0ea1c5f..9cff7713 100644 --- a/frontend/pshared/test/payment/request_dto_format_test.dart +++ b/frontend/pshared/test/payment/request_dto_format_test.dart @@ -158,16 +158,13 @@ void main() { final request = InitiatePaymentRequest( idempotencyKey: 'idem-2', quoteRef: 'q-1', - metadata: const {'client_payment_ref': 'cp-1'}, + clientPaymentRef: 'cp-1', ); final json = request.toJson(); expect(json['idempotencyKey'], equals('idem-2')); expect(json['quoteRef'], equals('q-1')); - expect( - (json['metadata'] as Map)['client_payment_ref'], - equals('cp-1'), - ); + expect(json['clientPaymentRef'], equals('cp-1')); expect(json.containsKey('intent'), isTrue); expect(json['intent'], isNull); }); @@ -176,16 +173,13 @@ void main() { final request = InitiatePaymentsRequest( idempotencyKey: 'idem-3', quoteRef: 'q-2', - metadata: const {'client_payment_ref': 'cp-1'}, + clientPaymentRef: 'cp-1', ); final json = request.toJson(); expect(json['idempotencyKey'], equals('idem-3')); expect(json['quoteRef'], equals('q-2')); - expect( - (json['metadata'] as Map)['client_payment_ref'], - equals('cp-1'), - ); + expect(json['clientPaymentRef'], equals('cp-1')); expect(json.containsKey('intentRef'), isFalse); expect(json.containsKey('intentRefs'), isFalse); }); diff --git a/interface/api/payments/request/payment.yaml b/interface/api/payments/request/payment.yaml index ac5df516..d0002d35 100644 --- a/interface/api/payments/request/payment.yaml +++ b/interface/api/payments/request/payment.yaml @@ -107,6 +107,9 @@ components: quoteRef: description: Reference to a previously generated quote to execute. type: string + clientPaymentRef: + description: Optional caller-side payment correlation reference. + type: string InitiatePaymentsRequest: description: Request payload to initiate multiple payments from a multi-quote reference. @@ -120,6 +123,9 @@ components: quoteRef: description: Reference to a previously generated multi-quote. type: string + clientPaymentRef: + description: Optional caller-side payment correlation reference. + type: string InitiatePaymentByQuoteRequest: description: Request payload to initiate one payment from an existing quote reference. @@ -133,3 +139,6 @@ components: quoteRef: description: Reference to a previously generated quote to execute. type: string + clientPaymentRef: + description: Optional caller-side payment correlation reference. + type: string -- 2.49.1