Skip to content
prod 352bb92
Browse

1 · Dependencies & assets

Objective — get the frozen vendor snapshot ready to boot: authenticate Composer to dodge the rate limit, install PHP and JS dependencies without clobbering vendor patches, audit .env.example for fail-loud placeholders, then build and commit frontend assets.

Steps at a glance:

  1. Authenticate Composer first — Give Composer a GitHub token before installing — anonymous pulls hit a 60-request/hour limit that fails large CodeCanyon installs partway through.
  2. Install PHP dependencies — Pull the locked PHP packages — but gate on vendor patches first if the ZIP shipped a pre-built vendor/.
  3. Audit .env.example for fail-loud placeholders — A .env.example that ships with plausible-but-fake values (APP_KEY=base64:abc…, DB_DATABASE=laravel) is dangerous: a teammate copies it, the app boots against the wrong database, and the failure surfaces hours later as corrupt data.
  4. Build & commit frontend assets — Frontend assets are built on your machine and committed, so the production server never needs Node.js (the build-locally strategy).
  5. Run a security & dev-dependency audit — Capture a vulnerability baseline now — it’s the reference point for Phase 9 hardening. Don’t chase every advisory mid-setup; record the counts and move on unless something is critical in a runtime path.
  6. Detect payment/Stripe billing architecture — CodeCanyon Laravel apps ship wildly different payment wiring: canonical Laravel Cashier subscriptions, ad-hoc invoice-per-cycle code, module-per-gateway packages, or multiple architectures in the same codebase.

The first boot starts with dependencies — but with three traps: Composer’s anonymous rate limit, the vendor’s patched vendor/ tree, and a .env.example full of real-looking placeholders that fail silently instead of loudly.

Give Composer a GitHub token before installing — anonymous pulls hit a 60-request/hour limit that fails large CodeCanyon installs partway through.

  1. Hand Composer a GitHub token so it never hits the anonymous limit.

    Terminal window
    composer config -g github-oauth.github.com "$(gh auth token)"
    # Expected: no output (the token is written to Composer's global auth.json)
    • ✅ The token is set without ever printing on screen.
  2. Confirm it landed with an exit-code check — never by reading the file.

    Terminal window
    composer config -g github-oauth.github.com >/dev/null 2>&1 && echo "✅ token set"
    # Expected: ✅ token set
    • ✅ token set prints.

Pull the locked PHP packages — but gate on vendor patches first if the ZIP shipped a pre-built vendor/.

  1. If a shipped vendor/ exists, diff it before any install (skip if vendor/ came from a prior lock-file install).

    /tech-stack/laravel/codecanyon/build/playbooks/setup-new/02-code-repo/03-extract-and-snapshot/
    if [ -d vendor ] && [ -f composer.lock ]; then
    echo "Shipped vendor/ detected — run Phase 2 extract/snapshot or Phase 3 commit/secure vendor-diff before composer install"
    # or page 6: /tech-stack/laravel/codecanyon/build/playbooks/setup-new/03-local-dev/06-commit-and-secure/
    fi
    • ✅ Either no shipped vendor/ exists, or author patches are documented in Zaj-CUSTOMIZATIONS.md before the install below.
  2. Install from the lock file.

    Terminal window
    composer install
    # Expected: "Generating optimized autoload files" with no errors
    • vendor/ is populated and Composer exits cleanly.
  3. If Composer dies with Allowed memory size … exhausted, lift the limit for the run.

    Terminal window
    COMPOSER_MEMORY_LIMIT=-1 composer install
    # Expected: the install completes without the memory error
    • ✅ Install completes under the lifted memory limit (if needed).
  4. Force a clean vendor tree if the ZIP shipped a pre-built one — composer install will NOT do it. When the vendor ships vendor/ already at the locked versions, composer install prints “Nothing to install, update or remove” and does not rewrite file contents — so any author tampering, license phone-home, or anti-null injection inside vendor/ survives. (Runtime-proven on a module-based CodeCanyon app: a planted beacon survived composer install 6→6; composer reinstall dropped it 6→0 with the route count unchanged.) Once §3 below confirms the seam, re-extract upstream:

    Terminal window
    # Re-extract specific packages the vendor tampered with (preferred — narrow, fast):
    composer reinstall <vendor>/<package> [<vendor>/<package> ...]
    # OR full clean rebuild when tampering is widespread / unknown:
    if [ -d vendor ]; then
    mv vendor "vendor.backup.$(date +%Y%m%d-%H%M%S)"
    fi
    composer install
    # Then grep-verify the formerly-tampered files are clean (example markers — adjust per finding):
    grep -rIn "envato\.\|verify\.js\|getScript\|getCourant\|A9H" vendor/ 2>/dev/null | head
    # Expected: reinstall completes; the tamper markers are GONE (zero matches)
    • ✅ The known-tampered vendor files are restored to clean upstream (grep shows zero tamper markers).
  5. Capture the required PHP extensions — the list the server must enable in Phase 4/9.

    Terminal window
    composer check-platform-reqs 2>/dev/null | grep -E "^ext-" | awk '{print $1}' | sort
    # Expected: a sorted list of ext-* requirements — save it for server setup
    • ✅ The ext-* requirements are recorded for staging/production provisioning.

