Compare commits

...

23 Commits

Author SHA1 Message Date
wolter 5326e9c8d9 更新models 2024-08-05 14:47:20 +08:00
wolter 490e2fa15f 后台,添加定时任务 2024-08-05 14:40:57 +08:00
陈俊宏 fb5c0e458b 回调查询 2024-08-02 18:14:04 +08:00
陈俊宏 b8e32f407a Merge branch 'dev/dev1.0' into feature_413_cjh
# Conflicts:
#	app/constants/common/common.go
2024-08-02 17:34:07 +08:00
wolter f1bb1ad53b fix 2024-08-02 17:32:49 +08:00
wolter b8da95788b 更新支付方式请求参数 2024-08-02 17:23:38 +08:00
wolter 88b95cb67a 后台 2024-08-02 17:21:18 +08:00
陈俊宏 4842aa47a3 必要位置添加日志 2024-08-02 16:53:48 +08:00
陈俊宏 a79b18d279 支付退款+退款单查询+订单关闭 2024-08-02 16:37:37 +08:00
陈俊宏 f1ee9b4196 支付退款+退款单查询+订单关闭 2024-08-02 16:37:07 +08:00
wolter bf4e24619d 修改后台路由,添加auth校验 2024-08-02 14:32:44 +08:00
wolter 92c1a3c230 添加dockerfile 2024-08-02 14:02:13 +08:00
wolter f8f0ac2913 应用和支付方式修改不能修改商户id 2024-08-02 13:54:25 +08:00
陈俊宏 4d4338f178 支付调起+订单查询 2024-08-02 13:49:28 +08:00
陈俊宏 c1e354e4ab 支付调起+订单查询 2024-08-02 13:47:52 +08:00
wolter 7a3823a2a7 更新订单和订单日志 2024-08-02 10:41:57 +08:00
陈俊宏 0b844afa5f Merge branch 'dev/dev1.0' into feature_413_cjh 2024-08-02 09:23:06 +08:00
wolter 5a60a92f4b 调整字段白名单和下游通知地址由app管理,更新应用模块 2024-08-02 09:18:49 +08:00
Rzy 4be80f228e <fix>(解决冲突):之后会另起分支 2024-08-01 14:53:22 +08:00
陈俊宏 1a292ff5d3 Merge branch 'dev/dev1.0' into feature_413_cjh
# Conflicts:
#	app/constants/common/common.go
#	app/data/app.go
#	app/data/order_log.go
#	app/data/pay_channel.go
2024-08-01 14:48:39 +08:00
陈俊宏 d343257e23 支付提交一波 2024-08-01 14:46:56 +08:00
wolter 95a050d64e 商户模块 2024-08-01 14:43:39 +08:00
wolter 689b0f80db 增加data层,更新model数据表 2024-07-31 18:38:17 +08:00
59 changed files with 3257 additions and 108 deletions

43
Dockerfile Normal file
View File

@ -0,0 +1,43 @@
## 使用官方Go镜像作为构建环境
#FROM golang:1.21.0 AS builder
#
## 设置工作目录
#WORKDIR /app
#
## 复制项目源码
#COPY . .
#
## 复制go模块依赖文件
#COPY go.mod go.sum ./
#
## 安装go模块依赖
#RUN go env -w GOPROXY=https://goproxy.cn,direct
#RUN go mod tidy
#
## 编译Go应用程序生成静态链接的二进制文件
#RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o server .
# 创建最终镜像用于运行编译后的Go程序
FROM alpine
RUN echo 'http://mirrors.ustc.edu.cn/alpine/v3.5/main' > /etc/apk/repositories \
&& echo 'http://mirrors.ustc.edu.cn/alpine/v3.5/community' >>/etc/apk/repositories \
&& apk update && apk add tzdata \
&& ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo "Asia/Shanghai" > /etc/timezone \
ENV server = "admin"
# 设置工作目录
WORKDIR /app
# 将编译好的二进制文件从构建阶段复制到运行阶段
COPY /server .
#COPY --from=builder /app/server .
COPY .env .
ENV TZ=Asia/Shanghai
# 设置容器启动时运行的命令
ENTRYPOINT ["/app/server", "-a", "$server"]

28
Makefile Normal file
View File

@ -0,0 +1,28 @@
GOHOSTOS:=$(shell go env GOHOSTOS)
GOPATH:=$(shell go env GOPATH)
VERSION=$(shell git describe --tags --always)
ifeq ($(GOHOSTOS), windows)
#the `find.exe` is different from `find` in bash/shell.
#to see https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/find.
#changed to use git-bash.exe to run find cli or other cli friendly, caused of every developer has a Git.
#Git_Bash= $(subst cmd\,bin\bash.exe,$(dir $(shell where git)))
Git_Bash=$(subst \,/,$(subst cmd\,bin\bash.exe,$(dir $(shell where git))))
INTERNAL_PROTO_FILES=$(shell $(Git_Bash) -c "find internal -name *.proto")
API_PROTO_FILES=$(shell $(Git_Bash) -c "find api -name *.proto")
else
INTERNAL_PROTO_FILES=$(shell find internal -name *.proto)
API_PROTO_FILES=$(shell find api -name *.proto)
endif
.PHONY: build
build:
sh build/shell/build.sh
.PHONY: api
api:
sh build/shell/build.sh & build/bin/snow -a api

View File

@ -1,9 +1,181 @@
package console
import (
"PaymentCenter/app/constants/common"
"PaymentCenter/app/data"
"PaymentCenter/app/http/entities"
"PaymentCenter/app/models/orderlogmodel"
"PaymentCenter/app/models/ordersmodel"
"PaymentCenter/app/third/paymentService"
"PaymentCenter/app/third/paymentService/payCommon"
"PaymentCenter/app/utils"
"PaymentCenter/config"
"context"
"encoding/json"
"github.com/qit-team/snow-core/command"
"strconv"
"sync"
"time"
"xorm.io/builder"
)
func RegisterCommand(c *command.Command) {
c.AddFunc("test", test)
c.AddFunc("closeOrder", closeOrder)
}
// 关闭长时间支付中的订单
func closeOrder() {
var now = time.Now().Format(time.DateTime)
utils.Log(nil, "关闭订单", now)
// 查询未支付的订单
repo := data.NewOrderRepo(ordersmodel.GetInstance().GetDb())
// 拼接条件
cond := builder.NewCond()
cond = cond.And(builder.Eq{"status": common.ORDER_STATUS_PAYING}, builder.Lt{"create_time": time.Now().Add(-time.Second * time.Duration(config.GetConf().CronConfig.CloseOrderTime))})
order := make([]ordersmodel.Orders, 0)
total, err := repo.OrderList(cond, entities.PageRequest{}, &order)
if err != nil {
utils.Log(nil, "关闭订单,查询未支付订单失败", err)
} else if total > 0 {
orderIds := make([]int64, 0)
for _, v := range order {
orderIds = append(orderIds, v.Id)
}
// 修改订单状态为关闭
cond = builder.NewCond()
cond = cond.And(builder.In("id", orderIds))
_, err = repo.OrderUpdate(&ordersmodel.Orders{Status: common.ORDER_STATUS_CLOSE}, cond, "status")
if err != nil {
utils.Log(nil, "关闭订单,修改订单状态失败", err)
return
}
}
utils.Log(nil, "关闭订单,修改订单状态成功", "count="+strconv.Itoa(len(order)))
}
// 主动查询订单支付状态
func queryOrder() {
var now = time.Now().Format(time.DateTime)
utils.Log(nil, "主动查询订单支付状态", now)
ctx := context.Background()
// 查询未支付的订单
repo := data.NewOrderRepo(ordersmodel.GetInstance().GetDb())
// 拼接条件
cond := builder.NewCond()
config.GetConf()
cond = cond.And(builder.Eq{"status": common.ORDER_STATUS_PAYING}, builder.Gt{"orders.create_time": time.Now().Add(-time.Second * time.Duration(config.GetConf().CronConfig.QueryOrderTime))})
order := make([]ordersmodel.OrdersLeftPayChannelList, 0)
err := repo.OrdersLeftPayChannelList(cond, entities.PageRequest{}, &order)
if err != nil {
utils.Log(nil, "主动查询订单支付状态,查询未付中订单失败", err)
return
} else if len(order) > 0 {
ch := make(chan struct{}, config.GetConf().CronConfig.ConcurrentNumber)
wg := sync.WaitGroup{}
for index := range order {
ch <- struct{}{}
wg.Add(1)
orderInfo := order[index]
// 发起查询上游支付
go func(orderInfo ordersmodel.OrdersLeftPayChannelList) {
defer func() {
<-ch
wg.Done()
}()
query := paymentService.PayOrderQueryRequest{
OrderId: orderInfo.Id,
}
switch orderInfo.ChannelType {
case common.PAY_CHANNEL_WECHAT_H5, common.PAY_CHANNEL_WECHAT_JSAPI, common.PAY_CHANNEL_WECHAT_NATIVE, common.PAY_CHANNEL_WECHAT_APP, common.PAY_CHANNEL_WECHAT_MINI:
_ = json.Unmarshal([]byte(orderInfo.ExtJson), &query.Wx)
query.PayChannel = payCommon.PAY_CHANNLE_TYPE_WECHAT
case common.PAY_CHANNEL_ALIPAY_JSAPI, common.PAY_CHANNEL_ALIPAY_WEB, common.PAY_CHANNEL_ALIPAY_MINI:
query.PayChannel = payCommon.PAY_CHANNLE_TYPE_ZFB
_ = json.Unmarshal([]byte(orderInfo.ExtJson), &query.Ali)
}
// 发起查询
result := paymentService.PayOrderQuery(ctx, query)
utils.Log(nil, "主动查询订单支付状态,上游返回数据", result)
// 查询成功,校验状态
var status int
if result.Code == payCommon.PAY_SUCCESS_CODE {
switch result.Result.TradeState {
case "SUCCESS":
// 成功
status = common.ORDER_STATUS_PAYED
case "REFUND":
// 退款
case "NOTPAY":
// 未支付
return
case "CLOSED":
// 关闭
status = common.ORDER_STATUS_CLOSE
}
// 回调通知下游 todo
// 更新订单状态 todo
orderUpdate := ordersmodel.Orders{
Id: orderInfo.Id,
Status: status,
}
session := ordersmodel.GetInstance().GetDb().NewSession()
if err = session.Begin(); err != nil {
utils.Log(nil, "主动查询订单支付状态,更新订单状态失败", err)
return
}
defer func() {
if err != nil {
session.Rollback()
} else {
err = session.Commit()
}
}()
orderLogRepo := data.NewOrderLogRepo(session)
orderRepo := data.NewOrderRepo(session)
conn := builder.NewCond()
conn = conn.And(builder.Eq{"id": orderInfo.Id})
_, err = orderRepo.OrderUpdate(&orderUpdate, conn)
if err != nil {
utils.Log(nil, "主动查询订单支付状态,更新订单状态失败", err)
return
}
// 写入支付日志
log := orderlogmodel.OrderLog{
OrderId: orderInfo.Id,
PayCallback: "",
Status: 0,
MerchantParam: "",
MerchantCallback: "",
}
_, err = orderLogRepo.OrderLogInsertOne(&log)
if err != nil {
utils.Log(nil, "主动查询订单支付状态,写入支付日志失败", err)
}
}
}(orderInfo)
}
wg.Wait()
}
}
// 回调下游
func callbackOrder() {
var now = time.Now().Format(time.DateTime)
utils.Log(nil, "回调下游", now)
// 查询回调失败的订单
// 发起回调
}

View File

