fix(payment): support source routing and compatible resume signing

This commit is contained in:
IanShaw027
2026-04-22 12:30:17 +08:00
parent b2e0712190
commit d6a04bb772
12 changed files with 570 additions and 136 deletions

View File

@@ -1,10 +1,14 @@
package service
import (
"bytes"
"context"
"encoding/hex"
"fmt"
"log/slog"
"math/rand/v2"
"os"
"strings"
"sync"
"time"
@@ -44,6 +48,8 @@ const (
orderIDPrefix = "sub2_"
)
const paymentResumeSigningKeyEnv = "PAYMENT_RESUME_SIGNING_KEY"
// --- Types ---
// generateOutTradeNo creates a unique external order ID for payment providers.
@@ -179,7 +185,7 @@ type PaymentService struct {
func NewPaymentService(entClient *dbent.Client, registry *payment.Registry, loadBalancer payment.LoadBalancer, redeemService *RedeemService, subscriptionSvc *SubscriptionService, configService *PaymentConfigService, userRepo UserRepository, groupRepo GroupRepository) *PaymentService {
svc := &PaymentService{entClient: entClient, registry: registry, loadBalancer: newVisibleMethodLoadBalancer(loadBalancer, configService), redeemService: redeemService, subscriptionSvc: subscriptionSvc, configService: configService, userRepo: userRepo, groupRepo: groupRepo}
svc.resumeService = NewPaymentResumeService(psResumeSigningKey(configService))
svc.resumeService = psNewPaymentResumeService(configService)
return svc
}
@@ -259,16 +265,54 @@ func (s *PaymentService) paymentResume() *PaymentResumeService {
if s.resumeService != nil {
return s.resumeService
}
return NewPaymentResumeService(psResumeSigningKey(s.configService))
return psNewPaymentResumeService(s.configService)
}
func psNewPaymentResumeService(configService *PaymentConfigService) *PaymentResumeService {
signingKey, verifyFallbacks := psResumeSigningKeys(configService)
return NewPaymentResumeService(signingKey, verifyFallbacks...)
}
func psResumeSigningKey(configService *PaymentConfigService) []byte {
signingKey, _ := psResumeSigningKeys(configService)
return signingKey
}
func psResumeSigningKeys(configService *PaymentConfigService) ([]byte, [][]byte) {
signingKey := parsePaymentResumeSigningKey(os.Getenv(paymentResumeSigningKeyEnv))
legacyKey := psResumeLegacyVerificationKey(configService)
if len(signingKey) == 0 {
if len(legacyKey) == 0 {
return nil, nil
}
return legacyKey, nil
}
if len(legacyKey) == 0 || bytes.Equal(legacyKey, signingKey) {
return signingKey, nil
}
return signingKey, [][]byte{legacyKey}
}
func psResumeLegacyVerificationKey(configService *PaymentConfigService) []byte {
if configService == nil {
return nil
}
return configService.encryptionKey
}
func parsePaymentResumeSigningKey(raw string) []byte {
raw = strings.TrimSpace(raw)
if raw == "" {
return nil
}
if len(raw) >= 64 && len(raw)%2 == 0 {
if decoded, err := hex.DecodeString(raw); err == nil && len(decoded) > 0 {
return decoded
}
}
return []byte(raw)
}
func psSliceContains(sl []string, s string) bool {
for _, v := range sl {
if v == s {