refactor(utils): 修复密码哈希比较逻辑错误 feat(user): 新增按状态筛选优惠券接口 docs: 添加虚拟发货与任务中心相关文档 fix(wechat): 修正Code2Session上下文传递问题 test: 补充订单折扣与积分转换测试用例 build: 更新配置文件与构建脚本 style: 清理多余的空行与注释
103 lines
4.5 KiB
Go
103 lines
4.5 KiB
Go
package app
|
||
|
||
import (
|
||
"net/http"
|
||
|
||
"bindbox-game/configs"
|
||
"bindbox-game/internal/code"
|
||
"bindbox-game/internal/pkg/core"
|
||
"bindbox-game/internal/pkg/pay"
|
||
"bindbox-game/internal/pkg/validation"
|
||
"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)
|
||
// 错误:参数绑定失败、配置缺失、订单不存在或状态不合法
|
||
// @Summary 小程序微信支付预下单
|
||
// @Description 根据`order_no`与`openid`创建或复用JSAPI预下单,返回`wx.requestPayment`调起参数;订单需为当前登录用户且状态为待支付
|
||
// @Tags APP端.支付
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Security LoginVerifyToken
|
||
// @Param RequestBody body jsapiPreorderRequest true "请求参数:业务订单号与openid"
|
||
// @Success 200 {object} jsapiPreorderResponse
|
||
// @Failure 400 {object} code.Failure "参数错误/配置缺失/订单不存在或状态不合法/微信预下单失败/签名构建失败"
|
||
// @Router /api/app/pay/wechat/jsapi/preorder [post]
|
||
func (h *handler) WechatJSAPIPreorder() core.HandlerFunc {
|
||
return func(ctx core.Context) {
|
||
req := new(jsapiPreorderRequest)
|
||
rsp := new(jsapiPreorderResponse)
|
||
if err := ctx.ShouldBindJSON(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
|
||
}
|
||
// 幂等:若已存在同一 out_trade_no 的预下单记录,直接复用其 prepay_id
|
||
existed, _ := h.readDB.PaymentPreorders.WithContext(ctx.RequestContext()).Where(h.readDB.PaymentPreorders.OutTradeNo.Eq(order.OrderNo)).First()
|
||
var prepayID string
|
||
if existed != nil {
|
||
prepayID = existed.PrepayID
|
||
if order.PayPreorderID == 0 {
|
||
_, _ = h.writeDB.Orders.WithContext(ctx.RequestContext()).Where(h.readDB.Orders.ID.Eq(order.ID)).Updates(map[string]any{
|
||
h.readDB.Orders.PayPreorderID.ColumnName().String(): existed.ID,
|
||
})
|
||
}
|
||
} else {
|
||
wc, err := pay.NewWechatPayClient(ctx.RequestContext())
|
||
if err != nil {
|
||
ctx.AbortWithError(core.Error(http.StatusBadRequest, 140004, err.Error()))
|
||
return
|
||
}
|
||
pid, 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
|
||
}
|
||
prepayID = pid
|
||
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)
|
||
}
|
||
}
|