Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions controller/token_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,21 +48,21 @@ type sqliteColumnInfo struct {
}

type legacyToken struct {
Id int `gorm:"primaryKey"`
UserId int `gorm:"index"`
Key string `gorm:"column:key;type:char(48);uniqueIndex"`
Status int `gorm:"default:1"`
Name string `gorm:"index"`
CreatedTime int64 `gorm:"bigint"`
AccessedTime int64 `gorm:"bigint"`
ExpiredTime int64 `gorm:"bigint;default:-1"`
RemainQuota int `gorm:"default:0"`
Id int `gorm:"primaryKey"`
UserId int `gorm:"index"`
Key string `gorm:"column:key;type:char(48);uniqueIndex"`
Status int `gorm:"default:1"`
Name string `gorm:"index"`
CreatedTime int64 `gorm:"bigint"`
AccessedTime int64 `gorm:"bigint"`
ExpiredTime int64 `gorm:"bigint;default:-1"`
RemainQuota int `gorm:"default:0"`
UnlimitedQuota bool
ModelLimitsEnabled bool
ModelLimits string `gorm:"type:text"`
AllowIps *string `gorm:"default:''"`
UsedQuota int `gorm:"default:0"`
Group string `gorm:"column:group;default:''"`
ModelLimits string `gorm:"type:text"`
AllowIps *string `gorm:"default:''"`
UsedQuota int `gorm:"default:0"`
Group string `gorm:"column:group;default:''"`
CrossGroupRetry bool
DeletedAt gorm.DeletedAt `gorm:"index"`
}
Expand Down
52 changes: 32 additions & 20 deletions controller/topup_waffo_pancake.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,6 @@ func RequestWaffoPancakeAmount(c *gin.Context) {

func getWaffoPancakePayMoney(amount int64, group string) float64 {
dAmount := decimal.NewFromInt(amount)
if operation_setting.GetQuotaDisplayType() == operation_setting.QuotaDisplayTypeTokens {
dAmount = dAmount.Div(decimal.NewFromFloat(common.QuotaPerUnit))
}

topupGroupRatio := common.GetTopupGroupRatio(group)
if topupGroupRatio == 0 {
Expand All @@ -74,20 +71,6 @@ func getWaffoPancakePayMoney(amount int64, group string) float64 {
return payMoney.InexactFloat64()
}

func normalizeWaffoPancakeTopUpAmount(amount int64) int64 {
if operation_setting.GetQuotaDisplayType() != operation_setting.QuotaDisplayTypeTokens {
return amount
}

normalized := decimal.NewFromInt(amount).
Div(decimal.NewFromFloat(common.QuotaPerUnit)).
IntPart()
if normalized < 1 {
return 1
}
return normalized
}

func formatWaffoPancakeAmount(payMoney float64) string {
return decimal.NewFromFloat(payMoney).StringFixed(2)
}
Expand Down Expand Up @@ -157,9 +140,10 @@ func RequestWaffoPancakePay(c *gin.Context) {
}

tradeNo := fmt.Sprintf("WAFFO_PANCAKE-%d-%d-%s", id, time.Now().UnixMilli(), randstr.String(6))
currency := strings.ToUpper(strings.TrimSpace(setting.WaffoPancakeCurrency))
topUp := &model.TopUp{
UserId: id,
Amount: normalizeWaffoPancakeTopUpAmount(req.Amount),
Amount: req.Amount,
Money: payMoney,
TradeNo: tradeNo,
PaymentMethod: model.PaymentMethodWaffoPancake,
Expand All @@ -178,7 +162,7 @@ func RequestWaffoPancakePay(c *gin.Context) {
StoreID: setting.WaffoPancakeStoreID,
ProductID: setting.WaffoPancakeProductID,
ProductType: "onetime",
Currency: strings.ToUpper(strings.TrimSpace(setting.WaffoPancakeCurrency)),
Currency: currency,
PriceSnapshot: &service.WaffoPancakePriceSnapshot{
Amount: formatWaffoPancakeAmount(payMoney),
TaxIncluded: false,
Expand All @@ -187,6 +171,14 @@ func RequestWaffoPancakePay(c *gin.Context) {
BuyerEmail: getWaffoPancakeBuyerEmail(user),
SuccessURL: getWaffoPancakeReturnURL(),
ExpiresInSeconds: &expiresInSeconds,
Metadata: buildWaffoPancakeOrderMetadata(
id,
tradeNo,
req.Amount,
req.Amount,
payMoney,
currency,
),
})
if err != nil {
logger.LogError(c.Request.Context(), fmt.Sprintf("Waffo Pancake 创建结账会话失败 user_id=%d trade_no=%s error=%q", id, tradeNo, err.Error()))
Expand All @@ -208,6 +200,26 @@ func RequestWaffoPancakePay(c *gin.Context) {
})
}

func buildWaffoPancakeOrderMetadata(userID int, tradeNo string, requestedAmount int64, storedAmount int64, money float64, currency string) *service.WaffoPancakeOrderMetadata {
mode := "prod"
if setting.WaffoPancakeSandbox {
mode = "test"
}

return &service.WaffoPancakeOrderMetadata{
TradeNo: tradeNo,
UserID: userID,
PaymentMethod: model.PaymentMethodWaffoPancake,
RequestedAmount: requestedAmount,
StoredAmount: storedAmount,
Money: formatWaffoPancakeAmount(money),
Currency: currency,
StoreID: setting.WaffoPancakeStoreID,
ProductID: setting.WaffoPancakeProductID,
Mode: mode,
}
}

func WaffoPancakeWebhook(c *gin.Context) {
if !isWaffoPancakeWebhookEnabled() {
logger.LogWarn(c.Request.Context(), fmt.Sprintf("Waffo Pancake webhook 被拒绝 reason=webhook_disabled path=%q client_ip=%s", c.Request.RequestURI, c.ClientIP()))
Expand Down Expand Up @@ -248,7 +260,7 @@ func WaffoPancakeWebhook(c *gin.Context) {
LockOrder(tradeNo)
defer UnlockOrder(tradeNo)

if err := model.RechargeWaffoPancake(tradeNo); err != nil {
if err := model.RechargeWaffoPancake(tradeNo, c.ClientIP()); err != nil {
logger.LogError(c.Request.Context(), fmt.Sprintf("Waffo Pancake 充值处理失败 trade_no=%s event_id=%s order_id=%s client_ip=%s error=%q", tradeNo, event.ID, event.Data.OrderID, c.ClientIP(), err.Error()))
c.String(http.StatusInternalServerError, "retry")
return
Expand Down
35 changes: 32 additions & 3 deletions controller/topup_waffo_pancake_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"testing"

"github.com/QuantumNous/new-api/common"
"github.com/QuantumNous/new-api/model"
"github.com/QuantumNous/new-api/setting"
"github.com/QuantumNous/new-api/setting/operation_setting"
"github.com/stretchr/testify/require"
Expand All @@ -27,6 +28,34 @@ func TestFormatWaffoPancakeAmount_UsesDisplayPriceString(t *testing.T) {
}
}

func TestBuildWaffoPancakeOrderMetadata(t *testing.T) {
originalSandbox := setting.WaffoPancakeSandbox
originalStoreID := setting.WaffoPancakeStoreID
originalProductID := setting.WaffoPancakeProductID
t.Cleanup(func() {
setting.WaffoPancakeSandbox = originalSandbox
setting.WaffoPancakeStoreID = originalStoreID
setting.WaffoPancakeProductID = originalProductID
})

setting.WaffoPancakeSandbox = true
setting.WaffoPancakeStoreID = "store_123"
setting.WaffoPancakeProductID = "product_456"

metadata := buildWaffoPancakeOrderMetadata(42, "WAFFO_PANCAKE-42-123456-abc123", 1000, 10, 29, "USD")

require.Equal(t, "WAFFO_PANCAKE-42-123456-abc123", metadata.TradeNo)
require.Equal(t, 42, metadata.UserID)
require.Equal(t, model.PaymentMethodWaffoPancake, metadata.PaymentMethod)
require.Equal(t, int64(1000), metadata.RequestedAmount)
require.Equal(t, int64(10), metadata.StoredAmount)
require.Equal(t, "29.00", metadata.Money)
require.Equal(t, "USD", metadata.Currency)
require.Equal(t, "store_123", metadata.StoreID)
require.Equal(t, "product_456", metadata.ProductID)
require.Equal(t, "test", metadata.Mode)
}

func TestGetWaffoPancakePayMoney(t *testing.T) {
originalUnitPrice := setting.WaffoPancakeUnitPrice
originalQuotaDisplayType := operation_setting.GetGeneralSetting().QuotaDisplayType
Expand Down Expand Up @@ -66,11 +95,11 @@ func TestGetWaffoPancakePayMoney(t *testing.T) {
expected: 24,
},
{
name: "tokens display converts quota to display units before pricing",
amount: int64(common.QuotaPerUnit * 3),
name: "quota display type does not affect Waffo Pancake pricing",
amount: 10,
group: "vip",
quotaDisplayType: operation_setting.QuotaDisplayTypeTokens,
expected: 4.5,
expected: 24,
},
{
name: "non-positive discount falls back to no discount",
Expand Down
6 changes: 3 additions & 3 deletions dto/gemini.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ func (r *GeminiChatRequest) UnmarshalJSON(data []byte) error {
}

type ToolConfig struct {
FunctionCallingConfig *FunctionCallingConfig `json:"functionCallingConfig,omitempty"`
RetrievalConfig *RetrievalConfig `json:"retrievalConfig,omitempty"`
IncludeServerSideToolInvocations *bool `json:"includeServerSideToolInvocations,omitempty"`
FunctionCallingConfig *FunctionCallingConfig `json:"functionCallingConfig,omitempty"`
RetrievalConfig *RetrievalConfig `json:"retrievalConfig,omitempty"`
IncludeServerSideToolInvocations *bool `json:"includeServerSideToolInvocations,omitempty"`
}

type FunctionCallingConfig struct {
Expand Down
36 changes: 18 additions & 18 deletions model/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,24 @@ import (
)

type Log struct {
Id int `json:"id" gorm:"index:idx_created_at_id,priority:1;index:idx_user_id_id,priority:2"`
UserId int `json:"user_id" gorm:"index;index:idx_user_id_id,priority:1"`
CreatedAt int64 `json:"created_at" gorm:"bigint;index:idx_created_at_id,priority:2;index:idx_created_at_type"`
Type int `json:"type" gorm:"index:idx_created_at_type"`
Content string `json:"content"`
Username string `json:"username" gorm:"index;index:index_username_model_name,priority:2;default:''"`
TokenName string `json:"token_name" gorm:"index;default:''"`
ModelName string `json:"model_name" gorm:"index;index:index_username_model_name,priority:1;default:''"`
Quota int `json:"quota" gorm:"default:0"`
PromptTokens int `json:"prompt_tokens" gorm:"default:0"`
CompletionTokens int `json:"completion_tokens" gorm:"default:0"`
UseTime int `json:"use_time" gorm:"default:0"`
IsStream bool `json:"is_stream"`
ChannelId int `json:"channel" gorm:"index"`
ChannelName string `json:"channel_name" gorm:"->"`
TokenId int `json:"token_id" gorm:"default:0;index"`
Group string `json:"group" gorm:"index"`
Ip string `json:"ip" gorm:"index;default:''"`
Id int `json:"id" gorm:"index:idx_created_at_id,priority:1;index:idx_user_id_id,priority:2"`
UserId int `json:"user_id" gorm:"index;index:idx_user_id_id,priority:1"`
CreatedAt int64 `json:"created_at" gorm:"bigint;index:idx_created_at_id,priority:2;index:idx_created_at_type"`
Type int `json:"type" gorm:"index:idx_created_at_type"`
Content string `json:"content"`
Username string `json:"username" gorm:"index;index:index_username_model_name,priority:2;default:''"`
TokenName string `json:"token_name" gorm:"index;default:''"`
ModelName string `json:"model_name" gorm:"index;index:index_username_model_name,priority:1;default:''"`
Quota int `json:"quota" gorm:"default:0"`
PromptTokens int `json:"prompt_tokens" gorm:"default:0"`
CompletionTokens int `json:"completion_tokens" gorm:"default:0"`
UseTime int `json:"use_time" gorm:"default:0"`
IsStream bool `json:"is_stream"`
ChannelId int `json:"channel" gorm:"index"`
ChannelName string `json:"channel_name" gorm:"->"`
TokenId int `json:"token_id" gorm:"default:0;index"`
Group string `json:"group" gorm:"index"`
Ip string `json:"ip" gorm:"index;default:''"`
RequestId string `json:"request_id,omitempty" gorm:"type:varchar(64);index:idx_logs_request_id;default:''"`
UpstreamRequestId string `json:"upstream_request_id,omitempty" gorm:"type:varchar(128);index:idx_logs_upstream_request_id;default:''"`
Other string `json:"other"`
Expand Down
27 changes: 26 additions & 1 deletion model/payment_method_guard_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ func TestRechargeWaffoPancake_RejectsMismatchedPaymentMethod(t *testing.T) {
insertUserForPaymentGuardTest(t, 101, 0)
insertTopUpForPaymentGuardTest(t, "waffo-pancake-guard", 101, PaymentProviderStripe)

err := RechargeWaffoPancake("waffo-pancake-guard")
err := RechargeWaffoPancake("waffo-pancake-guard", "203.0.113.10")
require.Error(t, err)

topUp := GetTopUpByTradeNo("waffo-pancake-guard")
Expand All @@ -102,6 +102,31 @@ func TestRechargeWaffoPancake_RejectsMismatchedPaymentMethod(t *testing.T) {
assert.Equal(t, 0, getUserQuotaForPaymentGuardTest(t, 101))
}

func TestRechargeWaffoPancake_RecordsTopupAuditInfo(t *testing.T) {
truncateTables(t)

insertUserForPaymentGuardTest(t, 102, 0)
insertTopUpForPaymentGuardTest(t, "waffo-pancake-audit", 102, PaymentProviderWaffoPancake)

err := RechargeWaffoPancake("waffo-pancake-audit", "203.0.113.11")
require.NoError(t, err)

var log Log
require.NoError(t, LOG_DB.Where("user_id = ? AND type = ?", 102, LogTypeTopup).First(&log).Error)
assert.Equal(t, "203.0.113.11", log.Ip)

var other map[string]any
require.NoError(t, common.Unmarshal([]byte(log.Other), &other))
adminInfo, ok := other["admin_info"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "203.0.113.11", adminInfo["caller_ip"])
assert.Equal(t, PaymentMethodWaffoPancake, adminInfo["payment_method"])
assert.Equal(t, PaymentMethodWaffoPancake, adminInfo["callback_payment_method"])
assert.NotEmpty(t, adminInfo["server_ip"])
assert.NotNil(t, adminInfo["node_name"])
assert.NotEmpty(t, adminInfo["version"])
}

func TestUpdatePendingTopUpStatus_RejectsMismatchedPaymentProvider(t *testing.T) {
testCases := []struct {
name string
Expand Down
4 changes: 2 additions & 2 deletions model/topup.go
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,7 @@ func RechargeWaffo(tradeNo string, callerIp string) (err error) {
return nil
}

func RechargeWaffoPancake(tradeNo string) (err error) {
func RechargeWaffoPancake(tradeNo string, callerIp string) (err error) {
if tradeNo == "" {
return errors.New("未提供支付单号")
}
Expand Down Expand Up @@ -580,7 +580,7 @@ func RechargeWaffoPancake(tradeNo string) (err error) {
}

if quotaToAdd > 0 {
RecordLog(topUp.UserId, LogTypeTopup, fmt.Sprintf("Waffo Pancake充值成功,充值额度: %v,支付金额: %.2f", logger.FormatQuota(quotaToAdd), topUp.Money))
RecordTopupLog(topUp.UserId, fmt.Sprintf("Waffo Pancake充值成功,充值额度: %v,支付金额: %.2f", logger.FormatQuota(quotaToAdd), topUp.Money), callerIp, topUp.PaymentMethod, PaymentMethodWaffoPancake)
}

return nil
Expand Down
48 changes: 24 additions & 24 deletions pkg/billingexpr/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,30 +39,30 @@ var (

// compileEnvPrototypeV1 is the v1 type-checking prototype used at compile time.
var compileEnvPrototypeV1 = map[string]interface{}{
"p": float64(0),
"c": float64(0),
"len": float64(0),
"cr": float64(0),
"cc": float64(0),
"cc1h": float64(0),
"img": float64(0),
"img_o": float64(0),
"ai": float64(0),
"ao": float64(0),
"tier": func(string, float64) float64 { return 0 },
"header": func(string) string { return "" },
"param": func(string) interface{} { return nil },
"has": func(interface{}, string) bool { return false },
"hour": func(string) int { return 0 },
"minute": func(string) int { return 0 },
"weekday": func(string) int { return 0 },
"month": func(string) int { return 0 },
"day": func(string) int { return 0 },
"max": math.Max,
"min": math.Min,
"abs": math.Abs,
"ceil": math.Ceil,
"floor": math.Floor,
"p": float64(0),
"c": float64(0),
"len": float64(0),
"cr": float64(0),
"cc": float64(0),
"cc1h": float64(0),
"img": float64(0),
"img_o": float64(0),
"ai": float64(0),
"ao": float64(0),
"tier": func(string, float64) float64 { return 0 },
"header": func(string) string { return "" },
"param": func(string) interface{} { return nil },
"has": func(interface{}, string) bool { return false },
"hour": func(string) int { return 0 },
"minute": func(string) int { return 0 },
"weekday": func(string) int { return 0 },
"month": func(string) int { return 0 },
"day": func(string) int { return 0 },
"max": math.Max,
"min": math.Min,
"abs": math.Abs,
"ceil": math.Ceil,
"floor": math.Floor,
}

func getCompileEnv(version int) map[string]interface{} {
Expand Down
Loading