# ShipStation Packing Slip Automation for n8n

This project automates the ShipStation packing slip workflow using Playwright browser automation.
It bridges the gap between n8n and ShipStation's web UI for generating packing slip PDFs.

## What It Does

The script replicates exactly what you showed in your video:

1. Opens ShipStation and navigates to the **Awaiting Shipment** orders page
2. Checks the boxes next to the orders you specify (by order number)
3. Clicks the **Print** dropdown and selects **Packing Slip**
4. Waits for the **Print Preview** modal to load the PDF
5. Clicks the **Download** button to save the packing slip PDF
6. Returns the file path so n8n can pick it up

## Prerequisites

- Node.js 18 or higher
- An n8n instance (self-hosted)
- A ShipStation account with access to `ship2.shipstation.com`

## Installation

```bash
# 1. Clone or copy this folder to your server
cd shipstation-packing-slips

# 2. Install dependencies
npm install

# 3. Install Playwright's browser (Chromium)
npx playwright install chromium

# 4. Create directories for saved sessions and downloads
mkdir -p shipstation-profile downloads
```

## First-Time Setup: Login

You must log in to ShipStation **once** manually so the session (cookies) is saved.
After that, automated runs will reuse the saved session without needing to log in again.

```bash
# Run in headed mode (opens a visible browser window)
npm run login
```

This opens a browser. Log in to ShipStation manually:
- Enter your email and password
- Complete any CAPTCHA or 2FA challenge
- Wait until you see the ShipStation dashboard
- Press Enter in the terminal to save the session

If you prefer to pre-fill credentials:
```bash
SHIPSTATION_EMAIL=your@email.com SHIPSTATION_PASSWORD=yourpassword npm run login
```

## How to Use

### Option A: Command Line (Test a Single Run)

```bash
# Download packing slips for specific orders
node run-once.js --orders "03-15164-04787"

# Multiple orders at once
node run-once.js --orders "03-15164-04787,03-15164-04788,03-15164-04789"

# Custom output directory
node run-once.js --orders "03-15164-04787" --output ./my-pdfs

# Run in headed mode to watch the automation (for debugging)
node run-once.js --orders "03-15164-04787" --headed
```

### Option B: HTTP Server (For n8n Integration)

Start the server:

```bash
npm start
```

The server runs on `http://localhost:3000` and exposes these endpoints:

#### Generate Packing Slips

```
POST http://localhost:3000/print-packing-slips
Content-Type: application/json

{
  "orderNumbers": ["03-15164-04787", "03-15164-04788"]
}
```

Response:
```json
{
  "ok": true,
  "filePath": "/downloads/packing-slips.pdf",
  "fileName": "packing-slips.pdf",
  "downloadUrl": "/download?file=packing-slips.pdf"
}
```

#### Download the PDF File

```
GET http://localhost:3000/download?file=packing-slips.pdf
```

Returns the binary PDF file.

#### List Available Downloads

```
GET http://localhost:3000/downloads
```

#### Health Check

```
GET http://localhost:3000/health
```

## n8n Integration

### Method 1: HTTP Request Node (Recommended)

1. Start the server on your server: `npm start`
2. In n8n, create a workflow with these nodes:

**Node 1: Trigger** (e.g., Schedule, Webhook, or manual)
↓
**Node 2: HTTP Request**
- Method: POST
- URL: `http://localhost:3000/print-packing-slips`
- Body:
```json
{
  "orderNumbers": ["{{$json.orderNumber}}"]
}
```
↓
**Node 3: HTTP Request** (download the PDF)
- Method: GET
- URL: `http://localhost:3000/download?file={{$json.fileName}}`
- Response Format: File
↓
**Node 4: Your next step** (email, upload to Google Drive, etc.)

### Method 2: Docker Compose (Run alongside n8n)

Create a `docker-compose.yml`:

```yaml
version: '3.8'

services:
  shipstation-automation:
    build: .
    ports:
      - "3000:3000"
    environment:
      - PORT=3000
      - OUTPUT_DIR=/app/downloads
      - SHIPSTATION_USERDATA_DIR=/app/profile
    volumes:
      - ./shipstation-profile:/app/profile
      - ./downloads:/app/downloads
    restart: unless-stopped

  n8n:
    image: n8nio/n8n
    ports:
      - "5678:5678"
    environment:
      - N8N_HOST=localhost
      - N8N_PORT=5678
      - N8N_PROTOCOL=http
      - WEBHOOK_URL=http://localhost:5678/
    volumes:
      - n8n_data:/home/node/.n8n
    restart: unless-stopped

volumes:
  n8n_data:
```

### Dockerfile

```dockerfile
FROM node:18-slim

# Install Chromium dependencies
RUN apt-get update && apt-get install -y \
    libnss3 libnspr4 libatk1.0-0 libatk-bridge2.0-0 \
    libcups2 libdrm2 libxkbcommon0 libxcomposite1 \
    libxdamage1 libxfixes3 libxrandr2 libgbm1 \
    libasound2 libpango-1.0-0 libcairo2 \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app

COPY package*.json ./
RUN npm ci --production
RUN npx playwright install chromium

COPY . .

EXPOSE 3000

CMD ["node", "server.js"]
```

## Files Overview

| File | Purpose |
|------|---------|
| `shipstation-packing-slips.js` | Core Playwright automation module |
| `login-setup.js` | First-time login helper (headed mode) |
| `run-once.js` | Standalone CLI for testing single runs |
| `server.js` | HTTP server for n8n integration |
| `package.json` | Dependencies and scripts |

## Troubleshooting

### Session Expired

If you get a login error, the saved session has expired. Re-run:
```bash
npm run login
```

### Wrong Orders Selected

The script selects orders by matching the order number in the table row.
Make sure the order numbers you pass exactly match what appears in ShipStation.
Order numbers typically look like `03-15164-04787`.

### ShipStation UI Changes

If ShipStation updates their UI, the CSS selectors may break. Run in headed mode to debug:
```bash
node run-once.js --orders "YOUR_ORDER_NUMBER" --headed
```

An error screenshot is automatically saved to the downloads folder when something goes wrong.

### CAPTCHA or 2FA

ShipStation may show a CAPTCHA or 2FA challenge during login. You cannot automate past these.
Run `npm run login` in headed mode to complete the challenge manually, then automated runs will
reuse the authenticated session.

### Headless Detection

If ShipStation blocks headless browsers, the script includes anti-detection flags.
If it still fails, try running with `--headed` or use a tool like `puppeteer-extra-plugin-stealth`.

## Important Notes

- ShipStation does **not** have a public API for generating packing slips. This has been
  requested for years but is still not available. Browser automation is the only workaround.
- Browser automation is inherently fragile. If ShipStation changes their UI, the script will
  need to be updated with new selectors.
- The script uses a persistent browser profile to maintain login sessions between runs.
- Smart Document Tracking in ShipStation will mark orders as "Printed" when the PDF is
  downloaded, just like when you do it manually.
