Last updated: Jul 25, 2025, 10:08 AM UTC

Hybrid Pricing Implementation Plan

Generated: 2025-07-23 21:00 UTC
Status: Implementation Plan
Region: us-east4
Verified: Based on project architecture

Overview

This document outlines the implementation plan for a hybrid pricing approach where:

  • Free Tier: Direct signup with email (no Stripe involvement)
  • Pro Tier: $10/month via Stripe Payment Links

This approach solves Stripe's $0.50 minimum payment limitation while maintaining a truly free tier.

Prerequisites

Before implementing the hybrid approach, ensure these components are ready:

1. Gmail API Setup COMPLETED

Gmail API is configured and ready to send welcome emails with API keys.

Status:

  • Gmail API enabled
  • Service account created: gmail-sender@convert-to-markdown-us-east4.iam.gserviceaccount.com
  • Service account key saved: deploy-gcp/keys/gmail-service-account.json
  • Domain-wide delegation configured in Google Workspace
  • Client ID 116940581280353324169 authorized with scope https://www.googleapis.com/auth/gmail.send

Email Sender: Set GMAIL_SENDER_EMAIL environment variable to the email address authorized in your Google Workspace (e.g., lindsay@knowcode.tech)

2. Firestore Database Required

Must have Firestore configured for storing user subscriptions.

Check:

# Verify Firestore is enabled
gcloud firestore databases list --project=YOUR_PROJECT_ID

3. Stripe Configuration Required for Pro Tier

  • Stripe account with products/prices created
  • Payment link for $10/month Pro subscription
  • Webhook endpoint configured
  • Customer portal enabled

Already completed from deployment:

  • Pro tier payment link: https://buy.stripe.com/bJe4gy1yQ9SJ2Mg1lNbfO07
  • Customer portal: https://billing.stripe.com/p/login/eVqfZgelCfd3cmQd4vbfO00

4. Project Configuration

  • Region: us-east4 (all functions must deploy here)
  • Project ID: Your GCP project ID
  • Domain: convert-to-markdown.knowcode.tech

5. Email Templates

Prepare welcome email templates for both tiers (see implementation below).

Architecture Overview

graph TD subgraph "Free Tier Flow" A[User Enters Email] --> B[Submit to /free-tier-signup] B --> C[Validate Email] C --> D[Generate API Key] D --> E[Store in Firestore] E --> F[Send Email via Gmail API] F --> G[User Receives API Key] end subgraph "Pro Tier Flow" H[User Clicks Pro] --> I[Stripe Payment Link] I --> J[Payment Processed] J --> K[Webhook Triggered] K --> L[Generate API Key] L --> M[Store in Firestore] M --> N[Send Email via Gmail API] N --> O[User Receives API Key] end

Implementation Steps

Step 1: Create Free Tier Signup Function

File: functions/free-tier-signup/index.js

const { Firestore } = require('@google-cloud/firestore');
const crypto = require('crypto');
const { sendWelcomeEmailFree } = require('../lib/email-gmail-api');

const db = new Firestore();

// API key generation functions
function generateApiKey() {
  const randomBytes = crypto.randomBytes(32).toString('hex');
  return `ctm_live_${randomBytes}`;
}

function hashApiKey(apiKey) {
  return crypto.createHash('sha256').update(apiKey).digest('hex');
}

// Email validation
function isValidEmail(email) {
  const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return re.test(email);
}

