Skip to content

Commit

Permalink
feat(payments): link bank accounts to payment accounts (#626)
Browse files Browse the repository at this point in the history
  • Loading branch information
paul-nicolas authored Oct 11, 2023
1 parent da71782 commit f74735f
Show file tree
Hide file tree
Showing 9 changed files with 76 additions and 28 deletions.
20 changes: 16 additions & 4 deletions components/payments/internal/app/api/bank_account.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type bankAccountResponse struct {
CreatedAt time.Time `json:"createdAt"`
Country string `json:"country"`
Provider string `json:"provider"`
AccountID string `json:"accountId,omitempty"`
Iban string `json:"iban,omitempty"`
AccountNumber string `json:"accountNumber,omitempty"`
SwiftBicCode string `json:"swiftBicCode,omitempty"`
Expand Down Expand Up @@ -90,6 +91,7 @@ func listBankAccountsHandler(repo bankAccountsRepository) http.HandlerFunc {
CreatedAt: ret[i].CreatedAt,
Country: ret[i].Country,
Provider: ret[i].Provider.String(),
AccountID: ret[i].AccountID.String(),
}
}

Expand Down Expand Up @@ -143,6 +145,7 @@ func readBankAccountHandler(repo readBankAccountRepository) http.HandlerFunc {
CreatedAt: account.CreatedAt,
Country: account.Country,
Provider: account.Provider.String(),
AccountID: account.AccountID.String(),
Iban: account.IBAN,
AccountNumber: account.AccountNumber,
SwiftBicCode: account.SwiftBicCode,
Expand All @@ -163,6 +166,7 @@ func readBankAccountHandler(repo readBankAccountRepository) http.HandlerFunc {
type createBankAccountRepository interface {
UpsertAccounts(ctx context.Context, provider models.ConnectorProvider, accounts []*models.Account) error
CreateBankAccount(ctx context.Context, account *models.BankAccount) error
LinkBankAccountWithAccount(ctx context.Context, id uuid.UUID, accountID *models.AccountID) error
IsInstalled(ctx context.Context, provider models.ConnectorProvider) (bool, error)
}

Expand Down Expand Up @@ -248,12 +252,13 @@ func createBankAccountHandler(repo createBankAccountRepository) http.HandlerFunc
// BankingCircle does not have external accounts so we need to create
// one by hand
if provider == models.ConnectorProviderBankingCircle {
accountID := models.AccountID{
Reference: bankAccount.ID.String(),
Provider: provider,
}
err = repo.UpsertAccounts(r.Context(), provider, []*models.Account{
{
ID: models.AccountID{
Reference: bankAccount.ID.String(),
Provider: provider,
},
ID: accountID,
CreatedAt: time.Now(),
Reference: bankAccount.ID.String(),
Provider: provider,
Expand All @@ -266,6 +271,13 @@ func createBankAccountHandler(repo createBankAccountRepository) http.HandlerFunc

return
}

err = repo.LinkBankAccountWithAccount(r.Context(), bankAccount.ID, &accountID)
if err != nil {
handleStorageErrors(w, r, err)

return
}
}

data := &bankAccountResponse{
Expand Down
12 changes: 6 additions & 6 deletions components/payments/internal/app/api/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,12 @@ func httpRouter(
for _, h := range connectorHandlers {
paymentsHandlers[h.Provider] = h.initiatePayment
}
authGroup.Path("/transfer-initiation").Methods(http.MethodPost).Handler(createTransferInitiationHandler(store, paymentsHandlers))
authGroup.Path("/transfer-initiation").Methods(http.MethodGet).Handler(listTransferInitiationsHandler(store))
authGroup.Path("/transfer-initiation/{transferID}/status").Methods(http.MethodPost).Handler(updateTransferInitiationStatusHandler(store, paymentsHandlers))
authGroup.Path("/transfer-initiation/{transferID}/retry").Methods(http.MethodPost).Handler(retryTransferInitiationHandler(store, paymentsHandlers))
authGroup.Path("/transfer-initiation/{transferID}").Methods(http.MethodGet).Handler(readTransferInitiationHandler(store))
authGroup.Path("/transfer-initiation/{transferID}").Methods(http.MethodDelete).Handler(deleteTransferInitiationHandler(store))
authGroup.Path("/transfer-initiations").Methods(http.MethodPost).Handler(createTransferInitiationHandler(store, paymentsHandlers))
authGroup.Path("/transfer-initiations").Methods(http.MethodGet).Handler(listTransferInitiationsHandler(store))
authGroup.Path("/transfer-initiations/{transferID}/status").Methods(http.MethodPost).Handler(updateTransferInitiationStatusHandler(store, paymentsHandlers))
authGroup.Path("/transfer-initiations/{transferID}/retry").Methods(http.MethodPost).Handler(retryTransferInitiationHandler(store, paymentsHandlers))
authGroup.Path("/transfer-initiations/{transferID}").Methods(http.MethodGet).Handler(readTransferInitiationHandler(store))
authGroup.Path("/transfer-initiations/{transferID}").Methods(http.MethodDelete).Handler(deleteTransferInitiationHandler(store))

authGroup.HandleFunc("/connectors", readConnectorsHandler(store))

Expand Down
4 changes: 2 additions & 2 deletions components/payments/internal/app/models/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ type AccountID struct {
Provider ConnectorProvider
}

func (aid AccountID) String() string {
if aid.Reference == "" {
func (aid *AccountID) String() string {
if aid == nil || aid.Reference == "" {
return ""
}

Expand Down
2 changes: 2 additions & 0 deletions components/payments/internal/app/models/bank_account.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ type BankAccount struct {
IBAN string `bun:"decrypted_iban,scanonly"`
SwiftBicCode string `bun:"decrypted_swift_bic_code,scanonly"`
Country string `bun:"country"`

AccountID *AccountID
}

func (a *BankAccount) Offuscate() error {
Expand Down
18 changes: 16 additions & 2 deletions components/payments/internal/app/storage/bank_accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ func (s *Storage) CreateBankAccount(ctx context.Context, bankAccount *models.Ban
Country: bankAccount.Country,
Provider: bankAccount.Provider,
Name: bankAccount.Name,
AccountID: bankAccount.AccountID,
}

var id uuid.UUID
Expand Down Expand Up @@ -42,11 +43,24 @@ func (s *Storage) updateBankAccountInformation(ctx context.Context, id uuid.UUID
return nil
}

func (s *Storage) LinkBankAccountWithAccount(ctx context.Context, id uuid.UUID, accountID *models.AccountID) error {
_, err := s.db.NewUpdate().
Model(&models.BankAccount{}).
Set("account_id = ?", accountID).
Where("id = ?", id).
Exec(ctx)
if err != nil {
return e("update bank account information", err)
}

return nil
}

func (s *Storage) ListBankAccounts(ctx context.Context, pagination PaginatorQuery) ([]*models.BankAccount, PaginationDetails, error) {
var bankAccounts []*models.BankAccount

query := s.db.NewSelect().
Column("id", "name", "created_at", "country", "provider").
Column("id", "name", "created_at", "country", "provider", "account_id").
Model(&bankAccounts)

query = pagination.apply(query, "bank_account.created_at")
Expand Down Expand Up @@ -98,7 +112,7 @@ func (s *Storage) GetBankAccount(ctx context.Context, id uuid.UUID, expand bool)
var account models.BankAccount
query := s.db.NewSelect().
Model(&account).
Column("id", "name", "created_at", "country", "provider")
Column("id", "name", "created_at", "country", "provider", "account_id")

if expand {
query = query.ColumnExpr("pgp_sym_decrypt(account_number, ?, ?) AS decrypted_account_number", s.configEncryptionKey, encryptionOptions).
Expand Down
20 changes: 20 additions & 0 deletions components/payments/internal/app/storage/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,26 @@ func registerMigrations(migrator *migrations.Migrator) {
return err
}

return nil
},
},
migrations.Migration{
Up: func(tx bun.Tx) error {
_, err := tx.Exec(`
ALTER TABLE accounts.bank_account ADD COLUMN account_id CHARACTER VARYING;
ALTER TABLE accounts.bank_account ADD CONSTRAINT bank_account_account_id
FOREIGN KEY (account_id)
REFERENCES accounts.account (id)
ON DELETE CASCADE
NOT DEFERRABLE
INITIALLY IMMEDIATE
;
`)
if err != nil {
return err
}

return nil
},
},
Expand Down
8 changes: 4 additions & 4 deletions components/payments/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ paths:
responses:
'204':
$ref: '#/components/responses/NoContent'
/transfer-initiation:
/transfer-initiations:
get:
summary: List Transfer Initiations
operationId: listTransferInitiations
Expand All @@ -76,7 +76,7 @@ paths:
responses:
'200':
$ref: '#/components/responses/TransferInitiation'
/transfer-initiation/{transferId}:
/transfer-initiations/{transferId}:
get:
summary: Get a transfer initiation
tags:
Expand All @@ -98,7 +98,7 @@ paths:
responses:
'204':
$ref: '#/components/responses/NoContent'
/transfer-initiation/{transferId}/status:
/transfer-initiations/{transferId}/status:
post:
summary: Update the status of a transfer initiation
tags:
Expand All @@ -112,7 +112,7 @@ paths:
responses:
'204':
$ref: '#/components/responses/NoContent'
/transfer-initiation/{transferId}/retry:
/transfer-initiations/{transferId}/retry:
post:
summary: Retry a failed transfer initiation
tags:
Expand Down
10 changes: 5 additions & 5 deletions docs/openapi/v2.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"url": "https://avatars.githubusercontent.com/u/84325077?s=200&v=4",
"altText": "Formance"
},
"version": "v1.0.20231010"
"version": "v1.0.20231011"
},
"servers": [
{
Expand Down Expand Up @@ -1823,7 +1823,7 @@
}
}
},
"/api/payments/transfer-initiation": {
"/api/payments/transfer-initiations": {
"get": {
"summary": "List Transfer Initiations",
"operationId": "listTransferInitiations",
Expand Down Expand Up @@ -1867,7 +1867,7 @@
}
}
},
"/api/payments/transfer-initiation/{transferId}": {
"/api/payments/transfer-initiations/{transferId}": {
"get": {
"summary": "Get a transfer initiation",
"tags": [
Expand Down Expand Up @@ -1904,7 +1904,7 @@
}
}
},
"/api/payments/transfer-initiation/{transferId}/status": {
"/api/payments/transfer-initiations/{transferId}/status": {
"post": {
"summary": "Update the status of a transfer initiation",
"tags": [
Expand All @@ -1927,7 +1927,7 @@
}
}
},
"/api/payments/transfer-initiation/{transferId}/retry": {
"/api/payments/transfer-initiations/{transferId}/retry": {
"post": {
"summary": "Retry a failed transfer initiation",
"tags": [
Expand Down
10 changes: 5 additions & 5 deletions openapi/build/generate.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"url": "https://avatars.githubusercontent.com/u/84325077?s=200&v=4",
"altText": "Formance"
},
"version": "v1.0.20231010"
"version": "v1.0.20231011"
},
"servers": [
{
Expand Down Expand Up @@ -1823,7 +1823,7 @@
}
}
},
"/api/payments/transfer-initiation": {
"/api/payments/transfer-initiations": {
"get": {
"summary": "List Transfer Initiations",
"operationId": "listTransferInitiations",
Expand Down Expand Up @@ -1867,7 +1867,7 @@
}
}
},
"/api/payments/transfer-initiation/{transferId}": {
"/api/payments/transfer-initiations/{transferId}": {
"get": {
"summary": "Get a transfer initiation",
"tags": [
Expand Down Expand Up @@ -1904,7 +1904,7 @@
}
}
},
"/api/payments/transfer-initiation/{transferId}/status": {
"/api/payments/transfer-initiations/{transferId}/status": {
"post": {
"summary": "Update the status of a transfer initiation",
"tags": [
Expand All @@ -1927,7 +1927,7 @@
}
}
},
"/api/payments/transfer-initiation/{transferId}/retry": {
"/api/payments/transfer-initiations/{transferId}/retry": {
"post": {
"summary": "Retry a failed transfer initiation",
"tags": [
Expand Down

1 comment on commit f74735f

@vercel
Copy link

@vercel vercel bot commented on f74735f Oct 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.