495 lines
19 KiB
Go
495 lines
19 KiB
Go
package game
|
||
|
||
import (
|
||
"context"
|
||
"crypto/sha256"
|
||
"encoding/hex"
|
||
"encoding/json"
|
||
"fmt"
|
||
"sort"
|
||
"strconv"
|
||
"strings"
|
||
"time"
|
||
|
||
gamev1 "hyapp.local/api/proto/game/v1"
|
||
"hyapp/pkg/xerr"
|
||
gamedomain "hyapp/services/game-service/internal/domain/game"
|
||
)
|
||
|
||
const (
|
||
// VIVAGAMES 文档定义 code=0 成功;游戏侧按 JSON code 判断业务结果,HTTP 200 只表示回调已被接收。
|
||
vivaGamesCodeOK = 0
|
||
vivaGamesCodeAppNotFound = 1001
|
||
vivaGamesCodeInvalidArgument = 1002
|
||
vivaGamesCodeSignatureInvalid = 1003
|
||
vivaGamesCodeCodeInvalid = 1004
|
||
vivaGamesCodeTokenInvalid = 1005
|
||
vivaGamesCodeInternalError = 1006
|
||
vivaGamesCodeGameMaintenance = 1007
|
||
vivaGamesCodeInsufficientBalance = 1008
|
||
vivaGamesCodeUserDisabled = 1009
|
||
vivaGamesCodeRequestExpired = 1010
|
||
|
||
vivaGamesDefaultTokenTTL = 24 * time.Hour
|
||
vivaGamesSignatureValidWindow = 5 * time.Minute
|
||
)
|
||
|
||
type vivaGamesAdapterConfig struct {
|
||
// app_id 来自 VIVAGAMES 后台;callbackSecret 存放 PDF 签名算法使用的 AppKey。
|
||
AppID int64 `json:"app_id"`
|
||
AppIDAlias int64 `json:"appId"`
|
||
AppSecret string `json:"app_secret"`
|
||
UIDMode string `json:"uid_mode"`
|
||
DefaultLang string `json:"default_lang"`
|
||
// token_ttl_seconds 控制启动 session 至少保留多久,避免 H5 游戏中途回调时 token 已过期。
|
||
TokenTTLSeconds int64 `json:"token_ttl_seconds"`
|
||
|
||
// 以下字段直接对应 NativeBridge.getConfig,后台热改后下一次启动生效。
|
||
Metadata string `json:"metadata"`
|
||
CoinType int64 `json:"coin_type"`
|
||
AmountRate int64 `json:"amount_rate"`
|
||
BGMSwitch *int64 `json:"bgm_switch"`
|
||
GSP int64 `json:"gsp"`
|
||
CurrencyIcon string `json:"currency_icon"`
|
||
|
||
// VIVAGAMES 文档没有游戏列表 API,game_urls 用后台配置保存厂商提供的逐游戏 H5 URL。
|
||
GameURLs map[string]string `json:"game_urls"`
|
||
LaunchURLTemplate string `json:"launch_url_template"`
|
||
}
|
||
|
||
func vivaGamesConfigFromPlatform(value any) vivaGamesAdapterConfig {
|
||
var raw string
|
||
switch typed := value.(type) {
|
||
case gamedomain.Platform:
|
||
raw = typed.AdapterConfigJSON
|
||
case gamedomain.LaunchableGame:
|
||
raw = typed.AdapterConfigJSON
|
||
}
|
||
var config vivaGamesAdapterConfig
|
||
_ = json.Unmarshal([]byte(strings.TrimSpace(raw)), &config)
|
||
if config.AppID == 0 {
|
||
config.AppID = config.AppIDAlias
|
||
}
|
||
config.AppSecret = strings.TrimSpace(config.AppSecret)
|
||
config.UIDMode = strings.ToLower(strings.TrimSpace(config.UIDMode))
|
||
config.DefaultLang = strings.TrimSpace(config.DefaultLang)
|
||
config.Metadata = strings.TrimSpace(config.Metadata)
|
||
config.CurrencyIcon = strings.TrimSpace(config.CurrencyIcon)
|
||
config.LaunchURLTemplate = strings.TrimSpace(config.LaunchURLTemplate)
|
||
config.GameURLs = normalizeStringMap(config.GameURLs)
|
||
return config
|
||
}
|
||
|
||
func (c vivaGamesAdapterConfig) TokenTTL() time.Duration {
|
||
if c.TokenTTLSeconds > 0 {
|
||
return time.Duration(c.TokenTTLSeconds) * time.Second
|
||
}
|
||
return vivaGamesDefaultTokenTTL
|
||
}
|
||
|
||
func (c vivaGamesAdapterConfig) LanguageValue() string {
|
||
if c.DefaultLang != "" {
|
||
return c.DefaultLang
|
||
}
|
||
return "en-US"
|
||
}
|
||
|
||
func (c vivaGamesAdapterConfig) AmountRateValue() int64 {
|
||
if c.AmountRate > 0 {
|
||
return c.AmountRate
|
||
}
|
||
return 1
|
||
}
|
||
|
||
func (c vivaGamesAdapterConfig) BGMSwitchValue() int64 {
|
||
if c.BGMSwitch != nil {
|
||
return *c.BGMSwitch
|
||
}
|
||
return 1
|
||
}
|
||
|
||
func (c vivaGamesAdapterConfig) GSPValue() int64 {
|
||
if c.GSP > 0 {
|
||
return c.GSP
|
||
}
|
||
return 101
|
||
}
|
||
|
||
func (c vivaGamesAdapterConfig) MetadataValue(session gamedomain.LaunchSession) string {
|
||
if c.Metadata != "" {
|
||
return c.Metadata
|
||
}
|
||
if strings.TrimSpace(session.RoomID) == "" {
|
||
return ""
|
||
}
|
||
raw, err := json.Marshal(map[string]string{"room_id": strings.TrimSpace(session.RoomID)})
|
||
if err != nil {
|
||
return ""
|
||
}
|
||
return string(raw)
|
||
}
|
||
|
||
func vivaGamesLaunchBaseURL(game gamedomain.LaunchableGame, config vivaGamesAdapterConfig) string {
|
||
for _, key := range []string{game.ProviderGameID, game.GameID, strings.ToLower(game.GameID)} {
|
||
if raw := strings.TrimSpace(config.GameURLs[strings.TrimSpace(key)]); raw != "" {
|
||
return raw
|
||
}
|
||
}
|
||
if config.LaunchURLTemplate == "" {
|
||
return ""
|
||
}
|
||
replacer := strings.NewReplacer(
|
||
"{provider_game_id}", strings.TrimSpace(game.ProviderGameID),
|
||
"{providerGameId}", strings.TrimSpace(game.ProviderGameID),
|
||
"{game_id}", strings.TrimSpace(game.GameID),
|
||
"{gameId}", strings.TrimSpace(game.GameID),
|
||
)
|
||
return strings.TrimSpace(replacer.Replace(config.LaunchURLTemplate))
|
||
}
|
||
|
||
func vivaGamesGameConfigQuery(currencyIcon string) string {
|
||
raw, err := json.Marshal(map[string]string{"currencyIcon": strings.TrimSpace(currencyIcon)})
|
||
if err != nil {
|
||
return ""
|
||
}
|
||
return string(raw)
|
||
}
|
||
|
||
func (s *Service) handleVivaGamesOperation(ctx context.Context, app string, platform gamedomain.Platform, req *gamev1.CallbackRequest, operation string, requestHash string) ([]byte, string, string, bool, string, error) {
|
||
if !callbackIPAllowed(req, platform.CallbackIPWhitelist) {
|
||
raw, contentType := vivaGamesJSON(vivaGamesCodeSignatureInvalid, "ip restricted", map[string]any{})
|
||
return raw, contentType, strconv.Itoa(vivaGamesCodeSignatureInvalid), false, "", nil
|
||
}
|
||
config := vivaGamesConfigFromPlatform(platform)
|
||
body, signed, decoded, expired, providerRequestID := s.decodeVivaGamesBody(req, platform.CallbackSecretCiphertext)
|
||
if !decoded {
|
||
raw, contentType := vivaGamesJSON(vivaGamesCodeInvalidArgument, "invalid argument", map[string]any{})
|
||
return raw, contentType, strconv.Itoa(vivaGamesCodeInvalidArgument), false, providerRequestID, nil
|
||
}
|
||
if expired {
|
||
raw, contentType := vivaGamesJSON(vivaGamesCodeRequestExpired, "request expired", map[string]any{})
|
||
return raw, contentType, strconv.Itoa(vivaGamesCodeRequestExpired), false, providerRequestID, nil
|
||
}
|
||
if !signed {
|
||
raw, contentType := vivaGamesJSON(vivaGamesCodeSignatureInvalid, "sign error", map[string]any{})
|
||
return raw, contentType, strconv.Itoa(vivaGamesCodeSignatureInvalid), false, providerRequestID, nil
|
||
}
|
||
appID := body.AppIDInt64()
|
||
if appID <= 0 {
|
||
raw, contentType := vivaGamesJSON(vivaGamesCodeInvalidArgument, "invalid app_id", map[string]any{})
|
||
return raw, contentType, strconv.Itoa(vivaGamesCodeInvalidArgument), signed, providerRequestID, nil
|
||
}
|
||
if config.AppID > 0 && appID != config.AppID {
|
||
raw, contentType := vivaGamesJSON(vivaGamesCodeAppNotFound, "appId not found", map[string]any{})
|
||
return raw, contentType, strconv.Itoa(vivaGamesCodeAppNotFound), signed, providerRequestID, nil
|
||
}
|
||
switch operation {
|
||
case "get-token", "get_token", "gettoken", "api/get-token", "api/get_token", "v1/api/get-token":
|
||
return s.handleVivaGamesGetToken(ctx, app, config, req, body)
|
||
case "get-user-info", "get_user_info", "userinfo", "user_info", "api/get-user-info", "api/get_user_info", "v1/api/get-user-info":
|
||
return s.handleVivaGamesUserInfo(ctx, app, config, req, body)
|
||
case "update-token", "update_token", "updatetoken", "api/update-token", "api/update_token", "v1/api/update-token":
|
||
return s.handleVivaGamesUpdateToken(ctx, app, config, body)
|
||
case "change-balance", "change_balance", "changebalance", "api/change-balance", "api/change_balance", "v1/api/change-balance":
|
||
return s.handleVivaGamesChangeBalance(ctx, app, config, req, body, requestHash)
|
||
default:
|
||
raw, contentType := vivaGamesJSON(vivaGamesCodeOK, "", map[string]any{})
|
||
return raw, contentType, strconv.Itoa(vivaGamesCodeOK), signed, providerRequestID, nil
|
||
}
|
||
}
|
||
|
||
type vivaGamesBody struct {
|
||
AppID json.Number `json:"app_id"`
|
||
UserID string `json:"user_id"`
|
||
Code string `json:"code"`
|
||
Token string `json:"token"`
|
||
ClientIP string `json:"client_ip"`
|
||
GameID json.Number `json:"game_id"`
|
||
OrderID string `json:"order_id"`
|
||
GameRoundID string `json:"game_round_id"`
|
||
TotalBet int64 `json:"total_bet"`
|
||
ChangeAmount int64 `json:"change_amount"`
|
||
ChangeReason string `json:"change_reason"`
|
||
ChangeTimeAt int64 `json:"change_time_at"`
|
||
Metadata string `json:"metadata"`
|
||
ExtendType string `json:"extend_type"`
|
||
Extend string `json:"extend"`
|
||
IgnoreExpire bool `json:"ignore_expire"`
|
||
Timestamp int64 `json:"timestamp"`
|
||
Signature string `json:"sign"`
|
||
}
|
||
|
||
func (s *Service) decodeVivaGamesBody(req *gamev1.CallbackRequest, secret string) (vivaGamesBody, bool, bool, bool, string) {
|
||
var body vivaGamesBody
|
||
decoder := json.NewDecoder(strings.NewReader(string(req.GetRawBody())))
|
||
decoder.UseNumber()
|
||
if err := decoder.Decode(&body); err != nil {
|
||
return vivaGamesBody{}, false, false, false, ""
|
||
}
|
||
body.normalize()
|
||
providerRequestID := strings.TrimSpace(body.OrderID)
|
||
params, err := vivaGamesParamsForSign(req.GetRawBody())
|
||
if err != nil {
|
||
return vivaGamesBody{}, false, false, false, providerRequestID
|
||
}
|
||
signed, expired := s.vivaGamesSignatureValid(params, body.Signature, body.Timestamp, secret)
|
||
return body, signed, true, expired, providerRequestID
|
||
}
|
||
|
||
func vivaGamesParamsForSign(raw []byte) (map[string]any, error) {
|
||
var params map[string]any
|
||
decoder := json.NewDecoder(strings.NewReader(string(raw)))
|
||
decoder.UseNumber()
|
||
if err := decoder.Decode(¶ms); err != nil {
|
||
return nil, err
|
||
}
|
||
delete(params, "sign")
|
||
return params, nil
|
||
}
|
||
|
||
func (s *Service) vivaGamesSignatureValid(params map[string]any, signature string, timestamp int64, secret string) (bool, bool) {
|
||
secret = strings.TrimSpace(secret)
|
||
if secret == "" || strings.TrimSpace(signature) == "" || timestamp <= 0 {
|
||
return false, false
|
||
}
|
||
requestAt := time.Unix(timestamp, 0)
|
||
now := s.now()
|
||
if requestAt.After(now.Add(vivaGamesSignatureValidWindow)) || now.Sub(requestAt) > vivaGamesSignatureValidWindow {
|
||
return false, true
|
||
}
|
||
expected := vivaGamesSign(params, secret)
|
||
return strings.EqualFold(strings.TrimSpace(signature), expected), false
|
||
}
|
||
|
||
func vivaGamesSign(params map[string]any, secret string) string {
|
||
keys := make([]string, 0, len(params))
|
||
for key, value := range params {
|
||
if value == nil || strings.TrimSpace(key) == "" || strings.EqualFold(key, "sign") {
|
||
continue
|
||
}
|
||
keys = append(keys, key)
|
||
}
|
||
sort.Strings(keys)
|
||
parts := make([]string, 0, len(keys)+1)
|
||
for _, key := range keys {
|
||
parts = append(parts, fmt.Sprintf("%s=%s", key, vivaGamesSignValue(params[key])))
|
||
}
|
||
parts = append(parts, "key="+strings.TrimSpace(secret))
|
||
sum := sha256.Sum256([]byte(strings.Join(parts, "&")))
|
||
return hex.EncodeToString(sum[:])
|
||
}
|
||
|
||
func vivaGamesSignValue(value any) string {
|
||
switch typed := value.(type) {
|
||
case json.Number:
|
||
return typed.String()
|
||
case string:
|
||
return typed
|
||
case bool:
|
||
if typed {
|
||
return "true"
|
||
}
|
||
return "false"
|
||
default:
|
||
return strings.TrimSpace(fmt.Sprint(typed))
|
||
}
|
||
}
|
||
|
||
func (body *vivaGamesBody) normalize() {
|
||
body.UserID = strings.TrimSpace(body.UserID)
|
||
body.Code = strings.TrimSpace(body.Code)
|
||
body.Token = strings.TrimSpace(body.Token)
|
||
body.ClientIP = strings.TrimSpace(body.ClientIP)
|
||
body.OrderID = strings.TrimSpace(body.OrderID)
|
||
body.GameRoundID = strings.TrimSpace(body.GameRoundID)
|
||
body.ChangeReason = strings.ToLower(strings.TrimSpace(body.ChangeReason))
|
||
body.Metadata = strings.TrimSpace(body.Metadata)
|
||
body.ExtendType = strings.TrimSpace(body.ExtendType)
|
||
body.Extend = strings.TrimSpace(body.Extend)
|
||
body.Signature = strings.TrimSpace(body.Signature)
|
||
}
|
||
|
||
func (body vivaGamesBody) AppIDInt64() int64 {
|
||
value, _ := strconv.ParseInt(strings.TrimSpace(body.AppID.String()), 10, 64)
|
||
return value
|
||
}
|
||
|
||
func (body vivaGamesBody) GameIDInt64() int64 {
|
||
value, _ := strconv.ParseInt(strings.TrimSpace(body.GameID.String()), 10, 64)
|
||
return value
|
||
}
|
||
|
||
func (s *Service) handleVivaGamesGetToken(ctx context.Context, app string, config vivaGamesAdapterConfig, req *gamev1.CallbackRequest, body vivaGamesBody) ([]byte, string, string, bool, string, error) {
|
||
if body.UserID == "" || body.Code == "" {
|
||
return vivaGamesAdapterError(vivaGamesCodeInvalidArgument, "invalid argument", true, "")
|
||
}
|
||
session, err := s.validVivaGamesSession(ctx, app, body.Code, false)
|
||
if err != nil || !vivaGamesSessionMatches(session, config, body.UserID, 0) {
|
||
return vivaGamesAdapterError(vivaGamesCodeCodeInvalid, "code error", true, "")
|
||
}
|
||
raw, contentType := vivaGamesJSON(vivaGamesCodeOK, "", map[string]any{
|
||
"token": body.Code,
|
||
"expire_at": session.ExpiresAtMS,
|
||
})
|
||
return raw, contentType, strconv.Itoa(vivaGamesCodeOK), true, "", nil
|
||
}
|
||
|
||
func (s *Service) handleVivaGamesUserInfo(ctx context.Context, app string, config vivaGamesAdapterConfig, req *gamev1.CallbackRequest, body vivaGamesBody) ([]byte, string, string, bool, string, error) {
|
||
if body.UserID == "" || body.Token == "" {
|
||
return vivaGamesAdapterError(vivaGamesCodeInvalidArgument, "invalid argument", true, "")
|
||
}
|
||
session, err := s.validVivaGamesSession(ctx, app, body.Token, false)
|
||
if err != nil || !vivaGamesSessionMatches(session, config, body.UserID, body.GameIDInt64()) {
|
||
return vivaGamesAdapterError(vivaGamesCodeTokenInvalid, "token error", true, "")
|
||
}
|
||
snapshot, err := s.yomiUserSnapshot(ctx, app, req, session.UserID)
|
||
if err != nil {
|
||
return vivaGamesAdapterError(vivaGamesCodeFromError(err), vivaGamesMessageFromError(err), true, "")
|
||
}
|
||
raw, contentType := vivaGamesJSON(vivaGamesCodeOK, "", map[string]any{
|
||
"user_id": externalUserID(session, config.UIDMode),
|
||
"nickname": snapshot.Nickname,
|
||
"avatar_url": snapshot.Avatar,
|
||
"balance": snapshot.Balance,
|
||
"status": 0,
|
||
})
|
||
return raw, contentType, strconv.Itoa(vivaGamesCodeOK), true, "", nil
|
||
}
|
||
|
||
func (s *Service) handleVivaGamesUpdateToken(ctx context.Context, app string, config vivaGamesAdapterConfig, body vivaGamesBody) ([]byte, string, string, bool, string, error) {
|
||
if body.UserID == "" || body.Token == "" {
|
||
return vivaGamesAdapterError(vivaGamesCodeInvalidArgument, "invalid argument", true, "")
|
||
}
|
||
session, err := s.validVivaGamesSession(ctx, app, body.Token, false)
|
||
if err != nil || !vivaGamesSessionMatches(session, config, body.UserID, 0) {
|
||
return vivaGamesAdapterError(vivaGamesCodeTokenInvalid, "token error", true, "")
|
||
}
|
||
raw, contentType := vivaGamesJSON(vivaGamesCodeOK, "", map[string]any{
|
||
"token": body.Token,
|
||
"expire_at": session.ExpiresAtMS,
|
||
})
|
||
return raw, contentType, strconv.Itoa(vivaGamesCodeOK), true, "", nil
|
||
}
|
||
|
||
func (s *Service) handleVivaGamesChangeBalance(ctx context.Context, app string, config vivaGamesAdapterConfig, req *gamev1.CallbackRequest, body vivaGamesBody, requestHash string) ([]byte, string, string, bool, string, error) {
|
||
if body.UserID == "" || body.Token == "" || body.OrderID == "" || body.GameIDInt64() <= 0 || body.ChangeReason == "" {
|
||
return vivaGamesAdapterError(vivaGamesCodeInvalidArgument, "invalid argument", true, body.OrderID)
|
||
}
|
||
session, err := s.validVivaGamesSession(ctx, app, body.Token, body.IgnoreExpire)
|
||
if err != nil || !vivaGamesSessionMatches(session, config, body.UserID, body.GameIDInt64()) {
|
||
return vivaGamesAdapterError(vivaGamesCodeTokenInvalid, "token error", true, body.OrderID)
|
||
}
|
||
if session.ProviderGameID == "" {
|
||
session.ProviderGameID = strconv.FormatInt(body.GameIDInt64(), 10)
|
||
}
|
||
if session.GameID == "" {
|
||
session.GameID = session.ProviderGameID
|
||
}
|
||
opType, amount := vivaGamesOpType(body.ChangeAmount)
|
||
result, err := s.applyYomiCoinChange(ctx, app, req, session, body.OrderID, body.GameRoundID, opType, amount, "", requestHash)
|
||
if err != nil {
|
||
code := vivaGamesCodeFromError(err)
|
||
balance := s.bestEffortCoinBalance(ctx, app, req.GetMeta().GetRequestId(), session.UserID)
|
||
raw, contentType := vivaGamesJSON(code, vivaGamesMessageFromError(err), map[string]any{"balance": balance})
|
||
return raw, contentType, strconv.Itoa(code), true, body.OrderID, nil
|
||
}
|
||
raw, contentType := vivaGamesJSON(vivaGamesCodeOK, "", map[string]any{"balance": result.BalanceAfter})
|
||
return raw, contentType, strconv.Itoa(vivaGamesCodeOK), true, body.OrderID, nil
|
||
}
|
||
|
||
func (s *Service) validVivaGamesSession(ctx context.Context, app string, token string, ignoreExpire bool) (gamedomain.LaunchSession, error) {
|
||
token = strings.TrimSpace(token)
|
||
if token == "" {
|
||
return gamedomain.LaunchSession{}, xerr.New(xerr.Unauthorized, "token invalid")
|
||
}
|
||
// VIVAGAMES token 直接复用 App access token;先解析 JWT 可以避免同一登录 token 多游戏启动时命中过期旧 session。
|
||
if strings.Count(token, ".") == 2 && !ignoreExpire {
|
||
if session, err := s.appSessionFromAccessToken(app, token); err == nil {
|
||
return session, nil
|
||
}
|
||
}
|
||
if session, err := s.repository.GetLaunchSessionByToken(ctx, app, token); err == nil {
|
||
if session.Status != gamedomain.SessionActive {
|
||
return gamedomain.LaunchSession{}, xerr.New(xerr.SessionExpired, "token expired")
|
||
}
|
||
if !ignoreExpire && session.ExpiresAtMS <= s.now().UnixMilli() {
|
||
return gamedomain.LaunchSession{}, xerr.New(xerr.SessionExpired, "token expired")
|
||
}
|
||
return session, nil
|
||
}
|
||
if strings.Count(token, ".") == 2 {
|
||
return s.appSessionFromAccessToken(app, token)
|
||
}
|
||
return gamedomain.LaunchSession{}, xerr.New(xerr.Unauthorized, "token invalid")
|
||
}
|
||
|
||
func vivaGamesSessionMatches(session gamedomain.LaunchSession, config vivaGamesAdapterConfig, userID string, gameID int64) bool {
|
||
if gameID > 0 && strings.TrimSpace(session.ProviderGameID) != "" && strconv.FormatInt(gameID, 10) != strings.TrimSpace(session.ProviderGameID) {
|
||
return false
|
||
}
|
||
userID = strings.TrimSpace(userID)
|
||
return userID == externalUserID(session, config.UIDMode) ||
|
||
userID == strings.TrimSpace(session.DisplayUserID) ||
|
||
userID == strconv.FormatInt(session.UserID, 10)
|
||
}
|
||
|
||
func vivaGamesOpType(changeAmount int64) (string, int64) {
|
||
if changeAmount < 0 {
|
||
return "debit", -changeAmount
|
||
}
|
||
return "credit", changeAmount
|
||
}
|
||
|
||
func vivaGamesAdapterError(code int, message string, signatureValid bool, providerRequestID string) ([]byte, string, string, bool, string, error) {
|
||
raw, contentType := vivaGamesJSON(code, message, map[string]any{})
|
||
return raw, contentType, strconv.Itoa(code), signatureValid, providerRequestID, nil
|
||
}
|
||
|
||
func vivaGamesJSON(code int, message string, data any) ([]byte, string) {
|
||
if data == nil {
|
||
data = map[string]any{}
|
||
}
|
||
return jsonResponse(map[string]any{
|
||
"code": code,
|
||
"msg": strings.TrimSpace(message),
|
||
"data": data,
|
||
})
|
||
}
|
||
|
||
func vivaGamesCodeFromError(err error) int {
|
||
switch {
|
||
case err == nil:
|
||
return vivaGamesCodeOK
|
||
case xerr.IsCode(err, xerr.InsufficientBalance):
|
||
return vivaGamesCodeInsufficientBalance
|
||
case xerr.IsCode(err, xerr.SessionExpired), xerr.IsCode(err, xerr.Unauthorized):
|
||
return vivaGamesCodeTokenInvalid
|
||
case xerr.IsCode(err, xerr.InvalidArgument):
|
||
return vivaGamesCodeInvalidArgument
|
||
case xerr.IsCode(err, xerr.Conflict):
|
||
return vivaGamesCodeGameMaintenance
|
||
case xerr.IsCode(err, xerr.NotFound):
|
||
return vivaGamesCodeTokenInvalid
|
||
default:
|
||
return vivaGamesCodeInternalError
|
||
}
|
||
}
|
||
|
||
func vivaGamesMessageFromError(err error) string {
|
||
switch vivaGamesCodeFromError(err) {
|
||
case vivaGamesCodeInsufficientBalance:
|
||
return "balance not enough"
|
||
case vivaGamesCodeTokenInvalid:
|
||
return "token error"
|
||
case vivaGamesCodeInvalidArgument:
|
||
return "invalid argument"
|
||
case vivaGamesCodeGameMaintenance:
|
||
return "game maintenance"
|
||
default:
|
||
if err != nil && strings.TrimSpace(xerr.MessageOf(err)) != "" {
|
||
return xerr.MessageOf(err)
|
||
}
|
||
return "internal error"
|
||
}
|
||
}
|