diff --git a/backend/internal/payment/crypto.go b/backend/internal/payment/crypto.go index 5467e50b..0581469d 100644 --- a/backend/internal/payment/crypto.go +++ b/backend/internal/payment/crypto.go @@ -16,6 +16,11 @@ const AES256KeySize = 32 // Encrypt encrypts plaintext using AES-256-GCM with the given 32-byte key. // The output format is "iv:authTag:ciphertext" where each component is base64-encoded, // matching the Node.js crypto.ts format for cross-compatibility. +// +// Deprecated: payment provider configs are now stored as plaintext JSON. +// This function is kept only for seeding legacy ciphertext in tests and for +// the transitional Decrypt fallback. Scheduled for removal after all live +// deployments complete migration by re-saving their configs. func Encrypt(plaintext string, key []byte) (string, error) { if len(key) != AES256KeySize { return "", fmt.Errorf("encryption key must be %d bytes, got %d", AES256KeySize, len(key)) @@ -54,6 +59,11 @@ func Encrypt(plaintext string, key []byte) (string, error) { // Decrypt decrypts a ciphertext string produced by Encrypt. // The input format is "iv:authTag:ciphertext" where each component is base64-encoded. +// +// Deprecated: payment provider configs are now stored as plaintext JSON. +// This function remains only as a read-path fallback for pre-migration +// ciphertext records. Scheduled for removal once all deployments re-save +// their provider configs through the admin UI. func Decrypt(ciphertext string, key []byte) (string, error) { if len(key) != AES256KeySize { return "", fmt.Errorf("encryption key must be %d bytes, got %d", AES256KeySize, len(key)) diff --git a/backend/internal/payment/load_balancer.go b/backend/internal/payment/load_balancer.go index 52a1b011..ec244cd6 100644 --- a/backend/internal/payment/load_balancer.go +++ b/backend/internal/payment/load_balancer.go @@ -283,6 +283,11 @@ func (lb *DefaultLoadBalancer) buildSelection(selected *dbent.PaymentProviderIns // Unreadable values (legacy ciphertext without a valid key, or malformed data) // are treated as empty so the service keeps running while the admin re-enters // the config via the UI. +// +// TODO(deprecated-legacy-ciphertext): The AES fallback branch below is a +// transitional compatibility shim for pre-plaintext records. Remove it (and +// the encryptionKey field + the Decrypt import) after a few releases once all +// live deployments have re-saved their provider configs through the UI. func (lb *DefaultLoadBalancer) decryptConfig(stored string) (map[string]string, error) { if stored == "" { return nil, nil @@ -291,7 +296,9 @@ func (lb *DefaultLoadBalancer) decryptConfig(stored string) (map[string]string, if err := json.Unmarshal([]byte(stored), &config); err == nil { return config, nil } + // Deprecated: legacy AES-256-GCM ciphertext fallback — scheduled for removal. if len(lb.encryptionKey) == AES256KeySize { + //nolint:staticcheck // SA1019: intentional legacy fallback, scheduled for removal if plaintext, err := Decrypt(stored, lb.encryptionKey); err == nil { if err := json.Unmarshal([]byte(plaintext), &config); err == nil { return config, nil diff --git a/backend/internal/service/payment_config_providers.go b/backend/internal/service/payment_config_providers.go index 59337ad6..8e470525 100644 --- a/backend/internal/service/payment_config_providers.go +++ b/backend/internal/service/payment_config_providers.go @@ -296,6 +296,10 @@ func (s *PaymentConfigService) mergeConfig(ctx context.Context, id int64, newCon // ("iv:authTag:ciphertext"). Values that cannot be parsed as either — including // legacy ciphertext with no/invalid TOTP_ENCRYPTION_KEY — are treated as empty, // letting the admin re-enter the config via the UI to complete the migration. +// +// TODO(deprecated-legacy-ciphertext): The AES fallback branch is a transitional +// shim for pre-plaintext records. Remove it (and the encryptionKey field) after +// a few releases once all live deployments have re-saved their provider configs. func (s *PaymentConfigService) decryptConfig(stored string) (map[string]string, error) { if stored == "" { return nil, nil @@ -304,7 +308,9 @@ func (s *PaymentConfigService) decryptConfig(stored string) (map[string]string, if err := json.Unmarshal([]byte(stored), &cfg); err == nil { return cfg, nil } + // Deprecated: legacy AES-256-GCM ciphertext fallback — scheduled for removal. if len(s.encryptionKey) == payment.AES256KeySize { + //nolint:staticcheck // SA1019: intentional legacy fallback, scheduled for removal if plaintext, err := payment.Decrypt(stored, s.encryptionKey); err == nil { if err := json.Unmarshal([]byte(plaintext), &cfg); err == nil { return cfg, nil