Skip to content

Commit

Permalink
feat(payments): replace http.Client with httpwrapper.Client in bankin…
Browse files Browse the repository at this point in the history
…gcircle
  • Loading branch information
laouji committed Sep 19, 2024
1 parent db7174a commit 876375d
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 172 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"time"

"github.com/formancehq/payments/internal/connectors/httpwrapper"
)

type Account struct {
Expand Down Expand Up @@ -58,24 +59,6 @@ func (c *Client) GetAccounts(ctx context.Context, page int, pageSize int, fromOp

req.Header.Set("Authorization", "Bearer "+c.accessToken)

resp, err := c.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to get accounts: %w", err)
}

defer func() {
err = resp.Body.Close()
if err != nil {
_ = err
// TODO(polo): log error
}
}()

responseBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read accounts response body: %w", err)
}

type response struct {
Result []Account `json:"result"`
PageInfo struct {
Expand All @@ -84,13 +67,16 @@ func (c *Client) GetAccounts(ctx context.Context, page int, pageSize int, fromOp
} `json:"pageInfo"`
}

var res response

if err = json.Unmarshal(responseBody, &res); err != nil {
return nil, fmt.Errorf("failed to unmarshal accounts response: %w", err)
res := response{Result: make([]Account, 0)}
statusCode, err := c.httpClient.Do(req, &res, nil)
switch err {
case nil:
return res.Result, nil
case httpwrapper.ErrStatusCodeUnexpected:
// TODO(polo): retryable errors
return nil, fmt.Errorf("received status code %d for get accounts", statusCode)
}

return res.Result, nil
return nil, fmt.Errorf("failed to get accounts: %w", err)
}

func (c *Client) GetAccount(ctx context.Context, accountID string) (*Account, error) {
Expand All @@ -109,27 +95,14 @@ func (c *Client) GetAccount(ctx context.Context, accountID string) (*Account, er
}
req.Header.Set("Authorization", "Bearer "+c.accessToken)

resp, err := c.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to get accounts: %w", err)
}

defer func() {
err = resp.Body.Close()
if err != nil {
_ = err
// TODO(polo): log error
}
}()

if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("wrong status code: %d", resp.StatusCode)
}

var account Account
if err := json.NewDecoder(resp.Body).Decode(&account); err != nil {
return nil, fmt.Errorf("failed to decode account response: %w", err)
statusCode, err := c.httpClient.Do(req, &account, nil)
switch err {
case nil:
return &account, nil
case httpwrapper.ErrStatusCodeUnexpected:
// TODO(polo): retryable errors
return nil, fmt.Errorf("received status code %d for get account", statusCode)
}

return &account, nil
return nil, fmt.Errorf("failed to get account: %w", err)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ package client

import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"strconv"
"time"

"github.com/formancehq/payments/internal/connectors/httpwrapper"
)

