started implementing stripe

This commit is contained in:
2025-06-26 15:15:16 +02:00
parent 3a0659b63b
commit 9b85f3bb8d
24 changed files with 2025 additions and 103 deletions

View File

@@ -1,7 +1,8 @@
from flask import render_template, Blueprint, redirect, url_for, request, flash, Response, jsonify, session, current_app
from flask_login import current_user, login_required
from models import User, db, Room, RoomFile, RoomMemberPermission, SiteSettings, Event, Conversation, Message, MessageAttachment, Notif, EmailTemplate, Mail, KeyValueSettings, DocuPulseSettings, PasswordSetupToken, Instance, ManagementAPIKey
from models import User, db, Room, RoomFile, RoomMemberPermission, SiteSettings, Event, Conversation, Message, MessageAttachment, Notif, EmailTemplate, Mail, KeyValueSettings, DocuPulseSettings, PasswordSetupToken, Instance, ManagementAPIKey, Customer, PricingPlan
from routes.auth import require_password_change
from extensions import csrf
import os
from werkzeug.utils import secure_filename
from sqlalchemy import func, case, literal_column, text
@@ -20,6 +21,7 @@ import requests
from functools import wraps
import socket
from urllib.parse import urlparse
import stripe
# Set up logging to show in console
logging.basicConfig(
@@ -1350,6 +1352,7 @@ def init_routes(main_bp):
nginx_settings = KeyValueSettings.get_value('nginx_settings')
git_settings = KeyValueSettings.get_value('git_settings')
cloudflare_settings = KeyValueSettings.get_value('cloudflare_settings')
stripe_settings = KeyValueSettings.get_value('stripe_settings')
# Get management API key for the connections tab
management_api_key = ManagementAPIKey.query.filter_by(is_active=True).first()
@@ -1427,6 +1430,7 @@ def init_routes(main_bp):
nginx_settings=nginx_settings,
git_settings=git_settings,
cloudflare_settings=cloudflare_settings,
stripe_settings=stripe_settings,
pricing_plans=pricing_plans,
csrf_token=generate_csrf())
@@ -2125,13 +2129,12 @@ def init_routes(main_bp):
email = data.get('email')
api_key = data.get('api_key')
zone_id = data.get('zone_id')
server_ip = data.get('server_ip')
if not email or not api_key or not zone_id or not server_ip:
if not email or not api_key or not zone_id:
return jsonify({'error': 'Missing required fields'}), 400
try:
# Test Cloudflare connection by getting zone details
# Test Cloudflare connection
headers = {
'X-Auth-Email': email,
'X-Auth-Key': api_key,
@@ -2142,21 +2145,77 @@ def init_routes(main_bp):
response = requests.get(
f'https://api.cloudflare.com/client/v4/zones/{zone_id}',
headers=headers,
timeout=10
timeout=5
)
if response.status_code == 200:
zone_data = response.json()
if zone_data.get('success'):
return jsonify({'message': 'Connection successful'})
else:
return jsonify({'error': f'API error: {zone_data.get("errors", [{}])[0].get("message", "Unknown error")}'}), 400
return jsonify({'message': 'Connection successful'})
else:
return jsonify({'error': f'Connection failed: HTTP {response.status_code}'}), 400
return jsonify({'error': f'Connection failed: {response.json().get("errors", [{}])[0].get("message", "Unknown error")}'}), 400
except Exception as e:
return jsonify({'error': f'Connection failed: {str(e)}'}), 400
@main_bp.route('/settings/save-stripe-connection', methods=['POST'])
@login_required
def save_stripe_connection():
if not current_user.is_admin:
return jsonify({'error': 'Unauthorized'}), 403
data = request.get_json()
publishable_key = data.get('publishable_key')
secret_key = data.get('secret_key')
webhook_secret = data.get('webhook_secret')
test_mode = data.get('test_mode', False)
customer_portal_url = data.get('customer_portal_url', '')
if not publishable_key or not secret_key:
return jsonify({'error': 'Missing required fields'}), 400
try:
# Save Stripe settings
KeyValueSettings.set_value('stripe_settings', {
'publishable_key': publishable_key,
'secret_key': secret_key,
'webhook_secret': webhook_secret,
'test_mode': test_mode,
'customer_portal_url': customer_portal_url
})
return jsonify({'message': 'Settings saved successfully'})
except Exception as e:
return jsonify({'error': str(e)}), 500
@main_bp.route('/settings/test-stripe-connection', methods=['POST'])
@login_required
def test_stripe_connection():
if not current_user.is_admin:
return jsonify({'error': 'Unauthorized'}), 403
data = request.get_json()
secret_key = data.get('secret_key')
if not secret_key:
return jsonify({'error': 'Missing required fields'}), 400
try:
# Test Stripe connection by making a simple API call
import stripe
stripe.api_key = secret_key
# Try to get account information
account = stripe.Account.retrieve()
return jsonify({'message': 'Connection successful'})
except stripe.error.AuthenticationError:
return jsonify({'error': 'Invalid API key'}), 400
except stripe.error.StripeError as e:
return jsonify({'error': f'Stripe error: {str(e)}'}), 400
except Exception as e:
return jsonify({'error': f'Connection failed: {str(e)}'}), 400
@main_bp.route('/instances/launch-progress')
@login_required
@require_password_change
@@ -2412,4 +2471,202 @@ def init_routes(main_bp):
'commit': commit,
'branch': branch,
'deployed_at': deployed_at
})
})
@main_bp.route('/api/create-checkout-session', methods=['POST'])
@csrf.exempt
def create_checkout_session():
"""Create a Stripe checkout session for a pricing plan"""
current_app.logger.info("=== CHECKOUT SESSION REQUEST START ===")
current_app.logger.info(f"Request method: {request.method}")
current_app.logger.info(f"Request headers: {dict(request.headers)}")
current_app.logger.info(f"Request data: {request.get_data()}")
try:
from utils.stripe_utils import create_checkout_session
data = request.get_json()
current_app.logger.info(f"Parsed JSON data: {data}")
plan_id = data.get('plan_id')
billing_cycle = data.get('billing_cycle', 'monthly')
current_app.logger.info(f"Plan ID: {plan_id}")
current_app.logger.info(f"Billing cycle: {billing_cycle}")
if not plan_id:
current_app.logger.error("Plan ID is missing")
return jsonify({'error': 'Plan ID is required'}), 400
if billing_cycle not in ['monthly', 'annual']:
current_app.logger.error(f"Invalid billing cycle: {billing_cycle}")
return jsonify({'error': 'Invalid billing cycle'}), 400
current_app.logger.info("Calling create_checkout_session function...")
# Create checkout session
checkout_url = create_checkout_session(
plan_id=plan_id,
billing_cycle=billing_cycle,
success_url=url_for('main.checkout_success', _external=True),
cancel_url=url_for('main.public_home', _external=True)
)
current_app.logger.info(f"Checkout URL created: {checkout_url}")
response_data = {
'success': True,
'checkout_url': checkout_url
}
current_app.logger.info(f"Returning response: {response_data}")
return jsonify(response_data)
except Exception as e:
current_app.logger.error(f"Error creating checkout session: {str(e)}")
current_app.logger.error(f"Exception type: {type(e)}")
import traceback
current_app.logger.error(f"Traceback: {traceback.format_exc()}")
return jsonify({'error': str(e)}), 500
finally:
current_app.logger.info("=== CHECKOUT SESSION REQUEST END ===")
@main_bp.route('/api/checkout-success')
def checkout_success():
"""Handle successful checkout"""
session_id = request.args.get('session_id')
subscription_info = None
# Get Stripe settings for customer portal link
stripe_settings = KeyValueSettings.get_value('stripe_settings')
if session_id:
try:
from utils.stripe_utils import get_subscription_info
from models import Customer, PricingPlan
subscription_info = get_subscription_info(session_id)
# Log the subscription info for debugging
current_app.logger.info(f"Checkout success - Session ID: {session_id}")
current_app.logger.info(f"Subscription info: {subscription_info}")
# Save or update customer information
if 'customer_details' in subscription_info:
customer_details = subscription_info['customer_details']
current_app.logger.info(f"Customer details: {customer_details}")
# Try to find existing customer by email
customer = Customer.query.filter_by(email=customer_details.get('email')).first()
if customer:
# Update existing customer
current_app.logger.info(f"Updating existing customer: {customer.email}")
else:
# Create new customer
customer = Customer()
current_app.logger.info(f"Creating new customer: {customer_details.get('email')}")
# Update customer information
customer.email = customer_details.get('email')
customer.name = customer_details.get('name')
customer.phone = customer_details.get('phone')
# Update billing address
if customer_details.get('address'):
address = customer_details['address']
customer.billing_address_line1 = address.get('line1')
customer.billing_address_line2 = address.get('line2')
customer.billing_city = address.get('city')
customer.billing_state = address.get('state')
customer.billing_postal_code = address.get('postal_code')
customer.billing_country = address.get('country')
# Update shipping address
if customer_details.get('shipping'):
shipping = customer_details['shipping']
customer.shipping_address_line1 = shipping.get('address', {}).get('line1')
customer.shipping_address_line2 = shipping.get('address', {}).get('line2')
customer.shipping_city = shipping.get('address', {}).get('city')
customer.shipping_state = shipping.get('address', {}).get('state')
customer.shipping_postal_code = shipping.get('address', {}).get('postal_code')
customer.shipping_country = shipping.get('address', {}).get('country')
# Update tax information
if customer_details.get('tax_ids'):
tax_ids = customer_details['tax_ids']
if tax_ids:
# Store the first tax ID (most common case)
customer.tax_id_type = tax_ids[0].get('type')
customer.tax_id_value = tax_ids[0].get('value')
# Update Stripe and subscription information
customer.stripe_customer_id = subscription_info.get('customer_id')
customer.stripe_subscription_id = subscription_info.get('subscription_id')
customer.subscription_status = subscription_info.get('status')
customer.subscription_plan_id = subscription_info.get('plan_id')
customer.subscription_billing_cycle = subscription_info.get('billing_cycle')
customer.subscription_current_period_start = subscription_info.get('current_period_start')
customer.subscription_current_period_end = subscription_info.get('current_period_end')
# Save to database
if not customer.id:
db.session.add(customer)
db.session.commit()
current_app.logger.info(f"Customer saved successfully: {customer.email}")
except Exception as e:
current_app.logger.error(f"Error processing checkout success: {str(e)}")
flash('Payment successful, but there was an issue processing your subscription. Please contact support.', 'warning')
# Render the success page with subscription info and stripe settings
return render_template('checkout_success.html', subscription_info=subscription_info, stripe_settings=stripe_settings)
@main_bp.route('/api/debug/pricing-plans')
@login_required
def debug_pricing_plans():
"""Debug endpoint to check pricing plans"""
try:
from models import PricingPlan
plans = PricingPlan.query.all()
plans_data = []
for plan in plans:
plans_data.append({
'id': plan.id,
'name': plan.name,
'monthly_price': plan.monthly_price,
'annual_price': plan.annual_price,
'stripe_product_id': plan.stripe_product_id,
'stripe_monthly_price_id': plan.stripe_monthly_price_id,
'stripe_annual_price_id': plan.stripe_annual_price_id,
'is_custom': plan.is_custom,
'button_text': plan.button_text
})
return jsonify({
'success': True,
'plans': plans_data,
'count': len(plans_data)
})
except Exception as e:
current_app.logger.error(f"Error getting pricing plans: {str(e)}")
return jsonify({'error': str(e)}), 500
@main_bp.route('/preview-success')
def preview_success():
"""Preview the checkout success page with sample data"""
# Get Stripe settings for customer portal link
stripe_settings = KeyValueSettings.get_value('stripe_settings')
sample_subscription_info = {
'plan_name': 'Professional Plan',
'billing_cycle': 'monthly',
'status': 'active',
'amount': 29.99,
'currency': 'usd'
}
return render_template('checkout_success.html', subscription_info=sample_subscription_info, stripe_settings=stripe_settings)