Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d54de73e11 | ||
|
|
61d69159ea |
@@ -13,7 +13,8 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Boot() (err error) {
|
func Boot() (err error) {
|
||||||
err = service.SystemCron().StartCron()
|
// 启动计划任务定时器,预防debug工具激活计划任务造成重复执行,此处不执行计划任务
|
||||||
|
//err = service.SystemCron().StartCron()
|
||||||
|
|
||||||
//用户活动持久化
|
//用户活动持久化
|
||||||
service.SystemCron().AddCronV2(v1.CronType_DAILY, func(ctx context.Context) error {
|
service.SystemCron().AddCronV2(v1.CronType_DAILY, func(ctx context.Context) error {
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ var (
|
|||||||
wait = false
|
wait = false
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const IpDbPath = "runtime/library/ip2region.xdb"
|
||||||
|
|
||||||
type sIp2region struct {
|
type sIp2region struct {
|
||||||
searcher *xdb.Searcher
|
searcher *xdb.Searcher
|
||||||
}
|
}
|
||||||
@@ -35,16 +37,17 @@ func init() {
|
|||||||
// Load 加载到内存中
|
// Load 加载到内存中
|
||||||
//
|
//
|
||||||
// @Description: 加载ip2region数据库到内存中。
|
// @Description: 加载ip2region数据库到内存中。
|
||||||
|
|
||||||
// @receiver s *sIp2region: sIp2region的实例。
|
// @receiver s *sIp2region: sIp2region的实例。
|
||||||
func (s *sIp2region) Load() {
|
func (s *sIp2region) Load() {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
var dbPath = "runtime/library/ip2region.xdb"
|
//var dbPath = "runtime/library/ip2region.xdb"
|
||||||
var url = "https://github.com/ayflying/resource/raw/refs/heads/main/attachment/ip2region.xdb"
|
var url = "https://github.com/ayflying/resource/raw/refs/heads/master/attachment/ip2region.xdb"
|
||||||
if wait {
|
if wait {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if gfile.IsEmpty(dbPath) {
|
if gfile.IsEmpty(IpDbPath) {
|
||||||
wait = true
|
wait = true
|
||||||
defer func() {
|
defer func() {
|
||||||
wait = false
|
wait = false
|
||||||
@@ -55,9 +58,9 @@ func (s *sIp2region) Load() {
|
|||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = gfile.PutBytes(dbPath, putData.ReadAll())
|
err = gfile.PutBytes(IpDbPath, putData.ReadAll())
|
||||||
}
|
}
|
||||||
cBuff := gfile.GetBytes(dbPath)
|
cBuff := gfile.GetBytes(IpDbPath)
|
||||||
/*
|
/*
|
||||||
var cBuff []byte
|
var cBuff []byte
|
||||||
if gres.Contains(dbPath) {
|
if gres.Contains(dbPath) {
|
||||||
@@ -78,7 +81,9 @@ func (s *sIp2region) Load() {
|
|||||||
|
|
||||||
func (s *sIp2region) GetIp(ip string) (res []string) {
|
func (s *sIp2region) GetIp(ip string) (res []string) {
|
||||||
//初始化加载
|
//初始化加载
|
||||||
|
if s.searcher != nil {
|
||||||
s.Load()
|
s.Load()
|
||||||
|
}
|
||||||
|
|
||||||
res = make([]string, 5)
|
res = make([]string, 5)
|
||||||
if s.searcher == nil {
|
if s.searcher == nil {
|
||||||
|
|||||||
32
package/pay/chongchong/chongchong.go
Normal file
32
package/pay/chongchong/chongchong.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package chongchong
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/ayflying/utility_go/package/pay/common"
|
||||||
|
"github.com/gogf/gf/v2/crypto/gmd5"
|
||||||
|
)
|
||||||
|
|
||||||
|
//验单
|
||||||
|
func (p *Pay) Verify(req *CallbackData, sign string) (isOk bool, err error) {
|
||||||
|
//req := g.RequestFromCtx(ctx).Request
|
||||||
|
//data, err := common.ParseNotifyToBodyMap(req)
|
||||||
|
|
||||||
|
var data = map[string]interface{}{
|
||||||
|
"orderPrice": req.OrderPrice,
|
||||||
|
"packageId": req.PackageId,
|
||||||
|
"partnerTransactionNo": req.PartnerTransactionNo,
|
||||||
|
"productId": req.ProductId,
|
||||||
|
"statusCode": req.StatusCode,
|
||||||
|
"transactionNo": req.TransactionNo,
|
||||||
|
}
|
||||||
|
|
||||||
|
dataStr, err := common.BuildSignStr(data)
|
||||||
|
|
||||||
|
var SingStr = fmt.Sprintf("%v&%v", dataStr, p.ApiKey)
|
||||||
|
sign2, err := gmd5.EncryptString(SingStr)
|
||||||
|
|
||||||
|
if sign == sign2 {
|
||||||
|
isOk = true
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
25
package/pay/chongchong/model.go
Normal file
25
package/pay/chongchong/model.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package chongchong
|
||||||
|
|
||||||
|
type Pay struct {
|
||||||
|
ApiKey string `json:"api_key"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(pay *Pay) *Pay {
|
||||||
|
return &Pay{
|
||||||
|
ApiKey: pay.ApiKey,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CallbackData 用于处理回调数据的结构体
|
||||||
|
type CallbackData struct {
|
||||||
|
TransactionNo string `json:"transactionNo" dc:"平台交易单号,唯一标识一笔交易"`
|
||||||
|
PartnerTransactionNo string `json:"partnerTransactionNo" dc:"合作方交易单号,由合作方生成"`
|
||||||
|
StatusCode string `json:"statusCode" dc:"交易状态码,SUCCESS表示成功,FAIL表示失败"`
|
||||||
|
ProductId int `json:"productId" dc:"产品ID,对应后台配置的商品"`
|
||||||
|
OrderPrice float64 `json:"orderPrice" dc:"订单金额,单位为元"`
|
||||||
|
PackageId int `json:"packageId" dc:"套餐ID,可选字段,部分商品有套餐区分"`
|
||||||
|
ProductName string `json:"productName" dc:"产品名称,展示用"`
|
||||||
|
ExtParam string `json:"extParam" dc:"扩展参数,回调时原样返回"`
|
||||||
|
UserId int `json:"userId" dc:"用户ID,标识购买者"`
|
||||||
|
Sign string `json:"sign" dc:"签名,用于验证请求合法性"`
|
||||||
|
}
|
||||||
@@ -1,30 +1,105 @@
|
|||||||
package common
|
package common
|
||||||
|
|
||||||
import "strings"
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/util/gconv"
|
||||||
|
"net/http"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FormatPublicKey 将原始公钥字符串格式化为标准PEM格式的公钥
|
||||||
|
// 功能:为原始公钥添加PEM头部和尾部,并按64字符长度拆分换行,符合PKCS#8标准格式要求
|
||||||
|
// 参数 publicKey: 原始未格式化的公钥字符串(通常为Base64编码且无换行)
|
||||||
|
// 返回值: 格式化后的PEM格式公钥字符串
|
||||||
func FormatPublicKey(publicKey string) (pKey string) {
|
func FormatPublicKey(publicKey string) (pKey string) {
|
||||||
var buffer strings.Builder
|
var buffer strings.Builder
|
||||||
|
// 写入PEM格式头部
|
||||||
buffer.WriteString("-----BEGIN PUBLIC KEY-----\n")
|
buffer.WriteString("-----BEGIN PUBLIC KEY-----\n")
|
||||||
|
|
||||||
|
// 定义每行公钥的标准长度(PEM格式要求64字符/行)
|
||||||
rawLen := 64
|
rawLen := 64
|
||||||
keyLen := len(publicKey)
|
keyLen := len(publicKey)
|
||||||
|
// 计算需要拆分的总行数(向上取整)
|
||||||
raws := keyLen / rawLen
|
raws := keyLen / rawLen
|
||||||
temp := keyLen % rawLen
|
temp := keyLen % rawLen
|
||||||
if temp > 0 {
|
if temp > 0 {
|
||||||
raws++
|
raws++ // 若有余数则增加一行
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 按行拆分并写入公钥内容
|
||||||
start := 0
|
start := 0
|
||||||
end := start + rawLen
|
end := start + rawLen
|
||||||
for i := 0; i < raws; i++ {
|
for i := 0; i < raws; i++ {
|
||||||
if i == raws-1 {
|
if i == raws-1 {
|
||||||
|
// 最后一行取剩余所有字符(处理不足64字符的情况)
|
||||||
buffer.WriteString(publicKey[start:])
|
buffer.WriteString(publicKey[start:])
|
||||||
} else {
|
} else {
|
||||||
|
// 非最后行取固定64字符
|
||||||
buffer.WriteString(publicKey[start:end])
|
buffer.WriteString(publicKey[start:end])
|
||||||
}
|
}
|
||||||
buffer.WriteByte('\n')
|
buffer.WriteByte('\n') // 每行结束添加换行符
|
||||||
start += rawLen
|
start += rawLen
|
||||||
end = start + rawLen
|
end = start + rawLen
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 写入PEM格式尾部
|
||||||
buffer.WriteString("-----END PUBLIC KEY-----\n")
|
buffer.WriteString("-----END PUBLIC KEY-----\n")
|
||||||
pKey = buffer.String()
|
pKey = buffer.String()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseNotifyToBodyMap 将HTTP请求中的表单数据解析为键值对映射
|
||||||
|
// 功能:解析请求表单数据,提取单值字段并转换为map[string]interface{}格式
|
||||||
|
// 参数 req: 包含表单数据的HTTP请求对象
|
||||||
|
// 返回值: 解析后的键值对映射(bm)和可能的错误(err)
|
||||||
|
func ParseNotifyToBodyMap(req *http.Request) (bm map[string]interface{}, err error) {
|
||||||
|
// 解析请求表单数据,若失败则返回错误
|
||||||
|
if err = req.ParseForm(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// 获取解析后的表单数据(key为字段名,value为字符串切片形式的字段值)
|
||||||
|
var form map[string][]string = req.Form
|
||||||
|
// 初始化结果映射,预分配容量(表单字段数+1,预留扩展空间)
|
||||||
|
bm = make(map[string]interface{}, len(form)+1)
|
||||||
|
// 遍历表单字段,仅保留单值字段(忽略多值字段)
|
||||||
|
for k, v := range form {
|
||||||
|
if len(v) == 1 {
|
||||||
|
bm[k] = v[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildSignStr 根据传入的g.Map构建签名字符串
|
||||||
|
// 规则:对所有非空值的键进行字母排序后,按"key=value&"格式拼接,最后去除末尾的"&"
|
||||||
|
// 参数 bm: 包含键值对的g.Map
|
||||||
|
// 返回值: 构建好的签名字符串和可能的错误
|
||||||
|
func BuildSignStr(bm g.Map) (string, error) {
|
||||||
|
var (
|
||||||
|
buf strings.Builder
|
||||||
|
keyList []string
|
||||||
|
)
|
||||||
|
// 收集所有键名
|
||||||
|
for k := range bm {
|
||||||
|
keyList = append(keyList, k)
|
||||||
|
}
|
||||||
|
// 对键名进行字母排序
|
||||||
|
sort.Strings(keyList)
|
||||||
|
// 遍历排序后的键,拼接非空值的键值对
|
||||||
|
for _, k := range keyList {
|
||||||
|
if v := bm[k]; v != "" {
|
||||||
|
buf.WriteString(k)
|
||||||
|
buf.WriteByte('=')
|
||||||
|
buf.WriteString(gconv.String(v))
|
||||||
|
buf.WriteByte('&')
|
||||||
|
// 去除末尾多余的'&'字符
|
||||||
|
// 检查是否有有效的键值对被拼接
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if buf.Len() <= 0 {
|
||||||
|
return "", errors.New("length is error")
|
||||||
|
}
|
||||||
|
return buf.String()[:buf.Len()-1], nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package honor
|
package honor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
@@ -28,7 +29,11 @@ func New(pay *Pay) *Pay {
|
|||||||
// sign: 签名的Base64编码字符串
|
// sign: 签名的Base64编码字符串
|
||||||
// pubKey: PEM格式的公钥字符串
|
// pubKey: PEM格式的公钥字符串
|
||||||
// 返回验证结果和可能的错误
|
// 返回验证结果和可能的错误
|
||||||
func (p *Pay) VerifyRSASignature(data []byte, sign string) (bool, error) {
|
func (p *Pay) VerifyRSASignature(ctx context.Context, data []byte, sign string) (bool, error) {
|
||||||
|
//req := g.RequestFromCtx(ctx).Request
|
||||||
|
//post, err := common.ParseNotifyToBodyMap(req)
|
||||||
|
//var data = gjson.MustEncode(post)
|
||||||
|
|
||||||
// 解码Base64格式的签名
|
// 解码Base64格式的签名
|
||||||
signBytes, err := base64.StdEncoding.DecodeString(sign)
|
signBytes, err := base64.StdEncoding.DecodeString(sign)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user