Install
openclaw skills install @plagtech/spraay-shopify-selfhostDeploy the open-source Spraay Shopify app — batch USDC payouts on Base — for your store in ~15 minutes. Walks through the custom app setup, Railway deploy, env vars, and Supabase database, plus fixes for 502s and OAuth errors. Zero Shopify scopes, non-custodial, self-hosted, MIT licensed.
openclaw skills install @plagtech/spraay-shopify-selfhostWalk a merchant from zero to a working batch-USDC-payouts page inside their Shopify admin in ~15 minutes. The app is open source (MIT), non-custodial, and requests zero Shopify scopes — it cannot read or write any store data.
Repo: https://github.com/plagtech/spraay-shopify
@shopify/shopify-app-remix, Vite, Polaris UI (fully embedded in admin)0x833589fCD6eDb6E08f4c7C32D4f71b54bdA029130x1646452F98E36A3c9Cfc3eDD8868221E207B5eEC (verified; never substitute another address)shopify schemaConfirm the user has (or help them create) each of these before starting:
plagtech/spraay-shopify (Node 20+ required if running outside Docker: >=20.19 <22 || >=22.12).railway.json and Dockerfile, so the build is automatic.your-app.up.railway.app.The app will crash-loop until env vars are set — that's expected. Continue.
https://your-domain/auth/callbackhttps://your-domain/auth/shopify/callbackhttps://your-domain/api/auth/callbackSet these in Railway → service → Variables. The repo's .env.example documents each one; the authoritative reference:
| Variable | Value | Notes |
|---|---|---|
SHOPIFY_API_KEY | App Client ID | Public — ships in the browser bundle; safe to share |
SHOPIFY_API_SECRET | App Client Secret | Secret — signs webhook HMACs and OAuth. Never commit or paste into chat logs |
SHOPIFY_APP_URL | https://your-app.up.railway.app | Must exactly match the App URL in Shopify settings; no trailing slash |
SCOPES | (empty) | Intentional. The app never touches store data. Leave blank unless the fork adds Shopify API usage — then keep it in sync with shopify.app.toml |
DATABASE_URL | postgresql://user:pass@host:6543/postgres?schema=shopify&pgbouncer=true | Supabase pooled connection (transaction pooler, port 6543) — used at runtime |
DIRECT_URL | postgresql://user:pass@host:5432/postgres?schema=shopify | Supabase direct connection (port 5432) — used by prisma migrate |
NODE_ENV | production | |
PORT | 3000 | Load-bearing. See gotcha below |
SHOP_CUSTOM_DOMAIN | (optional) | Only if the dev store uses a custom domain |
The PORT=3000 gotcha (most common failure): the Dockerfile's EXPOSE 3000 sets Railway's domain target port to 3000. If PORT is unset, Railway injects PORT=8080, the app binds :8080, the proxy still routes to :3000, and every route returns 502. If the user reports 502s on all routes, check this first.
Database notes: keep ?schema=shopify on both URLs so the app's tables stay isolated from anything else in the Supabase project. Both URLs point at the same database — only the port and pooling differ.
After saving variables, redeploy. Migrations run automatically on boot (prisma generate && prisma migrate deploy, then remix-serve). A clean boot — migrate succeeds and remix-serve prints its listen URL — means Steps 1–3 are done.
Diagnostic tip: a 502 while Railway shows the deployment "Online" is a port/proxy mismatch, not a crash. If the logs show a successful migrate and a listening server, fix PORT, don't debug the app.
From the Dev Dashboard, install the app on the store. Complete the OAuth prompt.
Open the app from the Shopify admin sidebar — the Polaris UI should render embedded.
Run a small real test: CSV with 1–2 recipients and small amounts. Header row is optional (first two columns are assumed address, amount); optional extra columns name, email, memo are supported, and the payout screen has a Download CSV template link and Load sample data button. Example:
wallet_address,amount
0xYourOtherWallet...,1.00
Review screen shows recipients, total, and the 0.3% protocol fee → connect wallet → approve USDC → execute → confirm the BaseScan link resolves.
For hacking on the app itself (not needed for a production deploy): npm install && npm run dev. The Shopify CLI creates a tunnel and injects SHOPIFY_API_KEY, SHOPIFY_API_SECRET, and SHOPIFY_APP_URL, so local .env only needs DATABASE_URL and DIRECT_URL.
Point a subdomain (e.g. payouts.yourstore.com) at the Railway service via CNAME, wait for the SSL cert to issue, then update SHOPIFY_APP_URL and the App URL + redirect URLs in Shopify settings to match. All three must agree or OAuth breaks.
| Symptom | Likely cause | Fix |
|---|---|---|
| 502 on every route | Railway injected PORT=8080 | Pin PORT=3000 |
| OAuth redirect error / "invalid redirect_uri" | App URL mismatch | SHOPIFY_APP_URL, Shopify App URL, and redirect URL prefixes must be identical (scheme, host, no trailing slash) |
| Prisma migrate fails | Using pooled URL for migrations | Migrations need DIRECT_URL (port 5432); runtime uses DATABASE_URL (port 6543) |
| App loads outside admin but not embedded | Wrong App URL or missing HTTPS | Embedded apps require the exact HTTPS App URL configured in Shopify |
| Wallet won't connect | Wrong network | The app targets Base (8453); switch network in the wallet |
shopify-batch-payouts skill