""" QBO Excel Sync - OAuth Callback Server This is a simple Flask server that handles OAuth callbacks from QuickBooks. Host this on your own server (e.g., https://yourcompany.com/oauth/callback) Usage: 1. Deploy this to your server 2. Set up SSL (required for QuickBooks production) 3. Register the callback URL in your QuickBooks app settings 4. Update the desktop app's redirect_uri to match Environment Variables: - PORT: Server port (default: 5000) - SECRET_KEY: Flask secret key for sessions """ import os import logging from datetime import datetime from flask import Flask, request, render_template_string, jsonify # Configure logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) logger = logging.getLogger(__name__) app = Flask(__name__) app.secret_key = os.environ.get('SECRET_KEY', 'your-secret-key-change-in-production') # HTML template for displaying the authorization code CALLBACK_TEMPLATE = """ QBO Excel Sync - Authorization
{% if error %}

Authorization Failed

Error: {{ error }}
{% if error_description %} Details: {{ error_description }} {% endif %}
{% else %}

Authorization Successful!

Copy the information below and paste it into the QBO Excel Sync app.

Company ID (Realm ID): {{ realm_id }}
Authorization Code
{{ code }}
Next Steps:
  1. Go back to the QBO Excel Sync desktop application
  2. Click "Enter Code Manually" button
  3. Paste the Authorization Code and Company ID
  4. Click Connect
{% endif %}
""" @app.route('/') def index(): """Health check endpoint.""" return jsonify({ 'status': 'ok', 'service': 'QBO Excel Sync OAuth Callback Server', 'timestamp': datetime.now().isoformat() }) @app.route('/oauth/callback') def oauth_callback(): """ Handle OAuth callback from QuickBooks. QuickBooks redirects here with: - code: Authorization code (on success) - realmId: Company ID - state: State parameter we sent - error: Error code (on failure) - error_description: Error details (on failure) """ # Get parameters code = request.args.get('code') realm_id = request.args.get('realmId') state = request.args.get('state') error = request.args.get('error') error_description = request.args.get('error_description') # Log the callback logger.info(f"OAuth callback received - realm_id: {realm_id}, state: {state}, error: {error}") if error: logger.error(f"OAuth error: {error} - {error_description}") return render_template_string( CALLBACK_TEMPLATE, error=error, error_description=error_description, year=datetime.now().year ) if not code: logger.error("No authorization code received") return render_template_string( CALLBACK_TEMPLATE, error="No authorization code received", error_description="The authorization server did not return a code.", year=datetime.now().year ) # Success - display the code to the user logger.info(f"Authorization successful for realm {realm_id}") return render_template_string( CALLBACK_TEMPLATE, code=code, realm_id=realm_id, state=state, error=None, year=datetime.now().year ) @app.route('/oauth/callback/json') def oauth_callback_json(): """ JSON endpoint for programmatic access. Can be used if you want to implement automatic code relay. """ code = request.args.get('code') realm_id = request.args.get('realmId') state = request.args.get('state') error = request.args.get('error') error_description = request.args.get('error_description') if error: return jsonify({ 'success': False, 'error': error, 'error_description': error_description }), 400 return jsonify({ 'success': True, 'code': code, 'realm_id': realm_id, 'state': state }) if __name__ == '__main__': port = int(os.environ.get('PORT', 5000)) debug = os.environ.get('DEBUG', 'false').lower() == 'true' print(f""" ╔═══════════════════════════════════════════════════════════════╗ ║ QBO Excel Sync - OAuth Callback Server ║ ╠═══════════════════════════════════════════════════════════════╣ ║ Server running on port {port} ║ ║ Callback URL: http://localhost:{port}/oauth/callback ║ ║ ║ ║ For production, deploy with HTTPS (required by QuickBooks) ║ ║ Example: https://yourcompany.com/oauth/callback ║ ╚═══════════════════════════════════════════════════════════════╝ """) app.run(host='0.0.0.0', port=port, debug=debug)