8000 feat(psu): add payment service user entity by paul-nicolas · Pull Request #424 · formancehq/payments · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

feat(psu): add payment service user entity #424

New issue 8000

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Apr 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
682 changes: 682 additions & 0 deletions docs/api/README.md

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions internal/api/backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ type Backend interface {
PaymentInitiationRelatedPaymentsList(ctx context.Context, id models.PaymentInitiationID, query storage.ListPaymentInitiationRelatedPaymentsQuery) (*bunpaginate.Cursor[models.Payment], error)
PaymentInitiationRelatedPaymentsListAll(ctx context.Context, id models.PaymentInitiationID) ([]models.Payment, error)

// Payment Service Users
PaymentServiceUsersCreate(ctx context.Context, psu models.PaymentServiceUser) error
PaymentServiceUsersGet(ctx context.Context, id uuid.UUID) (*models.PaymentServiceUser, error)
PaymentServiceUsersList(ctx context.Context, query storage.ListPSUsQuery) (*bunpaginate.Cursor[models.PaymentServiceUser], error)
PaymentServiceUsersForwardBankAccountToConnector(ctx context.Context, psuID, bankAccountID uuid.UUID, connectorID models.ConnectorID) (models.Task, error)
PaymentServiceUsersAddBankAccount(ctx context.Context, psuID uuid.UUID, bankAccountID uuid.UUID) error

// Pools
PoolsCreate(ctx context.Context, pool models.Pool) error
PoolsGet(ctx context.Context, id uuid.UUID) (*models.Pool, error)
Expand Down
73 changes: 73 additions & 0 deletions internal/api/backend/backend_generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 11 additions & 1 deletion internal/api/services/bank_accounts_forward_to_connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,17 @@
)

