feat(pay-v2): P8 Task1 Subscription 模型 + 三态状态机 + store

This commit is contained in:
wangjia
2026-07-10 17:45:10 +08:00
parent 346004adf3
commit 799d74668d
5 changed files with 184 additions and 1 deletions
+1 -1
View File
@@ -32,7 +32,7 @@ func OpenTestDB(t *testing.T) *gorm.DB {
t.Fatalf("open test db: %v", err)
}
if err := db.AutoMigrate(&OrderV2{}, &Attempt{}, &Account{}, &Refund{}, &WebhookDelivery{},
&Product{}, &ProductPrice{}); err != nil {
&Product{}, &ProductPrice{}, &Subscription{}); err != nil {
t.Fatalf("migrate: %v", err)
}
if err := UpgradeWebhookDeliveryIndex(db); err != nil {
+30
View File
@@ -121,3 +121,33 @@ type Refund struct {
InitiatedBy string `gorm:"size:16"` // business/platform
CompletedAt *time.Time
}
// ---- 订阅(recurring)----
type SubStatus string
const (
SubActive SubStatus = "active" // 已激活,正常续费
SubPastDue SubStatus = "past_due" // 某期扣款失败,待恢复(invoice.payment_failed)
SubCanceled SubStatus = "canceled" // 已取消(主动/网关删除),终态
)
// Subscription 是"同一 entitlement 的跨期账本"(设计 §5.1)。单笔层(OrderV2/Attempt)
// 与订阅层解耦:每期扣款仍落一张 renewal OrderV2,Subscription 只维护状态机 + 续费锚点。
type Subscription struct {
Base
SubID string `gorm:"uniqueIndex;size:64;not null"` // pay 生成的逻辑订阅号
OutTradeNo string `gorm:"index;size:64;not null"` // 首购 order(诞生订阅那笔)
MerchantID uint64 `gorm:"index"`
BizSystem string `gorm:"index;size:32"`
BizRef string `gorm:"size:128"`
BizCode string `gorm:"index;size:64"` // 套餐码副本,续费事件带回
Channel string `gorm:"index;size:32;not null;uniqueIndex:uq_sub_provider"`
ProviderSubRef string `gorm:"size:128;not null;uniqueIndex:uq_sub_provider"` // 渠道订阅号(stripe sub id)
RecurringKind string `gorm:"size:24"` // gateway_scheduled/token_offsession/...
AmountMinor int64 `gorm:"not null"`
Currency string `gorm:"size:16;not null"`
Status SubStatus `gorm:"index;size:16;not null"`
CurrentPeriodEnd *time.Time
CanceledAt *time.Time
}