增加虫虫助手支付

This commit is contained in:
ayflying
2025-07-31 19:11:19 +08:00
parent 61d69159ea
commit d54de73e11
6 changed files with 153 additions and 10 deletions

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

View 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:"签名,用于验证请求合法性"`
}

View File

@@ -1,30 +1,105 @@
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) {
var buffer strings.Builder
// 写入PEM格式头部
buffer.WriteString("-----BEGIN PUBLIC KEY-----\n")
// 定义每行公钥的标准长度PEM格式要求64字符/行)
rawLen := 64
keyLen := len(publicKey)
// 计算需要拆分的总行数(向上取整)
raws := keyLen / rawLen
temp := keyLen % rawLen
if temp > 0 {
raws++
raws++ // 若有余数则增加一行
}
// 按行拆分并写入公钥内容
start := 0
end := start + rawLen
for i := 0; i < raws; i++ {
if i == raws-1 {
// 最后一行取剩余所有字符处理不足64字符的情况
buffer.WriteString(publicKey[start:])
} else {
// 非最后行取固定64字符
buffer.WriteString(publicKey[start:end])
}
buffer.WriteByte('\n')
buffer.WriteByte('\n') // 每行结束添加换行符
start += rawLen
end = start + rawLen
}
// 写入PEM格式尾部
buffer.WriteString("-----END PUBLIC KEY-----\n")
pKey = buffer.String()
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
}

View File

@@ -1,6 +1,7 @@
package honor
import (
"context"
"crypto"
"crypto/rsa"
"crypto/sha256"
@@ -28,7 +29,11 @@ func New(pay *Pay) *Pay {
// sign: 签名的Base64编码字符串
// 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格式的签名
signBytes, err := base64.StdEncoding.DecodeString(sign)
if err != nil {