corpbankclient

package module
v0.0.0-...-2f18736 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 17, 2022 License: Apache-2.0 Imports: 20 Imported by: 0

README

Example to handle webhook notifications:

package main

import (
	"context"
	"errors"
	"log"
	"net/http"

	"github.com/birapi/go-corpbankclient"
)

func main() {
	client, err := corpbankclient.NewClient(corpbankclient.Credentials{
		APIKeyID:     "<API_KEY_ID>",
		APIKeySecret: "<API_KEY_SECRET>",
	}, nil)

	if err != nil {
		log.Fatal(err)
	}

	http.HandleFunc("/bank-transfers", client.WebhookHandler(func(c context.Context, t corpbankclient.Transaction) error {
		switch t.Direction {
		case corpbankclient.TrxDirectionIncoming:
			log.Printf("Incoming bank transfer from %s: %s %s", t.Sender.Name, t.Amount.StringFixed(2), t.Currency)

		case corpbankclient.TrxDirectionOutgoing:
			log.Printf("Outgoing bank transfer to %s: %s %s", t.Recipient.Name, t.Amount.StringFixed(2), t.Currency)

		default:
			return errors.New("unknown direction")
		}

		return nil
	}))

	log.Fatal(http.ListenAndServe(":8080", nil))
}

Example to make payment:

package main

import (
	"context"
	"log"
	"errors"

	"github.com/birapi/go-corpbankclient"
	"github.com/google/uuid"
	"github.com/shopspring/decimal"
)

func main() {
	client, err := corpbankclient.NewClient(corpbankclient.Credentials{
		APIKeyID:     "<API_KEY_ID>",
		APIKeySecret: "<API_KEY_SECRET>",
	}, nil)

	if err != nil {
		log.Fatal(err)
	}

	ctx := context.Background()

	paymentResult, err := client.MakePayment(ctx, corpbankclient.PaymentOrder{
		SenderIBAN:           "<SENDER_BANK_ACCOUNT_IBAN>",
		RecipientIBAN:        "<RECIPIENT_BANK_ACCOUNT_IBAN>",
		RecipientName:        "<RECIPIENT_NAME>",
		RecipientIdentityNum: "<RECIPIENT_NATIONAL_ID_NUMBER>",
		TransferAmount:       decimal.NewFromInt(3), // transfer amount
		RefCode:              uuid.New().String(),   // a unique reference code
		Description:          "test",                // the description of the bank transfer
	})

	if err != nil {
		// the error type can be checked as follows
		if errors.Is(err, corpbankclient.ErrInsufficientBalance) {
			log.Fatal("insufficient balance")
		}

		log.Fatal(err)
	}

	log.Printf("Payment ID: %s", paymentResult.PaymentID)
}

Example to list of transactions:

package main

import (
	"context"
	"errors"
	"log"
	"time"

	"github.com/birapi/go-corpbankclient"
)

func main() {
	client, err := corpbankclient.NewClient(corpbankclient.Credentials{
		APIKeyID:     "<API_KEY_ID>",
		APIKeySecret: "<API_KEY_SECRET>",
	}, nil)

	if err != nil {
		log.Fatal(err)
	}

	ctx := context.Background()

    // list the last 10 incoming transfers in the last 24 hours
	_, txList, err := client.Transactions(ctx,
		corpbankclient.WithPageSize(10),
		corpbankclient.WithFilterInDateRange(time.Now().Add(-24*time.Hour), time.Now()),
		corpbankclient.WithFilterIncomingTransactions(),
	)

	if err != nil {
		log.Fatal(err)
	}

	for _, t := range txList {
		switch t.Direction {
		case corpbankclient.TrxDirectionIncoming:
			log.Printf("Incoming bank transfer from %s at %s: %s %s", t.Sender.Name, t.Date.String(), t.Amount.StringFixed(2), t.Currency)

		case corpbankclient.TrxDirectionOutgoing:
			log.Printf("Outgoing bank transfer to %s at %s: %s %s", t.Recipient.Name, t.Date.String(), t.Amount.StringFixed(2), t.Currency)

		default:
			log.Fatal(errors.New("unknown direction"))
		}
	}
}

