Skip to content

fix: keep subscription pre-consume refund atomic (#5556) - #6089

Open
cat0825 wants to merge 1 commit into
QuantumNous:mainfrom
cat0825:codex/fix-5556-subscription-refund-tx
Open

fix: keep subscription pre-consume refund atomic (#5556)#6089
cat0825 wants to merge 1 commit into
QuantumNous:mainfrom
cat0825:codex/fix-5556-subscription-refund-tx

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。原先在事务中调用 PostConsumeUserSubscriptionDelta 会新开事务,可能出现额度已退回但记录状态未更新,导致重试重复退款。

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 -w /work -e GOMODCACHE=/gomod -e GOCACHE=/gocache golang:1.25.1 go test ./model -run 'TestRefundSubscriptionPreConsume|TestPreConsume|TestCAS|TestUserUpdate'
ok  	github.com/QuantumNous/new-api/model	0.027s

Summary by CodeRabbit

  • Bug Fixes

    • Subscription refunds now update consumed quota within the same database transaction as the refund-preconsumption status.
    • Refund pre-consumption is idempotent for the same request, preventing duplicate quota adjustments.
    • Quota calculations are clamped to avoid negative consumed amounts and to respect the subscription’s total limit.
  • Tests

    • Added integration-style test covering refund pre-consumption behavior, idempotency, and refund record status updates.
    • Updated test database setup and cleanup to include subscription pre-consumption records.

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 49621e45-5788-4afa-85b8-6840f493a3ec

📥 Commits

Reviewing files that changed from the base of the PR and between 1a48572 and 26ed8ca.

📒 Files selected for processing (3)
  • model/subscription.go
  • model/subscription_refund_test.go
  • model/task_cas_test.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • model/subscription_refund_test.go
  • model/task_cas_test.go
  • model/subscription.go

Walkthrough

Subscription refunds now reverse quota usage through the same database transaction that updates the pre-consume record. A shared helper handles locking and validation, while integration tests verify idempotent reversal and refunded record state.

Changes

Subscription refund consistency

Layer / File(s) Summary
Transactional refund delta updates
model/subscription.go
Refund processing applies quota deltas through a transaction-scoped helper that locks the subscription row, clamps usage, enforces the total limit, and updates the refund record in the same transaction.
Refund idempotency test coverage
model/subscription_refund_test.go, model/task_cas_test.go
Tests verify pre-consumption, repeated refunds, refunded record status, and migration and cleanup of pre-consume records.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant BillingSession
  participant RefundSubscriptionPreConsume
  participant UserSubscription
  participant SubscriptionPreConsumeRecord
  BillingSession->>RefundSubscriptionPreConsume: refund request
  RefundSubscriptionPreConsume->>UserSubscription: lock and decrement AmountUsed
  RefundSubscriptionPreConsume->>SubscriptionPreConsumeRecord: mark record refunded
  RefundSubscriptionPreConsume-->>BillingSession: commit transaction
Loading

Possibly related PRs

Poem

I’m a bunny with a ledger neat,
Refunds now hop on one transaction street.
Quota steps back just once,
Status follows without a chance
For duplicate hops to cheat!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately describes the atomic subscription refund fix.
Linked Issues check ✅ Passed The changes make refunding pre-consume usage transactional and add regression coverage for idempotent refunded status.
Out of Scope Changes check ✅ Passed The added tests and migration cleanup directly support the refund atomicity fix and do not introduce unrelated behavior.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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 11, 2026

Copy link
Copy Markdown
Contributor Author

@Calcium-Ion @t0ng7u 麻烦有空时 review 一下这个 PR。

范围:仅 #5556 — 订阅预扣退款在同一事务内回退 amount_used 并标记 pre-consume 记录,避免重复退款。
本地已补回归测试;CodeRabbit 无 actionable 问题。谢谢。

@cat0825
cat0825 force-pushed the codex/fix-5556-subscription-refund-tx branch from 1a48572 to 26ed8ca Compare July 20, 2026 06:29
@cat0825

cat0825 commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto latest main to resolve the merge conflict.

Same conflict surface as #6090: model/task_cas_test.go test cleanup. Kept DELETE FROM subscription_pre_consume_records and removed the duplicate oauth cleanup line.

Local verification:

  • go test ./model -run 'TestRefund|SubscriptionRefund|RefundSubscription' -count=1 → ok

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

退款链路,改金额与改状态的数据库事务不一致会导致重复退款问题

1 participant