Merge branch 'dev/dev1.0' into feature/rzy/api_1.0

# Conflicts:
#	app/constants/errorcode/error_code.go
This commit is contained in:
Rzy 2024-08-05 10:56:30 +08:00
commit 4d28483087
35 changed files with 1612 additions and 206 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"]

View File

@ -1,9 +1,86 @@
package console
import (
"PaymentCenter/app/constants/common"
"PaymentCenter/app/data"
"PaymentCenter/app/http/entities"
"PaymentCenter/app/models/ordersmodel"
"PaymentCenter/app/utils"
"github.com/qit-team/snow-core/command"
"strconv"
"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.Hour)})
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)
// 查询未支付的订单
repo := data.NewOrderRepo(ordersmodel.GetInstance().GetDb())
// 拼接条件
cond := builder.NewCond()
cond = cond.And(builder.Eq{"status": common.ORDER_STATUS_PAYING}, builder.Gt{"create_time": time.Now().Add(-time.Second)})
order := make([]ordersmodel.Orders, 0)
total, err := repo.OrderList(cond, entities.PageRequest{}, &order)
if err != nil {
utils.Log(nil, "主动查询订单支付状态,查询未付中订单失败", err)
return
} else if total > 0 {
// 发起查询上游支付
for _, v := range order {
go func(order ordersmodel.Orders) {
// 发起查询
utils.Log(nil, "主动查询订单支付状态,发起查询", order.Id)
// 解析上游结果
// 修改订单状态
}(v)
}
}
}
// 回调下游
func callbackOrder() {
var now = time.Now().Format(time.DateTime)
utils.Log(nil, "回调下游", now)
// 查询回调失败的订单
// 发起回调
}

View File

