Shipping
ShipStation labels for GoCommerce
A Go package that buys a label through ShipStation’s V2 API when an operator ships an order, and gives the engine back a tracking number and a label to print. The order stays in GoCommerce; nothing is pushed into ShipStation first.
- Shipping module
- 10 settings
- 7 tests
- ext/fulfill-shipstation
What it does
The module registers “shipstation” as a fulfillment provider. An operator ships through the engine’s own POST /api/admin/create-fulfillment with the provider shipstation — the engine keeps the order’s state and its events, and the module only talks to ShipStation.
It speaks the newer V2 API at api.shipstation.com, with a single API-Key header, and buys a label from a shipment described in the request with one POST /v2/labels. The package explains why not the older ssapi, which is organised around orders pushed into ShipStation first: that would make ShipStation a second place an order exists, and a second place it can change.
In the admin it describes itself as “Multi-carrier labels through ShipStation’s V2 API, on the service you name.” It uses Go’s standard library and no SDK.
Configuration
Four things are required: the API key, a default service code, and a ship-from address line and country. Set them in Config from your own main(), or leave Config empty and fill them in under Settings › Shipping providers.
| Setting | Environment variable | Required | What it does |
|---|---|---|---|
APIKeyAPI key | SHIPSTATION_API_KEY | Yes | A ShipStation V2 API key, sent as the API-Key header. |
ServiceCodeService code | — | Yes | The default service — usps_priority_mail, ups_ground and the rest. Meta service_code overrides it for one parcel. |
From.AddressLine1From: address line 1 | — | Yes | Where parcels are sent from. The order knows where it is going, never where it came from, so this is configuration. |
From.CountryCodeFrom: country (ISO 2) | — | Yes | The ship-from country, as two letters. |
From.Name, From.Phone, From.CityLocality, From.StateProvince, From.PostalCodeFrom: name, phone, city, state, postal code | — | No | The rest of the ship-from address. |
From.CompanyName, From.AddressLine2, From.Residential | — | No | Go only. Residential is ShipStation’s address_residential_indicator; left alone, the carrier decides. |
DefaultWeightGramsDefault weight (grams) | — | No | Per unit, for a parcel holding a variant with no weight recorded. 500 by default. |
DefaultLengthMM, DefaultWidthMM, DefaultHeightMM | — | No | The box sent when the engine has no unambiguous size, which is any parcel holding more than one unit. 150 × 150 × 100 mm by default. Go only. |
BaseURLAPI base URL | — | No | Overrides https://api.shipstation.com, for tests. Empty for production. |
Client | — | No | Replaces the HTTP client, which otherwise times out after 30 seconds. Go only; not in the panel. |
The module reads its Config struct, not the environment. The variable names are the ones the package’s own example or the reference binary uses; in your own main() you choose where each value comes from. Where a setting has a panel label, the admin’s settings drawer can hold it too, and a value typed there wins over Config.
Setting it up
- Get a V2 API keyIn ShipStation, create a key for the V2 API and note the service code to buy by default. A key and secret for the older ssapi will not work here.
- Install the moduleImport it and pass shipstation.New to gocommerce.New, as below — or run the reference binary with -carriers, which installs every carrier module idle.
- Fill in the settingsKey, service code and ship-from address, in Config or under Settings › Shipping providers. A value typed into the panel counts on the next shipment, with no restart.
- Ship an orderPOST /api/admin/create-fulfillment with the order id and the provider shipstation. The engine records the tracking number and label URL and moves the order to shipped, or to partial.
main.go
import (
"os"
"github.com/itswadesh/gocommerce/core"
shipstation "github.com/itswadesh/gocommerce/ext/fulfill-shipstation"
)
app, err := gocommerce.New(cfg,
shipstation.New(shipstation.Config{
APIKey: os.Getenv("SHIPSTATION_API_KEY"),
ServiceCode: "usps_priority_mail",
From: shipstation.Address{
Name: "Acme", AddressLine1: "215 Clayton St",
CityLocality: "San Francisco", StateProvince: "CA",
PostalCode: "94117", CountryCode: "US", Phone: "+15553334444",
},
}),
) The package doc’s own example, with its imports; cfg is your gocommerce.Config. With an empty shipstation.Config the module installs idle and waits for the panel. Import path github.com/itswadesh/gocommerce/ext/fulfill-shipstation.
How it works
-
A label from a described parcel
POST /v2/labels carries the service code, the ship-from and ship-to addresses and one package. The answer is a tracking number and a label download; the module keeps the PDF link, or the generic link, or the PNG.
-
No tracking number, no shipment
A label that comes back without a tracking number is an error, not a shipment, so the order is never moved to shipped on a parcel nobody can trace.
-
Centimetres, keeping the tenth
ShipStation takes grams, which the engine stores, and centimetres, which it does not. Sides are divided by ten and sent as decimals — 305 mm is 30.5 cm, not 30 — because, as the package puts it, rounding down is how a parcel gets refused at the counter for being over its band.
-
A unique id per parcel
Each label carries external_shipment_id: the order number on a first shipment, with -2, -3 appended for later parcels against the same order, so ShipStation never sees a repeat.
-
Per-parcel overrides
Meta on the ship request can name a different service_code, or a carrier_id, for one parcel.
-
Carrier worked out
The carrier code comes from what ShipStation reports, or from the service code — USPS (Stamps.com and Endicia labels too, since USPS carries them), FedEx, UPS, DHL, Canada Post, Australia Post — so the tracking number gets the right link.
What it does not do
Read these before an order depends on it. No store is known to run GoCommerce in production yet, so these come from the code, not from anyone’s experience.
- No rate shoppingThe service is a setting or a meta override. The module never asks ShipStation for rates or picks one for you.
- No tracking updatesNo webhook and no stored state: delivery events in ShipStation do not reach the order. Marking it delivered is still the operator’s job.
- No void callThere is no call to cancel a label. Deleting the fulfillment in GoCommerce does not touch the label in ShipStation.
- V2 keys onlyCredentials for the older ssapi — a key and a secret over HTTP Basic — do not work; the module speaks only the V2 API.
- No customs declarationThe request carries no customs items, so a cross-border parcel that needs a declaration is not covered by the module as written.
- A guessed box for several itemsFor anything but a single unit, the size sent is the configured default, not a measurement. The engine does not pack cartons.
FAQ
Questions about the ShipStation module
Does it push orders into ShipStation?
No, deliberately. The package keeps GoCommerce as the system of record and buys a label from a shipment described in the request, so the order never exists in two places that could disagree.
Which API key do I need?
A V2 API key, sent as the API-Key header. The older ShipStation API at ssapi.shipstation.com, with a key and secret over HTTP Basic, is not what this module speaks.
Can I use it without writing Go?
Yes. The reference binary’s -carriers flag installs every carrier module with an empty Config. Switch ShipStation on under Settings › Shipping providers and fill in the key, service code and ship-from address there.
Is this a ShipStation partnership?
No. The module calls ShipStation’s public API with a key you supply. ShipStation does not endorse GoCommerce, and no store is known to run this module in production yet.
Source
Everything on this page is read from ext/fulfill-shipstation in the GoCommerce repository, MIT licensed. When this page and the code disagree, the code is right and this page is out of date.ShipStation is a trademark of its owner; this module talks to its public API and implies no endorsement. See trademarks.
Try it against a store of your own
The one-command stack gives you GoCommerce’s API and admin on your own machine in minutes. Add this module to it and try it on test orders before a real one depends on it.