2026-09-30 12:09:53 +07:00
|
|
|
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))
|
|
|
|
|
}
|
2026-09-30 12:14:32 +07:00
|
|
|
|
|
|
|
|
// 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")
|
|
|
|
|
}
|