67 lines
2.3 KiB
Go
67 lines
2.3 KiB
Go
package validator
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"errors"
|
||
|
|
"strings"
|
||
|
|
"unicode/utf8"
|
||
|
|
|
||
|
|
"apskel-pos-be/internal/constants"
|
||
|
|
"apskel-pos-be/internal/contract"
|
||
|
|
)
|
||
|
|
|
||
|
|
// walletAdjustmentReasonLimit is the size of wallet_transactions.reason.
|
||
|
|
const walletAdjustmentReasonLimit = 255
|
||
|
|
|
||
|
|
// walletCashOutWords are reasons that describe turning balance into money. EnakPoint
|
||
|
|
// and EnakCoin can never be cashed out, and an adjustment must not be used to record
|
||
|
|
// that it happened (docs/prd-point-coin.md K7). Words like "tunai" are left out on
|
||
|
|
// purpose: "paid in cash but the points never arrived" is a legitimate reason.
|
||
|
|
var walletCashOutWords = []string{"pencairan", "cairkan", "cash out", "cashout", "withdraw"}
|
||
|
|
|
||
|
|
type WalletValidator interface {
|
||
|
|
ValidateAdjustRequest(req *contract.AdjustCustomerWalletRequest) (error, string)
|
||
|
|
}
|
||
|
|
|
||
|
|
type WalletValidatorImpl struct{}
|
||
|
|
|
||
|
|
func NewWalletValidator() *WalletValidatorImpl {
|
||
|
|
return &WalletValidatorImpl{}
|
||
|
|
}
|
||
|
|
|
||
|
|
func (v *WalletValidatorImpl) ValidateAdjustRequest(req *contract.AdjustCustomerWalletRequest) (error, string) {
|
||
|
|
if req == nil {
|
||
|
|
return errors.New("request body is required"), constants.MissingFieldErrorCode
|
||
|
|
}
|
||
|
|
|
||
|
|
req.Currency = strings.ToUpper(strings.TrimSpace(req.Currency))
|
||
|
|
if req.Currency == "" {
|
||
|
|
return errors.New("currency is required"), constants.MissingFieldErrorCode
|
||
|
|
}
|
||
|
|
if !constants.IsValidWalletCurrency(req.Currency) {
|
||
|
|
return errors.New("currency must be POINT or COIN"), constants.MalformedFieldErrorCode
|
||
|
|
}
|
||
|
|
|
||
|
|
if req.Amount == 0 {
|
||
|
|
return errors.New("amount must not be zero: use a positive amount to add and a negative one to take away"), constants.ValidationErrorCode
|
||
|
|
}
|
||
|
|
|
||
|
|
req.Reason = strings.TrimSpace(req.Reason)
|
||
|
|
if req.Reason == "" {
|
||
|
|
return errors.New("reason is required"), constants.MissingFieldErrorCode
|
||
|
|
}
|
||
|
|
if utf8.RuneCountInString(req.Reason) > walletAdjustmentReasonLimit {
|
||
|
|
return errors.New("reason must be at most 255 characters"), constants.MalformedFieldErrorCode
|
||
|
|
}
|
||
|
|
lower := strings.ToLower(req.Reason)
|
||
|
|
for _, word := range walletCashOutWords {
|
||
|
|
if strings.Contains(lower, word) {
|
||
|
|
return errors.New("EnakPoint and EnakCoin cannot be cashed out, so an adjustment cannot be for a cash-out"), constants.ValidationErrorCode
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if len(req.IdempotencyKey) > 50 {
|
||
|
|
return errors.New("idempotency_key must be at most 50 characters"), constants.MalformedFieldErrorCode
|
||
|
|
}
|
||
|
|
return nil, ""
|
||
|
|
}
|