npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@frydigital/payload-plugin-shipstation

v1.0.0

Published

ShipStation integration plugin for Payload CMS ecommerce

Downloads

638

Readme

@frydigital/payload-plugin-shipstation

CI npm version License: MIT

A comprehensive ShipStation integration plugin for Payload CMS ecommerce projects. Provides real-time shipping rate calculation, address validation, multi-package handling, and webhook support for tracking updates.

Note: This plugin uses the ShipStation V1 API (ssapi.shipstation.com). See ShipStation API Documentation for full API reference.

Features

Phase 1 (Available Now)

  • 🚚 Real-time shipping rate calculation via ShipStation API (v1)
  • 🍁 Canadian provincial flat rates (all 13 provinces/territories)
  • 📍 Custom shipping zones with postal code pattern matching
  • 🎁 Free shipping thresholds with rule-based eligibility
  • 📦 Multi-package shipment handling with auto-splitting
  • ✅ Address validation and correction
  • 🔔 Webhook support for shipment tracking updates
  • ⚡ Rate caching (Redis + in-memory fallback)
  • 🔐 Secure API credential management

🚧 Phase 2

  • 🌍 International shipping with customs forms
  • 💼 Multi-carrier account management
  • 📊 Shipping analytics and performance tracking

Installation

1. Install from GitHub Packages

pnpm add @frydigital/payload-plugin-shipstation
# or
npm install @frydigital/payload-plugin-shipstation
# or
yarn add @frydigital/payload-plugin-shipstation

2. Environment Variables

Add the following to your .env file (see .env.example for full template):

# ShipStation V1 API Configuration
# Get credentials from ShipStation: Settings > Account > API Settings

# Option 1: Combined format (API Key:Secret)
SHIPSTATION_API_KEY=your_api_key:your_api_secret

# Option 2: Separate variables
# SHIPSTATION_API_KEY=your_api_key
# SHIPSTATION_API_SECRET=your_api_secret

# Warehouse ID - numeric ID from ShipStation (Settings > Shipping > Ship From Locations)
SHIPSTATION_WAREHOUSE_ID=123456

# Optional: Webhook secret for signature verification
SHIPSTATION_WEBHOOK_SECRET=your_webhook_secret

# Optional: Redis for rate caching
REDIS_URL=redis://localhost:6379

3. Configure Plugin

Add the plugin to your Payload configuration:

// payload.config.ts
import { shipStationPlugin } from '@frydigital/payload-plugin-shipstation'

export default buildConfig({
  plugins: [
    // Add after ecommerce plugin
    shipStationPlugin({
      apiKey: process.env.SHIPSTATION_API_KEY!,
      warehouseId: process.env.SHIPSTATION_WAREHOUSE_ID!,
      
      // Enable features
      enabledFeatures: {
        addressValidation: true,
        multiPackage: true,
        autoCreateShipments: false,
        webhooks: true,
      },
      
      // Canadian provincial rates (in cents)
      provincialRates: [
        { province: 'BC', baseRate: 1200, enabled: true },
        { province: 'AB', baseRate: 1000, enabled: true },
        { province: 'SK', baseRate: 1500, enabled: true },
        { province: 'MB', baseRate: 1500, enabled: true },
        { province: 'ON', baseRate: 1000, enabled: true },
        { province: 'QC', baseRate: 1200, enabled: true },
        { province: 'NB', baseRate: 1500, enabled: true },
        { province: 'NS', baseRate: 1800, enabled: true },
        { province: 'PE', baseRate: 1800, enabled: true },
        { province: 'NL', baseRate: 2000, enabled: true },
        { province: 'YT', baseRate: 2500, enabled: true },
        { province: 'NT', baseRate: 3000, enabled: true },
        { province: 'NU', baseRate: 3500, enabled: true },
      ],
      
      // Free shipping configuration
      freeShippingConfig: {
        threshold: 10000, // $100.00 CAD
        eligibleCountries: ['CA'],
        excludedShippingClasses: ['oversized'],
      },
      
      // Shipping class rate modifiers
      shippingClassModifiers: {
        standard: 1.0,
        expedited: 1.5,
        fragile: 1.3,
        oversized: 2.0,
        custom: 1.0,
      },
      
      // Multi-package limits
      maxPackageWeight: { value: 70, unit: 'pound' },
      maxPackageDimensions: { length: 36, width: 24, height: 24, unit: 'inch' },
      
      // Cache configuration
      cache: {
        enableCache: true,
        cacheTTL: 300, // 5 minutes
        redisUrl: process.env.REDIS_URL,
        fallbackToMemory: true,
      },
      
      // Webhook configuration
      webhookSecret: process.env.SHIPSTATION_WEBHOOK_SECRET,
      enabledWebhookEvents: [
        'tracking.updated',
        'tracking.delivered',
      ],
    }),
  ],
})

Usage

Calculating Shipping Rates

From Frontend/API Route

// app/api/calculate-shipping/route.ts
import { NextRequest, NextResponse } from 'next/server'

export async function POST(req: NextRequest) {
  const { cartId, shippingAddress } = await req.json()
  
  const response = await fetch('/api/shipping/calculate-rates', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ cartId, shippingAddress }),
  })
  
  const { rates, freeShipping } = await response.json()
  
  return NextResponse.json({ rates, freeShipping })
}

Response Format

