fix(payment): integrate recharge fee rate in order flow and fix UI display
Backend: - Use cfg.RechargeFeeRate in order creation instead of hardcoded 0 - Remove dead getFeeRate stub method - All amounts computed server-side: order_amount, pay_amount, fee_rate Frontend - PaymentView: - Read recharge_fee_rate from checkout-info API (not per-method) - Show fee breakdown only when fee_rate > 0 - Show credited amount only when multiplier ≠ 1 Frontend - Order display (user + admin): - Fix fee_rate * 100 bug (fee_rate is already a percentage) - OrderTable: show pay_amount as primary, fee/credited as sub-lines - AdminOrderDetail: full breakdown (base/fee/paid/credited) - AdminRefundDialog: label "到账金额" for clarity - PaymentResultView: show pay_amount with fee info Types + i18n: - Add recharge_fee_rate to CheckoutInfoResponse - Add fee_rate to CreateOrderResult - Add translations: creditedAmount, fee, baseAmount, includedInPayAmount
This commit is contained in:
@@ -51,7 +51,7 @@ func (s *PaymentService) CreateOrder(ctx context.Context, req CreateOrderRequest
|
|||||||
} else if req.OrderType == payment.OrderTypeBalance {
|
} else if req.OrderType == payment.OrderTypeBalance {
|
||||||
orderAmount = calculateCreditedBalance(req.Amount, cfg.BalanceRechargeMultiplier)
|
orderAmount = calculateCreditedBalance(req.Amount, cfg.BalanceRechargeMultiplier)
|
||||||
}
|
}
|
||||||
feeRate := s.getFeeRate(req.PaymentType)
|
feeRate := cfg.RechargeFeeRate
|
||||||
payAmountStr := payment.CalculatePayAmount(limitAmount, feeRate)
|
payAmountStr := payment.CalculatePayAmount(limitAmount, feeRate)
|
||||||
payAmount, _ := strconv.ParseFloat(payAmountStr, 64)
|
payAmount, _ := strconv.ParseFloat(payAmountStr, 64)
|
||||||
order, err := s.createOrderInTx(ctx, req, user, plan, cfg, orderAmount, limitAmount, feeRate, payAmount)
|
order, err := s.createOrderInTx(ctx, req, user, plan, cfg, orderAmount, limitAmount, feeRate, payAmount)
|
||||||
|
|||||||
@@ -288,7 +288,6 @@ func psComputeValidityDays(days int, unit string) int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PaymentService) getFeeRate(_ string) float64 { return 0 }
|
|
||||||
|
|
||||||
func psStartOfDayUTC(t time.Time) time.Time {
|
func psStartOfDayUTC(t time.Time) time.Time {
|
||||||
y, m, d := t.UTC().Date()
|
y, m, d := t.UTC().Date()
|
||||||
|
|||||||
@@ -18,23 +18,27 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p class="text-xs text-gray-500 dark:text-gray-400">{{ t('payment.orders.amount') }}</p>
|
<p class="text-xs text-gray-500 dark:text-gray-400">{{ t('payment.orders.baseAmount') }}</p>
|
||||||
<p class="text-sm font-medium text-gray-900 dark:text-white">{{ order.order_type === 'balance' ? '$' : '¥' }}{{ order.amount.toFixed(2) }}</p>
|
<p class="text-sm font-medium text-gray-900 dark:text-white">¥{{ baseAmount.toFixed(2) }}</p>
|
||||||
|
</div>
|
||||||
|
<div v-if="order.fee_rate > 0">
|
||||||
|
<p class="text-xs text-gray-500 dark:text-gray-400">{{ t('payment.orders.fee') }} ({{ order.fee_rate }}%)</p>
|
||||||
|
<p class="text-sm font-medium text-gray-900 dark:text-white">¥{{ feeAmount.toFixed(2) }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p class="text-xs text-gray-500 dark:text-gray-400">{{ t('payment.orders.payAmount') }}</p>
|
<p class="text-xs text-gray-500 dark:text-gray-400">{{ t('payment.orders.payAmount') }}</p>
|
||||||
<p class="text-sm font-medium text-gray-900 dark:text-white">¥{{ order.pay_amount.toFixed(2) }}</p>
|
<p class="text-sm font-medium text-gray-900 dark:text-white">¥{{ order.pay_amount.toFixed(2) }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="order.amount !== order.pay_amount">
|
||||||
|
<p class="text-xs text-gray-500 dark:text-gray-400">{{ t('payment.orders.creditedAmount') }}</p>
|
||||||
|
<p class="text-sm font-medium text-gray-900 dark:text-white">{{ order.order_type === 'balance' ? '$' : '¥' }}{{ order.amount.toFixed(2) }}</p>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p class="text-xs text-gray-500 dark:text-gray-400">{{ t('payment.orders.paymentMethod') }}</p>
|
<p class="text-xs text-gray-500 dark:text-gray-400">{{ t('payment.orders.paymentMethod') }}</p>
|
||||||
<p class="text-sm text-gray-700 dark:text-gray-300">
|
<p class="text-sm text-gray-700 dark:text-gray-300">
|
||||||
{{ t('payment.methods.' + order.payment_type, order.payment_type) }}
|
{{ t('payment.methods.' + order.payment_type, order.payment_type) }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<p class="text-xs text-gray-500 dark:text-gray-400">{{ t('payment.admin.feeRate') }}</p>
|
|
||||||
<p class="text-sm text-gray-700 dark:text-gray-300">{{ (order.fee_rate * 100).toFixed(1) }}%</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
<div>
|
||||||
<p class="text-xs text-gray-500 dark:text-gray-400">{{ t('payment.admin.orderType') }}</p>
|
<p class="text-xs text-gray-500 dark:text-gray-400">{{ t('payment.admin.orderType') }}</p>
|
||||||
<p class="text-sm text-gray-700 dark:text-gray-300">
|
<p class="text-sm text-gray-700 dark:text-gray-300">
|
||||||
@@ -110,6 +114,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import BaseDialog from '@/components/common/BaseDialog.vue'
|
import BaseDialog from '@/components/common/BaseDialog.vue'
|
||||||
import type { PaymentOrder } from '@/types/payment'
|
import type { PaymentOrder } from '@/types/payment'
|
||||||
@@ -117,11 +122,24 @@ import { statusBadgeClass, canRefund as canRefundStatus, formatOrderDateTime } f
|
|||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
defineProps<{
|
const props = defineProps<{
|
||||||
show: boolean
|
show: boolean
|
||||||
order: PaymentOrder | null
|
order: PaymentOrder | null
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
/** 充值金额 (base amount before fee) = pay_amount - fee = pay_amount / (1 + fee_rate/100) */
|
||||||
|
const baseAmount = computed(() => {
|
||||||
|
if (!props.order) return 0
|
||||||
|
if (props.order.fee_rate <= 0) return props.order.pay_amount
|
||||||
|
return props.order.pay_amount / (1 + props.order.fee_rate / 100)
|
||||||
|
})
|
||||||
|
|
||||||
|
/** 手续费 = pay_amount - baseAmount */
|
||||||
|
const feeAmount = computed(() => {
|
||||||
|
if (!props.order || props.order.fee_rate <= 0) return 0
|
||||||
|
return props.order.pay_amount - baseAmount.value
|
||||||
|
})
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'close'): void
|
(e: 'close'): void
|
||||||
(e: 'cancel', order: PaymentOrder): void
|
(e: 'cancel', order: PaymentOrder): void
|
||||||
|
|||||||
@@ -51,12 +51,15 @@
|
|||||||
<span class="text-sm text-gray-600 dark:text-gray-400">#{{ value }}</span>
|
<span class="text-sm text-gray-600 dark:text-gray-400">#{{ value }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #cell-amount="{ value, row }">
|
<template #cell-pay_amount="{ value, row }">
|
||||||
<div class="text-sm">
|
<div class="text-sm">
|
||||||
<span class="font-medium text-gray-900 dark:text-white">{{ row.order_type === 'balance' ? '$' : '¥' }}{{ value.toFixed(2) }}</span>
|
<span class="font-medium text-gray-900 dark:text-white">¥{{ value.toFixed(2) }}</span>
|
||||||
<span v-if="row.pay_amount !== value" class="ml-1 text-xs text-gray-500">
|
<span v-if="row.fee_rate > 0" class="ml-1 text-xs text-gray-400" :title="t('payment.orders.fee') + ': ' + row.fee_rate + '%'">
|
||||||
({{ t('payment.orders.payAmount') }}: ¥{{ row.pay_amount.toFixed(2) }})
|
({{ row.fee_rate }}%)
|
||||||
</span>
|
</span>
|
||||||
|
<div v-if="row.amount !== row.pay_amount" class="text-xs text-gray-500">
|
||||||
|
{{ t('payment.orders.creditedAmount') }}: {{ row.order_type === 'balance' ? '$' : '¥' }}{{ row.amount.toFixed(2) }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -183,7 +186,7 @@ function emitFiltersChanged() {
|
|||||||
const columns = computed<Column[]>(() => [
|
const columns = computed<Column[]>(() => [
|
||||||
{ key: 'id', label: t('payment.orders.orderId') },
|
{ key: 'id', label: t('payment.orders.orderId') },
|
||||||
{ key: 'user_id', label: t('payment.orders.userId') },
|
{ key: 'user_id', label: t('payment.orders.userId') },
|
||||||
{ key: 'amount', label: t('payment.orders.amount') },
|
{ key: 'pay_amount', label: t('payment.orders.payAmount') },
|
||||||
{ key: 'payment_type', label: t('payment.orders.paymentMethod') },
|
{ key: 'payment_type', label: t('payment.orders.paymentMethod') },
|
||||||
{ key: 'status', label: t('payment.orders.status') },
|
{ key: 'status', label: t('payment.orders.status') },
|
||||||
{ key: 'order_type', label: t('payment.orders.orderType') },
|
{ key: 'order_type', label: t('payment.orders.orderType') },
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
<span class="font-mono text-gray-900 dark:text-white">#{{ order?.id }}</span>
|
<span class="font-mono text-gray-900 dark:text-white">#{{ order?.id }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-1 flex justify-between text-sm">
|
<div class="mt-1 flex justify-between text-sm">
|
||||||
<span class="text-gray-500 dark:text-gray-400">{{ t('payment.orders.amount') }}</span>
|
<span class="text-gray-500 dark:text-gray-400">{{ t('payment.orders.creditedAmount') }}</span>
|
||||||
<span class="font-medium text-gray-900 dark:text-white">{{ order?.order_type === 'balance' ? '$' : '¥' }}{{ order?.amount?.toFixed(2) }}</span>
|
<span class="font-medium text-gray-900 dark:text-white">{{ order?.order_type === 'balance' ? '$' : '¥' }}{{ order?.amount?.toFixed(2) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-1 flex justify-between text-sm">
|
<div class="mt-1 flex justify-between text-sm">
|
||||||
|
|||||||
@@ -12,10 +12,15 @@
|
|||||||
<span v-if="row.user_notes" class="ml-1 text-xs text-gray-400">({{ row.user_notes }})</span>
|
<span v-if="row.user_notes" class="ml-1 text-xs text-gray-400">({{ row.user_notes }})</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #cell-amount="{ value, row }">
|
<template #cell-pay_amount="{ value, row }">
|
||||||
<div class="text-sm">
|
<div class="text-sm">
|
||||||
<span class="font-medium text-gray-900 dark:text-white">{{ row.order_type === 'balance' ? '$' : '¥' }}{{ value.toFixed(2) }}</span>
|
<span class="font-medium text-gray-900 dark:text-white">¥{{ value.toFixed(2) }}</span>
|
||||||
<span v-if="row.pay_amount !== value" class="ml-1 text-xs text-gray-500">(¥{{ row.pay_amount.toFixed(2) }})</span>
|
<span v-if="row.fee_rate > 0" class="ml-1 text-xs text-gray-400" :title="t('payment.orders.fee') + ': ' + row.fee_rate + '%'">
|
||||||
|
({{ t('payment.orders.fee') }} {{ row.fee_rate }}%)
|
||||||
|
</span>
|
||||||
|
<div v-if="row.amount !== row.pay_amount" class="text-xs text-gray-500">
|
||||||
|
{{ t('payment.orders.creditedAmount') }}: {{ row.order_type === 'balance' ? '$' : '¥' }}{{ row.amount.toFixed(2) }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #cell-payment_type="{ value }">
|
<template #cell-payment_type="{ value }">
|
||||||
@@ -60,7 +65,7 @@ const columns = computed((): Column[] => {
|
|||||||
cols.push({ key: 'user_email', label: t('payment.admin.colUser') })
|
cols.push({ key: 'user_email', label: t('payment.admin.colUser') })
|
||||||
}
|
}
|
||||||
cols.push(
|
cols.push(
|
||||||
{ key: 'amount', label: t('payment.orders.amount') },
|
{ key: 'pay_amount', label: t('payment.orders.payAmount') },
|
||||||
{ key: 'payment_type', label: t('payment.orders.paymentMethod') },
|
{ key: 'payment_type', label: t('payment.orders.paymentMethod') },
|
||||||
{ key: 'status', label: t('payment.orders.status') },
|
{ key: 'status', label: t('payment.orders.status') },
|
||||||
{ key: 'created_at', label: t('payment.orders.createdAt') },
|
{ key: 'created_at', label: t('payment.orders.createdAt') },
|
||||||
|
|||||||
@@ -5387,6 +5387,10 @@ export default {
|
|||||||
orderNo: 'Order No.',
|
orderNo: 'Order No.',
|
||||||
amount: 'Amount',
|
amount: 'Amount',
|
||||||
payAmount: 'Paid',
|
payAmount: 'Paid',
|
||||||
|
creditedAmount: 'Credited Amount',
|
||||||
|
fee: 'Fee',
|
||||||
|
baseAmount: 'Base Amount',
|
||||||
|
includedInPayAmount: 'included in paid amount',
|
||||||
status: 'Status',
|
status: 'Status',
|
||||||
paymentMethod: 'Payment Method',
|
paymentMethod: 'Payment Method',
|
||||||
createdAt: 'Created',
|
createdAt: 'Created',
|
||||||
|
|||||||
@@ -5575,6 +5575,10 @@ export default {
|
|||||||
orderNo: '订单编号',
|
orderNo: '订单编号',
|
||||||
amount: '金额',
|
amount: '金额',
|
||||||
payAmount: '实付',
|
payAmount: '实付',
|
||||||
|
creditedAmount: '到账金额',
|
||||||
|
fee: '手续费',
|
||||||
|
baseAmount: '充值金额',
|
||||||
|
includedInPayAmount: '已含在实付金额中',
|
||||||
status: '状态',
|
status: '状态',
|
||||||
paymentMethod: '支付方式',
|
paymentMethod: '支付方式',
|
||||||
createdAt: '创建时间',
|
createdAt: '创建时间',
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ export interface CheckoutInfoResponse {
|
|||||||
plans: SubscriptionPlan[]
|
plans: SubscriptionPlan[]
|
||||||
balance_disabled: boolean
|
balance_disabled: boolean
|
||||||
balance_recharge_multiplier: number
|
balance_recharge_multiplier: number
|
||||||
|
recharge_fee_rate: number
|
||||||
help_text: string
|
help_text: string
|
||||||
help_image_url: string
|
help_image_url: string
|
||||||
stripe_publishable_key: string
|
stripe_publishable_key: string
|
||||||
@@ -162,6 +163,7 @@ export interface CreateOrderResult {
|
|||||||
qr_code?: string
|
qr_code?: string
|
||||||
client_secret?: string
|
client_secret?: string
|
||||||
pay_amount: number
|
pay_amount: number
|
||||||
|
fee_rate: number
|
||||||
expires_at: string
|
expires_at: string
|
||||||
payment_mode?: string
|
payment_mode?: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@
|
|||||||
<div><p class="text-xs text-gray-500 dark:text-gray-400">{{ t('payment.orders.amount') }}</p><p class="text-sm font-medium text-gray-900 dark:text-white">{{ selectedOrder.order_type === 'balance' ? '$' : '¥' }}{{ selectedOrder.amount.toFixed(2) }}</p></div>
|
<div><p class="text-xs text-gray-500 dark:text-gray-400">{{ t('payment.orders.amount') }}</p><p class="text-sm font-medium text-gray-900 dark:text-white">{{ selectedOrder.order_type === 'balance' ? '$' : '¥' }}{{ selectedOrder.amount.toFixed(2) }}</p></div>
|
||||||
<div><p class="text-xs text-gray-500 dark:text-gray-400">{{ t('payment.orders.payAmount') }}</p><p class="text-sm font-medium text-gray-900 dark:text-white">¥{{ selectedOrder.pay_amount.toFixed(2) }}</p></div>
|
<div><p class="text-xs text-gray-500 dark:text-gray-400">{{ t('payment.orders.payAmount') }}</p><p class="text-sm font-medium text-gray-900 dark:text-white">¥{{ selectedOrder.pay_amount.toFixed(2) }}</p></div>
|
||||||
<div><p class="text-xs text-gray-500 dark:text-gray-400">{{ t('payment.orders.paymentMethod') }}</p><p class="text-sm text-gray-700 dark:text-gray-300">{{ t('payment.methods.' + selectedOrder.payment_type, selectedOrder.payment_type) }}</p></div>
|
<div><p class="text-xs text-gray-500 dark:text-gray-400">{{ t('payment.orders.paymentMethod') }}</p><p class="text-sm text-gray-700 dark:text-gray-300">{{ t('payment.methods.' + selectedOrder.payment_type, selectedOrder.payment_type) }}</p></div>
|
||||||
<div><p class="text-xs text-gray-500 dark:text-gray-400">{{ t('payment.admin.feeRate') }}</p><p class="text-sm text-gray-700 dark:text-gray-300">{{ (selectedOrder.fee_rate * 100).toFixed(1) }}%</p></div>
|
<div><p class="text-xs text-gray-500 dark:text-gray-400">{{ t('payment.admin.feeRate') }}</p><p class="text-sm text-gray-700 dark:text-gray-300">{{ selectedOrder.fee_rate }}%</p></div>
|
||||||
<div><p class="text-xs text-gray-500 dark:text-gray-400">{{ t('payment.orders.createdAt') }}</p><p class="text-sm text-gray-700 dark:text-gray-300">{{ formatDateTime(selectedOrder.created_at) }}</p></div>
|
<div><p class="text-xs text-gray-500 dark:text-gray-400">{{ t('payment.orders.createdAt') }}</p><p class="text-sm text-gray-700 dark:text-gray-300">{{ formatDateTime(selectedOrder.created_at) }}</p></div>
|
||||||
<div><p class="text-xs text-gray-500 dark:text-gray-400">{{ t('payment.admin.expiresAt') }}</p><p class="text-sm text-gray-700 dark:text-gray-300">{{ formatDateTime(selectedOrder.expires_at) }}</p></div>
|
<div><p class="text-xs text-gray-500 dark:text-gray-400">{{ t('payment.admin.expiresAt') }}</p><p class="text-sm text-gray-700 dark:text-gray-300">{{ formatDateTime(selectedOrder.expires_at) }}</p></div>
|
||||||
<div v-if="selectedOrder.paid_at"><p class="text-xs text-gray-500 dark:text-gray-400">{{ t('payment.admin.paidAt') }}</p><p class="text-sm text-gray-700 dark:text-gray-300">{{ formatDateTime(selectedOrder.paid_at) }}</p></div>
|
<div v-if="selectedOrder.paid_at"><p class="text-xs text-gray-500 dark:text-gray-400">{{ t('payment.admin.paidAt') }}</p><p class="text-sm text-gray-700 dark:text-gray-300">{{ formatDateTime(selectedOrder.paid_at) }}</p></div>
|
||||||
|
|||||||
@@ -36,14 +36,18 @@
|
|||||||
<span class="text-gray-500 dark:text-gray-400">{{ t('payment.orders.orderNo') }}</span>
|
<span class="text-gray-500 dark:text-gray-400">{{ t('payment.orders.orderNo') }}</span>
|
||||||
<span class="font-medium text-gray-900 dark:text-white">{{ order.out_trade_no }}</span>
|
<span class="font-medium text-gray-900 dark:text-white">{{ order.out_trade_no }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between">
|
|
||||||
<span class="text-gray-500 dark:text-gray-400">{{ t('payment.orders.amount') }}</span>
|
|
||||||
<span class="font-medium text-gray-900 dark:text-white">{{ order.order_type === 'balance' ? '$' : '¥' }}{{ order.amount.toFixed(2) }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
<span class="text-gray-500 dark:text-gray-400">{{ t('payment.orders.payAmount') }}</span>
|
<span class="text-gray-500 dark:text-gray-400">{{ t('payment.orders.payAmount') }}</span>
|
||||||
<span class="font-medium text-gray-900 dark:text-white">¥{{ order.pay_amount.toFixed(2) }}</span>
|
<span class="font-medium text-gray-900 dark:text-white">¥{{ order.pay_amount.toFixed(2) }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="order.fee_rate > 0" class="flex justify-between">
|
||||||
|
<span class="text-gray-500 dark:text-gray-400">{{ t('payment.orders.fee') }} ({{ order.fee_rate }}%)</span>
|
||||||
|
<span class="text-xs text-gray-400 dark:text-gray-500">{{ t('payment.orders.includedInPayAmount') }}</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="order.amount !== order.pay_amount" class="flex justify-between">
|
||||||
|
<span class="text-gray-500 dark:text-gray-400">{{ t('payment.orders.creditedAmount') }}</span>
|
||||||
|
<span class="font-medium text-gray-900 dark:text-white">{{ order.order_type === 'balance' ? '$' : '¥' }}{{ order.amount.toFixed(2) }}</span>
|
||||||
|
</div>
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
<span class="text-gray-500 dark:text-gray-400">{{ t('payment.orders.paymentMethod') }}</span>
|
<span class="text-gray-500 dark:text-gray-400">{{ t('payment.orders.paymentMethod') }}</span>
|
||||||
<span class="font-medium text-gray-900 dark:text-white">{{ t('payment.methods.' + order.payment_type, order.payment_type) }}</span>
|
<span class="font-medium text-gray-900 dark:text-white">{{ t('payment.methods.' + order.payment_type, order.payment_type) }}</span>
|
||||||
|
|||||||
@@ -75,19 +75,19 @@
|
|||||||
<span class="text-gray-500 dark:text-gray-400">{{ t('payment.paymentAmount') }}</span>
|
<span class="text-gray-500 dark:text-gray-400">{{ t('payment.paymentAmount') }}</span>
|
||||||
<span class="text-gray-900 dark:text-white">¥{{ validAmount.toFixed(2) }}</span>
|
<span class="text-gray-900 dark:text-white">¥{{ validAmount.toFixed(2) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between">
|
|
||||||
<span class="text-gray-500 dark:text-gray-400">{{ t('payment.creditedBalance') }}</span>
|
|
||||||
<span class="text-gray-900 dark:text-white">${{ creditedAmount.toFixed(2) }}</span>
|
|
||||||
</div>
|
|
||||||
<div v-if="feeRate > 0" class="flex justify-between">
|
<div v-if="feeRate > 0" class="flex justify-between">
|
||||||
<span class="text-gray-500 dark:text-gray-400">{{ t('payment.fee') }} ({{ feeRate }}%)</span>
|
<span class="text-gray-500 dark:text-gray-400">{{ t('payment.fee') }} ({{ feeRate }}%)</span>
|
||||||
<span class="text-gray-900 dark:text-white">¥{{ feeAmount.toFixed(2) }}</span>
|
<span class="text-gray-900 dark:text-white">¥{{ feeAmount.toFixed(2) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between border-t border-gray-200 pt-2 dark:border-dark-600">
|
<div v-if="feeRate > 0" class="flex justify-between border-t border-gray-200 pt-2 dark:border-dark-600">
|
||||||
<span class="font-medium text-gray-700 dark:text-gray-300">{{ t('payment.actualPay') }}</span>
|
<span class="font-medium text-gray-700 dark:text-gray-300">{{ t('payment.actualPay') }}</span>
|
||||||
<span class="text-lg font-bold text-primary-600 dark:text-primary-400">¥{{ totalAmount.toFixed(2) }}</span>
|
<span class="text-lg font-bold text-primary-600 dark:text-primary-400">¥{{ totalAmount.toFixed(2) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<p class="border-t border-gray-200 pt-2 text-xs text-gray-500 dark:border-dark-600 dark:text-gray-400">
|
<div v-if="balanceRechargeMultiplier !== 1" class="flex justify-between" :class="{ 'border-t border-gray-200 pt-2 dark:border-dark-600': feeRate <= 0 }">
|
||||||
|
<span class="text-gray-500 dark:text-gray-400">{{ t('payment.creditedBalance') }}</span>
|
||||||
|
<span class="text-gray-900 dark:text-white">${{ creditedAmount.toFixed(2) }}</span>
|
||||||
|
</div>
|
||||||
|
<p v-if="balanceRechargeMultiplier !== 1" class="border-t border-gray-200 pt-2 text-xs text-gray-500 dark:border-dark-600 dark:text-gray-400">
|
||||||
{{ t('payment.rechargeRatePreview', { usd: balanceRechargeMultiplier.toFixed(2) }) }}
|
{{ t('payment.rechargeRatePreview', { usd: balanceRechargeMultiplier.toFixed(2) }) }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -361,7 +361,7 @@ function onStripeRedirect(orderId: number, payUrl: string) {
|
|||||||
// All checkout data from single API call
|
// All checkout data from single API call
|
||||||
const checkout = ref<CheckoutInfoResponse>({
|
const checkout = ref<CheckoutInfoResponse>({
|
||||||
methods: {}, global_min: 0, global_max: 0,
|
methods: {}, global_min: 0, global_max: 0,
|
||||||
plans: [], balance_disabled: false, balance_recharge_multiplier: 1, help_text: '', help_image_url: '', stripe_publishable_key: '',
|
plans: [], balance_disabled: false, balance_recharge_multiplier: 1, recharge_fee_rate: 0, help_text: '', help_image_url: '', stripe_publishable_key: '',
|
||||||
})
|
})
|
||||||
|
|
||||||
const tabs = computed(() => {
|
const tabs = computed(() => {
|
||||||
@@ -414,7 +414,7 @@ const methodOptions = computed<PaymentMethodOption[]>(() =>
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
const feeRate = computed(() => selectedLimit.value?.fee_rate ?? 0)
|
const feeRate = computed(() => checkout.value?.recharge_fee_rate ?? 0)
|
||||||
const feeAmount = computed(() =>
|
const feeAmount = computed(() =>
|
||||||
feeRate.value > 0 && validAmount.value > 0
|
feeRate.value > 0 && validAmount.value > 0
|
||||||
? Math.ceil(((validAmount.value * feeRate.value) / 100) * 100) / 100
|
? Math.ceil(((validAmount.value * feeRate.value) / 100) * 100) / 100
|
||||||
|
|||||||
Reference in New Issue
Block a user