diff --git a/api/core/types/config.go b/api/core/types/config.go index dda50c00..fa713e7e 100644 --- a/api/core/types/config.go +++ b/api/core/types/config.go @@ -57,7 +57,8 @@ type AlipayConfig struct { PublicKey string // 用户公钥文件路径 AlipayPublicKey string // 支付宝公钥文件路径 RootCert string // Root 秘钥路径 - NotifyHost string // 通知回调地址 + NotifyURL string // 异步通知地址 + ReturnURL string // 同步回调地址 } type WechatPayConfig struct { @@ -67,16 +68,18 @@ type WechatPayConfig struct { SerialNo string // 商户证书的证书序列号 PrivateKey string // 用户私钥文件路径 ApiV3Key string // API V3 秘钥 - NotifyHost string // 通知回调地址 + NotifyURL string // 异步通知地址 + ReturnURL string // 同步回调地址 } type HuPiPayConfig struct { //虎皮椒第四方支付配置 - Enabled bool // 是否启用该支付通道 - Name string // 支付名称,如:wechat/alipay - AppId string // App ID - AppSecret string // app 密钥 - ApiURL string // 支付网关 - NotifyHost string // 通知回调地址 + Enabled bool // 是否启用该支付通道 + Name string // 支付名称,如:wechat/alipay + AppId string // App ID + AppSecret string // app 密钥 + ApiURL string // 支付网关 + NotifyURL string // 异步通知地址 + ReturnURL string // 同步回调地址 } // GeekPayConfig GEEK支付配置 @@ -85,7 +88,8 @@ type GeekPayConfig struct { AppId string // 商户 ID PrivateKey string // 私钥 ApiURL string // API 网关 - NotifyHost string // 通知回调地址 + NotifyURL string // 异步通知地址 + ReturnURL string // 同步回调地址 } type XXLConfig struct { // XXL 任务调度配置 diff --git a/api/handler/payment_handler.go b/api/handler/payment_handler.go index 0fba56b4..3dd3108c 100644 --- a/api/handler/payment_handler.go +++ b/api/handler/payment_handler.go @@ -39,6 +39,7 @@ type PaymentHandler struct { geekPayService *payment.GeekPayService wechatPayService *payment.WechatPayService snowflake *service.Snowflake + userService *service.UserService fs embed.FS lock sync.Mutex signKey string // 用来签名的随机秘钥 @@ -51,6 +52,7 @@ func NewPaymentHandler( geekPayService *payment.GeekPayService, wechatPayService *payment.WechatPayService, db *gorm.DB, + userService *service.UserService, snowflake *service.Snowflake, fs embed.FS) *PaymentHandler { return &PaymentHandler{ @@ -59,6 +61,7 @@ func NewPaymentHandler( geekPayService: geekPayService, wechatPayService: wechatPayService, snowflake: snowflake, + userService: userService, fs: fs, lock: sync.Mutex{}, BaseHandler: BaseHandler{ @@ -72,12 +75,12 @@ func NewPaymentHandler( func (h *PaymentHandler) Pay(c *gin.Context) { payWay := c.Query("pay_way") payType := c.Query("pay_type") - productId := c.Query("pid") + productId := c.Query("product_id") device := c.Query("device") userId := c.Query("user_id") var product model.Product - err := h.DB.First(&product, productId).Error + err := h.DB.Debug().Where("id", productId).First(&product).Error if err != nil { resp.ERROR(c, "Product not found") return @@ -126,23 +129,20 @@ func (h *PaymentHandler) Pay(c *gin.Context) { var payURL string if payWay == "alipay" { // 支付宝 money := fmt.Sprintf("%.2f", order.Amount) - notifyURL := fmt.Sprintf("%s/api/payment/notify/alipay", h.App.Config.AlipayConfig.NotifyHost) - returnURL := fmt.Sprintf("%s/member", h.App.Config.AlipayConfig.NotifyHost) if device == "mobile" { payURL, err = h.alipayService.PayMobile(payment.AlipayParams{ OutTradeNo: orderNo, Subject: product.Name, TotalFee: money, - ReturnURL: returnURL, - NotifyURL: notifyURL, + NotifyURL: h.App.Config.AlipayConfig.NotifyURL, }) } else { payURL, err = h.alipayService.PayPC(payment.AlipayParams{ OutTradeNo: orderNo, Subject: product.Name, TotalFee: money, - ReturnURL: returnURL, - NotifyURL: notifyURL, + ReturnURL: h.App.Config.AlipayConfig.ReturnURL, + NotifyURL: h.App.Config.AlipayConfig.NotifyURL, }) } if err != nil { @@ -155,7 +155,8 @@ func (h *PaymentHandler) Pay(c *gin.Context) { TradeOrderId: orderNo, TotalFee: fmt.Sprintf("%f", order.Amount), Title: order.Subject, - NotifyURL: fmt.Sprintf("%s/api/payment/notify/hupi", h.App.Config.HuPiPayConfig.NotifyHost), + NotifyURL: h.App.Config.HuPiPayConfig.NotifyURL, + ReturnURL: h.App.Config.HuPiPayConfig.ReturnURL, WapName: "GeekAI助手", } r, err := h.huPiPayService.Pay(params) @@ -175,8 +176,6 @@ func (h *PaymentHandler) Pay(c *gin.Context) { c.Redirect(302, uri) } else if order.PayWay == "geek" { - notifyURL := fmt.Sprintf("%s/api/payment/notify/geek", h.App.Config.GeekPayConfig.NotifyHost) - returnURL := fmt.Sprintf("%s/member", h.App.Config.GeekPayConfig.NotifyHost) params := payment.GeekPayParams{ OutTradeNo: orderNo, Method: "web", @@ -185,8 +184,8 @@ func (h *PaymentHandler) Pay(c *gin.Context) { ClientIP: c.ClientIP(), Device: device, Type: payType, - ReturnURL: returnURL, - NotifyURL: notifyURL, + ReturnURL: h.App.Config.GeekPayConfig.ReturnURL, + NotifyURL: h.App.Config.GeekPayConfig.NotifyURL, } res, err := h.geekPayService.Pay(params) @@ -218,45 +217,26 @@ func (h *PaymentHandler) notify(orderNo string, tradeNo string) error { } var user model.User - res = h.DB.First(&user, order.UserId) - if res.Error != nil { - err := fmt.Errorf("error with fetch user info: %v", res.Error) - logger.Error(err) - return err + err := h.DB.First(&user, order.UserId).Error + if err != nil { + return fmt.Errorf("error with fetch user info: %v", res.Error) } var remark types.OrderRemark - err := utils.JsonDecode(order.Remark, &remark) + err = utils.JsonDecode(order.Remark, &remark) if err != nil { err := fmt.Errorf("error with decode order remark: %v", err) logger.Error(err) return err } - var opt string - var power int - if remark.Days > 0 { // VIP 充值 - if user.ExpiredTime >= time.Now().Unix() { - user.ExpiredTime = time.Unix(user.ExpiredTime, 0).AddDate(0, 0, remark.Days).Unix() - opt = "VIP充值,VIP 没到期,只延期不增加算力" - } else { - user.ExpiredTime = time.Now().AddDate(0, 0, remark.Days).Unix() - user.Power += h.App.SysConfig.VipMonthPower - power = h.App.SysConfig.VipMonthPower - opt = "VIP充值" - } - user.Vip = true - } else { // 充值点卡,直接增加次数即可 - user.Power += remark.Power - opt = "点卡充值" - power = remark.Power - } - - // 更新用户信息 - res = h.DB.Updates(&user) - if res.Error != nil { - err := fmt.Errorf("error with update user info: %v", res.Error) - logger.Error(err) + // 增加用户算力 + err = h.userService.IncreasePower(int(order.UserId), remark.Power, model.PowerLog{ + Type: types.PowerRecharge, + Model: order.PayWay, + Remark: fmt.Sprintf("充值算力,金额:%f,订单号:%s", order.Amount, order.OrderNo), + }) + if err != nil { return err } @@ -264,29 +244,16 @@ func (h *PaymentHandler) notify(orderNo string, tradeNo string) error { order.PayTime = time.Now().Unix() order.Status = types.OrderPaidSuccess order.TradeNo = tradeNo - res = h.DB.Updates(&order) - if res.Error != nil { - err := fmt.Errorf("error with update order info: %v", res.Error) - logger.Error(err) - return err + err = h.DB.Updates(&order).Error + if err != nil { + return fmt.Errorf("error with update order info: %v", err) } // 更新产品销量 - h.DB.Model(&model.Product{}).Where("id = ?", order.ProductId).UpdateColumn("sales", gorm.Expr("sales + ?", 1)) - - // 记录算力充值日志 - if power > 0 { - h.DB.Create(&model.PowerLog{ - UserId: user.Id, - Username: user.Username, - Type: types.PowerRecharge, - Amount: power, - Balance: user.Power, - Mark: types.PowerAdd, - Model: order.PayWay, - Remark: fmt.Sprintf("%s,金额:%f,订单号:%s", opt, order.Amount, order.OrderNo), - CreatedAt: time.Now(), - }) + err = h.DB.Model(&model.Product{}).Where("id = ?", order.ProductId). + UpdateColumn("sales", gorm.Expr("sales + ?", 1)).Error + if err != nil { + return fmt.Errorf("error with update product sales: %v", err) } return nil diff --git a/web/src/router.js b/web/src/router.js index e1ca5cd6..c805856b 100644 --- a/web/src/router.js +++ b/web/src/router.js @@ -139,9 +139,15 @@ const routes = [ { path: '/admin/login', name: 'admin-login', - meta: {title: 'Geek-AI 控制台登录'}, + meta: {title: '控制台登录'}, component: () => import('@/views/admin/Login.vue'), }, + { + path: '/payReturn', + name: 'pay-return', + meta: {title: '支付回调'}, + component: () => import('@/views/PayReturn.vue'), + }, { name: 'admin', path: '/admin', diff --git a/web/src/views/Member.vue b/web/src/views/Member.vue index c64d6f86..6b5bbda9 100644 --- a/web/src/views/Member.vue +++ b/web/src/views/Member.vue @@ -106,6 +106,12 @@ + +
+ 支付成功 + 支付失败 +
+
@@ -143,6 +149,7 @@ const payWays = ref([]) const vipInfoText = ref("") const store = useSharedStore() const profileKey = ref(0) +const showDialog = ref(false) onMounted(() => { @@ -193,8 +200,9 @@ const pay = (product, payWay) => { user_id: user.value.id, device: "jump" }).then(res => { - window.open(res.data, '_blank'); + window.open(res.data, '_blank'); loading.value = false + showDialog.value = true }).catch(e => { setTimeout(() => { ElMessage.error("生成支付订单失败:" + e.message) @@ -210,6 +218,13 @@ const redeemCallback = (success) => { } } +const payCallback = (success) => { + showDialog.value = false + if (success) { + profileKey.value += 1 + } +} +