package payment import ( "errors" "io" "log" "net/http" "strconv" "hfb_sys/backend/internal/integrations/payment/lakala" "hfb_sys/backend/internal/integrations/payment/leshua" "hfb_sys/backend/internal/integrations/payment/shuncheng" "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(c.Request.Context(), 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(c.Request.Context(), userID, orderID) if err != nil { writePaymentError(c, err) return } response.OK(c, item) } func (h *Handler) QueryRefundStatus(c *gin.Context) { orderID, ok := parseID(c) if !ok { return } item, err := h.service.QueryRefundStatus(c.Request.Context(), orderID) if err != nil { writePaymentError(c, err) return } response.OK(c, item) } func (h *Handler) AdminList(c *gin.Context) { query, ok := parseAdminPaymentQuery(c) if !ok { return } result, err := h.service.AdminList(c.Request.Context(), query) if err != nil { writePaymentError(c, err) return } response.OK(c, result) } 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(c.Request.Context(), 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 (h *Handler) LakalaNotify(c *gin.Context) { body, err := io.ReadAll(io.LimitReader(c.Request.Body, 1<<20)) if err != nil { c.JSON(http.StatusOK, gin.H{"code": "FAIL", "message": "读取失败"}) return } params, err := lakala.ParsePayload(body) if err != nil { c.JSON(http.StatusOK, gin.H{"code": "FAIL", "message": "解析失败"}) return } rawPayload := string(body) contentType := c.GetHeader("Content-Type") authorization := c.GetHeader("Authorization") log.Printf( "[payment] lakala notify received third_order_id=%s provider_order_id=%s status=%s amount=%s content_type=%s raw_payload=%s", params["third_order_id"], params["provider_order_id"], params["status"], params["amount"], contentType, rawPayload, ) result, err := h.service.HandleNotify(c.Request.Context(), "lakala", params, rawPayload, contentType, authorization) if err != nil || result == nil || !result.OK { log.Printf("[payment] lakala notify failed third_order_id=%s err=%v", params["third_order_id"], err) c.JSON(http.StatusOK, gin.H{"code": "FAIL", "message": "失败"}) return } log.Printf("[payment] lakala notify processed third_order_id=%s status=%s", params["third_order_id"], params["status"]) c.JSON(http.StatusOK, gin.H{"code": "SUCCESS", "message": "执行成功"}) } func (h *Handler) ShunchengNotify(c *gin.Context) { body, err := io.ReadAll(io.LimitReader(c.Request.Body, 1<<20)) if err != nil { c.String(http.StatusOK, "FAIL") return } params, err := shuncheng.ParsePayload(body) if err != nil { c.String(http.StatusOK, "FAIL") return } rawPayload := string(body) contentType := c.GetHeader("Content-Type") authorization := c.GetHeader("Authorization") log.Printf( "[payment] shuncheng notify received third_order_id=%s schc_order_id=%s schc_refund_id=%s status=%s amount=%s content_type=%s raw_payload=%s", params["third_order_id"], params["schc_order_id"], params["schc_refund_id"], params["status"], params["amount"], contentType, rawPayload, ) result, err := h.service.HandleNotify(c.Request.Context(), "shuncheng", params, rawPayload, contentType, authorization) if err != nil || result == nil || !result.OK { log.Printf("[payment] shuncheng notify failed third_order_id=%s err=%v", params["third_order_id"], err) c.String(http.StatusOK, "FAIL") return } log.Printf("[payment] shuncheng 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 parseAdminPaymentQuery(c *gin.Context) (AdminPaymentQuery, bool) { var query AdminPaymentQuery if raw := c.Query("user_id"); raw != "" { value, err := strconv.ParseUint(raw, 10, 64) if err != nil || value == 0 { response.BadRequest(c, "用户ID不正确") return query, false } query.UserID = value } if raw := c.Query("order_id"); raw != "" { value, err := strconv.ParseUint(raw, 10, 64) if err != nil || value == 0 { response.BadRequest(c, "订单ID不正确") return query, false } query.OrderID = value } query.OrderNo = c.Query("order_no") query.BizType = c.Query("biz_type") query.Status = c.Query("status") query.Provider = c.Query("provider") page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) query.Page = page query.PageSize = pageSize return query, 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, ErrRefundCannotStart): response.Error(c, http.StatusConflict, "refund_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", "支付处理失败") } }