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()), }) }