fix(payment): convert subscription CNY pay amount

This commit is contained in:
wucm667
2026-07-06 10:56:43 +08:00
parent f0f1f59a44
commit b408edf97b
5 changed files with 73 additions and 39 deletions
+9 -4
View File
@@ -283,7 +283,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 { formatPaymentAmount, normalizePaymentCurrency } from '@/components/payment/currency'
import { DEFAULT_PAYMENT_CURRENCY, 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'
@@ -579,12 +579,17 @@ 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 roundPaymentAmount(value, currency)
return roundPaymentAmount(value / balanceRechargeMultiplier.value, currency)
}
function formatSelectedPaymentAmount(value: number): string {
return formatPaymentAmount(value, selectedCurrency.value, localeCode.value)
}
function formatSelectedSubscriptionPaymentAmount(value: number): string {
return formatSelectedPaymentAmount(roundPaymentAmount(value, selectedCurrency.value))
return formatSelectedPaymentAmount(subscriptionPaymentAmountForCurrency(value, selectedCurrency.value))
}
const methodOptions = computed<PaymentMethodOption[]>(() =>
@@ -633,7 +638,7 @@ const canSubmit = computed(() =>
const subPaymentAmount = computed(() => {
const price = selectedPlan.value?.price ?? 0
return roundPaymentAmount(price, selectedCurrency.value)
return subscriptionPaymentAmountForCurrency(price, selectedCurrency.value)
})
const subFeeAmount = computed(() => {
@@ -647,7 +652,7 @@ const subTotalAmount = computed(() => {
})
function subscriptionTotalAmountForCurrency(value: number, currency: string): number {
const paymentAmount = roundPaymentAmount(value, currency)
const paymentAmount = subscriptionPaymentAmountForCurrency(value, currency)
if (feeRate.value <= 0 || paymentAmount <= 0) return paymentAmount
const fee = ceilPaymentAmount((paymentAmount * feeRate.value) / 100, currency)
return roundPaymentAmount(paymentAmount + fee, currency)
@@ -236,29 +236,28 @@ async function mountSubscriptionConfirm(options: Parameters<typeof checkoutInfoW
}
describe('PaymentView subscription confirmation amounts', () => {
it('keeps subscription plan price independent from balance recharge multiplier', async () => {
it('shows converted CNY pay amount for plan price, original price, and create button', async () => {
const wrapper = await mountSubscriptionConfirm({
checkout: {
balance_recharge_multiplier: 4,
balance_recharge_multiplier: 0.14,
},
method: {
currency: 'CNY',
},
plan: {
price: 200,
original_price: 300,
price: 9.99,
original_price: 12.99,
},
})
const text = wrapper.text()
const planPrice = formatPaymentAmount(200, 'CNY')
const originalPrice = formatPaymentAmount(300, 'CNY')
const convertedByRechargeMultiplier = formatPaymentAmount(50, 'CNY')
const convertedPrice = formatPaymentAmount(71.36, 'CNY')
const convertedOriginalPrice = formatPaymentAmount(92.79, 'CNY')
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)
expect(text).toContain(convertedPrice)
expect(text).toContain(convertedOriginalPrice)
expect(text).not.toContain(formatPaymentAmount(9.99, 'CNY'))
expect(wrapper.findAll('button').some(button => button.text().includes(convertedPrice))).toBe(true)
})
it('keeps plan price when multiplier is not configured or payment currency is not CNY', async () => {
@@ -294,26 +293,26 @@ describe('PaymentView subscription confirmation amounts', () => {
expect(usdWrapper.text()).toContain(formatPaymentAmount(9.99, 'USD'))
})
it('adds fee rate to the direct subscription plan price to match backend pay_amount', async () => {
it('adds fee rate after CNY multiplier conversion to match backend pay_amount', async () => {
const wrapper = await mountSubscriptionConfirm({
checkout: {
balance_recharge_multiplier: 4,
balance_recharge_multiplier: 0.14,
recharge_fee_rate: 2.5,
},
method: {
currency: 'CNY',
},
plan: {
price: 7.99,
price: 9.99,
},
})
const text = wrapper.text()
const price = formatPaymentAmount(7.99, 'CNY')
const fee = formatPaymentAmount(0.20, 'CNY')
const total = formatPaymentAmount(8.19, 'CNY')
const convertedPrice = formatPaymentAmount(71.36, 'CNY')
const fee = formatPaymentAmount(1.79, 'CNY')
const total = formatPaymentAmount(73.15, 'CNY')
expect(text).toContain(price)
expect(text).toContain(convertedPrice)
expect(text).toContain(fee)
expect(text).toContain(total)
expect(wrapper.findAll('button').some(button => button.text().includes(total))).toBe(true)