Payment System Integration
Run ID: 69a79982ca66473bfe6fb36e2026-03-29E-commerce
PantheraHive BOS
BOS Dashboard

This document outlines the technical and strategic blueprint for integrating a robust subscription payment system using Next.js and Stripe. It covers the end-to-end process from product catalog definition to checkout, payment processing, and conversion optimization, ensuring a scalable and secure solution for a professional workflow tool with tiered subscriptions.

1. Product Catalog Structure

The subscription tiers ($9/mo, $29/mo, $99/mo) will be defined as 'Products' and 'Prices' within Stripe. Each tier will have a unique Stripe Product ID and corresponding Price IDs for monthly recurring billing in USD.

Stripe Configuration:

  • Product 1: Basic Plan

* Name: Basic

* Description: Essential features for individual users.

* Price 1: $9.00 USD / month (recurring)

  • Product 2: Pro Plan

* Name: Pro

* Description: Advanced features for small teams.

* Price 1: $29.00 USD / month (recurring)

  • Product 3: Enterprise Plan

* Name: Enterprise

* Description: Comprehensive features for large organizations.

* Price 1: $99.00 USD / month (recurring)

Application Database Schema (Example using PostgreSQL/Prisma):

  • SubscriptionPlan table:

* id (UUID, Primary Key)

* name (String, e.g., 'Basic', 'Pro', 'Enterprise')

* description (Text)

* stripeProductId (String, unique Stripe Product ID)

* stripePriceId (String, unique Stripe Price ID for the monthly option)

* amount (Decimal, e.g., 9.00, 29.00, 99.00)

* currency (String, 'USD')

* interval (String, 'month')

* features (JSONB or Array of Strings, detailing plan benefits)

* isActive (Boolean, default true)

This structure allows for easy management and display of subscription options within the Next.js frontend, while Stripe handles the core product and pricing logic.

2. Checkout Flow

The checkout flow will be designed for simplicity and conversion, leveraging Stripe's robust capabilities.

  1. Plan Selection: User navigates to a dedicated 'Pricing' page displaying the three subscription tiers with their features and 'Subscribe' CTAs.
  2. User Authentication/Account Creation:

* If the user is not logged in, they will be prompted to sign up or log in before proceeding to payment.

* Upon successful login/signup, their user session will be established.

  1. Initiate Checkout Session (Next.js API Route):

* When a user clicks 'Subscribe' for a chosen plan, a server-side API route (e.g., /api/checkout_session) will be invoked.

* This API route will create a Stripe Checkout Session, specifying the stripePriceId of the selected plan, mode: 'subscription', success_url, cancel_url, and optionally customer_email (if logged in).

* The success_url will point to a confirmation page, and the cancel_url back to the pricing page.

  1. Redirect to Stripe Checkout: The Next.js frontend will receive the session.url from the API route and redirect the user to the Stripe-hosted checkout page.
  2. Payment Information Collection: On the Stripe Checkout page, the user will securely enter their payment details (credit card, etc.). Stripe handles PCI compliance.
  3. Payment Processing & Confirmation:

* Upon successful payment, Stripe redirects the user to the success_url defined in step 3.

* Simultaneously, Stripe sends a checkout.session.completed webhook event to our Next.js backend.

  1. Post-Checkout Provisioning: The webhook handler (see Payment Processing) will update the user's subscription status in our database and grant access to the corresponding features of the workflow tool.

3. Payment Processing (Stripe Integration)

Stripe will be the sole payment gateway, handling all aspects of subscription billing, recurring payments, and payment method management.

Key Components:

  • Stripe API Integration (Next.js API Routes):

* Create Checkout Session: As described in the checkout flow, a serverless function (Next.js API route) will interact with stripe.checkout.sessions.create() to initiate the payment process.

* Manage Subscriptions: API routes will also handle subscription modifications (upgrades, downgrades, cancellations) via stripe.subscriptions.update() and stripe.subscriptions.del().

