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

Commit dcb11c2

Browse files
authored
Merge pull request #38 from oxygenpay/develop
Develop
2 parents 272c5ed + 5df6991 commit dcb11c2

23 files changed

+239
-231
lines changed

cmd/root.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ func Execute() {
4040

4141
// resolveConfig or exit with error
4242
func resolveConfig() *config.Config {
43-
cfg, err := config.New(Version, Commit, configPath, skipConfig, EmbedFrontend)
43+
cfg, err := config.New(Commit, Version, configPath, skipConfig, EmbedFrontend)
4444
if err != nil {
4545
fmt.Printf("unable to initialize config: %s\n", err.Error())
4646
os.Exit(1)

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@ require (
2626
github.com/oxygenpay/tatum-sdk v0.0.0-20230529210116-d986b7743613
2727
github.com/pkg/errors v0.9.1
2828
github.com/robfig/cron/v3 v3.0.1
29-
github.com/rs/zerolog v1.26.1
29+
github.com/rs/zerolog v1.29.0
3030
github.com/rubenv/sql-migrate v1.2.0
3131
github.com/samber/lo v1.37.0
3232
github.com/spf13/cobra v1.6.1
3333
github.com/stretchr/testify v1.8.1
3434
github.com/tidwall/gjson v1.14.4
3535
github.com/wemeetagain/go-hdwallet v0.1.0
36-
github.com/ziflex/lecho/v3 v3.1.0
36+
github.com/ziflex/lecho/v3 v3.5.0
3737
go.etcd.io/bbolt v1.3.6
3838
go.uber.org/atomic v1.10.0
3939
golang.org/x/crypto v0.11.0

go.sum

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
104104
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
105105
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
106106
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
107+
github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
107108
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
108109
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
109110
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
@@ -458,6 +459,7 @@ github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc
458459
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
459460
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
460461
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
462+
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
461463
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
462464
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
463465
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
@@ -541,11 +543,14 @@ github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZV
541543
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
542544
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
543545
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
546+
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
544547
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
545548
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
546549
github.com/rs/zerolog v1.26.0/go.mod h1:yBiM87lvSqX8h0Ww4sdzNSkVYZ8dL2xjZJG1lAuGZEo=
547550
github.com/rs/zerolog v1.26.1 h1:/ihwxqH+4z8UxyI70wM1z9yCvkWcfz/a3mj48k/Zngc=
548551
github.com/rs/zerolog v1.26.1/go.mod h1:/wSSJWX7lVrsOwlbyTRSOJvqRlc+WjWlfes+CiJ+tmc=
552+
github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w=
553+
github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
549554
github.com/rubenv/sql-migrate v1.2.0 h1:fOXMPLMd41sK7Tg75SXDec15k3zg5WNV6SjuDRiNfcU=
550555
github.com/rubenv/sql-migrate v1.2.0/go.mod h1:Z5uVnq7vrIrPmHbVFfR4YLHRZquxeHpckCnRq0P/K9Y=
551556
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -636,6 +641,8 @@ github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
636641
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
637642
github.com/ziflex/lecho/v3 v3.1.0 h1:65bSzSc0yw7EEhi44lMnkOI877ZzbE7tGDWfYCQXZwI=
638643
github.com/ziflex/lecho/v3 v3.1.0/go.mod h1:dwQ6xCAKmSBHhwZ6XmiAiDptD7iklVkW7xQYGUncX0Q=
644+
github.com/ziflex/lecho/v3 v3.5.0 h1:Z4TBr8SbUUnfaVc8tGJf1Jhu0G9Jxjl77lPW0riXKak=
645+
github.com/ziflex/lecho/v3 v3.5.0/go.mod h1:+eInrytYHxVPI6NQbua9xXGerB1x0ujj9jAV33yBIko=
639646
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
640647
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
641648
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=

internal/server/http/server.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ func WithLogger(logger *zerolog.Logger) Opt {
9191
s.echo.Use(lecho.Middleware(lecho.Config{
9292
Logger: lecho.From(l, lecho.WithLevel(log.INFO)),
9393
RequestIDKey: middleware.RequestIDKey,
94+
Enricher: loggerEnricher,
9495
Skipper: func(c echo.Context) bool {
9596
path := c.Request().URL.Path
9697

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

110+
func loggerEnricher(c echo.Context, logger zerolog.Context) zerolog.Context {
111+
merchantID := c.Param("merchantId")
112+
if merchantID != "" {
113+
logger = logger.Str("merchant_id", merchantID)
114+
}
115+
116+
paymentID := c.Param("paymentId")
117+
if paymentID != "" {
118+
logger = logger.Str("payment_id", paymentID)
119+
}
120+
121+
return logger.Str("path", c.Path())
122+
}
123+
109124
const healthcheckPath = "/health"
110125

111126
func withHealthcheck(e *echo.Echo) {

internal/service/payment/service.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,11 @@ func (s *Service) Update(ctx context.Context, merchantID, id int64, props Update
580580
return s.entryToPayment(pt)
581581
}
582582

583+
func (s *Service) Fail(ctx context.Context, pt *Payment) error {
584+
_, err := s.Update(ctx, pt.MerchantID, pt.ID, UpdateProps{Status: StatusFailed})
585+
return err
586+
}
587+
583588
func (s *Service) SetWebhookTimestamp(ctx context.Context, merchantID, id int64, sentAt time.Time) error {
584589
err := s.repo.UpdatePaymentWebhookInfo(ctx, repository.UpdatePaymentWebhookInfoParams{
585590
ID: id,

internal/service/payment/service_withdrawal.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package payment
22

33
import (
44
"context"
5+
"fmt"
56
"strconv"
67
"time"
78

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

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

3940
payments := make([]*Payment, len(results))

internal/service/processing/service_withdrawal.go

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,16 @@ func (s *Service) BatchCreateWithdrawals(ctx context.Context, withdrawalIDs []in
2323
return nil, err
2424
}
2525

26-
// 1.Validate payments
27-
if errValidate := s.validateWithdrawals(withdrawals); errValidate != nil {
28-
return nil, errValidate
29-
}
30-
31-
// 2. Get OUTBOUND wallets and balances
26+
// 1. Get OUTBOUND wallets and balances
3227
outboundWallets, outboundBalances, err := s.getOutboundWalletsWithBalancesAsMap(ctx)
3328
if err != nil {
3429
return nil, errors.Wrap(err, "unable to get outbound wallets with balances")
3530
}
3631

3732
result := &TransferResult{}
3833

39-
// 3. For each withdrawal:
34+
// 2. For each withdrawal:
35+
// - Validate
4036
// - Resolve currency
4137
// - Resolve outbound system wallet & balance
4238
// - Resolve merchant balance & withdrawal address
@@ -47,6 +43,18 @@ func (s *Service) BatchCreateWithdrawals(ctx context.Context, withdrawalIDs []in
4743
for i := range withdrawals {
4844
withdrawal := withdrawals[i]
4945
group.Go(func() error {
46+
// Let's validate each withdrawal individually.
47+
// By doing so, we can reject it without blocking other withdrawals.
48+
if errValidate := validateWithdrawal(withdrawal); errValidate != nil {
49+
if errUpdate := s.payments.Fail(ctx, withdrawal); errUpdate != nil {
50+
result.registerErr(errors.Wrap(errUpdate, "unable to mark invalid withdrawal as failed"))
51+
} else {
52+
result.registerErr(errors.Wrap(errValidate, "withdrawal is invalid, marked as failed"))
53+
}
54+
55+
return nil
56+
}
57+
5058
currency, err := s.blockchain.GetCurrencyByTicker(withdrawal.Price.Ticker())
5159
if err != nil {
5260
result.registerErr(errors.Wrap(err, "unable to get withdrawal currency"))
@@ -540,27 +548,26 @@ func (s *Service) cancelWithdrawal(
540548
return nil
541549
}
542550

543-
func (s *Service) validateWithdrawals(withdrawals []*payment.Payment) error {
544-
for _, pt := range withdrawals {
545-
if pt.Type != payment.TypeWithdrawal {
546-
return errors.Wrap(ErrInvalidInput, "payment is not withdrawal")
547-
}
551+
func validateWithdrawal(pt *payment.Payment) error {
552+
if pt.Type != payment.TypeWithdrawal {
553+
return errors.Wrap(ErrInvalidInput, "payment is not withdrawal")
554+
}
548555

549-
if pt.Status != payment.StatusPending {
550-
return errors.Wrap(ErrInvalidInput, "withdrawal is not pending")
551-
}
556+
if pt.Status != payment.StatusPending {
557+
return errors.Wrap(ErrInvalidInput, "withdrawal is not pending")
558+
}
552559

553-
if pt.MerchantID == 0 {
554-
return errors.Wrap(ErrInvalidInput, "invalid merchant id")
555-
}
560+
if pt.MerchantID == 0 {
561+
return errors.Wrap(ErrInvalidInput, "invalid merchant id")
562+
}
556563

557-
if pt.WithdrawalBalanceID() < 1 {
558-
return errors.Wrap(ErrInvalidInput, "invalid balance id")
559-
}
564+
if pt.WithdrawalBalanceID() < 1 {
565+
return errors.Wrap(ErrInvalidInput, "invalid balance id")
566+
}
560567

561-
if pt.WithdrawalAddressID() < 1 {
562-
return errors.Wrap(ErrInvalidInput, "invalid address id")
563-
}
568+
// edge-case: a customer can delete the address while withdrawal is pending
569+
if pt.WithdrawalAddressID() < 1 {
570+
return errors.Wrap(ErrInvalidInput, "invalid address id")
564571
}
565572

566573
return nil

0 commit comments

Comments
 (0)