Files
apskel-pos-backend/internal/handler/customer_wallet_handler.go
2026-09-30 15:31:44 +07:00

93 lines
3.2 KiB
Go

package handler
import (
"strconv"
"strings"
"github.com/gin-gonic/gin"
"apskel-pos-be/internal/constants"
"apskel-pos-be/internal/contract"
"apskel-pos-be/internal/service"
"apskel-pos-be/internal/util"
)
// The header exchanges and transfers require (docs/prd-point-coin.md F4, F5), and the
// X- form the rest of the API uses, which is accepted too.
const (
idempotencyKeyHeader = "Idempotency-Key"
legacyIdempotencyKeyHeader = "X-Idempotency-Key"
)
// CustomerWalletHandler serves what the customer app does with its own wallet. Bodies
// hold the PIN, so they are never logged.
type CustomerWalletHandler struct {
wallets service.CustomerWalletService
}
func NewCustomerWalletHandler(wallets service.CustomerWalletService) *CustomerWalletHandler {
return &CustomerWalletHandler{wallets: wallets}
}
// PreviewExchange is GET /customer/wallet/exchange/preview?coins=.
func (h *CustomerWalletHandler) PreviewExchange(c *gin.Context) {
customerID, ok := customerIDFromGin(c, "CustomerWalletHandler::PreviewExchange")
if !ok {
return
}
var coins int64
if raw := c.Query("coins"); raw != "" {
var err error
coins, err = strconv.ParseInt(raw, 10, 64)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "coins must be a whole number"),
}), "CustomerWalletHandler::PreviewExchange")
return
}
}
util.HandleResponse(c.Writer, c.Request, h.wallets.PreviewExchange(c.Request.Context(), customerID, coins), "CustomerWalletHandler::PreviewExchange")
}
// Exchange is POST /customer/wallet/exchange.
func (h *CustomerWalletHandler) Exchange(c *gin.Context) {
customerID, ok := customerIDFromGin(c, "CustomerWalletHandler::Exchange")
if !ok {
return
}
var req contract.ExchangeCoinsRequest
if !bindPinRequest(c, &req, "CustomerWalletHandler::Exchange") {
return
}
util.HandleResponse(c.Writer, c.Request, h.wallets.Exchange(c.Request.Context(), customerID, &req, idempotencyKey(c), pinRequestInfo(c)), "CustomerWalletHandler::Exchange")
}
func idempotencyKey(c *gin.Context) string {
if key := strings.TrimSpace(c.GetHeader(idempotencyKeyHeader)); key != "" {
return key
}
return strings.TrimSpace(c.GetHeader(legacyIdempotencyKeyHeader))
}
// TransferRecipient is GET /customer/wallet/transfer/recipient?phone=.
func (h *CustomerWalletHandler) TransferRecipient(c *gin.Context) {
customerID, ok := customerIDFromGin(c, "CustomerWalletHandler::TransferRecipient")
if !ok {
return
}
util.HandleResponse(c.Writer, c.Request, h.wallets.TransferRecipient(c.Request.Context(), customerID, c.Query("phone")), "CustomerWalletHandler::TransferRecipient")
}
// Transfer is POST /customer/wallet/transfer.
func (h *CustomerWalletHandler) Transfer(c *gin.Context) {
customerID, ok := customerIDFromGin(c, "CustomerWalletHandler::Transfer")
if !ok {
return
}
var req contract.TransferWalletRequest
if !bindPinRequest(c, &req, "CustomerWalletHandler::Transfer") {
return
}
util.HandleResponse(c.Writer, c.Request, h.wallets.Transfer(c.Request.Context(), customerID, &req, idempotencyKey(c), pinRequestInfo(c)), "CustomerWalletHandler::Transfer")
}