101 lines
3.2 KiB
Vue
101 lines
3.2 KiB
Vue
<script setup lang="ts">
|
|
import { NInput } from 'naive-ui'
|
|
import { computed, ref, watch } from 'vue'
|
|
|
|
import { payInvoice } from '@/api/billing'
|
|
import FormModal from '@/components/FormModal.vue'
|
|
import { useToast } from '@/composables/useToast'
|
|
import { invoiceDate } from '@/components/tenant/invoiceStatus'
|
|
import { fmtMoney } from '@/composables/useFormat'
|
|
import type { Invoice, PaymentMethod } from '@/types/api'
|
|
|
|
/** 付款确认:经 OSP Gateway PayInvoice 按订阅默认付款方式即时扣款;
|
|
* email 是 API 必填项(接收付款回执),在此向用户收集。 */
|
|
const props = defineProps<{
|
|
show: boolean
|
|
cfgId: number
|
|
invoice: Invoice | null
|
|
methods: PaymentMethod[] | null
|
|
}>()
|
|
const emit = defineEmits<{ 'update:show': [boolean]; paid: [] }>()
|
|
const message = useToast()
|
|
|
|
const email = ref('')
|
|
const paying = ref(false)
|
|
const emailOk = computed(() => /.+@.+\..+/.test(email.value.trim()))
|
|
|
|
/** 扣款方式展示:优先第一张信用卡,其次任一登记方式 */
|
|
const payVia = computed(() => {
|
|
const ms = props.methods ?? []
|
|
const m = ms.find((x) => x.method === 'CREDIT_CARD') ?? ms[0]
|
|
if (!m) return '订阅默认付款方式'
|
|
if (m.method === 'CREDIT_CARD') return `${m.cardType || '银行卡'} •••• ${m.lastDigits}`
|
|
return `PayPal(${m.email})`
|
|
})
|
|
|
|
watch(
|
|
() => props.show,
|
|
(v) => {
|
|
if (v) paying.value = false
|
|
},
|
|
)
|
|
|
|
async function doPay() {
|
|
const inv = props.invoice
|
|
if (!inv || !emailOk.value) return
|
|
paying.value = true
|
|
try {
|
|
await payInvoice(props.cfgId, inv.internalId, email.value.trim())
|
|
message.success('付款已提交,发票状态稍后更新')
|
|
emit('update:show', false)
|
|
emit('paid')
|
|
} catch (e) {
|
|
message.error(e instanceof Error ? e.message : '付款失败')
|
|
} finally {
|
|
paying.value = false
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<FormModal
|
|
:show="show"
|
|
title="支付发票"
|
|
:width="440"
|
|
submit-text="确认支付"
|
|
:submitting="paying"
|
|
:submit-disabled="!emailOk"
|
|
@update:show="emit('update:show', $event)"
|
|
@submit="doPay"
|
|
>
|
|
<template v-if="invoice">
|
|
<div class="rounded-md bg-wash px-4 py-3">
|
|
<div class="text-xs text-ink-3">应付金额</div>
|
|
<div class="mt-1 text-[24px] font-semibold tabular-nums">
|
|
{{ fmtMoney(invoice.amountDue || invoice.amount, invoice.currency) }}
|
|
</div>
|
|
<div class="mt-1 text-xs text-ink-3">
|
|
发票 {{ invoice.number }} · 到期 {{ invoiceDate(invoice.timeDue) }}
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
class="mt-3 flex items-center justify-between rounded-md border border-line-soft px-4 py-2.5 text-[13px]"
|
|
>
|
|
<span class="text-ink-3">扣款方式</span>
|
|
<span class="font-medium">{{ payVia }}</span>
|
|
</div>
|
|
|
|
<div class="mt-3">
|
|
<div class="mb-1 text-xs text-ink-3">回执邮箱(必填)</div>
|
|
<NInput v-model:value="email" placeholder="billing@example.com" />
|
|
</div>
|
|
|
|
<div class="mt-3 text-xs leading-relaxed text-ink-3">
|
|
确认后经 OCI 支付网关按上述方式即时扣款,回执发送至邮箱;提交后发票转入「处理中」,
|
|
入账通常需要数分钟,期间请勿重复支付。
|
|
</div>
|
|
</template>
|
|
</FormModal>
|
|
</template>
|