Skip to content
This repository has been archived by the owner on Jul 1, 2024. It is now read-only.

Develop #38

Merged
merged 7 commits into from
Sep 28, 2023
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
2 changes: 1 addition & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func Execute() {

// resolveConfig or exit with error
func resolveConfig() *config.Config {
cfg, err := config.New(Version, Commit, configPath, skipConfig, EmbedFrontend)
cfg, err := config.New(Commit, Version, configPath, skipConfig, EmbedFrontend)
if err != nil {
fmt.Printf("unable to initialize config: %s\n", err.Error())
os.Exit(1)
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ require (
github.com/oxygenpay/tatum-sdk v0.0.0-20230529210116-d986b7743613
github.com/pkg/errors v0.9.1
github.com/robfig/cron/v3 v3.0.1
github.com/rs/zerolog v1.26.1
github.com/rs/zerolog v1.29.0
github.com/rubenv/sql-migrate v1.2.0
github.com/samber/lo v1.37.0
github.com/spf13/cobra v1.6.1
github.com/stretchr/testify v1.8.1
github.com/tidwall/gjson v1.14.4
github.com/wemeetagain/go-hdwallet v0.1.0
github.com/ziflex/lecho/v3 v3.1.0
github.com/ziflex/lecho/v3 v3.5.0
go.etcd.io/bbolt v1.3.6
go.uber.org/atomic v1.10.0
golang.org/x/crypto v0.11.0
Expand Down
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
Expand Down Expand Up @@ -458,6 +459,7 @@ github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
Expand Down Expand Up @@ -541,11 +543,14 @@ github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZV
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/rs/zerolog v1.26.0/go.mod h1:yBiM87lvSqX8h0Ww4sdzNSkVYZ8dL2xjZJG1lAuGZEo=
github.com/rs/zerolog v1.26.1 h1:/ihwxqH+4z8UxyI70wM1z9yCvkWcfz/a3mj48k/Zngc=
github.com/rs/zerolog v1.26.1/go.mod h1:/wSSJWX7lVrsOwlbyTRSOJvqRlc+WjWlfes+CiJ+tmc=
github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w=
github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
github.com/rubenv/sql-migrate v1.2.0 h1:fOXMPLMd41sK7Tg75SXDec15k3zg5WNV6SjuDRiNfcU=
github.com/rubenv/sql-migrate v1.2.0/go.mod h1:Z5uVnq7vrIrPmHbVFfR4YLHRZquxeHpckCnRq0P/K9Y=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
Expand Down Expand Up @@ -636,6 +641,8 @@ github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
github.com/ziflex/lecho/v3 v3.1.0 h1:65bSzSc0yw7EEhi44lMnkOI877ZzbE7tGDWfYCQXZwI=
github.com/ziflex/lecho/v3 v3.1.0/go.mod h1:dwQ6xCAKmSBHhwZ6XmiAiDptD7iklVkW7xQYGUncX0Q=
github.com/ziflex/lecho/v3 v3.5.0 h1:Z4TBr8SbUUnfaVc8tGJf1Jhu0G9Jxjl77lPW0riXKak=
github.com/ziflex/lecho/v3 v3.5.0/go.mod h1:+eInrytYHxVPI6NQbua9xXGerB1x0ujj9jAV33yBIko=
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
Expand Down
15 changes: 15 additions & 0 deletions internal/server/http/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ func WithLogger(logger *zerolog.Logger) Opt {
s.echo.Use(lecho.Middleware(lecho.Config{
Logger: lecho.From(l, lecho.WithLevel(log.INFO)),
RequestIDKey: middleware.RequestIDKey,
Enricher: loggerEnricher,
Skipper: func(c echo.Context) bool {
path := c.Request().URL.Path

Expand All @@ -106,6 +107,20 @@ func WithLogger(logger *zerolog.Logger) Opt {
}
}

func loggerEnricher(c echo.Context, logger zerolog.Context) zerolog.Context {
merchantID := c.Param("merchantId")
if merchantID != "" {
logger = logger.Str("merchant_id", merchantID)
}

paymentID := c.Param("paymentId")
if paymentID != "" {
logger = logger.Str("payment_id", paymentID)
}

return logger.Str("path", c.Path())
}

const healthcheckPath = "/health"

func withHealthcheck(e *echo.Echo) {
Expand Down
5 changes: 5 additions & 0 deletions internal/service/payment/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,11 @@ func (s *Service) Update(ctx context.Context, merchantID, id int64, props Update
return s.entryToPayment(pt)
}

func (s *Service) Fail(ctx context.Context, pt *Payment) error {
_, err := s.Update(ctx, pt.MerchantID, pt.ID, UpdateProps{Status: StatusFailed})
return err
}

func (s *Service) SetWebhookTimestamp(ctx context.Context, merchantID, id int64, sentAt time.Time) error {
err := s.repo.UpdatePaymentWebhookInfo(ctx, repository.UpdatePaymentWebhookInfoParams{
ID: id,
Expand Down
3 changes: 2 additions & 1 deletion internal/service/payment/service_withdrawal.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package payment

import (
"context"
"fmt"
"strconv"
"time"

Expand Down Expand Up @@ -33,7 +34,7 @@ func (s *Service) ListWithdrawals(ctx context.Context, status Status, filterByID
}

if len(filterByIDs) > 0 && len(results) != len(filterByIDs) {
return nil, errors.New("results len mismatch")
return nil, fmt.Errorf("withdrawals filter mismatch for status %q", status)
}

payments := make([]*Payment, len(results))
Expand Down
55 changes: 31 additions & 24 deletions internal/service/processing/service_withdrawal.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,16 @@ func (s *Service) BatchCreateWithdrawals(ctx context.Context, withdrawalIDs []in
return nil, err
}

// 1.Validate payments
if errValidate := s.validateWithdrawals(withdrawals); errValidate != nil {
return nil, errValidate
}

// 2. Get OUTBOUND wallets and balances
// 1. Get OUTBOUND wallets and balances
outboundWallets, outboundBalances, err := s.getOutboundWalletsWithBalancesAsMap(ctx)
if err != nil {
return nil, errors.Wrap(err, "unable to get outbound wallets with balances")
}

result := &TransferResult{}

// 3. For each withdrawal:
// 2. For each withdrawal:
// - Validate
// - Resolve currency
// - Resolve outbound system wallet & balance
// - Resolve merchant balance & withdrawal address
Expand All @@ -47,6 +43,18 @@ func (s *Service) BatchCreateWithdrawals(ctx context.Context, withdrawalIDs []in
for i := range withdrawals {
withdrawal := withdrawals[i]
group.Go(func() error {
// Let's validate each withdrawal individually.
// By doing so, we can reject it without blocking other withdrawals.
if errValidate := validateWithdrawal(withdrawal); errValidate != nil {
if errUpdate := s.payments.Fail(ctx, withdrawal); errUpdate != nil {
result.registerErr(errors.Wrap(errUpdate, "unable to mark invalid withdrawal as failed"))
} else {
result.registerErr(errors.Wrap(errValidate, "withdrawal is invalid, marked as failed"))
}

return nil
}

currency, err := s.blockchain.GetCurrencyByTicker(withdrawal.Price.Ticker())
if err != nil {
result.registerErr(errors.Wrap(err, "unable to get withdrawal currency"))
Expand Down Expand Up @@ -540,27 +548,26 @@ func (s *Service) cancelWithdrawal(
return nil
}

func (s *Service) validateWithdrawals(withdrawals []*payment.Payment) error {
for _, pt := range withdrawals {
if pt.Type != payment.TypeWithdrawal {
return errors.Wrap(ErrInvalidInput, "payment is not withdrawal")
}
func validateWithdrawal(pt *payment.Payment) error {
if pt.Type != payment.TypeWithdrawal {
return errors.Wrap(ErrInvalidInput, "payment is not withdrawal")
}

if pt.Status != payment.StatusPending {
return errors.Wrap(ErrInvalidInput, "withdrawal is not pending")
}
if pt.Status != payment.StatusPending {
return errors.Wrap(ErrInvalidInput, "withdrawal is not pending")
}

if pt.MerchantID == 0 {
return errors.Wrap(ErrInvalidInput, "invalid merchant id")
}
if pt.MerchantID == 0 {
return errors.Wrap(ErrInvalidInput, "invalid merchant id")
}

if pt.WithdrawalBalanceID() < 1 {
return errors.Wrap(ErrInvalidInput, "invalid balance id")
}
if pt.WithdrawalBalanceID() < 1 {
return errors.Wrap(ErrInvalidInput, "invalid balance id")
}

if pt.WithdrawalAddressID() < 1 {
return errors.Wrap(ErrInvalidInput, "invalid address id")
}
// edge-case: a customer can delete the address while withdrawal is pending
if pt.WithdrawalAddressID() < 1 {
return errors.Wrap(ErrInvalidInput, "invalid address id")
}

return nil
Expand Down
Loading