feat(v2): gateway.Refund 编排(部分/多次累计 + 超退守卫 + crypto 人工向闭环)+ RefundingProvider 加 refundID 幂等键

This commit is contained in:
wangjia
2026-07-10 17:23:14 +08:00
parent aa14204640
commit 2016c5d16e
10 changed files with 367 additions and 14 deletions
+26 -1
View File
@@ -20,6 +20,11 @@ import (
type Provider struct {
mu sync.Mutex
queryResults map[string]provider.PaidEvent
supportsRefund bool
refundRef string
refundStatus provider.PaidStatus
refundErr error
}
func New() *Provider { return &Provider{queryResults: map[string]provider.PaidEvent{}} }
@@ -29,7 +34,7 @@ func (p *Provider) Method() string { return "fake" }
func (p *Provider) Capabilities() provider.Capabilities {
return provider.Capabilities{
RenderTypes: []provider.RenderType{provider.RenderCryptoAddress},
SupportsRefund: false,
SupportsRefund: p.supportsRefund,
SettleCurrencies: []string{"USDT"},
Regions: []string{"global"},
}
@@ -89,3 +94,23 @@ func (p *Provider) SetQueryResult(providerRef string, ev provider.PaidEvent) {
defer p.mu.Unlock()
p.queryResults[providerRef] = ev
}
// EnableRefund 令 fake 表现为可退渠道并预置一次退款结果(测试缝)。
func (p *Provider) EnableRefund(refundRef string, status provider.PaidStatus, err error) {
p.mu.Lock()
defer p.mu.Unlock()
p.supportsRefund = true
p.refundRef = refundRef
p.refundStatus = status
p.refundErr = err
}
// Refund implements provider.RefundingProvider (测试缝:忽略入参,回放预置结果)。
func (p *Provider) Refund(_ context.Context, _, _ string, _ int64, _ string) (string, provider.PaidStatus, error) {
p.mu.Lock()
defer p.mu.Unlock()
if p.refundErr != nil {
return "", provider.PaidFailed, p.refundErr
}
return p.refundRef, p.refundStatus, nil
}
+4 -2
View File
@@ -109,10 +109,12 @@ type Provider interface {
Query(ctx context.Context, req QueryRequest) (*PaidEvent, error)
}
// RefundingProvider — 可选:支持渠道退款的 Provider 额外实现(P4;不支持则 capabilities=false)。
// RefundingProvider — 可选:支持渠道退款的 Provider 额外实现(P4)。refundID 为 pay 侧
// 退款单号,作渠道幂等键(alipay out_request_no / stripe Idempotency-Key)——同单多次
// 部分退款靠它去重,重试不重复退。不支持退款的渠道不实现本接口(capabilities=false)。
type RefundingProvider interface {
Provider
Refund(ctx context.Context, providerRef string, amountMinor int64, reason string) (refundRef string, status PaidStatus, err error)
Refund(ctx context.Context, providerRef, refundID string, amountMinor int64, reason string) (refundRef string, status PaidStatus, err error)
}
// RecurringProvider — 可选:支持自动续订(P8,设计 §5.1 4 类 kind)。