添加微信商户号支付支持

This commit is contained in:
futuresnail
2024-04-29 21:09:10 +08:00
parent fbfa2a71a9
commit a90f00f7a4
18 changed files with 644 additions and 34 deletions

View File

@@ -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" # 异步回调地址,域名改成你自己的

View File

@@ -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

View File

@@ -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

View File

@@ -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=

View File

@@ -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)
}

View File

@@ -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()

View File

@@ -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 {

View File

@@ -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)

View 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
}

View File

@@ -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 会员
}