From 34bd16ea3cbd12a0eb23cc33a0228825c87d5e39 Mon Sep 17 00:00:00 2001 From: erio Date: Mon, 6 Apr 2026 23:02:34 +0800 Subject: [PATCH] =?UTF-8?q?fix(payment):=20strict=20validation=20=E2=80=94?= =?UTF-8?q?=20all=20required=20fields=20must=20be=20filled=20on=20edit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Edit mode: sensitive fields show ••••••••, must remain filled to save - Non-sensitive fields pre-filled from backend on edit - Empty defaults (notifyUrl/returnUrl) auto-filled if backend has no value - Submit filters out •••••••• to keep existing backend credentials - All required fields validated regardless of create/edit mode --- .../payment/PaymentProviderDialog.vue | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/frontend/src/components/payment/PaymentProviderDialog.vue b/frontend/src/components/payment/PaymentProviderDialog.vue index c7446a3f6e..2dd2e0d162 100644 --- a/frontend/src/components/payment/PaymentProviderDialog.vue +++ b/frontend/src/components/payment/PaymentProviderDialog.vue @@ -298,20 +298,23 @@ function handleSave() { emitValidationError(t('admin.settings.payment.validationTypesRequired')) return } - // Validate required config on create - if (!props.editing) { - for (const f of PROVIDER_CONFIG_FIELDS[form.provider_key] || []) { - if (!f.optional && !(config[f.key] || '').trim()) { - const label = f.label || t(`admin.settings.payment.field_${f.key}`) - emitValidationError(t('admin.settings.payment.validationFieldRequired', { field: label })) - return - } + // Validate required config fields — all non-optional fields must be filled + for (const f of PROVIDER_CONFIG_FIELDS[form.provider_key] || []) { + if (f.optional) continue + const val = (config[f.key] || '').trim() + if (!val) { + const label = f.label || t(`admin.settings.payment.field_${f.key}`) + emitValidationError(t('admin.settings.payment.validationFieldRequired', { field: label })) + return } } const filteredConfig: Record = {} for (const [k, v] of Object.entries(config)) { - if (v && v.trim()) filteredConfig[k] = v + if (!v || !v.trim()) continue + // Skip masked values — backend keeps existing credentials + if (v === '••••••••') continue + filteredConfig[k] = v } emit('save', { @@ -349,15 +352,14 @@ function loadProvider(provider: ProviderInstance) { form.enabled = provider.enabled form.refund_enabled = provider.refund_enabled clearConfig() - // Pre-fill config from API response (non-sensitive in cleartext, sensitive masked) + // Pre-fill config from API response (non-sensitive in cleartext, sensitive masked as ••••••••) if (provider.config) { for (const [k, v] of Object.entries(provider.config)) { - // Skip masked sensitive values — leave field empty so user can re-enter - if (v !== '••••••••') { - config[k] = v - } + config[k] = v } } + // Apply defaults for any empty fields with defaultValue + applyDefaults() // Parse existing limits if (provider.limits) { try {