started implementing stripe
This commit is contained in:
281
routes/main.py
281
routes/main.py
@@ -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)
|
||||
Reference in New Issue
Block a user