init
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
package economy
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"hakemsho/internal/auth"
|
||||
"hakemsho/internal/httpx"
|
||||
)
|
||||
|
||||
// Handler endpointهای فروشگاه و کیفپول (همه پشت JWT).
|
||||
type Handler struct {
|
||||
svc *Service
|
||||
}
|
||||
|
||||
func NewHandler(svc *Service) *Handler { return &Handler{svc: svc} }
|
||||
|
||||
func uid(r *http.Request) (int64, bool) { return auth.UserID(r.Context()) }
|
||||
|
||||
// Wallet — GET /api/wallet
|
||||
func (h *Handler) Wallet(w http.ResponseWriter, r *http.Request) {
|
||||
id, ok := uid(r)
|
||||
if !ok {
|
||||
httpx.Error(w, http.StatusUnauthorized, "unauthorized")
|
||||
return
|
||||
}
|
||||
p, err := h.svc.GetProfile(r.Context(), id)
|
||||
if err != nil {
|
||||
httpx.Error(w, http.StatusNotFound, "user not found")
|
||||
return
|
||||
}
|
||||
httpx.JSON(w, http.StatusOK, p)
|
||||
}
|
||||
|
||||
// Shop — GET /api/shop
|
||||
func (h *Handler) Shop(w http.ResponseWriter, r *http.Request) {
|
||||
id, ok := uid(r)
|
||||
if !ok {
|
||||
httpx.Error(w, http.StatusUnauthorized, "unauthorized")
|
||||
return
|
||||
}
|
||||
owned, err := h.svc.OwnedCards(r.Context(), id)
|
||||
if err != nil {
|
||||
httpx.Error(w, http.StatusInternalServerError, "server error")
|
||||
return
|
||||
}
|
||||
p, _ := h.svc.GetProfile(r.Context(), id)
|
||||
selected := "simple"
|
||||
if p != nil {
|
||||
selected = p.SelectedCard
|
||||
}
|
||||
httpx.JSON(w, http.StatusOK, map[string]any{
|
||||
"catalog": FullCatalog(),
|
||||
"owned_cards": owned,
|
||||
"selected_card": selected,
|
||||
})
|
||||
}
|
||||
|
||||
type cardReq struct {
|
||||
CardID string `json:"card_id"`
|
||||
}
|
||||
|
||||
// BuyCard — POST /api/shop/buy-card
|
||||
func (h *Handler) BuyCard(w http.ResponseWriter, r *http.Request) {
|
||||
id, ok := uid(r)
|
||||
if !ok {
|
||||
httpx.Error(w, http.StatusUnauthorized, "unauthorized")
|
||||
return
|
||||
}
|
||||
var req cardReq
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
httpx.Error(w, http.StatusBadRequest, "invalid body")
|
||||
return
|
||||
}
|
||||
switch err := h.svc.BuyCard(r.Context(), id, req.CardID); {
|
||||
case err == nil:
|
||||
httpx.JSON(w, http.StatusOK, map[string]string{"message": "ok"})
|
||||
case errors.Is(err, ErrNotFound):
|
||||
httpx.Error(w, http.StatusNotFound, "card not found")
|
||||
case errors.Is(err, ErrAlreadyOwned):
|
||||
httpx.Error(w, http.StatusConflict, "already owned")
|
||||
case errors.Is(err, ErrInsufficient):
|
||||
httpx.Error(w, http.StatusPaymentRequired, "insufficient coins")
|
||||
default:
|
||||
httpx.Error(w, http.StatusInternalServerError, "server error")
|
||||
}
|
||||
}
|
||||
|
||||
// SelectCard — POST /api/shop/select-card
|
||||
func (h *Handler) SelectCard(w http.ResponseWriter, r *http.Request) {
|
||||
id, ok := uid(r)
|
||||
if !ok {
|
||||
httpx.Error(w, http.StatusUnauthorized, "unauthorized")
|
||||
return
|
||||
}
|
||||
var req cardReq
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
httpx.Error(w, http.StatusBadRequest, "invalid body")
|
||||
return
|
||||
}
|
||||
switch err := h.svc.SelectCard(r.Context(), id, req.CardID); {
|
||||
case err == nil:
|
||||
httpx.JSON(w, http.StatusOK, map[string]string{"message": "ok"})
|
||||
case errors.Is(err, ErrNotFound):
|
||||
httpx.Error(w, http.StatusNotFound, "card not found")
|
||||
case errors.Is(err, ErrNotOwned):
|
||||
httpx.Error(w, http.StatusForbidden, "card not owned")
|
||||
default:
|
||||
httpx.Error(w, http.StatusInternalServerError, "server error")
|
||||
}
|
||||
}
|
||||
|
||||
// Purchase — POST /api/shop/purchase (تأیید خرید IAP)
|
||||
func (h *Handler) Purchase(w http.ResponseWriter, r *http.Request) {
|
||||
id, ok := uid(r)
|
||||
if !ok {
|
||||
httpx.Error(w, http.StatusUnauthorized, "unauthorized")
|
||||
return
|
||||
}
|
||||
var req PurchaseRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
httpx.Error(w, http.StatusBadRequest, "invalid body")
|
||||
return
|
||||
}
|
||||
switch err := h.svc.VerifyPurchase(r.Context(), id, req); {
|
||||
case err == nil:
|
||||
p, _ := h.svc.GetProfile(r.Context(), id)
|
||||
httpx.JSON(w, http.StatusOK, map[string]any{"message": "ok", "wallet": p})
|
||||
case errors.Is(err, ErrAlreadyOwned):
|
||||
httpx.Error(w, http.StatusConflict, "already processed")
|
||||
case errors.Is(err, ErrIAPInvalid):
|
||||
httpx.Error(w, http.StatusBadRequest, "purchase not valid")
|
||||
case errors.Is(err, ErrNotFound):
|
||||
httpx.Error(w, http.StatusNotFound, "package not found")
|
||||
default:
|
||||
httpx.Error(w, http.StatusInternalServerError, "server error")
|
||||
}
|
||||
}
|
||||
|
||||
// Daily — POST /api/rewards/daily
|
||||
func (h *Handler) Daily(w http.ResponseWriter, r *http.Request) {
|
||||
id, ok := uid(r)
|
||||
if !ok {
|
||||
httpx.Error(w, http.StatusUnauthorized, "unauthorized")
|
||||
return
|
||||
}
|
||||
switch amount, err := h.svc.ClaimDaily(r.Context(), id); {
|
||||
case err == nil:
|
||||
httpx.JSON(w, http.StatusOK, map[string]any{"message": "ok", "amount": amount})
|
||||
case errors.Is(err, ErrTooSoon):
|
||||
httpx.Error(w, http.StatusTooManyRequests, "already claimed today")
|
||||
default:
|
||||
httpx.Error(w, http.StatusInternalServerError, "server error")
|
||||
}
|
||||
}
|
||||
|
||||
type adReq struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
// AdReward — POST /api/rewards/ad (سکه رایگان پس از دیدن کامل تبلیغ)
|
||||
func (h *Handler) AdReward(w http.ResponseWriter, r *http.Request) {
|
||||
id, ok := uid(r)
|
||||
if !ok {
|
||||
httpx.Error(w, http.StatusUnauthorized, "unauthorized")
|
||||
return
|
||||
}
|
||||
var req adReq
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
httpx.Error(w, http.StatusBadRequest, "invalid body")
|
||||
return
|
||||
}
|
||||
switch amount, err := h.svc.ClaimAdReward(r.Context(), id, req.Token); {
|
||||
case err == nil:
|
||||
httpx.JSON(w, http.StatusOK, map[string]any{"message": "ok", "amount": amount})
|
||||
case errors.Is(err, ErrAdNotVerified):
|
||||
httpx.Error(w, http.StatusBadRequest, "ad not verified")
|
||||
case errors.Is(err, ErrAdDuplicate):
|
||||
httpx.Error(w, http.StatusConflict, "already claimed")
|
||||
case errors.Is(err, ErrAdCap):
|
||||
httpx.Error(w, http.StatusTooManyRequests, "daily ad cap reached")
|
||||
default:
|
||||
httpx.Error(w, http.StatusInternalServerError, "server error")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user