Examples for the rest of the functionality:

package main

import (
	"context"
	"log"

	"github.com/birapi/go-corpbankclient"
	"github.com/google/uuid"
)

func main() {
	client, err := corpbankclient.NewClient(corpbankclient.Credentials{
		APIKeyID:     "<API_KEY_ID>",
		APIKeySecret: "<API_KEY_SECRET>",
	}, nil)

	if err != nil {
		log.Fatal(err)
	}

	ctx := context.Background()

	user, err := client.Me(ctx)
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("Authenticated API user: %s", user.Email)

	balance, err := client.AccountBalance(ctx, uuid.MustParse("<BANK_ACCOUNT_ID>"))
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("Account balance: %s", balance.Balance.StringFixed(2))

	_, apiKeys, err := client.APIKeys(ctx, corpbankclient.WithPageSize(100))
	if err != nil {
		log.Fatal(err)
	}

	for i, k := range apiKeys {
		log.Printf("API Key #%d: %s", i, k.ID)
	}

	// besides, the following functions can help to manage API keys
	// client.NewAPIKey()
	// client.EnableAPIKey()
	// client.DisableAPIKey()
	// client.DelAPIKey()
}

Example to make payment with a retry mechanism, using idempotency feature:

package main

import (
	"context"
	"errors"
	"log"
	"time"

	"github.com/birapi/go-corpbankclient"
	"github.com/cenkalti/backoff"
	"github.com/google/uuid"
	"github.com/shopspring/decimal"
)

func main() {
	client, err := corpbankclient.NewClient(corpbankclient.Credentials{
		APIKeyID:     "<API_KEY_ID>",
		APIKeySecret: "<API_KEY_SECRET>",
	}, nil)

	if err != nil {
		log.Fatal(err)
	}

	ctx := context.Background()

	retry := backoff.NewExponentialBackOff()
	retry.InitialInterval = 200 * time.Millisecond
	retry.MaxInterval = 30 * time.Second
	retry.MaxElapsedTime = 3 * time.Minute

	var paymentResult *corpbankclient.PaymentResult

	// following errors can not be recovered
	permanentErrors := []error{
		corpbankclient.ErrInsufficientBalance,
		corpbankclient.ErrCurrencyMismatch,
		corpbankclient.ErrIncorrectRecipientData,
		corpbankclient.ErrInvalidRecipientID,
		corpbankclient.ErrOutOfEFTHours,
	}

	idempotencyKey, err := uuid.NewRandom()
	if err != nil {
		log.Fatal(err)
	}

	err = backoff.RetryNotify(
		func() error {
			paymentResult, err = client.MakePayment(ctx, corpbankclient.PaymentOrder{
				SenderIBAN:           "<SENDER_BANK_ACCOUNT_IBAN>",
				RecipientIBAN:        "<RECIPIENT_BANK_ACCOUNT_IBAN>",
				RecipientName:        "<RECIPIENT_NAME>",
				RecipientIdentityNum: "<RECIPIENT_NATIONAL_ID_NUMBER>",
				TransferAmount:       decimal.NewFromInt(3), // transfer amount
				RefCode:              uuid.New().String(),   // a unique reference code
				Description:          "test",                // the description of the bank transfer
				IdempotencyKey:       idempotencyKey.String(),
			})

			for _, e := range permanentErrors {
				if errors.Is(err, e) {
					return backoff.Permanent(err)
				}
			}

			return err
		},

		backoff.WithContext(retry, ctx),

		func(e error, d time.Duration) {
			log.Printf("payment error (will retry after %s): %+v", d.String(), err)
		})

	if err != nil {
		// the error type can be checked as follows
		if errors.Is(err, corpbankclient.ErrInsufficientBalance) {
			log.Fatal("insufficient balance")
		}

		log.Fatal(err)
	}

	log.Printf("Payment ID: %s", paymentResult.PaymentID)
}

