package app import ( "net/http" "bindbox-game/configs" "bindbox-game/internal/code" "bindbox-game/internal/pkg/core" "bindbox-game/internal/pkg/validation" "bindbox-game/internal/pkg/pay" "bindbox-game/internal/repository/mysql/model" ) type jsapiPreorderRequest struct { OrderNo string `json:"order_no" form:"order_no"` OpenID string `json:"openid" form:"openid"` } type jsapiPreorderResponse struct { TimeStamp string `json:"timeStamp"` NonceStr string `json:"nonceStr"` Package string `json:"package"` SignType string `json:"signType"` PaySign string `json:"paySign"` } // WechatJSAPIPreorder 小程序微信支付预下单并返回调起参数 // 入参:order_no(业务订单号)、openid(用户在小程序的openid) // 返回:用于wx.requestPayment的参数(timeStamp/nonceStr/package/signType/paySign) // 错误:参数绑定失败、配置缺失、订单不存在或状态不合法 func (h *handler) WechatJSAPIPreorder() core.HandlerFunc { return func(ctx core.Context) { req := new(jsapiPreorderRequest) rsp := new(jsapiPreorderResponse) if err := ctx.ShouldBindForm(req); err != nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err))) return } if ok, err := pay.ValidateConfig(); !ok { ctx.AbortWithError(core.Error(http.StatusBadRequest, 140001, err.Error())) return } c := configs.Get() if req.OrderNo == "" || req.OpenID == "" { ctx.AbortWithError(core.Error(http.StatusBadRequest, 140002, "order_no/openid required")) return } // 查询订单并校验状态=1 待支付,归属当前用户 order, err := h.readDB.Orders.WithContext(ctx.RequestContext()).Where(h.readDB.Orders.OrderNo.Eq(req.OrderNo), h.readDB.Orders.UserID.Eq(int64(ctx.SessionUserInfo().Id)), h.readDB.Orders.Status.Eq(1)).First() if err != nil || order == nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, 140006, "order not found or status invalid")) return } wc, err := pay.NewWechatPayClient(ctx.RequestContext()) if err != nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, 140004, err.Error())) return } prepayID, err := wc.JSAPIPrepay(ctx.RequestContext(), c.Wechat.AppID, c.WechatPay.MchID, "订单"+req.OrderNo, req.OrderNo, order.ActualAmount, req.OpenID, c.WechatPay.NotifyURL) if err != nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, 140005, err.Error())) return } pre := &model.PaymentPreorders{ OrderID: order.ID, OrderNo: order.OrderNo, OutTradeNo: order.OrderNo, PrepayID: prepayID, AmountTotal: order.ActualAmount, PayerOpenid: req.OpenID, NotifyURL: c.WechatPay.NotifyURL, Status: "created", } if err := h.writeDB.PaymentPreorders.WithContext(ctx.RequestContext()).Omit(h.writeDB.PaymentPreorders.ExpiredAt).Create(pre); err == nil { _, _ = h.writeDB.Orders.WithContext(ctx.RequestContext()).Where(h.readDB.Orders.ID.Eq(order.ID)).Updates(map[string]any{ h.readDB.Orders.PayPreorderID.ColumnName().String(): pre.ID, }) } ts, nonce, pkg, signType, paySign, err := pay.BuildJSAPIParams(c.Wechat.AppID, prepayID) if err != nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, 140003, err.Error())) return } rsp.TimeStamp = ts rsp.NonceStr = nonce rsp.Package = pkg rsp.SignType = signType rsp.PaySign = paySign ctx.Payload(rsp) } }