diff --git a/server/api/subscribe.go b/server/api/subscribe.go index 82b36e4..de1b97a 100644 --- a/server/api/subscribe.go +++ b/server/api/subscribe.go @@ -1,6 +1,7 @@ package api import ( + "crm/common" "crm/models" "crm/response" "crm/service" @@ -25,8 +26,8 @@ func NewSubscribeApi() *SubscribeApi { func (s *SubscribeApi) Pay(context *gin.Context) { var param models.SubscribePayParam uid, _ := strconv.Atoi(context.Request.Header.Get("uid")) - err := context.ShouldBind(¶m); - if int64(uid) <= 0 || err != nil { + err := context.ShouldBind(¶m) + if int64(uid) <= 0 || err != nil { response.Result(response.ErrCodeParamInvalid, nil, context) return } @@ -36,18 +37,10 @@ func (s *SubscribeApi) Pay(context *gin.Context) { } // 支付成功回调 -func (s *SubscribeApi) Callback(context *gin.Context) { - context.Request.ParseForm() - var outTradeNo = context.Request.Form.Get("out_trade_no") - paySuccessURL, _ := s.subscribeService.Callback(outTradeNo) - context.Redirect(http.StatusMovedPermanently, paySuccessURL) -} - -// 支付通知 -func (s *SubscribeApi) Notify(context *gin.Context) { - context.Request.ParseForm() - var outTradeNo = context.Request.Form.Get("out_trade_no") - errCode := s.subscribeService.Notify(context.Request.Form, outTradeNo) +func (s *SubscribeApi) PayBack(context *gin.Context) { + notifyReq := common.GetAlipay().VerifySign(context.Request) + errCode := s.subscribeService.PayBack(notifyReq.GetString("out_trade_no")) + context.String(http.StatusOK, "%s", "success") response.Result(errCode, nil, context) } @@ -60,4 +53,4 @@ func (s *SubscribeApi) GetInfo(context *gin.Context) { } subscribeInfo, errCode := s.subscribeService.GetInfo(int64(uid)) response.Result(errCode, subscribeInfo, context) -} \ No newline at end of file +} diff --git a/server/dao/subscribe.go b/server/dao/subscribe.go index 373c724..5819c61 100644 --- a/server/dao/subscribe.go +++ b/server/dao/subscribe.go @@ -56,8 +56,8 @@ func (s *SubscribeDao) SetOrder(tradeNo string, param *models.SubscribePayOrder) return global.Rdb.Set(ctx, tradeNo, string(order), time.Minute*30).Err() } -func (s *SubscribeDao) GetOrder(orderNo string) (string, error) { - orderJson, err := global.Rdb.Get(ctx, orderNo).Result() +func (s *SubscribeDao) GetOrder(tradeNo string) (string, error) { + orderJson, err := global.Rdb.Get(ctx, tradeNo).Result() if err != nil { return StringNull, err } diff --git a/server/global/global.go b/server/global/global.go index 78573aa..70a69c0 100644 --- a/server/global/global.go +++ b/server/global/global.go @@ -3,8 +3,8 @@ package global import ( "crm/config" + "github.com/go-pay/gopay/alipay" "github.com/go-redis/redis/v9" - "github.com/smartwalle/alipay/v3" "gorm.io/gorm" ) diff --git a/server/initialize/router.go b/server/initialize/router.go index f76b997..d34893c 100644 --- a/server/initialize/router.go +++ b/server/initialize/router.go @@ -25,7 +25,7 @@ func Router() { route.POST("/user/register", api.NewUserApi().Register) route.POST("/user/pass", api.NewUserApi().ForgotPass) route.DELETE("/user/delete", api.NewUserApi().Delete) - route.GET("/subscribe/callback", api.NewSubscribeApi().Callback) + route.POST("/subscribe/payback", api.NewSubscribeApi().PayBack) // 初始化数据 route.POST("/init/data", api.InitData) @@ -73,7 +73,6 @@ func Router() { // 订阅模块 route.GET("/subscribe/info", api.NewSubscribeApi().GetInfo) route.POST("/subscribe/pay", api.NewSubscribeApi().Pay) - route.POST("/subscribe/notify", api.NewSubscribeApi().Notify) // 通知模块 route.GET("/notice/list", api.NewNoticeApi().GetList) diff --git a/server/models/subscribe.go b/server/models/subscribe.go index acf12d7..29b8b92 100644 --- a/server/models/subscribe.go +++ b/server/models/subscribe.go @@ -23,13 +23,14 @@ type SubscribeUpdateParam struct { } type SubscribePayParam struct { - Uid int64 `json:"uid" binding:"-"` - Version int `json:"version" binding:"required,oneof=2 3"` + Uid int64 `json:"uid" binding:"-"` + Duration int64 `json:"duration" binding:"required,oneof=30 90 180 365 730"` } type SubscribePayOrder struct { - Uid int64 `json:"uid"` - Version int `json:"version"` + Uid int64 `json:"uid"` + TradeNo string `json:"tradeNo"` + Duration int64 `json:"duration"` } type SubscribePayUrl struct { diff --git a/server/service/subscribe.go b/server/service/subscribe.go index 75971f4..3f994ac 100644 --- a/server/service/subscribe.go +++ b/server/service/subscribe.go @@ -1,23 +1,12 @@ package service import ( + "crm/common" "crm/dao" - "crm/global" "crm/models" "crm/response" "encoding/json" - "fmt" - "log" - "net/url" "time" - - "github.com/smartwalle/alipay/v3" - "github.com/smartwalle/xid" -) - -const ( - Paying = 1 // 支付中 - Payed = 2 // 已支付 ) type SubscribeService struct { @@ -37,134 +26,69 @@ func NewSubscribeService() *SubscribeService { func (s *SubscribeService) Pay(param models.SubscribePayParam) (*models.SubscribePayUrl, int) { // 构建订单支付信息 - tradeNo := fmt.Sprintf("%d", xid.Next()) - var p = alipay.TradePagePay{} - p.NotifyURL = global.Config.Alipay.NotifyURL - p.ReturnURL = global.Config.Alipay.ReturnURL - p.Subject = "支付测试:" + tradeNo - p.OutTradeNo = tradeNo - p.ProductCode = "FAST_INSTANT_TRADE_PAY" - switch param.Version { - case 2: - p.TotalAmount = "18.00" - case 3: - p.TotalAmount = "198.00" - } - - // 缓存订单信息 - order := &models.SubscribePayOrder{ - Uid: param.Uid, - Version: param.Version, - } - if err := s.subscribeDao.SetOrder(tradeNo, order); err != nil { - return nil, response.ErrCodeFailed - } - - // 返回支付链接 - payUrl, err := global.Alipay.TradePagePay(p) + tradeNo := common.GetAlipay().GenTradeNo() + totalAmount := float64(param.Duration) * 0.6 + payUrl, err := common.GetAlipay().PagePay(tradeNo, totalAmount) if err != nil { return nil, response.ErrCodeFailed } + + order := models.SubscribePayOrder{ + Uid: param.Uid, + TradeNo: tradeNo, + Duration: param.Duration, + } + + if err := s.subscribeDao.SetOrder(tradeNo, &order); err != nil { + return nil, response.ErrCodeFailed + } + subscribePayUrl := models.SubscribePayUrl{ - PayUrl: payUrl.String(), + PayUrl: payUrl, } return &subscribePayUrl, response.ErrCodeSuccess } // 支付成功回调 -func (s *SubscribeService) Callback(outTradeNo string) (string, int) { - - // 验证订单信息 - var p = alipay.TradeQuery{} - p.OutTradeNo = outTradeNo - rsp, err := global.Alipay.TradeQuery(p) - if err != nil { - log.Printf("验证订单 %s 信息发生错误: %s", outTradeNo, err.Error()) - return StringNull, response.ErrCodeFailed - } - if !rsp.IsSuccess() { - log.Printf("验证订单 %s 信息发生错误: %s-%s", outTradeNo, rsp.Content.Msg, rsp.Content.SubMsg) - return StringNull, response.ErrCodeFailed - } +func (s *SubscribeService) PayBack(outTradeNo string) int { // 获取订单信息 var order models.SubscribePayOrder orderJson, err := s.subscribeDao.GetOrder(outTradeNo) if err != nil { - return StringNull, response.ErrCodeFailed + return response.ErrCodeFailed } if err := json.Unmarshal([]byte(orderJson), &order); err != nil { - return StringNull, response.ErrCodeFailed + return response.ErrCodeFailed } // 创建订阅信息 - var expired int64 - var content string - switch order.Version { - case 2: - expired = time.Now().Unix() + int64(2592000) - content = SUBSCRIBE_NOTICE_TEMPLATE1 - case 3: - expired = time.Now().Unix() + int64(31104000) - content = SUBSCRIBE_NOTICE_TEMPLATE2 - } + expired := order.Duration * 24 * 60 * 60 if !s.subscribeDao.IsExists(order.Uid) { subscribe := models.SubscribeCreateParam{ Uid: order.Uid, - Version: order.Version, + Version: 2, Expired: expired, } if err := s.subscribeDao.Create(&subscribe); err != nil { - return StringNull, response.ErrCodeFailed + return response.ErrCodeFailed } } else { subscribe := models.SubscribeUpdateParam{ Uid: order.Uid, - Version: order.Version, Expired: expired, } if err := s.subscribeDao.Update(&subscribe); err != nil { - return StringNull, response.ErrCodeFailed + return response.ErrCodeFailed } } // 订阅通知 s.noticeDao.Create(&models.NoticeCreateParam{ - Content: content, + Content: SUBSCRIBE_NOTICE_TEMPLATE1, Creator: order.Uid, }) - return global.Config.Alipay.PaySuccessURL, response.ErrCodeSuccess -} - -// 异步验签 -func (s *SubscribeService) Notify(data url.Values, outTradeNo string) int { - ok, err := global.Alipay.VerifySign(data) - if err != nil { - log.Println("异步通知验证签名发生错误", err) - return response.ErrCodeFailed - } - - if !ok { - log.Println("异步通知验证签名未通过") - return response.ErrCodeFailed - } - - log.Println("异步通知验证签名通过") - - var p = alipay.TradeQuery{} - p.OutTradeNo = outTradeNo - rsp, err := global.Alipay.TradeQuery(p) - if err != nil { - log.Printf("异步通知验证订单 %s 信息发生错误: %s \n", outTradeNo, err.Error()) - return response.ErrCodeFailed - } - if !rsp.IsSuccess() { - log.Printf("异步通知验证订单 %s 信息发生错误: %s-%s \n", outTradeNo, rsp.Content.Msg, rsp.Content.SubMsg) - return response.ErrCodeFailed - } - - log.Printf("订单 %s 支付成功 \n", outTradeNo) return response.ErrCodeSuccess } diff --git a/web/src/views/Subscribe.vue b/web/src/views/Subscribe.vue index 0fb2cf4..b37c855 100644 --- a/web/src/views/Subscribe.vue +++ b/web/src/views/Subscribe.vue @@ -1,58 +1,90 @@ @@ -60,40 +92,74 @@ import { ref, reactive, onBeforeMount } from 'vue'; import { CheckCircleFilled } from '@ant-design/icons-vue'; import { subscribePay, getSubscribeInfo } from '../api/subscribe'; -import { useRouter } from 'vue-router' -import moment from 'moment' - -const router = useRouter() +import moment from 'moment'; +import { message, Modal } from 'ant-design-vue'; const version = ref(0) const expired = ref(undefined) -const payUrl = ref() const visible = ref(false) const disabled = ref(false) const activedClass = reactive(['card', 'card', 'card']) -const payResult = ref(false) -const title = ref('') -const buttonText = ref(undefined) +const subscribe = reactive({ + duration: 30, + payMode: 1, + payment: 0.00 +}) -const isClick = (index) => { - active.value = index -} +// 表单校验 +const rules = { + duration: [{ + required: true, + message: '请选择订阅时长', + trigger: 'blur', + }], + payMode: [{ + required: true, + message: '请选择支付方式', + trigger: 'blur', + }], + payment: [{ + required: true, + }], +}; + +const payResult = ref(false) +const subscribeFormRef = ref() // 初始化数据 onBeforeMount(() => { subscribeInfo() }) +// 点击购买 +const onBuy = () => { + visible.value = true +} + // 点击支付 -const onPay = (ver) => { - let param = { - version: ver +const onPay = () => { + if (subscribe.payMode == 2) { + message.error('暂不支持微信支付!') + return } - subscribePay(param).then((res) => { - if (res.data.code == 0) { - visible.value = false - payResult.value = true - window.open(res.data.data.payUrl, '_self') + subscribeFormRef.value.validateFields().then(() => { + let param = { + duration: subscribe.duration } + subscribePay(param).then((res) => { + if (res.data.code == 0) { + visible.value = false + payResult.value = true + Modal.info({ + title: '正在跳转到支付宝支付页面...', + centered: true, + okText: '返回', + onOk() { + window.stop() + }, + }); + window.open(res.data.data.payUrl, '_self') + } + }) }) } @@ -147,19 +213,19 @@ const subscribeInfo = () => { } .card { - min-height: 450px; + min-height: 480px; box-shadow: 0 1px 10px 0 rgb(33 41 48 / 5%); } .selected-free-card { - min-height: 450px; + min-height: 480px; box-shadow: 0 1px 10px 0 rgb(33 41 48 / 5%); border: 2px solid #ceebfa; background: #f0faff; } .selected-card { - min-height: 450px; + min-height: 480px; box-shadow: 0 1px 10px 0 rgb(33 41 48 / 5%); border: 2px solid #d6ddf9; background: #f3f6fd;