diff --git a/frontend/src/i18n/locales/en.ts b/frontend/src/i18n/locales/en.ts
index 24ce8bc271..92e45a1524 100644
--- a/frontend/src/i18n/locales/en.ts
+++ b/frontend/src/i18n/locales/en.ts
@@ -4356,6 +4356,18 @@ export default {
providerStripe: 'Stripe',
typeDisabled: 'type disabled',
enableTypesFirst: 'Enable at least one payment type above first',
+ field_apiBase: 'API Base URL',
+ field_notifyUrl: 'Notify URL',
+ field_returnUrl: 'Return URL',
+ field_privateKey: 'Private Key',
+ field_publicKey: 'Public Key',
+ field_mchId: 'Merchant ID',
+ field_apiV3Key: 'API v3 Key',
+ field_publicKeyId: 'Public Key ID',
+ field_certSerial: 'Certificate Serial',
+ field_secretKey: 'Secret Key',
+ field_publishableKey: 'Publishable Key',
+ field_webhookSecret: 'Webhook Secret',
productNamePrefix: 'Product Name Prefix',
productNameSuffix: 'Product Name Suffix',
preview: 'Preview',
diff --git a/frontend/src/i18n/locales/zh.ts b/frontend/src/i18n/locales/zh.ts
index 23fae5b2b0..01c36e9376 100644
--- a/frontend/src/i18n/locales/zh.ts
+++ b/frontend/src/i18n/locales/zh.ts
@@ -4529,6 +4529,18 @@ export default {
providerStripe: 'Stripe',
typeDisabled: '类型已禁用',
enableTypesFirst: '请先在上方启用至少一种支付方式',
+ field_apiBase: 'API 基础地址',
+ field_notifyUrl: '异步通知地址',
+ field_returnUrl: '同步跳转地址',
+ field_privateKey: '私钥',
+ field_publicKey: '公钥',
+ field_mchId: '商户号',
+ field_apiV3Key: 'API v3 密钥',
+ field_publicKeyId: '公钥 ID',
+ field_certSerial: '证书序列号',
+ field_secretKey: '密钥',
+ field_publishableKey: '公开密钥',
+ field_webhookSecret: 'Webhook 密钥',
productNamePrefix: '商品名前缀',
productNameSuffix: '商品名后缀',
preview: '预览',
diff --git a/frontend/src/views/admin/SettingsView.vue b/frontend/src/views/admin/SettingsView.vue
index b01ebae585..945d0f71db 100644
--- a/frontend/src/views/admin/SettingsView.vue
+++ b/frontend/src/views/admin/SettingsView.vue
@@ -1789,34 +1789,17 @@
+
@@ -2955,12 +2938,53 @@ const deletingProviderId = ref(null)
const providerForm = reactive({
name: '', provider_key: 'easypay', supported_types: '', enabled: true, refund_enabled: false,
})
-const providerConfig = reactive({
- pid: '', pkey: '', apiBase: '', notifyUrl: '', returnUrl: '',
- cid: '', cidAlipay: '', cidWxpay: '',
- appId: '', privateKey: '', publicKey: '',
- mchId: '', apiV3Key: '', publicKeyId: '', certSerial: '',
- secretKey: '', publishableKey: '', webhookSecret: '',
+const providerConfig = reactive>({})
+
+interface ConfigFieldDef {
+ key: string
+ label: string
+ sensitive: boolean
+ optional?: boolean
+}
+
+const PROVIDER_CONFIG_FIELDS: Record = {
+ easypay: [
+ { key: 'pid', label: 'PID', sensitive: false },
+ { key: 'pkey', label: 'PKey', sensitive: true },
+ { key: 'apiBase', label: '', sensitive: false, optional: true },
+ { key: 'notifyUrl', label: '', sensitive: false, optional: true },
+ { key: 'returnUrl', label: '', sensitive: false, optional: true },
+ ],
+ alipay: [
+ { key: 'appId', label: 'App ID', sensitive: false },
+ { key: 'privateKey', label: '', sensitive: true },
+ { key: 'publicKey', label: '', sensitive: true },
+ { key: 'notifyUrl', label: '', sensitive: false, optional: true },
+ { key: 'returnUrl', label: '', sensitive: false, optional: true },
+ ],
+ wxpay: [
+ { key: 'appId', label: 'App ID', sensitive: false },
+ { key: 'mchId', label: '', sensitive: false },
+ { key: 'privateKey', label: '', sensitive: true },
+ { key: 'apiV3Key', label: '', sensitive: true },
+ { key: 'publicKey', label: '', sensitive: true },
+ { key: 'publicKeyId', label: '', sensitive: false },
+ { key: 'certSerial', label: '', sensitive: false },
+ { key: 'notifyUrl', label: '', sensitive: false, optional: true },
+ ],
+ stripe: [
+ { key: 'secretKey', label: '', sensitive: true },
+ { key: 'publishableKey', label: '', sensitive: false },
+ { key: 'webhookSecret', label: '', sensitive: true },
+ ],
+}
+
+const currentProviderFields = computed(() => {
+ const fields = PROVIDER_CONFIG_FIELDS[providerForm.provider_key] || []
+ return fields.map(f => ({
+ ...f,
+ label: f.label || t(`admin.settings.payment.field_${f.key}`),
+ }))
})
const providerKeyOptions = computed(() => [
{ value: 'easypay', label: t('admin.settings.payment.providerEasypay') },
@@ -3030,7 +3054,8 @@ function resetProviderForm() {
providerForm.name = ''; providerForm.provider_key = defaultKey
providerForm.supported_types = (PROVIDER_SUPPORTED_TYPES[defaultKey] || []).join(',')
providerForm.enabled = true; providerForm.refund_enabled = false
- Object.keys(providerConfig).forEach(k => { (providerConfig as any)[k] = '' })
+ // Clear all config keys
+ Object.keys(providerConfig).forEach(k => delete providerConfig[k])
}
function openCreateProvider() { editingProvider.value = null; resetProviderForm(); showProviderDialog.value = true }
function openEditProvider(provider: ProviderInstance) {
@@ -3038,15 +3063,24 @@ function openEditProvider(provider: ProviderInstance) {
providerForm.name = provider.name; providerForm.provider_key = provider.provider_key
providerForm.supported_types = provider.supported_types; providerForm.enabled = provider.enabled
providerForm.refund_enabled = provider.refund_enabled
- Object.keys(providerConfig).forEach(k => { (providerConfig as any)[k] = '' })
+ // Clear and don't pre-fill config (sensitive fields, same as sub2apipay)
+ Object.keys(providerConfig).forEach(k => delete providerConfig[k])
showProviderDialog.value = true
}
async function handleSaveProvider() {
providerSaving.value = true
try {
- const data = { ...providerForm, config: { ...providerConfig } } as any
- if (editingProvider.value) { await adminAPI.payment.updateProvider(editingProvider.value.id, data) }
- else { await adminAPI.payment.createProvider(data) }
+ // Build camelCase payload matching backend struct
+ const payload: any = {
+ providerKey: providerForm.provider_key,
+ name: providerForm.name,
+ supportedTypes: providerForm.supported_types,
+ enabled: providerForm.enabled,
+ refundEnabled: providerForm.refund_enabled,
+ config: { ...providerConfig },
+ }
+ if (editingProvider.value) { await adminAPI.payment.updateProvider(editingProvider.value.id, payload) }
+ else { await adminAPI.payment.createProvider(payload) }
appStore.showSuccess(t('common.saved')); showProviderDialog.value = false; loadProviders()
} catch (err: unknown) { appStore.showError(extractApiErrorMessage(err, t('common.error'))) }
finally { providerSaving.value = false }