func (s *Service) BankAccountsForwardToConnector(ctx context.Context, bankAccountID uuid.UUID, connectorID models.ConnectorID, waitResult bool) (models.Task, error) {
task, err := s.engine.ForwardBankAccount(ctx, bankAccountID, connectorID, waitResult)
ba, err := s.storage.BankAccountsGet(ctx, bankAccountID, true)
if err != nil {
return models.Task{}, newStorageError(err, "failed to get bank account")
}

if ba == nil {
// Should not happen, but just in case
return models.Task{}, newStorageError(nil, "bank account not found")
}

Check warning on line 19 in internal/api/services/bank_accounts_forward_to_connector.go

View check run for this annotation

Codecov / codecov/patch

internal/api/services/bank_accounts_forward_to_connector.go#L17-L19

Added lines #L17 - L19 were not covered by tests

task, err := s.engine.ForwardBankAccount(ctx, *ba, connectorID, waitResult)
if err != nil {
return models.Task{}, handleEngineErrors(err)
}
Expand Down
73 changes: 48 additions & 25 deletions internal/api/services/bank_accounts_forward_to_connector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,46 +29,69 @@ func TestBankAccountsForwardToConnector(t *testing.T) {
}

tests := []struct {
name string
bankAccountID uuid.UUID
err error
expectedError error
typedError bool
name string
bankAccountID uuid.UUID
engineErr error
storageErr error
expectedEngineError error
expectedStorageError error
typedError bool
}{
{
name: "success",
err: nil,
expectedError: nil,
name: "success",
engineErr: nil,
},
{
name: "validation error",
err: engine.ErrValidation,
expectedError: ErrValidation,
typedError: true,
name: "validation error",
engineErr: engine.ErrValidation,
expectedEngineError: ErrValidation,
typedError: true,
},
{
name: "not found error",
err: engine.ErrNotFound,
expectedError: ErrNotFound,
typedError: true,
name: "not found error",
engineErr: engine.ErrNotFound,
expectedEngineError: ErrNotFound,
typedError: true,
},
{
name: "other error",
err: fmt.Errorf("error"),
expectedError: fmt.Errorf("error"),
name: "other error",
engineErr: fmt.Errorf("error"),
expectedEngineError: fmt.Errorf("error"),
},
{
name: "storage error not found",
storageErr: storage.ErrNotFound,
typedError: true,
expectedStorageError: newStorageError(storage.ErrNotFound, "failed to get bank account"),
},
{
name: "other error",
storageErr: fmt.Errorf("error"),
expectedStorageError: newStorageError(fmt.Errorf("error"), "failed to get bank account"),
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
eng.EXPECT().ForwardBankAccount(gomock.Any(), test.bankAccountID, connectorID, false).Return(models.Task{}, test.err)
store.EXPECT().BankAccountsGet(gomock.Any(), test.bankAccountID, true).Return(&models.BankAccount{}, test.storageErr)

if test.storageErr == nil {
eng.EXPECT().ForwardBankAccount(gomock.Any(), models.BankAccount{}, connectorID, false).Return(models.Task{}, test.engineErr)
}
_, err := s.BankAccountsForwardToConnector(context.Background(), test.bankAccountID, connectorID, false)
if test.expectedError == nil {
switch {
case test.expectedEngineError != nil && test.typedError:
require.ErrorIs(t, err, test.expectedEngineError)
case test.expectedEngineError != nil && !test.typedError:
require.Error(t, err)
require.Equal(t, test.expectedEngineError.Error(), err.Error())
case test.expectedStorageError != nil && test.typedError:
require.ErrorIs(t, err, test.expectedStorageError)
case test.expectedStorageError != nil && !test.typedError:
require.Error(t, err)
require.Equal(t, test.expectedStorageError.Error(), err.Error())
default:
require.NoError(t, err)
} else if test.typedError {
require.ErrorIs(t, err, test.expectedError)
} else {
require.Equal(t, test.expectedError, err)
}
})
}
Expand Down
11 changes: 11 additions & 0 deletions internal/api/services/payment_service_users_add_bank_account.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package services

import (
"context"

"github.com/google/uuid"
)

func (s *Service) PaymentServiceUsersAddBankAccount(ctx context.Context, psuID uuid.UUID, bankAccountID uuid.UUID) error {
return newStorageError(s.storage.PaymentServiceUsersAddBankAccount(ctx, psuID, bankAccountID), "failed to add bank account to payment service user")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package services

import (
"context"
"fmt"
"testing"

"github.com/formancehq/payments/internal/connectors/engine"
"github.com/formancehq/payments/internal/storage"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
gomock "go.uber.org/mock/gomock"
)

func TestPaymentServiceUsersAddBankAccount(t *testing.T) {
t.Parallel()

ctrl := gomock.NewController(t)
defer ctrl.Finish()
store := storage.NewMockStorage(ctrl)
eng := engine.NewMockEngine(ctrl)

s := New(store, eng, false)

tests := []struct {
name string
err error
expectedError error
}{
{
name: "success",
err: nil,
expectedError: nil,
},
{
name: "storage error not found",
err: storage.ErrNotFound,
expectedError: newStorageError(storage.ErrNotFound, "failed to add bank account to payment service user"),
},
{
name: "other error",
err: fmt.Errorf("error"),
expectedError: newStorageError(fmt.Errorf("error"), "failed to add bank account to payment service user"),
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
psuID, baID := uuid.New(), uuid.New()
store.EXPECT().PaymentServiceUsersAddBankAccount(gomock.Any(), psuID, baID).Return(test.err)
err := s.PaymentServiceUsersAddBankAccount(context.Background(), psuID, baID)
if test.expectedError == nil {
require.NoError(t, err)
} else {
require.Equal(t, test.expectedError, err)
}
})
}
}
11 changes: 11 additions & 0 deletions internal/api/services/payment_service_users_create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package services

import (
"context"

"github.com/formancehq/payments/internal/models"
)

func (s *Service) PaymentServiceUsersCreate(ctx context.Context, psu models.PaymentServiceUser) error {
return newStorageError(s.storage.PaymentServiceUsersCreate(ctx, psu), "cannot create payment service user")
}
58 changes: 58 additions & 0 deletions internal/api/services/payment_service_users_create_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package services

import (
"context"
"fmt"
"testing"

"github.com/formancehq/payments/internal/connectors/engine"
"github.com/formancehq/payments/internal/models"
"github.com/formancehq/payments/internal/storage"
"github.com/stretchr/testify/require"
gomock "go.uber.org/mock/gomock"
)

func TestPSUCreate(t *testing.T) {
t.Parallel()

ctrl := gomock.NewController(t)
defer ctrl.Finish()
store := storage.NewMockStorage(ctrl)
eng := engine.NewMockEngine(ctrl)

s := New(store, eng, false)

tests := []struct {
name string
err error
expectedError error
}{
{
name: "success",
err: nil,
expectedError: nil,
},
{
name: "storage error not found",
err: storage.ErrNotFound,
expectedError: newStorageError(storage.ErrNotFound, "cannot create payment service user"),
},
{
name: "other error",
err: fmt.Errorf("error"),
expectedError: newStorageError(fmt.Errorf("error"), "cannot create payment service user"),
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
store.EXPECT().PaymentServiceUsersCreate(gomock.Any(), models.PaymentServiceUser{}).Return(test.err)
err := s.PaymentServiceUsersCreate(context.Background(), models.PaymentServiceUser{})
if test.expectedError == nil {
require.NoError(t, err)
} else {
require.Equal(t, test.expectedError, err)
}
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package services

import (
"context"

"github.com/formancehq/payments/internal/models"
"github.com/formancehq/payments/internal/storage"
"github.com/google/uuid"
)

func (s *Service) PaymentServiceUsersForwardBankAccountToConnector(ctx context.Context, psuID, bankAccountID uuid.UUID, connectorID models.ConnectorID) (models.Task, error) {
ba, err := s.storage.BankAccountsGet(ctx, bankAccountID, true)
if err != nil {
return models.Task{}, newStorageError(err, "failed to get bank account")
}

if ba == nil {
// Should not happen, but just in case
return models.Task{}, newStorageError(storage.ErrNotFound, "bank account not found")
}

Check warning on line 20 in internal/api/services/payment_service_users_forward_bank_account.go

View check run for this annotation

Codecov / codecov/patch

internal/api/services/payment_service_users_forward_bank_account.go#L18-L20

Added lines #L18 - L20 were not covered by tests

psu, err := s.storage.PaymentServiceUsersGet(ctx, psuID)
if err != nil {
return models.Task{}, newStorageError(err, "failed to get payment service user")
}

models.FillBankAccountMetadataWithPaymentServiceUserInfo(ba, psu)

task, err := s.engine.ForwardBankAccount(ctx, *ba, connectorID, false)
if err != nil {
return models.Task{}, handleEngineErrors(err)
}

return task, nil
}
Loading
Loading
0