update pay function
This commit is contained in:
96
app.py
96
app.py
@@ -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)
|
remaining_value = current_price * (remaining_days / total_days)
|
||||||
# 实付金额 = 新套餐价格 - 剩余价值
|
# 实付金额 = 新套餐价格 - 剩余价值
|
||||||
final_price = max(0, price - remaining_value)
|
final_price = max(0, price - remaining_value)
|
||||||
|
|
||||||
|
# 如果剩余价值 >= 新套餐价格,标记为免费升级
|
||||||
|
if remaining_value >= price:
|
||||||
|
final_price = 0
|
||||||
elif current_plan == 'max' and to_plan_name == 'pro':
|
elif current_plan == 'max' and to_plan_name == 'pro':
|
||||||
# 降级:Max → Pro,到期后切换,全价购买
|
# 降级:Max → Pro,到期后切换,全价购买
|
||||||
is_downgrade = True
|
is_downgrade = True
|
||||||
@@ -1786,6 +1790,89 @@ def calculate_subscription_price():
|
|||||||
}), 500
|
}), 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'])
|
@app.route('/api/payment/create-order', methods=['POST'])
|
||||||
def create_payment_order():
|
def create_payment_order():
|
||||||
"""
|
"""
|
||||||
@@ -1819,6 +1906,15 @@ def create_payment_order():
|
|||||||
amount = price_result['final_amount']
|
amount = price_result['final_amount']
|
||||||
subscription_type = price_result.get('subscription_type', 'new') # new 或 renew
|
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:
|
try:
|
||||||
order = PaymentOrder(
|
order = PaymentOrder(
|
||||||
|
|||||||
@@ -270,6 +270,46 @@ export default function SubscriptionContentNew() {
|
|||||||
try {
|
try {
|
||||||
const price = priceInfo?.final_amount || getCurrentPrice(selectedPlan);
|
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({
|
subscriptionEvents.trackPaymentInitiated({
|
||||||
planName: selectedPlan.name,
|
planName: selectedPlan.name,
|
||||||
paymentMethod: 'wechat_pay',
|
paymentMethod: 'wechat_pay',
|
||||||
@@ -1122,7 +1162,9 @@ export default function SubscriptionContentNew() {
|
|||||||
<HStack spacing={2}>
|
<HStack spacing={2}>
|
||||||
<Icon as={FaCheck} color="green.400" />
|
<Icon as={FaCheck} color="green.400" />
|
||||||
<Text color="green.400" fontSize="sm" fontWeight="medium">
|
<Text color="green.400" fontSize="sm" fontWeight="medium">
|
||||||
升级到{selectedPlan.displayName},立即生效!按差价补缴费用
|
{priceInfo.final_amount === 0
|
||||||
|
? `恭喜!您的当前订阅剩余价值足够直接升级到${selectedPlan.displayName},无需支付额外费用!`
|
||||||
|
: `升级到${selectedPlan.displayName},立即生效!按差价补缴费用`}
|
||||||
</Text>
|
</Text>
|
||||||
</HStack>
|
</HStack>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -1293,7 +1335,9 @@ export default function SubscriptionContentNew() {
|
|||||||
bgGradient: 'linear-gradient(135deg, #F4E3A7, #D4AF37)',
|
bgGradient: 'linear-gradient(135deg, #F4E3A7, #D4AF37)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
创建微信支付订单
|
{priceInfo?.is_upgrade && priceInfo?.final_amount === 0
|
||||||
|
? '立即免费升级'
|
||||||
|
: '创建微信支付订单'}
|
||||||
</Button>
|
</Button>
|
||||||
</VStack>
|
</VStack>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ export const subscriptionConfig = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
question: 'Pro用户如何升级到Max?',
|
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吗?',
|
question: 'Max用户可以切换到Pro吗?',
|
||||||
|
|||||||
Reference in New Issue
Block a user