84 lines
2.9 KiB
Go
84 lines
2.9 KiB
Go
package useragent
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
// SDKCommonHeader 由 H5/Flutter 将 SDK 完整公共参数转为 UTF-8 JSON,再用标准 Base64 编码后传入。
|
|
const SDKCommonHeader = "X-SDK-Common"
|
|
|
|
// 限制解析开销;正常 SDK 公共参数远小于此大小。
|
|
const maxSDKCommonHeaderSize = 16 * 1024
|
|
|
|
// SDKCommonFields 接收五项 SDK 公共字段及客户端 app_version,完整 fields 中的其他字段自动忽略。
|
|
// 业务 DevID、用户 ID、渠道等继续走原有业务参数,不能被 SDK 对象覆盖。
|
|
type SDKCommonFields struct {
|
|
SID string `json:"sid" bson:"sid"`
|
|
DeviceID string `json:"device_id" bson:"device_id"`
|
|
Device string `json:"device" bson:"device"`
|
|
DeviceFingerprint string `json:"device_fingerprint" bson:"device_fingerprint"`
|
|
FPVersion string `json:"fp_version" bson:"fp_version"`
|
|
AppVersion string `json:"app_version" bson:"app_version"` // 客户端版本随下单快照保存,支付回调不使用当前版本补值。
|
|
}
|
|
|
|
// ParseSDKCommonHeader 缺少请求头返回 nil;格式错误交给调用方记录,不阻断业务请求。
|
|
// 不修剪、截断或推算字段值,SDK 指纹生成失败时也保留其空值。
|
|
func ParseSDKCommonHeader(header string) (*SDKCommonFields, error) {
|
|
header = strings.TrimSpace(header)
|
|
if header == "" {
|
|
return nil, nil
|
|
}
|
|
if len(header) > maxSDKCommonHeaderSize {
|
|
return nil, fmt.Errorf("SDK 公共参数请求头超过 %d 字节", maxSDKCommonHeaderSize)
|
|
}
|
|
data, err := base64.StdEncoding.DecodeString(header)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("SDK 公共参数 Base64 解码失败: %w", err)
|
|
}
|
|
if !utf8.Valid(data) {
|
|
return nil, fmt.Errorf("SDK 公共参数不是有效的 UTF-8")
|
|
}
|
|
var fields *SDKCommonFields
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return nil, fmt.Errorf("SDK 公共参数 JSON 解析失败: %w", err)
|
|
}
|
|
if fields == nil {
|
|
return nil, fmt.Errorf("SDK 公共参数必须是 JSON 对象,不能为 null")
|
|
}
|
|
return fields, nil
|
|
}
|
|
|
|
// Clone 下单时保存独立快照,避免请求对象后续变化影响支付事件。
|
|
func (fields *SDKCommonFields) Clone() *SDKCommonFields {
|
|
if fields == nil {
|
|
return nil
|
|
}
|
|
snapshot := *fields
|
|
return &snapshot
|
|
}
|
|
|
|
// MissingFields 仅用于缺失观测;合法空值仍原样上报,不作为业务失败。
|
|
func (fields *SDKCommonFields) MissingFields() []string {
|
|
if fields == nil {
|
|
fields = &SDKCommonFields{}
|
|
}
|
|
var missing []string
|
|
for _, field := range []struct{ name, value string }{
|
|
{"sid", fields.SID},
|
|
{"device_id", fields.DeviceID},
|
|
{"device", fields.Device},
|
|
{"device_fingerprint", fields.DeviceFingerprint},
|
|
{"fp_version", fields.FPVersion},
|
|
{"app_version", fields.AppVersion},
|
|
} {
|
|
if strings.TrimSpace(field.value) == "" {
|
|
missing = append(missing, field.name)
|
|
}
|
|
}
|
|
return missing
|
|
}
|