# Go-Parts Ship Parallel Integration Analysis

> **Date:** 2026-02-24
> **Status:** Phase 2 Complete - Central Integration Done
> **Objective:** Run Go-Parts Ship in parallel with Shipstation so admins can choose which system to ship from

---

## Table of Contents

1. [Executive Summary](#1-executive-summary)
2. [Current Architecture](#2-current-architecture)
3. [Go-Parts Ship Architecture](#3-go-parts-ship-architecture)
4. [Side-by-Side Comparison](#4-side-by-side-comparison)
5. [Gap Analysis - Go-Parts Ship](#5-gap-analysis---go-parts-ship)
6. [Integration Design](#6-integration-design)
7. [Files to Modify in Central](#7-files-to-modify-in-central)
8. [Files to Fix in Go-Parts Ship](#8-files-to-fix-in-go-parts-ship)
9. [Implementation Steps](#9-implementation-steps)
10. [Questions for Stakeholder](#10-questions-for-stakeholder)

---

## 1. Executive Summary

**Goal:** Allow admin users in Central to ship orders via either Shipstation (existing) or Go-Parts Ship (new), with a toggle/checkbox on the ship form. Shipstation remains the default; Go-Parts Ship runs in parallel for testing.

**Why this works:** Go-Parts Ship already exposes a **Shipstation-compatible REST API** at `/api/ss/`. It accepts the same Basic Auth headers, same JSON payloads, and returns the same response formats. Central can talk to Go-Parts Ship using the same code it uses for Shipstation — just pointed at a different URL.

**Key Principle:** Zero changes to the existing Shipstation flow. The toggle is additive only.

---

## 2. Current Architecture (Central)

### Shipping Pipeline

```
User clicks "Ship" on order
    ↓
ship.blade.php (form: select warehouse, carrier, method, items)
    ↓
POST /shipment/process
    ↓
ShipmentController::process()
    ↓
ShipmentProcessorService::processShipment()
    ├── buildShipStationOrderData()     → Builds SS-format JSON
    ├── createShipStationOrder()         → Calls ShipStationService
    ├── deductStockItems()               → WHIMS inventory deduction
    ├── DB::table('shipments')->insert() → Saves shipment record
    ├── DB::table('orders_items_shipments') → Links items
    └── checkAndUpdateOrderStatus()      → Updates order status
    ↓
ShipStationService::createOrder()
    → POST https://ssapi.shipstation.com/orders/createorder
    → Returns { orderId: 12345, ... }
    ↓
Order appears in Shipstation dashboard
Warehouse staff creates label in Shipstation
    ↓
Shipstation fires SHIP_NOTIFY webhook → Old Central receives it
    → Updates tracking_number, shipping_cost, carrier, date_shipped
```

### Key Files

| File | Purpose |
|:-----|:--------|
| `app/Services/ShipStationService.php` | API client → `https://ssapi.shipstation.com/` |
| `app/Services/ShipmentProcessorService.php` | Orchestrator: builds payload, calls API, saves records |
| `app/Services/CarrierRateService.php` | Rate fetching with 30-day cache |
| `app/Services/PackageCalculatorService.php` | Dimensions & weight calculation |
| `app/Services/WarehouseSelectionService.php` | Auto-selects warehouse by proximity |
| `app/Http/Controllers/ShipmentController.php` | Web controller for ship form |
| `resources/views/shipment/ship.blade.php` | Ship order form UI |
| `config/shipstation.php` | 22+ warehouse configs with auth credentials |

### Rate Calculation Flow

```
CarrierRateService::getRates()
    → ShipStationService::getRates(warehouse, order, dimensions, carrierType)
        → POST https://ssapi.shipstation.com/shipments/getrates
    → Applies markups: FedEx ×1.135, UPS ×1.10, USPS/Amazon no markup
    → Caches result for 30 days
```

---

## 3. Go-Parts Ship Architecture

### Tech Stack
- **Framework:** Laravel 11 + Filament (PHP)
- **Shipping Backend:** ShipEngine API
- **Database:** MySQL (separate from Central)
- **Deployment:** `https://shipdev.go-parts.com` (dev) / `https://ship.go-parts.com` (prod)

### Shipstation-Compatible API Layer (Phases 1-8 COMPLETE)

```
Central sends same headers/payloads
    ↓
https://ship.go-parts.com/api/ss/Orders/CreateOrder
    ↓
ShipstationApiAuth middleware (validates Basic Auth → warehouse context)
    ↓
OrderController::createOrder()
    → OrderTransformer::fromShipstationFormat()  → Maps SS fields to Go-Parts Ship fields
    → Saves order in Go-Parts Ship database
    → Returns SS-format response with orderId
    ↓
Warehouse staff opens Go-Parts Ship Filament UI
    → CreateShipment page → selects rate → purchases label via ShipEngine
    ↓
ShipmentService::createFromRate()
    → ShipEngine API → label created, tracking number assigned
    → WebhookDispatcher::dispatch('SHIP_NOTIFY', $shipment)
        → POST to Central's webhook URL with resource_url
    ↓
Central receives webhook → fetches shipment details → updates tracking
```

### API Endpoints (All Implemented)

| Method | Endpoint | Status |
|:-------|:---------|:-------|
| `POST` | `/api/ss/Orders/CreateOrder` | DONE |
| `GET` | `/api/ss/Orders` | DONE |
| `GET` | `/api/ss/Orders/{id}` | DONE |
| `DELETE` | `/api/ss/Orders/{id}` | DONE |
| `POST` | `/api/ss/orders/assignuser` | DONE |
| `POST` | `/api/ss/shipments/getrates` | DONE |
| `GET` | `/api/ss/Shipments/List` | DONE |
| `GET` | `/api/ss/carriers` | DONE |
| `GET` | `/api/ss/carriers/listservices` | DONE |
| `GET` | `/api/ss/warehouses` | DONE |
| `GET` | `/api/ss/stores` | DONE |
| `GET` | `/api/ss/stores/marketplaces` | DONE |
| `GET` | `/api/ss/accounts/listtags` | DONE |
| `GET` | `/api/ss/users` | DONE |

### Key Files

| File | Purpose |
|:-----|:--------|
| `routes/api.php` | Route group at `/api/ss/` |
| `app/Http/Middleware/ShipstationApiAuth.php` | Basic Auth → warehouse lookup |
| `app/Http/Controllers/Api/Shipstation/OrderController.php` | Order CRUD |
| `app/Http/Controllers/Api/Shipstation/RateController.php` | Rate calculation |
| `app/Services/ShipstationApi/Transformers/OrderTransformer.php` | SS ↔ Go-Parts Ship |
| `app/Services/ShipstationApi/Transformers/RateTransformer.php` | SS ↔ ShipEngine |
| `app/Services/ShipstationApi/WebhookDispatcher.php` | SHIP_NOTIFY push |
| `app/Services/ShipmentService.php` | Label creation via ShipEngine |
| `app/Models/WarehouseApiCredential.php` | Warehouse auth mapping |
| `config/shipengine.php` | ShipEngine API config |

---

## 4. Side-by-Side Comparison

### What Central Sends → What Each System Does

| Feature | Shipstation | Go-Parts Ship |
|:--------|:-----------|:-------------|
| **Base URL** | `https://ssapi.shipstation.com/` | `https://shipdev.go-parts.com/api/ss/` |
| **Auth** | `Basic <base64>` per warehouse | Same `Basic <base64>` (seeded from Central config) |
| **Create Order** | Stores in SS database, shows in SS UI | Stores in Go-Parts Ship DB, shows in Filament UI |
| **Get Rates** | SS calculates via its carrier accounts | ShipEngine calculates via its carrier accounts |
| **Label Creation** | SS dashboard by warehouse staff | Go-Parts Ship Filament UI by warehouse staff |
| **Webhook (SHIP_NOTIFY)** | SS pushes to Central webhook URL | Go-Parts Ship pushes to same Central webhook URL |
| **Webhook Payload** | `{ resource_type, resource_url }` | Same format `{ resource_type, resource_url }` |
| **Tracking Updates** | Via SS `Shipments/List` polling | Via Go-Parts Ship `Shipments/List` endpoint |
| **Carrier Accounts** | Configured in SS per warehouse | Configured in ShipEngine per user |
| **Rate Markup** | Applied by Central (FedEx ×1.135, UPS ×1.10) | Applied by Central (same code) |
| **Response Format** | Shipstation JSON | Identical Shipstation JSON |

### Credential Verification

Checked authorization strings between Central `config/shipstation.php` and Go-Parts Ship `WarehouseApiCredentialSeeder.php`:

| Warehouse | Central Config | Go-Parts Ship Seeder | Match? |
|:----------|:---------------|:---------------------|:-------|
| GA | `ZDY2Mzc5YmM5OGY0...` | `ZDY2Mzc5YmM5OGY0...` | YES |
| ELTCA | `ZjJiM2EzYjcwNDli...` | `ZjJiM2EzYjcwNDli...` | YES |
| PBITX | `NmQ0YzE4MmYyNDBh...` | `NmQ0YzE4MmYyNDBh...` | YES |
| PBI | `MTU5ZDdhYzQ4MTYx...` | `MTU5ZDdhYzQ4MTYx...` | YES |
| PERFRAD-PA | `MTZmNzA4OGM5YjI2...` | `MTZmNzA4OGM5YjI2...` | YES |
| NJ | `MDA3N2ZjMjkxMTIz...` | `ZDBjYTAwNjkyNzM1...` | **NO - MISMATCH** |
| DTL | `MjM4NjhhMDczMTMw...` | (check needed) | ? |
| TYC-WC | `MzM2ZTVhMWE1YWJl...` | (check needed) | ? |

**CRITICAL:** NJ warehouse credentials DO NOT match between Central and Go-Parts Ship seeder. This will cause auth failures for NJ warehouse.

---

## 5. Gap Analysis - Go-Parts Ship

### CRITICAL GAPS (Must fix before testing)

| # | Gap | Impact | Effort |
|:--|:----|:-------|:-------|
| 1 | **NJ credential mismatch** in seeder vs Central config | Auth failures for NJ warehouse | Small - update seeder |
| 2 | **Missing PARTSBOX warehouses** (V, E, B, A) in seeder | These warehouses cannot ship via Go-Parts Ship | Small - add to seeder |
| 3 | **`user_id` not set on warehouse_api_credentials** | RateController returns error "Carriers not configured" | Medium - need to assign Go-Parts Ship user to each warehouse |
| 4 | **ShipEngine sandbox mode enabled** (`SHIPENGINE_SANDBOX=true` in config) | All API calls go to sandbox, labels are test labels | Small - but need real API key for production |
| 5 | **Carrier accounts not linked** | ShipEngine needs UPS/FedEx/USPS carrier accounts connected to the Go-Parts Ship user | Medium - requires ShipEngine dashboard setup |
| 6 | **Migration status unknown** | DB tables may not exist yet | Small - run `php artisan migrate` |
| 7 | **Seeder status unknown** | Warehouse credentials may not be in database | Small - run seeders |

### IMPORTANT GAPS (Should fix before real testing)

| # | Gap | Impact | Effort |
|:--|:----|:-------|:-------|
| 8 | **RateTransformer: `otherCost` always 0** | Central uses `otherCost` in cost calculations. ShipEngine doesn't return a separate `otherCost` so it's always 0. Rates may differ from Shipstation | Small - verify if this matters or if `shippingAmount` includes everything |
| 9 | **RateTransformer: no `transitDays`** | Central may display transit time to users | Small - ShipEngine provides this data, just not mapped yet |
| 10 | **Webhook `account_code` uses warehouse code** (e.g., `GA`, `DEPO-IL`) but old Central may expect lowercase/abbreviated codes (e.g., `ga`, `depowc`) | Webhook receiver may not match the warehouse correctly | Medium - verify against old Central's webhook handler |
| 11 | **No `createLabel` / `voidLabel` API endpoints** | Central currently doesn't call these via API (labels are made in SS/GPS UI), but future enhancement | N/A for now |
| 12 | **Insurance options** not fully mapped through to ShipEngine | Orders with insurance may not be properly insured | Small - verify mapping |

### LOW PRIORITY GAPS

| # | Gap | Impact | Effort |
|:--|:----|:-------|:-------|
| 13 | **No carrier-specific markups** in Go-Parts Ship rates | Central applies its own markups, so this is fine | None needed |
| 14 | **CustomField1/2/3** not stored or returned in Go-Parts Ship order response | Old Central uses these for PAMS data, but webhook flow doesn't depend on them | Low |
| 15 | **`tagIds`** support is basic | Tags may not match between systems | Low |

---

## 6. Integration Design

### Approach: Service Switcher Pattern

Since Go-Parts Ship exposes the exact same API format as Shipstation, we create a thin switcher that routes requests to the correct endpoint.

```
ship.blade.php
    ↓  [shipping_provider = 'shipstation' | 'gopartsship']
ShipmentController::process()
    ↓
ShipmentProcessorService::processShipment($request)
    ↓  reads $request->shipping_provider
    ├── if 'shipstation' → ShipStationService (existing, unchanged)
    └── if 'gopartsship' → GoPartsShipService (new, same interface)
```

### What Changes in Central

1. **New config file:** `config/gopartsship.php` — Go-Parts Ship API URL + feature flag
2. **New service:** `app/Services/GoPartsShipService.php` — Mirrors ShipStationService but hits Go-Parts Ship URL
3. **Modify:** `ShipmentProcessorService.php` — Add routing based on `shipping_provider` field
4. **Modify:** `CarrierRateService.php` — Add Go-Parts Ship rate fetching option
5. **Modify:** `ship.blade.php` — Add toggle/checkbox (admin only)
6. **Modify:** `ShipmentController.php` — Pass `shipping_provider` through

### What Does NOT Change
- ShipStationService.php (untouched)
- All existing Shipstation flow (untouched)
- Database schema (no migration needed in Central)
- Webhook handling (Go-Parts Ship sends same format)
- Order status flow
- Stock deduction logic
- Email notifications

### Admin Toggle UI Design

```
┌────────────────────────────────────────┐
│ Shipping Provider                       │
│ ○ Shipstation (Default)                 │
│ ○ Go-Parts Ship (Testing)              │
│   └─ ⚠ Using Go-Parts Ship for testing │
└────────────────────────────────────────┘
```

Only visible when:
- User is admin (role check)
- Feature flag `gopartsship.enabled` is true in config

---

## 7. Files to Modify in Central

### New Files

| File | Purpose |
|:-----|:--------|
| `config/gopartsship.php` | API URL, enabled flag, allowed user IDs |
| `app/Services/GoPartsShipService.php` | API client mirroring ShipStationService for Go-Parts Ship |

### Modified Files

| File | Change |
|:-----|:-------|
| `app/Services/ShipmentProcessorService.php` | Add `shipping_provider` routing in `processShipment()` |
| `app/Services/CarrierRateService.php` | Add Go-Parts Ship rate fetching alongside Shipstation |
| `app/Http/Controllers/ShipmentController.php` | Pass `shipping_provider` from form to processor |
| `resources/views/shipment/ship.blade.php` | Add admin-only toggle for shipping provider |
| `app/Models/Shipment.php` | Add `shipping_provider` to fillable (to track which system was used) |

### Database Change (Central)

```sql
ALTER TABLE shipments ADD COLUMN shipping_provider VARCHAR(20) DEFAULT 'shipstation';
-- Values: 'shipstation', 'gopartsship'
-- Tracks which system was used for each shipment
```

---

## 8. Files to Fix in Go-Parts Ship

### Before Testing

| File | Fix Required |
|:-----|:------------|
| `database/seeders/WarehouseApiCredentialSeeder.php` | Fix NJ auth string, add PARTSBOX-V/E/B/A warehouses |
| `database/seeders/WebhookConfigurationSeeder.php` | Verify account_code format matches old Central expectations |
| `.env` | Set `SHIPENGINE_SANDBOX=false`, set production API key |
| `app/Services/ShipstationApi/Transformers/RateTransformer.php` | Add `transitDays` mapping from ShipEngine response |
| Warehouse API credentials table | Set `user_id` for all warehouses (needed for carrier lookup) |
| ShipEngine dashboard | Connect UPS/FedEx/USPS carrier accounts |

### Verification Needed

| Item | How to Verify |
|:-----|:-------------|
| Migrations have been run | `php artisan migrate:status` on Go-Parts Ship |
| Seeders have been run | Check `warehouse_api_credentials` table has rows |
| ShipEngine API key is valid | Test `/api/ss/carriers` endpoint |
| Webhook URLs are correct | Compare account_code with old Central's `ShipstationController` |
| Carrier accounts are connected | Test `/api/ss/shipments/getrates` with a real address |

---

## 9. Implementation Steps

### Phase 1: Verify Go-Parts Ship Readiness (Go-Parts Ship side)

1. SSH to Go-Parts Ship server
2. Run `php artisan migrate:status` to check DB state
3. Run seeders if needed (fix credentials first!)
4. Verify `.env` has correct `SHIPENGINE_API_KEY`
5. Set `user_id` on warehouse_api_credentials records
6. Test: `curl -H "Authorization: Basic <GA_auth>" https://shipdev.go-parts.com/api/ss/carriers`
7. Test: Rate request with known address

### Phase 2: Create Central Integration (Central side)

1. Create `config/gopartsship.php`
2. Create `GoPartsShipService.php` (copy ShipStationService, change endpoint)
3. Add `shipping_provider` column to shipments table
4. Modify `ShipmentProcessorService.php` to support provider routing
5. Modify `ShipmentController.php` to pass provider from form
6. Add admin toggle to `ship.blade.php`
7. Add admin toggle to `ship-order.blade.php` (Filament page version)

### Phase 3: Rate Comparison (Optional but recommended)

1. Modify `CarrierRateService.php` to optionally fetch from both providers
2. Show side-by-side rates on ship form (admin only)
3. Log rate differences for analysis

### Phase 4: End-to-End Testing

1. Admin selects "Go-Parts Ship" on ship form for a real order
2. Order appears in Go-Parts Ship Filament UI
3. Create label in Go-Parts Ship UI
4. Verify webhook fires to Central
5. Verify tracking number appears in Central
6. Verify no impact on Shipstation orders shipping simultaneously

### Phase 5: Gradual Rollout

1. Start with GA warehouse only
2. Expand to other warehouses one at a time
3. Monitor for rate differences, webhook reliability, label quality
4. When confident, make Go-Parts Ship the default

---

## 10. Questions for Stakeholder

Before implementation, the following questions need answers:

1. **Which warehouse(s) should we test first?** GA is recommended as it has the most volume and is the primary warehouse.

2. **Do we need Go-Parts Ship rates to match Shipstation rates exactly?** They use different carrier account connections (Shipstation carrier accounts vs ShipEngine carrier accounts), so rates may differ. Is this acceptable for testing?

3. **ShipEngine carrier accounts:** Are UPS/FedEx/USPS accounts already connected in Go-Parts Ship's ShipEngine dashboard? If not, this is a prerequisite.

4. **Production vs Dev URL:** Should we start with `https://shipdev.go-parts.com/api/ss/` (dev) or `https://ship.go-parts.com/api/ss/` (prod)?

5. **Webhook handling:** The old Central at `/home/opsgoparts/www/central/` handles webhooks. Does Go-Parts Ship's webhook need to fire to the same URL, or should we add a webhook receiver to the new Central?

6. **Who are the admin users that should see the toggle?** All admins, or specific user IDs?

7. **Should the toggle be per-order or a global setting?** Per-order (checkbox on ship form) gives more control but requires manual selection each time. A global setting (e.g., "all GA orders go to Go-Parts Ship") would be more automated.

8. **Go-Parts Ship database migrations:** Have the Phase 1-8 migrations been run on the Go-Parts Ship database? We need to verify before testing.

---

## Appendix A: File Inventory

### Central (`/home/centralgoparts/public_html/`)

**Shipping Services:**
- `app/Services/ShipStationService.php` — Shipstation API client (616 lines)
- `app/Services/ShipmentProcessorService.php` — Shipment orchestrator (720 lines)
- `app/Services/CarrierRateService.php` — Rate fetching with caching
- `app/Services/PackageCalculatorService.php` — Dimensions & weight
- `app/Services/WarehouseSelectionService.php` — Warehouse auto-selection
- `app/Services/RateCacheService.php` — Rate caching
- `app/Services/WebshipApiService.php` — Webship alternate provider (existing parallel pattern)

**Controllers:**
- `app/Http/Controllers/ShipmentController.php` — Ship form + process

**Views:**
- `resources/views/shipment/ship.blade.php` — Main ship form
- `resources/views/filament/pages/ship-order.blade.php` — Filament ship page

**Config:**
- `config/shipstation.php` — 22+ warehouse configs

**Models:**
- `app/Models/Shipment.php` — `shipments` table
- `app/Models/OrderItemShipment.php` — `orders_items_shipments` link table

### Go-Parts Ship (`/home/shipgoparts/public_html/shipdev/`)

**Shipstation-Compatible API:**
- `routes/api.php` — Route group `/api/ss/`
- `app/Http/Middleware/ShipstationApiAuth.php` — Basic Auth
- `app/Http/Controllers/Api/Shipstation/OrderController.php`
- `app/Http/Controllers/Api/Shipstation/RateController.php`
- `app/Http/Controllers/Api/Shipstation/ShipmentController.php`
- `app/Http/Controllers/Api/Shipstation/CarrierController.php`
- `app/Http/Controllers/Api/Shipstation/WarehouseController.php`
- `app/Http/Controllers/Api/Shipstation/StoreController.php`
- `app/Http/Controllers/Api/Shipstation/AccountController.php`
- `app/Http/Controllers/Api/Shipstation/BaseController.php`

**Transformers:**
- `app/Services/ShipstationApi/Transformers/OrderTransformer.php`
- `app/Services/ShipstationApi/Transformers/RateTransformer.php`
- `app/Services/ShipstationApi/Transformers/ShipmentTransformer.php`
- `app/Services/ShipstationApi/Transformers/CarrierTransformer.php`
- `app/Services/ShipstationApi/CarrierCodeMapper.php`

**Webhook:**
- `app/Services/ShipstationApi/WebhookDispatcher.php`
- `app/Jobs/SendShipstationWebhook.php`

**Core Shipping:**
- `app/Services/ShipmentService.php` — Label creation via ShipEngine
- `app/Services/Shipping/ShipEngine/ShipEngineClient.php`
- `app/Services/Shipping/ShipEngine/ShipEngineRatingService.php`
- `app/Services/Shipping/ShipEngine/ShipEngineShippingService.php`
- `app/Services/Shipping/ShipEngine/ShipEngineTrackingService.php`
- `app/Services/Shipping/ShipEngine/ShipEngineCarrierService.php`

**Models:**
- `app/Models/Order.php` — Orders with SS compat fields
- `app/Models/Shipment.php` — Shipments with ShipEngine fields
- `app/Models/WarehouseApiCredential.php` — Warehouse auth mapping
- `app/Models/WebhookConfiguration.php` — Webhook URL config
- `app/Models/WebhookBatch.php` — Webhook delivery tracking
- `app/Models/CarrierCodeMapping.php` — SS ↔ ShipEngine carrier mapping

**Seeders:**
- `database/seeders/WarehouseApiCredentialSeeder.php` — 22+ warehouses
- `database/seeders/WebhookConfigurationSeeder.php` — Webhook URLs
- `database/seeders/CarrierCodeMappingSeeder.php` — Carrier mappings

## Appendix B: Swagger API Reference

The full Shipstation-compatible API is documented in:
`/home/shipgoparts/public_html/shipdev/documentation/swagger-shipstation-api.yaml`

Key details:
- **Base URL:** `https://ship.go-parts.com/api/ss` (prod) / `https://shipdev.go-parts.com/api/ss` (dev)
- **Auth:** HTTP Basic with per-warehouse credentials
- **Rate Limiting:** `X-Rate-Limit-Remaining` and `X-Rate-Limit-Reset` headers
- **Webhooks:** SHIP_NOTIFY push to Central with `resource_url` callback