exports.freeTierSignup = async (req, res) => {
  // CORS headers for browser requests
  res.set('Access-Control-Allow-Origin', 'https://convert-to-markdown.knowcode.tech');
  res.set('Access-Control-Allow-Methods', 'POST, OPTIONS');
  res.set('Access-Control-Allow-Headers', 'Content-Type');
  
  if (req.method === 'OPTIONS') {
    res.status(204).send('');
    return;
  }
  
  // Only allow POST
  if (req.method !== 'POST') {
    res.status(405).json({ error: 'Method not allowed' });
    return;
  }
  
  const { email } = req.body;
  
  // Validate email
  if (!email || !isValidEmail(email)) {
    return res.status(400).json({ error: 'Please provide a valid email address' });
  }
  
  try {
    // Check if user already exists
    const existingUsers = await db.collection('subscriptions')
      .where('email', '==', email.toLowerCase())
      .limit(1)
      .get();
    
    if (!existingUsers.empty) {
      return res.status(400).json({ 
        error: 'This email is already registered. Check your inbox for your API key.' 
      });
    }
    
    // Rate limiting check (optional)
    // Could implement IP-based or email-based rate limiting here
    
    // Generate API key
    const apiKey = generateApiKey();
    const hashedApiKey = hashApiKey(apiKey);
    
    // Create unique user ID for free tier
    const userId = 'free_' + crypto.randomBytes(16).toString('hex');
    
    // Create user record
    const userDoc = {
      email: email.toLowerCase(),
      plan: 'free',
      status: 'active',
      apiKeyHash: hashedApiKey,
      monthlyUsage: 0,
      createdAt: new Date(),
      updatedAt: new Date(),
      source: 'web_signup',
      features: {
        maxConversions: 50,
        maxFileSize: 5242880, // 5MB in bytes
        priority: false,
        dashboard: false
      }
    };
    
    // Save to Firestore
    await db.collection('subscriptions').doc(userId).set(userDoc);
    
    // Send welcome email
    await sendWelcomeEmailFree(email, apiKey);
    
    // Log for monitoring
    console.log(`New free tier signup: ${email}`);
    
    res.json({ 
      success: true, 
      message: 'Check your email for your API key!' 
    });
    
  } catch (error) {
    console.error('Signup error:', error);
    res.status(500).json({ 
      error: 'An error occurred during signup. Please try again.' 
    });
  }
};

// Welcome email for free tier
async function sendWelcomeEmailFree(email, apiKey) {
  const subject = 'Welcome to Convert To Markdown - Your API Key Inside! 🎉';
  
  const htmlContent = `
<!DOCTYPE html>
<html>
<head>
  <style>
    body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
    .container { max-width: 600px; margin: 0 auto; padding: 20px; }
    .header { background: #667eea; color: white; padding: 20px; text-align: center; border-radius: 8px 8px 0 0; }
    .content { background: #f8f9fa; padding: 30px; border-radius: 0 0 8px 8px; }
    .api-key { background: #1e293b; color: #34d399; padding: 15px; border-radius: 6px; font-family: monospace; font-size: 14px; word-break: break-all; }
    .warning { background: #fef3c7; border: 1px solid #f59e0b; padding: 15px; border-radius: 6px; margin: 20px 0; }
    .features { background: white; padding: 20px; border-radius: 6px; margin: 20px 0; }
    .cta { background: #667eea; color: white; padding: 12px 24px; text-decoration: none; border-radius: 6px; display: inline-block; margin-top: 20px; }
  </style>
</head>
<body>
  <div class="container">
    <div class="header">
      <h1>Welcome to Convert To Markdown!</h1>
      <p>Your free account is now active</p>
    </div>
    <div class="content">
      <h2>Here's your API key:</h2>
      <div class="api-key">${apiKey}</div>
      
      <div class="warning">
        <strong>⚠️ Important:</strong> Save this API key securely. For security reasons, we don't store it and can't retrieve it for you. If you lose it, you'll need to create a new account.
      </div>
      
      <div class="features">
        <h3>Your Free Plan includes:</h3>
        <ul>
          <li>✅ 50 conversions per month</li>
          <li>✅ 5MB max file size</li>
          <li>✅ All file formats (Excel, Word, PDF, PowerPoint)</li>
          <li>✅ RESTful API access</li>
          <li>✅ Basic email support</li>
        </ul>
      </div>
      
      <h3>Quick Start:</h3>
      <p>Try your first conversion with this curl command:</p>
      <div style="background: #f1f5f9; padding: 15px; border-radius: 6px; font-family: monospace; font-size: 12px; overflow-x: auto;">
        curl -X POST https://convert-to-markdown.knowcode.tech/xlsx-converter \\<br>
        &nbsp;&nbsp;-H "x-api-key: ${apiKey}" \\<br>
        &nbsp;&nbsp;-F "file=@your-file.xlsx"
      </div>
      
      <p>Need more conversions? Upgrade to Pro for just $10/month and get:</p>
      <ul>
        <li>🚀 10,000 conversions per month (200x more!)</li>
        <li>📁 50MB file size limit</li>
        <li>📊 Usage dashboard</li>
        <li>⚡ Priority processing</li>
      </ul>
      
      <center>
        <a href="https://convert-to-markdown.knowcode.tech/pricing" class="cta">View API Documentation</a>
      </center>
      
      <p style="margin-top: 30px; color: #666;">
        Happy converting!<br>
        The Convert To Markdown Team
      </p>
      
      <hr style="margin: 30px 0; border: none; border-top: 1px solid #e2e8f0;">
      
      <p style="font-size: 12px; color: #666; text-align: center;">
        You received this email because you signed up for Convert To Markdown.<br>
        If you didn't sign up, please ignore this email.
      </p>
    </div>
  </div>
</body>
</html>
`;
  
  const textContent = `
Welcome to Convert To Markdown!

Your API Key:
${apiKey}

⚠️ IMPORTANT: Save this key securely - we can't retrieve it for you!

Your Free Plan includes:
- 50 conversions per month
- 5MB max file size  
- All file formats supported
- RESTful API access

Quick Start:
curl -X POST https://convert-to-markdown.knowcode.tech/xlsx-converter \\
  -H "x-api-key: ${apiKey}" \\
  -F "file=@your-file.xlsx"

Need more? Upgrade to Pro for $10/month:
https://convert-to-markdown.knowcode.tech/pricing

Happy converting!
The Convert To Markdown Team
`;
  
  await sendEmail({
    to: email,
    subject: subject,
    text: textContent,
    html: htmlContent
  });
}

