update pay function

This commit is contained in:
2025-11-20 08:33:26 +08:00
parent b0b227a5ef
commit 082e644534
3 changed files with 143 additions and 3 deletions

96
app.py
View File

@@ -1427,6 +1427,10 @@ def calculate_subscription_price_simple(user_id, to_plan_name, to_cycle, promo_c
remaining_value = current_price * (remaining_days / total_days)
# 实付金额 = 新套餐价格 - 剩余价值
final_price = max(0, price - remaining_value)
# 如果剩余价值 >= 新套餐价格,标记为免费升级
if remaining_value >= price:
final_price = 0
elif current_plan == 'max' and to_plan_name == 'pro':
# 降级Max → Pro到期后切换全价购买
is_downgrade = True
@@ -1786,6 +1790,89 @@ def calculate_subscription_price():
}), 500
@app.route('/api/subscription/free-upgrade', methods=['POST'])
@login_required
def free_upgrade_subscription():
"""
免费升级订阅(当剩余价值 >= 新套餐价格时)
Request Body:
{
"plan_name": "max",
"billing_cycle": "yearly"
}
"""
try:
data = request.get_json()
plan_name = data.get('plan_name')
billing_cycle = data.get('billing_cycle')
if not plan_name or not billing_cycle:
return jsonify({'success': False, 'error': '参数不完整'}), 400
user_id = current_user.id
# 计算价格,验证是否可以免费升级
price_result = calculate_subscription_price_simple(user_id, plan_name, billing_cycle, None)
if 'error' in price_result:
return jsonify({'success': False, 'error': price_result['error']}), 400
# 检查是否为升级且实付金额为0
if not price_result.get('is_upgrade') or price_result.get('final_amount', 1) > 0:
return jsonify({'success': False, 'error': '当前情况不符合免费升级条件'}), 400
# 获取当前订阅
subscription = UserSubscription.query.filter_by(user_id=user_id).first()
if not subscription:
return jsonify({'success': False, 'error': '未找到订阅记录'}), 404
# 计算新的到期时间(按剩余价值折算)
remaining_value = price_result.get('remaining_value', 0)
new_plan_price = price_result.get('new_plan_price', 0)
if new_plan_price > 0:
# 计算可以兑换的新套餐天数
value_ratio = remaining_value / new_plan_price
cycle_days_map = {
'monthly': 30,
'quarterly': 90,
'semiannual': 180,
'yearly': 365
}
new_cycle_days = cycle_days_map.get(billing_cycle, 365)
# 新的到期天数 = 周期天数 × 价值比例
new_days = int(new_cycle_days * value_ratio)
# 更新订阅信息
subscription.subscription_type = plan_name
subscription.billing_cycle = billing_cycle
subscription.start_date = datetime.utcnow()
subscription.end_date = datetime.utcnow() + timedelta(days=new_days)
subscription.subscription_status = 'active'
subscription.updated_at = datetime.utcnow()
db.session.commit()
return jsonify({
'success': True,
'message': f'升级成功!您的{plan_name.upper()}版本将持续{new_days}',
'data': {
'subscription_type': plan_name,
'end_date': subscription.end_date.isoformat(),
'days': new_days
}
})
else:
return jsonify({'success': False, 'error': '价格计算异常'}), 500
except Exception as e:
db.session.rollback()
return jsonify({'success': False, 'error': f'升级失败: {str(e)}'}), 500
@app.route('/api/payment/create-order', methods=['POST'])
def create_payment_order():
"""
@@ -1819,6 +1906,15 @@ def create_payment_order():
amount = price_result['final_amount']
subscription_type = price_result.get('subscription_type', 'new') # new 或 renew
# 检查是否为免费升级金额为0
if amount <= 0 and price_result.get('is_upgrade'):
return jsonify({
'success': False,
'error': '当前剩余价值可直接免费升级,请使用免费升级功能',
'should_free_upgrade': True,
'price_info': price_result
}), 400
# 创建订单
try:
order = PaymentOrder(

View File

@@ -270,6 +270,46 @@ export default function SubscriptionContentNew() {
try {
const price = priceInfo?.final_amount || getCurrentPrice(selectedPlan);
// 检查是否为免费升级(剩余价值足够抵扣新套餐价格)
if (price === 0 && priceInfo?.is_upgrade) {
const response = await fetch('/api/subscription/free-upgrade', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
credentials: 'include',
body: JSON.stringify({
plan_name: selectedPlan.name,
billing_cycle: selectedCycle,
}),
});
const data = await response.json();
if (data.success) {
subscriptionEvents.trackPaymentSuccessful({
planName: selectedPlan.name,
paymentMethod: 'free_upgrade',
amount: 0,
orderId: 'free_upgrade',
transactionId: 'free_upgrade',
});
toast({
title: '升级成功!',
description: data.message,
status: 'success',
duration: 5000,
isClosable: true,
});
onClose();
setTimeout(() => window.location.reload(), 2000);
return;
} else {
throw new Error(data.error || '免费升级失败');
}
}
subscriptionEvents.trackPaymentInitiated({
planName: selectedPlan.name,
paymentMethod: 'wechat_pay',
@@ -1122,7 +1162,9 @@ export default function SubscriptionContentNew() {
<HStack spacing={2}>
<Icon as={FaCheck} color="green.400" />
<Text color="green.400" fontSize="sm" fontWeight="medium">
{selectedPlan.displayName}
{priceInfo.final_amount === 0
? `恭喜!您的当前订阅剩余价值足够直接升级到${selectedPlan.displayName},无需支付额外费用!`
: `升级到${selectedPlan.displayName},立即生效!按差价补缴费用`}
</Text>
</HStack>
</Box>
@@ -1293,7 +1335,9 @@ export default function SubscriptionContentNew() {
bgGradient: 'linear-gradient(135deg, #F4E3A7, #D4AF37)',
}}
>
{priceInfo?.is_upgrade && priceInfo?.final_amount === 0
? '立即免费升级'
: '创建微信支付订单'}
</Button>
</VStack>
) : (

View File

@@ -163,7 +163,7 @@ export const subscriptionConfig = {
},
{
question: 'Pro用户如何升级到Max',
answer: '从Pro升级到Max需要补差价升级后立即生效。系统会根据您Pro订阅的剩余价值计算需要补缴的费用。支付成功后您将立即获得Max版本的所有功能。',
answer: '从Pro升级到Max需要补差价升级后立即生效。系统会根据您Pro订阅的剩余价值计算需要补缴的费用。支付成功后您将立即获得Max版本的所有功能。\n\n特别说明如果您的Pro订阅剩余价值超过或等于Max套餐的价格系统将自动为您免费升级到Max版本无需支付额外费用。升级后的有效期将根据剩余价值按比例计算。例如您的Pro年付版本剩余价值为1200元选择Max月付版本998元/月系统将为您提供约36天的Max版本使用时长1200÷998×30天',
},
{
question: 'Max用户可以切换到Pro吗',