From e8741787826940af6a3dd702c69ec82fde73ee14 Mon Sep 17 00:00:00 2001 From: RockYang Date: Thu, 19 Sep 2024 14:42:25 +0800 Subject: [PATCH] wechat payment for pc is ready --- api/core/types/config.go | 12 ++--- api/handler/payment_handler.go | 68 +++++++++++++------------- api/main.go | 1 + api/service/payment/geekpay_service.go | 9 +++- api/service/payment/hupipay_serive.go | 20 ++++---- api/service/payment/wepay_service.go | 2 - web/src/assets/css/member.styl | 60 +++++------------------ web/src/views/Dalle.vue | 4 +- web/src/views/Member.vue | 27 ++++++++-- web/src/views/admin/Order.vue | 1 + 10 files changed, 98 insertions(+), 106 deletions(-) diff --git a/api/core/types/config.go b/api/core/types/config.go index fa713e7e..6fae56b1 100644 --- a/api/core/types/config.go +++ b/api/core/types/config.go @@ -74,7 +74,6 @@ type WechatPayConfig struct { type HuPiPayConfig struct { //虎皮椒第四方支付配置 Enabled bool // 是否启用该支付通道 - Name string // 支付名称,如:wechat/alipay AppId string // App ID AppSecret string // app 密钥 ApiURL string // 支付网关 @@ -85,11 +84,12 @@ type HuPiPayConfig struct { //虎皮椒第四方支付配置 // GeekPayConfig GEEK支付配置 type GeekPayConfig struct { Enabled bool - AppId string // 商户 ID - PrivateKey string // 私钥 - ApiURL string // API 网关 - NotifyURL string // 异步通知地址 - ReturnURL string // 同步回调地址 + AppId string // 商户 ID + PrivateKey string // 私钥 + ApiURL string // API 网关 + NotifyURL string // 异步通知地址 + ReturnURL string // 同步回调地址 + Methods []string // 支付方式 } type XXLConfig struct { // XXL 任务调度配置 diff --git a/api/handler/payment_handler.go b/api/handler/payment_handler.go index 3dd3108c..43ddf5d2 100644 --- a/api/handler/payment_handler.go +++ b/api/handler/payment_handler.go @@ -80,7 +80,7 @@ func (h *PaymentHandler) Pay(c *gin.Context) { userId := c.Query("user_id") var product model.Product - err := h.DB.Debug().Where("id", productId).First(&product).Error + err := h.DB.Where("id", productId).First(&product).Error if err != nil { resp.ERROR(c, "Product not found") return @@ -150,7 +150,7 @@ func (h *PaymentHandler) Pay(c *gin.Context) { return } } else if order.PayWay == "hupi" { // 虎皮椒支付 - params := payment.HuPiPayReq{ + r, err := h.huPiPayService.Pay(payment.HuPiPayParams{ Version: "1.1", TradeOrderId: orderNo, TotalFee: fmt.Sprintf("%f", order.Amount), @@ -158,23 +158,25 @@ func (h *PaymentHandler) Pay(c *gin.Context) { NotifyURL: h.App.Config.HuPiPayConfig.NotifyURL, ReturnURL: h.App.Config.HuPiPayConfig.ReturnURL, WapName: "GeekAI助手", - } - r, err := h.huPiPayService.Pay(params) + }) if err != nil { resp.ERROR(c, err.Error()) return } - c.Redirect(302, r.URL) + payURL = r.URL } else if order.PayWay == "wechat" { - //uri, err := h.wechatPayService.PayUrlNative(order.OrderNo, int(order.Amount*100), order.Subject) - uri, err := h.wechatPayService.PayUrlNative(payment.WechatPayParams{}) + payURL, err = h.wechatPayService.PayUrlNative(payment.WechatPayParams{ + OutTradeNo: orderNo, + TotalFee: int(order.Amount * 100), + Subject: order.Subject, + NotifyURL: h.App.Config.WechatPayConfig.NotifyURL, + ReturnURL: h.App.Config.WechatPayConfig.ReturnURL, + }) if err != nil { resp.ERROR(c, err.Error()) return } - - c.Redirect(302, uri) } else if order.PayWay == "geek" { params := payment.GeekPayParams{ OutTradeNo: orderNo, @@ -201,11 +203,9 @@ func (h *PaymentHandler) Pay(c *gin.Context) { // 异步通知回调公共逻辑 func (h *PaymentHandler) notify(orderNo string, tradeNo string) error { var order model.Order - res := h.DB.Where("order_no = ?", orderNo).First(&order) - if res.Error != nil { - err := fmt.Errorf("error with fetch order: %v", res.Error) - logger.Error(err) - return err + err := h.DB.Where("order_no = ?", orderNo).First(&order).Error + if err != nil { + return fmt.Errorf("error with fetch order: %v", err) } h.lock.Lock() @@ -217,17 +217,15 @@ func (h *PaymentHandler) notify(orderNo string, tradeNo string) error { } var user model.User - err := h.DB.First(&user, order.UserId).Error + err = h.DB.First(&user, order.UserId).Error if err != nil { - return fmt.Errorf("error with fetch user info: %v", res.Error) + return fmt.Errorf("error with fetch user info: %v", err) } var remark types.OrderRemark err = utils.JsonDecode(order.Remark, &remark) if err != nil { - err := fmt.Errorf("error with decode order remark: %v", err) - logger.Error(err) - return err + return fmt.Errorf("error with decode order remark: %v", err) } // 增加用户算力 @@ -266,15 +264,12 @@ func (h *PaymentHandler) GetPayWays(c *gin.Context) { payWays = append(payWays, gin.H{"pay_way": "alipay", "pay_type": "alipay"}) } if h.App.Config.HuPiPayConfig.Enabled { - payWays = append(payWays, gin.H{"pay_way": "hupi", "pay_type": h.App.Config.HuPiPayConfig.Name}) + payWays = append(payWays, gin.H{"pay_way": "hupi", "pay_type": "wxpay"}) } if h.App.Config.GeekPayConfig.Enabled { - payWays = append(payWays, gin.H{"pay_way": "geek", "pay_type": "alipay"}) - payWays = append(payWays, gin.H{"pay_way": "geek", "pay_type": "wxpay"}) - payWays = append(payWays, gin.H{"pay_way": "geek", "pay_type": "qqpay"}) - payWays = append(payWays, gin.H{"pay_way": "geek", "pay_type": "jdpay"}) - payWays = append(payWays, gin.H{"pay_way": "geek", "pay_type": "douyin"}) - payWays = append(payWays, gin.H{"pay_way": "geek", "pay_type": "paypal"}) + for _, v := range h.App.Config.GeekPayConfig.Methods { + payWays = append(payWays, gin.H{"pay_way": "geek", "pay_type": v}) + } } if h.App.Config.WechatPayConfig.Enabled { payWays = append(payWays, gin.H{"pay_way": "wechat", "pay_type": "wxpay"}) @@ -292,15 +287,17 @@ func (h *PaymentHandler) HuPiPayNotify(c *gin.Context) { orderNo := c.Request.Form.Get("trade_order_id") tradeNo := c.Request.Form.Get("open_order_id") - logger.Infof("收到虎皮椒订单支付回调,订单 NO:%s,交易流水号:%s", orderNo, tradeNo) + logger.Infof("收到虎皮椒订单支付回调,%+v", c.Request.Form) - if err = h.huPiPayService.Check(tradeNo); err != nil { + if err = h.huPiPayService.Check(orderNo); err != nil { logger.Error("订单校验失败:", err) c.String(http.StatusOK, "fail") return } + err = h.notify(orderNo, tradeNo) if err != nil { + logger.Error(err) c.String(http.StatusOK, "fail") return } @@ -316,18 +313,18 @@ func (h *PaymentHandler) AlipayNotify(c *gin.Context) { return } - // TODO:验证交易签名 - res := h.alipayService.TradeVerify(c.Request) - logger.Infof("验证支付结果:%+v", res) - if !res.Success() { - logger.Error("订单校验失败:", res.Message) + result := h.alipayService.TradeVerify(c.Request) + logger.Infof("收到支付宝商号订单支付回调:%+v", result) + if !result.Success() { + logger.Error("订单校验失败:", result.Message) c.String(http.StatusOK, "fail") return } tradeNo := c.Request.Form.Get("trade_no") - err = h.notify(res.OutTradeNo, tradeNo) + err = h.notify(result.OutTradeNo, tradeNo) if err != nil { + logger.Error(err) c.String(http.StatusOK, "fail") return } @@ -358,6 +355,7 @@ func (h *PaymentHandler) GeekPayNotify(c *gin.Context) { err := h.notify(params["out_trade_no"], params["trade_no"]) if err != nil { + logger.Error(err) c.String(http.StatusOK, "fail") return } @@ -374,6 +372,7 @@ func (h *PaymentHandler) WechatPayNotify(c *gin.Context) { } result := h.wechatPayService.TradeVerify(c.Request) + logger.Infof("收到微信商号订单支付回调:%+v", result) if !result.Success() { logger.Error("订单校验失败:", err) c.JSON(http.StatusBadRequest, gin.H{ @@ -385,6 +384,7 @@ func (h *PaymentHandler) WechatPayNotify(c *gin.Context) { err = h.notify(result.OutTradeNo, result.TradeId) if err != nil { + logger.Error(err) c.String(http.StatusOK, "fail") return } diff --git a/api/main.go b/api/main.go index 6d00d3fd..b0b637b4 100644 --- a/api/main.go +++ b/api/main.go @@ -377,6 +377,7 @@ func main() { group.POST("notify/alipay", h.AlipayNotify) group.GET("notify/geek", h.GeekPayNotify) group.POST("notify/wechat", h.WechatPayNotify) + group.POST("notify/hupi", h.HuPiPayNotify) }), fx.Invoke(func(s *core.AppServer, h *admin.ProductHandler) { group := s.Engine.Group("/api/admin/product/") diff --git a/api/service/payment/geekpay_service.go b/api/service/payment/geekpay_service.go index 7348c0d8..c5306c6f 100644 --- a/api/service/payment/geekpay_service.go +++ b/api/service/payment/geekpay_service.go @@ -8,6 +8,7 @@ package payment // * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ import ( + "crypto/tls" "encoding/json" "errors" "fmt" @@ -108,7 +109,13 @@ func (s *GeekPayService) sendRequest(endpoint string, params map[string]string) apiURL := fmt.Sprintf("%s/mapi.php", endpoint) logger.Infof(apiURL) - resp, err := http.PostForm(apiURL, form) + tr := &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, // 取消 SSL 证书验证 + }, + } + client := &http.Client{Transport: tr} + resp, err := client.PostForm(apiURL, form) if err != nil { return nil, err } diff --git a/api/service/payment/hupipay_serive.go b/api/service/payment/hupipay_serive.go index 69a2e211..b7266b7e 100644 --- a/api/service/payment/hupipay_serive.go +++ b/api/service/payment/hupipay_serive.go @@ -37,7 +37,7 @@ func NewHuPiPay(config *types.AppConfig) *HuPiPayService { } } -type HuPiPayReq struct { +type HuPiPayParams struct { AppId string `json:"appid"` Version string `json:"version"` TradeOrderId string `json:"trade_order_id"` @@ -53,7 +53,7 @@ type HuPiPayReq struct { WapUrl string `json:"wap_url"` } -type HuPiResp struct { +type HuPiPayResp struct { Openid interface{} `json:"openid"` UrlQrcode string `json:"url_qrcode"` URL string `json:"url"` @@ -62,7 +62,7 @@ type HuPiResp struct { } // Pay 执行支付请求操作 -func (s *HuPiPayService) Pay(params HuPiPayReq) (HuPiResp, error) { +func (s *HuPiPayService) Pay(params HuPiPayParams) (HuPiPayResp, error) { data := url.Values{} simple := strconv.FormatInt(time.Now().Unix(), 10) params.AppId = s.appId @@ -80,22 +80,22 @@ func (s *HuPiPayService) Pay(params HuPiPayReq) (HuPiResp, error) { apiURL := fmt.Sprintf("%s/payment/do.html", s.apiURL) resp, err := http.PostForm(apiURL, data) if err != nil { - return HuPiResp{}, fmt.Errorf("error with requst api: %v", err) + return HuPiPayResp{}, fmt.Errorf("error with requst api: %v", err) } defer resp.Body.Close() all, err := io.ReadAll(resp.Body) if err != nil { - return HuPiResp{}, fmt.Errorf("error with reading response: %v", err) + return HuPiPayResp{}, fmt.Errorf("error with reading response: %v", err) } - var res HuPiResp + var res HuPiPayResp err = utils.JsonDecode(string(all), &res) if err != nil { - return HuPiResp{}, fmt.Errorf("error with decode payment result: %v", err) + return HuPiPayResp{}, fmt.Errorf("error with decode payment result: %v", err) } if res.ErrCode != 0 { - return HuPiResp{}, fmt.Errorf("error with generate pay url: %s", res.ErrMsg) + return HuPiPayResp{}, fmt.Errorf("error with generate pay url: %s", res.ErrMsg) } return res, nil @@ -127,10 +127,10 @@ func (s *HuPiPayService) Sign(params url.Values) string { } // Check 校验订单状态 -func (s *HuPiPayService) Check(tradeNo string) error { +func (s *HuPiPayService) Check(outTradeNo string) error { data := url.Values{} data.Add("appid", s.appId) - data.Add("open_order_id", tradeNo) + data.Add("out_trade_order", outTradeNo) stamp := strconv.FormatInt(time.Now().Unix(), 10) data.Add("time", stamp) data.Add("nonce_str", stamp) diff --git a/api/service/payment/wepay_service.go b/api/service/payment/wepay_service.go index 11554e9f..2e2ddb44 100644 --- a/api/service/payment/wepay_service.go +++ b/api/service/payment/wepay_service.go @@ -65,7 +65,6 @@ func (s *WechatPayService) PayUrlNative(params WechatPayParams) (string, error) Set("out_trade_no", params.OutTradeNo). Set("time_expire", expire). Set("notify_url", params.NotifyURL). - Set("return_url", params.ReturnURL). SetBodyMap("amount", func(bm gopay.BodyMap) { bm.Set("total", params.TotalFee). Set("currency", "CNY") @@ -91,7 +90,6 @@ func (s *WechatPayService) PayUrlH5(params WechatPayParams) (string, error) { Set("out_trade_no", params.OutTradeNo). Set("time_expire", expire). Set("notify_url", params.NotifyURL). - Set("return_url", params.ReturnURL). SetBodyMap("amount", func(bm gopay.BodyMap) { bm.Set("total", params.TotalFee). Set("currency", "CNY") diff --git a/web/src/assets/css/member.styl b/web/src/assets/css/member.styl index 4dc2209f..60ec8adc 100644 --- a/web/src/assets/css/member.styl +++ b/web/src/assets/css/member.styl @@ -2,53 +2,6 @@ background-color: #282c34; height 100% - .el-dialog { - .el-dialog__body { - .pay-container { - .amount { - text-align center - - span { - color #f56c6c - } - } - - .count-down { - display flex - justify-content center - } - - .pay-qrcode { - display flex - justify-content center - - .el-image { - width 360px; - height 360px; - } - } - - .tip { - display flex - justify-content center - - .el-icon { - font-size 24px - } - - .text { - font-size: 16px - margin-left 10px - } - } - - .tip.success { - color #07c160 - } - } - } - } - .title { text-align center background-color #25272d @@ -223,4 +176,17 @@ } } +} + +.pay-dialog { + .product-info { + text-align center + color #333333 + font-size 16px + + .price { + color #f56c6c + font-weight 700 + } + } } \ No newline at end of file diff --git a/web/src/views/Dalle.vue b/web/src/views/Dalle.vue index 4d37c82b..bb88bae2 100644 --- a/web/src/views/Dalle.vue +++ b/web/src/views/Dalle.vue @@ -156,14 +156,14 @@ - + - + diff --git a/web/src/views/Member.vue b/web/src/views/Member.vue index 6b5bbda9..de06254d 100644 --- a/web/src/views/Member.vue +++ b/web/src/views/Member.vue @@ -106,8 +106,12 @@ - -
+ +
+
请使用微信扫码支付:¥{{price}}
+ +
+
支付成功 支付失败
@@ -128,6 +132,7 @@ import UserOrder from "@/components/UserOrder.vue"; import {useSharedStore} from "@/store/sharedata"; import BindEmail from "@/components/BindEmail.vue"; import ThirdLogin from "@/components/ThirdLogin.vue"; +import QRCode from "qrcode"; const list = ref([]) const vipImg = ref("/images/vip.png") @@ -150,6 +155,8 @@ const vipInfoText = ref("") const store = useSharedStore() const profileKey = ref(0) const showDialog = ref(false) +const qrImg = ref("") +const price = ref(0) onMounted(() => { @@ -200,9 +207,20 @@ const pay = (product, payWay) => { user_id: user.value.id, device: "jump" }).then(res => { - window.open(res.data, '_blank'); - loading.value = false showDialog.value = true + loading.value = false + if (payWay.pay_way === 'wechat') { + price.value = Number((product.price - product.discount).toFixed(2)) + QRCode.toDataURL(res.data, {width: 300, height: 300, margin: 2}, (error, url) => { + if (error) { + console.error(error) + } else { + qrImg.value = url; + } + }) + } else { + window.open(res.data, '_blank'); + } }).catch(e => { setTimeout(() => { ElMessage.error("生成支付订单失败:" + e.message) @@ -225,6 +243,7 @@ const payCallback = (success) => { } } +