* Customer Portal: Integration with Stripe Customer Portal for self-service management of subscriptions, billing details, and payment methods. This reduces support overhead.

  • Stripe Webhooks: Essential for real-time synchronization of subscription status between Stripe and our application database.

* Endpoint: A dedicated Next.js API route (e.g., /api/webhooks/stripe) will be configured in Stripe to receive events.

* Event Handling: Key events to handle include:

* checkout.session.completed: Triggered when a user successfully completes a checkout session. This is where we create or update the user's Subscription record in our database and provision access.

* customer.subscription.updated: Fired when a subscription changes (e.g., plan upgrade/downgrade, renewal, trial end). Update user's plan and access rights.

* customer.subscription.deleted: When a subscription is cancelled. Revoke access or mark as 'cancelled_at_period_end'.

* invoice.payment_succeeded: Confirmation of a successful recurring payment.

* invoice.payment_failed: Notification of a failed recurring payment. Trigger dunning process (Stripe handles this automatically, but we might want to send custom notifications).

* customer.created: When a new customer is created in Stripe (e.g., via checkout session). Link to our internal user ID.

  • Security: All sensitive payment data is handled by Stripe, ensuring PCI compliance. Our Next.js backend will only store Stripe Customer IDs and Subscription IDs, never raw payment information. Webhook events will be verified using Stripe's signature header to prevent spoofing.

4. Subscription Management (Replacing Inventory Management)

For digital subscription products, 'inventory management' translates to robust subscription lifecycle management and access control.

Database Schema (Extending User/SubscriptionPlan):

  • User table:

* id (UUID, Primary Key)

* email (String, unique)

* stripeCustomerId (String, unique Stripe Customer ID)

* currentSubscriptionId (UUID, Foreign Key to Subscription table)

  • Subscription table:

* id (UUID, Primary Key)

* userId (UUID, Foreign Key to User table)

* planId (UUID, Foreign Key to SubscriptionPlan table)

* stripeSubscriptionId (String, unique Stripe Subscription ID)

* status (Enum: 'active', 'trialing', 'past_due', 'canceled', 'unpaid', 'incomplete', 'incomplete_expired') - synced with Stripe.

* currentPeriodStart (DateTime)

* currentPeriodEnd (DateTime)

* cancelAtPeriodEnd (Boolean, default false)

* canceledAt (DateTime, nullable)

* createdAt (DateTime)

* updatedAt (DateTime)

Management Processes:

  • Provisioning Access: Upon checkout.session.completed (or customer.subscription.updated), the webhook handler updates the User's currentSubscriptionId and Subscription record. The application then grants access to features based on the planId associated with the active subscription.
  • Upgrades/Downgrades: Users can initiate plan changes via the Stripe Customer Portal or a custom UI. Our backend will call stripe.subscriptions.update() to modify the subscription. Stripe handles prorations automatically. The customer.subscription.updated webhook will then update our database.
  • Cancellations: Users can cancel via the Customer Portal. The customer.subscription.deleted webhook will mark the subscription as canceled and set cancelAtPeriodEnd to true (if applicable), ensuring access until the current billing period ends.
  • Access Control: Middleware in Next.js (e.g., using next-auth or custom logic) will check the user's Subscription status and planId to determine feature access. For example, a user with a 'Basic' plan cannot access 'Pro' features.

5. Shipping Rules

Given that the product is a 'Professional workflow tool' offered via 'Subscription tiers', it is entirely digital. Therefore, traditional 'shipping rules' for physical goods are not applicable.

Digital Delivery & Access:

  • Upon successful payment and subscription activation (confirmed via Stripe webhooks), immediate digital access to the workflow tool's features corresponding to the purchased tier will be granted.
  • No physical goods are involved, eliminating the need for shipping addresses, carriers, tracking, or associated costs.
  • The 'delivery' mechanism is the activation of features within the web application, managed by the subscription status in the user's profile.

6. Conversion Optimization Tactics

