---
slug: "spraay-shopify-self-host"
source_type: "clawhub"
source_url: "https://clawhub.ai/skills/spraay-shopify-selfhost"
repo: ""
source_file: "description"
---
---
name: spraay-shopify-selfhost
description: Deploy and self-host the open-source Spraay Shopify app (batch USDC payouts on Base) for a merchant's store. Use this skill whenever a user wants to install, deploy, set up, self-host, or troubleshoot the Spraay Shopify app (github.com/plagtech/spraay-shopify) - including creating the custom app in the Shopify Dev Dashboard, deploying to Railway, configuring environment variables, setting up the Supabase/Postgres database, fixing 502 errors or OAuth redirect issues, or connecting a custom domain. Also use when a Shopify merchant asks how to add crypto payouts, USDC payments, or affiliate crypto payments to their store admin.
---

# Self-Host the Spraay Shopify App

Walk 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

## Stack at a glance

- Remix + `@shopify/shopify-app-remix`, Vite, Polaris UI (fully embedded in admin)
- wagmi v3 + viem v2 — MetaMask and Coinbase Smart Wallet
- Base (chain 8453), USDC `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`
- Batch contract `0x1646452F98E36A3c9Cfc3eDD8868221E207B5eEC` (verified; never substitute another address)
- Supabase PostgreSQL via Prisma, isolated in a `shopify` schema
- Railway hosting (Dockerfile, node:20-alpine)

## Prerequisites checklist

Confirm the user has (or help them create) each of these before starting:

1. A Shopify store (dev store is fine for testing)
2. A free Shopify Partners account → https://dev.shopify.com
3. A Railway account (hobby tier is enough)
4. A Supabase project (free tier is enough)
5. A wallet holding USDC on Base (MetaMask or Coinbase Smart Wallet)

## Step 1 — Fork and deploy to Railway

1. Fork `plagtech/spraay-shopify` (Node 20+ required if running outside Docker: `>=20.19 <22 || >=22.12`).
2. Easiest path: the **Deploy on Railway** button in the README — the template pre-prompts for every required env var, with PORT defaulted to 3000. Manual path: Railway → New Project → Deploy from GitHub repo → select the fork; the repo ships a `railway.json` and Dockerfile, so the build is automatic.
3. Generate a public domain for the service (Settings → Networking). Note the URL, e.g. `your-app.up.railway.app`.

The app will crash-loop until env vars are set — that's expected. Continue.

## Step 2 — Create the custom app in Shopify

1. Go to https://dev.shopify.com → Apps → **Create app** → **Custom distribution** (single merchant). Custom distribution is Shopify's intended path for merchant-specific tooling — no App Store review involved.
2. Set **App URL** to the Railway URL from Step 1 (https, no trailing slash).
3. Add all three OAuth redirect URLs (each prefixed by the App URL):
   - `https://your-domain/auth/callback`
   - `https://your-domain/auth/shopify/callback`
   - `https://your-domain/api/auth/callback`
4. Copy the **Client ID** and **Client Secret**.

## Step 3 — Environment variables

Set 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.

## Step 4 — Install and test

1. From the Dev Dashboard, install the app on the store. Complete the OAuth prompt.
2. Open the app from the Shopify admin sidebar — the Polaris UI should render embedded.
3. 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:

   ```csv
   wallet_address,amount
   0xYourOtherWallet...,1.00
   ```

4. Review screen shows recipients, total, and the 0.3% protocol fee → connect wallet → approve USDC → execute → confirm the BaseScan link resolves.

## Optional — local development

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`.

## Optional — custom domain

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.

## Troubleshooting

| 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 |

## Security posture (tell the user)

- **Zero scopes:** the app requests no Shopify permissions — it structurally cannot access orders, customers, or products.
- **Non-custodial:** USDC moves only when the merchant's own wallet signs, in their browser. The server never holds keys or funds.
- **Self-hosted:** credentials, database, and infrastructure belong to the merchant.
- The 0.3% protocol fee is itemized on the review screen and collected on-chain by the batch contract. It is contract-enforced on Base, not app code — self-hosting or forking does not remove it, and there is no subscription or plan.
- Security issues → report privately to support@spraay.app, not a public issue.

## Related

- Running payouts after deployment → `shopify-batch-payouts` skill
- Protocol docs → https://docs.spraay.app
