package payment import ( "errors" "io" "log" "net/http" "strconv" "hfb_sys/backend/internal/integrations/payment/leshua" "hfb_sys/backend/internal/middleware" "hfb_sys/backend/internal/modules/order" "hfb_sys/backend/pkg/response" "github.com/gin-gonic/gin" "gorm.io/gorm" ) type Handler struct { service *Service } func NewHandler(service *Service) *Handler { return &Handler{service: service} } func (h *Handler) Start(c *gin.Context) { userID, ok := currentUserID(c) if !ok { response.Unauthorized(c, "缺少用户上下文") return } orderID, ok := parseID(c) if !ok { return } var req StartPaymentRequest _ = c.ShouldBindJSON(&req) item, err := h.service.Start(userID, orderID, req, c.ClientIP()) if err != nil { writePaymentError(c, err) return } response.OK(c, item) } func (h *Handler) Query(c *gin.Context) { userID, ok := currentUserID(c) if !ok { response.Unauthorized(c, "缺少用户上下文") return } orderID, ok := parseID(c) if !ok { return } item, err := h.service.Query(userID, orderID) if err != nil { writePaymentError(c, err) return } response.OK(c, item) } func (h *Handler) WalletRecharge(c *gin.Context) { userID, ok := currentUserID(c) if !ok { response.Unauthorized(c, "缺少用户上下文") return } var req WalletRechargePaymentRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "充值金额不正确") return } item, err := h.service.StartWalletRecharge(userID, req, c.ClientIP()) if err != nil { writePaymentError(c, err) return } response.OK(c, item) } func (h *Handler) WalletRechargeQuery(c *gin.Context) { userID, ok := currentUserID(c) if !ok { response.Unauthorized(c, "缺少用户上下文") return } paymentID, ok := parseID(c) if !ok { return } item, err := h.service.QueryWalletRecharge(userID, paymentID) if err != nil { writePaymentError(c, err) return } response.OK(c, item) } func (h *Handler) LeshuaNotify(c *gin.Context) { body, err := io.ReadAll(io.LimitReader(c.Request.Body, 1<<20)) if err != nil { c.String(http.StatusBadRequest, "FAIL") return } params, err := leshua.ParsePayload(body) if err != nil { c.String(http.StatusBadRequest, "FAIL") return } rawPayload := string(body) contentType := c.GetHeader("Content-Type") log.Printf( "[payment] leshua notify received third_order_id=%s leshua_order_id=%s status=%s amount=%s content_type=%s raw_payload=%s", params["third_order_id"], params["leshua_order_id"], params["status"], params["amount"], contentType, rawPayload, ) result, err := h.service.HandleLeshuaNotify(params, rawPayload, contentType) if err != nil || result == nil || !result.OK { log.Printf("[payment] leshua notify failed third_order_id=%s err=%v", params["third_order_id"], err) c.String(http.StatusOK, "FAIL") return } log.Printf("[payment] leshua notify processed third_order_id=%s status=%s", params["third_order_id"], params["status"]) c.String(http.StatusOK, result.Message) } func currentUserID(c *gin.Context) (uint64, bool) { value, ok := c.Get(middleware.ContextUserID) if !ok { return 0, false } userID, ok := value.(uint64) return userID, ok } func parseID(c *gin.Context) (uint64, bool) { id, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil || id == 0 { response.BadRequest(c, "ID 不正确") return 0, false } return id, true } func writePaymentError(c *gin.Context, err error) { switch { case errors.Is(err, ErrDependencyUnavailable): response.ServiceUnavailable(c, "数据库未连接") case errors.Is(err, ErrPaymentUnavailable): response.Error(c, http.StatusBadGateway, "payment_unavailable", "支付渠道暂不可用") case errors.Is(err, ErrPaymentCannotStart), errors.Is(err, order.ErrOrderCannotPay): response.Error(c, http.StatusConflict, "payment_cannot_start", "当前订单不能支付") case errors.Is(err, ErrPaymentVerifyFailed): response.Error(c, http.StatusForbidden, "payment_verify_failed", "支付通知验签失败") case errors.Is(err, ErrPaymentNotFound), errors.Is(err, gorm.ErrRecordNotFound), order.IsNotFound(err): response.Error(c, http.StatusNotFound, "payment_not_found", "支付单不存在") case errors.Is(err, order.ErrListingUnavailable): response.Error(c, http.StatusConflict, "listing_unavailable", "该账号暂不可租") default: response.Error(c, http.StatusInternalServerError, "payment_error", "支付处理失败") } }