feat(pay-v2): P8 Task3 订阅创建 + 首期激活(subscription.created)+ 端点

This commit is contained in:
wangjia
2026-07-10 17:59:35 +08:00
parent 83e3551609
commit 4c242607a7
12 changed files with 411 additions and 11 deletions
+41 -2
View File
@@ -3,6 +3,7 @@ package gateway
import (
"context"
"errors"
"fmt"
"log"
"time"
@@ -96,7 +97,9 @@ func (g *Gateway) enqueuePaymentSucceeded(att *model.Attempt, paidAt time.Time)
}
// HandleCallback runs a channel's raw callback through its Provider.VerifyCallback
// (验签/解析封死在渠道内) then settles the normalized event.
// (验签/解析封死在渠道内) then dispatches the normalized event by Kind(设计 §5):
// 一次性/首期支付走既有 Settle(零 fork);订阅续费/催收/取消/拒付各有专属处理器
// (Task 4/5/6,本 Task 先占 stub 保证独立可编译)。
func (g *Gateway) HandleCallback(ctx context.Context, method string, in provider.CallbackInput) (SettleResult, error) {
prov, err := g.providers.Get(method)
if err != nil {
@@ -106,7 +109,43 @@ func (g *Gateway) HandleCallback(ctx context.Context, method string, in provider
if err != nil {
return SettleNotFound, err
}
return g.Settle(ctx, ev)
switch ev.Kind {
case provider.EventSubscriptionRenewal:
return g.settleRenewal(ctx, ev) // Task 4
case provider.EventSubscriptionPastDue:
return g.markSubscriptionPastDue(ctx, method, ev) // Task 5
case provider.EventSubscriptionCanceled:
return g.settleSubscriptionCanceled(ctx, ev) // Task 5
case provider.EventChargeback:
return g.recordChargeback(ctx, method, ev) // Task 6
default: // EventPayment:一次性 / 订阅首期
res, serr := g.Settle(ctx, ev)
if serr == nil && ev.SubscriptionRef != "" {
if aerr := g.onSubscriptionActivated(ctx, ev); aerr != nil {
return SettleFailed, aerr // 诞生订阅失败可重试(Stripe 重投)
}
}
return res, serr
}
}
// --- Task 4/5/6 处理器占位(本 Task 只需 default 分支可用 + onSubscriptionActivated)。
// 保持本 Task 独立可编译;后续 Task 各自替换实现 + 补测试。
func (g *Gateway) settleRenewal(ctx context.Context, ev *provider.PaidEvent) (SettleResult, error) {
return SettleFailed, fmt.Errorf("not implemented: %s", ev.Kind)
}
func (g *Gateway) markSubscriptionPastDue(ctx context.Context, method string, ev *provider.PaidEvent) (SettleResult, error) {
return SettleFailed, fmt.Errorf("not implemented: %s", ev.Kind)
}
func (g *Gateway) settleSubscriptionCanceled(ctx context.Context, ev *provider.PaidEvent) (SettleResult, error) {
return SettleFailed, fmt.Errorf("not implemented: %s", ev.Kind)
}
func (g *Gateway) recordChargeback(ctx context.Context, method string, ev *provider.PaidEvent) (SettleResult, error) {
return SettleFailed, fmt.Errorf("not implemented: %s", ev.Kind)
}
// SyncPendingAttempts polls every pending attempt via its Provider.Query and