3. Audit .env.example for fail-loud placeholders

Section titled “3. Audit .env.example for fail-loud placeholders”

A .env.example that ships with plausible-but-fake values (APP_KEY=base64:abc…, DB_DATABASE=laravel) is dangerous: a teammate copies it, the app boots against the wrong database, and the failure surfaces hours later as corrupt data. Placeholders must fail loud — empty or obviously-fake — so a missing value crashes immediately.

  1. Surface the risky keys.

    Terminal window
    grep -E "^(APP_KEY|DB_DATABASE|DB_USERNAME|DB_PASSWORD|MAIL_|STRIPE_)" .env.example || true
    # Expected: a line per key — flag any with a real-looking value
    • ✅ The keys carrying plausible-but-fake values are visible.
  2. Replace any real-looking value with a loud placeholder.

    KeyLoud placeholder
    APP_KEY=leave blank — key:generate fills it
    DB_DATABASE="REPLACE_ME_DB_NAME"
    DB_USERNAME="REPLACE_ME_DB_USER"
    DB_PASSWORD="REPLACE_ME_DB_PASS"
    STRIPE_KEY= / STRIPE_SECRET=leave blank
    • ✅ Every placeholder is blank or REPLACE_ME_* — nothing that could silently boot the wrong way.

Frontend assets are built on your machine and committed, so the production server never needs Node.js (the build-locally strategy). Install JS deps, then build.

  1. Install JS deps and build.

    Terminal window
    npm install
    npm run build # outputs to public/build/
    # Expected: a clean build with files written under public/build/
    • ✅ The build finishes and public/build/ is populated.
  2. Force-track the build output — it’s often gitignored by default, but the server relies on it.

    Terminal window
    git add -f public/build/
    ls public/build/manifest.json # must exist
    # Expected: the manifest.json path prints
    • public/build/manifest.json exists and is staged in git.

Capture a vulnerability baseline now — it’s the reference point for Phase 9 hardening. Don’t chase every advisory mid-setup; record the counts and move on unless something is critical in a runtime path.

  1. Record the audit baseline.

    Terminal window
    npm audit --omit=dev || true
    composer audit || true
    # Expected: advisory counts printed — note them, don't chase them all now
    • ✅ A baseline of npm + Composer advisories is recorded.
  2. Confirm composer.json doesn’t autoload dev files in production — a "files" array under autoload-dev breaks the production boot (composer install --no-dev skips it). The full pre-deploy check lives on page 5.

    • ✅ No production-required file is hiding under autoload-dev.

6. Detect payment/Stripe billing architecture

Section titled “6. Detect payment/Stripe billing architecture”

CodeCanyon Laravel apps ship wildly different payment wiring — canonical Laravel Cashier subscriptions, ad-hoc invoice-per-cycle code, provider modules under packages/, or multiple architectures in the same codebase. Knowing which pattern is live gates every Stripe decision in Phase 6 payments setup. Run the Composer/package/code checks now, while vendor/ is fresh, and lock the answer into Zaj-CUSTOMIZATIONS.md.

  1. Check composer.json for stripe/payment SDKs first — module vendors can hide payment code outside app/.

    Terminal window
    grep -nE '"[^"]*(stripe|paypal|razorpay|paddle|cashier|omnipay)[^"]*"\s*:' composer.json 2>/dev/null | head -30
    # Expected: stripe/payment SDK package names, or no output
    • ✅ Any SDK hit is recorded before code greps. A Stripe SDK hit means the app has Stripe-capable code even if app/ is quiet.
  2. Check for provider modules under packages/.

    Terminal window
    find packages -maxdepth 3 -type d \
    \( -iname "*Stripe*" -o -iname "*Paypal*" -o -iname "*Razorpay*" -o -iname "*Paddle*" \) \
    2>/dev/null | sort | head -30
    # Expected: payment gateway package folders, or no output
    • ✅ Module-per-gateway providers are surfaced before deciding Stripe is absent.
  3. Check for canonical Laravel Cashier use (subscription model via the Billable trait).

    Terminal window
    grep -rn "use.*Billable\|->newSubscription\|->subscriptions()\|Cashier::" \
    app/ packages/ --include="*.php" 2>/dev/null | head -20
    # Expected: lines containing Billable trait, newSubscription(), or Cashier:: facade — or no output
    • ✅ If output exists: canonical Cashier is present (at least partially). Note the files.
    • ✅ If no output: no Cashier subscriptions — the app uses ad-hoc invoices only.
  4. Check for ad-hoc / custom Stripe webhook controller (invoice-per-cycle telltale).

    Terminal window
    grep -rn "StripeWebhookController\|stripe.*invoice\|invoice.*stripe" \
    app/ packages/ --include="*.php" -i 2>/dev/null | head -20
    # Expected: custom webhook handler or invoice references — or no output
    • ✅ A custom StripeWebhookController confirms ad-hoc billing is present.
  5. Check for dual-subsystem billing (SuperAdmin operator billing vs tenant-level billing — two separate Stripe flows).

    Terminal window
    grep -rn "SuperAdmin\|superadmin\|super_admin" routes/ app/ packages/ \
    --include="*.php" 2>/dev/null | grep -i "stripe\|subscription\|billing" | head -20
    # Expected: SuperAdmin-namespaced billing routes signal the dual-subsystem pattern
    • ✅ If SuperAdmin-scoped Stripe routes appear alongside non-SuperAdmin Cashier use: dual-subsystem confirmed.
  6. Record the findings in Zaj-CUSTOMIZATIONS.md.

    Use the decision table below to classify what you found, then replace the earlier PROVISIONAL block with a VERIFIED block:

    What greps showedArchitectureStripe account strategy
    Cashier traits throughout, no custom webhook controllerSingle-tenant CashierOne Stripe account; restricted key rk_test_app_* safe
    Custom StripeWebhookController, no Cashier traitsAd-hoc invoices onlySingle Stripe account; standard key; webhook endpoint required
    Both Cashier traits AND custom controllerDual-subsystemPrefer a single Stripe account (Option C); both subsystems share one account
    SuperAdmin-scoped + non-SuperAdmin CashierDual-subsystem (operator + tenant)Prefer Option C; scope rk_test_app_* carefully
    Composer SDKs and provider modules under packages/[vendor]/[Gateway]/, but little app/ wiringvendor-module-per-gatewayKeys usually live in the admin DB Settings UI, not .env; Phase 6 applies feedback_tier_gating_model.md to the plans / packages tables
    No Stripe references found across Composer, packages/, app/, config/, and routes/No StripeSkip; note no_stripe: true
    # Run from the project root — updates Zaj-CUSTOMIZATIONS.md in-place
    import pathlib, datetime
    today = datetime.date.today().isoformat()
    f = pathlib.Path("Zaj-CUSTOMIZATIONS.md")
    if not f.exists():
    print("⚠️ Zaj-CUSTOMIZATIONS.md missing — create it first (Phase 1/2), then re-run.")
    raise SystemExit(1)
    text = f.read_text()
    old = "## Stripe account strategy — PROVISIONAL"
    new = f"## Stripe account strategy — VERIFIED {today}"
    if old in text:
    text = text.replace(old, new)
    # Append verification note after the header line
    note = (
    f"\n\n> **Verified {today}** via Phase-3 architecture detection greps.\n"
    "> Grep results: <!-- paste summary here -->\n"
    "> Architecture class: <!-- Cashier / Ad-hoc / Dual-subsystem / vendor-module-per-gateway / None -->\n"
    )
    text = text.replace(new, new + note, 1)
    f.write_text(text)
    print(f"✅ Updated: PROVISIONAL → VERIFIED {today}")
    else:
    print("⚠️ No PROVISIONAL block found — add the Stripe strategy block manually.")
    # Expected: "✅ Updated: PROVISIONAL → VERIFIED <date>"
    • Zaj-CUSTOMIZATIONS.md now has a VERIFIED Stripe strategy block with the grep summary and architecture class filled in.

Do not mark this step done until every box below is checked.

  • 🤖 Composer authenticated — no rate-limit failures.
  • 🤖 Dependencies installedcomposer install and npm install succeeded.
  • 🤖 Vendor patches noted — any shipped-vendor/ patches flagged for the page-6 comparison.
  • 🤖 Tampered vendor force-cleanedcomposer reinstall <pkg> (not plain composer install) used where the ZIP shipped a tampered vendor/; tamper-marker grep returns zero.
  • 🤖 Vendor seam proven — boot + route smoke-test before/after reinstall; AFTER ≥ BEFORE (no route regressed).
  • 🤖 Placeholders loud.env.example values are blank or REPLACE_ME_*.
  • 🤖 Build force-trackedpublic/build/manifest.json built and git add -f’d.
  • 🤖 Audit baseline recordednpm audit / composer audit counts captured.
  • 🤖 Stripe architecture verifiedZaj-CUSTOMIZATIONS.md updated with VERIFIED strategy, or skipped and no_stripe: true noted.