let’s add a “Save to Google Docs” export that converts your plan into a native Google Doc (not just a file upload). We’ll reuse the DOCX generator, then upload to Google Drive with conversion → application/vnd.google-apps.document.

Below is a complete, drop-in prompt for Replit + full code (Node/Express + TS). It uses Google Identity Services on the frontend to fetch a user access token, and the backend uploads the DOCX buffer to Drive, converting it into a Google Doc. It’s paywall-aware.

Prompt for Replit
Task: Add "Save to Google Docs" export for Business Plan (Pro-only)

Overview
- Frontend:
  - Add Google Sign-In helper using Google Identity Services (popup).
  - Add "Save to Google Docs" button beside Export PDF/DOCX.
  - Button obtains OAuth access token and posts plan to backend for conversion upload.

- Backend:
  - Route: POST /api/ai/plan/export/google-doc
  - Accepts: { plan: PlanResponse, businessName?: string, accessToken: string }
  - Requires: req.user.isPaid === true
  - Generates DOCX buffer via existing renderPlanDOCX(plan, meta).
  - Uploads to Google Drive with conversion to native Google Doc by setting requestBody.mimeType = "application/vnd.google-apps.document".
  - Returns: { fileId, fileUrl } where fileUrl is the browser URL to the created Google Doc.

Dependencies
- npm i googleapis
- Ensure pdf/docx export service file exists (exportTemplates.ts from previous step).
- Add GOOGLE_CLIENT_ID (frontend) to env and inject into page. No server secret needed for this flow because we use the user’s access token from Google Identity Services.
- Scopes: "https://www.googleapis.com/auth/drive.file"

Acceptance
- When Pro user clicks "Save to Google Docs":
  - If not signed in with Google, prompt sign-in.
  - On success, new Google Doc is created with correct title and content.
  - Server returns link; frontend toasts success and opens the doc in a new tab.
- If unpaid → 402 and open Paywall.
- Error states handled gracefully.

1) Frontend: Google Sign-In helper

src/services/googleAuth.ts

// Simple Google Identity Services helper (popup flow)
// Make sure you expose GOOGLE_CLIENT_ID to the client (e.g., via public env).
// Add <script src="https://accounts.google.com/gsi/client" async defer></script> to index.html

declare global {
  interface Window {
    google?: any;
  }
}

const CLIENT_ID = import.meta.env.VITE_GOOGLE_CLIENT_ID as string;

export async function getGoogleAccessToken(): Promise<string> {
  if (!window.google) throw new Error("Google API not loaded");
  if (!CLIENT_ID) throw new Error("Missing VITE_GOOGLE_CLIENT_ID");

  return new Promise((resolve, reject) => {
    try {
      // Popup token flow: drive.file scope lets app create/edit files it creates
      window.google.accounts.oauth2.initTokenClient({
        client_id: CLIENT_ID,
        scope: "https://www.googleapis.com/auth/drive.file",
        prompt: "consent",
        callback: (resp: any) => {
          if (resp && resp.access_token) resolve(resp.access_token);
          else reject(new Error("Failed to get Google access token"));
        },
      }).requestAccessToken();
    } catch (e) {
      reject(e);
    }
  });
}


In your main HTML (or index.html), add:
<script src="https://accounts.google.com/gsi/client" async defer></script>

2) Frontend: Export-to-Google-Doc service

src/services/planExportGoogle.ts

export type PlanMode = "lean" | "full";
export type PlanSection = { id: string; title: string; content: string };
export type PlanResponse = {
  mode: PlanMode;
  sections: PlanSection[];
  finance?: {
    assumptions: string[];
    monthly_table?: Array<{ month: string; revenue: number; costs: number; profit: number }>;
  };
};

export async function exportPlanToGoogleDoc(plan: PlanResponse, businessName: string, accessToken: string) {
  const res = await fetch("/api/ai/plan/export/google-doc", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ plan, businessName, accessToken }),
  });

  if (!res.ok) {
    if (res.status === 402) throw new Error("PAYWALL");
    const t = await res.text().catch(() => "");
    throw new Error(t || "Google Docs export failed");
  }

  return res.json() as Promise<{ fileId: string; fileUrl: string }>;
}

