67 lines
2.4 KiB
Go
67 lines
2.4 KiB
Go
package service
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"errors"
|
||
|
|
|
||
|
|
"github.com/google/uuid"
|
||
|
|
|
||
|
|
"apskel-pos-be/internal/constants"
|
||
|
|
"apskel-pos-be/internal/contract"
|
||
|
|
"apskel-pos-be/internal/models"
|
||
|
|
"apskel-pos-be/internal/processor"
|
||
|
|
"apskel-pos-be/internal/repository"
|
||
|
|
)
|
||
|
|
|
||
|
|
// CustomerWalletService moves balance on the customer's own request: exchanging
|
||
|
|
// EnakCoin into EnakPoint (docs/prd-point-coin.md F4).
|
||
|
|
type CustomerWalletService interface {
|
||
|
|
PreviewExchange(ctx context.Context, customerID uuid.UUID, coins int64) *contract.Response
|
||
|
|
Exchange(ctx context.Context, customerID uuid.UUID, req *contract.ExchangeCoinsRequest, idempotencyKey string, info models.CustomerPinRequestInfo) *contract.Response
|
||
|
|
}
|
||
|
|
|
||
|
|
type CustomerWalletServiceImpl struct {
|
||
|
|
exchanges *processor.WalletExchangeProcessor
|
||
|
|
}
|
||
|
|
|
||
|
|
func NewCustomerWalletService(exchanges *processor.WalletExchangeProcessor) *CustomerWalletServiceImpl {
|
||
|
|
return &CustomerWalletServiceImpl{exchanges: exchanges}
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *CustomerWalletServiceImpl) PreviewExchange(ctx context.Context, customerID uuid.UUID, coins int64) *contract.Response {
|
||
|
|
preview, err := s.exchanges.Preview(ctx, customerID, coins)
|
||
|
|
if err != nil {
|
||
|
|
return walletMoveErrorResponse(err)
|
||
|
|
}
|
||
|
|
return contract.BuildSuccessResponse(preview)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *CustomerWalletServiceImpl) Exchange(ctx context.Context, customerID uuid.UUID, req *contract.ExchangeCoinsRequest, idempotencyKey string, info models.CustomerPinRequestInfo) *contract.Response {
|
||
|
|
result, err := s.exchanges.Exchange(ctx, customerID, req.Coins, req.Pin, idempotencyKey, info)
|
||
|
|
if err != nil {
|
||
|
|
return walletMoveErrorResponse(err)
|
||
|
|
}
|
||
|
|
return contract.BuildSuccessResponse(result)
|
||
|
|
}
|
||
|
|
|
||
|
|
// walletMoveErrorResponse keeps the PIN codes the apps act on, and tells a refused
|
||
|
|
// request apart from a server failure.
|
||
|
|
func walletMoveErrorResponse(err error) *contract.Response {
|
||
|
|
var pinErr *processor.PinError
|
||
|
|
if errors.As(err, &pinErr) {
|
||
|
|
return PinErrorResponse(err)
|
||
|
|
}
|
||
|
|
code := constants.InternalServerErrorCode
|
||
|
|
switch {
|
||
|
|
case errors.Is(err, repository.ErrWalletNotFound):
|
||
|
|
code = constants.NotFoundErrorCode
|
||
|
|
case errors.Is(err, processor.ErrWalletMoveRejected),
|
||
|
|
errors.Is(err, processor.ErrWalletIdempotencyConflict),
|
||
|
|
errors.Is(err, processor.ErrWalletInvalidEntry):
|
||
|
|
code = constants.ValidationErrorCode
|
||
|
|
}
|
||
|
|
return contract.BuildErrorResponse([]*contract.ResponseError{
|
||
|
|
contract.NewResponseError(code, constants.WalletServiceEntity, err.Error()),
|
||
|
|
})
|
||
|
|
}
|