mirror of
https://github.com/yangjian102621/geekai.git
synced 2025-09-26 21:26:38 +08:00
添加微信商户号支付支持
This commit is contained in:
parent
fbfa2a71a9
commit
a90f00f7a4
@ -99,7 +99,16 @@ WeChatBot = false
|
||||
AlipayPublicKey = "certs/alipay/alipayPublicCert.crt" # 支付宝公钥证书
|
||||
RootCert = "certs/alipay/alipayRootCert.crt" # 支付宝根证书
|
||||
NotifyURL = "https://ai.r9it.com/api/payment/alipay/notify" # 支付异步回调地址
|
||||
|
||||
[WxpayConfig]
|
||||
Enabled = false # 启用微信支付通道
|
||||
SandBox = false # 是否启用沙盒模式
|
||||
AppId = "" # AppId
|
||||
WxAppSecret = ""
|
||||
MchId = "" # 商户ID
|
||||
MchKey = "" # 应用私钥mchAPIv3Key
|
||||
CertificateSerialNo = "" #证书序列号
|
||||
PrivateKey = "certs/wx/apiclient_key.pem" # 应用私钥证书
|
||||
NotifyURL = "https://ai.r9it.com/api/payment/wxpay/notify" # 支付异步回调地址
|
||||
[HuPiPayConfig]
|
||||
Enabled = false
|
||||
Name = "wechat"
|
||||
@ -121,4 +130,4 @@ WeChatBot = false
|
||||
AppId = "" # 商户 ID
|
||||
PrivateKey = "" # 秘钥
|
||||
ApiURL = "https://payjs.cn"
|
||||
NotifyURL = "https://ai.r9it.com/api/payment/payjs/notify" # 异步回调地址,域名改成你自己的
|
||||
NotifyURL = "https://ai.r9it.com/api/payment/payjs/notify" # 异步回调地址,域名改成你自己的
|
||||
|
@ -21,12 +21,12 @@ type AppConfig struct {
|
||||
MjPlusConfigs []MjPlusConfig // MJ plus config
|
||||
WeChatBot bool // 是否启用微信机器人
|
||||
SdConfigs []StableDiffusionConfig // sd AI draw service pool
|
||||
|
||||
XXLConfig XXLConfig
|
||||
AlipayConfig AlipayConfig
|
||||
HuPiPayConfig HuPiPayConfig
|
||||
SmtpConfig SmtpConfig // 邮件发送配置
|
||||
JPayConfig JPayConfig // payjs 支付配置
|
||||
XXLConfig XXLConfig
|
||||
AlipayConfig AlipayConfig
|
||||
WxpayConfig WxpayConfig
|
||||
HuPiPayConfig HuPiPayConfig
|
||||
SmtpConfig SmtpConfig // 邮件发送配置
|
||||
JPayConfig JPayConfig // payjs 支付配置
|
||||
}
|
||||
|
||||
type SmtpConfig struct {
|
||||
@ -77,6 +77,19 @@ type AlipayConfig struct {
|
||||
ReturnURL string // 支付成功返回地址
|
||||
}
|
||||
|
||||
type WxpayConfig struct {
|
||||
Enabled bool // 是否启用该支付通道
|
||||
SandBox bool // 是否沙盒环境
|
||||
AppId string // 应用 ID
|
||||
WxAppSecret string // 应用 Secret
|
||||
MchId string // 商户 ID
|
||||
MchKey string // 商户key
|
||||
CertificateSerialNo string // 商户key
|
||||
PrivateKey string // 商户私密钥文件地址
|
||||
NotifyURL string // 异步通知回调
|
||||
ReturnURL string // 支付成功返回地址
|
||||
}
|
||||
|
||||
type HuPiPayConfig struct { //虎皮椒第四方支付配置
|
||||
Enabled bool // 是否启用该支付通道
|
||||
Name string // 支付名称,如:wechat/alipay
|
||||
|
@ -26,12 +26,15 @@ require (
|
||||
require github.com/xxl-job/xxl-job-executor-go v1.2.0
|
||||
|
||||
require (
|
||||
github.com/chanxuehong/wechat v0.0.0-20230222024006-36f0325263cd
|
||||
github.com/mojocn/base64Captcha v1.3.1
|
||||
github.com/shopspring/decimal v1.3.1
|
||||
github.com/syndtr/goleveldb v1.0.0
|
||||
github.com/wechatpay-apiv3/wechatpay-go v0.2.18
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/chanxuehong/rand v0.0.0-20211009035549-2f07823e8e99 // indirect
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect
|
||||
golang.org/x/image v0.0.0-20190501045829-6d32002ffd75 // indirect
|
||||
|
@ -1,5 +1,7 @@
|
||||
github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
|
||||
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/agiledragon/gomonkey v2.0.2+incompatible h1:eXKi9/piiC3cjJD1658mEE2o3NjkJ5vDLgYjCQu0Xlw=
|
||||
github.com/agiledragon/gomonkey v2.0.2+incompatible/go.mod h1:2NGfXu1a80LLr2cmWXGBDaHEjb1idR6+FVlX5T3D9hw=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.62.405 h1:cKNFQmeCQFN0WNfjScKoVrGi7vXxTVbkCvCqSrOf+P4=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.62.405/go.mod h1:Api2AkmMgGaSUAhmk76oaFObkoeCPc/bKAqcyplPODs=
|
||||
github.com/aliyun/aliyun-oss-go-sdk v2.2.9+incompatible h1:Sg/2xHwDrioHpxTN6WMiwbXTpUEinBpHsN7mG21Rc2k=
|
||||
@ -12,6 +14,11 @@ github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s
|
||||
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chanxuehong/rand v0.0.0-20211009035549-2f07823e8e99 h1:K62Lb6bsgLOB++z/VAvRvtiEBdNCuMfmQGTGGWMdPpM=
|
||||
github.com/chanxuehong/rand v0.0.0-20211009035549-2f07823e8e99/go.mod h1:9+sJ9zvvkXC5sPjPEZM3Jpb9n2Q2VtcrGZly0UHYF5I=
|
||||
github.com/chanxuehong/util v0.0.0-20200304121633-ca8141845b13/go.mod h1:XEYt99iTxMqkv+gW85JX/DdUINHUe43Sbe5AtqSaDAQ=
|
||||
github.com/chanxuehong/wechat v0.0.0-20230222024006-36f0325263cd h1:v3JNsFZmplLO/Cmiyr/rGvR7lW1ld9lB+d5h4yR0MTI=
|
||||
github.com/chanxuehong/wechat v0.0.0-20230222024006-36f0325263cd/go.mod h1:mysjrtCs9MmN8hqDf4/mc4eQ26Rt9s1p5oO+fhJlLB4=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||
@ -211,6 +218,8 @@ github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVK
|
||||
github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
|
||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/wechatpay-apiv3/wechatpay-go v0.2.18 h1:vj5tvSmnEIz3ZsnFNNUzg+3Z46xgNMJbrO4aD4wP15w=
|
||||
github.com/wechatpay-apiv3/wechatpay-go v0.2.18/go.mod h1:A254AUBVB6R+EqQFo3yTgeh7HtyqRRtN2w9hQSOrd4Q=
|
||||
github.com/xxl-job/xxl-job-executor-go v1.2.0 h1:MTl2DpwrK2+hNjRRks2k7vB3oy+3onqm9OaSarneeLQ=
|
||||
github.com/xxl-job/xxl-job-executor-go v1.2.0/go.mod h1:bUFhz/5Irp9zkdYk5MxhQcDDT6LlZrI8+rv5mHtQ1mo=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
|
@ -34,6 +34,6 @@ func (h *ConfigHandler) Get(c *gin.Context) {
|
||||
resp.ERROR(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
value["wxAppId"] = h.App.Config.WxpayConfig.AppId
|
||||
resp.SUCCESS(c, value)
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import (
|
||||
|
||||
const (
|
||||
PayWayAlipay = "支付宝"
|
||||
PayWayWxPay = "微信"
|
||||
PayWayXunHu = "虎皮椒"
|
||||
PayWayJs = "PayJS"
|
||||
)
|
||||
@ -32,6 +33,7 @@ const (
|
||||
type PaymentHandler struct {
|
||||
BaseHandler
|
||||
alipayService *payment.AlipayService
|
||||
wxpayService *payment.WxpayService
|
||||
huPiPayService *payment.HuPiPayService
|
||||
js *payment.PayJS
|
||||
snowflake *service.Snowflake
|
||||
@ -42,6 +44,7 @@ type PaymentHandler struct {
|
||||
func NewPaymentHandler(
|
||||
server *core.AppServer,
|
||||
alipayService *payment.AlipayService,
|
||||
wxService *payment.WxpayService,
|
||||
huPiPayService *payment.HuPiPayService,
|
||||
js *payment.PayJS,
|
||||
db *gorm.DB,
|
||||
@ -49,6 +52,7 @@ func NewPaymentHandler(
|
||||
fs embed.FS) *PaymentHandler {
|
||||
return &PaymentHandler{
|
||||
alipayService: alipayService,
|
||||
wxpayService: wxService,
|
||||
huPiPayService: huPiPayService,
|
||||
js: js,
|
||||
snowflake: snowflake,
|
||||
@ -99,6 +103,23 @@ func (h *PaymentHandler) DoPay(c *gin.Context) {
|
||||
|
||||
c.Redirect(302, uri)
|
||||
return
|
||||
} else if payWay == "wxpay" { // 微信
|
||||
userId := h.GetLoginUserId(c)
|
||||
var user model.User
|
||||
res = h.DB.First(&user, userId)
|
||||
if res.Error != nil {
|
||||
resp.ERROR(c, "Invalid user ID")
|
||||
return
|
||||
}
|
||||
// 生成支付签名
|
||||
signInfo, err := h.wxpayService.PayUrlMobile(user, order)
|
||||
if err != nil {
|
||||
resp.ERROR(c, "error with generating Pay URL: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
resp.SUCCESS(c, signInfo)
|
||||
return
|
||||
} else if payWay == "hupi" { // 虎皮椒支付
|
||||
params := payment.HuPiPayReq{
|
||||
Version: "1.1",
|
||||
@ -106,7 +127,7 @@ func (h *PaymentHandler) DoPay(c *gin.Context) {
|
||||
TotalFee: fmt.Sprintf("%f", order.Amount),
|
||||
Title: order.Subject,
|
||||
NotifyURL: h.App.Config.HuPiPayConfig.NotifyURL,
|
||||
WapName: "极客学长",
|
||||
WapName: "AI小墨",
|
||||
}
|
||||
r, err := h.huPiPayService.Pay(params)
|
||||
if err != nil {
|
||||
@ -153,7 +174,27 @@ func (h *PaymentHandler) OrderQuery(c *gin.Context) {
|
||||
counter++
|
||||
}
|
||||
|
||||
resp.SUCCESS(c, gin.H{"status": order.Status})
|
||||
resp.SUCCESS(c, gin.H{"status": order.Status, "amount": order.Amount, "expire": ""})
|
||||
}
|
||||
|
||||
// OrderQueryAmount 查询订单状态
|
||||
func (h *PaymentHandler) OrderQueryAmount(c *gin.Context) {
|
||||
var data struct {
|
||||
OrderNo string `json:"order_no"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&data); err != nil {
|
||||
resp.ERROR(c, types.InvalidArgs)
|
||||
return
|
||||
}
|
||||
|
||||
var order model.Order
|
||||
res := h.DB.Where("order_no = ?", data.OrderNo).First(&order)
|
||||
if res.Error != nil {
|
||||
resp.ERROR(c, "Order not found")
|
||||
return
|
||||
}
|
||||
h.DB.Model(&order).UpdateColumn("status", types.OrderScanned)
|
||||
resp.SUCCESS(c, gin.H{"status": order.Status, "amount": order.Amount, "createTime": order.CreatedAt.Unix()})
|
||||
}
|
||||
|
||||
// PayQrcode 生成支付 URL 二维码
|
||||
@ -196,6 +237,9 @@ func (h *PaymentHandler) PayQrcode(c *gin.Context) {
|
||||
case "payjs":
|
||||
payWay = PayWayJs
|
||||
notifyURL = h.App.Config.JPayConfig.NotifyURL
|
||||
case "wxpay":
|
||||
payWay = PayWayWxPay
|
||||
notifyURL = h.App.Config.WxpayConfig.NotifyURL
|
||||
default:
|
||||
payWay = PayWayAlipay
|
||||
notifyURL = h.App.Config.AlipayConfig.NotifyURL
|
||||
@ -247,6 +291,8 @@ func (h *PaymentHandler) PayQrcode(c *gin.Context) {
|
||||
var logo string
|
||||
if data.PayWay == "alipay" {
|
||||
logo = "res/img/alipay.jpg"
|
||||
} else if data.PayWay == "wxpay" {
|
||||
logo = "res/img/wechat-pay.jpg"
|
||||
} else if data.PayWay == "hupi" {
|
||||
if h.App.Config.HuPiPayConfig.Name == "wechat" {
|
||||
logo = "res/img/wechat-pay.jpg"
|
||||
@ -268,6 +314,9 @@ func (h *PaymentHandler) PayQrcode(c *gin.Context) {
|
||||
}
|
||||
|
||||
imageURL := fmt.Sprintf("%s://%s/api/payment/doPay?order_no=%s&pay_way=%s", parse.Scheme, parse.Host, orderNo, data.PayWay)
|
||||
if data.PayWay == "wxpay" {
|
||||
imageURL = fmt.Sprintf("%s://%s/mobile/payment?order_no=%s&pay_way=%s", parse.Scheme, parse.Host, orderNo, data.PayWay)
|
||||
}
|
||||
imgData, err := utils.GenQrcode(imageURL, 400, file)
|
||||
if err != nil {
|
||||
resp.ERROR(c, err.Error())
|
||||
@ -325,7 +374,7 @@ func (h *PaymentHandler) Mobile(c *gin.Context) {
|
||||
NotifyURL: notifyURL,
|
||||
ReturnURL: returnURL,
|
||||
CallbackURL: returnURL,
|
||||
WapName: "极客学长",
|
||||
WapName: "AI小墨",
|
||||
}
|
||||
r, err := h.huPiPayService.Pay(params)
|
||||
if err != nil {
|
||||
@ -346,6 +395,16 @@ func (h *PaymentHandler) Mobile(c *gin.Context) {
|
||||
params.Add("notify_url", notifyURL)
|
||||
params.Add("auto", "0")
|
||||
payURL = h.js.PayH5(params)
|
||||
case "wxpay":
|
||||
payWay = PayWayWxPay
|
||||
notifyURL = h.App.Config.WxpayConfig.NotifyURL
|
||||
returnURL = h.App.Config.WxpayConfig.ReturnURL
|
||||
payURL = orderNo
|
||||
//signInfo, err = h.wxpayService.Pay(h.App.Config.WxpayConfig.MchId, h.App.Config.WxpayConfig.AppId, h.App.Config.WxpayConfig.MchKey)
|
||||
//if err != nil {
|
||||
// resp.ERROR(c, "error with generating Pay URL: "+err.Error())
|
||||
// return
|
||||
//}
|
||||
case "alipay":
|
||||
payWay = PayWayAlipay
|
||||
notifyURL = h.App.Config.AlipayConfig.NotifyURL
|
||||
@ -430,8 +489,13 @@ func (h *PaymentHandler) notify(orderNo string, tradeNo string) error {
|
||||
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
|
||||
if remark.Days == 1 {
|
||||
user.Power += remark.Power
|
||||
power = remark.Power
|
||||
} else {
|
||||
user.Power += h.App.SysConfig.VipMonthPower
|
||||
power = h.App.SysConfig.VipMonthPower
|
||||
}
|
||||
opt = "VIP充值"
|
||||
}
|
||||
user.Vip = true
|
||||
@ -487,6 +551,9 @@ func (h *PaymentHandler) GetPayWays(c *gin.Context) {
|
||||
if h.App.Config.AlipayConfig.Enabled {
|
||||
data["alipay"] = gin.H{"name": "alipay"}
|
||||
}
|
||||
if h.App.Config.WxpayConfig.Enabled {
|
||||
data["wxpay"] = gin.H{"name": "wxpay"}
|
||||
}
|
||||
if h.App.Config.HuPiPayConfig.Enabled {
|
||||
data["hupi"] = gin.H{"name": h.App.Config.HuPiPayConfig.Name}
|
||||
}
|
||||
@ -522,6 +589,23 @@ func (h *PaymentHandler) HuPiPayNotify(c *gin.Context) {
|
||||
c.String(http.StatusOK, "success")
|
||||
}
|
||||
|
||||
// WxpayNotify 微信支付回调
|
||||
func (h *PaymentHandler) WxpayNotify(c *gin.Context) {
|
||||
orderNo, outTradeNo, code := h.wxpayService.TradeVerify(c)
|
||||
logger.Infof("验证支付结果:%+v", code)
|
||||
if code != 200 {
|
||||
logger.Error("订单校验失败")
|
||||
c.String(http.StatusUnauthorized, "fail")
|
||||
return
|
||||
}
|
||||
err := h.notify(orderNo, outTradeNo)
|
||||
if err != nil {
|
||||
c.String(http.StatusOK, "fail")
|
||||
return
|
||||
}
|
||||
c.String(code, "fail")
|
||||
}
|
||||
|
||||
// AlipayNotify 支付宝支付回调
|
||||
func (h *PaymentHandler) AlipayNotify(c *gin.Context) {
|
||||
err := c.Request.ParseForm()
|
||||
|
@ -8,12 +8,14 @@ import (
|
||||
"chatplus/utils"
|
||||
"chatplus/utils/resp"
|
||||
"fmt"
|
||||
"github.com/chanxuehong/wechat/oauth2"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
|
||||
openoath "github.com/chanxuehong/wechat/open/oauth2"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
|
||||
"gorm.io/gorm"
|
||||
@ -155,6 +157,44 @@ func (h *UserHandler) Register(c *gin.Context) {
|
||||
resp.SUCCESS(c, tokenString)
|
||||
}
|
||||
|
||||
// WxLogin 微信内公众号一键授权(支持改造为微信登录)
|
||||
func (h *UserHandler) WxLogin(c *gin.Context) {
|
||||
var data struct {
|
||||
Code string `json:"code"`
|
||||
State string `json:"state"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&data); err != nil {
|
||||
resp.ERROR(c, types.InvalidArgs)
|
||||
return
|
||||
}
|
||||
oauth2Endpoint := openoath.NewEndpoint(h.App.Config.WxpayConfig.AppId, h.App.Config.WxpayConfig.WxAppSecret)
|
||||
oaClient := oauth2.Client{Endpoint: oauth2Endpoint}
|
||||
oaToken, errToken := oaClient.ExchangeToken(data.Code)
|
||||
if errToken != nil {
|
||||
logger.Error("errToken=", errToken)
|
||||
resp.ERROR(c, "登录超时,请重试")
|
||||
return
|
||||
}
|
||||
userinfo, err := openoath.GetUserInfo(oaToken.AccessToken, oaToken.OpenId, openoath.LanguageZhCN, nil)
|
||||
if err != nil {
|
||||
logger.Error("err=", err)
|
||||
resp.ERROR(c, "用户信息获取失败,请重试")
|
||||
return
|
||||
}
|
||||
var user model.User
|
||||
userId := h.GetLoginUserId(c)
|
||||
res := h.DB.Where("id = ?", userId).First(&user)
|
||||
user.OfficialOpenid = userinfo.OpenId
|
||||
user.Unionid = userinfo.UnionId
|
||||
res = h.DB.Updates(&user)
|
||||
if res.Error != nil {
|
||||
resp.ERROR(c, "保存数据失败")
|
||||
logger.Error(res.Error)
|
||||
return
|
||||
}
|
||||
resp.SUCCESS(c, "微信授权成功")
|
||||
}
|
||||
|
||||
// Login 用户登录
|
||||
func (h *UserHandler) Login(c *gin.Context) {
|
||||
var data struct {
|
||||
|
@ -188,6 +188,7 @@ func main() {
|
||||
}),
|
||||
|
||||
fx.Provide(payment.NewAlipayService),
|
||||
fx.Provide(payment.NewWxpayService),
|
||||
fx.Provide(payment.NewHuPiPay),
|
||||
fx.Provide(payment.NewPayJS),
|
||||
fx.Provide(service.NewSnowflake),
|
||||
@ -209,6 +210,7 @@ func main() {
|
||||
fx.Invoke(func(s *core.AppServer, h *handler.UserHandler) {
|
||||
group := s.Engine.Group("/api/user/")
|
||||
group.POST("register", h.Register)
|
||||
group.POST("wxLogin", h.WxLogin)
|
||||
group.POST("login", h.Login)
|
||||
group.GET("logout", h.Logout)
|
||||
group.GET("session", h.Session)
|
||||
@ -341,8 +343,10 @@ func main() {
|
||||
group.GET("doPay", h.DoPay)
|
||||
group.GET("payWays", h.GetPayWays)
|
||||
group.POST("query", h.OrderQuery)
|
||||
group.POST("queryOrder", h.OrderQueryAmount)
|
||||
group.POST("qrcode", h.PayQrcode)
|
||||
group.POST("mobile", h.Mobile)
|
||||
group.POST("wxpay/notify", h.WxpayNotify)
|
||||
group.POST("alipay/notify", h.AlipayNotify)
|
||||
group.POST("hupipay/notify", h.HuPiPayNotify)
|
||||
group.POST("payjs/notify", h.PayJsNotify)
|
||||
|
115
api/service/payment/wxpay_service.go
Normal file
115
api/service/payment/wxpay_service.go
Normal file
@ -0,0 +1,115 @@
|
||||
package payment
|
||||
|
||||
import (
|
||||
"chatplus/core/types"
|
||||
"chatplus/store/model"
|
||||
chatPlusUtils "chatplus/utils"
|
||||
"context"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/wechatpay-apiv3/wechatpay-go/core/auth/verifiers"
|
||||
"github.com/wechatpay-apiv3/wechatpay-go/core/downloader"
|
||||
"github.com/wechatpay-apiv3/wechatpay-go/core/notify"
|
||||
"github.com/wechatpay-apiv3/wechatpay-go/services/payments"
|
||||
"github.com/wechatpay-apiv3/wechatpay-go/services/payments/jsapi"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/wechatpay-apiv3/wechatpay-go/core"
|
||||
"github.com/wechatpay-apiv3/wechatpay-go/core/option"
|
||||
"github.com/wechatpay-apiv3/wechatpay-go/utils"
|
||||
)
|
||||
|
||||
type WxpayService struct {
|
||||
config *types.WxpayConfig
|
||||
client *core.Client
|
||||
certificateVisitor core.CertificateGetter
|
||||
}
|
||||
|
||||
func NewWxpayService(appConfig *types.AppConfig) *WxpayService {
|
||||
config := appConfig.WxpayConfig
|
||||
if !config.Enabled {
|
||||
logger.Info("Disabled Wxpay service")
|
||||
return nil
|
||||
}
|
||||
// 使用 utils 提供的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名
|
||||
mchPrivateKey, err := utils.LoadPrivateKeyWithPath(config.PrivateKey)
|
||||
if err != nil {
|
||||
log.Print("load merchant private key error")
|
||||
return nil
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
// 使用商户私钥等初始化 client,并使它具有自动定时获取微信支付平台证书的能力
|
||||
opts := []core.ClientOption{
|
||||
option.WithWechatPayAutoAuthCipher(config.MchId, config.CertificateSerialNo, mchPrivateKey, config.MchKey),
|
||||
}
|
||||
client, err := core.NewClient(ctx, opts...)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
// 1. 使用 `RegisterDownloaderWithPrivateKey` 注册下载器
|
||||
err2 := downloader.MgrInstance().RegisterDownloaderWithPrivateKey(ctx, mchPrivateKey, config.CertificateSerialNo, config.MchId, config.MchKey)
|
||||
if err2 != nil {
|
||||
logger.Error("支付回调校验失败,请检查应用私钥配置文件")
|
||||
return nil
|
||||
}
|
||||
// 2. 获取商户号对应的微信支付平台证书访问器
|
||||
certificateVisitor := downloader.MgrInstance().GetCertificateVisitor(config.MchId)
|
||||
return &WxpayService{&config, client, certificateVisitor}
|
||||
}
|
||||
|
||||
func (s *WxpayService) Pay(user model.User, order model.Order) (resp *jsapi.PrepayWithRequestPaymentResponse, err error) {
|
||||
return s.PayUrlMobile(user, order)
|
||||
}
|
||||
|
||||
func (s *WxpayService) PayUrlMobile(user model.User, order model.Order) (resp *jsapi.PrepayWithRequestPaymentResponse, err error) {
|
||||
svc := jsapi.JsapiApiService{Client: s.client}
|
||||
var outTradeNo = chatPlusUtils.RandString(16)
|
||||
resp, result, err := svc.PrepayWithRequestPayment(context.Background(),
|
||||
jsapi.PrepayRequest{
|
||||
Appid: core.String(s.config.AppId),
|
||||
Mchid: core.String(s.config.MchId),
|
||||
Description: core.String(order.Subject),
|
||||
OutTradeNo: core.String(outTradeNo),
|
||||
TimeExpire: core.Time(time.Now()),
|
||||
Attach: core.String(order.OrderNo),
|
||||
NotifyUrl: core.String(s.config.NotifyURL),
|
||||
SupportFapiao: core.Bool(false),
|
||||
Amount: &jsapi.Amount{
|
||||
Currency: core.String("CNY"),
|
||||
Total: core.Int64(int64(order.Amount * 100)),
|
||||
},
|
||||
Payer: &jsapi.Payer{
|
||||
Openid: core.String(user.OfficialOpenid),
|
||||
},
|
||||
SceneInfo: &jsapi.SceneInfo{
|
||||
PayerClientIp: core.String("127.0.0.1"),
|
||||
},
|
||||
SettleInfo: &jsapi.SettleInfo{
|
||||
ProfitSharing: core.Bool(false),
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
// 处理错误
|
||||
log.Printf("call Prepay err:%s", err)
|
||||
} else {
|
||||
// 处理返回结果
|
||||
log.Printf("status=%d resp=%s", result.Response.StatusCode, resp)
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (s *WxpayService) TradeVerify(c *gin.Context) (orderNo string, tradeNo string, code int) {
|
||||
// 3. 使用证书访问器初始化 `notify.Handler`
|
||||
handler, _ := notify.NewRSANotifyHandler(s.config.MchKey, verifiers.NewSHA256WithRSAVerifier(s.certificateVisitor))
|
||||
// 2. 获取商户号对应的微信支付平台证书访问器
|
||||
transaction := new(payments.Transaction)
|
||||
_, err3 := handler.ParseNotifyRequest(context.Background(), c.Request, transaction)
|
||||
// 如果验签未通过,或者解密失败
|
||||
if err3 != nil {
|
||||
return "0", "0", 401
|
||||
}
|
||||
return *transaction.Attach, *transaction.OutTradeNo, 200
|
||||
}
|
@ -2,18 +2,20 @@ package model
|
||||
|
||||
type User struct {
|
||||
BaseModel
|
||||
Username string
|
||||
Nickname string
|
||||
Password string
|
||||
Avatar string
|
||||
Salt string // 密码盐
|
||||
Power int // 剩余算力
|
||||
ChatConfig string `gorm:"column:chat_config_json"` // 聊天配置 json
|
||||
ChatRoles string `gorm:"column:chat_roles_json"` // 聊天角色
|
||||
ChatModels string `gorm:"column:chat_models_json"` // AI 模型,不同的用户拥有不同的聊天模型
|
||||
ExpiredTime int64 // 账户到期时间
|
||||
Status bool `gorm:"default:true"` // 当前状态
|
||||
LastLoginAt int64 // 最后登录时间
|
||||
LastLoginIp string // 最后登录 IP
|
||||
Vip bool // 是否 VIP 会员
|
||||
Username string
|
||||
Nickname string
|
||||
Password string
|
||||
Avatar string
|
||||
Salt string // 密码盐
|
||||
Power int // 剩余算力
|
||||
ChatConfig string `gorm:"column:chat_config_json"` // 聊天配置 json
|
||||
ChatRoles string `gorm:"column:chat_roles_json"` // 聊天角色
|
||||
ChatModels string `gorm:"column:chat_models_json"` // AI 模型,不同的用户拥有不同的聊天模型
|
||||
ExpiredTime int64 // 账户到期时间
|
||||
Status bool `gorm:"default:true"` // 当前状态
|
||||
OfficialOpenid string
|
||||
Unionid string
|
||||
LastLoginAt int64 // 最后登录时间
|
||||
LastLoginIp string // 最后登录 IP
|
||||
Vip bool // 是否 VIP 会员
|
||||
}
|
||||
|
@ -23,6 +23,7 @@
|
||||
"markdown-it-latex2img": "^0.0.6",
|
||||
"markdown-it-mathjax": "^2.0.0",
|
||||
"md-editor-v3": "^2.2.1",
|
||||
"moment": "^2.30.1",
|
||||
"pinia": "^2.1.4",
|
||||
"qrcode": "^1.5.3",
|
||||
"qs": "^6.11.1",
|
||||
@ -30,7 +31,8 @@
|
||||
"v3-waterfall": "^1.2.1",
|
||||
"vant": "^4.5.0",
|
||||
"vue": "^3.2.13",
|
||||
"vue-router": "^4.0.15"
|
||||
"vue-router": "^4.0.15",
|
||||
"weixin-js-sdk": "^1.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.18.6",
|
||||
|
@ -46,7 +46,8 @@ import {
|
||||
Tabs,
|
||||
Tag,
|
||||
TextEllipsis,
|
||||
Uploader
|
||||
Uploader,
|
||||
CountDown,
|
||||
} from "vant";
|
||||
import {router} from "@/router";
|
||||
import 'v3-waterfall/dist/style.css'
|
||||
@ -97,6 +98,7 @@ app.use(Lazyload);
|
||||
app.use(ImagePreview);
|
||||
app.use(Tab);
|
||||
app.use(Tabs);
|
||||
app.use(CountDown);
|
||||
app.use(router).use(ElementPlus).mount('#app')
|
||||
|
||||
|
||||
|
@ -212,6 +212,11 @@ const routes = [
|
||||
name: 'mobile-chat-session',
|
||||
component: () => import('@/views/mobile/ChatSession.vue'),
|
||||
},
|
||||
{
|
||||
path: '/mobile/payment',
|
||||
name: 'mobile-payment',
|
||||
component: () => import('@/views/mobile/Payment.vue'),
|
||||
},
|
||||
{
|
||||
path: '/mobile/chat/export',
|
||||
name: 'mobile-chat-export',
|
||||
@ -248,4 +253,4 @@ router.beforeEach((to, from, next) => {
|
||||
next()
|
||||
})
|
||||
|
||||
export {router, prevRoute};
|
||||
export {router, prevRoute};
|
||||
|
@ -30,6 +30,15 @@ export function isMobile() {
|
||||
return mobileRegex.test(userAgent);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否微信浏览器
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isWeChat() {
|
||||
const userAgent = navigator.userAgent.toLowerCase();
|
||||
return userAgent.indexOf('micromessenger') !== -1;
|
||||
}
|
||||
|
||||
// 格式化日期
|
||||
export function dateFormat(timestamp, format) {
|
||||
if (!timestamp) {
|
||||
|
103
web/src/utils/wechatAuth.js
Normal file
103
web/src/utils/wechatAuth.js
Normal file
@ -0,0 +1,103 @@
|
||||
//微信相关
|
||||
import wx from "weixin-js-sdk";
|
||||
|
||||
/**
|
||||
* 获取公众号授权URL
|
||||
* @returns {string}
|
||||
*/
|
||||
export function authUrl(appid, url = null) {
|
||||
if (url == null) {
|
||||
url = window.location.href
|
||||
}
|
||||
return `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appid}&redirect_uri=${encodeURIComponent(url)}&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect`
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取授权回调code
|
||||
*/
|
||||
export function getCode() {
|
||||
let url = window.location.href
|
||||
let urlStr = url.split('?')[1]
|
||||
const urlSearchParams = new URLSearchParams(urlStr)
|
||||
return Object.fromEntries(urlSearchParams.entries())
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取授权结果
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function authResult() {
|
||||
const queryBean = getCode()
|
||||
return queryBean !=null && queryBean.code !== undefined
|
||||
}
|
||||
|
||||
|
||||
export function getSignature (data, callback) {
|
||||
const {appId, nonceStr, paySign, timeStamp} = data
|
||||
// qryWxSignature 这个是调用后台获取签名的接口
|
||||
wx.config({
|
||||
beta: true,
|
||||
debug: false,
|
||||
appId: appId,
|
||||
timestamp: timeStamp,
|
||||
nonceStr: nonceStr,
|
||||
signature: paySign,
|
||||
// 这里是把所有的方法都写出来了 如果只需要一个方法可以只写一个
|
||||
jsApiList: [
|
||||
'checkJsApi',
|
||||
'onMenuShareTimeline',
|
||||
'onMenuShareAppMessage',
|
||||
'onMenuShareQQ',
|
||||
'onMenuShareWeibo',
|
||||
'hideMenuItems',
|
||||
'showMenuItems',
|
||||
'hideAllNonBaseMenuItem',
|
||||
'showAllNonBaseMenuItem',
|
||||
'translateVoice',
|
||||
'startRecord',
|
||||
'stopRecord',
|
||||
'onRecordEnd',
|
||||
'playVoice',
|
||||
'pauseVoice',
|
||||
'stopVoice',
|
||||
'uploadVoice',
|
||||
'downloadVoice',
|
||||
'chooseImage',
|
||||
'previewImage',
|
||||
'uploadImage',
|
||||
'downloadImage',
|
||||
'getNetworkType',
|
||||
'openLocation',
|
||||
'getLocation',
|
||||
'hideOptionMenu',
|
||||
'showOptionMenu',
|
||||
'closeWindow',
|
||||
'scanQRCode',
|
||||
'chooseWXPay',
|
||||
'openProductSpecificView',
|
||||
'addCard',
|
||||
'chooseCard',
|
||||
'openCard',
|
||||
'openWXDeviceLib',
|
||||
'closeWXDeviceLib',
|
||||
'configWXDeviceWiFi',
|
||||
'getWXDeviceInfos',
|
||||
'sendDataToWXDevice',
|
||||
'startScanWXDevice',
|
||||
'stopScanWXDevice',
|
||||
'connectWXDevice',
|
||||
'disconnectWXDevice',
|
||||
'getWXDeviceTicket',
|
||||
'WeixinJSBridgeReady',
|
||||
'onWXDeviceBindStateChange',
|
||||
'onWXDeviceStateChange',
|
||||
'onScanWXDeviceResult',
|
||||
'onReceiveDataFromWXDevice',
|
||||
'onWXDeviceBluetoothStateChange'
|
||||
]
|
||||
})
|
||||
wx.ready(function () {
|
||||
console.log(callback, 'callback')
|
||||
if (callback) callback()
|
||||
})
|
||||
}
|
@ -74,6 +74,9 @@
|
||||
<el-button type="primary" @click="alipay(scope.item)" size="small" v-if="payWays['alipay']">
|
||||
<i class="iconfont icon-alipay"></i> 支付宝
|
||||
</el-button>
|
||||
<el-button type="success" @click="wxpay(scope.item)" size="small" v-if="payWays['wxpay']">
|
||||
<span><i class="iconfont icon-wechat-pay"></i> 微信</span>
|
||||
</el-button>
|
||||
<el-button type="success" @click="huPiPay(scope.item)" size="small" v-if="payWays['hupi']">
|
||||
<span v-if="payWays['hupi']['name'] === 'wechat'"><i class="iconfont icon-wechat-pay"></i> 微信</span>
|
||||
<span v-else><i class="iconfont icon-alipay"></i> 支付宝</span>
|
||||
@ -290,7 +293,21 @@ const alipay = (row) => {
|
||||
}
|
||||
genPayQrcode()
|
||||
}
|
||||
//微信支付
|
||||
const wxpay = (row) => {
|
||||
payName.value = "微信"
|
||||
curPay.value = "wxpay"
|
||||
amount.value = (row.price - row.discount).toFixed(2)
|
||||
if (!isLogin.value) {
|
||||
showLoginDialog.value = true
|
||||
return
|
||||
}
|
||||
|
||||
if (row) {
|
||||
curPayProduct.value = row
|
||||
}
|
||||
genPayQrcode()
|
||||
}
|
||||
// 虎皮椒支付
|
||||
const huPiPay = (row) => {
|
||||
payName.value = payWays.value["hupi"]["name"] === "wechat" ? '微信' : '支付宝'
|
||||
|
187
web/src/views/mobile/Payment.vue
Normal file
187
web/src/views/mobile/Payment.vue
Normal file
@ -0,0 +1,187 @@
|
||||
<template>
|
||||
<div class="payment-content container">
|
||||
<van-nav-bar :title="title" :left-arrow="true" @click-left="onClickLeft"/>
|
||||
<div class="content">
|
||||
<div class="pay-price">
|
||||
<div class="pay-expire">支付倒计时:
|
||||
<van-count-down :time="data.expire" :auto-start="data.autoStart" format="mm:ss" @finish="finishPay"
|
||||
ref="countDownRef"/>
|
||||
</div>
|
||||
<div class="pay-title">支付金额</div>
|
||||
<div class="pay-amount">¥<span>{{ data.amount }}</span></div>
|
||||
</div>
|
||||
<div class="pay-btn">
|
||||
<van-button round block type="primary" @click="pay">支付</van-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import wx from "weixin-js-sdk";
|
||||
import {ref} from "vue";
|
||||
import {ElMessage} from "element-plus";
|
||||
import {checkSession} from "@/action/session";
|
||||
import {httpGet, httpPost} from "@/utils/http";
|
||||
import {authResult, authUrl, getCode, getSignature} from "@/utils/wechatAuth";
|
||||
import {isWeChat} from "@/utils/libs";
|
||||
import {useRouter} from "vue-router";
|
||||
import {setUserToken} from "@/store/session";
|
||||
import {prevRoute} from "@/router";
|
||||
|
||||
const title = ref('支付')
|
||||
const moment = require('moment');
|
||||
const countDownRef = ref(null)
|
||||
const router = useRouter()
|
||||
const data = ref({
|
||||
amount: '-',
|
||||
expire: 0,
|
||||
autoStart: false,
|
||||
orderTimeout: 1800
|
||||
})
|
||||
|
||||
const orderNo = router.currentRoute.value.query["order_no"]
|
||||
const payWay = router.currentRoute.value.query["pay_way"]
|
||||
if (isWeChat()) {
|
||||
checkSession().then(() => {
|
||||
httpGet("/api/config/get?key=system").then(res => {
|
||||
data.value.orderTimeout = res.data['order_pay_timeout']
|
||||
if (authResult()) {
|
||||
//判断是直接访问还是回调访问
|
||||
const queryParam = getCode()
|
||||
queryParam.login_type = "1"
|
||||
httpPost('/api/user/wxLogin', queryParam).then(() => {
|
||||
data.value.autoStart = false
|
||||
httpPost("/api/payment/queryOrder", {order_no: orderNo}).then(res => {
|
||||
const {amount, createTime} = res.data
|
||||
data.value.amount = amount
|
||||
let expire = createTime + data.value.orderTimeout - moment().unix()
|
||||
if (expire <= 0) {
|
||||
finishPay()
|
||||
} else {
|
||||
data.value.expire = Math.min(expire, data.value.orderTimeout) * 1000
|
||||
data.value.autoStart = true
|
||||
if (countDownRef.value) {
|
||||
countDownRef.value.start()
|
||||
}
|
||||
}
|
||||
}).catch(e => {
|
||||
ElMessage.error("查询支付状态失败:" + e.message)
|
||||
})
|
||||
}).catch((e) => {
|
||||
ElMessage.error('登录失败,' + e.message)
|
||||
})
|
||||
} else {
|
||||
window.location.href = authUrl(res.data['wxAppId'])
|
||||
}
|
||||
}).catch(e => {
|
||||
ElMessage.error("获取系统配置失败:" + e.message)
|
||||
})
|
||||
}).catch(() => {
|
||||
router.push('/login')
|
||||
})
|
||||
} else {
|
||||
ElMessage.warning("请使用微信支付")
|
||||
}
|
||||
|
||||
const pay = () => {
|
||||
if (!isWeChat()) {
|
||||
ElMessage.warning("请使用微信支付")
|
||||
return
|
||||
}
|
||||
httpGet(`/api/payment/doPay?order_no=${orderNo}&pay_way=${payWay}`).then(res => {
|
||||
const {nonceStr, paySign, signType, timeStamp} = res.data
|
||||
getSignature(res.data, () => {
|
||||
wx.chooseWXPay({
|
||||
timestamp: timeStamp, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
|
||||
nonceStr: nonceStr, // 支付签名随机串,不长于 32 位
|
||||
package: res.data['package'], // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=\*\*\*)
|
||||
signType: signType, // 微信支付V3的传入RSA,微信支付V2的传入格式与V2统一下单的签名格式保持一致
|
||||
paySign: paySign, // 支付签名
|
||||
success: function () {
|
||||
// 支付成功后的回调函数
|
||||
ElMessage.success('支付成功')
|
||||
let timer = setTimeout(() => {
|
||||
clearTimeout(timer)
|
||||
router.push('/mobile/profile')
|
||||
}, 1000)
|
||||
if (countDownRef.value) {
|
||||
countDownRef.value.stop()
|
||||
}
|
||||
},
|
||||
fail: function () {
|
||||
ElMessage.error('支付失败')
|
||||
}
|
||||
})
|
||||
})
|
||||
}).catch(e => {
|
||||
ElMessage.error("查询支付状态失败:" + e.message)
|
||||
})
|
||||
}
|
||||
const onClickLeft = () => router.push('/mobile/profile');
|
||||
const finishPay = () => {
|
||||
ElMessage.error('支付超时')
|
||||
let timer = setTimeout(() => {
|
||||
clearTimeout(timer)
|
||||
router.push('/mobile/profile')
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
.payment-content {
|
||||
.content {
|
||||
padding-top 60px
|
||||
|
||||
.van-cell__value {
|
||||
.van-image {
|
||||
width 100%
|
||||
}
|
||||
}
|
||||
|
||||
.pay-price {
|
||||
text-align center
|
||||
margin-bottom 30px
|
||||
padding 10px 15px
|
||||
|
||||
.pay-expire {
|
||||
color #2778FF
|
||||
display flex
|
||||
flex-direction row
|
||||
align-items center
|
||||
justify-content center
|
||||
font-size 15px
|
||||
|
||||
.van-count-down {
|
||||
color #2778FF
|
||||
font-size 15px
|
||||
}
|
||||
}
|
||||
|
||||
.pay-title {
|
||||
color #333
|
||||
font-size 15px
|
||||
margin-top 30px
|
||||
}
|
||||
|
||||
.pay-amount {
|
||||
color #666
|
||||
font-size 15px
|
||||
|
||||
span {
|
||||
font-size 36px
|
||||
font-weight bold
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.pay-btn {
|
||||
padding 10px 15px
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
@ -53,7 +53,9 @@
|
||||
<span v-if="payWays['hupi']['name'] === 'wechat'"><i class="iconfont icon-wechat-pay"></i> 微信</span>
|
||||
<span v-else><i class="iconfont icon-alipay"></i> 支付宝</span>
|
||||
</van-button>
|
||||
|
||||
<van-button type="success" @click="pay('wxpay',item)" size="small" v-if="payWays['wxpay']">
|
||||
<span><i class="iconfont icon-wechat-pay"></i> 微信</span>
|
||||
</van-button>
|
||||
<van-button type="success" @click="pay('payjs',item)" size="small" v-if="payWays['payjs']">
|
||||
<span><i class="iconfont icon-wechat-pay"></i> 微信</span>
|
||||
</van-button>
|
||||
@ -250,7 +252,11 @@ const pay = (payWay, item) => {
|
||||
user_id: loginUser.value.id
|
||||
}).then(res => {
|
||||
// console.log(res.data)
|
||||
location.href = res.data
|
||||
if (payWay === 'wxpay') {
|
||||
router.push({path: "payment", query: {order_no: res.data, pay_way: 'wxpay'}})
|
||||
} else {
|
||||
location.href = res.data
|
||||
}
|
||||
}).catch(e => {
|
||||
showFailToast("生成支付订单失败:" + e.message)
|
||||
})
|
||||
@ -302,4 +308,4 @@ const pay = (payWay, item) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
Loading…
Reference in New Issue
Block a user