To maximize subscription sign-ups and retention, the following tactics will be implemented:

  • Clear & Comparative Pricing Page: Design a visually appealing pricing page that clearly outlines the features and benefits of each tier. Use a comparison table format to highlight value propositions and differentiate plans effectively.
  • Prominent Call-to-Actions (CTAs): Use clear, action-oriented CTAs (e.g., 'Start Free Trial', 'Get Started', 'Subscribe Now') that stand out on the pricing page and relevant marketing touchpoints.
  • Social Proof & Testimonials: Display customer testimonials, logos of companies using the tool, or user statistics (e.g., 'Trusted by 10,000+ professionals') to build trust and credibility.
  • Free Trial Offerings: Consider offering a 7-day or 14-day free trial for the Pro or Enterprise tiers to allow users to experience the value before committing. Stripe supports trial periods natively.
  • Exit-Intent Pop-ups: Implement exit-intent technology to capture users who are about to leave the pricing page, potentially offering a limited-time discount or a reminder of key benefits.
  • Simplified Checkout Process: Leverage Stripe Checkout for a streamlined, secure, and mobile-optimized payment experience, minimizing friction.
  • A/B Testing: Continuously A/B test different pricing models, CTA button texts, page layouts, and feature descriptions to identify what resonates best with the target audience.
  • Value-Driven Messaging: Focus marketing and website copy on the problems the workflow tool solves and the value it provides, rather than just listing features.
  • FAQ Section: Include a comprehensive FAQ section on the pricing page to address common concerns and objections related to pricing, features, and cancellation policies.
  • Money-Back Guarantee: Offer a satisfaction guarantee (e.g., 30-day money-back) to reduce perceived risk for new subscribers.
Recommendations
  • **Implement Stripe Customer Portal:** Empower users with self-service capabilities for managing their subscriptions, payment methods, and billing history. This significantly reduces customer support overhead.
  • **Leverage Stripe Webhooks Extensively:** Ensure robust, real-time synchronization between Stripe and your application's database by handling all critical webhook events. This is crucial for accurate subscription status and feature access.
  • **Prioritize Security and Compliance:** While Stripe handles PCI compliance for payment data, ensure your Next.js application follows best practices for API key management, data privacy (GDPR, CCPA), and secure coding to protect user information.
  • **Develop a Clear Upgrade/Downgrade Path:** Design the user experience for plan changes to be intuitive and seamless, clearly communicating any prorations or changes in billing cycles.
  • **Monitor Key Subscription Metrics:** Implement analytics to track Monthly Recurring Revenue (MRR), churn rate, customer lifetime value (CLTV), and conversion rates to continuously optimize the subscription business model.
  • **Consider a Freemium Model or Limited Free Tier:** For a professional workflow tool, offering a perpetually free, feature-limited tier can act as a powerful lead generation tool, converting users to paid plans over time.
Next Steps
  1. Detailed API Route Design for Next.js Backend Week 1-2
  2. Stripe Account Setup & Configuration Week 1
  3. Database Schema Implementation Week 2
  4. Frontend UI/UX Design & Development Week 2-4
  5. Stripe Webhook Endpoint Development Week 3-4
  6. Subscription Management & Access Control Logic Week 4-5
  7. Comprehensive Testing & QA Week 5-6
  8. Deployment & Monitoring Setup Week 6
