|
|
@@ -1,6 +1,9 @@
|
|
|
package model
|
|
|
|
|
|
import (
|
|
|
+ "crypto/md5"
|
|
|
+ "encoding/hex"
|
|
|
+ "encoding/json"
|
|
|
"errors"
|
|
|
"fmt"
|
|
|
"math"
|
|
|
@@ -11,6 +14,8 @@ import (
|
|
|
"rocommon/util"
|
|
|
"roserver/baseserver/model"
|
|
|
"roserver/serverproto"
|
|
|
+ "sort"
|
|
|
+ "strconv"
|
|
|
"strings"
|
|
|
"time"
|
|
|
"unicode/utf8"
|
|
|
@@ -3695,13 +3700,40 @@ func (this *Role) PayInfoGet(goodsType, goodsId, count int32, rushStage, rushRou
|
|
|
util.InfoF("uid=%v pc mode: shop:%v id:%v, count:%v, price:%v", this.GetUUid(), payOrderInfo.GoodsType, payOrderInfo.GoodsId, payOrderInfo.Count, payOrderInfo.Amount)
|
|
|
return
|
|
|
}
|
|
|
- ordeinfo := OrderRequest{}
|
|
|
+ ordeinfo := OrderRequest{
|
|
|
+ GameArea: "s1",
|
|
|
+ GameLevel: "50",
|
|
|
+ GameOrderId: "ORDER123456",
|
|
|
+ GameCurrency: "CNY",
|
|
|
+ GamePrice: "99.99",
|
|
|
+ GameRoleId: "role_001",
|
|
|
+ GameRoleName: "测试角色",
|
|
|
+ GameGuid: 12345,
|
|
|
+ NotifyId: "-1",
|
|
|
+ Subject: "至尊礼包",
|
|
|
+ GameAccessVersion: "2507",
|
|
|
+ }
|
|
|
+ extra := &Extras{
|
|
|
+ ServerId: int(payOrderInfo.SId / 2),
|
|
|
+ Platform: this.platform,
|
|
|
+ CpOrderId: strconv.FormatUint(payOrderInfo.CpOrderId, 10),
|
|
|
+ }
|
|
|
+ v, _ := json.Marshal(extra)
|
|
|
+ ordeinfo.ExtendsInfoData = string(v)
|
|
|
+
|
|
|
payOrderInfo.SdkOrderId = sdkOrderId
|
|
|
//保存订单信息到数据库
|
|
|
ackMsg.Amount = payOrderInfo.Amount
|
|
|
ackMsg.CpOrderId = payOrderInfo.CpOrderId
|
|
|
- ackMsg.GoodsName = payOrderInfo.GoodsName
|
|
|
- //ackMsg.GoodsName = service.GetServiceConfig().Node.PayCallBack
|
|
|
+ //ackMsg.GoodsName = payOrderInfo.GoodsName
|
|
|
+ info, err := GenerateGameSign(ordeinfo, "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCfYd3FqSaWqCpWLSktBSSgAelt0F6T+tO4C25YKR/6X/sPacDBbX662/0fW+H+YbXigHWFB/yangkhiZTpD/VmiOo5lISX6L0/m+13ti9b8jzTZcfVngfLsP+Ztbk81N1Jk0gWF4bndZxREJ3IxcEDHnIrwXgLGA2GJ89kdgudwIDAQAB")
|
|
|
+ if err != nil {
|
|
|
+ ackMsg.Error = int32(serverproto.ErrorCode_ERROR_PAY_NOT_OPEN)
|
|
|
+ this.ReplayGate(ackMsg, true)
|
|
|
+ util.ErrorF("uid=%v GenerateGameSign err:%v", this.GetUUid(), err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ ackMsg.GoodsName = info
|
|
|
ssSaveReqMsg := &serverproto.SSPayInfoSaveReq{
|
|
|
PayOrderInfo: payOrderInfo,
|
|
|
SaveNotify: true,
|
|
|
@@ -3712,6 +3744,90 @@ func (this *Role) PayInfoGet(goodsType, goodsId, count int32, rushStage, rushRou
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+func GenerateGameSign(params interface{}, rsaPublicKey string) (string, error) {
|
|
|
+ // 1. 将结构体转换为 map
|
|
|
+ paramsMap, err := structToMap(params)
|
|
|
+ if err != nil {
|
|
|
+ return "", err
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 过滤掉 game_sign 字段
|
|
|
+ delete(paramsMap, "game_sign")
|
|
|
+
|
|
|
+ // 3. 获取所有 key 并排序(字典序)
|
|
|
+ keys := make([]string, 0, len(paramsMap))
|
|
|
+ for k := range paramsMap {
|
|
|
+ keys = append(keys, k)
|
|
|
+ }
|
|
|
+ sort.Strings(keys)
|
|
|
+
|
|
|
+ // 4. 按顺序构建 QueryString
|
|
|
+ var queryParts []string
|
|
|
+ for _, k := range keys {
|
|
|
+ v := paramsMap[k]
|
|
|
+ // 跳过空值或零值(根据实际需求调整)
|
|
|
+ if v == "" || v == nil {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ // 将值转换为字符串
|
|
|
+ strValue := fmt.Sprintf("%v", v)
|
|
|
+ queryParts = append(queryParts, fmt.Sprintf("%s=%s", k, strValue))
|
|
|
+ }
|
|
|
+ queryString := strings.Join(queryParts, "&")
|
|
|
+
|
|
|
+ // 5. 在 QueryString 后面拼接 RSA 公钥
|
|
|
+ stringToSign := queryString + rsaPublicKey
|
|
|
+
|
|
|
+ // 6. 计算 MD5
|
|
|
+ hash := md5.Sum([]byte(stringToSign))
|
|
|
+ sign := hex.EncodeToString(hash[:])
|
|
|
+
|
|
|
+ return sign, nil
|
|
|
+}
|
|
|
+
|
|
|
+// structToMap 将结构体转换为 map[string]interface{}
|
|
|
+// 使用 json tag 作为 key
|
|
|
+func structToMap(obj interface{}) (map[string]interface{}, error) {
|
|
|
+ result := make(map[string]interface{})
|
|
|
+
|
|
|
+ v := reflect.ValueOf(obj)
|
|
|
+ t := reflect.TypeOf(obj)
|
|
|
+
|
|
|
+ // 如果是指针,获取其指向的值
|
|
|
+ if v.Kind() == reflect.Ptr {
|
|
|
+ v = v.Elem()
|
|
|
+ t = t.Elem()
|
|
|
+ }
|
|
|
+
|
|
|
+ // 确保是结构体类型
|
|
|
+ if v.Kind() != reflect.Struct {
|
|
|
+ return nil, fmt.Errorf("expected struct, got %v", v.Kind())
|
|
|
+ }
|
|
|
+
|
|
|
+ for i := 0; i < v.NumField(); i++ {
|
|
|
+ field := t.Field(i)
|
|
|
+ value := v.Field(i)
|
|
|
+
|
|
|
+ // 获取 json tag
|
|
|
+ jsonTag := field.Tag.Get("json")
|
|
|
+ if jsonTag == "" || jsonTag == "-" {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理 omitempty(去除后面的,omitempty)
|
|
|
+ jsonTag = strings.Split(jsonTag, ",")[0]
|
|
|
+
|
|
|
+ // 获取字段值
|
|
|
+ if value.Kind() == reflect.Ptr && value.IsNil() {
|
|
|
+ result[jsonTag] = nil
|
|
|
+ } else {
|
|
|
+ result[jsonTag] = value.Interface()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return result, nil
|
|
|
+}
|
|
|
+
|
|
|
type OrderRequest struct {
|
|
|
// 訂單擴展數據,支持JSON或QueryString格式
|
|
|
ExtendsInfoData string `json:"extends_info_data"` // 最大128字符
|