3) Frontend: Wire the button in your Plan page

In BusinessPlanPage.tsx (where you render results and the Export buttons):

import { getGoogleAccessToken } from "@/services/googleAuth";
import { exportPlanToGoogleDoc } from "@/services/planExportGoogle";
import { Button } from "@/components/ui/button";
import { toast } from "sonner"; // or your toaster

// ...
const handleSaveToGoogleDocs = async () => {
  try {
    // Paywall check is also enforced server-side, but UI can pre-check if you have isPaid in state.
    const accessToken = await getGoogleAccessToken();
    const { fileUrl } = await exportPlanToGoogleDoc(planResponse, businessNameInputValue || "Business", accessToken);
    toast.success("Saved to Google Docs!");
    window.open(fileUrl, "_blank");
  } catch (e: any) {
    if (e.message === "PAYWALL") {
      openPaywallModal({ source: "plan_export_google" });
      return;
    }
    toast.error(e.message || "Could not save to Google Docs.");
  }
};

// In JSX, Pro-only (else show Upgrade CTA):
<Button onClick={handleSaveToGoogleDocs}>Save to Google Docs</Button>

4) Backend: Google Doc export route

server/routes/ai/planExportGoogle.ts

import { Router } from "express";
import { google } from "googleapis";
import { renderPlanDOCX, PlanResponse } from "../../services/exportTemplates";

const router = Router();

/** Replace with your real auth middleware */
function requireAuth(req: any, res: any, next: any) {
  if (!req.user) return res.status(401).json({ error: "Unauthorized" });
  next();
}

router.post("/api/ai/plan/export/google-doc", requireAuth, async (req: any, res) => {
  try {
    const { plan, businessName, accessToken } = req.body as {
      plan: PlanResponse;
      businessName?: string;
      accessToken?: string;
    };

    if (!req.user?.isPaid) {
      return res.status(402).json({ error: "Payment required", code: "PAYWALL" });
    }
    if (!plan || !accessToken) {
      return res.status(400).json({ error: "Missing plan or access token" });
    }

    const name = (businessName || "Your Business").trim();
    const meta = { businessName: name, subtitle: "Business Plan", generatedAt: new Date() };

    // 1) Render a DOCX buffer (we'll ask Drive to convert it)
    const docxBuffer = await renderPlanDOCX(plan, meta);

    // 2) Use user's access token to upload & convert to Google Doc
    const oauth2 = new google.auth.OAuth2();
    oauth2.setCredentials({ access_token: accessToken });

    const drive = google.drive({ version: "v3", auth: oauth2 });

    // drive.files.create with requestBody.mimeType set to Google Docs converts the upload
    const createResp = await drive.files.create({
      requestBody: {
        name: `${name} — Business Plan`,
        mimeType: "application/vnd.google-apps.document", // target: Google Doc (conversion)
      },
      media: {
        mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
        body: BufferToStream(docxBuffer),
      },
      fields: "id, webViewLink",
    });

    const fileId = createResp.data.id!;
    const fileUrl = createResp.data.webViewLink!;

    return res.json({ fileId, fileUrl });
  } catch (err: any) {
    console.error("google-doc export error:", err?.response?.data || err);
    return res.status(500).json({ error: "Failed to save to Google Docs" });
  }
});

// Helper: Buffer → Readable stream
import { Readable } from "stream";
function BufferToStream(buffer: Buffer) {
  const readable = new Readable();
  readable.push(buffer);
  readable.push(null);
  return readable;
}

export default router;


Add to your server entry (e.g., server/index.ts):
app.use(require('./routes/ai/planExportGoogle').default);

Install dependency:

npm i googleapis

5) Security & Notes

This flow uses the user’s Google access token (no server secret storage).

Ensure your front-end domain is added to your OAuth consent in Google Cloud Console for the client ID.

Scope drive.file allows creating/editing files your app creates (safer than full Drive access).

If you prefer server-side OAuth (refresh tokens), we can wire that later—this is the fastest path now.

6) UX Copy

Success toast: “Saved to Google Docs! Opening now…”

Paywall CTA (if unpaid): “Upgrade to save plans directly to Google Docs.”