Skip to content
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
42 changes: 27 additions & 15 deletions model/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,32 @@ type Account struct {
UsedSellerCode *string `gorm:"default:null" json:"usedSellerCode"`
}

type AccountNotificationEmail struct {
AccountAddress string `gorm:"primaryKey;length:42" json:"accountAddress"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
Email *string `gorm:"default:null" json:"email"`
EmailConfirmed bool `gorm:"not null;default:false" json:"emailConfirmed"`
PendingEmail string `gorm:"default:null" json:"pendingEmail"`
}

type AccountDto struct {
Email string `json:"email"`
EmailConfirmed bool `json:"emailConfirmed"`
PendingEmail string `json:"pendingEmail"`
ApplicantType string `json:"applicantType"`
Address string `json:"address"`
Uuid string `json:"uuid"`
KycStatus string `json:"kycStatus"`
ReceiveUpdates bool `json:"receiveUpdates"`
IsActive bool `json:"isActive"`
IsBlacklisted bool `json:"isBlacklisted"`
BlacklistedReason *string `json:"blacklistedReason"`
UsdBuyLimit int `json:"usdBuyLimit"`
VatPercentage int64 `json:"vatPercentage"`
ViesRegistered bool `json:"viesRegistered"`
UsedSellerCode *string `json:"referral"`
Email string `json:"email"`
EmailConfirmed bool `json:"emailConfirmed"`
PendingEmail string `json:"pendingEmail"`
AdditionalNotificationEmail string `json:"additionalNotificationEmail"`
AdditionalNotificationEmailConfirmed bool `json:"additionalNotificationEmailConfirmed"`
PendingAdditionalNotificationEmail string `json:"pendingAdditionalNotificationEmail"`
ApplicantType string `json:"applicantType"`
Address string `json:"address"`
Uuid string `json:"uuid"`
KycStatus string `json:"kycStatus"`
ReceiveUpdates bool `json:"receiveUpdates"`
IsActive bool `json:"isActive"`
IsBlacklisted bool `json:"isBlacklisted"`
BlacklistedReason *string `json:"blacklistedReason"`
UsdBuyLimit int `json:"usdBuyLimit"`
VatPercentage int64 `json:"vatPercentage"`
ViesRegistered bool `json:"viesRegistered"`
UsedSellerCode *string `json:"referral"`
}
120 changes: 110 additions & 10 deletions proxy/handlers/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,29 @@ import (
)

const (
baseAccountEndpoint = "/accounts"
getAccountEndpoint = "/account"
registerEmailEndpoint = "/email/register"
confirmEmailEndpoint = "/email/confirm"
subscribeEndpoint = "/subscribe"
unsubscribeEndpoint = "/unsubscribe"
blacklistEndpoint = "/blacklist"
addSellerCodeEndpoint = "/add-seller-code"
getKycinfoEndpoint = "/kyc-info"
getIsKybEndpoint = "/is-kyb"
baseAccountEndpoint = "/accounts"
getAccountEndpoint = "/account"
registerEmailEndpoint = "/email/register"
confirmEmailEndpoint = "/email/confirm"
registerNotificationEmailEndpoint = "/notification-email/register"
deleteNotificationEmailEndpoint = "/notification-email"
subscribeEndpoint = "/subscribe"
unsubscribeEndpoint = "/unsubscribe"
blacklistEndpoint = "/blacklist"
addSellerCodeEndpoint = "/add-seller-code"
getKycinfoEndpoint = "/kyc-info"
getIsKybEndpoint = "/is-kyb"
)

type registerEmailRequest struct {
Email string `json:"email"`
ReceiveUpdates bool `json:"receiveUpdates"`
}

type notificationEmailRequest struct {
Email string `json:"email"`
}

type blaclistUserRequest struct {
Address string `json:"address"`
Reasons string `json:"reasons"`
Expand Down Expand Up @@ -65,6 +71,8 @@ func NewAccountHandler(groupHandler *groupHandler) {
authEndpoints := []EndpointHandler{
{Method: http.MethodGet, Path: getAccountEndpoint, HandlerFunc: h.getOrCreateAccount},
{Method: http.MethodPost, Path: registerEmailEndpoint, HandlerFunc: h.registerEmail},
{Method: http.MethodPost, Path: registerNotificationEmailEndpoint, HandlerFunc: h.registerNotificationEmail},
{Method: http.MethodDelete, Path: deleteNotificationEmailEndpoint, HandlerFunc: h.deleteNotificationEmail},
{Method: http.MethodGet, Path: subscribeEndpoint, HandlerFunc: h.subscribe},
{Method: http.MethodGet, Path: unsubscribeEndpoint, HandlerFunc: h.unsubscribe},
{Method: http.MethodPost, Path: blacklistEndpoint, HandlerFunc: h.blackListAccount},
Expand Down Expand Up @@ -185,6 +193,98 @@ func (h *accountHandler) registerEmail(c *gin.Context) {
model.JsonResponse(c, http.StatusOK, accountDto, nodeAddress, "")
}

func (h *accountHandler) registerNotificationEmail(c *gin.Context) {
nodeAddress, err := service.GetAddress()
if err != nil {
log.Error("error while retrieving node address: " + err.Error())
model.JsonResponse(c, http.StatusInternalServerError, nil, "", err.Error())
return
}

address, err := middleware.AddressFromBearer(c)
if err != nil {
log.Error("error while retrieving address from bearer: " + err.Error())
model.JsonResponse(c, http.StatusBadRequest, nil, nodeAddress, err.Error())
return
}

var req notificationEmailRequest
err = c.Bind(&req)
if err != nil {
log.Error("error while binding request: " + err.Error())
model.JsonResponse(c, http.StatusBadRequest, nil, nodeAddress, err.Error())
return
}

account, err := service.RegisterNotificationEmail(address, req.Email)
if err != nil {
log.Error("error while register notification email: " + err.Error())
model.JsonResponse(c, http.StatusBadRequest, nil, nodeAddress, err.Error())
return
}

var kyc *model.Kyc
if account.Email != nil {
kyc, _, err = storage.GetKycByEmail(*account.Email)
if err != nil {
log.Error("error while retrieving kyc information from storage: " + err.Error())
model.JsonResponse(c, http.StatusInternalServerError, nil, nodeAddress, err.Error())
return
}
}

accountDto, err := service.NewAccountDto(account, kyc)
if err != nil {
log.Error("error while creating account dto: " + err.Error())
model.JsonResponse(c, http.StatusInternalServerError, nil, nodeAddress, err.Error())
return
}

model.JsonResponse(c, http.StatusOK, accountDto, nodeAddress, "")
}

func (h *accountHandler) deleteNotificationEmail(c *gin.Context) {
nodeAddress, err := service.GetAddress()
if err != nil {
log.Error("error while retrieving node address: " + err.Error())
model.JsonResponse(c, http.StatusInternalServerError, nil, "", err.Error())
return
}

address, err := middleware.AddressFromBearer(c)
if err != nil {
log.Error("error while retrieving address from bearer: " + err.Error())
model.JsonResponse(c, http.StatusBadRequest, nil, nodeAddress, err.Error())
return
}

account, err := service.DeleteNotificationEmail(address)
if err != nil {
log.Error("error while deleting notification email: " + err.Error())
model.JsonResponse(c, http.StatusBadRequest, nil, nodeAddress, err.Error())
return
}

var kyc *model.Kyc
if account.Email != nil {
kyc, _, err = storage.GetKycByEmail(*account.Email)
if err != nil {
log.Error("error while retrieving kyc information from storage: " + err.Error())
model.JsonResponse(c, http.StatusInternalServerError, nil, nodeAddress, err.Error())
return
}
}

accountDto, err := service.NewAccountDto(account, kyc)
if err != nil {
log.Error("error while creating account dto: " + err.Error())
model.JsonResponse(c, http.StatusInternalServerError, nil, nodeAddress, err.Error())
return
}

model.JsonResponse(c, http.StatusOK, accountDto, nodeAddress, "")
}

func (h *accountHandler) confirmEmail(c *gin.Context) {
nodeAddress, err := service.GetAddress()
if err != nil {
Expand Down
118 changes: 78 additions & 40 deletions service/accountService.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,10 @@ func ConfirmEmail(token string) (*model.Account, error) {
if err != nil {
return nil, errors.New("error while validating confirm jwt: " + err.Error())
}
if claims.Address == "" && claims.Email == "" {
if claims.Address == "" || claims.Email == "" {
return nil, errors.New("found bad claims in confirm token")
}
email := TrimWhitespacesAndToLower(claims.Email)

account, err := getAcocunt(claims.Address)
if err != nil {
Expand All @@ -94,19 +95,37 @@ func ConfirmEmail(token string) (*model.Account, error) {
return nil, ErrorAccountNotFound
}

if account.EmailConfirmed {
if *account.Email == claims.Email {
return account, nil // already confirmed
} else {
return nil, errors.New("account already has another email")
}
if account.EmailConfirmed && account.Email != nil && TrimWhitespacesAndToLower(*account.Email) == email {
return account, nil // already confirmed
}

if account.PendingEmail != claims.Email {
return nil, errors.New("wrong confirmation token")
if !account.EmailConfirmed && TrimWhitespacesAndToLower(account.PendingEmail) == email {
return confirmPrimaryEmail(account, email)
}

notificationEmail, found, err := storage.GetAccountNotificationEmailByAddress(claims.Address)
if err != nil {
return nil, errors.New("error while retrieving notification email from storage: " + err.Error())
}
if found {
confirmed, err := confirmPendingNotificationEmail(notificationEmail, email)
if err != nil {
return nil, err
}
if confirmed {
err = storage.CreateOrUpdateAccountNotificationEmail(notificationEmail)
if err != nil {
return nil, errors.New("error while updating notification email on storage: " + err.Error())
}
return account, nil
}
}

account.Email = &claims.Email
return nil, errors.New("wrong confirmation token")
}

func confirmPrimaryEmail(account *model.Account, email string) (*model.Account, error) {
account.Email = &email
account.EmailConfirmed = true
account.PendingEmail = ""
receiveUpdates := account.PendingReceiveUpdates
Expand All @@ -119,7 +138,7 @@ func ConfirmEmail(token string) (*model.Account, error) {
}
account.PendingReceiveUpdates = false

err = storage.UpdateAccount(account)
err := storage.UpdateAccount(account)
if err != nil {
return nil, errors.New("error while updating account on storage: " + err.Error())
}
Expand Down Expand Up @@ -175,6 +194,19 @@ func UnsubscribeEmail(kyc *model.Kyc) error {
}

func NewAccountDto(account *model.Account, kyc *model.Kyc) (*model.AccountDto, error) {
notificationEmail, found, err := storage.GetAccountNotificationEmailByAddress(account.Address)
if err != nil {
return nil, errors.New("error while retrieving notification email from storage: " + err.Error())
}
additionalNotificationEmail := ""
additionalNotificationEmailConfirmed := false
pendingAdditionalNotificationEmail := ""
if found && notificationEmail != nil {
additionalNotificationEmail = StringOrEmpty(notificationEmail.Email)
additionalNotificationEmailConfirmed = notificationEmail.EmailConfirmed
pendingAdditionalNotificationEmail = notificationEmail.PendingEmail
}

if kyc != nil {
limit := 0
if kyc.ApplicantType == model.BusinessCustomer {
Expand Down Expand Up @@ -202,21 +234,24 @@ func NewAccountDto(account *model.Account, kyc *model.Kyc) (*model.AccountDto, e
}

return &model.AccountDto{
Email: StringOrEmpty(account.Email),
EmailConfirmed: account.EmailConfirmed,
PendingEmail: account.PendingEmail,
Address: account.Address,
ApplicantType: kyc.ApplicantType,
Uuid: kyc.Uuid.String(),
KycStatus: kyc.KycStatus,
ReceiveUpdates: *kyc.ReceiveUpdates,
IsActive: kyc.IsActive,
IsBlacklisted: account.IsBlacklisted,
BlacklistedReason: account.BlacklistedReason,
UsdBuyLimit: limit,
VatPercentage: vatPercentage,
ViesRegistered: kyc.ViesRegistered,
UsedSellerCode: account.UsedSellerCode,
Email: StringOrEmpty(account.Email),
EmailConfirmed: account.EmailConfirmed,
PendingEmail: account.PendingEmail,
AdditionalNotificationEmail: additionalNotificationEmail,
AdditionalNotificationEmailConfirmed: additionalNotificationEmailConfirmed,
PendingAdditionalNotificationEmail: pendingAdditionalNotificationEmail,
Address: account.Address,
ApplicantType: kyc.ApplicantType,
Uuid: kyc.Uuid.String(),
KycStatus: kyc.KycStatus,
ReceiveUpdates: *kyc.ReceiveUpdates,
IsActive: kyc.IsActive,
IsBlacklisted: account.IsBlacklisted,
BlacklistedReason: account.BlacklistedReason,
UsdBuyLimit: limit,
VatPercentage: vatPercentage,
ViesRegistered: kyc.ViesRegistered,
UsedSellerCode: account.UsedSellerCode,
}, nil
}

Expand All @@ -227,20 +262,23 @@ func NewAccountDto(account *model.Account, kyc *model.Kyc) (*model.AccountDto, e
vatPercentage = 2200
}
return &model.AccountDto{
Email: StringOrEmpty(account.Email),
EmailConfirmed: account.EmailConfirmed,
PendingEmail: account.PendingEmail,
Address: account.Address,
ApplicantType: "",
Uuid: "",
KycStatus: "",
ReceiveUpdates: false,
IsActive: false,
IsBlacklisted: account.IsBlacklisted,
BlacklistedReason: account.BlacklistedReason,
UsdBuyLimit: UsdBuyLimit,
VatPercentage: int64(vatPercentage),
UsedSellerCode: account.UsedSellerCode,
Email: StringOrEmpty(account.Email),
EmailConfirmed: account.EmailConfirmed,
PendingEmail: account.PendingEmail,
AdditionalNotificationEmail: additionalNotificationEmail,
AdditionalNotificationEmailConfirmed: additionalNotificationEmailConfirmed,
PendingAdditionalNotificationEmail: pendingAdditionalNotificationEmail,
Address: account.Address,
ApplicantType: "",
Uuid: "",
KycStatus: "",
ReceiveUpdates: false,
IsActive: false,
IsBlacklisted: account.IsBlacklisted,
BlacklistedReason: account.BlacklistedReason,
UsdBuyLimit: UsdBuyLimit,
VatPercentage: int64(vatPercentage),
UsedSellerCode: account.UsedSellerCode,
}, nil

}
Expand Down
Loading
Loading