优化结构

This commit is contained in:
Yvan
2026-08-29 08:40:15 +08:00
parent b6d25c1a0f
commit 509df03d61
17 changed files with 234 additions and 40 deletions
+24 -9
View File
@@ -38,6 +38,7 @@ const (
PaymentModeExternal = "external"
PaymentModeInternal = "internal"
DemoBusinessType = "demo_subscription"
)
var SupportedPaymentProviders = []string{
@@ -537,6 +538,10 @@ func supportedPaymentProvider(provider string) bool {
return false
}
func IsDemoPaymentOrder(order *PaymentOrder) bool {
return order != nil && order.BusinessType == DemoBusinessType && strings.HasPrefix(strings.TrimSpace(order.TradeNo), "DEMO-PAY-")
}
func (uc *PaymentUsecase) Query(ctx context.Context, provider, tradeNo string) (*PaymentResult, error) {
if !validPaymentText(provider, 64) || !validPaymentText(tradeNo, 128) {
return nil, errors.New("查询支付参数不完整")
@@ -548,6 +553,9 @@ func (uc *PaymentUsecase) Query(ctx context.Context, provider, tradeNo string) (
if err != nil {
return nil, err
}
if IsDemoPaymentOrder(order) {
return paymentResultFromOrder(order), nil
}
if provider == PaymentInternal || order.PaymentMode == PaymentModeInternal {
return paymentResultFromOrder(order), nil
}
@@ -582,7 +590,14 @@ func (uc *PaymentUsecase) Refund(ctx context.Context, provider, tradeNo string,
if uc.orders == nil {
return nil, errors.New("支付订单仓储未接入")
}
return uc.refundWithOrder(ctx, provider, tradeNo, amount)
order, err := uc.orders.FindPaymentOrder(ctx, provider, tradeNo)
if err != nil {
return nil, err
}
if IsDemoPaymentOrder(order) {
return nil, errors.New("演示订单不执行真实退款")
}
return uc.refundWithOrder(ctx, order, amount)
}
// TestProvider exercises the configured provider without requiring a business
@@ -614,6 +629,9 @@ func (uc *PaymentUsecase) Fulfill(ctx context.Context, provider, tradeNo string)
if err != nil {
return nil, err
}
if IsDemoPaymentOrder(order) {
return nil, errors.New("演示订单不执行真实发货")
}
result := paymentResultFromOrder(order)
if result == nil {
return nil, ErrPaymentOrderNotFound
@@ -663,9 +681,6 @@ func (uc *PaymentUsecase) createWithOrder(ctx context.Context, req *PaymentReque
ConfirmationID: paymentConfirmationID(req.Provider, req.TradeNo),
RequestFingerprint: paymentOrderFingerprint(req, extra),
Extra: extra,
ClientIP: req.ClientIP,
UserAgent: req.UserAgent,
DeviceID: req.DeviceID,
}
order, created, err := uc.orders.CreatePaymentOrder(ctx, order)
if err != nil {
@@ -866,17 +881,17 @@ func (uc *PaymentUsecase) fulfillOrder(ctx context.Context, order *PaymentOrder,
return attachOrderResult(result, order), nil
}
func (uc *PaymentUsecase) refundWithOrder(ctx context.Context, provider, tradeNo string, amount int64) (*PaymentResult, error) {
current, err := uc.orders.FindPaymentOrder(ctx, provider, tradeNo)
if err != nil {
return nil, err
func (uc *PaymentUsecase) refundWithOrder(ctx context.Context, current *PaymentOrder, amount int64) (*PaymentResult, error) {
if current == nil {
return nil, ErrPaymentOrderNotFound
}
provider, tradeNo := current.Provider, current.TradeNo
handler := uc.fulfillments.Handler(current.BusinessType)
authorizer, ok := handler.(PaymentRefundAuthorizer)
if !ok {
return nil, fmt.Errorf("支付业务未实现退款授权: %s", current.BusinessType)
}
if err = authorizer.AuthorizeRefund(ctx, current, amount); err != nil {
if err := authorizer.AuthorizeRefund(ctx, current, amount); err != nil {
return nil, err
}
order, token, err := uc.orders.BeginPaymentRefund(ctx, provider, tradeNo, amount, 10*time.Minute)
-3
View File
@@ -88,9 +88,6 @@ type PaymentOrder struct {
RefundedAt *time.Time
FulfillmentLeaseUntil *time.Time
RefundLeaseUntil *time.Time
ClientIP string
UserAgent string
DeviceID string
}
type PaymentProviderUpdate struct {
+27
View File
@@ -157,6 +157,33 @@ func testBizPaymentOrder() *PaymentOrder {
return &PaymentOrder{Provider: PaymentAlipay, TradeNo: "order-1", BusinessType: "game_item", BusinessID: "item-1", Subject: "item", Amount: 100, Currency: "CNY", PaymentStatus: PaymentStatusPending, FulfillmentStatus: FulfillmentStatusPending, RefundStatus: RefundStatusNone, ConfirmationID: "11111111-1111-1111-1111-111111111111"}
}
func TestDemoPaymentOperationsNeverReachExternalProviderOrFulfillment(t *testing.T) {
repo := &paymentRepoStub{query: successPaymentResult(), refund: successPaymentResult()}
order := testBizPaymentOrder()
order.TradeNo = "DEMO-PAY-01"
order.BusinessType = DemoBusinessType
order.PaymentStatus = PaymentStatusPaid
orders := &paymentOrderRepoStub{order: order}
registry := NewPaymentFulfillmentRegistry()
handler := &paymentFulfillmentStub{}
if err := registry.Register(handler); err != nil {
t.Fatal(err)
}
logger := slog.New(slog.NewTextHandler(io.Discard, nil))
uc := NewPaymentUsecase(repo, orders, nil, nil, registry, logger)
result, err := uc.Query(context.Background(), order.Provider, order.TradeNo)
if err != nil || result == nil || repo.queryCalls != 0 {
t.Fatalf("demo query result=%#v calls=%d err=%v", result, repo.queryCalls, err)
}
if _, err = uc.Refund(context.Background(), order.Provider, order.TradeNo, 10); err == nil || repo.refundedReq != nil {
t.Fatalf("demo refund reached provider: req=%#v err=%v", repo.refundedReq, err)
}
if _, err = uc.Fulfill(context.Background(), order.Provider, order.TradeNo); err == nil || handler.calls != 0 {
t.Fatalf("demo fulfillment reached handler: calls=%d err=%v", handler.calls, err)
}
}
func successPaymentResult() *PaymentResult {
return &PaymentResult{
Provider: PaymentAlipay,