@ -11,5 +11,7 @@ import (
func RegisterSchedule(c *cron.Cron) {
//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

@ -3,7 +3,7 @@ package common
const (
TOKEN_PRE = "player_token_"
TOKEN_Admin = "Admin_token_"
ADMIN_V1 = "/admin/pay/api/v1"
ADMIN_V1 = "/pay/admin/api/v1"
FRONT_V1 = "/api/v1"
// 支付渠道枚举,1微信JSAPI2微信H53微信app4微信Native5微信小程序6支付宝网页&移动应用7支付宝小程序8支付宝JSAPI
@ -16,4 +16,16 @@ const (
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"
// '订单状态,待支付、支付中、支付成功、支付失败、订单关闭',
ORDER_STATUS_WAITPAY = 1
ORDER_STATUS_PAYING = 2
ORDER_STATUS_PAYED = 3
ORDER_STATUS_FAILED = 4
ORDER_STATUS_CLOSE = 5
)

View File

@ -29,6 +29,7 @@ const (
MerchantNotFound = 1100
// app
<<<<<<< HEAD
AppNotFound = 1200
AppDisabled = 1201
AppIpNotAllow = 1202
@ -44,6 +45,14 @@ const (
AppSM4DecryptFail = 1321
AppSM4EncryptKeyNotFound = 1322
AppSM4EncryptFail = 1323
=======
AppNotFound = 1200
AppDisabled = 1201
AppIpNotAllow = 1202
//渠道
PayChannelNotFound = 1300
>>>>>>> dev/dev1.0
)
var MsgEN = map[int]string{
@ -66,6 +75,7 @@ var MsgZH = map[int]string{
AppNotFound: "app_id未找到",
AppDisabled: "app通道关闭",
AppIpNotAllow: "ip不在白名单内",
<<<<<<< HEAD
AppRsaDecryptKeyNotFound: "密匙缺失无法进行Rsa解密",
AppRsaDecryptFail: "Rsa解密失败",
@ -81,6 +91,9 @@ var MsgZH = map[int]string{
AppSM4DecryptFail: "sm4解密失败",
AppSM4EncryptKeyNotFound: "密匙缺失无法进行sm4加密",
AppSM4EncryptFail: "sm4加密失败",
=======
PayChannelNotFound: "支付方式不存在",
>>>>>>> dev/dev1.0
}
var MsgMap map[string]map[int]string = map[string]map[int]string{"en": MsgZH}

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

@ -0,0 +1 @@
package data

View File

@ -25,15 +25,19 @@ func (m *PayChannelRepo) PayChannelList(conn builder.Cond, pageFilter entities.P
return repo.Desc("create_time").FindAndCount(payChannelList)
}
func (m *PayChannelRepo) PayChannelInsertOne(merchant *paychannelmodel.PayChannel) (int64, error) {
return m.repo.InsertOne(merchant)
func (m *PayChannelRepo) PayChannelInsertOne(payChannel *paychannelmodel.PayChannel) (int64, error) {
return m.repo.InsertOne(payChannel)
}
func (m *PayChannelRepo) PayChannelDelete(merchant *paychannelmodel.PayChannel, conn builder.Cond) (int64, error) {
return m.repo.Where(conn).Delete(merchant)
func (m *PayChannelRepo) PayChannelDelete(payChannel *paychannelmodel.PayChannel, conn builder.Cond) (int64, error) {
return m.repo.Where(conn).Delete(payChannel)
}
// columns 参数为要更新的字段,即使为空
func (m *PayChannelRepo) PayChannelUpdate(merchant *paychannelmodel.PayChannel, conn builder.Cond, columns ...string) (int64, error) {
return m.repo.Where(conn).MustCols(columns...).Update(merchant)
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

@ -13,6 +13,7 @@ import (
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))

View File

@ -1,6 +1,7 @@
package controllers
import (
"PaymentCenter/app/constants/common"
"PaymentCenter/app/utils"
"PaymentCenter/config"
"bytes"
@ -17,6 +18,7 @@ import (
"net/http"
"reflect"
"regexp"
"strconv"
"PaymentCenter/app/constants/errorcode"
@ -208,6 +210,27 @@ func phoneValidation(fl validator.FieldLevel) bool {
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 ""
}
func ErrWithCode(c *gin.Context, code int) {
Error(c, code, errorcode.GetMsg(code, c.GetHeader("local")))
}

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

@ -6,7 +6,8 @@ import (
)
type AppListRequest struct {
MerchantId int64 `json:"merchant_id"`
Id int64 `json:"id" form:"id"`
MerchantId int64 `json:"merchant_id" form:"merchant_id" validate:"required" label:"商户ID"`
entities.PageRequest
}
@ -15,8 +16,8 @@ type AppResponse struct {
MerchantId int64 `json:"merchant_id"`
AppName string `json:"app_name"`
AppRemark string `json:"app_remark"`
Status int `json:"status"`
KeyType int `json:"key_type"`
Status int32 `json:"status"`
KeyType int32 `json:"key_type"`
PublicKey string `json:"public_key"`
PrivateKey string `json:"private_key"`
MerchantPublicKey string `json:"merchant_public_key"`
@ -44,13 +45,13 @@ 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:"应用密钥类型"`
Status int32 `json:"status" validate:"oneof=0 1 2" label:"应用状态"`
KeyType int32 `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"`
NotifyUrl string `json:"notify_url" validate:"required" label:"通知地址"`
}
func (a *AppCreateRequest) RequestToDb() (db appmodel.App) {
@ -69,13 +70,12 @@ func (a *AppCreateRequest) RequestToDb() (db appmodel.App) {
type AppUpdateRequest struct {
Id int64 `json:"id" validate:"required" label:"应用ID"`
MerchantId int64 `json:"merchant_id" validate:"required" label:"商户ID"`
AppName string `json:"app_name" validate:"required" label:"应用名称"`
AppName string `json:"app_name" 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:"应用私钥"`
Status int32 `json:"status" label:"应用状态"`
KeyType int32 `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"`
@ -83,7 +83,6 @@ type AppUpdateRequest struct {
func (a *AppUpdateRequest) RequestToDb() (db appmodel.App) {
db.Id = a.Id
db.MerchantId = a.MerchantId
db.AppName = a.AppName
db.AppRemark = a.AppRemark
db.Status = a.Status

View File

@ -7,9 +7,10 @@ import (
type MerchantListRequest struct {
entities.PageRequest
Name string `form:"name"`
Contact string `form:"contact"`
Phone string `form:"phone"`
Name string `form:"name"`
Contact string `form:"contact"`
Phone string `form:"phone"`
GroupInfo string
}
type MerchantResponse struct {
@ -35,6 +36,7 @@ type MerchantCreateRequest struct {
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) {
@ -42,6 +44,7 @@ func (m *MerchantCreateRequest) RequestToDb() (db merchantmodel.Merchant) {
db.Contact = m.Contact
db.Phone = m.Phone
db.Remark = m.Remark
db.Creator = m.Creator
return db
}

View File

@ -49,16 +49,17 @@ type PayChannelCreateRequest struct {
}
type WechatPayChannel struct {
MchId string `json:"mch_id"` //直连商户号
MchCertificateSerialNumber string `json:"mch_certificate_serial_number"` //商户证书序列号
MchAPIv3Key string `json:"mch_APIv3_key"` //商户APIv3密钥
PrivateKeyPath string `json:"private_key_path"` //商户私钥文件路径
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 {
AliPublicKey string `json:"ali_public_key"` //支付宝公钥
PrivateKeyPath string `json:"private_key_path"` //应用私钥
SignType string `json:"sign_type"` //商户生成签名字符串所使用的签名算法类型目前支持RSA2和RSA推荐使用RSA2
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) {
@ -86,19 +87,24 @@ func (p *PayChannelCreateRequest) RequestToDb() (db paychannelmodel.PayChannel,
}
type PayChannelListRequest struct {
MerchantId int64 `json:"merchant_id"`
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"`
PayChannelCreateRequest
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.MerchantId = p.MerchantId
db.ChannelType = p.ChannelType
db.AppId = p.AppId
if p.ExpireTime != "" {

View File

@ -0,0 +1 @@
package front

View File

@ -9,10 +9,8 @@ import (
"PaymentCenter/app/http/requestmapping"
"PaymentCenter/app/services"
"PaymentCenter/app/utils"
"context"
"errors"
"PaymentCenter/config"
"github.com/gin-gonic/gin"
"github.com/qit-team/snow-core/redis"
"strings"
)
@ -62,20 +60,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
}
}
}
}

View File

@ -23,7 +23,7 @@ func RegisterAdminRoute(router *gin.Engine) {
}
}
v1 := router.Group("/admin/pay/api/v1", middlewares.ValidateRequest())
v1 := router.Group("/pay/admin/api/v1", middlewares.AdminAuth(), middlewares.ValidateRequest())
{
// 商户管理
merchant := v1.Group("/merchant")

View File

@ -4,13 +4,15 @@ package routes
* 配置路由
*/
import (
"PaymentCenter/app/constants/common"
"PaymentCenter/app/http/controllers"
"PaymentCenter/app/http/controllers/backend"
"PaymentCenter/app/http/controllers/front"
"PaymentCenter/app/http/middlewares"
"PaymentCenter/app/http/trace"
"PaymentCenter/app/utils/metric"
"PaymentCenter/config"
ginSwagger "github.com/swaggo/gin-swagger"
"github.com/swaggo/gin-swagger/swaggerFiles"
"github.com/gin-gonic/gin"
"github.com/qit-team/snow-core/http/middleware"
@ -44,11 +46,18 @@ func RegisterRoute(router *gin.Engine) {
router.NoRoute(controllers.Error404)
//router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
v1 := router.Group(common.FRONT_V1, middlewares.ValidateRequest())
{
pay := v1.Group("pay", middlewares.ValidatePayRequest())
pay.POST("web", backend.MerchantList) // 商户列表
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))
pay := v1.Group("pay", middlewares.ValidatePayRequest())
pay.POST("web", backend.MerchantList) // 商户列表
}

View File

@ -18,6 +18,7 @@ type Merchant struct {
Contact string `xorm:"'contact' varchar(128)"`
Phone string `xorm:"'phone' varchar(11)"`
Remark string `xorm:"'remark' varchar(1024)"`
Creator int `xorm:"'creator' int(10)"`
CreateTime time.Time `xorm:"'create_time' datetime created"`
UpdateTime time.Time `xorm:"'update_time' timestamp updated"`
DeleteTime time.Time `xorm:"'delete_time' timestamp deleted"`

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())
// }
//}

View File

@ -19,6 +19,9 @@ func AppList(req backend.AppListRequest) (result []appmodel.App, total int64, co
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)
@ -27,13 +30,13 @@ func AppList(req backend.AppListRequest) (result []appmodel.App, total int64, co
return appList, count, code
}
func AppCreate(App *appmodel.App) (code int) {
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})
conn = conn.And(builder.Eq{"id": app.MerchantId})
merchant := merchantmodel.Merchant{}
has, err := merchantRepo.MerchantDetail(&merchant, conn)
if err != nil {
@ -43,32 +46,26 @@ func AppCreate(App *appmodel.App) (code int) {
return errorcode.MerchantNotFound
}
_, err = repo.AppInsertOne(App)
_, err = repo.AppInsertOne(app)
code = handErr(err)
return
}
func AppUpdate(App *appmodel.App) (code int) {
func AppUpdate(app *appmodel.App) (code int) {
var err error
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)
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)
}
if !has {
return errorcode.MerchantNotFound
}
// 拼接查询条件
uconn := builder.NewCond()
uconn = uconn.And(builder.Eq{"Id": App.Id})
_, err = repo.AppUpdate(App, uconn, "app_remark")
code = handErr(err)
return
}

View File

@ -5,6 +5,7 @@ import (
"PaymentCenter/app/http/entities"
"PaymentCenter/app/http/entities/backend"
"PaymentCenter/app/models/merchantmodel"
"strings"
"xorm.io/builder"
)
@ -22,6 +23,9 @@ func MerchantList(req backend.MerchantListRequest) (result []merchantmodel.Merch
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)

View File

@ -17,6 +17,9 @@ func PayChannelList(req backend.PayChannelListRequest) (result []paychannelmodel
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)
@ -49,23 +52,11 @@ func PayChannelCreate(payChannel *paychannelmodel.PayChannel) (code int) {
func PayChannelUpdate(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
}
// 拼接查询条件
uconn := builder.NewCond()
uconn = uconn.And(builder.Eq{"Id": payChannel.Id})
_, err = repo.PayChannelUpdate(payChannel, uconn, "white_ip")
_, err := repo.PayChannelUpdate(payChannel, uconn, "white_ip")
code = handErr(err)
return
@ -83,3 +74,21 @@ func PayChannelDelete(req entities.IdRequest) (code int) {
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

@ -1,89 +1,89 @@
package mq
import (
"PaymentCenter/app/utils"
"PaymentCenter/config"
"context"
"encoding/json"
"fmt"
"github.com/Shopify/sarama"
"github.com/qit-team/snow-core/redis"
"strconv"
"sync"
)
type KafkaMq struct {
}
// 同步
func (n KafkaMq) Produce(name string, log interface{}, delayTime int, args ...interface{}) error {
kafConfig := sarama.NewConfig()
kafConfig.Producer.RequiredAcks = sarama.WaitForAll // 发送完数据需要leader和follow都确认
kafConfig.Producer.Partitioner = sarama.NewRandomPartitioner // 新选出一个partition
kafConfig.Producer.Return.Successes = true // 成功交付的消息将在success channel返回
// 构造一个消息
msg := &sarama.ProducerMessage{}
msg.Topic = name
var data, _ = json.Marshal(log)
msg.Value = sarama.StringEncoder(string(data))
// 连接kafka
client, err := sarama.NewSyncProducer(config.GetConf().KafkaUrl, kafConfig)
if err != nil {
fmt.Println("producer closed, err:", err)
return nil
}
defer client.Close()
// 发送消息
pid, offset, err := client.SendMessage(msg)
if err != nil {
utils.Log(nil, "send msg failed, err:", err, pid, offset)
return nil
}
return nil
}
func (n KafkaMq) Consume(name string, hand interface{}) {
consumer, err := sarama.NewConsumer(config.GetConf().KafkaUrl, nil)
if err != nil {
utils.Log(nil, "kafka comsume", err.Error())
return
}
partitionList, err := consumer.Partitions(name) // 根据topic取到所有的分区
if err != nil {
utils.Log(nil, "kafka comsume", err.Error())
return
}
//utils.Log(nil,"kafka comsume",name,partitionList)
for partition := range partitionList { // 遍历所有的分区
// 针对每个分区创建一个对应的分区消费者
var offsetReDis, _ = redis.GetRedis().Incr(context.Background(), "kafka_consume:"+strconv.Itoa(int(partition))).Result() //保证多消费者不重复消费
var offset int64 = sarama.OffsetNewest
if offsetReDis > 0 {
//offset = int64(offsetReDis)
}
pc, err := consumer.ConsumePartition(name, int32(partition), offset)
//utils.Log(nil,"partion",int32(partition))
if err != nil {
fmt.Printf("failed to start consumer for partition %d,err:%v\n", partition, err)
return
}
defer pc.AsyncClose()
var wg sync.WaitGroup
wg.Add(1)
// 异步从每个分区消费信息
go func(sarama.PartitionConsumer) {
for msg := range pc.Messages() {
defer wg.Done()
var handler = hand.(func(tag uint64, ch interface{}, msg []byte))
handler(0, nil, msg.Value)
//utils.Log(nil,"hand msg",string(msg.Value),msg.Offset)
}
}(pc)
wg.Wait()
}
}
func (n KafkaMq) DelyConsume(name string, hand interface{}) {
}
//
//import (
// "PaymentCenter/app/utils"
// "PaymentCenter/config"
// "context"
// "fmt"
// "github.com/Shopify/sarama"
// "github.com/qit-team/snow-core/redis"
//
// "strconv"
// "sync"
//)
//
//type KafkaMq struct {
//}
//
//// 同步
//func (n KafkaMq) Produce(name string, log interface{}, delayTime int, args ...interface{}) error {
// //kafConfig := sarama.NewConfig()
// //kafConfig.Producer.RequiredAcks = sarama.WaitForAll // 发送完数据需要leader和follow都确认
// //kafConfig.Producer.Partitioner = sarama.NewRandomPartitioner // 新选出一个partition
// //kafConfig.Producer.Return.Successes = true // 成功交付的消息将在success channel返回
// //
// //// 构造一个消息
// //msg := &sarama.ProducerMessage{}
// //msg.Topic = name
// //var data, _ = json.Marshal(log)
// //msg.Value = sarama.StringEncoder(string(data))
// //// 连接kafka
// //client, err := sarama.NewSyncProducer(config.GetConf().KafkaUrl, kafConfig)
// //if err != nil {
// // fmt.Println("producer closed, err:", err)
// // return nil
// //}
// //defer client.Close()
// //// 发送消息
// //pid, offset, err := client.SendMessage(msg)
// //if err != nil {
// // utils.Log(nil, "send msg failed, err:", err, pid, offset)
// // return nil
// //}
// return nil
//}
//
//func (n KafkaMq) Consume(name string, hand interface{}) {
// consumer, err := sarama.NewConsumer(config.GetConf().KafkaUrl, nil)
// if err != nil {
// utils.Log(nil, "kafka comsume", err.Error())
// return
// }
// partitionList, err := consumer.Partitions(name) // 根据topic取到所有的分区
// if err != nil {
// utils.Log(nil, "kafka comsume", err.Error())
// return
// }
// //utils.Log(nil,"kafka comsume",name,partitionList)
// for partition := range partitionList { // 遍历所有的分区
// // 针对每个分区创建一个对应的分区消费者
// var offsetReDis, _ = redis.GetRedis().Incr(context.Background(), "kafka_consume:"+strconv.Itoa(int(partition))).Result() //保证多消费者不重复消费
// var offset int64 = sarama.OffsetNewest
// if offsetReDis > 0 {
// //offset = int64(offsetReDis)
// }
// pc, err := consumer.ConsumePartition(name, int32(partition), offset)
// //utils.Log(nil,"partion",int32(partition))
// if err != nil {
// fmt.Printf("failed to start consumer for partition %d,err:%v\n", partition, err)
// return
// }
// defer pc.AsyncClose()
// var wg sync.WaitGroup
// wg.Add(1)
// // 异步从每个分区消费信息
// go func(sarama.PartitionConsumer) {
// for msg := range pc.Messages() {
// defer wg.Done()
// var handler = hand.(func(tag uint64, ch interface{}, msg []byte))
// handler(0, nil, msg.Value)
// //utils.Log(nil,"hand msg",string(msg.Value),msg.Offset)
// }
// }(pc)
// wg.Wait()
// }
//}
//func (n KafkaMq) DelyConsume(name string, hand interface{}) {
//
//}

View File

@ -440,3 +440,13 @@ func ContainsString(slice []string, s string) bool {
}
return false
}
// 判断切片是否包含指定字符串
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

@ -37,6 +37,7 @@ type Config struct {
OpenApi OpenApi `toml:"OpenApi"`
Jwt Jwt `toml:"Jwt"`
AliOss AliOss `toml:"AliOss"`
AdminGate []string `toml:"AdminGate"`
}
type AliOss struct {

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

18
go.mod
View File

@ -12,6 +12,7 @@ require (
github.com/bytedance/sonic v1.10.2
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
@ -66,6 +67,11 @@ require (
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
@ -112,13 +118,13 @@ require (
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.21.0 // indirect
golang.org/x/arch v0.6.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

44
go.sum
View File

@ -224,6 +224,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=
@ -313,8 +325,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=
@ -824,8 +836,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=
@ -862,8 +874,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=
@ -916,8 +928,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=
@ -942,8 +954,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=
@ -1019,8 +1031,8 @@ golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBc
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.5.0/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=
@ -1032,8 +1044,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=
@ -1103,8 +1115,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=