diff --git a/api/controller/Base.go b/api/controller/Base.go index 9d2e4e7..6c8cf8e 100644 --- a/api/controller/Base.go +++ b/api/controller/Base.go @@ -14,4 +14,5 @@ type Api struct { SystemMember // 会员配置 OrderMember // 订单-会员 CallBack // 回调 + Pay // 支付中心 } diff --git a/api/controller/Pay.go b/api/controller/Pay.go new file mode 100644 index 0000000..57ad902 --- /dev/null +++ b/api/controller/Pay.go @@ -0,0 +1,169 @@ +package controller + +import ( + "fmt" + "github.com/gin-gonic/gin" + "hepa-calc-api/api/dao" + "hepa-calc-api/api/model" + "hepa-calc-api/api/responses" + "hepa-calc-api/config" + "hepa-calc-api/extend/app" + "hepa-calc-api/global" + "hepa-calc-api/utils" + "net/http" + "time" +) + +type Pay struct{} + +// GetPayOrder 获取订单支付数据 +func (r *Pay) GetPayOrder(c *gin.Context) { + req := app.PayOrderRequest{} + if err := c.ShouldBindJSON(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } + + // 获取header签名 + requestSign := c.Request.Header.Get("sign") + if requestSign == "" { + responses.FailWithMessage("缺少签名", c) + return + } + + // 验证签名 + err := app.VerifySignature(req, requestSign) + if err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + var payStatus int // 支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款) + var orderStatus int // 订单状态(1:待支付 2:已完成 3:已取消) + var paymentAmountTotal int // 实际付款金额 + var userId int64 // 用户id + var orderNo string // 订单编号 + var goodName string // 商品名称 + var createdAt model.LocalTime // 订单创建时间 + var notifyUrl string // 回调通知地址 + + // 获取订单数据-单项 + orderSingleDao := dao.OrderSingleDao{} + maps := make(map[string]interface{}) + maps["order_no"] = req.OrderId + orderSingle, _ := orderSingleDao.GetOrderSingle(maps) + if orderSingle != nil { + payStatus = orderSingle.PayStatus + orderStatus = orderSingle.OrderStatus + paymentAmountTotal = int(orderSingle.PaymentAmountTotal * 100) + userId = orderSingle.UserId + orderNo = orderSingle.OrderNo + createdAt = orderSingle.CreatedAt + notifyUrl = config.C.Wechat.NotifyDomain + config.C.Wechat.SingleRefundNotifyUrl + + // 获取单项数据 + questionDao := dao.QuestionDao{} + question, err := questionDao.GetQuestionById(orderSingle.QuestionId) + if err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + goodName = question.QuestionTitle + } + + // 获取订单数据-会员 + orderMemberDao := dao.OrderMemberDao{} + maps = make(map[string]interface{}) + maps["order_no"] = req.OrderId + orderMember, _ := orderMemberDao.GetOrderMember(maps) + if orderMember != nil { + payStatus = orderMember.PayStatus + orderStatus = orderMember.OrderStatus + paymentAmountTotal = int(orderMember.PaymentAmountTotal * 100) + userId = orderMember.UserId + orderNo = orderMember.OrderNo + createdAt = orderMember.CreatedAt + notifyUrl = config.C.Wechat.NotifyDomain + config.C.Wechat.MemberRefundNotifyUrl + + // 获取对应数据 + systemMemberDao := dao.SystemMemberDao{} + systemMember, err := systemMemberDao.GetSystemMemberById(orderMember.SystemMemberId) + if err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + memberDays := fmt.Sprintf("%d", systemMember.MemberDays) + goodName = "肝病算一算" + memberDays + "会员" + } + + if orderSingle == nil && orderMember == nil { + c.JSON(http.StatusOK, gin.H{"code": 90002, "message": "订单不存在", "data": nil}) + return + } + + // 检测订单支付状态 + if payStatus == 2 { + c.JSON(http.StatusOK, gin.H{"code": 90000, "message": "订单已支付,请勿重新支付", "data": nil}) + return + } + + if payStatus == 3 { + c.JSON(http.StatusOK, gin.H{"code": 90003, "message": "订单支付中", "data": nil}) + return + } + + if payStatus != 1 { + c.JSON(http.StatusOK, gin.H{"code": 90004, "message": "订单支付失败", "data": nil}) + return + } + + // 检测订单取消状态 + if orderStatus == 3 { + c.JSON(http.StatusOK, gin.H{"code": 90001, "message": "订单已关闭", "data": nil}) + return + } + + if orderStatus == 2 { + c.JSON(http.StatusOK, gin.H{"code": 90004, "message": "订单支付失败", "data": nil}) + return + } + + // 验证订单过期支付时间 + now := time.Now() + validTime := time.Time(createdAt).Add(30 * time.Minute) + if validTime.Before(now) { + c.JSON(http.StatusOK, gin.H{"code": 90001, "message": "订单已关闭", "data": nil}) + return + } + + // 获取用户数据 + userDao := dao.UserDao{} + user, err := userDao.GetUserById(userId) + if err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 处理返回数据 + g := app.PayOrderDataResponse{ + AppId: config.C.Wechat.AppId, + Total: paymentAmountTotal, + Description: "肝病算一算", + OpenId: user.OpenId, + OutTradeNo: orderNo, + Attach: "", + NotifyUrl: notifyUrl, + GoodName: goodName, + } + + responses.OkWithData(g, c) + return +} diff --git a/api/router/router.go b/api/router/router.go index c05f7ed..eef51e2 100644 --- a/api/router/router.go +++ b/api/router/router.go @@ -123,6 +123,13 @@ func publicRouter(r *gin.Engine, api controller.Api) { } } } + + // 支付中心 + payGroup := r.Group("/pay") + { + // 获取订单支付数据 + payGroup.POST("/order", api.Pay.GetPayOrder) + } } // adminRouter 公共路由-验证权限 diff --git a/extend/app/pay.go b/extend/app/pay.go new file mode 100644 index 0000000..02fd9da --- /dev/null +++ b/extend/app/pay.go @@ -0,0 +1,53 @@ +package app + +import ( + "encoding/json" + "errors" + "fmt" +) + +// PayOrderRequest 获取订单支付请求数据 +type PayOrderRequest struct { + OrderId string `json:"orderId" label:"订单编号" validate:"required"` // 订单id +} + +// PayOrderDataResponse 获取订单支付返回数据-data +type PayOrderDataResponse struct { + AppId string `json:"appid"` // 公众号id + Total int `json:"total"` // 订单总金额(精确到分) + Description string `json:"description"` // 订单描述 + OpenId string `json:"openid"` // 下单用户 + OutTradeNo string `json:"out_trade_no"` // 商户订单 + Attach string `json:"attach"` // 附加信息 + NotifyUrl string `json:"notify_url"` // 异步接收微信支付结果通知的回调地址 + GoodName string `json:"goodName"` // 商品名称 +} + +// VerifySignature 验证签名 +func VerifySignature(req PayOrderRequest, requestSign string) error { + // 将 JSON 数据编码为字节数组 + jsonData, err := json.Marshal(req) + if err != nil { + return err + } + + maps := make(map[string]interface{}) + err = json.Unmarshal(jsonData, &maps) + if err != nil { + return err + } + + // 生成签名 + sign, err := GenSignature(maps) + if err != nil { + return err + } + + fmt.Println(sign) + // 对比签名 + if sign != requestSign { + return errors.New("签名错误") + } + + return nil +} diff --git a/utils/validator.go b/utils/validator.go index cc52a5d..143fcc9 100644 --- a/utils/validator.go +++ b/utils/validator.go @@ -1,13 +1,15 @@ package utils import ( + "errors" "github.com/go-playground/validator/v10" "hepa-calc-api/global" ) // Translate 检验并返回检验错误信息 func Translate(err error) (errMsg string) { - errs := err.(validator.ValidationErrors) + var errs validator.ValidationErrors + errors.As(err, &errs) for _, err := range errs { errMsg = err.Translate(global.Trans) }