Documentation

Index

Constants

View Source
const (
	AuthUserStatusActive       AuthUserStatus = "ACTIVE"
	AuthUserStatusSuspended    AuthUserStatus = "SUSPENDED"
	AuthUserStatusPaused       AuthUserStatus = "PAUSED"
	AuthUserStatusNotActivated AuthUserStatus = "WAITING_FOR_ACTIVATION"

	TrxDirectionIncoming TrxDirection = "INCOMING"
	TrxDirectionOutgoing TrxDirection = "OUTGOING"

	TrxTransferMethodHavale TrxTransferMethod = "HAVALE"
	TrxTransferMethodEFT    TrxTransferMethod = "EFT"
	TrxTransferMethodFAST   TrxTransferMethod = "FAST"
)

Variables

View Source
var ErrCurrencyMismatch = errors.New("payment error: currency mismatch")
View Source
var ErrIncorrectRecipientData = errors.New("payment error: incorrect recipient data")
View Source
var ErrInsufficientBalance = errors.New("payment error: insufficient balance")
View Source
var ErrInvalidRecipientID = errors.New("payment error: recipient id")
View Source
var ErrOutOfEFTHours = errors.New("payment error: out of eft hours")

Functions

This section is empty.

Types

type APIErr

type APIErr struct {
	Code    string `json:"code"`
	Message string `json:"message"`
}

func (*APIErr) Error

func (e *APIErr) Error() string

type APIKey

type APIKey struct {
	ID         uuid.UUID  `json:"apiKeyID"`
	CreatedAt  time.Time  `json:"createdAt"`
	ModifiedAt *time.Time `json:"modifiedAt,omitempty"`
	Enabled    bool       `json:"enabled"`
	Secret     *string    `json:"apiKeySecret,omitempty"`
}

type AccountBalance

type AccountBalance struct {
	Balance       decimal.Decimal `json:"balance"`
	LastUpdatedAt time.Time       `json:"lastUpdatedAt"`
}

type AuthUser

type AuthUser struct {
	Email     string         `json:"accountIdentifier"`
	FirstName string         `json:"firstName"`
	LastName  string         `json:"lastName"`
	Status    AuthUserStatus `json:"accountStatus"`
}

type AuthUserStatus

type AuthUserStatus string

type BearerToken

type BearerToken struct {
	APIKeyID  uuid.UUID
	Timestamp time.Time
	Signature []byte
}

func (*BearerToken) Pack

func (t *BearerToken) Pack() (string, error)

func (*BearerToken) Sign

func (t *BearerToken) Sign(apiKeySecret, contentToSign []byte) error

func (*BearerToken) Unpack

func (t *BearerToken) Unpack(packed string) error

func (*BearerToken) Verify

func (t *BearerToken) Verify(apiKeySecret, contentToSign []byte, maxClockSkew time.Duration) error

type Client

type Client struct {
	// contains filtered or unexported fields
}

func NewClient

func NewClient(apiCreds Credentials, clientOpts *ClientOptions) (*Client, error)

func (*Client) APIKeys

func (c *Client) APIKeys(ctx context.Context, options ...RequestOption) (*PageInfo, []APIKey, error)

APIKeys returns the list of API keys.

func (*Client) AccountBalance

func (c *Client) AccountBalance(ctx context.Context, accountID uuid.UUID) (*AccountBalance, error)

AccountBalance returns the balance information for the given account ID.

func (*Client) DelAPIKey

func (c *Client) DelAPIKey(ctx context.Context, apiKeyID uuid.UUID) error

DelAPIKey deletes the API key by the given ID.

func (*Client) DisableAPIKey

func (c *Client) DisableAPIKey(ctx context.Context, apiKeyID uuid.UUID) error

EnableAPIKey de-activate the API key by the given ID.

func (*Client) EnableAPIKey

func (c *Client) EnableAPIKey(ctx context.Context, apiKeyID uuid.UUID) error

EnableAPIKey activate the API key by the given ID.