File: functions/free-tier-signup/package.json

{
  "name": "free-tier-signup",
  "version": "1.0.0",
  "main": "index.js",
  "dependencies": {
    "@google-cloud/firestore": "^7.0.0",
    "googleapis": "^105.0.0"
  }
}

Step 2: Update Pricing Page

File: docs/pricing.md

Replace the Free Tier card button section with:

<!-- Replace the button/link with this form -->
<form id="free-tier-signup" style="margin-top: 20px;" onsubmit="return handleFreeSignup(event)">
  <input type="email" 
    id="free-email-input"
    name="email"
    placeholder="Enter your email address" 
    required
    style="width: 100%; padding: 12px; margin-bottom: 10px; border: 1px solid #e2e8f0; border-radius: 6px; font-size: 16px;">
  <button type="submit" 
    id="free-submit-btn"
    style="width: 100%; padding: 14px; background: #64748b; color: white; border: none; border-radius: 6px; font-weight: bold; cursor: pointer; font-size: 16px; transition: background 0.2s;">
    Get Your Free API Key →
  </button>
</form>
<p style="text-align: center; margin-top: 10px; font-size: 0.875rem; color: #666;">
  No credit card required • Instant access
</p>
<div id="free-signup-message" style="display: none; margin-top: 15px; padding: 12px; border-radius: 6px; text-align: center;"></div>

