- Backend: add ListProviderInstancesWithConfig that decrypts config
and masks sensitive fields (keys, secrets) as "••••••••"
- Frontend: pre-fill non-sensitive config values when editing provider
(PID, API base URL, notify URL, return URL, channel IDs etc.)
- Sensitive fields left empty for user to re-enter if needed
- Add PurchaseRouter.vue to dynamically render PaymentView or
PurchaseSubscriptionView based on payment_enabled vs
purchase_subscription_enabled settings
- Fix route guard to allow access when either payment system is enabled
- Split sidebar menu: /purchase shows for both systems, /orders only
for built-in payment
- Remove auto-migration that forcibly disabled legacy purchase system
on first startup (MigrateLegacyPurchaseURL)
- Add payment_enabled to PublicSettings API response so frontend can
check both systems from a single endpoint
- Restore legacy purchase link config UI controls in admin Payment tab
- Split payment settings save to use dedicated PUT /admin/payment/config
API instead of mixing into general settings endpoint
- Add admin payment config API functions (getConfig/updateConfig)
- payment_service: split GetDashboardStats (131→35 lines) into 4 sub-functions,
extract applyPagination helper, replace bubble sort with sort.Slice,
use sync.Once for EnsureProviders, define magic number constants
- providers: merge duplicate PC/Mobile functions in alipay/wxpay,
extract loadKeyPair/queryOrderTotalFen in wxpay, add centsToYuan in stripe,
define constants for success codes, currencies, event types
- handlers: extract requireAuth and parseIDParam helpers to eliminate
repeated auth/ID-parsing boilerplate
- registry: remove dead seen map in GetProviderByKey
- types: centralize order status constants in payment package
- load_balancer+config_service: unify duplicated containsType into
exported InstanceSupportsType function
Critical:
- Refund idempotency keys now include UnixNano timestamp (alipay/wxpay)
- PaymentView passes qr/pay_url params to QR code page
- Fix dead code branch in order result handling
Quality:
- Currency symbol $ → ¥ in admin stats and refund dialog
- StripePaymentView setTimeout cleanup on unmount
- Webhook body size limited to 1MB (io.LimitReader)
- SubscriptionPlan features type documented
- Run gofmt on user schema, config test, group handler
- Remove unused mergeGroupIDs function
- Restore shared test helpers (newJSONResponse, queuedHTTPUpstream)
that were in deleted Sora test file
- Add int64(0) param to SelectAccountWithLoadAwareness callers (signature change from channel scheduling refactor)
- Add UsageMapHook type and struct field to StreamingProcessor
- Revert Claude Max cache billing code to upstream/main (not part of channel feature)
- Revert credits overages logic to upstream/main (non-channel change)
- Remove Instructions field reference (non-channel OpenAI feature)
- Restore sora_client_handler_test.go from upstream + add channel service nil params
- Fix 7 stale comments still mentioning "限制检查" in handlers/services
- Make billingModelForRestriction explicitly list channel_mapped case
- Add slog.Warn for error swallowing in ResolveChannelMapping and
needsUpstreamChannelRestrictionCheck
- Document sticky session upstream check exemption
Move the model pricing restriction check from 8 handler entry points
to the account scheduling phase (SelectAccountForModelWithExclusions /
SelectAccountWithLoadAwareness), aligning restriction with billing:
- requested: check original request model against pricing list
- channel_mapped: check channel-mapped model against pricing list
- upstream: per-account check using account-mapped model
Handler layer now only resolves channel mapping (no restriction).
Scheduling layer performs pre-check for requested/channel_mapped,
and per-account filtering for upstream billing source.
Replace response.BadRequest with response.ErrorFrom + infraerrors.BadRequest
to provide machine-readable reason codes (VALIDATION_ERROR, INVALID_CHANNEL_ID,
MISSING_PARAMETER) for frontend i18n support.
- PricingSourceChannel/LiteLLM/Fallback for resolver source
- MediaTypeImage/Video/Prompt for result.MediaType
- Reuse BillingModeToken/BillingModeImage for billing mode
- Reuse BillingModelSourceChannelMapped/PlatformAnthropic in handler
- Backend: reject intervals with all-null price fields on save
- Backend: filterValidIntervals skips empty intervals in pricing resolver
- Frontend: red border + asterisk on empty interval rows
- Backend: antigravity groups now match anthropic/gemini channel pricing
- Fix errcheck: defer rows.Close() with nolint
- Fix errcheck: type assertion with ok check in channel cache
- Fix staticcheck ST1005: lowercase error string
- Fix staticcheck SA5011: nil check cost before use in openai gateway
- Fix gofmt: format chatcompletions_to_responses.go
- Parse candidatesTokensDetails from Gemini API to separate image/text output tokens
- Add image_output_tokens and image_output_cost to usage_log (migration 089)
- Support per-image-token pricing via output_cost_per_image_token from model pricing data
- Channel pricing ImageOutputPrice override works in token billing mode
- Auto-fill image_output_price in channel pricing form from model defaults
- Add "channel_mapped" billing model source as new default (migration 088)
- Bills by model name after channel mapping, before account mapping
- Fix channel cache error TTL sign error (115s → 5s)
- Fix Update channel only invalidating new groups, not removed groups
- Fix frontend model_mapping clearing sending undefined instead of {}
- Credits balance precheck via shared AccountUsageService cache before injection
- Skip credits injection for accounts with insufficient balance
- Don't mark credits exhausted for "exhausted your capacity on this model" 429s
Resolved conflicts:
- VERSION: set to 0.1.106.1
- wire_gen.go: keep our version with accountUsageService
- antigravity_gateway_service.go: keep accountUsageService field/param
- openai_gateway_service.go: keep billing model override logic
- account.go: keep both affinity methods and new custom base URL methods
- dto/types.go: keep both affinity fields and new custom base URL fields
- CreateAccountModal.vue: keep both affinity and custom base URL logic
- EditAccountModal.vue: keep both affinity and custom base URL state
Allow redeem codes with negative values to enable refund scenarios:
- Balance: negative value deducts balance (clamped to 0, never negative)
- Concurrency: negative value reduces concurrency (clamped to 0)
- Subscription: negative validity_days reduces remaining days; if
remaining days <= 0, the subscription is canceled (set to expired)
All deductions generate standard redeem code records for audit trail.
Allow redeem codes with negative values to enable refund scenarios:
- Balance: negative value deducts balance (clamped to 0, never negative)
- Concurrency: negative value reduces concurrency (clamped to 0)
- Subscription: negative validity_days reduces remaining days; if
remaining days <= 0, the subscription is canceled (set to expired)
All deductions generate standard redeem code records for audit trail.
- Fix 7 stale comments still mentioning "限制检查" in handlers/services
- Make billingModelForRestriction explicitly list channel_mapped case
- Add slog.Warn for error swallowing in ResolveChannelMapping and
needsUpstreamChannelRestrictionCheck
- Document sticky session upstream check exemption
Move the model pricing restriction check from 8 handler entry points
to the account scheduling phase (SelectAccountForModelWithExclusions /
SelectAccountWithLoadAwareness), aligning restriction with billing:
- requested: check original request model against pricing list
- channel_mapped: check channel-mapped model against pricing list
- upstream: per-account check using account-mapped model
Handler layer now only resolves channel mapping (no restriction).
Scheduling layer performs pre-check for requested/channel_mapped,
and per-account filtering for upstream billing source.