merge: P8 订阅/recurring + 拒付 chargeback 并入(订阅生命周期/续费/取消/past_due/chargeback/事件集收口)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u

# Conflicts:
#	internal/model/testdb.go
#	internal/provider/provider.go
#	internal/router/router.go
#	internal/store/order_query_test.go
#	main.go
This commit is contained in:
wangjia
2026-07-10 19:43:32 +08:00
32 changed files with 2826 additions and 52 deletions
+13
View File
@@ -27,6 +27,19 @@ func (s *OrderStore) GetOrder(outTradeNo string) (*model.OrderV2, error) {
return &o, nil
}
// MarkDisputed 打拒付标(P8 Task6):条件 UPDATE 仅在 disputed=false 时翻转,幂等——
// 重投同一 dispute 命中 rows_affected=0,不报错,调用方(recordChargeback)不看返回值
// 也安全(打标不改状态机,不存在"取消标记"这一操作,单向翻转足够)。
func (s *OrderStore) MarkDisputed(outTradeNo string) (bool, error) {
res := s.db.Model(&model.OrderV2{}).
Where("out_trade_no = ? AND disputed = ?", outTradeNo, false).
Update("disputed", true)
if res.Error != nil {
return false, fmt.Errorf("store.MarkDisputed: %w", res.Error)
}
return res.RowsAffected > 0, nil
}
// AttemptByProviderRef resolves an attempt from a bare provider_ref, so settlement
// can recover out_trade_no + channel from a callback/query that only carries the ref.
func (s *OrderStore) AttemptByProviderRef(providerRef string) (*model.Attempt, error) {