package handler import ( "errors" "net/http" "github.com/gin-gonic/gin" "github.com/wangjia/pay/internal/service" "github.com/wangjia/pay/internal/util" ) type OrderHandler struct { svc *service.OrderService } func NewOrderHandler(svc *service.OrderService) *OrderHandler { return &OrderHandler{svc: svc} } type createOrderRequest struct { ProductID uint64 `json:"product_id" binding:"required"` } // Create POST /api/v1/orders —— 下单,返回支付宝收银台跳转 URL。 func (h *OrderHandler) Create(c *gin.Context) { var req createOrderRequest if err := c.ShouldBindJSON(&req); err != nil { util.RespondError(c, http.StatusBadRequest, "bad_request", "参数错误:"+err.Error()) return } payURL, order, err := h.svc.Create(c.Request.Context(), req.ProductID, c.ClientIP()) if err != nil { if errors.Is(err, service.ErrProductNotFound) { util.RespondError(c, http.StatusNotFound, "product_not_found", err.Error()) return } util.RespondError(c, http.StatusInternalServerError, "create_failed", err.Error()) return } util.RespondSuccess(c, gin.H{ "pay_url": payURL, "out_trade_no": order.OutTradeNo, "amount": order.Amount, "subject": order.Subject, }) } // CreateQR POST /api/v1/orders/qr —— 扫码下单,返回二维码码串(前端渲染成二维码)。 func (h *OrderHandler) CreateQR(c *gin.Context) { var req createOrderRequest if err := c.ShouldBindJSON(&req); err != nil { util.RespondError(c, http.StatusBadRequest, "bad_request", "参数错误:"+err.Error()) return } qr, order, err := h.svc.CreateQR(c.Request.Context(), req.ProductID, c.ClientIP()) if err != nil { if errors.Is(err, service.ErrProductNotFound) { util.RespondError(c, http.StatusNotFound, "product_not_found", err.Error()) return } util.RespondError(c, http.StatusInternalServerError, "create_failed", err.Error()) return } util.RespondSuccess(c, gin.H{ "qr_code": qr, "out_trade_no": order.OutTradeNo, "amount": order.Amount, "subject": order.Subject, }) } // AlipayNotify POST /api/v1/notify/alipay —— 异步回调。 // 成功必须返回纯文本 "success",否则支付宝会按策略重发。 func (h *OrderHandler) AlipayNotify(c *gin.Context) { if err := h.svc.HandleAlipayNotify(c.Request.Context(), c.Request); err != nil { c.String(http.StatusOK, "failure") // 回 failure 让支付宝重试(也可记录后人工处理) return } c.String(http.StatusOK, "success") } // GetStatus GET /api/v1/orders/:out_trade_no —— 供结果页轮询订单状态。 func (h *OrderHandler) GetStatus(c *gin.Context) { out := c.Param("out_trade_no") o, err := h.svc.GetByOutTradeNo(out) if err != nil { util.RespondError(c, http.StatusNotFound, "order_not_found", "订单不存在") return } util.RespondSuccess(c, gin.H{ "out_trade_no": o.OutTradeNo, "subject": o.Subject, "amount": o.Amount, "status": o.Status, "trade_no": o.TradeNo, "paid_at": o.PaidAt, }) }