payment_system_integration.md
Download as Markdown
Copy all content
Full output as text
Download ZIP
IDE-ready project ZIP
Copy share link
Permanent URL for this run
Get Embed Code
Embed this result on any website
Print / Save PDF
Use browser print dialog
\n\n\n"); var hasSrcMain=Object.keys(extracted).some(function(k){return k.indexOf("src/main")>=0;}); if(!hasSrcMain) zip.file(folder+"src/main."+ext,"import React from 'react'\nimport ReactDOM from 'react-dom/client'\nimport App from './App'\nimport './index.css'\n\nReactDOM.createRoot(document.getElementById('root')!).render(\n \n \n \n)\n"); var hasSrcApp=Object.keys(extracted).some(function(k){return k==="src/App."+ext||k==="App."+ext;}); if(!hasSrcApp) zip.file(folder+"src/App."+ext,"import React from 'react'\nimport './App.css'\n\nfunction App(){\n return(\n
\n
\n

"+slugTitle(pn)+"

\n

Built with PantheraHive BOS

\n
\n
\n )\n}\nexport default App\n"); zip.file(folder+"src/index.css","*{margin:0;padding:0;box-sizing:border-box}\nbody{font-family:system-ui,-apple-system,sans-serif;background:#f0f2f5;color:#1a1a2e}\n.app{min-height:100vh;display:flex;flex-direction:column}\n.app-header{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;padding:40px}\nh1{font-size:2.5rem;font-weight:700}\n"); zip.file(folder+"src/App.css",""); zip.file(folder+"src/components/.gitkeep",""); zip.file(folder+"src/pages/.gitkeep",""); zip.file(folder+"src/hooks/.gitkeep",""); Object.keys(extracted).forEach(function(p){ var fp=p.startsWith("src/")?p:"src/"+p; zip.file(folder+fp,extracted[p]); }); zip.file(folder+"README.md","# "+slugTitle(pn)+"\n\nGenerated by PantheraHive BOS.\n\n## Setup\n\`\`\`bash\nnpm install\nnpm run dev\n\`\`\`\n\n## Build\n\`\`\`bash\nnpm run build\n\`\`\`\n\n## Open in IDE\nOpen the project folder in VS Code or WebStorm.\n"); zip.file(folder+".gitignore","node_modules/\ndist/\n.env\n.DS_Store\n*.local\n"); } /* --- Vue (Vite + Composition API + TypeScript) --- */ function buildVue(zip,folder,app,code,panelTxt){ var pn=pkgName(app); var C=cc(pn); var extracted=extractCode(panelTxt); zip.file(folder+"package.json",'{\n "name": "'+pn+'",\n "version": "0.0.0",\n "type": "module",\n "scripts": {\n "dev": "vite",\n "build": "vue-tsc -b && vite build",\n "preview": "vite preview"\n },\n "dependencies": {\n "vue": "^3.5.13",\n "vue-router": "^4.4.5",\n "pinia": "^2.3.0",\n "axios": "^1.7.9"\n },\n "devDependencies": {\n "@vitejs/plugin-vue": "^5.2.1",\n "typescript": "~5.7.3",\n "vite": "^6.0.5",\n "vue-tsc": "^2.2.0"\n }\n}\n'); zip.file(folder+"vite.config.ts","import { defineConfig } from 'vite'\nimport vue from '@vitejs/plugin-vue'\nimport { resolve } from 'path'\n\nexport default defineConfig({\n plugins: [vue()],\n resolve: { alias: { '@': resolve(__dirname,'src') } }\n})\n"); zip.file(folder+"tsconfig.json",'{"files":[],"references":[{"path":"./tsconfig.app.json"},{"path":"./tsconfig.node.json"}]}\n'); zip.file(folder+"tsconfig.app.json",'{\n "compilerOptions":{\n "target":"ES2020","useDefineForClassFields":true,"module":"ESNext","lib":["ES2020","DOM","DOM.Iterable"],\n "skipLibCheck":true,"moduleResolution":"bundler","allowImportingTsExtensions":true,\n "isolatedModules":true,"moduleDetection":"force","noEmit":true,"jsxImportSource":"vue",\n "strict":true,"paths":{"@/*":["./src/*"]}\n },\n "include":["src/**/*.ts","src/**/*.d.ts","src/**/*.tsx","src/**/*.vue"]\n}\n'); zip.file(folder+"env.d.ts","/// \n"); zip.file(folder+"index.html","\n\n\n \n \n "+slugTitle(pn)+"\n\n\n
\n \n\n\n"); var hasMain=Object.keys(extracted).some(function(k){return k==="src/main.ts"||k==="main.ts";}); if(!hasMain) zip.file(folder+"src/main.ts","import { createApp } from 'vue'\nimport { createPinia } from 'pinia'\nimport App from './App.vue'\nimport './assets/main.css'\n\nconst app = createApp(App)\napp.use(createPinia())\napp.mount('#app')\n"); var hasApp=Object.keys(extracted).some(function(k){return k.indexOf("App.vue")>=0;}); if(!hasApp) zip.file(folder+"src/App.vue","\n\n\n\n\n"); zip.file(folder+"src/assets/main.css","*{margin:0;padding:0;box-sizing:border-box}body{font-family:system-ui,sans-serif;background:#fff;color:#213547}\n"); zip.file(folder+"src/components/.gitkeep",""); zip.file(folder+"src/views/.gitkeep",""); zip.file(folder+"src/stores/.gitkeep",""); Object.keys(extracted).forEach(function(p){ var fp=p.startsWith("src/")?p:"src/"+p; zip.file(folder+fp,extracted[p]); }); zip.file(folder+"README.md","# "+slugTitle(pn)+"\n\nGenerated by PantheraHive BOS.\n\n## Setup\n\`\`\`bash\nnpm install\nnpm run dev\n\`\`\`\n\n## Build\n\`\`\`bash\nnpm run build\n\`\`\`\n\nOpen in VS Code or WebStorm.\n"); zip.file(folder+".gitignore","node_modules/\ndist/\n.env\n.DS_Store\n*.local\n"); } /* --- Angular (v19 standalone) --- */ function buildAngular(zip,folder,app,code,panelTxt){ var pn=pkgName(app); var C=cc(pn); var sel=pn.replace(/_/g,"-"); var extracted=extractCode(panelTxt); zip.file(folder+"package.json",'{\n "name": "'+pn+'",\n "version": "0.0.0",\n "scripts": {\n "ng": "ng",\n "start": "ng serve",\n "build": "ng build",\n "test": "ng test"\n },\n "dependencies": {\n "@angular/animations": "^19.0.0",\n "@angular/common": "^19.0.0",\n "@angular/compiler": "^19.0.0",\n "@angular/core": "^19.0.0",\n "@angular/forms": "^19.0.0",\n "@angular/platform-browser": "^19.0.0",\n "@angular/platform-browser-dynamic": "^19.0.0",\n "@angular/router": "^19.0.0",\n "rxjs": "~7.8.0",\n "tslib": "^2.3.0",\n "zone.js": "~0.15.0"\n },\n "devDependencies": {\n "@angular-devkit/build-angular": "^19.0.0",\n "@angular/cli": "^19.0.0",\n "@angular/compiler-cli": "^19.0.0",\n "typescript": "~5.6.0"\n }\n}\n'); zip.file(folder+"angular.json",'{\n "$schema": "./node_modules/@angular/cli/lib/config/schema.json",\n "version": 1,\n "newProjectRoot": "projects",\n "projects": {\n "'+pn+'": {\n "projectType": "application",\n "root": "",\n "sourceRoot": "src",\n "prefix": "app",\n "architect": {\n "build": {\n "builder": "@angular-devkit/build-angular:application",\n "options": {\n "outputPath": "dist/'+pn+'",\n "index": "src/index.html",\n "browser": "src/main.ts",\n "tsConfig": "tsconfig.app.json",\n "styles": ["src/styles.css"],\n "scripts": []\n }\n },\n "serve": {"builder":"@angular-devkit/build-angular:dev-server","configurations":{"production":{"buildTarget":"'+pn+':build:production"},"development":{"buildTarget":"'+pn+':build:development"}},"defaultConfiguration":"development"}\n }\n }\n }\n}\n'); zip.file(folder+"tsconfig.json",'{\n "compileOnSave": false,\n "compilerOptions": {"baseUrl":"./","outDir":"./dist/out-tsc","forceConsistentCasingInFileNames":true,"strict":true,"noImplicitOverride":true,"noPropertyAccessFromIndexSignature":true,"noImplicitReturns":true,"noFallthroughCasesInSwitch":true,"paths":{"@/*":["src/*"]},"skipLibCheck":true,"esModuleInterop":true,"sourceMap":true,"declaration":false,"experimentalDecorators":true,"moduleResolution":"bundler","importHelpers":true,"target":"ES2022","module":"ES2022","useDefineForClassFields":false,"lib":["ES2022","dom"]},\n "references":[{"path":"./tsconfig.app.json"}]\n}\n'); zip.file(folder+"tsconfig.app.json",'{\n "extends":"./tsconfig.json",\n "compilerOptions":{"outDir":"./dist/out-tsc","types":[]},\n "files":["src/main.ts"],\n "include":["src/**/*.d.ts"]\n}\n'); zip.file(folder+"src/index.html","\n\n\n \n "+slugTitle(pn)+"\n \n \n \n\n\n \n\n\n"); zip.file(folder+"src/main.ts","import { bootstrapApplication } from '@angular/platform-browser';\nimport { appConfig } from './app/app.config';\nimport { AppComponent } from './app/app.component';\n\nbootstrapApplication(AppComponent, appConfig)\n .catch(err => console.error(err));\n"); zip.file(folder+"src/styles.css","* { margin: 0; padding: 0; box-sizing: border-box; }\nbody { font-family: system-ui, -apple-system, sans-serif; background: #f9fafb; color: #111827; }\n"); var hasComp=Object.keys(extracted).some(function(k){return k.indexOf("app.component")>=0;}); if(!hasComp){ zip.file(folder+"src/app/app.component.ts","import { Component } from '@angular/core';\nimport { RouterOutlet } from '@angular/router';\n\n@Component({\n selector: 'app-root',\n standalone: true,\n imports: [RouterOutlet],\n templateUrl: './app.component.html',\n styleUrl: './app.component.css'\n})\nexport class AppComponent {\n title = '"+pn+"';\n}\n"); zip.file(folder+"src/app/app.component.html","
\n
\n

"+slugTitle(pn)+"

\n

Built with PantheraHive BOS

\n
\n \n
\n"); zip.file(folder+"src/app/app.component.css",".app-header{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:60vh;gap:16px}h1{font-size:2.5rem;font-weight:700;color:#6366f1}\n"); } zip.file(folder+"src/app/app.config.ts","import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';\nimport { provideRouter } from '@angular/router';\nimport { routes } from './app.routes';\n\nexport const appConfig: ApplicationConfig = {\n providers: [\n provideZoneChangeDetection({ eventCoalescing: true }),\n provideRouter(routes)\n ]\n};\n"); zip.file(folder+"src/app/app.routes.ts","import { Routes } from '@angular/router';\n\nexport const routes: Routes = [];\n"); Object.keys(extracted).forEach(function(p){ var fp=p.startsWith("src/")?p:"src/"+p; zip.file(folder+fp,extracted[p]); }); zip.file(folder+"README.md","# "+slugTitle(pn)+"\n\nGenerated by PantheraHive BOS.\n\n## Setup\n\`\`\`bash\nnpm install\nng serve\n# or: npm start\n\`\`\`\n\n## Build\n\`\`\`bash\nng build\n\`\`\`\n\nOpen in VS Code with Angular Language Service extension.\n"); zip.file(folder+".gitignore","node_modules/\ndist/\n.env\n.DS_Store\n*.local\n.angular/\n"); } /* --- Python --- */ function buildPython(zip,folder,app,code){ var title=slugTitle(app); var pn=pkgName(app); var src=code.replace(/^\`\`\`[\w]*\n?/m,"").replace(/\n?\`\`\`$/m,"").trim(); var reqMap={"numpy":"numpy","pandas":"pandas","sklearn":"scikit-learn","tensorflow":"tensorflow","torch":"torch","flask":"flask","fastapi":"fastapi","uvicorn":"uvicorn","requests":"requests","sqlalchemy":"sqlalchemy","pydantic":"pydantic","dotenv":"python-dotenv","PIL":"Pillow","cv2":"opencv-python","matplotlib":"matplotlib","seaborn":"seaborn","scipy":"scipy"}; var reqs=[]; Object.keys(reqMap).forEach(function(k){if(src.indexOf("import "+k)>=0||src.indexOf("from "+k)>=0)reqs.push(reqMap[k]);}); var reqsTxt=reqs.length?reqs.join("\n"):"# add dependencies here\n"; zip.file(folder+"main.py",src||"# "+title+"\n# Generated by PantheraHive BOS\n\nprint(title+\" loaded\")\n"); zip.file(folder+"requirements.txt",reqsTxt); zip.file(folder+".env.example","# Environment variables\n"); zip.file(folder+"README.md","# "+title+"\n\nGenerated by PantheraHive BOS.\n\n## Setup\n\`\`\`bash\npython3 -m venv .venv\nsource .venv/bin/activate\npip install -r requirements.txt\n\`\`\`\n\n## Run\n\`\`\`bash\npython main.py\n\`\`\`\n"); zip.file(folder+".gitignore",".venv/\n__pycache__/\n*.pyc\n.env\n.DS_Store\n"); } /* --- Node.js --- */ function buildNode(zip,folder,app,code){ var title=slugTitle(app); var pn=pkgName(app); var src=code.replace(/^\`\`\`[\w]*\n?/m,"").replace(/\n?\`\`\`$/m,"").trim(); var depMap={"mongoose":"^8.0.0","dotenv":"^16.4.5","axios":"^1.7.9","cors":"^2.8.5","bcryptjs":"^2.4.3","jsonwebtoken":"^9.0.2","socket.io":"^4.7.4","uuid":"^9.0.1","zod":"^3.22.4","express":"^4.18.2"}; var deps={}; Object.keys(depMap).forEach(function(k){if(src.indexOf(k)>=0)deps[k]=depMap[k];}); if(!deps["express"])deps["express"]="^4.18.2"; var pkgJson=JSON.stringify({"name":pn,"version":"1.0.0","main":"src/index.js","scripts":{"start":"node src/index.js","dev":"nodemon src/index.js"},"dependencies":deps,"devDependencies":{"nodemon":"^3.0.3"}},null,2)+"\n"; zip.file(folder+"package.json",pkgJson); var fallback="const express=require(\"express\");\nconst app=express();\napp.use(express.json());\n\napp.get(\"/\",(req,res)=>{\n res.json({message:\""+title+" API\"});\n});\n\nconst PORT=process.env.PORT||3000;\napp.listen(PORT,()=>console.log(\"Server on port \"+PORT));\n"; zip.file(folder+"src/index.js",src||fallback); zip.file(folder+".env.example","PORT=3000\n"); zip.file(folder+".gitignore","node_modules/\n.env\n.DS_Store\n"); zip.file(folder+"README.md","# "+title+"\n\nGenerated by PantheraHive BOS.\n\n## Setup\n\`\`\`bash\nnpm install\n\`\`\`\n\n## Run\n\`\`\`bash\nnpm run dev\n\`\`\`\n"); } /* --- Vanilla HTML --- */ function buildVanillaHtml(zip,folder,app,code){ var title=slugTitle(app); var isFullDoc=code.trim().toLowerCase().indexOf("=0||code.trim().toLowerCase().indexOf("=0; var indexHtml=isFullDoc?code:"\n\n\n\n\n"+title+"\n\n\n\n"+code+"\n\n\n\n"; zip.file(folder+"index.html",indexHtml); zip.file(folder+"style.css","/* "+title+" — styles */\n*{margin:0;padding:0;box-sizing:border-box}\nbody{font-family:system-ui,-apple-system,sans-serif;background:#fff;color:#1a1a2e}\n"); zip.file(folder+"script.js","/* "+title+" — scripts */\n"); zip.file(folder+"assets/.gitkeep",""); zip.file(folder+"README.md","# "+title+"\n\nGenerated by PantheraHive BOS.\n\n## Open\nDouble-click \`index.html\` in your browser.\n\nOr serve locally:\n\`\`\`bash\nnpx serve .\n# or\npython3 -m http.server 3000\n\`\`\`\n"); zip.file(folder+".gitignore",".DS_Store\nnode_modules/\n.env\n"); } /* ===== MAIN ===== */ var sc=document.createElement("script"); sc.src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"; sc.onerror=function(){ if(lbl)lbl.textContent="Download ZIP"; alert("JSZip load failed — check connection."); }; sc.onload=function(){ var zip=new JSZip(); var base=(_phFname||"output").replace(/\.[^.]+$/,""); var app=base.toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_+|_+$/g,"")||"my_app"; var folder=app+"/"; var vc=document.getElementById("panel-content"); var panelTxt=vc?(vc.innerText||vc.textContent||""):""; var lang=detectLang(_phCode,panelTxt); if(_phIsHtml){ buildVanillaHtml(zip,folder,app,_phCode); } else if(lang==="flutter"){ buildFlutter(zip,folder,app,_phCode,panelTxt); } else if(lang==="react-native"){ buildReactNative(zip,folder,app,_phCode,panelTxt); } else if(lang==="swift"){ buildSwift(zip,folder,app,_phCode,panelTxt); } else if(lang==="kotlin"){ buildKotlin(zip,folder,app,_phCode,panelTxt); } else if(lang==="react"){ buildReact(zip,folder,app,_phCode,panelTxt); } else if(lang==="vue"){ buildVue(zip,folder,app,_phCode,panelTxt); } else if(lang==="angular"){ buildAngular(zip,folder,app,_phCode,panelTxt); } else if(lang==="python"){ buildPython(zip,folder,app,_phCode); } else if(lang==="node"){ buildNode(zip,folder,app,_phCode); } else { /* Document/content workflow */ var title=app.replace(/_/g," "); var md=_phAll||_phCode||panelTxt||"No content"; zip.file(folder+app+".md",md); var h=""+title+""; h+="

"+title+"

"; var hc=md.replace(/&/g,"&").replace(//g,">"); hc=hc.replace(/^### (.+)$/gm,"

$1

"); hc=hc.replace(/^## (.+)$/gm,"

$1

"); hc=hc.replace(/^# (.+)$/gm,"

$1

"); hc=hc.replace(/\*\*(.+?)\*\*/g,"$1"); hc=hc.replace(/\n{2,}/g,"

"); h+="

"+hc+"

Generated by PantheraHive BOS
"; zip.file(folder+app+".html",h); zip.file(folder+"README.md","# "+title+"\n\nGenerated by PantheraHive BOS.\n\nFiles:\n- "+app+".md (Markdown)\n- "+app+".html (styled HTML)\n"); } zip.generateAsync({type:"blob"}).then(function(blob){ var a=document.createElement("a"); a.href=URL.createObjectURL(blob); a.download=app+".zip"; a.click(); URL.revokeObjectURL(a.href); if(lbl)lbl.textContent="Download ZIP"; }); }; document.head.appendChild(sc); } function phShare(){navigator.clipboard.writeText(window.location.href).then(function(){var el=document.getElementById("ph-share-lbl");if(el){el.textContent="Link copied!";setTimeout(function(){el.textContent="Copy share link";},2500);}});}function phEmbed(){var runId=window.location.pathname.split("/").pop().replace(".html","");var embedUrl="https://pantherahive.com/embed/"+runId;var code='';navigator.clipboard.writeText(code).then(function(){var el=document.getElementById("ph-embed-lbl");if(el){el.textContent="Embed code copied!";setTimeout(function(){el.textContent="Get Embed Code";},2500);}});}