func (*Client) MakePayment

func (c *Client) MakePayment(ctx context.Context, paymentOrder PaymentOrder) (*PaymentResult, error)

MakePayment sends payment order to the bank and returns the bank response.

func (*Client) Me

func (c *Client) Me(ctx context.Context) (*AuthUser, error)

Me returns the authenticated user details.

func (*Client) NewAPIKey

func (c *Client) NewAPIKey(ctx context.Context) (*APIKey, error)

NewAPIKey creates a new API key and returns with the secret key.

func (*Client) Transactions

func (c *Client) Transactions(ctx context.Context, options ...RequestOption) (*PageInfo, []Transaction, error)

Transactions returns the list of bank transactions. The list can be filtered by the given list of RequestOption.

func (*Client) WebhookHandler

func (c *Client) WebhookHandler(handler WebhookHandler) func(http.ResponseWriter, *http.Request)

type ClientOptions

type ClientOptions struct {
	APIBaseURL  string
	HTTPClient  *http.Client
	MaxTimeDiff time.Duration
}

type Credentials

type Credentials struct {
	APIKeyID     string
	APIKeySecret string
}

type PageInfo

type PageInfo struct {
	CurrentPage  int
	TotalPages   int
	TotalRecords int
}

type PaymentOrder

type PaymentOrder struct {
	IdempotencyKey       string
	SenderIBAN           string
	RecipientIBAN        string
	RecipientName        string
	RecipientIdentityNum string
	TransferAmount       decimal.Decimal
	RefCode              string
	Description          string
}

type PaymentResult

type PaymentResult struct {
	PaymentID uuid.UUID `json:"payment_id"`
}

type RequestOption

type RequestOption interface {
	// contains filtered or unexported methods
}

func WithFilterAccountIDs

func WithFilterAccountIDs(accountID ...uuid.UUID) RequestOption

WithFilterAccountIDs filters the list of bank transactions for the given list of account ID.

func WithFilterInDateRange

func WithFilterInDateRange(startDate, endDate time.Time) RequestOption

WithPageSize filters the list of bank transactions by the given date range

func WithFilterIncomingTransactions

func WithFilterIncomingTransactions() RequestOption

WithFilterIncomingTransactions filters the list of bank transactions for only incoming transfers.

func WithFilterOutgoingTransactions

func WithFilterOutgoingTransactions() RequestOption

WithFilterOutgoingTransactions filters the list of bank transactions for only outgoing transfers.

func WithPageNum

func WithPageNum(pageNum int) RequestOption

WithPageNum allows customizing the requested page number.

func WithPageSize

func WithPageSize(pageSize int) RequestOption

WithPageSize allows customizing the requested page size.

type Transaction

type Transaction struct {
	ID             uuid.UUID               `json:"id"`
	Date           time.Time               `json:"date"`
	Account        TransactionAccount      `json:"account"`
	Amount         decimal.Decimal         `json:"amount"`
	Currency       string                  `json:"currency"`
	Direction      TrxDirection            `json:"direction"`
	Description    string                  `json:"description"`
	ReceivedAt     time.Time               `json:"received_at"`
	RefCode        string                  `json:"reference_code"`
	TransferMethod TrxTransferMethod       `json:"transfer_type"`
	Sender         *TransactionParticipant `json:"sender"`
	Recipient      *TransactionParticipant `json:"recipient"`
	PaymentID      *uuid.UUID              `json:"payment_id"`
}

type TransactionAccount

type TransactionAccount struct {
	BankCode string `json:"bank_code"`
	IBAN     string `json:"iban"`
}

type TransactionParticipant

type TransactionParticipant struct {
	BankCode       string `json:"bank_code"`
	IBAN           string `json:"iban"`
	IdentityNumber string `json:"identity_number"`
	Name           string `json:"name"`
}

type TrxDirection

type TrxDirection string

type TrxTransferMethod

type TrxTransferMethod string

type WebhookHandler

type WebhookHandler func(context.Context, Transaction) error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL