diff --git a/.claude/skills/shopify-app-launch/SKILL.md b/.claude/skills/shopify-app-launch/SKILL.md new file mode 100644 index 0000000..5e60924 --- /dev/null +++ b/.claude/skills/shopify-app-launch/SKILL.md @@ -0,0 +1,299 @@ +--- +name: shopify-app-launch +description: Standard end-to-end process for building, deploying and submitting a public Shopify app to the App Store (dev setup, extensions, protected data, VPS deploy, pricing, listing assets, screencast, review submission). Use whenever the user is building a Shopify app, preparing it for release, deploying it, filling the Partner Dashboard listing, or submitting it for App Store review. +--- + +# Shopify app launch process + +The standard process for taking any public Shopify app from an empty folder to "Submitted for review". It was first run on Offerlyst (Bundles & BOGO, October 2026); `references/offerlyst-example.md` holds that app's concrete values as a worked example. + +Work through the phases in order. Each phase ends with a checklist; do not start the next phase while an item in the current one is open, unless the user says to. Track progress with a todo list that mirrors these checklists, and give the user a short status line at the end of each phase. + +## Ground rules for Claude + +- **Secrets never go in chat or Git.** This covers the client secret, DB passwords, encryption keys and storefront passwords. Generate them on the server (`openssl rand -hex 32`) and put them in `.env`, which is gitignored. If the user pastes one, tell them to rotate it. +- **The user types every password.** Claude does not enter passwords, payment details or create accounts. When a page needs one, open it, highlight the field and hand over to the user. +- **Before any irreversible click, confirm.** "Submit for review", deleting an app, and accepting terms or OAuth grants each need an explicit yes in chat for that click. +- **Cookie banners:** never click "Accept". If one covers the screen while recording, hide it with JS. +- **Use the Shopify AI Toolkit skill (`shopify`)** for every Admin GraphQL query, Function, Polaris and Liquid file. Search the docs, then validate. If a validator is broken, say so and fall back to `tsc` or `shopify app build`. +- **Verify, don't assume.** After every deploy, run `curl` against the live URL and check that the change you made is actually there. + +--- + +## Phase 1: Decide the product before coding + +1. **Write the brief:** + - the problem + - the 3–5 core features + - which features each plan gets + - the competitors (name, entry price, rating, what they lack) +2. **Choose the name.** + - Check the App Store (`apps.shopify.com/search?q=`) and domain availability before the user commits. + - The name must not contain "Shopify". + - Keep it under 30 characters. The listing title shows as `Name: Short descriptor` (e.g. "Offerlyst: Bundles & BOGO"). + - Pick an `app handle`, which becomes the App Store URL (`apps.shopify.com/`). +3. **Choose the app domain.** A subdomain of the user's company domain works (e.g. `bundles.metatronlabz.com`). It must not contain "shopify" or "example". +4. **Choose the surfaces.** The options are: + - embedded admin (App Home) + - theme app extension (app embed and/or blocks) + - app proxy (storefront pages under `/apps/`) + - Shopify Function (discounts, delivery, payment, validation) + - checkout UI extension + - webhooks + + Each one adds review requirements (see Phase 8). +5. **Brand assets.** The user supplies or approves these: + - the icon (1200×1200, full-bleed, no text or Shopify logo) + - a horizontal logo + - a brand palette + +**Checklist 1:** brief written · name and handle checked and approved · domain chosen · surfaces listed · icon ready + +--- + +## Phase 2: Scaffold and develop + +1. **Scaffold:** `shopify app init`, using the React Router template. Keep its pinned API version, or set the latest stable version (e.g. `2026-10`) in both `shopify.app.toml` and the extension tomls. +2. **Admin UI:** use Polaris web components (`s-page`, `s-section`, `s-grid`, `s-button` ...) and App Bridge. + - Lay out rows that hold long text with `s-grid` (`gridTemplateColumns="auto 1fr auto"`), so the help text wraps inside its own column. + - On Home, show a **setup checklist**: turn on the app embed (deep link `.../themes//editor?context=apps&activateAppId=/`), create the first item, then preview it. Reviewers check onboarding. +3. **Database:** Prisma. Use SQLite in dev only and switch to Postgres before deploying. Commit the migrations. +4. **Shopify Functions:** + - Prototype in JS. Before choosing a language, measure the instruction count with a large cart (200 lines). + - If JS goes over the limit (11M instructions for discounts), port the function to **Rust** (`shopify_function` crate). + - Keep a JS reference engine plus shared test vectors, so both implementations are tested against the same cases (parity tests). + - Always build with `shopify app function build`. A raw `cargo build` fails with "unknown import shopify_function_v2". + - Windows: use the GNU Rust toolchain if MSVC isn't installed, and build from PowerShell, because Git Bash's `link` shadows the linker. + - Pass configuration through metafields: `$app:configuration` for the function, and `input_variables` for query variables. +5. **Theme app extension:** + - Inherit the merchant theme's fonts and colours; offer accent colour and radius as settings. + - Mobile first: 44px tap targets and 16px inputs. + - Read storefront data from an app-data metafield (`app.metafields..`). +6. **App proxy pages:** the merchant's theme CSS may not load on these, especially on draft themes, so inline the app's own CSS and JS. Respect `path_prefix`. Verify the HMAC signature and return 400 for unsigned requests. +7. **Webhooks** (declare them in `shopify.app.toml`): + - `app/uninstalled` + - `app/scopes_update` + - `app_subscriptions/update` (if paid) + - the data topics you need + - **the three mandatory compliance topics:** `customers/data_request`, `customers/redact`, `shop/redact` +8. **Scopes:** request the minimum. Remember the scopes that features imply, e.g. an app proxy needs `write_app_proxy`, and theme embed detection needs `read_themes`. +9. **Tests:** + - unit tests for the core logic + - parity and differential tests for any Function + - `tsc --noEmit` + - `shopify app build` +10. **Public pages:** `/privacy` and `/terms`. The terms must include data processing terms if you touch customer data. +11. **Landing page (`/`):** do **not** show a manual "shop domain" login form. It violates App Store requirement 2.3.1. Show product info only; installs start from the App Store. + +**Checklist 2:** app runs in `shopify app dev` on a dev store · Function within limits · tests green · tsc clean · compliance webhooks · privacy and terms pages · no shop-domain form + +--- + +## Phase 3: Link the production app config + +1. Run `shopify app config link` to create or link the production app. + - **Back up `shopify.app.toml` first.** `config link` rewrites it and drops webhooks, the proxy, name, handle and api_version. Restore them afterwards. +2. In `shopify.app.toml`: + - `application_url = "https://"` + - `redirect_urls = ["https:///auth/callback"]` + - `[build] automatically_update_urls_on_dev = false`, so `shopify app dev` doesn't overwrite the production URLs + - check the `name`, `handle`, scopes, `[app_proxy]` and webhook subscriptions +3. Commit the toml. It holds no secrets. The client secret is shown only in the Dev Dashboard; copy it straight into the server's `.env`. + +**Checklist 3:** toml has production URLs · URL auto-update off · webhooks, proxy and scopes present · committed + +--- + +## Phase 4: Protected customer data (if you read orders or customers) + +Any `orders/*` or `customers/*` webhook, or any order or customer query, triggers this. Without it, `shopify app deploy` is blocked. + +1. Go to Partner Dashboard › App › **API access requests** › Protected customer data › Request access. +2. **Level 1** (no personal fields) is usually enough. Leave name, email, phone and address unticked unless a feature truly needs them; ticking them moves the request to Level 2 and adds many more questions. +3. Give each data use a reason (e.g. "Analytics: apply and report bundle discounts"). +4. Answer the data protection questions truthfully, and implement what you claim: + - **Encryption at rest:** AES-256-GCM for identifiers, and HMAC hashes where you only need to match values. Use a 64-hex-character key in `.env` (`DATA_ENCRYPTION_KEY`). + - **Retention:** delete shop data after uninstall, via `shop/redact` (48 hours later). + - **A data processing agreement in the terms:** controller and processor roles, breach notice within 72 hours, sub-processors. + - **Encrypted backups:** `pg_dump` piped through `gpg` or `age`. + - **Access limited to staff,** documented in `SECURITY.md`. +5. Re-run `shopify app deploy`. + +**Checklist 4:** request submitted · encryption implemented · terms include data processing terms · deploy succeeds + +--- + +## Phase 5: Deploy to the VPS + +1. **Dockerfile:** a multi-stage build that installs all dependencies, builds, then copies the output into a slim runtime that installs production dependencies only. Do **not** prune dev dependencies before `npm run build`. Commit `package-lock.json` (and `Cargo.lock`). +2. **docker-compose:** Postgres plus the app. + - Required env vars: `SHOPIFY_API_KEY`, `SHOPIFY_API_SECRET`, `SHOPIFY_APP_URL`, `SCOPES`, `DATABASE_URL`, `DATA_ENCRYPTION_KEY`. + - Use `${VAR:?}` so a missing variable fails loudly. + - The app runs `prisma migrate deploy` on start. +3. **Reverse proxy:** + - If the VPS has nothing on ports 80/443, add a Caddy service (automatic HTTPS). + - If the VPS **already runs Nginx**, add a compose override that binds the app to `127.0.0.1:`, an Nginx server block that proxies to it, and certbot for TLS. Check which ports are already in use first. +4. **On the server:** + ``` + mkdir -p /opt/ && cd /opt && git clone && cd + cp .env.example .env # fill secrets on the server: openssl rand -hex 32 + docker compose -f docker-compose.yml [-f docker-compose.nginx.yml] up -d --build + docker compose logs -f app + ``` +5. **DNS:** an A record for the app domain pointing to the VPS IP. +6. **Verify from outside:** + - `/privacy` and `/terms` return 200. + - Unsigned requests to `/proxy` and `/webhooks/*` return 400 or 401. + - `/` loads with **no** shop-domain form. +7. From the PC, run `shopify app deploy`. It releases the extensions, Function, toml config and webhooks as a new app version. +8. **Redeploy routine:** after every push, run `git pull && docker compose ... up -d --build app` on the VPS, then confirm the change is live with `curl`. + +**Checklist 5:** HTTPS live · DB migrated · health checks pass · `shopify app deploy` released · app installs on the dev store and opens embedded + +--- + +## Phase 6: Test on a development store + +1. Install from the Dev Dashboard (Test on development store). Open the app from the admin's Apps menu. +2. Walk through every feature as a merchant: onboarding checklist → app embed on → create → publish → storefront → cart → checkout. +3. Test edge cases: + - Several offers or features running at once + - Removing items, which should remove the effect + - Guest storefront access + - Uninstall, then reinstall without errors +4. **Billing:** with managed pricing, choose each paid plan on the dev store. These are test charges, $0. Confirm that the app reads the plan and applies its limits. +5. Fix the bugs, redeploy (Phase 5.8) and retest. + +**Checklist 6:** full flow works · edge cases pass · each plan's limits verified · uninstall and reinstall clean + +--- + +## Phase 7: Pricing (Shopify managed pricing) + +1. Go to Partner Dashboard › App › Distribution/Listing › **Pricing** › Managed pricing. +2. Create the plans. Keep stable keywords in the plan names (e.g. Starter, Growth, Plus), because the app maps the subscription name to its limits. Add yearly prices if wanted, and a free trial (e.g. 14 days) on paid plans. +3. Make the Free plan public so search shows "Free plan available". +4. A private `shopify-test` plan is created automatically. Leave it alone. +5. The app's Plans page links to `https://admin.shopify.com/store//charges//pricing_plans`. + +**Checklist 7:** plans public · trial set · plan names match the code mapping · test subscription approved + +--- + +## Phase 8: Listing content and assets + +Go to Partner Dashboard › Distribution › Shopify App Store › Create listing (English). + +| Asset | Spec | +|---|---| +| App icon | 1200×1200 PNG or JPG, full bleed, no text, no Shopify logo | +| Feature image | 1600×900, no Shopify trademark or tagline, no pricing, reviews or outcome claims | +| Screenshots | 1600×900 desktop, 3–6 of them, with alt text for each; no pricing, reviews or claims on the image | +| Screencast | a URL only (YouTube Unlisted, Loom, Vimeo); see Phase 9 | +| Promo video | optional public YouTube link; can be skipped | + +- **Screenshots:** capture the real UI from the dev store, then frame it in brand colours with a caption. A reusable script exists: `make_screenshots.py` takes raw images and outputs 1600×900 images plus `alt-text.txt`. Copy it from the Offerlyst repo's `brand/` folder. +- **Text fields:** + - name (it must match the toml `name`) + - subtitle + - introduction + - details + - feature list + - 5 search terms, one idea each, mixing broad terms with niche terms where you can rank + - primary category and tags + - Never name competitors. +- **URLs:** privacy policy, website, support FAQ (optional), support email, emergency contact (on the account). +- **App testing information:** + - **Screencast URL** + - **Testing instructions:** numbered steps covering setup, each core feature, storefront and checkout checks, how to reach paid features (test charge), and uninstall. + - The **demo store URL**, and the **storefront password**. The user types the password themselves. + - "No separate login: uses Shopify authentication" if true. +- Save, then check that the listing shows no issues. + +**Checklist 8:** icon · feature image · 3–6 screenshots with alt text · all text fields · search terms · URLs · emergency contact · testing instructions with demo store and password + +--- + +## Phase 9: Record the screencast + +1. Prepare the dev store with realistic data and at least one live item to show. +2. The user records the **entire screen** with their own tool (Snipping Tool, OBS, Loom). Claude drives Chrome through Claude in Chrome while the user records. + - Before recording, confirm the Chrome window is visible and the storefront domain is allowed for the extension. + - Do not type into fields Claude can't see. Verify each click with a screenshot or a JS read. + - Add on-screen English captions via an injected fixed-position div (brand colour, bottom centre, `pointer-events:none`). Place it so it doesn't cover the buttons being clicked. + - Hide cookie banners and newsletter pop-ups with JS. Never click Accept. +3. **Scene order** (about 2–4 minutes): + 1. Home and onboarding + 2. Theme editor app embed + 3. Create and configure the core item + 4. Publish or save + 5. List view + 6. Secondary features + 7. Storefront page + 8. Add to cart + 9. Cart + 10. Checkout showing the effect + 11. Plans page + 12. Closing tagline +4. The user trims the recording and uploads it to YouTube as **Unlisted**, with Audience set to "Not made for kids". Claude writes the title and description. Then Claude pastes the URL into `screencastUrl` and saves, then reloads the page to confirm it saved. + +**Checklist 9:** recorded · trimmed · uploaded Unlisted · URL saved in listing + +--- + +## Phase 10: App Store review page and submission + +Go to Partner Dashboard › Distribution › **App Store review**. + +1. **Preliminary steps** must all be green: + - queries supported API versions + - configuration requirements fixed + - emergency contact added + - listing created and checked for common issues + - protected data request complete +2. **Capabilities:** select what applies (e.g. embedded, online store). Each one adds checks: + - **Embedded app checks:** session tokens, App Bridge, no redirects out of the admin. + - **Online Store checks:** these run automatically every 2 hours. "Use theme app extensions" must pass. Tick "Confirm you have included detailed onboarding instructions" only if Home really has the setup checklist and deep link. +3. **AI self review:** run `/shopify-app-store-review` (Shopify AI Toolkit, topic `app-store-review`) against the codebase and fix everything it finds. Common catches: + - a manual shop-domain form (2.3.1) + - missing compliance webhooks + - unencrypted personal data + - billing outside the Shopify Billing API +4. **Before you submit:** + - [ ] the live server runs the latest commit (verify with `curl`, e.g. no shop-domain form on `/`) + - [ ] `shopify app deploy` was run for the latest extensions and config + - [ ] the testing instructions contain no placeholders (`ADD-...-HERE`) + - [ ] the screencast URL opens without signing in + - [ ] the demo store is reachable with the stated password +5. **The user** ticks "I've reviewed all the App Store Requirements" and clicks **Submit for review**. Claude clicks Submit only after an explicit yes in chat. + +**Checklist 10:** all review items green · AI self review clean · pre-submit gate passed · submitted + +--- + +## Phase 11: After submission + +- Watch the Partner email. Reviewers reply with numbered requirement IDs. Fix each one, redeploy, verify, and reply in the review thread. +- First reviews take days to a few weeks, so plan launch marketing around approval, not submission. +- After approval: + - clean up the dev store test data + - turn on listing translations + - collect the first honest reviews (incentivised reviews break Shopify's rules) + - apply for Built for Shopify once the app qualifies +- Keep `api_version` current. Shopify drops old versions roughly every 12 months. + +## Known pitfalls (from real runs) + +| Symptom | Cause | Fix | +|---|---|---| +| Function over the instruction limit | JS parsing a big cart | Port to Rust; use counting sorts instead of comparison sorts in hot loops | +| `unknown import shopify_function_v2` | Raw cargo build | `shopify app function build` | +| Linker not found on Windows | No MSVC / Git Bash `link` | GNU toolchain; build from PowerShell | +| toml lost webhooks, proxy, name | `shopify app config link` | Back up first; restore | +| Deploy blocked: protected customer data | Order or customer webhooks | Phase 4 request (Level 1) | +| Docker build fails at `npm run build` | Dev deps pruned too early | Multi-stage Dockerfile; commit the lockfile | +| Port 80/443 busy on the VPS | Existing Nginx | Bind the app to 127.0.0.1:PORT and proxy from Nginx | +| Storefront page unstyled | Proxy page on a draft or other theme | Inline the app's CSS and JS in proxy responses | +| Reviewer flags 2.3.1 | Shop-domain login form on `/` | Remove the form; redeploy; verify live | +| Feature "doesn't work" in test | Test products out of stock or not in the collection | Use in-stock products; check the data before recording | +| Chrome tab "hidden" or screenshot timeouts | Window minimised or not visible | Bring the window forward or open a new tab group; read state with JS | diff --git a/.claude/skills/shopify-app-launch/references/offerlyst-example.md b/.claude/skills/shopify-app-launch/references/offerlyst-example.md new file mode 100644 index 0000000..344214e --- /dev/null +++ b/.claude/skills/shopify-app-launch/references/offerlyst-example.md @@ -0,0 +1,52 @@ +# Worked example: Offerlyst (October 2026) + +Concrete values from the first run of this process. Use them as a pattern, not as defaults for other apps. + +| Item | Value | +|---|---| +| Listing name | Offerlyst: Bundles & BOGO (handle `offerlyst`) | +| Repo | https://git.metatroncube.in/MetatroncubeSoftwareSolutions/bundle-offers-app.git (branch `main`) | +| Local path | D:\MCS_Repos\2026\claude_bundle_offer_shopify_app\bundle-offers-app | +| App domain | https://bundles.metatronlabz.com (VPS, existing Nginx → `127.0.0.1:3310`) | +| VPS path | /opt/bundle-offers-app | +| Redeploy | `cd /opt/bundle-offers-app && git pull && docker compose -f docker-compose.yml -f docker-compose.nginx.yml up -d --build db app` | +| Partner org / app | partners.shopify.com/2451848/apps/435426983937 | +| Listing editor | apps.shopify.com/services/partner-app-submissions//en | +| Review page | partners.shopify.com/2451848/apps/435426983937/distribution/app-store | +| Dev store | veloxautomotive.myshopify.com (password protected) | +| API version | 2026-10 | +| Scopes | read_discounts, read_orders, read_products, read_themes, write_app_proxy, write_discounts, write_files | +| Surfaces | Embedded admin, Rust discount Function (`cart.lines.discounts.generate.run`), theme app extension (embed + 4 blocks), app proxy `/apps/bundles` | +| Protected data | Level 1 (no name/email/phone); order IDs HMAC-hashed, order numbers AES-256-GCM | +| Plans | Free $0 · Starter $9.99/mo ($99/yr) · Growth $24.99/mo ($249/yr) · Plus $59.99/mo ($599/yr); 14-day trial | +| Screencast | https://youtu.be/wYUTfZjhEI4 (Unlisted) | +| Brand files | `brand/` in repo: icon 1200, feature 1600×900, screenshots/listing/1-6 + alt-text.txt, make_screenshots.py | +| Launch runbook (artifact) | https://claude.ai/artifact/JPNkWKhLPfm7Wcid4LHktQ | + +## Screencast YouTube description template + +``` +A walkthrough of , a Shopify app for . + +What this video shows: +• Home page and setup checklist +• Turning on the app embed in the theme editor +• +• +• +• Storefront page and +• Cart and checkout with applied automatically +• Plans, billed through Shopify + +Recorded on a Shopify development store. + +Privacy policy: https:///privacy +Terms: https:///terms +Support: +``` + +## Caption overlay used while recording (inject per page) + +```js +window.olcap=function(t){let d=document.getElementById('olcap'); if(!d){d=document.createElement('div');d.id='olcap';Object.assign(d.style,{position:'fixed',left:'50%',bottom:'40px',transform:'translateX(-50%)',background:'rgba(4,91,89,0.96)',color:'#fff',font:'600 22px/1.35 system-ui,Segoe UI,sans-serif',padding:'14px 28px',borderRadius:'12px',zIndex:2147483647,maxWidth:'80vw',textAlign:'center',boxShadow:'0 6px 24px rgba(0,0,0,.25)',pointerEvents:'none'});document.body.appendChild(d);} d.textContent=t;}; +``` diff --git a/brand/screenshots/listing/1-offers.png b/brand/screenshots/listing/1-offers.png index 94c7dd5..218f397 100644 Binary files a/brand/screenshots/listing/1-offers.png and b/brand/screenshots/listing/1-offers.png differ diff --git a/brand/screenshots/raw/1-offers.png b/brand/screenshots/raw/1-offers.png index 0b34197..5b0d808 100644 Binary files a/brand/screenshots/raw/1-offers.png and b/brand/screenshots/raw/1-offers.png differ