<script>
async function handleFreeSignup(event) {
  event.preventDefault();
  
  const form = event.target;
  const email = form.email.value;
  const button = document.getElementById('free-submit-btn');
  const messageDiv = document.getElementById('free-signup-message');
  const originalButtonText = button.textContent;
  
  // Reset any previous messages
  messageDiv.style.display = 'none';
  
  // Disable form during submission
  button.disabled = true;
  button.textContent = 'Creating your account...';
  button.style.background = '#94a3b8';
  
  try {
    const response = await fetch('https://us-east4-YOUR_PROJECT_ID.cloudfunctions.net/free-tier-signup', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ email: email })
    });
    
    const result = await response.json();
    
    if (response.ok) {
      // Success
      messageDiv.style.display = 'block';
      messageDiv.style.background = '#dcfce7';
      messageDiv.style.color = '#166534';
      messageDiv.style.border = '1px solid #86efac';
      messageDiv.innerHTML = '✅ <strong>Success!</strong> Check your email for your API key.';
      
      // Hide the form
      form.style.display = 'none';
      
      // Track conversion if analytics is set up
      if (typeof gtag !== 'undefined') {
        gtag('event', 'sign_up', {
          method: 'email',
          value: 'free_tier'
        });
      }
    } else {
      // Error
      messageDiv.style.display = 'block';
      messageDiv.style.background = '#fee2e2';
      messageDiv.style.color = '#991b1b';
      messageDiv.style.border = '1px solid #fca5a5';
      messageDiv.textContent = result.error || 'Signup failed. Please try again.';
      
      // Re-enable form
      button.disabled = false;
      button.textContent = originalButtonText;
      button.style.background = '#64748b';
    }
  } catch (error) {
    // Network or other error
    console.error('Signup error:', error);
    messageDiv.style.display = 'block';
    messageDiv.style.background = '#fee2e2';
    messageDiv.style.color = '#991b1b';
    messageDiv.style.border = '1px solid #fca5a5';
    messageDiv.textContent = 'Network error. Please check your connection and try again.';
    
    // Re-enable form
    button.disabled = false;
    button.textContent = originalButtonText;
    button.style.background = '#64748b';
  }
  
  return false;
}
</script>

Step 3: Deploy Free Tier Signup Function

# Deploy the function
gcloud functions deploy free-tier-signup \
  --gen2 \
  --runtime=nodejs20 \
  --region=us-east4 \
  --source=functions/free-tier-signup \
  --entry-point=freeTierSignup \
  --trigger-http \
  --allow-unauthenticated \
  --set-env-vars="PROJECT_ID=YOUR_PROJECT_ID" \
  --memory=256MB \
  --timeout=30s \
  --max-instances=10

Step 4: Use Gmail API Email Library

The Gmail API email library is already created at functions/lib/email-gmail-api.js and includes:

  • Gmail API integration using domain-wide delegation
  • Service account authentication
  • Welcome email templates for both Free and Pro tiers
  • Error handling and logging

Key functions:

  • sendEmail({ to, subject, text, html }) - Generic email sending
  • sendWelcomeEmailFree(email, apiKey) - Free tier welcome email
  • sendWelcomeEmailPro(email, apiKey) - Pro tier welcome email

Step 5: Update Webhook Handler for Pro Tier

The existing Stripe webhook handler should already handle Pro subscriptions. Ensure it uses the same Firestore schema as the free tier signup.

Step 6: Testing Checklist

  1. Free Tier Signup:

    • Email validation works
    • Duplicate email check works
    • API key generation works
    • Email delivery works
    • Firestore record created
    • UI shows success message
    • Form is hidden after success
  2. Pro Tier Signup:

    • Stripe payment link works
    • Webhook receives event
    • API key generated
    • Email sent
    • Firestore record created
  3. API Key Validation:

    • Free tier keys work
    • Pro tier keys work
    • Usage limits enforced
    • Monthly reset works

Step 7: Monitoring

Set up monitoring for:

  • Free tier signup success/failure rate
  • Email delivery success
  • API key validation performance
  • Conversion rate (visits → signups)

Security Considerations

  1. Rate Limiting: Implement rate limiting on the free tier signup endpoint
  2. Email Validation: Consider adding email verification step
  3. CAPTCHA: Add reCAPTCHA if spam becomes an issue
  4. CORS: Restrict to your domain only
  5. API Key Security: Never log or expose raw API keys

Rollback Plan

If issues arise:

  1. Remove inline form from pricing page
  2. Revert to a single pricing approach
  3. Keep existing Pro tier Stripe flow
  4. All existing API keys continue working

Timeline

  • Day 1: Deploy free tier signup function
  • Day 2: Update pricing page with inline form
  • Day 3: Test and monitor
  • Day 4: Full rollout

Success Metrics

  • Free tier signup conversion rate > 5%
  • Email delivery success rate > 99%
  • API key validation latency < 100ms
  • User satisfaction (support tickets)

Next Steps:

  1. Verify all prerequisites are met
  2. Deploy the free tier signup function
  3. Update the pricing page
  4. Test end-to-end flow
  5. Monitor initial signups