fix: 修复订阅支付金额显示错误

This commit is contained in:
shaw
2026-06-30 16:26:22 +08:00
parent 89b2d63ef2
commit 930326116e
2 changed files with 23 additions and 36 deletions
+6 -20
View File
@@ -275,7 +275,7 @@ import { platformAccentBarClass, platformBadgeLightClass, platformBadgeClass, pl
import SubscriptionPlanCard from '@/components/payment/SubscriptionPlanCard.vue'
import PaymentStatusPanel from '@/components/payment/PaymentStatusPanel.vue'
import Icon from '@/components/icons/Icon.vue'
import { DEFAULT_PAYMENT_CURRENCY, formatPaymentAmount, normalizePaymentCurrency } from '@/components/payment/currency'
import { formatPaymentAmount, normalizePaymentCurrency } from '@/components/payment/currency'
import type { PaymentMethodOption } from '@/components/payment/PaymentMethodSelector.vue'
import { buildPaymentErrorToastMessage, describePaymentScenarioError } from './paymentUx'
import { hasWechatResumeQuery, parseWechatResumeRoute, stripWechatResumeQuery } from './paymentWechatResume'
@@ -540,10 +540,6 @@ const localeCode = computed(() => {
return undefined
})
interface PaymentAmountFormatOptions {
subscription?: boolean
}
function currencyFractionDigits(currency: string): number {
try {
return new Intl.NumberFormat(undefined, {
@@ -567,22 +563,12 @@ function ceilPaymentAmount(value: number, currency: string): number {
return Math.ceil(value * factor) / factor
}
function subscriptionPaymentAmountForCurrency(value: number, currency: string): number {
if (currency !== DEFAULT_PAYMENT_CURRENCY) return value
return roundPaymentAmount(value / balanceRechargeMultiplier.value, currency)
}
function subscriptionPaymentAmount(value: number): number {
return subscriptionPaymentAmountForCurrency(value, selectedCurrency.value)
}
function formatSelectedPaymentAmount(value: number, options: PaymentAmountFormatOptions = {}): string {
const amount = options.subscription ? subscriptionPaymentAmount(value) : value
return formatPaymentAmount(amount, selectedCurrency.value, localeCode.value)
function formatSelectedPaymentAmount(value: number): string {
return formatPaymentAmount(value, selectedCurrency.value, localeCode.value)
}
function formatSelectedSubscriptionPaymentAmount(value: number): string {
return formatSelectedPaymentAmount(value, { subscription: true })
return formatSelectedPaymentAmount(roundPaymentAmount(value, selectedCurrency.value))
}
const methodOptions = computed<PaymentMethodOption[]>(() =>
@@ -631,7 +617,7 @@ const canSubmit = computed(() =>
const subPaymentAmount = computed(() => {
const price = selectedPlan.value?.price ?? 0
return subscriptionPaymentAmount(price)
return roundPaymentAmount(price, selectedCurrency.value)
})
const subFeeAmount = computed(() => {
@@ -645,7 +631,7 @@ const subTotalAmount = computed(() => {
})
function subscriptionTotalAmountForCurrency(value: number, currency: string): number {
const paymentAmount = subscriptionPaymentAmountForCurrency(value, currency)
const paymentAmount = roundPaymentAmount(value, currency)
if (feeRate.value <= 0 || paymentAmount <= 0) return paymentAmount
const fee = ceilPaymentAmount((paymentAmount * feeRate.value) / 100, currency)
return roundPaymentAmount(paymentAmount + fee, currency)
@@ -236,28 +236,29 @@ async function mountSubscriptionConfirm(options: Parameters<typeof checkoutInfoW
}
describe('PaymentView subscription confirmation amounts', () => {
it('shows converted CNY pay amount for plan price, original price, and create button', async () => {
it('keeps subscription plan price independent from balance recharge multiplier', async () => {
const wrapper = await mountSubscriptionConfirm({
checkout: {
balance_recharge_multiplier: 0.14,
balance_recharge_multiplier: 4,
},
method: {
currency: 'CNY',
},
plan: {
price: 7.99,
original_price: 9.99,
price: 200,
original_price: 300,
},
})
const text = wrapper.text()
const convertedPrice = formatPaymentAmount(57.07, 'CNY')
const convertedOriginalPrice = formatPaymentAmount(71.36, 'CNY')
const planPrice = formatPaymentAmount(200, 'CNY')
const originalPrice = formatPaymentAmount(300, 'CNY')
const convertedByRechargeMultiplier = formatPaymentAmount(50, 'CNY')
expect(text).toContain(convertedPrice)
expect(text).toContain(convertedOriginalPrice)
expect(text).not.toContain(formatPaymentAmount(7.99, 'CNY'))
expect(wrapper.findAll('button').some(button => button.text().includes(convertedPrice))).toBe(true)
expect(text).toContain(planPrice)
expect(text).toContain(originalPrice)
expect(text).not.toContain(convertedByRechargeMultiplier)
expect(wrapper.findAll('button').some(button => button.text().includes(planPrice))).toBe(true)
})
it('keeps plan price when multiplier is not configured or payment currency is not CNY', async () => {
@@ -293,10 +294,10 @@ describe('PaymentView subscription confirmation amounts', () => {
expect(usdWrapper.text()).toContain(formatPaymentAmount(9.99, 'USD'))
})
it('adds fee rate after CNY multiplier conversion to match backend pay_amount', async () => {
it('adds fee rate to the direct subscription plan price to match backend pay_amount', async () => {
const wrapper = await mountSubscriptionConfirm({
checkout: {
balance_recharge_multiplier: 0.14,
balance_recharge_multiplier: 4,
recharge_fee_rate: 2.5,
},
method: {
@@ -308,11 +309,11 @@ describe('PaymentView subscription confirmation amounts', () => {
})
const text = wrapper.text()
const convertedPrice = formatPaymentAmount(57.07, 'CNY')
const fee = formatPaymentAmount(1.43, 'CNY')
const total = formatPaymentAmount(58.5, 'CNY')
const price = formatPaymentAmount(7.99, 'CNY')
const fee = formatPaymentAmount(0.20, 'CNY')
const total = formatPaymentAmount(8.19, 'CNY')
expect(text).toContain(convertedPrice)
expect(text).toContain(price)
expect(text).toContain(fee)
expect(text).toContain(total)
expect(wrapper.findAll('button').some(button => button.text().includes(total))).toBe(true)