mirror of
https://github.com/yangjian102621/geekai.git
synced 2025-09-17 16:56:38 +08:00
add geek payment
This commit is contained in:
parent
603bfa7def
commit
158db83965
@ -5,6 +5,8 @@
|
|||||||
* 功能优化:首次登录不需要验证码,直接登录,登录失败之后才弹出验证码
|
* 功能优化:首次登录不需要验证码,直接登录,登录失败之后才弹出验证码
|
||||||
* 功能新增:给 AI 应用(角色)增加分类,前端支持分类筛选
|
* 功能新增:给 AI 应用(角色)增加分类,前端支持分类筛选
|
||||||
* 功能优化:允许用户在聊天页面设置是否使用流式输出或者一次性输出,兼容 GPT-O1 模型。
|
* 功能优化:允许用户在聊天页面设置是否使用流式输出或者一次性输出,兼容 GPT-O1 模型。
|
||||||
|
* 功能优化:移除PayJS支付渠道支持,PayJs已经关闭注册服务,请使用其他支付方式。
|
||||||
|
* 功能新增:新增GeeK易支付支付渠道,支持支付宝,微信支付,QQ钱包,京东支付,抖音支付,Paypal支付等支付方式
|
||||||
|
|
||||||
## v4.1.3
|
## v4.1.3
|
||||||
* 功能优化:重构用户登录模块,给所有的登录组件增加行为验证码功能,支持用户绑定手机,邮箱和微信
|
* 功能优化:重构用户登录模块,给所有的登录组件增加行为验证码功能,支持用户绑定手机,邮箱和微信
|
||||||
|
@ -12,24 +12,23 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type AppConfig struct {
|
type AppConfig struct {
|
||||||
Path string `toml:"-"`
|
Path string `toml:"-"`
|
||||||
Listen string
|
Listen string
|
||||||
Session Session
|
Session Session
|
||||||
AdminSession Session
|
AdminSession Session
|
||||||
ProxyURL string
|
ProxyURL string
|
||||||
MysqlDns string // mysql 连接地址
|
MysqlDns string // mysql 连接地址
|
||||||
StaticDir string // 静态资源目录
|
StaticDir string // 静态资源目录
|
||||||
StaticUrl string // 静态资源 URL
|
StaticUrl string // 静态资源 URL
|
||||||
Redis RedisConfig // redis 连接信息
|
Redis RedisConfig // redis 连接信息
|
||||||
ApiConfig ApiConfig // ChatPlus API authorization configs
|
ApiConfig ApiConfig // ChatPlus API authorization configs
|
||||||
SMS SMSConfig // send mobile message config
|
SMS SMSConfig // send mobile message config
|
||||||
OSS OSSConfig // OSS config
|
OSS OSSConfig // OSS config
|
||||||
|
SmtpConfig SmtpConfig // 邮件发送配置
|
||||||
XXLConfig XXLConfig
|
XXLConfig XXLConfig
|
||||||
AlipayConfig AlipayConfig // 支付宝支付渠道配置
|
AlipayConfig AlipayConfig // 支付宝支付渠道配置
|
||||||
HuPiPayConfig HuPiPayConfig // 虎皮椒支付配置
|
HuPiPayConfig HuPiPayConfig // 虎皮椒支付配置
|
||||||
SmtpConfig SmtpConfig // 邮件发送配置
|
GeekPayConfig GeekPayConfig // GEEK 支付配置
|
||||||
JPayConfig JPayConfig // payjs 支付配置
|
|
||||||
WechatPayConfig WechatPayConfig // 微信支付渠道配置
|
WechatPayConfig WechatPayConfig // 微信支付渠道配置
|
||||||
TikaHost string // TiKa 服务器地址
|
TikaHost string // TiKa 服务器地址
|
||||||
}
|
}
|
||||||
@ -83,10 +82,9 @@ type HuPiPayConfig struct { //虎皮椒第四方支付配置
|
|||||||
ReturnURL string // 支付成功返回地址
|
ReturnURL string // 支付成功返回地址
|
||||||
}
|
}
|
||||||
|
|
||||||
// JPayConfig PayJs 支付配置
|
// GeekPayConfig GEEK支付配置
|
||||||
type JPayConfig struct {
|
type GeekPayConfig struct {
|
||||||
Enabled bool
|
Enabled bool
|
||||||
Name string // 支付名称,默认 wechat
|
|
||||||
AppId string // 商户 ID
|
AppId string // 商户 ID
|
||||||
PrivateKey string // 私钥
|
PrivateKey string // 私钥
|
||||||
ApiURL string // API 网关
|
ApiURL string // API 网关
|
||||||
|
@ -19,7 +19,6 @@ import (
|
|||||||
"geekai/utils"
|
"geekai/utils"
|
||||||
"geekai/utils/resp"
|
"geekai/utils/resp"
|
||||||
"github.com/shopspring/decimal"
|
"github.com/shopspring/decimal"
|
||||||
"math"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"sync"
|
"sync"
|
||||||
@ -34,19 +33,12 @@ type PayWay struct {
|
|||||||
Value string `json:"value"`
|
Value string `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
PayWayAlipay = PayWay{Name: "支付宝", Value: "alipay"}
|
|
||||||
PayWayXunHu = PayWay{Name: "虎皮椒", Value: "hupi"}
|
|
||||||
PayWayJs = PayWay{Name: "PayJS", Value: "payjs"}
|
|
||||||
PayWayWechat = PayWay{Name: "微信支付", Value: "wechat"}
|
|
||||||
)
|
|
||||||
|
|
||||||
// PaymentHandler 支付服务回调 handler
|
// PaymentHandler 支付服务回调 handler
|
||||||
type PaymentHandler struct {
|
type PaymentHandler struct {
|
||||||
BaseHandler
|
BaseHandler
|
||||||
alipayService *payment.AlipayService
|
alipayService *payment.AlipayService
|
||||||
huPiPayService *payment.HuPiPayService
|
huPiPayService *payment.HuPiPayService
|
||||||
jsPayService *payment.JPayService
|
geekPayService *payment.GeekPayService
|
||||||
wechatPayService *payment.WechatPayService
|
wechatPayService *payment.WechatPayService
|
||||||
snowflake *service.Snowflake
|
snowflake *service.Snowflake
|
||||||
fs embed.FS
|
fs embed.FS
|
||||||
@ -58,7 +50,7 @@ func NewPaymentHandler(
|
|||||||
server *core.AppServer,
|
server *core.AppServer,
|
||||||
alipayService *payment.AlipayService,
|
alipayService *payment.AlipayService,
|
||||||
huPiPayService *payment.HuPiPayService,
|
huPiPayService *payment.HuPiPayService,
|
||||||
jsPayService *payment.JPayService,
|
geekPayService *payment.GeekPayService,
|
||||||
wechatPayService *payment.WechatPayService,
|
wechatPayService *payment.WechatPayService,
|
||||||
db *gorm.DB,
|
db *gorm.DB,
|
||||||
snowflake *service.Snowflake,
|
snowflake *service.Snowflake,
|
||||||
@ -66,7 +58,7 @@ func NewPaymentHandler(
|
|||||||
return &PaymentHandler{
|
return &PaymentHandler{
|
||||||
alipayService: alipayService,
|
alipayService: alipayService,
|
||||||
huPiPayService: huPiPayService,
|
huPiPayService: huPiPayService,
|
||||||
jsPayService: jsPayService,
|
geekPayService: geekPayService,
|
||||||
wechatPayService: wechatPayService,
|
wechatPayService: wechatPayService,
|
||||||
snowflake: snowflake,
|
snowflake: snowflake,
|
||||||
fs: fs,
|
fs: fs,
|
||||||
@ -81,10 +73,9 @@ func NewPaymentHandler(
|
|||||||
|
|
||||||
func (h *PaymentHandler) DoPay(c *gin.Context) {
|
func (h *PaymentHandler) DoPay(c *gin.Context) {
|
||||||
orderNo := h.GetTrim(c, "order_no")
|
orderNo := h.GetTrim(c, "order_no")
|
||||||
payWay := h.GetTrim(c, "pay_way")
|
|
||||||
t := h.GetInt(c, "t", 0)
|
t := h.GetInt(c, "t", 0)
|
||||||
sign := h.GetTrim(c, "sign")
|
sign := h.GetTrim(c, "sign")
|
||||||
signStr := fmt.Sprintf("%s-%s-%d-%s", orderNo, payWay, t, h.signKey)
|
signStr := fmt.Sprintf("%s-%d-%s", orderNo, t, h.signKey)
|
||||||
newSign := utils.Sha256(signStr)
|
newSign := utils.Sha256(signStr)
|
||||||
if newSign != sign {
|
if newSign != sign {
|
||||||
resp.ERROR(c, "订单签名错误!")
|
resp.ERROR(c, "订单签名错误!")
|
||||||
@ -118,7 +109,7 @@ func (h *PaymentHandler) DoPay(c *gin.Context) {
|
|||||||
// 更新扫码状态
|
// 更新扫码状态
|
||||||
h.DB.Model(&order).UpdateColumn("status", types.OrderScanned)
|
h.DB.Model(&order).UpdateColumn("status", types.OrderScanned)
|
||||||
|
|
||||||
if payWay == "alipay" { // 支付宝
|
if order.PayWay == "alipay" { // 支付宝
|
||||||
amount := fmt.Sprintf("%.2f", order.Amount)
|
amount := fmt.Sprintf("%.2f", order.Amount)
|
||||||
uri, err := h.alipayService.PayUrlMobile(order.OrderNo, amount, order.Subject)
|
uri, err := h.alipayService.PayUrlMobile(order.OrderNo, amount, order.Subject)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -128,7 +119,7 @@ func (h *PaymentHandler) DoPay(c *gin.Context) {
|
|||||||
|
|
||||||
c.Redirect(302, uri)
|
c.Redirect(302, uri)
|
||||||
return
|
return
|
||||||
} else if payWay == "hupi" { // 虎皮椒支付
|
} else if order.PayWay == "hupi" { // 虎皮椒支付
|
||||||
params := payment.HuPiPayReq{
|
params := payment.HuPiPayReq{
|
||||||
Version: "1.1",
|
Version: "1.1",
|
||||||
TradeOrderId: orderNo,
|
TradeOrderId: orderNo,
|
||||||
@ -144,15 +135,41 @@ func (h *PaymentHandler) DoPay(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
c.Redirect(302, r.URL)
|
c.Redirect(302, r.URL)
|
||||||
|
} else if order.PayWay == "wechat" {
|
||||||
|
uri, err := h.wechatPayService.PayUrlNative(order.OrderNo, int(order.Amount*100), order.Subject)
|
||||||
|
if err != nil {
|
||||||
|
resp.ERROR(c, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Redirect(302, uri)
|
||||||
|
} else if order.PayWay == "geek" {
|
||||||
|
params := payment.GeekPayParams{
|
||||||
|
OutTradeNo: orderNo,
|
||||||
|
Method: "web",
|
||||||
|
Name: order.Subject,
|
||||||
|
Money: fmt.Sprintf("%f", order.Amount),
|
||||||
|
ClientIP: c.ClientIP(),
|
||||||
|
Device: "pc",
|
||||||
|
Type: "alipay",
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := h.geekPayService.Pay(params)
|
||||||
|
if err != nil {
|
||||||
|
resp.ERROR(c, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp.SUCCESS(c, s)
|
||||||
}
|
}
|
||||||
resp.ERROR(c, "Invalid operations")
|
//resp.ERROR(c, "Invalid operations")
|
||||||
}
|
}
|
||||||
|
|
||||||
// PayQrcode 生成支付 URL 二维码
|
// PayQrcode 生成支付 URL 二维码
|
||||||
func (h *PaymentHandler) PayQrcode(c *gin.Context) {
|
func (h *PaymentHandler) PayQrcode(c *gin.Context) {
|
||||||
var data struct {
|
var data struct {
|
||||||
PayWay string `json:"pay_way"` // 支付方式
|
PayWay string `json:"pay_way"` // 支付方式
|
||||||
ProductId uint `json:"product_id"`
|
PayType string `json:"pay_type"` // 支付类别:wechat,alipay,qq...
|
||||||
|
ProductId uint `json:"product_id"` // 支付产品ID
|
||||||
}
|
}
|
||||||
if err := c.ShouldBindJSON(&data); err != nil {
|
if err := c.ShouldBindJSON(&data); err != nil {
|
||||||
resp.ERROR(c, types.InvalidArgs)
|
resp.ERROR(c, types.InvalidArgs)
|
||||||
@ -177,24 +194,22 @@ func (h *PaymentHandler) PayQrcode(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var payWay string
|
|
||||||
var notifyURL string
|
var notifyURL string
|
||||||
switch data.PayWay {
|
switch data.PayWay {
|
||||||
case "hupi":
|
case "hupi":
|
||||||
payWay = PayWayXunHu.Value
|
|
||||||
notifyURL = h.App.Config.HuPiPayConfig.NotifyURL
|
notifyURL = h.App.Config.HuPiPayConfig.NotifyURL
|
||||||
break
|
break
|
||||||
case "payjs":
|
case "geek":
|
||||||
payWay = PayWayJs.Value
|
notifyURL = h.App.Config.GeekPayConfig.NotifyURL
|
||||||
notifyURL = h.App.Config.JPayConfig.NotifyURL
|
|
||||||
break
|
break
|
||||||
case "alipay":
|
case "alipay": // 支付宝商户支付
|
||||||
payWay = PayWayAlipay.Value
|
|
||||||
notifyURL = h.App.Config.AlipayConfig.NotifyURL
|
notifyURL = h.App.Config.AlipayConfig.NotifyURL
|
||||||
break
|
break
|
||||||
default:
|
case "wechat": // 微信商户支付
|
||||||
payWay = PayWayWechat.Value
|
|
||||||
notifyURL = h.App.Config.WechatPayConfig.NotifyURL
|
notifyURL = h.App.Config.WechatPayConfig.NotifyURL
|
||||||
|
default:
|
||||||
|
resp.ERROR(c, "Invalid pay way")
|
||||||
|
return
|
||||||
|
|
||||||
}
|
}
|
||||||
// 创建订单
|
// 创建订单
|
||||||
@ -215,7 +230,8 @@ func (h *PaymentHandler) PayQrcode(c *gin.Context) {
|
|||||||
Subject: product.Name,
|
Subject: product.Name,
|
||||||
Amount: amount,
|
Amount: amount,
|
||||||
Status: types.OrderNotPaid,
|
Status: types.OrderNotPaid,
|
||||||
PayWay: payWay,
|
PayWay: data.PayWay,
|
||||||
|
PayType: data.PayType,
|
||||||
Remark: utils.JsonEncode(remark),
|
Remark: utils.JsonEncode(remark),
|
||||||
}
|
}
|
||||||
res = h.DB.Create(&order)
|
res = h.DB.Create(&order)
|
||||||
@ -224,36 +240,26 @@ func (h *PaymentHandler) PayQrcode(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// PayJs 单独处理,只能用官方生成的二维码
|
|
||||||
if data.PayWay == "payjs" {
|
|
||||||
params := payment.JPayReq{
|
|
||||||
TotalFee: int(math.Ceil(order.Amount * 100)),
|
|
||||||
OutTradeNo: order.OrderNo,
|
|
||||||
Subject: product.Name,
|
|
||||||
}
|
|
||||||
r := h.jsPayService.Pay(params)
|
|
||||||
if r.IsOK() {
|
|
||||||
resp.SUCCESS(c, gin.H{"order_no": order.OrderNo, "image": r.Qrcode})
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
resp.ERROR(c, "error with generating payment qrcode: "+r.ReturnMsg)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var logo string
|
var logo string
|
||||||
if data.PayWay == "alipay" {
|
switch data.PayType {
|
||||||
|
case "alipay":
|
||||||
logo = "res/img/alipay.jpg"
|
logo = "res/img/alipay.jpg"
|
||||||
} else if data.PayWay == "hupi" {
|
break
|
||||||
if h.App.Config.HuPiPayConfig.Name == "wechat" {
|
case "wechat":
|
||||||
logo = "res/img/wechat-pay.jpg"
|
logo = "res/img/wechat-pay.jpg"
|
||||||
} else {
|
break
|
||||||
logo = "res/img/alipay.jpg"
|
case "qq":
|
||||||
}
|
logo = "res/img/qq-pay.jpg"
|
||||||
} else if data.PayWay == "wechat" {
|
break
|
||||||
|
default:
|
||||||
|
logo = "res/img/geek-pay.jpg"
|
||||||
|
|
||||||
|
}
|
||||||
|
if data.PayType == "alipay" {
|
||||||
|
logo = "res/img/alipay.jpg"
|
||||||
|
} else if data.PayType == "wechat" {
|
||||||
logo = "res/img/wechat-pay.jpg"
|
logo = "res/img/wechat-pay.jpg"
|
||||||
}
|
}
|
||||||
|
|
||||||
file, err := h.fs.Open(logo)
|
file, err := h.fs.Open(logo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.ERROR(c, "error with open qrcode log file: "+err.Error())
|
resp.ERROR(c, "error with open qrcode log file: "+err.Error())
|
||||||
@ -268,31 +274,21 @@ func (h *PaymentHandler) PayQrcode(c *gin.Context) {
|
|||||||
timestamp := time.Now().Unix()
|
timestamp := time.Now().Unix()
|
||||||
signStr := fmt.Sprintf("%s-%s-%d-%s", orderNo, data.PayWay, timestamp, h.signKey)
|
signStr := fmt.Sprintf("%s-%s-%d-%s", orderNo, data.PayWay, timestamp, h.signKey)
|
||||||
sign := utils.Sha256(signStr)
|
sign := utils.Sha256(signStr)
|
||||||
var imageURL string
|
payUrl := fmt.Sprintf("%s://%s/api/payment/doPay?order_no=%s&pay_way=%s&pay_type=%s&t=%d&sign=%s", parse.Scheme, parse.Host, orderNo, data.PayWay, data.PayType, timestamp, sign)
|
||||||
if data.PayWay == "wechat" {
|
imgData, err := utils.GenQrcode(payUrl, 400, file)
|
||||||
payUrl, err := h.wechatPayService.PayUrlNative(order.OrderNo, int(math.Floor(order.Amount*100)), product.Name)
|
|
||||||
if err != nil {
|
|
||||||
resp.ERROR(c, "error with generating wechat payment qrcode: "+err.Error())
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
imageURL = payUrl
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
imageURL = fmt.Sprintf("%s://%s/api/payment/doPay?order_no=%s&pay_way=%s&t=%d&sign=%s", parse.Scheme, parse.Host, orderNo, data.PayWay, timestamp, sign)
|
|
||||||
}
|
|
||||||
imgData, err := utils.GenQrcode(imageURL, 400, file)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.ERROR(c, err.Error())
|
resp.ERROR(c, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
imgDataBase64 := base64.StdEncoding.EncodeToString(imgData)
|
imgDataBase64 := base64.StdEncoding.EncodeToString(imgData)
|
||||||
resp.SUCCESS(c, gin.H{"order_no": orderNo, "image": fmt.Sprintf("data:image/jpg;base64, %s", imgDataBase64), "url": imageURL})
|
resp.SUCCESS(c, gin.H{"order_no": orderNo, "image": fmt.Sprintf("data:image/jpg;base64, %s", imgDataBase64), "url": payUrl})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mobile 移动端支付
|
// Mobile 移动端支付
|
||||||
func (h *PaymentHandler) Mobile(c *gin.Context) {
|
func (h *PaymentHandler) Mobile(c *gin.Context) {
|
||||||
var data struct {
|
var data struct {
|
||||||
PayWay string `json:"pay_way"` // 支付方式
|
PayWay string `json:"pay_way"` // 支付方式
|
||||||
|
PayType string `json:"pay_type"` // 支付类别:wechat,alipay,qq...
|
||||||
ProductId uint `json:"product_id"`
|
ProductId uint `json:"product_id"`
|
||||||
}
|
}
|
||||||
if err := c.ShouldBindJSON(&data); err != nil {
|
if err := c.ShouldBindJSON(&data); err != nil {
|
||||||
@ -319,12 +315,10 @@ func (h *PaymentHandler) Mobile(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
amount, _ := decimal.NewFromFloat(product.Price).Sub(decimal.NewFromFloat(product.Discount)).Float64()
|
amount, _ := decimal.NewFromFloat(product.Price).Sub(decimal.NewFromFloat(product.Discount)).Float64()
|
||||||
var payWay string
|
|
||||||
var notifyURL, returnURL string
|
var notifyURL, returnURL string
|
||||||
var payURL string
|
var payURL string
|
||||||
switch data.PayWay {
|
switch data.PayWay {
|
||||||
case "hupi":
|
case "hupi":
|
||||||
payWay = PayWayXunHu.Name
|
|
||||||
notifyURL = h.App.Config.HuPiPayConfig.NotifyURL
|
notifyURL = h.App.Config.HuPiPayConfig.NotifyURL
|
||||||
returnURL = h.App.Config.HuPiPayConfig.ReturnURL
|
returnURL = h.App.Config.HuPiPayConfig.ReturnURL
|
||||||
parse, _ := url.Parse(h.App.Config.HuPiPayConfig.ReturnURL)
|
parse, _ := url.Parse(h.App.Config.HuPiPayConfig.ReturnURL)
|
||||||
@ -349,20 +343,16 @@ func (h *PaymentHandler) Mobile(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
payURL = r.URL
|
payURL = r.URL
|
||||||
case "payjs":
|
case "geek":
|
||||||
payWay = PayWayJs.Name
|
//totalFee := decimal.NewFromFloat(product.Price).Sub(decimal.NewFromFloat(product.Discount)).Mul(decimal.NewFromInt(100)).IntPart()
|
||||||
notifyURL = h.App.Config.JPayConfig.NotifyURL
|
//params := url.Values{}
|
||||||
returnURL = h.App.Config.JPayConfig.ReturnURL
|
//params.Add("total_fee", fmt.Sprintf("%d", totalFee))
|
||||||
totalFee := decimal.NewFromFloat(product.Price).Sub(decimal.NewFromFloat(product.Discount)).Mul(decimal.NewFromInt(100)).IntPart()
|
//params.Add("out_trade_no", orderNo)
|
||||||
params := url.Values{}
|
//params.Add("body", product.Name)
|
||||||
params.Add("total_fee", fmt.Sprintf("%d", totalFee))
|
//params.Add("notify_url", notifyURL)
|
||||||
params.Add("out_trade_no", orderNo)
|
//params.Add("auto", "0")
|
||||||
params.Add("body", product.Name)
|
//payURL = h.geekPayService.Pay(params)
|
||||||
params.Add("notify_url", notifyURL)
|
|
||||||
params.Add("auto", "0")
|
|
||||||
payURL = h.jsPayService.PayH5(params)
|
|
||||||
case "alipay":
|
case "alipay":
|
||||||
payWay = PayWayAlipay.Name
|
|
||||||
payURL, err = h.alipayService.PayUrlMobile(orderNo, fmt.Sprintf("%.2f", amount), product.Name)
|
payURL, err = h.alipayService.PayUrlMobile(orderNo, fmt.Sprintf("%.2f", amount), product.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errMsg := "error with generating Alipay URL: " + err.Error()
|
errMsg := "error with generating Alipay URL: " + err.Error()
|
||||||
@ -370,7 +360,6 @@ func (h *PaymentHandler) Mobile(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
case "wechat":
|
case "wechat":
|
||||||
payWay = PayWayWechat.Name
|
|
||||||
payURL, err = h.wechatPayService.PayUrlH5(orderNo, int(amount*100), product.Name, c.ClientIP())
|
payURL, err = h.wechatPayService.PayUrlH5(orderNo, int(amount*100), product.Name, c.ClientIP())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errMsg := "error with generating Wechat URL: " + err.Error()
|
errMsg := "error with generating Wechat URL: " + err.Error()
|
||||||
@ -399,7 +388,8 @@ func (h *PaymentHandler) Mobile(c *gin.Context) {
|
|||||||
Subject: product.Name,
|
Subject: product.Name,
|
||||||
Amount: amount,
|
Amount: amount,
|
||||||
Status: types.OrderNotPaid,
|
Status: types.OrderNotPaid,
|
||||||
PayWay: payWay,
|
PayWay: data.PayWay,
|
||||||
|
PayType: data.PayType,
|
||||||
Remark: utils.JsonEncode(remark),
|
Remark: utils.JsonEncode(remark),
|
||||||
}
|
}
|
||||||
res = h.DB.Create(&order)
|
res = h.DB.Create(&order)
|
||||||
@ -506,20 +496,25 @@ func (h *PaymentHandler) notify(orderNo string, tradeNo string) error {
|
|||||||
|
|
||||||
// GetPayWays 获取支付方式
|
// GetPayWays 获取支付方式
|
||||||
func (h *PaymentHandler) GetPayWays(c *gin.Context) {
|
func (h *PaymentHandler) GetPayWays(c *gin.Context) {
|
||||||
data := gin.H{}
|
payWays := make([]gin.H, 0)
|
||||||
if h.App.Config.AlipayConfig.Enabled {
|
if h.App.Config.AlipayConfig.Enabled {
|
||||||
data["alipay"] = gin.H{"name": "alipay"}
|
payWays = append(payWays, gin.H{"pay_way": "alipay", "pay_type": "alipay"})
|
||||||
}
|
}
|
||||||
if h.App.Config.HuPiPayConfig.Enabled {
|
if h.App.Config.HuPiPayConfig.Enabled {
|
||||||
data["hupi"] = gin.H{"name": h.App.Config.HuPiPayConfig.Name}
|
payWays = append(payWays, gin.H{"pay_way": "hupi", "pay_type": h.App.Config.HuPiPayConfig.Name})
|
||||||
}
|
}
|
||||||
if h.App.Config.JPayConfig.Enabled {
|
if h.App.Config.GeekPayConfig.Enabled {
|
||||||
data["payjs"] = gin.H{"name": h.App.Config.JPayConfig.Name}
|
payWays = append(payWays, gin.H{"pay_way": "geek", "pay_type": "alipay"})
|
||||||
|
payWays = append(payWays, gin.H{"pay_way": "geek", "pay_type": "wechat"})
|
||||||
|
payWays = append(payWays, gin.H{"pay_way": "geek", "pay_type": "qq"})
|
||||||
|
payWays = append(payWays, gin.H{"pay_way": "geek", "pay_type": "jd"})
|
||||||
|
payWays = append(payWays, gin.H{"pay_way": "geek", "pay_type": "douyin"})
|
||||||
|
payWays = append(payWays, gin.H{"pay_way": "geek", "pay_type": "paypal"})
|
||||||
}
|
}
|
||||||
if h.App.Config.WechatPayConfig.Enabled {
|
if h.App.Config.WechatPayConfig.Enabled {
|
||||||
data["wechat"] = gin.H{"name": "wechat"}
|
payWays = append(payWays, gin.H{"pay_way": "wechat", "pay_type": "wechat"})
|
||||||
}
|
}
|
||||||
resp.SUCCESS(c, data)
|
resp.SUCCESS(c, payWays)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HuPiPayNotify 虎皮椒支付异步回调
|
// HuPiPayNotify 虎皮椒支付异步回调
|
||||||
@ -593,12 +588,12 @@ func (h *PaymentHandler) PayJsNotify(c *gin.Context) {
|
|||||||
|
|
||||||
// 校验订单支付状态
|
// 校验订单支付状态
|
||||||
tradeNo := c.Request.Form.Get("payjs_order_id")
|
tradeNo := c.Request.Form.Get("payjs_order_id")
|
||||||
err = h.jsPayService.TradeVerify(tradeNo)
|
//err = h.geekPayService.TradeVerify(tradeNo)
|
||||||
if err != nil {
|
//if err != nil {
|
||||||
logger.Error("订单校验失败:", err)
|
// logger.Error("订单校验失败:", err)
|
||||||
c.String(http.StatusOK, "fail")
|
// c.String(http.StatusOK, "fail")
|
||||||
return
|
// return
|
||||||
}
|
//}
|
||||||
|
|
||||||
err = h.notify(orderNo, tradeNo)
|
err = h.notify(orderNo, tradeNo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -11,10 +11,10 @@ import (
|
|||||||
type TestHandler struct {
|
type TestHandler struct {
|
||||||
db *gorm.DB
|
db *gorm.DB
|
||||||
snowflake *service.Snowflake
|
snowflake *service.Snowflake
|
||||||
js *payment.JPayService
|
js *payment.GeekPayService
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTestHandler(db *gorm.DB, snowflake *service.Snowflake, js *payment.JPayService) *TestHandler {
|
func NewTestHandler(db *gorm.DB, snowflake *service.Snowflake, js *payment.GeekPayService) *TestHandler {
|
||||||
return &TestHandler{db: db, snowflake: snowflake, js: js}
|
return &TestHandler{db: db, snowflake: snowflake, js: js}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BIN
api/res/img/geek-pay.jpg
Normal file
BIN
api/res/img/geek-pay.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 27 KiB |
BIN
api/res/img/qq-pay.jpg
Normal file
BIN
api/res/img/qq-pay.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
136
api/service/payment/geekpay_service.go
Normal file
136
api/service/payment/geekpay_service.go
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
package payment
|
||||||
|
|
||||||
|
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
// * Copyright 2023 The Geek-AI Authors. All rights reserved.
|
||||||
|
// * Use of this source code is governed by a Apache-2.0 license
|
||||||
|
// * that can be found in the LICENSE file.
|
||||||
|
// * @Author yangjian102621@163.com
|
||||||
|
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"geekai/core/types"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GeekPayService Geek 支付服务
|
||||||
|
type GeekPayService struct {
|
||||||
|
config *types.GeekPayConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewJPayService(appConfig *types.AppConfig) *GeekPayService {
|
||||||
|
return &GeekPayService{
|
||||||
|
config: &appConfig.GeekPayConfig,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type GeekPayParams struct {
|
||||||
|
Method string `json:"method"` // 接口类型
|
||||||
|
Device string `json:"device"` // 设备类型
|
||||||
|
Type string `json:"type"` // 支付方式
|
||||||
|
OutTradeNo string `json:"out_trade_no"` // 商户订单号
|
||||||
|
Name string `json:"name"` // 商品名称
|
||||||
|
Money string `json:"money"` // 商品金额
|
||||||
|
ClientIP string `json:"clientip"` //用户IP地址
|
||||||
|
SubOpenId string `json:"sub_openid"` // 微信用户 openid,仅小程序支付需要
|
||||||
|
SubAppId string `json:"sub_appid"` // 小程序 AppId,仅小程序支付需要
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pay 支付订单
|
||||||
|
func (s *GeekPayService) Pay(params GeekPayParams) (string, error) {
|
||||||
|
if params.Type == "wechat" {
|
||||||
|
params.Type = "wxpay"
|
||||||
|
}
|
||||||
|
p := map[string]string{
|
||||||
|
"pid": s.config.AppId,
|
||||||
|
"method": params.Method,
|
||||||
|
"device": params.Device,
|
||||||
|
"type": params.Type,
|
||||||
|
"out_trade_no": params.OutTradeNo,
|
||||||
|
"name": params.Name,
|
||||||
|
"money": params.Money,
|
||||||
|
"clientip": params.ClientIP,
|
||||||
|
"sub_openid": params.SubOpenId,
|
||||||
|
"sub_appid": params.SubAppId,
|
||||||
|
"notify_url": s.config.NotifyURL,
|
||||||
|
"return_url": s.config.ReturnURL,
|
||||||
|
"timestamp": fmt.Sprintf("%d", time.Now().Unix()),
|
||||||
|
}
|
||||||
|
sign, err := s.Sign(p)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
p["sign"] = sign
|
||||||
|
p["sign_type"] = "RSA"
|
||||||
|
return s.sendRequest(s.config.ApiURL, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GeekPayService) Sign(params map[string]string) (string, error) {
|
||||||
|
// 按字母顺序排序参数
|
||||||
|
var keys []string
|
||||||
|
for k := range params {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
|
|
||||||
|
// 构建待签名字符串
|
||||||
|
var signStr strings.Builder
|
||||||
|
for _, k := range keys {
|
||||||
|
signStr.WriteString(k)
|
||||||
|
signStr.WriteString("=")
|
||||||
|
signStr.WriteString(params[k])
|
||||||
|
signStr.WriteString("&")
|
||||||
|
}
|
||||||
|
signString := strings.TrimSuffix(signStr.String(), "&")
|
||||||
|
|
||||||
|
// 使用RSA私钥签名
|
||||||
|
block, _ := pem.Decode([]byte(s.config.PrivateKey))
|
||||||
|
if block == nil {
|
||||||
|
return "", fmt.Errorf("failed to decode private key")
|
||||||
|
}
|
||||||
|
|
||||||
|
priKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to parse private key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hashed := sha256.Sum256([]byte(signString))
|
||||||
|
signature, err := rsa.SignPKCS1v15(rand.Reader, priKey, crypto.SHA256, hashed[:])
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("failed to sign: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return base64.StdEncoding.EncodeToString(signature), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GeekPayService) sendRequest(apiEndpoint string, params map[string]string) (string, error) {
|
||||||
|
form := url.Values{}
|
||||||
|
for k, v := range params {
|
||||||
|
form.Add(k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.PostForm(apiEndpoint, form)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(body), nil
|
||||||
|
}
|
@ -1,153 +0,0 @@
|
|||||||
package payment
|
|
||||||
|
|
||||||
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
||||||
// * Copyright 2023 The Geek-AI Authors. All rights reserved.
|
|
||||||
// * Use of this source code is governed by a Apache-2.0 license
|
|
||||||
// * that can be found in the LICENSE file.
|
|
||||||
// * @Author yangjian102621@163.com
|
|
||||||
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/md5"
|
|
||||||
"encoding/hex"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"geekai/core/types"
|
|
||||||
"geekai/utils"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type JPayService struct {
|
|
||||||
config *types.JPayConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewJPayService(appConfig *types.AppConfig) *JPayService {
|
|
||||||
return &JPayService{
|
|
||||||
config: &appConfig.JPayConfig,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type JPayReq struct {
|
|
||||||
TotalFee int `json:"total_fee"`
|
|
||||||
OutTradeNo string `json:"out_trade_no"`
|
|
||||||
Subject string `json:"body"`
|
|
||||||
NotifyURL string `json:"notify_url"`
|
|
||||||
ReturnURL string `json:"callback_url"`
|
|
||||||
}
|
|
||||||
type JPayReps struct {
|
|
||||||
OutTradeNo string `json:"out_trade_no"`
|
|
||||||
OrderId string `json:"payjs_order_id"`
|
|
||||||
ReturnCode int `json:"return_code"`
|
|
||||||
ReturnMsg string `json:"return_msg"`
|
|
||||||
Sign string `json:"Sign"`
|
|
||||||
TotalFee string `json:"total_fee"`
|
|
||||||
CodeUrl string `json:"code_url,omitempty"`
|
|
||||||
Qrcode string `json:"qrcode,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r JPayReps) IsOK() bool {
|
|
||||||
return r.ReturnMsg == "SUCCESS"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *JPayService) Pay(param JPayReq) JPayReps {
|
|
||||||
param.NotifyURL = js.config.NotifyURL
|
|
||||||
var p = url.Values{}
|
|
||||||
encode := utils.JsonEncode(param)
|
|
||||||
m := make(map[string]interface{})
|
|
||||||
_ = utils.JsonDecode(encode, &m)
|
|
||||||
for k, v := range m {
|
|
||||||
p.Add(k, fmt.Sprintf("%v", v))
|
|
||||||
}
|
|
||||||
p.Add("mchid", js.config.AppId)
|
|
||||||
|
|
||||||
p.Add("sign", js.sign(p))
|
|
||||||
|
|
||||||
cli := http.Client{}
|
|
||||||
apiURL := fmt.Sprintf("%s/api/native", js.config.ApiURL)
|
|
||||||
r, err := cli.PostForm(apiURL, p)
|
|
||||||
if err != nil {
|
|
||||||
return JPayReps{ReturnMsg: err.Error()}
|
|
||||||
}
|
|
||||||
defer r.Body.Close()
|
|
||||||
bs, err := io.ReadAll(r.Body)
|
|
||||||
if err != nil {
|
|
||||||
return JPayReps{ReturnMsg: err.Error()}
|
|
||||||
}
|
|
||||||
|
|
||||||
var data JPayReps
|
|
||||||
err = utils.JsonDecode(string(bs), &data)
|
|
||||||
if err != nil {
|
|
||||||
return JPayReps{ReturnMsg: err.Error()}
|
|
||||||
}
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *JPayService) PayH5(p url.Values) string {
|
|
||||||
p.Add("mchid", js.config.AppId)
|
|
||||||
p.Add("sign", js.sign(p))
|
|
||||||
return fmt.Sprintf("%s/api/cashier?%s", js.config.ApiURL, p.Encode())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *JPayService) sign(params url.Values) string {
|
|
||||||
params.Del(`sign`)
|
|
||||||
var keys = make([]string, 0, 0)
|
|
||||||
for key := range params {
|
|
||||||
if params.Get(key) != `` {
|
|
||||||
keys = append(keys, key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sort.Strings(keys)
|
|
||||||
|
|
||||||
var pList = make([]string, 0, 0)
|
|
||||||
for _, key := range keys {
|
|
||||||
var value = strings.TrimSpace(params.Get(key))
|
|
||||||
if len(value) > 0 {
|
|
||||||
pList = append(pList, key+"="+value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var src = strings.Join(pList, "&")
|
|
||||||
src += "&key=" + js.config.PrivateKey
|
|
||||||
|
|
||||||
md5bs := md5.Sum([]byte(src))
|
|
||||||
md5res := hex.EncodeToString(md5bs[:])
|
|
||||||
return strings.ToUpper(md5res)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TradeVerify 查询订单支付状态
|
|
||||||
// @param tradeNo 支付平台交易 ID
|
|
||||||
func (js *JPayService) TradeVerify(tradeNo string) error {
|
|
||||||
apiURL := fmt.Sprintf("%s/api/check", js.config.ApiURL)
|
|
||||||
params := url.Values{}
|
|
||||||
params.Add("payjs_order_id", tradeNo)
|
|
||||||
params.Add("sign", js.sign(params))
|
|
||||||
data := strings.NewReader(params.Encode())
|
|
||||||
resp, err := http.Post(apiURL, "application/x-www-form-urlencoded", data)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error with http reqeust: %v", err)
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error with reading response: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var r struct {
|
|
||||||
ReturnCode int `json:"return_code"`
|
|
||||||
Status int `json:"status"`
|
|
||||||
}
|
|
||||||
err = utils.JsonDecode(string(body), &r)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error with decode response: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.ReturnCode == 1 && r.Status == 1 {
|
|
||||||
return nil
|
|
||||||
} else {
|
|
||||||
logger.Errorf("PayJs 支付验证响应:%s", string(body))
|
|
||||||
return errors.New("order not paid")
|
|
||||||
}
|
|
||||||
}
|
|
@ -18,6 +18,7 @@ type Order struct {
|
|||||||
Status types.OrderStatus
|
Status types.OrderStatus
|
||||||
Remark string
|
Remark string
|
||||||
PayTime int64
|
PayTime int64
|
||||||
PayWay string // 支付方式
|
PayWay string // 支付渠道
|
||||||
|
PayType string // 支付类型
|
||||||
DeletedAt gorm.DeletedAt
|
DeletedAt gorm.DeletedAt
|
||||||
}
|
}
|
||||||
|
@ -16,5 +16,6 @@ type Order struct {
|
|||||||
Status types.OrderStatus `json:"status"`
|
Status types.OrderStatus `json:"status"`
|
||||||
PayTime int64 `json:"pay_time"`
|
PayTime int64 `json:"pay_time"`
|
||||||
PayWay string `json:"pay_way"`
|
PayWay string `json:"pay_way"`
|
||||||
|
PayType string `json:"pay_type"`
|
||||||
Remark types.OrderRemark `json:"remark"`
|
Remark types.OrderRemark `json:"remark"`
|
||||||
}
|
}
|
||||||
|
@ -12,3 +12,4 @@ ALTER TABLE `chatgpt_app_types` MODIFY `id` int NOT NULL AUTO_INCREMENT;
|
|||||||
ALTER TABLE `chatgpt_chat_roles` ADD `tid` INT NOT NULL COMMENT '分类ID' AFTER `name`;
|
ALTER TABLE `chatgpt_chat_roles` ADD `tid` INT NOT NULL COMMENT '分类ID' AFTER `name`;
|
||||||
|
|
||||||
ALTER TABLE `chatgpt_chat_history` ADD `total_tokens` INT NOT NULL COMMENT '消耗总Token长度' AFTER `tokens`;
|
ALTER TABLE `chatgpt_chat_history` ADD `total_tokens` INT NOT NULL COMMENT '消耗总Token长度' AFTER `tokens`;
|
||||||
|
ALTER TABLE `chatgpt_orders` ADD `pay_type` VARCHAR(30) NOT NULL COMMENT '支付类型' AFTER `pay_way`;
|
@ -110,6 +110,7 @@
|
|||||||
overflow hidden
|
overflow hidden
|
||||||
cursor pointer
|
cursor pointer
|
||||||
transition: all 0.3s ease; /* 添加过渡效果 */
|
transition: all 0.3s ease; /* 添加过渡效果 */
|
||||||
|
margin-bottom 20px
|
||||||
|
|
||||||
.image-container {
|
.image-container {
|
||||||
display flex
|
display flex
|
||||||
@ -175,10 +176,11 @@
|
|||||||
.pay-way {
|
.pay-way {
|
||||||
padding 10px 0
|
padding 10px 0
|
||||||
display flex
|
display flex
|
||||||
justify-content: space-between
|
justify-content: center
|
||||||
|
flex-wrap wrap
|
||||||
|
|
||||||
.iconfont {
|
.el-button {
|
||||||
margin-right 5px
|
margin 10px 5px 0 5px
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-family: "iconfont"; /* Project id 4125778 */
|
font-family: "iconfont"; /* Project id 4125778 */
|
||||||
src: url('iconfont.woff2?t=1725929120246') format('woff2'),
|
src: url('iconfont.woff2?t=1726612860394') format('woff2'),
|
||||||
url('iconfont.woff?t=1725929120246') format('woff'),
|
url('iconfont.woff?t=1726612860394') format('woff'),
|
||||||
url('iconfont.ttf?t=1725929120246') format('truetype');
|
url('iconfont.ttf?t=1726612860394') format('truetype');
|
||||||
}
|
}
|
||||||
|
|
||||||
.iconfont {
|
.iconfont {
|
||||||
@ -13,6 +13,22 @@
|
|||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon-douyin:before {
|
||||||
|
content: "\e852";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-paypal:before {
|
||||||
|
content: "\e60f";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-qq:before {
|
||||||
|
content: "\e69f";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-jd-pay:before {
|
||||||
|
content: "\e8dd";
|
||||||
|
}
|
||||||
|
|
||||||
.icon-luma:before {
|
.icon-luma:before {
|
||||||
content: "\e704";
|
content: "\e704";
|
||||||
}
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
@ -5,6 +5,34 @@
|
|||||||
"css_prefix_text": "icon-",
|
"css_prefix_text": "icon-",
|
||||||
"description": "",
|
"description": "",
|
||||||
"glyphs": [
|
"glyphs": [
|
||||||
|
{
|
||||||
|
"icon_id": "22174321",
|
||||||
|
"name": "抖音支付",
|
||||||
|
"font_class": "douyin",
|
||||||
|
"unicode": "e852",
|
||||||
|
"unicode_decimal": 59474
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "1238433",
|
||||||
|
"name": "social-paypal",
|
||||||
|
"font_class": "paypal",
|
||||||
|
"unicode": "e60f",
|
||||||
|
"unicode_decimal": 58895
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "1244217",
|
||||||
|
"name": "qq",
|
||||||
|
"font_class": "qq",
|
||||||
|
"unicode": "e69f",
|
||||||
|
"unicode_decimal": 59039
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "18166714",
|
||||||
|
"name": "京东支付",
|
||||||
|
"font_class": "jd-pay",
|
||||||
|
"unicode": "e8dd",
|
||||||
|
"unicode_decimal": 59613
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"icon_id": "41645421",
|
"icon_id": "41645421",
|
||||||
"name": "luma-logo",
|
"name": "luma-logo",
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -36,57 +36,63 @@
|
|||||||
</el-alert>
|
</el-alert>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ItemList :items="list" v-if="list.length > 0" :gap="15" :width="240">
|
<el-row v-if="list.length > 0" :gutter="20" class="list-box">
|
||||||
<template #default="scope">
|
<el-col v-for="item in list" :key="item" :span="6">
|
||||||
<div class="product-item">
|
<div class="product-item">
|
||||||
<div class="image-container">
|
<div class="image-container">
|
||||||
<el-image :src="vipImg" fit="cover"/>
|
<el-image :src="vipImg" fit="cover"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="product-title">
|
<div class="product-title">
|
||||||
<span class="name">{{ scope.item.name }}</span>
|
<span class="name">{{ item.name }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="product-info">
|
<div class="product-info">
|
||||||
<div class="info-line">
|
<div class="info-line">
|
||||||
<span class="label">商品原价:</span>
|
<span class="label">商品原价:</span>
|
||||||
<span class="price">¥{{ scope.item.price }}</span>
|
<span class="price">¥{{ item.price }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-line">
|
<div class="info-line">
|
||||||
<span class="label">促销立减:</span>
|
<span class="label">促销立减:</span>
|
||||||
<span class="price">¥{{ scope.item.discount }}</span>
|
<span class="price">¥{{ item.discount }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-line">
|
<div class="info-line">
|
||||||
<span class="label">有效期:</span>
|
<span class="label">有效期:</span>
|
||||||
<span class="expire" v-if="scope.item.days > 0">{{ scope.item.days }}天</span>
|
<span class="expire" v-if="item.days > 0">{{ item.days }}天</span>
|
||||||
<span class="expire" v-else>长期有效</span>
|
<span class="expire" v-else>长期有效</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="info-line">
|
<div class="info-line">
|
||||||
<span class="label">算力值:</span>
|
<span class="label">算力值:</span>
|
||||||
<span class="power" v-if="scope.item.power > 0">{{ scope.item.power }}</span>
|
<span class="power">{{ item.power }}</span>
|
||||||
<span class="power" v-else>{{ vipMonthPower }}</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pay-way">
|
<div class="pay-way">
|
||||||
<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="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>
|
|
||||||
</el-button>
|
|
||||||
|
|
||||||
<el-button type="success" @click="PayJs(scope.item)" size="small" v-if="payWays['payjs']">
|
<span type="primary" v-for="payWay in payWays" @click="genPayQrcode(item,payWay)" :key="payWay">
|
||||||
<span><i class="iconfont icon-wechat-pay"></i> 微信</span>
|
<el-button v-if="payWay.pay_type==='alipay'" color="#409eff" circle>
|
||||||
</el-button>
|
<i class="iconfont icon-alipay" ></i>
|
||||||
<el-button type="success" @click="wechatPay(scope.item)" size="small" v-if="payWays['wechat']">
|
</el-button>
|
||||||
<i class="iconfont icon-wechat-pay"></i> 微信
|
<el-button v-else-if="payWay.pay_type==='qq'" class="qq" circle>
|
||||||
</el-button>
|
<i class="iconfont icon-qq" ></i>
|
||||||
|
</el-button>
|
||||||
|
<el-button v-else-if="payWay.pay_type==='paypal'" class="paypal" circle>
|
||||||
|
<i class="iconfont icon-paypal"></i>
|
||||||
|
</el-button>
|
||||||
|
<el-button v-else-if="payWay.pay_type==='jd'" class="jd" circle>
|
||||||
|
<i class="iconfont icon-jd-pay"></i>
|
||||||
|
</el-button>
|
||||||
|
<el-button v-else-if="payWay.pay_type==='douyin'" class="douyin" circle>
|
||||||
|
<i class="iconfont icon-douyin"></i>
|
||||||
|
</el-button>
|
||||||
|
<el-button v-else circle class="wechat">
|
||||||
|
<i class="iconfont icon-wechat-pay"></i>
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</el-col>
|
||||||
</ItemList>
|
</el-row>
|
||||||
|
<el-empty description="暂无数据" />
|
||||||
|
|
||||||
<h2 class="headline">消费账单</h2>
|
<h2 class="headline">消费账单</h2>
|
||||||
|
|
||||||
@ -132,7 +138,7 @@
|
|||||||
<el-icon>
|
<el-icon>
|
||||||
<InfoFilled/>
|
<InfoFilled/>
|
||||||
</el-icon>
|
</el-icon>
|
||||||
<span class="text">请打开手机{{ payName }}扫码支付</span>
|
<span class="text">请打开手机扫码支付</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -146,7 +152,6 @@
|
|||||||
import {onMounted, ref} from "vue"
|
import {onMounted, ref} from "vue"
|
||||||
import {ElMessage} from "element-plus";
|
import {ElMessage} from "element-plus";
|
||||||
import {httpGet, httpPost} from "@/utils/http";
|
import {httpGet, httpPost} from "@/utils/http";
|
||||||
import ItemList from "@/components/ItemList.vue";
|
|
||||||
import {InfoFilled, SuccessFilled} from "@element-plus/icons-vue";
|
import {InfoFilled, SuccessFilled} from "@element-plus/icons-vue";
|
||||||
import {checkSession, getSystemInfo} from "@/store/cache";
|
import {checkSession, getSystemInfo} from "@/store/cache";
|
||||||
import UserProfile from "@/components/UserProfile.vue";
|
import UserProfile from "@/components/UserProfile.vue";
|
||||||
@ -176,19 +181,16 @@ const text = ref("")
|
|||||||
const user = ref(null)
|
const user = ref(null)
|
||||||
const isLogin = ref(false)
|
const isLogin = ref(false)
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const curPayProduct = ref(null)
|
|
||||||
const activeOrderNo = ref("")
|
const activeOrderNo = ref("")
|
||||||
const countDownRef = ref(null)
|
const countDownRef = ref(null)
|
||||||
const orderTimeout = ref(1800)
|
const orderTimeout = ref(1800)
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
const orderPayInfoText = ref("")
|
const orderPayInfoText = ref("")
|
||||||
const vipMonthPower = ref(0)
|
|
||||||
const powerPrice = ref(0)
|
|
||||||
|
|
||||||
const payWays = ref({})
|
const payWays = ref([])
|
||||||
const amount = ref(0)
|
const amount = ref(0)
|
||||||
const payName = ref("支付宝")
|
const curPayProduct = ref(null)
|
||||||
const curPay = ref("alipay") // 当前支付方式
|
const curPayWay = ref({pay_way: "", pay_type:""})
|
||||||
const vipInfoText = ref("")
|
const vipInfoText = ref("")
|
||||||
const store = useSharedStore()
|
const store = useSharedStore()
|
||||||
const profileKey = ref(0)
|
const profileKey = ref(0)
|
||||||
@ -215,8 +217,6 @@ onMounted(() => {
|
|||||||
if (res.data['order_pay_timeout'] > 0) {
|
if (res.data['order_pay_timeout'] > 0) {
|
||||||
orderTimeout.value = res.data['order_pay_timeout']
|
orderTimeout.value = res.data['order_pay_timeout']
|
||||||
}
|
}
|
||||||
vipMonthPower.value = res.data['vip_month_power']
|
|
||||||
powerPrice.value = res.data['power_price']
|
|
||||||
vipInfoText.value = res.data['vip_info_text']
|
vipInfoText.value = res.data['vip_info_text']
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
ElMessage.error("获取系统配置失败:" + e.message)
|
ElMessage.error("获取系统配置失败:" + e.message)
|
||||||
@ -231,22 +231,24 @@ onMounted(() => {
|
|||||||
|
|
||||||
// refresh payment qrcode
|
// refresh payment qrcode
|
||||||
const refreshPayCode = () => {
|
const refreshPayCode = () => {
|
||||||
if (curPay.value === 'alipay') {
|
genPayQrcode(curPayProduct.value, curPayWay.value)
|
||||||
alipay(curPayProduct.value)
|
|
||||||
} else if (curPay.value === 'hupi') {
|
|
||||||
huPiPay(curPayProduct.value)
|
|
||||||
} else if (curPay.value === 'payjs') {
|
|
||||||
PayJs(curPayProduct.value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const genPayQrcode = () => {
|
const genPayQrcode = (product, payWay) => {
|
||||||
|
if (!isLogin.value) {
|
||||||
|
store.setShowLoginDialog(true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
loading.value = true
|
loading.value = true
|
||||||
text.value = ""
|
text.value = ""
|
||||||
|
curPayProduct.value = product
|
||||||
|
curPayWay.value = payWay
|
||||||
|
amount.value = (product.price - product.discount).toFixed(2)
|
||||||
httpPost("/api/payment/qrcode", {
|
httpPost("/api/payment/qrcode", {
|
||||||
pay_way: curPay.value,
|
pay_way: payWay.pay_way,
|
||||||
|
pay_type: payWay.pay_type,
|
||||||
product_id: curPayProduct.value.id,
|
product_id: curPayProduct.value.id,
|
||||||
user_id: user.value.id
|
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
showPayDialog.value = true
|
showPayDialog.value = true
|
||||||
qrcode.value = res.data['image']
|
qrcode.value = res.data['image']
|
||||||
@ -262,67 +264,6 @@ const genPayQrcode = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const alipay = (row) => {
|
|
||||||
payName.value = "支付宝"
|
|
||||||
curPay.value = "alipay"
|
|
||||||
amount.value = (row.price - row.discount).toFixed(2)
|
|
||||||
if (!isLogin.value) {
|
|
||||||
store.setShowLoginDialog(true)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (row) {
|
|
||||||
curPayProduct.value = row
|
|
||||||
}
|
|
||||||
genPayQrcode()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 虎皮椒支付
|
|
||||||
const huPiPay = (row) => {
|
|
||||||
payName.value = payWays.value["hupi"]["name"] === "wechat" ? '微信' : '支付宝'
|
|
||||||
curPay.value = "hupi"
|
|
||||||
amount.value = (row.price - row.discount).toFixed(2)
|
|
||||||
if (!isLogin.value) {
|
|
||||||
store.setShowLoginDialog(true)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (row) {
|
|
||||||
curPayProduct.value = row
|
|
||||||
}
|
|
||||||
genPayQrcode()
|
|
||||||
}
|
|
||||||
|
|
||||||
// PayJS 支付
|
|
||||||
const PayJs = (row) => {
|
|
||||||
payName.value = '微信'
|
|
||||||
curPay.value = "payjs"
|
|
||||||
amount.value = (row.price - row.discount).toFixed(2)
|
|
||||||
if (!isLogin.value) {
|
|
||||||
store.setShowLoginDialog(true)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (row) {
|
|
||||||
curPayProduct.value = row
|
|
||||||
}
|
|
||||||
genPayQrcode()
|
|
||||||
}
|
|
||||||
|
|
||||||
const wechatPay = (row) => {
|
|
||||||
payName.value = '微信'
|
|
||||||
curPay.value = "wechat"
|
|
||||||
amount.value = (row.price - row.discount).toFixed(2)
|
|
||||||
if (!isLogin.value) {
|
|
||||||
store.setShowLoginDialog(true)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (row) {
|
|
||||||
curPayProduct.value = row
|
|
||||||
}
|
|
||||||
genPayQrcode()
|
|
||||||
}
|
|
||||||
|
|
||||||
const queryOrder = (orderNo) => {
|
const queryOrder = (orderNo) => {
|
||||||
httpGet("/api/order/query", {order_no: orderNo}).then(res => {
|
httpGet("/api/order/query", {order_no: orderNo}).then(res => {
|
||||||
@ -331,11 +272,7 @@ const queryOrder = (orderNo) => {
|
|||||||
queryOrder(orderNo)
|
queryOrder(orderNo)
|
||||||
} else if (res.data.status === 2) {
|
} else if (res.data.status === 2) {
|
||||||
text.value = "支付成功,正在刷新页面"
|
text.value = "支付成功,正在刷新页面"
|
||||||
if (curPay.value === "payjs") {
|
setTimeout(() => location.reload(), 500)
|
||||||
setTimeout(() => location.reload(), 3000)
|
|
||||||
} else {
|
|
||||||
setTimeout(() => location.reload(), 500)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// 如果当前订单没有过期,继续等待订单的下一个状态
|
// 如果当前订单没有过期,继续等待订单的下一个状态
|
||||||
if (activeOrderNo.value === orderNo) {
|
if (activeOrderNo.value === orderNo) {
|
||||||
|
Loading…
Reference in New Issue
Block a user