Skip to content

fix billing refund consistency - #6066

Closed
cat0825 wants to merge 1 commit into
QuantumNous:mainfrom
cat0825:codex/fix-p0-billing-refunds
Closed

fix billing refund consistency#6066
cat0825 wants to merge 1 commit into
QuantumNous:mainfrom
cat0825:codex/fix-p0-billing-refunds

Conversation

@cat0825

@cat0825 cat0825 commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

⚠️ 提交说明 / PR Notice

Important

  • 请提供人工撰写的简洁摘要,避免直接粘贴未经整理的 AI 输出。

📝 变更描述 / Description

修复几处计费、退款和订阅隔离问题:

  • 订阅预扣退款现在在同一个数据库事务内同时回退 user_subscriptions.amount_used 并标记 subscription_pre_consume_records 为 refunded,避免额度已回退但记录状态更新失败后重试导致重复退款。
  • 订阅预扣增加请求分组校验,带 upgrade_group 的订阅计划只覆盖同分组请求,避免跨分组模型错误扣订阅额度。
  • 异步任务失败退款和负差额结算会同步回退用户/渠道 used_quota,避免退款后“剩余额度 + 已用额度”虚增。
  • 异常结束的流式响应如果没有上游 usage,不再用估算 prompt tokens 扣费;正常结束或上游返回 usage 的路径不变。
  • channels.group 扩到 varchar(1024),避免多分组渠道保存失败或升级时被迁移回 64 字符。

This PR was AI-assisted and manually reviewed before submission.

🚀 变更类型 / Type of change

  • 🐛 Bug 修复 (Bug fix) - 请关联对应 Issue,避免将设计取舍、理解偏差或预期不一致直接归类为 bug
  • ✨ 新功能 (New feature) - 重大特性建议先通过 Issue 沟通
  • ⚡ 性能优化 / 重构 (Refactor)
  • 📝 文档更新 (Documentation)

🔗 关联任务 / Related Issue

✅ 提交前检查项 / Checklist

  • 人工确认: 我已亲自整理并撰写此描述,没有直接粘贴未经处理的 AI 输出。
  • 非重复提交: 我已搜索现有的 IssuesPRs,确认不是重复提交。
  • Bug fix 说明: 若此 PR 标记为 Bug fix,我已提交或关联对应 Issue,且不会将设计取舍、预期不一致或理解偏差直接归类为 bug。
  • 变更理解: 我已理解这些更改的工作原理及可能影响。
  • 范围聚焦: 本 PR 未包含任何与当前任务无关的代码改动。
  • 本地验证: 已在本地运行并通过测试或手动验证,维护者可以据此复核结果。
  • 安全合规: 代码中无敏感凭据,且符合项目代码规范。

📸 运行证明 / Proof of Work

docker run --rm -v "$PWD":/work -v /tmp/new-api-gomod:/gomod -v /tmp/new-api-gocache:/gocache -w /work -e GOMODCACHE=/gomod -e GOCACHE=/gocache golang:1.25.1 go test ./model ./service
ok  	github.com/QuantumNous/new-api/model	5.210s
ok  	github.com/QuantumNous/new-api/service	0.327s

Additional check:

git merge-tree --write-tree upstream/main HEAD
# completed without merge conflicts

Summary by CodeRabbit

  • New Features

    • Added support for matching subscription plans to the requested group.
    • Expanded supported channel group values.
    • Added safeguards for reducing user and channel usage totals without going below zero.
  • Bug Fixes

    • Refunds now correctly restore usage totals and remain idempotent.
    • Prevented quota estimates from being applied when streaming ends abnormally without usage data.
    • Improved billing consistency during failed or recalculated quota operations.
  • Tests

    • Added coverage for group-based subscription selection, refunds, usage restoration, and abnormal stream handling.

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 52703a10-b95b-4f33-8aa1-6e8408c8fe02

📥 Commits

Reviewing files that changed from the base of the PR and between 4e57038 and b162ad1.

📒 Files selected for processing (11)
  • model/channel.go
  • model/subscription.go
  • model/subscription_preconsume_test.go
  • model/task_cas_test.go
  • model/user.go
  • service/billing_session.go
  • service/funding_source.go
  • service/task_billing.go
  • service/task_billing_test.go
  • service/text_quota.go
  • service/text_quota_test.go

Walkthrough

The changes add subscription plan group matching, make subscription refunds transaction-scoped, support clamped used-quota decrements for users and channels, correct task refund accounting, widen channel group storage, and skip estimated usage for abnormal streams without usage data.

Changes

Subscription billing

Layer / File(s) Summary
Group-aware subscription consumption and transactional refunds
model/subscription.go, model/subscription_preconsume_test.go, model/task_cas_test.go, service/billing_session.go, service/funding_source.go
Subscription funding passes request groups into plan selection, mismatched plans are skipped, and refund updates share one transaction with idempotency coverage.

Quota accounting

Layer / File(s) Summary
Clamped user and channel quota decrements
model/channel.go, model/user.go
Used-quota updates support positive increments and negative, floor-at-zero refunds; channel group storage expands to varchar(1024).
Task refund and recalculation integration
service/task_billing.go, service/task_billing_test.go
Task refunds and negative recalculations reduce user and channel used-quota totals, with persisted-balance assertions.

Text quota settlement

Layer / File(s) Summary
Abnormal-stream usage handling
service/text_quota.go, service/text_quota_test.go
Missing usage from abnormal streams skips token estimates and adds an explanatory billing reason, while normal streams retain estimation behavior.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Sequence Diagram(s)

sequenceDiagram
  participant TaskBilling
  participant UserModel
  participant ChannelModel
  participant Database
  TaskBilling->>UserModel: DecreaseUserUsedQuota(refund)
  UserModel->>Database: decrement used_quota with zero floor
  TaskBilling->>ChannelModel: DecreaseChannelUsedQuota(refund)
  ChannelModel->>Database: decrement used_quota with zero floor
Loading
sequenceDiagram
  participant Relay
  participant TextQuota
  participant Billing
  Relay->>TextQuota: provide stream status and missing usage
  TextQuota->>TextQuota: detect abnormal stream ending
  TextQuota->>Billing: settle without estimated usage
Loading

Poem

A rabbit hops through quotas bright,
Refunds now land just right.
Groups guide plans along the way,
Lost streams skip their guessed display.
“No double charge!” the bunny sings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cat0825

cat0825 commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

Closing this combined PR in favor of smaller one-issue PRs, as requested. I will reopen the changes as separate PRs for each linked issue.

@cat0825 cat0825 closed this Jul 10, 2026
@lidongpeng36

Copy link
Copy Markdown

Hi @cat0825, thanks for working on these fixes and for splitting the combined PR into focused changes. The channels.group expansion in this PR addresses the exact issue blocking our SQLite-to-MySQL migration: our channel group list contains full department names and exceeds varchar(64), while AutoMigrate changes a manually widened column back to 64. Are you planning to submit a separate PR for #6017 soon? If not, we would be happy to help prepare a focused PR for that change. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment