# EPIC Premium Member Hub — hosting and launch guide

Updated 24 September 2026. For the EPIC administrator or hosting developer.

The app is a responsive member portal with email-code sign-in, onboarding, export profiles, weekly buyer reports, community orders, AI opportunity matching, an export-readiness workbook, a cost/freight calculator and service enquiry forms. The chatbot has been retired.

## 1. Choose the hosting route

| Route | What runs where | Use when |
| --- | --- | --- |
| Existing hosted app | App and protected APIs on the current Site; n8n stays at n8n.epicland.in; membership and records stay in the private spreadsheet | Fastest activation; no server migration needed |
| Your VPS | App in a Node production container behind your HTTPS proxy; existing n8n remains separate; same spreadsheet bridge | Your developer manages the app, domain, server updates and backups |

The current app address is https://epic-buyer-connect.contact640451.chatgpt.site. A public homepage does not make member data public: protected requests recheck active membership. Publishing the app alone does not install the spreadsheet bridge or activate provider accounts.

Do not upload this as a static HTML site or install it as a WordPress plugin. The authentication, quota, order and billing routes require a server. Do not use `npm run start` from the original Sites starter as a VPS production command: that command is a local Wrangler development server. The VPS path below uses the included Next.js standalone build.

## 2. What is prepared, and what still needs connection

| Capability | Prepared in the app | Required for live use |
| --- | --- | --- |
| Member sign-in and saved profiles | Email OTP, active-status checks, account-scoped records | Updated Apps Script deployment and shared secret |
| Buyer reports | Public-source search, contact evidence, saved weekly allowance | Tavily key and membership bridge |
| Community orders | Submission, search, owner editing/closing, expiry, moderation status | Updated membership bridge; members then submit actual requirements |
| AI matching | Saved profile versus a current order or the member’s saved buyer | OpenAI key and updated bridge |
| Export checklist | Product/origin/destination rules, official references, evidence notes, download | Available without an AI key; importer/authority review still needed |
| Billing/onboarding | Verified Razorpay endpoints and companion n8n workflow | Razorpay secrets, private sheet access, workflow import and live verification |

No live member records, payments or messages were changed to test this release. Mock service tests do not verify real Google permissions, email delivery or provider credentials.

## 3. Google Sheet and Apps Script

Use the existing spreadsheet and **Sheet2**. Keep all original registration columns. In this setup, **Subscription** contains a Razorpay `sub_…` ID; **Subscription Status** must be `active`. A payment record, `ID Status = PAID`, or WhatsApp group membership is not proof of a current subscription.

1. Open the spreadsheet → Extensions → Apps Script.
2. Replace the code with `integrations/EPIC-Membership.gs` from this release. A copy is downloadable from the app’s `/setup` page.
3. Add the following Script properties. Secrets belong in Script properties, never sheet cells.

| Property | Value |
| --- | --- |
| `SPREADSHEET_ID` | `1a9RC7oOr-xKoGTg29Y7uSVRSguh9WZeXTe2fl_JD5nM` |
| `SHEET_NAME` | `Sheet2` |
| `SUBSCRIPTION_COLUMN_MODE` | `identifier` |
| `DUPLICATE_EMAIL_POLICY` | `subscription_records` |
| `ACTIVE_STATUS_VALUES` | `["active"]` |
| `RAZORPAY_PREMIUM_PLAN_ID` | Exact live Premium plan ID from your current subscription workflow |
| `BRIDGE_SECRET` | Random 32+ character secret, also used as the app’s `MEMBERSHIP_SHARED_SECRET` |
| `BILLING_SYNC_SECRET` | A different random 32+ character secret, shared with the app and the n8n reconciliation credential |
| `EPIC_DASHBOARD_URL` | Your final HTTPS app homepage, without a query string |

Leave `PREMIUM_SUBSCRIPTION_VALUES` and `ID_STATUS_VALUES` unset for this subscription-ID setup. Keep existing Script properties that are still applicable.

4. Deploy → Manage deployments → Edit → **New version**. Execute as yourself; allow Anyone to reach the Web app endpoint. Requests are authenticated by the private bridge secret, and protected member actions also require a valid active session. Complete Google’s authorisation.
5. Use the production URL ending `/exec`, not `/dev`. Updating the existing deployment preserves its URL. If you create a new deployment URL, update the app setting too.

The script creates auxiliary tabs as they are first needed: `EPIC Member Profiles`, `EPIC Buyer Reports`, `EPIC Community Orders`, `EPIC Service Enquiries`, and `EPIC Subscription Sync`. Preserve their headers. The buyer-report ledger is also the weekly quota record; deleting it can reset allowances.

Sessions last up to six hours. Every protected request rechecks membership. One normalized email shares its profile and weekly allowance across devices and subscriptions.

## 4. Private app settings

Set these through the current Site’s environment settings, or in `hosting/vps/.env.production` for your VPS. Never use `NEXT_PUBLIC_` for these keys. Restart/redeploy after changes. Do not send keys in chat or include them in a source archive.

| Setting | Purpose |
| --- | --- |
| `GOOGLE_SHEETS_WEB_APP_URL` | Apps Script production `/exec` URL |
| `MEMBERSHIP_SHARED_SECRET` | Same value as `BRIDGE_SECRET` |
| `TAVILY_API_KEY` | Buyer discovery and contact-page extraction |
| `OPENAI_API_KEY` | AI opportunity assessments; use a dedicated project key with billing controls |
| `OPENAI_MATCH_MODEL` | Default `gpt-4.1-mini`; use a Responses API model supporting structured JSON output |
| `EPIC_SUBSCRIPTION_URL` | `https://epickerala.com/join-epic-premium/` |
| `EPIC_WHATSAPP_GROUP_URL` | Current private `https://chat.whatsapp.com/...` invitation |
| `EPIC_TUTORIALS_URL` | Your member tutorial/course destination |
| `EPIC_SUPPORT_URL` | Your member support link |
| `RAZORPAY_KEY_ID`, `RAZORPAY_KEY_SECRET` | Provider credentials matching the subscription account and mode |
| `RAZORPAY_PREMIUM_PLAN_ID` | Same exact Premium plan as Apps Script |
| `RAZORPAY_WEBHOOK_SECRET` | 32+ character secret configured on the new Razorpay webhook |
| `BILLING_SYNC_SECRET` | Same value as Apps Script and n8n reconciliation Header Auth |
| `EPIC_APP_ORIGIN` | VPS only: your canonical HTTPS app origin, for example `https://members.your-domain.com`; required for same-origin form protection |
| `EPIC_TRUST_PROXY` | Leave empty on Sites. VPS Compose sets `true`; its trusted proxy must overwrite `X-Real-IP` and the app port must remain private |

The app calls OpenAI directly on the server. Members do not need the OpenAI Developers plugin. That plugin is an optional administrative way to provision a key; installing it alone does not activate this app.

If the plugin does not open, the hosting administrator can use the direct setup:

1. Create a project API key in the OpenAI Platform account using the [official OpenAI quickstart](https://developers.openai.com/api/docs/quickstart).
2. Save it as the secret server environment variable `OPENAI_API_KEY`. For the current hosted app, use its secret environment settings. For the prepared VPS deployment, put it in the private `.env.production` file described below.
3. Republish the hosted app, or recreate the VPS app container after updating its environment.
4. Sign in with an active EPIC member account, save an export profile, and request a match on an eligible buyer or current community order. A successful live assessment verifies the key and account access. A configured indicator alone does not.

Never put the key in browser code, Google Sheet cells, member forms or screenshots. Membership and Tavily buyer search require their separate settings. Old `OPENAI_CHAT_MODEL` is no longer used.

## 5. VPS deployment

Use the **project source checkout/export associated with this Site**, including the lockfile and hosting directory. Downloading the published webpage does not provide server source. Your developer should obtain the source using your owner access. Do not expose short-lived repository credentials or the original uploaded n8n workflow.

The included files are:

- `hosting/vps/Dockerfile`: multi-stage build, Node 24 runtime, non-root application user.
- `hosting/vps/compose.yaml`: app bound to `127.0.0.1:3001`, restart policy and healthcheck.
- `hosting/vps/nginx.conf.example`: separate HTTPS virtual host, forwarded headers and API timeouts.
- `hosting/vps/runtime-env.ts`: runtime environment adapter used only by the Node build.
- `scripts/build-vps.mjs`: production standalone build; normal Sites builds remain available.
- `.dockerignore`: excludes local secrets, uploads, dependency folders and transient files from the Docker build.

Use a maintained Linux VPS with Docker Engine and Compose installed from their official distribution. Allow HTTPS and your existing SSH administration route. Keep port 3001 bound to localhost. Outbound HTTPS is required for Google Apps Script, Razorpay, Tavily and OpenAI. Size CPU/memory after observing your traffic; building requires more memory than serving.

From the project root:

```bash
cp .env.example hosting/vps/.env.production
chmod 600 hosting/vps/.env.production
```

Edit that file securely with the settings above. Do not put secrets into the Dockerfile, build arguments, git or browser configuration.

```bash
docker compose -f hosting/vps/compose.yaml build
docker compose -f hosting/vps/compose.yaml up -d
docker compose -f hosting/vps/compose.yaml ps
docker compose -f hosting/vps/compose.yaml logs --tail=100 epic
```

The healthcheck confirms the app responds; it does **not** prove Google, email, billing or AI are configured. Inspect the app’s sign-in and activation states as well.

### Domain and HTTPS

1. Choose an app subdomain you own, for example `members.epicland.in`, and point its DNS record at the VPS. This example is not an already configured domain.
2. Keep n8n’s existing virtual host and volumes. Add a separate app virtual host; do not replace the current n8n proxy configuration or start a second proxy competing for ports 80/443.
3. Obtain a valid certificate using your existing proxy/certificate manager. In the supplied Nginx example, substitute the actual domain and certificate paths. Install the certificate before enabling its HTTPS block.
4. Proxy the app to `http://127.0.0.1:3001`. Preserve `Host`, `X-Forwarded-Host` and `X-Forwarded-Proto`. Overwrite `X-Real-IP` using the proxy’s trusted client-address setting and strip inbound `CF-Connecting-IP` on the VPS. If Nginx is itself behind a trusted CDN, configure its real-IP module for that CDN’s published address ranges first.
5. Set `EPIC_APP_ORIGIN` to the final HTTPS app origin in the runtime environment. Validate the proxy configuration and reload it using your server’s existing management process. Cookies require HTTPS; a direct plain-HTTP login is not the production flow.
6. Avoid caching `/api/*` and member responses at a CDN. The app marks its JSON responses `private, no-store`.

If your proxy runs inside Docker instead of on the host, `127.0.0.1` inside that proxy container is a different host. Attach the app to the proxy’s existing Docker network and route to its service name on port 3000. Keep the app unpublished to the public interface. Adapt the supplied Compose file to your existing network; do not recreate n8n’s stack.

### Build without Docker

Install the project’s pinned pnpm and a supported Node runtime, then run:

```bash
pnpm install --frozen-lockfile
node scripts/build-vps.mjs
```

Copy `public/` into `.next/standalone/public/` and `.next/static/` into `.next/standalone/.next/static/`. Run `node .next/standalone/server.js` with private runtime variables under your service manager and HTTPS proxy. Use the service manager for restart/log rotation. Node standalone output and public/static copy requirements follow the [Next.js output documentation](https://nextjs.org/docs/app/api-reference/config/next-config-js/output); deployment and proxy considerations are in the [self-hosting guide](https://nextjs.org/docs/app/guides/self-hosting).

The Node production build is tested separately from the hosted Worker build. This guide does not claim that a container, certificate or domain has already been deployed on your VPS.

## 6. n8n and existing-member migration

Use the previously prepared **EPIC Premium Onboarding v2** companion workflow. The live workflow at n8n.epicland.in has not been edited by this release.

1. Back up the existing workflow and spreadsheet. Import the companion as inactive.
2. Bind its Google Sheets credential to the existing private spreadsheet. Create HTTP Header Auth for reconciliation: header `Authorization`, value `Bearer YOUR_BILLING_SYNC_SECRET`.
3. For a new app domain, update every app HTTP target in the companion: the webhook-forwarding request uses `/api/billing/razorpay`; reconciliation uses `/api/billing/reconcile`. Keep the body and signature handling intact.
4. Run the manual reconciliation branch. It reads existing subscription IDs from Sheet2 and fetches current Razorpay state. It does not charge members, create a second subscription, or send backfill welcome emails.
5. Review failed, blank-ID and conflicting-email records. Do not activate everyone marked PAID. Existing active members sign in with their original email; no new payment is needed.
6. Configure Razorpay subscription lifecycle events to the new n8n **production** webhook. Keep Raw Body enabled and forward the original binary body and signature. JSON reserialization breaks signature validation.
7. Verify using a separate test sheet, matching test credentials and test plan before switching production settings. Test activation, replay, renewal and cancellation. Enable the six-hour reconciliation schedule after validation.
8. Only after the new flow works, disable the old success/cancellation/direct-invite email branches. Preserve the existing create-subscription endpoint used by the joining page.
9. Set Apps Script `EPIC_DASHBOARD_URL` to the final app domain. Update the official checkout’s successful-payment return to `https://YOUR-APP-DOMAIN/?welcome=1`. Returning to this page never grants access by itself.

Welcome emails for newly verified paid members link to the dashboard. The private WhatsApp invite is delivered only after active-member sign-in. Existing WhatsApp members can mark their onboarding step complete. That is self-confirmation, not a WhatsApp membership audit or removal automation.

## 7. Order-board operation and AI matching

Members save their export profiles, then submit requirements. A listing includes product/service, buyer, destination, quantity/scope, closing date, specifications, requested certificates, delivery term and a contact email explicitly approved for sharing.

- Current listings expire at 23:59:59 India time on their closing date.
- Members can have up to 10 current listings and 20 order updates per day.
- Owners can edit or close their submissions. Reopening an expired listing requires editing its closing date first.
- EPIC can moderate through the private `EPIC Community Orders` tab. Find the Order ID and set **Status** to `hidden`. Preserve other cells and headers. Members cannot override `hidden`. An authorised administrator can restore a reviewed record to `open` or `closed`.
- No entry is automatically called a verified buyer or confirmed commercial order. Report links direct members to EPIC support with the listing reference shown.
- AI matching is requested inside each order or buyer detail. It uses the saved profile and server-resolved opportunity evidence, returns a fit label, reasons, gaps and next step, and cannot modify records or send messages.
- A buyer assessment uses the member’s latest saved report. Examples are excluded. Matching a closed, expired or hidden order is rejected.
- AI has an independent limit of 20 requests/member/hour and 500 across members/hour. Failed provider calls may consume this guard; they never spend a buyer-search credit. Add provider-level spend alerts/limits appropriate to your budget.
- Buyer search remains one completed report per member per calendar week, resetting Monday 00:00 Asia/Kolkata. Reopening reports is free; no-match completed reports count. Failed searches release their reservation.

Do not publish confidential purchase orders, bank details or personal documents inside listing text. The form is for permissioned requirements; document handling can be arranged through the service enquiry flow.

## 8. Checklist maintenance

The checklist covers Indian exporter preparation and selected US, EU, UK, UAE and Saudi product guidance, with explicit review tasks for missing coverage. It is not a universal tariff or legal rules database.

Rules and official references live in `lib/requirements.ts`. Every step records its applicability, responsible party, evidence and dependencies. References whose content was reviewed have a date; references that could not be fully inspected ask for current verification. A checked link is not proof that no later rule has changed.

Before every shipment, confirm the exact commodity, origin, ingredients, processing, destination region, importer and current notices. EPIC should assign a regulatory reviewer to maintain these rules and record review dates only after checking the source. Never label a step approved merely because a member marks its evidence ready. Workbook notes remain in the open page and can be downloaded; they are not saved to the membership sheet.

## 9. Release checks

Use dedicated test members and a separate test spreadsheet/provider mode. Obtain consent before sending real email or submitting a real requirement.

- An active existing member receives a code and enters without another subscription.
- An inactive, blank-status or cancelled member cannot sign in; revocation blocks an existing session.
- A new verified paid subscription reaches the dashboard and receives its private onboarding links after sign-in.
- A profile saves and reloads for the same email on another device.
- One buyer search completes; the second is blocked; saved report reopening works.
- A requirement can be submitted, filtered, edited and closed by its owner. A different member cannot edit it. An expired/hidden requirement is absent from current orders.
- AI matching generates an assessment based on saved profile/record data. Missing keys produce an activation message.
- A service enquiry is stored with its consent and account identity.
- Checklist sources open, scope changes alter steps, and progress/notes appear in the downloaded workbook.
- Phone navigation, forms, drawers and the calculator remain usable.

## 10. Backups, upgrades and rollback

Back up the full spreadsheet including auxiliary tabs, Apps Script source/properties, n8n workflows and credentials using n8n’s supported backup process, private app environment, source version and proxy configuration. Encrypt backups containing credentials and member data. Preserve n8n’s encryption key with its credential backups.

For a VPS update, keep the current image tagged as a rollback version before building. Deploy the new image, verify health and the checks above, and restore the previous image if necessary. Preserve the current runtime settings. A code rollback does not undo sheet writes: avoid restoring an old report ledger into a live account, as it could reset quotas or lose submissions.

For the hosted Site, select the previous known-good version through the hosting controls if needed. Keep the new Apps Script version backward compatible during a cutover. Do not delete member data to troubleshoot access.

Monitor failed n8n executions, email quotas, bridge failures, provider spend and member-support reports. Revisit a database-backed architecture when sheet size, concurrent traffic or Apps Script quotas make the current bridge unsuitable; this release uses Sheets rather than claiming unlimited scale.
