improved logging in callbacks #606
@@ -21,6 +21,7 @@ import (
|
|||||||
msg "github.com/tech/sendico/pkg/messaging"
|
msg "github.com/tech/sendico/pkg/messaging"
|
||||||
"github.com/tech/sendico/pkg/mlogger"
|
"github.com/tech/sendico/pkg/mlogger"
|
||||||
"github.com/tech/sendico/pkg/mservice"
|
"github.com/tech/sendico/pkg/mservice"
|
||||||
|
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||||
feesv1 "github.com/tech/sendico/pkg/proto/billing/fees/v1"
|
feesv1 "github.com/tech/sendico/pkg/proto/billing/fees/v1"
|
||||||
tracev1 "github.com/tech/sendico/pkg/proto/common/trace/v1"
|
tracev1 "github.com/tech/sendico/pkg/proto/common/trace/v1"
|
||||||
"go.mongodb.org/mongo-driver/v2/bson"
|
"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)}
|
logFields := []zap.Field{zap.Time("booked_at_used", bookedAt)}
|
||||||
if !orgRef.IsZero() {
|
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)...)
|
logFields = append(logFields, logFieldsFromIntent(intent)...)
|
||||||
|
|||||||
@@ -73,9 +73,10 @@ func validateQuoteIdempotency(previewOnly bool, idempotencyKey string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type InitiatePayment struct {
|
type InitiatePayment struct {
|
||||||
PaymentBase `json:",inline"`
|
PaymentBase `json:",inline"`
|
||||||
Intent *PaymentIntent `json:"intent,omitempty"`
|
Intent *PaymentIntent `json:"intent,omitempty"`
|
||||||
QuoteRef string `json:"quoteRef,omitempty"`
|
QuoteRef string `json:"quoteRef,omitempty"`
|
||||||
|
ClientPaymentRef string `json:"clientPaymentRef,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r InitiatePayment) Validate() error {
|
func (r InitiatePayment) Validate() error {
|
||||||
@@ -106,8 +107,9 @@ func (r InitiatePayment) Validate() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type InitiatePayments struct {
|
type InitiatePayments struct {
|
||||||
PaymentBase `json:",inline"`
|
PaymentBase `json:",inline"`
|
||||||
QuoteRef string `json:"quoteRef,omitempty"`
|
QuoteRef string `json:"quoteRef,omitempty"`
|
||||||
|
ClientPaymentRef string `json:"clientPaymentRef,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *InitiatePayments) Validate() error {
|
func (r *InitiatePayments) Validate() error {
|
||||||
|
|||||||
@@ -120,11 +120,11 @@ func (a *AccountAPI) resetPassword(r *http.Request) http.HandlerFunc {
|
|||||||
var user model.Account
|
var user model.Account
|
||||||
err = a.db.Get(ctx, accountRef, &user)
|
err = a.db.Get(ctx, accountRef, &user)
|
||||||
if errors.Is(err, merrors.ErrNoData) {
|
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")
|
return response.NotFound(a.logger, a.Name(), "User not found")
|
||||||
}
|
}
|
||||||
if err != nil {
|
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)
|
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 {
|
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")
|
return response.DataConflict(a.logger, a.Name(), "Token does not match account")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
"github.com/tech/sendico/pkg/merrors"
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
"github.com/tech/sendico/pkg/mlogger"
|
"github.com/tech/sendico/pkg/mlogger"
|
||||||
|
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||||
"github.com/tech/sendico/pkg/vault/kv"
|
"github.com/tech/sendico/pkg/vault/kv"
|
||||||
"go.mongodb.org/mongo-driver/v2/bson"
|
"go.mongodb.org/mongo-driver/v2/bson"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
@@ -152,7 +153,7 @@ func (m *vaultSigningSecretManager) Provision(
|
|||||||
}
|
}
|
||||||
|
|
||||||
secretRef := "vault:" + secretPath + "#" + m.field
|
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
|
return secretRef, secret, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/tech/sendico/pkg/model"
|
"github.com/tech/sendico/pkg/model"
|
||||||
"github.com/tech/sendico/pkg/model/account_role"
|
"github.com/tech/sendico/pkg/model/account_role"
|
||||||
"github.com/tech/sendico/pkg/mservice"
|
"github.com/tech/sendico/pkg/mservice"
|
||||||
|
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||||
describablev1 "github.com/tech/sendico/pkg/proto/common/describable/v1"
|
describablev1 "github.com/tech/sendico/pkg/proto/common/describable/v1"
|
||||||
ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
|
ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
|
||||||
"github.com/tech/sendico/server/interface/api/srequest"
|
"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,
|
Describable: describable,
|
||||||
})
|
})
|
||||||
if err != nil {
|
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)
|
return response.Auto(a.logger, mservice.Ledger, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ func (a *LedgerAPI) listAccounts(r *http.Request, account *model.Account, token
|
|||||||
|
|
||||||
resp, err := a.client.ListAccounts(ctx, req)
|
resp, err := a.client.ListAccounts(ctx, req)
|
||||||
if err != nil {
|
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)
|
return response.Auto(a.logger, mservice.Ledger, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ func (a *PaymentAPI) initiatePayment(r *http.Request, account *model.Account, to
|
|||||||
req := &orchestrationv2.ExecutePaymentRequest{
|
req := &orchestrationv2.ExecutePaymentRequest{
|
||||||
Meta: requestMeta(orgRef.Hex(), payload.IdempotencyKey),
|
Meta: requestMeta(orgRef.Hex(), payload.IdempotencyKey),
|
||||||
QuotationRef: quotationRef,
|
QuotationRef: quotationRef,
|
||||||
ClientPaymentRef: metadataValue(payload.Metadata, "client_payment_ref"),
|
ClientPaymentRef: strings.TrimSpace(payload.ClientPaymentRef),
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := a.execution.ExecutePayment(ctx, req)
|
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.IdempotencyKey = strings.TrimSpace(payload.IdempotencyKey)
|
||||||
payload.QuoteRef = strings.TrimSpace(payload.QuoteRef)
|
payload.QuoteRef = strings.TrimSpace(payload.QuoteRef)
|
||||||
|
payload.ClientPaymentRef = strings.TrimSpace(payload.ClientPaymentRef)
|
||||||
|
|
||||||
if err := payload.Validate(); err != nil {
|
if err := payload.Validate(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -14,12 +14,12 @@ import (
|
|||||||
"go.mongodb.org/mongo-driver/v2/bson"
|
"go.mongodb.org/mongo-driver/v2/bson"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestInitiateByQuote_DoesNotUseIntentRef(t *testing.T) {
|
func TestInitiateByQuote_ForwardsClientPaymentRef(t *testing.T) {
|
||||||
orgRef := bson.NewObjectID()
|
orgRef := bson.NewObjectID()
|
||||||
exec := &fakeExecutionClientForBatch{}
|
exec := &fakeExecutionClientForBatch{}
|
||||||
api := newBatchAPI(exec)
|
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)
|
rr := invokeInitiateByQuote(t, api, orgRef, body)
|
||||||
if got, want := rr.Code, http.StatusOK; got != want {
|
if got, want := rr.Code, http.StatusOK; got != want {
|
||||||
t.Fatalf("status mismatch: got=%d want=%d body=%s", got, want, rr.Body.String())
|
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) {
|
func TestInitiateByQuote_RejectsMetadataIntentRef(t *testing.T) {
|
||||||
orgRef := bson.NewObjectID()
|
orgRef := bson.NewObjectID()
|
||||||
exec := &fakeExecutionClientForBatch{}
|
exec := &fakeExecutionClientForBatch{}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/tech/sendico/pkg/api/http/response"
|
"github.com/tech/sendico/pkg/api/http/response"
|
||||||
"github.com/tech/sendico/pkg/merrors"
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
"github.com/tech/sendico/pkg/model"
|
"github.com/tech/sendico/pkg/model"
|
||||||
|
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||||
orchestrationv2 "github.com/tech/sendico/pkg/proto/payments/orchestration/v2"
|
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/srequest"
|
||||||
"github.com/tech/sendico/server/interface/api/sresponse"
|
"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)
|
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)
|
idempotencyKey := strings.TrimSpace(payload.IdempotencyKey)
|
||||||
quotationRef := strings.TrimSpace(payload.QuoteRef)
|
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)
|
resp, err := a.execution.ExecuteBatchPayment(ctx, req)
|
||||||
if err != nil {
|
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)
|
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.IdempotencyKey = strings.TrimSpace(payload.IdempotencyKey)
|
||||||
payload.QuoteRef = strings.TrimSpace(payload.QuoteRef)
|
payload.QuoteRef = strings.TrimSpace(payload.QuoteRef)
|
||||||
|
payload.ClientPaymentRef = strings.TrimSpace(payload.ClientPaymentRef)
|
||||||
|
|
||||||
if err := payload.Validate(); err != nil {
|
if err := payload.Validate(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ func TestInitiatePaymentsByQuote_ForwardsClientPaymentRef(t *testing.T) {
|
|||||||
exec := &fakeExecutionClientForBatch{}
|
exec := &fakeExecutionClientForBatch{}
|
||||||
api := newBatchAPI(exec)
|
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)
|
rr := invokeInitiatePaymentsByQuote(t, api, orgRef, body)
|
||||||
if got, want := rr.Code, http.StatusOK; got != want {
|
if got, want := rr.Code, http.StatusOK; got != want {
|
||||||
t.Fatalf("status mismatch: got=%d want=%d body=%s", got, want, rr.Body.String())
|
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) {
|
func TestInitiatePaymentsByQuote_RejectsDeprecatedIntentRefField(t *testing.T) {
|
||||||
orgRef := bson.NewObjectID()
|
orgRef := bson.NewObjectID()
|
||||||
exec := &fakeExecutionClientForBatch{}
|
exec := &fakeExecutionClientForBatch{}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/tech/sendico/pkg/api/http/response"
|
"github.com/tech/sendico/pkg/api/http/response"
|
||||||
"github.com/tech/sendico/pkg/merrors"
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
"github.com/tech/sendico/pkg/model"
|
"github.com/tech/sendico/pkg/model"
|
||||||
|
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||||
quotationv2 "github.com/tech/sendico/pkg/proto/payments/quotation/v2"
|
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/srequest"
|
||||||
"github.com/tech/sendico/server/interface/api/sresponse"
|
"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)
|
resp, err := a.quotation.QuotePayment(ctx, req)
|
||||||
if err != nil {
|
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)
|
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)
|
resp, err := a.quotation.QuotePayments(ctx, req)
|
||||||
if err != nil {
|
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)
|
return grpcErrorResponse(a.logger, a.Name(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/tech/sendico/pkg/merrors"
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
"github.com/tech/sendico/pkg/model"
|
"github.com/tech/sendico/pkg/model"
|
||||||
"github.com/tech/sendico/pkg/mservice"
|
"github.com/tech/sendico/pkg/mservice"
|
||||||
|
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||||
connectorv1 "github.com/tech/sendico/pkg/proto/connector/v1"
|
connectorv1 "github.com/tech/sendico/pkg/proto/connector/v1"
|
||||||
"github.com/tech/sendico/server/interface/api/sresponse"
|
"github.com/tech/sendico/server/interface/api/sresponse"
|
||||||
mutil "github.com/tech/sendico/server/internal/mutil/param"
|
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"))
|
return response.Auto(a.logger, a.Name(), merrors.NoData("no crypto gateways available"))
|
||||||
}
|
}
|
||||||
a.logger.Debug("Resolved CRYPTO gateways for wallet balance lookup",
|
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.String("wallet_ref", walletRef),
|
||||||
zap.Int("gateway_count", len(cryptoGateways)))
|
zap.Int("gateway_count", len(cryptoGateways)))
|
||||||
|
|
||||||
route, routeErr := a.walletRoute(ctx, orgRef.Hex(), walletRef)
|
route, routeErr := a.walletRoute(ctx, orgRef.Hex(), walletRef)
|
||||||
if routeErr != nil {
|
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 {
|
if route != nil {
|
||||||
a.logger.Debug("Resolved stored wallet route",
|
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("wallet_ref", walletRef),
|
||||||
zap.String("route_network", route.Network),
|
zap.String("route_network", route.Network),
|
||||||
zap.String("route_gateway_id", route.GatewayID))
|
zap.String("route_gateway_id", route.GatewayID))
|
||||||
preferred := findGatewayForRoute(cryptoGateways, route)
|
preferred := findGatewayForRoute(cryptoGateways, route)
|
||||||
if preferred != nil {
|
if preferred != nil {
|
||||||
a.logger.Debug("Using preferred gateway from stored wallet route",
|
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("wallet_ref", walletRef),
|
||||||
zap.String("gateway_id", preferred.ID),
|
zap.String("gateway_id", preferred.ID),
|
||||||
zap.String("network", preferred.Network),
|
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 {
|
if preferredErr == nil && bal != nil {
|
||||||
a.rememberWalletRoute(ctx, orgRef.Hex(), walletRef, preferred.Network, preferred.ID)
|
a.rememberWalletRoute(ctx, orgRef.Hex(), walletRef, preferred.Network, preferred.ID)
|
||||||
a.logger.Debug("Wallet balance resolved via preferred gateway",
|
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("wallet_ref", walletRef),
|
||||||
zap.String("gateway_id", preferred.ID),
|
zap.String("gateway_id", preferred.ID),
|
||||||
zap.String("network", preferred.Network))
|
zap.String("network", preferred.Network))
|
||||||
@@ -124,20 +125,20 @@ func (a *WalletAPI) getWalletBalance(r *http.Request, account *model.Account, to
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
a.logger.Warn("Stored wallet route did not match any healthy discovery gateway",
|
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("wallet_ref", walletRef),
|
||||||
zap.String("route_network", route.Network),
|
zap.String("route_network", route.Network),
|
||||||
zap.String("route_gateway_id", route.GatewayID))
|
zap.String("route_gateway_id", route.GatewayID))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
a.logger.Debug("Stored wallet route not found; using gateway fallback",
|
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))
|
zap.String("wallet_ref", walletRef))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fall back to querying remaining gateways in parallel.
|
// Fall back to querying remaining gateways in parallel.
|
||||||
a.logger.Debug("Starting fallback wallet balance fan-out",
|
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.String("wallet_ref", walletRef),
|
||||||
zap.Int("gateway_count", len(cryptoGateways)))
|
zap.Int("gateway_count", len(cryptoGateways)))
|
||||||
bal, err := a.queryBalanceFromGateways(ctx, cryptoGateways, orgRef.Hex(), walletRef)
|
bal, err := a.queryBalanceFromGateways(ctx, cryptoGateways, orgRef.Hex(), walletRef)
|
||||||
|
|||||||
@@ -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))
|
return response.Auto(a.logger, a.Name(), merrors.InvalidArgument("no gateway available for network: "+networkName))
|
||||||
}
|
}
|
||||||
a.logger.Debug("Selected gateway for wallet creation",
|
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("network", networkName),
|
||||||
zap.String("gateway_id", gateway.ID),
|
zap.String("gateway_id", gateway.ID),
|
||||||
zap.String("gateway_network", gateway.Network),
|
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, networkName, gateway.ID)
|
||||||
a.rememberWalletRoute(ctx, orgRef.Hex(), walletRef, gateway.Network, gateway.ID)
|
a.rememberWalletRoute(ctx, orgRef.Hex(), walletRef, gateway.Network, gateway.ID)
|
||||||
a.logger.Debug("Persisted wallet route after wallet creation",
|
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("wallet_ref", walletRef),
|
||||||
zap.String("network", networkName),
|
zap.String("network", networkName),
|
||||||
zap.String("gateway_id", gateway.ID))
|
zap.String("gateway_id", gateway.ID))
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ func (a *WalletAPI) listWallets(r *http.Request, account *model.Account, token *
|
|||||||
return sresponse.Wallets(a.logger, nil, token)
|
return sresponse.Wallets(a.logger, nil, token)
|
||||||
}
|
}
|
||||||
a.logger.Debug("Resolved CRYPTO gateways for wallet list",
|
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)))
|
zap.Int("gateway_count", len(cryptoGateways)))
|
||||||
|
|
||||||
// Build request
|
// Build request
|
||||||
@@ -80,7 +80,7 @@ func (a *WalletAPI) listWallets(r *http.Request, account *model.Account, token *
|
|||||||
allAccounts := a.queryAllGateways(ctx, cryptoGateways, req)
|
allAccounts := a.queryAllGateways(ctx, cryptoGateways, req)
|
||||||
dedupedAccounts := dedupeAccountsByWalletRef(allAccounts)
|
dedupedAccounts := dedupeAccountsByWalletRef(allAccounts)
|
||||||
a.logger.Debug("Wallet list fan-out completed",
|
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_raw", len(allAccounts)),
|
||||||
zap.Int("accounts_deduped", len(dedupedAccounts)),
|
zap.Int("accounts_deduped", len(dedupedAccounts)),
|
||||||
zap.Int("gateway_count", len(cryptoGateways)))
|
zap.Int("gateway_count", len(cryptoGateways)))
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/tech/sendico/edge/callbacks/internal/storage"
|
"github.com/tech/sendico/edge/callbacks/internal/storage"
|
||||||
"github.com/tech/sendico/pkg/merrors"
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
"github.com/tech/sendico/pkg/mlogger"
|
"github.com/tech/sendico/pkg/mlogger"
|
||||||
|
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -171,14 +172,14 @@ func (s *service) handleTask(ctx context.Context, workerID string, task *model.T
|
|||||||
result = "blocked"
|
result = "blocked"
|
||||||
s.logger.Warn("Blocked task delivery due to URL validation failure",
|
s.logger.Warn("Blocked task delivery due to URL validation failure",
|
||||||
zap.String("worker_id", workerID),
|
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("event_id", task.EventID),
|
||||||
zap.Error(err),
|
zap.Error(err),
|
||||||
)
|
)
|
||||||
if markErr := s.tasks.MarkFailed(ctx, task.ID, attempt, err.Error(), statusCode, time.Now().UTC()); markErr != nil {
|
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",
|
s.logger.Warn("Failed to mark blocked task as failed",
|
||||||
zap.String("worker_id", workerID),
|
zap.String("worker_id", workerID),
|
||||||
zap.String("task_id", task.ID.Hex()),
|
mzap.ObjRef("task_ref", task.ID),
|
||||||
zap.Error(markErr),
|
zap.Error(markErr),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -195,7 +196,7 @@ func (s *service) handleTask(ctx context.Context, workerID string, task *model.T
|
|||||||
result = "sign_error"
|
result = "sign_error"
|
||||||
s.logger.Warn("Failed to sign task payload",
|
s.logger.Warn("Failed to sign task payload",
|
||||||
zap.String("worker_id", workerID),
|
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("event_id", task.EventID),
|
||||||
zap.String("signing_mode", task.SigningMode),
|
zap.String("signing_mode", task.SigningMode),
|
||||||
zap.Error(err),
|
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 {
|
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",
|
s.logger.Warn("Failed to mark signing-error task as failed",
|
||||||
zap.String("worker_id", workerID),
|
zap.String("worker_id", workerID),
|
||||||
zap.String("task_id", task.ID.Hex()),
|
mzap.ObjRef("task_ref", task.ID),
|
||||||
zap.Error(markErr),
|
zap.Error(markErr),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -218,7 +219,7 @@ func (s *service) handleTask(ctx context.Context, workerID string, task *model.T
|
|||||||
result = "request_error"
|
result = "request_error"
|
||||||
s.logger.Warn("Failed to build callback request",
|
s.logger.Warn("Failed to build callback request",
|
||||||
zap.String("worker_id", workerID),
|
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("event_id", task.EventID),
|
||||||
zap.String("endpoint_url", task.EndpointURL),
|
zap.String("endpoint_url", task.EndpointURL),
|
||||||
zap.Error(err),
|
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 {
|
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",
|
s.logger.Warn("Failed to mark request-error task as failed",
|
||||||
zap.String("worker_id", workerID),
|
zap.String("worker_id", workerID),
|
||||||
zap.String("task_id", task.ID.Hex()),
|
mzap.ObjRef("task_ref", task.ID),
|
||||||
zap.Error(markErr),
|
zap.Error(markErr),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -253,7 +254,7 @@ func (s *service) handleTask(ctx context.Context, workerID string, task *model.T
|
|||||||
case outcomeDelivered:
|
case outcomeDelivered:
|
||||||
result = string(outcomeDelivered)
|
result = string(outcomeDelivered)
|
||||||
if err := s.tasks.MarkDelivered(ctx, task.ID, statusCode, time.Since(started), now); err != nil {
|
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:
|
case outcomeRetry:
|
||||||
if attempt < task.MaxAttempts {
|
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",
|
s.logger.Warn("Task delivery retry scheduled",
|
||||||
zap.String("worker_id", workerID),
|
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("event_id", task.EventID),
|
||||||
zap.Int("attempt", attempt),
|
zap.Int("attempt", attempt),
|
||||||
zap.Int("status_code", statusCode),
|
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),
|
zap.Time("next_attempt_at", next),
|
||||||
)
|
)
|
||||||
if err := s.tasks.MarkRetry(ctx, task.ID, attempt, next, lastErr, statusCode, now); err != nil {
|
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 {
|
} else {
|
||||||
result = string(outcomeFailed)
|
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",
|
s.logger.Warn("Task delivery failed after reaching max attempts",
|
||||||
zap.String("worker_id", workerID),
|
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("event_id", task.EventID),
|
||||||
zap.Int("attempt", attempt),
|
zap.Int("attempt", attempt),
|
||||||
zap.Int("max_attempts", task.MaxAttempts),
|
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),
|
zap.String("reason", lastErr),
|
||||||
)
|
)
|
||||||
if err := s.tasks.MarkFailed(ctx, task.ID, attempt, lastErr, statusCode, now); err != nil {
|
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:
|
default:
|
||||||
@@ -302,14 +303,14 @@ func (s *service) handleTask(ctx context.Context, workerID string, task *model.T
|
|||||||
}
|
}
|
||||||
s.logger.Warn("Task delivery failed",
|
s.logger.Warn("Task delivery failed",
|
||||||
zap.String("worker_id", workerID),
|
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("event_id", task.EventID),
|
||||||
zap.Int("attempt", attempt),
|
zap.Int("attempt", attempt),
|
||||||
zap.Int("status_code", statusCode),
|
zap.Int("status_code", statusCode),
|
||||||
zap.String("reason", lastErr),
|
zap.String("reason", lastErr),
|
||||||
)
|
)
|
||||||
if err := s.tasks.MarkFailed(ctx, task.ID, attempt, lastErr, statusCode, now); err != nil {
|
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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/tech/sendico/edge/callbacks/internal/storage"
|
"github.com/tech/sendico/edge/callbacks/internal/storage"
|
||||||
"github.com/tech/sendico/pkg/merrors"
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
"github.com/tech/sendico/pkg/mlogger"
|
"github.com/tech/sendico/pkg/mlogger"
|
||||||
|
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||||
"go.mongodb.org/mongo-driver/v2/bson"
|
"go.mongodb.org/mongo-driver/v2/bson"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
@@ -45,7 +46,7 @@ func (s *service) Resolve(ctx context.Context, eventType string, organizationRef
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Warn("Failed to resolve active endpoints",
|
s.logger.Warn("Failed to resolve active endpoints",
|
||||||
zap.String("event_type", eventType),
|
zap.String("event_type", eventType),
|
||||||
zap.String("organization_ref", organizationRef.Hex()),
|
mzap.ObjRef("organization_ref", organizationRef),
|
||||||
zap.Error(err),
|
zap.Error(err),
|
||||||
)
|
)
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -53,7 +54,7 @@ func (s *service) Resolve(ctx context.Context, eventType string, organizationRef
|
|||||||
|
|
||||||
s.logger.Debug("Resolved active endpoints",
|
s.logger.Debug("Resolved active endpoints",
|
||||||
zap.String("event_type", eventType),
|
zap.String("event_type", eventType),
|
||||||
zap.String("organization_ref", organizationRef.Hex()),
|
mzap.ObjRef("organization_ref", organizationRef),
|
||||||
zap.Int("endpoints", len(endpoints)),
|
zap.Int("endpoints", len(endpoints)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -66,8 +66,7 @@ func (s *Service) postCreditResponder(_ context.Context, req *ledgerv1.PostCredi
|
|||||||
existingEntry, err := s.storage.JournalEntries().GetByIdempotencyKey(ctx, orgRef, req.IdempotencyKey)
|
existingEntry, err := s.storage.JournalEntries().GetByIdempotencyKey(ctx, orgRef, req.IdempotencyKey)
|
||||||
if err == nil && existingEntry != nil {
|
if err == nil && existingEntry != nil {
|
||||||
recordDuplicateRequest(journalEntryTypeCredit)
|
recordDuplicateRequest(journalEntryTypeCredit)
|
||||||
logger.Info("Duplicate credit request (idempotency)",
|
logger.Info("Duplicate credit request (idempotency)", mzap.StorableRef(existingEntry))
|
||||||
zap.String("existingEntryID", existingEntry.GetID().Hex()))
|
|
||||||
return &ledgerv1.PostResponse{
|
return &ledgerv1.PostResponse{
|
||||||
JournalEntryRef: existingEntry.GetID().Hex(),
|
JournalEntryRef: existingEntry.GetID().Hex(),
|
||||||
Version: existingEntry.Version,
|
Version: existingEntry.Version,
|
||||||
|
|||||||
@@ -64,8 +64,7 @@ func (s *Service) postDebitResponder(_ context.Context, req *ledgerv1.PostDebitR
|
|||||||
existingEntry, err := s.storage.JournalEntries().GetByIdempotencyKey(ctx, orgRef, req.IdempotencyKey)
|
existingEntry, err := s.storage.JournalEntries().GetByIdempotencyKey(ctx, orgRef, req.IdempotencyKey)
|
||||||
if err == nil && existingEntry != nil {
|
if err == nil && existingEntry != nil {
|
||||||
recordDuplicateRequest(journalEntryTypeDebit)
|
recordDuplicateRequest(journalEntryTypeDebit)
|
||||||
logger.Info("Duplicate debit request (idempotency)",
|
logger.Info("Duplicate debit request (idempotency)", mzap.StorableRef(existingEntry))
|
||||||
zap.String("existingEntryID", existingEntry.GetID().Hex()))
|
|
||||||
return &ledgerv1.PostResponse{
|
return &ledgerv1.PostResponse{
|
||||||
JournalEntryRef: existingEntry.GetID().Hex(),
|
JournalEntryRef: existingEntry.GetID().Hex(),
|
||||||
Version: existingEntry.Version,
|
Version: existingEntry.Version,
|
||||||
@@ -123,7 +122,7 @@ func (s *Service) postDebitResponder(_ context.Context, req *ledgerv1.PostDebitR
|
|||||||
if err == storage.ErrAccountNotFound {
|
if err == storage.ErrAccountNotFound {
|
||||||
return nil, merrors.NoData(fmt.Sprintf("charges[%d]: account not found", i))
|
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")
|
return nil, merrors.Internal("failed to get charge account")
|
||||||
}
|
}
|
||||||
if err := validateAccountForOrg(chargeAccount, orgRef, charge.Money.Currency); err != nil {
|
if err := validateAccountForOrg(chargeAccount, orgRef, charge.Money.Currency); err != nil {
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ func (s *Service) postExternalCreditResponder(_ context.Context, req *ledgerv1.P
|
|||||||
if err == nil && existingEntry != nil {
|
if err == nil && existingEntry != nil {
|
||||||
recordDuplicateRequest(journalEntryTypeCredit)
|
recordDuplicateRequest(journalEntryTypeCredit)
|
||||||
logger.Info("Duplicate external credit request (idempotency)",
|
logger.Info("Duplicate external credit request (idempotency)",
|
||||||
zap.String("existingEntryID", existingEntry.GetID().Hex()))
|
mzap.StorableRef(existingEntry))
|
||||||
return &ledgerv1.PostResponse{
|
return &ledgerv1.PostResponse{
|
||||||
JournalEntryRef: existingEntry.GetID().Hex(),
|
JournalEntryRef: existingEntry.GetID().Hex(),
|
||||||
Version: existingEntry.Version,
|
Version: existingEntry.Version,
|
||||||
@@ -140,7 +140,7 @@ func (s *Service) postExternalCreditResponder(_ context.Context, req *ledgerv1.P
|
|||||||
if err == storage.ErrAccountNotFound {
|
if err == storage.ErrAccountNotFound {
|
||||||
return nil, merrors.NoData(fmt.Sprintf("charges[%d]: account not found", i))
|
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")
|
return nil, merrors.Internal("failed to get charge account")
|
||||||
}
|
}
|
||||||
if err := validateAccountForOrg(chargeAccount, orgRef, charge.Money.Currency); err != nil {
|
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)
|
existingEntry, err := s.storage.JournalEntries().GetByIdempotencyKey(ctx, orgRef, req.IdempotencyKey)
|
||||||
if err == nil && existingEntry != nil {
|
if err == nil && existingEntry != nil {
|
||||||
recordDuplicateRequest(journalEntryTypeDebit)
|
recordDuplicateRequest(journalEntryTypeDebit)
|
||||||
logger.Info("Duplicate external debit request (idempotency)",
|
logger.Info("Duplicate external debit request (idempotency)", mzap.StorableRef(existingEntry))
|
||||||
zap.String("existingEntryID", existingEntry.GetID().Hex()))
|
|
||||||
return &ledgerv1.PostResponse{
|
return &ledgerv1.PostResponse{
|
||||||
JournalEntryRef: existingEntry.GetID().Hex(),
|
JournalEntryRef: existingEntry.GetID().Hex(),
|
||||||
Version: existingEntry.Version,
|
Version: existingEntry.Version,
|
||||||
@@ -366,7 +365,7 @@ func (s *Service) postExternalDebitResponder(_ context.Context, req *ledgerv1.Po
|
|||||||
if err == storage.ErrAccountNotFound {
|
if err == storage.ErrAccountNotFound {
|
||||||
return nil, merrors.NoData(fmt.Sprintf("charges[%d]: account not found", i))
|
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")
|
return nil, merrors.Internal("failed to get charge account")
|
||||||
}
|
}
|
||||||
if err := validateAccountForOrg(chargeAccount, orgRef, charge.Money.Currency); err != nil {
|
if err := validateAccountForOrg(chargeAccount, orgRef, charge.Money.Currency); err != nil {
|
||||||
|
|||||||
@@ -77,8 +77,7 @@ func (s *Service) fxResponder(_ context.Context, req *ledgerv1.FXRequest) gsresp
|
|||||||
existingEntry, err := s.storage.JournalEntries().GetByIdempotencyKey(ctx, orgRef, req.IdempotencyKey)
|
existingEntry, err := s.storage.JournalEntries().GetByIdempotencyKey(ctx, orgRef, req.IdempotencyKey)
|
||||||
if err == nil && existingEntry != nil {
|
if err == nil && existingEntry != nil {
|
||||||
recordDuplicateRequest(journalEntryTypeFX)
|
recordDuplicateRequest(journalEntryTypeFX)
|
||||||
logger.Info("Duplicate FX request (idempotency)",
|
logger.Info("Duplicate FX request (idempotency)", mzap.StorableRef(existingEntry))
|
||||||
zap.String("existingEntryID", existingEntry.GetID().Hex()))
|
|
||||||
return &ledgerv1.PostResponse{
|
return &ledgerv1.PostResponse{
|
||||||
JournalEntryRef: existingEntry.GetID().Hex(),
|
JournalEntryRef: existingEntry.GetID().Hex(),
|
||||||
Version: existingEntry.Version,
|
Version: existingEntry.Version,
|
||||||
@@ -162,7 +161,7 @@ func (s *Service) fxResponder(_ context.Context, req *ledgerv1.FXRequest) gsresp
|
|||||||
if err == storage.ErrAccountNotFound {
|
if err == storage.ErrAccountNotFound {
|
||||||
return nil, merrors.NoData(fmt.Sprintf("charges[%d]: account not found", i))
|
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")
|
return nil, merrors.Internal("failed to get charge account")
|
||||||
}
|
}
|
||||||
if err := validateAccountForOrg(chargeAccount, orgRef, charge.Money.Currency); err != nil {
|
if err := validateAccountForOrg(chargeAccount, orgRef, charge.Money.Currency); err != nil {
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ func (s *Service) resolveSettlementAccount(ctx context.Context, orgRef bson.Obje
|
|||||||
if errors.Is(err, storage.ErrAccountNotFound) {
|
if errors.Is(err, storage.ErrAccountNotFound) {
|
||||||
return nil, merrors.NoData("contra account not found")
|
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")
|
return nil, merrors.Internal("failed to load contra account")
|
||||||
}
|
}
|
||||||
if err := validateAccountForOrg(account, orgRef, currency); err != nil {
|
if err := validateAccountForOrg(account, orgRef, currency); err != nil {
|
||||||
|
|||||||
@@ -87,8 +87,7 @@ func (s *Service) transferResponder(_ context.Context, req *ledgerv1.TransferReq
|
|||||||
existingEntry, err := s.storage.JournalEntries().GetByIdempotencyKey(ctx, orgRef, req.IdempotencyKey)
|
existingEntry, err := s.storage.JournalEntries().GetByIdempotencyKey(ctx, orgRef, req.IdempotencyKey)
|
||||||
if err == nil && existingEntry != nil {
|
if err == nil && existingEntry != nil {
|
||||||
recordDuplicateRequest(journalEntryTypeTransfer)
|
recordDuplicateRequest(journalEntryTypeTransfer)
|
||||||
logger.Info("Duplicate transfer request (idempotency)",
|
logger.Info("Duplicate transfer request (idempotency)", mzap.StorableRef(existingEntry))
|
||||||
zap.String("existingEntryID", existingEntry.GetID().Hex()))
|
|
||||||
return &ledgerv1.PostResponse{
|
return &ledgerv1.PostResponse{
|
||||||
JournalEntryRef: existingEntry.GetID().Hex(),
|
JournalEntryRef: existingEntry.GetID().Hex(),
|
||||||
Version: existingEntry.Version,
|
Version: existingEntry.Version,
|
||||||
@@ -172,7 +171,7 @@ func (s *Service) transferResponder(_ context.Context, req *ledgerv1.TransferReq
|
|||||||
if err == storage.ErrAccountNotFound {
|
if err == storage.ErrAccountNotFound {
|
||||||
return nil, merrors.NoData(fmt.Sprintf("charges[%d]: account not found", i))
|
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")
|
return nil, merrors.Internal("failed to get charge account")
|
||||||
}
|
}
|
||||||
if err := validateAccountForOrg(chargeAccount, orgRef, charge.Money.Currency); err != nil {
|
if err := validateAccountForOrg(chargeAccount, orgRef, charge.Money.Currency); err != nil {
|
||||||
|
|||||||
@@ -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 err := b.repo.FindOneByFilter(ctx, filter, existing); err != nil {
|
||||||
if errors.Is(err, merrors.ErrNoData) {
|
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)
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if existing.GetID() != nil {
|
if existing.GetID() != nil {
|
||||||
balance.SetID(*existing.GetID())
|
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))
|
zap.String("balance", balance.Balance))
|
||||||
return b.repo.Update(ctx, balance)
|
return b.repo.Update(ctx, balance)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
paymentTypeAccount pkgmodel.PaymentType = 8
|
paymentTypeAccount pkgmodel.PaymentType = 8
|
||||||
maxPrivateMethodResolutionDepth = 8
|
maxPrivateMethodResolutionDepth int = 8
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Service) GetPaymentMethodPrivate(ctx context.Context, req *methodsv1.GetPaymentMethodPrivateRequest) (*methodsv1.GetPaymentMethodPrivateResponse, error) {
|
func (s *Service) GetPaymentMethodPrivate(ctx context.Context, req *methodsv1.GetPaymentMethodPrivateRequest) (*methodsv1.GetPaymentMethodPrivateResponse, error) {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
np "github.com/tech/sendico/pkg/messaging/notifications/processor"
|
np "github.com/tech/sendico/pkg/messaging/notifications/processor"
|
||||||
nm "github.com/tech/sendico/pkg/model/notification"
|
nm "github.com/tech/sendico/pkg/model/notification"
|
||||||
"github.com/tech/sendico/pkg/mservice"
|
"github.com/tech/sendico/pkg/mservice"
|
||||||
|
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||||
"go.mongodb.org/mongo-driver/v2/bson"
|
"go.mongodb.org/mongo-driver/v2/bson"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
@@ -62,25 +63,25 @@ func (s *Service) onRecipientNotification(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Warn("Failed to cascade archive payment methods by recipient",
|
s.logger.Warn("Failed to cascade archive payment methods by recipient",
|
||||||
zap.Error(err),
|
zap.Error(err),
|
||||||
zap.String("recipient_ref", recipientRef.Hex()),
|
mzap.ObjRef("recipient_ref", recipientRef),
|
||||||
zap.String("actor_account_ref", actorAccountRef.Hex()))
|
mzap.ObjRef("actor_account_ref", actorAccountRef))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s.logger.Info("Recipient archive cascade applied to payment methods",
|
s.logger.Info("Recipient archive cascade applied to payment methods",
|
||||||
zap.String("recipient_ref", recipientRef.Hex()),
|
mzap.ObjRef("recipient_ref", recipientRef),
|
||||||
zap.String("actor_account_ref", actorAccountRef.Hex()),
|
mzap.ObjRef("actor_account_ref", actorAccountRef),
|
||||||
zap.Int("updated_count", updated))
|
zap.Int("updated_count", updated))
|
||||||
case nm.NADeleted:
|
case nm.NADeleted:
|
||||||
if err := s.pmstore.DeleteByRecipient(ctx, recipientRef); err != nil {
|
if err := s.pmstore.DeleteByRecipient(ctx, recipientRef); err != nil {
|
||||||
s.logger.Warn("Failed to cascade delete payment methods by recipient",
|
s.logger.Warn("Failed to cascade delete payment methods by recipient",
|
||||||
zap.Error(err),
|
zap.Error(err),
|
||||||
zap.String("recipient_ref", recipientRef.Hex()),
|
mzap.ObjRef("recipient_ref", recipientRef),
|
||||||
zap.String("actor_account_ref", actorAccountRef.Hex()))
|
mzap.ObjRef("actor_account_ref", actorAccountRef))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s.logger.Info("Recipient delete cascade applied to payment methods",
|
s.logger.Info("Recipient delete cascade applied to payment methods",
|
||||||
zap.String("recipient_ref", recipientRef.Hex()),
|
mzap.ObjRef("recipient_ref", recipientRef),
|
||||||
zap.String("actor_account_ref", actorAccountRef.Hex()))
|
mzap.ObjRef("actor_account_ref", actorAccountRef))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/tech/sendico/pkg/merrors"
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
"github.com/tech/sendico/pkg/mlogger"
|
"github.com/tech/sendico/pkg/mlogger"
|
||||||
pm "github.com/tech/sendico/pkg/model"
|
pm "github.com/tech/sendico/pkg/model"
|
||||||
|
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||||
"go.mongodb.org/mongo-driver/v2/bson"
|
"go.mongodb.org/mongo-driver/v2/bson"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
@@ -24,7 +25,7 @@ type svc struct {
|
|||||||
func (s *svc) Create(in Input) (payment *Payment, err error) {
|
func (s *svc) Create(in Input) (payment *Payment, err error) {
|
||||||
logger := s.logger
|
logger := s.logger
|
||||||
logger.Debug("Starting Create",
|
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.String("quotation_ref", strings.TrimSpace(in.QuotationRef)),
|
||||||
zap.Int("steps_count", len(in.Steps)),
|
zap.Int("steps_count", len(in.Steps)),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/tech/sendico/payments/storage/model"
|
"github.com/tech/sendico/payments/storage/model"
|
||||||
"github.com/tech/sendico/pkg/merrors"
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
"github.com/tech/sendico/pkg/mlogger"
|
"github.com/tech/sendico/pkg/mlogger"
|
||||||
|
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -26,7 +27,7 @@ func (s *svc) TryReuse(
|
|||||||
) (payment *model.Payment, reused bool, err error) {
|
) (payment *model.Payment, reused bool, err error) {
|
||||||
logger := s.logger
|
logger := s.logger
|
||||||
logger.Debug("Starting Try reuse",
|
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) != ""),
|
zap.Bool("has_idempotency_key", strings.TrimSpace(in.IdempotencyKey) != ""),
|
||||||
)
|
)
|
||||||
defer func(start time.Time) {
|
defer func(start time.Time) {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/prepo"
|
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/prepo"
|
||||||
"github.com/tech/sendico/pkg/merrors"
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
"github.com/tech/sendico/pkg/mlogger"
|
"github.com/tech/sendico/pkg/mlogger"
|
||||||
|
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||||
"go.mongodb.org/mongo-driver/v2/bson"
|
"go.mongodb.org/mongo-driver/v2/bson"
|
||||||
"go.uber.org/zap"
|
"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) {
|
func (s *svc) GetPayment(ctx context.Context, in GetPaymentInput) (payment *agg.Payment, err error) {
|
||||||
logger := s.logger
|
logger := s.logger
|
||||||
logger.Debug("Starting Get payment",
|
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)),
|
zap.String("payment_ref", strings.TrimSpace(in.PaymentRef)),
|
||||||
)
|
)
|
||||||
defer func(start time.Time) {
|
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) {
|
func (s *svc) ListPayments(ctx context.Context, in ListPaymentsInput) (out *ListPaymentsOutput, err error) {
|
||||||
logger := s.logger
|
logger := s.logger
|
||||||
logger.Debug("Starting List payments",
|
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.String("quotation_ref", strings.TrimSpace(in.QuotationRef)),
|
||||||
zap.Int("states_count", len(in.States)),
|
zap.Int("states_count", len(in.States)),
|
||||||
zap.Int32("limit", in.Limit),
|
zap.Int32("limit", in.Limit),
|
||||||
|
|||||||
@@ -175,13 +175,13 @@ func (s *svc) GetByPaymentRef(ctx context.Context, orgRef bson.ObjectID, payment
|
|||||||
logger := s.logger
|
logger := s.logger
|
||||||
requestPaymentRef := strings.TrimSpace(paymentRef)
|
requestPaymentRef := strings.TrimSpace(paymentRef)
|
||||||
logger.Debug("Starting Get by payment ref",
|
logger.Debug("Starting Get by payment ref",
|
||||||
zap.String("organization_ref", orgRef.Hex()),
|
mzap.ObjRef("organization_ref", orgRef),
|
||||||
zap.String("payment_ref", requestPaymentRef),
|
zap.String("payment_ref", requestPaymentRef),
|
||||||
)
|
)
|
||||||
defer func(start time.Time) {
|
defer func(start time.Time) {
|
||||||
fields := []zap.Field{
|
fields := []zap.Field{
|
||||||
zap.Int64("duration_ms", time.Since(start).Milliseconds()),
|
zap.Int64("duration_ms", time.Since(start).Milliseconds()),
|
||||||
zap.String("organization_ref", orgRef.Hex()),
|
mzap.ObjRef("organization_ref", orgRef),
|
||||||
zap.String("payment_ref", requestPaymentRef),
|
zap.String("payment_ref", requestPaymentRef),
|
||||||
}
|
}
|
||||||
if payment != nil {
|
if payment != nil {
|
||||||
@@ -225,7 +225,7 @@ func (s *svc) GetByPaymentRefGlobal(ctx context.Context, paymentRef string) (pay
|
|||||||
}
|
}
|
||||||
if payment != nil {
|
if payment != nil {
|
||||||
fields = append(fields,
|
fields = append(fields,
|
||||||
zap.String("organization_ref", payment.OrganizationRef.Hex()),
|
mzap.ObjRef("organization_ref", payment.OrganizationRef),
|
||||||
zap.String("state", string(payment.State)),
|
zap.String("state", string(payment.State)),
|
||||||
zap.Uint64("version", payment.Version),
|
zap.Uint64("version", payment.Version),
|
||||||
)
|
)
|
||||||
@@ -253,7 +253,7 @@ func (s *svc) GetByIdempotencyKey(ctx context.Context, orgRef bson.ObjectID, ide
|
|||||||
logger := s.logger
|
logger := s.logger
|
||||||
hasKey := strings.TrimSpace(idempotencyKey) != ""
|
hasKey := strings.TrimSpace(idempotencyKey) != ""
|
||||||
logger.Debug("Starting Get by idempotency key",
|
logger.Debug("Starting Get by idempotency key",
|
||||||
zap.String("organization_ref", orgRef.Hex()),
|
mzap.ObjRef("organization_ref", orgRef),
|
||||||
zap.Bool("has_idempotency_key", hasKey),
|
zap.Bool("has_idempotency_key", hasKey),
|
||||||
)
|
)
|
||||||
defer func(start time.Time) {
|
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) {
|
func (s *svc) ListByQuotationRef(ctx context.Context, in ListByQuotationRefInput) (out *ListOutput, err error) {
|
||||||
logger := s.logger
|
logger := s.logger
|
||||||
logger.Debug("Starting List by quotation ref",
|
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.String("quotation_ref", strings.TrimSpace(in.QuotationRef)),
|
||||||
zap.Int32("limit", in.Limit),
|
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) {
|
func (s *svc) ListByState(ctx context.Context, in ListByStateInput) (out *ListOutput, err error) {
|
||||||
logger := s.logger
|
logger := s.logger
|
||||||
logger.Debug("Starting List by state",
|
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.String("state", string(in.State)),
|
||||||
zap.Int32("limit", in.Limit),
|
zap.Int32("limit", in.Limit),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,12 +5,13 @@ import (
|
|||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/tech/sendico/pkg/discovery"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"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/agg"
|
||||||
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/batchmeta"
|
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/batchmeta"
|
||||||
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/idem"
|
"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{
|
resolved, err := s.quote.ResolveAll(ctx, s.quoteStore, qsnap.ResolveAllInput{
|
||||||
OrganizationID: requestCtx.OrganizationID,
|
OrganizationRef: requestCtx.OrganizationID,
|
||||||
QuotationRef: requestCtx.QuotationRef,
|
QuotationRef: requestCtx.QuotationRef,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, remapResolveError(err)
|
return nil, remapResolveError(err)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
pon "github.com/tech/sendico/pkg/messaging/notifications/paymentorchestrator"
|
pon "github.com/tech/sendico/pkg/messaging/notifications/paymentorchestrator"
|
||||||
"github.com/tech/sendico/pkg/mlogger"
|
"github.com/tech/sendico/pkg/mlogger"
|
||||||
"github.com/tech/sendico/pkg/model"
|
"github.com/tech/sendico/pkg/model"
|
||||||
|
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -64,7 +65,7 @@ func (p *brokerPaymentStatusPublisher) Publish(_ context.Context, in paymentStat
|
|||||||
if paymentRef == "" || payment.OrganizationRef.IsZero() {
|
if paymentRef == "" || payment.OrganizationRef.IsZero() {
|
||||||
p.logger.Warn("Skipping payment status publish due to missing identifiers",
|
p.logger.Warn("Skipping payment status publish due to missing identifiers",
|
||||||
zap.String("payment_ref", paymentRef),
|
zap.String("payment_ref", paymentRef),
|
||||||
zap.String("organization_ref", payment.OrganizationRef.Hex()),
|
mzap.ObjRef("organization_ref", payment.OrganizationRef),
|
||||||
)
|
)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,8 +36,8 @@ type Output struct {
|
|||||||
|
|
||||||
// ResolveAllInput defines lookup scope for resolving all items in a batch quotation.
|
// ResolveAllInput defines lookup scope for resolving all items in a batch quotation.
|
||||||
type ResolveAllInput struct {
|
type ResolveAllInput struct {
|
||||||
OrganizationID bson.ObjectID
|
OrganizationRef bson.ObjectID
|
||||||
QuotationRef string
|
QuotationRef string
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveAllOutput contains all resolved items from a batch quotation.
|
// ResolveAllOutput contains all resolved items from a batch quotation.
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ func TestResolveAll_BatchReturnsAllItems(t *testing.T) {
|
|||||||
return record, nil
|
return record, nil
|
||||||
},
|
},
|
||||||
}, ResolveAllInput{
|
}, ResolveAllInput{
|
||||||
OrganizationID: orgID,
|
OrganizationRef: orgID,
|
||||||
QuotationRef: "batch-quote-ref",
|
QuotationRef: "batch-quote-ref",
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("ResolveAll returned error: %v", err)
|
t.Fatalf("ResolveAll returned error: %v", err)
|
||||||
@@ -94,8 +94,8 @@ func TestResolveAll_SingleShapeReturnsOneItem(t *testing.T) {
|
|||||||
return record, nil
|
return record, nil
|
||||||
},
|
},
|
||||||
}, ResolveAllInput{
|
}, ResolveAllInput{
|
||||||
OrganizationID: orgID,
|
OrganizationRef: orgID,
|
||||||
QuotationRef: "single-quote-ref",
|
QuotationRef: "single-quote-ref",
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("ResolveAll returned error: %v", err)
|
t.Fatalf("ResolveAll returned error: %v", err)
|
||||||
@@ -137,8 +137,8 @@ func TestResolveAll_NonExecutableItemFails(t *testing.T) {
|
|||||||
return record, nil
|
return record, nil
|
||||||
},
|
},
|
||||||
}, ResolveAllInput{
|
}, ResolveAllInput{
|
||||||
OrganizationID: orgID,
|
OrganizationRef: orgID,
|
||||||
QuotationRef: "batch-mixed",
|
QuotationRef: "batch-mixed",
|
||||||
})
|
})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("expected error for non-executable item")
|
t.Fatal("expected error for non-executable item")
|
||||||
@@ -172,8 +172,8 @@ func TestResolveAll_ExpiredQuoteFails(t *testing.T) {
|
|||||||
return record, nil
|
return record, nil
|
||||||
},
|
},
|
||||||
}, ResolveAllInput{
|
}, ResolveAllInput{
|
||||||
OrganizationID: orgID,
|
OrganizationRef: orgID,
|
||||||
QuotationRef: "expired-quote",
|
QuotationRef: "expired-quote",
|
||||||
})
|
})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("expected error for expired quote")
|
t.Fatal("expected error for expired quote")
|
||||||
@@ -187,8 +187,8 @@ func TestResolveAll_EmptyQuotationRefFails(t *testing.T) {
|
|||||||
resolver := New(Dependencies{Logger: zap.NewNop()})
|
resolver := New(Dependencies{Logger: zap.NewNop()})
|
||||||
|
|
||||||
_, err := resolver.ResolveAll(context.Background(), &fakeStore{}, ResolveAllInput{
|
_, err := resolver.ResolveAll(context.Background(), &fakeStore{}, ResolveAllInput{
|
||||||
OrganizationID: bson.NewObjectID(),
|
OrganizationRef: bson.NewObjectID(),
|
||||||
QuotationRef: "",
|
QuotationRef: "",
|
||||||
})
|
})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("expected error for empty quotation_ref")
|
t.Fatal("expected error for empty quotation_ref")
|
||||||
@@ -199,8 +199,8 @@ func TestResolveAll_QuoteNotFoundFails(t *testing.T) {
|
|||||||
resolver := New(Dependencies{Logger: zap.NewNop()})
|
resolver := New(Dependencies{Logger: zap.NewNop()})
|
||||||
|
|
||||||
_, err := resolver.ResolveAll(context.Background(), &fakeStore{}, ResolveAllInput{
|
_, err := resolver.ResolveAll(context.Background(), &fakeStore{}, ResolveAllInput{
|
||||||
OrganizationID: bson.NewObjectID(),
|
OrganizationRef: bson.NewObjectID(),
|
||||||
QuotationRef: "nonexistent",
|
QuotationRef: "nonexistent",
|
||||||
})
|
})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("expected error for not-found quote")
|
t.Fatal("expected error for not-found quote")
|
||||||
@@ -234,8 +234,8 @@ func TestResolveAll_SetsQuoteRefWhenEmpty(t *testing.T) {
|
|||||||
return record, nil
|
return record, nil
|
||||||
},
|
},
|
||||||
}, ResolveAllInput{
|
}, ResolveAllInput{
|
||||||
OrganizationID: orgID,
|
OrganizationRef: orgID,
|
||||||
QuotationRef: "batch-ref",
|
QuotationRef: "batch-ref",
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("ResolveAll returned error: %v", err)
|
t.Fatalf("ResolveAll returned error: %v", err)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
quotestorage "github.com/tech/sendico/payments/storage/quote"
|
quotestorage "github.com/tech/sendico/payments/storage/quote"
|
||||||
"github.com/tech/sendico/pkg/merrors"
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
"github.com/tech/sendico/pkg/mlogger"
|
"github.com/tech/sendico/pkg/mlogger"
|
||||||
|
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||||
"go.mongodb.org/mongo-driver/v2/bson"
|
"go.mongodb.org/mongo-driver/v2/bson"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
@@ -33,7 +34,7 @@ func (s *svc) Resolve(
|
|||||||
) (out *Output, err error) {
|
) (out *Output, err error) {
|
||||||
logger := s.logger
|
logger := s.logger
|
||||||
logger.Debug("Starting Resolve",
|
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)),
|
zap.String("quotation_ref", strings.TrimSpace(in.QuotationRef)),
|
||||||
)
|
)
|
||||||
defer func(start time.Time) {
|
defer func(start time.Time) {
|
||||||
@@ -105,7 +106,7 @@ func (s *svc) ResolveAll(
|
|||||||
) (out *ResolveAllOutput, err error) {
|
) (out *ResolveAllOutput, err error) {
|
||||||
logger := s.logger
|
logger := s.logger
|
||||||
logger.Debug("Starting ResolveAll",
|
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)),
|
zap.String("quotation_ref", strings.TrimSpace(in.QuotationRef)),
|
||||||
)
|
)
|
||||||
defer func(start time.Time) {
|
defer func(start time.Time) {
|
||||||
@@ -126,7 +127,7 @@ func (s *svc) ResolveAll(
|
|||||||
if store == nil {
|
if store == nil {
|
||||||
return nil, merrors.InvalidArgument("quotes store is required")
|
return nil, merrors.InvalidArgument("quotes store is required")
|
||||||
}
|
}
|
||||||
if in.OrganizationID.IsZero() {
|
if in.OrganizationRef.IsZero() {
|
||||||
return nil, merrors.InvalidArgument("organization_id is required")
|
return nil, merrors.InvalidArgument("organization_id is required")
|
||||||
}
|
}
|
||||||
quoteRef := strings.TrimSpace(in.QuotationRef)
|
quoteRef := strings.TrimSpace(in.QuotationRef)
|
||||||
@@ -134,7 +135,7 @@ func (s *svc) ResolveAll(
|
|||||||
return nil, merrors.InvalidArgument("quotation_ref is required")
|
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 err != nil {
|
||||||
if errors.Is(err, quotestorage.ErrQuoteNotFound) || errors.Is(err, merrors.ErrNoData) {
|
if errors.Is(err, quotestorage.ErrQuoteNotFound) || errors.Is(err, merrors.ErrNoData) {
|
||||||
return nil, ErrQuoteNotFound
|
return nil, ErrQuoteNotFound
|
||||||
|
|||||||
@@ -3,10 +3,12 @@ package orchestrator
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/tech/sendico/pkg/discovery"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"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/agg"
|
||||||
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/erecon"
|
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/erecon"
|
||||||
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/prepo"
|
"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",
|
s.logger.Debug("Reconciling payment from gateway execution event",
|
||||||
zap.String("payment_ref", strings.TrimSpace(payment.PaymentRef)),
|
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("step_ref", strings.TrimSpace(event.StepRef)),
|
||||||
zap.String("status", strings.TrimSpace(string(event.Status))),
|
zap.String("status", strings.TrimSpace(string(event.Status))),
|
||||||
zap.String("transfer_ref", strings.TrimSpace(event.TransferRef)),
|
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",
|
s.logger.Debug("Reconciling payment from observe polling result",
|
||||||
zap.String("payment_ref", strings.TrimSpace(payment.PaymentRef)),
|
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("step_ref", candidate.stepRef),
|
||||||
zap.String("status", strings.TrimSpace(string(event.Status))),
|
zap.String("status", strings.TrimSpace(string(event.Status))),
|
||||||
zap.String("transfer_ref", candidate.transferRef),
|
zap.String("transfer_ref", candidate.transferRef),
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tech/sendico/pkg/mlogger"
|
"github.com/tech/sendico/pkg/mlogger"
|
||||||
|
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||||
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
|
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
|
||||||
endpointv1 "github.com/tech/sendico/pkg/proto/payments/endpoint/v1"
|
endpointv1 "github.com/tech/sendico/pkg/proto/payments/endpoint/v1"
|
||||||
quotationv2 "github.com/tech/sendico/pkg/proto/payments/quotation/v2"
|
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 {
|
func (s *QuotationServiceV2) quotePaymentLogger(req *quotationv2.QuotePaymentRequest) mlogger.Logger {
|
||||||
return s.logger.With(
|
return s.logger.With(
|
||||||
zap.String("flow_ref", bson.NewObjectID().Hex()),
|
mzap.ObjRef("flow_ref", bson.NewObjectID()),
|
||||||
zap.String("rpc_method", "QuotePayment"),
|
zap.String("rpc_method", "QuotePayment"),
|
||||||
zap.String("organization_ref", strings.TrimSpace(req.GetMeta().GetOrganizationRef())),
|
zap.String("organization_ref", strings.TrimSpace(req.GetMeta().GetOrganizationRef())),
|
||||||
zap.String("idempotency_key", strings.TrimSpace(req.GetIdempotencyKey())),
|
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 {
|
func (s *QuotationServiceV2) quotePaymentsLogger(req *quotationv2.QuotePaymentsRequest) mlogger.Logger {
|
||||||
return s.logger.With(
|
return s.logger.With(
|
||||||
zap.String("flow_ref", bson.NewObjectID().Hex()),
|
mzap.ObjRef("flow_ref", bson.NewObjectID()),
|
||||||
zap.String("rpc_method", "QuotePayments"),
|
zap.String("rpc_method", "QuotePayments"),
|
||||||
zap.String("organization_ref", strings.TrimSpace(req.GetMeta().GetOrganizationRef())),
|
zap.String("organization_ref", strings.TrimSpace(req.GetMeta().GetOrganizationRef())),
|
||||||
zap.String("idempotency_key", strings.TrimSpace(req.GetIdempotencyKey())),
|
zap.String("idempotency_key", strings.TrimSpace(req.GetIdempotencyKey())),
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/tech/sendico/pkg/db/storable"
|
"github.com/tech/sendico/pkg/db/storable"
|
||||||
"github.com/tech/sendico/pkg/mlogger"
|
"github.com/tech/sendico/pkg/mlogger"
|
||||||
"github.com/tech/sendico/pkg/model"
|
"github.com/tech/sendico/pkg/model"
|
||||||
|
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||||
"go.mongodb.org/mongo-driver/v2/bson"
|
"go.mongodb.org/mongo-driver/v2/bson"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
@@ -43,7 +44,7 @@ func (db *IndexableDB[T]) Reorder(ctx context.Context, objectRef bson.ObjectID,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
db.logger.Error("Failed to get object for reordering",
|
db.logger.Error("Failed to get object for reordering",
|
||||||
zap.Error(err),
|
zap.Error(err),
|
||||||
zap.String("object_ref", objectRef.Hex()),
|
mzap.ObjRef("object_ref", objectRef),
|
||||||
zap.Int("new_index", newIndex))
|
zap.Int("new_index", newIndex))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -53,7 +54,7 @@ func (db *IndexableDB[T]) Reorder(ctx context.Context, objectRef bson.ObjectID,
|
|||||||
currentIndex := indexable.Index
|
currentIndex := indexable.Index
|
||||||
if currentIndex == newIndex {
|
if currentIndex == newIndex {
|
||||||
db.logger.Debug("No reordering needed - same index",
|
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("current_index", currentIndex),
|
||||||
zap.Int("new_index", newIndex))
|
zap.Int("new_index", newIndex))
|
||||||
return nil // No change needed
|
return nil // No change needed
|
||||||
@@ -71,14 +72,14 @@ func (db *IndexableDB[T]) Reorder(ctx context.Context, objectRef bson.ObjectID,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
db.logger.Error("Failed to shift objects during reordering (moving down)",
|
db.logger.Error("Failed to shift objects during reordering (moving down)",
|
||||||
zap.Error(err),
|
zap.Error(err),
|
||||||
zap.String("object_ref", objectRef.Hex()),
|
mzap.ObjRef("object_ref", objectRef),
|
||||||
zap.Int("current_index", currentIndex),
|
zap.Int("current_index", currentIndex),
|
||||||
zap.Int("new_index", newIndex),
|
zap.Int("new_index", newIndex),
|
||||||
zap.Int("updated_count", updatedCount))
|
zap.Int("updated_count", updatedCount))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
db.logger.Debug("Successfully shifted objects (moving down)",
|
db.logger.Debug("Successfully shifted objects (moving down)",
|
||||||
zap.String("object_ref", objectRef.Hex()),
|
mzap.ObjRef("object_ref", objectRef),
|
||||||
zap.Int("updated_count", updatedCount))
|
zap.Int("updated_count", updatedCount))
|
||||||
} else {
|
} else {
|
||||||
// Moving up: shift items between newIndex and currentIndex-1 down by +1
|
// 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 {
|
if err != nil {
|
||||||
db.logger.Error("Failed to shift objects during reordering (moving up)",
|
db.logger.Error("Failed to shift objects during reordering (moving up)",
|
||||||
zap.Error(err),
|
zap.Error(err),
|
||||||
zap.String("object_ref", objectRef.Hex()),
|
mzap.ObjRef("object_ref", objectRef),
|
||||||
zap.Int("current_index", currentIndex),
|
zap.Int("current_index", currentIndex),
|
||||||
zap.Int("new_index", newIndex),
|
zap.Int("new_index", newIndex),
|
||||||
zap.Int("updated_count", updatedCount))
|
zap.Int("updated_count", updatedCount))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
db.logger.Debug("Successfully shifted objects (moving up)",
|
db.logger.Debug("Successfully shifted objects (moving up)",
|
||||||
zap.String("object_ref", objectRef.Hex()),
|
mzap.ObjRef("object_ref", objectRef),
|
||||||
zap.Int("updated_count", updatedCount))
|
zap.Int("updated_count", updatedCount))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,14 +109,14 @@ func (db *IndexableDB[T]) Reorder(ctx context.Context, objectRef bson.ObjectID,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
db.logger.Error("Failed to update target object index",
|
db.logger.Error("Failed to update target object index",
|
||||||
zap.Error(err),
|
zap.Error(err),
|
||||||
zap.String("object_ref", objectRef.Hex()),
|
mzap.ObjRef("object_ref", objectRef),
|
||||||
zap.Int("current_index", currentIndex),
|
zap.Int("current_index", currentIndex),
|
||||||
zap.Int("new_index", newIndex))
|
zap.Int("new_index", newIndex))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
db.logger.Info("Successfully reordered object",
|
db.logger.Info("Successfully reordered object",
|
||||||
zap.String("object_ref", objectRef.Hex()),
|
mzap.ObjRef("object_ref", objectRef),
|
||||||
zap.Int("old_index", currentIndex),
|
zap.Int("old_index", currentIndex),
|
||||||
zap.Int("new_index", newIndex))
|
zap.Int("new_index", newIndex))
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -5,20 +5,22 @@ import 'package:pshared/data/dto/payment/intent/payment.dart';
|
|||||||
|
|
||||||
part 'initiate.g.dart';
|
part 'initiate.g.dart';
|
||||||
|
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class InitiatePaymentRequest extends PaymentBaseRequest {
|
class InitiatePaymentRequest extends PaymentBaseRequest {
|
||||||
final PaymentIntentDTO? intent;
|
final PaymentIntentDTO? intent;
|
||||||
final String? quoteRef;
|
final String? quoteRef;
|
||||||
|
final String? clientPaymentRef;
|
||||||
|
|
||||||
const InitiatePaymentRequest({
|
const InitiatePaymentRequest({
|
||||||
required super.idempotencyKey,
|
required super.idempotencyKey,
|
||||||
super.metadata,
|
super.metadata,
|
||||||
this.intent,
|
this.intent,
|
||||||
this.quoteRef,
|
this.quoteRef,
|
||||||
|
this.clientPaymentRef,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory InitiatePaymentRequest.fromJson(Map<String, dynamic> json) => _$InitiatePaymentRequestFromJson(json);
|
factory InitiatePaymentRequest.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$InitiatePaymentRequestFromJson(json);
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> toJson() => _$InitiatePaymentRequestToJson(this);
|
Map<String, dynamic> toJson() => _$InitiatePaymentRequestToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,18 +3,20 @@ import 'package:pshared/api/requests/payment/base.dart';
|
|||||||
|
|
||||||
part 'initiate_payments.g.dart';
|
part 'initiate_payments.g.dart';
|
||||||
|
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class InitiatePaymentsRequest extends PaymentBaseRequest {
|
class InitiatePaymentsRequest extends PaymentBaseRequest {
|
||||||
final String quoteRef;
|
final String quoteRef;
|
||||||
|
final String? clientPaymentRef;
|
||||||
|
|
||||||
const InitiatePaymentsRequest({
|
const InitiatePaymentsRequest({
|
||||||
required super.idempotencyKey,
|
required super.idempotencyKey,
|
||||||
super.metadata,
|
super.metadata,
|
||||||
required this.quoteRef,
|
required this.quoteRef,
|
||||||
|
this.clientPaymentRef,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory InitiatePaymentsRequest.fromJson(Map<String, dynamic> json) => _$InitiatePaymentsRequestFromJson(json);
|
factory InitiatePaymentsRequest.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$InitiatePaymentsRequestFromJson(json);
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> toJson() => _$InitiatePaymentsRequestToJson(this);
|
Map<String, dynamic> toJson() => _$InitiatePaymentsRequestToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import 'package:pshared/provider/resource.dart';
|
|||||||
import 'package:pshared/service/payment/multiple.dart';
|
import 'package:pshared/service/payment/multiple.dart';
|
||||||
import 'package:pshared/utils/exception.dart';
|
import 'package:pshared/utils/exception.dart';
|
||||||
|
|
||||||
|
|
||||||
class MultiPaymentProvider extends ChangeNotifier {
|
class MultiPaymentProvider extends ChangeNotifier {
|
||||||
late OrganizationsProvider _organization;
|
late OrganizationsProvider _organization;
|
||||||
late MultiQuotationProvider _quotation;
|
late MultiQuotationProvider _quotation;
|
||||||
@@ -30,6 +29,7 @@ class MultiPaymentProvider extends ChangeNotifier {
|
|||||||
|
|
||||||
Future<List<Payment>> pay({
|
Future<List<Payment>> pay({
|
||||||
String? idempotencyKey,
|
String? idempotencyKey,
|
||||||
|
String? clientPaymentRef,
|
||||||
Map<String, String>? metadata,
|
Map<String, String>? metadata,
|
||||||
}) async {
|
}) async {
|
||||||
if (!_organization.isOrganizationSet) {
|
if (!_organization.isOrganizationSet) {
|
||||||
@@ -52,6 +52,7 @@ class MultiPaymentProvider extends ChangeNotifier {
|
|||||||
_organization.current.id,
|
_organization.current.id,
|
||||||
quoteRef,
|
quoteRef,
|
||||||
idempotencyKey: idempotencyKey,
|
idempotencyKey: idempotencyKey,
|
||||||
|
clientPaymentRef: clientPaymentRef,
|
||||||
metadata: metadata,
|
metadata: metadata,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -7,12 +7,15 @@ import 'package:pshared/provider/resource.dart';
|
|||||||
import 'package:pshared/service/payment/service.dart';
|
import 'package:pshared/service/payment/service.dart';
|
||||||
import 'package:pshared/utils/exception.dart';
|
import 'package:pshared/utils/exception.dart';
|
||||||
|
|
||||||
|
|
||||||
class PaymentProvider extends ChangeNotifier {
|
class PaymentProvider extends ChangeNotifier {
|
||||||
late OrganizationsProvider _organization;
|
late OrganizationsProvider _organization;
|
||||||
late QuotationProvider _quotation;
|
late QuotationProvider _quotation;
|
||||||
|
|
||||||
Resource<Payment> _payment = Resource(data: null, isLoading: false, error: null);
|
Resource<Payment> _payment = Resource(
|
||||||
|
data: null,
|
||||||
|
isLoading: false,
|
||||||
|
error: null,
|
||||||
|
);
|
||||||
bool _isLoaded = false;
|
bool _isLoaded = false;
|
||||||
|
|
||||||
void update(OrganizationsProvider organization, QuotationProvider quotation) {
|
void update(OrganizationsProvider organization, QuotationProvider quotation) {
|
||||||
@@ -23,15 +26,21 @@ class PaymentProvider extends ChangeNotifier {
|
|||||||
Payment? get payment => _payment.data;
|
Payment? get payment => _payment.data;
|
||||||
bool get isLoading => _payment.isLoading;
|
bool get isLoading => _payment.isLoading;
|
||||||
Exception? get error => _payment.error;
|
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) {
|
void _setResource(Resource<Payment> payment) {
|
||||||
_payment = payment;
|
_payment = payment;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Payment?> pay({String? idempotencyKey, Map<String, String>? metadata}) async {
|
Future<Payment?> pay({
|
||||||
if (!_organization.isOrganizationSet) throw StateError('Organization is not set');
|
String? idempotencyKey,
|
||||||
|
String? clientPaymentRef,
|
||||||
|
Map<String, String>? metadata,
|
||||||
|
}) async {
|
||||||
|
if (!_organization.isOrganizationSet)
|
||||||
|
throw StateError('Organization is not set');
|
||||||
final quoteRef = _quotation.quotation?.quoteRef;
|
final quoteRef = _quotation.quotation?.quoteRef;
|
||||||
if (quoteRef == null || quoteRef.isEmpty) {
|
if (quoteRef == null || quoteRef.isEmpty) {
|
||||||
throw StateError('Quotation reference is not set');
|
throw StateError('Quotation reference is not set');
|
||||||
@@ -49,12 +58,17 @@ class PaymentProvider extends ChangeNotifier {
|
|||||||
_organization.current.id,
|
_organization.current.id,
|
||||||
quoteRef,
|
quoteRef,
|
||||||
idempotencyKey: resolvedIdempotencyKey,
|
idempotencyKey: resolvedIdempotencyKey,
|
||||||
|
clientPaymentRef: clientPaymentRef,
|
||||||
metadata: metadata,
|
metadata: metadata,
|
||||||
);
|
);
|
||||||
_isLoaded = true;
|
_isLoaded = true;
|
||||||
_setResource(_payment.copyWith(data: response, isLoading: false, error: null));
|
_setResource(
|
||||||
|
_payment.copyWith(data: response, isLoading: false, error: null),
|
||||||
|
);
|
||||||
} catch (e) {
|
} 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;
|
return _payment.data;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import 'package:pshared/models/payment/quote/quotes.dart';
|
|||||||
import 'package:pshared/service/authorization/service.dart';
|
import 'package:pshared/service/authorization/service.dart';
|
||||||
import 'package:pshared/service/services.dart';
|
import 'package:pshared/service/services.dart';
|
||||||
|
|
||||||
|
|
||||||
class MultiplePaymentsService {
|
class MultiplePaymentsService {
|
||||||
static final _logger = Logger('service.payment.multiple');
|
static final _logger = Logger('service.payment.multiple');
|
||||||
static const String _objectType = Services.payments;
|
static const String _objectType = Services.payments;
|
||||||
@@ -37,6 +36,7 @@ class MultiplePaymentsService {
|
|||||||
String organizationRef,
|
String organizationRef,
|
||||||
String quoteRef, {
|
String quoteRef, {
|
||||||
String? idempotencyKey,
|
String? idempotencyKey,
|
||||||
|
String? clientPaymentRef,
|
||||||
Map<String, String>? metadata,
|
Map<String, String>? metadata,
|
||||||
}) async {
|
}) async {
|
||||||
_logger.fine(
|
_logger.fine(
|
||||||
@@ -45,6 +45,7 @@ class MultiplePaymentsService {
|
|||||||
final request = InitiatePaymentsRequest(
|
final request = InitiatePaymentsRequest(
|
||||||
idempotencyKey: idempotencyKey ?? const Uuid().v4(),
|
idempotencyKey: idempotencyKey ?? const Uuid().v4(),
|
||||||
quoteRef: quoteRef,
|
quoteRef: quoteRef,
|
||||||
|
clientPaymentRef: clientPaymentRef,
|
||||||
metadata: metadata,
|
metadata: metadata,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ class PaymentService {
|
|||||||
String organizationRef,
|
String organizationRef,
|
||||||
String quotationRef, {
|
String quotationRef, {
|
||||||
String? idempotencyKey,
|
String? idempotencyKey,
|
||||||
|
String? clientPaymentRef,
|
||||||
Map<String, String>? metadata,
|
Map<String, String>? metadata,
|
||||||
}) async {
|
}) async {
|
||||||
_logger.fine(
|
_logger.fine(
|
||||||
@@ -90,6 +91,7 @@ class PaymentService {
|
|||||||
final request = InitiatePaymentRequest(
|
final request = InitiatePaymentRequest(
|
||||||
idempotencyKey: idempotencyKey ?? Uuid().v4(),
|
idempotencyKey: idempotencyKey ?? Uuid().v4(),
|
||||||
quoteRef: quotationRef,
|
quoteRef: quotationRef,
|
||||||
|
clientPaymentRef: clientPaymentRef,
|
||||||
metadata: metadata,
|
metadata: metadata,
|
||||||
);
|
);
|
||||||
final response = await AuthorizationService.getPOSTResponse(
|
final response = await AuthorizationService.getPOSTResponse(
|
||||||
|
|||||||
@@ -158,16 +158,13 @@ void main() {
|
|||||||
final request = InitiatePaymentRequest(
|
final request = InitiatePaymentRequest(
|
||||||
idempotencyKey: 'idem-2',
|
idempotencyKey: 'idem-2',
|
||||||
quoteRef: 'q-1',
|
quoteRef: 'q-1',
|
||||||
metadata: const {'client_payment_ref': 'cp-1'},
|
clientPaymentRef: 'cp-1',
|
||||||
);
|
);
|
||||||
|
|
||||||
final json = request.toJson();
|
final json = request.toJson();
|
||||||
expect(json['idempotencyKey'], equals('idem-2'));
|
expect(json['idempotencyKey'], equals('idem-2'));
|
||||||
expect(json['quoteRef'], equals('q-1'));
|
expect(json['quoteRef'], equals('q-1'));
|
||||||
expect(
|
expect(json['clientPaymentRef'], equals('cp-1'));
|
||||||
(json['metadata'] as Map<String, dynamic>)['client_payment_ref'],
|
|
||||||
equals('cp-1'),
|
|
||||||
);
|
|
||||||
expect(json.containsKey('intent'), isTrue);
|
expect(json.containsKey('intent'), isTrue);
|
||||||
expect(json['intent'], isNull);
|
expect(json['intent'], isNull);
|
||||||
});
|
});
|
||||||
@@ -176,16 +173,13 @@ void main() {
|
|||||||
final request = InitiatePaymentsRequest(
|
final request = InitiatePaymentsRequest(
|
||||||
idempotencyKey: 'idem-3',
|
idempotencyKey: 'idem-3',
|
||||||
quoteRef: 'q-2',
|
quoteRef: 'q-2',
|
||||||
metadata: const {'client_payment_ref': 'cp-1'},
|
clientPaymentRef: 'cp-1',
|
||||||
);
|
);
|
||||||
|
|
||||||
final json = request.toJson();
|
final json = request.toJson();
|
||||||
expect(json['idempotencyKey'], equals('idem-3'));
|
expect(json['idempotencyKey'], equals('idem-3'));
|
||||||
expect(json['quoteRef'], equals('q-2'));
|
expect(json['quoteRef'], equals('q-2'));
|
||||||
expect(
|
expect(json['clientPaymentRef'], equals('cp-1'));
|
||||||
(json['metadata'] as Map<String, dynamic>)['client_payment_ref'],
|
|
||||||
equals('cp-1'),
|
|
||||||
);
|
|
||||||
expect(json.containsKey('intentRef'), isFalse);
|
expect(json.containsKey('intentRef'), isFalse);
|
||||||
expect(json.containsKey('intentRefs'), isFalse);
|
expect(json.containsKey('intentRefs'), isFalse);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -107,6 +107,9 @@ components:
|
|||||||
quoteRef:
|
quoteRef:
|
||||||
description: Reference to a previously generated quote to execute.
|
description: Reference to a previously generated quote to execute.
|
||||||
type: string
|
type: string
|
||||||
|
clientPaymentRef:
|
||||||
|
description: Optional caller-side payment correlation reference.
|
||||||
|
type: string
|
||||||
|
|
||||||
InitiatePaymentsRequest:
|
InitiatePaymentsRequest:
|
||||||
description: Request payload to initiate multiple payments from a multi-quote reference.
|
description: Request payload to initiate multiple payments from a multi-quote reference.
|
||||||
@@ -120,6 +123,9 @@ components:
|
|||||||
quoteRef:
|
quoteRef:
|
||||||
description: Reference to a previously generated multi-quote.
|
description: Reference to a previously generated multi-quote.
|
||||||
type: string
|
type: string
|
||||||
|
clientPaymentRef:
|
||||||
|
description: Optional caller-side payment correlation reference.
|
||||||
|
type: string
|
||||||
|
|
||||||
InitiatePaymentByQuoteRequest:
|
InitiatePaymentByQuoteRequest:
|
||||||
description: Request payload to initiate one payment from an existing quote reference.
|
description: Request payload to initiate one payment from an existing quote reference.
|
||||||
@@ -133,3 +139,6 @@ components:
|
|||||||
quoteRef:
|
quoteRef:
|
||||||
description: Reference to a previously generated quote to execute.
|
description: Reference to a previously generated quote to execute.
|
||||||
type: string
|
type: string
|
||||||
|
clientPaymentRef:
|
||||||
|
description: Optional caller-side payment correlation reference.
|
||||||
|
type: string
|
||||||
|
|||||||
Reference in New Issue
Block a user