@frydigital/payload-plugin-shipstation
v1.0.0
Published
ShipStation integration plugin for Payload CMS ecommerce
Downloads
638
Maintainers
Readme
@frydigital/payload-plugin-shipstation
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-shipstation2. 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:63793. 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:
- Enable in Shipping Settings global (Custom Zones tab)
- Create zones in the Shipping Zones collection
- 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
- Go to Settings > API > Webhooks in ShipStation
- Add webhook URL:
https://yourdomain.com/api/shipping/shipstation/webhook - Select events: tracking.updated, tracking.delivered
- 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
shippingDetailsgroup with weight, dimensions, shipping class - variants - Inherits/overrides product shipping details
- orders - Adds
shippingDetailswith 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 testBuilding
pnpm buildLocal Development
Link the plugin locally:
cd packages/payload-plugin-shipstation
pnpm link
cd ../../
pnpm link @frydigital/payload-plugin-shipstationTroubleshooting
Rates Not Calculating
- Check ShipStation API key is valid
- Verify warehouse ID exists in ShipStation
- Check product weights are configured
- Review Payload logs for errors
Address Validation Failing
- Ensure address validation is enabled in settings
- Check validation mode (validate_only vs validate_and_clean)
- Verify address format matches ShipStation requirements
Cache Issues
- Check Redis connection if configured
- Verify cache TTL settings
- Clear cache: Delete Redis keys with pattern
shipstation:rate:*
Webhook Not Working
- Verify webhook secret matches ShipStation
- Check webhook URL is publicly accessible
- Review webhook event logs in ShipStation
- 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:uiTest 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 cleanCI/CD
Tests run automatically on:
- Pull request creation
- Push to
master/mainbranches - Node.js versions: 18.x, 20.x
Contributing
We welcome contributions! Please:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass (
pnpm test) - Submit a pull request
License
MIT License - see LICENSE file for details
Support
- Documentation: [Full docs coming soon]
- Issues: GitHub Issues
- Discussions: GitHub Discussions
Credits
Built with ❤️ for the Payload CMS community.
