feat: add Airwallex payments and multi-currency support
This commit is contained in:
@@ -149,6 +149,12 @@
|
||||
<svg v-else class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" /></svg>
|
||||
</button>
|
||||
</div>
|
||||
<Select
|
||||
v-else-if="field.options?.length"
|
||||
v-model="config[field.key]"
|
||||
:options="field.options"
|
||||
:searchable="field.options.length > 5"
|
||||
/>
|
||||
<input
|
||||
v-else
|
||||
type="text"
|
||||
@@ -156,6 +162,9 @@
|
||||
class="input"
|
||||
:placeholder="field.defaultValue || ''"
|
||||
/>
|
||||
<p v-if="field.hintKey" class="mt-1 text-xs leading-relaxed text-gray-500 dark:text-gray-400">
|
||||
{{ t(field.hintKey) }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -177,14 +186,17 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Stripe webhook hint -->
|
||||
<div v-if="stripeWebhookUrl" class="mt-3 rounded-lg border border-blue-200 bg-blue-50 p-3 dark:border-blue-800/50 dark:bg-blue-900/20">
|
||||
<!-- 服务商 Webhook 提示 -->
|
||||
<div v-if="providerWebhookUrl" class="mt-3 rounded-lg border border-blue-200 bg-blue-50 p-3 dark:border-blue-800/50 dark:bg-blue-900/20">
|
||||
<p class="text-xs text-blue-700 dark:text-blue-300">
|
||||
{{ t('admin.settings.payment.stripeWebhookHint') }}
|
||||
{{ t(providerWebhookHint) }}
|
||||
</p>
|
||||
<code class="mt-1 block break-all rounded bg-blue-100 px-2 py-1 text-xs text-blue-800 dark:bg-blue-900/40 dark:text-blue-200">
|
||||
{{ stripeWebhookUrl }}
|
||||
{{ providerWebhookUrl }}
|
||||
</code>
|
||||
<p v-if="form.provider_key === 'stripe'" class="mt-2 text-xs leading-relaxed text-blue-700 dark:text-blue-300">
|
||||
{{ t('admin.settings.payment.stripeWebhookApiVersionHint', { version: STRIPE_SDK_API_VERSION }) }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -266,6 +278,7 @@ import {
|
||||
WEBHOOK_PATHS,
|
||||
PAYMENT_MODE_QRCODE,
|
||||
PAYMENT_MODE_POPUP,
|
||||
STRIPE_SDK_API_VERSION,
|
||||
getAvailableTypes,
|
||||
extractBaseUrl,
|
||||
} from './providerConfig'
|
||||
@@ -330,8 +343,18 @@ const visibleFields = reactive<Record<string, boolean>>({})
|
||||
// --- Computed ---
|
||||
const defaultBaseUrl = typeof window !== 'undefined' ? window.location.origin : ''
|
||||
|
||||
const stripeWebhookUrl = computed(() =>
|
||||
form.provider_key === 'stripe' ? defaultBaseUrl + WEBHOOK_PATHS.stripe : '',
|
||||
const providerWebhookHintMap: Record<string, string> = {
|
||||
stripe: 'admin.settings.payment.stripeWebhookHint',
|
||||
airwallex: 'admin.settings.payment.airwallexWebhookHint',
|
||||
}
|
||||
|
||||
const providerWebhookUrl = computed(() => {
|
||||
const path = WEBHOOK_PATHS[form.provider_key]
|
||||
return providerWebhookHintMap[form.provider_key] && path ? defaultBaseUrl + path : ''
|
||||
})
|
||||
|
||||
const providerWebhookHint = computed(() =>
|
||||
providerWebhookHintMap[form.provider_key] || 'admin.settings.payment.stripeWebhookHint',
|
||||
)
|
||||
|
||||
const callbackPaths = computed(() => PROVIDER_CALLBACK_PATHS[form.provider_key] || null)
|
||||
@@ -415,6 +438,14 @@ const paymentGuide = computed<PaymentGuide | null>(() => {
|
||||
}
|
||||
}
|
||||
|
||||
if (form.provider_key === 'airwallex') {
|
||||
return {
|
||||
summary: t('admin.settings.payment.airwallexGuideSummary'),
|
||||
note: t('admin.settings.payment.airwallexGuideNote'),
|
||||
items: [],
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
@@ -527,9 +558,19 @@ function handleSave() {
|
||||
}
|
||||
}
|
||||
|
||||
const clearableConfigKeys = new Set(
|
||||
(PROVIDER_CONFIG_FIELDS[form.provider_key] || [])
|
||||
.filter(field => field.clearable)
|
||||
.map(field => field.key),
|
||||
)
|
||||
const filteredConfig: Record<string, string> = {}
|
||||
for (const [k, v] of Object.entries(config)) {
|
||||
if (!v || !v.trim()) continue
|
||||
if (!v || !v.trim()) {
|
||||
if (clearableConfigKeys.has(k)) {
|
||||
filteredConfig[k] = ''
|
||||
}
|
||||
continue
|
||||
}
|
||||
filteredConfig[k] = v
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user