gateway_key_billing.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. package handler
  2. import (
  3. "net/http"
  4. "time"
  5. "github.com/Wei-Shaw/sub2api/internal/config"
  6. "github.com/Wei-Shaw/sub2api/internal/pkg/timezone"
  7. middleware2 "github.com/Wei-Shaw/sub2api/internal/server/middleware"
  8. "github.com/Wei-Shaw/sub2api/internal/service"
  9. "github.com/gin-gonic/gin"
  10. )
  11. const keyBillingInfoSchemaVersion = 1
  12. type keyBillingInfoResponse struct {
  13. Object string `json:"object"`
  14. SchemaVersion int `json:"schema_version"`
  15. BillingScope string `json:"billing_scope"`
  16. GroupRateMultiplier float64 `json:"group_rate_multiplier"`
  17. UserRateMultiplier *float64 `json:"user_rate_multiplier,omitempty"`
  18. ResolvedRateMultiplier float64 `json:"resolved_rate_multiplier"`
  19. PeakRateEnabled bool `json:"peak_rate_enabled"`
  20. PeakStart *string `json:"peak_start,omitempty"`
  21. PeakEnd *string `json:"peak_end,omitempty"`
  22. PeakRateMultiplier *float64 `json:"peak_rate_multiplier,omitempty"`
  23. AppliedPeakMultiplier *float64 `json:"applied_peak_multiplier,omitempty"`
  24. EffectiveRateMultiplier float64 `json:"effective_rate_multiplier"`
  25. Timezone *string `json:"timezone,omitempty"`
  26. ObservedAt time.Time `json:"observed_at"`
  27. }
  28. // KeyBillingInfo returns the token billing multiplier effective for the authenticated API key.
  29. // GET /v1/sub2api/billing
  30. func (h *GatewayHandler) KeyBillingInfo(c *gin.Context) {
  31. apiKey, ok := middleware2.GetAPIKeyFromContext(c)
  32. if !ok {
  33. h.errorResponse(c, http.StatusUnauthorized, "authentication_error", "Invalid API key")
  34. return
  35. }
  36. if h.cfg != nil && h.cfg.RunMode == config.RunModeSimple {
  37. h.errorResponse(c, http.StatusNotFound, "not_found_error", "Billing information is not supported in simple mode")
  38. return
  39. }
  40. if apiKey.GroupID == nil {
  41. h.errorResponse(c, http.StatusForbidden, "permission_error", "API key is not assigned to a group")
  42. return
  43. }
  44. if apiKey.Group == nil {
  45. h.errorResponse(c, http.StatusInternalServerError, "api_error", "Billing information is unavailable")
  46. return
  47. }
  48. resolvedRate, ok := h.resolveKeyBillingRate(c, apiKey)
  49. if !ok {
  50. h.errorResponse(c, http.StatusInternalServerError, "api_error", "Billing information is unavailable")
  51. return
  52. }
  53. c.Header("Cache-Control", "no-store")
  54. c.JSON(http.StatusOK, buildKeyBillingInfo(apiKey, resolvedRate, timezone.Now()))
  55. }
  56. func (h *GatewayHandler) resolveKeyBillingRate(c *gin.Context, apiKey *service.APIKey) (float64, bool) {
  57. groupRate := apiKey.Group.RateMultiplier
  58. switch apiKey.Group.Platform {
  59. case service.PlatformOpenAI, service.PlatformGrok:
  60. if h.openAIGatewayService == nil {
  61. return 0, false
  62. }
  63. return h.openAIGatewayService.ResolveUserGroupRateMultiplier(c.Request.Context(), apiKey.UserID, *apiKey.GroupID, groupRate), true
  64. default:
  65. if h.gatewayService == nil {
  66. return 0, false
  67. }
  68. return h.gatewayService.ResolveUserGroupRateMultiplier(c.Request.Context(), apiKey.UserID, *apiKey.GroupID, groupRate), true
  69. }
  70. }
  71. func buildKeyBillingInfo(apiKey *service.APIKey, resolvedRate float64, now time.Time) keyBillingInfoResponse {
  72. groupRate := apiKey.Group.RateMultiplier
  73. var userRate *float64
  74. if resolvedRate != groupRate {
  75. userRate = &resolvedRate
  76. }
  77. appliedPeak := apiKey.Group.PeakMultiplierAt(now)
  78. response := keyBillingInfoResponse{
  79. Object: "sub2api.key_billing",
  80. SchemaVersion: keyBillingInfoSchemaVersion,
  81. BillingScope: "token",
  82. GroupRateMultiplier: groupRate,
  83. UserRateMultiplier: userRate,
  84. ResolvedRateMultiplier: resolvedRate,
  85. PeakRateEnabled: apiKey.Group.PeakRateEnabled,
  86. EffectiveRateMultiplier: resolvedRate * appliedPeak,
  87. ObservedAt: now.UTC(),
  88. }
  89. if apiKey.Group.PeakRateEnabled {
  90. response.PeakStart = &apiKey.Group.PeakStart
  91. response.PeakEnd = &apiKey.Group.PeakEnd
  92. response.PeakRateMultiplier = &apiKey.Group.PeakRateMultiplier
  93. response.AppliedPeakMultiplier = &appliedPeak
  94. tz := timezone.Location().String()
  95. response.Timezone = &tz
  96. }
  97. return response
  98. }