{
  "rates": [
    {
      "serviceName": "Canada Post Regular Parcel",
      "serviceCode": "canada_post_regular_parcel",
      "carrierCode": "canada_post",
      "shipmentCost": 12.50,
      "otherCost": 0,
      "deliveryDays": 3,
      "estimatedDeliveryDate": "2025-11-23T23:59:00Z"
    }
  ],
  "freeShipping": false,
  "appliedRule": "provincial_rate",
  "cacheHit": true
}

Address Validation

const response = await fetch('/api/shipping/validate-address', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    address: {
      street1: '123 Main Street',
      city: 'Toronto',
      state: 'ON',
      postalCode: 'M5H 2N2',
      country: 'CA',
    },
  }),
})

const { isValid, normalizedAddress, errors } = await response.json()

Product Shipping Configuration

Add shipping details to your products:

// When creating/updating products
await payload.create({
  collection: 'products',
  data: {
    title: 'Widget',
    // ... other product fields
    
    shippingDetails: {
      weight: { value: 2.5, unit: 'pound' },
      dimensions: { length: 12, width: 8, height: 4, unit: 'inch' },
      shippingClass: 'standard',
      requiresSignature: false,
      hazardousMaterials: false,
    },
  },
})

Shipping Zones

Enable custom shipping zones for advanced rate calculations based on postal codes:

  1. Enable in Shipping Settings global (Custom Zones tab)
  2. Create zones in the Shipping Zones collection
  3. Define postal code patterns using regex

Example: Greater Toronto Area Zone

{
  name: 'Greater Toronto Area',
  enabled: true,
  priority: 1,
  postalCodePatterns: ['^M[0-9][A-Z]'], // Toronto postal codes
  countries: ['CA'],
  provinces: ['ON'],
  rateType: 'flat',
  baseRate: 800, // $8.00
  freeShippingThreshold: 5000, // $50.00
}

Webhooks

Setup in ShipStation

  1. Go to Settings > API > Webhooks in ShipStation
  2. Add webhook URL: https://yourdomain.com/api/shipping/shipstation/webhook
  3. Select events: tracking.updated, tracking.delivered
  4. Add your webhook secret through a postman api request as a custom-key in the header.

Tracking Updates

The plugin automatically:

  • Updates order tracking numbers
  • Changes shipment status
  • Triggers customer notifications (order_shipped, order_delivered)
  • Stores tracking history

API Reference

Endpoints

| Endpoint | Method | Description | |----------|--------|-------------| | /api/shipping/calculate-rates | POST | Calculate shipping rates for cart | | /api/shipping/validate-address | POST | Validate and correct address | | /api/shipping/estimate | GET | Quick rate estimate | | /api/shipping/shipstation/webhook | POST | ShipStation webhook handler |

Collections Extended

  • products - Adds shippingDetails group with weight, dimensions, shipping class
  • variants - Inherits/overrides product shipping details
  • orders - Adds shippingDetails with tracking info, costs, history

Globals Added

  • shipping-settings - Central shipping configuration
  • shipping-zones (optional) - Custom postal code-based zones

Development

Running Tests

cd packages/payload-plugin-shipstation
pnpm install
pnpm test

Building

pnpm build

Local Development

Link the plugin locally:

cd packages/payload-plugin-shipstation
pnpm link

cd ../../
pnpm link @frydigital/payload-plugin-shipstation

Troubleshooting

Rates Not Calculating

  1. Check ShipStation API key is valid
  2. Verify warehouse ID exists in ShipStation
  3. Check product weights are configured
  4. Review Payload logs for errors

Address Validation Failing

  1. Ensure address validation is enabled in settings
  2. Check validation mode (validate_only vs validate_and_clean)
  3. Verify address format matches ShipStation requirements

Cache Issues

  1. Check Redis connection if configured
  2. Verify cache TTL settings
  3. Clear cache: Delete Redis keys with pattern shipstation:rate:*

Webhook Not Working

  1. Verify webhook secret matches ShipStation
  2. Check webhook URL is publicly accessible
  3. Review webhook event logs in ShipStation
  4. Check Payload logs for webhook processing errors

Phase 2 Roadmap

International Shipping (Q1 2026)

  • Customs form generation
  • HS code management
  • Duty calculation integration
  • Country restrictions

Carrier Account Management (Q1 2026)

  • Multiple accounts per carrier
  • Load balancing strategies
  • Account-specific rate calculation
  • Warehouse-based account assignment

Shipping Analytics (Q1 2026)

  • Rate request tracking
  • Delivery performance metrics
  • Carrier comparison insights
  • Customer satisfaction tracking

Development

Running Tests

# Run tests once
pnpm test

# Run tests in watch mode
pnpm test:watch

# Run tests with coverage
pnpm test:coverage

# Run tests with UI
pnpm test:ui

Test Coverage

The plugin has comprehensive test coverage:

  • ShipStation API Client: 85.21%
  • Shipment Creation Utility: 98.46%
  • Orders Collection Hook: 95.62%
  • Endpoints: 84-100%
  • Overall: 73 tests passing

Building

# Build the plugin
pnpm build

# Watch mode for development
pnpm dev

# Clean build artifacts
pnpm clean

CI/CD

Tests run automatically on:

  • Pull request creation
  • Push to master/main branches
  • Node.js versions: 18.x, 20.x

Contributing

We welcome contributions! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass (pnpm test)
  5. Submit a pull request

License

MIT License - see LICENSE file for details

Support

Credits

Built with ❤️ for the Payload CMS community.