@ -1,6 +1,8 @@
package console
import (
"PaymentCenter/config"
"fmt"
"github.com/robfig/cron"
)
@ -9,7 +11,10 @@ import (
* @wiki https://godoc.org/github.com/robfig/cron
*/
func RegisterSchedule(c *cron.Cron) {
fmt.Println(config.GetConf().CronConfig)
//c.AddFunc("0 30 * * * *", test)
//c.AddFunc("@hourly", test)
c.AddFunc("@every 10s", test)
//c.AddFunc("@every 10s", test)
c.AddFunc("@every 60s", closeOrder)
c.AddFunc("@every 10s", queryOrder)
}

View File

@ -1,7 +1,31 @@
package common
const (
TOKEN_PRE = "player_token_"
TOKEN_Admin = "Admin_token_"
ADMIN_V1 = "/admin/api/v1"
TOKEN_PRE = "player_token_"
TOKEN_Admin = "Admin_token_"
ADMIN_V1 = "/pay/admin/api/v1"
FRONT_API_V1 = "/v1"
// 支付渠道枚举,1微信JSAPI2微信H53微信app4微信Native5微信小程序6支付宝网页&移动应用7支付宝小程序8支付宝JSAPI
PAY_CHANNEL_UNKNOWN = 0
PAY_CHANNEL_WECHAT_JSAPI = 1
PAY_CHANNEL_WECHAT_H5 = 2
PAY_CHANNEL_WECHAT_APP = 3
PAY_CHANNEL_WECHAT_NATIVE = 4
PAY_CHANNEL_WECHAT_MINI = 5
PAY_CHANNEL_ALIPAY_WEB = 6
PAY_CHANNEL_ALIPAY_MINI = 7
PAY_CHANNEL_ALIPAY_JSAPI = 8
// 统一登陆信息
ADMIN_USER_ID = "User-Id"
ADMIN_USER_NAME = "User-Name"
ADMIN_USER_INCLUDEUSERS = "Include-Users"
// '订单状态1待支付、2支付中、3支付成功、4支付失败、5订单关闭',
ORDER_STATUS_WAITPAY = 1
ORDER_STATUS_PAYING = 2
ORDER_STATUS_PAYED = 3
ORDER_STATUS_FAILED = 4
ORDER_STATUS_CLOSE = 5
)

View File

@ -21,6 +21,13 @@ const (
//未登录
NotLogin = 1000
// 商户
MerchantNotFound = 1100
AppNotFound = 1200
PayChannelNotFound = 1300
)
var MsgEN = map[int]string{
@ -38,6 +45,10 @@ var MsgZH = map[int]string{
NotFound: "数据不存在",
NotAuth: "未经授权",
NotLogin: "未登录",
MerchantNotFound: "商户不存在",
AppNotFound: "应用不存在",
PayChannelNotFound: "支付方式不存在",
}
var MsgMap map[string]map[int]string = map[string]map[int]string{"en": MsgZH}

0
app/data/.gitkeep Normal file
View File

43
app/data/app.go Normal file
View File

@ -0,0 +1,43 @@
package data
import (
"PaymentCenter/app/http/entities"
"PaymentCenter/app/models/appmodel"
"xorm.io/builder"
"xorm.io/xorm"
)
type AppRepo struct {
repo xorm.Interface
}
func NewAppRepo(repo xorm.Interface) *AppRepo {
return &AppRepo{
repo: repo,
}
}
func (m *AppRepo) AppList(conn builder.Cond, pageFilter entities.PageRequest, appList *[]appmodel.App) (int64, error) {
repo := m.repo.Where(conn)
if pageFilter.Page > 0 {
repo = repo.Limit(pageFilter.PageSize, pageFilter.PageSize*(pageFilter.Page-1))
}
return repo.Desc("create_time").FindAndCount(appList)
}
func (m *AppRepo) AppInsertOne(app *appmodel.App) (int64, error) {
return m.repo.InsertOne(app)
}
func (m *AppRepo) AppDelete(app *appmodel.App, conn builder.Cond) (int64, error) {
return m.repo.Where(conn).Delete(app)
}
// columns 参数为要更新的字段
func (m *AppRepo) AppUpdate(app *appmodel.App, conn builder.Cond, columns ...string) (int64, error) {
return m.repo.Where(conn).MustCols(columns...).Update(app)
}
func (m *AppRepo) AppGet(app *appmodel.App, conn builder.Cond) (bool, error) {
return m.repo.Where(conn).Get(app)
}

1
app/data/merchant.go Normal file
View File

@ -0,0 +1 @@
package data

43
app/data/merchat.go Normal file
View File

@ -0,0 +1,43 @@
package data
import (
"PaymentCenter/app/http/entities"
"PaymentCenter/app/models/merchantmodel"
"xorm.io/builder"
"xorm.io/xorm"
)
type MerchantRepo struct {
repo xorm.Interface
}
func NewMerchantRepo(repo xorm.Interface) *MerchantRepo {
return &MerchantRepo{
repo: repo,
}
}
func (m *MerchantRepo) MerchantList(conn builder.Cond, pageFilter entities.PageRequest, merchantList *[]merchantmodel.Merchant) (int64, error) {
repo := m.repo.Where(conn)
if pageFilter.Page > 0 {
repo = repo.Limit(pageFilter.PageSize, pageFilter.PageSize*(pageFilter.Page-1))
}
return repo.Desc("create_time").FindAndCount(merchantList)
}
func (m *MerchantRepo) MerchantInsertOne(merchant *merchantmodel.Merchant) (int64, error) {
return m.repo.InsertOne(merchant)
}
func (m *MerchantRepo) MerchantDelete(merchant *merchantmodel.Merchant, conn builder.Cond) (int64, error) {
return m.repo.Where(conn).Delete(merchant)
}
// columns 参数为要更新的字段
func (m *MerchantRepo) MerchantUpdate(merchant *merchantmodel.Merchant, conn builder.Cond, columns ...string) (int64, error) {
return m.repo.Where(conn).MustCols(columns...).Update(merchant)
}
func (m *MerchantRepo) MerchantDetail(merchant *merchantmodel.Merchant, conn builder.Cond) (bool, error) {
return m.repo.Where(conn).Get(merchant)
}

39
app/data/order_log.go Normal file
View File

@ -0,0 +1,39 @@
package data
import (
"PaymentCenter/app/http/entities"
"PaymentCenter/app/models/orderlogmodel"
"xorm.io/builder"
"xorm.io/xorm"
)
type OrderLogRepo struct {
repo xorm.Interface
}
func NewOrderLogRepo(repo xorm.Interface) *OrderLogRepo {
return &OrderLogRepo{
repo: repo,
}
}
func (m *OrderLogRepo) OrderLogList(conn builder.Cond, pageFilter entities.PageRequest, orderLogList *[]orderlogmodel.OrderLog) (int64, error) {
repo := m.repo.Where(conn)
if pageFilter.Page > 0 {
repo = repo.Limit(pageFilter.PageSize, pageFilter.PageSize*(pageFilter.Page-1))
}
return repo.Desc("create_time").FindAndCount(orderLogList)
}
func (m *OrderLogRepo) OrderLogInsertOne(orderLog *orderlogmodel.OrderLog) (int64, error) {
return m.repo.InsertOne(orderLog)
}
func (m *OrderLogRepo) OrderLogDelete(orderLog *orderlogmodel.OrderLog, conn builder.Cond) (int64, error) {
return m.repo.Where(conn).Delete(orderLog)
}
// columns 参数为要更新的字段
func (m *OrderLogRepo) OrderLogUpdate(orderLog *orderlogmodel.OrderLog, conn builder.Cond, columns ...string) (int64, error) {
return m.repo.Where(conn).MustCols(columns...).Update(orderLog)
}

61
app/data/orders.go Normal file
View File

@ -0,0 +1,61 @@
package data
import (
"PaymentCenter/app/http/entities"
"PaymentCenter/app/models/ordersmodel"
"xorm.io/builder"
"xorm.io/xorm"
)
type OrderRepo struct {
repo xorm.Interface
}
func NewOrderRepo(repo xorm.Interface) *OrderRepo {
return &OrderRepo{
repo: repo,
}
}
func (m *OrderRepo) OrderList(conn builder.Cond, pageFilter entities.PageRequest, orderList *[]ordersmodel.Orders) (int64, error) {
repo := m.repo.Where(conn)
if pageFilter.Page > 0 {
repo = repo.Limit(pageFilter.PageSize, pageFilter.PageSize*(pageFilter.Page-1))
}
return repo.Desc("create_time").FindAndCount(orderList)
}
func (m *OrderRepo) OrderInsertOne(order *ordersmodel.Orders) (int64, error) {
return m.repo.InsertOne(order)
}
func (m *OrderRepo) OrderDelete(order *ordersmodel.Orders, conn builder.Cond) (int64, error) {
return m.repo.Where(conn).Delete(order)
}
// columns 参数为要更新的字段
func (m *OrderRepo) OrderUpdate(order *ordersmodel.Orders, conn builder.Cond, columns ...string) (int64, error) {
return m.repo.Where(conn).MustCols(columns...).Update(order)
}
func (m *OrderRepo) OrdersBackendList(conn builder.Cond, pageFilter entities.PageRequest, orderList *[]ordersmodel.OrdersBackendList) (int64, error) {
repo := m.repo.Select(`orders.*, merchant.name as merchant_name, app.app_name, pay_channel.pay_name`).
Where(conn)
if pageFilter.Page > 0 {
repo = repo.Limit(pageFilter.PageSize, pageFilter.PageSize*(pageFilter.Page-1))
}
repo = repo.Join("left", "app", "app.id = orders.app_id").
Join("left", "merchant", "merchant.id = orders.merchant_id").
Join("left", "pay_channel", "pay_channel.id = orders.pay_id")
return repo.Desc("create_time").FindAndCount(orderList)
}
func (m *OrderRepo) OrdersLeftPayChannelList(conn builder.Cond, pageFilter entities.PageRequest, orderList *[]ordersmodel.OrdersLeftPayChannelList) error {
repo := m.repo.Select(`orders.*,pay_channel.channel_type,pay_channel.app_id ,pay_channel.ext_json`).
Where(conn)
if pageFilter.Page > 0 {
repo = repo.Limit(pageFilter.PageSize, pageFilter.PageSize*(pageFilter.Page-1))
}
repo = repo.Join("left", "pay_channel", "pay_channel.id = orders.pay_id")
return repo.Find(orderList)
}

43
app/data/pay_channel.go Normal file
View File

@ -0,0 +1,43 @@
package data
import (
"PaymentCenter/app/http/entities"
"PaymentCenter/app/models/paychannelmodel"
"xorm.io/builder"
"xorm.io/xorm"
)
type PayChannelRepo struct {
repo xorm.Interface
}
func NewPayChannelRepo(repo xorm.Interface) *PayChannelRepo {
return &PayChannelRepo{
repo: repo,
}
}
func (m *PayChannelRepo) PayChannelList(conn builder.Cond, pageFilter entities.PageRequest, payChannelList *[]paychannelmodel.PayChannel) (int64, error) {
repo := m.repo.Where(conn)
if pageFilter.Page > 0 {
repo = repo.Limit(pageFilter.PageSize, pageFilter.PageSize*(pageFilter.Page-1))
}
return repo.Desc("create_time").FindAndCount(payChannelList)
}
func (m *PayChannelRepo) PayChannelInsertOne(payChannel *paychannelmodel.PayChannel) (int64, error) {
return m.repo.InsertOne(payChannel)
}
func (m *PayChannelRepo) PayChannelDelete(payChannel *paychannelmodel.PayChannel, conn builder.Cond) (int64, error) {
return m.repo.Where(conn).Delete(payChannel)
}
// columns 参数为要更新的字段,即使为空
func (m *PayChannelRepo) PayChannelUpdate(payChannel *paychannelmodel.PayChannel, conn builder.Cond, columns ...string) (int64, error) {
return m.repo.Where(conn).MustCols(columns...).Update(payChannel)
}
func (m *PayChannelRepo) PayChannelGet(payChannel *paychannelmodel.PayChannel, conn builder.Cond) (bool, error) {
return m.repo.Where(conn).Get(payChannel)
}

View File

@ -0,0 +1,75 @@
package backend
import (
"PaymentCenter/app/constants/errorcode"
"PaymentCenter/app/http/controllers"
"PaymentCenter/app/http/entities"
"PaymentCenter/app/http/entities/backend"
"PaymentCenter/app/models/appmodel"
"PaymentCenter/app/services"
"PaymentCenter/app/utils/sm2"
"github.com/ahmetb/go-linq/v3"
"github.com/gin-gonic/gin"
)
func AppList(c *gin.Context) {
req, _ := controllers.GetRequest(c).(*backend.AppListRequest)
req.SetDefault()
payChannelList, total, code := services.AppList(*req)
result := make([]backend.AppResponse, 0, len(payChannelList))
linq.From(payChannelList).SelectT(func(in appmodel.App) (out backend.AppResponse) {
out.ResponseFromDb(in)
return
}).ToSlice(&result)
data := entities.PageRsp{
Total: total,
Data: result,
}
controllers.HandCodeRes(c, data, code)
}
func AppCreate(c *gin.Context) {
req, _ := controllers.GetRequest(c).(*backend.AppCreateRequest)
payChannel := req.RequestToDb()
code := services.AppCreate(&payChannel)
data := backend.AppResponse{}
data.ResponseFromDb(payChannel)
controllers.HandCodeRes(c, data, code)
}
func AppUpdate(c *gin.Context) {
req, _ := controllers.GetRequest(c).(*backend.AppUpdateRequest)
payChannel := req.RequestToDb()
code := services.AppUpdate(&payChannel)
data := backend.AppResponse{}
data.ResponseFromDb(payChannel)
controllers.HandCodeRes(c, data, code)
}
func AppDelete(c *gin.Context) {
req, _ := controllers.GetRequest(c).(*entities.IdRequest)
code := services.AppDelete(*req)
controllers.HandCodeRes(c, nil, code)
}
func GenerateDecrypt(c *gin.Context) {
req, _ := controllers.GetRequest(c).(*backend.GenerateDecryptKeyRequest)
var publicKey, privateKey string
var err error
switch req.KeyType {
default:
publicKey, privateKey, err = sm2.GenerateSM2Key()
if err != nil {
controllers.Error(c, errorcode.SystemError, err.Error())
return
}
}
controllers.HandCodeRes(c, map[string]string{
"publicKey": publicKey,
"privateKey": privateKey,
}, errorcode.Success)
}

View File

@ -0,0 +1,55 @@
package backend
import (
"PaymentCenter/app/http/controllers"
"PaymentCenter/app/http/entities"
"PaymentCenter/app/http/entities/backend"
"PaymentCenter/app/models/merchantmodel"
"PaymentCenter/app/services"
"github.com/ahmetb/go-linq/v3"
"github.com/gin-gonic/gin"
)
func MerchantList(c *gin.Context) {
req, _ := controllers.GetRequest(c).(*backend.MerchantListRequest)
req.SetDefault()
req.GroupInfo = controllers.GetAdminUserIncludeUsers(c)
merchantList, total, code := services.MerchantList(*req)
result := make([]backend.MerchantResponse, 0, len(merchantList))
linq.From(merchantList).SelectT(func(in merchantmodel.Merchant) (out backend.MerchantResponse) {
out.ResponseFromDb(in)
return
}).ToSlice(&result)
data := entities.PageRsp{
Total: total,
Data: result,
}
controllers.HandCodeRes(c, data, code)
}
func MerchantCreate(c *gin.Context) {
req, _ := controllers.GetRequest(c).(*backend.MerchantCreateRequest)
merchant := req.RequestToDb()
code := services.MerchantCreate(&merchant)
data := backend.MerchantResponse{}
data.ResponseFromDb(merchant)
controllers.HandCodeRes(c, data, code)
}
func MerchantUpdate(c *gin.Context) {
req, _ := controllers.GetRequest(c).(*backend.MerchantUpdateRequest)
merchant := req.RequestToDb()
code := services.MerchantUpdate(&merchant)
data := backend.MerchantResponse{}
data.ResponseFromDb(merchant)
controllers.HandCodeRes(c, data, code)
}
func MerchantDelete(c *gin.Context) {
req, _ := controllers.GetRequest(c).(*entities.IdRequest)
code := services.MerchantDelete(*req)
controllers.HandCodeRes(c, nil, code)
}

View File

@ -0,0 +1,51 @@
package backend
import (
"PaymentCenter/app/constants/errorcode"
"PaymentCenter/app/http/controllers"
"PaymentCenter/app/http/entities"
"PaymentCenter/app/http/entities/backend"
"PaymentCenter/app/models/orderlogmodel"
"PaymentCenter/app/models/ordersmodel"
"PaymentCenter/app/services"
"github.com/ahmetb/go-linq/v3"
"github.com/gin-gonic/gin"
)
func OrderList(c *gin.Context) {
req, _ := controllers.GetRequest(c).(*backend.OrderListRequest)
filter, err := req.ValidateRequest()
if err != nil {
controllers.Error(c, errorcode.ParamError, err.Error())
return
}
orderList, total, code := services.OrderList(filter)
result := make([]backend.OrdersResponse, 0, len(orderList))
linq.From(orderList).SelectT(func(in ordersmodel.OrdersBackendList) (out backend.OrdersResponse) {
out.ResponseFromDb(in)
return
}).ToSlice(&result)
data := entities.PageRsp{
Total: total,
Data: result,
}
controllers.HandCodeRes(c, data, code)
}
func OrderLogsList(c *gin.Context) {
req, _ := controllers.GetRequest(c).(*backend.OrderLogsListRequest)
req.SetDefault()
orderLogList, total, code := services.OrderLogsList(*req)
result := make([]backend.OrderLogResponse, 0, len(orderLogList))
linq.From(orderLogList).SelectT(func(in orderlogmodel.OrderLog) (out backend.OrderLogResponse) {
out.ResponseFromDb(in)
return
}).ToSlice(&result)
data := entities.PageRsp{
Total: total,
Data: result,
}
controllers.HandCodeRes(c, data, code)
}

View File

@ -0,0 +1,63 @@
package backend
import (
"PaymentCenter/app/constants/errorcode"
"PaymentCenter/app/http/controllers"
"PaymentCenter/app/http/entities"
"PaymentCenter/app/http/entities/backend"
"PaymentCenter/app/models/paychannelmodel"
"PaymentCenter/app/services"
"github.com/ahmetb/go-linq/v3"
"github.com/gin-gonic/gin"
)
func PayChannelList(c *gin.Context) {
req, _ := controllers.GetRequest(c).(*backend.PayChannelListRequest)
req.SetDefault()
payChannelList, total, code := services.PayChannelList(*req)
result := make([]backend.PayChannelResponse, 0, len(payChannelList))
linq.From(payChannelList).SelectT(func(in paychannelmodel.PayChannel) (out backend.PayChannelResponse) {
out.ResponseFromDb(in)
return
}).ToSlice(&result)
data := entities.PageRsp{
Total: total,
Data: result,
}
controllers.HandCodeRes(c, data, code)
}
func PayChannelCreate(c *gin.Context) {
req, _ := controllers.GetRequest(c).(*backend.PayChannelCreateRequest)
payChannel, err := req.RequestToDb()
if err != nil {
controllers.Error(c, errorcode.ParamError, err.Error())
return
}
code := services.PayChannelCreate(&payChannel)
data := backend.PayChannelResponse{}
data.ResponseFromDb(payChannel)
controllers.HandCodeRes(c, data, code)
}
func PayChannelUpdate(c *gin.Context) {
req, _ := controllers.GetRequest(c).(*backend.PayChannelUpdateRequest)
payChannel, err := req.RequestToDb()
if err != nil {
controllers.Error(c, errorcode.ParamError, err.Error())
return
}
code := services.PayChannelUpdate(&payChannel)
data := backend.PayChannelResponse{}
data.ResponseFromDb(payChannel)
controllers.HandCodeRes(c, data, code)
}
func PayChannelDelete(c *gin.Context) {
req, _ := controllers.GetRequest(c).(*entities.IdRequest)
code := services.PayChannelDelete(*req)
controllers.HandCodeRes(c, nil, code)
}

View File

@ -1,6 +1,7 @@
package controllers
import (
"PaymentCenter/app/constants/common"
"PaymentCenter/app/utils"
"PaymentCenter/config"
"bytes"
@ -15,6 +16,9 @@ import (
zh_translations "gopkg.in/go-playground/validator.v9/translations/zh"
"io/ioutil"
"net/http"
"reflect"
"regexp"
"strconv"
"PaymentCenter/app/constants/errorcode"
@ -24,7 +28,11 @@ import (
/**
* 成功时返回
*/
func Success(c *gin.Context, data interface{}, message string) {
func Success(c *gin.Context, data interface{}, messageSlice ...string) {
var message string
if len(messageSlice) > 0 {
message = messageSlice[0]
}
if message == "" {
message = errorcode.GetMsg(errorcode.Success, c.GetHeader("local"))
}
@ -108,12 +116,26 @@ func GenRequest(c *gin.Context, request interface{}) (msgs []string, err error)
if err == nil {
validate := validator.New()
_ = validate.RegisterValidation("phoneValidation", phoneValidation)
zh_ch := zh.New()
uni := ut.New(zh_ch)
trans, _ := uni.GetTranslator("zh")
_ = validate.RegisterTranslation("phoneValidation", trans, func(ut ut.Translator) error {
return ut.Add("phoneValidation", "手机号不合法", true) // 添加翻译
}, func(ut ut.Translator, fe validator.FieldError) string {
t, _ := ut.T("phoneValidation", fe.Field()) // 获取翻译
return t
},
)
//注册一个函数获取struct tag里自定义的label作为字段名
validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
name := fld.Tag.Get("label")
return name
})
//验证器注册翻译器
zh_translations.RegisterDefaultTranslations(validate, trans)
_ = zh_translations.RegisterDefaultTranslations(validate, trans)
errValidate := validate.Struct(request)
if errValidate != nil {
@ -175,3 +197,36 @@ func Frequence(key string) bool {
return true
}
}
// 自定义验证器
func phoneValidation(fl validator.FieldLevel) bool {
phone := fl.Field().String()
if phone == "" {
return true
}
// 使用正则表达式验证手机号
phoneRegex := `^1[3-9]\d{9}$`
reg := regexp.MustCompile(phoneRegex)
return reg.MatchString(phone)
}
// 管理后台获取用户id
func GetAdminId(c *gin.Context) int {
userIdStr, _ := c.Get(common.ADMIN_USER_ID)
if userIdStr != nil {
var userId, _ = strconv.Atoi(userIdStr.(string))
return userId
}
return 0
}
// 获取对应数据权限中拥有用户数据ID
func GetAdminUserIncludeUsers(c *gin.Context) string {
name, _ := c.Get(common.ADMIN_USER_INCLUDEUSERS)
if name != nil {
var temp, _ = name.(string)
return temp
}
return ""
}

View File

@ -0,0 +1,10 @@
package front
import (
"PaymentCenter/app/http/controllers"
"github.com/gin-gonic/gin"
)
func HelloHandler(c *gin.Context) {
controllers.Success(c, "aaaa")
}

View File

@ -0,0 +1,127 @@
package front
import (
"PaymentCenter/app/constants/common"
"PaymentCenter/app/constants/errorcode"
"PaymentCenter/app/models/paychannelmodel"
"PaymentCenter/app/services"
"PaymentCenter/app/third/paymentService"
"encoding/json"
"fmt"
"github.com/gin-gonic/gin"
"github.com/go-pay/gopay"
"github.com/go-pay/gopay/alipay"
"github.com/go-pay/gopay/wechat/v3"
"github.com/go-pay/xlog"
"github.com/qit-team/snow-core/log/logger"
"strconv"
"net/http"
)
// WxCallback 微信支付回调
func WxCallback(c *gin.Context) {
payChannelId := c.Param("payChannelId")
logger.Info(c, "WxCallback-回调数据payChannelId", payChannelId)
if payChannelId == "" {
c.String(http.StatusBadRequest, "%s", "fail")
}
// 查询应用下的支付配置
var payChannelModel paychannelmodel.PayChannel
payChannelIdInt, _ := strconv.Atoi(payChannelId)
payChannelModel.Id = int64(payChannelIdInt)
services.PayChannelGet(&payChannelModel)
if payChannelModel.ChannelType != common.PAY_CHANNEL_WECHAT_H5 {
logger.Error(c, "WxCallback-回调数据解析支付配置错误,查询的数据不是当前渠道")
c.String(http.StatusBadRequest, "%s", "fail")
}
var wxConfig paymentService.WxPay
err := json.Unmarshal([]byte(payChannelModel.ExtJson), &wxConfig)
if err != nil {
logger.Error(c, "WxCallback-回调数据解析支付配置错误", fmt.Sprintf("错误原因:%s", err.Error()))
c.String(http.StatusBadRequest, "%s", "fail")
}
if wxConfig.ApiV3Key == "" || wxConfig.MchId == "" || wxConfig.PrivateKey == "" || wxConfig.SerialNo == "" {
logger.Error(c, "WxCallback-回调数据解析支付配置错误,解析出来的信息为空")
c.String(http.StatusBadRequest, "%s", "fail")
}
wxConfig.AppId = payChannelModel.AppId
logger.Info(c, "WxCallback-回调数据", c.Request)
notifyReq, err := wechat.V3ParseNotify(c.Request)
if err != nil {
logger.Error(c, "WxCallback-回调数据验签失败", err.Error())
return
}
err = paymentService.WxPayCallBack(notifyReq, wxConfig)
if err != nil {
logger.Error(c, "WxCallback-回调执行失败,失败原因:", err.Error())
return
}
// ====↓↓↓====异步通知应答====↓↓↓====
// 退款通知http应答码为200且返回状态码为SUCCESS才会当做商户接收成功否则会重试。
// 注意:重试过多会导致微信支付端积压过多通知而堵塞,影响其他正常通知。
c.JSON(http.StatusOK, &wechat.V3NotifyRsp{Code: gopay.SUCCESS, Message: "成功"})
}
// AliCallback 支付宝支付回调
func AliCallback(c *gin.Context) {
payChannelId := c.Param("payChannelId")
logger.Info(c, "AliCallback-回调数据APPID", payChannelId)
if payChannelId == "" {
c.String(http.StatusBadRequest, "%s", "fail")
}
// 查询应用下的支付配置
var payChannelModel paychannelmodel.PayChannel
payChannelIdInt, _ := strconv.Atoi(payChannelId)
payChannelModel.Id = int64(payChannelIdInt)
code := services.PayChannelGet(&payChannelModel)
if payChannelModel.ChannelType != common.PAY_CHANNEL_ALIPAY_WEB {
logger.Error(c, "AliCallback-回调数据解析支付配置错误,查询的数据不是当前渠道")
c.String(http.StatusBadRequest, "%s", "fail")
}
if code == errorcode.PayChannelNotFound {
logger.Error(c, "AliCallback-回调数据未获取到支付配置404")
c.String(http.StatusBadRequest, "%s", "fail")
}
var aliConfig paymentService.AliPay
var aliConfigModel struct {
PrivateKey string `json:"private_key"` // 应用私钥
AppPublicCert string `json:"app_public_cert"` // 应用公钥
AlipayRootCert string `json:"alipay_root_cert"` // 支付宝根证书
AlipayPublicCert string `json:"alipay_public_cert"` // 支付宝公钥
}
err := json.Unmarshal([]byte(payChannelModel.ExtJson), &aliConfigModel)
if err != nil {
logger.Error(c, "AliCallback-回调数据解析支付配置错误", fmt.Sprintf("错误原因:%s", err.Error()))
c.String(http.StatusBadRequest, "%s", "fail")
}
if aliConfigModel.AlipayPublicCert == "" || aliConfigModel.PrivateKey == "" || aliConfigModel.AppPublicCert == "" || aliConfigModel.AlipayRootCert == "" {
logger.Error(c, "AliCallback-回调数据解析支付配置错误,解析出来的信息为空")
c.String(http.StatusBadRequest, "%s", "fail")
}
aliConfig.AppId = payChannelModel.AppId
aliConfig.PrivateKey = aliConfigModel.PrivateKey
aliConfig.AppPublicCert = []byte(aliConfigModel.AppPublicCert)
aliConfig.AlipayRootCert = []byte(aliConfigModel.AlipayRootCert)
aliConfig.AlipayPublicCert = []byte(aliConfigModel.AlipayPublicCert)
notifyReq, err := alipay.ParseNotifyToBodyMap(c.Request) // c.Request 是 gin 框架的写法
logger.Info(c, "AliCallback-回调数据", c.Request)
if err != nil {
xlog.Error(err)
return
}
err = paymentService.ALiCallBack(notifyReq, aliConfig)
if err != nil {
logger.Error(c, "AliCallback-回调执行失败,失败原因:", err.Error())
return
}
c.String(http.StatusOK, "%s", "success")
}

View File

@ -1,12 +0,0 @@
package domains
type Filter struct {
Page int `json:"page"`
PageSize int `json:"page_size"`
}
type ProductFilter struct {
Page int `json:"page" form:"page"`
PageSize int `json:"page_size" form:"page_size"`
ProductName string `json:"product_name" form:"product_name"`
}

View File

@ -1,23 +0,0 @@
package domains
import "time"
type Product struct {
Id int
ProductName string
ProductType int
PacketRule int
ProductKind int
Amount string
CouponType int
IsSuperposition int
TemplateId int
Status int
IsNeedBill int
CreateTime time.Time
SupplierProductId int64
SupplierProductName string
Creator string
UpdateTime time.Time
}

View File

@ -0,0 +1,100 @@
package backend
import (
"PaymentCenter/app/http/entities"
"PaymentCenter/app/models/appmodel"
)
type AppListRequest struct {
Id int64 `json:"id" form:"id"`
MerchantId int64 `json:"merchant_id" form:"merchant_id" validate:"required" label:"商户ID"`
entities.PageRequest
}
type AppResponse struct {
Id int64 `json:"id"`
MerchantId int64 `json:"merchant_id"`
AppName string `json:"app_name"`
AppRemark string `json:"app_remark"`
Status int `json:"status"`
KeyType int `json:"key_type"`
PublicKey string `json:"public_key"`
PrivateKey string `json:"private_key"`
MerchantPublicKey string `json:"merchant_public_key"`
CreateTime string `json:"create_time"`
WhiteIp string `json:"white_ip"`
NotifyUrl string `json:"notify_url"`
}
func (a *AppResponse) ResponseFromDb(db appmodel.App) {
a.Id = db.Id
a.MerchantId = db.MerchantId
a.AppName = db.AppName
a.AppRemark = db.AppRemark
a.Status = db.Status
a.KeyType = db.KeyType
a.PublicKey = db.PublicKey
a.PrivateKey = db.PrivateKey
a.MerchantPublicKey = db.MerchantPublicKey
a.CreateTime = db.CreateTime.Format("2006-01-02 15:04:05")
a.WhiteIp = db.WhiteIp
a.NotifyUrl = db.NotifyUrl
}
type AppCreateRequest struct {
MerchantId int64 `json:"merchant_id" validate:"required" label:"商户ID"`
AppName string `json:"app_name" validate:"required" label:"应用名称"`
AppRemark string `json:"app_remark" label:"应用备注"`
Status int `json:"status" validate:"oneof=0 1 2" label:"应用状态"`
KeyType int `json:"key_type" validate:"required" label:"应用密钥类型"`
PublicKey string `json:"public_key" validate:"required" label:"应用公钥"`
PrivateKey string `json:"private_key" validate:"required" label:"应用私钥"`
MerchantPublicKey string `json:"merchant_public_key" label:"商户公钥"`
WhiteIp string `json:"white_ip"`
NotifyUrl string `json:"notify_url" validate:"required" label:"通知地址"`
}
func (a *AppCreateRequest) RequestToDb() (db appmodel.App) {
db.MerchantId = a.MerchantId
db.AppName = a.AppName
db.AppRemark = a.AppRemark
db.Status = a.Status
db.KeyType = a.KeyType
db.PublicKey = a.PublicKey
db.PrivateKey = a.PrivateKey
db.MerchantPublicKey = a.MerchantPublicKey
db.WhiteIp = a.WhiteIp
db.NotifyUrl = a.NotifyUrl
return
}
type AppUpdateRequest struct {
Id int64 `json:"id" validate:"required" label:"应用ID"`
AppName string `json:"app_name" label:"应用名称"`
AppRemark string `json:"app_remark" label:"应用备注"`
Status int `json:"status" label:"应用状态"`
KeyType int `json:"key_type" label:"应用密钥类型"`
PublicKey string `json:"public_key" label:"应用公钥"`
PrivateKey string `json:"private_key" label:"应用私钥"`
MerchantPublicKey string `json:"merchant_public_key" label:"商户公钥"`
WhiteIp string `json:"white_ip"`
NotifyUrl string `json:"notify_url"`
}
func (a *AppUpdateRequest) RequestToDb() (db appmodel.App) {
db.Id = a.Id
db.AppName = a.AppName
db.AppRemark = a.AppRemark
db.Status = a.Status
db.KeyType = a.KeyType
db.PublicKey = a.PublicKey
db.PrivateKey = a.PrivateKey
db.MerchantPublicKey = a.MerchantPublicKey
db.WhiteIp = a.WhiteIp
db.NotifyUrl = a.NotifyUrl
return
}
type GenerateDecryptKeyRequest struct {
KeyType int `json:"key_type" label:"密钥类型"`
}

View File

@ -0,0 +1,66 @@
package backend
import (
"PaymentCenter/app/http/entities"
"PaymentCenter/app/models/merchantmodel"
)
type MerchantListRequest struct {
entities.PageRequest
Name string `form:"name"`
Contact string `form:"contact"`
Phone string `form:"phone"`
GroupInfo string
}
type MerchantResponse struct {
Id int64 `json:"id"`
Name string `json:"name"`
Contact string `json:"contact"`
Phone string `json:"phone"`
Remark string `json:"remark"`
CreateTime string `json:"create_time"`
}
func (m *MerchantResponse) ResponseFromDb(db merchantmodel.Merchant) {
m.Id = db.Id
m.Name = db.Name
m.Contact = db.Contact
m.Phone = db.Phone
m.Remark = db.Remark
m.CreateTime = db.CreateTime.Format("2006-01-02 15:04:05")
}
type MerchantCreateRequest struct {
Name string `json:"name" validate:"required" label:"商户名称"`
Contact string `json:"contact" validate:"required" label:"联系人"`
Phone string `json:"phone" validate:"required,phoneValidation" label:"联系电话"`
Remark string `json:"remark" label:"备注"`
Creator int
}
func (m *MerchantCreateRequest) RequestToDb() (db merchantmodel.Merchant) {
db.Name = m.Name
db.Contact = m.Contact
db.Phone = m.Phone
db.Remark = m.Remark
db.Creator = m.Creator
return db
}
type MerchantUpdateRequest struct {
Id int64 `json:"id" validate:"required" label:"商户ID"`
Name string `json:"name"`
Contact string `json:"contact"`
Phone string `json:"phone" validate:"phoneValidation" label:"联系电话"`
Remark string `json:"remark"`
}
func (m *MerchantUpdateRequest) RequestToDb() (db merchantmodel.Merchant) {
db.Id = m.Id
db.Name = m.Name
db.Contact = m.Contact
db.Phone = m.Phone
db.Remark = m.Remark
return db
}

View File

@ -0,0 +1,127 @@
package backend
import (
"PaymentCenter/app/http/entities"
"PaymentCenter/app/models/orderlogmodel"
"PaymentCenter/app/models/ordersmodel"
"PaymentCenter/app/utils"
"github.com/pkg/errors"
"time"
)
type OrderListRequest struct {
Id int64 `json:"id" form:"id"`
MerchantId int64 `json:"merchant_id" form:"merchant_id"`
PayId int64 `json:"pay_id" form:"pay_id"`
AppId int64 `json:"app_id" form:"app_id"`
MerchantOrderId string `json:"merchant_order_id" form:"merchant_order_id"`
Status int `json:"status" form:"status"`
OrderType int `json:"order_type" form:"order_type"`
StartTime string `json:"start_time" form:"start_time"`
EndTime string `json:"end_time" form:"end_time"`
entities.PageRequest
}
type OrderList struct {
Id int64 `json:"id"`
MerchantId int64 `json:"merchant_id"`
PayId int64 `json:"pay_id"`
AppId int64 `json:"app_id"`
MerchantOrderId string `json:"merchant_order_id"`
Status int `json:"status"`
OrderType int `json:"order_type"`
StartTime time.Time `json:"start_time"`
EndTime time.Time `json:"end_time"`
entities.PageRequest
}
func (o *OrderListRequest) ValidateRequest() (r OrderList, err error) {
if o.StartTime != "" {
r.StartTime, err = utils.StrToTimeShanghai(o.StartTime)
if err != nil {
err = errors.Wrap(err, "时间格式错误")
return
}
}
if o.EndTime != "" {
r.EndTime, err = utils.StrToTimeShanghai(o.EndTime)
if err != nil {
err = errors.Wrap(err, "时间格式错误")
return
}
}
r.Id = o.Id
r.MerchantId = o.MerchantId
r.PayId = o.PayId
r.AppId = o.AppId
r.MerchantOrderId = o.MerchantOrderId
r.Status = o.Status
r.OrderType = o.OrderType
r.PageRequest = o.PageRequest
r.SetDefault()
return
}
type OrdersResponse struct {
Id int64 `json:"id"`
MerchantId int64 `json:"merchant_id"`
PayChannelId int64 `json:"pay_channel_id"`
AppId int64 `json:"app_id"`
OutTreadNo string `json:"out_tread_no"`
Status int `json:"status"`
OrderType int `json:"order_type"`
Amount int `json:"amount"`
IpAddress string `json:"ip_address"`
MerchantRequest string `json:"merchant_request"`
MerchantResponse string `json:"merchant_response"`
OrderResponse string `json:"order_response"`
ExtJson string `json:"ext_json"`
CreateTime string `json:"create_time"`
UpdateTime string `json:"update_time"`
MerchantName string `json:"merchant_name"`
PayName string `json:"pay_name"`
AppName string `json:"app_name"`
}
func (o *OrdersResponse) ResponseFromDb(db ordersmodel.OrdersBackendList) {
o.Id = db.Id
o.MerchantId = db.MerchantId
o.PayChannelId = db.PayChannelId
o.AppId = db.AppId
o.OutTreadNo = db.OutTreadNo
o.Status = db.Status
o.OrderType = db.OrderType
o.Amount = db.Amount
o.ExtJson = db.ExtJson
o.CreateTime = db.CreateTime.Format("2006-01-02 15:04:05")
o.UpdateTime = db.UpdateTime.Format("2006-01-02 15:04:05")
o.MerchantName = db.MerchantName
o.PayName = db.PayName
o.AppName = db.AppName
}
type OrderLogsListRequest struct {
OrderId int64 `json:"order_id" form:"order_id"`
entities.PageRequest
}
type OrderLogResponse struct {
Id int64 `json:"id"`
OrderId int64 `json:"order_id"`
PayCallback string `json:"pay_callback"`
Status int `json:"status"`
MerchantParam string `json:"merchant_param"`
MerchantCallback string `json:"merchant_callback"`
CreateTime string `json:"create_time"`
}
func (o *OrderLogResponse) ResponseFromDb(db orderlogmodel.OrderLog) {
o.Id = db.Id
o.OrderId = db.OrderId
o.PayCallback = db.PayCallback
o.Status = db.Status
o.MerchantParam = db.MerchantParam
o.MerchantCallback = db.MerchantCallback
o.CreateTime = db.CreateTime.Format("2006-01-02 15:04:05")
}

View File

@ -0,0 +1,127 @@
package backend
import (
"PaymentCenter/app/constants/common"
"PaymentCenter/app/http/entities"
"PaymentCenter/app/models/paychannelmodel"
"PaymentCenter/app/utils"
"encoding/json"
"github.com/pkg/errors"
)
type PayChannelResponse struct {
Id int64 `json:"id"`
PayName string `json:"pay_name"`
MerchantId int64 `json:"merchant_id"`
ChannelType int `json:"channel_type"`
AppId string `json:"app_id"`
ExpireTime string `json:"expire_time"`
CreateTime string `json:"create_time"`
AliPayPayChannel *AliPayPayChannel `json:"ali_pay_pay_channel,omitempty"`
WechatPayChannel *WechatPayChannel `json:"wechat_pay_channel,omitempty"`
}
func (p *PayChannelResponse) ResponseFromDb(db paychannelmodel.PayChannel) {
p.Id = db.Id
p.PayName = db.PayName
p.MerchantId = db.MerchantId
p.ChannelType = db.ChannelType
p.AppId = db.AppId
p.ExpireTime = db.ExpireTime.Format("2006-01-02 15:04:05")
p.CreateTime = db.CreateTime.Format("2006-01-02 15:04:05")
switch db.ChannelType {
case common.PAY_CHANNEL_WECHAT_H5, common.PAY_CHANNEL_WECHAT_JSAPI, common.PAY_CHANNEL_WECHAT_NATIVE, common.PAY_CHANNEL_WECHAT_APP, common.PAY_CHANNEL_WECHAT_MINI:
_ = json.Unmarshal([]byte(db.ExtJson), &p.WechatPayChannel)
case common.PAY_CHANNEL_ALIPAY_JSAPI, common.PAY_CHANNEL_ALIPAY_WEB, common.PAY_CHANNEL_ALIPAY_MINI:
_ = json.Unmarshal([]byte(db.ExtJson), &p.AliPayPayChannel)
}
}
type PayChannelCreateRequest struct {
PayName string `json:"pay_name" validate:"required" label:"支付渠道名称"`
MerchantId int64 `json:"merchant_id" validate:"required" label:"商户ID"`
ChannelType int `json:"channel_type" validate:"required" label:"支付渠道"` //支付渠道枚举,1微信JSAPI2微信H53微信app4微信Native5微信小程序6支付宝网页&移动应用7支付宝小程序8支付宝JSAPI
AppId string `json:"app_id" validate:"required" label:"应用appId"`
ExpireTime string `json:"expire_time"`
AliPayPayChannel AliPayPayChannel `json:"ali_pay_pay_channel,omitempty"`
WechatPayChannel WechatPayChannel `json:"wechat_pay_channel,omitempty"`
}
type WechatPayChannel struct {
MchId string `json:"mch_id"` // 商户ID 或者服务商模式的 sp_mchid
SerialNo string `json:"serial_no"` // 商户证书的证书序列号
ApiV3Key string `json:"api_v_3_key"` // apiV3Key商户平台获取
PrivateKey string `json:"private_key"` // 私钥 apiclient_key.pem 读取后的内容
}
type AliPayPayChannel struct {
PrivateKey string `json:"private_key"` // 应用私钥
AppPublicCert string `json:"app_public_cert"` // 应用公钥
AlipayRootCert string `json:"alipay_root_cert"` // 支付宝根证书
AlipayPublicCert string `json:"alipay_public_cert"` // 支付宝公钥
}
func (p *PayChannelCreateRequest) RequestToDb() (db paychannelmodel.PayChannel, err error) {
db.PayName = p.PayName
db.MerchantId = p.MerchantId
db.ChannelType = p.ChannelType
db.AppId = p.AppId
if p.ExpireTime != "" {
db.ExpireTime, err = utils.StrToTimeShanghai(p.ExpireTime)
if err != nil {
err = errors.Wrap(err, "时间格式错误")
}
}
switch p.ChannelType {
case common.PAY_CHANNEL_WECHAT_H5, common.PAY_CHANNEL_WECHAT_JSAPI, common.PAY_CHANNEL_WECHAT_NATIVE, common.PAY_CHANNEL_WECHAT_APP, common.PAY_CHANNEL_WECHAT_MINI:
b, _ := json.Marshal(p.WechatPayChannel)
db.ExtJson = string(b)
case common.PAY_CHANNEL_ALIPAY_JSAPI, common.PAY_CHANNEL_ALIPAY_WEB, common.PAY_CHANNEL_ALIPAY_MINI:
b, _ := json.Marshal(p.AliPayPayChannel)
db.ExtJson = string(b)
default:
err = errors.New("支付渠道类型错误")
}
return
}
type PayChannelListRequest struct {
Id int64 `json:"id" form:"id" `
MerchantId int64 `json:"merchant_id" form:"merchant_id" validate:"required" label:"商户ID"`
entities.PageRequest
}
type PayChannelUpdateRequest struct {
Id int64 `json:"id" validate:"required"`
PayName string `json:"pay_name" validate:"required" label:"支付渠道名称"`
ChannelType int `json:"channel_type" validate:"required" label:"支付渠道"` //支付渠道枚举,1微信JSAPI2微信H53微信app4微信Native5微信小程序6支付宝网页&移动应用7支付宝小程序8支付宝JSAPI
AppId string `json:"app_id" validate:"required" label:"应用appId"`
ExpireTime string `json:"expire_time"`
AliPayPayChannel AliPayPayChannel `json:"ali_pay_pay_channel,omitempty"`
WechatPayChannel WechatPayChannel `json:"wechat_pay_channel,omitempty"`
}
func (p PayChannelUpdateRequest) RequestToDb() (db paychannelmodel.PayChannel, err error) {
db.Id = p.Id
db.PayName = p.PayName
db.ChannelType = p.ChannelType
db.AppId = p.AppId
if p.ExpireTime != "" {
db.ExpireTime, err = utils.StrToTimeShanghai(p.ExpireTime)
if err != nil {
err = errors.Wrap(err, "时间格式错误")
}
}
switch p.ChannelType {
case common.PAY_CHANNEL_WECHAT_H5, common.PAY_CHANNEL_WECHAT_JSAPI, common.PAY_CHANNEL_WECHAT_NATIVE, common.PAY_CHANNEL_WECHAT_APP, common.PAY_CHANNEL_WECHAT_MINI:
b, _ := json.Marshal(p.WechatPayChannel)
db.ExtJson = string(b)
case common.PAY_CHANNEL_ALIPAY_JSAPI, common.PAY_CHANNEL_ALIPAY_WEB, common.PAY_CHANNEL_ALIPAY_MINI:
b, _ := json.Marshal(p.AliPayPayChannel)
db.ExtJson = string(b)
default:
err = errors.New("支付渠道类型错误")
}
return
}

View File

@ -1,15 +1,24 @@
package entities
type IdRequest struct {
Id int64 `json:"id"`
Id int64 `json:"id" form:"id" valid:"Required"`
}
type PageRequest struct {
Page int64 `json:"page"`
PageSize int64 `json:"pageSize"`
Page int `json:"page" form:"page"`
PageSize int `json:"page_size" form:"page_size"`
}
func (p *PageRequest) SetDefault() {
if p.Page == 0 {
p.Page = 1
}
if p.PageSize == 0 {
p.PageSize = 10
}
}
type PageRsp struct {
Total int64 `json:"total"`
Data []interface{} `json:"data"`
Total int64 `json:"total"`
Data interface{} `json:"data"`
}

View File

@ -0,0 +1 @@
package front

View File

@ -6,10 +6,8 @@ import (
"PaymentCenter/app/http/controllers"
"PaymentCenter/app/http/requestmapping"
"PaymentCenter/app/utils"
"context"
"errors"
"PaymentCenter/config"
"github.com/gin-gonic/gin"
"github.com/qit-team/snow-core/redis"
"strings"
)
@ -59,20 +57,42 @@ func Cors() gin.HandlerFunc {
func AdminAuth() gin.HandlerFunc {
return func(c *gin.Context) {
var token = c.GetHeader("token")
//将token放入redis
var playerId, err = redis.GetRedis().Get(context.Background(), utils.GetRealKey(common.TOKEN_Admin+token)).Result()
if rs, errRedis := redis.GetRedis().SIsMember(context.Background(), "disabled_uids", playerId).Result(); errRedis == nil && rs {
err = errors.New(errorcode.GetMsg(errorcode.NotFound, ""))
redis.GetRedis().SRem(context.Background(), "disabled_uids", playerId)
}
if err == nil {
c.Set("playerId", playerId)
c.Next()
return
} else {
controllers.HandCodeRes(c, nil, errorcode.Forbidden)
ip, _ := c.RemoteIP()
utils.Log(c, "请求地址RemoteIP()", ip.String(), config.GetConf().AdminGate)
clientIp := c.ClientIP()
utils.Log(c, "请求地址clientIp", clientIp)
if config.GetConf().Debug == false && !utils.SliceInStr(ip.String(), config.GetConf().AdminGate) {
c.Abort()
controllers.HandCodeRes(c, nil, errorcode.Forbidden)
return
}
var userName = c.GetHeader("User-Name")
if userName != "" {
c.Set(common.ADMIN_USER_NAME, userName)
}
var IncludeUsers = c.GetHeader("Include-Users")
if IncludeUsers != "" {
c.Set(common.ADMIN_USER_INCLUDEUSERS, IncludeUsers)
}
var adminId = c.GetHeader("User-Id")
// 测试环境直接放行
if config.GetConf().Debug == true {
c.Set(common.ADMIN_USER_ID, adminId)
c.Next()
} else {
utils.Log(c, "请求header信息", "adminId="+adminId, "IncludeUsers="+IncludeUsers)
// 正式环境校验
if adminId != "" {
c.Set(common.ADMIN_USER_ID, adminId)
c.Next()
} else {
c.Abort()
controllers.HandCodeRes(c, nil, errorcode.NotAuth)
return
}
}
}
}
@ -93,8 +113,9 @@ func ValidateRequest() gin.HandlerFunc {
v := handler()
msg, err := controllers.GenRequest(c, v)
if err != nil {
utils.Log(c, "path", path)
utils.Log(c, "参数错误", "path=", path, "err=", err.Error(), "msg=", msg)
controllers.Error(c, errorcode.ParamError, msg...)
c.Abort()
} else {
c.Set("request", v)
c.Next()

View File

@ -1,8 +1,30 @@
package requestmapping
import (
"PaymentCenter/app/constants/common"
"PaymentCenter/app/http/entities"
"PaymentCenter/app/http/entities/backend"
)
var BackendRequestMap = map[string]func() interface{}{
//common.ADMIN_V1 + "/product/create": func() interface{} {
// return new(backend.ProductCreateRequest)
//},
// 商户
common.ADMIN_V1 + "/merchant/create": func() interface{} { return new(backend.MerchantCreateRequest) },
common.ADMIN_V1 + "/merchant/update": func() interface{} { return new(backend.MerchantUpdateRequest) },
common.ADMIN_V1 + "/merchant/list": func() interface{} { return new(backend.MerchantListRequest) },
common.ADMIN_V1 + "/merchant/delete": func() interface{} { return new(entities.IdRequest) },
// 支付方式
common.ADMIN_V1 + "/paychannel/create": func() interface{} { return new(backend.PayChannelCreateRequest) },
common.ADMIN_V1 + "/paychannel/update": func() interface{} { return new(backend.PayChannelUpdateRequest) },
common.ADMIN_V1 + "/paychannel/list": func() interface{} { return new(backend.PayChannelListRequest) },
common.ADMIN_V1 + "/paychannel/delete": func() interface{} { return new(entities.IdRequest) },
// 应用
common.ADMIN_V1 + "/app/create": func() interface{} { return new(backend.AppCreateRequest) },
common.ADMIN_V1 + "/app/update": func() interface{} { return new(backend.AppUpdateRequest) },
common.ADMIN_V1 + "/app/list": func() interface{} { return new(backend.AppListRequest) },
common.ADMIN_V1 + "/app/delete": func() interface{} { return new(entities.IdRequest) },
common.ADMIN_V1 + "/app/decrypt": func() interface{} { return new(backend.GenerateDecryptKeyRequest) },
// 订单
common.ADMIN_V1 + "/order/list": func() interface{} { return new(backend.OrderListRequest) },
common.ADMIN_V1 + "/order/log/list": func() interface{} { return new(backend.OrderLogsListRequest) },
}

View File

@ -1,7 +1,3 @@
package requestmapping
var FrontRequestMap = map[string]func() interface{}{
//"/v1/login": func() interface{} {
// return new(front.LoginRequest)
//},
}
var FrontRequestMap = map[string]func() interface{}{}

View File

@ -2,6 +2,7 @@ package routes
import (
"PaymentCenter/app/http/controllers"
"PaymentCenter/app/http/controllers/backend"
"PaymentCenter/app/http/middlewares"
"PaymentCenter/app/http/trace"
"PaymentCenter/app/utils"
@ -22,9 +23,34 @@ func RegisterAdminRoute(router *gin.Engine) {
}
}
//v1 := router.Group("/admin/api/v1")
//{
//
//}
v1 := router.Group("/pay/admin/api/v1", middlewares.AdminAuth(), middlewares.ValidateRequest())
{
// 商户管理
merchant := v1.Group("/merchant")
merchant.GET("/list", backend.MerchantList) // 商户列表
merchant.POST("/create", backend.MerchantCreate) // 商户创建
merchant.PUT("/update", backend.MerchantUpdate) // 商户更新
merchant.DELETE("/delete", backend.MerchantDelete) // 商户删除
// 支付方式
pay := v1.Group("/paychannel")
pay.GET("/list", backend.PayChannelList) // 支付方式列表
pay.POST("/create", backend.PayChannelCreate) // 支付方式创建
pay.PUT("/update", backend.PayChannelUpdate) // 支付方式更新
pay.DELETE("/delete", backend.PayChannelDelete) // 支付方式删除
// 应用管理
app := v1.Group("/app")
app.GET("/list", backend.AppList) // 应用列表
app.POST("/create", backend.AppCreate) // 应用创建
app.PUT("/update", backend.AppUpdate) // 应用更新
app.DELETE("/delete", backend.AppDelete) // 应用删除
app.GET("/decrypt", backend.GenerateDecrypt) // 生成密钥对
// 订单
order := v1.Group("/order")
order.GET("/list", backend.OrderList) // 订单列表
order.GET("/log/list", backend.OrderLogsList) // 订单日志列表
}
}

View File

@ -5,6 +5,7 @@ package routes
*/
import (
"PaymentCenter/app/http/controllers"
"PaymentCenter/app/http/controllers/front"
"PaymentCenter/app/http/middlewares"
"PaymentCenter/app/http/trace"
"PaymentCenter/app/utils/metric"
@ -49,6 +50,16 @@ func RegisterRoute(router *gin.Engine) {
//
//}
v1 := router.Group("/v1")
{
//回调处理
notify := v1.Group("/notify")
{
notify.POST("/wx/:payChannelId", front.WxCallback)
notify.POST("/ali/:payChannelId", front.AliCallback)
}
}
router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
//router.GET("/hello", controllers.HelloHandler)

View File

@ -0,0 +1,49 @@
package appmodel
import (
"github.com/qit-team/snow-core/db"
"sync"
"time"
)
var (
once sync.Once
m *AppModel
)
// 实体
type App struct {
Id int64
MerchantId int64 `xorm:"'merchant_id' bigint(20)"`
AppName string `xorm:"'app_name' varchar(20)"`
AppRemark string `xorm:"'app_remark' varchar(200)"`
Status int `xorm:"'status' TINYINT"`
KeyType int `xorm:"'key_type' TINYINT"`
PublicKey string `xorm:"'public_key' varchar(1024)"`
PrivateKey string `xorm:"'private_key' TEXT"`
MerchantPublicKey string `xorm:"'merchant_public_key' varchar(1024)"`
CreateTime time.Time `xorm:"'create_time' datetime created"`
UpdateTime time.Time `xorm:"'update_time' timestamp updated"`
DeleteTime time.Time `xorm:"'delete_time' timestamp deleted"`
NotifyUrl string `xorm:"'notify_url' varchar(128)"`
WhiteIp string `xorm:"'white_ip' varchar(255)"`
}
// 表名
func (m *App) TableName() string {
return "app"
}
// 私有化防止被外部new
type AppModel struct {
db.Model //组合基础Model集成基础Model的属性和方法
}
// 单例模式
func GetInstance() *AppModel {
once.Do(func() {
m = new(AppModel)
//m.DiName = "" //设置数据库实例连接默认db.SingletonMain
})
return m
}

View File

@ -0,0 +1,44 @@
package merchantmodel
import (
"github.com/qit-team/snow-core/db"
"sync"
"time"
)
var (
once sync.Once
m *MerchantModel
)
// 实体
type Merchant struct {
Id int64
Name string `xorm:"'name' varchar(128)"`
Contact string `xorm:"'contact' varchar(128)"`
Phone string `xorm:"'phone' varchar(11)"`
Remark string `xorm:"'remark' varchar(1024)"`
CreateTime time.Time `xorm:"'create_time' datetime created"`
UpdateTime time.Time `xorm:"'update_time' timestamp updated"`
DeleteTime time.Time `xorm:"'delete_time' timestamp deleted"`
Creator int `xorm:"'creator' int(11)"`
}
// 表名
func (m *Merchant) TableName() string {
return "merchant"
}
// 私有化防止被外部new
type MerchantModel struct {
db.Model //组合基础Model集成基础Model的属性和方法
}
// 单例模式
func GetInstance() *MerchantModel {
once.Do(func() {
m = new(MerchantModel)
//m.DiName = "" //设置数据库实例连接默认db.SingletonMain
})
return m
}

View File

@ -0,0 +1,42 @@
package orderlogmodel
import (
"github.com/qit-team/snow-core/db"
"sync"
"time"
)
var (
once sync.Once
m *OrderLogModel
)
// 实体
type OrderLog struct {
Id int64
OrderId int64 `xorm:"'order_id' bigint(20)"`
PayCallback string `xorm:"'pay_callback' varchar(255)"`
Status int `xorm:"'status' int(11)"`
MerchantParam string `xorm:"'merchant_param' varchar(255)"`
MerchantCallback string `xorm:"'merchant_callback' varchar(255)"`
CreateTime time.Time `xorm:"'create_time' datetime created"`
}
// 表名
func (m *OrderLog) TableName() string {
return "order_log"
}
// 私有化防止被外部new
type OrderLogModel struct {
db.Model //组合基础Model集成基础Model的属性和方法
}
// 单例模式
func GetInstance() *OrderLogModel {
once.Do(func() {
m = new(OrderLogModel)
//m.DiName = "" //设置数据库实例连接默认db.SingletonMain
})
return m
}

View File

@ -0,0 +1,42 @@
package orderrequestlogmodel
import (
"github.com/qit-team/snow-core/db"
"sync"
"time"
)
var (
once sync.Once
m *OrderRequestLogModel
)
// 实体
type OrderRequestLog struct {
Id int64 `xorm:"'id' bigint(20) pk autoincr"`
IpAddress string `xorm:"'ip_address' varchar(16)"`
MerchantRequest string `xorm:"'merchant_request' JSON"`
MerchantResponse string `xorm:"'merchant_response' JSON"`
CreateTime time.Time `xorm:"'create_time' datetime"`
UpdateTime time.Time `xorm:"'update_time' timestamp"`
Status int `xorm:"'status' TINYINT"`
}
// 表名
func (m *OrderRequestLog) TableName() string {
return "order_request_log"
}
// 私有化防止被外部new
type OrderRequestLogModel struct {
db.Model //组合基础Model集成基础Model的属性和方法
}
// 单例模式
func GetInstance() *OrderRequestLogModel {
once.Do(func() {
m = new(OrderRequestLogModel)
//m.DiName = "" //设置数据库实例连接默认db.SingletonMain
})
return m
}

View File

@ -0,0 +1,60 @@
package ordersmodel
import (
"github.com/qit-team/snow-core/db"
"sync"
"time"
)
var (
once sync.Once
m *OrdersModel
)
// 实体
type Orders struct {
Id int64
MerchantId int64 `xorm:"'merchant_id' bigint(20)"`
PayChannelId int64 `xorm:"'pay_channel_id' bigint(20)"`
AppId int64 `xorm:"'app_id' bigint(20)"`
OutTreadNo string `xorm:"'out_tread_no' varchar(50)"`
OrderType int `xorm:"'order_type' TINYINT"`
Amount int `xorm:"'amount' int(11)"`
ExtJson string `xorm:"'ext_json' varchar(1024)"`
CreateTime time.Time `xorm:"'create_time' datetime created"`
UpdateTime time.Time `xorm:"'update_time' timestamp updated"`
Status int `xorm:"'status' TINYINT"`
DeleteTime time.Time `xorm:"'delete_time' timestamp deleted"`
}
type OrdersBackendList struct {
Orders `xorm:"extends"`
MerchantName string `xorm:"'merchant_name' varchar(128)"`
PayName string `xorm:"'pay_name' varchar(128)"`
AppName string `xorm:"'app_name' varchar(128)"`
}
type OrdersLeftPayChannelList struct {
Orders `xorm:"extends"`
ChannelType int `xorm:"'channel_type' int(11)"`
AppId string `xorm:"'app_id' varchar(255)"`
ExtJson string `xorm:"'ext_json' JSON"`
}
// 表名
func (m *Orders) TableName() string {
return "orders"
}
// 私有化防止被外部new
type OrdersModel struct {
db.Model //组合基础Model集成基础Model的属性和方法
}
// 单例模式
func GetInstance() *OrdersModel {
once.Do(func() {
m = new(OrdersModel)
//m.DiName = "" //设置数据库实例连接默认db.SingletonMain
})
return m
}

View File

@ -0,0 +1,42 @@
package orderthirdpaylogmodel
import (
"github.com/qit-team/snow-core/db"
"sync"
"time"
)
var (
once sync.Once
m *OrderThirdPayLogModel
)
// 实体
type OrderThirdPayLog struct {
Id int64 `xorm:"'id' bigint(20) pk autoincr"`
OrderId int64 `xorm:"'order_id' bigint(20)"`
PayCallback string `xorm:"'pay_callback' varchar(255)"`
Status int `xorm:"'status' TINYINT"`
MerchantParam string `xorm:"'merchant_param' varchar(255)"`
MerchantCallback string `xorm:"'merchant_callback' varchar(255)"`
CreateTime time.Time `xorm:"'create_time' datetime"`
}
// 表名
func (m *OrderThirdPayLog) TableName() string {
return "order_third_pay_log"
}
// 私有化防止被外部new
type OrderThirdPayLogModel struct {
db.Model //组合基础Model集成基础Model的属性和方法
}
// 单例模式
func GetInstance() *OrderThirdPayLogModel {
once.Do(func() {
m = new(OrderThirdPayLogModel)
//m.DiName = "" //设置数据库实例连接默认db.SingletonMain
})
return m
}

View File

@ -0,0 +1,45 @@
package paychannelmodel
import (
"github.com/qit-team/snow-core/db"
"sync"
"time"
)
var (
once sync.Once
m *PayChannelModel
)
// 实体
type PayChannel struct {
Id int64
PayName string `xorm:"'pay_name' varchar(128)"`
MerchantId int64 `xorm:"'merchant_id' bigint(20)"`
ChannelType int `xorm:"'channel_type' int(11)"`
AppId string `xorm:"'app_id' varchar(255)"`
ExtJson string `xorm:"'ext_json' JSON"`
ExpireTime time.Time `xorm:"'expire_time' datetime"`
CreateTime time.Time `xorm:"'create_time' datetime created"`
UpdateTime time.Time `xorm:"'update_time' timestamp updated"`
DeleteTime time.Time `xorm:"'delete_time' timestamp deleted"`
}
// 表名
func (m *PayChannel) TableName() string {
return "pay_channel"
}
// 私有化防止被外部new
type PayChannelModel struct {
db.Model //组合基础Model集成基础Model的属性和方法
}
// 单例模式
func GetInstance() *PayChannelModel {
once.Do(func() {
m = new(PayChannelModel)
//m.DiName = "" //设置数据库实例连接默认db.SingletonMain
})
return m
}

View File

@ -14,7 +14,7 @@ func SingleTalk(tag uint64, ch interface{}, msg []byte) {
// }
// }
// if !sendOk {
// common.PikaTool.Zadd(utils.GetRealKey(common2.USER_MSG)+data.Msg.To, data, time.Now().Unix())
// payCommon.PikaTool.Zadd(utils.GetRealKey(common2.USER_MSG)+data.Msg.To, data, time.Now().Unix())
// }
//}

99
app/services/app.go Normal file
View File

@ -0,0 +1,99 @@
package services
import (
"PaymentCenter/app/constants/errorcode"
"PaymentCenter/app/data"
"PaymentCenter/app/http/entities"
"PaymentCenter/app/http/entities/backend"
"PaymentCenter/app/models/appmodel"
"PaymentCenter/app/models/merchantmodel"
"PaymentCenter/app/models/paychannelmodel"
"xorm.io/builder"
)
func AppList(req backend.AppListRequest) (result []appmodel.App, total int64, code int) {
repo := data.NewAppRepo(paychannelmodel.GetInstance().GetDb())
// 拼接查询条件
conn := builder.NewCond()
if req.MerchantId > 0 {
conn = conn.And(builder.Eq{"merchant_id": req.MerchantId})
}
if req.Id > 0 {
conn = conn.And(builder.Eq{"id": req.Id})
}
// 调用repo
appList := make([]appmodel.App, 0)
count, err := repo.AppList(conn, req.PageRequest, &appList)
code = handErr(err)
return appList, count, code
}
func AppCreate(app *appmodel.App) (code int) {
db := paychannelmodel.GetInstance().GetDb()
repo := data.NewAppRepo(db)
merchantRepo := data.NewMerchantRepo(db)
// 拼接查询条件
conn := builder.NewCond()
conn = conn.And(builder.Eq{"id": app.MerchantId})
merchant := merchantmodel.Merchant{}
has, err := merchantRepo.MerchantDetail(&merchant, conn)
if err != nil {
return handErr(err)
}
if !has {
return errorcode.MerchantNotFound
}
_, err = repo.AppInsertOne(app)
code = handErr(err)
return
}
func AppUpdate(app *appmodel.App) (code int) {
var err error
db := paychannelmodel.GetInstance().GetDb()
repo := data.NewAppRepo(db)
// 拼接查询条件
conn := builder.NewCond()
conn = conn.And(builder.Eq{"Id": app.Id})
if app.AppName != "" {
// 编辑页面更新备注和白名单IP可更新为空
_, err = repo.AppUpdate(app, conn, "app_remark", "white_ip")
} else {
_, err = repo.AppUpdate(app, conn)
}
code = handErr(err)
return
}
func AppDelete(req entities.IdRequest) (code int) {
repo := data.NewAppRepo(paychannelmodel.GetInstance().GetDb())
// 拼接查询条件
conn := builder.NewCond()
conn = conn.And(builder.Eq{"Id": req.Id})
m := appmodel.App{Id: req.Id}
_, err := repo.AppDelete(&m, conn)
code = handErr(err)
return
}
func AppGet(app *appmodel.App) (code int) {
repo := data.NewAppRepo(appmodel.GetInstance().GetDb())
// 拼接查询条件
conn := builder.NewCond()
conn = conn.And(builder.Eq{"id": app.Id})
has, err := repo.AppGet(app, conn)
if err != nil {
return handErr(err)
}
if !has {
return errorcode.AppNotFound
}
code = errorcode.Success
return
}

15
app/services/common.go Normal file
View File

@ -0,0 +1,15 @@
package services
import (
"PaymentCenter/app/constants/errorcode"
"PaymentCenter/app/utils"
)
func handErr(err error) int {
if err != nil {
utils.Log(nil, "sys err", err.Error())
return errorcode.SystemError
} else {
return errorcode.Success
}
}

67
app/services/merchant.go Normal file
View File

@ -0,0 +1,67 @@
package services
import (
"PaymentCenter/app/data"
"PaymentCenter/app/http/entities"
"PaymentCenter/app/http/entities/backend"
"PaymentCenter/app/models/merchantmodel"
"strings"
"xorm.io/builder"
)
// MerchantList 商户列表
func MerchantList(req backend.MerchantListRequest) (result []merchantmodel.Merchant, total int64, code int) {
repo := data.NewMerchantRepo(merchantmodel.GetInstance().GetDb())
// 拼接查询条件
conn := builder.NewCond()
if req.Name != "" {
conn = conn.And(builder.Like{"name", req.Name})
}
if req.Contact != "" {
conn = conn.And(builder.Like{"contact", req.Contact})
}
if req.Phone != "" {
conn = conn.And(builder.Like{"phone", req.Phone})
}
if req.GroupInfo != "" {
conn = conn.And(builder.In("creator", strings.Split(req.GroupInfo, ",")))
}
// 调用repo
merchantList := make([]merchantmodel.Merchant, 0)
count, err := repo.MerchantList(conn, req.PageRequest, &merchantList)
code = handErr(err)
return merchantList, count, code
}
func MerchantCreate(merchant *merchantmodel.Merchant) (code int) {
repo := data.NewMerchantRepo(merchantmodel.GetInstance().GetDb())
_, err := repo.MerchantInsertOne(merchant)
code = handErr(err)
return
}
func MerchantUpdate(merchant *merchantmodel.Merchant) (code int) {
repo := data.NewMerchantRepo(merchantmodel.GetInstance().GetDb())
// 拼接查询条件
conn := builder.NewCond()
conn = conn.And(builder.Eq{"Id": merchant.Id})
_, err := repo.MerchantUpdate(merchant, conn, "remark")
code = handErr(err)
return
}
func MerchantDelete(req entities.IdRequest) (code int) {
repo := data.NewMerchantRepo(merchantmodel.GetInstance().GetDb())
// 拼接查询条件
conn := builder.NewCond()
conn = conn.And(builder.Eq{"Id": req.Id})
m := merchantmodel.Merchant{Id: req.Id}
_, err := repo.MerchantDelete(&m, conn)
code = handErr(err)
return
}

64
app/services/order.go Normal file
View File

@ -0,0 +1,64 @@
package services
import (
"PaymentCenter/app/data"
"PaymentCenter/app/http/entities/backend"
"PaymentCenter/app/models/orderlogmodel"
"PaymentCenter/app/models/ordersmodel"
"PaymentCenter/app/models/paychannelmodel"
"xorm.io/builder"
)
// 订单列表,后台查询
func OrderList(req backend.OrderList) (result []ordersmodel.OrdersBackendList, total int64, code int) {
repo := data.NewOrderRepo(paychannelmodel.GetInstance().GetDb())
// 拼接查询条件
conn := builder.NewCond()
if req.Id > 0 {
conn = conn.And(builder.Eq{"id": req.Id})
}
if req.MerchantId > 0 {
conn = conn.And(builder.Eq{"merchant_id": req.MerchantId})
}
if req.PayId > 0 {
conn = conn.And(builder.Eq{"pay_id": req.PayId})
}
if req.AppId > 0 {
conn = conn.And(builder.Eq{"app_id": req.AppId})
}
if req.MerchantOrderId != "" {
conn = conn.And(builder.Like{"merchant_order_id", req.MerchantOrderId})
}
if req.Status > 0 {
conn = conn.And(builder.Eq{"status": req.Status})
}
if req.OrderType > 0 {
conn = conn.And(builder.Eq{"order_type": req.OrderType})
}
if !req.StartTime.IsZero() {
conn = conn.And(builder.Gte{"start_time": req.StartTime})
}
if !req.EndTime.IsZero() {
conn = conn.And(builder.Lte{"end_time": req.EndTime})
}
// 调用repo
orderList := make([]ordersmodel.OrdersBackendList, 0)
count, err := repo.OrdersBackendList(conn, req.PageRequest, &orderList)
code = handErr(err)
return orderList, count, code
}
func OrderLogsList(req backend.OrderLogsListRequest) (result []orderlogmodel.OrderLog, total int64, code int) {
repo := data.NewOrderLogRepo(paychannelmodel.GetInstance().GetDb())
conn := builder.NewCond()
if req.OrderId > 0 {
conn = conn.And(builder.Eq{"order_id": req.OrderId})
}
// 调用repo
orderLogList := make([]orderlogmodel.OrderLog, 0)
count, err := repo.OrderLogList(conn, req.PageRequest, &orderLogList)
code = handErr(err)
return orderLogList, count, code
}

View File

@ -0,0 +1,94 @@
package services
import (
"PaymentCenter/app/constants/errorcode"
"PaymentCenter/app/data"
"PaymentCenter/app/http/entities"
"PaymentCenter/app/http/entities/backend"
"PaymentCenter/app/models/merchantmodel"
"PaymentCenter/app/models/paychannelmodel"
"xorm.io/builder"
)
func PayChannelList(req backend.PayChannelListRequest) (result []paychannelmodel.PayChannel, total int64, code int) {
repo := data.NewPayChannelRepo(paychannelmodel.GetInstance().GetDb())
// 拼接查询条件
conn := builder.NewCond()
if req.MerchantId > 0 {
conn = conn.And(builder.Eq{"merchant_id": req.MerchantId})
}
if req.Id > 0 {
conn = conn.And(builder.Eq{"id": req.Id})
}
// 调用repo
paychannelList := make([]paychannelmodel.PayChannel, 0)
count, err := repo.PayChannelList(conn, req.PageRequest, &paychannelList)
code = handErr(err)
return paychannelList, count, code
}
func PayChannelCreate(payChannel *paychannelmodel.PayChannel) (code int) {
db := paychannelmodel.GetInstance().GetDb()
repo := data.NewPayChannelRepo(db)
merchantRepo := data.NewMerchantRepo(db)
// 拼接查询条件
conn := builder.NewCond()
conn = conn.And(builder.Eq{"id": payChannel.MerchantId})
merchant := merchantmodel.Merchant{}
has, err := merchantRepo.MerchantDetail(&merchant, conn)
if err != nil {
return handErr(err)
}
if !has {
return errorcode.MerchantNotFound
}
_, err = repo.PayChannelInsertOne(payChannel)
code = handErr(err)
return
}
func PayChannelUpdate(payChannel *paychannelmodel.PayChannel) (code int) {
db := paychannelmodel.GetInstance().GetDb()
repo := data.NewPayChannelRepo(db)
// 拼接查询条件
uconn := builder.NewCond()
uconn = uconn.And(builder.Eq{"Id": payChannel.Id})
_, err := repo.PayChannelUpdate(payChannel, uconn, "white_ip")
code = handErr(err)
return
}
func PayChannelDelete(req entities.IdRequest) (code int) {
repo := data.NewPayChannelRepo(paychannelmodel.GetInstance().GetDb())
// 拼接查询条件
conn := builder.NewCond()
conn = conn.And(builder.Eq{"Id": req.Id})
m := paychannelmodel.PayChannel{Id: req.Id}
_, err := repo.PayChannelDelete(&m, conn)
code = handErr(err)
return
}
func PayChannelGet(payChannel *paychannelmodel.PayChannel) (code int) {
repo := data.NewPayChannelRepo(paychannelmodel.GetInstance().GetDb())
// 拼接查询条件
conn := builder.NewCond()
conn = conn.And(builder.Eq{"id": payChannel.Id})
has, err := repo.PayChannelGet(payChannel, conn)
if err != nil {
return handErr(err)
}
if !has {
return errorcode.PayChannelNotFound
}
code = errorcode.Success
return
}

View File

@ -0,0 +1,273 @@
package paymentService
import (
"PaymentCenter/app/third/paymentService/payCommon"
"context"
"errors"
"fmt"
"github.com/go-pay/gopay"
"github.com/go-pay/gopay/alipay"
"github.com/qit-team/snow-core/log/logger"
"strconv"
"sync"
)
var (
aliClient *alipay.Client
aliClientErr error
aliOnce sync.Once
)
// AliInitClient 使用提供的支付请求参数初始化支付宝客户端
func AliInitClient(aliConfig AliPay) {
aliOnce.Do(func() {
// 初始化支付宝客户端
// appid应用ID
// privateKey应用私钥支持PKCS1和PKCS8
// isProd是否是正式环境沙箱环境请选择新版沙箱应用。
aliClient, aliClientErr = alipay.NewClient(aliConfig.AppId, aliConfig.PrivateKey, true)
})
// 自定义配置http请求接收返回结果body大小默认 10MB
aliClient.SetBodySize(10) // 没有特殊需求,可忽略此配置
// 打开Debug开关输出日志默认关闭
aliClient.DebugSwitch = gopay.DebugOn
// 设置支付宝请求 公共参数
// 注意:具体设置哪些参数,根据不同的方法而不同,此处列举出所有设置参数
aliClient.SetLocation(alipay.LocationShanghai). // 设置时区,不设置或出错均为默认服务器时间
SetCharset(alipay.UTF8). // 设置字符编码,不设置默认 utf-8
SetSignType(alipay.RSA2) // 设置签名类型,不设置默认 RSA2
// 自动同步验签(只支持证书模式)
// 传入 alipayPublicCert.crt 内容
aliClient.AutoVerifySign(aliConfig.AlipayPublicCert)
// 证书内容
aliClientErr = aliClient.SetCertSnByContent(aliConfig.AppPublicCert, aliConfig.AlipayRootCert, aliConfig.AlipayPublicCert)
}
// GetAliClient 获取已经初始化的支付宝客户端
func GetAliClient() (*alipay.Client, error) {
if aliClient == nil {
return nil, errors.New("client not initialized")
}
if aliClientErr != nil {
return nil, aliClientErr
}
return aliClient, nil
}
// ALiH5PayInfo 支付宝手机网站支付
func ALiH5PayInfo(c context.Context, payOrderRequest PayOrderRequest) (string, error) {
// 初始化支付宝客户端
AliInitClient(payOrderRequest.Ali)
// 获取支付宝客户端
aliClient, err := GetAliClient()
if err != nil {
fmt.Println("Failed to get client:", err)
return "", err
}
// 初始化 BodyMap
amount := float64(payOrderRequest.Amount) / 100.0
bm := make(gopay.BodyMap)
bm.Set("out_trade_no", payOrderRequest.OrderId).
Set("total_amount", amount).
Set("subject", payOrderRequest.Description).
//Set("notify_url", fmt.Sprintf(payCommon.ALI_NOTIFY_URL_TEST+"%d", payOrderRequest.PayChannelId)).
Set("notify_url", fmt.Sprintf(payCommon.ALI_NOTIFY_URL_PROD+"%d", payOrderRequest.PayChannelId))
aliRsp, err := aliClient.TradeWapPay(c, bm)
if err != nil {
logger.Error(c, "ALiH5PayInfo 发生错误", fmt.Sprintf("错误信息:%s", err.Error()))
if bizErr, ok := alipay.IsBizError(err); ok {
return "", bizErr
}
return "", err
}
return aliRsp, nil
}
// ALiCallBack 支付宝支付回调
func ALiCallBack(notifyReq gopay.BodyMap, aliConfig AliPay) error {
ok, err := alipay.VerifySignWithCert(aliConfig.AlipayPublicCert, notifyReq)
if !ok || err != nil {
return err
}
// todo 拼装数据触发下游回调,数据待定
return nil
}
// ALiOrderQuery 查询支付宝订单
func ALiOrderQuery(ctx context.Context, aliConfig AliPay, OrderNo string) (PayOrderQueryInfo, error) {
// 初始化支付宝客户端
AliInitClient(aliConfig)
// 获取支付宝客户端
aliClient, err := GetAliClient()
if err != nil {
fmt.Println("Failed to get client:", err)
return PayOrderQueryInfo{}, err
}
// 请求参数
bm := make(gopay.BodyMap)
bm.Set("out_trade_no", OrderNo)
// 查询订单
aliRsp, err := aliClient.TradeQuery(ctx, bm)
if err != nil {
if bizErr, ok := alipay.IsBizError(err); ok {
return PayOrderQueryInfo{}, bizErr
}
return PayOrderQueryInfo{}, err
}
// 同步返回验签
ok, err := alipay.VerifySyncSignWithCert(aliConfig.AlipayPublicCert, aliRsp.SignData, aliRsp.Sign)
if err != nil || !ok {
return PayOrderQueryInfo{}, errors.New(fmt.Sprintf("验签失败,失败原因:%s", err.Error()))
}
// 映射交易状态
tradeState := ""
tradeStateDesc := ""
switch aliRsp.Response.TradeStatus {
case "TRADE_SUCCESS":
tradeState = "SUCCESS"
tradeStateDesc = "交易支付成功"
case "REFUND":
tradeState = "REFUND"
case "WAIT_BUYER_PAY":
tradeState = "NOTPAY"
tradeStateDesc = "交易创建,等待买家付款"
case "TRADE_CLOSED":
tradeState = "CLOSED"
tradeStateDesc = "未付款交易超时关闭,或支付完成后全额退款"
}
amountTotal, _ := strconv.Atoi(aliRsp.Response.TotalAmount)
payerTotal, _ := strconv.Atoi(aliRsp.Response.BuyerPayAmount)
// 构建数据
outTradeNo, _ := strconv.Atoi(aliRsp.Response.OutTradeNo)
return PayOrderQueryInfo{
AppId: aliConfig.AppId,
OutTradeNo: int64(outTradeNo),
TransactionId: aliRsp.Response.TradeNo,
TradeState: tradeState,
TradeStateDesc: tradeStateDesc,
SuccessTime: aliRsp.Response.SendPayDate,
AmountTotal: int64(amountTotal * 100),
PayerTotal: int64(payerTotal * 100),
}, nil
}
// AliRefundOrder 支付宝退款申请
func AliRefundOrder(ctx context.Context, orderRefundRequest OrderRefundRequest) (OrderRefundInfo, error) {
// 初始化支付宝客户端
AliInitClient(orderRefundRequest.Ali)
// 获取支付宝客户端
aliClient, err := GetAliClient()
if err != nil {
fmt.Println("Failed to get client:", err)
return OrderRefundInfo{}, err
}
// 请求参数
bm := make(gopay.BodyMap)
bm.Set("out_trade_no", orderRefundRequest.OrderId).
Set("refund_amount", orderRefundRequest.RefundAmount).
Set("refund_reason", orderRefundRequest.RefundReason).
Set("out_request_no", orderRefundRequest.RefundOrderId)
// 发起退款请求
aliRsp, err := aliClient.TradeRefund(ctx, bm)
if err != nil {
logger.Error(ctx, "AliRefundOrder 发生错误", fmt.Sprintf("申请退款接口失败,错误信息:%s", err.Error()))
if bizErr, ok := alipay.IsBizError(err); ok {
return OrderRefundInfo{}, bizErr
}
return OrderRefundInfo{}, err
}
refundFee, _ := strconv.Atoi(aliRsp.Response.RefundFee)
outTradeNo, _ := strconv.Atoi(aliRsp.Response.OutTradeNo)
return OrderRefundInfo{
OutTradeNo: int64(outTradeNo),
TransactionId: aliRsp.Response.TradeNo,
RefundFee: int64(refundFee * 100),
RefundOrderId: orderRefundRequest.RefundOrderId,
RefundStatus: payCommon.PAY_REFUND_STATU_SUCCESS,
}, nil
}
// AliRefundOrderQuery 支付宝订单退款查询
func AliRefundOrderQuery(ctx context.Context, orderRefundQueryRequest OrderRefundQueryRequest) (OrderRefundInfo, error) {
// 初始化支付宝客户端
AliInitClient(orderRefundQueryRequest.Ali)
// 获取支付宝客户端
aliClient, err := GetAliClient()
if err != nil {
fmt.Println("Failed to get client:", err)
return OrderRefundInfo{}, err
}
// 请求参数
bm := make(gopay.BodyMap)
bm.Set("out_trade_no", orderRefundQueryRequest.OrderId).
Set("out_request_no", orderRefundQueryRequest.RefundOrderId).
Set("query_options", []string{"gmt_refund_pay"})
// 发起退款查询请求
aliRsp, err := aliClient.TradeFastPayRefundQuery(ctx, bm)
if err != nil {
if bizErr, ok := alipay.IsBizError(err); ok {
return OrderRefundInfo{}, bizErr
}
return OrderRefundInfo{}, err
}
refundFee, _ := strconv.Atoi(aliRsp.Response.RefundAmount)
outTradeNo, _ := strconv.Atoi(aliRsp.Response.OutTradeNo)
refundOrderId, _ := strconv.Atoi(aliRsp.Response.OutRequestNo)
return OrderRefundInfo{
OutTradeNo: int64(outTradeNo),
TransactionId: aliRsp.Response.TradeNo,
RefundFee: int64(refundFee * 100),
RefundOrderId: int64(refundOrderId),
RefundStatus: payCommon.PAY_REFUND_STATU_SUCCESS,
RefundSuccessTime: aliRsp.Response.GmtRefundPay,
}, nil
}
// AliCloseOrder 支付宝订单关闭
func AliCloseOrder(ctx context.Context, orderCloseRequest OrderCloseRequest) (OrderCloseInfo, error) {
// 初始化支付宝客户端
AliInitClient(orderCloseRequest.Ali)
// 获取支付宝客户端
aliClient, err := GetAliClient()
if err != nil {
fmt.Println("Failed to get client:", err)
return OrderCloseInfo{}, err
}
// 请求参数
bm := make(gopay.BodyMap)
bm.Set("out_trade_no", orderCloseRequest.OrderId)
// 关闭订单
aliRsp, err := aliClient.TradeClose(ctx, bm)
if err != nil {
logger.Error(ctx, "AliCloseOrder 发生错误", fmt.Sprintf("申请退款接口失败,错误信息:%s", err.Error()))
if bizErr, ok := alipay.IsBizError(err); ok {
return OrderCloseInfo{}, bizErr
}
return OrderCloseInfo{}, err
}
outTradeNo, _ := strconv.Atoi(aliRsp.Response.OutTradeNo)
return OrderCloseInfo{
OutTradeNo: int64(outTradeNo),
}, nil
}

View File

@ -0,0 +1,38 @@
package payCommon
const (
TEST_URL = ""
PROD_URL = ""
// 支付渠道枚举,1微信JSAPI2微信H53微信app4微信Native5微信小程序6支付宝网页&移动应用7支付宝小程序8支付宝JSAPI
PAY_CHANNEL_UNKNOWN = 0
PAY_CHANNEL_WECHAT_JSAPI = 1
PAY_CHANNEL_WECHAT_H5 = 2
PAY_CHANNEL_WECHAT_APP = 3
PAY_CHANNEL_WECHAT_NATIVE = 4
PAY_CHANNEL_WECHAT_MINI = 5
PAY_CHANNEL_ALIPAY_WEB = 6
PAY_CHANNEL_ALIPAY_MINI = 7
PAY_CHANNEL_ALIPAY_JSAPI = 8
PAY_SUCCESS_CODE = 200 // 支付响应成功
PAY_ERROR_CODE = 500 // 支付响应失败
PAY_NOT_FOUND_CODE = 404 // 未找到支付渠道
WX_SUCCESS_CODE = 200 // 微信状态码返回成功
WX_NOTIFY_URL_TEST = TEST_URL + "/v1/notify/wx/" // 微信支付回调地址
WX_NOTIFY_URL_PROD = PROD_URL + "/v1/notify/wx/" // 微信支付回调地址
ALI_NOTIFY_URL_TEST = TEST_URL + "/v1/notify/ali/" // 支付宝支付回调地址
ALI_NOTIFY_URL_PROD = PROD_URL + "/v1/notify/ali/" // 支付宝支付回调地址
ORDER_NO_TYPE_ORDER_NO = 2
PAY_CHANNLE_TYPE_WECHAT = 1 // 支付类型: 微信
PAY_CHANNLE_TYPE_ZFB = 2 // 支付类型:支付宝
PAY_REFUND_STATU_COMMON = 0 // 未申请
PAY_REFUND_STATU_ING = 1 // 退款中
PAY_REFUND_STATU_SUCCESS = 2 // 退款成功
PAY_REFUND_STATU_FAIL = 3 // 退款失败
)

View File

@ -0,0 +1,265 @@
package paymentService
import (
"PaymentCenter/app/third/paymentService/payCommon"
"context"
"github.com/qit-team/snow-core/log/logger"
"strconv"
)
type PayOrderRequest struct {
PayChannelId int64 `json:"pay_channel_id"` // 支付方式ID
OrderId int64 `json:"order_id"` // 平台订单号
ChannelType int `json:"channel_type"` // 支付方式
Description string `json:"description"` // 商品描述
Amount int `json:"amount"` // 金额单位为分
PayerClientIp string `json:"payer_client_ip"` // 终端IP
Wx WxPay `json:"wx"`
Ali AliPay `json:"ali"`
}
type WxPay struct {
AppId string `json:"app_id"` // 应用ID
MchId string `json:"mch_id"` // 商户ID 或者服务商模式的 sp_mchid
SerialNo string `json:"serial_no"` // 商户证书的证书序列号
ApiV3Key string `json:"api_v_3_key"` // apiV3Key商户平台获取
PrivateKey string `json:"private_key"` // 私钥 apiclient_key.pem 读取后的内容
}
type AliPay struct {
AppId string `json:"app_id"` // 应用ID
PrivateKey string `json:"private_key"` // 应用私钥
AppPublicCert []byte `json:"app_public_cert"` // 应用公钥
AlipayRootCert []byte `json:"alipay_root_cert"` // 支付宝根证书
AlipayPublicCert []byte `json:"alipay_public_cert"` // 支付宝公钥
}
type PayOrderResponse struct {
Code int `json:"code"`
ErrorMsg string `json:"error_msg"`
Result string `json:"result"`
}
// PaymentService 统一发起支付
func PaymentService(c context.Context, payOrderRequest PayOrderRequest) (payOrderResponse PayOrderResponse) {
logger.Info(c, "PaymentService 收到支付请求", payOrderRequest)
var err error
var info string
switch payOrderRequest.ChannelType {
case payCommon.PAY_CHANNEL_WECHAT_H5:
// 微信H5支付
info, err = WxH5PayInfo(c, payOrderRequest)
case payCommon.PAY_CHANNEL_ALIPAY_WEB:
// 支付宝H5支付
info, err = ALiH5PayInfo(c, payOrderRequest)
default:
return PayOrderResponse{
Code: payCommon.PAY_NOT_FOUND_CODE,
ErrorMsg: "暂不支持该支付渠道,请后续再使用",
}
}
if err != nil {
return PayOrderResponse{
Code: payCommon.PAY_ERROR_CODE,
ErrorMsg: err.Error(),
}
}
return PayOrderResponse{
Code: payCommon.PAY_SUCCESS_CODE,
ErrorMsg: "",
Result: info,
}
}
type PayOrderQueryRequest struct {
OrderId int64 `json:"order_id"` // 商户订单号
PayChannel int `json:"pay_channel"` // 支付渠道类型 1:wx,2:zfb
Wx WxPay `json:"wx"`
Ali AliPay `json:"ali"`
}
type PayOrderQueryResponse struct {
Code int `json:"code"`
ErrorMsg string `json:"error_msg"`
Result PayOrderQueryInfo `json:"result"`
}
type PayOrderQueryInfo struct {
AppId string `json:"app_id"` // 应用ID
OutTradeNo int64 `json:"out_trade_no"` // 商家订单号
TransactionId string `json:"transaction_id"` // 第三方订单号
TradeState string `json:"trade_state"` // 交易状态 SUCCESS成功、REFUND转入退款、NOTPAY未支付、CLOSED已关闭
TradeStateDesc string `json:"trade_state_desc"` // 交易描述
SuccessTime string `json:"success_time"` // 交易完成时间
AmountTotal int64 `json:"amount_total"` // 订单总金额,单位:分
PayerTotal int64 `json:"payer_total"` // 支付总金额,单位:分
}
// PayOrderQuery 查询订单状态
func PayOrderQuery(c context.Context, payOrderQueryRequest PayOrderQueryRequest) PayOrderQueryResponse {
var err error
var info PayOrderQueryInfo
switch payOrderQueryRequest.PayChannel {
case payCommon.PAY_CHANNLE_TYPE_WECHAT:
// 微信H5支付
info, err = WxOrderQuery(c, payOrderQueryRequest.Wx, strconv.FormatInt(payOrderQueryRequest.OrderId, 10))
case payCommon.PAY_CHANNLE_TYPE_ZFB:
info, err = ALiOrderQuery(c, payOrderQueryRequest.Ali, strconv.FormatInt(payOrderQueryRequest.OrderId, 10))
default:
return PayOrderQueryResponse{
Code: payCommon.PAY_ERROR_CODE,
ErrorMsg: "暂不支持该支付渠道,请后续再使用",
}
}
if err != nil {
return PayOrderQueryResponse{
Code: payCommon.PAY_ERROR_CODE,
ErrorMsg: err.Error(),
}
}
return PayOrderQueryResponse{
Code: payCommon.PAY_SUCCESS_CODE,
Result: info,
}
}
type OrderRefundRequest struct {
OrderId int64 `json:"order_id"` // 订单编号
RefundOrderId int64 `json:"refund_order_id"` // 退款订单号
RefundReason string `json:"refund_reason"` // 退款原因
RefundAmount int64 `json:"refund_amount"` // 退款金额,单位分
PayChannel int `json:"pay_channel"` // 支付渠道类型 1:wx,2:zfb
Wx WxPay `json:"wx"`
Ali AliPay `json:"ali"`
}
type OrderRefundResponse struct {
Code int `json:"code"`
ErrorMsg string `json:"error_msg"`
Result OrderRefundInfo `json:"result"`
}
type OrderRefundInfo struct {
OutTradeNo int64 `json:"out_trade_no"` // 商家订单号
TransactionId string `json:"transaction_id"` // 第三方订单号
RefundFee int64 `json:"refund_fee"` // 退款金额
RefundOrderId int64 `json:"refund_order_id"` // 退款订单号
RefundStatus int `json:"refund_status"` // 退款状态 0未申请1退款中2退款成功3退款失败
RefundSuccessTime string `json:"refund_success_time"` // 退款时间
}
// OrderRefund 订单退款
func OrderRefund(c context.Context, orderRefundRequest OrderRefundRequest) OrderRefundResponse {
logger.Info(c, "PaymentService 收到退款请求", orderRefundRequest)
var err error
var info OrderRefundInfo
switch orderRefundRequest.PayChannel {
case payCommon.PAY_CHANNLE_TYPE_WECHAT:
// 微信H5支付
info, err = WxOrderRefund(c, orderRefundRequest)
case payCommon.PAY_CHANNLE_TYPE_ZFB:
info, err = AliRefundOrder(c, orderRefundRequest)
default:
return OrderRefundResponse{
Code: payCommon.PAY_ERROR_CODE,
ErrorMsg: "暂不支持该支付渠道,请后续再使用",
}
}
if err != nil {
return OrderRefundResponse{
Code: payCommon.PAY_ERROR_CODE,
ErrorMsg: err.Error(),
}
}
return OrderRefundResponse{
Code: payCommon.PAY_SUCCESS_CODE,
Result: info,
}
}
type OrderRefundQueryRequest struct {
RefundOrderId int64 `json:"refund_order_id"` // 退款订单号
OrderId int64 `json:"order_id"` // 支付订单号
PayChannel int `json:"pay_channel"` // 支付渠道类型 1:wx,2:zfb
Wx WxPay `json:"wx"`
Ali AliPay `json:"ali"`
}
type OrderRefundQueryResponse struct {
Code int `json:"code"`
ErrorMsg string `json:"error_msg"`
Result OrderRefundInfo `json:"result"`
}
// OrderRefundQuery 订单退款查询
func OrderRefundQuery(c context.Context, orderRefundQueryRequest OrderRefundQueryRequest) OrderRefundQueryResponse {
var err error
var info OrderRefundInfo
switch orderRefundQueryRequest.PayChannel {
case payCommon.PAY_CHANNLE_TYPE_WECHAT:
// 微信H5支付
info, err = WxOrderRefundQuery(c, orderRefundQueryRequest)
case payCommon.PAY_CHANNLE_TYPE_ZFB:
info, err = AliRefundOrderQuery(c, orderRefundQueryRequest)
default:
return OrderRefundQueryResponse{
Code: payCommon.PAY_ERROR_CODE,
ErrorMsg: "暂不支持该支付渠道,请后续再使用",
}
}
if err != nil {
return OrderRefundQueryResponse{
Code: payCommon.PAY_ERROR_CODE,
ErrorMsg: err.Error(),
}
}
return OrderRefundQueryResponse{
Code: payCommon.PAY_SUCCESS_CODE,
Result: info,
}
}
type OrderCloseRequest struct {
OrderId int64 `json:"order_id"` // 支付订单号
PayChannel int `json:"pay_channel"` // 支付渠道类型 1:wx,2:zfb
Wx WxPay `json:"wx"`
Ali AliPay `json:"ali"`
}
type OrderCloseResponse struct {
Code int `json:"code"`
ErrorMsg string `json:"error_msg"`
Result OrderCloseInfo `json:"result"`
}
type OrderCloseInfo struct {
OutTradeNo int64 `json:"out_trade_no"` // 商家订单号
}
// OrderClose 关闭订单
func OrderClose(c context.Context, orderCloseRequest OrderCloseRequest) OrderCloseResponse {
logger.Info(c, "PaymentService 收到关闭订单请求", orderCloseRequest)
var err error
var info OrderCloseInfo
switch orderCloseRequest.PayChannel {
case payCommon.PAY_CHANNLE_TYPE_WECHAT:
// 微信H5支付
info, err = WxCloseOrder(c, orderCloseRequest)
case payCommon.PAY_CHANNLE_TYPE_ZFB:
info, err = AliCloseOrder(c, orderCloseRequest)
default:
return OrderCloseResponse{
Code: payCommon.PAY_ERROR_CODE,
ErrorMsg: "暂不支持该支付渠道,请后续再使用",
}
}
if err != nil {
return OrderCloseResponse{
Code: payCommon.PAY_ERROR_CODE,
ErrorMsg: err.Error(),
}
}
return OrderCloseResponse{
Code: payCommon.PAY_SUCCESS_CODE,
Result: info,
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,311 @@
package paymentService
import (
"PaymentCenter/app/third/paymentService/payCommon"
"context"
"errors"
"fmt"
"github.com/go-pay/gopay"
"github.com/go-pay/gopay/wechat/v3"
"github.com/qit-team/snow-core/log/logger"
"strconv"
"sync"
"time"
)
var (
wxClient *wechat.ClientV3
clientErr error
once sync.Once
)
// InitClient 使用提供的支付请求参数初始化微信客户端
func InitClient(wxConfig WxPay) {
once.Do(func() {
// NewClientV3 初始化微信客户端 v3
// mchid商户ID 或者服务商模式的 sp_mchid
// serialNo商户证书的证书序列号
// apiV3KeyapiV3Key商户平台获取
// privateKey私钥 apiclient_key.pem 读取后的内容
wxClient, clientErr = wechat.NewClientV3(
wxConfig.MchId,
wxConfig.SerialNo,
wxConfig.ApiV3Key,
wxConfig.PrivateKey,
)
})
// 启用自动同步返回验签并定时更新微信平台API证书开启自动验签时无需单独设置微信平台API证书和序列号
clientErr = wxClient.AutoVerifySign()
// 自定义配置http请求接收返回结果body大小默认 10MB
wxClient.SetBodySize(10) // 没有特殊需求,可忽略此配置
// 打开Debug开关输出日志默认是关闭的
wxClient.DebugSwitch = gopay.DebugOn
}
// GetClient 获取已经初始化的微信客户端
func GetClient() (*wechat.ClientV3, error) {
if wxClient == nil {
return nil, errors.New("client not initialized")
}
if clientErr != nil {
return nil, clientErr
}
return wxClient, nil
}
// WxH5PayInfo 微信H5支付
func WxH5PayInfo(c context.Context, payOrderRequest PayOrderRequest) (string, error) {
// 初始化微信客户端
InitClient(payOrderRequest.Wx)
// 获取微信客户端
wxClient, err := GetClient()
if err != nil {
return "", err
}
expire := time.Now().Add(10 * time.Minute).Format(time.RFC3339)
// 初始化 BodyMap
bm := make(gopay.BodyMap)
bm.Set("appid", payOrderRequest.Wx.AppId).
Set("mchid", payOrderRequest.Wx.MchId).
Set("description", payOrderRequest.Description).
Set("out_trade_no", payOrderRequest.OrderId).
Set("time_expire", expire).
//Set("notify_url", fmt.Sprintf(payCommon.WX_NOTIFY_URL_TEST+"%d", payOrderRequest.PayChannelId)).
Set("notify_url", fmt.Sprintf(payCommon.WX_NOTIFY_URL_PROD+"%d", payOrderRequest.PayChannelId)).
SetBodyMap("amount", func(bm gopay.BodyMap) {
bm.Set("total", payOrderRequest.Amount).
Set("currency", "CNY")
}).
SetBodyMap("scene_info", func(bm gopay.BodyMap) {
bm.Set("payer_client_ip", payOrderRequest.PayerClientIp).
SetBodyMap("h5_info", func(bm gopay.BodyMap) {
bm.Set("type", "common")
})
})
wxRsp, err := wxClient.V3TransactionH5(c, bm)
if err != nil {
return "", err
}
if wxRsp.Code != wechat.Success || wxRsp.Response.H5Url == "" {
logger.Error(c, "WxH5PayInfo 发生错误", fmt.Sprintf("错误状态码:%d, 错误信息:%s", wxRsp.Code, wxRsp.Error))
return "", errors.New(fmt.Sprintf("发起支付失败,失败状态码:%d, 失败原因:%s", wxRsp.Code, wxRsp.Error))
}
return wxRsp.Response.H5Url, nil
}
// WxPayCallBack 微信支付回调
func WxPayCallBack(notifyReq *wechat.V3NotifyReq, wxConfig WxPay) error {
// 初始化微信客户端
InitClient(wxConfig)
// 获取微信客户端
wxClient, err := GetClient()
if err != nil {
return err
}
// 获取微信平台证书
certMap := wxClient.WxPublicKeyMap()
// 验证异步通知的签名
err = notifyReq.VerifySignByPKMap(certMap)
if err != nil {
return err
}
var CallBackInfo struct {
AppId string `json:"app_id"` // 应用ID
Mchid string `json:"mchid"` // 商户号
OutTradeNo string `json:"out_trade_no"` // 商户系统内部订单号
TransactionId string `json:"transaction_id"` // 微信系统订单号
TradeType string `json:"trade_type"` // JSAPI公众号支付 NATIVE扫码支付 AppApp支付 MICROPAY付款码支付 MWEBH5支付 FACEPAY刷脸支付
TradeState string `json:"trade_state"` // SUCCESS支付成功 REFUND转入退款 NOTPAY未支付 CLOSED已关闭 REVOKED已撤销付款码支付 USERPAYING用户支付中付款码支付 PAYERROR支付失败(其他原因,如银行返回失败)
TradeStateDesc string `json:"trade_state_desc"` // 交易状态描述
SuccessTime string `json:"success_time"` // 支付完成时间
Amount struct {
Total int64 `json:"total"`
PayerTotal int64 `json:"payer_total"`
} `json:"amount"`
}
// 通用通知解密(推荐此方法)
err = notifyReq.DecryptCipherTextToStruct(wxConfig.ApiV3Key, &CallBackInfo)
if err != nil {
return err
}
// todo 返回触发下游回调的格式
return nil
}
// WxOrderQuery 查询微信订单
func WxOrderQuery(ctx context.Context, wxConfig WxPay, orderNo string) (PayOrderQueryInfo, error) {
// 初始化微信客户端
InitClient(wxConfig)
// 获取微信客户端
wxClient, err := GetClient()
if err != nil {
return PayOrderQueryInfo{}, err
}
wxRsp, err := wxClient.V3TransactionQueryOrder(ctx, payCommon.ORDER_NO_TYPE_ORDER_NO, orderNo)
if err != nil {
return PayOrderQueryInfo{}, err
}
if wxRsp.Code != wechat.Success {
return PayOrderQueryInfo{}, errors.New(fmt.Sprintf("查询订单接口错误,错误状态码:%d, 错误信息:%s", wxRsp.Code, wxRsp.Error))
}
// 映射交易状态
tradeState := ""
switch wxRsp.Response.TradeState {
case "SUCCESS":
tradeState = "SUCCESS"
case "REFUND":
tradeState = "REFUND"
case "NOTPAY":
tradeState = "NOTPAY"
case "CLOSED":
tradeState = "CLOSED"
}
amountTotal := wxRsp.Response.Amount.Total
payerTotal := wxRsp.Response.Amount.PayerTotal
outTradeNo, _ := strconv.Atoi(wxRsp.Response.OutTradeNo)
return PayOrderQueryInfo{
AppId: wxRsp.Response.Appid,
OutTradeNo: int64(outTradeNo),
TransactionId: wxRsp.Response.TransactionId,
TradeState: tradeState,
TradeStateDesc: wxRsp.Response.TradeStateDesc,
SuccessTime: wxRsp.Response.SuccessTime,
AmountTotal: int64(amountTotal),
PayerTotal: int64(payerTotal),
}, nil
}
// WxOrderRefund 微信退款申请
func WxOrderRefund(ctx context.Context, orderRefundRequest OrderRefundRequest) (OrderRefundInfo, error) {
// 初始化微信客户端
InitClient(orderRefundRequest.Wx)
// 获取微信客户端
wxClient, err := GetClient()
if err != nil {
return OrderRefundInfo{}, err
}
// 初始化 BodyMap
bm := make(gopay.BodyMap)
bm.Set("out_trade_no", orderRefundRequest.OrderId).
Set("sign_type", "MD5").
// 必填 退款订单号(程序员定义的)
Set("out_refund_no", orderRefundRequest.RefundOrderId).
// 选填 退款描述
Set("reason", orderRefundRequest.RefundReason).
SetBodyMap("amount", func(bm gopay.BodyMap) {
// 退款金额:单位是分
bm.Set("refund", orderRefundRequest.RefundAmount). //实际退款金额
Set("total", orderRefundRequest.RefundAmount). // 折扣前总金额(不是实际退款数)
Set("currency", "CNY")
})
// body参数Body
refund, err := wxClient.V3Refund(ctx, bm)
if err != nil {
return OrderRefundInfo{}, err
}
// 将非正常退款异常记录
if refund.Code != wechat.Success {
logger.Error(ctx, "WxOrderRefund 发生错误", fmt.Sprintf("申请退款接口失败,错误状态码:%d, 错误信息:%s", refund.Code, refund.Error))
// 这里时对非正常退款的一些处理message我们将code统一使用自定义的然后把message抛出去
return OrderRefundInfo{}, errors.New(fmt.Sprintf("申请退款接口失败,错误状态码:%d, 错误信息:%s", refund.Code, refund.Error))
}
outTradeNo, _ := strconv.Atoi(refund.Response.OutTradeNo)
outRefundNo, _ := strconv.Atoi(refund.Response.OutRefundNo)
refundStatus := payCommon.PAY_REFUND_STATU_COMMON
switch refund.Response.Status {
case "SUCCESS":
refundStatus = payCommon.PAY_REFUND_STATU_SUCCESS
case "CLOSED":
refundStatus = payCommon.PAY_REFUND_STATU_FAIL
case "PROCESSING":
refundStatus = payCommon.PAY_REFUND_STATU_ING
case "ABNORMAL":
refundStatus = payCommon.PAY_REFUND_STATU_FAIL
}
return OrderRefundInfo{
OutTradeNo: int64(outTradeNo),
TransactionId: refund.Response.TransactionId,
RefundFee: int64(refund.Response.Amount.PayerRefund),
RefundOrderId: int64(outRefundNo),
RefundStatus: refundStatus,
RefundSuccessTime: refund.Response.SuccessTime,
}, nil
}
// WxOrderRefundQuery 微信订单退款查询
func WxOrderRefundQuery(ctx context.Context, orderRefundQueryRequest OrderRefundQueryRequest) (OrderRefundInfo, error) {
// 初始化微信客户端
InitClient(orderRefundQueryRequest.Wx)
// 获取微信客户端
wxClient, err := GetClient()
if err != nil {
return OrderRefundInfo{}, err
}
wxRsp, err := wxClient.V3RefundQuery(ctx, strconv.FormatInt(orderRefundQueryRequest.RefundOrderId, 10), nil)
if err != nil {
return OrderRefundInfo{}, err
}
if wxRsp.Code != wechat.Success {
return OrderRefundInfo{}, errors.New(fmt.Sprintf("查询订单接口错误,错误状态码:%d, 错误信息:%s", wxRsp.Code, wxRsp.Error))
}
outTradeNo, _ := strconv.Atoi(wxRsp.Response.OutTradeNo)
outRefundNo, _ := strconv.Atoi(wxRsp.Response.OutRefundNo)
refundStatus := payCommon.PAY_REFUND_STATU_COMMON
switch wxRsp.Response.Status {
case "SUCCESS":
refundStatus = payCommon.PAY_REFUND_STATU_SUCCESS
case "CLOSED":
refundStatus = payCommon.PAY_REFUND_STATU_FAIL
case "PROCESSING":
refundStatus = payCommon.PAY_REFUND_STATU_ING
case "ABNORMAL":
refundStatus = payCommon.PAY_REFUND_STATU_FAIL
}
return OrderRefundInfo{
OutTradeNo: int64(outTradeNo),
TransactionId: wxRsp.Response.TransactionId,
RefundFee: int64(wxRsp.Response.Amount.PayerRefund),
RefundOrderId: int64(outRefundNo),
RefundStatus: refundStatus,
RefundSuccessTime: wxRsp.Response.SuccessTime,
}, nil
}
// WxCloseOrder 微信订单关闭
func WxCloseOrder(ctx context.Context, orderCloseRequest OrderCloseRequest) (OrderCloseInfo, error) {
// 初始化微信客户端
InitClient(orderCloseRequest.Wx)
// 获取微信客户端
wxClient, err := GetClient()
if err != nil {
return OrderCloseInfo{}, err
}
wxRsp, err := wxClient.V3TransactionCloseOrder(ctx, "FY160932049419637602")
if err != nil {
return OrderCloseInfo{}, err
}
if wxRsp.Code != wechat.Success {
logger.Error(ctx, "WxCloseOrder 发生错误", fmt.Sprintf("查询订单接口错误,错误状态码:%d, 错误信息:%s", wxRsp.Code, wxRsp.Error))
return OrderCloseInfo{}, errors.New(fmt.Sprintf("查询订单接口错误,错误状态码:%d, 错误信息:%s", wxRsp.Code, wxRsp.Error))
}
return OrderCloseInfo{
OutTradeNo: orderCloseRequest.OrderId,
}, nil
}

View File

@ -1,8 +1,6 @@
package mq
import (
common3 "PaymentCenter/app/constants/common"
mq "PaymentCenter/app/utils/mq/mqs"
"sync"
)
@ -25,8 +23,8 @@ func (this *CMqManager) InitMq() {
this.mqs = make(map[string]Imq)
//this.mqs[common.MQ_RABBIT] = RabbitMq{}
//this.mqs[common.MQ_NSQ] = NsqMq{}
this.mqs[common3.MQ_NATS] = mq.NatsMq{}
this.mqs[common3.MQ_KFK] = mq.KafkaMq{}
//this.mqs[common3.MQ_NATS] = mq.NatsMq{}
//this.mqs[common3.MQ_KFK] = mq.KafkaMq{}
}
func (this *CMqManager) GetMqByName(name string) Imq {
once.Do(func() {

View File

@ -410,3 +410,13 @@ func ParseToken(tokenString string) (*jwt.Token, *Claims, error) {
})
return token, Claims, err
}
// 判断切片是否包含指定字符串
func SliceInStr(s string, slice []string) bool {
for _, v := range slice {
if s == v {
return true
}
}
return false
}

View File

@ -4,11 +4,10 @@ import (
"PaymentCenter/app/jobs"
"PaymentCenter/app/jobs/basejob"
"PaymentCenter/config"
"github.com/qit-team/snow-core/log/accesslogger"
"github.com/qit-team/snow-core/db"
"github.com/qit-team/snow-core/kernel/close"
"github.com/qit-team/snow-core/kernel/container"
"github.com/qit-team/snow-core/log/accesslogger"
"github.com/qit-team/snow-core/log/logger"
"github.com/qit-team/snow-core/redis"
)
@ -26,6 +25,8 @@ func Bootstrap(conf *config.Config) (err error) {
//注册db服务
//第一个参数为注入别名,第二个参数为配置,第三个参数可选为是否懒加载
err = db.Pr.Register(db.SingletonMain, conf.Db)
//cacher := caches.NewLRUCacher2(caches.NewMemoryStore(), time.Hour, 1000)
//db.GetDb().SetDefaultCacher(cacher)
if err != nil {
return

View File

@ -6,4 +6,4 @@ arch=$2 #架构amd64
rootPath=$(cd `dirname $0`/../../; pwd)
#编译
GOOS=$os GOARCH=$arch go build -o build/bin/snow main.go
GOOS=$os GOARCH=$arch go build -o build/bin/snow main.go

View File

@ -36,6 +36,8 @@ type Config struct {
OpenApi OpenApi `toml:"OpenApi"`
Jwt Jwt `toml:"Jwt"`
AliOss AliOss `toml:"AliOss"`
AdminGate []string `toml:"AdminGate"`
CronConfig CronConfig `toml:"CronConfig"`
}
type AliOss struct {
@ -82,6 +84,12 @@ type Nacos struct {
Port int64
}
type CronConfig struct {
CloseOrderTime int `toml:"CloseOrderTime"`
QueryOrderTime int `toml:"QueryOrderTime"`
ConcurrentNumber int `toml:"ConcurrentNumber"`
}
func newConfig() *Config {
return new(Config)
}

View File

@ -7,7 +7,7 @@ import (
/**
*事件触发
event.EventManger.TrigerEvent(common.Event_USER_LOG_IN, param)
event.EventManger.TrigerEvent(payCommon.Event_USER_LOG_IN, param)
*/
var EventManger *EventManagerFactory

24
go.mod
View File

@ -6,9 +6,11 @@ require (
gitee.com/chengdu_blue_brothers/openapi-go-sdk v0.0.2
github.com/BurntSushi/toml v0.4.1
github.com/Shopify/sarama v1.19.0
github.com/ahmetb/go-linq/v3 v3.2.0
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
github.com/forgoer/openssl v1.6.0
github.com/gin-gonic/gin v1.7.7
github.com/go-pay/gopay v1.5.103
github.com/go-playground/locales v0.14.0
github.com/go-playground/universal-translator v0.18.0
github.com/go-sql-driver/mysql v1.6.0
@ -29,6 +31,8 @@ require (
google.golang.org/grpc v1.56.3
google.golang.org/protobuf v1.30.0
gopkg.in/go-playground/validator.v9 v9.31.0
xorm.io/builder v0.3.9
xorm.io/xorm v1.2.5
)
require (
@ -52,11 +56,17 @@ require (
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 // indirect
github.com/eapache/queue v1.1.0 // indirect
github.com/emirpasic/gods v1.12.0 // indirect
github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.6 // indirect
github.com/go-openapi/spec v0.20.4 // indirect
github.com/go-openapi/swag v0.19.15 // indirect
github.com/go-pay/crypto v0.0.1 // indirect
github.com/go-pay/errgroup v0.0.2 // indirect
github.com/go-pay/util v0.0.2 // indirect
github.com/go-pay/xlog v0.0.3 // indirect
github.com/go-pay/xtime v0.0.2 // indirect
github.com/go-playground/validator/v10 v10.9.0 // indirect
github.com/go-redis/redis/v8 v8.11.4 // indirect
github.com/goccy/go-json v0.8.1 // indirect
@ -98,19 +108,17 @@ require (
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.21.0 // indirect
golang.org/x/crypto v0.17.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/crypto v0.25.0 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/time v0.1.0 // indirect
golang.org/x/tools v0.6.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
gopkg.in/ini.v1 v1.66.2 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
stathat.com/c/consistent v1.0.0 // indirect
xorm.io/builder v0.3.9 // indirect
xorm.io/core v0.7.3 // indirect
xorm.io/xorm v1.2.5 // indirect
)

47
go.sum
View File

@ -61,6 +61,8 @@ github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/agiledragon/gomonkey/v2 v2.3.1 h1:k+UnUY0EMNYUFUAQVETGY9uUTxjMdnUkP0ARyJS1zzs=
github.com/agiledragon/gomonkey/v2 v2.3.1/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY=
github.com/ahmetb/go-linq/v3 v3.2.0 h1:BEuMfp+b59io8g5wYzNoFe9pWPalRklhlhbiU3hYZDE=
github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/kLZh/cj9U=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@ -173,6 +175,7 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6 h1:6VSn3hB5U5GeA6kQw4TwWIWbOhtvR2hmbBJnTOtqTWc=
github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6/go.mod h1:YxOVT5+yHzKvwhsiSIWmbAYM3Dr9AEEbER2dVayfBkg=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/gzip v0.0.3 h1:etUaeesHhEORpZMp18zoOhepboiWnFtXrBZxszWUn4k=
@ -208,6 +211,18 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh
github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-pay/crypto v0.0.1 h1:B6InT8CLfSLc6nGRVx9VMJRBBazFMjr293+jl0lLXUY=
github.com/go-pay/crypto v0.0.1/go.mod h1:41oEIvHMKbNcYlWUlRWtsnC6+ASgh7u29z0gJXe5bes=
github.com/go-pay/errgroup v0.0.2 h1:5mZMdm0TDClDm2S3G0/sm0f8AuQRtz0dOrTHDR9R8Cc=
github.com/go-pay/errgroup v0.0.2/go.mod h1:0+4b8mvFMS71MIzsaC+gVvB4x37I93lRb2dqrwuU8x8=
github.com/go-pay/gopay v1.5.103 h1:tjteCpcApf0CpiwKywMl6UnbLkMeYLJVEuVBtsQbyb8=
github.com/go-pay/gopay v1.5.103/go.mod h1:4+jKRvgmB8clKN1/E9M60miXKbnZ8wWGcg/Hsxn7k44=
github.com/go-pay/util v0.0.2 h1:goJ4f6kNY5zzdtg1Cj8oWC+Cw7bfg/qq2rJangMAb9U=
github.com/go-pay/util v0.0.2/go.mod h1:qM8VbyF1n7YAPZBSJONSPMPsPedhUTktewUAdf1AjPg=
github.com/go-pay/xlog v0.0.3 h1:avyMhCL/JgBHreoGx/am/kHxfs1udDOAeVqbmzP/Yes=
github.com/go-pay/xlog v0.0.3/go.mod h1:mH47xbobrdsSHWsmFtSF5agWbMHFP+tK0ZbVCk5OAEw=
github.com/go-pay/xtime v0.0.2 h1:7YR4/iuELsEHpJ6LUO0SVK80hQxDO9MLCfuVYIiTCRM=
github.com/go-pay/xtime v0.0.2/go.mod h1:W1yRbJaSt4CSBcdAtLBQ8xajiN/Pl5hquGczUcUE9xE=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
@ -297,8 +312,8 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@ -793,8 +808,8 @@ golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -831,8 +846,8 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -885,8 +900,8 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -911,8 +926,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -987,8 +1002,8 @@ golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211213223007-03aa0b5f6827/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -1000,8 +1015,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -1071,8 +1086,8 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=