func (c *Client) login(ctx context.Context) error {
Expand All @@ -24,60 +24,36 @@ func (c *Client) login(ctx context.Context) error {

req.SetBasicAuth(c.username, c.password)

resp, err := c.httpClient.Do(req)
if err != nil {
return fmt.Errorf("failed to login: %w", err)
}

defer func() {
err = resp.Body.Close()
if err != nil {
_ = err
// TODO(polo): log error
}
}()

responseBody, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("failed to read login response body: %w", err)
}

if resp.StatusCode != http.StatusOK {
type responseError struct {
ErrorCode string `json:"errorCode"`
ErrorText string `json:"errorText"`
}
var errors []responseError
if err = json.Unmarshal(responseBody, &errors); err != nil {
return fmt.Errorf("failed to unmarshal login response: %w", err)
}
if len(errors) > 0 {
return fmt.Errorf("failed to login: %s %s", errors[0].ErrorCode, errors[0].ErrorText)
}
return fmt.Errorf("failed to login: %s", resp.Status)
}

//nolint:tagliatelle // allow for client-side structures
type response struct {
AccessToken string `json:"access_token"`
ExpiresIn string `json:"expires_in"`
}
type responseError struct {
ErrorCode string `json:"errorCode"`
ErrorText string `json:"errorText"`
}

var res response

if err = json.Unmarshal(responseBody, &res); err != nil {
return fmt.Errorf("failed to unmarshal login response: %w", err)
var errors []responseError
statusCode, err := c.httpClient.Do(req, &res, &errors)
switch err {
case nil:
// fallthrough
case httpwrapper.ErrStatusCodeUnexpected:
if len(errors) > 0 {
return fmt.Errorf("failed to login: %s %s", errors[0].ErrorCode, errors[0].ErrorText)
}
return fmt.Errorf("failed to login: %s", statusCode)
}
return fmt.Errorf("failed make login request: %w", err)

c.accessToken = res.AccessToken

expiresIn, err := strconv.Atoi(res.ExpiresIn)
if err != nil {
return fmt.Errorf("failed to convert expires_in to int: %w", err)
}

c.accessTokenExpiresAt = time.Now().Add(time.Duration(expiresIn) * time.Second)

return nil
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import (
"net/http"
"time"

"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"github.com/formancehq/payments/internal/connectors/httpwrapper"
)

type Client struct {
httpClient *http.Client
httpClient httpwrapper.Client

username string
password string
Expand All @@ -21,8 +21,12 @@ type Client struct {
accessTokenExpiresAt time.Time
}

func newHTTPClient(userCertificate, userCertificateKey string) (*http.Client, error) {
cert, err := tls.X509KeyPair([]byte(userCertificate), []byte(userCertificateKey))
func New(
username, password,
endpoint, authorizationEndpoint,
uCertificate, uCertificateKey string,
) (*Client, error) {
cert, err := tls.X509KeyPair([]byte(uCertificate), []byte(uCertificateKey))
if err != nil {
return nil, err
}
Expand All @@ -32,18 +36,10 @@ func newHTTPClient(userCertificate, userCertificateKey string) (*http.Client, er
Certificates: []tls.Certificate{cert},
}

return &http.Client{
Timeout: 10 * time.Second,
Transport: otelhttp.NewTransport(tr),
}, nil
}

func New(
username, password,
endpoint, authorizationEndpoint,
uCertificate, uCertificateKey string,
) (*Client, error) {
httpClient, err := newHTTPClient(uCertificate, uCertificateKey)
config := &httpwrapper.Config{
Transport: tr,
}
httpClient, err := httpwrapper.NewClient(config)
if err != nil {
return nil, err
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"time"

"github.com/formancehq/payments/internal/connectors/httpwrapper"
)

//nolint:tagliatelle // allow for client-side structures
Expand Down Expand Up @@ -102,24 +103,6 @@ func (c *Client) GetPayments(ctx context.Context, page int, pageSize int) ([]Pay

req.Header.Set("Authorization", "Bearer "+c.accessToken)

resp, err := c.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to get payments: %w", err)
}

defer func() {
err = resp.Body.Close()
if err != nil {
_ = err
// TODO(polo): log error
}
}()

responseBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read payments response body: %w", err)
}

type response struct {
Result []Payment `json:"result"`
PageInfo struct {
Expand All @@ -128,13 +111,16 @@ func (c *Client) GetPayments(ctx context.Context, page int, pageSize int) ([]Pay
} `json:"pageInfo"`
}

var res response

if err = json.Unmarshal(responseBody, &res); err != nil {
return nil, fmt.Errorf("failed to unmarshal payments response: %w", err)
res := response{Result: make([]Payment, 0)}
statusCode, err := c.httpClient.Do(req, &res, nil)
switch err {
case nil:
return res.Result, nil
case httpwrapper.ErrStatusCodeUnexpected:
// TODO(polo): retryable errors
return nil, fmt.Errorf("received status code %d for get payments", statusCode)
}

return res.Result, nil
return nil, fmt.Errorf("failed to get payments: %w", err)
}

type StatusResponse struct {
Expand All @@ -157,28 +143,14 @@ func (c *Client) GetPaymentStatus(ctx context.Context, paymentID string) (*Statu
}
req.Header.Set("Authorization", "Bearer "+c.accessToken)

resp, err := c.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to get payments: %w", err)
}

defer func() {
err = resp.Body.Close()
if err != nil {
_ = err
// TODO(polo): log error
}
}()

responseBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read payments response body: %w", err)
}

var res StatusResponse
if err = json.Unmarshal(responseBody, &res); err != nil {
return nil, fmt.Errorf("failed to unmarshal payments response: %w", err)
statusCode, err := c.httpClient.Do(req, &res, nil)
switch err {
case nil:
return &res, nil
case httpwrapper.ErrStatusCodeUnexpected:
// TODO(polo): retryable errors
return nil, fmt.Errorf("received status code %d for get payment status", statusCode)
}

return &res, nil
return nil, fmt.Errorf("failed to get payments status: %w", err)
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"fmt"
"net/http"
"time"

"github.com/formancehq/payments/internal/connectors/httpwrapper"
)

type PaymentAccount struct {
Expand Down Expand Up @@ -56,27 +58,14 @@ func (c *Client) InitiateTransferOrPayouts(ctx context.Context, transferRequest
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+c.accessToken)

resp, err := c.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to make transfer: %w", err)
}

defer func() {
err = resp.Body.Close()
if err != nil {
_ = err
// TODO(polo): log error
}
}()

if resp.StatusCode != http.StatusCreated {
return nil, fmt.Errorf("failed to make transfer: %w", err)
}

var transferResponse PaymentResponse
if err := json.NewDecoder(resp.Body).Decode(&transferResponse); err != nil {
return nil, fmt.Errorf("failed to unmarshal wallets response body: %w", err)
var res PaymentResponse
statusCode, err := c.httpClient.Do(req, &res, nil)
switch err {
case nil:
return &res, nil
case httpwrapper.ErrStatusCodeUnexpected:
// TODO(polo): retryable errors
return nil, fmt.Errorf("received status code %d for make payout", statusCode)
}

return &transferResponse, nil
return nil, fmt.Errorf("failed to make payout: %w", err)
}

0 comments on commit 876375d

Please sign in to comment.