From 412f26d82f5a8f6d153240d4351f08cb8b31c19e Mon Sep 17 00:00:00 2001 From: Paul Nicolas Date: Wed, 7 Feb 2024 17:08:48 +0100 Subject: [PATCH] fix(payment): missing check for account type (#1202) --- .../internal/api/service/service_test.go | 33 ++++++++++++++++++ .../api/service/transfer_initiation.go | 21 ++++++++++-- .../api/service/transfer_initiation_test.go | 34 +++++++++++++++++++ 3 files changed, 86 insertions(+), 2 deletions(-) diff --git a/components/payments/cmd/connectors/internal/api/service/service_test.go b/components/payments/cmd/connectors/internal/api/service/service_test.go index cd3b2fb3d3..200dc9d8d1 100644 --- a/components/payments/cmd/connectors/internal/api/service/service_test.go +++ b/components/payments/cmd/connectors/internal/api/service/service_test.go @@ -111,6 +111,21 @@ var ( }, }, } + + sourceAccountID = models.AccountID{ + Reference: "acc1", + ConnectorID: connectorDummyPay.ID, + } + + destinationAccountID = models.AccountID{ + Reference: "acc2", + ConnectorID: connectorDummyPay.ID, + } + + destinationExternalAccountID = models.AccountID{ + Reference: "acc3", + ConnectorID: connectorDummyPay.ID, + } ) type MockStore struct { @@ -151,6 +166,24 @@ func (m *MockStore) UpsertAccounts(ctx context.Context, accounts []*models.Accou } func (m *MockStore) GetAccount(ctx context.Context, id string) (*models.Account, error) { + switch id { + case sourceAccountID.String(): + return &models.Account{ + ID: sourceAccountID, + Type: models.AccountTypeInternal, + }, nil + case destinationAccountID.String(): + return &models.Account{ + ID: destinationAccountID, + Type: models.AccountTypeInternal, + }, nil + case destinationExternalAccountID.String(): + return &models.Account{ + ID: destinationAccountID, + Type: models.AccountTypeExternal, + }, nil + } + return nil, nil } diff --git a/components/payments/cmd/connectors/internal/api/service/transfer_initiation.go b/components/payments/cmd/connectors/internal/api/service/transfer_initiation.go index e92ebd6833..e72df5f5f6 100644 --- a/components/payments/cmd/connectors/internal/api/service/transfer_initiation.go +++ b/components/payments/cmd/connectors/internal/api/service/transfer_initiation.go @@ -109,11 +109,28 @@ func (s *Service) CreateTransferInitiation(ctx context.Context, req *CreateTrans } } - _, err := s.store.GetAccount(ctx, req.DestinationAccountID) + destinationAccount, err := s.store.GetAccount(ctx, req.DestinationAccountID) if err != nil { return nil, newStorageError(err, "getting destination account") } + transferType := models.MustTransferInitiationTypeFromString(req.Type) + + switch transferType { + case models.TransferInitiationTypeTransfer: + if destinationAccount.Type != models.AccountTypeInternal { + // account should be internal when doing a transfer, return an error + return nil, errors.Wrap(ErrValidation, "destination account must be internal when doing a transfer") + } + case models.TransferInitiationTypePayout: + switch destinationAccount.Type { + case models.AccountTypeExternal, models.AccountTypeExternalFormance: + default: + // account should be external when doing a payout, return an error + return nil, errors.Wrap(ErrValidation, "destination account must be external when doing a payout") + } + } + id := models.TransferInitiationID{ Reference: req.Reference, ConnectorID: connectorID, @@ -129,7 +146,7 @@ func (s *Service) CreateTransferInitiation(ctx context.Context, req *CreateTrans DestinationAccountID: models.MustAccountIDFromString(req.DestinationAccountID), ConnectorID: connectorID, Provider: connectorID.Provider, - Type: models.MustTransferInitiationTypeFromString(req.Type), + Type: transferType, Amount: req.Amount, InitialAmount: req.Amount, Asset: models.Asset(req.Asset), diff --git a/components/payments/cmd/connectors/internal/api/service/transfer_initiation_test.go b/components/payments/cmd/connectors/internal/api/service/transfer_initiation_test.go index 659526ba8b..943547540d 100644 --- a/components/payments/cmd/connectors/internal/api/service/transfer_initiation_test.go +++ b/components/payments/cmd/connectors/internal/api/service/transfer_initiation_test.go @@ -160,6 +160,40 @@ func TestCreateTransferInitiation(t *testing.T) { }, }, }, + { + name: "transfer with external account as destination", + req: &CreateTransferInitiationRequest{ + Reference: "ref1", + ScheduledAt: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC), + Description: "test", + SourceAccountID: sourceAccountID.String(), + DestinationAccountID: destinationExternalAccountID.String(), + ConnectorID: connectorDummyPay.ID.String(), + Provider: string(models.ConnectorProviderDummyPay), + Type: models.TransferInitiationTypeTransfer.String(), + Amount: big.NewInt(100), + Asset: "EUR/2", + Validated: false, + }, + expectedError: ErrValidation, + }, + { + name: "payout with internal account as destination", + req: &CreateTransferInitiationRequest{ + Reference: "ref1", + ScheduledAt: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC), + Description: "test", + SourceAccountID: sourceAccountID.String(), + DestinationAccountID: destinationAccountID.String(), + ConnectorID: connectorDummyPay.ID.String(), + Provider: string(models.ConnectorProviderDummyPay), + Type: models.TransferInitiationTypePayout.String(), + Amount: big.NewInt(100), + Asset: "EUR/2", + Validated: false, + }, + expectedError: ErrValidation, + }, { name: "invalid connector id", req: &CreateTransferInitiationRequest{